diff --git a/mingw-w64-innoextract/0001-loader-support-setup-loader-offset-table-revision-2.patch b/mingw-w64-innoextract/0001-loader-support-setup-loader-offset-table-revision-2.patch new file mode 100644 index 0000000000000..a55fa4c4b93f7 --- /dev/null +++ b/mingw-w64-innoextract/0001-loader-support-setup-loader-offset-table-revision-2.patch @@ -0,0 +1,97 @@ +From 67d95c40e742ca68340d44e34b1a56e8e8795828 Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Tue, 5 May 2026 01:13:19 +0200 +Subject: [PATCH 01/11] loader: support setup loader offset table revision 2 + +Inno Setup 6.5.0 bumped SetupLdrOffsetTableVersion from 1 to 2 and +widened the on-disk TSetupLdrOffsetTable struct: TotalSize, OffsetEXE, +Offset0 and Offset1 each grew from 32-bit LongWord to Int64, and a +new ReservedPadding UInt32 was inserted before TableCRC. The record +also lost its `packed` qualifier, but with 32-bit Delphi default +alignment (Int64 is 4-byte aligned in 32-bit Delphi) the on-disk +layout remains contiguous, growing from 48 to 64 bytes total. See +Projects/Src/Shared.Struct.pas at tag is-6_5_0 in +https://github.com/jrsoftware/issrc. + +Without this, every installer produced by Inno Setup 6.5.0 or newer +fails to load with: + + Warning: Unexpected setup loader revision: 2 + Warning: Setup loader checksum mismatch! + Could not determine setup data version! + +Detect revision 2 specifically and parse the new layout. The fields +that innoextract actually consumes (exe_offset, header_offset, +data_offset) are still stored as 32-bit in the offsets struct, so +truncate the read 64-bit values to 32-bit and emit a warning if any +of them exceed 4 GiB. Real installers that large are rare in the +wild but possible, and widening the struct is left for a follow-up. + +Assisted-by: Claude Opus 4.7 +Signed-off-by: Johannes Schindelin +--- + src/loader/offsets.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 48 insertions(+) + +diff --git a/src/loader/offsets.cpp b/src/loader/offsets.cpp +index 0c0d6a4..80bcdc4 100644 +--- a/src/loader/offsets.cpp ++++ b/src/loader/offsets.cpp +@@ -148,6 +148,54 @@ bool offsets::load_offsets_at(std::istream & is, boost::uint32_t pos) { + is.clear(); + debug("could not read loader header revision"); + return false; ++ } else if(revision == 2) { ++ /* ++ * Inno Setup 6.5.0 introduced a wider TSetupLdrOffsetTable layout ++ * (revision 2). The fields previously stored as 32-bit LongWord ++ * are now Int64, allowing setup binaries larger than 4 GiB. A new ++ * ReservedPadding UInt32 sits between Offset1 and TableCRC. The ++ * record itself is no longer Delphi-`packed`, but with 32-bit ++ * Delphi default alignment the on-disk layout remains contiguous ++ * (Int64 has 4-byte alignment in 32-bit mode), so the total size ++ * goes from 48 to 64 bytes. See Projects/Src/Shared.Struct.pas in ++ * issrc. ++ */ ++ (void)checksum.load(is); // TotalSize ++ boost::int64_t offset_exe = checksum.load(is); ++ boost::uint32_t uncompressed_exe = checksum.load(is); ++ boost::uint32_t crc_exe = checksum.load(is); ++ boost::int64_t offset0 = checksum.load(is); ++ boost::int64_t offset1 = checksum.load(is); ++ (void)checksum.load(is); // ReservedPadding ++ if(is.fail()) { ++ is.clear(); ++ debug("could not read loader header (revision 2)"); ++ return false; ++ } ++ boost::int64_t max_uint32 = boost::int64_t(std::numeric_limits::max()); ++ if(offset_exe < 0 || offset_exe > max_uint32 || ++ offset0 < 0 || offset0 > max_uint32 || ++ offset1 < 0 || offset1 > max_uint32) { ++ log_warning << "Loader header offsets exceed 4 GiB; truncating to 32-bit"; ++ } ++ exe_offset = boost::uint32_t(offset_exe); ++ exe_compressed_size = 0; ++ exe_uncompressed_size = uncompressed_exe; ++ exe_checksum.type = crypto::CRC32; ++ exe_checksum.crc32 = crc_exe; ++ message_offset = 0; ++ header_offset = boost::uint32_t(offset0); ++ data_offset = boost::uint32_t(offset1); ++ boost::uint32_t expected = util::load(is); ++ if(is.fail()) { ++ is.clear(); ++ debug("could not read loader header checksum (revision 2)"); ++ return false; ++ } ++ if(checksum.finalize() != expected) { ++ log_warning << "Setup loader checksum mismatch!"; ++ } ++ return true; + } else if(revision != 1) { + log_warning << "Unexpected setup loader revision: " << revision; + } +-- +2.54.0.windows.1 + diff --git a/mingw-w64-innoextract/0002-Add-support-for-Inno-Setup-6.4.2-and-6.4.3.patch b/mingw-w64-innoextract/0002-Add-support-for-Inno-Setup-6.4.2-and-6.4.3.patch new file mode 100644 index 0000000000000..beea52d998069 --- /dev/null +++ b/mingw-w64-innoextract/0002-Add-support-for-Inno-Setup-6.4.2-and-6.4.3.patch @@ -0,0 +1,85 @@ +From 9f3cee2dd8e1c105d0bd280a3bac331ea3a0417d Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Tue, 5 May 2026 01:24:06 +0200 +Subject: [PATCH 02/11] Add support for Inno Setup 6.4.2 and 6.4.3 + +Inno Setup 6.4.2 added the new [Setup] section directive +CloseApplicationsFilterExcludes (see +https://jrsoftware.org/files/is6.4-whatsnew.htm). It is serialised as a +binary string immediately after architectures_installed_in_64bit_mode_expr +in TSetupHeader (Projects/Src/Shared.Struct.pas in +https://github.com/jrsoftware/issrc at tag is-6_4_2). Read it as a new +header field for installers from 6.4.2 onward; clear it for older +installers. + +6.4.2 also bumped the SetupID magic to 'Inno Setup Setup Data (6.4.2)'; +6.4.3 to 'Inno Setup Setup Data (6.4.3)'. Both versions are otherwise +on-disk-compatible with the post-6.4.2 layout, so add them to the known +versions table without further parsing changes. + +This change is sourced from the prior-art PR #202 by itai-delphos +(https://github.com/dscharrer/innoextract/pull/202), which I am picking +up and extending to cover Inno Setup 6.5.x, 6.6.x and 6.7.x in the +follow-up commits in this series. + +Helped-by: itai-delphos@github +Assisted-by: Claude Opus 4.7 +Signed-off-by: Johannes Schindelin +--- + src/setup/header.cpp | 6 ++++++ + src/setup/header.hpp | 1 + + src/setup/version.cpp | 2 ++ + 3 files changed, 9 insertions(+) + +diff --git a/src/setup/header.cpp b/src/setup/header.cpp +index bbe9df9..6ed61cc 100644 +--- a/src/setup/header.cpp ++++ b/src/setup/header.cpp +@@ -266,6 +266,11 @@ void header::load(std::istream & is, const version & version) { + is >> util::binary_string(architectures_allowed_expr); + is >> util::binary_string(architectures_installed_in_64bit_mode_expr); + } ++ if(version >= INNO_VERSION(6, 4, 2)) { ++ is >> util::binary_string(close_applications_filter_excludes); ++ } else { ++ close_applications_filter_excludes.clear(); ++ } + if(version >= INNO_VERSION(5, 2, 5)) { + is >> util::ansi_string(license_text); + is >> util::ansi_string(info_before); +@@ -762,6 +767,7 @@ void header::decode(util::codepage_id codepage) { + util::to_utf8(create_uninstall_registry_key, codepage, &lead_bytes); + util::to_utf8(uninstallable, codepage); + util::to_utf8(close_applications_filter, codepage); ++ util::to_utf8(close_applications_filter_excludes, codepage); + util::to_utf8(setup_mutex, codepage, &lead_bytes); + util::to_utf8(changes_environment, codepage); + util::to_utf8(changes_associations, codepage); +diff --git a/src/setup/header.hpp b/src/setup/header.hpp +index 78e5b64..08d492c 100644 +--- a/src/setup/header.hpp ++++ b/src/setup/header.hpp +@@ -167,6 +167,7 @@ struct header { + std::string changes_associations; + std::string architectures_allowed_expr; + std::string architectures_installed_in_64bit_mode_expr; ++ std::string close_applications_filter_excludes; + std::string license_text; + std::string info_before; + std::string info_after; +diff --git a/src/setup/version.cpp b/src/setup/version.cpp +index 7f68af2..be6682b 100644 +--- a/src/setup/version.cpp ++++ b/src/setup/version.cpp +@@ -186,6 +186,8 @@ const known_version versions[] = { + { "Inno Setup Setup Data (6.3.0)", INNO_VERSION_EXT(6, 3, 0, 0), version::Unicode }, + { "Inno Setup Setup Data (6.4.0)", /* prerelease */ INNO_VERSION_EXT(6, 4, 0, 0), version::Unicode }, + { "Inno Setup Setup Data (6.4.0.1)", /* 6.4.0 */ INNO_VERSION_EXT(6, 4, 0, 1), version::Unicode }, ++ { "Inno Setup Setup Data (6.4.2)", INNO_VERSION_EXT(6, 4, 2, 0), version::Unicode }, ++ { "Inno Setup Setup Data (6.4.3)", INNO_VERSION_EXT(6, 4, 3, 0), version::Unicode }, + }; + + } // anonymous namespace +-- +2.54.0.windows.1 + diff --git a/mingw-w64-innoextract/0003-Add-support-for-Inno-Setup-6.5.0-6.5.1-6.5.2-6.5.3-a.patch b/mingw-w64-innoextract/0003-Add-support-for-Inno-Setup-6.5.0-6.5.1-6.5.2-6.5.3-a.patch new file mode 100644 index 0000000000000..30255678813e9 --- /dev/null +++ b/mingw-w64-innoextract/0003-Add-support-for-Inno-Setup-6.5.0-6.5.1-6.5.2-6.5.3-a.patch @@ -0,0 +1,112 @@ +From 6c584226b9adc9decd64f621d36d267b7bc5026d Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Tue, 5 May 2026 01:44:12 +0200 +Subject: [PATCH 03/11] Add support for Inno Setup 6.5.0, 6.5.1, 6.5.2, 6.5.3 + and 6.5.4 + +Inno Setup 6.5.0 made two on-disk changes to TSetupHeader: + + - A new SevenZipLibraryName: String, serialised right after + CloseApplicationsFilterExcludes (Projects/Src/Shared.Struct.pas + at tag is-6_5_0 in https://github.com/jrsoftware/issrc). + - A new NumISSigKeyEntries: Integer entry-count field, slotted + between NumDirEntries and NumFileEntries. ISSig (Inno Setup + Signature) key entries are part of the new ECDSA P-256 ISSigTool + technology preview added in 6.4.3 and turned into a serialised + table in 6.5.0. innoextract does not yet process the entries + themselves; for now consume the count so that NumFileEntries and + every entry count after it remains aligned. + +6.5.0 also bumped the SetupLdrOffsetTableVersion from 1 to 2 and +widened the offset table; that part is handled in the previous +"loader: support setup loader offset table revision 2" commit. + +Add SetupID magic strings: + - 6.5.0 covers 6.5.0 and 6.5.1, which share `Inno Setup Setup Data + (6.5.0)`. + - 6.5.2 covers 6.5.2, 6.5.3 and 6.5.4, which share + `Inno Setup Setup Data (6.5.2)`. The header changes (new wizard + image background colours and dynamic-dark-mode variants) are added + in a separate follow-up commit since they are not in the same + on-disk position as the older WizardImageBackColor field, so they + require a different branch in `header::load`. + +This change continues the prior-art line started by PR #202 +(https://github.com/dscharrer/innoextract/pull/202) by itai-delphos. + +Helped-by: itai-delphos@github +Assisted-by: Claude Opus 4.7 +Signed-off-by: Johannes Schindelin +--- + src/setup/header.cpp | 14 ++++++++++++++ + src/setup/header.hpp | 1 + + src/setup/version.cpp | 2 ++ + 3 files changed, 17 insertions(+) + +diff --git a/src/setup/header.cpp b/src/setup/header.cpp +index 6ed61cc..65c6603 100644 +--- a/src/setup/header.cpp ++++ b/src/setup/header.cpp +@@ -271,6 +271,11 @@ void header::load(std::istream & is, const version & version) { + } else { + close_applications_filter_excludes.clear(); + } ++ if(version >= INNO_VERSION(6, 5, 0)) { ++ is >> util::binary_string(seven_zip_library_name); ++ } else { ++ seven_zip_library_name.clear(); ++ } + if(version >= INNO_VERSION(5, 2, 5)) { + is >> util::ansi_string(license_text); + is >> util::ansi_string(info_before); +@@ -324,6 +329,14 @@ void header::load(std::istream & is, const version & version) { + } + + directory_count = util::load(is, version.bits()); ++ if(version >= INNO_VERSION(6, 5, 0)) { ++ // Inno Setup 6.5.0 inserted a NumISSigKeyEntries: Integer field ++ // between NumDirEntries and NumFileEntries ++ // (Projects/Src/Shared.Struct.pas at tag is-6_5_0). innoextract ++ // does not yet use the count, but the bytes must be consumed to ++ // keep the stream position correct. ++ (void)util::load(is, version.bits()); ++ } + file_count = util::load(is, version.bits()); + data_entry_count = util::load(is, version.bits()); + icon_count = util::load(is, version.bits()); +@@ -768,6 +781,7 @@ void header::decode(util::codepage_id codepage) { + util::to_utf8(uninstallable, codepage); + util::to_utf8(close_applications_filter, codepage); + util::to_utf8(close_applications_filter_excludes, codepage); ++ util::to_utf8(seven_zip_library_name, codepage); + util::to_utf8(setup_mutex, codepage, &lead_bytes); + util::to_utf8(changes_environment, codepage); + util::to_utf8(changes_associations, codepage); +diff --git a/src/setup/header.hpp b/src/setup/header.hpp +index 08d492c..fa47892 100644 +--- a/src/setup/header.hpp ++++ b/src/setup/header.hpp +@@ -168,6 +168,7 @@ struct header { + std::string architectures_allowed_expr; + std::string architectures_installed_in_64bit_mode_expr; + std::string close_applications_filter_excludes; ++ std::string seven_zip_library_name; + std::string license_text; + std::string info_before; + std::string info_after; +diff --git a/src/setup/version.cpp b/src/setup/version.cpp +index be6682b..2911396 100644 +--- a/src/setup/version.cpp ++++ b/src/setup/version.cpp +@@ -188,6 +188,8 @@ const known_version versions[] = { + { "Inno Setup Setup Data (6.4.0.1)", /* 6.4.0 */ INNO_VERSION_EXT(6, 4, 0, 1), version::Unicode }, + { "Inno Setup Setup Data (6.4.2)", INNO_VERSION_EXT(6, 4, 2, 0), version::Unicode }, + { "Inno Setup Setup Data (6.4.3)", INNO_VERSION_EXT(6, 4, 3, 0), version::Unicode }, ++ { "Inno Setup Setup Data (6.5.0)", INNO_VERSION_EXT(6, 5, 0, 0), version::Unicode }, ++ { "Inno Setup Setup Data (6.5.2)", INNO_VERSION_EXT(6, 5, 2, 0), version::Unicode }, + }; + + } // anonymous namespace +-- +2.54.0.windows.1 + diff --git a/mingw-w64-innoextract/0004-Add-support-for-Inno-Setup-6.6.0-and-6.6.1.patch b/mingw-w64-innoextract/0004-Add-support-for-Inno-Setup-6.6.0-and-6.6.1.patch new file mode 100644 index 0000000000000..f22185add67e3 --- /dev/null +++ b/mingw-w64-innoextract/0004-Add-support-for-Inno-Setup-6.6.0-and-6.6.1.patch @@ -0,0 +1,246 @@ +From 65c61e140738ffebf45083a10b6a2cb835f130b2 Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Tue, 5 May 2026 01:49:32 +0200 +Subject: [PATCH 04/11] Add support for Inno Setup 6.6.0 and 6.6.1 + +Inno Setup 6.6.0 made several rearrangements in TSetupHeader's wizard +section (Projects/Src/Shared.Struct.pas at tag is-6_6_0 in +https://github.com/jrsoftware/issrc): + + - Removed WizardStyle. The classic/modern toggle was always going to + end up as ModernStyle for any installer reaching modern Inno Setup + versions, so 6.6.0 dropped the field on disk entirely. + - Added WizardDarkStyle: TSetupWizardDarkStyle (light / dark / + dynamic), serialised AFTER the WizardSizePercentX/Y pair instead + of where WizardStyle used to sit. + - Added WizardImageBackColorDynamicDark and + WizardSmallImageBackColorDynamicDark, the dark-mode counterparts + of the colours added back in 6.5.2. + +6.6.1 then added one more field at the same position: + + - WizardImageOpacity: Byte. Defaults to 0xff (fully opaque) for + older installers. + +In innoextract these changes are not purely additive: the wizard read +block has to take a 6.6.0+ branch that omits the WizardStyle byte, +and the new colour and opacity fields land between the old +WizardImageAlphaFormat and the rest of the wizard scalars. Add a +stored_dark_style enum mapping (analogous to stored_setup_style), +declare it as a NAMED_ENUM and provide its NAMES so that +stored_enum<>::get()'s warning path can reference its name at compile +time, and add new header fields image_back_color_dynamic_dark, +small_image_back_color_dynamic_dark and wizard_image_opacity. + +TSetupHeaderOption itself was reshuffled at the same time. The +shWizardResizable bit was removed (the new modern wizard is always +resizable, so the flag became redundant) and four new wizard-styling +bits were appended at the same position: shWizardModern, +shWizardBorderStyled, shWizardKeepAspectRatio and +shWizardLightButtonsUnstyled. Mirror this in header::flags by adding +the four new enum values, gating the existing WizardResizable add to +``< 6.6.0`` and adding the four new flags for ``>= 6.6.0`` so that +the on-disk bit positions stay aligned. Provide NAMES strings for the +new flags. shWizardLightButtonsUnstyled is later dropped again in +6.7.0; that gate is added in the 6.7.0 patch so that this commit +remains self-contained for installers compiled with 6.6.0 or 6.6.1. + +Add the SetupID magic strings 'Inno Setup Setup Data (6.6.0)' and +'Inno Setup Setup Data (6.6.1)'. (Each 6.6.x point release carries a +distinct magic, unlike 6.5.0/.1 and 6.5.2/.3/.4.) + +Assisted-by: Claude Opus 4.7 +Signed-off-by: Johannes Schindelin +--- + src/setup/header.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++- + src/setup/header.hpp | 14 +++++++++ + src/setup/version.cpp | 2 ++ + 3 files changed, 83 insertions(+), 1 deletion(-) + +diff --git a/src/setup/header.cpp b/src/setup/header.cpp +index 65c6603..87b30b2 100644 +--- a/src/setup/header.cpp ++++ b/src/setup/header.cpp +@@ -56,6 +56,12 @@ STORED_ENUM_MAP(stored_setup_style, header::ClassicStyle, + header::ModernStyle + ); + ++STORED_ENUM_MAP(stored_dark_style, header::LightStyle, ++ header::LightStyle, ++ header::DarkStyle, ++ header::DynamicStyle ++); ++ + STORED_ENUM_MAP(stored_bool_auto_no_yes, header::Auto, + header::Auto, + header::No, +@@ -379,14 +385,25 @@ void header::load(std::istream & is, const version & version) { + small_image_back_color = 0; + } + +- if(version >= INNO_VERSION(6, 0, 0)) { ++ if(version >= INNO_VERSION(6, 6, 0)) { ++ // Inno Setup 6.6.0 removed WizardStyle (the classic/modern toggle is ++ // gone) and added WizardDarkStyle (light/dark/dynamic) in its place, ++ // after the WizardSizePercentX/Y pair instead of before it. See ++ // TSetupHeader in Projects/Src/Shared.Struct.pas at tag is-6_6_0. ++ wizard_style = ModernStyle; ++ wizard_resize_percent_x = util::load(is); ++ wizard_resize_percent_y = util::load(is); ++ wizard_dark_style = stored_enum(is).get(); ++ } else if(version >= INNO_VERSION(6, 0, 0)) { + wizard_style = stored_enum(is).get(); + wizard_resize_percent_x = util::load(is); + wizard_resize_percent_y = util::load(is); ++ wizard_dark_style = LightStyle; + } else { + wizard_style = ClassicStyle; + wizard_resize_percent_x = 0; + wizard_resize_percent_y = 0; ++ wizard_dark_style = LightStyle; + } + + if(version >= INNO_VERSION(5, 5, 7)) { +@@ -395,6 +412,30 @@ void header::load(std::istream & is, const version & version) { + image_alpha_format = AlphaIgnored; + } + ++ if(version >= INNO_VERSION(6, 5, 2)) { ++ // Inno Setup 6.5.2 re-introduced WizardImageBackColor and ++ // WizardSmallImageBackColor as serialised header fields after ++ // WizardImageAlphaFormat; before 5.5.7 they sat much earlier ++ // in the struct (handled above). ++ image_back_color = util::load(is); ++ small_image_back_color = util::load(is); ++ } ++ if(version >= INNO_VERSION(6, 6, 0)) { ++ // 6.6.0 added the DynamicDark variants. ++ image_back_color_dynamic_dark = util::load(is); ++ small_image_back_color_dynamic_dark = util::load(is); ++ } else { ++ image_back_color_dynamic_dark = 0; ++ small_image_back_color_dynamic_dark = 0; ++ } ++ if(version >= INNO_VERSION(6, 6, 1)) { ++ // 6.6.1 added WizardImageOpacity (Byte). Default for older versions ++ // is fully opaque. ++ wizard_image_opacity = util::load(is); ++ } else { ++ wizard_image_opacity = 0xff; ++ } ++ + if(version >= INNO_VERSION(6, 4, 0)) { + is.read(password.sha256, 4); + password.type = crypto::PBKDF2_SHA256_XChaCha20; +@@ -742,11 +783,26 @@ header::flags header::load_flags(std::istream & is, const version & version) { + if(version >= INNO_VERSION(6, 0, 0)) { + flagreader.add(AppNameHasConsts); + flagreader.add(UsePreviousPrivileges); ++ } ++ if(version >= INNO_VERSION(6, 0, 0) && version < INNO_VERSION(6, 6, 0)) { ++ // Inno Setup 6.6.0 dropped shWizardResizable from ++ // TSetupHeaderOption (the wizard is unconditionally resizable in ++ // the new modern style; the four flags appended at the same ++ // position in the enum take its slot). + flagreader.add(WizardResizable); + } + if(version >= INNO_VERSION(6, 3, 0)) { + flagreader.add(UninstallLogging); + } ++ if(version >= INNO_VERSION(6, 6, 0)) { ++ // Inno Setup 6.6.0 added four new wizard-styling flags after ++ // shUninstallLogging. WizardLightButtonsUnstyled is later ++ // dropped again in 6.7.0 (handled in the 6.7.0 patch). ++ flagreader.add(WizardModern); ++ flagreader.add(WizardBorderStyled); ++ flagreader.add(WizardKeepAspectRatio); ++ flagreader.add(WizardLightButtonsUnstyled); ++ } + + return flagreader.finalize(); + } +@@ -843,6 +899,10 @@ NAMES(setup::header::flags, "Setup Option", + "use_previous_privileges", + "wizard_resizable", + "uninstall_logging", ++ "wizard modern", ++ "wizard border styled", ++ "wizard keep aspect ratio", ++ "wizard light buttons unstyled", + "uninstallable", + "disable dir page", + "disable program group page", +@@ -895,6 +955,12 @@ NAMES(setup::header::style, "Style", + "modern", + ) + ++NAMES(setup::header::dark_style, "Dark Style", ++ "light", ++ "dark", ++ "dynamic", ++) ++ + NAMES(setup::header::auto_bool, "Auto Boolean", + "auto", + "no", +diff --git a/src/setup/header.hpp b/src/setup/header.hpp +index fa47892..77fa651 100644 +--- a/src/setup/header.hpp ++++ b/src/setup/header.hpp +@@ -102,6 +102,10 @@ struct header { + UsePreviousPrivileges, + WizardResizable, + UninstallLogging, ++ WizardModern, ++ WizardBorderStyled, ++ WizardKeepAspectRatio, ++ WizardLightButtonsUnstyled, + + // Obsolete flags + Uninstallable, +@@ -201,12 +205,21 @@ struct header { + Color back_color2; + Color image_back_color; + Color small_image_back_color; ++ Color image_back_color_dynamic_dark; ++ Color small_image_back_color_dynamic_dark; ++ boost::uint8_t wizard_image_opacity; + + enum style { + ClassicStyle, + ModernStyle + }; + style wizard_style; ++ enum dark_style { ++ LightStyle, ++ DarkStyle, ++ DynamicStyle ++ }; ++ dark_style wizard_dark_style; + boost::uint32_t wizard_resize_percent_x; + boost::uint32_t wizard_resize_percent_y; + +@@ -300,6 +313,7 @@ NAMED_ENUM(setup::header::alpha_format) + NAMED_ENUM(setup::header::install_verbosity) + NAMED_ENUM(setup::header::log_mode) + NAMED_ENUM(setup::header::style) ++NAMED_ENUM(setup::header::dark_style) + NAMED_ENUM(setup::header::auto_bool) + NAMED_ENUM(setup::header::privilege_level) + NAMED_ENUM(setup::header::language_detection_method) +diff --git a/src/setup/version.cpp b/src/setup/version.cpp +index 2911396..4aadc56 100644 +--- a/src/setup/version.cpp ++++ b/src/setup/version.cpp +@@ -190,6 +190,8 @@ const known_version versions[] = { + { "Inno Setup Setup Data (6.4.3)", INNO_VERSION_EXT(6, 4, 3, 0), version::Unicode }, + { "Inno Setup Setup Data (6.5.0)", INNO_VERSION_EXT(6, 5, 0, 0), version::Unicode }, + { "Inno Setup Setup Data (6.5.2)", INNO_VERSION_EXT(6, 5, 2, 0), version::Unicode }, ++ { "Inno Setup Setup Data (6.6.0)", INNO_VERSION_EXT(6, 6, 0, 0), version::Unicode }, ++ { "Inno Setup Setup Data (6.6.1)", INNO_VERSION_EXT(6, 6, 1, 0), version::Unicode }, + }; + + } // anonymous namespace +-- +2.54.0.windows.1 + diff --git a/mingw-w64-innoextract/0005-refactor-widen-seekable-offset-and-size-types-to-64-.patch b/mingw-w64-innoextract/0005-refactor-widen-seekable-offset-and-size-types-to-64-.patch new file mode 100644 index 0000000000000..034ffc2e1ede5 --- /dev/null +++ b/mingw-w64-innoextract/0005-refactor-widen-seekable-offset-and-size-types-to-64-.patch @@ -0,0 +1,320 @@ +From 37c2d6eed1fe0041fdd270f005022d4a10cd2437 Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Tue, 5 May 2026 12:42:56 +0200 +Subject: [PATCH 05/11] refactor: widen seekable offset and size types to + 64-bit + +Inno Setup 6.7.0 widens several on-disk size and offset fields from +32 to 64 bits (TSetupLdrOffsetTable.OffsetEXE/Offset0/Offset1, the +block-header stored_size, and the new "idskb32" disk-slice header), to +allow setup binaries larger than 4 GiB. The current "loader: support +setup loader offset table revision 2" code path already reads the +widened TSetupLdrOffsetTable fields as Int64, but truncates them back +to 32 bits with a warning when storing into `loader::offsets` because +the struct still holds them in `boost::uint32_t`. + +Widen the affected types now, ahead of consuming the new on-disk +formats, so that the follow-up commit can drop the truncations and +read the wider on-disk fields natively without churn in the call +sites. The fields touched are: + + - `loader::offsets::{exe_offset, exe_compressed_size, + exe_uncompressed_size, message_offset, header_offset, + data_offset}` and the matching argument of `load_offsets_at`. + - `stream::chunk::{sort_offset, offset}` (chunk byte offsets within + a slice; `size` was already 64-bit). + - `stream::slice_reader::{data_offset, slice_size}`, the matching + argument of the embedded-data constructor, and the offset + parameter of `seek(slice, offset)`. The local `read_pos`/ + `remaining` in `slice_reader::read` follow `slice_size`. + - `cli/extract.cpp`'s local `sort_offset` mirror of + `chunk::sort_offset`. + +The on-disk reads themselves are unchanged: every site still pulls in +the same number of bytes as before, just into wider locals or members, +and the existing `boost::uint32_t(...)` truncation in the loader-v2 +path remains in place so behaviour is bit-identical for installers up +to 4 GiB. Where `streampos` / `seekg(off_t)` interactions now produce +unsigned-to-signed warnings on the wider integers, wrap with +`std::streamoff(...)` to keep the conversion explicit and silent. + +A follow-up commit drops the loader-v2 truncation and the +"exceeds 4 GiB" warning so installers above the legacy limit are +parsed natively; another follow-up reads the new 64-bit +block-header `stored_size` for Inno Setup 6.7.0 and later. + +Assisted-by: Claude Opus 4.7 +Signed-off-by: Johannes Schindelin +--- + src/cli/debug.cpp | 2 +- + src/cli/extract.cpp | 6 +++--- + src/loader/offsets.cpp | 4 ++-- + src/loader/offsets.hpp | 14 +++++++------- + src/stream/chunk.hpp | 4 ++-- + src/stream/slice.cpp | 22 +++++++++++----------- + src/stream/slice.hpp | 8 ++++---- + 7 files changed, 30 insertions(+), 30 deletions(-) + +diff --git a/src/cli/debug.cpp b/src/cli/debug.cpp +index c4e4cd3..78d1c35 100644 +--- a/src/cli/debug.cpp ++++ b/src/cli/debug.cpp +@@ -652,7 +652,7 @@ static void dump_headers(std::istream & is, const setup::version & version, cons + void dump_headers(std::istream & is, const loader::offsets & offsets, const extract_options & o) { + + setup::version version; +- is.seekg(offsets.header_offset); ++ is.seekg(std::streamoff(offsets.header_offset)); + version.load(is); + + dump_headers(is, version, o, 0); +diff --git a/src/cli/extract.cpp b/src/cli/extract.cpp +index 4989311..ccff1bb 100644 +--- a/src/cli/extract.cpp ++++ b/src/cli/extract.cpp +@@ -946,7 +946,7 @@ void process_file(const fs::path & installer, const extract_options & o) { + + if(o.data_version) { + setup::version version; +- ifs.seekg(offsets.header_offset); ++ ifs.seekg(std::streamoff(offsets.header_offset)); + version.load(ifs); + if(o.silent) { + std::cout << version << '\n'; +@@ -985,7 +985,7 @@ void process_file(const fs::path & installer, const extract_options & o) { + } + #endif + +- ifs.seekg(offsets.header_offset); ++ ifs.seekg(std::streamoff(offsets.header_offset)); + setup::info info; + try { + info.load(ifs, entries, o.codepage); +@@ -1100,7 +1100,7 @@ void process_file(const fs::path & installer, const extract_options & o) { + if(o.test || o.extract) { + boost::uint64_t offset = info.data_entries[file.entry().location].uncompressed_size; + boost::uint32_t sort_slice = info.data_entries[file.entry().location].chunk.first_slice; +- boost::uint32_t sort_offset = info.data_entries[file.entry().location].chunk.sort_offset; ++ boost::uint64_t sort_offset = info.data_entries[file.entry().location].chunk.sort_offset; + BOOST_FOREACH(boost::uint32_t location, file.entry().additional_locations) { + setup::data_entry & data = info.data_entries[location]; + files_for_location[location].push_back(output_location(&file, offset)); +diff --git a/src/loader/offsets.cpp b/src/loader/offsets.cpp +index 80bcdc4..6285f0c 100644 +--- a/src/loader/offsets.cpp ++++ b/src/loader/offsets.cpp +@@ -109,9 +109,9 @@ bool offsets::load_from_exe_resource(std::istream & is) { + return load_offsets_at(is, resource.offset); + } + +-bool offsets::load_offsets_at(std::istream & is, boost::uint32_t pos) { ++bool offsets::load_offsets_at(std::istream & is, boost::uint64_t pos) { + +- if(is.seekg(pos).fail()) { ++ if(is.seekg(std::streamoff(pos)).fail()) { + is.clear(); + debug("could not seek to loader header"); + return false; +diff --git a/src/loader/offsets.hpp b/src/loader/offsets.hpp +index f49bd91..cefefb2 100644 +--- a/src/loader/offsets.hpp ++++ b/src/loader/offsets.hpp +@@ -60,17 +60,17 @@ struct offsets { + * + * A value of \c 0 means there is no setup.e32 embedded in this file + */ +- boost::uint32_t exe_offset; ++ boost::uint64_t exe_offset; + + /*! + * Size of `setup.e32` after compression, in bytes + * + * A value of \c 0 means the executable size is not known + */ +- boost::uint32_t exe_compressed_size; ++ boost::uint64_t exe_compressed_size; + + //! Size of `setup.e32` before compression, in bytes +- boost::uint32_t exe_uncompressed_size; ++ boost::uint64_t exe_uncompressed_size; + + /*! + * Checksum of `setup.e32` before compression +@@ -80,7 +80,7 @@ struct offsets { + crypto::checksum exe_checksum; + + //! Offset of embedded setup messages +- boost::uint32_t message_offset; ++ boost::uint64_t message_offset; + + /*! + * Offset of embedded `setup-0.bin` data (the setup headers) +@@ -93,7 +93,7 @@ struct offsets { + * + * Loading the version and headers is done in \ref setup::info. + */ +- boost::uint32_t header_offset; ++ boost::uint64_t header_offset; + + /*! + * Offset of embedded `setup-1.bin` data +@@ -109,7 +109,7 @@ struct offsets { + * The layout of the chunks and files is stored in the \ref setup::data_entry headers + * while the \ref setup::file_entry headers provide the filenames and meta information. + */ +- boost::uint32_t data_offset; ++ boost::uint64_t data_offset; + + /*! + * \brief Find the setup loader offsets in a file +@@ -128,7 +128,7 @@ private: + + bool load_from_exe_resource(std::istream & is); + +- bool load_offsets_at(std::istream & is, boost::uint32_t pos); ++ bool load_offsets_at(std::istream & is, boost::uint64_t pos); + + }; + +diff --git a/src/stream/chunk.hpp b/src/stream/chunk.hpp +index fae3484..16c3ccb 100644 +--- a/src/stream/chunk.hpp ++++ b/src/stream/chunk.hpp +@@ -81,9 +81,9 @@ struct chunk { + boost::uint32_t first_slice; //!< Slice where the chunk starts. + boost::uint32_t last_slice; //!< Slice where the chunk ends. + +- boost::uint32_t sort_offset; ++ boost::uint64_t sort_offset; + +- boost::uint32_t offset; //!< Offset of the compressed chunk in firstSlice. ++ boost::uint64_t offset; //!< Offset of the compressed chunk in firstSlice. + boost::uint64_t size; //! Total compressed size of the chunk. + + compression_method compression; //!< Compression method used by the chunk. +diff --git a/src/stream/slice.cpp b/src/stream/slice.cpp +index c638f40..6e28db0 100644 +--- a/src/stream/slice.cpp ++++ b/src/stream/slice.cpp +@@ -50,7 +50,7 @@ const char slice_ids[][8] = { + + } // anonymous namespace + +-slice_reader::slice_reader(std::istream * istream, boost::uint32_t offset) ++slice_reader::slice_reader(std::istream * istream, boost::uint64_t offset) + : data_offset(offset), + slices_per_disk(1), current_slice(0), slice_size(0), + is(istream) { +@@ -59,8 +59,8 @@ slice_reader::slice_reader(std::istream * istream, boost::uint32_t offset) + + std::streampos file_size = is->seekg(0, std::ios_base::end).tellg(); + +- slice_size = boost::uint32_t(std::min(file_size, max_size)); +- if(is->seekg(data_offset).fail()) { ++ slice_size = boost::uint64_t(std::min(file_size, max_size)); ++ if(is->seekg(std::streamoff(data_offset)).fail()) { + throw slice_error("could not seek to data"); + } + } +@@ -125,12 +125,12 @@ bool slice_reader::open_file(const path_type & file) { + if(ifs.fail()) { + ifs.close(); + throw slice_error("could not read slice size in \"" + file.string() + "\""); +- } else if(std::streampos(slice_size) > file_size) { ++ } else if(std::streampos(std::streamoff(slice_size)) > file_size) { + ifs.close(); + std::ostringstream oss; + oss << "bad slice size in " << file << ": " << slice_size << " > " << file_size; + throw slice_error(oss.str()); +- } else if(std::streampos(slice_size) < ifs.tellg()) { ++ } else if(std::streampos(std::streamoff(slice_size)) < ifs.tellg()) { + ifs.close(); + std::ostringstream oss; + oss << "bad slice size in " << file << ": " << slice_size << " < " << ifs.tellg(); +@@ -208,7 +208,7 @@ void slice_reader::open(size_t slice) { + throw slice_error(oss.str()); + } + +-bool slice_reader::seek(size_t slice, boost::uint32_t offset) { ++bool slice_reader::seek(size_t slice, boost::uint64_t offset) { + + seek(slice); + +@@ -218,7 +218,7 @@ bool slice_reader::seek(size_t slice, boost::uint32_t offset) { + return false; + } + +- if(is->seekg(offset).fail()) { ++ if(is->seekg(std::streamoff(offset)).fail()) { + return false; + } + +@@ -233,21 +233,21 @@ std::streamsize slice_reader::read(char * buffer, std::streamsize bytes) { + + while(bytes > 0) { + +- boost::uint32_t read_pos = boost::uint32_t(is->tellg()); ++ boost::uint64_t read_pos = boost::uint64_t(is->tellg()); + if(read_pos > slice_size) { + break; + } +- boost::uint32_t remaining = slice_size - read_pos; ++ boost::uint64_t remaining = slice_size - read_pos; + if(!remaining) { + seek(current_slice + 1); +- read_pos = boost::uint32_t(is->tellg()); ++ read_pos = boost::uint64_t(is->tellg()); + if(read_pos > slice_size) { + break; + } + remaining = slice_size - read_pos; + } + +- boost::uint64_t toread = std::min(boost::uint64_t(remaining), boost::uint64_t(bytes)); ++ boost::uint64_t toread = std::min(remaining, boost::uint64_t(bytes)); + toread = std::min(toread, boost::uint64_t(std::numeric_limits::max())); + if(is->read(buffer, std::streamsize(toread)).fail()) { + break; +diff --git a/src/stream/slice.hpp b/src/stream/slice.hpp +index b2ec52c..d0b3bc0 100644 +--- a/src/stream/slice.hpp ++++ b/src/stream/slice.hpp +@@ -59,7 +59,7 @@ class slice_reader : public boost::iostreams::source { + typedef boost::filesystem::path path_type; + + // Information for reading embedded setup data +- const boost::uint32_t data_offset; ++ const boost::uint64_t data_offset; + + // Information for eading external setup data + path_type dir; //!< Slice directory specified at construction. +@@ -69,7 +69,7 @@ class slice_reader : public boost::iostreams::source { + + // Information about the current slice + size_t current_slice; //!< Number of the currently opened slice. +- boost::uint32_t slice_size; //!< Size in bytes of the currently opened slice. ++ boost::uint64_t slice_size; //!< Size in bytes of the currently opened slice. + + // Streams + util::ifstream ifs; //!< File input stream used when reading from external slices. +@@ -97,7 +97,7 @@ public: + * The constructed reader will allow reading the byte range [data_offset, file end) + * from the setup executable and provide this as the range [0, file end - data_offset). + */ +- slice_reader(std::istream * istream, boost::uint32_t offset); ++ slice_reader(std::istream * istream, boost::uint64_t offset); + + /*! + * Construct a \ref slice_reader to read from external data slices (aka disks). +@@ -126,7 +126,7 @@ public: + * \return \c false if the requested slice could not be opened, or if the requested + * offset is not a valid position in that slice - \c true otherwise. + */ +- bool seek(size_t slice, boost::uint32_t offset); ++ bool seek(size_t slice, boost::uint64_t offset); + + /*! + * Read a number of bytes starting at the current slice and offset within that slice. +-- +2.54.0.windows.1 + diff --git a/mingw-w64-innoextract/0006-loader-block-consume-native-64-bit-offsets-and-sizes.patch b/mingw-w64-innoextract/0006-loader-block-consume-native-64-bit-offsets-and-sizes.patch new file mode 100644 index 0000000000000..fdfbc6f28ca0f --- /dev/null +++ b/mingw-w64-innoextract/0006-loader-block-consume-native-64-bit-offsets-and-sizes.patch @@ -0,0 +1,121 @@ +From b7388dfd161f8de29085e68fafbc32d0250c8924 Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Tue, 5 May 2026 13:07:43 +0200 +Subject: [PATCH 06/11] loader, block: consume native 64-bit offsets and sizes + +Now that loader::offsets, stream::chunk, stream::slice_reader and the +block-reader local size hold their values in uint64_t, drop the +truncating uint32_t casts that the previous "loader: support setup +loader offset table revision 2" commit had to use as a placeholder. +The loader-revision-2 path stores the full 64-bit OffsetEXE / Offset0 +/ Offset1 directly, and the warning about offsets exceeding 4 GiB is +gone because there is no longer any truncation happening; only a +much narrower sanity check for negative values remains, since the +on-disk fields are signed Int64 in Inno Setup's struct. + +stream/block.cpp grows a 6.7.0+ branch in block_reader::get that +loads stored_size as uint64_t to match the on-disk widening +introduced in Inno Setup 6.7.0 (TSetupLdrCompressedBlockHeader's +StoredSize was bumped from LongWord to Int64 in +Projects/Src/Stream.BlockReader.pas at tag is-6_7_0 in +https://github.com/jrsoftware/issrc). With the local stored_size +already widened by the previous refactor, the rest of block_reader +needs no change: the boost::iostreams::restrict slice still receives +the value unchanged via std::streamoff, and the per-4 KiB-subblock +checksum-overhead arithmetic now operates on uint64_t directly so +the explicit uint32_t cast falls away. + +Without this commit we observed `block header CRC32 mismatch` on +6.7.0 installers because the older code read just 4 bytes of a +freshly-widened 8-byte StoredSize and then mistook the next 4 bytes +(actually the upper half of StoredSize) for the compressed flag and +the trailing CRC, causing the actual_checksum.finalize() check to +fail. The mismatch is what motivated the type-widening refactor in +the previous commit. + +Assisted-by: Claude Opus 4.7 +Signed-off-by: Johannes Schindelin +--- + src/loader/offsets.cpp | 13 +++++-------- + src/stream/block.cpp | 17 +++++++++++++---- + 2 files changed, 18 insertions(+), 12 deletions(-) + +diff --git a/src/loader/offsets.cpp b/src/loader/offsets.cpp +index 6285f0c..2e30a44 100644 +--- a/src/loader/offsets.cpp ++++ b/src/loader/offsets.cpp +@@ -172,20 +172,17 @@ bool offsets::load_offsets_at(std::istream & is, boost::uint64_t pos) { + debug("could not read loader header (revision 2)"); + return false; + } +- boost::int64_t max_uint32 = boost::int64_t(std::numeric_limits::max()); +- if(offset_exe < 0 || offset_exe > max_uint32 || +- offset0 < 0 || offset0 > max_uint32 || +- offset1 < 0 || offset1 > max_uint32) { +- log_warning << "Loader header offsets exceed 4 GiB; truncating to 32-bit"; ++ if(offset_exe < 0 || offset0 < 0 || offset1 < 0) { ++ log_warning << "Loader header has negative offset(s)"; + } +- exe_offset = boost::uint32_t(offset_exe); ++ exe_offset = boost::uint64_t(offset_exe); + exe_compressed_size = 0; + exe_uncompressed_size = uncompressed_exe; + exe_checksum.type = crypto::CRC32; + exe_checksum.crc32 = crc_exe; + message_offset = 0; +- header_offset = boost::uint32_t(offset0); +- data_offset = boost::uint32_t(offset1); ++ header_offset = boost::uint64_t(offset0); ++ data_offset = boost::uint64_t(offset1); + boost::uint32_t expected = util::load(is); + if(is.fail()) { + is.clear(); +diff --git a/src/stream/block.cpp b/src/stream/block.cpp +index 2a90c20..9a7d958 100644 +--- a/src/stream/block.cpp ++++ b/src/stream/block.cpp +@@ -158,12 +158,21 @@ block_reader::pointer block_reader::get(std::istream & base, const setup::versio + crypto::crc32 actual_checksum; + actual_checksum.init(); + +- boost::uint32_t stored_size; ++ boost::uint64_t stored_size; + block_compression compression; + + if(version >= INNO_VERSION(4, 0, 9)) { + +- stored_size = actual_checksum.load(base); ++ if(version >= INNO_VERSION(6, 7, 0)) { ++ // Inno Setup 6.7.0 widened the block-header stored_size field ++ // from 4 to 8 bytes; the on-disk struct is now Int64 instead of ++ // LongWord, allowing data blocks larger than 4 GiB. See ++ // Projects/Src/Stream.BlockReader.pas at tag is-6_7_0 in ++ // https://github.com/jrsoftware/issrc. ++ stored_size = actual_checksum.load(base); ++ } else { ++ stored_size = actual_checksum.load(base); ++ } + boost::uint8_t compressed = actual_checksum.load(base); + + compression = compressed ? (version >= INNO_VERSION(4, 1, 6) ? LZMA1 : Zlib) : Stored; +@@ -180,7 +189,7 @@ block_reader::pointer block_reader::get(std::istream & base, const setup::versio + } + + // Add the size of a CRC32 checksum for each 4KiB subblock. +- stored_size += boost::uint32_t(util::ceildiv(stored_size, 4096) * 4); ++ stored_size += util::ceildiv(stored_size, 4096) * 4; + } + + if(actual_checksum.finalize() != expected_checksum) { +@@ -204,7 +213,7 @@ block_reader::pointer block_reader::get(std::istream & base, const setup::versio + + fis->push(inno_block_filter(), 4096); + +- fis->push(io::restrict(base, 0, stored_size)); ++ fis->push(io::restrict(base, std::streamoff(0), std::streamoff(stored_size))); + + fis->exceptions(std::ios_base::badbit | std::ios_base::failbit); + +-- +2.54.0.windows.1 + diff --git a/mingw-w64-innoextract/0007-util-storedenum-let-stored_flag_reader-pad-to-a-fixe.patch b/mingw-w64-innoextract/0007-util-storedenum-let-stored_flag_reader-pad-to-a-fixe.patch new file mode 100644 index 0000000000000..eb5e1b19cffb8 --- /dev/null +++ b/mingw-w64-innoextract/0007-util-storedenum-let-stored_flag_reader-pad-to-a-fixe.patch @@ -0,0 +1,61 @@ +From 0e92dbc4b09405bd7f4d506fc4b21fc0ae767bbb Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Wed, 6 May 2026 15:56:44 +0200 +Subject: [PATCH 07/11] util/storedenum: let stored_flag_reader pad to a fixed + byte count + +Inno Setup 6.7.0 padded both TSetupHeaderOption and TSetupFileEntryOption +to 57 elements (foUnusedPadding/shUnusedPadding = 56) so that the +serialised `packed set` always occupies 8 bytes regardless of how many +flags are actually defined. The motivation in issrc is to keep 32-bit +and 64-bit Delphi builds bit-compatible: a `set` with more than 32 +flags has a minimum size of 8 bytes in 64-bit Delphi, so 32-bit +builds need to match by reserving the same width. + +innoextract reads bitsets through stored_flag_reader<>, which sizes +itself to the number of flags the caller adds. To support the new +padding without exposing the internal byte counter or duplicating the +reader API, add a small public helper that drains additional +padding bytes from the stream until at least `target` bytes have been +consumed in total. A bytes_consumed() getter is added too so callers +can introspect the count if needed. + +The follow-up commits that bring innoextract up to Inno Setup 6.7.0 +use this from header::load_flags and file_entry::load. + +Assisted-by: Claude Opus 4.7 +Signed-off-by: Johannes Schindelin +--- + src/util/storedenum.hpp | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/src/util/storedenum.hpp b/src/util/storedenum.hpp +index 0ccf333..00aec39 100644 +--- a/src/util/storedenum.hpp ++++ b/src/util/storedenum.hpp +@@ -244,6 +244,22 @@ public: + return result; + } + ++ //! Number of stored bytes already read from the stream. ++ size_t bytes_consumed() const { return bytes; } ++ ++ /*! ++ * Discard padding bytes so that the total number of bytes read from ++ * the stream is at least `target`. Used for setups (e.g. Inno Setup ++ * 6.7.0) that always pad the bitset to a fixed width regardless of ++ * the number of flags actually defined. ++ */ ++ void discard_padding_to(size_t target) { ++ while(bytes < target) { ++ (void)util::load(stream); ++ bytes++; ++ } ++ } ++ + }; + + template +-- +2.54.0.windows.1 + diff --git a/mingw-w64-innoextract/0008-Add-support-for-the-Inno-Setup-6.5.0-entry-stream-re.patch b/mingw-w64-innoextract/0008-Add-support-for-the-Inno-Setup-6.5.0-entry-stream-re.patch new file mode 100644 index 0000000000000..678487207d528 --- /dev/null +++ b/mingw-w64-innoextract/0008-Add-support-for-the-Inno-Setup-6.5.0-entry-stream-re.patch @@ -0,0 +1,625 @@ +From cc1444754d1ac68c59acb91f60bfb22ac2b34acf Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Wed, 6 May 2026 20:33:46 +0200 +Subject: [PATCH 08/11] Add support for the Inno Setup 6.5.0 entry stream + rework + +Inno Setup 6.5.0 made several invasive changes to the on-disk +serialisation that go well beyond the SetupID magic and the new +SevenZipLibraryName header field that the previous patch picked up. To +actually parse a 6.5.0 installer, four further changes are needed. + +First, a new TSetupEncryptionHeader record now sits on the outer +stream, between the version magic and the first compressed block +(Projects/Src/Shared.Struct.pas at tag is-6_5_0 in +https://github.com/jrsoftware/issrc). It carries a 4-byte expected +CRC32, a 1-byte EncryptionUse flag, a 16-byte KDFSalt, the KDF +iteration count, the 24-byte BaseNonce triple +(RandomXorStartOffset / RandomXorFirstSlice / RemainingRandom) and a +4-byte PasswordTest, with the CRC32 covering the 49 bytes after +itself. Read it on the outer stream so the block reader starts at the +right offset; if the EncryptionUse byte is non-zero, log a warning +that decrypting 6.5.0+ encrypted installers is not yet supported. +Validate the CRC32 against expected_crc and warn on mismatch. + +Second, the matching encryption fields move out of TSetupHeader. The +4-byte SHA-256 digest prefix and the 44-byte salt-plus-nonce blob that +6.4.0 added directly to the header are no longer present in 6.5.0; the +shEncryptionUsed flag is also dropped from TSetupHeaderOption. Gate +the existing 6.4.x reads to ``< 6.5.0`` and clear the +header.password / password_salt members for 6.5.0+ so that downstream +code sees the same "no embedded password" state. + +Third, the new TSetupISSigKeyEntry array sits between the directory +and file entry streams. Read NumISSigKeyEntries (already wired up by +the 6.5.0 magic patch as a placeholder, now actually stored in the +new header.issig_key_count). After the directory entries, load +exactly that many records via the standard load_entries helper, +mirroring how component_entry, task_entry, etc. are handled. Each +record holds three binary strings (PublicX, PublicY, RuntimeID) +naming an Ed25519 public key the installer trusts; the new +issig_key_entry struct exposes them so callers that opt in via the +ISSigKeys entry-type flag can inspect them. + +Fourth, when a 6.5.0+ installer sets [Setup] SevenZipLibraryName +(supporting Extract7ZipArchive that was added in 6.4.0), an additional +binary string appears on the wizard / decompressor stream after the +decompressor DLL and before the decryptor DLL slot. Skip past it so +later reads of decrypt_dll align correctly. + +In TSetupFileEntry, 6.5.0 appended five new expression strings +(Excludes, DownloadISSigSource, DownloadUserName, DownloadPassword, +ExtractArchivePassword) and a per-file Verification packed record +made of an ISSigAllowedKeys ansi string, a 32-byte SHA-256 digest, and +a TSetupFileVerificationType byte enum (none / hash / IS sig); these +sit between BeforeInstall and MinVersion. Two new flag bits +foDownload and foExtractArchive were added to TSetupFileEntryOption +for the new [Files] flags `download` and `extractarchive`. Add the +matching members and stored_enum mapping, gated to `>= 6.5.0`. The +SHA-256 digest is stored in the existing file_entry::checksum field +(unused before 6.5.0 in upstream innoextract); the trailing +unconditional reset of that field is gated to `< 6.5.0` so the +verification hash survives. + +In TSetupFileLocationEntry, Inno Setup 6.4.3 (issrc commits 6aec0a55 +"Distinguish file options (fo) and file location options (flo)." and +00d335b7 "Cleanup: TSetupFileLocationEntry contained a few things +which Setup doesn't need and are only for the compiler.", both first +tagged in is-6_4_3) had already dropped four flags +(foVersionInfoNotValid, foIsUninstExe, foApplyTouchDateTime, +foSolidBreak) from the Flags bitset and removed the standalone Sign +field entirely; the [Files] `signonce` and `signcheck` directives +still record a per-file location signing intent at compile time but +the result is no longer serialised. Gate the four obsolete flag adds +to `< 6.4.3` and short-circuit the Sign read for `>= 6.4.3` to +NoSetting. + +Assisted-by: Claude Opus 4.7 +Signed-off-by: Johannes Schindelin +--- + CMakeLists.txt | 2 ++ + src/setup/data.cpp | 27 ++++++++++++++++---- + src/setup/file.cpp | 58 +++++++++++++++++++++++++++++++++++++++++- + src/setup/file.hpp | 19 +++++++++++++- + src/setup/header.cpp | 29 ++++++++++++++++----- + src/setup/header.hpp | 1 + + src/setup/info.cpp | 49 +++++++++++++++++++++++++++++++++++ + src/setup/info.hpp | 3 +++ + src/setup/issigkey.cpp | 38 +++++++++++++++++++++++++++ + src/setup/issigkey.hpp | 55 +++++++++++++++++++++++++++++++++++++++ + 10 files changed, 267 insertions(+), 14 deletions(-) + create mode 100644 src/setup/issigkey.cpp + create mode 100644 src/setup/issigkey.hpp + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 6995610..cd21eaf 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -416,6 +416,8 @@ set(INNOEXTRACT_SOURCES + src/setup/info.cpp + src/setup/ini.hpp + src/setup/ini.cpp ++ src/setup/issigkey.hpp ++ src/setup/issigkey.cpp + src/setup/item.hpp + src/setup/item.cpp + src/setup/language.hpp +diff --git a/src/setup/data.cpp b/src/setup/data.cpp +index 4444ce2..a269c3c 100644 +--- a/src/setup/data.cpp ++++ b/src/setup/data.cpp +@@ -136,20 +136,28 @@ void data_entry::load(std::istream & is, const info & i) { + stored_flag_reader flagreader(is, i.version.bits()); + + flagreader.add(VersionInfoValid); +- flagreader.add(VersionInfoNotValid); ++ if(i.version < INNO_VERSION(6, 4, 3)) { ++ // Inno Setup 6.4.3 dropped foVersionInfoNotValid, foIsUninstExe, ++ // foApplyTouchDateTime and foSolidBreak from ++ // TSetupFileLocationEntry.Flags. See ++ // Projects/Src/Shared.Struct.pas at tag is-6_4_3 in ++ // https://github.com/jrsoftware/issrc (issrc commit 6aec0a55, ++ // "Distinguish file options (fo) and file location options (flo)."). ++ flagreader.add(VersionInfoNotValid); ++ } + if(i.version >= INNO_VERSION(2, 0, 17) && i.version < INNO_VERSION(4, 0, 1)) { + flagreader.add(BZipped); + } + if(i.version >= INNO_VERSION(4, 0, 10)) { + flagreader.add(TimeStampInUTC); + } +- if(i.version >= INNO_VERSION(4, 1, 0)) { ++ if(i.version >= INNO_VERSION(4, 1, 0) && i.version < INNO_VERSION(6, 4, 3)) { + flagreader.add(IsUninstallerExe); + } + if(i.version >= INNO_VERSION(4, 1, 8)) { + flagreader.add(CallInstructionOptimized); + } +- if(i.version >= INNO_VERSION(4, 2, 0)) { ++ if(i.version >= INNO_VERSION(4, 2, 0) && i.version < INNO_VERSION(6, 4, 3)) { + flagreader.add(Touch); + } + if(i.version >= INNO_VERSION(4, 2, 2)) { +@@ -160,7 +168,7 @@ void data_entry::load(std::istream & is, const info & i) { + } else { + options |= ChunkCompressed; + } +- if(i.version >= INNO_VERSION(5, 1, 13)) { ++ if(i.version >= INNO_VERSION(5, 1, 13) && i.version < INNO_VERSION(6, 4, 3)) { + flagreader.add(SolidBreak); + } + if(i.version >= INNO_VERSION(5, 5, 7) && i.version < INNO_VERSION(6, 3, 0)) { +@@ -171,7 +179,16 @@ void data_entry::load(std::istream & is, const info & i) { + + options |= flagreader.finalize(); + +- if(i.version >= INNO_VERSION(6, 3, 0)) { ++ if(i.version >= INNO_VERSION(6, 4, 3)) { ++ // Inno Setup 6.4.3 dropped the standalone Sign field from ++ // TSetupFileLocationEntry. The compiler still records the source ++ // signing intent in the [Files] section, but it is no longer ++ // serialised per-file-location. See issrc commit 00d335b7 ++ // ("Cleanup: TSetupFileLocationEntry contained a few things which ++ // Setup doesn't need and are only for the compiler.") first ++ // tagged in is-6_4_3. ++ sign = NoSetting; ++ } else if(i.version >= INNO_VERSION(6, 3, 0)) { + sign = stored_enum(is).get(); + } else if(options & SignOnce) { + sign = Once; +diff --git a/src/setup/file.cpp b/src/setup/file.cpp +index a2e917e..f886409 100644 +--- a/src/setup/file.cpp ++++ b/src/setup/file.cpp +@@ -56,6 +56,14 @@ STORED_ENUM_MAP(stored_file_type_1, file_entry::UserFile, + file_entry::RegSvrExe, + ); + ++// Inno Setup 6.5.0 introduced the per-file verification kind alongside ++// the SHA-256 hash and ISSig allowed-keys list appended to TSetupFileEntry. ++STORED_ENUM_MAP(stored_file_verification_type, file_entry::FileVerificationNone, ++ file_entry::FileVerificationNone, ++ file_entry::FileVerificationHash, ++ file_entry::FileVerificationISSig, ++); ++ + } // anonymous namespace + + } // namespace setup +@@ -69,6 +77,12 @@ NAMES(setup::file_copy_mode, "File Copy Mode", + "always skip if same or older", + ) + ++NAMES(setup::file_entry::file_verification_type, "File Verification Type", ++ "none", ++ "hash", ++ "IS sig", ++) ++ + namespace setup { + + void file_entry::load(std::istream & is, const info & i) { +@@ -92,6 +106,34 @@ void file_entry::load(std::istream & is, const info & i) { + + load_condition_data(is, i); + ++ if(i.version >= INNO_VERSION(6, 5, 0)) { ++ // Inno Setup 6.5.0 appended five new expression strings (Excludes, ++ // DownloadISSigSource, DownloadUserName, DownloadPassword, ++ // ExtractArchivePassword) and a per-file Verification packed ++ // record (ISSigAllowedKeys ansi string, 32-byte SHA-256 digest, ++ // TSetupFileVerificationType byte enum) to TSetupFileEntry, after ++ // the BeforeInstall string and before MinVersion. See ++ // Projects/Src/Shared.Struct.pas at tag is-6_5_0 in ++ // https://github.com/jrsoftware/issrc. ++ is >> util::encoded_string(excludes, i.codepage, i.header.lead_bytes); ++ is >> util::encoded_string(download_source, i.codepage, i.header.lead_bytes); ++ is >> util::encoded_string(download_user, i.codepage, i.header.lead_bytes); ++ is >> util::encoded_string(download_password, i.codepage, i.header.lead_bytes); ++ is >> util::encoded_string(archive_password, i.codepage, i.header.lead_bytes); ++ is >> util::ansi_string(issig_allowed_keys); ++ is.read(checksum.sha256, std::streamsize(sizeof(checksum.sha256))); ++ checksum.type = crypto::SHA256; ++ verification = stored_enum(is).get(); ++ } else { ++ excludes.clear(); ++ download_source.clear(); ++ download_user.clear(); ++ download_password.clear(); ++ archive_password.clear(); ++ issig_allowed_keys.clear(); ++ verification = FileVerificationNone; ++ } ++ + load_version_data(is, i.version); + + location = util::load(is, i.version.bits()); +@@ -189,6 +231,14 @@ void file_entry::load(std::istream & is, const info & i) { + if(i.version >= INNO_VERSION(5, 2, 5)) { + flagreader.add(GacInstall); + } ++ if(i.version >= INNO_VERSION(6, 5, 0)) { ++ // Inno Setup 6.5.0 added two flags at the end of the bitset for ++ // the new [Files] flags `download` and `extractarchive` ++ // (TSetupFileEntry.Options in Projects/Src/Shared.Struct.pas at ++ // tag is-6_5_0 in https://github.com/jrsoftware/issrc). ++ flagreader.add(Download); ++ flagreader.add(ExtractArchive); ++ } + + options |= flagreader.finalize(); + +@@ -199,7 +249,11 @@ void file_entry::load(std::istream & is, const info & i) { + } + + additional_locations.clear(); +- checksum.type = crypto::None; ++ if(i.version < INNO_VERSION(6, 5, 0)) { ++ // For Inno Setup 6.5.0+, file_entry::checksum carries the SHA-256 ++ // from the per-file ISSig Verification block, populated above. ++ checksum.type = crypto::None; ++ } + size = 0; + + } +@@ -239,6 +293,8 @@ NAMES(setup::file_entry::flags, "File Option", + "set ntfs compression", + "unset ntfs compression", + "gac install", ++ "download", ++ "extract archive", + "readme", + ) + +diff --git a/src/setup/file.hpp b/src/setup/file.hpp +index 604b2ae..9f306bf 100644 +--- a/src/setup/file.hpp ++++ b/src/setup/file.hpp +@@ -77,11 +77,19 @@ struct file_entry : public item { + SetNtfsCompression, + UnsetNtfsCompression, + GacInstall, ++ Download, ++ ExtractArchive, + + // obsolete options: + IsReadmeFile + ); + ++ enum file_verification_type { ++ FileVerificationNone, ++ FileVerificationHash, ++ FileVerificationISSig, ++ }; ++ + enum file_type { + UserFile, + UninstExe, +@@ -96,6 +104,14 @@ struct file_entry : public item { + std::string destination; + std::string install_font_name; + std::string strong_assembly_name; ++ std::string excludes; //!< Inno Setup 6.5.0+ ++ std::string download_source; //!< DownloadISSigSource, 6.5.0+ ++ std::string download_user; //!< DownloadUserName, 6.5.0+ ++ std::string download_password; //!< 6.5.0+ ++ std::string archive_password; //!< ExtractArchivePassword, 6.5.0+ ++ std::string issig_allowed_keys; //!< 6.5.0+ ++ ++ file_verification_type verification; //!< 6.5.0+ + + boost::uint32_t location; //!< index into the data entry list + boost::uint32_t attributes; +@@ -110,7 +126,7 @@ struct file_entry : public item { + // Information about GOG Galaxy multi-part files + // These are not used in normal Inno Setup installers + std::vector additional_locations; +- crypto::checksum checksum; ++ crypto::checksum checksum; //!< Inno Setup 6.5.0+: SHA-256 from the per-file ISSig Verification block + boost::uint64_t size; + + void load(std::istream & is, const info & i); +@@ -121,5 +137,6 @@ struct file_entry : public item { + + NAMED_FLAGS(setup::file_entry::flags) + NAMED_ENUM(setup::file_entry::file_type) ++NAMED_ENUM(setup::file_entry::file_verification_type) + + #endif // INNOEXTRACT_SETUP_FILE_HPP +diff --git a/src/setup/header.cpp b/src/setup/header.cpp +index 87b30b2..1babfbe 100644 +--- a/src/setup/header.cpp ++++ b/src/setup/header.cpp +@@ -338,10 +338,14 @@ void header::load(std::istream & is, const version & version) { + if(version >= INNO_VERSION(6, 5, 0)) { + // Inno Setup 6.5.0 inserted a NumISSigKeyEntries: Integer field + // between NumDirEntries and NumFileEntries +- // (Projects/Src/Shared.Struct.pas at tag is-6_5_0). innoextract +- // does not yet use the count, but the bytes must be consumed to +- // keep the stream position correct. +- (void)util::load(is, version.bits()); ++ // (Projects/Src/Shared.Struct.pas at tag is-6_5_0). Each entry is a ++ // triple of binary strings (PublicX, PublicY, RuntimeID) describing ++ // an ISSigTool-issued public key the installer trusts; innoextract ++ // does not yet make use of the keys themselves but stores the count ++ // so info::try_load can skip the right number of entries. ++ issig_key_count = util::load(is, version.bits()); ++ } else { ++ issig_key_count = 0; + } + file_count = util::load(is, version.bits()); + data_entry_count = util::load(is, version.bits()); +@@ -436,7 +440,16 @@ void header::load(std::istream & is, const version & version) { + wizard_image_opacity = 0xff; + } + +- if(version >= INNO_VERSION(6, 4, 0)) { ++ if(version >= INNO_VERSION(6, 5, 0)) { ++ // Inno Setup 6.5.0 moved the encryption fields (password test, KDF ++ // salt, KDF iterations, base nonce) out of TSetupHeader and into a ++ // separate TSetupEncryptionHeader stored elsewhere. innoextract ++ // does not yet parse encrypted 6.5.0+ installers; for unencrypted ++ // ones the right thing is to read no encryption bytes from the ++ // header stream at all. ++ password.type = crypto::None; ++ password_salt.clear(); ++ } else if(version >= INNO_VERSION(6, 4, 0)) { + is.read(password.sha256, 4); + password.type = crypto::PBKDF2_SHA256_XChaCha20; + } else if(version >= INNO_VERSION(5, 3, 9)) { +@@ -449,7 +462,9 @@ void header::load(std::istream & is, const version & version) { + password.crc32 = util::load(is); + password.type = crypto::CRC32; + } +- if(version >= INNO_VERSION(6, 4, 0)) { ++ if(version >= INNO_VERSION(6, 5, 0)) { ++ // Already cleared above; nothing to read. ++ } else if(version >= INNO_VERSION(6, 4, 0)) { + password_salt.resize(44); // PBKDF2 salt + iteration count + ChaCha2 base nonce + is.read(&password_salt[0], std::streamsize(password_salt.length())); + } else if(version >= INNO_VERSION(4, 2, 2)) { +@@ -749,7 +764,7 @@ header::flags header::load_flags(std::istream & is, const version & version) { + flagreader.add(AppendDefaultDirName); + flagreader.add(AppendDefaultGroupName); + } +- if(version >= INNO_VERSION(4, 2, 2)) { ++ if(version >= INNO_VERSION(4, 2, 2) && version < INNO_VERSION(6, 5, 0)) { + flagreader.add(EncryptionUsed); + } + if(version >= INNO_VERSION(5, 0, 4) && version < INNO_VERSION(5, 6, 1)) { +diff --git a/src/setup/header.hpp b/src/setup/header.hpp +index 77fa651..a0211fc 100644 +--- a/src/setup/header.hpp ++++ b/src/setup/header.hpp +@@ -188,6 +188,7 @@ struct header { + size_t component_count; + size_t task_count; + size_t directory_count; ++ size_t issig_key_count; + size_t file_count; + size_t data_entry_count; + size_t icon_count; +diff --git a/src/setup/info.cpp b/src/setup/info.cpp +index d696924..0678f36 100644 +--- a/src/setup/info.cpp ++++ b/src/setup/info.cpp +@@ -27,6 +27,7 @@ + #include + + #include "crypto/hasher.hpp" ++#include "crypto/crc32.hpp" + #include "crypto/pbkdf2.hpp" + #include "crypto/sha256.hpp" + #include "crypto/xchacha20.hpp" +@@ -37,6 +38,7 @@ + #include "setup/file.hpp" + #include "setup/icon.hpp" + #include "setup/ini.hpp" ++#include "setup/issigkey.hpp" + #include "setup/item.hpp" + #include "setup/language.hpp" + #include "setup/message.hpp" +@@ -123,6 +125,16 @@ void load_wizard_and_decompressor(std::istream & is, const setup::version & vers + } + } + ++ if(version >= INNO_VERSION(6, 5, 0) && !header.seven_zip_library_name.empty()) { ++ // Inno Setup 6.5.0 added an embedded 7-Zip decoder DLL stream ++ // after the decompressor DLL block, present whenever the [Setup] ++ // directive SevenZipLibraryName is set (which Setup uses to ++ // implement Extract7ZipArchive support added in 6.4.0). Skip it; ++ // innoextract does not need to decode 7-Zip archives during a ++ // listing. ++ util::binary_string::skip(is); ++ } ++ + info.decrypt_dll.clear(); + if((header.options & header::EncryptionUsed) && version < INNO_VERSION(6, 4, 0)) { + if(entries & (info::DecryptDll | info::NoSkip)) { +@@ -204,6 +216,8 @@ void info::try_load(std::istream & is, entry_types entries, util::codepage_id fo + load_entries(*reader, entries, header.task_count, tasks, Tasks); + debug("loading directories"); + load_entries(*reader, entries, header.directory_count, directories, Directories); ++ debug("loading issig keys"); ++ load_entries(*reader, entries, header.issig_key_count, issig_keys, ISSigKeys); + debug("loading files"); + load_entries(*reader, entries, header.file_count, files, Files); + debug("loading icons"); +@@ -263,6 +277,41 @@ void info::load(std::istream & is, entry_types entries, util::codepage_id force_ + entries |= NoSkip; + } + ++ if(version >= INNO_VERSION(6, 5, 0)) { ++ // Inno Setup 6.5.0 split the encryption metadata out of ++ // TSetupHeader into a standalone TSetupEncryptionHeader stored on ++ // the outer stream between the version magic and the first block ++ // (Projects/Src/Shared.Struct.pas at tag is-6_5_0 in ++ // https://github.com/jrsoftware/issrc). Read it here so the block ++ // reader starts at the right offset; innoextract does not yet ++ // support actually decrypting 6.5.0+ encrypted installers. ++ boost::uint32_t expected_crc = util::load(is); ++ crypto::crc32 checksum; ++ checksum.init(); ++ boost::uint8_t encryption_use = checksum.load(is); ++ if(encryption_use != 0) { ++ log_warning << "Encrypted setup not supported; cannot decrypt files"; ++ } ++ // 16-byte KDFSalt + 4-byte KDFIterations ++ // + 8-byte BaseNonce.RandomXorStartOffset ++ // + 4-byte BaseNonce.RandomXorFirstSlice ++ // + 12-byte BaseNonce.RemainingRandom (3 x 4 bytes) ++ // + 4-byte PasswordTest = 48 bytes after encryption_use. ++ for(int i = 0; i < 16; i++) { ++ (void)checksum.load(is); ++ } ++ (void)checksum.load(is); ++ (void)checksum.load(is); ++ (void)checksum.load(is); ++ for(int i = 0; i < 3; i++) { ++ (void)checksum.load(is); ++ } ++ (void)checksum.load(is); ++ if(checksum.finalize() != expected_crc) { ++ log_warning << "Encryption header checksum mismatch!"; ++ } ++ } ++ + bool parsed_without_errors = false; + std::streampos start = is.tellg(); + for(;;) { +diff --git a/src/setup/info.hpp b/src/setup/info.hpp +index e08cfd4..fb8d57b 100644 +--- a/src/setup/info.hpp ++++ b/src/setup/info.hpp +@@ -43,6 +43,7 @@ struct directory_entry; + struct file_entry; + struct icon_entry; + struct ini_entry; ++struct issig_key_entry; + struct language_entry; + struct message_entry; + struct permission_entry; +@@ -69,6 +70,7 @@ struct info { + Files, + Icons, + IniEntries, ++ ISSigKeys, + Languages, + Messages, + Permissions, +@@ -98,6 +100,7 @@ struct info { + std::vector files; //! \c Files + std::vector icons; //! \c Icons + std::vector ini_entries; //! \c IniEntries ++ std::vector issig_keys; //! \c ISSigKeys + std::vector languages; //! \c Languages + std::vector messages; //! \c Messages + std::vector permissions; //! \c Permissions +diff --git a/src/setup/issigkey.cpp b/src/setup/issigkey.cpp +new file mode 100644 +index 0000000..7d3bfc0 +--- /dev/null ++++ b/src/setup/issigkey.cpp +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (C) 2011-2019 Daniel Scharrer ++ * ++ * This software is provided 'as-is', without any express or implied ++ * warranty. In no event will the author(s) be held liable for any damages ++ * arising from the use of this software. ++ * ++ * Permission is granted to anyone to use this software for any purpose, ++ * including commercial applications, and to alter it and redistribute it ++ * freely, subject to the following restrictions: ++ * ++ * 1. The origin of this software must not be misrepresented; you must not ++ * claim that you wrote the original software. If you use this software ++ * in a product, an acknowledgment in the product documentation would be ++ * appreciated but is not required. ++ * 2. Altered source versions must be plainly marked as such, and must not be ++ * misrepresented as being the original software. ++ * 3. This notice may not be removed or altered from any source distribution. ++ */ ++ ++#include "setup/issigkey.hpp" ++ ++#include "util/load.hpp" ++ ++namespace setup { ++ ++void issig_key_entry::load(std::istream & is, const info & /* i */) { ++ ++ // TSetupISSigKeyEntry: PublicX, PublicY, RuntimeID ++ // (Projects/Src/Shared.Struct.pas at tag is-6_5_0 in ++ // https://github.com/jrsoftware/issrc). ++ is >> util::binary_string(public_x); ++ is >> util::binary_string(public_y); ++ is >> util::binary_string(runtime_id); ++ ++} ++ ++} // namespace setup +diff --git a/src/setup/issigkey.hpp b/src/setup/issigkey.hpp +new file mode 100644 +index 0000000..e027d43 +--- /dev/null ++++ b/src/setup/issigkey.hpp +@@ -0,0 +1,55 @@ ++/* ++ * Copyright (C) 2011-2019 Daniel Scharrer ++ * ++ * This software is provided 'as-is', without any express or implied ++ * warranty. In no event will the author(s) be held liable for any damages ++ * arising from the use of this software. ++ * ++ * Permission is granted to anyone to use this software for any purpose, ++ * including commercial applications, and to alter it and redistribute it ++ * freely, subject to the following restrictions: ++ * ++ * 1. The origin of this software must not be misrepresented; you must not ++ * claim that you wrote the original software. If you use this software ++ * in a product, an acknowledgment in the product documentation would be ++ * appreciated but is not required. ++ * 2. Altered source versions must be plainly marked as such, and must not be ++ * misrepresented as being the original software. ++ * 3. This notice may not be removed or altered from any source distribution. ++ */ ++ ++/*! ++ * \file ++ * ++ * Structures for ISSig public key entries stored in Inno Setup 6.5.0+ files. ++ */ ++#ifndef INNOEXTRACT_SETUP_ISSIGKEY_HPP ++#define INNOEXTRACT_SETUP_ISSIGKEY_HPP ++ ++#include ++#include ++ ++namespace setup { ++ ++struct info; ++ ++struct issig_key_entry { ++ ++ // introduced in 6.5.0 ++ ++ // X coordinate of the Ed25519 public key, as raw bytes. ++ std::string public_x; ++ ++ // Y coordinate of the Ed25519 public key, as raw bytes. ++ std::string public_y; ++ ++ // Tool-supplied identifier (typically the ISSigTool key file name). ++ std::string runtime_id; ++ ++ void load(std::istream & is, const info & i); ++ ++}; ++ ++} // namespace setup ++ ++#endif // INNOEXTRACT_SETUP_ISSIGKEY_HPP +-- +2.54.0.windows.1 + diff --git a/mingw-w64-innoextract/0009-data-widen-the-per-file-location-start-offset-to-64-.patch b/mingw-w64-innoextract/0009-data-widen-the-per-file-location-start-offset-to-64-.patch new file mode 100644 index 0000000000000..f80facfc3a8af --- /dev/null +++ b/mingw-w64-innoextract/0009-data-widen-the-per-file-location-start-offset-to-64-.patch @@ -0,0 +1,50 @@ +From 5cb290709b281e8de5a752ae07b7c3eeddc5cd0f Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Wed, 6 May 2026 21:20:26 +0200 +Subject: [PATCH 09/11] data: widen the per-file location start offset to 64 + bits for Inno Setup 6.5.2 + +Inno Setup 6.5.2 widened TSetupFileLocationEntry.StartOffset from +LongWord (4 bytes) to Int64 (8 bytes) in +Projects/Src/Shared.Struct.pas (tag is-6_5_2 in +https://github.com/jrsoftware/issrc), so that the data offset within +a slice can exceed 4 GiB. The matching change for chunk.first_slice +and chunk.last_slice had already happened in 4.0.0 / 5.x; only +StartOffset was still 32-bit. + +Read the new layout as uint64 for `>= 6.5.2`, keep the old 32-bit +read for everything below. The earlier offsets-table widening +patch already widened chunk_location::offset itself to 64 bits, so +the only thing that needed to change here is the read width. + +Assisted-by: Claude Opus 4.7 +Signed-off-by: Johannes Schindelin +--- + src/setup/data.cpp | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/src/setup/data.cpp b/src/setup/data.cpp +index a269c3c..9e48939 100644 +--- a/src/setup/data.cpp ++++ b/src/setup/data.cpp +@@ -56,7 +56,16 @@ void data_entry::load(std::istream & is, const info & i) { + } + } + +- chunk.sort_offset = chunk.offset = util::load(is); ++ if(i.version >= INNO_VERSION(6, 5, 2)) { ++ // Inno Setup 6.5.2 widened TSetupFileLocationEntry.StartOffset ++ // from LongWord (4 bytes) to Int64 (8 bytes) so data offsets ++ // within a slice can exceed 4 GiB. See ++ // Projects/Src/Shared.Struct.pas at tag is-6_5_2 in ++ // https://github.com/jrsoftware/issrc. ++ chunk.sort_offset = chunk.offset = util::load(is); ++ } else { ++ chunk.sort_offset = chunk.offset = util::load(is); ++ } + + if(i.version >= INNO_VERSION(4, 0, 1)) { + file.offset = util::load(is); +-- +2.54.0.windows.1 + diff --git a/mingw-w64-innoextract/0010-Add-support-for-the-Inno-Setup-6.6.0-entry-stream-re.patch b/mingw-w64-innoextract/0010-Add-support-for-the-Inno-Setup-6.6.0-entry-stream-re.patch new file mode 100644 index 0000000000000..35e07152f44f4 --- /dev/null +++ b/mingw-w64-innoextract/0010-Add-support-for-the-Inno-Setup-6.6.0-entry-stream-re.patch @@ -0,0 +1,151 @@ +From 1f1c5ce97d4b44deca39c8fd11130936ac9522e4 Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Wed, 6 May 2026 22:33:50 +0200 +Subject: [PATCH 10/11] Add support for the Inno Setup 6.6.0 entry stream + rework + +Inno Setup 6.6.0 reorganised TSetupLanguageEntry beyond what is +already covered by the matching SetupID magic patch. To actually parse +6.6.0 language entries, the on-disk layout in +Projects/Src/Shared.Struct.pas (tag is-6_6_0 in +https://github.com/jrsoftware/issrc) needs three changes. + +The TitleFontName and CopyrightFontName binary strings were dropped +entirely (the new dialog-font-base-scale machinery makes per-language +title/copyright fonts unnecessary). The matching TitleFontSize and +CopyrightFontSize integers were replaced by +DialogFontBaseScaleHeight and DialogFontBaseScaleWidth, sitting +between DialogFontSize and WelcomeFontSize. And LanguageID narrowed +from Cardinal (4 bytes) to Word (2 bytes) on disk, since the upper +half was always zero anyway. Add corresponding members for the new +scale fields, gate the obsolete reads to ``< 6.6.0``, and read +LanguageID at the right width. + +In TSetupCustomMessageEntry/TSetupPermissionEntry the read order is +unchanged; only the wizard-and-decompressor stream gains material: +6.6.0 added a second copy of each wizard image group right after the +existing main and small image groups, holding the dynamic-dark images +that match the new WantWizardImagesDynamicDark plumbing in +Setup.MainFunc.pas (tag is-6_6_0). Both groups are always serialised +even when dark-mode imagery is not configured; the runtime is what +picks which set to display. innoextract does not expose dark-theme +images yet, but the bytes have to be consumed so the decompressor / +decryptor blocks that follow stay aligned. Read both groups into +local vectors and discard them, gated to ``>= 6.6.0``. + +Assisted-by: Claude Opus 4.7 +Signed-off-by: Johannes Schindelin +--- + src/setup/info.cpp | 11 +++++++++++ + src/setup/language.cpp | 43 +++++++++++++++++++++++++++++++++++++----- + src/setup/language.hpp | 2 ++ + 3 files changed, 51 insertions(+), 5 deletions(-) + +diff --git a/src/setup/info.cpp b/src/setup/info.cpp +index 0678f36..8afe01b 100644 +--- a/src/setup/info.cpp ++++ b/src/setup/info.cpp +@@ -113,6 +113,17 @@ void load_wizard_and_decompressor(std::istream & is, const setup::version & vers + load_wizard_images(is, version, info.wizard_images_small, entries); + } + ++ if(version >= INNO_VERSION(6, 6, 0)) { ++ // Inno Setup 6.6.0 added a second copy of each wizard image ++ // group for the dynamic-dark theme. The on-disk format always ++ // includes both copies regardless of whether dark-mode images ++ // were actually configured; the runtime decides which set to ++ // use based on WantWizardImagesDynamicDark. ++ std::vector dark_images, dark_images_small; ++ load_wizard_images(is, version, dark_images, entries); ++ load_wizard_images(is, version, dark_images_small, entries); ++ } ++ + info.decompressor_dll.clear(); + if(header.compression == stream::BZip2 + || (header.compression == stream::LZMA1 && version == INNO_VERSION(4, 1, 5)) +diff --git a/src/setup/language.cpp b/src/setup/language.cpp +index 6b40813..62be840 100644 +--- a/src/setup/language.cpp ++++ b/src/setup/language.cpp +@@ -140,9 +140,17 @@ void language_entry::load(std::istream & is, const info & i) { + } + + is >> util::binary_string(dialog_font); +- is >> util::binary_string(title_font); ++ if(i.version < INNO_VERSION(6, 6, 0)) { ++ is >> util::binary_string(title_font); ++ } else { ++ title_font.clear(); ++ } + is >> util::binary_string(welcome_font); +- is >> util::binary_string(copyright_font); ++ if(i.version < INNO_VERSION(6, 6, 0)) { ++ is >> util::binary_string(copyright_font); ++ } else { ++ copyright_font.clear(); ++ } + + if(i.version >= INNO_VERSION(4, 0, 0)) { + is >> util::binary_string(data); +@@ -156,7 +164,15 @@ void language_entry::load(std::istream & is, const info & i) { + license_text.clear(), info_before.clear(), info_after.clear(); + } + +- language_id = util::load(is); ++ if(i.version >= INNO_VERSION(6, 6, 0)) { ++ // Inno Setup 6.6.0 narrowed LanguageID from Cardinal (4 bytes) to ++ // Word (2 bytes) on disk; see TSetupLanguageEntry in ++ // Projects/Src/Shared.Struct.pas at tag is-6_6_0 in ++ // https://github.com/jrsoftware/issrc. ++ language_id = util::load(is); ++ } else { ++ language_id = util::load(is); ++ } + + if(i.version < INNO_VERSION(4, 2, 2)) { + codepage = default_codepage_for_language(language_id); +@@ -186,9 +202,26 @@ void language_entry::load(std::istream & is, const info & i) { + dialog_font_standard_height = 0; + } + +- title_font_size = util::load(is); ++ if(i.version >= INNO_VERSION(6, 6, 0)) { ++ // 6.6.0 replaced TitleFontSize and CopyrightFontSize with ++ // DialogFontBaseScaleHeight and DialogFontBaseScaleWidth (each a ++ // 32-bit Integer) sitting between DialogFontSize and ++ // WelcomeFontSize. See TSetupLanguageEntry in ++ // Projects/Src/Shared.Struct.pas at tag is-6_6_0. ++ dialog_font_base_scale_height = util::load(is); ++ dialog_font_base_scale_width = util::load(is); ++ title_font_size = 0; ++ } else { ++ dialog_font_base_scale_height = 0; ++ dialog_font_base_scale_width = 0; ++ title_font_size = util::load(is); ++ } + welcome_font_size = util::load(is); +- copyright_font_size = util::load(is); ++ if(i.version < INNO_VERSION(6, 6, 0)) { ++ copyright_font_size = util::load(is); ++ } else { ++ copyright_font_size = 0; ++ } + + if(i.version == INNO_VERSION_EXT(5, 5, 7, 1)) { + util::load(is); // always 8 or 9? +diff --git a/src/setup/language.hpp b/src/setup/language.hpp +index 8801c64..99b65d0 100644 +--- a/src/setup/language.hpp ++++ b/src/setup/language.hpp +@@ -56,6 +56,8 @@ struct language_entry { + boost::uint32_t codepage; + size_t dialog_font_size; + size_t dialog_font_standard_height; ++ size_t dialog_font_base_scale_height; //!< Inno Setup 6.6.0+ ++ size_t dialog_font_base_scale_width; //!< Inno Setup 6.6.0+ + size_t title_font_size; + size_t welcome_font_size; + size_t copyright_font_size; +-- +2.54.0.windows.1 + diff --git a/mingw-w64-innoextract/0011-Add-support-for-Inno-Setup-6.7.0-and-6.7.1.patch b/mingw-w64-innoextract/0011-Add-support-for-Inno-Setup-6.7.0-and-6.7.1.patch new file mode 100644 index 0000000000000..77afaffa66c0d --- /dev/null +++ b/mingw-w64-innoextract/0011-Add-support-for-Inno-Setup-6.7.0-and-6.7.1.patch @@ -0,0 +1,466 @@ +From 0254c3f11e34701000ad763be7753623f81152bf Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Thu, 7 May 2026 01:08:13 +0200 +Subject: [PATCH 11/11] Add support for Inno Setup 6.7.0 and 6.7.1 + +Inno Setup 6.7.0 made another sweep of changes to the on-disk +serialisation. The 6.7.x point releases share a single SetupID magic, +"Inno Setup Setup Data (6.7.0)" (Projects/Src/Setup.MainFunc.pas at +tag is-6_7_0 in https://github.com/jrsoftware/issrc), so 6.7.0 and +6.7.1 are detected together and parsed identically. + +In TSetupHeader, the five shUsePrevious* flags +(UsePreviousAppDir, UsePreviousGroup, UsePreviousSetupType, +UsePreviousTasks, UsePreviousUserInfo) were promoted out of the +TSetupHeaderOption bitset into expression-string fields of the same +names, so that they can take per-install conditional values rather +than just on/off. Read the five new strings between +SevenZipLibraryName and LicenseText, and gate the corresponding flag +adds in load_flags to ``< 6.7.0`` so the bitset matches the new +length on the wire. + +Three more wizard-colour fields were added next to the 6.5.2/6.6.0 +ones: WizardBackColor between WizardSmallImageBackColor and the +*DynamicDark colour pair, WizardBackColorDynamicDark in the same +position relative to the dynamic-dark companions, and +WizardBackImageOpacity (Byte) right after WizardImageOpacity. +TSetupWizardLightControlStyling (wcsAll, wcsAllButButtons, +wcsOnlyRequired) is also stored as a byte enum at that point. Add +header members for all four, default the older versions to 0xff / +LightControlStyleAll respectively, and provide the matching +stored_enum and NAMES blocks. + +TSetupHeaderOption itself dropped shWizardLightButtonsUnstyled (the +6.6.x experiment for light theme) and added shRedirectionGuard and +shWizardBevelsHidden at the end. Tighten the existing +WizardLightButtonsUnstyled add to ``< 6.7.0`` and add the two new +flags for ``>= 6.7.0`` with their NAMES strings, mirroring the +6.6.0 pattern. The whole bitset was also padded to 57 elements via +shUnusedPadding=56 so the serialised set is always 8 bytes regardless +of which subset of flags is actually defined. The motivation in +issrc is bit-compatibility between 32-bit and 64-bit Delphi builds +(a `set` with more than 32 elements has a minimum size of 8 bytes in +64-bit Delphi). With every active 6.7.0 header flag now modelled, the +flagreader fills 6 bytes; use the new storedenum helper to drain the +remaining 2 padding bytes up to 8 bytes total. The same pattern +applies to TSetupFileEntryOption with its foUnusedPadding=56 +sentinel; add the matching discard_padding_to(8) call after +file_entry's flagreader.finalize(). + +In TSetupComponentEntry and TSetupTaskEntry, Level narrowed from +Integer (4 bytes) to Byte (1 byte). Both fields were already +restricted to 0..99 in the compiler, so the storage shrink is purely +a wire-format change. + +Finally Setup.MainFunc.pas adds a third wizard image group on the +wizard / decompressor stream for the new WizardBackImageFile +directive, in addition to the existing main and small image groups. +The same group is duplicated for the dynamic-dark theme that 6.6.0 +introduced, matching the existing main/small dark pair. Read the new +non-dark group into a new info::wizard_images_back vector (so +extraction can later expose it), and read the dark back-image group +into a discarded local. Both are gated to ``>= 6.7.0`` and sit on +exactly the same fork between the two pre-existing groups and the +decompressor DLL block as the 6.6.0 dynamic-dark groups. + +Assisted-by: Claude Opus 4.7 +Signed-off-by: Johannes Schindelin +--- + src/setup/component.cpp | 8 +++- + src/setup/file.cpp | 9 ++++ + src/setup/header.cpp | 103 +++++++++++++++++++++++++++++++++++++--- + src/setup/header.hpp | 17 +++++++ + src/setup/info.cpp | 15 +++++- + src/setup/info.hpp | 1 + + src/setup/task.cpp | 7 ++- + src/setup/version.cpp | 1 + + 8 files changed, 151 insertions(+), 10 deletions(-) + +diff --git a/src/setup/component.cpp b/src/setup/component.cpp +index eab4f1a..b6d3734 100644 +--- a/src/setup/component.cpp ++++ b/src/setup/component.cpp +@@ -74,7 +74,13 @@ void component_entry::load(std::istream & is, const info & i) { + } else { + extra_disk_pace_required = util::load(is); + } +- if(i.version >= INNO_VERSION(4, 0, 0) || (i.version.is_isx() && i.version >= INNO_VERSION(3, 0, 3))) { ++ if(i.version >= INNO_VERSION(6, 7, 0)) { ++ // Inno Setup 6.7.0 narrowed TSetupComponentEntry.Level from ++ // Integer (4 bytes) to Byte (1 byte). See ++ // Projects/Src/Shared.Struct.pas at tag is-6_7_0 in ++ // https://github.com/jrsoftware/issrc. ++ level = util::load(is); ++ } else if(i.version >= INNO_VERSION(4, 0, 0) || (i.version.is_isx() && i.version >= INNO_VERSION(3, 0, 3))) { + level = util::load(is); + } else { + level = 0; +diff --git a/src/setup/file.cpp b/src/setup/file.cpp +index f886409..484b027 100644 +--- a/src/setup/file.cpp ++++ b/src/setup/file.cpp +@@ -241,6 +241,15 @@ void file_entry::load(std::istream & is, const info & i) { + } + + options |= flagreader.finalize(); ++ if(i.version >= INNO_VERSION(6, 7, 0)) { ++ // Inno Setup 6.7.0 padded TSetupFileEntryOption to 57 elements ++ // (foUnusedPadding=56) so the set is always 8 bytes regardless ++ // of the actual flag count, matching the analogous change to ++ // TSetupHeaderOption (see is-6_7_0 in issrc). Skip past the ++ // extra padding bytes so the trailing FileType byte is read ++ // from the right offset. ++ flagreader.discard_padding_to(8); ++ } + + if(i.version.bits() == 16 || i.version >= INNO_VERSION(5, 0, 0)) { + type = stored_enum(is).get(); +diff --git a/src/setup/header.cpp b/src/setup/header.cpp +index 1babfbe..7fbac6f 100644 +--- a/src/setup/header.cpp ++++ b/src/setup/header.cpp +@@ -62,6 +62,12 @@ STORED_ENUM_MAP(stored_dark_style, header::LightStyle, + header::DynamicStyle + ); + ++STORED_ENUM_MAP(stored_light_control_styling, header::LightControlStyleAll, ++ header::LightControlStyleAll, ++ header::LightControlStyleAllButButtons, ++ header::LightControlStyleOnlyRequired ++); ++ + STORED_ENUM_MAP(stored_bool_auto_no_yes, header::Auto, + header::Auto, + header::No, +@@ -282,6 +288,24 @@ void header::load(std::istream & is, const version & version) { + } else { + seven_zip_library_name.clear(); + } ++ if(version >= INNO_VERSION(6, 7, 0)) { ++ // Inno Setup 6.7.0 moved five settings out of the ++ // TSetupHeaderOption bitset into expression-string fields: ++ // UsePreviousAppDir, UsePreviousGroup, UsePreviousSetupType, ++ // UsePreviousTasks and UsePreviousUserInfo ++ // (Projects/Src/Shared.Struct.pas at tag is-6_7_0). ++ is >> util::binary_string(use_previous_app_dir); ++ is >> util::binary_string(use_previous_group); ++ is >> util::binary_string(use_previous_setup_type); ++ is >> util::binary_string(use_previous_tasks); ++ is >> util::binary_string(use_previous_user_info); ++ } else { ++ use_previous_app_dir.clear(); ++ use_previous_group.clear(); ++ use_previous_setup_type.clear(); ++ use_previous_tasks.clear(); ++ use_previous_user_info.clear(); ++ } + if(version >= INNO_VERSION(5, 2, 5)) { + is >> util::ansi_string(license_text); + is >> util::ansi_string(info_before); +@@ -424,6 +448,13 @@ void header::load(std::istream & is, const version & version) { + image_back_color = util::load(is); + small_image_back_color = util::load(is); + } ++ if(version >= INNO_VERSION(6, 7, 0)) { ++ // 6.7.0 inserted WizardBackColor between WizardSmallImageBackColor ++ // and the *DynamicDark colour pair. ++ wizard_back_color = util::load(is); ++ } else { ++ wizard_back_color = 0; ++ } + if(version >= INNO_VERSION(6, 6, 0)) { + // 6.6.0 added the DynamicDark variants. + image_back_color_dynamic_dark = util::load(is); +@@ -432,6 +463,13 @@ void header::load(std::istream & is, const version & version) { + image_back_color_dynamic_dark = 0; + small_image_back_color_dynamic_dark = 0; + } ++ if(version >= INNO_VERSION(6, 7, 0)) { ++ // 6.7.0 also added WizardBackColorDynamicDark, in the same ++ // position relative to its dynamic-dark companions. ++ wizard_back_color_dynamic_dark = util::load(is); ++ } else { ++ wizard_back_color_dynamic_dark = 0; ++ } + if(version >= INNO_VERSION(6, 6, 1)) { + // 6.6.1 added WizardImageOpacity (Byte). Default for older versions + // is fully opaque. +@@ -439,6 +477,16 @@ void header::load(std::istream & is, const version & version) { + } else { + wizard_image_opacity = 0xff; + } ++ if(version >= INNO_VERSION(6, 7, 0)) { ++ // 6.7.0 added WizardBackImageOpacity (Byte) and ++ // WizardLightControlStyling (TSetupWizardLightControlStyling, byte ++ // enum: wcsAll / wcsAllButButtons / wcsOnlyRequired). ++ wizard_back_image_opacity = util::load(is); ++ wizard_light_control_styling = stored_enum(is).get(); ++ } else { ++ wizard_back_image_opacity = 0xff; ++ wizard_light_control_styling = LightControlStyleAll; ++ } + + if(version >= INNO_VERSION(6, 5, 0)) { + // Inno Setup 6.5.0 moved the encryption fields (password test, KDF +@@ -700,19 +748,20 @@ header::flags header::load_flags(std::istream & is, const version & version) { + if(version >= INNO_VERSION(1, 3, 0) && version < INNO_VERSION(5, 3, 8)) { + flagreader.add(CreateUninstallRegKey); + } +- if(version >= INNO_VERSION(1, 3, 1)) { ++ if(version >= INNO_VERSION(1, 3, 1) && version < INNO_VERSION(6, 7, 0)) { + flagreader.add(UsePreviousAppDir); + } + if(version >= INNO_VERSION(1, 3, 3) && version < INNO_VERSION_EXT(6, 4, 0, 1)) { + flagreader.add(BackColorHorizontal); + } +- if(version >= INNO_VERSION(1, 3, 10)) { ++ if(version >= INNO_VERSION(1, 3, 10) && version < INNO_VERSION(6, 7, 0)) { + flagreader.add(UsePreviousGroup); + } + if(version >= INNO_VERSION(1, 3, 20)) { + flagreader.add(UpdateUninstallLogAppName); + } +- if(version >= INNO_VERSION(2, 0, 0) || (version.is_isx() && version >= INNO_VERSION(1, 3, 10))) { ++ if((version >= INNO_VERSION(2, 0, 0) || (version.is_isx() && version >= INNO_VERSION(1, 3, 10))) ++ && version < INNO_VERSION(6, 7, 0)) { + flagreader.add(UsePreviousSetupType); + } + if(version >= INNO_VERSION(2, 0, 0)) { +@@ -720,7 +769,9 @@ header::flags header::load_flags(std::istream & is, const version & version) { + flagreader.add(AlwaysShowComponentsList); + flagreader.add(FlatComponentsList); + flagreader.add(ShowComponentSizes); +- flagreader.add(UsePreviousTasks); ++ if(version < INNO_VERSION(6, 7, 0)) { ++ flagreader.add(UsePreviousTasks); ++ } + flagreader.add(DisableReadyPage); + } + if(version >= INNO_VERSION(2, 0, 7)) { +@@ -735,7 +786,9 @@ header::flags header::load_flags(std::istream & is, const version & version) { + } + if(version >= INNO_VERSION(3, 0, 0)) { + flagreader.add(UserInfoPage); +- flagreader.add(UsePreviousUserInfo); ++ if(version < INNO_VERSION(6, 7, 0)) { ++ flagreader.add(UsePreviousUserInfo); ++ } + } + if(version >= INNO_VERSION(3, 0, 1)) { + flagreader.add(UninstallRestartComputer); +@@ -811,13 +864,36 @@ header::flags header::load_flags(std::istream & is, const version & version) { + } + if(version >= INNO_VERSION(6, 6, 0)) { + // Inno Setup 6.6.0 added four new wizard-styling flags after +- // shUninstallLogging. WizardLightButtonsUnstyled is later +- // dropped again in 6.7.0 (handled in the 6.7.0 patch). ++ // shUninstallLogging, three of which carry over into 6.7.0. + flagreader.add(WizardModern); + flagreader.add(WizardBorderStyled); + flagreader.add(WizardKeepAspectRatio); ++ } ++ if(version >= INNO_VERSION(6, 6, 0) && version < INNO_VERSION(6, 7, 0)) { ++ // shWizardLightButtonsUnstyled exists only in the 6.6.x series; ++ // 6.7.0 dropped it again in favour of the new ++ // TSetupWizardLightControlStyling enum stored further up in ++ // TSetupHeader. + flagreader.add(WizardLightButtonsUnstyled); + } ++ if(version >= INNO_VERSION(6, 7, 0)) { ++ // Inno Setup 6.7.0 added two more wizard-related flags at the end ++ // of TSetupHeaderOption; see Projects/Src/Shared.Struct.pas at ++ // tag is-6_7_0 in https://github.com/jrsoftware/issrc. ++ flagreader.add(RedirectionGuard); ++ flagreader.add(WizardBevelsHidden); ++ } ++ ++ if(version >= INNO_VERSION(6, 7, 0)) { ++ // Inno Setup 6.7.0 padded TSetupHeaderOption to 57 elements via a ++ // shUnusedPadding=56 sentinel so the set is always 8 bytes ++ // regardless of the actual flag count, in order to keep 32-bit ++ // and 64-bit Delphi builds bit-compatible (Projects/Src/Shared.Struct.pas ++ // at tag is-6_7_0). The flags above only fill 6 bytes; skip the ++ // remaining padding bytes so file_count and the rest of the ++ // header stay aligned. ++ flagreader.discard_padding_to(8); ++ } + + return flagreader.finalize(); + } +@@ -853,6 +929,11 @@ void header::decode(util::codepage_id codepage) { + util::to_utf8(close_applications_filter, codepage); + util::to_utf8(close_applications_filter_excludes, codepage); + util::to_utf8(seven_zip_library_name, codepage); ++ util::to_utf8(use_previous_app_dir, codepage); ++ util::to_utf8(use_previous_group, codepage); ++ util::to_utf8(use_previous_setup_type, codepage); ++ util::to_utf8(use_previous_tasks, codepage); ++ util::to_utf8(use_previous_user_info, codepage); + util::to_utf8(setup_mutex, codepage, &lead_bytes); + util::to_utf8(changes_environment, codepage); + util::to_utf8(changes_associations, codepage); +@@ -918,6 +999,8 @@ NAMES(setup::header::flags, "Setup Option", + "wizard border styled", + "wizard keep aspect ratio", + "wizard light buttons unstyled", ++ "redirection guard", ++ "wizard bevels hidden", + "uninstallable", + "disable dir page", + "disable program group page", +@@ -976,6 +1059,12 @@ NAMES(setup::header::dark_style, "Dark Style", + "dynamic", + ) + ++NAMES(setup::header::light_control_styling, "Light Control Styling", ++ "all", ++ "all but buttons", ++ "only required", ++) ++ + NAMES(setup::header::auto_bool, "Auto Boolean", + "auto", + "no", +diff --git a/src/setup/header.hpp b/src/setup/header.hpp +index a0211fc..2138c22 100644 +--- a/src/setup/header.hpp ++++ b/src/setup/header.hpp +@@ -106,6 +106,8 @@ struct header { + WizardBorderStyled, + WizardKeepAspectRatio, + WizardLightButtonsUnstyled, ++ RedirectionGuard, ++ WizardBevelsHidden, + + // Obsolete flags + Uninstallable, +@@ -173,6 +175,11 @@ struct header { + std::string architectures_installed_in_64bit_mode_expr; + std::string close_applications_filter_excludes; + std::string seven_zip_library_name; ++ std::string use_previous_app_dir; ++ std::string use_previous_group; ++ std::string use_previous_setup_type; ++ std::string use_previous_tasks; ++ std::string use_previous_user_info; + std::string license_text; + std::string info_before; + std::string info_after; +@@ -208,7 +215,10 @@ struct header { + Color small_image_back_color; + Color image_back_color_dynamic_dark; + Color small_image_back_color_dynamic_dark; ++ Color wizard_back_color; ++ Color wizard_back_color_dynamic_dark; + boost::uint8_t wizard_image_opacity; ++ boost::uint8_t wizard_back_image_opacity; + + enum style { + ClassicStyle, +@@ -221,6 +231,12 @@ struct header { + DynamicStyle + }; + dark_style wizard_dark_style; ++ enum light_control_styling { ++ LightControlStyleAll, ++ LightControlStyleAllButButtons, ++ LightControlStyleOnlyRequired ++ }; ++ light_control_styling wizard_light_control_styling; + boost::uint32_t wizard_resize_percent_x; + boost::uint32_t wizard_resize_percent_y; + +@@ -315,6 +331,7 @@ NAMED_ENUM(setup::header::install_verbosity) + NAMED_ENUM(setup::header::log_mode) + NAMED_ENUM(setup::header::style) + NAMED_ENUM(setup::header::dark_style) ++NAMED_ENUM(setup::header::light_control_styling) + NAMED_ENUM(setup::header::auto_bool) + NAMED_ENUM(setup::header::privilege_level) + NAMED_ENUM(setup::header::language_detection_method) +diff --git a/src/setup/info.cpp b/src/setup/info.cpp +index 8afe01b..30a3c98 100644 +--- a/src/setup/info.cpp ++++ b/src/setup/info.cpp +@@ -106,6 +106,7 @@ void load_wizard_and_decompressor(std::istream & is, const setup::version & vers + + info.wizard_images.clear(); + info.wizard_images_small.clear(); ++ info.wizard_images_back.clear(); + + load_wizard_images(is, version, info.wizard_images, entries); + +@@ -113,15 +114,27 @@ void load_wizard_and_decompressor(std::istream & is, const setup::version & vers + load_wizard_images(is, version, info.wizard_images_small, entries); + } + ++ if(version >= INNO_VERSION(6, 7, 0)) { ++ // Inno Setup 6.7.0 added a "back image" wizard image group (used ++ // for the new WizardBackImageFile directive), in addition to the ++ // existing main and small image groups. See the wizard-image read ++ // block in Projects/Src/Setup.MainFunc.pas at tag is-6_7_0 in ++ // https://github.com/jrsoftware/issrc. ++ load_wizard_images(is, version, info.wizard_images_back, entries); ++ } ++ + if(version >= INNO_VERSION(6, 6, 0)) { + // Inno Setup 6.6.0 added a second copy of each wizard image + // group for the dynamic-dark theme. The on-disk format always + // includes both copies regardless of whether dark-mode images + // were actually configured; the runtime decides which set to + // use based on WantWizardImagesDynamicDark. +- std::vector dark_images, dark_images_small; ++ std::vector dark_images, dark_images_small, dark_images_back; + load_wizard_images(is, version, dark_images, entries); + load_wizard_images(is, version, dark_images_small, entries); ++ if(version >= INNO_VERSION(6, 7, 0)) { ++ load_wizard_images(is, version, dark_images_back, entries); ++ } + } + + info.decompressor_dll.clear(); +diff --git a/src/setup/info.hpp b/src/setup/info.hpp +index fb8d57b..10f7f07 100644 +--- a/src/setup/info.hpp ++++ b/src/setup/info.hpp +@@ -114,6 +114,7 @@ struct info { + //! Loading enabled by \c WizardImages + std::vector wizard_images; + std::vector wizard_images_small; ++ std::vector wizard_images_back; //!< Inno Setup 6.7.0+ background images + + //! Contents of the helper DLL used to decompress setup data in some versions. + //! Loading enabled by \c DecompressorDll +diff --git a/src/setup/task.cpp b/src/setup/task.cpp +index a0bbdd8..6fbe1ef 100644 +--- a/src/setup/task.cpp ++++ b/src/setup/task.cpp +@@ -45,7 +45,12 @@ void task_entry::load(std::istream & is, const info & i) { + } else { + check.clear(); + } +- if(i.version >= INNO_VERSION(4, 0, 0) || (i.version.is_isx() && i.version >= INNO_VERSION(3, 0, 3))) { ++ if(i.version >= INNO_VERSION(6, 7, 0)) { ++ // Inno Setup 6.7.0 narrowed TSetupTaskEntry.Level from Integer ++ // (4 bytes) to Byte (1 byte). See Projects/Src/Shared.Struct.pas ++ // at tag is-6_7_0 in https://github.com/jrsoftware/issrc. ++ level = util::load(is); ++ } else if(i.version >= INNO_VERSION(4, 0, 0) || (i.version.is_isx() && i.version >= INNO_VERSION(3, 0, 3))) { + level = util::load(is); + } else { + level = 0; +diff --git a/src/setup/version.cpp b/src/setup/version.cpp +index 4aadc56..01582aa 100644 +--- a/src/setup/version.cpp ++++ b/src/setup/version.cpp +@@ -192,6 +192,7 @@ const known_version versions[] = { + { "Inno Setup Setup Data (6.5.2)", INNO_VERSION_EXT(6, 5, 2, 0), version::Unicode }, + { "Inno Setup Setup Data (6.6.0)", INNO_VERSION_EXT(6, 6, 0, 0), version::Unicode }, + { "Inno Setup Setup Data (6.6.1)", INNO_VERSION_EXT(6, 6, 1, 0), version::Unicode }, ++ { "Inno Setup Setup Data (6.7.0)", INNO_VERSION_EXT(6, 7, 0, 0), version::Unicode }, + }; + + } // anonymous namespace +-- +2.54.0.windows.1 + diff --git a/mingw-w64-innoextract/PKGBUILD b/mingw-w64-innoextract/PKGBUILD index dd13c26cfbced..8681a48b9fd69 100644 --- a/mingw-w64-innoextract/PKGBUILD +++ b/mingw-w64-innoextract/PKGBUILD @@ -5,7 +5,7 @@ pkgbase=mingw-w64-${_realname} pkgname="${MINGW_PACKAGE_PREFIX}-${_realname}" _pkgver=1.9 pkgver=1.9.r82.6e9e34e -pkgrel=1 +pkgrel=2 _commit=6e9e34ed0876014fdb46e684103ef8c3605e382e pkgdesc="A tool to extract installers created by Inno Setup (mingw-w64)" arch=('any') @@ -22,8 +22,35 @@ makedepends=("${MINGW_PACKAGE_PREFIX}-cc" depends=("${MINGW_PACKAGE_PREFIX}-cc-libs" "${MINGW_PACKAGE_PREFIX}-boost-libs" "${MINGW_PACKAGE_PREFIX}-xz") -source=("git+https://github.com/dscharrer/innoextract#commit=${_commit}") -sha256sums=('9e12fe63358bfa1b9ed4810f850d2e628ef7b87acc1ad44fc4d35e1b3a8e2a8e') +source=("git+https://github.com/dscharrer/innoextract#commit=${_commit}" + # Adds Inno Setup 6.4.2 through 6.7.1 on-disk format support, + # exported from https://github.com/dscharrer/innoextract/pull/210 + # via git format-patch. Drop these once that PR is merged + # upstream and _commit is bumped to a commit that already + # contains them. + "0001-loader-support-setup-loader-offset-table-revision-2.patch" + "0002-Add-support-for-Inno-Setup-6.4.2-and-6.4.3.patch" + "0003-Add-support-for-Inno-Setup-6.5.0-6.5.1-6.5.2-6.5.3-a.patch" + "0004-Add-support-for-Inno-Setup-6.6.0-and-6.6.1.patch" + "0005-refactor-widen-seekable-offset-and-size-types-to-64-.patch" + "0006-loader-block-consume-native-64-bit-offsets-and-sizes.patch" + "0007-util-storedenum-let-stored_flag_reader-pad-to-a-fixe.patch" + "0008-Add-support-for-the-Inno-Setup-6.5.0-entry-stream-re.patch" + "0009-data-widen-the-per-file-location-start-offset-to-64-.patch" + "0010-Add-support-for-the-Inno-Setup-6.6.0-entry-stream-re.patch" + "0011-Add-support-for-Inno-Setup-6.7.0-and-6.7.1.patch") +sha256sums=('9e12fe63358bfa1b9ed4810f850d2e628ef7b87acc1ad44fc4d35e1b3a8e2a8e' + '8df6248cc85f7bad135acb20ca8acfad9c1af53f947729a858d7f470e70cb59c' + '3276cb3cbc2c9b4128047aae33385098c46dcaf138d7248a4b3e3541f256ea7f' + 'cd45a25d31f083c31eafc906e1cfc192e63f43c61d946dce5f177a266b185a2b' + 'c971ae68d5e289f351ff94b5a2c0a548f34b87e9d6e136e47c2c4c911d80500b' + '89fdeb932c6c86d304548e59a6a5bee56e13b55e7e0dea06259942d33fe6c322' + '7a757ad7f4221312954d55a67a84ca4453378fc30e0cefb80bf30f208f11c568' + '6ad83e255639e7ca733ee4e2d19c2d933064275be0219c56a0472a9ba734d99b' + '6e79f0d1f0cec40b9ceeb1f56bc7a1ab4e5597617e97627fa34ad8abe9e511c0' + 'a0d6329d286c16d9419935daf2056b0fd01b72b6d04ced23d7610ebb8af6b88f' + '0d1985a70c533a67221bf5b182ffb1105d393758f649acd10f6953d6e5dc612a' + '311eafccac8e1c980d29c629102517ea3da08971b237380c82590fda6cdf3da3') validpgpkeys=("ADE9653703D4ADE0E997758128555A66D7E1DEC9") # Daniel Scharrer pkgver() { @@ -31,6 +58,13 @@ pkgver() { printf "${_pkgver}.r%s.%s" "$(git rev-list --count ${_pkgver}..HEAD)" "$(git rev-parse --short HEAD)" } +prepare() { + cd "${srcdir}"/${_realname} + for _patch in "${srcdir}"/[0-9][0-9][0-9][0-9]-*.patch; do + patch -Np1 -i "${_patch}" + done +} + build() { mkdir -p "${srcdir}"/build-${MSYSTEM} && cd "${srcdir}"/build-${MSYSTEM}