#!/usr/bin/python

import sys
import io

class O65():

    header: dict[str, int] = {}
    options: list[(int, int, bytes)] = []
    text: bytes
    data: bytes
    undef_ref_cnt: int
    text_reloc: list[(int, int, int)] = []
    data_reloc: list[(int, int, int)] = []
    exports: list[(str, int, int)] = []

    def __init__(self, filename: str) -> None:
        with open(filename, "rb") as _file:
            self.header["no_c64"]   = int.from_bytes(_file.read(2))
            self.header["magic"]    = int.from_bytes(_file.read(3))
            self.header["version"]  = int.from_bytes(_file.read(1))
            self.header["mode"]     = int.from_bytes(_file.read(2), byteorder="little")
            self.header["tbase"]    = int.from_bytes(_file.read(2), byteorder="little")
            self.header["tlen"]     = int.from_bytes(_file.read(2), byteorder="little")
            self.header["dbase"]    = int.from_bytes(_file.read(2), byteorder="little")
            self.header["dlen"]     = int.from_bytes(_file.read(2), byteorder="little")
            self.header["bbase"]    = int.from_bytes(_file.read(2), byteorder="little")
            self.header["blen"]     = int.from_bytes(_file.read(2), byteorder="little")
            self.header["zbase"]    = int.from_bytes(_file.read(2), byteorder="little")
            self.header["zlen"]     = int.from_bytes(_file.read(2), byteorder="little")
            self.header["stack"]    = int.from_bytes(_file.read(2), byteorder="little")

            olen = int.from_bytes(_file.read(1))
            while olen != 0:
                otype = int.from_bytes(_file.read(1))
                obytes = _file.read(olen - 2)
                self.options.append((olen, otype, obytes))
                olen = int.from_bytes(_file.read(1))

            text_offs = _file.tell()
            print(f"Text offset: {text_offs}")

            self.text = _file.read(self.header["tlen"])
            self.data = _file.read(self.header["dlen"])

            self.undef_ref_cnt = _file.read(2)

            self.text_reloc = self._parse_reloc(_file)
            self.data_reloc = self._parse_reloc(_file)

            export_count = int.from_bytes(_file.read(2), byteorder="little")
            for _ in range(export_count):
                name: bytearray = bytearray()
                data = 0
                while True:
                    data = _file.read(1)
                    if data != b"\x00":
                        name.extend(data)
                    else:
                        break
                segment = int.from_bytes(_file.read(1))
                value = int.from_bytes(_file.read(2), byteorder="little")
                self.exports.append((name.decode(), segment, value))

    def _parse_reloc(self, fd: io.BufferedReader) -> list[(int, int, int)]:
        reloc: list[(int, int, int)] = []
        offset = fd.read(1)
        while offset != b'\x00':
            offset_val = 0
            while offset == b'\xff':
                offset = fd.read(1)
                offset_val += int.from_bytes(offset) - 1
            offset_val += int.from_bytes(offset)
            typebyte = int.from_bytes(fd.read(1))
            lobyte = None
            if typebyte & 0x40:
                lobyte = int.from_bytes(fd.read(1))
            reloc.append((offset_val, typebyte, lobyte))
            offset = fd.read(1)
        return reloc


def main() -> None:
    if len(sys.argv) < 2:
        print("Please supply a filename")
        return
    filename = sys.argv[1]
    print(filename)
    o65 = O65(filename)
    for item, value in o65.header.items():
        print(f"{item}:\t{value:#x}")
    
    total_olen = 0
    for option in o65.options:
        print(f"Length: {option[0]:#x} Type: {option[1]:#x}, Data: {option[2]}")
        total_olen += option[0]

    print(f"Total option length: {total_olen:#x}")

    print(f"Text size: {len(o65.text)}")
    print(f"Data size: {len(o65.data)}")

    for item in o65.exports:
        print(f"Name: {item[0]} Addr: {item[2]:#x}")

if __name__ == "__main__":
    main()

