some h2 mod stuff i'm storing here temp
This commit is contained in:
parent
a93414f472
commit
9a39f92c26
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
static std::deque<std::filesystem::path> search_paths{};
|
||||
return search_paths;
|
||||
}
|
||||
}
|
||||
|
||||
bool file::exists() const
|
||||
{
|
||||
return this->valid_;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
const std::string& file::get_buffer() const
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
void fs_startup_stub(const char* name)
|
||||
{
|
||||
console::info("[FS] Startup\n");
|
||||
|
||||
const std::string& file::get_name() const
|
||||
{
|
||||
return this->name_;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
std::unordered_set<std::string>& get_search_paths()
|
||||
{
|
||||
static std::unordered_set<std::string> search_paths{};
|
||||
return search_paths;
|
||||
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);
|
||||
@ -90,4 +228,4 @@ namespace filesystem
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(filesystem::component)
|
||||
REGISTER_COMPONENT(filesystem::component)
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
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)
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
@ -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;
|
||||
const auto _0 = gsl::finally([]()
|
||||
{
|
||||
release_assets = false;
|
||||
});
|
||||
|
||||
game::Com_Shutdown("");
|
||||
release_assets = false;
|
||||
}, 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);
|
||||
mod_path = path;
|
||||
restart();
|
||||
|
||||
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);
|
||||
mod_path.clear();
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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};
|
||||
|
@ -202,6 +202,7 @@ void limit_parallel_dll_loading()
|
||||
int main()
|
||||
{
|
||||
ShowWindow(GetConsoleWindow(), SW_HIDE);
|
||||
ShowWindow(GetConsoleWindow(), SW_SHOW);
|
||||
|
||||
FARPROC entry_point;
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user