feat: verify functions handle flat and nested ZIPs

This commit is contained in:
Abdessamad Derraz
2026-04-03 11:15:32 +02:00
parent 48d185dd7d
commit 76a3543672

View File

@@ -790,15 +790,14 @@ def _build_readme(
" 1. Find your RetroArch system directory:\n" " 1. Find your RetroArch system directory:\n"
" - RetroArch > Settings > Directory > System/BIOS\n" " - RetroArch > Settings > Directory > System/BIOS\n"
" - Default: retroarch/system/\n" " - Default: retroarch/system/\n"
' 2. Open the "system" folder from this archive\n' " 2. Extract all files from this archive directly into your system directory\n"
" 3. Copy ALL contents into your system directory\n" " 3. Overwrite if asked\n\n"
" 4. Overwrite if asked\n\n"
" Option C: Manual (handheld / SD card)\n" " Option C: Manual (handheld / SD card)\n"
" -------------------------------------\n" " -------------------------------------\n"
" Anbernic, Retroid, Miyoo, Trimui, etc.:\n" " Anbernic, Retroid, Miyoo, Trimui, etc.:\n"
" 1. Connect your SD card to your PC\n" " 1. Connect your SD card to your PC\n"
" 2. Find the BIOS folder (usually BIOS/ or system/)\n" " 2. Find the BIOS folder (usually BIOS/ or system/)\n"
' 3. Copy ALL contents of "system" from this archive\n' " 3. Extract all files from this archive directly into that folder\n"
" 4. Eject SD card and reboot your device\n\n" " 4. Eject SD card and reboot your device\n\n"
" Common paths by device:\n" " Common paths by device:\n"
" Anbernic (ArkOS/JELOS): BIOS/\n" " Anbernic (ArkOS/JELOS): BIOS/\n"
@@ -817,14 +816,13 @@ def _build_readme(
" 1. On your PC, open the Batocera network share:\n" " 1. On your PC, open the Batocera network share:\n"
" - Windows: \\\\BATOCERA\\share\\bios\\\n" " - Windows: \\\\BATOCERA\\share\\bios\\\n"
" - Mac/Linux: smb://batocera/share/bios/\n" " - Mac/Linux: smb://batocera/share/bios/\n"
' 2. Open the "bios" folder from this archive\n' " 2. Extract all files from this archive directly into the share\n"
" 3. Copy ALL contents into the share\n" " 3. Overwrite if asked\n\n"
" 4. Overwrite if asked\n\n"
" Option C: Manual (SD card)\n" " Option C: Manual (SD card)\n"
" --------------------------\n" " --------------------------\n"
" 1. Put the SD card in your PC\n" " 1. Put the SD card in your PC\n"
" 2. Navigate to /userdata/bios/ on the SHARE partition\n" " 2. Navigate to /userdata/bios/ on the SHARE partition\n"
' 3. Copy ALL contents of "bios" from this archive\n\n' " 3. Extract all files from this archive directly into that folder\n\n"
" NOTE: Dreamcast flash memory is named dc_nvmem.bin\n" " NOTE: Dreamcast flash memory is named dc_nvmem.bin\n"
" (if your setup asks for dc_flash.bin, same file).\n\n" " (if your setup asks for dc_flash.bin, same file).\n\n"
), ),
@@ -838,13 +836,12 @@ def _build_readme(
" 1. On your PC, open the Recalbox network share:\n" " 1. On your PC, open the Recalbox network share:\n"
" - Windows: \\\\RECALBOX\\share\\bios\\\n" " - Windows: \\\\RECALBOX\\share\\bios\\\n"
" - Mac/Linux: smb://recalbox/share/bios/\n" " - Mac/Linux: smb://recalbox/share/bios/\n"
' 2. Open the "bios" folder from this archive\n' " 2. Extract all files from this archive directly into the share\n\n"
" 3. Copy ALL contents into the share\n\n"
" Option C: Manual (SD card)\n" " Option C: Manual (SD card)\n"
" --------------------------\n" " --------------------------\n"
" 1. Put the SD card in your PC\n" " 1. Put the SD card in your PC\n"
" 2. Navigate to /recalbox/share/bios/\n" " 2. Navigate to /recalbox/share/bios/\n"
' 3. Copy ALL contents of "bios" from this archive\n\n' " 3. Extract all files from this archive directly into that folder\n\n"
), ),
"emudeck": ( "emudeck": (
"INSTALLATION GUIDE (Steam Deck / Linux)\n\n" "INSTALLATION GUIDE (Steam Deck / Linux)\n\n"
@@ -858,8 +855,7 @@ def _build_readme(
" ----------------\n" " ----------------\n"
" 1. Open Dolphin file manager\n" " 1. Open Dolphin file manager\n"
" 2. Navigate to ~/Emulation/bios/\n" " 2. Navigate to ~/Emulation/bios/\n"
' 3. Open the "bios" folder from this archive\n' " 3. Extract all files from this archive directly into ~/Emulation/bios/\n\n"
" 4. Copy ALL contents into ~/Emulation/bios/\n\n"
" STANDALONE EMULATORS (extra step)\n" " STANDALONE EMULATORS (extra step)\n"
" Switch and 3DS emulators need keys in specific folders:\n" " Switch and 3DS emulators need keys in specific folders:\n"
" prod.keys -> ~/.local/share/yuzu/keys/\n" " prod.keys -> ~/.local/share/yuzu/keys/\n"
@@ -878,11 +874,8 @@ def _build_readme(
" ----------------\n" " ----------------\n"
" 1. Open Dolphin file manager\n" " 1. Open Dolphin file manager\n"
" 2. Show hidden files (Ctrl+H)\n" " 2. Show hidden files (Ctrl+H)\n"
" 3. Navigate to ~/retrodeck/\n" " 3. Navigate to ~/retrodeck/bios/\n"
' 4. Open the "bios" folder from this archive\n' " 4. Extract all files from this archive directly into ~/retrodeck/bios/\n\n"
" 5. Copy ALL contents into ~/retrodeck/bios/\n"
' 6. If the archive contains a "roms" folder, copy\n'
" its contents into ~/retrodeck/roms/\n\n"
" NOTE: RetroDECK uses its own BIOS checker. After\n" " NOTE: RetroDECK uses its own BIOS checker. After\n"
" copying, open RetroDECK > Tools > BIOS Checker to\n" " copying, open RetroDECK > Tools > BIOS Checker to\n"
" verify everything is detected.\n\n" " verify everything is detected.\n\n"
@@ -898,9 +891,8 @@ def _build_readme(
" 1. Open your RetroBat installation folder\n" " 1. Open your RetroBat installation folder\n"
" 2. Navigate to the bios\\ subfolder\n" " 2. Navigate to the bios\\ subfolder\n"
" (default: C:\\RetroBat\\bios\\)\n" " (default: C:\\RetroBat\\bios\\)\n"
' 3. Open the "bios" folder from this archive\n' " 3. Extract all files from this archive directly into your bios\\ folder\n"
" 4. Copy ALL contents into your bios\\ folder\n" " 4. Overwrite if asked\n\n"
" 5. Overwrite if asked\n\n"
), ),
"bizhawk": ( "bizhawk": (
"INSTALLATION GUIDE\n\n" "INSTALLATION GUIDE\n\n"
@@ -908,16 +900,15 @@ def _build_readme(
" 2. Navigate to the Firmware subfolder:\n" " 2. Navigate to the Firmware subfolder:\n"
" - Windows: BizHawk\\Firmware\\\n" " - Windows: BizHawk\\Firmware\\\n"
" - Linux: ~/.config/BizHawk/Firmware/\n" " - Linux: ~/.config/BizHawk/Firmware/\n"
' 3. Open the "Firmware" folder from this archive\n' " 3. Extract all files from this archive directly into your Firmware folder\n"
" 4. Copy ALL contents into your Firmware folder\n" " 4. In BizHawk: Config > Paths > Firmware should\n"
" 5. In BizHawk: Config > Paths > Firmware should\n"
" point to this folder\n\n" " point to this folder\n\n"
), ),
"romm": ( "romm": (
"INSTALLATION GUIDE (RomM server)\n\n" "INSTALLATION GUIDE (RomM server)\n\n"
" 1. Locate your RomM library folder\n" " 1. Locate your RomM library folder\n"
" 2. Navigate to the bios/ subdirectory\n" " 2. Navigate to the bios/ subdirectory\n"
' 3. Copy ALL contents of "bios" from this archive\n' " 3. Extract all files from this archive directly into that folder\n"
" 4. Restart the RomM service to detect new files\n\n" " 4. Restart the RomM service to detect new files\n\n"
), ),
"retropie": ( "retropie": (
@@ -925,7 +916,7 @@ def _build_readme(
" Option A: Via network share\n" " Option A: Via network share\n"
" --------------------------\n" " --------------------------\n"
" 1. On your PC, open: \\\\RETROPIE\\bios\\\n" " 1. On your PC, open: \\\\RETROPIE\\bios\\\n"
' 2. Copy ALL contents of "BIOS" from this archive\n\n' " 2. Extract all files from this archive directly into that folder\n\n"
" Option B: Via SSH\n" " Option B: Via SSH\n"
" -----------------\n" " -----------------\n"
" 1. SSH into your Pi: ssh pi@retropie\n" " 1. SSH into your Pi: ssh pi@retropie\n"
@@ -934,7 +925,7 @@ def _build_readme(
" ---------------------\n" " ---------------------\n"
" 1. Put the SD card in your PC\n" " 1. Put the SD card in your PC\n"
" 2. Navigate to /home/pi/RetroPie/BIOS/\n" " 2. Navigate to /home/pi/RetroPie/BIOS/\n"
' 3. Copy ALL contents of "BIOS" from this archive\n\n' " 3. Extract all files from this archive directly into that folder\n\n"
), ),
} }
@@ -945,9 +936,8 @@ def _build_readme(
platform_name, platform_name,
( (
f"INSTALLATION\n\n" f"INSTALLATION\n\n"
f' 1. Open the "{base_dest or "files"}" folder in this archive\n' f" 1. Extract all files from this archive directly into your BIOS directory\n"
f" 2. Copy ALL contents to your BIOS directory\n" f" 2. Overwrite if asked\n\n"
f" 3. Overwrite if asked\n\n"
), ),
) )
@@ -3287,7 +3277,11 @@ def verify_pack(
# Data directory: check against cached files # Data directory: check against cached files
if status == "untracked" and _data_index: if status == "untracked" and _data_index:
_bn = os.path.basename(name) _bn = os.path.basename(name)
_pr = name[len("system/") :] if name.startswith("system/") else name _pr = name
for _known_prefix in ("system/", "bios/", "BIOS/", "Firmware/"):
if name.startswith(_known_prefix):
_pr = name[len(_known_prefix):]
break
_cands = [] _cands = []
if _pr in _data_path_index: if _pr in _data_path_index:
_cands.append(_data_path_index[_pr]) _cands.append(_data_path_index[_pr])
@@ -3454,6 +3448,13 @@ def verify_pack_against_platform(
zip_set = set(zf.namelist()) zip_set = set(zf.namelist())
zip_lower = {n.lower(): n for n in zip_set} zip_lower = {n.lower(): n for n in zip_set}
# Auto-detect flat vs nested ZIP
is_flat = bool(base_dest) and not any(
n.startswith(base_dest + "/")
for n in zip_set
if n not in ("README.txt", "manifest.json") and not n.endswith("/")
)
# Structural checks # Structural checks
dupes = sum(1 for c in Counter(zf.namelist()).values() if c > 1) dupes = sum(1 for c in Counter(zf.namelist()).values() if c > 1)
if dupes: if dupes:
@@ -3483,7 +3484,7 @@ def verify_pack_against_platform(
dest = fe.get("destination", fe.get("name", "")) dest = fe.get("destination", fe.get("name", ""))
if not dest: if not dest:
continue continue
expected = f"{base_dest}/{dest}" if base_dest else dest expected = f"{base_dest}/{dest}" if base_dest and not is_flat else dest
baseline_checked += 1 baseline_checked += 1
if expected in zip_set or expected.lower() in zip_lower: if expected in zip_set or expected.lower() in zip_lower:
@@ -3510,7 +3511,7 @@ def verify_pack_against_platform(
continue continue
raw_dest = u.get("path") or u["name"] raw_dest = u.get("path") or u["name"]
dest = f"{raw_dest}{u['name']}" if raw_dest.endswith("/") else raw_dest dest = f"{raw_dest}{u['name']}" if raw_dest.endswith("/") else raw_dest
if extras_pfx: if extras_pfx and not (is_flat and extras_pfx == base_dest):
if not dest.startswith(f"{extras_pfx}/"): if not dest.startswith(f"{extras_pfx}/"):
full = f"{extras_pfx}/{dest}" full = f"{extras_pfx}/{dest}"
else: else: