|
本帖最后由 Sidlady 于 2012-3-22 21:10 编辑
I've decided to create a new topic to collect all examples how to handle GTASA memory via SCM.
If you don't understand what is written there, maybe it's not yours. I'll try to explain more things later by editing this post.
All codes are written in Sanny Builder v2.99. To get it working, you should download the latest version of SB.
http://www.gtaforums.com/index.php?showtopic=211077
All codes are tested in San Andreas v1.0 US. Memory addresses could be different in other versions. If something is not working, be sure you use the version I said.
1.
Now, we have three ways to handle the game memory.
1. Initial memory handling way was published in this topic
Stat opcodes provide limited memory access to locations near the stat pools. It allows to make things like changing the player's money, for example.
Advantages:
- the only opcode is using
Disadvantages
- memory range is very limited, many useful addresses are inaccesible.
2. Second way: using the Xieon's patch, that changes three opcodes in gta-sa.exe and provides extremely wide possibilities for game memory handling.
Download
Advantages:
- all game addresses are accessible
- possibility to protect a memory region with VirtualProtect to rewrite it
Disadvantages
- requires exe patching; may not work with different versions (but in fact, I did not see any messages that the patch is working incorrectly).
3. Third way: using the SA arrays to get an access to any addresses in range of 0..FFFFFFFF. Initially was posted there.
Advantages:
- all game addresses are accessible
- easy to use
- nothing especial required; scm-based solution
Disadvantages
- some of addresses still coudn't be rewritable (because of AccessViolation Error).
2.
For the last way, there are three routines to read/write values to the specified address:
(briefly, so far).
Memory Handling Routines (san andreas)
1 MemoryWrite: write new value with specified length into the memory
Params: 0@ = address; 2@ = new value; 3@ - value length (1, 2, 3, 4)
2 MemoryWrite_DWORD: write new DWord value into the memory
Params: 0@ = address; 1@ = new value;
3 MemoryRead: read DWord value from the memory
Params: 0@ = address; 1@ = returned value;
note that some address could be unreadable/unrewriteable!!!
to change such addresses try Xieon's MemPatch: ..\tools\Sa Memory Patch\- //--write specified number of bytes into memory
- :MemoryWrite
- 0085: 5@ = 0@
- 0@ /= 4
- 0@ *= 4 // memory address
- 0062: 5@ -= 0@ // offset (0, 1, 2, 3)
- :_GetInitValue // if you specify mem offset in 5@, you're able to gosub here
- gosub @MemoryRead // get initial value
- 3@ *= 8 // bytes -> bits
- 5@ *= 8
- dec(3@)
- for 6@ = 0 to 3@
- if
- 08B6: test 2@ bit 6@
- then
- 08BF: set 1@ bit 5@ // 1
- else
- 08C5: clear 1@ bit 5@ // 0
- end
- inc(5@) // next memory bit
- end
- 008A: &0(0@,1i) = 1@ // write new value
- return
- //--write 32-bit value into memory-----------
- :MemoryWrite32bit
- 0@ -= 0xA49960
- 0@ /= 4
- 008A: &0(0@,1i) = 1@
- return
- //--read 32-bit value from memory-----------
- :MemoryRead
- 0@ -= 0xA49960
- 0@ /= 4
- 008B: 1@ = &0(0@,1i)
- return
复制代码
3.
I wrote some examples using these routines. Most of examples were published at gtaf, some of them was out of there.
Now, they are:
-------------------------------------------------
EXAMPLE 1.
Make extremely long trains, 15+ carriages!
Original (in Russian)
Screenshot
-------------------------------------------------- :LONGTRAINS
- thread 'TRAINS'
- for 0@ = -382229 to -382216
- wait 0
- &0(0@,1i) = #STREAKC
- end
- // type0 changed!
- // load models
- #FREIGHT.Load
- #FREIFLAT.Load
- #STREAKC.Load
- while true
- if and
- Model.Available(#FREIGHT)
- Model.Available(#FREIFLAT)
- Model.Available(#STREAKC)
- then
- Break
- end
- wait 0
- end
- // create train with new carriages
- 06D8: 1@ = create_train_at 2278.1771 -1144.8823 27.5108 type 0 direction 1
- #FREIGHT.Destroy
- #FREIFLAT.Destroy
- #STREAKC.Destroy
- end_thread
复制代码
-------------------------------------------------
EXAMPLE 2.
New cheats in run-time
Original
-------------------------------------------------- :NEWCHEATS
- // EXAMPLE 1: TEST 1 key press (space)
- thread 'CHEATS'
- 0@ = -229908
- while true
- 008B: 1@ = &0(0@,1i) // get last keypresses
- 0085: 2@ = 1@
- div(1@, 0x 1 00) // 1char: 0x100, 2chars: 0x10000: 3chars: 0x1000000
- mul(1@, 0x 1 00) // same
- 0062: 2@ -= 1@ // get needed number of chars (1)
- if
- 2@ == 0x20 // test if it's SPACE
- then
- 03E5: text_box 'CHEAT1'// Cheat activated
- Break
- end
- wait 1000
- end
- // EXAMPLE 2: TEST 2 keys ('NO')
- 0@ = -229908
- while true
- 008B: 1@ = &0(0@,1i) // get last keypresses
- 0085: 2@ = 1@
- div(1@, 0x 1 00 00) // 1char: 0x100, 2chars: 0x10000: 3chars: 0x1000000
- mul(1@, 0x 1 00 00) // same
- 0062: 2@ -= 1@ // get needed number of chars (2)
- if
- 2@ == 0x4e4f // test if player typed NO
- then
- 03E5: text_box 'CHEAT1' // Cheat activated
- Break
- end
- wait 1000
- end
- // EXAMPLE 3: TEST 3 keys ('WOW')
- 0@ = -229908
- while true
- 008B: 1@ = &0(0@,1i) // get last keypresses
- 0085: 2@ = 1@
- div(1@, 0x 1 00 00 00) // 1char: 0x100, 2chars: 0x10000: 3chars: 0x1000000
- mul(1@, 0x 1 00 00 00) // same
- 0062: 2@ -= 1@ // get needed number of chars (3)
- if
- 2@ == 0x574f57 // test if player typed WOW
- then
- 03E5: text_box 'CHEAT1'// Cheat activated
- Break
- end
- wait 1000
- end
- // EXAMPLE 4: TEST 4 keys ('HACK')
- 0@ = -229908
- while true
- if
- &0(0@,1i) == 0x4841434B // test if player typed HACK
- then
- 03E5: text_box 'CHEAT1' // Cheat activated
- Break
- end
- wait 1000
- end
- // EXAMPLE 5: TEST 5 keys ('SANNY')
- // test 5th char 's' from address +4b, then test 'anny' from the beginning;
- // addr keys
- // -229908: X X X X | -229908: A N N Y
- // -229907: X X X O <- | -229907: _ _ _ S
- while true
- 0@ = -229907 // +32bits; next 4 bytes/chars in the last keypresses block
- 008B: 1@ = &0(0@,1i)
- 0085: 2@ = 1@
- div(1@, 0x100)
- mul(1@, 0x100)
- 0062: 2@ -= 1@ // 5th char is the last in the second block:
- if
- 2@ == 0x53 // test if S is the 5th pressed key
- then
- 0@ = -229908
- 008B: 3@ = &0(0@,1i)
- if 3@ == 0x414E4E59 // test if player also typed ANNY after that
- then
- 03E5: text_box 'CHEAT1' // Cheat activated
- Break
- end
- end
- wait 1000
- end
- // EXAMPLE 6: TEST 16 keys: '1234567812345678'
- 0@ = -57477
- while true
- if
- &0(0@,1v) == "8765432187654321" // test if last pressed key combo is "1234567812345678"
- then
- 03E5: text_box 'CHEAT1' // Cheat activated
- Break
- end
- wait 1000
- end
- end_thread
复制代码
-------------------------------------------------
EXAMPLE 3.
Changing of the local variables of any thread
Original (in Russian)
-------------------------------------------------- // ---------------------------------------------
- // This thread searches the one named TEST
- // and changes its local variable 10@
- // ---------------------------------------------
- :CHANGELOCALVAR
- thread 'CLV'
- {
- 0@ = thread address
- 1@ = temp
- }
- 0@ = 0xA8B42C
- // FIND_THREAD_LOOP
- while true
- gosub @MemoryRead
- // 1@ = first active thread
- if 1@ == 0
- then
- Break // no threads, search failed
- end
- 0085: 0@ = 1@ // save the pointer
- // get thread name magic address
- div(1@, 8)
- dec(1@, 1 348 395)
- {
- IMPORTANT NOTE:
- thread names are stored in lowercase registry,
- so we've to compare it in lowercase as well.
- SB option 'Case Converting' has to be set to 'As is'!
- }
- if &0(1@,1s) == 'test'
- // check if this thread's name is "test"
- then
- // well, we've found it, can do everything with it
- // 0@ contains that thread address
- // get address of 10@
- inc(0@, 0x3c) // thread locals pool
- inc(0@, 40) // local var name * 4; i.e. set to 36 to change 9@
- // MEMORY WRITE DWORD
- 1@ = 3333 // new value of thread('test').10@
- gosub @MemoryWrite32bit
- Break // end the LOOP
- else
- // no, that thread has another name
- // check the next one
- wait 0
- end
- // go to while_begin
- end
- // variable changed, end_thread
- end_thread
- // ---------------------------------------------
- // This thread shows a number after 1 sec
- // after activated. The number is stored in 10@
- // ---------------------------------------------
- :TEST1
- thread 'TEST'
- 10@ = 10000
- wait 3000
- 054C: use_GXT_table 'POOL'
- 01E3: text_1number_styled 'NUM' 10@ 5000 ms 1 // ~1~
- end_thread
复制代码
optimized version of CLV thread (see above):- :CLV
- 03A4: name_thread 'CLV'
- 0006: 0@ = 67251
- :CLV_LOOP
- 008B: 0@ = &0(0@,1i)
- 00D6: if
- 8039: not 0@ == 0
- 004D: jump_if_false @CLV_END
- 0001: wait 0 ms
- 0085: 1@ = 0@
- 0016: 1@ /= 8
- 000E: 1@ -= 1348395
- 0016: 0@ /= 4
- 000E: 0@ -= 2696792
- 00D6: if
- 05AD: &0(1@,1s) == 'test'
- 004D: jump_if_false @CLV_LOOP
- // 25 is the local number + 15
- // i.e. set to 24 to change 9@
- 000A: 0@ += 25
- // 3333 is a new value of the local
- 0004: &0(0@,1i) = 3333
- :CLV_END
- 004E: end_thread
复制代码
-------------------------------------------------
EXAMPLE 4.
Remove the message "To stop Carl..." when the player
first time stealing a car
Original
-------------------------------------------------- :MSGREMOVE
- thread 'NOMSG'
- 0@ = 0xC0BC15 // ADDRESS
- 2@ = 1 // VALUE
- 3@ = 1 // LENGTH (Byte)
- gosub @MemoryWrite
- end_thread
复制代码
4.
You can grab a main.scm with all examples from here:
http://www.mysharefile.com/v/7409206/memhandling.rar.html
- Readme included
- source included
changes history:
06 dec 2006:
- added link to the Xieon's patch
- added optimized version of CLV thread (example #3)
This post has been edited by Seemann on Wednesday, Dec 6 2006, 15:37
-------------------- |
|
|