#!/usr/bin/env python3 import argparse import macresources import sys import tempfile from os import path def fourcc(s): b = s.encode('mac_roman').ljust(4, b' ') if len(b) != 4: raise ValueError('wrong length') return b def seemshex(s): s = s.lower() if not s: return False return all(c in '0123456789abcdef' for c in s) def seemsdec(s): if not s: return False return all(c in '0123456789' for c in s) def resid(s): s = s.lower() if s.startswith('0x') and seemshex(s[2:]): thenum = int(s[2:], 16) if thenum > 0x7fff: thenum -= 0x8000 elif s.startswith('$') and seemshex(s[1:]): thenum = int(s[1:], 16) if thenum > 0x7fff: thenum -= 0x8000 else: thenum = int(s) if not (-0x8000 <= thenum <= 0x7fff): raise ValueError return thenum parser = argparse.ArgumentParser(description=''' Copy a single MacOS resource from a source file to the standard output. If the source filename ends with `.r' or `.rdump', then it is parsed using the `SimpleRez' subset of the Rez language. TODO: find a way to specify null characters so that desk accessories can be looked up by name. ''') parser.add_argument('srcfile', help='resource file or Rez file') parser.add_argument('type', type=fourcc, help='four-byte type of resource (converted to Mac Roman pre-lookup)') parser.add_argument('id', type=resid, help='ID number of resource (-32768 to 32767)') parser.add_argument('-f', dest='tofile', action='store_true', help='copy to a tempfile instead') args = parser.parse_args() with open(args.srcfile, 'rb') as f: raw = f.read() if args.srcfile.endswith('.r') or args.srcfile.endswith('.rdump'): resources = macresources.parse_rez_code(raw) else: resources = macresources.parse_file(raw) for r in resources: if r.type == args.type and r.id == args.id: myres = r break else: raise ValueError(args.type, args.id) if args.tofile: tmpname = '-%s-%s-%d' % (path.basename(args.srcfile), myres.type.decode('mac_roman'), myres.id) with tempfile.NamedTemporaryFile(suffix=tmpname, delete=False, mode='wb') as f: f.write(myres.data) print(f.name) else: try: sys.stdout.buffer.write(myres.data) except BrokenPipeError: pass