From 14f15ae47c6aefdde8e56b7b9939ea84c4062463 Mon Sep 17 00:00:00 2001 From: Federico Cecchetto Date: Mon, 22 Aug 2022 01:42:13 +0200 Subject: [PATCH] More mod loading stuff --- src/client/component/database.cpp | 232 +++++----------------------- src/client/component/fastfiles.cpp | 219 ++++++++++++++++++++++---- src/client/component/fastfiles.hpp | 2 + src/client/component/filesystem.cpp | 23 +++ src/client/component/filesystem.hpp | 3 +- src/client/component/mods.cpp | 44 +++++- src/client/component/mods.hpp | 2 + src/client/game/dvars.cpp | 36 +++++ src/client/game/symbols.hpp | 11 +- src/common/utils/flags.cpp | 111 ++++++++++--- src/common/utils/flags.hpp | 5 + src/common/utils/nt.cpp | 11 +- src/common/utils/nt.hpp | 2 +- 13 files changed, 445 insertions(+), 256 deletions(-) diff --git a/src/client/component/database.cpp b/src/client/component/database.cpp index 8c1fb55f..f80428e8 100644 --- a/src/client/component/database.cpp +++ b/src/client/component/database.cpp @@ -45,6 +45,13 @@ namespace database game::dvar_t* db_filesysImpl = nullptr; utils::hook::detour db_fs_initialize_hook; + std::unordered_map file_search_folders = + { + {".flac", "sound/"}, + {".bik", "video/"}, + {".ff", "zone/"}, + }; + game::DB_FileSysInterface* db_fs_initialize_stub() { switch (db_filesysImpl->current.integer) @@ -80,14 +87,17 @@ namespace database { std::string name = file; - if (name.ends_with(".flac")) + const auto search_path = [&](const std::string& ext, const std::string& path) { - name = "sound/" + name; - } + if (name.ends_with(ext) && !filesystem::exists(name)) + { + name = path + name; + } + }; - if (name.ends_with(".bik")) + for (const auto& [ext, path] : file_search_folders) { - name = "videos/" + name; + search_path(ext, path); } std::string path{}; @@ -97,7 +107,6 @@ namespace database } const auto handle = handle_allocator.allocate(); - std::memset(handle, 0, sizeof(handle)); try { @@ -232,8 +241,31 @@ namespace database bool bnet_fs_exists_stub(game::DB_FileSysInterface* this_, game::DB_IFileSysFile* handle, const char* filename) { - std::string path{}; - return filesystem::find_file(filename, &path) || bnet_fs_exists_hook.invoke(this_, handle, filename); + std::string name = filename; + const auto search_path = [&](const std::string& ext, const std::string& path) + { + if (!name.ends_with(ext)) + { + return false; + } + + return filesystem::exists(name) || filesystem::exists(path + name); + }; + + if (filesystem::exists(filename)) + { + return true; + } + + for (const auto& [ext, path] : file_search_folders) + { + if (search_path(ext, path)) + { + return true; + } + } + + return bnet_fs_exists_hook.invoke(this_, handle, filename); } uint64_t bink_io_read_stub(game::DB_IFileSysFile** handle, void* dest, uint64_t bytes) @@ -291,133 +323,6 @@ namespace database } } } - - utils::hook::detour db_link_xasset_entry1_hook; - game::XAssetEntry* db_link_xasset_entry1_stub(game::XAssetType type, game::XAssetHeader* header) - { - const auto result = db_link_xasset_entry1_hook.invoke(type, header); - if (result->asset.type == game::ASSET_TYPE_SOUND) - { - const auto sound = result->asset.header.sound; - - if (utils::flags::has_flag("dumpsoundaliases")) - { - sound::dump_sound(sound); - } - - for (auto i = 0; i < sound->count; i++) - { - const auto alias = &sound->head[i]; - if (alias->soundFile != nullptr && alias->soundFile->type == 2) - { - const auto file_index = alias->soundFile->u.streamSnd.filename.fileIndex; - const auto length = alias->soundFile->u.streamSnd.filename.info.packed.length; - const auto offset = alias->soundFile->u.streamSnd.filename.info.packed.offset; - const auto name = utils::string::va("%s.flac", alias->aliasName); - - sound_files[file_index][offset] = name; - sound_sizes[name] = length; - } - } - } - - return result; - } - - void dump_flac_sound(const std::string& sound_name, unsigned short file_index, uint64_t start_offset, uint64_t size) - { - const auto name = utils::string::va("soundfile%i.pak", file_index); - const auto fs_interface = db_fs_initialize_stub(); - - const auto handle = fs_interface->vftbl->OpenFile(fs_interface, game::Sys_Folder::SF_PAKFILE, name); - if (handle == nullptr) - { - console::error("Sound file %s not found\n", name); - return; - } - - const auto buffer = utils::memory::get_allocator()->allocate_array(size); - const auto _0 = gsl::finally([&]() - { - utils::memory::get_allocator()->free(buffer); - }); - - const auto result = fs_interface->vftbl->Read(fs_interface, handle, start_offset, size, buffer); - if (result != game::FILESYSRESULT_SUCCESS) - { - console::error("Error reading file %s\n", name); - } - - const auto path = utils::string::va("dumps/sound/%s", sound_name.data()); - utils::io::write_file(path, std::string(buffer, size), false); - console::info("Sound dumped to %s\n", path); - } - - void dump_sound_file(unsigned short file_index) - { - const auto name = utils::string::va("soundfile%i.pak", file_index); - const auto fs_interface = db_fs_initialize_stub(); - - const auto handle = fs_interface->vftbl->OpenFile(fs_interface, game::Sys_Folder::SF_PAKFILE, name); - if (handle == nullptr) - { - console::error("Sound file %s not found\n", name); - return; - } - - const auto size = fs_interface->vftbl->Size(fs_interface, handle); - const auto buffer = utils::memory::get_allocator()->allocate_array(size); - const auto _0 = gsl::finally([&]() - { - utils::memory::get_allocator()->free(buffer); - }); - - const auto result = fs_interface->vftbl->Read(fs_interface, handle, 0, size, buffer); - if (result != game::FILESYSRESULT_SUCCESS) - { - console::error("Error reading file %s\n", name); - } - - std::vector signature = {0x66, 0x4C, 0x61, 0x43}; - - const auto check_signature = [&](char* start) - { - for (auto i = 0; i < signature.size(); i++) - { - if (start[i] != signature[i]) - { - return false; - } - } - - return true; - }; - - const auto end = buffer + size - signature.size() - 1; - for (auto pos = buffer; pos < end;) - { - const auto start_pos = pos; - if (check_signature(start_pos)) - { - ++pos; - - while (pos < end && !check_signature(pos)) - { - ++pos; - } - - const auto flac_size = static_cast(pos - start_pos); - std::string data{start_pos, flac_size}; - - const auto progress = static_cast(100 * (static_cast(start_pos - buffer) / static_cast(end - buffer))); - const auto sound_name = get_sound_file_name(file_index, static_cast(start_pos - buffer)); - const auto path = utils::string::va("dumps/sound/%s", sound_name.data()); - - utils::io::write_file(path, data, false); - console::info("Sound dumped: %s (%i%%)\n", path, progress); - } - } - } } class component final : public component_interface @@ -461,63 +366,6 @@ namespace database bink_io_read_hook.create(0x1407191B0, bink_io_read_stub); bink_io_seek_hook.create(0x140719200, bink_io_seek_stub); } - - if (!utils::flags::has_flag("sounddumputils")) - { - return; - } - - db_link_xasset_entry1_hook.create(0x140414900, db_link_xasset_entry1_stub); - - command::add("listSoundFiles", []() - { - for (const auto& packed : sound_files) - { - for (const auto& sound : packed.second) - { - console::info("soundfile%i.pak %s %llX", packed.first, sound.second.data(), sound.first); - } - } - }); - - command::add("dumpSoundFile", [](const command::params& params) - { - if (params.size() < 2) - { - console::info("Usage: dumpSoundFile \n"); - return; - } - - const auto index = static_cast(atoi(params.get(1))); - dump_sound_file(index); - }); - - command::add("dumpSound", [](const command::params& params) - { - if (params.size() < 2) - { - console::info("Usage: dumpSound \n"); - return; - } - - const auto name = params.get(1); - for (const auto& packed : sound_files) - { - for (auto i = packed.second.begin(); i != packed.second.end();) - { - if (i->second != name) - { - ++i; - continue; - } - - const auto& sound_name = i->second; - const auto start_offset = i->first; - dump_flac_sound(sound_name, packed.first, start_offset, sound_sizes[sound_name]); - break; - } - } - }); } }; } diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp index 0421f083..ba4644d6 100644 --- a/src/client/component/fastfiles.cpp +++ b/src/client/component/fastfiles.cpp @@ -82,25 +82,6 @@ namespace fastfiles return db_read_stream_file_hook.invoke(a1, a2); } - bool exists(const std::string& zone) - { - const auto is_localized = game::DB_IsLocalized(zone.data()); - const auto db_fs = game::DB_FSInitialize(); - - auto handle = db_fs->vftbl->OpenFile(db_fs, - (is_localized ? game::SF_ZONE_LOC : game::SF_ZONE), utils::string::va("%s.ff", zone.data())); - const auto _0 = gsl::finally([&] - { - if (handle != nullptr) - { - db_fs->vftbl->Close(db_fs, handle); - } - - }); - - return handle != nullptr; - } - void skip_extra_zones_stub(utils::hook::assembler& a) { const auto skip = a.newLabel(); @@ -123,8 +104,187 @@ namespace fastfiles a.and_(r15d, r14d); a.jmp(0x140415E29); } + + bool try_load_zone(std::string name, bool localized, bool game = false) + { + if (localized) + { + const auto language = game::SEH_GetCurrentLanguageCode(); + try_load_zone(language + "_"s + name, false); + + if (language != "eng"s) + { + try_load_zone("eng_" + name, false); + } + } + + if (!fastfiles::exists(name)) + { + return false; + } + + game::XZoneInfo info{}; + info.name = name.data(); + info.allocFlags = (game ? game::DB_ZONE_GAME : game::DB_ZONE_COMMON) | game::DB_ZONE_CUSTOM; + info.freeFlags = 0; + game::DB_LoadXAssets(&info, 1u, game::DBSyncMode::DB_LOAD_ASYNC); + return true; + } + + void load_post_gfx_and_ui_and_common_zones(game::XZoneInfo* zoneInfo, + unsigned int zoneCount, game::DBSyncMode syncMode) + { + // code_post_gfx_mp + // ui_mp + // common_mp + + try_load_zone("h2_mod_ui", true); + try_load_zone("h2_mod_common", true); + + game::DB_LoadXAssets(zoneInfo, zoneCount, syncMode); + + try_load_zone("mod", true); + } + + constexpr unsigned int get_asset_type_size(const game::XAssetType type) + { + constexpr int asset_type_sizes[] = + { + 96, 88, 128, 56, 40, 216, 56, 696, + 624, 32, 32, 32, 32, 32, 2112, 2032, + 104, 32, 24, 1520, 152, 152, 16, 64, + 640, 40, 16, 136, 24, 296, 176, 2896, + 48, 0, 24, 200, 88, 16, 144, 3848, + 56, 72, 16, 16, 0, 0, 0, 0, 24, + 40, 24, 48, 40, 24, 16, 80, 128, + 2280, 136, 32, 72, 24, 64, 88, 48, + 32, 96, 120, 64, 32, 32 + }; + + return asset_type_sizes[type]; + } + + constexpr unsigned int get_pool_type_size(const game::XAssetType type) + { + constexpr int asset_pool_sizes[] = + { + 150, 1024, 16, 1, 128, 7000, 5248, 5120, + 10624, 256, 49152, 12288, 12288, 72864, + 512, 3072, 12000, 16000, 256, 64, 64, 64, + 64, 10000, 1, 1, 1, 1, 1, 2, 1, 1, 32, 0, + 128, 400, 0, 11500, 128, 360, 1, 2048, 4, + 6, 0, 0, 0, 0, 1024, 768, 400, 128, 128, + 24, 24, 24, 32, 128, 2, 0, 64, 384, 128, + 1, 128, 64, 32, 32, 16, 32, 16 + }; + + return asset_pool_sizes[type]; + } + + template + char* reallocate_asset_pool() + { + constexpr auto element_size = get_asset_type_size(Type); + static char new_pool[element_size * Size] = {0}; + static_assert(element_size != 0); + assert(element_size == game::DB_GetXAssetTypeSize(Type)); + + std::memmove(new_pool, game::g_assetPool[Type], game::g_poolSize[Type] * element_size); + + game::g_assetPool[Type] = new_pool; + game::g_poolSize[Type] = Size; + + return new_pool; + } + + template + char* reallocate_asset_pool_multiplier() + { + constexpr auto pool_size = get_pool_type_size(Type); + return reallocate_asset_pool(); + } + + void reallocate_asset_pools() + { + const auto xmodel_pool = reallocate_asset_pool_multiplier(); + utils::hook::inject(0x140413D93, xmodel_pool + 8); + + reallocate_asset_pool_multiplier(); + reallocate_asset_pool_multiplier(); + reallocate_asset_pool_multiplier(); + } + + void add_custom_level_load_zone(void* load, const char* name, bool localized, const size_t size_est) + { + if (localized) + { + const auto language = game::SEH_GetCurrentLanguageCode(); + const auto eng_name = "eng_"s + name; + const auto lang_name = language + "_"s + name; + + if (fastfiles::exists(lang_name)) + { + add_custom_level_load_zone(load, lang_name.data(), false, size_est); + } + + if (eng_name != lang_name && fastfiles::exists(eng_name)) + { + add_custom_level_load_zone(load, eng_name.data(), false, size_est); + } + } + + game::DB_LevelLoadAddZone(load, name, game::DB_ZONE_GAME | game::DB_ZONE_CUSTOM, size_est); + } + + void db_load_level_add_custom_zone_stub(void* load, const char* name, const unsigned int alloc_flags, + const size_t size_est) + { + //add_custom_level_load_zone(load, name, true, size_est); + try_load_zone(name, true, true); + } + + void db_load_level_add_map_zone_stub(void* load, const char* name, const unsigned int alloc_flags, + const size_t size_est) + { + auto is_builtin_map = false; + for (auto map = &game::maps[0]; map->unk; ++map) + { + if (!std::strcmp(map->name, name)) + { + is_builtin_map = true; + break; + } + } + + if (is_builtin_map) + { + game::DB_LevelLoadAddZone(load, name, alloc_flags, size_est); + } + else + { + add_custom_level_load_zone(load, name, true, size_est); + } + } } + bool exists(const std::string& zone) + { + const auto is_localized = game::DB_IsLocalized(zone.data()); + const auto db_fs = game::DB_FSInitialize(); + + auto handle = db_fs->vftbl->OpenFile(db_fs, + (is_localized ? game::SF_ZONE_LOC : game::SF_ZONE), utils::string::va("%s.ff", zone.data())); + const auto _0 = gsl::finally([&] + { + if (handle != nullptr) + { + db_fs->vftbl->Close(db_fs, handle); + } + + }); + + return handle != nullptr; + } void enum_assets(const game::XAssetType type, const std::function& callback, const bool includeOverride) { game::DB_EnumXAssets_Internal(type, static_cast([](game::XAssetHeader header, void* data) @@ -163,6 +323,17 @@ namespace fastfiles utils::hook::nop(0x140415DFC, 13); utils::hook::jump(0x140415DFC, utils::hook::assemble(skip_extra_zones_stub), true); + // load our custom ui and common zones + utils::hook::call(0x14074E22A, load_post_gfx_and_ui_and_common_zones); + + reallocate_asset_pools(); + + // only load extra zones with addon maps & common_specialops & common_survival & custom maps if the exist + utils::hook::call(0x1404128B0, db_load_level_add_map_zone_stub); + utils::hook::call(0x140412854, db_load_level_add_custom_zone_stub); + utils::hook::call(0x14041282D, db_load_level_add_custom_zone_stub); + utils::hook::call(0x14041287C, db_load_level_add_custom_zone_stub); + command::add("loadzone", [](const command::params& params) { if (params.size() < 2) @@ -172,18 +343,10 @@ namespace fastfiles } const auto name = params.get(1); - - if (!fastfiles::exists(name)) + if (!try_load_zone(name, false)) { console::warn("loadzone: zone \"%s\" could not be found!\n", name); - return; } - - game::XZoneInfo info{}; - info.name = name; - info.allocFlags = game::DB_ZONE_GAME | game::DB_ZONE_CUSTOM; - info.freeFlags = 0; - game::DB_LoadXAssets(&info, 1u, game::DBSyncMode::DB_LOAD_SYNC); }); command::add("g_poolSizes", []() diff --git a/src/client/component/fastfiles.hpp b/src/client/component/fastfiles.hpp index 09e4767b..be2a072c 100644 --- a/src/client/component/fastfiles.hpp +++ b/src/client/component/fastfiles.hpp @@ -6,4 +6,6 @@ namespace fastfiles { void enum_assets(const game::XAssetType type, const std::function& callback, const bool includeOverride); std::string get_current_fastfile(); + + bool exists(const std::string& zone); } diff --git a/src/client/component/filesystem.cpp b/src/client/component/filesystem.cpp index 8e9da72d..8e74802a 100644 --- a/src/client/component/filesystem.cpp +++ b/src/client/component/filesystem.cpp @@ -4,11 +4,13 @@ #include "filesystem.hpp" #include "console.hpp" #include "localized_strings.hpp" +#include "mods.hpp" #include "game/game.hpp" #include #include +#include namespace filesystem { @@ -39,6 +41,13 @@ namespace filesystem filesystem::register_path(L"."); filesystem::register_path(L"h2-mod"); + const auto mod_path = utils::flags::get_flag("mod"); + if (mod_path.has_value()) + { + filesystem::register_path(mod_path.value()); + mods::mod_path = mod_path.value(); + } + localized_strings::clear(); utils::hook::invoke(0x14060BF50, name); @@ -129,6 +138,20 @@ namespace filesystem return false; } + bool exists(const std::string& path) + { + for (const auto& search_path : get_search_paths_internal()) + { + const auto path_ = search_path / path; + if (utils::io::file_exists(path_.generic_string())) + { + return true; + } + } + + return false; + } + void register_path(const std::filesystem::path& path) { if (!initialized) diff --git a/src/client/component/filesystem.hpp b/src/client/component/filesystem.hpp index cc07b7d6..79d3fca8 100644 --- a/src/client/component/filesystem.hpp +++ b/src/client/component/filesystem.hpp @@ -7,7 +7,8 @@ namespace filesystem std::string read_file(const std::string& path); bool read_file(const std::string& path, std::string* data, std::string* real_path = nullptr); bool find_file(const std::string& path, std::string* real_path); - + bool exists(const std::string& path); + void register_path(const std::filesystem::path& path); void unregister_path(const std::filesystem::path& path); diff --git a/src/client/component/mods.cpp b/src/client/component/mods.cpp index aa2b8551..3349fca5 100644 --- a/src/client/component/mods.cpp +++ b/src/client/component/mods.cpp @@ -57,6 +57,17 @@ namespace mods game::Com_Shutdown(""); }, scheduler::pipeline::main); } + + void full_restart(const std::string& arg) + { + utils::nt::relaunch_self(" -singleplayer "s.append(arg), true); + utils::nt::terminate(); + } + } + + bool mod_requires_restart(const std::string& path) + { + return utils::io::file_exists(path + "/mod.ff") || utils::io::file_exists(path + "/zone/mod.ff"); } class component final : public component_interface @@ -94,10 +105,20 @@ namespace mods } console::info("Loading mod %s\n", path); - filesystem::unregister_path(mod_path); - filesystem::register_path(path); - mod_path = path; - restart(); + + if (mod_requires_restart(mod_path) || mod_requires_restart(path)) + { + // vid_restart is still broken :( + console::info("Restarting...\n"); + full_restart("-mod "s + path); + } + else + { + filesystem::unregister_path(mod_path); + filesystem::register_path(path); + mod_path = path; + restart(); + } }); command::add("unloadmod", [](const command::params& params) @@ -116,9 +137,18 @@ namespace mods } console::info("Unloading mod %s\n", mod_path.data()); - filesystem::unregister_path(mod_path); - mod_path.clear(); - restart(); + + if (mod_requires_restart(mod_path)) + { + console::info("Restarting...\n"); + full_restart(""); + } + else + { + filesystem::unregister_path(mod_path); + mod_path.clear(); + restart(); + } }); command::add("com_restart", []() diff --git a/src/client/component/mods.hpp b/src/client/component/mods.hpp index 364a2f11..7b7655f6 100644 --- a/src/client/component/mods.hpp +++ b/src/client/component/mods.hpp @@ -3,4 +3,6 @@ namespace mods { extern std::string mod_path; + + bool mod_requires_restart(const std::string& path); } \ No newline at end of file diff --git a/src/client/game/dvars.cpp b/src/client/game/dvars.cpp index 5cea39e8..5fc1e56b 100644 --- a/src/client/game/dvars.cpp +++ b/src/client/game/dvars.cpp @@ -3355,6 +3355,42 @@ namespace dvars "friendlyNameFontSizeSplitscreen", "Fontsize of the popup friendly names, in splitscreen.", }, + { + "fs_basegame", + "Base game name", + }, + { + "fs_basepath", + "Base game path", + }, + { + "fs_basepath_output", + "Base game path", + }, + { + "fs_cdpath", + "CD path", + }, + { + "fs_copyfiles", + "Copy all used files to another location", + }, + { + "fs_debug", + "Enable file system debugging information", + }, + { + "fs_game", + "Game data directory. Must be \"\" or a sub directory of 'mods/'.", + }, + { + "fs_homepath", + "Game home path", + }, + { + "fs_ignoreLocalized", + "Ignore localized assets", + }, { "fx_alphaThreshold", "Don't draw billboard sprites, oriented sprites or tails with alpha below this threshold (0-256).", diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index b7dff7f6..203f3114 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -45,6 +45,9 @@ namespace game WEAK symbol DB_GetRawBuffer{0x140413C40}; WEAK symbol DB_LinkXAssetEntry1{0x140414900}; WEAK symbol DB_IsLocalized{0x1404141E0}; + WEAK symbol DB_GetXAssetTypeSize{0x1403E40D0}; + WEAK symbol DB_LevelLoadAddZone{0x1404145D0}; WEAK symbol Dvar_FindVar{0x140618F90}; WEAK symbol Dvar_FindMalleableVar{0x140618F00}; @@ -121,6 +124,7 @@ namespace game WEAK symbol Scr_GetInt{0x1405C7890}; WEAK symbol Scr_AddInt{0x1405C69A0}; WEAK symbol Scr_AddString{0x1405C6A80}; + WEAK symbol Scr_LoadScript{0x1405BCEC0}; WEAK symbol VM_Execute{0x1405C8DB0}; @@ -191,7 +195,12 @@ namespace game WEAK symbol hWnd{0x14CCF81C0}; - WEAK symbol g_assetNames{0x140BEF280}; + WEAK game::symbol g_assetNames{0x140BEF280}; + + WEAK game::symbol g_assetPool{0x140BF3620}; + + WEAK game::symbol g_zoneCount{0x1422F45F4}; + WEAK game::symbol g_zoneIndex{0x1422F8DC8}; WEAK symbol g_compressor{0x142065E80}; WEAK symbol g_poolSize{0x140BF2E40}; diff --git a/src/common/utils/flags.cpp b/src/common/utils/flags.cpp index cbada183..539b6640 100644 --- a/src/common/utils/flags.cpp +++ b/src/common/utils/flags.cpp @@ -6,42 +6,69 @@ namespace utils::flags { - void parse_flags(std::vector& flags) + namespace { - int num_args; - auto* const argv = CommandLineToArgvW(GetCommandLineW(), &num_args); + bool parsed = false; - flags.clear(); + using flag_map_t = std::unordered_map>; - if (argv) + flag_map_t& get_flags() { - for (auto i = 0; i < num_args; ++i) - { - std::wstring wide_flag(argv[i]); - if (wide_flag[0] == L'-') - { - wide_flag.erase(wide_flag.begin()); - flags.emplace_back(string::convert(wide_flag)); - } - } + static flag_map_t map = {}; + return map; + } - LocalFree(argv); + void parse_flags(flag_map_t& flags) + { + int num_args; + auto* const argv = CommandLineToArgvW(GetCommandLineW(), &num_args); + + flags.clear(); + + if (argv) + { + std::optional last_flag{}; + for (auto i = 0; i < num_args; ++i) + { + std::wstring wide_flag(argv[i]); + if (wide_flag[0] == L'-') + { + wide_flag.erase(wide_flag.begin()); + const auto flag = string::convert(wide_flag); + + last_flag = flag; + flags[flag] = {}; + } + else if (last_flag.has_value()) + { + const auto& flag = last_flag.value(); + + flags[flag] = string::convert(wide_flag); + last_flag = {}; + } + } + + LocalFree(argv); + } + } + + void check_parse_flags() + { + if (!parsed) + { + parse_flags(get_flags()); + parsed = true; + } } } bool has_flag(const std::string& flag) { - static auto parsed = false; - static std::vector enabled_flags; + check_parse_flags(); - if (!parsed) + for (const auto& [name, value] : get_flags()) { - parse_flags(enabled_flags); - } - - for (const auto& entry : enabled_flags) - { - if (string::to_lower(entry) == string::to_lower(flag)) + if (string::to_lower(name) == string::to_lower(flag)) { return true; } @@ -49,4 +76,40 @@ namespace utils::flags return false; } + + std::optional get_flag(const std::string& flag) + { + check_parse_flags(); + + for (const auto& [name, value] : get_flags()) + { + if (string::to_lower(name) == string::to_lower(flag)) + { + return value; + } + } + + return {}; + } + + std::optional get_flag(const std::string& flag, const std::string& shortname) + { + auto value = get_flag(flag); + if (!value.has_value()) + { + value = get_flag(shortname); + } + return value; + } + + std::string get_flag(const std::string& flag, const std::string& shortname, + const std::string& default_) + { + const auto value = get_flag(flag, shortname); + if (!value.has_value()) + { + return default_; + } + return value.value(); + } } diff --git a/src/common/utils/flags.hpp b/src/common/utils/flags.hpp index cf304b20..c38b076b 100644 --- a/src/common/utils/flags.hpp +++ b/src/common/utils/flags.hpp @@ -1,8 +1,13 @@ #pragma once #include +#include namespace utils::flags { bool has_flag(const std::string& flag); + std::optional get_flag(const std::string& flag); + std::optional get_flag(const std::string& flag, const std::string& shortname); + std::string get_flag(const std::string& flag, const std::string& shortname, + const std::string& default_); } diff --git a/src/common/utils/nt.cpp b/src/common/utils/nt.cpp index 455c3b86..858f73f7 100644 --- a/src/common/utils/nt.cpp +++ b/src/common/utils/nt.cpp @@ -226,7 +226,7 @@ namespace utils::nt return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, res)); } - void relaunch_self(const std::string& extra_command_line) + void relaunch_self(const std::string& extra_command_line, bool override_command_line) { const utils::nt::library self; @@ -243,7 +243,14 @@ namespace utils::nt std::string command_line = GetCommandLineA(); if (!extra_command_line.empty()) { - command_line += " " + extra_command_line; + if (override_command_line) + { + command_line = extra_command_line; + } + else + { + command_line += " " + extra_command_line; + } } CreateProcessA(self.get_path().data(), command_line.data(), nullptr, nullptr, false, NULL, nullptr, current_dir, diff --git a/src/common/utils/nt.hpp b/src/common/utils/nt.hpp index 1101554c..ec479e88 100644 --- a/src/common/utils/nt.hpp +++ b/src/common/utils/nt.hpp @@ -105,6 +105,6 @@ namespace utils::nt __declspec(noreturn) void raise_hard_exception(); std::string load_resource(int id); - void relaunch_self(const std::string& extra_command_line = ""); + void relaunch_self(const std::string& extra_command_line = "", bool override_command_line = false); __declspec(noreturn) void terminate(uint32_t code = 0); }