第8回  「じっくりソースを追いましょう その2」



コードファイルの解析

前回、frmMaferLite.frm ソースプログラム3
「CodeSet」という関数を呼び出していました。
今回は、この関数を見ていく事にしましょう。


MaferLite.bas ソースプログラム2
Public Function CodeSet() As Long

'配列に読み込んだ内容を1行ずつ評価するために、添字の初期化を行う
lngCodeIndex = 0

'改造コード部分を格納するための配列の初期化
Erase strCodeData
intCodeDataIndex = 0

'戻り値の初期値セット
CodeSet = -1


とりあえず、ループに入る前の部分だけを見ていきましょう。

まず、配列の添字、及びその中身をクリアします。
これは、MaferLite で続けて2つ以上のコードファイルを読み込む時の事を考えた処理です。
一度○○というゲームを改造し、そのまま続けて△△というゲームを改造する場合とかですね。

そして、この関数の戻り値をセットします。
まだ肝心な部分は何も実行しておりませんので、エラーを表す「-1」をセットしておきます。
これらの初期化を行った後、以下のループを実行していきます。


MaferLite.bas ソースプログラム3
01>  Do While Not strCodeString(lngCodeIndex) = "EOF"
02>    Select Case UCase(GetStrL(strCodeString(lngCodeIndex), ":"))
03>    Case "#MAFER_LITE"
04>      CodeSet = -2
05>    Case "#TARGET"
06>      Select Case UCase(GetStrR(strCodeString(lngCodeIndex), ":"))
07>        Case "FILE"
08>          CodeSet = 1
09>  '      Case "MEMORY"
10>  '        CodeSet = 2
11>  '      Case "REGISTRY"
12>  '        CodeSet = 3
13>      End Select
14>    Case "#CODE"
15>      'コード用配列に、コード内容を読み込む
16>      strCodeData(intCodeDataIndex) = UCase(GetStrR(strCodeString(lngCodeIndex), ":"))
17>      intCodeDataIndex = intCodeDataIndex + 1
18>    Case "#FILENAME"
19>      'ファイル名を変数にセットする
20>      strDataFile = UCase(GetStrR(strCodeString(lngCodeIndex), ":"))
21>    Case Else
22>    End Select
23>    lngCodeIndex = lngCodeIndex + 1
24>  Loop
25>  strCodeData(intCodeDataIndex) = "EOF"

26>  End Function


1〜24行目までがループ(繰り返し)文となります。
前回、frmMaferLite.frm ソースプログラム2の18行目でつけた目印がありましたね。
この目印が現れるまで、繰り返しを行うわけです。
つまりは、コードファイルを格納した配列の最後まで・・・という事です。

さて、2行目なのですが、これは、関数の中から別の関数を呼び出しているので、少し見づらいかと思います。
「UCase」というのは、アルファベットを全て大文字に変換する関数です。
これは、コードファイルが大文字で書かれていても、小文字で書かれていても構わないようにするための処理です。

「GetStrL」というのは、文字列操作用の自作関数です。
前回やったように、「Shift」+「F2」で、この関数の定義場所へジャンプしてみましょう。
そこの説明にもあるように、この関数は「指定した文字の左側の文字列を取り出す」ためのものです。

今回の場合は、「strCodeString(lngCodeIndex)」の文字列中から「:」より左側の部分を抜き出せ。 という事になります。
「strCodeString(lngCodeIndex)」には、コードファイル「code1.txt」を読み込んだ場合だと、
「#MAFER_LITE:」が格納されているはずです。
そして、この文字列の「:」より左側を抜き出すわけですから、「#MAFER_LITE」が抜き出されるわけですね。

同様に「GetStrR」という関数の働きも確認しておきましょう。
これは、指定した文字の右側の文字列を取り出すので、例えば
Moji = GetStrR("123+456=579" , "=")
という文を実行すると、Moji には、「579」が格納されます。

こういった、あると便利かな?という関数を自作した場合は、他の様々なプログラムでも流用したいので、
標準モジュール(今回は、「StrCtrl.bas」)に定義しておきましょう。
別のプログラムでこれらの関数を簡単に利用できるようになるので、おすすめです。


話を戻しまして、2行目を実行した結果ですが、今回の例では「#MAFER_LITE」となるはずですので、
3〜4行目が実行される事になります。
この時点で、コードファイルである事が確認できたので、「CodeSet」に「-2」を格納します。

今の段階では、まだ、このコードファイルが完全なものかが分からないので、
(もしかしたら、ファイル名が抜けてるかもしれない・・・等)
関数の戻り値は、エラーを表すものにしておきます。

後は、同じように1行ずつ調べていくわけですが、
6行目では、改造する対象が「ファイル」「メモリ」「レジストリ」のどれかを判断しています。
その後、関数の戻り値をセットします。

16〜17行目では、改造コード部分を、専用の配列に格納し、
19行目では、ファイル名を変数にセットしています。

25行目では、改造コード格納専用の配列の最後に、データの終わりを示す目印を格納しています。
これは前回(frmMaferLite.frm ソースプログラム2)の18行目と同じ目的です。


ざっと説明していきましたが、本当はここで行っている戻り値のセットは好ましくありません。
なぜなら、8行目さえ実行されてしまえば、たとえファイル名が抜けていてもエラーとならないからです。
これを防ぐためには、26行目の直前に、「全ての項目に抜けがないか」「不適切な値が入力されていないか」
といったエラーチェックを行い、そこで戻り値をセットするべきなのですが、今回は省略しています。


ファイル改造用フォームの表示

上記の処理が終了すると、 frmMaferLite.frm ソースプログラム3 の4行目に制御が戻ります。
そしてファイル改造用のフォーム(frmML_FileEdit.frm)が起動されるわけです。
では、その処理を順番に見ていきましょう。


frmML_FileEdit.frm ソースプログラム1
Private Sub Form_Load()
  txtFileName.Text = strDataFile
End Sub


これは、フォームが起動された時に処理される文章です。
「ファイル名」を示すテキストボックスに、「CodeSet」関数で得たファイル名を代入しています。

この状態からファイルのフルパス名を入力し、「書き換え」ボタンを押せば、改造完了でした。
ファイルのフルパス名をドラッグ&ドロップで取得する方法は、第2回で解説済みです。
また、「参照」ボタンを使って、ファイルを選択する方法も用意してありますが、
これは、「コードファイルを開く」ボタンとほぼ同じ内容なので、説明は省略します。

では、肝心の「書き換え」ボタンの処理を見てみましょう。


frmML_FileEdit.frm ソースプログラム2
Private Sub cmdExecute_Click()
On Error GoTo ErrHandler

'各種エラーチェック
If txtFileFullpath.Text = "" Then     'フルパスを入力しているかチェック
  MsgBox "ファイルのフルパス名を入力してください", vbOKOnly + vbExclamation, "入力エラー"
  txtFileFullpath.SetFocus
  Exit Sub
ElseIf Dir(txtFileFullpath.Text) = "" Then 'ファイルの存在チェック
  MsgBox txtFileFullpath.Text & vbCrLf & "ファイルが存在しません" & vbCrLf & vbCrLf & "パスとファイル名を確認して下さい", vbOKOnly + vbExclamation, "読み込みエラー"
  txtFileFullpath.SetFocus
  SelText txtFileFullpath
  Exit Sub
  '指定されたファイルと違うファイル名を入力していないかチェック(コメントアウトしている方の ElseIf文でも、結果は同じ)
'ElseIf UCase(txtFileName.Text) <> UCase(Right(txtFileFullpath.Text, Len(txtFileName.Text))) Then
ElseIf StrComp(txtFileName.Text, Right(txtFileFullpath.Text, Len(txtFileName.Text)), vbTextCompare) Then
  MsgBox "コードファイルのファイル名と、入力されたフルパス名とが異なっています", vbOKOnly + vbExclamation, "入力エラー"
  txtFileFullpath.SetFocus
  SelText txtFileFullpath
  Exit Sub
End If


書き換えを行う前に、エラーチェックを行っています。
今回チェックしているのは、以下の3つです。

1.ファイルのフルパスを入力しているか
2.指定したファイルが存在しているか
3.コードファイルに記述したファイルと、異なるファイルを指定していないか

1番は、入力を忘れた場合の処理ですが、本来なら、1文字も入力をしていないのなら
「書き換え」ボタンを押せないようにする方が親切でしょう。

2番は、指定したファイルがあるかどうかを、「Dir」関数を用いてチェックしています。
ただし、この関数では、隠しファイル属性のついたファイルはチェックできません。
ですが、セーブデータが隠しファイルである可能性は低いので、このままにしておきます。

3番は、目的のファイルを間違えていないかをチェックしています。
この時、ファイル名を比較するのに「UCase」関数と「StrComp」関数のどちらを使っても構いません。
どちらも、大文字と小文字を区別しないための処理です。

それでは実際の書き換えを行っている部分を見ていきます。


frmML_FileEdit.frm ソースプログラム3
'改造コードのみを格納していた配列の最初から、ファイルの書き換えを実行していく
intCodeDataIndex = 0
Do While Not strCodeData(intCodeDataIndex) = "EOF"
  ret = BinFileEdit(txtFileFullpath.Text, strCodeData(intCodeDataIndex))
  If ret = 0 Then
    intCodeDataIndex = intCodeDataIndex + 1
  Else
    MsgBox "セーブデータを書き換えられませんでした" & vbCrLf & "エラー番号" & ret, vbOKOnly + vbExclamation, "書き込みエラー"
    Exit Sub
  End If
Loop

MsgBox "セーブデータを書き換えました", vbOKOnly + vbInformation, "書き換え完了"

cmdBackMain_Click


「CodeSet」関数を実行した時点で、この添字は「#CODE:」の行数と同じだけ増えていますので、
改造コード部分の格納専用配列の添字を「0」にします。
そして、ファイルの書き換えを実行していきます。

「code1.txt」を使用した場合、改造コードの内容は
「10-61」「20>22-62」
となっていますので、このループ文は2回実行されるわけですね。
(1回目で「10-61」、2回目で「20>22-62」の書き換えが行われる)

ただし、「1回目の書き換えが成功し、2回目の書き換えが失敗した」というような場合は
「セーブデータを書き換えられませんでした」というメッセージが表示されるものの、
実際には、1回目の書き換えは実行されていますので、中途半端に書き換えがされてしまいます。

これを防ぐためには、ファイルのバックアップを作成しておき、書き換えに失敗したら
それを使ってファイルを元に戻すという方法が考えられます。
ただ、今回はそこまで触れませんので、余裕があれば各自で考えてみて下さい。
また、「BinFileEdit」関数の使い方は、第2回を参照して下さい。

そして書き換えが終わったら、メッセージを表示して、メイン画面へと戻ります。
メイン画面へ戻るためには、「メインメニューに戻る」ボタンを押せばよいのですが、
ここでは、その処理を直接呼び出しています。(cmdBackMain_Click)
このように記述すると「メインメニューに戻る」ボタンを押した時と同じ動作をします。

では最後に、メインメニューに戻る処理を見ておきます。


frmML_FileEdit.frm ソースプログラム4
Private Sub cmdBackMain_Click()
  Unload Me
  frmMaferLite.Show
End Sub


非常にシンプルですね。
まず、自分自身(ファイル改造用フォーム)をメモリから解放し、メイン画面を表示しています。
フォームの表示や非表示についての説明は、VBちょっといい話を参照して下さい。

とりあえずは、これで一通りの説明が終わりました。
実際にVBを使って、プログラムを動かしながら見ていくと、より分かり易いでしょう。
VBは「F8」キーを一回押す毎に、プログラムが1行ずつ実行されますので、これを有効利用しましょう。

MaferLiteの欠点

現在の「MaferLite」にはいくつもの欠点があります。
(というか、わざとそのようにしています)
例えば、前述したように、中途半端にファイルを書き換えてしまう事があるのも欠点ですね。

もう1つの欠点は、コードファイルを読んだ後「×」ボタンで終了すると、タスクが残ってしまうという事です。
試しに、コードファイルを読んで、ファイル書き換え画面を呼び出してみましょう。
そこで、書き換えをしてもしなくても結構ですので、「×」ボタンを押して画面を閉じてしまいます。

普通ならこれでプログラムは終了すると思うのですが、
「Ctrl+Alt+Delete」キーを押して、タスクリストを確認してみましょう。
どうです?「MaferLite」がまだ残っていますね。

これは、メインメニューがまだ終了していないので、このような状態になってしまっています。
frmMaferLite.frm ソースプログラム3の7行目で「Me.Hide」としていますが、
これは、メイン画面を表示しないようにしただけで、メモリ上にはきっちり残っています。

だから、ファイル改造用フォームだけ終了してもダメなわけですね。
以下のように、終了ボタンを追加するなどして、対処しておきましょう。
もちろん、このようにしても「×」ボタンを押されれば意味はありません。
完全な回避方法は、各自で考えてみて下さい。


ファイル名   
ファイルのフルパス名    「参照」
   書き換え メインメニューに戻る 終了


今回はこれで終わりです。
次回以降は、自作関数の説明をしていきたいと思いますので、お楽しみに〜。



前に戻る     次に進む

講座の初めに戻る