summaryrefslogtreecommitdiff
path: root/tools/map2sym.py
blob: b7f703d9d051dc700fa0ee5059aa3fba7d51554d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/env python

"""
Convert an rgbds .map file into a sorted .sym file
with bank and section data in comments.
"""

import sys
import re

def total_bank_size(type):
	# used to output the size of EMPTY banks
	sizes = {
		'ROM0':  0x4000, # 0000-3FFF
		'ROMX':  0x4000, # 4000-7FFF
		'VRAM':  0x2000, # 8000-9FFF
		'SRAM':  0x2000, # A000-BFFF
		'WRAM0': 0x1000, # C000-CFFF
		'WRAMX': 0x1000, # D000-DFFF
		                 # E000-FDFF: echo RAM
		'OAM':     0xA0, # FE00-FE9F
		                 # FEA0-FEFF: unusable
		                 # FF00-FF7F: hardware I/O registers
		'HRAM':    0x80, # FF80-FFFF
	}
	return sizes[type]

def map_to_sym(input):
	# analogous to ";File generated by rgblink"
	yield ';File generated by map2sym.py\n'

	# ex: OAM:
	unused_rx = re.compile(r'^([A-Z]+):$')
	# ex: ROM Bank #1:
	bank_rx = re.compile(r'^([A-Z]+) Bank #([0-9]+)')
	# ex:   SECTION: $4000-$747A ($347B bytes) ["bank1"]
	section_rx = re.compile(r' +SECTION: \$([0-9A-F]+)(?:-\$([0-9A-F]+))? \(\$([0-9A-F]+) bytes\) \["(.+)"\]')
	# ex:            $4025 = PlaceWaitingText.Waiting
	label_rx = re.compile(r' +\$([0-9A-F]+) = (.+)')
	# ex:     SLACK: $0B85 bytes
	slack_rx = re.compile(r' +SLACK: \$([0-9A-F]+) bytes')

	rom_size = 0
	bank_type = None
	bank_number = None
	bank_size = 0
	bank_lines = []
	section_lines = []

	for line in input:

		if line.startswith('  EMPTY'):
			# empty banks have their entire capacity as slack
			line = '    SLACK: $%04X bytes\n' % total_bank_size(bank_type)

		x = re.match(unused_rx, line)
		if x:
			# start an unused bank
			bank_type = x.group(1)
			bank_number = None
			bank_size = 0
			del bank_lines[:]
			del section_lines[:]
			continue

		x = re.match(bank_rx, line)
		if x:
			# start a new bank
			bank_type = x.group(1)
			bank_number = '%02X' % int(x.group(2))
			if bank_type == 'ROM':
				bank_type = 'ROM0' if bank_number == '00' else 'ROMX'
			if bank_type == 'WRAM':
				bank_type = 'WRAM0' if bank_number == '00' else 'WRAMX'
			bank_size = 0
			del bank_lines[:]
			del section_lines[:]
			continue

		x = re.match(section_rx, line)
		if x:
			# finish current section
			bank_lines.extend(sorted(section_lines))
			# start a new section
			start = x.group(1)
			end = x.group(2) or start
			size = x.group(3).zfill(4)
			name = x.group(4)
			bank_size += int(size, 16)
			# ex: ; 01:4000-747A ($347B) bank1
			bank_lines.append('; %s:%s-%s ($%s) %s\n' % (bank_number, start, end, size, name))
			del section_lines[:]
			continue

		x = re.match(label_rx, line)
		if x:
			# add label to section
			address = x.group(1)
			label = x.group(2)
			# ex: 01:4025 PlaceWaitingText.Waiting
			section_lines.append('%s:%s %s\n' % (bank_number, address, label))
			continue

		x = re.match(slack_rx, line)
		if x:
			# finish current section
			bank_lines.extend(sorted(section_lines))
			# finish current bank
			if bank_type.startswith('ROM'): # ROM0 or ROMX
				rom_size += bank_size
			slack = int(x.group(1), 16)
			if bank_type in {'ROM0', 'WRAM0', 'OAM', 'HRAM'}:
				# ex: ; ROM0 ($3E93) ($016D free)
				yield '; %s ($%04X) ($%04X free)\n' % (bank_type, bank_size, slack)
			else:
				# ex: ; ROMX $01 ($347B) ($0B85 free)
				yield '; %s $%s ($%04X) ($%04X free)\n' % (bank_type, bank_number, bank_size, slack)
			for line in bank_lines:
				yield line
			continue

	total_rom_size = 0x4000 * 128
	free_space = total_rom_size - rom_size
	percent_free = free_space * 100.0 / total_rom_size
	yield '; ROM: %.2f%% free space ($%06X) ($%06X free)\n' % (percent_free, rom_size, free_space)

def main():
	if len(sys.argv) < 3:
		sys.stderr.write('Usage: %s pokecrystal.map sorted.sym\n' % sys.argv[0])
		sys.exit(1)
	with open(sys.argv[1], 'r') as infile:
		input = infile.readlines()
		output = map_to_sym(input)
	with open(sys.argv[2], 'w') as outfile:
		outfile.writelines(output)

if __name__ == '__main__':
	main()