Merge branch 'master' of https://git.alterware.dev/AlterWare/iw4-sp
This commit is contained in:
parent
6125b85f72
commit
2b6ab07460
2
.gitignore
vendored
2
.gitignore
vendored
@ -53,7 +53,7 @@ Temporary Items
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Ignore everything in the build directory
|
||||
# Build results
|
||||
build
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
|
24
.gitmodules
vendored
Normal file
24
.gitmodules
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
[submodule "deps/discord-rpc"]
|
||||
path = deps/discord-rpc
|
||||
url = https://github.com/discord/discord-rpc.git
|
||||
[submodule "deps/GSL"]
|
||||
path = deps/GSL
|
||||
url = https://github.com/microsoft/GSL.git
|
||||
[submodule "deps/libtomcrypt"]
|
||||
path = deps/libtomcrypt
|
||||
url = https://github.com/libtom/libtomcrypt.git
|
||||
[submodule "deps/libtommath"]
|
||||
path = deps/libtommath
|
||||
url = https://github.com/libtom/libtommath.git
|
||||
[submodule "deps/minhook"]
|
||||
path = deps/minhook
|
||||
url = https://github.com/TsudaKageyu/minhook.git
|
||||
[submodule "deps/rapidjson"]
|
||||
path = deps/rapidjson
|
||||
url = https://github.com/Tencent/rapidjson
|
||||
[submodule "deps/udis86"]
|
||||
path = deps/udis86
|
||||
url = https://github.com/vmt/udis86.git
|
||||
[submodule "deps/zlib"]
|
||||
path = deps/zlib
|
||||
url = https://github.com/madler/zlib.git
|
15
README.md
15
README.md
@ -1,5 +1,3 @@
|
||||
[![website](https://img.shields.io/badge/Repackers-_Website-blue)](https://rimmyscorner.com/)
|
||||
|
||||
# IW4: SP Client
|
||||
|
||||
This is a client modification for IW4 (Singleplayer) 159
|
||||
@ -7,8 +5,10 @@ This is a client modification for IW4 (Singleplayer) 159
|
||||
|
||||
## How to compile
|
||||
|
||||
This project requires [Git](https://git-scm.com), [Premake5](https://premake.github.io), and [MSVC](https://visualstudio.microsoft.com/vs/features/cplusplus) to build.
|
||||
|
||||
- Run `premake5 vs2022` or use the delivered `generate.bat`.
|
||||
- Build via solution file in `build\iw4-sp.sln`.
|
||||
- Build via solution file found in `build\iw4-sp.sln`.
|
||||
|
||||
## Premake arguments
|
||||
|
||||
@ -16,11 +16,12 @@ This is a client modification for IW4 (Singleplayer) 159
|
||||
|:----------------------------|:-----------------------------------------------|
|
||||
| `--copy-to=PATH` | Optional, copy the exe to a custom folder after build. |
|
||||
|
||||
## Command line arguments
|
||||
## Contributing
|
||||
|
||||
| Argument | Description |
|
||||
|:------------------------|:-----------------------------------------------|
|
||||
| `-nosteam` | Disable Steam integration. |
|
||||
Contributions are welcome! Please follow the guidelines below:
|
||||
|
||||
- Sign [AlterWare CLA](https://alterware.dev/cla) and send a pull request or email your patch at patches@alterware.dev
|
||||
- Make sure that PRs have only one commit, and deal with one issue only
|
||||
|
||||
## Disclaimer
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
gitVersioningCommand = "git describe --tags --always"
|
||||
gitVersioningCommand = "git describe --tags --dirty --always"
|
||||
gitCurrentBranchCommand = "git symbolic-ref -q --short HEAD"
|
||||
|
||||
-- Quote the given string input as a C string
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "localize_entry.hpp"
|
||||
#include "map_ents.hpp"
|
||||
#include "raw_file.hpp"
|
||||
#include "string_table.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
@ -13,13 +14,16 @@ void load_asset(game::XAssetType type, game::XAssetHeader* header) {
|
||||
if (header) {
|
||||
switch (type) {
|
||||
case game::ASSET_TYPE_LOCALIZE_ENTRY:
|
||||
process_localize_entry(*header);
|
||||
process_localize_entry(header);
|
||||
break;
|
||||
case game::ASSET_TYPE_MAP_ENTS:
|
||||
process_map_ents(*header);
|
||||
process_map_ents(header);
|
||||
break;
|
||||
case game::ASSET_TYPE_RAWFILE:
|
||||
process_raw_file(*header);
|
||||
process_raw_file(header);
|
||||
break;
|
||||
case game::ASSET_TYPE_STRINGTABLE:
|
||||
process_string_table(header);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -11,12 +11,12 @@ namespace {
|
||||
bool is_enabled() { IS_FLAG_ENABLED(dump_localize_entry); }
|
||||
} // namespace
|
||||
|
||||
void process_localize_entry(game::XAssetHeader header) {
|
||||
void process_localize_entry(game::XAssetHeader* header) {
|
||||
if (!is_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* localize = header.localize;
|
||||
auto* localize = header->localize;
|
||||
const auto filename =
|
||||
utils::string::va("raw/localizedstrings/{0}", localize->name);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace assets {
|
||||
void process_localize_entry(game::XAssetHeader header);
|
||||
void process_localize_entry(game::XAssetHeader* header);
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ void load_map_entities(game::MapEnts* entry) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void process_map_ents(game::XAssetHeader header) {
|
||||
auto* map_ents = header.mapEnts;
|
||||
void process_map_ents(game::XAssetHeader* header) {
|
||||
auto* map_ents = header->mapEnts;
|
||||
load_map_entities(map_ents);
|
||||
}
|
||||
} // namespace assets
|
||||
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace assets {
|
||||
void process_map_ents(game::XAssetHeader header);
|
||||
void process_map_ents(game::XAssetHeader* header);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ char* db_read_raw_file_stub(const char* filename, char* buf, int size) {
|
||||
auto file_handle = 0;
|
||||
const auto file_size = game::FS_FOpenFileRead(filename, &file_handle);
|
||||
|
||||
if (file_handle != 0) {
|
||||
if (file_handle) {
|
||||
if ((file_size + 1) <= size) {
|
||||
game::FS_Read(buf, file_size, file_handle);
|
||||
buf[file_size] = '\0';
|
||||
@ -78,7 +78,7 @@ const char* com_load_info_string_load_obj(const char* file_name,
|
||||
game::FS_FOpenFileByMode(file_name, &file_handle, game::FS_READ);
|
||||
if (file_len < 0) {
|
||||
game::Com_DPrintf(game::CON_CHANNEL_SYSTEM,
|
||||
"Could not load %s [%s] as rawfile", file_desc,
|
||||
"Could not load %s [%s] as rawfile\n", file_desc,
|
||||
file_name);
|
||||
return nullptr;
|
||||
}
|
||||
@ -134,12 +134,12 @@ const char* com_load_info_string_stub(const char* file_name,
|
||||
bool is_enabled() { IS_FLAG_ENABLED(dump_raw_file); }
|
||||
} // namespace
|
||||
|
||||
void process_raw_file(game::XAssetHeader header) {
|
||||
void process_raw_file(game::XAssetHeader* header) {
|
||||
if (!is_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* raw_file = header.rawfile;
|
||||
const auto* raw_file = header->rawfile;
|
||||
const auto filename = utils::string::va("raw/{0}", raw_file->name);
|
||||
|
||||
if (raw_file->compressedLen > 0) {
|
||||
@ -147,7 +147,7 @@ void process_raw_file(game::XAssetHeader header) {
|
||||
uncompressed.resize(raw_file->len);
|
||||
|
||||
if (uncompress(uncompressed.data(), (uLongf*)&raw_file->len,
|
||||
(const Bytef*)raw_file->buffer,
|
||||
reinterpret_cast<const Bytef*>(raw_file->buffer),
|
||||
raw_file->compressedLen) == Z_OK) {
|
||||
std::string data;
|
||||
data.assign(uncompressed.begin(), uncompressed.end());
|
||||
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace assets {
|
||||
void process_raw_file(game::XAssetHeader header);
|
||||
void process_raw_file(game::XAssetHeader* header);
|
||||
}
|
||||
|
46
src/client/component/assets/string_table.cpp
Normal file
46
src/client/component/assets/string_table.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "string_table.hpp"
|
||||
|
||||
#include <utils/flags.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace assets {
|
||||
namespace {
|
||||
void dump_string_table(game::XAssetHeader* header) {
|
||||
const auto* string_table = header->stringTable;
|
||||
const auto filename = utils::string::va("raw/{0}", string_table->name);
|
||||
|
||||
std::string csv;
|
||||
|
||||
const auto rows = string_table->rowCount;
|
||||
const auto columns = string_table->columnCount;
|
||||
|
||||
for (auto x = 0; x < rows; ++x) {
|
||||
for (auto y = 0; y < columns; ++y) {
|
||||
const char* cell = string_table->values[(x * columns) + y].string;
|
||||
csv += cell;
|
||||
|
||||
if (y + 1 < columns) {
|
||||
csv += ",";
|
||||
}
|
||||
}
|
||||
|
||||
if (x + 1 < rows) {
|
||||
csv += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
utils::io::write_file(filename, csv);
|
||||
}
|
||||
|
||||
bool is_enabled() { IS_FLAG_ENABLED(dump_string_table); }
|
||||
} // namespace
|
||||
|
||||
void process_string_table(game::XAssetHeader* header) {
|
||||
if (is_enabled()) {
|
||||
dump_string_table(header);
|
||||
}
|
||||
}
|
||||
} // namespace assets
|
5
src/client/component/assets/string_table.hpp
Normal file
5
src/client/component/assets/string_table.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace assets {
|
||||
void process_string_table(game::XAssetHeader* header);
|
||||
}
|
@ -94,7 +94,7 @@ game::define_s* copy_define([[maybe_unused]] game::source_s* source,
|
||||
game::token_s *token, *newtoken, *lasttoken;
|
||||
|
||||
auto* newdefine = static_cast<game::define_s*>(
|
||||
game::GetMemory(sizeof(game::define_s) + strlen(define->name) + 1));
|
||||
game::GetMemory(sizeof(game::define_s) + std::strlen(define->name) + 1));
|
||||
|
||||
// copy the define name
|
||||
newdefine->name = (char*)newdefine + sizeof(game::define_s);
|
||||
|
@ -153,7 +153,7 @@ game::script_s* load_script_file(const char* filename) {
|
||||
// pointer to end of script buffer
|
||||
script->end_p = &script->buffer[length];
|
||||
// set if there's a token available in script->token
|
||||
script->tokenavailable = 0;
|
||||
script->tokenavailable = false;
|
||||
|
||||
script->line = 1;
|
||||
script->lastline = 1;
|
||||
@ -183,7 +183,7 @@ game::script_s* load_script_memory(const char* ptr, int length,
|
||||
// pointer to end of script buffer
|
||||
script->end_p = &script->buffer[length];
|
||||
// set if there's a token available in script->token
|
||||
script->tokenavailable = 0;
|
||||
script->tokenavailable = false;
|
||||
|
||||
script->line = 1;
|
||||
script->lastline = 1;
|
||||
|
@ -98,7 +98,7 @@ public:
|
||||
|
||||
register_branding_dvars();
|
||||
utils::hook(0x57DAFF, cg_draw_full_screen_debug_overlays_stub, HOOK_CALL)
|
||||
.install()
|
||||
.install() // hook*
|
||||
->quick();
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ public:
|
||||
utils::hook::set<std::uint32_t>(0x47C2E0, 0xC301B0);
|
||||
utils::hook::set<std::uint32_t>(0x4EEA90, 0xC301B0);
|
||||
utils::hook::set<std::uint32_t>(0x40E380, 0xC301B0);
|
||||
utils::hook::set<std::uint32_t>(0x4E45C0, 0xC301B0);
|
||||
|
||||
// Killer caller
|
||||
utils::hook::set<std::uint8_t>(0x43F320, 0xC3);
|
||||
|
@ -119,7 +119,7 @@ public:
|
||||
|
||||
#ifdef _DEBUG
|
||||
utils::hook(0x4C79DF, g_init_game_stub, HOOK_CALL)
|
||||
.install()
|
||||
.install() // hook*
|
||||
->quick(); // Scr_FreeEntityList
|
||||
#endif
|
||||
}
|
||||
|
@ -61,7 +61,6 @@ private:
|
||||
static void ready(const DiscordUser* request) {
|
||||
ZeroMemory(&discord_presence, sizeof(discord_presence));
|
||||
|
||||
discord_presence.state = "Singleplayer";
|
||||
discord_presence.instance = 1;
|
||||
discord_presence.startTimestamp = 0;
|
||||
printf("Discord: Ready\n");
|
||||
@ -69,7 +68,7 @@ private:
|
||||
}
|
||||
|
||||
static void errored(const int error_code, const char* message) {
|
||||
printf("Discord: (%i) %s", error_code, message);
|
||||
printf("Discord: (%i) %s\n", error_code, message);
|
||||
}
|
||||
};
|
||||
} // namespace discord
|
||||
|
@ -19,9 +19,9 @@ const game::dvar_t* dvar_register_name(const char* dvar_name, const char* value,
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_start() override {
|
||||
dvar::override::register_bool("intro", true, game::DVAR_NONE);
|
||||
dvar::override::register_bool("intro", false, game::DVAR_ARCHIVE);
|
||||
dvar::override::register_float("cg_fov", 65.0f, 65.0f, 160.0f,
|
||||
game::DVAR_ARCHIVE);
|
||||
game::DVAR_ARCHIVE | game::DVAR_SAVED);
|
||||
dvar::override::register_string("fs_basegame", BASEGAME, game::DVAR_INIT);
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
1206
src/client/component/gsc/error.cpp
Normal file
1206
src/client/component/gsc/error.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -69,9 +69,6 @@ private:
|
||||
// Build os path stuff
|
||||
utils::hook::set<std::uint8_t>(0x6300BF, 0xEB);
|
||||
|
||||
// Show intro (or not)
|
||||
utils::hook::set<std::uint8_t>(0x6035BD, 0x0);
|
||||
|
||||
// raw -> main
|
||||
utils::hook::set<std::uint32_t>(0x50A0B2, 0x723390);
|
||||
|
||||
|
@ -109,6 +109,59 @@ void pm_player_trace_stub(game::pmove_t* pm, game::trace_t* results,
|
||||
results->startsolid = false;
|
||||
}
|
||||
}
|
||||
|
||||
void pm_crash_land_stub(const float* v, float scale, const float* result) {
|
||||
if (!dvars::pm_disableLandingSlowdown->current.enabled) {
|
||||
game::Vec3Scale(v, scale, result);
|
||||
}
|
||||
}
|
||||
|
||||
void __declspec(naked) jump_check_stub() {
|
||||
__asm {
|
||||
push eax;
|
||||
mov eax, dvars::pm_bunnyHop;
|
||||
cmp byte ptr [eax + 0x10], 1;
|
||||
pop eax;
|
||||
|
||||
je auto_hop;
|
||||
|
||||
// Game's code
|
||||
test dword ptr [ebx + 0x48], 0x400;
|
||||
push 0x4D25EF;
|
||||
ret;
|
||||
|
||||
auto_hop:
|
||||
push 0x4D25FE;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
|
||||
void __declspec(naked) p_move_single_stub() {
|
||||
__asm {
|
||||
push eax;
|
||||
mov eax, dvars::pm_snapVector;
|
||||
cmp byte ptr [eax + 0x10], 1;
|
||||
pop eax;
|
||||
|
||||
jnz skip;
|
||||
|
||||
lea ebp, [ebp + 0x28]; // velocity
|
||||
push ebp;
|
||||
call game::Sys_SnapVector;
|
||||
add esp, 0x4;
|
||||
|
||||
skip:
|
||||
|
||||
// Game's code
|
||||
pop edi;
|
||||
pop esi;
|
||||
pop ebp;
|
||||
pop ebx;
|
||||
add esp, 0x90;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
|
||||
void g_scr_is_sprinting(const game::scr_entref_t entref) {
|
||||
const auto* client = game::GetEntity(entref)->client;
|
||||
if (!client) {
|
||||
@ -154,6 +207,13 @@ public:
|
||||
.install()
|
||||
->quick(); // PM_CorrectAllSolid
|
||||
|
||||
utils::hook(0x64E571, pm_crash_land_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick(); // Vec3Scale
|
||||
utils::hook(0x4D25E8, jump_check_stub, HOOK_JUMP).install()->quick();
|
||||
|
||||
utils::hook(0x6530C3, p_move_single_stub, HOOK_JUMP).install()->quick();
|
||||
|
||||
gsc::add_method("IsSprinting", g_scr_is_sprinting);
|
||||
register_dvars();
|
||||
}
|
||||
@ -172,6 +232,11 @@ public:
|
||||
"pm_playerCollision", true, game::DVAR_NONE, "Push intersecting players away from each other");
|
||||
dvars::pm_elevators = game::Dvar_RegisterBool(
|
||||
"pm_elevators", false, game::DVAR_NONE, "CoD4 elevators");
|
||||
dvars::pm_disableLandingSlowdown = game::Dvar_RegisterBool("pm_disableLandingSlowdown",
|
||||
false, game::DVAR_NONE, "Toggle landing slowdown");
|
||||
dvars::pm_bunnyHop = game::Dvar_RegisterBool("pm_bunnyHop",
|
||||
false, game::DVAR_NONE, "Constantly jump when holding space");
|
||||
dvars::pm_snapVector = game::Dvar_RegisterBool("pm_snapVector", false, game::DVAR_NONE, "Snap velocity");
|
||||
// clang-format on
|
||||
}
|
||||
};
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <utils/binary_resource.hpp>
|
||||
#include <utils/flags.hpp>
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
@ -14,28 +13,12 @@
|
||||
#include "scheduler.hpp"
|
||||
|
||||
namespace {
|
||||
enum class ownership_state {
|
||||
success,
|
||||
unowned,
|
||||
nosteam,
|
||||
error,
|
||||
};
|
||||
|
||||
utils::binary_resource runner_file(RUNNER, "iw4sp-runner.exe");
|
||||
|
||||
bool is_disabled() {
|
||||
static const auto disabled = utils::flags::has_flag("nosteam");
|
||||
return disabled;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class steam_proxy final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
if (is_disabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->load_client();
|
||||
this->clean_up_on_error();
|
||||
|
||||
@ -63,9 +46,9 @@ public:
|
||||
private:
|
||||
utils::nt::library steam_client_module_{};
|
||||
|
||||
steam::interface client_engine_ {};
|
||||
steam::interface client_user_ {};
|
||||
steam::interface client_utils_ {};
|
||||
steam::interface client_engine_{};
|
||||
steam::interface client_user_{};
|
||||
steam::interface client_utils_{};
|
||||
|
||||
void* steam_pipe_ = nullptr;
|
||||
void* global_user_ = nullptr;
|
||||
@ -113,36 +96,25 @@ private:
|
||||
14, this->steam_pipe_); // GetIClientUtils
|
||||
}
|
||||
|
||||
ownership_state start_mod(const std::string& title,
|
||||
const std::size_t app_id) {
|
||||
void start_mod(const std::string& title, const std::size_t app_id) {
|
||||
__try {
|
||||
return this->start_mod_unsafe(title, app_id);
|
||||
this->start_mod_unsafe(title, app_id);
|
||||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
this->do_cleanup();
|
||||
return ownership_state::error;
|
||||
}
|
||||
}
|
||||
|
||||
ownership_state start_mod_unsafe(const std::string& title,
|
||||
std::size_t app_id) {
|
||||
void start_mod_unsafe(const std::string& title, std::size_t app_id) {
|
||||
if (!this->client_utils_ || !this->client_user_)
|
||||
return ownership_state::nosteam;
|
||||
return;
|
||||
|
||||
if (!this->client_user_.invoke<bool>("BIsSubscribedApp", app_id)) {
|
||||
#ifdef _DEBUG
|
||||
app_id = 480; // Spacewar
|
||||
#else
|
||||
return ownership_state::unowned;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (is_disabled()) {
|
||||
return ownership_state::success;
|
||||
}
|
||||
|
||||
this->client_utils_.invoke<void>("SetAppIDForCurrentPipe", app_id, false);
|
||||
|
||||
char our_directory[MAX_PATH] = {0};
|
||||
char our_directory[MAX_PATH]{};
|
||||
GetCurrentDirectoryA(sizeof(our_directory), our_directory);
|
||||
|
||||
const auto path = runner_file.get_extracted_file();
|
||||
@ -160,8 +132,6 @@ private:
|
||||
this->client_user_.invoke<bool>("SpawnProcess", path.data(), cmd_line,
|
||||
our_directory, game_id.bits, title.data(),
|
||||
app_id, 0, 0, 0);
|
||||
|
||||
return ownership_state::success;
|
||||
}
|
||||
|
||||
void do_cleanup() {
|
||||
|
629
src/client/component/ui.cpp
Normal file
629
src/client/component/ui.cpp
Normal file
@ -0,0 +1,629 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "botlib/l_precomp.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace ui {
|
||||
namespace {
|
||||
#define ITEM_TYPE_LISTBOX 6 // scrollable list
|
||||
#define ITEM_TYPE_MULTI 12 // multiple list setting, enumerated
|
||||
|
||||
#define PARSE_FLOAT_TOKEN(name) \
|
||||
if (!game::I_stricmp(token.string, #name)) { \
|
||||
if (!game::PC_Float_Parse(handle, &g_load.loadAssets.##name##)) { \
|
||||
return false; \
|
||||
} \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define PARSE_INT_TOKEN(name) \
|
||||
if (!game::I_stricmp(token.string, #name)) { \
|
||||
if (!game::PC_Int_Parse(handle, &g_load.loadAssets.##name##)) { \
|
||||
return false; \
|
||||
} \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define FREE_STATEMENT(statement) \
|
||||
{ \
|
||||
if ((statement)) { \
|
||||
menu_free_expression_supporting_data((statement)->supportingData); \
|
||||
} \
|
||||
game::free_expression((statement)); \
|
||||
}
|
||||
|
||||
template <typename T, int N, int M> struct KeywordHashEntry {
|
||||
const char* keyword;
|
||||
int (*func)(T*, int);
|
||||
};
|
||||
|
||||
KeywordHashEntry<game::menuDef_t, 128, 3523>** menu_parse_keyword_hash;
|
||||
|
||||
std::vector<game::menuDef_t*> loaded_menus_list;
|
||||
|
||||
struct {
|
||||
game::loadAssets_t loadAssets;
|
||||
game::MenuList menuList;
|
||||
game::itemDef_s* items[512];
|
||||
game::menuDef_t* menus[512];
|
||||
} g_load;
|
||||
|
||||
char menu_buf[0x8000];
|
||||
|
||||
int asset_parse(int handle) {
|
||||
game::pc_token_s token{};
|
||||
|
||||
if (!game::PC_ReadTokenHandle(handle, &token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (game::I_stricmp(token.string, "{") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (!game::PC_ReadTokenHandle(handle, &token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!game::I_stricmp(token.string, ";")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!game::I_stricmp(token.string, "}")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PARSE_FLOAT_TOKEN(fadeClamp);
|
||||
PARSE_INT_TOKEN(fadeCycle);
|
||||
PARSE_FLOAT_TOKEN(fadeAmount);
|
||||
PARSE_FLOAT_TOKEN(fadeInAmount);
|
||||
|
||||
game::PC_SourceError(
|
||||
handle,
|
||||
"Unknown token %s in assetGlobalDef. Valid commands are 'fadeClamp', "
|
||||
"'fadeCycle', 'fadeAmount', and 'fadeInAmount'\n",
|
||||
token.string);
|
||||
}
|
||||
}
|
||||
|
||||
void* alloc(int size, int alignment) {
|
||||
return game::Hunk_AllocAlignInternal(size, alignment);
|
||||
}
|
||||
|
||||
template <int HASH_COUNT, int HASH_SEED>
|
||||
int keyword_hash_key(const char* keyword) {
|
||||
auto hash = 0;
|
||||
for (auto i = 0; keyword[i]; ++i) {
|
||||
hash +=
|
||||
(i + HASH_SEED) * std::tolower(static_cast<unsigned char>(keyword[i]));
|
||||
}
|
||||
return (hash + (hash >> 8)) & (128 - 1);
|
||||
}
|
||||
|
||||
template <typename T, int N, int M>
|
||||
KeywordHashEntry<T, N, M>* keyword_hash_find(KeywordHashEntry<T, N, M>** table,
|
||||
const char* keyword) {
|
||||
auto hash = keyword_hash_key<N, M>(keyword);
|
||||
KeywordHashEntry<T, N, M>* key = table[hash];
|
||||
if (key && !game::I_stricmp(key->keyword, keyword)) {
|
||||
return key;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int menu_parse(int handle, game::menuDef_t* menu) {
|
||||
game::pc_token_s token{};
|
||||
|
||||
if (!game::PC_ReadTokenHandle(handle, &token))
|
||||
return false;
|
||||
|
||||
if (*token.string != '{')
|
||||
return false;
|
||||
|
||||
while (true) {
|
||||
std::memset(&token, 0, sizeof(game::pc_token_s));
|
||||
|
||||
if (!game::PC_ReadTokenHandle(handle, &token)) {
|
||||
game::PC_SourceError(handle, "end of file inside menu\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*token.string == '}') {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* key = keyword_hash_find(menu_parse_keyword_hash, token.string);
|
||||
if (!key) {
|
||||
game::PC_SourceError(handle, "unknown menu keyword %s", token.string);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!key->func(menu, handle)) {
|
||||
game::PC_SourceError(handle, "couldn't parse menu keyword %s",
|
||||
token.string);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void item_set_screen_coords([[maybe_unused]] int context_index,
|
||||
game::itemDef_s* item, float x, float y,
|
||||
int horz_align, int vert_align) {
|
||||
assert(item);
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->window.border) {
|
||||
x += item->window.borderSize;
|
||||
y += item->window.borderSize;
|
||||
}
|
||||
|
||||
item->window.rect = item->window.rectClient;
|
||||
item->window.rect.x = item->window.rect.x + x;
|
||||
item->window.rect.y = item->window.rect.y + y;
|
||||
|
||||
if (!item->window.rect.horzAlign && !item->window.rect.vertAlign) {
|
||||
item->window.rect.horzAlign = static_cast<char>(horz_align);
|
||||
item->window.rect.vertAlign = static_cast<char>(vert_align);
|
||||
}
|
||||
}
|
||||
|
||||
game::rectDef_s* window_get_rect(game::windowDef_t* w) {
|
||||
assert(w);
|
||||
return &w->rect;
|
||||
}
|
||||
|
||||
void menu_update_position(int context_index, game::menuDef_t* menu) {
|
||||
if (menu == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* rect = window_get_rect(&menu->window);
|
||||
float x = rect->x;
|
||||
float y = rect->y;
|
||||
if (menu->window.border) {
|
||||
x += menu->window.borderSize;
|
||||
y += menu->window.borderSize;
|
||||
}
|
||||
|
||||
for (int i = 0; i < menu->itemCount; ++i) {
|
||||
item_set_screen_coords(context_index, menu->items[i], x, y, rect->horzAlign,
|
||||
rect->vertAlign);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_post_parse(game::menuDef_t* menu) {
|
||||
assert(menu);
|
||||
|
||||
const auto item_count = 4 * menu->itemCount;
|
||||
menu->items = static_cast<game::itemDef_s**>(alloc(item_count, 4));
|
||||
std::memcpy(menu->items, g_load.items, item_count);
|
||||
|
||||
if (menu->fullScreen) {
|
||||
menu->window.rect.x = 0.0f;
|
||||
menu->window.rect.y = 0.0f;
|
||||
menu->window.rect.w = 640.0f;
|
||||
menu->window.rect.h = 480.0f;
|
||||
}
|
||||
|
||||
menu_update_position(0, menu);
|
||||
}
|
||||
|
||||
void menu_set_cursor_item(int context_index, game::menuDef_t* menu,
|
||||
int cursor_item) {
|
||||
assert(context_index < game::MAX_POSSIBLE_LOCAL_CLIENTS);
|
||||
assert(menu);
|
||||
menu->cursorItem[context_index] = cursor_item;
|
||||
}
|
||||
|
||||
void window_init(game::windowDef_t* w) {
|
||||
std::memset(w, 0, sizeof(game::windowDef_t));
|
||||
w->borderSize = 1.0f;
|
||||
w->foreColor[0] = w->foreColor[1] = w->foreColor[2] = w->foreColor[3] = 1.0f;
|
||||
}
|
||||
|
||||
void menu_init(game::menuDef_t* menu) {
|
||||
std::memset(menu, 0, sizeof(game::menuDef_t));
|
||||
menu_set_cursor_item(0, menu, -1);
|
||||
|
||||
menu->fadeAmount = g_load.loadAssets.fadeAmount;
|
||||
menu->fadeInAmount = g_load.loadAssets.fadeInAmount;
|
||||
menu->fadeClamp = g_load.loadAssets.fadeClamp;
|
||||
menu->fadeCycle = g_load.loadAssets.fadeCycle;
|
||||
menu->items = g_load.items;
|
||||
|
||||
window_init(&menu->window);
|
||||
}
|
||||
|
||||
void menu_free_expression_supporting_data(
|
||||
game::ExpressionSupportingData* data) {
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < data->uifunctions.totalFunctions; ++i) {
|
||||
auto* function = data->uifunctions.functions[i];
|
||||
FREE_STATEMENT(function);
|
||||
}
|
||||
|
||||
data->uifunctions.totalFunctions = 0;
|
||||
}
|
||||
|
||||
void menu_free_event_handler(game::MenuEventHandlerSet* event_handler) {
|
||||
if (!event_handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < event_handler->eventHandlerCount; ++i) {
|
||||
auto* event = event_handler->eventHandlers[i];
|
||||
|
||||
game::ConditionalScript* conditional_script;
|
||||
game::MenuEventHandlerSet* else_script;
|
||||
game::SetLocalVarData* local_var;
|
||||
|
||||
switch (event->eventType) {
|
||||
case game::EVENT_IF:
|
||||
conditional_script = event->eventData.conditionalScript;
|
||||
menu_free_event_handler(conditional_script->eventHandlerSet);
|
||||
FREE_STATEMENT(conditional_script->eventExpression);
|
||||
break;
|
||||
case game::EVENT_ELSE:
|
||||
else_script = event->eventData.elseScript;
|
||||
menu_free_event_handler(else_script);
|
||||
break;
|
||||
case game::EVENT_SET_LOCAL_VAR_BOOL:
|
||||
case game::EVENT_SET_LOCAL_VAR_INT:
|
||||
case game::EVENT_SET_LOCAL_VAR_FLOAT:
|
||||
case game::EVENT_SET_LOCAL_VAR_STRING:
|
||||
local_var = event->eventData.setLocalVarData;
|
||||
FREE_STATEMENT(local_var->expression);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
event_handler->eventHandlerCount = 0;
|
||||
}
|
||||
|
||||
void menu_free_item_key_handler(const game::ItemKeyHandler* key_handler) {
|
||||
while (key_handler) {
|
||||
menu_free_event_handler(key_handler->action);
|
||||
key_handler = key_handler->next;
|
||||
}
|
||||
}
|
||||
|
||||
void menu_free_memory(game::menuDef_t* menu) {
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < menu->itemCount; ++i) {
|
||||
auto* item = menu->items[i];
|
||||
|
||||
FREE_STATEMENT(item->visibleExp);
|
||||
FREE_STATEMENT(item->disabledExp);
|
||||
FREE_STATEMENT(item->textExp);
|
||||
FREE_STATEMENT(item->materialExp);
|
||||
|
||||
menu_free_event_handler(item->mouseEnterText);
|
||||
menu_free_event_handler(item->mouseExitText);
|
||||
menu_free_event_handler(item->mouseEnter);
|
||||
menu_free_event_handler(item->mouseExit);
|
||||
menu_free_event_handler(item->action);
|
||||
menu_free_event_handler(item->accept);
|
||||
menu_free_event_handler(item->onFocus);
|
||||
menu_free_event_handler(item->leaveFocus);
|
||||
|
||||
menu_free_item_key_handler(item->onKey);
|
||||
|
||||
// free ItemFloatExpression*
|
||||
game::Menu_FreeItemMemory(item);
|
||||
|
||||
if (item->dataType == ITEM_TYPE_LISTBOX) {
|
||||
menu_free_event_handler(item->typeData.listBox->onDoubleClick);
|
||||
}
|
||||
}
|
||||
|
||||
menu->itemCount = 0;
|
||||
|
||||
menu_free_event_handler(menu->onOpen);
|
||||
menu_free_event_handler(menu->onCloseRequest);
|
||||
menu_free_event_handler(menu->onClose);
|
||||
menu_free_event_handler(menu->onESC);
|
||||
|
||||
menu_free_item_key_handler(menu->onKey);
|
||||
|
||||
FREE_STATEMENT(menu->visibleExp);
|
||||
FREE_STATEMENT(menu->rectXExp);
|
||||
FREE_STATEMENT(menu->rectYExp);
|
||||
FREE_STATEMENT(menu->rectWExp);
|
||||
FREE_STATEMENT(menu->rectHExp);
|
||||
FREE_STATEMENT(menu->openSoundExp);
|
||||
FREE_STATEMENT(menu->closeSoundExp);
|
||||
|
||||
// Our menu compiler code does not support parsing 'ui functions'
|
||||
if (!menu->expressionData) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < menu->expressionData->uifunctions.totalFunctions; ++i) {
|
||||
auto* function_statement = menu->expressionData->uifunctions.functions[i];
|
||||
FREE_STATEMENT(function_statement);
|
||||
}
|
||||
|
||||
menu->expressionData->uifunctions.totalFunctions = 0;
|
||||
}
|
||||
|
||||
void menus_free_all_memory(game::UiContext* dc) {
|
||||
for (auto menu = 0; menu < dc->menuCount; ++menu) {
|
||||
menu_free_memory(dc->Menus[menu]);
|
||||
}
|
||||
}
|
||||
|
||||
bool menu_new(int handle) {
|
||||
auto* menu = static_cast<game::menuDef_t*>(alloc(sizeof(game::menuDef_t), 4));
|
||||
menu_init(menu);
|
||||
|
||||
if (!menu_parse(handle, menu)) {
|
||||
menu_free_memory(menu);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!menu->window.name) {
|
||||
game::PC_SourceError(handle, "menu has no name");
|
||||
menu_free_memory(menu);
|
||||
return false;
|
||||
}
|
||||
|
||||
menu_post_parse(menu);
|
||||
if (static_cast<std::size_t>(g_load.menuList.menuCount) >=
|
||||
std::extent_v<decltype(g_load.menus)>) {
|
||||
game::Com_Error(game::ERR_DROP, "\x15"
|
||||
"Menu_New: "
|
||||
"\x14"
|
||||
"EXE_ERR_OUT_OF_MEMORY");
|
||||
}
|
||||
|
||||
g_load.menuList.menus[g_load.menuList.menuCount++] = menu;
|
||||
|
||||
loaded_menus_list.emplace_back(menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_menu_internal(const char* menu_file) {
|
||||
const char* builtin_defines[2];
|
||||
game::pc_token_s token;
|
||||
|
||||
builtin_defines[0] = "PC";
|
||||
builtin_defines[1] = nullptr;
|
||||
|
||||
game::Com_Printf(game::CON_CHANNEL_UI, "\tLoading '%s'...\n", menu_file);
|
||||
|
||||
const auto handle = pc::load_source_handle(menu_file, builtin_defines);
|
||||
if (!handle) {
|
||||
game::Com_PrintError(game::CON_CHANNEL_UI, "Couldn't find menu file '%s'\n",
|
||||
menu_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
while (game::PC_ReadTokenHandle(handle, &token)) {
|
||||
if (!game::I_stricmp(token.string, "}") ||
|
||||
!game::I_stricmp(token.string, "{")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!game::I_stricmp(token.string, "assetGlobalDef")) {
|
||||
asset_parse(handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!game::I_stricmp(token.string, "menudef")) {
|
||||
menu_new(handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
game::PC_SourceError(handle,
|
||||
"Unknown token %s in menu file. Expected \"menudef\" "
|
||||
"or \"assetglobaldef\".\n",
|
||||
token.string);
|
||||
}
|
||||
|
||||
pc::free_source_handle(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
int load_menu(const char** p) {
|
||||
if (*game::Com_Parse(p) != '{') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
const auto* token = game::Com_Parse(p);
|
||||
if (!game::I_stricmp(token, "}")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!token || !*token) {
|
||||
break;
|
||||
}
|
||||
|
||||
parse_menu_internal(token);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
game::MenuList* load_menus_load_obj(const char* menu_file) {
|
||||
std::memset(&g_load, 0, sizeof(g_load));
|
||||
g_load.menuList.menus = g_load.menus;
|
||||
|
||||
auto f = 0;
|
||||
auto len = game::FS_FOpenFileRead(menu_file, &f);
|
||||
|
||||
if (!f) {
|
||||
game::Com_PrintWarning(game::CON_CHANNEL_UI,
|
||||
"WARNING: menu file not found: %s\n", menu_file);
|
||||
#ifdef UI_LOAD_DEFAULT_MENU
|
||||
len = game::FS_FOpenFileByMode("ui/default.menu", &f, game::FS_READ);
|
||||
if (!f) {
|
||||
game::Com_Error(game::ERR_SERVERDISCONNECT,
|
||||
"\x15"
|
||||
"default.menu file not found. This is a default menu "
|
||||
"that you should have.\n");
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (static_cast<std::size_t>(len) >= sizeof(menu_buf)) {
|
||||
game::FS_FCloseFile(f);
|
||||
game::Com_Error(game::ERR_SERVERDISCONNECT,
|
||||
"\x15^1menu file too large: %s is %i, max allowed is %i",
|
||||
menu_file, len, sizeof(menu_buf));
|
||||
}
|
||||
|
||||
game::FS_Read(menu_buf, len, f);
|
||||
menu_buf[len] = '\0';
|
||||
game::FS_FCloseFile(f);
|
||||
|
||||
game::Com_Compress(menu_buf);
|
||||
|
||||
const auto* p = menu_buf;
|
||||
game::Com_BeginParseSession(menu_file);
|
||||
|
||||
for (auto* token = game::Com_Parse(&p); token; token = game::Com_Parse(&p)) {
|
||||
if (!*token || *token == '}' || !game::I_stricmp(token, "}") ||
|
||||
!game::I_stricmp(token, "loadmenu") && !load_menu(&p)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
game::Com_EndParseSession();
|
||||
return &g_load.menuList;
|
||||
}
|
||||
|
||||
game::MenuList* load_menu_load_obj(const char* menu_file) {
|
||||
std::memset(&g_load, 0, sizeof(g_load));
|
||||
g_load.menuList.menus = g_load.menus;
|
||||
|
||||
if (!parse_menu_internal(menu_file)) {
|
||||
game::Com_PrintWarning(game::CON_CHANNEL_UI,
|
||||
"WARNING: menu file not found: %s\n", menu_file);
|
||||
|
||||
#ifdef UI_LOAD_DEFAULT_MENU
|
||||
if (!parse_menu_internal("ui/default.menu")) {
|
||||
game::Com_Error(game::ERR_SERVERDISCONNECT,
|
||||
"\x15"
|
||||
"default.menu file not found. This is a default menu "
|
||||
"that you should have.\n");
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
return &g_load.menuList;
|
||||
}
|
||||
|
||||
game::MenuList* load_menus_fast_file(const char* menu_file) {
|
||||
return game::DB_FindXAssetHeader(game::ASSET_TYPE_MENULIST, menu_file)
|
||||
.menuList;
|
||||
}
|
||||
|
||||
game::MenuList* load_menus_stub(const char* menu_file) {
|
||||
auto* menu_list = load_menus_load_obj(menu_file);
|
||||
if (!menu_list) {
|
||||
menu_list = load_menus_fast_file(menu_file);
|
||||
}
|
||||
|
||||
return menu_list;
|
||||
}
|
||||
|
||||
game::MenuList* load_menu_fast_file(const char* menu_file) {
|
||||
return game::DB_FindXAssetHeader(game::ASSET_TYPE_MENULIST, menu_file)
|
||||
.menuList;
|
||||
}
|
||||
|
||||
game::MenuList* load_menu_stub(const char* menu_file) {
|
||||
auto* menu_list = load_menu_load_obj(menu_file);
|
||||
if (!menu_list) {
|
||||
menu_list = load_menu_fast_file(menu_file);
|
||||
}
|
||||
|
||||
return menu_list;
|
||||
}
|
||||
|
||||
void snd_fade_all_sounds_stub(float volume, int fade_time) {
|
||||
utils::hook::invoke<void>(0x4206A0, volume, fade_time);
|
||||
|
||||
for (auto& menu : loaded_menus_list) {
|
||||
menu_free_memory(menu);
|
||||
}
|
||||
|
||||
loaded_menus_list.clear();
|
||||
}
|
||||
|
||||
void ui_shutdown_stub() {
|
||||
for (auto& menu : loaded_menus_list) {
|
||||
menu_free_memory(menu);
|
||||
}
|
||||
|
||||
loaded_menus_list.clear();
|
||||
|
||||
utils::hook::invoke<void>(0x4CBD70);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
static_assert(offsetof(game::UiContext, menuCount) == 0xA38);
|
||||
static_assert(offsetof(game::UiContext, Menus) == 0x38);
|
||||
|
||||
static_assert(offsetof(game::menuDef_t, itemCount) == 0xAC);
|
||||
|
||||
static_assert(offsetof(game::itemDef_s, dataType) == 0xBC);
|
||||
static_assert(offsetof(game::itemDef_s, typeData) == 0x134);
|
||||
static_assert(offsetof(game::itemDef_s, floatExpressionCount) == 0x13C);
|
||||
static_assert(offsetof(game::itemDef_s, floatExpressions) == 0x140);
|
||||
|
||||
void post_load() override {
|
||||
menu_parse_keyword_hash =
|
||||
reinterpret_cast<KeywordHashEntry<game::menuDef_t, 128, 3523>**>(
|
||||
0x1933DB0);
|
||||
|
||||
patch_sp();
|
||||
}
|
||||
|
||||
void pre_destroy() override { assert(loaded_menus_list.empty()); }
|
||||
|
||||
private:
|
||||
static void patch_sp() {
|
||||
utils::hook(0x62DDD0, load_menus_stub, HOOK_JUMP).install()->quick();
|
||||
utils::hook::nop(0x62DDD0 + 5, 3);
|
||||
|
||||
utils::hook(0x621194, load_menu_stub, HOOK_CALL).install()->quick();
|
||||
|
||||
// Remove the menus from memory (CG_Shutdown)
|
||||
utils::hook(0x447905, snd_fade_all_sounds_stub, HOOK_CALL)
|
||||
.install() // hook*
|
||||
->quick();
|
||||
// Remove the menus from memory (UI_Shutdown)
|
||||
utils::hook(0x4F4C4F, ui_shutdown_stub, HOOK_CALL)
|
||||
.install() // hook*
|
||||
->quick();
|
||||
}
|
||||
};
|
||||
} // namespace ui
|
||||
|
||||
REGISTER_COMPONENT(ui::component)
|
@ -25,32 +25,32 @@ void set_aspect_ratio() {
|
||||
|
||||
__declspec(naked) void set_aspect_ratio_stub() {
|
||||
__asm {
|
||||
mov eax, [eax + 0x10];
|
||||
cmp eax, 4;
|
||||
mov eax, [eax + 0x10];
|
||||
cmp eax, 4;
|
||||
|
||||
mov dword ptr ds:0x1C91A68, edx;
|
||||
mov dword ptr ds:0x1C91A6C, esi;
|
||||
mov dword ptr ds:0x1C91A74, ecx;
|
||||
mov dword ptr ds:0x1C91A68, edx;
|
||||
mov dword ptr ds:0x1C91A6C, esi;
|
||||
mov dword ptr ds:0x1C91A74, ecx;
|
||||
|
||||
ja default_case;
|
||||
je custom_ratio;
|
||||
ja default_case;
|
||||
je custom_ratio;
|
||||
|
||||
push 0x50AE6C;
|
||||
ret;
|
||||
push 0x50AE6C;
|
||||
ret;
|
||||
|
||||
default_case:
|
||||
push 0x50AF6C;
|
||||
ret;
|
||||
default_case:
|
||||
push 0x50AF6C;
|
||||
ret;
|
||||
|
||||
custom_ratio:
|
||||
pushad;
|
||||
call set_aspect_ratio;
|
||||
popad;
|
||||
custom_ratio:
|
||||
pushad;
|
||||
call set_aspect_ratio;
|
||||
popad;
|
||||
|
||||
mov eax, 1; // set widescreen to 1
|
||||
mov eax, 1; // set widescreen to 1
|
||||
|
||||
push 0x50AF05;
|
||||
ret;
|
||||
push 0x50AF05;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,9 @@ const game::dvar_t* pm_rocketJump = nullptr;
|
||||
const game::dvar_t* pm_rocketJumpScale = nullptr;
|
||||
const game::dvar_t* pm_playerCollision = nullptr;
|
||||
const game::dvar_t* pm_elevators = nullptr;
|
||||
const game::dvar_t* pm_disableLandingSlowdown = nullptr;
|
||||
const game::dvar_t* pm_bunnyHop = nullptr;
|
||||
const game::dvar_t* pm_snapVector = nullptr;
|
||||
|
||||
const game::dvar_t* cg_drawVersion = nullptr;
|
||||
const game::dvar_t* cg_drawVersionX = nullptr;
|
||||
@ -27,4 +30,9 @@ const game::dvar_t** sv_mapname =
|
||||
|
||||
const game::dvar_t** version =
|
||||
reinterpret_cast<const game::dvar_t**>(0x145D690);
|
||||
|
||||
const game::dvar_t** com_developer =
|
||||
reinterpret_cast<const game::dvar_t**>(0x145D648);
|
||||
const game::dvar_t** com_developer_script =
|
||||
reinterpret_cast<const game::dvar_t**>(0x145EC58);
|
||||
} // namespace dvars
|
||||
|
@ -9,6 +9,9 @@ extern const game::dvar_t* pm_rocketJump;
|
||||
extern const game::dvar_t* pm_rocketJumpScale;
|
||||
extern const game::dvar_t* pm_playerCollision;
|
||||
extern const game::dvar_t* pm_elevators;
|
||||
extern const game::dvar_t* pm_disableLandingSlowdown;
|
||||
extern const game::dvar_t* pm_bunnyHop;
|
||||
extern const game::dvar_t* pm_snapVector;
|
||||
|
||||
extern const game::dvar_t* cg_drawVersion;
|
||||
extern const game::dvar_t* cg_drawVersionX;
|
||||
@ -24,4 +27,7 @@ extern const game::dvar_t** g_specialops;
|
||||
extern const game::dvar_t** sv_mapname;
|
||||
|
||||
extern const game::dvar_t** version;
|
||||
|
||||
extern const game::dvar_t** com_developer;
|
||||
extern const game::dvar_t** com_developer_script;
|
||||
} // namespace dvars
|
||||
|
@ -5,28 +5,29 @@
|
||||
|
||||
namespace game::engine {
|
||||
fast_critical_section_scope_read::fast_critical_section_scope_read(
|
||||
FastCriticalSection* cs)
|
||||
FastCriticalSection* cs) noexcept
|
||||
: cs_(cs) {
|
||||
if (this->cs_) {
|
||||
Sys_LockRead(this->cs_);
|
||||
}
|
||||
}
|
||||
|
||||
fast_critical_section_scope_read::~fast_critical_section_scope_read() {
|
||||
fast_critical_section_scope_read::~fast_critical_section_scope_read() noexcept {
|
||||
if (this->cs_) {
|
||||
Sys_UnlockRead(this->cs_);
|
||||
}
|
||||
}
|
||||
|
||||
fast_critical_section_scope_write::fast_critical_section_scope_write(
|
||||
FastCriticalSection* cs)
|
||||
FastCriticalSection* cs) noexcept
|
||||
: cs_(cs) {
|
||||
if (this->cs_) {
|
||||
Sys_LockWrite(this->cs_);
|
||||
}
|
||||
}
|
||||
|
||||
fast_critical_section_scope_write::~fast_critical_section_scope_write() {
|
||||
fast_critical_section_scope_write::
|
||||
~fast_critical_section_scope_write() noexcept {
|
||||
if (this->cs_) {
|
||||
Sys_UnlockWrite(this->cs_);
|
||||
}
|
||||
|
@ -3,8 +3,8 @@
|
||||
namespace game::engine {
|
||||
class fast_critical_section_scope_read {
|
||||
public:
|
||||
fast_critical_section_scope_read(FastCriticalSection* cs);
|
||||
~fast_critical_section_scope_read();
|
||||
fast_critical_section_scope_read(FastCriticalSection* cs) noexcept;
|
||||
~fast_critical_section_scope_read() noexcept;
|
||||
|
||||
private:
|
||||
FastCriticalSection* cs_;
|
||||
@ -12,8 +12,8 @@ private:
|
||||
|
||||
class fast_critical_section_scope_write {
|
||||
public:
|
||||
fast_critical_section_scope_write(FastCriticalSection* cs);
|
||||
~fast_critical_section_scope_write();
|
||||
fast_critical_section_scope_write(FastCriticalSection* cs) noexcept;
|
||||
~fast_critical_section_scope_write() noexcept;
|
||||
|
||||
private:
|
||||
FastCriticalSection* cs_;
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
namespace game::engine {
|
||||
scoped_critical_section::scoped_critical_section(
|
||||
const CriticalSection s, const ScopedCriticalSectionType type)
|
||||
const CriticalSection s, const ScopedCriticalSectionType type) noexcept
|
||||
: s_(s), is_scoped_release_(false) {
|
||||
if (type == SCOPED_CRITSECT_NORMAL) {
|
||||
Sys_EnterCriticalSection(this->s_);
|
||||
@ -17,35 +17,37 @@ scoped_critical_section::scoped_critical_section(
|
||||
Sys_LeaveCriticalSection(this->s_);
|
||||
this->is_scoped_release_ = true;
|
||||
}
|
||||
|
||||
this->has_ownership_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scoped_critical_section::~scoped_critical_section() {
|
||||
scoped_critical_section::~scoped_critical_section() noexcept {
|
||||
if (!this->has_ownership_ || this->is_scoped_release_) {
|
||||
if (!this->has_ownership_ && this->is_scoped_release_)
|
||||
if (!this->has_ownership_ && this->is_scoped_release_) {
|
||||
Sys_EnterCriticalSection(this->s_);
|
||||
}
|
||||
} else {
|
||||
Sys_LeaveCriticalSection(this->s_);
|
||||
}
|
||||
}
|
||||
|
||||
void scoped_critical_section::enter_crit_sect() {
|
||||
void scoped_critical_section::enter_crit_sect() noexcept {
|
||||
assert(!this->has_ownership_);
|
||||
|
||||
this->has_ownership_ = true;
|
||||
Sys_EnterCriticalSection(this->s_);
|
||||
}
|
||||
|
||||
void scoped_critical_section::leave_crit_sect() {
|
||||
void scoped_critical_section::leave_crit_sect() noexcept {
|
||||
assert(this->has_ownership_);
|
||||
|
||||
this->has_ownership_ = false;
|
||||
Sys_LeaveCriticalSection(this->s_);
|
||||
}
|
||||
|
||||
bool scoped_critical_section::try_enter_crit_sect() {
|
||||
bool scoped_critical_section::try_enter_crit_sect() noexcept {
|
||||
assert(!this->has_ownership_);
|
||||
|
||||
const auto result = Sys_TryEnterCriticalSection(this->s_);
|
||||
@ -53,11 +55,11 @@ bool scoped_critical_section::try_enter_crit_sect() {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool scoped_critical_section::has_ownership() const {
|
||||
bool scoped_critical_section::has_ownership() const noexcept {
|
||||
return this->has_ownership_;
|
||||
}
|
||||
|
||||
bool scoped_critical_section::is_scoped_release() const {
|
||||
bool scoped_critical_section::is_scoped_release() const noexcept {
|
||||
return this->is_scoped_release_;
|
||||
}
|
||||
} // namespace game::engine
|
||||
|
@ -3,15 +3,16 @@
|
||||
namespace game::engine {
|
||||
class scoped_critical_section {
|
||||
public:
|
||||
scoped_critical_section(CriticalSection s, ScopedCriticalSectionType type);
|
||||
~scoped_critical_section();
|
||||
scoped_critical_section(CriticalSection s,
|
||||
ScopedCriticalSectionType type) noexcept;
|
||||
~scoped_critical_section() noexcept;
|
||||
|
||||
void enter_crit_sect();
|
||||
void leave_crit_sect();
|
||||
bool try_enter_crit_sect();
|
||||
void enter_crit_sect() noexcept;
|
||||
void leave_crit_sect() noexcept;
|
||||
bool try_enter_crit_sect() noexcept;
|
||||
|
||||
[[nodiscard]] bool has_ownership() const;
|
||||
[[nodiscard]] bool is_scoped_release() const;
|
||||
[[nodiscard]] bool has_ownership() const noexcept;
|
||||
[[nodiscard]] bool is_scoped_release() const noexcept;
|
||||
|
||||
private:
|
||||
CriticalSection s_;
|
||||
|
@ -81,6 +81,12 @@ void Sys_UnlockWrite(FastCriticalSection* critSect) {
|
||||
Sys_TempPriorityEnd(&critSect->tempPriority);
|
||||
}
|
||||
|
||||
void Sys_SnapVector(float* v) {
|
||||
v[0] = std::floorf(v[0] + 0.5f);
|
||||
v[1] = std::floorf(v[1] + 0.5f);
|
||||
v[2] = std::floorf(v[2] + 0.5f);
|
||||
}
|
||||
|
||||
int PC_Int_Parse(int handle, int* i) {
|
||||
const static DWORD PC_Int_Parse_t = 0x62DF10;
|
||||
int result{};
|
||||
@ -116,4 +122,18 @@ int PC_Float_Parse(int handle, float* f) {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Menu_FreeItemMemory(itemDef_s* item) {
|
||||
const static DWORD Menu_FreeItemMemory_t = 0x62B7E0;
|
||||
|
||||
__asm {
|
||||
pushad;
|
||||
|
||||
mov edi, item;
|
||||
call Menu_FreeItemMemory_t;
|
||||
|
||||
popad;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
|
@ -39,16 +39,25 @@ bool Sys_TryEnterCriticalSection(CriticalSection critSect);
|
||||
void Sys_LockRead(FastCriticalSection* critSect);
|
||||
void Sys_UnlockRead(FastCriticalSection* critSect);
|
||||
void Sys_UnlockWrite(FastCriticalSection* critSect);
|
||||
void Sys_SnapVector(float* v);
|
||||
|
||||
int PC_Int_Parse(int handle, int* i);
|
||||
int PC_Float_Parse(int handle, float* f);
|
||||
|
||||
void Menu_FreeItemMemory(itemDef_s* item);
|
||||
|
||||
// Global definitions
|
||||
constexpr auto CMD_MAX_NESTING = 8;
|
||||
|
||||
constexpr auto MAX_POSSIBLE_LOCAL_CLIENTS = 1;
|
||||
|
||||
constexpr std::size_t MAX_LOCAL_CLIENTS = 1;
|
||||
|
||||
constexpr auto MAX_QPATH = 64;
|
||||
|
||||
constexpr auto MAX_OPCODE_LOOKUP_SIZE = 0x1000000;
|
||||
constexpr auto MAX_SOURCEPOS_LOOKUP_SIZE = 0x800000;
|
||||
constexpr auto MAX_SOURCEBUF_LOOKUP_SIZE = 0x40000;
|
||||
} // namespace game
|
||||
|
||||
#include "symbols.hpp"
|
||||
|
@ -14,22 +14,342 @@ struct scr_entref_t {
|
||||
unsigned __int16 classnum;
|
||||
};
|
||||
|
||||
typedef void(__cdecl* BuiltinMethod)(scr_entref_t);
|
||||
typedef void (*BuiltinMethod)(scr_entref_t);
|
||||
|
||||
typedef void(__cdecl* BuiltinFunction)();
|
||||
typedef void (*BuiltinFunction)();
|
||||
|
||||
struct BuiltinMethodDef {
|
||||
const char* actionString;
|
||||
void(__cdecl* actionFunc)(scr_entref_t);
|
||||
void (*actionFunc)(scr_entref_t);
|
||||
int type;
|
||||
};
|
||||
|
||||
struct BuiltinFunctionDef {
|
||||
const char* actionString;
|
||||
void(__cdecl* actionFunc)();
|
||||
void (*actionFunc)();
|
||||
int type;
|
||||
};
|
||||
|
||||
struct OpcodeLookup {
|
||||
const char* codePos;
|
||||
unsigned int sourcePosIndex;
|
||||
unsigned __int16 sourcePosCount;
|
||||
unsigned __int16 cumulOffset;
|
||||
unsigned __int16* localVars;
|
||||
int profileTime;
|
||||
int profileBuiltInTime;
|
||||
int profileUsage;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OpcodeLookup) == 0x1C);
|
||||
|
||||
struct SourceLookup {
|
||||
unsigned int sourcePos;
|
||||
int type;
|
||||
};
|
||||
|
||||
struct SaveSourceBufferInfo {
|
||||
char* buf;
|
||||
char* sourceBuf;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct scrParserGlob_t {
|
||||
OpcodeLookup* opcodeLookup;
|
||||
unsigned int opcodeLookupMaxSize;
|
||||
unsigned int opcodeLookupLen;
|
||||
SourceLookup* sourcePosLookup;
|
||||
unsigned int sourcePosLookupMaxSize;
|
||||
unsigned int sourcePosLookupLen;
|
||||
unsigned int sourceBufferLookupMaxSize;
|
||||
const char* currentCodePos;
|
||||
unsigned int currentSourcePosCount;
|
||||
SaveSourceBufferInfo* saveSourceBufferLookup;
|
||||
unsigned int saveSourceBufferLookupLen;
|
||||
int delayedSourceIndex;
|
||||
int threadStartSourceIndex;
|
||||
};
|
||||
|
||||
static_assert(sizeof(scrParserGlob_t) == 0x34);
|
||||
|
||||
struct SourceBufferInfo {
|
||||
const char* codePos;
|
||||
char* buf;
|
||||
const char* sourceBuf;
|
||||
int len;
|
||||
int sortedIndex;
|
||||
bool archive;
|
||||
int time;
|
||||
int avgTime;
|
||||
int maxTime;
|
||||
float totalTime;
|
||||
float totalBuiltIn;
|
||||
};
|
||||
|
||||
struct CodeOffsetMap {
|
||||
int type;
|
||||
unsigned int cumulOffset;
|
||||
int codeOffset;
|
||||
int sourcePos;
|
||||
int newCodeOffest;
|
||||
};
|
||||
|
||||
struct scrParserPub_t {
|
||||
SourceBufferInfo* sourceBufferLookup;
|
||||
unsigned int sourceBufferLookupLen;
|
||||
const char* scriptfilename;
|
||||
const char* sourceBuf;
|
||||
CodeOffsetMap* codeOffsetMap;
|
||||
unsigned int codeOffsetMapLen;
|
||||
int useCodeOffsetMap;
|
||||
};
|
||||
|
||||
static_assert(sizeof(scrParserPub_t) == 0x1C);
|
||||
|
||||
struct VariableStackBuffer {
|
||||
const char* pos;
|
||||
unsigned __int16 size;
|
||||
unsigned __int16 bufLen;
|
||||
unsigned __int16 localId;
|
||||
unsigned __int8 time;
|
||||
char buf[1];
|
||||
};
|
||||
|
||||
union VariableUnion {
|
||||
int intValue;
|
||||
float floatValue;
|
||||
unsigned int stringValue;
|
||||
const float* vectorValue;
|
||||
const char* codePosValue;
|
||||
unsigned int pointerValue;
|
||||
VariableStackBuffer* stackValue;
|
||||
unsigned int entityOffset;
|
||||
};
|
||||
|
||||
struct VariableValue {
|
||||
VariableUnion u;
|
||||
int type;
|
||||
};
|
||||
|
||||
struct function_stack_t {
|
||||
const char* pos;
|
||||
unsigned int localId;
|
||||
unsigned int localVarCount;
|
||||
VariableValue* top;
|
||||
VariableValue* startTop;
|
||||
};
|
||||
|
||||
struct function_frame_t {
|
||||
function_stack_t fs;
|
||||
int topType;
|
||||
};
|
||||
|
||||
struct scrVmPub_t {
|
||||
unsigned int* localVars;
|
||||
VariableValue* maxstack;
|
||||
int function_count;
|
||||
function_frame_t* function_frame;
|
||||
VariableValue* top;
|
||||
bool debugCode;
|
||||
bool abort_on_error;
|
||||
bool terminal_error;
|
||||
unsigned int inparamcount;
|
||||
unsigned int outparamcount;
|
||||
function_frame_t function_frame_start[32];
|
||||
VariableValue stack[2048];
|
||||
};
|
||||
|
||||
struct HunkUser {
|
||||
HunkUser* current;
|
||||
HunkUser* next;
|
||||
int maxSize;
|
||||
int end;
|
||||
int pos;
|
||||
const char* name;
|
||||
bool fixed;
|
||||
int type;
|
||||
unsigned __int8 buf[1];
|
||||
};
|
||||
|
||||
static_assert(sizeof(HunkUser) == 0x24);
|
||||
|
||||
struct scrVarPub_t {
|
||||
const char* fieldBuffer;
|
||||
unsigned __int16 canonicalStrCount;
|
||||
bool developer_script;
|
||||
bool evaluate;
|
||||
const char* error_message;
|
||||
int error_index;
|
||||
unsigned int time;
|
||||
unsigned int timeArrayId;
|
||||
unsigned int pauseArrayId;
|
||||
unsigned int notifyArrayId;
|
||||
unsigned int objectStackId;
|
||||
unsigned int levelId;
|
||||
unsigned int gameId;
|
||||
unsigned int animId;
|
||||
unsigned int freeEntList;
|
||||
unsigned int tempVariable;
|
||||
unsigned int numScriptValues[2];
|
||||
bool bInited;
|
||||
unsigned __int16 savecount;
|
||||
unsigned __int16 savecountMark;
|
||||
unsigned int checksum;
|
||||
unsigned int entId;
|
||||
unsigned int entFieldName;
|
||||
HunkUser* programHunkUser;
|
||||
const char* programBuffer;
|
||||
const char* endScriptBuffer;
|
||||
unsigned __int16 saveIdMap[36864];
|
||||
unsigned __int16 saveIdMapRev[36864];
|
||||
bool bScriptProfile;
|
||||
float scriptProfileMinTime;
|
||||
bool bScriptProfileBuiltin;
|
||||
float scriptProfileBuiltinMinTime;
|
||||
unsigned int numScriptThreads;
|
||||
unsigned int numScriptObjects;
|
||||
const char* varUsagePos;
|
||||
int ext_threadcount;
|
||||
int totalObjectRefCount;
|
||||
volatile int totalVectorRefCount;
|
||||
unsigned int removeId;
|
||||
};
|
||||
|
||||
static_assert(sizeof(scrVarPub_t) == 0x2408C);
|
||||
|
||||
struct scr_localVar_t {
|
||||
unsigned int name;
|
||||
unsigned int sourcePos;
|
||||
};
|
||||
|
||||
struct scr_block_t {
|
||||
int abortLevel;
|
||||
int localVarsCreateCount;
|
||||
int localVarsPublicCount;
|
||||
int localVarsCount;
|
||||
unsigned __int8 localVarsInitBits[8];
|
||||
scr_localVar_t localVars[64];
|
||||
};
|
||||
|
||||
struct scrCompilePub_t {
|
||||
int value_count;
|
||||
int far_function_count;
|
||||
unsigned int loadedscripts;
|
||||
unsigned int scriptsPos;
|
||||
unsigned int scriptsCount;
|
||||
unsigned int scriptsDefine;
|
||||
unsigned int builtinFunc;
|
||||
unsigned int builtinMeth;
|
||||
unsigned __int16 canonicalStrings[65536];
|
||||
const char* in_ptr;
|
||||
bool in_ptr_valid;
|
||||
const char* parseBuf;
|
||||
bool script_loading;
|
||||
bool allowedBreakpoint;
|
||||
int developer_statement;
|
||||
char* opcodePos;
|
||||
unsigned int programLen;
|
||||
int func_table_size;
|
||||
int func_table[1024];
|
||||
scr_block_t* pauseBlock;
|
||||
};
|
||||
|
||||
struct CaseStatementInfo {
|
||||
unsigned int name;
|
||||
const char* codePos;
|
||||
unsigned int sourcePos;
|
||||
CaseStatementInfo* next;
|
||||
};
|
||||
|
||||
struct BreakStatementInfo {
|
||||
const char* codePos;
|
||||
const char* nextCodePos;
|
||||
BreakStatementInfo* next;
|
||||
};
|
||||
|
||||
struct ContinueStatementInfo {
|
||||
const char* codePos;
|
||||
const char* nextCodePos;
|
||||
ContinueStatementInfo* next;
|
||||
};
|
||||
|
||||
struct PrecacheEntry {
|
||||
unsigned __int16 filename;
|
||||
bool include;
|
||||
unsigned int sourcePos;
|
||||
};
|
||||
|
||||
union sval_u {
|
||||
int type;
|
||||
unsigned int stringValue;
|
||||
unsigned int idValue;
|
||||
float floatValue;
|
||||
int intValue;
|
||||
sval_u* node;
|
||||
unsigned int sourcePosValue;
|
||||
const char* codePosValue;
|
||||
const char* debugString;
|
||||
scr_block_t* block;
|
||||
};
|
||||
|
||||
struct VariableCompileValue {
|
||||
VariableValue value;
|
||||
sval_u sourcePos;
|
||||
};
|
||||
|
||||
struct scrCompileGlob_t {
|
||||
unsigned char* codePos;
|
||||
unsigned char* prevOpcodePos;
|
||||
unsigned int filePosId;
|
||||
unsigned int fileCountId;
|
||||
int cumulOffset;
|
||||
int prevCumulOffset;
|
||||
int maxOffset;
|
||||
int maxCallOffset;
|
||||
bool bConstRefCount;
|
||||
bool in_developer_thread;
|
||||
unsigned int developer_thread_sourcePos;
|
||||
bool firstThread[2];
|
||||
CaseStatementInfo* currentCaseStatement;
|
||||
bool bCanBreak;
|
||||
BreakStatementInfo* currentBreakStatement;
|
||||
bool bCanContinue;
|
||||
ContinueStatementInfo* currentContinueStatement;
|
||||
scr_block_t** breakChildBlocks;
|
||||
int* breakChildCount;
|
||||
scr_block_t* breakBlock;
|
||||
scr_block_t** continueChildBlocks;
|
||||
int* continueChildCount;
|
||||
bool forceNotCreate;
|
||||
PrecacheEntry* precachescriptList;
|
||||
VariableCompileValue value_start[32];
|
||||
};
|
||||
|
||||
struct scr_animtree_t {
|
||||
void* anims;
|
||||
};
|
||||
|
||||
struct scrAnimPub_t {
|
||||
unsigned int animtrees;
|
||||
unsigned int animtree_node;
|
||||
unsigned int animTreeNames;
|
||||
scr_animtree_t xanim_lookup[2][128];
|
||||
unsigned int xanim_num[2];
|
||||
unsigned int animTreeIndex;
|
||||
bool animtree_loading;
|
||||
};
|
||||
|
||||
enum {
|
||||
SOURCE_TYPE_BREAKPOINT = 0x1,
|
||||
SOURCE_TYPE_CALL = 0x2,
|
||||
SOURCE_TYPE_CALL_POINTER = 0x4,
|
||||
SOURCE_TYPE_THREAD_START = 0x8,
|
||||
SOURCE_TYPE_BUILTIN_CALL = 0x10,
|
||||
SOURCE_TYPE_NOTIFY = 0x20,
|
||||
SOURCE_TYPE_GETFUNCTION = 0x40,
|
||||
SOURCE_TYPE_WAIT = 0x80,
|
||||
};
|
||||
|
||||
enum {
|
||||
VAR_STRING = 0x2,
|
||||
VAR_FLOAT = 0x5,
|
||||
@ -490,12 +810,455 @@ struct GameWorldSp {
|
||||
|
||||
static_assert(sizeof(GameWorldSp) == 0x38);
|
||||
|
||||
struct StringTableCell {
|
||||
const char* string;
|
||||
int hash;
|
||||
};
|
||||
|
||||
struct StringTable {
|
||||
const char* name;
|
||||
int columnCount;
|
||||
int rowCount;
|
||||
StringTableCell* values;
|
||||
};
|
||||
|
||||
#define MAX_UISTRINGS 0x800
|
||||
|
||||
struct rectDef_s {
|
||||
float x;
|
||||
float y;
|
||||
float w;
|
||||
float h;
|
||||
char horzAlign;
|
||||
char vertAlign;
|
||||
};
|
||||
|
||||
struct windowDef_t {
|
||||
const char* name;
|
||||
rectDef_s rect;
|
||||
rectDef_s rectClient;
|
||||
const char* group;
|
||||
int style;
|
||||
int border;
|
||||
int ownerDraw;
|
||||
int ownerDrawFlags;
|
||||
float borderSize;
|
||||
int staticFlags;
|
||||
int dynamicFlags[1];
|
||||
int nextTime;
|
||||
float foreColor[4];
|
||||
float backColor[4];
|
||||
float borderColor[4];
|
||||
float outlineColor[4];
|
||||
float disableColor[4];
|
||||
void* background;
|
||||
};
|
||||
|
||||
static_assert(sizeof(windowDef_t) == 0xA4);
|
||||
|
||||
enum expDataType {
|
||||
VAL_INT = 0x0,
|
||||
VAL_FLOAT = 0x1,
|
||||
VAL_STRING = 0x2,
|
||||
NUM_INTERNAL_DATATYPES = 0x3,
|
||||
VAL_FUNCTION = 0x3,
|
||||
NUM_DATATYPES = 0x4,
|
||||
};
|
||||
|
||||
struct Operand {
|
||||
expDataType dataType;
|
||||
char internals[4];
|
||||
};
|
||||
|
||||
union entryInternalData {
|
||||
int op;
|
||||
Operand operand;
|
||||
};
|
||||
|
||||
struct expressionEntry {
|
||||
int type;
|
||||
entryInternalData data;
|
||||
};
|
||||
|
||||
struct menuTransition {
|
||||
int transitionType;
|
||||
int targetField;
|
||||
int startTime;
|
||||
float startVal;
|
||||
float endVal;
|
||||
float time;
|
||||
int endTriggerType;
|
||||
};
|
||||
|
||||
struct StaticDvar {
|
||||
dvar_t* dvar;
|
||||
char* dvarName;
|
||||
};
|
||||
|
||||
struct StaticDvarList {
|
||||
int numStaticDvars;
|
||||
StaticDvar** staticDvars;
|
||||
};
|
||||
|
||||
struct StringList {
|
||||
int totalStrings;
|
||||
const char** strings;
|
||||
};
|
||||
|
||||
struct ExpressionSupportingData; // required against my will
|
||||
|
||||
struct Statement_s {
|
||||
int numEntries;
|
||||
expressionEntry* entries;
|
||||
ExpressionSupportingData* supportingData;
|
||||
int lastExecuteTime;
|
||||
Operand lastResult;
|
||||
};
|
||||
|
||||
struct UIFunctionList {
|
||||
int totalFunctions;
|
||||
Statement_s** functions;
|
||||
};
|
||||
|
||||
struct ExpressionSupportingData {
|
||||
UIFunctionList uifunctions;
|
||||
StaticDvarList staticDvarList;
|
||||
StringList uiStrings;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Statement_s) == 0x18);
|
||||
|
||||
struct ItemFloatExpression {
|
||||
int target;
|
||||
Statement_s* expression;
|
||||
};
|
||||
|
||||
struct MenuEventHandlerSet; // required against my will
|
||||
|
||||
struct ConditionalScript {
|
||||
MenuEventHandlerSet* eventHandlerSet;
|
||||
Statement_s* eventExpression;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ConditionalScript) == 0x8);
|
||||
|
||||
struct SetLocalVarData {
|
||||
const char* localVarName;
|
||||
Statement_s* expression;
|
||||
};
|
||||
|
||||
union EventData {
|
||||
const char* unconditionalScript;
|
||||
ConditionalScript* conditionalScript;
|
||||
MenuEventHandlerSet* elseScript;
|
||||
SetLocalVarData* setLocalVarData;
|
||||
};
|
||||
|
||||
#define MAX_EVENT_HANDLERS_PER_EVENT 200
|
||||
|
||||
enum EventType {
|
||||
EVENT_UNCONDITIONAL = 0x0,
|
||||
EVENT_IF = 0x1,
|
||||
EVENT_ELSE = 0x2,
|
||||
EVENT_SET_LOCAL_VAR_BOOL = 0x3,
|
||||
EVENT_SET_LOCAL_VAR_INT = 0x4,
|
||||
EVENT_SET_LOCAL_VAR_FLOAT = 0x5,
|
||||
EVENT_SET_LOCAL_VAR_STRING = 0x6,
|
||||
EVENT_COUNT = 0x7,
|
||||
};
|
||||
|
||||
struct MenuEventHandler {
|
||||
EventData eventData;
|
||||
char eventType;
|
||||
};
|
||||
|
||||
static_assert(sizeof(MenuEventHandler) == 0x8);
|
||||
|
||||
struct MenuEventHandlerSet {
|
||||
int eventHandlerCount;
|
||||
MenuEventHandler** eventHandlers;
|
||||
};
|
||||
|
||||
struct ItemKeyHandler {
|
||||
int key;
|
||||
MenuEventHandlerSet* action;
|
||||
ItemKeyHandler* next;
|
||||
};
|
||||
|
||||
struct columnInfo_s {
|
||||
int pos;
|
||||
int width;
|
||||
int maxChars;
|
||||
int alignment;
|
||||
};
|
||||
|
||||
struct listBoxDef_s {
|
||||
int mousePos;
|
||||
int startPos[1];
|
||||
int endPos[1];
|
||||
int drawPadding;
|
||||
float elementWidth;
|
||||
float elementHeight;
|
||||
int elementStyle;
|
||||
int numColumns;
|
||||
columnInfo_s columnInfo[16];
|
||||
MenuEventHandlerSet* onDoubleClick;
|
||||
int notselectable;
|
||||
int noScrollBars;
|
||||
int usePaging;
|
||||
float selectBorder[4];
|
||||
void* selectIcon;
|
||||
};
|
||||
|
||||
static_assert(sizeof(listBoxDef_s) == 0x144);
|
||||
|
||||
struct editFieldDef_s {
|
||||
float minVal;
|
||||
float maxVal;
|
||||
float defVal;
|
||||
float range;
|
||||
int maxChars;
|
||||
int maxCharsGotoNext;
|
||||
int maxPaintChars;
|
||||
int paintOffset;
|
||||
};
|
||||
|
||||
static_assert(sizeof(editFieldDef_s) == 0x20);
|
||||
|
||||
#define MAX_MULTI_DVARS 32
|
||||
|
||||
struct multiDef_s {
|
||||
const char* dvarList[MAX_MULTI_DVARS];
|
||||
const char* dvarStr[MAX_MULTI_DVARS];
|
||||
float dvarValue[MAX_MULTI_DVARS];
|
||||
int count;
|
||||
int strDef;
|
||||
};
|
||||
|
||||
static_assert(sizeof(multiDef_s) == 0x188);
|
||||
|
||||
struct newsTickerDef_s {
|
||||
int feedId;
|
||||
int speed;
|
||||
int spacing;
|
||||
int lastTime;
|
||||
int start;
|
||||
int end;
|
||||
float x;
|
||||
};
|
||||
|
||||
static_assert(sizeof(newsTickerDef_s) == 0x1C);
|
||||
|
||||
struct textScrollDef_s {
|
||||
int startTime;
|
||||
};
|
||||
|
||||
union itemDefData_t {
|
||||
listBoxDef_s* listBox;
|
||||
editFieldDef_s* editField;
|
||||
multiDef_s* multi;
|
||||
const char* enumDvarName;
|
||||
newsTickerDef_s* ticker;
|
||||
textScrollDef_s* scroll;
|
||||
void* data;
|
||||
};
|
||||
|
||||
struct menuDef_t; // required against my will
|
||||
|
||||
struct itemDef_s {
|
||||
windowDef_t window;
|
||||
rectDef_s textRect[1];
|
||||
int type;
|
||||
int dataType;
|
||||
int alignment;
|
||||
int fontEnum;
|
||||
int textAlignMode;
|
||||
float textalignx;
|
||||
float textaligny;
|
||||
float textscale;
|
||||
int textStyle;
|
||||
int gameMsgWindowIndex;
|
||||
int gameMsgWindowMode;
|
||||
const char* text;
|
||||
int itemFlags;
|
||||
menuDef_t* parent;
|
||||
MenuEventHandlerSet* mouseEnterText;
|
||||
MenuEventHandlerSet* mouseExitText;
|
||||
MenuEventHandlerSet* mouseEnter;
|
||||
MenuEventHandlerSet* mouseExit;
|
||||
MenuEventHandlerSet* action;
|
||||
MenuEventHandlerSet* accept;
|
||||
MenuEventHandlerSet* onFocus;
|
||||
MenuEventHandlerSet* leaveFocus;
|
||||
const char* dvar;
|
||||
const char* dvarTest;
|
||||
ItemKeyHandler* onKey;
|
||||
const char* enableDvar;
|
||||
const char* localVar;
|
||||
int dvarFlags;
|
||||
void* focusSound;
|
||||
float special;
|
||||
int cursorPos[1];
|
||||
itemDefData_t typeData;
|
||||
int imageTrack;
|
||||
int floatExpressionCount;
|
||||
ItemFloatExpression* floatExpressions;
|
||||
Statement_s* visibleExp;
|
||||
Statement_s* disabledExp;
|
||||
Statement_s* textExp;
|
||||
Statement_s* materialExp;
|
||||
float glowColor[4];
|
||||
bool decayActive;
|
||||
int fxBirthTime;
|
||||
int fxLetterTime;
|
||||
int fxDecayStartTime;
|
||||
int fxDecayDuration;
|
||||
int lastSoundPlayedTime;
|
||||
};
|
||||
|
||||
static_assert(sizeof(itemDef_s) == 0x17C);
|
||||
|
||||
struct menuDef_t {
|
||||
windowDef_t window;
|
||||
const char* font;
|
||||
int fullScreen;
|
||||
int itemCount;
|
||||
int fontIndex;
|
||||
int cursorItem[1];
|
||||
int fadeCycle;
|
||||
float fadeClamp;
|
||||
float fadeAmount;
|
||||
float fadeInAmount;
|
||||
float blurRadius;
|
||||
MenuEventHandlerSet* onOpen;
|
||||
MenuEventHandlerSet* onCloseRequest;
|
||||
MenuEventHandlerSet* onClose;
|
||||
MenuEventHandlerSet* onESC;
|
||||
ItemKeyHandler* onKey;
|
||||
Statement_s* visibleExp;
|
||||
const char* allowedBinding;
|
||||
const char* soundName;
|
||||
int imageTrack;
|
||||
float focusColor[4];
|
||||
Statement_s* rectXExp;
|
||||
Statement_s* rectYExp;
|
||||
Statement_s* rectWExp;
|
||||
Statement_s* rectHExp;
|
||||
Statement_s* openSoundExp;
|
||||
Statement_s* closeSoundExp;
|
||||
itemDef_s** items;
|
||||
menuTransition scaleTransition[1];
|
||||
menuTransition alphaTransition[1];
|
||||
menuTransition xTransition[1];
|
||||
menuTransition yTransition[1];
|
||||
ExpressionSupportingData* expressionData;
|
||||
};
|
||||
|
||||
static_assert(sizeof(menuDef_t) == 0x190);
|
||||
|
||||
struct loadAssets_t {
|
||||
float fadeClamp;
|
||||
int fadeCycle;
|
||||
float fadeAmount;
|
||||
float fadeInAmount;
|
||||
};
|
||||
|
||||
enum UILocalVarType {
|
||||
UILOCALVAR_INT = 0x0,
|
||||
UILOCALVAR_FLOAT = 0x1,
|
||||
UILOCALVAR_STRING = 0x2,
|
||||
};
|
||||
|
||||
struct UILocalVar {
|
||||
UILocalVarType type;
|
||||
const char* name;
|
||||
union {
|
||||
int integer;
|
||||
float value;
|
||||
const char* string;
|
||||
} u;
|
||||
};
|
||||
|
||||
struct UILocalVarContext {
|
||||
UILocalVar table[256];
|
||||
};
|
||||
|
||||
struct UiContext {
|
||||
int localClientNum;
|
||||
float bias;
|
||||
int realTime;
|
||||
int frameTime;
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
int lastMoveTime;
|
||||
} cursor;
|
||||
int isCursorVisible;
|
||||
int paintFull;
|
||||
int screenWidth;
|
||||
int screenHeight;
|
||||
float screenAspect;
|
||||
float FPS;
|
||||
float blurRadiusOut;
|
||||
menuDef_t* Menus[640];
|
||||
int menuCount;
|
||||
menuDef_t* menuStack[16];
|
||||
int openMenuCount;
|
||||
UILocalVarContext localVars;
|
||||
const StringTable* cinematicSubtitles;
|
||||
};
|
||||
|
||||
struct MenuList {
|
||||
const char* name;
|
||||
int menuCount;
|
||||
void** menus;
|
||||
menuDef_t** menus;
|
||||
};
|
||||
|
||||
struct savegameStatus_s {
|
||||
int sortKey;
|
||||
int sortDir;
|
||||
int displaySavegames[256];
|
||||
};
|
||||
|
||||
struct qtime_s {
|
||||
int tm_sec;
|
||||
int tm_min;
|
||||
int tm_hour;
|
||||
int tm_mday;
|
||||
int tm_mon;
|
||||
int tm_year;
|
||||
int tm_wday;
|
||||
int tm_yday;
|
||||
int tm_isdst;
|
||||
};
|
||||
|
||||
struct SavegameInfo {
|
||||
const char* savegameFile;
|
||||
const char* savegameName;
|
||||
const char* imageName;
|
||||
const char* mapName;
|
||||
const char* savegameInfoText;
|
||||
const char* time;
|
||||
const char* date;
|
||||
qtime_s tm;
|
||||
};
|
||||
|
||||
struct uiInfo_s {
|
||||
UiContext uiDC;
|
||||
SavegameInfo savegameList[512];
|
||||
int savegameCount;
|
||||
savegameStatus_s savegameStatus;
|
||||
int timeIndex;
|
||||
int previousTimes[4];
|
||||
bool allowScriptMenuResponse;
|
||||
char savegameName[64];
|
||||
char savegameInfo[256];
|
||||
void* sshotImage;
|
||||
char sshotImageName[64];
|
||||
};
|
||||
|
||||
static_assert(sizeof(uiInfo_s) == 0x9C2C);
|
||||
|
||||
struct LocalizeEntry {
|
||||
const char* value;
|
||||
const char* name;
|
||||
@ -510,6 +1273,7 @@ union XAssetHeader {
|
||||
LocalizeEntry* localize;
|
||||
WeaponCompleteDef* weapon;
|
||||
RawFile* rawfile;
|
||||
StringTable* stringTable;
|
||||
void* data;
|
||||
};
|
||||
|
||||
|
@ -8,11 +8,16 @@ WEAK symbol<void(int channel, const char* fmt, ...)> Com_Printf{0x41BD20};
|
||||
WEAK symbol<void(int channel, const char* fmt, ...)> Com_PrintWarning{0x406320};
|
||||
WEAK symbol<void(int channel, const char* fmt, ...)> Com_PrintError{0x4C6980};
|
||||
WEAK symbol<void(int channel, const char* fmt, ...)> Com_DPrintf{0x42B1F0};
|
||||
WEAK symbol<void(int channel, const char* msg, int error)> Com_PrintMessage{
|
||||
0x456B70};
|
||||
WEAK symbol<void(errorParm_t code, const char* fmt, ...)> Com_Error{0x43DD90};
|
||||
WEAK symbol<void()> Com_OpenLogFile{0x603030};
|
||||
WEAK symbol<int(char* data_p)> Com_Compress{0x4316A0};
|
||||
WEAK symbol<void()> Com_EventLoop{0x4987C0};
|
||||
WEAK symbol<void()> Com_ServerPacketEvent{0x47FD30};
|
||||
WEAK symbol<void(const char* filename)> Com_BeginParseSession{0x4A5C90};
|
||||
WEAK symbol<void()> Com_EndParseSession{0x4D12C0};
|
||||
WEAK symbol<const char*(const char** data_p)> Com_Parse{0x486600};
|
||||
|
||||
WEAK symbol<const char*(const char* fmt, ...)> va{0x4869F0};
|
||||
|
||||
@ -31,6 +36,7 @@ WEAK symbol<bool()> Sys_IsServerThread{0x4590E0};
|
||||
WEAK symbol<bool()> Sys_IsDatabaseThread{0x4C9380};
|
||||
WEAK symbol<void(int valueIndex, void* data)> Sys_SetValue{0x483310};
|
||||
WEAK symbol<void(int msec)> Sys_Sleep{0x4CFBE0};
|
||||
WEAK symbol<void(const char* error, ...)> Sys_Error{0x40BFF0};
|
||||
|
||||
WEAK symbol<short(short l)> BigShort{0x40E7E0};
|
||||
WEAK symbol<short(short l)> ShortNoSwap{0x4261A0};
|
||||
@ -94,7 +100,23 @@ WEAK symbol<void(const char* error)> Scr_Error{0x4E9C50};
|
||||
WEAK symbol<void(const char* error)> Scr_ObjectError{0x470600};
|
||||
WEAK symbol<void(unsigned int paramIndex, const char* error)> Scr_ParamError{
|
||||
0x42C880};
|
||||
WEAK symbol<void()> Scr_ShutdownAllocNode{0x486BC0};
|
||||
WEAK symbol<unsigned int(const char* filename)> Scr_CreateCanonicalFilename{
|
||||
0x43A5E0};
|
||||
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)>
|
||||
FindVariable{0x4B78B0};
|
||||
WEAK symbol<void(unsigned int parentId, unsigned int unsignedValue)>
|
||||
RemoveVariable{0x4C2DD0};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int id)> FindObject{
|
||||
0x49A980};
|
||||
WEAK symbol<gentity_s*(scr_entref_t entref)> GetEntity{0x4678C0};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)>
|
||||
GetVariable{0x482290};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)>
|
||||
GetNewVariable{0x4F1990};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int id)> GetObject{
|
||||
0x4370B0};
|
||||
|
||||
WEAK symbol<unsigned int()> Scr_GetNumParam{0x4443F0};
|
||||
WEAK symbol<void()> Scr_ClearOutParams{0x4A3A00};
|
||||
@ -110,6 +132,7 @@ WEAK symbol<void(float value)> Scr_AddFloat{0x4986E0};
|
||||
WEAK symbol<int(unsigned int index)> Scr_GetType{0x464EE0};
|
||||
WEAK symbol<void(int func, const char* name)> Scr_RegisterFunction{0x4F59C0};
|
||||
WEAK symbol<unsigned int(unsigned int index)> Scr_GetFunc{0x438E10};
|
||||
WEAK symbol<int(const char* pos)> Scr_IsInOpcodeMemory{0x47D1D0};
|
||||
|
||||
WEAK symbol<char*(const char* filename, const char* extFilename,
|
||||
const char* codePos, bool archive)>
|
||||
@ -120,17 +143,35 @@ WEAK symbol<int(const char* filename, const char* name)> Scr_GetFunctionHandle{
|
||||
WEAK symbol<int(int handle, unsigned int paramcount)> Scr_ExecThread{0x41A2C0};
|
||||
WEAK symbol<void(unsigned __int16 handle)> Scr_FreeThread{0x4C44A0};
|
||||
|
||||
WEAK symbol<void(sval_u* parseData, unsigned char user)> ScriptParse{0x4956B0};
|
||||
WEAK symbol<void(sval_u* val, unsigned int filePosId, unsigned int fileCountId,
|
||||
unsigned int scriptId, PrecacheEntry* entries,
|
||||
int entriesCount)>
|
||||
ScriptCompile{0x4FFDA0};
|
||||
|
||||
WEAK symbol<char*(int len)> TempMalloc{0x4EA7C0};
|
||||
|
||||
// SL
|
||||
WEAK symbol<const char*(unsigned int stringValue)> SL_ConvertToString{0x40E990};
|
||||
WEAK symbol<void(unsigned int stringValue)> SL_AddRefToString{0x4C4BD0};
|
||||
WEAK symbol<void(unsigned int stringValue)> SL_RemoveRefToString{0x4698E0};
|
||||
|
||||
// NET
|
||||
WEAK symbol<const char*(netadr_t a)> NET_AdrToString{0x4BF490};
|
||||
WEAK symbol<const char*()> NET_ErrorString{0x430390};
|
||||
|
||||
// Memory
|
||||
WEAK symbol<void*(int size)> Hunk_AllocateTempMemory{0x492DF0};
|
||||
WEAK symbol<void*(int size, int alignment)> Hunk_AllocAlignInternal{0x486C40};
|
||||
WEAK symbol<void*(int size)> Hunk_AllocateTempMemoryHigh{0x403B40};
|
||||
WEAK symbol<HunkUser*(int maxSize, const char* name, bool fixed, int type)>
|
||||
Hunk_UserCreate{0x4F1A10};
|
||||
WEAK symbol<void*(HunkUser* user, int size, int alignment)> Hunk_UserAlloc{
|
||||
0x469410};
|
||||
|
||||
WEAK symbol<void(Statement_s* statement)> free_expression{0x436260};
|
||||
|
||||
WEAK symbol<void(void*)> _free{0x674BC5};
|
||||
|
||||
// Zone
|
||||
WEAK symbol<void*(int size)> Z_VirtualAllocInternal{0x4D9CF0};
|
||||
@ -150,6 +191,7 @@ WEAK symbol<void(XZoneInfo* zoneInfo, unsigned int zoneCount,
|
||||
DB_LoadXAssets{0x4CFC90};
|
||||
WEAK symbol<char*(const char* filename, char* buf, int size)> DB_ReadRawFile{
|
||||
0x46DA60};
|
||||
WEAK symbol<int(RawFile* rawfile)> DB_GetRawFileLen{0x4D2E60};
|
||||
|
||||
// FS
|
||||
WEAK symbol<int(const char* qpath, void** buffer)> _FS_ReadFile{0x4A5480};
|
||||
@ -215,16 +257,44 @@ WEAK symbol<int(const char* s0, const char* s1, int n)> I_strnicmp{0x491E60};
|
||||
|
||||
WEAK symbol<void(field_t* edit)> Field_Clear{0x45C350};
|
||||
|
||||
// String
|
||||
WEAK symbol<int(const char* string)> StringTable_HashString{0x498080};
|
||||
|
||||
// Vec3
|
||||
WEAK symbol<void(const float* v, float scale, const float* result)> Vec3Scale{
|
||||
0x429220};
|
||||
|
||||
// Variables
|
||||
WEAK symbol<CmdArgs> cmd_args{0x144FED0};
|
||||
WEAK symbol<CmdArgs> sv_cmd_args{0x145ABA0};
|
||||
WEAK symbol<gentity_s> g_entities{0xEAAC38};
|
||||
WEAK symbol<gclient_s> g_clients{0x10911E8};
|
||||
|
||||
WEAK symbol<scrVmPub_t> scrVmPub{0x190DDF0};
|
||||
WEAK symbol<scrVarPub_t> scrVarPub{0x18E7508};
|
||||
WEAK symbol<scrCompilePub_t> scrCompilePub{0x156BF88};
|
||||
WEAK symbol<scrCompileGlob_t> scrCompileGlob{0x158CFC8};
|
||||
WEAK symbol<scrAnimPub_t> scrAnimPub{0x156BB68};
|
||||
|
||||
WEAK symbol<bool> g_loadedImpureScript{0x168F308};
|
||||
WEAK symbol<char> g_EndPos{0x1912598};
|
||||
|
||||
WEAK symbol<unsigned char*> g_largeLocalBuf{0x195AAF8};
|
||||
|
||||
WEAK symbol<int> g_largeLocalPos{0x1963998};
|
||||
WEAK symbol<int> g_maxLargeLocalPos{0x195AAFC};
|
||||
|
||||
WEAK symbol<int> g_largeLocalRightPos{0x195AAE8};
|
||||
WEAK symbol<int> g_minLargeLocalRightPos{0x195AB00};
|
||||
|
||||
WEAK symbol<unsigned long> g_dwTlsIndex{0x1BFC750};
|
||||
|
||||
WEAK symbol<int> com_frameTime{0x145EC7C};
|
||||
|
||||
WEAK symbol<bool> cin_skippable{0x73264C};
|
||||
|
||||
WEAK symbol<int> com_fixedConsolePosition{0x145EC10};
|
||||
|
||||
WEAK symbol<field_t> g_consoleField{0x88C700};
|
||||
WEAK symbol<ConDrawInputGlob> conDrawInputGlob{0x86E788};
|
||||
WEAK symbol<Console> con{0x86ED88};
|
||||
@ -243,17 +313,9 @@ WEAK symbol<SOCKET> ip_socket{0x1A040C8};
|
||||
WEAK symbol<source_s*> sourceFiles{0x7440E8};
|
||||
WEAK symbol<int> numtokens{0x7441F0};
|
||||
|
||||
WEAK symbol<uiInfo_s> uiInfoArray{0x1920470};
|
||||
|
||||
WEAK symbol<void*> DB_GetXAssetSizeHandlers{0x733408};
|
||||
WEAK symbol<void*> DB_XAssetPool{0x7337F8};
|
||||
WEAK symbol<unsigned int> g_poolSize{0x733510};
|
||||
|
||||
WEAK symbol<unsigned char*> g_largeLocalBuf{0x195AAF8};
|
||||
|
||||
WEAK symbol<int> g_largeLocalPos{0x1963998};
|
||||
WEAK symbol<int> g_maxLargeLocalPos{0x195AAFC};
|
||||
|
||||
WEAK symbol<int> g_largeLocalRightPos{0x195AAE8};
|
||||
WEAK symbol<int> g_minLargeLocalRightPos{0x195AB00};
|
||||
|
||||
WEAK symbol<unsigned long> g_dwTlsIndex{0x1BFC750};
|
||||
} // namespace game
|
||||
|
@ -21,15 +21,25 @@
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#undef GetObject
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
#pragma comment(lib, "Crypt32.lib")
|
||||
|
118
src/common/utils/csv.cpp
Normal file
118
src/common/utils/csv.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include "csv.hpp"
|
||||
#include "io.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
namespace utils {
|
||||
csv::csv(const std::string& file, const bool is_file, const bool allow_comments)
|
||||
: valid_(false) {
|
||||
this->parse(file, is_file, allow_comments);
|
||||
}
|
||||
|
||||
std::size_t csv::get_rows() const noexcept { return this->data_map_.size(); }
|
||||
|
||||
std::size_t csv::get_columns() const {
|
||||
std::size_t count = 0;
|
||||
|
||||
for (std::size_t i = 0; i < this->get_rows(); ++i) {
|
||||
count = std::max(this->get_columns(i), count);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
std::size_t csv::get_columns(const std::size_t row) const {
|
||||
if (this->data_map_.size() > row) {
|
||||
return this->data_map_[row].size();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string csv::get_element_at(const std::size_t row,
|
||||
const std::size_t column) const {
|
||||
if (this->data_map_.size() > row) {
|
||||
const auto& data = this->data_map_[row];
|
||||
if (data.size() > column) {
|
||||
return data[column];
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool csv::is_valid() const noexcept { return this->valid_; }
|
||||
|
||||
void csv::parse(const std::string& file, const bool is_file,
|
||||
const bool allow_comments) {
|
||||
std::string buffer;
|
||||
|
||||
if (is_file) {
|
||||
if (io::read_file(file, &buffer) && !buffer.empty()) {
|
||||
this->valid_ = true;
|
||||
}
|
||||
} else {
|
||||
buffer = file;
|
||||
}
|
||||
|
||||
if (!buffer.empty()) {
|
||||
const auto rows = string::split(buffer, '\n');
|
||||
|
||||
for (auto& row : rows) {
|
||||
this->parse_row(row, allow_comments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void csv::parse_row(const std::string& row, const bool allow_comments) {
|
||||
bool is_string = false;
|
||||
std::string element;
|
||||
std::vector<std::string> data;
|
||||
char temp_char = '\0';
|
||||
|
||||
for (std::size_t i = 0; i < row.size(); ++i) {
|
||||
if (row[i] == ',' && !is_string) // Flush entry
|
||||
{
|
||||
data.push_back(element);
|
||||
element.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (row[i] == '"') // Start / Terminate string
|
||||
{
|
||||
is_string = !is_string;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i < (row.size() - 1) && row[i] == '\\' && row[i + 1] == '"' &&
|
||||
is_string) // Handle quotes in strings as \"
|
||||
{
|
||||
temp_char = '"';
|
||||
++i;
|
||||
} else if (!is_string && (row[i] == '\n' || row[i] == '\x0D' ||
|
||||
row[i] == '\x0A' || row[i] == '\t')) {
|
||||
continue;
|
||||
} else if (!is_string &&
|
||||
(row[i] == '#' ||
|
||||
(row[i] == '/' && (i + 1) < row.size() && row[i + 1] == '/')) &&
|
||||
allow_comments) {
|
||||
// Skip comments. I know CSVs usually don't have comments, but in this
|
||||
// case it's useful
|
||||
return;
|
||||
} else {
|
||||
temp_char = row[i];
|
||||
}
|
||||
|
||||
element.push_back(temp_char);
|
||||
}
|
||||
|
||||
// Push last element
|
||||
data.push_back(element);
|
||||
|
||||
if (data.empty() || (data.size() == 1 && data[0].empty())) // Skip empty rows
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->data_map_.push_back(data);
|
||||
}
|
||||
} // namespace utils
|
27
src/common/utils/csv.hpp
Normal file
27
src/common/utils/csv.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace utils {
|
||||
class csv {
|
||||
public:
|
||||
csv(const std::string& file, bool is_file = true, bool allow_comments = true);
|
||||
|
||||
[[nodiscard]] std::size_t get_rows() const noexcept;
|
||||
[[nodiscard]] std::size_t get_columns() const;
|
||||
[[nodiscard]] std::size_t get_columns(std::size_t row) const;
|
||||
|
||||
[[nodiscard]] std::string get_element_at(std::size_t row,
|
||||
std::size_t column) const;
|
||||
|
||||
[[nodiscard]] bool is_valid() const noexcept;
|
||||
|
||||
private:
|
||||
bool valid_;
|
||||
std::vector<std::vector<std::string>> data_map_;
|
||||
|
||||
void parse(const std::string& file, bool is_file = true,
|
||||
bool allow_comments = true);
|
||||
void parse_row(const std::string& row, bool allow_comments = true);
|
||||
};
|
||||
} // namespace utils
|
@ -57,10 +57,9 @@ std::vector<PIMAGE_SECTION_HEADER> library::get_section_headers() const {
|
||||
|
||||
for (uint16_t i = 0; i < nt_headers->FileHeader.NumberOfSections;
|
||||
++i, ++section) {
|
||||
if (section)
|
||||
if (section) {
|
||||
headers.push_back(section);
|
||||
else
|
||||
OutputDebugStringA("There was an invalid section :O");
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
@ -97,8 +96,9 @@ bool library::is_valid() const {
|
||||
}
|
||||
|
||||
std::string library::get_name() const {
|
||||
if (!this->is_valid())
|
||||
if (!this->is_valid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto path = this->get_path();
|
||||
const auto pos = path.find_last_of("/\\");
|
||||
@ -109,8 +109,9 @@ std::string library::get_name() const {
|
||||
}
|
||||
|
||||
std::string library::get_path() const {
|
||||
if (!this->is_valid())
|
||||
if (!this->is_valid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
char name[MAX_PATH] = {0};
|
||||
GetModuleFileNameA(this->module_, name, sizeof name);
|
||||
@ -119,8 +120,9 @@ std::string library::get_path() const {
|
||||
}
|
||||
|
||||
std::string library::get_folder() const {
|
||||
if (!this->is_valid())
|
||||
if (!this->is_valid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto path = std::filesystem::path(this->get_path());
|
||||
return path.parent_path().generic_string();
|
||||
@ -202,6 +204,15 @@ std::string library::get_dll_directory() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
bool is_wine() {
|
||||
static const auto has_wine_export = []() -> bool {
|
||||
const library ntdll("ntdll.dll");
|
||||
return ntdll.get_proc<void*>("wine_get_version");
|
||||
}();
|
||||
|
||||
return has_wine_export;
|
||||
}
|
||||
|
||||
void raise_hard_exception() {
|
||||
int data = false;
|
||||
const library ntdll("ntdll.dll");
|
||||
|
@ -101,6 +101,8 @@ private:
|
||||
HMODULE module_;
|
||||
};
|
||||
|
||||
bool is_wine();
|
||||
|
||||
__declspec(noreturn) void raise_hard_exception();
|
||||
std::string load_resource(int id);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user