make extras/ a submodule

Use pokemontools v1.3.0 and use the same preprocessor as the pokecrystal
project.
This commit is contained in:
Bryan Bishop 2013-09-01 02:27:44 -05:00
parent 4d6b824969
commit e84a5b2062
30 changed files with 18 additions and 6207 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "extras"]
path = extras
url = git://github.com/kanzure/pokemon-reverse-engineering-tools.git

View File

@ -1,17 +1,16 @@
.SUFFIXES: .asm .tx .o .gbc
TEXTFILES = text/oakspeech.tx text/pokedex.tx text/mapRedsHouse1F.tx \
text/mapBluesHouse.tx text/mapPalletTown.tx
TEXTFILES := $(shell find ./ -type f -name '*.asm')
all: pokered.gbc
pokered.o: pokered.tx main.tx constants.tx music.tx wram.tx ${TEXTFILES}
pokered.o: pokered.tx main.tx constants.tx music.tx wram.tx ${TEXTFILES:.asm=.tx}
rgbasm -o pokered.o pokered.tx
pokeblue.o: pokeblue.asm main.tx constants.tx ${TEXTFILES}
pokeblue.o: pokeblue.tx main.tx constants.tx music.tx wram.tx ${TEXTFILES:.asm=.tx}
rgbasm -o pokeblue.o pokeblue.asm
redrle: extras/redrle.c
redrle: extras/redtools/redrle.c
${CC} -o $@ $>
.asm.tx:
@ -28,6 +27,6 @@ pokeblue.gbc: pokeblue.o
cmp blue.gbc $@
clean:
rm -f main.tx pokered.o pokered.gbc pokeblue.o pokeblue.gbc redrle ${TEXTFILES}
rm -f pokered.o pokered.gbc pokeblue.o pokeblue.gbc redrle $(TEXTFILES:.asm=.tx)
more: pokered.gbc pokeblue.gbc

1
extras Submodule

@ -0,0 +1 @@
Subproject commit 795cd58a70c80082003e40127241cfaefa0fae8b

View File

@ -1,41 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2011-01-04
#purpose: insert labels into map headers
import sys
asm = None
asm_lines = []
def load_asm():
global asm, asm_lines
asm = open("../pokered.asm", "r").read()
asm_lines = asm.split("\n")
def find_with_start_of_line(name):
global asm_lines
for line in asm_lines:
if len(line) > len(name) and ": " in line:
if line[:len(name)] == name: return True
return False
def process_lines():
global asm, asm_lines
for line in asm_lines:
if not "_h:" in line: continue #skip
index = asm_lines.index(line)
name = line.split("_h:")[0]
if "Blocks" in asm_lines[index+3]: continue #skip, already done
#if not (str(name + "Blocks:") in asm): continue #skip, no block label found
if not find_with_start_of_line(name + "Blocks:"): continue #skip
orig_line = asm_lines[index+3]
fixed_line = orig_line.split(",")
fixed_line[0] = " dw " + name + "Blocks"
fixed_line = ",".join(fixed_line)
asm_lines[index+3] = fixed_line
if __name__ == "__main__":
load_asm()
process_lines()
sys.stdout.write("\n".join(asm_lines))

View File

@ -1,502 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-03
#purpose: map which addresses are left
#note: use python2.7 because of subprocess
import sys, os
from copy import copy, deepcopy
import subprocess
import json
from extract_maps import rom, assert_rom, load_rom, calculate_pointer, load_map_pointers, read_all_map_headers, map_headers
from pokered_dir import pokered_dir
try:
from pretty_map_headers import map_header_pretty_printer, map_name_cleaner
except Exception:
pass
#store each line of source code here
asm = None
#store each incbin line separately
incbin_lines = []
#storage for processed incbin lines
processed_incbins = {}
def offset_to_pointer(offset):
if type(offset) == str: offset = int(offset, base)
return int(offset) % 0x4000 + 0x4000
def load_asm(filename=os.path.join(pokered_dir, "main.asm")):
"""loads the asm source code into memory
this also detects if the revision of the repository
is using main.asm, common.asm or pokered.asm, which is
useful when generating images in romvisualizer.py"""
global asm
# chronological order is important
defaults = [os.path.join(pokered_dir, f) for f in ["main.asm", "common.asm", "pokered.asm"]]
if filename in defaults:
if not load_asm_if_one_exists_in(defaults):
raise Exception("This shouldn't happen")
elif os.path.exists(filename):
asm = get_all_lines_from_file(filename)
if asm is None:
raise Exception("file doesn't exists (did you mean one among: {0}?)".format(", ".join(defaults)))
return asm
def load_asm_if_one_exists_in(defaults):
global asm
for f in defaults:
if os.path.exists(f):
asm = get_all_lines_from_file(f)
return True
return False
def get_all_lines_from_file(filename):
try:
return open(filename, "r").read().split("\n")
except IOError as e:
raise(e)
def isolate_incbins():
"find each incbin line"
global incbin_lines
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"
global incbins
incbins = {} #reset
for incbin in incbin_lines:
processed_incbin = {}
line_number = asm.index(incbin)
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", "$")
processed_incbin = {
"line_number": line_number,
"line": incbin,
"start": start,
"interval": interval,
"end": end,
}
#don't add this incbin if the interval is 0
if interval != 0:
processed_incbins[line_number] = processed_incbin
def find_incbin_to_replace_for(address):
"""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)
for incbin_key in processed_incbins.keys():
incbin = processed_incbins[incbin_key]
start = incbin["start"]
end = incbin["end"]
#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
def split_incbin_line_into_three(line, start_address, byte_count):
"""
splits an incbin line into three pieces.
you can replace the middle one with the new content of length bytecount
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)
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
def generate_diff_insert(line_number, newline):
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)
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()
try:
diffcontent = subprocess.check_output(
"diff -u {0} {1}".format(os.path.join(pokered_dir, "main.asm"), newfile_filename),
shell=True)
except AttributeError, exc:
raise exc
except Exception, exc:
diffcontent = exc.output
os.system("rm " + original_filename)
os.system("rm " + newfile_filename)
return diffcontent
def insert_map_header_asm(map_id):
map = map_headers[map_id]
line_number = find_incbin_to_replace_for(map["address"])
if line_number == None: # or map_name_cleaner(map["name"], 0) in "\n".join(line for line in asm):
print "i think map id=" + str(map_id) + " has previously been added."
return #this map has already been added i bet
newlines = split_incbin_line_into_three(line_number, map["address"], 12 + (11 * len(map["connections"])))
map_header_asm = map_header_pretty_printer(map_headers[map_id])
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0
elif len(newlines) == 3:
index = 1
newlines[0] += "\n" #spacing is a nice thing to have
newlines[index] = map_header_asm
newlines = "\n".join(line for line in newlines)
diff = generate_diff_insert(line_number, newlines)
print diff
print "... Applying diff."
#write the diff to a file
fh = open("temp.patch", "w")
fh.write(diff)
fh.close()
#apply the patch
os.system("patch {0} temp.patch".format(os.path.join(pokered_dir, "main.asm")))
#remove the patch
os.system("rm temp.patch")
def wrapper_insert_map_header_asm(map_id):
"reload the asm because it has changed (probably)"
load_asm()
isolate_incbins()
process_incbins()
insert_map_header_asm(map_id)
def dump_all_remaining_maps():
for map_id in map_headers:
print "Inserting map id=" + str(map_id)
wrapper_insert_map_header_asm(map_id)
def reset_incbins():
"reset asm before inserting another diff"
asm = None
incbin_lines = []
processed_incbins = {}
load_asm()
isolate_incbins()
process_incbins()
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 {0} {1}".format(
os.path.join(pokered_dir, "main.asm"),
os.path.join(pokered_dir, "main1.asm")))
os.system("patch {0} {1}".format(
os.path.join(pokered_dir, "main.asm"),
"temp.patch"))
#remove the patch
os.system("rm temp.patch")
#confirm it's working
if do_compile:
try:
subprocess.check_call("cd {0}; make clean; LC_CTYPE=C make".format(pokered_dir), shell=True)
return True
except Exception, exc:
if try_fixing:
os.system("mv {0} {1}".format(
os.path.join(pokered_dir, "main1.asm"),
os.path.join(pokered_dir, "main.asm")))
return False
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 is_probably_pointer(input):
try:
blah = int(input, 16)
return True
except:
return False
label_errors = ""
def get_labels_between(start_line_id, end_line_id, bank_id):
labels = []
#label = {
# "line_number": 15,
# "bank_id": 32,
# "label": "PalletTownText1",
# "local_pointer": "$5315",
# "address": 0x75315,
#}
global label_errors
errors = ""
current_line_offset = 0
sublines = asm[start_line_id : end_line_id + 1]
for line in sublines:
label = {}
line_id = start_line_id + current_line_offset
address = None
local_pointer = None
if ": ; 0x" in line:
temp = line.split(": ; 0x")[1]
if not " " in temp:
address = int("0x" + temp, 16)
else:
temp2 = temp.split(" ")[0]
address = int("0x" + temp2, 16)
elif ": ; " in line:
partial = line.split(": ; ")[1]
if ": ; $" in line:
temp = line.split(": ; $")[1]
if " " in temp:
temp = temp.split(" ")[0]
local_pointer = "$" + temp
elif " " in partial:
if " to " in partial:
temp = partial.split(" to ")[0]
if "0x" in temp:
address = int(temp, 16)
elif len(temp) == 4:
local_pointer = "$" + temp
else:
errors += "found \" to \" in partial on line " + str(line_id) + ", but don't know what to do (debug14)" + "\n"
errors += "line is: " + line + "\n"
continue
elif partial[4] == " " and partial[5] == "(":
temp = partial[0:4]
address = int(temp, 16)
elif partial[5] == " " and partial[6] == "(":
temp = partial[0:5]
address = int(temp, 16)
elif len(partial[4]) == 4 or partial[4] == " ": #then it's probably a local pointer
temp = partial[0:4]
local_pointer = "$" + temp
else:
errors += "found \": ; \" and another \" \" in line " + str(line_id) + ", but don't know what to do (debug15)" + "\n"
errors += "line is: " + line + "\n"
continue
else:
if len(partial) > 3 and partial[2] == ":": #14:3BAC
temp = partial[2].split(":")[1]
if len(temp) == 3 or len(temp) == 4:
local_pointer = "$" + temp
else:
temp = temp.split(" ")[0]
local_pointer = "$" + temp
elif len(partial) == 4 or (len(partial) == 3 and is_probably_pointer(partial)):
local_pointer = "$" + partial
else:
errors += "found \": ; \" in line " + str(line_id) + ", but don't know what to do (debug16)" + "\n"
errors += "line is: " + line + "\n"
continue
else:
#this line doesn't have a label
continue
if local_pointer != None and not is_probably_pointer(local_pointer.replace("0x", "").replace("$", "")):
continue
line_label = line.split(": ;")[0]
if address == None and local_pointer != None:
temp = int(local_pointer.replace("$", "0x"), 16)
if temp < 0x4000 or bank_id == 0:
address = temp
else:
address = calculate_pointer(int(local_pointer.replace("$", "0x"), 16), bank_id)
elif local_pointer == None and address != None:
if address < 0x4000:
local_pointer = hex(address).replace("0x", "$")
else:
local_pointer = hex((address % 0x4000) + 0x4000).replace("0x", "$")
print line_label + " is at " + hex(address)
label = {
"line_number": line_id,
"bank_id": bank_id,
"label": line_label,
"local_pointer": local_pointer,
"address": address
}
labels.append(label)
current_line_offset += 1
label_errors += errors
return labels
def scan_for_predefined_labels():
"""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: ; 4F96 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..
"""
bank_intervals = {}
all_labels = []
if asm is None:
load_asm()
#figure out line numbers for each bank
for bank_id in range(0x2d):
abbreviation = ("%.x" % (bank_id)).upper()
abbreviation_next = ("%.x" % (bank_id+1)).upper()
if bank_id == 0:
abbreviation = "0"
abbreviation_next = "1"
start_line_id = index(asm, lambda line: "\"bank" + abbreviation + "\"" in line)
if bank_id != 0x2c:
end_line_id = index(asm, lambda line: "\"bank" + abbreviation_next + "\"" in line)
else:
end_line_id = len(asm) - 1
print "bank" + abbreviation + " starts at " + str(start_line_id) + " to " + str(end_line_id)
bank_intervals[bank_id] = {
"start": start_line_id,
"end": end_line_id,
}
for bank_id in bank_intervals.keys():
bank_data = bank_intervals[bank_id]
start_line_id = bank_data["start"]
end_line_id = bank_data["end"]
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
def write_all_labels(all_labels):
fh = open("labels.json", "w")
fh.write(json.dumps(all_labels))
fh.close()
def analyze_intervals():
"""find the largest baserom.gbc intervals"""
global asm
global 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
if __name__ == "__main__":
#load map headers
load_rom()
load_map_pointers()
read_all_map_headers()
#load incbins (mandatory)
load_asm()
#isolate_incbins()
#process_incbins()
#print processed_incbins
#line_number = find_incbin_to_replace_for(0x492c3)
#newlines = split_incbin_line_into_three(line_number, 0x492c3, 12)
#diff = generate_diff_insert(line_number, newlines)
#print diff
#insert_map_header_asm(86)
#dump_all_remaining_maps()
scan_for_predefined_labels()
print "Errors:"
print label_errors

View File

@ -1,716 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-06
#analyze texts, how many commands are unknown?
import extract_maps
import analyze_incbins #for asm
try:
from pretty_map_headers import map_name_cleaner, txt_bytes, spacing, constant_abbreviation_bytes
except Exception, exc: pass
from operator import itemgetter
import sys
debug = False #set to True to increase logging output
#how many times is each command byte called?
totals = {}
total_text_commands = 0
should_be_total = 0
def get_text_pointer(texts_pointer, text_id):
if type(texts_pointer) == str: texts_pointer = int(texts_pointer, 16)
if type(text_id) == str: text_id = int(text_id)
byte1 = ord(extract_maps.rom[texts_pointer + ((text_id - 1) * 2)])
byte2 = ord(extract_maps.rom[texts_pointer + ((text_id - 1) * 2) + 1])
pointer = (byte1 + (byte2 << 8))
return pointer
def how_many_until(byte, starting):
index = extract_maps.rom.find(byte, starting)
return index-starting
def print_command_debug_info(command_byte, text_id, text_pointer, map_id):
if debug:
print "byte is " + str(command_byte) + " on text #" + str(text_id) + " at " + hex(text_pointer) + " on map " + str(map_id) + " (" + extract_maps.map_headers[map_id]["name"] + ")"
def add_command_byte_to_totals(byte):
global totals
if not byte in totals.keys(): totals[byte] = 1
else: totals[byte] += 1
def process_00_subcommands(start_address, end_address):
"""split this text up into multiple lines
based on subcommands ending each line"""
lines = {}
subsection = extract_maps.rom[start_address:end_address+1]
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
#don't forget the last line
lines[line_count] = current_line
line_count += 1
return lines
def parse_text_script(text_pointer, text_id, map_id, txfar=False):
global total_text_commands
offset = text_pointer
commands = {}
command_counter = 0
if extract_maps.rom == None:
extract_maps.load_rom()
end = False
while not end:
command = {}
command_byte = ord(extract_maps.rom[offset])
print_command_debug_info(command_byte, text_id, text_pointer, map_id)
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)
#whichever command comes first
jump = min([jump57, jump50, jump58])
end_address = offset + jump - 1 #we want the address before $57
command = {"type": command_byte,
"start_address": offset,
"end_address": end_address,
"size": jump,
"lines": process_00_subcommands(offset+1, end_address),
}
offset += jump
elif command_byte == 0x17:
#TX_FAR [pointer][bank]
pointer_byte1 = ord(extract_maps.rom[offset+1])
pointer_byte2 = ord(extract_maps.rom[offset+2])
pointer_bank = ord(extract_maps.rom[offset+3])
pointer = (pointer_byte1 + (pointer_byte2 << 8))
pointer = extract_maps.calculate_pointer(pointer, pointer_bank)
command = {"type": command_byte,
"start_address": offset,
"end_address": offset + 3, #last byte belonging to this command
"pointer": pointer, #parameter
}
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,
}
#this byte simply indicates to end the script
end = True
#this byte simply indicates to end the script
if command_byte == 0x50 and ord(extract_maps.rom[offset+1]) == 0x50: #$50$50 means end completely
end = True
commands[command_counter+1] = command
#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(extract_maps.rom[offset+1])
pointer_byte2 = ord(extract_maps.rom[offset+2])
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
}
#view near these bytes
#subsection = extract_maps.rom[offset:offset+size+1] #peak ahead
#for x in subsection:
# print hex(ord(x))
#print "--"
offset += 2 + 1 #go to the next byte
#use this to look at the surrounding bytes
if debug:
print "next command is: " + hex(ord(extract_maps.rom[offset])) + " ... we are at command number: " + str(command_counter) + " near " + hex(offset) + " on map_id=" + str(map_id) + " for text_id=" + str(text_id) + " and txfar(recursion)=" + str(txfar)
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(extract_maps.rom[offset+1]), ord(extract_maps.rom[offset+2])],
"y": ord(extract_maps.rom[offset+3]),
"x": ord(extract_maps.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)
#whichever command comes first
jump = min([jump57, jump50, jump58])
end_address = offset + jump - 1 #we want the address before $57
command = {"type": command_byte,
"start_address": offset,
"end_address": end_address,
"size": jump,
"lines": process_00_subcommands(offset+1, end_address),
}
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(extract_maps.rom[offset+1])
ram_address_byte2 = ord(extract_maps.rom[offset+2])
read_byte = ord(extract_maps.rom[offset+3])
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
}
offset += 4
elif command_byte == 0xB:
#0B = sound_86 (Hiro obtains ITEM)[same as 0F]
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xE:
#0E = sound_91 (learnt something)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xF:
#0F = sound_86 (given rare candy)[same as 0B]
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x10:
#10 = sound_89 (PKMN successfully caught)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x11:
#11 = sound_94 (Hiro gives OAK the PARCEL)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x12:
#12 = sound_9A (successfully caught)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x13:
#13 = sound_98 (song heard when "new data will be added for..")
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x14:
#14 = MonCry (Nidorina)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x15:
#14 = MonCry (Pidgeot)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x16:
#14 = MonCry (Dewgong)
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x19:
#19 = play a 'bump' noise
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x1F:
#1F = play some pokemon's roar, don't know which..
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x20:
#20 = oddish roar?
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x3F:
#3F = some other roar
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x9D:
#9D = a roar or some other sound, four times in quick succession
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0x76:
#76 = another roar
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xCA:
#CA = stop music, start this other song that i can't name
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xF6:
#F6 = play a 'blurp blurp' noise.. like something is increasing
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xFA:
#FA = change music to champion song?
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xFE:
#FE = another roar, kinda glitchy?
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
elif command_byte == 0xFF:
#FF = change music to a specific song that i don't know the name of
command = {"type": command_byte, "start_address": offset, "end_address": offset}
offset += 1
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(extract_maps.rom[offset])) + " on map_id=" + str(map_id) + " text_id=" + str(text_id)
#end at the first unknown command
end = True
add_command_byte_to_totals(command_byte)
commands[command_counter] = command
command_counter += 1
total_text_commands += len(commands)
return commands
def analyze_texts():
global should_be_total
texts = {}
for map_id in extract_maps.map_headers:
if map_id in extract_maps.bad_maps: continue #skip
map2 = extract_maps.map_headers[map_id]
map2["texts"] = {}
referenced_texts = map2["referenced_texts"]
should_be_total += len(referenced_texts)
texts_pointer = int(map2["texts_pointer"], 16)
#print "Checking texts on... map_id=" + str(map_id) + " and len(referenced_texts)=" + str(len(referenced_texts))
for text_id in referenced_texts:
text_pointer = get_text_pointer(texts_pointer, text_id)
if 0x4000 <= text_pointer <= 0x7fff: #only care about bank when it's between 4000-7fff
text_pointer = extract_maps.calculate_pointer(text_pointer, int(map2["bank"], 16))
#print "Working on map id=" + str(map2["id"]) + " and text id=" + str(text_id)
#print "for map_id=" + str(map_id) + " texts_pointer=" + hex(texts_pointer) + " text_id=" + str(text_id) + " the pointer=" + hex(text_pointer)
commands = parse_text_script(text_pointer, text_id, map_id)
#process TX_FARs
for command_id in commands:
#skip commands starting with an unknown command byte
if len(commands[command_id]) == 0: continue
if commands[command_id]["type"] == 0x17:
TX_FAR = parse_text_script(commands[command_id]["pointer"], text_id, map_id, txfar=True)
if debug:
if len(TX_FAR.keys()) > 0:
#print "TX_FAR object: " + str(TX_FAR)
print "processing a TX_FAR at " + hex(commands[command_id]["pointer"]) + "... first byte is: " + str(ord(extract_maps.rom[commands[command_id]["pointer"]])) + " .. offset: " + hex(commands[command_id]["pointer"])
##sys.exit(0)
commands[command_id]["TX_FAR"] = TX_FAR
#map2["texts"][text_id][command_id]["TX_FAR"] = parse_text_script(command["pointer"], text_id, map_id)
map2["texts"][text_id] = commands
texts[map_id] = map2["texts"]
extract_maps.map_headers[map_id]["texts"] = map2["texts"]
return texts
def find_missing_08s(all_texts):
"""determines which $08s have yet to be inserted
based on their start addresses"""
missing_08s = 0
for map_id in all_texts.keys():
for text_id in all_texts[map_id].keys():
for line_id in all_texts[map_id][text_id].keys():
if not line_id == 0:
current_line = all_texts[map_id][text_id][line_id]
if "type" in current_line.keys():
if current_line["type"] == 0x8:
missing_08s += 1
print "missing $08 on map_id=" + str(map_id) + " text_id=" + str(text_id) + " line_id=" + str(line_id) + " at " + hex(current_line["start_address"])
return missing_08s
def text_pretty_printer_at(start_address, label="SomeLabel"):
commands = parse_text_script(start_address, None, None)
needs_to_begin_with_0 = True #how should this be determined?
#wanted_command = None
#if needs_to_begin_with_0:
# wanted_command = None
# for command_id in commands:
# command = commands[command_id]
# if command["type"] == 0:
# wanted_command = command_id
#
# if wanted_command == None:
# raise "error: address did not start with a $0 text"
#start with zero please
byte_count = 0
output = ""
had_text_end_byte = False
had_text_end_byte_57_58 = False
had_db_last = False
first_line = True
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?
if command["type"] == 0x1: #TX_RAM
if first_line:
output = "\n"
output += label + ": ; " + hex(start_address)
first_line = False
p1 = command["pointer"][0]
p2 = command["pointer"][1]
#remember to account for big endian -> little endian
output += "\n" + spacing + "TX_RAM $%.2x%.2x" %(p2, p1)
byte_count += 3
had_db_last = False
elif command["type"] == 0x17: #TX_FAR
if first_line:
output = "\n"
output += label + ": ; " + hex(start_address)
first_line = False
#p1 = command["pointer"][0]
#p2 = command["pointer"][1]
output += "\n" + spacing + "TX_FAR _" + label + " ; " + hex(command["pointer"])
byte_count += 4 #$17, bank, address word
had_db_last = False
elif command["type"] == 0x9: #TX_RAM_HEX2DEC
if first_line:
output = "\n" + label + ": ; " + hex(start_address)
first_line = False
#address, read_byte
output += "\n" + spacing + "TX_NUM $%.2x%.2x, $%.2x" % (command["address"][1], command["address"][0], command["read_byte"])
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 first_line:
output = "\n" + label + ": ; " + hex(start_address)
first_line = False
if had_db_last:
output += ", $50"
else:
output += "\n" + spacing + "db $50"
byte_count += 1
had_db_last = True
elif command["type"] in [0x57, 0x58] and not had_text_end_byte_57_58:
if first_line: #shouldn't happen, really
output = "\n" + label + ": ; " + hex(start_address)
first_line = False
if had_db_last:
output += ", $%.2x" % (command["type"])
else:
output += "\n" + spacing + "db $%.2x" % (command["type"])
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 first_line:
output = "\n" + label + ": ; " + hex(start_address)
first_line = False
if had_db_last:
output += ", $0b"
else:
output += "\n" + spacing + "db $0B"
byte_count += 1
had_db_last = True
elif command["type"] == 0x11:
if first_line:
output = "\n" + label + ": ; " + hex(start_address)
first_line = False
if had_db_last:
output += ", $11"
else:
output += "\n" + spacing + "db $11"
byte_count += 1
had_db_last = True
elif command["type"] == 0x6: #wait for keypress
if first_line:
output = "\n" + label + ": ; " + hex(start_address)
first_line = False
if had_db_last:
output += ", $6"
else:
output += "\n" + spacing + "db $6"
byte_count += 1
had_db_last = True
else:
print "ERROR in command: " + hex(command["type"])
had_db_last = False
#everything else is for $0s, really
continue
lines = commands[this_command]["lines"]
#reset this in case we have non-$0s later
had_db_last = False
#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"])
if first_line:
output = "\n"
output += label + ": ; " + hex(start_address) + "\n"
first_line = False
else:
output += "\n"
first = True #first byte
for line_id in lines:
line = lines[line_id]
output += spacing + "db "
if first and needs_to_begin_with_0:
output += "$0, "
first = False
byte_count += 1
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
if byte in txt_bytes:
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
output += txt_bytes[byte]
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
#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 += ", "
output += "$" + hex(byte)[2:]
was_byte = True
#add a comma unless it's the end of the line
#if byte_count+1 != len(line):
# output += ", "
first_byte = False
byte_count += 1
#close final quotes
if quotes_open:
output += "\""
quotes_open = False
output += "\n"
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)
print output
return (output, byte_count)
def is_label_in_asm(label):
for line in analyze_incbins.asm:
if label in line:
if line[0:len(label)] == label:
return True
return False
def find_undone_texts():
usable_table = {}
if analyze_incbins.asm == None:
analyze_incbins.load_asm()
for map_id in extract_maps.map_headers:
#skip bad maps
if map_id in extract_maps.bad_maps: continue
map2 = extract_maps.map_headers[map_id]
name = map_name_cleaner(map2["name"], None)[:-2] + "Text"
for text_id in map2["referenced_texts"]:
label = name + str(text_id)
if len(extract_maps.map_headers[map_id]["texts"][text_id].keys()) == 0: continue
if len(extract_maps.map_headers[map_id]["texts"][text_id][0].keys()) == 0: continue
try:
address = extract_maps.map_headers[map_id]["texts"][text_id][0]["start_address"]
except:
address = extract_maps.map_headers[map_id]["texts"][text_id][1]["start_address"]
if not is_label_in_asm(label):
print label + " map_id=" + str(map_id) + " text_id=" + str(text_id) + " at " + hex(address) + " byte is: " + hex(ord(extract_maps.rom[address]))
if not address in usable_table.keys():
usable_table[address] = 1
else:
usable_table[address] += 1
print "\n\n which ones are priority?"
sorted_results = sorted(usable_table.iteritems(), key=itemgetter(1), reverse=True)
for result in sorted_results:
print str(result[1]) + " times: " + hex(result[0])
def scan_rom_for_tx_fars(printer=True):
"""find TX_FARs
search only addresses that are INCBINed
keep only TX_FARs that are valid
returns a list of [TX_FAR target address, TX_FAR address]"""
rom = extract_maps.rom
analyze_incbins.load_asm()
analyze_incbins.isolate_incbins()
analyze_incbins.process_incbins()
possible_tx_fars = []
possible_tx_far_targets = []
for incbin_line_number in analyze_incbins.processed_incbins.keys():
incbin = analyze_incbins.processed_incbins[incbin_line_number]
start_address = incbin["start"]
end_address = incbin["end"]
if incbin["interval"] == 0: continue #skip this one
subrom = rom[start_address:end_address]
for address in range(start_address, end_address):
current_byte = ord(rom[address])
if current_byte == 0x17:
if ord(rom[address+4]) == 0x50:
byte1 = ord(rom[address+1])
byte2 = ord(rom[address+2])
address2 = byte1 + (byte2 << 8)
if address2 > 0x3fff:
address2 = extract_maps.calculate_pointer(address2, ord(rom[address+3]))
#print "possible TX_FAR at " + hex(address) + " to " + hex(address2)
possible_tx_fars.append(address)
possible_tx_far_targets.append([address2, address])
if printer:
pre_handled = []
#address_bundle is [TX_FAR target address, TX_FAR address]
for address_bundle in possible_tx_far_targets:
if address_bundle[0] in [0xa82f8, 0xa8315]:
continue #bad
if address_bundle[0] in pre_handled:
continue #already did this
print "-------"
print "TX_FAR is at: " + hex(address_bundle[1])
#let's try printing out the TX_FAR?
text_pretty_printer_at(address_bundle[1], "blah")
text_pretty_printer_at(address_bundle[0], "_blah")
print "-------"
pre_handled.append(address_bundle[0])
return possible_tx_far_targets
if __name__ == "__main__":
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
#text_output = analyze_texts()
#print text_output
#these aren't really "missing", just a certain type that was
#known to be missed on a first pass.
#missing_08s = find_missing_08s(text_output)
#print "\n\n---- stats ----\n\n"
#popular_text_commands = sorted(totals.iteritems(), key=itemgetter(1), reverse=True)
#convert the first values (command byte) to hex
#for popular_item in popular_text_commands:
# print hex(popular_item[0]) + " was used " + str(popular_item[1]) + " times."
#print "popular text commands: " + str(popular_text_commands)
#print "total text commands: " + str(total_text_commands)
#print "total text scripts: " + str(should_be_total)
#print "missing 08s: " + str(missing_08s)
#print "\n\n"
#text_pretty_printer_at(0x800b1)
#find_undone_texts()
scan_rom_for_tx_fars()

View File

@ -1,104 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-15
#help with connection math
import extract_maps
from pretty_map_headers import map_constants, map_name_cleaner, offset_to_pointer
def print_connections(map_id, in_connection_id=None, do_output=False):
map1 = extract_maps.map_headers[map_id]
map1_name = map1["name"]
connections = map1["connections"]
output = ""
if in_connection_id != None:
connections2 = {}
connections2[in_connection_id] = connections[in_connection_id]
connections = connections2
for connection_id in connections:
connection = connections[connection_id]
direction = connection["direction"]
connected_pointer = int(connection["connected_map_tile_pointer"], 16)
current_pointer = int(connection["current_map_tile_pointer"], 16)
map2_id = connection["map_id"]
map2 = extract_maps.map_headers[map2_id]
map2_name = map2["name"]
map2_cname = map_name_cleaner(map2["name"], None)[:-2]
map2_bank = int(map2["bank"], 16)
map2_blocks_pointer = offset_to_pointer(int(map2["map_pointer"], 16))
map2_height = int(map2["y"], 16)
map2_width = int(map2["x"], 16)
output += map1_name + " (id=" + str(map_id) + ") " + direction + " to " + map2_name + "\n"
output += "map2 blocks pointer: " + hex(map2_blocks_pointer) + "\n"
output += "map2 height: " + str(map2_height) + "\n"
output += "map2 width: " + str(map2_width) + "\n"
output += "map1 connection pointer: " + hex(connected_pointer) + "\n"
shift = 0
#not sure about the calculated shift for NORTH or SOUTH
if direction == "NORTH":
calculated = map2_blocks_pointer + (map2_height - 3) * map2_width
result = connected_pointer - calculated
if result != 0:
shift = result #seems to always be 2?
calculated = map2_blocks_pointer + (map2_height - 3) * map2_width + shift
output += "shift: " + str(shift) + "\n"
formula = map2_cname + "Blocks + (" + map2_cname + "Height - 3) * " + map2_cname + "Width + " + str(shift)
else:
formula = map2_cname + "Blocks + (" + map2_cname + "Height - 3) * " + map2_cname + "Width"
elif direction == "SOUTH":
calculated = map2_blocks_pointer
result = connected_pointer - calculated
formula = map2_cname + "Blocks"
if result != 0:
shift = result
calculated = map2_blocks_pointer + shift
output += "shift: " + str(shift) + "\n"
formula += " + " + str(shift)
elif direction == "WEST":
calculated = map2_blocks_pointer - 3 + (map2_width)
result = connected_pointer - calculated
formula = map2_cname + "Blocks - 3 + (" + map2_cname + "Width)"
if result != 0:
shift = result / map2_width
shift += 1
calculated = map2_blocks_pointer - 3 + (map2_width * shift)
output += "shift: " + str(shift) + "\n"
formula = map2_cname + "Blocks - 3 + (" + map2_cname + "Width * " + str(shift) + ")"
elif direction == "EAST":
calculated = map2_blocks_pointer + (map2_width)
result = connected_pointer - calculated
output += ".. result is: " + str(result) + "\n"
formula = map2_cname + "Blocks + (" + map2_cname + "Width)"
if result != 0:
shift = result / map2_width
shift += 1
calculated = map2_blocks_pointer + (map2_width * shift)
output += "shift: " + str(shift) + "\n"
formula = map2_cname + "Blocks" + " + (" + map2_cname + "Width * " + str(shift) + ")"
output += "formula: " + formula + "\n"
result = connected_pointer - calculated
output += "result: " + str(result) + "\n"
output += "\n\n"
if in_connection_id != None:
return formula
if do_output == True:
return output
if __name__ == "__main__":
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
#trouble:
#print_connections(13)
#print_connections(15)
for map_id in extract_maps.map_headers.keys():
if map_id not in extract_maps.bad_maps:
print print_connections(map_id, do_output=True)

View File

@ -1,702 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-02
#url: http://hax.iimarck.us/files/rbheaders.txt
import json
#parse hex values as base 16 (see calculate_pointer)
base = 16
#where to load the rom from
rom_filename = "../baserom.gbc"
rom = None #load the rom later
#map header pointers start at 0x1AE
start_map_header_pointers = 0x1AE
#bank bytes for each map header start at 0xC23D
start_map_header_pointer_banks = 0xC23D
#number of maps in this list
map_count = 0xF8 #including the 0th the total is is 248 or 0xF8
bad_maps = [0x0b, 0x45, 0x4b, 0x4e, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x70, 0x72, 0x73, 0x74, 0x75, 0xad, 0xcc, 0xcd, 0xce, 0xe7, 0xed, 0xee, 0xf1, 0xf2, 0xf3, 0xf4]
maps = {
0x00: "Pallet Town",
0x01: "Viridian City",
0x02: "Pewter City",
0x03: "Cerulean City",
0x04: "Lavender Town", #??
0x05: "Vermilion City", #correct
0x06: "Celadon City",
0x07: "Fuchsia City",
0x08: "Cinnabar Island",
0x09: "Indigo Plateau",
0x0A: "Saffron City",
0x0B: "FREEZE",
0x0C: "Route 1",
0x0D: "Route 2",
0x0E: "Route 3",
0x0F: "Route 4",
0x10: "Route 5",
0x11: "Route 6",
0x12: "Route 7",
0x13: "Route 8",
0x14: "Route 9",
0x15: "Route 10",
0x16: "Route 11",
0x17: "Route 12",
0x18: "Route 13",
0x19: "Route 14",
0x1A: "Route 15",
0x1B: "Route 16",
0x1C: "Route 17",
0x1D: "Route 18",
0x1E: "Route 19",
0x1F: "Route 20",
0x20: "Route 21",
0x21: "Route 22",
0x22: "Route 23",
0x23: "Route 24",
0x24: "Route 25",
0x25: "Red's House 1F",
0x26: "Red's House 2F",
0x27: "Blue's House",
0x28: "Oak's Lab",
0x29: "Viridian Poke Center",
0x2A: "Viridian Mart",
0x2B: "School",
0x2C: "Viridian House",
0x2D: "Viridian Gym",
0x2E: "Digletts Cave (Route 2)",
0x2F: "Viridian Forest (exit)",
0x30: "Route 2 House",
0x31: "Route 2 Gate",
0x32: "Viridian Forest (Entrance)",
0x33: "Viridian Forest",
0x34: "Museum F1",
0x35: "Museum F2",
0x36: "Pewter Gym",
0x37: "Pewter House (1)",
0x38: "Pewter Mart",
0x39: "Pewter House (2)",
0x3A: "Pewter Pokecenter",
0x3B: "Mt. Moon (1)",
0x3C: "Mt. Moon (2)",
0x3D: "Mt. Moon (3)",
0x3E: "Cerulean House (Trashed)",
0x3F: "Cerulean House (2)",
0x40: "Cerulean Pokecenter",
0x41: "Cerulean Gym",
0x42: "Bike Shop",
0x43: "Cerulean Mart",
0x44: "Mt. Moon Pokecenter",
0x45: "COPY OF: Trashed House",
0x46: "Route 5 Gate",
0x47: "Underground Tunnel Entrance (Route 5)",
0x48: "Day Care M",
0x49: "Route 6 Gate",
0x4A: "Underground Tunnel Entrance (Route 6)",
0x4B: "COPY OF: Underground Tunnel Entrance (Route 6)",
0x4C: "Route 7 Gate",
0x4D: "Underground Path Entrance (Route 7)",
0x4E: "COPY OF: Underground Path Entrance (Route 7)",
0x4F: "Route 8 Gate",
0x50: "Underground Path Entrance (Route 8)",
0x51: "Rock Tunnel Pokecenter",
0x52: "Rock Tunnel (1)",
0x53: "Power Plant",
0x54: "Route 11 Gate",
0x55: "Digletts Cave Entrance (Route 11)",
0x56: "Route 11 Gate (Upstairs)",
0x57: "Route 12 Gate",
0x58: "Bill's House",
0x59: "Vermilion Pokecenter",
0x5A: "Fan Club",
0x5B: "Vermilion Mart",
0x5C: "Vermilion Gym",
0x5D: "Vermilion House (1)",
0x5E: "Vermilion Dock",
0x5F: "S.S. Anne (1)",
0x60: "S.S. Anne (2)",
0x61: "S.S. Anne (3)",
0x62: "S.S. Anne (4)",
0x63: "S.S. Anne (5)",
0x64: "S.S. Anne (6)",
0x65: "S.S. Anne (7)",
0x66: "S.S. Anne (8)",
0x67: "S.S. Anne (9)",
0x68: "S.S. Anne (10)",
0x69: "FREEZE",
0x6A: "FREEZE",
0x6B: "FREEZE",
0x6C: "Victory Road (1)",
0x6D: "FREEZE",
0x6E: "FREEZE",
0x6F: "FREEZE",
0x70: "FREEZE",
0x71: "Lance",
0x72: "FREEZE",
0x73: "FREEZE",
0x74: "FREEZE",
0x75: "FREEZE",
0x76: "Hall of Fame Room",
0x77: "Underground Path (N/S)",
0x78: "Gary",
0x79: "Underground Path (W/E)",
0x7A: "Celadon Mart (1)",
0x7B: "Celadon Mart (2)",
0x7C: "Celadon Mart (3)",
0x7D: "Celadon Mart (4)",
0x7E: "Celadon Mart Roof",
0x7F: "Celadon Mart Elevator",
0x80: "Celadon Mansion (1)",
0x81: "Celadon Mansion (2)",
0x82: "Celadon Mansion (3)",
0x83: "Celadon Mansion (4)",
0x84: "Celadon Mansion (5)",
0x85: "Celadon Pokecenter",
0x86: "Celadon Gym",
0x87: "Celadon Game Corner",
0x88: "Celadon Mart 5",
0x89: "Celadon Prize Room",
0x8A: "Celadon Diner",
0x8B: "Celadon House",
0x8C: "Celadon Hotel",
0x8D: "Lavender Pokecenter",
0x8E: "Pokemon Tower (1)",
0x8F: "Pokemon Tower (2)",
0x90: "Pokemon Tower (3)",
0x91: "Pokemon Tower (4)",
0x92: "Pokemon Tower (5)",
0x93: "Pokemon Tower (6) ",
0x94: "Pokemon Tower (7)",
0x95: "Lavender House (1)",
0x96: "Lavender Mart",
0x97: "Lavender House (2)",
0x98: "Fuchsia Mart",
0x99: "Fuchsia House (1)",
0x9A: "Fuchsia Pokecenter",
0x9B: "Fuchsia House (2)",
0x9C: "Safari Zone Entrance",
0x9D: "Fuchsia Gym",
0x9E: "Fuchsia Meeting Room",
0x9F: "Seafoam Islands (2)",
0xA0: "Seafoam Islands (3)",
0xA1: "Seafoam Islands (4)",
0xA2: "Seafoam Islands (5)",
0xA3: "Vermilion House (2)",
0xA4: "Fuchsia House (3)",
0xA5: "Mansion (1)",
0xA6: "Cinnabar Gym",
0xA7: "Lab (1)",
0xA8: "Lab (2)",
0xA9: "Lab (3)",
0xAA: "Lab (4)",
0xAB: "Cinnabar Pokecenter",
0xAC: "Cinnabar Mart",
0xAD: "COPY: Cinnabar Mart",
0xAE: "Indigo Plateau Lobby",
0xAF: "Copycat's House F1",
0xB0: "Copycat's House F2",
0xB1: "Fighting Dojo",
0xB2: "Saffron Gym",
0xB3: "Saffron House (1)",
0xB4: "Saffron Mart",
0xB5: "Silph Co (1)",
0xB6: "Saffron Pokecenter",
0xB7: "Saffron House (2)",
0xB8: "Route 15 Gate",
0xBA: "Route 16 Gate Map",
0xBB: "Route 16 Gate Upstairs",
0xBC: "Route 16 House",
0xBD: "Route 12 House",
0xBE: "Route 18 Gate",
0xBF: "Route 18 Gate Header",
0xC0: "Seafoam Islands (1)",
0xC1: "Route 22 Gate",
0xC2: "Victory Road (2)",
0xC3: "Route 12 Gate Upstairs",
0xC4: "Vermilion House (3)",
0xC5: "Diglett's Cave",
0xC6: "Victory Road (3)",
0xC7: "Rocket Hideout (1)",
0xC8: "Rocket Hideout (2)",
0xC9: "Rocket Hideout (3)",
0xCA: "Rocket Hideout (4) ",
0xCB: "Rocket Hideout (Elevator)",
0xCC: "FREEZE",
0xCD: "FREEZE",
0xCE: "FREEZE",
0xCF: "Silph Co (2)",
0xD0: "Silph Co (3)",
0xD1: "Silph Co (4)",
0xD2: "Silph Co (5)",
0xD3: "Silph Co (6)",
0xD4: "Silph Co (7)",
0xD5: "Silph Co (8)",
0xD6: "Mansion (2)",
0xD7: "Mansion (3)",
0xD8: "Mansion (4)",
0xD9: "Safari Zone East",
0xDA: "Safari Zone North",
0xDB: "Safari Zone West",
0xDC: "Safari Zone Center",
0xDD: "Safari Zone Rest House (1)",
0xDE: "Safari Zone Secret House",
0xDF: "Safari Zone Rest House (2)",
0xE0: "Safari Zone Rest House (3)",
0xE1: "Safari Zone Rest House (4)",
0xE2: "Unknown Dungeon (2)",
0xE3: "Unknown Dungeon (3)",
0xE4: "Unknown Dungeon (1)",
0xE5: "Name Rater",
0xE6: "Cerulean House (3)",
0xE7: "FREEZE",
0xE8: "Rock Tunnel (2)",
0xE9: "Silph Co (9)",
0xEA: "Silph Co (10)",
0xEB: "Silph Co (11)",
0xEC: "Silph Co (Elevator)",
0xED: "FREEZE",
0xEE: "FREEZE",
0xEF: "Battle Center M",
0xF0: "Trade Center M",
0xF1: "FREEZE",
0xF2: "FREEZE",
0xF3: "FREEZE",
0xF4: "FREEZE",
0xF5: "Lorelei",
0xF6: "Bruno",
0xF7: "Agatha"
}
map_pointers = {
#0x00: {
# "name": "Pallet Town",
# "address": 0x182a1
# },
}
map_headers = {
#0x00: {
# "name": "Pallet Town",
# "address": 0x182a1,
# "tileset"
# "y"
# "x"
# "map_pointer"
# "texts_pointer"
# "script_pointer"
# "connection_byte"
# "num_connections"
# "connections":
# { "0":
# { map_id, connected_map_tile_pointer, current_map_tile_pointer, bigness, width, y, x, window_pointer }
# },
# "object_data_pointer"
# },
}
#haters gonna hate
def load_rom(filename=None):
"load the rom into a global (returns True/False)"
global rom
if not filename:
filename = rom_filename
try:
rom = open(filename, "rb").read()
return True
except Exception, exception:
print "error loading rom"
return False
def assert_rom():
global rom
assert rom, "rom must be loaded, see load_rom()"
def calculate_pointer(short_pointer, bank):
short_pointer = int(short_pointer)
bank = int(bank)
pointer = short_pointer - 0x4000 + (bank * 0x4000)
#result will be an integer
return pointer
def get_nth_map_header_pointer_bank_byte_address(map_id):
"returns the address to the bank byte associated with this map pointer"
address = start_map_header_pointer_banks + map_id
return address
def get_nth_map_header_pointer_bank_byte(map_id):
"returns the bank number for this map header"
assert_rom()
address = get_nth_map_header_pointer_bank_byte_address(map_id)
bank_byte = ord(rom[address])
return bank_byte
def get_nth_map_header_pointer(map_id):
"returns the full pointer to the map header struct for this map"
assert_rom()
#figure out where the bytes for this pointer are located
byte1_address = start_map_header_pointers + (map_id * 2)
byte2_address = start_map_header_pointers + (map_id * 2) + 1
#grab the two bytes making up the partial pointer
byte1 = ord(rom[byte1_address])
byte2 = ord(rom[byte2_address])
#swap the bytes (16-bit pointers for z80 are little endian)
temp = byte1
byte1 = byte2
byte2 = temp
del temp
#combine these into a single pointer (0x byte1 byte2)
partial_pointer = (byte2 + (byte1 << 8))
#print hex(partial_pointer)
#get the bank id
bank = get_nth_map_header_pointer_bank_byte(map_id)
#calculate the full pointer
pointer = calculate_pointer(partial_pointer, bank)
#return it as an integer
return pointer
def load_map_pointers():
global maps
global map_pointers
for map in maps.keys():
pointer = get_nth_map_header_pointer(map)
#print maps[map] + "\t\t\t" + hex(pointer)
entry = {
"name": maps[map],
"address": hex(pointer),
"bank": hex(get_nth_map_header_pointer_bank_byte(map))
}
map_pointers[map] = entry
#print json.dumps(map_pointers)
def read_connection_bytes(connection_bytes, bank):
map_id = ord(connection_bytes[0])
#connection strip
connected_map_tile_pointer_byte1 = ord(connection_bytes[1])
connected_map_tile_pointer_byte2 = ord(connection_bytes[2])
connected_map_tile_pointer = (connected_map_tile_pointer_byte1 + (connected_map_tile_pointer_byte2 << 8))
#connection strip
current_map_tile_pointer_byte1 = ord(connection_bytes[3])
current_map_tile_pointer_byte2 = ord(connection_bytes[4])
current_map_tile_pointer = (current_map_tile_pointer_byte1 + (current_map_tile_pointer_byte2 << 8))
bigness_byte = ord(connection_bytes[5])
width_byte = ord(connection_bytes[6])
y = ord(connection_bytes[7])
x = ord(connection_bytes[8])
#window
window_pointer_byte1 = ord(connection_bytes[9])
window_pointer_byte2 = ord(connection_bytes[10])
window_pointer = (window_pointer_byte1 + (window_pointer_byte2 << 8))
connection_data = {
"map_id": map_id,
"connected_map_tile_pointer": hex(connected_map_tile_pointer),
"current_map_tile_pointer": hex(current_map_tile_pointer),
"bigness": hex(bigness_byte),
"width": hex(width_byte),
"y": y,
"x": x,
"window_pointer": hex(window_pointer),
}
return connection_data
def read_warp_data(address, warp_count):
warps = {}
for warp_id in range(0, warp_count):
offset = address + (warp_id*4) #4 bytes per warp
warp = {}
warp["y"] = ord(rom[offset])
warp["x"] = ord(rom[offset+1])
warp["warp_to_point"] = ord(rom[offset+2])
warp["warp_to_map_id"] = ord(rom[offset+3])
warps[warp_id] = warp
return warps
def read_sign_data(address, sign_count):
signs = {}
for sign_id in range(0, sign_count):
offset = address + (sign_id * 3)
sign = {}
sign["y"] = ord(rom[offset])
sign["x"] = ord(rom[offset+1])
sign["text_id"] = ord(rom[offset+2])
signs[sign_id] = sign
return signs
def read_warp_tos(address, warp_count):
warp_tos = {}
for warp_to_id in range(0, warp_count):
offset = address + (warp_to_id * 4)
warp_to = {}
warp_to["event_displacement"] = [ord(rom[offset]),ord(rom[offset+1])]
warp_to["y"] = ord(rom[offset+2])
warp_to["x"] = ord(rom[offset+3])
warp_tos[warp_to_id] = warp_to
return warp_tos
def get_object_data(address):
if type(address) == str: address = int(address, base)
output = {}
maps_border_tile = ord(rom[address])
number_of_warps = ord(rom[address+1])
if number_of_warps == 0: warps = {}
else:
warps = read_warp_data(address+2, number_of_warps)
offset = number_of_warps * 4
address = address + 2 + offset
number_of_signs = ord(rom[address])
if number_of_signs == 0: signs = {}
else:
signs = read_sign_data(address+1, number_of_signs)
offset = number_of_signs * 3
address = address + 1 + offset
number_of_things = ord(rom[address])
address = address + 1
things = {}
for thing_id in range(0, number_of_things):
thing = {}
picture_number = ord(rom[address])
y = ord(rom[address+1])
x = ord(rom[address+2])
movement1 = ord(rom[address+3])
movement2 = ord(rom[address+4])
text_string_number = ord(rom[address+5])
address += 5 + 1
if text_string_number & (1 << 6) != 0: #trainer
thing["type"] = "trainer"
thing["trainer_type"] = ord(rom[address])
thing["pokemon_set"] = ord(rom[address+1])
address += 2
elif text_string_number & (1 << 7) != 0: #item
thing["type"] = "item"
thing["item_number"] = ord(rom[address])
address += 1
else: #normal person
thing["type"] = "person"
thing["picture_number"] = picture_number
thing["y"] = y
thing["x"] = x
thing["movement1"] = movement1
thing["movement2"] = movement2
thing["original_text_string_number"] = text_string_number
thing["text_string_number"] = text_string_number & 0xF
things[thing_id] = thing
warp_tos = read_warp_tos(address, number_of_warps)
output["maps_border_tile"] = maps_border_tile
output["number_of_warps"] = number_of_warps
output["warps"] = warps
output["number_of_signs"] = number_of_signs
output["signs"] = signs
output["number_of_things"] = number_of_things
output["things"] = things
output["warp_tos"] = warp_tos
return output
def compute_object_data_size(object):
size = 4
size += 6 * (int(object["number_of_things"]))
trainer_count = 0
item_count = 0
for thing in object["things"]:
thing = object["things"][thing]
if thing["type"] == "trainer": trainer_count += 1
elif thing["type"] == "item": item_count += 1
size += 2 * trainer_count
size += item_count
size += 8 * object["number_of_warps"]
size += 3 * object["number_of_signs"]
return size
def get_direction(connection_byte, connection_id):
"""given a connection byte and a connection id, which direction is this connection?
the 0th connection of $5 is SOUTH and the 1st 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]
def read_map_header(address, bank):
address = int(address, base)
bank = int(bank, base)
tileset = ord(rom[address])
y = ord(rom[address+1])
x = ord(rom[address+2])
map_pointer_byte1 = ord(rom[address+3])
map_pointer_byte2 = ord(rom[address+4])
partial_map_pointer = (map_pointer_byte1 + (map_pointer_byte2 << 8))
map_pointer = calculate_pointer(partial_map_pointer, bank)
texts_pointer_byte1 = ord(rom[address+5])
texts_pointer_byte2 = ord(rom[address+6])
partial_texts_pointer = (texts_pointer_byte1 + (texts_pointer_byte2 << 8))
texts_pointer = calculate_pointer(partial_texts_pointer, bank)
script_pointer_byte1 = ord(rom[address+7])
script_pointer_byte2 = ord(rom[address+8])
partial_script_pointer = (script_pointer_byte1 + ( script_pointer_byte2 << 8))
script_pointer = calculate_pointer(partial_script_pointer, bank)
connection_byte = ord(rom[address+9]) #0xc = NORTH | SOUTH
# <&IIMarckus> the connection byte is a bitmask allowing 0-4 connections
# <&IIMarckus> each connection is 11 bytes
# <&IIMarckus> or'd
# <&IIMarckus> east = 1, west = 2, south = 4, north = 8
# <&IIMarckus> so a connection byte of 0xc means north/south
# <&IIMarckus> which means there are 22 more bytes, 11 for each connection
# < kanzure> 4 | 8 = c?
# <&IIMarckus> yes
# <&IIMarckus> easier to see if you convert to binary
# <&IIMarckus> 0100 | 1000 = 1100
num_connections = 0
connection_value = bin(connection_byte)[2:]
if connection_value[0] == "1": #NORTH
num_connections += 1
if len(connection_value) > 1 and connection_value[1] == "1": #SOUTH
num_connections += 1
if len(connection_value) > 2 and connection_value[2] == "1": #WEST
num_connections += 1
if len(connection_value) > 3 and connection_value[3] == "1": #EAST
num_connections += 1
#quick test for connection data
#connection0_stuff = rom[(address + 10):(address + 10 + 11)]
#print "Route: " + hex(ord(connection0_stuff[0]))
#setup
connections = {}
#go straight to object data if there are no connections
if num_connections > 0:
for connection in range(0, num_connections):
base_connection_address = address + 10 + (11 * connection)
connection_bytes = rom[base_connection_address : base_connection_address + 11]
connection_data = read_connection_bytes(connection_bytes, bank)
connection_data["direction"] = get_direction(connection_byte, connection)
connections[connection] = connection_data
#we might have to jump around a bit
offset = address + 10 + (11 * num_connections)
#object data
object_data_pointer_byte1 = ord(rom[offset])
object_data_pointer_byte2 = ord(rom[offset+1])
partial_object_data_pointer = (object_data_pointer_byte1 + (object_data_pointer_byte2 << 8))
object_data_pointer = calculate_pointer(partial_object_data_pointer, bank)
object_data = get_object_data(object_data_pointer)
texts = set()
for thing_id in object_data["things"].keys():
thing = object_data["things"][thing_id]
texts.add(thing["text_string_number"])
for sign_id in object_data["signs"].keys():
sign = object_data["signs"][sign_id]
texts.add(sign["text_id"])
texts = list(texts)
number_of_referenced_texts = len(texts)
map_header = {
"tileset": hex(tileset),
"y": hex(y),
"x": hex(x),
"map_pointer": hex(map_pointer),
"texts_pointer": hex(texts_pointer),
"number_of_referenced_texts": number_of_referenced_texts,
"referenced_texts": texts,
"script_pointer": hex(script_pointer),
"connection_byte": hex(connection_byte),
"num_connections": str(num_connections),
"connections": connections, #NORTH, SOUTH, WEST, EAST order matters
"object_data_pointer": hex(object_data_pointer),
"object_data": object_data,
}
return map_header
def read_all_map_headers():
if rom == None: load_rom()
assert_rom()
if len(map_pointers) == 0: load_map_pointers()
for map_id in map_pointers.keys():
if map_id in bad_maps: continue
map2 = map_pointers[map_id]
map_header = read_map_header(map2["address"], map2["bank"])
map_header["id"] = map_id
map_header["name"] = map2["name"]
map_header["address"] = map2["address"]
map_header["bank"] = map2["bank"]
map_headers[map_id] = map_header
return map_headers
if __name__ == "__main__":
#read binary data from file
load_rom()
#where are the map structs?
load_map_pointers()
#print json.dumps(map_pointers)
#experimental...
#print json.dumps(read_map_header(map_pointers[0]["address"], map_pointers[0]["bank"]))
read_all_map_headers()
#print json.dumps(map_headers)
#print map_headers[37]
for header in map_headers:
if header in bad_maps: continue
print "map " + str(header) + " has " + str(map_headers[header]["number_of_referenced_texts"]) + " referenced texts"

View File

@ -1,94 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-14
#split out blocksets into binary data files
# There's also code here to spit out asm for each blockset,
# but it's too many lines and will probably crash rgbasm.
import sys
import extract_maps
extract_maps.load_rom()
spacing = " "
tileblocks = {
"Tset00_Block": [0x645E0, 0x64DE0, ""],
"Tset01_Block": [0x65270, 0x653A0, ""],
"Tset02_Block": [0x693BF, 0x6960F, ""],
"Tset03_Block": [0x6A9FF, 0x6B1FF, ""],
"Tset05_Block": [0x6867F, 0x68DBF, ""],
"Tset08_Block": [0x65980, 0x65BB0, ""],
"Tset09_Block": [0x69BFF, 0x6A3FF, ""],
"Tset0B_Block": [0x6FEF0, 0x70000, ""],
"Tset0D_Block": [0x6E930, 0x6ED10, ""],
"Tset0E_Block": [0x66BF0, 0x66D60, ""],
"Tset0F_Block": [0x6C5C0, 0x6CCA0, ""],
"Tset10_Block": [0x67350, 0x676F0, ""],
"Tset13_Block": [0x66190, 0x66610, ""],
"Tset11_Block": [0x6D0C0, 0x6D8C0, ""],
"Tset12_Block": [0x6DEA0, 0x6E390, ""],
"Tset14_Block": [0x6F2D0, 0x6F670, ""],
"Tset15_Block": [0x6FB20, 0x6FD60, ""],
"Tset16_Block": [0x6B7FF, 0x6C000, ""],
"Tset17_Block": [0x67B50, 0x68000, ""],
}
#10:02 <+sawakita> each block is composed by 4x4 tiles
#10:03 <+sawakita> so you can see a blockset as a list of 16-bytes long arrays
#10:07 <+sawakita> 4 bytes for each row of the block, left-to-right, top-to-down
#10:08 <+sawakita> so first byte is top-left tile
#10:08 <+kanzure> top-left tile byte is a tile id?
#10:08 <+sawakita> exactly
#10:09 <+kanzure> what does a tile id reference
#10:10 <+sawakita> tile ID $00 is the first tile of the tileset's graphics (first 16-bytes, since gfx are 2bpp)
output = ""
for tileblock_id in tileblocks.keys():
tileblock = tileblocks[tileblock_id]
start_address = tileblock[0]
end_address = tileblock[1]
block_count = (end_address - start_address) / 16
main_data = extract_maps.rom[start_address:end_address]
blockset_id = int(tileblock_id[4:6], 16)
output = "; "
output += tileblock_id + " - " + str(block_count) + " blocks (" + hex(start_address) + " to " + hex(end_address) + ")\n"
#print tileblock_id + " has " + str(block_count) + " block tiles."
for block_id in range(0, block_count):
start_address2 = start_address + (16 * block_id)
end_address2 = start_address + (16 * block_id) + 16
data = extract_maps.rom[start_address2:end_address2]
output += spacing + "; block " + str(block_id + 1) + " on " + tileblock_id + "\n"
#output += spacing + spacing + "db $%.2x" % (ord(data[0]))
#for data_bit in range(1,15):
# output += ", $%.2x" % (ord(data[data_bit]))
#output += ", $%.2x" % (ord(data[15]))
#output += "\n"
for row_id in range(0,4):
subdata = data[row_id * 4:row_id * 4 + 4]
subdata2 = [ord(subdata[0]), ord(subdata[1]), ord(subdata[2]), ord(subdata[3])]
output += spacing + spacing + " ; row " + str(row_id + 1) + "\n"
output += spacing + spacing + spacing + "db $%.2x, $%.2x, $%.2x, $%.2x\n" % (subdata2[0], subdata2[1], subdata2[2], subdata2[3])
fh = open("../gfx/blocksets/%.2x.bst" % (blockset_id), "w")
fh.write(main_data)
fh.close()
print output
"""
Tset00_Block:
; block 1
; row 1
db 0, 0, 0, 0
; row 2
db 0, 0, 0, 0
; row 3
db 0, 0, 0, 0
; row 4
db 0, 0, 0, 0
"""

View File

@ -1,37 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-14
#throw tilesets into separate files
import extract_maps
extract_maps.load_rom()
locations = {
"Tset00_GFX": [0x64000, 0x645E0, "00"],
"Tset01_GFX": [0x64DE0, 0x65270, "01"],
"Tset08_GFX": [0x653A0, 0x65980, "08"],
"Tset13_GFX": [0x65BB0, 0x66190, "13"],
"Tset0E_GFX": [0x66610, 0x66BF0, "0e"],
"Tset10_GFX": [0x66D60, 0x67350, "10"],
"Tset17_GFX": [0x676F0, 0x67B50, "17"],
"Tset05_GFX": [0x6807F, 0x6867F, "05"],
"Tset02_GFX": [0x68DBF, 0x693BF, "02"],
"Tset09_GFX": [0x6960F, 0x69BFF, "09"],
"Tset03_GFX": [0x6A3FF, 0x6A9FF, "03"],
"Tset16_GFX": [0x6B1FF, 0x6B7FF, "16"],
"Tset0F_GFX": [0x6C000, 0x6C5C0, "0f"],
"Tset11_GFX": [0x6CCA0, 0x6D0C0, "11"],
"Tset12_GFX": [0x6D8C0, 0x6DEA0, "12"],
"Tset0D_GFX": [0x6E390, 0x6E930, "0d"],
"Tset14_GFX": [0x6ED10, 0x6F2D0, "14"],
"Tset15_GFX": [0x6F670, 0x6FB20, "15"],
"Tset0B_GFX": [0x6FD60, 0x6FEF0, "0b"],
}
for tileset_id in locations.keys():
tileset = locations[tileset_id]
print "writing ../gfx/tilesets/" + tileset[2] + ".2bpp"
fh = open("../gfx/tilesets/" + tileset[2] + ".2bpp", "w")
fh.write(extract_maps.rom[tileset[0]:tileset[1]])
fh.close()
print "Done."

View File

@ -1,82 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-27
#fix trainer header labels to not suck so much
import analyze_incbins
def replace_trainer_header_labels(debug=False):
"""trainer header labels could be better"""
asm = analyze_incbins.asm
if debug: print str(type(asm))
single_asm = "\n".join(asm)
current_map_name = "asdjkl;"
line_id = 0
trainer_header_counter = 1
for line in asm:
trainer_header_base = current_map_name + "TrainerHeader"
trainer_header_name = trainer_header_base + str(trainer_header_counter)
#we've found a TrainerHeaders thing
if "TrainerHeaders:" in line:
current_map_name = line.split("TrainerHeaders")[0]
if line[0:len(current_map_name)] == current_map_name:
trainer_header_counter = 1
#replace a trainer header label
elif "TrainerHeader_" in line and line[0:14] == "TrainerHeader_":
temp = line.split("TrainerHeader_")[1]
temp = temp.split(": ;")[0]
old_label = "TrainerHeader_" + temp
new_label = current_map_name + "TH" + str(trainer_header_counter) #trainer_header_name
single_asm = single_asm.replace(old_label + ":", new_label + ":")
single_asm = single_asm.replace(old_label + "\n", new_label + "\n")
if debug: print "old_label = " + old_label
if debug: print "new_label = " + new_label
trainer_header_counter += 1
elif trainer_header_base in line and line[0:len(trainer_header_base)] == trainer_header_base and (line[len(trainer_header_base)+1:len(trainer_header_base)+2] == ":" or line[len(trainer_header_base)+2:len(trainer_header_base)+3] == ":"):
if line[len(trainer_header_base)+1:len(trainer_header_base)+2] == ":":
trainer_header_counter = int(line[len(trainer_header_base):len(trainer_header_base)+1])
elif line[len(trainer_header_base)+2:len(trainer_header_base)+3] == ":":
trainer_header_counter = int(line[len(trainer_header_base):len(trainer_header_base)+2])
trainer_header_name = trainer_header_base + str(trainer_header_counter)
#replace a text label
elif " TextBeforeBattle" in line and not current_map_name in line:
old_label = line.split("dw ")[1].split(" ;")[0]
new_label = current_map_name + "B4BattleTxt" + str(trainer_header_counter) #trainer_header_name + "BeforeBattleText"
single_asm = single_asm.replace(old_label + ":", new_label + ":")
single_asm = single_asm.replace(old_label + "\n", new_label + "\n")
single_asm = single_asm.replace(old_label + " ;", new_label + " ;")
if debug: print "old_label = " + old_label
if debug: print "new_label = " + new_label
#replace a text label
elif " TextAfterBattle" in line and not current_map_name in line:
old_label = line.split("dw ")[1].split(" ;")[0]
new_label = current_map_name + "AfterBattleTxt" + str(trainer_header_counter) #trainer_header_name + "AfterBattleText"
single_asm = single_asm.replace(old_label + ":", new_label + ":")
single_asm = single_asm.replace(old_label + "\n", new_label + "\n")
single_asm = single_asm.replace(old_label + " ;", new_label + " ;")
if debug: print "old_label = " + old_label
if debug: print "new_label = " + new_label
#replace a text label
elif " TextEndBattle" in line and not current_map_name in line:
old_label = line.split("dw ")[1].split(" ;")[0]
new_label = current_map_name + "EndBattleTxt" + str(trainer_header_counter) #trainer_header_name + "EndBattleText"
single_asm = single_asm.replace(old_label + ":", new_label + ":")
single_asm = single_asm.replace(old_label + "\n", new_label + "\n")
single_asm = single_asm.replace(old_label + " ;", new_label + " ;")
if debug: print "old_label = " + old_label
if debug: print "new_label = " + new_label
line_id += 1
print single_asm
if __name__ == "__main__":
analyze_incbins.load_asm()
replace_trainer_header_labels()

View File

@ -1,853 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-09
import extract_maps
import os
import json
from copy import copy, deepcopy
from pretty_map_headers import random_hash, map_name_cleaner
from ctypes import c_int8
import sys
spacing = " "
temp_opt_table = [
[ "ADC A", 0x8f, 0 ],
[ "ADC B", 0x88, 0 ],
[ "ADC C", 0x89, 0 ],
[ "ADC D", 0x8a, 0 ],
[ "ADC E", 0x8b, 0 ],
[ "ADC H", 0x8c, 0 ],
[ "ADC [HL]", 0x8e, 0 ],
[ "ADC L", 0x8d, 0 ],
[ "ADC x", 0xce, 1 ],
[ "ADD A", 0x87, 0 ],
[ "ADD B", 0x80, 0 ],
[ "ADD C", 0x81, 0 ],
[ "ADD D", 0x82, 0 ],
[ "ADD E", 0x83, 0 ],
[ "ADD H", 0x84, 0 ],
[ "ADD [HL]", 0x86, 0 ],
[ "ADD HL, BC", 0x9, 0 ],
[ "ADD HL, DE", 0x19, 0 ],
[ "ADD HL, HL", 0x29, 0 ],
[ "ADD HL, SP", 0x39, 0 ],
[ "ADD L", 0x85, 0 ],
[ "ADD SP, x", 0xe8, 1 ],
[ "ADD x", 0xc6, 1 ],
[ "AND A", 0xa7, 0 ],
[ "AND B", 0xa0, 0 ],
[ "AND C", 0xa1, 0 ],
[ "AND D", 0xa2, 0 ],
[ "AND E", 0xa3, 0 ],
[ "AND H", 0xa4, 0 ],
[ "AND [HL]", 0xa6, 0 ],
[ "AND L", 0xa5, 0 ],
[ "AND x", 0xe6, 1 ],
[ "BIT 0, A", 0x47cb, 3 ],
[ "BIT 0, B", 0x40cb, 3 ],
[ "BIT 0, C", 0x41cb, 3 ],
[ "BIT 0, D", 0x42cb, 3 ],
[ "BIT 0, E", 0x43cb, 3 ],
[ "BIT 0, H", 0x44cb, 3 ],
[ "BIT 0, [HL]", 0x46cb, 3 ],
[ "BIT 0, L", 0x45cb, 3 ],
[ "BIT 1, A", 0x4fcb, 3 ],
[ "BIT 1, B", 0x48cb, 3 ],
[ "BIT 1, C", 0x49cb, 3 ],
[ "BIT 1, D", 0x4acb, 3 ],
[ "BIT 1, E", 0x4bcb, 3 ],
[ "BIT 1, H", 0x4ccb, 3 ],
[ "BIT 1, [HL]", 0x4ecb, 3 ],
[ "BIT 1, L", 0x4dcb, 3 ],
[ "BIT 2, A", 0x57cb, 3 ],
[ "BIT 2, B", 0x50cb, 3 ],
[ "BIT 2, C", 0x51cb, 3 ],
[ "BIT 2, D", 0x52cb, 3 ],
[ "BIT 2, E", 0x53cb, 3 ],
[ "BIT 2, H", 0x54cb, 3 ],
[ "BIT 2, [HL]", 0x56cb, 3 ],
[ "BIT 2, L", 0x55cb, 3 ],
[ "BIT 3, A", 0x5fcb, 3 ],
[ "BIT 3, B", 0x58cb, 3 ],
[ "BIT 3, C", 0x59cb, 3 ],
[ "BIT 3, D", 0x5acb, 3 ],
[ "BIT 3, E", 0x5bcb, 3 ],
[ "BIT 3, H", 0x5ccb, 3 ],
[ "BIT 3, [HL]", 0x5ecb, 3 ],
[ "BIT 3, L", 0x5dcb, 3 ],
[ "BIT 4, A", 0x67cb, 3 ],
[ "BIT 4, B", 0x60cb, 3 ],
[ "BIT 4, C", 0x61cb, 3 ],
[ "BIT 4, D", 0x62cb, 3 ],
[ "BIT 4, E", 0x63cb, 3 ],
[ "BIT 4, H", 0x64cb, 3 ],
[ "BIT 4, [HL]", 0x66cb, 3 ],
[ "BIT 4, L", 0x65cb, 3 ],
[ "BIT 5, A", 0x6fcb, 3 ],
[ "BIT 5, B", 0x68cb, 3 ],
[ "BIT 5, C", 0x69cb, 3 ],
[ "BIT 5, D", 0x6acb, 3 ],
[ "BIT 5, E", 0x6bcb, 3 ],
[ "BIT 5, H", 0x6ccb, 3 ],
[ "BIT 5, [HL]", 0x6ecb, 3 ],
[ "BIT 5, L", 0x6dcb, 3 ],
[ "BIT 6, A", 0x77cb, 3 ],
[ "BIT 6, B", 0x70cb, 3 ],
[ "BIT 6, C", 0x71cb, 3 ],
[ "BIT 6, D", 0x72cb, 3 ],
[ "BIT 6, E", 0x73cb, 3 ],
[ "BIT 6, H", 0x74cb, 3 ],
[ "BIT 6, [HL]", 0x76cb, 3 ],
[ "BIT 6, L", 0x75cb, 3 ],
[ "BIT 7, A", 0x7fcb, 3 ],
[ "BIT 7, B", 0x78cb, 3 ],
[ "BIT 7, C", 0x79cb, 3 ],
[ "BIT 7, D", 0x7acb, 3 ],
[ "BIT 7, E", 0x7bcb, 3 ],
[ "BIT 7, H", 0x7ccb, 3 ],
[ "BIT 7, [HL]", 0x7ecb, 3 ],
[ "BIT 7, L", 0x7dcb, 3 ],
[ "CALL C, ?", 0xdc, 2 ],
[ "CALL NC, ?", 0xd4, 2 ],
[ "CALL NZ, ?", 0xc4, 2 ],
[ "CALL Z, ?", 0xcc, 2 ],
[ "CALL ?", 0xcd, 2 ],
[ "CCF", 0x3f, 0 ],
[ "CP A", 0xbf, 0 ],
[ "CP B", 0xb8, 0 ],
[ "CP C", 0xb9, 0 ],
[ "CP D", 0xba, 0 ],
[ "CP E", 0xbb, 0 ],
[ "CP H", 0xbc, 0 ],
[ "CP [HL]", 0xbe, 0 ],
[ "CPL", 0x2f, 0 ],
[ "CP L", 0xbd, 0 ],
[ "CP x", 0xfe, 1 ],
[ "DAA", 0x27, 0 ],
[ "DEBUG", 0xed, 0 ],
[ "DEC A", 0x3d, 0 ],
[ "DEC B", 0x5, 0 ],
[ "DEC BC", 0xb, 0 ],
[ "DEC C", 0xd, 0 ],
[ "DEC D", 0x15, 0 ],
[ "DEC DE", 0x1b, 0 ],
[ "DEC E", 0x1d, 0 ],
[ "DEC H", 0x25, 0 ],
[ "DEC HL", 0x2b, 0 ],
[ "DEC [HL]", 0x35, 0 ],
[ "DEC L", 0x2d, 0 ],
[ "DEC SP", 0x3b, 0 ],
[ "DI", 0xf3, 0 ],
[ "EI", 0xfb, 0 ],
[ "HALT", 0x76, 0 ],
[ "INC A", 0x3c, 0 ],
[ "INC B", 0x4, 0 ],
[ "INC BC", 0x3, 0 ],
[ "INC C", 0xc, 0 ],
[ "INC D", 0x14, 0 ],
[ "INC DE", 0x13, 0 ],
[ "INC E", 0x1c, 0 ],
[ "INC H", 0x24, 0 ],
[ "INC HL", 0x23, 0 ],
[ "INC [HL]", 0x34, 0 ],
[ "INC L", 0x2c, 0 ],
[ "INC SP", 0x33, 0 ],
[ "JP C, ?", 0xda, 2 ],
[ "JP HL", 0xe9, 0 ],
[ "JP NC, ?", 0xd2, 2 ],
[ "JP NZ, ?", 0xc2, 2 ],
[ "JP Z, ?", 0xca, 2 ],
[ "JP ?", 0xc3, 2 ],
[ "JR C, x", 0x38, 1 ],
[ "JR NC, x", 0x30, 1 ],
[ "JR NZ, x", 0x20, 1 ],
[ "JR Z, x", 0x28, 1 ],
[ "JR x", 0x18, 1 ],
[ "LD A, A", 0x7f, 0 ],
[ "LD A, B", 0x78, 0 ],
[ "LD A, C", 0x79, 0 ],
[ "LD A, D", 0x7a, 0 ],
[ "LD A, E", 0x7b, 0 ],
[ "LD A, H", 0x7c, 0 ],
[ "LD A, L", 0x7d, 0 ],
[ "LD A, [$FF00+C]", 0xf2, 0 ],
[ "LD A, [$FF00+x]", 0xf0, 1 ],
# [ "LDH A, [x]", 0xf0, 1 ], #rgbds has trouble with this one?
[ "LD A, [BC]", 0xa, 0 ],
[ "LD A, [DE]", 0x1a, 0 ],
# [ "LD A, [HL+]", 0x2a, 0 ],
# [ "LD A, [HL-]", 0x3a, 0 ],
[ "LD A, [HL]", 0x7e, 0 ],
[ "LD A, [HLD]", 0x3a, 0 ],
[ "LD A, [HLI]", 0x2a, 0 ],
[ "LD A, [?]", 0xfa, 2 ],
[ "LD A, x", 0x3e, 1 ],
[ "LD B, A", 0x47, 0 ],
[ "LD B, B", 0x40, 0 ],
[ "LD B, C", 0x41, 0 ],
[ "LD [BC], A", 0x2, 0 ],
[ "LD B, D", 0x42, 0 ],
[ "LD B, E", 0x43, 0 ],
[ "LD B, H", 0x44, 0 ],
[ "LD B, [HL]", 0x46, 0 ],
[ "LD B, L", 0x45, 0 ],
[ "LD B, x", 0x6, 1 ],
[ "LD C, A", 0x4f, 0 ],
[ "LD C, B", 0x48, 0 ],
[ "LD C, C", 0x49, 0 ],
[ "LD C, D", 0x4a, 0 ],
[ "LD C, E", 0x4b, 0 ],
[ "LD C, H", 0x4c, 0 ],
[ "LD C, [HL]", 0x4e, 0 ],
[ "LD C, L", 0x4d, 0 ],
[ "LD C, x", 0xe, 1 ],
[ "LD D, A", 0x57, 0 ],
# [ "LDD A, [HL]", 0x3a, 0 ],
[ "LD D, B", 0x50, 0 ],
[ "LD D, C", 0x51, 0 ],
[ "LD D, D", 0x52, 0 ],
[ "LD D, E", 0x53, 0 ],
[ "LD [DE], A", 0x12, 0 ],
[ "LD D, H", 0x54, 0 ],
[ "LD D, [HL]", 0x56, 0 ],
# [ "LDD [HL], A", 0x32, 0 ],
[ "LD D, L", 0x55, 0 ],
[ "LD D, x", 0x16, 1 ],
[ "LD E, A", 0x5f, 0 ],
[ "LD E, B", 0x58, 0 ],
[ "LD E, C", 0x59, 0 ],
[ "LD E, D", 0x5a, 0 ],
[ "LD E, E", 0x5b, 0 ],
[ "LD E, H", 0x5c, 0 ],
[ "LD E, [HL]", 0x5e, 0 ],
[ "LD E, L", 0x5d, 0 ],
[ "LD E, x", 0x1e, 1 ],
[ "LD [$FF00+C], A", 0xe2, 0 ],
[ "LD [$FF00+x], A", 0xe0, 1 ],
# [ "LDH [x], A", 0xe0, 1 ],
[ "LD H, A", 0x67, 0 ],
[ "LD H, B", 0x60, 0 ],
[ "LD H, C", 0x61, 0 ],
[ "LD H, D", 0x62, 0 ],
[ "LD H, E", 0x63, 0 ],
[ "LD H, H", 0x64, 0 ],
[ "LD H, [HL]", 0x66, 0 ],
[ "LD H, L", 0x65, 0 ],
# [ "LD [HL+], A", 0x22, 0 ],
# [ "LD [HL-], A", 0x32, 0 ],
[ "LD [HL], A", 0x77, 0 ],
[ "LD [HL], B", 0x70, 0 ],
[ "LD [HL], C", 0x71, 0 ],
[ "LD [HL], D", 0x72, 0 ],
[ "LD [HLD], A", 0x32, 0 ],
[ "LD [HL], E", 0x73, 0 ],
[ "LD [HL], H", 0x74, 0 ],
[ "LD [HLI], A", 0x22, 0 ],
[ "LD [HL], L", 0x75, 0 ],
[ "LD HL, SP+x", 0xf8, 1 ],
[ "LD [HL], x", 0x36, 1 ],
[ "LD H, x", 0x26, 1 ],
# [ "LDI A, [HL]", 0x2a, 0 ],
# [ "LDI [HL], A", 0x22, 0 ],
[ "LD L, A", 0x6f, 0 ],
[ "LD L, B", 0x68, 0 ],
[ "LD L, C", 0x69, 0 ],
[ "LD L, D", 0x6a, 0 ],
[ "LD L, E", 0x6b, 0 ],
[ "LD L, H", 0x6c, 0 ],
[ "LD L, [HL]", 0x6e, 0 ],
[ "LD L, L", 0x6d, 0 ],
[ "LD L, x", 0x2e, 1 ],
# [ "LD PC, HL", 0xe9, 0 ], #prefer jp [hl]
[ "LD SP, HL", 0xf9, 0 ],
[ "LD BC, ?", 0x1, 2 ],
[ "LD DE, ?", 0x11, 2 ],
[ "LD HL, ?", 0x21, 2 ],
[ "LD SP, ?", 0x31, 2 ],
# [ "LD [?], SP", 0x8, 2 ],
[ "LD [?], A", 0xea, 2 ],
[ "NOP", 0x0, 0 ],
[ "OR A", 0xb7, 0 ],
[ "OR B", 0xb0, 0 ],
[ "OR C", 0xb1, 0 ],
[ "OR D", 0xb2, 0 ],
[ "OR E", 0xb3, 0 ],
[ "OR H", 0xb4, 0 ],
[ "OR [HL]", 0xb6, 0 ],
[ "OR L", 0xb5, 0 ],
[ "OR x", 0xf6, 1 ],
[ "POP AF", 0xf1, 0 ],
[ "POP BC", 0xc1, 0 ],
[ "POP DE", 0xd1, 0 ],
[ "POP HL", 0xe1, 0 ],
[ "PUSH AF", 0xf5, 0 ],
[ "PUSH BC", 0xc5, 0 ],
[ "PUSH DE", 0xd5, 0 ],
[ "PUSH HL", 0xe5, 0 ],
[ "RES 0, A", 0x87cb, 3 ],
[ "RES 0, B", 0x80cb, 3 ],
[ "RES 0, C", 0x81cb, 3 ],
[ "RES 0, D", 0x82cb, 3 ],
[ "RES 0, E", 0x83cb, 3 ],
[ "RES 0, H", 0x84cb, 3 ],
[ "RES 0, [HL]", 0x86cb, 3 ],
[ "RES 0, L", 0x85cb, 3 ],
[ "RES 1, A", 0x8fcb, 3 ],
[ "RES 1, B", 0x88cb, 3 ],
[ "RES 1, C", 0x89cb, 3 ],
[ "RES 1, D", 0x8acb, 3 ],
[ "RES 1, E", 0x8bcb, 3 ],
[ "RES 1, H", 0x8ccb, 3 ],
[ "RES 1, [HL]", 0x8ecb, 3 ],
[ "RES 1, L", 0x8dcb, 3 ],
[ "RES 2, A", 0x97cb, 3 ],
[ "RES 2, B", 0x90cb, 3 ],
[ "RES 2, C", 0x91cb, 3 ],
[ "RES 2, D", 0x92cb, 3 ],
[ "RES 2, E", 0x93cb, 3 ],
[ "RES 2, H", 0x94cb, 3 ],
[ "RES 2, [HL]", 0x96cb, 3 ],
[ "RES 2, L", 0x95cb, 3 ],
[ "RES 3, A", 0x9fcb, 3 ],
[ "RES 3, B", 0x98cb, 3 ],
[ "RES 3, C", 0x99cb, 3 ],
[ "RES 3, D", 0x9acb, 3 ],
[ "RES 3, E", 0x9bcb, 3 ],
[ "RES 3, H", 0x9ccb, 3 ],
[ "RES 3, [HL]", 0x9ecb, 3 ],
[ "RES 3, L", 0x9dcb, 3 ],
[ "RES 4, A", 0xa7cb, 3 ],
[ "RES 4, B", 0xa0cb, 3 ],
[ "RES 4, C", 0xa1cb, 3 ],
[ "RES 4, D", 0xa2cb, 3 ],
[ "RES 4, E", 0xa3cb, 3 ],
[ "RES 4, H", 0xa4cb, 3 ],
[ "RES 4, [HL]", 0xa6cb, 3 ],
[ "RES 4, L", 0xa5cb, 3 ],
[ "RES 5, A", 0xafcb, 3 ],
[ "RES 5, B", 0xa8cb, 3 ],
[ "RES 5, C", 0xa9cb, 3 ],
[ "RES 5, D", 0xaacb, 3 ],
[ "RES 5, E", 0xabcb, 3 ],
[ "RES 5, H", 0xaccb, 3 ],
[ "RES 5, [HL]", 0xaecb, 3 ],
[ "RES 5, L", 0xadcb, 3 ],
[ "RES 6, A", 0xb7cb, 3 ],
[ "RES 6, B", 0xb0cb, 3 ],
[ "RES 6, C", 0xb1cb, 3 ],
[ "RES 6, D", 0xb2cb, 3 ],
[ "RES 6, E", 0xb3cb, 3 ],
[ "RES 6, H", 0xb4cb, 3 ],
[ "RES 6, [HL]", 0xb6cb, 3 ],
[ "RES 6, L", 0xb5cb, 3 ],
[ "RES 7, A", 0xbfcb, 3 ],
[ "RES 7, B", 0xb8cb, 3 ],
[ "RES 7, C", 0xb9cb, 3 ],
[ "RES 7, D", 0xbacb, 3 ],
[ "RES 7, E", 0xbbcb, 3 ],
[ "RES 7, H", 0xbccb, 3 ],
[ "RES 7, [HL]", 0xbecb, 3 ],
[ "RES 7, L", 0xbdcb, 3 ],
[ "RETI", 0xd9, 0 ],
[ "RET C", 0xd8, 0 ],
[ "RET NC", 0xd0, 0 ],
[ "RET NZ", 0xc0, 0 ],
[ "RET Z", 0xc8, 0 ],
[ "RET", 0xc9, 0 ],
[ "RLA", 0x17, 0 ],
[ "RL A", 0x17cb, 3 ],
[ "RL B", 0x10cb, 3 ],
[ "RL C", 0x11cb, 3 ],
[ "RLCA", 0x7, 0 ],
[ "RLC A", 0x7cb, 3 ],
[ "RLC B", 0xcb, 3 ],
[ "RLC C", 0x1cb, 3 ],
[ "RLC D", 0x2cb, 3 ],
[ "RLC E", 0x3cb, 3 ],
[ "RLC H", 0x4cb, 3 ],
[ "RLC [HL]", 0x6cb, 3 ],
[ "RLC L", 0x5cb, 3 ],
[ "RL D", 0x12cb, 3 ],
[ "RL E", 0x13cb, 3 ],
[ "RL H", 0x14cb, 3 ],
[ "RL [HL]", 0x16cb, 3 ],
[ "RL L", 0x15cb, 3 ],
[ "RRA", 0x1f, 0 ],
[ "RR A", 0x1fcb, 3 ],
[ "RR B", 0x18cb, 3 ],
[ "RR C", 0x19cb, 3 ],
[ "RRCA", 0xf, 0 ],
[ "RRC A", 0xfcb, 3 ],
[ "RRC B", 0x8cb, 3 ],
[ "RRC C", 0x9cb, 3 ],
[ "RRC D", 0xacb, 3 ],
[ "RRC E", 0xbcb, 3 ],
[ "RRC H", 0xccb, 3 ],
[ "RRC [HL]", 0xecb, 3 ],
[ "RRC L", 0xdcb, 3 ],
[ "RR D", 0x1acb, 3 ],
[ "RR E", 0x1bcb, 3 ],
[ "RR H", 0x1ccb, 3 ],
[ "RR [HL]", 0x1ecb, 3 ],
[ "RR L", 0x1dcb, 3 ],
[ "RST $0", 0xc7, 0 ],
[ "RST $10", 0xd7, 0 ],
[ "RST $18", 0xdf, 0 ],
[ "RST $20", 0xe7, 0 ],
[ "RST $28", 0xef, 0 ],
[ "RST $30", 0xf7, 0 ],
[ "RST $38", 0xff, 0 ],
[ "RST $8", 0xcf, 0 ],
[ "SBC A", 0x9f, 0 ],
[ "SBC B", 0x98, 0 ],
[ "SBC C", 0x99, 0 ],
[ "SBC D", 0x9a, 0 ],
[ "SBC E", 0x9b, 0 ],
[ "SBC H", 0x9c, 0 ],
[ "SBC [HL]", 0x9e, 0 ],
[ "SBC L", 0x9d, 0 ],
[ "SBC x", 0xde, 1 ],
[ "SCF", 0x37, 0 ],
[ "SET 0, A", 0xc7cb, 3 ],
[ "SET 0, B", 0xc0cb, 3 ],
[ "SET 0, C", 0xc1cb, 3 ],
[ "SET 0, D", 0xc2cb, 3 ],
[ "SET 0, E", 0xc3cb, 3 ],
[ "SET 0, H", 0xc4cb, 3 ],
[ "SET 0, [HL]", 0xc6cb, 3 ],
[ "SET 0, L", 0xc5cb, 3 ],
[ "SET 1, A", 0xcfcb, 3 ],
[ "SET 1, B", 0xc8cb, 3 ],
[ "SET 1, C", 0xc9cb, 3 ],
[ "SET 1, D", 0xcacb, 3 ],
[ "SET 1, E", 0xcbcb, 3 ],
[ "SET 1, H", 0xcccb, 3 ],
[ "SET 1, [HL]", 0xcecb, 3 ],
[ "SET 1, L", 0xcdcb, 3 ],
[ "SET 2, A", 0xd7cb, 3 ],
[ "SET 2, B", 0xd0cb, 3 ],
[ "SET 2, C", 0xd1cb, 3 ],
[ "SET 2, D", 0xd2cb, 3 ],
[ "SET 2, E", 0xd3cb, 3 ],
[ "SET 2, H", 0xd4cb, 3 ],
[ "SET 2, [HL]", 0xd6cb, 3 ],
[ "SET 2, L", 0xd5cb, 3 ],
[ "SET 3, A", 0xdfcb, 3 ],
[ "SET 3, B", 0xd8cb, 3 ],
[ "SET 3, C", 0xd9cb, 3 ],
[ "SET 3, D", 0xdacb, 3 ],
[ "SET 3, E", 0xdbcb, 3 ],
[ "SET 3, H", 0xdccb, 3 ],
[ "SET 3, [HL]", 0xdecb, 3 ],
[ "SET 3, L", 0xddcb, 3 ],
[ "SET 4, A", 0xe7cb, 3 ],
[ "SET 4, B", 0xe0cb, 3 ],
[ "SET 4, C", 0xe1cb, 3 ],
[ "SET 4, D", 0xe2cb, 3 ],
[ "SET 4, E", 0xe3cb, 3 ],
[ "SET 4, H", 0xe4cb, 3 ],
[ "SET 4, [HL]", 0xe6cb, 3 ],
[ "SET 4, L", 0xe5cb, 3 ],
[ "SET 5, A", 0xefcb, 3 ],
[ "SET 5, B", 0xe8cb, 3 ],
[ "SET 5, C", 0xe9cb, 3 ],
[ "SET 5, D", 0xeacb, 3 ],
[ "SET 5, E", 0xebcb, 3 ],
[ "SET 5, H", 0xeccb, 3 ],
[ "SET 5, [HL]", 0xeecb, 3 ],
[ "SET 5, L", 0xedcb, 3 ],
[ "SET 6, A", 0xf7cb, 3 ],
[ "SET 6, B", 0xf0cb, 3 ],
[ "SET 6, C", 0xf1cb, 3 ],
[ "SET 6, D", 0xf2cb, 3 ],
[ "SET 6, E", 0xf3cb, 3 ],
[ "SET 6, H", 0xf4cb, 3 ],
[ "SET 6, [HL]", 0xf6cb, 3 ],
[ "SET 6, L", 0xf5cb, 3 ],
[ "SET 7, A", 0xffcb, 3 ],
[ "SET 7, B", 0xf8cb, 3 ],
[ "SET 7, C", 0xf9cb, 3 ],
[ "SET 7, D", 0xfacb, 3 ],
[ "SET 7, E", 0xfbcb, 3 ],
[ "SET 7, H", 0xfccb, 3 ],
[ "SET 7, [HL]", 0xfecb, 3 ],
[ "SET 7, L", 0xfdcb, 3 ],
[ "SLA A", 0x27cb, 3 ],
[ "SLA B", 0x20cb, 3 ],
[ "SLA C", 0x21cb, 3 ],
[ "SLA D", 0x22cb, 3 ],
[ "SLA E", 0x23cb, 3 ],
[ "SLA H", 0x24cb, 3 ],
[ "SLA [HL]", 0x26cb, 3 ],
[ "SLA L", 0x25cb, 3 ],
[ "SRA A", 0x2fcb, 3 ],
[ "SRA B", 0x28cb, 3 ],
[ "SRA C", 0x29cb, 3 ],
[ "SRA D", 0x2acb, 3 ],
[ "SRA E", 0x2bcb, 3 ],
[ "SRA H", 0x2ccb, 3 ],
[ "SRA [HL]", 0x2ecb, 3 ],
[ "SRA L", 0x2dcb, 3 ],
[ "SRL A", 0x3fcb, 3 ],
[ "SRL B", 0x38cb, 3 ],
[ "SRL C", 0x39cb, 3 ],
[ "SRL D", 0x3acb, 3 ],
[ "SRL E", 0x3bcb, 3 ],
[ "SRL H", 0x3ccb, 3 ],
[ "SRL [HL]", 0x3ecb, 3 ],
[ "SRL L", 0x3dcb, 3 ],
[ "STOP", 0x10, 0 ],
[ "SUB A", 0x97, 0 ],
[ "SUB B", 0x90, 0 ],
[ "SUB C", 0x91, 0 ],
[ "SUB D", 0x92, 0 ],
[ "SUB E", 0x93, 0 ],
[ "SUB H", 0x94, 0 ],
[ "SUB [HL]", 0x96, 0 ],
[ "SUB L", 0x95, 0 ],
[ "SUB x", 0xd6, 1 ],
[ "SWAP A", 0x37cb, 3 ],
[ "SWAP B", 0x30cb, 3 ],
[ "SWAP C", 0x31cb, 3 ],
[ "SWAP D", 0x32cb, 3 ],
[ "SWAP E", 0x33cb, 3 ],
[ "SWAP H", 0x34cb, 3 ],
[ "SWAP [HL]", 0x36cb, 3 ],
[ "SWAP L", 0x35cb, 3 ],
[ "XOR A", 0xaf, 0 ],
[ "XOR B", 0xa8, 0 ],
[ "XOR C", 0xa9, 0 ],
[ "XOR D", 0xaa, 0 ],
[ "XOR E", 0xab, 0 ],
[ "XOR H", 0xac, 0 ],
[ "XOR [HL]", 0xae, 0 ],
[ "XOR L", 0xad, 0 ],
[ "XOR x", 0xee, 1 ],
[ "E", 0x100, -1 ],
]
#find conflicts
conflict_table = {}
for line in temp_opt_table:
if line[1] in conflict_table.keys():
print "CONFLICT: " + line[0] + " ($" + hex(line[1])[2:] + ") .... " + conflict_table[line[1]]
else:
conflict_table[line[1]] = line[0]
#construct real opt_table
opt_table = {}
for line in temp_opt_table:
opt_table[line[1]] = [line[0], line[2]]
del temp_opt_table
del line
end_08_scripts_with = [
0xe9, #jp hl
#0xc3, #jp
##0x18, #jr
0xc9, #ret
###0xda, 0xe9, 0xd2, 0xc2, 0xca, 0xc3, 0x38, 0x30, 0x20, 0x28, 0x18, 0xd8, 0xd0, 0xc0, 0xc8, 0xc9
]
relative_jumps = [0x38, 0x30, 0x20, 0x28, 0x18, 0xc3, 0xda, 0xc2]
relative_unconditional_jumps = [0xc3, 0x18]
#TODO: replace call and a pointer with call and a label
call_commands = [0xdc, 0xd4, 0xc4, 0xcc, 0xcd]
all_labels = {}
def load_labels(filename="labels.json"):
global all_labels
if os.path.exists(filename):
all_labels = json.loads(open(filename, "r").read())
else:
print "You must run analyze_incbins.scan_for_predefined_labels() to create \"labels.json\". Trying..."
import analyze_incbins
analyze_incbins.scan_for_predefined_labels()
load_labels()
def find_label(local_address, bank_id=0):
global all_labels
#keep an integer
if type(local_address) == str:
local_address1 = int(local_address.replace("$", "0x"), 16)
else: local_address1 = local_address
#turn local_address into a string
if type(local_address) == str:
if "0x" in local_address: local_address = local_address.replace("0x", "$")
elif not "$" in local_address: local_address = "$" + local_address
if type(local_address) == int:
local_address = "$%.x" % (local_address)
local_address = local_address.upper()
for label_entry in all_labels:
if label_entry["local_pointer"].upper() == local_address:
if label_entry["bank_id"] == bank_id or (local_address1 < 0x8000 and (label_entry["bank_id"] == 0 or label_entry["bank_id"] == 1)):
return label_entry["label"]
return None
def random_asm_label():
return ".ASM_" + random_hash()
def asm_label(address):
# why using a random value when you can use the eff. address?
return ".ASM_" + hex(address)[2:]
def output_bank_opcodes(original_offset, max_byte_count=0x4000):
#fs = current_address
#b = bank_byte
#in = input_data -- extract_maps.rom
#bank_size = byte_count
#i = offset
#ad = end_address
#a, oa = current_byte_number
bank_id = 0
if original_offset > 0x8000:
bank_id = original_offset / 0x4000
print "bank id is: " + str(bank_id)
last_hl_address = None #for when we're scanning the main map script
last_a_address = None
used_3d97 = False
rom = extract_maps.rom
offset = original_offset
current_byte_number = 0 #start from the beginning
#we don't actually have an end address, but we'll just say $4000
end_address = original_offset + max_byte_count
byte_labels = {}
output = ""
keep_reading = True
while offset <= end_address and keep_reading:
current_byte = ord(extract_maps.rom[offset])
is_data = False
maybe_byte = current_byte
#first check if this byte already has a label
#if it does, use the label
#if not, generate a new label
if offset in byte_labels.keys():
line_label = byte_labels[offset]["name"]
byte_labels[offset]["usage"] += 1
else:
line_label = asm_label(offset)
byte_labels[offset] = {}
byte_labels[offset]["name"] = line_label
byte_labels[offset]["usage"] = 0
byte_labels[offset]["definition"] = True
output += line_label.lower() + "\n" #" ; " + hex(offset) + "\n"
#find out if there's a two byte key like this
temp_maybe = maybe_byte
temp_maybe += ( ord(rom[offset+1]) << 8)
if temp_maybe in opt_table.keys() and ord(rom[offset+1])!=0:
opstr = opt_table[temp_maybe][0].lower()
if "x" in opstr:
for x in range(0, opstr.count("x")):
insertion = ord(rom[offset + 1])
insertion = "$" + hex(insertion)[2:]
opstr = opstr[:opstr.find("x")].lower() + insertion + opstr[opstr.find("x")+1:].lower()
current_byte += 1
offset += 1
if "?" in opstr:
for y in range(0, opstr.count("?")):
byte1 = ord(rom[offset + 1])
byte2 = ord(rom[offset + 2])
number = byte1
number += byte2 << 8;
insertion = "$%.4x" % (number)
opstr = opstr[:opstr.find("?")].lower() + insertion + opstr[opstr.find("?")+1:].lower()
current_byte_number += 2
offset += 2
output += spacing + opstr #+ " ; " + hex(offset)
output += "\n"
current_byte_number += 2
offset += 2
elif maybe_byte in opt_table.keys():
op_code = opt_table[maybe_byte]
op_code_type = op_code[1]
op_code_byte = maybe_byte
#type = -1 when it's the E op
#if op_code_type != -1:
if op_code_type == 0 and ord(rom[offset]) == op_code_byte:
op_str = op_code[0].lower()
output += spacing + op_code[0].lower() #+ " ; " + hex(offset)
output += "\n"
offset += 1
current_byte_number += 1
elif op_code_type == 1 and ord(rom[offset]) == op_code_byte:
oplen = len(op_code[0])
opstr = copy(op_code[0])
xes = op_code[0].count("x")
include_comment = False
for x in range(0, xes):
insertion = ord(rom[offset + 1])
insertion = "$" + hex(insertion)[2:]
if current_byte == 0x18 or current_byte==0x20 or current_byte in relative_jumps: #jr or jr nz
#generate a label for the byte we're jumping to
target_address = offset + 2 + c_int8(ord(rom[offset + 1])).value
if target_address in byte_labels.keys():
byte_labels[target_address]["usage"] = 1 + byte_labels[target_address]["usage"]
line_label2 = byte_labels[target_address]["name"]
else:
line_label2 = asm_label(target_address)
byte_labels[target_address] = {}
byte_labels[target_address]["name"] = line_label2
byte_labels[target_address]["usage"] = 1
byte_labels[target_address]["definition"] = False
insertion = line_label2.lower()
include_comment = True
elif current_byte == 0x3e:
last_a_address = ord(rom[offset + 1])
opstr = opstr[:opstr.find("x")].lower() + insertion + opstr[opstr.find("x")+1:].lower()
output += spacing + opstr
if include_comment:
output += " ; " + hex(offset)
if current_byte in relative_jumps:
output += " $" + hex(ord(rom[offset + 1]))[2:]
output += "\n"
current_byte_number += 1
offset += 1
insertion = ""
current_byte_number += 1
offset += 1
include_comment = False
elif op_code_type == 2 and ord(rom[offset]) == op_code_byte:
oplen = len(op_code[0])
opstr = copy(op_code[0])
qes = op_code[0].count("?")
for x in range(0, qes):
byte1 = ord(rom[offset + 1])
byte2 = ord(rom[offset + 2])
number = byte1
number += byte2 << 8;
insertion = "$%.4x" % (number)
if maybe_byte in call_commands or current_byte in relative_unconditional_jumps or current_byte in relative_jumps:
result = find_label(insertion, bank_id)
if result != None:
insertion = result
opstr = opstr[:opstr.find("?")].lower() + insertion + opstr[opstr.find("?")+1:].lower()
output += spacing + opstr #+ " ; " + hex(offset)
output += "\n"
current_byte_number += 2
offset += 2
current_byte_number += 1
offset += 1
if current_byte == 0x21:
last_hl_address = byte1 + (byte2 << 8)
if current_byte == 0xcd:
if number == 0x3d97: used_3d97 = True
#duck out if this is jp $24d7
if current_byte == 0xc3 or current_byte in relative_unconditional_jumps:
if current_byte == 0xc3:
if number == 0x3d97: used_3d97 = True
#if number == 0x24d7: #jp
if not has_outstanding_labels(byte_labels) or all_outstanding_labels_are_reverse(byte_labels, offset):
keep_reading = False
is_data = False
break
else:
is_data = True
#stop reading at a jump, relative jump or return
if current_byte in end_08_scripts_with:
if not has_outstanding_labels(byte_labels) and all_outstanding_labels_are_reverse(byte_labels, offset):
keep_reading = False
is_data = False #cleanup
break
else:
is_data = False
keep_reading = True
else:
is_data = False
keep_reading = True
else:
# if is_data and keep_reading:
output += spacing + "db $" + hex(ord(rom[offset]))[2:] #+ " ; " + hex(offset)
output += "\n"
offset += 1
current_byte_number += 1
#else the while loop would have spit out the opcode
#these two are done prior
#offset += 1
#current_byte_number += 1
#clean up unused labels
for label_line in byte_labels.keys():
address = label_line
label_line = byte_labels[label_line]
if label_line["usage"] == 0:
output = output.replace((label_line["name"] + "\n").lower(), "")
#add the offset of the final location
output += "; " + hex(offset)
return (output, offset, last_hl_address, last_a_address, used_3d97)
def has_outstanding_labels(byte_labels):
"""
if a label is used once, it means it has to be called or specified later
"""
for label_line in byte_labels.keys():
real_line = byte_labels[label_line]
if real_line["definition"] == False: return True
return False
def all_outstanding_labels_are_reverse(byte_labels, offset):
for label_id in byte_labels.keys():
line = byte_labels[label_id] # label_id is also the address
if line["definition"] == False:
if not label_id < offset: return False
return True
def text_asm_pretty_printer(label, address_of_08, include_08=True):
"""returns (output, end_address)"""
output = label + ": ; " + hex(address_of_08) + "\n"
if include_08:
output += spacing + "db $08 ; asm\n"
results = output_bank_opcodes(address_of_08 + 1)
else:
results = output_bank_opcodes(address_of_08)
output += results[0]
end_address = results[1]
return (output, end_address)
if __name__ == "__main__":
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
#0x18f96 is PalletTownText1
#0x19B5D is BluesHouseText1
print output_bank_opcodes(int(sys.argv[1], 16))[0]

View File

@ -1,8 +0,0 @@
import json
import analyze_incbins
analyze_incbins.scan_for_predefined_labels()
with open('../pokered.sym', 'w') as sym:
for label in json.load(open('labels.json')):
sym.write('{0:x}:{1} {2}\n'.format(label['bank_id'], label['local_pointer'][1:], label['label']))

View File

@ -1,82 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-05
#insert object data into pokered.asm
import extract_maps
from pretty_map_headers import map_name_cleaner, object_data_pretty_printer, make_object_label_name, make_text_label, map_constants
from analyze_incbins import asm, offset_to_pointer, find_incbin_to_replace_for, split_incbin_line_into_three, generate_diff_insert, load_asm, isolate_incbins, process_incbins
import analyze_incbins
import os, sys
import subprocess
spacing = " "
def insert_object(map_id):
map = extract_maps.map_headers[map_id]
object = map["object_data"]
size = extract_maps.compute_object_data_size(object)
address = int(map["object_data_pointer"], 16)
line_number = find_incbin_to_replace_for(address)
if line_number == None:
print "skipping object data for map " + str(map["id"]) + " at " + map["object_data_pointer"] + " for " + str(size) + " bytes."
return
newlines = split_incbin_line_into_three(line_number, address, size)
object_asm = object_data_pretty_printer(map_id)
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = object_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
diff = generate_diff_insert(line_number, newlines)
print diff
print "... Applying diff."
#write the diff to a file
fh = open("temp.patch", "w")
fh.write(diff)
fh.close()
#apply the patch
os.system("patch ../pokered.asm temp.patch")
#remove the patch
os.system("rm temp.patch")
#confirm it's working
subprocess.check_call("cd ../; make clean; LC_CTYPE=UTF-8 make", shell=True)
def insert_all_objects():
for map_id in extract_maps.map_headers.keys():
if map_id not in extract_maps.bad_maps:
insert_object(map_id)
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
load_asm()
isolate_incbins()
process_incbins()
if __name__ == "__main__":
#load map headers and object data
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
#load incbins
load_asm()
isolate_incbins()
process_incbins()
#insert_object(1)
insert_all_objects()

View File

@ -1,891 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-07, 2012-01-17, 2012-01-27
#insert TX_FAR targets into pokered.asm
#and other insertion tasks
import extract_maps
from analyze_texts import analyze_texts, text_pretty_printer_at, scan_rom_for_tx_fars
from pretty_map_headers import map_name_cleaner, make_text_label, map_constants, find_all_tx_fars, tx_far_pretty_printer, tx_far_label_maker
import pretty_map_headers
from analyze_incbins import asm, offset_to_pointer, find_incbin_to_replace_for, split_incbin_line_into_three, generate_diff_insert, load_asm, isolate_incbins, process_incbins, reset_incbins, apply_diff
import analyze_incbins
from gbz80disasm import text_asm_pretty_printer, output_bank_opcodes, load_labels, find_label
import os, sys
import subprocess
spacing = " "
tx_fars = None
failed_attempts = {}
pokemons = ["BULBASAUR", "IVYSAUR", "VENUSAUR", "CHARMANDER", "CHARMELEON", "CHARIZARD", "SQUIRTLE", "WARTORTLE", "BLASTOISE", "CATERPIE", "METAPOD", "BUTTERFREE", "WEEDLE", "KAKUNA", "BEEDRILL", "PIDGEY", "PIDGEOTTO", "PIDGEOT", "RATTATA", "RATICATE", "SPEAROW", "FEAROW", "EKANS", "ARBOK", "PIKACHU", "RAICHU", "SANDSHREW", "SANDSLASH", "NIDORANF", "NIDORINA", "NIDOQUEEN", "NIDORANM", "NIDORINO", "NIDOKING", "CLEFAIRY", "CLEFABLE", "VULPIX", "NINETALES", "JIGGLYPUFF", "WIGGLYTUFF", "ZUBAT", "GOLBAT", "ODDISH", "GLOOM", "VILEPLUME", "PARAS", "PARASECT", "VENONAT", "VENOMOTH", "DIGLETT", "DUGTRIO", "MEOWTH", "PERSIAN", "PSYDUCK", "GOLDUCK", "MANKEY", "PRIMEAPE", "GROWLITHE", "ARCANINE", "POLIWAG", "POLIWHIRL", "POLIWRATH", "ABRA", "KADABRA", "ALAKAZAM", "MACHOP", "MACHOKE", "MACHAMP", "BELLSPROUT", "WEEPINBELL", "VICTREEBEL", "TENTACOOL", "TENTACRUEL", "GEODUDE", "GRAVELER", "GOLEM", "PONYTA", "RAPIDASH", "SLOWPOKE", "SLOWBRO", "MAGNEMITE", "MAGNETON", "FARFETCH_D", "DODUO", "DODRIO", "SEEL", "DEWGONG", "GRIMER", "MUK", "SHELLDER", "CLOYSTER", "GASTLY", "HAUNTER", "GENGAR", "ONIX", "DROWZEE", "HYPNO", "KRABBY", "KINGLER", "VOLTORB", "ELECTRODE", "EXEGGCUTE", "EXEGGUTOR", "CUBONE", "MAROWAK", "HITMONLEE", "HITMONCHAN", "LICKITUNG", "KOFFING", "WEEZING", "RHYHORN", "RHYDON", "CHANSEY", "TANGELA", "KANGASKHAN", "HORSEA", "SEADRA", "GOLDEEN", "SEAKING", "STARYU", "STARMIE", "MR_MIME", "SCYTHER", "JYNX", "ELECTABUZZ", "MAGMAR", "PINSIR", "TAUROS", "MAGIKARP", "GYARADOS", "LAPRAS", "DITTO", "EEVEE", "VAPOREON", "JOLTEON", "FLAREON", "PORYGON", "OMANYTE", "OMASTAR", "KABUTO", "KABUTOPS", "AERODACTYL", "SNORLAX", "ARTICUNO", "ZAPDOS", "MOLTRES", "DRATINI", "DRAGONAIR", "DRAGONITE", "MEWTWO", "MEW"]
moves = [["POUND", 0x01], ["KARATE_CHOP", 0x02], ["DOUBLESLAP", 0x03], ["COMET_PUNCH", 0x04], ["MEGA_PUNCH", 0x05], ["PAY_DAY", 0x06], ["FIRE_PUNCH", 0x07], ["ICE_PUNCH", 0x08], ["THUNDERPUNCH", 0x09], ["SCRATCH", 0x0A], ["VICEGRIP", 0x0B], ["GUILLOTINE", 0x0C], ["RAZOR_WIND", 0x0D], ["SWORDS_DANCE", 0x0E], ["CUT", 0x0F], ["GUST", 0x10], ["WING_ATTACK", 0x11], ["WHIRLWIND", 0x12], ["FLY", 0x13], ["BIND", 0x14], ["SLAM", 0x15], ["VINE_WHIP", 0x16], ["STOMP", 0x17], ["DOUBLE_KICK", 0x18], ["MEGA_KICK", 0x19], ["JUMP_KICK", 0x1A], ["ROLLING_KICK", 0x1B], ["SAND_ATTACK", 0x1C], ["HEADBUTT", 0x1D], ["HORN_ATTACK", 0x1E], ["FURY_ATTACK", 0x1F], ["HORN_DRILL", 0x20], ["TACKLE", 0x21], ["BODY_SLAM", 0x22], ["WRAP", 0x23], ["TAKE_DOWN", 0x24], ["THRASH", 0x25], ["DOUBLE_EDGE", 0x26], ["TAIL_WHIP", 0x27], ["POISON_STING", 0x28], ["TWINEEDLE", 0x29], ["PIN_MISSILE", 0x2A], ["LEER", 0x2B], ["BITE", 0x2C], ["GROWL", 0x2D], ["ROAR", 0x2E], ["SING", 0x2F], ["SUPERSONIC", 0x30], ["SONICBOOM", 0x31], ["DISABLE", 0x32], ["ACID", 0x33], ["EMBER", 0x34], ["FLAMETHROWER", 0x35], ["MIST", 0x36], ["WATER_GUN", 0x37], ["HYDRO_PUMP", 0x38], ["SURF", 0x39], ["ICE_BEAM", 0x3A], ["BLIZZARD", 0x3B], ["PSYBEAM", 0x3C], ["BUBBLEBEAM", 0x3D], ["AURORA_BEAM", 0x3E], ["HYPER_BEAM", 0x3F], ["PECK", 0x40], ["DRILL_PECK", 0x41], ["SUBMISSION", 0x42], ["LOW_KICK", 0x43], ["COUNTER", 0x44], ["SEISMIC_TOSS", 0x45], ["STRENGTH", 0x46], ["ABSORB", 0x47], ["MEGA_DRAIN", 0x48], ["LEECH_SEED", 0x49], ["GROWTH", 0x4A], ["RAZOR_LEAF", 0x4B], ["SOLARBEAM", 0x4C], ["POISONPOWDER", 0x4D], ["STUN_SPORE", 0x4E], ["SLEEP_POWDER", 0x4F], ["PETAL_DANCE", 0x50], ["STRING_SHOT", 0x51], ["DRAGON_RAGE", 0x52], ["FIRE_SPIN", 0x53], ["THUNDERSHOCK", 0x54], ["THUNDERBOLT", 0x55], ["THUNDER_WAVE", 0x56], ["THUNDER", 0x57], ["ROCK_THROW", 0x58], ["EARTHQUAKE", 0x59], ["FISSURE", 0x5A], ["DIG", 0x5B], ["TOXIC", 0x5C], ["CONFUSION", 0x5D], ["PSYCHIC_M", 0x5E], ["HYPNOSIS", 0x5F], ["MEDITATE", 0x60], ["AGILITY", 0x61], ["QUICK_ATTACK", 0x62], ["RAGE", 0x63], ["TELEPORT", 0x64], ["NIGHT_SHADE", 0x65], ["MIMIC", 0x66], ["SCREECH", 0x67], ["DOUBLE_TEAM", 0x68], ["RECOVER", 0x69], ["HARDEN", 0x6A], ["MINIMIZE", 0x6B], ["SMOKESCREEN", 0x6C], ["CONFUSE_RAY", 0x6D], ["WITHDRAW", 0x6E], ["DEFENSE_CURL", 0x6F], ["BARRIER", 0x70], ["LIGHT_SCREEN", 0x71], ["HAZE", 0x72], ["REFLECT", 0x73], ["FOCUS_ENERGY", 0x74], ["BIDE", 0x75], ["METRONOME", 0x76], ["MIRROR_MOVE", 0x77], ["SELFDESTRUCT", 0x78], ["EGG_BOMB", 0x79], ["LICK", 0x7A], ["SMOG", 0x7B], ["SLUDGE", 0x7C], ["BONE_CLUB", 0x7D], ["FIRE_BLAST", 0x7E], ["WATERFALL", 0x7F], ["CLAMP", 0x80], ["SWIFT", 0x81], ["SKULL_BASH", 0x82], ["SPIKE_CANNON", 0x83], ["CONSTRICT", 0x84], ["AMNESIA", 0x85], ["KINESIS", 0x86], ["SOFTBOILED", 0x87], ["HI_JUMP_KICK", 0x88], ["GLARE", 0x89], ["DREAM_EATER", 0x8A], ["POISON_GAS", 0x8B], ["BARRAGE", 0x8C], ["LEECH_LIFE", 0x8D], ["LOVELY_KISS", 0x8E], ["SKY_ATTACK", 0x8F], ["TRANSFORM", 0x90], ["BUBBLE", 0x91], ["DIZZY_PUNCH", 0x92], ["SPORE", 0x93], ["FLASH", 0x94], ["PSYWAVE", 0x95], ["SPLASH", 0x96], ["ACID_ARMOR", 0x97], ["CRABHAMMER", 0x98], ["EXPLOSION", 0x99], ["FURY_SWIPES", 0x9A], ["BONEMERANG", 0x9B], ["REST", 0x9C], ["ROCK_SLIDE", 0x9D], ["HYPER_FANG", 0x9E], ["SHARPEN", 0x9F], ["CONVERSION", 0xA0], ["TRI_ATTACK", 0xA1], ["SUPER_FANG", 0xA2], ["SLASH", 0xA3], ["SUBSTITUTE", 0xA4], ["STRUGGLE", 0xA5]]
elemental_types = [
["NORMAL", "EQU", 0x00],
["FIGHTING", "EQU", 0x01],
["FLYING", "EQU", 0x02],
["POISON", "EQU", 0x03],
["GROUND", "EQU", 0x04],
["ROCK", "EQU", 0x05],
["BUG", "EQU", 0x07],
["GHOST", "EQU", 0x08],
["FIRE", "EQU", 0x14],
["WATER", "EQU", 0x15],
["GRASS", "EQU", 0x16],
["ELECTRIC", "EQU", 0x17],
["PSYCHIC", "EQU", 0x18],
["ICE", "EQU", 0x19],
["DRAGON", "EQU", 0x1A]]
def local_reset_incbins():
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
def find_tx_far_entry(map_id, text_id):
for tx_far_line in tx_fars:
if tx_far_line[0] == map_id and tx_far_line[1] == text_id:
return tx_far_line
def insert_tx_far(map_id, text_id, tx_far_line=None):
"inserts a tx_far"
global tx_fars
if tx_far_line == None:
tx_far_line = find_tx_far_entry(map_id, text_id)
text_pointer = tx_far_line[2]
start_address = tx_far_line[3]
tx_far_object = tx_far_line[4]
end_address = tx_far_object[1]["end_address"] + 1 #the end byte; +1 because of a bug somewhere :(
line_number = find_incbin_to_replace_for(start_address)
if line_number == None:
print "skipping tx_far for map_id=" + str(map_id) + " text_id=" + str(text_id) + " text_pointer=" + hex(text_pointer) + " tx_far_start_address=" + hex(start_address)
return
#also do a name check
label = tx_far_label_maker(extract_maps.map_headers[map_id]["name"], text_id)
if (label + ":") in "\n".join(analyze_incbins.asm):
print "skipping tx_far for map_id=" + str(map_id) + " text_id=" + str(text_id) + " text_pointer=" + hex(text_pointer) + " tx_far_start_address=" + hex(start_address)
return
newlines = split_incbin_line_into_three(line_number, start_address, end_address - start_address)
tx_far_asm = tx_far_pretty_printer(tx_far_line)
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = tx_far_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$") #where does this keep coming from??
#signs are dumb; cluster the labels please
if "\"needs fulfilled!\", $55" in newlines:
newlines = "\n" + label + ": "
line_number += 1
if ("STRENGTH to move!" in newlines) or ("it the way it is." in newlines):
newlines = "\n" + label + ": "
line_number += 1
if "@\"" in newlines and not "@@\"" in newlines:
newlines = newlines.replace("@", "@@")
#Char52 doesn't work yet? oh well
newlines = newlines.replace("Char52", "$52")
diff = generate_diff_insert(line_number, newlines)
print "working on map_id=" + str(map_id) + " text_id=" + str(text_id)
print diff
apply_diff(diff)
def insert_all_tx_far_targets():
for tx_far in tx_fars:
map_id = tx_far[0]
text_id = tx_far[1]
#if map_id <= 185: continue #i'm just trying to get it going faster
insert_tx_far(map_id, text_id, tx_far_line=tx_far)
reset_incbins()
analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
load_asm()
isolate_incbins()
process_incbins()
def all_texts_are_tx_fars(map_id):
map2 = extract_maps.map_headers[map_id]
for text_id in map2["texts"]:
txt = map2["texts"][text_id]
if not "TX_FAR" in txt[0].keys(): return False
return True
def texts_label_pretty_printer(map_id):
"output a texts label for map if all texts are TX_FARs and in the asm already"
#extract_maps.map_headers[map_id]["texts"][text_id][0]["TX_FAR"]
#if not all_texts_are_tx_fars(map_id): return None
map2 = extract_maps.map_headers[map_id]
#pointer to the list of texts
texts_list_pointer = int(map2["texts_pointer"], 16)
#get the label for this texts list
base_label = map_name_cleaner(map2["name"], None)[:-2]
label = base_label + "Texts"
#make up a label for each text
text_labels = []
text_id = 1
for text in map2["texts"].keys():
text_label = base_label + "Text" + str(text_id)
text_labels.append(text_label)
text_id += 1
output = label + ": ; " + hex(texts_list_pointer)
output += "\n"
output += spacing + "dw "
first = True
for labela in text_labels:
if not first:
output += ", " + labela
else:
output += labela
first = False
return output
def insert_texts_label(map_id):
#if not all_texts_are_tx_fars(map_id): return None
map2 = extract_maps.map_headers[map_id]
base_label = map_name_cleaner(map2["name"], None)[:-2]
label = base_label + "Texts"
texts_pointer = int(map2["texts_pointer"], 16)
insert_asm = texts_label_pretty_printer(map_id)
line_number = find_incbin_to_replace_for(texts_pointer)
if line_number == None:
print "skipping texts label for map_id=" + str(map_id) + " texts_pointer=" + hex(texts_pointer) + " because the address is taken"
return
#also do a name check
if (label + ":") in "\n".join(analyze_incbins.asm):
print "skipping texts label for map_id=" + str(map_id) + " texts_pointer=" + hex(texts_pointer) + " because the label is already used"
return
newlines = split_incbin_line_into_three(line_number, texts_pointer, len(map2["referenced_texts"])*2 )
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = insert_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$")
diff = generate_diff_insert(line_number, newlines)
print "working on map_id=" + str(map_id) + " texts_pointer=" + hex(texts_pointer)
print diff
apply_diff(diff)
#untested as of 2012-01-07
def insert_all_texts_labels():
for map_id in extract_maps.map_headers.keys():
if map_id not in extract_maps.bad_maps:
if len(extract_maps.map_headers[map_id]["referenced_texts"]) > 0:
insert_texts_label(map_id)
reset_incbins()
analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
load_asm()
isolate_incbins()
process_incbins()
def txt_to_tx_far_pretty_printer(address, label, target_label, include_byte=False):
output = "\n" + label + ": ; " + hex(address) + "\n"
output += spacing + "TX_FAR " + target_label + "\n"
if include_byte:
output += spacing + "db $50\n"
return output
def insert_text_label_tx_far(map_id, text_id):
if map_id in extract_maps.bad_maps:
print "bad map id=" + str(map_id)
return
map2 = extract_maps.map_headers[map_id]
if map2["texts"][text_id] == {0: {}}: return None
base_label = map_name_cleaner(map2["name"], None)[:-2]
label = base_label + "Text" + str(text_id)
target_label = "_" + label
start_address = map2["texts"][text_id][0]["start_address"]
if 0x4000 <= start_address <= 0x7fff:
start_address = extract_maps.calculate_pointer(start_address, int(map2["bank"],16))
include_byte = False
print map2["texts"][text_id]
if "type" in map2["texts"][text_id][1].keys():
if map2["texts"][text_id][1]["type"] == 0x50:
include_byte = True
tx_far_asm = txt_to_tx_far_pretty_printer(start_address, label, target_label, include_byte=include_byte)
line_number = find_incbin_to_replace_for(start_address)
if line_number == None:
print "skipping text label that calls TX_FAR for map_id=" + str(map_id) + " text_id=" + str(text_id) + " because the address is taken " + hex(start_address)
return
#also do a name check
if 1 < ("\n".join(analyze_incbins.asm)).count("\n" + label + ":"):
print "skipping text label that calls TX_FAR for map_id=" + str(map_id) + " text_id" + str(text_id) + " because the label is already used (" + label + ":)"
return
extra = 0
if include_byte: extra += 1
newlines = split_incbin_line_into_three(line_number, start_address, 4 + extra )
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = tx_far_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$")
diff = generate_diff_insert(line_number, newlines)
print "working on map_id=" + str(map_id) + " text_id=" + str(text_id)
print diff
apply_diff(diff)
def insert_all_text_labels():
for map_id in extract_maps.map_headers.keys():
if map_id <= 100: continue #skip
if map_id not in extract_maps.bad_maps:
for text_id in extract_maps.map_headers[map_id]["referenced_texts"]:
insert_text_label_tx_far(map_id, text_id)
reset_incbins()
analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
load_asm()
isolate_incbins()
process_incbins()
#TODO: if line_id !=0 then don't include the label?
def insert_08_asm(map_id, text_id, line_id=0):
map2 = extract_maps.map_headers[map_id]
base_label = map_name_cleaner(map2["name"], None)[:-2]
label = base_label + "Text" + str(text_id)
start_address = all_texts[map_id][text_id][line_id]["start_address"]
(text_asm, end_address) = text_asm_pretty_printer(label, start_address)
print "end address is: " + hex(end_address)
#find where to insert the assembly
line_number = find_incbin_to_replace_for(start_address)
if line_number == None:
print "skipping text label for a $08 on map_id=" + str(map_id) + " text_id=" + str(text_id) + " because the address is taken"
return
#also do a name check
if 1 <= ("\n".join(analyze_incbins.asm)).count("\n" + label + ":"):
print "skipping text label for a $08 on map_id=" + str(map_id) + " text_id=" + str(text_id) + " because the label is already taken (" + label + ":)"
return
newlines = split_incbin_line_into_three(line_number, start_address, end_address - start_address )
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = text_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$")
diff = generate_diff_insert(line_number, newlines)
print "working on map_id=" + str(map_id) + " text_id=" + str(text_id)
print diff
result = apply_diff(diff)
if result == False:
failed_attempts[len(failed_attempts.keys())] = {"map_id": map_id, "text_id": text_id}
def find_all_08s():
all_08s = []
for map_id in all_texts:
for text_id in all_texts[map_id].keys():
if 0 in all_texts[map_id][text_id].keys():
for line_id in all_texts[map_id][text_id].keys():
if all_texts[map_id][text_id][line_id]["type"] == 0x8:
all_08s.append([map_id, text_id, line_id])
return all_08s
def insert_all_08s():
all_08s = find_all_08s()
for the_08_line in all_08s:
map_id = the_08_line[0]
if map_id <= 86: continue #speed things up
text_id = the_08_line[1]
line_id = the_08_line[2]
print "processing map_id=" + str(map_id) + " text_id=" + str(text_id)
insert_08_asm(map_id, text_id, line_id)
#reset everything
analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
def insert_asm(start_address, label, text_asm=None, end_address=None):
if text_asm == None and end_address == None:
(text_asm, end_address) = text_asm_pretty_printer(label, start_address, include_08=False)
print "end address is: " + hex(end_address)
#find where to insert the assembly
line_number = find_incbin_to_replace_for(start_address)
if line_number == None:
print "skipping asm because the address is taken"
return False
#name check
if (label + ":") in "\n".join(analyze_incbins.asm):
print "skipping asm because the label is taken"
return False
newlines = split_incbin_line_into_three(line_number, start_address, end_address - start_address )
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = text_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$")
diff = generate_diff_insert(line_number, newlines)
print diff
result = apply_diff(diff, try_fixing=True)
return True
def insert_text(address, label, apply=False, try_fixing=True):
"inserts a text script (but not $8s)"
start_address = address
line_number = find_incbin_to_replace_for(start_address)
if line_number == None:
print "skipping text at " + hex(start_address) + " with address " + label
return "skip"
#another reason to skip is if the interval is 0
processed_incbin = analyze_incbins.processed_incbins[line_number]
if processed_incbin["interval"] == 0:
print "skipping text at " + hex(start_address) + " with address " + label + " because the interval is 0"
return "skip"
text_asm, byte_count = text_pretty_printer_at(start_address, label)
end_address = start_address + byte_count
newlines = split_incbin_line_into_three(line_number, start_address, byte_count)
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = text_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$") #where does this keep coming from??
#Char52 doesn't work yet
newlines = newlines.replace("Char52", "$52")
diff = generate_diff_insert(line_number, newlines)
print diff
if apply:
return apply_diff(diff, try_fixing=try_fixing)
else: #simulate a successful insertion
return True
#move this into another file?
def scan_for_map_scripts_pointer():
for map_id in extract_maps.map_headers.keys(): #skip id=0 (Pallet Town) because the naming conventions are wonky
map2 = extract_maps.map_headers[map_id]
if map_id in extract_maps.bad_maps or map_id in [0, 39, 37, 38]: continue #skip
script_pointer = int(map2["script_pointer"], 16)
main_asm_output, offset, last_hl_address, last_a_address, used_3d97 = output_bank_opcodes(script_pointer)
hl_pointer = "None"
first_script_text = ""
if last_hl_address != None and last_hl_address != "None" and used_3d97==True:
if last_hl_address > 0x3fff:
hl_pointer = extract_maps.calculate_pointer(last_hl_address, int(map2["bank"], 16))
else:
hl_pointer = last_hl_address
byte1 = ord(extract_maps.rom[hl_pointer])
byte2 = ord(extract_maps.rom[hl_pointer+1])
address = byte1 + (byte2 << 8)
if address > 0x3fff:
first_script_pointer = extract_maps.calculate_pointer(address, int(map2["bank"], 16))
else:
first_script_pointer = address
#for later output
first_script_text = " first_script=" + hex(first_script_pointer)
#go ahead and insert this script pointer
insert_asm(first_script_pointer, map_name_cleaner(map2["name"], None)[:-2] + "Script0")
#reset everything
#analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
a_numbers = [0]
last_a_id = 0
script_pointers = [hex(first_script_pointer)]
latest_script_pointer = first_script_pointer
while last_a_id == (max(a_numbers)) or last_a_id==0:
asm_output, offset, last_hl_address2, last_a_id, byte1, byte2, address = None, None, None, None, None, None, None
asm_output, offset, last_hl_address2, last_a_id, used_3d97_2 = output_bank_opcodes(latest_script_pointer)
if last_a_id == (max(a_numbers) + 1):
a_numbers.append(last_a_id)
else:
break
byte1 = ord(extract_maps.rom[hl_pointer + (2*last_a_id)])
byte2 = ord(extract_maps.rom[hl_pointer + (2*last_a_id) + 1])
address2 = byte1 + (byte2 << 8)
if address2 > 0x3fff:
latest_script_pointer = extract_maps.calculate_pointer(address2, int(map2["bank"], 16))
else:
latest_script_pointer = address2
script_pointers.append(hex(latest_script_pointer))
#print "latest script pointer (part 1): " + hex(address2)
#print "latest script pointer: " + hex(latest_script_pointer)
#go ahead and insert the asm for this script
result = insert_asm(latest_script_pointer, map_name_cleaner(map2["name"], None)[:-2] + "Script" + str(len(script_pointers) - 1))
if result:
#reset everything
#analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
print "map_id=" + str(map_id) + " scripts are: " + str(script_pointers)
if last_hl_address == None: last_hl_address = "None"
else: last_hl_address = hex(last_hl_address)
if hl_pointer != None and hl_pointer != "None": hl_pointer = hex(hl_pointer)
print "map_id=" + str(map_id) + " " + map2["name"] + " script_pointer=" + hex(script_pointer) + " script_pointers=" + hl_pointer + first_script_text
print main_asm_output
print "\n\n"
#insert asm for the main script
result = insert_asm(script_pointer, map_name_cleaner(map2["name"], None)[:-2] + "Script")
if result:
#reset everything
#analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
#insert script pointer list asm if there's anything of value
if hl_pointer != None and hl_pointer != "None" and used_3d97==True:
start_address = int(hl_pointer, 16) #where to insert this list
total_size = len(a_numbers) * 2
script_label = map_name_cleaner(map2["name"], None)[:-2] + "Script"
scripts_label = script_label + "s"
script_asm = scripts_label + ": ; " + hex(start_address) + "\n"
script_asm += spacing + "dw"
first = True
for id in a_numbers:
if first:
script_asm += " "
first = False
else:
script_asm += ", "
script_asm += script_label + str(id)
script_asm += "\n" #extra newline?
result = insert_asm(start_address, scripts_label, text_asm=script_asm, end_address=start_address + total_size)
if result:
#reset everything
#analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
else:
print "trouble inserting map script pointer list"
print script_asm
sys.exit(0)
def scan_rom_for_tx_fars_and_insert():
"""calls analyze_texts.scan_rom_for_tx_fars()
looks through INCBIN'd addresses from main.asm,
finds TX_FARs that aren't included yet.
"""
x = 0
address_bundles = scan_rom_for_tx_fars(printer=True)
for address_bundle in address_bundles:
tx_far_address = address_bundle[1]
tx_far_target_address = address_bundle[0]
if tx_far_address in [0xeff2]: continue #skip
#if tx_far_address < 0x7627b: continue #because it stopped a few times for errors
tx_far_label = "UnnamedText_%.2x" % (tx_far_address)
tx_far_target_label = "_" + tx_far_label
#let's also do a quick check if it might be in the file already
if not (": ; " + hex(tx_far_address) in analyze_incbins.asm):
print "inserting text at " + hex(tx_far_address)
result = insert_text(tx_far_target_address, tx_far_target_label, apply=True)
else:
#we can't just pretend like it worked, because we don't know what label was used
#so, figure out the label
for line in analyze_incbins.asm_lines:
if ": ; " + hex(tx_far_address) in line:
tx_far_target_label = line.split(":")[0]
result = "skip"
if result == True or result == None:
local_reset_incbins()
result2 = insert_text(tx_far_address, tx_far_label, apply=True)
local_reset_incbins()
elif result == "skip":
print "skipping " + hex(tx_far_address)
# result2 = insert_text(tx_far_address, tx_far_label, apply=True)
# local_reset_incbins()
#just skip these for now
#if not result or not result2:
# sys.exit(0)
def get_mon_name(id):
return pokemons[id]
def get_type_label(id):
for line in elemental_types:
if line[2] == id: return line[0]
return None
def get_attack_label(id):
for move in moves:
if move[1] == id: return move[0]
return "0" #no move
def get_pointer_target_at(address):
rom = extract_maps.rom
byte1 = ord(rom[address])
byte2 = ord(rom[address+1])
pointer = (byte1 + (byte2 << 8))
return pointer
def get_frontsprite_label(id):
return get_mon_name(id).title() + "PicFront"
def get_backsprite_label(id):
return get_mon_name(id).title() + "PicBack"
def base_data_pretty_printer(id):
"""returns beautified asm for this pokemon
uses 28 bytes
pokedex number, base hp, base attack, base defense, base speed, base special
type 1 (label), type 2 (label), catch rate, base experience yield
dimensions of frontsprite (byte)
frontsprite label pointer
backsprite label pointer
attacks known at level 1 (4 bytes, 4 constants)
growth rate (byte)
incbin - tm/hm flags (7 bytes)
padding (0)
"""
output = ""
rom = extract_maps.rom
base_address = 0x383de + (28 * (id))
pokedex_number = id
mon_name = get_mon_name(id)
base_hp = ord(rom[base_address + 1])
base_attack = ord(rom[base_address + 2])
base_defense = ord(rom[base_address + 3])
base_speed = ord(rom[base_address + 4])
base_special = ord(rom[base_address + 5])
type1_id = ord(rom[base_address + 6])
type2_id = ord(rom[base_address + 7])
type1 = get_type_label(type1_id)
type2 = get_type_label(type2_id)
catch_rate = ord(rom[base_address + 8])
base_exp_yield = ord(rom[base_address + 9])
frontsprite_dimensions = ord(rom[base_address + 10])
frontsprite = get_frontsprite_label(id)
backsprite = get_backsprite_label(id)
#attacks known at level 0
attack1 = get_attack_label(ord(rom[base_address + 15]))
attack2 = get_attack_label(ord(rom[base_address + 16]))
attack3 = get_attack_label(ord(rom[base_address + 17]))
attack4 = get_attack_label(ord(rom[base_address + 18]))
growth_rate = ord(rom[base_address + 19])
incbin_start_address = base_address + 20
incbin_end_address = base_address + 27
output = mon_name.title() + ("BaseStats: ; 0x%.x" % (base_address)) + "\n"
output += spacing + "db DEX_" + mon_name.upper() + " ; pokedex id\n"
output += spacing + ("db " + str(base_hp)) + " ; base hp\n"
output += spacing + "db " + str(base_attack) + " ; base attack\n"
output += spacing + "db " + str(base_defense) + " ; base defense\n"
output += spacing + "db " + str(base_speed) + " ; base speed\n"
output += spacing + "db " + str(base_special) + " ; base special\n\n"
output += spacing + "db " + type1 + " ; species type 1\n"
output += spacing + "db " + type2 + " ; species type 2\n\n"
output += spacing + "db " + str(catch_rate) + " ; catch rate\n"
output += spacing + "db " + str(base_exp_yield) + " ; base exp yield\n"
output += spacing + ("db $%.2x" % (frontsprite_dimensions)) + " ; sprite dimensions\n\n"
output += spacing + "dw " + frontsprite + "\n"
output += spacing + "dw " + backsprite + "\n"
output += spacing + "\n" + spacing + "; attacks known at lvl 0\n"
output += spacing + "db " + attack1 + "\n"
output += spacing + "db " + attack2 + "\n"
output += spacing + "db " + attack3 + "\n"
output += spacing + "db " + attack4 + "\n\n"
output += spacing + "db " + str(growth_rate) + " ; growth rate\n"
output += spacing + "\n" + spacing + "; learnset\n"
#learnset crap
output += spacing + "db %" + bin(ord(rom[base_address + 20]))[2:].zfill(8) + "\n"
output += spacing + "db %" + bin(ord(rom[base_address + 21]))[2:].zfill(8) + "\n"
output += spacing + "db %" + bin(ord(rom[base_address + 22]))[2:].zfill(8) + "\n"
output += spacing + "db %" + bin(ord(rom[base_address + 23]))[2:].zfill(8) + "\n"
output += spacing + "db %" + bin(ord(rom[base_address + 24]))[2:].zfill(8) + "\n"
output += spacing + "db %" + bin(ord(rom[base_address + 25]))[2:].zfill(8) + "\n"
output += spacing + "db %" + bin(ord(rom[base_address + 26]))[2:].zfill(8) + "\n\n"
output += spacing + "db 0 ; padding\n"
return output
def insert_base_stats(id):
insert_asm = base_data_pretty_printer(id)
address = 0x383de + (28 * (id))
line_number = find_incbin_to_replace_for(address)
label = get_mon_name(id).title() + "BaseStats"
if line_number == None:
print "skipping, already inserted at " + hex(address)
return
#also do a name check
if (label + ":") in "\n".join(analyze_incbins.asm):
print "skipping " + label + " because it is already in use.."
return
newlines = split_incbin_line_into_three(line_number, address, 28 )
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = insert_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
newlines = newlines.replace("$x", "$")
diff = generate_diff_insert(line_number, newlines)
print diff
apply_diff(diff, try_fixing=False, do_compile=False)
def insert_all_base_stats():
for id in range(0, 151):
#if id < 62: continue #skip
insert_base_stats(id)
#reset everything
reset_incbins()
analyze_incbins.reset_incbins()
asm = None
incbin_lines = []
processed_incbins = {}
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload
load_asm()
isolate_incbins()
process_incbins()
if __name__ == "__main__":
#load map headers and object data
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
load_labels()
#print base_data_pretty_printer(0)
load_asm()
isolate_incbins()
process_incbins()
#insert_base_stats(1)
insert_all_base_stats()
#load texts (these two have different formats)
#all_texts = pretty_map_headers.analyze_texts.analyze_texts()
#pretty_map_headers.all_texts = all_texts
#tx_fars = pretty_map_headers.find_all_tx_fars()
#load incbins
#reset_incbins()
#scan_for_map_scripts_pointer()
#scan_rom_for_tx_fars_and_insert()
#insert_text(0xa586b, "_VermilionCityText14")
#insert _ViridianCityText10
#insert_tx_far(1, 10)
#just me testing a pokemart sign duplicate
#insert_tx_far(3, 14)
#this is the big one
#insert_all_tx_far_targets()
#for map_id in extract_maps.map_headers.keys():
# if map_id not in extract_maps.bad_maps:
# if len(extract_maps.map_headers[map_id]["referenced_texts"]) > 0:
# texts_label_pretty_printer(map_id)
#insert_texts_label(240)
#insert_all_texts_labels()
#insert_text_label_tx_far(240, 1)
#insert_all_text_labels()
#insert_08_asm(83, 1)
#insert_all_08s()
#insert_asm(0x1da56, "NameRaterText1")
#insert_text_label_tx_far(91, 1)
#insert_text(0x44276, "ViridianPokeCenterText4")
#insert_texts_label(4)
#insert_all_texts_labels()

View File

@ -1,37 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-15
#dump map height/width constants
import extract_maps
from pretty_map_headers import map_name_cleaner, map_constants
def get_map_size_constants(do_sed=False):
output = ""
sed_lines = ""
for map_id in extract_maps.map_headers.keys():
if map_id in extract_maps.bad_maps: continue #skip
map2 = extract_maps.map_headers[map_id]
base_name = map_name_cleaner(map2["name"], None)[:-2]
constant_name = map_constants[map_id]
height = int(map2["y"], 16)
width = int(map2["x"], 16)
output += "; " + base_name + "_h map_id=" + str(map_id) + "\n"
output += constant_name + "_HEIGHT EQU $%.2x\n" % (height)
output += constant_name + "_WIDTH EQU $%.2x\n" % (width)
output += "\n"
sed_lines += "sed -i 's/" + base_name + "Height/" + constant_name + "_HEIGHT" + "/g' main.asm" + "\n"
sed_lines += "sed -i 's/" + base_name + "Width/" + constant_name + "_WIDTH" + "/g' main.asm" + "\n"
if do_sed:
return sed_lines
else:
return output
if __name__ == "__main__":
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
print get_map_size_constants(do_sed=True)

View File

@ -1,171 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-03
#purpose: extract .blk files from baserom.gbc
#note: use python2.7 because of subprocess in analyze_incbins
import extract_maps #rom, assert_rom, load_rom, calculate_pointer, load_map_pointers, read_all_map_headers, map_headers
from pretty_map_headers import map_name_cleaner
from analyze_incbins import asm, offset_to_pointer, find_incbin_to_replace_for, split_incbin_line_into_three, generate_diff_insert, load_asm, isolate_incbins, process_incbins
import analyze_incbins
import os, sys
import subprocess
spacing = " "
used_map_pointers = []
def extract_map_block_data(map_id, savefile=False):
map = extract_maps.map_headers[map_id]
if map["name"] == "FREEZE": return #skip this one
blocksdata_pointer = int(map["map_pointer"], 16)
y = int(map["y"], 16)
x = int(map["x"], 16)
size = x*y
#fetch the data from the rom
blocksdata = extract_maps.rom[blocksdata_pointer:blocksdata_pointer+size]
#clean up the filename and label (for pokered.asm)
cleaned_name = map_name_cleaner(map["name"], None)
label_text = cleaned_name.replace("_h", "Blocks")
filename = cleaned_name.replace("_h", "").lower()
full_filepath = "maps/" + filename + ".blk"
if savefile:
print "Saving ../maps/" + filename + ".blk for map id=" + str(map_id)
fh = open("../maps/" + filename + ".blk", "w")
fh.write(blocksdata)
fh.close()
def make_labels(name):
cleaned_name = map_name_cleaner(name, None)
label_text = cleaned_name.replace("_h", "Blocks")
filename = cleaned_name.replace("_h", "").lower()
full_filepath = "maps/" + filename + ".blk"
return cleaned_name, label_text, filename, full_filepath
def generate_label_asm(name,size=None):
cleaned_name, label_text, filename, full_filepath = make_labels(name)
output = label_text + ":"
if size: output += " ; " + str(size) + "\n"
else: output += "\n"
output += spacing + "INCBIN \"" + full_filepath + "\""
return output
def insert_map_block_label(map_id):
map = extract_maps.map_headers[map_id]
address = int(map["map_pointer"], 16)
y = int(map["y"], 16)
x = int(map["x"], 16)
size = x*y
print "map name: " + map["name"]
print "map address: " + map["map_pointer"]
line_number = find_incbin_to_replace_for(address)
if line_number == None:
print "skipping map id=" + str(map_id) + " probably because it was already done."
used_map_pointers.append(map["map_pointer"])
return
newlines = split_incbin_line_into_three(line_number, address, size)
label_asm = generate_label_asm(map["name"], size)
newlines = newlines.split("\n")
if len(newlines) == 2: index = 0 #replace the 1st line with new content
elif len(newlines) == 3: index = 1 #replace the 2nd line with new content
newlines[index] = label_asm
if len(newlines) == 3 and newlines[2][-2:] == "$0":
#get rid of the last incbin line if it is only including 0 bytes
del newlines[2]
#note that this has to be done after adding in the new asm
newlines = "\n".join(line for line in newlines)
#fix a lame error from somewhere
newlines = newlines.replace("$x", "$")
diff = generate_diff_insert(line_number, newlines)
print diff
print "... Applying diff."
#write the diff to a file
fh = open("temp.patch", "w")
fh.write(diff)
fh.close()
#apply the patch
os.system("patch ../pokered.asm temp.patch")
#remove the patch
os.system("rm temp.patch")
#confirm it's working
subprocess.check_call("cd ../; make clean; LC_CTYPE=UTF-8 make", shell=True)
def get_all_map_blockdata():
for map in extract_maps.map_headers.keys():
extract_map_block_data(map)
def insert_all_labels():
"this is very buggy, don't use it"
#limit = 200 #0:150
for map in extract_maps.map_headers.keys():
mapmap = extract_maps.map_headers[map]
if mapmap["name"] == "FREEZE": continue #skip this one
if "Ash's" in mapmap["name"]: continue
if "Gary's" in mapmap["name"]: continue
if not ("cat" in mapmap["name"]) and "copy" in mapmap["name"].lower(): continue #skip this one
#bill's house breaks things?
#if mapmap["name"] == "Bill's House": continue
if mapmap["name"] == "Viridian Forest": continue
#if mapmap["name"] == "Cerulean Mart": continue
if mapmap["name"] == "Virdian Forest Exit": continue
#if "copy" in mapmap["name"].lower(): continue #skip this one too..
if mapmap["map_pointer"] in used_map_pointers: continue #skip for sure
#reset asm
analyze_incbins.asm = None
analyze_incbins.incbin_lines = []
analyze_incbins.processed_incbins = {}
#reload asm each time
load_asm()
#check if this label is already in there
cleaned_name, label_text, filename, full_filepath = make_labels(mapmap["name"])
if label_text in "\n".join(line for line in analyze_incbins.asm):
print "skipping (found label text in asm already)"
used_map_pointers.append(mapmap["map_pointer"])
continue #skip this one
isolate_incbins()
process_incbins()
print "XYZ|" + mapmap["name"]
insert_map_block_label(map)
used_map_pointers.append(mapmap["map_pointer"])
if __name__ == "__main__":
#load map headers
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
#load incbins
load_asm()
isolate_incbins()
process_incbins()
#extract_map_block_data(2)
#get_all_map_blockdata()
#insert_map_block_label(49)
insert_all_labels()

View File

@ -1,4 +0,0 @@
import os
#main dir of repo (simply one level up than here)
pokered_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

View File

@ -1,755 +0,0 @@
# -*- coding: utf-8 -*-
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-02
#purpose: dump asm for each map header
import json
import extract_maps
import sprite_helper
import random
import string
import analyze_texts #hopefully not a dependency loop
base = 16
spacing = " "
all_texts = None
#map constants
map_constants = [
["PALLET_TOWN", 0x00],
["VIRIDIAN_CITY", 0x01],
["PEWTER_CITY", 0x02],
["CERULEAN_CITY", 0x03],
["LAVENDER_TOWN", 0x04],
["VERMILION_CITY", 0x05],
["CELADON_CITY", 0x06],
["FUCHSIA_CITY", 0x07],
["CINNABAR_ISLAND", 0x08],
["INDIGO_PLATEAU", 0x09],
["SAFFRON_CITY", 0x0A],
["ROUTE_1", 0x0C],
["ROUTE_2", 0x0D],
["ROUTE_3", 0x0E],
["ROUTE_4", 0x0F],
["ROUTE_5", 0x10],
["ROUTE_6", 0x11],
["ROUTE_7", 0x12],
["ROUTE_8", 0x13],
["ROUTE_9", 0x14],
["ROUTE_10", 0x15],
["ROUTE_11", 0x16],
["ROUTE_12", 0x17],
["ROUTE_13", 0x18],
["ROUTE_14", 0x19],
["ROUTE_15", 0x1A],
["ROUTE_16", 0x1B],
["ROUTE_17", 0x1C],
["ROUTE_18", 0x1D],
["ROUTE_19", 0x1E],
["ROUTE_20", 0x1F],
["ROUTE_21", 0x20],
["ROUTE_22", 0x21],
["ROUTE_23", 0x22],
["ROUTE_24", 0x23],
["ROUTE_25", 0x24],
["REDS_HOUSE_1F", 0x25],
["REDS_HOUSE_2F", 0x26],
["BLUES_HOUSE", 0x27],
["OAKS_LAB", 0x28],
["VIRIDIAN_POKECENTER", 0x29],
["VIRIDIAN_MART", 0x2A],
["VIRIDIAN_SCHOOL", 0x2B],
["VIRIDIAN_HOUSE", 0x2C],
["VIRIDIAN_GYM", 0x2D],
["DIGLETTS_CAVE_EXIT", 0x2E],
["VIRIDIAN_FOREST_EXIT", 0x2F],
["ROUTE_2_HOUSE", 0x30],
["ROUTE_2_GATE", 0x31],
["VIRIDIAN_FOREST_ENTRANCE", 0x32],
["VIRIDIAN_FOREST", 0x33],
["MUSEUM_1F", 0x34],
["MUSEUM_2F", 0x35],
["PEWTER_GYM", 0x36],
["PEWTER_HOUSE_1", 0x37],
["PEWTER_MART", 0x38],
["PEWTER_HOUSE_2", 0x39],
["PEWTER_POKECENTER", 0x3A],
["MT_MOON_1", 0x3B],
["MT_MOON_2", 0x3C],
["MT_MOON_3", 0x3D],
["TRASHED_HOUSE", 0x3E],
["CERULEAN_HOUSE", 0x3F],
["CERULEAN_POKECENTER", 0x40],
["CERULEAN_GYM", 0x41],
["BIKE_SHOP", 0x42],
["CERULEAN_MART", 0x43],
["MT_MOON_POKECENTER", 0x44],
["ROUTE_5_GATE", 0x46],
["PATH_ENTRANCE_ROUTE_5", 0x47],
["DAYCAREM", 0x48],
["ROUTE_6_GATE", 0x49],
["PATH_ENTRANCE_ROUTE_6", 0x4A],
["ROUTE_7_GATE", 0x4C],
["PATH_ENTRANCE_ROUTE_7", 0x4D],
["ROUTE_8_GATE", 0x4F],
["PATH_ENTRANCE_ROUTE_8", 0x50],
["ROCK_TUNNEL_POKECENTER", 0x51],
["ROCK_TUNNEL_1", 0x52],
["POWER_PLANT", 0x53],
["ROUTE_11_GATE_1F", 0x54],
["DIGLETTS_CAVE_ENTRANCE", 0x55],
["ROUTE_11_GATE_2F", 0x56],
["ROUTE_12_GATE", 0x57],
["BILLS_HOUSE", 0x58],
["VERMILION_POKECENTER", 0x59],
["POKEMON_FAN_CLUB", 0x5A],
["VERMILION_MART", 0x5B],
["VERMILION_GYM", 0x5C],
["VERMILION_HOUSE_1", 0x5D],
["VERMILION_DOCK", 0x5E],
["SS_ANNE_1", 0x5F],
["SS_ANNE_2", 0x60],
["SS_ANNE_3", 0x61],
["SS_ANNE_4", 0x62],
["SS_ANNE_5", 0x63],
["SS_ANNE_6", 0x64],
["SS_ANNE_7", 0x65],
["SS_ANNE_8", 0x66],
["SS_ANNE_9", 0x67],
["SS_ANNE_10", 0x68],
["VICTORY_ROAD_1", 0x6C],
["LANCES_ROOM", 0x71],
["HALL_OF_FAME", 0x76],
["UNDERGROUND_PATH_NS", 0x77],
["CHAMPIONS_ROOM", 0x78],
["UNDERGROUND_PATH_WE", 0x79],
["CELADON_MART_1", 0x7A],
["CELADON_MART_2", 0x7B],
["CELADON_MART_3", 0x7C],
["CELADON_MART_4", 0x7D],
["CELADON_MART_5", 0x7E],
["CELADON_MART_6", 0x7F],
["CELADON_MANSION_1", 0x80],
["CELADON_MANSION_2", 0x81],
["CELADON_MANSION_3", 0x82],
["CELADON_MANSION_4", 0x83],
["CELADON_MANSION_5", 0x84],
["CELADON_POKECENTER", 0x85],
["CELADON_GYM", 0x86],
["GAME_CORNER", 0x87],
["CELADON_HOUSE", 0x88],
["CELADONPRIZE_ROOM", 0x89],
["CELADON_DINER", 0x8A],
["CELADON_HOUSE_2", 0x8B],
["CELADONHOTEL", 0x8C],
["LAVENDER_POKECENTER", 0x8D],
["POKEMONTOWER_1", 0x8E],
["POKEMONTOWER_2", 0x8F],
["POKEMONTOWER_3", 0x90],
["POKEMONTOWER_4", 0x91],
["POKEMONTOWER_5", 0x92],
["POKEMONTOWER_6", 0x93],
["POKEMONTOWER_7", 0x94],
["LAVENDER_HOUSE_1", 0x95],
["LAVENDER_MART", 0x96],
["LAVENDER_HOUSE_2", 0x97],
["FUCHSIA_MART", 0x98],
["FUCHSIA_HOUSE_1", 0x99],
["FUCHSIA_POKECENTER", 0x9A],
["FUCHSIA_HOUSE_2", 0x9B],
["SAFARIZONEENTRANCE", 0x9C],
["FUCHSIA_GYM", 0x9D],
["FUCHSIAMEETINGROOM", 0x9E],
["SEAFOAM_ISLANDS_2", 0x9F],
["SEAFOAM_ISLANDS_3", 0xA0],
["SEAFOAM_ISLANDS_4", 0xA1],
["SEAFOAM_ISLANDS_5", 0xA2],
["VERMILION_HOUSE_2", 0xA3],
["FUCHSIA_HOUSE_3", 0xA4],
["MANSION_1", 0xA5],
["CINNABAR_GYM", 0xA6],
["CINNABAR_LAB_1", 0xA7],
["CINNABAR_LAB_2", 0xA8],
["CINNABAR_LAB_3", 0xA9],
["CINNABAR_LAB_4", 0xAA],
["CINNABAR_POKECENTER", 0xAB],
["CINNABAR_MART", 0xAC],
["INDIGO_PLATEAU_LOBBY", 0xAE],
["COPYCATS_HOUSE_1F", 0xAF],
["COPYCATS_HOUSE_2F", 0xB0],
["FIGHTINGDOJO", 0xB1],
["SAFFRON_GYM", 0xB2],
["SAFFRON_HOUSE_1", 0xB3],
["SAFFRON_MART", 0xB4],
["SILPH_CO_1F", 0xB5],
["SAFFRON_POKECENTER", 0xB6],
["SAFFRON_HOUSE_2", 0xB7],
["ROUTE_15_GATE", 0xB8],
["ROUTE_16_GATE_1F", 0xBA],
["ROUTE_16_GATE_2F", 0xBB],
["ROUTE_16_HOUSE", 0xBC],
["ROUTE_12_HOUSE", 0xBD],
["ROUTE_18_GATE_1F", 0xBE],
["ROUTE_18_GATE_2F", 0xBF],
["SEAFOAM_ISLANDS_1", 0xC0],
["ROUTE_22_GATE", 0xC1],
["VICTORY_ROAD_2", 0xC2],
["ROUTE_12_GATE_2F", 0xC3],
["VERMILION_HOUSE_3", 0xC4],
["DIGLETTS_CAVE", 0xC5],
["VICTORY_ROAD_3", 0xC6],
["ROCKET_HIDEOUT_1", 0xC7],
["ROCKET_HIDEOUT_2", 0xC8],
["ROCKET_HIDEOUT_3", 0xC9],
["ROCKET_HIDEOUT_4", 0xCA],
["ROCKET_HIDEOUT_ELEVATOR", 0xCB],
["SILPH_CO_2F", 0xCF],
["SILPH_CO_3F", 0xD0],
["SILPH_CO_4F", 0xD1],
["SILPH_CO_5F", 0xD2],
["SILPH_CO_6F", 0xD3],
["SILPH_CO_7F", 0xD4],
["SILPH_CO_8F", 0xD5],
["MANSION_2", 0xD6],
["MANSION_3", 0xD7],
["MANSION_4", 0xD8],
["SAFARI_ZONE_EAST", 0xD9],
["SAFARI_ZONE_NORTH", 0xDA],
["SAFARI_ZONE_WEST", 0xDB],
["SAFARI_ZONE_CENTER", 0xDC],
["SAFARI_ZONE_REST_HOUSE_1", 0xDD],
["SAFARI_ZONE_SECRET_HOUSE", 0xDE],
["SAFARI_ZONE_REST_HOUSE_2", 0xDF],
["SAFARI_ZONE_REST_HOUSE_3", 0xE0],
["SAFARI_ZONE_REST_HOUSE_4", 0xE1],
["UNKNOWN_DUNGEON_2", 0xE2],
["UNKNOWN_DUNGEON_3", 0xE3],
["UNKNOWN_DUNGEON_1", 0xE4],
["NAME_RATERS_HOUSE", 0xE5],
["CERULEAN_HOUSE_3", 0xE6],
["ROCK_TUNNEL_2", 0xE8],
["SILPH_CO_9F", 0xE9],
["SILPH_CO_10F", 0xEA],
["SILPH_CO_11F", 0xEB],
["SILPH_CO_ELEVATOR", 0xEC],
["BATTLE_CENTER", 0xEF],
["TRADE_CENTER", 0xF0],
["LORELEIS_ROOM", 0xF5],
["BRUNOS_ROOM", 0xF6],
["AGATHAS_ROOM", 0xF7],
["BEACH_HOUSE", 0xF8]]
#i prefer a different data structure
temp = {}
for constant in map_constants:
value = constant[1]
name = constant[0]
temp[value] = name
map_constants = temp
del temp
#these appear outside of quotes
constant_abbreviation_bytes = {
}
#these appear in quotes
char_conversion = [
(" ", 0x7F),
("A", 0x80),
("B", 0x81),
("C", 0x82),
("D", 0x83),
("E", 0x84),
("F", 0x85),
("G", 0x86),
("H", 0x87),
("I", 0x88),
("J", 0x89),
("K", 0x8A),
("L", 0x8B),
("M", 0x8C),
("N", 0x8D),
("O", 0x8E),
("P", 0x8F),
("Q", 0x90),
("R", 0x91),
("S", 0x92),
("T", 0x93),
("U", 0x94),
("V", 0x95),
("W", 0x96),
("X", 0x97),
("Y", 0x98),
("Z", 0x99),
("(", 0x9A),
(")", 0x9B),
(":", 0x9C),
(";", 0x9D),
("[", 0x9E),
("]", 0x9F),
("a", 0xA0),
("b", 0xA1),
("c", 0xA2),
("d", 0xA3),
("e", 0xA4),
("f", 0xA5),
("g", 0xA6),
("h", 0xA7),
("i", 0xA8),
("j", 0xA9),
("k", 0xAA),
("l", 0xAB),
("m", 0xAC),
("n", 0xAD),
("o", 0xAE),
("p", 0xAF),
("q", 0xB0),
("r", 0xB1),
("s", 0xB2),
("t", 0xB3),
("u", 0xB4),
("v", 0xB5),
("w", 0xB6),
("x", 0xB7),
("y", 0xB8),
("z", 0xB9),
("é", 0xBA),
("'d", 0xBB),
("'l", 0xBC),
("'s", 0xBD),
("'t", 0xBE),
("'v", 0xBF),
("'", 0xE0),
("-", 0xE3),
("'r", 0xE4),
("'m", 0xE5),
("?", 0xE6),
("!", 0xE7),
(".", 0xE8),
("", 0xEF),
#("¥", 0xF0),
("/", 0xF3),
(",", 0xF4),
("", 0xF5),
("0", 0xF6),
("1", 0xF7),
("2", 0xF8),
("3", 0xF9),
("4", 0xFA),
("5", 0xFB),
("6", 0xFC),
("7", 0xFD),
("8", 0xFE),
("9", 0xFF)]
#these appear in quotes
txt_bytes = {
0x50: "@",
0x54: "#",
0x75: "",
}
for item in char_conversion:
txt_bytes[item[1]] = item[0]
del char_conversion
#this was originally for renaming freeze maps for a unique name
def random_hash():
available_chars = string.hexdigits[:16]
return ''.join(
random.choice(available_chars)
for dummy in xrange(5))
def offset_to_pointer(offset):
if type(offset) == str: offset = int(offset, base)
return int(offset) % 0x4000 + 0x4000
def map_name_cleaner(name, id):
"names have to be acceptable asm labels"
#duck out early
if name == "FREEZE":
#name += "_" + random_hash() + "_h"
name += "_" + str(id) + "_h"
return name
#the long haul
name = name.replace(":", "")
name = name.replace("(", "")
name = name.replace(")", "")
name = name.replace("'", "")
name = name.replace("/", "") #N/S -> NS, W/E -> WE
name = name.replace(".", "") #S.S. -> SS, Mt. -> Mt
name = name.replace(" ", "") #or '_' ??
name = name + "_h"
return name
def write_connections(north, south, west, east):
#north 0, south 1, west 2, east 3
if north and south and west and east: return "NORTH | SOUTH | WEST | EAST"
if north and south and west and not east: return "NORTH | SOUTH | WEST"
if north and south and not west and east: return "NORTH | SOUTH | EAST"
if north and not south and west and east: return "NORTH | WEST | EAST"
if not north and south and west and east: return "SOUTH | WEST | EAST"
if north and south and not west and not east: return "NORTH | SOUTH"
if not north and not south and west and east: return "WEST | EAST"
if not north and south and west and not east: return "SOUTH | WEST"
if not north and south and not west and east: return "SOUTH | EAST"
if north and not south and west and not east: return "NORTH | WEST"
if north and not south and not west and east: return "NORTH | EAST"
raise Exception, "unpredicted outcome on write_connections"
#TODO: make this elegant
def connection_line(byte):
if type(byte) == str:
byte = int(byte, base)
connections = 0
north, south, west, east = False, False, False, False
temp = bin(byte)[2:]
if len(temp) == 1:
if temp[0] == "1": #EAST
east = True
elif len(temp) == 2:
if temp[0] == "1": #WEST
west = True
if temp[1] == "1": #EAST
east = True
elif len(temp) == 3:
if temp[0] == "1": #SOUTH
south = True
if temp[1] == "1": #WEST
west = True
if temp[2] == "1": #EAST
east = True
elif len(temp) == 4:
if temp[0] == "1": #NORTH
north = True
if temp[1] == "1": #SOUTH
south = True
if temp[2] == "1": #WEST
west = True
if temp[3] == "1": #EAST
east = True
if north: connections += 1
if south: connections += 1
if west: connections += 1
if east: connections += 1
#i don't have time to optimize this
if connections == 0:
return "$00"
if connections == 1:
if north: return "NORTH"
if south: return "SOUTH"
if west: return "WEST"
if east: return "EAST"
if connections >= 2:
return write_connections(north, south, west, east)
def connection_pretty_printer(connections):
#map_id, connected_map_tile_pointer, current_map_tile_pointer, bigness, width, y, x, window_pointer
output = ""
for connection in connections.keys():
connection = connections[connection]
map_id = hex(connection["map_id"])[2:].zfill(2)
connected_map_tile_pointer = connection["connected_map_tile_pointer"][2:]
current_map_tile_pointer = connection["current_map_tile_pointer"][2:]
bigness = hex(int(connection["bigness"], base))[2:].zfill(2)
width = hex(int(connection["width"], base))[2:].zfill(2)
y = hex(connection["y"])[2:].zfill(2)
x = hex(connection["x"])[2:].zfill(2)
window_pointer = connection["window_pointer"][2:]
output += spacing + "db $" + map_id + " ; some map\n"
output += spacing + "dw $" + connected_map_tile_pointer + ", $" + current_map_tile_pointer + " ; pointers (connected, current) (strip)\n"
output += spacing + "db $" + bigness + ", $" + width + " ; bigness, width\n"
output += spacing + "db $" + y + ", $" + x + " ; alignments (y, x)\n"
output += spacing + "dw $" + window_pointer + " ; window\n\n"
return output
def map_header_pretty_printer(map_header):
address = map_header["address"]
bank = map_header["bank"]
id = map_header["id"]
name = map_header["name"]
asm_name = map_name_cleaner(name, id)
if name == "FREEZE": return "" #skip freeze maps
tileset = map_header["tileset"][2:]
y = int(map_header["y"], base)
x = int(map_header["x"], base)
map_pointer = map_header["map_pointer"]
texts_pointer = map_header["texts_pointer"]
script_pointer = map_header["script_pointer"]
connection_byte = map_header["connection_byte"]
connections = map_header["connections"]
object_data_pointer = map_header["object_data_pointer"]
byte_size = 12 + (11 * len(connections.keys()))
map_pointer = hex(offset_to_pointer(map_pointer))[2:]
texts_pointer = hex(offset_to_pointer(texts_pointer))[2:]
script_pointer = hex(offset_to_pointer(script_pointer))[2:]
object_data_pointer = hex(offset_to_pointer(object_data_pointer))[2:]
#formatting: hex(y)[2:].zill(2) or "%02x" % (y,)
output = asm_name + ": ; " + address + " to " + hex(int(address, base) + byte_size) + " (" + str(byte_size) + " bytes) (id=" + str(id) + ")\n"
output += spacing + "db $" + str(tileset).zfill(2) + " ; tileset\n"
output += spacing + "db $" + hex(y)[2:].zfill(2) + ", $" + hex(x)[2:].zfill(2) + " ; dimensions (y, x)\n"
output += spacing + "dw $" + map_pointer + ", $" + texts_pointer + ", $" + script_pointer + " ; blocks, texts, scripts\n"
output += spacing + "db " + connection_line(connection_byte) + " ; connections\n\n"
if len(connections) > 0:
output += spacing + "; connections data\n\n"
output += connection_pretty_printer(connections)
output += spacing + "; end connection data\n\n"
#TODO: print out label for object_data_pointer if it's already in the file
output += spacing + "dw $" + object_data_pointer + " ; objects\n"
return output
def make_object_label_name(name):
"""make a label for the asm file
like: PalletTownObject"""
name = map_name_cleaner(name, None)
return name.replace("_h", "") + "Object"
def make_text_label(map_name, id):
"""using standard object labels
for instance, PalletTownText3"""
label = map_name_cleaner(map_name, None)[:-2] + "Text" + str(id)
return label
def object_data_pretty_printer(map_id):
map = extract_maps.map_headers[map_id]
output = ""
label_name = make_object_label_name(map["name"])
object_data_pointer = map["object_data_pointer"]
object = map["object_data"]
size = extract_maps.compute_object_data_size(object)
output += label_name + ": ; " + object_data_pointer + " (size=" + str(size) + ")\n"
output += spacing + "db $" + hex(object["maps_border_tile"])[2:] + " ; border tile\n"
output += "\n"
output += spacing + "db $" + hex(int(object["number_of_warps"]))[2:] + " ; warps\n"
#warps
for warp_id in object["warps"]:
warp = object["warps"][warp_id]
y = warp["y"]
x = warp["x"]
warp_to_point = warp["warp_to_point"]
warp_to_map_id = warp["warp_to_map_id"]
try:
warp_to_map_constant = map_constants[warp_to_map_id]
except Exception, exc:
warp_to_map_constant = "$" + hex(warp_to_map_id)[2:]
output += spacing + "db $" + hex(int(y))[2:] + ", $" + hex(int(x))[2:] + ", $" + hex(int(warp_to_point))[2:] + ", " + warp_to_map_constant + "\n"
output += "\n"
output += spacing + "db $" + hex(int(object["number_of_signs"]))[2:] + " ; signs\n"
#signs
for sign_id in object["signs"]:
sign = object["signs"][sign_id]
y = sign["y"]
x = sign["x"]
text_id = sign["text_id"]
output += spacing + "db $" + hex(int(y))[2:] + ", $" + hex(int(x))[2:] + ", $" + hex(int(text_id))[2:] + " ; " + make_text_label(map["name"], text_id) + "\n"
output += "\n"
output += spacing + "db $" + hex(int(object["number_of_things"]))[2:] + " ; people\n"
#people
for thing_id in object["things"]:
thing = object["things"][thing_id]
ending = ""
if thing["type"] == "item":
ending = ", $" + hex(int(thing["item_number"]))[2:] + " ; item\n"
elif thing["type"] == "trainer":
ending = ", $" + hex(int(thing["trainer_type"]))[2:] + ", $" + hex(int(thing["pokemon_set"]))[2:] + " ; trainer\n"
else:
ending = " ; person\n"
picture_number = hex(int(thing["picture_number"]))[2:]
y = hex(int(thing["y"]) - 4)[2:]
x = hex(int(thing["x"]) - 4)[2:]
movement1 = hex(int(thing["movement1"]))[2:]
movement2 = hex(int(thing["movement2"]))[2:]
text_id = hex(int(thing["original_text_string_number"]))[2:]
output += spacing + "db " + sprite_helper.sprites[thing["picture_number"]] + ", $" + y + " + 4, $" + x + " + 4, $" + movement1 + ", $" + movement2 + ", $" + text_id + ending
output += "\n"
if object["number_of_warps"] > 0:
output += spacing + "; warp-to\n"
for warp_to_id in object["warp_tos"]:
warp_to = object["warp_tos"][warp_to_id]
map_width = map["x"]
warp_to_y = hex(int(warp_to["y"]))[2:]
warp_to_x = hex(int(warp_to["x"]))[2:]
try:
previous_location = map_constants[object["warps"][warp_to_id]["warp_to_map_id"]]
comment = " ; " + previous_location
except Exception, exc:
comment = ""
output += spacing + "EVENT_DISP $" + map_width[2:] + ", $" + warp_to_y + ", $" + warp_to_x + comment + "\n"
#output += spacing + "dw $" + hex(int(warp_to["event_displacement"][1]))[2:] + hex(int(warp_to["event_displacement"][0]))[2:] + "\n"
#output += spacing + "db $" + hex(int(warp_to["y"]))[2:] + ", $" + hex(int(warp_to["x"]))[2:] + "\n"
#output += "\n"
output += "\n"
while output[-1] == "\n":
output = output[:-1]
output += "\n"
return output
def find_all_tx_fars():
global all_texts
tx_fars = [] #[map_id, text_id, text_pointer, tx_far_pointer, TX_FAR]
for map_id in all_texts:
map2 = all_texts[map_id]
for text_id in map2.keys():
text = map2[text_id]
for command_id in text.keys():
command = text[command_id]
if "TX_FAR" in command.keys():
TX_FAR = command["TX_FAR"]
if TX_FAR[0]["type"] == 0x0:
tx_fars.append([map_id, text_id, analyze_texts.get_text_pointer(int(extract_maps.map_headers[map_id]["texts_pointer"], 16), text_id), command["pointer"], TX_FAR])
return tx_fars
def tx_far_label_maker(map_name, text_id):
label = "_" + map_name_cleaner(map_name, None)[:-2] + "Text" + str(text_id)
return label
def tx_far_pretty_printer(tx_far):
"pretty output for a tx_far"
map_id = tx_far[0]
map2 = extract_maps.map_headers[map_id]
text_id = tx_far[1]
text_pointer = tx_far[2]
tx_far_start_address = tx_far[3]
text_far = tx_far[4]
lines = text_far[0]["lines"]
label = tx_far_label_maker(map2["name"], text_id)
#add the ending byte on the next line
#lines[len(lines.keys())+1] = [text_far[1]["type"]]
#add the ending byte to the last line- always seems $57
lines[len(lines.keys())-1].append(text_far[1]["type"])
output = "\n"
output += label + ": ; " + hex(tx_far_start_address) + "\n"
first = True
for line_id in lines:
line = lines[line_id]
output += spacing + "db "
if first:
output += "$0, "
first = False
quotes_open = False
first_byte = True
was_byte = False
byte_count = 0
for byte in line:
if byte in txt_bytes:
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
output += txt_bytes[byte]
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
#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 += ", "
output += "$" + hex(byte)[2:]
was_byte = True
#add a comma unless it's the end of the line
#if byte_count+1 != len(line):
# output += ", "
first_byte = False
byte_count += 1
#close final quotes
if quotes_open:
output += "\""
quotes_open = False
output += "\n"
#output += "\n"
return output
def print_all_headers():
maps = []
for map in extract_maps.map_headers:
maps.append(extract_maps.map_headers[map])
maps = sorted(maps, key=lambda map: int(map["address"], base))
for map in maps:
output = map_header_pretty_printer(map)
if output != "": print output
if __name__ == "__main__":
#read binary data from file
extract_maps.load_rom()
#where are the map structs?
extract_maps.load_map_pointers()
#load map headers into memory
extract_maps.read_all_map_headers()
#load texts
all_texts = analyze_texts.analyze_texts()
#print them out
#print_all_headers()
#print out only the object data for pallet town (map 0)
#print object_data_pretty_printer(0)
#prepare to pretty print tx_fars
#first you must load all_texts
#tx_fars = find_all_tx_fars()
#for entry in tx_fars:
# print tx_far_pretty_printer(entry)

View File

@ -1,21 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-16
from optparse import OptionParser
from analyze_texts import text_pretty_printer_at
def main():
usage = "usage: %prog address label"
parser = OptionParser(usage)
(options, args) = parser.parse_args()
if len(args) == 1:
print "usage: python pretty_text.py address label"
args.append("UnnamedText_" + (args[0].replace("0x", "")))
elif len(args) != 2:
parser.error("we need both an address and a label")
address = int(args[0], 16)
label = args[1]
text_pretty_printer_at(address, label)
if __name__ == "__main__":
main()

View File

@ -1,126 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-24
from optparse import OptionParser
from gbz80disasm import load_labels, find_label
from extract_maps import calculate_pointer
import sys
spacing = "\t"
rom = None
def pretty_print_trainer_header(address, label=None):
"""make pretty text for a trainer header"""
global rom
output = ""
bank_id = 0
if address > 0x4000:
bank_id = address / 0x4000
#convert address to an integer if necessary
if type(address) == str:
if "$" in address: address = address.replace("$", "0x")
address = int(address, 16)
#label this section of asm
if label == None:
output += "TrainerHeader_" + hex(address)[2:] + ": ; 0x" + hex(address)[2:] + "\n"
else:
output += label + ": ; 0x" + hex(address)[2:] + "\n"
#flag's bit
output += spacing + "db $" + hex(ord(rom[address]))[2:] + " ; flag's bit\n"
#trainer's view range
view_range = ord(rom[address+1]) >> 4
output += spacing + "db ($" + hex(view_range)[2:] + " << 4) ; trainer's view range\n"
#flag's byte
pointer_byte1 = ord(rom[address+2])
pointer_byte2 = ord(rom[address+3])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
partial_pointer = "$%.2x" % partial_pointer
output += spacing + "dw " + partial_pointer + " ; flag's byte\n"
#TextBeforeBattle
pointer_byte1 = ord(rom[address+4])
pointer_byte2 = ord(rom[address+5])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
label = find_label(partial_pointer, bank_id)
if label == None:
print "label not found for (TextBeforeBattle) " + hex(calculate_pointer(partial_pointer, bank_id))
print ""
label = "$" + hex(partial_pointer)[2:]
#sys.exit(0)
output += spacing + "dw " + label + " ; " + hex(partial_pointer) + " TextBeforeBattle\n"
#TextAfterBattle
pointer_byte1 = ord(rom[address+6])
pointer_byte2 = ord(rom[address+7])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
label = find_label(partial_pointer, bank_id)
if label == None:
print "label not found for (TextAfterBattle) " + hex(calculate_pointer(partial_pointer, bank_id))
print ""
label = "$" + hex(partial_pointer)[2:]
#sys.exit(0)
output += spacing + "dw " + label + " ; " + hex(partial_pointer) + " TextAfterBattle\n"
#TextEndBattle
pointer_byte1 = ord(rom[address+8])
pointer_byte2 = ord(rom[address+9])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
label = find_label(partial_pointer, bank_id)
if label == None:
print "label not found for (TextEndBattle) " + hex(calculate_pointer(partial_pointer, bank_id))
print ""
label = "$" + hex(partial_pointer)[2:]
#sys.exit(0)
output += spacing + "dw " + label + " ; " + hex(partial_pointer) + " TextEndBattle\n"
#TextEndBattle
pointer_byte1 = ord(rom[address+10])
pointer_byte2 = ord(rom[address+11])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
label = find_label(partial_pointer, bank_id)
if label == None:
print "label not found for (TextEndBattle) " + hex(calculate_pointer(partial_pointer, bank_id))
print ""
label = "$" + hex(partial_pointer)[2:]
#sys.exit(0)
output += spacing + "dw " + label + " ; " + hex(partial_pointer) + " TextEndBattle\n"
output += "; " + hex(address+12) + "\n"
return output
def all_trainer_headers_at(address):
i = 0
while ord(rom[address + (i*12)]) != 0xff:
print pretty_print_trainer_header(address + (i*12))
i += 1
def main():
load_labels()
usage = "usage: %prog address"
parser = OptionParser(usage)
(options, args) = parser.parse_args()
if len(args) == 1:
print "usage: python pretty_trainer_headers.py address label\n"
args.append("TrainerHeader_" + (args[0].replace("0x", "")))
elif len(args) != 2:
parser.error("we need both an address and a label")
address = int(args[0], 16)
label = args[1]
global rom
rom = open("../baserom.gbc", "r").read()
#print pretty_print_trainer_header(address, label)
print all_trainer_headers_at(address)
if __name__ == "__main__":
main()

View File

@ -1,128 +0,0 @@
/*
* Copyright © 2011 IIMarckus <iimarckus@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This program compresses or decompresses the Town Map tilemap
* from Pokémon Red, Blue, and Yellow.
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void
usage()
{
fprintf(stderr, "Usage: redrle [-d] infile outfile\n");
exit(1);
}
int
main(int argc, char *argv[])
{
FILE *infile, *outfile;
bool d = false; /* compress or decompress flag */
if (argc < 3 || argc > 4)
usage();
if (strcmp(argv[1], "-d") == 0) {
if (argc != 4)
usage();
d = true;
}
infile = fopen(argv[argc - 2], "rb");
if (infile == NULL) {
fprintf(stderr, "Error opening file '%s': ", argv[argc - 2]);
perror(NULL);
exit(1);
}
outfile = fopen(argv[argc - 1], "wb");
if (outfile == NULL) {
fprintf(stderr, "Error opening file '%s': ", argv[argc - 1]);
perror(NULL);
exit(1);
}
if (d) { /* decompress */
for (;;) {
int i, count;
int byte = fgetc(infile);
if (byte == 0)
break;
count = byte & 0xF;
byte >>= 4;
if (feof(infile)) {
fprintf(stderr, "Decompress error: reached "
"end of file without finding terminating "
"null byte.\n");
exit(1);
}
for (i = 0; i < count;
++i)
fputc(byte, outfile);
}
} else { /* compress */
int byte, count = 0, lastbyte = 0;
for (;;) {
byte = fgetc(infile);
if (feof(infile)) {
while (count > 0xF) {
count -= 0xF;
fputc(lastbyte << 4 | 0xF, outfile);
}
if (count != 0) {
fputc(lastbyte << 4 | count, outfile);
}
break;
}
if (byte > 0xF) {
fprintf(stderr, "Compress error: read a byte "
"greater than 0xF.\n");
exit(1);
}
if (byte == lastbyte)
++count;
else {
while (count > 0xF) {
count -= 0xF;
fputc(lastbyte << 4 | 0xF, outfile);
}
if (count != 0) {
fputc(lastbyte << 4 | count, outfile);
count = 0;
}
lastbyte = byte;
count = 1;
}
}
fputc(0, outfile); /* Terminating 0x00 */
}
fclose(infile);
fclose(outfile);
return 0;
}

View File

@ -1,245 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-15
#replace dimensions with constants
import sys #for non-newline-terminated output :/
from add_map_labels_to_map_headers import find_with_start_of_line
from pretty_map_headers import map_name_cleaner, spacing, offset_to_pointer, map_constants
from connection_helper import print_connections
from ctypes import c_int8
# X/Y_Movement_Of_Connection
#~~~~~~~~~~~~~~~~~~~~~~~~~
#
# A X movement is how many map blocks there are to the left of one of your north/south connections.
# A Y movement is how many map blocks there are above your west/east connection.
#===============================================================================
# #4-#5 : Current Map Position
#===============================================================================
#
# This points to the part of the current map (further up in RAM)
# that the connection strips upperleft block is placed on the current map.
#
# ____________________
# Connection |
# Direction | Formula
# ___________|_______
#
# North: C6EB + X_Movement_of_Connection Strip
#
# South: C6EB + (Height of Map + 3) * (Width of Map + 6) +
# X_Movement_of_Connection Strip
#
# West: C6E8 + (Width of Map + 6) * (Y_Movement_of_"Connection Strip" + 3)
#
# East: C6E5 + (Width of Map + 6) * (Y_Movement_of_"Connection Strip" + 4)
asm = None
asm_lines = None
def load_asm():
global asm, asm_lines
asm = open("../main.asm", "r").read()
asm_lines = asm.split("\n")
def get_xy_movement_of_connection_strip(map_id, connection_id):
map1 = extract_maps.map_headers[map_id]
connections = map1["connections"]
connection = connections[connection_id]
direction = connection["direction"]
current_map_location = int(connection["current_map_tile_pointer"], 16)
map2 = extract_maps.map_headers[connection["map_id"]]
map2_height = int(map2["y"], 16)
map2_width = int(map2["x"], 16)
y_mov = None
#if direction == "WEST":
# y_mov = ((current_map_location - 0xC6E8) / (map2_width + 6)) - 3
#elif direction == "EAST":
# y_mov = ((current_map_location - 0xC6E5) / (map2_width + 6)) - 4
if direction in ["WEST", "EAST"]:
y_mov = c_int8(connection["y"]).value / -2
x_mov = None
#if direction == "NORTH":
# x_mov = current_map_location - 0xC6EB
#elif direction == "SOUTH":
# x_mov = current_map_location - 0xC6EB - ((map2_height + 3) * (map2_width + 6))
if direction in ["NORTH", "SOUTH"]:
x_mov = c_int8(connection["x"]).value / -2
return {"y_mov": y_mov, "x_mov": x_mov}
def find_line_starting_with(value):
global asm_lines
id = 0
for line in asm_lines:
if len(line) < len(value): continue
if line[:len(value)] == value:
return asm_lines.index(line)
id += 1
return False #not found
def current_map_position_formula(map_id, connection_id):
map1_id = map_id
map1 = extract_maps.map_headers[map_id]
connections = map1["connections"]
connection = connections[connection_id]
map1_height = int(map1["y"], 16)
map1_width = int(map1["x"], 16)
map1_name = map1["name"]
map1_name = map_name_cleaner(map1_name, None)[:-2]
direction = connection["direction"]
current_map_location = int(connection["current_map_tile_pointer"], 16)
map2_id = connection["map_id"]
map2 = extract_maps.map_headers[map2_id]
map2_name = map2["name"]
map2_name = map_name_cleaner(map2_name, None)[:-2]
map2_height = int(map2["y"], 16)
map2_width = int(map2["x"], 16)
y_mov = None
if direction == "WEST":
y_mov = ((current_map_location - 0xC6E8) / (map1_width + 6)) - 3
elif direction == "EAST":
y_mov = ((current_map_location - 0xC6E5) / (map1_width + 6)) - 4
x_mov = None
if direction == "NORTH":
x_mov = current_map_location - 0xC6EB
elif direction == "SOUTH":
x_mov = current_map_location - 0xC6EB - ((map1_height + 3) * (map1_width + 6))
formula = ""
if direction == "NORTH":
formula = "$C6EB + " + str(x_mov)
elif direction == "SOUTH":
formula = "$C6EB + (" + map1_name + "Height + 3) * (" + map1_name + "Width + 6) + " + str(x_mov)
elif direction == "WEST":
formula = "$C6E8 + (" + map1_name + "Width + 6) * (" + str(y_mov) + " + 3)"
elif direction == "EAST":
formula = "$C6E5 + (" + map1_name + "Width + 6) * (" + str(y_mov) + " + 4)"
return formula
def replace_values():
global asm_lines #0-15 ok
for map_id in [3]: #extract_maps.map_headers.keys():
if map_id in extract_maps.bad_maps: continue #skip
if map_id == 12: continue #skip Route 1
map1 = extract_maps.map_headers[map_id]
label_name = map_name_cleaner(map1["name"], None)
clean_name = label_name[:-2]
line_number = find_line_starting_with(label_name)
if line_number == False: continue #skip, not found
#replace dimensions if necessary
if "dimensions" in asm_lines[line_number + 2] and "$" in asm_lines[line_number + 2] and not "\t" in asm_lines[line_number+2]:
asm_lines[line_number + 2] = spacing + "db " + clean_name + "Height, " + clean_name + "Width ; dimensions (y, x)"
#skip the rest of this if there are no connections
if len(map1["connections"]) == 0: continue
if not "; connections data" in asm_lines[line_number + 6]: continue
connection_offset = line_number + 8
for connection_id in map1["connections"]:
connection = map1["connections"][connection_id]
direction = connection["direction"]
map2_id = connection["map_id"]
map2 = extract_maps.map_headers[map2_id]
map2_name = map_name_cleaner(map2["name"], None)[:-2]
map2_height = int(map2["y"], 16)
map2_width = int(map2["x"], 16)
movements = get_xy_movement_of_connection_strip(map_id, connection_id)
y_mov = movements["y_mov"]
x_mov = movements["x_mov"]
#replace the first two pointers
if " dw " in asm_lines[connection_offset + 1]:
formula = print_connections(map_id, in_connection_id=connection_id)
formula2 = current_map_position_formula(map_id, connection_id)
temp_line = asm_lines[connection_offset + 1]
temp_line = spacing + "dw " + formula + " ; connection strip location\n" #connection strip location
temp_line += spacing + "dw " + formula2 + " ; current map position" #current map position
asm_lines[connection_offset + 1] = temp_line
#bigness, width
if "bigness, width" in asm_lines[connection_offset + 2]:
temp_line = spacing + "db "
if int(connection["bigness"],16) == map2_width:
temp_line += map2_name + "Width"
elif int(connection["bigness"],16) == map2_height:
temp_line += map2_name + "Height"
else: #dunno wtf to do
temp_line += "$" + hex(int(connection["bigness"],16))[2:]
#if direction in ["NORTH", "SOUTH"]:
# temp_line += map2_name + "Width"
#elif direction in ["WEST", "EAST"]:
# temp_line += map2_name + "Height"
temp_line += ", " + map2_name + "Width"
temp_line += " ; bigness, width"
asm_lines[connection_offset + 2] = temp_line
#alignments (y, x)
if "alignments (y, x)" in asm_lines[connection_offset + 3]:
temp_line = spacing + "db "
if direction == "NORTH":
temp_line += "(" + map2_name + "Height * 2) - 1"
elif direction == "SOUTH":
temp_line += "0"
elif direction in ["WEST", "EAST"]:
#TODO: this might be y_mov/4 ??
temp_line += "(" + str(y_mov) + " * -2)"
temp_line += ", "
#Relative X-Position of player after entering connected map.
if direction in ["NORTH", "SOUTH"]:
temp_line += "(" + str(x_mov) + " * -2)"
elif direction == "WEST":
temp_line += "(" + map2_name + "Width * 2) - 1"
elif direction == "EAST":
temp_line += "0"
temp_line += " ; alignments (y, x)"
asm_lines[connection_offset + 3] = temp_line
#window
if "; window" in asm_lines[connection_offset + 4]:
temp_line = spacing + "dw "
if direction == "NORTH":
temp_line += "$C6E9 + " + map2_name + "Height * (" + map2_name + "Width + 6)"
elif direction in ["SOUTH", "EAST"]:
temp_line += "$C6EF + " + map2_name + "Width"
elif direction == "WEST":
temp_line += "$C6EE + 2 * " + map2_name + "Width"
temp_line += " ; window"
asm_lines[connection_offset + 4] = temp_line
#jump to the next connection
connection_offset += 6
if __name__ == "__main__":
import extract_maps
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
load_asm()
replace_values()
sys.stdout.write("\n".join(asm_lines))

View File

@ -1,28 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-13
import os
changeset_numbers = range(1145, 1149)
def take_snapshot_image(changeset_number):
"turn main.asm into an image at a certain version"
print "reverting main.asm to r" + str(changeset_number)
#revert the file (it used to be common.asm)
os.system("rm ../main.asm; rm ../common.asm; rm ../pokered.asm")
os.system("hg revert ../main.asm -r" + str(changeset_number))
os.system("hg revert ../common.asm -r" + str(changeset_number))
os.system("hg revert ../pokered.asm -r" + str(changeset_number))
print "generating the image.."
#draw the image
os.system("python romviz.py")
#move the file
os.system("mv test.png versions/" + str(changeset_number) + ".png")
for changeset_number in changeset_numbers:
take_snapshot_image(changeset_number)

View File

@ -1,40 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-10
#show me an image
import Image
from math import floor
import extract_maps
import analyze_incbins
print "loading rom.."
extract_maps.load_rom()
#extract_maps.load_map_pointers()
#extract_maps.read_all_map_headers()
print "analyzing incbins.."
analyze_incbins.load_asm()
analyze_incbins.isolate_incbins()
analyze_incbins.process_incbins()
width = 1024
height = 1024
im = Image.new("P", (width, height), 0)
im.putpalette([
0, 0, 0,
126, 30, 156,
])
print "drawing incbins..."
for incbin_key in analyze_incbins.processed_incbins:
incbin = analyze_incbins.processed_incbins[incbin_key]
start = incbin["start"]
end = incbin["end"]
for pos in range(start, end+1):
widthx = int(pos % width)
heighty = int(floor(pos / height))
im.putpixel((widthx, heighty), 1)
im.save("test.png")

View File

@ -1,402 +0,0 @@
import extract_maps
spacing = "\t"
#provided by sawakita
#these were originally used for making the initial_icon_constants
#but the label names in constants.asm have since been edited
initial_icon_constants = {
0x01: ["Hiro", ""],
0x02: ["Rival", ""],
0x03: ["Oak", ""],
0x04: ["blonde boy", ""],
0x05: ["machoke/slowbro OW", "machoke slowbro"],
0x06: ["blonde(horse-tail-hair) girl", "blonde ponytail girl"],
0x07: ["black-hair boy 1", "black hair boy 1"],
0x08: ["little kid (F)", "little girl"],
0x09: ["bird", ""],
0x0A: ["fat bald man", "fat bald guy"],
0x0B: ["monk", ""],
0x0C: ["black-hair boy 2/Brock", "black hair boy 2"],
0x0D: ["girl", ""],
0x0E: ["hiker/angry man", "hiker"],
0x0F: ["foulard woman", "foulard woman"],
0x10: ["rich(black-hat) man", "gentleman"],
0x11: ["sister", ""],
0x12: ["motorbiker", "biker"],
0x13: ["sailor", ""],
0x14: ["cook", ""],
0x15: ["sun-glasses guy (bike seller)", "sunglasses guy"],
0x16: ["mr. fuji", ""],
0x17: ["giovanni", ""],
0x18: ["rocket guy", "rocket grunt"],
0x19: ["medium", ""],
0x1A: ["waiter", ""],
0x1B: ["erika", ""],
0x1C: ["mother (geisha)", "mom geisha"],
0x1D: ["brunette girl", ""],
0x1E: ["lance", ""],
0x1F: ["oak's aide/scientist", "oak scientist aide"],
0x20: ["oak's aide", "oak aide"],
0x21: ["punk", ""],
0x22: ["swimmer", ""],
0x23: ["white player", ""],
0x24: ["gym helper", ""],
0x25: ["old (wo)man", "old person"],
0x26: ["mart guy", ""],
0x27: ["fisher", ""],
0x28: ["old woman/medium?", "old medium woman"],
0x29: ["nurse", ""],
0x2A: ["cable-club woman", "cable club woman"],
0x2B: ["Mr. Masterball?", "mr masterball"],
0x2C: ["person that gives Lapras", "lapras giver"],
0x2D: ["semi-bald fat guy", "balding fat guy"],
0x2E: ["black hat white beard man ", ""],
0x2F: ["fat man", ""],
0x30: ["dojo guy", ""],
0x31: ["guard (cop?)", "guard cop"],
0x32: ["cop (guard)", "cop guard"],
0x33: ["mom", ""],
0x34: ["semi-bald man", "balding guy"],
0x35: ["young girl", ""],
0x36: ["gameboy kid", ""],
0x37: ["gameboy kid copy", ""],
0x38: ["clefairy-like", "clefairylike"],
0x39: ["Agatha", ""],
0x3A: ["Bruno", ""],
0x3B: ["Lorelei", ""],
0x3C: ["seel", ""],
0x3D: ["ball", ""],
0x3E: ["omanyte", ""],
0x3F: ["boulder", ""],
0x40: ["paper sheet", ""],
0x41: ["book/map/dex", ""],
0x42: ["clipboard", ""],
0x43: ["snorlax", ""],
0x44: ["old amber copy", ""],
0x45: ["old amber", ""],
0x46: ["lying old man unused 1", ""],
0x47: ["lying old man unused 2", ""],
0x48: ["lying old man", ""],
}
#somewhat more recent sprite labels
sprite_constants = {
0x01: "SPRITE_RED",
0x02: "SPRITE_BLUE",
0x03: "SPRITE_OAK",
0x04: "SPRITE_BUG_CATCHER",
0x05: "SPRITE_SLOWBRO",
0x06: "SPRITE_LASS",
0x07: "SPRITE_BLACK_HAIR_BOY_1",
0x08: "SPRITE_LITTLE_GIRL",
0x09: "SPRITE_BIRD",
0x0a: "SPRITE_FAT_BALD_GUY",
0x0b: "SPRITE_GAMBLER",
0x0c: "SPRITE_BLACK_HAIR_BOY_2",
0x0d: "SPRITE_GIRL",
0x0e: "SPRITE_HIKER",
0x0f: "SPRITE_FOULARD_WOMAN",
0x10: "SPRITE_GENTLEMAN",
0x11: "SPRITE_DAISY",
0x12: "SPRITE_BIKER",
0x13: "SPRITE_SAILOR",
0x14: "SPRITE_COOK",
0x15: "SPRITE_BIKE_SHOP_GUY",
0x16: "SPRITE_MR_FUJI",
0x17: "SPRITE_GIOVANNI",
0x18: "SPRITE_ROCKET",
0x19: "SPRITE_MEDIUM",
0x1a: "SPRITE_WAITER",
0x1b: "SPRITE_ERIKA",
0x1c: "SPRITE_MOM_GEISHA",
0x1d: "SPRITE_BRUNETTE_GIRL",
0x1e: "SPRITE_LANCE",
0x1f: "SPRITE_OAK_SCIENTIST_AIDE",
0x20: "SPRITE_OAK_AIDE",
0x21: "SPRITE_ROCKER",
0x22: "SPRITE_SWIMMER",
0x23: "SPRITE_WHITE_PLAYER",
0x24: "SPRITE_GYM_HELPER",
0x25: "SPRITE_OLD_PERSON",
0x26: "SPRITE_MART_GUY",
0x27: "SPRITE_FISHER",
0x28: "SPRITE_OLD_MEDIUM_WOMAN",
0x29: "SPRITE_NURSE",
0x2a: "SPRITE_CABLE_CLUB_WOMAN",
0x2b: "SPRITE_MR_MASTERBALL",
0x2c: "SPRITE_LAPRAS_GIVER",
0x2d: "SPRITE_WARDEN",
0x2e: "SPRITE_SS_CAPTAIN",
0x2f: "SPRITE_FISHER2",
0x30: "SPRITE_BLACKBELT",
0x31: "SPRITE_GUARD",
0x32: "SPRITE_____NOT____USED____",
0x33: "SPRITE_MOM",
0x34: "SPRITE_BALDING_GUY",
0x35: "SPRITE_YOUNG_BOY",
0x36: "SPRITE_GAMEBOY_KID",
0x37: "SPRITE_GAMEBOY_KID_COPY",
0x38: "SPRITE_CLEFAIRY",
0x39: "SPRITE_AGATHA",
0x3a: "SPRITE_BRUNO",
0x3b: "SPRITE_LORELEI",
0x3c: "SPRITE_SEEL",
0x3d: "SPRITE_BALL",
0x3e: "SPRITE_OMANYTE",
0x3f: "SPRITE_BOULDER",
0x40: "SPRITE_PAPER_SHEET",
0x41: "SPRITE_BOOK_MAP_DEX",
0x42: "SPRITE_CLIPBOARD",
0x43: "SPRITE_SNORLAX",
0x44: "SPRITE_OLD_AMBER_COPY",
0x45: "SPRITE_OLD_AMBER",
0x46: "SPRITE_LYING_OLD_MAN_UNUSED_1",
0x47: "SPRITE_LYING_OLD_MAN_UNUSED_2",
0x48: "SPRITE_LYING_OLD_MAN",
}
dont_use = [0x32, 0x44, 0x46, 0x47, 0x37]
#sprites after 0x23 have only one image
#SPRITE_BIKE_SHOP_GUY only has 1
icons = {}
unique_icons = set()
todo_sprites = {}
sprites = {}
def load_icons():
for map_id in map_headers:
if map_id in [0x0b, 0x45, 0x4b, 0x4e, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x70, 0x72, 0x73, 0x74, 0x75, 0xad, 0xcc, 0xcd, 0xce, 0xe7, 0xed, 0xee, 0xf1, 0xf2, 0xf3, 0xf4]: continue #skip
map = map_headers[map_id]
for thing_id in map["object_data"]["things"]:
thing = map["object_data"]["things"][thing_id]
pic = thing["picture_number"]
unique_icons.add(pic)
if not icons.has_key(pic): icons[pic] = []
alerter = None
if int(thing["y"])-4 > int(map["y"], 16)*2: alerter = True
if int(thing["x"])-4 > int(map["x"], 16)*2: alerter = True
icons[pic].append((map["name"] + " (id=" + str(map["id"]) + ")", thing["y"], thing["x"], alerter))
def print_appearances():
"""print appearances of each icon
see: http://diyhpl.us/~bryan/irc/pokered/sprite_appearances.txt
"""
output = ""
for icon_id in icons:
icon = icons[icon_id]
possible_name = ""
if icon_id in initial_icon_constants.keys():
possible_name = " (sawakita suggests: " + initial_icon_constants[icon_id][0] + ")"
output += "sprite " + hex(icon_id) + possible_name + ":\n"
for appearance in icon:
if appearance[3] != None: outside_alert = " !! OUTSIDE BOUNDS"
else: outside_alert = ""
output += spacing + ".. in " + appearance[0] + " at (" + str(appearance[1]) + ", " + str(appearance[2]) + ")" + outside_alert + "\n"
output += "\n"
print output
def insert_todo_sprites():
load_icons()
counter = 1
for icon in unique_icons:
if icon not in initial_icon_constants:
todo_sprites[icon] = counter
initial_icon_constants[icon] = None
counter += 1
def sprite_name_cleaner(badname):
output = "SPRITE_" + badname
output = output.replace(" ", "_")
output = output.replace("/", "_")
output = output.replace(".", "")
output = output.upper()
while output[-1] == "_":
output = output[:-1]
return output
def sprite_namer():
"makes up better constant names for each sprite"
insert_todo_sprites()
for sprite_id in initial_icon_constants:
suggestions = initial_icon_constants[sprite_id]
if suggestions == None:
sprites[sprite_id] = "SPRITE_TODO_" + str(todo_sprites[sprite_id])
continue #next please
original = suggestions[0]
if suggestions[1] != "": original = suggestions[1]
result = sprite_name_cleaner(original)
sprites[sprite_id] = result
def sprite_printer():
"""prints out a list of sprite constants to put into constants.asm
it's deprecated- use the names from the current file instead."""
for key in sprites:
line_length = len(sprites[key]) + len(" EQU $") + 2
if line_length < 40:
extra = (40 - line_length) * " "
else: extra = ""
value = hex(key)[2:]
if len(value) == 1: value = "0" + value
print sprites[key] + extra + " EQU $" + value
def parse_sprite_sheet_pointer_table():
"""parses the bytes making up the pointer table
first two bytes are the pointer
third byte is the number of bytes (1 * 4 tiles * 16 bytes each, or 3 * 4 tiles * 16 bytes per tile)
1 = 1 pose
3 = 3 poses, possibly 6 immediately after
$C0 or $40
fourth byte is the rom bank
so a quick estimation is that, if it has 3, and there's no other pointer that points to the one after the 3rd & next 3, then assume those next 3 are the 4th, 5th and 6th
"""
rom = extract_maps.rom
ptable_address = 0x17b27 #5:7b27
ptable_pointers = []
ptable_sheet_data = {}
#load up pointers please
for sprite_id in sprite_constants.keys():
pointer_offset = 0x17b27 + ((sprite_id -1) * 4)
pointer_byte1 = ord(rom[pointer_offset])
pointer_byte2 = ord(rom[pointer_offset+1])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
bank = ord(rom[pointer_offset+3])
pointer = extract_maps.calculate_pointer(partial_pointer, bank)
ptable_pointers.append(pointer)
#72 sprite pointers, we're not using id=$32
for sprite_id in sprite_constants.keys():
sprite_name = sprite_constants[sprite_id]
#some basic information about this sprite first
data_entry = {"sprite_id": sprite_id, "sprite_name": sprite_name}
#calculate where it is in the 0x17b27 pointer table
pointer_offset = 0x17b27 + ((sprite_id -1) * 4)
data_entry["sprite_ptr_table_entry_address"] = pointer_offset
#actual sprite pointer
pointer_byte1 = ord(rom[pointer_offset])
pointer_byte2 = ord(rom[pointer_offset+1])
partial_pointer = (pointer_byte1 + (pointer_byte2 << 8))
bank = ord(rom[pointer_offset+3])
pointer = extract_maps.calculate_pointer(partial_pointer, bank)
data_entry["pointer"] = pointer
data_entry["bank"] = bank
byte_count = ord(rom[pointer_offset+2])
data_entry["byte_count"] = byte_count
has_more_text = ""
data_entry["poses"] = 1
if byte_count == 0xc0: #has at least 3 poses
setter1, setter2, setter3 = False, False, False
data_entry["poses"] = 3
#let's check if there's possibly more
if not ((byte_count + pointer) in ptable_pointers): #yep, probably (#4)
data_entry["poses"] += 1
data_entry["byte_count"] += 64
setter1 = True
if setter1 and not ((byte_count + pointer + 64) in ptable_pointers): #has another (#5)
data_entry["poses"] += 1
data_entry["byte_count"] += 64
setter2 = True
if setter2 and not ((byte_count + pointer + 64 + 64) in ptable_pointers): #has a #6
data_entry["poses"] += 1
data_entry["byte_count"] += 64
setter3 = True
print ("$%.2x " % (sprite_id)) + sprite_name + " has $%.2x bytes" % (byte_count) + " pointing to 0x%.x" % (pointer) + " bank is $%.2x" % (bank) + " with pose_count=" + str(data_entry["poses"])
ptable_sheet_data[sprite_id] = data_entry
return ptable_sheet_data
def pretty_print_sheet_incbins(ptable_sheet_data):
"""make things look less awful"""
output = ""
used_addresses = []
for sheet_id in ptable_sheet_data:
sheet_data = ptable_sheet_data[sheet_id]
name = sheet_data["sprite_name"].split("SPRITE_")[1].lower().title()
clean_name = name.replace("_", "")
address = sheet_data["pointer"]
byte_count = sheet_data["byte_count"]
#if not (0x10000 <= address <= 0x12e7f): continue #skip
#if not (0x14180 <= address <= 0x17840): continue #skip
if address in used_addresses: continue #skip
used_addresses.append(address)
output += clean_name + "Sprite: ; 0x%.x" % (address) + "\n"
#output += spacing + "INCBIN \"baserom.gbc\",$%.x,$%.x - $%.x" % (address, address + byte_count, address) + "\n"
output += spacing + "INCBIN \"gfx/sprites/" + name.lower() + ".2bpp\" ; was $%.x" % (address) + "\n"
filename = "../gfx/sprites/" + name.lower() + ".2bpp"
#fh = open(filename, "w")
#fh.write(extract_maps.rom[address : address + byte_count])
#fh.close()
return output
def pretty_print_sheet_data(ptable_sheet_data):
"""make the pointer table not suck so much"""
output = "SpriteSheetPointerTable: ; 0x17b27\n"
used_addresses = []
for sheet_id in ptable_sheet_data:
sheet_data = ptable_sheet_data[sheet_id]
address = sheet_data["pointer"]
checker = False
for x in used_addresses:
if not checker and x[0] == address:
checker = True
clean_name = x[1]
if not checker:
name = sheet_data["sprite_name"].split("SPRITE_")[1].lower().title()
clean_name = name.replace("_", "")
clean_name += "Sprite"
byte_count = sheet_data["byte_count"]
if byte_count > 0x40:
byte_count = 0xc0
output += "\n\t; " + sprite_constants[sheet_data["sprite_id"]] + "\n"
output += spacing + "dw " + clean_name + "\n"
output += spacing + "db $%.2x ; byte count\n" % (byte_count)
output += spacing + "db BANK(" + clean_name + ")\n"
used_addresses.append((address, clean_name))
output += "; 0x17c47"
return output
if __name__ == "__main__":
extract_maps.load_rom()
#extract_maps.load_map_pointers()
#extract_maps.read_all_map_headers()
#sprite_namer()
#load_icons()
#print_appearances()
#sprite_printer()
ptable_sheet_data = parse_sprite_sheet_pointer_table()
print pretty_print_sheet_incbins(ptable_sheet_data)
print pretty_print_sheet_data(ptable_sheet_data)

View File

@ -1,55 +0,0 @@
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-03
#utilities for working with text pointers
import extract_maps #rom, assert_rom, load_rom, calculate_pointer, load_map_pointers, read_all_map_headers, map_headers
from pretty_map_headers import map_name_cleaner
#import analyze_incbins #asm, offset_to_pointer, find_incbin_to_replace_for, split_incbin_line_into_three, generate_diff_insert, load_asm, isolate_incbins, process_incbins
spacing = " "
def test_first_text_pointer_bytes(range=20): #30 for viridian city, 34 for cerulean city, 36 for celadon, 48 for fuchsia city, 50 for safron
"""
does the first text pointer byte always point to (end address of text pointer list) + 1?
range determines how far is acceptable.
r=15 means 30 text pointers
"""
for map_id in extract_maps.map_headers:
map = extract_maps.map_headers[map_id]
bank = int(map["bank"],16)
text_list_pointer = int(map["texts_pointer"], 16)
bad_names = ["FREEZE", "COPY: Cinnibar Mart", "COPY OF: Underground Tunnel Entrance (Route 6)", "COPY OF: Trashed House", "COPY OF: Underground Path Entrance (Route 7)"]
if map["name"] in bad_names: continue
#extract the bytes making up the first text pointer
pointer_byte1 = ord(extract_maps.rom[text_list_pointer])
pointer_byte2 = ord(extract_maps.rom[text_list_pointer+1])
#swap the bytes
temp = pointer_byte1
pointer_byte1 = pointer_byte2
pointer_byte2 = temp
del temp
#combine these into a single pointer
partial_pointer = (pointer_byte2 + (pointer_byte1 << 8))
#get the full pointer
first_text_pointer = extract_maps.calculate_pointer(partial_pointer, bank)
#if (first_text_pointer <= (text_list_pointer+range)):
print "map " + map["name"] + " (" + str(map["id"]) + ")"
print spacing + "text_pointer (list) = " + hex(text_list_pointer)
print spacing + "first_text_pointer (first text) = " + hex(first_text_pointer)
print spacing + "difference = " + str(first_text_pointer - text_list_pointer)
#return False
return True
if __name__ == "__main__":
extract_maps.load_rom()
extract_maps.load_map_pointers()
extract_maps.read_all_map_headers()
print test_first_text_pointer_bytes()

View File

@ -1,4 +1,4 @@
_RED EQU 0
_BLUE EQU 1
INCLUDE "wram.asm"
INCLUDE "main.tx"
INCLUDE "main.asm"

View File

@ -1,4 +1,4 @@
_RED EQU 1
_BLUE EQU 0
INCLUDE "wram.asm"
INCLUDE "main.tx"
INCLUDE "main.asm"

View File

@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
import extras.pokemontools.preprocessor as preprocessor
import sys
chars = {
@ -254,81 +256,11 @@ chars = {
"6": 0xFC,
"7": 0xFD,
"8": 0xFE,
"9": 0xFF
"9": 0xFF,
}
for l in sys.stdin:
preprocessor.chars = chars
# strip comments
line = l.partition(";")
i = 0
asm = ""
while i < len(line) and l[0] != ";":
asm = asm + line[i]
i = i + 1
# skip asm with no quotes
if "\"" not in asm:
sys.stdout.write(l)
continue
# split by quotes
asms = asm.split("\"")
# skip asm that actually does use ASCII in quotes
lowasm = asms[0].lower()
if "section" in lowasm \
or "include" in lowasm \
or "incbin" in lowasm:
sys.stdout.write(l)
continue
even = False
i = 0
for token in asms:
i = i + 1
if even:
# token is a string to convert to byte values
while len(token):
# read a single UTF-8 codepoint
char = token[0]
if ord(char) >= 0xFC:
char = char + token[1:6]
token = token[6:]
elif ord(char) >= 0xF8:
char = char + token[1:5]
token = token[5:]
elif ord(char) >= 0xF0:
char = char + token[1:4]
token = token[4:]
elif ord(char) >= 0xE0:
char = char + token[1:3]
token = token[3:]
elif ord(char) >= 0xC0:
char = char + token[1:2]
token = token[2:]
else:
token = token[1:]
# certain apostrophe-letter pairs are only a single byte
if char == "'" and \
(token[0] == "d" or \
token[0] == "l" or \
token[0] == "m" or \
token[0] == "r" or \
token[0] == "s" or \
token[0] == "t" or \
token[0] == "v"):
char = char + token[0]
token = token[1:]
sys.stdout.write("${0:02X}".format(chars[char]))
if len(token):
sys.stdout.write(", ")
else:
sys.stdout.write(token)
even = not even
macros = []
macro_table = preprocessor.make_macro_table(macros)
preprocessor.preprocess(macro_table)