diff --git a/CMakeLists.txt b/CMakeLists.txt index 64f095d..c221c15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,10 +106,11 @@ set(INNOEXTRACT_SOURCES src/setup/Version.cpp src/setup/WindowsVersion.cpp - src/stream/BlockReader.cpp - src/stream/ChunkReader.cpp - src/stream/LzmaFilter.cpp - src/stream/SliceReader.cpp + src/stream/block.cpp + src/stream/chunk.cpp + src/stream/file.cpp + src/stream/lzma.cpp + src/stream/slice.cpp src/util/console.cpp src/util/load.cpp diff --git a/src/InnoExtract.cpp b/src/InnoExtract.cpp index 2a3f972..0ad04d9 100644 --- a/src/InnoExtract.cpp +++ b/src/InnoExtract.cpp @@ -14,17 +14,11 @@ #include #include #include - +#include #include #include - #include - #include -#include -#include -#include -#include #include @@ -47,12 +41,10 @@ #include "setup/SetupTypeEntry.hpp" #include "setup/Version.hpp" -#include "stream/BlockReader.hpp" -#include "stream/ChunkReader.hpp" -#include "stream/LzmaFilter.hpp" -#include "stream/SliceReader.hpp" -#include "stream/ChecksumFilter.hpp" -#include "stream/InstructionFilter.hpp" +#include "stream/block.hpp" +#include "stream/chunk.hpp" +#include "stream/file.hpp" +#include "stream/slice.hpp" #include "util/console.hpp" #include "util/load.hpp" @@ -81,8 +73,7 @@ struct FileLocationComparer { }; -static void printSetupItem(std::ostream & os, const SetupItem & item, - const SetupHeader & header) { +static void print(std::ostream & os, const SetupItem & item, const SetupHeader & header) { os << if_not_empty(" Componenets", item.components); os << if_not_empty(" Tasks", item.tasks); @@ -107,7 +98,7 @@ static void print(std::ostream & os, const RunEntry & entry, const SetupHeader & os << if_not_empty(" Verb", entry.verb); os << if_not_empty(" Description", entry.verb); - printSetupItem(cout, entry, header); + print(cout, static_cast(entry), header); os << if_not_equal(" Show command", entry.showCmd, 1); os << if_not_equal(" Wait", entry.wait, RunEntry::WaitUntilTerminated); @@ -116,40 +107,6 @@ static void print(std::ostream & os, const RunEntry & entry, const SetupHeader & } -static std::ostream & operator<<(std::ostream & os, const Checksum & checksum) { - - std::ios_base::fmtflags old = os.flags(); - - cout << checksum.type << ' '; - - switch(checksum.type) { - case Checksum::Adler32: { - cout << print_hex(checksum.adler32); - break; - } - case Checksum::Crc32: { - cout << print_hex(checksum.crc32); - break; - } - case Checksum::MD5: { - for(size_t i = 0; i < ARRAY_SIZE(checksum.md5); i++) { - cout << std::setfill('0') << std::hex << std::setw(2) << int(uint8_t(checksum.md5[i])); - } - break; - } - case Checksum::Sha1: { - for(size_t i = 0; i < ARRAY_SIZE(checksum.sha1); i++) { - cout << std::setfill('0') << std::hex << std::setw(2) << int(uint8_t(checksum.sha1[i])); - } - break; - } - } - - os.setf(old, std::ios_base::basefield); - - return os; -} - static const char * magicNumbers[][2] = { { "GIF89a", "gif" }, { "GIF87a", "gif" }, @@ -206,9 +163,9 @@ static void readWizardImageAndDecompressor(std::istream & is, const InnoVersion dump(is, "wizard_small"); } - if(header.compressMethod == SetupHeader::BZip2 - || (header.compressMethod == SetupHeader::LZMA1 && version == INNO_VERSION(4, 1, 5)) - || (header.compressMethod == SetupHeader::Zlib && version >= INNO_VERSION(4, 2, 6))) { + if(header.compressMethod == stream::chunk::BZip2 + || (header.compressMethod == stream::chunk::LZMA1 && version == INNO_VERSION(4, 1, 5)) + || (header.compressMethod == stream::chunk::Zlib && version >= INNO_VERSION(4, 2, 6))) { dump(is, "decompressor"); } @@ -274,7 +231,7 @@ int main(int argc, char * argv[]) { cout << "version: " << color::white << version << color::reset << endl; - boost::shared_ptr is(BlockReader::get(ifs, version)); + stream::block_reader::pointer is = stream::block_reader::get(ifs, version); if(!is) { log_error << "error reading block"; return 1; @@ -582,7 +539,7 @@ int main(int argc, char * argv[]) { cout << " - " << quoted(entry.name) << ':' << endl; - printSetupItem(cout, entry, header); + print(cout, entry, header); if(!entry.permissions.empty()) { cout << " Permissions: " << entry.permissions.length() << " bytes"; @@ -624,7 +581,7 @@ int main(int argc, char * argv[]) { cout << if_not_empty(" Install font name", entry.installFontName); cout << if_not_empty(" Strong assembly name", entry.strongAssemblyName); - printSetupItem(cout, entry, header); + print(cout, entry, header); cout << if_not_zero(" Attributes", entry.attributes); cout << if_not_zero(" Size", entry.externalSize); @@ -655,7 +612,7 @@ int main(int argc, char * argv[]) { cout << if_not_empty(" Comment", entry.comment); cout << if_not_empty(" App user model id", entry.appUserModelId); - printSetupItem(cout, entry, header); + print(cout, entry, header); cout << if_not_zero(" Icon index", entry.iconIndex); cout << if_not_equal(" Show command", entry.showCmd, 1); @@ -682,7 +639,7 @@ int main(int argc, char * argv[]) { cout << " set [" << quoted(entry.section) << "] "; cout << quoted(entry.key) << " = " << quoted(entry.value) << std::endl; - printSetupItem(cout, entry, header); + print(cout, entry, header); cout << if_not_zero(" Options", entry.options); @@ -718,7 +675,7 @@ int main(int argc, char * argv[]) { } cout << endl; - printSetupItem(cout, entry, header); + print(cout, entry, header); if(!entry.permissions.empty()) { cout << " Permissions: " << entry.permissions.length() << " bytes"; @@ -743,7 +700,7 @@ int main(int argc, char * argv[]) { cout << " - " << quoted(entry.name) << " (" << color::cyan << entry.type << color::reset << ')' << endl; - printSetupItem(cout, entry, header); + print(cout, entry, header); } @@ -761,7 +718,7 @@ int main(int argc, char * argv[]) { cout << " - " << quoted(entry.name) << " (" << color::cyan << entry.type << color::reset << ')' << endl; - printSetupItem(cout, entry, header); + print(cout, entry, header); } @@ -809,7 +766,7 @@ int main(int argc, char * argv[]) { // TODO skip to end if not there yet - is.reset(BlockReader::get(ifs, version)); + is = stream::block_reader::get(ifs, version); if(!is) { log_error << "error reading block"; return 1; @@ -839,7 +796,7 @@ int main(int argc, char * argv[]) { << " size " << color::cyan << print_hex(entry.chunkSize) << color::reset << std::endl; cout << if_not_zero(" File offset", print_hex(entry.fileOffset)); - cout << if_not_zero(" File size", print_bytes(entry.fileSize)); + cout << if_not_zero(" File size", print_bytes(entry.file_size)); cout << " Checksum: " << entry.checksum << endl; @@ -885,68 +842,46 @@ int main(int argc, char * argv[]) { } } - typedef std::map > Chunks; + typedef std::map > Chunks; Chunks chunks; for(size_t i = 0; i < locations.size(); i++) { const FileLocationEntry & location = locations[i]; - chunks[ChunkReader::Chunk(location.firstSlice, location.chunkOffset, location.chunkSize, - location.options & FileLocationEntry::ChunkCompressed, - location.options & FileLocationEntry::ChunkEncrypted) + + stream::chunk::compression_method compression = stream::chunk::Stored; + if(location.options & FileLocationEntry::ChunkCompressed) { + compression = header.compressMethod; + } + + chunks[stream::chunk(location.firstSlice, location.chunkOffset, location.chunkSize, + compression, location.options & FileLocationEntry::ChunkEncrypted) ].push_back(i); - assert(header.compressMethod == SetupHeader::BZip2 + assert(header.compressMethod == stream::chunk::BZip2 || !(location.options & FileLocationEntry::BZipped)); } - boost::shared_ptr slice_reader; + boost::shared_ptr slice_reader; if(offsets.dataOffset) { - slice_reader.reset(new SliceReader(argv[1], offsets.dataOffset)); + slice_reader = boost::make_shared(argv[1], offsets.dataOffset); } else { fs::path path(argv[1]); - - slice_reader.reset(new SliceReader(path.parent_path().string() + '/', - path.stem().string(), header.slicesPerDisk)); + slice_reader = boost::make_shared(path.parent_path().string() + '/', + path.stem().string(), + header.slicesPerDisk); } try { BOOST_FOREACH(Chunks::value_type & chunk, chunks) { - cout << "[starting " << (chunk.first.compressed ? header.compressMethod : SetupHeader::Stored) - << " chunk @ " << chunk.first.firstSlice << " + " << print_hex(offsets.dataOffset) - << " + " << print_hex(chunk.first.chunkOffset) << ']' << std::endl; + cout << "[starting " << chunk.first.compression + << " chunk @ " << chunk.first.first_slice << " + " << print_hex(offsets.dataOffset) + << " + " << print_hex(chunk.first.offset) << ']' << std::endl; std::sort(chunk.second.begin(), chunk.second.end(), FileLocationComparer(locations)); - if(!slice_reader->seek(chunk.first.firstSlice, chunk.first.chunkOffset)) { - log_error << "error seeking"; - return 1; - } - - const char chunkId[4] = { 'z', 'l', 'b', 0x1a }; - - char magic[4]; - if(slice_reader->read(magic, 4) != 4 || memcmp(magic, chunkId, 4)) { - log_error << "bad chunk id"; - return 1; - } - - typedef io::chain chunk_stream_type; - chunk_stream_type chunk_source; - - if(chunk.first.compressed) { - switch(header.compressMethod) { - case SetupHeader::Stored: break; - case SetupHeader::Zlib: chunk_source.push(io::zlib_decompressor(), 8192); break; - case SetupHeader::BZip2: chunk_source.push(io::bzip2_decompressor(), 8192); break; - case SetupHeader::LZMA1: chunk_source.push(inno_lzma1_decompressor(), 8192); break; - case SetupHeader::LZMA2: chunk_source.push(inno_lzma2_decompressor(), 8192); break; - default: log_error << "unknown compression"; - } - } - - int64_t csize = int64_t(chunk.first.chunkSize); - chunk_source.push(io::restrict(boost::ref(*slice_reader), 0, csize)); + stream::chunk_reader::pointer chunk_source; + chunk_source = stream::chunk_reader::get(*slice_reader, chunk.first); uint64_t offset = 0; @@ -960,9 +895,9 @@ int main(int argc, char * argv[]) { if(location.fileOffset > offset) { std::cout << "discarding " << print_bytes(location.fileOffset - offset) << std::endl; - discard(chunk_source, location.fileOffset - offset); + discard(*chunk_source, location.fileOffset - offset); } - offset = location.fileOffset + location.fileSize; + offset = location.fileOffset + location.file_size; std::cout << "-> reading "; bool named = false; @@ -977,27 +912,12 @@ int main(int argc, char * argv[]) { std::cout << "unnamed file"; } std::cout << " @ " << print_hex(location.fileOffset) - << " (" << print_bytes(location.fileSize) << ')' << std::endl; + << " (" << print_bytes(location.file_size) << ')' << std::endl; Hasher hasher; - hasher.init(location.checksum.type); - - int64_t file_size = int64_t(location.fileSize); - io::restriction raw_src(chunk_source, 0, file_size); - - io::filtering_istream file_source; - - file_source.push(checksum_filter(&hasher), 8192); - - if(location.options & FileLocationEntry::CallInstructionOptimized) { - if(version < INNO_VERSION(5, 2, 0)) { - file_source.push(call_instruction_decoder_4108(), 8192); - } else { - file_source.push(call_instruction_decoder_5200(version >= INNO_VERSION(5, 3, 9)), 8192); - } - } - file_source.push(raw_src); + stream::file_reader::pointer file_source; + file_source = stream::file_reader::get(*chunk_source, location, version, hasher); BOOST_FOREACH(size_t file_i, files_for_location[location_i]) { if(!files[file_i].destination.empty()) { @@ -1015,16 +935,16 @@ int main(int argc, char * argv[]) { boost::posix_time::ptime start(boost::posix_time::microsec_clock::universal_time()); - while(!file_source.eof()) { + while(!file_source->eof()) { - std::streamsize n = file_source.read(buffer, ARRAY_SIZE(buffer)).gcount(); + std::streamsize n = file_source->read(buffer, ARRAY_SIZE(buffer)).gcount(); if(n > 0) { ofs.write(buffer, n); total += uint64_t(n); - float new_status = float(size_t(1000.f * float(total) / float(location.fileSize))) + float new_status = float(size_t(1000.f * float(total) / float(location.file_size))) * (1 / 1000.f); if(status != new_status && new_status != 100.f) { @@ -1067,9 +987,8 @@ int main(int argc, char * argv[]) { } } - } catch(io::bzip2_error e) { - log_error << e.what() << ": " << - e.error(); + } catch(std::ios_base::failure e) { + log_error << e.what(); } std::cout << color::green << "Done" << color::reset << std::dec; diff --git a/src/crypto/Checksum.cpp b/src/crypto/Checksum.cpp index 96d4576..d4c9636 100644 --- a/src/crypto/Checksum.cpp +++ b/src/crypto/Checksum.cpp @@ -3,6 +3,8 @@ #include +#include "util/output.hpp" + ENUM_NAMES(Checksum::Type, "Checksum Type", "Adler32", "CRC32", @@ -23,3 +25,37 @@ bool Checksum::operator==(const Checksum & o) const { case Sha1: return !memcmp(sha1, o.sha1, ARRAY_SIZE(sha1)); }; } + +std::ostream & operator<<(std::ostream & os, const Checksum & checksum) { + + std::ios_base::fmtflags old = os.flags(); + + os << checksum.type << ' '; + + switch(checksum.type) { + case Checksum::Adler32: { + os << print_hex(checksum.adler32); + break; + } + case Checksum::Crc32: { + os << print_hex(checksum.crc32); + break; + } + case Checksum::MD5: { + for(size_t i = 0; i < ARRAY_SIZE(checksum.md5); i++) { + os << std::setfill('0') << std::hex << std::setw(2) << int(uint8_t(checksum.md5[i])); + } + break; + } + case Checksum::Sha1: { + for(size_t i = 0; i < ARRAY_SIZE(checksum.sha1); i++) { + os << std::setfill('0') << std::hex << std::setw(2) << int(uint8_t(checksum.sha1[i])); + } + break; + } + } + + os.setf(old, std::ios_base::basefield); + + return os; +} diff --git a/src/crypto/Checksum.hpp b/src/crypto/Checksum.hpp index 0cc2769..270e1c2 100644 --- a/src/crypto/Checksum.hpp +++ b/src/crypto/Checksum.hpp @@ -4,7 +4,8 @@ #include #include -#include +#include +#include #include "util/endian.hpp" #include "util/enum.hpp" @@ -75,4 +76,6 @@ public: NAMED_ENUM(Checksum::Type) +std::ostream & operator<<(std::ostream & os, const Checksum & checksum); + #endif // INNOEXTRACT_CRYPTO_CHECKSUM_HPP diff --git a/src/setup/FileLocationEntry.cpp b/src/setup/FileLocationEntry.cpp index 304beca..c577bc8 100644 --- a/src/setup/FileLocationEntry.cpp +++ b/src/setup/FileLocationEntry.cpp @@ -27,10 +27,10 @@ void FileLocationEntry::load(std::istream & is, const InnoVersion & version) { } if(version >= INNO_VERSION(4, 0, 0)) { - fileSize = load_number(is); + file_size = load_number(is); chunkSize = load_number(is); } else { - fileSize = load_number(is); + file_size = load_number(is); chunkSize = load_number(is); } diff --git a/src/setup/FileLocationEntry.hpp b/src/setup/FileLocationEntry.hpp index 3cdebd9..10f8a52 100644 --- a/src/setup/FileLocationEntry.hpp +++ b/src/setup/FileLocationEntry.hpp @@ -37,7 +37,7 @@ struct FileLocationEntry : public SetupItem { uint64_t chunkSize; //! total compressed size of the chunk uint64_t fileOffset; //!< offset of this file within the decompressed chunk - uint64_t fileSize; //!< decompressed size of this file + uint64_t file_size; //!< decompressed size of this file Checksum checksum; diff --git a/src/setup/SetupHeader.cpp b/src/setup/SetupHeader.cpp index 3931a40..35f2543 100644 --- a/src/setup/SetupHeader.cpp +++ b/src/setup/SetupHeader.cpp @@ -75,34 +75,34 @@ STORED_ENUM_MAP(StoredRestartComputer, SetupHeader::Auto, ); // pre-4.2.5 -STORED_ENUM_MAP(StoredCompressionMethod0, SetupHeader::Unknown, - SetupHeader::Zlib, - SetupHeader::BZip2, - SetupHeader::LZMA1 +STORED_ENUM_MAP(StoredCompressionMethod0, stream::chunk::Unknown, + stream::chunk::Zlib, + stream::chunk::BZip2, + stream::chunk::LZMA1 ); // 4.2.5 -STORED_ENUM_MAP(StoredCompressionMethod1, SetupHeader::Unknown, - SetupHeader::Stored, - SetupHeader::BZip2, - SetupHeader::LZMA1 +STORED_ENUM_MAP(StoredCompressionMethod1, stream::chunk::Unknown, + stream::chunk::Stored, + stream::chunk::BZip2, + stream::chunk::LZMA1 ); // [4.2.6 5.3.9) -STORED_ENUM_MAP(StoredCompressionMethod2, SetupHeader::Unknown, - SetupHeader::Stored, - SetupHeader::Zlib, - SetupHeader::BZip2, - SetupHeader::LZMA1 +STORED_ENUM_MAP(StoredCompressionMethod2, stream::chunk::Unknown, + stream::chunk::Stored, + stream::chunk::Zlib, + stream::chunk::BZip2, + stream::chunk::LZMA1 ); // 5.3.9+ -STORED_ENUM_MAP(StoredCompressionMethod3, SetupHeader::Unknown, - SetupHeader::Stored, - SetupHeader::Zlib, - SetupHeader::BZip2, - SetupHeader::LZMA1, - SetupHeader::LZMA2 +STORED_ENUM_MAP(StoredCompressionMethod3, stream::chunk::Unknown, + stream::chunk::Stored, + stream::chunk::Zlib, + stream::chunk::BZip2, + stream::chunk::LZMA1, + stream::chunk::LZMA2 ); STORED_ENUM_MAP(StoredDisablePage, SetupHeader::Auto, @@ -528,7 +528,7 @@ void SetupHeader::load(std::istream & is, const InnoVersion & version) { } if(version < INNO_VERSION(4, 1, 5)) { - compressMethod = (options & BzipUsed) ? BZip2 : Zlib; + compressMethod = (options & BzipUsed) ? stream::chunk::BZip2 : stream::chunk::Zlib; } if(version < INNO_VERSION(5, 3, 3)) { @@ -662,12 +662,3 @@ ENUM_NAMES(SetupHeader::LanguageDetection, "Language Detection", "locale", "none", ) - -ENUM_NAMES(SetupHeader::CompressionMethod, "Compression Method", - "stored", - "zlib", - "bzip2", - "lzma1", - "lzma2", - "unknown", -) diff --git a/src/setup/SetupHeader.hpp b/src/setup/SetupHeader.hpp index 392505f..d2c5828 100644 --- a/src/setup/SetupHeader.hpp +++ b/src/setup/SetupHeader.hpp @@ -11,6 +11,7 @@ #include "crypto/Checksum.hpp" #include "setup/Version.hpp" #include "setup/WindowsVersion.hpp" +#include "stream/chunk.hpp" #include "util/enum.hpp" #include "util/flags.hpp" @@ -202,15 +203,7 @@ struct SetupHeader { }; LanguageDetection languageDetectionMethod; - enum CompressionMethod { - Stored, - Zlib, - BZip2, - LZMA1, - LZMA2, - Unknown - }; - CompressionMethod compressMethod; + stream::chunk::compression_method compressMethod; Architectures architecturesAllowed; Architectures architecturesInstallIn64BitMode; @@ -247,6 +240,4 @@ NAMED_ENUM(SetupHeader::Privileges) NAMED_ENUM(SetupHeader::LanguageDetection) -NAMED_ENUM(SetupHeader::CompressionMethod) - #endif // INNOEXTRACT_SETUP_SETUPHEADER_HPP diff --git a/src/stream/BlockFilter.hpp b/src/stream/BlockFilter.hpp deleted file mode 100644 index 81498cf..0000000 --- a/src/stream/BlockFilter.hpp +++ /dev/null @@ -1,106 +0,0 @@ - -#ifndef INNOEXTRACT_STREAM_BLOCKFILTER_HPP -#define INNOEXTRACT_STREAM_BLOCKFILTER_HPP - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "crypto/CRC32.hpp" -#include "util/endian.hpp" - -struct block_error : public std::ios_base::failure { - - inline block_error(std::string msg) : failure(msg) { } - -}; - -/*! - * A filter that reads a block of 4096-byte chunks where each chunk is preceeded by - * a CRC32 checksum. The last chunk can be shorter than 4096 bytes. - * - * If chunk checksum is wrong a block_error is thrown before any data of that - * chunk is returned. - * - * block_error is also thrown if there is trailing data: 0 < (total size % (4096 + 4)) < 5 - */ -class inno_block_filter : public boost::iostreams::multichar_input_filter { - -private: - - typedef boost::iostreams::multichar_input_filter base_type; - -public: - - typedef base_type::char_type char_type; - typedef base_type::category category; - - inno_block_filter() : pos(0), length(0) { } - - template - bool read_chunk(Source & src) { - - uint32_t blockCrc32; - char temp[sizeof(blockCrc32)]; - std::streamsize nread = boost::iostreams::read(src, temp, sizeof(temp)); - if(nread == EOF) { - return false; - } else if(nread != sizeof(temp)) { - throw block_error("unexpected block end"); - } - std::memcpy(&blockCrc32, temp, sizeof(blockCrc32)); - blockCrc32 = little_endian::byteswap_if_alien(blockCrc32); - - length = size_t(boost::iostreams::read(src, buffer, sizeof(buffer))); - if(length == size_t(EOF)) { - throw block_error("unexpected block end"); - } - - Crc32 actual; - actual.init(); - actual.update(buffer, length); - if(actual.finalize() != blockCrc32) { - throw block_error("block CRC32 mismatch"); - } - - pos = 0; - - return true; - } - - template - std::streamsize read(Source & src, char * dest, std::streamsize n) { - - std::streamsize read = 0; - while(n) { - - if(pos == length && !read_chunk(src)) { - return read ? read : EOF; - } - - std::streamsize size = std::min(n, std::streamsize(length - pos)); - - std::copy(buffer + pos, buffer + pos + size, dest + read); - - pos += size_t(size), n -= size, read += size; - } - - return read; - } - -private: - - size_t pos; //! Current read position in the buffer. - size_t length; //! Length of the buffer. This is always 4096 except for the last chunk. - char buffer[4096]; - -}; - -#endif // INNOEXTRACT_STREAM_BLOCKFILTER_HPP diff --git a/src/stream/BlockReader.cpp b/src/stream/BlockReader.cpp deleted file mode 100644 index c7e2ad9..0000000 --- a/src/stream/BlockReader.cpp +++ /dev/null @@ -1,86 +0,0 @@ - -#include "stream/BlockReader.hpp" - -#include -#include - -#include -#include -#include - -#include "crypto/CRC32.hpp" -#include "setup/Version.hpp" -#include "stream/BlockFilter.hpp" -#include "stream/LzmaFilter.hpp" -#include "util/enum.hpp" -#include "util/load.hpp" -#include "util/log.hpp" -#include "util/util.hpp" - -namespace io = boost::iostreams; - -namespace { - -enum BlockCompression { - Stored, - Zlib, - LZMA1, -}; - -} // anonymous namespace - -NAMED_ENUM(BlockCompression) - -ENUM_NAMES(BlockCompression, "Compression", "stored", "zlib", "lzma1") - -std::istream * BlockReader::get(std::istream & base, const InnoVersion & version) { - - uint32_t expectedCrc = load_number(base); - Crc32 actualCrc; - actualCrc.init(); - - uint32_t storedSize; - BlockCompression compression; - - if(version >= INNO_VERSION(4, 0, 9)) { - storedSize = actualCrc.load(base); - uint8_t compressed = actualCrc.load(base); - compression = compressed ? (version >= INNO_VERSION(4, 1, 6) ? LZMA1 : Zlib) : Stored; - - } else { - - uint32_t compressedSize = actualCrc.load(base); - uint32_t uncompressedSize = actualCrc.load(base); - - if(compressedSize == uint32_t(-1)) { - storedSize = uncompressedSize, compression = Stored; - } else { - storedSize = compressedSize, compression = Zlib; - } - - // Add the size of a CRC32 checksum for each 4KiB subblock. - storedSize += uint32_t(ceildiv(storedSize, 4096) * 4); - } - - if(actualCrc.finalize() != expectedCrc) { - log_error << "block CRC32 mismatch"; - return NULL; - } - - debug("[block] size: " << storedSize << " compression: " << compression); - - io::filtering_istream * fis; - fis = new io::filtering_istream; - - switch(compression) { - case Stored: break; - case Zlib: fis->push(io::zlib_decompressor(), 8192); break; - case LZMA1: fis->push(inno_lzma1_decompressor(), 8192); break; - } - - fis->push(inno_block_filter(), 4096); - - fis->push(io::restrict(base, 0, storedSize)); - - return fis; -} diff --git a/src/stream/BlockReader.hpp b/src/stream/BlockReader.hpp deleted file mode 100644 index bb4fe1c..0000000 --- a/src/stream/BlockReader.hpp +++ /dev/null @@ -1,20 +0,0 @@ - -#ifndef INNOEXTRACT_STREAM_BLOCKREADER_HPP -#define INNOEXTRACT_STREAM_BLOCKREADER_HPP - -#include - -struct InnoVersion; - -/*! - * Reads a compressed and checksumed block of data used to store the setup headers. - */ -class BlockReader { - -public: - - static std::istream * get(std::istream & base, const InnoVersion & version); - -}; - -#endif // INNOEXTRACT_STREAM_BLOCKREADER_HPP diff --git a/src/stream/ChunkReader.cpp b/src/stream/ChunkReader.cpp deleted file mode 100644 index 2b26123..0000000 --- a/src/stream/ChunkReader.cpp +++ /dev/null @@ -1,37 +0,0 @@ - -#include "ChunkReader.hpp" - -#include "SliceReader.hpp" - -const char chunkId[4] = { 'z', 'l', 'b', 0x1a }; - -ChunkReader::Chunk::Chunk(size_t _firstSlice, uint32_t _chunkOffset, uint64_t _chunkSize, - bool _compressed, bool _encrypted) - : firstSlice(_firstSlice), chunkOffset(_chunkOffset), chunkSize(_chunkSize), - compressed(_compressed), encrypted(_encrypted) { } - -bool ChunkReader::Chunk::operator<(const ChunkReader::Chunk & o) const { - - if(firstSlice != o.firstSlice) { - return (firstSlice < o.firstSlice); - } else if(chunkOffset != o.chunkOffset) { - return (chunkOffset < o.chunkOffset); - } else if(chunkSize != o.chunkSize) { - return (chunkSize < o.chunkSize); - } else if(compressed != o.compressed) { - return (compressed < o.compressed); - } else if(encrypted != o.encrypted) { - return (encrypted < o.encrypted); - } - - return false; -} - -bool ChunkReader::Chunk::operator==(const ChunkReader::Chunk & o) const { - - return (firstSlice == o.firstSlice - && chunkOffset == o.chunkOffset - && chunkSize == o.chunkSize - && compressed == o.compressed - && encrypted == o.encrypted); -} diff --git a/src/stream/ChunkReader.hpp b/src/stream/ChunkReader.hpp deleted file mode 100644 index 467d4b5..0000000 --- a/src/stream/ChunkReader.hpp +++ /dev/null @@ -1,60 +0,0 @@ - -#ifndef INNOEXTRACT_STREAM_CHUNKREADER_HPP -#define INNOEXTRACT_STREAM_CHUNKREADER_HPP - -#include -#include -#include -#include -#include - -class silce_source; - -class ChunkReader { - - uint64_t storedSize; - std::string password; - std::string salt; - -public: - - struct Chunk { - - size_t firstSlice; //!< Slice where the chunk starts. - uint32_t chunkOffset; //!< Offset of the compressed chunk in firstSlice. - uint64_t chunkSize; //! Total compressed size of the chunk. - - bool compressed; - bool encrypted; - - Chunk(size_t firstSlice, uint32_t chunkOffset, uint64_t chunkSize, - bool compressed, bool encrypted); - - bool operator<(const Chunk & o) const; - bool operator==(const Chunk & o) const; - }; - - explicit ChunkReader(uint64_t _storedSize) : storedSize(_storedSize) { } - - uint64_t chunkDecompressedBytesRead; - uint64_t chunkCompressedBytesLeft; - - enum CompressionMethod { }; - CompressionMethod chunkCompression; - - boost::shared_ptr get(boost::shared_ptr base); - -}; - -/* - * - * Open chunk: - * - * seek to dataOffset + firstSliceOffset - * 4 bytes -> chunkId - * - * - * - */ - -#endif // INNOEXTRACT_STREAM_CHUNKREADER_HPP diff --git a/src/stream/SliceReader.cpp b/src/stream/SliceReader.cpp deleted file mode 100644 index 7774c84..0000000 --- a/src/stream/SliceReader.cpp +++ /dev/null @@ -1,192 +0,0 @@ - -#include "stream/SliceReader.hpp" - -#include -#include -#include -#include - -#include "util/console.hpp" -#include "util/load.hpp" -#include "util/log.hpp" -#include "util/util.hpp" - -using std::string; - -namespace { - -const char sliceIds[][8] = { - { 'i', 'd', 's', 'k', 'a', '1', '6', 0x1a }, - { 'i', 'd', 's', 'k', 'a', '3', '2', 0x1a }, -}; - -} // anonymous namespace - -SliceReader::SliceReader(const string & setupFile, uint32_t _dataOffset) - : dir(), lastDir(), baseFile(), dataOffset(_dataOffset), slicesPerDisk(1), - currentSlice(0) { - - ifs.open(setupFile.c_str(), std::ios_base::binary | std::ios_base::in | std::ios_base::ate); - - sliceSize = uint32_t(std::min(ifs.tellg(), std::numeric_limits::max())); - if(ifs.seekg(dataOffset).fail()) { - ifs.close(); - } -} - -SliceReader::SliceReader(const std::string & _dir, const std::string & _baseFile, - size_t _slicesPerDisk) - : dir(_dir), lastDir(_dir), baseFile(_baseFile), dataOffset(0), slicesPerDisk(_slicesPerDisk), - currentSlice(0) { } - -bool SliceReader::seek(size_t slice) { - - if(slice == currentSlice && ifs.is_open()) { - return true; - } - - if(dataOffset != 0) { - log_error << "[slice] cannot change slices in single-file setup"; - return false; - } - - return open(slice, string()); -} - -bool SliceReader::openFile(const std::string & file) { - - log_info << "[slice] opening " << file; - - ifs.close(); - - ifs.open(file.c_str(), std::ios_base::in | std::ios_base::binary | std::ios_base::ate); - if(ifs.fail()) { - return false; - } - - std::streampos fileSize = ifs.tellg(); - ifs.seekg(0); - - char magic[8]; - if(ifs.read(magic, 8).fail()) { - ifs.close(); - log_error << "[slice] error reading magic number"; - return false; - } - bool found = false; - for(size_t i = 0; ARRAY_SIZE(sliceIds); i++) { - if(!std::memcmp(magic, sliceIds[i], 8)) { - found = true; - break; - } - } - if(!found) { - log_error << "[slice] bad magic number"; - ifs.close(); - return false; - } - - sliceSize = load_number(ifs); - if(ifs.fail() || std::streampos(sliceSize) > fileSize) { - log_error << "[slice] bad slice size: " << sliceSize << " > " << fileSize; - ifs.close(); - return false; - } - - sliceFile = file; - - size_t dirpos = file.find_last_of('/'); - if(dirpos == string::npos) { - lastDir.clear(); - } else { - lastDir = file.substr(0, dirpos + 1); - } - - return true; -} - -bool SliceReader::open(size_t slice, const std::string & file) { - - currentSlice = slice; - ifs.close(); - - if(slicesPerDisk == 0) { - log_error << "[slice] slices per disk must not be zero"; - return false; - } - - std::string sliceFile = file; - - if(sliceFile.empty()) { - - std::ostringstream oss; - oss << baseFile << '-'; - if(slicesPerDisk == 1) { - oss << (slice + 1); - } else { - size_t major = (slice / slicesPerDisk) + 1; - size_t minor = slice % slicesPerDisk; - oss << major << char(uint8_t('a') + minor); - } - oss << ".bin"; - - sliceFile = oss.str(); - } - - if(sliceFile[0] == '/') { - return openFile(sliceFile); - } - - if(openFile(lastDir + sliceFile)) { - return true; - } - - if(dir != lastDir && openFile(dir + sliceFile)) { - return true; - } - - return false; -} - -bool SliceReader::seek(size_t slice, uint32_t offset) { - - if(!seek(slice)) { - return false; - } - - if(offset > sliceSize - dataOffset) { - return false; - } - - if(ifs.seekg(dataOffset + offset).fail()) { - return false; - } - - return true; -} - -std::streamsize SliceReader::read(char * buffer, std::streamsize bytes) { - - std::streamsize nread = 0; - - if(!seek(currentSlice)) { - return nread; - } - - while(bytes > 0) { - - std::streamsize remaining = std::streamsize(sliceSize - size_t(ifs.tellg())); - if(!remaining) { - if(!seek(currentSlice + 1)) { - return nread; - } - remaining = std::streamsize(sliceSize - size_t(ifs.tellg())); - } - - std::streamsize read = ifs.read(buffer, std::min(remaining, bytes)).gcount(); - - nread += read, buffer += read, bytes -= read; - } - - return nread; -} diff --git a/src/stream/SliceReader.hpp b/src/stream/SliceReader.hpp deleted file mode 100644 index fc8f23c..0000000 --- a/src/stream/SliceReader.hpp +++ /dev/null @@ -1,49 +0,0 @@ - -#ifndef INNOEXTRACT_STREAM_SLICEREADER_HPP -#define INNOEXTRACT_STREAM_SLICEREADER_HPP - -#include - -#include - -class SliceReader : public boost::iostreams::source { - - std::string dir; - std::string lastDir; - std::string baseFile; - const uint32_t dataOffset; - const size_t slicesPerDisk; - - size_t currentSlice; - std::string sliceFile; - uint32_t sliceSize; - - std::ifstream ifs; - - bool seek(size_t slice); - bool openFile(const std::string & file); - -public: - - SliceReader(const std::string & setupFile, uint32_t dataOffset); - - /*! - * if Ver>=4107 then baseFile := PathChangeExt(PathExtractName(SetupLdrOriginalFilename), '') - * else baseFile:=SetupHeader.BaseFilename; - */ - SliceReader(const std::string & dir, const std::string & baseFile, size_t slicesPerDisk); - - bool seek(size_t slice, uint32_t offset); - - std::streamsize read(char * buffer, std::streamsize bytes); - - inline size_t slice() { return currentSlice; } - inline std::string & file() { return sliceFile; } - - bool open(size_t slice, const std::string & sliceFile); - - inline bool isOpen() { return ifs.is_open(); } - -}; - -#endif // INNOEXTRACT_STREAM_SLICEREADER_HPP diff --git a/src/stream/block.cpp b/src/stream/block.cpp new file mode 100644 index 0000000..418be46 --- /dev/null +++ b/src/stream/block.cpp @@ -0,0 +1,183 @@ + +#include "stream/block.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "crypto/CRC32.hpp" +#include "setup/Version.hpp" +#include "stream/lzma.hpp" +#include "util/endian.hpp" +#include "util/enum.hpp" +#include "util/load.hpp" +#include "util/log.hpp" +#include "util/util.hpp" + +namespace io = boost::iostreams; + +namespace stream { + +namespace { + +enum block_compression { + Stored, + Zlib, + LZMA1, +}; + +/*! + * A filter that reads a block of 4096-byte chunks where each chunk is preceeded by + * a CRC32 checksum. The last chunk can be shorter than 4096 bytes. + * + * If chunk checksum is wrong a block_error is thrown before any data of that + * chunk is returned. + * + * block_error is also thrown if there is trailing data: 0 < (total size % (4096 + 4)) < 5 + */ +class inno_block_filter : public boost::iostreams::multichar_input_filter { + +private: + + typedef boost::iostreams::multichar_input_filter base_type; + +public: + + typedef base_type::char_type char_type; + typedef base_type::category category; + + inno_block_filter() : pos(0), length(0) { } + + template + bool read_chunk(Source & src) { + + uint32_t block_crc32; + char temp[sizeof(block_crc32)]; + std::streamsize nread = boost::iostreams::read(src, temp, sizeof(temp)); + if(nread == EOF) { + return false; + } else if(nread != sizeof(temp)) { + throw block_error("unexpected block end"); + } + std::memcpy(&block_crc32, temp, sizeof(block_crc32)); + block_crc32 = little_endian::byteswap_if_alien(block_crc32); + + length = size_t(boost::iostreams::read(src, buffer, sizeof(buffer))); + if(length == size_t(EOF)) { + throw block_error("unexpected block end"); + } + + Crc32 actual; + actual.init(); + actual.update(buffer, length); + if(actual.finalize() != block_crc32) { + throw block_error("block CRC32 mismatch"); + } + + pos = 0; + + return true; + } + + template + std::streamsize read(Source & src, char * dest, std::streamsize n) { + + std::streamsize read = 0; + while(n) { + + if(pos == length && !read_chunk(src)) { + return read ? read : EOF; + } + + std::streamsize size = std::min(n, std::streamsize(length - pos)); + + std::copy(buffer + pos, buffer + pos + size, dest + read); + + pos += size_t(size), n -= size, read += size; + } + + return read; + } + +private: + + size_t pos; //! Current read position in the buffer. + size_t length; //! Length of the buffer. This is always 4096 except for the last chunk. + char buffer[4096]; + +}; + +} // anonymous namespace + +} // namespace stream + +NAMED_ENUM(stream::block_compression) + +ENUM_NAMES(stream::block_compression, "Compression", "stored", "zlib", "lzma1") + +namespace stream { + +block_reader::pointer block_reader::get(std::istream & base, const InnoVersion & version) { + + uint32_t expected_checksum = load_number(base); + Crc32 actual_checksum; + actual_checksum.init(); + + uint32_t stored_size; + block_compression compression; + + if(version >= INNO_VERSION(4, 0, 9)) { + + stored_size = actual_checksum.load(base); + uint8_t compressed = actual_checksum.load(base); + + compression = compressed ? (version >= INNO_VERSION(4, 1, 6) ? LZMA1 : Zlib) : Stored; + + } else { + + uint32_t compressed_size = actual_checksum.load(base); + uint32_t uncompressed_size = actual_checksum.load(base); + + if(compressed_size == uint32_t(-1)) { + stored_size = uncompressed_size, compression = Stored; + } else { + stored_size = compressed_size, compression = Zlib; + } + + // Add the size of a CRC32 checksum for each 4KiB subblock. + stored_size += uint32_t(ceildiv(stored_size, 4096) * 4); + } + + if(actual_checksum.finalize() != expected_checksum) { + throw block_error("block CRC32 mismatch"); + } + + debug("[block] size: " << stored_size << " compression: " << compression); + + boost::shared_ptr fis = boost::make_shared(); + + switch(compression) { + case Stored: break; + case Zlib: fis->push(io::zlib_decompressor(), 8192); break; + case LZMA1: fis->push(inno_lzma1_decompressor(), 8192); break; + } + + fis->push(inno_block_filter(), 4096); + + fis->push(io::restrict(base, 0, stored_size)); + + return fis; +} + +} // namespace stream diff --git a/src/stream/block.hpp b/src/stream/block.hpp new file mode 100644 index 0000000..23c48b2 --- /dev/null +++ b/src/stream/block.hpp @@ -0,0 +1,35 @@ + +#ifndef INNOEXTRACT_STREAM_BLOCK_HPP +#define INNOEXTRACT_STREAM_BLOCK_HPP + +#include + +#include + +struct InnoVersion; + +namespace stream { + +struct block_error : public std::ios_base::failure { + + inline block_error(std::string msg) : failure(msg) { } + +}; + +/*! + * Reads a compressed and checksumed block of data used to store the setup headers. + */ +class block_reader { + +public: + + typedef std::istream type; + typedef boost::shared_ptr pointer; + + static pointer get(std::istream & base, const InnoVersion & version); + +}; + +} // namespace stream + +#endif // INNOEXTRACT_STREAM_BLOCK_HPP diff --git a/src/stream/ChecksumFilter.hpp b/src/stream/checksum.hpp similarity index 82% rename from src/stream/ChecksumFilter.hpp rename to src/stream/checksum.hpp index f6eba28..498cf57 100644 --- a/src/stream/ChecksumFilter.hpp +++ b/src/stream/checksum.hpp @@ -1,12 +1,14 @@ -#ifndef INNOEXTRACT_CHECKSUMFILTER_HPP -#define INNOEXTRACT_CHECKSUMFILTER_HPP +#ifndef INNOEXTRACT_STREAM_CHECKSUM_HPP +#define INNOEXTRACT_STREAM_CHECKSUM_HPP #include #include #include "crypto/Hasher.hpp" +namespace stream { + class checksum_filter : public boost::iostreams::multichar_input_filter { private: @@ -39,4 +41,6 @@ private: }; -#endif // INNOEXTRACT_CHECKSUMFILTER_HPP +} // namespace stream + +#endif // INNOEXTRACT_STREAM_CHECKSUM_HPP diff --git a/src/stream/chunk.cpp b/src/stream/chunk.cpp new file mode 100644 index 0000000..367b396 --- /dev/null +++ b/src/stream/chunk.cpp @@ -0,0 +1,87 @@ + +#include "chunk.hpp" + +#include +#include +#include +#include +#include + +#include "stream/lzma.hpp" +#include "stream/slice.hpp" +#include "util/log.hpp" +#include "util/util.hpp" + +namespace io = boost::iostreams; + +namespace stream { + +static const char chunk_id[4] = { 'z', 'l', 'b', 0x1a }; + +chunk::chunk(size_t first_slice, uint32_t offset, uint64_t size, + compression_method compression, bool encrypted) + : first_slice(first_slice), offset(offset), size(size), + compression(compression), encrypted(encrypted) { } + +bool chunk::operator<(const chunk & o) const { + + if(first_slice != o.first_slice) { + return (first_slice < o.first_slice); + } else if(offset != o.offset) { + return (offset < o.offset); + } else if(size != o.size) { + return (size < o.size); + } else if(compression != o.compression) { + return (compression < o.compression); + } else if(encrypted != o.encrypted) { + return (encrypted < o.encrypted); + } + + return false; +} + +bool chunk::operator==(const chunk & o) const { + return (first_slice == o.first_slice + && offset == o.offset + && size == o.size + && compression == o.compression + && encrypted == o.encrypted); +} + +chunk_reader::pointer chunk_reader::get(slice_reader & base, const chunk & chunk) { + + if(!base.seek(chunk.first_slice, chunk.offset)) { + throw chunk_error("error seeking"); + } + + char magic[ARRAY_SIZE(chunk_id)]; + if(base.read(magic, 4) != 4 || memcmp(magic, chunk_id, ARRAY_SIZE(chunk_id))) { + throw chunk_error("bad chunk magic"); + } + + pointer result = boost::make_shared(); + + switch(chunk.compression) { + case chunk::Stored: break; + case chunk::Zlib: result->push(io::zlib_decompressor(), 8192); break; + case chunk::BZip2: result->push(io::bzip2_decompressor(), 8192); break; + case chunk::LZMA1: result->push(inno_lzma1_decompressor(), 8192); break; + case chunk::LZMA2: result->push(inno_lzma2_decompressor(), 8192); break; + default: throw chunk_error("unknown compression"); + } + + result->push(io::restrict(boost::ref(base), 0, int64_t(chunk.size))); + + return result; +} + +} // namespace stream + +ENUM_NAMES(stream::chunk::compression_method, "Compression Method", + "stored", + "zlib", + "bzip2", + "lzma1", + "lzma2", + "unknown", +) diff --git a/src/stream/chunk.hpp b/src/stream/chunk.hpp new file mode 100644 index 0000000..bb2f9ec --- /dev/null +++ b/src/stream/chunk.hpp @@ -0,0 +1,67 @@ + +#ifndef INNOEXTRACT_STREAM_CHUNK_HPP +#define INNOEXTRACT_STREAM_CHUNK_HPP + +#include +#include +#include + +#include +#include + +#include "util/enum.hpp" + +namespace stream { + +class slice_reader; + +struct chunk_error : public std::ios_base::failure { + + inline chunk_error(std::string msg) : failure(msg) { } + +}; + +struct chunk { + + enum compression_method { + Stored, + Zlib, + BZip2, + LZMA1, + LZMA2, + Unknown + }; + + size_t first_slice; //!< Slice where the chunk starts. + + uint32_t offset; //!< Offset of the compressed chunk in firstSlice. + uint64_t size; //! Total compressed size of the chunk. + + compression_method compression; + bool encrypted; + + chunk(size_t first_slice, uint32_t offset, uint64_t size, + compression_method compression, bool encrypted); + + bool operator<(const chunk & o) const; + bool operator==(const chunk & o) const; +}; + +class silce_source; + +class chunk_reader { + +public: + + typedef boost::iostreams::chain type; + typedef boost::shared_ptr pointer; + + static pointer get(slice_reader & base, const chunk & chunk); + +}; + +} // namespace stream + +NAMED_ENUM(stream::chunk::compression_method) + +#endif // INNOEXTRACT_STREAM_CHUNK_HPP diff --git a/src/stream/InstructionFilter.hpp b/src/stream/file.cpp similarity index 74% rename from src/stream/InstructionFilter.hpp rename to src/stream/file.cpp index 1a7de12..5002f9e 100644 --- a/src/stream/InstructionFilter.hpp +++ b/src/stream/file.cpp @@ -1,14 +1,21 @@ -#ifndef INNOEXTRACT_STREAM_INSTRUCTIONFILTER_HPP -#define INNOEXTRACT_STREAM_INSTRUCTIONFILTER_HPP +#include "stream/file.hpp" -#include -#include +#include +#include +#include -#include -#include +#include "setup/FileLocationEntry.hpp" +#include "setup/Version.hpp" +#include "stream/checksum.hpp" -class call_instruction_decoder_4108 : public boost::iostreams::multichar_input_filter { +namespace io = boost::iostreams; + +namespace stream { + +namespace { + +class inno_exe_decoder_4108 : public boost::iostreams::multichar_input_filter { private: @@ -19,12 +26,14 @@ public: typedef base_type::char_type char_type; typedef base_type::category category; - inline call_instruction_decoder_4108() { cclose(); } + inno_exe_decoder_4108() { close(0); } template std::streamsize read(Source & src, char * dest, std::streamsize n); - inline void cclose() { + + template + void close(const Source &) { addr_bytes_left = 0, addr_offset = 5; } @@ -34,7 +43,7 @@ public: }; -class call_instruction_decoder_5200 : public boost::iostreams::multichar_input_filter { +class inno_exe_decoder_5200 : public boost::iostreams::multichar_input_filter { private: @@ -45,13 +54,14 @@ public: typedef base_type::char_type char_type; typedef base_type::category category; - inline call_instruction_decoder_5200(bool _flip_high_byte) - : flip_high_byte(_flip_high_byte) { cclose(); } + inno_exe_decoder_5200(bool _flip_high_byte) + : flip_high_byte(_flip_high_byte) { close(0); } template std::streamsize read(Source & src, char * dest, std::streamsize n); - inline void cclose() { + template + void close(const Source &) { offset = 0, flush_bytes = 0; } @@ -90,7 +100,7 @@ private: // Implementation: template -std::streamsize call_instruction_decoder_4108::read(Source & src, char * dest, std::streamsize n) { +std::streamsize inno_exe_decoder_4108::read(Source & src, char * dest, std::streamsize n) { for(std::streamsize i = 0; i < n; i++, addr_offset++) { @@ -120,7 +130,7 @@ std::streamsize call_instruction_decoder_4108::read(Source & src, char * dest, s } template -std::streamsize call_instruction_decoder_5200::read(Source & src, char * dest, std::streamsize n) { +std::streamsize inno_exe_decoder_5200::read(Source & src, char * dest, std::streamsize n) { char * end = dest + n; @@ -215,4 +225,28 @@ std::streamsize call_instruction_decoder_5200::read(Source & src, char * dest, s } -#endif // INNOEXTRACT_STREAM_INSTRUCTIONFILTER_HPP +} // anonymous namespace + +file_reader::pointer file_reader::get(base_type & base, const FileLocationEntry & location, + const InnoVersion & version, Hasher & hasher) { + + hasher.init(location.checksum.type); + + boost::shared_ptr result = boost::make_shared(); + + result->push(stream::checksum_filter(&hasher), 8192); + + if(location.options & FileLocationEntry::CallInstructionOptimized) { + if(version < INNO_VERSION(5, 2, 0)) { + result->push(inno_exe_decoder_4108(), 8192); + } else { + result->push(inno_exe_decoder_5200(version >= INNO_VERSION(5, 3, 9)), 8192); + } + } + + result->push(io::restrict(base, 0, int64_t(location.file_size))); + + return result; +} + +} // namespace stream diff --git a/src/stream/file.hpp b/src/stream/file.hpp new file mode 100644 index 0000000..80925c6 --- /dev/null +++ b/src/stream/file.hpp @@ -0,0 +1,32 @@ + +#ifndef INNOEXTRACT_STREAM_FILE_HPP +#define INNOEXTRACT_STREAM_FILE_HPP + +#include + +#include +#include + +class Hasher; +struct FileLocationEntry; +struct InnoVersion; + +namespace stream { + +class file_reader { + + typedef boost::iostreams::chain base_type; + +public: + + typedef std::istream type; + typedef boost::shared_ptr pointer; + + static pointer get(base_type & base, const FileLocationEntry & location, + const InnoVersion & version, Hasher & hasher); + +}; + +} + +#endif // INNOEXTRACT_STREAM_FILE_HPP diff --git a/src/stream/LzmaFilter.cpp b/src/stream/lzma.cpp similarity index 97% rename from src/stream/LzmaFilter.cpp rename to src/stream/lzma.cpp index fb7688f..3a23761 100644 --- a/src/stream/LzmaFilter.cpp +++ b/src/stream/lzma.cpp @@ -1,10 +1,12 @@ -#include "stream/LzmaFilter.hpp" +#include "stream/lzma.hpp" #include #include +namespace stream { + static lzma_stream * init_raw_lzma_stream(lzma_vli filter, lzma_options_lzma & options) { options.preset_dict = NULL; @@ -124,3 +126,5 @@ bool inno_lzma2_decompressor_impl::filter(const char * & begin_in, const char * return lzma_decompressor_impl_base::filter(begin_in, end_in, begin_out, end_out, flush); } + +} // namespace stream diff --git a/src/stream/LzmaFilter.hpp b/src/stream/lzma.hpp similarity index 94% rename from src/stream/LzmaFilter.hpp rename to src/stream/lzma.hpp index 09a475f..c8c1c97 100644 --- a/src/stream/LzmaFilter.hpp +++ b/src/stream/lzma.hpp @@ -1,11 +1,14 @@ -#ifndef INNOEXTRACT_STREAM_LZMAFILTER_HPP -#define INNOEXTRACT_STREAM_LZMAFILTER_HPP +#ifndef INNOEXTRACT_STREAM_LZMA_HPP +#define INNOEXTRACT_STREAM_LZMA_HPP #include +#include + #include #include -#include + +namespace stream { struct lzma_error : public std::ios_base::failure { @@ -95,4 +98,6 @@ typedef lzma_decompressor inno_lzma1_decompressor; */ typedef lzma_decompressor inno_lzma2_decompressor; -#endif // INNOEXTRACT_STREAM_LZMAFILTER_HPP +} // namespace stream + +#endif // INNOEXTRACT_STREAM_LZMA_HPP diff --git a/src/stream/slice.cpp b/src/stream/slice.cpp new file mode 100644 index 0000000..15e7be1 --- /dev/null +++ b/src/stream/slice.cpp @@ -0,0 +1,196 @@ + +#include "stream/slice.hpp" + +#include +#include +#include +#include + +#include "util/console.hpp" +#include "util/load.hpp" +#include "util/log.hpp" +#include "util/util.hpp" + +using std::string; + +namespace stream { + +namespace { + +const char slice_ids[][8] = { + { 'i', 'd', 's', 'k', 'a', '1', '6', 0x1a }, + { 'i', 'd', 's', 'k', 'a', '3', '2', 0x1a }, +}; + +} // anonymous namespace + +slice_reader::slice_reader(const string & setup_file, uint32_t data_offset) + : dir(), last_dir(), base_file(), data_offset(data_offset), slices_per_disk(1), + current_slice(0) { + + ifs.open(setup_file.c_str(), std::ios_base::binary | std::ios_base::in | std::ios_base::ate); + + slice_size = uint32_t(std::min(ifs.tellg(), std::numeric_limits::max())); + if(ifs.seekg(data_offset).fail()) { + ifs.close(); + } +} + +slice_reader::slice_reader(const std::string & dir, const std::string & base_file, + size_t slices_per_disk) + : dir(dir), last_dir(dir), base_file(base_file), data_offset(0), slices_per_disk(slices_per_disk), + current_slice(0) { } + +bool slice_reader::seek(size_t slice) { + + if(slice == current_slice && ifs.is_open()) { + return true; + } + + if(data_offset != 0) { + log_error << "[slice] cannot change slices in single-file setup"; + return false; + } + + return open(slice, string()); +} + +bool slice_reader::openFile(const std::string & file) { + + log_info << "[slice] opening " << file; + + ifs.close(); + + ifs.open(file.c_str(), std::ios_base::in | std::ios_base::binary | std::ios_base::ate); + if(ifs.fail()) { + return false; + } + + std::streampos fileSize = ifs.tellg(); + ifs.seekg(0); + + char magic[8]; + if(ifs.read(magic, 8).fail()) { + ifs.close(); + log_error << "[slice] error reading magic number"; + return false; + } + bool found = false; + for(size_t i = 0; ARRAY_SIZE(slice_ids); i++) { + if(!std::memcmp(magic, slice_ids[i], 8)) { + found = true; + break; + } + } + if(!found) { + log_error << "[slice] bad magic number"; + ifs.close(); + return false; + } + + slice_size = load_number(ifs); + if(ifs.fail() || std::streampos(slice_size) > fileSize) { + log_error << "[slice] bad slice size: " << slice_size << " > " << fileSize; + ifs.close(); + return false; + } + + slice_file = file; + + size_t dirpos = file.find_last_of('/'); + if(dirpos == string::npos) { + last_dir.clear(); + } else { + last_dir = file.substr(0, dirpos + 1); + } + + return true; +} + +bool slice_reader::open(size_t slice, const std::string & file) { + + current_slice = slice; + ifs.close(); + + if(slices_per_disk == 0) { + log_error << "[slice] slices per disk must not be zero"; + return false; + } + + std::string sliceFile = file; + + if(sliceFile.empty()) { + + std::ostringstream oss; + oss << base_file << '-'; + if(slices_per_disk == 1) { + oss << (slice + 1); + } else { + size_t major = (slice / slices_per_disk) + 1; + size_t minor = slice % slices_per_disk; + oss << major << char(uint8_t('a') + minor); + } + oss << ".bin"; + + sliceFile = oss.str(); + } + + if(sliceFile[0] == '/') { + return openFile(sliceFile); + } + + if(openFile(last_dir + sliceFile)) { + return true; + } + + if(dir != last_dir && openFile(dir + sliceFile)) { + return true; + } + + return false; +} + +bool slice_reader::seek(size_t slice, uint32_t offset) { + + if(!seek(slice)) { + return false; + } + + if(offset > slice_size - data_offset) { + return false; + } + + if(ifs.seekg(data_offset + offset).fail()) { + return false; + } + + return true; +} + +std::streamsize slice_reader::read(char * buffer, std::streamsize bytes) { + + std::streamsize nread = 0; + + if(!seek(current_slice)) { + return nread; + } + + while(bytes > 0) { + + std::streamsize remaining = std::streamsize(slice_size - size_t(ifs.tellg())); + if(!remaining) { + if(!seek(current_slice + 1)) { + return nread; + } + remaining = std::streamsize(slice_size - size_t(ifs.tellg())); + } + + std::streamsize read = ifs.read(buffer, std::min(remaining, bytes)).gcount(); + + nread += read, buffer += read, bytes -= read; + } + + return nread; +} + +} // namespace stream diff --git a/src/stream/slice.hpp b/src/stream/slice.hpp new file mode 100644 index 0000000..389e6a6 --- /dev/null +++ b/src/stream/slice.hpp @@ -0,0 +1,53 @@ + +#ifndef INNOEXTRACT_STREAM_SLICEREADER_HPP +#define INNOEXTRACT_STREAM_SLICEREADER_HPP + +#include + +#include + +namespace stream { + +class slice_reader : public boost::iostreams::source { + + std::string dir; + std::string last_dir; + std::string base_file; + const uint32_t data_offset; + const size_t slices_per_disk; + + size_t current_slice; + std::string slice_file; + uint32_t slice_size; + + std::ifstream ifs; + + bool seek(size_t slice); + bool openFile(const std::string & file); + +public: + + slice_reader(const std::string & setup_file, uint32_t data_offset); + + /*! + * if Ver>=4107 then baseFile := PathChangeExt(PathExtractName(SetupLdrOriginalFilename), '') + * else baseFile:=SetupHeader.BaseFilename; + */ + slice_reader(const std::string & dir, const std::string & base_file, size_t slices_per_disk); + + bool seek(size_t slice, uint32_t offset); + + std::streamsize read(char * buffer, std::streamsize bytes); + + inline size_t slice() { return current_slice; } + inline std::string & file() { return slice_file; } + + bool open(size_t slice, const std::string & slice_file); + + inline bool is_open() { return ifs.is_open(); } + +}; + +} // namespace stream + +#endif // INNOEXTRACT_STREAM_SLICEREADER_HPP diff --git a/src/util/console.cpp b/src/util/console.cpp index 14d9460..09a7650 100644 --- a/src/util/console.cpp +++ b/src/util/console.cpp @@ -74,7 +74,7 @@ void init() { } -} +} // namespace color namespace progress {