第17回  「専用改造ツールの作成(メモリ改造)」



フォーム内で共通の変数の解説

今回の講座を読み進める前に、前回の講座を読み直しておくことをお勧めします。
基本的な流れは、前回のセーブデータ改造とほぼ同じですので、
重複する内容については、省略してあります。

では、「KAIZOU_OreBusterM.vbp」をVBで開き、前回同様
変数宣言部分のプロシージャを見ておきましょう。


General_Declarations プロシージャ
'対象のプロセス名を格納するための変数
Dim ProcessName As String

'データ読み込み時に、アイテムを何種類持っていたか
Dim ItemNumber As Long

'各ステータスがメモリ上のどの場所に格納されているかを記述(外部ファイルより読み込み)
Dim StatusData() As String

'上記配列の上限値、添字
Dim StatusDataUpper As Long, StatusDataIndex As Long

'ゲーム中のパラメータが格納されているアドレスをセットする変数(StatusData 配列から取り出す)
Dim A_name As String, A_hp As String, A_max_hp As String
Dim A_str As String, A_def As String, A_luck As String, A_speed As String, A_gold As String
Dim A_i1 As String, A_i2 As String, A_i3 As String, A_i4 As String, A_i5 As String


今回はメモリを書き換えるので、対象となるプロセス名が必要となりますので、
これを「ProcessName」に格納しておきます。
また、「ItemNumber」については、前回と同じ働きをします。

前回と異なるのは、「StatusData()」「StatusDataUpper」「StatusDataIndex」
そして、下3行にまとめた変数群ですね。

これらを解説する前に、まず、今回の改造ツールのコンセプトを説明しておかねばなりません。
メモリの改造には第4回で説明したように、ゲームのバージョンが変わると、
そのパラメータが格納されているアドレスが変更される事が多い、というデメリットがありました。

そこで、プログラムに直接アドレスを記述するのではなく、
外部ファイルに、各パラメータがどのアドレスに記録されているかという情報を記録しておき、
メモリを書き換える前にこのファイルを読み込んで、対象となるアドレスを探し出してから
実際の書き換えを行う。という方法を使いました。

これならば、ゲームのバージョンが変わっても、
プログラムの変更は必要なく、外部ファイルを書き換えるだけで済みます。
この改造ツールでは、「ore_mem_data.txt」というファイルがそれにあたります。


それでは、変数の解説に戻りましょう。
「ore_mem_data.txt」ファイルを開いてみてください。
以下のような内容になっていますね。


ore_mem_data.txt の中身
NAME:419674
HP:41CDB8
MAX_HP:41CDBA
STR:41CDC0
DEF:41CDC4
LUCK:41CDC8
SPEED:41CDCA
GOLD:419672
ITEM1:4196AC
ITEM2:4196C1
ITEM3:4196D6
ITEM4:4196EB
ITEM5:419700


大体予測できると思いますが、「:」の左側がパラメータの名前、右側がアドレスです。
この13行分のデータを、一旦「StatusData()」配列に代入します。
そして、このアドレス部分を1行ずつ抜き出し、NAMEなら「A_name」変数にする事になります。
(実際の処理部分については後述)



データ読み込み処理

次に、読み込みボタンをクリックした時の処理を見てみましょう。
ただし、ボタンを使用不可にする処理や、
持っていないアイテムの個数を増やすのを避けるための処理については、
前回と全く変わっていないので省略しています。


cmdRead_Click プロシージャ1
Private Sub cmdRead_Click()

  ProcessName = "OREBUST.EXE"

  '対象のプロセスが開かれているかを調べる
  'アドレスを決め打ちしているので、あまり好ましくない処理である
  Dim ret As String
  ret = GetMemoryData(ProcessName, "419674", "1")
  If ret = "Process Open Error !!" Then
    MsgBox "対象となるゲームが起動していません!", vbOKOnly + vbExclamation, "プロセスオープンエラー"
    Exit Sub
  End If

  '外部ファイル(ore_mem_data.txt)から、各ステータスのあるアドレスを読み込み、配列に格納する
  Dim DataFileName As String
  If Right(App.Path, 1) = "\" Then
    DataFileName = App.Path & "ore_mem_data.txt"
  Else
    DataFileName = App.Path & "\ore_mem_data.txt"
  End If

  StatusData = StringArrayLoad(DataFileName)
  StatusDataUpper = UBound(StatusData)

  '各データをメモリ上から読み込み、それぞれのボタンを使用可・不可に
  DataGet

  ・
  ・
  ・
 (以下省略)


まずは、対象となるゲームのプロセス名をセットしています。

次に、指定したアドレス(419674)から1バイト読み込みを行っています。
この時に読み取るデータには意味はなく、単に対象となるプロセスが開いているかどうかのチェックに利用しています。
(アドレスの数値(419674)も意味はありません)

実は、これは良い処理ではありません。
本来ならば、プロセスがオープンしているかどうかだけをチェックするべきなのですが、
そのためのユーザー定義関数「GetProcessHandle」を、Privateで宣言していたため、
直接呼び出す事ができなかったので、このようなプログラムを書いてしまいました。
(皆さんはマネをしないようにしてくださいね)

さて、プロセスのオープンに成功したら、今度は各パラメータのアドレスを
外部ファイルから読み込むための処理を行います。

外部ファイルは、改造ツールと同じフォルダに置いてありますので、
改造ツールのインストール先のフォルダ名とファイル名を繋げて、フルパス名を作ります。

そして、ユーザー定義関数「StringArrayLoad」を使って、内容を配列に読み込みます。
また、配列の上限(個数)も、この時取得しておきます。

あとは、この配列から値を取り出すだけです。
これは、「DataGet」サブルーチンに記述してあります。


DataGet プロシージャ
Private Sub DataGet()
'各ステータス・パラメータの値が格納されているアドレスを、配列から取得
For StatusDataIndex = 0 To StatusDataUpper
  Select Case GetStrL(StatusData(StatusDataIndex), ":")
  Case "NAME"
    A_name = GetStrR(StatusData(StatusDataIndex), ":")
  Case "HP"
    A_hp = GetStrR(StatusData(StatusDataIndex), ":")
  Case "MAX_HP"
    A_max_hp = GetStrR(StatusData(StatusDataIndex), ":")
  
  Case "STR"
    A_str = GetStrR(StatusData(StatusDataIndex), ":")
  Case "DEF"
    A_def = GetStrR(StatusData(StatusDataIndex), ":")
  Case "LUCK"
    A_luck = GetStrR(StatusData(StatusDataIndex), ":")
  Case "SPEED"
    A_speed = GetStrR(StatusData(StatusDataIndex), ":")
  Case "GOLD"
    A_gold = GetStrR(StatusData(StatusDataIndex), ":")
  
  Case "ITEM1"
    A_i1 = GetStrR(StatusData(StatusDataIndex), ":")
  Case "ITEM2"
    A_i2 = GetStrR(StatusData(StatusDataIndex), ":")
  Case "ITEM3"
    A_i3 = GetStrR(StatusData(StatusDataIndex), ":")
  Case "ITEM4"
    A_i4 = GetStrR(StatusData(StatusDataIndex), ":")
  Case "ITEM5"
    A_i5 = GetStrR(StatusData(StatusDataIndex), ":")
  
  Case Else
  End Select
Next

'上記で取得したアドレスを元に、各ステータスの値をメモリから取得し、表示する
  lblName.Caption = HexToDecOrAscii(GetMemoryData(ProcessName, A_name, "16"), "$2")
  txtHP.Text = HexToDecOrAscii(GetMemoryData(ProcessName, A_hp, "2"), "M")
  txtMaxHP.Text = HexToDecOrAscii(GetMemoryData(ProcessName, A_max_hp, "2"), "M")
  txtSTR.Text = HexToDecOrAscii(GetMemoryData(ProcessName, A_str, "2"), "M")
  txtDEF.Text = HexToDecOrAscii(GetMemoryData(ProcessName, A_def, "2"), "M")
  txtLUCK.Text = HexToDecOrAscii(GetMemoryData(ProcessName, A_luck, "2"), "M")
  txtSPEED.Text = HexToDecOrAscii(GetMemoryData(ProcessName, A_speed, "2"), "M")
  txtGOLD.Text = HexToDecOrAscii(GetMemoryData(ProcessName, A_gold, "2"), "M")
  txtITEM(0).Text = HexToDecOrAscii(GetMemoryData(ProcessName, A_i1, "1"), "S")
  txtITEM(1).Text = HexToDecOrAscii(GetMemoryData(ProcessName, A_i2, "1"), "S")
  txtITEM(2).Text = HexToDecOrAscii(GetMemoryData(ProcessName, A_i3, "1"), "S")
  txtITEM(3).Text = HexToDecOrAscii(GetMemoryData(ProcessName, A_i4, "1"), "S")
  txtITEM(4).Text = HexToDecOrAscii(GetMemoryData(ProcessName, A_i5, "1"), "S")
End Sub


前半部分は、配列の中身をを1つずつ取り出して、各変数にセットしています。
例えば「DEF:41CDC4」を読み込んだ時は、「A_def」変数に「41CDC4」をセットしています。

後半部分は、前回の「DataGet」とほぼ同じです。
ただ、ファイルの読み込みではなく、メモリの読み込みになったため、
使用するユーザー定義関数が「GetFileData」から「GetMemoryData」になっています。


データのチェック〜書き込み処理

さて、読み込みに成功すれば、後は画面上でデータを修正し、それをメモリに書き込むだけですね。
この処理は前回と全く同じか、ほとんど変わっていません。

詳しくは前回の内容をご覧下さい。
データ入力時のチェック
書き込み前の最終チェック
データの書き換え

違いがあるとすれば、ファイルの書き換えではなく、メモリの書き換えになったため
使用するユーザー定義関数が「BinFileEdit」から「MemoryEdit」になっているぐらいです。


これで専用改造ツールの作成は終了です。
次回は、メモリダンプツールの作成について解説する予定です。



前に戻る     次に進む

講座の初めに戻る