diff --git a/docs/lua/classes/scr_value_wrapper.md b/docs/lua/classes/scr_value_wrapper.md new file mode 100644 index 00000000..9c12e1be --- /dev/null +++ b/docs/lua/classes/scr_value_wrapper.md @@ -0,0 +1,99 @@ +# Class: scr_value_wrapper + +Class for wrapping arguments and return values of GTA script functions, used by add_script_function_hook. + +## Functions (6) + +### `get_int()` + +Get the int value currently contained by the wrapper. + +- **Parameters:** + - `index` (int): The index to access. + +- **Returns:** + - `int`: The current value. + +**Example Usage:** +```lua +value = scr_value_wrapper:get_int(0) +``` + +--- + +### `set_int()` + +Set the int value contained by the wrapper. + +- **Parameters:** + - `index` (int): The index to access. + - `value` (int): The new value. + +**Example Usage:** +```lua +scr_value_wrapper:set_int(0, 42) +``` + +--- + +### `get_float()` + +Get the float value currently contained by the wrapper. + +- **Parameters:** + - `index` (int): The index to access. + +- **Returns:** + - `float`: The current value. + +**Example Usage:** +```lua +value = scr_value_wrapper:get_float(0) +``` + +--- + +### `set_float()` + +Set the float value contained by the wrapper. + +- **Parameters:** + - `index` (int): The index to access. + - `value` (float): The new value. + +**Example Usage:** +```lua +scr_value_wrapper:set_float(0, 3.14) +``` + +--- + +### `get_string()` + +Get the string value currently contained by the wrapper. + +- **Parameters:** + - `index` (int): The index to access. + +- **Returns:** + - `string`: The current value. + +**Example Usage:** +```lua +value = scr_value_wrapper:get_string(0) +``` + +--- + +### `set_string()` + +Set the string value contained by the wrapper. + +- **Parameters:** + - `index` (int): The index to access. + - `value` (string): The new value. + +**Example Usage:** +```lua +scr_value_wrapper:set_string(0, "hello") +``` \ No newline at end of file diff --git a/docs/lua/tables/scr_function.md b/docs/lua/tables/scr_function.md index 10944e76..887abc47 100644 --- a/docs/lua/tables/scr_function.md +++ b/docs/lua/tables/scr_function.md @@ -1,8 +1,8 @@ # Table: scr_function -Table for calling GTA script functions. Needs to be called in the fiber pool. Only call the function when necessary. +Table for calling and hooking GTA script functions. Calls must be made in the fiber pool. -## Functions (2) +## Functions (4) ### `call_script_function(script_name, function_name, pattern, return_type_string, args_)` @@ -49,4 +49,37 @@ local value = scr_function.call_script_function("freemode", 0xE792, "string", { scr_function.call_script_function(script_name, instruction_pointer, return_type_string, args_) ``` +### `add_script_function_hook(script_name, hook_name, pattern, hook_func)` +Hooks a script function. If the callback returns `false`, the original function is skipped, and values in `rets` are pushed to the stack. If `true`, the original function executes normally. + +- **Parameters:** + - `script_name` (string): Name of the script. + - `hook_name` (string): Name of the hook. This parameter needs to be unique. + - `pattern` (string): Pattern to scan for within the script. + - `hook_func` (string): The callback function. It receives args and rets, which can be read or set via `get/set_int/float/string` methods. Return value determines whether to skip or execute the original function. + +**Example Usage:** +```lua +scr_function.add_script_function_hook("some_script", "my_hook", "2D 00 ? ? 00 61", function(args, rets) + local val = args:get_int(0) + if val == 1 then + rets:set_int(0, 100) + return false + end + return true +end) +``` + +### `remove_script_function_hook(script_name, hook_name)` + +Removes an existing script function hook. + +- **Parameters:** + - `script_name` (string): Name of the script associated with the hook. + - `hook_name` (string): The unique name provided during add_script_function_hook. + +**Example Usage:** +```lua +scr_function.remove_script_function_hook("some_script", "my_hook") +``` \ No newline at end of file diff --git a/src/gta/enums.hpp b/src/gta/enums.hpp index 9a798eb7..6035b32a 100644 --- a/src/gta/enums.hpp +++ b/src/gta/enums.hpp @@ -2817,4 +2817,139 @@ enum KeyboardState : int32_t ACTIVE, COMPLETED, CANCELED +}; + +enum class scrOpcode : std::uint8_t +{ + NOP, + IADD, + ISUB, + IMUL, + IDIV, + IMOD, + INOT, + INEG, + IEQ, + INE, + IGT, + IGE, + ILT, + ILE, + FADD, + FSUB, + FMUL, + FDIV, + FMOD, + FNEG, + FEQ, + FNE, + FGT, + FGE, + FLT, + FLE, + VADD, + VSUB, + VMUL, + VDIV, + VNEG, + IAND, + IOR, + IXOR, + I2F, + F2I, + F2V, + PUSH_CONST_U8, + PUSH_CONST_U8_U8, + PUSH_CONST_U8_U8_U8, + PUSH_CONST_U32, + PUSH_CONST_F, + DUP, + DROP, + NATIVE, + ENTER, + LEAVE, + LOAD, + STORE, + STORE_REV, + LOAD_N, + STORE_N, + ARRAY_U8, + ARRAY_U8_LOAD, + ARRAY_U8_STORE, + LOCAL_U8, + LOCAL_U8_LOAD, + LOCAL_U8_STORE, + STATIC_U8, + STATIC_U8_LOAD, + STATIC_U8_STORE, + IADD_U8, + IMUL_U8, + IOFFSET, + IOFFSET_U8, + IOFFSET_U8_LOAD, + IOFFSET_U8_STORE, + PUSH_CONST_S16, + IADD_S16, + IMUL_S16, + IOFFSET_S16, + IOFFSET_S16_LOAD, + IOFFSET_S16_STORE, + ARRAY_U16, + ARRAY_U16_LOAD, + ARRAY_U16_STORE, + LOCAL_U16, + LOCAL_U16_LOAD, + LOCAL_U16_STORE, + STATIC_U16, + STATIC_U16_LOAD, + STATIC_U16_STORE, + GLOBAL_U16, + GLOBAL_U16_LOAD, + GLOBAL_U16_STORE, + J, + JZ, + IEQ_JZ, + INE_JZ, + IGT_JZ, + IGE_JZ, + ILT_JZ, + ILE_JZ, + CALL, + STATIC_U24, + STATIC_U24_LOAD, + STATIC_U24_STORE, + GLOBAL_U24, + GLOBAL_U24_LOAD, + GLOBAL_U24_STORE, + PUSH_CONST_U24, + SWITCH, + STRING, + STRINGHASH, + TEXT_LABEL_ASSIGN_STRING, + TEXT_LABEL_ASSIGN_INT, + TEXT_LABEL_APPEND_STRING, + TEXT_LABEL_APPEND_INT, + TEXT_LABEL_COPY, + CATCH, + THROW, + CALLINDIRECT, + PUSH_CONST_M1, + PUSH_CONST_0, + PUSH_CONST_1, + PUSH_CONST_2, + PUSH_CONST_3, + PUSH_CONST_4, + PUSH_CONST_5, + PUSH_CONST_6, + PUSH_CONST_7, + PUSH_CONST_FM1, + PUSH_CONST_F0, + PUSH_CONST_F1, + PUSH_CONST_F2, + PUSH_CONST_F3, + PUSH_CONST_F4, + PUSH_CONST_F5, + PUSH_CONST_F6, + PUSH_CONST_F7, + IS_BIT_SET }; \ No newline at end of file diff --git a/src/gta_pointers.hpp b/src/gta_pointers.hpp index 7db4c230..6d2ce4cf 100644 --- a/src/gta_pointers.hpp +++ b/src/gta_pointers.hpp @@ -420,6 +420,8 @@ namespace big CAnticheatContext** m_anticheat_context; PVOID m_game_skeleton_update; + + PVOID m_script_vm_on_enter_end; }; #pragma pack(pop) static_assert(sizeof(gta_pointers) % 8 == 0, "Pointers are not properly aligned"); diff --git a/src/hooks/script/init_native_tables.cpp b/src/hooks/script/init_native_tables.cpp index 42589082..1cbd9fb5 100644 --- a/src/hooks/script/init_native_tables.cpp +++ b/src/hooks/script/init_native_tables.cpp @@ -1,6 +1,7 @@ #include "hooking/hooking.hpp" #include "native_hooks/native_hooks.hpp" #include "services/script_patcher/script_patcher_service.hpp" +#include "services/script_function_hook/script_function_hook_service.hpp" namespace big { @@ -12,6 +13,7 @@ namespace big { g_script_patcher_service->on_script_load(program); g_native_hooks->hook_program(program); + g_script_function_hook_service->resolve_hooks(program); } return ret; diff --git a/src/lua/bindings/scr_function.cpp b/src/lua/bindings/scr_function.cpp index 69ee4742..39206644 100644 --- a/src/lua/bindings/scr_function.cpp +++ b/src/lua/bindings/scr_function.cpp @@ -5,6 +5,7 @@ #include "memory/pattern.hpp" #include "pointers.hpp" #include "util/scripts.hpp" +#include "services/script_function_hook/script_function_hook_service.hpp" namespace lua::scr_function { @@ -351,10 +352,149 @@ namespace lua::scr_function } } + // Lua API: Class + // Name: scr_value_wrapper + // Class for wrapping arguments and return values of GTA script functions, used by add_script_function_hook. + class scr_value_wrapper_t + { + rage::scrValue* m_data; + + public: + scr_value_wrapper_t(rage::scrValue* data) : + m_data(data) + { + } + + // Lua API: Function + // Class: scr_value_wrapper + // Name: get_int + // Param: index: int: The index to access. + // Returns: int: The current value. + // Get the int value currently contained by the wrapper. + int get_int(int index) + { + return m_data[index].Int; + } + + // Lua API: Function + // Class: scr_value_wrapper + // Name: set_int + // Param: index: int: The index to access. + // Param: value: int: The new value. + // Set the int value contained by the wrapper. + void set_int(int index, int value) + { + m_data[index].Int = value; + } + + // Lua API: Function + // Class: scr_value_wrapper + // Name: get_float + // Param: index: int: The index to access. + // Returns: float: The current value. + // Get the float value currently contained by the wrapper. + float get_float(int index) + { + return m_data[index].Float; + } + + // Lua API: Function + // Class: scr_value_wrapper + // Name: set_float + // Param: index: int: The index to access. + // Param: value: float: The new value. + // Set the float value contained by the wrapper. + void set_float(int index, float value) + { + m_data[index].Float = value; + } + + // Lua API: Function + // Class: scr_value_wrapper + // Name: get_string + // Param: index: int: The index to access. + // Returns: string: The current value. + // Get the string value currently contained by the wrapper. + const char* get_string(int index) + { + return m_data[index].String; + } + + // Lua API: Function + // Class: scr_value_wrapper + // Name: set_string + // Param: index: int: The index to access. + // Param: value: string: The new value. + // Set the string value contained by the wrapper. + void set_string(int index, const char* value) + { + m_data[index].String = value; + } + }; + + // Lua API: function + // Table: scr_function + // Name: add_script_function_hook + // Param: script_name: string: Name of the script. + // Param: hook_name: string: Name of the hook. This parameter needs to be unique. + // Param: pattern: string: Pattern to scan for within the script. + // Param: hook_func: function: The callback function. It receives args and rets, which can be read or set via `get/set_int/float/string` methods. Return value determines whether to skip or execute the original function. + // Hooks a script function. If the callback returns `false`, the original function is skipped, and values in `rets` are pushed to the stack. If `true`, the original function executes normally. + // **Example Usage:** + // ```lua + // scr_function.add_script_function_hook("some_script", "my_hook", "2D 00 ? ? 00 61", function(args, rets) + // local val = args:get_int(0) + // if val == 1 then + // rets:set_int(0, 100) + // return false + // end + // return true + // end) + // ``` + static void add_script_function_hook(const std::string& script_name, const std::string& hook_name, const std::string& pattern, sol::protected_function hook_func) + { + big::g_script_function_hook_service->add_hook(rage::joaat(script_name), hook_name, pattern, [hook_func](rage::scrValue* args, rage::scrValue* rets) -> bool { + scr_value_wrapper_t lua_args(args); + scr_value_wrapper_t lua_rets(rets); + + auto result = hook_func(lua_args, lua_rets); + if (!result.valid()) + return true; // don't skip the original if lua fails + + return result.get(); + }); + } + + // Lua API: function + // Table: scr_function + // Name: remove_script_function_hook + // Param: script_name: string: Name of the script associated with the hook. + // Param: hook_name: string: The unique name provided during add_script_function_hook. + // Removes an existing script function hook. + // **Example Usage:** + // ```lua + // scr_function.remove_script_function_hook("some_script", "my_hook") + // ``` + static void remove_script_function_hook(const std::string& script_name, const std::string& hook_name) + { + big::g_script_function_hook_service->remove_hook(rage::joaat(script_name), hook_name); + } + void bind(sol::state& state) { + auto ut = state.new_usertype("scr_value_wrapper"); + + ut["get_int"] = &scr_value_wrapper_t::get_int; + ut["set_int"] = &scr_value_wrapper_t::set_int; + ut["get_float"] = &scr_value_wrapper_t::get_float; + ut["set_float"] = &scr_value_wrapper_t::set_float; + ut["get_string"] = &scr_value_wrapper_t::get_string; + ut["set_string"] = &scr_value_wrapper_t::set_string; + auto ns = state["scr_function"].get_or_create(); - ns["call_script_function"] = sol::overload(call_script_function_by_signature, call_script_function_by_instruction_pointer); + ns["call_script_function"] = sol::overload(call_script_function_by_signature, call_script_function_by_instruction_pointer); + ns["add_script_function_hook"] = add_script_function_hook; + ns["remove_script_function_hook"] = remove_script_function_hook; } } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 06baafde..7cab9eae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,6 +30,7 @@ #include "services/vehicle/handling_service.hpp" #include "services/vehicle/xml_vehicles_service.hpp" #include "services/xml_maps/xml_map_service.hpp" +#include "services/script_function_hook/script_function_hook_service.hpp" #include "thread_pool.hpp" #include "util/is_proton.hpp" #include "version.hpp" @@ -190,6 +191,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID) auto script_connection_service_instance = std::make_unique(); auto xml_vehicles_service_instance = std::make_unique(); auto xml_maps_service_instance = std::make_unique(); + auto script_function_hook_service_instance = std::make_unique(); LOG(INFO) << "Registered service instances..."; g_notification_service.initialise(); @@ -283,6 +285,8 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID) LOG(INFO) << "Context Service reset."; xml_vehicles_service_instance.reset(); LOG(INFO) << "Xml Vehicles Service reset."; + script_function_hook_service_instance.reset(); + LOG(INFO) << "Script Function Hook Service reset."; LOG(INFO) << "Services uninitialized."; hooking_instance.reset(); diff --git a/src/pointers.cpp b/src/pointers.cpp index cf2e74be..3fdd7974 100644 --- a/src/pointers.cpp +++ b/src/pointers.cpp @@ -1970,6 +1970,15 @@ namespace big { g_pointers->m_gta.m_game_skeleton_update = ptr.as(); } + }, + // Script VM On Enter End + { + "SVMOEE", + "E9 ? ? ? ? 44 0F B6 4F ? 44 0F B6 47", + [](memory::handle ptr) + { + g_pointers->m_gta.m_script_vm_on_enter_end = ptr.as(); + } } >(); // don't leave a trailing comma at the end diff --git a/src/services/script_function_hook/script_function_hook.asm b/src/services/script_function_hook/script_function_hook.asm new file mode 100644 index 00000000..57189b2c --- /dev/null +++ b/src/services/script_function_hook/script_function_hook.asm @@ -0,0 +1,51 @@ +EXTERN process_script_function_hooks:PROC +EXTERN g_script_vm_on_enter_end_jump_offset:QWORD + +.code + +script_vm_on_enter_end_handler PROC + ; fix the stack + add rsp, 8 + + ; save stuff + pushfq + push r11 + push r10 + push r9 + push r8 + push rdx + push rcx + push rbx + push rsi + push rdi + + ; setup args and call our function + mov rcx, rsp ; ip + lea rdx, [rsp + 8] ; base + lea r8, [rsp + 16] ; sp + mov r9, r14 ; fp + sub rsp, 30h + mov [rsp + 20h], r15 ; ctx + mov [rsp + 28h], r13 ; code + call process_script_function_hooks + + add rsp, 30h + + ; restore stuff + pop rdi + pop rsi + pop rbx + pop rcx + pop rdx + pop r8 + pop r9 + pop r10 + pop r11 + popfq + + ; original logic (jump to next case) + mov rax, qword ptr [g_script_vm_on_enter_end_jump_offset] + jmp rax +script_vm_on_enter_end_handler ENDP + +END \ No newline at end of file diff --git a/src/services/script_function_hook/script_function_hook_service.cpp b/src/services/script_function_hook/script_function_hook_service.cpp new file mode 100644 index 00000000..ffad1816 --- /dev/null +++ b/src/services/script_function_hook/script_function_hook_service.cpp @@ -0,0 +1,146 @@ +#include "script_function_hook_service.hpp" +#include "gta_util.hpp" +#include "util/scripts.hpp" +#include "hooking/call_hook.hpp" + +namespace +{ + extern "C" std::uintptr_t g_script_vm_on_enter_end_jump_offset{}; + big::call_hook* g_script_vm_on_enter_end_hook{}; + + extern "C" void script_vm_on_enter_end_handler(); + extern "C" void process_script_function_hooks(std::uint8_t*& ip, std::uint8_t*& base, rage::scrValue*& sp, rage::scrValue* fp, rage::scrThreadContext* ctx, std::uint8_t** code) + { + big::g_script_function_hook_service->process_hooks(ip, base, sp, fp, ctx, code); + } +} + +namespace big +{ + script_function_hook_service::script_function_hook_service() + { + g_script_vm_on_enter_end_jump_offset = memory::handle(g_pointers->m_gta.m_script_vm_on_enter_end).add(1).rip().as(); + + g_script_vm_on_enter_end_hook = new call_hook(g_pointers->m_gta.m_script_vm_on_enter_end, script_vm_on_enter_end_handler); + g_script_vm_on_enter_end_hook->enable(); + + g_script_function_hook_service = this; + } + + script_function_hook_service::~script_function_hook_service() + { + g_script_vm_on_enter_end_jump_offset = 0; + + g_script_vm_on_enter_end_hook->disable(); + delete g_script_vm_on_enter_end_hook; + g_script_vm_on_enter_end_hook = nullptr; + + g_script_function_hook_service = nullptr; + } + + void script_function_hook_service::process_hook(const function_hook& hook, std::uint8_t*& ip, std::uint8_t*& base, rage::scrValue*& sp, rage::scrValue* fp, std::uint8_t** code) + { + rage::scrValue ret_val[256]{}; + if (!hook.m_hook_func(fp, ret_val)) + { + // push return value onto stack + for (std::uint32_t i = 0; i < hook.m_ret_count; i++) + (++sp)->Any = ret_val[i].Any; + + // jump to LEAVE + std::int64_t offset = ip - base + (hook.m_end_ip - hook.m_start_ip); + ip = &code[offset >> 14][offset & 0x3FFF] - 1; + base = &ip[-offset]; + } + } + + void script_function_hook_service::resolve_hook(function_hook& hook, rage::scrProgram* program) + { + auto location = scripts::get_code_location_by_pattern(program, memory::pattern(hook.m_pattern)); + if (!location) + { + LOG(FATAL) << "Failed to find pattern " << hook.m_name << " in script " << program->m_name; + return; + } + + LOG(VERBOSE) << "Found pattern " << hook.m_name << " at " << HEX_TO_UPPER(*location) << " in script " << program->m_name; + + std::uint32_t post_enter = *location + scripts::get_insn_size(program, *location); + std::uint32_t pos = post_enter; + + while (pos < program->m_code_size) + { + std::uint8_t* op = program->get_code_address(pos); + std::uint32_t size = scripts::get_insn_size(program, pos); + + if (*op == static_cast(scrOpcode::LEAVE)) + { + std::uint32_t next = pos + size; + std::uint8_t* next_op = program->get_code_address(next); + + // If next op is end of code or ENTER, this is the last LEAVE of the function + if (next >= program->m_code_size || *next_op == static_cast(scrOpcode::ENTER)) + { + hook.m_start_ip = post_enter; + hook.m_end_ip = pos; + hook.m_ret_count = op[2]; + hook.m_resolved = true; + break; + } + } + + pos += size; + } + } + + void script_function_hook_service::process_hooks(std::uint8_t*& ip, std::uint8_t*& base, rage::scrValue*& sp, rage::scrValue* fp, rage::scrThreadContext* ctx, std::uint8_t** code) + { + for (const auto& hook : m_hooks) + { + if (!hook.m_resolved || hook.m_script != ctx->m_script_hash || hook.m_start_ip != static_cast(ip - base)) + continue; + + process_hook(hook, ip, base, sp, fp, code); + } + } + + void script_function_hook_service::resolve_hooks(rage::scrProgram* program) + { + for (auto& hook : m_hooks) + { + if (hook.m_resolved || !program || hook.m_script != program->m_name_hash) + continue; + + resolve_hook(hook, program); + } + } + + void script_function_hook_service::add_hook(rage::joaat_t script, const std::string& name, const std::string& pattern, const hook_func& hook_func) + { + // prevent duplicates + for (const auto& hook : m_hooks) + { + if (hook.m_script == script && hook.m_name == name) + return; + } + + function_hook hook{}; + hook.m_script = script; + hook.m_name = name; + hook.m_pattern = pattern; + hook.m_hook_func = hook_func; + + // if the program is already available, set the data now, otherwise we will try in init_native_tables + if (auto program = gta_util::find_script_program(script)) + resolve_hook(hook, program); + + m_hooks.push_back(hook); + } + + void script_function_hook_service::remove_hook(rage::joaat_t script, const std::string& name) + { + m_hooks.erase(std::remove_if(m_hooks.begin(), m_hooks.end(), [&](const function_hook& hook) { + return hook.m_script == script && hook.m_name == name; + }), m_hooks.end()); + } +} \ No newline at end of file diff --git a/src/services/script_function_hook/script_function_hook_service.hpp b/src/services/script_function_hook/script_function_hook_service.hpp new file mode 100644 index 00000000..6e84a3df --- /dev/null +++ b/src/services/script_function_hook/script_function_hook_service.hpp @@ -0,0 +1,45 @@ +#pragma once + +namespace rage +{ + union scrValue; + class scrThreadContext; + class scrProgram; +} + +namespace big +{ + class script_function_hook_service + { + public: + using hook_func = std::function; + + script_function_hook_service(); + ~script_function_hook_service(); + + void process_hooks(std::uint8_t*& ip, std::uint8_t*& base, rage::scrValue*& sp, rage::scrValue* fp, rage::scrThreadContext* ctx, std::uint8_t** code); + void resolve_hooks(rage::scrProgram* program); + void add_hook(rage::joaat_t script, const std::string& name, const std::string& pattern, const hook_func& hook_func); + void remove_hook(rage::joaat_t script, const std::string& name); + + private: + struct function_hook + { + rage::joaat_t m_script; + std::string m_name; + std::string m_pattern; + std::uint32_t m_start_ip; + std::uint32_t m_end_ip; + std::uint32_t m_ret_count; + hook_func m_hook_func; + bool m_resolved; + }; + + static void process_hook(const function_hook& hook, std::uint8_t*& ip, std::uint8_t*& base, rage::scrValue*& sp, rage::scrValue* fp, std::uint8_t** code); + static void resolve_hook(function_hook& hook, rage::scrProgram* program); + + std::vector m_hooks; + }; + + inline script_function_hook_service* g_script_function_hook_service{}; +} \ No newline at end of file diff --git a/src/util/scripts.hpp b/src/util/scripts.hpp index cf8ffdad..9e81a4be 100644 --- a/src/util/scripts.hpp +++ b/src/util/scripts.hpp @@ -310,4 +310,85 @@ namespace big::scripts SCRIPT::SET_SCRIPT_WITH_NAME_HASH_AS_NO_LONGER_NEEDED(hash); } + + inline std::uint32_t get_insn_size(rage::scrProgram* program, std::uint32_t index) + { + if (!program || !program->m_code_blocks) + return 1; + + auto code = program->get_code_address(index); + + switch (static_cast(*code)) + { + case scrOpcode::PUSH_CONST_U8: + case scrOpcode::ARRAY_U8: + case scrOpcode::ARRAY_U8_LOAD: + case scrOpcode::ARRAY_U8_STORE: + case scrOpcode::LOCAL_U8: + case scrOpcode::LOCAL_U8_LOAD: + case scrOpcode::LOCAL_U8_STORE: + case scrOpcode::STATIC_U8: + case scrOpcode::STATIC_U8_LOAD: + case scrOpcode::STATIC_U8_STORE: + case scrOpcode::IADD_U8: + case scrOpcode::IMUL_U8: + case scrOpcode::IOFFSET_U8: + case scrOpcode::IOFFSET_U8_LOAD: + case scrOpcode::IOFFSET_U8_STORE: + case scrOpcode::TEXT_LABEL_ASSIGN_STRING: + case scrOpcode::TEXT_LABEL_ASSIGN_INT: + case scrOpcode::TEXT_LABEL_APPEND_STRING: + case scrOpcode::TEXT_LABEL_APPEND_INT: + return 2; + case scrOpcode::PUSH_CONST_U8_U8: + case scrOpcode::LEAVE: + case scrOpcode::PUSH_CONST_S16: + case scrOpcode::IADD_S16: + case scrOpcode::IMUL_S16: + case scrOpcode::IOFFSET_S16: + case scrOpcode::IOFFSET_S16_LOAD: + case scrOpcode::IOFFSET_S16_STORE: + case scrOpcode::ARRAY_U16: + case scrOpcode::ARRAY_U16_LOAD: + case scrOpcode::ARRAY_U16_STORE: + case scrOpcode::LOCAL_U16: + case scrOpcode::LOCAL_U16_LOAD: + case scrOpcode::LOCAL_U16_STORE: + case scrOpcode::STATIC_U16: + case scrOpcode::STATIC_U16_LOAD: + case scrOpcode::STATIC_U16_STORE: + case scrOpcode::GLOBAL_U16: + case scrOpcode::GLOBAL_U16_LOAD: + case scrOpcode::GLOBAL_U16_STORE: + case scrOpcode::J: + case scrOpcode::JZ: + case scrOpcode::IEQ_JZ: + case scrOpcode::INE_JZ: + case scrOpcode::IGT_JZ: + case scrOpcode::IGE_JZ: + case scrOpcode::ILT_JZ: + case scrOpcode::ILE_JZ: + return 3; + case scrOpcode::PUSH_CONST_U8_U8_U8: + case scrOpcode::NATIVE: + case scrOpcode::CALL: + case scrOpcode::STATIC_U24: + case scrOpcode::STATIC_U24_LOAD: + case scrOpcode::STATIC_U24_STORE: + case scrOpcode::GLOBAL_U24: + case scrOpcode::GLOBAL_U24_LOAD: + case scrOpcode::GLOBAL_U24_STORE: + case scrOpcode::PUSH_CONST_U24: + return 4; + case scrOpcode::PUSH_CONST_U32: + case scrOpcode::PUSH_CONST_F: + return 5; + case scrOpcode::ENTER: + return 5 + code[4]; + case scrOpcode::SWITCH: + return 2 + code[1] * 6; + } + + return 1; + } }