mirror of
https://github.com/RasppleII/a2server.git
synced 2024-12-23 23:29:46 +00:00
Different tack: Just install boot blocks
This isn't done, but it reflects largely starting over. The goal right now is to install the boot blocks. To do this we need to: * Find out if we need to install anything * Download the disk image archive * Extract the disk image from the archive * Mount the image * Copy the files out of the image * Unmount the image * Patch the files if they need patching * Verify the files are patched correctly The branching possibilities for installing the disk images are: 1. That the image is currently always a .sea.bin file. In future, it might not be! After all, the other six disk images are available multiple places and they're not stored as self-extracting images in MacBinary format. 2. That there are three different ways I know of to extract the HFS disk image: Mount it as a loop filesystem (Linux), attach it as a disk image file (OS X), or extract its contents using hfsutils which should be able to run just about anywhere. Of course hfsutils has an additional branch in that hfsutils "sanitizes" the files you copy out of it for you in ways we don't want, so we need to know the file names it uses so we can change them back to OS X expectations. 3. How are we installing these files? If we're installing them for netatalk, we need to rename a file. If just to a UNIX path, that's something different. If there was some reason to package them in ProDOS format, again, we need to do something different. At this time, since I'm developing this on a Mac, I'm assuming .sea.bin with an 800k image, I'm implementing hdiutil to mount the image, and I'll implement the UNIX naming first (so as Mac/Linux mount the files.) Next I'll implement renaming for netatalk and hfsutils. I'll check it out on the pi and implement the Linux filesystem mounting probably last, then clean it up to behave more like Python is supposed to if I can before I start on the GS/OS install itself. I need a better MacBinary unpacker than unar gives me. The way that works right now is just clumsy.
This commit is contained in:
parent
1796676df1
commit
d9c4af34e0
@ -2,6 +2,8 @@
|
||||
|
||||
import os, sys, subprocess
|
||||
import tempfile
|
||||
import hashlib
|
||||
import xml.etree.cElementTree as ET
|
||||
|
||||
gsosDir = '/media/A2SHARED/FILES'
|
||||
imagesDir = gsosDir + '/GSOS.INSTALLER/IMAGES'
|
||||
@ -20,6 +22,38 @@ adtproDir = commDir + '/ADTPRO'
|
||||
disk7_filename = 'Disk_7_of_7-Apple_II_Setup.sea.bin'
|
||||
disk7_url = 'http://archive.org/download/download.info.apple.com.2012.11/download.info.apple.com.2012.11.zip/download.info.apple.com%2FApple_Support_Area%2FApple_Software_Updates%2FEnglish-North_American%2FApple_II%2FApple_IIGS_System_6.0.1%2F' + disk7_filename
|
||||
|
||||
a2boot_files = [
|
||||
{
|
||||
'unix': 'Apple ::e Boot Blocks',
|
||||
'hfsutils': 'Apple_--e_Boot_Blocks.bin',
|
||||
'netatalk': 'Apple :2f:2fe Boot Blocks',
|
||||
'digest': 'cada362ac2eca3ffa506e9b4e76650ba031e0035',
|
||||
'digest_patched': '6b7fc12fd118e1cb9e39c7a2b8cc870c844a3bac'
|
||||
},
|
||||
{
|
||||
'unix': 'Basic.System',
|
||||
'hfsutils': 'Basic.System.bin',
|
||||
'digest': '4d53424f1451cd2e874cf792dbdc8cc6735dcd36'
|
||||
},
|
||||
{
|
||||
'unix': 'ProDOS16 Boot Blocks',
|
||||
'hfsutils': 'ProDOS16_Boot_Blocks.bin',
|
||||
'digest': 'fab829e82e6662ed6aab119ad18e16ded7d43cda',
|
||||
},
|
||||
{
|
||||
'unix': 'ProDOS16 Image',
|
||||
'hfsutils': 'ProDOS16_Image.bin',
|
||||
'digest': 'db4608067b9e7877f45eb557971c4d8c45b46be5',
|
||||
'digest_patched': '5c35d5533901b292ab7c2f5a3c76cb3113f66085'
|
||||
},
|
||||
{
|
||||
'unix': 'p8',
|
||||
'hfsutils': 'p8.bin',
|
||||
'digest': '36c288a5272cf01e0a64eed16786258959118e0e'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
try:
|
||||
stdin_input = raw_input # Python 2
|
||||
except NameError:
|
||||
@ -30,7 +64,6 @@ try:
|
||||
except ImportError:
|
||||
import urllib2 as urlrequest # Python 2
|
||||
|
||||
|
||||
# Differing from the shell script in that we explicitly strip the / here
|
||||
if 'A2SERVER_SCRIPT_URL' in os.environ:
|
||||
scriptURL = os.environ['A2SERVER_SCRIPT_URL']
|
||||
@ -40,6 +73,16 @@ if 'A2SERVER_SCRIPT_URL' in os.environ:
|
||||
else:
|
||||
scriptURL = 'http://appleii.ivanx.com/a2server'
|
||||
|
||||
def sha1file (filename, blocksize=65536):
|
||||
f = open(filename, "rb")
|
||||
digest = hashlib.sha1()
|
||||
buf = f.read(blocksize)
|
||||
while len(buf) > 0:
|
||||
digest.update(buf)
|
||||
buf = f.read(blocksize)
|
||||
f.close()
|
||||
return digest.hexdigest()
|
||||
|
||||
def download_url(url, filename):
|
||||
html = urlrequest.urlopen(url)
|
||||
data = html.read()
|
||||
@ -51,13 +94,15 @@ def download_url(url, filename):
|
||||
# self-extracting disk image files. The Unarchiver's unar is able
|
||||
# to unwrap the MacBinary wrapper for us, but we have to extract the
|
||||
# disk image oursselves. Fortunately, it's uncompressed.
|
||||
def extract_800k_sea_bin(wrapper_name, image_name, sea_name = None):
|
||||
def extract_800k_sea_bin(wrapper_name, image_name, extract_dir, sea_name = None):
|
||||
# First we need to get rid of the MacBinary wrapper
|
||||
# FIXME: Can we learn to read MacBinary? I bet we can!
|
||||
cmdline = ['unar', '-q', '-k', 'skip', wrapper_name]
|
||||
if not os.path.isfile(wrapper_name):
|
||||
raise IOError('Archive file "' + wrapper_name + '" does not exist')
|
||||
cmdline = ['unar', '-q', '-o', extract_dir, '-k', 'skip', wrapper_name]
|
||||
ret = subprocess.call(cmdline)
|
||||
if ret != 0:
|
||||
raise IOError('unar returned with status ' + ret)
|
||||
raise IOError('unar returned with status %i' % (ret))
|
||||
|
||||
# MAYBE we can guess the name?
|
||||
if sea_name == None:
|
||||
@ -68,9 +113,14 @@ def extract_800k_sea_bin(wrapper_name, image_name, sea_name = None):
|
||||
'" doesn\'t end with .sea.bin')
|
||||
|
||||
# Do we have the right file?
|
||||
sea_name = os.path.join(extract_dir, sea_name)
|
||||
if not os.path.isfile(sea_name):
|
||||
raise IOError('Expected image archive "' + sea_name + '" does not exist')
|
||||
|
||||
# Cowardly refuse to overwrite image_name
|
||||
if os.path.exists(image_name):
|
||||
raise IOError('"' + image_name + '" already exists')
|
||||
|
||||
# The image starts 84 bytes in, and is exactly 819200 bytes long
|
||||
with open(sea_name, 'rb') as src, open(image_name, 'wb') as dst:
|
||||
src.seek(84)
|
||||
@ -82,8 +132,76 @@ def extract_800k_sea_bin(wrapper_name, image_name, sea_name = None):
|
||||
os.unlink(sea_name)
|
||||
os.unlink(wrapper_name)
|
||||
|
||||
def install_bootblocks(dir):
|
||||
pass
|
||||
def plist_keyvalue(plist_dict, key):
|
||||
if plist_dict.tag != 'dict':
|
||||
raise ValueError('not a plist dict')
|
||||
found = False
|
||||
for elem in plist_dict:
|
||||
if found:
|
||||
return elem
|
||||
if elem.tag == 'key' and elem.text == key:
|
||||
found = True
|
||||
return None
|
||||
|
||||
def find_mountpoint(xmlstr):
|
||||
plistroot = ET.fromstring(xmlstr)
|
||||
if plistroot.tag != 'plist':
|
||||
raise ValueError('xmlstr is not an XML-format plist')
|
||||
if plistroot[0].tag == 'dict':
|
||||
sys_entities = plist_keyvalue(plistroot[0], 'system-entities')
|
||||
if sys_entities.tag != 'array':
|
||||
raise ValueError('expected dict to contain an array')
|
||||
for child in sys_entities:
|
||||
if child.tag == 'dict':
|
||||
mountpoint = plist_keyvalue(child, 'mount-point')
|
||||
return mountpoint.text
|
||||
else:
|
||||
raise ValueError('system-entities should be an array of dict objects')
|
||||
else:
|
||||
raise ValueError('Root element is not a dict')
|
||||
|
||||
def install_bootblocks(installdir, installtype):
|
||||
if installtype not in ['unix', 'netatalk']:
|
||||
raise ValueError('Only basic UNIX and netatalk formats are supported for now')
|
||||
|
||||
if not os.path.isdir(installdir):
|
||||
os.makedirs(installdir, mode=0755)
|
||||
|
||||
bootblock_tmp = tempfile.mkdtemp(prefix = "tmp-a2sv-bootblocks.")
|
||||
|
||||
unpacked_a2boot = False
|
||||
for bootfile in a2boot_files:
|
||||
if installtype == 'unix':
|
||||
dst = bootfile['unix']
|
||||
elif installtype == 'netatalk':
|
||||
dst = bootfile['netatalk'] or bootfile['unix']
|
||||
|
||||
if not os.path.isfile(os.path.join(installdir, dst)):
|
||||
# We need to fetch it
|
||||
if not unpacked_a2boot:
|
||||
disk7_file = os.path.join(bootblock_tmp, disk7_filename)
|
||||
a2setup_img_name = os.path.join(bootblock_tmp, 'A2SETUP.img')
|
||||
download_url(disk7_url, disk7_file)
|
||||
# If file is wrapped as .sea.bin (always true for now)
|
||||
if True:
|
||||
sea_name = 'Disk 7 of 7-Apple II Setup.sea'
|
||||
extract_800k_sea_bin(disk7_file, a2setup_img_name, bootblock_tmp, sea_name)
|
||||
else:
|
||||
# Implement non .sea.bin version
|
||||
pass
|
||||
|
||||
if os.uname()[0] == 'Darwin':
|
||||
xmlstr = subprocess.check_output(['hdiutil', 'attach', '-plist',
|
||||
a2setup_img_name])
|
||||
mountpoint = find_mountpoint(xmlstr)
|
||||
print ('Contents of ' + mountpoint)
|
||||
os.system('ls -l "' + mountpoint + '"')
|
||||
os.system('hdiutil eject "' + mountpoint + '"')
|
||||
|
||||
unpacked_a2boot = True
|
||||
|
||||
os.unlink(a2setup_img_name)
|
||||
os.removedirs(bootblock_tmp)
|
||||
|
||||
def do_install():
|
||||
netboot_tmp = tempfile.mkdtemp(suffix = '.a2server-netboot')
|
||||
@ -166,8 +284,7 @@ if __name__ == '__main__':
|
||||
sys.exit(ret)
|
||||
"""
|
||||
|
||||
reply = stdin_input("""
|
||||
Do you want to set up A2SERVER to be able to boot Apple II
|
||||
computers over the network? [y] """)
|
||||
if reply.startswith('y') or reply.startswith('Y') or reply == '':
|
||||
do_install()
|
||||
install_bootblocks(os.path.join(os.getcwd(), 'a2boot'), 'unix')
|
||||
#reply = stdin_input("""\nDo you want to set up A2SERVER to be able to boot Apple II\ncomputers over the network? [y] """)
|
||||
#if reply.startswith('y') or reply.startswith('Y') or reply == '':
|
||||
# do_install()
|
||||
|
Loading…
Reference in New Issue
Block a user