diff options
Diffstat (limited to 'extras/analyze_incbins.py')
m--------- | extras | 0 | ||||
-rw-r--r-- | extras/analyze_incbins.py | 502 |
2 files changed, 0 insertions, 502 deletions
diff --git a/extras b/extras new file mode 160000 +Subproject 795cd58a70c80082003e40127241cfaefa0fae8 diff --git a/extras/analyze_incbins.py b/extras/analyze_incbins.py deleted file mode 100644 index be3c1c6a..00000000 --- a/extras/analyze_incbins.py +++ /dev/null @@ -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 - |