メモリ関係

Last-modified: 2015-05-03 (日) 21:25:18

メモリ関係 †
少し専門的な知識が必要となるため、その部分も含めて説明します


2進数、10進数、16進数 †
10進数は0~9までの10種類の文字を使って数を表現します。
2進数は0と1
16進数では0~Fを用います

2進数 0 1 10 11 100 101 110 111 1000 1001 1010 1011 1100 1101 1110 1111 10000
10進数 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16進数 0 1 2 3 4 5 6 7 8 9 A B C D E F 10

コンピュータは全て2進数 電圧が高いか(1)低いか(0)を判断してデータをやりとりします。
2の乗数である16進数で計算する方が扱いやすい為、メモリアドレスは通常16進数で表記します。
16進数で表記する場合は、数値の前に"0x"を付けて表記します。


メモリ †

メモリはデータを記憶する主記憶装置です
プログラムやデータを記憶し、データ、命令をCPUへ送る重要な役割をします。
WindowsなどのOS、GTA:SA等、実行しているプログラムは全てこのメモリに入っています。
ここで出てくるメモリアドレスはGTA:SAの開始アドレスを基点とした相対アドレスです。
一つのメモリアドレス中には1バイトのデータが入っており、1バイトは通常8ビットです。
ビットとはコンピュータが扱う情報の最小単位で、0と1の2進数を使用します
8桁の2進数ですので00000000~11111111の256(2の8乗)通りあります。

このデータを直接読んだり、書き換えることでopcode命令では出来ない事をします。


構造体 †
構造体は複数のデータ型をまとめて管理する物です。 似たものに配列がありますが、こちらは一つのデータ型しか扱えません。
全てのPEDをまとめて管理するもの、その中のPEDごとを管理する構造体があります。
これらは全てメモリに書き込まれています。

0A96: $ActorStruct = actor $PLAYER_ACTOR struct
0A97: $CarStruct = car $MyCar struct
0A98: $ObjectStruct = object 0@ struct
CPed・CVehicle・CObjectのポインタ(メモリアドレス)を取得するためのコードです。

0A9F: 0@ = current_thread_pointer
0AAA: 0@ = thread 'OTB' pointer
0A9Fは実行中のスレッドのポインタ
0AAAは指定したスレッドのポインタです。

0A8D: 0@ = read_memory 0x863984 size 4 virtual_protect 0
0A8C: write_memory 0x863984 size 4 value 0.008 virtual_protect 1
メモリのアドレスと長さを指定し、そこに数値を書き込んだり読み出したりします。上は重力値です。

0x863984 … メモリアドレス ここでは重力値

size 4 … 何バイト書き込むか(1,2,4byte)

value 0.008 … 書き込む値。この場合は重力加速度 (1ms当たりの速度(m/s)の増加量) 8m/s^2

virtual_protect 1 … 仮想保護。他からのメモリ書き込みから保護するかしないか(1で有効)
メモリアドレスについては
http://www.gtamodding.com/index.php?title=Memory_Addresses_%28SA%29
を参照してください。
主にこれらを使用してメモリ値を読み書きします。


例 †
0A97: 0@ = car 1@ struct
000A: 0@ += 0x8C
0A8D: 2@ = read_memory 0@ size 4 virtual_protect 0
0017: 2@ /= 2.0
0A8C: write_memory 0@ size 4 value 2@ virtual_protect 0
乗り物1@の重量を半分にするコードです。
乗り物の重量を変えるopcodeは01EC: make_car 34@ very_heavy 1 しか存在せず、軽くすることは出来ません。
http://www.gtamodding.com/index.php?title=Memory_Addresses_%28SA%29
より
CVehicle+140 = [float] Mass (kg) from handling.cfg
140=0x8Cなので、アドレスに0x8Cを足し、2@に値を格納します。(10進数で書いても問題ありません。)
このときのサイズは

[byte] …1byte 整数
[float]…4byte 少数
[word] …2byte 整数 word = 文字は2byte
[dword]…4byte 整数 double word = 2文字分 2*2=4byte
となっています。
[float]より、0017を使用し 重量を半分に。
同アドレスに計算した値を書き込んでいます。

特殊なものとして耐性チェックのようにbit毎にフラグが定められているものもあります。
ここで[float]となっているCPed +0x42が1byteを指定しているのは、
CVehicle +0x42(+66) = [byte]となっている為です。
CPed +0x42のフラグも実は1byte整数型なので、他でも対応が可能なことから1byteとなっています。


構造体の見方 †
例として、レーシングチェックポイントの構造体を挙げます。

開始アドレス 0xC7F158
要素サイズ 0x38
要素数 0x20

  1. 0x00 [BYTE] タイプ
  2. 0x04 [DWORD] ハンドル
  3. 0x08 [DWORD] 色
  4. 0x10 [FLOAT] X座標
  5. 0x14 [FLOAT] Y座標
  6. 0x18 [FLOAT] Z座標
  7. 0x1C [FLOAT] チェックポイントの向きX
  8. 0x20 [FLOAT] チェックポイントの向きY
  9. 0x24 [FLOAT] チェックポイントの向きZ
  10. 0x2C [FLOAT] チェックポイントのサイズ
    開始アドレス … 構造体が始まるアドレス。
    要素サイズ …… 一つの要素全体のサイズ。
    要素数 ………… この構造体が同時に持つことが出来る要素の最大数。
                   この場合、同時に0x20=32個までのレーシングチェックポイントを持つことが出来る。

例えば、レーシングチェックポイントを作成しその色を変えたい場合
06D5: 22@ = create_racing_checkpoint_at 0.0 0.0 0.0 point_to 0.0 0.0 0.0 type 3 radius 2.0
for 31@ = 0xC7F15C to 0xC7F824 step 0x38

   0A8D: 29@ = read_memory 31@ size 4 virtual_protect 0
   if
   003B: (check) 29@ == 22@ // (int)
   then
       000A: 31@ += 0x4
       0A8C: write_memory 31@ size 4 value 0x8000FF00 virtual_protect 0
       break
   end

end
構造体から一つ一つのHandleを調べ、作ったチェックポイントと一致したら色情報を書き込むコードです。

指定する際の0xC7F824は
0xC7F15C(構造体先頭+0x04(Handle))+0x1F(要素数-1)*0x38(要素サイズ)から。
3行目の0A8Dで29@に構造体からHandleを取得。

そのHandleと自分が作成したHandleを比較。

一致した場合Handleのポインタである31@から4byte進めてColor情報が入っているところへポインタを移す。

色情報を書き込み、ループを抜ける。

と言った感じに構造体を上手く利用することによって、色々なことが出来るようになります。
ちなみに、チェックポイントの色は移動または作成と同時にメモリへ書き込まなければなりません