def test_unknown_file_type(self): file_type = 'tzx' snapshot_file = self.write_bin_file(suffix='.{0}'.format(file_type)) with self.assertRaisesRegex( SnapshotError, "{}: Unknown file type '{}'".format(snapshot_file, file_type)): get_snapshot(snapshot_file)
def main(args): parser = argparse.ArgumentParser( usage='bin2tap.py [options] FILE [file.tap]', description="Convert a binary (raw memory) file or a SNA, SZX or Z80 snapshot into a TAP file. " "FILE may be a regular file, or '-' to read a binary file from standard input.", add_help=False ) parser.add_argument('infile', help=argparse.SUPPRESS, nargs='?') parser.add_argument('outfile', help=argparse.SUPPRESS, nargs='?') group = parser.add_argument_group('Options') group.add_argument('-c', '--clear', dest='clear', metavar='N', type=int, help="Use a 'CLEAR N' command in the BASIC loader and leave the stack pointer alone") group.add_argument('-e', '--end', dest='end', metavar='ADDR', type=int, default=65536, help="Set the end address when reading a snapshot") group.add_argument('-o', '--org', dest='org', metavar='ORG', type=int, help="Set the origin address (default: 16384 for a snapshot, otherwise 65536 minus the length of FILE)") group.add_argument('-p', '--stack', dest='stack', metavar='STACK', type=int, help="Set the stack pointer (default: ORG)") group.add_argument('-s', '--start', dest='start', metavar='START', type=int, help="Set the start address to JP to (default: ORG)") group.add_argument('-S', '--screen', dest='screen', metavar='FILE', help="Add a loading screen to the TAP file; FILE may be a snapshot or a 6912-byte SCR file") group.add_argument('-t', '--tapfile', dest='tapfile', metavar='TAPFILE', help="Set the TAP filename") 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) infile = namespace.infile if unknown_args or infile is None: parser.exit(2, parser.format_help()) if infile.lower().endswith(('.sna', '.szx', '.z80')): org = namespace.org or 16384 if org >= namespace.end: raise SkoolKitError('End address must be greater than {}'.format(org)) ram = get_snapshot(infile)[org:namespace.end] else: ram = read_bin_file(infile, 49152) if len(ram) == 0: raise SkoolKitError('{} is empty'.format(infile)) org = namespace.org or 65536 - len(ram) clear = namespace.clear start = namespace.start or org stack = namespace.stack or org tapfile = namespace.outfile or namespace.tapfile if tapfile is None: if infile.lower().endswith(('.bin', '.sna', '.szx', '.z80')): prefix = infile[:-4] elif infile == '-': prefix = 'program' else: prefix = infile tapfile = prefix + ".tap" scr = namespace.screen if scr is not None: if scr.lower().endswith(('.sna', '.szx', '.z80')): scr = get_snapshot(scr)[16384:23296] else: scr = read_bin_file(scr, 6912) run(ram, clear, org, start, stack, tapfile, scr)
def test_szx_128k_no_specregs(self): szx = self._get_szx_header(2, specregs=False) tmp_szx = self.write_bin_file(szx, suffix='.szx') try: get_snapshot(tmp_szx) except SnapshotError as e: self.assertEqual(e.args[0], "SPECREGS (SPCR) block not found")
def test_szx_48k_missing_page(self): szx = self._get_szx_header() szx.extend(self._get_zxstrampage(5, True, [0] * 16384)) tmp_szx = self.write_bin_file(szx, suffix='.szx') try: get_snapshot(tmp_szx) except SnapshotError as e: self.assertEqual(e.args[0], "Page 2 not found")
def test_bad_z80(self): header = [0] * 30 header[6] = 255 # Set PC > 0 to indicate a v1 Z80 snapshot header[12] |= 32 # Signal that the RAM data block is compressed z80 = header + [255] # Good byte to start with z80 += [237, 237, 0, 11] # Bad block of length 0 z80 += [0, 237, 237, 0] # Terminator z80_file = self.write_bin_file(z80, suffix='.z80') with self.assertRaisesRegexp(SnapshotError, 'Found ED ED 00 0B'): get_snapshot(z80_file)
def test_szx_48k_bad_page_size(self): szx = self._get_szx_header() ram = [0, 0, 0] # Bad page size (3 != 16384) page = 5 szx.extend(self._get_zxstrampage(page, False, ram)) tmp_szx = self.write_bin_file(szx, suffix='.szx') try: get_snapshot(tmp_szx) except SnapshotError as e: self.assertEqual(e.args[0], "Page {0} is {1} bytes (should be 16384)".format(5, len(ram)))
def test_bad_z80(self): header = [0] * 30 header[6] = 255 # Set PC > 0 to indicate a v1 Z80 snapshot header[12] |= 32 # Signal that the RAM data block is compressed z80 = header + [255] # Good byte to start with z80 += [237, 237, 0, 11] # Bad block of length 0 z80 += [0, 237, 237, 0] # Terminator z80_file = self.write_bin_file(z80, suffix='.z80') with self.assertRaisesRegex(SnapshotError, 'Found ED ED 00 0B'): get_snapshot(z80_file)
def test_szx_48k_bad_zlib_block(self): szx = self._get_szx_header() szx.extend((82, 65, 77, 80)) # RAMP ram = (1, 2, 3, 4, 5, 6, 7, 8) # Invalid zlib block size = len(ram) + 3 szx.extend((size % 256, size // 256, 0, 0)) szx.extend((1, 0)) # Compressed page = 5 szx.append(page) tmp_szx = self.write_bin_file(szx, suffix='.szx') try: get_snapshot(tmp_szx) except SnapshotError as e: self.assertTrue(e.args[0].startswith("Error while decompressing page {0}:".format(page)))
def _check_z80(self, z80file, data, org=None, sp=None, pc=None, border=7, iff=1, im=1): with open(z80file, 'rb') as f: z80h = f.read(34) if org is None: org = 65536 - len(data) if sp is None: sp = org if pc is None: pc = org self.assertEqual((z80h[12] >> 1) & 7, border) # BORDER self.assertEqual(z80h[27], iff) # IFF1 self.assertEqual(z80h[28], iff) # IFF2 self.assertEqual(z80h[29] & 3, im) # Interrupt mode self.assertEqual(z80h[8] + 256 * z80h[9], sp) # SP self.assertEqual(z80h[10], 63) # I self.assertEqual(z80h[23] + 256 * z80h[24], 23610) # IY self.assertEqual(z80h[32] + 256 * z80h[33], pc) # PC snapshot = get_snapshot(z80file) self.assertEqual(data, snapshot[org:org + len(data)])
def run(snafile, imgfname, options): snapshot = get_snapshot(snafile) skool = BackToSkool(snapshot) width = 192 eric = 210 for address in options.mutable: skool.alter_skool_udgs(address) x = y = 0 height = 21 skool.hide_chars() if options.blackboard: bb = BLACKBOARDS[options.blackboard] width, height, x, y = bb['geometry'] eric_x, eric_y = bb['location'] skool.place_char(eric, eric_x, eric_y, 0) elif options.geometry: wh, xy = options.geometry.split('+', 1) width, height = [int(n) for n in wh.split('x')] x, y = [int(n) for n in xy.split('+')] for spec in options.text: board_id, sep, text = spec.partition(':') if sep: bb = BLACKBOARDS[board_id] skool.write(bb['tiles'], text) for spec in options.place_char: values = [] for n in spec.split(','): try: values.append(int(n)) except ValueError: values.append(None) skool.place_char(*values[:4]) for spec in options.pokes: addr, val = spec.split(',', 1) step = 1 if '-' in addr: addr1, addr2 = addr.split('-', 1) addr1 = int(addr1) if '-' in addr2: addr2, step = [int(i) for i in addr2.split('-', 1)] else: addr2 = int(addr2) else: addr1 = int(addr) addr2 = addr1 addr2 += 1 value = int(val) for a in range(addr1, addr2, step): snapshot[a] = value udg_array = skool.get_skool_udgs(x, y, width, height) frame = Frame(udg_array, options.scale) image_writer = ImageWriter() image_format = 'gif' if imgfname.lower()[-4:] == '.gif' else 'png' with open(imgfname, "wb") as f: image_writer.write_image([frame], f, image_format)
def _test_z80(self, options, header, exp_header=None, ram=None, exp_ram=None, version=3, compress=False): if exp_header is None: exp_header = header if ram is None: ram = [0] * 49152 if exp_ram is None: exp_ram = ram infile = self.write_z80_file(header, ram, version, compress) outfile = '{}-out.z80'.format(infile[:-4]) self.tempfiles.append(outfile) output, error = self.run_snapmod('{} {} {}'.format( options, infile, outfile)) self.assertEqual([], output) self.assertEqual(error, '') z80_header = list(read_bin_file(outfile, len(exp_header))) self.assertEqual(exp_header, z80_header) z80_ram = get_snapshot(outfile)[16384:] self.assertEqual(exp_ram, z80_ram)
def run(infile, outfile, options): x, y = [int(c) for c in options.xy.split(',', 1)] w, h = [int(c) for c in options.wh.split(',', 1)] w = min(32 - x, w) h = min(24 - y, h) if infile[-4:].lower() == '.scr': with open(infile, 'rb') as f: scr = bytearray(f.read(6912)) snapshot = [0] * 65536 snapshot[16384:16384 + len(scr)] = scr else: snapshot = get_snapshot(infile) for spec in options.pokes: poke(snapshot, spec) scr = snapshot[16384:23296] if options.invert: for i in range(6144, 6912): if scr[i] & 128: df = 2048 * (i // 256 - 24) + i % 256 for j in range(df, df + 2048, 256): scr[j] ^= 255 scr[i] -= 128 scrshot = _get_screenshot(scr, x, y, w, h, options.flip) _write_image(scrshot, outfile, options.scale, options.animated)
def _find_text(infile, text): size = len(text) byte_values = [ord(c) for c in text] snapshot = get_snapshot(infile) for a in range(16384, 65536 - size + 1): if snapshot[a:a + size] == byte_values: print("{0}-{1} {0:04X}-{1:04X}: {2}".format(a, a + size - 1, text))
def run(infile, outfile, options): x, y = options.origin w = min(32 - x, options.size[0]) h = min(24 - y, options.size[1]) if infile[-4:].lower() == '.scr': scr = read_bin_file(infile, 6912) snapshot = [0] * 65536 snapshot[16384:16384 + len(scr)] = scr else: snapshot = get_snapshot(infile) for spec in options.pokes: poke(snapshot, spec) scrshot = _get_screenshot(snapshot[16384:23296], x, y, w, h) if options.invert: for row in scrshot: for udg in row: if udg.attr & 128: udg.data = [b^255 for b in udg.data] udg.attr &= 127 flip_udgs(scrshot, options.flip) rotate_udgs(scrshot, options.rotate) _write_image(scrshot, outfile, options.scale, options.animated)
def _read_z80(z80file): data = read_bin_file(z80file) if get_word(data, 6) > 0: header = data[:30] else: header_len = 32 + get_word(data, 30) header = data[:header_len] return list(header), get_snapshot(z80file)
def run(snafile, imgfname, options): snapshot = get_snapshot(snafile) game = ContactSamCruise(snapshot) x = y = 0 width, height = 256, 42 game.hide_chars() game.reset_fuses() game.switch_lights_on() game.raise_blinds() game.close_doors() game.adjust_rope(options.rope) if options.geometry: wh, xy = options.geometry.split('+', 1) width, height = [int(n) for n in wh.split('x')] x, y = [int(n) for n in xy.split('+')] for spec in options.place_char: values = [] index = 0 for n in spec.split(','): if not values and '.' in n: n, i = n.split('.', 1) try: index = int(i) except: pass try: values.append(int(n)) except ValueError: values.append(None) game.place_char(*values[:5], index=index) for spec in options.pokes: addr, val = spec.split(',', 1) step = 1 if '-' in addr: addr1, addr2 = addr.split('-', 1) addr1 = int(addr1) if '-' in addr2: addr2, step = [int(i) for i in addr2.split('-', 1)] else: addr2 = int(addr2) else: addr1 = int(addr) addr2 = addr1 addr2 += 1 value = int(val) for a in range(addr1, addr2, step): snapshot[a] = value udg_array = game.get_play_area_udgs(x, y, width, height) frame = Frame(udg_array, options.scale) image_writer = ImageWriter() image_format = 'gif' if imgfname.lower()[-4:] == '.gif' else 'png' with open(imgfname, "wb") as f: image_writer.write_image([frame], f, image_format)
def test_sna_48k(self): header = [0] * 27 exp_ram = [(n + 23) & 255 for n in range(49152)] sna = header + exp_ram tmp_sna = self.write_bin_file(sna, suffix='.sna') snapshot = get_snapshot(tmp_sna) ram = snapshot[16384:] self.assertEqual(len(ram), 49152) self.assertEqual(ram, exp_ram)
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 run(subcommand): func = functions[subcommand][0] if not os.path.isdir(BUILD_DIR): os.mkdir(BUILD_DIR) if not os.path.isfile(MM_Z80): tap2sna.main(('-d', BUILD_DIR, '@{}/manic_miner.t2s'.format(MANICMINER_HOME))) ctlfile = '{}/{}.ctl'.format(BUILD_DIR, subcommand) with open(ctlfile, 'wt') as f: f.write(func(get_snapshot(MM_Z80))) sna2skool.main(('-c', ctlfile, MM_Z80))
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 run(subcommand): func = functions[subcommand][0] if not os.path.isdir(BUILD_DIR): os.mkdir(BUILD_DIR) if not os.path.isfile(MM_Z80): tap2sna.main( ('-d', BUILD_DIR, '@{}/manic_miner.t2s'.format(MANICMINER_HOME))) ctlfile = '{}/{}.ctl'.format(BUILD_DIR, subcommand) with open(ctlfile, 'wt') as f: f.write(func(get_snapshot(MM_Z80))) sna2skool.main(('-c', ctlfile, MM_Z80))
def _test_szx(self, exp_ram, compress, machine_id=1, ch7ffd=0, pages={}, page=None): tmp_szx = self.write_szx(exp_ram, compress, machine_id, ch7ffd, pages) snapshot = get_snapshot(tmp_szx, page) self._check_ram(snapshot[16384:], exp_ram, machine_id, ch7ffd, pages, page)
def find(infile, byte_seq): step = 1 if '-' in byte_seq: byte_seq, step = byte_seq.split('-', 1) step = get_int_param(step) byte_values = [get_int_param(i) for i in byte_seq.split(',')] offset = step * len(byte_values) snapshot = get_snapshot(infile) for a in range(16384, 65537 - offset): if snapshot[a:a + offset:step] == byte_values: print("{}-{}-{}: {}".format(a, a + offset - step, step, byte_seq))
def test_tap_file_in_zip_archive(self): data = [1] block = create_tap_data_block(data) tap_name = 'game.tap' zip_fname = self._write_tap([block], zip_archive=True, tap_name=tap_name) z80file = self.write_bin_file(suffix='.z80') start = 16385 output, error = self.run_tap2sna('--force --ram load=1,{} {} {}'.format(start, zip_fname, z80file)) self.assertEqual(output, 'Extracting {}\nWriting {}\n'.format(tap_name, z80file)) self.assertEqual(error, '') snapshot = get_snapshot(z80file) self.assertEqual(data, snapshot[start:start + len(data)])
def test_standard_load_ignores_headerless_block(self): code_start = 16384 code_header = self._create_tap_bytes_header_block(code_start) code = [2] blocks = [code_header, self._create_tap_data_block(code)] blocks.append(self._create_tap_data_block([23])) tapfile = self._write_tap(blocks) z80file = self.write_bin_file(suffix='.z80') output, error = self.run_tap2sna('--force {} {}'.format(tapfile, z80file)) self.assertEqual(error, '') snapshot = get_snapshot(z80file) self.assertEqual(snapshot[code_start:code_start + len(code)], code)
def peek(infile, addr_range): step = 1 if '-' in addr_range: addr1, addr2 = addr_range.split('-', 1) addr1 = get_int_param(addr1) if '-' in addr2: addr2, step = [get_int_param(i) for i in addr2.split('-', 1)] else: addr2 = get_int_param(addr2) else: addr1 = addr2 = get_int_param(addr_range) snapshot = get_snapshot(infile) for a in range(addr1, addr2 + 1, step): print('{}: {}'.format(a, snapshot[a]))
def run(imgfname, options): snapshot = get_snapshot('{}/build/jet_set_willy.z80'.format(JETSETWILLY_HOME)) _do_pokes(options.pokes, snapshot) jsw = JetSetWilly(snapshot) udg_array = _place_willy(jsw, options.room, options.willy) if options.geometry: wh, xy = options.geometry.split('+', 1) width, height = [int(n) for n in wh.split('x')] x, y = [int(n) for n in xy.split('+')] udg_array = [row[x:x + width] for row in udg_array[y:y + height]] frame = Frame(udg_array, options.scale) image_writer = ImageWriter() with open(imgfname, "wb") as f: image_writer.write_image([frame], f)
def _test_z80(self, exp_ram, version, compress, machine_id=0, modify=False, out_7ffd=0, pages={}, page=None): model, tmp_z80 = self.write_z80(exp_ram, version, compress, machine_id, modify, out_7ffd, pages) snapshot = get_snapshot(tmp_z80, page) self._check_ram(snapshot[16384:], exp_ram, model, out_7ffd, pages, page)
def _get_snapshot(self, start=16384, data=None, options='', load_options=None, blocks=None, tzx=False): if blocks is None: blocks = [create_tap_data_block(data)] if tzx: tape_file = self._write_tzx(blocks) else: tape_file = self._write_tap(blocks) z80file = self.write_bin_file(suffix='.z80') if load_options is None: load_options = '--ram load=1,{}'.format(start) output, error = self.run_tap2sna('--force {} {} {} {}'.format(load_options, options, tape_file, z80file)) self.assertEqual(output, 'Writing {}\n'.format(z80file)) self.assertEqual(error, '') return get_snapshot(z80file)
def run(imgfname, options): snapshot = get_snapshot('{}/build/manic_miner.z80'.format(MANICMINER_HOME)) _do_pokes(options.pokes, snapshot) mm = ManicMiner(snapshot) udg_array = _place_willy(mm, options.cavern, options.willy) if options.geometry: wh, xy = options.geometry.split('+', 1) width, height = [int(n) for n in wh.split('x')] x, y = [int(n) for n in xy.split('+')] udg_array = [row[x:x + width] for row in udg_array[y:y + height]] frame = Frame(udg_array, options.scale) image_writer = ImageWriter() with open(imgfname, "wb") as f: image_writer.write_image([frame], f)
def _find(infile, byte_seq): step = 1 if '-' in byte_seq: byte_seq, step = byte_seq.split('-', 1) step = get_int_param(step) try: byte_values = [get_int_param(i) for i in byte_seq.split(',')] except ValueError: raise SkoolKitError('Invalid byte sequence: {}'.format(byte_seq)) offset = step * len(byte_values) snapshot = get_snapshot(infile) for a in range(16384, 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 test_standard_load_ignores_truncated_header_block(self): code_start = 30000 code = [2, 3, 4] length = len(code) blocks = [ create_tap_header_block(start=code_start)[:-1], create_tap_data_block(code), ] tapfile = self._write_tap(blocks) z80file = self.write_bin_file(suffix='.z80') output, error = self.run_tap2sna('--force {} {}'.format(tapfile, z80file)) self.assertEqual(error, '') snapshot = get_snapshot(z80file) self.assertEqual([0] * length, snapshot[code_start:code_start + length])
def _test_z80(self, options, header, exp_header, ram=None, exp_ram=None, version=3, compress=False): if ram is None: ram = [0] * 49152 if exp_ram is None: exp_ram = ram infile = self.write_z80_file(header, ram, version, compress) outfile = '{}-out.z80'.format(infile[:-4]) self.tempfiles.append(outfile) output, error = self.run_snapmod('{} {} {}'.format(options, infile, outfile)) self.assertEqual(output, '') self.assertEqual(error, '') z80_header = list(read_bin_file(outfile, len(exp_header))) self.assertEqual(exp_header, z80_header) z80_ram = get_snapshot(outfile)[16384:] self.assertEqual(exp_ram, z80_ram)
def test_standard_load_ignores_headerless_block(self): code_start = 16384 code = [2] blocks = [ create_tap_header_block(start=code_start), create_tap_data_block(code), create_tap_data_block([23]) ] tapfile = self._write_tap(blocks) z80file = self.write_bin_file(suffix='.z80') output, error = self.run_tap2sna('--force {} {}'.format(tapfile, z80file)) self.assertEqual(error, '') snapshot = get_snapshot(z80file) self.assertEqual(code, snapshot[code_start:code_start + len(code)])
def run(imgfname, options): snapshot = get_snapshot('{}/build/manic_miner.z80'.format(MANICMINER_HOME)) _do_pokes(options.pokes, snapshot) mm = ManicMiner(snapshot) udg_array = _place_willy(mm, options.cavern, options.willy) if options.geometry: wh, xy = options.geometry.split('+', 1) width, height = [int(n) for n in wh.split('x')] x, y = [int(n) for n in xy.split('+')] udg_array = [row[x:x + width] for row in udg_array[y:y + height]] frame = Frame(udg_array, options.scale) image_format = 'gif' if imgfname.lower()[-4:] == '.gif' else 'png' image_writer = ImageWriter() with open(imgfname, "wb") as f: image_writer.write_image([frame], f, image_format)
def run(subcommand): func = functions[subcommand][0] if not os.path.isdir(BUILD_DIR): os.mkdir(BUILD_DIR) if not os.path.isfile(MM_Z80): tap2sna.main(("-d", BUILD_DIR, "@{}/manic_miner.t2s".format(MANICMINER_HOME))) ctlfile = "{}/{}.ctl".format(BUILD_DIR, subcommand) with open(ctlfile, "wt") as f: f.write(func(get_snapshot(MM_Z80))) stdout = sys.stdout sys.stdout = StringIO() sna2skool.main(("-c", ctlfile, MM_Z80)) skool = sys.stdout.getvalue() sys.stdout = stdout for line in skool.split("\n")[2:-1]: print(line)
def run(subcommand): func = functions[subcommand][0] if not os.path.isdir(BUILD_DIR): os.mkdir(BUILD_DIR) if not os.path.isfile(MM_Z80): tap2sna.main( ('-d', BUILD_DIR, '@{}/manic_miner.t2s'.format(MANICMINER_HOME))) ctlfile = '{}/{}.ctl'.format(BUILD_DIR, subcommand) with open(ctlfile, 'wt') as f: f.write(func(get_snapshot(MM_Z80))) stdout = sys.stdout sys.stdout = StringIO() sna2skool.main(('-c', ctlfile, MM_Z80)) skool = sys.stdout.getvalue() sys.stdout = stdout for line in skool.split('\n')[2:-1]: print(line)
def _peek(infile, specs): addr_ranges = [] for addr_range in specs: try: values = [get_int_param(i) for i in addr_range.split('-', 2)] except ValueError: raise SkoolKitError('Invalid address range: {}'.format(addr_range)) addr_ranges.append(values + [values[0], 1][len(values) - 1:]) snapshot = get_snapshot(infile) for addr1, addr2, step in addr_ranges: for a in range(addr1, addr2 + 1, step): value = snapshot[a] if 32 <= value <= 126: char = chr(value) else: char = '' print('{0:>5} {0:04X}: {1:>3} {1:02X} {1:08b} {2}'.format(a, value, char))
def test_standard_load_from_tap_file(self): basic_data = [1, 2, 3] code_start = 32768 code = [4, 5] blocks = [ create_tap_header_block(data_type=0), create_tap_data_block(basic_data), create_tap_header_block(start=code_start), create_tap_data_block(code) ] tapfile = self._write_tap(blocks) z80file = self.write_bin_file(suffix='.z80') output, error = self.run_tap2sna('--force {} {}'.format(tapfile, z80file)) self.assertEqual(error, '') snapshot = get_snapshot(z80file) self.assertEqual(basic_data, snapshot[23755:23755 + len(basic_data)]) self.assertEqual(code, snapshot[code_start:code_start + len(code)])
def test_standard_load_from_tzx_file(self): basic_data = [6, 7] code_start = 49152 code = [8, 9, 10] blocks = [ [48, 3, 65, 66, 67], # Text description block (0x30): ABC create_tzx_header_block(data_type=0), create_tzx_data_block(basic_data), create_tzx_header_block(start=code_start), create_tzx_data_block(code) ] tzxfile = self._write_tzx(blocks) z80file = self.write_bin_file(suffix='.z80') output, error = self.run_tap2sna('--force {} {}'.format(tzxfile, z80file)) self.assertEqual(error, '') snapshot = get_snapshot(z80file) self.assertEqual(basic_data, snapshot[23755:23755 + len(basic_data)]) self.assertEqual(code, snapshot[code_start:code_start + len(code)])
def test_sna_128k_page_5(self): header = [0] * 27 page5 = [(n + 37) & 255 for n in range(16384)] page2 = [(n + 19) & 255 for n in range(16384)] page3 = [(n + 203) & 255 for n in range(16384)] page0 = [(n + 197) & 255 for n in range(16384)] page1 = [(n + 117) & 255 for n in range(16384)] page4 = [(n + 5) & 255 for n in range(16384)] page6 = [(n + 23) & 255 for n in range(16384)] page7 = [(n + 41) & 255 for n in range(16384)] config = [0, 0] # PC config.append(3) # Port 7ffd (page 3 mapped to 49152-65535) config.append(0) # TR-DOS ROM not paged sna = header + page5 + page2 + page3 + config + page0 + page1 + page4 + page6 + page7 tmp_sna = self.write_bin_file(sna, suffix='.sna') snapshot = get_snapshot(tmp_sna, 5) ram = snapshot[16384:] self.assertEqual(len(ram), 49152) self.assertEqual(ram, page5 + page2 + page5)
def run(snafile, options): # Read the snapshot file if snafile[-4:].lower() in ('.sna', '.szx', '.z80'): snapshot = get_snapshot(snafile, options.page) start = max(START, options.start) else: ram = read_bin_file(snafile) if options.org is None: org = 65536 - len(ram) else: org = options.org snapshot = [0] * org snapshot.extend(ram) start = max(org, options.start) end = min(options.end, len(snapshot)) # Pad out the end of the snapshot to avoid disassembly errors when an # instruction crosses the 64K boundary snapshot += [0] * (65539 - len(snapshot)) if options.sftfile: # Use a skool file template info('Using skool file template: {}'.format(options.sftfile)) writer = SftParser(snapshot, options.sftfile, options.zfill, options.asm_hex, options.asm_lower) writer.write_skool(options.start, options.end) return if options.genctlfile: # Generate a control file ctls = generate_ctls(snapshot, start, end, options.code_map) write_ctl(options.genctlfile, ctls, options.ctl_hex) ctl_parser = CtlParser(ctls) elif options.ctlfile: # Use a control file info('Using control file: {}'.format(options.ctlfile)) ctl_parser = CtlParser() ctl_parser.parse_ctl(options.ctlfile, options.start, options.end) else: ctl_parser = CtlParser({start: 'c', end: 'i'}) writer = SkoolWriter(snapshot, ctl_parser, options) writer.write_skool(options.write_refs, options.text)
def _test_szx(self, exp_ram, compress, machine_id=1, ch7ffd=0, pages={}, page=None): szx = self._get_szx_header(machine_id, ch7ffd) rampages = {5: self._get_zxstrampage(5, compress, exp_ram[:16384])} if machine_id >= 1: # 48K and 128K rampages[2] = self._get_zxstrampage(2, compress, exp_ram[16384:32768]) if machine_id == 1: # 48K rampages[0] = self._get_zxstrampage(0, compress, exp_ram[32768:]) else: # 128K rampages[ch7ffd & 7] = self._get_zxstrampage(ch7ffd & 7, compress, exp_ram[32768:]) for bank, data in pages.items(): rampages[bank] = self._get_zxstrampage(bank, compress, data) for bank in set(range(8)) - set(rampages.keys()): rampages[bank] = self._get_zxstrampage(bank, compress, [0] * 16384) for rampage in rampages.values(): szx.extend(rampage) tmp_szx = self.write_bin_file(szx, suffix='.szx') snapshot = get_snapshot(tmp_szx, page) self._check_ram(snapshot[16384:], exp_ram, machine_id, ch7ffd, pages, page)
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...[-N]]', help='Search for the byte sequence A,B... with distance 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('-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 snapshot_type = infile[-4:].lower() if snapshot_type not in ('.sna', '.szx', '.z80'): raise SkoolKitError('Unrecognised snapshot type') if namespace.find is not None: _find(infile, namespace.find) elif namespace.text is not None: _find_text(infile, namespace.text) elif namespace.peek is not None: _peek(infile, namespace.peek) elif namespace.basic: print(BasicLister().list_basic(get_snapshot(infile))) elif snapshot_type == '.sna': _analyse_sna(infile) elif snapshot_type == '.z80': _analyse_z80(infile) else: _analyse_szx(infile)
def run(snafile, options, config): # Read the snapshot file if snafile[-4:].lower() in ('.sna', '.szx', '.z80'): snapshot = get_snapshot(snafile, options.page) start = max(START, options.start) else: ram = read_bin_file(snafile, 65536) if options.org is None: org = 65536 - len(ram) else: org = options.org snapshot = [0] * org snapshot.extend(ram) start = max(org, options.start) end = min(options.end, len(snapshot)) snapshot += [0] * (65536 - len(snapshot)) if options.sftfile: # Use a skool file template info('Using skool file template: {}'.format(options.sftfile)) writer = SftParser(snapshot, options.sftfile, options.zfill, options.base == 16, options.case == 1) writer.write_skool(options.start, options.end) return if options.genctlfile: # Generate a control file ctls = generate_ctls(snapshot, start, end, options.code_map) write_ctl(options.genctlfile, ctls, options.ctl_hex) ctl_parser = CtlParser(ctls) elif options.ctlfile: # Use a control file info('Using control file: {}'.format(options.ctlfile)) ctl_parser = CtlParser() ctl_parser.parse_ctl(options.ctlfile, options.start, options.end) else: ctl_parser = CtlParser({start: 'c', end: 'i'}) writer = SkoolWriter(snapshot, ctl_parser, options, config) writer.write_skool(options.write_refs, options.text)
def test_bad_ram_size(self): ram_size = 3 with self.assertRaisesRegex(SnapshotError, 'RAM size is {}'.format(ram_size)): get_snapshot( self.write_bin_file([0] * (27 + ram_size), suffix='.sna'))
def main(args): parser = argparse.ArgumentParser( usage='bin2tap.py [options] FILE [file.tap]', description= "Convert a binary (raw memory) file or a SNA, SZX or Z80 snapshot into a TAP file. " "FILE may be a regular file, or '-' to read a binary file from standard input.", add_help=False) parser.add_argument('infile', help=argparse.SUPPRESS, nargs='?') parser.add_argument('outfile', help=argparse.SUPPRESS, nargs='?') group = parser.add_argument_group('Options') group.add_argument( '-c', '--clear', dest='clear', metavar='N', type=integer, help= "Use a 'CLEAR N' command in the BASIC loader and leave the stack pointer alone." ) group.add_argument('-e', '--end', dest='end', metavar='ADDR', type=integer, default=65536, help="Set the end address when reading a snapshot.") group.add_argument( '-o', '--org', dest='org', metavar='ORG', type=integer, help= "Set the origin address (default: 16384 for a snapshot, otherwise 65536 minus the length of FILE)." ) group.add_argument('-p', '--stack', dest='stack', metavar='STACK', type=integer, help="Set the stack pointer (default: ORG).") group.add_argument('-s', '--start', dest='start', metavar='START', type=integer, help="Set the start address to JP to (default: ORG).") group.add_argument( '-S', '--screen', dest='screen', metavar='FILE', help= "Add a loading screen to the TAP file. FILE may be a snapshot or a 6912-byte SCR file." ) 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) infile = namespace.infile if unknown_args or infile is None: parser.exit(2, parser.format_help()) if infile.lower().endswith(('.sna', '.szx', '.z80')): org = namespace.org or 16384 if org >= namespace.end: raise SkoolKitError( 'End address must be greater than {}'.format(org)) ram = get_snapshot(infile)[org:namespace.end] else: ram = read_bin_file(infile, 49152) if len(ram) == 0: raise SkoolKitError('{} is empty'.format(infile)) org = namespace.org or 65536 - len(ram) clear = namespace.clear start = namespace.start or org stack = namespace.stack or org tapfile = namespace.outfile if tapfile is None: if infile.lower().endswith(('.bin', '.sna', '.szx', '.z80')): prefix = infile[:-4] elif infile == '-': prefix = 'program' else: prefix = infile tapfile = prefix + ".tap" scr = namespace.screen if scr is not None: if scr.lower().endswith(('.sna', '.szx', '.z80')): scr = get_snapshot(scr)[16384:23296] else: scr = read_bin_file(scr, 6912) run(ram, clear, org, start, stack, tapfile, scr)