diff --git a/CMakeLists.txt b/CMakeLists.txt index 993595c..897a877 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,9 +31,13 @@ set(INNOEXTRACT_SOURCES src/ChunkFilter.cpp src/ExeReader.cpp src/InnoExtract.cpp + src/LoadingUtils.cpp src/LzmaFilter.cpp + src/Output.cpp + src/SetupHeader.cpp src/SetupLoader.cpp src/Utils.cpp + src/Version.cpp ) add_executable(innoextract ${INNOEXTRACT_SOURCES}) diff --git a/src/BitfieldConverter.hpp b/src/BitfieldConverter.hpp new file mode 100644 index 0000000..ba9ae69 --- /dev/null +++ b/src/BitfieldConverter.hpp @@ -0,0 +1,240 @@ + +#include +#include +#include +#include +#include + +namespace boost { + template + class integer_traits : public integer_traits { }; +} + +template +struct is_power_of_two { + static const bool value = false; +}; +template +struct is_power_of_two::type> { + static const bool value = true; + typedef Type type; +}; + +template +struct log_next_power_of_two { + static const size_t value = boost::static_log2::value + 1; +}; +template +struct log_next_power_of_two >::type> { + static const size_t value = boost::static_log2::value; +}; + +template +struct next_power_of_two { + static const size_t value = size_t(1) << (boost::static_log2::value + 1); +}; +template +struct next_power_of_two >::type> { + static const size_t value = N; +}; + + +struct fast_integers { + +private: + + template struct _impl { }; + template struct _impl<8, Dummy> { typedef uint_fast8_t type; }; + template struct _impl<16, Dummy> { typedef uint_fast16_t type; }; + template struct _impl<32, Dummy> { typedef uint_fast32_t type; }; + template struct _impl<64, Dummy> { typedef uint_fast64_t type; }; + template struct _impl<128, Dummy> { typedef __uint128_t type; }; + +public: + + template + struct bits : public _impl::value>::value> { }; + +}; + +struct exact_integers { + +private: + + template struct _impl { }; + template struct _impl<8, Dummy> { typedef uint8_t type; }; + template struct _impl<16, Dummy> { typedef uint16_t type; }; + template struct _impl<32, Dummy> { typedef uint32_t type; }; + template struct _impl<64, Dummy> { typedef uint64_t type; }; + template struct _impl<128, Dummy> { typedef __uint128_t type; }; + +public: + + template + struct bits : public _impl::value>::value> { }; + +}; + +struct bitset_types { + + template + struct bits { + typedef std::bitset type; + }; + +}; + + +/*! + * Converter that rearranges bits in an integer. + * + * Conversion is reduced to a minimal number of mask & shift operations at compile-time. + * + * Usage: + * + * bitset_converter<> is an empty converter list (cannot be used without adding at least one mappings). + * (list)::add::map<from, to> maps the from'th input bit to the to'th output bit. + * + * Convenience function to add a continous region of mappings: + * bitset_converter<>::add::value<to2> is equivalent to bitset_converter<>::add::map<0, to2> + * (list)::add::map<from, to>::add::value<to2> is equivalent to ::add::map<from, to>::add::map<from + 1, to2> + * + * Inut bits without a corresponding "from" entry are ignored. + * Output bit without a corresponding "to" entry are always zero. + * + * The same input/output bit can appear in multiple mappings. + * + * Invoke the converter: (list)::convert(integer) + * + * Limitations: + * + * Input bits must fit in a native integer type provided by in_types::bits<bits>. + * + * Output bits must fit in an integer type selected by out_types::bits<bits>. + * + * Example: + * + * // Create a converter that swaps the first two bits, keeps the next one and ignores all others. + * typedef bitset_converter<>::add::map<0, 1>::add::map<1, 0>::add::value<2> Converter; + * + * // Convert something. + * Converter::convert(3); + * + */ +template +struct bitset_converter { + +private: + + typedef ptrdiff_t shift_type; + typedef size_t index_type; + + template + struct IterateEntries { + static const typename Combiner::type value = Combiner::template combine::value)>::value; + }; + template struct IterateEntries { static const typename Combiner::type value = Combiner::base; }; + template struct Combiner { typedef Type type; static const Type base = Base; }; + + template + struct MaxCombiner : public Combiner::const_min> { + template + struct combine { static const Type value = boost::static_signed_max::value, accumulator>::value; }; + }; + + template + struct MinCombiner : public Combiner::const_max> { + template + struct combine { static const Type value = boost::static_signed_min::value, accumulator>::value; }; + }; + + struct ShiftGetter { template struct get { static const shift_type value = Entry::shift; }; }; + struct FromGetter { template struct get { static const index_type value = Entry::from; }; }; + struct ToGetter { template struct get { static const index_type value = Entry::to; }; }; + + template + struct ShiftMaskCombiner : public Combiner { + template + struct combine { static const Type value = mask | ( (Entry::shift == Shift) ? (Type(1) << Entry::from) : Type(0) ); }; + }; + + template + struct Builder; + + template + struct Entry { + + typedef Entry This; + + static const index_type from = From; + static const index_type to = To; + typedef Next next; + + static const shift_type shift = shift_type(from) - shift_type(to); + + static const shift_type max_shift = IterateEntries, This>::value; + static const shift_type min_shift = IterateEntries, This>::value; + + static const index_type in_bits = IterateEntries, This>::value + 1; + typedef typename in_types::template bits::type in_type; + + static const index_type out_bits = IterateEntries, This>::value + 1; + typedef typename out_types::template bits::type out_type; + + template + struct ShiftMask { static const in_type value = IterateEntries, This>::value; }; + + template + inline static typename boost::enable_if_c<(Shift >= shift_type(0)), out_type>::type evaluate(in_type value) { + return out_type((value & ShiftMask::value) >> Shift); + } + template + inline static typename boost::enable_if_c<(Shift < shift_type(0)), out_type>::type evaluate(in_type value) { + return out_type(value & ShiftMask::value) << (-Shift); + } + + template + struct NextShift { static const shift_type value = Shift + 1; }; + template + struct NextShift::value == in_type(0)>::type > { + static const shift_type value = NextShift::value; + }; + + template + inline static typename boost::enable_if_c<(NextShift::value != max_shift + 1), out_type>::type map(in_type value) { + return evaluate(value) | (map::value>(value)); + } + template + inline static typename boost::enable_if_c<(NextShift::value == max_shift + 1), out_type>::type map(in_type value) { + return evaluate(value); + } + + public: + + typedef Builder add; + + static out_type convert(in_type value) { + return map(value); + } + + }; + + template + struct Builder { + + template + struct map : public Entry { }; + + template + struct value : public Entry { }; + + template + struct value : public Entry<0, To> { }; + + }; + +public: + + typedef Builder add; + +}; diff --git a/src/BlockReader.cpp b/src/BlockReader.cpp index fea48ff..2f53558 100644 --- a/src/BlockReader.cpp +++ b/src/BlockReader.cpp @@ -5,52 +5,87 @@ #include #include +#include #include #include "Types.h" #include "Utils.hpp" #include "ChunkFilter.hpp" #include "LzmaFilter.hpp" +#include "LoadingUtils.hpp" +#include "Enum.hpp" using std::cout; using std::endl; namespace io = boost::iostreams; -#pragma pack(push,1) - -struct BlockHeader { - u32 storedSize; //!< Total bytes written, including the CRCs. - u8 compressed; //!< True if data is compressed, false if not. +enum Compression { + Stored, + Zlib, + LZMA1, }; -#pragma pack(pop) +NAMED_ENUM(Compression) + +ENUM_NAMES(Compression, "Compression", "stored", "zlib", "lzma1") + +template +T loadNumberChecked(std::istream & is, u32 & crc) { + T value = load(is); + crc = lzma_crc32(reinterpret_cast(&value), sizeof(value), crc); + return fromLittleEndian(value); +}; -std::istream * BlockReader::get(std::istream & base) { +std::istream * BlockReader::get(std::istream & base, const InnoVersion & version) { - u32 crc; - BlockHeader block; - if(read(read(base, crc), block).fail()) { - return NULL; + u32 expectedCrc = loadNumber(base); + u32 actualCrc = 0; + + u64 storedSize; + Compression compression; + bool chunked; + + if(version >= INNO_VERSION(4, 0, 9)) { + storedSize = loadNumberChecked(base, actualCrc); + u8 compressed = loadNumberChecked(base, actualCrc); + compression = compressed ? (version >= INNO_VERSION(4, 1, 6) ? LZMA1 : Zlib) : Stored; + chunked = true; + + } else { + + u32 compressedSize = loadNumberChecked(base, actualCrc); + u32 uncompressedSize = loadNumberChecked(base, actualCrc); + + if(compressedSize == u32(-1)) { + storedSize = uncompressedSize, compression = Stored; + } else { + storedSize = compressedSize, compression = Zlib; + } + + // Add the size of a CRC32 checksum for each 4KiB chunk. + storedSize += ceildiv(storedSize, 4096) * 4; } - u32 actual = lzma_crc32(reinterpret_cast(&block), sizeof(block), 0); - if(crc != actual) { + if(actualCrc != expectedCrc) { + error << "block CRC32 mismatch"; return NULL; } - cout << "block size: " << block.storedSize << " compressed: " << int(block.compressed) << endl; + cout << "[block] size: " << storedSize << " compression: " << compression << endl; io::filtering_istream * fis; fis = new io::filtering_istream; - if(block.compressed) { - fis->push(inno_lzma_decompressor(), 8192); + switch(compression) { + case Stored: break; + case Zlib: fis->push(io::zlib_decompressor(), 8192); break; + case LZMA1: fis->push(inno_lzma_decompressor(), 8192); break; } fis->push(inno_chunk_filter(), 4096); - fis->push(io::restrict(base, 0, block.storedSize)); + fis->push(io::restrict(base, 0, storedSize)); return fis; } diff --git a/src/BlockReader.hpp b/src/BlockReader.hpp index c0f2321..8555634 100644 --- a/src/BlockReader.hpp +++ b/src/BlockReader.hpp @@ -1,10 +1,17 @@ +#ifndef INNOEXTRACT_BLOCKREADER_HPP +#define INNOEXTRACT_BLOCKREADER_HPP + #include +#include "Version.hpp" + class BlockReader { public: - static std::istream * get(std::istream & base); + static std::istream * get(std::istream & base, const InnoVersion & version); }; + +#endif // INNOEXTRACT_BLOCKREADER_HPP diff --git a/src/ChunkFilter.cpp b/src/ChunkFilter.cpp index eb1a3f3..3ebffcb 100644 --- a/src/ChunkFilter.cpp +++ b/src/ChunkFilter.cpp @@ -3,12 +3,14 @@ #include +#include + void inno_chunk_filter::checkCrc(u32 expected) const { u32 actual = lzma_crc32(reinterpret_cast(buffer), length, 0); if(actual != expected) { - std::cout << "[chunk] crc failed" << std::endl; + error << "[chunk] CRC32 mismatch"; throw std::string("chunk CRC32 mismatch"); } diff --git a/src/ChunkFilter.hpp b/src/ChunkFilter.hpp index 596285e..afda40d 100644 --- a/src/ChunkFilter.hpp +++ b/src/ChunkFilter.hpp @@ -1,4 +1,7 @@ +#ifndef INNOEXTRACT_CHUNKFILTER_HPP +#define INNOEXTRACT_CHUNKFILTER_HPP + #include #include #include @@ -7,6 +10,7 @@ #include #include "Types.h" +#include "Output.hpp" class inno_chunk_filter : public boost::iostreams::multichar_input_filter { @@ -26,25 +30,23 @@ public: template bool readChunk(Source & src) { - std::cout << "[chunk] big read" << std::endl; - u32 chunkCrc32 = *reinterpret_cast(buffer); std::streamsize nread = boost::iostreams::read(src, reinterpret_cast(&chunkCrc32), 4); if(nread == -1) { std::cout << "[chunk] end" << std::endl; return false; } else if(nread != 4) { - std::cout << "[chunk] unexpected block end" << std::endl; + error << "[chunk] unexpected block end"; throw std::string("unexpected block end"); } length = boost::iostreams::read(src, buffer, sizeof(buffer)); if(length == (size_t)-1) { - std::cout << "[chunk] unexpected chunk end" << std::endl; + error << "[chunk] unexpected chunk end"; throw std::string("unexpected chunk end"); } - std::cout << "[chunk] -> length=" << length << std::endl; + std::cout << "[chunk] read chunk: " << length << " bytes" << std::endl; checkCrc(chunkCrc32); @@ -56,8 +58,6 @@ public: template std::streamsize read(Source & src, char * dest, std::streamsize n) { - std::cout << "[chunk] small read " << n << std::endl; - size_t read = 0; while(n) { @@ -72,8 +72,6 @@ public: dest += size, n -= size; read += size; - std::cout << "[chunk] +" << size << " remaining: " << (length - pos) << std::endl; - } return read; @@ -86,3 +84,5 @@ private: char buffer[4096]; }; + +#endif // INNOEXTRACT_CHUNKFILTER_HPP diff --git a/src/Enum.hpp b/src/Enum.hpp new file mode 100644 index 0000000..9175d52 --- /dev/null +++ b/src/Enum.hpp @@ -0,0 +1,63 @@ + +#ifndef INNOEXTRACT_ENUM_HPP +#define INNOEXTRACT_ENUM_HPP + +#include +#include +#include "Utils.hpp" +#include "Flags.hpp" +#include "Output.hpp" + +template +struct EnumNames { + + const size_t count; + + const char * name; + + const char * names[0]; + +}; + +#define NAMED_ENUM(Enum) \ + template <> struct EnumNames { \ + static const char * name; \ + static const char * names[]; \ + static const size_t count; \ + }; \ + std::ostream & operator<<(std::ostream & os, Enum value); + +#define ENUM_NAMES(Enum, Name, ...) \ + const char * EnumNames::name = (Name); \ + const char * EnumNames::names[] = { __VA_ARGS__ }; \ + const size_t EnumNames::count = ARRAY_SIZE(EnumNames::names); \ + std::ostream & operator<<(std::ostream & os, Enum value) { \ + if(value < EnumNames::count) { \ + return os << EnumNames::names[value]; \ + } else { \ + return os << "(unknown:" << int(value) << ')'; \ + } \ + } + +template +std::ostream & operator<<(std::ostream & os, Flags flags) { + color::shell_command prev = color::current; + if(flags) { + bool first = true; + for(size_t i = 0; i < Flags::bits; i++) { + if(flags & Enum(i)) { + if(first) { + first = false; + } else { + os << color::dim_white << ", " << prev; + } + os << Enum(i); + } + } + return os; + } else { + return os << color::dim_white << "(none)" << prev; + } +} + +#endif // INNOEXTRACT_ENUM_HPP diff --git a/src/ExeReader.cpp b/src/ExeReader.cpp index e94a50e..10c0c93 100644 --- a/src/ExeReader.cpp +++ b/src/ExeReader.cpp @@ -9,9 +9,9 @@ #include "Types.h" #include "ExeFormat.hpp" #include "Utils.hpp" +#include "Output.hpp" using std::cout; -using std::cerr; using std::string; using std::endl; using std::setw; @@ -23,7 +23,7 @@ size_t ExeReader::findResourceEntry(std::istream & ifs, int id) { CoffResourceTable table; if(read(ifs, table).fail()) { - cerr << "error reading resource table" << endl; + error << "error reading resource table"; return 0; } @@ -37,7 +37,7 @@ size_t ExeReader::findResourceEntry(std::istream & ifs, int id) { CoffResourceEntry entry; if(read(ifs, entry).fail()) { - cerr << "error reading resource table entry" << endl; + error << "error reading resource table entry"; return 0; } @@ -61,7 +61,7 @@ bool ExeReader::loadSectionTable(std::istream & ifs, size_t peOffset, const Coff ifs.seekg(sectionTableOffset); if(ifs.read(reinterpret_cast(table.data()), sizeof(CoffSection) * table.size()).fail()) { - cerr << "error coff loading section table"; + error << "error coff loading section table"; return false; } @@ -94,31 +94,31 @@ ExeReader::Resource ExeReader::findResource(std::istream & is, int name, int typ u16 peOffset; if(read(is.seekg(0x3c), peOffset).fail()) { - cerr << "error reading PE signature offset"; + error << "error reading PE signature offset"; return result; } cout << "PE signature is @ " << hex << peOffset << dec << endl; char magic[4]; if(is.seekg(peOffset).read(magic, 4).fail()) { - cerr << "error reading PE signature" << endl; + error << "error reading PE signature"; return result; } static const char expectedMagic[] = { 'P', 'E', 0, 0 }; if(std::memcmp(magic, expectedMagic, 4)) { - cerr << "wrong PE signature - not an exe file" << endl; + error << "wrong PE signature - not an exe file"; return result; } CoffFileHeader coff; if(read(is, coff).fail()) { - cerr << "error reading COFF file header" << endl; + error << "error reading COFF file header"; return result; } u16 optionalHeaderMagic; if(read(is, optionalHeaderMagic).fail()) { - cerr << "error reading the optional header magic number" << endl; + error << "error reading the optional header magic number"; return result; } @@ -131,22 +131,22 @@ ExeReader::Resource ExeReader::findResource(std::istream & is, int name, int typ u32 ndirectories; if(read(is, ndirectories).fail()) { - cerr << "error reading number of data directories" << endl; + error << "error reading number of data directories"; return result; } cout << "number of directories is " << ndirectories << endl; if(ndirectories < 3) { - cerr << "no resource directory found" << endl; + error << "no resource directory found"; return result; } CoffDataDirectory resources; if(read(is.seekg(16, strm::cur), resources).fail()) { - cerr << "error reading resource directory offset"; + error << "error reading resource directory offset"; return result; } if(!resources.address || !resources.size) { - cerr << "missing resource directory" << endl; + error << "missing resource directory"; return result; } @@ -157,7 +157,7 @@ ExeReader::Resource ExeReader::findResource(std::istream & is, int name, int typ size_t resourceOffset = memoryAddressToFileOffset(sections, resources.address); if(!resourceOffset) { - cerr << "error mapping virtual resource address " << hex << resources.address << dec << " to file offset" << endl; + error << "error mapping virtual resource address " << hex << resources.address << dec << " to file offset"; return result; } cout << "resource table is @ RVA " << hex << resources.address << " -> @ " << resourceOffset << dec << endl; @@ -166,11 +166,11 @@ ExeReader::Resource ExeReader::findResource(std::istream & is, int name, int typ u32 typeOffset = findResourceEntry(is, type); if(!typeOffset) { - cerr << "missing data resource entry" << endl; + error << "missing data resource entry"; return result; } if(!(typeOffset & (1 << 31))) { - cerr << "unexpected resource leaf for data" << endl; + error << "unexpected resource leaf for data"; return result; } typeOffset &= ~(1 << 31), typeOffset += resourceOffset; @@ -181,11 +181,11 @@ ExeReader::Resource ExeReader::findResource(std::istream & is, int name, int typ u32 nameOffset = findResourceEntry(is, name); if(!nameOffset) { - cerr << "missing installer resource entry" << endl; + error << "missing installer resource entry"; return result; } if(!(nameOffset & (1 << 31))) { - cerr << "unexpected resource leaf for installer" << endl; + error << "unexpected resource leaf for installer"; return result; } nameOffset &= ~(1 << 31), nameOffset += resourceOffset; @@ -196,11 +196,11 @@ ExeReader::Resource ExeReader::findResource(std::istream & is, int name, int typ u32 finalOffset = findResourceEntry(is, language); if(!finalOffset) { - cerr << "missing final resource entry" << endl; + error << "missing final resource entry"; return result; } if(finalOffset & (1 << 31)) { - cerr << "unexpected table for final resource entry" << endl; + error << "unexpected table for final resource entry"; return result; } finalOffset += resourceOffset; @@ -209,7 +209,7 @@ ExeReader::Resource ExeReader::findResource(std::istream & is, int name, int typ CoffResourceLeaf leaf; if(read(is.seekg(finalOffset), leaf).fail()) { - cerr << "error loading final resource entry" << endl; + error << "error loading final resource entry"; return result; } @@ -217,7 +217,7 @@ ExeReader::Resource ExeReader::findResource(std::istream & is, int name, int typ size_t dataOffset = memoryAddressToFileOffset(sections, leaf.address); if(!dataOffset) { - cerr << "error mapping final virtual resource address " << hex << leaf.address << dec << " to file offset" << endl; + error << "error mapping final virtual resource address " << hex << leaf.address << dec << " to file offset"; return result; } diff --git a/src/Flags.hpp b/src/Flags.hpp new file mode 100644 index 0000000..b92295a --- /dev/null +++ b/src/Flags.hpp @@ -0,0 +1,171 @@ + +#ifndef INNOEXTRACT_FLAGS_HPP +#define INNOEXTRACT_FLAGS_HPP + +#include + +// loosely based on QFlags from Qt + +template +class EnumSize { static const size_t value = 32; }; + +/*! + * A typesafe way to define flags as a combination of enum values. + * + * This type should not be used directly, only through DECLARE_FLAGS. + */ +template +class Flags { + +public: + + typedef _Enum Enum; + static const size_t bits = EnumSize::value; + typedef std::bitset Type; + +private: + + typedef void ** Zero; + typedef void(*TypesafeBoolean)(); + + Type flags; + + inline Flags(Type flag) : flags(flag) { } + +public: + + inline Flags(Enum flag) : flags(Type().set(size_t(flag))) { } + + inline Flags(Zero = 0) : flags() { } + + inline Flags(const Flags & o) : flags(o.flags) { } + + static inline Flags load(Type flags) { + return Flags(flags, true); + } + + inline bool has(Enum flag) const { + return flags.test(size_t(flag)); + } + + inline bool hasAll(Flags o) const { + return (flags & o.flags) == o.flags; + } + + inline operator TypesafeBoolean() const { + return reinterpret_cast(flags.any()); + } + + inline Flags operator~() const { + return Flags(~flags); + } + + inline bool operator!() const { + return flags.none(); + } + + inline Flags operator&(Flags o) const { + return Flags(flags & o.flags); + } + + inline Flags operator|(Flags o) const { + return Flags(flags | o.flags); + } + + inline Flags operator^(Flags o) const { + return Flags(flags ^ o.flags); + } + + inline Flags & operator&=(const Flags & o) { + flags &= o.flags; + return *this; + } + + inline Flags & operator|=(Flags o) { + flags |= o.flags; + return *this; + } + + inline Flags & operator^=(Flags o) { + flags ^= o.flags; + return *this; + } + + inline Flags operator&(Enum flag) const { + return operator&(Flags(flag)); + } + + inline Flags operator|(Enum flag) const { + return operator|(Flags(flag)); + } + + inline Flags operator^(Enum flag) const { + return operator^(Flags(flag)); + } + + inline Flags & operator&=(Enum flag) { + + return operator&=(Flags(flag)); + } + + inline Flags & operator|=(Enum flag) { + return operator|=(Flags(flag)); + } + + inline Flags & operator^=(Enum flag) { + return operator^=(flag); + } + + inline Flags & operator=(Flags o) { + flags = o.flags; + return *this; + } + + static inline Flags all() { + return Flags(Type().flip()); + } + +}; + +/*! + * Declare a flag type using values from a given enum. + * This should always be used instead of using Flags<Enum> directly. + * + * @param Enum should be an enum with values that have exactly one bit set. + * @param Flagname is the name for the flag type to be defined. + */ +#define DECLARE_FLAGS_SIZE(Enum, Flagname, Size) \ + template <> \ + struct EnumSize { \ + static const size_t value = (Size); \ + }; \ + typedef Flags Flagname; +#define FLAGS_ENUM_END_HELPER(Enum) Enum ## __End +#define FLAGS_ENUM_END(Enum) FLAGS_ENUM_END_HELPER(Enum) +#define DECLARE_FLAGS(Enum, Flagname) DECLARE_FLAGS_SIZE(Enum, Flagname, FLAGS_ENUM_END(Enum)) + +/*! + * Declare overloaded operators for a given flag type. + */ +#define DECLARE_FLAGS_OPERATORS(Flagname) \ + inline Flagname operator|(Flagname::Enum a, Flagname::Enum b) { \ + return Flagname(a) | b; \ + } \ + inline Flagname operator|(Flagname::Enum a, Flagname b) { \ + return b | a; \ + } \ + inline Flagname operator~(Flagname::Enum a) { \ + return ~Flagname(a); \ + } +// TODO prevent combination with integers! + +#define FLAGS_ENUM(Flagname) Flagname ## __Enum +#define FLAGS(Flagname, ...) \ + enum FLAGS_ENUM(Flagname) { \ + __VA_ARGS__ \ + FLAGS_ENUM_END(Flagname) \ + }; \ + DECLARE_FLAGS_SIZE(FLAGS_ENUM(Flagname), Flagname, FLAGS_ENUM_END(Flagname)) \ + DECLARE_FLAGS_OPERATORS(Flagname) + +#endif // INNOEXTRACT_FLAGS_HPP diff --git a/src/InnoExtract.cpp b/src/InnoExtract.cpp index 8bc12d2..9460cc6 100644 --- a/src/InnoExtract.cpp +++ b/src/InnoExtract.cpp @@ -12,13 +12,13 @@ #include #include "Types.h" +#include "SetupHeader.hpp" #include "SetupLoader.hpp" #include "Utils.hpp" - +#include "Output.hpp" #include "BlockReader.hpp" using std::cout; -using std::cerr; using std::string; using std::endl; using std::setw; @@ -38,93 +38,185 @@ struct BlockHeader { int main(int argc, char * argv[]) { if(argc <= 1) { - cerr << "usage: innoextract " << endl; + std::cout << "usage: innoextract " << endl; return 1; } std::ifstream ifs(argv[1], strm::in | strm::binary | strm::ate); if(!ifs.is_open()) { - cerr << "error opening file" << endl; + error << "error opening file"; return 1; } u64 fileSize = ifs.tellg(); if(!fileSize) { - cerr << "cannot read file or empty file" << endl; + error << "cannot read file or empty file"; return 1; } SetupLoader::Offsets offsets; if(!SetupLoader::getOffsets(ifs, offsets)) { - cerr << "failed to load setup loader offsets" << endl; + error << "failed to load setup loader offsets"; + // TODO try offset0 = 0 return 1; } + cout << color::white; cout << "loaded offsets:" << endl; cout << "- total size: " << offsets.totalSize << endl; cout << "- exe: @ " << hex << offsets.exeOffset << dec << " compressed: " << offsets.exeCompressedSize << " uncompressed: " << offsets.exeUncompressedSize << endl; cout << "- exe checksum: " << hex << setfill('0') << setw(8) << offsets.exeChecksum << dec << " (" << (offsets.exeChecksumMode == ChecksumAdler32 ? "Alder32" : "CRC32") << ')' << endl; cout << "- messageOffset: " << hex << offsets.messageOffset << dec << endl; cout << "- offset: 0: " << hex << offsets.offset0 << " 1: " << offsets.messageOffset << dec << endl; + cout << color::reset; ifs.seekg(offsets.offset0); - char version[64]; - if(read(ifs, version).fail()) { - cerr << "error reading version!" << endl; + InnoVersion version; + version.load(ifs); + if(ifs.fail()) { + error << "error reading setup data version!"; return 1; } - cout << "version: \"" << safestring(version) << '"' << endl; - std::istream * _is = BlockReader::get(ifs); + cout << "version: " << color::white << version << color::reset << endl; + + std::istream * _is = BlockReader::get(ifs, version); if(!_is) { - cerr << "error reading block" << endl; + error << "error reading block"; return 1; } std::istream & is = *_is; - std::string strings[29]; - - for(size_t i = 0; i < sizeof(strings)/sizeof(*strings); i++) { - - u32 size; - if(read(is, size).fail()) { - cerr << "error reading string size #" << i << endl; - return 1; - } - - strings[i].resize(size); - - if(is.read(&strings[i][0], size).fail()) { - cerr << "error reading string #" << i << endl; - return 1; - } - + is.exceptions(strm::badbit | strm::failbit); + + /* + std::ofstream ofs("dump.bin", strm::trunc | strm::out | strm::binary); + + do { + char buf[4096]; + size_t in = is.read(buf, ARRAY_SIZE(buf)).gcount(); + cout << in << endl; + ofs.write(buf, in); + } while(!is.eof()); + + if(is.bad()) { + error << "read error"; + } else if(!is.eof()) { + warning << "not eof"; + } + + return 0;*/ + + SetupHeader header; + header.load(is, version); + if(is.fail()) { + error << "error reading setup data header!"; + return 1; + } + + cout << IfNotEmpty("App name", header.appName); + cout << IfNotEmpty("App ver name", header.appVerName); + cout << IfNotEmpty("App id", header.appId); + cout << IfNotEmpty("Copyright", header.appCopyright); + cout << IfNotEmpty("Publisher", header.appPublisher); + cout << IfNotEmpty("Publisher URL", header.appPublisherURL); + cout << IfNotEmpty("Support phone", header.appSupportPhone); + cout << IfNotEmpty("Support URL", header.appSupportURL); + cout << IfNotEmpty("Updates URL", header.appUpdatesURL); + cout << IfNotEmpty("Version", header.appVersion); + cout << IfNotEmpty("Default dir name", header.defaultDirName); + cout << IfNotEmpty("Default group name", header.defaultGroupName); + cout << IfNotEmpty("Uninstall icon name", header.uninstallIconName); + cout << IfNotEmpty("Base filename", header.baseFilename); + cout << IfNotEmpty("Uninstall files dir", header.uninstallFilesDir); + cout << IfNotEmpty("Uninstall display name", header.uninstallDisplayName); + cout << IfNotEmpty("Uninstall display icon", header.uninstallDisplayIcon); + cout << IfNotEmpty("App mutex", header.appMutex); + cout << IfNotEmpty("Default user name", header.defaultUserInfoName); + cout << IfNotEmpty("Default user org", header.defaultUserInfoOrg); + cout << IfNotEmpty("Default user serial", header.defaultUserInfoSerial); + cout << IfNotEmpty("Readme", header.appReadmeFile); + cout << IfNotEmpty("Contact", header.appContact); + cout << IfNotEmpty("Comments", header.appComments); + cout << IfNotEmpty("Modify path", header.appModifyPath); + cout << IfNotEmpty("Uninstall reg key", header.createUninstallRegKey); + cout << IfNotEmpty("Uninstallable", header.uninstallable); + cout << IfNotEmpty("License", header.licenseText); + cout << IfNotEmpty("Info before text", header.infoBeforeText); + cout << IfNotEmpty("Info after text", header.infoAfterText); + cout << IfNotEmpty("Uninstaller signature", header.signedUninstallerSignature); + cout << IfNotEmpty("Compiled code", header.compiledCodeText); + + cout << IfNotZero("Lead bytes", header.leadBytes); + + cout << IfNotZero("Language entries", header.numLanguageEntries); + cout << IfNotZero("Custom message entries", header.numCustomMessageEntries); + cout << IfNotZero("Permission entries", header.numPermissionEntries); + cout << IfNotZero("Type entries", header.numTypeEntries); + cout << IfNotZero("Component entries", header.numComponentEntries); + cout << IfNotZero("Task entries", header.numTaskEntries); + cout << IfNotZero("Dir entries", header.numDirEntries); + cout << IfNotZero("File entries", header.numFileEntries); + cout << IfNotZero("File location entries", header.numFileLocationEntries); + cout << IfNotZero("Icon entries", header.numIconEntries); + cout << IfNotZero("Ini entries", header.numIniEntries); + cout << IfNotZero("Registry entries", header.numRegistryEntries); + cout << IfNotZero("Delete entries", header.numInstallDeleteEntries); + cout << IfNotZero("Uninstall delete entries", header.numUninstallDeleteEntries); + cout << IfNotZero("Run entries", header.numRunEntries); + cout << IfNotZero("Uninstall run entries", header.numUninstallRunEntries); + + cout << IfNotZero("License size", header.licenseSize); + cout << IfNotZero("Info before size", header.infoBeforeSize); + cout << IfNotZero("Info after size", header.infoAfterSize); + + cout << "Min version: " << header.minVersion << endl; + if(header.onlyBelowVersion.winVersion || header.onlyBelowVersion.ntVersion || header.onlyBelowVersion.ntServicePack) { + cout << "Only below version: " << header.onlyBelowVersion << endl; } - const char * names[29] = { - "App Name", "App Ver Name", "App Id", "Copyright", "Publisher", "Publisher URL", - "SupportPhone", "Support URL", "Updates URL", "Version", "Default Dir Name", - "Default Group Name", "Base Filename", "License Text", - "Info Before Text", "Info After Text", "Uninstall Files Dir", "Uninstall Display Name", - "Uninstall Display Icon", "App Mutex", "Default User Info Name", - "Default User Info Org", "Default User Info Serial", "Compiled Code Text", - "Readme", "Contact", "Comments", "App Modify Path", - "Signed Uninstaller Signature" - }; - - for(size_t i = 0; i < sizeof(strings)/sizeof(*strings); i++) { - if(i != 23) { - cout << "- " << names[i] << ": \"" << strings[i] << '"' << endl; - } else { - cout << "- " << names[i] << ": " << strings[i].length() << " bytes" << endl; - } + cout << hex; + cout << IfNotZero("Back color", header.backColor); + cout << IfNotZero("Back color2", header.backColor2); + cout << IfNotZero("Wizard image back color", header.wizardImageBackColor); + cout << IfNotZero("Wizard small image back color", header.wizardSmallImageBackColor); + cout << dec; + + if(header.options & (shPassword|shEncryptionUsed)) { + cout << "Password type: " << color::cyan << header.passwordType << color::reset << endl; + // TODO print password + // TODO print salt } + cout << IfNotZero("Extra disk space required", header.extraDiskSpaceRequired); + cout << IfNotZero("Slices per disk", header.slicesPerDisk); + + cout << IfNot("Install mode", header.installMode, SetupHeader::NormalInstallMode); + cout << "Uninstall log mode: " << color::cyan << header.uninstallLogMode << color::reset << endl; + cout << "Uninstall style: " << color::cyan << header.uninstallStyle << color::reset << endl; + cout << "Dir exists warning: " << color::cyan << header.dirExistsWarning << color::reset << endl; + cout << IfNot("Privileges required", header.privilegesRequired, SetupHeader::NoPrivileges); + cout << "Show language dialog: " << color::cyan << header.showLanguageDialog << color::reset << endl; + cout << IfNot("Danguage detection", header.languageDetectionMethod, SetupHeader::NoLanguageDetection); + cout << "Compression: " << color::cyan << header.compressMethod << color::reset << endl; + cout << "Architectures allowed: " << color::cyan << header.architecturesAllowed << color::reset << endl; + cout << "Architectures installed in 64-bit mode: " << color::cyan << header.architecturesInstallIn64BitMode << color::reset << endl; + + if(header.options & shSignedUninstaller) { + cout << IfNotZero("Size before signing uninstaller", header.signedUninstallerOrigSize); + cout << IfNotZero("Uninstaller header checksum", header.signedUninstallerHdrChecksum); + } + + cout << "Disable dir page: " << color::cyan << header.disableDirPage << color::reset << endl; + cout << "Disable program group page: " << color::cyan << header.disableProgramGroupPage << color::reset << endl; + + cout << IfNotZero("Uninstall display size", header.uninstallDisplaySize); - delete _is; + cout << "Options: " << color::green << header.options << color::reset << endl; + cout << color::reset; return 0; } diff --git a/src/LoadingUtils.cpp b/src/LoadingUtils.cpp new file mode 100644 index 0000000..45c5cc6 --- /dev/null +++ b/src/LoadingUtils.cpp @@ -0,0 +1,87 @@ + +#include "LoadingUtils.hpp" + +#include + +#include +#include + +#include "Output.hpp" +#include "Utils.hpp" + +void BinaryString::loadInto(std::istream & is, std::string & target) { + + size_t length = loadNumber(is); + if(is.fail()) { + return; + } + + target.resize(length); + is.read(&target[0], length); +} + +static void convert(iconv_t converter, const std::string & from, std::string & to) { + + const char * inbuf = from.data(); + size_t insize = from.size(); + + size_t outbase = 0; + + if(!insize) { + to.clear(); + return; + } + + iconv(converter, NULL, NULL, NULL, NULL); + + while(insize) { + + to.resize(outbase + insize); + + char * outbuf = &to[0] + outbase; + size_t outsize = to.size() - outbase; + + size_t ret = iconv(converter, const_cast(&inbuf), &insize, &outbuf, &outsize); + if(ret == size_t(-1) && errno != E2BIG) { + error << "iconv error"; + to.clear(); + return; + } + + outbase = to.size() - outsize; + } + +} + +void AnsiString::loadInto(std::istream & is, std::string & target) { + + std::string temp; + BinaryString::loadInto(is, temp); + + static iconv_t converter = NULL; + if(!converter) { + converter = iconv_open("UTF-8", "CP1252"); + if(!converter) { + error << "missing CP1252 -> UTF-8 converter"; + } + } + + convert(converter, temp, target); +} + +void WideString::loadInto(std::istream & is, std::string & target) { + + std::string temp; + BinaryString::loadInto(is, temp); + + static iconv_t converter = NULL; + if(!converter) { + converter = iconv_open("UTF-8", "UTF-16"); + if(!converter) { + error << "missing UTF-16 -> UTF-8 converter"; + } + } + + convert(converter, temp, target); + +} diff --git a/src/LoadingUtils.hpp b/src/LoadingUtils.hpp new file mode 100644 index 0000000..adf2986 --- /dev/null +++ b/src/LoadingUtils.hpp @@ -0,0 +1,119 @@ + +#ifndef INNOEXTRACT_LOADINGUTILS_HPP +#define INNOEXTRACT_LOADINGUTILS_HPP + +#include +#include +#include +#include +#include "Types.h" + +inline u8 fromLittleEndian(u8 value) { return value; } +inline s8 fromLittleEndian(s8 value) { return value; } + +#ifdef BOOST_LITTLE_ENDIAN + +inline u16 fromLittleEndian(u16 value) { return value; } +inline u32 fromLittleEndian(u32 value) { return value; } +inline u64 fromLittleEndian(u64 value) { return value; } +inline s16 fromLittleEndian(s16 value) { return value; } +inline s32 fromLittleEndian(s32 value) { return value; } +inline s64 fromLittleEndian(s64 value) { return value; } + +#else + +// TODO implement! +#error "Host endianness not supported!" + +#endif + +struct BinaryString { + + std::string & data; + + inline BinaryString(std::string & target) : data(target) { } + + static void loadInto(std::istream & is, std::string & target); + +}; +inline std::istream & operator>>(std::istream & is, const BinaryString & str) { + BinaryString::loadInto(is, str.data); + return is; +} + +struct AnsiString { + + std::string & data; + + inline AnsiString(std::string & target) : data(target) { } + + static void loadInto(std::istream & is, std::string & target); + +}; + +inline std::istream & operator>>(std::istream & is, const AnsiString & str) { + AnsiString::loadInto(is, str.data); + return is; +} + +struct WideString { + + std::string & data; + bool wide; + + inline WideString(std::string & target, bool _wide /*= true*/) : data(target), wide(_wide) { } + + static void loadInto(std::istream & is, std::string & target); + +}; + +inline std::istream & operator>>(std::istream & is, const WideString & str) { + str.wide ? WideString::loadInto(is, str.data) : AnsiString::loadInto(is, str.data); + return is; +} + +template +inline T load(std::istream & is) { + T value; + is.read(reinterpret_cast(&value), sizeof(value)); + return value; +} + +template +inline T loadNumber(std::istream & is) { + return fromLittleEndian(load(is)); +} + +template ::is_signed> +struct compatible_integer { typedef void type; }; +template +struct compatible_integer { typedef u8 type; }; +template +struct compatible_integer { typedef s8 type; }; +template +struct compatible_integer { typedef u16 type; }; +template +struct compatible_integer { typedef s16 type; }; +template +struct compatible_integer { typedef u32 type; }; +template +struct compatible_integer { typedef s32 type; }; +template +struct compatible_integer { typedef u64 type; }; +template +struct compatible_integer { typedef s64 type; }; + +template +T loadNumber(std::istream & is, size_t bits) { + if(bits == 8) { + return loadNumber::type>(is); + } else if(bits == 16) { + return loadNumber::type>(is); + } else if(bits == 32) { + return loadNumber::type>(is); + } else { + return loadNumber::type>(is); + } +} + +#endif // INNOEXTRACT_LOADINGUTILS_HPP diff --git a/src/LzmaFilter.cpp b/src/LzmaFilter.cpp index 7ee368e..7e1ab55 100644 --- a/src/LzmaFilter.cpp +++ b/src/LzmaFilter.cpp @@ -17,7 +17,7 @@ inno_lzma_decompressor_impl::~inno_lzma_decompressor_impl() { close(); } bool inno_lzma_decompressor_impl::filter(const char * & begin_in, const char * end_in, char * & begin_out, char * end_out, bool flush) { (void)flush; - cout << "[lzma] filter " << (end_in - begin_in) << " -> " << (end_out - begin_out) << endl; + size_t bufsize_in = (end_in - begin_in), bufsize_out = (end_out - begin_out); // Read enough bytes to decode the header. while(nread != 5) { @@ -81,7 +81,7 @@ bool inno_lzma_decompressor_impl::filter(const char * & begin_in, const char * e lzma_ret ret = lzma_code(strm, LZMA_RUN); - cout << "[lzma] consumed " << (reinterpret_cast(strm->next_in) - begin_in) << " -> " << (reinterpret_cast(strm->next_out) - begin_out) << " ret=" << ret << endl; + cout << "[lzma] decompressed " << (reinterpret_cast(strm->next_in) - begin_in) << " / " << bufsize_in << " -> " << (reinterpret_cast(strm->next_out) - begin_out) << " / " << bufsize_out << " ret=" << ret << endl; begin_in = reinterpret_cast(strm->next_in); diff --git a/src/Output.cpp b/src/Output.cpp new file mode 100644 index 0000000..799d677 --- /dev/null +++ b/src/Output.cpp @@ -0,0 +1,8 @@ + +#include "Output.hpp" + +namespace color { + +shell_command current = color::reset; + +} diff --git a/src/Output.hpp b/src/Output.hpp new file mode 100644 index 0000000..77ebd94 --- /dev/null +++ b/src/Output.hpp @@ -0,0 +1,66 @@ + +#ifndef INNOEXTRACT_COLOROUT_HPP +#define INNOEXTRACT_COLOROUT_HPP + +#include +#include + +namespace color { + + struct shell_command { + + int c0; + int c1; + + }; + + const shell_command reset = { 0, 0 }; + + const shell_command black = { 1, 30 }; + const shell_command red = { 1, 31 }; + const shell_command green = { 1, 32 }; + const shell_command yellow = { 1, 33 }; + const shell_command blue = { 1, 34 }; + const shell_command magenta = { 1, 35 }; + const shell_command cyan = { 1, 36 }; + const shell_command white = { 1, 37 }; + + const shell_command dim_black = { 0, 30 }; + const shell_command dim_red = { 0, 31 }; + const shell_command dim_green = { 0, 32 }; + const shell_command dim_yellow = { 0, 33 }; + const shell_command dim_blue = { 0, 34 }; + const shell_command dim_magenta = { 0, 35 }; + const shell_command dim_cyan = { 0, 36 }; + const shell_command dim_white = { 0, 37 }; + + extern shell_command current; + +}; + +inline std::ostream & operator<<(std::ostream & os, const color::shell_command command) { + color::current = command; + std::ios_base::fmtflags old = os.flags(); + os << "\x1B[" << std::dec << command.c0 << ';' << command.c1 << 'm'; + os.setf(old, std::ios_base::basefield); + return os; +} + +struct error_base { + + color::shell_command previous; + + inline error_base(color::shell_command type) : previous(color::current) { + std::cerr << type << "!! "; + } + + inline ~error_base() { + std::cerr << previous << std::endl; + } + +}; + +#define error (error_base(color::red), std::cerr) +#define warning (error_base(color::yellow), std::cerr) + +#endif // INNOEXTRACT_COLOROUT_HPP diff --git a/src/SetupHeader.cpp b/src/SetupHeader.cpp new file mode 100644 index 0000000..1bbf306 --- /dev/null +++ b/src/SetupHeader.cpp @@ -0,0 +1,586 @@ + +#include "SetupHeader.hpp" + +#include +#include +#include + +#include "LoadingUtils.hpp" +#include "SetupHeaderFormat.hpp" +#include "Utils.hpp" + +void SetupVersionData::load(std::istream & is, const InnoVersion & version) { + + if(version <= INNO_VERSION(1, 2, 16)) { // in 1.2.16, not in 1.3.25 + winVersion = loadNumber(is); + ntVersion = loadNumber(is); + ntServicePack = 0; + } else { + winVersion = loadNumber(is); + ntVersion = loadNumber(is); + ntServicePack = loadNumber(is); + } + +} + +std::ostream & operator<<(std::ostream & os, const SetupVersionData & svd) { + return os << " win " << svd.winVersion << " nt " << svd.ntVersion << " service pack " << svd.ntServicePack; +} + +void SetupHeader::load(std::istream & is, const InnoVersion & version) { + + options = 0; + + if(version <= INNO_VERSION(1, 2, 16)) { + loadNumber(is); // uncompressed size of the setup header structure + } + + is >> WideString(appName, version.unicode); + is >> WideString(appVerName, version.unicode); + if(version > INNO_VERSION(1, 2, 16)) { // not in 1.2.16, in 1.3.25 + is >> WideString(appId, version.unicode); + } + is >> WideString(appCopyright, version.unicode); + if(version > INNO_VERSION(1, 2, 16)) { // not in 1.2.16, in 1.3.25 + is >> WideString(appPublisher, version.unicode); + is >> WideString(appPublisherURL, version.unicode); + } else { + appPublisher.clear(), appPublisherURL.clear(); + } + if(version >= INNO_VERSION(5, 1, 13)) { + is >> WideString(appSupportPhone, version.unicode); + } else { + appSupportPhone.clear(); + } + if(version > INNO_VERSION(1, 2, 16)) { // not in 1.2.16, in 1.3.25 + is >> WideString(appSupportURL, version.unicode); + is >> WideString(appUpdatesURL, version.unicode); + is >> WideString(appVersion, version.unicode); + } else { + appSupportURL.clear(), appUpdatesURL.clear(), appVersion.clear(); + } + is >> WideString(defaultDirName, version.unicode); + is >> WideString(defaultGroupName, version.unicode); + if(version < INNO_VERSION(3, 0, 0)) { + is >> AnsiString(uninstallIconName); + } else { + uninstallIconName.clear(); + } + is >> WideString(baseFilename, version.unicode); + if(version > INNO_VERSION(1, 2, 16)) { // not in 1.2.16, in 1.3.25 + if(version < INNO_VERSION(5, 2, 5)) { + is >> AnsiString(licenseText); + is >> AnsiString(infoBeforeText); + is >> AnsiString(infoAfterText); + } + is >> WideString(uninstallFilesDir, version.unicode); + is >> WideString(uninstallDisplayName, version.unicode); + is >> WideString(uninstallDisplayIcon, version.unicode); + is >> WideString(appMutex, version.unicode); + } else { + licenseText.clear(), infoBeforeText.clear(), infoAfterText.clear(); + uninstallFilesDir.clear(), uninstallDisplayName.clear(); + uninstallDisplayIcon.clear(), appMutex.clear(); + } + if(version >= INNO_VERSION(3, 0, 0)) { + is >> WideString(defaultUserInfoName, version.unicode); + is >> WideString(defaultUserInfoOrg, version.unicode); + } else { + defaultUserInfoName.clear(), defaultUserInfoOrg.clear(); + } + if(version >= INNO_VERSION_EXT(3, 0, 6, 1)) { + is >> WideString(defaultUserInfoSerial, version.unicode); + if(version < INNO_VERSION(5, 2, 5)) { + is >> BinaryString(compiledCodeText); + } + } else { + defaultUserInfoSerial.clear(), compiledCodeText.clear(); + } + if(version >= INNO_VERSION(4, 2, 4)) { + is >> WideString(appReadmeFile, version.unicode); + is >> WideString(appContact, version.unicode); + is >> WideString(appComments, version.unicode); + is >> WideString(appModifyPath, version.unicode); + } else { + appReadmeFile.clear(), appContact.clear(); + appComments.clear(), appModifyPath.clear(); + } + if(version >= INNO_VERSION(5, 3, 8)) { + is >> WideString(createUninstallRegKey, version.unicode); + } else { + createUninstallRegKey.clear(); + } + if(version >= INNO_VERSION(5, 3, 10)) { + is >> WideString(uninstallable, version.unicode); + } else { + uninstallable.clear(); + } + if(version >= INNO_VERSION(5, 2, 5)) { + is >> AnsiString(licenseText); + is >> AnsiString(infoBeforeText); + is >> AnsiString(infoAfterText); + } + if(version >= INNO_VERSION(5, 2, 1) && version < INNO_VERSION(5, 3, 10)) { + is >> BinaryString(signedUninstallerSignature); + } else { + signedUninstallerSignature.clear(); + } + if(version >= INNO_VERSION(5, 2, 5)) { + is >> BinaryString(compiledCodeText); + } + + if(version > INNO_VERSION(1, 3, 26) && !version.unicode) { + leadBytes = CharSet(is).getBitSet(); + } else { + leadBytes = 0; + } + + if(version >= INNO_VERSION(4, 0, 0)) { + numLanguageEntries = loadNumber(is); + } else { + numLanguageEntries = 0; + } + + if(version >= INNO_VERSION(4, 2, 1)) { + numCustomMessageEntries = loadNumber(is); + } else { + numCustomMessageEntries = 0; + } + + if(version >= INNO_VERSION(4, 1, 0)) { + numPermissionEntries = loadNumber(is); + } else { + numPermissionEntries = 0; + } + + if(version > INNO_VERSION(1, 3, 26)) { // not in 1.3.26, in 2.0.8 + numTypeEntries = loadNumber(is); + numComponentEntries = loadNumber(is); + numTaskEntries = loadNumber(is); + } else { + numTypeEntries = 0, numComponentEntries = 0, numTaskEntries = 0; + } + + numDirEntries = loadNumber(is, version.bits); + numFileEntries = loadNumber(is, version.bits); + numFileLocationEntries = loadNumber(is, version.bits); + numIconEntries = loadNumber(is, version.bits); + numIniEntries = loadNumber(is, version.bits); + numRegistryEntries = loadNumber(is, version.bits); + numInstallDeleteEntries = loadNumber(is, version.bits); + numUninstallDeleteEntries = loadNumber(is, version.bits); + numRunEntries = loadNumber(is, version.bits); + numUninstallRunEntries = loadNumber(is, version.bits); + + if(version <= INNO_VERSION(1, 2, 16)) { // in 1.2.16, not in 1.3.25 + licenseSize = loadNumber(is, version.bits); + infoBeforeSize = loadNumber(is, version.bits); + infoAfterSize = loadNumber(is, version.bits); + } else { + licenseSize = infoBeforeSize = infoAfterSize = 0; + } + + minVersion.load(is, version); + onlyBelowVersion.load(is, version); + + backColor = loadNumber(is); + if(version > INNO_VERSION(1, 2, 16)) { // not in 1.2.16, in 1.3.25 + backColor2 = loadNumber(is); + } else { + backColor2 = 0; + } + wizardImageBackColor = loadNumber(is); + if(version > INNO_VERSION(1, 3, 26) && version < INNO_VERSION(5, 0, 4)) { // not in 1.3.26, in 2.0.8 + wizardSmallImageBackColor = loadNumber(is); + } else { + wizardSmallImageBackColor = 0; + } + + if(version < INNO_VERSION(4, 2, 0)) { + password = loadNumber(is), passwordType = PlainPassword; + } else if(version < INNO_VERSION(5, 3, 9)) { + is.read(passwordMd5, sizeof(passwordMd5)), passwordType = Md5Password; + } else { + is.read(passwordSha1, sizeof(passwordSha1)), passwordType = Sha1Password; + } + if(version >= INNO_VERSION(4, 2, 2)) { + is.read(passwordSalt, sizeof(passwordSalt)); + } else { + memset(passwordSalt, 0, sizeof(passwordSalt)); + } + + if(version < INNO_VERSION(4, 0, 0)) { + extraDiskSpaceRequired = loadNumber(is); + slicesPerDisk = 0; + } else { + extraDiskSpaceRequired = loadNumber(is); + slicesPerDisk = loadNumber(is); + } + + if(version > INNO_VERSION(1, 3, 26) && version < INNO_VERSION(5, 0, 0)) { + // removed in 5.0.0, not in 1.2.10, not in 1.3.25 + installMode = StoredEnum(is).get(); + } else { + installMode = NormalInstallMode; + } + + if(version > INNO_VERSION(1, 2, 16)) { // not in 1.2.16, in 1.3.25 + uninstallLogMode = StoredEnum(is).get(); + } else { + uninstallLogMode = AppendLog; + } + + if(version > INNO_VERSION(1, 3, 26) && version < INNO_VERSION(5, 0, 0)) { + uninstallStyle = StoredEnum(is).get(); + } else { + uninstallStyle = (version < INNO_VERSION(5, 0, 0)) ? ClassicStyle : ModernStyle; + } + + if(version > INNO_VERSION(1, 2, 16)) { // not in 1.2.16, in 1.3.25 + dirExistsWarning = StoredEnum(is).get(); + } else { + dirExistsWarning = Auto; + } + + if(version >= INNO_VERSION(3, 0, 0) && version < INNO_VERSION(3, 0, 3)) { // only in [3.0.0, 3.0.3)? + AutoBoolean val = StoredEnum(is).get(); + switch(val) { + case Yes: options |= shAlwaysRestart; break; + case Auto: options |= shRestartIfNeededByRun; break; + case No: break; + } + } + + if(version >= INNO_VERSION(5, 3, 7)) { + privilegesRequired = StoredEnum(is).get(); + } else if(version >= INNO_VERSION(3, 0, 4)) { + privilegesRequired = StoredEnum(is).get(); + } + + if(version >= INNO_VERSION(4, 0, 10)) { + showLanguageDialog = StoredEnum(is).get(); + languageDetectionMethod = StoredEnum(is).get(); + } + + if(version >= INNO_VERSION(5, 3, 9)) { + compressMethod = StoredEnum(is).get(); + } else if(version >= INNO_VERSION(4, 2, 6)) { + compressMethod = StoredEnum(is).get(); + } else if(version >= INNO_VERSION(4, 2, 5)) { + compressMethod = StoredEnum(is).get(); + } else if(version >= INNO_VERSION(4, 1, 5)) { + compressMethod = StoredEnum(is).get(); + } + + if(version >= INNO_VERSION(5, 0, 2)) { + architecturesAllowed = StoredFlags(is).get(); + architecturesInstallIn64BitMode = StoredFlags(is).get(); + } else { + architecturesAllowed = Architectures::all(); + architecturesInstallIn64BitMode = Architectures::all(); + } + + if(version >= INNO_VERSION(5, 2, 1) && version < INNO_VERSION(5, 3, 10)) { + signedUninstallerOrigSize = loadNumber(is); + signedUninstallerHdrChecksum = loadNumber(is); + } else { + signedUninstallerOrigSize = signedUninstallerHdrChecksum = 0; + } + + if(version >= INNO_VERSION(5, 3, 3)) { + disableDirPage = StoredEnum(is).get(); + disableProgramGroupPage = StoredEnum(is).get(); + } + + if(version >= INNO_VERSION(5, 3, 6)) { + uninstallDisplaySize = loadNumber(is); + } else { + uninstallDisplaySize = 0; + } + + + StoredFlagReader flags; + + flags.add(shDisableStartupPrompt); + if(version < INNO_VERSION(5, 3, 10)) { + flags.add(shUninstallable); + } + flags.add(shCreateAppDir); + if(version < INNO_VERSION(5, 3, 3)) { + flags.add(shDisableDirPage); + } + if(version <= INNO_VERSION(1, 2, 16)) { + flags.add(shDisableDirExistsWarning); // only in 1.2.10, not in 1.3.25 + } + if(version < INNO_VERSION(5, 3, 3)) { + flags.add(shDisableProgramGroupPage); + } + flags.add(shAllowNoIcons); + if(version < INNO_VERSION(3, 0, 0) || version >= INNO_VERSION(3, 0, 3)) { + flags.add(shAlwaysRestart); + } + if(version <= INNO_VERSION(1, 2, 16)) { + flags.add(shBackSolid); // only in 1.2.10, not in 1.3.25 + } + flags.add(shAlwaysUsePersonalGroup); + flags.add(shWindowVisible); + flags.add(shWindowShowCaption); + flags.add(shWindowResizable); + flags.add(shWindowStartMaximized); + flags.add(shEnableDirDoesntExistWarning); + if(version < INNO_VERSION(4, 1, 2)) { + flags.add(shDisableAppendDir); + } + flags.add(shPassword); + flags.add(shAllowRootDirectory); + flags.add(shDisableFinishedPage); + + if(version.bits != 16) { + if(version < INNO_VERSION(3, 0, 4)) { + flags.add(shAdminPrivilegesRequired); + } + if(version < INNO_VERSION(3, 0, 0)) { + flags.add(shAlwaysCreateUninstallIcon); + } + if(version <= INNO_VERSION(1, 2, 16)) { + flags.add(shOverwriteUninstRegEntries); // only in 1.2.10, win32-only); not in 1.3.25 + } + flags.add(shChangesAssociations); + } + + if(version > INNO_VERSION(1, 2, 16)) { // new after 1.2.16); in 1.3.25 + if(version < INNO_VERSION(5, 3, 8)) { + flags.add(shCreateUninstallRegKey); + } + flags.add(shUsePreviousAppDir); + flags.add(shBackColorHorizontal); + flags.add(shUsePreviousGroup); + flags.add(shUpdateUninstallLogAppName); + } + + if(version > INNO_VERSION(1, 3, 26)) { // new after 1.3.26 + flags.add(shUsePreviousSetupType); + flags.add(shDisableReadyMemo); + flags.add(shAlwaysShowComponentsList); + flags.add(shFlatComponentsList); + flags.add(shShowComponentSizes); + flags.add(shUsePreviousTasks); + flags.add(shDisableReadyPage); + flags.add(shAlwaysShowDirOnReadyPage); + flags.add(shAlwaysShowGroupOnReadyPage); + } + + if(version >= INNO_VERSION(2, 0, 17) && version < INNO_VERSION(4, 1, 5)) { + flags.add(shBzipUsed); + } + + if(version >= INNO_VERSION(2, 0, 18)) { + flags.add(shAllowUNCPath); + } + + if(version >= INNO_VERSION(3, 0, 0)) { + flags.add(shUserInfoPage); + flags.add(shUsePreviousUserInfo); + } + + if(version >= INNO_VERSION(3, 0, 1)) { + flags.add(shUninstallRestartComputer); + } + + if(version >= INNO_VERSION(3, 0, 3)) { + flags.add(shRestartIfNeededByRun); + } + + if(version >= INNO_VERSION_EXT(3, 0, 6, 1)) { + flags.add(shShowTasksTreeLines); + } + + if(version >= INNO_VERSION(4, 0, 0) && version < INNO_VERSION(4, 0, 10)) { + flags.add(shShowLanguageDialog); + } + + if(version >= INNO_VERSION(4, 0, 1) && version < INNO_VERSION(4, 0, 10)) { + flags.add(shDetectLanguageUsingLocale); + } + + if(version >= INNO_VERSION(4, 0, 9)) { + flags.add(shAllowCancelDuringInstall); + } + + if(version >= INNO_VERSION(4, 1, 3)) { + flags.add(shWizardImageStretch); + } + + if(version >= INNO_VERSION(4, 1, 8)) { + flags.add(shAppendDefaultDirName); + flags.add(shAppendDefaultGroupName); + } + + if(version >= INNO_VERSION(4, 2, 2)) { + flags.add(shEncryptionUsed); + } + + if(version >= INNO_VERSION(5, 0, 4)) { + flags.add(shChangesEnvironment); + } + + if(version >= INNO_VERSION(5, 1, 7) && !version.unicode) { + flags.add(shShowUndisplayableLanguages); + } + + if(version >= INNO_VERSION(5, 1, 13)) { + flags.add(shSetupLogging); + } + + if(version >= INNO_VERSION(5, 2, 1)) { + flags.add(shSignedUninstaller); + } + + if(version >= INNO_VERSION(5, 3, 8)) { + flags.add(shUsePreviousLanguage); + } + + if(version >= INNO_VERSION(5, 3, 9)) { + flags.add(shDisableWelcomePage); + } + + options |= flags.get(is); + + if(version < INNO_VERSION(3, 0, 4)) { + privilegesRequired = (options & shAdminPrivilegesRequired) ? AdminPriviliges : NoPrivileges; + } + + if(version < INNO_VERSION(4, 0, 10)) { + showLanguageDialog = (options & shShowLanguageDialog) ? Yes : No; + languageDetectionMethod = (options & shDetectLanguageUsingLocale) ? LocaleLanguage : UILanguage; + } + + if(version < INNO_VERSION(4, 1, 5)) { + compressMethod = (options & shBzipUsed) ? BZip2 : Zlib; + } + + if(version < INNO_VERSION(5, 3, 3)) { + disableDirPage = (options & shDisableDirPage) ? Yes : No; + disableProgramGroupPage = (options & shDisableProgramGroupPage) ? Yes : No; + } + +} + +ENUM_NAMES(SetupHeaderOptions::Enum, "Setup Option", + "disable startup prompt", + "create app dir", + "allow no icons", + "always restart", + "always use personal group", + "window visible", + "window show caption", + "window resizable", + "window start maximized", + "enable dir doesn't exist warning", + "password", + "allow root directory", + "disable finished page", + "changes associations", + "use previous app dir", + "back color horizontal", + "use previous group", + "update uninstall log app name", + "use previous setup type", + "disable ready memo", + "always show components list", + "flat components list", + "show component sizes", + "use previous tasks", + "disable ready page", + "always show dir on ready page", + "always show group on ready page", + "allow unc path", + "user info page", + "use previous user info", + "uninstall restart computer", + "restart if needed by run", + "show tasks tree lines", + "allow cancel during install", + "wizard image stretch", + "append default dir name", + "append default group name", + "encrypted", + "changes environment", + "show undisplayable languages", + "setup logging", + "signed uninstaller", + "use previous language", + "disable welcome page", + "uninstallable", + "disable dir page", + "disable program group page", + "disable append dir", + "admin privilegesrequired", + "always create uninstall icon", + "create uninstall reg key", + "bzip used", + "show language dialog", + "detect language using locale", + "disable dir exists warning", + "back solid", + "overwrite uninst reg entries", +) +BOOST_STATIC_ASSERT(EnumSize::value == EnumNames::count); + +ENUM_NAMES(Architectures::Enum, "Architecture", + "unknown", + "x86", + "amd64", + "IA64", +) + +ENUM_NAMES(SetupHeader::PasswordType, "Password Type", + "plain", + "MD5", + "SHA1", +) + +ENUM_NAMES(SetupHeader::InstallMode, "Install Mode", + "normal", + "silent", + "very silent", +) + +ENUM_NAMES(SetupHeader::UninstallLogMode, "Uninstall Log Mode", + "append", + "new log", + "overwrite", +) + +ENUM_NAMES(SetupHeader::UninstallStyle, "Uninstall Style", + "classic", + "modern", +) + +ENUM_NAMES(SetupHeader::AutoBoolean, "Auto Boolean", + "auto", + "no", + "yes", +) + +ENUM_NAMES(SetupHeader::Privileges, "Privileges", + "none", + "power user", + "admin", + "lowest", +) + +ENUM_NAMES(SetupHeader::LanguageDetection, "Language Detection", + "ui language", + "locale", + "none", +) + +ENUM_NAMES(SetupHeader::CompressionMethod, "Compression Method", + "stored", + "zlib", + "bzip2", + "lzma1", + "lzma2", + "unknown", +) diff --git a/src/SetupHeader.hpp b/src/SetupHeader.hpp new file mode 100644 index 0000000..81ac140 --- /dev/null +++ b/src/SetupHeader.hpp @@ -0,0 +1,307 @@ + +#ifndef INNOEXTRACT_SETUPHEADER_HPP +#define INNOEXTRACT_SETUPHEADER_HPP + +#include +#include +#include +#include +#include "Types.h" +#include "Flags.hpp" +#include "Enum.hpp" +#include "Version.hpp" + +struct SetupVersionData { + + s32 winVersion, ntVersion; // Cardinal + s16 ntServicePack; // Word + + void load(std::istream & is, const InnoVersion & version); + +}; + +std::ostream & operator<<(std::ostream & os, const SetupVersionData & svd); + +typedef char MD5Digest[16]; +typedef char SHA1Digest[20]; +typedef char SetupSalt[8]; + +FLAGS(SetupHeaderOptions, + + shDisableStartupPrompt, + shCreateAppDir, + shAllowNoIcons, + shAlwaysRestart, // TODO missing in [3.0.0, 3.0.3) + shAlwaysUsePersonalGroup, + shWindowVisible, + shWindowShowCaption, + shWindowResizable, + shWindowStartMaximized, + shEnableDirDoesntExistWarning, + shPassword, + shAllowRootDirectory, + shDisableFinishedPage, + shChangesAssociations, + shUsePreviousAppDir, + shBackColorHorizontal, + shUsePreviousGroup, + shUpdateUninstallLogAppName, + shUsePreviousSetupType, + shDisableReadyMemo, + shAlwaysShowComponentsList, + shFlatComponentsList, + shShowComponentSizes, + shUsePreviousTasks, + shDisableReadyPage, + shAlwaysShowDirOnReadyPage, + shAlwaysShowGroupOnReadyPage, + + // new in 2.0.18 + shAllowUNCPath, + + // new in 3.0.0 + shUserInfoPage, + shUsePreviousUserInfo, + + // new in 3.0.1 + shUninstallRestartComputer, + + // new in 3.0.3 + shRestartIfNeededByRun, + + // new in 3.0.8 + shShowTasksTreeLines, + + // new in 4.0.9 + shAllowCancelDuringInstall, + + // new in 4.1.3 + shWizardImageStretch, + + // new in 4.1.8 + shAppendDefaultDirName, + shAppendDefaultGroupName, + + // new in 4.2.2 + shEncryptionUsed, + + // new in 5.0.4 + shChangesEnvironment, + + // new in 5.1.7 + shShowUndisplayableLanguages, // TODO 5.2.5+: only if not unicode + + // new in 5.1.13 + shSetupLogging, + + // new in 5.2.1 + shSignedUninstaller, + + // new in 5.3.8 + shUsePreviousLanguage, + + // new in 5.3.9 + shDisableWelcomePage, + + // Obsolete flags + shUninstallable, // TODO removed in 5.3.10 + shDisableDirPage, // TODO removed in 5.3.3 + shDisableProgramGroupPage, // TODO removed in 5.3.3 + shDisableAppendDir, // TODO removed in 4.1.2 + shAdminPrivilegesRequired, // TODO removed in 3.0.4 + shAlwaysCreateUninstallIcon, // TODO removed in 3.0.0 + shCreateUninstallRegKey, // TODO removed in 5.3.8 + shBzipUsed, // only in [2.0.17, 4.1.5) + shShowLanguageDialog, // only in [4.0.0, 4.0.10) + shDetectLanguageUsingLocale, // only in [4.0.1, 4.0.10) + + // only in very old versions: + shDisableDirExistsWarning, + shBackSolid, + shOverwriteUninstRegEntries, +) + +NAMED_ENUM(SetupHeaderOptions::Enum) + +FLAGS(Architectures, + ArchitectureUnknown, + ArchitectureX86, + ArchitectureAmd64, + ArchitectureIA64, +) + +NAMED_ENUM(Architectures::Enum) + +struct SetupHeader { + + // Setup data header. + + std::string appName; + std::string appVerName; + std::string appId; + std::string appCopyright; + std::string appPublisher; + std::string appPublisherURL; + std::string appSupportPhone; + std::string appSupportURL; + std::string appUpdatesURL; + std::string appVersion; + std::string defaultDirName; + std::string defaultGroupName; + std::string uninstallIconName; + std::string baseFilename; + std::string uninstallFilesDir; + std::string uninstallDisplayName; + std::string uninstallDisplayIcon; + std::string appMutex; + std::string defaultUserInfoName; + std::string defaultUserInfoOrg; + std::string defaultUserInfoSerial; + std::string appReadmeFile; + std::string appContact; + std::string appComments; + std::string appModifyPath; + std::string createUninstallRegKey; + std::string uninstallable; + std::string licenseText; + std::string infoBeforeText; + std::string infoAfterText; + std::string signedUninstallerSignature; + std::string compiledCodeText; + + std::bitset<256> leadBytes; + + size_t numLanguageEntries; + size_t numCustomMessageEntries; + size_t numPermissionEntries; + size_t numTypeEntries; + size_t numComponentEntries; + size_t numTaskEntries; + size_t numDirEntries; + size_t numFileEntries; + size_t numFileLocationEntries; + size_t numIconEntries; + size_t numIniEntries; + size_t numRegistryEntries; + size_t numInstallDeleteEntries; + size_t numUninstallDeleteEntries; + size_t numRunEntries; + size_t numUninstallRunEntries; + + size_t licenseSize; + size_t infoBeforeSize; + size_t infoAfterSize; + + SetupVersionData minVersion; + SetupVersionData onlyBelowVersion; + + Color backColor; + Color backColor2; + Color wizardImageBackColor; + Color wizardSmallImageBackColor; + + enum PasswordType { + PlainPassword, + Md5Password, + Sha1Password + }; + union { + s32 password; // probably CRC32 + MD5Digest passwordMd5; + SHA1Digest passwordSha1; + }; + PasswordType passwordType; + SetupSalt passwordSalt; + + s64 extraDiskSpaceRequired; + size_t slicesPerDisk; + + enum InstallMode { + NormalInstallMode, + SilentInstallMode, + VerySilentInstallMode, + }; + InstallMode installMode; + + enum UninstallLogMode { + AppendLog, + NewLog, + OverwriteLog + }; + UninstallLogMode uninstallLogMode; + + enum UninstallStyle { + ClassicStyle, + ModernStyle + }; + UninstallStyle uninstallStyle; + + enum AutoBoolean { + Auto, + No, + Yes + }; + + AutoBoolean dirExistsWarning; + + enum Privileges { + NoPrivileges, + PowerUserPrivileges, + AdminPriviliges, + LowestPrivileges + }; + Privileges privilegesRequired; + + AutoBoolean showLanguageDialog; + + enum LanguageDetection { + UILanguage, + LocaleLanguage, + NoLanguageDetection + }; + LanguageDetection languageDetectionMethod; + + enum CompressionMethod { + Stored, + Zlib, + BZip2, + LZMA1, + LZMA2, + Unknown + }; + CompressionMethod compressMethod; + + Architectures architecturesAllowed; + Architectures architecturesInstallIn64BitMode; + + u64 signedUninstallerOrigSize; + u32 signedUninstallerHdrChecksum; + + AutoBoolean disableDirPage; + AutoBoolean disableProgramGroupPage; + + size_t uninstallDisplaySize; + + SetupHeaderOptions options; + + void load(std::istream & is, const InnoVersion & version); + +}; + +NAMED_ENUM(SetupHeader::PasswordType) + +NAMED_ENUM(SetupHeader::InstallMode) + +NAMED_ENUM(SetupHeader::UninstallLogMode) + +NAMED_ENUM(SetupHeader::UninstallStyle) + +NAMED_ENUM(SetupHeader::AutoBoolean) + +NAMED_ENUM(SetupHeader::Privileges) + +NAMED_ENUM(SetupHeader::LanguageDetection) + +NAMED_ENUM(SetupHeader::CompressionMethod) + +#endif // INNOEXTRACT_SETUPHEADER_HPP diff --git a/src/SetupHeaderFormat.hpp b/src/SetupHeaderFormat.hpp index 2a66995..18ce8dc 100644 --- a/src/SetupHeaderFormat.hpp +++ b/src/SetupHeaderFormat.hpp @@ -1,84 +1,245 @@ +#include +#include +#include + #include "Types.h" +#include "SetupHeader.hpp" +#include "LoadingUtils.hpp" +#include "Enum.hpp" +#include "Output.hpp" -#include -#include -#include +template +struct EnumValueMap { + + typedef Enum enum_type; + typedef Enum flag_type; + +}; -#include -#include -#include -#include -#include +#define STORED_ENUM_MAP(MapName, Default, ...) \ +struct MapName : public EnumValueMap { \ + static const flag_type default_value; \ + static const flag_type values[]; \ + static const size_t count; \ +}; \ +const MapName::flag_type MapName::default_value = Default; \ +const MapName::flag_type MapName::values[] = { __VA_ARGS__ }; \ +const size_t MapName::count = ARRAY_SIZE(MapName::values) -#pragma pack(push,1) +#define STORED_FLAGS_MAP(MapName, Flag0, ...) STORED_ENUM_MAP(MapName, Flag0, Flag0, ## __VA_ARGS__) -/* -// 2.0.8, 2.0.11 -enum SetupHeaderOption_20008 { - shDisableStartupPrompt, - shUninstallable, - shCreateAppDir, - shDisableDirPage, - shDisableProgramGroupPage, - shAllowNoIcons, - shAlwaysRestart, - shAlwaysUsePersonalGroup, - shWindowVisible, - shWindowShowCaption, - shWindowResizable, - shWindowStartMaximized, - shEnableDirDoesntExistWarning, - shDisableAppendDir, - shPassword, - shAllowRootDirectory, - shDisableFinishedPage, - shAdminPrivilegesRequired, - shAlwaysCreateUninstallIcon, - shChangesAssociations, - shCreateUninstallRegKey, - shUsePreviousAppDir, - shBackColorHorizontal, - shUsePreviousGroup, - shUpdateUninstallLogAppName, - shUsePreviousSetupType, - shDisableReadyMemo, - shAlwaysShowComponentsList, - shFlatComponentsList, - shShowComponentSizes, - shUsePreviousTasks, - shDisableReadyPage, - shAlwaysShowDirOnReadyPage, - shAlwaysShowGroupOnReadyPage, +template +struct StoredEnum { + + u32 value; + +public: + + typedef Mapping mapping_type; + typedef typename Mapping::enum_type enum_type; + + static const size_t size = Mapping::count; + + inline StoredEnum(std::istream & is) { + value = loadNumber(is); // TODO use larger types for larger enums + } + + enum_type get() { + + if(value < size) { + return Mapping::values[value]; + } + + warning << "warning: unexpected " << EnumNames::name << " value: " << value; + + return Mapping::default_value; + } + }; -// 5.2.3 -enum SetupHeaderOption_50203 { +template +class StoredBitfield { + + typedef u8 base_type; + + static const size_t base_size = sizeof(base_type) * 8; + static const size_t count = (Bits + (base_size - 1)) / base_size; // ceildiv + + base_type bits[count]; + +public: + + static const size_t size = Bits; + + inline StoredBitfield(std::istream & is) { + for(size_t i = 0; i < count; i++) { + bits[i] = loadNumber(is); + } + } + + inline u64 getLowerBits() const { + + BOOST_STATIC_ASSERT(sizeof(u64) % sizeof(base_type) == 0); + + u64 result = 0; + + for(size_t i = 0; i < std::min(sizeof(u64) / sizeof(base_type), count); i++) { + result |= (u64(bits[i]) << (i * base_size)); + } + + return result; + } + + inline std::bitset getBitSet() const { + + static const size_t ulong_size = sizeof(unsigned long) * 8; + + BOOST_STATIC_ASSERT(base_size % ulong_size == 0 || base_size < ulong_size); + + std::bitset result(0); + for(size_t i = 0; i < count; i++) { + for(size_t j = 0; j < ceildiv(base_size, ulong_size); j++) { + result |= std::bitset(static_cast(bits[i] >> (j * ulong_size))) + << ((i * base_size) + (j * ulong_size)); + } + } + return result; + } + +}; + +template +class StoredFlags : private StoredBitfield { + +public: + + typedef Mapping mapping_type; + typedef typename Mapping::enum_type enum_type; + typedef Flags flag_type; + + inline StoredFlags(std::istream & is) : StoredBitfield(is) { } + + flag_type get() { + + u64 bits = this->getLowerBits(); + flag_type result = 0; + + for(size_t i = 0; i < this->size; i++) { + if(bits & (u64(1) << i)) { + result |= Mapping::values[i]; + bits &= ~(u64(1) << i); + } + } + + if(bits) { + warning << "unexpected " << EnumNames::name << " flags: " << std::hex << bits << std::dec; + } + + return result; + } + +}; + +template +class StoredFlagReader { + +public: + + typedef Enum enum_type; + typedef Flags flag_type; + + std::vector mappings; + + void add(enum_type flag) { + mappings.push_back(flag); + } + + + flag_type get(std::istream & is) { + + u64 bits = 0; + + /* + if(mappings.size() <= 32) { + bits = loadNumber(is); + } else if(mappings.size() <= 256) { + bits = loadNumber(is); + for(size_t i = 1; i < 4; i++) { + u64 temp = loadNumber(is); + if(temp) { + warning << "unexpected " << EnumNames::name << " flags: " << std::hex << bits << std::dec << " @ " << i; + } + } + } else { + error << "error reading " << EnumNames::name << ": too many flags: " << mappings.size(); + bits = 0; + }*/ + + typedef u8 stored_type; + static const size_t stored_bits = sizeof(stored_type) * 8; + for(size_t i = 0; i < ceildiv(mappings.size(), stored_bits); i++) { + bits |= u64(load(is)) << (i * stored_bits); + } + + std::cout << "read " << mappings.size() << " flags: " << std::hex << bits << std::dec << std::endl; + + flag_type result = 0; + + for(size_t i = 0; i < mappings.size(); i++) { + if(bits & (u64(1) << i)) { + result |= mappings[i]; + bits &= ~(u64(1) << i); + } + } + + if(bits) { + warning << "unexpected " << EnumNames::name << " flags: " << std::hex << bits << std::dec; + } + + return result; + } + +}; + +template +class StoredFlagReader > : public StoredFlagReader { }; + +/* +enum _SetupHeaderOption { + shDisableStartupPrompt, - shUninstallable, + shUninstallable, // TODO removed in 5.3.10 shCreateAppDir, - shDisableDirPage, - shDisableProgramGroupPage, + shDisableDirPage, // TODO removed in 5.3.3 + shDisableDirExistsWarning, // TODO only in 1.2.10, not in 1.3.25 + shDisableProgramGroupPage, // TODO removed in 5.3.3 shAllowNoIcons, - shAlwaysRestart, + shAlwaysRestart, // TODO missing in [3.0.0, 3.0.3) + shBackSolid, // TODO only in 1.2.10, not in 1.3.25 shAlwaysUsePersonalGroup, shWindowVisible, shWindowShowCaption, shWindowResizable, shWindowStartMaximized, shEnableDirDoesntExistWarning, - // -shDisableAppendDir + shDisableAppendDir, // TODO removed in 4.1.2 shPassword, shAllowRootDirectory, shDisableFinishedPage, - // -shAdminPrivilegesRequired - // -shAlwaysCreateUninstallIcon - shChangesAssociations, - shCreateUninstallRegKey, + shAdminPrivilegesRequired, // TODO removed in 3.0.4, 1.2.10: win32-only + shAlwaysCreateUninstallIcon, // TODO removed in 3.0.0, 1.2.10: win32-only + shOverwriteUninstRegEntries, // TODO only in 1.2.10, win32-only, not in 1.3.25 + shChangesAssociations, // TODO 1.2.10: win32-only + + // new after 1.2.16, in 1.3.25 + shCreateUninstallRegKey, // TODO removed in 5.3.8 shUsePreviousAppDir, shBackColorHorizontal, shUsePreviousGroup, shUpdateUninstallLogAppName, + + // new after 1.3.26 shUsePreviousSetupType, shDisableReadyMemo, shAlwaysShowComponentsList, @@ -88,66 +249,276 @@ enum SetupHeaderOption_50203 { shDisableReadyPage, shAlwaysShowDirOnReadyPage, shAlwaysShowGroupOnReadyPage, - // New: + + // only in [2.0.17, 4.1.5) + shBzipUsed, + + // new in 2.0.18 shAllowUNCPath, + + // new in 3.0.0 shUserInfoPage, shUsePreviousUserInfo, + + // new in 3.0.1 shUninstallRestartComputer, + + // new in 3.0.3 shRestartIfNeededByRun, + + // new in 3.0.6.1 shShowTasksTreeLines, + + // only in [4.0.0, 4.0.10) + shShowLanguageDialog, + + // only in [4.0.1, 4.0.10) + shDetectLanguageUsingLocale, + + // new in 4.0.9 shAllowCancelDuringInstall, + + // new in 4.1.3 shWizardImageStretch, + + // new in 4.1.8 shAppendDefaultDirName, shAppendDefaultGroupName, + + // new in 4.2.2 shEncryptionUsed, + + // new in 5.0.4 shChangesEnvironment, - shShowUndisplayableLanguages, + + // new in 5.1.7 + shShowUndisplayableLanguages, // TODO 5.2.5+: only if not unicode + + // new in 5.1.13 shSetupLogging, + + // new in 5.2.1 shSignedUninstaller, -}; - -typedef u8 MD5Digest[16]; -typedef u8 SetupSalt[8]; + + // new in 5.3.8 + shUsePreviousLanguage, + + // new in 5.3.9 + shDisableWelcomePage, + +};*/ -// 2.0.8, 2.0.11 -struct SetupVersionData { - s32 WinVersion, NTVersion; // Cardinal - s16 NTServicePack; // Word -}; +typedef StoredBitfield<256> CharSet; + +STORED_ENUM_MAP(StoredInstallMode, SetupHeader::NormalInstallMode, + SetupHeader::NormalInstallMode, + SetupHeader::SilentInstallMode, + SetupHeader::VerySilentInstallMode +); + +STORED_ENUM_MAP(StoredUninstallLogMode, SetupHeader::AppendLog, + SetupHeader::AppendLog, + SetupHeader::NewLog, + SetupHeader::OverwriteLog +); + +STORED_ENUM_MAP(StoredUninstallStyle, SetupHeader::ClassicStyle, + SetupHeader::ClassicStyle, + SetupHeader::ModernStyle +); + +STORED_ENUM_MAP(StoredDirExistsWarning, SetupHeader::Auto, + SetupHeader::Auto, + SetupHeader::No, + SetupHeader::Yes +); + +// pre- 5.3.7 +STORED_ENUM_MAP(StoredPrivileges0, SetupHeader::NoPrivileges, + SetupHeader::NoPrivileges, + SetupHeader::PowerUserPrivileges, + SetupHeader::AdminPriviliges, +); + +// post- 5.3.7 +STORED_ENUM_MAP(StoredPrivileges1, SetupHeader::NoPrivileges, + SetupHeader::NoPrivileges, + SetupHeader::PowerUserPrivileges, + SetupHeader::AdminPriviliges, + SetupHeader::LowestPrivileges +); + +STORED_ENUM_MAP(StoredShowLanguageDialog, SetupHeader::Yes, + SetupHeader::Yes, + SetupHeader::No, + SetupHeader::Auto +); + +STORED_ENUM_MAP(StoredLanguageDetectionMethod, SetupHeader::UILanguage, + SetupHeader::UILanguage, + SetupHeader::LocaleLanguage, + SetupHeader::NoLanguageDetection +); + +STORED_FLAGS_MAP(StoredArchitectures, + ArchitectureUnknown, + ArchitectureX86, + ArchitectureAmd64, + ArchitectureIA64 +); + +STORED_ENUM_MAP(StoredRestartComputer, SetupHeader::Auto, + SetupHeader::Auto, + SetupHeader::No, + SetupHeader::Yes +); + +// pre-4.2.5 +STORED_ENUM_MAP(StoredCompressionMethod0, SetupHeader::Unknown, + SetupHeader::Zlib, + SetupHeader::BZip2, + SetupHeader::LZMA1 +); + +// 4.2.5 +STORED_ENUM_MAP(StoredCompressionMethod1, SetupHeader::Unknown, + SetupHeader::Stored, + SetupHeader::BZip2, + SetupHeader::LZMA1 +); + +// [4.2.6 5.3.9) +STORED_ENUM_MAP(StoredCompressionMethod2, SetupHeader::Unknown, + SetupHeader::Stored, + SetupHeader::Zlib, + SetupHeader::BZip2, + SetupHeader::LZMA1 +); + +// 5.3.9+ +STORED_ENUM_MAP(StoredCompressionMethod3, SetupHeader::Unknown, + SetupHeader::Stored, + SetupHeader::Zlib, + SetupHeader::BZip2, + SetupHeader::LZMA1, + SetupHeader::LZMA2 +); + +STORED_ENUM_MAP(StoredDisablePage, SetupHeader::Auto, + SetupHeader::Auto, + SetupHeader::No, + SetupHeader::Yes +); -// 2.0.8, 2.0.11 -struct SetupHeader_20008 { +/* +struct SetupHeader { + + union { + + struct { + + std::string AppName, AppVerName, AppId, AppCopyright; // String TODO 1.2.10: PChar + std::string AppPublisher, AppPublisherURL; // String TODO not in 1.2.10 + std::string AppSupportPhone; // String TODO new in 5.1.13 + std::string AppSupportURL, AppUpdatesURL, AppVersion; // String TODO not in 1.2.10 + std::string DefaultDirName, DefaultGroupName; // String + std::string UninstallIconName; // String TODO removed in 3.0.0 + std::string BaseFilename; //String + std::string LicenseText, InfoBeforeText, InfoAfterText, UninstallFilesDir, + UninstallDisplayName, UninstallDisplayIcon, AppMutex; // String TODO not in 1.2.10 + std::string DefaultUserInfoName, DefaultUserInfoOrg; // String TODO new in 3.0.0 + std::string DefaultUserInfoSerial, CompiledCodeText; // String TODO new in 3.0.6.1 + std::string AppReadmeFile, AppContact, AppComments, AppModifyPath; // String TODO new in 4.2.4 + std::string SignedUninstallerSignature; // String TODO new in 5.2.1 + + }; + + struct { // TODO 5.2.5+ - const size_t numstrings; + std::wstring AppName, AppVerName, AppId, AppCopyright, AppPublisher, AppPublisherURL, + AppSupportPhone, AppSupportURL, AppUpdatesURL, AppVersion, DefaultDirName, + DefaultGroupName, BaseFilename, UninstallFilesDir, UninstallDisplayName, + UninstallDisplayIcon, AppMutex, DefaultUserInfoName, DefaultUserInfoOrg, + DefaultUserInfoSerial, AppReadmeFile, AppContact, AppComments, + AppModifyPath; // String / WideString + + std::string CreateUninstallRegKey; // String / WideString TODO new in 5.3.8 + std::string Uninstallable; // String / WideString TODO new in 5.3.10 + + std::string LicenseText, InfoBeforeText, InfoAfterText; // AnsiString + std::string SignedUninstallerSignature; // AnsiString TODO removed in 5.3.10 + std::string CompiledCodeText; // AnsiString + + }; - std::string AppName, AppVerName, AppId, AppCopyright, AppPublisher, AppPublisherURL, - AppSupportURL, AppUpdatesURL, AppVersion, DefaultDirName, - DefaultGroupName, UninstallIconName, BaseFilename, LicenseText, - InfoBeforeText, InfoAfterText, UninstallFilesDir, UninstallDisplayName, - UninstallDisplayIcon, AppMutex; // String + }; - CharSet LeadBytes; + CharSet LeadBytes; // set of Char TODO 5.2.5+: set of AnsiChar, only exists if not unicode - s32 NumTypeEntries, NumComponentEntries, NumTaskEntries; // Integer + s32 NumLanguageEntries; // Integer TODO new in 4.0.0 + s32 NumCustomMessageEntries; // Integer TODO new in 4.2.1 + s32 NumPermissionEntries; // Integer TODO new in 4.1.0 + s32 NumTypeEntries, NumComponentEntries, NumTaskEntries; // Integer TODO not in 1.2.10, not in 1.3.25 s32 NumDirEntries, NumFileEntries, NumFileLocationEntries, NumIconEntries, NumIniEntries, NumRegistryEntries, NumInstallDeleteEntries, NumUninstallDeleteEntries, NumRunEntries, NumUninstallRunEntries; // Integer - SetupVersionData MinVersion, OnlyBelowVersion; - s32 BackColor, BackColor2, WizardImageBackColor; // LongInt - s32 WizardSmallImageBackColor; // LongInt - s32 Password; // LongInt - s32 ExtraDiskSpaceRequired; // LongInt - u8 InstallMode; // (imNormal, imSilent, imVerySilent); - u8 UninstallLogMode; // (lmAppend, lmNew, lmOverwrite); - u8 UninstallStyle; // (usClassic, usModern); - u8 DirExistsWarning; // (ddAuto, ddNo, ddYes); - u64 Options; // set of SetupHeaderOption_20008 - -}; */ + + u32 LicenseSize, InfoBeforeSize, InfoAfterSize; // Cardinal TODO only in 1.2.10 + + union { + LegacySetupVersionData MinVersion, OnlyBelowVersion; // TODO only in 1.2.10 + SetupVersionData MinVersion, OnlyBelowVersion; + }; + + s32 BackColor; // LongInt + s32 BackColor2; // LongInt TODO not in 1.2.10 + s32 WizardImageBackColor; // LongInt + s32 WizardSmallImageBackColor; // LongInt TODO removed in 4.0.4, not in 1.2.10, not in 1.3.25 + union { + s32 Password; // LongInt TODO removed in 4.2.0 + MD5Digest PasswordHash; // TODO only in [4.2.0, 5.3.9) + SHA1Digest PasswordHash; // TODO new in 5.3.9 + }; + SetupSalt PasswordSalt; // TODO new in 4.2.2 + s32 ExtraDiskSpaceRequired; // LongInt TODO from 4.0.0: Integer64 + s32 SlicesPerDisk; // Integer TODO new in 4.0.0 + + StoredEnum installMode; // (imNormal, imSilent, imVerySilent) TODO removed in 5.0.0, not in 1.2.10, not in 1.3.25 + StoredEnum uninstallLogMode; // (lmAppend, lmNew, lmOverwrite) TODO not in 1.2.10 + StoredEnum uninstallStyle; // (usClassic, usModern) TODO removed in 5.0.0, not in 1.2.10, not in 1.3.25 + StoredEnum dirExistsWarning; // (ddAuto, ddNo, ddYes) TODO not in 1.2.10 + StoredEnum restartComputer; // (rcAuto, rcNo, rcYes) TODO only in [3.0.0, 3.0.3)? + StoredEnum privilegesRequired; // (prNone, prPowerUser, prAdmin) TODO new in 3.0.4 + StoredEnum showLanguageDialog; // (slYes, slNo, slAuto) TODO new in 4.0.10 + StoredEnum languageDetectionMethod; // (ldUILanguage, ldLocale, ldNone) TODO new in 4.0.10 + StoredEnum compressMethod; // CompressionMethod TODO new in 4.1.5 + StoredFlags architecturesAllowed, architecturesInstallIn64BitMode; // set of SetupProcessorArchitecture TODO new in 5.1.0 + + s32 signedUninstallerOrigSize; // LongWord TODO only in [5.2.1, 5.3.10) + u32 signedUninstallerHdrChecksum; // DWORD TODO only in [5.2.1, 5.3.10) + + StoredEnum disableDirPage, disableProgramGroupPage; // new in 5.3.3 + + u32 UninstallDisplaySize; // Cardinal TODO new in 5.3.6 + + StoredFlags options; // set of SetupHeaderOption + +}; +*/ -/* -#define ARRAY_SIZE(array) (sizeof(array)/sizeof(*(array))) +// --------------------------------------------------------------------------------------- +/* TODO remove: + * - TSetupHeader + * - TSetupVersionData + * - TSetupHeaderOption + * - TSetupProcessorArchitecture + * - SetupHeaderStrings + * - SetupID + * - TSetupID + */ + +/* template struct enum_size { }; @@ -223,218 +594,9 @@ static std::ostream & operator<<(std::ostream & os, EnumSet rhs) { return os << rhs.bitset(); } -template -struct EnumValueMap { - - typedef Enum enum_type; - typedef Enum flag_type; - -}; -template -struct EnumValueMap > { - - typedef Enum enum_type; - typedef EnumSet flag_type; - -};*/ -namespace boost { - template - class integer_traits : public integer_traits { }; -} -template -struct is_power_of_two { - static const bool value = false; -}; -template -struct is_power_of_two::type> { - static const bool value = true; - typedef Type type; -}; - -template -struct log_next_power_of_two { - static const size_t value = boost::static_log2::value + 1; -}; -template -struct log_next_power_of_two >::type> { - static const size_t value = boost::static_log2::value; -}; - -template -struct next_power_of_two { - static const size_t value = size_t(1) << (boost::static_log2::value + 1); -}; -template -struct next_power_of_two >::type> { - static const size_t value = N; -}; - -template struct fast_type_impl { }; - -template <> struct fast_type_impl<8> { typedef uint_fast8_t type; }; -template <> struct fast_type_impl<16> { typedef uint_fast16_t type; }; -template <> struct fast_type_impl<32> { typedef uint_fast32_t type; }; -template <> struct fast_type_impl<64> { typedef uint_fast64_t type; }; -template <> struct fast_type_impl<128> { typedef __uint128_t type; }; - -template -struct fast_type : public fast_type_impl< boost::static_unsigned_max<8, next_power_of_two::value>::value > { }; - -struct BitsetConverter { - -private: - - typedef ptrdiff_t shift_type; - typedef size_t index_type; - - template - struct IterateEntries { - static const typename Combiner::type value = Combiner::template combine::value)>::value; - }; - template struct IterateEntries { static const typename Combiner::type value = Combiner::base; }; - template struct Combiner { typedef Type type; static const Type base = Base; }; - - template - struct MaxCombiner : public Combiner::const_min> { - template - struct combine { static const Type value = boost::static_signed_max::value, accumulator>::value; }; - }; - - template - struct MinCombiner : public Combiner::const_max> { - template - struct combine { static const Type value = boost::static_signed_min::value, accumulator>::value; }; - }; - - struct ShiftGetter { template struct get { static const shift_type value = Entry::shift; }; }; - struct FromGetter { template struct get { static const index_type value = Entry::from; }; }; - struct ToGetter { template struct get { static const index_type value = Entry::to; }; }; - - template - struct ShiftMaskCombiner : public Combiner { - template - struct combine { static const Type value = mask | ( (Entry::shift == Shift) ? (Type(1) << Entry::from) : Type(0) ); }; - }; - - template - struct Builder; - - template - struct Entry { - - typedef Entry This; - - static const index_type from = From; - static const index_type to = To; - typedef Next next; - - static const shift_type shift = shift_type(from) - shift_type(to); - - static const shift_type max_shift = IterateEntries, This>::value; - static const shift_type min_shift = IterateEntries, This>::value; - - static const index_type max_from = IterateEntries, This>::value; - typedef typename fast_type::type in_type; - - static const index_type max_to = IterateEntries, This>::value; - typedef typename fast_type::type out_type; - - template struct ShiftMask { static const in_type value = IterateEntries, This>::value; }; - - template inline static typename boost::enable_if_c<(Shift >= shift_type(0)), out_type>::type evaluate(in_type value) { - return out_type((value & ShiftMask::value) >> Shift); - } - template inline static typename boost::enable_if_c<(Shift < shift_type(0)), out_type>::type evaluate(in_type value) { - return out_type(value & ShiftMask::value) << (-Shift); - } - - template struct NextShift { static const shift_type value = Shift + 1; }; - template - struct NextShift::value == in_type(0)>::type > { - static const shift_type value = NextShift::value; - }; - - template - inline static typename boost::enable_if_c<(NextShift::value != max_shift + 1), out_type>::type map(in_type value) { - return evaluate(value) | (map::value>(value)); - } - template - inline static typename boost::enable_if_c<(NextShift::value == max_shift + 1), out_type>::type map(in_type value) { - return evaluate(value); - } - - public: - - typedef Builder add; - - static out_type convert(in_type value) { - return map(value); - } - - }; - - template - struct Builder { - - template - struct map : public Entry { }; - - template - struct value : public Entry { }; - - template - struct value : public Entry<0, To> { }; - - }; - - -public: - - typedef Builder add; - -}; - - - - - - -/* - - - - -#define STORED_ENUM_MAP(MapName, Default, ...) \ -struct MapName : public EnumValueMap { \ - static const flag_type default_value; \ - static const flag_type values[]; \ - static const size_t count; \ -}; \ -const MapName::flag_type MapName::default_value = Default; \ -const MapName::flag_type MapName::values[] = { __VA_ARGS__ }; \ -const size_t MapName::count = ARRAY_SIZE(MapName::values) - -template -struct EnumNames { - - const size_t count; - - const char * name; - - const char * names[0]; - -}; - -#define ENUM_NAMES(Enum, Default, ...) - -enum UninstallLogMode { lmAppend, lmNew, lmOverwrite, lmUnknown }; -ENUM_NAMES(UninstallLogMode, "Append", "New", "Overwrite"); - -enum DirExistsWarning { ddAuto, ddNo, ddYes, ddUnknown }; -ENUM_NAMES(DirExistsWarning, "Auto", "No", "Yes"); STORED_ENUM_MAP(UninstallLogModeMapper, lmUnknown, lmAppend, lmNew, lmOverwrite); STORED_ENUM_MAP(DirExistsWarningMapper, ddUnknown, ddAuto, ddNo, ddYes); @@ -574,42 +736,4 @@ struct StoredFlags { };*/ -/* -// 5.2.3 -struct SetupHeader_50203 { - - std::string AppName, AppVerName, AppId, AppCopyright, AppPublisher, AppPublisherURL, - AppSupportPhone, AppSupportURL, AppUpdatesURL, AppVersion, DefaultDirName, - DefaultGroupName, BaseFilename, LicenseText, - InfoBeforeText, InfoAfterText, UninstallFilesDir, UninstallDisplayName, - UninstallDisplayIcon, AppMutex, DefaultUserInfoName, - DefaultUserInfoOrg, DefaultUserInfoSerial, CompiledCodeText, - AppReadmeFile, AppContact, AppComments, AppModifyPath, - SignedUninstallerSignature; - - CharSet LeadBytes; - - s32 NumLanguageEntries, NumCustomMessageEntries, NumPermissionEntries, - NumTypeEntries, NumComponentEntries, NumTaskEntries, NumDirEntries, - NumFileEntries, NumFileLocationEntries, NumIconEntries, NumIniEntries, - NumRegistryEntries, NumInstallDeleteEntries, NumUninstallDeleteEntries, - NumRunEntries, NumUninstallRunEntries; // Integer - SetupVersionData MinVersion, OnlyBelowVersion; - s32 BackColor, BackColor2, WizardImageBackColor; // LongInt - MD5Digest PasswordHash; - u64 PasswordSalt; // array[0..7] of Byte - s64 ExtraDiskSpaceRequired; // Integer64 - s32 SlicesPerDisk; // Integer - StoredEnum uninstallLogMode; // (lmAppend, lmNew, lmOverwrite) - u8 DirExistsWarning; // (ddAuto, ddNo, ddYes) - u8 PrivilegesRequired; // (prNone, prPowerUser, prAdmin) - u8 ShowLanguageDialog; // (slYes, slNo, slAuto) - LanguageDetectionMethod: (ldUILanguage, ldLocale, ldNone); - CompressMethod: TSetupCompressMethod; - ArchitecturesAllowed, ArchitecturesInstallIn64BitMode: TSetupProcessorArchitectures; - SignedUninstallerOrigSize: LongWord; - SignedUninstallerHdrChecksum: DWORD; - u64 Options; // set of SetupHeaderOption_50203; -}; */ - -#pragma pack(pop) +//#pragma pack(pop) diff --git a/src/SetupLoader.cpp b/src/SetupLoader.cpp index 4f8fbe8..e15b648 100644 --- a/src/SetupLoader.cpp +++ b/src/SetupLoader.cpp @@ -8,9 +8,9 @@ #include "ExeReader.hpp" #include "SetupLoaderFormat.hpp" #include "Utils.hpp" +#include "Output.hpp" using std::cout; -using std::cerr; using std::string; using std::endl; using std::setw; @@ -22,17 +22,17 @@ bool SetupLoader::getOldOffsets(std::istream & is, Offsets & offsets) { SetupLoaderHeader locator; if(read(is.seekg(0x30), locator).fail()) { - cerr << "error reading exe header" << endl; + error << "error reading exe header"; return false; } if(locator.id != SetupLoaderHeaderMagic) { - cerr << "invalid exe header id: " << hex << locator.id << dec << endl; + cout << "invalid exe header id: " << hex << locator.id << dec << endl; return false; } if(locator.offsetTableOffset != ~locator.notOffsetTableOffset) { - cerr << "offset table offset mismatch" << endl; + cout << "offset table offset mismatch" << endl; return false; } @@ -56,23 +56,23 @@ bool SetupLoader::getNewOffsets(std::istream & is, Offsets & offsets) { bool SetupLoader::getOffsetsAt(std::istream & is, Offsets & offsets, size_t pos) { if(is.seekg(pos).fail()) { - cerr << "invalid offset table offset" << endl; + error << "invalid offset table offset"; return false; } u32 magic; if(read(is, magic).fail()) { - cerr << "error reading setup loader offset magic" << endl; + error << "error reading setup loader offset magic"; return false; } if(magic != SetupLoaderOffsetTableMagic) { - cerr << "invalid setup loader offset id: " << hex << magic << dec << endl; + error << "invalid setup loader offset id: " << hex << magic << dec; return false; } u64 bigversion; if(read(is, bigversion).fail()) { - cerr << "error reading setup loader offset bigversion" << endl; + error << "error reading setup loader offset bigversion"; return false; } @@ -82,11 +82,11 @@ bool SetupLoader::getOffsetsAt(std::istream & is, Offsets & offsets, size_t pos) switch(bigversion) { - case SetupLoaderOffsetTableID_20: { + case SetupLoaderOffsetTableID_10: { - SetupLoaderOffsetTable20 offsets20; + SetupLoaderOffsetTable10 offsets20; if(read(is, offsets20).fail()) { - cerr << "error reading setup loader offsets v20" << endl; + error << "error reading setup loader offsets v20"; return false; } @@ -106,7 +106,7 @@ bool SetupLoader::getOffsetsAt(std::istream & is, Offsets & offsets, size_t pos) SetupLoaderOffsetTable40 offsets40; if(read(is, offsets40).fail()) { - cerr << "error reading setup loader offsets v40" << endl; + error << "error reading setup loader offsets v40"; return false; } @@ -127,7 +127,7 @@ bool SetupLoader::getOffsetsAt(std::istream & is, Offsets & offsets, size_t pos) SetupLoaderOffsetTable40b offsets40b; if(read(is, offsets40b).fail()) { - cerr << "error reading setup loader offsets v40" << endl; + error << "error reading setup loader offsets v40"; return false; } @@ -142,7 +142,7 @@ bool SetupLoader::getOffsetsAt(std::istream & is, Offsets & offsets, size_t pos) if(bigversion == SetupLoaderOffsetTableID_40c) { if(read(is, expected).fail()) { - cerr << "error reading crc checksum" << endl; + error << "error reading crc checksum"; return false; } actual = lzma_crc32(reinterpret_cast(&offsets40b), sizeof(offsets40b), actual); @@ -156,7 +156,7 @@ bool SetupLoader::getOffsetsAt(std::istream & is, Offsets & offsets, size_t pos) SetupLoaderOffsetTable41 offsets41; if(read(is, offsets41).fail()) { - cerr << "error reading setup loader offsets v40" << endl; + error << "error reading setup loader offsets v40"; return false; } @@ -178,12 +178,12 @@ bool SetupLoader::getOffsetsAt(std::istream & is, Offsets & offsets, size_t pos) SetupLoaderOffsetTable51 offsets51; if(read(is, offsets51).fail()) { - cerr << "error reading setup loader offsets v40" << endl; + error << "error reading setup loader offsets v40"; return false; } if(offsets51.version != 1) { - cerr << "warning: unexpected setup loader offset table version: " << offsets51.version << endl; + error << "warning: unexpected setup loader offset table version: " << offsets51.version; } offsets.totalSize = offsets51.totalSize; @@ -201,14 +201,14 @@ bool SetupLoader::getOffsetsAt(std::istream & is, Offsets & offsets, size_t pos) } default: { - cerr << "unsupported setup loader offset table version: " << hex << bigversion << dec << endl; + error << "unsupported setup loader offset table version: " << hex << bigversion << dec; return false; } } if(actual != expected) { - cerr << "CRC32 mismatch in setup loader offsets" << endl; + error << "CRC32 mismatch in setup loader offsets"; return false; } else { cout << "setup loader offset table CRC32 match" << endl; diff --git a/src/SetupLoader.hpp b/src/SetupLoader.hpp index a3c2cf9..528a14b 100644 --- a/src/SetupLoader.hpp +++ b/src/SetupLoader.hpp @@ -15,18 +15,18 @@ public: struct Offsets { - size_t totalSize; + size_t totalSize; //!< Minimum expected size of the setup file - size_t exeOffset; - size_t exeCompressedSize; - size_t exeUncompressedSize; - s32 exeChecksum; - ChecksumMode exeChecksumMode; + size_t exeOffset; //!< Offset of compressed setup.e32 + size_t exeCompressedSize; //!< Size of setup.e32 after compression + size_t exeUncompressedSize; //!< Size of setup.e32 before compression + s32 exeChecksum; //!< Checksum of setup.e32 before compression + ChecksumMode exeChecksumMode; //! Type of the checksum in exeChecksum - size_t messageOffset; + size_t messageOffset; // TODO document - size_t offset0; - size_t offset1; + size_t offset0; //!< Offset of embedded setup-0.bin data + size_t offset1; //!< Offset of embedded setup-1.bin data, or 0 when DiskSpanning=yes }; diff --git a/src/SetupLoaderFormat.hpp b/src/SetupLoaderFormat.hpp index 8690934..b2d690b 100644 --- a/src/SetupLoaderFormat.hpp +++ b/src/SetupLoaderFormat.hpp @@ -9,7 +9,7 @@ const u32 SetupLoaderOffsetTableMagic = 0x506c4472; const u32 SetupLoaderHeaderMagic = 0x6f6e6e49; enum SetupLoaderOffsetTableID { - SetupLoaderOffsetTableID_20 = 0x7856658732305374l, + SetupLoaderOffsetTableID_10 = 0x7856658732305374l, SetupLoaderOffsetTableID_40 = 0x7856658734305374l, SetupLoaderOffsetTableID_40b = 0x7856658735305374l, SetupLoaderOffsetTableID_40c = 0x7856658736305374l, @@ -19,7 +19,7 @@ enum SetupLoaderOffsetTableID { // 2.0.8, 2.0.11, 2.0.17, 2.0.18 // 3.0.0, 3.0.1, 3.0.2, 3.0.3, 3.0.4, 3.0.5, 3.0.6, 3.0.7, 3.0.8 -struct SetupLoaderOffsetTable20 { +struct SetupLoaderOffsetTable10 { s32 totalSize; s32 exeOffset; s32 exeCompressedSize; @@ -58,7 +58,7 @@ struct SetupLoaderOffsetTable40b { // 4.1.0, 4.1.1, 4.1.2, 4.1.3, 4.1.4, 4.1.5 //! added tableCrc struct SetupLoaderOffsetTable40c : public SetupLoaderOffsetTable40b { - s32 tableCrc; + s32 tableCrc; //!< CRC32 of all prior fields in this structure }; // 4.1.6, 4.1.7, 4.1.8 @@ -73,7 +73,7 @@ struct SetupLoaderOffsetTable41 { s32 exeCrc; s32 offset0; s32 offset1; - s32 tableCrc; + s32 tableCrc; //!< CRC32 of all prior fields in this structure }; // 5.1.5, 5.1.7, 5.1.10, 5.1.13 @@ -82,13 +82,13 @@ struct SetupLoaderOffsetTable41 { //! added version struct SetupLoaderOffsetTable51 { u32 version; //!< = 1 - u32 totalSize; //!< Minimum expected size of setup.exe - u32 exeOffset; //!< Offset of compressed setup.e32 - u32 exeUncompressedSize; //!< Size of setup.e32 before compression - s32 exeCrc; //!< CRC of setup.e32 before compression - u32 offset0; //!< Offset of embedded setup-0.bin data - u32 offset1; //!< Offset of embedded setup-1.bin data, or 0 when DiskSpanning=yes - s32 tableCrc; //!< CRC of all prior fields in this record + u32 totalSize; + u32 exeOffset; + u32 exeUncompressedSize; + s32 exeCrc; + u32 offset0; + u32 offset1; + s32 tableCrc; //!< CRC32 of all prior fields in this structure }; // 2.0.8, 2.0.11, 2.0.17, 2.0.18 diff --git a/src/Types.h b/src/Types.h index ef407cf..ab31ca0 100644 --- a/src/Types.h +++ b/src/Types.h @@ -11,4 +11,6 @@ typedef uint32_t u32; typedef int64_t s64; typedef uint64_t u64; +typedef u32 Color; + typedef std::ios_base strm; diff --git a/src/UnDeflate.cpp b/src/UnDeflate.cpp index eb0586e..514ec0d 100644 --- a/src/UnDeflate.cpp +++ b/src/UnDeflate.cpp @@ -11,7 +11,7 @@ #include #include -#include "liblzmadec/lzmadec.h" +//#include "liblzmadec/lzmadec.h" #include "Types.h" @@ -215,7 +215,7 @@ int main(int argc, const char * argv[]) { } while(false); // Try to interpret as LZMA1 datastream. - do { + /*do { u8 properties = u8(data[offset]); @@ -279,7 +279,7 @@ int main(int argc, const char * argv[]) { case LZMADEC_OK: case LZMADEC_STREAM_END: - case LZMADEC_BUF_ERROR: { */ + case LZMADEC_BUF_ERROR: { size_t filesize = bufsize - stream.avail_out; @@ -302,7 +302,7 @@ int main(int argc, const char * argv[]) { /* } - } */ + } } while(false); } while((ret == LZMADEC_BUF_ERROR || ret == LZMADEC_OK) && stream.avail_in); @@ -313,7 +313,7 @@ int main(int argc, const char * argv[]) { lzmadec_end(&stream); - } while(false); + } while(false);*/ // Try to interpret as XZ datastream. do { diff --git a/src/Utils.hpp b/src/Utils.hpp index 11ebb55..d7ad12b 100644 --- a/src/Utils.hpp +++ b/src/Utils.hpp @@ -1,7 +1,12 @@ +#ifndef INNOEXTRACT_UTILS_HPP +#define INNOEXTRACT_UTILS_HPP + #include #include +#include "Output.hpp" + template inline std::istream & read(std::istream & ifs, T & data) { return ifs.read(reinterpret_cast(&data), sizeof(T)); @@ -16,3 +21,70 @@ std::string safestring(const char (&data)[N]) { return safestring(data, N); } +#define ARRAY_SIZE(array) (sizeof(array)/sizeof(*(array))) + +struct Quoted { + + const std::string & str; + + Quoted(const std::string & _str) : str(_str) { } + +}; +inline std::ostream & operator<<(std::ostream & os, const Quoted & q) { + color::shell_command prev = color::current; + return os << '"' << color::green << q.str << prev << '"'; +} + +struct IfNotEmpty { + + const std::string & name; + const std::string & value; + + IfNotEmpty(const std::string & _name, const std::string & _value) : name(_name), value(_value) { } + +}; +inline std::ostream & operator<<(std::ostream & os, const IfNotEmpty & s) { + if(s.value.length() > 100) { + color::shell_command prev = color::current; + return os << s.name << ": " << color::white << s.value.length() << prev << " bytes" << std::endl; + } else if(!s.value.empty()) { + return os << s.name << ": " << Quoted(s.value) << std::endl; + } else { + return os; + } +} + +template +struct _IfNot { + + const std::string & name; + const T value; + const T excluded; + + _IfNot(const std::string & _name, T _value, T _excluded) : name(_name), value(_value), excluded(_excluded) { } + +}; +template +inline std::ostream & operator<<(std::ostream & os, const _IfNot & s) { + if(s.value != s.excluded) { + color::shell_command prev = color::current; + return os << s.name << ": " << color::cyan << s.value << prev << std::endl; + } else { + return os; + } +} +template +_IfNot IfNot(const std::string & name, T value, T excluded) { + return _IfNot(name, value, excluded); +} +template +_IfNot IfNotZero(const std::string & name, T value) { + return _IfNot(name, value, T(0)); +} + +template +inline A ceildiv(A num, B denom) { + return A((num + (denom - 1)) / denom); +} + +#endif // INNOEXTRACT_UTILS_HPP diff --git a/src/Version.cpp b/src/Version.cpp new file mode 100644 index 0000000..677d7f5 --- /dev/null +++ b/src/Version.cpp @@ -0,0 +1,181 @@ + +#include "Version.hpp" + +#include + +#include + +#include "Utils.hpp" + +typedef char StoredLegacySetupDataVersion[12]; + +struct KnownLegacySetupDataVersion { + + char name[13]; // terminating 0 byte is ignored + + InnoVersionConstant version; + + unsigned char bits; + +}; + +const KnownLegacySetupDataVersion knownLegacySetupDataVersions[] = { + { "i1.2.10--16\x1a", INNO_VERSION(1, 2, 10), 16 }, + { "i1.2.10--32\x1a", INNO_VERSION(1, 2, 10), 32 }, +}; + +typedef char StoredSetupDataVersion[64]; + +struct KnownSetupDataVersion { + + StoredSetupDataVersion name; + + InnoVersionConstant version; + bool unicode; + +}; + +const KnownSetupDataVersion knownSetupDataVersions[] = { + { "Inno Setup Setup Data (1.3.25)", INNO_VERSION_EXT(1, 3, 25, 0) }, + { "Inno Setup Setup Data (2.0.8)", INNO_VERSION_EXT(2, 0, 8, 0) }, + { "Inno Setup Setup Data (2.0.11)", INNO_VERSION_EXT(2, 0, 11, 0) }, + { "Inno Setup Setup Data (2.0.17)", INNO_VERSION_EXT(2, 0, 17, 0) }, + { "Inno Setup Setup Data (2.0.18)", INNO_VERSION_EXT(2, 0, 18, 0) }, + { "Inno Setup Setup Data (3.0.0a)", INNO_VERSION_EXT(3, 0, 0, 0) }, + { "Inno Setup Setup Data (3.0.1)", INNO_VERSION_EXT(3, 0, 1, 0) }, + { "Inno Setup Setup Data (3.0.3)", INNO_VERSION_EXT(3, 0, 3, 0) }, + { "Inno Setup Setup Data (3.0.5)", INNO_VERSION_EXT(3, 0, 5, 0) }, + { "My Inno Setup Extensions Setup Data (3.0.6.1)", INNO_VERSION_EXT(3, 0, 6, 1) }, + { "Inno Setup Setup Data (4.0.0a)", INNO_VERSION_EXT(4, 0, 0, 0) }, + { "Inno Setup Setup Data (4.0.1)", INNO_VERSION_EXT(4, 0, 1, 0) }, + { "Inno Setup Setup Data (4.0.3)", INNO_VERSION_EXT(4, 0, 3, 0) }, + { "Inno Setup Setup Data (4.0.5)", INNO_VERSION_EXT(4, 0, 5, 0) }, + { "Inno Setup Setup Data (4.0.9)", INNO_VERSION_EXT(4, 0, 9, 0) }, + { "Inno Setup Setup Data (4.0.10)", INNO_VERSION_EXT(4, 0, 10, 0) }, + { "Inno Setup Setup Data (4.0.11)", INNO_VERSION_EXT(4, 0, 11, 0) }, + { "Inno Setup Setup Data (4.1.0)", INNO_VERSION_EXT(4, 1, 0, 0) }, + { "Inno Setup Setup Data (4.1.2)", INNO_VERSION_EXT(4, 1, 2, 0) }, + { "Inno Setup Setup Data (4.1.3)", INNO_VERSION_EXT(4, 1, 3, 0) }, + { "Inno Setup Setup Data (4.1.4)", INNO_VERSION_EXT(4, 1, 4, 0) }, + { "Inno Setup Setup Data (4.1.5)", INNO_VERSION_EXT(4, 1, 5, 0) }, + { "Inno Setup Setup Data (4.1.6)", INNO_VERSION_EXT(4, 1, 6, 0) }, + { "Inno Setup Setup Data (4.1.8)", INNO_VERSION_EXT(4, 1, 8, 0) }, + { "Inno Setup Setup Data (4.2.0)", INNO_VERSION_EXT(4, 2, 0, 0) }, + { "Inno Setup Setup Data (4.2.1)", INNO_VERSION_EXT(4, 2, 1, 0) }, + { "Inno Setup Setup Data (4.2.2)", INNO_VERSION_EXT(4, 2, 2, 0) }, + { "Inno Setup Setup Data (4.2.3)", INNO_VERSION_EXT(4, 2, 3, 0) }, + { "Inno Setup Setup Data (4.2.5)", INNO_VERSION_EXT(4, 2, 5, 0) }, + { "Inno Setup Setup Data (4.2.6)", INNO_VERSION_EXT(4, 2, 6, 0) }, + { "Inno Setup Setup Data (5.0.0)", INNO_VERSION_EXT(5, 0, 0, 0) }, + { "Inno Setup Setup Data (5.0.1)", INNO_VERSION_EXT(5, 0, 1, 0) }, + { "Inno Setup Setup Data (5.0.3)", INNO_VERSION_EXT(5, 0, 3, 0) }, + { "Inno Setup Setup Data (5.0.4)", INNO_VERSION_EXT(5, 0, 4, 0) }, + { "Inno Setup Setup Data (5.1.0)", INNO_VERSION_EXT(5, 1, 0, 0) }, + { "Inno Setup Setup Data (5.1.2)", INNO_VERSION_EXT(5, 1, 2, 0) }, + { "Inno Setup Setup Data (5.1.7)", INNO_VERSION_EXT(5, 1, 7, 0) }, + { "Inno Setup Setup Data (5.1.10)", INNO_VERSION_EXT(5, 1, 10, 0) }, + { "Inno Setup Setup Data (5.1.13)", INNO_VERSION_EXT(5, 1, 13, 0) }, + { "Inno Setup Setup Data (5.2.0)", INNO_VERSION_EXT(5, 2, 0, 0) }, + { "Inno Setup Setup Data (5.2.1)", INNO_VERSION_EXT(5, 2, 1, 0) }, + { "Inno Setup Setup Data (5.2.3)", INNO_VERSION_EXT(5, 2, 3, 0) }, + { "Inno Setup Setup Data (5.2.5)", INNO_VERSION_EXT(5, 2, 5, 0) }, + { "Inno Setup Setup Data (5.2.5) (u)", INNO_VERSION_EXT(5, 2, 5, 0), true }, + { "Inno Setup Setup Data (5.3.0)", INNO_VERSION_EXT(5, 3, 0, 0) }, + { "Inno Setup Setup Data (5.3.0) (u)", INNO_VERSION_EXT(5, 3, 0, 0), true }, + { "Inno Setup Setup Data (5.3.3)", INNO_VERSION_EXT(5, 3, 3, 0) }, + { "Inno Setup Setup Data (5.3.3) (u)", INNO_VERSION_EXT(5, 3, 3, 0), true }, + { "Inno Setup Setup Data (5.3.5)", INNO_VERSION_EXT(5, 3, 5, 0) }, + { "Inno Setup Setup Data (5.3.5) (u)", INNO_VERSION_EXT(5, 3, 5, 0), true }, + { "Inno Setup Setup Data (5.3.6)", INNO_VERSION_EXT(5, 3, 6, 0) }, + { "Inno Setup Setup Data (5.3.6) (u)", INNO_VERSION_EXT(5, 3, 6, 0), true }, + { "Inno Setup Setup Data (5.3.7)", INNO_VERSION_EXT(5, 3, 7, 0) }, + { "Inno Setup Setup Data (5.3.7) (u)", INNO_VERSION_EXT(5, 3, 7, 0), true }, + { "Inno Setup Setup Data (5.3.8)", INNO_VERSION_EXT(5, 3, 8, 0) }, + { "Inno Setup Setup Data (5.3.8) (u)", INNO_VERSION_EXT(5, 3, 8, 0), true }, + { "Inno Setup Setup Data (5.3.9)", INNO_VERSION_EXT(5, 3, 9, 0) }, + { "Inno Setup Setup Data (5.3.9) (u)", INNO_VERSION_EXT(5, 3, 9, 0), true }, + { "Inno Setup Setup Data (5.3.10)", INNO_VERSION_EXT(5, 3, 10, 0) }, + { "Inno Setup Setup Data (5.3.10) (u)", INNO_VERSION_EXT(5, 3, 10, 0), true }, + { "Inno Setup Setup Data (5.4.2)", INNO_VERSION_EXT(5, 4, 2, 0) }, + { "Inno Setup Setup Data (5.4.2) (u)", INNO_VERSION_EXT(5, 4, 2, 0), true }, +}; +using std::cout; +using std::string; +using std::endl; + +std::ostream & operator<<(std::ostream & os, const InnoVersion & v) { + + os << (v.version >> 24) << '.' << ((v.version >> 16) & 0xff) << '.' << ((v.version >> 8) & 0xff); + if(v.version & 0xff) { + os << '.' << (v.version & 0xff); + } + + if(v.unicode) { + os << " (unicode)"; + } + + if(v.bits != 32) { + os << " (" << int(v.bits) << "-bit)"; + } + + if(!v.known) { + os << " [unsupported]"; + } + + return os; +} + +void InnoVersion::load(std::istream & is) { + + BOOST_STATIC_ASSERT(sizeof(StoredLegacySetupDataVersion) <= sizeof(StoredSetupDataVersion)); + + StoredLegacySetupDataVersion legacyVersion; + is.read(legacyVersion, sizeof(legacyVersion)); + + if(legacyVersion[0] == 'i' && legacyVersion[sizeof(legacyVersion) - 1] == '\x1a') { + + cout << "found legacy version: \"" << safestring(legacyVersion, sizeof(legacyVersion) - 1) << '"' << endl; + + for(size_t i = 0; i < ARRAY_SIZE(knownLegacySetupDataVersions); i++) { + if(!memcmp(legacyVersion, knownLegacySetupDataVersions[i].name, sizeof(legacyVersion))) { + version = knownLegacySetupDataVersions[i].version; + bits = knownLegacySetupDataVersions[i].bits; + unicode = false; + known = true; + cout << "-> version is known" << endl; + return; + } + } + + // TODO autodetect version + + known = false; + + cout << "-> unknown version" << endl; + throw new string("bad version"); + } + + StoredSetupDataVersion storedVersion; + memcpy(storedVersion, legacyVersion, sizeof(legacyVersion)); + is.read(storedVersion + sizeof(legacyVersion), sizeof(storedVersion) - sizeof(legacyVersion)); + + cout << "found version: \"" << safestring(storedVersion) << '"' << endl; + + for(size_t i = 0; i < ARRAY_SIZE(knownSetupDataVersions); i++) { + if(!memcmp(storedVersion, knownSetupDataVersions[i].name, sizeof(storedVersion))) { + version = knownSetupDataVersions[i].version; + bits = 32; + unicode = knownSetupDataVersions[i].unicode; + known = true; + cout << "-> version is known" << endl; + return; + } + } + + // TODO autodetect version + + known = false; + + cout << "-> unknown version" << endl; + throw new string("bad version"); +} diff --git a/src/Version.hpp b/src/Version.hpp new file mode 100644 index 0000000..6c4bac2 --- /dev/null +++ b/src/Version.hpp @@ -0,0 +1,64 @@ + +#ifndef INNOEXTRACT_VERSION_HPP +#define INNOEXTRACT_VERSION_HPP + +#include +#include + +#include "Types.h" + +typedef u32 InnoVersionConstant; +#define INNO_VERSION_EXT(a, b, c, d) ((u32(a) << 24) | (u32(b) << 16) | (u32(c) << 8) | u32(d)) +#define INNO_VERSION(a, b, c) INNO_VERSION_EXT(a, b, c, 0) + +struct InnoVersion { + + InnoVersionConstant version; + + char bits; // 16 or 32 + + bool unicode; + + bool known; + + + inline InnoVersion() : known(false) { }; + + inline InnoVersion(InnoVersionConstant _version, bool _unicode = false, bool _known = false, char _bits = 32) : version(_version), unicode(_unicode), known(_known), bits(_bits) { }; + + + inline InnoVersion(char a, char b, char c, char d = 0, bool _unicode = false, bool _known = false, char _bits = 32) : version(INNO_VERSION_EXT(a, b, c, d)), unicode(_unicode), known(_known), bits(_bits) { }; + + inline int a() { return version >> 24; } + inline int b() { return (version >> 16) & 0xff; } + inline int c() { return (version >> 8) & 0xff; } + inline int d() { return version & 0xff; } + + void load(std::istream & is); + +}; + +inline bool operator==(const InnoVersion & a, const InnoVersion & b) { return a.version == b.version; } +inline bool operator!=(const InnoVersion & a, const InnoVersion & b) { return !operator==(a, b); } +inline bool operator< (const InnoVersion & a, const InnoVersion & b) { return a.version < b.version; } +inline bool operator> (const InnoVersion & a, const InnoVersion & b) { return operator< (b, a); } +inline bool operator<=(const InnoVersion & a, const InnoVersion & b) { return !operator> (a, b); } +inline bool operator>=(const InnoVersion & a, const InnoVersion & b) { return !operator< (a, b); } + +inline bool operator==(const InnoVersion & a, InnoVersionConstant b) { return a.version == b; } +inline bool operator!=(const InnoVersion & a, InnoVersionConstant b) { return !operator==(a, b); } +inline bool operator< (const InnoVersion & a, InnoVersionConstant b) { return a.version < b; } +inline bool operator> (const InnoVersion & a, InnoVersionConstant b) { return operator< (b, a); } +inline bool operator<=(const InnoVersion & a, InnoVersionConstant b) { return !operator> (a, b); } +inline bool operator>=(const InnoVersion & a, InnoVersionConstant b) { return !operator< (a, b); } + +inline bool operator==(InnoVersionConstant a, const InnoVersion & b) { return a == b.version; } +inline bool operator!=(InnoVersionConstant a, const InnoVersion & b) { return !operator==(a, b); } +inline bool operator< (InnoVersionConstant a, const InnoVersion & b) { return a < b.version; } +inline bool operator> (InnoVersionConstant a, const InnoVersion & b) { return operator< (b, a); } +inline bool operator<=(InnoVersionConstant a, const InnoVersion & b) { return !operator> (a, b); } +inline bool operator>=(InnoVersionConstant a, const InnoVersion & b) { return !operator< (a, b); } + +std::ostream & operator<<(std::ostream & os, const InnoVersion & version); + +#endif // INNOEXTRACT_VERSION_HPP