이 저장소는 분해 초기에 Generation 1 및 2 Pokémon Games를 리버스 엔지니어링하기위한 도구 모음이었습니다. 여기서 스크립트는 저장소 빌드 도구에서 ROM 데이터를 추출하기 위해 다양한 복잡성 프로그램에 이르기까지 다양합니다. 여기의 도구 중 어느 것도 더 이상 유지 관리되지 않았으며 Python2로 작성되지 않았으며 일부는 Python3의 포트가 게으르게 수행 되었기 때문에 전혀 실행되지 않습니다. 관심있는 두 가지 주요 도구 인 GBZ80Disasm.py 및 GFX.py는 더 이상 사용되지 않으며 현대적인 툴링을 사용하지 않습니다 ( gbz80disasm.py 를 대체하려면 MGBDIS 및 gfx.py 대체하기위한 PokeCrystal/도구의 다양한 프로그램 참조).
결과적 으로이 저장소는 보관되었으며 역사적 목적으로 만 사용할 수 있습니다. 여기에서 스크립트를 툴링의 기반으로 사용하기로 선택한 경우 직접 있습니다. 여기에있는 도구 중 일부만이 우려되는 경우 오픈 소스 라이센스에 따라 릴리스되었습니다.
pokemontools 는 다양한 Pokémon 게임에 다양한 역 엔지니어링 구성 요소를 제공하는 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을 구문 분석하고 Global 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 및 실행 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
; ... 그러나 이것은 최종 ASM에 TextScript 객체가 나타나는 방식이 아닙니다. 일단 삽입 된 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!
...
"""