ハックゲヌム時蚈塔-最初の恐怖

玠晎らしい日本のサバむバルホラヌゲヌムを取り、それがどのように機胜するかを芋お、それを英語に翻蚳し、それで䜕か他のこずをしたしょう。



はじめに



ClockTower日本ではClocktower-The First Fearずしお知られおいたすは、スヌパヌ任倩堂向けにヒュヌマン゚ンタヌテむメントが最初にリリヌスしたゲヌムです。







これは、ポむントアンドクリックアドベンチャヌゞャンルのゲヌムの1぀ですが、サバむバルホラヌゞャンルの創蚭者の1人にもなっおいたす。 陰謀は、シェルタヌの閉鎖埌の4人の孀児が邞宅に行き着き、䞀人ず぀圌らが消え始めたずいうこずです。 プレむダヌは孀児の䞀人であるゞェニファヌを操䜜し、逃げ道や友人を探し、邞宅で䜕が起こっおいるのかを芋぀けようずしたす。



ゲヌムの雰囲気は厳しく、家の䞭の出来事は偶然に起こりたすが、明らかな理由もなく蚀い換えれば、家は魔力に満ちおいたす、時には䜕かがあなたを殺すこずができたす、クリッパヌのサむコパスが家䞭を远いかけおいたすバむオハザヌド3ネメシスは最初のそのような文字ではありたせん







ゲヌムでは、迅速な思考を必芁ずする危険な状況がしばしば発生し、唯䞀の救いは生き残り、逃げ、隠れるこずです。しかし、ゞェニファヌの心を保぀こずも重芁です。そうしないず、ゞェニファヌは心臓発䜜で倒れお死ぬかもしれたせんゲヌムIllbleedのスタむルで。



このゲヌムは1994幎にSNESでリリヌスされ、1997幎にPSXで匷化されたコンテンツで再リリヌスされ、Wonderswanコン゜ヌルでリリヌスされたしたしかし、Wonderswanを気にする人はいたせんでした。



はい、SNES ROMに぀いおは、Aeon Genesisが英語の翻蚳をリリヌスしたしたそうだったようですが、SNESのバヌゞョンはPCのバヌゞョンよりも劣っおおり、PCのバヌゞョンの方がはるかに優れたサりンドでした。FMVの玹介ビデオなどがありたす。 おそらく、より倚くのコンテンツを含む唯䞀のバヌゞョンはPSXバヌゞョンですが、それに぀いおは埌で説明したす。



最初に、CDからゲヌムを「離乳」したしょう。



パヌト1-研究



次のようなゲヌムフォルダヌがありたす。







BGフォルダヌがあり、その䞭には、分割圢匏の郚屋の背景に䌌たものがあり、それに付随する.MAPファむルがありたす。







PTNファむルずPYXファむルを含むDATAフォルダヌがありたす...それが䜕なのかわかりたせん...レベル画面をロゞックなどに関連付けおいるのかもしれたせん。



ダむアログで䜿甚される人々の顔を持぀bmpを含むFACEフォルダヌもありたす。







ゞェニファヌが䜿甚できるアむテムを含むフォルダヌもありたす。







PATTERNフォルダヌにはABMファむルが含たれおいたす。 スプラむトアニメヌションに䜿甚されるず思いたすが、間違っおいる可胜性がありたす。



SCEフォルダヌには、CT.ADOずCT.ADTの2぀のファむルが含たれおいたす。 それらを考慮したす-これらは、ゲヌム党䜓で発生する論理アクションスクリプトであり、独自のバむナリスクリプト蚀語で蚘述されおいたす...そしお、ほずんどの時間をそれらに費やしたす。



SNDDATAフォルダヌには、音楜ず効果音のMIDIファむルずWAVファむルが含たれおいたす。



VISUALフォルダヌには、すべおの高解像床レンダリング、メニュヌオプションなどが含たれおいたす。 BMP圢匏。 玹介ビデオもここに保存されおいたす。







最埌に、DATA.BINファむルがありたす。それが䜕をするのかただわかりたせん。 さらに、ゲヌムct32.exeの実行可胜ファむルがありたす。



CDなしでゲヌムを実行するず、次のメッセヌゞが衚瀺されたす。







デバッガでゲヌムを実行しお、この行を芋぀けたす。 圌女は、ゲヌムがゲヌムデヌタの堎所を知らない぀たり、CDが挿入されおいないこずを報告するだけであるこずがわかりたす。



パヌト2-NoCDの䜜成



IDAを芋るず、RegQueryValueExが䜿甚されおいるこずがわかりたす。぀たり、レゞストリ倀が読み取られおいたす。 ゲヌムはそこから䜕を埗たすか







たあ...私はそれが答えだず思いたす。



たた停止したす。



䜕が原因であるかを確認したしょう。コヌド内でこれをハヌドコヌディングできたす。







䜙分な䜜業は無駄ではなかったようです ゲヌムはNOREG匕数を探しおいたす。 芋぀からない堎合、レゞストリ倀をチェックし、スキャンをスキップしお、芪ディレクトリにあるず想定しおゲヌムを起動したす。 ここでは、簡単なパッチが圹立ちたす。これにより、ゲヌムは垞にNOREG匕数で始たるず考えるようになりたす。 これを行うには、jzを0xEBたたは無条件のJMPに眮き換えたす。



実行しお取埗







はい



パヌト3-ゲヌム内テキストの怜玢



少し考えおみたしょう-すべおのテキストが実行可胜ファむルにあるず期埅できたすか それは可胜です、倚くのゲヌムで起こりたす。 バむナリファむルには静的な行もありたすたずえば、CDがないこずで芋぀かった゚ラヌが、テキストは他の堎所デヌタファむルにあるのかもしれたせんに隠されおいるず感じおいたす。 芋おみたしょう







SHIFT-JIS圢匏非垞に䞀般的を䜿甚する日本語のテキストは、通垞0x80-0x8Fの範囲の制埡文字で始たりたす...実行可胜ファむルで類䌌したものを探すこずから始めるのがいいでしょう。



次に、これらの行を新しいファむルにコピヌしおメモ垳++で開き、Google翻蚳に貌り付けたす。







そのため、ゲヌムダむアログではなく゚ラヌメッセヌゞに䌌おいたす。 他のファむルを探す時間です







うヌん、理解できる-PTNおよびPYXファむルにはテキストがありたせん。 SCEフォルダヌを確認したしょう。







CT.ADTファむルには4バむトのオフセットが含たれおいるようですファむルの最埌たでは0x100たでカりントし、䞀床に4バむト。



しかし、CT.ADOファむルでは...











ここで、ファむルパスに類䌌したASCII文字列だけでなく、SHIFT-JIS圢匏のテキスト文字列も芋぀かりたした。 このファむル内のデヌタは奇劙に芋えたす...それをよりよく理解できるかどうかを芋おみたしょう。 それらを解析したい堎合は、そうする必芁がありたす。



パヌト4-ADO / ADTの詳现



ADTがどのように機胜するかに぀いおはすでに少しわかっおいるので、ADOがどのように芋えるかを芋おみたしょう。







最䞊郚でマゞックADCオブゞェクトファむルが開始されるため、0xFFの束が256ず぀オフセットされ、デヌタが開始されるようです。 本物のコンピュヌタヌ科孊者のように考える時です



これらの行はすべお、明癜な怜玢のないバむナリ圢匏で、おそらくADTファむルを陀きたす。 内郚には、解析時にゲヌムに衚瀺される内容をゲヌムに理解させる制埡コヌドずデヌタが混圚しおいる必芁がありたす。これらはすべお䜕らかの構造を持っおいたす。



.BMPで文字列を調べるず、同じパタヌンがあるこずに気付くでしょう。



39FFの埌にはれロの2バむト倀ほずんどの堎合、しかし0x0100であるため、これはWORDの倀だず思いたす、れロ倀で終わるASCII行、およびむンデントが続きたす。 実際、FACEのBMPをロヌドするたびに、倀は0xFF39になりたす



実行可胜ファむルを確認したしょう。







わあ これだけでなく、他の意味もありたす。 Shift-JISの数行をチェックしお、パタヌンが芋぀かるかどうかを確認したしょう。







いいね すべおの行は0xFF33で始たり、2぀の16ビット倀0x0F00があり、その埌にShift-JISの行が続きたす。



泚 SHIFT-JISがヌル倀で終了しないこずに気付く堎合がありたすが、これは䞍可胜です。 䞀郚のプログラムはシングルバむトずマルチバむトの䞡方の文字倀を凊理できたすが、叀いプログラムではこれは深刻な問題でした。 その結果、ご芧のずおり、すべおの改行0x0aの埌に0x00が続きたす。 実際、ALL ALL ASCII文字の埌には0x00䞡方の数字たたは英字が続きたす。 したがっお、テキストレンダリングは、ASCII文字0-127のマルチバむト解釈をサポヌトでき、制埡バむトでデヌタバむトを読み取るずきに混乱せず、逆もたた同様です。



したがっお、ゲヌムのロゞックは、䜕らかの方法で終了を芋぀けるたでおそらく新しいオペコヌド通垞は0x28FFを芋぀けるこずによっお文字列を解析する必芁があるず結論付けおいたす。



したがっお、ADOファむルには、スクリプト化された「オペコヌド」ずそれに続くデヌタが詰め蟌たれおいたす。 理論的には、ゲヌムがそれらを読み取り、以前のオペコヌドに基づいお、どのデヌタが予期されるかを知っおいるず想定できたす。 これで、すべおのケヌスで䞊蚘で芋぀けた実行可胜ファむルのスむッチコンストラクトを芋お、ゲヌムで䜿甚されるすべおのオペコヌドを匷調衚瀺できたす圢匏をより完党に理解するため。



倀0xFF20、0xFF87に泚目し、ADOファむルでそれらを探し、次のオペコヌドの前に同じ数のデヌタバむトがあるかどうかを刀断したす。 それらが2バむト倀、文字列などであるかどうかを調べおみたしょう。







さらに、実行可胜ファむルには非垞に興味深いテキストが含たれおいるこずに気付くでしょう。







これは実際にはリストであり、オペコヌドの名前のように疑わしく芋えたす。 幞いなこずに、そうです これで、オペコヌドの名前がわかりたした。







この段階で、デバッガヌを䜿甚しおゲヌムを開始し、構成の実行を䞭断しお、異なるオペコヌドのアクションを芳察できたす。 私たちの興味の1぀はJMPです...



実際、最初のJMP0xFF22の埌には2バむト倀0x17がありたす。



ゲヌムでそれを芳察し、ADO_offsetをIDAで芳察される倉数ずしお蚭定するず、ゲヌムはこの倀から0x1B32に移行するこずがわかりたす。 圌女はこれを行う必芁があるこずをどのように知っおいたすか これは芁因ではありたせん...おそらく







うん



0x17 * 4 = 0x5C、぀たり、ADTはさたざたなシヌンの遷移テヌブルです。 CALL0xFF23関数も同様に機胜したすが、しばらくするずこのオフセットに戻りたす...最初のいく぀かのADTオフセットは0xFF00を指したす。 これはゲヌムでは非垞に重芁であるず思われ、トランゞションは実際にそれらをスキップしたすトランゞション埌にオフセットに+2を远加したす。 これはRETNオペコヌドのようなものですか そう思う。



ただし、ADTファむルの最埌には異なる倀が含たれおおり、ADOファむルのサむズを倧きく超えおいるこずに気付くでしょう。 圌らは䜕を䞎えたすか それを理解したすが、たず、これらの遷移がどのように機胜するかを理解するために、ゲヌムの実行プロセスを詳しく芋おみたしょう特別な泚意を払いたす。







メモリダンプを䜜成するず、メモリ内のADCオブゞェクトファむルCT.ADOの倀が0x8000 int16であり、0x8000たたは32 KBごずに曞き蟌たれおいるこずがわかりたす。 たた、ADOは倉曎されおいたせん。 たた、実行可胜ファむルで、関数が倀を解析し、この倀が芋぀かった堎合NOPなど、2バむトをスキップするこずもわかりたす。







ゲヌムはデヌタを32 KBのフラグメントに分割するためほずんどの堎合、より倚くのセグメントにメモリにアクセスするため、2バむト倀に遭遇するこずが倚い-これは重芁です、ADTには䜕らかの皮類のアドレス倉換が必芁ですADTはアドレスのオフセットを䜿甚するため 4バむト。







ここに倚くの数孊がありたす。デバッガで芳察した堎合、これは次のようになりたす。







最初に関数が芋぀からず、ADOファむルの最埌ぞのポむンタヌがあるず仮定しお、ADTファむルの最埌に到達したした最埌のRETNのオフセットは0x253F4です。 ADTリストでは、0x453F4ずしおリストされおいたす。 他のさたざたなアドレスを探した埌、倉換に2぀の有効なバむトが䜿甚され、それらを半分に分割しお最埌に挿入しおいるこずに気付きたした。



悪くはありたせんが、ADTファむルを生成できるようになりたした逆の操䜜通過した0x8000の間隔に応じお、最も重芁なものを乗算するこずにより。 たた、䞀般的なオペコヌドスキヌムがあり、行の堎所がわかっおいたす。 分解する前に、賞を芋お最善を尜くしたしょう翻蚳が最初。



パヌト4CTTDTIの番



栌玍されおいるフォヌマット文字列ず、ADOファむルからそれらを読み取る方法を知っおいたす。 もちろん、それらを挿入し盎すには、ADTオフセットを倉曎する必芁がありたす。これは、文字列のサむズがはるかに倧きくなる堎合ず小さくなる堎合があるためです。 たず、ADO行をテキストファむルにカットし、非垞に簡単に線集できるものにするこずを怜蚎しおください。これは別のプログラムず芋なすこずができたす。 新しいADOファむルに行を簡単に挿入し、オフセットを簡単に倉曎できる圢匏を䜜成したしょう。 これには䜕が必芁ですか



そのため、行の開始䜍眮のオフセット、元の行のサむズバむト単䜍、次に行自䜓、次のようなものです。



0xE92 25 blahblahblah
      
      





cttd.pyを䜜成したす。



 ''' CTD - Clocktower Text Dumper by rFx ''' import os,sys,struct,binascii f = open("CT_J.ADO","rb") data = f.read() f.close() g = open("ct_txt.txt","wb") for i in range(0,len(data)-1): if(data[i] == '\x33' and data[i+1] == '\xff'): #   6 -   ,     . i+=6 str_offset = i str_end = data[i:].index('\xff') -1 newstr = data[i:i+str_end] strlen = len(newstr) newstr = newstr.replace("\x0a\x00","[NEWLINE]") #      ASCII,    . newstr = newstr.replace("\x00","") g.write("%#x\t%d\t" % (str_offset,strlen)) g.write(newstr) g.write("\n") g.close()
      
      





埌で新しいファむルを生成するために、CT.ADOをCT_J.ADOに名前倉曎したした。



このプログラムは、ADOファむルを読み取り、0xFF33を芋぀けお6バむトをスキップしオペコヌドず2぀の2バむト倀をバむパスするため、最初の行オフセット、行の長さ、および行自䜓をタブ区切り圢匏で曞き蟌み、新しいファむルで新しい行で終わりたす。



すべおの倀0x0a改行を[NEWLINE]に眮き換えおいるこずに気づくでしょう。 これは、行党䜓を1行で凊理し、テキストファむルの圢匏を倉曎せずに新しい行を必芁な堎所に定矩できるためです。



楜しみのために、バカなこずをしたしょう。このテキストを翻蚳者で解析したす。 これは、Google翻蚳にデヌタをロヌドし、蚀語を自動的に認識し、翻蚳しお目的の蚀語のテキストを返すPythonモゞュヌルです。



cttt.py



 #!/usr/bin/env python # -*- encoding: utf-8 -*- ''' Clocktower Auto Translator by rFx ''' import os,sys,binascii,struct from translate import Translator translator = Translator(to_lang="en") #Set to English by Default f = open("ct_txt.txt","rb") g = open("ct_txt_proc2.txt","wb") proc_str = [] for line in f.readlines(): proc_str.append(line.rstrip()) for x in range(0,len(proc_str)): line = proc_str[x] o,l,instr = line.split("\t") ts = translator.translate(instr.decode("SHIFT-JIS").encode("UTF-8")) ts = ts.encode("SHIFT-JIS","replace") proc_str[x] = "%s\t%s\t%s" % (o,l,ts) g.write(proc_str[x]+"\n") #for pc in proc_str: # g.write(pc) g.close()
      
      





むンゞェクタヌで数行詊しおみたしょう-このパッケヌゞの最埌のプログラムは、テキストファむルを解析し、行のすべおのASCII文字にれロを远加し、蟞曞に行を読み蟌んで、どのオフセットが含たれおいるかを確認したす。 さらに、ADOをれロから再䜜成しADTを読み取り、すべおの「シヌン」をオフセットずずもに配列にロヌドし、行間およびその埌のすべおのデヌタをコピヌしたす、ADOの「シヌン」のサむズに基づいおADTを再生成したす。



ctti.py



 ''' Clocktower Text Injector by rFx ''' import os,sys,struct,binascii def is_ascii(s): return all(ord(c) < 128 for c in s) def get_real_offset(offset): #   high_val = offset & 0xFFFF0000 high_val /= 2 low_val = offset & 0xFFFF return high_val+low_val def get_fake_offset(offset): #   mult = int(offset / 0x8000) shft_val = 0x8000 * mult low_val = offset & 0xFFFF return offset + shft_val f = open("CT_J.ADO","rb") data = f.read() f.close() offset_vals = [] adt_list = [] newdata = "" f = open("ct_txt_proc.txt","rb") lines = f.readlines() o,l,s = lines[0].split("\t") first_offset = int(o,16) o,l,s = lines[0].split("\t") last_offset_strend = int(o,16) + int(l) newdata = data[:first_offset] for i in range(0,len(lines)): line = lines[i] offset, osl, instr = line.split("\t") offset = int(offset,16) instr = instr.rstrip('\n') instr = instr.replace("[NEWLINE]","\x0a") #  ASCII. instr = instr.decode("SHIFT-JIS") newstr = "" for char in instr: if(is_ascii(char)): newstr+=char+'\x00' else: newstr+=char instr = newstr instr = instr.encode("SHIFT-JIS") newstrlen = len(instr) osl = int(osl) strldiff = newstrlen - osl #  if(i < len(lines)-1): nextline = lines[i+1] nextoffset,nsl,nstr = nextline.split("\t") offset_vals.append({"offset":offset,"val":strldiff}) nextoffset = int(nextoffset,16) newdata += instr+data[offset+osl:nextoffset] else: offset_vals.append({"offset":offset,"val":strldiff}) newdata += instr + data[offset+osl:] #    EOF f.close() #   ADO. g = open("CT.ADO","wb") g.write(newdata) g.close() #  ADT. f = open("CT_J.ADT","rb") datat = f.read() f.close() g = open("CT.ADT","wb") for i in range(0,len(datat),4): cur_offset = get_real_offset(struct.unpack("<I",datat[i:i+4])[0]) final_adj = 0 for offset in offset_vals: if(cur_offset > offset["offset"]): final_adj += offset["val"] g.write(struct.pack("<I",get_fake_offset(cur_offset + final_adj))) g.close()
      
      





仕組みを確認したしょう。







いいね



幞いなこずに、英語はClocktowerファンフォヌラムで翻蚳されたrtfファむルを芋぀け、倧たかな翻蚳に基づいお行を手動で線集するこずができたした。







すべおが翻蚳され、準備完了です 実行したしょう







くそヌ



䜕か問題がありたす。IDAにファむルをドロップしお、䜕が起こるか芋おみたしょう。











ADOファむルをメモリに読み蟌んでいるように芋えたすが、ポむンタを䜿甚しようずしおいたすが、この堎所には䜕もないため䜿甚できたせん。







おそらく構造䜓a1ずそれが実行するmallocを芳察したすが、これはおそらく問題です。 もう少し掘り䞋げた埌、これらのポむンタヌがここで䜜成されおいるこずがわかりたす。







぀たり、ゲヌムはcmp5オペコヌドに基づいおADO 5 * 0x8000のフラグメントに察しおのみポむンタヌを䜜成したすが、EOFの前にADOデヌタを読み取りたすこれは間違いです。 その結果、0x28000以䞋のサむズのADOファむルをロヌドできたす。 私たちを止めたすか もちろん違いたす メモリ内のSCE構造を詳しく芋おみたしょう...



ADOポむンタヌを読み蟌むすべおのケヌスを5から6に倉曎しお別のポむンタヌを远加できたすが、この最埌のポむンタヌの埌に䜕が起こるでしょうか もちろん、ADTシフトが開始されたす







オフセットADT 0x00はstruct_head + 0x2Aにあり、0x7D0に移動しおいるこずがわかりたす... 0x7D0ぞのポむンタヌ??? 0x8000のように芋えたす。



その結果、ADTファむルには0x4800バむトしかありたせん。 最初のADTむンデックスをたずえば0x2Eに䞋げるず、別のADOポむンタヌを曞き蟌むために4倍のバむトが埗られ、最埌にただ倚くの空きスペヌスがあるず蚀えたす



0x2Aぞの構造䜓リンクを芋぀けお、0x2Eに倉曎したす







はい オブゞェクト指向のリバヌス゚ンゞニアリングが倧奜きです。



玠晎らしい、今ゲヌムは完党に翻蚳されおいたす。 次は



16進゚ディタを䜿甚しおCT32.exeにバむナリ倉曎を加える必芁がありたす。



  529B: 74 EB BC7B: 2A 2E BC8D: D0 CC BD35: 2A 2E BD62: 2A 2E D4DA: 2A 2E D4FC: 2A 2E DA58: 2A 2E DA79: 2A 2E 103DA: 2A 2E 10407: 2A 2E 104F8: 2A 2E 105BB: 2A 2E 105E8: 2A 2E 10703: 2A 2E 10730: 2A 2E 115FA: 2A 2E 116B2: 05 06 116E8: 05 06 11720: 2A 2E 11729: D0 CC 1195D: 05 06 1C50F: 20 00
      
      





Work for the Future-パヌト5-SCEDASM-SCE逆アセンブラヌ



次の論理ステップは、他のすべおのオペコヌドを逆アセンブルしお、ゲヌム/゚ディタヌで読み取られるテキストファむルを䜜成するこずです。



このようなもの



 ''' Clocktower ADC Object File Disassembler by rFx ''' import os,sys,binascii,struct ADO_FILENAME = "CT_J.ADO" ADT_FILENAME = "CT_J.ADT" ADO_OP = { 0xFF00:"RETN", #Scene Prologue - 0 bytes of data. - Also an END value... the game looks to denote endings. 0xFF01:"UNK_01", # varying length data 0xFF02:"UNK_02", # 3 bytes of data 0xFF03:"UNK_03", # 3 bytes of data 0xFF04:"UNK_04", # 3 bytes of data 0xFF05:"UNK_05", # 3 bytes of data 0xFF06:"UNK_06", # 3 bytes of data 0xFF07:"UNK_07", # 3 bytes of data 0xFF0A:"UNK_0A", # 4 bytes of data. Looks like an offset to another link in the list? 0xFF0C:"UNK_0C", # 4 bytes of data 0xFF0D:"UNK_0D", # 4 bytes of data 0xFF10:"UNK_10", # 4 bytes of data 0xFF11:"UNK_11", # 4 bytes of data 0xFF12:"UNK_12", # 4 bytes of data 0xFF13:"UNK_13", # 4 bytes of data 0xFF14:"UNK_14", # 4 bytes of data 0xFF15:"UNK_15", # 4 bytes of data 0xFF16:"UNK_16", # 4 bytes of data 0xFF1F:"UNK_1F", # 0 bytes of data 0xFF20:"ALL", # 0 bytes of data. Only at the end of the ADO (twice) #All opcodes above this are like... prologue opcodes (basically in some other list) 0xFF21:"ALLEND", # 2 bytes of data 0xFF22:"JMP", # 2 bytes of data - I think it uses the value for the int offset in adt as destination +adds 2 0xFF23:"CALL", # 6 bytes of data 0xFF24:"EVDEF", # Not used in the game 0xFF25:"!!!!!!", #Not used in the game 0xFF26:"!!!!!!", #Not used in the game 0xFF27:"!!!!!!", #Not used in the game 0xFF28:"!!!!!!", #0 bytes of data. 0xFF29:"END_IF", # 4 bytes of data 0xFF2A:"WHILE", # 4 bytes of data 0xFF2B:"NOP", # 0 bytes of data 0xFF2C:"BREAK", # Not used in the game 0xFF2D:"ENDIF", # 2 bytes of data 0xFF2E:"ENDWHILE", # 2 bytes of data 0xFF2F:"ELSE", # 2 bytes of data 0xFF30:"MSGINIT", # 10 bytes of data 0xFF31:"MSGTYPE", # Not used in the game 0xFF32:"MSGATTR", # 16 bytes of data 0xFF33:"MSGOUT", # Varying length, our in-game text uses this. :) 0xFF34:"SETMARK", #Varying length 0xFF35:"SETWAIT", #Not used in the game 0xFF36:"MSGWAIT", #0 bytes of data 0xFF37:"EVSTART", #4 bytes of data 0xFF38:"BGFILEDISP", #Not used in the game. 0xFF39:"BGLOAD", #Varying length, normally a path to a BMP file is passed in. 0xFF3A:"PALLOAD", #Varying length. Also takes BMP files. 0xFF3B:"BGMREQ", #Varying length - loads a MIDI file into memory. 0xFF3C:"SPRCLR", #2 bytes of data. 0xFF3D:"ABSOBJANIM", #Not used in the game 0xFF3E:"OBJANIM", #Not used in the game. 0xFF3F:"ALLSPRCLR", #0 bytes of data 0xFF40:"MSGCLR", #0 bytes 0f data 0xFF41:"SCREENCLR", #0 bytes of data 0xFF42:"SCREENON", #0 bytes of data 0xFF43:"SCREENOFF", #0 bytes of data 0xFF44:"SCREENIN", # Not used in the game. 0xFF45:"SCREENOUT", # Not used in the game. 0xFF46:"BGDISP", # Always 12 bytes of data. 0xFF47:"BGANIM", #14 bytes of data. 0xFF48:"BGSCROLL",#10 bytes of data. 0xFF49:"PALSET", #10 bytes of data. 0xFF4A:"BGWAIT", #0 bytes of data. 0xFF4B:"WAIT", #4 bytes of data. 0xFF4C:"BWAIT", #Not used in the game. 0xFF4D:"BOXFILL", #14 bytes of data. 0xFF4E:"BGCLR", # Not used in the game. 0xFF4F:"SETBKCOL", #6 bytes of data. 0xFF50:"MSGCOL", #Not used in the game. 0xFF51:"MSGSPD", #2 bytes of data. 0xFF52:"MAPINIT", #12 bytes of data. 0xFF53:"MAPLOAD", #Two Paths... Sometimes NULL NULL - Loads the background blit bmp and the map file to load it. 0xFF54:"MAPDISP", #Not used in the game. 0xFF55:"SPRENT", #16 bytes of data. 0xFF56:"SETPROC", #2 bytes of data. 0xFF57:"SCEINIT", #0 bytes of data. 0xFF58:"USERCTL", #2 bytes of data. 0xFF59:"MAPATTR", #2 bytes of data. 0xFF5A:"MAPPOS", #6 bytes of data. 0xFF5B:"SPRPOS", #8 bytes of data. 0xFF5C:"SPRANIM", #8 bytes of data. 0xFF5D:"SPRDIR", #10 bytes of data. 0xFF5E:"GAMEINIT", #0 bytes of data. 0xFF5F:"CONTINIT", #0 bytes of data. 0xFF60:"SCEEND", #0 bytes of data. 0xFF61:"MAPSCROLL", #6 bytes of data. 0xFF62:"SPRLMT", #6 bytes of data. 0xFF63:"SPRWALKX", #10 bytes of data. 0xFF64:"ALLSPRDISP", #Not used in the game. 0xFF65:"MAPWRT", #Not used in the game. 0xFF66:"SPRWAIT", #2 bytes of data. 0xFF67:"SEREQ", #Varying length - loads a .WAV file. 0xFF68:"SNDSTOP", #0 bytes of data. 0xFF69:"SESTOP", #Varying length - specifies a .WAV to stop or ALL for all sounds. 0xFF6A:"BGMSTOP", #0 bytes of data. 0xFF6B:"DOORNOSET", #0 bytes of data. 0xFF6C:"RAND", #6 bytes of data. 0xFF6D:"BTWAIT", #2 bytes of data 0xFF6E:"FAWAIT", #0 bytes of data 0xFF6F:"SCLBLOCK", #Varying length - no idea. 0xFF70:"EVSTOP", #Not used in the game. 0xFF71:"SEREQPV", #Varying length - .WAV path input, I think this is to play and repeat. 0xFF72:"SEREQSPR", #Varying length - .WAV path input, I think this is like SEREQPV except different somehow. 0xFF73:"SCERESET", #0 bytes of data. 0xFF74:"BGSPRENT", #12 bytes of data. 0xFF75:"BGSPRPOS", #Not used in the game. 0xFF76:"BGSPRSET", #Not used in the game. 0xFF77:"SLANTSET", #8 bytes of data. 0xFF78:"SLANTCLR", #0 bytes of data. 0xFF79:"DUMMY", #Not used in the game. 0xFF7A:"SPCFUNC", #Varying length - usage uncertain. 0xFF7B:"SEPAN", #Varying length - guessing to set the L/R of Stereo SE. 0xFF7C:"SEVOL", #Varying length - guessing toe set the volume level of SE 0xFF7D:"BGDISPTRN", #14 bytes of data. 0xFF7E:"DEBUG", #Not used in the game. 0xFF7F:"TRACE", #Not used in the game. 0xFF80:"TMWAIT", #4 bytes of data. 0xFF81:"BGSPRANIM", #18 bytes of data. 0xFF82:"ABSSPRENT", #Not used in the game. 0xFF83:"NEXTCOM", #2 bytes of data. 0xFF84:"WORKCLR", #0 bytes of data. 0xFF85:"BGBUFCLR", #4 bytes of data. 0xFF86:"ABSBGSPRENT", #12 bytes of data. 0xFF87:"AVIPLAY", #This one is used only once - to load the intro AVI file. 0xFF88:"AVISTOP", #0 bytes of data. 0xFF89:"SPRMARK", #Only used in PSX Version. 0xFF8A:"BGMATTR",#Only used in PSX Version. #BIG GAP IN OPCODES... maybe not even in existence. 0xFFA0:"UNK_A0", #12 bytes of data. 0xFFB0:"UNK_B0", #12 bytes of data. 0xFFDF:"UNK_DF", #2 bytes of data. 0xFFE0:"UNK_E0", #0 bytes of data. 0xFFEA:"UNK_EA", #0 bytes of data. 0xFFEF:"UNK_EF" #12 bytes of data. } if(__name__=="__main__"): print("#Disassembling ADO/ADT...") #Read ADO/ADT Data to memory. f = open(ADO_FILENAME,"rb") ado_data = f.read() f.close() f = open(ADT_FILENAME,"rb") adt_data = f.read() f.close() scene_count = -1 #Skip ADO Header i = 256 while i < (len(ado_data) -1): cur_val = struct.unpack("<H",ado_data[i:i+2])[0] if(cur_val in ADO_OP.keys()): #0xFF00 if(cur_val == 0xFF00): scene_count +=1 print("#----SCENE %d (Offset %#x)" % (scene_count,i)) print(ADO_OP[cur_val]) i+=2 elif(cur_val == 0xFF1F or cur_val == 0xFF20 or cur_val == 0xFF84 or cur_val == 0xFFEA or cur_val == 0xFFE0 or cur_val == 0xFF88 or cur_val == 0xFF78 or cur_val == 0xFF73 or cur_val == 0xFF6E or cur_val == 0xFF6B or cur_val == 0xFF6A or cur_val == 0xFF68 or cur_val == 0xFF60 or cur_val == 0xFF5F or cur_val == 0xFF5E or cur_val == 0xFF57 or cur_val == 0xFF4A or cur_val == 0xFF43 or cur_val == 0xFF42 or cur_val == 0xFF41 or cur_val == 0xFF40 or cur_val == 0xFF36 or cur_val == 0xFF3F or cur_val == 0xFF36 or cur_val == 0xFF2B or cur_val == 0xFF28): print(ADO_OP[cur_val]) i+=2 #0xFF22 elif(cur_val == 0xFF22 or cur_val == 0xFF51 or cur_val == 0xFF21 or cur_val == 0xFF2D or cur_val == 0xFF2E or cur_val == 0xFF2F or cur_val == 0xFF3C or cur_val == 0xFF56 or cur_val == 0xFF58 or cur_val == 0xFF59 or cur_val == 0xFF66 or cur_val == 0xFF6D or cur_val == 0xFF83 or cur_val == 0xFFDF): i+=2 jmpdata = struct.unpack("<H",ado_data[i:i+2])[0] print("%s %d" % (ADO_OP[cur_val],jmpdata)) i+=2 #0xFF23 elif(cur_val == 0xFF23): i+=2 val_1 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_2 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_3 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 print("%s %#x %#x %#x" % (ADO_OP[cur_val],val_1,val_2,val_3)) elif cur_val == 0xFF29 or cur_val == 0xFF2A or cur_val == 0xFF37: i+=2 val_1 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_2 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 print("%s %d %d" % (ADO_OP[cur_val],val_1,val_2)) elif cur_val in range(0xFF02,0xFF08): i+=2 pri_val = struct.unpack("b",ado_data[i])[0] i+=1 sec_val = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 print("%s %d %d" % (ADO_OP[cur_val],pri_val,sec_val)) elif cur_val in range(0xFF0A,0xFF17): i+=2 pri_val = struct.unpack("<I",ado_data[i:i+4])[0] i+=4 print("%s %#x" % (ADO_OP[cur_val],pri_val)) elif (cur_val == 0xFF30): i+=2 val_1 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_2 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_3 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_4 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_5 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 print("%s %#x %#x %#x %#x %#x" % (ADO_OP[cur_val],val_1,val_2,val_3,val_4,val_5)) elif (cur_val == 0xFF33): i+=2 val_1 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_2 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 endstr_offset = ado_data[i:].index("\xff") endstr_offset -=1 instr = ado_data[i:i+endstr_offset] i+= len(instr) #Decode to UTF-8 instr = instr.replace("\x0a\x00","[NEWLINE]") instr = instr.replace("\x00","[NULL]") instr = instr.decode("SHIFT-JIS") instr = instr.encode("UTF-8") print("%s %#x %#x ``%s``" % (ADO_OP[cur_val],val_1,val_2,instr)) elif (cur_val == 0xFF32): i+=2 val_1 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_2 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_3 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_4 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_5 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_6 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_7 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_8 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 print("%s %#x %#x %#x %#x %#x %#x %#x %#x" % (ADO_OP[cur_val],val_1,val_2,val_3,val_4,val_5,val_6,val_7,val_8)) elif(cur_val == 0xFF34): i+=2 endval_offset = ado_data[i:].index("\xff") - 1 instr = ado_data[i:i+endstr_offset] i+= len(instr) print("%s %s" % (ADO_OP[cur_val],binascii.hexlify(instr))) i+=2 elif(cur_val in range(0xFF39,0xFF3C) or cur_val == 0xFF67): i+=2 val_1 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 endstr_offset = ado_data[i:].index("\xff") - 1 instr = ado_data[i:i+endstr_offset] i+= len(instr) if(instr.find("\x00\x00\x00") != -1): finstr = instr[:instr.index("\x00")] val_2 = struct.unpack("b",instr[instr.index("\x00")+1:instr.index("\x00")+2])[0] val_3 = struct.unpack("b",instr[instr.index("\x00")+2:])[0] print("%s %#x %s %#x %#x" % (ADO_OP[cur_val],val_1,finstr,val_2,val_3)) elif(instr.find("\x00\x00") != -1): finstr = instr[:instr.index("\x00")] val_2 = struct.unpack("b",instr[instr.index("\x00")+1:])[0] print("%s %#x %s %#x" % (ADO_OP[cur_val],val_1,finstr,val_2)) elif(cur_val == 0xFF69): i+=2 endstr_offset = ado_data[i:].index("\xff") - 1 instr = ado_data[i:i+endstr_offset] i+= len(instr) if(instr.find("\x00\x00\x00") != -1): finstr = instr[:instr.index("\x00")] val_2 = struct.unpack("b",instr[instr.index("\x00")+1:instr.index("\x00")+2])[0] val_3 = struct.unpack("b",instr[instr.index("\x00")+2:])[0] print("%s %s %#x %#x" % (ADO_OP[cur_val],finstr,val_2,val_3)) elif(instr.find("\x00\x00") != -1): finstr = instr[:instr.index("\x00")] val_2 = struct.unpack("b",instr[instr.index("\x00")+1:])[0] print("%s %s %#x" % (ADO_OP[cur_val],finstr,val_2)) elif(cur_val == 0xFF71 or cur_val == 0xFF72): i+=2 val_1 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_2 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_3 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 endstr_offset = ado_data[i:].index("\xff") - 1 instr = ado_data[i:i+endstr_offset] i+= len(instr) if(instr.find("\x00\x00\x00") != -1): finstr = instr[:instr.index("\x00")] val_4 = struct.unpack("b",instr[instr.index("\x00")+1:instr.index("\x00")+2])[0] val_5 = struct.unpack("b",instr[instr.index("\x00")+2:])[0] print("%s %#x %#x %#x %s %#x %#x" % (ADO_OP[cur_val],val_1,val_2,val_3,finstr,val_4,val_5)) elif(instr.find("\x00\x00") != -1): finstr = instr[:instr.index("\x00")] val_4 = struct.unpack("b",instr[instr.index("\x00")+1:])[0] print("%s %#x %#x %#x %s %#x" % (ADO_OP[cur_val],val_1,val_2,val_3,finstr,val_4)) elif(cur_val == 0xFF87): i+=2 val_1 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_2 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_3 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_4 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 val_5 = struct.unpack("<H",ado_data[i:i+2])[0] i+=2 endstr_offset = ado_data[i:].index("\xff") - 1 instr = ado_data[i:i+endstr_offset] i+= len(instr) if(instr.find("\x00\x00\x00") != -1): finstr = instr[:instr.index("\x00")] val_6 = struct.unpack("b",instr[instr.index("\x00")+1:instr.index("\x00")+2])[0] val_7 = struct.unpack("b",instr[instr.index("\x00")+2:])[0] print("%s %#x %#x %#x %#x %#x %s %#x %#x" % (ADO_OP[cur_val],val_1,val_2,val_3,val_4,val_5,finstr,val_6,val_7)) elif(instr.find("\x00\x00") != -1): finstr = instr[:instr.index("\x00")] val_6 = struct.unpack("b",instr[instr.index("\x00")+1:])[0] print("%s %#x %#x %#x %#x %#x %s %#x" % (ADO_OP[cur_val],val_1,val_2,val_3,val_4,val_5,finstr,val_6)) #NOT DONE YET else: i+=1 else: i+=1
      
      





その埌、もちろん、これらすべおをADO / ADTペアに戻す必芁がありたす。



Work for the Future-パヌト6-PSXバヌゞョン



ゲヌムのPSXバヌゞョンもADO / ADTを䜿甚したす。 リ゜ヌスを倉換し、PSXからPCバヌゞョンに排他的なコンテンツを远加できるようです。



All Articles