move python code into it's own directory.

This commit is contained in:
Kelvin Sherlock
2020-09-02 22:06:41 -04:00
parent fe7c71e7c7
commit b262c8bf2f
6 changed files with 87 additions and 8 deletions

36
python/machines.py Normal file
View File

@@ -0,0 +1,36 @@
MACHINES = (
"apple1",
"apple2", "apple2p", "apple2jp",
"apple3",
"apple2e", "apple2ees", "apple2euk", "apple2ep",
"apple2ee", "apple2eeuk", "apple2eefr",
"apple2gs", "apple2gsr0", "apple2gsr1",
"apple2c", "apple2cp",
# laser family
"laser128", "laser2c", "las128ex", "las128e2",
# IIe clones
"mprof3", "prav8c", "spectred",
# II clones
"ace100", "agat7", "agat9", "albert",
"am100", "am64", "basis108", "craft2p",
"dodo", "elppa", "hkc8800a", "ivelultr",
"maxxi", "microeng", "prav82", "prav8m",
"space84", "uniap2en", "uniap2pt", "uniap2ti",
"zijini",
# China Education Computer
"cec2000", "cece", "cecg", "ceci", "cecm",
)
SLOTS = (
"sl0", "sl1", "sl2", "sl3",
"sl4", "sl5", "sl6", "sl7",
"exp", "aux",
"rs232",
"gameio",
"printer",
"modem"
)

38
python/mkdevices.py Normal file
View File

@@ -0,0 +1,38 @@
import subprocess
from plist import to_plist
import xml.etree.ElementTree as ET
from machines import MACHINES
devices = {}
for m in MACHINES:
st = subprocess.run(["mame", m, "-listxml"], capture_output=True)
if st.returncode != 0:
print("mame error: {}".format(m))
exit(1)
xml = st.stdout
root = ET.fromstring(xml)
nodes = root.findall("machine[@isdevice='yes']")
for d in nodes:
name = d.get("name") # devname
desc = d.find("description").text
tmp = {
"Name": name,
"Description": desc
}
devices[name] = tmp
with open("../Ample/Resources/devices.plist", "w") as f:
f.write(to_plist(devices))

174
python/mkmachines.py Normal file
View File

@@ -0,0 +1,174 @@
import subprocess
from plist import to_plist
import xml.etree.ElementTree as ET
from machines import MACHINES, SLOTS
# 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',
))
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 <device> node which includes the name and extensions.
# slot media generally has a <device> 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 = {}
for m in MACHINES:
print(m)
st = subprocess.run(["mame", m, "-listxml"], capture_output=True)
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 = [
{
"value": int(x.text),
"description": 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["value"])
# tmp.insert(0, {"value": 0, "default": False, "description": "" })
data["ram"] = tmp
data["media"] = find_media(machine)
# node = machine.find('display[@tag="screen"]')
node = machine.find('./display')
data["resolution"] = [int(node.get("width")), int(node.get("height")) * 2]
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))

82
python/mkmodels.py Normal file
View File

@@ -0,0 +1,82 @@
import subprocess
from plist import to_plist
import xml.etree.ElementTree as ET
from machines import MACHINES
import re
apple1_children = None
apple2_children = ["apple2", "apple2p", "apple2jp"]
apple3_children = None
apple2e_children = ["apple2e", "apple2ees", "apple2euk", "apple2ep", "apple2ee", "apple2eeuk", "apple2eefr"]
apple2c_children = ["apple2c", "apple2cp"]
apple2gs_children = ["apple2gsr0", "apple2gsr1", "apple2gs"]
laser_children = ["laser128", "laser2c", "las128ex", "las128e2"]
ii_clones_children = ["ace100", "agat7", "agat9", "albert",
"am100", "am64", "basis108", "craft2p",
"dodo", "elppa", "hkc8800a", "ivelultr",
"maxxi", "microeng", "prav82", "prav8m",
"space84", "uniap2en", "uniap2pt", "uniap2ti"]
iie_clones_children = ["mprof3", "prav8c", "spectred", "zijini"]
cec_children = ["cec2000", "cece", "cecg", "ceci", "cecm"]
tree = [
("Apple I", "apple1", apple1_children),
("Apple ][", "apple2", apple2_children),
("Apple IIe", "apple2e", apple2e_children),
("Apple //c", "apple2c", apple2c_children),
("Apple IIgs", "apple2gs", apple2gs_children),
("Apple ///", "apple3", apple3_children),
("Laser", "laser128", laser_children),
("China Education Computer", None, cec_children)
("II Clones", None, ii_clones_children),
("IIe Clones", None, iie_clones_children),
]
st = subprocess.run(["mame", "-listfull", *MACHINES], check=True, capture_output=True, text=True)
# Name: Description:
# apple2gs "Apple IIgs (ROM03)"
# apple2gsr0 "Apple IIgs (ROM00)"
names = {}
t = st.stdout
lines = t.split("\n")
lines.pop(0)
for x in lines:
x = x.strip()
if x == "": continue
m = re.fullmatch(r"^([A-Za-z0-9_]+)\s+\"([^\"]+)\"$", x)
if not m:
print("hmmm....", x)
continue
name = m[1]
desc = m[2]
names[name] = desc
def make_children(clist):
global names
return [
{ "description": names[x], "value": x}
for x in clist
]
data = []
for x in tree:
desc, value, children = x
tmp = { "description": desc }
if value: tmp["value"] = value
if children: tmp["children"] = make_children(children)
data.append(tmp)
path = "../Ample/Resources/models.plist"
with open(path, "w") as f:
f.write(to_plist(data))

126
python/plist.py Normal file
View File

@@ -0,0 +1,126 @@
__all__ = ['to_plist']
from xml.sax.saxutils import escape
from base64 import b64encode
from datetime import date, datetime, timezone
_header = (
'<?xml version="1.0" encoding="UTF-8"?>\n'
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n'
'<plist version="1.0">\n'
)
_trailer = '</plist>\n'
INDENT = " "
def _bad(x, akku, indent=""):
raise ValueError("plist: bad type: {} ({})".format(type(x), x))
def _encode_array(x, akku, indent=""):
indent2 = indent + INDENT
akku.append(indent + "<array>\n")
for v in x:
_encoder.get(type(v), _bad)(v, akku, indent2)
akku.append(indent + "</array>\n")
def _encode_dict(x, akku, indent=""):
indent2 = indent + INDENT
akku.append(indent + "<dict>\n")
for k,v in x.items():
# key must be string?
if type(k) != str:
raise ValueError("plist: dictionary key must be string: {}: {}".format(type(k), k))
akku.append("{}<key>{}</key>\n".format(indent2, escape(k)))
_encoder.get(type(v), _bad)(v, akku, indent2)
akku.append(indent + "</dict>\n")
def _encode_bool(x, akku, indent=""):
if x: akku.append(indent + "<true/>\n")
else: akku.append(indent + "<false/>\n")
def _encode_integer(x, akku, indent=""):
akku.append("{}<integer>{}</integer>\n".format(indent, x))
def _encode_real(x, akku, indent=""):
akku.append("{}<real>{}</real>\n".format(indent, x))
def _encode_string(x, akku, indent=""):
akku.append("{}<string>{}</string>\n".format(indent, escape(x)))
# data is YYYY-MM-DD T HH:MM:SS Z
def _encode_date(x, akku, indent=""):
s = x.strftime('%Y-%m-%d')
akku.append("{}<date>{}</date>\n".format(indent, s))
def _encode_datetime(x, akku, indent=""):
# if not x.tzinfo
# raise ValueError("plist: datetime must have tzinfo: {}".format(x))
# if x.tzinfo.utcoffset(x) == None:
# raise ValueError("plist: datetime must have utc offset: {}".format(x))
utc = x.astimezone(timezone.utc)
s = utc.strftime('%Y-%m-%dT%H:%M:%SZ')
akku.append("{}<date>{}</date>\n".format(indent, s))
def _encode_data(x, akku, indent=""):
# data is base64 encoded
CHUNKSIZE = 32
if len(x) < CHUNKSIZE:
akku.append("{}<data>{}</data>\n".format(indent, b64encode(x).encode('ascii')))
return
indent2 = indent + INDENT
akku.append(indent + "<data>\n")
for i in range(0, len(x), CHUNKSIZE):
akku.append(
"{}{}\n".format(
indent2,
b64encode(x[i:i+CHUNKSIZE]).encode('ascii')
)
)
akku.append(indent + "</data>\n")
# data, data not yet supported.
_encoder = {
str: _encode_string,
float: _encode_real,
int: _encode_integer,
bool: _encode_bool,
tuple: _encode_array,
list: _encode_array,
dict: _encode_dict,
bytes: _encode_data,
bytearray: _encode_data,
date: _encode_date,
datetime: _encode_datetime,
}
def to_plist(x):
akku = []
akku.append(_header)
_encoder.get(type(x), _bad)(x, akku, INDENT)
akku.append(_trailer)
return ''.join(akku)

73
python/rom.py Normal file
View File

@@ -0,0 +1,73 @@
from plist import to_plist
ROMS = """
a1cass
a2aevm80
a2ap16
a2ap16a
a2aplcrd
a2cffa02
a2cffa2
a2corvus
a2diskii
a2diskiing
a2focdrv
a2hsscsi
a2iwm
a2memexp
a2mouse
a2pic
a2ramfac
a2scsi
a2ssc
a2surance
a2swyft
a2thunpl
a2tmstho
a2twarp
a2ultrme
a2ulttrm
a2vidtrm
a2vtc1
a2vulcan
a2vulgld
a2vuliie
a2zipdrv
a3fdc
apple1
apple2
apple2c
apple2e
apple2gs
apple3
cec2000
cece
cecg
ceci
cecm
cga
cmsscsi
d2fdc
diskii13
keytronic_pc3270
m68705p3
votrax
zijini
""".splitlines()
#
# others
# mprof3
# spectred
# tk3000
# prav8c
#
data = {}
data["source"] = "https://archive.org/download/mame0224_rom"
data["type"] = "7z"
data["version"] = "0.224"
data["roms"] = ROMS
# print(ROMS)
with open("../Ample/Resources/roms.plist", "w") as f:
f.write(to_plist(data))