mirror of
https://github.com/pret/pokegold-spaceworld.git
synced 2026-03-21 17:45:52 -05:00
189 lines
5.8 KiB
Python
189 lines
5.8 KiB
Python
#!/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()
|