From 6f95b55aa4825af5d8beec2c29897b781e712508 Mon Sep 17 00:00:00 2001 From: FutureRave Date: Sat, 3 Dec 2022 15:41:24 +0000 Subject: [PATCH] link errors --- .gitmodules | 4 + deps/extra/gsc-tool/interface.cpp | 28 + deps/extra/gsc-tool/interface.hpp | 9 + deps/gsc-tool | 1 + deps/premake/gsc-tool.lua | 65 +++ src/game/game.cpp | 65 ++- src/game/game.hpp | 8 +- src/game/scripting/executer.cpp | 37 +- src/game/scripting/executer.hpp | 2 - src/game/scripting/functions.cpp | 815 +++--------------------------- src/game/scripting/functions.hpp | 12 +- src/game/structs.hpp | 21 + src/module/gsc/script_error.cpp | 128 +++++ src/module/gsc/script_error.hpp | 6 + src/module/gsc/script_loading.cpp | 12 + src/module/gsc/script_loading.hpp | 6 + src/module/scripting.cpp | 380 +++++++++----- src/module/scripting.hpp | 12 + 18 files changed, 677 insertions(+), 934 deletions(-) create mode 100644 deps/extra/gsc-tool/interface.cpp create mode 100644 deps/extra/gsc-tool/interface.hpp create mode 160000 deps/gsc-tool create mode 100644 deps/premake/gsc-tool.lua create mode 100644 src/module/gsc/script_error.cpp create mode 100644 src/module/gsc/script_error.hpp create mode 100644 src/module/gsc/script_loading.cpp create mode 100644 src/module/gsc/script_loading.hpp create mode 100644 src/module/scripting.hpp diff --git a/.gitmodules b/.gitmodules index 07229b4..85554ed 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,3 +37,7 @@ [submodule "deps/minhook"] path = deps/minhook url = https://github.com/TsudaKageyu/minhook.git +[submodule "deps/gsc-tool"] + path = deps/gsc-tool + url = https://github.com/xensik/gsc-tool.git + branch = xlabs diff --git a/deps/extra/gsc-tool/interface.cpp b/deps/extra/gsc-tool/interface.cpp new file mode 100644 index 0000000..fe4482c --- /dev/null +++ b/deps/extra/gsc-tool/interface.cpp @@ -0,0 +1,28 @@ +#include +#include +#include "interface.hpp" + +namespace gsc +{ + std::unique_ptr compiler() + { + auto compiler = std::make_unique(); + compiler->mode(xsk::gsc::build::prod); + return compiler; + } + + std::unique_ptr decompiler() + { + return std::make_unique(); + } + + std::unique_ptr assembler() + { + return std::make_unique(); + } + + std::unique_ptr disassembler() + { + return std::make_unique(); + } +} diff --git a/deps/extra/gsc-tool/interface.hpp b/deps/extra/gsc-tool/interface.hpp new file mode 100644 index 0000000..133e6ae --- /dev/null +++ b/deps/extra/gsc-tool/interface.hpp @@ -0,0 +1,9 @@ +#pragma once + +namespace gsc +{ + std::unique_ptr compiler(); + std::unique_ptr decompiler(); + std::unique_ptr assembler(); + std::unique_ptr disassembler(); +} diff --git a/deps/gsc-tool b/deps/gsc-tool new file mode 160000 index 0000000..7d37402 --- /dev/null +++ b/deps/gsc-tool @@ -0,0 +1 @@ +Subproject commit 7d374025b7675bada64c247ebe9378dd335a33da diff --git a/deps/premake/gsc-tool.lua b/deps/premake/gsc-tool.lua new file mode 100644 index 0000000..a83f07e --- /dev/null +++ b/deps/premake/gsc-tool.lua @@ -0,0 +1,65 @@ +gsc_tool = { + source = path.join(dependencies.basePath, "gsc-tool/src"), +} + +function gsc_tool.import() + links { "xsk-gsc-iw5", "xsk-gsc-utils" } + gsc_tool.includes() +end + +function gsc_tool.includes() + includedirs { + path.join(gsc_tool.source, "iw5"), + path.join(gsc_tool.source, "utils"), + path.join(dependencies.basePath, "extra/gsc-tool"), + } +end + +function gsc_tool.project() + project "xsk-gsc-utils" + kind "StaticLib" + language "C++" + + pchheader "stdafx.hpp" + pchsource (path.join(gsc_tool.source, "utils/stdafx.cpp")) + + files { + path.join(gsc_tool.source, "utils/**.hpp"), + path.join(gsc_tool.source, "utils/**.cpp"), + } + + includedirs { + path.join(gsc_tool.source, "utils"), + gsc_tool.source, + } + + zlib.includes() + + project "xsk-gsc-iw5" + kind "StaticLib" + + language "C++" + cppdialect "C++20" + + filter "toolset:msc*" + buildoptions "/bigobj" + buildoptions "/Zc:__cplusplus" + filter {} + + pchheader "stdafx.hpp" + pchsource (path.join(gsc_tool.source, "iw5/stdafx.cpp")) + + files { + path.join(gsc_tool.source, "iw5/**.hpp"), + path.join(gsc_tool.source, "iw5/**.cpp"), + path.join(dependencies.basePath, "extra/gsc-tool/interface.cpp"), + } + + includedirs { + path.join(gsc_tool.source, "iw5"), + gsc_tool.source, + path.join(dependencies.basePath, "extra/gsc-tool"), + } +end + +table.insert(dependencies, gsc_tool) diff --git a/src/game/game.cpp b/src/game/game.cpp index 2a82dd2..091d30f 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -12,6 +12,7 @@ namespace game Com_Filter_t Com_Filter; DB_LoadXAssets_t DB_LoadXAssets; + DB_FindXAssetHeader_t DB_FindXAssetHeader; Dvar_RegisterBool_t Dvar_RegisterBool; Dvar_RegisterString_t Dvar_RegisterString; @@ -92,9 +93,6 @@ namespace game scrVarPub_t* scr_VarPub; scrVmPub_t* scr_VmPub; - scr_call_t* scr_instanceFunctions; - scr_call_t* scr_globalFunctions; - unsigned int* levelEntityId; int* g_script_error_level; @@ -412,16 +410,6 @@ namespace game return result; } - scr_call_t Scr_GetFunc(const unsigned int index) - { - if (index > 0x1C7) - { - return scr_instanceFunctions[index]; - } - - return scr_globalFunctions[index]; - } - __declspec(naked) void scr_notify_id_multiplayer(unsigned int id, unsigned int string_value, unsigned int paramcount) { @@ -543,6 +531,50 @@ namespace game return SL_GetStringOfSize(str, user, std::strlen(str) + 1, 7); } + unsigned int sl_get_canonical_string(const char* str) + { + static DWORD func = SELECT_VALUE(0x610750, 0x56B040, 0x0); + unsigned int result{}; + + __asm + { + pushad + mov edi, str + call func + mov result, eax + popad + } + + return result; + } + + unsigned int sl_get_canonical_dedicated(const char* str) + { + static DWORD func = 0x4F1620; + unsigned int result{}; + + __asm + { + pushad + mov esi, str + call func + mov result, eax + popad + } + + return result; + } + + unsigned int SL_GetCanonicalString(const char* str) + { + if (!is_dedi()) + { + return sl_get_canonical_string(str); + } + + return sl_get_canonical_dedicated(str); + } + __declspec(naked) void sv_send_client_game_state_mp(mp::client_t* /*client*/) { static DWORD func = 0x570FC0; @@ -989,6 +1021,7 @@ namespace game native::Com_Filter = native::Com_Filter_t(SELECT_VALUE(0x44EFF0, 0x5B7C30, 0x0)); native::DB_LoadXAssets = native::DB_LoadXAssets_t(SELECT_VALUE(0x48A8E0, 0x4CD020, 0x44F770)); + native::DB_FindXAssetHeader = native::DB_FindXAssetHeader_t(SELECT_VALUE(0x4FF000, 0x4CA620, 0x44E7A0)); native::Dvar_RegisterBool = native::Dvar_RegisterBool_t(SELECT_VALUE(0x4914D0, 0x5BE9F0, 0x0)); native::Dvar_RegisterString = native::Dvar_RegisterString_t(SELECT_VALUE(0x5197F0, 0x5BEC90, 0x0)); @@ -1084,12 +1117,6 @@ namespace game native::scr_VarPub = reinterpret_cast(SELECT_VALUE(0x0, 0x208E188, 0x1CD8720)); native::scr_VmPub = reinterpret_cast(SELECT_VALUE(0x1BF2580, 0x20B4A80, 0x1F5B080)); - native::scr_instanceFunctions = reinterpret_cast(SELECT_VALUE(0x184CDB0, 0x1D4F258, - 0x1BF59C8)); - native::scr_globalFunctions = reinterpret_cast(SELECT_VALUE(0x186C68C, 0x1D6EB34, - 0x1C152A4 - )); - native::g_script_error_level = reinterpret_cast(SELECT_VALUE(0x1BEFCFC, 0x20B21FC, 0x1F5B058)); native::g_script_error = reinterpret_cast(SELECT_VALUE(0x1BF1D18, 0x20B4218, 0x1F5A818)); diff --git a/src/game/game.hpp b/src/game/game.hpp index f64dbba..e028e45 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -26,6 +26,9 @@ namespace game typedef void (*DB_LoadXAssets_t)(XZoneInfo* zoneInfo, unsigned int zoneCount, int sync); extern DB_LoadXAssets_t DB_LoadXAssets; + typedef XAssetHeader (*DB_FindXAssetHeader_t)(XAssetType type, const char* name, int allowCreateDefault); + extern DB_FindXAssetHeader_t DB_FindXAssetHeader; + typedef const dvar_t* (*Dvar_RegisterBool_t)(const char* dvarName, bool value, unsigned __int16 flags, const char* description); extern Dvar_RegisterBool_t Dvar_RegisterBool; @@ -171,9 +174,6 @@ namespace game extern scrVarPub_t* scr_VarPub; extern scrVmPub_t* scr_VmPub; - extern scr_call_t* scr_instanceFunctions; - extern scr_call_t* scr_globalFunctions; - extern unsigned int* levelEntityId; extern int* g_script_error_level; @@ -272,7 +272,6 @@ namespace game const float* Scr_AllocVector(const float* v); void Scr_ClearOutParams(); scr_entref_t Scr_GetEntityIdRef(unsigned int id); - scr_call_t Scr_GetFunc(unsigned int index); void Scr_NotifyId(unsigned int id, unsigned int stringValue, unsigned int paramcount); int Scr_SetObjectField(unsigned int classnum, int entnum, int offset); void Scr_AddString(const char* value); @@ -280,6 +279,7 @@ namespace game const char* SL_ConvertToString(unsigned int stringValue); unsigned int SL_GetString(const char* str, unsigned int user); + unsigned int SL_GetCanonicalString(const char* str); void SV_SendClientGameState(mp::client_t* client); int SV_IsTestClient(int clientNum); diff --git a/src/game/scripting/executer.cpp b/src/game/scripting/executer.cpp index b37c802..4e02fc6 100644 --- a/src/game/scripting/executer.cpp +++ b/src/game/scripting/executer.cpp @@ -1,7 +1,8 @@ #include +#include "game/game.hpp" + #include -#include "game/game.hpp" #include "functions.hpp" #include "stack_isolation.hpp" #include "safe_executer.hpp" @@ -111,7 +112,7 @@ namespace game::scripting chaiscript::Boxed_Value executer::call(const std::string& function, const unsigned int entity_id, std::vector arguments) const { - const auto function_index = find_function_index(function, entity_id == 0); + const auto function_index = ::scripting::find_function_index(function, entity_id == 0); if (function_index < 0) { throw std::runtime_error("No function found for name '" + function + "'"); @@ -121,7 +122,7 @@ namespace game::scripting ? native::Scr_GetEntityIdRef(entity_id) : native::scr_entref_t{~0u}; - const auto function_ptr = native::Scr_GetFunc(function_index); + const auto function_ptr = ::scripting::get_function_by_index(function_index); stack_isolation _; @@ -142,34 +143,8 @@ namespace game::scripting return this->context_->get_parameters()->get_return_value(); } - int executer::find_function_index(const std::string& function, const bool prefer_global) + bool executer::function_exists(const std::string& function, bool prefer_global) { - const auto target = utils::string::to_lower(function); - - const auto primary_map = prefer_global - ? &global_function_map - : &instance_function_map; - const auto secondary_map = !prefer_global - ? &global_function_map - : &instance_function_map; - - auto function_entry = primary_map->find(target); - if (function_entry != primary_map->end()) - { - return function_entry->second; - } - - function_entry = secondary_map->find(target); - if (function_entry != secondary_map->end()) - { - return function_entry->second; - } - - return -1; - } - - bool executer::function_exists(const std::string& function, const bool prefer_global) - { - return find_function_index(function, prefer_global) >= 0; + return ::scripting::find_function_index(function, prefer_global) >= 0; } } diff --git a/src/game/scripting/executer.hpp b/src/game/scripting/executer.hpp index 8baa07d..8fab705 100644 --- a/src/game/scripting/executer.hpp +++ b/src/game/scripting/executer.hpp @@ -27,7 +27,5 @@ namespace game::scripting std::unordered_map> entity_fields_; int get_field_id(int classnum, const std::string& field) const; - - static int find_function_index(const std::string& function, bool prefer_global); }; } diff --git a/src/game/scripting/functions.cpp b/src/game/scripting/functions.cpp index 685a45c..1861179 100644 --- a/src/game/scripting/functions.cpp +++ b/src/game/scripting/functions.cpp @@ -1,748 +1,93 @@ #include #include "functions.hpp" -namespace game::scripting +#include + +#include +#include + +namespace scripting { - std::map instance_function_map = + int find_function_index(const std::string& name, const bool prefer_global) { - {"getviewmodel", 33457}, - {"fragbuttonpressed", 33458}, - {"secondaryoffhandbuttonpressed", 33459}, - {"getcurrentweaponclipammo", 33460}, - {"setvelocity", 33461}, - {"getplayerviewheight", 33462}, - {"enablemousesteer", 33545}, - {"getnormalizedmovement", 33463}, - {"getnormalizedcameramovement", 33486}, - {"giveweapon", 33487}, - {"takeweapon", 33488}, - {"takeallweapons", 33489}, - {"getcurrentweapon", 33490}, - {"getcurrentprimaryweapon", 33491}, - {"getcurrentoffhand", 33492}, - {"hasweapon", 33493}, - {"switchtoweapon", 33494}, - {"switchtoweaponimmediate", 33495}, - {"switchtooffhand", 33496}, - {"givestartammo", 33522}, - {"givemaxammo", 33523}, - {"getfractionstartammo", 33524}, - {"getfractionmaxammo", 33525}, - {"isdualwielding", 33526}, - {"isreloading", 33527}, - {"isswitchingweapon", 33528}, - {"setorigin", 33529}, - {"getvelocity", 33530}, - {"setplayerangles", 33531}, - {"getplayerangles", 33532}, - {"usebuttonpressed", 33533}, - {"attackbuttonpressed", 33534}, - {"adsbuttonpressed", 33535}, - {"meleebuttonpressed", 33536}, - {"playerads", 33537}, - {"isonground", 33538}, - {"isusingturret", 33539}, - {"setviewmodel", 33540}, - {"setoffhandprimaryclass", 33541}, - {"getoffhandprimaryclass", 33542}, - {"setoffhandsecondaryclass", 33497}, - {"getoffhandsecondaryclass", 33498}, - {"beginlocationselection", 33499}, - {"endlocationselection", 33500}, - {"disableweapons", 33501}, - {"enableweapons", 33502}, - {"disableoffhandweapons", 33503}, - {"enableoffhandweapons", 33504}, - {"disableweaponswitch", 33505}, - {"enableweaponswitch", 33506}, - {"openpopupmenu", 33507}, - {"openpopupmenunomouse", 33508}, - {"closepopupmenu", 33509}, - {"openmenu", 33510}, - {"closemenu", 33511}, - {"freezecontrols", 33513}, - {"disableusability", 33514}, - {"enableusability", 33515}, - {"setwhizbyspreads", 33516}, - {"setwhizbyradii", 33517}, - {"setreverb", 33518}, - {"deactivatereverb", 33519}, - {"setvolmod", 33520}, - {"setchannelvolume", 33521}, - {"setchannelvolumes", 33464}, - {"deactivatechannelvolumes", 33465}, - {"playlocalsound", 33466}, - {"stoplocalsound", 33467}, - {"setweaponammoclip", 33468}, - {"setweaponammostock", 33469}, - {"getweaponammoclip", 33470}, - {"getweaponammostock", 33471}, - {"anyammoforweaponmodes", 33472}, - {"setclientdvar", 33473}, - {"setclientdvars", 33474}, - {"allowads", 33475}, - {"allowjump", 33476}, - {"allowsprint", 33477}, - {"setspreadoverride", 33478}, - {"resetspreadoverride", 33479}, - {"setaimspreadmovementscale", 33480}, - {"setactionslot", 33481}, - {"setviewkickscale", 33482}, - {"getviewkickscale", 33483}, - {"getweaponslistall", 33484}, - {"getweaponslistprimaries", 33485}, - {"getweaponslistoffhands", 33430}, - {"getweaponslistitems", 33431}, - {"getweaponslistexclusives", 33432}, - {"getweaponslist", 33433}, - {"canplayerplacesentry", 33434}, - {"canplayerplacetank", 33435}, - {"visionsetnakedforplayer", 33436}, - {"visionsetnightforplayer", 33437}, - {"visionsetmissilecamforplayer", 33438}, - {"visionsetthermalforplayer", 33439}, - {"visionsetpainforplayer", 33440}, - {"setblurforplayer", 33441}, - {"getplayerweaponmodel", 33442}, - {"getplayerknifemodel", 33443}, - {"updateplayermodelwithweapons", 33444}, - {"notifyonplayercommand", 33445}, - {"canmantle", 33446}, - {"forcemantle", 33447}, - {"ismantling", 33448}, - {"playfx", 33449}, - {"recoilscaleon", 33450}, - {"recoilscaleoff", 33451}, - {"weaponlockstart", 33452}, - {"weaponlockfinalize", 33453}, - {"weaponlockfree", 33454}, - {"weaponlocktargettooclose", 33455}, - {"weaponlocknoclearance", 33390}, - {"visionsyncwithplayer", 33391}, - {"showhudsplash", 33392}, - {"setperk", 33393}, - {"hasperk", 33394}, - {"clearperks", 33395}, - {"unsetperk", 33396}, - {"noclip", 33397}, - {"ufo", 33398}, + const auto target = utils::string::to_lower(name); + auto first = xsk::gsc::iw5::resolver::function_id; + auto second = xsk::gsc::iw5::resolver::method_id; + if (!prefer_global) + { + std::swap(first, second); + } - // playercmd #2 - {"pingplayer", 33308}, - {"buttonpressed", 33309}, - {"sayall", 33310}, - {"sayteam", 33311}, - {"showscoreboard", 33312}, - {"setspawnweapon", 33313}, - {"dropitem", 33314}, - {"dropscavengerbag", 33315}, - {"finishplayerdamage", 33340}, - {"suicide", 33341}, - {"closeingamemenu", 33342}, - {"iprintln", 33343}, - {"iprintlnbold", 33344}, - {"spawn", 33345}, - {"setentertime", 33346}, - {"cloneplayer", 33347}, - {"istalking", 33348}, - {"allowspectateteam", 33349}, - {"getguid", 33350}, - {"getxuid", 33382}, - {"ishost", 33383}, - {"getspectatingplayer", 33384}, - {"predictstreampos", 33385}, - {"updatescores", 33386}, - {"updatedmscores", 33387}, - {"setrank", 33388}, - {"setcardtitle", 33389}, - {"setcardicon", 33420}, - {"setcardnameplate", 33421}, - {"setcarddisplayslot", 33422}, - {"regweaponforfxremoval", 33423}, - {"laststandrevive", 33424}, - {"setspectatedefaults", 33425}, - {"getthirdpersoncrosshairoffset", 33426}, - {"disableweaponpickup", 33427}, - {"enableweaponpickup", 33428}, + const auto first_res = first(target); + if (first_res) + { + return first_res; + } - // HECmd - {"settext", 32950}, - {"clearalltextafterhudelem", 32951}, - {"setshader", 32952}, - {"settargetent", 32953}, - {"cleartargetent", 32954}, - {"settimer", 32955}, - {"settimerup", 32956}, - {"settimerstatic", 32957}, - {"settenthstimer", 32958}, - {"settenthstimerup", 32959}, - {"settenthstimerstatic", 32960}, - {"setclock", 32961}, - {"setclockup", 32962}, - {"setvalue", 32963}, - {"setwaypoint", 32964}, - {"rotatingicon", 32965}, - {"secondaryarrow", 32891}, - {"setwaypointiconoffscreenonly", 32892}, - {"fadeovertime", 32893}, - {"scaleovertime", 32894}, - {"moveovertime", 32895}, - {"reset", 32896}, - {"destroy", 32897}, - {"setpulsefx", 32898}, - {"setplayernamestring", 32899}, - {"fadeovertime2", 33547}, - {"scaleovertime2", 33548}, - {"changefontscaleovertime", 32900}, + const auto second_res = second(target); + if (second_res) + { + return second_res; + } - // ScrCmd - {"attach", 32791}, - {"attachshieldmodel", 32792}, - {"detach", 32804}, - {"detachshieldmodel", 32805}, - {"moveshieldmodel", 32806}, - {"detachall", 32807}, - {"getattachsize", 32808}, - {"getattachmodelname", 32809}, - {"getattachtagname", 32810}, - {"getattachignorecollision", 32835}, - {"hidepart", 32836}, - {"allinstances", 32837}, - {"hideallparts", 32838}, - {"showpart", 32839}, - {"showallparts", 32840}, - {"linkto", 32841}, - {"linktoblendtotag", 32842}, - {"unlink", 32843}, - {"islinked", 32867}, - {"enablelinkto", 32868}, - {"playerlinkto", 32885}, - {"playerlinktodelta", 32886}, - {"playerlinkweaponviewtodelta", 32887}, - {"playerlinktoabsolute", 32888}, - {"playerlinktoblend", 32889}, - {"playerlinkedoffsetenable", 32890}, - {"playerlinkedoffsetdisable", 32916}, - {"playerlinkedsetviewznear", 32917}, - {"playerlinkedsetusebaseangleforviewclamp", 32918}, - {"lerpviewangleclamp", 32919}, - {"setviewangleresistance", 32920}, - {"geteye", 32921}, - {"istouching", 32922}, - {"stoploopsound", 32923}, - {"stopsounds", 32924}, - {"playrumbleonentity", 32925}, - {"playrumblelooponentity", 32926}, - {"stoprumble", 32927}, - {"delete", 32928}, - {"setmodel", 32929}, - {"laseron", 32930}, - {"laseroff", 32931}, - {"laseraltviewon", 32932}, - {"laseraltviewoff", 32933}, - {"thermalvisionon", 32934}, - {"thermalvisionoff", 32935}, - {"thermaldrawenable", 32803}, - {"thermaldrawdisable", 32768}, - {"thermalvisionfofoverlayon", 32936}, - {"thermalvisionfofoverlayoff", 32937}, - {"autospotoverlayon", 32938}, - {"autospotoverlayoff", 32939}, - {"setcontents", 32940}, - {"makeusable", 32941}, - {"makeunusable", 32942}, - {"setcursorhint", 32966}, - {"sethintstring", 32967}, - {"forceusehinton", 32968}, - {"forceusehintoff", 32969}, - {"makesoft", 32970}, - {"makehard", 32971}, - {"willneverchange", 32972}, - {"startfiring", 32973}, - {"stopfiring", 32974}, - {"isfiringturret", 32975}, - {"startbarrelspin", 32976}, - {"stopbarrelspin", 32977}, - {"getbarrelspinrate", 32978}, - {"remotecontrolturret", 32979}, - {"remotecontrolturretoff", 32980}, - {"shootturret", 32981}, - {"getturretowner", 32982}, - {"setsentryowner", 33006}, - {"setsentrycarrier", 33007}, - {"setturretminimapvisible", 33008}, - {"settargetentity", 33009}, - {"snaptotargetentity", 33010}, - {"cleartargetentity", 33011}, - {"getturrettarget", 33012}, - {"setplayerspread", 33013}, - {"setaispread", 33014}, - {"setsuppressiontime", 33015}, - {"setconvergencetime", 33049}, - {"setconvergenceheightpercent", 33050}, - {"setturretteam", 33051}, - {"maketurretsolid", 33052}, - {"maketurretoperable", 33053}, - {"maketurretinoperable", 33054}, - {"setturretaccuracy", 33082}, - {"setrightarc", 33083}, - {"setleftarc", 33084}, - {"settoparc", 33085}, - {"setbottomarc", 33086}, - {"setautorotationdelay", 33087}, - {"setdefaultdroppitch", 33088}, - {"restoredefaultdroppitch", 33089}, - {"turretfiredisable", 33090}, - {"turretfireenable", 33121}, - {"setturretmodechangewait", 33122}, - {"usetriggerrequirelookat", 33123}, - {"getstance", 33124}, - {"setstance", 33125}, - {"itemweaponsetammo", 33126}, - {"getammocount", 33127}, - {"gettagorigin", 33128}, - {"gettagangles", 33129}, - {"shellshock", 33130}, - {"stunplayer", 33131}, - {"stopshellshock", 33132}, - {"fadeoutshellshock", 33133}, - {"setdepthoffield", 33134}, - {"setviewmodeldepthoffield", 33135}, - {"setmotionblurmovescale", 33136}, - {"setmotionblurturnscale", 33168}, - {"setmotionblurzoomscale", 33169}, - {"viewkick", 33170}, - {"localtoworldcoords", 33171}, - {"getentitynumber", 33172}, - {"getentityvelocity", 33173}, - {"enablegrenadetouchdamage", 33174}, - {"disablegrenadetouchdamage", 33175}, - {"enableaimassist", 33176}, - {"disableaimassist", 33207}, - {"radiusdamage", 33208}, - {"detonate", 33209}, - {"damageconetrace", 33210}, - {"sightconetrace", 33211}, - {"settargetent", 33212}, - {"settargetpos", 33213}, - {"cleartarget", 33214}, - {"setflightmodedirect", 33215}, - {"setflightmodetop", 33216}, - {"getlightintensity", 33217}, - {"setlightintensity", 33218}, - {"isragdoll", 33219}, - {"setmovespeedscale", 33220}, - {"cameralinkto", 33221}, - {"cameraunlink", 33222}, - {"controlslinkto", 33251}, - {"controlsunlink", 33252}, - {"makevehiclesolidcapsule", 33253}, - {"makevehiclesolidsphere", 33254}, - {"remotecontrolvehicle", 33256}, - {"remotecontrolvehicleoff", 33257}, - {"isfiringvehicleturret", 33258}, - {"drivevehicleandcontrolturret", 33259}, - {"drivevehicleandcontrolturretoff", 33260}, - {"getplayersetting", 33261}, - {"getlocalplayerprofiledata", 33262}, - {"setlocalplayerprofiledata", 33263}, - {"remotecamerasoundscapeon", 33264}, - {"remotecamerasoundscapeoff", 33265}, - {"radarjamon", 33266}, - {"radarjamoff", 33267}, - {"setmotiontrackervisible", 33268}, - {"getmotiontrackervisible", 33269}, - {"circle", 33270}, - {"getpointinbounds", 33271}, - {"transfermarkstonewscriptmodel", 33272}, - {"setwatersheeting", 33273}, - {"setweaponhudiconoverride", 33274}, - {"getweaponhudiconoverride", 33275}, - {"setempjammed", 33276}, - {"playersetexpfog", 33277}, - {"isitemunlocked", 33278}, - {"getplayerdata", 33279}, - {"setplayerdata", 33306}, + return -1; + } - // Vehicle stuff - {"teleport", 0x824C}, - {"attachpath", 0x824D}, - {"getattachpos", 0x824E}, - {"startpath", 0x824F}, - {"setswitchnode", 0x8250}, - {"setwaitspeed", 0x8251}, - {"finishdamage", 0x8252}, - {"setspeed", 0x8253}, - {"setspeedimmediate", 0x8254}, - {"setwaitspeed", 0x8251}, - - {"setlookatent", 0x8237}, - {"clearlookatent", 0x8238}, - - {"setvehgoalpos", 33325}, - {"setturningability", 33381}, - - // some entity (script_model) stuff - {"moveto", 33399}, - {"movex", 33400}, - {"movey", 33401}, - {"movez", 33402}, - {"movegravity", 33403}, - {"moveslide", 33404}, - {"stopmoveslide", 33405}, - {"rotateto", 33406}, - {"rotatepitch", 33407}, - {"rotateyaw", 33408}, - {"rotateroll", 33409}, - {"addpitch", 33410}, - {"addyaw", 33411}, - {"addroll", 33412}, - {"vibrate", 33413}, - {"rotatevelocity", 33414}, - {"solid", 33415}, - {"notsolid", 33416}, - {"setcandamage", 33417}, - {"setcanradiusdamage", 33418}, - {"physicslaunchclient", 33419}, - {"physicslaunchserver", 33351}, - {"physicslaunchserveritem", 33352}, - {"clonebrushmodeltoscriptmodel", 33353}, - {"scriptmodelplayanim", 33354}, - {"scriptmodelclearanim", 33355}, - - // varied ent/player script commands - {"getorigin", 32910}, - {"useby", 32914}, - {"playsound", 32915}, - {"playsoundasmaster", 32878}, - {"playsoundtoteam", 32771}, - {"playsoundtoplayer", 32772}, - {"playloopsound", 32879}, - {"getnormalhealth", 32884}, - {"setnormalhealth", 32844}, - {"show", 32847}, - {"hide", 32848}, - {"playerhide", 32773}, - {"showtoplayer", 32774}, - {"enableplayeruse", 32775}, - {"disableplayeruse", 32776}, - {"setscriptmoverkillcam", 33546}, - {"makescrambler", 32777}, - {"makeportableradar", 32778}, - {"maketrophysystem", 32779}, - {"setmode", 32864}, - {"getmode", 32865}, - {"placespawnpoint", 32780}, - {"setteamfortrigger", 32781}, - {"clientclaimtrigger", 32782}, - {"clientreleasetrigger", 32783}, - {"releaseclaimedtrigger", 32784}, - {"isusingonlinedataoffline", 32785}, - {"getrestedtime", 32786}, - {"sendleaderboards", 32787}, - {"logstring", 32800}, - {"isonladder", 32788}, - {"startragdoll", 32798}, - {"getcorpseanim", 32789}, - {"playerforcedeathanim", 32790}, - {"startac130", 33543}, - {"stopac130", 33544}, - }; - - std::map global_function_map = + std::uint32_t parse_token_id(const std::string& name) { - // global stuff #1 - {"iprintln", 362}, - {"iprintlnbold", 363}, - {"logstring", 364}, - {"getent", 365}, - {"getentarray", 366}, - {"spawnplane", 367}, - {"spawnstruct", 368}, - {"spawnhelicopter", 369}, - {"isalive", 370}, - {"isspawner", 371}, - {"createattractorent", 372}, - {"createattractororigin", 373}, - {"createrepulsorent", 374}, - {"createrepulsororigin", 375}, - {"deleteattractor", 376}, - {"playsoundatpos", 377}, - {"newhudelem", 378}, - {"newclienthudelem", 379}, - {"newteamhudelem", 380}, - {"resettimeout", 381}, - {"precachefxteamthermal", 382}, - {"isplayer", 383}, - {"isplayernumber", 384}, - {"setsunlight", 57}, - {"resetsunlight", 58}, - {"setwinningplayer", 385}, - {"setwinningteam", 311}, - {"announcement", 312}, - {"clientannouncement", 313}, - {"getteamscore", 314}, - {"setteamscore", 315}, - {"setclientnamemode", 316}, - {"updateclientnames", 317}, - {"getteamplayersalive", 318}, - {"logprint", 319}, - {"worldentnumber", 320}, - {"obituary", 321}, - {"positionwouldtelefrag", 322}, - {"canspawn", 323}, - {"getstarttime", 324}, - {"precachestatusicon", 325}, - {"precacheminimapicon", 327}, - {"precachempanim", 328}, - {"restart", 329}, - {"exitlevel", 330}, - {"addtestclient", 331}, - {"makedvarserverinfo", 332}, - {"setarchive", 333}, - {"allclientsprint", 334}, - {"clientprint", 335}, - {"mapexists", 336}, - {"isvalidgametype", 337}, - {"matchend", 338}, - {"setplayerteamrank", 339}, - {"endparty", 340}, - {"setteamradar", 341}, - {"getteamradar", 342}, - {"setteamradarstrength", 343}, - {"getteamradarstrength", 344}, - {"getuavstrengthmin", 345}, - {"getuavstrengthmax", 262}, - {"getuavstrengthlevelneutral", 263}, - {"getuavstrengthlevelshowenemyfastsweep", 264}, - {"getuavstrengthlevelshowenemydirectional", 265}, - {"blockteamradar", 266}, - {"unblockteamradar", 267}, - {"isteamradarblocked", 268}, - {"getassignedteam", 269}, - {"setmatchdata", 270}, - {"getmatchdata", 271}, - {"sendmatchdata", 272}, - {"clearmatchdata", 273}, - {"setmatchdatadef", 274}, - {"setmatchclientip", 275}, - {"setmatchdataid", 276}, - {"setclientmatchdata", 277}, - {"getclientmatchdata", 278}, - {"setclientmatchdatadef", 279}, - {"sendclientmatchdata", 280}, - {"getbuildversion", 281}, - {"getbuildnumber", 282}, - {"getsystemtime", 283}, - {"getmatchrulesdata", 284}, - {"isusingmatchrulesdata", 285}, - {"kick", 286}, - {"issplitscreen", 287}, - {"setmapcenter", 288}, - {"setgameendtime", 289}, - {"visionsetnaked", 290}, - {"visionsetnight", 291}, - {"visionsetmissilecam", 292}, - {"visionsetthermal", 217}, - {"visionsetpain", 218}, - {"endlobby", 219}, - {"ambience", 220}, - {"getmapcustom", 221}, - {"updateskill", 222}, - {"spawnsighttrace", 223}, + if (name.starts_with("_ID")) + { + return static_cast(std::strtol(name.substr(3).data(), nullptr, 10)); + } - // global stuff #2 - {"setprintchannel", 14}, - {"print", 15}, - {"println", 16}, - {"print3d", 17}, - {"line", 18}, - {"spawnturret", 19}, - {"canspawnturret", 20}, - {"assert", 21}, - {"assertex", 38}, - {"assertmsg", 39}, - {"isdefined", 40}, - {"isstring", 41}, - {"setdvar", 42}, - {"setdynamicdvar", 43}, - {"setdvarifuninitialized", 44}, - {"setdevdvar", 45}, - {"setdevdvarifuninitialized", 46}, - {"getdvar", 47}, - {"getdvarint", 48}, - {"getdvarfloat", 49}, - {"getdvarvector", 50}, - {"gettime", 51}, - {"getentbynum", 52}, - {"getweaponmodel", 53}, - {"getweaponhidetags", 81}, - {"getanimlength", 82}, - {"animhasnotetrack", 83}, - {"getnotetracktimes", 84}, - {"spawn", 85}, - {"spawnloopsound", 86}, - {"bullettrace", 87}, - {"bullettracepassed", 88}, - {"sighttracepassed", 116}, - {"physicstrace", 117}, - {"physicstracenormal", 118}, - {"playerphysicstrace", 119}, - {"getgroundposition", 120}, - {"getmovedelta", 121}, - {"getangledelta", 122}, - {"getnorthyaw", 123}, - {"setnorthyaw", 150}, - {"setslowmotion", 151}, - {"randomint", 152}, - {"randomfloat", 153}, - {"randomintrange", 154}, - {"randomfloatrange", 155}, - {"sin", 156}, - {"cos", 157}, - {"tan", 158}, - {"asin", 159}, - {"acos", 160}, - {"atan", 161}, - {"int", 162}, - {"float", 163}, - {"abs", 164}, - {"min", 165}, - {"max", 198}, - {"floor", 199}, - {"ceil", 200}, - {"exp", 201}, - {"log", 202}, - {"sqrt", 203}, - {"squared", 204}, - {"clamp", 205}, - {"angleclamp", 206}, - {"angleclamp180", 207}, - {"vectorfromlinetopoint", 208}, - {"pointonsegmentnearesttopoint", 209}, - {"distance", 210}, - {"distance2d", 211}, - {"distancesquared", 212}, - {"length", 213}, - {"lengthsquared", 214}, - {"closer", 215}, - {"vectordot", 216}, - {"vectornormalize", 246}, - {"vectortoangles", 247}, - {"vectortoyaw", 248}, - {"vectorlerp", 249}, - {"anglestoup", 250}, - {"anglestoright", 251}, - {"anglestoforward", 252}, - {"combineangles", 253}, - {"transformmove", 254}, - {"issubstr", 255}, - {"isendstr", 256}, - {"getsubstr", 257}, - {"tolower", 258}, - {"strtok", 259}, - {"stricmp", 260}, - {"ambientplay", 261}, - {"ambientstop", 293}, - {"precachemodel", 294}, - {"precacheshellshock", 295}, - {"precacheitem", 296}, - {"precacheshader", 297}, - {"precachestring", 298}, - {"precachemenu", 299}, - {"precacherumble", 300}, - {"precachelocationselector", 301}, - {"precacheleaderboards", 302}, - {"precacheheadicon", 326}, - {"loadfx", 303}, - {"playfx", 304}, - {"playfxontag", 305}, - {"stopfxontag", 306}, - {"playloopedfx", 307}, - {"spawnfx", 308}, - {"triggerfx", 309}, - {"playfxontagforclients", 310}, - {"physicsexplosionsphere", 346}, - {"physicsexplosioncylinder", 347}, - {"physicsjolt", 348}, - {"physicsjitter", 349}, - {"setexpfog", 350}, - {"isexplosivedamagemod", 351}, - {"radiusdamage", 352}, - {"setplayerignoreradiusdamage", 353}, - {"glassradiusdamage", 354}, - {"earthquake", 355}, - {"getnumparts", 356}, - {"getpartname", 386}, - {"weaponfiretime", 387}, - {"weaponclipsize", 388}, - {"weaponisauto", 389}, - {"weaponissemiauto", 390}, - {"weaponisboltaction", 391}, - {"weaponinheritsperks", 392}, - {"weaponburstcount", 393}, - {"weapontype", 394}, - {"weaponclass", 395}, - {"weaponinventorytype", 437}, - {"weaponstartammo", 438}, - {"weaponmaxammo", 439}, - {"weaponaltweaponname", 440}, - {"isweaponcliponly", 441}, - {"isweapondetonationtimed", 442}, - {"weaponhasthermalscope", 443}, - {"getvehiclenode", 444}, - {"getvehiclenodearray", 445}, - {"getallvehiclenodes", 446}, - {"getnumvehicles", 447}, - {"precachevehicle", 448}, - {"spawnvehicle", 449}, - {"getarray", 450}, - {"getspawnerarray", 408}, - {"playrumbleonposition", 409}, - {"playrumblelooponposition", 410}, - {"stopallrumbles", 411}, - {"soundexists", 412}, - {"openfile", 413}, - {"closefile", 414}, - {"fprintln", 415}, - {"fprintfields", 416}, - {"freadln", 417}, - {"fgetarg", 418}, - {"setminimap", 419}, - {"setthermalbodymaterial", 420}, - {"getarraykeys", 421}, - {"getfirstarraykey", 422}, - {"getnextarraykey", 396}, - {"sortbydistance", 397}, - {"tablelookup", 398}, - {"tablelookupbyrow", 399}, - {"tablelookupistring", 400}, - {"tablelookupistringbyrow", 401}, - {"tablelookuprownum", 402}, - {"getmissileowner", 403}, - {"magicbullet", 404}, - {"getweaponflashtagname", 405}, - {"averagepoint", 406}, - {"averagenormal", 407}, - {"getglass", 423}, - {"getglassarray", 424}, - {"getglassorigin", 425}, - {"isglassdestroyed", 426}, - {"destroyglass", 427}, - {"deleteglass", 428}, - {"getentchannelscount", 429}, + return 0; + } - // objective - {"objective_add", 431}, - {"objective_delete", 432}, - {"objective_state", 433}, - {"objective_icon", 434}, - {"objective_position", 435}, - {"objective_current", 436}, - {"objective_onentity", 357}, - {"objective_team", 358}, - {"objective_player", 359}, - {"objective_playerteam", 360}, - {"objective_playerenemyteam", 361}, - }; + std::string find_token(std::uint32_t id) + { + return xsk::gsc::iw5::resolver::token_name(static_cast(id)); + } + + std::string find_token_single(std::uint32_t id) + { + return xsk::gsc::iw5::resolver::token_name(static_cast(id)); + } + + unsigned int find_token_id(const std::string& name) + { + const auto id = xsk::gsc::iw5::resolver::token_id(name); + if (id) + { + return id; + } + + const auto parsed_id = parse_token_id(name); + if (parsed_id) + { + return parsed_id; + } + + return game::native::SL_GetCanonicalString(name.data()); + } + + game::native::scr_call_t get_function_by_index(const std::uint32_t index) + { + static const auto function_table = SELECT_VALUE(0x186C68C, 0x1D6EB34, 0x1C152A4); + static const auto method_table = SELECT_VALUE(0x184CDB0, 0x1D4F258, 0x1BF59C8); + + if (index < 0x1C7) + { + return reinterpret_cast(function_table)[index - 1]; + } + + return reinterpret_cast(method_table)[index]; + } + + game::native::scr_call_t find_function(const std::string& name, const bool prefer_global) + { + const auto index = find_function_index(name, prefer_global); + if (index < 0) return nullptr; + + return get_function_by_index(index); + } } diff --git a/src/game/scripting/functions.hpp b/src/game/scripting/functions.hpp index d9e6174..6efcb68 100644 --- a/src/game/scripting/functions.hpp +++ b/src/game/scripting/functions.hpp @@ -1,7 +1,13 @@ #pragma once +#include "game/game.hpp" -namespace game::scripting +namespace scripting { - extern std::map instance_function_map; - extern std::map global_function_map; + 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); + + int find_function_index(const std::string& name, bool prefer_global); + game::native::scr_call_t get_function_by_index(std::uint32_t index); + game::native::scr_call_t find_function(const std::string& name, bool prefer_global); } diff --git a/src/game/structs.hpp b/src/game/structs.hpp index 4bc64e4..ca4d9b9 100644 --- a/src/game/structs.hpp +++ b/src/game/structs.hpp @@ -374,6 +374,27 @@ namespace game }; #pragma pack(pop) + enum XAssetType + { + ASSET_TYPE_SCRIPTFILE = 0x27, + }; + + struct ScriptFile + { + const char* name; + int compressedLen; + int len; + int bytecodeLen; + const char* buffer; + unsigned char* bytecode; + }; + + union XAssetHeader + { + void* data; + ScriptFile* scriptfile; + }; + enum errorParm_t { ERR_FATAL = 0x0, diff --git a/src/module/gsc/script_error.cpp b/src/module/gsc/script_error.cpp new file mode 100644 index 0000000..4639437 --- /dev/null +++ b/src/module/gsc/script_error.cpp @@ -0,0 +1,128 @@ +#include +#include +#include "game/game.hpp" + +#include "script_error.hpp" + +#include "module/scripting.hpp" + +#include +#include + +using namespace utils::string; + +namespace gsc +{ + namespace + { + utils::hook::detour scr_emit_function_hook; + + unsigned int current_filename = 0; + + std::string unknown_function_error; + + void scr_emit_function_stub(unsigned int filename, unsigned int thread_name, char* code_pos) + { + current_filename = filename; + scr_emit_function_hook.invoke(filename, thread_name, code_pos); + } + + std::string get_filename_name() + { + const auto filename_str = game::native::SL_ConvertToString(current_filename); + const auto id = std::strtol(filename_str, nullptr, 10); + if (!id) + { + return filename_str; + } + + return scripting::get_token(id); + } + + void get_unknown_function_error(const char* code_pos) + { + const auto function = find_function(code_pos); + if (function.has_value()) + { + const auto& pos = function.value(); + unknown_function_error = std::format( + "while processing function '{}' in script '{}':\nunknown script '{}'", pos.first, pos.second, scripting::current_file + ); + } + else + { + unknown_function_error = std::format("unknown script '{}'", scripting::current_file); + } + } + + void get_unknown_function_error(unsigned int thread_name) + { + const auto filename = get_filename_name(); + const auto name = scripting::get_token(thread_name); + + unknown_function_error = std::format( + "while processing script '{}':\nunknown function '{}::{}'", scripting::current_file, filename, name + ); + } + + void compile_error_stub(const char* code_pos, [[maybe_unused]] const char* msg) + { + get_unknown_function_error(code_pos); + game::native::Com_Error(game::native::ERR_DROP, "script link error\n%s", unknown_function_error.data()); + } + + unsigned int find_variable_stub(unsigned int parent_id, unsigned int thread_name) + { + const auto res = utils::hook::invoke(SELECT_VALUE(0x4C4E70, 0x5651F0, 0x0), parent_id, thread_name); + if (!res) + { + get_unknown_function_error(thread_name); + game::native::Com_Error(game::native::ERR_DROP, "script link error\n%s", unknown_function_error.data()); + } + + return res; + } + } + + std::optional> find_function(const char* pos) + { + for (const auto& file : scripting::script_function_table_sort) + { + for (auto i = file.second.begin(); i != file.second.end() && std::next(i) != file.second.end(); ++i) + { + const auto next = std::next(i); + if (pos >= i->second && pos < next->second) + { + return {std::make_pair(i->first, file.first)}; + } + } + } + + return {}; + } + + class error final : public module + { + public: + void post_load() override + { + if (game::is_dedi()) + { + return; + } + + scr_emit_function_hook.create(SELECT_VALUE(0x40DCB0, 0x561400, 0x0), &scr_emit_function_stub); + + utils::hook(SELECT_VALUE(0x60DABA, 0x5615FA, 0x0), &compile_error_stub, HOOK_CALL).install()->quick(); + utils::hook(SELECT_VALUE(0x60DAD1, 0x561611, 0x0), &compile_error_stub, HOOK_CALL).install()->quick(); + utils::hook(SELECT_VALUE(0x40DCFA, 0x56144A, 0x0), &find_variable_stub, HOOK_CALL).install()->quick(); + } + + void pre_destroy() override + { + scr_emit_function_hook.clear(); + } + }; +} + +REGISTER_MODULE(gsc::error) diff --git a/src/module/gsc/script_error.hpp b/src/module/gsc/script_error.hpp new file mode 100644 index 0000000..284af43 --- /dev/null +++ b/src/module/gsc/script_error.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace gsc +{ + std::optional> find_function(const char* pos); +} diff --git a/src/module/gsc/script_loading.cpp b/src/module/gsc/script_loading.cpp new file mode 100644 index 0000000..e60ec04 --- /dev/null +++ b/src/module/gsc/script_loading.cpp @@ -0,0 +1,12 @@ +#include +#include "game/game.hpp" + +#include "script_loading.hpp" + +namespace gsc +{ + game::native::ScriptFile* find_script(game::native::XAssetType type, const char* name, int allow_create_default) + { + return game::native::DB_FindXAssetHeader(type, name, allow_create_default).scriptfile; + } +} diff --git a/src/module/gsc/script_loading.hpp b/src/module/gsc/script_loading.hpp new file mode 100644 index 0000000..e84756a --- /dev/null +++ b/src/module/gsc/script_loading.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace gsc +{ + game::native::ScriptFile* find_script(game::native::XAssetType type, const char* name, int allow_create_default); +} diff --git a/src/module/scripting.cpp b/src/module/scripting.cpp index b3a8420..d390965 100644 --- a/src/module/scripting.cpp +++ b/src/module/scripting.cpp @@ -4,171 +4,271 @@ #include #include "game/scripting/context.hpp" +#include "game/scripting/functions.hpp" + #include "scheduler.hpp" +#include "scripting.hpp" -class scripting final : public module +#include "gsc/script_loading.hpp" + +namespace scripting { -public: - void post_load() override + std::unordered_map> script_function_table; + std::unordered_map>> script_function_table_sort; + std::unordered_map> script_function_table_rev; + + std::string current_file; + + namespace { - start_hook_.initialize(SELECT_VALUE(0x50C575, 0x50D4F2, 0x48A026), &start_execution_stub, HOOK_CALL) // - ->install() // - ->quick(); + std::uint32_t current_file_id = 0; + std::string current_script_file; - stop_hook_.initialize(SELECT_VALUE(0x528B04, 0x569E46, 0x4F03FA), &stop_execution_stub, HOOK_CALL) // - ->install() // - ->quick(); - - utils::hook(SELECT_VALUE(0x4F9706, 0x5772A0, 0x4FAB88), &frame_stub, HOOK_CALL).install()->quick(); - utils::hook(SELECT_VALUE(0x4FFA48, 0x5774AB, 0x4FEFD7), &frame_stub, HOOK_CALL).install()->quick(); - // Only relevant one? - - utils::hook(SELECT_VALUE(0x6109F3, 0x56B637, 0x4EDFF7), &vm_notify_stub, HOOK_CALL).install()->quick(); - utils::hook(SELECT_VALUE(0x6128BE, 0x56D541, 0x4EFAF9), &vm_notify_stub, HOOK_CALL).install()->quick(); - - if (game::is_sp()) + std::string get_token(unsigned int id) { - utils::hook(0x50C57E, &frame_stub, HOOK_CALL).install()->quick(); - utils::hook(0x6523A3, &frame_stub, HOOK_CALL).install()->quick(); - utils::hook(0x5145D2, &frame_stub, HOOK_CALL).install()->quick(); - - utils::hook(0x610970, &vm_notify_stub, HOOK_JUMP).install()->quick(); - } - } - - void pre_destroy() override - { - this->scripts_.clear(); - } - -private: - std::vector> scripts_; - - void load_scripts() - { - const auto script_dir = "open-iw5/scripts/"s; - - if (!utils::io::directory_exists(script_dir)) - { - return; + return find_token(id); } - const auto scripts = utils::io::list_files(script_dir); - - for (const auto& script : scripts) + void add_function_sort(unsigned int id, const char* pos) { - if (script.substr(script.find_last_of('.') + 1) == "chai") + std::string filename = current_file; + if (current_file_id) { - try + filename = get_token(current_file_id); + } + + if (!script_function_table_sort.contains(filename)) + { + auto* script = gsc::find_script(game::native::ASSET_TYPE_SCRIPTFILE, current_script_file.data(), false); + if (script) { - auto context = std::make_unique(); - context->get_chai()->eval_file(script); - this->scripts_.push_back(std::move(context)); + const auto* end = &script->bytecode[script->bytecodeLen]; + script_function_table_sort[filename].emplace_back("__end__", reinterpret_cast(end)); } - catch (chaiscript::exception::eval_error& e) + } + + const auto name = scripting::get_token(id); + auto& itr = script_function_table_sort[filename]; + itr.insert(itr.end() - 1, { name, pos }); + } + + void add_function(const std::string& file, unsigned int id, const char* pos) + { + const auto name = get_token(id); + script_function_table[file][name] = pos; + script_function_table_rev[pos] = { file, name }; + } + + void scr_set_thread_position(unsigned int thread_name, const char* code_pos) + { + add_function_sort(thread_name, code_pos); + + if (current_file_id) + { + const auto name = get_token(current_file_id); + add_function(name, thread_name, code_pos); + } + else + { + add_function(current_file, thread_name, code_pos); + } + + utils::hook::invoke(SELECT_VALUE(0x4845F0, 0x5616D0, 0x0), thread_name, code_pos); + } + + void process_script(const char* filename) + { + current_script_file = filename; + + const auto file_id = std::strtol(filename, nullptr, 10); + if (file_id) + { + current_file_id = file_id; + } + else + { + current_file_id = 0; + current_file = filename; + } + + utils::hook::invoke(SELECT_VALUE(0x446850, 0x56B130, 0x0), filename); + } + } + + std::string get_token(unsigned int id) + { + return find_token(id); + } + + class scripting_class final : public module + { + public: + void post_load() override + { + start_hook_.initialize(SELECT_VALUE(0x50C575, 0x50D4F2, 0x48A026), &start_execution_stub, HOOK_CALL) // + ->install() // + ->quick(); + + stop_hook_.initialize(SELECT_VALUE(0x528B04, 0x569E46, 0x4F03FA), &stop_execution_stub, HOOK_CALL) // + ->install() // + ->quick(); + + utils::hook(SELECT_VALUE(0x4F9706, 0x5772A0, 0x4FAB88), &frame_stub, HOOK_CALL).install()->quick(); + utils::hook(SELECT_VALUE(0x4FFA48, 0x5774AB, 0x4FEFD7), &frame_stub, HOOK_CALL).install()->quick(); + // Only relevant one? + + utils::hook(SELECT_VALUE(0x6109F3, 0x56B637, 0x4EDFF7), &vm_notify_stub, HOOK_CALL).install()->quick(); + utils::hook(SELECT_VALUE(0x6128BE, 0x56D541, 0x4EFAF9), &vm_notify_stub, HOOK_CALL).install()->quick(); + + if (game::is_sp()) + { + utils::hook(0x50C57E, &frame_stub, HOOK_CALL).install()->quick(); + utils::hook(0x6523A3, &frame_stub, HOOK_CALL).install()->quick(); + utils::hook(0x5145D2, &frame_stub, HOOK_CALL).install()->quick(); + + utils::hook(0x610970, &vm_notify_stub, HOOK_JUMP).install()->quick(); + } + + if (game::is_dedi()) return; + + utils::hook(SELECT_VALUE(0x44690A, 0x56B1EA, 0x0), &scr_set_thread_position, HOOK_CALL).install()->quick(); + utils::hook(SELECT_VALUE(0x4232A8, 0x561748, 0x0), &process_script, HOOK_CALL).install()->quick(); + } + + void pre_destroy() override + { + this->scripts_.clear(); + } + + private: + std::vector> scripts_; + + void load_scripts() + { + const auto script_dir = "open-iw5/scripts/"s; + + if (!utils::io::directory_exists(script_dir)) + { + return; + } + + const auto scripts = utils::io::list_files(script_dir); + + for (const auto& script : scripts) + { + if (script.substr(script.find_last_of('.') + 1) == "chai") { - throw std::runtime_error(e.pretty_print()); + try + { + auto context = std::make_unique(); + context->get_chai()->eval_file(script); + this->scripts_.push_back(std::move(context)); + } + catch (chaiscript::exception::eval_error& e) + { + throw std::runtime_error(e.pretty_print()); + } } } } - } - void start_execution() - { - try + void start_execution() { - this->load_scripts(); - } - catch (std::exception& e) - { - propagate_error(e); - } - } - - void stop_execution() - { - this->scripts_.clear(); - } - - void run_frame() - { - for (const auto& script : this->scripts_) - { - script->get_scheduler()->run_frame(); - } - } - - void dispatch(game::scripting::event* event) - { - for (const auto& script : this->scripts_) - { - script->get_event_handler()->dispatch(event); - } - } - - static utils::hook start_hook_; - static utils::hook stop_hook_; - - static void propagate_error(const std::exception& e) - { - printf("\n******* Script execution error *******\n"); - printf("%s\n", e.what()); - printf("**************************************\n\n"); - - scheduler::once([] - { - game::native::Com_Error(game::native::errorParm_t::ERR_SCRIPT, "Script execution error\n(see console for actual details)\n"); - }, scheduler::pipeline::main); - } - - static void start_execution_stub() - { - module_loader::get()->start_execution(); - static_cast(start_hook_.get_original())(); - } - - static void stop_execution_stub() - { - module_loader::get()->stop_execution(); - static_cast(stop_hook_.get_original())(); - } - - static void vm_notify_stub(const unsigned int notify_id, const unsigned short type, - game::native::VariableValue* stack) - { - try - { - game::scripting::event e; - e.name = game::native::SL_ConvertToString(type); - e.entity_id = notify_id; - - if (e.name == "touch") return; // Skip that for now - - //printf("%X: %s\n", e.entity_id, e.name.data()); - - for (auto* value = stack; value->type != game::native::SCRIPT_END; --value) + try { - e.arguments.emplace_back(*value); + this->load_scripts(); + } + catch (std::exception& e) + { + propagate_error(e); + } + } + + void stop_execution() + { + this->scripts_.clear(); + } + + void run_frame() const + { + for (const auto& script : this->scripts_) + { + script->get_scheduler()->run_frame(); + } + } + + void dispatch(game::scripting::event* event) const + { + for (const auto& script : this->scripts_) + { + script->get_event_handler()->dispatch(event); + } + } + + static utils::hook start_hook_; + static utils::hook stop_hook_; + + static void propagate_error(const std::exception& e) + { + printf("\n******* Script execution error *******\n"); + printf("%s\n", e.what()); + printf("**************************************\n\n"); + + scheduler::once([] + { + game::native::Com_Error(game::native::errorParm_t::ERR_SCRIPT, "Script execution error\n(see console for actual details)\n"); + }, scheduler::pipeline::main); + } + + static void start_execution_stub() + { + module_loader::get()->start_execution(); + static_cast(start_hook_.get_original())(); + } + + static void stop_execution_stub() + { + module_loader::get()->stop_execution(); + static_cast(stop_hook_.get_original())(); + } + + static void vm_notify_stub(const unsigned int notify_id, const unsigned short type, + game::native::VariableValue* stack) + { + try + { + game::scripting::event e; + e.name = game::native::SL_ConvertToString(type); + e.entity_id = notify_id; + + if (e.name == "touch") return; // Skip that for now + + for (auto* value = stack; value->type != game::native::SCRIPT_END; --value) + { + e.arguments.emplace_back(*value); + } + + module_loader::get()->dispatch(&e); + } + catch (std::exception& e) + { + propagate_error(e); } - module_loader::get()->dispatch(&e); + game::native::VM_Notify(notify_id, type, stack); } - catch (std::exception& e) + + static int frame_stub(const int a1, const int a2) { - propagate_error(e); + module_loader::get()->run_frame(); + return game::native::G_RunFrame(a1, a2); } + }; - game::native::VM_Notify(notify_id, type, stack); - } + utils::hook scripting_class::start_hook_; + utils::hook scripting_class::stop_hook_; +} - static int frame_stub(const int a1, const int a2) - { - module_loader::get()->run_frame(); - return game::native::G_RunFrame(a1, a2); - } -}; -utils::hook scripting::start_hook_; -utils::hook scripting::stop_hook_; -REGISTER_MODULE(scripting) +REGISTER_MODULE(scripting::scripting_class) diff --git a/src/module/scripting.hpp b/src/module/scripting.hpp new file mode 100644 index 0000000..2f2a480 --- /dev/null +++ b/src/module/scripting.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace scripting +{ + extern std::unordered_map> script_function_table; + extern std::unordered_map>> script_function_table_sort; + extern std::unordered_map> script_function_table_rev; + + extern std::string current_file; + + std::string get_token(unsigned int id); +}