pokegold-spaceworld/utils/map2link.py
Rangi42 5904823317
Some checks failed
CI / build (push) Has been cancelled
Remove executable permission from various files
2025-12-02 12:23:39 -05:00

189 lines
5.8 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import re
import sys
from collections import OrderedDict
class MapFile:
LINETYPE_BLANK = -1
LINETYPE_BANK = 0
LINETYPE_SECTION = 1
LINETYPE_SYMBOL = 2
LINETYPE_SLACK = 3
LINETYPE_EMPTY = 4
bank_types = ('ROM', 'VRAM', 'SRAM', 'WRAM', 'OAM', 'HRAM')
bank_starts = (0x4000, 0x8000, 0xa000, 0xd000, 0xfe00, 0xff80)
class MapFileLine:
def __init__(self, linestr):
self.raw = linestr
def __str__(self):
return self.raw
class BlankLine(MapFileLine):
def __init__(self, linestr):
super().__init__(linestr)
class BankLine(MapFileLine):
def __init__(self, linestr):
super().__init__(linestr)
match = re.search('#(\d+)', linestr, re.I)
if match is None:
self.bankno = 0
else:
self.bankno = int(match.group(1))
bankname = linestr.split()[0].rstrip(':')
try:
self.banktype = MapFile.bank_types.index(bankname)
except ValueError as e:
raise ValueError(f'Unrecognized bank type: {bankname}') from e
@property
def name(self):
if self.banktype == 0: # ROM
if self.bankno == 0:
return 'ROM0'
else:
return f'ROMX ${self.bankno:02x}'
elif self.banktype == 3: # WRAM
if self.bankno == 0:
return 'WRAM0'
else:
return f'WRAMX ${self.bankno:02x}'
elif self.banktype < 3:
return f'{MapFile.bank_types[self.banktype]} {self.bankno:d}'
else:
return f'{MapFile.bank_types[self.banktype]}'
@property
def start(self):
if self.bankno == 0:
if self.banktype == 0:
return 0x0000
elif self.banktype == 3:
return 0xc000
return MapFile.bank_starts[self.banktype]
def __hash__(self):
return hash(self.name)
class SectionLine(MapFileLine):
def __init__(self, linestr):
super().__init__(linestr)
match = re.search(r'\$([0-9A-F]{4}) \(\$([0-9A-F]+) bytes\) \["(.+)"\]', linestr, re.I)
end, size, self.name = match.groups()
self.end = int(end, 16)
self.size = int(size, 16)
if self.size > 0:
self.end += 1
self.start = self.end - self.size
self.symbols = []
class SymbolLine(MapFileLine):
def __init__(self, linestr):
super().__init__(linestr)
class SlackLine(MapFileLine):
def __init__(self, linestr):
super().__init__(linestr)
match = re.search(r'\$([0-9A-F]{4}) bytes', linestr, re.I)
self.slack = int(match.group(1), 16)
class EmptyLine(MapFileLine):
def __init__(self, linestr):
super().__init__(linestr)
line_classes = {
LINETYPE_BLANK: BlankLine,
LINETYPE_BANK: BankLine,
LINETYPE_SECTION: SectionLine,
LINETYPE_SYMBOL: SymbolLine,
LINETYPE_SLACK: SlackLine,
LINETYPE_EMPTY: EmptyLine
}
def __init__(self, fname, *fpargs, **fpkwargs):
self.fname = fname
self.fp = None
self.fpargs = fpargs
self.fpkwargs = fpkwargs
def open(self):
if self.fp is None or self.fp.closed:
self.fp = open(self.fname, *self.fpargs, **self.fpkwargs)
def close(self):
if not self.fp.closed:
self.fp.close()
self.fp = None
def __enter__(self):
self.open()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.fp.__exit__(exc_type, exc_val, exc_tb)
self.fp = None
def __iter__(self):
if self.fp is None or self.fp.closed:
print('Warning: Cowardly refusing to read closed file', file=sys.stderr)
raise StopIteration
for line in self.fp:
linestr = line.strip('\n')
if linestr == '':
line_type = self.LINETYPE_BLANK
elif 'SECTION:' in linestr:
line_type = self.LINETYPE_SECTION
elif 'SLACK:' in linestr:
line_type = self.LINETYPE_SLACK
elif linestr == ' EMPTY':
line_type = self.LINETYPE_EMPTY
elif ' = ' in linestr:
line_type = self.LINETYPE_SYMBOL
else:
line_type = self.LINETYPE_BANK
yield self.line_classes[line_type](linestr)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('mapfile', type=MapFile)
parser.add_argument('linkfile', type=argparse.FileType('w'))
args = parser.parse_args()
rom_map = OrderedDict()
cur_bank = None
print('; Automatically generated by map2link.py', file=args.linkfile)
with args.mapfile:
for line in args.mapfile:
if isinstance(line, MapFile.BankLine):
cur_bank = line
rom_map[cur_bank] = []
elif isinstance(line, MapFile.SectionLine):
rom_map[cur_bank].append(line)
for bank, sections in rom_map.items():
if len(sections) == 0:
continue
sections.sort(key=lambda s: s.start)
print(bank.name, file=args.linkfile)
start = bank.start
for section in sections:
if section.start > start:
print(f'\t; ${start:04x}', file=args.linkfile)
print(f'\torg ${section.start:04x}', file=args.linkfile)
print(f'\t"{section.name}" ; ${section.start:04x}, size: ${section.size:04x}', file=args.linkfile)
start = section.end
if __name__ == '__main__':
main()