From 350f11b0c099ac7c241f8f83850ff17fbe6727b6 Mon Sep 17 00:00:00 2001 From: Elliot Nunn Date: Tue, 6 Nov 2018 08:38:59 +0800 Subject: [PATCH] Refactor Make/DumpHFS into Python library --- bin/DumpHFS | 48 +---------------- bin/MakeHFS | 99 +---------------------------------- machfs/main.py | 136 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 139 insertions(+), 144 deletions(-) diff --git a/bin/DumpHFS b/bin/DumpHFS index 7a49e10..d148021 100755 --- a/bin/DumpHFS +++ b/bin/DumpHFS @@ -1,16 +1,7 @@ #!/usr/bin/env python3 import argparse -import os -import os.path as path -from machfs import Volume, Folder, File -import macresources - -def any_exists(at_path): - if path.exists(at_path): return True - if path.exists(at_path + '.rdump'): return True - if path.exists(at_path + '.idump'): return True - return False +from machfs import Volume args = argparse.ArgumentParser() @@ -23,39 +14,4 @@ with open(args.src[0], 'rb') as f: v = Volume() v.read(f.read()) -written = [] -for p, obj in v.iter_paths(): - nativepath = path.join(args.dir[0], *(comp.replace(path.sep, ':') for comp in p)) - - if isinstance(obj, Folder): - os.makedirs(nativepath, exist_ok=True) - - elif obj.mddate != obj.bkdate or not any_exists(nativepath): - data = obj.data - if obj.type == b'TEXT': - data = data.decode('mac_roman').replace('\r', os.linesep).encode('utf8') - - rsrc = obj.rsrc - if rsrc: - rsrc = macresources.parse_file(rsrc) - rsrc = macresources.make_rez_code(rsrc, ascii_clean=True) - - info = obj.type + obj.creator - if info == b'????????': info = b'' - - for thing, suffix in ((data, ''), (rsrc, '.rdump'), (info, '.idump')): - wholepath = nativepath + suffix - if thing or (suffix == '' and not rsrc): - written.append(wholepath) - with open(written[-1], 'wb') as f: - f.write(thing) - else: - try: - os.remove(wholepath) - except FileNotFoundError: - pass - -if written: - t = path.getmtime(written[-1]) - for f in written: - os.utime(f, (t, t)) +v.write_folder(args.dir[0]) diff --git a/bin/MakeHFS b/bin/MakeHFS index ad3f736..0a56886 100755 --- a/bin/MakeHFS +++ b/bin/MakeHFS @@ -1,12 +1,8 @@ #!/usr/bin/env python3 import argparse -import os -import os.path as path -import re from datetime import datetime -from machfs import Volume, Folder, File -import macresources +from machfs import Volume ######################################################################## @@ -76,99 +72,8 @@ args = args.parse_args() vol = Volume() vol.name = args.name -vol.crdate = vol.mddate = vol.bkdate = args.date -######################################################################## - -def includefilter(n): - if n.startswith('.'): return False - if n.endswith('.rdump'): return True - if n.endswith('.idump'): return True - return True - -def swapsep(n): - return n.replace(':', path.sep) - -def mkbasename(n): - base, ext = path.splitext(n) - if ext in ('.rdump', '.idump'): - return base - else: - return n - -if args.dir is not None: - tmptree = {args.dir: vol} - - for dirpath, dirnames, filenames in os.walk(args.dir): - dirnames[:] = [swapsep(x) for x in dirnames if includefilter(x)] - filenames[:] = [swapsep(x) for x in filenames if includefilter(x)] - - for dn in dirnames: - newdir = Folder() - newdir.crdate = newdir.mddate = newdir.bkdate = args.date - tmptree[dirpath][dn] = newdir - tmptree[path.join(dirpath, dn)] = newdir - - for fn in filenames: - basename = mkbasename(fn) - fullbase = path.join(dirpath, basename) - fullpath = path.join(dirpath, fn) - - try: - thefile = tmptree[fullbase] - except KeyError: - thefile = File() - thefile.real_t = 0 # for the MPW hack - thefile.crdate = thefile.mddate = thefile.bkdate = args.date - thefile.contributors = [] - tmptree[fullbase] = thefile - - if fn.endswith('.idump'): - with open(fullpath, 'rb') as f: - thefile.type = f.read(4) - thefile.creator = f.read(4) - elif fn.endswith('rdump'): - rez = open(fullpath, 'rb').read() - resources = macresources.parse_rez_code(rez) - resfork = macresources.make_file(resources, align=4) - thefile.rsrc = resfork - else: - thefile.data = open(fullpath, 'rb').read() - - thefile.contributors.append(fullpath) - if args.mpw_dates: - thefile.real_t = max(thefile.real_t, path.getmtime(fullpath)) - - tmptree[dirpath][basename] = thefile - - for pathtpl, obj in vol.iter_paths(): - try: - if obj.type == b'TEXT': - obj.data = obj.data.decode('utf8').replace('\r\n', '\r').replace('\n', '\r').encode('mac_roman') - except AttributeError: - pass - -######################################################################## - -if args.mpw_dates: - all_real_times = set() - for pathtpl, obj in vol.iter_paths(): - try: - all_real_times.add(obj.real_t) - except AttributeError: - pass - ts2idx = {ts: idx for (idx, ts) in enumerate(sorted(set(all_real_times)))} - - for pathtpl, obj in vol.iter_paths(): - try: - real_t = obj.real_t - except AttributeError: - pass - else: - fake_t = obj.crdate + 60 * ts2idx[real_t] - obj.crdate = obj.mddate = obj.bkdate = fake_t - -######################################################################## +if args.dir: vol.read_folder(args.dir, date=args.date, mpw_dates=args.mpw_dates) image = vol.write(args.size, startapp=args.app) with open(args.dest[0], 'wb') as f: diff --git a/machfs/main.py b/machfs/main.py index ce763d3..1e90fe9 100644 --- a/machfs/main.py +++ b/machfs/main.py @@ -1,5 +1,7 @@ import struct -from macresources import Resource, make_file, parse_file +import os +from os import path +from macresources import Resource, make_file, parse_file, make_rez_code, parse_rez_code from . import btree, bitmanip, directory @@ -494,3 +496,135 @@ class Volume(directory.AbstractFolder): finalchunks.append(vib) finalchunks.append(bytes(512)) return b''.join(finalchunks) + + def read_folder(self, folder_path, date=0, mpw_dates=False): + def includefilter(n): + if n.startswith('.'): return False + if n.endswith('.rdump'): return True + if n.endswith('.idump'): return True + return True + + def swapsep(n): + return n.replace(':', path.sep) + + def mkbasename(n): + base, ext = path.splitext(n) + if ext in ('.rdump', '.idump'): + return base + else: + return n + + self.crdate = self.mddate = self.bkdate = date + + tmptree = {folder_path: self} + + for dirpath, dirnames, filenames in os.walk(folder_path): + dirnames[:] = [swapsep(x) for x in dirnames if includefilter(x)] + filenames[:] = [swapsep(x) for x in filenames if includefilter(x)] + + for dn in dirnames: + newdir = Folder() + newdir.crdate = newdir.mddate = newdir.bkdate = date + tmptree[dirpath][dn] = newdir + tmptree[path.join(dirpath, dn)] = newdir + + for fn in filenames: + basename = mkbasename(fn) + fullbase = path.join(dirpath, basename) + fullpath = path.join(dirpath, fn) + + try: + thefile = tmptree[fullbase] + except KeyError: + thefile = File() + thefile.real_t = 0 # for the MPW hack + thefile.crdate = thefile.mddate = thefile.bkdate = date + thefile.contributors = [] + tmptree[fullbase] = thefile + + if fn.endswith('.idump'): + with open(fullpath, 'rb') as f: + thefile.type = f.read(4) + thefile.creator = f.read(4) + elif fn.endswith('rdump'): + rez = open(fullpath, 'rb').read() + resources = parse_rez_code(rez) + resfork = make_file(resources, align=4) + thefile.rsrc = resfork + else: + thefile.data = open(fullpath, 'rb').read() + + thefile.contributors.append(fullpath) + if mpw_dates: + thefile.real_t = max(thefile.real_t, path.getmtime(fullpath)) + + tmptree[dirpath][basename] = thefile + + for pathtpl, obj in self.iter_paths(): + try: + if obj.type == b'TEXT': + obj.data = obj.data.decode('utf8').replace('\r\n', '\r').replace('\n', '\r').encode('mac_roman') + except AttributeError: + pass + + if mpw_dates: + all_real_times = set() + for pathtpl, obj in self.iter_paths(): + try: + all_real_times.add(obj.real_t) + except AttributeError: + pass + ts2idx = {ts: idx for (idx, ts) in enumerate(sorted(set(all_real_times)))} + + for pathtpl, obj in self.iter_paths(): + try: + real_t = obj.real_t + except AttributeError: + pass + else: + fake_t = obj.crdate + 60 * ts2idx[real_t] + obj.crdate = obj.mddate = obj.bkdate = fake_t + + def write_folder(self, folder_path): + def any_exists(at_path): + if path.exists(at_path): return True + if path.exists(at_path + '.rdump'): return True + if path.exists(at_path + '.idump'): return True + return False + + written = [] + for p, obj in self.iter_paths(): + nativepath = path.join(folder_path, *(comp.replace(path.sep, ':') for comp in p)) + + if isinstance(obj, Folder): + os.makedirs(nativepath, exist_ok=True) + + elif obj.mddate != obj.bkdate or not any_exists(nativepath): + data = obj.data + if obj.type == b'TEXT': + data = data.decode('mac_roman').replace('\r', os.linesep).encode('utf8') + + rsrc = obj.rsrc + if rsrc: + rsrc = parse_file(rsrc) + rsrc = make_rez_code(rsrc, ascii_clean=True) + + info = obj.type + obj.creator + if info == b'????????': info = b'' + + for thing, suffix in ((data, ''), (rsrc, '.rdump'), (info, '.idump')): + wholepath = nativepath + suffix + if thing or (suffix == '' and not rsrc): + written.append(wholepath) + with open(written[-1], 'wb') as f: + f.write(thing) + else: + try: + os.remove(wholepath) + except FileNotFoundError: + pass + + if written: + t = path.getmtime(written[-1]) + for w in written: + os.utime(w, (t, t))