Update gsc tool (#593)

* update premake and remove gsc-tool

* add new gsc-tool submodule

* new compiler but crashes [skip ci]

* lol [skip ci]

* tiny changes [skip ci]

* revert moving of gsc rawfile filter [skip ci]

* Revert "update all git submodules"

This reverts commit 33f03c7a80.

* fixes and improvements to gsc

* remove weapon reallocs for now

crashes for some reason

* fix build?

* fix build

* small fixes

* increase game memory

---------

Co-authored-by: m <mjkzyalt@gmail.com>
This commit is contained in:
quaK 2023-04-23 15:46:40 +03:00 committed by GitHub
parent e12ccd0e47
commit 57c04f4eb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 742 additions and 432 deletions

2
.gitmodules vendored
View File

@ -50,7 +50,7 @@
[submodule "deps/gsc-tool"]
path = deps/gsc-tool
url = https://github.com/xensik/gsc-tool.git
branch = xlabs
branch = dev
[submodule "deps/stb"]
path = deps/stb
url = https://github.com/nothings/stb.git

View File

@ -1,30 +0,0 @@
#include "stdafx.hpp"
#include <xsk/h1.hpp>
#include "interface.hpp"
namespace gsc
{
std::unique_ptr<xsk::gsc::compiler> compiler()
{
auto compiler = std::make_unique<xsk::gsc::h1::compiler>();
compiler->mode(xsk::gsc::build::prod);
return compiler;
}
std::unique_ptr<xsk::gsc::decompiler> decompiler()
{
return std::make_unique<xsk::gsc::h1::decompiler>();
}
std::unique_ptr<xsk::gsc::assembler> assembler()
{
return std::make_unique<xsk::gsc::h1::assembler>();
}
std::unique_ptr<xsk::gsc::disassembler> disassembler()
{
return std::make_unique<xsk::gsc::h1::disassembler>();
}
}

View File

@ -1,9 +0,0 @@
#pragma once
namespace gsc
{
std::unique_ptr<xsk::gsc::compiler> compiler();
std::unique_ptr<xsk::gsc::decompiler> decompiler();
std::unique_ptr<xsk::gsc::assembler> assembler();
std::unique_ptr<xsk::gsc::disassembler> disassembler();
}

2
deps/gsc-tool vendored

@ -1 +1 @@
Subproject commit 7d374025b7675bada64c247ebe9378dd335a33da
Subproject commit e05d853ba6448212166b18847b430768d99d39e3

View File

@ -1,5 +1,5 @@
gsc_tool = {
source = path.join(dependencies.basePath, "gsc-tool/src")
source = path.join(dependencies.basePath, "gsc-tool")
}
function gsc_tool.import()
@ -9,60 +9,54 @@ end
function gsc_tool.includes()
includedirs {
path.join(gsc_tool.source, "utils"),
path.join(gsc_tool.source, "h1"),
path.join(dependencies.basePath, "extra/gsc-tool") -- https://github.com/GEEKiDoS/open-teknomw3/blob/master/deps/extra/gsc-tool
path.join(gsc_tool.source, "include")
}
end
-- https://github.com/xensik/gsc-tool/blob/dev/premake5.lua#L95
function gsc_tool.project()
project "xsk-gsc-utils"
kind "StaticLib"
language "C++"
kind "StaticLib"
language "C++"
warnings "Off"
pchheader "stdafx.hpp"
pchsource(path.join(gsc_tool.source, "utils/stdafx.cpp"))
files {
path.join(gsc_tool.source, "include/xsk/utils/*.hpp"),
path.join(gsc_tool.source, "src/utils/*.cpp")
}
files {
path.join(gsc_tool.source, "utils/**.h"),
path.join(gsc_tool.source, "utils/**.hpp"),
path.join(gsc_tool.source, "utils/**.cpp")
}
includedirs {
path.join(gsc_tool.source, "include")
}
includedirs {
path.join(gsc_tool.source, "utils"),
gsc_tool.source
}
zlib.includes()
zlib.includes()
project "xsk-gsc-h1"
kind "StaticLib"
language "C++"
kind "StaticLib"
language "C++"
warnings "Off"
pchheader "stdafx.hpp"
pchsource(path.join(gsc_tool.source, "h1/stdafx.cpp"))
files {
path.join(gsc_tool.source, "h1/**.h"),
path.join(gsc_tool.source, "h1/**.hpp"),
path.join(gsc_tool.source, "h1/**.cpp"),
path.join(dependencies.basePath, "extra/gsc-tool/interface.cpp")
}
includedirs {
path.join(gsc_tool.source, "h1"),
gsc_tool.source,
path.join(dependencies.basePath, "extra/gsc-tool")
}
-- https://github.com/xensik/gsc-tool/blob/dev/premake5.lua#L25
-- adding these build options fixes a bunch of parser stuff
filter "action:vs*"
buildoptions "/bigobj"
buildoptions "/Zc:__cplusplus"
filter {}
files {
path.join(gsc_tool.source, "include/xsk/stdinc.hpp"),
path.join(gsc_tool.source, "include/xsk/gsc/engine/h1.hpp"),
path.join(gsc_tool.source, "src/gsc/engine/h1.cpp"),
path.join(gsc_tool.source, "src/gsc/engine/h1_code.cpp"),
path.join(gsc_tool.source, "src/gsc/engine/h1_func.cpp"),
path.join(gsc_tool.source, "src/gsc/engine/h1_meth.cpp"),
path.join(gsc_tool.source, "src/gsc/engine/h1_token.cpp"), path.join(gsc_tool.source, "src/gsc/*.cpp"),
path.join(gsc_tool.source, "src/gsc/common/*.cpp"),
path.join(gsc_tool.source, "include/xsk/gsc/common/*.hpp")
}
includedirs {
path.join(gsc_tool.source, "include")
}
end
table.insert(dependencies, gsc_tool)

View File

@ -28,7 +28,7 @@ namespace fastfiles
game::dvar_t* g_dump_scripts;
game::dvar_t* db_print_default_assets;
std::vector<HANDLE> fastfile_handles;
utils::concurrency::container<std::vector<HANDLE>> fastfile_handles;
bool is_mod_pre_gfx = false;
void db_try_load_x_file_internal(const char* zone_name, const int flags)
@ -226,7 +226,10 @@ namespace fastfiles
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr);
if (handle != INVALID_HANDLE_VALUE)
{
fastfile_handles.push_back(handle);
fastfile_handles.access([&](std::vector<HANDLE>& handles)
{
handles.push_back(handle);
});
}
return handle;
@ -1035,7 +1038,7 @@ namespace fastfiles
void reallocate_asset_pools()
{
reallocate_attachment_and_weapon();
//reallocate_attachment_and_weapon();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_XANIM, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_SOUND, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_LOADED_SOUND, 2>();
@ -1105,10 +1108,14 @@ namespace fastfiles
void close_fastfile_handles()
{
for (const auto& handle : fastfile_handles)
fastfile_handles.access([&](std::vector<HANDLE>& handles)
{
CloseHandle(handle);
}
for (const auto& handle : handles)
{
CloseHandle(handle);
}
});
}
void set_usermap(const std::string& usermap)

View File

@ -2,11 +2,15 @@
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "script_extension.hpp"
#include "script_error.hpp"
#include "component/scripting.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
using namespace utils::string;
namespace gsc
{
@ -18,6 +22,37 @@ namespace gsc
std::string unknown_function_error;
std::array<const char*, 27> var_typename =
{
"undefined",
"object",
"string",
"localized string",
"vector",
"float",
"int",
"codepos",
"precodepos",
"function",
"builtin function",
"builtin method",
"stack",
"animation",
"pre animation",
"thread",
"thread",
"thread",
"thread",
"struct",
"removed entity",
"entity",
"array",
"removed thread",
"<free>",
"thread list",
"endon list",
};
void scr_emit_function_stub(std::uint32_t filename, std::uint32_t thread_name, char* code_pos)
{
current_filename = filename;
@ -43,8 +78,7 @@ namespace gsc
{
const auto& pos = function.value();
unknown_function_error = std::format(
"while processing function '{}' in script '{}':\nunknown script '{}'",
pos.first, pos.second, scripting::current_file
"while processing function '{}' in script '{}':\nunknown script '{}'", pos.first, pos.second, scripting::current_file
);
}
else
@ -59,30 +93,192 @@ namespace gsc
const auto name = scripting::get_token(thread_name);
unknown_function_error = std::format(
"while processing script '{}':\nunknown function '{}::{}'",
scripting::current_file, filename, name
"while processing script '{}':\nunknown function '{}::{}'", scripting::current_file, filename, name
);
}
void unknown_function_stub(const char* code_pos)
void compile_error_stub(const char* code_pos, [[maybe_unused]] const char* msg)
{
get_unknown_function_error(code_pos);
game::Com_Error(game::ERR_DROP, "script link error\n%s",
unknown_function_error.data());
game::Com_Error(game::ERR_SCRIPT_DROP, "script link error\n%s", unknown_function_error.data());
}
std::uint32_t find_variable_stub(std::uint32_t parent_id, std::uint32_t thread_name)
{
const auto res = game::FindVariable(parent_id, thread_name);
if (!res)
{
get_unknown_function_error(thread_name);
game::Com_Error(game::ERR_DROP, "script link error\n%s",
unknown_function_error.data());
game::Com_Error(game::ERR_SCRIPT_DROP, "script link error\n%s", unknown_function_error.data());
}
return res;
}
unsigned int scr_get_object(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_POINTER)
{
return value->u.pointerValue;
}
scr_error(va("Type %s is not an object", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
unsigned int scr_get_const_string(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (game::Scr_CastString(value))
{
assert(value->type == game::VAR_STRING);
return value->u.stringValue;
}
game::Scr_ErrorInternal();
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
unsigned int scr_get_const_istring(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_ISTRING)
{
return value->u.stringValue;
}
scr_error(va("Type %s is not a localized string", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
void scr_validate_localized_string_ref(int parm_index, const char* token, int token_len)
{
assert(token);
assert(token_len >= 0);
if (token_len < 2)
{
return;
}
for (auto char_iter = 0; char_iter < token_len; ++char_iter)
{
if (!std::isalnum(static_cast<unsigned char>(token[char_iter])) && token[char_iter] != '_')
{
scr_error(va("Illegal localized string reference: %s must contain only alpha-numeric characters and underscores", token));
}
}
}
void scr_get_vector(unsigned int index, float* vector_value)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_VECTOR)
{
std::memcpy(vector_value, value->u.vectorValue, sizeof(std::float_t[3]));
return;
}
scr_error(va("Type %s is not a vector", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
}
int scr_get_int(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_INTEGER)
{
return value->u.intValue;
}
scr_error(va("Type %s is not an int", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
float scr_get_float(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_FLOAT)
{
return value->u.floatValue;
}
if (value->type == game::VAR_INTEGER)
{
return static_cast<float>(value->u.intValue);
}
scr_error(va("Type %s is not a float", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0.0f;
}
int scr_get_pointer_type(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
if ((game::scr_VmPub->top - index)->type == game::VAR_POINTER)
{
return static_cast<int>(game::GetObjectType((game::scr_VmPub->top - index)->u.uintValue));
}
scr_error(va("Type %s is not an object", var_typename[(game::scr_VmPub->top - index)->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
int scr_get_type(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
return (game::scr_VmPub->top - index)->type;
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
const char* scr_get_type_name(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
return var_typename[(game::scr_VmPub->top - index)->type];
}
scr_error(va("Parameter %u does not exist", index + 1));
return nullptr;
}
template <size_t rva>
void safe_func()
{
@ -128,11 +324,27 @@ namespace gsc
{
scr_emit_function_hook.create(SELECT_VALUE(0x3BD680_b, 0x504660_b), &scr_emit_function_stub);
utils::hook::call(SELECT_VALUE(0x3BD626_b, 0x504606_b), unknown_function_stub); // CompileError (LinkFile)
utils::hook::call(SELECT_VALUE(0x3BD672_b, 0x504652_b), unknown_function_stub); // ^
utils::hook::call(SELECT_VALUE(0x3BD75A_b, 0x50473A_b), find_variable_stub); // Scr_EmitFunction
utils::hook::call(SELECT_VALUE(0x3BD626_b, 0x504606_b), compile_error_stub); // CompileError (LinkFile)
utils::hook::call(SELECT_VALUE(0x3BD672_b, 0x504652_b), compile_error_stub); // ^
utils::hook::call(SELECT_VALUE(0x3BD75A_b, 0x50473A_b), find_variable_stub); // Scr_EmitFunction
safe_func<0xBA7A0>(); // fix vlobby cac crash
// Restore basic error messages for commonly used scr functions
utils::hook::jump(SELECT_VALUE(0x3C89F0_b, 0x50F9E0_b), scr_get_object);
utils::hook::jump(SELECT_VALUE(0x3C84C0_b, 0x50F560_b), scr_get_const_string);
utils::hook::jump(SELECT_VALUE(0x3C8280_b, 0x50F320_b), scr_get_const_istring);
utils::hook::jump(SELECT_VALUE(0x2D6950_b, 0x452EF0_b), scr_validate_localized_string_ref);
utils::hook::jump(SELECT_VALUE(0x3C8F30_b, 0x50FF20_b), scr_get_vector);
utils::hook::jump(SELECT_VALUE(0x3C8930_b, 0x50F920_b), scr_get_int);
utils::hook::jump(SELECT_VALUE(0x3C87D0_b, 0x50F870_b), scr_get_float);
utils::hook::jump(SELECT_VALUE(0x3C8C10_b, 0x50FC00_b), scr_get_pointer_type);
utils::hook::jump(SELECT_VALUE(0x3C8DE0_b, 0x50FDD0_b), scr_get_type);
utils::hook::jump(SELECT_VALUE(0x3C8E50_b, 0x50FE40_b), scr_get_type_name);
if (!game::environment::is_sp())
{
safe_func<0xBA7A0>(); // fix vlobby cac crash
}
}
void pre_destroy() override

View File

@ -1,4 +1,3 @@
#pragma once
namespace gsc

View File

@ -3,23 +3,17 @@
#include "game/dvars.hpp"
#include "game/game.hpp"
#include "game/scripting/execution.hpp"
#include "game/scripting/function.hpp"
#include "game/scripting/functions.hpp"
#include "game/scripting/lua/error.hpp"
#include <utils/hook.hpp>
#include "component/logfile.hpp"
#include "component/command.hpp"
#include "component/console.hpp"
#include "component/scripting.hpp"
#include "component/logfile.hpp"
#include <xsk/gsc/types.hpp>
#include <xsk/resolver.hpp>
#include "script_extension.hpp"
#include "script_error.hpp"
#include "script_extension.hpp"
#include "script_loading.hpp"
#include <utils/hook.hpp>
namespace gsc
{
@ -33,8 +27,8 @@ namespace gsc
namespace
{
std::unordered_map<std::uint32_t, script_function> functions;
std::unordered_map<std::uint32_t, script_method> methods;
std::unordered_map<std::uint16_t, script_function> functions;
std::unordered_map<std::uint16_t, script_method> methods;
bool force_error_print = false;
std::optional<std::string> gsc_error_msg;
@ -70,10 +64,15 @@ namespace gsc
reinterpret_cast<size_t>(pos - 2));
}
game::scr_entref_t get_entity_id_stub(std::uint32_t ent_id)
{
const auto ref = game::Scr_GetEntityIdRef(ent_id);
saved_ent_ref = ref;
return ref;
}
void execute_custom_function(const std::uint16_t id)
{
auto error = false;
try
{
const auto& function = functions[id];
@ -85,23 +84,33 @@ namespace gsc
return_value(result);
}
}
catch (const std::exception& e)
catch (const std::exception& ex)
{
error = true;
force_error_print = true;
gsc_error_msg = e.what();
scr_error(ex.what());
}
}
void vm_call_builtin_function_stub(builtin_function func)
{
const auto function_id = get_function_id();
const auto custom = functions.contains(static_cast<std::uint16_t>(function_id));
if (custom)
{
execute_custom_function(function_id);
return;
}
if (error)
if (func == nullptr)
{
game::Scr_ErrorInternal();
scr_error("function doesn't exist");
return;
}
func();
}
void execute_custom_method(const std::uint16_t id)
{
auto error = false;
try
{
const auto& method = methods[id];
@ -113,70 +122,29 @@ namespace gsc
return_value(result);
}
}
catch (const std::exception& e)
catch (const std::exception& ex)
{
error = true;
force_error_print = true;
gsc_error_msg = e.what();
}
if (error)
{
game::Scr_ErrorInternal();
scr_error(ex.what());
}
}
void vm_call_builtin_function_stub(builtin_function function)
void vm_call_builtin_method_stub(builtin_method meth)
{
const auto function_id = get_function_id();
if (!functions.contains(function_id))
const auto method_id = get_function_id();
const auto custom = methods.contains(static_cast<std::uint16_t>(method_id));
if (custom)
{
if (function == nullptr)
{
force_error_print = true;
gsc_error_msg = "function doesn't exist";
game::Scr_ErrorInternal();
}
else
{
function();
}
execute_custom_method(method_id);
return;
}
else
{
execute_custom_function(function_id);
}
}
game::scr_entref_t get_entity_id_stub(std::uint32_t ent_id)
{
const auto ref = game::Scr_GetEntityIdRef(ent_id);
saved_ent_ref = ref;
return ref;
}
void vm_call_builtin_method_stub(builtin_method method)
{
const auto function_id = get_function_id();
if (!methods.contains(function_id))
if (meth == nullptr)
{
if (method == nullptr)
{
force_error_print = true;
gsc_error_msg = "method doesn't exist";
game::Scr_ErrorInternal();
}
else
{
method(saved_ent_ref);
}
}
else
{
execute_custom_method(function_id);
scr_error("function doesn't exist");
return;
}
meth(saved_ent_ref);
}
void builtin_call_error(const std::string& error)
@ -185,13 +153,11 @@ namespace gsc
if (function_id > 0x1000)
{
console::warn("in call to builtin method \"%s\"%s",
xsk::gsc::h1::resolver::method_name(function_id).data(), error.data());
console::warn("in call to builtin method \"%s\"%s", gsc_ctx->meth_name(function_id).data(), error.data());
}
else
{
console::warn("in call to builtin function \"%s\"%s",
xsk::gsc::h1::resolver::function_name(function_id).data(), error.data());
console::warn("in call to builtin function \"%s\"%s", gsc_ctx->func_name(function_id).data(), error.data());
}
}
@ -199,7 +165,8 @@ namespace gsc
{
try
{
return {xsk::gsc::h1::resolver::opcode_name(opcode)};
const auto index = gsc_ctx->opcode_enum(opcode);
return { gsc_ctx->opcode_name(index) };
}
catch (...)
{
@ -227,7 +194,8 @@ namespace gsc
void vm_error_stub(int mark_pos)
{
if (!developer_script->current.enabled && !force_error_print)
const bool dev_script = developer_script ? developer_script->current.enabled : false;
if (!dev_script && !force_error_print)
{
utils::hook::invoke<void>(SELECT_VALUE(0x415C90_b, 0x59DDA0_b), mark_pos);
return;
@ -284,19 +252,27 @@ namespace gsc
}
}
void scr_error(const char* error)
{
force_error_print = true;
gsc_error_msg = error;
game::Scr_ErrorInternal();
}
namespace function
{
void add(const std::string& name, script_function function)
{
if (xsk::gsc::h1::resolver::find_function(name))
if (gsc_ctx->func_exists(name))
{
const auto id = xsk::gsc::h1::resolver::function_id(name);
const auto id = gsc_ctx->func_id(name);
functions[id] = function;
}
else
{
const auto id = ++function_id_start;
xsk::gsc::h1::resolver::add_function(name, static_cast<std::uint16_t>(id));
gsc_ctx->func_add(name, static_cast<std::uint16_t>(id));
functions[id] = function;
}
}
@ -306,15 +282,15 @@ namespace gsc
{
void add(const std::string& name, script_method method)
{
if (xsk::gsc::h1::resolver::find_method(name))
if (gsc_ctx->meth_exists(name))
{
const auto id = xsk::gsc::h1::resolver::method_id(name);
const auto id = gsc_ctx->meth_id(name);
methods[id] = method;
}
else
{
const auto id = ++method_id_start;
xsk::gsc::h1::resolver::add_method(name, static_cast<std::uint16_t>(id));
gsc_ctx->meth_add(name, static_cast<std::uint16_t>(id));
methods[id] = method;
}
}
@ -350,6 +326,8 @@ namespace gsc
public:
void post_unpack() override
{
developer_script = dvars::register_bool("developer_script", false, 0, "Enable developer script comments");
utils::hook::set<uint32_t>(SELECT_VALUE(0x3BD86C_b, 0x50484C_b), 0x1000); // change builtin func count
utils::hook::set<uint32_t>(SELECT_VALUE(0x3BD872_b, 0x504852_b) + 4,
@ -376,7 +354,7 @@ namespace gsc
utils::hook::nop(SELECT_VALUE(0x3CBA4E_b, 0x512AAE_b), 2);
utils::hook::call(SELECT_VALUE(0x3CBA46_b, 0x512AA6_b), vm_call_builtin_method_stub);
utils::hook::call(SELECT_VALUE(0x3CC9F3_b, 0x513A53_b), vm_error_stub);
utils::hook::call(SELECT_VALUE(0x3CC9F3_b, 0x513A53_b), vm_error_stub); // LargeLocalResetToMark
if (game::environment::is_dedi())
{
@ -442,7 +420,7 @@ namespace gsc
if (what.type != game::VAR_FUNCTION || with.type != game::VAR_FUNCTION)
{
throw std::runtime_error("replaceFunc: parameter 1 must be a function");
throw std::runtime_error("replacefunc: parameter 1 must be a function");
}
logfile::set_gsc_hook(what.u.codePosValue, with.u.codePosValue);
@ -473,8 +451,7 @@ namespace gsc
function::add("executecommand", [](const function_args& args)
{
const auto cmd = args[0].as<std::string>();
command::execute(cmd, false);
command::execute(args[0].as<std::string>(), false);
return scripting::script_value{};
});

View File

@ -34,6 +34,8 @@ namespace gsc
extern const game::dvar_t* developer_script;
void scr_error(const char* error);
namespace function
{
void add(const std::string& name, script_function function);

View File

@ -6,7 +6,7 @@
#include "component/filesystem.hpp"
#include "component/logfile.hpp"
#include "component/scripting.hpp"
#include "component/gsc/script_loading.hpp"
#include "component/memory.hpp"
#include "game/dvars.hpp"
@ -14,27 +14,22 @@
#include "game/scripting/execution.hpp"
#include "game/scripting/function.hpp"
#include <xsk/gsc/types.hpp>
#include <xsk/gsc/interfaces/compiler.hpp>
#include <xsk/gsc/interfaces/decompiler.hpp>
#include <xsk/gsc/interfaces/assembler.hpp>
#include <xsk/gsc/interfaces/disassembler.hpp>
#include <xsk/utils/compression.hpp>
#include <xsk/resolver.hpp>
#include <interface.hpp>
#include "script_extension.hpp"
#include "script_loading.hpp"
#include <utils/compression.hpp>
#include <utils/hook.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
namespace gsc
{
std::unique_ptr<xsk::gsc::h1::context> gsc_ctx = std::make_unique<xsk::gsc::h1::context>();;
namespace
{
auto compiler = ::gsc::compiler();
auto decompiler = ::gsc::decompiler();
auto assembler = ::gsc::assembler();
auto disassembler = ::gsc::disassembler();
utils::hook::detour scr_begin_load_scripts_hook;
utils::hook::detour scr_end_load_scripts_hook;
std::unordered_map<std::string, std::uint32_t> main_handles;
std::unordered_map<std::string, std::uint32_t> init_handles;
@ -46,7 +41,7 @@ namespace gsc
{
char* buf = nullptr;
char* pos = nullptr;
unsigned int size = 0x1000000;
const unsigned int size = memory::custom_script_mem_size;
} script_memory;
char* allocate_buffer(size_t size)
@ -83,14 +78,14 @@ namespace gsc
free_script_memory();
}
bool read_scriptfile(const std::string& name, std::string* data)
bool read_raw_script_file(const std::string& name, std::string* data)
{
if (filesystem::read_file(name, data))
{
return true;
}
const auto name_str = name.data();
const auto* name_str = name.data();
if (game::DB_XAssetExists(game::ASSET_TYPE_RAWFILE, name_str) &&
!game::DB_IsXAssetDefault(game::ASSET_TYPE_RAWFILE, name_str))
{
@ -113,9 +108,9 @@ namespace gsc
game::ScriptFile* load_custom_script(const char* file_name, const std::string& real_name)
{
if (loaded_scripts.contains(real_name))
if (const auto itr = loaded_scripts.find(real_name); itr != loaded_scripts.end())
{
return loaded_scripts[real_name];
return itr->second;
}
if (game::VirtualLobby_Loaded() && !force_load)
@ -123,28 +118,61 @@ namespace gsc
return nullptr;
}
std::string source_buffer;
if (!read_scriptfile(real_name + ".gsc", &source_buffer) || source_buffer.empty())
std::string source_buffer{};
if (!read_raw_script_file(real_name + ".gsc", &source_buffer) || source_buffer.empty())
{
return nullptr;
}
if (game::DB_XAssetExists(game::ASSET_TYPE_SCRIPTFILE, file_name) &&
// filter out "GSC rawfiles" that were used for development usage and are not meant for us.
// each "GSC rawfile" has a ScriptFile counterpart to be used instead
if (game::DB_XAssetExists(game::ASSET_TYPE_SCRIPTFILE, file_name) &&
!game::DB_IsXAssetDefault(game::ASSET_TYPE_SCRIPTFILE, file_name))
{
// filter out gsc rawfiles that contain developer code (has ScriptFile counterparts for ship, won't compile either)
if ((real_name.starts_with("maps/createfx") || real_name.starts_with("maps/createart") || real_name.starts_with("maps/mp"))
if ((real_name.starts_with("maps/createfx") || real_name.starts_with("maps/createart") || real_name.starts_with("maps/mp"))
&& (real_name.ends_with("_fx") || real_name.ends_with("_fog") || real_name.ends_with("_hdr")))
{
console::debug("Refusing to compile rawfile '%s'\n", real_name.data());
return game::DB_FindXAssetHeader(game::ASSET_TYPE_SCRIPTFILE, file_name, false).scriptfile;
}
}
auto data = std::vector<std::uint8_t>{source_buffer.begin(), source_buffer.end()};
console::debug("Loading custom gsc '%s.gsc'", real_name.data());
try
{
compiler->compile(real_name, data);
auto& compiler = gsc_ctx->compiler();
auto& assembler = gsc_ctx->assembler();
std::vector<std::uint8_t> data;
data.assign(source_buffer.begin(), source_buffer.end());
const auto assembly_ptr = compiler.compile(real_name, data);
const auto output_script = assembler.assemble(*assembly_ptr);
const auto bytecode = output_script.first; // formerly named "script"
const auto stack = output_script.second;
const auto script_file_ptr = static_cast<game::ScriptFile*>(scriptfile_allocator.allocate(sizeof(game::ScriptFile)));
script_file_ptr->name = file_name;
script_file_ptr->len = static_cast<int>(stack.size);
script_file_ptr->bytecodeLen = static_cast<int>(bytecode.size);
const auto stack_size = static_cast<std::uint32_t>(stack.size + 1);
const auto byte_code_size = static_cast<std::uint32_t>(bytecode.size + 1);
script_file_ptr->buffer = static_cast<char*>(scriptfile_allocator.allocate(stack_size));
std::memcpy(const_cast<char*>(script_file_ptr->buffer), stack.data, stack.size);
script_file_ptr->bytecode = allocate_buffer(byte_code_size);
std::memcpy(script_file_ptr->bytecode, bytecode.data, bytecode.size);
script_file_ptr->compressedLen = 0;
loaded_scripts[real_name] = script_file_ptr;
return script_file_ptr;
}
catch (const std::exception& e)
{
@ -153,46 +181,21 @@ namespace gsc
console::error("**********************************************\n");
return nullptr;
}
}
auto assembly = compiler->output();
try
std::string get_raw_script_file_name(const std::string& name)
{
if (name.ends_with(".gsh"))
{
assembler->assemble(real_name, assembly);
}
catch (const std::exception& e)
{
console::error("*********** script compile error *************\n");
console::error("failed to assemble '%s':\n%s", real_name.data(), e.what());
console::error("**********************************************\n");
return nullptr;
return name;
}
const auto script_file_ptr = scriptfile_allocator.allocate<game::ScriptFile>();
script_file_ptr->name = file_name;
const auto stack = assembler->output_stack();
script_file_ptr->len = static_cast<int>(stack.size());
const auto script = assembler->output_script();
script_file_ptr->bytecodeLen = static_cast<int>(script.size());
script_file_ptr->buffer = game::Hunk_AllocateTempMemoryHigh(stack.size() + 1);
std::memcpy(script_file_ptr->buffer, stack.data(), stack.size());
script_file_ptr->bytecode = allocate_buffer(script.size() + 1);
std::memcpy(script_file_ptr->bytecode, script.data(), script.size());
script_file_ptr->compressedLen = 0;
loaded_scripts[real_name] = script_file_ptr;
return script_file_ptr;
return name + ".gsc";
}
std::string get_script_file_name(const std::string& name)
{
const auto id = xsk::gsc::h1::resolver::token_id(name);
const auto id = gsc_ctx->token_id(name);
if (!id)
{
return name;
@ -201,7 +204,7 @@ namespace gsc
return std::to_string(id);
}
std::vector<std::uint8_t> decompile_scriptfile(const std::string& name, const std::string& real_name)
std::pair<xsk::gsc::buffer, std::vector<std::uint8_t>> read_compiled_script_file(const std::string& name, const std::string& real_name)
{
const auto* script_file = game::DB_FindXAssetHeader(game::ASSET_TYPE_SCRIPTFILE, name.data(), false).scriptfile;
if (script_file == nullptr)
@ -209,19 +212,17 @@ namespace gsc
throw std::runtime_error(std::format("Could not load scriptfile '{}'", real_name));
}
console::info("Decompiling scriptfile '%s'\n", real_name.data());
console::debug("Decompiling scriptfile '%s'\n", real_name.data());
std::vector<std::uint8_t> stack{script_file->buffer, script_file->buffer + script_file->len};
std::vector<std::uint8_t> bytecode{script_file->bytecode, script_file->bytecode + script_file->bytecodeLen};
const auto len = script_file->compressedLen;
const std::string stack{script_file->buffer, static_cast<std::uint32_t>(len)};
auto decompressed_stack = xsk::utils::zlib::decompress(stack, static_cast<std::uint32_t>(stack.size()));
const auto decompressed_stack = utils::compression::zlib::decompress(stack);
disassembler->disassemble(name, bytecode, decompressed_stack);
auto output = disassembler->output();
std::vector<std::uint8_t> stack_data;
stack_data.assign(decompressed_stack.begin(), decompressed_stack.end());
decompiler->decompile(name, output);
return decompiler->output();
return {{reinterpret_cast<std::uint8_t*>(script_file->bytecode), static_cast<std::uint32_t>(script_file->bytecodeLen)}, stack_data};
}
void load_script(const std::string& name)
@ -231,31 +232,31 @@ namespace gsc
return;
}
const auto main_handle = game::Scr_GetFunctionHandle(name.data(), xsk::gsc::h1::resolver::token_id("main"));
const auto init_handle = game::Scr_GetFunctionHandle(name.data(), xsk::gsc::h1::resolver::token_id("init"));
const auto main_handle = game::Scr_GetFunctionHandle(name.data(), gsc_ctx->token_id("main"));
const auto init_handle = game::Scr_GetFunctionHandle(name.data(), gsc_ctx->token_id("init"));
if (main_handle)
{
console::info("Loaded '%s::main'\n", name.data());
console::debug("Loaded '%s::main'\n", name.data());
main_handles[name] = main_handle;
}
if (init_handle)
{
console::info("Loaded '%s::init'\n", name.data());
console::debug("Loaded '%s::init'\n", name.data());
init_handles[name] = init_handle;
}
}
void load_scripts(const std::filesystem::path& root_dir, const std::filesystem::path& script_dir)
void load_scripts(const std::filesystem::path& root_dir, const std::filesystem::path& subfolder)
{
std::filesystem::path script_dir_path = root_dir / script_dir;
if (!utils::io::directory_exists(script_dir_path.generic_string()))
std::filesystem::path script_dir = root_dir / subfolder;
if (!utils::io::directory_exists(script_dir.generic_string()))
{
return;
}
const auto scripts = utils::io::list_files(script_dir_path.generic_string());
const auto scripts = utils::io::list_files(script_dir.generic_string());
for (const auto& script : scripts)
{
if (!script.ends_with(".gsc"))
@ -281,7 +282,7 @@ namespace gsc
return game::DB_IsXAssetDefault(type, name);
}
void gscr_load_gametype_script_stub(void* a1, void* a2)
void load_gametype_script_stub(void* a1, void* a2)
{
utils::hook::invoke<void>(SELECT_VALUE(0x2B9DA0_b, 0x18BC00_b), a1, a2);
@ -319,28 +320,48 @@ namespace gsc
return;
}
utils::hook::invoke<void>(SELECT_VALUE(0x1F1E00_b, 0x396080_b), rawfile, buf, size);
game::DB_GetRawBuffer(rawfile, buf, size);
}
void pmem_init_stub()
void scr_begin_load_scripts_stub()
{
utils::hook::invoke<void>(SELECT_VALUE(0x420260_b, 0x5A5590_b));
const bool dev_script = developer_script ? developer_script->current.enabled : false;
const auto comp_mode = dev_script ?
xsk::gsc::build::dev:
xsk::gsc::build::prod;
const auto type_0 = &game::g_scriptmem[0];
const auto type_1 = &game::g_scriptmem[1];
gsc_ctx->init(comp_mode, [](const std::string& include_name)
-> std::pair<xsk::gsc::buffer, std::vector<std::uint8_t>>
{
const auto real_name = get_raw_script_file_name(include_name);
const auto size_0 = 0x100000; // default size
const auto size_1 = 0x100000 + script_memory.size;
std::string file_buffer;
if (!read_raw_script_file(real_name, &file_buffer) || file_buffer.empty())
{
const auto name = get_script_file_name(include_name);
if (game::DB_XAssetExists(game::ASSET_TYPE_SCRIPTFILE, name.data()))
{
return read_compiled_script_file(name, real_name);
}
const auto block = reinterpret_cast<char*>(VirtualAlloc(NULL, size_0 + size_1, MEM_RESERVE, PAGE_READWRITE));
throw std::runtime_error(std::format("Could not load gsc file '{}'", real_name));
}
type_0->buf = block;
type_0->size = size_0;
std::vector<std::uint8_t> script_data;
script_data.assign(file_buffer.begin(), file_buffer.end());
type_1->buf = block + size_0;
type_1->size = size_1;
return {{}, script_data};
});
utils::hook::set<uint32_t>(SELECT_VALUE(0x420252_b, 0x5A5582_b), size_0 + size_1);
scr_begin_load_scripts_hook.invoke<void>();
}
void scr_end_load_scripts_stub()
{
// cleanup the compiler
gsc_ctx->cleanup();
scr_end_load_scripts_hook.invoke<void>();
}
}
@ -348,7 +369,7 @@ namespace gsc
{
for (auto& function_handle : main_handles)
{
console::info("Executing '%s::main'\n", function_handle.first.data());
console::debug("Executing '%s::main'\n", function_handle.first.data());
game::RemoveRefToObject(game::Scr_ExecThread(function_handle.second, 0));
}
}
@ -357,7 +378,7 @@ namespace gsc
{
for (auto& function_handle : init_handles)
{
console::info("Executing '%s::init'\n", function_handle.first.data());
console::debug("Executing '%s::init'\n", function_handle.first.data());
game::RemoveRefToObject(game::Scr_ExecThread(function_handle.second, 0));
}
}
@ -368,7 +389,7 @@ namespace gsc
const auto id = static_cast<std::uint16_t>(std::atoi(name));
if (id)
{
real_name = xsk::gsc::h1::resolver::token_name(id);
real_name = gsc_ctx->token_name(id);
}
auto* script = load_custom_script(name, real_name);
@ -385,49 +406,26 @@ namespace gsc
public:
void post_unpack() override
{
// allow custom scripts to include other custom scripts
xsk::gsc::h1::resolver::init([](const auto& include_name)
{
const auto real_name = include_name + ".gsc";
// Load our scripts with an uncompressed stack
utils::hook::call(SELECT_VALUE(0x3C7280_b, 0x50E3C0_b), db_get_raw_buffer_stub);
std::string file_buffer;
if (!read_scriptfile(real_name, &file_buffer) || file_buffer.empty())
{
const auto name = get_script_file_name(include_name);
if (game::DB_XAssetExists(game::ASSET_TYPE_SCRIPTFILE, name.data()))
{
return decompile_scriptfile(name, real_name);
}
else
{
throw std::runtime_error(std::format("Could not load gsc file '{}'", real_name));
}
}
scr_begin_load_scripts_hook.create(SELECT_VALUE(0x3BDB90_b, 0x504BC0_b), scr_begin_load_scripts_stub);
scr_end_load_scripts_hook.create(SELECT_VALUE(0x3BDCC0_b, 0x504CF0_b), scr_end_load_scripts_stub);
std::vector<std::uint8_t> result;
result.assign(file_buffer.begin(), file_buffer.end());
return result;
});
// hook xasset functions to return our own custom scripts
// ProcessScript: hook xasset functions to return our own custom scripts
utils::hook::call(SELECT_VALUE(0x3C7217_b, 0x50E357_b), find_script);
utils::hook::call(SELECT_VALUE(0x3C7227_b, 0x50E367_b), db_is_x_asset_default);
// GScr_LoadScripts
utils::hook::call(SELECT_VALUE(0x2BA152_b, 0x18C325_b), gscr_load_gametype_script_stub);
// GScr_LoadScripts: initial loading of scripts
utils::hook::call(SELECT_VALUE(0x2BA152_b, 0x18C325_b), load_gametype_script_stub);
// loads scripts with an uncompressed stack
utils::hook::call(SELECT_VALUE(0x3C7280_b, 0x50E3C0_b), db_get_raw_buffer_stub);
// Increase script memory
utils::hook::call(SELECT_VALUE(0x38639C_b, 0x15C4D6_b), pmem_init_stub);
// main is called from scripting.cpp
// init is called from scripting.cpp
scripting::on_shutdown([](bool free_scripts, bool post_shutdown)
{
if (free_scripts && post_shutdown)
{
xsk::gsc::h1::resolver::cleanup();
clear();
}
});

View File

@ -1,7 +1,10 @@
#pragma once
#include <xsk/gsc/engine/h1.hpp>
namespace gsc
{
extern std::unique_ptr<xsk::gsc::h1::context> gsc_ctx;
void load_main_handles();
void load_init_handles();
game::ScriptFile* find_script(game::XAssetType type, const char* name, int allow_create_default);

View File

@ -7,13 +7,7 @@
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <xsk/gsc/types.hpp>
#include <xsk/gsc/interfaces/compiler.hpp>
#include <xsk/gsc/interfaces/decompiler.hpp>
#include <xsk/gsc/interfaces/assembler.hpp>
#include <xsk/gsc/interfaces/disassembler.hpp>
#include <xsk/resolver.hpp>
#include <interface.hpp>
#include "gsc/script_loading.hpp"
namespace mapents
{
@ -128,7 +122,7 @@ namespace mapents
}
const auto key_ = key.substr(1, key.size() - 2);
const auto id = xsk::gsc::h1::resolver::token_id(key_);
const auto id = gsc::gsc_ctx->token_id(key_);
if (id == 0)
{
console::warn("[map_ents parser] Key '%s' not found, on line %i (%s)\n", key_.data(), i, line.data());

View File

@ -0,0 +1,143 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "memory.hpp"
#include "game/game.hpp"
#include <utils/hook.hpp>
#include <utils/flags.hpp>
namespace memory
{
namespace
{
constexpr auto mem_low_size = 0x80000000ui64 * 2; // default: 0x80000000
constexpr auto mem_high_size = 0x80000000ui64 * 2; // default: 0x80000000
constexpr auto script_mem_low_size = 0x100000ui64; // default: 0x100000
constexpr auto script_mem_high_size = 0x100000ui64 + custom_script_mem_size; // default: 0x100000
constexpr auto phys_mem_low_size = 0x700000000ui64; // default: 0x700000000
constexpr auto phys_mem_high_size = 0x300000000i64; // default: 0x300000000
constexpr auto pmem_alloc_size =
mem_low_size +
mem_high_size +
script_mem_low_size +
script_mem_high_size +
phys_mem_low_size +
phys_mem_high_size;
constexpr auto mem_low_buf = 0;
constexpr auto mem_high_buf = mem_low_buf + mem_low_size;
constexpr auto script_mem_low_buf = mem_high_buf + mem_high_size;
constexpr auto script_mem_high_buf = script_mem_low_buf + script_mem_low_size;
constexpr auto phys_mem_low_buf = script_mem_high_buf + script_mem_high_size;
constexpr auto phys_mem_high_buf = phys_mem_low_buf + phys_mem_low_size;
constexpr auto stream_mem_size = 0x2000000ui64;
void pmem_init()
{
const auto size = pmem_alloc_size;
const auto allocated_buffer = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_READWRITE);
auto buffer = reinterpret_cast<unsigned char*>(allocated_buffer);
*game::pmem_size = size;
*game::pmem_buffer = buffer;
memset(game::g_mem, 0, sizeof(*game::g_mem));
game::g_mem->prim[game::PHYS_ALLOC_LOW].buf = buffer + mem_low_buf;
game::g_mem->prim[game::PHYS_ALLOC_LOW].pos = mem_low_size;
game::g_mem->prim[game::PHYS_ALLOC_HIGH].buf = buffer + mem_high_buf;
game::g_mem->prim[game::PHYS_ALLOC_HIGH].pos = mem_high_size;
game::g_mem->prim[game::PHYS_ALLOC_LOW].unk1 = 0;
game::g_mem->prim[game::PHYS_ALLOC_HIGH].unk1 = 0;
memset(game::g_scriptmem, 0, sizeof(*game::g_scriptmem));
game::g_scriptmem->prim[game::PHYS_ALLOC_LOW].buf = buffer + script_mem_low_buf;
game::g_scriptmem->prim[game::PHYS_ALLOC_LOW].pos = script_mem_low_size;
game::g_scriptmem->prim[game::PHYS_ALLOC_HIGH].buf = buffer + script_mem_high_buf;
game::g_scriptmem->prim[game::PHYS_ALLOC_HIGH].pos = script_mem_high_size;
game::g_scriptmem->prim[game::PHYS_ALLOC_LOW].unk1 = 0;
game::g_scriptmem->prim[game::PHYS_ALLOC_HIGH].unk1 = 0;
memset(game::g_physmem, 0, sizeof(*game::g_physmem));
game::g_physmem->prim[game::PHYS_ALLOC_LOW].buf = buffer + phys_mem_low_buf;
game::g_physmem->prim[game::PHYS_ALLOC_LOW].pos = phys_mem_low_size;
game::g_physmem->prim[game::PHYS_ALLOC_HIGH].buf = buffer + phys_mem_high_buf;
game::g_physmem->prim[game::PHYS_ALLOC_HIGH].pos = phys_mem_high_size;
game::g_physmem->prim[game::PHYS_ALLOC_LOW].unk1 = 2;
game::g_physmem->prim[game::PHYS_ALLOC_HIGH].unk1 = 2;
*game::stream_size = stream_mem_size;
*game::stream_buffer = reinterpret_cast<unsigned char*>(VirtualAlloc(NULL, *game::stream_size, MEM_COMMIT, PAGE_READWRITE));
}
void pmem_init_stub()
{
// call our own init
pmem_init();
const auto script_mem_size = script_mem_low_size + script_mem_high_size;
utils::hook::set<uint32_t>(SELECT_VALUE(0x420252_b, 0x5A5582_b), static_cast<uint32_t>(script_mem_size));
}
}
namespace
{
int out_of_memory_text_stub(char* dest, int size, const char* fmt, ...)
{
fmt = "%s (%d)\n\n"
"Disable shader caching, lower graphic settings, free up RAM, or update your GPU drivers.\n\n"
"If this still occurs, try using the '-memoryfix' parameter to generate the 'players2' folder.";
char buffer[2048];
{
va_list ap;
va_start(ap, fmt);
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt, ap);
va_end(ap);
}
return utils::hook::invoke<int>(SELECT_VALUE(0x429200_b, 0x5AF0F0_b), dest, size, "%s", buffer);
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
// patch PMem_Init, so we can use whatever memory size we want
utils::hook::call(SELECT_VALUE(0x38639C_b, 0x15C4D6_b), pmem_init_stub);
// Com_sprintf for "Out of memory. You are probably low on disk space."
utils::hook::call(SELECT_VALUE(0x457BC9_b, 0x1D8E09_b), out_of_memory_text_stub);
// "fix" for rare 'Out of memory error' error
// this will *at least* generate the configs for mp/sp, which is the #1 issue
if (utils::flags::has_flag("memoryfix"))
{
utils::hook::jump(SELECT_VALUE(0x5110D0_b, 0x6200C0_b), malloc);
utils::hook::jump(SELECT_VALUE(0x510FF0_b, 0x61FFE0_b), _aligned_malloc);
utils::hook::jump(SELECT_VALUE(0x511130_b, 0x620120_b), free);
utils::hook::jump(SELECT_VALUE(0x511220_b, 0x620210_b), realloc);
utils::hook::jump(SELECT_VALUE(0x511050_b, 0x620040_b), _aligned_realloc);
}
}
};
}
REGISTER_COMPONENT(memory::component)

View File

@ -0,0 +1,6 @@
#pragma once
namespace memory
{
constexpr auto custom_script_mem_size = 0x1000000ui64;
}

View File

@ -244,26 +244,6 @@ namespace patches
}
}
int out_of_memory_text_stub(char* dest, int size, const char* fmt, ...)
{
fmt = "%s (%d)\n\n"
"Disable shader caching, lower graphic settings, free up RAM, or update your GPU drivers.\n\n"
"If this still occurs, try using the '-memoryfix' parameter to generate the 'players2' folder.";
char buffer[2048];
{
va_list ap;
va_start(ap, fmt);
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt, ap);
va_end(ap);
}
return utils::hook::invoke<int>(SELECT_VALUE(0x429200_b, 0x5AF0F0_b), dest, size, "%s", buffer);
}
void create_2d_texture_stub_1(const char* fmt, ...)
{
fmt = "Create2DTexture( %s, %i, %i, %i, %i ) failed\n\n"
@ -377,18 +357,6 @@ namespace patches
utils::hook::call(SELECT_VALUE(0x55E919_b, 0x681A69_b), create_2d_texture_stub_1); // Sys_Error for "Create2DTexture( %s, %i, %i, %i, %i ) failed"
utils::hook::call(SELECT_VALUE(0x55EACB_b, 0x681C1B_b), create_2d_texture_stub_2); // Com_Error for ^
utils::hook::call(SELECT_VALUE(0x5B35BA_b, 0x6CB1BC_b), swap_chain_stub); // Com_Error for "IDXGISwapChain::Present failed: %s"
utils::hook::call(SELECT_VALUE(0x457BC9_b, 0x1D8E09_b), out_of_memory_text_stub); // Com_sprintf for "Out of memory. You are probably low on disk space."
// "fix" for rare 'Out of memory error' error
// this will *at least* generate the configs for mp/sp, which is the #1 issue
if (utils::flags::has_flag("memoryfix"))
{
utils::hook::jump(SELECT_VALUE(0x5110D0_b, 0x6200C0_b), malloc);
utils::hook::jump(SELECT_VALUE(0x510FF0_b, 0x61FFE0_b), _aligned_malloc);
utils::hook::jump(SELECT_VALUE(0x511130_b, 0x620120_b), free);
utils::hook::jump(SELECT_VALUE(0x511220_b, 0x620210_b), realloc);
utils::hook::jump(SELECT_VALUE(0x511050_b, 0x620040_b), _aligned_realloc);
}
// Uncheat protect gamepad-related dvars
dvars::override::register_float("gpad_button_deadzone", 0.13f, 0, 1, game::DVAR_FLAG_SAVED);

View File

@ -252,12 +252,12 @@ namespace scripting
std::optional<std::string> get_canonical_string(const unsigned int id)
{
if (canonical_string_table.find(id) == canonical_string_table.end())
if (const auto itr = canonical_string_table.find(id); itr != canonical_string_table.end())
{
return {};
return itr->second;
}
return {canonical_string_table[id]};
return {};
}
class component final : public component_interface

View File

@ -39,7 +39,7 @@ namespace weapon
// precache items
for (std::size_t i = 0; i < weapons.size(); i++)
{
console::debug("precaching weapon \"%s\"\n", weapons[i]->name);
//console::debug("precaching weapon \"%s\"\n", weapons[i]->name);
game::G_GetWeaponForName(weapons[i]->name);
}
}

View File

@ -1,80 +1,79 @@
#include <std_include.hpp>
#include "functions.hpp"
#include "component/console.hpp"
#include "component/gsc/script_extension.hpp"
#include <xsk/gsc/types.hpp>
#include <xsk/resolver.hpp>
#include <utils/string.hpp>
#include "component/gsc/script_extension.hpp"
#include "component/gsc/script_loading.hpp"
namespace scripting
{
namespace
{
int find_function_index(const std::string& name, const bool prefer_global)
int find_function_index(const std::string& name, [[maybe_unused]] const bool prefer_global)
{
const auto target = utils::string::to_lower(name);
auto first = xsk::gsc::h1::resolver::function_id;
auto second = xsk::gsc::h1::resolver::method_id;
auto const& first = gsc::gsc_ctx->func_map();
auto const& second = gsc::gsc_ctx->meth_map();
if (!prefer_global)
{
std::swap(first, second);
if (const auto itr = second.find(name); itr != second.end())
{
return static_cast<int>(itr->second);
}
if (const auto itr = first.find(name); itr != first.end())
{
return static_cast<int>(itr->second);
}
}
const auto first_res = first(target);
if (first_res)
if (const auto itr = first.find(name); itr != first.end())
{
return first_res;
return static_cast<int>(itr->second);
}
const auto second_res = second(target);
if (second_res)
if (const auto itr = second.find(name); itr != second.end())
{
return second_res;
return static_cast<int>(itr->second);
}
return -1;
}
script_function get_function_by_index(const unsigned index)
{
if (index < 0x1000)
{
return reinterpret_cast<script_function*>(gsc::func_table)[index - 1];
}
return reinterpret_cast<script_function*>(gsc::meth_table)[index - 0x8000];
}
unsigned int parse_token_id(const std::string& name)
{
if (name.starts_with("_ID"))
{
return static_cast<unsigned int>(std::strtol(name.substr(3).data(), nullptr, 10));
}
if (name.starts_with("_id_"))
{
return static_cast<unsigned int>(std::strtol(name.substr(4).data(), nullptr, 16));
}
return 0;
}
}
std::string find_token(unsigned int id)
std::uint32_t parse_token_id(const std::string& name)
{
return xsk::gsc::h1::resolver::token_name(static_cast<std::uint16_t>(id));
if (name.starts_with("_ID"))
{
return static_cast<std::uint32_t>(std::strtol(name.substr(3).data(), nullptr, 10));
}
if (name.starts_with("_id_"))
{
return static_cast<std::uint32_t>(std::strtol(name.substr(4).data(), nullptr, 16));
}
return 0;
}
std::string find_token(std::uint32_t id)
{
return gsc::gsc_ctx->token_name(id);
}
std::string find_token_single(std::uint32_t id)
{
return gsc::gsc_ctx->token_name(id);
}
unsigned int find_token_id(const std::string& name)
{
const auto result = xsk::gsc::h1::resolver::token_id(name);
if (result)
const auto id = gsc::gsc_ctx->token_id(name);
if (id)
{
return result;
return id;
}
const auto parsed_id = parse_token_id(name);
@ -86,14 +85,24 @@ namespace scripting
return game::SL_GetCanonicalString(name.data());
}
script_function get_function_by_index(const std::uint32_t index)
{
static const auto function_table = &gsc::func_table;
static const auto method_table = &gsc::meth_table;
if (index < 0x1000)
{
return reinterpret_cast<script_function*>(function_table)[index - 1];
}
return reinterpret_cast<script_function*>(method_table)[index - 0x8000];
}
script_function find_function(const std::string& name, const bool prefer_global)
{
const auto index = find_function_index(name, prefer_global);
if (index < 0)
{
return nullptr;
}
if (index < 0) return nullptr;
return get_function_by_index(index);
}
}
}

View File

@ -5,8 +5,10 @@ namespace scripting
{
using script_function = void(*)(game::scr_entref_t);
std::string find_token(unsigned int id);
std::string find_token(std::uint32_t id);
std::string find_token_single(std::uint32_t id);
unsigned int find_token_id(const std::string& name);
script_function get_function_by_index(std::uint32_t index);
script_function find_function(const std::string& name, const bool prefer_global);
}

View File

@ -11,8 +11,7 @@
#include "component/fastfiles.hpp"
#include "component/scheduler.hpp"
#include <xsk/gsc/types.hpp>
#include <xsk/resolver.hpp>
#include "component/gsc/script_loading.hpp"
#include <utils/string.hpp>
#include <utils/io.hpp>
@ -217,7 +216,7 @@ namespace scripting::lua
auto entity_type = state.new_usertype<entity>("entity");
for (const auto& func : xsk::gsc::h1::resolver::get_methods())
for (auto const& func : gsc::gsc_ctx->meth_map())
{
const auto name = std::string(func.first);
entity_type[name] = [name](const entity& entity, const sol::this_state s, sol::variadic_args va)
@ -336,7 +335,7 @@ namespace scripting::lua
auto game_type = state.new_usertype<game>("game_");
state["game"] = game();
for (const auto& func : xsk::gsc::h1::resolver::get_functions())
for (auto const& func : gsc::gsc_ctx->func_map())
{
const auto name = std::string(func.first);
game_type[name] = [name](const game&, const sol::this_state s, sol::variadic_args va)

View File

@ -1844,6 +1844,13 @@ namespace game
int ingame_cursor_visible;
};
enum PMem_Direction
{
PHYS_ALLOC_LOW = 0x0,
PHYS_ALLOC_HIGH = 0x1,
PHYS_ALLOC_COUNT = 0x2,
};
enum PMem_Source
{
PMEM_SOURCE_EXTERNAL = 0x0,
@ -1852,19 +1859,38 @@ namespace game
PMEM_SOURCE_DEFAULT_HIGH = 0x3,
PMEM_SOURCE_MOVIE = 0x4,
PMEM_SOURCE_SCRIPT = 0x5,
PMEM_SOURCE_UNK5 = 0x5,
PMEM_SOURCE_UNK6 = 0x6,
PMEM_SOURCE_UNK7 = 0x7,
PMEM_SOURCE_UNK8 = 0x8,
PMEM_SOURCE_CUSTOMIZATION = 0x9,
};
struct physical_memory
struct PhysicalMemoryAllocation
{
char __pad0[0x10];
char* buf;
char __pad1[0x8];
int unk1;
size_t size;
char __pad2[0x500];
};
const char* name;
char __pad0[16];
unsigned __int64 pos;
char __pad1[8];
}; static_assert(sizeof(PhysicalMemoryAllocation) == 40);
static_assert(sizeof(physical_memory) == 0x530);
struct PhysicalMemoryPrim
{
const char* name;
unsigned int allocListCount;
char __pad0[4];
unsigned char* buf;
char __pad1[8];
int unk1;
char __pad2[4];
unsigned __int64 pos;
PhysicalMemoryAllocation allocList[32];
}; static_assert(sizeof(PhysicalMemoryPrim) == 1328);
struct PhysicalMemory
{
PhysicalMemoryPrim prim[2];
}; static_assert(sizeof(PhysicalMemory) == 0xA60);
namespace mp
{

View File

@ -96,6 +96,7 @@ namespace game
WEAK symbol<void(const char* gameName)> FS_Startup{0x40D890, 0x0};
WEAK symbol<void(const char* path, const char* dir)> FS_AddLocalizedGameDirectory{0x40B1E0, 0x1878F0};
WEAK symbol<unsigned int(unsigned int)> GetObjectType{0x3C3680, 0x50A810};
WEAK symbol<unsigned int(unsigned int, unsigned int)> GetVariable{0x3C3740, 0x50A8D0};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewVariable{0x3C3360, 0x50A4F0};
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewArrayVariable{0x3C31E0, 0x50A370};
@ -177,6 +178,7 @@ namespace game
WEAK symbol<float(int index)> Scr_GetFloat{0x3C87D0, 0x50F870};
WEAK symbol<const char*(int index)> Scr_GetString{0x3C8CC0, 0x50FCB0};
WEAK symbol<int()> Scr_GetNumParam{0x3C89E0, 0x50F9D0};
WEAK symbol<bool(VariableValue* value)> Scr_CastString{0x3C4450, 0x50B4B0};
WEAK symbol<void()> Scr_ClearOutParams{0x3C7EF0, 0x50F070};
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x3C6760, 0x50D8E0};
WEAK symbol<unsigned int(int classnum, unsigned int entnum)> Scr_GetEntityId{0x3C66B0, 0x50D830};
@ -307,7 +309,15 @@ namespace game
WEAK symbol<scrVmPub_t> scr_VmPub{0xC3F4E20, 0xB7AE3C0};
WEAK symbol<function_stack_t> scr_function_stack{0xC4015C0, 0xB7B8940};
WEAK symbol<physical_memory> g_scriptmem{0xD5F3140, 0xC92EC40};
WEAK game::symbol<unsigned __int64> pmem_size{0xD5F26D8, 0xC92E1D8};
WEAK game::symbol<unsigned char*> pmem_buffer{0xD5F26D0, 0xC92E1D0};
WEAK game::symbol<PhysicalMemory> g_mem{0xD5F26E0, 0xC92E1E0};
WEAK game::symbol<PhysicalMemory> g_scriptmem{0xD5F3140, 0xC92EC40};
WEAK game::symbol<PhysicalMemory> g_physmem{0xD5F3BA0, 0xC92F6A0};
WEAK game::symbol<unsigned __int64> stream_size{0x1DAD810, 0x258AA10};
WEAK game::symbol<unsigned char*> stream_buffer{0x1DAD808, 0x258AA08};
WEAK symbol<GfxDrawMethod_s> gfxDrawMethod{0xF7530B0, 0xE9213F0};

View File

@ -43,7 +43,7 @@ namespace utils
{
std::lock_guard _(this->mutex_);
const auto data = memory::allocate(length);
auto* data = memory::allocate(length);
this->pool_.push_back(data);
return data;
}
@ -57,7 +57,7 @@ namespace utils
{
std::lock_guard _(this->mutex_);
const auto data = memory::duplicate_string(string);
auto* data = memory::duplicate_string(string);
this->pool_.push_back(data);
return data;
}