gokuta Posted July 13, 2017 Share Posted July 13, 2017 (edited) 0AB1: call_scm_func @MY_FUNC 2 'GXT' // Short string, two variables passed, right? :MY_FUNC // do something with [email protected]: ret 0 [email protected] stores a nonsensical value. Is it possible to pass a [short / long] string literal to a SCM function? Anything I am doing wrong? Thanks for an answer Edited July 13, 2017 by gokuta Link to comment Share on other sites More sharing options...
kosticn101 Posted July 13, 2017 Share Posted July 13, 2017 (edited) I'll wait with you for somebody with an answer to your question , I just wanted to say that this variant is also possible: {$CLEO}0000:const STRCPY = 0x0069F910end:loopwait 0if 0AB0: key_pressed 49 //1jf @loop0AC8: [email protected] = allocate_memory_size 2000AA5: call STRCPY num_params 2 pop 2 "Very, very long sentence you got here." [email protected]: call_scm_func @test 3 [email protected] 5 6.200AC9: free_allocated_memory [email protected]: end_custom_thread:testwait 00AD1: show_formatted_text_highpriority "0 = %s, 1 = %d, 2 = %.2f" time 1 [email protected] [email protected] [email protected] 0AB0: key_pressed 50 //2jf @test0AB2: ret 0 My guess is that [email protected] here stores a pointer to some memory location where actual string is. Pointer should be an integer, so it takes 4 bytes, meaning that you can freely use [email protected], [email protected] (without skipping).Is this right? Is there an list of method addresses (for example I can't find strcpy here, maybe I'm looking at the wrong place). Edited July 14, 2017 by kosticn101 Link to comment Share on other sites More sharing options...
DK22Pac Posted July 14, 2017 Share Posted July 14, 2017 (edited) With latest CLEO and SannyBuilder version, 1. When you pass a a 7-byte length string (' ') into scm function as first parameter, you'll get a pointer to this string in [email protected] {$CLEO}0000:while true 0001: wait 0 0AB1: call @test 1 'My text'end:test0ACD: print_string_now [email protected] time 100 // we have a pointer to 'My text' string in [email protected]: ret 02. Passing a variable-length string (" ") into scm function is forbidden.3. When you pass a string variable ([email protected], [email protected], s$, v$) into scm function as first parameter, you'll get a pointer to this variable in [email protected] {$CLEO}0000:while true 0001: wait 0 0AB1: call @test 1 [email protected]:[email protected] = 'Test'0ACD: print_string_now [email protected] time 100 // we have a pointer to [email protected] in [email protected]: ret 0 Is there an list of method addresses This list is outdated. Nowatimes, we use IDA databases to document .exe functions and variables. The last 'public' database, made by listener, is also outdated. I guess, most of 'advanced' coders use their own databases, so you can find someone and ask him to share it. Edited July 14, 2017 by DK22Pac kosticn101 1 Link to comment Share on other sites More sharing options...
kosticn101 Posted July 14, 2017 Share Posted July 14, 2017 (edited) 3. When you pass a string variable ([email protected], [email protected], s$, v$) into scm function as first parameter, you'll get a pointer to this variable in [email protected] {$CLEO}0000:while true 0001: wait 0 0AB1: call @test 1 [email protected]:[email protected] = 'Test'0ACD: print_string_now [email protected] time 100 // we have a pointer to [email protected] in [email protected]: ret 0 This confuses me. {$CLEO}0000:while true [email protected] = 'tseT' //<- memory location 0xblahblah 0AB1: call @test 1 [email protected] wait 200 0ACD: print_string_now [email protected] time 100 wait 200 end:[email protected] = 'Test'//[email protected] points to 0xblahblah0ACD: print_string_now [email protected] time 100 //"Test" is printed//does that mean that [email protected] here also is at 0xblahblah?//if that's the case, why [email protected] outside scm_func still has value "tseT"?0AB2: ret 0 Edited July 14, 2017 by kosticn101 Link to comment Share on other sites More sharing options...
DK22Pac Posted July 14, 2017 Share Posted July 14, 2017 (edited) does that mean that [email protected] here also is at 0xblahblah? Yes. if that's the case, why [email protected] outside scm_func still has value "tseT"? After scm function was called (and we return back), CLEO restores local variables values. Edited July 14, 2017 by DK22Pac kosticn101 1 Link to comment Share on other sites More sharing options...
gokuta Posted July 15, 2017 Author Share Posted July 15, 2017 (edited) There are opcodes, that do accept strings as parameters, but do not accept pointers to strings. Any ideas how to get a string when a string pointer is given? Something like: 7FFF: [email protected] = string_ptr [email protected] Edited July 15, 2017 by gokuta Link to comment Share on other sites More sharing options...
kosticn101 Posted July 15, 2017 Share Posted July 15, 2017 (edited) Maybe this helps {$CLEO}thread 'test' 0A9F: [email protected] = [email protected] += 0x80A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0 //first 4 [email protected] += 0x4 0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 0 //second 4 bytes033E: set_draw_text_position 250.0 40.0 GXT [email protected] //pay attention to 's'0A93: end_custom_thread http://gtaforums.com/topic/456691-memory-handling/?p=1060079026 Edited July 15, 2017 by kosticn101 Link to comment Share on other sites More sharing options...
gokuta Posted July 15, 2017 Author Share Posted July 15, 2017 (edited) Thanks, got it and it works. Now I am experiencing one more problem 0AB1: call_scm_func @MY_FUNC 2 'ONE' 'TWO' Can I pass two pointers to two short strings this way? Only the last string was passed when I test it Edited July 15, 2017 by gokuta Link to comment Share on other sites More sharing options...
kosticn101 Posted July 15, 2017 Share Posted July 15, 2017 (edited) I don't know why this happens. Saw little nice combination in some scripts, not completely sure how it works, but can serve as a (temporary) workaround. {$CLEO}0000: NOP:loopwait 0if 0AB0: key_pressed 49 //1jf @loop0AB1: call_scm_func @show_values 3 @BB_19 @BEEFY @IE23jump @loop:show_values0AB1: call_scm_func @read_strings 3 ( [email protected] [email protected] [email protected] ) ( [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] )00BC: show_text_highpriority GXT [email protected] time 500 flag 1wait 60000BC: show_text_highpriority GXT [email protected] time 500 flag 1wait 60000BC: show_text_highpriority GXT [email protected] time 500 flag 1wait 6000AB2: ret 0:read_strings0AC6: [email protected] = label [email protected] offset0AC6: [email protected] = label [email protected] offset0AC6: [email protected] = label [email protected] offset//for some reason you need to//add 0x03 to original label [email protected] += 0x03 [email protected] += [email protected] += 0x03//string pointer -> short string0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 00A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 00A8D: [email protected] = read_memory [email protected] size 4 virtual_protect [email protected] += [email protected] += [email protected] += 0x040A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 00A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 00A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 00AB2: ret 6 [email protected] [email protected] [email protected] [email protected] [email protected] [email protected]:BB_190900: 'BB_19'//0900: clear_object [email protected]([email protected],16i) last_weapon_damage//interesting...:BEEFY0900: 'BEEFY':IE230900: 'IE23'0000: NOP Edited July 16, 2017 by kosticn101 Link to comment Share on other sites More sharing options...
gokuta Posted July 16, 2017 Author Share Posted July 16, 2017 0AB1: call_scm_func @show_values 3 @BB_19 @BEEFY @IE23:BB_190900: 'BB_19':BEEFY0900: 'BEEFY':IE230900: 'IE23' Very interesting, what does it mean to pass a label as a parameter? Also it looks messy I would rather do it this way: 05AA: [email protected] = 'ONE'05AA: [email protected] = 'TWO'05AA: [email protected] = 'THREE'0AB1: @show_values 6 [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] The only problem is 6 variables wasted. Somebody, please tell me there is a way to pass string literals like that: 0AB1: @show_values 3 'ONE' 'TWO' 'THREE' Link to comment Share on other sites More sharing options...
kosticn101 Posted July 16, 2017 Share Posted July 16, 2017 (edited) Very interesting, what does it mean to pass a label as a parameter?This is how I understood this matter, maybe I'm wrong somewhere: Every label has its global and local offset (distance from the beginning in memory): - global offset is calculated from the top of the main.scm and is represented by a nonnegative integer (>= 0) - local offsets are calculated from the top of a thread (custom script) I guess, and they're generally represented by a negative integers. So when you assign label to a variable: [email protected] = @label, you are actually asigning label's local offset, which is (negative) integer. {$CLEO}//== local offset: 0, if you try to jump here you jump to the beginning of main.scm0000: //2B (opcode) = 2B:lab1 //== local offset: 0 - 2 = -20001: 0 //2B (opcode) + 4B (integer) = 6B0AB0: 49 //2B (opcode) + 4B (integer) = 6B004D: -2 //2B (opcode) + 4B (integer) = 6B0AD1: "%d" time 1000 @lab1 //2B (opcode) + 3B (string) + 2x4B (integer) = 13B // <- prints -20002: -39 //2B (opcode) + 4B (integer) = 6B // + --- // 37B:lab3 //== local offset: -2 - 37 = -390001: 10000A93:When you decompile this with "Thread + Local Offset" option enabled, you get: // This file was decompiled using SASCM.ini published by GTAG (http://gtag.gtagaming.com/opcode-database) on 14.6.2013{$CLEO .cs}//-------------MAIN---------------0000: NOP :Noname_2 //offset -20001: wait 0 ms 0AB0: key_pressed 49 004D: jump_if_false @Noname_2 0AD1: show_formatted_text_highpriority "%d" time 1000 -2 //parameter was a label0002: jump @Noname_39 :Noname_39 //offset -390001: wait 1000 ms 0A93: end_custom_thread About jump to zero offset: http://gtaforums.com/topic/526239-jump-to-zero-offset-mainscm-modding/?p=1061729193About 0AC6: label @label offset opcode: http://gtaforums.com/topic/489137-label-opcode/?p=1060772349 Also: http://gtaforums.com/topic/211077-rel-sanny-builder/?p=3169340 ==== UPDATE ==== Three wasted variables and some wasted lines of code. But you can reuse those variables latter, after you free memory. {$CLEO}0000:const STRCPY = 0x0069F910end:loopwait 0if 0AB0: key_pressed 49 //1jf @loop0AC8: [email protected] = allocate_memory_size 80AC8: [email protected] = allocate_memory_size 80AC8: [email protected] = allocate_memory_size 80AA5: call STRCPY num_params 2 pop 2 "BB_19" [email protected]: call STRCPY num_params 2 pop 2 "BEEFY" [email protected]: call STRCPY num_params 2 pop 2 "IE23" [email protected]: call_scm_func @test 3 [email protected] [email protected] [email protected]: free_allocated_memory [email protected]: free_allocated_memory [email protected]: free_allocated_memory [email protected]: end_custom_thread:test0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 00A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 00A8D: [email protected] = read_memory [email protected] size 4 virtual_protect [email protected] += [email protected] += [email protected] += 0x040A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 00A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 00A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 000BC: show_text_highpriority GXT [email protected] time 500 flag 1wait 60000BC: show_text_highpriority GXT [email protected] time 500 flag 1wait 60000BC: show_text_highpriority GXT [email protected] time 500 flag 1wait 6000AB2: ret 0 Edited July 17, 2017 by kosticn101 Link to comment Share on other sites More sharing options...
X-Falcon Posted July 16, 2017 Share Posted July 16, 2017 0AB1: call_scm_func @show_values 3 @BB_19 @BEEFY @IE23:BB_190900: 'BB_19':BEEFY0900: 'BEEFY':IE230900: 'IE23' Very interesting, what does it mean to pass a label as a parameter? Also it looks messy I would rather do it this way: 05AA: [email protected] = 'ONE'05AA: [email protected] = 'TWO'05AA: [email protected] = 'THREE'0AB1: @show_values 6 [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] The only problem is 6 variables wasted. Somebody, please tell me there is a way to pass string literals like that: 0AB1: @show_values 3 'ONE' 'TWO' 'THREE' hrmp! if the point's for reading string without cut off word, probally you can use gosub statement. 0AB1 can be used for it without var, if basic consept is Insufficient, You would found dificullity for pass string exepct know them while converted as integer, every var saving maximum 4-bytes, every 1-word / 1-char equal by 1-bytes, short string operate 8-bytes if one of byte's wrong some print text can error for read In a chain, it's make unclear and longer than place string via var then you use gosub absolutely Link to comment Share on other sites More sharing options...
DK22Pac Posted July 17, 2017 Share Posted July 17, 2017 (edited) Somebody, please tell me there is a way to pass string literals like that:Not possible with current CLEO version. Only one string can be passed without problems. 2 options if you want to write a clean code: 1. Write your own opcodes, 0AB1/0AB2 replacements, fixed versions of original ones. 2. Forget about CLEO and try lua scripting/C++ programming. Edited July 17, 2017 by DK22Pac kosticn101 and dkluin 2 Link to comment Share on other sites More sharing options...
X-Falcon Posted July 18, 2017 Share Posted July 18, 2017 Somebody, please tell me there is a way to pass string literals like that: Not possible with current CLEO version. Only one string can be passed without problems. I has understand it, however way is still possible to read the string (' ' / " ") in the normal range before the call (0AB1) begins with certain simply tricks. ==== UPDATE ==== Three wasted variables and some wasted lines of code. But you can reuse those variables latter, after you free memory. {$CLEO}0000:const STRCPY = 0x0069F910end:loopwait 0if 0AB0: key_pressed 49 //1jf @loop0AC8: [email protected] = allocate_memory_size 80AC8: [email protected] = allocate_memory_size 80AC8: [email protected] = allocate_memory_size 80AA5: call STRCPY num_params 2 pop 2 "BB_19" [email protected]: call STRCPY num_params 2 pop 2 "BEEFY" [email protected]: call STRCPY num_params 2 pop 2 "IE23" [email protected]: call_scm_func @test 3 [email protected] [email protected] [email protected]: free_allocated_memory [email protected]: free_allocated_memory [email protected]: free_allocated_memory [email protected]: end_custom_thread:test0A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 00A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 00A8D: [email protected] = read_memory [email protected] size 4 virtual_protect [email protected] += [email protected] += [email protected] += 0x040A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 00A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 00A8D: [email protected] = read_memory [email protected] size 4 virtual_protect 000BC: show_text_highpriority GXT [email protected] time 500 flag 1wait 60000BC: show_text_highpriority GXT [email protected] time 500 flag 1wait 60000BC: show_text_highpriority GXT [email protected] time 500 flag 1wait 6000AB2: ret 0 It's too much of a hassle, but the more efficient gosub is reversed for this matter. What I'm test that all along. If you like something complexity, Maybe scripting with 0AB1 should be learned more. {$CLEO}constString_A = [email protected]_B = [email protected]_C = [email protected]_D = [email protected]_E = [email protected] = [email protected]: NOPwhile true 0001: wait 0 ms 00D6: if 0AB0: key_pressed 49 number_1 then 00D6: if 0029: TimerA >= 0 then 05AA: String_A = 'BB_19' // Time // @s = 'short' 05AA: String_B = 'BEEFY' // Beefy Baron 05AA: String_C = 'FAR_1' // Back to School 0050: gosub @Sub00BC 0006: TimerA = -3000 // total time for appear end else 00D6: if 0AB0: key_pressed 50 number_2 then 03EB: clear_small_messages_only // clean priority text 0AB1: call_scm_func @ShowFormatTXT 11 0x54584554 0x00000000 {single string {4-byte} 0x73277449 0x00654D5F {connective string {7-byte} 0x73277449 0x5458545F {connective string {8-byte} 0x73696854 0x0873495F 0x6F63704F 0x1A3A6564 {connective long string {16-byte} 2769 end endend:ShowFormatTXT0AD1: print_format_string_now "%s : %s : %s : %s%s %.4X" time 100 String_A String_B String_C String_D String_E [email protected]: ret 0:Sub00BC00BB: print_string_in_sequence GXT String_A time 1000 flag 100BB: print_string_in_sequence GXT String_B time 1000 flag 100BB: print_string_in_sequence GXT String_C time 1000 flag 10051: return Link to comment Share on other sites More sharing options...
DK22Pac Posted July 18, 2017 Share Posted July 18, 2017 (edited) 0AB1: call_scm_func @ShowFormatTXT 11 0x54584554 0x00000000 {single string {4-byte} 0x73277449 0x00654D5F {connective string {7-byte} 0x73277449 0x5458545F {connective string {8-byte} 0x73696854 0x0873495F 0x6F63704F 0x1A3A6564 {connective long string {16-byte} 2769 Are you sure it's 'simple'? Is it even readable? Read my post again, do you see a 'clean code' collocation there? Edited July 18, 2017 by DK22Pac Link to comment Share on other sites More sharing options...
kosticn101 Posted July 18, 2017 Share Posted July 18, 2017 (edited) 0AB1: call_scm_func @ShowFormatTXT 11 0x54584554 0x00000000 {single string {4-byte} 0x73277449 0x00654D5F {connective string {7-byte} 0x73277449 0x5458545F {connective string {8-byte} 0x73696854 0x0873495F 0x6F63704F 0x1A3A6564 {connective long string {16-byte} 2769 Hah, this is new for me. It looks like we can't get closer to an answer than this (if we're not aiming for readable code). Yesterday I've deleted SA so I can't test this, but for this is needed ASCII to HEX converter. Bytes are reversed. 0x73277449 0x00654D5F 49 74 27 73 5F 4D 65 00 It's_Me (2769)10 -> (0AD1)16 Edited July 18, 2017 by kosticn101 Link to comment Share on other sites More sharing options...
X-Falcon Posted July 18, 2017 Share Posted July 18, 2017 (edited) Are you sure it's 'simple'? Is it even readable? Read my post again, do you see a 'clean code' collocation there? 0AB1: call_scm_func @ShowFormatTXT 11 0x54584554 0x00000000 {single string {4-byte} 0x73277449 0x00654D5F {connective string {7-byte} 0x73277449 0x5458545F {connective string {8-byte} 0x73696854 0x0873495F 0x6F63704F 0x1A3A6564 {connective long string {16-byte} 2769 Hah, this is new for me. It looks like we can't get closer to an answer than this (if we're not aiming for readable code). Yesterday I've deleted SA so I can't test this, but for this is needed ASCII to HEX converter. Bytes are reversed. 0x73277449 0x00654D5F 49 74 27 73 5F 4D 65 00 It's_Me (2769)10 -> (0AD1)16 We have google for that, boy There also some secret why forced use text in hex, plus with the help of bitwise operation for accepted their condition uppercases & lowercases. Edited July 18, 2017 by X-Falcon Link to comment Share on other sites More sharing options...
DK22Pac Posted July 19, 2017 Share Posted July 19, 2017 (edited) #include "plugin.h"#include "CLEO.h"#include <map>#include <list>// Note: CLEO_VERSION and CLEO_GetVersion were not updated with latest (~1 year?) CLEO updatesconst unsigned int CURR_CLEO_VERSION = 0x04031300;using namespace plugin;class ScmFunctionOpcodes {public: enum ScriptParamType { dt_eop, dt_int32, dt_gvar, dt_lvar, dt_int8, dt_int16, dt_float, dt_gvar_arr, dt_lvar_arr, dt_text_label, dt_gvar_text_label, dt_lvar_text_label, dt_gvar_text_label_arr, dt_lvar_text_label_arr, dt_string_varlen, dt_string, dt_gvar_string, dt_lvar_string, dt_gvar_string_arr, dt_lvar_string_arr }; static bool IsEOP(int t) { return t == dt_eop; } static bool IsNumber(int t) { return t == dt_int32 || t == dt_int8 || t == dt_int16 || t == dt_float; } static bool IsVariable(int t) { return t == dt_gvar || t == dt_lvar || t == dt_gvar_arr || t == dt_lvar_arr; } static bool IsTextLabel(int t) { return t == dt_text_label; } static bool IsTextLabelVar(int t) { return t == dt_gvar_text_label || t == dt_lvar_text_label || t == dt_gvar_text_label_arr || t == dt_lvar_text_label_arr; } static bool IsString(int t) { return t == dt_string_varlen; } static bool IsStringVar(int t) { return t == dt_gvar_string || t == dt_lvar_string || t == dt_gvar_string_arr || t == dt_lvar_string_arr; } enum ErrorType { ERR_NONE, ERR_UNSUPPORTED_CLEO_VERSION, ERR_CLEO_NOT_FOUND, ERR_INCORRECT_USAGE, ERR_REACHED_VARIABLES_LIMIT, ERR_NOT_ENOUGH_PARAMETERS, ERR_INCOMPATIBLE_PARAMETER_TYPES }; template <typename... ArgTypes> static void ShowError(ErrorType type, char const *section, ArgTypes... args) { std::string err; if (section) { err = section; err += ": "; } if (type == ERR_UNSUPPORTED_CLEO_VERSION) err += "Unsupported CLEO version:\nNeeded: %d.%d.%d.%d\nYours: %d.%d.%d.%d"; else if (type == ERR_CLEO_NOT_FOUND) err += "Can't obtain cleo.asi handle"; else if (type == ERR_INCORRECT_USAGE) err += "Incorrect usage"; else if (type == ERR_REACHED_VARIABLES_LIMIT) err += "Reached variables limit(%d)"; else if (type == ERR_NOT_ENOUGH_PARAMETERS) err += "Not enough parameters:\nRequired: %d\nPassed: %d"; else if (type == ERR_INCOMPATIBLE_PARAMETER_TYPES) err += "Incompatible parameter types (%s > %s)"; else err += "Error"; Error(err.c_str(), args...); } static char const *GetParameterTypeName(int type) { if (type == dt_eop) return "eop"; if (type == dt_int32) return "int32"; if (type == dt_gvar) return "gvar"; if (type == dt_lvar) return "lvar"; if (type == dt_int8) return "int8"; if (type == dt_int16) return "int16"; if (type == dt_float) return "float"; if (type == dt_gvar_arr) return "gvar_arr"; if (type == dt_lvar_arr) return "lvar_arr"; if (type == dt_text_label) return "text_label"; if (type == dt_gvar_text_label) return "gvar_text_label"; if (type == dt_lvar_text_label) return "lvar_text_label"; if (type == dt_gvar_text_label_arr) return "gvar_text_label_arr"; if (type == dt_lvar_text_label_arr) return "lvar_text_label_arr"; if (type == dt_string_varlen) return "string_varlen"; if (type == dt_string) return "string"; if (type == dt_gvar_string) return "gvar_string"; if (type == dt_lvar_string) return "lvar_string"; if (type == dt_gvar_string_arr) return "gvar_string_arr"; if (type == dt_lvar_string_arr) return "lvar_string_arr"; return "unknown"; } class ScmFunction { public: SCRIPT_VAR m_savedvars[32]; BYTE *m_retnaddr; ScmFunction(CScriptThread *thread, int funcaddr, SCRIPT_VAR *vars) { // save variables CopyVariables(m_savedvars, GetScriptVariables(thread)); // set variables CopyVariables(GetScriptVariables(thread), vars); // save current address m_retnaddr = thread->ip; // jump to scm function code CLEO_ThreadJumpAtLabelPtr(thread, funcaddr); } }; static unsigned int GetCLEOBase() { static unsigned int cleoBase = reinterpret_cast<unsigned int>(GetModuleHandle("cleo.asi")); return cleoBase; } static SCRIPT_VAR *GetScriptVariables(CScriptThread *thread) { if (thread->missionFlag) return missionLocals; return thread->tls; } static void CopyVariables(SCRIPT_VAR *dst, SCRIPT_VAR *src) { for (unsigned int i = 0; i < 32; i++) dst[i] = src[i]; } static void SkipScriptUnusedParams(CScriptThread *thread) { while (CLEO_GetOperandType(thread)) CLEO_SkipOpcodeParams(thread, 1); thread->ip++; } static void WriteString(CScriptThread *thread, char const *str) { char *dst = reinterpret_cast<char *>(CLEO_GetPointerToScriptVariable(thread)); memcpy(dst, str, 16); dst[15] = '\0'; } static void WriteTextLabel(CScriptThread *thread, char const *str) { char *dst = reinterpret_cast<char *>(CLEO_GetPointerToScriptVariable(thread)); memcpy(dst, str, 8); dst[7] = '\0'; } static std::map<CScriptThread *, std::list<ScmFunction>> &GetFunctions() { static std::map<CScriptThread *, std::list<ScmFunction>> functions; return functions; } static unsigned int &GetAddScriptAddr() { static unsigned int addr; return addr; } static unsigned int &GetRemoveScriptAddr() { static unsigned int addr; return addr; } static void __fastcall OnAddScript(CScriptThread *thread, int, CScriptThread **queue) { if (!GetAddScriptAddr()) GetAddScriptAddr() = patch::GetUInt(GetCLEOBase() + 0x61D5C); reinterpret_cast<void(__thiscall*)(CScriptThread *, CScriptThread**)>(GetAddScriptAddr())(thread, queue); } static void __fastcall OnRemoveScript(CScriptThread *thread, int, CScriptThread **queue) { // remove all scm functions associated with this script auto it = GetFunctions().find(thread); if (it != GetFunctions().end()) GetFunctions().erase(it); if (!GetRemoveScriptAddr()) GetRemoveScriptAddr() = patch::GetUInt(GetCLEOBase() + 0x61D58); reinterpret_cast<void(__thiscall*)(CScriptThread *, CScriptThread**)>(GetRemoveScriptAddr())(thread, queue); } static OpcodeResult CALLBACK OpcodeScmFuncCall(CScriptThread *thread) { int addr = CLEO_GetIntOpcodeParam(thread); int paramsreq = CLEO_GetIntOpcodeParam(thread); SCRIPT_VAR vars[32]; for (unsigned int i = 0; i < 32; i++) vars[i].nParam = 0; unsigned int varcount = 0; bool err = false; for (int i = 0; i < paramsreq; i++) { int op = CLEO_GetOperandType(thread); if (IsNumber(op) || IsVariable(op)) { if (varcount < 32) vars[varcount++].nParam = CLEO_GetIntOpcodeParam(thread); else { ShowError(ERR_REACHED_VARIABLES_LIMIT, "OFB1", varcount + 1); err = true; break; } } else if (IsString(op) || IsStringVar(op)) { if (varcount < 29) { CLEO_ReadStringOpcodeParam(thread, reinterpret_cast<char *>(&vars[varcount]), 16); varcount += 4; } else { ShowError(ERR_REACHED_VARIABLES_LIMIT, "OFB1", varcount + 4); err = true; break; } } else if (IsTextLabel(op) || IsTextLabelVar(op)) { if (varcount < 31) { CLEO_ReadStringOpcodeParam(thread, reinterpret_cast<char *>(&vars[varcount]), 8); varcount += 2; } else { ShowError(ERR_REACHED_VARIABLES_LIMIT, "OFB1", varcount + 2); err = true; break; } } else { ShowError(ERR_NOT_ENOUGH_PARAMETERS, "0FB1", paramsreq, i); err = true; break; } } if (!err) { std::list<ScmFunction> &list = GetFunctions()[thread]; list.emplace_back(thread, addr, vars); } else SkipScriptUnusedParams(thread); return OR_CONTINUE; } static OpcodeResult CALLBACK OpcodeScmFuncRetn(CScriptThread *thread) { auto it = GetFunctions().find(thread); if (it != GetFunctions().end()) { std::list<ScmFunction> &list = (*it).second; ScmFunction &func = list.back(); // read return parameters int paramsreq = CLEO_GetIntOpcodeParam(thread); // pass all parameters BYTE *readIp = thread->ip; BYTE *writeIp = func.m_retnaddr; SCRIPT_VAR tmpvars[32]; for (int i = 0; i < paramsreq; i++) { thread->ip = readIp; int inop = CLEO_GetOperandType(thread); if (IsNumber(inop) || IsVariable(inop)) { DWORD param = CLEO_GetIntOpcodeParam(thread); readIp = thread->ip; thread->ip = writeIp; int outop = CLEO_GetOperandType(thread); if (IsEOP(outop)) { ShowError(ERR_NOT_ENOUGH_PARAMETERS, "0FB1", paramsreq, i); break; } else if (!IsVariable(outop)) { ShowError(ERR_INCOMPATIBLE_PARAMETER_TYPES, "0FB1", GetParameterTypeName(inop), GetParameterTypeName(outop)); break; } CopyVariables(tmpvars, GetScriptVariables(thread)); CopyVariables(GetScriptVariables(thread), func.m_savedvars); CLEO_SetIntOpcodeParam(thread, param); CopyVariables(func.m_savedvars, GetScriptVariables(thread)); CopyVariables(GetScriptVariables(thread), tmpvars); writeIp = thread->ip; } else if (IsString(inop) || IsStringVar(inop)) { char str[16]; CLEO_ReadStringOpcodeParam(thread, str, 16); readIp = thread->ip; thread->ip = writeIp; int outop = CLEO_GetOperandType(thread); if (IsEOP(outop)) { ShowError(ERR_NOT_ENOUGH_PARAMETERS, "0FB1", paramsreq, i); break; } else if (!IsStringVar(outop)) { ShowError(ERR_INCOMPATIBLE_PARAMETER_TYPES, "0FB1", GetParameterTypeName(inop), GetParameterTypeName(outop)); break; } CopyVariables(tmpvars, GetScriptVariables(thread)); CopyVariables(GetScriptVariables(thread), func.m_savedvars); WriteString(thread, str); CopyVariables(func.m_savedvars, GetScriptVariables(thread)); CopyVariables(GetScriptVariables(thread), tmpvars); writeIp = thread->ip; } else if (IsTextLabel(inop) || IsTextLabelVar(inop)) { char str[8]; CLEO_ReadStringOpcodeParam(thread, str, 8); readIp = thread->ip; thread->ip = writeIp; int outop = CLEO_GetOperandType(thread); if (IsEOP(outop)) { ShowError(ERR_NOT_ENOUGH_PARAMETERS, "0FB1", paramsreq, i); break; } else if (!IsTextLabelVar(outop)) { ShowError(ERR_INCOMPATIBLE_PARAMETER_TYPES, "0FB1", GetParameterTypeName(inop), GetParameterTypeName(outop)); break; } CopyVariables(tmpvars, GetScriptVariables(thread)); CopyVariables(GetScriptVariables(thread), func.m_savedvars); WriteTextLabel(thread, str); CopyVariables(func.m_savedvars, GetScriptVariables(thread)); CopyVariables(GetScriptVariables(thread), tmpvars); writeIp = thread->ip; } else { ShowError(ERR_NOT_ENOUGH_PARAMETERS, "0FB2", paramsreq, i); break; } } thread->ip = writeIp; CopyVariables(GetScriptVariables(thread), func.m_savedvars); // skip everything else SkipScriptUnusedParams(thread); // remove function from list list.pop_back(); // remove script info if there's no more functions if (list.size() == 0) GetFunctions().erase(it); } else { // If 0FB2 called, but there's no script info in the list SkipScriptUnusedParams(thread); ShowError(ERR_INCORRECT_USAGE, "0FB2"); } return OR_CONTINUE; } static void InstallCLEOPatches() { unsigned int cleoBase = GetCLEOBase(); if (cleoBase != 0) { GetAddScriptAddr() = reinterpret_cast<unsigned int>(patch::TranslateCallOffset<void*>(cleoBase + 0x1FC0D)); patch::RedirectCall(cleoBase + 0x1FC0D, OnAddScript); patch::Nop(cleoBase + 0x1FC0D + 5, 1); GetRemoveScriptAddr() = reinterpret_cast<unsigned int>(patch::TranslateCallOffset<void*>(cleoBase + 0x1FBED)); patch::RedirectCall(cleoBase + 0x1FBED, OnRemoveScript); patch::Nop(cleoBase + 0x1FBED + 5, 1); } else ShowError(ERR_CLEO_NOT_FOUND, 0); } ScmFunctionOpcodes() { // Validate CLEO version if (CLEO_GetVersion() == CURR_CLEO_VERSION) { // Add our hooks to CLEO InstallCLEOPatches(); // Register opcodes CLEO_RegisterOpcode(0x0FB1, OpcodeScmFuncCall); CLEO_RegisterOpcode(0x0FB2, OpcodeScmFuncRetn); } else { ShowError(ERR_UNSUPPORTED_CLEO_VERSION, 0, (CURR_CLEO_VERSION >> 24) & 0xFF, (CURR_CLEO_VERSION >> 16) & 0xFF, (CURR_CLEO_VERSION >> 8) & 0xFF, CURR_CLEO_VERSION & 0xFF, (CLEO_GetVersion() >> 24) & 0xFF, (CLEO_GetVersion() >> 16) & 0xFF, (CLEO_GetVersion() >> 8) & 0xFF, CLEO_GetVersion() & 0xFF); } }} plg; {$CLEO}{$OPCODE 0FB1=-1,call %1p%}{$OPCODE 0FB2=-1,ret %1d%}0000:while true 0FB1: call @test 5 "This" 'is' "a" 'simple' "text" [email protected] [email protected] [email protected] [email protected] [email protected] 0AD1: print_formatted_now "%s %s %s %s %s" time 1000 [email protected] [email protected] [email protected] [email protected] [email protected] 0001: wait 1000 end:test0AD1: print_formatted_now "%s %s %s %s %s" time 1000 [email protected] [email protected] [email protected] [email protected] [email protected]: wait 10000FB2: ret 5 [email protected] [email protected] [email protected] "returned" 'one!' ScmFunctionOpcodes Edited July 19, 2017 by DK22Pac gokuta and X-Falcon 2 Link to comment Share on other sites More sharing options...
X-Falcon Posted July 19, 2017 Share Posted July 19, 2017 ScmFunctionOpcodes Heh, I had expected that you're being create an improved for this opcode. Great work, DK. Link to comment Share on other sites More sharing options...
gokuta Posted July 19, 2017 Author Share Posted July 19, 2017 (edited) Seriously, nobody takes proper care of CLEO anymore... Edited July 19, 2017 by gokuta Link to comment Share on other sites More sharing options...