tarted work on restoring Apple @e support.

This commit is contained in:
Storm Dragon
2026-04-28 19:20:12 -04:00
parent 1063f21c43
commit b6d77279f7
7 changed files with 707 additions and 0 deletions
@@ -79,6 +79,33 @@ download_with_fallback() {
return 1
}
internet_available() {
local targetUrl
local targetUrls=(
"https://www.gnu.org/"
"https://1.1.1.1/"
)
for targetUrl in "${targetUrls[@]}"; do
if curl -fsS --connect-timeout 5 --max-time 8 "$targetUrl" > /dev/null 2>&1; then
return 0
fi
done
return 1
}
require_internet_connection() {
local message="No internet connection detected. Configure a connection from System Settings, Internet Configuration, then try again."
if internet_available; then
return 0
fi
ui_msgbox "Game Installer" "Game Installer" "$message"
return 1
}
get_remote_file_size() {
local sourceUrl="$1"
local headerOutput
@@ -69,6 +69,8 @@ trap finish_logging EXIT
# shellcheck disable=SC1091
source "${scriptDir}/game_install_functions.sh"
require_internet_connection
speak "Installing ${gameName}"
# shellcheck source=/dev/null
source "${installerPath}"
+151
View File
@@ -7,9 +7,157 @@ import os
import sys
import time
import curses
import hashlib
import shutil
import subprocess
import tempfile
import zipfile
from pathlib import Path
import speechd # Python bindings for Speech Dispatcher
from stormux_speech_settings import load_speech_settings, save_speech_settings
APPLE2E_CID = "QmY9fUfpexakGuq6MGn3pSLpNJxy8jW7rU79GGywBSekHY"
APPLE2E_ZIP_NAME = "apple2e.zip"
APPLE2E_ZIP_SHA256 = "5632c11d86c173f9e2a686e31542c16aafd1cc9ef3b6a3bcb5ff44a19bdd1195"
APPLE2E_SUPPORT_DIR = Path.home() / ".local" / "games" / "apple2e"
APPLE2E_CACHE_DIR = Path.home() / ".cache" / "stormux"
APPLE2E_BOOT_DISK = "Echo II - Textalker DOS 3.3.dsk"
def speak_message(message, wait=False):
flag = "-Cw" if wait else "-C"
subprocess.run(["spd-say", flag, message], check=False)
def internet_available():
"""Return True when outbound HTTPS appears available."""
targets = ("https://www.gnu.org/", "https://1.1.1.1/")
for target in targets:
result = subprocess.run(
["curl", "-fsS", "--connect-timeout", "5", "--max-time", "8", target],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=False,
)
if result.returncode == 0:
return True
return False
def apple_support_url():
gateway = os.environ.get("ipfsGateway", "https://ipfs.stormux.org")
return f"{gateway}/ipfs/{APPLE2E_CID}?filename={APPLE2E_ZIP_NAME}"
def sha256_file(filePath):
digest = hashlib.sha256()
with open(filePath, "rb") as fileHandle:
for chunk in iter(lambda: fileHandle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def download_file(sourceUrl, outputPath):
subprocess.run(
[
"curl",
"--fail",
"--show-error",
"--location",
"--connect-timeout",
"20",
"--retry",
"3",
"--retry-delay",
"2",
"--output",
str(outputPath),
sourceUrl,
],
check=True,
)
def apple_support_installed(supportDir=APPLE2E_SUPPORT_DIR):
supportDir = Path(supportDir)
return (
(supportDir / APPLE2E_BOOT_DISK).is_file()
and (supportDir / "disks").is_dir()
and any((supportDir / "disks").glob("*.dsk"))
)
def validate_zip_member(memberName):
memberPath = Path(memberName)
return (
not memberPath.is_absolute()
and ".." not in memberPath.parts
and len(memberPath.parts) >= 1
and memberPath.parts[0] == "apple2e"
)
def extract_apple_support_archive(zipPath, supportDir):
supportDir = Path(supportDir)
gamesDir = supportDir.parent
gamesDir.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(zipPath) as archive:
memberNames = archive.namelist()
if not all(validate_zip_member(name) for name in memberNames):
raise RuntimeError("Apple 2e support archive contains unsafe paths.")
if f"apple2e/{APPLE2E_BOOT_DISK}" not in memberNames:
raise RuntimeError("Apple 2e support archive is missing the boot disk.")
if not any(name.startswith("apple2e/disks/") and name.lower().endswith(".dsk") for name in memberNames):
raise RuntimeError("Apple 2e support archive is missing disk images.")
tempRoot = Path(tempfile.mkdtemp(prefix="apple2e_extract_", dir=str(gamesDir)))
try:
archive.extractall(tempRoot)
extractedDir = tempRoot / "apple2e"
if supportDir.exists():
shutil.rmtree(supportDir)
shutil.move(str(extractedDir), str(supportDir))
(supportDir / ".stormux-installed").write_text("installed\n")
finally:
shutil.rmtree(tempRoot, ignore_errors=True)
def ensure_apple_support_files(
supportDir=APPLE2E_SUPPORT_DIR,
cacheDir=APPLE2E_CACHE_DIR,
speak=speak_message,
):
supportDir = Path(supportDir)
cacheDir = Path(cacheDir)
if apple_support_installed(supportDir):
return True
if not internet_available():
message = (
"No internet connection detected. Configure a connection from "
"System Settings, Internet Configuration, then open Apple 2e again."
)
print(message)
speak(message, True)
return False
cacheDir.mkdir(parents=True, exist_ok=True)
zipPath = cacheDir / APPLE2E_ZIP_NAME
speak("Apple 2e support files are not installed. Downloading them now.", True)
download_file(apple_support_url(), zipPath)
if sha256_file(zipPath) != APPLE2E_ZIP_SHA256:
zipPath.unlink(missing_ok=True)
raise RuntimeError("Apple 2e support archive failed checksum verification.")
extract_apple_support_archive(zipPath, supportDir)
speak("Apple 2e support files installed.", True)
return True
class VoicedDiskMenu:
def __init__(self, title="Apple 2e Disk Menu"):
self.title = title
@@ -363,6 +511,9 @@ class VoicedDiskMenu:
# Run the menu
if __name__ == "__main__":
if not ensure_apple_support_files():
sys.exit(1)
# Create the menu
menu = VoicedDiskMenu()
@@ -33,6 +33,31 @@ progress_beep() {
fi
}
internet_available() {
local targetUrl
local targetUrls=(
"https://www.gnu.org/"
"https://1.1.1.1/"
)
for targetUrl in "${targetUrls[@]}"; do
if curl -fsS --connect-timeout 5 --max-time 8 "$targetUrl" > /dev/null 2>&1; then
return 0
fi
done
return 1
}
require_internet_connection() {
if internet_available; then
return 0
fi
speak "No internet connection detected. Configure a connection from System Settings, Internet Configuration, then try again."
return 1
}
# Download with progress feedback
download_with_progress() {
local url="$1"
@@ -192,6 +217,8 @@ auto_install() {
return 0
fi
require_internet_connection || return 1
# Download and install
local temp_zip="/tmp/${directory}_download.zip"
if download_with_progress "$url" "$temp_zip" "$name"; then
@@ -233,6 +260,8 @@ auto_install() {
return 0
fi
require_internet_connection || return 1
# Install package
if install_system_package "$package" "$name"; then
speak "Installation complete"
+410
View File
@@ -0,0 +1,410 @@
#
# CORE CONFIGURATION OPTIONS
#
readconfig 1
writeconfig 0
#
# CORE SEARCH PATH OPTIONS
#
homepath .
rompath $HOME/.mame/roms
hashpath $HOME/.mame/hash;/usr/lib/mame/hash
samplepath $HOME/.mame/samples
artpath $HOME/.mame/artwork;/usr/lib/mame/artwork
ctrlrpath $HOME/.mame/ctrlr;/usr/lib/mame/ctrlr
inipath $HOME/.mame/ini
fontpath .
cheatpath cheat
crosshairpath crosshair
pluginspath /usr/lib/mame/plugins
languagepath $HOME/.mame/language;/usr/lib/mame/language
swpath software
#
# CORE OUTPUT DIRECTORY OPTIONS
#
cfg_directory $HOME/.mame/cfg
nvram_directory $HOME/.mame/nvram
input_directory $HOME/.mame/inp
state_directory $HOME/.mame/sta
snapshot_directory $HOME/.mame/snap
diff_directory $HOME/.mame/diff
comment_directory $HOME/.mame/comments
share_directory share
#
# CORE STATE/PLAYBACK OPTIONS
#
state
autosave 0
rewind 0
rewind_capacity 100
playback
record
exit_after_playback 0
mngwrite
aviwrite
wavwrite
snapname %g/%i
snapsize auto
snapview auto
snapbilinear 1
statename %g
burnin 0
#
# CORE PERFORMANCE OPTIONS
#
autoframeskip 0
frameskip 0
seconds_to_run 0
throttle 1
sleep 1
speed 1.0
refreshspeed 0
lowlatency 0
#
# CORE RENDER OPTIONS
#
keepaspect 1
unevenstretch 1
unevenstretchx 0
unevenstretchy 0
autostretchxy 0
intoverscan 0
intscalex 0
intscaley 0
#
# CORE ROTATION OPTIONS
#
rotate 1
ror 0
rol 0
autoror 0
autorol 0
flipx 0
flipy 0
#
# CORE ARTWORK OPTIONS
#
artwork_crop 0
fallback_artwork
override_artwork
#
# CORE SCREEN OPTIONS
#
brightness 1.0
contrast 1.0
gamma 1.0
pause_brightness 0.65
effect none
#
# CORE VECTOR OPTIONS
#
beam_width_min 1.0
beam_width_max 1.0
beam_dot_size 1.0
beam_intensity_weight 0
flicker 0
#
# CORE SOUND OPTIONS
#
samplerate 48000
samples 1
volume 0
#
# CORE INPUT OPTIONS
#
coin_lockout 1
ctrlr
mouse 0
joystick 1
lightgun 0
multikeyboard 0
multimouse 0
steadykey 0
ui_active 0
offscreen_reload 0
joystick_map auto
joystick_deadzone 0.15
joystick_saturation 0.85
joystick_threshold 0.3
natural 0
joystick_contradictory 0
coin_impulse 0
#
# CORE INPUT AUTOMATIC ENABLE OPTIONS
#
paddle_device keyboard
adstick_device keyboard
pedal_device keyboard
dial_device keyboard
trackball_device keyboard
lightgun_device keyboard
positional_device keyboard
mouse_device mouse
#
# CORE DEBUGGING OPTIONS
#
verbose 0
log 0
oslog 0
debug 0
update_in_pause 0
debugscript
debuglog 0
#
# CORE COMM OPTIONS
#
comm_localhost 0.0.0.0
comm_localport 15112
comm_remotehost 127.0.0.1
comm_remoteport 15112
comm_framesync 0
#
# CORE MISC OPTIONS
#
drc 1
drc_use_c 0
drc_log_uml 0
drc_log_native 0
bios
cheat 0
skip_gameinfo 0
uifont default
ui cabinet
ramsize
confirm_quit 0
ui_mouse 1
language
nvram_save 1
#
# SCRIPTING OPTIONS
#
autoboot_command
autoboot_delay 0
autoboot_script
console 0
plugins 1
plugin
noplugin
#
# HTTP SERVER OPTIONS
#
http 0
http_port 8080
http_root web
#
# OSD INPUT MAPPING OPTIONS
#
uimodekey auto
controller_map none
background_input 0
#
# OSD FONT OPTIONS
#
uifontprovider auto
#
# OSD OUTPUT OPTIONS
#
output auto
#
# OSD INPUT OPTIONS
#
keyboardprovider auto
mouseprovider auto
lightgunprovider auto
joystickprovider auto
#
# OSD DEBUGGING OPTIONS
#
debugger auto
debugger_host localhost
debugger_port 23946
debugger_font auto
debugger_font_size 0
watchdog 0
#
# OSD PERFORMANCE OPTIONS
#
numprocessors auto
bench 0
#
# OSD VIDEO OPTIONS
#
video opengl
numscreens 1
window 0
maximize 1
waitvsync 0
syncrefresh 0
monitorprovider auto
#
# OSD PER-WINDOW VIDEO OPTIONS
#
screen auto
aspect auto
resolution auto
view auto
screen0 auto
aspect0 auto
resolution0 auto
view0 auto
screen1 auto
aspect1 auto
resolution1 auto
view1 auto
screen2 auto
aspect2 auto
resolution2 auto
view2 auto
screen3 auto
aspect3 auto
resolution3 auto
view3 auto
#
# OSD FULL SCREEN OPTIONS
#
switchres 0
#
# OSD ACCELERATED VIDEO OPTIONS
#
filter 1
prescale 1
#
# OpenGL-SPECIFIC OPTIONS
#
gl_forcepow2texture 0
gl_notexturerect 0
gl_vbo 1
gl_pbo 1
gl_glsl 0
gl_glsl_filter 1
glsl_shader_mame0 none
glsl_shader_mame1 none
glsl_shader_mame2 none
glsl_shader_mame3 none
glsl_shader_mame4 none
glsl_shader_mame5 none
glsl_shader_mame6 none
glsl_shader_mame7 none
glsl_shader_mame8 none
glsl_shader_mame9 none
glsl_shader_screen0 none
glsl_shader_screen1 none
glsl_shader_screen2 none
glsl_shader_screen3 none
glsl_shader_screen4 none
glsl_shader_screen5 none
glsl_shader_screen6 none
glsl_shader_screen7 none
glsl_shader_screen8 none
glsl_shader_screen9 none
#
# OSD SOUND OPTIONS
#
sound auto
audio_latency 0.0
#
# OSD MIDI OPTIONS
#
midiprovider auto
#
# OSD EMULATED NETWORKING OPTIONS
#
networkprovider auto
#
# BGFX POST-PROCESSING OPTIONS
#
bgfx_path $HOME/.mame/bgfx;/usr/lib/mame/bgfx
bgfx_backend auto
bgfx_debug 0
bgfx_screen_chains
bgfx_shadow_mask slot-mask.png
bgfx_lut lut-default.png
bgfx_avi_name auto
#
# SDL PERFORMANCE OPTIONS
#
sdlvideofps 0
#
# SDL VIDEO OPTIONS
#
centerh 1
centerv 1
scalemode none
#
# SDL FULL SCREEN OPTIONS
#
useallheads 0
attach_window
#
# SDL KEYBOARD MAPPING
#
keymap 0
keymap_file keymap.dat
#
# SDL INPUT OPTIONS
#
enable_touch 0
sixaxis 0
dual_lightgun 0
#
# SDL LIGHTGUN MAPPING
#
lightgun_index1 auto
lightgun_index2 auto
lightgun_index3 auto
lightgun_index4 auto
lightgun_index5 auto
lightgun_index6 auto
lightgun_index7 auto
lightgun_index8 auto
#
# SDL LOW-LEVEL DRIVER OPTIONS
#
videodriver auto
renderdriver auto
audiodriver auto
gl_lib auto
#
# FRONTEND COMMAND OPTIONS
#
dtd 1
+17
View File
@@ -0,0 +1,17 @@
#
# PLUGINS OPTIONS
#
console 0
cheat 0
inputmacro 0
timecode 0
discord 0
hiscore 0
data 1
layout 1
cheatfind 0
autofire 0
dummy 0
timer 0
gdbstub 0
portname 0
+71
View File
@@ -0,0 +1,71 @@
#
# UI SEARCH PATH OPTIONS
#
historypath history;dats;.
categorypath folders
cabinets_directory cabinets;cabdevs
cpanels_directory cpanel
pcbs_directory pcb
flyers_directory flyers
titles_directory titles
ends_directory ends
marquees_directory marquees
artwork_preview_directory "artwork preview;artpreview"
bosses_directory bosses
logos_directory logo
scores_directory scores
versus_directory versus
gameover_directory gameover
howto_directory howto
select_directory select
icons_directory icons
covers_directory covers
ui_path ui
#
# UI MISC OPTIONS
#
system_names
skip_warnings 0
unthrottle_mute 0
#
# UI OPTIONS
#
infos_text_size 0.75
font_rows 30
ui_border_color ffffffff
ui_bg_color ef101030
ui_clone_color ff808080
ui_dipsw_color ffffff00
ui_gfxviewer_color ef101030
ui_mousedown_bg_color b0606000
ui_mousedown_color ffffff80
ui_mouseover_bg_color 70404000
ui_mouseover_color ffffff80
ui_selected_bg_color ef808000
ui_selected_color ffffff00
ui_slider_color ffffffff
ui_subitem_color ffffffff
ui_text_bg_color ef000000
ui_text_color ffffffff
ui_unavail_color ff404040
#
# SYSTEM/SOFTWARE SELECTION MENU OPTIONS
#
hide_main_panel 0
use_background 1
skip_biosmenu 0
skip_partsmenu 0
remember_last 1
last_used_machine
last_used_filter
system_right_panel image
software_right_panel image
system_right_image snap
software_right_image snap
enlarge_snaps 1
forced4x3 1
info_audit_enabled 0
hide_romless 1