import argparse import subprocess from plist import to_plist import xml.etree.ElementTree as ET from machines import MACHINES, SLOTS # macintosh errata: # maclc has scsi:1 - scsi:7 and lcpds slots, but none are currently configurable. # maciifx has scsi:1 - scsi:7 (not configurable) # maciifx has "nb9", "nba" - "nbe" as configurable nubus slots # # w/ nubus, can specify video card which may have different resolution. # # Inside Macintosh: Devices chapter 2 explains the Nubus slot name scheme # (essentially $01-$0e; $0 and $f are reserved) # # don't allow these for now. generally because they add floppy/hard drives # but don't work with normal disk images DISABLED = set(( 'pcxport', 'hsscsi', # doesn't work 'corvus', # these apparently don't use normal disk images. 'zipdrive', 'focusdrive', 'vulcan', 'vulcangold', 'vulcaniie', 'sider', 'xebec', 'sider1', 'sider2', )) def find_machine_media(parent): # look for relevant device nodes. If the tag contains a slot, skip since it's # not built in. Except the Apple3, where the floppy drives are actually slots 0/1/2/3/4 # # apple1 has a "snapshot" device. not currently supported. # # in the //c (but not //c+) the floppy drives are in slot 6 which doesn't otherwise exist. # # no machines have built-in hard drives. mname = parent.get("name") remap = { "cassette": "cass", "apple1_cass": "cass", "apple2_cass": "cass", "floppy_5_25": "flop_5_25", "floppy_3_5": "flop_3_5", # mac "scsi_hdd": "hard", "cdrom": "cdrm", } media = {} for x in parent.findall("./device"): tag = x.get("tag") typ = x.get("type") intf = x.get("interface") if intf == None: intf = typ # cassette has no interface. # print(" ",intf) slot = None if ':' in tag: tt = tag.split(':') if len(tt) >= 3: slot = tt[0] # exclude: # apple1 - tag="exp:cassette:cassette" # apple2 - tag="sl6:diskiing:0:525" - # include: # apple2c - tag="sl6:0:525" - . # apple3 - tag="0:525" - # format slot name : slotoption name : machine->device type name if mname == "apple2c" and slot == "sl6": slot = None # hack for now - these are scsi:1-7 slots but slot option isn't adjustable. if mname[0:3] == "mac" and slot == "scsi": slot = None if slot: continue # skip slot devices -- they'll be handled as part of the device. #if ":" in tag and tag[0] not in "0123456789": continue if intf in remap: name = remap[intf] media[name] = media.get(name, 0) + 1 return media def find_media(parent, include_slots=False): # not strictly correct since they could have different extensions. # built-in devices (cassette_image, floppy_sonny, floppy_apple) and default slots # have a top-level node which includes the name and extensions. # slot media generally has a node inline (corvus, diskii) # -or- # slot/slotoption default="yes", devname is a machine with a device node. # diskiing is an exception, naturally. # this ignores the above. remap_dev = { "cassette_image": "cass", "floppy_apple": "flop_5_25", "harddisk_image": "hard", "floppy_sonny": "flop_3_5", } remap_slot = { "harddisk": "hard", "hdd": "hard", "cdrom": "cdrm", "525": "flop_5_25", } media = {} # floppies for x in parent.findall("./device_ref"): name = x.get("name") if name in remap_dev: name = remap_dev[name] media[name] = media.get(name, 0) + 1 # ata_slot (vulcan, cffa, zip, etc) needs to check slot to see if default. # nscsi_connector (a2scsi, a2hsscsi) needs to check slot to see if default. # a2scsi - has 6 slots each with an option to be a cdrom or hard disk. # default is 1 cdrom, 1 hard disk. # could use -sl6:scsi:scsibus:6 harddisk|cdrom to explicitly set them. # this would, of course, screw up the device counting logic. # focus/vulcan can also enable a second harddisk/cdrom. if not include_slots: return media for x in parent.findall("./slot/slotoption"): if x.get("default") != "yes": continue name = x.get("name") if name in remap_slot: name = remap_slot[name] media[name] = media.get(name, 0) + 1 # special case for the pc transporter. not in the xml but it adds 2 5.25" floppies # n.b. - floppies are 5.25" 360k or 180k. not bootable, not usable from prodos # without special prodos file or loading driver into pc transporter ram. if parent.get("name") == "pcxport": media.get["flop_5_25"] = media.get("flop_5_25", 0) + 2 if not media: return None return media devices = {} p = argparse.ArgumentParser() p.add_argument('machine', nargs="*") args = p.parse_args() machines = args.machine if not machines: machines = MACHINES for m in machines: print(m) env = {'DYLD_FALLBACK_FRAMEWORK_PATH': '../embedded'} st = subprocess.run(["../embedded/mame64", m, "-listxml"], capture_output=True, env=env) if st.returncode != 0: print("mame error: {}".format(m)) exit(1) data = { } xml = st.stdout root = ET.fromstring(xml) path = 'machine[@name="{}"]'.format(m) machine = root.find(path) data["value"] = m data["description"] = machine.find("description").text tmp = [ { "intValue": int(x.text), "description": x.get("name"), "value": x.get("name"), "default": x.get("default") == "yes" } for x in machine.findall('ramoption') ] # sort and add empty starting entry. tmp.sort(key=lambda x: x["intValue"]) # tmp.insert(0, {"value": 0, "default": False, "description": "" }) data["ram"] = tmp data["media"] = find_machine_media(machine) # node = machine.find('display[@tag="screen"]') node = machine.find('./display') hscale = 2 if m[0:3] == "mac": hscale = 1 data["resolution"] = [int(node.get("width")), int(node.get("height")) * hscale] mm = {} for x in root.findall("machine[@isdevice='yes']"): name = x.get("name") mm[name] = x # .find("description").text # also need to find media... # print(mm) # ss = {} for s in SLOTS: path = 'slot[@name="{}"]/slotoption'.format(s) nodes = machine.findall(path) if not nodes: continue tmp = [] has_default = False for x in nodes: name = x.get("name") devname = x.get("devname") desc = mm[devname].find("description").text default = x.get("default") == "yes" disabled = name in DISABLED d = { "value": name, "description": desc, "default": default } if disabled: d["disabled"] = True else: media = find_media(mm[devname], True) if media: d["media"] = media tmp.append(d) has_default |= default tmp.sort(key=lambda x: x["description"].upper() ) tmp.insert(0, {"value": "", "description": "—None—", "default": not has_default}) data[s] = tmp path = "../Ample/Resources/{}.plist".format(m) with open(path, "w") as f: f.write(to_plist(data))