diff -Nru innoextract-1.6/CHANGELOG innoextract-1.7/CHANGELOG --- innoextract-1.6/CHANGELOG 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/CHANGELOG 2018-06-12 18:50:34.000000000 +0000 @@ -1,4 +1,23 @@ +innoextract 1.7 (2018-06-12) + - Added support for Inno Setup 5.6.0 installers + - Added support for new GOG installers with GOG Galaxy file parts + - Added support for encrypted installers with the --password (-P) and --password-file options + - Added a --show-password option to print password check information + - Added a --check-password option to abort if the provided password does not match the stored checksum + - Added a --info (-i) convenience option to print information about the installer + - Added a --list-sizes option to print file sizes even with --quiet or --silent + - Added a --list-checksums option to print file checksums + - Added a --data-version (-V) option to print the data version and exit + - Added a --no-extract-unknown (-n) option to abort on unknown Inno Setup data versions + - Fixed building in paths that contain regex expressions + - Fixed case-sensitivity in parent directory when creating subdirectories + - Fixed .bin slice file names used with Inno Setup versions older than 4.1.7 + - Fixed build with newer libc++ versions + - Made loading of .bin slice files case-insensitive + - The --test option can now be combined with --extract to abort on file checksum errors + - Now compiles in C++17 mode if supported + innoextract 1.6 (2016-03-24) - Added support for Inno Setup 5.5.7 (and 5.5.8) installers - Added a --collisions=rename-all option @@ -53,7 +72,7 @@ - Fixed original console text color not being restored under Windows innoextract 1.4 (2013-03-11) - - Fixed build on non-Linux platforms with a separate libiconv (Windows™, Mac OS X) + - Fixed build on non-Linux platforms with a separate libiconv (Windows™, macOS) - Fixed build on systems with non-standard iconv function prototypes (FreeBSD) - Fixed MSVC build - Fixed build with older glibc versions diff -Nru innoextract-1.6/cmake/check/cxx11-alignof.cpp innoextract-1.7/cmake/check/cxx11-alignof.cpp --- innoextract-1.6/cmake/check/cxx11-alignof.cpp 1970-01-01 00:00:00.000000000 +0000 +++ innoextract-1.7/cmake/check/cxx11-alignof.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -0,0 +1,3 @@ +int main() { + return alignof(int) != 1; +} diff -Nru innoextract-1.6/cmake/check/cxx11-std-codecvt_utf8_utf16.cpp innoextract-1.7/cmake/check/cxx11-std-codecvt_utf8_utf16.cpp --- innoextract-1.6/cmake/check/cxx11-std-codecvt_utf8_utf16.cpp 1970-01-01 00:00:00.000000000 +0000 +++ innoextract-1.7/cmake/check/cxx11-std-codecvt_utf8_utf16.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -0,0 +1,6 @@ +#include + +int main() { + std::codecvt_utf8_utf16 codecvt; + return 0; +} diff -Nru innoextract-1.6/cmake/check/cxx11-std-unique_ptr.cpp innoextract-1.7/cmake/check/cxx11-std-unique_ptr.cpp --- innoextract-1.6/cmake/check/cxx11-std-unique_ptr.cpp 1970-01-01 00:00:00.000000000 +0000 +++ innoextract-1.7/cmake/check/cxx11-std-unique_ptr.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -0,0 +1,6 @@ +#include + +int main() { + std::unique_ptr ptr(new char); + return !ptr; +} diff -Nru innoextract-1.6/cmake/check-cxx11-alignof.cpp innoextract-1.7/cmake/check-cxx11-alignof.cpp --- innoextract-1.6/cmake/check-cxx11-alignof.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/cmake/check-cxx11-alignof.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -int main() { - return alignof(int) != 1; -} diff -Nru innoextract-1.6/cmake/check-cxx11-std-codecvt_utf8_utf16.cpp innoextract-1.7/cmake/check-cxx11-std-codecvt_utf8_utf16.cpp --- innoextract-1.6/cmake/check-cxx11-std-codecvt_utf8_utf16.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/cmake/check-cxx11-std-codecvt_utf8_utf16.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -#include - -int main() { - std::codecvt_utf8_utf16 codecvt; - return 0; -} diff -Nru innoextract-1.6/cmake/check-cxx11-std-unique_ptr.cpp innoextract-1.7/cmake/check-cxx11-std-unique_ptr.cpp --- innoextract-1.6/cmake/check-cxx11-std-unique_ptr.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/cmake/check-cxx11-std-unique_ptr.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -#include - -int main() { - std::unique_ptr ptr(new char); - return !ptr; -} diff -Nru innoextract-1.6/cmake/CompileCheck.cmake innoextract-1.7/cmake/CompileCheck.cmake --- innoextract-1.6/cmake/CompileCheck.cmake 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/cmake/CompileCheck.cmake 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ -# Copyright (C) 2011-2016 Daniel Scharrer +# Copyright (C) 2011-2017 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 @@ -74,7 +74,7 @@ # Check if we can compile and link a simple file with the new flags try_compile( - check_compiler_flag ${CMAKE_BINARY_DIR} ${FILE} + check_compiler_flag ${PROJECT_BINARY_DIR} ${FILE} CMAKE_FLAGS "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" "-DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS}" "-DCMAKE_SHARED_LINKER_FLAGS=${CMAKE_SHARED_LINKER_FLAGS}" @@ -117,16 +117,34 @@ function(check_flag RESULT FLAG TYPE) set(compile_test_file "${CMAKE_CURRENT_BINARY_DIR}/compile_flag_test.cpp") - file(WRITE ${compile_test_file} "__attribute__((const)) int main(){ return 0; }\n") + if(MSVC) + file(WRITE ${compile_test_file} "int main(){ return 0; }\n") + else() + file(WRITE ${compile_test_file} "__attribute__((const)) int main(){ return 0; }\n") + endif() check_compile(result "${compile_test_file}" "${FLAG}" "${TYPE} flag") set(${RESULT} "${result}" PARENT_SCOPE) endfunction(check_flag) +macro(strip_warning_flags VAR) + string(REGEX REPLACE "(^| )\\-(W[^ ]*|pedantic)" "" ${VAR} "${${VAR}}") +endmacro() + function(check_builtin RESULT EXPR) - set(compile_test_file "${CMAKE_CURRENT_BINARY_DIR}/compile_expr_test.cpp") + string(REGEX REPLACE "[^a-zA-Z0-9_][^a-zA-Z0-9_]*" "-" check "${EXPR}") + string(REGEX REPLACE "_*\\-_*" "-" check "${check}") + string(REGEX REPLACE "^[_\\-]+" "" check "${check}") + string(REGEX REPLACE "[_\\-]+$" "" check "${check}") + set(compile_test_file "${CMAKE_CURRENT_BINARY_DIR}/check-builtin-${check}.cpp") string(REGEX MATCH "[a-zA-Z_][a-zA-Z_0-9]*" type "${EXPR}") file(WRITE ${compile_test_file} "__attribute__((const)) int main(){ (void)(${EXPR}); return 0; }\n") + set(old_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(old_CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") + strip_warning_flags(CMAKE_CXX_FLAGS) + strip_warning_flags(CMAKE_EXE_LINKER_FLAGS) check_compile(result "${compile_test_file}" "${type}" "compiler builtin") + set(CMAKE_CXX_FLAGS "${old_CMAKE_CXX_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${old_CMAKE_EXE_LINKER_FLAGS}") set(${RESULT} "${result}" PARENT_SCOPE) endfunction(check_builtin) @@ -178,7 +196,7 @@ list(APPEND LIBRARY_FILE "${CMAKE_THREAD_LIBS_INIT}") endif() try_compile( - CHECK_${LIBRARY_NAME}_LINK "${CMAKE_BINARY_DIR}" "${link_test_file}" + CHECK_${LIBRARY_NAME}_LINK "${PROJECT_BINARY_DIR}" "${link_test_file}" CMAKE_FLAGS "-DLINK_LIBRARIES=${LIBRARY_FILE}" "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" "-DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS}" @@ -191,6 +209,8 @@ ############################################################################## # Check that a a library actually works for the current configuration +# This is neede because CMake prefers /usr/lib over /usr/lib32 for -m32 builds +# See https://public.kitware.com/Bug/view.php?id=11260 function(check_link_library LIBRARY_NAME LIBRARY_VARIABLE) if(MSVC) diff -Nru innoextract-1.6/cmake/cpplint.py innoextract-1.7/cmake/cpplint.py --- innoextract-1.6/cmake/cpplint.py 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/cmake/cpplint.py 2018-06-12 18:50:34.000000000 +0000 @@ -14,7 +14,7 @@ # - Allow #ifdef BOOST_PP_IS_ITERATING + #endif in place of header guards # - C++ source files are named .cpp, not .cc # -# Copyright (c) 2011-1015 Daniel Scharrer +# Copyright (c) 2011-2018 Daniel Scharrer # Copyright (c) 2009 Google Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -675,7 +675,7 @@ class _FunctionState(object): """Tracks current function name and the number of lines in its body.""" - _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. + _NORMAL_TRIGGER = 260 # for --v=0, 500 for --v=1, etc. _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. def __init__(self): @@ -2326,9 +2326,9 @@ not Match(r'^\s*//.*http(s?)://\S*$', line) and not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): line_width = GetLineWidth(line) - if line_width > 100: + if line_width > 120: error(filename, linenum, 'whitespace/line_length', 4, - 'Lines should very rarely be longer than 100 characters') + 'Lines should very rarely be longer than 120 characters') if (cleansed_line.count(';') > 1 and # allow one-line definitions for small structs or classes diff -Nru innoextract-1.6/cmake/CreateSourceGroups.cmake innoextract-1.7/cmake/CreateSourceGroups.cmake --- innoextract-1.6/cmake/CreateSourceGroups.cmake 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/cmake/CreateSourceGroups.cmake 2018-06-12 18:50:34.000000000 +0000 @@ -4,7 +4,7 @@ # that replicate the folder hierarchy on disk function(create_source_groups source_files_variable) foreach(source_file ${${source_files_variable}}) - string( REGEX REPLACE ${CMAKE_CURRENT_SOURCE_DIR} "" relative_directory "${source_file}") + string( REPLACE ${CMAKE_CURRENT_SOURCE_DIR} "" relative_directory "${source_file}") string( REGEX REPLACE "[\\\\/][^\\\\/]*$" "" relative_directory "${relative_directory}") string( REGEX REPLACE "^[\\\\/]" "" relative_directory "${relative_directory}") if( WIN32 ) diff -Nru innoextract-1.6/cmake/CXX11Check.cmake innoextract-1.7/cmake/CXX11Check.cmake --- innoextract-1.6/cmake/CXX11Check.cmake 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/cmake/CXX11Check.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ - -# Copyright (C) 2013-2016 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(CheckCXXSourceCompiles) -include(CompileCheck) - -set(_HAS_CXX11 0) -get_filename_component(CXX11_CHECK_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) - -function(enable_cxx11) - if(MSVC) - if(NOT MSVC_VERSION LESS 1600) - set(_HAS_CXX11 1 PARENT_SCOPE) - endif() - else() - add_cxxflag("-std=c++14") - if(NOT FLAG_FOUND) - add_cxxflag("-std=c++11") - endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" PARENT_SCOPE) - if(FLAG_FOUND OR NOT CMAKE_COMPILER_IS_GNUCXX) - if(SET_WARNING_FLAGS) - add_cxxflag("-pedantic") - endif() - set(_HAS_CXX11 1 PARENT_SCOPE) - endif() - endif() -endfunction(enable_cxx11) - -function(check_cxx11 CHECK RESULTVAR) - if(_HAS_CXX11) - if(MSVC AND ARGC GREATER 2) - if(MSVC_VERSION LESS ARGV2) - set(result) - else() - set(result 1) - endif() - else() - string(REGEX REPLACE "[^a-zA-Z0-9_][^a-zA-Z0-9_]*" "-" check "${CHECK}") - set(file "${CXX11_CHECK_DIR}/check-cxx11-${check}.cpp") - check_compile(result "${file}" "${CHECK}" "C++11 feature") - endif() - if(NOT DEFINED result OR result STREQUAL "") - set(${RESULTVAR} OFF PARENT_SCOPE) - else() - set(${RESULTVAR} ON PARENT_SCOPE) - endif() - else() - set(${RESULTVAR} OFF PARENT_SCOPE) - endif() -endfunction() diff -Nru innoextract-1.6/cmake/CXXVersionCheck.cmake innoextract-1.7/cmake/CXXVersionCheck.cmake --- innoextract-1.6/cmake/CXXVersionCheck.cmake 1970-01-01 00:00:00.000000000 +0000 +++ innoextract-1.7/cmake/CXXVersionCheck.cmake 2018-06-12 18:50:34.000000000 +0000 @@ -0,0 +1,87 @@ + +# Copyright (C) 2013-2018 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(CheckCXXSourceCompiles) +include(CompileCheck) + +set(_HAS_CXX11 0) +set(CXX11_CHECK_DIR "${CMAKE_CURRENT_LIST_DIR}/check") + +function(enable_cxx_version version) + if(MSVC) + if(NOT version LESS 2011 AND MSVC_VERSION LESS 1600) + if(NOT version LESS 2017 AND NOT MSVC_VERSION LESS 1911) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17") + elseif(NOT version LESS 2014 AND NOT MSVC_VERSION LESS 1910) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++14") + elseif(NOT version LESS 2014 AND NOT MSVC_VERSION LESS 1900) + # Only introduced with update 3 of MSVC 2015 + add_cxxflag("/std:c++14") + endif() + set(_HAS_CXX11 1 PARENT_SCOPE) + endif() + else() + set(FLAG_FOUND 0) + if(NOT version LESS 2017) + add_cxxflag("-std=c++17") + endif() + if(NOT version LESS 2014 AND NOT FLAG_FOUND) + add_cxxflag("-std=c++14") + endif() + if(NOT version LESS 2011 AND NOT FLAG_FOUND) + add_cxxflag("-std=c++11") + endif() + if(NOT version LESS 2011 AND FLAG_FOUND OR NOT CMAKE_COMPILER_IS_GNUCXX) + if(SET_WARNING_FLAGS) + add_cxxflag("-pedantic") + endif() + set(_HAS_CXX11 1 PARENT_SCOPE) + endif() + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" PARENT_SCOPE) +endfunction(enable_cxx_version) + +function(check_cxx11 CHECK RESULTVAR) + if(_HAS_CXX11) + if(MSVC AND ARGC GREATER 2) + if(MSVC_VERSION LESS ARGV2) + set(result) + else() + set(result 1) + endif() + else() + string(REGEX REPLACE "[^a-zA-Z0-9_][^a-zA-Z0-9_]*" "-" check "${CHECK}") + set(file "${CXX11_CHECK_DIR}/cxx11-${check}.cpp") + set(old_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(old_CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") + strip_warning_flags(CMAKE_CXX_FLAGS) + strip_warning_flags(CMAKE_EXE_LINKER_FLAGS) + check_compile(result "${file}" "${CHECK}" "C++11 feature") + set(CMAKE_CXX_FLAGS "${old_CMAKE_CXX_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${old_CMAKE_EXE_LINKER_FLAGS}") + endif() + if(NOT DEFINED result OR result STREQUAL "") + set(${RESULTVAR} OFF PARENT_SCOPE) + else() + set(${RESULTVAR} ON PARENT_SCOPE) + endif() + else() + set(${RESULTVAR} OFF PARENT_SCOPE) + endif() +endfunction() diff -Nru innoextract-1.6/cmake/Doxygen.cmake innoextract-1.7/cmake/Doxygen.cmake --- innoextract-1.6/cmake/Doxygen.cmake 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/cmake/Doxygen.cmake 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ -# Copyright (C) 2011-2012 Daniel Scharrer +# Copyright (C) 2011-2017 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 @@ -42,7 +42,7 @@ return() endif() - set(doxyfile "${CMAKE_BINARY_DIR}/Doxyfile.${TARGET_NAME}") + set(doxyfile "${PROJECT_BINARY_DIR}/Doxyfile.${TARGET_NAME}") set(defines "-DDOXYGEN_OUTPUT_DIR=${OUT_DIR}") version_file("${DOXYFILE_IN}" "${doxyfile}" "${VERSION_FILE}" "${GIT_DIR}" "${defines}") @@ -51,7 +51,7 @@ COMMAND "${CMAKE_COMMAND}" -E make_directory "${OUT_DIR}" COMMAND ${DOXYGEN_EXECUTABLE} "${doxyfile}" DEPENDS "${doxyfile}" - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" COMMENT "Building doxygen documentation." VERBATIM SOURCES "${DOXYFILE_IN}" diff -Nru innoextract-1.6/cmake/StyleCheck.cmake innoextract-1.7/cmake/StyleCheck.cmake --- innoextract-1.6/cmake/StyleCheck.cmake 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/cmake/StyleCheck.cmake 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ -# Copyright (C) 2013-2015 Daniel Scharrer +# Copyright (C) 2013-2018 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 @@ -42,6 +42,9 @@ # Suggessts excessive indentation. set(STYLE_FILTER ${STYLE_FILTER},-whitespace/labels) +# Disallows brace on new line after long class memeber init list +set(STYLE_FILTER ${STYLE_FILTER},-whitespace/braces) + # Don't tell me how to name my variables. set(STYLE_FILTER ${STYLE_FILTER},-runtime/arrays) @@ -71,7 +74,7 @@ add_custom_target(${TARGET_NAME} COMMAND "${CMAKE_COMMAND}" -E chdir - "${CMAKE_SOURCE_DIR}" + "${PROJECT_SOURCE_DIR}" "${PYTHON_EXECUTABLE}" "${STYLE_CHECK_SCRIPT}" "--filter=${STYLE_FILTER}" diff -Nru innoextract-1.6/CMakeLists.txt innoextract-1.7/CMakeLists.txt --- innoextract-1.6/CMakeLists.txt 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/CMakeLists.txt 2018-06-12 18:50:34.000000000 +0000 @@ -11,18 +11,30 @@ # Define configuration options +macro(suboption _var _comment _type _default) + if(NOT DEFINED ${_var}) + set(${_var} "${_default}") + else() + set(${_var} "${${_var}}" CACHE ${_type} "${_comment}") + endif() +endmacro() + option(STRICT_USE "Abort if there are missing optional dependencies" OFF) -option(USE_LZMA "Build lzma decompression support" ON) +option(USE_ARC4 "Build ARC4 decryption support" ON) +option(USE_LZMA "Build LZMA decompression support" ON) set(WITH_CONV CACHE STRING "The library to use for charset conversions") -option(ENABLE_BUILTIN_CONV "Build internal charset conversion routines" ON) option(DEBUG_EXTRA "Expensive debug options" OFF) option(SET_WARNING_FLAGS "Adjust compiler warning flags" ON) option(SET_OPTIMIZATION_FLAGS "Adjust compiler optimization flags" ON) -option(USE_CXX11 "Try to use C++11 if available" ON) +suboption(CXX_STD_VERSION "Maximum C++ standard version to enable" STRING 2017) option(USE_DYNAMIC_UTIMENSAT "Dynamically load utimensat if not available at compile time" OFF) -if(NOT DEFINED DEBUG AND CMAKE_BUILD_TYPE STREQUAL "Debug") - set(DEBUG 1) + +set(default_DEBUG OFF) +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(default_DEBUG ON) endif() +suboption(DEBUG "Build with debug output" BOOL ${default_DEBUG}) + if(DEBUG) add_definitions(-DDEBUG=1) endif() @@ -61,11 +73,11 @@ include(CheckSymbolExists) -set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") # For custom cmake modules +set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") # For custom cmake modules include(BuildType) include(CompileCheck) include(CreateSourceGroups) -include(CXX11Check) +include(CXXVersionCheck) include(Doxygen) include(FilterList) include(PrintConfiguration) @@ -106,6 +118,10 @@ unset(LIBRARIES) +if(USE_ARC4) + set(INNOEXTRACT_HAVE_ARC4 1) +endif() + if(USE_LZMA) find_package(LZMA REQUIRED) check_link_library(LZMA LZMA_LIBRARIES) @@ -150,7 +166,6 @@ set(INNOEXTRACT_HAVE_ICONV 0) set(INNOEXTRACT_HAVE_WIN32_CONV 0) -set(INNOEXTRACT_HAVE_BUILTIN_CONV ${ENABLE_BUILTIN_CONV}) if(WIN32 AND (NOT WITH_CONV OR WITH_CONV STREQUAL "win32")) set(INNOEXTRACT_HAVE_WIN32_CONV 1) elseif(NOT WITH_CONV OR WITH_CONV STREQUAL "iconv") @@ -170,19 +185,14 @@ elseif(WITH_CONV AND NOT WITH_CONV STREQUAL "builtin") message(FATAL_ERROR "Invalid WITH_CONV option: ${WITH_CONV}") endif() -if(NOT INNOEXTRACT_HAVE_ICONV AND NOT INNOEXTRACT_HAVE_WIN32_CONV - AND NOT INNOEXTRACT_HAVE_BUILTIN_CONV) - message(WARNING "\nBuilding without any charset conversion support.\n" - "Any non-ASCII characters in extracted filenames will be missing.") -endif() # Set compiler flags if(Boost_VERSION LESS 104800) # Older Boost versions don't work with C++11 -elseif(USE_CXX11) - enable_cxx11() +elseif(NOT CXX_STD_VERSION LESS 2011) + enable_cxx_version(${CXX_STD_VERSION}) check_cxx11("alignof" INNOEXTRACT_HAVE_ALIGNOF) if(WIN32) check_cxx11("std::codecvt_utf8_utf16" INNOEXTRACT_HAVE_STD_CODECVT_UTF8_UTF16 1600) @@ -248,6 +258,10 @@ check_symbol_exists(waitpid "sys/wait.h" INNOEXTRACT_HAVE_WAITPID) endif() +endif() + +if(NOT MSVC) + if(CMAKE_CXX_COMPILER_ID STREQUAL "PathScale") # EKOPath recognizes these but then fails to link else() @@ -283,10 +297,14 @@ src/cli/extract.cpp src/cli/gog.hpp src/cli/gog.cpp + src/cli/goggalaxy.hpp + src/cli/goggalaxy.cpp src/cli/main.cpp src/crypto/adler32.hpp src/crypto/adler32.cpp + src/crypto/arc4.hpp if INNOEXTRACT_HAVE_ARC4 + src/crypto/arc4.cpp if INNOEXTRACT_HAVE_ARC4 src/crypto/checksum.hpp src/crypto/checksum.cpp src/crypto/crc32.hpp @@ -401,11 +419,17 @@ configure_file("src/configure.hpp.in" "configure.hpp") -set(VERSION_FILE "${CMAKE_BINARY_DIR}/release.cpp") +set(VERSION_FILE "${PROJECT_BINARY_DIR}/release.cpp") set(VERSION_SOURCES VERSION "VERSION" LICENSE "LICENSE") -version_file("src/release.cpp.in" "${VERSION_FILE}" "${VERSION_SOURCES}" ".git") +version_file("src/release.cpp.in" ${VERSION_FILE} "${VERSION_SOURCES}" ".git") list(APPEND INNOEXTRACT_SOURCES ${VERSION_FILE}) +set(MAN_INPUT "doc/innoextract.1.in") +set(MAN_FILE "${PROJECT_BINARY_DIR}/innoextract.1") +set(MAN_SOURCES VERSION "VERSION" CHANGELOG "CHANGELOG") +version_file(${MAN_INPUT} ${MAN_FILE} "${MAN_SOURCES}" ".git") +add_custom_target(manpage ALL DEPENDS ${MAN_FILE}) + # Main targets @@ -414,14 +438,14 @@ install(TARGETS innoextract RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -install(FILES doc/innoextract.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 OPTIONAL) +install(FILES ${MAN_FILE} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 OPTIONAL) # Additional targets. add_style_check_target(style "${ALL_INNOEXTRACT_SOURCES}" innoextract) -add_doxygen_target(doc "doc/Doxyfile.in" "VERSION" ".git" "${CMAKE_BINARY_DIR}/doc") +add_doxygen_target(doc "doc/Doxyfile.in" "VERSION" ".git" "${PROJECT_BINARY_DIR}/doc") # Print a configuration summary @@ -435,6 +459,10 @@ set(BUILD_TYPE_SUFFIX "${BUILD_TYPE_SUFFIX} without debug output") endif() message(" - Build type: ${CMAKE_BUILD_TYPE}${BUILD_TYPE_SUFFIX}") +print_configuration("ARC4 decryption" FIRST + INNOEXTRACT_HAVE_ARC4 "enabled" + 1 "disabled" +) print_configuration("LZMA decompression" FIRST INNOEXTRACT_HAVE_LZMA "enabled" 1 "disabled" @@ -452,6 +480,6 @@ print_configuration("Charset conversion" INNOEXTRACT_HAVE_ICONV "iconv" INNOEXTRACT_HAVE_WIN32_CONV "Win32" - INNOEXTRACT_HAVE_BUILTIN_CONV "builtin" + 1 "builtin" ) message("") diff -Nru innoextract-1.6/debian/changelog innoextract-1.7/debian/changelog --- innoextract-1.6/debian/changelog 2017-10-26 17:16:53.000000000 +0000 +++ innoextract-1.7/debian/changelog 2018-06-12 18:50:32.000000000 +0000 @@ -1,58 +1,83 @@ -innoextract (1.6-1build3) bionic; urgency=medium +innoextract (1.7-0ppa1~cosmic) cosmic; urgency=low - * No-change rebuild for boost soname change. + * Bump version to 1.7 (new upstream release): + * Added support for Inno Setup 5.6.0 installers + * Added support for new GOG installers with GOG Galaxy file parts + * Added support for encrypted installers + * Added --list-sizes and --list-checksums options to print file information + * Adde a --data-version (-V) option to check if an executable is an + Inno Setup installer + * Fixed case-sensitivity in parent directory when creating subdirectories + * Fixed issues with names used to load .bin slice files - -- Matthias Klose Thu, 26 Oct 2017 17:16:53 +0000 + -- Daniel Scharrer Tue, 12 Jun 2018 20:50:32 +0200 -innoextract (1.6-1build2) zesty; urgency=high +innoextract (1.6-0ppa1~cosmic) cosmic; urgency=low - * No change rebuild against boost1.62. + * Added support for Inno Setup 5.5.7 (and 5.5.8) installers + * Added a --collisions=rename-all option + * Fixed issues with the --collisions=rename option + * Unsafe characters in special constant strings (ie : in {code:…}) are now replaced with $ + * Windows: Fixed progress bar flickering while printing extracted filenames + * Windows binaries: Fixed crash on platforms without AVX support - -- Dimitri John Ledkov Tue, 01 Nov 2016 16:24:15 +0000 + -- Daniel Scharrer Fri, 25 Mar 2016 00:27:10 +0100 -innoextract (1.6-1build1) yakkety; urgency=medium +innoextract (1.5-0ppa1~cosmic) cosmic; urgency=low - * No-change rebuild for boost soname change. + * Bump version to 1.5 (new upstream release): + * Added support for Inno Setup 5.5.6 installers + * Added --include and --exclude-temp options to filter extracted files + * Improved handling of file collisions and added a --collisions option to control the behavior + * Added support for newer GOG.com multi-part installers via the --gog option + * Added support for building without iconv, using builtin conversions and/or Win32 API instead + * Various bug fixes and improvements - -- Matthias Klose Thu, 04 Aug 2016 08:19:28 +0000 + -- Daniel Scharrer Thu, 24 Sep 2015 22:54:26 +0200 -innoextract (1.6-1) unstable; urgency=low +innoextract (1.4-0ppa1~cosmic) cosmic; urgency=low - * New upstream release - * Added build-hardening flags - * Updated Homepage and Vcs-URLs - * Updated debian Standards-Version - * Updated watch file with PGP support and releases instead of tags + * Bump version to 1.4 (new upstream release): + * Fixed issues with the progress bar in sandbox environments + * Fixed extracting very large installers with 32-bit innoextract builds + * Improved handling + * The --list command-line option can now combined with --test or --extract + * The --version command-line option can now be modified with --quiet + or --silent + * Added support for preserving timestamps of extracted files + (enabled by default) + * Added a --timestamps (-T) command-line options to control or disable + file timestamps + * Added an --output-dir (-d) command-line option to control where files + are extracted + * Various bug fixes and tweaks - -- Lennart Weller Fri, 08 Apr 2016 11:52:16 +0200 + -- Daniel Scharrer Mon, 11 Mar 2013 17:19:38 +0100 -innoextract (1.5-1) unstable; urgency=low +innoextract (1.3-0ppa1~cosmic) cosmic; urgency=low - * New upstream release (Closes: #799985) + * New upstream release 1.3: + * Respect --quiet and --silent for multi-file installers + * Compile in C++11 mode if supported + * Warn about unsupported setup data versions + * Add support for Inno Setup 5.5.0 installers - -- Lennart Weller Fri, 25 Sep 2015 08:37:46 +0200 + -- Daniel Scharrer Tue, 03 Jul 2012 18:19:42 +0200 -innoextract (1.4-1) unstable; urgency=low +innoextract (1.2-0ppa2~cosmic) cosmic; urgency=low - * New upstream release (Closes: #730524) + * Install the upstream changelog file with the correct name. - -- Lennart Weller Wed, 27 Nov 2013 15:42:57 +0100 + -- Daniel Scharrer Tue, 03 Apr 2012 21:17:42 +0200 -innoextract (1.3-1) unstable; urgency=low +innoextract (1.2-0ppa1~cosmic) cosmic; urgency=low - * New upstream release - * Update Debian Standards Version to 3.9.4 + * New upstream release: fix compile errors on older systems and remove unused dependency. - -- Lennart Weller Thu, 25 Oct 2012 23:01:31 +0200 + -- Daniel Scharrer Sun, 01 Apr 2012 02:26:24 +0200 -innoextract (1.2+git20120504-2) unstable; urgency=low +innoextract (1.1-0ppa1~cosmic) cosmic; urgency=low - * introduced hardening to the binary to solve all warnings + * Initial release - -- Lennart Weller Thu, 04 Oct 2012 23:05:27 +0200 - -innoextract (1.2+git20120504-1) unstable; urgency=low - - * Initial release. (Closes: #678294) - - -- Lennart Weller Wed, 20 Jun 2012 18:12:47 +0200 + -- Daniel Scharrer Sun, 25 Mar 2012 08:01:24 +0200 diff -Nru innoextract-1.6/debian/compat innoextract-1.7/debian/compat --- innoextract-1.6/debian/compat 2016-04-08 10:27:55.000000000 +0000 +++ innoextract-1.7/debian/compat 2014-06-17 17:39:04.000000000 +0000 @@ -1 +1 @@ -9 +7 diff -Nru innoextract-1.6/debian/control innoextract-1.7/debian/control --- innoextract-1.6/debian/control 2016-11-01 16:24:15.000000000 +0000 +++ innoextract-1.7/debian/control 2018-06-11 10:42:57.000000000 +0000 @@ -1,30 +1,17 @@ Source: innoextract Section: utils Priority: extra -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Lennart Weller -Uploaders: Sebastian Reichel -Standards-Version: 3.9.7 -Vcs-git: https://anonscm.debian.org/git/collab-maint/innoextract.git -Vcs-Browser: https://anonscm.debian.org/git/collab-maint/innoextract.git +Maintainer: Daniel Scharrer +Build-Depends: debhelper (>= 7.0.50), cmake (>= 2.8), libboost-dev, libboost-iostreams-dev , libboost-filesystem-dev, libboost-date-time-dev, libboost-system-dev, libboost-program-options-dev, liblzma-dev +Standards-Version: 3.9.4 Homepage: http://constexpr.org/innoextract/ -Build-Depends: cmake, - debhelper (>= 9), - dpkg-dev (>= 1.16.1~), - libboost-iostreams-dev, - libboost-filesystem-dev, - libboost-date-time-dev, - libboost-system-dev, - libboost-program-options-dev, - liblzma-dev +Vcs-Git: git://github.com/dscharrer/innoextract.git +Vcs-Browser: https://github.com/dscharrer/innoextract Package: innoextract Architecture: any -Multi-Arch: foreign -Depends: ${misc:Depends}, - ${shlibs:Depends} -Description: Tool for extracting data from an Inno Setup installer +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: Tool to extract installers created by Inno Setup Inno Setup is a tool to create installers for Microsoft Windows applications. - Inno Extracts allows one to extract such installers under non-windows systems - without running the actual installer using wine. Inno Extract currently - supports installers created by Inno Setup 1.2.10 to 5.5.8. + innoextract allows one to extract such installers under non-windows systems + without running the actual installer using wine. diff -Nru innoextract-1.6/debian/copyright innoextract-1.7/debian/copyright --- innoextract-1.6/debian/copyright 2016-04-08 10:27:55.000000000 +0000 +++ innoextract-1.7/debian/copyright 2018-06-12 13:49:18.000000000 +0000 @@ -1,13 +1,29 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: InnoExtract -Source: https://github.com/dscharrer/InnoExtract +Upstream-Name: innoextract +Source: http://constexpr.org/innoextract/ Files: * -Copyright: Copyright 2011-2012 Daniel Scharrer +Copyright: 2011-2018 Daniel Scharrer License: Zlib + 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. Files: cmake/cpplint.py -Copyright: 2009 Google Inc. +Copyright: Copyright 2011-2018 Daniel Scharrer + Copyright 2009 Google Inc. License: BSD-3-clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -32,26 +48,3 @@ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Files: debian/* -Copyright: Copyright 2012 Lennart Weller -License: Zlib - -License: Zlib - This software is provided 'as-is', without any express or implied warranty. In - no event will the authors 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. diff -Nru innoextract-1.6/debian/docs innoextract-1.7/debian/docs --- innoextract-1.6/debian/docs 1970-01-01 00:00:00.000000000 +0000 +++ innoextract-1.7/debian/docs 2014-06-17 17:39:04.000000000 +0000 @@ -0,0 +1 @@ +README.md diff -Nru innoextract-1.6/debian/innoextract.docs innoextract-1.7/debian/innoextract.docs --- innoextract-1.6/debian/innoextract.docs 2016-04-08 10:27:55.000000000 +0000 +++ innoextract-1.7/debian/innoextract.docs 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -README.md diff -Nru innoextract-1.6/debian/rules innoextract-1.7/debian/rules --- innoextract-1.6/debian/rules 2016-04-08 10:27:55.000000000 +0000 +++ innoextract-1.7/debian/rules 2018-06-11 10:42:57.000000000 +0000 @@ -1,13 +1,12 @@ #!/usr/bin/make -f +# -*- makefile -*- +# We don't need any custom rules as debhelper is able to figure out everything. -export DEB_BUILD_MAINT_OPTIONS = hardening=+all -DPKG_EXPORT_BUILDFLAGS = 1 -include /usr/share/dpkg/buildflags.mk -CFLAGS+=$(CPPFLAGS) -CXXFLAGS+=$(CPPFLAGS) +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +override_dh_installchangelogs: + dh_installchangelogs CHANGELOG %: dh $@ - -override_dh_auto_configure: - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr diff -Nru innoextract-1.6/debian/upstream/signing-key.asc innoextract-1.7/debian/upstream/signing-key.asc --- innoextract-1.6/debian/upstream/signing-key.asc 2016-04-08 10:27:55.000000000 +0000 +++ innoextract-1.7/debian/upstream/signing-key.asc 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1 - -mQINBE9ui90BEADL05mUhBeu/n9Ej6FF6793B8zJO/tDrGAj96hGAtVRAgzQ/bkU -YnLe9GQ3uWQBzeppoCGxsLn7yTUVf2P/LvHyj1ouco4XmVOUM86QjfCX1Of7+F7R -SOBaiirF1oucV6GdexcbCRf3QG5CxalBZvRaoLTLV4DTADAmB1+Uh6oDN7Klt6vg -NLJsGX5AX61cPm7wHJ+3BrCQhytZ54P83oluNujIyaREUdKYopcbWDDAl148IHCo -sp9RF+E80AF2B4XG+vm0bHuHunmOLlOOiSTxV3CXdGX6AXUybL05jj5OygxFjmLa -ZoOGn3HyC+4axHGZUPeNdTTEJmWM8N7SwfBnqAmv2CTmIGzgUjtc+gdy5ib7+HZL -pIxJqkclNFDUP9SDwouj5PyYu2ATAZ5jvTt4lknS+uV3o4E1sWlAyPGKGEnqaiQD -RAHlnENLjycZ7byihg0CTi46ZiooZjcGDPxwuloncV5W7MJllwMEW/HrTKwDsmxN -9g26Y9nipfiy2OnLZkwufIciVqREXGp9zS851BHGxW9rA9bnV22wOuSPtvJZizpx -ofiHuga/gT5qAuN1rJGRFkhuW5yrM1lq4wkcweZUInh0xvUw2jJe7JoD0i5/SZh+ -pvdCHEI24Mo07p1s1wz0zt/26j4R1HBW2LS+x42dbv0l0rrNB+LUiD46QQARAQAB -tCZEYW5pZWwgU2NoYXJyZXIgPGRhbmllbEBjb25zdGV4cHIub3JnPokCOAQTAQIA -IgUCT26L3QIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQKFVaZtfh3snF -kA//U87xIGd4G7VZSkcxqH0wkwEFI4o3LAyryCyRf8raSw0M7YSkR5qfVtQ3bH9w -qzxg6JFSIPqGyiAunBn7l9cwFA3CmC32tW6tbTHp12BYFDimPpazqcqOvHObHENA -GwsCXn8XK9ZgUXMiz6On8LutWtcH5wk4AurYSUzWx2DAh6JXm3MgJX6CHOv0GL13 -hB+JUKktlCfOkniofZatsx/Ai+g8oTcNQmQ9dYFZt2Pj2rxE1Vf2jI4+X3AD63En -9fFpWhZWeyOuMzRqLz0R8MfllItlpnFmhMp/3UISj6KAe4fMXYlyvOzAqGSe3MzP -cVktz7dmCWasfx+p8peas/ekqKm17DFT+FSIeXlq1nhYvvfd2hyGgmWwLuuuqiwS -9V3LE09gM5W5731BSXt4epGm+smo2HsdSR89rau4O/jvgqljNzysvLItUn7gtV9t -PT0pKuieBa/V9riGlAeapYVBnMAg1pBApOeW3eq8YZoW5up05Z7rhkJ/BywhI1wZ -olQAuoLqGvPmeN4UJAGYGMlQ4tdYJ8w9L6zLrFF4HKAi/X/zr+z3jxC7r4U2JuZU -7FkSqqqoAykDpi4npJ6vawhvjOgOP6QS/y0NQOGMUHG01yaABzrXvw5fP2DVxBCe -jJSSiihL4kneqOW2Nktjl1ZzfkoExBrw973O8HYzUuaBgRq5Ag0ET26L3QEQAP9T -t5UXC97cqH20krh/0bpJfa6C13fKObHrbXJRKzjsiArDqU7UvjR4qm3kde3dKm2A -nwv673gbMRHH7APnSQ0Ww/wYkvv7/INN/0krO3gTVz7qAsuqVTEF1zVaOnipEbFs -TOQ+0xPZkuDHJ7MoyVJMBaZJzDnJ6ClSWLEqjSzfrlqJmeIlqaR3q8LJNpdhNQcB -GgnmD78DhaFB5pveBTKGy15C+vGD49F1LGMRBGFf84ore7va6dYf0yVYvDBoHj0z -qf7fIltemTShq4858VJLxQSk3t81zhmw7PODh9qqUQjnw8YQtPAUQmSzZH4DruTL -kVPH6eoagGMADI9ZUtkc5MG7aQ8ZwYuVAG1cj3pH5aq1cB+tTadWioclv+YtGv5z -8ep7+E1l3dcbURa+6vKaUg4tNeXl2IZeGfsH7h/E+1GXpJXbMCVGiiM+EW6wveJF -2UhgynlqCqarF2Rade5pQ2VVcP8btsd1CG02CQXytIpLx4E1ebcCW2FVDos2lkn9 -LdyzwkH3LWIzlUh6isyL2noyypxDu1qbja8NKmfhmrhT3gyOFJ75pQS13tXvul94 -lMaLHhXNj4nq9cuQS/YLebCaTZFnOLyf9A+D653qLHOAn1CtFwkn4bSY2GdK9+gt -K2kuiTYgCPBdh6wJyAc0skglwVZyaCcF7DUiGuZbABEBAAGJAh8EGAECAAkFAk9u -i90CGwwACgkQKFVaZtfh3sl0ixAAhrl8JFJtSYc7O5Ojo9XMPlA3se2cwjxnPFxW -LmNQJSSMUAoWJWeYmrnOVPonOYALaK5pa5w3LQdbPtPfu4PZfgYl5Hh0Krdn7/PK -Id09pl/HSduob9lce79zGiCrqtcAyrEJjGNRnRI5aUKtQgtM71+FMe22lcTLV2fk -Yq2FrugREmyAqDHSVwtSycopcpSUQ3GL7XP8KT05jJplIeXaP5Q+qpTjWvcfWs31 -K1Z0F1YxBUO5ip1hD4PvEbrA/edLl+/EylhEbaT3kFUcVUGQK18/dp854lOkju1u -yVbTXaHPBfkAVFEo7YiMaaWxDgPTmIzQ7LSCfsti4cwlHpxpQ7oOIQAjckD+F2c+ -6hrCgVGp1IzgTwkC+fCtYFOmxy4ocSRvnpMXDJnb3Bpi6eBvAWKzREuZYl/fNi6d -jaerUB7SeosMlGxXkNTTALKWBUmuwS4fMi6Vp6lluJ5JdjSbY1y365wlVzufkT0c -YmwtO5Z/p6WSlxEY9pHfEh1z/BMmWy6VQa0JKsRc9CqrLZ2brtBUDI1BbbnSx68p -BROZkwNvrklRCK6wnDNIBtYFyH8PCRZinClXEUFmEBnw/7DmLMwb+41gtOVUDaB5 -pDrNpvuGoUbTP2IYJjDm9kbP/u3YN8TKqDkD1SR6724IBHkraKJRc31XaVRLCYLq -UUKYud4= -=0/IL ------END PGP PUBLIC KEY BLOCK----- diff -Nru innoextract-1.6/debian/watch innoextract-1.7/debian/watch --- innoextract-1.6/debian/watch 2016-04-08 10:27:55.000000000 +0000 +++ innoextract-1.7/debian/watch 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -version=3 -opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/-$1\.tar\.gz/,pgpsigurlmangle=s/$/.sig/ \ - https://github.com/dscharrer/innoextract/releases .*?/(?:\d\S*)/innoextract-(\d\S*)\.tar\.gz diff -Nru innoextract-1.6/doc/Doxyfile.in innoextract-1.7/doc/Doxyfile.in --- innoextract-1.6/doc/Doxyfile.in 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/doc/Doxyfile.in 2018-06-12 18:50:34.000000000 +0000 @@ -1512,7 +1512,7 @@ # undefined via #undef or recursively expanded use the := operator # instead of the = operator. -PREDEFINED = INNOEXTRACT_HAVE_LZMA +PREDEFINED = INNOEXTRACT_HAVE_ARC4 INNOEXTRACT_HAVE_LZMA # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff -Nru innoextract-1.6/doc/innoextract.1 innoextract-1.7/doc/innoextract.1 --- innoextract-1.6/doc/innoextract.1 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/doc/innoextract.1 1970-01-01 00:00:00.000000000 +0000 @@ -1,270 +0,0 @@ -.\" Manpage for innoextract. -.\" Contact daniel@constexpr.org to correct errors or typos. -.TH innoextract 1 "2016-03-02" "1.6" -.SH NAME -innoextract - tool to extract installers created by Inno Setup -.SH SYNOPSIS -.B innoextract -.RB [ \-\-extract ] -.RB [ \-\-lowercase ] -[options] [\fB\-\-\fP] \fIinstallers\fP ... - -\fBinnoextract \-\-list\fP [options] [\fB\-\-\fP] \fIinstallers\fP ... - -\fBinnoextract \-\-test\fP [options] [\fB\-\-\fP] \fIinstallers\fP ... -.SH DESCRIPTION -\fBinnoextract\fP is a tool that can extract installer executables created by Inno Setup. -.PP -\fBinnoextract\fP will extract files from installers specified on the command line. -.PP -To extract a multi-part installer with external data files, only the executable (.exe) file needs to be given as an argument to \fBinnoextract\fP. -.SH OPTIONS SUMMARY -.PP -Here is a short summary of the options available in innoextract. Please refer to the detailed documentation below for a complete description. -.TP -.B Generic options: -.nf - \-h \-\-help Show supported options - \-v \-\-version Print version information - \-\-license Show license information -.fi -.TP -.B Actions: -.nf - \-t \-\-test Only verify checksums, don't write anything - \-e \-\-extract Extract files (default action) - \-l \-\-list Only list files, don't write anything - \-\-list\-languages List languages supported by the installer - \-\-gog\-game\-id Determine the GOG.com game ID for this installer -.fi -.TP -.B Modifiers: -.nf - \-\-collisions \fIACTION\fP How to handle duplicate files - \-\-default\-language Default language for renaming - \-\-dump Dump contents without converting filenames - \-L \-\-lowercase Convert extracted filenames to lower-case - \-T \-\-timestamps \fITZ\fP Timezone for file times or "local" or "none" - \-d \-\-output\-dir \fIDIR\fP Extract files into the given directory - \-g \-\-gog Process additional archives from GOG.com installers -.fi -.TP -.B Filters: -.nf - \-m \-\-exclude\-temp Don't extract temporary files - \-\-language \fILANG\fP Extract only files for this language - \-\-language\-only Only extract language-specific files - \-I \-\-include \fIEXPR\fP Extract only files that match this path -.fi -.TP -.B Display options: -.nf - \-q \-\-quiet Output less information - \-s \-\-silent Output only error/warning information - \-\-no\-warn\-unused Don't warn on unused \fI.bin\fP files - \-c \-\-color[=\fIENABLE\fP] Enable/disable color output - \-p \-\-progress[=\fIENABLE\fP] Enable/disable the progress bar -.fi -.SH OPTIONS -.TP -\fB--\fP -Treat all arguments after this one as files, even if they begin with a dash. -.TP -\fB\-\-collisions\fP \fIACTION\fP -Inno Setup installers can contain duplicate files with the same name. This option tells innoextract what to do when such a collisions is encountered. Valid actions are: - -.RS -.TP -"\fBoverwrite\fP" -Extract only one of the colliding files. The choice is done similar to how Inno Setup overwrites files during installation. This is the default. -.TP -"\fBrename\fP" -Rename files that would be overwritten using the "\fBoverwrite\fP" action by appending a suffix comprised of the file's language, the component it belongs to and/or a number to make the filename unique. The language suffix (if applicable) is also appended to the \fIdefault\fP file that would have been extracted with the "\fBoverwrite\fP" action. -.TP -"\fBrename-all\fP" -Rename all colliding files by appending a suffix comprised of the file's language, the component it belongs to and/or a number to make the filename unique. The complete suffix is appended to both files that would have been overwritten using the "\fBoverwrite\fP" action and to those that would have overwritten other files. -.TP -"\fBerror\fP" -Exit when a collision is detected. -.RE -.IP -.B Rename rules: - -1. If the \fBcomponent\fP is not the same for all files in the collision set (all files with the same filename), "\fB#\fP" (without quotes) followed by the component id is appended to all files that are specific to a single component. - -2. If the \fBlanguage\fP is not the same for all files in the collision set, "\fB@\fP" (without quotes) followed by the language id is appended to all files that are specific to a single component unless that language matches the default language specified by the \fB--default-language\fP. While the suffix is omitted for the default language, no numbered suffix is added in it's place unless needed to make the filename unique. - -3. If no suffix was added by the previous steps, or if the filename is not yet unique, "\fB$\fP" (without quotes) followed by the lowest integer (starting at 0) to make the filename unique is appended. - -With the "\fBrename\fP" action, steps 1 and 3 are only applied to files that would have been overwritten by the "\fBoverwrite\fP" action while "\fBrename-all\fP" applies them to all files in the collision set. -.TP -\fB\-\-default\-language\fP \fILANG\fP -Set a language as the default. - -With \fB\-\-collisions\=overwrite\fP (the default) this will change the choice of which file to keep to always prefer the given language. In effect, \fB\-\-default\-language\fP behaves almost like \fB\-\-language\fP, except that files are extracted for all languages if they have different names. - -When using the \fB\-\-collisions\=rename\fP option, \fB\-\-default\-language\fP chooses a language for which the files should keep the original name if possible. -.TP -\fB\-c\fP, \fB\-\-color\fP[=\fIENABLE\fP] -By default -.B innoextract -will try to detect if the terminal supports shell escape codes and enable or disable color output accordingly. Specifically, colors will be enabled if both \fBstdout\fP and \fBstderr\fP point to a TTY and the \fBTERM\fP environment variable is not set to "\fBdumb\fP". Pass \fB1\fP or \fBtrue\fP to \fB\-\-color\fP to force color output. Pass \fB0\fP or \fBfalse\fP to never output color codes. -.TP -\fB\-\-dump\fP -Don't convert Windows paths to UNIX paths and don't substitute constants in paths. - -When combining \fB\-\-dump\fP with \fB\-\-extract\fP innoextract will \fInot\fP ensure that the paths don't point outside the destination directory. Use this option with caution when handling untrusted files. -.TP -\fB\-m\fP, \fB\-\-exclude\-temp\fP -Don't extract files that would have been deleted at the end of the install process. Such files are marked with [temp] in the file listing. - -This option takes precedence over \fB\-\-include\fP and \fB\-\-language\fP: temporary files are never extracted when using the \fB\-\-exclude\-temp\fP, even if they match the selected language or include expressions. -.TP -\fB\-e\fP, \fB\-\-extract\fP -Extract all files to the current directory. This action is enabled by default, unless either \fB\-\-list\fP or \fB\-\-extract\fP is specified. You may only specify one of \fB\-\-extract\fP and \fB\-\-test\fP. -.TP -\fB\-g\fP, \fB\-\-gog\fP -Try to process additional .bin files that have the same basename as the setup but are not actually part of the Inno Setup installer. This is the case for newer multi-part GOG.com installers where these .bin files are RAR archives, potential encrypted with the MD5 checksum of the game ID (see the \fB\-\-gog\-game\-id\fP option). - -Extracting these RAR archives requires rar, unrar or lsar/unar command-line utilities to be in the PATH. - -The \fB\-\-list\fP, \fB\-\-test\fP, \fB\-\-extract\fP and \fB\-\-output\-dir\fP options are passed along to unrar/unar, but other options may be ignored for the RAR files. For multi-part RAR archives, the \fB\-\-test\fP requires a writable output directory for temporary files. - -Note that is option is geared towards GOG.com installers. Other installers may come be bundled with different extraneous \fI.bin\fP which this option might not be able to handle. -.TP -\fB\-\-gog\-game\-id\fP -Determine the ID used by GOG.com for the game contained in this installer. This will only work with Galaxy-ready GOG.com installers. - -This option can be combined with \fB\-\-silent\fP to print only the game ID without additional syntax that would make consumption by other scripts harder. - -The \fB\-\-gog\-game\-id\fP action can be combined with \fB\-\-list\fP, \fB\-\-test\fP, \fB\-\-extract\fP and/or \fB\-\-list\-languages\fP. If \fB\-\-silent\fP and \fB\-\-gog\-game\-id\fP are combined with \fB\-\-list\fP and/or \fB\-\-list\-languages\fP, the game ID (or an empty line) will be printed on it's own line before the file list but after the language list. - -For newer multi-part GOG.com installers the \fI.bin\fP files are not part of the Inno Setup installer but instead are RAR archives. Some of these RAR files are encrypted, with the password being the MD5 checksum of the game ID: - - \fBinnoextract \-\-gog\-game\-id --silent\fP \fIsetup_....exe\fP | \fBmd5sum\fP | \fBcut \-d\fP ' ' \fB\-f\fP 1 -.TP -\fB\-h\fP, \fB\-\-help\fP -Show a list of the supported options. -.TP -\fB\-I\fP, \fB\-\-include\fP \fIEXPR\fP -If this option is specified, innoextract will only process files whose path matches \fIEXPR\fP. The expression can be either a single path component (a file or directory name) or a series of successive path components joined by the OS path separator (\\ on Windows, / elsewhere). - -The expression is always matched against one or more full path components. Filtering by parts of filenames is currently not supported. Matching is done case-insensitively. - -\fIEXPR\fP may contain one leading path separator, in which case the rest of the expression is matched against the start of the path. Otherwise, the expression is matched against any part of the path. - -The \fB\-\-include\fP option may be repeated in order allow files matching against one of multiple patterns. If \fB\-\-include\fP is not used, all files are processed. -.TP -\fB\-\-language\fP \fILANG\fP -Extract only language-independent files and files for the given language. By default all files are extracted. - -To also skip language-independent files, combine this option with \fB\-\-language\-only\fP. -.TP -\fB\-\-language\-only\fP -Only extract files that are language-specific. - -This option can be combined with \fB\-\-language\fP to only extract the files of a specific language. -.TP -\fB\-\-license\fP -Show license information. -.TP -\fB\-l\fP, \fB\-\-list\fP -List files contained in the installer but don't extract anything. - -This option can be combined with \fB\-\-silent\fP to print only the names of the contained files (one per line) without additional syntax that would make consumption by other scripts harder. - -The \fB\-\-list\fP action can be combined with \fB\-\-test\fP, \fB\-\-extract\fP, \fB\-\-list\-languages\fP and/or \fB\-\-gog\-game\-id\fP to display the names of the files as they are extracted even with \fB\-\-silent\fP. -.TP -\fB\-\-list\-languages\fP -List languages supported by the installer. - -This option can be combined with \fB\-\-silent\fP to print only the identifiers of the languages (one per line) followed by a space and then the language name, without additional syntax that would make consumption by other scripts harder. - -The \fB\-\-list\-languages\fP action can be combined with \fB\-\-list\fP, \fB\-\-test\fP, \fB\-\-extract\fP and/or \fB\-\-gog\-game\-id\fP to display the available languages before doing anything else. If \fB\-\-silent\fP and \fB\-\-list\-languages\fP are combined with \fB\-\-list\fP and/or \fB\-\-gog\-game\-id\fP, the languages list will be terminated with an empty line and will precede both the game ID and files list. -.TP -\fB\-L\fP, \fB\-\-lowercase\fP -Convert filenames stored in the installer to lower-case before extracting. -.TP -\fB\-d\fP, \fB\-\-output\-dir\fP \fIDIR\fP -Extract all files into the given directory. By default, \fBinnoextract\fP will extract all files to the current directory. - -If the specified directory does not exist, it will be created. However, the parent directory must exist or extracting will fail. -.TP -\fB\-p\fP, \fB\-\-progress\fP[=\fIENABLE\fP] -By default \fBinnoextract\fP will try to detect if the terminal supports shell escape codes and enable or disable progress bar output accordingly. Pass \fB1\fP or \fBtrue\fP to \fB\-\-progress\fP to force progress bar output. Pass \fB0\fP or \fBfalse\fP to never show a progress bar. -.TP -\fB\-q\fP, \fB\-\-quiet\fP -Less verbose output. -.TP -\fB\-s\fP, \fB\-\-silent\fP -Don't output anything except errors and warnings unless explicitly requested. - -This option can be combined with \fB\-\-list\fP to print only the names of the contained files (one per line) without additional syntax that would make consumption by other scripts harder. -.TP -\fB\-t\fP, \fB\-\-test\fP -Test archive integrity but don't write any output files. You may only specify one of \fB\-\-extract\fP and \fB\-\-test\fP. -.TP -\fB\-T\fP, \fB\-\-timestamps\fP \fITZ\fP -Inno Setup installers can contain timestamps in both UTC and 'local' timezones. - -The \fB\-\-timestamps\fP option specifies what timezone should be used to adjust these 'local' file times. - -Valid values are those accepted by \fBtzset\fP in the \fBTZ\fP environment variable, except with the direction of the time offset reversed: both \fB\-T CET\fP and \fB\-T GMT+1\fP will (when DST is in effect) give the same result. - -Besides timezones, two special values are accepted: - -.RS -.HP -"\fBnone\fP" -Don't preserve file times for extracted files, both for UTC and 'local' timestamps. The file times wil be left the way the OS set them when creating the output files. -.HP -"\fBlocal\fP" -Use the system timezone for 'local' timestamps. This is the normal Inno Setup behavior, and can be used together with the \fBTZ\fP environment variable. -.RE -.IP - -The default value for this option is \fBUTC\fP, causing innoextract to not adjust 'local' file times. File times marked as UTC in the Inno Setup file will never be adjusted no matter what \fB\-\-timestamps\fP is set to. -.TP -\fB\-v\fP, \fB\-\-version\fP -Print the \fBinnoextract\fP version number and supported Inno Setup versions. - -If combined with the \fB\-\-silent\fP option, only the version \fInumber\fP is printed. Otherwise, the output will contain the name (innoextract) followed by the version number on the first line, and, unless the \fB\-\-quiet\fP options is specified, the range of suuported Inno Setup installer versions on the second line. -.TP -\fB\-\-no\-warn\-unused\fP -By default, innoextract will print a warning if it encounters \fI.bin\fP files that look like they could be part of the setup but are not used. This option disables that warning. -.SH PATH CONSTANTS -Paths in Inno Setup installers can contain constants (variable or code references) that are expanded at install time. innoextract expands all such constants to their name and replaces unsafe characters with \fB$\fP. For exmaple \fB{app}\fP is expanded to \fBapp\fP while \fB{code:Example}\fP is expanded to \fBcode$Example\fP. - -There is currently no way to configure this expansion except for disabling it with the \fB\-\-dump\fP option. -.SH EXIT VALUES -.PP -.IP \fB0\fP -Success -.IP \fB1\fP -Syntax or usage error -.IP \fB2+\fP -Broken or unsupported setup file, or input/output error -.SH LIMITATIONS -There is no support for extracting individual components and limited support for filtering by name. - -Included scripts and checks are not executed. - -The mapping from Inno Setup constants like the application directory to subdirectories is hard-coded. - -Names for data slice/disk files in multi-file installers must follow the standard naming scheme. - -Encrypted installers are not supported. -.SH SEE ALSO -\fBcabextract\fP(1), \fBunar\fP(1), \fBunrar\fP(1), \fBunshield\fP(1), \fBtzset\fP(3) -.SH BUGS -.PP -Please report bugs to http://innoextract.constexpr.org/issues. -.SH CREDITS -.PP -\fBinnoextract\fP is distributed under the zlib/libpng license. See the LICENSE file for details. -.PP -A website is available at http://constexpr.org/innoextract/. -.PP -This program uses the excellent lzma/xz decompression library written by Lasse Collin. -.SH AUTHOR -Daniel Scharrer (daniel@constexpr.org) diff -Nru innoextract-1.6/doc/innoextract.1.in innoextract-1.7/doc/innoextract.1.in --- innoextract-1.6/doc/innoextract.1.in 1970-01-01 00:00:00.000000000 +0000 +++ innoextract-1.7/doc/innoextract.1.in 2018-06-12 18:50:34.000000000 +0000 @@ -0,0 +1,362 @@ +.\" Manpage for innoextract. +.\" Contact daniel@constexpr.org to correct errors or typos. +.TH innoextract 1 "@CHANGELOG_0_NUMBER@" "@VERSION_0_NUMBER@@GIT_SUFFIX_7@" +.SH NAME +innoextract - tool to extract installers created by Inno Setup +.SH SYNOPSIS +.B innoextract +.RB [ \-\-extract ] +.RB [ \-\-lowercase ] +[options] [\fB\-\-\fP] \fIinstallers\fP ... + +\fBinnoextract \-\-list\fP [options] [\fB\-\-\fP] \fIinstallers\fP ... + +\fBinnoextract \-\-test\fP [options] [\fB\-\-\fP] \fIinstallers\fP ... +.SH DESCRIPTION +\fBinnoextract\fP is a tool that can extract installer executables created by Inno Setup. +.PP +\fBinnoextract\fP will extract files from installers specified on the command line. +.PP +To extract a multi-part installer with external data files, only the executable (.exe) file needs to be given as an argument to \fBinnoextract\fP. +.SH OPTIONS SUMMARY +.PP +Here is a short summary of the options available in innoextract. Please refer to the detailed documentation below for a complete description. +.TP +.B Generic options: +.nf + \-h \-\-help Show supported options + \-v \-\-version Print version information + \-\-license Show license information +.fi +.TP +.B Actions: +.nf + \-t \-\-test Only verify checksums, don't write anything + \-e \-\-extract Extract files (default action) + \-l \-\-list Only list files, don't write anything + \-\-list\-sizes List file sizes + \-\-list\-checksums List file checksums + \-i \-\-info Print information about the installer + \-\-list\-languages List languages supported by the installer + \-\-gog\-game\-id Determine the GOG.com game ID for this installer + \-\-show\-password Show password check information + \-\-check\-password Abort if the password is incorrect + \-V \-\-data\-version Only print the data version +.fi +.TP +.B Modifiers: +.nf + \-\-collisions \fIACTION\fP How to handle duplicate files + \-\-default\-language Default language for renaming + \-\-dump Dump contents without converting filenames + \-L \-\-lowercase Convert extracted filenames to lower-case + \-T \-\-timestamps \fITZ\fP Timezone for file times or "local" or "none" + \-d \-\-output\-dir \fIDIR\fP Extract files into the given directory + \-P \-\-password \fIPASSWORD\fP Password for encrypted files + \-\-password\-file \fIFILE\fP File to load password from + \-g \-\-gog Process additional archives from GOG.com installers + \-\-no\-gog\-galaxy Don't re-assemble GOG Galaxy file parts + \-n \-\-no\-extract\-unknown Don't extract unknown Inno Setup versions +.fi +.TP +.B Filters: +.nf + \-m \-\-exclude\-temp Don't extract temporary files + \-\-language \fILANG\fP Extract only files for this language + \-\-language\-only Only extract language-specific files + \-I \-\-include \fIEXPR\fP Extract only files that match this path +.fi +.TP +.B Display options: +.nf + \-q \-\-quiet Output less information + \-s \-\-silent Output only error/warning information + \-\-no\-warn\-unused Don't warn on unused \fI.bin\fP files + \-c \-\-color[=\fIENABLE\fP] Enable/disable color output + \-p \-\-progress[=\fIENABLE\fP] Enable/disable the progress bar +.fi +.SH OPTIONS +.TP +\fB--\fP +Treat all arguments after this one as files, even if they begin with a dash. +.TP +\fB\-\-check\-password\fB +Abort processing if the password provided using the \fB\-\-password\fP or \fB\-\-password\-file\fP option does not match the checksum stored in the installer. + +The password checksum used for this check can be retrieved using the \fB\-\-show\-password\fP option. +.TP +\fB\-\-collisions\fP \fIACTION\fP +Inno Setup installers can contain duplicate files with the same name. This option tells innoextract what to do when such a collisions is encountered. Valid actions are: + +.RS +.TP +"\fBoverwrite\fP" +Extract only one of the colliding files. The choice is done similar to how Inno Setup overwrites files during installation. This is the default. +.TP +"\fBrename\fP" +Rename files that would be overwritten using the "\fBoverwrite\fP" action by appending a suffix comprised of the file's language, the component it belongs to and/or a number to make the filename unique. The language suffix (if applicable) is also appended to the \fIdefault\fP file that would have been extracted with the "\fBoverwrite\fP" action. +.TP +"\fBrename-all\fP" +Rename all colliding files by appending a suffix comprised of the file's language, the component it belongs to and/or a number to make the filename unique. The complete suffix is appended to both files that would have been overwritten using the "\fBoverwrite\fP" action and to those that would have overwritten other files. +.TP +"\fBerror\fP" +Exit when a collision is detected. +.RE +.IP +.B Rename rules: + +1. If the \fBcomponent\fP is not the same for all files in the collision set (all files with the same filename), "\fB#\fP" (without quotes) followed by the component id is appended to all files that are specific to a single component. + +2. If the \fBlanguage\fP is not the same for all files in the collision set, "\fB@\fP" (without quotes) followed by the language id is appended to all files that are specific to a single component unless that language matches the default language specified by the \fB--default-language\fP. While the suffix is omitted for the default language, no numbered suffix is added in it's place unless needed to make the filename unique. + +3. If no suffix was added by the previous steps, or if the filename is not yet unique, "\fB$\fP" (without quotes) followed by the lowest integer (starting at 0) to make the filename unique is appended. + +With the "\fBrename\fP" action, steps 1 and 3 are only applied to files that would have been overwritten by the "\fBoverwrite\fP" action while "\fBrename-all\fP" applies them to all files in the collision set. +.TP +\fB\-c\fP, \fB\-\-color\fP[=\fIENABLE\fP] +By default +.B innoextract +will try to detect if the terminal supports shell escape codes and enable or disable color output accordingly. Specifically, colors will be enabled if both \fBstdout\fP and \fBstderr\fP point to a TTY and the \fBTERM\fP environment variable is not set to "\fBdumb\fP". Pass \fB1\fP or \fBtrue\fP to \fB\-\-color\fP to force color output. Pass \fB0\fP or \fBfalse\fP to never output color codes. +.TP +\fB\-V\FP, \fB\-\-data\-version\fP +Print the Inno Setup data version of the installer and exit immediately. + +The version printed using this option is the one stored in the setup file and can differ from the version printed with other actions as the stored data version is not always correct. + +This option can be used to determine if a file is an Inno Setup installer without loading any compressed headers. + +This option cannot be combined with any other action. +.TP +\fB\-\-default\-language\fP \fILANG\fP +Set a language as the default. + +With \fB\-\-collisions\=overwrite\fP (the default) this will change the choice of which file to keep to always prefer the given language. In effect, \fB\-\-default\-language\fP behaves almost like \fB\-\-language\fP, except that files are extracted for all languages if they have different names. + +When using the \fB\-\-collisions\=rename\fP option, \fB\-\-default\-language\fP chooses a language for which the files should keep the original name if possible. +.TP +\fB\-\-dump\fP +Don't convert Windows paths to UNIX paths and don't substitute constants in paths. + +When combining \fB\-\-dump\fP with \fB\-\-extract\fP innoextract will \fInot\fP ensure that the paths don't point outside the destination directory. Use this option with caution when handling untrusted files. +.TP +\fB\-m\fP, \fB\-\-exclude\-temp\fP +Don't extract files that would have been deleted at the end of the install process. Such files are marked with [temp] in the file listing. + +This option takes precedence over \fB\-\-include\fP and \fB\-\-language\fP: temporary files are never extracted when using the \fB\-\-exclude\-temp\fP, even if they match the selected language or include expressions. +.TP +\fB\-e\fP, \fB\-\-extract\fP +Extract all files to the current directory. This action is enabled by default, unless one or more of the \fB\-\-list\fP, \fB\-\-list\-sizes\fP, \fB\-\-list\-checksums\fP, \fB\-\-test\fP, \fB\-\-list\-languages\fP, \fB\-\-gog\-game\-id\fP, \fB\-\-show\-password\fP or \fB\-\-check\-password\fP actions are specified. + +By default innoextract will continue after encountering file checksum errors. The \fB\-\-extract\fP option can be combined with \fB\-\-test\fP to abort on checksum errors. +.TP +\fB\-n\fP, \fB\-\-no\-extract\-unknown\fP +By default innoextract will try to extract installers with an unknown Inno Setup data version by treating it as the closest known version. This option tells innoextract to abort instead. +.TP +\fB\-g\fP, \fB\-\-gog\fP +Try to process additional .bin files that have the same basename as the setup but are not actually part of the Inno Setup installer. This is the case for newer multi-part GOG.com installers where these .bin files are RAR archives, potential encrypted with the MD5 checksum of the game ID (see the \fB\-\-gog\-game\-id\fP option). + +Extracting these RAR archives requires rar, unrar or lsar/unar command-line utilities to be in the PATH. + +The \fB\-\-list\fP, \fB\-\-test\fP, \fB\-\-extract\fP and \fB\-\-output\-dir\fP options are passed along to unrar/unar, but other options may be ignored for the RAR files. For multi-part RAR archives, the \fB\-\-test\fP requires a writable output directory for temporary files. + +Note that is option is geared towards GOG.com installers. Other installers may come be bundled with different extraneous \fI.bin\fP which this option might not be able to handle. + +This option also forces re-assembly of GOG Galaxy file parts. See the \fB\-\-no\-gog\-galaxy\fP option for details. +.TP +\fB\-\-no\-gog\-galaxy\fP +Some GOG.com installers contain files in GOG Galaxy format (split into multiple parts that are individually compressed) which are re-assembled using post-install scripts. By default \fBinnoextract\fP will try to re\-assemble such files if it detects a GOG.com installer. This option disables that. + +GOG.com installers are detected using the publisher and URL fields in the setup headers. Use the \fB\-\-gog\fP option to force reassembly for all installers. +.TP +\fB\-\-gog\-game\-id\fP +Determine the ID used by GOG.com for the game contained in this installer. This will only work with Galaxy-ready GOG.com installers. + +This option can be combined with \fB\-\-silent\fP to print only the game ID without additional syntax that would make consumption by other scripts harder. + +The \fB\-\-gog\-game\-id\fP action can be combined with \fB\-\-list\fP, \fB\-\-test\fP, \fB\-\-extract\fP and/or \fB\-\-list\-languages\fP. If \fB\-\-silent\fP and \fB\-\-gog\-game\-id\fP are combined with \fB\-\-list\fP and/or \fB\-\-list\-languages\fP, the game ID (or an empty line) will be printed on it's own line before the file list but after the language list. + +For newer multi-part GOG.com installers the \fI.bin\fP files are not part of the Inno Setup installer but instead are RAR archives. Some of these RAR files are encrypted, with the password being the MD5 checksum of the game ID: + + \fBinnoextract \-\-gog\-game\-id --silent\fP \fIsetup_....exe\fP | \fBmd5sum\fP | \fBcut \-d\fP ' ' \fB\-f\fP 1 +.TP +\fB\-h\fP, \fB\-\-help\fP +Show a list of the supported options. +.TP +\fB\-I\fP, \fB\-\-include\fP \fIEXPR\fP +If this option is specified, innoextract will only process files whose path matches \fIEXPR\fP. The expression can be either a single path component (a file or directory name) or a series of successive path components joined by the OS path separator (\\ on Windows, / elsewhere). + +The expression is always matched against one or more full path components. Filtering by parts of filenames is currently not supported. Matching is done case-insensitively. + +\fIEXPR\fP may contain one leading path separator, in which case the rest of the expression is matched against the start of the path. Otherwise, the expression is matched against any part of the path. + +The \fB\-\-include\fP option may be repeated in order allow files matching against one of multiple patterns. If \fB\-\-include\fP is not used, all files are processed. +.TP +\fB\-i\fP \fB\-\-info\fP +This is a convenience option to enable all actions that print information about the installer. + +Scrips should not rely on the output format with this option and should instead enable the individual actions instead. + +Currently this option enables \fB\-\-list\-languages\fP, \fB\-\-gog\-game\-id\fP and \fB\-\-show\-password\fP. +.TP +\fB\-\-language\fP \fILANG\fP +Extract only language-independent files and files for the given language. By default all files are extracted. + +To also skip language-independent files, combine this option with \fB\-\-language\-only\fP. +.TP +\fB\-\-language\-only\fP +Only extract files that are language-specific. + +This option can be combined with \fB\-\-language\fP to only extract the files of a specific language. +.TP +\fB\-\-license\fP +Show license information. +.TP +\fB\-l\fP, \fB\-\-list\fP +List files contained in the installer but don't extract anything. + +This action also enables the \fB\-\-list\-sizes\fP action unless either \fB\-\-quiet\fP or \fB\-\-silent\fP is specified. + +This option can be combined with \fB\-\-silent\fP to print only the names of the contained files (one per line) without additional syntax that would make consumption by other scripts harder. + +The \fB\-\-list\fP action can be combined with \fB\-\-test\fP, \fB\-\-extract\fP, \fB\-\-list\-languages\fP and/or \fB\-\-gog\-game\-id\fP to display the names of the files as they are extracted even with \fB\-\-silent\fP. +.TP +\fB\-\-list\-checksums\fP +List checksums for files contained in the installer. + +This option implies the \fB\-\-list\fP action and can be combined with the \fB\-\-list\-sizes\fP option to print both the size and checksum for each file. + +With \fB\-\-silent\fP the file checksum will be printed at the start of the line (but after the file size if enabled with the \fB\-\-list\-sizes\fP option) followed by a space. Otherwise the checksum is printed after the file name. + +The checksum type can be one of \fBAdler32\fP, \fBCRC32\fP, \fBMD5\fP or \fBSHA-1\fP and is printed in fron of the checksum hash followed by a space. \fBAdler32\fP and \fBCRC32\fP checksums are printed as "\fB0x\fP" followed by the 32-bit hexadecimal value. + +Different files in the same installer can have different checksum types if GOG Galaxy file part reassembly is not disabled using the \fB\-\-no\-gog\-galaxy\fP option. +.TP +\fB\-\-list\-languages\fP +List languages supported by the installer. + +This option can be combined with \fB\-\-silent\fP to print only the identifiers of the languages (one per line) followed by a space and then the language name, without additional syntax that would make consumption by other scripts harder. + +The \fB\-\-list\-languages\fP action can be combined with \fB\-\-list\fP, \fB\-\-test\fP, \fB\-\-extract\fP and/or \fB\-\-gog\-game\-id\fP to display the available languages before doing anything else. If \fB\-\-silent\fP and \fB\-\-list\-languages\fP are combined with \fB\-\-list\fP and/or \fB\-\-gog\-game\-id\fP, the languages list will be terminated with an empty line and will precede both the game ID and files list. +.TP +\fB\-\-list\-sizes\fP +List uncompressed sizes for files contained in the installer. + +This option implies the \fB\-\-list\fP action and can be combined with the \fB\-\-list\-checksums\fP option to print both the size and checksum for each file. + +With \fB\-\-silent\fP the file size in bytes will be printed at the start of the line followed by a space. Otherwise the size is printed after the file name in a human-friendly format. +.TP +\fB\-L\fP, \fB\-\-lowercase\fP +Convert filenames stored in the installer to lower-case before extracting. +.TP +\fB\-d\fP, \fB\-\-output\-dir\fP \fIDIR\fP +Extract all files into the given directory. By default, \fBinnoextract\fP will extract all files to the current directory. + +If the specified directory does not exist, it will be created. However, the parent directory must exist or extracting will fail. +.TP +\fB\-P\fP, \fB\-\-password \fIPASSWORD\fP +Specifies the password to decrypt encrypted files. The password is assumed to be encoded as UTF-8 and converted the internal encoding according used in the installer as needed. + +Use the \fB\-\-password-file\fP option to load the password from a file or standard input instead. This option cannot be combined with \fB\-\-password-file\fP. + +If this password does not match the checksum stored in the installer, encrypted files will be skipped but unencrypted files will still be extracted. Use the \fB\-\-check\-password\fP option to abort processing entirely if the password is incorrect. +.TP +\fB\-\-password-file\fP \fIFILE\fP +Load a password form the specified file. Only the first line excluding the terminating carriage return and/or line break is used as the password. The password is assumed to be encoded as UTF-8 and converted the internal encoding according used in the installer as needed. + +If the special file name "\fB-\fP" is used, the password will be read from standard input. + +Use the \fB\-\-password\fP option to specify the password on the command\-line instead. This option cannot be combined with \fB\-\-password\fP. + +If this password does not match the checksum stored in the installer, encrypted files will be skipped but unencrypted files will still be extracted. Use the \fB\-\-check\-password\fP option to abort processing entirely if the password is incorrect. +.TP +\fB\-p\fP, \fB\-\-progress\fP[=\fIENABLE\fP] +By default \fBinnoextract\fP will try to detect if the terminal supports shell escape codes and enable or disable progress bar output accordingly. Pass \fB1\fP or \fBtrue\fP to \fB\-\-progress\fP to force progress bar output. Pass \fB0\fP or \fBfalse\fP to never show a progress bar. +.TP +\fB\-q\fP, \fB\-\-quiet\fP +Less verbose output. +.TP +\fB\-\-show\-password\fP +Show checksum \fB$c\fP and salt \fB$s\fP used for the password \fB$p\fP check as well as encoding of the password. The checksum is calculated from the salt concatenated with the password: + + \fB$c = hash($s . $p)\fP + +With the \fB\-\-silent\fP option, the checksum name and hash is printed on one line seperated by a space followed by the salt encoded as hex bytes and password encoding on separate lines. + +Checksum types can be \fBCRC32\fP, \fBMD5\fP or \fBSHA-1\fP although \fBCRC32\fP is not used in installers with encryption. + +The password encoding is either \fBMS-ANSI\fP (Windows-1252) or \fBUTF16-LE\fP. + +Use the \fB\-\-password\fP or \fB\-\-password\-file\fP option together with \fB\-\-check\-password\fP to check if a password matches this checksum. +.TP +\fB\-s\fP, \fB\-\-silent\fP +Don't output anything except errors and warnings unless explicitly requested and use a machine-readable output format. + +This option can be combined with \fB\-\-list\fP to print only the names of the contained files (one per line) without additional syntax that would make consumption by other scripts harder. +.TP +\fB\-t\fP, \fB\-\-test\fP +Test archive integrity but don't write any output files. + +This option can be combined with \fB\-\-extract\fP to abort on file checksum errors. +.TP +\fB\-T\fP, \fB\-\-timestamps\fP \fITZ\fP +Inno Setup installers can contain timestamps in both UTC and 'local' timezones. + +The \fB\-\-timestamps\fP option specifies what timezone should be used to adjust these 'local' file times. + +Valid values are those accepted by \fBtzset\fP in the \fBTZ\fP environment variable, except with the direction of the time offset reversed: both \fB\-T CET\fP and \fB\-T GMT+1\fP will (when DST is in effect) give the same result. + +Besides timezones, two special values are accepted: + +.RS +.HP +"\fBnone\fP" +Don't preserve file times for extracted files, both for UTC and 'local' timestamps. The file times wil be left the way the OS set them when creating the output files. +.HP +"\fBlocal\fP" +Use the system timezone for 'local' timestamps. This is the normal Inno Setup behavior, and can be used together with the \fBTZ\fP environment variable. +.RE +.IP + +The default value for this option is \fBUTC\fP, causing innoextract to not adjust 'local' file times. File times marked as UTC in the Inno Setup file will never be adjusted no matter what \fB\-\-timestamps\fP is set to. +.TP +\fB\-v\fP, \fB\-\-version\fP +Print the \fBinnoextract\fP version number and supported Inno Setup versions. + +If combined with the \fB\-\-silent\fP option, only the version \fInumber\fP is printed. Otherwise, the output will contain the name (innoextract) followed by the version number on the first line, and, unless the \fB\-\-quiet\fP options is specified, the range of suuported Inno Setup installer versions on the second line. +.TP +\fB\-\-no\-warn\-unused\fP +By default, innoextract will print a warning if it encounters \fI.bin\fP files that look like they could be part of the setup but are not used. This option disables that warning. +.SH PATH CONSTANTS +Paths in Inno Setup installers can contain constants (variable or code references) that are expanded at install time. innoextract expands all such constants to their name and replaces unsafe characters with \fB$\fP. For exmaple \fB{app}\fP is expanded to \fBapp\fP while \fB{code:Example}\fP is expanded to \fBcode$Example\fP. + +There is currently no way to configure this expansion except for disabling it with the \fB\-\-dump\fP option. +.SH EXIT VALUES +.PP +.IP \fB0\fP +Success +.IP \fB1\fP +Syntax or usage error +.IP \fB2+\fP +Broken or unsupported setup file, or input/output error +.SH LIMITATIONS +There is no support for extracting individual components and limited support for filtering by name. + +Included scripts and checks are not executed. + +The mapping from Inno Setup constants like the application directory to subdirectories is hard-coded. + +Names for data slice/disk files in multi-file installers must follow the standard naming scheme. +.SH SEE ALSO +\fBcabextract\fP(1), \fBunar\fP(1), \fBunrar\fP(1), \fBunshield\fP(1), \fBtzset\fP(3) +.SH BUGS +.PP +Please report bugs to http://innoextract.constexpr.org/issues. +.SH CREDITS +.PP +\fBinnoextract\fP is distributed under the zlib/libpng license. See the LICENSE file for details. +.PP +A website is available at http://constexpr.org/innoextract/. +.PP +This program uses the excellent lzma/xz decompression library written by Lasse Collin. +.SH AUTHOR +Daniel Scharrer (daniel@constexpr.org) diff -Nru innoextract-1.6/LICENSE innoextract-1.7/LICENSE --- innoextract-1.6/LICENSE 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/LICENSE 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ -Copyright (C) 2011-2016 Daniel Scharrer +Copyright (C) 2011-2018 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 diff -Nru innoextract-1.6/README.md innoextract-1.7/README.md --- innoextract-1.6/README.md 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/README.md 2018-06-12 18:50:34.000000000 +0000 @@ -1,7 +1,9 @@ # innoextract - A tool to unpack installers created by Inno Setup -[Inno Setup](http://www.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 5.5.5. +[Inno Setup](http://www.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 5.6.0. + +In addition to standard Inno Setup installers, innoextract also supports some modified Inno Setup variants including Martijn Laan's My Inno Setup Extensions 3.0.6.1 as well as GOG.com's Inno Setup-based game installers. innoextract is available under the ZLIB license - see the LICENSE file. @@ -43,15 +45,15 @@ | Option | Default | Description | |:------------------------ |:---------:|:----------- | +| `USE_ARC4` | `ON` | Build ARC4 decryption support. | `USE_LZMA` | `ON` | Use `liblzma`. | `WITH_CONV` | *not set* | The charset conversion library to use. Valid values are `iconv`, `win32` and `builtin`^1. If not set, a library appropriate for the target platform will be chosen. -| `ENABLE_BUILTIN_CONV` | `ON` | Build internal Windows-1252 and UTF-16LE to UTF-18 charset conversion routines. These might be used even if `WITH_CONV` is not set to `builtin`. | `CMAKE_BUILD_TYPE` | `Release` | Set to `Debug` to enable debug output. | `DEBUG` | `OFF`^2 | Enable debug output and runtime checks. | `DEBUG_EXTRA` | `OFF` | Expensive debug options. | `SET_WARNING_FLAGS` | `ON` | Adjust compiler warning flags. This should not affect the produced binaries but is useful to catch potential problems. | `SET_OPTIMIZATION_FLAGS` | `ON` | Adjust compiler optimization flags. For non-debug builds the only thing this does is instruct the linker to only link against libraries that are actually needed. -| `USE_CXX11` | `ON` | Try to compile in C++11 mode if available. +| `CXX_STD_VERSION` | `2017` | Maximum C++ standard version to enable. | `USE_DYNAMIC_UTIMENSAT` | `OFF` | Dynamically load utimensat(2) if not available at compile time | `USE_STATIC_LIBS` | `OFF`^3 | Turns on static linking for all libraries, including `-static-libgcc` and `-static-libstdc++`. You can also use the individual options below: | `LZMA_USE_STATIC_LIBS` | `OFF`^4 | Statically link `liblzma`. @@ -100,8 +102,6 @@ * Names for data slice/disk files in multi-file installers must follow the standard naming scheme. -* Encrypted installers are not supported. - A perhaps more complete, but Windows-only, tool to extract Inno Setup files is [innounp](http://innounp.sourceforge.net/). Extracting Windows installer executables created by programs other than Inno Setup is out of the scope of this project. Some of these can be unpacked by the following programs: diff -Nru innoextract-1.6/src/cli/debug.cpp innoextract-1.7/src/cli/debug.cpp --- innoextract-1.6/src/cli/debug.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/cli/debug.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -471,18 +471,9 @@ if(header.options & (setup::header::Password | setup::header::EncryptionUsed)) { std::cout << "Password: " << color::cyan << header.password << color::reset << '\n'; - setup::salt empty_salt; - std::memset(empty_salt, 0, sizeof(empty_salt)); - BOOST_STATIC_ASSERT(sizeof(empty_salt) == sizeof(header.password_salt)); - if(memcmp(empty_salt, header.password_salt, sizeof(header.password_salt))) { - std::cout << "Password salt: " << color::cyan; - std::cout << std::hex; - for(std::size_t i = 0; i < std::size_t(boost::size(header.password_salt)); i++) { - std::cout << std::setfill('0') << std::setw(2) - << int(boost::uint8_t(header.password_salt[i])); - } - std::cout << color::reset << '\n'; - std::cout << std::dec; + if(!header.password_salt.empty()) { + std::cout << "Password salt: " << color::cyan + << print_hex(header.password_salt) << color::reset << '\n'; } } @@ -555,21 +546,22 @@ static void print_aux(const setup::info & info) { - if(info.wizard_image.empty() && info.wizard_image_small.empty() + if(info.wizard_images.empty() && info.wizard_images_small.empty() && info.decompressor_dll.empty()) { return; } std::cout << '\n'; - if(!info.wizard_image.empty()) { - std::cout << "Wizard image: " << print_bytes(info.wizard_image.length()) - << " (" << guess_extension(info.wizard_image) << ")\n"; + for(size_t i = 0; i < info.wizard_images.size(); i++) { + std::cout << "Wizard image #" << (i + 1) << ": " << print_bytes(info.wizard_images[i].length()) + << " (" << guess_extension(info.wizard_images[i]) << ")\n"; } - if(!info.wizard_image_small.empty()) { - std::cout << "Wizard small image: " << print_bytes(info.wizard_image_small.length()) - << " (" << guess_extension(info.wizard_image_small) << ")\n"; + for(size_t i = 0; i < info.wizard_images_small.size(); i++) { + std::cout << "Wizard small image #" << (i + 1) << ": " + << print_bytes(info.wizard_images_small[i].length()) + << " (" << guess_extension(info.wizard_images_small[i]) << ")\n"; } if(!info.decompressor_dll.empty()) { diff -Nru innoextract-1.6/src/cli/extract.cpp innoextract-1.7/src/cli/extract.cpp --- innoextract-1.6/src/cli/extract.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/cli/extract.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2016 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -21,18 +21,21 @@ #include "cli/extract.hpp" #include +#include #include #include #include #include #include -#include +#include #include +#include #include #include #include #include +#include #include #include @@ -43,6 +46,10 @@ #include "cli/debug.hpp" #include "cli/gog.hpp" +#include "cli/goggalaxy.hpp" + +#include "crypto/checksum.hpp" +#include "crypto/hasher.hpp" #include "loader/offsets.hpp" @@ -59,6 +66,7 @@ #include "util/boostfs_compat.hpp" #include "util/console.hpp" +#include "util/encoding.hpp" #include "util/fstream.hpp" #include "util/load.hpp" #include "util/log.hpp" @@ -69,102 +77,169 @@ namespace { -static size_t probe_bin_files(const extract_options & o, const setup::info & info, - const fs::path & dir, const std::string & basename, - size_t format = 0, size_t start = 0) { +template +class processed_item { - size_t count = 0; + std::string path_; + const Entry * entry_; - std::vector files; +public: - for(size_t i = start;; i++) { - - fs::path file; - if(format == 0) { - file = dir / basename; - } else { - file = dir / stream::slice_reader::slice_filename(basename, i, format); - } - - try { - if(!fs::is_regular_file(file)) { - break; - } - } catch(...) { - break; - } - - if(o.gog) { - files.push_back(file); - } else { - log_warning << file.filename() << " is not part of the installer!"; - count++; - } - - if(format == 0) { - break; - } - - } + processed_item(const std::string & path, const Entry * entry) + : path_(path), entry_(entry) { } - if(!files.empty()) { - gog::process_bin_files(files, o, info); - } + bool has_entry() const { return entry_ != NULL; } + const Entry & entry() const { return *entry_; } + const std::string & path() const { return path_; } - return count; -} + void set_entry(const Entry * entry) { entry_ = entry; } + void set_path(const std::string & path) { path_ = path; } + +}; -struct file_output { +class processed_file : public processed_item { - fs::path name; - util::ofstream stream; +public: - explicit file_output(const fs::path & file) : name(file) { - try { - stream.open(name, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); - if(!stream.is_open()) { - throw 0; - } - } catch(...) { - throw std::runtime_error("Coul not open output file \"" + name.string() + '"'); - } - } + processed_file(const setup::file_entry * entry, const std::string & path) + : processed_item(path, entry) { } + + bool is_multipart() const { return !entry().additional_locations.empty(); } }; -template -class processed_item { +class processed_directory : public processed_item { - std::string path_; - const Entry * entry_; bool implied_; public: - processed_item() : entry_(NULL), implied_(false) { } - - processed_item(const Entry * entry, const std::string & path, bool implied = false) - : path_(path), entry_(entry), implied_(implied) { } + explicit processed_directory(const std::string & path) + : processed_item(path, NULL), implied_(false) { } - processed_item(const std::string & path, bool implied = false) - : path_(path), entry_(NULL), implied_(implied) { } - - processed_item(const processed_item & o) - : path_(o.path_), entry_(o.entry_), implied_(o.implied_) { } - - bool has_entry() const { return entry_ != NULL; } - const Entry & entry() const { return *entry_; } - const std::string & path() const { return path_; } bool implied() const { return implied_; } - void set_entry(const Entry * entry) { entry_ = entry; } - void set_path(const std::string & path) { path_ = path; } void set_implied(bool implied) { implied_ = implied; } }; -typedef processed_item processed_file; -typedef processed_item processed_directory; +class file_output : private boost::noncopyable { + + fs::path path_; + const processed_file * file_; + util::ofstream stream_; + + crypto::hasher checksum_; + bool checksum_valid_; + + boost::uint64_t position_; + boost::uint64_t total_written_; + + bool write_; + +public: + + explicit file_output(const fs::path & dir, const processed_file * f, bool write) + : path_(dir / f->path()) + , file_(f) + , checksum_(f->entry().checksum.type) + , checksum_valid_(f->entry().checksum.type != crypto::None) + , position_(0) + , total_written_(0) + , write_(write) + { + if(write_) { + try { + stream_.open(path_, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); + if(!stream_.is_open()) { + throw std::exception(); + } + } catch(...) { + throw std::runtime_error("Coul not open output file \"" + path_.string() + '"'); + } + } + } + + bool write(const char * data, size_t n) { + + if(write_) { + stream_.write(data, std::streamsize(n)); + } + + if(checksum_valid_) { + checksum_.update(data, n); + } + + position_ += n; + total_written_ += n; + + return !write_ || !stream_.fail(); + } + + void seek(boost::uint64_t new_position) { + + if(new_position == position_) { + return; + } + + checksum_valid_ = false; + + debug("seeking output from " << print_hex(position_) << " to " << print_hex(new_position)); + + if(!write_) { + return; + } + + const boost::uint64_t max = boost::uint64_t(std::numeric_limits::max() / 4); + + if(new_position <= max) { + stream_.seekp(util::ofstream::off_type(new_position), std::ios_base::beg); + } else { + if(new_position > position_) { + boost::uint64_t diff = new_position - position_; + while(diff > max) { + stream_.seekp(util::ofstream::off_type(max), std::ios_base::cur); + diff -= max; + } + stream_.seekp(util::ofstream::off_type(diff), std::ios_base::cur); + } else { + boost::uint64_t diff = position_ - new_position; + while(diff > max) { + stream_.seekp(-util::ofstream::off_type(max), std::ios_base::cur); + diff -= max; + } + stream_.seekp(-util::ofstream::off_type(diff), std::ios_base::cur); + } + } + + position_ = new_position; + + } + + void close() { + + if(write_) { + stream_.close(); + } + + } + + const fs::path & path() const { return path_; } + const processed_file * file() const { return file_; } + + bool is_complete() const { + return total_written_ == file_->entry().size; + } + + bool has_checksum() const { + return checksum_valid_ && position_ == file_->entry().size; + } + + crypto::checksum checksum() { + return checksum_.finalize(); + } + +}; class path_filter { @@ -208,12 +283,12 @@ }; -static void print_filter_info(const setup::item & item, bool temp) { +void print_filter_info(const setup::item & item, bool temp) { bool first = true; if(!item.languages.empty()) { - std::cout << (first ? " [" : ", "); + std::cout << " ["; first = false; std::cout << color::green << item.languages << color::reset; } @@ -228,35 +303,43 @@ if(!first) { std::cout << "]"; } + } -static void print_filter_info(const setup::file_entry & file) { +void print_filter_info(const setup::file_entry & file) { bool is_temp = !!(file.options & setup::file_entry::DeleteAfterInstall); print_filter_info(file, is_temp); } -static void print_filter_info(const setup::directory_entry & dir) { +void print_filter_info(const setup::directory_entry & dir) { bool is_temp = !!(dir.options & setup::directory_entry::DeleteAfterInstall); print_filter_info(dir, is_temp); } -static void print_size_info(const stream::file & file) { +void print_size_info(const stream::file & file, boost::uint64_t size) { if(logger::debug) { std::cout << " @ " << print_hex(file.offset); } - std::cout << " (" << color::dim_cyan << print_bytes(file.size) << color::reset << ")"; + std::cout << " (" << color::dim_cyan << print_bytes(size ? size : file.size) << color::reset << ")"; } -static bool prompt_overwrite() { +void print_checksum_info(const stream::file & file, const crypto::checksum * checksum) { + + if(!checksum || checksum->type == crypto::None) { + checksum = &file.checksum; + } + + std::cout << color::dim_magenta << *checksum << color::reset; +} + +bool prompt_overwrite() { return true; // TODO the user always overwrites } -static const char * handle_collision(const setup::file_entry & oldfile, - const setup::data_entry & olddata, - const setup::file_entry & newfile, - const setup::data_entry & newdata) { +const char * handle_collision(const setup::file_entry & oldfile, const setup::data_entry & olddata, + const setup::file_entry & newfile, const setup::data_entry & newdata) { bool allow_timestamp = true; @@ -332,7 +415,7 @@ #endif typedef boost::unordered_map > CollisionMap; -static std::string parent_dir(const std::string & path) { +std::string parent_dir(const std::string & path) { size_t pos = path.find_last_of(setup::path_sep); if(pos == std::string::npos) { @@ -342,8 +425,8 @@ return path.substr(0, pos); } -static bool insert_dirs(DirectoriesMap & processed_directories, const path_filter & includes, - const std::string & internal_path, std::string & path, bool implied) { +bool insert_dirs(DirectoriesMap & processed_directories, const path_filter & includes, + const std::string & internal_path, std::string & path, bool implied) { std::string dir = parent_dir(path); std::string internal_dir = parent_dir(internal_path); @@ -381,20 +464,25 @@ size_t oldlength = dir.length(); if(insert_dirs(processed_directories, includes, internal_dir, dir, implied)) { + // Existing dir case differs, fix path if(dir.length() == oldlength) { path.replace(0, dir.length(), dir); } else { path = dir + path.substr(oldlength); } + // Also fix previously inserted directory + DirectoriesMap::iterator inserted = processed_directories.find(internal_dir); + if(inserted != processed_directories.end()) { + inserted->second.set_path(dir); + } return true; } return false; } -static bool rename_collision(const extract_options & o, FilesMap & processed_files, - const std::string & path, const processed_file & other, - bool common_component, bool common_language, bool first) { +bool rename_collision(const extract_options & o, FilesMap & processed_files, const std::string & path, + const processed_file & other, bool common_component, bool common_language, bool first) { const setup::file_entry & file = other.entry(); @@ -439,14 +527,14 @@ } -static void rename_collisions(const extract_options & o, FilesMap & processed_files, - const CollisionMap & collisions) { +void rename_collisions(const extract_options & o, FilesMap & processed_files, + const CollisionMap & collisions) { BOOST_FOREACH(const CollisionMap::value_type & collision, collisions) { const std::string & path = collision.first; - const processed_file & base = processed_files[path]; + const processed_file & base = processed_files.find(path)->second; const setup::file_entry & file = base.entry(); bool common_component = true; @@ -470,70 +558,7 @@ } } -} // anonymous namespace - -void process_file(const fs::path & file, const extract_options & o) { - - bool is_directory; - try { - is_directory = fs::is_directory(file); - } catch(...) { - throw std::runtime_error("Could not open file \"" + file.string() - + "\": access denied"); - } - if(is_directory) { - throw std::runtime_error("Input file \"" + file.string() + "\" is a directory!"); - } - - util::ifstream ifs; - try { - ifs.open(file, std::ios_base::in | std::ios_base::binary); - if(!ifs.is_open()) { - throw 0; - } - } catch(...) { - throw std::runtime_error("Could not open file \"" + file.string() + '"'); - } - - loader::offsets offsets; - offsets.load(ifs); - -#ifdef DEBUG - if(logger::debug) { - print_offsets(offsets); - std::cout << '\n'; - } -#endif - - setup::info::entry_types entries = 0; - if(o.list || o.test || o.extract) { - entries |= setup::info::Files; - entries |= setup::info::Directories; - entries |= setup::info::DataEntries; - } - if(o.list_languages) { - entries |= setup::info::Languages; - } - if(o.gog_game_id || o.gog) { - entries |= setup::info::RegistryEntries; - } -#ifdef DEBUG - if(logger::debug) { - entries = setup::info::entry_types::all(); - } -#endif - - ifs.seekg(offsets.header_offset); - setup::info info; - try { - info.load(ifs, entries); - } catch(const std::ios_base::failure & e) { - std::ostringstream oss; - oss << "Stream error while parsing setup headers!\n"; - oss << " ├─ detected setup version: " << info.version << '\n'; - oss << " └─ error reason: " << e.what(); - throw format_error(oss.str()); - } +bool print_file_info(const extract_options & o, const setup::info & info) { if(!o.quiet) { const std::string & name = info.header.app_versioned_name.empty() @@ -551,15 +576,15 @@ << std::endl; } -#ifdef DEBUG + #ifdef DEBUG if(logger::debug) { std::cout << '\n'; print_info(info); std::cout << '\n'; } -#endif + #endif - bool multiple_sections = (o.list_languages + o.gog_game_id + o.list > 1); + bool multiple_sections = (o.list_languages + o.gog_game_id + o.list + o.show_password > 1); if(!o.quiet && multiple_sections) { std::cout << '\n'; } @@ -575,7 +600,10 @@ } BOOST_FOREACH(const setup::language_entry & language, info.languages) { std::cout << " - " << color::green << language.name << color::reset; - std::cout << ": " << color::white << language.language_name << color::reset << '\n'; + if(!language.language_name.empty()) { + std::cout << ": " << color::white << language.language_name << color::reset; + } + std::cout << '\n'; } if(info.languages.empty()) { std::cout << " (none)\n"; @@ -595,29 +623,65 @@ } else if(!o.silent) { std::cout << "GOG.com game ID is " << color::cyan << id << color::reset << '\n'; } else { - std::cout << id; + std::cout << id << '\n'; } if((o.silent || !o.quiet) && multiple_sections) { std::cout << '\n'; } } - if(!o.list && !o.test && !o.extract) { - return; + if(o.show_password) { + if(info.header.options & setup::header::Password) { + if(o.silent) { + std::cout << info.header.password << '\n'; + } else { + std::cout << "Password hash: " << color::yellow << info.header.password << color::reset << '\n'; + } + if(o.silent) { + std::cout << print_hex(info.header.password_salt) << '\n'; + } else if(!info.header.password_salt.empty()) { + std::cout << "Password salt: " << color::yellow + << print_hex(info.header.password_salt) << color::reset; + if(!o.quiet) { + std::cout << " (hex bytes, prepended to password)"; + } + std::cout << '\n'; + } + if(o.silent) { + std::cout << util::encoding_name(info.version.codepage()) << '\n'; + } else { + std::cout << "Password encoding: " << color::yellow + << util::encoding_name(info.version.codepage()) << color::reset << '\n'; + } + } else if(!o.quiet) { + std::cout << "Setup is not passworded!\n"; + } + if((o.silent || !o.quiet) && multiple_sections) { + std::cout << '\n'; + } } - if(!o.silent && multiple_sections) { - std::cout << "Files:\n"; - } + return multiple_sections; +} + +struct processed_entries { + + FilesMap files; + + DirectoriesMap directories; + +}; + +processed_entries filter_entries(const extract_options & o, const setup::info & info) { + + processed_entries processed; - FilesMap processed_files; #if BOOST_VERSION >= 105000 - processed_files.reserve(info.files.size()); + processed.files.reserve(info.files.size()); #endif - DirectoriesMap processed_directories; #if BOOST_VERSION >= 104800 - processed_directories.reserve(info.directories.size() + processed.directories.reserve(info.directories.size() + size_t(std::log(double(info.files.size())))); #endif @@ -648,17 +712,17 @@ bool path_included = includes.match(internal_path); - insert_dirs(processed_directories, includes, internal_path, path, path_included); + insert_dirs(processed.directories, includes, internal_path, path, path_included); DirectoriesMap::iterator it; if(path_included) { - std::pair existing = processed_directories.insert( + std::pair existing = processed.directories.insert( std::make_pair(internal_path, processed_directory(path)) ); it = existing.first; } else { - it = processed_directories.find(internal_path); - if(it == processed_directories.end()) { + it = processed.directories.find(internal_path); + if(it == processed.directories.end()) { continue; } } @@ -693,13 +757,13 @@ bool path_included = includes.match(internal_path); - insert_dirs(processed_directories, includes, internal_path, path, path_included); + insert_dirs(processed.directories, includes, internal_path, path, path_included); if(!path_included) { continue; // Ignore excluded file } - std::pair insertion = processed_files.insert(std::make_pair( + std::pair insertion = processed.files.insert(std::make_pair( internal_path, processed_file(&file, path) )); @@ -736,8 +800,13 @@ const std::string & clobberedpath = skip ? path : existing.path(); std::cout << '"' << color::dim_yellow << clobberedpath << color::reset << '"'; print_filter_info(skip ? file : existing.entry()); - if(!o.quiet) { - print_size_info(skip ? newdata.file : olddata.file); + if(o.list_sizes) { + print_size_info(skip ? newdata.file : olddata.file, skip ? file.size : existing.entry().size); + } + if(o.list_checksums) { + std::cout << ' '; + print_checksum_info(skip ? newdata.file : olddata.file, + skip ? &file.checksum : &existing.entry().checksum); } std::cout << " - " << (skip ? skip : "overwritten") << '\n'; } @@ -757,10 +826,134 @@ } if(o.collisions == RenameCollisions || o.collisions == RenameAllCollisions) { - rename_collisions(o, processed_files, collisions); - collisions.clear(); + rename_collisions(o, processed.files, collisions); + } + + return processed; +} + +} // anonymous namespace + +void process_file(const fs::path & file, const extract_options & o) { + + bool is_directory; + try { + is_directory = fs::is_directory(file); + } catch(...) { + throw std::runtime_error("Could not open file \"" + file.string() + + "\": access denied"); + } + if(is_directory) { + throw std::runtime_error("Input file \"" + file.string() + "\" is a directory!"); + } + + util::ifstream ifs; + try { + ifs.open(file, std::ios_base::in | std::ios_base::binary); + if(!ifs.is_open()) { + throw std::exception(); + } + } catch(...) { + throw std::runtime_error("Could not open file \"" + file.string() + '"'); } + loader::offsets offsets; + offsets.load(ifs); + + if(o.data_version) { + setup::version version; + ifs.seekg(offsets.header_offset); + version.load(ifs); + if(o.silent) { + std::cout << version << '\n'; + } else { + std::cout << color::white << version << color::reset << '\n'; + } + return; + } + +#ifdef DEBUG + if(logger::debug) { + print_offsets(offsets); + std::cout << '\n'; + } +#endif + + setup::info::entry_types entries = 0; + if(o.list || o.test || o.extract || (o.gog_galaxy && o.list_languages)) { + entries |= setup::info::Files; + entries |= setup::info::Directories; + entries |= setup::info::DataEntries; + } + if(o.list_languages) { + entries |= setup::info::Languages; + } + if(o.gog_game_id || o.gog) { + entries |= setup::info::RegistryEntries; + } + if(!o.extract_unknown) { + entries |= setup::info::NoUnknownVersion; + } +#ifdef DEBUG + if(logger::debug) { + entries = setup::info::entry_types::all(); + } +#endif + + ifs.seekg(offsets.header_offset); + setup::info info; + try { + info.load(ifs, entries); + } catch(const std::ios_base::failure & e) { + std::ostringstream oss; + oss << "Stream error while parsing setup headers!\n"; + oss << " ├─ detected setup version: " << info.version << '\n'; + oss << " └─ error reason: " << e.what(); + throw format_error(oss.str()); + } + + if(o.gog_galaxy && (o.list || o.test || o.extract || o.list_languages)) { + gog::parse_galaxy_files(info, o.gog); + } + + bool multiple_sections = print_file_info(o, info); + + std::string password; + if(o.password.empty()) { + if(!o.quiet && (o.list || o.test || o.extract) && (info.header.options & setup::header::EncryptionUsed)) { + log_warning << "Setup contains encrypted files, use the --password option to extract them"; + } + } else { + util::from_utf8(o.password, password, info.version.codepage()); + if(info.header.options & setup::header::Password) { + crypto::hasher checksum(info.header.password.type); + checksum.update(info.header.password_salt.c_str(), info.header.password_salt.length()); + checksum.update(password.c_str(), password.length()); + if(checksum.finalize() != info.header.password) { + if(o.check_password) { + throw std::runtime_error("Incorrect password provided"); + } + log_error << "Incorrect password provided"; + password.clear(); + } + } + #if !INNOEXTRACT_HAVE_ARC4 + if((o.extract || o.test) && (info.header.options & setup::header::EncryptionUsed)) { + log_warning << "ARC4 decryption not supported in this build, skipping compressed chunks"; + } + password.clear(); + #endif + } + + if(!o.list && !o.test && !o.extract) { + return; + } + + if(!o.silent && multiple_sections) { + std::cout << "Files:\n"; + } + + processed_entries processed = filter_entries(o, info); if(o.extract && !o.output_dir.empty()) { fs::create_directories(o.output_dir); @@ -768,7 +961,7 @@ if(o.list || o.extract) { - BOOST_FOREACH(const DirectoriesMap::value_type & i, processed_directories) { + BOOST_FOREACH(const DirectoriesMap::value_type & i, processed.directories) { const std::string & path = i.second.path(); @@ -802,68 +995,77 @@ } - std::vector< std::vector > files_for_location; + typedef std::pair output_location; + std::vector< std::vector > files_for_location; files_for_location.resize(info.data_entries.size()); - BOOST_FOREACH(const FilesMap::value_type & i, processed_files) { + BOOST_FOREACH(const FilesMap::value_type & i, processed.files) { const processed_file & file = i.second; - files_for_location[file.entry().location].push_back(&file); + files_for_location[file.entry().location].push_back(output_location(&file, 0)); + if(o.test || o.extract) { + boost::uint64_t offset = info.data_entries[file.entry().location].uncompressed_size; + BOOST_FOREACH(boost::uint32_t location, file.entry().additional_locations) { + files_for_location[location].push_back(output_location(&file, offset)); + offset += info.data_entries[location].uncompressed_size; + } + } } boost::uint64_t total_size = 0; - size_t max_slice = 0; typedef std::map Files; typedef std::map Chunks; Chunks chunks; for(size_t i = 0; i < info.data_entries.size(); i++) { - setup::data_entry & location = info.data_entries[i]; - if(!offsets.data_offset) { - max_slice = std::max(max_slice, location.chunk.first_slice); - max_slice = std::max(max_slice, location.chunk.last_slice); - } if(files_for_location[i].empty()) { continue; } + setup::data_entry & location = info.data_entries[i]; if(location.chunk.compression == stream::UnknownCompression) { location.chunk.compression = info.header.compression; } chunks[location.chunk][location.file] = i; - total_size += location.file.size; + total_size += location.uncompressed_size; } - fs::path dir = file.parent_path(); - std::string basename = util::as_string(file.stem()); - boost::scoped_ptr slice_reader; if(o.extract || o.test) { if(offsets.data_offset) { slice_reader.reset(new stream::slice_reader(&ifs, offsets.data_offset)); } else { - slice_reader.reset(new stream::slice_reader(dir, basename, info.header.slices_per_disk)); + fs::path dir = file.parent_path(); + std::string basename = util::as_string(file.stem()); + std::string basename2 = info.header.base_filename; + // Prevent access to unexpected files + std::replace(basename2.begin(), basename2.end(), '/', '_'); + std::replace(basename2.begin(), basename2.end(), '\\', '_'); + // Older Inno Setup versions used the basename stored in the headers, change our default accordingly + if(info.version < INNO_VERSION(4, 1, 7) && !basename2.empty()) { + std::swap(basename2, basename); + } + slice_reader.reset(new stream::slice_reader(dir, basename, basename2, info.header.slices_per_disk)); } } progress extract_progress(total_size); + typedef boost::ptr_map multi_part_outputs; + multi_part_outputs multi_outputs; + BOOST_FOREACH(const Chunks::value_type & chunk, chunks) { debug("[starting " << chunk.first.compression << " chunk @ slice " << chunk.first.first_slice << " + " << print_hex(offsets.data_offset) << " + " << print_hex(chunk.first.offset) << ']'); - if(chunk.first.encrypted) { - log_warning << "Skipping encrypted chunk (unsupported)"; - } - stream::chunk_reader::pointer chunk_source; - if((o.extract || o.test) && !chunk.first.encrypted) { - chunk_source = stream::chunk_reader::get(*slice_reader, chunk.first); + if((o.extract || o.test) && (chunk.first.encryption == stream::Plaintext || !password.empty())) { + chunk_source = stream::chunk_reader::get(*slice_reader, chunk.first, password); } boost::uint64_t offset = 0; BOOST_FOREACH(const Files::value_type & location, chunk.second) { const stream::file & file = location.first; - const std::vector & names = files_for_location[location.second]; + const std::vector & output_locations = files_for_location[location.second]; if(file.offset > offset) { debug("discarding " << print_bytes(file.offset - offset) @@ -880,34 +1082,71 @@ if(!o.silent) { - std::cout << " - "; bool named = false; - BOOST_FOREACH(const processed_file * name, names) { + boost::uint64_t size = 0; + const crypto::checksum * checksum = NULL; + BOOST_FOREACH(const output_location & output, output_locations) { + if(output.second != 0) { + continue; + } + if(output.first->entry().size != 0) { + if(size != 0 && size != output.first->entry().size) { + log_warning << "Mismatched output sizes"; + } + size = output.first->entry().size; + } + if(output.first->entry().checksum.type != crypto::None) { + if(checksum && *checksum != output.first->entry().checksum) { + log_warning << "Mismatched output checksums"; + } + checksum = &output.first->entry().checksum; + } if(named) { std::cout << ", "; + } else { + std::cout << " - "; + named = true; } - if(chunk.first.encrypted) { - std::cout << '"' << color::dim_yellow << name->path() << color::reset << '"'; + if(chunk.first.encryption != stream::Plaintext) { + if(password.empty()) { + std::cout << '"' << color::dim_yellow << output.first->path() << color::reset << '"'; + } else { + std::cout << '"' << color::yellow << output.first->path() << color::reset << '"'; + } } else { - std::cout << '"' << color::white << name->path() << color::reset << '"'; + std::cout << '"' << color::white << output.first->path() << color::reset << '"'; } - print_filter_info(name->entry()); - named = true; - } - if(!named) { - std::cout << color::white << "unnamed file" << color::reset; - } - if(!o.quiet) { - print_size_info(file); + print_filter_info(output.first->entry()); } - if(chunk.first.encrypted) { - std::cout << " - encrypted"; + + if(named) { + if(o.list_sizes) { + print_size_info(file, size); + } + if(o.list_checksums) { + std::cout << ' '; + print_checksum_info(file, checksum); + } + if(chunk.first.encryption != stream::Plaintext && password.empty()) { + std::cout << " - encrypted"; + } + std::cout << '\n'; } - std::cout << '\n'; } else { - BOOST_FOREACH(const processed_file * name, names) { - std::cout << color::white << name->path() << color::reset << '\n'; + BOOST_FOREACH(const output_location & output, output_locations) { + if(output.second == 0) { + const processed_file * fileinfo = output.first; + if(o.list_sizes) { + boost::uint64_t size = fileinfo->entry().size; + std::cout << color::dim_cyan << (size != 0 ? size : file.size) << color::reset << ' '; + } + if(o.list_checksums) { + print_checksum_info(file, &fileinfo->entry().checksum); + std::cout << ' '; + } + std::cout << color::white << fileinfo->path() << color::reset << '\n'; + } } } @@ -938,49 +1177,110 @@ file_source = stream::file_reader::get(*chunk_source, file, &checksum); // Open output files - boost::ptr_vector output; - if(!o.test) { - output.reserve(names.size()); - BOOST_FOREACH(const processed_file * name, names) { - try { - output.push_back(new file_output(o.output_dir / name->path())); - } catch(boost::bad_pointer &) { - // should never happen - std::terminate(); + boost::ptr_vector single_outputs; + std::vector outputs; + BOOST_FOREACH(const output_location & location, output_locations) { + const processed_file * fileinfo = location.first; + try { + + if(!o.extract && fileinfo->entry().checksum.type == crypto::None) { + continue; } + + // Re-use existing file output for multi-part files + file_output * output = NULL; + if(fileinfo->is_multipart()) { + multi_part_outputs::iterator it = multi_outputs.find(fileinfo); + if(it != multi_outputs.end()) { + output = it->second; + } + } + + if(!output) { + output = new file_output(o.output_dir, fileinfo, o.extract); + if(fileinfo->is_multipart()) { + multi_outputs.insert(fileinfo, output); + } else { + single_outputs.push_back(output); + } + } + + outputs.push_back(output); + + output->seek(location.second); + + } catch(boost::bad_pointer &) { + // should never happen + std::terminate(); } } // Copy data + boost::uint64_t output_size = 0; while(!file_source->eof()) { char buffer[8192 * 10]; std::streamsize buffer_size = std::streamsize(boost::size(buffer)); std::streamsize n = file_source->read(buffer, buffer_size).gcount(); if(n > 0) { - BOOST_FOREACH(file_output & out, output) { - out.stream.write(buffer, n); - if(out.stream.fail()) { - throw std::runtime_error("Error writing file \"" - + out.name.string() + '"'); + BOOST_FOREACH(file_output * output, outputs) { + bool success = output->write(buffer, size_t(n)); + if(!success) { + throw std::runtime_error("Error writing file \"" + output->path().string() + '"'); } } extract_progress.update(boost::uint64_t(n)); + output_size += boost::uint64_t(n); } } - // Adjust file timestamps - if(o.preserve_file_times) { - const setup::data_entry & data = info.data_entries[location.second]; - util::time filetime = data.timestamp; - if(o.local_timestamps && !(data.options & data.TimeStampInUTC)) { - filetime = util::to_local_time(filetime); - } - BOOST_FOREACH(file_output & out, output) { - out.stream.close(); - if(!util::set_file_time(out.name, filetime, data.timestamp_nsec)) { - log_warning << "Error setting timestamp on file " << out.name; + const setup::data_entry & data = info.data_entries[location.second]; + + if(output_size != data.uncompressed_size) { + log_warning << "Unexpected output file size: " << output_size << " != " << data.uncompressed_size; + } + + util::time filetime = data.timestamp; + if(o.extract && o.preserve_file_times && o.local_timestamps && !(data.options & data.TimeStampInUTC)) { + filetime = util::to_local_time(filetime); + } + + BOOST_FOREACH(file_output * output, outputs) { + + if(output->file()->is_multipart() && !output->is_complete()) { + continue; + } + + // Verify output checksum if available + if(output->file()->entry().checksum.type != crypto::None) { + if(output->has_checksum()) { + crypto::checksum checksum = output->checksum(); + if(checksum != output->file()->entry().checksum) { + log_warning << "Output checksum mismatch for " << output->file()->path() << ":\n" + << " ├─ actual: " << checksum << '\n' + << " └─ expected: " << output->file()->entry().checksum; + if(o.test) { + throw std::runtime_error("Integrity test failed!"); + } + } + } else { + // This should not happen + log_warning << "Could not verify output checksum of file " << output->file()->path(); } } + + // Adjust file timestamps + if(o.extract && o.preserve_file_times) { + output->close(); + if(!util::set_file_time(output->path(), filetime, data.timestamp_nsec)) { + log_warning << "Error setting timestamp on file " << output->path(); + } + } + + if(output->file()->is_multipart()) { + debug("[finalizing multi-part file]"); + multi_outputs.erase(output->file()); + } + } // Verify checksums @@ -992,6 +1292,7 @@ throw std::runtime_error("Integrity test failed!"); } } + } #ifdef DEBUG @@ -1004,35 +1305,12 @@ extract_progress.clear(); + if(!multi_outputs.empty()) { + log_warning << "Incomplete multi-part files"; + } + if(o.warn_unused || o.gog) { - size_t bin_count = 0; - bin_count += size_t(probe_bin_files(o, info, dir, basename + ".bin")); - bin_count += size_t(probe_bin_files(o, info, dir, basename + "-0" + ".bin")); - size_t slice = 0; - size_t format = 1; - if(!offsets.data_offset && info.header.slices_per_disk == 1) { - slice = max_slice + 1; - } - bin_count += probe_bin_files(o, info, dir, basename, format, slice); - slice = 0; - format = 2; - if(!offsets.data_offset && info.header.slices_per_disk != 1) { - slice = max_slice + 1; - format = info.header.slices_per_disk; - } - bin_count += probe_bin_files(o, info, dir, basename, format, slice); - if(bin_count) { - const char * verb = "inspecting"; - if(o.extract) { - verb = "extracting"; - } else if(o.test) { - verb = "testing"; - } else if(o.list) { - verb = "listing the contents of"; - } - std::cerr << color::yellow << "Use the --gog option to try " << verb << " " - << (bin_count > 1 ? "these files" : "this file") << ".\n" << color::reset; - } + gog::probe_bin_files(o, info, file, offsets.data_offset == 0); } } diff -Nru innoextract-1.6/src/cli/extract.hpp innoextract-1.7/src/cli/extract.hpp --- innoextract-1.6/src/cli/extract.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/cli/extract.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2016 Daniel Scharrer + * Copyright (C) 2014-2018 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 @@ -52,16 +52,25 @@ bool warn_unused; //!< Warn if there are unused files + bool list_sizes; //!< Show size information for files + bool list_checksums; //!< Show checksum information for files + + bool data_version; //!< Print the data version bool list; //!< List files bool test; //!< Test files (but don't extract) bool extract; //!< Extract files bool list_languages; //!< List available languages bool gog_game_id; //!< Show the GOG.com game id + bool show_password; //!< Show password check information + bool check_password; //!< Abort if the provided password is incorrect bool preserve_file_times; //!< Set timestamps of extracted files bool local_timestamps; //!< Use local timezone for setting timestamps bool gog; //!< Try to extract additional archives used in GOG.com installers + bool gog_galaxy; //!< Try to re-assemble GOG Galaxy files + + bool extract_unknown; //!< Try to extract unknown Inno Setup versions bool extract_temp; //!< Extract temporary files bool language_only; //!< Extract files not associated with any language @@ -72,8 +81,34 @@ CollisionAction collisions; std::string default_language; + std::string password; + boost::filesystem::path output_dir; + extract_options() + : quiet(false) + , silent(false) + , warn_unused(false) + , list_sizes(false) + , list_checksums(false) + , data_version(false) + , list(false) + , test(false) + , extract(false) + , list_languages(false) + , gog_game_id(false) + , show_password(false) + , check_password(false) + , preserve_file_times(false) + , local_timestamps(false) + , gog(false) + , gog_galaxy(false) + , extract_unknown(false) + , extract_temp(false) + , language_only(false) + , collisions(OverwriteCollisions) + { } + }; void process_file(const boost::filesystem::path & file, const extract_options & o); diff -Nru innoextract-1.6/src/cli/gog.cpp innoextract-1.7/src/cli/gog.cpp --- innoextract-1.6/src/cli/gog.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/cli/gog.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2015 Daniel Scharrer + * Copyright (C) 2014-2018 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 @@ -24,9 +24,12 @@ #include #include #include +#include #include +#include #include +#include #include #include @@ -36,9 +39,13 @@ #include "loader/offsets.hpp" +#include "setup/data.hpp" #include "setup/info.hpp" #include "setup/registry.hpp" +#include "stream/slice.hpp" + +#include "util/console.hpp" #include "util/boostfs_compat.hpp" #include "util/fstream.hpp" #include "util/log.hpp" @@ -80,7 +87,7 @@ namespace { -static std::string get_verb(const extract_options & o) { +std::string get_verb(const extract_options & o) { const char * verb = "inspect"; if(o.extract) { verb = "extract"; @@ -92,13 +99,12 @@ return verb; } -static volatile sig_atomic_t quit_requested = 0; -static void quit_handler(int /* ignored */) { +volatile sig_atomic_t quit_requested = 0; +void quit_handler(int /* ignored */) { quit_requested = 1; } -static bool process_file_unrar(const fs::path & file, const extract_options & o, - const std::string & password) { +bool process_file_unrar(const std::string & file, const extract_options & o, const std::string & password) { std::vector args; args.push_back("unrar"); @@ -146,8 +152,7 @@ args.push_back("--"); - std::string filename = file.string(); - args.push_back(filename.c_str()); + args.push_back(file.c_str()); std::string dir = o.output_dir.string(); if(!dir.empty()) { @@ -173,15 +178,13 @@ } if(ret > 0) { - throw std::runtime_error("Could not " + get_verb(o) + " \"" + file.string() - + "\": unrar failed"); + throw std::runtime_error("Could not " + get_verb(o) + " \"" + file + "\": unrar failed"); } return true; } -static bool process_file_unar(const fs::path & file, const extract_options & o, - const std::string & password) { +bool process_file_unar(const std::string & file, const extract_options & o, const std::string & password) { std::string dir = o.output_dir.string(); @@ -217,8 +220,7 @@ args.push_back("--"); - std::string filename = file.string(); - args.push_back(filename.c_str()); + args.push_back(file.c_str()); args.push_back(NULL); @@ -228,19 +230,17 @@ } if(ret > 0) { - throw std::runtime_error("Could not " + get_verb(o) + " \"" + file.string() - + "\": unar failed"); + throw std::runtime_error("Could not " + get_verb(o) + " \"" + file + "\": unar failed"); } return true; } -static bool process_rar_file(const fs::path & file, const extract_options & o, - const std::string & password) { +bool process_rar_file(const std::string & file, const extract_options & o, const std::string & password) { return process_file_unrar(file, o, password) || process_file_unar(file, o, password); } -static char hex_char(int c) { +char hex_char(int c) { if(c < 10) { return char('0' + c); } else { @@ -248,7 +248,7 @@ } } -class temporary_directory { +class temporary_directory : private boost::noncopyable { fs::path path; @@ -284,8 +284,8 @@ }; -static void process_rar_files(const std::vector & files, - const extract_options & o, const setup::info & info) { +void process_rar_files(const std::vector & files, + const extract_options & o, const setup::info & info) { if((!o.list && !o.test && !o.extract) || files.empty()) { return; @@ -301,8 +301,8 @@ md5.finalize(hash); password.resize(size_t(boost::size(hash) * 2)); for(size_t i = 0; i < size_t(boost::size(hash)); i++) { - password[2 * i + 0] = hex_char(((unsigned char)hash[i]) / 16); - password[2 * i + 1] = hex_char(((unsigned char)hash[i]) % 16); + password[2 * i + 0] = hex_char(boost::uint8_t(hash[i]) / 16); + password[2 * i + 1] = hex_char(boost::uint8_t(hash[i]) % 16); } } @@ -312,7 +312,7 @@ bool ok = true; BOOST_FOREACH(const fs::path & file, files) { - if(!process_rar_file(file, o, password)) { + if(!process_rar_file(file.string(), o, password)) { ok = false; } } @@ -378,7 +378,7 @@ + "\": unable to create .r?? symlinks"); } - if(process_rar_file(first_file, o, password)) { + if(process_rar_file(first_file.string(), o, password)) { return; } @@ -401,8 +401,6 @@ + "\": install `unrar` or `unar`"); } -} // anonymous namespace - void process_bin_files(const std::vector & files, const extract_options & o, const setup::info & info) { @@ -428,6 +426,7 @@ extract_options new_options = o; new_options.gog = false; new_options.warn_unused = false; + std::cout << '\n'; process_file(files.front(), new_options); return; } @@ -439,4 +438,99 @@ + "\": unknown filetype"); } +size_t probe_bin_file_series(const extract_options & o, const setup::info & info, const fs::path & dir, + const std::string & basename, size_t format = 0, size_t start = 0) { + + size_t count = 0; + + std::vector files; + + for(size_t i = start;; i++) { + + fs::path file; + if(format == 0) { + file = dir / basename; + } else { + file = dir / stream::slice_reader::slice_filename(basename, i, format); + } + + try { + if(!fs::is_regular_file(file)) { + break; + } + } catch(...) { + break; + } + + if(o.gog) { + files.push_back(file); + } else { + log_warning << file.filename() << " is not part of the installer!"; + count++; + } + + if(format == 0) { + break; + } + + } + + if(!files.empty()) { + process_bin_files(files, o, info); + } + + return count; +} + +} // anonymous namespace + +void probe_bin_files(const extract_options & o, const setup::info & info, + const fs::path & setup_file, bool external) { + + boost::filesystem::path dir = setup_file.parent_path(); + std::string basename = util::as_string(setup_file.stem()); + + size_t bin_count = 0; + bin_count += probe_bin_file_series(o, info, dir, basename + ".bin"); + bin_count += probe_bin_file_series(o, info, dir, basename + "-0" + ".bin"); + + + size_t max_slice = 0; + if(external) { + BOOST_FOREACH(const setup::data_entry & location, info.data_entries) { + max_slice = std::max(max_slice, location.chunk.first_slice); + max_slice = std::max(max_slice, location.chunk.last_slice); + } + } + + size_t slice = 0; + size_t format = 1; + if(external && info.header.slices_per_disk == 1) { + slice = max_slice + 1; + } + bin_count += probe_bin_file_series(o, info, dir, basename, format, slice); + + slice = 0; + format = 2; + if(external && info.header.slices_per_disk != 1) { + slice = max_slice + 1; + format = info.header.slices_per_disk; + } + bin_count += probe_bin_file_series(o, info, dir, basename, format, slice); + + if(bin_count) { + const char * verb = "inspecting"; + if(o.extract) { + verb = "extracting"; + } else if(o.test) { + verb = "testing"; + } else if(o.list) { + verb = "listing the contents of"; + } + std::cerr << color::yellow << "Use the --gog option to try " << verb << " " + << (bin_count > 1 ? "these files" : "this file") << ".\n" << color::reset; + } + +} + } // namespace gog diff -Nru innoextract-1.6/src/cli/goggalaxy.cpp innoextract-1.7/src/cli/goggalaxy.cpp --- innoextract-1.6/src/cli/goggalaxy.cpp 1970-01-01 00:00:00.000000000 +0000 +++ innoextract-1.7/src/cli/goggalaxy.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2018 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 "cli/goggalaxy.hpp" + +#include +#include +#include + +#include +#include +#include +#include + +#include "crypto/checksum.hpp" + +#include "setup/data.hpp" +#include "setup/file.hpp" +#include "setup/info.hpp" +#include "setup/language.hpp" + +#include "util/log.hpp" + +namespace gog { + +namespace { + +std::vector parse_function_call(const std::string & code, const std::string & name) { + + std::vector arguments; + if(code.empty()) { + return arguments; + } + + const char whitespace[] = " \t\r\n"; + const char separator[] = " \t\r\n(),'"; + + size_t start = code.find_first_not_of(whitespace); + if(start == std::string::npos) { + return arguments; + } + + size_t end = code.find_first_of(separator, start); + if(end == std::string::npos) { + return arguments; + } + + size_t parenthesis = code.find_first_not_of(whitespace, end); + if(parenthesis == std::string::npos || code[parenthesis] != '(') { + return arguments; + } + + if(end - start != name.length() || code.compare(start, end - start, name) != 0) { + return arguments; + } + + size_t p = parenthesis + 1; + while(true) { + + p = code.find_first_not_of(whitespace, p); + if(p == std::string::npos) { + log_warning << "Error parsing function call: " << code; + return arguments; + } + + arguments.resize(arguments.size() + 1); + + if(code[p] == '\'') { + p++; + while(true) { + size_t string_end = code.find('\'', p); + arguments.back() += code.substr(p, string_end - p); + if(string_end == std::string::npos || string_end + 1 == code.size()) { + log_warning << "Error parsing function call: " << code; + return arguments; + } + p = string_end + 1; + if(code[p] == '\'') { + arguments.back() += '\''; + p++; + } else { + break; + } + } + } else { + size_t token_end = code.find_first_of(separator, p); + arguments.back() = code.substr(p, token_end - p); + if(token_end == std::string::npos || token_end == code.size()) { + log_warning << "Error parsing function call: " << code; + return arguments; + } + p = token_end; + } + + p = code.find_first_not_of(whitespace, p); + if(p == std::string::npos) { + log_warning << "Error parsing function call: " << code; + return arguments; + } + + if(code[p] == ')') { + break; + } else if(code[p] == ',') { + p++; + } else { + log_warning << "Error parsing function call: " << code; + return arguments; + } + + } + + p++; + if(p != code.size()) { + p = code.find_first_not_of(whitespace, p); + if(p != std::string::npos) { + if(code[p] != ';' || code.find_first_not_of(whitespace, p + 1) != std::string::npos) { + log_warning << "Error parsing function call: " << code; + } + } + } + + return arguments; +} + +int parse_hex(char c) { + if(c >= '0' && c <= '9') { + return c - '0'; + } else if(c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } else if(c >= 'A' && c <= 'F') { + return c - 'a' + 10; + } else { + return -1; + } +} + +crypto::checksum parse_checksum(const std::string & string) { + + crypto::checksum checksum; + checksum.type = crypto::MD5; + + if(string.length() != 32) { + // Unknown checksum type + checksum.type = crypto::None; + return checksum; + } + + for(size_t i = 0; i < 16; i++) { + int a = parse_hex(string[2 * i]); + int b = parse_hex(string[2 * i + 1]); + if(a < 0 || b < 0) { + checksum.type = crypto::None; + break; + } + checksum.md5[i] = char((a << 4) | b); + } + + return checksum; +} + +struct constraint { + + std::string name; + bool negated; + + explicit constraint(const std::string & name, bool negated = false) : name(name), negated(negated) { } + +}; + +std::vector parse_constraints(const std::string & input) { + + std::vector result; + + size_t start = 0; + + while(start < input.length()) { + + start = input.find_first_not_of(" \t\r\n", start); + if(start == std::string::npos) { + break; + } + + bool negated = false; + if(input[start] == '!') { + negated = true; + start++; + } + + size_t end = input.find('#', start); + if(end == std::string::npos) { + end = input.length(); + } + + if(end != start) { + std::string token = input.substr(start, end - start); + boost::trim(token); + result.push_back(constraint(token, negated)); + } + + if(end == std::string::npos) { + end = input.length(); + } + + start = end + 1; + } + + return result; +} + +std::string create_constraint_expression(std::vector & constraints) { + + std::string result; + + BOOST_FOREACH(const constraint & entry, constraints) { + + if(!result.empty()) { + result += " or "; + } + + if(entry.negated) { + result += " not "; + } + + result += entry.name; + + } + + return result; +} + +} // anonymous namespace + +void parse_galaxy_files(setup::info & info, bool force) { + + if(!force) { + bool is_gog = boost::icontains(info.header.app_publisher, "GOG.com"); + is_gog = is_gog || boost::icontains(info.header.app_publisher_url, "www.gog.com"); + is_gog = is_gog || boost::icontains(info.header.app_support_url, "www.gog.com"); + is_gog = is_gog || boost::icontains(info.header.app_updates_url, "www.gog.com"); + if(!is_gog) { + return; + } + } + + setup::file_entry * file_start = NULL; + size_t remaining_parts = 0; + + bool has_language_constraints = false; + std::set all_languages; + + BOOST_FOREACH(setup::file_entry & file, info.files) { + + // Multi-part file info: file checksum, filename, part count + std::vector start_info = parse_function_call(file.before_install, "before_install"); + if(start_info.empty()) { + start_info = parse_function_call(file.before_install, "before_install_dependency"); + } + if(!start_info.empty()) { + + if(remaining_parts != 0) { + log_warning << "Incomplete GOG Galaxy file " << file_start->destination; + remaining_parts = 0; + } + + // Recover the original filename - parts are named after the MD5 hash of their contents + if(start_info.size() >= 2 && !start_info[1].empty()) { + file.destination = start_info[1]; + } + + file.checksum = parse_checksum(start_info[0]); + file.size = 0; + if(file.checksum.type == crypto::None) { + log_warning << "Could not parse checksum for GOG Galaxy file " << file.destination + << ": " << start_info[0]; + } + + if(start_info.size() < 3) { + log_warning << "Missing part count for GOG Galaxy file " << file.destination; + remaining_parts = 1; + } else { + try { + remaining_parts = boost::lexical_cast(start_info[2]); + if(remaining_parts == 0) { + remaining_parts = 1; + } + file_start = &file; + } catch(...) { + log_warning << "Could not parse part count for GOG Galaxy file " << file.destination + << ": " << start_info[2]; + } + } + + } + + // File part ifo: part checksum, compressed part size, uncompressed part size + std::vector part_info = parse_function_call(file.after_install, "after_install"); + if(part_info.empty()) { + part_info = parse_function_call(file.after_install, "after_install_dependency"); + } + if(!part_info.empty()) { + if(remaining_parts == 0) { + log_warning << "Missing file start for GOG Galaxy file part " << file.destination; + } else if(file.location > info.data_entries.size()) { + log_warning << "Invalid data location for GOG Galaxy file part " << file.destination; + remaining_parts = 0; + } else if(part_info.size() < 3) { + log_warning << "Missing size for GOG Galaxy file part " << file.destination; + remaining_parts = 0; + } else { + + remaining_parts--; + + setup::data_entry & data = info.data_entries[file.location]; + + // Ignore file part MD5 checksum, setup already contains a better one for the deflated data + + try { + boost::uint64_t compressed_size = boost::lexical_cast(part_info[1]); + if(data.file.size != compressed_size) { + log_warning << "Unexpected compressed size for GOG Galaxy file part " << file.destination + << ": " << compressed_size << " != " << data.file.size; + } + } catch(...) { + log_warning << "Could not parse compressed size for GOG Galaxy file part " << file.destination + << ": " << part_info[1]; + } + + try { + + // GOG Galaxy file parts are deflated, inflate them while extracting + data.uncompressed_size = boost::lexical_cast(part_info[2]); + data.file.filter = stream::ZlibFilter; + + file_start->size += data.uncompressed_size; + + if(&file != file_start) { + + // Ignore this file entry and instead add the data location to the start file + file.destination.clear(); + file_start->additional_locations.push_back(file.location); + + if(file.components != file_start->components || file.tasks != file_start->tasks + || file.languages != file_start->languages || file.check != file_start->check + || file.options != file_start->options) { + log_warning << "Mismatched constraints for different parts of GOG Galaxy file " + << file_start->destination << ": " << file.destination; + } + + } + + } catch(...) { + log_warning << "Could not parse size for GOG Galaxy file part " << file.destination + << ": " << part_info[1]; + remaining_parts = 0; + } + + } + } else if(!start_info.empty()) { + log_warning << "Missing part info for GOG Galaxy file " << file.destination; + remaining_parts = 0; + } else if(remaining_parts != 0) { + log_warning << "Incomplete GOG Galaxy file " << file_start->destination; + remaining_parts = 0; + } + + if(!file.destination.empty()) { + // languages, architectures, winversions + std::vector check = parse_function_call(file.check, "check_if_install"); + if(!check.empty() && !check[0].empty()) { + std::vector languages = parse_constraints(check[0]); + BOOST_FOREACH(const constraint & language, languages) { + all_languages.insert(language.name); + } + } + } + + has_language_constraints = has_language_constraints || !file.languages.empty(); + + } + + if(remaining_parts != 0) { + log_warning << "Incomplete GOG Galaxy file " << file_start->destination; + } + + /* + * GOG Galaxy multi-part files also have their own constraints, convert these to standard + * Inno Setup ones. + * + * Do this in a separate loop to not break constraint checks above. + */ + + BOOST_FOREACH(setup::file_entry & file, info.files) { + + if(file.destination.empty()) { + continue; + } + + // languages, architectures, winversions + std::vector check = parse_function_call(file.check, "check_if_install"); + if(!check.empty()) { + + if(!check[0].empty()) { + + std::vector languages = parse_constraints(check[0]); + + // Ignore constraints that just contain all languages + bool has_all_languages = false; + if(languages.size() >= all_languages.size() && all_languages.size() > 1) { + has_all_languages = true; + BOOST_FOREACH(const std::string & known_language, all_languages) { + bool has_language = false; + BOOST_FOREACH(const constraint & language, languages) { + if(!language.negated && language.name == known_language) { + has_language = true; + break; + } + } + if(!has_language) { + has_all_languages = false; + break; + } + } + } + + if(!languages.empty() && !has_all_languages) { + if(!file.languages.empty()) { + log_warning << "Overwriting language constraints for GOG Galaxy file " << file.destination; + } + file.languages = create_constraint_expression(languages); + } + + } + + if(check.size() >= 2 && !check[1].empty() && check[1] != "32#64#") { + log_warning << "Ignoring architecture constraint for GOG Galaxy file " << file.destination + << ": " << check[1]; + } + + if(check.size() >= 3 && !check[2].empty()) { + log_warning << "Ignoring OS constraint for GOG Galaxy file " << file.destination + << ": " << check[2]; + } + + if(file.components.empty()) { + file.components = "game"; + } + + } + + // component id, ? + std::vector dependency = parse_function_call(file.check, "check_if_install_dependency"); + if(!dependency.empty()) { + if(file.components.empty() && !dependency[0].empty()) { + file.components = dependency[0]; + } + } + + } + + if(!all_languages.empty()) { + if(!has_language_constraints) { + info.languages.clear(); + } + info.languages.reserve(all_languages.size()); + BOOST_FOREACH(const std::string & name, all_languages) { + setup::language_entry language; + language.name = name; + language.dialog_font_size = 0; + language.dialog_font_standard_height = 0; + language.title_font_size = 0; + language.welcome_font_size = 0; + language.copyright_font_size = 0; + language.right_to_left = false; + info.languages.push_back(language); + } + } + +} + +} // namespace gog diff -Nru innoextract-1.6/src/cli/goggalaxy.hpp innoextract-1.7/src/cli/goggalaxy.hpp --- innoextract-1.6/src/cli/goggalaxy.hpp 1970-01-01 00:00:00.000000000 +0000 +++ innoextract-1.7/src/cli/goggalaxy.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2018 Daniel Scharrer + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the author(s) be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/*! + * \file + * + * GOG.com-specific extensions. + */ +#ifndef INNOEXTRACT_CLI_GOGGALAXY_HPP +#define INNOEXTRACT_CLI_GOGGALAXY_HPP + +namespace setup { struct info; } + +namespace gog { + +/*! + * For some GOG installers, some application files are shipped in GOG Galaxy format: + * Thse files are split into one or more parts and then individually compressed. + * The parts are decompressed and reassembled by pre-/post-install scripts. + * This function parses the arguments to those scripts so that we can re-assemble them ourselves. + * + * The first part of a multi-part file has a before_install script that configures the output filename + * as well as the number of parts in the file and a checksum for the whole file. + * + * Each part (including the first) has an after_install script with a checksum for the decompressed + * part as well as compressed and decompressed sizes. + * + * Additionally, language constrained are also parsed from check scripts and added to the language list. + */ +void parse_galaxy_files(setup::info & info, bool force); + +} // namespace gog + +#endif // INNOEXTRACT_CLI_GOGGALAXY_HPP + diff -Nru innoextract-1.6/src/cli/gog.hpp innoextract-1.7/src/cli/gog.hpp --- innoextract-1.6/src/cli/gog.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/cli/gog.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2015 Daniel Scharrer + * Copyright (C) 2014-2018 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 @@ -40,8 +40,8 @@ //! \return the GOG.com game ID for this installer or an empty string std::string get_game_id(const setup::info & info); -void process_bin_files(const std::vector & files, - const extract_options & o, const setup::info & info); +void probe_bin_files(const extract_options & o, const setup::info & info, + const boost::filesystem::path & setup_file, bool external); } // namespace gog diff -Nru innoextract-1.6/src/cli/main.cpp innoextract-1.7/src/cli/main.cpp --- innoextract-1.6/src/cli/main.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/cli/main.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2016 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -38,6 +38,7 @@ #include "setup/version.hpp" #include "util/console.hpp" +#include "util/fstream.hpp" #include "util/log.hpp" #include "util/time.hpp" #include "util/windows.hpp" @@ -132,8 +133,14 @@ ("test,t", "Only verify checksums, don't write anything") ("extract,e", "Extract files (default action)") ("list,l", "Only list files, don't write anything") + ("list-sizes", "List file sizes") + ("list-checksums", "List file checksums") + ("info,i", "Print information about the installer") ("list-languages", "List languages supported by the installer") - ("gog-game-id", "Determine the GOG.com game ID for this installer") + ("gog-game-id", "Determine the installer's GOG.com game ID") + ("show-password", "Show password check information") + ("check-password", "Abort if the password is incorrect") + ("data-version,V", "Only print the data version") ; po::options_description modifiers("Modifiers"); @@ -144,7 +151,11 @@ ("lowercase,L", "Convert extracted filenames to lower-case") ("timestamps,T", po::value(), "Timezone for file times or \"local\" or \"none\"") ("output-dir,d", po::value(), "Extract files into the given directory") + ("password,P", po::value(), "Password for encrypted files") + ("password-file", po::value(), "File to load password from") ("gog,g", "Extract additional archives from GOG.com installers") + ("no-gog-galaxy", "Don't re-assemble GOG Galaxy file parts") + ("no-extract-unknown,n", "Don't extract unknown Inno Setup versions") ; po::options_description filter("Filters"); @@ -188,7 +199,7 @@ po::store(po::command_line_parser(argc, argv).options(options_desc).positional(p).run(), options); po::notify(options); - } catch(po::error & e) { + } catch(std::exception & e) { color::init(color::disable, color::disable); // Be conservative std::cerr << "Error parsing command-line: " << e.what() << "\n\n"; print_help(get_command(argv[0]), visible); @@ -239,26 +250,35 @@ } // Main action. - o.list = (options.count("list") != 0); + o.list_sizes = (options.count("list-sizes") != 0); + o.list_checksums = (options.count("list-checksums") != 0); + bool explicit_list = (options.count("list") != 0); + o.list = explicit_list || o.list_sizes || o.list_checksums; o.extract = (options.count("extract") != 0); o.test = (options.count("test") != 0); o.list_languages = (options.count("list-languages") != 0); o.gog_game_id = (options.count("gog-game-id") != 0); - bool explicit_action = o.list || o.test || o.extract - || o.list_languages || o.gog_game_id; + o.show_password = (options.count("show-password") != 0); + o.check_password = (options.count("check-password") != 0); + if(options.count("info") != 0) { + o.list_languages = true; + o.gog_game_id = true; + o.show_password = true; + } + bool explicit_action = o.list || o.test || o.extract || o.list_languages + || o.gog_game_id || o.show_password || o.check_password; if(!explicit_action) { o.extract = true; } - if(o.extract && o.test) { - log_error << "Combining --extract and --test is not allowed!"; - return ExitUserError; - } if(!o.extract && !o.test) { progress::set_enabled(false); } if(!o.silent && (o.test || o.extract)) { o.list = true; } + if(!o.quiet && explicit_list) { + o.list_sizes = true; + } // Additional actions. o.filenames.set_expand(options.count("dump") == 0); @@ -360,7 +380,59 @@ } } + { + po::variables_map::const_iterator password = options.find("password"); + po::variables_map::const_iterator password_file = options.find("password-file"); + if(password != options.end() && password_file != options.end()) { + log_error << "Combining --password and --password-file is not allowed"; + return ExitUserError; + } + if(password != options.end()) { + o.password = password->second.as(); + } + if(password_file != options.end()) { + std::istream * is = &std::cin; + fs::path file = password_file->second.as(); + util::ifstream ifs; + if(file != "-") { + ifs.open(file); + if(!ifs.is_open()) { + log_error << "Could not open password file " << file; + return ExitDataError; + } + is = &ifs; + } + std::getline(*is, o.password); + if(!o.password.empty() && o.password[o.password.size() - 1] == '\n') { + o.password.resize(o.password.size() - 1); + } + if(!o.password.empty() && o.password[o.password.size() - 1] == '\r') { + o.password.resize(o.password.size() - 1); + } + if(!*is) { + log_error << "Could not read password file " << file; + return ExitDataError; + } + } + if(o.check_password && o.password.empty()) { + log_error << "Combining --check-password requires a password"; + return ExitUserError; + } + } + o.gog = (options.count("gog") != 0); + o.gog_galaxy = (options.count("no-gog-galaxy") == 0); + + o.data_version = (options.count("data-version") != 0); + if(o.data_version) { + logger::quiet = true; + if(explicit_action) { + log_error << "Combining --data-version with other options is not allowed"; + return ExitUserError; + } + } + + o.extract_unknown = (options.count("no-extract-unknown") == 0); const std::vector & files = options["setup-files"] .as< std::vector >(); @@ -369,6 +441,9 @@ try { BOOST_FOREACH(const std::string & file, files) { process_file(file, o); + if(!o.data_version && files.size() > 1) { + std::cout << '\n'; + } } } catch(const std::ios_base::failure & e) { log_error << "Stream error while extracting files!\n" diff -Nru innoextract-1.6/src/configure.hpp.in innoextract-1.7/src/configure.hpp.in --- innoextract-1.6/src/configure.hpp.in 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/configure.hpp.in 2018-06-12 18:50:34.000000000 +0000 @@ -33,6 +33,7 @@ #cmakedefine01 INNOEXTRACT_HAVE_STD_UNIQUE_PTR // Optional dependencies +#cmakedefine01 INNOEXTRACT_HAVE_ARC4 #cmakedefine01 INNOEXTRACT_HAVE_LZMA #cmakedefine01 INNOEXTRACT_HAVE_ICONV #cmakedefine01 INNOEXTRACT_HAVE_WIN32_CONV diff -Nru innoextract-1.6/src/crypto/adler32.cpp innoextract-1.7/src/crypto/adler32.cpp --- innoextract-1.6/src/crypto/adler32.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/crypto/adler32.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -25,7 +25,7 @@ namespace crypto { -void adler32::update(const char * input, size_t length) { +void adler32::update(const char * data, size_t length) { const boost::uint_fast32_t base = 65521; @@ -35,7 +35,7 @@ if(length % 8 != 0) { do { - s1 += boost::uint8_t(*input++); + s1 += boost::uint8_t(*data++); s2 += s1; length--; } while(length % 8 != 0); @@ -49,17 +49,17 @@ while(length > 0) { - s1 += boost::uint8_t(input[0]), s2 += s1; - s1 += boost::uint8_t(input[1]), s2 += s1; - s1 += boost::uint8_t(input[2]), s2 += s1; - s1 += boost::uint8_t(input[3]), s2 += s1; - s1 += boost::uint8_t(input[4]), s2 += s1; - s1 += boost::uint8_t(input[5]), s2 += s1; - s1 += boost::uint8_t(input[6]), s2 += s1; - s1 += boost::uint8_t(input[7]), s2 += s1; + s1 += boost::uint8_t(data[0]), s2 += s1; + s1 += boost::uint8_t(data[1]), s2 += s1; + s1 += boost::uint8_t(data[2]), s2 += s1; + s1 += boost::uint8_t(data[3]), s2 += s1; + s1 += boost::uint8_t(data[4]), s2 += s1; + s1 += boost::uint8_t(data[5]), s2 += s1; + s1 += boost::uint8_t(data[6]), s2 += s1; + s1 += boost::uint8_t(data[7]), s2 += s1; length -= 8; - input += 8; + data += 8; if(s1 >= base) { s1 -= base; diff -Nru innoextract-1.6/src/crypto/arc4.cpp innoextract-1.7/src/crypto/arc4.cpp --- innoextract-1.6/src/crypto/arc4.cpp 1970-01-01 00:00:00.000000000 +0000 +++ innoextract-1.7/src/crypto/arc4.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2018 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 "crypto/arc4.hpp" + +#include + +#include "util/endian.hpp" + +namespace crypto { + +void arc4::init(const char * key, size_t length) { + + a = b = 0; + + for(size_t i = 0; i < sizeof(state); i++){ + state[i] = boost::uint8_t(i); + } + + size_t j = 0; + for(size_t i = 0; i < sizeof(state); i++) { + j = (j + state[i] + boost::uint8_t(key[i % length])) % sizeof(state); + std::swap(state[i], state[j]); + } + +} + +void arc4::update() { + + a = (a + 1) % sizeof(state); + b = (b + state[a]) % sizeof(state); + + std::swap(state[a], state[b]); + +} + +void arc4::discard(size_t length) { + + for(size_t i = 0; i < length; i++) { + update(); + } + +} + +void arc4::crypt(const char * in, char * out, size_t length) { + + for(size_t i = 0; i < length; i++) { + update(); + out[i] = char(state[size_t(state[a] + state[b]) % sizeof(state)] ^ boost::uint8_t(in[i])); + } + +} + +} // namespace crypto diff -Nru innoextract-1.6/src/crypto/arc4.hpp innoextract-1.7/src/crypto/arc4.hpp --- innoextract-1.6/src/crypto/arc4.hpp 1970-01-01 00:00:00.000000000 +0000 +++ innoextract-1.7/src/crypto/arc4.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 Daniel Scharrer + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the author(s) be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/*! + * \file + * + * Alledged RC4 en-/decryption routines. + */ +#ifndef INNOEXTRACT_CRYPTO_ARC4_HPP +#define INNOEXTRACT_CRYPTO_ARC4_HPP + +#include + +#include + +#include "configure.hpp" + +#if INNOEXTRACT_HAVE_ARC4 + +namespace crypto { + +//! Alledged RC4 en-/decryption calculation +struct arc4 { + + void init(const char * key, size_t length); + + void discard(size_t length); + + void crypt(const char * in, char * out, size_t length); + +private: + + void update(); + + boost::uint8_t state[256]; + size_t a, b; + +}; + +} // namespace crypto + +#endif // INNOEXTRACT_HAVE_ARC4 + +#endif // INNOEXTRACT_CRYPTO_ARC4_HPP + diff -Nru innoextract-1.6/src/crypto/checksum.cpp innoextract-1.7/src/crypto/checksum.cpp --- innoextract-1.6/src/crypto/checksum.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/crypto/checksum.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -29,17 +29,18 @@ namespace crypto { -bool checksum::operator==(const checksum & o) const { +bool checksum::operator==(const checksum & other) const { - if(o.type != type) { + if(other.type != type) { return false; } switch(type) { - case Adler32: return (adler32 == o.adler32); - case CRC32: return (crc32 == o.crc32); - case MD5: return !memcmp(md5, o.md5, sizeof(md5)); - case SHA1: return !memcmp(sha1, o.sha1, sizeof(sha1)); + case None: return true; + case Adler32: return (adler32 == other.adler32); + case CRC32: return (crc32 == other.crc32); + case MD5: return !memcmp(md5, other.md5, sizeof(md5)); + case SHA1: return !memcmp(sha1, other.sha1, sizeof(sha1)); default: return false; }; } @@ -47,10 +48,11 @@ } // namespace crypto NAMES(crypto::checksum_type, "Checksum Type", + "None", "Adler32", "CRC32", "MD5", - "Sha1", + "SHA-1", ) std::ostream & operator<<(std::ostream & os, const crypto::checksum & checksum) { @@ -60,6 +62,10 @@ os << checksum.type << ' '; switch(checksum.type) { + case crypto::None: { + os << "(no checksum)"; + break; + } case crypto::Adler32: { os << "0x" << std::hex << std::setw(8) << checksum.adler32; break; diff -Nru innoextract-1.6/src/crypto/checksum.hpp innoextract-1.7/src/crypto/checksum.hpp --- innoextract-1.6/src/crypto/checksum.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/crypto/checksum.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -39,6 +39,7 @@ namespace crypto { enum checksum_type { + None, Adler32, CRC32, MD5, diff -Nru innoextract-1.6/src/crypto/crc32.cpp innoextract-1.7/src/crypto/crc32.cpp --- innoextract-1.6/src/crypto/crc32.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/crypto/crc32.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -91,25 +91,26 @@ return crc >> 8; } -void crc32::update(const char * s, size_t n) { +void crc32::update(const char * data, size_t length) { - for(; (size_t(s) % 4 != 0) && n > 0; n--) { - crc = crc32_table[crc32_index(crc) ^ boost::uint8_t(*s++)] ^ crc32_shifted(crc); + for(; (size_t(data) % 4 != 0) && length > 0; length--) { + crc = crc32_table[crc32_index(crc) ^ boost::uint8_t(*data++)] ^ crc32_shifted(crc); } - while(n >= 4) { - crc ^= util::little_endian::load(s); + while(length >= 4) { + crc ^= util::little_endian::load(data); crc = crc32_table[crc32_index(crc)] ^ crc32_shifted(crc); crc = crc32_table[crc32_index(crc)] ^ crc32_shifted(crc); crc = crc32_table[crc32_index(crc)] ^ crc32_shifted(crc); crc = crc32_table[crc32_index(crc)] ^ crc32_shifted(crc); - n -= 4; - s += 4; + length -= 4; + data += 4; } - while(n--) { - crc = crc32_table[crc32_index(crc) ^ boost::uint8_t(*s++)] ^ crc32_shifted(crc); + while(length--) { + crc = crc32_table[crc32_index(crc) ^ boost::uint8_t(*data++)] ^ crc32_shifted(crc); } + } } // namespace crypto diff -Nru innoextract-1.6/src/crypto/hasher.cpp innoextract-1.7/src/crypto/hasher.cpp --- innoextract-1.6/src/crypto/hasher.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/crypto/hasher.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -22,36 +22,28 @@ namespace crypto { -hasher::hasher(const hasher & o) { - - type = o.type; - - switch(type) { - case crypto::Adler32: adler32 = o.adler32; break; - case crypto::CRC32: crc32 = o.crc32; break; - case crypto::MD5: md5 = o.md5; break; - case crypto::SHA1: sha1 = o.sha1; break; - }; -} - hasher::hasher(checksum_type type) : type(type) { switch(type) { + case crypto::None: break; case crypto::Adler32: adler32.init(); break; case crypto::CRC32: crc32.init(); break; case crypto::MD5: md5.init(); break; case crypto::SHA1: sha1.init(); break; }; + } void hasher::update(const char * data, size_t size) { switch(type) { + case crypto::None: break; case crypto::Adler32: adler32.update(data, size); break; case crypto::CRC32: crc32.update(data, size); break; case crypto::MD5: md5.update(data, size); break; case crypto::SHA1: sha1.update(data, size); break; }; + } checksum hasher::finalize() { @@ -61,6 +53,7 @@ result.type = type; switch(type) { + case crypto::None: break; case crypto::Adler32: result.adler32 = adler32.finalize(); break; case crypto::CRC32: result.crc32 = crc32.finalize(); break; case crypto::MD5: md5.finalize(result.md5); break; diff -Nru innoextract-1.6/src/crypto/hasher.hpp innoextract-1.7/src/crypto/hasher.hpp --- innoextract-1.6/src/crypto/hasher.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/crypto/hasher.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -42,7 +42,6 @@ public: explicit hasher(checksum_type type); - hasher(const hasher & o); void update(const char * data, size_t size); diff -Nru innoextract-1.6/src/crypto/iteratedhash.hpp innoextract-1.7/src/crypto/iteratedhash.hpp --- innoextract-1.6/src/crypto/iteratedhash.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/crypto/iteratedhash.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -68,7 +68,7 @@ } hash_word bit_count_lo() const { return count_lo << 3; } - char data[block_size]; + char buffer[block_size]; hash_word state[hash_size]; hash_word count_lo, count_hi; @@ -76,40 +76,40 @@ }; template -void iterated_hash::update(const char * input, size_t len) { +void iterated_hash::update(const char * data, size_t length) { hash_word old_count_lo = count_lo; - if((count_lo = old_count_lo + hash_word(len)) < old_count_lo) { + if((count_lo = old_count_lo + hash_word(length)) < old_count_lo) { count_hi++; // carry from low to high } - count_hi += hash_word(util::safe_right_shift<8 * sizeof(hash_word)>(len)); + count_hi += hash_word(util::safe_right_shift<8 * sizeof(hash_word)>(length)); size_t num = util::mod_power_of_2(old_count_lo, size_t(block_size)); if(num != 0) { // process left over data - if(num + len >= block_size) { - std::memcpy(data + num, input, block_size-num); - hash(data, block_size); - input += (block_size - num); - len -= (block_size - num); + if(num + length >= block_size) { + std::memcpy(buffer + num, data, block_size-num); + hash(buffer, block_size); + data += (block_size - num); + length -= (block_size - num); // drop through and do the rest } else { - std::memcpy(data + num, input, len); + std::memcpy(buffer + num, data, length); return; } } // now process the input data in blocks of BlockSize bytes and save the leftovers to m_data - if(len >= block_size) { - size_t leftOver = hash(input, len); - input += (len - leftOver); - len = leftOver; + if(length >= block_size) { + size_t left_over = hash(data, length); + data += (length - left_over); + length = left_over; } - if(len) { - memcpy(data, input, len); + if(length) { + memcpy(buffer, data, length); } } @@ -151,30 +151,30 @@ size_t num = util::mod_power_of_2(count_lo, size_t(block_size)); - data[num++] = pad_first; + buffer[num++] = pad_first; if(num <= last_block_size) { - memset(data + num, 0, last_block_size - num); + memset(buffer + num, 0, last_block_size - num); } else { - memset(data + num, 0, block_size - num); - hash(data, block_size); - memset(data, 0, last_block_size); + memset(buffer + num, 0, block_size - num); + hash(buffer, block_size); + memset(buffer, 0, last_block_size); } } template -void iterated_hash::finalize(char * digest) { +void iterated_hash::finalize(char * result) { size_t order = transform::offset * sizeof(hash_word); size_t size = block_size - 2 * sizeof(hash_word); pad(size); - byte_order::store(bit_count_lo(), data + size + order); - byte_order::store(bit_count_hi(), data + size + sizeof(hash_word) - order); + byte_order::store(bit_count_lo(), buffer + size + order); + byte_order::store(bit_count_hi(), buffer + size + sizeof(hash_word) - order); - hash(data, block_size); + hash(buffer, block_size); - byte_order::store(state, hash_size, digest); + byte_order::store(state, hash_size, result); } diff -Nru innoextract-1.6/src/crypto/md5.cpp innoextract-1.7/src/crypto/md5.cpp --- innoextract-1.6/src/crypto/md5.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/crypto/md5.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -35,101 +35,101 @@ state[3] = 0x10325476L; } -void md5_transform::transform(hash_word * digest, const hash_word * in) { +void md5_transform::transform(hash_word * state, const hash_word * data) { -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) + #define F1(x, y, z) (z ^ (x & (y ^ z))) + #define F2(x, y, z) F1(z, x, y) + #define F3(x, y, z) (x ^ y ^ z) + #define F4(x, y, z) (y ^ (x | ~z)) -#define MD5STEP(f, w, x, y, z, data, s) \ - w = util::rotl_fixed(w + f(x, y, z) + data, s) + x + #define MD5STEP(f, w, x, y, z, word, s) \ + w = util::rotl_fixed(w + f(x, y, z) + word, s) + x hash_word a, b, c, d; - a = digest[0]; - b = digest[1]; - c = digest[2]; - d = digest[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - digest[0] += a; - digest[1] += b; - digest[2] += c; - digest[3] += d; - -#undef MD5STEP -#undef F4 -#undef F3 -#undef F2 -#undef F1 + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + MD5STEP(F1, a, b, c, d, data[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, data[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, data[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, data[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, data[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, data[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, data[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, data[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, data[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, data[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, data[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, data[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, data[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, data[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, data[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, data[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, data[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, data[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, data[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, data[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, data[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, data[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, data[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, data[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, data[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, data[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, data[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, data[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, data[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, data[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, data[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, data[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, data[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, data[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, data[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, data[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, data[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, data[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, data[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, data[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, data[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, data[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, data[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, data[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, data[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, data[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, data[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, data[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, data[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, data[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, data[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, data[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, data[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, data[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, data[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, data[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, data[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, data[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, data[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, data[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, data[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, data[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, data[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, data[9] + 0xeb86d391, 21); + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + #undef MD5STEP + #undef F4 + #undef F3 + #undef F2 + #undef F1 } diff -Nru innoextract-1.6/src/crypto/md5.hpp innoextract-1.7/src/crypto/md5.hpp --- innoextract-1.6/src/crypto/md5.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/crypto/md5.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -45,7 +45,8 @@ static void init(hash_word * state); - static void transform(hash_word * digest, const hash_word * data); + static void transform(hash_word * state, const hash_word * data); + }; typedef iterated_hash md5; diff -Nru innoextract-1.6/src/crypto/sha1.cpp innoextract-1.7/src/crypto/sha1.cpp --- innoextract-1.6/src/crypto/sha1.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/crypto/sha1.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -37,31 +37,31 @@ void sha1_transform::transform(hash_word * state, const hash_word * data) { -#define blk0(i) (W[i] = data[i]) -#define blk1(i) (W[i & 15] = util::rotl_fixed(W[(i + 13) & 15] ^ W[(i + 8) & 15] \ - ^ W[(i + 2) & 15] ^ W[i & 15], 1)) - -#define f1(x, y, z) (z ^ (x & (y ^ z))) -#define f2(x, y, z) (x ^ y ^ z) -#define f3(x, y, z) ((x & y) | (z & (x | y))) -#define f4(x, y, z) (x ^ y ^ z) - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v, w, x, y, z, i) \ - z += f1(w, x, y) + blk0(i) + 0x5A827999 + util::rotl_fixed(v, 5); \ - w = util::rotl_fixed(w, 30); -#define R1(v, w, x, y, z, i) \ - z += f1(w, x, y) + blk1(i) + 0x5A827999 + util::rotl_fixed(v, 5); \ - w = util::rotl_fixed(w, 30); -#define R2(v, w, x, y, z, i) \ - z += f2(w, x, y) + blk1(i) + 0x6ED9EBA1 + util::rotl_fixed(v, 5); \ - w = util::rotl_fixed(w, 30); -#define R3(v, w, x, y, z, i) \ - z += f3(w, x, y) + blk1(i) + 0x8F1BBCDC + util::rotl_fixed(v, 5); \ - w = util::rotl_fixed(w, 30); -#define R4(v, w, x, y, z, i) \ - z += f4(w, x, y) + blk1(i) + 0xCA62C1D6 + util::rotl_fixed(v, 5); \ - w = util::rotl_fixed(w, 30); + #define blk0(i) (W[i] = data[i]) + #define blk1(i) (W[i & 15] = util::rotl_fixed(W[(i + 13) & 15] ^ W[(i + 8) & 15] \ + ^ W[(i + 2) & 15] ^ W[i & 15], 1)) + + #define f1(x, y, z) (z ^ (x & (y ^ z))) + #define f2(x, y, z) (x ^ y ^ z) + #define f3(x, y, z) ((x & y) | (z & (x | y))) + #define f4(x, y, z) (x ^ y ^ z) + + /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ + #define R0(v, w, x, y, z, i) \ + z += f1(w, x, y) + blk0(i) + 0x5A827999 + util::rotl_fixed(v, 5); \ + w = util::rotl_fixed(w, 30); + #define R1(v, w, x, y, z, i) \ + z += f1(w, x, y) + blk1(i) + 0x5A827999 + util::rotl_fixed(v, 5); \ + w = util::rotl_fixed(w, 30); + #define R2(v, w, x, y, z, i) \ + z += f2(w, x, y) + blk1(i) + 0x6ED9EBA1 + util::rotl_fixed(v, 5); \ + w = util::rotl_fixed(w, 30); + #define R3(v, w, x, y, z, i) \ + z += f3(w, x, y) + blk1(i) + 0x8F1BBCDC + util::rotl_fixed(v, 5); \ + w = util::rotl_fixed(w, 30); + #define R4(v, w, x, y, z, i) \ + z += f4(w, x, y) + blk1(i) + 0xCA62C1D6 + util::rotl_fixed(v, 5); \ + w = util::rotl_fixed(w, 30); hash_word W[16]; @@ -181,19 +181,19 @@ state[3] += d; state[4] += e; -#undef R4 -#undef R3 -#undef R2 -#undef R1 -#undef R0 - -#undef f4 -#undef f3 -#undef f2 -#undef f1 + #undef R4 + #undef R3 + #undef R2 + #undef R1 + #undef R0 + + #undef f4 + #undef f3 + #undef f2 + #undef f1 -#undef blk1 -#undef blk0 + #undef blk1 + #undef blk0 } diff -Nru innoextract-1.6/src/crypto/sha1.hpp innoextract-1.7/src/crypto/sha1.hpp --- innoextract-1.6/src/crypto/sha1.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/crypto/sha1.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -45,7 +45,7 @@ static void init(hash_word * state); - static void transform(hash_word * digest, const hash_word * data); + static void transform(hash_word * state, const hash_word * data); }; typedef iterated_hash sha1; diff -Nru innoextract-1.6/src/loader/exereader.cpp innoextract-1.7/src/loader/exereader.cpp --- innoextract-1.6/src/loader/exereader.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/loader/exereader.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -45,7 +45,7 @@ PEMagic2 = 0x0000, // "\0\0" }; -static BinaryType determine_binary_type(std::istream & is) { +BinaryType determine_binary_type(std::istream & is) { boost::uint16_t dos_magic = util::load(is.seekg(0)); if(is.fail() || dos_magic != DOSMagic) { @@ -365,7 +365,7 @@ return is_table; } -boost::uint32_t pe_reader::find_resource_entry(std::istream & is, boost::uint32_t needle) { +boost::uint32_t pe_reader::find_resource_entry(std::istream & is, boost::uint32_t id) { // skip: characteristics + timestamp + major version + minor version if(is.seekg(4 + 4 + 2 + 2, std::ios_base::cur).fail()) { @@ -378,7 +378,7 @@ // Number of id resource entries. boost::uint16_t nbids = util::load(is); - if(needle == Default) { + if(id == Default) { boost::uint32_t offset = util::load(is.seekg(4, std::ios_base::cur)); return is.fail() ? 0 : offset; } @@ -391,14 +391,14 @@ for(size_t i = 0; i < nbids; i++) { - boost::uint32_t id = util::load(is); - boost::uint32_t offset = util::load(is); + boost::uint32_t entry_id = util::load(is); + boost::uint32_t entry_offset = util::load(is); if(is.fail()) { return 0; } - if(id == needle) { - return offset; + if(entry_id == id) { + return entry_offset; } } diff -Nru innoextract-1.6/src/loader/offsets.cpp innoextract-1.7/src/loader/offsets.cpp --- innoextract-1.6/src/loader/offsets.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/loader/offsets.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -47,7 +47,7 @@ }; -static const setup_loader_version known_setup_loader_versions[] = { +const setup_loader_version known_setup_loader_versions[] = { { { 'r', 'D', 'l', 'P', 't', 'S', '0', '2', 0x87, 'e', 'V', 'x' }, INNO_VERSION(1, 2, 10) }, { { 'r', 'D', 'l', 'P', 't', 'S', '0', '4', 0x87, 'e', 'V', 'x' }, INNO_VERSION(4, 0, 0) }, { { 'r', 'D', 'l', 'P', 't', 'S', '0', '5', 0x87, 'e', 'V', 'x' }, INNO_VERSION(4, 0, 3) }, diff -Nru innoextract-1.6/src/release.cpp.in innoextract-1.7/src/release.cpp.in --- innoextract-1.6/src/release.cpp.in 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/release.cpp.in 2018-06-12 18:50:34.000000000 +0000 @@ -38,7 +38,7 @@ const char innoextract_name[] = "${VERSION_0_NAME}"; -const char innoextract_version[] = "${VERSION_0_NUMBER}${GIT_SUFFIX_5}"; +const char innoextract_version[] = "${VERSION_0_NUMBER}${GIT_SUFFIX_7}"; const char innosetup_versions[] = "${VERSION_2}"; diff -Nru innoextract-1.6/src/setup/data.cpp innoextract-1.7/src/setup/data.cpp --- innoextract-1.6/src/setup/data.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/data.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -20,6 +20,8 @@ #include "setup/data.hpp" +#include + #include "setup/version.hpp" #include "util/load.hpp" #include "util/log.hpp" @@ -57,6 +59,7 @@ file.size = util::load(is); chunk.size = util::load(is); } + uncompressed_size = file.size; if(version >= INNO_VERSION(5, 3, 9)) { is.read(file.checksum.sha1, std::streamsize(sizeof(file.checksum.sha1))); @@ -80,6 +83,7 @@ boost::uint16_t date = util::load(is); struct tm t; + std::memset(&t, 0, sizeof(t)); t.tm_sec = util::get_bits(time, 0, 4) * 2; // [0, 58] t.tm_min = util::get_bits(time, 5, 10); // [0, 59] t.tm_hour = util::get_bits(time, 11, 15); // [0, 23] @@ -144,6 +148,11 @@ if(version >= INNO_VERSION(5, 1, 13)) { flagreader.add(SolidBreak); } + if(version >= INNO_VERSION(5, 5, 7)) { + // Actually added in Inno Setup 5.5.9 but the data version was not bumped + flagreader.add(Sign); + flagreader.add(SignOnce); + } options |= flagreader; @@ -157,7 +166,15 @@ chunk.compression = stream::BZip2; } - chunk.encrypted = ((options & ChunkEncrypted) != 0); + if(options & ChunkEncrypted) { + if(version >= INNO_VERSION(5, 3, 9)) { + chunk.encryption = stream::ARC4_SHA1; + } else { + chunk.encryption = stream::ARC4_MD5; + } + } else { + chunk.encryption = stream::Plaintext; + } if(options & CallInstructionOptimized) { if(version < INNO_VERSION(5, 2, 0)) { @@ -172,7 +189,7 @@ } } -} // namespace setup; +} // namespace setup NAMES(setup::data_entry::flags, "File Location Option", "version info valid", @@ -184,5 +201,7 @@ "chunk encrypted", "chunk compressed", "solid break", + "sign", + "sign once", "bzipped", ) diff -Nru innoextract-1.6/src/setup/data.hpp innoextract-1.7/src/setup/data.hpp --- innoextract-1.6/src/setup/data.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/data.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -54,6 +54,8 @@ ChunkEncrypted, ChunkCompressed, SolidBreak, + Sign, + SignOnce, // obsolete: BZipped @@ -63,6 +65,8 @@ stream::file file; + boost::uint64_t uncompressed_size; + boost::int64_t timestamp; boost::uint32_t timestamp_nsec; diff -Nru innoextract-1.6/src/setup/expression.cpp innoextract-1.7/src/setup/expression.cpp --- innoextract-1.6/src/setup/expression.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/expression.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Daniel Scharrer + * Copyright (C) 2012-2018 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 @@ -31,11 +31,11 @@ namespace { -static bool is_identifier_start(char c) { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; +bool is_identifier_start(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-'; } -static bool is_identifier(char c) { +bool is_identifier(char c) { return is_identifier_start(c) || (c >= '0' && c <= '9') || c == '\\'; } @@ -150,11 +150,11 @@ } // anonymous namespace -bool expression_match(const std::string & test, const std::string & expr) { +bool expression_match(const std::string & test, const std::string & expression) { try { - return evaluator(expr, test).eval(); + return evaluator(expression, test).eval(); } catch(const std::runtime_error & error) { - log_warning << "Error evaluating \"" << expr << "\": " << error.what(); + log_warning << "Error evaluating \"" << expression << "\": " << error.what(); return true; } } diff -Nru innoextract-1.6/src/setup/file.cpp innoextract-1.7/src/setup/file.cpp --- innoextract-1.6/src/setup/file.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/file.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -191,6 +191,11 @@ } else { type = stored_enum(is).get(); } + + additional_locations.clear(); + checksum.type = crypto::None; + size = 0; + } } // namespace setup diff -Nru innoextract-1.6/src/setup/file.hpp innoextract-1.7/src/setup/file.hpp --- innoextract-1.6/src/setup/file.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/file.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -28,9 +28,11 @@ #include #include +#include #include +#include "crypto/checksum.hpp" #include "setup/item.hpp" #include "util/enum.hpp" #include "util/flags.hpp" @@ -105,6 +107,12 @@ file_type type; + // Information about GOG Galaxy multi-part files + // These are not used in normal Inno Setup installers + std::vector additional_locations; + crypto::checksum checksum; + boost::uint64_t size; + void load(std::istream & is, const version & version); }; diff -Nru innoextract-1.6/src/setup/filename.cpp innoextract-1.7/src/setup/filename.cpp --- innoextract-1.6/src/setup/filename.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/filename.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2016 Daniel Scharrer + * Copyright (C) 2012-2018 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 @@ -48,12 +48,12 @@ case '|': return true; case '?': return true; case '*': return true; + default: return false; } - return false; } }; -static std::string replace_unsafe_chars(const std::string & str) { +std::string replace_unsafe_chars(const std::string & str) { std::string result; result.resize(str.size()); std::replace_copy_if(str.begin(), str.end(), result.begin(), is_unsafe_path_char(), '$'); @@ -160,20 +160,20 @@ return result; } -std::string filename_map::convert(std::string input) const { +std::string filename_map::convert(std::string path) const { // Convert paths to lower-case if requested if(lowercase) { - std::transform(input.begin(), input.end(), input.begin(), ::tolower); + std::transform(path.begin(), path.end(), path.begin(), ::tolower); } // Don't expand variables if requested if(!expand) { - return input; + return path; } - it begin = input.begin(); - std::string expanded = expand_variables(begin, input.end()); + it begin = path.begin(); + std::string expanded = expand_variables(begin, path.end()); return shorten_path(expanded); } diff -Nru innoextract-1.6/src/setup/header.cpp innoextract-1.7/src/setup/header.cpp --- innoextract-1.6/src/setup/header.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/header.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2016 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -90,13 +90,21 @@ header::NoLanguageDetection ); -STORED_FLAGS_MAP(stored_architectures, +STORED_FLAGS_MAP(stored_architectures_0, header::ArchitectureUnknown, header::X86, header::Amd64, header::IA64 ); +STORED_FLAGS_MAP(stored_architectures_1, + header::ArchitectureUnknown, + header::X86, + header::Amd64, + header::IA64, + header::ARM64, +); + // pre-4.2.5 STORED_ENUM_MAP(stored_compression_method_0, stream::UnknownCompression, stream::Zlib, @@ -332,9 +340,11 @@ password.type = crypto::SHA1; } if(version >= INNO_VERSION(4, 2, 2)) { - is.read(password_salt, std::streamsize(sizeof(password_salt))); + password_salt.resize(8); + is.read(&password_salt[0], std::streamsize(password_salt.length())); + password_salt.insert(0, "PasswordCheckHash"); } else { - std::memset(password_salt, 0, sizeof(password_salt)); + password_salt.clear(); } if(version >= INNO_VERSION(4, 0, 0)) { @@ -399,9 +409,12 @@ compression = stored_enum(is).get(); } - if(version >= INNO_VERSION(5, 1, 0)) { - architectures_allowed = stored_flags(is).get(); - architectures_installed_in_64bit_mode = stored_flags(is).get(); + if(version >= INNO_VERSION(5, 6, 0)) { + architectures_allowed = stored_flags(is).get(); + architectures_installed_in_64bit_mode = stored_flags(is).get(); + } else if(version >= INNO_VERSION(5, 1, 0)) { + architectures_allowed = stored_flags(is).get(); + architectures_installed_in_64bit_mode = stored_flags(is).get(); } else { architectures_allowed = architecture_types::all(); architectures_installed_in_64bit_mode = architecture_types::all(); @@ -692,6 +705,7 @@ "x86", "amd64", "IA64", + "ARM64", ) NAMES(setup::header::alpha_format, "Alpha Format", diff -Nru innoextract-1.6/src/setup/header.hpp innoextract-1.7/src/setup/header.hpp --- innoextract-1.6/src/setup/header.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/header.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2016 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -121,7 +121,8 @@ ArchitectureUnknown, X86, Amd64, - IA64 + IA64, + ARM64 ); std::string app_name; @@ -194,7 +195,7 @@ alpha_format image_alpha_format; crypto::checksum password; - salt password_salt; + std::string password_salt; boost::int64_t extra_disk_space_required; size_t slices_per_disk; diff -Nru innoextract-1.6/src/setup/info.cpp innoextract-1.7/src/setup/info.cpp --- innoextract-1.6/src/setup/info.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/info.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -22,6 +22,7 @@ #include #include +#include #include @@ -51,19 +52,19 @@ struct no_arg { }; template -static void load_entry(std::istream & is, const setup::version & version, +void load_entry(std::istream & is, const setup::version & version, Entry & entity, Arg arg) { entity.load(is, version, arg); } template -static void load_entry(std::istream & is, const setup::version & version, +void load_entry(std::istream & is, const setup::version & version, Entry & entity, no_arg arg) { (void)arg; entity.load(is, version); } template -static void load_entries(std::istream & is, const setup::version & version, +void load_entries(std::istream & is, const setup::version & version, info::entry_types entry_types, size_t count, std::vector & entries, info::entry_types::enum_type entry_type, Arg arg = Arg()) { @@ -84,32 +85,49 @@ } template -static void load_entries(std::istream & is, const setup::version & version, +void load_entries(std::istream & is, const setup::version & version, info::entry_types entry_types, size_t count, std::vector & entries, info::entry_types::enum_type entry_type) { load_entries(is, version, entry_types, count, entries, entry_type); } -static void load_wizard_and_decompressor(std::istream & is, const setup::version & version, - const setup::header & header, - setup::info & info, info::entry_types entries) { +void load_wizard_images(std::istream & is, const setup::version & version, + std::vector & images, info::entry_types entries) { - (void)entries; + size_t count = 1; + if(version >= INNO_VERSION(5, 6, 0)) { + count = util::load(is); + } - info.wizard_image.clear(); - info.wizard_image_small.clear(); if(entries & (info::WizardImages | info::NoSkip)) { - is >> util::binary_string(info.wizard_image); - if(version >= INNO_VERSION(2, 0, 0)) { - is >> util::binary_string(info.wizard_image_small); + images.resize(count); + for(size_t i = 0; i < count; i++) { + is >> util::binary_string(images[i]); + } + if(version < INNO_VERSION(5, 6, 0) && images[0].empty()) { + images.clear(); } } else { - util::binary_string::skip(is); - if(version >= INNO_VERSION(2, 0, 0)) { + for(size_t i = 0; i < count; i++) { util::binary_string::skip(is); } } +} + +void load_wizard_and_decompressor(std::istream & is, const setup::version & version, + const setup::header & header, + setup::info & info, info::entry_types entries) { + + info.wizard_images.clear(); + info.wizard_images_small.clear(); + + load_wizard_images(is, version, info.wizard_images, entries); + + if(version >= INNO_VERSION(2, 0, 0)) { + load_wizard_images(is, version, info.wizard_images_small, entries); + } + info.decompressor_dll.clear(); if(header.compression == stream::BZip2 || (header.compression == stream::LZMA1 && version == INNO_VERSION(4, 1, 5)) @@ -134,9 +152,7 @@ } -} // anonymous namespace - -static void check_is_end(stream::block_reader::pointer & is, const char * what) { +void check_is_end(stream::block_reader::pointer & is, const char * what) { is->exceptions(std::ios_base::goodbit); char dummy; if(!is->get(dummy).eof()) { @@ -144,50 +160,52 @@ } } -void info::load(std::istream & ifs, entry_types e, const setup::version & v) { +} // anonymous namespace + +void info::load(std::istream & is, entry_types entries, const setup::version & version) { - if(e & (Messages | NoSkip)) { - e |= Languages; + if(entries & (Messages | NoSkip)) { + entries |= Languages; } - stream::block_reader::pointer is = stream::block_reader::get(ifs, v); + stream::block_reader::pointer reader = stream::block_reader::get(is, version); - header.load(*is, v); + header.load(*reader, version); - load_entries(*is, v, e, header.language_count, languages, Languages); + load_entries(*reader, version, entries, header.language_count, languages, Languages); - if(v < INNO_VERSION(4, 0, 0)) { - load_wizard_and_decompressor(*is, v, header, *this, e); + if(version < INNO_VERSION(4, 0, 0)) { + load_wizard_and_decompressor(*reader, version, header, *this, entries); } - load_entries(*is, v, e, header.message_count, messages, Messages, languages); - load_entries(*is, v, e, header.permission_count, permissions, Permissions); - load_entries(*is, v, e, header.type_count, types, Types); - load_entries(*is, v, e, header.component_count, components, Components); - load_entries(*is, v, e, header.task_count, tasks, Tasks); - load_entries(*is, v, e, header.directory_count, directories, Directories); - load_entries(*is, v, e, header.file_count, files, Files); - load_entries(*is, v, e, header.icon_count, icons, Icons); - load_entries(*is, v, e, header.ini_entry_count, ini_entries, IniEntries); - load_entries(*is, v, e, header.registry_entry_count, registry_entries, RegistryEntries); - load_entries(*is, v, e, header.delete_entry_count, delete_entries, DeleteEntries); - load_entries(*is, v, e, header.uninstall_delete_entry_count, uninstall_delete_entries, + load_entries(*reader, version, entries, header.message_count, messages, Messages, languages); + load_entries(*reader, version, entries, header.permission_count, permissions, Permissions); + load_entries(*reader, version, entries, header.type_count, types, Types); + load_entries(*reader, version, entries, header.component_count, components, Components); + load_entries(*reader, version, entries, header.task_count, tasks, Tasks); + load_entries(*reader, version, entries, header.directory_count, directories, Directories); + load_entries(*reader, version, entries, header.file_count, files, Files); + load_entries(*reader, version, entries, header.icon_count, icons, Icons); + load_entries(*reader, version, entries, header.ini_entry_count, ini_entries, IniEntries); + load_entries(*reader, version, entries, header.registry_entry_count, registry_entries, RegistryEntries); + load_entries(*reader, version, entries, header.delete_entry_count, delete_entries, DeleteEntries); + load_entries(*reader, version, entries, header.uninstall_delete_entry_count, uninstall_delete_entries, UninstallDeleteEntries); - load_entries(*is, v, e, header.run_entry_count, run_entries, RunEntries); - load_entries(*is, v, e, header.uninstall_run_entry_count, uninstall_run_entries, + load_entries(*reader, version, entries, header.run_entry_count, run_entries, RunEntries); + load_entries(*reader, version, entries, header.uninstall_run_entry_count, uninstall_run_entries, UninstallRunEntries); - if(v >= INNO_VERSION(4, 0, 0)) { - load_wizard_and_decompressor(*is, v, header, *this, e); + if(version >= INNO_VERSION(4, 0, 0)) { + load_wizard_and_decompressor(*reader, version, header, *this, entries); } // restart the compression stream - check_is_end(is, "unknown data at end of primary header stream"); - is = stream::block_reader::get(ifs, v); + check_is_end(reader, "unknown data at end of primary header stream"); + reader = stream::block_reader::get(is, version); - load_entries(*is, v, e, header.data_entry_count, data_entries, DataEntries); + load_entries(*reader, version, entries, header.data_entry_count, data_entries, DataEntries); - check_is_end(is, "unknown data at end of secondary header stream"); + check_is_end(reader, "unknown data at end of secondary header stream"); } void info::load(std::istream & is, entry_types entries) { @@ -195,6 +213,11 @@ version.load(is); if(!version.known) { + if(entries & NoUnknownVersion) { + std::ostringstream oss; + oss << "Unexpected setup data version: " << version; + throw std::runtime_error(oss.str()); + } log_warning << "Unexpected setup data version: " << color::white << version << color::reset; } @@ -209,7 +232,7 @@ entries |= NoSkip; } if(!version.known || ambiguous) { - std::ios_base::streampos start = is.tellg(); + std::streampos start = is.tellg(); try { load(is, entries, version); return; diff -Nru innoextract-1.6/src/setup/info.hpp innoextract-1.7/src/setup/info.hpp --- innoextract-1.6/src/setup/info.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/info.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -55,6 +55,7 @@ */ struct info { + // Explicit constructor/destructor required to allow forward-declaring entry types info(); ~info(); @@ -78,7 +79,8 @@ WizardImages, DecompressorDll, DecryptDll, - NoSkip + NoSkip, + NoUnknownVersion ); setup::version version; @@ -104,8 +106,8 @@ //! Images displayed in the installer UI. //! Loading enabled by \c WizardImages - std::string wizard_image; - std::string wizard_image_small; + std::vector wizard_images; + std::vector wizard_images_small; //! Contents of the helper DLL used to decompress setup data in some versions. //! Loading enabled by \c DecompressorDll diff -Nru innoextract-1.6/src/setup/permission.cpp innoextract-1.7/src/setup/permission.cpp --- innoextract-1.6/src/setup/permission.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/permission.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -25,9 +25,9 @@ namespace setup { -void permission_entry::load(std::istream & is, const version & v) { +void permission_entry::load(std::istream & is, const version & version) { - (void)v; + (void)version; is >> util::binary_string(permissions); // an array of TGrantPermissionEntry's diff -Nru innoextract-1.6/src/setup/version.cpp innoextract-1.7/src/setup/version.cpp --- innoextract-1.6/src/setup/version.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/version.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2016 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -53,7 +53,7 @@ }; -static const known_legacy_version legacy_versions[] = { +const known_legacy_version legacy_versions[] = { { "i1.2.10--16\x1a", INNO_VERSION(1, 2, 10), 16 }, { "i1.2.10--32\x1a", INNO_VERSION(1, 2, 10), 32 }, }; @@ -71,7 +71,7 @@ }; -static const known_version versions[] = { +const known_version versions[] = { { "Inno Setup Setup Data (1.3.21)", INNO_VERSION_EXT(1, 3, 21, 0), false }, { "Inno Setup Setup Data (1.3.25)", INNO_VERSION_EXT(1, 3, 25, 0), false }, { "Inno Setup Setup Data (2.0.0)", INNO_VERSION_EXT(2, 0, 0, 0), false }, @@ -151,23 +151,25 @@ { "Inno Setup Setup Data (5.5.6) (u)", INNO_VERSION_EXT(5, 5, 6, 0), true }, { "Inno Setup Setup Data (5.5.7)", INNO_VERSION_EXT(5, 5, 7, 0), false }, { "Inno Setup Setup Data (5.5.7) (u)", INNO_VERSION_EXT(5, 5, 7, 0), true }, + { "Inno Setup Setup Data (5.6.0)", INNO_VERSION_EXT(5, 6, 0, 0), false }, + { "Inno Setup Setup Data (5.6.0) (u)", INNO_VERSION_EXT(5, 6, 0, 0), true }, }; } // anonymous namespace -std::ostream & operator<<(std::ostream & os, const version & v) { +std::ostream & operator<<(std::ostream & os, const version & version) { - os << v.a() << '.' << v.b() << '.' << v.c(); - if(v.d()) { - os << '.' << v.d(); + os << version.a() << '.' << version.b() << '.' << version.c(); + if(version.d()) { + os << '.' << version.d(); } - if(v.unicode) { + if(version.unicode) { os << " (unicode)"; } - if(v.bits != 32) { - os << " (" << int(v.bits) << "-bit)"; + if(version.bits != 32) { + os << " (" << int(version.bits) << "-bit)"; } return os; @@ -218,7 +220,7 @@ unsigned b = util::to_unsigned(version_str.data() + 3, 1); unsigned c = util::to_unsigned(version_str.data() + 5, 2); value = INNO_VERSION(a, b, c); - } catch(boost::bad_lexical_cast) { + } catch(const boost::bad_lexical_cast &) { throw version_error(); } @@ -303,7 +305,7 @@ value = INNO_VERSION_EXT(a, b, c, d); break; - } catch(boost::bad_lexical_cast) { + } catch(const boost::bad_lexical_cast &) { continue; } } @@ -338,6 +340,11 @@ return true; } + if(value == INNO_VERSION(5, 5, 7)) { + // might be either 5.5.7 or 5.6.0 + return true; + } + return false; } diff -Nru innoextract-1.6/src/setup/windows.cpp innoextract-1.7/src/setup/windows.cpp --- innoextract-1.6/src/setup/windows.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/windows.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -95,7 +95,10 @@ { "Windows XP", { 5, 1, 2600 } }, { "Windows XP x64", { 5, 2, 3790 } }, { "Windows Vista", { 6, 0, 6000 } }, - { "Windows 7", { 6, 1, 7600 } } + { "Windows 7", { 6, 1, 7600 } }, + { "Windows 8", { 6, 2, 0 } }, + { "Windows 8.1", { 6, 3, 0 } }, + { "Windows 10", { 10, 0, 0 } }, }; const char * get_version_name(const windows_version::data & version, bool nt = false) { @@ -118,25 +121,25 @@ return NULL; } -} // nanonymous namespace +} // anonymous namespace -std::ostream & operator<<(std::ostream & os, const windows_version::data & v) { - os << v.major << '.' << v.minor; - if(v.build) { - os << v.build; +std::ostream & operator<<(std::ostream & os, const windows_version::data & version) { + os << version.major << '.' << version.minor; + if(version.build) { + os << version.build; } return os; } -std::ostream & operator<<(std::ostream & os, const windows_version & v) { +std::ostream & operator<<(std::ostream & os, const windows_version & version) { - os << v.win_version; - if(v.nt_version != v.win_version) { - os << " nt " << v.nt_version; + os << version.win_version; + if(version.nt_version != version.win_version) { + os << " nt " << version.nt_version; } - const char * win_name = get_version_name(v.win_version); - const char * nt_name = get_version_name(v.nt_version, true); + const char * win_name = get_version_name(version.win_version); + const char * nt_name = get_version_name(version.nt_version, true); if(win_name || nt_name) { os << " ("; @@ -152,10 +155,10 @@ os << ')'; } - if(v.nt_service_pack.major || v.nt_service_pack.minor) { - os << " service pack " << v.nt_service_pack.major; - if(v.nt_service_pack.minor) { - os << '.' << v.nt_service_pack.minor; + if(version.nt_service_pack.major || version.nt_service_pack.minor) { + os << " service pack " << version.nt_service_pack.major; + if(version.nt_service_pack.minor) { + os << '.' << version.nt_service_pack.minor; } } diff -Nru innoextract-1.6/src/setup/windows.hpp innoextract-1.7/src/setup/windows.hpp --- innoextract-1.6/src/setup/windows.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/setup/windows.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -97,8 +97,8 @@ }; -std::ostream & operator<<(std::ostream & os, const windows_version::data & svd); -std::ostream & operator<<(std::ostream & os, const windows_version & svd); +std::ostream & operator<<(std::ostream & os, const windows_version::data & version); +std::ostream & operator<<(std::ostream & os, const windows_version & version); } // namespace setup diff -Nru innoextract-1.6/src/stream/block.hpp innoextract-1.7/src/stream/block.hpp --- innoextract-1.6/src/stream/block.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/stream/block.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -41,7 +41,7 @@ //! Error thrown by \ref chunk_reader::get or the returned stream if there was a problem. struct block_error : public std::ios_base::failure { - explicit block_error(std::string msg) : std::ios_base::failure(msg) { } + explicit block_error(const std::string & msg) : std::ios_base::failure(msg) { } }; diff -Nru innoextract-1.6/src/stream/checksum.hpp innoextract-1.7/src/stream/checksum.hpp --- innoextract-1.6/src/stream/checksum.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/stream/checksum.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -59,7 +59,6 @@ : hasher(type) , output(output) { } - checksum_filter(const checksum_filter & o) : hasher(o.hasher), output(o.output) { } template std::streamsize read(Source & src, char * dest, std::streamsize n) { diff -Nru innoextract-1.6/src/stream/chunk.cpp innoextract-1.7/src/stream/chunk.cpp --- innoextract-1.6/src/stream/chunk.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/stream/chunk.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -20,22 +20,77 @@ #include "chunk.hpp" +#include + +#include +#include +#include #include #include #include #include #include "release.hpp" +#include "crypto/arc4.hpp" +#include "crypto/checksum.hpp" +#include "crypto/hasher.hpp" #include "stream/lzma.hpp" #include "stream/restrict.hpp" #include "stream/slice.hpp" #include "util/log.hpp" + namespace io = boost::iostreams; namespace stream { -static const char chunk_id[4] = { 'z', 'l', 'b', 0x1a }; +namespace { + +const char chunk_id[4] = { 'z', 'l', 'b', 0x1a }; + +#if INNOEXTRACT_HAVE_ARC4 + +/*! + * Filter to en-/decrypt files files stored by Inno Setup. + */ +class inno_arc4_crypter : public boost::iostreams::multichar_input_filter { + +private: + + typedef boost::iostreams::multichar_input_filter base_type; + +public: + + typedef base_type::char_type char_type; + typedef base_type::category category; + + inno_arc4_crypter(const char * key, size_t length) { + + arc4.init(key, length); + arc4.discard(1000); + + } + + template + std::streamsize read(Source & src, char * dest, std::streamsize n) { + + std::streamsize length = boost::iostreams::read(src, dest, n); + if(length != EOF) { + arc4.crypt(dest, dest, size_t(n)); + } + + return length; + } + +private: + + crypto::arc4 arc4; + +}; + +#endif // INNOEXTRACT_HAVE_ARC4 + +} // anonymous namespace bool chunk::operator<(const chunk & o) const { @@ -47,8 +102,8 @@ return (size < o.size); } else if(compression != o.compression) { return (compression < o.compression); - } else if(encrypted != o.encrypted) { - return (encrypted < o.encrypted); + } else if(encryption != o.encryption) { + return (encryption < o.encryption); } return false; @@ -59,17 +114,17 @@ && offset == o.offset && size == o.size && compression == o.compression - && encrypted == o.encrypted); + && encryption == o.encryption); } -chunk_reader::pointer chunk_reader::get(slice_reader & base, const chunk & chunk) { +chunk_reader::pointer chunk_reader::get(slice_reader & base, const chunk & chunk , const std::string & key) { if(!base.seek(chunk.first_slice, chunk.offset)) { throw chunk_error("could not seek to chunk start"); } char magic[sizeof(chunk_id)]; - if(base.read(magic, 4) != 4 || memcmp(magic, chunk_id, sizeof(chunk_id))) { + if(base.read(magic, 4) != 4 || std::memcmp(magic, chunk_id, sizeof(chunk_id)) != 0) { throw chunk_error("bad chunk magic"); } @@ -90,6 +145,25 @@ default: throw chunk_error("unknown chunk compression"); } + if(chunk.encryption != Plaintext) { + #if INNOEXTRACT_HAVE_ARC4 + char salt[8]; + if(base.read(salt, 8) != 8) { + throw chunk_error("could not read chunk salt"); + } + crypto::hasher hasher(chunk.encryption == ARC4_SHA1 ? crypto::SHA1 : crypto::MD5); + hasher.update(salt, sizeof(salt)); + hasher.update(key.c_str(), key.length()); + crypto::checksum checksum = hasher.finalize(); + const char * salted_key = chunk.encryption == ARC4_SHA1 ? checksum.sha1 : checksum.md5; + size_t key_length = chunk.encryption == ARC4_SHA1 ? sizeof(checksum.sha1) : sizeof(checksum.md5); + result->push(inno_arc4_crypter(salted_key, key_length), 8192); + #else + (void)key; + throw chunk_error("ARC4 decryption not supported"); + #endif + } + result->push(restrict(base, chunk.size)); return result; @@ -105,3 +179,9 @@ "lzma2", "unknown", ) + +NAMES(stream::encryption_method, "Encryption Method", + "plaintext", + "rc4 + md5", + "rc4 + sha1", +) diff -Nru innoextract-1.6/src/stream/chunk.hpp innoextract-1.7/src/stream/chunk.hpp --- innoextract-1.6/src/stream/chunk.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/stream/chunk.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -46,7 +47,7 @@ //! Error thrown by \ref chunk_reader::get if there was a problem. struct chunk_error : public std::ios_base::failure { - explicit chunk_error(std::string msg) : std::ios_base::failure(msg) { } + explicit chunk_error(const std::string & msg) : std::ios_base::failure(msg) { } }; @@ -60,6 +61,13 @@ UnknownCompression }; +//! Encryption methods supported by chunks. +enum encryption_method { + Plaintext, + ARC4_MD5, + ARC4_SHA1, +}; + /*! * Information specifying a compressed chunk. * @@ -76,7 +84,7 @@ boost::uint64_t size; //! Total compressed size of the chunk. compression_method compression; //!< Compression method used by the chunk. - bool encrypted; //!< Is the chunk encrypted? Unsupported for now. + encryption_method encryption; //!< Encryption method used by the chunk. bool operator<(const chunk & o) const; bool operator==(const chunk & o) const; @@ -103,13 +111,14 @@ * * \param base The slice reader for the setup file(s). * \param chunk Information specifying the chunk to read. + * \param key Key used for encrypted chunks. * * \throws chunk_error if the chunk header could not be read or was invalid, * or if the chunk compression is not supported by this build. * * \return a pointer to a non-seekable input filter chain for the requested file. */ - static pointer get(slice_reader & base, const ::stream::chunk & chunk); + static pointer get(slice_reader & base, const ::stream::chunk & chunk, const std::string & key); }; @@ -117,4 +126,6 @@ NAMED_ENUM(stream::compression_method) +NAMED_ENUM(stream::encryption_method) + #endif // INNOEXTRACT_STREAM_CHUNK_HPP diff -Nru innoextract-1.6/src/stream/exefilter.hpp innoextract-1.7/src/stream/exefilter.hpp --- innoextract-1.6/src/stream/exefilter.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/stream/exefilter.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -63,8 +63,8 @@ template - void close(const Source &) { - addr_bytes_left = 0, addr_offset = 5; + void close(const Source & /* source */) { + addr = 0, addr_bytes_left = 0, addr_offset = 5; } private: @@ -103,7 +103,7 @@ std::streamsize read(Source & src, char * dest, std::streamsize n); template - void close(const Source &) { + void close(const Source & /* source */) { offset = 0, flush_bytes = 0; } diff -Nru innoextract-1.6/src/stream/file.cpp innoextract-1.7/src/stream/file.cpp --- innoextract-1.6/src/stream/file.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/stream/file.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -21,6 +21,7 @@ #include "stream/file.hpp" #include +#include #include "stream/checksum.hpp" #include "stream/exefilter.hpp" @@ -55,6 +56,10 @@ util::unique_ptr::type result(new io::filtering_istream); + if(file.filter == ZlibFilter) { + result->push(io::zlib_decompressor(), 8192); + } + if(checksum) { result->push(stream::checksum_filter(checksum, file.checksum.type), 8192); } @@ -64,6 +69,7 @@ case InstructionFilter4108: result->push(stream::inno_exe_decoder_4108(), 8192); break; case InstructionFilter5200: result->push(stream::inno_exe_decoder_5200(false), 8192); break; case InstructionFilter5309: result->push(stream::inno_exe_decoder_5200(true), 8192); break; + case ZlibFilter: /* applied *after* calculating the checksum */ break; } result->push(stream::restrict(base, file.size)); diff -Nru innoextract-1.6/src/stream/file.hpp innoextract-1.7/src/stream/file.hpp --- innoextract-1.6/src/stream/file.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/stream/file.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -40,6 +40,7 @@ InstructionFilter4108, InstructionFilter5200, InstructionFilter5309, + ZlibFilter, }; /*! @@ -52,7 +53,7 @@ struct file { boost::uint64_t offset; //!< Offset of this file within the decompressed chunk. - boost::uint64_t size; //!< Decompressed size of this file. + boost::uint64_t size; //!< Pre-filter size of this file in the decompressed chunk. crypto::checksum checksum; //!< Checksum for the file. diff -Nru innoextract-1.6/src/stream/lzma.cpp innoextract-1.7/src/stream/lzma.cpp --- innoextract-1.6/src/stream/lzma.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/stream/lzma.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2016 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -35,10 +35,6 @@ options.preset_dict = NULL; - if(options.dict_size > (boost::uint32_t(1) << 28)) { - throw lzma_error("inno lzma dict size too large", LZMA_FORMAT_ERROR); - } - lzma_stream * strm = new lzma_stream; lzma_stream tmp = LZMA_STREAM_INIT; *strm = tmp; diff -Nru innoextract-1.6/src/stream/lzma.hpp innoextract-1.7/src/stream/lzma.hpp --- innoextract-1.6/src/stream/lzma.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/stream/lzma.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -41,7 +41,7 @@ //! Error thrown if there was en error in an LZMA stream struct lzma_error : public std::ios_base::failure { - lzma_error(std::string msg, int code) + lzma_error(const std::string & msg, int code) : std::ios_base::failure(msg), error_code(code) { } //! \return the liblzma code for the error. @@ -52,7 +52,7 @@ int error_code; }; -class lzma_decompressor_impl_base : public boost::noncopyable { +class lzma_decompressor_impl_base : private boost::noncopyable { public: diff -Nru innoextract-1.6/src/stream/slice.cpp innoextract-1.7/src/stream/slice.cpp --- innoextract-1.6/src/stream/slice.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/stream/slice.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -25,6 +25,8 @@ #include #include +#include +#include #include #include "util/console.hpp" @@ -44,9 +46,8 @@ slice_reader::slice_reader(std::istream * istream, boost::uint32_t data_offset) : data_offset(data_offset), - dir(), last_dir(), base_file(), slices_per_disk(1), - current_slice(0), slice_file(), slice_size(0), - ifs(), is(istream) { + slices_per_disk(1), current_slice(0), slice_size(0), + is(istream) { std::streampos max_size = std::streampos(std::numeric_limits::max()); @@ -58,12 +59,12 @@ } } -slice_reader::slice_reader(const path_type & dir, const std::string & base_file, - size_t slices_per_disk) +slice_reader::slice_reader(const path_type & dir, const std::string & basename, + const std::string & basename2, size_t slices_per_disk) : data_offset(0), - dir(dir), last_dir(dir), base_file(base_file), slices_per_disk(slices_per_disk), - current_slice(0), slice_file(), slice_size(0), - ifs(), is(&ifs) { } + dir(dir), base_file(basename), base_file2(basename2), + slices_per_disk(slices_per_disk), current_slice(0), slice_size(0), + is(&ifs) { } void slice_reader::seek(size_t slice) { @@ -80,6 +81,10 @@ bool slice_reader::open_file(const path_type & file) { + if(!boost::filesystem::exists(file)) { + return false; + } + log_info << "Opening \"" << color::cyan << file.string() << color::reset << '"'; ifs.close(); @@ -126,10 +131,6 @@ throw slice_error(oss.str()); } - slice_file = file; - - last_dir = file.parent_path(); - return true; } @@ -156,6 +157,19 @@ return oss.str(); } +bool slice_reader::open_file_case_insensitive(const path_type & dir, const path_type & filename) { + + boost::filesystem::directory_iterator end; + for(boost::filesystem::directory_iterator i(dir); i != end; ++i) { + path_type actual_filename = i->path().filename(); + if(boost::iequals(actual_filename.string(), filename.string()) && open_file(dir / actual_filename)) { + return true; + } + } + + return false; +} + void slice_reader::open(size_t slice) { current_slice = slice; @@ -163,17 +177,28 @@ ifs.close(); path_type slice_file = slice_filename(base_file, slice, slices_per_disk); + if(open_file(dir / slice_file)) { + return; + } - if(open_file(last_dir / slice_file)) { + path_type slice_file2 = slice_filename(base_file2, slice, slices_per_disk); + if(!base_file2.empty() && slice_file2 != slice_file && open_file(dir / slice_file2)) { return; } - if(dir != last_dir && open_file(dir / slice_file)) { + if(open_file_case_insensitive(dir, slice_file)) { + return; + } + + if(!base_file2.empty() && slice_file2 != slice_file && open_file_case_insensitive(dir, slice_file2)) { return; } std::ostringstream oss; oss << "could not open slice " << slice << ": " << slice_file; + if(!base_file2.empty() && slice_file2 != slice_file) { + oss << " or " << slice_file2; + } throw slice_error(oss.str()); } diff -Nru innoextract-1.6/src/stream/slice.hpp innoextract-1.7/src/stream/slice.hpp --- innoextract-1.6/src/stream/slice.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/stream/slice.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -39,7 +39,7 @@ //! Error thrown by \ref slice_reader if there was a problem. struct slice_error : public std::ios_base::failure { - explicit slice_error(std::string msg) : std::ios_base::failure(msg) { } + explicit slice_error(const std::string & msg) : std::ios_base::failure(msg) { } }; @@ -63,13 +63,12 @@ // Information for eading external setup data path_type dir; //!< Slice directory specified at construction. - path_type last_dir; //!< Directory containing the current slice. std::string base_file; //!< Base file name for slices. + std::string base_file2; //!< Fallback base filename for slices. const size_t slices_per_disk; //!< Number of slices grouped into each disk (for names). // Information about the current slice size_t current_slice; //!< Number of the currently opened slice. - path_type slice_file; //!< Filename of the currently opened slice. boost::uint32_t slice_size; //!< Size in bytes of the currently opened slice. // Streams @@ -78,6 +77,7 @@ void seek(size_t slice); bool open_file(const path_type & file); + bool open_file_case_insensitive(const path_type & dir, const path_type & filename); void open(size_t slice); public: @@ -111,9 +111,11 @@ * * \param dir The directory containing the slice files. * \param basename The base name for slice files. + * \param basename2 Alternative base name for slice files. * \param slices_per_disk How many slices are grouped into one disk. Must not be \c 0. */ - slice_reader(const path_type & dir, const std::string & basename, size_t slices_per_disk); + slice_reader(const path_type & dir, const std::string & basename, const std::string & basename2, + size_t slices_per_disk); /*! * Attempt to seek to an offset within a slice. @@ -146,9 +148,6 @@ //! \return the number currently opened slice. size_t slice() { return current_slice; } - //! \return filename for the currently opened slice. - path_type & file() { return slice_file; } - //! \return true a slice is currently open. bool is_open() { return (is != &ifs || ifs.is_open()); } diff -Nru innoextract-1.6/src/util/boostfs_compat.hpp innoextract-1.7/src/util/boostfs_compat.hpp --- innoextract-1.6/src/util/boostfs_compat.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/boostfs_compat.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Daniel Scharrer + * Copyright (C) 2012-2018 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 @@ -65,6 +65,6 @@ return path.string(); } -} // namespace utl +} // namespace util #endif // INNOEXTRACT_UTIL_BOOSTFS_COMPAT_HPP diff -Nru innoextract-1.6/src/util/console.cpp innoextract-1.7/src/util/console.cpp --- innoextract-1.6/src/util/console.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/console.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -357,12 +357,13 @@ } } - boost::uint64_t time = last_time; + boost::uint64_t time; try { boost::posix_time::ptime now(boost::posix_time::microsec_clock::universal_time()); time = boost::uint64_t((now - start_time).total_microseconds()); } catch(...) { // this shouldn't happen, assume no time has passed + time = last_time; } #if defined(_WIN32) diff -Nru innoextract-1.6/src/util/console.hpp innoextract-1.7/src/util/console.hpp --- innoextract-1.6/src/util/console.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/console.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2016 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -43,8 +43,6 @@ const char * command; }; -std::ostream & operator<<(std::ostream & os, const shell_command command); - //! Reset the output color to the original value. extern shell_command reset; @@ -69,7 +67,7 @@ //! The last set output color. extern shell_command current; -inline std::ostream & operator<<(std::ostream & os, const shell_command command) { +inline std::ostream & operator<<(std::ostream & os, shell_command command) { color::current = command; return os << command.command; } @@ -120,10 +118,6 @@ * \param show_rate Display the rate at which the progress changes. */ progress(boost::uint64_t max = 0, bool show_rate = true); - progress(const progress & o) - : max(o.max), value(o.value), show_rate(o.show_rate), start_time(o.start_time), - last_status(o.last_status), last_time(o.last_time), - last_rate(o.last_rate), label(o.label.str()) { } /*! * Update the progress bar. diff -Nru innoextract-1.6/src/util/encoding.cpp innoextract-1.7/src/util/encoding.cpp --- innoextract-1.6/src/util/encoding.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/encoding.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -71,6 +71,7 @@ #include #include #include +#include #include "util/log.hpp" #include "util/math.hpp" @@ -87,11 +88,84 @@ namespace { -static const char replacement_char = '_'; +//! Get names for encodings where iconv doesn't have the codepage alias +const char * get_encoding_name(codepage_id codepage) { + switch(codepage) { + case 708: return "ISO-8859-6"; + case 936: return "GBK"; + case 949: return "UHC"; + case 950: return "BIG5"; + // iconv's behavior for "UTF-16" is platform-dependent if there is no BOM. + // There never is any BOM in Inno Setup files and it's always little-endian, + // so we specify the exact encoding. + case 1200: return "UTF-16LE"; + case 1201: return "UTF-16BE"; + case 1252: return "MS-ANSI"; + case 1361: return "JOHAB"; + case 10000: return "MACINTOSH"; + case 10002: return "BIG5"; + case 10008: return "GB2312"; + case 12000: return "UTF-32LE"; + case 12001: return "UTF-32BE"; + case 20003: return "IBM5550"; + case 20127: return "US-ASCII"; + case 20261: return "T.61"; + case 20269: return "ISO_6937"; + case 20273: return "IBM273"; + case 20277: return "IBM277"; + case 20278: return "IBM278"; + case 20280: return "IBM280"; + case 20284: return "IBM284"; + case 20285: return "IBM285"; + case 20290: return "IBM290"; + case 20297: return "IBM297"; + case 20420: return "IBM420"; + case 20423: return "IBM423"; + case 20424: return "IBM424"; + case 20866: return "KOI8-R"; + case 20871: return "IBM871"; + case 20880: return "IBM880"; + case 20905: return "IBM905"; + case 20924: return "IBM1047"; + case 20932: return "EUC-JP-MS"; + case 20936: return "EUC-CN"; + case 21025: return "IBM1025"; + case 21866: return "KOI8-U"; + case 28591: return "ISO-8859-1"; + case 28592: return "ISO-8859-2"; + case 28593: return "ISO-8859-3"; + case 28594: return "ISO-8859-4"; + case 28595: return "ISO-8859-5"; + case 28596: return "ISO-8859-6"; + case 28597: return "ISO-8859-7"; + case 28598: return "ISO-8859-8"; + case 28599: return "ISO-8859-9"; + case 28603: return "ISO-8859-13"; + case 28605: return "ISO-8859-15"; + case 38598: return "ISO-8859-8"; + case 50220: return "ISO-2022-JP"; + case 50221: return "ISO-2022-JP-2"; + case 50222: return "ISO-2022-JP-3"; + case 50225: return "ISO-2022-KR"; + case 50227: return "ISO-2022-CN"; + case 50229: return "ISO-2022-CN-EXT"; + case 50930: return "EBCDIC-JP-E"; + case 51932: return "EUC-JP"; + case 51936: return "EUC-CN"; + case 51949: return "EUC-KR"; + case 51950: return "EUC-CN"; + case 54936: return "GB18030"; + case 65000: return "UTF-7"; + case 65001: return "UTF-8"; + default: return NULL; + } +} typedef boost::uint32_t unicode_char; -static size_t get_encoding_size(codepage_id codepage) { +const unicode_char replacement_char = '_'; + +size_t get_encoding_size(codepage_id codepage) { switch(codepage) { case 1200: return 2u; // UTF-16LE case 1201: return 2u; // UTF-16BE @@ -102,14 +176,15 @@ } //! Fallback conversion that will at least work for ASCII characters -static void to_utf8_fallback(const std::string & from, std::string & to, codepage_id cp) { +void to_utf8_fallback(const std::string & from, std::string & to, codepage_id codepage) { - size_t skip = get_encoding_size(cp); + size_t skip = get_encoding_size(codepage); size_t shift = 0; - switch(cp) { + switch(codepage) { case 1201: shift = 1u * 8u; break; // UTF-16BE case 12001: shift = 3u * 8u; break; // UTF-32BE + default: break; } to.clear(); @@ -129,38 +204,86 @@ // replace non-ASCII characters with underscores if((unicode_char(ascii) << shift) != unicode) { warn = true; - ascii = replacement_char; + ascii = char(replacement_char); } to.push_back(ascii); } if(warn) { - static bool warned = false; - log_warning << "Unknown data while converting from CP" << cp << " to UTF-8."; - if(!warned && (cp == cp_windows1252 || cp == cp_utf16le)) { - #if INNOEXTRACT_HAVE_ICONV - log_warning << " └─ make sure your iconv installation supports Windows-1252 and UTF-16LE"; - #elif !INNOEXTRACT_HAVE_BUILTIN_CONV && !INNOEXTRACT_HAVE_WIN32_CONV - log_warning << " └─ build innoextract with charset conversion routines enabled!"; - #endif - warned = true; - } + log_warning << "Unknown data while converting from CP" << codepage << " to UTF-8."; } } -#if INNOEXTRACT_HAVE_BUILTIN_CONV +bool is_utf8_continuation_byte(unicode_char chr) { + return (chr & 0xc0) == 0x80; +} -static size_t utf8_length(unicode_char chr) { - if (chr < 0x80) return 1; - else if(chr < 0x800) return 2; - else if(chr < 0x10000) return 3; - else if(chr <= 0x0010ffff) return 4; +template +unicode_char utf8_read(In & it, In end, unicode_char replacement = replacement_char) { + + if(it == end) { + return unicode_char(-1); + } + unicode_char chr = boost::uint8_t(*it++); + + // For multi-byte characters, read the remaining bytes + if(chr & (1 << 7)) { + + if(is_utf8_continuation_byte(chr)) { + // Bad start position + return replacement; + } + + if(it == end || !is_utf8_continuation_byte(boost::uint8_t(*it))) { + // Unexpected end of multi-byte sequence + return replacement; + } + chr &= 0x3f, chr <<= 6, chr |= unicode_char(boost::uint8_t(*it++) & 0x3f); + + if(chr & (1 << (5 + 6))) { + + if(it == end || !is_utf8_continuation_byte(boost::uint8_t(*it))) { + // Unexpected end of multi-byte sequence + return replacement; + } + chr &= ~unicode_char(1 << (5 + 6)), chr <<= 6, chr |= unicode_char(boost::uint8_t(*it++) & 0x3f); + + if(chr & (1 << (4 + 6 + 6))) { + + if(it == end || !is_utf8_continuation_byte(boost::uint8_t(*it))) { + // Unexpected end of multi-byte sequence + return replacement; + } + chr &= ~unicode_char(1 << (4 + 6 + 6)), chr <<= 6, chr |= unicode_char(boost::uint8_t(*it++) & 0x3f); + + if(chr & (1 << (3 + 6 + 6 + 6))) { + // Illegal UTF-8 byte + return replacement; + } + + } + } + } + + return chr; +} + +size_t utf8_length(unicode_char chr) { + if(chr < 0x80) { + return 1; + } else if(chr < 0x800) { + return 2; + } else if(chr < 0x10000) { + return 3; + } else if(chr <= 0x0010ffff) { + return 4; + } return 1; } -static void utf8_write(std::string & to, unicode_char chr) { +void utf8_write(std::string & to, unicode_char chr) { static const boost::uint8_t first_bytes[7] = { 0x00, 0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc @@ -172,34 +295,36 @@ // Extract bytes to write boost::uint8_t bytes[4]; switch(length) { - case 4: bytes[3] = static_cast((chr | 0x80) & 0xBF), chr >>= 6; - case 3: bytes[2] = static_cast((chr | 0x80) & 0xBF), chr >>= 6; - case 2: bytes[1] = static_cast((chr | 0x80) & 0xBF), chr >>= 6; + case 4: bytes[3] = static_cast((chr | 0x80) & 0xBF), chr >>= 6; /* fall-through */ + case 3: bytes[2] = static_cast((chr | 0x80) & 0xBF), chr >>= 6; /* fall-through */ + case 2: bytes[1] = static_cast((chr | 0x80) & 0xBF), chr >>= 6; /* fall-through */ case 1: bytes[0] = static_cast(chr | first_bytes[length]); + default: break; } // Add them to the output const boost::uint8_t * cur_byte = bytes; switch(length) { - case 4: to.push_back(char(*cur_byte++)); - case 3: to.push_back(char(*cur_byte++)); - case 2: to.push_back(char(*cur_byte++)); + case 4: to.push_back(char(*cur_byte++)); /* fall-through */ + case 3: to.push_back(char(*cur_byte++)); /* fall-through */ + case 2: to.push_back(char(*cur_byte++)); /* fall-through */ case 1: to.push_back(char(*cur_byte++)); + default: break; } } //! \return true c is is the first part of an UTF-16 surrogate pair -static bool is_utf16_high_surrogate(unicode_char chr) { +bool is_utf16_high_surrogate(unicode_char chr) { return chr >= 0xd800 && chr <= 0xdbff; } //! \return true c is is the second part of an UTF-16 surrogate pair -static bool is_utf16_low_surrogate(unicode_char chr) { +bool is_utf16_low_surrogate(unicode_char chr) { return chr >= 0xdc00 && chr <= 0xdfff; } -static void utf16le_to_utf8(const std::string & from, std::string & to) { +void utf16le_to_utf8(const std::string & from, std::string & to) { if(from.size() % 2 != 0) { log_warning << "Unexpected trailing byte in UTF-16 string."; @@ -262,16 +387,48 @@ } -static void windows1252_to_utf8(const std::string & from, std::string & to) { +void utf8_to_utf16le(const std::string & from, std::string & to) { - static unicode_char replacements[] = { - 0x20ac, replacement_char, 0x201a, 0x192, 0x201e, 0x2026, 0x2020, 0x2021, 0x2c6, - 0x2030, 0x160, 0x2039, 0x152, replacement_char, 0x17d, replacement_char, - replacement_char, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x2dc, - 0x2122, 0x161, 0x203a, 0x153, replacement_char, 0x17e, 0x178 - }; + to.clear(); + to.reserve(from.size() * 2); // optimistically, most strings only have ASCII characters + + bool warn = false; + + for(std::string::const_iterator i = from.begin(); i != from.end(); ) { + + unicode_char chr = utf8_read(i, from.end()); + + if((chr >= 0xd800 && chr <= 0xdfff) || chr > 0x10ffff) { + chr = replacement_char; + warn = true; + } else if(chr >= 0x10000) { + chr -= 0x10000; + unicode_char high_surrogate = 0xd800 + (chr >> 10); + to.push_back(char(boost::uint8_t(high_surrogate))); + to.push_back(char(boost::uint8_t(high_surrogate >> 8))); + chr = 0xdc00 + (chr & 0x3ff); + } + + to.push_back(char(boost::uint8_t(chr))); + to.push_back(char(boost::uint8_t(chr >> 8))); + } + + if(warn) { + log_warning << "Unexpected data while converting from UTF-8 to UTF-16LE."; + } + +} + +unicode_char windows1252_replacements[] = { + 0x20ac, replacement_char, 0x201a, 0x192, 0x201e, 0x2026, 0x2020, 0x2021, 0x2c6, + 0x2030, 0x160, 0x2039, 0x152, replacement_char, 0x17d, replacement_char, + replacement_char, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x2dc, + 0x2122, 0x161, 0x203a, 0x153, replacement_char, 0x17e, 0x178 +}; - BOOST_STATIC_ASSERT(sizeof(replacements) == (160 - 128) * sizeof(*replacements)); +BOOST_STATIC_ASSERT(sizeof(windows1252_replacements) == (160 - 128) * sizeof(*windows1252_replacements)); + +void windows1252_to_utf8(const std::string & from, std::string & to) { to.clear(); to.reserve(from.size()); // optimistically, most strings only have ASCII characters @@ -283,8 +440,8 @@ // Windows-1252 maps almost directly to Unicode - yay! unicode_char chr = boost::uint8_t(c); if(chr >= 128 && chr < 160) { - chr = replacements[chr - 128]; - warn = warn || (chr == unicode_char(replacement_char)); + chr = windows1252_replacements[chr - 128]; + warn = warn || chr == replacement_char; } utf8_write(to, chr); @@ -296,98 +453,48 @@ } -static bool to_utf8_builtin(const std::string & from, std::string & to, codepage_id cp) { +void utf8_to_windows1252(const std::string & from, std::string & to) { + + to.clear(); + to.reserve(from.size()); // optimistically, most strings only have ASCII characters + + bool warn = false; - switch(cp) { - case cp_utf16le: utf16le_to_utf8(from, to); return true; - case cp_windows1252: windows1252_to_utf8(from, to); return true; - case cp_iso_8859_1: windows1252_to_utf8(from, to); return true; - default: return false; + for(std::string::const_iterator i = from.begin(); i != from.end(); ) { + + unicode_char chr = utf8_read(i, from.end()); + + // Windows-1252 maps almost directly to Unicode - yay! + if(chr >= 256 || (chr >= 128 && chr < 160)) { + size_t i = 0; + for(; i < size_t(boost::size(windows1252_replacements)); i++) { + if(chr == windows1252_replacements[i] && windows1252_replacements[i] != replacement_char) { + break; + } + } + if(i < size_t(boost::size(windows1252_replacements))) { + chr = unicode_char(128 + i); + } else { + chr = replacement_char; + warn = true; + } + } + + to.push_back(char(boost::uint8_t(chr))); + } + + if(warn) { + log_warning << "Unsupported character while converting from UTF-8 to Windows-1252."; } } -#endif // INNOEXTRACT_HAVE_BUILTIN_CONV - #if INNOEXTRACT_HAVE_ICONV typedef boost::unordered_map converter_map; -static converter_map converters; - -//! Get names for encodings where iconv doesn't have the codepage alias -static const char * get_encoding_name(codepage_id codepage) { - switch(codepage) { - case 708: return "ISO-8859-6"; - case 936: return "GBK"; - case 949: return "UHC"; - case 950: return "BIG5"; - // iconv's behavior for "UTF-16" is platform-dependent if there is no BOM. - // There never is any BOM in Inno Setup files and it's always little-endian, - // so we specify the exact encoding. - case 1200: return "UTF-16LE"; - case 1201: return "UTF-16BE"; - case 1252: return "MS-ANSI"; - case 1361: return "JOHAB"; - case 10000: return "MACINTOSH"; - case 10002: return "BIG5"; - case 10008: return "GB2312"; - case 12000: return "UTF-32LE"; - case 12001: return "UTF-32BE"; - case 20003: return "IBM5550"; - case 20127: return "US-ASCII"; - case 20261: return "T.61"; - case 20269: return "ISO_6937"; - case 20273: return "IBM273"; - case 20277: return "IBM277"; - case 20278: return "IBM278"; - case 20280: return "IBM280"; - case 20284: return "IBM284"; - case 20285: return "IBM285"; - case 20290: return "IBM290"; - case 20297: return "IBM297"; - case 20420: return "IBM420"; - case 20423: return "IBM423"; - case 20424: return "IBM424"; - case 20866: return "KOI8-R"; - case 20871: return "IBM871"; - case 20880: return "IBM880"; - case 20905: return "IBM905"; - case 20924: return "IBM1047"; - case 20932: return "EUC-JP-MS"; - case 20936: return "EUC-CN"; - case 21025: return "IBM1025"; - case 21866: return "KOI8-U"; - case 28591: return "ISO-8859-1"; - case 28592: return "ISO-8859-2"; - case 28593: return "ISO-8859-3"; - case 28594: return "ISO-8859-4"; - case 28595: return "ISO-8859-5"; - case 28596: return "ISO-8859-6"; - case 28597: return "ISO-8859-7"; - case 28598: return "ISO-8859-8"; - case 28599: return "ISO-8859-9"; - case 28603: return "ISO-8859-13"; - case 28605: return "ISO-8859-15"; - case 38598: return "ISO-8859-8"; - case 50220: return "ISO-2022-JP"; - case 50221: return "ISO-2022-JP-2"; - case 50222: return "ISO-2022-JP-3"; - case 50225: return "ISO-2022-KR"; - case 50227: return "ISO-2022-CN"; - case 50229: return "ISO-2022-CN-EXT"; - case 50930: return "EBCDIC-JP-E"; - case 51932: return "EUC-JP"; - case 51936: return "EUC-CN"; - case 51949: return "EUC-KR"; - case 51950: return "EUC-CN"; - case 54936: return "GB18030"; - case 65000: return "UTF-7"; - case 65001: return "UTF-8"; - default: return NULL; - } -} +converter_map converters; -static iconv_t get_converter(codepage_id codepage) { +iconv_t get_converter(codepage_id codepage) { // Try to reuse an existing converter if possible converter_map::const_iterator i = converters.find(codepage); @@ -422,9 +529,9 @@ return converters[codepage] = handle; } -static bool to_utf8_iconv(const std::string & from, std::string & to, codepage_id cp) { +bool to_utf8_iconv(const std::string & from, std::string & to, codepage_id codepage) { - iconv_t converter = get_converter(cp); + iconv_t converter = get_converter(codepage); if(converter == iconv_t(-1)) { return false; } @@ -447,7 +554,7 @@ iconv(converter, NULL, NULL, NULL, NULL); - size_t skip = get_encoding_size(cp); + size_t skip = get_encoding_size(codepage); bool warn = false; @@ -465,9 +572,9 @@ } else if(/*errno == EILSEQ &&*/ insize >= 2) { // invalid byte (sequence) - add a replacement char and try the next byte if(outsize == 0) { - to.push_back(replacement_char); + to.push_back(char(replacement_char)); } else { - *outbuf = replacement_char; + *outbuf = char(replacement_char); outsize--; } inbuf.buf += skip; @@ -484,7 +591,7 @@ } if(warn) { - log_warning << "Unexpected data while converting from CP" << cp << " to UTF-8."; + log_warning << "Unexpected data while converting from CP" << codepage << " to UTF-8."; } to.resize(outbase); @@ -496,7 +603,7 @@ #if INNOEXTRACT_HAVE_WIN32_CONV -static std::string windows_error_string(DWORD code) { +std::string windows_error_string(DWORD code) { char * error; DWORD n = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, code, 0, reinterpret_cast(&error), 0, @@ -513,7 +620,7 @@ } } -static bool to_utf8_win32(const std::string & from, std::string & to, codepage_id cp) { +bool to_utf8_win32(const std::string & from, std::string & to, codepage_id codepage) { int ret = 0; @@ -521,18 +628,18 @@ const WCHAR * utf16; int utf16_size; std::vector buffer; - if(cp == cp_utf16le) { + if(codepage == cp_utf16le) { utf16 = reinterpret_cast(from.data()); utf16_size = int(from.size()) / 2; } else { - utf16_size = MultiByteToWideChar(cp, 0, from.data(), int(from.length()), NULL, 0); + utf16_size = MultiByteToWideChar(codepage, 0, from.data(), int(from.length()), NULL, 0); if(utf16_size > 0) { buffer.resize(size_t(utf16_size)); - ret = MultiByteToWideChar(cp, 0, from.data(), int(from.length()), + ret = MultiByteToWideChar(codepage, 0, from.data(), int(from.length()), &buffer.front(), utf16_size); } if(utf16_size <= 0 || ret <= 0) { - log_warning << "Error while converting from CP" << cp << " to UTF-16: " + log_warning << "Error while converting from CP" << codepage << " to UTF-16: " << windows_error_string(GetLastError()); return false; } @@ -559,38 +666,76 @@ } // anonymous namespace -void to_utf8(const std::string & from, std::string & to, codepage_id cp) { +void to_utf8(const std::string & from, std::string & to, codepage_id codepage) { if(from.empty()) { to.clear(); return; } - if(cp == cp_utf8 || cp == cp_ascii) { + if(codepage == cp_utf8 || codepage == cp_ascii) { to = from; return; } - #if INNOEXTRACT_HAVE_BUILTIN_CONV - if(to_utf8_builtin(from, to, cp)) { - return; + switch(codepage) { + case cp_utf16le: utf16le_to_utf8(from, to); return; + case cp_windows1252: windows1252_to_utf8(from, to); return; + case cp_iso_8859_1: windows1252_to_utf8(from, to); return; + default: break; } - #endif #if INNOEXTRACT_HAVE_ICONV - if(to_utf8_iconv(from, to, cp)) { + if(to_utf8_iconv(from, to, codepage)) { return; } #endif #if INNOEXTRACT_HAVE_WIN32_CONV - if(to_utf8_win32(from, to, cp)) { + if(to_utf8_win32(from, to, codepage)) { return; } #endif - to_utf8_fallback(from, to, cp); + to_utf8_fallback(from, to, codepage); + +} + +void from_utf8(const std::string & from, std::string & to, codepage_id codepage) { + + if(from.empty()) { + to.clear(); + return; + } + + if(codepage == cp_utf8) { + to = from; + return; + } + + switch(codepage) { + case cp_utf16le: utf8_to_utf16le(from, to); return; + case cp_windows1252: utf8_to_windows1252(from, to); return; + case cp_iso_8859_1: utf8_to_windows1252(from, to); return; + default: { + log_warning << "Unsupported output codepage: " << codepage; + to = from; + } + } + +} + +std::string encoding_name(codepage_id codepage) { + + const char * name = get_encoding_name(codepage); + if(name) { + return name; + } + + std::ostringstream oss; + oss << "Windows-" << codepage; + return oss.str(); } } // namespace util diff -Nru innoextract-1.6/src/util/encoding.hpp innoextract-1.7/src/util/encoding.hpp --- innoextract-1.6/src/util/encoding.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/encoding.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -44,6 +44,18 @@ */ void to_utf8(const std::string & from, std::string & to, codepage_id codepage = 1252); +/*! + * Convert a string from UTF-8 to a specified encoding. + * \param from The input string to convert. + * \param to The output for the converted string. + * \param codepage The Windows codepage number for the input string encoding. + * + * \note This function is not thread-safe. + */ +void from_utf8(const std::string & from, std::string & to, codepage_id codepage = 1252); + +std::string encoding_name(codepage_id codepage); + } // namespace util #endif // INNOEXTRACT_UTIL_ENCODING_HPP diff -Nru innoextract-1.6/src/util/endian.hpp innoextract-1.7/src/util/endian.hpp --- innoextract-1.6/src/util/endian.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/endian.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 Daniel Scharrer + * Copyright (C) 2011-2017 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 @@ -136,7 +136,7 @@ */ template static void load(const char * buffer, T * values, size_t count) { - if(Endianness::native() || sizeof(*values) == 8) { + if(Endianness::native() || sizeof(*values) == 1) { std::memcpy(values, buffer, sizeof(*values) * count); } else { for(size_t i = 0; i < count; i++, buffer += sizeof(*values)) { @@ -170,7 +170,7 @@ */ template static void store(T * values, size_t count, char * buffer) { - if(Endianness::native() || sizeof(*values) == 8) { + if(Endianness::native() || sizeof(*values) == 1) { std::memcpy(buffer, values, sizeof(*values) * count); } else { for(size_t i = 0; i < count; i++, buffer += sizeof(*values)) { diff -Nru innoextract-1.6/src/util/flags.hpp innoextract-1.7/src/util/flags.hpp --- innoextract-1.6/src/util/flags.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/flags.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -63,9 +63,7 @@ /* implicit */ inline flags(enum_type flag) : _flags(Type().set(size_t(flag))) { } - /* implicit */ inline flags(Zero = 0) : _flags() { } - - flags(const flags & o) : _flags(o._flags) { } + /* implicit */ inline flags(Zero /* zero */ = 0) : _flags() { } static flags load(Type _flags) { return flags(_flags, true); @@ -144,11 +142,6 @@ return operator^=(flag); } - flags & operator=(flags o) { - _flags = o._flags; - return *this; - } - //! Get a set of flags with all possible values set. static flags all() { return flags(Type().flip()); diff -Nru innoextract-1.6/src/util/load.cpp innoextract-1.7/src/util/load.cpp --- innoextract-1.6/src/util/load.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/load.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -44,7 +44,7 @@ } } -void binary_string::skip(std::istream& is) { +void binary_string::skip(std::istream & is) { boost::uint32_t length = util::load(is); if(is.fail()) { diff -Nru innoextract-1.6/src/util/log.hpp innoextract-1.7/src/util/log.hpp --- innoextract-1.6/src/util/log.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/log.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -29,6 +29,8 @@ #include #include +#include + #ifdef DEBUG #define debug(...) \ if(::logger::debug) \ @@ -46,7 +48,7 @@ /*! * logger class that allows longging via the stream operator. */ -class logger { +class logger : private boost::noncopyable { public: diff -Nru innoextract-1.6/src/util/math.hpp innoextract-1.7/src/util/math.hpp --- innoextract-1.6/src/util/math.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/math.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -56,12 +56,12 @@ struct safe_shifter { template - static T right_shift(T, unsigned int) { + static T right_shift(T /* value */, unsigned int /* bits */) { return 0; } template - static T left_shift(T, unsigned int) { + static T left_shift(T /* value */, unsigned int /* bits */) { return 0; } diff -Nru innoextract-1.6/src/util/output.hpp innoextract-1.7/src/util/output.hpp --- innoextract-1.6/src/util/output.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/output.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Daniel Scharrer + * Copyright (C) 2011-2018 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 @@ -46,7 +46,7 @@ color::shell_command prev = color::current; os << '"' << color::green; for(std::string::const_iterator i = q.str.begin(); i != q.str.end(); ++i) { - unsigned char c = (unsigned char)*i; + boost::uint8_t c = boost::uint8_t(*i); if(c < ' ' && c != '\t' && c != '\r' && c != '\n') { std::ios_base::fmtflags old = os.flags(); os << color::red << '<' << std::hex << std::setfill('0') << std::setw(2) @@ -105,7 +105,7 @@ } } -} +} // namespace detail template @@ -143,13 +143,45 @@ return os; } +struct print_hex_string { + + const char * data; + size_t size; + + explicit print_hex_string(const char * data, size_t size) : data(data), size(size) { } + +}; + +inline std::ostream & operator<<(std::ostream & os, const print_hex_string & s) { + + std::ios_base::fmtflags old = os.flags(); + char oldfill = os.fill('0'); + + os << std::hex; + for(size_t i = 0; i < s.size; i++) { + os << std::setw(2) << int(boost::uint8_t(s.data[i])); + } + + os.fill(oldfill); + os.setf(old, std::ios_base::basefield); + return os; } +} // namespace detail + template detail::print_hex print_hex(T value) { return detail::print_hex(value); } +inline detail::print_hex_string print_hex(const char * data, size_t size) { + return detail::print_hex_string(data, size); +} + +inline detail::print_hex_string print_hex(const std::string & data) { + return print_hex(data.c_str(), data.size()); +} + const char * const byte_size_units[] = { "B", "KiB", @@ -203,7 +235,7 @@ return os << ' ' << byte_size_units[i]; } -} +} // namespace detail template detail::print_bytes print_bytes(T value, int precision = 3) { diff -Nru innoextract-1.6/src/util/storedenum.hpp innoextract-1.7/src/util/storedenum.hpp --- innoextract-1.6/src/util/storedenum.hpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/storedenum.hpp 2018-06-12 18:50:34.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 Daniel Scharrer + * Copyright (C) 2011-2017 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 @@ -55,12 +55,12 @@ #define STORED_ENUM_MAP(MapName, Default, /* elements */ ...) \ STORED_MAP_HELPER(MapName, Default, \ static const enum_type default_value;, \ - ## __VA_ARGS__); \ + __VA_ARGS__); \ const MapName::enum_type MapName::default_value = Default //! Declare a mapping from bits to flag enum elements to be used for \ref stored_flags #define STORED_FLAGS_MAP(MapName, Flag0, /* additional flags */ ...) \ - STORED_MAP_HELPER(MapName, Flag0, , Flag0, ## __VA_ARGS__) + STORED_MAP_HELPER(MapName, Flag0, , Flag0, __VA_ARGS__) template struct stored_enum { @@ -219,7 +219,7 @@ public: explicit stored_flag_reader(std::istream & _is, size_t pad_bits = 32) - : pad_bits(pad_bits), is(_is), pos(0), result(0), bytes(0) { } + : pad_bits(pad_bits), is(_is), pos(0), buffer(0), result(0), bytes(0) { } //! Declare the next possible flag. void add(enum_type flag) { diff -Nru innoextract-1.6/src/util/time.cpp innoextract-1.7/src/util/time.cpp --- innoextract-1.6/src/util/time.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/time.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -258,7 +258,7 @@ (int fd, const char *path, const struct timespec times[2], int flag); #endif -bool set_file_time(const boost::filesystem::path & path, time t, boost::uint32_t nsec) { +bool set_file_time(const boost::filesystem::path & path, time sec, boost::uint32_t nsec) { #if (INNOEXTRACT_HAVE_DYNAMIC_UTIMENSAT || INNOEXTRACT_HAVE_UTIMENSAT) \ && INNOEXTRACT_HAVE_AT_FDCWD @@ -266,7 +266,7 @@ // nanosecond precision, for Linux and POSIX.1-2008+ systems struct timespec timens[2]; - timens[0].tv_sec = to_time_t(t, path.string().c_str()); + timens[0].tv_sec = to_time_t(sec, path.string().c_str()); timens[0].tv_nsec = boost::int32_t(nsec); timens[1] = timens[0]; @@ -298,7 +298,7 @@ return false; } - FILETIME filetime = to_filetime(t, nsec); + FILETIME filetime = to_filetime(sec, nsec); bool ret = (SetFileTime(handle, &filetime, &filetime, &filetime) != 0); CloseHandle(handle); @@ -310,7 +310,7 @@ // microsecond precision, for older POSIX systems (4.3BSD, POSIX.1-2001) struct timeval times[2]; - times[0].tv_sec = to_time_t(t, path.string().c_str()); + times[0].tv_sec = to_time_t(sec, path.string().c_str()); times[0].tv_usec = boost::int32_t(nsec / 1000); times[1] = times[0]; @@ -322,7 +322,7 @@ try { (void)nsec; // sub-second precision not supported by Boost - std::time_t tt = to_time_t(t, path.string().c_str()); + std::time_t tt = to_time_t(sec, path.string().c_str()); boost::filesystem::last_write_time(path, tt); return true; } catch(...) { diff -Nru innoextract-1.6/src/util/windows.cpp innoextract-1.7/src/util/windows.cpp --- innoextract-1.6/src/util/windows.cpp 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/src/util/windows.cpp 2018-06-12 18:50:34.000000000 +0000 @@ -54,6 +54,16 @@ #include "util/ansi.hpp" +// Disable telemetry added in Visual Studio 2015 +#if defined(_MSC_VER) && _MSC_VER >= 1900 +extern "C" { + void _cdecl __vcrt_initialize_telemetry_provider() { } + void _cdecl __telemetry_main_invoke_trigger() { } + void _cdecl __telemetry_main_return_trigger() { } + void _cdecl __vcrt_uninitialize_telemetry_provider() { } +}; +#endif + namespace util { class windows_console_sink : public util::ansi_console_parser { diff -Nru innoextract-1.6/VERSION innoextract-1.7/VERSION --- innoextract-1.6/VERSION 2016-04-08 09:51:38.000000000 +0000 +++ innoextract-1.7/VERSION 2018-06-12 18:50:34.000000000 +0000 @@ -1,7 +1,7 @@ -innoextract 1.6 +innoextract 1.7 Known working Inno Setup versions: -Inno Setup 1.2.10 to 5.5.8 +Inno Setup 1.2.10 to 5.6.0 Bug tracker: http://innoextract.constexpr.org/issues