Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
    1. Welcome to GTAForums!

    1. GTANet.com

    1. GTA Online

      1. The Cayo Perico Heist
      2. The Diamond Casino Heist
      3. Find Lobbies & Players
      4. Guides & Strategies
      5. Vehicles
      6. Content Creator
      7. Help & Support
    2. Red Dead Online

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

    1. Red Dead Redemption 2

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

    1. Grand Theft Auto Series

    2. GTA VI

      1. St. Andrews Cathedral
    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. Guides & Strategies
      2. Help & Support
    6. GTA Vice City

      1. Guides & Strategies
      2. Help & Support
    7. GTA III

      1. Guides & Strategies
      2. 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. 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
    3. Gangs

    1. Announcements

    2. Support

    3. Suggestions

Sign in to follow this  

Define your own text

Recommended Posts


I know this may seem a bit pointless but there are uses for it, especially if you combine it with my key reader code where (provided you ensure against buffer overruns which can seriously screw up your game) you can type and display text. Anyway, here is a way to define text through the SCM (useful for typing as previously mentioned or changing text).


First you need to set up CyQs memory hacking code and a variable we will be using throughout:



0004: 8288?? = -1995422835&&0004: 8292?? = 1342335705&&0004: 8296?? = -1025694744&&0004: 8300?? = 1949868543&&0004: 8304?? = 495648893&&0004: 8308?? = 8221756&&0004: 8312?? = -1070589815&&0004: 8316?? = 10011777&&0004: 8320?? = 1599930368&&0004: 8324?? = 79846238&&0004: 8328?? = 272862464&&0004: 8332?? = 23779721&&0004: 8336?? = -587536304&&0004: 8340?? = 950140866&&0004: 8344?? = -1962902156&&0004: 8348?? = 1949868800&&0004: 8352?? = 1133314173&&0004: 8356?? = 1792641296&&0004: 8360?? = 568872961&&0004: 8364?? = 822067931&&0004: 8368?? = -1731952192&&0004: 8372?? = 1560281088&&0004: 8376?? = -1034199457&&0004: 8380?? = 4&&    03FD: set_player -5035& handling_responsiveness  8532704&&0124: write_mem_address  6850752&& value  8532745&&



0004: $SCM_MEM_OFFSET = 8524416&&



This will set up OpCodes 0124 and 0125 as write_mem and read_mem respectively, as detailed here and in the original topic linked to in there.


The variable defined here is just the address in memory of the start of the SCM block (0x821280) and is a constant. This just saves us having to type the address out every time we need it.


Next (note: you must make sure that the game will execute the command in the correct order (except the next, which must NOT be executed, merely exist)) you need to find the pointer to the text we are going to overwrite to display this. Most of the original games have unused GXT entries (and they say as much in the file), however it is easier to temporarilly steal a name then return it after use. Unfortunately the GXT entries are loaded into a random point of memory so we cannot simply hard code this, however there is a pointer to the start at 0x94B220 (9744928) so we can read this address to get our pointer address. This is the hardest part as you need to know the name of the first GXT entry in main, fortunately this is (unless your GXT has been completely redone) usually ACCURA so we will assume this is so. This does not matter for this part but will later for displaying out text.


Basically we read the address at 0x94B220 and store the result to $GXT_POINTER (must be a global as we use lots of different threads for this) then read the address at $GXT_POINTER and store the result to $GXT_ORIGINAL. This saves the location of the original ACCURA text so we don't loose it. This code should also be run every time the game is started as the address will change every time. If you are using darkpact, simply add it to the end of the darkpact code in mission 1, otherwise make your own version:



0125: read_mem_address  9744928&& into $GXT_POINTER0125: read_mem_address  $GXT_POINTER into $GXT_ORIGINAL



Now we are ready to define the text. This uses a little known feature of the create_thread OpCode, it is variable length. This means we can define each letter after it as it's ASCII value in decimal for as long as we like (there is an ingame limit to the length of the OpCode but we never execute this code so it doesn't matter). Text ingame is in unicode format which (effectively) means that every ASCII character has a 0x00 between them, if we use a 1 byte number for each letter (which we can) there will be a 0x04 (the data type definition for a 1 byte number) between each one, this can be sorted out later. Ingame text is also 0x000000 (triple NULL) delimited, the create_thread OpCode is also NULL delimited so we would need a 0000: nop command after the text for this to work, however because of the way our code later works, we need 2 0000: nops after it so we can detect a 4 byte end to the text:



:labeltext1004F: create_thread 72? 101? 108? 108? 111? 32? 87? 111? 114? 108? 100? 33?;                    H    e    l    l    o       W    o    r    l    d   !    0000: nop0000: nop



This must be an unreachable piece of code but must still have the label (the label is very important). This can either be done by placing the code outside a thread or directly after the 'DEFINE MEMORY' line or by jumping past it in code, e.g:



0002: jump ££aftertext\\ Text (including label) goes here:aftertext



If you are adding this as a darkpact mod (the OpCode writing code is already set up to this effect) the easiest way is to add it just after the thread(s) you are adding.


Now we need to change this into unicode format text, as it is at the moment the code will compile to look something like this:



4F 00 04 48 04 65 04 6C 04 6C 04 6F 04 20 04 57 04 6F 04 72 04 6C 04 64 04 21 00 00 00 00 00



But we want it to look like this (as explained before):



4F 00 00 48 00 65 00 6C 00 6C 00 6F 00 20 00 57 00 6F 00 72 00 6C 00 64 00 21 00 00 00 00 00



So we need to run a thread to do this, this again makes use of create_thread's variable length, but this time the parameters will be passed to it and the thread run. You will need the following for every piece of text you are adding through this method:



004F: create_thread ££CREATE_TEXT ££labeltext1



Notice the 2 labels on this line, the first is the label of the thread we are creating (and will remain constant for every instance of this code as we will have multiple running copies of the same code), the second will pass the address of the text we want to use to [email protected] (the first local) within our new thread. If you defined the text directly after the 'DEFINE MEMORY' line it will be saved with other globals as we want it (i.e. saved modified after we modify it), otherwise you will need to run these threads every time again (see the part on defining the GXT pointer).


Thread code:



:CREATE_TEXT   005c: [email protected] += $SCM_MEM_OFFSET:CREATE_TEXT_loop0125: read_mem_address  [email protected] into [email protected]  00D6: if  0?  8039:    NOT [email protected] ==  65532&& 004D: jump if false ££CREATE_TEXT_end000E:  [email protected] -=  262144&&0124: write_mem_address  [email protected] value  [email protected]:  [email protected] +=  2?  0002: jump ££CREATE_TEXT_loop  :CREATE_TEXT_end0124: write_mem_address  [email protected] value  0?004E: end_thread



As we defined [email protected] in the create_thread line, the first line, '005c: [email protected] += $SCM_MEM_OFFSET', which modifies [email protected] is vaild, '0006: [email protected] = $SCM_MEM_OFFSET' would overwrite our definition. This sets the value of [email protected] to the absolute address of the passed label (i.e. the address in memory, rather than just the address in the SCM). We then strip out the 0x04s one by one by subtracting 262144 (0x40000) from every 2 bytes. SCM numbers are stored little endian so the first 4 bytes will be:



4F 00 04 48 - 00 00 04 00 = 4F 00 00 48



The address then advances 2 (not 4) and repeats:



00 48 04 65 - 00 00 04 00 = 00 48 00 65



Notice that the first 0x04 is already 0x00 from the previous run of the code. This code continues until the current 4 bytes = 65532, the reason for this is as follows:


The last 4 bytes of text code are:



04 64 04 21



After a few repetitions this will look like:



00 64 04 21



After another one this will look like:



00 64 00 21



We now have all the text in unicode format, however our loop does not know this so it goes on to do the next set of 4:



00 21 00 00 - 00 00 04 00 = 00 21 FC FF (-253696)



This is because the original number is less than 262144 so we get a negative number, which is stored as a signed 32 bit integer. The code does that fine then moves onto the next set (it still does not know it's finished). The next set looks like:



FC FF 00 00



Which in decimal is our magic number 65532, the code now knows it has finished, it resets those 4 bytes to 0 and ends the thread, the code now in full unicode format and correctly delimited.


Now the final part, displaying the text. The following code is only required once in your SCM:



:MAKE_TEXT005c: [email protected] += $SCM_MEM_OFFSET  \\ integer values (never used in VC or GTA 3)000A: [email protected] +=  3?  \\ integer values  0124: write_mem_address  $GXT_POINTER value  [email protected]: return:UNMAKE_TEXT0124: write_mem_address  $GXT_POINTER value  $GXT_ORIGINAL0051: return



Then use:



0006: [email protected] = ££labeltext1 0050: gosub ££MAKE_TEXT 00BC: text highpriority "ACCURA"  10000&& ms  1?0050: gosub ££UNMAKE_TEXT 



Wherever you want to display your custom text. The whole code uses the label to reference your text, so you set [email protected] to the label of the text you want to display then call the newly defined sub. This adds the SCM base address to [email protected] and another 3 (as the start of the actual unicode text is 3 bytes after the label as there is the 2 byte OpCode and the first parameter type which we don't want), redirects the ACCURA pointer to our text and returns. We then display our text using whichever text OpCode you want and return the text pointer to its original position. It is possible to use an unused GXT entry to display the text so we don't need to return it every time (such as 'RCH1_1'), however this requires searching memory for the pointer, this can be done using the following code but as it is extra I'm not explaining how it works. Having said that, just so you don't get too confused, I use the variable $GXT_ORIGINAL for 2 different things to save on globals and set it to the original address of the text at the end anyway:



0125: read_mem_address  9744928&& into $GXT_POINTER0084:  $GXT_ORIGINAL =  $GXT_pointer  0008:  $GXT_ORIGINAL +=  4?0006: [email protected] = 0?0006: [email protected] = 826819410&&:find_RCH1_1_10125: read_mem_address  $GXT_ORIGINAL into [email protected] 00D6: if  0?  803b:   NOT [email protected] == [email protected] 004D: jump if false ££find_RCH1_1_20008: $GXT_ORIGINAL +=  12? 000A: [email protected] +=  12? 00D6: if  0?  8029:    NOT [email protected] >=  30000& 004D: jump if false ££find_RCH1_1_30002: jump ££find_RCH1_1_1:find_RCH1_1_2   00D6: if  0?  0039:    [email protected] == 826819410&&004D: jump if false ££find_RCH1_1_30006: [email protected] = 12639&0008: $GXT_ORIGINAL +=  4?0002: jump ££find_RCH1_1_1




005e: $GXT_POINTER += [email protected]

0125: read_mem_address $GXT_POINTER into $GXT_ORIGINAL[/code]


I know all this all seems alot of hard work for not alot of advantage but most of it is just initial setup and once done you can just use:



0006: [email protected] = ££labeltext1 0050: gosub ££MAKE_TEXT 00BC: text highpriority "ACCURA"  10000&& ms  1?0050: gosub ££UNMAKE_TEXT 



to display your text from wherever you want. It even works with numerical text, despite the fact that the original ACCURA text wasn't a number text:



:labeltext2004F: create_thread 72? 101? 108? 108? 111? 32? 87? 111? 114? 108? 100? 32? 126? 49? 126?;                    H    e    l    l    o       W    o    r    l    d        ~   1    ~0000: nop0000: nop






0006: [email protected] = ££labeltext2 0050: gosub ££MAKE_TEXT 01E5: text 1number highpriority "ACCURA"  100?  10000&& ms  1?0050: gosub ££UNMAKE_TEXT 


Share this post

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

  • 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.