27 changed files with 845 additions and 754 deletions
@ -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
|
||||
@ -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; |
||||
} |
||||
@ -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
|
||||
@ -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); |
||||
} |
||||
@ -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
|
||||
@ -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; |
||||
} |
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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", |
||||
) |
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
Loading…
Reference in new issue