diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e1f426..bbf9833 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -232,6 +232,8 @@ set(INNOEXTRACT_SOURCES src/cli/debug.hpp src/cli/debug.cpp if DEBUG + src/cli/gog.hpp + src/cli/gog.cpp src/cli/main.cpp src/crypto/adler32.hpp diff --git a/doc/innoextract.1 b/doc/innoextract.1 index 2683b66..df1ac59 100644 --- a/doc/innoextract.1 +++ b/doc/innoextract.1 @@ -34,6 +34,7 @@ Here is a short summary of the options available in innoextract. Please refer to \-t \-\-test Only verify checksums, don't write anything \-e \-\-extract Extract files (default action) \-l \-\-list Only list files, don't write anything + \-\-gog\-game\-id Determine the GOG.com game ID for this installer .fi .TP .B Modifiers: @@ -64,6 +65,18 @@ Don't convert Windows paths to UNIX paths and don't substitute variables in path .TP \fB\-e\fP, \fB\-\-extract\fP Extract all files to the current directory. This action is enabled by default, unless either \fB\-\-list\fP or \fB\-\-extract\fP is specified. You may only specify one of \fB\-\-extract\fP and \fB\-\-test\fP. +.TP +\fB\-\-gog\-game\-id\fP +Determine the ID used by GOG.com for the game contained in this installer. This will only work with Galaxy-ready GOG.com installers. + +This option can be combined with \fB\-\-silent\fP to print only the game ID without additional syntax that would make consumption by other scripts harder. + +The \fB\-\-gog\-game\-id\fP action can be combined with \fB\-\-list\fP, \fB\-\-test\fP and/or \fB\-\-extract\fP. If \fB\-\-gog\-game\-id\fP, \fB\-\-list\fP and \fB\-\-silent\fP are combined, the game ID (or an empty line) will be printed on it's own line before the file list. + +For some newer multi-part GOG.com installers the \fI.bin\fP files are not part of the Inno Setup installer but instead are encrypted RAR archives. The password can be calculated as the MD5 checksum of the game ID: + + \fBinnoextract \-\-gog\-game\-id --silent\fP \fIsetup_....exe\fP | \fBmd5sum\fP | \fBcut \-d\fP ' ' \fB\-f\fP 1 + .TP \fB\-h\fP, \fB\-\-help\fP Show a list of the supported options. @@ -79,7 +92,7 @@ List files contained in the installer but don't extract anything. This option can be combined with \fB\-\-silent\fP to print only the names of the contained files (one per line) without additional syntax that would make consumption by other scripts harder. -The \fB\-\-list\fP option can be combined with \fB\-\-test\fP or \fB\-\-extract\fP to display the names of the files as they are extracted even with \fB\-\-silent\fP. +The \fB\-\-list\fP action can be combined with \fB\-\-test\fP, \fB\-\-extract\fP and/or \fB\-\-gog\-game\-id\fP to display the names of the files as they are extracted even with \fB\-\-silent\fP. .TP \fB\-L\fP, \fB\-\-lowercase\fP Convert filenames stored in the installer to lower-case before extracting. diff --git a/src/cli/gog.cpp b/src/cli/gog.cpp new file mode 100644 index 0000000..f117719 --- /dev/null +++ b/src/cli/gog.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 Daniel Scharrer + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the author(s) be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "cli/gog.hpp" + +#include +#include + +#include +#include + +#include "setup/info.hpp" +#include "setup/registry.hpp" + +namespace gog { + +std::string get_game_id(const setup::info & info) { + + std::string id; + + const char * prefix = "SOFTWARE\\GOG.com\\Games\\"; + size_t prefix_length = std::strlen(prefix); + + BOOST_FOREACH(const setup::registry_entry & entry, info.registry_entries) { + + if(!boost::istarts_with(entry.key, prefix)) { + continue; + } + + if(entry.key.find('\\', prefix_length) != std::string::npos) { + continue; + } + + if(boost::iequals(entry.name, "gameID")) { + return entry.value; + } + + if(id.empty()) { + id = entry.key.substr(prefix_length); + } + + } + + return id; +} + +} // namespace gog diff --git a/src/cli/gog.hpp b/src/cli/gog.hpp new file mode 100644 index 0000000..c3ec31b --- /dev/null +++ b/src/cli/gog.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 Daniel Scharrer + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the author(s) be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/*! + * \file + * + * GOG.com-specific extensions. + */ +#ifndef INNOEXTRACT_CLI_GOG_HPP +#define INNOEXTRACT_CLI_GOG_HPP + +#include + +namespace setup { struct info; } + +namespace gog { + +//! \return the GOG.com game ID for this installer or an empty string +std::string get_game_id(const setup::info & info); + +} // namespace gog + +#endif // INNOEXTRACT_CLI_GOG_HPP diff --git a/src/cli/main.cpp b/src/cli/main.cpp index 405ea12..c293876 100644 --- a/src/cli/main.cpp +++ b/src/cli/main.cpp @@ -41,6 +41,7 @@ #include "release.hpp" #include "cli/debug.hpp" +#include "cli/gog.hpp" #include "loader/offsets.hpp" @@ -108,8 +109,9 @@ struct options { bool silent; bool list; // The --list action has been explicitely specified - bool test; // The --test action has been explicit specified + bool test; // The --test action has been explicitely specified bool extract; // The --extract action has been specified or automatically enabled + bool gog_game_id; // The --gog-game-id action has been explicitely specified bool preserve_file_times; bool local_timestamps; @@ -217,6 +219,9 @@ static void process_file(const fs::path & file, const options & o) { #endif setup::info::entry_types entries = setup::info::DataEntries | setup::info::Files; + if(o.gog_game_id) { + entries |= setup::info::RegistryEntries; + } #ifdef DEBUG if(logger::debug) { entries = setup::info::entry_types::all(); @@ -230,8 +235,15 @@ static void process_file(const fs::path & file, const options & o) { if(!o.quiet) { const std::string & name = info.header.app_versioned_name.empty() ? info.header.app_name : info.header.app_versioned_name; - std::cout << (o.extract ? "Extracting" : o.test ? "Testing" : "Listing") - << " \"" << color::green << name << color::reset + const char * verb = "Inspecting"; + if(o.extract) { + verb = "Extracting"; + } else if(o.test) { + verb = "Testing"; + } else if(o.list) { + verb = "Listing"; + } + std::cout << verb << " \"" << color::green << name << color::reset << "\" - setup data version " << color::white << info.version << color::reset << std::endl; } @@ -244,6 +256,26 @@ static void process_file(const fs::path & file, const options & o) { } #endif + if(o.gog_game_id) { + std::string id = gog::get_game_id(info); + if(id.empty()) { + if(!o.quiet) { + std::cout << "No GOG.com game ID found!\n"; + } + } else if(!o.silent) { + std::cout << "GOG.com game ID is " << color::cyan << id << color::reset << '\n'; + } else { + std::cout << id; + } + if(o.silent && o.list) { + std::cout << '\n'; + } + } + + if(!o.list && !o.test && !o.extract) { + return; + } + boost::uint64_t total_size = 0; std::vector< std::vector > files_for_location; @@ -471,6 +503,7 @@ int main(int argc, char * argv[]) { ("test,t", "Only verify checksums, don't write anything") ("extract,e", "Extract files (default action)") ("list,l", "Only list files, don't write anything") + ("gog-game-id", "Determine the GOG.com game ID for this installer") ; po::options_description filter("Modifiers"); @@ -564,7 +597,8 @@ int main(int argc, char * argv[]) { o.list = (options.count("list") != 0); o.extract = (options.count("extract") != 0); o.test = (options.count("test") != 0); - bool explicit_action = o.list || o.test || o.extract; + o.gog_game_id = (options.count("gog-game-id") != 0); + bool explicit_action = o.list || o.test || o.extract || o.gog_game_id; if(!explicit_action) { o.extract = true; } @@ -575,7 +609,7 @@ int main(int argc, char * argv[]) { if(!o.extract && !o.test) { progress::set_enabled(false); } - if(!o.silent) { + if(!o.silent && !o.gog_game_id) { o.list = true; }