diff --git a/home/stormux/.local/.functions/download.sh b/home/stormux/.local/.functions/download.sh index 75d9c30..5a339ae 100755 --- a/home/stormux/.local/.functions/download.sh +++ b/home/stormux/.local/.functions/download.sh @@ -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 diff --git a/home/stormux/.local/.functions/install_game.sh b/home/stormux/.local/.functions/install_game.sh index c69c00e..cf48f8c 100755 --- a/home/stormux/.local/.functions/install_game.sh +++ b/home/stormux/.local/.functions/install_game.sh @@ -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}" diff --git a/home/stormux/.local/bin/apple_2e.py b/home/stormux/.local/bin/apple_2e.py index 189dc99..4019064 100755 --- a/home/stormux/.local/bin/apple_2e.py +++ b/home/stormux/.local/bin/apple_2e.py @@ -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() diff --git a/home/stormux/.local/bin/stormux_install_helper.sh b/home/stormux/.local/bin/stormux_install_helper.sh index fb3641d..2cff7c2 100755 --- a/home/stormux/.local/bin/stormux_install_helper.sh +++ b/home/stormux/.local/bin/stormux_install_helper.sh @@ -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" diff --git a/home/stormux/.mame/mame.ini b/home/stormux/.mame/mame.ini new file mode 100644 index 0000000..fc5a4a3 --- /dev/null +++ b/home/stormux/.mame/mame.ini @@ -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 diff --git a/home/stormux/.mame/plugin.ini b/home/stormux/.mame/plugin.ini new file mode 100644 index 0000000..2fe29ed --- /dev/null +++ b/home/stormux/.mame/plugin.ini @@ -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 diff --git a/home/stormux/.mame/ui.ini b/home/stormux/.mame/ui.ini new file mode 100644 index 0000000..9c2cbf1 --- /dev/null +++ b/home/stormux/.mame/ui.ini @@ -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