このリポジトリは、分解の初期の時代に、生成1と2のポケモンゲームをリバースエンジニアリングするためのツールのコレクションでした。ここのスクリプトは、リポジトリビルドツールから、複雑さのさまざまなプログラムまで、ROMデータを抽出します。ここのツールはいずれも維持されておらず、Python2で記述されており、Python3へのポートがかなりゆっくりと行われたため、まったく実行されません。関心のある2つの主なツール、GBZ80disasm.pyとgfx.pyはもはや使用されておらず、最新の更新ツールを持っています( gbz80disasm.pyの代替についてはMGBDISを参照し、 gfx.pyの交換用のPokeClystal/ツールのさまざまなプログラムを参照)。
その結果、このリポジトリはアーカイブされており、歴史的な目的でのみ利用できます。ここでスクリプトをツールのベースとして使用することを選択した場合、あなたは自分でいます。ここのツールのほんの一部のみが、それがあなたにとって懸念されている場合、オープンソースライセンスの下でリリースされました。
pokemontoolsは、さまざまなポケモンゲームにさまざまなリバースエンジニアリングコンポーネントを提供するPythonモジュールです。これには次のものが含まれます。
このPythonライブラリをsite-packagesにインストールするには:
pip install --upgrade pokemontools
そして、地元の開発作業のために:
python setup.py develop
そしてもちろんローカルインストール:
python setup.py install
テストを実行します:
nosetests-2.7
たくさんのスパミー出力があるかもしれません。スパムをいくつかドロップしてください:
nosetests-2.7 tests.integration.tests --nocapture --nologcapture
crystal.py ROMを解析し、グローバルto_asm()メソッドを使用して人間が読み取るASMをダンプするための便利なクラスを提供します。このASMは、元のROMにコンパイルすることができます。現在、マップヘッダー、「セカンド」マップヘッダー、マップイベントヘッダー、マップスクリプトヘッダー、マップトリガー、マップ「コールバック」、マップブロックダタ、XYトリガー、ワープ、ピープルイベント、テキスト、スクリプトを解析します。
注:これらの例全体で、 import pokemontools.crystal代わりにreload(crystal)を使用することができます。モジュールが初めてロードされたら、ファイルが変更され、更新が必要な場合は、リロードする必要があります。
import pokemontools . crystal as crystal
# parse the ROM
crystal . run_main ()
# create a new dump
asm = crystal . Asm ()
# insert the first 10 maps
x = 10
asm . insert_with_dependencies ( crystal . all_map_headers [: x ])
# dump to extras/output.txt
asm . dump ()これらのラインを実行した後、 cp extras/output.txt main.asmおよびrun git diff main.asm main.asmの変更が発生したことを確認します。新しく挿入されたASMコンパイルが同じROMにコンパイルされるかどうかをテストするには、 make clean && make使用します。何かが壊れた場合、これは非常に大声で文句を言います。
ユニットテストでは、ほとんどのクラスをカバーしています。
python tests.py既知のスクリプト(0x58043)のアドレスのみで始まる特定のスクリプトを調査する方法のデモを次に示します。この場合、スクリプトは2writetextコマンドを呼び出して、いくつかのダイアログを表示します。このダイアログは、例の最後に表示されます。
import pokemontools . crystal as crystal
# parse the script at 0x58043 from the map event header at 0x584c3
# from the second map header at 0x958b8
# from the map header at 0x941ae
# for "Ruins of Alph Outside" (map_group=3 map_id=0x16)
script = Script ( 0x58043 )
# show the script
print script . to_asm ()
# what labels does it point to in the to_asm output?
# these must be present in the final asm file for rgbasm to compile the file
objdeps = script . get_dependencies ()
print str ( objdeps )
# the individual commands that make up the script
commands = script . commands
print str ( commands )
# the 3rd command is 2writetext and points to a text
thirdcommand = script . commands [ 2 ]
print thirdcommand
# <crystal.2writetextCommand instance at 0x8ad4c0c>
# look at the command parameters
params = thirdcommand . params
print params
# {0: <crystal.RawTextPointerLabelParam instance at 0x8ad4b0c>}
# 2writetext always has a single parameter
definition_param_count = len ( getattr ( crystal , "2writetextCommand" ). param_types . keys ())
current_param_count = len ( params . keys ())
assert definition_param_count == current_param_count , "this should never " +
"happen: instance of a command has more parameters than the " +
"definition of the command allows"
# get the first parameter (the text pointer)
param = params [ 0 ]
print param
# <crystal.RawTextPointerLabelParam instance at 0x8ad4b0c>
# RawTextPointerLabelParam instances point to their text
text = param . text
print text
# <crystal.TextScript instance at 0x8ad47ec>
# now investigate this text appearing in this script in "Ruins of Alph Outside"
print text . to_asm ()最終出力は次のテキストになります。
db $ 0 , "Hm? That' s a # - " , $ 4f
db "DEX, isn' t it?" , $ 55
; ...ただし、これはそのTextScriptオブジェクトが最終的なASMにどのように表示されるかではありません。挿入された後にmain.asmでどのように表示されるかを確認するには、 print crystal.to_asm(text)を実行して、次のことを取得します。
UnknownText_0x580c7: ; 0x580c7
db $ 0 , "Hm? That' s a # - " , $ 4f
db "DEX, isn' t it?" , $ 55
db "May I see it?" , $ 51
db "There are so many" , $ 4f
db "kinds of #MON." , $ 51
db "Hm? What' s this?" , $ 51
db "What is this" , $ 4f
db "#MON?" , $ 51
db "It looks like the" , $ 4f
db "strange writing on" , $ 51
db "the walls of the" , $ 4f
db "RUINS." , $ 51
db "If those drawings" , $ 4f
db "are really #-" , $ 55
db "MON, there should" , $ 55
db "be many more." , $ 51
db "I know! Let me up-" , $ 4f
db "grade your #-" , $ 55
db "DEX. Follow me." , $ 57
; 0x581e5 別のアプローチは、ROM全体を解析し、特定のアドレスでスクリプトを確認することです。これには、スクリプトオブジェクトがmap_groupとmap_id変数を設定するという利点があります。
import pokemontools . crystal as crystal
# parse the ROM
crystal . run_main ()
# get the parsed script
script = crystal . script_parse_table [ 0x58043 ]
# read its attributes to figure out map group / map id
map_group = script . map_group
map_id = script . map_id
# MapHeader is not given all the info yet
# in the mean time "map_names" contains some metadata
map_dict = crystal . map_names [ map_group ][ map_id ]
map_header = map_dict [ "header_new" ]
print map_dict [ "name" ]
# Ruins of Alph Outside上記はこれを示していませんが、0x58043のスクリプトがMapEventHeaderで人物として参照されていることがわかります。
print map_header . second_map_header . event_header . to_asm ()これは、次のような構造を示します。
person_event $ 3c , 19 , 15 , $ 7 , $ 0 , 255 , 255 , $ 0 , 0 , UnknownScript_0x58043 , $ 0703この中:
MapEventHeader_0x584c3: ; 0x584c3
; filler
db 0 , 0
; warps
db 11
warp_def $ 11 , $ 2 , 1 , GROUP_RUINS_OF_ALPH_HO_OH_CHAMBER , MAP_RUINS_OF_ALPH_HO_OH_CHAMBER
warp_def $ 7 , $ e , 1 , GROUP_RUINS_OF_ALPH_KABUTO_CHAMBER , MAP_RUINS_OF_ALPH_KABUTO_CHAMBER
warp_def $ 1d , $ 2 , 1 , GROUP_RUINS_OF_ALPH_OMANYTE_CHAMBER , MAP_RUINS_OF_ALPH_OMANYTE_CHAMBER
warp_def $ 21 , $ 10 , 1 , GROUP_RUINS_OF_ALPH_AERODACTYL_CHAMBER , MAP_RUINS_OF_ALPH_AERODACTYL_CHAMBER
warp_def $ d , $ a , 1 , GROUP_RUINS_OF_ALPH_INNER_CHAMBER , MAP_RUINS_OF_ALPH_INNER_CHAMBER
warp_def $ b , $ 11 , 1 , GROUP_RUINS_OF_ALPH_RESEARCH_CENTER , MAP_RUINS_OF_ALPH_RESEARCH_CENTER
warp_def $ 13 , $ 6 , 1 , GROUP_UNION_CAVE_B1F , MAP_UNION_CAVE_B1F
warp_def $ 1b , $ 6 , 2 , GROUP_UNION_CAVE_B1F , MAP_UNION_CAVE_B1F
warp_def $ 5 , $ 7 , 3 , GROUP_ROUTE_36_RUINS_OF_ALPH_GATE , MAP_ROUTE_36_RUINS_OF_ALPH_GATE
warp_def $ 14 , $ d , 1 , GROUP_ROUTE_32_RUINS_OF_ALPH_GATE , MAP_ROUTE_32_RUINS_OF_ALPH_GATE
warp_def $ 15 , $ d , 2 , GROUP_ROUTE_32_RUINS_OF_ALPH_GATE , MAP_ROUTE_32_RUINS_OF_ALPH_GATE
; xy triggers
db 2
xy_trigger 1 , $ e , $ b , $ 0 , UnknownScript_0x58031 , $ 0 , $ 0
xy_trigger 1 , $ f , $ a , $ 0 , UnknownScript_0x5803a , $ 0 , $ 0
; signposts
db 3
signpost 8 , 16 , $ 0 , UnknownScript_0x580b1
signpost 16 , 12 , $ 0 , UnknownScript_0x580b4
signpost 12 , 18 , $ 0 , UnknownScript_0x580b7
; people-events
db 5
person_event $ 27 , 24 , 8 , $ 6 , $ 0 , 255 , 255 , $ 2 , 1 , Trainer_0x58089 , $ ffff
person_event $ 3c , 19 , 15 , $ 7 , $ 0 , 255 , 255 , $ 0 , 0 , UnknownScript_0x58043 , $ 0703
person_event $ 3a , 21 , 17 , $ 3 , $ 0 , 255 , 255 , $ a0 , 0 , UnknownScript_0x58061 , $ 078e
person_event $ 27 , 15 , 18 , $ 2 , $ 11 , 255 , 255 , $ b0 , 0 , UnknownScript_0x58076 , $ 078f
person_event $ 27 , 12 , 16 , $ 7 , $ 0 , 255 , 255 , $ 80 , 0 , UnknownScript_0x5807e , $ 078f
; 0x58560 import pokemontools . crystal as crystal
# load the bytes
crystal . load_rom ()
# get a sequence of bytes
crystal . rom_interval ( 0x112116 , 10 )
# ['0x48', '0x54', '0x54', '0x50', '0x2f', '0x31', '0x2e', '0x30', '0xd', '0xa']
crystal . rom_interval ( 0x112116 , 10 , strings = False )
# [72, 84, 84, 80, 47, 49, 46, 48, 13, 10]
# get bytes until a certain byte
crystal . rom_until ( 0x112116 , 0x50 , strings = False )
# ['0x48', '0x54', '0x54']
# [72, 84, 84]
# or just look at the encoded characters directly
crystal . rom [ 0x112116 : 0x112116 + 10 ]
# 'HTTP/1.0rn'
# look at a text at 0x197186
text = crystal . parse_text_at2 ( 0x197186 , 601 , debug = False )
print text0x197186の最後のテキストは次のようになります。
"""
OAK: Aha! So
you're !
I'm OAK! A #MON
researcher.
I was just visit-
ing my old friend
MR.#MON.
I heard you were
running an errand
for PROF.ELM, so I
waited here.
Oh! What's this?
A rare #MON!
...
"""