#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "loader/SetupLoader.hpp" #include "setup/MessageEntry.hpp" #include "setup/DeleteEntry.hpp" #include "setup/DirectoryEntry.hpp" #include "setup/FileEntry.hpp" #include "setup/FileLocationEntry.hpp" #include "setup/IconEntry.hpp" #include "setup/IniEntry.hpp" #include "setup/LanguageEntry.hpp" #include "setup/PermissionEntry.hpp" #include "setup/RegistryEntry.hpp" #include "setup/RunEntry.hpp" #include "setup/SetupComponentEntry.hpp" #include "setup/SetupHeader.hpp" #include "setup/SetupTaskEntry.hpp" #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 "util/LoadingUtils.hpp" #include "util/Output.hpp" #include "util/Utils.hpp" class progress { public: static void show(float value, const std::string & label) { struct winsize w; ioctl(0, TIOCGWINSZ, &w); clear(); std::ios_base::fmtflags flags = std::cout.flags(); size_t progress_length = w.ws_col - label.length() - 6 - 2 - 2 - 1; if(progress_length > 10) { size_t progress = size_t(ceil(float(progress_length) * value)); std::cout << '['; for(size_t i = 0; i < progress; i++) { std::cout << '='; } std::cout << '>'; for(size_t i = progress; i < progress_length; i++) { std::cout << ' '; } std::cout << ']'; } std::cout << std::right << std::fixed << std::setprecision(1) << std::setfill(' ') << std::setw(5) << (value * 100) << "% " << label; std::cout.flush(); std::cout.flags(flags); } static void clear() { std::cout << "\33[2K\r"; } }; using std::cout; using std::string; using std::endl; using std::setw; using std::setfill; using std::hex; using std::dec; template void discard(T & is, uint64_t bytes) { std::cout << "discarding " << PrintBytes(bytes) << std::endl; char buf[1024]; while(bytes) { std::streamsize n = std::streamsize(std::min(bytes, ARRAY_SIZE(buf))); is.read(buf, n); bytes -= uint64_t(n); } } namespace io = boost::iostreams; namespace fs = boost::filesystem; struct FileLocationComparer { const std::vector & locations; explicit FileLocationComparer(const std::vector & loc) : locations(loc) { } FileLocationComparer(const FileLocationComparer & o) : locations(o.locations) { } bool operator()(size_t a, size_t b) { return (locations[a].fileOffset < locations[b].fileOffset); } }; static void printSetupItem(std::ostream & os, const SetupItem & item, const SetupHeader & header) { os << IfNotEmpty(" Componenets", item.components); os << IfNotEmpty(" Tasks", item.tasks); os << IfNotEmpty(" Languages", item.languages); os << IfNotEmpty(" Check", item.check); os << IfNotEmpty(" After install", item.afterInstall); os << IfNotEmpty(" Before install", item.beforeInstall); os << IfNot(" Min version", item.minVersion, header.minVersion); os << IfNot(" Only below version", item.onlyBelowVersion, header.onlyBelowVersion); } static void print(std::ostream & os, const RunEntry & entry, const SetupHeader & header) { os << " - " << Quoted(entry.name) << ':' << endl; os << IfNotEmpty(" Parameters", entry.parameters); os << IfNotEmpty(" Working directory", entry.workingDir); os << IfNotEmpty(" Run once id", entry.runOnceId); os << IfNotEmpty(" Status message", entry.statusMessage); os << IfNotEmpty(" Verb", entry.verb); os << IfNotEmpty(" Description", entry.verb); printSetupItem(cout, entry, header); os << IfNot(" Show command", entry.showCmd, 1); os << IfNot(" Wait", entry.wait, RunEntry::WaitUntilTerminated); os << IfNotZero(" Options", entry.options); } 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 << PrintHex(checksum.adler32); break; } case Checksum::Crc32: { cout << PrintHex(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" }, { "\xFF\xD8", "jpg" }, { "\x89PNG\r\n\x1A\n", "png" }, { "%PDF", "pdf" }, { "MZ", "dll" }, { "BM", "bmp" }, }; static const char * guessExtension(const string & data) { for(size_t i = 0; i < ARRAY_SIZE(magicNumbers); i++) { size_t n = strlen(magicNumbers[i][0]); if(!data.compare(0, n, magicNumbers[i][0], n)) { return magicNumbers[i][1]; } } return "bin"; } static void dump(std::istream & is, const string & file) { // TODO stream std::string data; is >> BinaryString(data); cout << "Resource: " << color::cyan << file << color::reset << ": " << color::white << data.length() << color::reset << " bytes" << endl; if(data.empty()) { return; } std::string filename = file + '.' + guessExtension(data); std::ofstream ofs(filename.c_str(), std::ios_base::trunc | std::ios_base::binary | std::ios_base::out); ofs << data; }; static void readWizardImageAndDecompressor(std::istream & is, const InnoVersion & version, const SetupHeader & header) { cout << endl; dump(is, "wizard"); if(version >= INNO_VERSION(2, 0, 0)) { 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))) { dump(is, "decompressor"); } if(is.fail()) { LogError << "error reading misc setup data"; } } int main(int argc, char * argv[]) { if(argc <= 1) { std::cout << "usage: innoextract " << endl; return 1; } std::ifstream ifs(argv[1], std::ios_base::in | std::ios_base::binary | std::ios_base::ate); if(!ifs.is_open()) { LogError << "error opening file"; return 1; } SetupLoader offsets; offsets.load(ifs); cout << std::boolalpha; cout << "loaded offsets:" << endl; if(offsets.exeOffset) { cout << "- exe: @ " << color::cyan << PrintHex(offsets.exeOffset) << color::reset; if(offsets.exeCompressedSize) { cout << " compressed: " << color::cyan << PrintHex(offsets.exeCompressedSize) << color::reset; } cout << " uncompressed: " << color::cyan << PrintBytes(offsets.exeUncompressedSize) << color::reset << endl; cout << "- exe checksum: " << color::cyan << offsets.exeChecksum << color::reset << endl; } cout << IfNotZero("- message offset", PrintHex(offsets.messageOffset)); cout << "- header offset: " << color::cyan << PrintHex(offsets.headerOffset) << color::reset << endl; cout << IfNotZero("- data offset", PrintHex(offsets.dataOffset)); ifs.seekg(offsets.headerOffset); InnoVersion version; version.load(ifs); if(ifs.fail()) { LogError << "error reading setup data version!"; return 1; } if(!version.known) { LogError << "unknown version!"; return 1; // TODO } cout << "version: " << color::white << version << color::reset << endl; boost::shared_ptr is(BlockReader::get(ifs, version)); if(!is) { LogError << "error reading block"; return 1; } is->exceptions(std::ios_base::badbit | std::ios_base::failbit); SetupHeader header; header.load(*is, version); if(is->fail()) { LogError << "error reading setup data header!"; return 1; } cout << endl; cout << IfNotEmpty("App name", header.appName); cout << IfNotEmpty("App ver name", header.appVerName); cout << IfNotEmpty("App id", header.appId); cout << IfNotEmpty("Copyright", header.appCopyright); cout << IfNotEmpty("Publisher", header.appPublisher); cout << IfNotEmpty("Publisher URL", header.appPublisherURL); cout << IfNotEmpty("Support phone", header.appSupportPhone); cout << IfNotEmpty("Support URL", header.appSupportURL); cout << IfNotEmpty("Updates URL", header.appUpdatesURL); cout << IfNotEmpty("Version", header.appVersion); cout << IfNotEmpty("Default dir name", header.defaultDirName); cout << IfNotEmpty("Default group name", header.defaultGroupName); cout << IfNotEmpty("Uninstall icon name", header.uninstallIconName); cout << IfNotEmpty("Base filename", header.baseFilename); cout << IfNotEmpty("Uninstall files dir", header.uninstallFilesDir); cout << IfNotEmpty("Uninstall display name", header.uninstallDisplayName); cout << IfNotEmpty("Uninstall display icon", header.uninstallDisplayIcon); cout << IfNotEmpty("App mutex", header.appMutex); cout << IfNotEmpty("Default user name", header.defaultUserInfoName); cout << IfNotEmpty("Default user org", header.defaultUserInfoOrg); cout << IfNotEmpty("Default user serial", header.defaultUserInfoSerial); cout << IfNotEmpty("Readme", header.appReadmeFile); cout << IfNotEmpty("Contact", header.appContact); cout << IfNotEmpty("Comments", header.appComments); cout << IfNotEmpty("Modify path", header.appModifyPath); cout << IfNotEmpty("Uninstall reg key", header.createUninstallRegKey); cout << IfNotEmpty("Uninstallable", header.uninstallable); cout << IfNotEmpty("License", header.licenseText); cout << IfNotEmpty("Info before text", header.infoBeforeText); cout << IfNotEmpty("Info after text", header.infoAfterText); cout << IfNotEmpty("Uninstaller signature", header.signedUninstallerSignature); cout << IfNotEmpty("Compiled code", header.compiledCodeText); cout << IfNotZero("Lead bytes", header.leadBytes); cout << IfNotZero("Language entries", header.numLanguageEntries); cout << IfNotZero("Custom message entries", header.numCustomMessageEntries); cout << IfNotZero("Permission entries", header.numPermissionEntries); cout << IfNotZero("Type entries", header.numTypeEntries); cout << IfNotZero("Component entries", header.numComponentEntries); cout << IfNotZero("Task entries", header.numTaskEntries); cout << IfNotZero("Dir entries", header.numDirectoryEntries); cout << IfNotZero("File entries", header.numFileEntries); cout << IfNotZero("File location entries", header.numFileLocationEntries); cout << IfNotZero("Icon entries", header.numIconEntries); cout << IfNotZero("Ini entries", header.numIniEntries); cout << IfNotZero("Registry entries", header.numRegistryEntries); cout << IfNotZero("Delete entries", header.numDeleteEntries); cout << IfNotZero("Uninstall delete entries", header.numUninstallDeleteEntries); cout << IfNotZero("Run entries", header.numRunEntries); cout << IfNotZero("Uninstall run entries", header.numUninstallRunEntries); cout << IfNot("Min version", header.minVersion, WindowsVersion::none); cout << IfNot("Only below version", header.onlyBelowVersion, WindowsVersion::none); cout << hex; cout << IfNotZero("Back color", header.backColor); cout << IfNotZero("Back color2", header.backColor2); cout << IfNotZero("Wizard image back color", header.wizardImageBackColor); cout << IfNotZero("Wizard small image back color", header.wizardSmallImageBackColor); cout << dec; if(header.options & (SetupHeader::Password | SetupHeader::EncryptionUsed)) { cout << "Password: " << color::cyan << header.password << color::reset << endl; // TODO print salt } cout << IfNotZero("Extra disk space required", header.extraDiskSpaceRequired); cout << IfNotZero("Slices per disk", header.slicesPerDisk); cout << IfNot("Install mode", header.installMode, SetupHeader::NormalInstallMode); cout << "Uninstall log mode: " << color::cyan << header.uninstallLogMode << color::reset << endl; cout << "Uninstall style: " << color::cyan << header.uninstallStyle << color::reset << endl; cout << "Dir exists warning: " << color::cyan << header.dirExistsWarning << color::reset << endl; cout << IfNot("Privileges required", header.privilegesRequired, SetupHeader::NoPrivileges); cout << "Show language dialog: " << color::cyan << header.showLanguageDialog << color::reset << endl; cout << IfNot("Danguage detection", header.languageDetectionMethod, SetupHeader::NoLanguageDetection); cout << "Compression: " << color::cyan << header.compressMethod << color::reset << endl; cout << "Architectures allowed: " << color::cyan << header.architecturesAllowed << color::reset << endl; cout << "Architectures installed in 64-bit mode: " << color::cyan << header.architecturesInstallIn64BitMode << color::reset << endl; if(header.options & SetupHeader::SignedUninstaller) { cout << IfNotZero("Size before signing uninstaller", header.signedUninstallerOrigSize); cout << IfNotZero("Uninstaller header checksum", header.signedUninstallerHdrChecksum); } cout << "Disable dir page: " << color::cyan << header.disableDirPage << color::reset << endl; cout << "Disable program group page: " << color::cyan << header.disableProgramGroupPage << color::reset << endl; cout << IfNotZero("Uninstall display size", header.uninstallDisplaySize); cout << "Options: " << color::green << header.options << color::reset << endl; cout << color::reset; if(header.numLanguageEntries) { cout << endl << "Language entries:" << endl; } std::vector languages; languages.resize(header.numLanguageEntries); for(size_t i = 0; i < header.numLanguageEntries; i++) { LanguageEntry & entry = languages[i]; entry.load(*is, version); if(is->fail()) { LogError << "error reading language entry #" << i; } cout << " - " << Quoted(entry.name) << ':' << endl; cout << IfNotEmpty(" Language name", entry.languageName); cout << IfNotEmpty(" Dialog font", entry.dialogFontName); cout << IfNotEmpty(" Title font", entry.titleFontName); cout << IfNotEmpty(" Welcome font", entry.welcomeFontName); cout << IfNotEmpty(" Copyright font", entry.copyrightFontName); cout << IfNotEmpty(" Data", entry.data); cout << IfNotEmpty(" License", entry.licenseText); cout << IfNotEmpty(" Info before text", entry.infoBeforeText); cout << IfNotEmpty(" Info after text", entry.infoAfterText); cout << " Language id: " << color::cyan << hex << entry.languageId << dec << color::reset << endl; cout << IfNotZero(" Codepage", entry.codepage); cout << IfNotZero(" Dialog font size", entry.dialogFontSize); cout << IfNotZero(" Dialog font standard height", entry.dialogFontStandardHeight); cout << IfNotZero(" Title font size", entry.titleFontSize); cout << IfNotZero(" Welcome font size", entry.welcomeFontSize); cout << IfNotZero(" Copyright font size", entry.copyrightFontSize); cout << IfNot(" Right to left", entry.rightToLeft, false); }; if(version < INNO_VERSION(4, 0, 0)) { readWizardImageAndDecompressor(*is, version, header); } if(header.numCustomMessageEntries) { cout << endl << "Message entries:" << endl; } for(size_t i = 0; i < header.numCustomMessageEntries; i++) { MessageEntry entry; entry.load(*is, version); if(is->fail()) { LogError << "error reading custom message entry #" << i; } if(entry.language >= 0 ? size_t(entry.language) >= languages.size() : entry.language != -1) { LogWarning << "unexpected language index: " << entry.language; } uint32_t codepage; if(entry.language < 0) { codepage = version.codepage(); } else { codepage = languages[size_t(entry.language)].codepage; } string decoded; toUtf8(entry.value, decoded, codepage); cout << " - " << Quoted(entry.name); if(entry.language < 0) { cout << " (default) = "; } else { cout << " (" << color::cyan << languages[size_t(entry.language)].name << color::reset << ") = "; } cout << Quoted(decoded) << endl; } if(header.numPermissionEntries) { cout << endl << "Permission entries:" << endl; } for(size_t i = 0; i < header.numPermissionEntries; i++) { PermissionEntry entry; entry.load(*is, version); if(is->fail()) { LogError << "error reading permission entry #" << i; } cout << " - " << entry.permissions.length() << " bytes"; } if(header.numTypeEntries) { cout << endl << "Type entries:" << endl; } for(size_t i = 0; i < header.numTypeEntries; i++) { SetupTypeEntry entry; entry.load(*is, version); if(is->fail()) { LogError << "error reading type entry #" << i; } cout << " - " << Quoted(entry.name) << ':' << endl; cout << IfNotEmpty(" Description", entry.description); cout << IfNotEmpty(" Languages", entry.languages); cout << IfNotEmpty(" Check", entry.check); cout << IfNot(" Min version", entry.minVersion, header.minVersion); cout << IfNot(" Only below version", entry.onlyBelowVersion, header.onlyBelowVersion); cout << IfNotZero(" Options", entry.options); cout << IfNot(" Type", entry.type, SetupTypeEntry::User); cout << IfNotZero(" Size", entry.size); } if(header.numComponentEntries) { cout << endl << "Component entries:" << endl; } for(size_t i = 0; i < header.numComponentEntries; i++) { SetupComponentEntry entry; entry.load(*is, version); if(is->fail()) { LogError << "error reading component entry #" << i; } cout << " - " << Quoted(entry.name) << ':' << endl; cout << IfNotEmpty(" Types", entry.types); cout << IfNotEmpty(" Description", entry.description); cout << IfNotEmpty(" Languages", entry.languages); cout << IfNotEmpty(" Check", entry.check); cout << IfNotZero(" Extra disk space required", entry.extraDiskSpaceRequired); cout << IfNotZero(" Level", entry.level); cout << IfNot(" Used", entry.used, true); cout << IfNot(" Min version", entry.minVersion, header.minVersion); cout << IfNot(" Only below version", entry.onlyBelowVersion, header.onlyBelowVersion); cout << IfNotZero(" Options", entry.options); cout << IfNotZero(" Size", entry.size); } if(header.numTaskEntries) { cout << endl << "Task entries:" << endl; } for(size_t i = 0; i < header.numTaskEntries; i++) { SetupTaskEntry entry; entry.load(*is, version); if(is->fail()) { LogError << "error reading task entry #" << i; } cout << " - " << Quoted(entry.name) << ':' << endl; cout << IfNotEmpty(" Description", entry.description); cout << IfNotEmpty(" Group description", entry.groupDescription); cout << IfNotEmpty(" Components", entry.components); cout << IfNotEmpty(" Languages", entry.languages); cout << IfNotEmpty(" Check", entry.check); cout << IfNotZero(" Level", entry.level); cout << IfNot(" Used", entry.used, true); cout << IfNot(" Min version", entry.minVersion, header.minVersion); cout << IfNot(" Only below version", entry.onlyBelowVersion, header.onlyBelowVersion); cout << IfNotZero(" Options", entry.options); } if(header.numDirectoryEntries) { cout << endl << "Directory entries:" << endl; } std::vector directories; directories.resize(header.numDirectoryEntries); for(size_t i = 0; i < header.numDirectoryEntries; i++) { DirectoryEntry & entry = directories[i]; entry.load(*is, version); if(is->fail()) { LogError << "error reading directory entry #" << i; } cout << " - " << Quoted(entry.name) << ':' << endl; printSetupItem(cout, entry, header); if(!entry.permissions.empty()) { cout << " Permissions: " << entry.permissions.length() << " bytes"; } cout << IfNotZero(" Attributes", entry.attributes); cout << IfNot(" Permission entry", entry.permission, -1); cout << IfNotZero(" Options", entry.options); } if(header.numFileEntries) { cout << endl << "File entries:" << endl; } std::vector files; files.resize(header.numFileEntries); for(size_t i = 0; i < header.numFileEntries; i++) { FileEntry & entry = files[i]; entry.load(*is, version); if(is->fail()) { LogError << "error reading file entry #" << i; } if(entry.destination.empty()) { cout << " - File #" << i; } else { cout << " - " << Quoted(entry.destination); } if(entry.location != uint32_t(-1)) { cout << " (location: " << color::cyan << entry.location << color::reset << ')'; } cout << endl; cout << IfNotEmpty(" Source", entry.source); cout << IfNotEmpty(" Install font name", entry.installFontName); cout << IfNotEmpty(" Strong assembly name", entry.strongAssemblyName); printSetupItem(cout, entry, header); cout << IfNotZero(" Attributes", entry.attributes); cout << IfNotZero(" Size", entry.externalSize); cout << IfNot(" Permission entry", entry.permission, -1); cout << IfNotZero(" Options", entry.options); cout << IfNot(" Type", entry.type, FileEntry::UserFile); } if(header.numIconEntries) { cout << endl << "Icon entries:" << endl; } for(size_t i = 0; i < header.numIconEntries; i++) { IconEntry entry; entry.load(*is, version); if(is->fail()) { LogError << "error reading icon entry #" << i; } cout << " - " << Quoted(entry.name) << " -> " << Quoted(entry.filename) << endl; cout << IfNotEmpty(" Parameters", entry.parameters); cout << IfNotEmpty(" Working directory", entry.workingDir); cout << IfNotEmpty(" Icon file", entry.iconFilename); cout << IfNotEmpty(" Comment", entry.comment); cout << IfNotEmpty(" App user model id", entry.appUserModelId); printSetupItem(cout, entry, header); cout << IfNotZero(" Icon index", entry.iconIndex); cout << IfNot(" Show command", entry.showCmd, 1); cout << IfNot(" Close on exit", entry.closeOnExit, IconEntry::NoSetting); cout << IfNotZero(" Hotkey", entry.hotkey); cout << IfNotZero(" Options", entry.options); } if(header.numIniEntries) { cout << endl << "Ini entries:" << endl; } for(size_t i = 0; i < header.numIniEntries; i++) { IniEntry entry; entry.load(*is, version); if(is->fail()) { LogError << "error reading ini entry #" << i; } cout << " - in " << Quoted(entry.inifile); cout << " set [" << Quoted(entry.section) << "] "; cout << Quoted(entry.key) << " = " << Quoted(entry.value) << std::endl; printSetupItem(cout, entry, header); cout << IfNotZero(" Options", entry.options); } if(header.numRegistryEntries) { cout << endl << "Registry entries:" << endl; } for(size_t i = 0; i < header.numRegistryEntries; i++) { RegistryEntry entry; entry.load(*is, version); if(is->fail()) { LogError << "error reading registry entry #" << i; } cout << " - "; if(entry.hive != RegistryEntry::Unset) { cout << entry.hive << '\\'; } cout << Quoted(entry.key); cout << endl << " "; if(entry.name.empty()) { cout << "(default)"; } else { cout << Quoted(entry.name); } if(!entry.value.empty()) { cout << " = " << Quoted(entry.value); } if(entry.type != RegistryEntry::None) { cout << " (" << color::cyan << entry.type << color::reset << ')'; } cout << endl; printSetupItem(cout, entry, header); if(!entry.permissions.empty()) { cout << " Permissions: " << entry.permissions.length() << " bytes"; } cout << IfNot(" Permission entry", entry.permission, -1); cout << IfNotZero(" Options", entry.options); } if(header.numDeleteEntries) { cout << endl << "Delete entries:" << endl; } for(size_t i = 0; i < header.numDeleteEntries; i++) { DeleteEntry entry; entry.load(*is, version); if(is->fail()) { LogError << "error reading install delete entry #" << i; } cout << " - " << Quoted(entry.name) << " (" << color::cyan << entry.type << color::reset << ')' << endl; printSetupItem(cout, entry, header); } if(header.numUninstallDeleteEntries) { cout << endl << "Uninstall delete entries:" << endl; } for(size_t i = 0; i < header.numUninstallDeleteEntries; i++) { DeleteEntry entry; entry.load(*is, version); if(is->fail()) { LogError << "error reading uninstall delete entry #" << i; } cout << " - " << Quoted(entry.name) << " (" << color::cyan << entry.type << color::reset << ')' << endl; printSetupItem(cout, entry, header); } if(header.numRunEntries) { cout << endl << "Run entries:" << endl; } for(size_t i = 0; i < header.numRunEntries; i++) { RunEntry entry; entry.load(*is, version); if(is->fail()) { LogError << "error reading install run entry #" << i; } print(cout, entry, header); } if(header.numUninstallRunEntries) { cout << endl << "Uninstall run entries:" << endl; } for(size_t i = 0; i < header.numUninstallRunEntries; i++) { RunEntry entry; entry.load(*is, version); if(is->fail()) { LogError << "error reading uninstall run entry #" << i; } print(cout, entry, header); } if(version >= INNO_VERSION(4, 0, 0)) { readWizardImageAndDecompressor(*is, version, header); } { is->exceptions(std::ios_base::goodbit); char dummy; if(!is->get(dummy).eof()) { LogWarning << "expected end of stream"; } } // TODO skip to end if not there yet is.reset(BlockReader::get(ifs, version)); if(!is) { LogError << "error reading block"; return 1; } is->exceptions(std::ios_base::badbit | std::ios_base::failbit); if(header.numFileLocationEntries) { cout << endl << "File location entries:" << endl; } std::vector locations; locations.resize(header.numFileLocationEntries); for(size_t i = 0; i < header.numFileLocationEntries; i++) { FileLocationEntry & entry = locations[i]; entry.load(*is, version); if(is->fail()) { LogError << "error reading file location entry #" << i; } cout << " - " << "File location #" << i << ':' << endl; cout << IfNotZero(" First slice", entry.firstSlice); cout << IfNot(" Last slice", entry.lastSlice, entry.firstSlice); cout << " Chunk: offset " << color::cyan << PrintHex(entry.chunkOffset) << color::reset << " size " << color::cyan << PrintHex(entry.chunkSize) << color::reset << std::endl; cout << IfNotZero(" File offset", PrintHex(entry.fileOffset)); cout << IfNotZero(" File size", PrintBytes(entry.fileSize)); cout << " Checksum: " << entry.checksum << endl; std::tm t; if(entry.options & FileLocationEntry::TimeStampInUTC) { gmtime_r(&entry.timestamp.tv_sec, &t); } else { localtime_r(&entry.timestamp.tv_sec, &t); } cout << " Timestamp: " << color::cyan << (t.tm_year + 1900) << '-' << std::setfill('0') << std::setw(2) << (t.tm_mon + 1) << '-' << std::setfill('0') << std::setw(2) << t.tm_mday << ' ' << std::setfill(' ') << std::setw(2) << t.tm_hour << ':' << std::setfill('0') << std::setw(2) << t.tm_min << ':' << std::setfill('0') << std::setw(2) << t.tm_sec << color::reset << " +" << entry.timestamp.tv_nsec << endl; cout << IfNotZero(" Options", entry.options); if(entry.options & FileLocationEntry::VersionInfoValid) { cout << IfNotZero(" File version LS", entry.fileVersionLS); cout << IfNotZero(" File version MS", entry.fileVersionMS); } } { is->exceptions(std::ios_base::goodbit); char dummy; if(!is->get(dummy).eof()) { LogWarning << "expected end of stream"; } } is.reset(); std::vector > files_for_location; files_for_location.resize(locations.size()); for(size_t i = 0; i < files.size(); i++) { if(files[i].location < files_for_location.size()) { files_for_location[files[i].location].push_back(i); } } 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) ].push_back(i); assert(header.compressMethod == SetupHeader::BZip2 || !(location.options & FileLocationEntry::BZipped)); } boost::shared_ptr slice_reader; if(offsets.dataOffset) { slice_reader.reset(new SliceReader(argv[1], offsets.dataOffset)); } else { fs::path path(argv[1]); slice_reader.reset(new SliceReader(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 << " + " << PrintHex(offsets.dataOffset) << " + " << PrintHex(chunk.first.chunkOffset) << ']' << std::endl; std::sort(chunk.second.begin(), chunk.second.end(), FileLocationComparer(locations)); if(!slice_reader->seek(chunk.first.firstSlice, chunk.first.chunkOffset)) { LogError << "error seeking" << std::endl; return 1; } const char chunkId[4] = { 'z', 'l', 'b', 0x1a }; char magic[4]; if(slice_reader->read(magic, 4) != 4 || memcmp(magic, chunkId, 4)) { LogError << "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: LogError << "unknown compression"; } } int64_t csize = int64_t(chunk.first.chunkSize); chunk_source.push(io::restrict(boost::ref(*slice_reader.get()), 0, csize)); uint64_t offset = 0; BOOST_FOREACH(size_t location_i, chunk.second) { const FileLocationEntry & location = locations[location_i]; if(location.fileOffset < offset) { LogError << "bad offset"; return 1; } if(location.fileOffset > offset) { discard(chunk_source, location.fileOffset - offset); } offset = location.fileOffset + location.fileSize; std::cout << "-> reading "; bool named = false; BOOST_FOREACH(size_t file_i, files_for_location[location_i]) { if(!files[file_i].destination.empty()) { std::cout << '"' << files[file_i].destination << '"'; named = true; break; } } if(!named) { std::cout << "unnamed file"; } std::cout << " @ " << PrintHex(location.fileOffset) << " (" << PrintBytes(location.fileSize) << ')' << 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); BOOST_FOREACH(size_t file_i, files_for_location[location_i]) { if(!files[file_i].destination.empty()) { std::ofstream ofs(files[file_i].destination.c_str()); char buffer[8192 * 10]; float status = 0.f; uint64_t total = 0; std::ostringstream oss; float last_rate = 0; int64_t last_milliseconds = 0; boost::posix_time::ptime start(boost::posix_time::microsec_clock::universal_time()); while(!file_source.eof()) { 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))) * (1 / 1000.f); if(status != new_status && new_status != 100.f) { boost::posix_time::ptime now(boost::posix_time::microsec_clock::universal_time()); int64_t milliseconds = (now - start).total_milliseconds(); if(milliseconds - last_milliseconds > 200) { last_milliseconds = milliseconds; if(total >= 10 * 1024 && milliseconds > 0) { float rate = 1000.f * float(total) / float(milliseconds); if(rate != last_rate) { last_rate = rate; oss.str(string()); // clear the buffer oss << std::right << std::fixed << std::setfill(' ') << std::setw(8) << PrintBytes(rate) << "/s"; } } status = new_status; progress::show(status, oss.str()); } } } } break; // TODO ... } } progress::clear(); Checksum actual; hasher.finalize(actual); if(actual != location.checksum) { LogWarning << "checksum mismatch:"; LogWarning << "actual: " << actual; LogWarning << "expected: " << location.checksum; } } } } catch(io::bzip2_error e) { LogError << e.what() << ": " << e.error(); } std::cout << color::green << "Done" << color::reset; if(total_errors || total_warnings) { std::cout << " with "; if(total_errors) { std::cout << color::red << total_errors << " errors" << color::reset; } if(total_errors && total_warnings) { std::cout << " and "; } if(total_warnings) { std::cout << color::yellow << total_warnings << " warnings" << color::reset; } } std::cout << '.' << std::endl; return 0; }