Jump to content
    1. Welcome to GTAForums!

    1. GTANet.com

    1. GTA Online

      1. Los Santos Drug Wars
      2. Updates
      3. Find Lobbies & Players
      4. Guides & Strategies
      5. Vehicles
      6. Content Creator
      7. Help & Support
    2. Red Dead Online

      1. Blood Money
      2. Frontier Pursuits
      3. Find Lobbies & Outlaws
      4. Help & Support
    3. Crews

    1. Grand Theft Auto Series

      1. Bugs*
      2. St. Andrews Cathedral
    2. GTA VI

    3. GTA V

      1. Guides & Strategies
      2. Help & Support
    4. GTA IV

      1. The Lost and Damned
      2. The Ballad of Gay Tony
      3. Guides & Strategies
      4. Help & Support
    5. GTA San Andreas

      1. Classic GTA SA
      2. Guides & Strategies
      3. Help & Support
    6. GTA Vice City

      1. Classic GTA VC
      2. Guides & Strategies
      3. Help & Support
    7. GTA III

      1. Classic GTA III
      2. Guides & Strategies
      3. Help & Support
    8. Portable Games

      1. GTA Chinatown Wars
      2. GTA Vice City Stories
      3. GTA Liberty City Stories
    9. Top-Down Games

      1. GTA Advance
      2. GTA 2
      3. GTA
    1. Red Dead Redemption 2

      1. PC
      2. Help & Support
    2. Red Dead Redemption

    1. GTA Mods

      1. GTA V
      2. GTA IV
      3. GTA III, VC & SA
      4. Tutorials
    2. Red Dead Mods

      1. Documentation
    3. Mod Showroom

      1. Scripts & Plugins
      2. Maps
      3. Total Conversions
      4. Vehicles
      5. Textures
      6. Characters
      7. Tools
      8. Other
      9. Workshop
    4. Featured Mods

      1. Design Your Own Mission
      2. OpenIV
      3. GTA: Underground
      4. GTA: Liberty City
      5. GTA: State of Liberty
    1. Rockstar Games

    2. Rockstar Collectors

    1. Off-Topic

      1. General Chat
      2. Gaming
      3. Technology
      4. Movies & TV
      5. Music
      6. Sports
      7. Vehicles
    2. Expression

      1. Graphics / Visual Arts
      2. GFX Requests & Tutorials
      3. Writers' Discussion
      4. Debates & Discussion
    1. Announcements

    2. Forum Support

    3. Suggestions

passing string literal to a scm function


gokuta
 Share

Recommended Posts

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 by gokuta
Link to comment
Share on other sites

I'll wait with you for somebody with an answer to your question :D,

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 by kosticn101
Link to comment
Share on other sites

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 0
2. 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 by DK22Pac
Link to comment
Share on other sites

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 by kosticn101
Link to comment
Share on other sites

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 by DK22Pac
Link to comment
Share on other sites

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 by gokuta
Link to comment
Share on other sites

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 by kosticn101
Link to comment
Share on other sites

Thanks, got it and it works. Now I am experiencing one more problem :D

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 by gokuta
Link to comment
Share on other sites

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 by kosticn101
Link to comment
Share on other sites

 

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

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=1061729193

About 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 by kosticn101
Link to comment
Share on other sites

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 :whuh:

Link to comment
Share on other sites

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 by DK22Pac
  • Like 2
Link to comment
Share on other sites

 

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. :sly:

 

==== 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

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 by DK22Pac
Link to comment
Share on other sites

 

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 by kosticn101
Link to comment
Share on other sites

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 :ph34r:

There also some secret why forced use text in hex, plus with the help of bitwise operation for accepted their condition uppercases & lowercases.

Edited by X-Falcon
Link to comment
Share on other sites

 

 

 

#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 by DK22Pac
  • Like 2
Link to comment
Share on other sites

Seriously, nobody takes proper care of CLEO anymore...

Edited by gokuta
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • 1 User Currently Viewing
    0 members, 0 Anonymous, 1 Guest

×
×
  • Create New...

Important Information

By using GTAForums.com, you agree to our Terms of Use and Privacy Policy.