DQ3/6 モンスター画像の構造3

前回まででモンスター画像の構造はわかっている限りで説明したわけですが、実際モンスター画像を移植するとなると、その2で説明した部分がネックになります。アニメーションデータの開始アドレス、各スプライト画像情報の配置アドレス、使用するSNES4BPPS画像データの開始インデックスをインポート先にあわせてシフト、変更する必要があります。初めは手動でやってみたのですが、アニメーションデータの開始アドレスのシフトをプチプチやったところでギブアップしました。というわけでプログラムを書いて以上の処理を自動でやることにしたのですが、珍しくほぼ一発で目的のデータを抽出することができました。それほど長いコードではないので丸ごと載せてしまいます。使いたい人は使ってください。言語はC#です。

[csharp]
public class Snes4bppsConverter
{
public int orgindex;
public int newindex;
}

internal class SpriteExtractor
{
public int StartPos;
public int StartPos2;
public int EndPos;
public List<int> ElemNum = new List<int>();

public void ShiftAddr(ByteArrayList ar, int diffaddr, int snes4bppsindex, ref ByteArrayList SpriteByte, ref ByteArrayList SNES4BPPSByte)
{
SpriteByte.AddRange(ar.GetRange(StartPos, StartPos2 – StartPos));
int LimitPos = SFCDisasm.ConvertRAMtoROMAddress(ar.Get3Byte(StartPos));
int tmpaddr = StartPos;
while (tmpaddr < LimitPos)
{
SpriteByte.Set3Byte(tmpaddr – StartPos, ar.Get3Byte(tmpaddr) + diffaddr);
tmpaddr += 3;
}

SpriteByte.AddRange(ar.GetRange(StartPos2, EndPos – StartPos2 + 1));
LimitPos = SFCDisasm.ConvertRAMtoROMAddress(ar.Get3Byte(StartPos2));
tmpaddr = StartPos2;

SortedList<int, Snes4bppsConverter> tbl = new SortedList<int, Snes4bppsConverter>();

while (tmpaddr < LimitPos)
{
SpriteByte.Set3Byte(tmpaddr – StartPos, ar.Get3Byte(tmpaddr) + diffaddr);
int Pos = SFCDisasm.ConvertRAMtoROMAddress(ar.Get3Byte(tmpaddr));
int Length = ar.Get2Byte(Pos);
Pos += 2;
Length -= 2;
int Length2 = ar.Get2Byte(Pos);
Pos += 2;
Length -= 2;

while (Length > 0)
{
int grapos = ar.Get2Byte(Pos);
if (grapos != 0)
{
if ((grapos & 0xff00) != 0xff00)
{
if (!tbl.ContainsKey(grapos))
{
Snes4bppsConverter elem = new Snes4bppsConverter();
elem.orgindex = grapos;
tbl.Add(grapos, elem);
}
}
}
Length -= 2;
Pos += 2;
}
tmpaddr += 3;
}

int newpos = snes4bppsindex;
List<Snes4bppsConverter> list = new List<Snes4bppsConverter>();
foreach (KeyValuePair<int, Snes4bppsConverter> kvp in tbl)
{
kvp.Value.newindex = newpos++;
list.Add(kvp.Value);
}

LimitPos = SFCDisasm.ConvertRAMtoROMAddress(ar.Get3Byte(StartPos2));
tmpaddr = StartPos2;
int baseaddr = tmpaddr;
while (tmpaddr < LimitPos)
{
int pos = SFCDisasm.ConvertRAMtoROMAddress(ar.Get3Byte(tmpaddr));
int Length = ar.Get2Byte(pos);
pos += 2;
Length -= 2;
int Length2 = ar.Get2Byte(pos);
pos += 2;
Length -= 2;

while (Length > 0)
{
int grapos = ar.Get2Byte(pos);
if (grapos != 0)
{
if ((grapos & 0xff00) != 0xff00)
{
Snes4bppsConverter elem = tbl[grapos];
SpriteByte.Set2Byte(pos – baseaddr + StartPos2 – StartPos, elem.newindex);
}
}
Length -= 2;
pos += 2;
}

tmpaddr += 3;
}
for (int j = 0; j < list.Count; j++)
{
Snes4bppsConverter elem = list[j];
SNES4BPPSByte.AddRange(ar.GetRange(DQ36BMP.GetGraphicAddr(elem.orgindex), 32));
}
}
}
[/csharp]

ByteArrayListはByte型の可変長配列です。Listで定義して、2バイト以上の値をリトルエンディアンでセットするための関数(Get2Byte,Set2Byteなど)をいくつか定義してあります。なお、StartPosはスプライトグラフィックインデックスの0-2バイトに相当するアドレス、StartPos2はスプライトグラフィックインデックスの3-5バイトに相当するアドレス、EndPosはスプライトデータの終わりのアドレスをROM上のアドレスにコンバートしたものです。SFCDisasm.ConvertRAMtoROMAddressはRAM上のアドレス?をROM上のアドレスに変換する関数です。実装はもっさりしてますが、確実に動作するほうを重視しました。

DQ3解析
dq_492をフォローする
レトロゲームハッカーズ

コメント

タイトルとURLをコピーしました