From 5ae76a1396d9bbd935bc78a41a3f4f7776acabd6 Mon Sep 17 00:00:00 2001 From: UserUnknownFactor <63057995+UserUnknownFactor@users.noreply.github.com> Date: Fri, 14 Feb 2025 20:30:51 +0300 Subject: [PATCH 1/8] Windows build --- .github/workflows/ci.yml | 128 +++++++++++++++++++++++---------------- README.md | 2 +- 2 files changed, 78 insertions(+), 52 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05123cd..c6f649b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,57 +1,83 @@ -name: CI +name: Release on: push: - branches: [ master ] - pull_request: - branches: [ master ] - schedule: - - cron: '21 11 * * 5' + tags: + - '*' jobs: - - linux: - name: Linux build - runs-on: ubuntu-latest - + build: + runs-on: windows-latest + defaults: + run: + shell: msys2 {0} steps: - - uses: actions/checkout@v4 - - - name: Update - run: sudo apt-get update - - - name: Dependencies - run: sudo apt-get install build-essential cmake libboost-all-dev liblzma-dev - - - name: Configure - run: cmake --version && cmake -B ${{github.workspace}}/build -Werror=dev -Werror=deprecated -DCONTINUOUS_INTEGRATION=1 - - - name: Build - run: cmake --build ${{github.workspace}}/build - - - name: Check Style - run: cmake --build ${{github.workspace}}/build --target style - - macos: - name: macOS build - runs-on: macos-latest - - steps: - - uses: actions/checkout@v4 - - - name: Update - run: brew update - - - name: Workaround for Python install isssues - https://github.com/actions/runner-images/issues/8838 - run: brew install python@3 || brew link --overwrite python@3 - - - name: Dependencies - env: - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1 - run: brew install boost xz - - - name: Configure - run: cmake --version && cmake -B ${{github.workspace}}/build -Werror=dev -Werror=deprecated -DCONTINUOUS_INTEGRATION=1 - - - name: Build - run: cmake --build ${{github.workspace}}/build + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: >- + mingw-w64-x86_64-gcc + mingw-w64-x86_64-toolchain + base-devel + mingw-w64-x86_64-make + mingw-w64-x86_64-cmake + mingw-w64-x86_64-boost + mingw-w64-x86_64-zlib + mingw-w64-x86_64-bzip2 + mingw-w64-x86_64-xz + + - name: Build + run: | + mkdir build + cd build + cmake -G"MSYS Makefiles" \ + -DUSE_LTO=OFF \ + -DCMAKE_EXE_LINKER_FLAGS="-static -static-libgcc -static-libstdc++" \ + -DCMAKE_BUILD_TYPE=Release \ + -DUSE_STATIC_LIBS=ON \ + -DBUILD_SHARED_LIBS=OFF .. + mingw32-make -j8 + strip innoextract.exe + + - name: Find and Copy Required DLLs + run: | + mkdir release + cp build/innoextract.exe release/ + + echo "Checking for missing DLL dependencies..." + ldd release/innoextract.exe | grep "not found" || true + + echo "Copying necessary DLLs..." + for dll in $(ldd release/innoextract.exe | grep mingw64 | awk '{print $3}'); do + cp -v "$dll" release/ || true + done + + echo "Final dependency check..." + ldd release/innoextract.exe | grep "not found" || true + + - name: Package Binaries + shell: pwsh + run: | + $VERSION = if ($env:GITHUB_REF -match '^refs/tags/(.+)') { $matches[1] } else { 'dev' } + $ZIP_NAME = "innoextract$VERSION.zip" + + Set-Location release + 7z a "..\$ZIP_NAME" * + + echo "UPLOAD_FILE=$ZIP_NAME" >> $env:GITHUB_ENV + + - name: Create Release + id: create_release + uses: softprops/action-gh-release@v2 + #if: startsWith(github.ref, 'refs/tags/') + with: + name: Release ${{ github.ref_name }} + files: ${{ env.UPLOAD_FILE }} + draft: false + prerelease: false + generate_release_notes: true diff --git a/README.md b/README.md index 3a5e826..4c510f9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # innoextract - A tool to unpack installers created by Inno Setup -[Inno Setup](https://jrsoftware.org/isinfo.php) is a tool to create installers for Microsoft Windows applications. innoextract allows to extract such installers under non-Windows systems without running the actual installer using wine. innoextract currently supports installers created by Inno Setup 1.2.10 to 6.3.3. +[Inno Setup](https://jrsoftware.org/isinfo.php) is a tool to create installers for Microsoft Windows applications. innoextract allows to extract such installers under non-Windows systems without running the actual installer using wine. innoextract currently supports installers created by Inno Setup 1.2.10 to 6.4.0. In addition to standard Inno Setup installers, innoextract also supports some modified Inno Setup variants including Martijn Laan's My Inno Setup Extensions 1.3.10 to 3.0.6.1 as well as GOG.com's Inno Setup-based game installers. innoextract is able to unpack Wadjet Eye Games installers (to play with AGS), Arx Fatalis patches (for use with Arx Libertatis) as well as various other Inno Setup executables. From ee4706274ae6d1daec4f93e372a9e75b28a9d1e6 Mon Sep 17 00:00:00 2001 From: UserUnknownFactor <63057995+UserUnknownFactor@users.noreply.github.com> Date: Sun, 7 Sep 2025 23:25:44 +0300 Subject: [PATCH 2/8] 6.4.3 support --- .gitignore | 1 + README.md | 2 +- VERSION | 2 +- src/crypto/checksum.cpp | 4 ++-- src/setup/data.cpp | 12 +++++++----- src/setup/header.cpp | 6 ++++++ src/setup/header.hpp | 1 + src/setup/version.cpp | 2 ++ 8 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d1aff01 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/*.diff diff --git a/README.md b/README.md index 4c510f9..9558a12 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # innoextract - A tool to unpack installers created by Inno Setup -[Inno Setup](https://jrsoftware.org/isinfo.php) is a tool to create installers for Microsoft Windows applications. innoextract allows to extract such installers under non-Windows systems without running the actual installer using wine. innoextract currently supports installers created by Inno Setup 1.2.10 to 6.4.0. +[Inno Setup](https://jrsoftware.org/isinfo.php) is a tool to create installers for Microsoft Windows applications. innoextract allows to extract such installers under non-Windows systems without running the actual installer using wine. innoextract currently supports installers created by Inno Setup 1.2.10 to 6.4.3. In addition to standard Inno Setup installers, innoextract also supports some modified Inno Setup variants including Martijn Laan's My Inno Setup Extensions 1.3.10 to 3.0.6.1 as well as GOG.com's Inno Setup-based game installers. innoextract is able to unpack Wadjet Eye Games installers (to play with AGS), Arx Fatalis patches (for use with Arx Libertatis) as well as various other Inno Setup executables. diff --git a/VERSION b/VERSION index e2b4fe9..cf6688b 100644 --- a/VERSION +++ b/VERSION @@ -1,7 +1,7 @@ innoextract 1.10-dev Known working Inno Setup versions: -Inno Setup 1.2.10 to 6.3.3 +Inno Setup 1.2.10 to 6.4.3 Bug tracker: https://innoextract.constexpr.org/issues diff --git a/src/crypto/checksum.cpp b/src/crypto/checksum.cpp index 2715d75..d62cb3f 100644 --- a/src/crypto/checksum.cpp +++ b/src/crypto/checksum.cpp @@ -90,8 +90,8 @@ std::ostream & operator<<(std::ostream & os, const crypto::checksum & checksum) break; } case crypto::SHA256: { - for(size_t i = 0; i < size_t(boost::size(checksum.sha1)); i++) { - os << std::setfill('0') << std::hex << std::setw(2) << int(boost::uint8_t(checksum.sha1[i])); + for(size_t i = 0; i < size_t(boost::size(checksum.sha256)); i++) { + os << std::setfill('0') << std::hex << std::setw(2) << int(boost::uint8_t(checksum.sha256[i])); } break; } diff --git a/src/setup/data.cpp b/src/setup/data.cpp index 4444ce2..b4ff446 100644 --- a/src/setup/data.cpp +++ b/src/setup/data.cpp @@ -136,20 +136,22 @@ 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)) { + 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 +162,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 +173,7 @@ 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, 3, 0) && i.version < INNO_VERSION(6, 4, 3)) { sign = stored_enum(is).get(); } else if(options & SignOnce) { sign = Once; diff --git a/src/setup/header.cpp b/src/setup/header.cpp index bbe9df9..d226448 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); @@ -765,6 +770,7 @@ void header::decode(util::codepage_id codepage) { util::to_utf8(setup_mutex, codepage, &lead_bytes); util::to_utf8(changes_environment, codepage); util::to_utf8(changes_associations, codepage); + util::to_utf8(close_applications_filter_excludes, 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 From 391f8e173f3ca86109cea13bb5401713ddd2ee0f Mon Sep 17 00:00:00 2001 From: UserUnknownFactor <63057995+UserUnknownFactor@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:19:51 +0300 Subject: [PATCH 3/8] 6.5.4 support (including rev.2 with 64-bit offsets) --- .github/workflows/ci.yml | 18 ++++++++- .gitignore | 4 +- CMakeLists.txt | 20 ++++++---- README.md | 2 +- VERSION | 4 +- cmake/BuildType.cmake | 4 ++ src/cli/extract.cpp | 2 +- src/loader/offsets.cpp | 80 ++++++++++++++++++++++++++-------------- src/loader/offsets.hpp | 5 +++ src/setup/data.cpp | 8 +++- src/setup/file.cpp | 27 ++++++++++++++ src/setup/file.hpp | 9 ++++- src/setup/header.cpp | 22 +++++++++-- src/setup/header.hpp | 2 + src/setup/info.cpp | 52 ++++++++++++++++++++++++-- src/setup/info.hpp | 19 +++++++++- src/setup/issigkey.cpp | 13 +++++++ src/setup/issigkey.hpp | 21 +++++++++++ src/setup/version.cpp | 5 +++ src/setup/version.hpp | 8 +++- src/stream/block.cpp | 19 +++++++++- src/stream/slice.cpp | 63 +++++++++++++++++++++++++------ src/util/output.hpp | 66 ++++++++++++++++++++++++++++++++- 23 files changed, 406 insertions(+), 67 deletions(-) create mode 100644 src/setup/issigkey.cpp create mode 100644 src/setup/issigkey.hpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6f649b..a0c377b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,7 @@ name: Release on: + workflow_dispatch: push: tags: - '*' @@ -71,13 +72,26 @@ jobs: echo "UPLOAD_FILE=$ZIP_NAME" >> $env:GITHUB_ENV + $TAG = $VERSION + if ($TAG.Length -ge 3) { + $X = $TAG[0] + $Y = $TAG[1] + $Z = $TAG[2] + $INNO_VERSION = "$X.$Y.$Z" + } else { + $INNO_VERSION = "dev" + } + + echo "INNO_VERSION=$INNO_VERSION" >> $env:GITHUB_ENV + - name: Create Release id: create_release uses: softprops/action-gh-release@v2 #if: startsWith(github.ref, 'refs/tags/') with: - name: Release ${{ github.ref_name }} + name: Windows version + body: Latest innoextract Windows x64 build with support of Inno Setup installers up to version ${{ env.INNO_VERSION }} files: ${{ env.UPLOAD_FILE }} draft: false prerelease: false - generate_release_notes: true + generate_release_notes: false \ No newline at end of file diff --git a/.gitignore b/.gitignore index d1aff01..3870779 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -/*.diff +*.diff +*.cmd +build diff --git a/CMakeLists.txt b/CMakeLists.txt index 6995610..88d12c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,13 +173,17 @@ else() set(INNOEXTRACT_HAVE_LZMA 0) endif() -find_package(Boost REQUIRED COMPONENTS - iostreams - filesystem - date_time - system - program_options -) +set(BOOST_REQUIRED_COMPONENTS + iostreams + filesystem + date_time + program_options + ) +find_package(Boost REQUIRED COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) +if(Boost_VERSION VERSION_LESS 1.69.0) + find_package(Boost REQUIRED COMPONENTS ${BOOST_REQUIRED_COMPONENTS} system) +endif() + list(APPEND LIBRARIES ${Boost_LIBRARIES}) link_directories(${Boost_LIBRARY_DIRS}) include_directories(SYSTEM ${Boost_INCLUDE_DIR}) @@ -416,6 +420,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/README.md b/README.md index 9558a12..7218662 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # innoextract - A tool to unpack installers created by Inno Setup -[Inno Setup](https://jrsoftware.org/isinfo.php) is a tool to create installers for Microsoft Windows applications. innoextract allows to extract such installers under non-Windows systems without running the actual installer using wine. innoextract currently supports installers created by Inno Setup 1.2.10 to 6.4.3. +[Inno Setup](https://jrsoftware.org/isinfo.php) is a tool to create installers for Microsoft Windows applications. innoextract allows to extract such installers under non-Windows systems without running the actual installer using wine. innoextract currently supports installers created by Inno Setup 1.2.10 to 6.5.4. In addition to standard Inno Setup installers, innoextract also supports some modified Inno Setup variants including Martijn Laan's My Inno Setup Extensions 1.3.10 to 3.0.6.1 as well as GOG.com's Inno Setup-based game installers. innoextract is able to unpack Wadjet Eye Games installers (to play with AGS), Arx Fatalis patches (for use with Arx Libertatis) as well as various other Inno Setup executables. diff --git a/VERSION b/VERSION index cf6688b..8ad6b19 100644 --- a/VERSION +++ b/VERSION @@ -1,7 +1,7 @@ -innoextract 1.10-dev +innoextract 1.11-dev Known working Inno Setup versions: -Inno Setup 1.2.10 to 6.4.3 +Inno Setup 1.2.10 to 6.5.4 Bug tracker: https://innoextract.constexpr.org/issues diff --git a/cmake/BuildType.cmake b/cmake/BuildType.cmake index b829a06..0c3b1cb 100644 --- a/cmake/BuildType.cmake +++ b/cmake/BuildType.cmake @@ -25,6 +25,10 @@ option(SET_OPTIMIZATION_FLAGS "Adjust compiler optimization flags" ON) if(MSVC) + if(DEBUG) + add_compile_options(/MP /MT $<$:/MTd>) + endif() + if(USE_LTO) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL") set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") diff --git a/src/cli/extract.cpp b/src/cli/extract.cpp index 4989311..e9a078d 100644 --- a/src/cli/extract.cpp +++ b/src/cli/extract.cpp @@ -988,7 +988,7 @@ void process_file(const fs::path & installer, const extract_options & o) { ifs.seekg(offsets.header_offset); setup::info info; try { - info.load(ifs, entries, o.codepage); + info.load(ifs, entries, o.codepage, offsets.revision); } catch(const setup::version_error &) { fs::path headerfile = installer; headerfile.replace_extension(".0"); diff --git a/src/loader/offsets.cpp b/src/loader/offsets.cpp index 0c0d6a4..1efe288 100644 --- a/src/loader/offsets.cpp +++ b/src/loader/offsets.cpp @@ -142,45 +142,67 @@ bool offsets::load_offsets_at(std::istream & is, boost::uint32_t pos) { checksum.init(); checksum.update(magic, sizeof(magic)); - if(version >= INNO_VERSION(5, 1, 5)) { - boost::uint32_t revision = checksum.load(is); + revision = 1; + if(version >= INNO_VERSION(5, 1, 5)) { + revision = checksum.load(is); if(is.fail()) { is.clear(); debug("could not read loader header revision"); return false; - } else if(revision != 1) { + } else if(revision != 1 && revision != 2) { log_warning << "Unexpected setup loader revision: " << revision; } } - (void)checksum.load(is); - exe_offset = checksum.load(is); - - if(version >= INNO_VERSION(4, 1, 6)) { - exe_compressed_size = 0; - } else { - exe_compressed_size = checksum.load(is); - } - - exe_uncompressed_size = checksum.load(is); - - if(version >= INNO_VERSION(4, 0, 3)) { + if(revision == 2) { + + exe_offset = static_cast(checksum.load(is)); + exe_uncompressed_size = static_cast(checksum.load(is)); + exe_checksum.type = crypto::CRC32; exe_checksum.crc32 = checksum.load(is); - } else { - exe_checksum.type = crypto::Adler32; - exe_checksum.adler32 = checksum.load(is); - } - - if(version >= INNO_VERSION(4, 0, 0)) { + + (void)checksum.load(is); + + header_offset = static_cast(checksum.load(is)); + data_offset = static_cast(checksum.load(is)); + + (void)checksum.load(is); + + exe_compressed_size = 0; message_offset = 0; + } else { - message_offset = util::load(is); + + (void)checksum.load(is); + exe_offset = checksum.load(is); + + if(version >= INNO_VERSION(4, 1, 6)) { + exe_compressed_size = 0; + } else { + exe_compressed_size = checksum.load(is); + } + + exe_uncompressed_size = checksum.load(is); + + if(version >= INNO_VERSION(4, 0, 3)) { + exe_checksum.type = crypto::CRC32; + exe_checksum.crc32 = checksum.load(is); + } else { + exe_checksum.type = crypto::Adler32; + exe_checksum.adler32 = checksum.load(is); + } + + if(version >= INNO_VERSION(4, 0, 0)) { + message_offset = 0; + } else { + message_offset = util::load(is); + } + + header_offset = checksum.load(is); + data_offset = checksum.load(is); } - header_offset = checksum.load(is); - data_offset = checksum.load(is); - if(is.fail()) { is.clear(); debug("could not read loader header"); @@ -194,14 +216,18 @@ bool offsets::load_offsets_at(std::istream & is, boost::uint32_t pos) { debug("could not read loader header checksum"); return false; } - if(checksum.finalize() != expected) { - log_warning << "Setup loader checksum mismatch!"; + boost::uint32_t actual = checksum.finalize(); + if(actual != expected) { + log_warning << "Setup loader checksum mismatch! Expected: " + << print_hex(expected) + << " Actual: " << print_hex(actual); } } return true; } + void offsets::load(std::istream & is) { found_magic = false; diff --git a/src/loader/offsets.hpp b/src/loader/offsets.hpp index f49bd91..b633d0e 100644 --- a/src/loader/offsets.hpp +++ b/src/loader/offsets.hpp @@ -55,6 +55,11 @@ struct offsets { */ bool found_magic; + /*! + * Marks 64 bit offset usage if 2 + */ + boost::uint32_t revision; + /*! * Offset of compressed `setup.e32` (the actual installer code) * diff --git a/src/setup/data.cpp b/src/setup/data.cpp index b4ff446..5af88f7 100644 --- a/src/setup/data.cpp +++ b/src/setup/data.cpp @@ -55,8 +55,12 @@ void data_entry::load(std::istream & is, const info & i) { chunk.first_slice--, chunk.last_slice--; } } - - chunk.sort_offset = chunk.offset = util::load(is); + + if(i.version >= INNO_VERSION(6, 5, 2)) { + 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); diff --git a/src/setup/file.cpp b/src/setup/file.cpp index a2e917e..e1ed946 100644 --- a/src/setup/file.cpp +++ b/src/setup/file.cpp @@ -92,6 +92,27 @@ void file_entry::load(std::istream & is, const info & i) { load_condition_data(is, i); + if(i.version >= INNO_VERSION(6, 5, 0)) { + is >> util::encoded_string(excludes, i.codepage, i.header.lead_bytes); + is >> util::encoded_string(download_issig_source, i.codepage, i.header.lead_bytes); + is >> util::encoded_string(download_user_name, i.codepage, i.header.lead_bytes); + is >> util::encoded_string(download_password, i.codepage, i.header.lead_bytes); + is >> util::encoded_string(extract_archive_password, i.codepage, i.header.lead_bytes); + + // Verification structure + std::string issig_allowed_keys; + is >> util::ansi_string(issig_allowed_keys); + char hash[32]; + is.read(hash, 32); + boost::uint8_t verification_type = util::load(is); + } else { + excludes.clear(); + download_issig_source.clear(); + download_user_name.clear(); + download_password.clear(); + extract_archive_password.clear(); + } + load_version_data(is, i.version); location = util::load(is, i.version.bits()); @@ -189,6 +210,10 @@ 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)) { + flagreader.add(Download); + flagreader.add(ExtractArchive); + } options |= flagreader.finalize(); @@ -239,6 +264,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..66aeb5c 100644 --- a/src/setup/file.hpp +++ b/src/setup/file.hpp @@ -77,7 +77,9 @@ struct file_entry : public item { SetNtfsCompression, UnsetNtfsCompression, GacInstall, - + Download, + ExtractArchive, + // obsolete options: IsReadmeFile ); @@ -96,6 +98,11 @@ struct file_entry : public item { std::string destination; std::string install_font_name; std::string strong_assembly_name; + std::string excludes; + std::string download_issig_source; + std::string download_user_name; + std::string download_password; + std::string extract_archive_password; boost::uint32_t location; //!< index into the data entry list boost::uint32_t attributes; diff --git a/src/setup/header.cpp b/src/setup/header.cpp index d226448..8feb35b 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,11 @@ void header::load(std::istream & is, const version & version) { } directory_count = util::load(is, version.bits()); + if(version >= INNO_VERSION(6, 5, 0)) { + issig_key_count = util::load(is); + } else { + issig_key_count = 0; + } file_count = util::load(is, version.bits()); data_entry_count = util::load(is, version.bits()); icon_count = util::load(is, version.bits()); @@ -382,7 +392,10 @@ void header::load(std::istream & is, const version & version) { image_alpha_format = AlphaIgnored; } - if(version >= INNO_VERSION(6, 4, 0)) { + if(version >= INNO_VERSION(6, 5, 0)) { + // moved to encryption header (read in setup::info::load) + password.type = crypto::PBKDF2_SHA256_XChaCha20; + } 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)) { @@ -395,7 +408,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)) { + // moved to encryption header (read in setup::info::load) + } 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)) { @@ -767,10 +782,11 @@ 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(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); - util::to_utf8(close_applications_filter_excludes, codepage); } diff --git a/src/setup/header.hpp b/src/setup/header.hpp index 08d492c..a944d5e 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; @@ -183,6 +184,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..d605975 100644 --- a/src/setup/info.cpp +++ b/src/setup/info.cpp @@ -30,6 +30,7 @@ #include "crypto/pbkdf2.hpp" #include "crypto/sha256.hpp" #include "crypto/xchacha20.hpp" +#include "crypto/crc32.hpp" #include "setup/component.hpp" #include "setup/data.hpp" #include "setup/delete.hpp" @@ -204,6 +205,11 @@ 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("slipping issig keys"); + for(size_t i = 0; i < header.issig_key_count; i++) { + issig_key_entry entry; + entry.load(*reader, *this); + } debug("loading files"); load_entries(*reader, entries, header.file_count, files, Files); debug("loading icons"); @@ -238,10 +244,50 @@ void info::try_load(std::istream & is, entry_types entries, util::codepage_id fo check_is_end(reader, "unknown data at end of secondary header stream"); } -void info::load(std::istream & is, entry_types entries, util::codepage_id force_codepage) { - +void info::load(std::istream & is, entry_types entries, util::codepage_id force_codepage, + boost::uint32_t loader_revision) { + version.load(is); - + if(loader_revision == 2 && version >= INNO_VERSION(6, 5, 0)) { + version.set_64bit(); + } + + if(version >= INNO_VERSION(6, 5, 0)) { + boost::uint32_t eh_expected_crc = util::load(is); + + encryption_header eh; + if(is.read(reinterpret_cast(&eh), sizeof(eh)).fail()) { + throw std::runtime_error("failed to read encryption header"); + } + + crypto::crc32 eh_crc; + eh_crc.init(); + eh_crc.update(reinterpret_cast(&eh), sizeof(eh)); + if(eh_crc.finalize() != eh_expected_crc) { + //throw std::runtime_error("encryption header CRC mismatch"); + } + + if(eh.encryption_use == 2) { + throw std::runtime_error("full encryption mode is not supported"); + } + + if(eh.encryption_use == 1) { + header.password.type = crypto::PBKDF2_SHA256_XChaCha20; + header.password_salt.resize(44); + std::memcpy(&header.password_salt[0], eh.kdf_salt, 16); + util::little_endian::store( + boost::uint32_t(eh.kdf_iterations), &header.password_salt[16] + ); + std::memcpy(&header.password_salt[20], &eh.base_nonce, sizeof(eh.base_nonce)); + + std::memcpy(header.password.sha256, &eh.password_test, 4); + } else { + header.password.type = crypto::None; + header.password_salt.clear(); + std::memset(header.password.sha256, 0, 4); + } + } + if(!version.known) { if(entries & NoUnknownVersion) { std::ostringstream oss; diff --git a/src/setup/info.hpp b/src/setup/info.hpp index e08cfd4..bfd5fb1 100644 --- a/src/setup/info.hpp +++ b/src/setup/info.hpp @@ -31,6 +31,7 @@ #include "setup/header.hpp" #include "setup/version.hpp" +#include "setup/issigkey.hpp" #include "util/encoding.hpp" #include "util/flags.hpp" @@ -51,6 +52,20 @@ struct run_entry; struct task_entry; struct type_entry; +#pragma pack(push, 1) +struct encryption_header { + boost::uint8_t encryption_use; // 0=none, 1=files, 2=full + boost::uint8_t kdf_salt[16]; + boost::int32_t kdf_iterations; + struct { + boost::int64_t random_xor_start_offset; + boost::int32_t random_xor_first_slice; + boost::int32_t remaining_random[3]; + } base_nonce; + boost::int32_t password_test; +}; +#pragma pack(pop) + /*! * Class used to hold and load the various \ref setup headers. */ @@ -106,6 +121,7 @@ struct info { std::vector uninstall_run_entries; //! \c UninstallRunEntries std::vector tasks; //! \c Tasks std::vector types; //! \c Types + std::vector issig_keys; //! \c ISSigKeys //! Images displayed in the installer UI. //! Loading enabled by \c WizardImages @@ -130,7 +146,8 @@ struct info { * \param entries What kinds of entries to load. * \param force_codepage Windows codepage to use for strings in ANSI installers. */ - void load(std::istream & is, entry_types entries, util::codepage_id force_codepage = 0); + void load(std::istream & is, entry_types entries, util::codepage_id force_codepage = 0, + boost::uint32_t loader_revision = 1); std::string get_key(const std::string & password); diff --git a/src/setup/issigkey.cpp b/src/setup/issigkey.cpp new file mode 100644 index 0000000..f61e9be --- /dev/null +++ b/src/setup/issigkey.cpp @@ -0,0 +1,13 @@ +#include "setup/issigkey.hpp" +#include "setup/info.hpp" +#include "util/load.hpp" + +namespace setup { + +void issig_key_entry::load(std::istream & is, const info & i) { + is >> util::encoded_string(public_x, i.codepage); + is >> util::encoded_string(public_y, i.codepage); + is >> util::encoded_string(runtime_id, i.codepage); +} + +} // namespace setup \ No newline at end of file diff --git a/src/setup/issigkey.hpp b/src/setup/issigkey.hpp new file mode 100644 index 0000000..a119375 --- /dev/null +++ b/src/setup/issigkey.hpp @@ -0,0 +1,21 @@ +#ifndef INNOEXTRACT_SETUP_ISSIGKEY_HPP +#define INNOEXTRACT_SETUP_ISSIGKEY_HPP + +#include +#include + +namespace setup { + +struct info; + +struct issig_key_entry { + std::string public_x; + std::string public_y; + std::string runtime_id; + + void load(std::istream & is, const info & i); +}; + +} // namespace setup + +#endif // INNOEXTRACT_SETUP_ISSIGKEY_HPP \ No newline at end of file diff --git a/src/setup/version.cpp b/src/setup/version.cpp index be6682b..89424e2 100644 --- a/src/setup/version.cpp +++ b/src/setup/version.cpp @@ -188,6 +188,11 @@ 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.1)", INNO_VERSION_EXT(6, 5, 1, 0), version::Unicode }, + { "Inno Setup Setup Data (6.5.2)", INNO_VERSION_EXT(6, 5, 2, 0), version::Unicode }, + { "Inno Setup Setup Data (6.5.3)", INNO_VERSION_EXT(6, 5, 3, 0), version::Unicode }, + { "Inno Setup Setup Data (6.5.4)", INNO_VERSION_EXT(6, 5, 4, 0), version::Unicode }, }; } // anonymous namespace diff --git a/src/setup/version.hpp b/src/setup/version.hpp index f882978..d2f0dd4 100644 --- a/src/setup/version.hpp +++ b/src/setup/version.hpp @@ -51,7 +51,8 @@ struct version { FLAGS(flags, Bits16, Unicode, - ISX + ISX, + Bits64 ); version_constant value; @@ -77,9 +78,12 @@ struct version { void load(std::istream & is); - boost::uint16_t bits() const { return (variant & Bits16) ? 16 : 32; } + boost::uint16_t bits() const { return (variant & Bits16) ? 16 : 32; } + + void set_64bit() { variant |= Bits64; } bool is_unicode() const { return (variant & Unicode) != 0; } bool is_isx() const { return (variant & ISX) != 0; } + bool is_64bit() const { return (variant & Bits64) != 0; } //! \return true if the version stored might not be correct bool is_ambiguous() const; diff --git a/src/stream/block.cpp b/src/stream/block.cpp index 2a90c20..0f3c04f 100644 --- a/src/stream/block.cpp +++ b/src/stream/block.cpp @@ -39,6 +39,7 @@ #include "setup/version.hpp" #include "stream/lzma.hpp" #include "util/endian.hpp" +#include "util/output.hpp" #include "util/enum.hpp" #include "util/load.hpp" #include "util/log.hpp" @@ -154,6 +155,18 @@ block_reader::pointer block_reader::get(std::istream & base, const setup::versio USE_ENUM_NAMES(block_compression) +#ifdef DEBUG + std::streampos current_pos = base.tellg(); + char dump_buffer[128]; + std::streamsize bytes_read = base.read(dump_buffer, sizeof(dump_buffer)).gcount(); + base.clear(); + + debug("block header at offset " << print_hex(current_pos) << ":"); + debug(print_hex_dump(dump_buffer, bytes_read, current_pos)); + + base.seekg(current_pos); +#endif + boost::uint32_t expected_checksum = util::load(base); crypto::crc32 actual_checksum; actual_checksum.init(); @@ -166,8 +179,12 @@ block_reader::pointer block_reader::get(std::istream & base, const setup::versio stored_size = actual_checksum.load(base); boost::uint8_t compressed = actual_checksum.load(base); +#ifdef DEBUG + debug("" << (version.is_64bit() ? "64 bit offsets" : "32 bit offsets")); + debug("read stored_size: " << print_hex(stored_size) << " (" << stored_size << " bytes)"); + debug("compressed flag: " << print_hex(boost::uint32_t(compressed))); +#endif compression = compressed ? (version >= INNO_VERSION(4, 1, 6) ? LZMA1 : Zlib) : Stored; - } else { boost::uint32_t compressed_size = actual_checksum.load(base); diff --git a/src/stream/slice.cpp b/src/stream/slice.cpp index c638f40..6a5923a 100644 --- a/src/stream/slice.cpp +++ b/src/stream/slice.cpp @@ -46,6 +46,7 @@ namespace { const char slice_ids[][8] = { { 'i', 'd', 's', 'k', 'a', '1', '6', 0x1a }, { 'i', 'd', 's', 'k', 'a', '3', '2', 0x1a }, + { 'i', 'd', 's', 'k', 'b', '3', '2', 0x1a }, }; } // anonymous namespace @@ -109,31 +110,69 @@ bool slice_reader::open_file(const path_type & file) { ifs.close(); throw slice_error("could not read slice magic number in \"" + file.string() + "\""); } + bool found = false; - for(size_t i = 0; boost::size(slice_ids); i++) { + bool is_64bit_size = false; + + for(size_t i = 0; i < boost::size(slice_ids); i++) { if(!std::memcmp(magic, slice_ids[i], 8)) { found = true; + if(magic[4] == 'b') { + is_64bit_size = true; + } break; } } + if(!found) { ifs.close(); throw slice_error("bad slice magic number in \"" + file.string() + "\""); } - slice_size = util::load(ifs); - if(ifs.fail()) { - ifs.close(); - throw slice_error("could not read slice size in \"" + file.string() + "\""); - } else if(std::streampos(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()) { + std::streampos header_end_pos; + + if(is_64bit_size) { + boost::uint64_t slice_size_64 = util::load(ifs); + if(ifs.fail()) { + ifs.close(); + throw slice_error("could not read slice size in \"" + file.string() + "\""); + } + + header_end_pos = ifs.tellg(); + + if(slice_size_64 > std::numeric_limits::max()) { + ifs.close(); + throw slice_error("slice size exceeds 32-bit limit in \"" + file.string() + "\""); + } + + slice_size = boost::uint32_t(slice_size_64); + + if(std::streampos(slice_size_64) > file_size) { + ifs.close(); + std::ostringstream oss; + oss << "bad slice size in " << file << ": " << slice_size_64 << " > " << file_size; + throw slice_error(oss.str()); + } + } else { + slice_size = util::load(ifs); + if(ifs.fail()) { + ifs.close(); + throw slice_error("could not read slice size in \"" + file.string() + "\""); + } + + header_end_pos = ifs.tellg(); + + if(std::streampos(slice_size) > file_size) { + ifs.close(); + std::ostringstream oss; + oss << "bad slice size in " << file << ": " << slice_size << " > " << file_size; + throw slice_error(oss.str()); + } + } + if(std::streampos(slice_size) < header_end_pos) { ifs.close(); std::ostringstream oss; - oss << "bad slice size in " << file << ": " << slice_size << " < " << ifs.tellg(); + oss << "bad slice size in " << file << ": " << slice_size << " < " << header_end_pos; throw slice_error(oss.str()); } diff --git a/src/util/output.hpp b/src/util/output.hpp index 74c754c..69d8b41 100644 --- a/src/util/output.hpp +++ b/src/util/output.hpp @@ -73,7 +73,7 @@ inline std::ostream & operator<<(std::ostream & os, const if_not_empty & s) { if(s.value.length() > 100) { color::shell_command prev = color::current; return os << s.name << ": " << color::white << s.value.length() << prev - << " bytes" << '\n'; + << " bytes" << '\n'; } else if(!s.value.empty()) { return os << s.name << ": " << quoted(s.value) << '\n'; } else { @@ -182,6 +182,70 @@ inline detail::print_hex_string print_hex(const std::string & data) { return print_hex(data.c_str(), data.size()); } +namespace detail { + +struct print_hex_dump { + + const char * data; + size_t size; + size_t start_offset; + size_t bytes_per_line; + + explicit print_hex_dump(const char * buffer, size_t length, + size_t offset = 0, size_t width = 16) + : data(buffer), size(length), start_offset(offset), bytes_per_line(width) { } + +}; + +inline std::ostream & operator<<(std::ostream & os, const print_hex_dump & s) { + + std::ios_base::fmtflags old = os.flags(); + char oldfill = os.fill('0'); + + for(size_t i = 0; i < s.size; i += s.bytes_per_line) { + os << std::hex << std::setw(8) << (s.start_offset + i) << ": "; + size_t line_bytes = std::min(s.bytes_per_line, s.size - i); + for(size_t j = 0; j < s.bytes_per_line; j++) { + if(j < line_bytes) { + os << std::hex << std::setw(2) << int(boost::uint8_t(s.data[i + j])) << " "; + } else { + os << " "; + } + if(j == 7) os << " "; + } + os << " |"; + for(size_t j = 0; j < line_bytes; j++) { + char c = s.data[i + j]; + if(c >= 0x20 && c < 0x7F) { + os << c; + } else { + os << '.'; + } + } + for(size_t j = line_bytes; j < s.bytes_per_line; j++) { + os << ' '; + } + + os << "|\n"; + } + + os.fill(oldfill); + os.setf(old, std::ios_base::basefield); + return os; +} + +} // namespace detail + +inline detail::print_hex_dump print_hex_dump( + const char * data, size_t size, size_t offset = 0, size_t width = 16) { + return detail::print_hex_dump(data, size, offset, width); +} + +inline detail::print_hex_dump print_hex_dump( + const std::string & data, size_t offset = 0, size_t width = 16) { + return print_hex_dump(data.c_str(), data.size(), offset, width); +} + const char * const byte_size_units[] = { "B", "KiB", From c2f3ad7ebfc19eddbf114cbef3772902fd4a327f Mon Sep 17 00:00:00 2001 From: Charles Pigott Date: Fri, 30 Jan 2026 22:12:19 +0000 Subject: [PATCH 4/8] 6.5 improvements --- src/cli/extract.cpp | 2 +- src/loader/offsets.cpp | 4 +- src/setup/data.cpp | 2 +- src/setup/file.cpp | 52 +++++++++++++-------- src/setup/file.hpp | 19 ++++++-- src/setup/header.cpp | 4 +- src/setup/header.hpp | 4 +- src/setup/info.cpp | 101 ++++++++++++++++++----------------------- src/setup/info.hpp | 20 ++------ src/setup/issigkey.cpp | 22 ++++++++- src/setup/issigkey.hpp | 22 ++++++++- src/setup/version.hpp | 4 +- src/stream/block.cpp | 4 +- src/stream/chunk.hpp | 4 +- src/stream/slice.cpp | 6 +-- src/stream/slice.hpp | 2 +- src/util/output.hpp | 11 ++--- 17 files changed, 158 insertions(+), 125 deletions(-) diff --git a/src/cli/extract.cpp b/src/cli/extract.cpp index e9a078d..c738288 100644 --- a/src/cli/extract.cpp +++ b/src/cli/extract.cpp @@ -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 1efe288..f151470 100644 --- a/src/loader/offsets.cpp +++ b/src/loader/offsets.cpp @@ -219,8 +219,8 @@ bool offsets::load_offsets_at(std::istream & is, boost::uint32_t pos) { boost::uint32_t actual = checksum.finalize(); if(actual != expected) { log_warning << "Setup loader checksum mismatch! Expected: " - << print_hex(expected) - << " Actual: " << print_hex(actual); + << print_hex(expected) + << " Actual: " << print_hex(actual); } } diff --git a/src/setup/data.cpp b/src/setup/data.cpp index 5af88f7..c22b72b 100644 --- a/src/setup/data.cpp +++ b/src/setup/data.cpp @@ -57,7 +57,7 @@ void data_entry::load(std::istream & is, const info & i) { } if(i.version >= INNO_VERSION(6, 5, 2)) { - chunk.sort_offset = chunk.offset = util::load(is); + chunk.sort_offset = chunk.offset = util::load(is); } else { chunk.sort_offset = chunk.offset = util::load(is); } diff --git a/src/setup/file.cpp b/src/setup/file.cpp index e1ed946..112f8a7 100644 --- a/src/setup/file.cpp +++ b/src/setup/file.cpp @@ -29,6 +29,11 @@ namespace setup { namespace { +STORED_ENUM_MAP(stored_file_verification_type, file_entry::fvNone, + file_entry::fvNone, + file_entry::fvHash, + file_entry::fvISSig, +); enum file_copy_mode { cmNormal, @@ -60,6 +65,14 @@ STORED_ENUM_MAP(stored_file_type_1, file_entry::UserFile, } // namespace setup +NAMED_ENUM(setup::file_entry::file_verification_type) + +NAMES(setup::file_entry::file_verification_type, "File Verification Type", + "none", + "hash", + "IS sig", +) + NAMED_ENUM(setup::file_copy_mode) NAMES(setup::file_copy_mode, "File Copy Mode", @@ -92,26 +105,25 @@ void file_entry::load(std::istream & is, const info & i) { load_condition_data(is, i); - if(i.version >= INNO_VERSION(6, 5, 0)) { - is >> util::encoded_string(excludes, i.codepage, i.header.lead_bytes); - is >> util::encoded_string(download_issig_source, i.codepage, i.header.lead_bytes); - is >> util::encoded_string(download_user_name, i.codepage, i.header.lead_bytes); - is >> util::encoded_string(download_password, i.codepage, i.header.lead_bytes); - is >> util::encoded_string(extract_archive_password, i.codepage, i.header.lead_bytes); - - // Verification structure - std::string issig_allowed_keys; - is >> util::ansi_string(issig_allowed_keys); - char hash[32]; - is.read(hash, 32); - boost::uint8_t verification_type = util::load(is); - } else { - excludes.clear(); - download_issig_source.clear(); - download_user_name.clear(); - download_password.clear(); - extract_archive_password.clear(); - } + if(i.version >= INNO_VERSION(6, 5, 0)) { + is >> util::encoded_string(excludes, i.codepage, i.header.lead_bytes); + is >> util::encoded_string(download_issig_source, i.codepage, i.header.lead_bytes); + is >> util::encoded_string(download_user_name, i.codepage, i.header.lead_bytes); + is >> util::encoded_string(download_password, i.codepage, i.header.lead_bytes); + is >> util::encoded_string(extract_archive_password, i.codepage, i.header.lead_bytes); + + // Verification structure + 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_issig_source.clear(); + download_user_name.clear(); + download_password.clear(); + extract_archive_password.clear(); + } load_version_data(is, i.version); diff --git a/src/setup/file.hpp b/src/setup/file.hpp index 66aeb5c..7241e71 100644 --- a/src/setup/file.hpp +++ b/src/setup/file.hpp @@ -84,6 +84,12 @@ struct file_entry : public item { IsReadmeFile ); + enum file_verification_type { + fvNone, + fvHash, + fvISSig, + }; + enum file_type { UserFile, UninstExe, @@ -98,11 +104,14 @@ struct file_entry : public item { std::string destination; std::string install_font_name; std::string strong_assembly_name; - std::string excludes; - std::string download_issig_source; - std::string download_user_name; - std::string download_password; - std::string extract_archive_password; + std::string excludes; + std::string download_issig_source; + std::string download_user_name; + std::string download_password; + std::string extract_archive_password; + + std::string issig_allowed_keys; + file_verification_type verification; boost::uint32_t location; //!< index into the data entry list boost::uint32_t attributes; diff --git a/src/setup/header.cpp b/src/setup/header.cpp index 8feb35b..a954734 100644 --- a/src/setup/header.cpp +++ b/src/setup/header.cpp @@ -710,7 +710,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)) { @@ -830,7 +830,6 @@ NAMES(setup::header::flags, "Setup Option", "wizard image stretch", "append default dir name", "append default group name", - "encrypted", "changes environment", "show undisplayable languages", "setup logging", @@ -858,6 +857,7 @@ NAMES(setup::header::flags, "Setup Option", "disable dir exists warning", "back solid", "overwrite uninst reg entries", + "encrypted", ) NAMES(setup::header::architecture_types, "Architecture", diff --git a/src/setup/header.hpp b/src/setup/header.hpp index a944d5e..7c748a5 100644 --- a/src/setup/header.hpp +++ b/src/setup/header.hpp @@ -87,7 +87,6 @@ struct header { WizardImageStretch, AppendDefaultDirName, AppendDefaultGroupName, - EncryptionUsed, ChangesEnvironment, ShowUndisplayableLanguages, SetupLogging, @@ -116,7 +115,8 @@ struct header { DetectLanguageUsingLocale, DisableDirExistsWarning, BackSolid, - OverwriteUninstRegEntries + OverwriteUninstRegEntries, + EncryptionUsed ); diff --git a/src/setup/info.cpp b/src/setup/info.cpp index d605975..c129693 100644 --- a/src/setup/info.cpp +++ b/src/setup/info.cpp @@ -39,6 +39,7 @@ #include "setup/icon.hpp" #include "setup/ini.hpp" #include "setup/item.hpp" +#include "setup/issigkey.hpp" #include "setup/language.hpp" #include "setup/message.hpp" #include "setup/permission.hpp" @@ -159,7 +160,7 @@ void info::try_load(std::istream & is, entry_types entries, util::codepage_id fo debug("loading main header"); header.load(*reader, version); - debug("loading languages"); + debug("loading " << header.language_count << " languages"); load_entries(*reader, entries, header.language_count, languages, Languages); debug("determining encoding"); @@ -193,39 +194,36 @@ void info::try_load(std::istream & is, entry_types entries, util::codepage_id fo load_wizard_and_decompressor(*reader, version, header, *this, entries); } - debug("loading messages"); + debug("loading " << header.message_count << " messages"); load_entries(*reader, entries, header.message_count, messages, Messages); - debug("loading permissions"); + debug("loading " << header.permission_count << " permissions"); load_entries(*reader, entries, header.permission_count, permissions, Permissions); - debug("loading types"); + debug("loading " << header.type_count << " types"); load_entries(*reader, entries, header.type_count, types, Types); - debug("loading components"); + debug("loading " << header.component_count << " components"); load_entries(*reader, entries, header.component_count, components, Components); - debug("loading tasks"); + debug("loading " << header.task_count << " tasks"); load_entries(*reader, entries, header.task_count, tasks, Tasks); - debug("loading directories"); + debug("loading " << header.directory_count << " directories"); load_entries(*reader, entries, header.directory_count, directories, Directories); - debug("slipping issig keys"); - for(size_t i = 0; i < header.issig_key_count; i++) { - issig_key_entry entry; - entry.load(*reader, *this); - } - debug("loading files"); + debug("loading " << header.issig_key_count << " issigs"); + load_entries(*reader, entries, header.issig_key_count, issig_keys, ISSigs); + debug("loading " << header.file_count << " files"); load_entries(*reader, entries, header.file_count, files, Files); - debug("loading icons"); + debug("loading " << header.icon_count << " icons"); load_entries(*reader, entries, header.icon_count, icons, Icons); - debug("loading ini entries"); + debug("loading " << header.ini_entry_count << " ini entries"); load_entries(*reader, entries, header.ini_entry_count, ini_entries, IniEntries); - debug("loading registry entries"); + debug("loading " << header.registry_entry_count << " registry entries"); load_entries(*reader, entries, header.registry_entry_count, registry_entries, RegistryEntries); - debug("loading delete entries"); + debug("loading " << header.delete_entry_count << " delete entries"); load_entries(*reader, entries, header.delete_entry_count, delete_entries, DeleteEntries); - debug("loading uninstall delete entries"); + debug("loading " << header.uninstall_delete_entry_count << " uninstall delete entries"); load_entries(*reader, entries, header.uninstall_delete_entry_count, uninstall_delete_entries, UninstallDeleteEntries); - debug("loading run entries"); + debug("loading " << header.run_entry_count << " run entries"); load_entries(*reader, entries, header.run_entry_count, run_entries, RunEntries); - debug("loading uninstall run entries"); + debug("loading " << header.uninstall_run_entry_count << " uninstall run entries"); load_entries(*reader, entries, header.uninstall_run_entry_count, uninstall_run_entries, UninstallRunEntries); @@ -245,49 +243,13 @@ void info::try_load(std::istream & is, entry_types entries, util::codepage_id fo } void info::load(std::istream & is, entry_types entries, util::codepage_id force_codepage, - boost::uint32_t loader_revision) { + boost::uint32_t loader_revision) { version.load(is); if(loader_revision == 2 && version >= INNO_VERSION(6, 5, 0)) { version.set_64bit(); } - if(version >= INNO_VERSION(6, 5, 0)) { - boost::uint32_t eh_expected_crc = util::load(is); - - encryption_header eh; - if(is.read(reinterpret_cast(&eh), sizeof(eh)).fail()) { - throw std::runtime_error("failed to read encryption header"); - } - - crypto::crc32 eh_crc; - eh_crc.init(); - eh_crc.update(reinterpret_cast(&eh), sizeof(eh)); - if(eh_crc.finalize() != eh_expected_crc) { - //throw std::runtime_error("encryption header CRC mismatch"); - } - - if(eh.encryption_use == 2) { - throw std::runtime_error("full encryption mode is not supported"); - } - - if(eh.encryption_use == 1) { - header.password.type = crypto::PBKDF2_SHA256_XChaCha20; - header.password_salt.resize(44); - std::memcpy(&header.password_salt[0], eh.kdf_salt, 16); - util::little_endian::store( - boost::uint32_t(eh.kdf_iterations), &header.password_salt[16] - ); - std::memcpy(&header.password_salt[20], &eh.base_nonce, sizeof(eh.base_nonce)); - - std::memcpy(header.password.sha256, &eh.password_test, 4); - } else { - header.password.type = crypto::None; - header.password_salt.clear(); - std::memset(header.password.sha256, 0, 4); - } - } - if(!version.known) { if(entries & NoUnknownVersion) { std::ostringstream oss; @@ -309,6 +271,31 @@ void info::load(std::istream & is, entry_types entries, util::codepage_id force_ entries |= NoSkip; } + if(version >= INNO_VERSION(6, 5, 0)) { + boost::uint32_t expected_encryption_crc = util::load(is); + crypto::crc32 checksum; + checksum.init(); + boost::uint8_t encryption_use = checksum.load(is); + if(encryption_use != 0) { + log_error << "Unsupported encrypted setup"; + } + + for(int i = 0; i < 16; i++) { + /* KDFSalt */(void)checksum.load(is); + } + /* KDFIterations */(void)checksum.load(is); + /* BaseNonce.RandomXorStartOffset */(void)checksum.load(is); + /* BaseNonce.RandomXorFirstSlice */(void)checksum.load(is); + for(int i = 0; i < 3; i++) { + /* BaseNonce.RemainingRandom */(void)checksum.load(is); + } + /* PasswordTest */(void)checksum.load(is); + + if(checksum.finalize() != expected_encryption_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 bfd5fb1..d4e1fb5 100644 --- a/src/setup/info.hpp +++ b/src/setup/info.hpp @@ -41,6 +41,7 @@ struct component_entry; struct data_entry; struct delete_entry; struct directory_entry; +struct issig_key_entry; struct file_entry; struct icon_entry; struct ini_entry; @@ -52,20 +53,6 @@ struct run_entry; struct task_entry; struct type_entry; -#pragma pack(push, 1) -struct encryption_header { - boost::uint8_t encryption_use; // 0=none, 1=files, 2=full - boost::uint8_t kdf_salt[16]; - boost::int32_t kdf_iterations; - struct { - boost::int64_t random_xor_start_offset; - boost::int32_t random_xor_first_slice; - boost::int32_t remaining_random[3]; - } base_nonce; - boost::int32_t password_test; -}; -#pragma pack(pop) - /*! * Class used to hold and load the various \ref setup headers. */ @@ -81,6 +68,7 @@ struct info { DeleteEntries, UninstallDeleteEntries, Directories, + ISSigs, Files, Icons, IniEntries, @@ -110,6 +98,7 @@ struct info { std::vector delete_entries; //! \c DeleteEntries std::vector uninstall_delete_entries; //! \c UninstallDeleteEntries std::vector directories; //! \c Directories + std::vector issig_keys; //! \c ISSigKeys std::vector files; //! \c Files std::vector icons; //! \c Icons std::vector ini_entries; //! \c IniEntries @@ -121,7 +110,6 @@ struct info { std::vector uninstall_run_entries; //! \c UninstallRunEntries std::vector tasks; //! \c Tasks std::vector types; //! \c Types - std::vector issig_keys; //! \c ISSigKeys //! Images displayed in the installer UI. //! Loading enabled by \c WizardImages @@ -147,7 +135,7 @@ struct info { * \param force_codepage Windows codepage to use for strings in ANSI installers. */ void load(std::istream & is, entry_types entries, util::codepage_id force_codepage = 0, - boost::uint32_t loader_revision = 1); + boost::uint32_t loader_revision = 1); std::string get_key(const std::string & password); diff --git a/src/setup/issigkey.cpp b/src/setup/issigkey.cpp index f61e9be..23475e5 100644 --- a/src/setup/issigkey.cpp +++ b/src/setup/issigkey.cpp @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2011-2025 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 "setup/info.hpp" #include "util/load.hpp" @@ -10,4 +30,4 @@ void issig_key_entry::load(std::istream & is, const info & i) { is >> util::encoded_string(runtime_id, i.codepage); } -} // namespace setup \ No newline at end of file +} // namespace setup diff --git a/src/setup/issigkey.hpp b/src/setup/issigkey.hpp index a119375..15a346d 100644 --- a/src/setup/issigkey.hpp +++ b/src/setup/issigkey.hpp @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2011-2025 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. + */ + #ifndef INNOEXTRACT_SETUP_ISSIGKEY_HPP #define INNOEXTRACT_SETUP_ISSIGKEY_HPP @@ -18,4 +38,4 @@ struct issig_key_entry { } // namespace setup -#endif // INNOEXTRACT_SETUP_ISSIGKEY_HPP \ No newline at end of file +#endif // INNOEXTRACT_SETUP_ISSIGKEY_HPP diff --git a/src/setup/version.hpp b/src/setup/version.hpp index d2f0dd4..f040c9d 100644 --- a/src/setup/version.hpp +++ b/src/setup/version.hpp @@ -78,9 +78,9 @@ struct version { void load(std::istream & is); - boost::uint16_t bits() const { return (variant & Bits16) ? 16 : 32; } + boost::uint16_t bits() const { return (variant & Bits16) ? 16 : 32; } - void set_64bit() { variant |= Bits64; } + void set_64bit() { variant |= Bits64; } bool is_unicode() const { return (variant & Unicode) != 0; } bool is_isx() const { return (variant & ISX) != 0; } bool is_64bit() const { return (variant & Bits64) != 0; } diff --git a/src/stream/block.cpp b/src/stream/block.cpp index 0f3c04f..c4e627c 100644 --- a/src/stream/block.cpp +++ b/src/stream/block.cpp @@ -179,11 +179,11 @@ block_reader::pointer block_reader::get(std::istream & base, const setup::versio stored_size = actual_checksum.load(base); boost::uint8_t compressed = actual_checksum.load(base); -#ifdef DEBUG + #ifdef DEBUG debug("" << (version.is_64bit() ? "64 bit offsets" : "32 bit offsets")); debug("read stored_size: " << print_hex(stored_size) << " (" << stored_size << " bytes)"); debug("compressed flag: " << print_hex(boost::uint32_t(compressed))); -#endif + #endif compression = compressed ? (version >= INNO_VERSION(4, 1, 6) ? LZMA1 : Zlib) : Stored; } else { 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 6a5923a..27491f0 100644 --- a/src/stream/slice.cpp +++ b/src/stream/slice.cpp @@ -147,7 +147,7 @@ bool slice_reader::open_file(const path_type & file) { slice_size = boost::uint32_t(slice_size_64); - if(std::streampos(slice_size_64) > file_size) { + if(slice_size_64 > boost::uint64_t(file_size)) { ifs.close(); std::ostringstream oss; oss << "bad slice size in " << file << ": " << slice_size_64 << " > " << file_size; @@ -247,7 +247,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); @@ -257,7 +257,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; } diff --git a/src/stream/slice.hpp b/src/stream/slice.hpp index b2ec52c..9c25247 100644 --- a/src/stream/slice.hpp +++ b/src/stream/slice.hpp @@ -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. diff --git a/src/util/output.hpp b/src/util/output.hpp index 69d8b41..350f431 100644 --- a/src/util/output.hpp +++ b/src/util/output.hpp @@ -73,7 +73,7 @@ inline std::ostream & operator<<(std::ostream & os, const if_not_empty & s) { if(s.value.length() > 100) { color::shell_command prev = color::current; return os << s.name << ": " << color::white << s.value.length() << prev - << " bytes" << '\n'; + << " bytes" << '\n'; } else if(!s.value.empty()) { return os << s.name << ": " << quoted(s.value) << '\n'; } else { @@ -191,8 +191,7 @@ struct print_hex_dump { size_t start_offset; size_t bytes_per_line; - explicit print_hex_dump(const char * buffer, size_t length, - size_t offset = 0, size_t width = 16) + explicit print_hex_dump(const char * buffer, size_t length, size_t offset = 0, size_t width = 16) : data(buffer), size(length), start_offset(offset), bytes_per_line(width) { } }; @@ -236,13 +235,11 @@ inline std::ostream & operator<<(std::ostream & os, const print_hex_dump & s) { } // namespace detail -inline detail::print_hex_dump print_hex_dump( - const char * data, size_t size, size_t offset = 0, size_t width = 16) { +inline detail::print_hex_dump print_hex_dump(const char * data, size_t size, size_t offset = 0, size_t width = 16) { return detail::print_hex_dump(data, size, offset, width); } -inline detail::print_hex_dump print_hex_dump( - const std::string & data, size_t offset = 0, size_t width = 16) { +inline detail::print_hex_dump print_hex_dump(const std::string & data, size_t offset = 0, size_t width = 16) { return print_hex_dump(data.c_str(), data.size(), offset, width); } From 5e08b764854b1fbf7330d6462014697ee95cfea6 Mon Sep 17 00:00:00 2001 From: Charles Pigott Date: Fri, 30 Jan 2026 22:23:45 +0000 Subject: [PATCH 5/8] Inno 6.6 support --- src/cli/debug.cpp | 2 ++ src/setup/header.cpp | 39 +++++++++++++++++++++++++++++++++++--- src/setup/header.hpp | 10 +++++++++- src/setup/info.cpp | 7 +++++++ src/setup/language.cpp | 43 ++++++++++++++++++++++++++++++++++-------- src/setup/language.hpp | 2 ++ src/setup/version.cpp | 2 ++ 7 files changed, 93 insertions(+), 12 deletions(-) diff --git a/src/cli/debug.cpp b/src/cli/debug.cpp index c4e4cd3..6ae4599 100644 --- a/src/cli/debug.cpp +++ b/src/cli/debug.cpp @@ -124,6 +124,8 @@ static void print_entry(const setup::info & info, size_t i, std::cout << if_not_zero(" Dialog font size", entry.dialog_font_size); std::cout << if_not_zero(" Dialog font standard height", entry.dialog_font_standard_height); + std::cout << if_not_zero(" Dialog font scale height", entry.dialog_font_scale_height); + std::cout << if_not_zero(" Dialog font scale width", entry.dialog_font_scale_width); std::cout << if_not_zero(" Title font size", entry.title_font_size); std::cout << if_not_zero(" Welcome font size", entry.welcome_font_size); std::cout << if_not_zero(" Copyright font size", entry.copyright_font_size); diff --git a/src/setup/header.cpp b/src/setup/header.cpp index a954734..ab849a0 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_setup_dark_style, header::LightStyle, + header::LightStyle, + header::DarkStyle, + header::DynamicStyle +); + STORED_ENUM_MAP(stored_bool_auto_no_yes, header::Auto, header::Auto, header::No, @@ -365,6 +371,7 @@ void header::load(std::istream & is, const version & version) { } else { back_color2 = 0; } + // added again in 6.6, see below if(version < INNO_VERSION(5, 5, 7)) { image_back_color = util::load(is); } else { @@ -376,10 +383,14 @@ 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)) { + wizard_resize_percent_x = util::load(is, version.bits()); + wizard_resize_percent_y = util::load(is, version.bits()); + 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_resize_percent_x = util::load(is, version.bits()); + wizard_resize_percent_y = util::load(is, version.bits()); } else { wizard_style = ClassicStyle; wizard_resize_percent_x = 0; @@ -391,6 +402,13 @@ void header::load(std::istream & is, const version & version) { } else { image_alpha_format = AlphaIgnored; } + + if(version >= INNO_VERSION(6, 6, 0)) { + image_back_color = util::load(is, version.bits()); + small_image_back_color = util::load(is, version.bits()); + image_back_color2 = util::load(is, version.bits()); + small_image_back_color2 = util::load(is, version.bits()); + } if(version >= INNO_VERSION(6, 5, 0)) { // moved to encryption header (read in setup::info::load) @@ -744,11 +762,19 @@ 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)) { flagreader.add(WizardResizable); } if(version >= INNO_VERSION(6, 3, 0)) { flagreader.add(UninstallLogging); } + if(version >= INNO_VERSION(6, 6, 0)) { + flagreader.add(WizardModern); + flagreader.add(WizardBorderStyled); + flagreader.add(WizardKeepAspectRatio); + flagreader.add(WizardLightButtonsUnstyled); + } return flagreader.finalize(); } @@ -858,6 +884,7 @@ NAMES(setup::header::flags, "Setup Option", "back solid", "overwrite uninst reg entries", "encrypted", + "wizard_light_buttons_unstyled", ) NAMES(setup::header::architecture_types, "Architecture", @@ -897,6 +924,12 @@ NAMES(setup::header::style, "Style", "modern", ) +NAMES(setup::header::dark_style, "DarkStyle", + "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 7c748a5..fd27fe1 100644 --- a/src/setup/header.hpp +++ b/src/setup/header.hpp @@ -116,7 +116,8 @@ struct header { DisableDirExistsWarning, BackSolid, OverwriteUninstRegEntries, - EncryptionUsed + EncryptionUsed, + WizardLightButtonsUnstyled ); @@ -208,6 +209,12 @@ struct header { 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; @@ -301,6 +308,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/info.cpp b/src/setup/info.cpp index c129693..1766a01 100644 --- a/src/setup/info.cpp +++ b/src/setup/info.cpp @@ -112,6 +112,13 @@ void load_wizard_and_decompressor(std::istream & is, const setup::version & vers if(version >= INNO_VERSION(2, 0, 0) || version.is_isx()) { load_wizard_images(is, version, info.wizard_images_small, entries); } + + // Duplicated dark mode versions + // TODO: figure out some way of specifying this + if(version >= INNO_VERSION(6, 6, 0)) { + load_wizard_images(is, version, info.wizard_images, entries); + load_wizard_images(is, version, info.wizard_images_small, entries); + } info.decompressor_dll.clear(); if(header.compression == stream::BZip2 diff --git a/src/setup/language.cpp b/src/setup/language.cpp index 6b40813..05f44ef 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,11 @@ 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)) { + 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); @@ -165,10 +177,10 @@ void language_entry::load(std::istream & is, const info & i) { if(!codepage) { codepage = util::cp_windows1252; } + } else if(i.version < INNO_VERSION(5, 3, 0)) { + (void)util::load(is); + codepage = util::cp_utf16le; } else { - if(i.version < INNO_VERSION(5, 3, 0)) { - (void)util::load(is); - } codepage = util::cp_utf16le; } @@ -185,10 +197,25 @@ void language_entry::load(std::istream & is, const info & i) { } else { dialog_font_standard_height = 0; } + + if(i.version >= INNO_VERSION(6, 6, 0)) { + dialog_font_scale_height = util::load(is); + dialog_font_scale_width = util::load(is); + } else { + dialog_font_scale_height = dialog_font_scale_width = 0; + } - title_font_size = util::load(is); + if(i.version < INNO_VERSION(6, 6, 0)) { + title_font_size = util::load(is); + } else { + title_font_size = 0; + } 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..48ab6cd 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_scale_height; + size_t dialog_font_scale_width; size_t title_font_size; size_t welcome_font_size; size_t copyright_font_size; diff --git a/src/setup/version.cpp b/src/setup/version.cpp index 89424e2..5932e9b 100644 --- a/src/setup/version.cpp +++ b/src/setup/version.cpp @@ -193,6 +193,8 @@ 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.5.3)", INNO_VERSION_EXT(6, 5, 3, 0), version::Unicode }, { "Inno Setup Setup Data (6.5.4)", INNO_VERSION_EXT(6, 5, 4, 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 From fed08ebcd76af403063ca75f5bce26f5125a0971 Mon Sep 17 00:00:00 2001 From: Charles Pigott Date: Wed, 28 Jan 2026 13:08:42 +0000 Subject: [PATCH 6/8] Inno 6.7 support --- src/setup/component.cpp | 4 ++- src/setup/file.cpp | 7 ++++ src/setup/header.cpp | 75 +++++++++++++++++++++++++++++++++++++---- src/setup/header.hpp | 25 ++++++++++++-- src/setup/info.cpp | 10 ++++++ src/setup/info.hpp | 1 + src/setup/task.cpp | 4 ++- src/setup/version.cpp | 1 + src/stream/block.cpp | 12 ++++--- 9 files changed, 124 insertions(+), 15 deletions(-) diff --git a/src/setup/component.cpp b/src/setup/component.cpp index eab4f1a..80804d5 100644 --- a/src/setup/component.cpp +++ b/src/setup/component.cpp @@ -74,7 +74,9 @@ 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)) { + 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 112f8a7..fd88d80 100644 --- a/src/setup/file.cpp +++ b/src/setup/file.cpp @@ -228,6 +228,13 @@ void file_entry::load(std::istream & is, const info & i) { } options |= flagreader.finalize(); + if(i.version >= INNO_VERSION(6, 7, 0)) { + // Inno forces the TSetupFileEntryOption enum to be 8 bytes, + // but currently only contains 5 bytes of values + (void)util::load(is); + (void)util::load(is); + (void)util::load(is); + } 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 ab849a0..bf23297 100644 --- a/src/setup/header.cpp +++ b/src/setup/header.cpp @@ -51,6 +51,12 @@ STORED_ENUM_MAP(stored_log_mode, header::AppendLog, header::OverwriteLog ); +STORED_ENUM_MAP(stored_light_control_styling, header::All, + header::All, + header::AllButButtons, + header::OnlyRequired +); + STORED_ENUM_MAP(stored_setup_style, header::ClassicStyle, header::ClassicStyle, header::ModernStyle @@ -282,6 +288,20 @@ void header::load(std::istream & is, const version & version) { } else { seven_zip_library_name.clear(); } + if(version >= INNO_VERSION(6, 7, 0)) { + std::string tmp; + 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); @@ -403,7 +423,17 @@ void header::load(std::istream & is, const version & version) { image_alpha_format = AlphaIgnored; } - if(version >= INNO_VERSION(6, 6, 0)) { + if(version >= INNO_VERSION(6, 7, 0)) { + image_back_color = util::load(is, version.bits()); + small_image_back_color = util::load(is, version.bits()); + back_color = util::load(is, version.bits()); + image_back_color2 = util::load(is, version.bits()); + small_image_back_color2 = util::load(is, version.bits()); + back_color2 = util::load(is, version.bits()); + image_opacity = util::load(is, version.bits()); + back_image_opacity = util::load(is, version.bits()); + wizard_light_control_styling = stored_enum(is).get(); + } else if(version >= INNO_VERSION(6, 6, 0)) { image_back_color = util::load(is, version.bits()); small_image_back_color = util::load(is, version.bits()); image_back_color2 = util::load(is, version.bits()); @@ -565,6 +595,12 @@ void header::load(std::istream & is, const version & version) { } options |= load_flags(is, version); + if(version >= INNO_VERSION(6, 7, 0)) { + // Inno forces the TSetupHeaderOption enum to be 8 bytes, + // but currently only contains 6 bytes of values + (void)util::load(is); + (void)util::load(is); + } if(version < INNO_VERSION(3, 0, 4)) { privileges_required = (options & AdminPrivilegesRequired) ? AdminPriviliges : NoPrivileges; @@ -664,19 +700,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)) { @@ -684,7 +721,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)) { @@ -699,7 +738,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); @@ -769,12 +810,20 @@ header::flags header::load_flags(std::istream & is, const version & version) { if(version >= INNO_VERSION(6, 3, 0)) { flagreader.add(UninstallLogging); } + if(version >= INNO_VERSION(6, 6, 0)) { flagreader.add(WizardModern); flagreader.add(WizardBorderStyled); flagreader.add(WizardKeepAspectRatio); + } + if(version >= INNO_VERSION(6, 6, 0) && version < INNO_VERSION(6, 7, 0)) { flagreader.add(WizardLightButtonsUnstyled); } + + if(version >= INNO_VERSION(6, 7, 0)) { + flagreader.add(RedirectionGuard); + flagreader.add(WizardBevelsHidden); + } return flagreader.finalize(); } @@ -868,8 +917,13 @@ NAMES(setup::header::flags, "Setup Option", "force close applications", "app name_has_consts", "use_previous_privileges", - "wizard_resizable", "uninstall_logging", + "wizard_modern", + "wizard_border_styled", + "wizard_keep_aspect_ratio", + "redirection_guard", + "wizard_bevels_hidden", + "uninstallable", "disable dir page", "disable program group page", @@ -885,6 +939,7 @@ NAMES(setup::header::flags, "Setup Option", "overwrite uninst reg entries", "encrypted", "wizard_light_buttons_unstyled", + "wizard_resizable", ) NAMES(setup::header::architecture_types, "Architecture", @@ -919,6 +974,12 @@ NAMES(setup::header::log_mode, "Uninstall Log Mode", "overwrite", ) +NAMES(setup::header::light_control_styling, "Light Control Styling", + "all", + "all but buttons", + "only required", +) + NAMES(setup::header::style, "Style", "classic", "modern", diff --git a/src/setup/header.hpp b/src/setup/header.hpp index fd27fe1..f7fd8d3 100644 --- a/src/setup/header.hpp +++ b/src/setup/header.hpp @@ -99,8 +99,12 @@ struct header { ForceCloseApplications, AppNameHasConsts, UsePreviousPrivileges, - WizardResizable, UninstallLogging, + WizardModern, + WizardBorderStyled, + WizardKeepAspectRatio, + RedirectionGuard, + WizardBevelsHidden, // Obsolete flags Uninstallable, @@ -117,7 +121,8 @@ struct header { BackSolid, OverwriteUninstRegEntries, EncryptionUsed, - WizardLightButtonsUnstyled + WizardLightButtonsUnstyled, + WizardResizable ); @@ -170,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; @@ -202,7 +212,17 @@ struct header { Color back_color; Color back_color2; Color image_back_color; + Color image_back_color2; Color small_image_back_color; + Color small_image_back_color2; + uint8_t image_opacity; + uint8_t back_image_opacity; + enum light_control_styling { + All, + AllButButtons, + OnlyRequired, + }; + light_control_styling wizard_light_control_styling; enum style { ClassicStyle, @@ -307,6 +327,7 @@ NAMED_FLAGS(setup::header::privileges_required_overrides) NAMED_ENUM(setup::header::alpha_format) NAMED_ENUM(setup::header::install_verbosity) NAMED_ENUM(setup::header::log_mode) +NAMED_ENUM(setup::header::light_control_styling) NAMED_ENUM(setup::header::style) NAMED_ENUM(setup::header::dark_style) NAMED_ENUM(setup::header::auto_bool) diff --git a/src/setup/info.cpp b/src/setup/info.cpp index 1766a01..a06a7ad 100644 --- a/src/setup/info.cpp +++ b/src/setup/info.cpp @@ -113,12 +113,22 @@ 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)) + { + load_wizard_images(is, version, info.wizard_images_back, entries); + } + // Duplicated dark mode versions // TODO: figure out some way of specifying this if(version >= INNO_VERSION(6, 6, 0)) { load_wizard_images(is, version, info.wizard_images, entries); load_wizard_images(is, version, info.wizard_images_small, entries); } + + if(version >= INNO_VERSION(6, 7, 0)) + { + load_wizard_images(is, version, info.wizard_images_back, entries); + } info.decompressor_dll.clear(); if(header.compression == stream::BZip2 diff --git a/src/setup/info.hpp b/src/setup/info.hpp index d4e1fb5..84c99cd 100644 --- a/src/setup/info.hpp +++ b/src/setup/info.hpp @@ -115,6 +115,7 @@ struct info { //! Loading enabled by \c WizardImages std::vector wizard_images; std::vector wizard_images_small; + std::vector wizard_images_back; //! 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..ced3674 100644 --- a/src/setup/task.cpp +++ b/src/setup/task.cpp @@ -45,7 +45,9 @@ 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)) { + 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 5932e9b..26bee13 100644 --- a/src/setup/version.cpp +++ b/src/setup/version.cpp @@ -195,6 +195,7 @@ const known_version versions[] = { { "Inno Setup Setup Data (6.5.4)", INNO_VERSION_EXT(6, 5, 4, 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 diff --git a/src/stream/block.cpp b/src/stream/block.cpp index c4e627c..5d222f6 100644 --- a/src/stream/block.cpp +++ b/src/stream/block.cpp @@ -171,12 +171,16 @@ 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)) { + stored_size = actual_checksum.load(base); + } else { + stored_size = actual_checksum.load(base); + } boost::uint8_t compressed = actual_checksum.load(base); #ifdef DEBUG @@ -197,7 +201,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) { @@ -221,7 +225,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, 0, static_cast(stored_size))); fis->exceptions(std::ios_base::badbit | std::ios_base::failbit); From 90711b55ff0cfe6b8280408547f35cbd7147f4b0 Mon Sep 17 00:00:00 2001 From: Charles Pigott Date: Fri, 30 Jan 2026 23:12:27 +0000 Subject: [PATCH 7/8] fixup! 6.5 improvements --- src/stream/block.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/block.cpp b/src/stream/block.cpp index 5d222f6..0612d16 100644 --- a/src/stream/block.cpp +++ b/src/stream/block.cpp @@ -156,13 +156,13 @@ block_reader::pointer block_reader::get(std::istream & base, const setup::versio USE_ENUM_NAMES(block_compression) #ifdef DEBUG - std::streampos current_pos = base.tellg(); + std::streamoff current_pos = base.tellg(); char dump_buffer[128]; std::streamsize bytes_read = base.read(dump_buffer, sizeof(dump_buffer)).gcount(); base.clear(); debug("block header at offset " << print_hex(current_pos) << ":"); - debug(print_hex_dump(dump_buffer, bytes_read, current_pos)); + debug(print_hex_dump(dump_buffer, std::size_t(bytes_read), std::size_t(current_pos))); base.seekg(current_pos); #endif From e8960a05eef5ea218bf05a1e23adc3f688c3d5b5 Mon Sep 17 00:00:00 2001 From: Michael Cho Date: Mon, 11 Aug 2025 12:06:17 -0400 Subject: [PATCH 8/8] CMake: Fix build with Boost 1.89.0 --- CMakeLists.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 88d12c1..b129635 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,16 +174,16 @@ else() endif() set(BOOST_REQUIRED_COMPONENTS - iostreams - filesystem - date_time - program_options - ) + iostreams + filesystem + date_time + program_options +) find_package(Boost REQUIRED COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) -if(Boost_VERSION VERSION_LESS 1.69.0) - find_package(Boost REQUIRED COMPONENTS ${BOOST_REQUIRED_COMPONENTS} system) +if(Boost_MAJOR_VERSION EQUAL 1 AND Boost_MINOR_VERSION LESS 69) + list(APPEND BOOST_REQUIRED_COMPONENTS system) + find_package(Boost REQUIRED COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) endif() - list(APPEND LIBRARIES ${Boost_LIBRARIES}) link_directories(${Boost_LIBRARY_DIRS}) include_directories(SYSTEM ${Boost_INCLUDE_DIR})