1 changed files with 259 additions and 0 deletions
			
			
		@ -0,0 +1,259 @@ | 
				
			|||
#!/usr/bin/env python3 | 
				
			|||
# vim:set tabstop=4 shiftwidth=4 expandtab smarttab | 
				
			|||
 | 
				
			|||
from sys import argv, exit, stderr | 
				
			|||
from struct import unpack, calcsize | 
				
			|||
from enum import Enum | 
				
			|||
 | 
				
			|||
class nodetype(Enum): | 
				
			|||
    REPT = 0 | 
				
			|||
    FILE = 1 | 
				
			|||
    MACRO = 2 | 
				
			|||
 | 
				
			|||
class symtype(Enum): | 
				
			|||
    LOCAL = 0 | 
				
			|||
    IMPORT = 1 | 
				
			|||
    EXPORT = 2 | 
				
			|||
 | 
				
			|||
class sectype(Enum): | 
				
			|||
    WRAM0 = 0 | 
				
			|||
    VRAM = 1 | 
				
			|||
    ROMX = 2 | 
				
			|||
    ROM0 = 3 | 
				
			|||
    HRAM = 4 | 
				
			|||
    WRAMX = 5 | 
				
			|||
    SRAM = 6 | 
				
			|||
    OAM = 7 | 
				
			|||
 | 
				
			|||
class patchtype(Enum): | 
				
			|||
    BYTE = 0 | 
				
			|||
    WORD = 1 | 
				
			|||
    LONG = 2 | 
				
			|||
    JR = 3 | 
				
			|||
 | 
				
			|||
class asserttype(Enum): | 
				
			|||
    WARN = 0 | 
				
			|||
    ERROR = 1 | 
				
			|||
    FAIL = 2 | 
				
			|||
 | 
				
			|||
def unpack_file(fmt, file): | 
				
			|||
    size = calcsize(fmt) | 
				
			|||
    return unpack(fmt, file.read(size)) | 
				
			|||
 | 
				
			|||
def read_string(file): | 
				
			|||
    buf = bytearray() | 
				
			|||
    while True: | 
				
			|||
        b = file.read(1) | 
				
			|||
        if b is None or b == b'\0': | 
				
			|||
            return buf.decode() | 
				
			|||
        else: | 
				
			|||
            buf += b | 
				
			|||
 | 
				
			|||
def parse_object(filename): | 
				
			|||
    file = open(filename, "rb") | 
				
			|||
 | 
				
			|||
    obj = { | 
				
			|||
        "nodes": [], | 
				
			|||
        "symbols": [], | 
				
			|||
        "sections": [], | 
				
			|||
        "assertions": [], | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    magic = unpack_file("4s", file)[0] | 
				
			|||
    if magic == b'RGB6': | 
				
			|||
        obj_ver = 6 | 
				
			|||
    elif magic == b'RGB9': | 
				
			|||
        obj_ver = 10 + unpack_file("<I", file)[0] | 
				
			|||
    else: | 
				
			|||
        print("Error: File '%s' is of an unknown format." % filename, file=stderr) | 
				
			|||
        return None | 
				
			|||
 | 
				
			|||
    if obj_ver not in [6, 10, 11, 13, 14, 16]: | 
				
			|||
        print("Error: File '%s' is of an unknown format: %d" % (filename, obj_ver), file=stderr) | 
				
			|||
        return None | 
				
			|||
 | 
				
			|||
    obj["version"] = obj_ver | 
				
			|||
 | 
				
			|||
    num_symbols, num_sections = unpack_file("<II", file) | 
				
			|||
 | 
				
			|||
    num_nodes = 0 | 
				
			|||
    if obj_ver >= 16: | 
				
			|||
        num_nodes = unpack_file("<I", file)[0] | 
				
			|||
 | 
				
			|||
    for x in range(num_nodes): | 
				
			|||
        node = {} | 
				
			|||
 | 
				
			|||
        node["parent_id"], node["parent_line"] = unpack_file("<II", file) | 
				
			|||
        node["type"] = nodetype(unpack_file("<B", file)[0]) | 
				
			|||
        if node["type"] != nodetype.REPT: | 
				
			|||
            node["name"] = read_string(file) | 
				
			|||
        else: | 
				
			|||
            depth = unpack_file("<I", file)[0] | 
				
			|||
            node["depth"] = [unpack_file("<I", file) for x in range(depth)] | 
				
			|||
        obj["nodes"].append(node) | 
				
			|||
 | 
				
			|||
    for x in range(num_symbols): | 
				
			|||
        symbol = {} | 
				
			|||
 | 
				
			|||
        symbol["name"] = read_string(file) | 
				
			|||
        symbol["type"] = symtype(unpack_file("<B", file)[0]) | 
				
			|||
        if symbol["type"] != symtype.IMPORT: | 
				
			|||
            if obj_ver >= 16: | 
				
			|||
                node = unpack_file("<I", file)[0] | 
				
			|||
                symbol["node"] = node | 
				
			|||
            else: | 
				
			|||
                symbol["filename"] = read_string(file) | 
				
			|||
            symbol["line_num"], symbol["section"], symbol["value"] = unpack_file("<III", file) | 
				
			|||
 | 
				
			|||
        obj["symbols"].append(symbol) | 
				
			|||
 | 
				
			|||
    for x in range(num_sections): | 
				
			|||
        section = {} | 
				
			|||
 | 
				
			|||
        section["name"] = read_string(file) | 
				
			|||
        size, type, section["org"], section["bank"] = unpack_file("<IBII", file) | 
				
			|||
        section["type"] = sectype(type) | 
				
			|||
 | 
				
			|||
        if obj_ver >= 14: | 
				
			|||
            section["align"], section["offset"] = unpack_file("<BI", file) | 
				
			|||
        else: | 
				
			|||
            section["align"] = unpack_file("<I", file)[0] | 
				
			|||
 | 
				
			|||
        if section["type"] == sectype.ROMX or section["type"] == sectype.ROM0: | 
				
			|||
            section["data"] = file.read(size) | 
				
			|||
 | 
				
			|||
            section["patches"] = [] | 
				
			|||
            num_patches = unpack_file("<I", file)[0] | 
				
			|||
            for x in range(num_patches): | 
				
			|||
                patch = {} | 
				
			|||
 | 
				
			|||
                if obj_ver >= 16: | 
				
			|||
                    node = unpack_file("<I", file)[0] | 
				
			|||
                    patch["node"] = node | 
				
			|||
                else: | 
				
			|||
                    patch["filename"] = read_string(file) | 
				
			|||
                if obj_ver < 11 or obj_ver >= 16: | 
				
			|||
                    patch["line"] = unpack_file("<I", file)[0] | 
				
			|||
                patch["offset"] = unpack_file("<I", file)[0] | 
				
			|||
                if obj_ver >= 14: | 
				
			|||
                    patch["pcsectionid"], patch["pcoffset"] = unpack_file("<II", file) | 
				
			|||
                type, rpn_size = unpack_file("<BI", file) | 
				
			|||
                patch["type"] = patchtype(type) | 
				
			|||
                patch["rpn"] = file.read(rpn_size) | 
				
			|||
 | 
				
			|||
                section["patches"].append(patch) | 
				
			|||
 | 
				
			|||
        obj["sections"].append(section) | 
				
			|||
 | 
				
			|||
    num_assertions = 0 | 
				
			|||
    if obj_ver >= 13: | 
				
			|||
        num_assertions = unpack_file("<I", file)[0] | 
				
			|||
 | 
				
			|||
    for x in range(num_assertions): | 
				
			|||
        assertion = {} | 
				
			|||
 | 
				
			|||
        if obj_ver >= 16: | 
				
			|||
            node = unpack_file("<I", file)[0] | 
				
			|||
            assertion["node"] = node | 
				
			|||
            assertion["line"] = unpack_file("<I", file)[0] | 
				
			|||
        else: | 
				
			|||
            assertion["filename"] = read_string(file) | 
				
			|||
        assertion["offset"] = unpack_file("<I", file)[0] | 
				
			|||
        if obj_ver >= 14: | 
				
			|||
            assertion["section"], assertion["pcoffset"] = unpack_file("<II") | 
				
			|||
        type, rpn_size = unpack_file("<BI", file) | 
				
			|||
        assertion["type"] = asserttype(type) | 
				
			|||
        assertion["rpn"] = file.read(rpn_size) | 
				
			|||
        assertion["message"] = read_string(file) | 
				
			|||
 | 
				
			|||
        obj["assertions"].append(assertion) | 
				
			|||
 | 
				
			|||
    return obj | 
				
			|||
 | 
				
			|||
 | 
				
			|||
if len(argv) <= 1: | 
				
			|||
    print("Usage: %s [-D] <objects.o...>" % argv[0], file=stderr) | 
				
			|||
    exit(1) | 
				
			|||
 | 
				
			|||
argi = 1 | 
				
			|||
 | 
				
			|||
just_dump = False | 
				
			|||
if argv[argi] == "-D": | 
				
			|||
    just_dump = True | 
				
			|||
    argi += 1 | 
				
			|||
if argv[argi] == "--": | 
				
			|||
    argi += 1 | 
				
			|||
 | 
				
			|||
globalsyms = {} | 
				
			|||
 | 
				
			|||
for filename in argv[argi:]: | 
				
			|||
    print(filename, file=stderr) | 
				
			|||
 | 
				
			|||
    obj = parse_object(filename) | 
				
			|||
    if obj is None: | 
				
			|||
        exit(1) | 
				
			|||
 | 
				
			|||
    if just_dump: | 
				
			|||
        for symbol in obj["symbols"]: | 
				
			|||
            print(symbol["name"]) | 
				
			|||
        continue | 
				
			|||
 | 
				
			|||
    localsyms = {} | 
				
			|||
 | 
				
			|||
    imports = 0 | 
				
			|||
    exports = 0 | 
				
			|||
    for symbol in obj["symbols"]: | 
				
			|||
        if symbol["type"] == symtype.LOCAL: | 
				
			|||
            if symbol["name"] not in localsyms: | 
				
			|||
                localsyms[symbol["name"]] = 0 | 
				
			|||
        elif symbol["type"] == symtype.IMPORT: | 
				
			|||
            imports += 1 | 
				
			|||
            if symbol["name"] not in globalsyms: | 
				
			|||
                globalsyms[symbol["name"]] = 0 | 
				
			|||
        elif symbol["type"] == symtype.EXPORT: | 
				
			|||
            exports += 1 | 
				
			|||
            if symbol["name"] not in globalsyms: | 
				
			|||
                globalsyms[symbol["name"]] = 0 | 
				
			|||
 | 
				
			|||
    print("Locals:", len(localsyms), file=stderr) | 
				
			|||
    print("Imports:", imports, file=stderr) | 
				
			|||
    print("Exports:", exports, file=stderr) | 
				
			|||
 | 
				
			|||
    def parse_rpn(rpn): | 
				
			|||
        pos = 0 | 
				
			|||
        while pos < len(rpn): | 
				
			|||
            if rpn[pos] == 0x51: | 
				
			|||
                pos += 1 | 
				
			|||
                while rpn[pos] != 0: | 
				
			|||
                    pos += 1 | 
				
			|||
                pos += 1 | 
				
			|||
            elif rpn[pos] == 0x80: | 
				
			|||
                pos += 5 | 
				
			|||
            elif rpn[pos] == 0x50 or rpn[pos] == 0x81: | 
				
			|||
                symbol_id = unpack("<I", rpn[pos + 1:pos + 5])[0] | 
				
			|||
                if symbol_id != 0xffffffff: | 
				
			|||
                    symbol = obj["symbols"][symbol_id] | 
				
			|||
                    if symbol["type"] == symtype.LOCAL: | 
				
			|||
                        localsyms[symbol["name"]] += 1 | 
				
			|||
                    else: | 
				
			|||
                        globalsyms[symbol["name"]] += 1 | 
				
			|||
                pos += 5 | 
				
			|||
            else: | 
				
			|||
                pos += 1 | 
				
			|||
 | 
				
			|||
    for section in obj["sections"]: | 
				
			|||
        if section["type"] == sectype.ROMX or section["type"] == sectype.ROM0: | 
				
			|||
            for patch in section["patches"]: | 
				
			|||
                parse_rpn(patch["rpn"]) | 
				
			|||
    for assertion in obj["assertions"]: | 
				
			|||
        parse_rpn(assertion["rpn"]) | 
				
			|||
 | 
				
			|||
    print(file=stderr) | 
				
			|||
 | 
				
			|||
if just_dump: | 
				
			|||
    exit() | 
				
			|||
 | 
				
			|||
print("Unreferenced globals:", file=stderr) | 
				
			|||
for symbol in globalsyms: | 
				
			|||
    if globalsyms[symbol] == 0: | 
				
			|||
        print(symbol) | 
				
			|||
					Loading…
					
					
				
		Reference in new issue