def main(): parser = argparse.ArgumentParser( description='Remove all noise, idealize the data') parser.add_argument('file', nargs='?', default='/dev/stdin', help='TZX file, stdin if omitted') parser.add_argument('-o', '--to', dest='to', metavar='TARGET', default='/dev/stdout', help='target TZX file, stdout if omitted') parser.add_argument('-c', '--stripcrc', dest='stripcrc', action='store_true', help='also remove blocks with bad CRC') args = parser.parse_args() fout = TzxFile() crcCnt = 0 noiseCnt = 0 file = TzxFile() file.read(args.file) for b in file.blocks: # Convert Turbo blocks to standard timed blocks if possible if b.id in [0x11, 0x14]: o = b.asData() # Use all data blocks for the output if b.id in [0x10, 0x11, 0x14]: if not b.valid(): crcCnt += 1 if b.valid() or not args.stripcrc: fout.blocks.append(b) continue # Use all pause blocks if they mean "stop the tape" if b.id in [0x20, 0x2A]: if b.id != 0x20 or b.length() == 0: fout.blocks.append(b) continue # Use all meta blocks if b.id not in [0x12, 0x13, 0x15, 0x18, 0x19]: fout.blocks.append(b) continue noiseCnt += 1 fout.write(args.to) print('Blocks found: %3d' % (len(file.blocks)), file=sys.stderr) print('Noise blocks removed: %3d' % (noiseCnt), file=sys.stderr) print('Blocks with CRC errors: %3d' % (crcCnt), file=sys.stderr) print('Blocks written: %3d' % (len(fout.blocks)), file=sys.stderr)
def main(): parser = argparse.ArgumentParser(description='Convert to TAP file format') parser.add_argument('file', nargs='?', type=argparse.FileType('rb'), default=(None if sys.stdin.isatty() else sys.stdin.buffer), help='TZX file, stdin if omitted') parser.add_argument('-o', '--to', dest='to', metavar='TARGET', type=argparse.FileType('wb'), default=sys.stdout.buffer, help='TAP file, stdout if omitted') parser.add_argument('-i', '--ignore', dest='ignore', action='store_true', help='ignore blocks that cannot be stored in a TAP file') args = parser.parse_args() if args.file is None: parser.print_help(sys.stderr) sys.exit(1) file = TzxFile() file.read(args.file) outf = args.to if args.to != '-' else sys.stdout.buffer with outf if isinstance(outf, io.IOBase) else open(outf, 'wb') as tap: writeAllBlocks(file, tap, args.ignore)
def load(self, filename, startFrame=None, endFrame=None): try: self.samples.open(filename) self.samples.fileRange(startFrame, endFrame) tzx = TzxFile() while True: try: tzxbd = TzxbData() (tzxData, startPos, endPos) = self._loadBlock() tzxbd.setup(tzxData) tzx.blocks.append(tzxbd) if self.verbose: startMillis = self.samples.toMilliSeconds(startPos) startSecs = startMillis // 1000 startMins = startSecs // 60 print(('{:3d}:{:02d}.{:03d} {:9d} - {:9d}: {}').format( startMins, startSecs % 60, startMillis % 1000, startPos, endPos, str(tzxbd)), file=sys.stderr) except BadBlock: continue # Try again with the next block except EOFError: break # we're done! finally: self.samples.close() return tzx
def main(): parser = argparse.ArgumentParser( description='List the contents of a TZX file') parser.add_argument('file', nargs='*', help='TZX files, stdin if omitted') parser.add_argument('-s', '--short', dest='short', action='store_true', help='list only the ZX Spectrum header names') parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='show content of information blocks') args = parser.parse_args() for f in args.file if len(args.file) > 0 else ['/dev/stdin']: if len(args.file) > 1: print('\n%s:' % (f)) tzx = TzxFile() tzx.read(f) cnt = 0 for b in tzx.blocks: if args.short: if hasattr(b, 'tap') and isinstance(b.tap, TapHeader): print('%s: %s' % (b.tap.type(), b.tap.name())) else: print('%3d %-27s %s' % (cnt, b.type, str(b))) if args.verbose: info = b.info() if info is not None: print(textwrap.indent(info.strip(), '\t')) cnt += 1
def load(self, filename, startFrame=None, endFrame=None): try: self.samples.open(filename) self.samples.fileRange(startFrame, endFrame) tzx = TzxFile() while True: try: tzxbd = TzxbData() (tzxData, startPos, endPos) = self._loadBlock() tzxbd.setup(tzxData) tzx.blocks.append(tzxbd) if self.verbose: print( ('{} {:9d} - {:9d} : {}').format( str( datetime.timedelta( seconds=self.samples.toSeconds( startPos))), # show time startPos, endPos, str(tzxbd)), file=sys.stderr) except BadBlock: continue # Try again with the next block except EOFError: break # we're done! finally: self.samples.close() return tzx
def main(): parser = argparse.ArgumentParser(description='Merges TZX files') parser.add_argument('files', nargs='+', help='TZX files to merge') parser.add_argument('-o', '--to', metavar='TARGET', default='/dev/stdout', help='target TZX file, stdout if omitted') args = parser.parse_args() file = TzxFile() for f in args.files: mergeFile = TzxFile() mergeFile.read(f) file.blocks.extend(mergeFile.blocks) file.write(args.to)
def main(): parser = argparse.ArgumentParser( description='Split into separate programs') parser.add_argument('file', nargs='?', default='/dev/stdin', help='TZX file, stdin if omitted') parser.add_argument('-d', '--dir', dest='dir', metavar='TARGET', default='./', help='target directory, default is cwd') parser.add_argument('-1', '--single', dest='single', action='store_true', help='split into single loadable files') parser.add_argument('-s', '--skip', dest='skip', action='store_true', help='skip all blocks before first Program') args = parser.parse_args() file = TzxFile() file.read(args.file) dir = args.dir if dir[-1] != '/': dir += '/' fname = 'preamble' fout = TzxFile() if not args.skip else None for b in file.blocks: if hasattr(b, 'tap') and isinstance( b.tap, TapHeader) and (b.tap.typeId() == 0 or args.single): writeTzx(fout, fname, dir) fout = TzxFile() fname = b.tap.name().strip() if not fout is None: fout.blocks.append(b) writeTzx(fout, fname, dir)
def load(self, filename, startFrame=None, endFrame=None): try: self.samples.open(filename) self.samples.fileRange(startFrame, endFrame) tzx = TzxFile() while True: try: tzxbd = TzxbData() tzxbd.setup(self._loadBlock()) tzx.blocks.append(tzxbd) if self.verbose: print(str(tzxbd), file=sys.stderr) except BadBlock: continue # Try again with the next block except EOFError: break # we're done! finally: self.samples.close() return tzx
def main(): parser = argparse.ArgumentParser(description='List the contents of a TZX file') parser.add_argument('file', nargs=argparse.REMAINDER, type=argparse.FileType('rb'), help='TZX files, stdin if omitted') parser.add_argument('-s', '--short', dest='short', action='store_true', help='list only the ZX Spectrum header names') parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='show content of information blocks') args = parser.parse_args() files = list(args.file) if not sys.stdin.isatty() and len(files) == 0: files.append(sys.stdin.buffer) if len(files) == 0: parser.print_help(sys.stderr) sys.exit(1) for f in files: if len(files) > 1: name = f.name if hasattr(f, 'name') else f print('\n%s:' % (name)) tzx = TzxFile() tzx.read(f) cnt = 0 for b in tzx.blocks: if args.short: if hasattr(b, 'tap') and isinstance(b.tap, TapHeader): print('%s: %s' % (b.tap.type(), b.tap.name())) else: print('%3d %-27s %s' % (cnt, b.type, str(b))) if args.verbose: info = b.info() if info is not None: print(textwrap.indent(info.strip(), '\t')) cnt += 1
def main(): parser = argparse.ArgumentParser(description='Convert to TAP file format') parser.add_argument('file', nargs='?', default='/dev/stdin', help='TZX file, stdin if omitted') parser.add_argument('-o', '--to', dest='to', metavar='TARGET', default='/dev/stdout', help='TAP file, stdout if omitted') parser.add_argument('-i', '--ignore', dest='ignore', action='store_true', help='ignore blocks that cannot be stored in a TAP file') args = parser.parse_args() file = TzxFile() file.read(args.file or '/dev/stdin') with open(args.to, 'wb') as out: writeAllBlocks(file, out, args.ignore)
def main(): parser = argparse.ArgumentParser(description='Write data block content') parser.add_argument('file', nargs='?', type=argparse.FileType('rb'), default=(None if sys.stdin.isatty() else sys.stdin.buffer), help='TZX file, stdin if omitted') parser.add_argument('-b', '--block', dest='block', type=int, metavar='NR', help='block number to cat') parser.add_argument('-o', '--to', dest='to', metavar='TARGET', type=argparse.FileType('wb'), default=sys.stdout.buffer, help='target file, stdout if omitted') parser.add_argument('-s', '--skip', dest='skip', type=int, metavar='BYTES', help='skip the given number of bytes before output') parser.add_argument('-l', '--length', dest='length', type=int, metavar='BYTES', help='limit output to the given number of bytes') parser.add_argument('-t', '--text', dest='text', action='store_true', help='convert ZX Spectrum text to plain text') parser.add_argument('-B', '--basic', dest='basic', action='store_true', help='convert ZX Spectrum BASIC to plain text') parser.add_argument('-A', '--assembler', dest='assembler', action='store_true', help='disassemble Z80 code') parser.add_argument('-S', '--screen', dest='screen', action='store_true', help='convert a ZX Spectrum SCREEN$ to PNG') parser.add_argument('-d', '--dump', dest='dump', action='store_true', help='convert to a hex dump') parser.add_argument('-O', '--org', dest='org', type=int, metavar='BASE', help='base address for disassembled code') args = parser.parse_args() if args.file is None: parser.print_help(sys.stderr) sys.exit(1) file = TzxFile() file.read(args.file) converter = lambda data, out, org: out.write(data) # default binary output if args.basic: converter = convertToBasic elif args.assembler: converter = convertToAssembler elif args.screen: converter = convertToScreen elif args.text: converter = convertToText elif args.dump: converter = convertToDump writer = lambda out, dump, org : writeBlock(out, dump, converter, args.skip, args.length, args.org or org or 0) outf = args.to if args.to != '-' else sys.stdout.buffer with outf if isinstance(outf, io.IOBase) else open(outf, 'wb') as out: if args.block != None: writeSingleBlock(file, out, args.block, writer) else: writeAllBlocks(file, out, writer)
def main(): parser = argparse.ArgumentParser(description='Write data block content') parser.add_argument('file', nargs='?', default='/dev/stdin', help='TZX file, stdin if omitted') parser.add_argument('-b', '--block', dest='block', type=int, metavar='NR', help='block number to cat') parser.add_argument('-o', '--to', dest='to', metavar='TARGET', default='/dev/stdout', help='target file, stdout if omitted') parser.add_argument('-s', '--skip', dest='skip', type=int, metavar='BYTES', help='skip the given number of bytes before output') parser.add_argument('-l', '--length', dest='length', type=int, metavar='BYTES', help='limit output to the given number of bytes') parser.add_argument('-t', '--text', dest='text', action='store_true', help='convert ZX Spectrum text to UTF-8') parser.add_argument('-B', '--basic', dest='basic', action='store_true', help='convert ZX Spectrum BASIC to UTF-8 text') parser.add_argument('-A', '--assembler', dest='assembler', action='store_true', help='disassemble Z80 code') parser.add_argument('-S', '--screen', dest='screen', action='store_true', help='convert a ZX Spectrum SCREEN$ to PNG') parser.add_argument('-d', '--dump', dest='dump', action='store_true', help='convert to a hex dump') parser.add_argument('-O', '--org', dest='org', type=int, metavar='BASE', help='base address for disassembled code') args = parser.parse_args() file = TzxFile() file.read(args.file or '/dev/stdin') converter = None if args.basic: converter = lambda data, out, org: out.write(convertToBasic(data).encode('utf-8')) elif args.assembler: converter = lambda data, out, org: out.write(convertToAssembler(data, org or 0).encode('utf-8')) elif args.screen: converter = lambda data, out, org: convertToScreen(data, out) elif args.text: converter = lambda data, out, org: out.write(convertToText(data).encode('utf-8')) elif args.dump: converter = lambda data, out, org: out.write(convertToDump(data).encode('utf-8')) writer = lambda out, block, org : writeBlock(out, block, converter, args.skip, args.length, args.org or org) with open(args.to, 'wb') as out: if args.block != None: writeSingleBlock(file, out, args.block, writer) else: writeAllBlocks(file, out, writer)
def main(): parser = argparse.ArgumentParser( description='Split into separate programs') parser.add_argument('blocks', nargs='*', help='block numbers and ranges to keep') parser.add_argument('-i', '--from', dest='file', metavar='SOURCE', default='/dev/stdin', help='source TZX file, stdin if omitted') parser.add_argument('-o', '--to', dest='to', metavar='TARGET', default='/dev/stdout', help='target TZX file, stdout if omitted') parser.add_argument('-v', '--invert', dest='invert', action='store_true', help='do not keep, but remove the blocks') args = parser.parse_args() file = TzxFile() file.read(args.file) lastBlock = len(file.blocks) ranges = [] for rng in args.blocks: m = re.match(r'^(-?\d+)$', rng) if m: v1 = int(m.group(1)) appendRange(ranges, v1, v1, lastBlock) continue m = re.match(r'^(-?\d+):(-?\d+)$', rng) if m: v1 = int(m.group(1)) v2 = int(m.group(2)) appendRange(ranges, v1, v2, lastBlock) continue m = re.match(r'^(-?\d+):$', rng) if m: v1 = int(m.group(1)) appendRange(ranges, v1, lastBlock, lastBlock) continue m = re.match(r'^:(-?\d+)$', rng) if m: v2 = int(m.group(1)) appendRange(ranges, 0, v2, lastBlock) continue print('Illegal range: %s' % (rng), file=sys.stderr) exit(1) fout = TzxFile() count = 0 for b in file.blocks: if isInRange(ranges, count) != args.invert: fout.blocks.append(b) count += 1 fout.write(args.to)
def main(): parser = argparse.ArgumentParser( description='List the contents of a TZX file') parser.add_argument('file', nargs=argparse.REMAINDER, type=argparse.FileType('rb'), help='TZX files, stdin if omitted') parser.add_argument('-s', '--short', dest='short', action='store_true', help='list only the ZX Spectrum header names') parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='show content of information blocks') parser.add_argument('-w', '--wide', dest='printwide', action='store_true', help='output horizontal, comma separated') parser.add_argument('-X', '--hexdump', dest='hexdump', action='store_true', help='Show 16 byte sample hexdump after block data') args = parser.parse_args() TapFile.showHexSample = args.hexdump files = list(args.file) if not sys.stdin.isatty() and len(files) == 0: files.append(sys.stdin.buffer) if len(files) == 0: parser.print_help(sys.stderr) sys.exit(1) for f in files: if len(files) > 1: name = f.name if hasattr(f, 'name') else f print('\n%s:' % (name)) tzx = TzxFile() tzx.read(f) cnt = 0 sep = "" # endmarker either \n or , for b in tzx.blocks: if args.short: if hasattr(b, 'tap') and isinstance(b.tap, TapHeader): print('%s%s: %s (%s)' % (sep, b.tap.type(), b.tap.name().strip(), b.tap.length()), end="") else: print('%s%3d %-27s %s' % (sep, cnt, b.type, str(b)), end="") if args.verbose: info = b.info() if info is not None: print(textwrap.indent(info.strip(), '\t'), end="") cnt += 1 if args.printwide: sep = ", " else: sep = "\n" # endmarker either \n or , print("") # extra \n
def main(): parser = argparse.ArgumentParser( description='Remove all noise, idealize the data') parser.add_argument( 'file', nargs='?', type=argparse.FileType('rb'), default=(None if sys.stdin.isatty() else sys.stdin.buffer), help='TZX file, stdin if omitted') parser.add_argument('-o', '--to', dest='to', metavar='TARGET', type=argparse.FileType('wb'), default=sys.stdout.buffer, help='target TZX file, stdout if omitted') parser.add_argument('-c', '--stripcrc', dest='stripcrc', action='store_true', help='also remove blocks with bad CRC') parser.add_argument( '-H', '--headermustmatch', dest='headermustmatch', action='store_true', help= 'Remove blocks not proceeded by matching header (keep only matching header-block pairs)' ) args = parser.parse_args() if args.file is None: parser.print_help(sys.stderr) sys.exit(1) fout = TzxFile() crcCnt = 0 noiseCnt = 0 headerlessCnt = 0 file = TzxFile() file.read(args.file) numbytes = 0 blocklengthfromheader = 0 for b in file.blocks: # Convert Turbo blocks to standard timed blocks if possible if b.id == 0x11: # turbo data b = b.asData() # when args.headermustmatch keep last header found if args.headermustmatch and b.id == 0x10 and b.valid() and isinstance( b.tap, TapHeader): if blocklengthfromheader != 0: # header after header makes the first one a orphan header print("Orphan header: {} ({})".format( lastheader.tap.name().strip(), blocklengthfromheader), file=sys.stderr) headerlessCnt = headerlessCnt + 1 lastheader = b blocklengthfromheader = lastheader.tap.length() continue # Use all data blocks for the output if b.id in [0x10, 0x11, 0x14]: if not b.valid(): crcCnt += 1 if b.valid() or not args.stripcrc: if args.headermustmatch: if blocklengthfromheader == len( b.tap.data) - 2 and not isinstance( b.tap, TapHeader): # this is a datablock with matching header fout.blocks.append(lastheader) # write header only now fout.blocks.append(b) numbytes += len(b.tap.data) blocklengthfromheader = 0 else: # as before. fout.blocks.append(b) numbytes += len(b.tap.data) if blocklengthfromheader != 0: print("Orphan header: {} ({})".format( lastheader.tap.name().strip(), blocklengthfromheader), file=sys.stderr) headerlessCnt = headerlessCnt + 1 blocklengthfromheader = 0 continue blocklengthfromheader = 0 # Use all pause blocks if they mean "stop the tape" if b.id in [0x20, 0x2A]: if b.id != 0x20 or b.length() == 0: fout.blocks.append(b) numbytes += len(b.tap.data) continue # Use all meta blocks if b.id not in [0x12, 0x13, 0x15, 0x18, 0x19]: fout.blocks.append(b) numbytes += len(b.tap.data) continue noiseCnt += 1 fout.write(args.to) print('Blocks found: %3d' % (len(file.blocks)), file=sys.stderr) print('Noise blocks removed: %3d' % (noiseCnt), file=sys.stderr) print('Blocks with CRC errors: %3d' % (crcCnt), file=sys.stderr) if args.headermustmatch: print('Skipped headerless blocks: %3d' % (headerlessCnt), file=sys.stderr) print('Blocks written: %3d' % (len(fout.blocks)), file=sys.stderr) print('Total bytes written: %3d' % (numbytes), file=sys.stderr) # DJS
def main(): parser = argparse.ArgumentParser(description='Playback a tzx file') parser.add_argument( 'file', nargs='?', type=argparse.FileType('rb'), default=(None if sys.stdin.isatty() else sys.stdin.buffer), help='TZX file, stdin if omitted') parser.add_argument('-o', '--to', dest='to', metavar='TARGET', type=argparse.FileType('wb'), default=None, help='Create WAV file instead of playing audio') parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='Be verbose about what you are doing') parser.add_argument('-s', '--stop', dest='stop', action='store_true', help='Execute Stop-The-Tape commands') parser.add_argument('-K', '--48k', dest='mode48k', action='store_true', help='Enable ZX Spectrum 48K mode') parser.add_argument('-r', '--rate', dest='rate', default=44100, type=int, help='Output sample rate') parser.add_argument('-c', '--clock', dest='clock', default=3500000, type=int, help='Reference Z80 CPU clock, in Hz') parser.add_argument( '-S', '--sine', dest='sine', action='store_true', help='Generate soft sine pulses (square pulses otherwise)') args = parser.parse_args() if args.file is None: parser.print_help(sys.stderr) sys.exit(1) tzx = TzxFile() tzx.read(args.file) stream = streamAudio(tzx, rate=args.rate, stopAlways=args.stop, stop48k=args.mode48k, sine=args.sine, cpufreq=args.clock, verbose=args.verbose, npy=args.to is None) audiostream = audio = wav = None try: if args.to is not None: # Generate WAV file wav = wave.open(args.to, mode='wb') wav.setnchannels(1) wav.setsampwidth(2) wav.setframerate(args.rate) for b in stream: wav.writeframesraw(b) wav.writeframesraw(silence[0:16]) else: # Audio Playback with sd.Stream(samplerate=args.rate, channels=1, latency='high') as out: for b in stream: out.write(b) except KeyboardInterrupt: print('', file=sys.stderr) print("D BREAK - CONT repeats, 0:1", file=sys.stderr) finally: if wav is not None: wav.close() if audiostream is not None: audiostream.stop_stream() audiostream.close() if audio is not None: audio.terminate()