mirror of
https://github.com/elliotnunn/machfs.git
synced 2024-06-18 00:29:31 +00:00
Compare commits
4 Commits
b44a2f9cd3
...
4f554286e6
Author | SHA1 | Date | |
---|---|---|---|
|
4f554286e6 | ||
|
8dd4181d36 | ||
|
4db5dd3819 | ||
|
4d0927eb65 |
14
README.md
14
README.md
|
@ -7,7 +7,7 @@ codes](https://en.wikipedia.org/wiki/Creator_code) are first-class
|
|||
citizens.
|
||||
|
||||
Python interface
|
||||
----------------
|
||||
================
|
||||
The Python API is simple. The contents of a `Volume` or a `Folder` are
|
||||
accessed using the index operator `[]`. While working on a filesystem,
|
||||
its entire high-level contents are stored in memory as a Python object.
|
||||
|
@ -42,7 +42,7 @@ with open('FloppyImage.dsk', 'rb') as f:
|
|||
```
|
||||
|
||||
Command-line interface
|
||||
----------------------
|
||||
======================
|
||||
This package also installs the `MakeHFS` and `DumpHFS` utilities, for
|
||||
working with folders on your native filesystem. Briefly, resource forks
|
||||
are stored in Rez-formatted `.rdump` files, and type and creator codes
|
||||
|
@ -56,7 +56,7 @@ Mac OS Roman encoding with Mac-style (CR) line endings.
|
|||
Both commands have a `--help` argument to display their options.
|
||||
|
||||
Why?
|
||||
----
|
||||
====
|
||||
I want an automated, reproducible way to compile legacy MacOS software.
|
||||
Without any current operating system fully supporting HFS,
|
||||
[libhfs/hfsutils](https://www.mars.org/home/rob/proj/hfs/) (a C library
|
||||
|
@ -71,3 +71,11 @@ high-level programming language seemed like a reasonable tradeoff. As a
|
|||
result, `machfs` has nearly an order of magnitude fewer lines than
|
||||
`libhfs`, and is more maintainable, at a nearly negligible cost in
|
||||
performance.
|
||||
|
||||
Changes
|
||||
=======
|
||||
1.3
|
||||
---
|
||||
- Sam Fuller's btree fix for volumes with *many* files:
|
||||
https://github.com/elliotnunn/machfs/commit/b44a2f9
|
||||
- Support for Aliases, represented in the native FS as symlinks
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import argparse
|
||||
from machfs import Volume
|
||||
import os
|
||||
|
||||
args = argparse.ArgumentParser()
|
||||
|
||||
|
@ -10,6 +11,11 @@ args.add_argument('dir', metavar='OUTPUT', nargs=1, help='Destination folder')
|
|||
|
||||
args = args.parse_args()
|
||||
|
||||
try:
|
||||
os.mkdir(args.dir[0])
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
with open(args.src[0], 'rb') as f:
|
||||
v = Volume()
|
||||
v.read(f.read())
|
||||
|
|
57
bin/MakeHFS
57
bin/MakeHFS
|
@ -123,17 +123,62 @@ vol.name = args.name
|
|||
|
||||
if args.dir: vol.read_folder(args.dir, date=args.date, mpw_dates=args.mpw_dates)
|
||||
|
||||
with open(args.dest[0], 'ab+') as f:
|
||||
if args.size is None: args.size = hack_file_size(f)
|
||||
if args.size == 0: args.size = 800 * 1024
|
||||
try:
|
||||
f = open(args.dest[0], 'rb+')
|
||||
existed = True
|
||||
except FileNotFoundError:
|
||||
f = open(args.dest[0], 'wb+')
|
||||
existed = False
|
||||
|
||||
with open(args.dest[0], 'rb+') as f:
|
||||
left, gap, right = vol.write(args.size, startapp=args.app, sparse=True)
|
||||
if args.size is not None:
|
||||
offset = 0
|
||||
size = args.size
|
||||
trunc = True
|
||||
nameoffset = None
|
||||
|
||||
elif not existed: # make a tiny floppy
|
||||
offset = 0
|
||||
size = 800 * 1024
|
||||
trunc = True
|
||||
nameoffset = None
|
||||
|
||||
elif f.read(2) == b'ER': # is partitioned disk
|
||||
blksize = int.from_bytes(f.read(2), byteorder='big')
|
||||
f.seek(blksize + 4)
|
||||
entrycnt = int.from_bytes(f.read(4), byteorder='big')
|
||||
|
||||
for i in range(entrycnt):
|
||||
entryoffset = blksize * (i + 1)
|
||||
f.seek(entryoffset + 48)
|
||||
if f.read(10) == b'Apple_HFS\x00':
|
||||
f.seek(entryoffset + 8)
|
||||
offset = blksize * int.from_bytes(f.read(4), byteorder='big')
|
||||
size = blksize * int.from_bytes(f.read(4), byteorder='big')
|
||||
trunc = False
|
||||
nameoffset = entryoffset + 16
|
||||
break
|
||||
else:
|
||||
raise ValueError("No HFS partition in this map")
|
||||
|
||||
else: # is raw filesystem
|
||||
offset = 0
|
||||
size = hack_file_size(f)
|
||||
trunc = False
|
||||
|
||||
left, gap, right = vol.write(size, startapp=args.app, sparse=True)
|
||||
|
||||
f.seek(offset)
|
||||
f.write(left)
|
||||
f.seek(gap, 1)
|
||||
problem = len(left) + gap - f.tell()
|
||||
if problem > 0:
|
||||
f.write(bytes(problem))
|
||||
f.write(right)
|
||||
f.truncate()
|
||||
|
||||
if nameoffset is not None:
|
||||
f.seek(nameoffset)
|
||||
f.write(args.name.encode('mac_roman').ljust(32, b'\x00'))
|
||||
|
||||
|
||||
|
||||
if trunc: f.truncate()
|
||||
|
|
|
@ -2,7 +2,7 @@ from collections.abc import MutableMapping
|
|||
import os
|
||||
from os import path
|
||||
from macresources import make_rez_code, parse_rez_code, make_file, parse_file
|
||||
from warnings import warn
|
||||
import sys
|
||||
|
||||
|
||||
TEXT_TYPES = [b'TEXT', b'ttro'] # Teach Text read-only
|
||||
|
@ -267,7 +267,7 @@ class AbstractFolder(MutableMapping):
|
|||
blacklist_test = ':'.join(p) + ':'
|
||||
if blacklist_test.startswith(tuple(blacklist)): continue
|
||||
if _unsyncability(p[-1]):
|
||||
warn('Ignoring unsyncable name: %r' % (':' + ':'.join(p)))
|
||||
print('Ignoring unsyncable name: %r' % (':' + ':'.join(p)), file=sys.stderr)
|
||||
blacklist.append(blacklist_test)
|
||||
continue
|
||||
|
||||
|
@ -293,8 +293,14 @@ class AbstractFolder(MutableMapping):
|
|||
|
||||
# write a resource dump iff that fork has any bytes (dump may still be empty)
|
||||
if obj.rsrc:
|
||||
with open(rsrc_path, 'wb') as f:
|
||||
try:
|
||||
rdump = make_rez_code(parse_file(obj.rsrc), ascii_clean=True)
|
||||
except:
|
||||
with open(nativepath + '.rdump.corrupt', 'wb') as f:
|
||||
f.write(obj.rsrc)
|
||||
print('Dumping corrupt resource fork: %r' % (':' + ':'.join(p)), file=sys.stderr)
|
||||
else:
|
||||
with open(rsrc_path, 'wb') as f:
|
||||
f.write(rdump)
|
||||
else:
|
||||
_try_delete(rsrc_path)
|
||||
|
|
Loading…
Reference in New Issue
Block a user