2012-03-05 22:15:35 -08:00
# -*- coding: utf-8 -*-
#utilities to help disassemble pokémon crystal
2012-03-24 19:34:19 -07:00
import sys , os , inspect , md5 , json
2012-03-24 20:59:46 -07:00
from copy import copy , deepcopy
import subprocess
2012-03-27 00:01:04 -07:00
from new import classobj
2012-04-02 10:36:08 -07:00
import random
2012-03-05 22:15:35 -08:00
2012-03-13 22:27:44 -07:00
#for IntervalMap
from bisect import bisect_left , bisect_right
from itertools import izip
2012-03-24 01:54:28 -07:00
#for testing all this crap
2012-04-06 09:54:48 -07:00
try :
import unittest2 as unittest
except ImportError :
import unittest
2012-03-24 01:54:28 -07:00
2012-05-19 22:02:43 -07:00
# for capwords
import string
2012-04-06 10:11:40 -07:00
# Check for things we need in unittest.
if not hasattr ( unittest . TestCase , ' setUpClass ' ) :
print " The unittest2 module or Python 2.7 is required to run this script. "
sys . exit ( 1 )
2012-03-24 19:34:19 -07:00
if not hasattr ( json , " dumps " ) :
json . dumps = json . write
2012-04-06 10:38:22 -07:00
# New versions of json don't have read anymore.
if not hasattr ( json , " read " ) :
json . read = json . loads
2012-04-08 12:46:37 -07:00
spacing = " \t "
2012-05-16 13:22:39 -07:00
lousy_dragon_shrine_hack = [ 0x18d079 , 0x18d0a9 , 0x18d061 , 0x18d091 ]
2012-03-05 22:15:35 -08:00
#table of pointers to map groups
#each map group contains some number of map headers
map_group_pointer_table = 0x94000
map_group_count = 26
map_group_offsets = [ ]
map_header_byte_size = 9
second_map_header_byte_size = 12
#event segment sizes
warp_byte_size = 5
trigger_byte_size = 8
signpost_byte_size = 5
people_event_byte_size = 13
#a message to show with NotImplementedErrors
bryan_message = " bryan hasn ' t got to this yet "
2012-03-18 23:41:22 -07:00
max_texts = 3
text_count = 0
texts = [ ]
2012-04-08 12:46:37 -07:00
#these appear outside of quotes (see pokered/extras/pretty_map_headers.py)
#this doesn't do anything but is still used in TextScript
constant_abbreviation_bytes = { }
2012-04-17 19:37:46 -07:00
# Import the characters from its module.
from chars import chars , jap_chars
2012-03-05 22:15:35 -08:00
2012-05-19 22:24:25 -07:00
from trainers import *
2012-05-20 01:10:40 -07:00
from move_constants import moves
2012-05-19 22:24:25 -07:00
# for fixing trainer_group_names
import re
trainer_group_pointer_table_address = 0x39999
trainer_group_pointer_table_address_gs = 0x3993E
2012-03-25 23:47:58 -07:00
class Size ( ) :
""" a simple way to track whether or not a size
includes the first value or not , like for
whether or not the size of a command in a script
also includes the command byte or not """
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def __init__ ( self , size , inclusive = False ) :
self . inclusive = inclusive
if inclusive : size = size - 1
self . size = size
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def inclusive ( self ) :
return self . size + 1
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def exclusive ( self ) :
return self . size
2012-03-13 22:27:44 -07:00
class IntervalMap ( object ) :
"""
This class maps a set of intervals to a set of values .
>> > i = IntervalMap ( )
>> > i [ 0 : 5 ] = " hello world "
>> > i [ 6 : 10 ] = " hello cruel world "
>> > print i [ 4 ]
" hello world "
"""
2012-04-17 18:22:28 -07:00
2012-03-13 22:27:44 -07:00
def __init__ ( self ) :
""" initializes an empty IntervalMap """
self . _bounds = [ ]
self . _items = [ ]
self . _upperitem = None
2012-04-17 18:22:28 -07:00
2012-03-13 22:27:44 -07:00
def __setitem__ ( self , _slice , _value ) :
""" sets an interval mapping """
assert isinstance ( _slice , slice ) , ' The key must be a slice object '
2012-04-17 18:22:28 -07:00
2012-03-13 22:27:44 -07:00
if _slice . start is None :
start_point = - 1
else :
start_point = bisect_left ( self . _bounds , _slice . start )
2012-04-17 18:22:28 -07:00
2012-03-13 22:27:44 -07:00
if _slice . stop is None :
end_point = - 1
else :
end_point = bisect_left ( self . _bounds , _slice . stop )
2012-04-17 18:22:28 -07:00
2012-03-13 22:27:44 -07:00
if start_point > = 0 :
if start_point < len ( self . _bounds ) and self . _bounds [ start_point ] < _slice . start :
2012-04-17 18:22:28 -07:00
start_point + = 1
2012-03-13 22:27:44 -07:00
2012-04-17 18:22:28 -07:00
if end_point > = 0 :
2012-03-13 22:27:44 -07:00
self . _bounds [ start_point : end_point ] = [ _slice . start , _slice . stop ]
if start_point < len ( self . _items ) :
self . _items [ start_point : end_point ] = [ self . _items [ start_point ] , _value ]
else :
self . _items [ start_point : end_point ] = [ self . _upperitem , _value ]
else :
self . _bounds [ start_point : ] = [ _slice . start ]
if start_point < len ( self . _items ) :
self . _items [ start_point : ] = [ self . _items [ start_point ] , _value ]
else :
self . _items [ start_point : ] = [ self . _upperitem ]
self . _upperitem = _value
else :
if end_point > = 0 :
self . _bounds [ : end_point ] = [ _slice . stop ]
self . _items [ : end_point ] = [ _value ]
else :
self . _bounds [ : ] = [ ]
self . _items [ : ] = [ ]
self . _upperitem = _value
2012-04-17 18:22:28 -07:00
2012-03-13 22:27:44 -07:00
def __getitem__ ( self , _point ) :
""" gets a value from the mapping """
2012-04-17 18:22:28 -07:00
assert not isinstance ( _point , slice ) , ' The key cannot be a slice object '
2012-03-13 22:27:44 -07:00
index = bisect_right ( self . _bounds , _point )
if index < len ( self . _bounds ) :
return self . _items [ index ]
else :
return self . _upperitem
2012-04-17 18:22:28 -07:00
2012-03-13 22:27:44 -07:00
def items ( self ) :
""" returns an iterator with each item being
( ( low_bound , high_bound ) , value )
these items are returned in order """
previous_bound = None
for ( b , v ) in izip ( self . _bounds , self . _items ) :
if v is not None :
yield ( previous_bound , b ) , v
previous_bound = b
if self . _upperitem is not None :
yield ( previous_bound , None ) , self . _upperitem
2012-04-17 18:22:28 -07:00
2012-03-13 22:27:44 -07:00
def values ( self ) :
""" returns an iterator with each item being a stored value
the items are returned in order """
for v in self . _items :
if v is not None :
yield v
if self . _upperitem is not None :
yield self . _upperitem
2012-04-17 18:22:28 -07:00
2012-03-13 22:27:44 -07:00
def __repr__ ( self ) :
s = [ ]
for b , v in self . items ( ) :
if v is not None :
s . append ( ' [ %r , %r ] => %r ' % (
b [ 0 ] ,
b [ 1 ] ,
v
) )
return ' { ' + ' , ' . join ( s ) + ' } '
2012-04-17 18:22:28 -07:00
2012-04-08 13:41:52 -07:00
# ---- script_parse_table explanation ----
# This is an IntervalMap that keeps track of previously parsed scripts, texts
# and other objects. Anything that has a location in the ROM should be mapped
# to an interval (a range of addresses) in this structure. Each object that is
# assigned to an interval should implement attributes or methods like:
# ATTRIBUTE/METHOD EXPLANATION
# label what the heck to call the object
# address where it begins
# to_asm() spit out asm (not including label)
2012-03-13 22:27:44 -07:00
#keys are intervals "500..555" of byte addresses for each script
2012-04-08 13:41:52 -07:00
#last byte is not inclusive(?) really? according to who??
2012-03-13 22:27:44 -07:00
#this is how to make sure scripts are not recalculated
script_parse_table = IntervalMap ( )
def is_script_already_parsed_at ( address ) :
""" looks up whether or not a script is parsed at a certain address """
if script_parse_table [ address ] == None : return False
return True
2012-04-17 18:22:28 -07:00
2012-04-14 11:01:20 -07:00
def script_parse_table_pretty_printer ( ) :
""" helpful debugging output """
for each in script_parse_table . items ( ) :
print each
2012-03-13 22:27:44 -07:00
2012-03-05 22:15:35 -08:00
def map_name_cleaner ( input ) :
""" generate a valid asm label for a given map name """
return input . replace ( " : " , " " ) . \
replace ( " ( " , " " ) . \
replace ( " ) " , " " ) . \
replace ( " ' " , " " ) . \
replace ( " / " , " " ) . \
2012-03-25 23:47:58 -07:00
replace ( " , " , " " ) . \
2012-03-05 22:15:35 -08:00
replace ( " . " , " " ) . \
replace ( " Pokémon Center " , " PokeCenter " ) . \
2012-05-20 23:31:26 -07:00
replace ( " é " , " e " ) . \
2012-05-20 23:35:45 -07:00
replace ( " - " , " " ) . \
2012-05-20 23:38:11 -07:00
replace ( " Hooh " , " HoOh " ) . \
replace ( " hooh " , " HoOh " ) . \
2012-03-05 22:15:35 -08:00
replace ( " " , " " )
class RomStr ( str ) :
""" simple wrapper to prevent a giant rom from being shown on screen """
2012-04-17 18:22:28 -07:00
2012-03-24 11:22:28 -07:00
def length ( self ) :
""" len(self) """
return len ( self )
2012-04-17 18:22:28 -07:00
2012-03-05 22:15:35 -08:00
def __repr__ ( self ) :
return " RomStr(too long) "
2012-04-17 18:22:28 -07:00
2012-04-06 12:22:35 -07:00
def interval ( self , offset , length , strings = True , debug = True ) :
""" returns hex values for the rom starting at offset until offset+length """
returnable = [ ]
for byte in self [ offset : offset + length ] :
if strings :
returnable . append ( hex ( ord ( byte ) ) )
else :
returnable . append ( ord ( byte ) )
return returnable
2012-04-17 18:22:28 -07:00
2012-04-06 12:22:35 -07:00
def until ( self , offset , byte , strings = True , debug = False ) :
""" returns hex values from rom starting at offset until the given byte """
return self . interval ( offset , self . find ( chr ( byte ) , offset ) - offset , strings = strings )
2012-04-17 18:22:28 -07:00
2012-04-02 09:45:42 -07:00
rom = RomStr ( None )
2012-04-17 18:22:28 -07:00
2012-04-02 10:36:08 -07:00
def direct_load_rom ( filename = " ../baserom.gbc " ) :
2012-03-05 22:15:35 -08:00
""" loads bytes into memory """
global rom
2012-04-20 08:34:07 -07:00
file_handler = open ( filename , " rb " )
2012-03-05 22:15:35 -08:00
rom = RomStr ( file_handler . read ( ) )
file_handler . close ( )
return rom
2012-04-17 18:22:28 -07:00
2012-04-02 10:36:08 -07:00
def load_rom ( filename = " ../baserom.gbc " ) :
2012-04-02 09:45:42 -07:00
""" checks that the loaded rom matches the path
and then loads the rom if necessary . """
global rom
if rom != RomStr ( None ) and rom != None :
return rom
if not isinstance ( rom , RomStr ) :
2012-04-02 10:36:08 -07:00
return direct_load_rom ( filename = filename )
2012-04-02 09:45:42 -07:00
elif os . lstat ( filename ) . st_size != len ( rom ) :
2012-04-02 10:36:08 -07:00
return direct_load_rom ( filename )
2012-03-05 22:15:35 -08:00
2012-03-24 11:22:28 -07:00
class AsmList ( list ) :
""" simple wrapper to prevent all asm lines from being shown on screen """
2012-04-17 18:22:28 -07:00
2012-03-24 11:22:28 -07:00
def length ( self ) :
""" len(self) """
return len ( self )
2012-04-17 18:22:28 -07:00
2012-03-24 11:22:28 -07:00
def __repr__ ( self ) :
return " AsmList(too long) "
2012-04-17 18:22:28 -07:00
2012-03-24 11:22:28 -07:00
def load_asm ( filename = " ../main.asm " ) :
""" loads the asm source code into memory """
global asm
asm = open ( filename , " r " ) . read ( ) . split ( " \n " )
asm = AsmList ( asm )
return asm
def grouper ( some_list , count = 2 ) :
""" splits a list into sublists
given : [ 1 , 2 , 3 , 4 ]
returns : [ [ 1 , 2 ] , [ 3 , 4 ] ] """
return [ some_list [ i : i + count ] for i in range ( 0 , len ( some_list ) , count ) ]
2012-03-25 23:47:58 -07:00
def is_valid_address ( address ) :
""" is_valid_rom_address """
if address == None : return False
if type ( address ) == str :
address = int ( address , 16 )
if 0 < = address < = 2097152 : return True
else : return False
2012-03-24 02:43:06 -07:00
def rom_interval ( offset , length , strings = True , debug = True ) :
2012-03-05 22:15:35 -08:00
""" returns hex values for the rom starting at offset until offset+length """
global rom
2012-04-06 12:22:35 -07:00
return rom . interval ( offset , length , strings = strings , debug = debug )
2012-03-05 22:15:35 -08:00
2012-04-06 12:22:35 -07:00
def rom_until ( offset , byte , strings = True , debug = True ) :
2012-03-05 22:15:35 -08:00
""" returns hex values from rom starting at offset until the given byte """
global rom
2012-04-06 12:22:35 -07:00
return rom . until ( offset , byte , strings = strings , debug = debug )
2012-03-05 22:15:35 -08:00
2012-03-11 23:59:23 -07:00
def how_many_until ( byte , starting ) :
index = rom . find ( byte , starting )
return index - starting
2012-03-05 22:15:35 -08:00
def load_map_group_offsets ( ) :
""" reads the map group table for the list of pointers """
global map_group_pointer_table , map_group_count , map_group_offsets
global rom
2012-03-24 13:05:32 -07:00
map_group_offsets = [ ] #otherwise this method can only be used once
2012-03-05 22:15:35 -08:00
data = rom_interval ( map_group_pointer_table , map_group_count * 2 , strings = False )
data = grouper ( data )
for pointer_parts in data :
pointer = pointer_parts [ 0 ] + ( pointer_parts [ 1 ] << 8 )
offset = pointer - 0x4000 + map_group_pointer_table
map_group_offsets . append ( offset )
return map_group_offsets
def calculate_bank ( address ) :
""" you are too lazy to divide on your own? """
if type ( address ) == str :
address = int ( address , 16 )
2012-03-24 02:43:06 -07:00
if 0x4000 < = address < = 0x7FFF :
raise Exception , " bank 1 does not exist "
2012-03-05 22:15:35 -08:00
return int ( address ) / 0x4000
2012-04-17 18:22:28 -07:00
2012-03-12 01:01:38 -07:00
def calculate_pointer ( short_pointer , bank = None ) :
2012-03-05 22:15:35 -08:00
""" calculates the full address given a 4-byte pointer and bank byte """
short_pointer = int ( short_pointer )
2012-03-12 01:17:39 -07:00
if 0x4000 < = short_pointer < = 0x7fff :
2012-03-12 01:01:38 -07:00
short_pointer - = 0x4000
bank = int ( bank )
else :
bank = 0
pointer = short_pointer + ( bank * 0x4000 )
2012-03-05 22:15:35 -08:00
return pointer
2012-04-17 18:22:28 -07:00
2012-03-07 14:59:47 -08:00
def calculate_pointer_from_bytes_at ( address , bank = False ) :
""" calculates a pointer from 2 bytes at a location
or 3 - byte pointer [ bank ] [ 2 - byte pointer ] if bank = True """
if bank == True :
bank = ord ( rom [ address ] )
address + = 1
2012-03-29 23:20:50 -07:00
elif bank == False or bank == None :
2012-03-07 14:59:47 -08:00
bank = calculate_bank ( address )
elif bank == " reverse " or bank == " reversed " :
bank = ord ( rom [ address + 2 ] )
2012-03-11 23:09:25 -07:00
elif type ( bank ) == int :
pass
2012-03-07 14:59:47 -08:00
else :
2012-03-29 23:20:50 -07:00
raise Exception , " bad bank given to calculate_pointer_from_bytes_at "
2012-03-07 14:59:47 -08:00
byte1 = ord ( rom [ address ] )
byte2 = ord ( rom [ address + 1 ] )
temp = byte1 + ( byte2 << 8 )
2012-03-22 23:26:02 -07:00
if temp == 0 :
return None
2012-03-07 14:59:47 -08:00
return calculate_pointer ( temp , bank )
2012-03-07 20:31:59 -08:00
def clean_up_long_info ( long_info ) :
""" cleans up some data from parse_script_engine_script_at formatting issues """
long_info = str ( long_info )
#get rid of the first newline
if long_info [ 0 ] == " \n " :
long_info = long_info [ 1 : ]
#get rid of the last newline and any leftover space
if long_info . count ( " \n " ) > 0 :
if long_info [ long_info . rindex ( " \n " ) + 1 : ] . isspace ( ) :
long_info = long_info [ : long_info . rindex ( " \n " ) ]
#remove spaces+hash from the front of each line
new_lines = [ ]
for line in long_info . split ( " \n " ) :
line = line . strip ( )
if line [ 0 ] == " # " :
line = line [ 1 : ]
new_lines . append ( line )
long_info = " \n " . join ( new_lines )
return long_info
2012-03-13 22:27:44 -07:00
def command_debug_information ( command_byte = None , map_group = None , map_id = None , address = 0 , info = None , long_info = None , pksv_name = None ) :
2012-03-23 22:58:14 -07:00
" used to help debug in parse_script_engine_script_at "
2012-03-07 20:31:59 -08:00
info1 = " parsing command byte " + hex ( command_byte ) + " for map " + \
2012-03-07 14:59:47 -08:00
str ( map_group ) + " . " + str ( map_id ) + " at " + hex ( address )
2012-03-11 13:30:14 -07:00
info1 + = " pksv: " + str ( pksv_name )
#info1 += " info: " + str(info)
2012-03-11 00:01:33 -08:00
#info1 += " long_info: " + long_info
2012-03-07 20:31:59 -08:00
return info1
2012-03-05 22:15:35 -08:00
2012-05-04 21:03:03 -07:00
all_texts = [ ]
class TextScript :
2012-04-29 13:15:48 -07:00
""" A text is a sequence of bytes (and sometimes commands). It ' s not the
same thing as a Script . The bytes are translated into characters based
on the lookup table ( see chars . py ) . The in - text commands are for including
values from RAM , playing sound , etc .
see : http : / / hax . iimarck . us / files / scriptingcodes_eng . htm #InText
"""
base_label = " UnknownText_ "
2012-05-04 21:03:03 -07:00
def __init__ ( self , address , map_group = None , map_id = None , debug = False , label = None , force = False , show = None ) :
2012-04-29 13:15:48 -07:00
self . address = address
2012-05-20 01:39:25 -07:00
# $91, $84, $82, $54, $8c
2012-05-05 13:01:42 -07:00
# 0x19768c is a a weird problem?
2012-05-13 20:48:35 -07:00
if address in [ 0x26ef , 0x26f2 , 0x6ee , 0x1071 , 0x5ce33 , 0x69523 , 0x7ee98 , 0x72176 , 0x7a578 , 0x19c09b , 0x19768c ] :
2012-05-04 21:03:03 -07:00
return None
2012-04-29 13:15:48 -07:00
self . map_group , self . map_id , self . debug = map_group , map_id , debug
2012-05-04 19:50:24 -07:00
self . dependencies = None
self . commands = None
2012-05-04 19:29:07 -07:00
self . force = force
2012-05-20 01:39:25 -07:00
2012-05-04 19:29:07 -07:00
if is_script_already_parsed_at ( address ) and not force :
raise Exception , " TextScript already parsed at " + hex ( address )
2012-04-29 13:15:48 -07:00
if not label :
label = self . base_label + hex ( address )
self . label = Label ( name = label , address = address , object = self )
2012-05-20 01:39:25 -07:00
2012-04-29 13:15:48 -07:00
self . parse ( )
2012-05-13 20:48:35 -07:00
def is_valid ( self ) :
return not ( self . address in [ 0x26ef , 0x26f2 , 0x6ee , 0x1071 , 0x5ce33 , 0x69523 , 0x7ee98 , 0x72176 , 0x7a578 , 0x19c09b , 0x19768c ] )
2012-05-04 19:44:57 -07:00
# hmm this looks exactly like Script.get_dependencies (which makes sense..)
2012-04-29 13:15:48 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-05-13 20:48:35 -07:00
if self . address in [ 0x26ef , 0x26f2 , 0x6ee , 0x1071 , 0x5ce33 , 0x69523 , 0x7ee98 , 0x72176 , 0x7a578 , 0x19c09b , 0x19768c ] :
return [ ]
2012-05-04 19:29:07 -07:00
if self . dependencies != None and not recompute :
global_dependencies . update ( self . dependencies )
return self . dependencies
2012-05-20 01:39:25 -07:00
2012-05-04 19:29:07 -07:00
dependencies = [ ]
2012-05-04 19:50:24 -07:00
2012-05-04 19:29:07 -07:00
for command in self . commands :
deps = command . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies )
dependencies . extend ( deps )
2012-05-20 01:39:25 -07:00
2012-05-04 19:29:07 -07:00
self . dependencies = dependencies
2012-04-29 13:15:48 -07:00
return self . dependencies
2012-05-04 19:44:57 -07:00
# this is almost an exact copy of Script.parse
# with the exception of using text_command_classes instead of command_classes
2012-04-29 13:15:48 -07:00
def parse ( self ) :
2012-05-13 20:48:35 -07:00
if self . address in [ 0x26ef , 0x26f2 , 0x6ee , 0x1071 , 0x5ce33 , 0x69523 , 0x7ee98 , 0x72176 , 0x7a578 , 0x19c09b , 0x19768c ] :
return None
2012-04-29 13:15:48 -07:00
global text_command_classes , script_parse_table
2012-05-04 19:44:57 -07:00
current_address = copy ( self . address )
start_address = copy ( current_address )
2012-05-20 01:39:25 -07:00
2012-05-04 19:44:57 -07:00
# don't clutter up my screen
if self . debug :
print " NewTextScript.parse address= " + hex ( self . address ) + " map_group= " + str ( self . map_group ) + " map_id= " + str ( self . map_id )
# load up the rom if it hasn't been loaded already
load_rom ( )
# in the event that the script parsing fails.. it would be nice to leave evidence
script_parse_table [ start_address : start_address + 1 ] = " incomplete NewTextScript.parse "
# start with a blank script
commands = [ ]
# use this to control the while loop
end = False
2012-05-20 01:39:25 -07:00
2012-05-04 19:44:57 -07:00
# for each command found...
while not end :
# get the current scripting byte
cur_byte = ord ( rom [ current_address ] )
# reset the command class (last command was probably different)
scripting_command_class = None
# match the command id byte to a scripting command class like MainText
for class_ in text_command_classes :
2012-05-04 20:03:52 -07:00
if class_ [ 1 ] . id == cur_byte :
scripting_command_class = class_ [ 1 ]
2012-05-20 01:39:25 -07:00
2012-05-26 14:10:11 -07:00
if self . address == 0x9c00e and self . debug :
if current_address > 0x9c087 :
print " self.commands is: " + str ( commands )
print " command 0 address is: " + hex ( commands [ 0 ] . address ) + " last_address= " + hex ( commands [ 0 ] . last_address )
print " command 1 address is: " + hex ( commands [ 1 ] . address ) + " last_address= " + hex ( commands [ 1 ] . last_address )
raise Exception , " going beyond the bounds for this text script "
2012-05-04 19:44:57 -07:00
# no matching command found
if scripting_command_class == None :
2012-05-26 14:10:11 -07:00
raise Exception , " unable to parse text command $ %.2x in the text script at %s at %s " % ( cur_byte , hex ( start_address ) , hex ( current_address ) )
2012-05-04 19:44:57 -07:00
# create an instance of the command class and let it parse its parameter bytes
cls = scripting_command_class ( address = current_address , map_group = self . map_group , map_id = self . map_id , debug = self . debug , force = self . force )
if self . debug :
print cls . to_asm ( )
2012-05-20 01:39:25 -07:00
2012-05-04 19:44:57 -07:00
# store it in this script object
commands . append ( cls )
# certain commands will end the scripting engine
end = cls . end
# skip past the command's parameter bytes to go to the next command
2012-05-11 11:05:02 -07:00
#current_address += cls.size
current_address = cls . last_address
2012-05-04 19:44:57 -07:00
# last byte belonging to script is last byte of last command,
# or the last byte of the last command's last parameter
# (actually i think this might be the next byte after??)
self . last_address = current_address
2012-05-11 11:05:02 -07:00
if self . debug :
print " cls.address is: " + hex ( cls . address )
print " cls.size is: " + hex ( cls . size )
print " cls.last_address is: " + hex ( cls . last_address )
print " self.last_address is: " + hex ( self . last_address )
assert self . last_address == ( cls . address + cls . size ) , " the last address should equal the last command ' s (address + size) "
assert self . last_address == cls . last_address , " the last address of the TextScript should be the last_address of its last command "
2012-05-10 07:18:23 -07:00
# just some debugging..
2012-05-11 11:05:02 -07:00
if self . debug :
last_address = self . last_address
print " TextScript last_address == " + hex ( last_address )
#assert last_address != 0x5db06, "TextScript.parse somehow has a text with a last_address of 0x5db06 instead of 0x5db07"
2012-05-10 07:18:23 -07:00
2012-05-04 19:44:57 -07:00
# store the script in the global table/map thing
script_parse_table [ start_address : current_address ] = self
2012-05-04 21:03:03 -07:00
all_texts . append ( self )
2012-05-04 20:03:52 -07:00
if self . debug :
asm_output = " \n " . join ( [ command . to_asm ( ) for command in commands ] )
print " -------------- \n " + asm_output
2012-05-04 19:44:57 -07:00
# store the script
self . commands = commands
return commands
2012-04-29 13:15:48 -07:00
def to_asm ( self ) :
2012-05-13 20:48:35 -07:00
if self . address in [ 0x26ef , 0x26f2 , 0x6ee , 0x1071 , 0x5ce33 , 0x69523 , 0x7ee98 , 0x72176 , 0x7a578 , 0x19c09b , 0x19768c ] :
return None
2012-05-04 19:50:24 -07:00
asm_output = " \n " . join ( [ command . to_asm ( ) for command in self . commands ] )
return asm_output
2012-04-29 13:15:48 -07:00
2012-05-04 21:03:03 -07:00
class OldTextScript :
2012-03-23 22:58:14 -07:00
" a text is a sequence of commands different from a script-engine script "
2012-04-20 20:19:01 -07:00
base_label = " UnknownText_ "
def __init__ ( self , address , map_group = None , map_id = None , debug = True , show = True , force = False , label = None ) :
2012-04-08 12:46:37 -07:00
self . address = address
self . map_group , self . map_id , self . debug , self . show , self . force = map_group , map_id , debug , show , force
2012-04-20 20:19:01 -07:00
if not label :
label = self . base_label + hex ( address )
2012-04-26 16:41:13 -07:00
self . label = Label ( name = label , address = address , object = self )
2012-04-20 20:19:01 -07:00
self . dependencies = [ ]
2012-04-08 12:46:37 -07:00
self . parse_text_at ( address )
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
@staticmethod
def find_addresses ( ) :
""" returns a list of text pointers
useful for testing parse_text_engine_script_at
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
Note that this list is not exhaustive . There are some texts that
are only pointed to from some script that a current script just
points to . So find_all_text_pointers_in_script_engine_script will
have to recursively follow through each script to find those .
. . it does this now : )
"""
addresses = set ( )
#for each map group
for map_group in map_names :
#for each map id
for map_id in map_names [ map_group ] :
#skip the offset key
if map_id == " offset " : continue
#dump this into smap
smap = map_names [ map_group ] [ map_id ]
#signposts
signposts = smap [ " signposts " ]
#for each signpost
for signpost in signposts :
if signpost [ " func " ] in [ 0 , 1 , 2 , 3 , 4 ] :
#dump this into script
script = signpost [ " script " ]
elif signpost [ " func " ] in [ 05 , 06 ] :
script = signpost [ " script " ]
else : continue
#skip signposts with no bytes
if len ( script ) == 0 : continue
#find all text pointers in script
texts = find_all_text_pointers_in_script_engine_script ( script , smap [ " event_bank " ] )
#dump these addresses in
addresses . update ( texts )
#xy triggers
xy_triggers = smap [ " xy_triggers " ]
#for each xy trigger
for xy_trigger in xy_triggers :
#dump this into script
script = xy_trigger [ " script " ]
#find all text pointers in script
texts = find_all_text_pointers_in_script_engine_script ( script , smap [ " event_bank " ] )
#dump these addresses in
addresses . update ( texts )
#trigger scripts
triggers = smap [ " trigger_scripts " ]
#for each trigger
for ( i , trigger ) in triggers . items ( ) :
#dump this into script
script = trigger [ " script " ]
#find all text pointers in script
texts = find_all_text_pointers_in_script_engine_script ( script , calculate_bank ( trigger [ " address " ] ) )
#dump these addresses in
addresses . update ( texts )
#callback scripts
callbacks = smap [ " callback_scripts " ]
#for each callback
for ( k , callback ) in callbacks . items ( ) :
#dump this into script
script = callback [ " script " ]
#find all text pointers in script
texts = find_all_text_pointers_in_script_engine_script ( script , calculate_bank ( callback [ " address " ] ) )
#dump these addresses in
addresses . update ( texts )
#people-events
events = smap [ " people_events " ]
#for each event
for event in events :
if event [ " event_type " ] == " script " :
#dump this into script
script = event [ " script " ]
#find all text pointers in script
texts = find_all_text_pointers_in_script_engine_script ( script , smap [ " event_bank " ] )
#dump these addresses in
addresses . update ( texts )
if event [ " event_type " ] == " trainer " :
trainer_data = event [ " trainer_data " ]
addresses . update ( [ trainer_data [ " text_when_seen_ptr " ] ] )
addresses . update ( [ trainer_data [ " text_when_trainer_beaten_ptr " ] ] )
trainer_bank = calculate_bank ( event [ " trainer_data_address " ] )
script1 = trainer_data [ " script_talk_again " ]
texts1 = find_all_text_pointers_in_script_engine_script ( script1 , trainer_bank )
addresses . update ( texts1 )
script2 = trainer_data [ " script_when_lost " ]
texts2 = find_all_text_pointers_in_script_engine_script ( script2 , trainer_bank )
addresses . update ( texts2 )
return addresses
2012-04-17 18:22:28 -07:00
2012-04-08 12:46:37 -07:00
def parse_text_at ( self , address ) :
2012-03-23 22:58:14 -07:00
""" parses a text-engine script ( " in-text scripts " )
http : / / hax . iimarck . us / files / scriptingcodes_eng . htm #InText
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
This is presently very broken .
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
see parse_text_at2 , parse_text_at , and process_00_subcommands
"""
2012-04-08 11:35:35 -07:00
global rom , text_count , max_texts , texts , script_parse_table
2012-03-23 22:58:14 -07:00
if rom == None :
2012-04-02 10:36:08 -07:00
direct_load_rom ( )
2012-03-23 22:58:14 -07:00
if address == None :
return " not a script "
2012-04-08 12:46:37 -07:00
map_group , map_id , debug , show , force = self . map_group , self . map_id , self . debug , self . show , self . force
2012-03-23 22:58:14 -07:00
commands = { }
2012-04-08 11:35:35 -07:00
if is_script_already_parsed_at ( address ) and not force :
print " text is already parsed at this location: " + hex ( address )
2012-04-27 23:34:20 -07:00
raise Exception , " text is already parsed, what ' s going on ? "
2012-04-08 11:35:35 -07:00
return script_parse_table [ address ]
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
total_text_commands = 0
command_counter = 0
original_address = address
offset = address
end = False
2012-04-08 11:35:35 -07:00
script_parse_table [ original_address : original_address + 1 ] = " incomplete text "
2012-03-23 22:58:14 -07:00
while not end :
address = offset
command = { }
command_byte = ord ( rom [ address ] )
2012-03-24 23:09:46 -07:00
if debug :
print " TextScript.parse_script_at has encountered a command byte " + hex ( command_byte ) + " at " + hex ( address )
2012-03-23 22:58:14 -07:00
end_address = address + 1
if command_byte == 0 :
#read until $57, $50 or $58
jump57 = how_many_until ( chr ( 0x57 ) , offset )
jump50 = how_many_until ( chr ( 0x50 ) , offset )
jump58 = how_many_until ( chr ( 0x58 ) , offset )
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
#whichever command comes first
jump = min ( [ jump57 , jump50 , jump58 ] )
2012-04-17 18:22:28 -07:00
2012-04-26 13:04:09 -07:00
end_address = offset + jump #we want the address before $57
2012-04-17 18:22:28 -07:00
2012-04-26 13:04:09 -07:00
lines = process_00_subcommands ( offset + 1 , end_address , debug = debug )
2012-04-17 18:22:28 -07:00
2012-03-24 23:09:46 -07:00
if show and debug :
text = parse_text_at2 ( offset + 1 , end_address - offset + 1 , debug = debug )
2012-03-23 22:58:14 -07:00
print text
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
command = { " type " : command_byte ,
" start_address " : offset ,
" end_address " : end_address ,
" size " : jump ,
" lines " : lines ,
2012-04-17 18:22:28 -07:00
}
2012-03-23 22:58:14 -07:00
offset + = jump
elif command_byte == 0x17 :
#TX_FAR [pointer][bank]
pointer_byte1 = ord ( rom [ offset + 1 ] )
pointer_byte2 = ord ( rom [ offset + 2 ] )
pointer_bank = ord ( rom [ offset + 3 ] )
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
pointer = ( pointer_byte1 + ( pointer_byte2 << 8 ) )
pointer = extract_maps . calculate_pointer ( pointer , pointer_bank )
2012-04-17 18:22:28 -07:00
2012-04-20 20:19:01 -07:00
text = TextScript ( pointer , map_group = self . map_group , map_id = self . amp_id , debug = self . debug , \
2012-04-26 16:45:53 -07:00
show = self . debug , force = self . debug , label = " Target " + self . label . name )
2012-05-13 20:48:35 -07:00
if text . is_valid ( ) :
self . dependencies . append ( text )
2012-04-20 20:19:01 -07:00
2012-03-23 22:58:14 -07:00
command = { " type " : command_byte ,
" start_address " : offset ,
" end_address " : offset + 3 , #last byte belonging to this command
" pointer " : pointer , #parameter
2012-04-20 20:19:01 -07:00
" text " : text ,
2012-03-23 22:58:14 -07:00
}
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
offset + = 3 + 1
elif command_byte == 0x50 or command_byte == 0x57 or command_byte == 0x58 : #end text
command = { " type " : command_byte ,
" start_address " : offset ,
" end_address " : offset ,
}
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
#this byte simply indicates to end the script
end = True
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
#this byte simply indicates to end the script
if command_byte == 0x50 and ord ( rom [ offset + 1 ] ) == 0x50 : #$50$50 means end completely
end = True
commands [ command_counter + 1 ] = command
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
#also save the next byte, before we quit
commands [ command_counter + 1 ] [ " start_address " ] + = 1
commands [ command_counter + 1 ] [ " end_address " ] + = 1
add_command_byte_to_totals ( command_byte )
elif command_byte == 0x50 : #only end if we started with $0
if len ( commands . keys ( ) ) > 0 :
if commands [ 0 ] [ " type " ] == 0x0 : end = True
elif command_byte == 0x57 or command_byte == 0x58 : #end completely
end = True
offset + = 1 #go past this 0x50
elif command_byte == 0x1 :
#01 = text from RAM. [01][2-byte pointer]
size = 3 #total size, including the command byte
pointer_byte1 = ord ( rom [ offset + 1 ] )
pointer_byte2 = ord ( rom [ offset + 2 ] )
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
command = { " type " : command_byte ,
" start_address " : offset + 1 ,
" end_address " : offset + 2 , #last byte belonging to this command
" pointer " : [ pointer_byte1 , pointer_byte2 ] , #RAM pointer
}
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
#view near these bytes
#subsection = rom[offset:offset+size+1] #peak ahead
#for x in subsection:
# print hex(ord(x))
#print "--"
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
offset + = 2 + 1 #go to the next byte
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
#use this to look at the surrounding bytes
if debug :
print " next command is: " + hex ( ord ( rom [ offset ] ) ) + " ... we are at command number: " + str ( command_counter ) + " near " + hex ( offset ) + " on map_id= " + str ( map_id )
elif command_byte == 0x7 :
#07 = shift texts 1 row above (2nd line becomes 1st line); address for next text = 2nd line. [07]
size = 1
command = { " type " : command_byte ,
" start_address " : offset ,
" end_address " : offset ,
}
offset + = 1
elif command_byte == 0x3 :
#03 = set new address in RAM for text. [03][2-byte RAM address]
size = 3
command = { " type " : command_byte , " start_address " : offset , " end_address " : offset + 2 }
offset + = size
elif command_byte == 0x4 : #draw box
#04 = draw box. [04][2-Byte pointer][height Y][width X]
size = 5 #including the command
command = {
" type " : command_byte ,
" start_address " : offset ,
" end_address " : offset + size ,
" pointer_bytes " : [ ord ( rom [ offset + 1 ] ) , ord ( rom [ offset + 2 ] ) ] ,
" y " : ord ( rom [ offset + 3 ] ) ,
" x " : ord ( rom [ offset + 4 ] ) ,
}
offset + = size + 1
elif command_byte == 0x5 :
#05 = write text starting at 2nd line of text-box. [05][text][ending command]
#read until $57, $50 or $58
jump57 = how_many_until ( chr ( 0x57 ) , offset )
jump50 = how_many_until ( chr ( 0x50 ) , offset )
jump58 = how_many_until ( chr ( 0x58 ) , offset )
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
#whichever command comes first
jump = min ( [ jump57 , jump50 , jump58 ] )
2012-04-17 18:22:28 -07:00
2012-04-26 13:04:09 -07:00
end_address = offset + jump #we want the address before $57
2012-04-26 12:59:24 -07:00
2012-04-26 13:04:09 -07:00
lines = process_00_subcommands ( offset + 1 , end_address , debug = debug )
2012-04-17 18:22:28 -07:00
2012-03-24 23:09:46 -07:00
if show and debug :
text = parse_text_at2 ( offset + 1 , end_address - offset + 1 , debug = debug )
2012-03-23 22:58:14 -07:00
print text
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
command = { " type " : command_byte ,
" start_address " : offset ,
" end_address " : end_address ,
" size " : jump ,
" lines " : lines ,
}
offset = end_address + 1
elif command_byte == 0x6 :
#06 = wait for keypress A or B (put blinking arrow in textbox). [06]
command = { " type " : command_byte , " start_address " : offset , " end_address " : offset }
offset + = 1
elif command_byte == 0x7 :
#07 = shift texts 1 row above (2nd line becomes 1st line); address for next text = 2nd line. [07]
command = { " type " : command_byte , " start_address " : offset , " end_address " : offset }
offset + = 1
elif command_byte == 0x8 :
#08 = asm until whenever
command = { " type " : command_byte , " start_address " : offset , " end_address " : offset }
offset + = 1
end = True
elif command_byte == 0x9 :
#09 = write hex-to-dec number from RAM to textbox [09][2-byte RAM address][byte bbbbcccc]
# bbbb = how many bytes to read (read number is big-endian)
# cccc = how many digits display (decimal)
#(note: max of decimal digits is 7,i.e. max number correctly displayable is 9999999)
ram_address_byte1 = ord ( rom [ offset + 1 ] )
ram_address_byte2 = ord ( rom [ offset + 2 ] )
read_byte = ord ( rom [ offset + 3 ] )
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
command = {
" type " : command_byte ,
" address " : [ ram_address_byte1 , ram_address_byte2 ] ,
" read_byte " : read_byte , #split this up when we make a macro for this
}
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
offset + = 4
else :
#if len(commands) > 0:
# print "Unknown text command " + hex(command_byte) + " at " + hex(offset) + ", script began with " + hex(commands[0]["type"])
if debug :
print " Unknown text command at " + hex ( offset ) + " - command: " + hex ( ord ( rom [ offset ] ) ) + " on map_id= " + str ( map_id )
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
#end at the first unknown command
end = True
commands [ command_counter ] = command
command_counter + = 1
total_text_commands + = len ( commands )
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
text_count + = 1
#if text_count >= max_texts:
# sys.exit()
2012-04-17 18:22:28 -07:00
2012-04-08 12:46:37 -07:00
self . commands = commands
2012-04-26 13:04:09 -07:00
self . last_address = offset
script_parse_table [ original_address : offset ] = self
2012-04-30 00:21:43 -07:00
all_texts . append ( self )
2012-04-21 16:09:03 -07:00
self . size = self . byte_count = self . last_address - original_address
2012-03-23 22:58:14 -07:00
return commands
2012-04-17 18:22:28 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
#if recompute:
# raise NotImplementedError, bryan_message
global_dependencies . update ( self . dependencies )
2012-04-20 20:19:01 -07:00
return self . dependencies
2012-04-08 12:46:37 -07:00
def to_asm ( self , label = None ) :
address = self . address
start_address = address
2012-04-26 16:41:13 -07:00
if label == None : label = self . label . name
2012-04-28 09:08:05 -07:00
#using deepcopy because otherwise additional @s get appended each time
#like to the end of the text for TextScript(0x5cf3a)
commands = deepcopy ( self . commands )
2012-03-24 00:13:59 -07:00
#apparently this isn't important anymore?
needs_to_begin_with_0 = True
#start with zero please
2012-04-17 18:22:28 -07:00
byte_count = 0
2012-03-24 00:13:59 -07:00
#where we store all output
output = " "
had_text_end_byte = False
had_text_end_byte_57_58 = False
had_db_last = False
2012-04-25 19:53:20 -07:00
xspacing = " "
2012-03-24 00:13:59 -07:00
#reset this pretty fast..
first_line = True
#for each command..
for this_command in commands . keys ( ) :
if not " lines " in commands [ this_command ] . keys ( ) :
command = commands [ this_command ]
if not " type " in command . keys ( ) :
print " ERROR in command: " + str ( command )
continue #dunno what to do here?
2012-04-17 18:22:28 -07:00
2012-03-24 00:13:59 -07:00
if command [ " type " ] == 0x1 : #TX_RAM
p1 = command [ " pointer " ] [ 0 ]
p2 = command [ " pointer " ] [ 1 ]
2012-04-17 18:22:28 -07:00
2012-03-24 00:13:59 -07:00
#remember to account for big endian -> little endian
2012-04-25 19:53:20 -07:00
output + = " \n " + xspacing + " TX_RAM $ %.2x %.2x " % ( p2 , p1 )
2012-03-24 00:13:59 -07:00
byte_count + = 3
had_db_last = False
elif command [ " type " ] == 0x17 : #TX_FAR
#p1 = command["pointer"][0]
#p2 = command["pointer"][1]
2012-04-25 19:53:20 -07:00
output + = " \n " + xspacing + " TX_FAR _ " + label + " ; " + hex ( command [ " pointer " ] )
2012-03-24 00:13:59 -07:00
byte_count + = 4 #$17, bank, address word
had_db_last = False
elif command [ " type " ] == 0x9 : #TX_RAM_HEX2DEC
#address, read_byte
2012-04-25 19:53:20 -07:00
output + = " \n " + xspacing + " TX_NUM $ %.2x %.2x , $ %.2x " % ( command [ " address " ] [ 1 ] , command [ " address " ] [ 0 ] , command [ " read_byte " ] )
2012-03-24 00:13:59 -07:00
had_db_last = False
byte_count + = 4
elif command [ " type " ] == 0x50 and not had_text_end_byte :
#had_text_end_byte helps us avoid repeating $50s
if had_db_last :
output + = " , $50 "
else :
2012-04-25 19:53:20 -07:00
output + = " \n " + xspacing + " db $50 "
2012-03-24 00:13:59 -07:00
byte_count + = 1
had_db_last = True
elif command [ " type " ] in [ 0x57 , 0x58 ] and not had_text_end_byte_57_58 :
if had_db_last :
output + = " , $ %.2x " % ( command [ " type " ] )
else :
2012-04-25 19:53:20 -07:00
output + = " \n " + xspacing + " db $ %.2x " % ( command [ " type " ] )
2012-03-24 00:13:59 -07:00
byte_count + = 1
had_db_last = True
elif command [ " type " ] in [ 0x57 , 0x58 ] and had_text_end_byte_57_58 :
pass #this is ok
elif command [ " type " ] == 0x50 and had_text_end_byte :
pass #this is also ok
elif command [ " type " ] == 0x0b :
if had_db_last :
output + = " , $0b "
else :
2012-04-25 19:53:20 -07:00
output + = " \n " + xspacing + " db $0B "
2012-03-24 00:13:59 -07:00
byte_count + = 1
had_db_last = True
elif command [ " type " ] == 0x11 :
if had_db_last :
output + = " , $11 "
else :
2012-04-25 19:53:20 -07:00
output + = " \n " + xspacing + " db $11 "
2012-03-24 00:13:59 -07:00
byte_count + = 1
had_db_last = True
elif command [ " type " ] == 0x6 : #wait for keypress
if had_db_last :
output + = " , $6 "
else :
2012-04-25 19:53:20 -07:00
output + = " \n " + xspacing + " db $6 "
2012-03-24 00:13:59 -07:00
byte_count + = 1
had_db_last = True
else :
print " ERROR in command: " + hex ( command [ " type " ] )
had_db_last = False
2012-04-17 18:22:28 -07:00
2012-03-24 00:13:59 -07:00
#everything else is for $0s, really
continue
lines = commands [ this_command ] [ " lines " ]
2012-04-17 18:22:28 -07:00
2012-03-24 00:13:59 -07:00
#reset this in case we have non-$0s later
had_db_last = False
2012-04-17 18:22:28 -07:00
2012-03-24 00:13:59 -07:00
#add the ending byte to the last line- always seems $57
#this should already be in there, but it's not because of a bug in the text parser
lines [ len ( lines . keys ( ) ) - 1 ] . append ( commands [ len ( commands . keys ( ) ) - 1 ] [ " type " ] )
2012-04-17 18:22:28 -07:00
2012-03-24 00:13:59 -07:00
first = True #first byte
for line_id in lines :
line = lines [ line_id ]
2012-04-25 19:53:20 -07:00
output + = xspacing + " db "
2012-03-24 00:13:59 -07:00
if first and needs_to_begin_with_0 :
output + = " $0, "
first = False
byte_count + = 1
2012-04-17 18:22:28 -07:00
2012-03-24 00:13:59 -07:00
quotes_open = False
first_byte = True
was_byte = False
for byte in line :
if byte == 0x50 :
had_text_end_byte = True #don't repeat it
if byte in [ 0x58 , 0x57 ] :
had_text_end_byte_57_58 = True
2012-04-17 18:22:28 -07:00
2012-04-08 12:46:37 -07:00
if byte in chars :
2012-03-24 00:13:59 -07:00
if not quotes_open and not first_byte : #start text
output + = " , \" "
quotes_open = True
first_byte = False
if not quotes_open and first_byte : #start text
output + = " \" "
quotes_open = True
2012-04-08 12:46:37 -07:00
output + = chars [ byte ]
2012-03-24 00:13:59 -07:00
elif byte in constant_abbreviation_bytes :
if quotes_open :
output + = " \" "
quotes_open = False
if not first_byte :
output + = " , "
output + = constant_abbreviation_bytes [ byte ]
else :
if quotes_open :
output + = " \" "
quotes_open = False
2012-04-17 18:22:28 -07:00
2012-03-24 00:13:59 -07:00
#if you want the ending byte on the last line
#if not (byte == 0x57 or byte == 0x50 or byte == 0x58):
if not first_byte :
output + = " , "
2012-04-17 18:22:28 -07:00
2012-03-24 00:13:59 -07:00
output + = " $ " + hex ( byte ) [ 2 : ]
was_byte = True
2012-04-17 18:22:28 -07:00
2012-03-24 00:13:59 -07:00
#add a comma unless it's the end of the line
#if byte_count+1 != len(line):
# output += ", "
2012-04-17 18:22:28 -07:00
2012-03-24 00:13:59 -07:00
first_byte = False
byte_count + = 1
#close final quotes
if quotes_open :
output + = " \" "
quotes_open = False
2012-04-17 18:22:28 -07:00
2012-03-24 00:13:59 -07:00
output + = " \n "
2012-04-25 19:53:20 -07:00
#include_newline = "\n"
#if len(output)!=0 and output[-1] == "\n":
# include_newline = ""
#output += include_newline + "; " + hex(start_address) + " + " + str(byte_count) + " bytes = " + hex(start_address + byte_count)
2012-04-26 23:06:31 -07:00
if len ( output ) > 0 and output [ - 1 ] == " \n " :
2012-04-25 19:53:20 -07:00
output = output [ : - 1 ]
2012-04-21 16:09:03 -07:00
self . size = self . byte_count = byte_count
return output
2012-03-24 00:13:59 -07:00
2012-04-08 11:35:35 -07:00
def parse_text_engine_script_at ( address , map_group = None , map_id = None , debug = True , show = True , force = False ) :
2012-03-23 22:58:14 -07:00
""" parses a text-engine script ( " in-text scripts " )
http : / / hax . iimarck . us / files / scriptingcodes_eng . htm #InText
see parse_text_at2 , parse_text_at , and process_00_subcommands
"""
2012-04-08 11:35:35 -07:00
if is_script_already_parsed_at ( address ) and not force :
return script_parse_table [ address ]
2012-04-08 12:46:37 -07:00
return TextScript ( address , map_group = map_group , map_id = map_id , debug = debug , show = show , force = force )
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
def find_text_addresses ( ) :
""" returns a list of text pointers
useful for testing parse_text_engine_script_at """
return TextScript . find_addresses ( )
2012-04-26 14:50:52 -07:00
class EncodedText :
2012-03-23 22:58:14 -07:00
""" a sequence of bytes that, when decoded, represent readable text
2012-04-26 14:58:20 -07:00
based on the chars table from preprocessor . py and other places """
2012-04-26 14:50:52 -07:00
base_label = " UnknownRawText_ "
2012-04-17 18:22:28 -07:00
2012-04-26 14:50:52 -07:00
def __init__ ( self , address , bank = None , map_group = None , map_id = None , debug = True , label = None ) :
self . address = address
if bank :
self . bank = bank
else :
self . bank = calculate_bank ( address )
self . map_group , self . map_id , self . debug = map_group , map_id , debug
if not label :
label = self . base_label + hex ( address )
2012-04-26 16:41:13 -07:00
self . label = Label ( name = label , address = address , object = self )
2012-05-22 09:57:00 -07:00
self . dependencies = None
2012-04-26 14:50:52 -07:00
self . parse ( )
script_parse_table [ self . address : self . last_address ] = self
2012-05-20 01:39:25 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-26 14:50:52 -07:00
return [ ]
2012-05-20 01:39:25 -07:00
2012-04-26 14:50:52 -07:00
def parse ( self ) :
offset = self . address
#read until $57, $50 or $58
jump57 = how_many_until ( chr ( 0x57 ) , offset )
jump50 = how_many_until ( chr ( 0x50 ) , offset )
jump58 = how_many_until ( chr ( 0x58 ) , offset )
#whichever command comes first
jump = min ( [ jump57 , jump50 , jump58 ] )
end_address = offset + jump #we want the address before $57
2012-05-20 01:39:25 -07:00
2012-04-26 14:50:52 -07:00
text = parse_text_at2 ( offset , end_address - offset , debug = self . debug )
2012-05-22 09:38:57 -07:00
if jump == jump50 :
text + = " @ "
2012-04-26 14:50:52 -07:00
self . text = text
self . last_address = self . end_address = end_address
def to_asm ( self ) :
return " \" " + self . text + " \" "
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
@staticmethod
2012-03-24 23:09:46 -07:00
def process_00_subcommands ( start_address , end_address , debug = True ) :
2012-03-23 22:58:14 -07:00
""" split this text up into multiple lines
based on subcommands ending each line """
2012-03-24 23:09:46 -07:00
if debug :
print " process_00_subcommands( " + hex ( start_address ) + " , " + hex ( end_address ) + " ) "
2012-03-23 22:58:14 -07:00
lines = { }
subsection = rom [ start_address : end_address ]
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
line_count = 0
current_line = [ ]
for pbyte in subsection :
byte = ord ( pbyte )
current_line . append ( byte )
if byte == 0x4f or byte == 0x51 or byte == 0x55 :
lines [ line_count ] = current_line
current_line = [ ]
line_count + = 1
2012-05-20 01:39:25 -07:00
2012-03-23 22:58:14 -07:00
#don't forget the last line
lines [ line_count ] = current_line
line_count + = 1
return lines
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
@staticmethod
2012-04-08 21:05:19 -07:00
def from_bytes ( bytes , debug = True , japanese = False ) :
2012-03-23 22:58:14 -07:00
""" assembles a string based on bytes looked up in the chars table """
line = " "
2012-04-08 21:05:19 -07:00
if japanese : charset = jap_chars
else : charset = chars
2012-03-23 22:58:14 -07:00
for byte in bytes :
if type ( byte ) != int :
byte = ord ( byte )
2012-04-08 21:05:19 -07:00
if byte in charset . keys ( ) :
line + = charset [ byte ]
2012-03-24 23:09:46 -07:00
elif debug :
2012-03-23 22:58:14 -07:00
print " byte not known: " + hex ( byte )
return line
2012-04-17 18:22:28 -07:00
2012-03-23 22:58:14 -07:00
@staticmethod
2012-04-08 21:05:19 -07:00
def parse_text_at ( address , count = 10 , debug = True , japanese = False ) :
2012-03-23 22:58:14 -07:00
""" returns a string of text from an address
this does not handle text commands """
output = " "
2012-03-24 23:09:46 -07:00
commands = process_00_subcommands ( address , address + count , debug = debug )
2012-03-23 22:58:14 -07:00
for ( line_id , line ) in commands . items ( ) :
2012-04-08 21:05:19 -07:00
output + = parse_text_from_bytes ( line , debug = debug , japanese = japanese )
2012-03-24 23:09:46 -07:00
texts . append ( [ address , output ] )
2012-03-23 22:58:14 -07:00
return output
2012-04-17 18:22:28 -07:00
2012-03-24 23:09:46 -07:00
def process_00_subcommands ( start_address , end_address , debug = True ) :
2012-03-11 23:59:23 -07:00
""" split this text up into multiple lines
based on subcommands ending each line """
2012-03-24 23:09:46 -07:00
return EncodedText . process_00_subcommands ( start_address , end_address , debug = debug )
2012-04-17 18:22:28 -07:00
2012-04-08 21:05:19 -07:00
def parse_text_from_bytes ( bytes , debug = True , japanese = False ) :
2012-03-11 23:59:23 -07:00
""" assembles a string based on bytes looked up in the chars table """
2012-04-08 21:05:19 -07:00
return EncodedText . from_bytes ( bytes , debug = debug , japanese = japanese )
2012-04-17 18:22:28 -07:00
2012-03-24 23:09:46 -07:00
def parse_text_at ( address , count = 10 , debug = True ) :
2012-03-11 23:59:23 -07:00
""" returns a list of bytes from an address
see parse_text_at2 for pretty printing """
2012-03-24 23:09:46 -07:00
return parse_text_from_bytes ( rom_interval ( address , count , strings = False ) , debug = debug )
2012-04-17 18:22:28 -07:00
2012-04-08 21:05:19 -07:00
def parse_text_at2 ( address , count = 10 , debug = True , japanese = False ) :
2012-03-23 22:58:14 -07:00
""" returns a string of text from an address
this does not handle text commands """
2012-04-08 21:05:19 -07:00
return EncodedText . parse_text_at ( address , count , debug = debug , japanese = japanese )
2012-03-11 23:59:23 -07:00
2012-04-27 23:34:20 -07:00
def parse_text_at3 ( address , map_group = None , map_id = None , debug = False ) :
deh = script_parse_table [ address ]
if deh :
return deh
else :
2012-05-13 20:48:35 -07:00
text = TextScript ( address , map_group = map_group , map_id = map_id , debug = debug )
if text . is_valid ( ) :
return text
else : return None
2012-04-27 23:34:20 -07:00
2012-03-23 21:36:39 -07:00
def rom_text_at ( address , count = 10 ) :
""" prints out raw text from the ROM
like for 0x112110 """
return " " . join ( [ chr ( x ) for x in rom_interval ( address , count , strings = False ) ] )
2012-03-25 23:47:58 -07:00
def get_map_constant_label ( map_group = None , map_id = None ) :
""" returns PALLET_TOWN for some map group/id pair """
if map_group == None : raise Exception , " need map_group "
if map_id == None : raise Exception , " need map_id "
2012-05-20 01:39:25 -07:00
2012-03-25 23:47:58 -07:00
global map_internal_ids
for ( id , each ) in map_internal_ids . items ( ) :
if each [ " map_group " ] == map_group and each [ " map_id " ] == map_id :
return each [ " label " ]
return None
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def get_map_constant_label_by_id ( global_id ) :
""" returns a map constant label for a particular map id """
global map_internal_ids
return map_internal_ids [ global_id ] [ " label " ]
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def get_id_for_map_constant_label ( label ) :
""" returns some global id for a given map constant label
PALLET_TOWN = 1 , for instance . """
global map_internal_ids
for ( id , each ) in map_internal_ids . items ( ) :
if each [ " label " ] == label : return id
return None
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def generate_map_constant_labels ( ) :
""" generates the global for this script
mapping ids to map groups / ids / labels """
global map_internal_ids
map_internal_ids = { }
i = 0
for map_group in map_names . keys ( ) :
for map_id in map_names [ map_group ] . keys ( ) :
2012-04-02 10:44:23 -07:00
if map_id == " offset " : continue
2012-03-25 23:47:58 -07:00
cmap = map_names [ map_group ] [ map_id ]
name = cmap [ " name " ]
name = name . replace ( " Pokémon Center " , " PokeCenter " ) . \
2012-04-26 12:13:06 -07:00
replace ( " " , " _ " ) . \
replace ( " - " , " _ " ) . \
replace ( " é " , " e " )
2012-03-25 23:47:58 -07:00
constant_label = map_name_cleaner ( name ) . upper ( )
map_internal_ids [ i ] = { " label " : constant_label ,
" map_id " : map_id ,
" map_group " : map_group }
i + = 1
return map_internal_ids
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
#see generate_map_constant_labels() later
def generate_map_constants ( ) :
""" generates content for constants.asm
this will generate two macros : GROUP and MAP """
global map_internal_ids
if map_internal_ids == None or map_internal_ids == { } :
generate_map_constant_labels ( )
globals , groups , maps = " " , " " , " "
for ( id , each ) in map_internal_ids . items ( ) :
2012-04-26 12:13:06 -07:00
label = each [ " label " ] . replace ( " - " , " _ " ) . replace ( " é " , " e " ) . upper ( )
groups + = " GROUP_ " + label + " EQU $ %.2x " % ( each [ " map_group " ] )
2012-03-25 23:47:58 -07:00
groups + = " \n "
2012-04-26 12:13:06 -07:00
maps + = " MAP_ " + label + " EQU $ %.2x " % ( each [ " map_id " ] )
2012-03-25 23:47:58 -07:00
maps + = " \n "
2012-04-26 12:13:06 -07:00
globals + = label + " EQU $ %.2x " % ( id )
2012-03-25 23:47:58 -07:00
globals + = " \n "
#for multi-byte constants:
#print each["label"] + " EQUS \"$%.2x,$%.2x\"" % (each["map_group"], each["map_id"])
print globals
print groups
2012-04-26 12:13:06 -07:00
print maps
2012-03-25 23:47:58 -07:00
2012-05-27 17:45:44 -07:00
def generate_map_constants_dimensions ( ) :
""" Generate _WIDTH and _HEIGHT properties.
"""
global map_internal_ids
output = " "
if map_internal_ids == None or map_internal_ids == { } :
generate_map_constant_labels ( )
for ( id , each ) in map_internal_ids . items ( ) :
map_group = each [ " map_group " ]
map_id = each [ " map_id " ]
label = each [ " label " ] . replace ( " - " , " _ " ) . replace ( " é " , " e " ) . upper ( )
output + = label + " _HEIGHT EQU %d \n " % ( map_names [ map_group ] [ map_id ] [ " header_new " ] . second_map_header . height . byte )
output + = label + " _WIDTH EQU %d \n " % ( map_names [ map_group ] [ map_id ] [ " header_new " ] . second_map_header . width . byte )
return output
2012-04-17 19:37:46 -07:00
from pokemon_constants import pokemon_constants
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def get_pokemon_constant_by_id ( id ) :
2012-04-08 21:47:28 -07:00
if id == 0 : return None
2012-03-25 23:47:58 -07:00
return pokemon_constants [ id ]
2012-04-08 21:47:28 -07:00
def parse_script_asm_at ( * args , * * kwargs ) :
#XXX TODO
return None
2012-04-17 19:37:46 -07:00
from item_constants import item_constants
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def find_item_label_by_id ( id ) :
if id in item_constants . keys ( ) :
2012-04-17 18:22:28 -07:00
return item_constants [ id ]
2012-03-25 23:47:58 -07:00
else : return None
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def generate_item_constants ( ) :
""" make a list of items to put in constants.asm """
2012-03-29 21:41:06 -07:00
output = " "
2012-03-25 23:47:58 -07:00
for ( id , item ) in item_constants . items ( ) :
val = ( " $ %.2x " % id ) . upper ( )
while len ( item ) < 13 : item + = " "
2012-04-27 13:47:51 -07:00
output + = item + " EQU " + val + " \n "
2012-03-29 21:41:06 -07:00
return output
2012-03-25 23:47:58 -07:00
2012-03-24 23:09:46 -07:00
def find_all_text_pointers_in_script_engine_script ( script , bank = None , debug = False ) :
2012-03-13 14:01:52 -07:00
""" returns a list of text pointers
based on each script - engine script command """
#TODO: recursively follow any jumps in the script
if script == None : return [ ]
addresses = set ( )
2012-04-18 21:20:28 -07:00
for ( k , command ) in enumerate ( script . commands ) :
2012-03-24 23:09:46 -07:00
if debug :
print " command is: " + str ( command )
2012-04-18 21:20:28 -07:00
if command . id == 0x4B :
addresses . add ( command . params [ 0 ] . parsed_address )
elif command . id == 0x4C :
addresses . add ( command . params [ 0 ] . parsed_address )
elif command . id == 0x51 :
addresses . add ( command . params [ 0 ] . parsed_address )
elif command . id == 0x53 :
addresses . add ( command . params [ 0 ] . parsed_address )
elif command . id == 0x64 :
addresses . add ( command . params [ 0 ] . parsed_address )
addresses . add ( command . params [ 1 ] . parsed_address )
2012-03-13 14:01:52 -07:00
return addresses
2012-03-11 13:30:14 -07:00
def translate_command_byte ( crystal = None , gold = None ) :
""" takes a command byte from either crystal or gold
returns the command byte in the other ( non - given ) game
The new commands are values 0x52 and 0x9F . This means :
Crystal ' s 0x00– 0x51 correspond to Gold ' s 0x00 – 0x51
Crystal ' s 0x53– 0x9E correspond to Gold ' s 0x52 – 0x9D
Crystal ' s 0xA0– 0xA5 correspond to Gold ' s 0x9E – 0xA3
see : http : / / www . pokecommunity . com / showpost . php ? p = 4347261
"""
2012-03-24 02:43:06 -07:00
if crystal != None : #convert to gold
2012-03-11 13:30:14 -07:00
if crystal < = 0x51 : return crystal
if crystal == 0x52 : return None
if 0x53 < = crystal < = 0x9E : return crystal - 1
if crystal == 0x9F : return None
if 0xA0 < = crystal < = 0xA5 : return crystal - 2
2012-03-24 02:43:06 -07:00
if crystal > 0xA5 : raise Exception , " dunno yet if crystal has new insertions after crystal:0xA5 (gold:0xA3) "
elif gold != None : #convert to crystal
2012-03-11 13:30:14 -07:00
if gold < = 0x51 : return gold
if 0x52 < = gold < = 0x9D : return gold + 1
if 0x9E < = gold < = 0xA3 : return gold + 2
2012-03-24 02:43:06 -07:00
if gold > 0xA3 : raise Exception , " dunno yet if crystal has new insertions after gold:0xA3 (crystal:0xA5) "
else : raise Exception , " translate_command_byte needs either a crystal or gold command "
2012-03-11 13:30:14 -07:00
2012-04-17 19:37:46 -07:00
from pksv import pksv_gs , pksv_crystal , pksv_crystal_unknowns , \
2012-04-17 21:33:23 -07:00
pksv_crystal_more_enders
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class SingleByteParam ( ) :
""" or SingleByte(CommandParam) """
size = 1
should_be_decimal = False
2012-04-26 11:13:24 -07:00
byte_type = " db "
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def __init__ ( self , * args , * * kwargs ) :
2012-03-29 21:41:06 -07:00
for ( key , value ) in kwargs . items ( ) :
2012-03-25 23:47:58 -07:00
setattr ( self , key , value )
#check address
if not hasattr ( self , " address " ) :
2012-03-27 00:01:04 -07:00
raise Exception , " an address is a requirement "
2012-03-25 23:47:58 -07:00
elif self . address == None :
raise Exception , " address must not be None "
elif not is_valid_address ( self . address ) :
raise Exception , " address must be valid "
#check size
if not hasattr ( self , " size " ) or self . size == None :
raise Exception , " size is probably 1? "
#parse bytes from ROM
self . parse ( )
2012-04-17 18:22:28 -07:00
2012-03-29 21:41:06 -07:00
def parse ( self ) : self . byte = ord ( rom [ self . address ] )
2012-04-17 18:22:28 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-27 11:46:57 -07:00
return [ ]
2012-04-20 20:19:01 -07:00
2012-03-25 23:47:58 -07:00
def to_asm ( self ) :
if not self . should_be_decimal : return hex ( self . byte ) . replace ( " 0x " , " $ " )
else : return str ( self . byte )
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class DollarSignByte ( SingleByteParam ) :
def to_asm ( self ) : return hex ( self . byte ) . replace ( " 0x " , " $ " )
2012-04-18 21:20:28 -07:00
HexByte = DollarSignByte
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class ItemLabelByte ( DollarSignByte ) :
def to_asm ( self ) :
label = find_item_label_by_id ( self . byte )
if label : return label
elif not label : return DollarSignByte . to_asm ( self )
2012-04-17 18:22:28 -07:00
2012-03-27 00:01:04 -07:00
class DecimalParam ( SingleByteParam ) :
2012-03-25 23:47:58 -07:00
should_be_decimal = True
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class MultiByteParam ( ) :
""" or MultiByte(CommandParam) """
size = 2
should_be_decimal = False
2012-04-26 11:13:24 -07:00
byte_type = " dw "
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def __init__ ( self , * args , * * kwargs ) :
self . prefix = " $ " #default.. feel free to set 0x in kwargs
2012-03-29 21:41:06 -07:00
for ( key , value ) in kwargs . items ( ) :
2012-03-25 23:47:58 -07:00
setattr ( self , key , value )
#check address
if not hasattr ( self , " address " ) or self . address == None :
2012-03-27 00:01:04 -07:00
raise Exception , " an address is a requirement "
2012-03-25 23:47:58 -07:00
elif not is_valid_address ( self . address ) :
raise Exception , " address must be valid "
#check size
if not hasattr ( self , " size " ) or self . size == None :
raise Exception , " don ' t know how many bytes to read (size) "
self . parse ( )
2012-04-17 18:22:28 -07:00
2012-04-18 21:20:28 -07:00
def parse ( self ) :
self . bytes = rom_interval ( self . address , self . size , strings = False )
2012-04-21 00:25:56 -07:00
self . parsed_number = self . bytes [ 0 ] + ( self . bytes [ 1 ] << 8 )
2012-04-18 21:20:28 -07:00
if hasattr ( self , " bank " ) :
self . parsed_address = calculate_pointer_from_bytes_at ( self . address , bank = self . bank )
else :
self . parsed_address = calculate_pointer_from_bytes_at ( self . address , bank = None )
2012-04-17 18:22:28 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-20 16:26:52 -07:00
return [ ]
2012-03-25 23:47:58 -07:00
#you won't actually use this to_asm because it's too generic
#def to_asm(self): return ", ".join([(self.prefix+"%.2x")%x for x in self.bytes])
def to_asm ( self ) :
if not self . should_be_decimal :
return self . prefix + " " . join ( [ ( " %.2x " ) % x for x in reversed ( self . bytes ) ] )
elif self . should_be_decimal :
decimal = int ( " 0x " + " " . join ( [ ( " %.2x " ) % x for x in reversed ( self . bytes ) ] ) , 16 )
return str ( decimal )
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class PointerLabelParam ( MultiByteParam ) :
#default size is 2 bytes
default_size = 2
size = 2
#default is to not parse out a bank
bank = False
2012-04-14 23:18:25 -07:00
force = False
2012-04-21 01:15:56 -07:00
debug = False
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def __init__ ( self , * args , * * kwargs ) :
2012-04-27 11:41:44 -07:00
self . dependencies = None
2012-03-25 23:47:58 -07:00
#bank can be overriden
if " bank " in kwargs . keys ( ) :
2012-04-14 10:23:53 -07:00
if kwargs [ " bank " ] != False and kwargs [ " bank " ] != None and kwargs [ " bank " ] in [ True , " reverse " ] :
2012-03-25 23:47:58 -07:00
#not +=1 because child classes set size=3 already
self . size = self . default_size + 1
2012-04-26 14:50:52 -07:00
self . given_bank = kwargs [ " bank " ]
2012-04-14 10:23:53 -07:00
#if kwargs["bank"] not in [None, False, True, "reverse"]:
# raise Exception, "bank cannot be: " + str(kwargs["bank"])
2012-03-25 23:47:58 -07:00
if self . size > 3 :
raise Exception , " param size is too large "
#continue instantiation.. self.bank will be set down the road
MultiByteParam . __init__ ( self , * args , * * kwargs )
2012-04-17 18:22:28 -07:00
2012-04-18 21:20:28 -07:00
def parse ( self ) :
self . parsed_address = calculate_pointer_from_bytes_at ( self . address , bank = self . bank )
MultiByteParam . parse ( self )
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-20 20:19:01 -07:00
dependencies = [ ]
2012-04-22 13:48:47 -07:00
if self . parsed_address == self . address :
return dependencies
2012-04-27 11:41:44 -07:00
if self . dependencies != None and not recompute :
2012-04-27 12:58:57 -07:00
global_dependencies . update ( self . dependencies )
2012-04-27 11:41:44 -07:00
return self . dependencies
2012-04-20 20:19:01 -07:00
thing = script_parse_table [ self . parsed_address ]
2012-04-22 13:48:47 -07:00
if thing and thing . address == self . parsed_address and not ( thing is self ) :
2012-04-27 23:34:20 -07:00
#if self.debug:
# print "parsed address is: " + hex(self.parsed_address) + " with label: " + thing.label.name + " of type: " + str(thing.__class__)
2012-04-20 20:19:01 -07:00
dependencies . append ( thing )
2012-04-27 12:58:57 -07:00
if not thing in global_dependencies :
global_dependencies . add ( thing )
more = thing . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies )
dependencies . extend ( more )
2012-04-27 11:41:44 -07:00
self . dependencies = dependencies
2012-04-20 16:26:52 -07:00
return dependencies
2012-03-25 23:47:58 -07:00
def to_asm ( self ) :
bank = self . bank
#we pass bank= for whether or not to include a bank byte when reading
#.. it's not related to caddress
2012-05-15 20:15:08 -07:00
caddress = None
if not ( hasattr ( self , " parsed_address " ) and self . parsed_address != None ) :
caddress = calculate_pointer_from_bytes_at ( self . address , bank = self . bank )
else :
caddress = self . parsed_address
2012-03-25 23:47:58 -07:00
label = get_label_for ( caddress )
pointer_part = label #use the label, if it is found
2012-04-17 18:22:28 -07:00
2012-04-14 08:54:48 -07:00
#check that the label actually points to the right place
result = script_parse_table [ caddress ]
if result != None and hasattr ( result , " label " ) :
2012-04-26 16:41:13 -07:00
if result . label . name != label :
2012-04-14 08:54:48 -07:00
label = None
elif result . address != caddress :
label = None
elif result != None :
label = None
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
#setup output bytes if the label was not found
if not label :
#pointer_part = (", ".join([(self.prefix+"%.2x")%x for x in reversed(self.bytes[1:])]))
2012-03-29 23:20:50 -07:00
pointer_part = self . prefix + ( " %.2x " % self . bytes [ 1 ] ) + ( " %.2x " % self . bytes [ 0 ] )
2012-04-27 15:16:45 -07:00
2012-03-25 23:47:58 -07:00
#bank positioning matters!
if bank == True or bank == " reverse " : #bank, pointer
#possibly use BANK(LABEL) if we know the bank
if not label :
bank_part = ( ( self . prefix + " %.2x " ) % bank )
else :
2012-05-16 16:33:24 -07:00
if " $ " in label :
if 0x4000 < = caddress < = 0x7FFF :
#bank_part = "$%.2x" % (calculate_bank(self.parent.parent.address))
bank_part = " 1 "
else :
bank_part = " $ %.2x " % ( calculate_bank ( caddress ) )
else :
bank_part = " BANK( " + label + " ) "
2012-03-25 23:47:58 -07:00
#return the asm based on the order the bytes were specified to be in
if bank == " reverse " : #pointer, bank
return pointer_part + " , " + bank_part
elif bank == True : #bank, pointer
return bank_part + " , " + pointer_part
else : raise Exception , " this should never happen "
raise Exception , " this should never happen "
#this next one will either return the label or the raw bytes
elif bank == False or bank == None : #pointer
return pointer_part #this could be the same as label
else :
2012-04-15 19:47:12 -07:00
#raise Exception, "this should never happen"
return pointer_part #probably in the same bank ?
2012-03-25 23:47:58 -07:00
raise Exception , " this should never happen "
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class PointerLabelBeforeBank ( PointerLabelParam ) :
bank = True #bank appears first, see calculate_pointer_from_bytes_at
size = 3
2012-05-16 16:33:24 -07:00
byte_type = " dw "
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class PointerLabelAfterBank ( PointerLabelParam ) :
bank = " reverse " #bank appears last, see calculate_pointer_from_bytes_at
size = 3
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class ScriptPointerLabelParam ( PointerLabelParam ) : pass
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class ScriptPointerLabelBeforeBank ( PointerLabelBeforeBank ) : pass
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class ScriptPointerLabelAfterBank ( PointerLabelAfterBank ) : pass
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def _parse_script_pointer_bytes ( self ) :
PointerLabelParam . parse ( self )
2012-04-08 11:14:31 -07:00
print " _parse_script_pointer_bytes - calculating the pointer located at " + hex ( self . address )
2012-03-25 23:47:58 -07:00
address = calculate_pointer_from_bytes_at ( self . address , bank = self . bank )
2012-04-14 13:48:26 -07:00
if address != None and address > 0x4000 :
print " _parse_script_pointer_bytes - the pointer is: " + hex ( address )
self . script = parse_script_engine_script_at ( address , debug = self . debug , force = self . force , map_group = self . map_group , map_id = self . map_id )
2012-03-27 00:01:04 -07:00
ScriptPointerLabelParam . parse = _parse_script_pointer_bytes
2012-03-25 23:47:58 -07:00
ScriptPointerLabelBeforeBank . parse = _parse_script_pointer_bytes
ScriptPointerLabelAfterBank . parse = _parse_script_pointer_bytes
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class PointerLabelToScriptPointer ( PointerLabelParam ) :
def parse ( self ) :
PointerLabelParam . parse ( self )
address = calculate_pointer_from_bytes_at ( self . address , bank = self . bank )
address2 = calculate_pointer_from_bytes_at ( address , bank = " reverse " ) #maybe not "reverse"?
2012-04-08 10:19:03 -07:00
self . script = parse_script_engine_script_at ( address2 , origin = False , map_group = self . map_group , map_id = self . map_id , force = self . force , debug = self . debug )
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class AsmPointerParam ( PointerLabelBeforeBank ) :
def parse ( self ) :
PointerLabelBeforeBank . parse ( self )
address = calculate_pointer_from_bytes_at ( self . address , bank = self . bank ) #3-byte pointer
2012-04-08 10:19:03 -07:00
self . asm = parse_script_asm_at ( address , map_group = self . map_group , map_id = self . map_id , force = self . force , debug = self . debug ) #might end in some specific way?
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class PointerToAsmPointerParam ( PointerLabelParam ) :
def parse ( self ) :
PointerLabelParam . parse ( self )
address = calculate_pointer_from_bytes_at ( self . address , bank = self . bank ) #2-byte pointer
address2 = calculate_pointer_from_bytes_at ( address , bank = " reverse " ) #maybe not "reverse"?
2012-04-08 10:19:03 -07:00
self . asm = parse_script_asm_at ( address , map_group = self . map_group , map_id = self . map_id , force = self . force , debug = self . debug ) #might end in some specific way?
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class RAMAddressParam ( MultiByteParam ) :
def to_asm ( self ) :
address = calculate_pointer_from_bytes_at ( self . address , bank = False )
label = get_ram_label ( address )
2012-04-27 13:39:41 -07:00
if label : return label
2012-04-27 14:04:16 -07:00
else : return " $ " + " " . join ( [ " %.2x " % x for x in reversed ( self . bytes ) ] ) + " "
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class MoneyByteParam ( MultiByteParam ) :
size = 3
max_value = 0x0F423F
should_be_decimal = True
2012-04-27 14:33:37 -07:00
def parse ( self ) :
MultiByteParam . parse ( self )
# in the rom as xxyyzz
2012-04-27 14:48:25 -07:00
self . x = self . bytes [ 2 ]
2012-04-27 14:33:37 -07:00
self . y = self . bytes [ 1 ]
2012-04-27 14:48:25 -07:00
self . z = self . bytes [ 0 ]
2012-04-27 14:33:37 -07:00
def to_asm ( self ) :
2012-04-27 14:48:25 -07:00
return str ( self . x + ( self . y << 8 ) + ( self . z << 16 ) )
2012-04-27 14:33:37 -07:00
2012-05-20 01:39:25 -07:00
#this is used by the preprocessor
2012-04-27 14:33:37 -07:00
@staticmethod
def from_asm ( value ) :
#max is 0F423F
#z = 0x0F ; y = 0x42 ; x = 0x3F
#999999 = x + (y << 8) + (z << 16)
2012-05-20 01:39:25 -07:00
2012-04-27 14:33:37 -07:00
value = int ( value )
x = ( value & 0x0000FF )
y = ( value & 0x00FF00 ) >> 8
z = ( value & 0xFF0000 ) >> 16
2012-04-17 18:22:28 -07:00
2012-05-16 10:07:20 -07:00
return str ( z ) + " \n db " + str ( y ) + " \n db " + str ( x )
2012-04-17 18:22:28 -07:00
2012-05-16 12:24:18 -07:00
def read_money ( address , dohex = False ) :
z = ord ( rom [ address ] )
y = ord ( rom [ address + 1 ] )
x = ord ( rom [ address + 2 ] )
answer = x + ( y << 8 ) + ( z << 16 )
if not dohex :
return answer
else :
return hex ( answer )
def write_money ( money ) :
value = money
x = ( value & 0x0000FF )
y = ( value & 0x00FF00 ) >> 8
z = ( value & 0xFF0000 ) >> 16
return " db " + str ( z ) + " \n db " + str ( y ) + " \n db " + str ( x )
2012-03-25 23:47:58 -07:00
class CoinByteParam ( MultiByteParam ) :
size = 2
max_value = 0x270F
should_be_decimal = True
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class MapGroupParam ( SingleByteParam ) :
def to_asm ( self ) :
map_id = ord ( rom [ self . address + 1 ] )
map_constant_label = get_map_constant_label ( map_id = map_id , map_group = self . byte ) #like PALLET_TOWN
if map_constant_label == None : return str ( self . byte )
#else: return "GROUP("+map_constant_label+")"
else : return " GROUP_ " + map_constant_label
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class MapIdParam ( SingleByteParam ) :
def parse ( self ) :
SingleByteParam . parse ( self )
self . map_group = ord ( rom [ self . address - 1 ] )
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def to_asm ( self ) :
map_group = ord ( rom [ self . address - 1 ] )
map_constant_label = get_map_constant_label ( map_id = self . byte , map_group = map_group )
if map_constant_label == None : return str ( self . byte )
#else: return "MAP("+map_constant_label+")"
else : return " MAP_ " + map_constant_label
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class MapGroupIdParam ( MultiByteParam ) :
def parse ( self ) :
MultiByteParam . parse ( self )
self . map_group = self . bytes [ 0 ]
self . map_id = self . bytes [ 1 ]
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
def to_asm ( self ) :
map_group = self . map_group
map_id = self . map_id
label = get_map_constant_label ( map_group = map_group , map_id = map_id )
return label
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class PokemonParam ( SingleByteParam ) :
def to_asm ( self ) :
pokemon_constant = get_pokemon_constant_by_id ( self . byte )
if pokemon_constant : return pokemon_constant
else : return str ( self . byte )
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class PointerParamToItemAndLetter ( MultiByteParam ) :
#[2F][2byte pointer to item no + 0x20 bytes letter text]
2012-03-27 00:01:04 -07:00
#raise NotImplementedError, bryan_message
pass
2012-04-17 18:22:28 -07:00
2012-03-25 23:47:58 -07:00
class TrainerIdParam ( SingleByteParam ) :
2012-05-20 01:02:35 -07:00
def to_asm ( self ) :
# find the group id by first finding the param type id
i = 0
foundit = None
for ( k , v ) in self . parent . param_types . items ( ) :
if v [ " class " ] == TrainerGroupParam :
foundit = i
break
i + = 1
2012-04-17 18:22:28 -07:00
2012-05-20 01:02:35 -07:00
if foundit == None :
raise Exception , " didn ' t find a TrainerGroupParam in this command?? "
# now get the trainer group id
trainer_group_id = self . parent . params [ foundit ] . byte
# check the rule to see whether to use an id or not
2012-05-22 09:44:42 -07:00
if ( " uses_numeric_trainer_ids " in trainer_group_names [ trainer_group_id ] . keys ( ) ) or \
( not " trainer_names " in trainer_group_names [ trainer_group_id ] . keys ( ) ) :
2012-05-20 01:02:35 -07:00
return str ( self . byte )
else :
return trainer_group_names [ trainer_group_id ] [ " trainer_names " ] [ self . byte - 1 ]
2012-04-17 18:22:28 -07:00
2012-05-20 01:02:35 -07:00
class TrainerGroupParam ( SingleByteParam ) :
def to_asm ( self ) :
trainer_group_id = self . byte
return trainer_group_names [ trainer_group_id ] [ " constant " ]
2012-04-17 18:22:28 -07:00
2012-05-20 01:10:40 -07:00
class MoveParam ( SingleByteParam ) :
def to_asm ( self ) :
2012-05-20 01:15:36 -07:00
if self . byte in moves . keys ( ) :
return moves [ self . byte ]
else :
# this happens for move=0 (no move) in trainer headers
return str ( self . byte )
2012-05-20 01:10:40 -07:00
2012-03-27 00:01:04 -07:00
class MenuDataPointerParam ( PointerLabelParam ) :
#read menu data at the target site
#raise NotImplementedError, bryan_message
pass
2012-04-17 18:22:28 -07:00
2012-05-22 09:44:42 -07:00
string_to_text_texts = [ ]
2012-03-27 00:01:04 -07:00
class RawTextPointerLabelParam ( PointerLabelParam ) :
2012-04-08 11:35:35 -07:00
#not sure if these are always to a text script or raw text?
2012-04-26 14:50:52 -07:00
def parse ( self ) :
PointerLabelParam . parse ( self )
#bank = calculate_bank(self.address)
address = calculate_pointer_from_bytes_at ( self . address , bank = False )
self . calculated_address = address
2012-04-27 23:34:20 -07:00
#self.text = parse_text_at3(address, map_group=self.map_group, map_id=self.map_id, debug=self.debug)
#self.text = TextScript(address, map_group=self.map_group, map_id=self.map_id, debug=self.debug)
self . text = parse_text_engine_script_at ( address , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
2012-04-17 18:22:28 -07:00
2012-05-22 09:50:25 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
global_dependencies . add ( self . text )
return [ self . text ]
class EncodedTextLabelParam ( PointerLabelParam ) :
def parse ( self ) :
PointerLabelParam . parse ( self )
address = calculate_pointer_from_bytes_at ( self . address , bank = False )
self . parsed_address = address
self . text = EncodedText ( address , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
if isinstance ( self . text , EncodedText ) :
string_to_text_texts . append ( self . text )
2012-05-22 09:44:42 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
global_dependencies . add ( self . text )
2012-04-26 14:50:52 -07:00
return [ self . text ]
2012-04-17 18:22:28 -07:00
2012-03-27 00:01:04 -07:00
class TextPointerLabelParam ( PointerLabelParam ) :
2012-04-08 11:35:35 -07:00
""" this is a pointer to a text script """
bank = False
2012-04-27 15:38:49 -07:00
text = None
2012-04-08 11:35:35 -07:00
def parse ( self ) :
PointerLabelParam . parse ( self )
address = calculate_pointer_from_bytes_at ( self . address , bank = self . bank )
2012-04-08 14:09:00 -07:00
if address != None and address != 0 :
self . text = parse_text_engine_script_at ( address , map_group = self . map_group , map_id = self . map_id , force = self . force , debug = self . debug )
2012-04-27 16:50:13 -07:00
if not self . text :
self . text = script_parse_table [ address ]
2012-05-20 01:39:25 -07:00
2012-04-27 15:35:06 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-27 15:38:49 -07:00
if self . text :
global_dependencies . add ( self . text )
return [ self . text ]
else :
return [ ]
2012-04-17 18:22:28 -07:00
2012-05-04 18:47:43 -07:00
class TextPointerLabelAfterBankParam ( PointerLabelAfterBank ) :
text = None
def parse ( self ) :
PointerLabelAfterBank . parse ( self )
address = calculate_pointer_from_bytes_at ( self . address , bank = self . bank )
if address != None and address != 0 :
self . text = parse_text_engine_script_at ( address , map_group = self . map_group , map_id = self . map_id , force = self . force , debug = self . debug )
if not self . text :
self . text = script_parse_table [ address ]
2012-05-20 01:39:25 -07:00
2012-05-04 18:47:43 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
if self . text :
global_dependencies . add ( self . text )
return [ self . text ]
else :
return [ ]
2012-03-27 00:01:04 -07:00
class MovementPointerLabelParam ( PointerLabelParam ) :
2012-05-04 23:38:14 -07:00
def parse ( self ) :
PointerLabelParam . parse ( self )
2012-05-16 16:33:24 -07:00
if is_script_already_parsed_at ( self . parsed_address ) :
self . movement = script_parse_table [ self . parsed_address ]
else :
self . movement = ApplyMovementData ( self . parsed_address , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
2012-04-17 18:22:28 -07:00
2012-05-04 23:38:14 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
if hasattr ( self , " movement " ) and self . movement :
global_dependencies . add ( self . movement )
2012-05-13 20:48:35 -07:00
return [ self . movement ] + self . movement . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies )
2012-05-04 23:38:14 -07:00
else :
raise Exception , " MovementPointerLabelParam hasn ' t been parsed yet "
2012-04-17 18:22:28 -07:00
2012-03-27 00:01:04 -07:00
class MapDataPointerParam ( PointerLabelParam ) :
pass
2012-03-25 23:47:58 -07:00
2012-04-14 16:23:55 -07:00
class Command :
"""
Note : when dumping to asm , anything in script_parse_table that directly
inherits Command should not be . to_asm ( ) ' d.
"""
#use this when the "byte id" doesn't matter
#.. for example, a non-script command doesn't use the "byte id"
2012-04-14 11:01:20 -07:00
override_byte_check = False
2012-04-26 16:41:13 -07:00
base_label = " UnseenLabel_ "
2012-04-17 18:22:28 -07:00
2012-04-02 20:34:02 -07:00
def __init__ ( self , address = None , * pargs , * * kwargs ) :
2012-04-08 10:19:03 -07:00
""" params:
address - where the command starts
force - whether or not to force the script to be parsed ( default False )
debug - are we in debug mode ? default False
map_group
map_id
"""
defaults = { " force " : False , " debug " : False , " map_group " : None , " map_id " : None }
2012-03-27 00:01:04 -07:00
if not is_valid_address ( address ) :
raise Exception , " address is invalid "
2012-04-08 10:19:03 -07:00
#set up some variables
2012-03-27 00:01:04 -07:00
self . address = address
2012-04-02 20:34:02 -07:00
self . last_address = None
2012-04-21 00:16:12 -07:00
#setup the label based on base_label if available
2012-04-26 16:41:13 -07:00
label = self . base_label + hex ( self . address )
self . label = Label ( name = label , address = address , object = self )
2012-04-08 10:19:03 -07:00
#params are where this command's byte parameters are stored
2012-04-02 20:34:02 -07:00
self . params = { }
2012-04-27 11:41:44 -07:00
self . dependencies = None
2012-04-08 10:19:03 -07:00
#override default settings
defaults . update ( kwargs )
#set everything
for ( key , value ) in defaults . items ( ) :
setattr ( self , key , value )
#but also store these kwargs
self . args = defaults
#start parsing this command's parameter bytes
2012-04-02 20:34:02 -07:00
self . parse ( )
2012-04-17 18:22:28 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-20 16:26:52 -07:00
dependencies = [ ]
2012-05-13 20:48:35 -07:00
#if self.dependencies != None and not recompute:
# global_dependencies.update(self.dependencies)
# return self.dependencies
2012-04-20 16:26:52 -07:00
for ( key , param ) in self . params . items ( ) :
2012-04-22 13:48:47 -07:00
if hasattr ( param , " get_dependencies " ) and param != self :
2012-04-27 12:58:57 -07:00
deps = param . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies )
2012-04-22 13:48:47 -07:00
if deps != None and not self in deps :
2012-04-21 16:09:03 -07:00
dependencies . extend ( deps )
2012-04-27 11:41:44 -07:00
self . dependencies = dependencies
2012-04-20 16:26:52 -07:00
return dependencies
2012-03-27 00:01:04 -07:00
def to_asm ( self ) :
#start with the rgbasm macro name for this command
2012-04-25 18:02:40 -07:00
output = " "
2012-04-26 12:37:09 -07:00
#if len(self.macro_name) > 0 and self.macro_name[0].isdigit():
# output += "_"
2012-04-25 18:02:40 -07:00
output + = self . macro_name
2012-03-27 00:01:04 -07:00
#return if there are no params
if len ( self . param_types . keys ( ) ) == 0 : return output
#first one will have no prefixing comma
first = True
#start reading the bytes after the command byte
2012-04-14 11:01:20 -07:00
if not self . override_byte_check :
current_address = self . address + 1
else :
current_address = self . address
2012-04-14 11:45:05 -07:00
#output = self.macro_name + ", ".join([param.to_asm() for (key, param) in self.params.items()])
2012-03-27 00:01:04 -07:00
#add each param
2012-04-02 20:34:02 -07:00
for ( key , param ) in self . params . items ( ) :
2012-03-27 00:01:04 -07:00
name = param . name
#the first param shouldn't have ", " prefixed
2012-04-02 20:34:02 -07:00
if first :
output + = " "
first = False
2012-03-27 00:01:04 -07:00
#but all other params should
else : output + = " , "
#now add the asm-compatible param string
2012-04-02 20:34:02 -07:00
output + = param . to_asm ( )
current_address + = param . size
2012-03-27 00:01:04 -07:00
#for param_type in self.param_types:
# name = param_type["name"]
# klass = param_type["klass"]
# #create an instance of this type
# #tell it to begin parsing at this latest byte
# obj = klass(address=current_address)
# #the first param shouldn't have ", " prefixed
# if first: first = False
# #but all other params should
# else: output += ", "
# #now add the asm-compatible param string
# output += obj.to_asm()
# current_address += obj.size
return output
2012-04-17 18:22:28 -07:00
2012-03-27 00:01:04 -07:00
def parse ( self ) :
#id, size (inclusive), param_types
#param_type = {"name": each[1], "class": each[0]}
2012-04-14 11:01:20 -07:00
if not self . override_byte_check :
current_address = self . address + 1
else :
current_address = self . address
2012-04-02 20:34:02 -07:00
byte = ord ( rom [ self . address ] )
2012-04-14 13:48:26 -07:00
if not self . override_byte_check and ( not byte == self . id ) :
2012-04-02 20:34:02 -07:00
raise Exception , " byte ( " + hex ( byte ) + " ) != self.id ( " + hex ( self . id ) + " ) "
2012-03-27 00:01:04 -07:00
i = 0
for ( key , param_type ) in self . param_types . items ( ) :
name = param_type [ " name " ]
klass = param_type [ " class " ]
#make an instance of this class, like SingleByteParam()
#or ItemLabelByte.. by making an instance, obj.parse() is called
2012-05-16 16:33:24 -07:00
obj = klass ( address = current_address , name = name , parent = self , * * dict ( [ ( k , v ) for ( k , v ) in self . args . items ( ) if k not in [ " parent " ] ] ) )
2012-03-27 00:01:04 -07:00
#save this for later
self . params [ i ] = obj
#increment our counters
current_address + = obj . size
i + = 1
2012-04-02 20:34:02 -07:00
self . last_address = current_address
2012-03-27 00:01:04 -07:00
return True
2012-04-17 18:22:28 -07:00
2012-03-27 00:01:04 -07:00
class GivePoke ( Command ) :
id = 0x2D
2012-04-08 14:38:34 -07:00
macro_name = " givepoke "
2012-04-08 19:15:50 -07:00
size = 4 #minimum
2012-04-08 14:38:34 -07:00
end = False
2012-03-27 00:01:04 -07:00
param_types = {
0 : { " name " : " pokemon " , " class " : PokemonParam } ,
1 : { " name " : " level " , " class " : SingleByteParam } ,
2 : { " name " : " item " , " class " : ItemLabelByte } ,
3 : { " name " : " trainer " , " class " : SingleByteParam } ,
4 : { " name " : " trainer_name_pointer " , " class " : MultiByteParam } , #should probably use TextLabelParam
5 : { " name " : " pkmn_nickname " , " class " : MultiByteParam } , #XXX TextLabelParam ?
}
2012-05-16 10:44:55 -07:00
allowed_lengths = [ 4 , 6 ]
2012-03-27 00:01:04 -07:00
def parse ( self ) :
self . params = { }
2012-04-08 14:38:34 -07:00
byte = ord ( rom [ self . address ] )
2012-03-27 00:01:04 -07:00
if not byte == self . id :
raise Exception , " this should never happen "
current_address = self . address + 1
i = 0
2012-04-08 21:47:28 -07:00
self . size = 1
2012-03-27 00:01:04 -07:00
for ( key , param_type ) in self . param_types . items ( ) :
#stop executing after the 4th byte unless it == 0x1
2012-04-08 19:15:50 -07:00
if i == 4 : print " self.params[3].byte is: " + str ( self . params [ 3 ] . byte )
2012-04-08 14:38:34 -07:00
if i == 4 and self . params [ 3 ] . byte != 1 : break
2012-03-27 00:01:04 -07:00
name = param_type [ " name " ]
klass = param_type [ " class " ]
#make an instance of this class, like SingleByteParam()
#or ItemLabelByte.. by making an instance, obj.parse() is called
obj = klass ( address = current_address , name = name )
#save this for later
self . params [ i ] = obj
#increment our counters
current_address + = obj . size
2012-04-08 19:15:50 -07:00
self . size + = obj . size
2012-03-27 00:01:04 -07:00
i + = 1
2012-04-02 20:34:02 -07:00
self . last_address = current_address
2012-03-27 00:01:04 -07:00
return True
2012-04-26 12:00:55 -07:00
class DataByteWordMacro ( Command ) :
""" Only used by the preprocessor.
"""
id = None
macro_name = " dbw "
size = 3
override_byte_check = True
param_types = {
0 : { " name " : " db value " , " class " : DecimalParam } ,
1 : { " name " : " dw value " , " class " : PointerLabelParam } ,
}
def __init__ ( self ) : pass
def parse ( self ) : pass
def to_asm ( self ) : pass
2012-04-17 18:22:28 -07:00
2012-05-04 23:38:14 -07:00
class MovementCommand ( Command ) :
# the vast majority of movement commands do not end the movement script
end = False
# this is only used for e.g. macros that don't appear as a byte in the ROM
# don't use the override because all movements are specified with a byte
override_byte_check = False
# most commands have size=1 but one or two have a single parameter (gasp)
size = 1
param_types = { }
params = [ ]
# most movement commands won't have any dependencies
# get_dependencies on Command will look at the values of params
# so this doesn't need to be specified by MovementCommand as long as it extends Command
#def get_dependencies(self, recompute=False, global_dependencies=set()):
# return []
def parse ( self ) :
2012-05-13 19:53:48 -07:00
if ord ( rom [ self . address ] ) < 0x45 :
# this is mostly handled in to_asm
pass
else :
Command . parse ( self )
2012-05-04 23:38:14 -07:00
def to_asm ( self ) :
2012-05-13 19:53:48 -07:00
#return "db $%.2x"%(self.byte)
return Command . to_asm ( self )
2012-05-04 23:38:14 -07:00
2012-05-13 19:53:48 -07:00
class MovementDBCommand ( Command ) :
end = False
macro_name = " db "
override_byte_check = True
id = None
byte = None
size = 1
param_types = {
0 : { " name " : " db value " , " class " : SingleByteParam } ,
}
params = [ ]
2012-05-20 01:39:25 -07:00
2012-05-13 19:53:48 -07:00
def to_asm ( self ) :
asm = Command . to_asm ( self )
return asm + " ; movement "
2012-05-05 00:01:01 -07:00
2012-05-13 16:53:11 -07:00
# down, up, left, right
2012-05-12 00:18:27 -07:00
movement_command_bases = {
0x00 : " turn_head " ,
0x04 : " half_step " ,
0x08 : " slow_step " , #small_step?
0x0C : " step " ,
0x10 : " big_step " , #fast_step?
0x14 : " slow_slide_step " ,
0x18 : " slide_step " ,
0x1C : " fast_slide_step " ,
0x20 : " turn_away " ,
0x24 : " turn_in " , #towards?
0x28 : " turn_waterfall " , #what??
0x2C : " slow_jump_step " ,
0x30 : " jump_step " ,
0x34 : " fast_jump_step " ,
# tauwasser says the pattern stops at $45 but $38 looks more realistic?
0x3A : " remove_fixed_facing " ,
0x3B : " fix_facing " ,
0x3D : " hide_person " ,
0x45 : " accelerate_last " ,
0x46 : [ " step_sleep " , [ " duration " , DecimalParam ] ] ,
0x47 : " step_end " ,
0x49 : " hide_person " ,
# do these next two have any params ??
0x4C : " teleport_from " ,
0x4D : " teleport_to " ,
2012-05-20 01:39:25 -07:00
2012-05-12 00:18:27 -07:00
0x4E : " skyfall " ,
0x4F : " step_wait5 " ,
0x55 : [ " step_shake " , [ " displacement " , DecimalParam ] ] ,
}
# create MovementCommands from movement_command_bases
2012-05-12 12:15:42 -07:00
def create_movement_commands ( debug = False ) :
2012-05-12 00:18:27 -07:00
""" Creates MovementCommands from movement_command_bases.
This is just a cheap trick instead of manually defining
all of those classes .
"""
#movement_command_classes = inspect.getmembers(sys.modules[__name__], \
# lambda obj: inspect.isclass(obj) and \
# issubclass(obj, MovementCommand) and \
# not (obj is MovementCommand))
2012-05-13 16:53:11 -07:00
movement_command_classes2 = [ ]
2012-05-12 00:18:27 -07:00
for ( byte , cmd ) in movement_command_bases . items ( ) :
if type ( cmd ) == str :
cmd = [ cmd ]
cmd_name = cmd [ 0 ] . replace ( " " , " _ " )
params = { " id " : byte , " size " : 1 , " end " : byte is 0x47 , " macro_name " : cmd_name }
params [ " param_types " ] = { }
if len ( cmd ) > 1 :
param_types = cmd [ 1 : ]
for ( i , each ) in enumerate ( param_types ) :
thing = { " name " : each [ 0 ] , " class " : each [ 1 ] }
params [ " param_types " ] [ i ] = thing
if debug :
print " each is: " + str ( each )
print " thing[class] is: " + str ( thing [ " class " ] )
params [ " size " ] + = thing [ " class " ] . size
2012-05-20 01:39:25 -07:00
2012-05-13 16:53:11 -07:00
if byte < = 0x34 :
for x in range ( 0 , 4 ) :
2012-05-20 01:39:25 -07:00
2012-05-13 16:53:11 -07:00
direction = None
if x == 0 :
direction = " down "
elif x == 1 :
direction = " up "
elif x == 2 :
direction = " left "
elif x == 3 :
direction = " right "
else : raise Exception , " this should never happen "
cmd_name = cmd [ 0 ] . replace ( " " , " _ " ) + " _ " + direction
klass_name = cmd_name + " Command "
params [ " id " ] = copy ( byte )
params [ " macro_name " ] = cmd_name
klass = classobj ( copy ( klass_name ) , ( MovementCommand , ) , deepcopy ( params ) )
globals ( ) [ klass_name ] = klass
movement_command_classes2 . append ( klass )
byte + = 1
del cmd_name
del params
del klass_name
else :
klass_name = cmd_name + " Command "
klass = classobj ( klass_name , ( MovementCommand , ) , params )
globals ( ) [ klass_name ] = klass
movement_command_classes2 . append ( klass )
2012-05-12 00:18:27 -07:00
#later an individual klass will be instantiated to handle something
2012-05-13 16:53:11 -07:00
return movement_command_classes2
2012-05-12 00:18:27 -07:00
movement_command_classes = create_movement_commands ( )
2012-05-04 23:38:14 -07:00
2012-05-05 00:29:41 -07:00
all_movements = [ ]
2012-05-04 23:38:14 -07:00
class ApplyMovementData :
base_label = " MovementData_ "
2012-05-20 01:39:25 -07:00
2012-05-05 00:29:41 -07:00
def __init__ ( self , address , map_group = None , map_id = None , debug = False , label = None , force = False ) :
2012-05-04 23:38:14 -07:00
self . address = address
self . map_group = map_group
self . map_id = map_id
self . debug = debug
2012-05-05 00:29:41 -07:00
self . force = force
2012-05-20 01:39:25 -07:00
2012-05-04 23:38:14 -07:00
if not label :
label = self . base_label + hex ( address )
self . label = Label ( name = label , address = address , object = self )
2012-05-12 00:18:27 -07:00
self . dependencies = [ ]
self . commands = [ ]
2012-05-04 23:38:14 -07:00
self . parse ( )
2012-05-20 01:39:25 -07:00
2012-05-04 23:38:14 -07:00
# this is almost an exact copy of Script.parse
# with the exception of using text_command_classes instead of command_classes
def parse ( self ) :
2012-05-11 22:59:15 -07:00
global movement_command_classes , script_parse_table
2012-05-05 00:29:41 -07:00
address = self . address
2012-05-04 23:38:14 -07:00
# i feel like checking myself
assert is_valid_address ( address ) , " ApplyMovementData.parse must be given a valid address "
2012-05-20 01:39:25 -07:00
2012-05-04 23:38:14 -07:00
current_address = copy ( self . address )
start_address = copy ( current_address )
2012-05-20 01:39:25 -07:00
2012-05-04 23:38:14 -07:00
# don't clutter up my screen
if self . debug :
print " ApplyMovementData.parse address= " + hex ( self . address ) + " map_group= " + str ( self . map_group ) + " map_id= " + str ( self . map_id )
# load up the rom if it hasn't been loaded already
load_rom ( )
# in the event that the script parsing fails.. it would be nice to leave evidence
script_parse_table [ start_address : start_address + 1 ] = " incomplete ApplyMovementData.parse "
# start with a blank script
commands = [ ]
# use this to control the while loop
end = False
2012-05-20 01:39:25 -07:00
2012-05-04 23:38:14 -07:00
# for each command found...
while not end :
# get the current scripting byte
cur_byte = ord ( rom [ current_address ] )
# reset the command class (last command was probably different)
scripting_command_class = None
# match the command id byte to a scripting command class like "step half"
for class_ in movement_command_classes :
# allow lists of ids
2012-05-13 16:53:11 -07:00
if ( type ( class_ . id ) == list and cur_byte in class_ . id ) \
or class_ . id == cur_byte :
scripting_command_class = class_
2012-05-20 01:39:25 -07:00
2012-05-05 00:29:41 -07:00
# temporary fix for applymovement scripts
if ord ( rom [ current_address ] ) == 0x47 :
end = True
2012-05-20 01:39:25 -07:00
2012-05-04 23:38:14 -07:00
# no matching command found
2012-05-13 19:32:28 -07:00
xyz = None
2012-05-04 23:38:14 -07:00
if scripting_command_class == None :
2012-05-13 19:53:48 -07:00
scripting_command_class = MovementDBCommand
#scripting_command_class = deepcopy(MovementCommand)
#scripting_command_class.id = scripting_command_class.byte = ord(rom[current_address])
#scripting_command_class.macro_name = "db"
#scripting_command_class.size = 1
#scripting_command_class.override_byte_check = True
#scripting_command_class.id = None
#scripting_command_class.param_types = {0: {"name": "db value", "class": DecimalParam}}
2012-05-13 19:32:28 -07:00
xyz = True
2012-05-04 23:38:14 -07:00
# create an instance of the command class and let it parse its parameter bytes
cls = scripting_command_class ( address = current_address , map_group = self . map_group , map_id = self . map_id , debug = self . debug , force = self . force )
if self . debug :
print cls . to_asm ( )
2012-05-20 01:39:25 -07:00
2012-05-04 23:38:14 -07:00
# store it in this script object
commands . append ( cls )
# certain commands will end the movement engine
end = cls . end
# skip past the command's parameter bytes to go to the next command
current_address + = cls . size
# last byte belonging to script is last byte of last command,
# or the last byte of the last command's last parameter
# (actually i think this might be the next byte after??)
self . last_address = current_address
# store the script in the global table/map thing
2012-05-05 00:29:41 -07:00
all_movements . append ( self )
2012-05-04 23:38:14 -07:00
script_parse_table [ start_address : current_address ] = self
if self . debug :
asm_output = " \n " . join ( [ command . to_asm ( ) for command in commands ] )
print " -------------- \n " + asm_output
# store the script
self . commands = commands
return commands
def to_asm ( self ) :
asm_output = " \n " . join ( [ command . to_asm ( ) for command in self . commands ] )
return asm_output
2012-05-11 14:26:36 -07:00
# TODO: get_dependencies doesn't work if ApplyMovementData uses labels in the future
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
return [ ]
2012-05-13 19:53:48 -07:00
def print_all_movements ( ) :
for each in all_movements :
print each . to_asm ( )
print " ------------------ "
print " done "
2012-05-01 09:25:05 -07:00
class TextCommand ( Command ) :
# an individual text command will not end it
end = False
# this is only used for e.g. macros that don't appear as a byte in the ROM
2012-05-04 12:39:31 -07:00
# don't use the override because all text commands are specified with a byte
2012-05-01 09:25:05 -07:00
override_byte_check = False
# in the case of text/asm commands, size is unknown until after parsing
2012-05-04 12:39:31 -07:00
# some text commands can specify this upfront but not $0
2012-05-01 09:25:05 -07:00
size = None
2012-05-04 18:47:43 -07:00
param_types = { }
2012-05-01 09:25:05 -07:00
params = [ ]
# most text commands won't have any dependencies
# .. except for that one that points to another location for text
# get_dependencies on Command will look at the values of params
2012-05-04 12:39:31 -07:00
# so this doesn't need to be specified by TextCommand as long as it extends Command
2012-05-01 09:25:05 -07:00
#def get_dependencies(self, recompute=False, global_dependencies=set()):
# return []
2012-05-04 17:00:58 -07:00
# this is a regular command in a TextScript for writing text
# but unlike other macros that preprocessor.py handles,
# the preprocessor-parser is custom and MainText is not
# used as a macro by main.asm - however, MainText is
# treated as a macro for the sake of parsing the ROM because
# it is called with $0. This is very similar to how Script
# is parsed and handled. But again, script command macros
# are quite different.. preprocessor.py allows some of them
# to handle how they should be parsed from main.asm, in
# addition to their regular "parse()" method.
2012-05-01 09:25:05 -07:00
class MainText ( TextCommand ) :
" Write text. Structure: [00][Text][0x50 (ends code)] "
id = 0x0
macro_name = " do_text "
2012-05-23 15:38:15 -07:00
use_zero = True
2012-05-01 09:25:05 -07:00
def parse ( self ) :
offset = self . address
2012-05-23 15:39:30 -07:00
# the code below assumes we're jumping past a $0 byte
2012-05-23 15:56:34 -07:00
if self . use_zero == False :
2012-05-23 16:15:48 -07:00
offset = offset
else :
offset = offset + 1
2012-05-23 15:39:30 -07:00
2012-05-01 09:25:05 -07:00
# read until $50, $57 or $58 (not sure about $58...)
jump57 = how_many_until ( chr ( 0x57 ) , offset )
jump50 = how_many_until ( chr ( 0x50 ) , offset )
jump58 = how_many_until ( chr ( 0x58 ) , offset )
# pick whichever one comes first
jump = min ( [ jump57 , jump50 , jump58 ] )
# if $57 appears first then this command is the last in this text script
2012-05-05 13:01:42 -07:00
if jump == jump57 or jump == jump58 :
2012-05-01 09:25:05 -07:00
self . end = True
2012-05-26 14:10:11 -07:00
jump + = 1
2012-05-01 09:25:05 -07:00
# we want the address after the $57
# ("last_address" is misnamed everywhere)
2012-05-23 16:15:48 -07:00
end_address = offset + jump
2012-05-01 09:25:05 -07:00
self . last_address = self . end_address = end_address
# read the text bytes into a structure
2012-05-04 12:39:31 -07:00
# skip the first offset byte because that's the command byte
2012-05-26 14:10:11 -07:00
self . bytes = rom_interval ( offset , jump , strings = False )
2012-05-01 09:25:05 -07:00
2012-05-04 12:39:31 -07:00
# include the original command in the size calculation
2012-05-23 16:15:48 -07:00
self . size = jump
if self . use_zero :
2012-05-26 14:10:11 -07:00
self . last_address = self . address + jump + 1
self . size = self . last_address - self . address
if self . address == 0x9c00e and self . debug :
if self . last_address != 0x9c086 :
print " self.address is: " + hex ( self . address )
print " jump is: " + str ( jump )
print " bytes are: " + str ( self . bytes )
print " self.size is: " + str ( self . size )
print " self.last_address is: " + hex ( self . last_address )
raise Exception , " last_address is wrong for 0x9c00e "
2012-05-01 09:25:05 -07:00
def to_asm ( self ) :
if self . size < 2 or len ( self . bytes ) < 1 :
raise Exception , " $0 text command can ' t end itself with no follow-on bytes "
2012-05-20 01:39:25 -07:00
2012-05-23 15:56:34 -07:00
if self . use_zero :
2012-05-23 15:38:15 -07:00
output = " db $0 "
else :
2012-05-23 15:56:34 -07:00
output = " "
2012-05-01 09:25:05 -07:00
# db $0, $57 or db $0, $50 or w/e
if self . size == 2 and len ( self . bytes ) == 1 :
output + = " , $ %.2x " % ( self . bytes [ 0 ] )
return output
# whether or not quotes are open
in_quotes = False
# whether or not to print "db " next
new_line = False
2012-05-04 14:49:16 -07:00
# whether or not there was a ", " last..
# this is useful outside of quotes
was_comma = False
2012-05-20 01:39:25 -07:00
2012-05-04 15:25:48 -07:00
# has a $50 or $57 been passed yet?
end = False
2012-05-04 14:49:16 -07:00
2012-05-23 15:56:34 -07:00
if not self . use_zero :
new_line = True
was_comma = False
2012-05-01 09:25:05 -07:00
for byte in self . bytes :
2012-05-04 15:25:48 -07:00
if end :
raise Exception , " the text ended due to a $50 or $57 but there are more bytes? "
2012-05-04 15:37:08 -07:00
if new_line :
if in_quotes :
raise Exception , " can ' t be in_quotes on a newline "
elif was_comma :
raise Exception , " last line ' s last character can ' t be a comma "
output + = " db "
2012-05-04 14:49:16 -07:00
# $4f, $51 and $55 can end a line
if byte in [ 0x4f , 0x51 , 0x55 ] :
assert not new_line , " can ' t have $4f, $51, $55 as the first character on a newline "
2012-05-20 01:39:25 -07:00
2012-05-04 14:49:16 -07:00
if in_quotes :
output + = " \" , $ %.2x \n " % ( byte )
elif not in_quotes :
if not was_comma :
output + = " , "
output + = " $ %.2x \n " % ( byte )
2012-05-04 15:25:48 -07:00
# reset everything
in_quotes = False
new_line = True
was_comma = False
2012-05-04 14:49:16 -07:00
elif byte == 0x50 :
2012-05-04 15:25:48 -07:00
# technically you could have this i guess... db "@"
# but in most situations it will be added to the end of the previous line
2012-05-05 13:01:42 -07:00
#assert not new_line, "can't have $50 or '@' as the first character on a newline in the text at "+hex(self.address)
2012-05-04 14:51:27 -07:00
if in_quotes :
2012-05-04 15:25:48 -07:00
output + = " @ \" \n "
new_line = True
elif not in_quotes :
2012-05-11 12:29:19 -07:00
if not was_comma and not new_line :
2012-05-04 15:25:48 -07:00
output + = " , "
output + = " \" @ \" \n "
2012-05-20 01:39:25 -07:00
2012-05-04 15:25:48 -07:00
# reset everything
2012-05-04 18:13:35 -07:00
in_quotes = False
new_line = True
was_comma = False
end = True
2012-05-05 13:01:42 -07:00
# self.end should be set in parse or constructor
# so this is very useless here.. but it's a truism i guess
2012-05-04 21:03:03 -07:00
self . end = True
2012-05-05 13:01:42 -07:00
elif byte == 0x57 or byte == 0x58 :
2012-05-04 18:13:35 -07:00
# close any quotes
if in_quotes :
output + = " \" "
was_comma = False
2012-05-11 12:50:53 -07:00
if not was_comma and not new_line :
2012-05-04 18:13:35 -07:00
output + = " , "
2012-05-05 13:01:42 -07:00
output + = " $ %.2x \n " % ( byte )
2012-05-04 18:13:35 -07:00
2012-05-04 15:25:48 -07:00
in_quotes = False
new_line = True
was_comma = False
end = True
2012-05-05 13:01:42 -07:00
# dunno if $58 should end a text script or not
# also! self.end should be set in parse not in to_asm
# so this is pretty useless overall...
if byte == 0x58 :
self . end = True
2012-05-04 15:25:48 -07:00
elif byte in chars . keys ( ) :
2012-05-04 15:37:08 -07:00
# figure out what the character actually is
2012-05-04 15:25:48 -07:00
char = chars [ byte ]
2012-05-04 17:00:58 -07:00
# oh wait.. quotes isn't a valid character in the first place :(
2012-05-04 15:25:48 -07:00
if char == " \" " :
2012-05-04 15:37:08 -07:00
if in_quotes :
output + = " \" "
in_quotes = False
2012-05-04 17:00:58 -07:00
elif not in_quotes :
2012-05-04 15:37:08 -07:00
if new_line :
output + = " \" "
elif not new_line :
if not was_comma :
output + = " , "
output + = " \" "
in_quotes = True
2012-05-04 17:00:58 -07:00
2012-05-04 17:05:33 -07:00
# the above if statement is probably never called
2012-05-04 15:37:08 -07:00
else :
2012-05-04 17:00:58 -07:00
if not in_quotes :
if not new_line and not was_comma :
output + = " , "
output + = " \" "
in_quotes = True
2012-05-04 15:37:08 -07:00
output + = char
new_line = False
was_comma = False
end = False
2012-05-04 15:25:48 -07:00
else :
# raise Exception, "unknown byte in text script ($%.2x)" % (byte)
# just add an unknown byte directly to the text.. what's the worse that can happen?
if in_quotes :
output + = " \" , $ %.2x " % ( byte )
in_quotes = False
was_comma = False
new_line = False
elif not in_quotes :
if not was_comma and not new_line :
output + = " , "
output + = " $ %.2x " % ( byte )
2012-05-20 01:39:25 -07:00
2012-05-04 15:25:48 -07:00
# reset things
in_quotes = False
new_line = False
was_comma = False
2012-05-01 09:25:05 -07:00
2012-05-04 18:14:00 -07:00
# this shouldn't happen because of the rom_until calls in the parse method
if not end :
2012-05-05 13:01:42 -07:00
raise Exception , " ran out of bytes without the script ending? starts at " + hex ( self . address )
2012-05-04 18:14:00 -07:00
2012-05-04 17:00:58 -07:00
# last character may or may not be allowed to be a newline?
# Script.to_asm() has command.to_asm()+"\n"
if output [ - 1 ] == " \n " :
output = output [ : - 1 ]
2012-05-01 09:25:05 -07:00
return output
2012-05-23 15:38:15 -07:00
class PokedexText ( MainText ) :
use_zero = False
2012-05-01 09:25:05 -07:00
class WriteTextFromRAM ( TextCommand ) :
"""
Write text from ram . Structure : [ 01 ] [ Ram address ( 2 byte ) ]
For valid ram addresses see Glossary . This enables use of variable text strings .
"""
id = 0x1
macro_name = " text_from_ram "
size = 3
2012-05-04 18:47:43 -07:00
param_types = {
2012-05-20 01:39:25 -07:00
0 : { " name " : " pointer " , " class " : MultiByteParam } ,
2012-05-04 18:47:43 -07:00
}
2012-05-01 09:25:05 -07:00
class WriteNumberFromRAM ( TextCommand ) :
"""
02 = Write number from ram . Structure : [ 02 ] [ Ram address ( 2 byte ) ] [ Byte ]
2012-05-20 01:39:25 -07:00
2012-05-01 09:25:05 -07:00
Byte :
2012-05-20 01:39:25 -07:00
2012-05-01 09:25:05 -07:00
Bit5 : Bit6 : Bit7
1 : 1 : 1 = PokéDollar | Don ’ t write zeros
0 : 1 : 1 = Don ’ t write zeros
0 : 0 : 1 = Spaces instead of zeros
0 : 0 : 0 = Write zeros
0 : 1 : 0 = Write zeros
1 : 0 : 0 = PokéDollar
1 : 1 : 0 = PokéDollar
1 : 0 : 1 = Spaces instead of zeros | PokéDollar
2012-05-20 01:39:25 -07:00
2012-05-01 09:25:05 -07:00
Number of figures = Byte AND 0x1F * 2
No Hex - - > Dec Conversio
"""
id = 0x2
macro_name = " number_from_ram "
size = 4
2012-05-04 18:47:43 -07:00
param_types = {
0 : { " name " : " pointer " , " class " : PointerLabelParam } ,
1 : { " name " : " config " , " class " : HexByte } ,
}
2012-05-01 09:25:05 -07:00
class SetWriteRAMLocation ( TextCommand ) :
" Define new ram address to write to. Structure: [03][Ram address (2byte)] "
id = 0x3
macro_name = " store_at "
size = 3
2012-05-04 18:47:43 -07:00
param_types = {
0 : { " name " : " ram address " , " class " : PointerLabelParam } ,
}
2012-05-01 09:25:05 -07:00
class ShowBoxWithValueAt ( TextCommand ) :
" 04 = Write a box. Structure: [04][Ram address (2byte)][Y][X] "
id = 0x4
macro_name = " text_box "
size = 5
2012-05-04 18:47:43 -07:00
param_types = {
0 : { " name " : " ram address " , " class " : PointerLabelParam } ,
1 : { " name " : " y " , " class " : DecimalParam } ,
2 : { " name " : " x " , " class " : DecimalParam } ,
}
2012-05-01 09:25:05 -07:00
class Populate2ndLineOfTextBoxWithRAMContents ( TextCommand ) :
" 05 = New ram address to write to becomes 2nd line of a text box. Structure: [05] "
id = 0x5
macro_name = " text_dunno1 "
size = 1
class ShowArrowsAndButtonWait ( TextCommand ) :
" 06 = Wait for key down + show arrows. Structure: [06] "
id = 0x6
2012-05-11 13:09:26 -07:00
macro_name = " text_waitbutton "
2012-05-01 09:25:05 -07:00
size = 1
class Populate2ndLine ( TextCommand ) :
"""
07 = New ram address to write to becomes 2 nd line of a text box
Textbox + show arrows . Structure : [ 07 ]
"""
id = 0x7
macro_name = " text_dunno2 "
size = 1
class TextInlineAsm ( TextCommand ) :
" 08 = After the code an ASM script starts. Structure: [08][Script] "
id = 0x8
macro_name = " start_asm "
end = True
2012-05-05 00:46:46 -07:00
size = 1
2012-05-04 18:47:43 -07:00
# TODO: parse the following asm with gbz80disasm
2012-05-01 09:25:05 -07:00
class WriteDecimalNumberFromRAM ( TextCommand ) :
"""
09 = Write number from rom / ram in decimal . Structure : [ 09 ] [ Ram address / Pointer ( 2 byte ) ] [ Byte ]
Byte :
2012-05-20 01:39:25 -07:00
2012-05-01 09:25:05 -07:00
Is split : 1. 4 bits = Number of bytes to load . 0 = 3 , 1 = 1 , 2 = 2
2. 4 bits = Number of figures of displayed number
0 = Don ’ t care
1 = Don ’ t care
> = 2 = Number
"""
id = 0x9
macro_name = " deciram "
size = 4
2012-05-04 18:47:43 -07:00
param_types = {
0 : { " name " : " pointer? " , " class " : PointerLabelParam } ,
1 : { " name " : " config " , " class " : HexByte } ,
}
2012-05-01 09:25:05 -07:00
class InterpretDataStream ( TextCommand ) :
"""
0 A = Interpret Data stream . Structure : [ 0 A ]
see : http : / / hax . iimarck . us / files / scriptingcodes_eng . htm #Marke88
"""
id = 0xA
macro_name = " interpret_data "
size = 1
class Play0thSound ( TextCommand ) :
" 0B = Play sound 0x0000. Structure: [0B] "
id = 0xB
sound_num = 0
macro_name = " sound0 "
size = 1
class LimitedIntrepretDataStream ( TextCommand ) :
"""
0 C = Interpret Data stream . Structure : [ 0 C ] [ Number of codes to interpret ]
For every interpretation there is a “ … “ written
"""
id = 0xC
macro_name = " limited_interpret_data "
size = 2
2012-05-04 18:47:43 -07:00
param_types = {
0 : { " name " : " number of codes to interpret " , " class " : DecimalParam } ,
}
2012-05-01 09:25:05 -07:00
class WaitForKeyDownDisplayArrow ( ShowArrowsAndButtonWait ) :
"""
0 D = Wait for key down display arrow . Structure : [ 0 D ]
"""
id = 0xD
macro_name = " waitbutton2 "
size = 1
class Play9thSound ( Play0thSound ) :
id = 0xE
sound_num = 9
macro_name = " sound0x09 "
size = 1
class Play1stSound ( Play0thSound ) :
id = 0xF
sound_num = 1
macro_name = " sound0x0F "
size = 1
class Play2ndSound ( Play0thSound ) :
id = 0x10
sound_num = 2
macro_name = " sound0x02 "
size = 1
class Play10thSound ( Play0thSound ) :
id = 0x11
sound_num = 10
macro_name = " sound0x0A "
size = 1
class Play45thSound ( Play0thSound ) :
id = 0x12
sound_num = 0x2D
macro_name = " sound0x2D "
size = 1
class Play44thSound ( Play0thSound ) :
id = 0x13
sound_num = 0x2C
macro_name = " sound0x2C "
size = 1
class DisplayByteFromRAMAt ( TextCommand ) :
"""
14 = Display MEMORY . Structure : [ 14 ] [ Byte ]
2012-05-20 01:39:25 -07:00
2012-05-01 09:25:05 -07:00
Byte :
2012-05-20 01:39:25 -07:00
2012-05-01 09:25:05 -07:00
00 = MEMORY1
01 = MEMORY2
02 = MEMORY
04 = TEMPMEMORY2
05 = TEMPMEMORY1
"""
id = 0x14
macro_name = " show_byte_at "
size = 2
2012-05-04 18:47:43 -07:00
param_types = {
1 : { " name " : " memory byte id " , " class " : DecimalParam } ,
}
2012-05-01 09:25:05 -07:00
class WriteCurrentDay ( TextCommand ) :
" 15 = Write current day. Structure: [15] "
id = 0x15
macro_name = " current_day "
size = 1
class TextJump ( TextCommand ) :
" 16 = 3byte pointer to new text follows. Structure: [16][2byte pointer][bank] "
id = 0x16
macro_name = " text_jump "
size = 4
2012-05-04 18:47:43 -07:00
param_types = {
0 : { " name " : " text " , " class " : TextPointerLabelAfterBankParam } ,
}
2012-05-04 21:03:03 -07:00
# this is needed because sometimes a script ends with $50 $50
class TextEndingCommand ( TextCommand ) :
id = 0x50
macro_name = " db "
override_byte_check = False
size = 1
end = True
def to_asm ( self ) :
return " db $50 "
2012-05-01 09:25:05 -07:00
2012-05-04 19:05:59 -07:00
text_command_classes = inspect . getmembers ( sys . modules [ __name__ ] , \
lambda obj : inspect . isclass ( obj ) and \
issubclass ( obj , TextCommand ) and \
2012-05-26 14:10:11 -07:00
obj != TextCommand and obj != PokedexText )
2012-05-04 19:05:59 -07:00
2012-04-17 21:33:23 -07:00
#byte: [name, [param1 name, param1 type], [param2 name, param2 type], ...]
#0x9E: ["verbosegiveitem", ["item", ItemLabelByte], ["quantity", SingleByteParam]],
pksv_crystal_more = {
0x00 : [ " 2call " , [ " pointer " , ScriptPointerLabelParam ] ] ,
0x01 : [ " 3call " , [ " pointer " , ScriptPointerLabelBeforeBank ] ] ,
0x02 : [ " 2ptcall " , [ " pointer " , PointerLabelToScriptPointer ] ] ,
0x03 : [ " 2jump " , [ " pointer " , ScriptPointerLabelParam ] ] ,
0x04 : [ " 3jump " , [ " pointer " , ScriptPointerLabelBeforeBank ] ] ,
0x05 : [ " 2ptjump " , [ " pointer " , PointerLabelToScriptPointer ] ] ,
0x06 : [ " if equal " , [ " byte " , SingleByteParam ] , [ " pointer " , ScriptPointerLabelParam ] ] ,
0x07 : [ " if not equal " , [ " byte " , SingleByteParam ] , [ " pointer " , ScriptPointerLabelParam ] ] ,
2012-04-22 18:49:00 -07:00
0x08 : [ " iffalse " , [ " pointer " , ScriptPointerLabelParam ] ] ,
0x09 : [ " iftrue " , [ " pointer " , ScriptPointerLabelParam ] ] ,
2012-04-17 21:33:23 -07:00
0x0A : [ " if less than " , [ " byte " , SingleByteParam ] , [ " pointer " , ScriptPointerLabelParam ] ] ,
0x0B : [ " if greater than " , [ " byte " , SingleByteParam ] , [ " pointer " , ScriptPointerLabelParam ] ] ,
0x0C : [ " jumpstd " , [ " predefined_script " , MultiByteParam ] ] ,
0x0D : [ " callstd " , [ " predefined_script " , MultiByteParam ] ] ,
0x0E : [ " 3callasm " , [ " asm " , AsmPointerParam ] ] ,
0x0F : [ " special " , [ " predefined_script " , MultiByteParam ] ] ,
0x10 : [ " 2ptcallasm " , [ " asm " , PointerToAsmPointerParam ] ] ,
#should map_group/map_id be dealt with in some special way in the asm?
0x11 : [ " checkmaptriggers " , [ " map_group " , SingleByteParam ] , [ " map_id " , SingleByteParam ] ] ,
0x12 : [ " domaptrigger " , [ " map_group " , MapGroupParam ] , [ " map_id " , MapIdParam ] , [ " trigger_id " , SingleByteParam ] ] ,
0x13 : [ " checktriggers " ] ,
0x14 : [ " dotrigger " , [ " trigger_id " , SingleByteParam ] ] ,
0x15 : [ " writebyte " , [ " value " , SingleByteParam ] ] ,
0x16 : [ " addvar " , [ " value " , SingleByteParam ] ] ,
0x17 : [ " random " , [ " input " , SingleByteParam ] ] ,
0x18 : [ " checkver " ] ,
0x19 : [ " copybytetovar " , [ " address " , RAMAddressParam ] ] ,
0x1A : [ " copyvartobyte " , [ " address " , RAMAddressParam ] ] ,
0x1B : [ " loadvar " , [ " address " , RAMAddressParam ] , [ " value " , SingleByteParam ] ] ,
0x1C : [ " checkcode " , [ " variable_id " , SingleByteParam ] ] ,
0x1D : [ " writevarcode " , [ " variable_id " , SingleByteParam ] ] ,
0x1E : [ " writecode " , [ " variable_id " , SingleByteParam ] , [ " value " , SingleByteParam ] ] ,
0x1F : [ " giveitem " , [ " item " , ItemLabelByte ] , [ " quantity " , SingleByteParam ] ] ,
2012-04-22 18:49:00 -07:00
0x20 : [ " takeitem " , [ " item " , ItemLabelByte ] , [ " quantity " , DecimalParam ] ] ,
2012-04-17 21:33:23 -07:00
0x21 : [ " checkitem " , [ " item " , ItemLabelByte ] ] ,
0x22 : [ " givemoney " , [ " account " , SingleByteParam ] , [ " money " , MoneyByteParam ] ] ,
0x23 : [ " takemoney " , [ " account " , SingleByteParam ] , [ " money " , MoneyByteParam ] ] ,
2012-04-26 23:07:30 -07:00
0x24 : [ " checkmoney " , [ " account " , SingleByteParam ] , [ " money " , MoneyByteParam ] ] ,
2012-04-17 21:33:23 -07:00
0x25 : [ " givecoins " , [ " coins " , CoinByteParam ] ] ,
0x26 : [ " takecoins " , [ " coins " , CoinByteParam ] ] ,
0x27 : [ " checkcoins " , [ " coins " , CoinByteParam ] ] ,
#0x28-0x2A not from pksv
0x28 : [ " addcellnum " , [ " person " , SingleByteParam ] ] ,
0x29 : [ " delcellnum " , [ " person " , SingleByteParam ] ] ,
0x2A : [ " checkcellnum " , [ " person " , SingleByteParam ] ] ,
#back on track...
0x2B : [ " checktime " , [ " time " , SingleByteParam ] ] ,
0x2C : [ " checkpoke " , [ " pkmn " , PokemonParam ] ] ,
#0x2D: ["givepoke", ], .... see GivePoke class
0x2E : [ " giveegg " , [ " pkmn " , PokemonParam ] , [ " level " , DecimalParam ] ] ,
0x2F : [ " givepokeitem " , [ " pointer " , PointerParamToItemAndLetter ] ] ,
0x30 : [ " checkpokeitem " , [ " pointer " , PointerParamToItemAndLetter ] ] , #not pksv
0x31 : [ " checkbit1 " , [ " bit_number " , MultiByteParam ] ] ,
0x32 : [ " clearbit1 " , [ " bit_number " , MultiByteParam ] ] ,
0x33 : [ " setbit1 " , [ " bit_number " , MultiByteParam ] ] ,
0x34 : [ " checkbit2 " , [ " bit_number " , MultiByteParam ] ] ,
0x35 : [ " clearbit2 " , [ " bit_number " , MultiByteParam ] ] ,
0x36 : [ " setbit2 " , [ " bit_number " , MultiByteParam ] ] ,
0x37 : [ " wildoff " ] ,
0x38 : [ " wildon " ] ,
0x39 : [ " xycompare " , [ " pointer " , MultiByteParam ] ] ,
0x3A : [ " warpmod " , [ " warp_id " , SingleByteParam ] , [ " map_group " , MapGroupParam ] , [ " map_id " , MapIdParam ] ] ,
0x3B : [ " blackoutmod " , [ " map_group " , MapGroupParam ] , [ " map_id " , MapIdParam ] ] ,
0x3C : [ " warp " , [ " map_group " , MapGroupParam ] , [ " map_id " , MapIdParam ] , [ " x " , SingleByteParam ] , [ " y " , SingleByteParam ] ] ,
0x3D : [ " readmoney " , [ " account " , SingleByteParam ] , [ " memory " , SingleByteParam ] ] , #not pksv
0x3E : [ " readcoins " , [ " memory " , SingleByteParam ] ] , #not pksv
0x3F : [ " RAM2MEM " , [ " memory " , SingleByteParam ] ] , #not pksv
0x40 : [ " pokenamemem " , [ " pokemon " , PokemonParam ] , [ " memory " , SingleByteParam ] ] , #not pksv
0x41 : [ " itemtotext " , [ " item " , ItemLabelByte ] , [ " memory " , SingleByteParam ] ] ,
0x42 : [ " mapnametotext " , [ " memory " , SingleByteParam ] ] , #not pksv
2012-05-18 23:44:44 -07:00
0x43 : [ " trainertotext " , [ " trainer_id " , TrainerGroupParam ] , [ " trainer_group " , TrainerIdParam ] , [ " memory " , SingleByteParam ] ] ,
2012-05-22 09:50:25 -07:00
0x44 : [ " stringtotext " , [ " text_pointer " , EncodedTextLabelParam ] , [ " memory " , SingleByteParam ] ] ,
2012-04-17 21:33:23 -07:00
0x45 : [ " itemnotify " ] ,
0x46 : [ " pocketisfull " ] ,
0x47 : [ " loadfont " ] ,
0x48 : [ " refreshscreen " , [ " dummy " , SingleByteParam ] ] ,
0x49 : [ " loadmovesprites " ] ,
0x4A : [ " loadbytec1ce " , [ " byte " , SingleByteParam ] ] , #not pksv
0x4B : [ " 3writetext " , [ " text_pointer " , PointerLabelBeforeBank ] ] ,
2012-04-27 16:50:13 -07:00
0x4C : [ " 2writetext " , [ " text_pointer " , RawTextPointerLabelParam ] ] ,
2012-04-17 21:33:23 -07:00
0x4D : [ " repeattext " , [ " byte " , SingleByteParam ] , [ " byte " , SingleByteParam ] ] , #not pksv
0x4E : [ " yesorno " ] ,
0x4F : [ " loadmenudata " , [ " data " , MenuDataPointerParam ] ] ,
0x50 : [ " writebackup " ] ,
0x51 : [ " jumptextfaceplayer " , [ " text_pointer " , RawTextPointerLabelParam ] ] ,
2012-04-26 14:50:52 -07:00
0x53 : [ " jumptext " , [ " text_pointer " , RawTextPointerLabelParam ] ] ,
2012-04-17 21:33:23 -07:00
0x54 : [ " closetext " ] ,
0x55 : [ " keeptextopen " ] ,
0x56 : [ " pokepic " , [ " pokemon " , PokemonParam ] ] ,
0x57 : [ " pokepicyesorno " ] ,
0x58 : [ " interpretmenu " ] ,
0x59 : [ " interpretmenu2 " ] ,
#not pksv
0x5A : [ " loadpikachudata " ] ,
0x5B : [ " battlecheck " ] ,
0x5C : [ " loadtrainerdata " ] ,
#back to pksv..
0x5D : [ " loadpokedata " , [ " pokemon " , PokemonParam ] , [ " level " , DecimalParam ] ] ,
0x5E : [ " loadtrainer " , [ " trainer_group " , TrainerGroupParam ] , [ " trainer_id " , TrainerIdParam ] ] ,
0x5F : [ " startbattle " ] ,
0x60 : [ " returnafterbattle " ] ,
0x61 : [ " catchtutorial " , [ " byte " , SingleByteParam ] ] ,
#not pksv
0x62 : [ " trainertext " , [ " which_text " , SingleByteParam ] ] ,
0x63 : [ " trainerstatus " , [ " action " , SingleByteParam ] ] ,
#back to pksv..
0x64 : [ " winlosstext " , [ " win_text_pointer " , TextPointerLabelParam ] , [ " loss_text_pointer " , TextPointerLabelParam ] ] ,
0x65 : [ " scripttalkafter " ] , #not pksv
0x66 : [ " talkaftercancel " ] ,
0x67 : [ " talkaftercheck " ] ,
0x68 : [ " setlasttalked " , [ " person " , SingleByteParam ] ] ,
0x69 : [ " applymovement " , [ " person " , SingleByteParam ] , [ " data " , MovementPointerLabelParam ] ] ,
0x6A : [ " applymovement2 " , [ " data " , MovementPointerLabelParam ] ] , #not pksv
0x6B : [ " faceplayer " ] ,
0x6C : [ " faceperson " , [ " person1 " , SingleByteParam ] , [ " person2 " , SingleByteParam ] ] ,
0x6D : [ " variablesprite " , [ " byte " , SingleByteParam ] , [ " sprite " , SingleByteParam ] ] ,
0x6E : [ " disappear " , [ " person " , SingleByteParam ] ] , #hideperson
0x6F : [ " appear " , [ " person " , SingleByteParam ] ] , #showperson
0x70 : [ " follow " , [ " person2 " , SingleByteParam ] , [ " person1 " , SingleByteParam ] ] ,
0x71 : [ " stopfollow " ] ,
0x72 : [ " moveperson " , [ " person " , SingleByteParam ] , [ " x " , SingleByteParam ] , [ " y " , SingleByteParam ] ] ,
0x73 : [ " writepersonxy " , [ " person " , SingleByteParam ] ] , #not pksv
0x74 : [ " loademote " , [ " bubble " , SingleByteParam ] ] ,
2012-04-22 18:49:00 -07:00
0x75 : [ " showemote " , [ " bubble " , SingleByteParam ] , [ " person " , SingleByteParam ] , [ " time " , DecimalParam ] ] ,
2012-04-17 21:33:23 -07:00
0x76 : [ " spriteface " , [ " person " , SingleByteParam ] , [ " facing " , SingleByteParam ] ] ,
0x77 : [ " follownotexact " , [ " person2 " , SingleByteParam ] , [ " person1 " , SingleByteParam ] ] ,
2012-04-22 18:49:00 -07:00
0x78 : [ " earthquake " , [ " param " , DecimalParam ] ] ,
2012-04-17 21:33:23 -07:00
0x79 : [ " changemap " , [ " map_data_pointer " , MapDataPointerParam ] ] ,
0x7A : [ " changeblock " , [ " x " , SingleByteParam ] , [ " y " , SingleByteParam ] , [ " block " , SingleByteParam ] ] ,
0x7B : [ " reloadmap " ] ,
0x7C : [ " reloadmappart " ] ,
0x7D : [ " writecmdqueue " , [ " queue_pointer " , MultiByteParam ] ] ,
0x7E : [ " delcmdqueue " , [ " byte " , SingleByteParam ] ] ,
0x7F : [ " playmusic " , [ " music_pointer " , MultiByteParam ] ] ,
0x80 : [ " playrammusic " ] ,
0x81 : [ " musicfadeout " , [ " music " , MultiByteParam ] , [ " fadetime " , SingleByteParam ] ] ,
0x82 : [ " playmapmusic " ] ,
0x83 : [ " reloadmapmusic " ] ,
2012-05-22 18:57:09 -07:00
0x84 : [ " cry " , [ " cry_id " , MultiByteParam ] ] , #XXX maybe it should use PokemonParam
2012-04-17 21:33:23 -07:00
0x85 : [ " playsound " , [ " sound_pointer " , MultiByteParam ] ] ,
0x86 : [ " waitbutton " ] ,
0x87 : [ " warpsound " ] ,
0x88 : [ " specialsound " ] ,
0x89 : [ " passtoengine " , [ " data_pointer " , PointerLabelBeforeBank ] ] ,
0x8A : [ " newloadmap " , [ " which_method " , SingleByteParam ] ] ,
2012-04-22 18:49:00 -07:00
0x8B : [ " pause " , [ " length " , DecimalParam ] ] ,
2012-04-17 21:33:23 -07:00
0x8C : [ " deactivatefacing " , [ " time " , SingleByteParam ] ] ,
0x8D : [ " priorityjump " , [ " pointer " , ScriptPointerLabelParam ] ] ,
0x8E : [ " warpcheck " ] ,
0x8F : [ " ptpriorityjump " , [ " pointer " , ScriptPointerLabelParam ] ] ,
0x90 : [ " return " ] ,
0x91 : [ " end " ] ,
0x92 : [ " reloadandreturn " ] ,
0x93 : [ " resetfuncs " ] ,
0x94 : [ " pokemart " , [ " dialog_id " , SingleByteParam ] , [ " mart_id " , MultiByteParam ] ] , #maybe it should be a pokemark constant id/label?
0x95 : [ " elevator " , [ " floor_list_pointer " , PointerLabelParam ] ] ,
0x96 : [ " trade " , [ " trade_id " , SingleByteParam ] ] ,
0x97 : [ " askforphonenumber " , [ " number " , SingleByteParam ] ] ,
0x98 : [ " phonecall " , [ " caller_name " , RawTextPointerLabelParam ] ] ,
0x99 : [ " hangup " ] ,
0x9A : [ " describedecoration " , [ " byte " , SingleByteParam ] ] ,
0x9B : [ " fruittree " , [ " tree_id " , SingleByteParam ] ] ,
0x9C : [ " specialphonecall " , [ " call_id " , SingleByteParam ] , [ " wtf " , SingleByteParam ] ] ,
0x9D : [ " checkphonecall " ] ,
0x9E : [ " verbosegiveitem " , [ " item " , ItemLabelByte ] , [ " quantity " , DecimalParam ] ] ,
0x9F : [ " verbosegiveitem2 " , [ " item " , ItemLabelByte ] ] ,
0xA0 : [ " loadwilddata " , [ " map_group " , MapGroupParam ] , [ " map_id " , MapIdParam ] ] ,
0xA1 : [ " halloffame " ] ,
0xA2 : [ " credits " ] ,
0xA3 : [ " warpfacing " , [ " facing " , SingleByteParam ] , [ " map_group " , MapGroupParam ] , [ " map_id " , MapIdParam ] , [ " x " , SingleByteParam ] , [ " y " , SingleByteParam ] ] ,
0xA4 : [ " storetext " , [ " pointer " , PointerLabelBeforeBank ] , [ " memory " , SingleByteParam ] ] ,
0xA5 : [ " displaylocation " , [ " id " , SingleByteParam ] ] ,
0xA8 : [ " unknown0xa8 " , [ " unknown " , SingleByteParam ] ] ,
0xB2 : [ " unknown0xb2 " , [ " unknown " , SingleByteParam ] ] ,
0xCC : [ " unknown0xcc " ] ,
}
2012-03-27 00:01:04 -07:00
def create_command_classes ( debug = False ) :
2012-03-25 23:47:58 -07:00
""" creates some classes for each command byte """
2012-04-08 14:38:34 -07:00
#don't forget to add any manually created script command classes
2012-04-14 11:01:20 -07:00
#.. except for Warp, Signpost and some others that aren't found in scripts
2012-04-08 14:38:34 -07:00
klasses = [ GivePoke ]
2012-03-25 23:47:58 -07:00
for ( byte , cmd ) in pksv_crystal_more . items ( ) :
2012-04-08 11:14:31 -07:00
cmd_name = cmd [ 0 ] . replace ( " " , " _ " )
2012-04-02 20:34:02 -07:00
params = { " id " : byte , " size " : 1 , " end " : byte in pksv_crystal_more_enders , " macro_name " : cmd_name }
params [ " param_types " ] = { }
2012-03-25 23:47:58 -07:00
if len ( cmd ) > 1 :
param_types = cmd [ 1 : ]
for ( i , each ) in enumerate ( param_types ) :
thing = { " name " : each [ 0 ] , " class " : each [ 1 ] }
params [ " param_types " ] [ i ] = thing
2012-03-27 00:01:04 -07:00
if debug :
print " each is: " + str ( each )
print " thing[class] is: " + str ( thing [ " class " ] )
2012-03-25 23:47:58 -07:00
params [ " size " ] + = thing [ " class " ] . size
klass_name = cmd_name + " Command "
klass = classobj ( klass_name , ( Command , ) , params )
globals ( ) [ klass_name ] = klass
2012-03-27 00:01:04 -07:00
klasses . append ( klass )
2012-03-25 23:47:58 -07:00
#later an individual klass will be instantiated to handle something
return klasses
2012-03-27 00:01:04 -07:00
command_classes = create_command_classes ( )
2012-03-25 23:47:58 -07:00
2012-04-25 18:02:40 -07:00
def generate_macros ( filename = " ../script_macros.asm " ) :
""" generates all macros based on commands
this is dumped into script_macros . asm """
output = " ; This file is generated by generate_macros. \n "
for command in command_classes :
output + = " \n "
2012-04-26 12:37:09 -07:00
#if command.macro_name[0].isdigit():
# output += "_"
2012-04-25 18:02:40 -07:00
output + = command . macro_name + " : MACRO \n "
output + = spacing + " db $ %.2x \n " % ( command . id )
current_param = 1
for ( index , each ) in command . param_types . items ( ) :
if issubclass ( each [ " class " ] , SingleByteParam ) :
output + = spacing + " db \\ " + str ( current_param ) + " \n "
elif issubclass ( each [ " class " ] , MultiByteParam ) :
output + = spacing + " dw \\ " + str ( current_param ) + " \n "
current_param + = 1
output + = spacing + " ENDM \n "
fh = open ( filename , " w " )
fh . write ( output )
fh . close ( )
return output
2012-03-11 13:30:14 -07:00
#use this to keep track of commands without pksv names
pksv_no_names = { }
def pretty_print_pksv_no_names ( ) :
2012-03-14 20:03:45 -07:00
""" just some nice debugging output
use this to keep track of commands without pksv names
pksv_no_names is created in parse_script_engine_script_at """
2012-03-11 13:30:14 -07:00
for ( command_byte , addresses ) in pksv_no_names . items ( ) :
if command_byte in pksv_crystal_unknowns : continue
print hex ( command_byte ) + " appearing in these scripts: "
for address in addresses :
print " " + hex ( address )
2012-03-15 00:09:17 -07:00
recursive_scripts = set ( [ ] )
2012-03-24 02:43:06 -07:00
def rec_parse_script_engine_script_at ( address , origin = None , debug = True ) :
2012-03-14 20:03:45 -07:00
""" this is called in parse_script_engine_script_at for recursion
when this works it should be flipped back to using the regular
parser . """
2012-03-15 00:09:17 -07:00
recursive_scripts . add ( ( address , origin ) )
2012-03-24 02:43:06 -07:00
return parse_script_engine_script_at ( address , origin = origin , debug = debug )
2012-04-17 18:22:28 -07:00
2012-03-24 02:43:06 -07:00
def find_broken_recursive_scripts ( output = False , debug = True ) :
2012-03-15 00:09:17 -07:00
""" well.. these at least have a chance of maybe being broken? """
for r in list ( recursive_scripts ) :
script = { }
length = " not counted here "
if is_script_already_parsed_at ( r [ 0 ] ) :
script = script_parse_table [ r [ 0 ] ]
length = str ( len ( script ) )
if len ( script ) > 20 or script == { } :
print " ******************* begin "
print " script at " + hex ( r [ 0 ] ) + " from main script " + hex ( r [ 1 ] ) + " with length: " + length
if output :
parse_script_engine_script_at ( r [ 0 ] , force = True , debug = True )
print " ==================== end "
2012-04-17 18:22:28 -07:00
2012-03-18 21:05:28 -07:00
stop_points = [ 0x1aafa2 ,
0x9f58f , #battle tower
0x9f62f , #battle tower
]
2012-04-21 08:24:44 -07:00
class Script :
2012-04-26 16:41:13 -07:00
base_label = " UnknownScript_ "
2012-03-25 11:53:21 -07:00
def __init__ ( self , * args , * * kwargs ) :
self . address = None
self . commands = None
if len ( kwargs ) == 0 and len ( args ) == 0 :
raise Exception , " Script.__init__ must be given some arguments "
#first positional argument is address
if len ( args ) == 1 :
address = args [ 0 ]
if type ( address ) == str :
address = int ( address , 16 )
elif type ( address ) != int :
raise Exception , " address must be an integer or string "
self . address = address
elif len ( args ) > 1 :
raise Exception , " don ' t know what to do with second (or later) positional arguments "
2012-04-27 11:41:44 -07:00
self . dependencies = None
2012-04-26 16:41:13 -07:00
if " label " in kwargs . keys ( ) :
label = kwargs [ " label " ]
else :
label = None
if not label :
label = self . base_label + hex ( self . address )
self . label = Label ( name = label , address = address , object = self )
2012-05-02 11:37:11 -07:00
if " map_group " in kwargs . keys ( ) :
self . map_group = kwargs [ " map_group " ]
if " map_id " in kwargs . keys ( ) :
self . map_id = kwargs [ " map_id " ]
2012-05-16 16:33:24 -07:00
if " parent " in kwargs . keys ( ) :
self . parent = kwargs [ " parent " ]
2012-03-25 11:53:21 -07:00
#parse the script at the address
2012-04-08 13:41:52 -07:00
if " use_old_parse " in kwargs . keys ( ) and kwargs [ " use_old_parse " ] == True :
self . old_parse ( * * kwargs )
else :
self . parse ( self . address , * * kwargs )
2012-04-17 18:22:28 -07:00
2012-03-25 11:53:21 -07:00
def pksv_list ( self ) :
""" shows a list of pksv names for each command in the script """
items = [ ]
2012-04-08 12:46:37 -07:00
if type ( self . commands ) == dict :
for ( id , command ) in self . commands . items ( ) :
if command [ " type " ] in pksv_crystal :
items . append ( pksv_crystal [ command [ " type " ] ] )
else :
items . append ( hex ( command [ " type " ] ) )
else :
for command in self . commands :
items . append ( command . macro_name )
2012-03-25 11:53:21 -07:00
return items
2012-04-17 18:22:28 -07:00
2012-03-25 11:53:21 -07:00
def to_pksv ( self ) :
""" returns a string of pksv command names """
pksv = self . pksv_list ( )
output = " script starting at: " + hex ( self . address ) + " .. "
first = True
for item in pksv :
item = str ( item )
if first :
output + = item
first = False
else :
output + = " , " + item
return output
2012-04-17 18:22:28 -07:00
2012-03-25 11:53:21 -07:00
def show_pksv ( self ) :
""" prints a list of pksv command names in this script """
print self . to_pksv ( )
2012-04-17 18:22:28 -07:00
2012-04-08 12:46:37 -07:00
def parse ( self , start_address , force = False , map_group = None , map_id = None , force_top = True , origin = True , debug = False ) :
""" parses a script using the Command classes
as an alternative to the old method using hard - coded commands
2012-04-17 18:22:28 -07:00
2012-04-08 12:46:37 -07:00
force_top just means ' force the main script to get parsed, but not any subscripts '
"""
global command_classes , rom , script_parse_table
current_address = start_address
2012-04-08 21:05:19 -07:00
print " Script.parse address= " + hex ( self . address ) + " map_group= " + str ( map_group ) + " map_id= " + str ( map_id )
2012-04-08 12:46:37 -07:00
if start_address in stop_points and force == False :
print " script parsing is stopping at stop_point= " + hex ( start_address ) + " at map_group= " + str ( map_group ) + " map_id= " + str ( map_id )
return None
if start_address < 0x4000 and start_address not in [ 0x26ef , 0x114 , 0x1108 ] :
print " address is less than 0x4000.. address is: " + hex ( start_address )
sys . exit ( 1 )
if is_script_already_parsed_at ( start_address ) and not force and not force_top :
raise Exception , " this script has already been parsed before, please use that instance ( " + hex ( start_address ) + " ) "
2012-05-20 01:39:25 -07:00
2012-04-29 08:40:34 -07:00
# load up the rom if it hasn't been loaded already
2012-04-08 12:46:37 -07:00
load_rom ( )
2012-04-29 08:40:34 -07:00
# in the event that the script parsing fails.. it would be nice to leave evidence
2012-04-08 12:46:37 -07:00
script_parse_table [ start_address : start_address + 1 ] = " incomplete parse_script_with_command_classes "
2012-04-29 08:40:34 -07:00
# start with a blank script
2012-04-08 12:46:37 -07:00
commands = [ ]
2012-04-29 08:40:34 -07:00
# use this to control the while loop
2012-04-08 12:46:37 -07:00
end = False
2012-04-29 08:40:34 -07:00
# for each command found..
2012-04-08 12:46:37 -07:00
while not end :
2012-04-29 08:40:34 -07:00
# get the current scripting byte
2012-04-08 12:46:37 -07:00
cur_byte = ord ( rom [ current_address ] )
2012-04-29 08:40:34 -07:00
# reset the command class (last command was probably different)
scripting_command_class = None
# match the command id byte to a scripting command class like GivePoke
for class_ in command_classes :
if class_ . id == cur_byte :
scripting_command_class = class_
# no matching command found (not implemented yet)- just end this script
# NOTE: might be better to raise an exception and end the program?
if scripting_command_class == None :
2012-04-08 12:46:37 -07:00
print " parsing script; current_address is: " + hex ( current_address )
current_address + = 1
2012-04-21 01:15:56 -07:00
asm_output = " \n " . join ( [ command . to_asm ( ) for command in commands ] )
2012-04-14 23:27:20 -07:00
end = True
continue
2012-04-29 08:40:34 -07:00
# maybe the program should exit with failure instead?
2012-04-14 23:27:20 -07:00
#raise Exception, "no command found? id: " + hex(cur_byte) + " at " + hex(current_address) + " asm is:\n" + asm_output
2012-04-29 08:40:34 -07:00
# create an instance of the command class and let it parse its parameter bytes
#print "about to parse command(script@"+hex(start_address)+"): " + str(scripting_command_class.macro_name)
2012-05-16 16:33:24 -07:00
cls = scripting_command_class ( address = current_address , force = force , map_group = map_group , map_id = map_id , parent = self )
2012-05-20 01:39:25 -07:00
2012-04-29 08:40:34 -07:00
#if self.debug:
# print cls.to_asm()
2012-05-20 01:39:25 -07:00
2012-04-29 08:40:34 -07:00
# store it in this script object
2012-04-08 12:46:37 -07:00
commands . append ( cls )
2012-05-20 01:39:25 -07:00
2012-04-29 08:40:34 -07:00
# certain commands will end the scripting engine
end = cls . end
2012-05-20 01:39:25 -07:00
2012-04-29 08:40:34 -07:00
# skip past the command's parameter bytes to go to the next command
2012-04-08 12:46:37 -07:00
#current_address = cls.last_address + 1
current_address + = cls . size
2012-04-29 08:40:34 -07:00
# last byte belonging to script is last byte of last command,
# or the last byte of the last command's last parameter
2012-04-21 01:15:56 -07:00
self . last_address = current_address
2012-04-29 08:40:34 -07:00
# store the script in the global table/map thing
2012-04-08 12:46:37 -07:00
script_parse_table [ start_address : current_address ] = self
2012-05-20 01:39:25 -07:00
2012-04-21 01:15:56 -07:00
asm_output = " \n " . join ( [ command . to_asm ( ) for command in commands ] )
2012-04-08 12:46:37 -07:00
print " -------------- \n " + asm_output
2012-05-20 01:39:25 -07:00
2012-04-29 08:40:34 -07:00
# store the script
2012-04-08 12:46:37 -07:00
self . commands = commands
2012-05-20 01:39:25 -07:00
2012-04-08 12:46:37 -07:00
return commands
2012-04-17 18:22:28 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-27 11:41:44 -07:00
if self . dependencies != None and not recompute :
2012-04-27 12:58:57 -07:00
global_dependencies . update ( self . dependencies )
2012-04-27 11:29:41 -07:00
return self . dependencies
2012-04-20 16:26:52 -07:00
dependencies = [ ]
for command in self . commands :
2012-04-27 12:58:57 -07:00
deps = command . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies )
2012-04-20 16:26:52 -07:00
dependencies . extend ( deps )
2012-04-27 11:29:41 -07:00
self . dependencies = dependencies
2012-04-20 16:26:52 -07:00
return dependencies
2012-04-08 12:46:37 -07:00
def to_asm ( self ) :
asm_output = " " . join ( [ command . to_asm ( ) + " \n " for command in self . commands ] )
2012-04-21 08:24:44 -07:00
if asm_output [ - 1 ] == " \n " :
asm_output = asm_output [ : - 1 ]
2012-04-08 12:46:37 -07:00
return asm_output
2012-04-17 18:22:28 -07:00
2012-04-08 12:46:37 -07:00
def old_parse ( self , * args , * * kwargs ) :
2012-04-18 14:54:11 -07:00
""" included from old_parse_scripts """
from old_parse_scripts import old_parse
Script . old_parse = old_parse
2012-04-17 18:22:28 -07:00
2012-03-25 11:53:21 -07:00
def parse_script_engine_script_at ( address , map_group = None , map_id = None , force = False , debug = True , origin = True ) :
if is_script_already_parsed_at ( address ) and not force :
return script_parse_table [ address ]
return Script ( address , map_group = map_group , map_id = map_id , force = force , debug = debug , origin = origin )
2012-03-07 14:59:47 -08:00
2012-04-08 13:41:52 -07:00
def compare_script_parsing_methods ( address ) :
"""
compares the parsed scripts using the new method and the old method
The new method is Script . parse , the old method is Script . old_parse .
There are likely to be problems with the new script parser , the one
that uses the command classes to parse bytes . To look for these
problems , you can compare the output of one parsing method to the
output of the other . When there ' s a difference, there is something
worth correcting . Probably by each command ' s " macro_name " attribute.
"""
2012-04-08 13:52:36 -07:00
load_rom ( )
separator = " ################ compare_script_parsing_methods "
#first do it the old way
print separator
print " parsing the script at " + hex ( address ) + " using the old method "
oldscript = Script ( address , debug = True , force = True , origin = True , use_old_parse = True )
#and now the old way
print separator
print " parsing the script at " + hex ( address ) + " using the new method "
newscript = Script ( address , debug = True , force = True , origin = True )
#let the comparison begin..
errors = 0
print separator + " COMPARISON RESULTS "
if not len ( oldscript . commands . keys ( ) ) == len ( newscript . commands ) :
print " the two scripts don ' t have the same number of commands "
errors + = 1
for ( id , oldcommand ) in oldscript . commands . items ( ) :
newcommand = newscript . commands [ id ]
oldcommand_pksv_name = pksv_crystal [ oldcommand [ " type " ] ] . replace ( " " , " _ " )
if oldcommand [ " start_address " ] != newcommand . address :
print " the two addresses (command id= " + str ( id ) + " ) do not match old= " + hex ( oldcommand [ " start_address " ] ) + " new= " + hex ( newcommand . address )
errors + = 1
if oldcommand_pksv_name != newcommand . macro_name :
print " the two commands (id= " + str ( id ) + " ) do not have the same name old= " + oldcommand_pksv_name + " new= " + newcommand . macro_name
errors + = 1
print " total comparison errors: " + str ( errors )
return oldscript , newscript
2012-04-08 13:41:52 -07:00
2012-04-17 18:22:28 -07:00
2012-04-14 11:01:20 -07:00
class Warp ( Command ) :
""" only used outside of scripts """
size = warp_byte_size
2012-04-26 08:27:46 -07:00
macro_name = " warp_def "
2012-04-14 11:01:20 -07:00
param_types = {
0 : { " name " : " y " , " class " : HexByte } ,
1 : { " name " : " x " , " class " : HexByte } ,
2 : { " name " : " warp_to " , " class " : DecimalParam } ,
3 : { " name " : " map_bank " , " class " : MapGroupParam } ,
4 : { " name " : " map_id " , " class " : MapIdParam } ,
}
override_byte_check = True
2012-04-17 18:22:28 -07:00
2012-04-14 11:01:20 -07:00
def __init__ ( self , * args , * * kwargs ) :
self . id = kwargs [ " id " ]
script_parse_table [ kwargs [ " address " ] : kwargs [ " address " ] + self . size ] = self
Command . __init__ ( self , * args , * * kwargs )
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-20 20:19:01 -07:00
return [ ]
2012-04-14 11:01:20 -07:00
all_warps = [ ]
def parse_warps ( address , warp_count , bank = None , map_group = None , map_id = None , debug = True ) :
2012-03-05 22:15:35 -08:00
warps = [ ]
2012-04-14 11:01:20 -07:00
current_address = address
for each in range ( warp_count ) :
2012-04-14 15:58:15 -07:00
warp = Warp ( address = current_address , id = each , bank = bank , map_group = map_group , map_id = map_id , debug = debug )
2012-04-14 11:01:20 -07:00
current_address + = warp_byte_size
warps . append ( warp )
all_warps . extend ( warps )
2012-03-05 22:15:35 -08:00
return warps
2012-04-14 11:01:20 -07:00
2012-04-15 12:33:41 -07:00
def old_parse_warp_bytes ( some_bytes , debug = True ) :
""" parse some number of warps from the data """
assert len ( some_bytes ) % warp_byte_size == 0 , " wrong number of bytes "
warps = [ ]
for bytes in grouper ( some_bytes , count = warp_byte_size ) :
y = int ( bytes [ 0 ] , 16 )
x = int ( bytes [ 1 ] , 16 )
warp_to = int ( bytes [ 2 ] , 16 )
map_group = int ( bytes [ 3 ] , 16 )
map_id = int ( bytes [ 4 ] , 16 )
warps . append ( {
" y " : y ,
" x " : x ,
" warp_to " : warp_to ,
" map_group " : map_group ,
" map_id " : map_id ,
} )
return warps
2012-04-14 15:58:15 -07:00
class XYTrigger ( Command ) :
size = trigger_byte_size
2012-04-26 08:27:46 -07:00
macro_name = " xy_trigger "
2012-04-14 15:58:15 -07:00
param_types = {
0 : { " name " : " number " , " class " : DecimalParam } ,
1 : { " name " : " y " , " class " : HexByte } ,
2 : { " name " : " x " , " class " : HexByte } ,
3 : { " name " : " unknown1 " , " class " : SingleByteParam } ,
4 : { " name " : " script " , " class " : ScriptPointerLabelParam } ,
5 : { " name " : " unknown2 " , " class " : SingleByteParam } ,
6 : { " name " : " unknown3 " , " class " : SingleByteParam } ,
}
override_byte_check = True
2012-04-17 18:22:28 -07:00
2012-04-14 15:58:15 -07:00
def __init__ ( self , * args , * * kwargs ) :
self . id = kwargs [ " id " ]
2012-04-27 11:41:44 -07:00
self . dependencies = None
2012-04-14 15:58:15 -07:00
Command . __init__ ( self , * args , * * kwargs )
2012-05-20 01:39:25 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-20 20:19:01 -07:00
dependencies = [ ]
2012-04-27 11:41:44 -07:00
if self . dependencies != None and not recompute :
2012-04-27 12:58:57 -07:00
global_dependencies . update ( self . dependencies )
2012-04-27 11:41:44 -07:00
return self . dependencies
2012-04-20 20:19:01 -07:00
thing = script_parse_table [ self . params [ 4 ] . parsed_address ]
2012-04-21 16:09:03 -07:00
if thing and thing != self . params [ 4 ] :
2012-04-20 20:19:01 -07:00
dependencies . append ( thing )
2012-04-27 12:58:57 -07:00
global_dependencies . add ( thing )
2012-04-27 11:41:44 -07:00
self . dependencies = dependencies
2012-04-20 20:19:01 -07:00
return dependencies
2012-04-14 15:58:15 -07:00
all_xy_triggers = [ ]
def parse_xy_triggers ( address , trigger_count , bank = None , map_group = None , map_id = None , debug = True ) :
xy_triggers = [ ]
current_address = address
for each in range ( trigger_count ) :
xy_trigger = XYTrigger ( address = current_address , id = each , bank = bank , map_group = map_group , map_id = map_id , debug = debug )
current_address + = trigger_byte_size
xy_triggers . append ( xy_trigger )
all_xy_triggers . extend ( xy_triggers )
return xy_triggers
def old_parse_xy_trigger_bytes ( some_bytes , bank = None , map_group = None , map_id = None , debug = True ) :
2012-03-05 22:15:35 -08:00
""" parse some number of triggers from the data """
assert len ( some_bytes ) % trigger_byte_size == 0 , " wrong number of bytes "
triggers = [ ]
for bytes in grouper ( some_bytes , count = trigger_byte_size ) :
trigger_number = int ( bytes [ 0 ] , 16 )
y = int ( bytes [ 1 ] , 16 )
x = int ( bytes [ 2 ] , 16 )
unknown1 = int ( bytes [ 3 ] , 16 ) #XXX probably 00?
script_ptr_byte1 = int ( bytes [ 4 ] , 16 )
script_ptr_byte2 = int ( bytes [ 5 ] , 16 )
script_ptr = script_ptr_byte1 + ( script_ptr_byte2 << 8 )
script_address = None
script = None
if bank :
script_address = calculate_pointer ( script_ptr , bank )
2012-03-07 20:31:59 -08:00
print " ******* parsing xy trigger byte scripts... x= " + str ( x ) + " y= " + str ( y )
script = parse_script_engine_script_at ( script_address , map_group = map_group , map_id = map_id )
2012-04-17 18:22:28 -07:00
2012-03-05 22:15:35 -08:00
triggers . append ( {
" trigger_number " : trigger_number ,
" y " : y ,
" x " : x ,
" unknown1 " : unknown1 , #probably 00
" script_ptr " : script_ptr ,
" script_pointer " : { " 1 " : script_ptr_byte1 , " 2 " : script_ptr_byte2 } ,
" script_address " : script_address ,
" script " : script ,
} )
return triggers
2012-04-14 13:16:09 -07:00
2012-04-17 18:22:28 -07:00
2012-04-14 13:16:09 -07:00
class ItemFragment ( Command ) :
""" used by ItemFragmentParam and PeopleEvent
( for items placed on a map ) """
size = 2
2012-05-16 19:16:28 -07:00
macro_name = " db "
2012-04-14 13:16:09 -07:00
base_label = " ItemFragment_ "
override_byte_check = True
param_types = {
0 : { " name " : " item " , " class " : ItemLabelByte } ,
1 : { " name " : " quantity " , " class " : DecimalParam } ,
}
2012-04-17 18:22:28 -07:00
2012-04-14 13:48:26 -07:00
def __init__ ( self , address = None , bank = None , map_group = None , map_id = None , debug = False , label = None ) :
2012-04-14 13:16:09 -07:00
assert is_valid_address ( address ) , " PeopleEvent must be given a valid address "
self . address = address
self . last_address = address + self . size
self . bank = bank
2012-04-26 16:41:13 -07:00
if not label :
label = self . base_label + hex ( address )
self . label = Label ( name = label , address = address , object = self )
2012-04-14 13:16:09 -07:00
self . map_group = map_group
self . map_id = map_id
self . debug = debug
2012-04-14 13:48:26 -07:00
self . params = { }
2012-04-27 11:41:44 -07:00
self . dependencies = None
2012-04-14 13:48:26 -07:00
self . args = { " debug " : debug , " map_group " : map_group , " map_id " : map_id , " bank " : bank }
2012-04-14 13:16:09 -07:00
script_parse_table [ self . address : self . last_address ] = self
self . parse ( )
2012-04-17 18:22:28 -07:00
2012-04-14 13:48:26 -07:00
class ItemFragmentParam ( PointerLabelParam ) :
2012-04-14 13:16:09 -07:00
""" used by PeopleEvent """
2012-04-17 18:22:28 -07:00
2012-04-14 13:16:09 -07:00
def parse ( self ) :
PointerLabelParam . parse ( self )
2012-05-20 01:39:25 -07:00
2012-04-14 13:16:09 -07:00
address = calculate_pointer_from_bytes_at ( self . address , bank = self . bank )
2012-04-21 00:16:12 -07:00
self . calculated_address = address
2012-05-20 01:39:25 -07:00
2012-04-14 13:48:26 -07:00
itemfrag = ItemFragment ( address = address , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
2012-04-21 00:16:12 -07:00
self . itemfrag = itemfrag
2012-04-17 18:22:28 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-27 11:46:57 -07:00
if self . dependencies != None and not recompute :
2012-04-27 12:58:57 -07:00
global_dependencies . update ( self . dependencies )
2012-04-27 11:46:57 -07:00
return self . dependencies
2012-04-27 12:58:57 -07:00
self . dependencies = [ self . itemfrag ] . extend ( self . itemfrag . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
global_dependencies . add ( self . itemfrag )
2012-04-21 00:16:12 -07:00
return self . dependencies
2012-04-17 18:22:28 -07:00
2012-05-18 19:31:51 -07:00
trainer_group_maximums = { }
2012-04-14 13:16:09 -07:00
class TrainerFragment ( Command ) :
""" used by TrainerFragmentParam and PeopleEvent for trainer data
2012-05-20 01:39:25 -07:00
2012-04-21 00:16:12 -07:00
Maybe this shouldn ' t be a Command. The output might sprawl
over multiple lines , and maybe it should be commented in to_asm ?
2012-05-20 01:39:25 -07:00
2012-03-11 23:09:25 -07:00
[ Bit no . ( 2 byte ) ] [ Trainer group ] [ Trainer ]
[ 2 byte pointer to Text when seen ]
[ 2 byte pointer to text when trainer beaten ]
[ 2 byte pointer to script when lost ( 0000 = Blackout ) ]
[ 2 byte pointer to script if won / talked to again ]
2012-04-17 18:22:28 -07:00
2012-03-11 23:09:25 -07:00
The bit number tell the game later on if the trainer has been
beaten already ( bit = 1 ) or not ( bit = 0 ) . All Bit number of BitTable1 .
2012-04-17 18:22:28 -07:00
2012-03-11 23:09:25 -07:00
03 = Nothing
04 = Nothing
05 = Nothing
06 = Nothing
"""
2012-04-14 13:16:09 -07:00
size = 12
macro_name = " trainer_def "
base_label = " Trainer_ "
override_byte_check = True
param_types = {
0 : { " name " : " bit_number " , " class " : MultiByteParam } ,
1 : { " name " : " trainer_group " , " class " : TrainerGroupParam } ,
2 : { " name " : " trainer_id " , " class " : TrainerIdParam } ,
3 : { " name " : " text_when_seen " , " class " : TextPointerLabelParam } ,
4 : { " name " : " text_when_trainer_beaten " , " class " : TextPointerLabelParam } ,
5 : { " name " : " script_when_lost " , " class " : ScriptPointerLabelParam } ,
6 : { " name " : " script_talk_again " , " class " : ScriptPointerLabelParam } ,
}
2012-04-17 18:22:28 -07:00
2012-04-14 13:16:09 -07:00
def __init__ ( self , * args , * * kwargs ) :
2012-04-14 13:48:26 -07:00
address = kwargs [ " address " ]
print " TrainerFragment address= " + hex ( address )
2012-04-21 16:24:54 -07:00
self . address = address
2012-04-14 13:16:09 -07:00
self . last_address = self . address + self . size
2012-04-21 16:24:54 -07:00
if not is_valid_address ( address ) or address in [ 0x26ef ] :
self . include_in_asm = False
return
2012-04-14 13:16:09 -07:00
script_parse_table [ self . address : self . last_address ] = self
2012-04-27 11:41:44 -07:00
self . dependencies = None
2012-04-21 16:24:54 -07:00
Command . __init__ ( self , * args , * * kwargs )
2012-05-20 01:39:25 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-21 00:16:12 -07:00
deps = [ ]
2012-04-21 16:24:54 -07:00
if not is_valid_address ( self . address ) : return deps
2012-04-27 11:41:44 -07:00
if self . dependencies != None and not recompute :
2012-04-27 12:58:57 -07:00
global_dependencies . update ( self . dependencies )
2012-04-27 11:41:44 -07:00
return self . dependencies
2012-04-27 12:58:57 -07:00
#deps.append(self.params[3])
deps . extend ( self . params [ 3 ] . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
#deps.append(self.params[4])
deps . extend ( self . params [ 4 ] . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
#deps.append(self.params[5])
deps . extend ( self . params [ 5 ] . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
#deps.append(self.params[6])
deps . extend ( self . params [ 6 ] . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
2012-04-27 13:02:53 -07:00
self . dependencies = deps
2012-04-21 00:16:12 -07:00
return deps
2012-04-17 18:22:28 -07:00
2012-05-18 19:31:51 -07:00
def parse ( self ) :
Command . parse ( self )
# get the trainer group id
trainer_group = self . params [ 1 ] . byte
# get the trainer id
trainer_id = self . params [ 2 ] . byte
if not trainer_group in trainer_group_maximums . keys ( ) :
2012-05-19 11:57:19 -07:00
trainer_group_maximums [ trainer_group ] = set ( [ trainer_id ] )
2012-05-18 23:44:44 -07:00
else :
2012-05-19 11:57:19 -07:00
trainer_group_maximums [ trainer_group ] . add ( trainer_id )
2012-05-18 19:31:51 -07:00
2012-05-20 02:29:19 -07:00
# give this object a possibly better label
label = " Trainer "
2012-05-22 09:44:42 -07:00
if ( " uses_numeric_trainer_ids " in trainer_group_names [ trainer_group ] . keys ( ) ) \
or ( " trainer_names " not in trainer_group_names [ trainer_group ] . keys ( ) ) :
2012-05-20 23:34:21 -07:00
label + = string . capwords ( trainer_group_names [ trainer_group ] [ " constant " ] )
2012-05-22 09:44:42 -07:00
if " trainer_names " in trainer_group_names [ trainer_group ] . keys ( ) \
and len ( trainer_group_names [ trainer_group ] [ " trainer_names " ] ) > 1 :
2012-05-20 23:34:21 -07:00
label + = str ( trainer_id )
2012-05-20 02:11:37 -07:00
else :
label + = string . capwords ( trainer_group_names [ trainer_group ] [ " constant " ] ) + \
string . capwords ( trainer_group_names [ trainer_group ] [ " trainer_names " ] [ trainer_id - 1 ] )
2012-05-20 23:34:21 -07:00
label = label . replace ( " Gruntm " , " GruntM " ) . replace ( " Gruntf " , " GruntF " ) . replace ( " Lt_surge " , " LtSurge " )
2012-05-20 02:11:37 -07:00
self . label = Label ( name = label , address = self . address , object = self )
2012-05-20 02:29:19 -07:00
# ---- give better labels to the objects created by TrainerFragment ----
text_when_seen_text = script_parse_table [ self . params [ 3 ] . parsed_address ]
if text_when_seen_text != None :
text_when_seen_label = Label ( name = label + " WhenSeenText " , address = text_when_seen_text . address , object = text_when_seen_text )
text_when_seen_text . label = text_when_seen_label
text_when_beaten_text = script_parse_table [ self . params [ 4 ] . parsed_address ]
if text_when_beaten_text != None :
text_when_beaten_label = Label ( name = label + " WhenBeatenText " , address = text_when_beaten_text . address , object = text_when_beaten_text )
text_when_beaten_text . label = text_when_beaten_label
script_when_lost = script_parse_table [ self . params [ 5 ] . parsed_address ]
if script_when_lost != None :
script_when_lost_label = Label ( name = label + " WhenLostScript " , address = script_when_lost . address , object = script_when_lost )
script_when_lost . label = script_when_lost_label
script_talk_again = script_parse_table [ self . params [ 6 ] . parsed_address ]
if script_talk_again != None :
script_talk_again_label = Label ( name = label + " WhenTalkScript " , address = script_talk_again . address , object = script_talk_again )
script_talk_again . label = script_talk_again_label
2012-04-21 00:25:56 -07:00
def to_asm ( self ) :
xspacing = " "
output = " "
output + = xspacing + " ; bit/flag number \n "
2012-04-22 18:49:00 -07:00
output + = xspacing + " dw $ %.2x " % ( self . params [ 0 ] . parsed_number )
2012-04-21 00:25:56 -07:00
output + = " \n \n " + xspacing + " ; trainer group && trainer id \n "
2012-05-20 01:33:35 -07:00
output + = xspacing + " db %s , %s " % ( self . params [ 1 ] . to_asm ( ) , self . params [ 2 ] . to_asm ( ) )
2012-04-21 00:25:56 -07:00
output + = " \n \n " + xspacing + " ; text when seen \n "
output + = xspacing + " dw " + self . params [ 3 ] . to_asm ( )
output + = " \n \n " + xspacing + " ; text when trainer beaten \n "
output + = xspacing + " dw " + self . params [ 4 ] . to_asm ( )
output + = " \n \n " + xspacing + " ; script when lost \n "
output + = xspacing + " dw " + self . params [ 5 ] . to_asm ( )
output + = " \n \n " + xspacing + " ; script when talk again \n "
output + = xspacing + " dw " + self . params [ 6 ] . to_asm ( )
return output
2012-04-14 13:16:09 -07:00
class TrainerFragmentParam ( PointerLabelParam ) :
""" used by PeopleEvent to point to trainer data """
def parse ( self ) :
address = calculate_pointer_from_bytes_at ( self . address , bank = self . bank )
2012-04-21 00:16:12 -07:00
self . calculated_address = address
2012-04-21 16:24:54 -07:00
if address == 0x26ef :
2012-04-27 12:58:57 -07:00
self . trainerfrag = None
2012-04-21 16:24:54 -07:00
else :
trainerfrag = TrainerFragment ( address = address , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
2012-04-27 12:58:57 -07:00
self . trainerfrag = trainerfrag
2012-04-14 13:48:26 -07:00
PointerLabelParam . parse ( self )
2012-04-17 18:22:28 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-21 00:16:12 -07:00
deps = [ ]
2012-04-27 11:41:44 -07:00
if self . dependencies != None and not recompute :
2012-04-27 12:58:57 -07:00
global_dependencies . update ( self . dependencies )
2012-04-27 11:41:44 -07:00
return self . dependencies
2012-04-27 12:58:57 -07:00
if self . trainerfrag :
global_dependencies . add ( self . trainerfrag )
deps . append ( self . trainerfrag )
deps . extend ( self . trainerfrag . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
2012-04-27 11:41:44 -07:00
self . dependencies = deps
2012-04-21 00:16:12 -07:00
return deps
2012-04-17 18:22:28 -07:00
2012-05-19 14:39:36 -07:00
trainer_group_table = None
2012-05-19 13:43:26 -07:00
class TrainerGroupTable :
""" A list of pointers.
2012-05-19 19:13:57 -07:00
This should probably be called TrainerGroupPointerTable .
2012-05-19 13:43:26 -07:00
"""
def __init__ ( self ) :
assert 0x43 in trainer_group_maximums . keys ( ) , " TrainerGroupTable should onyl be created after all the trainers have been found "
self . address = trainer_group_pointer_table_address
self . bank = calculate_bank ( trainer_group_pointer_table_address )
self . label = Label ( name = " TrainerGroupPointerTable " , address = self . address , object = self )
self . size = None
self . last_address = None
self . dependencies = None
self . headers = [ ]
self . parse ( )
2012-05-20 07:32:59 -07:00
script_parse_table [ self . address : self . last_address ] = self
2012-05-19 13:43:26 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
global_dependencies . update ( self . headers )
2012-05-20 01:39:25 -07:00
if recompute == True and self . dependencies != None and self . dependencies != [ ] :
2012-05-19 13:43:26 -07:00
return self . dependencies
2012-05-19 19:13:57 -07:00
dependencies = copy ( self . headers )
2012-05-19 13:43:26 -07:00
for header in self . headers :
2012-05-19 19:13:57 -07:00
dependencies . extend ( header . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
2012-05-19 13:43:26 -07:00
return dependencies
def parse ( self ) :
2012-05-19 14:39:36 -07:00
size = 0
2012-05-19 13:43:26 -07:00
for ( key , kvalue ) in trainer_group_names . items ( ) :
# calculate the location of this trainer group header from its pointer
pointer_bytes_location = kvalue [ " pointer_address " ]
parsed_address = calculate_pointer_from_bytes_at ( pointer_bytes_location , bank = self . bank )
trainer_group_names [ key ] [ " parsed_address " ] = parsed_address
# parse the trainer group header at this location
name = kvalue [ " name " ]
trainer_group_header = TrainerGroupHeader ( address = parsed_address , group_id = key , group_name = name )
trainer_group_names [ key ] [ " header " ] = trainer_group_header
self . headers . append ( trainer_group_header )
# keep track of the size of this pointer table
size + = 2
self . size = size
self . last_address = self . address + self . size
def to_asm ( self ) :
output = " " . join ( [ str ( " dw " + get_label_for ( header . address ) + " \n " ) for header in self . headers ] )
return output
class TrainerGroupHeader :
"""
A trainer group header is a repeating list of individual trainer headers .
< Trainer Name > < 0x50 > < Data type > < Pokémon Data > + < 0xFF >
Data type < 0x00 > : Pokémon Data is < Level > < Species > . Used by most trainers .
Data type < 0x01 > : Pokémon Data is < Level > < Pokémon > < Move1 > < Move2 > < Move3 > < Move4 > . Used often for Gym Leaders .
Data type < 0x02 > : Pokémon Data is < Level > < Pokémon > < Held Item > . Used mainly by Pokéfans .
Data type < 0x03 > : Pokémon Data is < Level > < Pokémon > < Held Item > < Move1 > < Move2 > < Move3 > < Move4 > . Used by a few Cooltrainers .
"""
def __init__ ( self , address = None , group_id = None , group_name = None ) :
assert address != None , " TrainerGroupHeader requires an address "
assert group_id != None , " TrainerGroupHeader requires a group_id "
assert group_name != None , " TrainerGroupHeader requires a group_name "
self . address = address
self . group_id = group_id
self . group_name = group_name
self . dependencies = None
self . individual_trainer_headers = [ ]
self . label = Label ( name = group_name + " TrainerGroupHeader " , address = self . address , object = self )
self . parse ( )
2012-05-19 15:30:49 -07:00
script_parse_table [ address : self . last_address ] = self
2012-05-19 13:43:26 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
""" TrainerGroupHeader has no dependencies.
"""
# TODO: possibly include self.individual_trainer_headers
if recompute or self . dependencies == None :
self . dependencies = [ ]
return self . dependencies
def parse ( self ) :
"""
how do i know when there ' s no more data for this header?
do a global analysis of the rom and figure out the max ids
this wont work for rom hacks of course
see find_trainer_ids_from_scripts
"""
size = 0
current_address = self . address
2012-05-19 14:39:36 -07:00
if self . group_id not in trainer_group_maximums . keys ( ) :
self . size = 0
self . last_address = current_address
return
2012-05-19 13:43:26 -07:00
# create an IndividualTrainerHeader for each id in range(min id, max id + 1)
min_id = min ( trainer_group_maximums [ self . group_id ] )
max_id = max ( trainer_group_maximums [ self . group_id ] )
2012-05-19 16:28:23 -07:00
if self . group_id == 0x0C :
# CAL appears a third time with third-stage evos (meganium, typhlosion, feraligatr)
max_id + = 1
2012-05-19 16:33:42 -07:00
elif self . group_id == 0x29 :
# there's a missing supernerd :(
max_id + = 1
2012-05-19 16:36:11 -07:00
elif self . group_id == 0x2D :
# missing bikers
max_id + = 2
2012-05-19 16:42:46 -07:00
elif self . group_id == 0x31 :
# missing jugglers
max_id + = 3
2012-05-19 16:44:00 -07:00
elif self . group_id == 0x32 :
# blackbelt wai
max_id + = 1
2012-05-19 16:52:23 -07:00
elif self . group_id == 0x3C :
# kimono girl miki
max_id + = 1
2012-05-19 16:54:32 -07:00
elif self . group_id == 0x3D :
# twins lea & pia
max_id + = 1
2012-05-19 16:28:23 -07:00
2012-05-19 13:43:26 -07:00
for trainer_id in range ( min_id , max_id + 1 ) :
trainer_header = TrainerHeader ( address = current_address , trainer_group_id = self . group_id , trainer_id = trainer_id , parent = self )
2012-05-19 14:39:36 -07:00
self . individual_trainer_headers . append ( trainer_header )
#current_address += trainer_header.size
current_address = trainer_header . last_address
2012-05-19 13:43:26 -07:00
size + = trainer_header . size
self . last_address = current_address
self . size = size
def to_asm ( self ) :
2012-05-20 01:20:01 -07:00
output = " \n \n " . join ( [ " ; " + header . make_constant_name ( ) + " ( " + str ( header . trainer_id ) + " ) at " + hex ( header . address ) + " \n " + header . to_asm ( ) for header in self . individual_trainer_headers ] )
2012-05-19 14:39:36 -07:00
return output
2012-05-19 13:43:26 -07:00
class TrainerHeader :
"""
< Trainer Name > < 0x50 > < Data type > < Pokémon Data > + < 0xFF >
Data type < 0x00 > : Pokémon Data is < Level > < Species > . Used by most trainers .
Data type < 0x01 > : Pokémon Data is < Level > < Pokémon > < Move1 > < Move2 > < Move3 > < Move4 > . Used often for Gym Leaders .
Data type < 0x02 > : Pokémon Data is < Level > < Pokémon > < Held Item > . Used mainly by Pokéfans .
Data type < 0x03 > : Pokémon Data is < Level > < Pokémon > < Held Item > < Move1 > < Move2 > < Move3 > < Move4 > . Used by a few Cooltrainers .
"""
def __init__ ( self , address = None , trainer_group_id = None , trainer_id = None , parent = None ) :
self . parent = parent
self . address = address
self . trainer_group_id = trainer_group_id
self . trainer_id = trainer_id
self . dependencies = [ ]
self . size = None
self . last_address = None
self . parse ( )
self . label = Label ( name = self . make_name ( ) , address = self . address , object = self )
# this shouldn't be added to script_parse_table because
# TrainerGroupHeader covers its address range
def make_name ( self ) :
""" Must occur after parse() is called.
Constructs a name based on self . parent . group_name and self . name .
"""
2012-05-19 14:39:36 -07:00
if self . trainer_group_id in [ 0x14 , 0x16 , 0x17 , 0x18 , 0x19 , 0x1B , 0x1C , 0x1D , 0x1E , 0x20 , 0x21 , 0x22 , 0x24 , 0x25 , 0x26 , 0x27 , 0x28 , 0x29 , 0x2B , 0x2C , 0x2D , 0x2F , 0x30 , 0x31 , 0x32 , 0x34 , 0x35 , 0x36 , 0x38 , 0x39 , 0x3A , 0x3B , 0x3C , 0x3D , 0x3E , 0x41 ] :
return self . parent . group_name . upper ( ) + " _ " + self . name [ : - 1 ]
else :
return self . parent . group_name + " _ " + str ( self . trainer_id )
2012-05-19 13:43:26 -07:00
2012-05-19 22:02:43 -07:00
def make_constant_name ( self ) :
if hasattr ( self , " seed_constant_name " ) :
seed = self . seed_constant_name
else :
seed = self . name
2012-05-20 00:15:40 -07:00
if " ? " in seed :
2012-05-20 00:21:26 -07:00
if seed [ - 2 ] . isdigit ( ) :
x = 2
else :
x = 1
seed = trainer_group_names [ self . trainer_group_id ] [ " name " ] + " _ " + seed [ - x : ]
2012-05-20 00:15:40 -07:00
elif self . trainer_group_id == 0x1f and " EXECUTIVE " in seed :
seed = " GRUNT_ " + seed
2012-05-20 00:50:21 -07:00
elif self . trainer_group_id == 0x2d and " BENNY " in seed . upper ( ) :
seed = " BIKER_BENNY "
elif self . trainer_group_id == 0x24 and " BENNY " in seed . upper ( ) :
seed = " BUG_CATCHER_BENNY "
2012-05-20 00:15:40 -07:00
2012-05-19 22:02:43 -07:00
return string . capwords ( seed ) . \
replace ( " @ " , " " ) . \
replace ( " & " , " AND " ) . \
replace ( " " , " " ) . \
replace ( " . " , " _ " ) . \
upper ( )
2012-05-19 13:43:26 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
if recompute or self . dependencies == None :
self . dependencies = [ ]
return self . dependencies
def parse ( self ) :
address = self . address
# figure out how many bytes until 0x50 "@"
jump = how_many_until ( chr ( 0x50 ) , address )
# parse the "@" into the name
self . name = parse_text_at ( address , jump + 1 )
# where is the next byte?
current_address = address + jump + 1
# figure out the pokemon data type
self . data_type = ord ( rom [ current_address ] )
current_address + = 1
# figure out which partymon parser to use for this trainer header
party_mon_parser = None
for monparser in trainer_party_mon_parsers :
if monparser . id == self . data_type :
party_mon_parser = monparser
break
if party_mon_parser == None :
raise Exception , " no trainer party mon parser found to parse data type " + hex ( self . data_type )
self . party_mons = party_mon_parser ( address = current_address , group_id = self . trainer_group_id , trainer_id = self . trainer_id , parent = self )
# let's have everything in trainer_party_mon_parsers handle the last $FF
2012-05-19 14:39:36 -07:00
#self.size = self.party_mons.size + 1 + len(self.name)
self . size = self . party_mons . last_address - self . address
2012-05-19 13:43:26 -07:00
self . last_address = self . party_mons . last_address
def to_asm ( self ) :
output = " db \" " + self . name + " \" \n "
2012-05-20 01:20:01 -07:00
output + = " db $ %.2x ; data type \n " % ( self . data_type )
2012-05-19 13:43:26 -07:00
output + = self . party_mons . to_asm ( )
2012-05-19 14:39:36 -07:00
output + = " \n ; last_address= " + hex ( self . last_address ) + " size= " + str ( self . size )
2012-05-19 13:43:26 -07:00
return output
class TrainerPartyMonParser :
""" Just a generic trainer party mon parser.
Don ' t use this directly. Only use the child classes.
"""
id = None
dependencies = None
param_types = None
# could go either way on this one.. TrainerGroupHeader.parse would need to be changed
# so as to not increase current_address by one after reading "data_type"
override_byte_check = True
def __init__ ( self , address = None , group_id = None , trainer_id = None , parent = None ) :
self . address = address
self . group_id = group_id
self . trainer_id = trainer_id
self . parent = parent
self . args = { }
2012-05-19 14:39:36 -07:00
self . mons = { }
2012-05-19 13:43:26 -07:00
self . parse ( )
# pick up the $FF at the end
self . last_address + = 1
def parse ( self ) :
2012-05-19 14:39:36 -07:00
current_address = self . address
pkmn = 0
continuer = True
while continuer :
self . mons [ pkmn ] = { }
i = 0
for ( key , param_type ) in self . param_types . items ( ) :
name = param_type [ " name " ]
klass = param_type [ " class " ]
#make an instance of this class, like SingleByteParam()
#or ItemLabelByte.. by making an instance, obj.parse() is called
obj = klass ( address = current_address , name = name , parent = self , * * dict ( [ ( k , v ) for ( k , v ) in self . args . items ( ) if k not in [ " parent " ] ] ) )
#save this for later
self . mons [ pkmn ] [ i ] = obj
#increment our counters
current_address + = obj . size
i + = 1
pkmn + = 1
if ord ( rom [ current_address ] ) == 0xFF :
break
2012-05-19 13:43:26 -07:00
self . last_address = current_address
return True
def to_asm ( self ) :
2012-05-20 01:20:01 -07:00
output = " "
#output = "; " + ", ".join([param_type["name"] for (key, param_type) in self.param_types.items()]) + "\n"
2012-05-19 14:39:36 -07:00
for mon in self . mons :
output + = " db " + " , " . join ( [ param . to_asm ( ) for ( name , param ) in self . mons [ mon ] . items ( ) ] )
output + = " \n "
output + = " db $ff ; end trainer party mons "
2012-05-19 13:43:26 -07:00
return output
class TrainerPartyMonParser0 ( TrainerPartyMonParser ) :
""" Data type <0x00>: Pokémon Data is <Level> <Species>. Used by most trainers. """
id = 0
size = 2 + 1
param_types = {
0 : { " name " : " level " , " class " : DecimalParam } ,
1 : { " name " : " species " , " class " : PokemonParam } ,
}
class TrainerPartyMonParser1 ( TrainerPartyMonParser ) :
""" Data type <0x01>: Pokémon Data is <Level> <Pokémon> <Move1> <Move2> <Move3> <Move4>. Used often for Gym Leaders. """
id = 1
size = 6 + 1
param_types = {
0 : { " name " : " level " , " class " : DecimalParam } ,
1 : { " name " : " species " , " class " : PokemonParam } ,
2 : { " name " : " move1 " , " class " : MoveParam } ,
3 : { " name " : " move2 " , " class " : MoveParam } ,
4 : { " name " : " move3 " , " class " : MoveParam } ,
5 : { " name " : " move4 " , " class " : MoveParam } ,
}
class TrainerPartyMonParser2 ( TrainerPartyMonParser ) :
""" Data type <0x02>: Pokémon Data is <Level> <Pokémon> <Held Item>. Used mainly by Pokéfans. """
id = 2
size = 3 + 1
param_types = {
0 : { " name " : " level " , " class " : DecimalParam } ,
1 : { " name " : " species " , " class " : PokemonParam } ,
2 : { " name " : " item " , " class " : ItemLabelByte } ,
}
class TrainerPartyMonParser3 ( TrainerPartyMonParser ) :
""" Data type <0x03>: Pokémon Data is <Level> <Pokémon> <Held Item> <Move1> <Move2> <Move3> <Move4>.
Used by a few Cooltrainers . """
id = 3
size = 7 + 1
param_types = {
0 : { " name " : " level " , " class " : DecimalParam } ,
1 : { " name " : " species " , " class " : PokemonParam } ,
2 : { " name " : " item " , " class " : ItemLabelByte } ,
3 : { " name " : " move1 " , " class " : MoveParam } ,
4 : { " name " : " move2 " , " class " : MoveParam } ,
5 : { " name " : " move3 " , " class " : MoveParam } ,
6 : { " name " : " move4 " , " class " : MoveParam } ,
}
trainer_party_mon_parsers = [ TrainerPartyMonParser0 , TrainerPartyMonParser1 , TrainerPartyMonParser2 , TrainerPartyMonParser3 ]
2012-05-18 19:53:17 -07:00
def find_trainer_ids_from_scripts ( ) :
""" Looks through all scripts to find trainer group numbers and trainer numbers.
This can be used with trainer_group_maximums to figure out the current number of
trainers in each of the originating trainer groups .
"""
2012-05-19 11:57:19 -07:00
total_unreferenced_trainers = 0
2012-05-18 19:53:17 -07:00
# look at each possibly relevant script
for item in script_parse_table . items ( ) :
object = item [ 1 ]
if isinstance ( object , Script ) :
check_script_has_trainer_data ( object )
2012-05-19 11:57:19 -07:00
# make a set of each list of trainer ids to avoid dupes
# this will be used later in TrainerGroupTable
for item in trainer_group_maximums . items ( ) :
key = item [ 0 ]
value = set ( item [ 1 ] )
trainer_group_maximums [ key ] = value
def report_unreferenced_trainer_ids ( ) :
""" Reports on the number of unreferenced trainer ids in each group.
This should be called after find_trainer_ids_from_scripts .
These are trainer groups with " unused " trainer ids . The
" find_trainer_ids_from_scripts " function analyzes each script in the game ,
and each map header in the game ( because of code in TrainerFragment ) , and
finds all references to trainers . But , if there are any trainers that are
referenced in raw ASM , this method does not detect them . Each instance of a
trainer reference is added to a global table called
" trainer_group_maximums " . Next , " find_trainer_ids_from_scripts " looks at
the trainer IDs referenced for each group and takes the minimum number and
the maximum number . To find whether or not there are any unused trainers ,
it takes the minimum and maximum ids and then sees which intermediate
numbers are missing from the list of " referenced " trainer ids .
"""
for item in trainer_group_maximums . items ( ) :
key = item [ 0 ]
value = item [ 1 ]
# i'm curious: are there any missing trainer ids in this group?
min_id = min ( value )
max_id = max ( value )
expectables = range ( min_id , max_id + 1 )
unreferenced = set ( )
for expectable in expectables :
if not expectable in value :
unreferenced . add ( expectable )
if len ( unreferenced ) > 0 :
total_unreferenced_trainers + = len ( unreferenced )
output = " trainer group " + hex ( key ) + " ( \" " + trainer_group_names [ key ] [ " name " ] + " \" ) "
output + = " (min= " + str ( min_id ) + " , max= " + str ( max_id ) + " ) "
output + = " has " + str ( len ( unreferenced ) ) + " unreferenced trainer ids "
output + = " : " + str ( unreferenced )
print output
print " total unreferenced trainers: " + str ( total_unreferenced_trainers )
2012-05-18 19:53:17 -07:00
def check_script_has_trainer_data ( script ) :
""" see find_trainer_ids_from_scripts
"""
for command in script . commands :
trainer_group = None
trainer_id = None
if command . id == 0x43 :
2012-05-18 23:44:44 -07:00
trainer_group = command . params [ 0 ] . byte
trainer_id = command . params [ 1 ] . byte
2012-05-18 19:53:17 -07:00
elif command . id == 0x5E :
trainer_group = command . params [ 0 ] . byte
trainer_id = command . params [ 1 ] . byte
if trainer_group != None and trainer_id != None :
if trainer_group in trainer_group_maximums . keys ( ) :
2012-05-19 11:57:19 -07:00
trainer_group_maximums [ trainer_group ] . add ( trainer_id )
2012-05-18 19:53:17 -07:00
else :
2012-05-19 11:57:19 -07:00
trainer_group_maximums [ trainer_group ] = set ( [ trainer_id ] )
2012-05-18 23:44:44 -07:00
def trainer_name_from_group ( group_id , trainer_id = 0 ) :
""" This doesn ' t actually work for trainer_id > 0. """
bank = calculate_bank ( 0x39999 )
ptr_address = 0x39999 + ( ( group_id - 1 ) * 2 )
address = calculate_pointer_from_bytes_at ( ptr_address , bank = bank )
text = parse_text_at2 ( address , how_many_until ( chr ( 0x50 ) , address ) )
return text
def trainer_group_report ( ) :
2012-05-19 13:30:02 -07:00
""" Reports how many trainer ids are used in each trainer group.
"""
2012-05-18 23:44:44 -07:00
output = " "
total = 0
for trainer_group_id in trainer_group_maximums . keys ( ) :
group_name = trainer_group_names [ trainer_group_id ] [ " name " ]
first_name = trainer_name_from_group ( trainer_group_id ) . replace ( " \n " , " " )
2012-05-19 13:30:02 -07:00
trainers = len ( trainer_group_maximums [ trainer_group_id ] )
2012-05-18 23:44:44 -07:00
total + = trainers
output + = " group " + hex ( trainer_group_id ) + " : \n "
output + = " \t name: " + group_name + " \n "
output + = " \t first: " + first_name + " \n "
output + = " \t trainer count: \t " + str ( trainers ) + " \n \n "
output + = " total trainers: " + str ( total )
return output
2012-05-18 19:53:17 -07:00
2012-05-20 07:32:59 -07:00
def make_trainer_group_name_trainer_ids ( trainer_group_table , debug = True ) :
2012-05-19 22:02:43 -07:00
""" Edits trainer_group_names and sets the trainer names.
For instance , " AMY & MAY " becomes " AMY_AND_MAY1 " and " AMY_AND_MAY2 "
This should only be used after TrainerGroupTable . parse has been called .
"""
assert trainer_group_table != None , " TrainerGroupTable must be called before setting the trainer names "
if debug :
print " starting to make trainer names and give ids to repeated trainer names "
2012-05-19 22:20:31 -07:00
i = 1
2012-05-19 22:02:43 -07:00
for header in trainer_group_table . headers :
trainer_names = [ ] # (name, trainer_header)
dupes = set ( )
2012-05-20 00:15:40 -07:00
group_id = i
2012-05-19 22:02:43 -07:00
group_name = header . group_name
for trainer_header in header . individual_trainer_headers :
if trainer_header . name in [ x [ 0 ] for x in trainer_names ] :
dupes . add ( trainer_header . name )
trainer_names . append ( [ trainer_header . name , trainer_header ] )
# now fix trainers with duplicate names by appending an id
if len ( dupes ) > 0 :
for dupe in dupes :
culprits = [ trainer_header for trainer_header in header . individual_trainer_headers if trainer_header . name == dupe ]
for ( id , culprit ) in enumerate ( culprits ) :
culprit . seed_constant_name = culprit . name . replace ( " @ " , " " ) + str ( id + 1 )
culprit . constant_name = culprit . make_constant_name ( )
# now add the trainer names to trainer_group_names
trainer_group_names [ i ] [ " trainer_names " ] = [ theader . make_constant_name ( ) for theader in header . individual_trainer_headers ]
2012-05-19 22:20:31 -07:00
i + = 1
2012-05-19 22:02:43 -07:00
if debug :
print " done improving trainer names "
2012-05-20 00:15:40 -07:00
def pretty_print_trainer_id_constants ( ) :
""" Prints out some constants for trainer ids, for " constants.asm " .
make_trainer_group_name_trainer_ids must be called prior to this .
"""
assert trainer_group_table != None , " must make trainer_group_table first "
assert trainer_group_names != None , " must have trainer_group_names available "
assert " trainer_names " in trainer_group_names [ 1 ] . keys ( ) , " trainer_names must be set in trainer_group_names "
output = " "
for ( key , value ) in trainer_group_names . items ( ) :
if " uses_numeric_trainer_ids " in trainer_group_names [ key ] . keys ( ) :
continue
id = key
group = value
header = group [ " header " ]
name = group [ " name " ]
trainer_names = group [ " trainer_names " ]
output + = " ; " + name + " \n "
for ( id , name ) in enumerate ( trainer_names ) :
output + = name . upper ( ) + " EQU $ %.2x " % ( id + 1 ) + " \n "
output + = " \n "
return output
2012-04-14 13:16:09 -07:00
class PeopleEvent ( Command ) :
size = people_event_byte_size
2012-04-26 08:27:46 -07:00
macro_name = " person_event "
2012-04-14 13:16:09 -07:00
base_label = " PeopleEvent_ "
override_byte_check = True
param_types = {
2012-05-20 02:11:37 -07:00
0 : { " name " : " sprite " , " class " : HexByte } ,
2012-04-14 13:16:09 -07:00
1 : { " name " : " y from top+4 " , " class " : DecimalParam } ,
2012-04-14 13:48:26 -07:00
2 : { " name " : " x from top+4 " , " class " : DecimalParam } ,
3 : { " name " : " facing " , " class " : HexByte } ,
4 : { " name " : " movement " , " class " : HexByte } ,
2012-04-14 13:16:09 -07:00
5 : { " name " : " clock_hour " , " class " : DecimalParam } ,
6 : { " name " : " clock_daytime " , " class " : DecimalParam } ,
2012-04-14 13:48:26 -07:00
7 : { " name " : " color_function " , " class " : HexByte } ,
2012-04-14 13:16:09 -07:00
8 : { " name " : " sight_range " , " class " : DecimalParam } ,
9 : { " name " : " pointer " , " class " : PointerLabelParam } , #or ScriptPointerLabelParam or ItemLabelParam
10 : { " name " : " BitTable1 bit number " , " class " : MultiByteParam } ,
}
2012-04-17 18:22:28 -07:00
2012-04-26 12:30:30 -07:00
def xto_asm ( self ) :
2012-04-25 23:07:04 -07:00
output = " \n ; person-event \n ; picture, y, x, facing, movement, clock_hour, clock_daytime, color_function, sight_range \n "
output + = " db $ %.2x , %d , %d , $ %.2x , $ %.2x , %d , %d , $ %.2x , %d \n " % ( self . params [ 0 ] . byte , self . params [ 1 ] . byte , self . params [ 2 ] . byte , self . params [ 3 ] . byte , self . params [ 4 ] . byte , self . params [ 5 ] . byte , self . params [ 6 ] . byte , self . params [ 7 ] . byte , self . params [ 8 ] . byte )
output + = " ; pointer \n dw %s \n " % ( self . params [ 9 ] . to_asm ( ) )
output + = " ; BitTable1 bit number \n dw %s " % ( self . params [ 10 ] . to_asm ( ) )
return output
2012-04-14 13:48:26 -07:00
def __init__ ( self , address , id , bank = None , map_group = None , map_id = None , debug = False , label = None , force = False ) :
2012-04-14 13:16:09 -07:00
assert is_valid_address ( address ) , " PeopleEvent must be given a valid address "
self . address = address
self . last_address = address + people_event_byte_size
self . id = id
self . bank = bank
2012-04-26 16:41:13 -07:00
if not label :
label = self . base_label + hex ( address )
self . label = Label ( name = label , address = address , object = self )
2012-04-14 13:16:09 -07:00
self . map_group = map_group
self . map_id = map_id
self . debug = debug
2012-04-14 13:48:26 -07:00
self . force = force
self . params = { }
2012-04-27 11:41:44 -07:00
self . dependencies = None
2012-04-14 15:58:15 -07:00
#PeopleEvent should probably not be in the global script_parse_table
2012-04-15 16:26:06 -07:00
#script_parse_table[self.address : self.last_address] = self
2012-04-14 13:16:09 -07:00
self . parse ( )
2012-04-17 18:22:28 -07:00
2012-04-14 13:16:09 -07:00
def parse ( self ) :
address = self . address
bank = self . bank
color_function_byte = None
lower_bits = None
higher_bits = None
is_regular_script = None
is_give_item = None
is_trainer = None
self . params = { }
current_address = self . address
i = 0
self . size = 1
color_function_byte = None
for ( key , param_type ) in self . param_types . items ( ) :
if i == 9 :
if is_give_item :
name = " item_fragment_pointer "
klass = ItemFragmentParam
elif is_regular_script :
name = " script_pointer "
klass = ScriptPointerLabelParam
elif is_trainer :
name = " trainer "
2012-04-14 13:48:26 -07:00
#klass = MultiByteParam
2012-04-14 13:16:09 -07:00
klass = TrainerFragmentParam
else :
name = " unknown "
klass = MultiByteParam
else :
name = param_type [ " name " ]
2012-04-14 13:48:26 -07:00
klass = param_type [ " class " ]
obj = klass ( address = current_address , name = name , debug = self . debug , force = self . force , map_group = self . map_group , map_id = self . map_id , bank = self . bank )
2012-04-14 13:16:09 -07:00
self . params [ i ] = obj
if i == 7 :
color_function_byte = ord ( rom [ current_address ] )
lower_bits = color_function_byte & 0xF
higher_bits = color_function_byte >> 4
is_regular_script = lower_bits == 00
is_give_item = lower_bits == 01
is_trainer = lower_bits == 02
current_address + = obj . size
self . size + = obj . size
i + = 1
self . last_address = current_address
self . is_trainer = is_trainer
self . is_give_item = is_give_item
self . is_regular_script = is_regular_script
2012-04-15 14:37:31 -07:00
self . y = self . params [ 1 ] . byte
self . x = self . params [ 2 ] . byte
self . facing = self . params [ 3 ] . byte
self . movement = self . params [ 4 ] . byte
self . clock_hour = self . params [ 5 ] . byte
self . clock_daytime = self . params [ 6 ] . byte
self . color_function = self . params [ 7 ] . byte
self . sight_range = self . params [ 8 ] . byte
self . pointer = self . params [ 9 ] . bytes
self . bit_number = self . params [ 10 ] . bytes
2012-04-14 13:16:09 -07:00
return True
2012-04-17 18:22:28 -07:00
2012-04-14 13:16:09 -07:00
all_people_events = [ ]
2012-04-14 13:48:26 -07:00
def parse_people_events ( address , people_event_count , bank = None , map_group = None , map_id = None , debug = False , force = False ) :
#people_event_byte_size
2012-04-14 13:16:09 -07:00
people_events = [ ]
current_address = address
id = 0
for each in range ( people_event_count ) :
2012-04-14 13:48:26 -07:00
pevent = PeopleEvent ( address = current_address , id = id , bank = bank , map_group = map_group , map_id = map_id , debug = debug , force = force )
2012-04-14 13:16:09 -07:00
current_address + = people_event_byte_size
people_events . append ( pevent )
id + = 1
2012-04-14 15:58:15 -07:00
all_people_events . extend ( people_events )
2012-03-05 22:15:35 -08:00
return people_events
2012-04-15 12:33:41 -07:00
def old_parse_people_event_bytes ( some_bytes , address = None , map_group = None , map_id = None , debug = True ) :
""" parse some number of people-events from the data
see http : / / hax . iimarck . us / files / scriptingcodes_eng . htm #Scripthdr
For example , map 1.1 ( group 1 map 1 ) has four person - events .
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
37 05 07 06 00 FF FF 00 00 02 40 FF FF
3 B 08 0 C 05 01 FF FF 00 00 05 40 FF FF
3 A 07 06 06 00 FF FF A0 00 08 40 FF FF
29 05 0 B 06 00 FF FF 00 00 0 B 40 FF FF
"""
assert len ( some_bytes ) % people_event_byte_size == 0 , " wrong number of bytes "
#address is not actually required for this function to work...
bank = None
if address :
bank = calculate_bank ( address )
people_events = [ ]
for bytes in grouper ( some_bytes , count = people_event_byte_size ) :
pict = int ( bytes [ 0 ] , 16 )
y = int ( bytes [ 1 ] , 16 ) #y from top + 4
x = int ( bytes [ 2 ] , 16 ) #x from left + 4
face = int ( bytes [ 3 ] , 16 ) #0-4 for regular, 6-9 for static facing
move = int ( bytes [ 4 ] , 16 )
clock_time_byte1 = int ( bytes [ 5 ] , 16 )
clock_time_byte2 = int ( bytes [ 6 ] , 16 )
color_function_byte = int ( bytes [ 7 ] , 16 ) #Color|Function
trainer_sight_range = int ( bytes [ 8 ] , 16 )
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
lower_bits = color_function_byte & 0xF
#lower_bits_high = lower_bits >> 2
#lower_bits_low = lower_bits & 3
higher_bits = color_function_byte >> 4
#higher_bits_high = higher_bits >> 2
#higher_bits_low = higher_bits & 3
is_regular_script = lower_bits == 00
#pointer points to script
is_give_item = lower_bits == 01
#pointer points to [Item no.][Amount]
is_trainer = lower_bits == 02
#pointer points to trainer header
#goldmap called these next two bytes "text_block" and "text_bank"?
script_pointer_byte1 = int ( bytes [ 9 ] , 16 )
script_pointer_byte2 = int ( bytes [ 10 ] , 16 )
script_pointer = script_pointer_byte1 + ( script_pointer_byte2 << 8 )
#calculate the full address by assuming it's in the current bank
#but what if it's not in the same bank?
extra_portion = { }
if bank :
ptr_address = calculate_pointer ( script_pointer , bank )
if is_regular_script :
print " parsing a person-script at x= " + str ( x - 4 ) + " y= " + str ( y - 4 ) + " address= " + hex ( ptr_address )
script = parse_script_engine_script_at ( ptr_address , map_group = map_group , map_id = map_id )
extra_portion = {
" script_address " : ptr_address ,
" script " : script ,
" event_type " : " script " ,
}
if is_give_item :
print " ... not parsing give item event... [item id][quantity] "
extra_portion = {
" event_type " : " give_item " ,
" give_item_data_address " : ptr_address ,
" item_id " : ord ( rom [ ptr_address ] ) ,
" item_qty " : ord ( rom [ ptr_address + 1 ] ) ,
}
if is_trainer :
print " parsing a trainer (person-event) at x= " + str ( x ) + " y= " + str ( y )
parsed_trainer = parse_trainer_header_at ( ptr_address , map_group = map_group , map_id = map_id )
extra_portion = {
" event_type " : " trainer " ,
" trainer_data_address " : ptr_address ,
" trainer_data " : parsed_trainer ,
}
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
#XXX not sure what's going on here
#bit no. of bit table 1 (hidden if set)
#note: FFFF for none
when_byte = int ( bytes [ 11 ] , 16 )
hide = int ( bytes [ 12 ] , 16 )
bit_number_of_bit_table1_byte2 = int ( bytes [ 11 ] , 16 )
bit_number_of_bit_table1_byte1 = int ( bytes [ 12 ] , 16 )
bit_number_of_bit_table1 = bit_number_of_bit_table1_byte1 + ( bit_number_of_bit_table1_byte2 << 8 )
people_event = {
" pict " : pict ,
" y " : y , #y from top + 4
" x " : x , #x from left + 4
" face " : face , #0-4 for regular, 6-9 for static facing
" move " : move ,
" clock_time " : { " 1 " : clock_time_byte1 ,
" 2 " : clock_time_byte2 } , #clock/time setting byte 1
" color_function_byte " : color_function_byte , #Color|Function
" trainer_sight_range " : trainer_sight_range , #trainer range of sight
" script_pointer " : { " 1 " : script_pointer_byte1 ,
" 2 " : script_pointer_byte2 } ,
#"text_block": text_block, #script pointer byte 1
#"text_bank": text_bank, #script pointer byte 2
" when_byte " : when_byte , #bit no. of bit table 1 (hidden if set)
" hide " : hide , #note: FFFF for none
" is_trainer " : is_trainer ,
" is_regular_script " : is_regular_script ,
" is_give_item " : is_give_item ,
}
people_event . update ( extra_portion )
people_events . append ( people_event )
return people_events
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
class SignpostRemoteBase :
def __init__ ( self , address , bank = None , map_group = None , map_id = None , signpost = None , debug = False , label = None ) :
self . address = address
self . last_address = address + self . size
script_parse_table [ self . address : self . last_address ] = self
self . bank = bank
self . map_group = map_group
self . map_id = map_id
self . signpost = signpost
self . debug = debug
self . params = [ ]
2012-04-26 16:41:13 -07:00
if not label :
label = self . base_label + hex ( address )
self . label = Label ( name = label , address = address , object = self )
2012-04-27 11:41:44 -07:00
self . dependencies = None
2012-04-14 10:23:53 -07:00
self . parse ( )
2012-04-17 18:22:28 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-20 16:26:52 -07:00
dependencies = [ ]
2012-04-27 11:41:44 -07:00
if self . dependencies != None and not recompute :
2012-04-27 12:58:57 -07:00
global_dependencies . update ( self . dependencies )
2012-04-27 11:41:44 -07:00
return self . dependencies
2012-04-20 16:26:52 -07:00
for p in self . params :
2012-04-27 12:58:57 -07:00
deps = p . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies )
2012-04-20 16:26:52 -07:00
dependencies . extend ( deps )
2012-04-27 11:41:44 -07:00
self . dependencies = dependencies
2012-04-20 16:26:52 -07:00
return dependencies
2012-04-14 10:23:53 -07:00
def to_asm ( self ) :
""" very similar to Command.to_asm """
if len ( self . params ) == 0 : return " "
2012-05-13 22:58:11 -07:00
#output = ", ".join([p.to_asm() for p in self.params])
output = " "
for param in self . params :
if issubclass ( param . __class__ , SingleByteParam ) :
output + = " db "
else :
output + = " dw "
output + = param . to_asm ( ) + " \n "
2012-04-14 10:23:53 -07:00
return output
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
class SignpostRemoteScriptChunk ( SignpostRemoteBase ) :
"""
a signpost might point to [ Bit - Nr . ( 2 byte ) ] [ 2 byte pointer to script ]
"""
base_label = " SignpostRemoteScript_ "
size = 4
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
def parse ( self ) :
address = self . address
bank = self . bank
#bit_table_byte1 = ord(rom[address])
#bit_table_byte2 = ord(rom[address+1])
bit_table = MultiByteParam ( address = address , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
self . params . append ( bit_table )
#script_address = calculate_pointer_from_bytes_at(address+2, bank=bank)
#script = parse_script_engine_script_at(script_address, map_group=self.map_group, map_id=self.map_id, debug=self.debug)
script_param = ScriptPointerLabelParam ( address = address + 2 , map_group = self . map_group , map_id = self . map_id , debug = self . debug , force = False )
self . params . append ( script_param )
self . script = script_param . script
self . signpost . remote_script = self . script
#self.bit_table_bytes = [bit_table_byte1, bit_table_byte2]
#self.script_address = script_address
#self.script = script
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
class SignpostRemoteItemChunk ( SignpostRemoteBase ) :
"""
a signpost might point to [ Bit - Nr . ( 2 byte ) ] [ Item no . ]
"""
base_label = " SignpostRemoteItem_ "
size = 3
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
def parse ( self ) :
address = self . address
bank = self . bank
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
bit_table = MultiByteParam ( address = address , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
self . params . append ( bit_table )
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
item = ItemLabelByte ( address = address + 2 )
self . params . append ( item )
self . item = item
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
class SignpostRemoteUnknownChunk ( SignpostRemoteBase ) :
"""
a signpost might point to [ Bit - Nr . ( 2 byte ) ] [ ? ? ]
"""
base_label = " SignpostRemoteUnknown_ "
size = 3
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
def parse ( self ) :
address = self . address
bank = self . bank
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
bit_table = MultiByteParam ( address = address , bank = self . bank , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
self . params . append ( bit_table )
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
byte = SingleByteParam ( address = address + 2 )
self . params . append ( byte )
2012-04-17 18:22:28 -07:00
2012-04-14 11:45:05 -07:00
#this could potentially extend Command
#see how class Warp does this
2012-04-26 11:13:24 -07:00
class Signpost ( Command ) :
2012-04-14 08:54:48 -07:00
""" parse some number of signposts from the data
[ Y position ] [ X position ] [ Function ] [ Script pointer ( 2 byte ) ]
functions :
00 Sign can be read from all directions
script pointer to : script
01 Sign can only be read from below
script pointer to : script
02 Sign can only be read from above
script pointer to : script
03 Sign can only be read from right
script pointer to : script
04 Sign can only be read from left
script pointer to : script
05 If bit of BitTable1 is set then pointer is interpreted
script pointer to : [ Bit - Nr . ( 2 byte ) ] [ 2 byte pointer to script ]
06 If bit of BitTable1 is not set then pointer is interpreted
script pointer to : [ Bit - Nr . ( 2 byte ) ] [ 2 byte pointer to script ]
07 If bit of BitTable1 is set then item is given
script pointer to : [ Bit - Nr . ( 2 byte ) ] [ Item no . ]
08 No Action
script pointer to : [ Bit - Nr . ( 2 byte ) ] [ ? ? ]
"""
size = 5
2012-04-26 08:27:46 -07:00
macro_name = " signpost "
2012-04-26 11:13:24 -07:00
override_byte_check = True
2012-04-17 18:22:28 -07:00
2012-04-26 11:55:55 -07:00
#preprocessor uses this
param_types = {
0 : { " name " : " y " , " class " : DecimalParam } ,
1 : { " name " : " x " , " class " : DecimalParam } ,
2 : { " name " : " function " , " class " : HexByte } ,
3 : { " name " : " pointer " , " class " : PointerLabelParam } ,
}
2012-04-14 08:54:48 -07:00
def __init__ ( self , address , id , bank = None , map_group = None , map_id = None , debug = True , label = None ) :
self . address = address
self . id = id
if label == None :
2012-04-26 16:41:13 -07:00
label = " UnknownSignpost_ " + str ( map_group ) + " Map " + str ( map_id ) + " _ " + hex ( address )
self . label = Label ( name = label , address = address , object = self )
2012-04-14 08:54:48 -07:00
self . map_group = map_group
self . map_id = map_id
self . debug = debug
self . bank = bank
self . last_address = self . address + self . size
self . y , self . x , self . func = None , None , None
2012-04-14 15:58:15 -07:00
#Signpost should probably not be in the globals
2012-04-15 16:26:06 -07:00
#script_parse_table[self.address : self.last_address] = self
2012-04-14 10:23:53 -07:00
self . remotes = [ ]
self . params = [ ]
2012-04-27 11:41:44 -07:00
self . dependencies = None
2012-04-14 08:54:48 -07:00
self . parse ( )
2012-04-17 18:22:28 -07:00
2012-04-14 08:54:48 -07:00
def parse ( self ) :
""" parse just one signpost """
2012-04-14 10:23:53 -07:00
address = self . address
2012-04-14 08:54:48 -07:00
bank = self . bank
self . last_address = self . address + self . size
2012-04-14 10:23:53 -07:00
bytes = rom_interval ( self . address , self . size ) #, signpost_byte_size)
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
self . y = int ( bytes [ 0 ] , 16 )
self . x = int ( bytes [ 1 ] , 16 )
self . func = int ( bytes [ 2 ] , 16 )
y , x , func = self . y , self . x , self . func
2012-04-14 08:54:48 -07:00
2012-04-14 10:23:53 -07:00
#y
self . params . append ( DecimalParam ( address = address , bank = self . bank , map_group = self . map_group , map_id = self . map_id , debug = self . debug ) )
#x
self . params . append ( DecimalParam ( address = address + 1 , bank = self . bank , map_group = self . map_group , map_id = self . map_id , debug = self . debug ) )
#func
self . params . append ( HexByte ( address = address + 2 , bank = self . bank , map_group = self . map_group , map_id = self . map_id , debug = self . debug ) )
output = " ******* parsing signpost " + str ( self . id ) + " at: "
output + = " x= " + str ( x ) + " y= " + str ( y ) + " on map_group= "
output + = str ( self . map_group ) + " map_id= " + str ( self . map_id )
if func in [ 0 , 1 , 2 , 3 , 4 ] :
#signpost's script pointer points to a script
script_ptr_byte1 = int ( bytes [ 3 ] , 16 )
script_ptr_byte2 = int ( bytes [ 4 ] , 16 )
script_pointer = script_ptr_byte1 + ( script_ptr_byte2 << 8 )
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
script_address = calculate_pointer ( script_pointer , bank )
output + = " script@ " + hex ( script_address )
print output
param = ScriptPointerLabelParam ( address = self . address + 3 , map_group = self . map_group , map_id = self . map_id , debug = self . debug , force = False )
self . params . append ( param )
2012-05-20 12:32:19 -07:00
param = script_parse_table [ param . parsed_address ]
2012-05-20 12:23:13 -07:00
param . label = Label ( address = param . address , object = param , name = " Map " + map_names [ self . map_group ] [ self . map_id ] [ " label " ] + " Signpost " + str ( self . id ) + " Script " )
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
#self.script_address = script_address
#self.script = script
elif func in [ 5 , 6 ] :
#signpost's script pointer points to [Bit-Nr. (2byte)][2byte pointer to script]
ptr_byte1 = int ( bytes [ 3 ] , 16 )
ptr_byte2 = int ( bytes [ 4 ] , 16 )
pointer = ptr_byte1 + ( ptr_byte2 << 8 )
address = calculate_pointer ( pointer , bank )
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
bit_table_byte1 = ord ( rom [ address ] )
bit_table_byte2 = ord ( rom [ address + 1 ] )
script_ptr_byte1 = ord ( rom [ address + 2 ] )
script_ptr_byte2 = ord ( rom [ address + 3 ] )
script_address = calculate_pointer_from_bytes_at ( address + 2 , bank = bank )
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
output + = " remote_chunk@ " + hex ( address ) + " remote_script@ " + hex ( script_address )
print output
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
r1 = SignpostRemoteScriptChunk ( address , signpost = self , \
bank = self . bank , map_group = self . map_group , map_id = self . map_id , \
debug = self . debug )
self . remotes . append ( r1 )
2012-04-17 18:22:28 -07:00
2012-05-20 12:23:13 -07:00
# give a better label to the SignpostRemoteScriptChunk
r1 . label = Label ( address = r1 . address , object = r1 , name = " Map " + map_names [ self . map_group ] [ self . map_id ] [ " label " ] + " SignpostPtr " + str ( self . id ) )
2012-05-13 22:51:16 -07:00
mb = PointerLabelParam ( address = self . address + 3 , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
2012-04-14 10:23:53 -07:00
self . params . append ( mb )
2012-05-20 12:23:13 -07:00
# update the remote script address
param = script_parse_table [ script_address ]
param . label = Label ( address = param . address , object = param , name = " Map " + map_names [ self . map_group ] [ self . map_id ] [ " label " ] + " Signpost " + str ( self . id ) + " Script " )
2012-04-14 10:23:53 -07:00
elif func == 7 :
#signpost's script pointer points to [Bit-Nr. (2byte)][Item no.]
ptr_byte1 = int ( bytes [ 3 ] , 16 )
ptr_byte2 = int ( bytes [ 4 ] , 16 )
pointer = ptr_byte1 + ( ptr_byte2 << 8 )
address = calculate_pointer ( pointer , bank )
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
item_id = ord ( rom [ address + 2 ] )
output + = " item_id= " + str ( item_id )
print output
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
r1 = SignpostRemoteItemChunk ( address , signpost = self , \
bank = self . bank , map_group = self . map_group , map_id = self . map_id , \
debug = self . debug )
self . remotes . append ( r1 )
2012-05-20 12:23:13 -07:00
r1 . label = Label ( address = r1 . address , object = r1 , name = " Map " + map_names [ self . map_group ] [ self . map_id ] [ " label " ] + " SignpostItem " + str ( self . id ) )
2012-04-17 18:22:28 -07:00
2012-05-13 22:51:16 -07:00
mb = PointerLabelParam ( address = self . address + 3 , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
2012-04-14 10:23:53 -07:00
self . params . append ( mb )
2012-04-14 08:54:48 -07:00
2012-04-14 10:23:53 -07:00
#bit_table_byte1 = ord(rom[address])
#bit_table_byte2 = ord(rom[address+1])
#self.bit_table_bytes = [bit_table_byte1, bit_table_byte2]
#self.item_id = item_id
elif func == 8 :
#signpost's script pointer points to [Bit-Nr. (2byte)][??]
ptr_byte1 = int ( bytes [ 3 ] , 16 )
ptr_byte2 = int ( bytes [ 4 ] , 16 )
pointer = ptr_byte1 + ( ptr_byte2 << 8 )
address = calculate_pointer ( pointer , bank )
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
output + = " remote unknown chunk at= " + hex ( address )
print output
2012-04-17 18:22:28 -07:00
2012-04-14 10:23:53 -07:00
r1 = SignpostRemoteUnknownChunk ( address , signpost = self , \
bank = self . bank , map_group = self . map_group , map_id = self . map_id , \
debug = self . debug )
self . remotes . append ( r1 )
2012-04-17 18:22:28 -07:00
2012-05-13 22:51:16 -07:00
mb = PointerLabelParam ( address = self . address + 3 , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
2012-04-14 10:23:53 -07:00
self . params . append ( mb )
else :
raise Exception , " unknown signpost type byte= " + hex ( func ) + " signpost@ " + hex ( self . address )
2012-05-20 01:39:25 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-20 16:26:52 -07:00
dependencies = [ ]
2012-04-27 11:41:44 -07:00
if self . dependencies != None and not recompute :
2012-04-27 12:58:57 -07:00
global_dependencies . update ( self . dependencies )
2012-04-27 11:41:44 -07:00
return self . dependencies
2012-04-20 16:26:52 -07:00
for p in self . params :
2012-04-27 12:58:57 -07:00
dependencies . extend ( p . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
2012-04-27 11:41:44 -07:00
self . dependencies = dependencies
2012-04-20 16:26:52 -07:00
return dependencies
2012-04-14 08:54:48 -07:00
def to_asm ( self ) :
2012-04-26 08:27:46 -07:00
output = self . macro_name + " "
2012-04-14 10:23:53 -07:00
if self . params == [ ] : raise Exception , " signpost has no params? "
2012-04-26 08:27:46 -07:00
output + = " , " . join ( [ p . to_asm ( ) for p in self . params ] )
2012-04-14 10:23:53 -07:00
return output
2012-04-14 08:54:48 -07:00
all_signposts = [ ]
2012-04-14 15:58:15 -07:00
def parse_signposts ( address , signpost_count , bank = None , map_group = None , map_id = None , debug = True ) :
2012-04-14 10:23:53 -07:00
if bank == None : raise Exception , " signposts need to know their bank "
2012-04-14 08:54:48 -07:00
signposts = [ ]
current_address = address
id = 0
for each in range ( signpost_count ) :
signpost = Signpost ( current_address , id , bank = bank , map_group = map_group , map_id = map_id , debug = debug )
current_address + = signpost_byte_size #i think ??
signposts . append ( signpost )
id + = 1
all_signposts . extend ( signposts )
return signposts
2012-04-15 12:33:41 -07:00
def old_parse_signpost_bytes ( some_bytes , bank = None , map_group = None , map_id = None , debug = True ) :
assert len ( some_bytes ) % signpost_byte_size == 0 , " wrong number of bytes "
signposts = [ ]
for bytes in grouper ( some_bytes , count = signpost_byte_size ) :
y = int ( bytes [ 0 ] , 16 )
x = int ( bytes [ 1 ] , 16 )
func = int ( bytes [ 2 ] , 16 )
additional = { }
if func in [ 0 , 1 , 2 , 3 , 4 ] :
print " ******* parsing signpost script.. signpost is at: x= " + str ( x ) + " y= " + str ( y )
script_ptr_byte1 = int ( bytes [ 3 ] , 16 )
script_ptr_byte2 = int ( bytes [ 4 ] , 16 )
script_pointer = script_ptr_byte1 + ( script_ptr_byte2 << 8 )
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
script_address = None
script = None
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
script_address = calculate_pointer ( script_pointer , bank )
script = parse_script_engine_script_at ( script_address , map_group = map_group , map_id = map_id )
additional = {
" script_ptr " : script_pointer ,
" script_pointer " : { " 1 " : script_ptr_byte1 , " 2 " : script_ptr_byte2 } ,
" script_address " : script_address ,
" script " : script ,
}
elif func in [ 5 , 6 ] :
print " ******* parsing signpost script.. signpost is at: x= " + str ( x ) + " y= " + str ( y )
ptr_byte1 = int ( bytes [ 3 ] , 16 )
ptr_byte2 = int ( bytes [ 4 ] , 16 )
pointer = ptr_byte1 + ( ptr_byte2 << 8 )
address = calculate_pointer ( pointer , bank )
bit_table_byte1 = ord ( rom [ address ] )
bit_table_byte2 = ord ( rom [ address + 1 ] )
script_ptr_byte1 = ord ( rom [ address + 2 ] )
script_ptr_byte2 = ord ( rom [ address + 3 ] )
script_address = calculate_pointer_from_bytes_at ( address + 2 , bank = bank )
script = parse_script_engine_script_at ( script_address , map_group = map_group , map_id = map_id )
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
additional = {
" bit_table_bytes " : { " 1 " : bit_table_byte1 , " 2 " : bit_table_byte2 } ,
" script_ptr " : script_ptr_byte1 + ( script_ptr_byte2 << 8 ) ,
" script_pointer " : { " 1 " : script_ptr_byte1 , " 2 " : script_ptr_byte2 } ,
" script_address " : script_address ,
" script " : script ,
}
else :
print " .. type 7 or 8 signpost not parsed yet. "
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
spost = {
" y " : y ,
" x " : x ,
" func " : func ,
}
spost . update ( additional )
signposts . append ( spost )
return signposts
2012-04-17 18:22:28 -07:00
2012-04-14 22:06:47 -07:00
class MapHeader :
2012-04-14 23:11:00 -07:00
base_label = " MapHeader_ "
2012-04-17 18:22:28 -07:00
2012-04-14 23:11:00 -07:00
def __init__ ( self , address , map_group = None , map_id = None , debug = True , label = None , bank = 0x25 ) :
2012-04-15 10:26:46 -07:00
print " creating a MapHeader at " + hex ( address ) + " map_group= " + str ( map_group ) + " map_id= " + str ( map_id )
2012-04-14 22:06:47 -07:00
self . address = address
self . map_group = map_group
self . map_id = map_id
2012-04-14 23:11:00 -07:00
self . bank = bank
2012-04-14 22:06:47 -07:00
self . debug = debug
2012-04-27 11:41:44 -07:00
self . dependencies = None
2012-05-20 07:32:59 -07:00
label = self . make_label ( )
self . label = Label ( name = label , address = address , object = self )
2012-04-25 19:53:20 -07:00
self . last_address = address + 9
2012-04-14 23:18:25 -07:00
script_parse_table [ address : self . last_address ] = self
2012-04-14 22:06:47 -07:00
self . parse ( )
2012-04-17 18:22:28 -07:00
2012-05-16 22:40:40 -07:00
def make_label ( self ) :
return map_names [ self . map_group ] [ self . map_id ] [ " label " ] + " _MapHeader "
2012-04-14 22:06:47 -07:00
def parse ( self ) :
2012-04-14 23:11:00 -07:00
address = self . address
2012-04-15 10:26:46 -07:00
print " parsing a MapHeader at " + hex ( address )
2012-04-14 23:18:25 -07:00
self . bank = HexByte ( address = address )
self . tileset = HexByte ( address = address + 1 )
self . permission = DecimalParam ( address = address + 2 )
2012-04-15 12:39:13 -07:00
self . second_map_header_address = calculate_pointer ( ord ( rom [ address + 3 ] ) + ( ord ( rom [ address + 4 ] ) << 8 ) , self . bank . byte )
2012-04-14 23:11:00 -07:00
#TODO: is the bank really supposed to be 0x25 all the time ??
2012-04-15 12:49:24 -07:00
self . second_map_header = SecondMapHeader ( self . second_map_header_address , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
2012-04-15 16:24:56 -07:00
all_second_map_headers . append ( self . second_map_header )
2012-04-14 23:18:25 -07:00
self . location_on_world_map = HexByte ( address = address + 5 )
self . music = HexByte ( address = address + 6 )
self . time_of_day = DecimalParam ( address = address + 7 )
self . fishing_group = DecimalParam ( address = address + 8 )
2012-05-20 01:39:25 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-27 11:41:44 -07:00
if self . dependencies != None and not recompute :
2012-04-27 12:58:57 -07:00
global_dependencies . update ( self . dependencies )
2012-04-27 11:41:44 -07:00
return self . dependencies
2012-04-20 16:26:52 -07:00
dependencies = [ self . second_map_header ]
2012-04-27 12:58:57 -07:00
global_dependencies . add ( self . second_map_header )
dependencies . append ( self . second_map_header . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
2012-04-27 11:41:44 -07:00
self . dependencies = dependencies
2012-04-20 16:26:52 -07:00
return dependencies
2012-04-17 18:22:28 -07:00
2012-04-14 22:06:47 -07:00
def to_asm ( self ) :
2012-04-14 23:11:00 -07:00
output = " ; bank, tileset, permission \n "
output + = " db " + " , " . join ( [ self . bank . to_asm ( ) , self . tileset . to_asm ( ) , self . permission . to_asm ( ) ] )
output + = " \n \n ; second map header \n "
2012-04-15 19:47:12 -07:00
output + = " dw " + PointerLabelParam ( address = self . address + 3 ) . to_asm ( ) #TODO: should we include bank=self.bank.byte ??
2012-04-14 23:11:00 -07:00
output + = " \n \n ; location on world map, music, time of day, fishing group \n "
2012-04-15 19:47:12 -07:00
output + = " db " + " , " . join ( [ self . location_on_world_map . to_asm ( ) , self . music . to_asm ( ) , self . time_of_day . to_asm ( ) , self . fishing_group . to_asm ( ) ] )
2012-04-14 23:11:00 -07:00
return output
2012-04-15 16:24:56 -07:00
2012-04-17 18:22:28 -07:00
2012-04-15 16:24:56 -07:00
all_map_headers = [ ]
2012-03-24 02:43:06 -07:00
def parse_map_header_at ( address , map_group = None , map_id = None , debug = True ) :
2012-03-05 22:15:35 -08:00
""" parses an arbitrary map header at some address """
2012-03-18 21:05:28 -07:00
print " parsing a map header at: " + hex ( address )
2012-04-14 23:11:00 -07:00
map_header = MapHeader ( address , map_group = map_group , map_id = map_id , debug = debug )
2012-04-15 16:24:56 -07:00
all_map_headers . append ( map_header )
2012-03-05 22:15:35 -08:00
return map_header
2012-04-15 12:33:41 -07:00
def old_parse_map_header_at ( address , map_group = None , map_id = None , debug = True ) :
""" parses an arbitrary map header at some address """
print " parsing a map header at: " + hex ( address )
bytes = rom_interval ( address , map_header_byte_size , strings = False , debug = debug )
bank = bytes [ 0 ]
tileset = bytes [ 1 ]
permission = bytes [ 2 ]
second_map_header_address = calculate_pointer ( bytes [ 3 ] + ( bytes [ 4 ] << 8 ) , 0x25 )
location_on_world_map = bytes [ 5 ] #pokégear world map location
music = bytes [ 6 ]
time_of_day = bytes [ 7 ]
fishing_group = bytes [ 8 ]
map_header = {
" bank " : bank ,
" tileset " : tileset ,
" permission " : permission , #map type?
" second_map_header_pointer " : { " 1 " : bytes [ 3 ] , " 2 " : bytes [ 4 ] } ,
" second_map_header_address " : second_map_header_address ,
" location_on_world_map " : location_on_world_map , #area
" music " : music ,
" time_of_day " : time_of_day ,
" fishing " : fishing_group ,
}
print " second map header address is: " + hex ( second_map_header_address )
2012-04-15 12:49:24 -07:00
map_header [ " second_map_header " ] = old_parse_second_map_header_at ( second_map_header_address , debug = debug )
2012-04-15 14:37:31 -07:00
event_header_address = map_header [ " second_map_header " ] [ " event_address " ]
script_header_address = map_header [ " second_map_header " ] [ " script_address " ]
#maybe event_header and script_header should be put under map_header["second_map_header"]
map_header [ " event_header " ] = old_parse_map_event_header_at ( event_header_address , map_group = map_group , map_id = map_id , debug = debug )
map_header [ " script_header " ] = old_parse_map_script_header_at ( script_header_address , map_group = map_group , map_id = map_id , debug = debug )
2012-04-15 12:33:41 -07:00
return map_header
2012-04-17 18:22:28 -07:00
2012-05-26 12:46:34 -07:00
def get_direction ( connection_byte , connection_id ) :
""" Given a connection byte and a connection id, which direction is this
connection ?
example :
The 0 th connection of $ 5 is SOUTH and the 1 st connection is
EAST .
"""
connection_options = [ 0b1000 , 0b0100 , 0b0010 , 0b0001 ]
results = [ " NORTH " , " SOUTH " , " WEST " , " EAST " ]
for option in connection_options :
if ( option & connection_byte ) == 0 :
results [ connection_options . index ( option ) ] = " "
# prune results
while " " in results :
results . remove ( " " )
return results [ connection_id ]
2012-04-14 18:03:19 -07:00
class SecondMapHeader :
base_label = " SecondMapHeader_ "
2012-04-17 18:22:28 -07:00
2012-04-14 18:03:19 -07:00
def __init__ ( self , address , map_group = None , map_id = None , debug = True , bank = None , label = None ) :
2012-04-15 10:26:46 -07:00
print " creating a SecondMapHeader at " + hex ( address )
2012-04-14 18:03:19 -07:00
self . address = address
self . map_group = map_group
self . map_id = map_id
self . debug = debug
self . bank = bank
2012-04-27 11:41:44 -07:00
self . dependencies = None
2012-05-20 07:32:59 -07:00
label = self . make_label ( )
self . label = Label ( name = label , address = address , object = self )
2012-05-26 12:46:34 -07:00
# the minimum number of bytes is 12
2012-04-14 18:03:19 -07:00
self . last_address = address + 12
2012-05-26 12:46:34 -07:00
self . size = 12
2012-04-14 18:03:19 -07:00
script_parse_table [ address : self . last_address ] = self
self . parse ( )
2012-05-20 01:39:25 -07:00
2012-05-16 22:40:40 -07:00
def make_label ( self ) :
return map_names [ self . map_group ] [ self . map_id ] [ " label " ] + " _SecondMapHeader "
2012-04-17 18:22:28 -07:00
2012-04-14 18:03:19 -07:00
def parse ( self ) :
address = self . address
bytes = rom_interval ( address , second_map_header_byte_size , strings = False )
2012-05-26 12:46:34 -07:00
size = second_map_header_byte_size
# for later
self . connections = [ ]
2012-04-14 18:03:19 -07:00
2012-04-14 23:18:25 -07:00
self . border_block = HexByte ( address = address )
self . height = DecimalParam ( address = address + 1 )
self . width = DecimalParam ( address = address + 2 )
2012-04-17 18:22:28 -07:00
2012-04-14 18:03:19 -07:00
#bank appears first
###self.blockdata_address = PointerLabelBeforeBank(address+3)
2012-04-15 12:49:24 -07:00
self . blockdata_address = calculate_pointer_from_bytes_at ( address + 3 , bank = True )
2012-05-20 07:37:43 -07:00
xyz = script_parse_table [ self . blockdata_address ]
if xyz == None :
self . blockdata = MapBlockData ( self . blockdata_address , map_group = self . map_group , map_id = self . map_id , debug = self . debug , width = self . width , height = self . height )
else :
self . blockdata = xyz
2012-04-17 18:22:28 -07:00
2012-04-14 18:03:19 -07:00
#bank appears first
###self.script_address = PointerLabelBeforeBank(address+6)
2012-04-15 12:49:24 -07:00
self . script_header_address = calculate_pointer_from_bytes_at ( address + 6 , bank = True )
2012-04-15 15:21:07 -07:00
self . script_header = MapScriptHeader ( self . script_header_address , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
2012-04-15 16:24:56 -07:00
all_map_script_headers . append ( self . script_header )
2012-04-15 19:47:12 -07:00
self . event_bank = ord ( rom [ address + 6 ] )
2012-04-15 12:49:24 -07:00
self . event_header_address = calculate_pointer_from_bytes_at ( address + 9 , bank = ord ( rom [ address + 6 ] ) )
2012-04-21 00:16:12 -07:00
self . event_header = MapEventHeader ( self . event_header_address , map_group = self . map_group , map_id = self . map_id , debug = self . debug )
2012-05-26 12:46:34 -07:00
self . connection_byte = DecimalParam ( address = address + 11 )
2012-04-15 16:24:56 -07:00
all_map_event_headers . append ( self . event_header )
2012-04-14 18:03:19 -07:00
2012-05-26 12:46:34 -07:00
self . size = size
2012-05-26 20:19:53 -07:00
if self . connection_byte == 0 :
return True
2012-05-26 12:46:34 -07:00
current_address = address + 12
# short alias
2012-05-26 20:19:53 -07:00
cb = self . connection_byte . byte
2012-05-26 12:46:34 -07:00
# east = 1, west = 2, south = 4, north = 8 (or'd together)
east = ( ( cb & 0x1 ) != 0 )
west = ( ( cb & 0x2 ) != 0 )
south = ( ( cb & 0x4 ) != 0 )
north = ( ( cb & 0x8 ) != 0 )
directions = [ east , west , south , north ]
connection_count = directions . count ( True )
for connection in range ( 0 , connection_count ) :
2012-05-26 20:19:53 -07:00
direction = get_direction ( self . connection_byte . byte , connection )
connection = Connection ( current_address , direction = direction , map_group = self . map_group , map_id = self . map_id , debug = self . debug , smh = self )
2012-05-26 12:46:34 -07:00
self . connections . append ( connection )
# 12 bytes each?
current_address + = connection . size
2012-04-17 18:22:28 -07:00
2012-05-27 17:45:44 -07:00
self . last_address = current_address
2012-04-14 18:03:19 -07:00
return True
2012-05-20 01:39:25 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-27 11:41:44 -07:00
if self . dependencies != None and not recompute :
2012-04-27 12:58:57 -07:00
global_dependencies . update ( self . dependencies )
2012-04-27 11:41:44 -07:00
return self . dependencies
2012-04-20 16:26:52 -07:00
dependencies = [ self . script_header , self . event_header , self . blockdata ]
2012-04-27 12:58:57 -07:00
global_dependencies . update ( dependencies )
dependencies . append ( self . script_header . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
dependencies . append ( self . event_header . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
2012-04-27 11:41:44 -07:00
self . dependencies = dependencies
2012-04-20 16:26:52 -07:00
return dependencies
2012-04-17 18:22:28 -07:00
2012-04-14 19:46:04 -07:00
def to_asm ( self ) :
2012-05-27 17:45:44 -07:00
self_constant_label = get_map_constant_label ( map_group = self . map_group , map_id = self . map_id )
2012-04-14 19:46:04 -07:00
output = " ; border block \n "
output + = " db " + self . border_block . to_asm ( ) + " \n \n "
output + = " ; height, width \n "
2012-05-27 17:45:44 -07:00
output + = " db " + self_constant_label + " _HEIGHT, " + self_constant_label + " _WIDTH \n \n "
2012-04-14 19:46:04 -07:00
output + = " ; blockdata (bank-then-pointer) \n "
2012-04-25 23:07:04 -07:00
thing = ScriptPointerLabelBeforeBank ( address = self . address + 3 , map_group = self . map_group , map_id = self . map_id , debug = self . debug ) . to_asm ( )
2012-04-26 08:27:46 -07:00
output + = " dbw " + thing . split ( " , " ) [ 0 ] + " , " + thing . split ( " , " ) [ 1 ] + " \n \n "
2012-04-14 19:46:04 -07:00
output + = " ; script header (bank-then-pointer) \n "
2012-04-25 23:07:04 -07:00
thing = ScriptPointerLabelBeforeBank ( address = self . address + 6 , map_group = self . map_group , map_id = self . map_id , debug = self . debug ) . to_asm ( )
2012-04-26 08:27:46 -07:00
output + = " dbw " + thing . split ( " , " ) [ 0 ] + " , " + thing . split ( " , " ) [ 1 ] + " \n \n "
2012-04-14 19:46:04 -07:00
output + = " ; map event header (bank-then-pointer) \n "
2012-04-15 19:47:12 -07:00
output + = " dw " + PointerLabelParam ( address = self . address + 9 , bank = self . event_bank , map_group = self . map_group , map_id = self . map_id , debug = self . debug ) . to_asm ( ) + " \n \n "
2012-05-27 17:45:44 -07:00
2012-04-14 19:46:04 -07:00
output + = " ; connections \n "
2012-05-27 17:45:44 -07:00
dir_results = [ ]
connection_options = [ 0b1000 , 0b0100 , 0b0010 , 0b0001 ]
dirs = [ " NORTH " , " SOUTH " , " WEST " , " EAST " ]
for ( id , each ) in enumerate ( dirs ) :
if ( ( connection_options [ id ] & self . connection_byte . byte ) != 0 ) :
dir_results . append ( each )
output + = " db " + " | " . join ( dir_results )
2012-05-26 12:46:34 -07:00
if self . connection_byte == 0 :
return output
else :
output + = " \n \n "
2012-05-26 20:19:53 -07:00
connections = " \n \n " . join ( [ connection . to_asm ( ) for connection in self . connections ] )
2012-05-26 12:46:34 -07:00
output + = connections
2012-04-14 19:46:04 -07:00
return output
2012-04-15 16:24:56 -07:00
2012-05-26 20:19:53 -07:00
strip_pointer_data = [ ]
2012-05-27 13:54:44 -07:00
strip_destination_data = [ ]
2012-05-27 11:46:58 -07:00
connections = [ ]
wrong_norths = [ ]
wrong_easts = [ ]
wrong_souths = [ ]
wrong_wests = [ ]
2012-05-26 20:19:53 -07:00
2012-05-26 12:46:34 -07:00
class Connection :
size = 12
2012-05-26 20:19:53 -07:00
def __init__ ( self , address , direction = None , map_group = None , map_id = None , debug = True , smh = None ) :
2012-05-26 12:46:34 -07:00
self . address = address
self . direction = direction . lower ( )
self . map_group = map_group
self . map_id = map_id
self . debug = debug
2012-05-26 20:19:53 -07:00
self . smh = smh
2012-05-26 12:46:34 -07:00
self . last_address = address + self . size
2012-05-27 11:46:58 -07:00
connections . append ( self )
2012-05-26 12:46:34 -07:00
self . parse ( )
def parse ( self ) :
current_address = self . address
is_vertical = ( ( self . direction == " north " ) or ( self . direction == " south " ) )
is_horizontal = ( ( self . direction == " east " ) or ( self . direction == " west " ) )
connected_map_group_id = ord ( rom [ current_address ] )
self . connected_map_group_id = connected_map_group_id
current_address + = 1
connected_map_id = ord ( rom [ current_address ] )
self . connected_map_id = connected_map_id
current_address + = 1
# window (use JohtoMap's calculation, not this)
# up: C701h + height_of_connected_map * (width_of_connected_map + 6)
# left: C706h + 2 * width_of_connected_map
# down/right: C707h + width_of_connected_map
#
# 2 bytes (flipped) - X position of starting point for intermediate
# tiles (scrolls through connected map line-by-line. this way you can
# change Y position also)
#
# According to JohtoMap, the calculation for tile data pointer is:
# int p = otherMap.tileDataLocation;
# int h = (otherMap.width - otherMap.height)
# if (h > 0)
# p += (h * otherMap.height) + (otherMap.height * 3) + (otherMap.height + 3)
# else
# p += (otherMap.height * otherMap.width) - (otherMap.width * 3);
# c.tileDataPointer = gb.Get2BytePointer(p);
#
# tauwasser calls this "connection strip pointer"
tile_data_pointer = ord ( rom [ current_address ] ) + ( ord ( rom [ current_address + 1 ] ) << 8 )
strip_pointer = tile_data_pointer
self . strip_pointer = tile_data_pointer
current_address + = 2
# 10:19 <comet> memoryotherpointer is <comet> what johtomap calls OMDL (in ram where the tiles start getting pulled from the other map)
memory_other_pointer = ord ( rom [ current_address ] ) + ( ord ( rom [ current_address + 1 ] ) << 8 )
# 10:42 <comet> it would be a good idea to rename otherpointer strippointer or striploc
# 10:42 <comet> since thats more accurate
# 11:05 <comet> Above: C803h + xoffset
# 11:05 <comet> Below: C803h + (m.height + 3) * (m.width + 6) + xoffset
# 11:05 <comet> Left: C800h + (m.width + 6) * (yoffset + 3)
# 11:05 <comet> Right: C7FDh + (m.width + 6) * (yoffset + 4)
#
# tauwasser calls this "connection strip destination" and lin calls this "memoryOtherPointer"
# Points to the upper left block of the connection strip
# (The bank the Blockdata is in, is loaded out of the Mapheader of the connected Map.)
# The connection strip is always 3 Blocks high resp. wide
# (depending on the connection's direction)
strip_destination = memory_other_pointer
self . strip_destination = memory_other_pointer
current_address + = 2
# length of the connection strip in blocks
connection_strip_length = ord ( rom [ current_address ] )
current_address + = 1
connected_map_width = ord ( rom [ current_address ] )
current_address + = 1
self . connection_strip_length = connection_strip_length
self . connected_map_width = connected_map_width
y_position_after_map_change = ord ( rom [ current_address ] )
2012-05-26 21:13:48 -07:00
yoffset = y_position_after_map_change
2012-05-26 12:46:34 -07:00
current_address + = 1
x_position_after_map_change = ord ( rom [ current_address ] )
2012-05-26 21:13:48 -07:00
xoffset = x_position_after_map_change
2012-05-26 12:46:34 -07:00
current_address + = 1
# in pokered these were called alignments? same thing?
self . yoffset = y_position_after_map_change
self . xoffset = x_position_after_map_change
# tauwasser calls this "window" and lin calls this "memoryCurrentPointer"
# Position of the upper left block after entering the Map
#
# tauwasser's formula for windows:
# Above: C701h + Height_of_connected_map * (Width_of_connected_map + 6)
# Left: C706h + 2 * Width_of_connected_map
# Below/Right: C707h + Width_of_connected_map
window = ord ( rom [ current_address ] ) + ( ord ( rom [ current_address + 1 ] ) << 8 )
current_address + = 2
self . window = window
2012-05-27 11:46:58 -07:00
current_map_height = self . smh . height . byte
current_map_width = self . smh . width . byte
2012-05-27 13:54:44 -07:00
2012-05-26 20:19:53 -07:00
if " header_new " in map_names [ connected_map_group_id ] [ connected_map_id ] . keys ( ) :
2012-05-27 13:54:44 -07:00
# the below code ensures that there's an equation to handle strip_pointer
ldirection = self . direction . lower ( )
2012-05-26 20:19:53 -07:00
connected_map_header = map_names [ connected_map_group_id ] [ connected_map_id ] [ " header_new " ]
connected_second_map_header = connected_map_header . second_map_header
connected_map_height = connected_second_map_header . height . byte
connected_map_width = connected_second_map_header . width . byte
p = connected_second_map_header . blockdata . address
2012-05-27 10:51:53 -07:00
h = None
method = " default "
2012-05-26 20:19:53 -07:00
if ldirection == " north " :
h = connected_map_width - self . smh . width . byte
2012-05-27 10:51:53 -07:00
if ( ( p + ( ( connected_map_height * connected_map_width ) - ( connected_map_width * 3 ) ) ) % 0x4000 ) + 0x4000 == strip_pointer :
# lin's equation:
# p += (otherMap.height * otherMap.width) - (otherMap.width * 3)
2012-05-26 20:19:53 -07:00
p + = ( connected_map_height * connected_map_width ) - ( connected_map_width * 3 )
2012-05-27 10:51:53 -07:00
method = " north1 "
elif ( ( p + connected_map_width + xoffset + ( 16 * connected_map_height ) - 16 ) % 0x4000 ) + 0x4000 == strip_pointer :
p + = connected_map_width + xoffset + ( 16 * connected_map_height ) - 16
method = " north2 "
elif p != strip_pointer :
# worst case scenario: we don't know how to calculate p, so we'll just set it as a constant
# example: Route10North north to Route9 (strip_pointer=0x7eae, connected map's blockdata=0x7de9)
p = strip_pointer
method = " north3 "
else :
2012-05-27 11:46:58 -07:00
# this doesn't seem to ever happen
2012-05-27 10:51:53 -07:00
# or just do nothing (value is already ok)
method = " north4 "
2012-05-26 20:19:53 -07:00
elif ldirection == " west " :
h = connected_map_height - self . smh . height . byte
2012-05-27 11:46:58 -07:00
if ( ( p + ( h * connected_map_width ) - ( connected_map_width * 3 ) + ( connected_map_width - 1 ) - 2 ) % 0x4000 ) + 0x4000 == strip_pointer :
2012-05-27 10:51:53 -07:00
# lin's method:
# p += (h * otherMap.width) - (otherMap.width * 3) + (otherMap.width - 3)
p + = ( h * connected_map_width ) - ( connected_map_width * 3 ) + ( connected_map_width - 1 ) - 2
method = " west1 "
elif ( ( p + connected_map_width - 3 ) % 0x4000 ) + 0x4000 == strip_pointer :
2012-05-26 20:19:53 -07:00
print " west h <= 0 "
2012-05-27 10:51:53 -07:00
# lin's method:
# p += otherMap.width - 3
2012-05-26 20:19:53 -07:00
p + = connected_map_width - 3
2012-05-27 10:51:53 -07:00
method = " west2 "
2012-05-27 11:46:58 -07:00
elif ( ( p + xoffset + ( current_map_height * 2 ) ) % 0x4000 + 0x4000 ) == strip_pointer :
method = " west3 "
p + = xoffset + ( current_map_height * 2 )
elif ( p % 0x4000 ) + 0x4000 != strip_pointer :
# worst case scenario: dunno what to do
method = " west4 "
p = strip_pointer
else :
# this doesn't seem to ever happen
# do nothing
method = " west5 "
2012-05-26 20:19:53 -07:00
elif ldirection == " south " :
print " south.. dunno what to do? "
2012-05-26 21:13:48 -07:00
2012-05-27 10:51:53 -07:00
if ( p % 0x4000 ) + 0x4000 == strip_pointer :
# do nothing
method = " south1 "
elif ( ( p + ( xoffset - connection_strip_length + self . smh . width . byte ) / 2 ) % 0x4000 ) + 0x4000 == strip_pointer :
# comet's method
method = " south2 "
p + = ( xoffset - connection_strip_length + self . smh . width . byte ) / 2
elif ( ( p + ( ( xoffset - connection_strip_length + self . smh . width . byte ) / 2 ) - 1 ) % 0x4000 ) + 0x4000 == strip_pointer :
method = " south3 "
p + = ( ( xoffset - connection_strip_length + self . smh . width . byte ) / 2 ) - 1
2012-05-26 20:19:53 -07:00
elif ldirection == " east " :
2012-05-27 10:51:53 -07:00
if ( p % 0x4000 ) + 0x4000 == strip_pointer :
# do nothing
method = " east1 "
elif ( ( p + ( connected_map_height - connection_strip_length ) * connected_map_width ) % 0x4000 ) + 0x4000 == strip_pointer :
p + = ( connected_map_height - connection_strip_length ) * connected_map_width
method = " east2 "
2012-05-27 11:46:58 -07:00
elif ( ( p + 100 - 4 * connected_map_width ) % 0x4000 ) + 0x4000 == strip_pointer :
method = " east3 "
p + = 100 - 4 * connected_map_width
elif ( ( p + 2 * ( 100 - 4 * connected_map_width ) ) % 0x4000 ) + 0x4000 == strip_pointer :
method = " east4 "
# the "2" is possibly ( connected_map_height / current_map_height )
# or current_map_width/yoffset or connected_map_width/yoffset
p + = 2 * ( 100 - 4 * connected_map_width )
2012-05-27 10:51:53 -07:00
2012-05-26 20:19:53 -07:00
# convert the address to a 2-byte pointer
intermediate_p = p
p = ( p % 0x4000 ) + 0x4000
2012-05-27 10:51:53 -07:00
data = {
" strip_pointer " : strip_pointer ,
" strip_length " : connection_strip_length ,
" other_blockdata_address " : connected_second_map_header . blockdata . address ,
" other_blockdata_pointer " : ( connected_second_map_header . blockdata . address % 0x4000 ) + 0x4000 ,
" xoffset " : xoffset ,
" yoffset " : yoffset ,
" connected_map_height " : connected_map_height ,
" connected_map_width " : connected_map_width ,
" connected_map_group_id " : connected_map_group_id ,
" connected_map_id " : connected_map_id ,
" connected_map_label " : map_names [ connected_map_group_id ] [ connected_map_id ] [ " label " ] ,
" current_map_width " : self . smh . width . byte ,
" current_map_height " : self . smh . height . byte ,
" current_map_label " : map_names [ self . smh . map_group ] [ self . smh . map_id ] [ " label " ] ,
" current_map_group_id " : self . smh . map_group ,
" current_map_id " : self . smh . map_id ,
" difference " : strip_pointer - ( ( connected_second_map_header . blockdata . address % 0x4000 ) + 0x4000 ) ,
" direction " : ldirection ,
" method " : method ,
}
strip_pointer_data . append ( data )
2012-05-26 20:19:53 -07:00
2012-05-27 10:51:53 -07:00
if p != strip_pointer :
print " method: " + method + " direction: " + ldirection
2012-05-26 20:19:53 -07:00
print " other map blockdata address: " + hex ( connected_second_map_header . blockdata . address )
print " h = " + str ( h )
print " initial p = " + hex ( connected_second_map_header . blockdata . address )
print " intermediate p = " + hex ( intermediate_p )
print " final p = " + hex ( p )
2012-05-27 10:51:53 -07:00
print " connection length = " + str ( connection_strip_length )
2012-05-26 20:19:53 -07:00
print " strip_pointer = " + hex ( strip_pointer )
print " other map height = " + str ( connected_map_height )
print " other map width = " + str ( connected_map_width )
o = " other map group_id= " + hex ( connected_map_group_id ) + " map_id= " + hex ( connected_map_id ) + " " + map_names [ connected_map_group_id ] [ connected_map_id ] [ " label " ] + " smh= " + hex ( connected_second_map_header . address )
o + = " width= " + str ( connected_second_map_header . width . byte ) + " height= " + str ( connected_second_map_header . height . byte )
print o
o = " current map group_id= " + hex ( self . map_group ) + " map_id= " + hex ( self . map_id ) + " " + map_names [ self . map_group ] [ self . map_id ] [ " label " ] + " smh= " + hex ( self . smh . address )
o + = " width= " + str ( self . smh . width . byte ) + " height= " + str ( self . smh . height . byte )
print o
2012-05-27 11:46:58 -07:00
if ldirection == " east " :
wrong_easts . append ( data )
elif ldirection == " west " :
wrong_wests . append ( data )
elif ldirection == " south " :
wrong_souths . append ( data )
elif ldirection == " north " :
wrong_norths . append ( data )
# this will only happen if there's a bad formula
2012-05-26 20:19:53 -07:00
raise Exception , " tauwasser strip_pointer calculation was wrong? strip_pointer= " + hex ( strip_pointer ) + " p= " + hex ( p )
2012-05-27 13:54:44 -07:00
calculated_destination = None
method = " strip_destination_default "
x_movement_of_the_connection_strip_in_blocks = None
y_movement_of_the_connection_strip_in_blocks = None
2012-05-27 17:45:44 -07:00
# the below code makes sure there's an equation to calculate strip_destination
2012-05-27 13:54:44 -07:00
# 11:05 <comet> Above: C803h + xoffset
# 11:05 <comet> Below: C803h + (m.height + 3) * (m.width + 6) + xoffset
# 11:05 <comet> Left: C800h + (m.width + 6) * (yoffset + 3)
# 11:05 <comet> Right: C7FDh + (m.width + 6) * (yoffset + 4)
#
# tauwasser calls this "connection strip destination" and lin calls this "memoryOtherPointer"
# Points to the upper left block of the connection strip
# (The bank the Blockdata is in, is loaded out of the Mapheader of the connected Map.)
# The connection strip is always 3 Blocks high resp. wide
# (depending on the connection's direction)
if ldirection == " north " :
x_movement_of_the_connection_strip_in_blocks = strip_destination - 0xC703
print " (north) x_movement_of_the_connection_strip_in_blocks is: " + str ( x_movement_of_the_connection_strip_in_blocks )
if x_movement_of_the_connection_strip_in_blocks < 0 :
raise Exception , " x_movement_of_the_connection_strip_in_blocks is wrong? " + str ( x_movement_of_the_connection_strip_in_blocks )
elif ldirection == " south " :
# strip_destination =
# 0xc703 + (current_map_height + 3) * (current_map_width + 6) + x_movement_of_the_connection_strip_in_blocks
x_movement_of_the_connection_strip_in_blocks = strip_destination - ( 0xc703 + ( current_map_height + 3 ) * ( current_map_width + 6 ) )
print " (south) x_movement_of_the_connection_strip_in_blocks is: " + str ( x_movement_of_the_connection_strip_in_blocks )
elif ldirection == " east " :
# strip_destination =
# 0xc700 + (current_map_width + 6) * (y_movement_of_the_connection_strip_in_blocks + 3)
y_movement_of_the_connection_strip_in_blocks = ( strip_destination - 0xc700 ) / ( current_map_width + 6 ) - 3
print " (east) y_movement_of_the_connection_strip_in_blocks is: " + str ( y_movement_of_the_connection_strip_in_blocks )
elif ldirection == " west " :
# strip_destination =
# 0xc6fd + (current_map_width + 6) * (y_movement_of_the_connection_strip_in_blocks + 4)
y_movement_of_the_connection_strip_in_blocks = ( strip_destination - 0xc6fd ) / ( current_map_width + 6 ) - 4
print " (west) y_movement_of_the_connection_strip_in_blocks is: " + str ( y_movement_of_the_connection_strip_in_blocks )
# let's also check the window equations
# tauwasser calls this "window" and lin calls this "memoryCurrentPointer"
# Position of the upper left block after entering the Map
#
# tauwasser's formula for windows:
# Above: C701h + Height_of_connected_map * (Width_of_connected_map + 6)
# Left: C706h + 2 * Width_of_connected_map
# Below/Right: C707h + Width_of_connected_map
window_worked = False
if ldirection == " north " :
# tauwasser's formula: 0xc701 + connected_map_height * (connected_map_width + 6)
window_start = 0xc801
if window == window_start + ( connected_map_height * 6 ) + ( connected_map_height * connected_map_width ) :
window_worked = True
elif ldirection == " east " :
window_start = 0xc807
if window == ( window_start + connected_map_width ) :
window_worked = True
elif ldirection == " south " :
window_start = 0xc807
if window == ( window_start + connected_map_width ) :
window_worked = True
elif ldirection == " west " :
window_start = 0xc807
if window == ( window_start + xoffset ) :
window_worked = True
data = {
" window " : window ,
" window_start " : window_start ,
" window_diff " : window - window_start ,
" window_worked " : window_worked ,
" strip_destination " : strip_destination ,
" strip_length " : connection_strip_length ,
" other_blockdata_address " : connected_second_map_header . blockdata . address ,
" other_blockdata_pointer " : ( connected_second_map_header . blockdata . address % 0x4000 ) + 0x4000 ,
" xoffset " : xoffset ,
" yoffset " : yoffset ,
" connected_map_height " : connected_map_height ,
" connected_map_width " : connected_map_width ,
" connected_map_group_id " : connected_map_group_id ,
" connected_map_id " : connected_map_id ,
" connected_map_label " : map_names [ connected_map_group_id ] [ connected_map_id ] [ " label " ] ,
" current_map_width " : self . smh . width . byte ,
" current_map_height " : self . smh . height . byte ,
" current_map_label " : map_names [ self . smh . map_group ] [ self . smh . map_id ] [ " label " ] ,
" current_map_group_id " : self . smh . map_group ,
" current_map_id " : self . smh . map_id ,
" y_movement_of_the_connection_strip_in_blocks " : y_movement_of_the_connection_strip_in_blocks ,
" x_movement_of_the_connection_strip_in_blocks " : x_movement_of_the_connection_strip_in_blocks ,
" direction " : ldirection ,
" method " : method ,
}
strip_destination_data . append ( data )
2012-05-26 12:46:34 -07:00
def to_asm ( self ) :
2012-05-26 20:19:53 -07:00
output = " "
2012-05-26 12:46:34 -07:00
ldirection = self . direction . lower ( )
2012-05-26 20:19:53 -07:00
connected_map_group_id = self . connected_map_group_id
connected_map_id = self . connected_map_id
connected_map_header = map_names [ connected_map_group_id ] [ connected_map_id ] [ " header_new " ]
connected_second_map_header = connected_map_header . second_map_header
connected_map_height = connected_second_map_header . height . byte
connected_map_width = connected_second_map_header . width . byte
2012-05-27 17:45:44 -07:00
connection_strip_length = self . connection_strip_length
connected_map_width = self . connected_map_width
current_map_height = self . smh . height . byte
current_map_width = self . smh . width . byte
2012-05-27 16:24:09 -07:00
2012-05-26 20:19:53 -07:00
map_constant_label = get_map_constant_label ( map_group = connected_map_group_id , map_id = connected_map_id )
2012-05-27 16:24:09 -07:00
self_constant_label = get_map_constant_label ( map_group = self . smh . map_group , map_id = self . smh . map_id )
2012-05-26 12:46:34 -07:00
if map_constant_label != None :
map_group_label = " GROUP_ " + map_constant_label
map_label = " MAP_ " + map_constant_label
else :
map_group_label = str ( connected_map_group_id )
map_label = str ( connected_map_id )
output + = " ; " + self . direction . upper ( ) + " to " \
+ map_names [ connected_map_group_id ] [ connected_map_id ] [ " name " ] \
+ " \n "
output + = " db %s , %s ; connected map (group, id) \n " % ( map_group_label , map_label )
2012-05-27 17:45:44 -07:00
yoffset = self . yoffset
xoffset = self . xoffset
2012-05-26 20:19:53 -07:00
# According to JohtoMap, the calculation for tile data pointer is:
# int p = otherMap.tileDataLocation;
# int h = (otherMap.width - otherMap.height)
# if (h > 0)
# p += (h * otherMap.height) + (otherMap.height * 3) + (otherMap.height + 3)
# else
# p += (otherMap.height * otherMap.width) - (otherMap.width * 3);
# c.tileDataPointer = gb.Get2BytePointer(p);
2012-05-26 12:46:34 -07:00
strip_pointer = self . strip_pointer
2012-05-26 20:19:53 -07:00
p = connected_second_map_header . blockdata . address
2012-05-27 16:24:09 -07:00
output + = " dw "
2012-05-26 20:19:53 -07:00
if ldirection == " north " :
h = connected_map_width - self . smh . width . byte
2012-05-27 16:24:09 -07:00
if ( ( p + ( ( connected_map_height * connected_map_width ) - ( connected_map_width * 3 ) ) ) % 0x4000 ) + 0x4000 == strip_pointer :
# lin's equation:
# p += (otherMap.height * otherMap.width) - (otherMap.width * 3)
2012-05-26 20:19:53 -07:00
p + = ( connected_map_height * connected_map_width ) - ( connected_map_width * 3 )
2012-05-27 16:24:09 -07:00
method = " north1 "
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " + ( " + map_constant_label + " _HEIGHT * " + map_constant_label + " _WIDTH) - ( " + map_constant_label + " _WIDTH * 3)) "
elif ( ( p + connected_map_width + xoffset + ( 16 * connected_map_height ) - 16 ) % 0x4000 ) + 0x4000 == strip_pointer :
p + = connected_map_width + xoffset + ( 16 * connected_map_height ) - 16
method = " north2 "
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " + " + map_constant_label + " _WIDTH + " + str ( xoffset ) + " + (16 * " + map_constant_label + " _HEIGHT) - 16) "
elif p != strip_pointer :
# worst case scenario: we don't know how to calculate p, so we'll just set it as a constant
# example: Route10North north to Route9 (strip_pointer=0x7eae, connected map's blockdata=0x7de9)
p = strip_pointer
method = " north3 "
output + = " $ %.2x " % ( p )
else :
# this doesn't seem to ever happen
# or just do nothing (value is already ok)
method = " north4 "
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " ) "
2012-05-26 20:19:53 -07:00
elif ldirection == " west " :
h = connected_map_height - self . smh . height . byte
2012-05-27 16:24:09 -07:00
h_out = " ( " + map_constant_label + " _HEIGHT - " + self_constant_label + " _HEIGHT) "
if ( ( p + ( h * connected_map_width ) - ( connected_map_width * 3 ) + ( connected_map_width - 1 ) - 2 ) % 0x4000 ) + 0x4000 == strip_pointer :
# lin's method:
# p += (h * otherMap.width) - (otherMap.width * 3) + (otherMap.width - 3)
p + = ( h * connected_map_width ) - ( connected_map_width * 3 ) + ( connected_map_width - 1 ) - 2
method = " west1 "
this_part = " (( " + h_out + " * " + map_constant_label + " _WIDTH) - ( " + map_constant_label + " _WIDTH * 3) + ( " + map_constant_label + " _WIDTH - 1) - 2) "
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " + " + this_part + " ) "
elif ( ( p + connected_map_width - 3 ) % 0x4000 ) + 0x4000 == strip_pointer :
2012-05-26 20:19:53 -07:00
print " west h <= 0 "
2012-05-27 16:24:09 -07:00
# lin's method:
# p += otherMap.width - 3
2012-05-26 20:19:53 -07:00
p + = connected_map_width - 3
2012-05-27 16:24:09 -07:00
method = " west2 "
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " + " + map_constant_label + " _WIDTH - 3) "
elif ( ( p + xoffset + ( current_map_height * 2 ) ) % 0x4000 + 0x4000 ) == strip_pointer :
method = " west3 "
p + = xoffset + ( current_map_height * 2 )
2012-05-27 17:45:44 -07:00
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " + " + str ( xoffset ) + " + ( " + map_constant_label + " _HEIGHT * 2)) "
2012-05-27 16:24:09 -07:00
elif ( p % 0x4000 ) + 0x4000 != strip_pointer :
# worst case scenario: dunno what to do
method = " west4 "
p = strip_pointer
output + = " $ %.2x " % ( ( p % 0x4000 ) + 0x4000 )
else :
# this doesn't seem to ever happen
# do nothing
method = " west5 "
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " ) "
elif ldirection == " south " :
if ( p % 0x4000 ) + 0x4000 == strip_pointer :
# do nothing
method = " south1 "
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " ) "
elif ( ( p + ( xoffset - connection_strip_length + self . smh . width . byte ) / 2 ) % 0x4000 ) + 0x4000 == strip_pointer :
# comet's method
method = " south2 "
p + = ( xoffset - connection_strip_length + self . smh . width . byte ) / 2
this_part = " (( " + str ( xoffset ) + " - " + str ( connection_strip_length ) + " + " + self_constant_label + " _WIDTH) / 2) "
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " + " + this_part + " ) "
elif ( ( p + ( ( xoffset - connection_strip_length + self . smh . width . byte ) / 2 ) - 1 ) % 0x4000 ) + 0x4000 == strip_pointer :
method = " south3 "
p + = ( ( xoffset - connection_strip_length + self . smh . width . byte ) / 2 ) - 1
this_part = " ((( " + str ( xoffset ) + " - " + str ( connection_strip_length ) + " + " + self_constant_label + " _WIDTH) / 2) - 1) "
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " + " + this_part + " ) "
elif ldirection == " east " :
if ( p % 0x4000 ) + 0x4000 == strip_pointer :
# do nothing
method = " east1 "
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " ) "
elif ( ( p + ( connected_map_height - connection_strip_length ) * connected_map_width ) % 0x4000 ) + 0x4000 == strip_pointer :
p + = ( connected_map_height - connection_strip_length ) * connected_map_width
method = " east2 "
this_part = " (( " + map_constant_label + " _HEIGHT - " + connection_strip_length + " ) * " + map_constant_label + " _WIDTH) "
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " + " + this_part + " ) "
elif ( ( p + 100 - 4 * connected_map_width ) % 0x4000 ) + 0x4000 == strip_pointer :
method = " east3 "
p + = 100 - 4 * connected_map_width
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " + 100 - ( " + map_constant_label + " _WIDTH * 4)) "
elif ( ( p + 2 * ( 100 - 4 * connected_map_width ) ) % 0x4000 ) + 0x4000 == strip_pointer :
method = " east4 "
# the "2" is possibly ( connected_map_height / current_map_height )
# or current_map_width/yoffset or connected_map_width/yoffset
p + = 2 * ( 100 - 4 * connected_map_width )
output + = " ( " + get_label_for ( connected_second_map_header . blockdata . address ) + " + ((100 - ( " + map_constant_label + " _WIDTH * 4)) * 2)) "
2012-05-27 17:45:44 -07:00
output + = " ; strip pointer \n "
2012-05-26 20:19:53 -07:00
# tauwasser calls this "connection strip destination" and lin calls this "memoryOtherPointer"
# Points to the upper left block of the connection strip
# (The bank the Blockdata is in, is loaded out of the Mapheader of the connected Map.)
# The connection strip is always 3 Blocks high resp. wide
# (depending on the connection's direction)
2012-05-26 12:46:34 -07:00
strip_destination = self . strip_destination
2012-05-27 17:45:44 -07:00
output + = " dw "
# i am not convinced about these calculations
if ldirection == " north " :
x_movement_of_the_connection_strip_in_blocks = strip_destination - 0xC703
xmov = x_movement_of_the_connection_strip_in_blocks
output + = " ($C703 + " + str ( xmov ) + " ) "
elif ldirection == " south " :
# strip_destination =
# 0xc703 + (current_map_height + 3) * (current_map_width + 6) + x_movement_of_the_connection_strip_in_blocks
x_movement_of_the_connection_strip_in_blocks = strip_destination - ( 0xc703 + ( current_map_height + 3 ) * ( current_map_width + 6 ) )
xmov = x_movement_of_the_connection_strip_in_blocks
2012-05-27 18:02:34 -07:00
output + = " ($C703 + ((( " + self_constant_label + " _HEIGHT + 3) * ( " + self_constant_label + " _WIDTH + 6)) + " + str ( xmov ) + " )) "
2012-05-27 17:45:44 -07:00
elif ldirection == " east " :
# strip_destination =
# 0xc700 + (current_map_width + 6) * (y_movement_of_the_connection_strip_in_blocks + 3)
y_movement_of_the_connection_strip_in_blocks = ( strip_destination - 0xc700 ) / ( current_map_width + 6 ) - 3
ymov = y_movement_of_the_connection_strip_in_blocks
output + = " ($C700 + (( " + self_constant_label + " _WIDTH + 6) * ( " + str ( ymov ) + " + 3))) "
elif ldirection == " west " :
# strip_destination =
# 0xc6fd + (current_map_width + 6) * (y_movement_of_the_connection_strip_in_blocks + 4)
y_movement_of_the_connection_strip_in_blocks = ( strip_destination - 0xc6fd ) / ( current_map_width + 6 ) - 4
ymov = y_movement_of_the_connection_strip_in_blocks
output + = " ($C6FD + (( " + self_constant_label + " _WIDTH + 6) * ( " + str ( ymov ) + " + 4))) "
output + = " ; strip destination \n "
output + = " db " + str ( connection_strip_length , ) + " , " + map_constant_label + " _WIDTH ; (connection strip length, connected map width) \n "
2012-05-26 12:46:34 -07:00
#if ldirection in ["east", "west"]:
# Y_movement_of_connection_strip_in_blocks =
#elif direction in ["north", "south"]:
# X_movement_of_connection_strip_in_blocks =
2012-05-26 20:19:53 -07:00
# Above: (Height_of_connected_map * 2) - 1
2012-05-26 12:46:34 -07:00
# Below: 0
# Left/Right: (Y_movement_of_connection_strip_in_blocks * -2)
yoffset = self . yoffset # y_position_after_map_change
2012-05-26 20:19:53 -07:00
if ldirection == " south " and yoffset != 0 :
raise Exception , " tauwasser was wrong about yoffset=0 for south? it ' s: " + str ( yoffset )
elif ldirection == " north " and yoffset != ( ( connected_map_height * 2 ) - 1 ) :
raise Exception , " tauwasser was wrong about yoffset for north? it ' s: " + str ( yoffset )
#elif not ((yoffset % -2) == 0):
# raise Exception, "tauwasser was wrong about yoffset for west/east? it's not divisible by -2: " + str(yoffset)
2012-05-26 12:46:34 -07:00
# Left: (Width_of_connected_map * 2) - 1
# Right: 0
# Above/Below: (X_movement_of_connection_strip_in_blocks * -2)
xoffset = self . xoffset # x_position_after_map_change
2012-05-26 20:19:53 -07:00
if ldirection == " east " and xoffset != 0 :
raise Exception , " tauwasser was wrong about xoffset=0 for east? it ' s: " + str ( xoffset )
elif ldirection == " west " and xoffset != ( ( connected_map_width * 2 ) - 1 ) :
raise Exception , " tauwasser was wrong about xoffset for west? it ' s: " + str ( xoffset )
#elif not ((xoffset % -2) == 0):
# raise Exception, "tauwasser was wrong about xoffset for north/south? it's not divisible by -2: " + str(xoffset)
2012-05-27 18:02:34 -07:00
output + = " db "
2012-05-27 17:45:44 -07:00
if ldirection == " south " :
2012-05-27 18:02:34 -07:00
output + = " 0 "
2012-05-27 17:45:44 -07:00
elif ldirection == " north " :
2012-05-27 18:02:34 -07:00
output + = " (( " + map_constant_label + " _HEIGHT * 2) - 1) "
2012-05-27 17:45:44 -07:00
else :
2012-05-27 18:02:34 -07:00
output + = str ( yoffset )
2012-05-27 17:45:44 -07:00
2012-05-27 18:02:34 -07:00
output + = " , "
2012-05-27 17:45:44 -07:00
2012-05-26 20:19:53 -07:00
if ldirection == " east " :
2012-05-27 18:02:34 -07:00
output + = " 0 "
2012-05-26 20:19:53 -07:00
elif ldirection == " west " :
2012-05-27 18:02:34 -07:00
output + = " (( " + map_constant_label + " _WIDTH * 2) - 1) "
2012-05-27 17:45:44 -07:00
else :
2012-05-27 18:02:34 -07:00
output + = str ( xoffset )
2012-05-26 20:19:53 -07:00
2012-05-27 18:02:34 -07:00
output + = " ; yoffset, xoffset \n "
2012-05-26 20:19:53 -07:00
2012-05-26 12:46:34 -07:00
window = self . window
2012-05-27 17:45:44 -07:00
output + = " dw "
# let's also check the window equations
# tauwasser calls this "window" and lin calls this "memoryCurrentPointer"
# Position of the upper left block after entering the Map
#
# tauwasser's formula for windows:
# Above: C701h + Height_of_connected_map * (Width_of_connected_map + 6)
# Left: C706h + 2 * Width_of_connected_map
# Below/Right: C707h + Width_of_connected_map
window_worked = False
if ldirection == " north " :
# tauwasser's formula: 0xc701 + connected_map_height * (connected_map_width + 6)
window_start = 0xc801
if window == window_start + ( connected_map_height * 6 ) + ( connected_map_height * connected_map_width ) :
window_worked = True
output + = " ($C801 + (( " + map_constant_label + " _HEIGHT * 6) + ( " + map_constant_label + " _HEIGHT * " + map_constant_label + " _WIDTH))) "
elif ldirection == " east " :
window_start = 0xc807
if window == ( window_start + connected_map_width ) :
window_worked = True
output + = " ($C807 + " + map_constant_label + " _WIDTH) "
elif ldirection == " south " :
window_start = 0xc807
if window == ( window_start + connected_map_width ) :
window_worked = True
output + = " ($C807 + " + map_constant_label + " _WIDTH) "
elif ldirection == " west " :
window_start = 0xc807
if window == ( window_start + xoffset ) :
window_worked = True
output + = " ($C807 + " + str ( xoffset ) + " ) "
output + = " ; window "
2012-05-26 12:46:34 -07:00
return output
2012-04-17 18:22:28 -07:00
2012-04-15 16:24:56 -07:00
all_second_map_headers = [ ]
2012-03-24 02:43:06 -07:00
def parse_second_map_header_at ( address , map_group = None , map_id = None , debug = True ) :
2012-03-05 22:15:35 -08:00
""" each map has a second map header """
2012-04-15 16:24:56 -07:00
smh = SecondMapHeader ( address , map_group = map_group , map_id = map_id , debug = debug )
all_second_map_headers . append ( smh )
return smh
2012-03-05 22:15:35 -08:00
2012-04-15 12:33:41 -07:00
def old_parse_second_map_header_at ( address , map_group = None , map_id = None , debug = True ) :
""" each map has a second map header """
bytes = rom_interval ( address , second_map_header_byte_size , strings = False )
border_block = bytes [ 0 ]
height = bytes [ 1 ]
width = bytes [ 2 ]
blockdata_bank = bytes [ 3 ]
blockdata_pointer = bytes [ 4 ] + ( bytes [ 5 ] << 8 )
blockdata_address = calculate_pointer ( blockdata_pointer , blockdata_bank )
script_bank = bytes [ 6 ]
script_pointer = bytes [ 7 ] + ( bytes [ 8 ] << 8 )
script_address = calculate_pointer ( script_pointer , script_bank )
event_bank = script_bank
event_pointer = bytes [ 9 ] + ( bytes [ 10 ] << 8 )
event_address = calculate_pointer ( event_pointer , event_bank )
connections = bytes [ 11 ]
return {
" border_block " : border_block ,
" height " : height ,
" width " : width ,
" blockdata_bank " : blockdata_bank ,
" blockdata_pointer " : { " 1 " : bytes [ 4 ] , " 2 " : bytes [ 5 ] } ,
" blockdata_address " : blockdata_address ,
" script_bank " : script_bank ,
" script_pointer " : { " 1 " : bytes [ 7 ] , " 2 " : bytes [ 8 ] } ,
" script_address " : script_address ,
" event_bank " : event_bank ,
" event_pointer " : { " 1 " : bytes [ 9 ] , " 2 " : bytes [ 10 ] } ,
" event_address " : event_address ,
" connections " : connections ,
}
2012-04-17 18:22:28 -07:00
2012-04-14 22:06:47 -07:00
class MapBlockData :
base_label = " MapBlockData_ "
maps_path = os . path . realpath ( os . path . join ( os . path . realpath ( " . " ) , " ../maps " ) )
2012-04-17 18:22:28 -07:00
2012-04-14 22:06:47 -07:00
def __init__ ( self , address , map_group = None , map_id = None , debug = True , bank = None , label = None , width = None , height = None ) :
self . address = address
self . map_group = map_group
self . map_id = map_id
self . map_name = map_names [ map_group ] [ map_id ] [ " label " ]
self . map_path = os . path . join ( self . maps_path , self . map_name + " .blk " )
self . debug = debug
self . bank = bank
if width and height :
self . width = width
self . height = height
else :
raise Exception , " MapBlockData needs to know the width/height of its map "
2012-05-20 07:32:59 -07:00
label = self . make_label ( )
self . label = Label ( name = label , address = address , object = self )
2012-04-14 23:18:25 -07:00
self . last_address = self . address + ( self . width . byte * self . height . byte )
2012-04-14 22:06:47 -07:00
script_parse_table [ address : self . last_address ] = self
self . parse ( )
2012-05-20 01:39:25 -07:00
2012-05-16 22:40:40 -07:00
def make_label ( self ) :
return map_names [ self . map_group ] [ self . map_id ] [ " label " ] + " _BlockData "
2012-04-17 18:22:28 -07:00
2012-04-14 22:06:47 -07:00
def save_to_file ( self ) :
#check if the file exists already
map_path = self . map_path
2012-04-19 20:16:33 -07:00
if not os . path . exists ( self . maps_path ) :
os . mkdir ( self . maps_path )
2012-04-14 22:06:47 -07:00
if not os . path . exists ( map_path ) :
#dump to file
2012-04-14 23:18:25 -07:00
#bytes = rom_interval(self.address, self.width.byte*self.height.byte, strings=True)
bytes = rom [ self . address : self . address + self . width . byte * self . height . byte ]
2012-04-14 22:06:47 -07:00
file_handler = open ( map_path , " w " )
file_handler . write ( bytes )
file_handler . close ( )
2012-04-17 18:22:28 -07:00
2012-04-14 22:06:47 -07:00
def parse ( self ) :
self . save_to_file ( )
2012-04-17 18:22:28 -07:00
2012-04-14 22:06:47 -07:00
def to_asm ( self ) :
return " INCBIN \" maps/ " + self . map_name + " .blk \" "
2012-04-17 18:22:28 -07:00
2012-04-14 16:23:55 -07:00
class MapEventHeader :
base_label = " MapEventHeader_ "
2012-04-17 18:22:28 -07:00
2012-04-14 16:23:55 -07:00
def __init__ ( self , address , map_group = None , map_id = None , debug = True , bank = None , label = None ) :
2012-04-15 10:26:46 -07:00
print " making a MapEventHeader at " + hex ( address ) + " map_group= " + str ( map_group ) + " map_id= " + str ( map_id )
2012-04-14 16:23:55 -07:00
self . address = address
self . map_group = map_group
self . map_id = map_id
self . debug = debug
self . bank = bank
2012-04-27 11:41:44 -07:00
self . dependencies = None
2012-05-20 07:32:59 -07:00
label = self . make_label ( )
2012-05-20 07:56:27 -07:00
self . label = Label ( name = label , address = address , object = self )
2012-04-14 16:23:55 -07:00
self . parse ( )
script_parse_table [ address : self . last_address ] = self
2012-05-20 01:39:25 -07:00
2012-05-16 22:40:40 -07:00
def make_label ( self ) :
return map_names [ self . map_group ] [ self . map_id ] [ " label " ] + " _MapEventHeader "
2012-04-17 18:22:28 -07:00
2012-04-14 16:23:55 -07:00
def parse ( self ) :
2012-04-14 18:03:19 -07:00
map_group , map_id , debug = self . map_group , self . map_id , self . debug
2012-04-14 16:23:55 -07:00
address = self . address
bank = calculate_bank ( self . address ) #or use self.bank
print " event header address is: " + hex ( address )
2012-04-17 18:22:28 -07:00
2012-04-14 16:23:55 -07:00
filler1 = ord ( rom [ address ] )
filler2 = ord ( rom [ address + 1 ] )
self . fillers = [ filler1 , filler2 ]
2012-04-17 18:22:28 -07:00
2012-04-14 16:23:55 -07:00
#warps
warp_count = ord ( rom [ address + 2 ] )
warp_byte_count = warp_byte_size * warp_count
after_warps = address + 3 + warp_byte_count
warps = parse_warps ( address + 3 , warp_count , bank = bank , map_group = map_group , map_id = map_id , debug = debug )
self . warp_count = warp_count
self . warps = warps
2012-03-05 22:15:35 -08:00
2012-04-14 16:23:55 -07:00
#triggers (based on xy location)
2012-04-14 18:03:19 -07:00
xy_trigger_count = ord ( rom [ after_warps ] )
trigger_byte_count = trigger_byte_size * xy_trigger_count
xy_triggers = parse_xy_triggers ( after_warps + 1 , xy_trigger_count , bank = bank , map_group = map_group , map_id = map_id , debug = debug )
2012-04-14 16:23:55 -07:00
after_triggers = after_warps + 1 + trigger_byte_count
self . xy_trigger_count = xy_trigger_count
self . xy_triggers = xy_triggers
2012-04-17 18:22:28 -07:00
2012-04-14 16:23:55 -07:00
#signposts
signpost_count = ord ( rom [ after_triggers ] )
signpost_byte_count = signpost_byte_size * signpost_count
#signposts = rom_interval(after_triggers+1, signpost_byte_count)
signposts = parse_signposts ( after_triggers + 1 , signpost_count , bank = bank , map_group = map_group , map_id = map_id , debug = debug )
after_signposts = after_triggers + 1 + signpost_byte_count
self . signpost_count = signpost_count
self . signposts = signposts
2012-04-17 18:22:28 -07:00
2012-04-14 16:23:55 -07:00
#people events
people_event_count = ord ( rom [ after_signposts ] )
people_event_byte_count = people_event_byte_size * people_event_count
#people_events_bytes = rom_interval(after_signposts+1, people_event_byte_count)
#people_events = parse_people_event_bytes(people_events_bytes, address=after_signposts+1, map_group=map_group, map_id=map_id)
2012-04-20 20:19:01 -07:00
people_events = parse_people_events ( after_signposts + 1 , people_event_count , bank = calculate_bank ( after_signposts + 2 ) , map_group = map_group , map_id = map_id , debug = debug )
2012-04-14 16:23:55 -07:00
self . people_event_count = people_event_count
self . people_events = people_events
2012-04-17 18:22:28 -07:00
2012-04-14 16:23:55 -07:00
if people_event_count > 0 :
self . last_address = people_events [ - 1 ] . last_address
else :
self . last_address = after_signposts + 1
return True
2012-05-20 01:39:25 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-27 11:41:44 -07:00
if self . dependencies != None and not recompute :
2012-04-27 12:58:57 -07:00
global_dependencies . update ( self . dependencies )
2012-04-27 11:41:44 -07:00
return self . dependencies
2012-04-21 01:15:56 -07:00
bases = [ ]
bases + = self . people_events
bases + = self . signposts
bases + = self . xy_triggers
bases + = self . warps
2012-05-20 01:39:25 -07:00
2012-04-21 01:15:56 -07:00
dependencies = [ ]
for p in bases :
2012-04-27 12:58:57 -07:00
dependencies . extend ( p . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
2012-04-27 11:41:44 -07:00
self . dependencies = dependencies
2012-04-20 16:26:52 -07:00
return dependencies
2012-04-17 18:22:28 -07:00
2012-04-14 16:23:55 -07:00
def to_asm ( self ) :
2012-04-14 19:46:04 -07:00
xspacing = " " #was =spacing
2012-04-26 12:26:16 -07:00
output = " ; filler \n "
output + = " db %d , %d \n \n " % ( self . fillers [ 0 ] , self . fillers [ 1 ] )
2012-04-14 19:46:04 -07:00
output + = xspacing + " ; warps \n "
2012-04-20 20:19:01 -07:00
output + = xspacing + " db %d " % ( self . warp_count )
if len ( self . warps ) > 0 :
output + = " \n "
2012-04-14 19:46:04 -07:00
output + = " \n " . join ( [ xspacing + warp . to_asm ( ) for warp in self . warps ] )
2012-03-05 22:15:35 -08:00
2012-04-14 16:23:55 -07:00
output + = " \n \n "
2012-04-14 19:46:04 -07:00
output + = xspacing + " ; xy triggers \n "
2012-04-20 20:19:01 -07:00
output + = xspacing + " db %d " % ( self . xy_trigger_count )
if len ( self . xy_triggers ) > 0 :
output + = " \n "
2012-04-14 19:46:04 -07:00
output + = " \n " . join ( [ xspacing + xy_trigger . to_asm ( ) for xy_trigger in self . xy_triggers ] )
2012-04-14 16:23:55 -07:00
output + = " \n \n "
2012-04-14 19:46:04 -07:00
output + = xspacing + " ; signposts \n "
2012-04-20 20:19:01 -07:00
output + = xspacing + " db %d " % ( self . signpost_count )
if len ( self . signposts ) > 0 :
output + = " \n "
2012-04-14 19:46:04 -07:00
output + = " \n " . join ( [ xspacing + signpost . to_asm ( ) for signpost in self . signposts ] )
2012-04-14 16:23:55 -07:00
output + = " \n \n "
2012-04-14 19:46:04 -07:00
output + = xspacing + " ; people-events \n "
2012-04-20 20:19:01 -07:00
output + = xspacing + " db %d " % ( self . people_event_count )
if len ( self . people_events ) > 0 :
output + = " \n "
for people_event in self . people_events :
output + = xspacing
output + = people_event . to_asm ( )
output + = " \n "
2012-05-20 01:39:25 -07:00
2012-04-21 08:24:44 -07:00
if output [ - 1 ] == " \n " :
output = output [ : - 1 ]
2012-04-14 16:23:55 -07:00
return output
2012-04-15 16:24:56 -07:00
all_map_event_headers = [ ]
2012-04-14 16:23:55 -07:00
def parse_map_event_header_at ( address , map_group = None , map_id = None , debug = True , bank = None ) :
""" parse crystal map event header byte structure thing """
2012-04-15 16:24:56 -07:00
ev = MapEventHeader ( address , map_group = map_group , map_id = map_id , debug = debug , bank = bank )
all_map_event_headers . append ( ev )
return ev
2012-03-05 22:15:35 -08:00
2012-04-15 12:33:41 -07:00
def old_parse_map_event_header_at ( address , map_group = None , map_id = None , debug = True ) :
""" parse crystal map event header byte structure thing """
returnable = { }
bank = calculate_bank ( address )
print " event header address is: " + hex ( address )
filler1 = ord ( rom [ address ] )
filler2 = ord ( rom [ address + 1 ] )
returnable . update ( { " 1 " : filler1 , " 2 " : filler2 } )
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
#warps
warp_count = ord ( rom [ address + 2 ] )
warp_byte_count = warp_byte_size * warp_count
warps = rom_interval ( address + 3 , warp_byte_count )
after_warps = address + 3 + warp_byte_count
returnable . update ( { " warp_count " : warp_count , " warps " : old_parse_warp_bytes ( warps ) } )
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
#triggers (based on xy location)
trigger_count = ord ( rom [ after_warps ] )
trigger_byte_count = trigger_byte_size * trigger_count
triggers = rom_interval ( after_warps + 1 , trigger_byte_count )
after_triggers = after_warps + 1 + trigger_byte_count
returnable . update ( { " xy_trigger_count " : trigger_count , " xy_triggers " : old_parse_xy_trigger_bytes ( triggers , bank = bank , map_group = map_group , map_id = map_id ) } )
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
#signposts
signpost_count = ord ( rom [ after_triggers ] )
signpost_byte_count = signpost_byte_size * signpost_count
signposts = rom_interval ( after_triggers + 1 , signpost_byte_count )
after_signposts = after_triggers + 1 + signpost_byte_count
2012-04-15 14:37:31 -07:00
returnable . update ( { " signpost_count " : signpost_count , " signposts " : old_parse_signpost_bytes ( signposts , bank = bank , map_group = map_group , map_id = map_id ) } )
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
#people events
people_event_count = ord ( rom [ after_signposts ] )
people_event_byte_count = people_event_byte_size * people_event_count
people_events_bytes = rom_interval ( after_signposts + 1 , people_event_byte_count )
people_events = old_parse_people_event_bytes ( people_events_bytes , address = after_signposts + 1 , map_group = map_group , map_id = map_id )
returnable . update ( { " people_event_count " : people_event_count , " people_events " : people_events } )
return returnable
2012-04-17 18:22:28 -07:00
2012-04-14 22:50:48 -07:00
class MapScriptHeader :
2012-03-05 22:15:35 -08:00
""" parses a script header
2012-04-17 18:22:28 -07:00
2012-03-05 22:15:35 -08:00
This structure allows the game to have e . g . one - time only events on a map
or first enter events or permanent changes to the map or permanent script
calls .
This header a combination of a trigger script section and a callback script
section . I don ' t know if these ' trigger scripts ' are the same as the others
referenced in the map event header , so this might need to be renamed very
2012-04-14 22:50:48 -07:00
soon . The scripts in MapEventHeader are called XYTrigger .
2012-03-05 22:15:35 -08:00
2012-04-17 18:22:28 -07:00
trigger scripts :
2012-03-05 22:15:35 -08:00
[ [ Number1 of pointers ] Number1 * [ 2 byte pointer to script ] [ 00 ] [ 00 ] ]
callback scripts :
[ [ Number2 of pointers ] Number2 * [ hook number ] [ 2 byte pointer to script ] ]
hook byte choices :
01 - map data has already been loaded to ram , tileset and sprites still missing
map change ( 3 rd step )
loading ( 2 nd step )
map connection ( 3 rd step )
after battle ( 1 st step )
02 - map data , tileset and sprites are all loaded
map change ( 5 th step )
03 - neither map data not tilesets nor sprites are loaded
map change ( 2 nd step )
loading ( 1 st step )
map connection ( 2 nd step )
04 - map data and tileset loaded , sprites still missing
map change ( 4 th step )
loading ( 3 rd step )
sprite reload ( 1 st step )
map connection ( 4 th step )
after battle ( 2 nd step )
05 - neither map data not tilesets nor sprites are loaded
map change ( 1 st step )
map connection ( 1 st step )
When certain events occur , the call backs will be called in this order ( same info as above ) :
map change :
05 , 03 , 01 , 04 , 02
loading :
03 , 01 , 04
sprite reload :
04
map connection :
05 , 03 , 01 , 04 note that #2 is not called (unlike "map change")
after battle :
01 , 04
"""
2012-04-14 22:50:48 -07:00
base_label = " MapScriptHeader_ "
2012-04-17 18:22:28 -07:00
2012-04-14 22:50:48 -07:00
def __init__ ( self , address , map_group = None , map_id = None , debug = True , bank = None , label = None ) :
2012-04-15 10:26:46 -07:00
print " creating a MapScriptHeader at " + hex ( address ) + " map_group= " + str ( map_group ) + " map_id= " + str ( map_id )
2012-04-14 22:50:48 -07:00
self . address = address
self . map_group = map_group
self . map_id = map_id
self . debug = debug
self . bank = bank
2012-04-27 11:41:44 -07:00
self . dependencies = None
2012-05-20 07:32:59 -07:00
label = self . make_label ( )
self . label = Label ( name = label , address = address , object = self )
2012-04-14 22:50:48 -07:00
self . parse ( )
script_parse_table [ address : self . last_address ] = self
2012-05-20 01:39:25 -07:00
2012-05-16 22:40:40 -07:00
def make_label ( self ) :
return map_names [ self . map_group ] [ self . map_id ] [ " label " ] + " _MapScriptHeader "
2012-04-17 18:22:28 -07:00
2012-04-14 22:50:48 -07:00
def parse ( self ) :
address = self . address
map_group = self . map_group
map_id = self . map_id
debug = self . debug
#[[Number1 of pointers] Number1 * [2byte pointer to script][00][00]]
self . trigger_count = ord ( rom [ address ] )
self . triggers = [ ]
ptr_line_size = 4
groups = grouper ( rom_interval ( address + 1 , self . trigger_count * ptr_line_size , strings = False ) , count = ptr_line_size )
2012-04-15 15:21:07 -07:00
current_address = address + 1
2012-04-14 22:50:48 -07:00
for ( index , trigger_bytes ) in enumerate ( groups ) :
2012-04-15 10:26:46 -07:00
print " parsing a map trigger script at " + hex ( current_address ) + " map_group= " + str ( map_group ) + " map_id= " + str ( map_id )
2012-04-14 23:18:25 -07:00
script = ScriptPointerLabelParam ( address = current_address , map_group = map_group , map_id = map_id , debug = debug )
2012-04-25 16:51:54 -07:00
extra_bytes = MultiByteParam ( address = current_address + 2 , map_group = map_group , map_id = map_id , debug = debug )
self . triggers . append ( [ script , extra_bytes ] )
2012-04-14 22:50:48 -07:00
current_address + = ptr_line_size
current_address = address + ( self . trigger_count * ptr_line_size ) + 1
#[[Number2 of pointers] Number2 * [hook number][2byte pointer to script]]
callback_ptr_line_size = 3
2012-04-14 23:27:20 -07:00
self . callback_count = DecimalParam ( address = current_address )
2012-04-15 20:12:10 -07:00
self . callback_count = self . callback_count . byte
2012-04-15 10:26:46 -07:00
current_address + = 1
2012-04-14 22:50:48 -07:00
self . callbacks = [ ]
2012-04-15 20:12:10 -07:00
for index in range ( self . callback_count ) :
2012-04-15 10:26:46 -07:00
print " parsing a callback script at " + hex ( current_address ) + " map_group= " + str ( map_group ) + " map_id= " + str ( map_id )
2012-04-14 23:27:20 -07:00
hook_byte = HexByte ( address = current_address )
2012-04-14 23:18:25 -07:00
callback = ScriptPointerLabelParam ( address = current_address + 1 , map_group = map_group , map_id = map_id , debug = debug )
2012-04-14 23:27:20 -07:00
self . callbacks . append ( { " hook " : hook_byte , " callback " : callback } )
current_address + = 3 #i think?
self . last_address = current_address
2012-04-15 10:26:46 -07:00
print " done parsing a MapScriptHeader map_group= " + str ( map_group ) + " map_id= " + str ( map_id )
2012-04-14 22:50:48 -07:00
return True
2012-05-20 01:39:25 -07:00
2012-04-27 12:58:57 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
2012-04-27 11:41:44 -07:00
if self . dependencies != None and not recompute :
2012-04-27 12:58:57 -07:00
global_dependencies . update ( self . dependencies )
2012-04-27 11:41:44 -07:00
return self . dependencies
2012-04-25 16:51:54 -07:00
dependencies = [ ]
for p in list ( self . triggers ) :
#dependencies.append(p[0])
2012-04-27 12:58:57 -07:00
dependencies . extend ( p [ 0 ] . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
2012-04-20 16:26:52 -07:00
for callback in self . callbacks :
2012-04-20 20:19:01 -07:00
dependencies . append ( callback [ " callback " ] )
2012-04-27 12:58:57 -07:00
global_dependencies . add ( callback [ " callback " ] )
dependencies . extend ( callback [ " callback " ] . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) )
2012-04-27 11:41:44 -07:00
self . dependencies = dependencies
2012-04-20 16:26:52 -07:00
return dependencies
2012-04-17 18:22:28 -07:00
2012-04-14 22:50:48 -07:00
def to_asm ( self ) :
output = " "
output + = " ; trigger count \n "
2012-04-15 20:12:10 -07:00
output + = " db %d \n " % self . trigger_count
if len ( self . triggers ) > 0 :
output + = " \n ; triggers \n "
2012-04-25 16:51:54 -07:00
output + = " \n " . join ( [ str ( " dw " + p [ 0 ] . to_asm ( ) + " , " + p [ 1 ] . to_asm ( ) ) for p in self . triggers ] )
2012-04-21 16:09:03 -07:00
output + = " \n "
2012-04-15 20:12:10 -07:00
output + = " \n ; callback count \n "
2012-04-21 01:15:56 -07:00
output + = " db %d " % self . callback_count
2012-04-15 20:12:10 -07:00
if len ( self . callbacks ) > 0 :
2012-04-25 18:23:10 -07:00
output + = " \n \n ; callbacks \n \n "
2012-04-26 12:46:46 -07:00
output + = " \n \n " . join ( [ " dbw " + str ( p [ " hook " ] . byte ) + " , " + p [ " callback " ] . to_asm ( ) for p in self . callbacks ] )
2012-04-14 22:50:48 -07:00
return output
2012-04-15 16:24:56 -07:00
all_map_script_headers = [ ]
2012-04-14 22:50:48 -07:00
def parse_map_script_header_at ( address , map_group = None , map_id = None , debug = True ) :
2012-04-15 16:24:56 -07:00
evv = MapScriptHeader ( address , map_group = map_group , map_id = map_id , debug = debug )
all_map_script_headers . append ( evv )
return evv
2012-03-05 22:15:35 -08:00
2012-04-15 12:33:41 -07:00
def old_parse_map_script_header_at ( address , map_group = None , map_id = None , debug = True ) :
print " starting to parse the map ' s script header.. "
#[[Number1 of pointers] Number1 * [2byte pointer to script][00][00]]
ptr_line_size = 4 #[2byte pointer to script][00][00]
trigger_ptr_cnt = ord ( rom [ address ] )
trigger_pointers = grouper ( rom_interval ( address + 1 , trigger_ptr_cnt * ptr_line_size , strings = False ) , count = ptr_line_size )
triggers = { }
for index , trigger_pointer in enumerate ( trigger_pointers ) :
print " parsing a trigger header... "
byte1 = trigger_pointer [ 0 ]
byte2 = trigger_pointer [ 1 ]
ptr = byte1 + ( byte2 << 8 )
trigger_address = calculate_pointer ( ptr , calculate_bank ( address ) )
trigger_script = parse_script_engine_script_at ( trigger_address , map_group = map_group , map_id = map_id )
triggers [ index ] = {
" script " : trigger_script ,
" address " : trigger_address ,
" pointer " : { " 1 " : byte1 , " 2 " : byte2 } ,
}
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
#bump ahead in the byte stream
address + = trigger_ptr_cnt * ptr_line_size + 1
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
#[[Number2 of pointers] Number2 * [hook number][2byte pointer to script]]
callback_ptr_line_size = 3
callback_ptr_cnt = ord ( rom [ address ] )
callback_ptrs = grouper ( rom_interval ( address + 1 , callback_ptr_cnt * callback_ptr_line_size , strings = False ) , count = callback_ptr_line_size )
callback_pointers = { }
callbacks = { }
for index , callback_line in enumerate ( callback_ptrs ) :
print " parsing a callback header... "
hook_byte = callback_line [ 0 ] #1, 2, 3, 4, 5
callback_byte1 = callback_line [ 1 ]
callback_byte2 = callback_line [ 2 ]
callback_ptr = callback_byte1 + ( callback_byte2 << 8 )
callback_address = calculate_pointer ( callback_ptr , calculate_bank ( address ) )
callback_script = parse_script_engine_script_at ( callback_address )
callback_pointers [ len ( callback_pointers . keys ( ) ) ] = [ hook_byte , callback_ptr ]
callbacks [ index ] = {
" script " : callback_script ,
" address " : callback_address ,
" pointer " : { " 1 " : callback_byte1 , " 2 " : callback_byte2 } ,
}
2012-04-17 18:22:28 -07:00
2012-04-15 12:33:41 -07:00
#XXX do these triggers/callbacks call asm or script engine scripts?
return {
#"trigger_ptr_cnt": trigger_ptr_cnt,
" trigger_pointers " : trigger_pointers ,
#"callback_ptr_cnt": callback_ptr_cnt,
#"callback_ptr_scripts": callback_ptrs,
" callback_pointers " : callback_pointers ,
" trigger_scripts " : triggers ,
" callback_scripts " : callbacks ,
}
2012-04-14 15:58:15 -07:00
def old_parse_trainer_header_at ( address , map_group = None , map_id = None , debug = True ) :
bank = calculate_bank ( address )
bytes = rom_interval ( address , 12 , strings = False )
bit_number = bytes [ 0 ] + ( bytes [ 1 ] << 8 )
trainer_group = bytes [ 2 ]
trainer_id = bytes [ 3 ]
text_when_seen_ptr = calculate_pointer_from_bytes_at ( address + 4 , bank = bank )
text_when_seen = parse_text_engine_script_at ( text_when_seen_ptr , map_group = map_group , map_id = map_id , debug = debug )
text_when_trainer_beaten_ptr = calculate_pointer_from_bytes_at ( address + 6 , bank = bank )
text_when_trainer_beaten = parse_text_engine_script_at ( text_when_trainer_beaten_ptr , map_group = map_group , map_id = map_id , debug = debug )
if [ ord ( rom [ address + 8 ] ) , ord ( rom [ address + 9 ] ) ] == [ 0 , 0 ] :
script_when_lost_ptr = 0
script_when_lost = None
else :
print " parsing script-when-lost "
script_when_lost_ptr = calculate_pointer_from_bytes_at ( address + 8 , bank = bank )
script_when_lost = None
silver_avoids = [ 0xfa53 ]
if script_when_lost_ptr > 0x4000 and not script_when_lost_ptr in silver_avoids :
script_when_lost = parse_script_engine_script_at ( script_when_lost_ptr , map_group = map_group , map_id = map_id , debug = debug )
2012-04-17 18:22:28 -07:00
2012-04-14 15:58:15 -07:00
print " parsing script-talk-again " #or is this a text?
script_talk_again_ptr = calculate_pointer_from_bytes_at ( address + 10 , bank = bank )
script_talk_again = None
if script_talk_again_ptr > 0x4000 :
script_talk_again = parse_script_engine_script_at ( script_talk_again_ptr , map_group = map_group , map_id = map_id , debug = debug )
2012-04-17 18:22:28 -07:00
2012-04-14 15:58:15 -07:00
return {
" bit_number " : bit_number ,
" trainer_group " : trainer_group ,
" trainer_id " : trainer_id ,
" text_when_seen_ptr " : text_when_seen_ptr ,
" text_when_seen " : text_when_seen ,
" text_when_trainer_beaten_ptr " : text_when_trainer_beaten_ptr ,
" text_when_trainer_beaten " : text_when_trainer_beaten ,
" script_when_lost_ptr " : script_when_lost_ptr ,
" script_when_lost " : script_when_lost ,
" script_talk_again_ptr " : script_talk_again_ptr ,
" script_talk_again " : script_talk_again ,
}
def old_parse_people_event_bytes ( some_bytes , address = None , map_group = None , map_id = None , debug = True ) :
""" parse some number of people-events from the data
see PeopleEvent
see http : / / hax . iimarck . us / files / scriptingcodes_eng . htm #Scripthdr
For example , map 1.1 ( group 1 map 1 ) has four person - events .
2012-04-17 18:22:28 -07:00
2012-04-14 15:58:15 -07:00
37 05 07 06 00 FF FF 00 00 02 40 FF FF
3 B 08 0 C 05 01 FF FF 00 00 05 40 FF FF
3 A 07 06 06 00 FF FF A0 00 08 40 FF FF
29 05 0 B 06 00 FF FF 00 00 0 B 40 FF FF
2012-04-17 18:22:28 -07:00
2012-04-14 15:58:15 -07:00
max of 14 people per map ?
"""
assert len ( some_bytes ) % people_event_byte_size == 0 , " wrong number of bytes "
#address is not actually required for this function to work...
bank = None
if address :
bank = calculate_bank ( address )
people_events = [ ]
for bytes in grouper ( some_bytes , count = people_event_byte_size ) :
pict = int ( bytes [ 0 ] , 16 )
y = int ( bytes [ 1 ] , 16 ) #y from top + 4
x = int ( bytes [ 2 ] , 16 ) #x from left + 4
face = int ( bytes [ 3 ] , 16 ) #0-4 for regular, 6-9 for static facing
move = int ( bytes [ 4 ] , 16 )
clock_time_byte1 = int ( bytes [ 5 ] , 16 )
clock_time_byte2 = int ( bytes [ 6 ] , 16 )
color_function_byte = int ( bytes [ 7 ] , 16 ) #Color|Function
trainer_sight_range = int ( bytes [ 8 ] , 16 )
2012-04-17 18:22:28 -07:00
2012-04-14 15:58:15 -07:00
lower_bits = color_function_byte & 0xF
#lower_bits_high = lower_bits >> 2
#lower_bits_low = lower_bits & 3
higher_bits = color_function_byte >> 4
#higher_bits_high = higher_bits >> 2
#higher_bits_low = higher_bits & 3
is_regular_script = lower_bits == 00
#pointer points to script
is_give_item = lower_bits == 01
#pointer points to [Item no.][Amount]
is_trainer = lower_bits == 02
#pointer points to trainer header
#goldmap called these next two bytes "text_block" and "text_bank"?
script_pointer_byte1 = int ( bytes [ 9 ] , 16 )
script_pointer_byte2 = int ( bytes [ 10 ] , 16 )
script_pointer = script_pointer_byte1 + ( script_pointer_byte2 << 8 )
#calculate the full address by assuming it's in the current bank
#but what if it's not in the same bank?
extra_portion = { }
if bank :
ptr_address = calculate_pointer ( script_pointer , bank )
if is_regular_script :
print " parsing a person-script at x= " + str ( x - 4 ) + " y= " + str ( y - 4 ) + " address= " + hex ( ptr_address )
script = parse_script_engine_script_at ( ptr_address , map_group = map_group , map_id = map_id )
extra_portion = {
" script_address " : ptr_address ,
" script " : script ,
" event_type " : " script " ,
}
if is_give_item :
print " ... not parsing give item event... [item id][quantity] "
extra_portion = {
" event_type " : " give_item " ,
" give_item_data_address " : ptr_address ,
" item_id " : ord ( rom [ ptr_address ] ) ,
" item_qty " : ord ( rom [ ptr_address + 1 ] ) ,
}
if is_trainer :
print " parsing a trainer (person-event) at x= " + str ( x ) + " y= " + str ( y )
2012-04-15 12:33:41 -07:00
parsed_trainer = old_parse_trainer_header_at ( ptr_address , map_group = map_group , map_id = map_id )
2012-04-14 15:58:15 -07:00
extra_portion = {
" event_type " : " trainer " ,
" trainer_data_address " : ptr_address ,
" trainer_data " : parsed_trainer ,
}
2012-04-17 18:22:28 -07:00
2012-04-14 15:58:15 -07:00
#XXX not sure what's going on here
#bit no. of bit table 1 (hidden if set)
#note: FFFF for none
when_byte = int ( bytes [ 11 ] , 16 )
hide = int ( bytes [ 12 ] , 16 )
bit_number_of_bit_table1_byte2 = int ( bytes [ 11 ] , 16 )
bit_number_of_bit_table1_byte1 = int ( bytes [ 12 ] , 16 )
bit_number_of_bit_table1 = bit_number_of_bit_table1_byte1 + ( bit_number_of_bit_table1_byte2 << 8 )
people_event = {
" pict " : pict ,
" y " : y , #y from top + 4
" x " : x , #x from left + 4
" face " : face , #0-4 for regular, 6-9 for static facing
" move " : move ,
" clock_time " : { " 1 " : clock_time_byte1 ,
" 2 " : clock_time_byte2 } , #clock/time setting byte 1
" color_function_byte " : color_function_byte , #Color|Function
" trainer_sight_range " : trainer_sight_range , #trainer range of sight
" script_pointer " : { " 1 " : script_pointer_byte1 ,
" 2 " : script_pointer_byte2 } ,
#"text_block": text_block, #script pointer byte 1
#"text_bank": text_bank, #script pointer byte 2
" when_byte " : when_byte , #bit no. of bit table 1 (hidden if set)
" hide " : hide , #note: FFFF for none
" is_trainer " : is_trainer ,
" is_regular_script " : is_regular_script ,
" is_give_item " : is_give_item ,
}
people_event . update ( extra_portion )
people_events . append ( people_event )
return people_events
2012-03-11 23:09:25 -07:00
def parse_map_header_by_id ( * args , * * kwargs ) :
""" convenience function to parse a specific map """
map_group , map_id = None , None
if " map_group " in kwargs . keys ( ) :
map_group = kwargs [ " map_group " ]
if " map_id " in kwargs . keys ( ) :
map_id = kwargs [ " map_id " ]
if ( map_group == None and map_id != None ) or \
( map_group != None and map_id == None ) :
raise Exception , " map_group and map_id must both be provided "
elif map_group == None and map_id == None and len ( args ) == 0 :
raise Exception , " must be given an argument "
elif len ( args ) == 1 and type ( args [ 0 ] ) == str :
map_group = int ( args [ 0 ] . split ( " . " ) [ 0 ] )
map_id = int ( args [ 0 ] . split ( " . " ) [ 1 ] )
else :
raise Exception , " dunno what to do with input "
offset = map_names [ map_group ] [ " offset " ]
map_header_offset = offset + ( ( map_id - 1 ) * map_header_byte_size )
return parse_map_header_at ( map_header_offset , map_group = map_group , map_id = map_id )
2012-03-24 02:43:06 -07:00
def parse_all_map_headers ( debug = True ) :
2012-03-05 22:15:35 -08:00
""" calls parse_map_header_at for each map in each map group """
global map_names
if not map_names [ 1 ] . has_key ( " offset " ) :
2012-03-11 23:09:25 -07:00
raise Exception , " dunno what to do - map_names should have groups with pre-calculated offsets by now "
2012-03-05 22:15:35 -08:00
for group_id , group_data in map_names . items ( ) :
offset = group_data [ " offset " ]
#we only care about the maps
2012-03-11 23:09:25 -07:00
#del group_data["offset"]
2012-03-05 22:15:35 -08:00
for map_id , map_data in group_data . items ( ) :
2012-03-11 23:09:25 -07:00
if map_id == " offset " : continue #skip the "offset" address for this map group
2012-03-24 02:43:06 -07:00
if debug : print " map_group is: " + str ( group_id ) + " map_id is: " + str ( map_id )
2012-03-11 23:09:25 -07:00
map_header_offset = offset + ( ( map_id - 1 ) * map_header_byte_size )
2012-04-15 14:37:31 -07:00
map_names [ group_id ] [ map_id ] [ " header_offset " ] = map_header_offset
2012-04-17 18:22:28 -07:00
2012-04-15 12:39:13 -07:00
new_parsed_map = parse_map_header_at ( map_header_offset , map_group = group_id , map_id = map_id , debug = debug )
map_names [ group_id ] [ map_id ] [ " header_new " ] = new_parsed_map
2012-04-15 19:47:12 -07:00
old_parsed_map = old_parse_map_header_at ( map_header_offset , map_group = group_id , map_id = map_id , debug = debug )
map_names [ group_id ] [ map_id ] [ " header_old " ] = old_parsed_map
2012-03-05 22:15:35 -08:00
2012-05-23 17:21:52 -07:00
class PokedexEntryPointerTable :
""" A list of pointers.
"""
def __init__ ( self ) :
self . address = 0x44378
self . target_bank = calculate_bank ( 0x181695 )
self . label = Label ( name = " PokedexDataPointerTable " , address = self . address , object = self )
self . size = None
self . last_address = None
self . dependencies = None
self . entries = [ ]
self . parse ( )
script_parse_table [ self . address : self . last_address ] = self
2012-05-23 17:40:12 -07:00
2012-05-23 17:21:52 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
global_dependencies . update ( self . entries )
dependencies = [ ]
[ dependencies . extend ( entry . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies ) ) for entry in self . entries ]
return dependencies
2012-05-23 17:40:12 -07:00
2012-05-23 17:21:52 -07:00
def parse ( self ) :
size = 0
lastpointer = 0
for i in range ( 251 ) :
# Those are consecutive in GS!
if i == 0x40 :
self . target_bank = 0x6e
elif i == 0x80 :
self . target_bank = 0x73
elif i == 0xc0 :
self . target_bank = 0x74
loc = self . address + ( i * 2 )
pointer = calculate_pointer_from_bytes_at ( loc , bank = self . target_bank )
#print(hex(pointer))
#if pointer < lastpointer:
# self.target_bank += 1
# pointer += 0x4000
2012-05-23 17:40:12 -07:00
self . entries . append ( PokedexEntry ( pointer , i + 1 ) )
2012-05-23 17:21:52 -07:00
size + = 2
self . size = size
self . last_address = self . address + self . size
def to_asm ( self ) :
output = " " . join ( [ str ( " dw " + get_label_for ( entry . address ) + " \n " ) for entry in self . entries ] )
return output
class PokedexEntry :
""" """
def __init__ ( self , address , pokemon_id ) :
self . address = address
self . dependencies = None
#label = self.make_label()
if pokemon_id in pokemon_constants :
pokename = string . capwords ( pokemon_constants [ pokemon_id ] . replace ( " __ " , " " ) . replace ( " _ " , " " ) ) . replace ( " " , " " )
else :
pokename = " Pokemon {0} " . format ( pokemon_id )
self . label = Label ( name = pokename + " PokedexEntry " , address = self . address , object = self )
self . parse ( )
script_parse_table [ address : self . last_address ] = self
2012-05-23 17:40:12 -07:00
2012-05-23 17:21:52 -07:00
def get_dependencies ( self , recompute = False , global_dependencies = set ( ) ) :
return [ ]
def parse ( self ) :
# eww.
address = self . address
jump = how_many_until ( chr ( 0x50 ) , address )
self . species = parse_text_at ( address , jump + 1 )
address = address + jump + 1
2012-05-23 17:40:12 -07:00
2012-05-23 17:21:52 -07:00
self . weight = ord ( rom [ address ] ) + ( ord ( rom [ address + 1 ] ) << 8 )
self . height = ord ( rom [ address + 2 ] ) + ( ord ( rom [ address + 3 ] ) << 8 )
address + = 4
2012-05-23 17:40:12 -07:00
2012-05-23 17:21:52 -07:00
jump = how_many_until ( chr ( 0x50 ) , address )
self . page1 = PokedexText ( address )
address = address + jump + 1
jump = how_many_until ( chr ( 0x50 ) , address )
self . page2 = PokedexText ( address )
2012-05-23 17:40:12 -07:00
2012-05-24 20:01:50 -07:00
self . last_address = address + jump + 1
2012-05-23 17:21:52 -07:00
#print(self.to_asm())
return True
def to_asm ( self ) :
output = """ \
db " {0} " ; species name
2012-05-23 17:40:12 -07:00
dw { 1 } , { 2 } ; height , weight
2012-05-23 17:21:52 -07:00
{ 3 }
{ 4 } """ .format(self.species, self.weight, self.height, self.page1.to_asm(), self.page2.to_asm())
return output
2012-04-17 18:22:28 -07:00
#map names with no labels will be generated at the end of the structure
2012-03-05 22:15:35 -08:00
map_names = {
1 : {
0x1 : { " name " : " Olivine Pokémon Center 1F " ,
" label " : " OlivinePokeCenter1F " } ,
0x2 : { " name " : " Olivine Gym " } ,
0x3 : { " name " : " Olivine Voltorb House " } ,
0x4 : { " name " : " Olivine House Beta " } ,
0x5 : { " name " : " Olivine Punishment Speech House " } ,
0x6 : { " name " : " Olivine Good Rod House " } ,
0x7 : { " name " : " Olivine Cafe " } ,
0x8 : { " name " : " Olivine Mart " } ,
0x9 : { " name " : " Route 38 Ecruteak Gate " } ,
0xA : { " name " : " Route 39 Barn " } ,
0xB : { " name " : " Route 39 Farmhouse " } ,
0xC : { " name " : " Route 38 " } ,
0xD : { " name " : " Route 39 " } ,
0xE : { " name " : " Olivine City " } ,
} ,
2 : {
0x1 : { " name " : " Mahogany Red Gyarados Speech House " } ,
0x2 : { " name " : " Mahogany Gym " } ,
0x3 : { " name " : " Mahogany Pokémon Center 1F " ,
" label " : " MahoganyPokeCenter1F " } ,
0x4 : { " name " : " Route 42 Ecruteak Gate " } ,
0x5 : { " name " : " Route 42 " } ,
2012-03-09 20:26:00 -08:00
0x6 : { " name " : " Route 44 " } ,
2012-03-05 22:15:35 -08:00
0x7 : { " name " : " Mahogany Town " } ,
} ,
3 : {
0x1 : { " name " : " Sprout Tower 1F " } ,
0x2 : { " name " : " Sprout Tower 2F " } ,
0x3 : { " name " : " Sprout Tower 3F " } ,
0x4 : { " name " : " Tin Tower 1F " } ,
0x5 : { " name " : " Tin Tower 2F " } ,
0x6 : { " name " : " Tin Tower 3F " } ,
0x7 : { " name " : " Tin Tower 4F " } ,
0x8 : { " name " : " Tin Tower 5F " } ,
0x9 : { " name " : " Tin Tower 6F " } ,
0xA : { " name " : " Tin Tower 7F " } ,
0xB : { " name " : " Tin Tower 8F " } ,
0xC : { " name " : " Tin Tower 9F " } ,
2012-03-24 01:54:46 -07:00
0xD : { " name " : " Burned Tower 1F " } ,
2012-03-05 22:15:35 -08:00
0xE : { " name " : " Burned Tower B1F " } ,
0xF : { " name " : " National Park " } ,
0x10 : { " name " : " National Park Bug Contest " } ,
0x11 : { " name " : " Radio Tower 1F " } ,
0x12 : { " name " : " Radio Tower 2F " } ,
0x13 : { " name " : " Radio Tower 3F " } ,
0x14 : { " name " : " Radio Tower 4F " } ,
0x15 : { " name " : " Radio Tower 5F " } ,
0x16 : { " name " : " Ruins of Alph Outside " } ,
0x17 : { " name " : " Ruins of Alph Ho-oh Chamber " } ,
0x18 : { " name " : " Ruins of Alph Kabuto Chamber " } ,
0x19 : { " name " : " Ruins of Alph Omanyte Chamber " } ,
0x1A : { " name " : " Ruins of Alph Aerodactyl Chamber " } ,
0x1B : { " name " : " Ruins of Alph Inner Chamber " } ,
0x1C : { " name " : " Ruins of Alph Research Center " } ,
0x1D : { " name " : " Ruins of Alph Ho-oh Item Room " } ,
0x1E : { " name " : " Ruins of Alph Kabuto Item Room " } ,
0x1F : { " name " : " Ruins of Alph Omanyte Item Room " } ,
0x20 : { " name " : " Ruins of Alph Aerodactyl Item Room " } ,
0x21 : { " name " : " Ruins of Alph Ho-Oh Word Room " } ,
0x22 : { " name " : " Ruins of Alph Kabuto Word Room " } ,
0x23 : { " name " : " Ruins of Alph Omanyte Word Room " } ,
0x24 : { " name " : " Ruins of Alph Aerodactyl Word Room " } ,
0x25 : { " name " : " Union Cave 1F " } ,
0x26 : { " name " : " Union Cave B1F " } ,
0x27 : { " name " : " Union Cave B2F " } ,
0x28 : { " name " : " Slowpoke Well B1F " } ,
0x29 : { " name " : " Slowpoke Well B2F " } ,
0x2A : { " name " : " Olivine Lighthouse 1F " } ,
0x2B : { " name " : " Olivine Lighthouse 2F " } ,
0x2C : { " name " : " Olivine Lighthouse 3F " } ,
0x2D : { " name " : " Olivine Lighthouse 4F " } ,
0x2E : { " name " : " Olivine Lighthouse 5F " } ,
0x2F : { " name " : " Olivine Lighthouse 6F " } ,
0x30 : { " name " : " Mahogany Mart 1F " } ,
0x31 : { " name " : " Team Rocket Base B1F " } ,
0x32 : { " name " : " Team Rocket Base B2F " } ,
0x33 : { " name " : " Team Rocket Base B3F " } ,
0x34 : { " name " : " Ilex Forest " } ,
0x35 : { " name " : " Warehouse Entrance " } ,
0x36 : { " name " : " Underground Path Switch Room Entrances " } ,
0x37 : { " name " : " Goldenrod Dept Store B1F " } ,
0x38 : { " name " : " Underground Warehouse " } ,
0x39 : { " name " : " Mount Mortar 1F Outside " } ,
0x3A : { " name " : " Mount Mortar 1F Inside " } ,
0x3B : { " name " : " Mount Mortar 2F Inside " } ,
0x3C : { " name " : " Mount Mortar B1F " } ,
0x3D : { " name " : " Ice Path 1F " } ,
0x3E : { " name " : " Ice Path B1F " } ,
0x3F : { " name " : " Ice Path B2F Mahogany Side " } ,
0x40 : { " name " : " Ice Path B2F Blackthorn Side " } ,
0x41 : { " name " : " Ice Path B3F " } ,
0x42 : { " name " : " Whirl Island NW " } ,
0x43 : { " name " : " Whirl Island NE " } ,
0x44 : { " name " : " Whirl Island SW " } ,
0x45 : { " name " : " Whirl Island Cave " } ,
0x46 : { " name " : " Whirl Island SE " } ,
0x47 : { " name " : " Whirl Island B1F " } ,
0x48 : { " name " : " Whirl Island B2F " } ,
0x49 : { " name " : " Whirl Island Lugia Chamber " } ,
0x4A : { " name " : " Silver Cave Room 1 " } ,
0x4B : { " name " : " Silver Cave Room 2 " } ,
0x4C : { " name " : " Silver Cave Room 3 " } ,
0x4D : { " name " : " Silver Cave Item Rooms " } ,
0x4E : { " name " : " Dark Cave Violet Entrance " } ,
0x4F : { " name " : " Dark Cave Blackthorn Entrance " } ,
0x50 : { " name " : " Dragon ' s Den 1F " } ,
0x51 : { " name " : " Dragon ' s Den B1F " } ,
0x52 : { " name " : " Dragon Shrine " } ,
0x53 : { " name " : " Tohjo Falls " } ,
0x54 : { " name " : " Diglett ' s Cave " } ,
0x55 : { " name " : " Mount Moon " } ,
0x56 : { " name " : " Underground " } ,
0x57 : { " name " : " Rock Tunnel 1F " } ,
0x58 : { " name " : " Rock Tunnel B1F " } ,
0x59 : { " name " : " Safari Zone Fuchsia Gate Beta " } ,
0x5A : { " name " : " Safari Zone Beta " } ,
0x5B : { " name " : " Victory Road " } ,
} ,
4 : {
0x1 : { " name " : " Ecruteak House " } , #passage to Tin Tower
0x2 : { " name " : " Wise Trio ' s Room " } ,
0x3 : { " name " : " Ecruteak Pokémon Center 1F " ,
" label " : " EcruteakPokeCenter1F " } ,
0x4 : { " name " : " Ecruteak Lugia Speech House " } ,
0x5 : { " name " : " Dance Theatre " } ,
0x6 : { " name " : " Ecruteak Mart " } ,
0x7 : { " name " : " Ecruteak Gym " } ,
0x8 : { " name " : " Ecruteak Itemfinder House " } ,
0x9 : { " name " : " Ecruteak City " } ,
} ,
5 : {
0x1 : { " name " : " Blackthorn Gym 1F " } ,
0x2 : { " name " : " Blackthorn Gym 2F " } ,
0x3 : { " name " : " Blackthorn Dragon Speech House " } ,
0x4 : { " name " : " Blackthorn Dodrio Trade House " } ,
0x5 : { " name " : " Blackthorn Mart " } ,
0x6 : { " name " : " Blackthorn Pokémon Center 1F " ,
" label " : " BlackthornPokeCenter1F " } ,
0x7 : { " name " : " Move Deleter ' s House " } ,
0x8 : { " name " : " Route 45 " } ,
0x9 : { " name " : " Route 46 " } ,
0xA : { " name " : " Blackthorn City " } ,
} ,
6 : {
0x1 : { " name " : " Cinnabar Pokémon Center 1F " ,
" label " : " CinnabarPokeCenter1F " } ,
0x2 : { " name " : " Cinnabar Pokémon Center 2F Beta " ,
" label " : " CinnabarPokeCenter2FBeta " } ,
0x3 : { " name " : " Route 19 - Fuchsia Gate " } ,
0x4 : { " name " : " Seafoam Gym " } ,
0x5 : { " name " : " Route 19 " } ,
0x6 : { " name " : " Route 20 " } ,
0x7 : { " name " : " Route 21 " } ,
0x8 : { " name " : " Cinnabar Island " } ,
} ,
7 : {
0x1 : { " name " : " Cerulean Gym Badge Speech House " } ,
0x2 : { " name " : " Cerulean Police Station " } ,
0x3 : { " name " : " Cerulean Trade Speech House " } ,
0x4 : { " name " : " Cerulean Pokémon Center 1F " ,
" label " : " CeruleanPokeCenter1F " } ,
0x5 : { " name " : " Cerulean Pokémon Center 2F Beta " ,
" label " : " CeruleanPokeCenter2FBeta " } ,
0x6 : { " name " : " Cerulean Gym " } ,
0x7 : { " name " : " Cerulean Mart " } ,
0x8 : { " name " : " Route 10 Pokémon Center 1F " ,
" label " : " Route10PokeCenter1F " } ,
0x9 : { " name " : " Route 10 Pokémon Center 2F Beta " ,
" label " : " Route10PokeCenter2FBeta " } ,
0xA : { " name " : " Power Plant " } ,
0xB : { " name " : " Bill ' s House " } ,
0xC : { " name " : " Route 4 " } ,
0xD : { " name " : " Route 9 " } ,
2012-05-16 12:24:18 -07:00
0xE : { " name " : " Route 10 North " } ,
2012-03-05 22:15:35 -08:00
0xF : { " name " : " Route 24 " } ,
0x10 : { " name " : " Route 25 " } ,
0x11 : { " name " : " Cerulean City " } ,
} ,
8 : {
0x1 : { " name " : " Azalea Pokémon Center 1F " ,
" label " : " AzaleaPokeCenter1F " } ,
0x2 : { " name " : " Charcoal Kiln " } ,
0x3 : { " name " : " Azalea Mart " } ,
0x4 : { " name " : " Kurt ' s House " } ,
0x5 : { " name " : " Azalea Gym " } ,
0x6 : { " name " : " Route 33 " } ,
0x7 : { " name " : " Azalea Town " } ,
} ,
9 : {
0x1 : { " name " : " Lake of Rage Hidden Power House " } ,
0x2 : { " name " : " Lake of Rage Magikarp House " } ,
0x3 : { " name " : " Route 43 Mahogany Gate " } ,
0x4 : { " name " : " Route 43 Gate " } ,
0x5 : { " name " : " Route 43 " } ,
0x6 : { " name " : " Lake of Rage " } ,
} ,
10 : {
0x1 : { " name " : " Route 32 " } ,
0x2 : { " name " : " Route 35 " } ,
0x3 : { " name " : " Route 36 " } ,
0x4 : { " name " : " Route 37 " } ,
0x5 : { " name " : " Violet City " } ,
0x6 : { " name " : " Violet Mart " } ,
0x7 : { " name " : " Violet Gym " } ,
0x8 : { " name " : " Earl ' s Pokémon Academy " ,
" label " : " EarlsPokemonAcademy " } ,
0x9 : { " name " : " Violet Nickname Speech House " } ,
0xA : { " name " : " Violet Pokémon Center 1F " ,
" label " : " VioletPokeCenter1F " } ,
0xB : { " name " : " Violet Onix Trade House " } ,
0xC : { " name " : " Route 32 Ruins of Alph Gate " } ,
0xD : { " name " : " Route 32 Pokémon Center 1F " ,
" label " : " Route32PokeCenter1F " } ,
0xE : { " name " : " Route 35 Goldenrod gate " } ,
0xF : { " name " : " Route 35 National Park gate " } ,
0x10 : { " name " : " Route 36 Ruins of Alph gate " } ,
0x11 : { " name " : " Route 36 National Park gate " } ,
} ,
11 : {
0x1 : { " name " : " Route 34 " } ,
0x2 : { " name " : " Goldenrod City " } ,
0x3 : { " name " : " Goldenrod Gym " } ,
0x4 : { " name " : " Goldenrod Bike Shop " } ,
0x5 : { " name " : " Goldenrod Happiness Rater " } ,
0x6 : { " name " : " Goldenrod Bill ' s House " } ,
0x7 : { " name " : " Goldenrod Magnet Train Station " } ,
0x8 : { " name " : " Goldenrod Flower Shop " } ,
0x9 : { " name " : " Goldenrod PP Speech House " } ,
0xA : { " name " : " Goldenrod Name Rater ' s House " } ,
0xB : { " name " : " Goldenrod Dept Store 1F " } ,
0xC : { " name " : " Goldenrod Dept Store 2F " } ,
0xD : { " name " : " Goldenrod Dept Store 3F " } ,
0xE : { " name " : " Goldenrod Dept Store 4F " } ,
0xF : { " name " : " Goldenrod Dept Store 5F " } ,
0x10 : { " name " : " Goldenrod Dept Store 6F " } ,
0x11 : { " name " : " Goldenrod Dept Store Elevator " } ,
0x12 : { " name " : " Goldenrod Dept Store Roof " } ,
0x13 : { " name " : " Goldenrod Game Corner " } ,
0x14 : { " name " : " Goldenrod Pokémon Center 1F " ,
" label " : " GoldenrodPokeCenter1F " } ,
0x15 : { " name " : " Goldenrod PokéCom Center 2F Mobile " ,
" label " : " GoldenrodPokeComCenter2FMobile " } ,
0x16 : { " name " : " Ilex Forest Azalea Gate " } ,
0x17 : { " name " : " Route 34 Ilex Forest Gate " } ,
0x18 : { " name " : " Day Care " } ,
} ,
12 : {
0x1 : { " name " : " Route 6 " } ,
0x2 : { " name " : " Route 11 " } ,
0x3 : { " name " : " Vermilion City " } ,
0x4 : { " name " : " Vermilion House Fishing Speech House " } ,
0x5 : { " name " : " Vermilion Pokémon Center 1F " ,
" label " : " VermilionPokeCenter1F " } ,
0x6 : { " name " : " Vermilion Pokémon Center 2F Beta " ,
" label " : " VermilionPokeCenter2FBeta " } ,
0x7 : { " name " : " Pokémon Fan Club " } ,
0x8 : { " name " : " Vermilion Magnet Train Speech House " } ,
0x9 : { " name " : " Vermilion Mart " } ,
0xA : { " name " : " Vermilion House Diglett ' s Cave Speech House " } ,
0xB : { " name " : " Vermilion Gym " } ,
0xC : { " name " : " Route 6 Saffron Gate " } ,
0xD : { " name " : " Route 6 Underground Entrance " } ,
} ,
13 : {
0x1 : { " name " : " Route 1 " } ,
0x2 : { " name " : " Pallet Town " } ,
0x3 : { " name " : " Red ' s House 1F " } ,
0x4 : { " name " : " Red ' s House 2F " } ,
0x5 : { " name " : " Blue ' s House " } ,
0x6 : { " name " : " Oak ' s Lab " } ,
} ,
14 : {
0x1 : { " name " : " Route 3 " } ,
0x2 : { " name " : " Pewter City " } ,
0x3 : { " name " : " Pewter Nidoran Speech House " } ,
0x4 : { " name " : " Pewter Gym " } ,
0x5 : { " name " : " Pewter Mart " } ,
0x6 : { " name " : " Pewter Pokémon Center 1F " ,
" label " : " PewterPokeCenter1F " } ,
0x7 : { " name " : " Pewter Pokémon Center 2F Beta " ,
" label " : " PewterPokeCEnter2FBeta " } ,
0x8 : { " name " : " Pewter Snooze Speech House " } ,
} ,
15 : {
0x1 : { " name " : " Olivine Port " } ,
0x2 : { " name " : " Vermilion Port " } ,
0x3 : { " name " : " Fast Ship 1F " } ,
0x4 : { " name " : " Fast Ship Cabins NNW, NNE, NE " ,
" label " : " FastShipCabins_NNW_NNE_NE " } ,
0x5 : { " name " : " Fast Ship Cabins SW, SSW, NW " ,
" label " : " FastShipCabins_SW_SSW_NW " } ,
0x6 : { " name " : " Fast Ship Cabins SE, SSE, Captain ' s Cabin " ,
" label " : " FastShipCabins_SE_SSE_CaptainsCabin " } ,
0x7 : { " name " : " Fast Ship B1F " } ,
0x8 : { " name " : " Olivine Port Passage " } ,
0x9 : { " name " : " Vermilion Port Passage " } ,
0xA : { " name " : " Mount Moon Square " } ,
0xB : { " name " : " Mount Moon Gift Shop " } ,
0xC : { " name " : " Tin Tower Roof " } ,
} ,
16 : {
0x1 : { " name " : " Route 23 " } ,
0x2 : { " name " : " Indigo Plateau Pokémon Center 1F " ,
" label " : " IndigoPlateauPokeCenter1F " } ,
0x3 : { " name " : " Will ' s Room " } ,
0x4 : { " name " : " Koga ' s Room " } ,
0x5 : { " name " : " Bruno ' s Room " } ,
0x6 : { " name " : " Karen ' s Room " } ,
0x7 : { " name " : " Lance ' s Room " } ,
0x8 : { " name " : " Hall of Fame " ,
" label " : " HallOfFame " } ,
} ,
17 : {
0x1 : { " name " : " Route 13 " } ,
0x2 : { " name " : " Route 14 " } ,
0x3 : { " name " : " Route 15 " } ,
0x4 : { " name " : " Route 18 " } ,
0x5 : { " name " : " Fuchsia City " } ,
0x6 : { " name " : " Fuchsia Mart " } ,
0x7 : { " name " : " Safari Zone Main Office " } ,
0x8 : { " name " : " Fuchsia Gym " } ,
0x9 : { " name " : " Fuchsia Bill Speech House " } ,
0xA : { " name " : " Fuchsia Pokémon Center 1F " ,
" label " : " FuchsiaPokeCenter1F " } ,
0xB : { " name " : " Fuchsia Pokémon Center 2F Beta " ,
" label " : " FuchsiaPokeCenter2FBeta " } ,
0xC : { " name " : " Safari Zone Warden ' s Home " } ,
0xD : { " name " : " Route 15 Fuchsia Gate " } ,
} ,
18 : {
0x1 : { " name " : " Route 8 " } ,
0x2 : { " name " : " Route 12 " } ,
2012-05-16 12:24:18 -07:00
0x3 : { " name " : " Route 10 South " } ,
2012-03-05 22:15:35 -08:00
0x4 : { " name " : " Lavender Town " } ,
0x5 : { " name " : " Lavender Pokémon Center 1F " ,
" label " : " LavenderPokeCenter1F " } ,
0x6 : { " name " : " Lavender Pokémon Center 2F Beta " ,
" label " : " LavenderPokeCenter2FBeta " } ,
0x7 : { " name " : " Mr. Fuji ' s House " } ,
0x8 : { " name " : " Lavender Town Speech House " } ,
0x9 : { " name " : " Lavender Name Rater " } ,
0xA : { " name " : " Lavender Mart " } ,
0xB : { " name " : " Soul House " } ,
0xC : { " name " : " Lav Radio Tower 1F " } ,
0xD : { " name " : " Route 8 Saffron Gate " } ,
0xE : { " name " : " Route 12 Super Rod House " } ,
} ,
19 : {
0x1 : { " name " : " Route 28 " } ,
0x2 : { " name " : " Silver Cave Outside " } ,
0x3 : { " name " : " Silver Cave Pokémon Center 1F " ,
" label " : " SilverCavePokeCenter1F " } ,
0x4 : { " name " : " Route 28 Famous Speech House " } ,
} ,
20 : {
0x1 : { " name " : " Pokémon Center 2F " ,
" label " : " PokeCenter2F " } ,
0x2 : { " name " : " Trade Center " } ,
0x3 : { " name " : " Colosseum " } ,
0x4 : { " name " : " Time Capsule " } ,
0x5 : { " name " : " Mobile Trade Room Mobile " } ,
0x6 : { " name " : " Mobile Battle Room " } ,
} ,
21 : {
0x1 : { " name " : " Route 7 " } ,
0x2 : { " name " : " Route 16 " } ,
0x3 : { " name " : " Route 17 " } ,
0x4 : { " name " : " Celadon City " } ,
0x5 : { " name " : " Celadon Dept Store 1F " } ,
0x6 : { " name " : " Celadon Dept Store 2F " } ,
0x7 : { " name " : " Celadon Dept Store 3F " } ,
0x8 : { " name " : " Celadon Dept Store 4F " } ,
0x9 : { " name " : " Celadon Dept Store 5F " } ,
0xA : { " name " : " Celadon Dept Store 6F " } ,
0xB : { " name " : " Celadon Dept Store Elevator " } ,
0xC : { " name " : " Celadon Mansion 1F " } ,
0xD : { " name " : " Celadon Mansion 2F " } ,
0xE : { " name " : " Celadon Mansion 3F " } ,
0xF : { " name " : " Celadon Mansion Roof " } ,
0x10 : { " name " : " Celadon Mansion Roof House " } ,
0x11 : { " name " : " Celadon Pokémon Center 1F " ,
" label " : " CeladonPokeCenter1F " } ,
0x12 : { " name " : " Celadon Pokémon Center 2F Beta " ,
" label " : " CeladonPokeCenter2FBeta " } ,
0x13 : { " name " : " Celadon Game Corner " } ,
0x14 : { " name " : " Celadon Game Corner Prize Room " } ,
0x15 : { " name " : " Celadon Gym " } ,
0x16 : { " name " : " Celadon Cafe " } ,
0x17 : { " name " : " Route 16 Fuchsia Speech House " } ,
0x18 : { " name " : " Route 16 Gate " } ,
0x19 : { " name " : " Route 7 Saffron Gate " } ,
0x1A : { " name " : " Route 17 18 Gate " } ,
} ,
22 : {
0x1 : { " name " : " Route 40 " } ,
0x2 : { " name " : " Route 41 " } ,
0x3 : { " name " : " Cianwood City " } ,
0x4 : { " name " : " Mania ' s House " } ,
0x5 : { " name " : " Cianwood Gym " } ,
0x6 : { " name " : " Cianwood Pokémon Center 1F " ,
" label " : " CianwoodPokeCenter1F " } ,
0x7 : { " name " : " Cianwood Pharmacy " } ,
0x8 : { " name " : " Cianwood City Photo Studio " } ,
0x9 : { " name " : " Cianwood Lugia Speech House " } ,
0xA : { " name " : " Poke Seer ' s House " } ,
0xB : { " name " : " Battle Tower 1F " } ,
0xC : { " name " : " Battle Tower Battle Room " } ,
0xD : { " name " : " Battle Tower Elevator " } ,
0xE : { " name " : " Battle Tower Hallway " } ,
0xF : { " name " : " Route 40 Battle Tower Gate " } ,
0x10 : { " name " : " Battle Tower Outside " } ,
} ,
23 : {
0x1 : { " name " : " Route 2 " } ,
0x2 : { " name " : " Route 22 " } ,
0x3 : { " name " : " Viridian City " } ,
0x4 : { " name " : " Viridian Gym " } ,
0x5 : { " name " : " Viridian Nickname Speech House " } ,
0x6 : { " name " : " Trainer House 1F " } ,
0x7 : { " name " : " Trainer House B1F " } ,
0x8 : { " name " : " Viridian Mart " } ,
0x9 : { " name " : " Viridian Pokémon Center 1F " ,
" label " : " ViridianPokeCenter1F " } ,
0xA : { " name " : " Viridian Pokémon Center 2F Beta " ,
" label " : " ViridianPokeCenter2FBeta " } ,
0xB : { " name " : " Route 2 Nugget Speech House " } ,
0xC : { " name " : " Route 2 Gate " } ,
0xD : { " name " : " Victory Road Gate " } ,
} ,
24 : {
0x1 : { " name " : " Route 26 " } ,
0x2 : { " name " : " Route 27 " } ,
0x3 : { " name " : " Route 29 " } ,
0x4 : { " name " : " New Bark Town " } ,
0x5 : { " name " : " Elm ' s Lab " } ,
0x6 : { " name " : " Kris ' s House 1F " } ,
0x7 : { " name " : " Kris ' s House 2F " } ,
0x8 : { " name " : " Kris ' s Neighbor ' s House " } ,
0x9 : { " name " : " Elm ' s House " } ,
0xA : { " name " : " Route 26 Heal Speech House " } ,
0xB : { " name " : " Route 26 Day of Week Siblings House " } ,
0xC : { " name " : " Route 27 Sandstorm House " } ,
0xD : { " name " : " Route 29 46 Gate " } ,
} ,
25 : {
0x1 : { " name " : " Route 5 " } ,
0x2 : { " name " : " Saffron City " } ,
0x3 : { " name " : " Fighting Dojo " } ,
0x4 : { " name " : " Saffron Gym " } ,
0x5 : { " name " : " Saffron Mart " } ,
0x6 : { " name " : " Saffron Pokémon Center 1F " ,
" label " : " SaffronPokeCenter1F " } ,
0x7 : { " name " : " Saffron Pokémon Center 2F Beta " ,
" label " : " SaffronPokeCenter2FBeta " } ,
0x8 : { " name " : " Mr. Psychic ' s House " } ,
0x9 : { " name " : " Saffron Train Station " } ,
0xA : { " name " : " Silph Co. 1F " } ,
0xB : { " name " : " Copycat ' s House 1F " } ,
0xC : { " name " : " Copycat ' s House 2F " } ,
0xD : { " name " : " Route 5 Underground Entrance " } ,
0xE : { " name " : " Route 5 Saffron City Gate " } ,
0xF : { " name " : " Route 5 Cleanse Tag Speech House " } ,
} ,
26 : {
0x1 : { " name " : " Route 30 " } ,
0x2 : { " name " : " Route 31 " } ,
0x3 : { " name " : " Cherrygrove City " } ,
0x4 : { " name " : " Cherrygrove Mart " } ,
0x5 : { " name " : " Cherrygrove Pokémon Center 1F " ,
" label " : " CherrygrovePokeCenter1F " } ,
0x6 : { " name " : " Cherrygrove Gym Speech House " } ,
0x7 : { " name " : " Guide Gent ' s House " } ,
0x8 : { " name " : " Cherrygrove Evolution Speech House " } ,
0x9 : { " name " : " Route 30 Berry Speech House " } ,
0xA : { " name " : " Mr. Pokémon ' s House " } ,
0xB : { " name " : " Route 31 Violet Gate " } ,
} ,
}
2012-04-17 18:22:28 -07:00
2012-03-05 22:15:35 -08:00
#generate labels for each map name
for map_group_id in map_names . keys ( ) :
map_group = map_names [ map_group_id ]
for map_id in map_group . keys ( ) :
#skip if we maybe already have the 'offset' label set in this map group
if map_id == " offset " : continue
#skip if we provided a pre-set value for the map's label
if map_group [ map_id ] . has_key ( " label " ) : continue
#convience alias
map_data = map_group [ map_id ]
#clean up the map name to be an asm label
cleaned_name = map_name_cleaner ( map_data [ " name " ] )
#set the value in the original dictionary
map_names [ map_group_id ] [ map_id ] [ " label " ] = cleaned_name
2012-03-25 23:47:58 -07:00
#generate map constants (like 1=PALLET_TOWN)
generate_map_constant_labels ( )
2012-03-05 22:15:35 -08:00
2012-03-24 16:01:37 -07:00
#### asm utilities ####
#these are pulled in from pokered/extras/analyze_incbins.py
#store each line of source code here
asm = None
#store each incbin line separately
incbin_lines = [ ]
#storage for processed incbin lines
processed_incbins = { }
2012-04-16 07:55:28 -07:00
def to_asm ( some_object ) :
2012-04-20 15:57:59 -07:00
""" shows an object ' s asm with a label and an ending comment
showing the next byte address """
if isinstance ( some_object , int ) :
some_object = script_parse_table [ some_object ]
#add one to the last_address to show where the next byte is in the file
2012-04-25 16:51:54 -07:00
last_address = some_object . last_address
2012-04-20 15:57:59 -07:00
#create a line like "label: ; 0x10101"
2012-04-26 16:41:13 -07:00
asm = some_object . label . name + " : ; " + hex ( some_object . address ) + " \n "
2012-04-20 15:57:59 -07:00
#now add the inner/actual asm
2012-04-21 16:09:03 -07:00
#asm += spacing + some_object.to_asm().replace("\n", "\n"+spacing).replace("\n"+spacing+"\n"+spacing, "\n\n"+spacing)
asmr = some_object . to_asm ( )
asmr = asmr . replace ( " \n " , " \n " + spacing )
asmr = asmr . replace ( " \n " + spacing + " \n " , " \n \n " + spacing )
2012-04-21 21:46:58 -07:00
asmr = asmr . replace ( " \n \n " + spacing + spacing , " \n \n " + spacing )
2012-04-21 16:09:03 -07:00
asm + = spacing + asmr
2012-04-20 15:57:59 -07:00
#show the address of the next byte below this
2012-04-22 13:08:58 -07:00
asm + = " \n ; " + hex ( last_address )
2012-04-16 07:55:28 -07:00
return asm
2012-04-21 00:43:19 -07:00
def flattener ( x ) :
" flattens a list of sublists into just one list (generator) "
try :
it = iter ( x )
except TypeError :
yield x
else :
for i in it :
for j in flattener ( i ) :
yield j
def flatten ( x ) :
" flattens a list of sublists into just one list "
return list ( flattener ( x ) )
2012-04-27 12:58:57 -07:00
def get_dependencies_for ( some_object , recompute = False , global_dependencies = set ( ) ) :
2012-04-20 16:26:52 -07:00
"""
calculates which labels need to be satisfied for an object
to be inserted into the asm and compile successfully .
You could also choose to not insert labels into the asm , but
then you ' re losing out on the main value of having asm in the
first place .
"""
2012-04-27 11:58:02 -07:00
try :
if isinstance ( some_object , int ) :
some_object = script_parse_table [ some_object ]
if some_object . dependencies != None and not recompute :
2012-04-27 12:58:57 -07:00
global_dependencies . update ( some_object . dependencies )
else :
some_object . get_dependencies ( recompute = recompute , global_dependencies = global_dependencies )
return global_dependencies
2012-04-27 11:58:02 -07:00
except RuntimeError , e :
#1552, 1291, 2075, 1552, 1291...
print " some_object is: " + str ( some_object )
print " class type: " + str ( some_object . __class__ )
print " label name: " + str ( some_object . label . name )
print " address: " + str ( some_object . address )
print " asm is: \n \n " + to_asm ( some_object )
raise e
2012-04-20 16:26:52 -07:00
2012-03-24 16:01:37 -07:00
def isolate_incbins ( ) :
" find each incbin line "
2012-03-24 20:59:46 -07:00
global incbin_lines , asm
2012-03-24 16:01:37 -07:00
incbin_lines = [ ]
for line in asm :
if line == " " : continue
if line . count ( " " ) == len ( line ) : continue
#clean up whitespace at beginning of line
while line [ 0 ] == " " :
line = line [ 1 : ]
if line [ 0 : 6 ] == " INCBIN " and " baserom.gbc " in line :
incbin_lines . append ( line )
return incbin_lines
def process_incbins ( ) :
" parse incbin lines into memory "
2012-03-24 19:34:19 -07:00
global asm , incbin_lines , processed_incbins
#load asm if it isn't ready yet
if asm == [ ] or asm == None :
load_asm ( )
#get a list of incbins if that hasn't happened yet
if incbin_lines == [ ] or incbin_lines == None :
isolate_incbins ( )
#reset the global that this function creates
processed_incbins = { }
#for each incbin..
2012-03-24 16:01:37 -07:00
for incbin in incbin_lines :
2012-03-24 19:34:19 -07:00
#reset this entry
2012-03-24 16:01:37 -07:00
processed_incbin = { }
2012-03-24 19:34:19 -07:00
#get the line number from the global asm line list
2012-03-24 16:01:37 -07:00
line_number = asm . index ( incbin )
2012-03-24 19:34:19 -07:00
#forget about all the leading characters
2012-03-24 16:01:37 -07:00
partial_start = incbin [ 21 : ]
start = partial_start . split ( " , " ) [ 0 ] . replace ( " $ " , " 0x " )
start = eval ( start )
start_hex = hex ( start ) . replace ( " 0x " , " $ " )
partial_interval = incbin [ 21 : ] . split ( " , " ) [ 1 ]
partial_interval = partial_interval . replace ( " ; " , " # " )
partial_interval = partial_interval . replace ( " $ " , " 0x " ) . replace ( " 0xx " , " 0x " )
interval = eval ( partial_interval )
interval_hex = hex ( interval ) . replace ( " 0x " , " $ " ) . replace ( " x " , " " )
end = start + interval
end_hex = hex ( end ) . replace ( " 0x " , " $ " )
2012-03-24 19:34:19 -07:00
processed_incbin = { " line_number " : line_number ,
2012-03-24 16:01:37 -07:00
" line " : incbin ,
" start " : start ,
" interval " : interval ,
2012-03-24 19:34:19 -07:00
" end " : end , }
2012-03-24 16:01:37 -07:00
#don't add this incbin if the interval is 0
if interval != 0 :
processed_incbins [ line_number ] = processed_incbin
2012-03-24 19:34:19 -07:00
return processed_incbins
2012-03-24 16:01:37 -07:00
def reset_incbins ( ) :
" reset asm before inserting another diff "
2012-03-24 19:34:19 -07:00
global asm , incbin_lines , processed_incbins
2012-03-24 16:01:37 -07:00
asm = None
incbin_lines = [ ]
processed_incbins = { }
load_asm ( )
isolate_incbins ( )
process_incbins ( )
def find_incbin_to_replace_for ( address , debug = False , rom_file = " ../baserom.gbc " ) :
""" returns a line number for which incbin to edit
if you were to insert bytes into main . asm """
if type ( address ) == str : address = int ( address , 16 )
if not ( 0 < = address < = os . lstat ( rom_file ) . st_size ) :
raise IndexError , " address is out of bounds "
for incbin_key in processed_incbins . keys ( ) :
incbin = processed_incbins [ incbin_key ]
start = incbin [ " start " ]
end = incbin [ " end " ]
if debug :
print " start is: " + str ( start )
print " end is: " + str ( end )
print " address is: " + str ( type ( address ) )
print " checking.... " + hex ( start ) + " <= " + hex ( address ) + " <= " + hex ( end )
if start < = address < = end :
return incbin_key
return None
2012-03-24 20:59:46 -07:00
def split_incbin_line_into_three ( line , start_address , byte_count , rom_file = " ../baserom.gbc " ) :
2012-03-24 16:01:37 -07:00
"""
splits an incbin line into three pieces .
you can replace the middle one with the new content of length bytecount
2012-04-17 18:22:28 -07:00
2012-03-24 16:01:37 -07:00
start_address : where you want to start inserting bytes
byte_count : how many bytes you will be inserting
"""
if type ( start_address ) == str : start_address = int ( start_address , 16 )
if not ( 0 < = start_address < = os . lstat ( rom_file ) . st_size ) :
raise IndexError , " start_address is out of bounds "
if len ( processed_incbins ) == 0 :
raise Exception , " processed_incbins must be populated "
original_incbin = processed_incbins [ line ]
start = original_incbin [ " start " ]
end = original_incbin [ " end " ]
#start, end1, end2 (to be printed as start, end1 - end2)
if start_address - start > 0 :
first = ( start , start_address , start )
else :
first = ( None ) #skip this one because we're not including anything
#this is the one you will replace with whatever content
second = ( start_address , byte_count )
third = ( start_address + byte_count , end - ( start_address + byte_count ) )
output = " "
if first :
output + = " INCBIN \" baserom.gbc \" ,$ " + hex ( first [ 0 ] ) [ 2 : ] + " ,$ " + hex ( first [ 1 ] ) [ 2 : ] + " - $ " + hex ( first [ 2 ] ) [ 2 : ] + " \n "
output + = " INCBIN \" baserom.gbc \" ,$ " + hex ( second [ 0 ] ) [ 2 : ] + " , " + str ( byte_count ) + " \n "
output + = " INCBIN \" baserom.gbc \" ,$ " + hex ( third [ 0 ] ) [ 2 : ] + " ,$ " + hex ( third [ 1 ] ) [ 2 : ] #no newline
return output
2012-03-24 20:59:46 -07:00
def generate_diff_insert ( line_number , newline , debug = False ) :
""" generates a diff between the old main.asm and the new main.asm
note : requires python2 .7 i think ? b / c of subprocess . check_output """
global asm
2012-03-24 16:01:37 -07:00
original = " \n " . join ( line for line in asm )
newfile = deepcopy ( asm )
newfile [ line_number ] = newline #possibly inserting multiple lines
newfile = " \n " . join ( line for line in newfile )
2012-03-24 20:59:46 -07:00
#make sure there's a newline at the end of the file
if newfile [ - 1 ] != " \n " :
newfile + = " \n "
2012-03-24 16:01:37 -07:00
original_filename = " ejroqjfoad.temp "
newfile_filename = " fjiqefo.temp "
original_fh = open ( original_filename , " w " )
original_fh . write ( original )
original_fh . close ( )
newfile_fh = open ( newfile_filename , " w " )
newfile_fh . write ( newfile )
newfile_fh . close ( )
2012-04-06 11:12:30 -07:00
try :
from subprocess import CalledProcessError
except ImportError :
CalledProcessError = None
2012-03-24 16:01:37 -07:00
try :
diffcontent = subprocess . check_output ( " diff -u ../main.asm " + newfile_filename , shell = True )
2012-04-06 11:12:30 -07:00
except ( AttributeError , CalledProcessError ) :
2012-03-24 20:59:46 -07:00
p = subprocess . Popen ( [ " diff " , " -u " , " ../main.asm " , newfile_filename ] , stdout = subprocess . PIPE , stderr = subprocess . PIPE )
out , err = p . communicate ( )
diffcontent = out
2012-03-24 16:01:37 -07:00
os . system ( " rm " + original_filename )
os . system ( " rm " + newfile_filename )
2012-03-24 20:59:46 -07:00
if debug : print diffcontent
2012-03-24 16:01:37 -07:00
return diffcontent
def apply_diff ( diff , try_fixing = True , do_compile = True ) :
print " ... Applying diff. "
#write the diff to a file
fh = open ( " temp.patch " , " w " )
fh . write ( diff )
fh . close ( )
#apply the patch
os . system ( " cp ../main.asm ../main1.asm " )
os . system ( " patch ../main.asm temp.patch " )
#remove the patch
os . system ( " rm temp.patch " )
#confirm it's working
if do_compile :
try :
2012-03-24 19:34:19 -07:00
subprocess . check_call ( " cd ../; make clean; make " , shell = True )
2012-03-24 16:01:37 -07:00
return True
except Exception , exc :
if try_fixing :
os . system ( " mv ../main1.asm ../main.asm " )
return False
2012-04-21 09:33:23 -07:00
class AsmLine :
#TODO: parse label lines
def __init__ ( self , line , bank = None ) :
self . line = line
self . bank = bank
def to_asm ( self ) :
return self . line
class Incbin :
2012-04-21 16:09:03 -07:00
def __init__ ( self , line , bank = None , debug = False ) :
2012-04-21 09:33:23 -07:00
self . line = line
self . bank = bank
2012-04-21 14:06:22 -07:00
self . replace_me = False
2012-04-21 16:09:03 -07:00
self . debug = debug
2012-04-21 09:33:23 -07:00
self . parse ( )
def parse ( self ) :
incbin = self . line
partial_start = incbin [ 21 : ]
start = partial_start . split ( " , " ) [ 0 ] . replace ( " $ " , " 0x " )
2012-04-21 16:09:03 -07:00
if self . debug :
print " Incbin.parse -- line is: " + self . line
print " Incbin.parse -- partial_start is: " + partial_start
print " Incbin.parse -- start is: " + start
try :
start = eval ( start )
except Exception , e :
raise Exception , " problem with evaluating interval range "
2012-04-21 09:33:23 -07:00
start_hex = hex ( start ) . replace ( " 0x " , " $ " )
partial_interval = incbin [ 21 : ] . split ( " , " ) [ 1 ]
partial_interval = partial_interval . replace ( " ; " , " # " )
partial_interval = partial_interval . replace ( " $ " , " 0x " ) . replace ( " 0xx " , " 0x " )
interval = eval ( partial_interval )
interval_hex = hex ( interval ) . replace ( " 0x " , " $ " ) . replace ( " x " , " " )
end = start + interval
end_hex = hex ( end ) . replace ( " 0x " , " $ " )
self . address = start
self . start_address = start
self . end_address = end
self . last_address = end
self . interval = interval
def to_asm ( self ) :
if self . interval > 0 :
return self . line
else : return " "
2012-04-21 10:38:12 -07:00
def split ( self , start_address , byte_count ) :
""" splits this incbin into three separate incbins """
if start_address < self . start_address or start_address > self . end_address :
raise Exception , " this incbin doesn ' t handle this address "
incbins = [ ]
2012-04-21 16:09:03 -07:00
if self . debug :
print " splitting an incbin ( " + self . line + " ) into three at " + hex ( start_address ) + " for " + str ( byte_count ) + " bytes "
2012-04-21 10:38:12 -07:00
#start, end1, end2 (to be printed as start, end1 - end2)
2012-04-21 16:09:03 -07:00
if ( start_address - self . start_address ) > 0 :
first = ( self . start_address , start_address , self . start_address )
2012-04-21 10:38:12 -07:00
incbins . append ( Incbin ( " INCBIN \" baserom.gbc \" ,$ %.2x ,$ %.2x - $ %.2x " % ( first [ 0 ] , first [ 1 ] , first [ 2 ] ) ) )
2012-04-21 16:09:03 -07:00
if self . debug :
print " " + incbins [ 0 ] . line
2012-04-21 10:38:12 -07:00
else :
#skip this one because we're not including anything
first = None
#this is the one you will replace with whatever content
second = ( start_address , byte_count )
incbins . append ( Incbin ( " INCBIN \" baserom.gbc \" ,$ %.2x ,$ %.2x " % ( start_address , byte_count ) ) )
2012-04-21 14:06:22 -07:00
incbins [ - 1 ] . replace_me = True
2012-04-21 16:09:03 -07:00
if self . debug :
print " " + incbins [ - 1 ] . line
2012-04-21 10:38:12 -07:00
2012-04-21 16:09:03 -07:00
if ( self . last_address - ( start_address + byte_count ) ) > 0 :
third = ( start_address + byte_count , self . last_address - ( start_address + byte_count ) )
2012-04-21 14:06:22 -07:00
incbins . append ( Incbin ( " INCBIN \" baserom.gbc \" ,$ %.2x ,$ %.2x " % ( third [ 0 ] , third [ 1 ] ) ) )
2012-04-21 16:09:03 -07:00
if self . debug :
print " " + incbins [ - 1 ] . line
2012-04-21 09:33:23 -07:00
2012-04-21 10:38:12 -07:00
return incbins
2012-05-20 01:39:25 -07:00
2012-04-21 14:11:00 -07:00
class AsmSection :
2012-04-21 09:33:23 -07:00
def __init__ ( self , line ) :
self . bank_id = None
self . line = line
self . parse ( )
def parse ( self ) :
line = self . line
2012-04-21 14:11:00 -07:00
bank_id = int ( line . split ( " \" " ) [ 1 ] . split ( " bank " ) [ 1 ] , 16 )
2012-04-21 09:33:23 -07:00
self . bank_id = bank_id
start_address = bank_id * 0x4000
end_address = ( bank_id * 0x4000 ) + 0x4000 - 1
2012-05-20 01:39:25 -07:00
2012-04-21 16:09:03 -07:00
self . address = self . start_address = start_address
self . last_address = None
self . end_address = None
2012-04-21 09:33:23 -07:00
#this entity doesn't actually take up this space..
#although it could be argued that lines should exist under this object
#self.address = self.start_address = start_address
#self.last_address = self.end_address = end_address
def to_asm ( self ) :
return self . line
2012-04-26 16:04:20 -07:00
new_asm = None
def load_asm2 ( filename = " ../main.asm " , force = False ) :
""" loads the asm source code into memory """
global new_asm
if new_asm == None or force :
new_asm = Asm ( filename = filename )
return new_asm
2012-04-21 09:33:23 -07:00
class Asm :
""" controls the overall asm output """
2012-04-21 16:09:03 -07:00
def __init__ ( self , filename = " ../main.asm " , debug = True ) :
2012-04-21 09:33:23 -07:00
self . parts = [ ]
2012-04-26 16:41:13 -07:00
self . labels = [ ]
2012-04-21 09:33:23 -07:00
self . filename = filename
2012-04-21 16:09:03 -07:00
self . debug = debug
2012-04-21 09:33:23 -07:00
self . load_and_parse ( )
def load_and_parse ( self ) :
self . parts = [ ]
asm = open ( self . filename , " r " ) . read ( ) . split ( " \n " )
asm_list = AsmList ( asm )
bank = 0
for line in asm_list :
if line [ 0 : 6 ] == " INCBIN " or line [ 1 : 6 ] == " INCBIN " :
thing = Incbin ( line , bank = bank )
2012-04-21 14:11:00 -07:00
elif line [ 0 : 7 ] == " SECTION " :
2012-04-21 09:33:23 -07:00
thing = AsmSection ( line )
bank = thing . bank_id
else :
thing = AsmLine ( line , bank = bank )
2012-04-26 16:04:20 -07:00
label = get_label_from_line ( line )
if label :
2012-04-26 16:41:13 -07:00
laddress = get_address_from_line_comment ( line )
thing . label = Label ( name = label , address = laddress , object = thing , add_to_globals = False )
self . labels . append ( thing . label )
2012-04-21 09:33:23 -07:00
self . parts . append ( thing )
2012-04-26 16:04:20 -07:00
def is_label_name_in_file ( self , label_name ) :
2012-04-26 16:41:13 -07:00
for llabel in self . labels :
if llabel . name == label_name :
return llabel
return False
2012-04-26 16:04:20 -07:00
def does_address_have_label ( self , address ) :
""" Checks if an address has a label.
"""
# either something will directly have the address
# or- it's possibel that no label was given
# or there will be an Incbin that covers the range
for part in self . parts :
2012-04-26 16:41:13 -07:00
if isinstance ( part , Incbin ) and part . start_address < = address < = part . end_address :
2012-04-26 16:04:20 -07:00
return False
2012-04-26 16:41:13 -07:00
elif hasattr ( part , " address " ) and part . address == address and hasattr ( part , " label " ) :
2012-04-26 16:04:20 -07:00
return part . label
return None
2012-04-21 14:06:22 -07:00
def insert ( self , new_object ) :
2012-04-27 23:34:20 -07:00
if isinstance ( new_object , ScriptPointerLabelParam ) :
# its' probably being injected in some get_dependencies() somewhere
print " don ' t know why ScriptPointerLabelParam is getting to this point? "
return
2012-05-20 01:39:25 -07:00
2012-04-26 16:41:13 -07:00
#first some validation
2012-04-21 16:09:03 -07:00
if not hasattr ( new_object , " address " ) :
print " object needs to have an address property: " + str ( new_object )
return
2012-05-20 01:39:25 -07:00
2012-05-16 13:22:39 -07:00
start_address = new_object . address
# skip this dragon shrine script calling itself
# what about other scripts that call themselves ?
if start_address in lousy_dragon_shrine_hack :
print " skipping 0x18d079 in dragon shrine for a lousy hack "
return
2012-05-20 01:39:25 -07:00
2012-05-13 20:48:35 -07:00
if not hasattr ( new_object , " label " ) and hasattr ( new_object , " is_valid " ) and not new_object . is_valid ( ) :
return
2012-04-27 23:34:20 -07:00
debugmsg = " object is " + new_object . label . name + " type= " + str ( new_object . __class__ ) + " new_object= " + str ( new_object )
debugmsg + = " label = " + new_object . label . name
debugmsg + = " start_address= " + hex ( start_address ) #+" end_address="+hex(end_address)
2012-05-20 01:39:25 -07:00
2012-04-21 16:09:03 -07:00
if not hasattr ( new_object , " last_address " ) :
2012-04-27 23:34:20 -07:00
print debugmsg
raise Exception , " object needs to have a last_address property "
end_address = new_object . last_address
debugmsg + = " last_address= " + hex ( end_address )
2012-04-26 16:41:13 -07:00
2012-04-21 14:06:22 -07:00
#check if the object is already inserted
if new_object in self . parts :
2012-04-21 16:09:03 -07:00
print " object was previously inserted ( " + str ( new_object ) + " ) "
return
2012-04-26 16:41:13 -07:00
#check by label
if self . is_label_name_in_file ( new_object . label . name ) :
print " object was previously inserted ( " + str ( new_object ) + " ) by label: " + new_object . label . name
return
#check by address
2012-04-26 23:06:31 -07:00
#if self.does_address_have_label(new_object.address):
# print "object's address is already used ("+str(new_object)+") at "+hex(new_object.address)+" label="+new_object.label.name
# return
2012-05-20 01:39:25 -07:00
2012-04-21 16:09:03 -07:00
if self . debug :
2012-04-27 11:58:02 -07:00
print debugmsg
2012-04-27 23:34:20 -07:00
del debugmsg
2012-04-21 16:09:03 -07:00
if ( end_address < start_address ) or ( ( end_address - start_address ) < 0 ) :
if not self . debug :
2012-04-27 23:34:20 -07:00
print " object is new_object= " + str ( new_object )
2012-04-21 16:09:03 -07:00
print " start_address= " + hex ( start_address ) + " end_address= " + hex ( end_address )
if hasattr ( new_object , " to_asm " ) :
print to_asm ( new_object )
raise Exception , " Asm.insert was given an object with a bad address range "
2012-05-20 01:39:25 -07:00
2012-04-21 14:06:22 -07:00
# 1) find which object needs to be replaced
# or
# 2) find which object goes after it
found = False
for object in list ( self . parts ) :
2012-04-21 14:11:00 -07:00
#skip objects without a defined interval (like a comment line)
2012-04-21 16:09:03 -07:00
if not hasattr ( object , " address " ) or not hasattr ( object , " last_address " ) :
continue
#skip an AsmSection
if isinstance ( object , AsmSection ) :
continue
2012-04-21 14:06:22 -07:00
#replace an incbin with three incbins, replace middle incbin with whatever
2012-04-25 12:05:51 -07:00
elif isinstance ( object , Incbin ) and ( object . address < = start_address < object . last_address ) :
2012-04-21 14:06:22 -07:00
#split up the incbin into three segments
incbins = object . split ( start_address , end_address - start_address )
#figure out which incbin to replace with the new object
if incbins [ 0 ] . replace_me :
index = 0
else : #assume incbins[1].replace_me (the middle one)
index = 1
#replace that index with the new_object
2012-04-21 21:37:02 -07:00
incbins [ index ] = new_object
2012-04-21 14:06:22 -07:00
#insert these incbins into self.parts
gindex = self . parts . index ( object )
2012-04-22 18:33:42 -07:00
self . parts = self . parts [ : gindex ] + incbins + self . parts [ gindex : ]
self . parts . remove ( object )
2012-04-21 14:06:22 -07:00
found = True
break
2012-05-16 13:22:39 -07:00
elif object . address < = start_address < object . last_address :
print " this is probably a script that is looping back on itself? "
found = True
break
2012-04-21 14:06:22 -07:00
#insert before the current object
elif object . address > end_address :
#insert_before = index of object
index = self . parts . index ( object )
self . parts . insert ( index , new_object )
found = True
break
if not found :
raise Exception , " unable to insert object into Asm "
2012-04-26 16:41:13 -07:00
self . labels . append ( new_object . label )
2012-05-20 01:39:25 -07:00
return True
2012-05-08 16:34:27 -07:00
def insert_with_dependencies ( self , input ) :
if type ( input ) == list :
input_objects = input
else :
input_objects = [ input ]
2012-05-20 01:39:25 -07:00
2012-05-08 16:34:27 -07:00
for object0 in input_objects :
global_dependencies = set ( [ object0 ] )
poopbutt = get_dependencies_for ( object0 , global_dependencies = global_dependencies , recompute = False )
objects = global_dependencies
objects . update ( poopbutt )
new_objects = copy ( objects )
for object in objects :
if hasattr ( object , " dependencies " ) and object . dependencies == None :
new_objects . update ( object . get_dependencies ( ) )
for object in new_objects :
if isinstance ( object , ScriptPointerLabelParam ) :
continue
#if object in self.parts:
# if self.debug:
# print "already inserted -- object.__class__="+str(object.__class__)+" object is: "+str(object)+\
# " for object.__class__="+str(object0.__class__)+" object="+str(object0)
# continue
if self . debug :
print " object is: " + str ( object )
self . insert ( object )
2012-05-20 01:39:25 -07:00
2012-05-08 16:34:27 -07:00
#just some old debugging
#if object.label.name == "UnknownText_0x60128":
# raise Exception, "debugging..."
#elif object.label.name == "UnknownScript_0x60011":
# raise Exception, "debugging.. dependencies are: " + str(object.dependencies) + " versus: " + str(object.get_dependencies())
def insert_single_with_dependencies ( self , object ) :
self . insert_with_dependencies ( object )
2012-04-26 22:59:05 -07:00
def insert_multiple_with_dependencies ( self , objects ) :
2012-05-08 16:34:27 -07:00
self . insert_with_dependencies ( objects )
2012-04-21 16:59:58 -07:00
def insert_all ( self , limit = 100 ) :
count = 0
2012-04-21 16:09:03 -07:00
for each in script_parse_table . items ( ) :
2012-04-21 16:59:58 -07:00
if count == limit : break
2012-04-21 16:09:03 -07:00
object = each [ 1 ]
if type ( object ) == str : continue
self . insert_single_with_dependencies ( object )
2012-04-21 16:59:58 -07:00
count + = 1
def insert_and_dump ( self , limit = 100 , filename = " output.txt " ) :
self . insert_all ( limit = limit )
2012-04-22 18:33:42 -07:00
self . dump ( filename = filename )
def dump ( self , filename = " output.txt " ) :
2012-04-21 16:59:58 -07:00
fh = open ( filename , " w " )
2012-04-22 13:08:58 -07:00
newlines_before_next_obj_requested = 0
newlines_before_next_obj_given = 0
2012-04-25 14:11:38 -07:00
current_requested_newlines_before = 0
current_requested_newlines_after = 0
previous_requested_newlines_before = 0
previous_requested_newlines_after = 0
written_newlines = 0
write_something = False
first = True
last = None
2012-04-21 16:59:58 -07:00
for each in self . parts :
2012-04-22 13:08:58 -07:00
asm = " "
2012-04-25 14:11:38 -07:00
previous_requested_newlines_after = current_requested_newlines_after
current_requested_newlines_before = current_requested_newlines_after
write_something = True
if ( isinstance ( each , str ) and each == " " ) or ( isinstance ( each , AsmLine ) and each . line == " " ) :
current_requested_newlines_before = 0
if current_requested_newlines_after < 2 :
current_requested_newlines_after + = 1
write_something = False
elif ( isinstance ( each , str ) and each != " " ) or ( isinstance ( each , AsmLine ) and each . line != " " ) :
if isinstance ( each , AsmLine ) :
2012-04-22 13:08:58 -07:00
asm = each . to_asm ( )
2012-04-25 14:11:38 -07:00
elif isinstance ( each , str ) :
asm = each
current_requested_newlines_before = 0
current_requested_newlines_after = 1
elif isinstance ( each , AsmSection ) or isinstance ( each , Incbin ) or hasattr ( each , " to_asm " ) :
2012-04-25 16:18:14 -07:00
if isinstance ( each , AsmSection ) or isinstance ( each , Incbin ) :
asm = each . to_asm ( )
else :
asm = to_asm ( each )
2012-04-25 14:11:38 -07:00
current_requested_newlines_before = 2
current_requested_newlines_after = 2
2012-04-22 13:08:58 -07:00
else :
2012-04-25 14:11:38 -07:00
raise Exception , " dunno what to do with( " + str ( each ) + " ) in Asm.parts "
if write_something :
if not first :
newlines_before = max ( [ current_requested_newlines_before , previous_requested_newlines_after ] )
while written_newlines < newlines_before :
fh . write ( " \n " )
written_newlines + = 1
else :
first = False
2012-04-22 13:08:58 -07:00
fh . write ( asm )
2012-04-25 14:11:38 -07:00
written_newlines = 0
last = each
2012-04-21 09:33:23 -07:00
2012-04-25 18:23:10 -07:00
#make sure the file ends with a newline
fh . write ( " \n " )
2012-05-16 12:37:30 -07:00
def list_things_in_bank ( bank ) :
objects = [ ]
for blah in script_parse_table . items ( ) :
object = blah [ 1 ]
if hasattr ( object , " address " ) and calculate_bank ( object . address ) == bank :
objects . append ( object )
return objects
2012-05-05 15:04:49 -07:00
def list_texts_in_bank ( bank ) :
""" Narrows down the list of objects
that you will be inserting into Asm .
"""
if len ( all_texts ) == 0 :
raise Exception , " all_texts is blank.. run_main() will populate it "
assert bank != None , " list_texts_in_banks must be given a particular bank "
assert 0 < = bank < 0x80 , " bank doesn ' t exist in the ROM "
texts = [ ]
for text in all_texts :
if calculate_bank ( text . address ) == bank :
texts . append ( text )
return texts
2012-05-11 14:30:12 -07:00
def list_movements_in_bank ( bank ) :
""" Narrows down the list of objects
to speed up Asm insertion .
"""
if len ( all_movements ) == 0 :
raise Exception , " all_movements is blank.. run_main() will populate it "
assert bank != None , " list_movements_in_bank must be given a particular bank "
assert 0 < = bank < 0x80 , " bank doesn ' t exist in the ROM (out of bounds) "
2012-05-20 01:39:25 -07:00
2012-05-11 14:30:12 -07:00
movements = [ ]
for movement in all_movements :
if calculate_bank ( movement . address ) == bank :
movements . append ( movement )
return movements
2012-05-05 15:04:49 -07:00
def dump_asm_for_texts_in_bank ( bank , start = 50 , end = 100 ) :
""" Simple utility to help with dumping texts into a particular bank. This
is helpful for figuring out which text is breaking that bank .
"""
# load and parse the ROM if necessary
if rom == None or len ( rom ) < = 4 :
load_rom ( )
run_main ( )
2012-05-20 01:39:25 -07:00
2012-05-05 15:04:49 -07:00
# get all texts
# first 100 look okay?
texts = list_texts_in_bank ( bank ) [ start : end ]
# create a new dump
asm = Asm ( )
# start the insertion process
asm . insert_multiple_with_dependencies ( texts )
2012-05-20 01:39:25 -07:00
2012-05-05 15:04:49 -07:00
# start dumping
asm . dump ( )
print " done dumping texts for bank $ %.2x " % ( bank )
2012-05-11 14:30:12 -07:00
def dump_asm_for_movements_in_bank ( bank , start = 0 , end = 100 ) :
if rom == None or len ( rom ) < = 4 :
load_rom ( )
run_main ( )
movements = list_movements_in_bank ( bank ) [ start : end ]
asm = Asm ( )
asm . insert_with_dependencies ( movements )
asm . dump ( )
print " done dumping movements for bank $ %.2x " % ( bank )
2012-05-16 12:37:30 -07:00
def dump_things_in_bank ( bank , start = 50 , end = 100 ) :
""" is helpful for figuring out which object is breaking that bank.
"""
# load and parse the ROM if necessary
if rom == None or len ( rom ) < = 4 :
load_rom ( )
run_main ( )
2012-05-20 01:39:25 -07:00
2012-05-16 12:37:30 -07:00
things = list_things_in_bank ( bank ) [ start : end ]
# create a new dump
asm = Asm ( )
# start the insertion process
2012-05-16 16:33:24 -07:00
asm . insert_with_dependencies ( things )
2012-05-20 01:39:25 -07:00
2012-05-16 12:37:30 -07:00
# start dumping
asm . dump ( )
print " done dumping things for bank $ %.2x " % ( bank )
2012-03-24 16:01:37 -07:00
def index ( seq , f ) :
""" return the index of the first item in seq
where f ( item ) == True . """
return next ( ( i for i in xrange ( len ( seq ) ) if f ( seq [ i ] ) ) , None )
def analyze_intervals ( ) :
""" find the largest baserom.gbc intervals """
global asm , processed_incbins
if asm == None :
load_asm ( )
if processed_incbins == { } :
isolate_incbins ( )
process_incbins ( )
results = [ ]
ordered_keys = sorted ( processed_incbins , key = lambda entry : processed_incbins [ entry ] [ " interval " ] )
ordered_keys . reverse ( )
for key in ordered_keys :
results . append ( processed_incbins [ key ] )
return results
2012-04-08 09:48:23 -07:00
all_labels = [ ]
2012-03-24 19:34:19 -07:00
def write_all_labels ( all_labels , filename = " labels.json " ) :
fh = open ( filename , " w " )
2012-03-24 16:01:37 -07:00
fh . write ( json . dumps ( all_labels ) )
fh . close ( )
2012-03-24 19:34:19 -07:00
return True
2012-03-24 16:01:37 -07:00
2012-04-02 10:44:23 -07:00
#TODO: implement get_ram_label
2012-04-26 15:41:37 -07:00
#wram.asm integration would be nice
2012-04-02 10:44:23 -07:00
def get_ram_label ( address ) :
""" not implemented yet.. supposed to get a label for a particular RAM location
like W_PARTYPOKE1HP """
return None
2012-03-29 23:20:50 -07:00
def get_label_for ( address ) :
""" returns a label assigned to a particular address """
global all_labels
2012-04-27 23:34:20 -07:00
if address == None :
2012-04-08 14:09:00 -07:00
return None
2012-04-27 23:34:20 -07:00
if type ( address ) != int :
raise Exception , " get_label_for requires an integer address, got: " + str ( type ( address ) )
2012-04-08 11:40:55 -07:00
2012-05-16 13:22:39 -07:00
# lousy hack to get around recursive scripts in dragon shrine
if address in lousy_dragon_shrine_hack :
return None
2012-04-08 11:40:55 -07:00
#the old way
2012-03-29 23:20:50 -07:00
for thing in all_labels :
if thing [ " address " ] == address :
return thing [ " label " ]
2012-04-17 18:22:28 -07:00
2012-04-08 11:40:55 -07:00
#the new way
2012-04-27 13:33:37 -07:00
obj = script_parse_table [ address ]
if obj :
2012-04-08 11:40:55 -07:00
if hasattr ( obj , " label " ) :
2012-04-27 13:33:37 -07:00
return obj . label . name
2012-04-08 11:40:55 -07:00
else :
return " AlreadyParsedNoDefaultUnknownLabel_ " + hex ( address )
2012-04-18 21:20:28 -07:00
#return "NotYetParsed_"+hex(address)
2012-04-27 15:16:45 -07:00
if address > 0x7FFF :
value = 0x4000 + ( address % 0x4000 )
return " $ %.2x " % ( value )
else :
return " $ %.2x " % ( address )
2012-03-29 23:20:50 -07:00
2012-03-24 16:01:37 -07:00
def remove_quoted_text ( line ) :
""" get rid of content inside quotes
and also removes the quotes from the input string """
while line . count ( " \" " ) % 2 == 0 and line . count ( " \" " ) > 0 :
first = line . find ( " \" " )
second = line . find ( " \" " , first + 1 )
line = line [ 0 : first ] + line [ second + 1 : ]
while line . count ( " \' " ) % 2 == 0 and line . count ( " ' " ) > 0 :
first = line . find ( " \' " )
second = line . find ( " \' " , first + 1 )
line = line [ 0 : first ] + line [ second + 1 : ]
return line
2012-04-26 15:41:37 -07:00
# all_new_labels is a temporary replacement for all_labels,
# at least until the two approaches are merged in the code base.
all_new_labels = [ ]
2012-04-02 10:36:08 -07:00
class Label :
2012-04-26 15:41:37 -07:00
""" Every object in script_parse_table is given a label.
2012-05-20 01:39:25 -07:00
2012-04-26 15:41:37 -07:00
This label is simply a way to keep track of what objects have
been previously written to file .
"""
2012-04-26 16:41:13 -07:00
def __init__ ( self , name = None , address = None , line_number = None , object = None , is_in_file = None , address_is_in_file = None , add_to_globals = True ) :
2012-05-16 22:40:40 -07:00
assert address != None , " need an address "
2012-04-26 15:41:37 -07:00
assert is_valid_address ( address ) , " address must be valid "
2012-05-16 22:40:40 -07:00
assert object != None , " need an object to relate with "
2012-04-26 15:41:37 -07:00
self . address = address
self . object = object
2012-05-20 01:39:25 -07:00
2012-04-26 15:41:37 -07:00
# label might not be in the file yet
self . line_number = line_number
2012-05-20 01:39:25 -07:00
2012-04-26 16:04:20 -07:00
# -- These were some old attempts to check whether the label
# -- was already in use. They work, but the other method is
# -- better.
#
2012-04-26 15:41:37 -07:00
# check if the label is in the file already
2012-04-26 16:04:20 -07:00
# check if the address of this label is already in use
2012-04-26 15:41:37 -07:00
self . is_in_file = is_in_file
self . address_is_in_file = address_is_in_file
2012-05-20 01:39:25 -07:00
2012-05-16 22:40:40 -07:00
if name == None :
name = object . base_label + " _ " + hex ( object . address )
self . name = name
2012-04-26 15:41:37 -07:00
2012-04-26 16:41:13 -07:00
if add_to_globals :
all_new_labels . append ( self )
2012-04-26 15:41:37 -07:00
2012-04-26 16:04:20 -07:00
def check_is_in_file ( self ) :
""" This method checks if the label appears in the file
based on the entries to the Asm . parts list .
"""
#assert new_asm != None, "new_asm should be an instance of Asm"
load_asm2 ( )
is_in_file = new_asm . is_label_name_in_file ( self . name )
self . is_in_file = is_in_file
return is_in_file
def check_address_is_in_file ( self ) :
""" Checks if the address is in use by another label.
"""
load_asm2 ( )
self . address_is_in_file = new_asm . does_address_have_label ( self . address )
return self . address_is_in_file
def get_line_number_from_raw_file ( self ) :
2012-04-26 15:41:37 -07:00
""" Reads the asm file to figure out the line number.
2012-05-20 01:39:25 -07:00
2012-04-26 15:41:37 -07:00
Note that this label might not be in the file yet , like
if this is a newly generated label . However , if crystal . py
has been run before and manipulated main . asm , then it is
possible that this label is already in the file .
"""
2012-04-26 16:04:20 -07:00
lineno = old_is_label_in_asm ( self . name )
2012-04-26 15:41:37 -07:00
if lineno :
self . line_number = lineno
self . is_in_file = True
return lineno
else :
self . line_number = None
self . is_in_file = False
return None
2012-04-26 16:04:20 -07:00
def old_check_is_in_file ( self ) :
2012-04-26 15:41:37 -07:00
""" Reads the asm file to figure out if this label
is already inserted or not .
"""
2012-04-26 16:04:20 -07:00
self . get_line_number_from_raw_file ( )
2012-04-26 15:41:37 -07:00
return self . is_in_file
2012-05-20 01:39:25 -07:00
2012-04-26 16:04:20 -07:00
def old_check_address_is_in_file ( self ) :
2012-04-26 15:41:37 -07:00
""" Checks whether or not the address of the object is
already in the file . This might happen if the label name
is different but the address is the same . Another scenario
is that the label is already used , but at a different
address .
2012-05-20 01:39:25 -07:00
2012-04-26 15:41:37 -07:00
This method works by looking at the INCBINs . When there is
an INCBIN that covers this address in the file , then there
is no label at this address yet ( or there is , but we can
easily add another label in front of the incbin or something ) ,
and when there is no INCBIN that has this address , then we
know that something is already using this address .
"""
if processed_incbins == { } :
process_incbins ( )
2012-05-20 01:39:25 -07:00
2012-04-26 15:41:37 -07:00
incbin = find_incbin_to_replace_for ( self . address )
if incbin == None :
return True
else :
return False
2012-04-02 10:36:08 -07:00
2012-05-16 22:40:40 -07:00
def make_label ( self ) :
""" Generates a label name based on parents and self.object.
"""
object = self . object
name = object . make_label ( )
return name
2012-03-24 19:34:19 -07:00
def line_has_comment_address ( line , returnable = { } , bank = None ) :
2012-03-24 16:01:37 -07:00
""" checks that a given line has a comment
2012-03-24 19:34:19 -07:00
with a valid address , and returns the address in the object .
Note : bank is required if you have a 4 - letter - or - less address ,
because otherwise there is no way to figure out which bank
is curretly being scanned . """
2012-03-24 16:01:37 -07:00
#first set the bank/offset to nada
returnable [ " bank " ] = None
returnable [ " offset " ] = None
returnable [ " address " ] = None
#only valid characters are 0-9A-F
valid = [ str ( x ) for x in range ( 0 , 10 ) ] + [ chr ( x ) for x in range ( 97 , 102 + 1 ) ]
#check if there is a comment in this line
if " ; " not in line :
return False
#first throw away anything in quotes
if ( line . count ( " \" " ) % 2 == 0 and line . count ( " \" " ) != 0 ) \
or ( line . count ( " \' " ) % 2 == 0 and line . count ( " \' " ) != 0 ) :
line = remove_quoted_text ( line )
#check if there is still a comment in this line after quotes removed
if " ; " not in line :
return False
#but even if there's a semicolon there must be later text
if line [ - 1 ] == " ; " :
return False
#and just a space doesn't count
if line [ - 2 : ] == " ; " :
return False
#and multiple whitespace doesn't count either
2012-03-24 19:34:19 -07:00
line = line . rstrip ( " " ) . lstrip ( " " )
2012-03-24 16:01:37 -07:00
if line [ - 1 ] == " ; " :
return False
#there must be more content after the semicolon
if len ( line ) - 1 == line . find ( " ; " ) :
return False
#split it up into the main comment part
comment = line [ line . find ( " ; " ) + 1 : ]
#don't want no leading whitespace
comment = comment . lstrip ( " " ) . rstrip ( " " )
#split up multi-token comments into single tokens
token = comment
if " " in comment :
#use the first token in the comment
token = comment . split ( " " ) [ 0 ]
if token in [ " 0x " , " $ " , " x " , " : " ] :
return False
2012-03-24 19:34:19 -07:00
offset = None
2012-03-24 16:01:37 -07:00
#process a token with a A:B format
if " : " in token : #3:3F0A, $3:$3F0A, 0x3:0x3F0A, 3:3F0A
#split up the token
bank_piece = token . split ( " : " ) [ 0 ] . lower ( )
offset_piece = token . split ( " : " ) [ 1 ] . lower ( )
#filter out blanks/duds
if bank_piece in [ " $ " , " 0x " , " x " ] \
or offset_piece in [ " $ " , " 0x " , " x " ] :
return False
#they can't have both "$" and "x"
if " $ " in bank_piece and " x " in bank_piece :
return False
if " $ " in offset_piece and " x " in offset_piece :
return False
#process the bank piece
if " $ " in bank_piece :
bank_piece = bank_piece . replace ( " $ " , " 0x " )
#check characters for validity?
for c in bank_piece . replace ( " x " , " " ) :
if c not in valid :
return False
bank = int ( bank_piece , 16 )
#process the offset piece
if " $ " in offset_piece :
offset_piece = offset_piece . replace ( " $ " , " 0x " )
#check characters for validity?
for c in offset_piece . replace ( " x " , " " ) :
if c not in valid :
return False
offset = int ( offset_piece , 16 )
#filter out blanks/duds
elif token in [ " $ " , " 0x " , " x " ] :
return False
#can't have both "$" and "x" in the number
elif " $ " in token and " x " in token :
return False
elif " x " in token and not " 0x " in token : #it should be 0x
return False
elif " $ " in token and not " x " in token :
token = token . replace ( " $ " , " 0x " )
offset = int ( token , 16 )
elif " 0x " in token and not " $ " in token :
offset = int ( token , 16 )
else : #might just be "1" at this point
token = token . lower ( )
#check if there are bad characters
for c in token :
if c not in valid :
return False
offset = int ( token , 16 )
if offset == None and bank == None :
return False
2012-03-24 19:34:19 -07:00
if bank == None :
bank = calculate_bank ( offset )
2012-03-24 16:01:37 -07:00
returnable [ " bank " ] = bank
returnable [ " offset " ] = offset
returnable [ " address " ] = calculate_pointer ( offset , bank = bank )
return True
2012-04-17 18:22:28 -07:00
2012-03-24 16:01:37 -07:00
def line_has_label ( line ) :
""" returns True if the line has an asm label """
if not isinstance ( line , str ) :
raise Exception , " can ' t check this type of object "
line = line . rstrip ( " " ) . lstrip ( " " )
line = remove_quoted_text ( line )
if " ; " in line :
line = line . split ( " ; " ) [ 0 ]
if 0 < = len ( line ) < = 1 :
return False
if " : " not in line :
return False
if line [ 0 ] == " ; " :
return False
if line [ 0 ] == " \" " :
return False
if " :: " in line :
return False
return True
2012-04-17 18:22:28 -07:00
2012-04-26 16:41:13 -07:00
def get_address_from_line_comment ( line , bank = None ) :
""" wrapper for line_has_comment_address
"""
returnable = { }
result = line_has_comment_address ( line , returnable = returnable , bank = bank )
if not result :
return False
return returnable [ " address " ]
2012-03-24 16:01:37 -07:00
def get_label_from_line ( line ) :
""" returns the label from the line """
#check if the line has a label
if not line_has_label ( line ) :
return None
#split up the line
label = line . split ( " : " ) [ 0 ]
return label
2012-04-17 18:22:28 -07:00
2012-04-26 16:04:20 -07:00
def old_is_label_in_asm ( label ) :
""" Returns the line number or returns None if the
label is not in the file . This is an " old " method
because it looks directly at the list of lines
rather than reading a globally shared instance of
the Asm class .
2012-04-26 15:41:37 -07:00
"""
# line numbering begins at 1 in vim
i = 1
2012-05-20 01:39:25 -07:00
2012-04-26 15:41:37 -07:00
# check if any line starts with this label
for line in asm :
if line_has_label ( line ) :
thislabel = get_label_from_line ( line )
if thislabel == label :
return i
i + = 1
return False
2012-03-24 16:01:37 -07:00
def find_labels_without_addresses ( ) :
""" scans the asm source and finds labels that are unmarked """
without_addresses = [ ]
for ( line_number , line ) in enumerate ( asm ) :
if line_has_label ( line ) :
label = get_label_from_line ( line )
if not line_has_comment_address ( line ) :
without_addresses . append ( { " line_number " : line_number , " line " : line , " label " : label } )
return without_addresses
label_errors = " "
2012-03-24 19:34:19 -07:00
def get_labels_between ( start_line_id , end_line_id , bank ) :
2012-03-24 16:01:37 -07:00
labels = [ ]
#label = {
# "line_number": 15,
# "bank": 32,
# "label": "PalletTownText1",
# "offset": 0x5315,
# "address": 0x75315,
#}
2012-03-24 19:34:19 -07:00
if asm == None :
load_asm ( )
2012-03-24 16:01:37 -07:00
sublines = asm [ start_line_id : end_line_id + 1 ]
for ( current_line_offset , line ) in enumerate ( sublines ) :
#skip lines without labels
if not line_has_label ( line ) : continue
#reset some variables
line_id = start_line_id + current_line_offset
line_label = get_label_from_line ( line )
address = None
offset = None
#setup a place to store return values from line_has_comment_address
returnable = { }
#get the address from the comment
2012-03-24 19:34:19 -07:00
has_comment = line_has_comment_address ( line , returnable = returnable , bank = bank )
2012-03-24 16:01:37 -07:00
#skip this line if it has no address in the comment
if not has_comment : continue
#parse data from line_has_comment_address
address = returnable [ " address " ]
bank = returnable [ " bank " ]
offset = returnable [ " offset " ]
#dump all this info into a single structure
label = {
" line_number " : line_id ,
" bank " : bank ,
" label " : line_label ,
" offset " : offset ,
" address " : address ,
}
#store this structure
labels . append ( label )
return labels
2012-03-24 19:34:19 -07:00
def scan_for_predefined_labels ( debug = False ) :
2012-03-24 16:01:37 -07:00
""" looks through the asm file for labels at specific addresses,
this relies on the label having its address after . ex :
ViridianCity_h : ; 0x18357 to 0x18384 ( 45 bytes ) ( bank = 6 ) ( id = 1 )
PalletTownText1 : ; 4 F96 0x18f96
ViridianCityText1 : ; 0x19102
It would be more productive to use rgbasm to spit out all label
addresses , but faster to write this script . rgbasm would be able
to grab all label addresses better than this script . .
"""
2012-03-24 19:34:19 -07:00
global all_labels
2012-03-24 16:01:37 -07:00
all_labels = [ ]
2012-03-24 19:34:19 -07:00
bank_intervals = { }
2012-03-24 16:01:37 -07:00
#figure out line numbers for each bank
for bank_id in range ( 0x7F + 1 ) :
abbreviation = ( " % .x " % ( bank_id ) ) . upper ( )
abbreviation_next = ( " % .x " % ( bank_id + 1 ) ) . upper ( )
if bank_id == 0 :
abbreviation = " 0 "
abbreviation_next = " 1 "
2012-03-24 19:34:19 -07:00
#calculate the start/stop line numbers for this bank
2012-03-24 16:01:37 -07:00
start_line_id = index ( asm , lambda line : " \" bank " + abbreviation + " \" " in line )
2012-03-24 19:34:19 -07:00
if bank_id != 0x7F :
2012-03-24 16:01:37 -07:00
end_line_id = index ( asm , lambda line : " \" bank " + abbreviation_next + " \" " in line )
2012-03-24 19:34:19 -07:00
end_line_id + = 1
2012-03-24 16:01:37 -07:00
else :
end_line_id = len ( asm ) - 1
2012-03-24 19:34:19 -07:00
if debug :
output = " bank " + abbreviation + " starts at "
output + = str ( start_line_id )
output + = " to "
output + = str ( end_line_id )
print output
2012-04-17 18:22:28 -07:00
2012-03-24 19:34:19 -07:00
#store the start/stop line number for this bank
bank_intervals [ bank_id ] = { " start " : start_line_id ,
" end " : end_line_id , }
#for each bank..
2012-03-24 16:01:37 -07:00
for bank_id in bank_intervals . keys ( ) :
2012-03-24 19:34:19 -07:00
#get the start/stop line number
2012-03-24 16:01:37 -07:00
bank_data = bank_intervals [ bank_id ]
start_line_id = bank_data [ " start " ]
end_line_id = bank_data [ " end " ]
2012-03-24 19:34:19 -07:00
#get all labels between these two lines
2012-03-24 16:01:37 -07:00
labels = get_labels_between ( start_line_id , end_line_id , bank_id )
#bank_intervals[bank_id]["labels"] = labels
all_labels . extend ( labels )
write_all_labels ( all_labels )
return all_labels
2012-03-24 01:54:28 -07:00
#### generic testing ####
class TestCram ( unittest . TestCase ) :
" this is where i cram all of my unit tests together "
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
@classmethod
def setUpClass ( cls ) :
global rom
2012-04-02 10:36:08 -07:00
cls . rom = direct_load_rom ( )
2012-03-24 01:54:28 -07:00
rom = cls . rom
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
@classmethod
def tearDownClass ( cls ) :
del cls . rom
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_generic_useless ( self ) :
" do i know how to write a test? "
self . assertEqual ( 1 , 1 )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_map_name_cleaner ( self ) :
name = " hello world "
cleaned_name = map_name_cleaner ( name )
self . assertNotEqual ( name , cleaned_name )
self . failUnless ( " " not in cleaned_name )
name = " Some Random Pokémon Center "
cleaned_name = map_name_cleaner ( name )
self . assertNotEqual ( name , cleaned_name )
self . failIf ( " " in cleaned_name )
self . failIf ( " é " in cleaned_name )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_grouper ( self ) :
data = range ( 0 , 10 )
groups = grouper ( data , count = 2 )
self . assertEquals ( len ( groups ) , 5 )
data = range ( 0 , 20 )
groups = grouper ( data , count = 2 )
self . assertEquals ( len ( groups ) , 10 )
self . assertNotEqual ( data , groups )
self . assertNotEqual ( len ( data ) , len ( groups ) )
2012-04-17 18:22:28 -07:00
2012-04-02 10:36:08 -07:00
def test_direct_load_rom ( self ) :
2012-03-24 01:54:28 -07:00
rom = self . rom
self . assertEqual ( len ( rom ) , 2097152 )
self . failUnless ( isinstance ( rom , RomStr ) )
2012-04-17 18:22:28 -07:00
2012-04-02 10:36:08 -07:00
def test_load_rom ( self ) :
global rom
rom = None
load_rom ( )
self . failIf ( rom == None )
rom = RomStr ( None )
load_rom ( )
self . failIf ( rom == RomStr ( None ) )
2012-04-17 18:22:28 -07:00
2012-03-24 11:22:28 -07:00
def test_load_asm ( self ) :
asm = load_asm ( )
joined_lines = " \n " . join ( asm )
self . failUnless ( " SECTION " in joined_lines )
self . failUnless ( " bank " in joined_lines )
self . failUnless ( isinstance ( asm , AsmList ) )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_rom_file_existence ( self ) :
" ROM file must exist "
self . failUnless ( " baserom.gbc " in os . listdir ( " ../ " ) )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_rom_md5 ( self ) :
" ROM file must have the correct md5 sum "
rom = self . rom
correct = " 9f2922b235a5eeb78d65594e82ef5dde "
md5sum = md5 . md5 ( rom ) . hexdigest ( )
self . assertEqual ( md5sum , correct )
2012-04-17 18:22:28 -07:00
2012-03-24 02:43:06 -07:00
def test_bizarre_http_presence ( self ) :
rom_segment = self . rom [ 0x112116 : 0x112116 + 8 ]
self . assertEqual ( rom_segment , " HTTP/1.0 " )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_rom_interval ( self ) :
address = 0x100
interval = 10
correct_strings = [ ' 0x0 ' , ' 0xc3 ' , ' 0x6e ' , ' 0x1 ' , ' 0xce ' ,
' 0xed ' , ' 0x66 ' , ' 0x66 ' , ' 0xcc ' , ' 0xd ' ]
byte_strings = rom_interval ( address , interval , strings = True )
self . assertEqual ( byte_strings , correct_strings )
correct_ints = [ 0 , 195 , 110 , 1 , 206 , 237 , 102 , 102 , 204 , 13 ]
ints = rom_interval ( address , interval , strings = False )
self . assertEqual ( ints , correct_ints )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_rom_until ( self ) :
address = 0x1337
byte = 0x13
bytes = rom_until ( address , byte , strings = True )
self . failUnless ( len ( bytes ) == 3 )
self . failUnless ( bytes [ 0 ] == ' 0xd5 ' )
bytes = rom_until ( address , byte , strings = False )
self . failUnless ( len ( bytes ) == 3 )
self . failUnless ( bytes [ 0 ] == 0xd5 )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_how_many_until ( self ) :
how_many = how_many_until ( chr ( 0x13 ) , 0x1337 )
self . assertEqual ( how_many , 3 )
2012-04-17 18:22:28 -07:00
2012-03-24 02:43:06 -07:00
def test_calculate_bank ( self ) :
self . failUnless ( calculate_bank ( 0x8000 ) == 2 )
self . failUnless ( calculate_bank ( " 0x9000 " ) == 2 )
self . failUnless ( calculate_bank ( 0 ) == 0 )
for address in [ 0x4000 , 0x5000 , 0x6000 , 0x7000 ] :
self . assertRaises ( Exception , calculate_bank , address )
2012-04-17 18:22:28 -07:00
2012-03-24 02:43:06 -07:00
def test_calculate_pointer ( self ) :
2012-03-24 13:05:32 -07:00
#for offset <= 0x4000
self . assertEqual ( calculate_pointer ( 0x0000 ) , 0x0000 )
self . assertEqual ( calculate_pointer ( 0x3FFF ) , 0x3FFF )
#for 0x4000 <= offset <= 0x7FFFF
self . assertEqual ( calculate_pointer ( 0x430F , bank = 5 ) , 0x1430F )
#for offset >= 0x7FFF
self . assertEqual ( calculate_pointer ( 0x8FFF , bank = 6 ) , calculate_pointer ( 0x8FFF , bank = 7 ) )
2012-04-17 18:22:28 -07:00
2012-03-24 23:09:46 -07:00
def test_calculate_pointer_from_bytes_at ( self ) :
addr1 = calculate_pointer_from_bytes_at ( 0x100 , bank = False )
self . assertEqual ( addr1 , 0xc300 )
addr2 = calculate_pointer_from_bytes_at ( 0x100 , bank = True )
self . assertEqual ( addr2 , 0x2ec3 )
2012-04-17 18:22:28 -07:00
2012-03-24 02:43:06 -07:00
def test_rom_text_at ( self ) :
self . assertEquals ( rom_text_at ( 0x112116 , 8 ) , " HTTP/1.0 " )
2012-04-17 18:22:28 -07:00
2012-03-24 02:43:06 -07:00
def test_translate_command_byte ( self ) :
self . failUnless ( translate_command_byte ( crystal = 0x0 ) == 0x0 )
self . failUnless ( translate_command_byte ( crystal = 0x10 ) == 0x10 )
self . failUnless ( translate_command_byte ( crystal = 0x40 ) == 0x40 )
self . failUnless ( translate_command_byte ( gold = 0x0 ) == 0x0 )
self . failUnless ( translate_command_byte ( gold = 0x10 ) == 0x10 )
self . failUnless ( translate_command_byte ( gold = 0x40 ) == 0x40 )
self . assertEqual ( translate_command_byte ( gold = 0x0 ) , translate_command_byte ( crystal = 0x0 ) )
self . failUnless ( translate_command_byte ( gold = 0x52 ) == 0x53 )
self . failUnless ( translate_command_byte ( gold = 0x53 ) == 0x54 )
self . failUnless ( translate_command_byte ( crystal = 0x53 ) == 0x52 )
self . failUnless ( translate_command_byte ( crystal = 0x52 ) == None )
self . assertRaises ( Exception , translate_command_byte , None , gold = 0xA4 )
2012-04-17 18:22:28 -07:00
2012-03-24 02:43:06 -07:00
def test_pksv_integrity ( self ) :
" does pksv_gs look okay? "
self . assertEqual ( pksv_gs [ 0x00 ] , " 2call " )
self . assertEqual ( pksv_gs [ 0x2D ] , " givepoke " )
self . assertEqual ( pksv_gs [ 0x85 ] , " waitbutton " )
self . assertEqual ( pksv_crystal [ 0x00 ] , " 2call " )
self . assertEqual ( pksv_crystal [ 0x86 ] , " waitbutton " )
self . assertEqual ( pksv_crystal [ 0xA2 ] , " credits " )
2012-04-17 18:22:28 -07:00
2012-03-24 02:43:06 -07:00
def test_chars_integrity ( self ) :
self . assertEqual ( chars [ 0x80 ] , " A " )
self . assertEqual ( chars [ 0xA0 ] , " a " )
self . assertEqual ( chars [ 0xF0 ] , " ¥ " )
self . assertEqual ( jap_chars [ 0x44 ] , " ぱ " )
2012-04-17 18:22:28 -07:00
2012-03-24 03:00:45 -07:00
def test_map_names_integrity ( self ) :
def map_name ( map_group , map_id ) : return map_names [ map_group ] [ map_id ] [ " name " ]
self . assertEqual ( map_name ( 2 , 7 ) , " Mahogany Town " )
self . assertEqual ( map_name ( 3 , 0x34 ) , " Ilex Forest " )
self . assertEqual ( map_name ( 7 , 0x11 ) , " Cerulean City " )
2012-04-17 18:22:28 -07:00
2012-03-24 13:05:32 -07:00
def test_load_map_group_offsets ( self ) :
addresses = load_map_group_offsets ( )
self . assertEqual ( len ( addresses ) , 26 , msg = " there should be 26 map groups " )
addresses = load_map_group_offsets ( )
self . assertEqual ( len ( addresses ) , 26 , msg = " there should still be 26 map groups " )
self . assertIn ( 0x94034 , addresses )
for address in addresses :
self . assertGreaterEqual ( address , 0x4000 )
self . failIf ( 0x4000 < = address < = 0x7FFF )
self . failIf ( address < = 0x4000 )
2012-04-17 18:22:28 -07:00
2012-03-24 20:59:46 -07:00
def test_index ( self ) :
self . assertTrue ( index ( [ 1 , 2 , 3 , 4 ] , lambda f : True ) == 0 )
self . assertTrue ( index ( [ 1 , 2 , 3 , 4 ] , lambda f : f == 3 ) == 2 )
2012-04-17 18:22:28 -07:00
2012-03-29 21:41:06 -07:00
def test_get_pokemon_constant_by_id ( self ) :
x = get_pokemon_constant_by_id
self . assertEqual ( x ( 1 ) , " BULBASAUR " )
self . assertEqual ( x ( 151 ) , " MEW " )
self . assertEqual ( x ( 250 ) , " HO_OH " )
2012-04-17 18:22:28 -07:00
2012-03-29 21:41:06 -07:00
def test_find_item_label_by_id ( self ) :
x = find_item_label_by_id
self . assertEqual ( x ( 249 ) , " HM_07 " )
self . assertEqual ( x ( 173 ) , " BERRY " )
2012-03-29 23:20:50 -07:00
self . assertEqual ( x ( 45 ) , None )
2012-04-17 18:22:28 -07:00
2012-03-29 21:41:06 -07:00
def test_generate_item_constants ( self ) :
x = generate_item_constants
r = x ( )
self . failUnless ( " HM_07 " in r )
self . failUnless ( " EQU " in r )
2012-04-17 18:22:28 -07:00
2012-03-29 23:20:50 -07:00
def test_get_label_for ( self ) :
global all_labels
temp = copy ( all_labels )
#this is basd on the format defined in get_labels_between
all_labels = [ { " label " : " poop " , " address " : 0x5 ,
" offset " : 0x5 , " bank " : 0 ,
" line_number " : 2
} ]
self . assertEqual ( get_label_for ( 5 ) , " poop " )
all_labels = temp
2012-04-17 18:22:28 -07:00
2012-04-02 10:36:08 -07:00
def test_generate_map_constant_labels ( self ) :
ids = generate_map_constant_labels ( )
self . assertEqual ( ids [ 0 ] [ " label " ] , " OLIVINE_POKECENTER_1F " )
self . assertEqual ( ids [ 1 ] [ " label " ] , " OLIVINE_GYM " )
2012-04-17 18:22:28 -07:00
2012-04-02 12:22:50 -07:00
def test_get_id_for_map_constant_label ( self ) :
global map_internal_ids
map_internal_ids = generate_map_constant_labels ( )
self . assertEqual ( get_id_for_map_constant_label ( " OLIVINE_GYM " ) , 1 )
self . assertEqual ( get_id_for_map_constant_label ( " OLIVINE_POKECENTER_1F " ) , 0 )
2012-04-17 18:22:28 -07:00
2012-04-02 12:22:50 -07:00
def test_get_map_constant_label_by_id ( self ) :
global map_internal_ids
map_internal_ids = generate_map_constant_labels ( )
self . assertEqual ( get_map_constant_label_by_id ( 0 ) , " OLIVINE_POKECENTER_1F " )
self . assertEqual ( get_map_constant_label_by_id ( 1 ) , " OLIVINE_GYM " )
2012-04-17 18:22:28 -07:00
2012-04-02 10:36:08 -07:00
def test_is_valid_address ( self ) :
self . assertTrue ( is_valid_address ( 0 ) )
self . assertTrue ( is_valid_address ( 1 ) )
self . assertTrue ( is_valid_address ( 10 ) )
self . assertTrue ( is_valid_address ( 100 ) )
self . assertTrue ( is_valid_address ( 1000 ) )
self . assertTrue ( is_valid_address ( 10000 ) )
self . assertFalse ( is_valid_address ( 2097153 ) )
self . assertFalse ( is_valid_address ( 2098000 ) )
addresses = [ random . randrange ( 0 , 2097153 ) for i in range ( 0 , 9 + 1 ) ]
for address in addresses :
self . assertTrue ( is_valid_address ( address ) )
2012-04-17 18:22:28 -07:00
2012-03-24 03:00:45 -07:00
class TestIntervalMap ( unittest . TestCase ) :
def test_intervals ( self ) :
i = IntervalMap ( )
first = " hello world "
second = " testing 123 "
i [ 0 : 5 ] = first
i [ 5 : 10 ] = second
self . assertEqual ( i [ 0 ] , first )
self . assertEqual ( i [ 1 ] , first )
self . assertNotEqual ( i [ 5 ] , first )
self . assertEqual ( i [ 6 ] , second )
i [ 3 : 10 ] = second
self . assertEqual ( i [ 3 ] , second )
self . assertNotEqual ( i [ 4 ] , first )
2012-04-17 18:22:28 -07:00
2012-03-24 03:00:45 -07:00
def test_items ( self ) :
i = IntervalMap ( )
first = " hello world "
second = " testing 123 "
i [ 0 : 5 ] = first
i [ 5 : 10 ] = second
results = list ( i . items ( ) )
self . failUnless ( len ( results ) == 2 )
self . assertEqual ( results [ 0 ] , ( ( 0 , 5 ) , " hello world " ) )
self . assertEqual ( results [ 1 ] , ( ( 5 , 10 ) , " testing 123 " ) )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
class TestRomStr ( unittest . TestCase ) :
""" RomStr is a class that should act exactly like str()
except that it never shows the contents of it string
unless explicitly forced """
sample_text = " hello world! "
sample = None
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def setUp ( self ) :
if self . sample == None :
self . __class__ . sample = RomStr ( self . sample_text )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_equals ( self ) :
" check if RomStr() == str() "
self . assertEquals ( self . sample_text , self . sample )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_not_equal ( self ) :
" check if RomStr( ' a ' ) != RomStr( ' b ' ) "
self . assertNotEqual ( RomStr ( ' a ' ) , RomStr ( ' b ' ) )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_appending ( self ) :
" check if RomStr()+ ' a ' ==str()+ ' a ' "
self . assertEquals ( self . sample_text + ' a ' , self . sample + ' a ' )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_conversion ( self ) :
" check if RomStr() -> str() works "
self . assertEquals ( str ( self . sample ) , self . sample_text )
2012-04-17 18:22:28 -07:00
2012-03-24 11:22:28 -07:00
def test_inheritance ( self ) :
self . failUnless ( issubclass ( RomStr , str ) )
2012-04-17 18:22:28 -07:00
2012-03-24 11:22:28 -07:00
def test_length ( self ) :
self . assertEquals ( len ( self . sample_text ) , len ( self . sample ) )
self . assertEquals ( len ( self . sample_text ) , self . sample . length ( ) )
self . assertEquals ( len ( self . sample ) , self . sample . length ( ) )
2012-04-17 18:22:28 -07:00
2012-04-06 12:22:35 -07:00
def test_rom_interval ( self ) :
global rom
load_rom ( )
address = 0x100
interval = 10
correct_strings = [ ' 0x0 ' , ' 0xc3 ' , ' 0x6e ' , ' 0x1 ' , ' 0xce ' ,
' 0xed ' , ' 0x66 ' , ' 0x66 ' , ' 0xcc ' , ' 0xd ' ]
byte_strings = rom . interval ( address , interval , strings = True )
self . assertEqual ( byte_strings , correct_strings )
correct_ints = [ 0 , 195 , 110 , 1 , 206 , 237 , 102 , 102 , 204 , 13 ]
ints = rom . interval ( address , interval , strings = False )
self . assertEqual ( ints , correct_ints )
2012-04-17 18:22:28 -07:00
2012-04-06 12:22:35 -07:00
def test_rom_until ( self ) :
global rom
load_rom ( )
address = 0x1337
byte = 0x13
bytes = rom . until ( address , byte , strings = True )
self . failUnless ( len ( bytes ) == 3 )
self . failUnless ( bytes [ 0 ] == ' 0xd5 ' )
bytes = rom . until ( address , byte , strings = False )
self . failUnless ( len ( bytes ) == 3 )
self . failUnless ( bytes [ 0 ] == 0xd5 )
2012-04-17 18:22:28 -07:00
2012-03-24 11:22:28 -07:00
class TestAsmList ( unittest . TestCase ) :
""" AsmList is a class that should act exactly like list()
except that it never shows the contents of its list
unless explicitly forced """
2012-04-17 18:22:28 -07:00
2012-03-24 11:22:28 -07:00
def test_equals ( self ) :
base = [ 1 , 2 , 3 ]
asm = AsmList ( base )
self . assertEquals ( base , asm )
self . assertEquals ( asm , base )
self . assertEquals ( base , list ( asm ) )
2012-04-17 18:22:28 -07:00
2012-03-24 11:22:28 -07:00
def test_inheritance ( self ) :
self . failUnless ( issubclass ( AsmList , list ) )
2012-04-17 18:22:28 -07:00
2012-03-24 11:22:28 -07:00
def test_length ( self ) :
base = range ( 0 , 10 )
asm = AsmList ( base )
self . assertEquals ( len ( base ) , len ( asm ) )
self . assertEquals ( len ( base ) , asm . length ( ) )
self . assertEquals ( len ( base ) , len ( list ( asm ) ) )
self . assertEquals ( len ( asm ) , asm . length ( ) )
2012-04-17 18:22:28 -07:00
2012-03-24 16:01:37 -07:00
def test_remove_quoted_text ( self ) :
x = remove_quoted_text
self . assertEqual ( x ( " hello world " ) , " hello world " )
self . assertEqual ( x ( " hello \" world \" " ) , " hello " )
input = ' hello world " testing 123 " '
self . assertNotEqual ( x ( input ) , input )
input = " hello world ' testing 123 ' "
self . assertNotEqual ( x ( input ) , input )
self . failIf ( " testing " in x ( input ) )
2012-04-17 18:22:28 -07:00
2012-03-24 16:01:37 -07:00
def test_line_has_comment_address ( self ) :
x = line_has_comment_address
self . assertFalse ( x ( " " ) )
self . assertFalse ( x ( " ; " ) )
self . assertFalse ( x ( " ;;; " ) )
self . assertFalse ( x ( " :; " ) )
self . assertFalse ( x ( " :;: " ) )
self . assertFalse ( x ( " ;: " ) )
self . assertFalse ( x ( " " ) )
self . assertFalse ( x ( " " . join ( " " * 5 ) ) )
self . assertFalse ( x ( " " . join ( " " * 10 ) ) )
self . assertFalse ( x ( " hello world " ) )
self . assertFalse ( x ( " hello_world " ) )
self . assertFalse ( x ( " hello_world: " ) )
self . assertFalse ( x ( " hello_world:; " ) )
self . assertFalse ( x ( " hello_world: ; " ) )
self . assertFalse ( x ( " hello_world: ; " ) )
self . assertFalse ( x ( " hello_world: ; " + " " . join ( " " * 5 ) ) )
self . assertFalse ( x ( " hello_world: ; " + " " . join ( " " * 10 ) ) )
self . assertTrue ( x ( " ;1 " ) )
self . assertTrue ( x ( " ;F " ) )
self . assertTrue ( x ( " ;$00FF " ) )
self . assertTrue ( x ( " ;0x00FF " ) )
self . assertTrue ( x ( " ; 0x00FF " ) )
self . assertTrue ( x ( " ;$3:$300 " ) )
self . assertTrue ( x ( " ;0x3:$300 " ) )
self . assertTrue ( x ( " ;$3:0x300 " ) )
self . assertTrue ( x ( " ;3:300 " ) )
self . assertTrue ( x ( " ;3:FFAA " ) )
self . assertFalse ( x ( ' hello world " how are you today;0x1 " ' ) )
self . assertTrue ( x ( ' hello world " how are you today:0x1 " ;1 ' ) )
2012-03-24 19:34:19 -07:00
returnable = { }
self . assertTrue ( x ( " hello_world: ; 0x4050 " , returnable = returnable , bank = 5 ) )
self . assertTrue ( returnable [ " address " ] == 0x14050 )
2012-04-17 18:22:28 -07:00
2012-03-24 16:01:37 -07:00
def test_line_has_label ( self ) :
x = line_has_label
self . assertTrue ( x ( " hi: " ) )
self . assertTrue ( x ( " Hello: " ) )
self . assertTrue ( x ( " MyLabel: ; test xyz " ) )
self . assertFalse ( x ( " : " ) )
self . assertFalse ( x ( " ;HelloWorld: " ) )
self . assertFalse ( x ( " :::: " ) )
self . assertFalse ( x ( " :;:;:;::: " ) )
2012-04-17 18:22:28 -07:00
2012-03-24 16:01:37 -07:00
def test_get_label_from_line ( self ) :
x = get_label_from_line
self . assertEqual ( x ( " HelloWorld: " ) , " HelloWorld " )
self . assertEqual ( x ( " HiWorld: " ) , " HiWorld " )
self . assertEqual ( x ( " HiWorld " ) , None )
2012-04-17 18:22:28 -07:00
2012-03-24 16:01:37 -07:00
def test_find_labels_without_addresses ( self ) :
global asm
asm = [ " hello_world: ; 0x1 " , " hello_world2: ; " ]
labels = find_labels_without_addresses ( )
self . failUnless ( labels [ 0 ] [ " label " ] == " hello_world2 " )
asm = [ " hello world: ;1 " , " hello_world: ;2 " ]
labels = find_labels_without_addresses ( )
self . failUnless ( len ( labels ) == 0 )
asm = None
2012-04-17 18:22:28 -07:00
2012-03-24 19:34:19 -07:00
def test_get_labels_between ( self ) :
global asm
x = get_labels_between #(start_line_id, end_line_id, bank)
asm = [ " HelloWorld: ;1 " ,
" hi: " ,
" no label on this line " ,
]
labels = x ( 0 , 2 , 0x12 )
self . assertEqual ( len ( labels ) , 1 )
self . assertEqual ( labels [ 0 ] [ " label " ] , " HelloWorld " )
del asm
2012-04-17 18:22:28 -07:00
2012-03-24 19:34:19 -07:00
def test_scan_for_predefined_labels ( self ) :
#label keys: line_number, bank, label, offset, address
load_asm ( )
all_labels = scan_for_predefined_labels ( )
label_names = [ x [ " label " ] for x in all_labels ]
self . assertIn ( " GetFarByte " , label_names )
self . assertIn ( " AddNTimes " , label_names )
self . assertIn ( " CheckShininess " , label_names )
2012-04-17 18:22:28 -07:00
2012-03-24 19:34:19 -07:00
def test_write_all_labels ( self ) :
""" dumping json into a file """
filename = " test_labels.json "
#remove the current file
if os . path . exists ( filename ) :
os . system ( " rm " + filename )
#make up some labels
labels = [ ]
#fake label 1
label = { " line_number " : 5 , " bank " : 0 , " label " : " SomeLabel " , " address " : 0x10 }
labels . append ( label )
#fake label 2
label = { " line_number " : 15 , " bank " : 2 , " label " : " SomeOtherLabel " , " address " : 0x9F0A }
labels . append ( label )
#dump to file
write_all_labels ( labels , filename = filename )
#open the file and read the contents
file_handler = open ( filename , " r " )
contents = file_handler . read ( )
file_handler . close ( )
#parse into json
obj = json . read ( contents )
#begin testing
self . assertEqual ( len ( obj ) , len ( labels ) )
self . assertEqual ( len ( obj ) , 2 )
self . assertEqual ( obj , labels )
2012-04-17 18:22:28 -07:00
2012-03-24 19:34:19 -07:00
def test_isolate_incbins ( self ) :
global asm
asm = [ " 123 " , " 456 " , " 789 " , " abc " , " def " , " ghi " ,
' INCBIN " baserom.gbc " ,$12DA,$12F8 - $12DA ' ,
" jkl " ,
' INCBIN " baserom.gbc " ,$137A,$13D0 - $137A ' ]
lines = isolate_incbins ( )
self . assertIn ( asm [ 6 ] , lines )
self . assertIn ( asm [ 8 ] , lines )
for line in lines :
self . assertIn ( " baserom " , line )
2012-04-17 18:22:28 -07:00
2012-03-24 19:34:19 -07:00
def test_process_incbins ( self ) :
global incbin_lines , processed_incbins , asm
incbin_lines = [ ' INCBIN " baserom.gbc " ,$12DA,$12F8 - $12DA ' ,
' INCBIN " baserom.gbc " ,$137A,$13D0 - $137A ' ]
asm = copy ( incbin_lines )
asm . insert ( 1 , " some other random line " )
processed_incbins = process_incbins ( )
self . assertEqual ( len ( processed_incbins ) , len ( incbin_lines ) )
self . assertEqual ( processed_incbins [ 0 ] [ " line " ] , incbin_lines [ 0 ] )
self . assertEqual ( processed_incbins [ 2 ] [ " line " ] , incbin_lines [ 1 ] )
2012-04-17 18:22:28 -07:00
2012-03-24 19:34:19 -07:00
def test_reset_incbins ( self ) :
global asm , incbin_lines , processed_incbins
#temporarily override the functions
global load_asm , isolate_incbins , process_incbins
temp1 , temp2 , temp3 = load_asm , isolate_incbins , process_incbins
def load_asm ( ) : pass
def isolate_incbins ( ) : pass
def process_incbins ( ) : pass
#call reset
reset_incbins ( )
#check the results
self . assertTrue ( asm == [ ] or asm == None )
self . assertTrue ( incbin_lines == [ ] )
self . assertTrue ( processed_incbins == { } )
#reset the original functions
load_asm , isolate_incbins , process_incbins = temp1 , temp2 , temp3
2012-04-17 18:22:28 -07:00
2012-03-24 20:59:46 -07:00
def test_find_incbin_to_replace_for ( self ) :
global asm , incbin_lines , processed_incbins
asm = [ ' first line ' , ' second line ' , ' third line ' ,
' INCBIN " baserom.gbc " ,$90,$200 - $90 ' ,
' fifth line ' , ' last line ' ]
isolate_incbins ( )
process_incbins ( )
line_num = find_incbin_to_replace_for ( 0x100 )
#must be the 4th line (the INBIN line)
self . assertEqual ( line_num , 3 )
2012-04-17 18:22:28 -07:00
2012-03-24 20:59:46 -07:00
def test_split_incbin_line_into_three ( self ) :
global asm , incbin_lines , processed_incbins
asm = [ ' first line ' , ' second line ' , ' third line ' ,
' INCBIN " baserom.gbc " ,$90,$200 - $90 ' ,
' fifth line ' , ' last line ' ]
isolate_incbins ( )
process_incbins ( )
content = split_incbin_line_into_three ( 3 , 0x100 , 10 )
#must end up with three INCBINs in output
self . failUnless ( content . count ( " INCBIN " ) == 3 )
2012-04-17 18:22:28 -07:00
2012-03-24 20:59:46 -07:00
def test_analyze_intervals ( self ) :
global asm , incbin_lines , processed_incbins
asm , incbin_lines , processed_incbins = None , [ ] , { }
asm = [ ' first line ' , ' second line ' , ' third line ' ,
' INCBIN " baserom.gbc " ,$90,$200 - $90 ' ,
' fifth line ' , ' last line ' ,
' INCBIN " baserom.gbc " ,$33F,$4000 - $33F ' ]
isolate_incbins ( )
process_incbins ( )
largest = analyze_intervals ( )
self . assertEqual ( largest [ 0 ] [ " line_number " ] , 6 )
self . assertEqual ( largest [ 0 ] [ " line " ] , asm [ 6 ] )
self . assertEqual ( largest [ 1 ] [ " line_number " ] , 3 )
self . assertEqual ( largest [ 1 ] [ " line " ] , asm [ 3 ] )
2012-04-17 18:22:28 -07:00
2012-03-24 20:59:46 -07:00
def test_generate_diff_insert ( self ) :
global asm
asm = [ ' first line ' , ' second line ' , ' third line ' ,
' INCBIN " baserom.gbc " ,$90,$200 - $90 ' ,
' fifth line ' , ' last line ' ,
' INCBIN " baserom.gbc " ,$33F,$4000 - $33F ' ]
diff = generate_diff_insert ( 0 , " the real first line " , debug = False )
self . assertIn ( " the real first line " , diff )
self . assertIn ( " INCBIN " , diff )
self . assertNotIn ( " No newline at end of file " , diff )
self . assertIn ( " + " + asm [ 1 ] , diff )
2012-04-17 18:22:28 -07:00
2012-03-24 02:43:06 -07:00
class TestMapParsing ( unittest . TestCase ) :
def test_parse_all_map_headers ( self ) :
2012-04-18 21:20:28 -07:00
global parse_map_header_at , old_parse_map_header_at , counter
2012-03-24 02:43:06 -07:00
counter = 0
for k in map_names . keys ( ) :
if " offset " not in map_names [ k ] . keys ( ) :
map_names [ k ] [ " offset " ] = 0
temp = parse_map_header_at
2012-04-18 21:20:28 -07:00
temp2 = old_parse_map_header_at
2012-03-24 02:43:06 -07:00
def parse_map_header_at ( address , map_group = None , map_id = None , debug = False ) :
global counter
counter + = 1
return { }
2012-04-18 21:20:28 -07:00
old_parse_map_header_at = parse_map_header_at
2012-03-24 02:43:06 -07:00
parse_all_map_headers ( debug = False )
2012-04-18 21:20:28 -07:00
#parse_all_map_headers is currently doing it 2x
#because of the new/old map header parsing routines
self . assertEqual ( counter , 388 * 2 )
2012-03-24 02:43:06 -07:00
parse_map_header_at = temp
2012-04-18 21:20:28 -07:00
old_parse_map_header_at = temp2
2012-04-17 18:22:28 -07:00
2012-03-24 02:43:06 -07:00
class TestTextScript ( unittest . TestCase ) :
2012-03-24 13:05:32 -07:00
""" for testing ' in-script ' commands, etc. """
#def test_to_asm(self):
# pass #or raise NotImplementedError, bryan_message
#def test_find_addresses(self):
# pass #or raise NotImplementedError, bryan_message
#def test_parse_text_at(self):
# pass #or raise NotImplementedError, bryan_message
2012-04-17 18:22:28 -07:00
2012-03-24 02:43:06 -07:00
class TestEncodedText ( unittest . TestCase ) :
2012-03-24 13:05:32 -07:00
""" for testing chars-table encoded text chunks """
2012-04-17 18:22:28 -07:00
2012-03-24 23:09:46 -07:00
def test_process_00_subcommands ( self ) :
g = process_00_subcommands ( 0x197186 , 0x197186 + 601 , debug = False )
self . assertEqual ( len ( g ) , 42 )
self . assertEqual ( len ( g [ 0 ] ) , 13 )
self . assertEqual ( g [ 1 ] , [ 184 , 174 , 180 , 211 , 164 , 127 , 20 , 231 , 81 ] )
2012-04-17 18:22:28 -07:00
2012-03-24 23:09:46 -07:00
def test_parse_text_at2 ( self ) :
oakspeech = parse_text_at2 ( 0x197186 , 601 , debug = False )
self . assertIn ( " encyclopedia " , oakspeech )
self . assertIn ( " researcher " , oakspeech )
self . assertIn ( " dependable " , oakspeech )
2012-04-17 18:22:28 -07:00
2012-03-24 23:43:42 -07:00
def test_parse_text_engine_script_at ( self ) :
p = parse_text_engine_script_at ( 0x197185 , debug = False )
2012-04-18 21:20:28 -07:00
self . assertEqual ( len ( p . commands ) , 2 )
self . assertEqual ( len ( p . commands [ 0 ] [ " lines " ] ) , 41 )
2012-04-17 18:22:28 -07:00
2012-03-24 23:09:46 -07:00
#don't really care about these other two
def test_parse_text_from_bytes ( self ) : pass
def test_parse_text_at ( self ) : pass
2012-04-17 18:22:28 -07:00
2012-03-24 02:43:06 -07:00
class TestScript ( unittest . TestCase ) :
2012-03-24 23:09:46 -07:00
""" for testing parse_script_engine_script_at and script parsing in
general . Script should be a class . """
2012-03-24 13:05:32 -07:00
#def test_parse_script_engine_script_at(self):
# pass #or raise NotImplementedError, bryan_message
2012-04-17 18:22:28 -07:00
2012-03-24 23:09:46 -07:00
def test_find_all_text_pointers_in_script_engine_script ( self ) :
address = 0x197637 #0x197634
script = parse_script_engine_script_at ( address , debug = False )
bank = calculate_bank ( address )
r = find_all_text_pointers_in_script_engine_script ( script , bank = bank , debug = False )
results = list ( r )
self . assertIn ( 0x197661 , results )
2012-04-17 18:22:28 -07:00
2012-04-02 10:36:08 -07:00
class TestLabel ( unittest . TestCase ) :
def test_label_making ( self ) :
line_number = 2
address = 0xf0c0
label_name = " poop "
l = Label ( name = label_name , address = address , line_number = line_number )
self . failUnless ( hasattr ( l , " name " ) )
self . failUnless ( hasattr ( l , " address " ) )
self . failUnless ( hasattr ( l , " line_number " ) )
self . failIf ( isinstance ( l . address , str ) )
self . failIf ( isinstance ( l . line_number , str ) )
self . failUnless ( isinstance ( l . name , str ) )
self . assertEqual ( l . line_number , line_number )
self . assertEqual ( l . name , label_name )
self . assertEqual ( l . address , address )
2012-04-17 18:22:28 -07:00
2012-03-29 23:20:50 -07:00
class TestByteParams ( unittest . TestCase ) :
2012-03-29 21:41:06 -07:00
@classmethod
def setUpClass ( cls ) :
2012-04-02 10:36:08 -07:00
load_rom ( )
2012-03-29 21:41:06 -07:00
cls . address = 10
cls . sbp = SingleByteParam ( address = cls . address )
2012-04-17 18:22:28 -07:00
2012-03-29 21:41:06 -07:00
@classmethod
def tearDownClass ( cls ) :
del cls . sbp
2012-04-17 18:22:28 -07:00
2012-03-29 21:41:06 -07:00
def test__init__ ( self ) :
self . assertEqual ( self . sbp . size , 1 )
self . assertEqual ( self . sbp . address , self . address )
2012-04-17 18:22:28 -07:00
2012-03-29 21:41:06 -07:00
def test_parse ( self ) :
self . sbp . parse ( )
self . assertEqual ( str ( self . sbp . byte ) , str ( 45 ) )
2012-04-17 18:22:28 -07:00
2012-03-29 21:41:06 -07:00
def test_to_asm ( self ) :
self . assertEqual ( self . sbp . to_asm ( ) , " $2d " )
self . sbp . should_be_decimal = True
self . assertEqual ( self . sbp . to_asm ( ) , str ( 45 ) )
2012-04-17 18:22:28 -07:00
2012-04-18 21:20:28 -07:00
#HexByte and DollarSignByte are the same now
2012-03-29 23:20:50 -07:00
def test_HexByte_to_asm ( self ) :
h = HexByte ( address = 10 )
a = h . to_asm ( )
2012-04-18 21:20:28 -07:00
self . assertEqual ( a , " $2d " )
2012-04-17 18:22:28 -07:00
2012-03-29 23:20:50 -07:00
def test_DollarSignByte_to_asm ( self ) :
d = DollarSignByte ( address = 10 )
a = d . to_asm ( )
self . assertEqual ( a , " $2d " )
2012-04-17 18:22:28 -07:00
2012-03-29 23:20:50 -07:00
def test_ItemLabelByte_to_asm ( self ) :
i = ItemLabelByte ( address = 433 )
self . assertEqual ( i . byte , 54 )
self . assertEqual ( i . to_asm ( ) , " COIN_CASE " )
self . assertEqual ( ItemLabelByte ( address = 10 ) . to_asm ( ) , " $2d " )
2012-04-17 18:22:28 -07:00
2012-03-29 23:20:50 -07:00
def test_DecimalParam_to_asm ( self ) :
d = DecimalParam ( address = 10 )
x = d . to_asm ( )
self . assertEqual ( x , str ( 0x2d ) )
2012-04-17 18:22:28 -07:00
2012-03-29 23:20:50 -07:00
class TestMultiByteParam ( unittest . TestCase ) :
def setup_for ( self , somecls , byte_size = 2 , address = 443 , * * kwargs ) :
self . cls = somecls ( address = address , size = byte_size , * * kwargs )
self . assertEqual ( self . cls . address , address )
self . assertEqual ( self . cls . bytes , rom_interval ( address , byte_size , strings = False ) )
self . assertEqual ( self . cls . size , byte_size )
2012-04-17 18:22:28 -07:00
2012-03-29 23:20:50 -07:00
def test_two_byte_param ( self ) :
self . setup_for ( MultiByteParam , byte_size = 2 )
self . assertEqual ( self . cls . to_asm ( ) , " $f0c0 " )
2012-04-17 18:22:28 -07:00
2012-03-29 23:20:50 -07:00
def test_three_byte_param ( self ) :
self . setup_for ( MultiByteParam , byte_size = 3 )
2012-04-17 18:22:28 -07:00
2012-03-29 23:20:50 -07:00
def test_PointerLabelParam_no_bank ( self ) :
self . setup_for ( PointerLabelParam , bank = None )
#assuming no label at this location..
self . assertEqual ( self . cls . to_asm ( ) , " $f0c0 " )
global all_labels
2012-04-02 10:36:08 -07:00
#hm.. maybe all_labels should be using a class?
2012-03-29 23:20:50 -07:00
all_labels = [ { " label " : " poop " , " address " : 0xf0c0 ,
" offset " : 0xf0c0 , " bank " : 0 ,
" line_number " : 2
} ]
self . assertEqual ( self . cls . to_asm ( ) , " poop " )
2012-04-17 18:22:28 -07:00
2012-04-02 20:34:02 -07:00
class TestPostParsing : #(unittest.TestCase):
2012-04-02 12:22:50 -07:00
""" tests that must be run after parsing all maps """
@classmethod
def setUpClass ( cls ) :
run_main ( )
2012-04-17 18:22:28 -07:00
2012-04-02 12:22:50 -07:00
def test_signpost_counts ( self ) :
self . assertEqual ( len ( map_names [ 1 ] [ 1 ] [ " signposts " ] ) , 0 )
self . assertEqual ( len ( map_names [ 1 ] [ 2 ] [ " signposts " ] ) , 2 )
self . assertEqual ( len ( map_names [ 10 ] [ 5 ] [ " signposts " ] ) , 7 )
2012-04-17 18:22:28 -07:00
2012-04-02 12:22:50 -07:00
def test_warp_counts ( self ) :
self . assertEqual ( map_names [ 10 ] [ 5 ] [ " warp_count " ] , 9 )
self . assertEqual ( map_names [ 18 ] [ 5 ] [ " warp_count " ] , 3 )
self . assertEqual ( map_names [ 15 ] [ 1 ] [ " warp_count " ] , 2 )
2012-04-17 18:22:28 -07:00
2012-04-02 12:22:50 -07:00
def test_map_sizes ( self ) :
self . assertEqual ( map_names [ 15 ] [ 1 ] [ " height " ] , 18 )
self . assertEqual ( map_names [ 15 ] [ 1 ] [ " width " ] , 10 )
self . assertEqual ( map_names [ 7 ] [ 1 ] [ " height " ] , 4 )
self . assertEqual ( map_names [ 7 ] [ 1 ] [ " width " ] , 4 )
2012-04-17 18:22:28 -07:00
2012-04-02 12:22:50 -07:00
def test_map_connection_counts ( self ) :
self . assertEqual ( map_names [ 7 ] [ 1 ] [ " connections " ] , 0 )
self . assertEqual ( map_names [ 10 ] [ 1 ] [ " connections " ] , 12 )
self . assertEqual ( map_names [ 10 ] [ 2 ] [ " connections " ] , 12 )
self . assertEqual ( map_names [ 11 ] [ 1 ] [ " connections " ] , 9 ) #or 13?
2012-04-17 18:22:28 -07:00
2012-04-02 12:22:50 -07:00
def test_second_map_header_address ( self ) :
self . assertEqual ( map_names [ 11 ] [ 1 ] [ " second_map_header_address " ] , 0x9509c )
self . assertEqual ( map_names [ 1 ] [ 5 ] [ " second_map_header_address " ] , 0x95bd0 )
2012-04-17 18:22:28 -07:00
2012-04-02 18:27:11 -07:00
def test_event_address ( self ) :
self . assertEqual ( map_names [ 17 ] [ 5 ] [ " event_address " ] , 0x194d67 )
self . assertEqual ( map_names [ 23 ] [ 3 ] [ " event_address " ] , 0x1a9ec9 )
2012-04-17 18:22:28 -07:00
2012-04-02 18:27:11 -07:00
def test_people_event_counts ( self ) :
self . assertEqual ( len ( map_names [ 23 ] [ 3 ] [ " people_events " ] ) , 4 )
self . assertEqual ( len ( map_names [ 10 ] [ 3 ] [ " people_events " ] ) , 9 )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
class TestMetaTesting ( unittest . TestCase ) :
""" test whether or not i am finding at least
some of the tests in this file """
tests = None
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def setUp ( self ) :
if self . tests == None :
self . __class__ . tests = assemble_test_cases ( )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_assemble_test_cases_count ( self ) :
" does assemble_test_cases find some tests? "
self . failUnless ( len ( self . tests ) > 0 )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_assemble_test_cases_inclusion ( self ) :
" is this class found by assemble_test_cases? "
#i guess it would have to be for this to be running?
self . failUnless ( self . __class__ in self . tests )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def test_assemble_test_cases_others ( self ) :
" test other inclusions for assemble_test_cases "
self . failUnless ( TestRomStr in self . tests )
self . failUnless ( TestCram in self . tests )
2012-04-17 18:22:28 -07:00
2012-03-24 12:22:59 -07:00
def test_check_has_test ( self ) :
self . failUnless ( check_has_test ( " beaver " , [ " test_beaver " ] ) )
self . failUnless ( check_has_test ( " beaver " , [ " test_beaver_2 " ] ) )
self . failIf ( check_has_test ( " beaver_1 " , [ " test_beaver " ] ) )
2012-04-17 18:22:28 -07:00
2012-03-24 12:22:59 -07:00
def test_find_untested_methods ( self ) :
untested = find_untested_methods ( )
#the return type must be an iterable
self . failUnless ( hasattr ( untested , " __iter__ " ) )
#.. basically, a list
self . failUnless ( isinstance ( untested , list ) )
2012-04-17 18:22:28 -07:00
2012-03-24 12:22:59 -07:00
def test_find_untested_methods_method ( self ) :
""" create a function and see if it is found """
#setup a function in the global namespace
global some_random_test_method
#define the method
def some_random_test_method ( ) : pass
#first make sure it is in the global scope
members = inspect . getmembers ( sys . modules [ __name__ ] , inspect . isfunction )
func_names = [ functuple [ 0 ] for functuple in members ]
self . assertIn ( " some_random_test_method " , func_names )
#test whether or not it is found by find_untested_methods
untested = find_untested_methods ( )
self . assertIn ( " some_random_test_method " , untested )
#remove the test method from the global namespace
del some_random_test_method
2012-04-17 18:22:28 -07:00
2012-03-24 12:22:59 -07:00
def test_load_tests ( self ) :
loader = unittest . TestLoader ( )
suite = load_tests ( loader , None , None )
suite . _tests [ 0 ] . _testMethodName
membership_test = lambda member : \
inspect . isclass ( member ) and issubclass ( member , unittest . TestCase )
tests = inspect . getmembers ( sys . modules [ __name__ ] , membership_test )
classes = [ x [ 1 ] for x in tests ]
for test in suite . _tests :
self . assertIn ( test . __class__ , classes )
2012-04-17 18:22:28 -07:00
2012-03-24 12:31:33 -07:00
def test_report_untested ( self ) :
untested = find_untested_methods ( )
output = report_untested ( )
if len ( untested ) > 0 :
self . assertIn ( " NOT TESTED " , output )
for name in untested :
self . assertIn ( name , output )
elif len ( untested ) == 0 :
self . assertNotIn ( " NOT TESTED " , output )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def assemble_test_cases ( ) :
""" finds classes that inherit from unittest.TestCase
2012-04-17 18:22:28 -07:00
because i am too lazy to remember to add them to a
2012-03-24 01:54:28 -07:00
global list of tests for the suite runner """
classes = [ ]
clsmembers = inspect . getmembers ( sys . modules [ __name__ ] , inspect . isclass )
for ( name , some_class ) in clsmembers :
if issubclass ( some_class , unittest . TestCase ) :
classes . append ( some_class )
return classes
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def load_tests ( loader , tests , pattern ) :
suite = unittest . TestSuite ( )
for test_class in assemble_test_cases ( ) :
tests = loader . loadTestsFromTestCase ( test_class )
suite . addTests ( tests )
return suite
2012-04-17 18:22:28 -07:00
2012-03-24 12:22:59 -07:00
def check_has_test ( func_name , tested_names ) :
""" checks if there is a test dedicated to this function """
if " test_ " + func_name in tested_names :
return True
for name in tested_names :
if " test_ " + func_name in name :
return True
return False
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def find_untested_methods ( ) :
2012-03-24 12:22:59 -07:00
""" finds all untested functions in this module
2012-03-24 01:54:28 -07:00
by searching for method names in test case
method names . """
2012-03-24 12:22:59 -07:00
untested = [ ]
2012-03-24 23:09:46 -07:00
avoid_funcs = [ " main " , " run_tests " , " run_main " , " copy " , " deepcopy " ]
2012-03-24 12:22:59 -07:00
test_funcs = [ ]
#get a list of all classes in this module
classes = inspect . getmembers ( sys . modules [ __name__ ] , inspect . isclass )
#for each class..
for ( name , klass ) in classes :
#only look at those that have tests
if issubclass ( klass , unittest . TestCase ) :
#look at this class' methods
funcs = inspect . getmembers ( klass , inspect . ismethod )
#for each method..
for ( name2 , func ) in funcs :
#store the ones that begin with test_
if " test_ " in name2 and name2 [ 0 : 5 ] == " test_ " :
test_funcs . append ( [ name2 , func ] )
#assemble a list of all test method names (test_x, test_y, ..)
tested_names = [ funcz [ 0 ] for funcz in test_funcs ]
#now get a list of all functions in this module
funcs = inspect . getmembers ( sys . modules [ __name__ ] , inspect . isfunction )
#for each function..
for ( name , func ) in funcs :
#we don't care about some of these
if name in avoid_funcs : continue
2012-03-25 23:47:58 -07:00
#skip functions beginning with _
2012-04-17 18:22:28 -07:00
if name [ 0 ] == " _ " : continue
2012-03-24 12:22:59 -07:00
#check if this function has a test named after it
has_test = check_has_test ( name , tested_names )
if not has_test :
untested . append ( name )
return untested
2012-04-17 18:22:28 -07:00
2012-03-24 12:22:59 -07:00
def report_untested ( ) :
untested = find_untested_methods ( )
2012-03-24 13:05:32 -07:00
output = " NOT TESTED: [ "
first = True
for name in untested :
if first :
output + = name
first = False
else : output + = " , " + name
2012-03-24 23:09:46 -07:00
output + = " ] \n "
output + = " total untested: " + str ( len ( untested ) )
2012-03-24 12:31:33 -07:00
return output
2012-03-24 01:54:28 -07:00
#### ways to run this file ####
2012-03-24 12:22:59 -07:00
def run_tests ( ) : #rather than unittest.main()
2012-03-24 01:54:28 -07:00
loader = unittest . TestLoader ( )
suite = load_tests ( loader , None , None )
unittest . TextTestRunner ( verbosity = 2 ) . run ( suite )
2012-03-24 12:31:33 -07:00
print report_untested ( )
2012-04-17 18:22:28 -07:00
2012-03-24 01:54:28 -07:00
def run_main ( ) :
2012-05-18 23:44:44 -07:00
# read the rom and figure out the offsets for maps
2012-04-02 10:36:08 -07:00
direct_load_rom ( )
2012-03-05 22:15:35 -08:00
load_map_group_offsets ( )
2012-05-18 23:44:44 -07:00
# add the offsets into our map structure, why not (johto maps only)
2012-03-24 01:54:28 -07:00
[ map_names [ map_group_id + 1 ] . update ( { " offset " : offset } ) for map_group_id , offset in enumerate ( map_group_offsets ) ]
2012-05-18 23:44:44 -07:00
# parse map header bytes for each map
2012-03-24 01:54:28 -07:00
parse_all_map_headers ( )
2012-04-17 18:22:28 -07:00
2012-05-18 23:44:44 -07:00
# find trainers based on scripts and map headers
# this can only happen after parsing the entire map and map scripts
find_trainer_ids_from_scripts ( )
# and parse the main TrainerGroupTable once we know the max number of trainers
2012-05-20 02:32:39 -07:00
#global trainer_group_table
2012-05-20 07:32:59 -07:00
trainer_group_table = TrainerGroupTable ( )
2012-05-18 23:44:44 -07:00
2012-05-19 22:02:43 -07:00
# improve duplicate trainer names
2012-05-20 07:32:59 -07:00
make_trainer_group_name_trainer_ids ( trainer_group_table )
2012-05-19 22:02:43 -07:00
2012-03-24 01:54:28 -07:00
#just a helpful alias
main = run_main
#when you run the file.. do unit tests
if __name__ == " __main__ " :
run_tests ( )
#when you load the module.. parse everything
elif __name__ == " crystal " : pass
#run_main()