mirror of
https://github.com/ksherlock/ample.git
synced 2026-01-23 07:16:02 +00:00
move python code into it's own directory.
This commit is contained in:
36
python/machines.py
Normal file
36
python/machines.py
Normal 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
38
python/mkdevices.py
Normal 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
174
python/mkmachines.py
Normal 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
82
python/mkmodels.py
Normal 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
126
python/plist.py
Normal 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
73
python/rom.py
Normal 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))
|
||||
Reference in New Issue
Block a user