More mod loading stuff
This commit is contained in:
parent
da7bd08bad
commit
14f15ae47c
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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", []()
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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", []()
|
||||
|
@ -3,4 +3,6 @@
|
||||
namespace mods
|
||||
{
|
||||
extern std::string mod_path;
|
||||
|
||||
bool mod_requires_restart(const std::string& path);
|
||||
}
|
@ -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).",
|
||||
|
@ -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};
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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_);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user