some h2 mod stuff i'm storing here temp

This commit is contained in:
m 2022-09-03 00:23:04 -05:00
parent a93414f472
commit 9a39f92c26
14 changed files with 472 additions and 108 deletions

View File

@ -9,6 +9,7 @@
#include "console.hpp"
#include "game_console.hpp"
#include "fastfiles.hpp"
#include "filesystem.hpp"
#include "scheduler.hpp"
#include "logfile.hpp"
#include "dvars.hpp"
@ -130,6 +131,11 @@ namespace command
dvars::on_register(dvar_name, [dvar_name, value]()
{
game::Dvar_SetCommand(game::generateHashValue(dvar_name.data()), "", value.data());
if (dvar_name == "fs_game")
{
filesystem::register_path(value);
}
});
}
}

View File

@ -143,6 +143,32 @@ namespace fastfiles
}
}
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;
}
utils::hook::detour sys_createfile_hook;
HANDLE sys_create_file_stub(game::Sys_Folder folder, const char* base_filename)
{
@ -206,12 +232,9 @@ namespace fastfiles
// ui
// common
if (fastfiles::exists("mod"))
{
data.push_back({ "mod", game::DB_ZONE_COMMON | game::DB_ZONE_CUSTOM, 0 });
}
game::DB_LoadXAssets(data.data(), static_cast<std::uint32_t>(data.size()), syncMode);
try_load_zone("mod", true);
}
void load_ui_zones(game::XZoneInfo* zoneInfo, unsigned int zoneCount, game::DBSyncMode syncMode)
@ -227,8 +250,8 @@ namespace fastfiles
bool exists(const std::string& zone)
{
auto is_localized = game::DB_IsLocalized(zone.data());
auto handle = game::Sys_CreateFile((is_localized ? game::SF_ZONE_LOC : game::SF_ZONE), utils::string::va("%s.ff", zone.data()));
const auto is_localized = game::DB_IsLocalized(zone.data());
const auto handle = game::Sys_CreateFile((is_localized ? game::SF_ZONE_LOC : game::SF_ZONE), utils::string::va("%s.ff", zone.data()));
if (handle != (HANDLE)-1)
{
CloseHandle(handle);
@ -262,7 +285,6 @@ namespace fastfiles
{
db_try_load_x_file_internal_hook.create(
SELECT_VALUE(0x1F5700_b, 0x39A620_b), &db_try_load_x_file_internal);
db_find_xasset_header_hook.create(game::DB_FindXAssetHeader, db_find_xasset_header_stub);
g_dump_scripts = dvars::register_bool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts");
@ -312,22 +334,11 @@ namespace fastfiles
return;
}
const char* name = params.get(1);
if (!fastfiles::exists(name))
const auto name = params.get(1);
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;
info.freeFlags = 0;
info.allocFlags |= game::DB_ZONE_CUSTOM; // skip extra zones with this flag!
game::DB_LoadXAssets(&info, 1, game::DBSyncMode::DB_LOAD_ASYNC);
});
}
};

View File

@ -1,72 +1,142 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "console.hpp"
#include "filesystem.hpp"
#include "game_module.hpp"
#include "localized_strings.hpp"
#include "game/game.hpp"
#include "dvars.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/io.hpp>
#include <utils/hook.hpp>
#include <utils/flags.hpp>
namespace filesystem
{
file::file(std::string name)
: name_(std::move(name))
namespace
{
char* buffer{};
const auto size = game::FS_ReadFile(this->name_.data(), &buffer);
bool initialized = false;
if (size >= 0 && buffer)
std::deque<std::filesystem::path>& get_search_paths_internal()
{
this->valid_ = true;
this->buffer_.append(buffer, size);
game::FS_FreeFile(buffer);
}
}
bool file::exists() const
{
return this->valid_;
}
const std::string& file::get_buffer() const
{
return this->buffer_;
}
const std::string& file::get_name() const
{
return this->name_;
}
std::unordered_set<std::string>& get_search_paths()
{
static std::unordered_set<std::string> search_paths{};
static std::deque<std::filesystem::path> search_paths{};
return search_paths;
}
bool is_fallback_lang()
{
static const auto* loc_language = game::Dvar_FindVar("loc_language");
const auto id = loc_language->current.integer;
return id == 5 || id == 6 || id == 8 || id == 9 || id == 10 || id == 11 || id == 12 || id == 13 || id == 15;
}
void fs_startup_stub(const char* name)
{
console::info("[FS] Startup\n");
initialized = true;
filesystem::register_path(L".");
filesystem::register_path(L"h1-mod");
filesystem::register_path(L"data");
localized_strings::clear();
utils::hook::invoke<void>(SELECT_VALUE(0x40D890_b, 0x189A40_b), name);
}
std::vector<std::filesystem::path> get_paths(const std::filesystem::path& path)
{
std::vector<std::filesystem::path> paths{};
const auto code = game::SEH_GetCurrentLanguageName();
paths.push_back(path);
if (is_fallback_lang())
{
paths.push_back(path / "fallback");
}
paths.push_back(path / code);
return paths;
}
bool can_insert_path(const std::filesystem::path& path)
{
for (const auto& path_ : get_search_paths_internal())
{
if (path_ == path)
{
return false;
}
}
return true;
}
const char* sys_default_install_path_stub()
{
static auto current_path = std::filesystem::current_path().string();
return current_path.data();
}
}
std::string read_file(const std::string& path)
{
for (const auto& search_path : get_search_paths())
for (const auto& search_path : get_search_paths_internal())
{
const auto path_ = search_path + "/" + path;
if (utils::io::file_exists(path_))
const auto path_ = search_path / path;
if (utils::io::file_exists(path_.generic_string()))
{
return utils::io::read_file(path_);
return utils::io::read_file(path_.generic_string());
}
}
return {};
}
bool read_file(const std::string& path, std::string* data)
bool read_file(const std::string& path, std::string* data, std::string* real_path)
{
for (const auto& search_path : get_search_paths())
for (const auto& search_path : get_search_paths_internal())
{
const auto path_ = search_path + "/" + path;
if (utils::io::read_file(path_, data))
const auto path_ = search_path / path;
if (utils::io::read_file(path_.generic_string(), data))
{
if (real_path != nullptr)
{
*real_path = path_.generic_string();
}
return true;
}
}
return false;
}
bool find_file(const std::string& path, std::string* real_path)
{
for (const auto& search_path : get_search_paths_internal())
{
const auto path_ = search_path / path;
if (utils::io::file_exists(path_.generic_string()))
{
*real_path = path_.generic_string();
return true;
}
}
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;
}
@ -75,14 +145,82 @@ namespace filesystem
return false;
}
void register_path(const std::filesystem::path& path)
{
if (!initialized)
{
return;
}
const auto paths = get_paths(path);
for (const auto& path_ : paths)
{
if (can_insert_path(path_))
{
console::info("[FS] Registering path '%s'\n", path_.generic_string().data());
get_search_paths_internal().push_front(path_);
}
}
}
void unregister_path(const std::filesystem::path& path)
{
if (!initialized)
{
return;
}
const auto paths = get_paths(path);
for (const auto& path_ : paths)
{
auto& search_paths = get_search_paths_internal();
for (auto i = search_paths.begin(); i != search_paths.end();)
{
if (*i == path_)
{
console::info("[FS] Unregistering path '%s'\n", path_.generic_string().data());
i = search_paths.erase(i);
}
else
{
++i;
}
}
}
}
std::vector<std::string> get_search_paths()
{
std::vector<std::string> paths{};
for (const auto& path : get_search_paths_internal())
{
paths.push_back(path.generic_string());
}
return paths;
}
std::vector<std::string> get_search_paths_rev()
{
std::vector<std::string> paths{};
const auto& search_paths = get_search_paths_internal();
for (auto i = search_paths.rbegin(); i != search_paths.rend(); ++i)
{
paths.push_back(i->generic_string());
}
return paths;
}
class component final : public component_interface
{
public:
void post_unpack() override
{
get_search_paths().insert(".");
get_search_paths().insert("h1-mod");
get_search_paths().insert("data");
utils::hook::call(SELECT_VALUE(0x40C992_b, 0x18939F_b), fs_startup_stub);
utils::hook::jump(SELECT_VALUE(0x42CE00_b, 0x5B3440_b), sys_default_install_path_stub);
// fs_game flags
utils::hook::set<uint32_t>(SELECT_VALUE(0x40D2A5_b, 0x189275_b), 0);

View File

@ -2,22 +2,14 @@
namespace filesystem
{
class file
{
public:
file(std::string name);
bool exists() const;
const std::string& get_buffer() const;
const std::string& get_name() const;
private:
bool valid_ = false;
std::string name_;
std::string buffer_;
};
std::unordered_set<std::string>& get_search_paths();
std::string read_file(const std::string& path);
bool read_file(const std::string& path, std::string* data);
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);
std::vector<std::string> get_search_paths();
std::vector<std::string> get_search_paths_rev();
}

View File

@ -1,10 +1,16 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "console.hpp"
#include "filesystem.hpp"
#include "localized_strings.hpp"
#include "game/game.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/concurrency.hpp>
#include "game/game.hpp"
#include <utils/io.hpp>
namespace localized_strings
{
@ -12,7 +18,13 @@ namespace localized_strings
{
utils::hook::detour seh_string_ed_get_string_hook;
using localized_map = std::unordered_map<std::string, std::string>;
struct localize_entry
{
std::string value{};
bool volatile_{};
};
using localized_map = std::unordered_map<std::string, localize_entry>;
utils::concurrency::container<localized_map> localized_overrides;
const char* seh_string_ed_get_string(const char* reference)
@ -22,22 +34,155 @@ namespace localized_strings
const auto entry = map.find(reference);
if (entry != map.end())
{
return utils::string::va("%s", entry->second.data());
return utils::string::va("%s", entry->second.value.data());
}
return seh_string_ed_get_string_hook.invoke<const char*>(reference);
});
}
game::XAssetHeader db_find_localize_entry_stub(game::XAssetType type, const char* name, int allow_create_default)
{
const auto value = localized_overrides.access<const char*>([&](const localized_map& map)
-> const char*
{
const auto entry = map.find(name);
if (entry != map.end())
{
return utils::string::va("%s", entry->second.value.data());
}
void override(const std::string& key, const std::string& value)
return nullptr;
});
if (value == nullptr)
{
return game::DB_FindXAssetHeader(type, name, allow_create_default);
}
static game::LocalizeEntry entry{};
entry.value = value;
entry.name = name;
return static_cast<game::XAssetHeader>(&entry);
}
bool parse_localized_strings_file(const std::string& data)
{
rapidjson::Document j;
j.Parse(data.data());
if (!j.IsObject())
{
return false;
}
localized_overrides.access([&](localized_map& map)
{
const auto obj = j.GetObj();
for (const auto& [key, value] : obj)
{
if (!key.IsString() || !value.IsString())
{
continue;
}
const auto name = key.GetString();
const auto str = value.GetString();
const auto entry = map.find(name);
if (entry == map.end() || entry->second.volatile_)
{
map[name] = {str, true};
}
}
});
return true;
}
bool try_load_file(const std::string& path, const std::string& language)
{
const auto file = utils::string::va("%s/localizedstrings/%s.json", path.data(), language.data());
if (!utils::io::file_exists(file))
{
return false;
}
console::info("Parsing %s\n", file);
const auto data = utils::io::read_file(file);
if (!parse_localized_strings_file(data))
{
console::error("Invalid language json file\n");
return false;
}
return true;
}
void load_localized_strings()
{
bool found = false;
const auto search_paths = filesystem::get_search_paths_rev();
const auto language = game::SEH_GetCurrentLanguageName();
for (const auto& path : search_paths)
{
bool found_in_current_path = false;
if (try_load_file(path, "english"))
{
found_in_current_path = true;
found = true;
}
if (language != "english"s && !try_load_file(path, language) && found_in_current_path)
{
console::warn("No valid language file found for '%s' in '%s/localizedstrings/', falling back to 'english'\n",
language, path.data());
}
else
{
found = true;
}
}
if (!found)
{
console::warn("[Localized strings] No valid language file found!\n");
}
}
}
void override(const std::string& key, const std::string& value, bool volatile_)
{
localized_overrides.access([&](localized_map& map)
{
map[key] = value;
map[key] = {value, volatile_};
});
}
void clear()
{
localized_overrides.access([&](localized_map& map)
{
for (auto i = map.begin(); i != map.end();)
{
if (i->second.volatile_)
{
i = map.erase(i);
}
else
{
++i;
}
}
});
load_localized_strings();
}
class component final : public component_interface
{
public:
@ -45,6 +190,7 @@ namespace localized_strings
{
// Change some localized strings
seh_string_ed_get_string_hook.create(SELECT_VALUE(0x3E6CE0_b, 0x585DA0_b), &seh_string_ed_get_string);
utils::hook::call(SELECT_VALUE(0x3E67C9_b, 0x585889_b), db_find_localize_entry_stub);
}
};
}

View File

@ -2,5 +2,6 @@
namespace localized_strings
{
void override(const std::string& key, const std::string& value);
void override(const std::string& key, const std::string& value, bool volatile_ = false);
void clear();
}

View File

@ -2,15 +2,15 @@
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "command.hpp"
#include "console.hpp"
#include "scheduler.hpp"
#include "filesystem.hpp"
#include "materials.hpp"
#include "fonts.hpp"
#include "localized_strings.hpp"
#include "materials.hpp"
#include "mods.hpp"
#include "scheduler.hpp"
#include <utils/hook.hpp>
#include <utils/io.hpp>
@ -32,6 +32,8 @@ namespace mods
fonts::clear();
}
localized_strings::clear();
db_release_xassets_hook.invoke<void>();
}
@ -40,10 +42,27 @@ namespace mods
scheduler::once([]()
{
release_assets = true;
game::Com_Shutdown("");
const auto _0 = gsl::finally([]()
{
release_assets = false;
});
game::Com_Shutdown("");
}, scheduler::pipeline::main);
}
void full_restart(const std::string& arg)
{
auto mode = game::environment::is_mp() ? " -multiplayer "s : " -singleplayer "s;
utils::nt::relaunch_self(mode.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
@ -51,11 +70,6 @@ namespace mods
public:
void post_unpack() override
{
if (!game::environment::is_sp())
{
return;
}
if (!utils::io::directory_exists("mods"))
{
utils::io::create_directory("mods");
@ -86,10 +100,21 @@ namespace mods
}
console::info("Loading mod %s\n", path);
filesystem::get_search_paths().erase(mod_path);
filesystem::get_search_paths().insert(path);
if (mod_requires_restart(mod_path) || mod_requires_restart(path))
{
// vid_restart is still broken :(
// TODO: above was fed's comment for H2, can we actually use it just fine?
console::info("Restarting...\n");
full_restart("+set fs_game \""s + path + "\"");
}
else
{
filesystem::unregister_path(mod_path);
filesystem::register_path(path);
mod_path = path;
restart();
}
});
command::add("unloadmod", [](const command::params& params)
@ -108,8 +133,27 @@ namespace mods
}
console::info("Unloading mod %s\n", mod_path.data());
filesystem::get_search_paths().erase(mod_path);
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", []()
{
if (!game::Com_InFrontend())
{
return;
}
restart();
});
}

View File

@ -84,10 +84,10 @@ namespace patches
file_name.append(".cfg");
}
const auto file = filesystem::file(file_name);
if (file.exists())
std::string buffer{};
if (filesystem::read_file(file_name, &buffer))
{
snprintf(buf, size, "%s\n", file.get_buffer().data());
snprintf(buf, size, "%s\n", buffer.data());
return buf;
}

View File

@ -1421,6 +1421,12 @@ namespace game
const char* name;
};
struct LocalizeEntry
{
const char* value;
const char* name;
};
union XAssetHeader
{
void* data;
@ -1434,6 +1440,7 @@ namespace game
TTF* ttf;
XModel* model;
WeaponDef* weapon;
LocalizeEntry* localize;
};
struct XAsset

View File

@ -212,6 +212,9 @@ namespace game
WEAK symbol<bool(const char* path)> Sys_FileExists{0x0, 0x0};
WEAK symbol<HANDLE(Sys_Folder, const char* baseFilename)> Sys_CreateFile{0x42C430, 0x5B2860};
WEAK symbol<const char*()> SEH_GetCurrentLanguageCode{0x3E5FB0, 0x585090};
WEAK symbol<const char*()> SEH_GetCurrentLanguageName{0x3E6030, 0x5850F0};
WEAK symbol<const char*(const char*)> UI_GetMapDisplayName{0x0, 0x4DDEE0};
WEAK symbol<const char*(const char*)> UI_GetGameTypeDisplayName{0x0, 0x4DD8C0};
WEAK symbol<void(unsigned int localClientNum, const char** args)> UI_RunMenuScript{0x3F3AA0, 0x1E35B0};

View File

@ -202,6 +202,7 @@ void limit_parallel_dll_loading()
int main()
{
ShowWindow(GetConsoleWindow(), SW_HIDE);
ShowWindow(GetConsoleWindow(), SW_SHOW);
FARPROC entry_point;

View File

@ -85,6 +85,9 @@
#include <MinHook.h>
#include <tomcrypt.h>
#define RAPIDJSON_NOEXCEPT
#define RAPIDJSON_ASSERT(cond) if(cond); else throw std::runtime_error("rapidjson assert fail");
#include <rapidjson/document.h>
#include <rapidjson/prettywriter.h>
#include <rapidjson/stringbuffer.h>

View File

@ -225,7 +225,7 @@ namespace utils::nt
return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, res));
}
void relaunch_self()
void relaunch_self(const std::string& extra_command_line, bool override_command_line)
{
const utils::nt::library self;
@ -238,9 +238,21 @@ namespace utils::nt
char current_dir[MAX_PATH];
GetCurrentDirectoryA(sizeof(current_dir), current_dir);
auto* const command_line = GetCommandLineA();
CreateProcessA(self.get_path().data(), command_line, nullptr, nullptr, false, NULL, nullptr, current_dir,
std::string command_line = GetCommandLineA();
if (!extra_command_line.empty())
{
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,
&startup_info, &process_info);
if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE) CloseHandle(process_info.hThread);

View File

@ -105,6 +105,6 @@ namespace utils::nt
__declspec(noreturn) void raise_hard_exception();
std::string load_resource(int id);
void relaunch_self();
void relaunch_self(const std::string& extra_command_line = "", bool override_command_line = false);
__declspec(noreturn) void terminate(uint32_t code = 0);
}