def poke(snapshot, param_str): try: addr, val = param_str.split(',', 1) except ValueError: raise SkoolKitError("Value missing in poke spec: {}".format(param_str)) try: if val.startswith('^'): value = get_int_param(val[1:]) poke_f = lambda b: b ^ value elif val.startswith('+'): value = get_int_param(val[1:]) poke_f = lambda b: (b + value) & 255 else: value = get_int_param(val) poke_f = lambda b: value except ValueError: raise SkoolKitError('Invalid value in poke spec: {}'.format(param_str)) try: values = [get_int_param(i) for i in addr.split('-', 2)] except ValueError: raise SkoolKitError( 'Invalid address range in poke spec: {}'.format(param_str)) addr1, addr2, step = values + [values[0], 1][len(values) - 1:] for a in range(addr1, addr2 + 1, step): snapshot[a] = poke_f(snapshot[a])
def set_z80_registers(z80, *specs): for spec in specs: reg, sep, val = spec.lower().partition('=') if sep: if reg.startswith('^'): size = len(reg) - 1 else: size = len(reg) if reg == 'pc' and z80[6:8] != [0, 0]: offset = 6 else: offset = Z80_REGISTERS.get(reg, -1) if offset >= 0: try: value = get_int_param(val, True) except ValueError: raise SkoolKitError("Cannot parse register value: {}".format(spec)) lsb, msb = value % 256, (value & 65535) // 256 if size == 1: z80[offset] = lsb elif size == 2: z80[offset:offset + 2] = [lsb, msb] if reg == 'r' and lsb & 128: z80[12] |= 1 else: raise SkoolKitError('Invalid register: {}'.format(spec))
def _analyse_tzx(tzx, basic_block, options): if tzx[:8] != bytearray((90, 88, 84, 97, 112, 101, 33, 26)): raise SkoolKitError("Not a TZX file") try: version = 'Version: {}.{}'.format(tzx[8], tzx[9]) if not basic_block: print(version) except IndexError: raise SkoolKitError('TZX version number not found') block_ids = set() if options.block_ids: for block_id in options.block_ids.split(','): try: block_ids.add(int(block_id, 16)) except ValueError: block_ids.add(-1) block_num = 1 i = 10 while i < len(tzx): i, block_id, header, info, tape_data = _get_block_info( tzx, i, block_num) if basic_block: _list_basic(block_num, tape_data, *basic_block) elif not block_ids or block_id in block_ids: _print_block(block_num, tape_data, options.data, info, block_id, header) block_num += 1
def parse_block(self, text, begin): try: index = parse_brackets(text, begin)[0] except ClosingBracketError: raise SkoolKitError("No closing ')' on parameter list: {}...".format(text[begin:begin + 15])) try: index, flag = parse_brackets(text, index, '', '<', '>') except ClosingBracketError: raise SkoolKitError("No closing '>' on flags: {}...".format(text[index:index + 15])) wrap_flag = 1 if flag == 'nowrap': wrap_flag = 0 elif flag == 'wrapalign': wrap_flag = 2 indexes = [(index, 1)] # Parse the table rows or list items while True: start = text.find('{ ', index) if start < 0: break try: end = text.index(' }', start) except ValueError: raise SkoolKitError("No closing ' }}' on row/item: {}...".format(text[start:start + 15])) index = end + 2 indexes.append((index, wrap_flag)) indexes.append((len(text), 1)) return indexes
def move(snapshot, param_str): params = param_str.split(',', 2) if len(params) < 3: raise SkoolKitError("Not enough arguments in move spec (expected 3): {}".format(param_str)) try: src, length, dest = [get_int_param(p, True) for p in params] except ValueError: raise SkoolKitError('Invalid integer in move spec: {}'.format(param_str)) snapshot[dest:dest + length] = snapshot[src:src + length]
def run(infile, outfile, options): if options.binary or options.org is not None: snapshot = read_bin_file(infile, 49152) if options.org is None: org = 65536 - len(snapshot) else: org = options.org snapshot = [0] * org + list(snapshot) + [0] * (65536 - org - len(snapshot)) elif infile[-4:].lower() == '.scr': scr = read_bin_file(infile, 6912) snapshot = [0] * 65536 snapshot[16384:16384 + len(scr)] = scr elif infile[-4:].lower() in ('.sna', '.szx', '.z80'): snapshot = get_snapshot(infile) else: try: snapshot = BinWriter(infile, fix_mode=options.fix_mode).snapshot except SkoolKitError: raise except: raise SkoolKitError( 'Unable to parse {} as a skool file'.format(infile)) for spec in options.moves: move(snapshot, spec) for spec in options.pokes: poke(snapshot, spec) if options.macro is not None: match = re.match('(#?)(FONT|SCR|UDG|UDGARRAY)([^A-Z]|$)', options.macro) if match: macro = match.group(2) try: frame = MACROS[macro](snapshot, options.macro[match.end(2):]) except skoolmacro.MacroParsingError as e: raise SkoolKitError('Invalid #{} macro: {}'.format( macro, e.args[0])) else: raise SkoolKitError('Macro must be #FONT, #SCR, #UDG or #UDGARRAY') else: (x, y), (w, h) = options.origin, options.size frame = Frame(scr_udgs(snapshot, x, y, w, h), options.scale) if options.invert: for row in frame.udgs: for udg in row: if udg.attr & 128: udg.data = [b ^ 255 for b in udg.data] udg.attr &= 127 flip_udgs(frame.udgs, options.flip) rotate_udgs(frame.udgs, options.rotate) _write_image(frame, outfile, options.animated)
def _get_code_blocks(snapshot, start, end, fname): if os.path.isdir(fname): raise SkoolKitError('{0} is a directory'.format(fname)) try: size = os.path.getsize(fname) except OSError as e: if e.errno == 2: raise SkoolKitError('{0}: file not found'.format(fname)) raise SkoolKitError('Failed to get size of {}: {}'.format( fname, e.strerror)) if size == 8192: # Assume this is a Z80 map file sys.stderr.write('Reading {0}'.format(fname)) sys.stderr.flush() addresses = [] data = read_bin_file(fname) address = start & 65528 for b in data[start // 8:end // 8 + 1]: for i in range(8): if b & 1 and start <= address < end: addresses.append(address) b >>= 1 address += 1 elif size == 65536: # Assume this is a SpecEmu map file sys.stderr.write('Reading {}'.format(fname)) sys.stderr.flush() addresses = [] data = read_bin_file(fname) for address in range(start, end): if data[address] & 1: addresses.append(address) else: sys.stderr.write('Reading {0}: '.format(fname)) sys.stderr.flush() with open_file(fname) as f: addresses = _get_addresses(f, fname, size, start, end) sys.stderr.write('\n') code_blocks = [] disassembler = Disassembler(snapshot) for address in addresses: size = disassembler.disassemble(address, address + 1)[0].size() if code_blocks and address <= sum(code_blocks[-1]): if address == sum(code_blocks[-1]): code_blocks[-1][1] += size else: code_blocks.append([address, size]) return code_blocks
def copy_resources(search_dir, extra_search_dirs, root_dir, fnames, dest_dir, themes=(), suffix=None, single_css=None, indent=0): if not fnames: return files = [] for fname in fnames.split(';'): f = find(fname, extra_search_dirs, search_dir) if f: files.append(f) else: raise SkoolKitError('{}: file not found'.format(normpath(fname))) if themes and fname.endswith(suffix): for theme in themes: f = find('{}-{}{}'.format(fname[:-len(suffix)], theme, suffix), extra_search_dirs, search_dir) if f: files.append(f) for theme in themes: f = find('{}{}'.format(theme, suffix), extra_search_dirs, search_dir) if f: files.append(f) if single_css: dest_css = normpath(root_dir, dest_dir, single_css) if isdir(dest_css): raise SkoolKitError( "Cannot write CSS file '{}': {} already exists and is a directory" .format(normpath(single_css), dest_css)) with open(dest_css, 'w') as css: for f in files: notify('{}Appending {} to {}'.format(indent * ' ', normpath(f), dest_css)) with open(f) as src: for line in src: css.write(line) css.write('\n') return single_css for f in files: copy_resource(f, root_dir, dest_dir, indent) return ';'.join([basename(f) for f in files])
def pop_snapshot(self): """Discard the current memory snapshot and replace it with the one that was most recently saved (by :meth:`~skoolkit.skoolasm.AsmWriter.push_snapshot`).""" if len(self._snapshots) < 2: raise SkoolKitError("Cannot pop snapshot when snapshot stack is empty") self.snapshot = self._snapshots.pop()[0]
def parse_blocks(self, text): markers = ((TABLE_MARKER, TABLE_END_MARKER), (UDGTABLE_MARKER, TABLE_END_MARKER), (LIST_MARKER, LIST_END_MARKER)) indexes = [] # Find table/list markers and row/item definitions index = 0 while True: starts = [text.find(marker[0], index) for marker in markers] for i, start in enumerate(starts): if start >= 0: if start > 0: indexes.append(start - 1) marker, end_marker = markers[i] try: end = text.index(end_marker, start) + len(end_marker) except ValueError: raise SkoolKitError("No end marker found: {}...".format(text[start:start + len(marker) + 15])) indexes.extend(self.parse_block(text[:end], start + len(marker))) break else: break index = indexes[-1] + 1 # Insert newlines end = len(text) if end not in indexes: indexes.append(end) indexes.sort() lines = [] start = 0 for end in indexes: lines.append(text[start:end]) start = end + 1 return lines
def add_lines(ref_parser, config_specs, section=None): for config_spec in config_specs: section_name, sep, line = config_spec.partition('/') if not sep: raise SkoolKitError("Malformed SectionName/Line spec: {0}".format(config_spec)) if section is None or section_name == section: ref_parser.add_line(section_name, line)
def parse_blocks(self, text): markers = ((TABLE_MARKER, TABLE_END_MARKER), (UDGTABLE_MARKER, TABLE_END_MARKER), (LIST_MARKER, LIST_END_MARKER)) indexes = [] # Find table/list markers and row/item definitions index = 0 while True: starts = [(marker[0], marker[1], text.find(marker[0], index)) for marker in markers] for marker, end_marker, start in starts: if start >= 0: if start > 0: indexes.append((start - 1, 1)) try: end = text.index(end_marker, start) + len(end_marker) except ValueError: raise SkoolKitError("No end marker found: {}...".format(text[start:start + len(marker) + 15])) indexes.extend(self.parse_block(text[:end], start + len(marker))) break else: break index = indexes[-1][0] + 1 if not indexes or indexes[-1][0] != len(text): indexes.append((len(text), 1)) indexes.sort(key=lambda e: e[0]) lines = [] start = 0 for end, wrap_flag in indexes: lines.append((text[start:end].strip(), wrap_flag)) start = end return lines
def set_z80_state(z80, *specs): for spec in specs: name, sep, val = spec.lower().partition('=') try: if name == 'iff': z80[27] = z80[28] = get_int_param(val) & 255 elif name == 'im': z80[29] &= 252 # Clear bits 0 and 1 z80[29] |= get_int_param(val) & 3 elif name == 'border': z80[12] &= 241 # Clear bits 1-3 z80[12] |= (get_int_param(val) & 7) * 2 # Border colour else: raise SkoolKitError("Invalid parameter: {}".format(spec)) except ValueError: raise SkoolKitError("Cannot parse integer: {}".format(spec))
def _load(snapshot, counters, blocks, param_str): try: block_num, start, length, step, offset, inc = _get_load_params( param_str) except ValueError: raise SkoolKitError( 'Invalid integer in load spec: {}'.format(param_str)) default_index = 1 load_last = False if block_num.startswith('+'): block_num = block_num[1:] default_index = 0 if block_num.endswith('+'): block_num = block_num[:-1] load_last = True block_num = get_int_param(block_num) index = counters.setdefault(block_num, default_index) try: block = blocks[block_num - 1] except IndexError: raise TapeError("Block {} not found".format(block_num)) if length is None and load_last: length = len(block) - index length = _load_block(snapshot, block, start, length, step, offset, inc, index) counters[block_num] += length
def main(args): parser = argparse.ArgumentParser( usage="tapinfo.py FILE", description="Show the blocks in a TAP or TZX file.", add_help=False ) parser.add_argument('infile', help=argparse.SUPPRESS, nargs='?') group = parser.add_argument_group('Options') group.add_argument('-b', '--tzx-blocks', dest='block_ids', metavar='IDs', help="Show TZX blocks with these IDs only. " "'IDs' is a comma-separated list of hexadecimal block IDs, e.g. 10,11,2a.") group.add_argument('-B', '--basic', metavar='N[,A]', help='List the BASIC program in block N loaded at address A (default 23755).') group.add_argument('-V', '--version', action='version', version='SkoolKit {}'.format(VERSION), help='Show SkoolKit version number and exit.') namespace, unknown_args = parser.parse_known_args(args) if unknown_args or namespace.infile is None: parser.exit(2, parser.format_help()) infile = namespace.infile tape_type = infile[-4:].lower() if tape_type not in ('.tap', '.tzx'): raise SkoolKitError('Unrecognised tape type') basic_block = _get_basic_block(namespace.basic) with open(infile, 'rb') as f: tape = f.read() if tape_type == '.tap': _analyse_tap(tape, basic_block) else: _analyse_tzx(tape, basic_block, namespace)
def pop_snapshot(self): """Replace the current memory snapshot with the one most recently saved by :meth:`~skoolkit.skoolasm.AsmWriter.push_snapshot`.""" if len(self._snapshots) < 2: raise SkoolKitError( "Cannot pop snapshot when snapshot stack is empty") self.snapshot[:] = self._snapshots.pop()[0]
def _get_basic_block(spec): if spec: try: if ',' in spec: return [get_int_param(i) for i in spec.split(',', 1)] return get_int_param(spec), 23755 except ValueError: raise SkoolKitError('Invalid block specification: {}'.format(spec))
def _get_address_ranges(specs, step=1): addr_ranges = [] for addr_range in specs: try: values = [get_int_param(i, True) for i in addr_range.split('-', 2)] except ValueError: raise SkoolKitError('Invalid address range: {}'.format(addr_range)) addr_ranges.append(values + [values[0], step][len(values) - 1:]) return addr_ranges
def _get_basic_block(spec): if spec: try: if ',' in spec: params = spec.split(',', 1) return get_int_param(params[0]), get_int_param(params[1], True) return get_int_param(spec), 23755 except ValueError: raise SkoolKitError('Invalid block specification: {}'.format(spec))
def parse_ref_files(reffiles, ref_parser, fnames, search_dir=''): for f in fnames: if f: ref_f = normpath(search_dir, f) if not isfile(os.path.join(search_dir, f)): raise SkoolKitError('{}: file not found'.format(ref_f)) if ref_f not in reffiles: reffiles.append(ref_f) ref_parser.parse(ref_f)
def main(args): parser = argparse.ArgumentParser( usage='snapinfo.py [options] file', description="Analyse an SNA, SZX or Z80 snapshot.", add_help=False ) parser.add_argument('infile', help=argparse.SUPPRESS, nargs='?') group = parser.add_argument_group('Options') group.add_argument('-b', '--basic', action='store_true', help='List the BASIC program.') group.add_argument('-f', '--find', metavar='A[,B...[-M[-N]]]', help='Search for the byte sequence A,B... with distance ranging from M to N (default=1) between bytes.') group.add_argument('-p', '--peek', metavar='A[-B[-C]]', action='append', help='Show the contents of addresses A TO B STEP C. This option may be used multiple times.') group.add_argument('-t', '--find-text', dest='text', metavar='TEXT', help='Search for a text string.') group.add_argument('-T', '--find-tile', dest='tile', metavar='X,Y[-M[-N]]', help='Search for the graphic data of the tile at (X,Y) with distance ranging from M to N (default=1) between bytes.') group.add_argument('-v', '--variables', action='store_true', help='List variables.') group.add_argument('-V', '--version', action='version', version='SkoolKit {}'.format(VERSION), help='Show SkoolKit version number and exit.') group.add_argument('-w', '--word', metavar='A[-B[-C]]', action='append', help='Show the words at addresses A TO B STEP C. This option may be used multiple times.') namespace, unknown_args = parser.parse_known_args(args) if unknown_args or namespace.infile is None: parser.exit(2, parser.format_help()) infile = namespace.infile snapshot_type = infile[-4:].lower() if snapshot_type not in ('.sna', '.szx', '.z80'): raise SkoolKitError('Unrecognised snapshot type') if any((namespace.find, namespace.tile, namespace.text, namespace.peek, namespace.word, namespace.basic, namespace.variables)): snapshot = get_snapshot(infile) if namespace.find: _find(snapshot, namespace.find) elif namespace.tile: _find_tile(snapshot, namespace.tile) elif namespace.text: _find_text(snapshot, namespace.text) elif namespace.peek: _peek(snapshot, namespace.peek) elif namespace.word: _word(snapshot, namespace.word) else: if namespace.basic: print(BasicLister().list_basic(snapshot)) if namespace.variables: print(VariableLister().list_variables(snapshot)) elif snapshot_type == '.sna': _analyse_sna(infile) elif snapshot_type == '.z80': _analyse_z80(infile) else: _analyse_szx(infile)
def _find(snapshot, byte_seq, base_addr=16384): steps = '1' if '-' in byte_seq: byte_seq, steps = byte_seq.split('-', 1) try: byte_values = [get_int_param(i, True) for i in byte_seq.split(',')] except ValueError: raise SkoolKitError('Invalid byte sequence: {}'.format(byte_seq)) try: if '-' in steps: limits = [get_int_param(n, True) for n in steps.split('-', 1)] steps = range(limits[0], limits[1] + 1) else: steps = [get_int_param(steps, True)] except ValueError: raise SkoolKitError('Invalid distance: {}'.format(steps)) for step in steps: offset = step * len(byte_values) for a in range(base_addr, 65537 - offset): if snapshot[a:a + offset:step] == byte_values: print("{0}-{1}-{2} {0:04X}-{1:04X}-{2:X}: {3}".format(a, a + offset - step, step, byte_seq))
def copy_resource(fname, root_dir, dest_dir): base_f = basename(fname) dest_f = normpath(root_dir, dest_dir, base_f) if not isfile(dest_f) or os.stat(fname).st_mtime > os.stat(dest_f).st_mtime: fname_n = normpath(fname) dest_d = dirname(dest_f) if isfile(dest_d): raise SkoolKitError("Cannot copy {0} to {1}: {1} is not a directory".format(fname_n, normpath(dest_dir))) if not isdir(dest_d): os.makedirs(dest_d) notify('Copying {} to {}'.format(fname_n, normpath(dest_dir, base_f))) shutil.copy2(fname, dest_f)
def _find_tile(snapshot, coords): steps = '1' if '-' in coords: coords, steps = coords.split('-', 1) try: x, y = [get_int_param(i) for i in coords.split(',', 1)] if not 0 <= x < 32 or not 0 <= y < 24: raise ValueError except ValueError: raise SkoolKitError('Invalid tile coordinates: {}'.format(coords)) df_addr = 16384 + 2048 * (y // 8) + 32 * (y & 7) + x byte_seq = snapshot[df_addr:df_addr + 2048:256] for b in byte_seq: print('|{:08b}|'.format(b).replace('0', ' ').replace('1', '*')) _find(snapshot, '{}-{}'.format(','.join([str(b) for b in byte_seq]), steps), 23296)
def _get_ram(blocks, options): snapshot = [0] * 65536 operations = [] standard_load = True for spec in options.ram_ops: op_type, sep, param_str = spec.partition('=') if op_type in ('load', 'move', 'poke', 'sysvars'): operations.append((op_type, param_str)) if op_type == 'load': standard_load = False else: raise SkoolKitError("Invalid operation: {}".format(spec)) if standard_load: start = None for block_num, block in enumerate(blocks, 1): if block: if block[0] == 0 and len(block) >= 19: # Header block_type = block[1] if block_type == 3: # Bytes start = block[14] + 256 * block[15] elif block_type == 0: # Program start = 23755 else: raise TapeError( 'Unknown block type ({}) in header block {}'. format(block_type, block_num)) elif block[0] == 255 and start is not None: # Data _load_block(snapshot, block, start) start = None counters = {} for op_type, param_str in operations: if op_type == 'load': _load(snapshot, counters, blocks, param_str) elif op_type == 'move': move(snapshot, param_str) elif op_type == 'poke': poke(snapshot, param_str) elif op_type == 'sysvars': snapshot[23552:23755] = SYSVARS return snapshot[16384:]
def parse_block(self, text, start): indexes = [] index = text.index(' ', start) indexes.append(index) index += 1 # Parse the table rows or list items while True: start = text.find('{ ', index) if start < 0: break try: end = text.index(' }', start) except ValueError: raise SkoolKitError("No closing ' }}' on row/item: {}...".format(text[start:start + 15])) index = end + 2 indexes.append(index) indexes.append(len(text)) return indexes
def _analyse_sna(snafile): sna = read_bin_file(snafile, 147488) if len(sna) not in (49179, 131103, 147487): raise SkoolKitError('{}: not a SNA file'.format(snafile)) is128 = len(sna) > 49179 print('RAM: {}K'.format(128 if is128 else 48)) print('Interrupts: {}abled'.format('en' if sna[19] & 4 else 'dis')) print('Interrupt mode: {}'.format(sna[25])) print('Border: {}'.format(sna[26] & 7)) # Print register contents reg = Registers() reg.i = sna[0] reg.hl2 = get_word(sna, 1) reg.de2 = get_word(sna, 3) reg.bc2 = get_word(sna, 5) reg.f2 = sna[7] reg.a2 = sna[8] reg.hl = get_word(sna, 9) reg.de = get_word(sna, 11) reg.bc = get_word(sna, 13) reg.iy = get_word(sna, 15) reg.ix = get_word(sna, 17) reg.r = sna[20] reg.f = sna[21] reg.a = sna[22] reg.sp = get_word(sna, 23) if is128: reg.pc = get_word(sna, 49179) else: reg.pc = get_word(sna, reg.sp - 16357) print('Registers:') for line in reg.get_lines(): print(' ' + line) if is128: _print_ram_banks(sna)
def _analyse_szx(szxfile): szx = read_bin_file(szxfile) magic = _get_block_id(szx, 0) if magic != 'ZXST': raise SkoolKitError("{} is not an SZX file".format(szxfile)) print('Version: {}.{}'.format(szx[4], szx[5])) machine_id = szx[6] print('Machine: {}'.format(SZX_MACHINES.get(machine_id, 'Unknown'))) variables = {'chMachineId': machine_id} i = 8 while i < len(szx): block_id = _get_block_id(szx, i) block_size = get_dword(szx, i + 4) i += 8 print('{}: {} bytes'.format(block_id, block_size)) printer = SZX_BLOCK_PRINTERS.get(block_id) if printer: for line in printer(szx[i:i + block_size], variables): print(" " + line) i += block_size
def write_disassembly(html_writer, files, search_dir, extra_search_dirs, pages, css_themes, single_css): game_dir = html_writer.file_info.game_dir paths = html_writer.paths game_vars = html_writer.game_vars # Create the disassembly subdirectory if necessary odir = html_writer.file_info.odir if not isdir(odir): notify('Creating directory {0}'.format(odir)) os.makedirs(odir) # Copy CSS, JavaScript and font files if necessary html_writer.set_style_sheet( copy_resources(search_dir, extra_search_dirs, odir, game_vars.get('StyleSheet'), paths.get('StyleSheetPath', ''), css_themes, '.css', single_css)) js_path = paths.get('JavaScriptPath', '') copy_resources(search_dir, extra_search_dirs, odir, game_vars.get('JavaScript'), js_path) copy_resources(search_dir, extra_search_dirs, odir, game_vars.get('Font'), paths.get('FontPath', '')) # Copy resources named in the [Resources] section resources = html_writer.ref_parser.get_dictionary('Resources') for f, dest_dir in resources.items(): fname = find(f, extra_search_dirs, search_dir) if not fname: raise SkoolKitError( 'Cannot copy resource "{}": file not found'.format( normpath(f))) copy_resource(fname, odir, dest_dir) # Write disassembly files if 'd' in files: if html_writer.asm_single_page_template: message = 'Writing {}'.format( normpath(game_dir, paths['AsmSinglePage'])) else: message = 'Writing disassembly files in {}'.format( normpath(game_dir, html_writer.code_path)) clock(html_writer.write_asm_entries, ' ' + message) # Write the memory map files if 'm' in files: for map_name in html_writer.main_memory_maps: clock(html_writer.write_map, ' Writing {}'.format(normpath(game_dir, paths[map_name])), map_name) # Write pages defined by [Page:*] sections if 'P' in files: for page_id in pages: page_details = html_writer.pages[page_id] copy_resources(search_dir, extra_search_dirs, odir, page_details.get('JavaScript'), js_path, indent=2) clock(html_writer.write_page, ' Writing {}'.format(normpath(game_dir, paths[page_id])), page_id) # Write other code files if 'o' in files: for code_id, code in html_writer.other_code: skoolfile = find(code['Source'], extra_search_dirs, search_dir) if not skoolfile: raise SkoolKitError('{}: file not found'.format( normpath(code['Source']))) skool2_parser = clock(html_writer.parser.clone, ' Parsing {0}'.format(skoolfile), skoolfile) html_writer2 = html_writer.clone(skool2_parser, code_id) map_name = code['IndexPageId'] map_path = paths[map_name] asm_path = paths[code['CodePathId']] clock(html_writer2.write_map, ' Writing {}'.format(normpath(game_dir, map_path)), map_name) if html_writer.asm_single_page_template: message = 'Writing {}'.format( normpath(game_dir, paths[code['AsmSinglePageId']])) else: message = 'Writing disassembly files in {}'.format( normpath(game_dir, asm_path)) clock(html_writer2.write_entries, ' ' + message, asm_path, map_path) # Write index.html if 'i' in files: clock(html_writer.write_index, ' Writing {}'.format(normpath(game_dir, paths['GameIndex'])))
def process_file(infile, topdir, options): extra_search_dirs = options.search pages = options.pages stdin = False skoolfile_f = reffile_f = None ref_search_dir = module_path = '' if infile.endswith('.ref'): reffile_f = find(infile, extra_search_dirs) if reffile_f: ref_search_dir = module_path = dirname(reffile_f) prefix = get_prefix(basename(reffile_f)) elif infile == '-': stdin = True skoolfile_f = infile prefix = 'program' else: skoolfile_f = find(infile, extra_search_dirs) if skoolfile_f: ref_search_dir = module_path = dirname(skoolfile_f) prefix = get_prefix(basename(skoolfile_f)) reffile_f = find('{}.ref'.format(prefix), extra_search_dirs, ref_search_dir) if reffile_f: ref_search_dir = dirname(reffile_f) if skoolfile_f is reffile_f is None: raise SkoolKitError('{}: file not found'.format(normpath(infile))) reffiles = [] if reffile_f: reffiles.append(normpath(reffile_f)) base_ref = prefix + '.ref' for f in sorted(os.listdir(ref_search_dir or '.')): if isfile(os.path.join(ref_search_dir, f)) and f.endswith( '.ref') and f.startswith(prefix) and f != base_ref: reffiles.append(normpath(ref_search_dir, f)) ref_parser = RefParser() ref_parser.parse(StringIO(defaults.get_section('Config'))) config = ref_parser.get_dictionary('Config') for oreffile_f in reffiles: ref_parser.parse(oreffile_f) add_lines(ref_parser, options.config_specs, 'Config') config.update(ref_parser.get_dictionary('Config')) extra_reffiles = config.get('RefFiles') if extra_reffiles: for f in extra_reffiles.split(';'): if isfile(os.path.join(ref_search_dir, f)): ref_f = normpath(ref_search_dir, f) if ref_f not in reffiles: reffiles.append(ref_f) ref_parser.parse(ref_f) add_lines(ref_parser, options.config_specs) if skoolfile_f is None: skoolfile = config.get('SkoolFile', '{}.skool'.format(prefix)) skoolfile_f = find(skoolfile, extra_search_dirs, ref_search_dir) if skoolfile_f is None: raise SkoolKitError('{}: file not found'.format( normpath(skoolfile))) skoolfile_n = normpath(skoolfile_f) if not stdin: notify('Using skool file: {}'.format(skoolfile_n)) if reffiles: if len(reffiles) > 1: suffix = 's' else: suffix = '' notify('Using ref file{0}: {1}'.format(suffix, ', '.join(reffiles))) elif not stdin: notify('Found no ref file for {}'.format(skoolfile_n)) html_writer_class = get_class(config['HtmlWriterClass'], module_path) game_dir = config.get('GameDir', prefix) # Parse the skool file and initialise the writer if stdin: fname = 'skool file from standard input' else: fname = skoolfile_f skool_parser = clock(SkoolParser, 'Parsing {}'.format(fname), skoolfile_f, case=options.case, base=options.base, html=True, create_labels=options.create_labels, asm_labels=options.asm_labels) file_info = FileInfo(topdir, game_dir, options.new_images) html_writer = html_writer_class(skool_parser, ref_parser, file_info) # Check that the specified pages exist all_page_ids = html_writer.get_page_ids() for page_id in pages: if page_id not in all_page_ids: raise SkoolKitError('Invalid page ID: {0}'.format(page_id)) pages = pages or all_page_ids write_disassembly(html_writer, options.files, ref_search_dir, extra_search_dirs, pages, options.themes, options.single_css)