diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05123cd..a0c377b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,57 +1,97 @@ -name: CI +name: Release on: + workflow_dispatch: 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 + + $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: 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: false \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3870779 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.diff +*.cmd +build diff --git a/CMakeLists.txt b/CMakeLists.txt index 6995610..b129635 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,13 +173,17 @@ else() set(INNOEXTRACT_HAVE_LZMA 0) endif() -find_package(Boost REQUIRED COMPONENTS +set(BOOST_REQUIRED_COMPONENTS iostreams filesystem date_time - system program_options ) +find_package(Boost REQUIRED COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) +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}) @@ -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 3a5e826..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.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.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 e2b4fe9..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.3.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/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/cli/extract.cpp b/src/cli/extract.cpp index 4989311..c738288 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"); @@ -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/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/loader/offsets.cpp b/src/loader/offsets.cpp index 0c0d6a4..f151470 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/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/data.cpp b/src/setup/data.cpp index 4444ce2..c22b72b 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); @@ -136,20 +140,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 +166,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 +177,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/file.cpp b/src/setup/file.cpp index a2e917e..fd88d80 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,6 +105,26 @@ 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 + 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); location = util::load(is, i.version.bits()); @@ -189,8 +222,19 @@ 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(); + 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(); @@ -239,6 +283,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..7241e71 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 { + fvNone, + fvHash, + fvISSig, + }; + 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; + 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 bbe9df9..bf23297 100644 --- a/src/setup/header.cpp +++ b/src/setup/header.cpp @@ -51,11 +51,23 @@ 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 ); +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, @@ -266,6 +278,30 @@ 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(6, 5, 0)) { + is >> util::binary_string(seven_zip_library_name); + } 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); @@ -319,6 +355,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()); @@ -350,6 +391,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 { @@ -361,10 +403,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; @@ -376,8 +422,28 @@ void header::load(std::istream & is, const version & version) { } else { image_alpha_format = AlphaIgnored; } - - if(version >= INNO_VERSION(6, 4, 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()); + 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) + 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)) { @@ -390,7 +456,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)) { @@ -527,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; @@ -626,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)) { @@ -646,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)) { @@ -661,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); @@ -690,7 +769,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)) { @@ -724,11 +803,27 @@ 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); + } + 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(); } @@ -762,6 +857,8 @@ 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); @@ -808,7 +905,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", @@ -821,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", @@ -836,6 +937,9 @@ NAMES(setup::header::flags, "Setup Option", "disable dir exists warning", "back solid", "overwrite uninst reg entries", + "encrypted", + "wizard_light_buttons_unstyled", + "wizard_resizable", ) NAMES(setup::header::architecture_types, "Architecture", @@ -870,11 +974,23 @@ 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", ) +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 78e5b64..f7fd8d3 100644 --- a/src/setup/header.hpp +++ b/src/setup/header.hpp @@ -87,7 +87,6 @@ struct header { WizardImageStretch, AppendDefaultDirName, AppendDefaultGroupName, - EncryptionUsed, ChangesEnvironment, ShowUndisplayableLanguages, SetupLogging, @@ -100,8 +99,12 @@ struct header { ForceCloseApplications, AppNameHasConsts, UsePreviousPrivileges, - WizardResizable, UninstallLogging, + WizardModern, + WizardBorderStyled, + WizardKeepAspectRatio, + RedirectionGuard, + WizardBevelsHidden, // Obsolete flags Uninstallable, @@ -116,7 +119,10 @@ struct header { DetectLanguageUsingLocale, DisableDirExistsWarning, BackSolid, - OverwriteUninstRegEntries + OverwriteUninstRegEntries, + EncryptionUsed, + WizardLightButtonsUnstyled, + WizardResizable ); @@ -167,6 +173,13 @@ 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 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; @@ -182,6 +195,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; @@ -198,13 +212,29 @@ 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, 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; @@ -297,7 +327,9 @@ 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) 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 d696924..a06a7ad 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" @@ -38,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" @@ -110,6 +112,23 @@ 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); } + + 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 @@ -158,7 +177,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"); @@ -192,34 +211,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("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); @@ -238,10 +259,14 @@ 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.known) { if(entries & NoUnknownVersion) { std::ostringstream oss; @@ -263,6 +288,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 e08cfd4..84c99cd 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" @@ -40,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; @@ -66,6 +68,7 @@ struct info { DeleteEntries, UninstallDeleteEntries, Directories, + ISSigs, Files, Icons, IniEntries, @@ -95,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 @@ -111,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 @@ -130,7 +135,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..23475e5 --- /dev/null +++ b/src/setup/issigkey.cpp @@ -0,0 +1,33 @@ +/* + * 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" + +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 diff --git a/src/setup/issigkey.hpp b/src/setup/issigkey.hpp new file mode 100644 index 0000000..15a346d --- /dev/null +++ b/src/setup/issigkey.hpp @@ -0,0 +1,41 @@ +/* + * 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 + +#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 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/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 7f68af2..26bee13 100644 --- a/src/setup/version.cpp +++ b/src/setup/version.cpp @@ -186,6 +186,16 @@ 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 }, + { "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 }, + { "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/setup/version.hpp b/src/setup/version.hpp index f882978..f040c9d 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; @@ -78,8 +79,11 @@ struct version { void load(std::istream & is); 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..0612d16 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,20 +155,40 @@ block_reader::pointer block_reader::get(std::istream & base, const setup::versio USE_ENUM_NAMES(block_compression) +#ifdef DEBUG + 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, std::size_t(bytes_read), std::size_t(current_pos))); + + base.seekg(current_pos); +#endif + boost::uint32_t expected_checksum = util::load(base); 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 + 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); @@ -180,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) { @@ -204,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); 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..27491f0 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(slice_size_64 > boost::uint64_t(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()); } @@ -208,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); @@ -218,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 74c754c..350f431 100644 --- a/src/util/output.hpp +++ b/src/util/output.hpp @@ -182,6 +182,67 @@ 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",