Browse Source

Clean stream code.

pull/1/head
Daniel Scharrer 15 years ago
parent
commit
d1a8ae35e1
  1. 9
      CMakeLists.txt
  2. 181
      src/InnoExtract.cpp
  3. 36
      src/crypto/Checksum.cpp
  4. 5
      src/crypto/Checksum.hpp
  5. 4
      src/setup/FileLocationEntry.cpp
  6. 2
      src/setup/FileLocationEntry.hpp
  7. 49
      src/setup/SetupHeader.cpp
  8. 13
      src/setup/SetupHeader.hpp
  9. 106
      src/stream/BlockFilter.hpp
  10. 86
      src/stream/BlockReader.cpp
  11. 20
      src/stream/BlockReader.hpp
  12. 37
      src/stream/ChunkReader.cpp
  13. 60
      src/stream/ChunkReader.hpp
  14. 192
      src/stream/SliceReader.cpp
  15. 49
      src/stream/SliceReader.hpp
  16. 183
      src/stream/block.cpp
  17. 35
      src/stream/block.hpp
  18. 10
      src/stream/checksum.hpp
  19. 87
      src/stream/chunk.cpp
  20. 67
      src/stream/chunk.hpp
  21. 66
      src/stream/file.cpp
  22. 32
      src/stream/file.hpp
  23. 6
      src/stream/lzma.cpp
  24. 13
      src/stream/lzma.hpp
  25. 196
      src/stream/slice.cpp
  26. 53
      src/stream/slice.hpp
  27. 2
      src/util/console.cpp

9
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

181
src/InnoExtract.cpp

@ -14,17 +14,11 @@
#include <boost/shared_ptr.hpp>
#include <boost/foreach.hpp>
#include <boost/ref.hpp>
#include <boost/make_shared.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/bzip2.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <boost/iostreams/restrict.hpp>
#include <crypto/Hasher.hpp>
@ -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<const SetupItem &>(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<std::istream> 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<ChunkReader::Chunk, std::vector<size_t> > Chunks;
typedef std::map<stream::chunk, std::vector<size_t> > 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<SliceReader> slice_reader;
boost::shared_ptr<stream::slice_reader> slice_reader;
if(offsets.dataOffset) {
slice_reader.reset(new SliceReader(argv[1], offsets.dataOffset));
slice_reader = boost::make_shared<stream::slice_reader>(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<stream::slice_reader>(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<io::input> 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<chunk_stream_type> 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;

36
src/crypto/Checksum.cpp

@ -3,6 +3,8 @@
#include <cstring>
#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;
}

5
src/crypto/Checksum.hpp

@ -4,7 +4,8 @@
#include <stdint.h>
#include <cstring>
#include <iostream>
#include <iosfwd>
#include <istream>
#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

4
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<uint64_t>(is);
file_size = load_number<uint64_t>(is);
chunkSize = load_number<uint64_t>(is);
} else {
fileSize = load_number<uint32_t>(is);
file_size = load_number<uint32_t>(is);
chunkSize = load_number<uint32_t>(is);
}

2
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;

49
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",
)

13
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

106
src/stream/BlockFilter.hpp

@ -1,106 +0,0 @@
#ifndef INNOEXTRACT_STREAM_BLOCKFILTER_HPP
#define INNOEXTRACT_STREAM_BLOCKFILTER_HPP
#include <stdint.h>
#include <cstring>
#include <string>
#include <algorithm>
#include <iosfwd>
#include <cassert>
#include <boost/iostreams/char_traits.hpp>
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/read.hpp>
#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<typename Source>
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<typename Source>
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

86
src/stream/BlockReader.cpp

@ -1,86 +0,0 @@
#include "stream/BlockReader.hpp"
#include <stdint.h>
#include <istream>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/restrict.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#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<uint32_t>(base);
Crc32 actualCrc;
actualCrc.init();
uint32_t storedSize;
BlockCompression compression;
if(version >= INNO_VERSION(4, 0, 9)) {
storedSize = actualCrc.load<little_endian, uint32_t>(base);
uint8_t compressed = actualCrc.load<little_endian, uint8_t>(base);
compression = compressed ? (version >= INNO_VERSION(4, 1, 6) ? LZMA1 : Zlib) : Stored;
} else {
uint32_t compressedSize = actualCrc.load<little_endian, uint32_t>(base);
uint32_t uncompressedSize = actualCrc.load<little_endian, uint32_t>(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<uint64_t>(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;
}

20
src/stream/BlockReader.hpp

@ -1,20 +0,0 @@
#ifndef INNOEXTRACT_STREAM_BLOCKREADER_HPP
#define INNOEXTRACT_STREAM_BLOCKREADER_HPP
#include <iosfwd>
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

37
src/stream/ChunkReader.cpp

@ -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);
}

60
src/stream/ChunkReader.hpp

@ -1,60 +0,0 @@
#ifndef INNOEXTRACT_STREAM_CHUNKREADER_HPP
#define INNOEXTRACT_STREAM_CHUNKREADER_HPP
#include <stddef.h>
#include <stdint.h>
#include <ios>
#include <boost/shared_ptr.hpp>
#include <boost/iostreams/filtering_stream.hpp>
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<std::istream> get(boost::shared_ptr<silce_source> base);
};
/*
*
* Open chunk:
*
* seek to dataOffset + firstSliceOffset
* 4 bytes -> chunkId
*
*
*
*/
#endif // INNOEXTRACT_STREAM_CHUNKREADER_HPP

192
src/stream/SliceReader.cpp

@ -1,192 +0,0 @@
#include "stream/SliceReader.hpp"
#include <stdint.h>
#include <sstream>
#include <cstring>
#include <limits>
#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<std::streampos>(ifs.tellg(), std::numeric_limits<int32_t>::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<uint32_t>(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;
}

49
src/stream/SliceReader.hpp

@ -1,49 +0,0 @@
#ifndef INNOEXTRACT_STREAM_SLICEREADER_HPP
#define INNOEXTRACT_STREAM_SLICEREADER_HPP
#include <fstream>
#include <boost/iostreams/concepts.hpp>
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

183
src/stream/block.cpp

@ -0,0 +1,183 @@
#include "stream/block.hpp"
#include <stdint.h>
#include <cstring>
#include <string>
#include <istream>
#include <algorithm>
#include <cassert>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/restrict.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <boost/iostreams/char_traits.hpp>
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/read.hpp>
#include <boost/make_shared.hpp>
#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<typename Source>
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<typename Source>
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<uint32_t>(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<little_endian, uint32_t>(base);
uint8_t compressed = actual_checksum.load<little_endian, uint8_t>(base);
compression = compressed ? (version >= INNO_VERSION(4, 1, 6) ? LZMA1 : Zlib) : Stored;
} else {
uint32_t compressed_size = actual_checksum.load<little_endian, uint32_t>(base);
uint32_t uncompressed_size = actual_checksum.load<little_endian, uint32_t>(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<uint64_t>(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<io::filtering_istream> fis = boost::make_shared<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, stored_size));
return fis;
}
} // namespace stream

35
src/stream/block.hpp

@ -0,0 +1,35 @@
#ifndef INNOEXTRACT_STREAM_BLOCK_HPP
#define INNOEXTRACT_STREAM_BLOCK_HPP
#include <iosfwd>
#include <boost/shared_ptr.hpp>
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<type> pointer;
static pointer get(std::istream & base, const InnoVersion & version);
};
} // namespace stream
#endif // INNOEXTRACT_STREAM_BLOCK_HPP

10
src/stream/ChecksumFilter.hpp → 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 <boost/iostreams/concepts.hpp>
#include <boost/iostreams/read.hpp>
#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

87
src/stream/chunk.cpp

@ -0,0 +1,87 @@
#include "chunk.hpp"
#include <boost/iostreams/filter/bzip2.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <boost/iostreams/restrict.hpp>
#include <boost/make_shared.hpp>
#include <boost/ref.hpp>
#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<type>();
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",
)

67
src/stream/chunk.hpp

@ -0,0 +1,67 @@
#ifndef INNOEXTRACT_STREAM_CHUNK_HPP
#define INNOEXTRACT_STREAM_CHUNK_HPP
#include <stddef.h>
#include <stdint.h>
#include <ios>
#include <boost/shared_ptr.hpp>
#include <boost/iostreams/chain.hpp>
#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<boost::iostreams::input> type;
typedef boost::shared_ptr<type> pointer;
static pointer get(slice_reader & base, const chunk & chunk);
};
} // namespace stream
NAMED_ENUM(stream::chunk::compression_method)
#endif // INNOEXTRACT_STREAM_CHUNK_HPP

66
src/stream/InstructionFilter.hpp → src/stream/file.cpp

@ -1,14 +1,21 @@
#ifndef INNOEXTRACT_STREAM_INSTRUCTIONFILTER_HPP
#define INNOEXTRACT_STREAM_INSTRUCTIONFILTER_HPP
#include "stream/file.hpp"
#include <stdint.h>
#include <cassert>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/restrict.hpp>
#include <boost/make_shared.hpp>
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/read.hpp>
#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<typename Source>
std::streamsize read(Source & src, char * dest, std::streamsize n);
inline void cclose() {
template<typename Source>
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<typename Source>
std::streamsize read(Source & src, char * dest, std::streamsize n);
inline void cclose() {
template<typename Source>
void close(const Source &) {
offset = 0, flush_bytes = 0;
}
@ -90,7 +100,7 @@ private:
// Implementation:
template<typename Source>
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<typename Source>
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<io::filtering_istream> result = boost::make_shared<io::filtering_istream>();
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

32
src/stream/file.hpp

@ -0,0 +1,32 @@
#ifndef INNOEXTRACT_STREAM_FILE_HPP
#define INNOEXTRACT_STREAM_FILE_HPP
#include <istream>
#include <boost/shared_ptr.hpp>
#include <boost/iostreams/chain.hpp>
class Hasher;
struct FileLocationEntry;
struct InnoVersion;
namespace stream {
class file_reader {
typedef boost::iostreams::chain<boost::iostreams::input> base_type;
public:
typedef std::istream type;
typedef boost::shared_ptr<type> pointer;
static pointer get(base_type & base, const FileLocationEntry & location,
const InnoVersion & version, Hasher & hasher);
};
}
#endif // INNOEXTRACT_STREAM_FILE_HPP

6
src/stream/LzmaFilter.cpp → src/stream/lzma.cpp

@ -1,10 +1,12 @@
#include "stream/LzmaFilter.hpp"
#include "stream/lzma.hpp"
#include <stdint.h>
#include <lzma.h>
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

13
src/stream/LzmaFilter.hpp → 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 <stddef.h>
#include <iosfwd>
#include <boost/iostreams/filter/symmetric.hpp>
#include <boost/noncopyable.hpp>
#include <iosfwd>
namespace stream {
struct lzma_error : public std::ios_base::failure {
@ -95,4 +98,6 @@ typedef lzma_decompressor<inno_lzma1_decompressor_impl> inno_lzma1_decompressor;
*/
typedef lzma_decompressor<inno_lzma2_decompressor_impl> inno_lzma2_decompressor;
#endif // INNOEXTRACT_STREAM_LZMAFILTER_HPP
} // namespace stream
#endif // INNOEXTRACT_STREAM_LZMA_HPP

196
src/stream/slice.cpp

@ -0,0 +1,196 @@
#include "stream/slice.hpp"
#include <stdint.h>
#include <sstream>
#include <cstring>
#include <limits>
#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<std::streampos>(ifs.tellg(), std::numeric_limits<int32_t>::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<uint32_t>(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

53
src/stream/slice.hpp

@ -0,0 +1,53 @@
#ifndef INNOEXTRACT_STREAM_SLICEREADER_HPP
#define INNOEXTRACT_STREAM_SLICEREADER_HPP
#include <fstream>
#include <boost/iostreams/concepts.hpp>
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

2
src/util/console.cpp

@ -74,7 +74,7 @@ void init() {
}
}
} // namespace color
namespace progress {

Loading…
Cancel
Save