tbxi-patches/ataboot.py

97 lines
2.9 KiB
Python
Executable File

#!/usr/bin/env python3
import patch_common
from os import path
import os
import struct
import shutil
import re
import binascii
src, cleanup = patch_common.get_src(desc='''
Support ATA startup disks without an Apple_Driver_ATA partition. This allows Mac
OS to be installed on a disk without repartitioning it. Accomplished by
shoehorning the Drive Setup 2.1 ATA driver into the ROM ATALoad driver, to
replace missing driver partitions.
''')
def align_bytearray(ary, factor):
while len(ary) % factor != 0:
ary.append(0)
def find_InitDevice(code):
for i in range(0, len(code), 2):
# checking Driver Descriptor Map magic number (there are two but this one always first)
if code[i:i+2] == b'ER':
for j in reversed(range(0, i, 2)):
if code[j:j+2] == b'NV': # LINK A6,#$xxxx
return j
raise ValueError('Function InitDevice not found')
def patch_ataload(code):
code = bytearray(code)
align_bytearray(code, 2)
cut1 = len(code) # boundary between original and glue
# Parse the DumpObj'd file
with open(path.join(path.dirname(__file__), 'ATALoad.dmp')) as f:
for l in f:
m = re.match(r'^[0-9A-F]{8}: ([0-9A-F ]+)', l) or re.match('^ {13}([0-9A-F][0-9A-F ]*)', l)
if m:
code.extend(binascii.unhexlify(m.group(1).replace(' ', '')))
align_bytearray(code, 2)
cut2 = len(code) # boundary between glue and driver
with open(path.join(path.dirname(__file__), 'AppleATADisk'), 'rb') as f:
code.extend(f.read())
InitDevice = find_InitDevice(code[:cut1])
print('ATALoad patch: InitDevice=0x%X, glue=0x%X, driver=0x%X' % (InitDevice, cut1, cut2))
# "Link" the new code into the old
for i in range(cut1, len(code), 2):
if code[i:i+4] == b'Nsrt':
code[i:i+4] = code[InitDevice:InitDevice+4] # copy the first LINK from the original function
code[InitDevice:InitDevice+2] = b'\x60\x00' # BRA.W opcode, to...
code[InitDevice+2:InitDevice+4] = struct.pack('>h', cut1 - (InitDevice+2)) # ...the start of the new code
if code[i:i+2] == b'At':
code[i:i+2] = struct.pack('>h', cut2 - i)
if code[i:i+4] == b'Size':
code[i:i+4] = struct.pack('>L', len(code) - cut2)
if code[i:i+2] == b'ID': # reference to original InitDevice, skipping the mangled 4-byte LINK
code[i:i+2] = struct.pack('>h', (InitDevice+4) - i)
return bytes(code)
found_drvr = False
for (parent, folders, files) in os.walk(src):
folders.sort(); files.sort() # make it kinda deterministic
for filename in files:
full = path.join(parent, filename)
if filename == 'DRVR_-20175_ATALoad':
code = open(full, 'rb').read()
code = patch_ataload(code)
open(full, 'wb').write(code)
found_drvr = True
if not found_drvr:
raise ValueError('ATALoad DRVR not found')
cleanup()