Quantcast

Jump to content

» «
Photo

[SA] Memory access via stat opcodes

37 replies to this topic
pdescobar
  • pdescobar

    Conformist Scum Panda

  • Members
  • Joined: 19 Jul 2005

#1

Posted 20 September 2006 - 11:17 AM Edited by pdescobar, 08 October 2006 - 06:22 PM.

Update (1 Oct 2006) -- The first post was drastically reorganized to provide a summary of the current situation and remove some outdated information. The genesis of this idea was discussions between PLPynton and myself during a brief hijacking of Dem's TFB development topic.

Note that all memory locations given in this post are for the US version 1.0 exe; for other regions and versions, the absolute locations are probably different but the method of accessing them should be the same.

The SA stat opcodes do not do boundary checking on the stat ID numbers they are passed, so they provide limited memory access to locations near the stat pools. The chunk of memory you can get to isn't super large, but there are some interesting things you can play around with. The stat pools themselves, based on op9080's memory investigations are an array of 223 ints (stats 120-342) starting at location 0xB79000 and an array of 82 floats (stats 0-81) starting at location 0xB79380.

But, you can use stat IDs 82-119 to get to the memory just before this range and IDs 343-65535 to get to memory above this range. Essentially, what happens is that for a given X (where X is from 82 to 65535 inclusive) stat X gives you access to memory location 0xB78F68 + 4*(X-82). Because these values are handled by the opcodes as if you are reading from the integer stat pool, they will be read and written as 4-byte integers, meaning you will generally have to translate them if the underlying data is really another size and/or type.

Note that because the float stat pool (stats 0-81) is also inside this range, you get an overlap where stats 344-425 also access stats 0-81, however they interpret them as INTs and so you're generally better off accessing these stats in the normal manner. Below is a table showing the memory layout and how it corresponds to the stat ID numbers:

CODE
Offset-H  Offset-D  Stat ID
========  ========  ========
0xB78F68  12029800  stat 82  (always read as 4b INT)
  ...       ...       thru
0xB78FFC  12029948  stat 119 (always read as 4b INT)
0xB79000  12029952  stat 120 (properly-handled INTs)
  ...       ...       thru
0xB79378  12030840  stat 342 (properly-handled INTs)
0xB7937C  12030844  stat 343 (always read as 4b INT)
0xB79380  12030848  stat 344 (always read as 4b INT) or 0  (properly-handled floats)
  ...       ...       thru
0xB794C4  12031172  stat 425 (always read as 4b INT) or 81 (properly-handled floats)
0xB794C8  12031176  stat 426 (always read as 4b INT)
  ...       ...       thru
0xBB8E1C  12291612  stat 65535 (always read as 4b INT)


Other stat ID numbers work as well, but they overflow into the 0-65535 range. For example, stat -1 is the same as stat 65535 and stat 65536 is the same as stat 0. To restate the summary, the full useful range of stat id numbers appears to be 0-65535 with stats 82-65535 spanning the entire memory range of 0xB78F68 - 0xBB8E1F in 4-byte integer chunks and stats 0-81 overlapping a small part of that range but providing true float access to that part.

The first main caveat to this memory access is that the stat opcodes will always interpret the value in memory as a signed 4-byte integer even if it is something else. Therefore, you always want to use 0652 for reading so that the stat opcodes don't wrongly transform the value. For example, if you are reading a value you know is a float, you still use 0652 to read it, and then just treat it as a float afterwards.

The second main caveat is that the stat-changing opcodes are limited in how much they can change those stats. They will only assign (or increment) in the range of -32768 to 32767. So if you need to assign a much higher value, as when working with a float, you have to use multiple small increments until you reach your goal.

Here's an example using stat 85. The set of stat IDs 82-119 access the memory just before the two stat pools and IDs 82-118 correspond to definitions 22-58 in ar_stats.dat. For the example, I want to change stat 85 from 10.0 to 20.0. Because these are outside the "legitimate" stat pools, they are treated as integers and so 10.0 is read as 1092616192 and 20.0 would be 1101004800. Thus, I need to add 8388608 to the current value, but have to do it in chunks no larger than 32767. See the examples section at the end for code which does this.

Here are a couple of other examples of stat-based memory access chosen because they were known locations which would have easily verifiable results. I chose the player's money which is stored as 32-bit ints at locations 0xB7CE50 (actual value) and 0xB7CE54 (displayed value), (and elsewhere) and the color of the text of the money display on the HUD at xBAB230 (1 byte each for RGBA). Taking the offsets of those locations from the start of the stat pool and plugging into the previously listed formula, that gives "stat" values of 4108, 4109 and 51460 respectively.

After setting my money via one of the normal opcodes:
CODE
0109: player $PLAYER_CHAR money += 53891

the 4108/4109 stats updated to the same value, as seen in the below screenshot (you'll have to trust me on the output tounge2.gif) user posted image

Similarly, the following command in the script set the money back to 30000:
CODE
0629: change_stat 4108 to 30000


As for the HUD, somewhat randomly changing the value of stat 51460 resulted in the expected on-screen results:
user posted image

The rest of the topic concerns interesting memory locations we can reach and what to do with them as well as general discussions on the methods.

=====================================================

Code examples (Sanny Builder format)

Making adjustments
No matter what the underlying data type is, to change them you have to treat them as integers and make the changes in several small increments. Note that this can take a quite long time if the values are vastly different. Suggestions for more efficient methods are greatly appreciated.

Here's a generic updating subroutine for the stats outside the legitimate ranges. I've updated it to handle both "absolute" updates (similar to the 0629: change_stat opcode) as well as "relative" updates similar to 0623: increase_stat and 0625: decrease_stat opcodes. If you gosub to @StatUpdateAbsolute, it makes the necessary adjustments to 22@ and falls through to the relative code; note that the result is that 22@ is modified from what was sent to it. In either case, it assumes 21@ is the stat ID and 22@ is the new value or increase/decrease. Note that the absolute call also uses 23@ for temporary storage.
CODE
:StatUpdateAbsolute
// Changes stat 21@ to be exactly 22@
0652: 23@ = stat 21@
0062: 22@ -= 23@

:StatUpdateRelative
// Changes stat 21@ by adding 22@ to it
// A positive 22@ results in an increase, and a negative 22@ is a decrease.
if
 0 > 22@
jf @StatUpdateIncreaseLoop
22@ *= -1 // make it positive
:StatUpdateDecreaseLoop
if
 22@ >= 32767
jf @StatUpdateMakeLastDecrease
wait 0
0625: decrease_stat 21@ by 32767
22@ -= 32767
jump @StatUpdateDecreaseLoop
:StatUpdateMakeLastDecrease
0625: decrease_stat 21@ by 22@
jump @StatUpdateEnd

:StatUpdateIncreaseLoop
if
 22@ >= 32767
jf @StatUpdateMakeLastIncrease
wait 0
0623: increase_stat 21@ by 32767
22@ -= 32767
jump @StatUpdateIncreaseLoop
:StatUpdateMakeLastIncrease
0623: increase_stat 21@ by 22@

:StatUpdateEnd
return


And here's an alternative version from Seemann and slightly modified by me which takes advantage of Sanny Builder's ability to have a more high-level syntax. The advantages are readability and not having to worry about internal label names; but SAMB users would have to work a little harder to translate it wink.gif
CODE
:StatUpdateAbsolute
// Changes stat 21@ to be exactly 22@
0652: 23@ = stat 21@
0062: 22@ -= 23@

:StatUpdateRelative
// Changes stat 21@ by adding 22@ to it
// A positive 22@ results in an increase, and a negative 22@ is a decrease.
if 22@ > 0
then
  while 22@ >= 32767
    wait 0
    0623: increase_stat 21@ by 32767
    22@ -= 32767
  end
  0623: increase_stat 21@ by 22@
else
  22@ *= -1 // make it positive
  while 22@ >= 32767
    wait 0
    0625: decrease_stat 21@ by 32767
    22@ -= 32767
  end
  0625: decrease_stat 21@ by 22@
end
return



Type Conversions

32-bit floats - you want to read them as integer with 0652, then treat them as floats for processing, then switch back and treat them as integers for changing the stat value.

Reading:
CODE
0652: 10@ = stat 85 // read as int
10@ += 1.0  // use float assignment and math afterwards
0087: 11@ = 10@ // use float assignment and math afterwards


Changing to a specific hardcoded target value:
CODE
0652: 10@ = stat 85 // read as int (e.g. 1092616192)
11@ = 20.0 // hardcoded target value

21@ = 85 // stat ID num
0085: 22@ = 11@  // int assign
0062: 22@ -= 10@ // int difference for stat value change (e.g. 8388608)
gosub @StatUpdateRelative // See above subroutine


Changing to a calculated target value (in this case 1/2 of original):
CODE
0652: 10@ = stat 86 // read as int

0087: 11@ = 10@ // treat as float to process
11@ *= 0.5 // calculated target value

21@ = 86 // stat ID num
0085: 22@ = 11@  // int assign
0062: 22@ -= 10@ // int difference for stat value change
gosub @StatUpdateRelative // See above subroutine


RGBA color definitions:
Reading:
CODE
0652: 10@ = stat 51460 // as integer

0085: 13@ = 10@
13@ /= 16777216 // 13@ will be Alpha byte
0085: 15@ = 13@
if
 0 > 10@
jf @SkipNegativeA
// The INT value is negative, so we have to adjust
15@ -= 1
13@ = 256
005A: 13@ += 15@
:SkipNegativeA
15@ *= 16777216
0062: 10@ -= 15@

0085: 12@ = 10@
12@ /= 65536 // 12@ will be Blue byte
0085: 15@ = 12@
15@ *= 65536
0062: 10@ -= 15@

0085: 11@ = 10@
11@ /= 256 // 11@ will be Green byte
0085: 15@ = 11@
15@ *= 256
0062: 10@ -= 15@ // 10@ will be Red byte

0302: text_4numbers 'HJSTATF' 10@ 11@ 12@ 13@ 10000 ms 1 // debugging output to easily check numbers


Writing: (Note this can take freaking forever with large changes to the alpha because of the inefficient looping assignment methods). Note also that I use the absolute version of the stat update subroutine here.
CODE
10@ = 255 // Red
11@ = 128 // Green
12@ = 128 // Blue
13@ = 255 // Alpha
0085: 14@ = 10@
0085: 15@ = 11@
15@ *= 256
005A: 14@ += 15@
0085: 15@ = 12@
15@ *= 65536
005A: 14@ += 15@

// Deal with potential signed values
if
 13@ > 127
jf @SkipNegative  
13@ -= 256
:SkipNegative
0085: 15@ = 13@
15@ *= 16777216
005A: 14@ += 15@ // 14@ is now the INT for the RGBA quad

21@ = 51460
0085: 22@ = 14@
gosub @StatUpdateAbsolute // See above subroutine

PLPynton
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#2

Posted 20 September 2006 - 02:02 PM Edited by PLPynton, 20 September 2006 - 02:20 PM.

mate thank you for opening this, i was also hesitating to trash over another topic.

i found think about more safe method to write them with check (Stat 46689 MENU-ACTIVE OR PLAYING) then just bump it. some of memory adress can not be changed while plying because of crash. and another is that negative values work too bu not the same adressing applies.

mem adress for stat
stat = 0x00b78E20h + 4 * stat number

ok, here are my examples for you all. examples of useless stuff and heavy ressistible stuff as well.
Stat 04108 ---Money Posessed
Stat 04109 ---Money Displayed
Stat 50560 ---Wanted Level (RO)
Stat 46683 MENU-SUBTITLES
Stat 46681 MENU-BRIGHTNESS (0-384)
Stat 46675 MENU-RADAR MODE
Stat 46674 MENU-HUD MODE
Stat 46724 MENU-STORE GALLERY PHOTOS
Stat 46682 MENU-DRAW DISTANCE
Stat 46717 MENU-ANTI ALIASING (1 = off, 2 = 1x, 3 = 2x, 4 = 3x)
Stat 46720 MENU-RESOLUTION
Stat 46715 MENU-MIP MAPPING
Stat 46718 MENU-CONTROLLER (0 = mouse+keys, 1 = joypad)
Stat 46689 MENU-ACTIVE OR PLAYING
Stat 51447 HUD-WEAPON TEXTURE
Stat 51450 HUD-RADAR TEXTURE
Stat 51459 HUD-HEALTH BAR (RGBA)
Stat 51460 HUD-CASH & ENTER VEHICLE (RGBA)
Stat 51461 HUD-P2 (RGBA)
Stat 51462 HUD-BREATH METER BAR & STATUS TEXT (RGBA)
Stat 51463 HUD-CLOCK & ARMOUR BAR (RGBA)
Stat 51464 HUD-STYLED TEXT (RGBA)
Stat 51465 HUD-WANTED METER (RGBA)
Stat 51467 HUD-RADIOSTATION TEXT (RGBA)
here is heavy sh*t:
Stat 49154 MENU-MAP-PLAYER TARGET X
Stat 49155 MENU-MAP-PLAYER TARGET Y
Stat 49164 MENU-MAP-PLAYER TARGET X
Stat 49165 MENU-MAP-PLAYER TARGET Y
Stat 49144 MENU-MAP-PLAYER TARGET X
Stat 49145 MENU-MAP-PLAYER TARGET Y
here is a way of setting custom text on screen without gxt twists
Stat 50784 ---TEXT BOX 4Letters-01
thru
Stat 50880 ---TEXT BOX 4Letters-97
Stat 51176 ---TEXT STYLED 4Letters-01
thru
Stat 51224 ---TEXT STYLED 4Letters-48

Demarest
  • Demarest

    what could be

  • BUSTED!
  • Joined: 12 Jul 2003

#3

Posted 20 September 2006 - 04:55 PM

Looks like I need to start boning up on SA mem-hacking. SCM-based mem-hacking is so damn sexy. As is a purple cash readout wink.gif I can't wait to abuse this. Anything I can do to help in terms of development? Though it sounds like the two of you have it well under control...

PLPynton
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#4

Posted 20 September 2006 - 05:29 PM

QUOTE (Demarest @ Sep 20 2006, 16:55)
Looks like I need to start boning up on SA mem-hacking. SCM-based mem-hacking is so damn sexy. As is a purple cash readout wink.gif I can't wait to abuse this. Anything I can do to help in terms of development? Though it sounds like the two of you have it well under control...

your help will be needed.
we need:

1. coded method to update stats to any given value also negative ones, pseudo floats and casual text strings (some of completed in first post)
2. lots of testings for hazardous memory chunks (you update 4 bits at once in loop and sometimes one stat applies to 4 bits with few functions).
3. new adresses: get some art money or whatever, trace something then check if its offset is above B78E20h or else it is going to be hard to get to it thru script. you can check memory adresses topic but you will not find much over there because they were always chasing functions that we do have in coding.

yesterday i have finished my teleporter code thanks to mem access, now i can place marker on map and press the button to teleport player right to this spot (adresses in second post).

i am looking since ages for airplane velocity 75.0 m/s limit someone?

i suspect radio counters are located around 0096907Bh and i have not found favourite radio stat nowhere till now.

pdescobar
  • pdescobar

    Conformist Scum Panda

  • Members
  • Joined: 19 Jul 2005

#5

Posted 20 September 2006 - 10:18 PM

QUOTE (PLPynton @ Sep 20 2006, 13:29)
yesterday i have finished my teleporter code thanks to mem access, now i can place marker on map and press the button to teleport player right to this spot (adresses in second post).

Oooh. nice!

QUOTE (continued...)
i am looking since ages for airplane velocity 75.0 m/s limit someone?

Well the Hydra speed limit might be an unsigned byte at 0x6DADE8; quite a bit lower in memory than we can currently get to easily. That assumes that I am reading the source code to op9080's SCM Hook correctly. Plane and Jetpack height limits are near that as well.

QUOTE (continued...)
i suspect radio counters are located around 0096907Bh and i have not found favourite radio stat nowhere till now.

Hmmm. Interesting. monocle.gif I need to learn better memory-hacking skills.


Some more addresses:
The section of stats 430-443 seem to be stat and skill timers related to ar_stats.dat; I haven't investigated them fully yet but here are some preliminary guesses:
  • stat 430: increases when sprinting; possibly timer for sprint stamina increase
  • stat 431: increases when sprinting on bike; possibly timer for cycle stamina increase
  • stat 432: increases when on bike and moving over a certain minimum speed; possibly timer for cycle skill increase
  • stat 433: increases when swimming; possibly timer for swim stamina increase
  • stat 435: increases when in car and moving over a certain minimum speed; possibly timer for driving skill increase
  • stat 436: increases when in plane or heli and moving over a certain minimum speed; possibly timer for flying skill increase
  • stat 438: increases when on motorbike and moving over a certain minimum speed; possibly timer for bike skill increase
  • stats 439 and 443: increase when jogging, sprinting, swimming, cycling; possibly timers for fat decrease, muscle increase, or max health increase.

PLPynton
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#6

Posted 20 September 2006 - 11:22 PM

-1(INTEGER) Total mission points
-343() UNUSED BY EXE. you can store anything here
-345(FLOAT) Total mission points
-347(FLOAT) Distance traveled on foot
-348(FLOAT) Distance traveled by car
-349(FLOAT) Distance traveled by motorbike
-350(FLOAT) Distance traveled by boat
-351(FLOAT) Distance traveled by golf cart
-352(FLOAT) Distance traveled by helicopter
-353(FLOAT) Distance traveled by plane
-372(FLOAT) Distance traveled on exercise bike
-452(INTEGER) Hazardous- do not change it
-492(INTEGER) Hazardous- do not change it

i have a question:
there are few kind of 4b adress types:

1. truncated whatever integer or float like all the normal ingame stats
2. normal (you can change them to 65535 for example in one step (stat 81 or 82 i do not recall)
3. however you read them you have to get them to int then to flaot ant this again to float (stat 365 for example)

how we will indicate them in description? A B C D ...?

pdescobar
  • pdescobar

    Conformist Scum Panda

  • Members
  • Joined: 19 Jul 2005

#7

Posted 21 September 2006 - 01:21 AM

QUOTE (PLPynton @ Sep 20 2006, 19:22)
-1(INTEGER) Total mission points
-343() UNUSED BY EXE. you can store anything here
-345(FLOAT) Total mission points
-347(FLOAT) Distance traveled on foot
-348(FLOAT) Distance traveled by car
-349(FLOAT) Distance traveled by motorbike
-350(FLOAT) Distance traveled by boat
-351(FLOAT) Distance traveled by golf cart
-352(FLOAT) Distance traveled by helicopter
-353(FLOAT) Distance traveled by plane
-372(FLOAT) Distance traveled on exercise bike

Any stat X from 344 to 425 (inclusive) should be just a less useful way of accessing stat (X-344) so we really don't need to list them explicitly. I say "Less useful" because they are read as INTs instead of their true floats.

QUOTE (continued...)
-452(INTEGER) Hazardous- do not change it
-492(INTEGER) Hazardous- do not change it

If I calculated the offset correctly, 452 is a pointer to the last controlled car since that's what Jernej posted for memory offset 0xB79530 in the memory topic. No clue on 492.

QUOTE (continued...)
i have a question:
there are few kind of 4b adress types:

1. truncated whatever integer or float like all the normal ingame stats
2. normal (you can change them to 65535 for example in one step (stat 81 or 82 i do not recall)
3. however you read them you have to get them to int then to flaot ant this again to float (stat 365 for example)

how we will indicate them in description? A B C D ...?

Stats 0-81 are all internally floats and the stat opcodes are smart enough to make the proper translations on the fly. So both "0629: change_stat 0 to 5" or "062A: change_stat 0 to 5.0" should work fine and result in the actual data becoming 5.0

Stats 120-342 are all internally 16-bit integers sign-extended to 32-bits and once again the stat opcodes translate when necessary.

Any other stat value will be the type 3 in your quote where they are treated as 32-bit integers and assignment involves the kind of looping shown in the first post. Since what you have to do to translate those to and from useful values is dependant upon what the underlying data types and sizes actually are, I'd say the simplest thing is just indicate the underlying data type in its descriptions like you did here.

Picolini
  • Picolini

    ZOMG ROFL XD!!!!11

  • BUSTED!
  • Joined: 07 Dec 2002

#8

Posted 21 September 2006 - 04:43 AM

I have no clue how you guys do any of this, but I understand what you are doing, and I have to say it sounds great. Keep up the good work guys. icon14.gif icon14.gif

jarjar
  • jarjar

    Boss

  • BUSTED!
  • Joined: 07 Aug 2005

#9

Posted 21 September 2006 - 05:52 AM

Bloody hell, this is awesome, I didn't realize anyone had found a way to do this yet. Or even that it was possible. I am very interested in finding how this all works out, it will change scm modding once again, I can see it now icon14.gif

Keep up the great work guys, I'll do some research and see if I can get in on the action too tounge.gif.

PLPynton
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#10

Posted 21 September 2006 - 12:44 PM Edited by PLPynton, 01 October 2006 - 08:03 PM.

pure way to change most of or even bring some (whenever needed) text messages, boxes ect.

user posted image

first thread is a simple button press engine to see effects:

CODE
:TESTER
03A4: name_thread 'TESTER'
0004: $CTEXT(@0,70i)  =  0;; integer values

:TESTER04
0001: wait  250 ms
00D6: if  0
0256:   is_player_playing $PLAYER
004D: jump_if_false ££TESTER04
00D6: if  0
80E1:   NOT   key_pressed  0  18
004D: jump_if_false ££TESTER70
00D6: if  0
80E1:   NOT   key_pressed  0  19
004D: jump_if_false ££TESTER80
0002: jump ££TESTER04

:TESTER70
00D6: if  0
0038:   $DISPLAY ==  0;; integer values
004D: jump_if_false ££TESTER04
0004: $CTMESSAGE  =  0;; integer values
0417: start_mission  135; Custom text database
0001: wait  50 ms
004F: create_thread ££CTEXT
0002: jump ££TESTER04

:TESTER80
00D6: if  0
0038:   $DISPLAY ==  0;; integer values
004D: jump_if_false ££TESTER04
0004: $CTMESSAGE  =  1;; integer values
0417: start_mission  135; Custom text database
0001: wait  50 ms
004F: create_thread ££CTEXT
0002: jump ££TESTER04
004E: end_thread


second part is main part where the adresses are written into memory:

CODE
:CTEXT
03A4: name_thread 'CTEXT'
0004: $DISPLAY  =  1;; integer values
03E6: remove_text_box
03EB: clear_small_messages_only
00BE: print_now_clear_all
0006: @0 = 0;; integer values
0006: @1 = 0;; integer values
008B: @2 = $STAT;; integer values and handles
00D6: if  25
0018:   $TIME >  10000;; integer values
0018:   $CTMAX >  95;; integer values
8018:   NOT   $CTMAX >  0;; integer values
8018:   NOT   $STAT >  0;; integer values
8018:   NOT   $COUNT >  0;; integer values
001C:   $COUNT > $CTMAX;; integer values
004D: jump_if_false ££CTEXT_1
0004: $STAT  =  50784;; integer values
0004: $TIME  =  5000;; integer values
0004: $CTMAX  =  75;; integer values
0004: $CTEXT  =  1095909712;; integer values
0004: $CTEXT(1)  =  1163150669;; integer values
0004: $CTEXT(2)  =  1461736274;; integer values
0004: $CTEXT(3)  =  1196314450;; integer values
0004: $COUNT  =  4;; integer values
008B: @2 = $STAT;; integer values and handles

:CTEXT_1
;0629: change_stat @2 to 0;; integer values
00D6: if  0
803C:   NOT   $CTMAX == @1;; integer values
004D: jump_if_false ££CTEXT_2
000A: @1 +=  1;; integer values
000A: @2 +=  1;; integer values
0002: jump ££CTEXT_1

:CTEXT_2
0652: get_stat  $STAT store_to @3; integer
0064: @3 -= $CTEXT(@0,95i);; integer values
00D6: if  0
001B:    0 > @3;; integer values
004D: jump_if_false ££CTEXT_5
0012: @3 *=  -1;; integer values

:CTEXT_3
00D6: if  0
0029:   @3 >=  32767;; integer values
004D: jump_if_false ££CTEXT_4
0623: increase_stat  $STAT by  32767;; integer values
000E: @3 -=  32767;; integer values
0002: jump ££CTEXT_3

:CTEXT_4
0623: increase_stat  $STAT by  @3;; integer values
0002: jump ££CTEXT_8

:CTEXT_5
00D6: if  0
0029:   @3 >=  32767;; integer values
004D: jump_if_false ££CTEXT_6
0625: decrease_stat  $STAT by  32767;; integer values
000E: @3 -=  32767;; integer values
0002: jump ££CTEXT_5

:CTEXT_6
0625: decrease_stat  $STAT by  @3;; integer values

:CTEXT_8
0008: $STAT +=  1;; integer values
000A: @0 +=  1;; integer values
00D6: if  0
803C:   NOT   $COUNT == @0;; integer values
004D: jump_if_false ££CTEXT_10
0001: wait  0 ms
0002: jump ££CTEXT_2

:CTEXT_10
0001: wait  $TIME ms
03E6: remove_text_box
03EB: clear_small_messages_only
00BE: print_now_clear_all
0004: $DISPLAY  =  0;; integer values
004E: end_thread


and this is an example how to not to lose so much memory in main section and handle containment of messages via mission script.

CODE
;-------------Mission 135---------------
; Custom text database

:CTDATA_1
03A4: name_thread 'CTDATA'

:CTDATA_2
0871: init_jump_table $CTMESSAGE total_jumps  6  0 £CTDATA_M00 jumps  1 £CTDATA_M01  2 £CTDATA_M02  3 £CTDATA_M03  4 £CTDATA_M04  5 £CTDATA_M05  6 £CTDATA_M06  -1 £CTDATA_M07

:CTDATA_M00
0004: $STAT =  50784;; integer values
0004: $TIME  =  5000;; integer values
0004: $CTMAX  =  75;; integer values
0004: $CTEXT  =  1919903827;; integer values
0004: $CTEXT(1)  =  1702109300;; integer values
0004: $CTEXT(2)  =  1663071352;; integer values
0004: $CTEXT(3)  =  1646292577;; integer values
0004: $CTEXT(4)  =  1869357157;; integer values
0004: $CTEXT(5)  =  1684366433;; integer values
0004: $CTEXT(6)  =  1935762720;; integer values
0004: $CTEXT(7)  =  1851859065;; integer values
0004: $CTEXT(8)  =  1634082916;; integer values
0004: $CTEXT(9)  =  1998615667;; integer values
0004: $CTEXT(10)  =  1701603688;; integer values
0004: $CTEXT(11)  =  1852795936;; integer values
0004: $CTEXT(12)  =  1852776551;; integer values
0004: $CTEXT(13)  =  1635000421;; integer values
0004: $CTEXT(14)  =  544433515;; integer values
0004: $CTEXT(15)  =  1701998445;; integer values
0004: $CTEXT(16)  =  1835627552;; integer values
0004: $CTEXT(17)  =  1869881445;; integer values
0004: $CTEXT(18)  =  1634692128;; integer values
0004: $CTEXT(19)  =  11876;; integer values
0004: $COUNT  =  20;; integer values
0002: jump £CTDATA_M07

:CTDATA_M01
0004: $STAT =  50784;; integer values
0004: $TIME  =  5000;; integer values
0004: $CTMAX  =  75;; integer values
0004: $CTEXT  =  1801680198;; integer values
0004: $CTEXT(1)  =  779381024;; integer values
0004: $COUNT  =  2;; integer values
0002: jump £CTDATA_M07

:CTDATA_M02
0004: $STAT =  50784;; integer values
0004: $TIME  =  5000;; integer values
0004: $CTMAX  =  75;; integer values
0004: $CTEXT  =  1919903827;; integer values
0004: $CTEXT(1)  =  1702109300;; integer values
0004: $CTEXT(2)  =  1663071352;; integer values
0004: $CTEXT(3)  =  1646292577;; integer values
0004: $CTEXT(4)  =  1869357157;; integer values
0004: $CTEXT(5)  =  1684366433;; integer values
0004: $CTEXT(6)  =  1935762720;; integer values
0004: $CTEXT(7)  =  1851859065;; integer values
0004: $CTEXT(8)  =  1634082916;; integer values
0004: $CTEXT(9)  =  1998615667;; integer values
0004: $CTEXT(10)  =  1701603688;; integer values
0004: $CTEXT(11)  =  1852795936;; integer values
0004: $CTEXT(12)  =  1852776551;; integer values
0004: $CTEXT(13)  =  1635000421;; integer values
0004: $CTEXT(14)  =  544433515;; integer values
0004: $CTEXT(15)  =  1701998445;; integer values
0004: $CTEXT(16)  =  1835627552;; integer values
0004: $CTEXT(17)  =  1869881445;; integer values
0004: $CTEXT(18)  =  1634692128;; integer values
0004: $CTEXT(19)  =  11876;; integer values
0004: $COUNT  =  20;; integer values
0002: jump £CTDATA_M07

:CTDATA_M03
0004: $STAT =  50784;; integer values
0004: $TIME  =  5000;; integer values
0004: $CTMAX  =  75;; integer values
0004: $CTEXT  =  1919903827;; integer values
0004: $CTEXT(1)  =  1702109300;; integer values
0004: $CTEXT(2)  =  1663071352;; integer values
0004: $CTEXT(3)  =  1646292577;; integer values
0004: $CTEXT(4)  =  1869357157;; integer values
0004: $CTEXT(5)  =  1684366433;; integer values
0004: $CTEXT(6)  =  1935762720;; integer values
0004: $CTEXT(7)  =  1851859065;; integer values
0004: $CTEXT(8)  =  1634082916;; integer values
0004: $CTEXT(9)  =  1998615667;; integer values
0004: $CTEXT(10)  =  1701603688;; integer values
0004: $CTEXT(11)  =  1852795936;; integer values
0004: $CTEXT(12)  =  1852776551;; integer values
0004: $CTEXT(13)  =  1635000421;; integer values
0004: $CTEXT(14)  =  544433515;; integer values
0004: $CTEXT(15)  =  1701998445;; integer values
0004: $CTEXT(16)  =  1835627552;; integer values
0004: $CTEXT(17)  =  1869881445;; integer values
0004: $CTEXT(18)  =  1634692128;; integer values
0004: $CTEXT(19)  =  11876;; integer values
0004: $COUNT  =  20;; integer values
0002: jump £CTDATA_M07

:CTDATA_M04
0004: $STAT =  50784;; integer values
0004: $TIME  =  5000;; integer values
0004: $CTMAX  =  75;; integer values
0004: $CTEXT  =  1919903827;; integer values
0004: $CTEXT(1)  =  1702109300;; integer values
0004: $CTEXT(2)  =  1663071352;; integer values
0004: $CTEXT(3)  =  1646292577;; integer values
0004: $CTEXT(4)  =  1869357157;; integer values
0004: $CTEXT(5)  =  1684366433;; integer values
0004: $CTEXT(6)  =  1935762720;; integer values
0004: $CTEXT(7)  =  1851859065;; integer values
0004: $CTEXT(8)  =  1634082916;; integer values
0004: $CTEXT(9)  =  1998615667;; integer values
0004: $CTEXT(10)  =  1701603688;; integer values
0004: $CTEXT(11)  =  1852795936;; integer values
0004: $CTEXT(12)  =  1852776551;; integer values
0004: $CTEXT(13)  =  1635000421;; integer values
0004: $CTEXT(14)  =  544433515;; integer values
0004: $CTEXT(15)  =  1701998445;; integer values
0004: $CTEXT(16)  =  1835627552;; integer values
0004: $CTEXT(17)  =  1869881445;; integer values
0004: $CTEXT(18)  =  1634692128;; integer values
0004: $CTEXT(19)  =  11876;; integer values
0004: $COUNT  =  20;; integer values
0002: jump £CTDATA_M07

:CTDATA_M05
0004: $STAT =  50784;; integer values
0004: $TIME  =  5000;; integer values
0004: $CTMAX  =  75;; integer values
0004: $CTEXT  =  1919903827;; integer values
0004: $CTEXT(1)  =  1702109300;; integer values
0004: $CTEXT(2)  =  1663071352;; integer values
0004: $CTEXT(3)  =  1646292577;; integer values
0004: $CTEXT(4)  =  1869357157;; integer values
0004: $CTEXT(5)  =  1684366433;; integer values
0004: $CTEXT(6)  =  1935762720;; integer values
0004: $CTEXT(7)  =  1851859065;; integer values
0004: $CTEXT(8)  =  1634082916;; integer values
0004: $CTEXT(9)  =  1998615667;; integer values
0004: $CTEXT(10)  =  1701603688;; integer values
0004: $CTEXT(11)  =  1852795936;; integer values
0004: $CTEXT(12)  =  1852776551;; integer values
0004: $CTEXT(13)  =  1635000421;; integer values
0004: $CTEXT(14)  =  544433515;; integer values
0004: $CTEXT(15)  =  1701998445;; integer values
0004: $CTEXT(16)  =  1835627552;; integer values
0004: $CTEXT(17)  =  1869881445;; integer values
0004: $CTEXT(18)  =  1634692128;; integer values
0004: $CTEXT(19)  =  11876;; integer values
0004: $COUNT  =  20;; integer values
0002: jump £CTDATA_M07

:CTDATA_M06
0004: $STAT =  50784;; integer values
0004: $TIME  =  5000;; integer values
0004: $CTMAX  =  75;; integer values
0004: $CTEXT  =  1919903827;; integer values
0004: $CTEXT(1)  =  1702109300;; integer values
0004: $CTEXT(2)  =  1663071352;; integer values
0004: $CTEXT(3)  =  1646292577;; integer values
0004: $CTEXT(4)  =  1869357157;; integer values
0004: $CTEXT(5)  =  1684366433;; integer values
0004: $CTEXT(6)  =  1935762720;; integer values
0004: $CTEXT(7)  =  1851859065;; integer values
0004: $CTEXT(8)  =  1634082916;; integer values
0004: $CTEXT(9)  =  1998615667;; integer values
0004: $CTEXT(10)  =  1701603688;; integer values
0004: $CTEXT(11)  =  1852795936;; integer values
0004: $CTEXT(12)  =  1852776551;; integer values
0004: $CTEXT(13)  =  1635000421;; integer values
0004: $CTEXT(14)  =  544433515;; integer values
0004: $CTEXT(15)  =  1701998445;; integer values
0004: $CTEXT(16)  =  1835627552;; integer values
0004: $CTEXT(17)  =  1869881445;; integer values
0004: $CTEXT(18)  =  1634692128;; integer values
0004: $CTEXT(19)  =  11876;; integer values
0004: $COUNT  =  20;; integer values

:CTDATA_M07
004E: end_thread


i hope the code is clear enough to get the idea.

@pdescobar: mate thanks for that address, i will check it thru. it might be it if i can get into this memory range!
edit: yes that was it, changed few bits in it and works but unfortunatelly i cannot get anywhere else except <0x00B78E20 thru 0x00BB8E1C> have you find any mathod to get outside it?
what a shame we can not access memory range to put piece of code in it.
EDIT: there was a mistake in your nick name man, sorry for that.

Demarest
  • Demarest

    what could be

  • BUSTED!
  • Joined: 12 Jul 2003

#11

Posted 21 September 2006 - 05:26 PM

@PLPynton: PLEASE don't take offense at this. Just as you stated, minimal footprint is key and the following is intended to be maximum compression. It ONLY adds 80 bytes to MAIN (not including the create_thread)

CODE
:TESTER
0004: $CTMESSAGE  =  18

:TESTER2
0001: wait  127 ms
00D6: if  1
00E1:   key_pressed  0 $CTMESSAGE
0038:   $DISPLAY ==  0
004D: jump_if_false ££TESTER3
0417: start_mission  135

:TESTER3
0008: $CTMESSAGE +=  1
00D6: if  0
00E1:   $CTMESSAGE >  19
004D: jump_if_false ££TESTER2
0004: $CTMESSAGE  =  18
0002: jump ££TESTER2


...with the following as the mission

CODE
;-------------Mission 135---------------
; Custom text database

:CTDATA_1
03A4: name_thread 'CTDATA'
0004: $DISPLAY  =  1
0006: @4 =  50784
0006: @5  =  75
00D6: if  0
0038:   $CTMESSAGE ==  18
004D: jump_if_false £CTDATA_M01
0006: @34  =  1919903827
0006: @35  =  1702109300
0006: @36  =  1663071352
0006: @37  =  1646292577
0006: @38  =  1869357157
0006: @39  =  1684366433
0006: @40  =  1935762720
0006: @41  =  1851859065
0006: @42  =  1634082916
0006: @43  =  1998615667
0006: @44  =  1701603688
0006: @45  =  1852795936
0006: @46  =  1852776551
0006: @47  =  1635000421
0006: @48  =  544433515
0006: @49  =  1701998445
0006: @50  =  1835627552
0006: @51  =  1869881445
0006: @52  =  1634692128
0006: @53  =  11876
0006: @6  =  20
0002: jump £CTDATA_M07

:CTDATA_M01
0006: @34  =  1801680198
0006: @35  =  779381024
0006: @6  =  2

:CTDATA_M07
0050: gosub £CTEXTCLEARALL
0085: @2 = @4

:CTEXT_1
00D6: if  0
803B:   NOT   @5 == @1
004D: jump_if_false £CTEXT_2
000A: @1 +=  1
000A: @2 +=  1
0002: jump £CTEXT_1

:CTEXT_2
0001: wait  0 ms
0652: get_stat @4 store_to @3
0062: @3 -= @34(@0,95i)
00D6: if  0
001B:    0 > @3
004D: jump_if_false £CTEXT_5
0012: @3 *=  -1

:CTEXT_3
0623: increase_stat @4 by  32767
00D6: if  0
0019:   @3 >  32767
004D: jump_if_false £CTEXT_8
000E: @3 -=  32767
0002: jump £CTEXT_3

:CTEXT_5
0625: decrease_stat @4 by  @3
00D6: if  0
0019:   @3 >  32767
004D: jump_if_false £CTEXT_8
000E: @3 -=  32767
0002: jump £CTEXT_5

:CTEXT_8
000A: @4 +=  1
000A: @0 +=  1
00D6: if  0
003B:    @6 == @0
004D: jump_if_false £CTEXT_2
0001: wait  5000 ms
0050: gosub £CTEXTCLEARALL
0004: $DISPLAY  =  0
004E: end_thread

:CTEXTCLEARALL
03E6: remove_text_box
03EB: clear_small_messages_only
00BE: print_now_clear_all
0051: return


No offense intended. I just really enjoy compressing existing code. The above only requires 2 globals as well. One key fix is that you checked if var >= 32767 when I'm almost certain you want if var > 32767. VERY awesome development!

PLPynton
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#12

Posted 21 September 2006 - 06:24 PM Edited by PLPynton, 21 September 2006 - 06:28 PM.

no offence taken, i have had no time to write footprint while i have had it ready for something else.
nice, well compressed.
but my idea was to create thread that runs all mechanics so you can access it from any part of script.

but i like it, not perfect solution but i will never ever use gxt files again. as i can create text directly for each displayed and print it onscreen at any time.

i wonder if there is more pearls like that inside this chunk, 256kb i pretty nothing but...

Demarest
  • Demarest

    what could be

  • BUSTED!
  • Joined: 12 Jul 2003

#13

Posted 21 September 2006 - 10:00 PM

Sorry. With the advancements I've made with savegame compatibility, DMA, reduced globals, etc are always a goal of mine even when I don't mean to. I'm sure the MAIN thread there could be expanded upon easily enough.

Hey, can you give us a step by step as to how this works exactly? How you arrived at the numbers you chose, how to spark it showing up in game, etc. I must confess I'm a little curious and can't readily tell by looking at it. blush.gif

PLPynton
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#14

Posted 22 September 2006 - 07:43 AM

perticular memory address leads to corner text box. whenever change detected the hame starts to display a text box for some period of time dependent of lenghtof the text.
i will fly over my code birefly:
$MESSAGE specify a variation of text used to display
$TIME custom time in ms after which text box will dissapear
$STAT number for stat where the perticular memory adress is located
$CTMAX (max lenght of memory chunk for perticular text) overload protection. absolutely necessary in my opinion because it forces you to give it in order to not to go above memory range (what causes game crash)
$COUNT since an array is used not in its full capacity you have to specify how many blocks are used to be filled with text
$CTEXT(@0,50i) here the text data is stored as 4 bytes long per each

idea was to store hundreds of coded messages in mission. then whenever you have necesity to put custom text instead of displayed by default or you like to bring your own message:
set message number, launch and forget about it the text appears. or you set message, launch mission THEN run opcode with default text and after few ms lunch thread to exchange original message data.

text is normal code! 4 bytes (signs) you have to count into 4bytes integer. its value you put into this array.

i understand you shortened my code and removed all of the protections but you can see that after that it is bearly capable of 33% original capabilities. but you are right the code of mine was far away of being optimized or whatsoever.


Demarest
  • Demarest

    what could be

  • BUSTED!
  • Joined: 12 Jul 2003

#15

Posted 22 September 2006 - 08:01 AM

The only way I limited the code was rendering the mission capable of 2 different messages. Since the code I was working with also only made provisions for 2 codes, it works the same. Even your offered code could not accomodate more than 2 since you only sniffed for 2 keypresses. Anybody capable of understanding this stuff will also know how to set ONE global var as per their own triggers and then launch the mission. As for overrun protection: necessary? Yes. Necessary in the code itself? No. If you publish "Hey guys, don't use messages longer than X in this code" then you've satisfied your requirement to let us know while allowing the code to be that much smaller.

I'm still not entirely clear how you arrived at the values though. As you know from my works with SCM-based mem-hacking and such, I'm familiar with converting 4 bytes to hex and using their int representation, etc... But I'm still not clear on what values you'd use for A, B, C, and so forth. Is it standard ASCII where 65 (I believe) is A and it counts up from there or what?

PLPynton
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#16

Posted 22 September 2006 - 09:07 AM

here is an example for codes coresponding to some letters

user posted image

word "FINE" as 4b int has a value of 1162758470

be aware of fact that:
-SA print function does not display some cases like 2 spaces one after another... (i believe unter space works fine for that)
-you have to clear containment of old memory cells is order to display different message properly

Demarest
  • Demarest

    what could be

  • BUSTED!
  • Joined: 12 Jul 2003

#17

Posted 22 September 2006 - 09:40 AM

Very good. I assumed it was that sort of ASCII map thing (though I had the value wrong). Still, it's important to publish so that others can make use of it. I think that image does a fine job of fulfilling that role smile.gif

pdescobar
  • pdescobar

    Conformist Scum Panda

  • Members
  • Joined: 19 Jul 2005

#18

Posted 22 September 2006 - 11:47 PM Edited by pdescobar, 23 September 2006 - 11:37 PM.

QUOTE (Demarest @ Sep 22 2006, 05:40)
Very good. I assumed it was that sort of ASCII map thing (though I had the value wrong).

Actually, you had the value spot on. On that screen cap, A is 0x41 which is 65 in decimal. wink.gif

I was playing with PLPynton's text routines a little bit and you can sorta type the text directly using the "long string variable" type. There are two problems with that, though. The first is that everything gets shifted to upper case; the second is that you seem to be forced to use underscores instead of spaces (at least Sanny complains about spaces), and underscores apparently don't word wrap. So this seems to be mainly useful only for short messages. Examples:

Short text, works pretty good aside from capitalization.
CODE
:CTDATA_M01
0004: $STAT =  50784// integer values
0004: $TIME  =  5000// integer values
0004: $CTMAX  =  75// integer values
// Long strings are 16 bytes, so size goes from 75 to 18 (with 3 leftover bytes)
// Count is ( (total characters +1) / 4 )
0@ = 0
06D1: $CTEXT(0@,18v) = "Hello_World."
0004: $COUNT  =  4// integer values
0002: jump @CTDATA_M07

user posted image

Long text; Houston, we have a problem
CODE
:CTDATA_M00
0004: $STAT =  50784// integer values
0004: $TIME  =  5000// integer values
0004: $CTMAX  =  75// integer values
0@ = 0
06D1: $CTEXT(0@,18v)  =  "This_is_a_test_o"
0@ = 1
06D1: $CTEXT(0@,18v)  =  "f_the_emergency_"
0@ = 2
06D1: $CTEXT(0@,18v)  =  "broadcast_system"
0@ = 3
06D1: $CTEXT(0@,18v)  =  "._This_is_only_a"
0@ = 4
06D1: $CTEXT(0@,18v)  =  "_test."
0004: $COUNT  =  18// (total characters + 1) / 4
0002: jump @CTDATA_M07

user posted image

======================
QUOTE (PLPynton)
@pedescobar: mate thanks for that address, i will check it thru. it might be it if i can get into this memory range!
edit: yes that was it, changed few bits in it and works but unfortunatelly i cannot get anywhere else except <0x00B78E20 thru 0x00BB8E1C> have you find any mathod to get outside it?
what a shame we can not access memory range to put piece of code in it.

The range of useful addresses I've found is similar to yours, although I still don't understand where negative stat IDs point. I don't think we'll be getting outside of that range via the stat opcodes, but Xieon's patch looks like the Holy Grail of SCM memory-hacking anyhow wink.gif

These are the memory locations I've been able to get to through the stats:
CODE
Offset-H  Offset-D  Stat ID
========  ========  ========
0xB78F68  12029800  stat 82  (always read as 4b INT)
  ...       ...       thru
0xB78FFC  12029948  stat 119 (always read as 4b INT)
0xB79000  12029952  stat 120 (properly-handled INTs)
  ...       ...       thru
0xB79378  12030840  stat 342 (properly-handled INTs)
0xB7937C  12030844  stat 343 (always read as 4b INT)
0xB79380  12030848  stat 344 (always read as 4b INT) or 0  (properly-handled floats)
  ...       ...       thru
0xB794C4  12031172  stat 425 (always read as 4b INT) or 81 (properly-handled floats)
0xB794C8  12031176  stat 426 (always read as 4b INT)
  ...       ...       thru
0xBB8E1C  12291612  stat 65535 (always read as 4b INT)


EDIT: I'm now confident that negative stat IDs don't point anywhere new.
stat -1 is the same as stat 65535,
stat -2 is the same as stat 65534,
and it continues on from there with stat -X being equivalent to stat 65536-X

So, the full useful range of stat id numbers appears to be 0-65535 with stats 82-65535 spanning the entire memory range of 0xB78F68 - 0xBB8E1F and stats 0-81 overlapping a small part of that range but providing true float access to that part.

Demarest
  • Demarest

    what could be

  • BUSTED!
  • Joined: 12 Jul 2003

#19

Posted 23 September 2006 - 12:18 AM

QUOTE (pdescobar @ Sep 22 2006, 19:47)
Xieon's patch looks like the Holy Grail of SCM memory-hacking anyhow wink.gif

I respectfully and humbly disagree. His work is AMAZING and the type selector is tits and then some.

However, I feel (and Y_Less agrees) that a purely SCM-based solution is prefered. This thread here opens up X% of it. Y_Less has published a tentative method, etc. Not everybody's going to be willing to patch their EXE.

Awesome advancement though, sir. Is there any known limit to verlenstr's? I know that when stored in vars, they're limited to 16, but original code has shown me at least one example of 17 chars. Also, have you tested spaces in SAMB?

pdescobar
  • pdescobar

    Conformist Scum Panda

  • Members
  • Joined: 19 Jul 2005

#20

Posted 23 September 2006 - 01:00 AM

QUOTE (Demarest @ Sep 22 2006, 20:18)
QUOTE (pdescobar @ Sep 22 2006, 19:47)
Xieon's patch looks like the Holy Grail of SCM memory-hacking anyhow wink.gif

I respectfully and humbly disagree. His work is AMAZING and the type selector is tits and then some.

However, I feel (and Y_Less agrees) that a purely SCM-based solution is prefered. This thread here opens up X% of it. Y_Less has published a tentative method, etc. Not everybody's going to be willing to patch their EXE.

Okay, so you caught me in some hyperbole. A purely SCM-based solution is obviously preferable, but those aren't too far advanced currently. Xieon's patch isn't really the Holy Grail since it does require modifying the exe, but for now it is the most simple and flexible method for SCM-based memory-hacking in SA and it reaches pretty well everywhere with its support for virtualprotect. So please pardon my overenthusiasm. wink.gif

QUOTE (continued...)
Awesome advancement though, sir. Is there any known limit to verlenstr's? I know that when stored in vars, they're limited to 16, but original code has shown me at least one example of 17 chars. Also, have you tested spaces in SAMB?

Haven't tested in SAMB since I got sidetracked with my radio project. Regarding length, I really don't know, but I do find it interesting that you can assign the a full 16-byte string to a variable rather than having to limit to 15 with a null-terminator. (assuming they're stored as c-style strings)

Demarest
  • Demarest

    what could be

  • BUSTED!
  • Joined: 12 Jul 2003

#21

Posted 23 September 2006 - 01:27 AM

Well until we (read: me; I'm all wet when it comes to most forms of mem-hacking) get a list of supported EXE's, I'm not sure you can count on my vote in the simplicity column. If that one patch works on all common EXE's, okay.

I'm not sure how it works exactly. I know that in all my efforts (compacting of MAIN threads for TTSA), nothing crashed the game. Storing a 17 byte varlenstr and then reading it, only provided the 16 bytes. Then when I did in fact store the full 16 (and therefore not the terminator), it read all 16 and was able to manipulate them accordingly. Maybe it's because I was using vars. Try it raw (by plucking the terminator with a hex editor) and I'm sure you'd see problems. The ONLY reason that bothers me is because unless you're using a varlenstr array, there's no need to have a finite cap of 16 bytes. And the part that REALLY mystifies me is that if the engine is going to assume 16 bytes on retrieval, then why did they bother naming animations and such with 17+ bytes? *scratches head*

What would REALLY be keen is if we could figure out a way to tie the debug functions into this. Every time I think varlenstr's, I drool over the degub commands that SEEM to be built in free texters.

pdescobar
  • pdescobar

    Conformist Scum Panda

  • Members
  • Joined: 19 Jul 2005

#22

Posted 23 September 2006 - 07:54 AM

I did some more testing on using long string variables. SAMB also refuses to compile with spaces in the string, but if I hex-edit the compiled SCM and turn the underscores into spaces, it works properly in-game and line-wraps inside the box. And the reason the text displays as uppercase is because the compilers (both of them) make that adjustment.

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia

#23

Posted 23 September 2006 - 08:30 AM Edited by Seemann, 23 September 2006 - 08:34 AM.

QUOTE (pdescobar @ Sep 23 2006, 07:54)
I did some more testing on using long string variables. SAMB also refuses to compile with spaces in the string, but if I hex-edit the compiled SCM and turn the underscores into spaces, it works properly in-game and line-wraps inside the box. And the reason the text displays as uppercase is because the compilers (both of them) make that adjustment.

I can remove both of the limits (capitalization and spaces inaccessibility).

So, please, test if the game is able to support some special bytes within long strings, like #9 (TAB), #13#10 (carriage return), and some else. If they are supported, I can make the compiler compile the line with such chars (with c-like method: using \n instead of #13#10 as carriage return, for example).

So, it will allow to use the strings like
CODE
"Line 1 text \n Line 2 text"
which will be displayed as two-lined message.

Not sure if the game is able to parse them, but if so I think it would be very useful.

pdescobar
  • pdescobar

    Conformist Scum Panda

  • Members
  • Joined: 19 Jul 2005

#24

Posted 24 September 2006 - 12:50 AM

QUOTE (Seemann @ Sep 23 2006, 04:30)
QUOTE (pdescobar @ Sep 23 2006, 07:54)
I did some more testing on using long string variables. SAMB also refuses to compile with spaces in the string, but if I hex-edit the compiled SCM and turn the underscores into spaces, it works properly in-game and line-wraps inside the box. And the reason the text displays as uppercase is because the compilers (both of them) make that adjustment.

I can remove both of the limits (capitalization and spaces inaccessibility).

That would be excellent. icon14.gif

QUOTE (continued...)
So, please, test if the game is able to support some special bytes within long strings, like #9 (TAB), #13#10 (carriage return), and some else. If they are supported, I can make the compiler compile the line with such chars (with c-like method: using \n instead of #13#10 as carriage return, for example).

So, it will allow to use the strings like
CODE
"Line 1 text \n Line 2 text"
which will be displayed as two-lined message.

Not sure if the game is able to parse them, but if so I think it would be very useful.

All of those special characters (TAB, CR, and LF) are basically interpreted the same as underscores -- they wind up being single non-breaking spaces. However, the game already has a provision for embedding newlines via "~n~". Off-hand, the only special character I think you'd need to support is a way to escape single and double quotes. For example, making it so that
CODE
"Test\"Test\'Test"

gets compiled as a single long string containing
CODE
Test"Test'Test

Embedded quotes like that do display properly if hex-edited in, although I didn't test that one until after I took the below screenshot of the other tests.

user posted image

====================

Also, I'm now pretty sure that negative stat IDs don't point anywhere new; I edited a note into my previous post which discussed the memory range and will probably update the first post to more accurately reflect our new knowledge later on.

Seemann
  • Seemann

    Ruhe

  • Members
  • Joined: 03 Sep 2004
  • Russia

#25

Posted 24 September 2006 - 02:56 AM Edited by Seemann, 24 September 2006 - 08:00 AM.

QUOTE (pdescobar @ Sep 24 2006, 00:50)
Off-hand, the only special character I think you'd need to support is a way to escape single and double quotes.

Now, SB 2.98 supports the single quotes within "", and the double quotes within ''
(for example: "'single'", '"double"')

but OK, I'll try to implement an escape char. (EDIT: Done.)


Edit:

A litte question too.

Does the game support both of the variants ~n~ and ~N~, or ~n~ only?
I just want to implement \n constant, which will be replaced with ~n~ (I think \n is more understable to use).

So, the line "line 1 \n line 2" will be compiled as "line 1 ~n~ line 2".

Also, I've implemented \s constant which is extra space within the string.
So, the line "Sanny \s\s\s\ Builder" have to be displayed as
CODE
"Sanny † † Builder" †// 5 spaces
in-game.

Single space between the words is also supported.

PLPynton
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#26

Posted 25 September 2006 - 04:00 AM Edited by PLPynton, 08 October 2006 - 07:16 PM.

stat 48694-50453 is a table (known as well as pool) for all possible radar makers.
174 markers as game maximum, 10 "stat numbers" per one marker.
marker 1 breakdown (i will not attempt to breake it down completely)
Stat 48694 MAPMARKER001-COORD X [float]
Stat 48695 MAPMARKER001-COORD Y [float]
Stat 48696 MAPMARKER001-COORD Z [float]
Stat 48697 MAPMARKER001-COUNTER
Stat 48698 MAPMARKER001-RADIUS (POI cylinder) [float]
Stat 48699 MAPMARKER001-unknown
Stat 48700 MAPMARKER001-unknown
Stat 48701 MAPMARKER001-texture,radar,visibility,unused= corresponds bytes 0-3
Stat 48702 MAPMARKER001-unknown 1b bitmask
Stat 48703 MAPMARKER001-unknown 2b bitmask

actually it does not add nothing special into mission coding except that you can read/write all the parameters. for someone with good imagination it can be really useful

EDIT: i have found something much more useful for mission coding. objects parameters definitions table!
it is located in range 61212 through 64351. 156 object entries, 20 "stat numbers" per each.
example object [bouy] breakdown (complette)
Stat 62032 OBJECT -BOUY MASS
Stat 62033 OBJECT -BOUY TURNMASS
Stat 62034 OBJECT -BOUY AIR RESISTANCE
Stat 62035 OBJECT -BOUY ELASTICITY
Stat 62036 OBJECT -BOUY PERCENTAGE SUBMERGED
Stat 62037 OBJECT -BOUY UPROOT LIMIT
Stat 62038 OBJECT -BOUY CDMULTIPLIER
Stat 62039 OBJECT -BOUY CDEFFECT,SPCDRESPONSEC,CAMAVOID,CEXPLOSION
Stat 62040 OBJECT -BOUY FX_TYPE,Unused,Unused,Unused
Stat 62041 OBJECT -BOUY FX_OFFSET X
Stat 62042 OBJECT -BOUY FX_OFFSET Y
Stat 62043 OBJECT -BOUY FX_OFFSET Z
Stat 62044 OBJECT -BOUY FX_NAME (internal identifier)
Stat 62045 OBJECT -BOUY SMASH MULTIPLIER
Stat 62046 OBJECT -BOUY BREAK VELOCITY X
Stat 62047 OBJECT -BOUY BREAK VELOCITY Y
Stat 62048 OBJECT -BOUY BREAK VELOCITY Z
Stat 62049 OBJECT -BOUY BREAK VELOCITY RANDOMNESS
Stat 62050 OBJECT -BOUY GUN BREAK
Stat 62051 OBJECT -BOUY PRODUCE SPARKS

i believe that i do not have to comment how deamn great is to have access to object parameters alright.

i someone will bother to mess with vehicle table (as i wanted to do for Tomworld and Picolini) you have to chose Xien's memory patch for exe with DMA opcodes 'cause the table is located at memory cell C2B9E0h (56x4b per entry).

PLPynton
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#27

Posted 07 October 2006 - 05:03 PM

as ordered:

Stat 04080 HUD-BREATHMETER- current value

by the maens of original script conditions it is 4byte float with max valueof 1150.0 so you have to get it increased to prevent breathmeter to drop dramatically. max value is stored at some offset much higher than stats can get but is protected after all because is computed.

pdescobar
  • pdescobar

    Conformist Scum Panda

  • Members
  • Joined: 19 Jul 2005

#28

Posted 07 October 2006 - 05:45 PM Edited by pdescobar, 08 October 2006 - 07:00 PM.

To get an approximation of the current breath meter maximum, you can take the base 1150.0 and add 1.5 * stat_225 (lung capacity). So, with a lung capacity of 200, the breath meter maxes at approx 1450.0 and with a lung capacity of 1000, the breath meter maxes at approx 2650.0. There's some roundoff error though so that value is around +/- 5.0 from the true maximum.

EDIT: Some sample code; quick & dirty just to illustrate accessing the values.
CODE
:BreathMeter
0652: 1@ = stat 4080 // Current Breath Meter Value (FLOAT even though INT opcode used)
0653: 2@ = stat 225 // Lung Capacity  (FLOAT)
2@ *= 1.5
2@ += 1150.0  // Now 2@ is approximate Breath Meter max

// Simple Text Display:
0092: 1@ = float_to_integer 1@
0092: 2@ = float_to_integer 2@
02FD: text_2numbers_lowpriority 'TIME' 1@ 2@ 100 ms 1  // ~1~:~1~
wait 100
jump @BreathMeter

PLPynton
  • PLPynton

    Player Hater

  • Members
  • Joined: 09 Jul 2005

#29

Posted 08 October 2006 - 04:15 PM Edited by PLPynton, 08 October 2006 - 04:22 PM.

stat 00082 STAT_INC_SNIPERRIFLE_SKILL (ar_stats.dat)
stat 00083 STAT_DEC_FAT (ar_stats.dat)
stat 00084 STAT_DEC_BODY_MUSCLE (ar_stats.dat)
stat 00085 STAT_DEC_MAX_HEALTH (ar_stats.dat)
stat 00086 STAT_EXERCISE_RATE_CYCLE (ar_stats.dat)
stat 00087 STAT_EXERCISE_RATE_CYCLE_SPRINT (ar_stats.dat)
stat 00088 STAT_EXERCISE_RATE_SWIM (ar_stats.dat)
stat 00089 STAT_EXERCISE_RATE_SWIM_SPRINT (ar_stats.dat)
stat 00090 STAT_EXERCISE_RATE_SPRINT (ar_stats.dat)
stat 00091 STAT_EXERCISE_RATE_RUN (ar_stats.dat)
stat 00092 STAT_EXERCISE_RATE_FIGHT (ar_stats.dat)
stat 00093 STAT_TIMELIMIT_CYCLE_STAMINA (ar_stats.dat)
stat 00094 STAT_TIMELIMIT_SWIM_STAMINA (ar_stats.dat)
stat 00095 STAT_TIMELIMIT_SPRINT_STAMINA (ar_stats.dat)
stat 00096 STAT_TIMELIMIT_RUNNING (ar_stats.dat)
stat 00097 STAT_TIMELIMIT_DRIVING_SKILL (ar_stats.dat)
stat 00098 STAT_TIMELIMIT_FLYING_SKILL (ar_stats.dat)
stat 00099 STAT_TIMELIMIT_CYCLE_SKILL (ar_stats.dat)
stat 00100 STAT_TIMELIMIT_MOTORBIKE_SKILL (ar_stats.dat)
stat 00101 STAT_TIMELIMIT_BOAT_SKILL (ar_stats.dat)
stat 00102 STAT_TIMELIMIT_FAT_ADJUST (ar_stats.dat)
stat 00103 STAT_TIMELIMIT_FAT_ADJUST_STRENUOUS (ar_stats.dat)
stat 00104 STAT_TIMELIMIT_BREATH_UNDERWATER (ar_stats.dat)
stat 00105 STAT_TIMELIMIT_MAX_HEALTH (ar_stats.dat)
stat 00106 STAT_TIMELIMIT_PISTOL_SKILL (ar_stats.dat)
stat 00107 STAT_TIMELIMIT_PISTOL_SILENCED_SKILL (ar_stats.dat)
stat 00108 STAT_TIMELIMIT_DESERT_EAGLE_SKILL (ar_stats.dat)
stat 00109 STAT_TIMELIMIT_SHOTGUN_SKILL (ar_stats.dat)
stat 00110 STAT_TIMELIMIT_SAWNOFF_SHOTGUN_SKILL (ar_stats.dat)
stat 00111 STAT_TIMELIMIT_SPAS12_SHOTGUN_SKILL (ar_stats.dat)
stat 00112 STAT_TIMELIMIT_MICRO_UZI_SKILL (ar_stats.dat)
stat 00113 STAT_TIMELIMIT_MP5_SKILL (ar_stats.dat)
stat 00114 STAT_TIMELIMIT_AK47_SKILL (ar_stats.dat)
stat 00115 STAT_TIMELIMIT_M4_SKILL (ar_stats.dat)
stat 00116 STAT_TIMELIMIT_SNIPERRIFLE_SKILL (ar_stats.dat)
stat 00117 STAT_TIMELIMIT_DEATH_HEALTH (ar_stats.dat)
stat 00118 STAT_TIMELIMIT_ADD_TO_HEALTH (ar_stats.dat)

it could have been posted before, in this case that will be supreme confirmation.

-/TNT\-
  • -/TNT\-

    not like any other rainbow.

  • Members
  • Joined: 06 Aug 2006

#30

Posted 08 October 2006 - 04:55 PM

Although I am a total novice when I come to these matters, I vaguely understand what you are doing. Do your duty and scm coding will go to the new dimension!

inlove.gif cookie.gif cookie.gif cookie.gif




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users