Repositori ini adalah kumpulan alat untuk rekayasa terbalik Game Generasi 1 dan 2 Pokémon di masa -masa awal pembongkaran. Skrip di sini berkisar dari alat pembuatan repositori hingga program yang beragam kompleksitas untuk mengekstrak data ROM. Tidak ada alat di sini yang dipelihara lagi dan ditulis dalam Python2, dan beberapa tidak berjalan sama sekali karena port ke Python3 dilakukan dengan agak malas. Dua alat utama yang menarik, gbz80disasm.py dan gfx.py tidak lagi digunakan dan memiliki perkakas modern yang diperbarui (lihat MGBDIS untuk penggantian gbz80disasm.py dan berbagai program dalam pokecrystal/alat untuk penggantian untuk gfx.py ).
Akibatnya, repositori ini telah diarsipkan dan hanya tersedia untuk tujuan historis. Anda sendiri jika Anda memilih untuk menggunakan skrip apa pun di sini sebagai basis untuk alat. Hanya beberapa alat di sini yang dirilis di bawah lisensi open source jika itu menjadi perhatian Anda.
pokemontools adalah modul Python yang menyediakan berbagai komponen rekayasa terbalik untuk berbagai game Pokémon. Ini termasuk:
Untuk menginstal pustaka Python ini di site-packages :
pip install --upgrade pokemontools
Dan untuk pekerjaan pengembangan lokal:
python setup.py develop
Dan tentu saja instalasi lokal:
python setup.py install
Jalankan tes dengan:
nosetests-2.7
Mungkin ada banyak output spam. Jatuhkan beberapa spam dengan:
nosetests-2.7 tests.integration.tests --nocapture --nologcapture
crystal.py mem-parsing ROM dan menyediakan kelas yang nyaman untuk membuang ASM yang dapat dibaca manusia dengan metode global to_asm() . ASM ini kemudian dapat dikompilasi kembali ke ROM asli. Saat ini mem-parsing header peta, header peta "kedua", header acara peta, header skrip peta, pemicu peta, peta "callbacks", blockdata peta, pemicu xy, warps, orang-orang-acara, teks dan skrip.
Catatan: Di seluruh contoh ini dimungkinkan untuk menggunakan reload(crystal) alih -alih import pokemontools.crystal . Setelah modul dimuat pertama kali, itu harus dimuat ulang jika file berubah dan pembaruan diinginkan.
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 () Setelah menjalankan garis -garis itu, cp extras/output.txt main.asm dan menjalankan git diff main.asm untuk mengkonfirmasi bahwa perubahan main.asm telah terjadi. Untuk menguji apakah ASM yang baru dimasukkan dikompilasi menjadi ROM yang sama, gunakan: make clean && make . Ini akan mengeluh dengan sangat keras jika ada sesuatu yang rusak.
Tes unit mencakup sebagian besar kelas.
python tests.py Berikut adalah demo cara menyelidiki skrip tertentu, dimulai dengan hanya alamat untuk skrip yang diketahui (0x58043). Dalam hal ini, skrip memanggil perintah 2writetext untuk menampilkan beberapa dialog. Dialog ini akan ditampilkan di akhir contoh.
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 ()Output akhir akan menjadi teks berikut.
db $ 0 , "Hm? That' s a # - " , $ 4f
db "DEX, isn' t it?" , $ 55
; ... Namun, ini bukan bagaimana objek TextScript itu akan muncul di ASM akhir. Untuk melihat bagaimana itu akan muncul di main.asm setelah dimasukkan, Anda akan menjalankan print crystal.to_asm(text) untuk mendapatkan yang berikut.
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 Pendekatan lain adalah mengurai seluruh ROM, kemudian memeriksa skrip di alamat tertentu. Ini memiliki keuntungan bahwa objek skrip akan memiliki set variabel map_group dan 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 Meskipun hal di atas tidak menunjukkan ini, ternyata skrip di 0x58043 dirujuk dalam MapEventHeader sebagai orang-orang.
print map_header . second_map_header . event_header . to_asm ()Ini akan menunjukkan struktur secara kasar seperti:
person_event $ 3c , 19 , 15 , $ 7 , $ 0 , 255 , 255 , $ 0 , 0 , UnknownScript_0x58043 , $ 0703Dalam hal ini:
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 textTeks terakhir itu di 0x197186 akan terlihat seperti:
"""
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!
...
"""