/* * Copyright (C) 2011-2015 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 "setup/info.hpp" #include #include #include #include "setup/component.hpp" #include "setup/data.hpp" #include "setup/delete.hpp" #include "setup/directory.hpp" #include "setup/file.hpp" #include "setup/icon.hpp" #include "setup/ini.hpp" #include "setup/item.hpp" #include "setup/language.hpp" #include "setup/message.hpp" #include "setup/permission.hpp" #include "setup/registry.hpp" #include "setup/run.hpp" #include "setup/task.hpp" #include "setup/type.hpp" #include "stream/block.hpp" #include "util/load.hpp" #include "util/log.hpp" namespace setup { namespace { struct no_arg { }; template static void load_entry(std::istream & is, const setup::version & version, Entry & entity, Arg arg) { entity.load(is, version, arg); } template static void load_entry(std::istream & is, const setup::version & version, Entry & entity, no_arg arg) { (void)arg; entity.load(is, version); } template static void load_entries(std::istream & is, const setup::version & version, info::entry_types entry_types, size_t count, std::vector & entries, info::entry_types::enum_type entry_type, Arg arg = Arg()) { entries.clear(); if(entry_types & entry_type) { entries.resize(count); for(size_t i = 0; i < count; i++) { Entry & entry = entries[i]; load_entry(is, version, entry, arg); } } else { for(size_t i = 0; i < count; i++) { Entry entry; load_entry(is, version, entry, arg); } } } template static void load_entries(std::istream & is, const setup::version & version, info::entry_types entry_types, size_t count, std::vector & entries, info::entry_types::enum_type entry_type) { load_entries(is, version, entry_types, count, entries, entry_type); } static void load_wizard_and_decompressor(std::istream & is, const setup::version & version, const setup::header & header, setup::info & info, info::entry_types entries) { (void)entries; info.wizard_image.clear(); info.wizard_image_small.clear(); if(entries & (info::WizardImages | info::NoSkip)) { is >> util::binary_string(info.wizard_image); if(version >= INNO_VERSION(2, 0, 0)) { is >> util::binary_string(info.wizard_image_small); } } else { util::binary_string::skip(is); if(version >= INNO_VERSION(2, 0, 0)) { util::binary_string::skip(is); } } info.decompressor_dll.clear(); if(header.compression == stream::BZip2 || (header.compression == stream::LZMA1 && version == INNO_VERSION(4, 1, 5)) || (header.compression == stream::Zlib && version >= INNO_VERSION(4, 2, 6))) { if(entries & (info::DecompressorDll | info::NoSkip)) { is >> util::binary_string(info.decompressor_dll); } else { // decompressor dll - we don't need this util::binary_string::skip(is); } } info.decrypt_dll.clear(); if(header.options & header::EncryptionUsed) { if(entries & (info::DecryptDll | info::NoSkip)) { is >> util::binary_string(info.decrypt_dll); } else { // decrypt dll - we don't need this util::binary_string::skip(is); } } } } // anonymous namespace static void check_is_end(stream::block_reader::pointer & is, const char * what) { is->exceptions(std::ios_base::goodbit); char dummy; if(!is->get(dummy).eof()) { throw std::ios_base::failure(what); } } void info::load(std::istream & ifs, entry_types e, const setup::version & v) { if(e & (Messages | NoSkip)) { e |= Languages; } stream::block_reader::pointer is = stream::block_reader::get(ifs, v); header.load(*is, v); load_entries(*is, v, e, header.language_count, languages, Languages); if(v < INNO_VERSION(4, 0, 0)) { load_wizard_and_decompressor(*is, v, header, *this, e); } load_entries(*is, v, e, header.message_count, messages, Messages, languages); load_entries(*is, v, e, header.permission_count, permissions, Permissions); load_entries(*is, v, e, header.type_count, types, Types); load_entries(*is, v, e, header.component_count, components, Components); load_entries(*is, v, e, header.task_count, tasks, Tasks); load_entries(*is, v, e, header.directory_count, directories, Directories); load_entries(*is, v, e, header.file_count, files, Files); load_entries(*is, v, e, header.icon_count, icons, Icons); load_entries(*is, v, e, header.ini_entry_count, ini_entries, IniEntries); load_entries(*is, v, e, header.registry_entry_count, registry_entries, RegistryEntries); load_entries(*is, v, e, header.delete_entry_count, delete_entries, DeleteEntries); load_entries(*is, v, e, header.uninstall_delete_entry_count, uninstall_delete_entries, UninstallDeleteEntries); load_entries(*is, v, e, header.run_entry_count, run_entries, RunEntries); load_entries(*is, v, e, header.uninstall_run_entry_count, uninstall_run_entries, UninstallRunEntries); if(v >= INNO_VERSION(4, 0, 0)) { load_wizard_and_decompressor(*is, v, header, *this, e); } // restart the compression stream check_is_end(is, "unknown data at end of primary header stream"); is = stream::block_reader::get(ifs, v); load_entries(*is, v, e, header.data_entry_count, data_entries, DataEntries); check_is_end(is, "unknown data at end of secondary header stream"); } void info::load(std::istream & is, entry_types entries) { version.load(is); if(!version.known) { log_warning << "Unexpected setup data version: " << color::white << version << color::reset; } version_constant listed_version = version.value; // Some setup versions didn't increment the data version number when they should have. // To work around this, we try to parse the headers for both data versions. bool ambiguous = version.is_ambiguous(); if(ambiguous) { // Force parsing all headers so that we don't miss any errors. entries |= NoSkip; } if(!version.known || ambiguous) { std::ios_base::streampos start = is.tellg(); try { load(is, entries, version); return; } catch(...) { version.value = version.next(); if(!version) { version.value = listed_version; throw; } is.clear(); is.seekg(start); } } try { load(is, entries, version); } catch(...) { version.value = listed_version; throw; } } info::info() { } info::~info() { } } // namespace setup