diff options
Diffstat (limited to 'extras')
m--------- | extras | 0 | ||||
-rw-r--r-- | extras/configuration.py | 57 | ||||
-rw-r--r-- | extras/gbz80disasm.py | 947 | ||||
-rw-r--r-- | extras/gfx.py | 750 | ||||
-rw-r--r-- | extras/labels.py | 213 | ||||
-rw-r--r-- | extras/scan_includes.py | 65 | ||||
-rw-r--r-- | extras/wram.py | 313 |
7 files changed, 0 insertions, 2345 deletions
diff --git a/extras b/extras new file mode 160000 +Subproject dfe657177453423987544798d9763b2938874b4 diff --git a/extras/configuration.py b/extras/configuration.py deleted file mode 100644 index 1592fe6..0000000 --- a/extras/configuration.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -Configuration -""" - -import os - -class ConfigException(Exception): - """ - Configuration error. Maybe a missing config variable. - """ - -class Config(object): - """ - The Config class handles all configuration for pokemontools. Other classes - and functions use a Config object to determine where expected files can be - located. - """ - - def __init__(self, **kwargs): - """ - Store all parameters. - """ - self._config = {} - - for (key, value) in kwargs.items(): - if key not in self.__dict__: - self._config[key] = value - else: - raise ConfigException( - "Can't store \"{0}\" in configuration because the key conflicts with an existing property." - .format(key) - ) - - if "path" not in self._config: - self._config["path"] = os.getcwd() - - # vba save states go into ./save-states/ - if "save_state_path" not in self._config: - self._config["save_state_path"] = os.path.join(self._config["path"], "save-states/") - - # assume rom is at ./baserom.gbc - if "rom" not in self._config: - self._config["rom_path"] = os.path.join(self._config["path"], "baserom.gbc") - - def __getattr__(self, key): - """ - Grab the value from the class properties, then check the configuration, - and raise an exception if nothing works. - """ - if key in self.__dict__: - return self.__dict__[key] - elif key in self._config: - return self._config[key] - else: - raise ConfigException( - "no config found for \"{0}\"".format(key) - ) diff --git a/extras/gbz80disasm.py b/extras/gbz80disasm.py deleted file mode 100644 index bd55d21..0000000 --- a/extras/gbz80disasm.py +++ /dev/null @@ -1,947 +0,0 @@ -# -*- coding: utf-8 -*- -""" -GBC disassembler -""" - -import os -import sys -from copy import copy, deepcopy -from ctypes import c_int8 -import random -import json - -import configuration -import labels -import wram - -# New versions of json don't have read anymore. -if not hasattr(json, "read"): - json.read = json.loads - -spacing = "\t" - -temp_opt_table = [ - [ "ADC A", 0x8f, 0 ], - [ "ADC B", 0x88, 0 ], - [ "ADC C", 0x89, 0 ], - [ "ADC D", 0x8a, 0 ], - [ "ADC E", 0x8b, 0 ], - [ "ADC H", 0x8c, 0 ], - [ "ADC [HL]", 0x8e, 0 ], - [ "ADC L", 0x8d, 0 ], - [ "ADC x", 0xce, 1 ], - [ "ADD A", 0x87, 0 ], - [ "ADD B", 0x80, 0 ], - [ "ADD C", 0x81, 0 ], - [ "ADD D", 0x82, 0 ], - [ "ADD E", 0x83, 0 ], - [ "ADD H", 0x84, 0 ], - [ "ADD [HL]", 0x86, 0 ], - [ "ADD HL, BC", 0x9, 0 ], - [ "ADD HL, DE", 0x19, 0 ], - [ "ADD HL, HL", 0x29, 0 ], - [ "ADD HL, SP", 0x39, 0 ], - [ "ADD L", 0x85, 0 ], - [ "ADD SP, x", 0xe8, 1 ], - [ "ADD x", 0xc6, 1 ], - [ "AND A", 0xa7, 0 ], - [ "AND B", 0xa0, 0 ], - [ "AND C", 0xa1, 0 ], - [ "AND D", 0xa2, 0 ], - [ "AND E", 0xa3, 0 ], - [ "AND H", 0xa4, 0 ], - [ "AND [HL]", 0xa6, 0 ], - [ "AND L", 0xa5, 0 ], - [ "AND x", 0xe6, 1 ], - [ "BIT 0, A", 0x47cb, 3 ], - [ "BIT 0, B", 0x40cb, 3 ], - [ "BIT 0, C", 0x41cb, 3 ], - [ "BIT 0, D", 0x42cb, 3 ], - [ "BIT 0, E", 0x43cb, 3 ], - [ "BIT 0, H", 0x44cb, 3 ], - [ "BIT 0, [HL]", 0x46cb, 3 ], - [ "BIT 0, L", 0x45cb, 3 ], - [ "BIT 1, A", 0x4fcb, 3 ], - [ "BIT 1, B", 0x48cb, 3 ], - [ "BIT 1, C", 0x49cb, 3 ], - [ "BIT 1, D", 0x4acb, 3 ], - [ "BIT 1, E", 0x4bcb, 3 ], - [ "BIT 1, H", 0x4ccb, 3 ], - [ "BIT 1, [HL]", 0x4ecb, 3 ], - [ "BIT 1, L", 0x4dcb, 3 ], - [ "BIT 2, A", 0x57cb, 3 ], - [ "BIT 2, B", 0x50cb, 3 ], - [ "BIT 2, C", 0x51cb, 3 ], - [ "BIT 2, D", 0x52cb, 3 ], - [ "BIT 2, E", 0x53cb, 3 ], - [ "BIT 2, H", 0x54cb, 3 ], - [ "BIT 2, [HL]", 0x56cb, 3 ], - [ "BIT 2, L", 0x55cb, 3 ], - [ "BIT 3, A", 0x5fcb, 3 ], - [ "BIT 3, B", 0x58cb, 3 ], - [ "BIT 3, C", 0x59cb, 3 ], - [ "BIT 3, D", 0x5acb, 3 ], - [ "BIT 3, E", 0x5bcb, 3 ], - [ "BIT 3, H", 0x5ccb, 3 ], - [ "BIT 3, [HL]", 0x5ecb, 3 ], - [ "BIT 3, L", 0x5dcb, 3 ], - [ "BIT 4, A", 0x67cb, 3 ], - [ "BIT 4, B", 0x60cb, 3 ], - [ "BIT 4, C", 0x61cb, 3 ], - [ "BIT 4, D", 0x62cb, 3 ], - [ "BIT 4, E", 0x63cb, 3 ], - [ "BIT 4, H", 0x64cb, 3 ], - [ "BIT 4, [HL]", 0x66cb, 3 ], - [ "BIT 4, L", 0x65cb, 3 ], - [ "BIT 5, A", 0x6fcb, 3 ], - [ "BIT 5, B", 0x68cb, 3 ], - [ "BIT 5, C", 0x69cb, 3 ], - [ "BIT 5, D", 0x6acb, 3 ], - [ "BIT 5, E", 0x6bcb, 3 ], - [ "BIT 5, H", 0x6ccb, 3 ], - [ "BIT 5, [HL]", 0x6ecb, 3 ], - [ "BIT 5, L", 0x6dcb, 3 ], - [ "BIT 6, A", 0x77cb, 3 ], - [ "BIT 6, B", 0x70cb, 3 ], - [ "BIT 6, C", 0x71cb, 3 ], - [ "BIT 6, D", 0x72cb, 3 ], - [ "BIT 6, E", 0x73cb, 3 ], - [ "BIT 6, H", 0x74cb, 3 ], - [ "BIT 6, [HL]", 0x76cb, 3 ], - [ "BIT 6, L", 0x75cb, 3 ], - [ "BIT 7, A", 0x7fcb, 3 ], - [ "BIT 7, B", 0x78cb, 3 ], - [ "BIT 7, C", 0x79cb, 3 ], - [ "BIT 7, D", 0x7acb, 3 ], - [ "BIT 7, E", 0x7bcb, 3 ], - [ "BIT 7, H", 0x7ccb, 3 ], - [ "BIT 7, [HL]", 0x7ecb, 3 ], - [ "BIT 7, L", 0x7dcb, 3 ], - [ "CALL C, ?", 0xdc, 2 ], - [ "CALL NC, ?", 0xd4, 2 ], - [ "CALL NZ, ?", 0xc4, 2 ], - [ "CALL Z, ?", 0xcc, 2 ], - [ "CALL ?", 0xcd, 2 ], - [ "CCF", 0x3f, 0 ], - [ "CP A", 0xbf, 0 ], - [ "CP B", 0xb8, 0 ], - [ "CP C", 0xb9, 0 ], - [ "CP D", 0xba, 0 ], - [ "CP E", 0xbb, 0 ], - [ "CP H", 0xbc, 0 ], - [ "CP [HL]", 0xbe, 0 ], - [ "CPL", 0x2f, 0 ], - [ "CP L", 0xbd, 0 ], - [ "CP x", 0xfe, 1 ], - [ "DAA", 0x27, 0 ], - [ "DEBUG", 0xed, 0 ], - [ "DEC A", 0x3d, 0 ], - [ "DEC B", 0x5, 0 ], - [ "DEC BC", 0xb, 0 ], - [ "DEC C", 0xd, 0 ], - [ "DEC D", 0x15, 0 ], - [ "DEC DE", 0x1b, 0 ], - [ "DEC E", 0x1d, 0 ], - [ "DEC H", 0x25, 0 ], - [ "DEC HL", 0x2b, 0 ], - [ "DEC [HL]", 0x35, 0 ], - [ "DEC L", 0x2d, 0 ], - [ "DEC SP", 0x3b, 0 ], - [ "DI", 0xf3, 0 ], - [ "EI", 0xfb, 0 ], - [ "HALT", 0x76, 0 ], - [ "INC A", 0x3c, 0 ], - [ "INC B", 0x4, 0 ], - [ "INC BC", 0x3, 0 ], - [ "INC C", 0xc, 0 ], - [ "INC D", 0x14, 0 ], - [ "INC DE", 0x13, 0 ], - [ "INC E", 0x1c, 0 ], - [ "INC H", 0x24, 0 ], - [ "INC HL", 0x23, 0 ], - [ "INC [HL]", 0x34, 0 ], - [ "INC L", 0x2c, 0 ], - [ "INC SP", 0x33, 0 ], - [ "JP C, ?", 0xda, 2 ], - [ "JP [HL]", 0xe9, 0 ], - [ "JP NC, ?", 0xd2, 2 ], - [ "JP NZ, ?", 0xc2, 2 ], - [ "JP Z, ?", 0xca, 2 ], - [ "JP ?", 0xc3, 2 ], - [ "JR C, x", 0x38, 1 ], - [ "JR NC, x", 0x30, 1 ], - [ "JR NZ, x", 0x20, 1 ], - [ "JR Z, x", 0x28, 1 ], - [ "JR x", 0x18, 1 ], - [ "LD A, A", 0x7f, 0 ], - [ "LD A, B", 0x78, 0 ], - [ "LD A, C", 0x79, 0 ], - [ "LD A, D", 0x7a, 0 ], - [ "LD A, E", 0x7b, 0 ], - [ "LD A, H", 0x7c, 0 ], - [ "LD A, L", 0x7d, 0 ], - [ "LD A, [$FF00+C]", 0xf2, 0 ], - [ "LD A, [$FF00+x]", 0xf0, 1 ], -# [ "LDH A, [x]", 0xf0, 1 ], # rgbds has trouble with this one? - [ "LD A, [BC]", 0xa, 0 ], - [ "LD A, [DE]", 0x1a, 0 ], -# [ "LD A, [HL+]", 0x2a, 0 ], -# [ "LD A, [HL-]", 0x3a, 0 ], - [ "LD A, [HL]", 0x7e, 0 ], - [ "LD A, [HLD]", 0x3a, 0 ], - [ "LD A, [HLI]", 0x2a, 0 ], - [ "LD A, [?]", 0xfa, 2 ], - [ "LD A, x", 0x3e, 1 ], - [ "LD B, A", 0x47, 0 ], - [ "LD B, B", 0x40, 0 ], - [ "LD B, C", 0x41, 0 ], - [ "LD [BC], A", 0x2, 0 ], - [ "LD B, D", 0x42, 0 ], - [ "LD B, E", 0x43, 0 ], - [ "LD B, H", 0x44, 0 ], - [ "LD B, [HL]", 0x46, 0 ], - [ "LD B, L", 0x45, 0 ], - [ "LD B, x", 0x6, 1 ], - [ "LD C, A", 0x4f, 0 ], - [ "LD C, B", 0x48, 0 ], - [ "LD C, C", 0x49, 0 ], - [ "LD C, D", 0x4a, 0 ], - [ "LD C, E", 0x4b, 0 ], - [ "LD C, H", 0x4c, 0 ], - [ "LD C, [HL]", 0x4e, 0 ], - [ "LD C, L", 0x4d, 0 ], - [ "LD C, x", 0xe, 1 ], - [ "LD D, A", 0x57, 0 ], -# [ "LDD A, [HL]", 0x3a, 0 ], - [ "LD D, B", 0x50, 0 ], - [ "LD D, C", 0x51, 0 ], - [ "LD D, D", 0x52, 0 ], - [ "LD D, E", 0x53, 0 ], - [ "LD [DE], A", 0x12, 0 ], - [ "LD D, H", 0x54, 0 ], - [ "LD D, [HL]", 0x56, 0 ], -# [ "LDD [HL], A", 0x32, 0 ], - [ "LD D, L", 0x55, 0 ], - [ "LD D, x", 0x16, 1 ], - [ "LD E, A", 0x5f, 0 ], - [ "LD E, B", 0x58, 0 ], - [ "LD E, C", 0x59, 0 ], - [ "LD E, D", 0x5a, 0 ], - [ "LD E, E", 0x5b, 0 ], - [ "LD E, H", 0x5c, 0 ], - [ "LD E, [HL]", 0x5e, 0 ], - [ "LD E, L", 0x5d, 0 ], - [ "LD E, x", 0x1e, 1 ], - [ "LD [$FF00+C], A", 0xe2, 0 ], - [ "LD [$FF00+x], A", 0xe0, 1 ], -# [ "LDH [x], A", 0xe0, 1 ], - [ "LD H, A", 0x67, 0 ], - [ "LD H, B", 0x60, 0 ], - [ "LD H, C", 0x61, 0 ], - [ "LD H, D", 0x62, 0 ], - [ "LD H, E", 0x63, 0 ], - [ "LD H, H", 0x64, 0 ], - [ "LD H, [HL]", 0x66, 0 ], - [ "LD H, L", 0x65, 0 ], -# [ "LD [HL+], A", 0x22, 0 ], -# [ "LD [HL-], A", 0x32, 0 ], - [ "LD [HL], A", 0x77, 0 ], - [ "LD [HL], B", 0x70, 0 ], - [ "LD [HL], C", 0x71, 0 ], - [ "LD [HL], D", 0x72, 0 ], - [ "LD [HLD], A", 0x32, 0 ], - [ "LD [HL], E", 0x73, 0 ], - [ "LD [HL], H", 0x74, 0 ], - [ "LD [HLI], A", 0x22, 0 ], - [ "LD [HL], L", 0x75, 0 ], -# [ "LD HL, SP+x", 0xf8, 1 ], # rgbds uses [sp+x] - [ "LD HL, [SP+x]", 0xf8, 1 ], - [ "LD [HL], x", 0x36, 1 ], - [ "LD H, x", 0x26, 1 ], -# [ "LDI A, [HL]", 0x2a, 0 ], -# [ "LDI [HL], A", 0x22, 0 ], - [ "LD L, A", 0x6f, 0 ], - [ "LD L, B", 0x68, 0 ], - [ "LD L, C", 0x69, 0 ], - [ "LD L, D", 0x6a, 0 ], - [ "LD L, E", 0x6b, 0 ], - [ "LD L, H", 0x6c, 0 ], - [ "LD L, [HL]", 0x6e, 0 ], - [ "LD L, L", 0x6d, 0 ], - [ "LD L, x", 0x2e, 1 ], -# [ "LD PC, HL", 0xe9, 0 ], #prefer jp [hl] - [ "LD SP, HL", 0xf9, 0 ], - [ "LD BC, ?", 0x1, 2 ], - [ "LD DE, ?", 0x11, 2 ], - [ "LD HL, ?", 0x21, 2 ], - [ "LD SP, ?", 0x31, 2 ], - [ "LD [?], SP", 0x8, 2 ], - [ "LD [?], A", 0xea, 2 ], - [ "NOP", 0x0, 0 ], - [ "OR A", 0xb7, 0 ], - [ "OR B", 0xb0, 0 ], - [ "OR C", 0xb1, 0 ], - [ "OR D", 0xb2, 0 ], - [ "OR E", 0xb3, 0 ], - [ "OR H", 0xb4, 0 ], - [ "OR [HL]", 0xb6, 0 ], - [ "OR L", 0xb5, 0 ], - [ "OR x", 0xf6, 1 ], - [ "POP AF", 0xf1, 0 ], - [ "POP BC", 0xc1, 0 ], - [ "POP DE", 0xd1, 0 ], - [ "POP HL", 0xe1, 0 ], - [ "PUSH AF", 0xf5, 0 ], - [ "PUSH BC", 0xc5, 0 ], - [ "PUSH DE", 0xd5, 0 ], - [ "PUSH HL", 0xe5, 0 ], - [ "RES 0, A", 0x87cb, 3 ], - [ "RES 0, B", 0x80cb, 3 ], - [ "RES 0, C", 0x81cb, 3 ], - [ "RES 0, D", 0x82cb, 3 ], - [ "RES 0, E", 0x83cb, 3 ], - [ "RES 0, H", 0x84cb, 3 ], - [ "RES 0, [HL]", 0x86cb, 3 ], - [ "RES 0, L", 0x85cb, 3 ], - [ "RES 1, A", 0x8fcb, 3 ], - [ "RES 1, B", 0x88cb, 3 ], - [ "RES 1, C", 0x89cb, 3 ], - [ "RES 1, D", 0x8acb, 3 ], - [ "RES 1, E", 0x8bcb, 3 ], - [ "RES 1, H", 0x8ccb, 3 ], - [ "RES 1, [HL]", 0x8ecb, 3 ], - [ "RES 1, L", 0x8dcb, 3 ], - [ "RES 2, A", 0x97cb, 3 ], - [ "RES 2, B", 0x90cb, 3 ], - [ "RES 2, C", 0x91cb, 3 ], - [ "RES 2, D", 0x92cb, 3 ], - [ "RES 2, E", 0x93cb, 3 ], - [ "RES 2, H", 0x94cb, 3 ], - [ "RES 2, [HL]", 0x96cb, 3 ], - [ "RES 2, L", 0x95cb, 3 ], - [ "RES 3, A", 0x9fcb, 3 ], - [ "RES 3, B", 0x98cb, 3 ], - [ "RES 3, C", 0x99cb, 3 ], - [ "RES 3, D", 0x9acb, 3 ], - [ "RES 3, E", 0x9bcb, 3 ], - [ "RES 3, H", 0x9ccb, 3 ], - [ "RES 3, [HL]", 0x9ecb, 3 ], - [ "RES 3, L", 0x9dcb, 3 ], - [ "RES 4, A", 0xa7cb, 3 ], - [ "RES 4, B", 0xa0cb, 3 ], - [ "RES 4, C", 0xa1cb, 3 ], - [ "RES 4, D", 0xa2cb, 3 ], - [ "RES 4, E", 0xa3cb, 3 ], - [ "RES 4, H", 0xa4cb, 3 ], - [ "RES 4, [HL]", 0xa6cb, 3 ], - [ "RES 4, L", 0xa5cb, 3 ], - [ "RES 5, A", 0xafcb, 3 ], - [ "RES 5, B", 0xa8cb, 3 ], - [ "RES 5, C", 0xa9cb, 3 ], - [ "RES 5, D", 0xaacb, 3 ], - [ "RES 5, E", 0xabcb, 3 ], - [ "RES 5, H", 0xaccb, 3 ], - [ "RES 5, [HL]", 0xaecb, 3 ], - [ "RES 5, L", 0xadcb, 3 ], - [ "RES 6, A", 0xb7cb, 3 ], - [ "RES 6, B", 0xb0cb, 3 ], - [ "RES 6, C", 0xb1cb, 3 ], - [ "RES 6, D", 0xb2cb, 3 ], - [ "RES 6, E", 0xb3cb, 3 ], - [ "RES 6, H", 0xb4cb, 3 ], - [ "RES 6, [HL]", 0xb6cb, 3 ], - [ "RES 6, L", 0xb5cb, 3 ], - [ "RES 7, A", 0xbfcb, 3 ], - [ "RES 7, B", 0xb8cb, 3 ], - [ "RES 7, C", 0xb9cb, 3 ], - [ "RES 7, D", 0xbacb, 3 ], - [ "RES 7, E", 0xbbcb, 3 ], - [ "RES 7, H", 0xbccb, 3 ], - [ "RES 7, [HL]", 0xbecb, 3 ], - [ "RES 7, L", 0xbdcb, 3 ], - [ "RETI", 0xd9, 0 ], - [ "RET C", 0xd8, 0 ], - [ "RET NC", 0xd0, 0 ], - [ "RET NZ", 0xc0, 0 ], - [ "RET Z", 0xc8, 0 ], - [ "RET", 0xc9, 0 ], - [ "RLA", 0x17, 0 ], - [ "RL A", 0x17cb, 3 ], - [ "RL B", 0x10cb, 3 ], - [ "RL C", 0x11cb, 3 ], - [ "RLCA", 0x7, 0 ], - [ "RLC A", 0x7cb, 3 ], - [ "RLC B", 0xcb, 3 ], - [ "RLC C", 0x1cb, 3 ], - [ "RLC D", 0x2cb, 3 ], - [ "RLC E", 0x3cb, 3 ], - [ "RLC H", 0x4cb, 3 ], - [ "RLC [HL]", 0x6cb, 3 ], - [ "RLC L", 0x5cb, 3 ], - [ "RL D", 0x12cb, 3 ], - [ "RL E", 0x13cb, 3 ], - [ "RL H", 0x14cb, 3 ], - [ "RL [HL]", 0x16cb, 3 ], - [ "RL L", 0x15cb, 3 ], - [ "RRA", 0x1f, 0 ], - [ "RR A", 0x1fcb, 3 ], - [ "RR B", 0x18cb, 3 ], - [ "RR C", 0x19cb, 3 ], - [ "RRCA", 0xf, 0 ], - [ "RRC A", 0xfcb, 3 ], - [ "RRC B", 0x8cb, 3 ], - [ "RRC C", 0x9cb, 3 ], - [ "RRC D", 0xacb, 3 ], - [ "RRC E", 0xbcb, 3 ], - [ "RRC H", 0xccb, 3 ], - [ "RRC [HL]", 0xecb, 3 ], - [ "RRC L", 0xdcb, 3 ], - [ "RR D", 0x1acb, 3 ], - [ "RR E", 0x1bcb, 3 ], - [ "RR H", 0x1ccb, 3 ], - [ "RR [HL]", 0x1ecb, 3 ], - [ "RR L", 0x1dcb, 3 ], - [ "RST $0", 0xc7, 0 ], - [ "RST $10", 0xd7, 0 ], - [ "RST $18", 0xdf, 0 ], - [ "RST $20", 0xe7, 0 ], - [ "RST $28", 0xef, 0 ], - [ "RST $30", 0xf7, 0 ], - [ "RST $38", 0xff, 0 ], - [ "RST $8", 0xcf, 0 ], - [ "SBC A", 0x9f, 0 ], - [ "SBC B", 0x98, 0 ], - [ "SBC C", 0x99, 0 ], - [ "SBC D", 0x9a, 0 ], - [ "SBC E", 0x9b, 0 ], - [ "SBC H", 0x9c, 0 ], - [ "SBC [HL]", 0x9e, 0 ], - [ "SBC L", 0x9d, 0 ], - [ "SBC x", 0xde, 1 ], - [ "SCF", 0x37, 0 ], - [ "SET 0, A", 0xc7cb, 3 ], - [ "SET 0, B", 0xc0cb, 3 ], - [ "SET 0, C", 0xc1cb, 3 ], - [ "SET 0, D", 0xc2cb, 3 ], - [ "SET 0, E", 0xc3cb, 3 ], - [ "SET 0, H", 0xc4cb, 3 ], - [ "SET 0, [HL]", 0xc6cb, 3 ], - [ "SET 0, L", 0xc5cb, 3 ], - [ "SET 1, A", 0xcfcb, 3 ], - [ "SET 1, B", 0xc8cb, 3 ], - [ "SET 1, C", 0xc9cb, 3 ], - [ "SET 1, D", 0xcacb, 3 ], - [ "SET 1, E", 0xcbcb, 3 ], - [ "SET 1, H", 0xcccb, 3 ], - [ "SET 1, [HL]", 0xcecb, 3 ], - [ "SET 1, L", 0xcdcb, 3 ], - [ "SET 2, A", 0xd7cb, 3 ], - [ "SET 2, B", 0xd0cb, 3 ], - [ "SET 2, C", 0xd1cb, 3 ], - [ "SET 2, D", 0xd2cb, 3 ], - [ "SET 2, E", 0xd3cb, 3 ], - [ "SET 2, H", 0xd4cb, 3 ], - [ "SET 2, [HL]", 0xd6cb, 3 ], - [ "SET 2, L", 0xd5cb, 3 ], - [ "SET 3, A", 0xdfcb, 3 ], - [ "SET 3, B", 0xd8cb, 3 ], - [ "SET 3, C", 0xd9cb, 3 ], - [ "SET 3, D", 0xdacb, 3 ], - [ "SET 3, E", 0xdbcb, 3 ], - [ "SET 3, H", 0xdccb, 3 ], - [ "SET 3, [HL]", 0xdecb, 3 ], - [ "SET 3, L", 0xddcb, 3 ], - [ "SET 4, A", 0xe7cb, 3 ], - [ "SET 4, B", 0xe0cb, 3 ], - [ "SET 4, C", 0xe1cb, 3 ], - [ "SET 4, D", 0xe2cb, 3 ], - [ "SET 4, E", 0xe3cb, 3 ], - [ "SET 4, H", 0xe4cb, 3 ], - [ "SET 4, [HL]", 0xe6cb, 3 ], - [ "SET 4, L", 0xe5cb, 3 ], - [ "SET 5, A", 0xefcb, 3 ], - [ "SET 5, B", 0xe8cb, 3 ], - [ "SET 5, C", 0xe9cb, 3 ], - [ "SET 5, D", 0xeacb, 3 ], - [ "SET 5, E", 0xebcb, 3 ], - [ "SET 5, H", 0xeccb, 3 ], - [ "SET 5, [HL]", 0xeecb, 3 ], - [ "SET 5, L", 0xedcb, 3 ], - [ "SET 6, A", 0xf7cb, 3 ], - [ "SET 6, B", 0xf0cb, 3 ], - [ "SET 6, C", 0xf1cb, 3 ], - [ "SET 6, D", 0xf2cb, 3 ], - [ "SET 6, E", 0xf3cb, 3 ], - [ "SET 6, H", 0xf4cb, 3 ], - [ "SET 6, [HL]", 0xf6cb, 3 ], - [ "SET 6, L", 0xf5cb, 3 ], - [ "SET 7, A", 0xffcb, 3 ], - [ "SET 7, B", 0xf8cb, 3 ], - [ "SET 7, C", 0xf9cb, 3 ], - [ "SET 7, D", 0xfacb, 3 ], - [ "SET 7, E", 0xfbcb, 3 ], - [ "SET 7, H", 0xfccb, 3 ], - [ "SET 7, [HL]", 0xfecb, 3 ], - [ "SET 7, L", 0xfdcb, 3 ], - [ "SLA A", 0x27cb, 3 ], - [ "SLA B", 0x20cb, 3 ], - [ "SLA C", 0x21cb, 3 ], - [ "SLA D", 0x22cb, 3 ], - [ "SLA E", 0x23cb, 3 ], - [ "SLA H", 0x24cb, 3 ], - [ "SLA [HL]", 0x26cb, 3 ], - [ "SLA L", 0x25cb, 3 ], - [ "SRA A", 0x2fcb, 3 ], - [ "SRA B", 0x28cb, 3 ], - [ "SRA C", 0x29cb, 3 ], - [ "SRA D", 0x2acb, 3 ], - [ "SRA E", 0x2bcb, 3 ], - [ "SRA H", 0x2ccb, 3 ], - [ "SRA [HL]", 0x2ecb, 3 ], - [ "SRA L", 0x2dcb, 3 ], - [ "SRL A", 0x3fcb, 3 ], - [ "SRL B", 0x38cb, 3 ], - [ "SRL C", 0x39cb, 3 ], - [ "SRL D", 0x3acb, 3 ], - [ "SRL E", 0x3bcb, 3 ], - [ "SRL H", 0x3ccb, 3 ], - [ "SRL [HL]", 0x3ecb, 3 ], - [ "SRL L", 0x3dcb, 3 ], - [ "STOP", 0x10, 0 ], - [ "SUB A", 0x97, 0 ], - [ "SUB B", 0x90, 0 ], - [ "SUB C", 0x91, 0 ], - [ "SUB D", 0x92, 0 ], - [ "SUB E", 0x93, 0 ], - [ "SUB H", 0x94, 0 ], - [ "SUB [HL]", 0x96, 0 ], - [ "SUB L", 0x95, 0 ], - [ "SUB x", 0xd6, 1 ], - [ "SWAP A", 0x37cb, 3 ], - [ "SWAP B", 0x30cb, 3 ], - [ "SWAP C", 0x31cb, 3 ], - [ "SWAP D", 0x32cb, 3 ], - [ "SWAP E", 0x33cb, 3 ], - [ "SWAP H", 0x34cb, 3 ], - [ "SWAP [HL]", 0x36cb, 3 ], - [ "SWAP L", 0x35cb, 3 ], - [ "XOR A", 0xaf, 0 ], - [ "XOR B", 0xa8, 0 ], - [ "XOR C", 0xa9, 0 ], - [ "XOR D", 0xaa, 0 ], - [ "XOR E", 0xab, 0 ], - [ "XOR H", 0xac, 0 ], - [ "XOR [HL]", 0xae, 0 ], - [ "XOR L", 0xad, 0 ], - [ "XOR x", 0xee, 1 ], - [ "E", 0x100, -1 ], -] - -# construct a more useful version of opt_table -opt_table = {} -for line in temp_opt_table: - opt_table[line[1]] = [line[0], line[2]] -del line - -end_08_scripts_with = [ -0xc9, #ret -0xd9, #reti -0xe9, #jp hl -#0xc3, #jp -##0x18, #jr -###0xda, 0xe9, 0xd2, 0xc2, 0xca, 0xc3, 0x38, 0x30, 0x20, 0x28, 0x18, 0xd8, 0xd0, 0xc0, 0xc8, 0xc9 -] - -discrete_jumps = [0xda, 0xe9, 0xd2, 0xc2, 0xca, 0xc3] -relative_jumps = [0x38, 0x30, 0x20, 0x28, 0x18, 0xc3, 0xda, 0xc2] -relative_unconditional_jumps = [0xc3, 0x18] - -call_commands = [0xdc, 0xd4, 0xc4, 0xcc, 0xcd] - -def asm_label(address): - """ - Return the ASM label using the address. - """ - # why using a random value when you can use the address? - return '.asm_%x' % address - -def data_label(address): - return '.data_%x' % address - -def get_local_address(address): - bank = address / 0x4000 - return (address & 0x3fff) + 0x4000 * bool(bank) - -def get_global_address(address, bank): - if address < 0x8000: - return (address & 0x3fff) + 0x4000 * bank - return None - - return ".ASM_" + hex(address)[2:] - -def has_outstanding_labels(byte_labels): - """ - Check whether a label is used once in the asm output. - - If so, then that means it has to be called or specified later. - """ - for label_line in byte_labels.keys(): - real_line = byte_labels[label_line] - if real_line["definition"] == False: return True - return False - -def all_outstanding_labels_are_reverse(byte_labels, offset): - for label_id in byte_labels.keys(): - line = byte_labels[label_id] # label_id is also the address - if line["definition"] == False: - if not label_id < offset: return False - return True - -class Disassembler(object): - """ - GBC disassembler - """ - - def __init__(self, config): - """ - Setup the class instance. - """ - self.config = config - - self.wram = wram.WRAMProcessor(self.config) - self.labels = labels.Labels(self.config) - - def initialize(self): - """ - Setup the disassembler. - """ - self.wram.initialize() - self.labels.initialize() - - # TODO: fix how ROM is handled throughout the project. - rom_path = os.path.join(self.config.path, "baserom.gbc") - self.rom = bytearray(open(rom_path, "rb").read()) - - def find_label(self, local_address, bank_id=0): - # keep an integer - if type(local_address) == str: - local_address = int(local_address.replace("$", "0x"), 16) - - if local_address < 0x8000: - for label_entry in self.labels.labels: - if get_local_address(label_entry["address"]) == local_address: - if "bank" in label_entry and (label_entry["bank"] == bank_id or label_entry["bank"] == 0): - return label_entry["label"] - if local_address in self.wram.wram_labels.keys(): - return self.wram.wram_labels[local_address][-1] - for constants in [self.wram.gbhw_constants, self.wram.hram_constants]: - if local_address in constants.keys() and local_address >= 0xff00: - return constants[local_address] - return None - - def find_address_from_label(self, label): - for label_entry in self.labels.labels: - if label == label_entry["label"]: - return label_entry["address"] - return None - - def output_bank_opcodes(self, original_offset, max_byte_count=0x4000, include_last_address=True, stop_at=[], debug=False): - """ - Output bank opcodes. - - fs = current_address - b = bank_byte - in = input_data -- rom - bank_size = byte_count - i = offset - ad = end_address - a, oa = current_byte_number - - stop_at can be used to supply a list of addresses to not disassemble - over. This is useful if you know in advance that there are a lot of - fall-throughs. - """ - - bank_id = original_offset / 0x4000 - if debug: print "bank id is: " + str(bank_id) - - last_hl_address = None #for when we're scanning the main map script - last_a_address = None - used_3d97 = False - - rom = self.rom - - offset = original_offset - current_byte_number = 0 #start from the beginning - - #we don't actually have an end address, but we'll just say $4000 - end_address = original_offset + max_byte_count - - byte_labels = {} - data_tables = {} - - first_loop = True - output = "" - keep_reading = True - is_data = False - while offset <= end_address and keep_reading: - current_byte = rom[offset] - maybe_byte = current_byte - - # stop at any address - if not first_loop and offset in stop_at: - keep_reading = False - break - - #first check if this byte already has a label - #if it does, use the label - #if not, generate a new label - if offset in byte_labels.keys(): - line_label = byte_labels[offset]["name"] - byte_labels[offset]["usage"] += 1 - output += "\n" - else: - line_label = asm_label(offset) - byte_labels[offset] = {} - byte_labels[offset]["name"] = line_label - byte_labels[offset]["usage"] = 0 - byte_labels[offset]["definition"] = True - output += line_label + "\n" #" ; " + hex(offset) + "\n" - - #find out if there's a two byte key like this - temp_maybe = maybe_byte - temp_maybe += ( rom[offset+1] << 8) - if not is_data and temp_maybe in opt_table.keys() and rom[offset+1]!=0: - opstr = opt_table[temp_maybe][0].lower() - - if "x" in opstr: - for x in range(0, opstr.count("x")): - insertion = rom[offset + 1] - insertion = "$" + hex(insertion)[2:] - - opstr = opstr[:opstr.find("x")].lower() + insertion + opstr[opstr.find("x")+1:].lower() - - current_byte += 1 - offset += 1 - if "?" in opstr: - for y in range(0, opstr.count("?")): - byte1 = rom[offset + 1] - byte2 = rom[offset + 2] - - number = byte1 - number += byte2 << 8; - - insertion = "$%.4x" % (number) - - opstr = opstr[:opstr.find("?")].lower() + insertion + opstr[opstr.find("?")+1:].lower() - - current_byte_number += 2 - offset += 2 - - output += spacing + opstr #+ " ; " + hex(offset) - output += "\n" - - current_byte_number += 2 - offset += 2 - elif not is_data and maybe_byte in opt_table.keys(): - op_code = opt_table[maybe_byte] - op_code_type = op_code[1] - op_code_byte = maybe_byte - - #type = -1 when it's the E op - #if op_code_type != -1: - if op_code_type == 0 and rom[offset] == op_code_byte: - op_str = op_code[0].lower() - - output += spacing + op_code[0].lower() #+ " ; " + hex(offset) - output += "\n" - - offset += 1 - current_byte_number += 1 - elif op_code_type == 1 and rom[offset] == op_code_byte: - oplen = len(op_code[0]) - opstr = copy(op_code[0]) - xes = op_code[0].count("x") - include_comment = False - for x in range(0, xes): - insertion = rom[offset + 1] - insertion = "$" + hex(insertion)[2:] - - if current_byte == 0x18 or current_byte==0x20 or current_byte in relative_jumps: #jr or jr nz - #generate a label for the byte we're jumping to - target_address = offset + 2 + c_int8(rom[offset + 1]).value - if target_address in byte_labels.keys(): - byte_labels[target_address]["usage"] = 1 + byte_labels[target_address]["usage"] - line_label2 = byte_labels[target_address]["name"] - else: - line_label2 = asm_label(target_address) - byte_labels[target_address] = {} - byte_labels[target_address]["name"] = line_label2 - byte_labels[target_address]["usage"] = 1 - byte_labels[target_address]["definition"] = False - - insertion = line_label2 - if has_outstanding_labels(byte_labels) and all_outstanding_labels_are_reverse(byte_labels, offset): - include_comment = True - elif current_byte == 0x3e: - last_a_address = rom[offset + 1] - - opstr = opstr[:opstr.find("x")].lower() + insertion + opstr[opstr.find("x")+1:].lower() - - # because the $ff00+$ff syntax is silly - if opstr.count("$") > 1 and "+" in opstr: - first_orig = opstr[opstr.find("$"):opstr.find("+")] - first_val = eval(first_orig.replace("$","0x")) - - second_orig = opstr[opstr.find("+$")+1:opstr.find("]")] - second_val = eval(second_orig.replace("$","0x")) - - combined_val = "$%.4x" % (first_val + second_val) - result = self.find_label(combined_val, bank_id) - if result != None: - combined_val = result - - replacetron = "[%s+%s]" % (first_orig, second_orig) - opstr = opstr.replace(replacetron, "[%s]" % combined_val) - - output += spacing + opstr - if include_comment: - output += " ; " + hex(offset) - if current_byte in relative_jumps: - output += " $" + hex(rom[offset + 1])[2:] - output += "\n" - - current_byte_number += 1 - offset += 1 - insertion = "" - - current_byte_number += 1 - offset += 1 - include_comment = False - elif op_code_type == 2 and rom[offset] == op_code_byte: - oplen = len(op_code[0]) - opstr = copy(op_code[0]) - qes = op_code[0].count("?") - for x in range(0, qes): - byte1 = rom[offset + 1] - byte2 = rom[offset + 2] - - number = byte1 - number += byte2 << 8 - - if current_byte not in call_commands + discrete_jumps + relative_jumps: - pointer = get_global_address(number, bank_id) - if pointer not in data_tables.keys(): - data_tables[pointer] = {} - data_tables[pointer]['usage'] = 0 - else: - data_tables[pointer]['usage'] += 1 - - insertion = "$%.4x" % (number) - result = self.find_label(insertion, bank_id) - if result != None: - insertion = result - - opstr = opstr[:opstr.find("?")].lower() + insertion + opstr[opstr.find("?")+1:].lower() - output += spacing + opstr #+ " ; " + hex(offset) - output += "\n" - - current_byte_number += 2 - offset += 2 - - current_byte_number += 1 - offset += 1 - - if current_byte == 0x21: - last_hl_address = byte1 + (byte2 << 8) - if current_byte == 0xcd: - if number == 0x3d97: used_3d97 = True - - #duck out if this is jp $24d7 - if current_byte == 0xc3 or current_byte in relative_unconditional_jumps: - if current_byte == 0xc3: - if number == 0x3d97: used_3d97 = True - #if number == 0x24d7: #jp - if not has_outstanding_labels(byte_labels) or all_outstanding_labels_are_reverse(byte_labels, offset): - keep_reading = False - is_data = False - break - else: - is_data = True - else: - #if is_data and keep_reading: - output += spacing + "db $" + hex(rom[offset])[2:] #+ " ; " + hex(offset) - output += "\n" - offset += 1 - current_byte_number += 1 - if offset in byte_labels.keys(): - is_data = False - keep_reading = True - #else the while loop would have spit out the opcode - - #these two are done prior - #offset += 1 - #current_byte_number += 1 - - if not is_data and current_byte in relative_unconditional_jumps + end_08_scripts_with: - #stop reading at a jump, relative jump or return - if not has_outstanding_labels(byte_labels) or all_outstanding_labels_are_reverse(byte_labels, offset): - keep_reading = False - is_data = False #cleanup - break - elif offset not in byte_labels.keys() and offset in data_tables.keys(): - is_data = True - keep_reading = True - else: - is_data = False - keep_reading = True - output += "\n" - elif is_data and offset not in byte_labels.keys(): - is_data = True - keep_reading = True - else: - is_data = False - keep_reading = True - - if offset in data_tables.keys(): - output = output.replace('$%x' % (get_local_address(offset)), data_label(offset)) - output += data_label(offset) + '\n' - is_data = True - keep_reading = True - - first_loop = False - - #clean up unused labels - for label_line in byte_labels.keys(): - address = label_line - label_line = byte_labels[label_line] - if label_line["usage"] == 0: - output = output.replace((label_line["name"] + "\n"), "") - - #tone down excessive spacing - output = output.replace("\n\n\n","\n\n") - - #add the offset of the final location - if include_last_address: - output += "; " + hex(offset) - - return (output, offset, last_hl_address, last_a_address, used_3d97) - -if __name__ == "__main__": - conf = configuration.Config() - disasm = Disassembler(conf) - disasm.initialize() - - addr = sys.argv[1] - if ":" in addr: - addr = addr.split(":") - addr = int(addr[0], 16)*0x4000+(int(addr[1], 16)%0x4000) - else: - label_addr = disasm.find_address_from_label(addr) - if label_addr: - addr = label_addr - else: - addr = int(addr, 16) - - output = disasm.output_bank_opcodes(addr)[0] - print output diff --git a/extras/gfx.py b/extras/gfx.py deleted file mode 100644 index f27c17d..0000000 --- a/extras/gfx.py +++ /dev/null @@ -1,750 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import sys -import png -from math import sqrt, floor, ceil -import argparse - -def split(list_, interval): - """ - Split a list by length. - """ - for i in xrange(0, len(list_), interval): - j = min(i + interval, len(list_)) - yield list_[i:j] - -def get_tiles(image): - """ - Split a 2bpp image into 8x8 tiles. - """ - return list(split(image, 0x10)) - -def connect(tiles): - """ - Combine 8x8 tiles into a 2bpp image. - """ - return [byte for tile in tiles for byte in tile] - -def transpose(tiles, width=None): - """ - Transpose a tile arrangement along line y=-x. - - 00 01 02 03 04 05 00 06 0c 12 18 1e - 06 07 08 09 0a 0b 01 07 0d 13 19 1f - 0c 0d 0e 0f 10 11 <-> 02 08 0e 14 1a 20 - 12 13 14 15 16 17 03 09 0f 15 1b 21 - 18 19 1a 1b 1c 1d 04 0a 10 16 1c 22 - 1e 1f 20 21 22 23 05 0b 11 17 1d 23 - - 00 01 02 03 00 04 08 - 04 05 06 07 <-> 01 05 09 - 08 09 0a 0b 02 06 0a - 03 07 0b - """ - if width == None: - width = int(sqrt(len(tiles))) # assume square image - tiles = sorted(enumerate(tiles), key= lambda (i, tile): i % width) - return [tile for i, tile in tiles] - -def transpose_tiles(image, width=None): - return connect(transpose(get_tiles(image), width)) - -def interleave(tiles, width): - """ - 00 01 02 03 04 05 00 02 04 06 08 0a - 06 07 08 09 0a 0b 01 03 05 07 09 0b - 0c 0d 0e 0f 10 11 --> 0c 0e 10 12 14 16 - 12 13 14 15 16 17 0d 0f 11 13 15 17 - 18 19 1a 1b 1c 1d 18 1a 1c 1e 20 22 - 1e 1f 20 21 22 23 19 1b 1d 1f 21 23 - """ - interleaved = [] - left, right = split(tiles[::2], width), split(tiles[1::2], width) - for l, r in zip(left, right): - interleaved += l + r - return interleaved - -def deinterleave(tiles, width): - """ - 00 02 04 06 08 0a 00 01 02 03 04 05 - 01 03 05 07 09 0b 06 07 08 09 0a 0b - 0c 0e 10 12 14 16 --> 0c 0d 0e 0f 10 11 - 0d 0f 11 13 15 17 12 13 14 15 16 17 - 18 1a 1c 1e 20 22 18 19 1a 1b 1c 1d - 19 1b 1d 1f 21 23 1e 1f 20 21 22 23 - """ - deinterleaved = [] - rows = list(split(tiles, width)) - for left, right in zip(rows[::2], rows[1::2]): - for l, r in zip(left, right): - deinterleaved += [l, r] - return deinterleaved - -def interleave_tiles(image, width): - return connect(interleave(get_tiles(image), width)) - -def deinterleave_tiles(image, width): - return connect(deinterleave(get_tiles(image), width)) - - -def condense_tiles_to_map(image, pic=0): - tiles = get_tiles(image) - - # Leave the first frame intact for pics. - new_tiles = tiles[:pic] - tilemap = range(pic) - - for i, tile in enumerate(tiles[pic:]): - if tile not in new_tiles: - new_tiles += [tile] - - # Match the first frame where possible. - this_i = i % pic if pic else i - if tile == new_tiles[this_i]: - tilemap += [this_i] - else: - tilemap += [new_tiles.index(tile)] - - new_image = connect(new_tiles) - return new_image, tilemap - - -def to_file(filename, data): - """ - Apparently open(filename, 'wb').write(bytearray(data)) won't work. - """ - file = open(filename, 'wb') - for byte in data: - file.write('%c' % byte) - file.close() - -def bin_to_rgb(word): - red = word & 0b11111 - word >>= 5 - green = word & 0b11111 - word >>= 5 - blue = word & 0b11111 - return (red, green, blue) - -def convert_binary_pal_to_text_by_filename(filename): - pal = bytearray(open(filename).read()) - return convert_binary_pal_to_text(pal) - -def convert_binary_pal_to_text(pal): - output = '' - words = [hi * 0x100 + lo for lo, hi in zip(pal[::2], pal[1::2])] - for word in words: - red, green, blue = ['%.2d' % c for c in bin_to_rgb(word)] - output += '\tRGB ' + ', '.join((red, green, blue)) - output += '\n' - return output - -def read_rgb_macros(lines): - colors = [] - for line in lines: - macro = line.split(" ")[0].strip() - if macro == 'RGB': - params = ' '.join(line.split(" ")[1:]).split(',') - red, green, blue = [int(v) for v in params] - colors += [[red, green, blue]] - return colors - -def flatten(planar): - """ - Flatten planar 2bpp image data into a quaternary pixel map. - """ - strips = [] - for bottom, top in split(planar, 2): - bottom = bottom - top = top - strip = [] - for i in xrange(7,-1,-1): - color = ( - (bottom >> i & 1) + - (top *2 >> i & 2) - ) - strip += [color] - strips += strip - return strips - - -def to_lines(image, width): - """ - Convert a tiled quaternary pixel map to lines of quaternary pixels. - """ - tile_width = 8 - tile_height = 8 - num_columns = width / tile_width - height = len(image) / width - - lines = [] - for cur_line in xrange(height): - tile_row = cur_line / tile_height - line = [] - for column in xrange(num_columns): - anchor = ( - num_columns * tile_row * tile_width * tile_height + - column * tile_width * tile_height + - cur_line % tile_height * tile_width - ) - line += image[anchor : anchor + tile_width] - lines += [line] - return lines - -def pal_to_png(filename): - """ - Interpret a .pal file as a png palette. - """ - with open(filename) as rgbs: - colors = read_rgb_macros(rgbs.readlines()) - a = 255 - palette = [] - for color in colors: - # even distribution over 000-255 - r, g, b = [int(hue * 8.25) for hue in color] - palette += [(r, g, b, a)] - white = (255,255,255,255) - black = (000,000,000,255) - if white not in palette and len(palette) < 4: - palette = [white] + palette - if black not in palette and len(palette) < 4: - palette = palette + [black] - return palette - - -def png_to_rgb(palette): - """ - Convert a png palette to rgb macros. - """ - output = '' - for color in palette: - r, g, b = [color[c] / 8 for c in 'rgb'] - output += '\tRGB ' + ', '.join(['%.2d' % hue for hue in (r, g, b)]) - output += '\n' - return output - - -def read_filename_arguments(filename): - """ - Infer graphics conversion arguments given a filename. - - Arguments are separated with '.'. - """ - parsed_arguments = {} - - int_arguments = { - 'w': 'width', - 'h': 'height', - 't': 'tile_padding', - } - arguments = os.path.splitext(filename)[0].lstrip('.').split('.')[1:] - for argument in arguments: - - # Check for integer arguments first (i.e. "w128"). - arg = argument[0] - param = argument[1:] - if param.isdigit(): - arg = int_arguments.get(arg, False) - if arg: - parsed_arguments[arg] = int(param) - - elif argument == 'arrange': - parsed_arguments['norepeat'] = True - parsed_arguments['tilemap'] = True - - # Pic dimensions (i.e. "6x6"). - elif 'x' in argument and any(map(str.isdigit, argument)): - w, h = argument.split('x') - if w.isdigit() and h.isdigit(): - parsed_arguments['pic_dimensions'] = (int(w), int(h)) - - else: - parsed_arguments[argument] = True - - return parsed_arguments - - -def export_2bpp_to_png(filein, fileout=None, pal_file=None, height=0, width=0, tile_padding=0, pic_dimensions=None, **kwargs): - - if fileout == None: - fileout = os.path.splitext(filein)[0] + '.png' - - image = open(filein, 'rb').read() - - arguments = { - 'width': width, - 'height': height, - 'pal_file': pal_file, - 'tile_padding': tile_padding, - 'pic_dimensions': pic_dimensions, - } - arguments.update(read_filename_arguments(filein)) - - if pal_file == None: - if os.path.exists(os.path.splitext(fileout)[0]+'.pal'): - arguments['pal_file'] = os.path.splitext(fileout)[0]+'.pal' - - result = convert_2bpp_to_png(image, **arguments) - width, height, palette, greyscale, bitdepth, px_map = result - - w = png.Writer( - width, - height, - palette=palette, - compression=9, - greyscale=greyscale, - bitdepth=bitdepth - ) - with open(fileout, 'wb') as f: - w.write(f, px_map) - - -def convert_2bpp_to_png(image, **kwargs): - """ - Convert a planar 2bpp graphic to png. - """ - - image = bytearray(image) - - pad_color = bytearray([0]) - - width = kwargs.get('width', 0) - height = kwargs.get('height', 0) - tile_padding = kwargs.get('tile_padding', 0) - pic_dimensions = kwargs.get('pic_dimensions', None) - pal_file = kwargs.get('pal_file', None) - interleave = kwargs.get('interleave', False) - - # Width must be specified to interleave. - if interleave and width: - image = interleave_tiles(image, width / 8) - - # Pad the image by a given number of tiles if asked. - image += pad_color * 0x10 * tile_padding - - # Some images are transposed in blocks. - if pic_dimensions: - w, h = pic_dimensions - if not width: width = w * 8 - - pic_length = w * h * 0x10 - - trailing = len(image) % pic_length - - pic = [] - for i in xrange(0, len(image) - trailing, pic_length): - pic += transpose_tiles(image[i:i+pic_length], h) - image = bytearray(pic) + image[len(image) - trailing:] - - # Pad out trailing lines. - image += pad_color * 0x10 * ((w - (len(image) / 0x10) % h) % w) - - def px_length(img): - return len(img) * 4 - def tile_length(img): - return len(img) * 4 / (8*8) - - if width and height: - tile_width = width / 8 - more_tile_padding = (tile_width - (tile_length(image) % tile_width or tile_width)) - image += pad_color * 0x10 * more_tile_padding - - elif width and not height: - tile_width = width / 8 - more_tile_padding = (tile_width - (tile_length(image) % tile_width or tile_width)) - image += pad_color * 0x10 * more_tile_padding - height = px_length(image) / width - - elif height and not width: - tile_height = height / 8 - more_tile_padding = (tile_height - (tile_length(image) % tile_height or tile_height)) - image += pad_color * 0x10 * more_tile_padding - width = px_length(image) / height - - # at least one dimension should be given - if width * height != px_length(image): - # look for possible combos of width/height that would form a rectangle - matches = [] - # Height need not be divisible by 8, but width must. - # See pokered gfx/minimize_pic.1bpp. - for w in range(8, px_length(image) / 2 + 1, 8): - h = px_length(image) / w - if w * h == px_length(image): - matches += [(w, h)] - # go for the most square image - if len(matches): - width, height = sorted(matches, key= lambda (w, h): (h % 8 != 0, w + h))[0] # favor height - else: - raise Exception, 'Image can\'t be divided into tiles (%d px)!' % (px_length(image)) - - # convert tiles to lines - lines = to_lines(flatten(image), width) - - if pal_file == None: - palette = None - greyscale = True - bitdepth = 2 - px_map = [[3 - pixel for pixel in line] for line in lines] - - else: # gbc color - palette = pal_to_png(pal_file) - greyscale = False - bitdepth = 8 - px_map = [[pixel for pixel in line] for line in lines] - - return width, height, palette, greyscale, bitdepth, px_map - -def export_png_to_2bpp(filein, fileout=None, palout=None, **kwargs): - - arguments = { - 'tile_padding': 0, - 'pic_dimensions': None, - 'animate': False, - 'stupid_bitmask_hack': [], - } - arguments.update(kwargs) - arguments.update(read_filename_arguments(filein)) - - image, arguments = png_to_2bpp(filein, **arguments) - - if fileout == None: - fileout = os.path.splitext(filein)[0] + '.2bpp' - to_file(fileout, image) - - tmap = arguments.get('tmap') - - if tmap != None and arguments['animate'] and arguments['pic_dimensions']: - # Generate pic animation data. - frame_text, bitmask_text = get_pic_animation(tmap, *arguments['pic_dimensions']) - - frames_path = os.path.join(os.path.split(fileout)[0], 'frames.asm') - with open(frames_path, 'w') as out: - out.write(frame_text) - - bitmask_path = os.path.join(os.path.split(fileout)[0], 'bitmask.asm') - - # The following Pokemon have a bitmask dummied out. - for exception in arguments['stupid_bitmask_hack']: - if exception in bitmask_path: - bitmasks = bitmask_text.split(';') - bitmasks[-1] = bitmasks[-1].replace('1', '0') - bitmask_text = ';'.join(bitmasks) - - with open(bitmask_path, 'w') as out: - out.write(bitmask_text) - - elif tmap != None and arguments.get('tilemap', False): - tilemap_path = os.path.splitext(fileout)[0] + '.tilemap' - to_file(tilemap_path, tmap) - - palette = arguments.get('palette') - if palout == None: - palout = os.path.splitext(fileout)[0] + '.pal' - export_palette(palette, palout) - - -def get_image_padding(width, height, wstep=8, hstep=8): - - padding = { - 'left': 0, - 'right': 0, - 'top': 0, - 'bottom': 0, - } - - if width % wstep and width >= wstep: - pad = float(width % wstep) / 2 - padding['left'] = int(ceil(pad)) - padding['right'] = int(floor(pad)) - - if height % hstep and height >= hstep: - pad = float(height % hstep) / 2 - padding['top'] = int(ceil(pad)) - padding['bottom'] = int(floor(pad)) - - return padding - - -def png_to_2bpp(filein, **kwargs): - """ - Convert a png image to planar 2bpp. - """ - - arguments = { - 'tile_padding': 0, - 'pic_dimensions': False, - 'interleave': False, - 'norepeat': False, - 'tilemap': False, - } - arguments.update(kwargs) - - if type(filein) is str: - filein = open(filein) - - assert type(filein) is file - - width, height, rgba, info = png.Reader(filein).asRGBA8() - - # png.Reader returns flat pixel data. Nested is easier to work with - len_px = len('rgba') - image = [] - palette = [] - for line in rgba: - newline = [] - for px in xrange(0, len(line), len_px): - color = dict(zip('rgba', line[px:px+len_px])) - if color not in palette: - if len(palette) < 4: - palette += [color] - else: - # TODO Find the nearest match - print 'WARNING: %s: Color %s truncated to' % (filein, color), - color = sorted(palette, key=lambda x: sum(x.values()))[0] - print color - newline += [color] - image += [newline] - - assert len(palette) <= 4, '%s: palette should be 4 colors, is really %d (%s)' % (filein, len(palette), palette) - - # Pad out smaller palettes with greyscale colors - greyscale = { - 'black': { 'r': 0x00, 'g': 0x00, 'b': 0x00, 'a': 0xff }, - 'grey': { 'r': 0x55, 'g': 0x55, 'b': 0x55, 'a': 0xff }, - 'gray': { 'r': 0xaa, 'g': 0xaa, 'b': 0xaa, 'a': 0xff }, - 'white': { 'r': 0xff, 'g': 0xff, 'b': 0xff, 'a': 0xff }, - } - preference = 'white', 'black', 'grey', 'gray' - for hue in map(greyscale.get, preference): - if len(palette) >= 4: - break - if hue not in palette: - palette += [hue] - - palette.sort(key=lambda x: sum(x.values())) - - # Game Boy palette order - palette.reverse() - - # Map pixels to quaternary color ids - padding = get_image_padding(width, height) - width += padding['left'] + padding['right'] - height += padding['top'] + padding['bottom'] - pad = bytearray([0]) - - qmap = [] - qmap += pad * width * padding['top'] - for line in image: - qmap += pad * padding['left'] - for color in line: - qmap += [palette.index(color)] - qmap += pad * padding['right'] - qmap += pad * width * padding['bottom'] - - # Graphics are stored in tiles instead of lines - tile_width = 8 - tile_height = 8 - num_columns = max(width, tile_width) / tile_width - num_rows = max(height, tile_height) / tile_height - image = [] - - for row in xrange(num_rows): - for column in xrange(num_columns): - - # Split it up into strips to convert to planar data - for strip in xrange(min(tile_height, height)): - anchor = ( - row * num_columns * tile_width * tile_height + - column * tile_width + - strip * width - ) - line = qmap[anchor : anchor + tile_width] - bottom, top = 0, 0 - for bit, quad in enumerate(line): - bottom += (quad & 1) << (7 - bit) - top += (quad /2 & 1) << (7 - bit) - image += [bottom, top] - - dim = arguments['pic_dimensions'] - if dim: - if type(dim) in (tuple, list): - w, h = dim - else: - # infer dimensions based on width. - w = width / tile_width - h = height / tile_height - if h % w == 0: - h = w - - tiles = get_tiles(image) - pic_length = w * h - tile_width = width / 8 - trailing = len(tiles) % pic_length - new_image = [] - for block in xrange(len(tiles) / pic_length): - offset = (h * tile_width) * ((block * w) / tile_width) + ((block * w) % tile_width) - pic = [] - for row in xrange(h): - index = offset + (row * tile_width) - pic += tiles[index:index + w] - new_image += transpose(pic, w) - new_image += tiles[len(tiles) - trailing:] - image = connect(new_image) - - # Remove any tile padding used to make the png rectangular. - image = image[:len(image) - arguments['tile_padding'] * 0x10] - - tmap = None - - if arguments['interleave']: - image = deinterleave_tiles(image, num_columns) - - if arguments['pic_dimensions']: - image, tmap = condense_tiles_to_map(image, w * h) - elif arguments['norepeat']: - image, tmap = condense_tiles_to_map(image) - if not arguments['tilemap']: - tmap = None - - arguments.update({ 'palette': palette, 'tmap': tmap, }) - - return image, arguments - - -def export_palette(palette, filename): - """ - Export a palette from png to rgb macros in a .pal file. - """ - - if os.path.exists(filename): - - # Pic palettes are 2 colors (black/white are added later). - with open(filename) as rgbs: - colors = read_rgb_macros(rgbs.readlines()) - - if len(colors) == 2: - palette = palette[1:3] - - text = png_to_rgb(palette) - with open(filename, 'w') as out: - out.write(text) - -def convert_2bpp_to_1bpp(data): - """ - Convert planar 2bpp image data to 1bpp. Assume images are two colors. - """ - return data[::2] - -def convert_1bpp_to_2bpp(data): - """ - Convert 1bpp image data to planar 2bpp (black/white). - """ - output = [] - for i in data: - output += [i, i] - return output - - -def export_2bpp_to_1bpp(filename): - name, extension = os.path.splitext(filename) - image = open(filename, 'rb').read() - image = convert_2bpp_to_1bpp(image) - to_file(name + '.1bpp', image) - -def export_1bpp_to_2bpp(filename): - name, extension = os.path.splitext(filename) - image = open(filename, 'rb').read() - image = convert_1bpp_to_2bpp(image) - to_file(name + '.2bpp', image) - - -def export_1bpp_to_png(filename, fileout=None): - - if fileout == None: - fileout = os.path.splitext(filename)[0] + '.png' - - arguments = read_filename_arguments(filename) - - image = open(filename, 'rb').read() - image = convert_1bpp_to_2bpp(image) - - result = convert_2bpp_to_png(image, **arguments) - width, height, palette, greyscale, bitdepth, px_map = result - - w = png.Writer(width, height, palette=palette, compression=9, greyscale=greyscale, bitdepth=bitdepth) - with open(fileout, 'wb') as f: - w.write(f, px_map) - - -def export_png_to_1bpp(filename, fileout=None): - - if fileout == None: - fileout = os.path.splitext(filename)[0] + '.1bpp' - - arguments = read_filename_arguments(filename) - image = png_to_1bpp(filename, **arguments) - - to_file(fileout, image) - -def png_to_1bpp(filename, **kwargs): - image, kwargs = png_to_2bpp(filename, **kwargs) - return convert_2bpp_to_1bpp(image) - -def convert_to_2bpp(filenames=[]): - for filename in filenames: - filename, name, extension = try_decompress(filename) - if extension == '.1bpp': - export_1bpp_to_2bpp(filename) - elif extension == '.2bpp': - pass - elif extension == '.png': - export_png_to_2bpp(filename) - else: - raise Exception, "Don't know how to convert {} to 2bpp!".format(filename) - -def convert_to_1bpp(filenames=[]): - for filename in filenames: - filename, name, extension = try_decompress(filename) - if extension == '.1bpp': - pass - elif extension == '.2bpp': - export_2bpp_to_1bpp(filename) - elif extension == '.png': - export_png_to_1bpp(filename) - else: - raise Exception, "Don't know how to convert {} to 1bpp!".format(filename) - -def convert_to_png(filenames=[]): - for filename in filenames: - filename, name, extension = try_decompress(filename) - if extension == '.1bpp': - export_1bpp_to_png(filename) - elif extension == '.2bpp': - export_2bpp_to_png(filename) - elif extension == '.png': - pass - else: - raise Exception, "Don't know how to convert {} to png!".format(filename) - -def main(): - ap = argparse.ArgumentParser() - ap.add_argument('mode') - ap.add_argument('filenames', nargs='*') - args = ap.parse_args() - - method = { - '2bpp': convert_to_2bpp, - '1bpp': convert_to_1bpp, - 'png': convert_to_png, - }.get(args.mode, None) - - if method == None: - raise Exception, "Unknown conversion method!" - - method(args.filenames) - - -if __name__ == "__main__": - main() - diff --git a/extras/labels.py b/extras/labels.py deleted file mode 100644 index 72700a5..0000000 --- a/extras/labels.py +++ /dev/null @@ -1,213 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Various label/line-related functions. -""" - -import os -import json -import logging - -import pointers -import sym - -class Labels(object): - """ - Store all labels. - """ - - def __init__(self, config, filename="pokecrystal.map"): - """ - Setup the instance. - """ - self.config = config - self.filename = filename - self.path = os.path.join(self.config.path, self.filename) - - def initialize(self): - """ - Handle anything requiring file-loading and such. - """ - # Look for a mapfile if it's not given - if not os.path.exists(self.path): - self.filename = find_mapfile_in_dir(self.config.path) - if self.filename == None: - raise Exception, "Couldn't find any mapfiles. Run rgblink -m to create a mapfile." - self.path = os.path.join(self.config.path, self.filename) - - self.labels = sym.read_mapfile(self.path) - -def find_mapfile_in_dir(path): - for filename in os.listdir(path): - if os.path.splitext(filename)[1] == '.map': - return filename - return None - -def remove_quoted_text(line): - """get rid of content inside quotes - and also removes the quotes from the input string""" - while line.count("\"") % 2 == 0 and line.count("\"") > 0: - first = line.find("\"") - second = line.find("\"", first+1) - line = line[0:first] + line[second+1:] - while line.count("\'") % 2 == 0 and line.count("'") > 0: - first = line.find("\'") - second = line.find("\'", first+1) - line = line[0:first] + line[second+1:] - return line - -def line_has_comment_address(line, returnable={}, bank=None): - """checks that a given line has a comment - with a valid address, and returns the address in the object. - Note: bank is required if you have a 4-letter-or-less address, - because otherwise there is no way to figure out which bank - is curretly being scanned.""" - #first set the bank/offset to nada - returnable["bank"] = None - returnable["offset"] = None - returnable["address"] = None - #only valid characters are 0-9a-fA-F - valid = [str(x) for x in range(10)] + \ - [chr(x) for x in range(ord('a'), ord('f')+1)] + \ - [chr(x) for x in range(ord('A'), ord('F')+1)] - #check if there is a comment in this line - if ";" not in line: - return False - #first throw away anything in quotes - if (line.count("\"") % 2 == 0 and line.count("\"")!=0) \ - or (line.count("\'") % 2 == 0 and line.count("\'")!=0): - line = remove_quoted_text(line) - #check if there is still a comment in this line after quotes removed - if ";" not in line: - return False - #but even if there's a semicolon there must be later text - if line[-1] == ";": - return False - #and just a space doesn't count - if line[-2:] == "; ": - return False - #and multiple whitespace doesn't count either - line = line.rstrip(" ").lstrip(" ") - if line[-1] == ";": - return False - #there must be more content after the semicolon - if len(line)-1 == line.find(";"): - return False - #split it up into the main comment part - comment = line[line.find(";")+1:] - #don't want no leading whitespace - comment = comment.lstrip(" ").rstrip(" ") - #split up multi-token comments into single tokens - token = comment - if " " in comment: - #use the first token in the comment - token = comment.split(" ")[0] - if token in ["0x", "$", "x", ":"]: - return False - offset = None - #process a token with a A:B format - if ":" in token: #3:3F0A, $3:$3F0A, 0x3:0x3F0A, 3:3F0A - #split up the token - bank_piece = token.split(":")[0].lower() - offset_piece = token.split(":")[1].lower() - #filter out blanks/duds - if bank_piece in ["$", "0x", "x"] \ - or offset_piece in ["$", "0x", "x"]: - return False - #they can't have both "$" and "x" - if "$" in bank_piece and "x" in bank_piece: - return False - if "$" in offset_piece and "x" in offset_piece: - return False - #process the bank piece - if "$" in bank_piece: - bank_piece = bank_piece.replace("$", "0x") - #check characters for validity? - for c in bank_piece.replace("x", ""): - if c not in valid: - return False - bank = int(bank_piece, 16) - #process the offset piece - if "$" in offset_piece: - offset_piece = offset_piece.replace("$", "0x") - #check characters for validity? - for c in offset_piece.replace("x", ""): - if c not in valid: - return False - if len(offset_piece) == 0: - return None - offset = int(offset_piece, 16) - #filter out blanks/duds - elif token in ["$", "0x", "x"]: - return False - #can't have both "$" and "x" in the number - elif "$" in token and "x" in token: - return False - elif "x" in token and not "0x" in token: #it should be 0x - return False - elif "$" in token and not "x" in token: - token = token.replace("$", "0x") - offset = int(token, 16) - elif "0x" in token and not "$" in token: - offset = int(token, 16) - else: #might just be "1" at this point - token = token.lower() - #check if there are bad characters - for c in token: - if c not in valid: - return False - offset = int(token, 16) - if offset == None and bank == None: - return False - if bank == None: - bank = pointers.calculate_bank(offset) - returnable["bank"] = bank - returnable["offset"] = offset - returnable["address"] = pointers.calculate_pointer(offset, bank=bank) - return True - -def get_address_from_line_comment(line, bank=None): - """ - wrapper for line_has_comment_address - """ - returnable = {} - result = line_has_comment_address(line, returnable=returnable, bank=bank) - if not result: - return False - return returnable["address"] - -def line_has_label(line): - """returns True if the line has an asm label""" - if not isinstance(line, str): - raise Exception, "can't check this type of object" - line = line.rstrip(" ").lstrip(" ") - line = remove_quoted_text(line) - if ";" in line: - line = line.split(";")[0] - if 0 <= len(line) <= 1: - return False - if ":" not in line: - return False - if line[0] == ";": - return False - if line[0] == "\"": - return False - return True - -def get_label_from_line(line): - """returns the label from the line""" - #check if the line has a label - if not line_has_label(line): - return None - #split up the line - label = line.split(":")[0] - return label - -def find_labels_without_addresses(asm): - """scans the asm source and finds labels that are unmarked""" - without_addresses = [] - for (line_number, line) in enumerate(asm): - if line_has_label(line): - label = get_label_from_line(line) - if not line_has_comment_address(line): - without_addresses.append({"line_number": line_number, "line": line, "label": label}) - return without_addresses diff --git a/extras/scan_includes.py b/extras/scan_includes.py deleted file mode 100644 index 8ce9a37..0000000 --- a/extras/scan_includes.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/python -# coding: utf-8 - -""" -Recursively scan an asm file for dependencies. -""" - -import os -import sys -import argparse - - -class IncludeReader: - """ - Usage: - includer = IncludeReader() - includer.read(filename) - or - includer = IncludeReader(filename='filename.asm') - includer.read() - """ - path = '' - includes = [] - directives = ['INCLUDE', 'INCBIN'] - extensions = ['.asm'] - - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - - def read(self, filename=None): - """ - Recursively look for includes in <filename> and add them to self.includes. - """ - if filename is None: - if hasattr(self, 'filename'): - filename = os.path.join(self.path, self.filename) - else: - raise Exception, 'no filename given!' - if os.path.splitext(filename)[1] in self.extensions and os.path.exists(filename): - for line in open(filename).readlines(): - self.read_line(line) - - def read_line(self, line): - """ - Add any includes in <line> to self.includes, and look for includes in those. - """ - parts = line[:line.find(';')].split() - for directive in self.directives: - if directive in map(str.upper, parts): - include = os.path.join(self.path, "src/" + parts[parts.index(directive) + 1].split('"')[1]) - if include not in self.includes and include != "src/baserom.gbc": - self.includes.append(include) - self.read(include) - -if __name__ == '__main__': - ap = argparse.ArgumentParser() - ap.add_argument('-i', default='') - ap.add_argument('filenames', nargs='*') - args = ap.parse_args() - - includes = IncludeReader(path=args.i) - for filename in args.filenames: - includes.read(filename) - sys.stdout.write(' '.join(includes.includes)) - diff --git a/extras/wram.py b/extras/wram.py deleted file mode 100644 index b6d7fc6..0000000 --- a/extras/wram.py +++ /dev/null @@ -1,313 +0,0 @@ -# coding: utf-8 -""" -RGBDS BSS section and constant parsing. -""" - -import os - - -def separate_comment(line): - if ';' in line: - i = line.find(';') - return line[:i], line[i:] - return line, None - - -def rgbasm_to_py(text): - return text.replace('$', '0x').replace('%', '0b') - - -def make_wram_labels(wram_sections): - wram_labels = {} - for section in wram_sections: - for label in section['labels']: - if label['address'] not in wram_labels.keys(): - wram_labels[label['address']] = [] - wram_labels[label['address']] += [label['label']] - return wram_labels - -def bracket_value(string, i=0): - return string.split('[')[1 + i*2].split(']')[0] - -class BSSReader: - """ - Read rgbasm BSS/WRAM sections and associate labels with addresses. - Also reads constants/variables, even in macros. - """ - sections = [] - section = None - address = None - macros = {} - constants = {} - - section_types = { - 'VRAM': 0x8000, - 'SRAM': 0xa000, - 'WRAM0': 0xc000, - 'WRAMX': 0xd000, - 'HRAM': 0xff80, - } - - def __init__(self, *args, **kwargs): - self.__dict__.update(kwargs) - - def read_bss_line(self, l): - parts = l.strip().split(' ') - token = parts[0].strip() - params = ' '.join(parts[1:]).split(',') - - if token in ['ds', 'db', 'dw']: - if any(params): - length = eval(rgbasm_to_py(params[0]), self.constants.copy()) - else: - length = {'ds': 1, 'db': 1, 'dw': 2}[token] - self.address += length - # assume adjacent labels to use the same space - for label in self.section['labels'][::-1]: - if label['length'] == 0: - label['length'] = length - else: - break - - elif token in self.macros.keys(): - macro_text = '\n'.join(self.macros[token]) + '\n' - for i, p in enumerate(params): - macro_text = macro_text.replace('\\'+str(i+1),p) - macro_text = macro_text.split('\n') - macro_reader = BSSReader( - sections = list(self.sections), - section = dict(self.section), - address = self.address, - constants = self.constants, - ) - macro_sections = macro_reader.read_bss_sections(macro_text) - self.section = macro_sections[-1] - if self.section['labels']: - self.address = self.section['labels'][-1]['address'] + self.section['labels'][-1]['length'] - - - def read_bss_sections(self, bss): - - if self.section is None: - self.section = { - "labels": [], - } - - if type(bss) is str: - bss = bss.split('\n') - - macro = False - macro_name = None - for line in bss: - line = line.lstrip() - line, comment = separate_comment(line) - line = line.strip() - split_line = line.split() - split_line_upper = map(str.upper, split_line) - - if not line: - pass - - elif line[-4:].upper() == 'ENDM': - macro = False - macro_name = None - - elif macro: - self.macros[macro_name] += [line] - - elif line[-5:].upper() == 'MACRO': - macro_name = line.split(':')[0] - macro = True - self.macros[macro_name] = [] - - elif 'INCLUDE' == line[:7].upper(): - filename = line.split('"')[1] - self.read_bss_sections(open(filename).readlines()) - - elif 'SECTION' == line[:7].upper(): - if self.section: # previous - self.sections += [self.section] - - section_def = line.split(',') - name = section_def[0].split('"')[1] - type_ = section_def[1].strip() - if len(section_def) > 2: - bank = bracket_value(section_def[2]) - else: - bank = None - - if '[' in type_: - self.address = int(rgbasm_to_py(bracket_value(type_)), 16) - else: - if self.address == None or bank != self.section['bank'] or self.section['type'] != type_: - self.address = self.section_types.get(type_, self.address) - # else: keep going from this address - - self.section = { - 'name': name, - 'type': type_, - 'bank': bank, - 'start': self.address, - 'labels': [], - } - - elif ':' in line: - # rgbasm allows labels without :, but prefer convention - label = line[:line.find(':')] - if '\\' in label: - raise Exception, line + ' ' + label - if ';' not in label: - section_label = { - 'label': label, - 'address': self.address, - 'length': 0, - } - self.section['labels'] += [section_label] - self.read_bss_line(line.split(':')[-1]) - - elif any(x in split_line_upper for x in ['EQU', '=', 'SET']): # TODO: refactor - for x in ['EQU', '=', 'SET']: - if x in split_line_upper: - index = split_line_upper.index(x) - real = split_line[index] - name, value = map(' '.join, [split_line[:index], split_line[index+1:]]) - value = rgbasm_to_py(value) - self.constants[name] = eval(value, self.constants.copy()) - - else: - self.read_bss_line(line) - - self.sections += [self.section] - return self.sections - -def read_bss_sections(bss): - reader = BSSReader() - return reader.read_bss_sections(bss) - - -def constants_to_dict(constants): - """Deprecated. Use BSSReader.""" - return dict((eval(rgbasm_to_py(constant[constant.find('EQU')+3:constant.find(';')])), constant[:constant.find('EQU')].strip()) for constant in constants) - -def scrape_constants(text): - if type(text) is not list: - text = text.split('\n') - bss = BSSReader() - bss.read_bss_sections(text) - constants = bss.constants - return {v: k for k, v in constants.items()} - -def read_constants(filepath): - """ - Load lines from a file and grab any constants using BSSReader. - """ - lines = [] - if os.path.exists(filepath): - with open(filepath, "r") as file_handler: - lines = file_handler.readlines() - return scrape_constants(lines) - -class WRAMProcessor(object): - """ - RGBDS BSS section and constant parsing. - """ - - def __init__(self, config): - """ - Setup for WRAM parsing. - """ - self.config = config - - self.paths = {} - - if hasattr(self.config, "wram"): - self.paths["wram"] = self.config.wram - else: - self.paths["wram"] = os.path.join(self.config.path, "wram.asm") - - if hasattr(self.config, "hram"): - self.paths["hram"] = self.config.hram - else: - self.paths["hram"] = os.path.join(self.config.path, "hram.asm") - - if hasattr(self.config, "gbhw"): - self.paths["gbhw"] = self.config.gbhw - else: - self.paths["gbhw"] = os.path.join(self.config.path, "gbhw.asm") - - def initialize(self): - """ - Read constants. - """ - self.setup_wram_sections() - self.setup_wram_labels() - self.setup_hram_constants() - self.setup_gbhw_constants() - - self.reformat_wram_labels() - - def read_wram_sections(self): - """ - Opens the wram file and calls read_bss_sections. - """ - wram_content = None - wram_file_path = self.paths["wram"] - - with open(wram_file_path, "r") as wram: - wram_content = wram.readlines() - - wram_sections = read_bss_sections(wram_content) - return wram_sections - - def setup_wram_sections(self): - """ - Call read_wram_sections and set a variable. - """ - self.wram_sections = self.read_wram_sections() - return self.wram_sections - - def setup_wram_labels(self): - """ - Make wram labels based on self.wram_sections as input. - """ - self.wram_labels = make_wram_labels(self.wram_sections) - return self.wram_labels - - def read_hram_constants(self): - """ - Read constants from hram.asm using read_constants. - """ - hram_constants = read_constants(self.paths["hram"]) - return hram_constants - - def setup_hram_constants(self): - """ - Call read_hram_constants and set a variable. - """ - self.hram_constants = self.read_hram_constants() - return self.hram_constants - - def read_gbhw_constants(self): - """ - Read constants from gbhw.asm using read_constants. - """ - gbhw_constants = read_constants(self.paths["gbhw"]) - return gbhw_constants - - def setup_gbhw_constants(self): - """ - Call read_gbhw_constants and set a variable. - """ - self.gbhw_constants = self.read_gbhw_constants() - return self.gbhw_constants - - def reformat_wram_labels(self): - """ - Flips the wram_labels dictionary the other way around to access - addresses by label. - """ - self.wram = {} - - for (address, labels) in self.wram_labels.iteritems(): - for label in labels: - self.wram[label] = address |