Dieses Repository war eine Sammlung von Tools für Reverse Engineering Die Pokémon -Spiele der Generation 1 und 2 in den frühen Tagen des Zerlegens. Die Skripte hier reichen von Repository -Build -Tools bis hin zu Programmen unterschiedlicher Komplexität, um ROM -Daten zu extrahieren. Keines der Werkzeuge hier wird mehr gepflegt und in Python2 geschrieben, und einige laufen überhaupt nicht, weil der Hafen nach Python3 ziemlich träge gemacht wurde. Die beiden wichtigsten Instrumente von GBZ80Disasm.py und gfx.py werden nicht mehr verwendet und haben moderne, aktualisierte Werkzeuge (siehe MGBDIS für einen Ersatz für gbz80disasm.py und die verschiedenen Programme in Pokrystal/Tools für einen Ersatz für gfx.py ).
Infolgedessen wurde dieses Repository archiviert und ist nur für historische Zwecke verfügbar. Sie sind alleine, wenn Sie hier Skripte als Basis für Werkzeuge verwenden. Nur wenige der Tools hier wurden unter einer Open -Source -Lizenz veröffentlicht, wenn dies für Sie von Bedeutung ist.
pokemontools ist ein Python -Modul, das verschiedene Reverse Engineering -Komponenten für verschiedene Pokémon -Spiele bietet. Dies beinhaltet:
So installieren Sie diese Python-Bibliothek in site-packages :
pip install --upgrade pokemontools
Und für die lokale Entwicklungsarbeit:
python setup.py develop
Und natürlich lokale Installation:
python setup.py install
Führen Sie die Tests mit: durch::
nosetests-2.7
Es könnte viel Spam -Ausgang geben. Lassen Sie einen Teil des Spams mit:
nosetests-2.7 tests.integration.tests --nocapture --nologcapture
crystal.py analysiert das ROM und bietet bequeme Klassen, um die menschlich-lesbare ASM mit der globalen Methode to_asm() abzuwerfen. Dieser ASM kann dann wieder in das ursprüngliche ROM zusammengestellt werden. Derzeit pariert es Kartenheader, "zweite" Kartenheader, Kartenereignisheader, Kartenskript-Header, Kartenauslöser, Karten-"-Callbacks", Map BlockData, XY Triggers, Rasps, People-Events, Texte und Skripte.
HINWEIS: In diesen Beispielen ist es möglich, reload(crystal) anstelle von import pokemontools.crystal zu verwenden. Sobald das Modul zum ersten Mal geladen wurde, muss es neu geladen werden, wenn sich die Datei ändert und die Aktualisierungen erwünscht sind.
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 () Nach dem Ausführen dieser Linien cp extras/output.txt main.asm und git diff main.asm um zu bestätigen, dass Änderungen am main.asm aufgetreten sind. Um zu testen, ob der neu eingefügte ASM in das gleiche ROM kompiliert oder nicht, verwenden Sie: make clean && make . Dies wird sich sehr laut beschweren, wenn etwas gebrochen ist.
Unit -Tests decken die meisten Klassen ab.
python tests.py Hier ist eine Demo, wie man ein bestimmtes Skript untersucht, beginnend mit nur einer Adresse zu einem bekannten Skript (0x58043). In diesem Fall ruft das Skript den Befehl 2writetext auf, um einen Dialogfeld anzuzeigen. Dieser Dialog wird am Ende des Beispiels angezeigt.
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 ()Die endgültige Ausgabe ist der folgende Text.
db $ 0 , "Hm? That' s a # - " , $ 4f
db "DEX, isn' t it?" , $ 55
; ... Dies ist jedoch nicht so, dass dieses TextScript -Objekt im endgültigen ASM angezeigt wird. Um zu sehen, wie es in main.asm nach dem Einfügen erscheint, werden Sie print crystal.to_asm(text) , um die folgenden zu erhalten.
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 Ein weiterer Ansatz besteht darin, das gesamte ROM zu analysieren und dann ein Skript an einer bestimmten Adresse zu überprüfen. Dies hat den Vorteil, dass das Skriptobjekt die Variablen map_group und map_id hat.
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 Während das oben genannte nicht angezeigt wird, stellt sich heraus, dass das Skript bei 0x58043 im MapEventHeader als Person-Ereignis bezeichnet wird.
print map_header . second_map_header . event_header . to_asm ()Dies zeigt eine Struktur ungefähr:
person_event $ 3c , 19 , 15 , $ 7 , $ 0 , 255 , 255 , $ 0 , 0 , UnknownScript_0x58043 , $ 0703Innerhalb davon:
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 textDieser letzte Text bei 0x197186 sieht aus wie:
"""
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!
...
"""