Advertisement
Guest User

wram.py

a guest
Mar 25th, 2019
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.21 KB | None | 0 0
  1. # coding: utf-8
  2. """
  3. RGBDS BSS section and constant parsing.
  4. """
  5.  
  6. import os
  7. import os.path
  8.  
  9.  
  10. def separate_comment(line):
  11.     if ';' in line:
  12.         i = line.find(';')
  13.         return line[:i], line[i:]
  14.     return line, None
  15.  
  16.  
  17. def rgbasm_to_py(text):
  18.     return text.replace('$', '0x').replace('%', '0b')
  19.  
  20.  
  21. def make_wram_labels(wram_sections):
  22.     wram_labels = {}
  23.     for section in wram_sections:
  24.         for label in section['labels']:
  25.             if label['address'] not in list(wram_labels.keys()):
  26.                 wram_labels[label['address']] = []
  27.             wram_labels[label['address']] += [label['label']]
  28.     return wram_labels
  29.  
  30. def bracket_value(string, i=0):
  31.     return string.split('[')[1 + i*2].split(']')[0]
  32.  
  33. import ast
  34.  
  35.    
  36. class BSSReader:
  37.     """
  38.    Read rgbasm BSS/WRAM sections and associate labels with addresses.
  39.    Also reads constants/variables, even in macros.
  40.    """
  41.     sections  = []
  42.     section   = None
  43.     address   = None
  44.     macros    = {}
  45.     constants = {}
  46.  
  47.     section_types = {
  48.         'VRAM':  0x8000,
  49.         'SRAM':  0xa000,
  50.         'WRAM0': 0xc000,
  51.         'WRAMX': 0xd000,
  52.         'HRAM':  0xff80,
  53.     }
  54.  
  55.     def __init__(self, *args, **kwargs):
  56.         self.__dict__.update(kwargs)
  57.  
  58.    
  59.        
  60.     def read_bss_line(self, l):
  61.         parts = l.strip().split(' ')
  62.         token = parts[0].strip()
  63.         params = ' '.join(parts[1:]).split(',')
  64.  
  65.         if token in ['ds', 'db', 'dw']:
  66.             if any(params):
  67.                 length = eval(rgbasm_to_py(params[0]), self.constants.copy())
  68.             else:
  69.                 length = {'ds': 1, 'db': 1, 'dw': 2}[token]
  70.             self.address += length
  71.             # assume adjacent labels to use the same space
  72.             for label in self.section['labels'][::-1]:
  73.                 if label['length'] == 0:
  74.                     label['length'] = length
  75.                 else:
  76.                     break
  77.  
  78.         elif token in list(self.macros.keys()):
  79.             macro_text = '\n'.join(self.macros[token]) + '\n'
  80.             for i, p in enumerate(params):
  81.                 macro_text = macro_text.replace('\\'+str(i+1), p)
  82.             macro_text = macro_text.split('\n')
  83.             macro_reader = BSSReader(
  84.                 sections  = list(self.sections),
  85.                 section   = dict(self.section),
  86.                 address   = self.address,
  87.                 constants = self.constants,
  88.             )
  89.             macro_sections = macro_reader.read_bss_sections(macro_text)
  90.             self.section = macro_sections[-1]
  91.             if self.section['labels']:
  92.                 self.address = self.section['labels'][-1]['address'] + self.section['labels'][-1]['length']
  93.  
  94.  
  95.     def read_bss_sections(self, bss):
  96.  
  97.         if self.section is None:
  98.             self.section = {
  99.                 "labels": [],
  100.             }
  101.  
  102.         if isinstance(bss, str):
  103.             bss = bss.split('\n')
  104.  
  105.         macro = False
  106.         macro_name = None
  107.         for line in bss:
  108.             line = line.lstrip()
  109.             line, comment = separate_comment(line)
  110.             line = line.strip()
  111.             split_line = line.split()
  112.             split_line_upper = list(map(str.upper, split_line))
  113.  
  114.             if not line:
  115.                 pass
  116.  
  117.             elif line[-4:].upper() == 'ENDM':
  118.                 macro = False
  119.                 macro_name = None
  120.  
  121.             elif macro:
  122.                 self.macros[macro_name] += [line]
  123.  
  124.             elif line[-5:].upper() == 'MACRO':
  125.                 macro_name = line.split(':')[0]
  126.                 macro = True
  127.                 self.macros[macro_name] = []
  128.  
  129.             elif 'INCLUDE' == line[:7].upper():
  130.                 filename = line.split('"')[1]
  131.                 if os.path.exists("src/"):
  132.                     self.read_bss_sections(open("src/" + filename).readlines())
  133.                 else:
  134.                     self.read_bss_sections(open(filename).readlines())
  135.  
  136.             elif 'SECTION' == line[:7].upper():
  137.                 if self.section: # previous
  138.                     self.sections += [self.section]
  139.  
  140.                 section_def = line.split(',')
  141.                 name  = section_def[0].split('"')[1]
  142.                 type_ = section_def[1].strip()
  143.                 if len(section_def) > 2:
  144.                     bank = bracket_value(section_def[2])
  145.                 else:
  146.                     bank = None
  147.  
  148.                 if '[' in type_:
  149.                     self.address = int(rgbasm_to_py(bracket_value(type_)), 16)
  150.                 else:
  151.                     if self.address == None or bank != self.section['bank'] or self.section['type'] != type_:
  152.                         self.address = self.section_types.get(type_, self.address)
  153.                     # else: keep going from this address
  154.  
  155.                 self.section = {
  156.                     'name': name,
  157.                     'type': type_,
  158.                     'bank': bank,
  159.                     'start': self.address,
  160.                     'labels': [],
  161.                 }
  162.  
  163.             elif ':' in line:
  164.                 # rgbasm allows labels without :, but prefer convention
  165.                 label = line[:line.find(':')]
  166.                 if '\\' in label:
  167.                     raise Exception(line + ' ' + label)
  168.                 if ';' not in label:
  169.                     section_label = {
  170.                         'label': label,
  171.                         'address': self.address,
  172.                         'length': 0,
  173.                     }
  174.                     self.section['labels'] += [section_label]
  175.                     self.read_bss_line(line.split(':')[-1])
  176.  
  177.             elif any(x in split_line_upper for x in ['EQU', '=', 'SET']): # TODO: refactor
  178.                 for x in ['EQU', '=', 'SET']:
  179.                     if x in split_line_upper:
  180.                         index = split_line_upper.index(x)
  181.                         real = split_line[index]
  182.                         name, value = list(map(' '.join, [split_line[:index], split_line[index+1:]]))
  183.                         value = rgbasm_to_py(value)
  184.                         self.constants[name] = eval(value, self.constants.copy())
  185.  
  186.             else:
  187.                 self.read_bss_line(line)
  188.  
  189.         self.sections += [self.section]
  190.         return self.sections
  191.  
  192. def read_bss_sections(bss):
  193.     reader = BSSReader()
  194.     return reader.read_bss_sections(bss)
  195.  
  196.  
  197. def constants_to_dict(constants):
  198.     """Deprecated. Use BSSReader."""
  199.     return dict((eval(rgbasm_to_py(constant[constant.find('EQU')+3:constant.find(';')])), constant[:constant.find('EQU')].strip()) for constant in constants)
  200.  
  201. def scrape_constants(text):
  202.     if not isinstance(text, list):
  203.         text = text.split('\n')
  204.     bss = BSSReader()
  205.     bss.read_bss_sections(text)
  206.     constants = bss.constants
  207.     return {v: k for k, v in list(constants.items())}
  208.  
  209. def read_constants(filepath):
  210.     """
  211.    Load lines from a file and grab any constants using BSSReader.
  212.    """
  213.     lines = []
  214.     if os.path.exists(filepath):
  215.         with open(filepath, "r") as file_handler:
  216.             lines = file_handler.readlines()
  217.     return scrape_constants(lines)
  218.  
  219. class WRAMProcessor(object):
  220.     """
  221.    RGBDS BSS section and constant parsing.
  222.    """
  223.  
  224.     def __init__(self, config):
  225.         """
  226.        Setup for WRAM parsing.
  227.        """
  228.         self.config = config
  229.  
  230.         self.paths = {}
  231.  
  232.         if os.path.exists("src/"):
  233.             path = "src/"
  234.         else:
  235.             path = ""
  236.  
  237.         if hasattr(self.config, "wram"):
  238.             self.paths["wram"] = self.config.wram
  239.         else:
  240.             self.paths["wram"] = os.path.join(self.config.path, path + "wram.asm")
  241.  
  242.         if hasattr(self.config, "hram"):
  243.             self.paths["hram"] = self.config.hram
  244.         else:
  245.             self.paths["hram"] = os.path.join(self.config.path, path + "hram.asm")
  246.  
  247.         if hasattr(self.config, "gbhw"):
  248.             self.paths["gbhw"] = self.config.gbhw
  249.         else:
  250.             self.paths["gbhw"] = os.path.join(self.config.path, path + "gbhw.asm")
  251.  
  252.     def initialize(self):
  253.         """
  254.        Read constants.
  255.        """
  256.         self.setup_wram_sections()
  257.         self.setup_wram_labels()
  258.         self.setup_hram_constants()
  259.         self.setup_gbhw_constants()
  260.  
  261.         self.reformat_wram_labels()
  262.  
  263.     def read_wram_sections(self):
  264.         """
  265.        Opens the wram file and calls read_bss_sections.
  266.        """
  267.         wram_content = None
  268.         wram_file_path = self.paths["wram"]
  269.  
  270.         with open(wram_file_path, "r") as wram:
  271.             wram_content = wram.readlines()
  272.  
  273.         wram_sections = read_bss_sections(wram_content)
  274.         return wram_sections
  275.  
  276.     def setup_wram_sections(self):
  277.         """
  278.        Call read_wram_sections and set a variable.
  279.        """
  280.         self.wram_sections = self.read_wram_sections()
  281.         return self.wram_sections
  282.  
  283.     def setup_wram_labels(self):
  284.         """
  285.        Make wram labels based on self.wram_sections as input.
  286.        """
  287.         self.wram_labels = make_wram_labels(self.wram_sections)
  288.         return self.wram_labels
  289.  
  290.     def read_hram_constants(self):
  291.         """
  292.        Read constants from hram.asm using read_constants.
  293.        """
  294.         hram_constants = read_constants(self.paths["hram"])
  295.         return hram_constants
  296.  
  297.     def setup_hram_constants(self):
  298.         """
  299.        Call read_hram_constants and set a variable.
  300.        """
  301.         self.hram_constants = self.read_hram_constants()
  302.         return self.hram_constants
  303.  
  304.     def read_gbhw_constants(self):
  305.         """
  306.        Read constants from gbhw.asm using read_constants.
  307.        """
  308.         gbhw_constants = read_constants(self.paths["gbhw"])
  309.         return gbhw_constants
  310.  
  311.     def setup_gbhw_constants(self):
  312.         """
  313.        Call read_gbhw_constants and set a variable.
  314.        """
  315.         self.gbhw_constants = self.read_gbhw_constants()
  316.         return self.gbhw_constants
  317.  
  318.     def reformat_wram_labels(self):
  319.         """
  320.        Flips the wram_labels dictionary the other way around to access
  321.        addresses by label.
  322.        """
  323.         self.wram = {}
  324.  
  325.         for (address, labels) in list(self.wram_labels.items()):
  326.             for label in labels:
  327.                 self.wram[label] = address
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement