該存儲庫是在拆卸的早期,用於逆向工程1和2Pokémon遊戲的工具集合。此處的腳本從存儲庫構建工具到各種複雜性的程序,都可以提取ROM數據。這裡沒有一個工具可以維護並寫在python2中,有些工具根本沒有運行,因為python3的端口很懶惰。 GBZ80DISASM.PY和GFX.PY的兩個主要工具不再使用,並且具有現代化的更新工具(請參閱MGBDIS,以替換gbz80disasm.py ,以及pokecrystal/工具中的各種程序以替換gfx.py的替換)。
結果,該存儲庫已被存檔,僅用於歷史目的。如果您選擇在此處使用任何腳本作為工具的基礎,那麼您就是自己的。如果您關注的話,這裡只有少數幾個工具是在開源許可下發布的。
pokemontools是一個Python模塊,為各種神奇寶貝遊戲提供了各種反向工程組件。這包括:
要在site-packages中安裝此Python庫:
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觸發器,扭曲,扭曲,人物事件,文本,文本和腳本。
注意:在這些示例中,可以使用reload(crystal)而不是import pokemontools.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和運行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雖然上面沒有顯示這一點,但事實證明,在MapEventHeader中將0x58043的腳本稱為人類事件。
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 text最後一個在0x197186的文字看起來像:
"""
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!
...
"""