import lvgl as lv import os from mpos import Activity, TaskManager, sdcard class Main(Activity): romdir = "/roms" doomdir = romdir + "/doom" retrogodir = "/retro-go" configdir = retrogodir + "/config" bootfile = configdir + "/boot.json" partition_label = "prboom-go" mountpoint_sdcard = "/sdcard" esp32_partition_type_ota_0 = 16 #partition_label = "retro-core" # Widgets: status_label = None wadlist = None bootfile_prefix = "" bootfile_to_write = "" def onCreate(self): screen = lv.obj() screen.set_style_pad_all(15, lv.PART.MAIN) # Create title label title_label = lv.label(screen) title_label.set_text("Choose your DOOM:") title_label.align(lv.ALIGN.TOP_LEFT, 0, 0) # Create list widget for WAD files self.wadlist = lv.list(screen) self.wadlist.set_size(lv.pct(100), lv.pct(70)) self.wadlist.center() # Create status label for messages self.status_label = lv.label(screen) self.status_label.set_width(lv.pct(90)) self.status_label.set_long_mode(lv.label.LONG_MODE.WRAP) self.status_label.align(lv.ALIGN.BOTTOM_LEFT, 0, 0) # Set default green color for status label self.status_label.set_style_text_color(lv.color_hex(0x00FF00), lv.PART.MAIN) self.setContentView(screen) def onResume(self, screen): # Try to mount the SD card and if successful, use it, as retro-go can only use one or the other: self.bootfile_prefix = "" mounted_sdcard = sdcard.mount_with_optional_format(self.mountpoint_sdcard) if mounted_sdcard: print("sdcard is mounted, configuring it...") self.bootfile_prefix = self.mountpoint_sdcard self.bootfile_to_write = self.bootfile_prefix + self.bootfile print(f"writing to {self.bootfile_to_write}") # Scan for WAD files and populate the list self.refresh_wad_list() def scan_wad_files(self, directory): """Scan a directory for .wad and .zip files""" wad_files = [] try: for filename in os.listdir(directory): if filename.lower().endswith(('.wad', '.zip')): wad_files.append(filename) # Sort the list for consistent ordering wad_files.sort() print(f"Found {len(wad_files)} WAD files in {directory}: {wad_files}") except OSError as e: print(f"Directory does not exist or cannot be read: {directory}") except Exception as e: print(f"Error scanning directory {directory}: {e}") return wad_files def get_file_size_warning(self, filepath): """Get file size warning suffix if file is too small or empty""" try: size = os.stat(filepath)[6] # Get file size if size == 0: return " (EMPTY FILE)" # Red elif size < 80 * 1024: # 80KB return " (TOO SMALL)" # Orange except Exception as e: print(f"Error checking file size for {filepath}: {e}") return "" def refresh_wad_list(self): """Scan for WAD files and populate the list""" self.status_label.set_text(f"Listing files in: {self.bootfile_prefix + self.doomdir}") print("refresh_wad_list: Clearing current list") self.wadlist.clean() # Scan internal storage or SD card all_wads = self.scan_wad_files(self.bootfile_prefix + self.doomdir) all_wads.sort() if len(all_wads) == 0: self.status_label.set_text(f"No .wad or .zip files found in {self.doomdir}") print("No WAD files found") return # Populate list with WAD files print(f"refresh_wad_list: Populating list with {len(all_wads)} WAD files") self.status_label.set_text(f"Listed files in: {self.bootfile_prefix + self.doomdir}") for wad_file in all_wads: # Get file size warning if applicable warning = self.get_file_size_warning(self.bootfile_prefix + self.doomdir + '/' + wad_file) button_text = wad_file + warning button = self.wadlist.add_button(None, button_text) button.add_event_cb(lambda e, p=self.doomdir + '/' + wad_file: TaskManager.create_task(self.start_wad(self.bootfile_prefix, self.bootfile_to_write, p)), lv.EVENT.CLICKED, None) # If only one WAD file, auto-start it if len(all_wads) == 1: print(f"refresh_wad_list: Only one WAD file found, auto-starting: {all_wads[0]}") TaskManager.create_task(self.start_wad(self.bootfile_prefix, self.bootfile_to_write, self.doomdir + '/' + all_wads[0])) def mkdir(self, dirname): # Would be better to only create it if it doesn't exist try: os.mkdir(dirname) except Exception as e: # Not really useful to show this in the UI, as it's usually just an "already exists" error: print(f"Info: could not create directory {dirname} because: {e}") async def start_wad(self, bootfile_prefix, bootfile_to_write, wadfile): self.status_label.set_text(f"Launching Doom with file: {bootfile_prefix}{wadfile}") await TaskManager.sleep(1) # Give the user a minimal amount of time to read the filename # Create these folders, in case the user wants to add doom later: self.mkdir(bootfile_prefix + self.romdir) self.mkdir(bootfile_prefix + self.doomdir) # Create structure to place bootfile: self.mkdir(bootfile_prefix + self.retrogodir) self.mkdir(bootfile_prefix + self.configdir) try: import json # Would be better to only write this if it differs from what's already there: fd = open(bootfile_to_write, 'w') bootconfig = { "BootName": "doom", "BootArgs": f"/sd{wadfile}", "BootSlot": -1, "BootFlags": 0 } json.dump(bootconfig, fd) fd.close() except Exception as e: self.status_label.set_text(f"ERROR: could not write config file: {e}") return results = [] try: from esp32 import Partition results = Partition.find(label=self.partition_label) except Exception as e: self.status_label.set_text(f"ERROR: could not search for internal partition with label {self.partition_label}, unable to start: {e}") return if len(results) < 1: self.status_label.set_text(f"ERROR: could not find internal partition with label {self.partition_label}, unable to start") return partition = results[0] try: partition.set_boot() except Exception as e: print(f"ERROR: could not set partition {partition} as boot, it probably doesn't contain a valid program: {e}") try: import vfs vfs.umount('/') except Exception as e: print(f"Warning: could not unmount internal filesystem from /: {e}") # Write the currently booted OTA partition number to NVS, so that retro-go's apps know where to go back to: try: from esp32 import NVS nvs = NVS('fri3d.sys') boot_partition = nvs.get_i32('boot_partition') print(f"boot_partition in fri3d.sys of NVS: {boot_partition}") running_partition = Partition(Partition.RUNNING) running_partition_nr = running_partition.info()[1] - self.esp32_partition_type_ota_0 print(f"running_partition_nr: {running_partition_nr}") if running_partition_nr != boot_partition: print(f"setting boot_partition in fri3d.sys of NVS to {running_partition_nr}") nvs.set_i32('boot_partition', running_partition_nr) else: print("No need to update boot_partition") except Exception as e: print(f"Warning: could not write currently booted partition to boot_partition in fri3d.sys of NVS: {e}") try: import machine machine.reset() except Exception as e: print(f"Warning: could not restart machine: {e}")