ที่เก็บนี้เป็นชุดของเครื่องมือสำหรับวิศวกรรมย้อนกลับเกมPokémon Generation 1 และ 2 ในช่วงแรก ๆ ของการถอดประกอบ สคริปต์ที่นี่มีตั้งแต่ที่เก็บสร้างเครื่องมือไปจนถึงโปรแกรมที่มีความซับซ้อนที่แตกต่างกันไปจนถึงการแยกข้อมูล ROM ไม่มีเครื่องมือใดที่ได้รับการบำรุงรักษาอีกต่อไปและเขียนใน Python2 และบางอย่างไม่ทำงานเลยเพราะพอร์ตไปยัง Python3 นั้นค่อนข้างเกียจคร้าน เครื่องมือหลักสองอย่างที่น่าสนใจคือ GBZ80DISASM.PY และ GFX.PY ไม่ได้ใช้อีกต่อไปและมีเครื่องมือที่ทันสมัยและได้รับการปรับปรุง (ดู MGBDIS สำหรับการเปลี่ยน gbz80disasm.py และโปรแกรมต่าง ๆ ใน Pokecrystal/เครื่องมือแทน gfx.py )
เป็นผลให้พื้นที่เก็บข้อมูลนี้ได้รับการเก็บถาวรและมีให้เฉพาะเพื่อจุดประสงค์ทางประวัติศาสตร์ คุณเป็นของตัวเองถ้าคุณเลือกที่จะใช้สคริปต์ใด ๆ ที่นี่เป็นฐานสำหรับการใช้เครื่องมือ มีเครื่องมือเพียงไม่กี่ตัวเท่านั้นที่ได้รับการปล่อยตัวภายใต้ใบอนุญาตโอเพ่นซอร์สหากเป็นเรื่องที่คุณกังวล
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 และให้ชั้นเรียนที่สะดวกในการทิ้ง ASM ที่มนุษย์อ่านได้ด้วยวิธีการทั่วโลก to_asm() ASM นี้สามารถรวบรวมกลับเข้าไปใน ROM ดั้งเดิม ขณะนี้มันแยกวิเคราะห์ส่วนหัวแผนที่ "สอง" ส่วนหัวแผนที่ส่วนหัวของเหตุการณ์แผนที่ส่วนหัวของสคริปต์แผนที่ทริกเกอร์แผนที่ "การโทรกลับ" แผนที่ BlockData, XY Triggers, Warps, ผู้คนเหตุการณ์และสคริปต์
หมายเหตุ: ตลอดตัวอย่างเหล่านี้เป็นไปได้ที่จะใช้ 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 ในขณะที่ด้านบนไม่แสดงสิ่งนี้ แต่กลับกลายเป็นว่าสคริปต์ที่ 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 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!
...
"""