More mod loading stuff

This commit is contained in:
Federico Cecchetto 2022-08-22 01:42:13 +02:00
parent da7bd08bad
commit 14f15ae47c
13 changed files with 445 additions and 256 deletions

View File

@ -45,6 +45,13 @@ namespace database
game::dvar_t* db_filesysImpl = nullptr;
utils::hook::detour db_fs_initialize_hook;
std::unordered_map<std::string, std::string> 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<game::DB_IFileSysFile>();
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<bool>(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<bool>(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<game::XAssetEntry*>(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<char>(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<char>(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<std::uint8_t> 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<size_t>(pos - start_pos);
std::string data{start_pos, flac_size};
const auto progress = static_cast<int>(100 * (static_cast<float>(start_pos - buffer) / static_cast<float>(end - buffer)));
const auto sound_name = get_sound_file_name(file_index, static_cast<uint64_t>(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 <index>\n");
return;
}
const auto index = static_cast<unsigned short>(atoi(params.get(1)));
dump_sound_file(index);
});
command::add("dumpSound", [](const command::params& params)
{
if (params.size() < 2)
{
console::info("Usage: dumpSound <name>\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;
}
}
});
}
};
}

View File

@ -82,25 +82,6 @@ namespace fastfiles
return db_read_stream_file_hook.invoke<void>(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 <game::XAssetType Type, size_t Size>
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 <game::XAssetType Type, size_t Multiplier>
char* reallocate_asset_pool_multiplier()
{
constexpr auto pool_size = get_pool_type_size(Type);
return reallocate_asset_pool<Type, pool_size * Multiplier>();
}
void reallocate_asset_pools()
{
const auto xmodel_pool = reallocate_asset_pool_multiplier<game::ASSET_TYPE_XMODEL, 2>();
utils::hook::inject(0x140413D93, xmodel_pool + 8);
reallocate_asset_pool_multiplier<game::ASSET_TYPE_WEAPON, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_SOUND, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_LOADED_SOUND, 2>();
}
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<void(game::XAssetHeader)>& callback, const bool includeOverride)
{
game::DB_EnumXAssets_Internal(type, static_cast<void(*)(game::XAssetHeader, void*)>([](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", []()

View File

@ -6,4 +6,6 @@ namespace fastfiles
{
void enum_assets(const game::XAssetType type, const std::function<void(game::XAssetHeader)>& callback, const bool includeOverride);
std::string get_current_fastfile();
bool exists(const std::string& zone);
}

View File

@ -4,11 +4,13 @@
#include "filesystem.hpp"
#include "console.hpp"
#include "localized_strings.hpp"
#include "mods.hpp"
#include "game/game.hpp"
#include <utils/io.hpp>
#include <utils/hook.hpp>
#include <utils/flags.hpp>
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<void>(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)

View File

@ -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);

View File

@ -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", []()

View File

@ -3,4 +3,6 @@
namespace mods
{
extern std::string mod_path;
bool mod_requires_restart(const std::string& path);
}

View File

@ -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).",

View File

@ -45,6 +45,9 @@ namespace game
WEAK symbol<int(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x140413C40};
WEAK symbol<XAssetEntry*(XAssetType type, XAssetHeader* header)> DB_LinkXAssetEntry1{0x140414900};
WEAK symbol<bool(const char* zoneName)> DB_IsLocalized{0x1404141E0};
WEAK symbol<size_t(XAssetType type)> DB_GetXAssetTypeSize{0x1403E40D0};
WEAK symbol<void(void* levelLoad, const char* name,
const unsigned int allocFlags, const unsigned __int64 sizeEst)> DB_LevelLoadAddZone{0x1404145D0};
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x140618F90};
WEAK symbol<dvar_t*(int hash)> Dvar_FindMalleableVar{0x140618F00};
@ -121,6 +124,7 @@ namespace game
WEAK symbol<int(unsigned int index)> Scr_GetInt{0x1405C7890};
WEAK symbol<void(int value)> Scr_AddInt{0x1405C69A0};
WEAK symbol<void(const char* value)> Scr_AddString{0x1405C6A80};
WEAK symbol<void(const char* name)> Scr_LoadScript{0x1405BCEC0};
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x1405C8DB0};
@ -191,7 +195,12 @@ namespace game
WEAK symbol<HWND> hWnd{0x14CCF81C0};
WEAK symbol<const char*> g_assetNames{0x140BEF280};
WEAK game::symbol<const char*> g_assetNames{0x140BEF280};
WEAK game::symbol<void*> g_assetPool{0x140BF3620};
WEAK game::symbol<unsigned int> g_zoneCount{0x1422F45F4};
WEAK game::symbol<unsigned short> g_zoneIndex{0x1422F8DC8};
WEAK symbol<int> g_compressor{0x142065E80};
WEAK symbol<int> g_poolSize{0x140BF2E40};

View File

@ -6,42 +6,69 @@
namespace utils::flags
{
void parse_flags(std::vector<std::string>& flags)
namespace
{
int num_args;
auto* const argv = CommandLineToArgvW(GetCommandLineW(), &num_args);
bool parsed = false;
flags.clear();
using flag_map_t = std::unordered_map<std::string, std::optional<std::string>>;
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<std::string> 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<std::string> 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<std::string> 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<std::string> 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();
}
}

View File

@ -1,8 +1,13 @@
#pragma once
#include <string>
#include <optional>
namespace utils::flags
{
bool has_flag(const std::string& flag);
std::optional<std::string> get_flag(const std::string& flag);
std::optional<std::string> 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_);
}

View File

@ -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,

View File

@ -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);
}