mirror of
https://github.com/elliotnunn/enablifier.git
synced 2025-02-19 17:31:36 +00:00
Initial commit
This commit is contained in:
commit
88b93fe9ff
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.DS_Store
|
20
README.md
Normal file
20
README.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
*Enablifier*: add a System Enabler to a Mac OS ROM file
|
||||||
|
=======================================================
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
The destination file must first contain an Open Firmware bootinfo image (<https://github.com/elliotnunn/newworld-rom>). System Enabler data will be copied from the source file to the destination file's data and resource forks. This tool is *idempotent*: existing enabler data will be removed from the destination.
|
||||||
|
|
||||||
|
enablify SOURCE-ENABLER DEST-FILE
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
cp tbxi tbxi-enabler
|
||||||
|
enablify templates/mac-os-rom-9.6.1 tbxi-enabler
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
- Python 3
|
||||||
|
- Rez and DeRez (part of Apple's Command Line Developer Tools)
|
153
enablify
Executable file
153
enablify
Executable file
@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from zlib import adler32
|
||||||
|
from subprocess import run, DEVNULL, PIPE
|
||||||
|
from sys import argv
|
||||||
|
from os.path import exists
|
||||||
|
from shutil import copyfile
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
MAGIC = b'Joy!peffpwpc'
|
||||||
|
RFORK = '/..namedfork/rsrc'
|
||||||
|
|
||||||
|
|
||||||
|
# def copyfile(a, b):
|
||||||
|
# run(['cp', a, b], check=True, stderr=DEVNULL)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_info_size(b):
|
||||||
|
a, b, c = b.partition(b'constant info-size')
|
||||||
|
a, b, c = a.rpartition(b'h#')
|
||||||
|
return int(c, 16)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_cfrg_resources(path, *args):
|
||||||
|
cfrg_derez = run(['DeRez', '-only', 'cfrg', *args, path], check=True, stdout=PIPE).stdout
|
||||||
|
|
||||||
|
line_groups = []
|
||||||
|
for l in cfrg_derez.split(b'\n'):
|
||||||
|
if not l: continue
|
||||||
|
|
||||||
|
if l.startswith(b'data'):
|
||||||
|
line_groups.append([l])
|
||||||
|
else:
|
||||||
|
line_groups[-1].append(l)
|
||||||
|
|
||||||
|
cfrg_tuples = [] # list of (firstline, lastline, binary)
|
||||||
|
for l in line_groups:
|
||||||
|
if not l[-1].startswith(b'}'): raise ValueError
|
||||||
|
|
||||||
|
accum = bytearray()
|
||||||
|
for dl in l:
|
||||||
|
if not dl.lstrip().startswith(b'$'): continue
|
||||||
|
_, _, h = dl.partition(b'$"')
|
||||||
|
h, _, _ = h.rpartition(b'"')
|
||||||
|
h = h.decode('ascii')
|
||||||
|
h = bytes.fromhex(h)
|
||||||
|
accum.extend(h)
|
||||||
|
|
||||||
|
cfrg_tuples.append((l[0], l[-1], accum))
|
||||||
|
|
||||||
|
return cfrg_tuples
|
||||||
|
|
||||||
|
|
||||||
|
def _save_cfrg_resources(path, the_resources):
|
||||||
|
savestr = bytearray()
|
||||||
|
|
||||||
|
for firstline, lastline, data in the_resources:
|
||||||
|
savestr.extend(firstline)
|
||||||
|
savestr.extend(b'\n')
|
||||||
|
savestr.extend(b'$"' + data.hex().encode('ascii') + b'"\n')
|
||||||
|
# for b in data:
|
||||||
|
# savestr.extend((' $"%02x"\n' % b).encode('ascii'))
|
||||||
|
savestr.extend(lastline)
|
||||||
|
savestr.extend(b'\n')
|
||||||
|
|
||||||
|
# run(['cat'], check=True, input=savestr)
|
||||||
|
with tempfile.NamedTemporaryFile(mode='wb') as f:
|
||||||
|
f.write(savestr)
|
||||||
|
f.flush()
|
||||||
|
run(['Rez', '-a', '-o', path, f.name], check=True)
|
||||||
|
|
||||||
|
|
||||||
|
# return (start, stop)
|
||||||
|
def _get_code_fragment_range(b):
|
||||||
|
NASTY = b'\r\\ h#'
|
||||||
|
info_size = _get_info_size(b)
|
||||||
|
the_start = b.find(MAGIC, info_size)
|
||||||
|
|
||||||
|
the_end = len(b)
|
||||||
|
if NASTY in b[-100:]:
|
||||||
|
the_end = b.rfind(NASTY)
|
||||||
|
|
||||||
|
return the_start, the_end
|
||||||
|
|
||||||
|
|
||||||
|
def _find_all(a_str, sub):
|
||||||
|
start = 0
|
||||||
|
while True:
|
||||||
|
start = a_str.find(sub, start)
|
||||||
|
if start == -1: return
|
||||||
|
yield start
|
||||||
|
start += len(sub) # use start += 1 to find overlapping matches
|
||||||
|
|
||||||
|
|
||||||
|
def _replace_int32_in_bytearray(b, x, y):
|
||||||
|
x = x.to_bytes(length=4, byteorder='big', signed=False)
|
||||||
|
y = y.to_bytes(length=4, byteorder='big', signed=False)
|
||||||
|
b[:] = b.replace(x, y)
|
||||||
|
|
||||||
|
|
||||||
|
def enablify(orig, dest):
|
||||||
|
with open(orig, 'rb') as f:
|
||||||
|
orig_df = f.read()
|
||||||
|
with open(dest, 'rb') as f:
|
||||||
|
dest_df = bytearray(f.read())
|
||||||
|
|
||||||
|
# trim dest data fork if it already has enabler-related junk at the end
|
||||||
|
dest_info_size = _get_info_size(dest_df)
|
||||||
|
if len(dest_df) > dest_info_size + 20:
|
||||||
|
cut_at = dest_df.find(MAGIC, dest_info_size)
|
||||||
|
del dest_df[cut_at:]
|
||||||
|
|
||||||
|
dest_needs_checksum = b'\r\\ h#' in dest_df[-20:]
|
||||||
|
|
||||||
|
old_offset, old_stop = _get_code_fragment_range(orig_df)
|
||||||
|
|
||||||
|
while len(dest_df) % 16:
|
||||||
|
dest_df.append(0)
|
||||||
|
new_offset = len(dest_df)
|
||||||
|
|
||||||
|
every_sub_offset = list(_find_all(orig_df[old_offset:old_stop], MAGIC))
|
||||||
|
|
||||||
|
the_resources = list(_get_cfrg_resources(orig))
|
||||||
|
|
||||||
|
for _, _, rsrc_data in the_resources:
|
||||||
|
for this_sub_offset in every_sub_offset:
|
||||||
|
# print('Replacing', hex(old_offset + this_sub_offset), 'with', hex(new_offset + this_sub_offset))
|
||||||
|
_replace_int32_in_bytearray(rsrc_data, old_offset + this_sub_offset, new_offset + this_sub_offset)
|
||||||
|
|
||||||
|
dest_df.extend(orig_df[old_offset:old_stop])
|
||||||
|
|
||||||
|
# re-checksum?
|
||||||
|
if dest_needs_checksum:
|
||||||
|
cksum = adler32(dest_df)
|
||||||
|
cksum_str = ('\r\\ h# %08X' % cksum).encode('ascii')
|
||||||
|
dest_df.extend(cksum_str)
|
||||||
|
|
||||||
|
|
||||||
|
with open(dest, 'wb') as f:
|
||||||
|
f.write(dest_df)
|
||||||
|
|
||||||
|
if exists(orig + '.r'):
|
||||||
|
# print('doing the rez thing')
|
||||||
|
run(['Rez', '-o', dest, orig + '.r'], check=True)
|
||||||
|
else:
|
||||||
|
copyfile(orig + RFORK, dest + RFORK)
|
||||||
|
|
||||||
|
_save_cfrg_resources(dest, the_resources)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
orig, dest = argv[1:]
|
||||||
|
enablify(orig, dest)
|
9855
templates/mac-os-rom-9.6.1
Normal file
9855
templates/mac-os-rom-9.6.1
Normal file
File diff suppressed because one or more lines are too long
39098
templates/mac-os-rom-9.6.1.r
Normal file
39098
templates/mac-os-rom-9.6.1.r
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user