psp: detect installed PS1 games (incl. Category Lite folders)

PS1 EBOOTs install as PSP/GAME/{category}/{GAME_ID}/EBOOT.PBP, which the
PSP index ignored (it only indexed ISO games/videos), so the app never
marked them 'on PSP'. Index homebrew EBOOTs by folder id + embedded title,
match PS1 library items against them, and make 'Delete from PSP' remove the
whole game folder.
This commit is contained in:
2026-09-07 19:59:30 -04:00
parent 0ebd173af7
commit 2c6b07e286
2 changed files with 65 additions and 4 deletions
+46 -1
View File
@@ -3604,6 +3604,7 @@ class MainWindow(QMainWindow):
self._psp_root = "" self._psp_root = ""
self._psp_index = {} self._psp_index = {}
self._psp_paths = {} self._psp_paths = {}
self._psp_homebrew = []
self._pending_umd_dump = None self._pending_umd_dump = None
self._auto_rename_umd = None self._auto_rename_umd = None
self._library_threads = [] self._library_threads = []
@@ -4149,6 +4150,7 @@ class MainWindow(QMainWindow):
inventory = self.psp_page.inventory() or {} inventory = self.psp_page.inventory() or {}
self._psp_index = psp.build_index(inventory) self._psp_index = psp.build_index(inventory)
self._psp_paths = {"raw": {}, "norm": {}} self._psp_paths = {"raw": {}, "norm": {}}
self._psp_homebrew = list(inventory.get("homebrew", []))
for group in ("games", "videos"): for group in ("games", "videos"):
for it in inventory.get(group, []): for it in inventory.get(group, []):
stem = Path(it["name"]).stem stem = Path(it["name"]).stem
@@ -4162,6 +4164,7 @@ class MainWindow(QMainWindow):
else: else:
self._psp_index = {} self._psp_index = {}
self._psp_paths = {} self._psp_paths = {}
self._psp_homebrew = []
if hasattr(self, "details_panel"): if hasattr(self, "details_panel"):
self.details_panel.set_copy_available(bool(self._psp_root)) self.details_panel.set_copy_available(bool(self._psp_root))
current = getattr(self, "_current_item", None) current = getattr(self, "_current_item", None)
@@ -4180,7 +4183,20 @@ class MainWindow(QMainWindow):
if stem.lower() in self._psp_index.get("raw", set()): if stem.lower() in self._psp_index.get("raw", set()):
return True return True
norm = matcher.normalize_name(stem) norm = matcher.normalize_name(stem)
return bool(norm) and norm in self._psp_index.get("names", set()) if norm and norm in self._psp_index.get("names", set()):
return True
if item.get("system") == "ps1":
# PS1 EBOOTs install as PSP/GAME/{category}/{GAME_ID}/EBOOT.PBP, so
# match the source game-folder id (or the embedded title) against
# the homebrew index (which includes Category Lite subfolders).
game_id = Path(item.get("path") or "").parent.name.strip().lower()
if game_id and game_id in self._psp_index.get("homebrew_ids", set()):
return True
if stem.lower() in self._psp_index.get("homebrew_names", set()):
return True
if norm and norm in self._psp_index.get("homebrew_names", set()):
return True
return False
def _psp_dot_icon(self): def _psp_dot_icon(self):
icon = getattr(self, "_psp_icon", None) icon = getattr(self, "_psp_icon", None)
@@ -4371,6 +4387,35 @@ class MainWindow(QMainWindow):
QMessageBox.information(self, "Not on PSP", "That disc is not installed on the PSP.") QMessageBox.information(self, "Not on PSP", "That disc is not installed on the PSP.")
return return
stem = Path(item.get("canonical") or item.get("name") or "").stem stem = Path(item.get("canonical") or item.get("name") or "").stem
if item.get("system") == "ps1":
# PS1 EBOOTs live as PSP/GAME/{category}/{GAME_ID}/EBOOT.PBP; delete
# the whole game folder, matched by the source folder id (or title).
game_id = Path(item.get("path") or "").parent.name.strip().lower()
hb = self._psp_homebrew or []
if game_id:
matches = [h for h in hb if (h.get("id") or "").strip().lower() == game_id]
else:
matches = [h for h in hb if Path(h.get("name") or "").stem.lower() == stem.lower()]
if not matches:
QMessageBox.warning(self, "Not found", f"Could not find the installed copy on the PSP:\n{stem}")
return
name = item.get("title_override") or item.get("canonical") or item.get("name") or stem
preview = "\n".join(str(Path(m["path"]).parent) for m in matches[:5])
res = QMessageBox.question(
self, "Delete from PSP",
f"Delete this game folder from the PSP?\n\n{name}\n\n{preview}",
QMessageBox.Yes | QMessageBox.Cancel, QMessageBox.Cancel,
)
if res != QMessageBox.Yes:
return
failed = 0
for m in matches:
if not transfers.remove_path(Path(m["path"]).parent):
failed += 1
if failed:
QMessageBox.warning(self, "Delete incomplete", f"{failed} folder(s) could not be deleted.")
self._refresh_psp()
return
matches = list(self._psp_paths.get("raw", {}).get(stem.lower(), [])) matches = list(self._psp_paths.get("raw", {}).get(stem.lower(), []))
if not matches: if not matches:
n = matcher.normalize_name(stem) n = matcher.normalize_name(stem)
+19 -3
View File
@@ -236,8 +236,11 @@ def list_installed(root):
def build_index(inventory): def build_index(inventory):
"""Build a lookup index for matching library discs against the PSP. """Build a lookup index for matching library discs against the PSP.
Returns ``{"raw": set, "names": set}`` where ``raw`` holds lowercase ISO Returns ``{"raw", "names", "homebrew_ids", "homebrew_names"}`` where
stems (exact filename match) and ``names`` holds fuzzy-normalized stems. ``raw`` holds lowercase ISO stems (exact filename match), ``names`` holds
fuzzy-normalized stems, and the homebrew keys index ``PSP/GAME/**/EBOOT.PBP``
(homebrew + PS1 Classics, including Category Lite subfolders) by folder id
and embedded title.
""" """
raw, names = set(), set() raw, names = set(), set()
for group in ("games", "videos"): for group in ("games", "videos"):
@@ -247,7 +250,20 @@ def build_index(inventory):
norm = matcher.normalize_name(stem) norm = matcher.normalize_name(stem)
if norm: if norm:
names.add(norm) names.add(norm)
return {"raw": raw, "names": names}
hb_ids, hb_names = set(), set()
for it in (inventory or {}).get("homebrew", []):
ident = str(it.get("id") or "").strip().lower()
if ident:
hb_ids.add(ident)
stem = Path(it.get("name") or "").stem.strip().lower()
if stem:
hb_names.add(stem)
norm = matcher.normalize_name(Path(it.get("name") or "").stem)
if norm:
hb_names.add(norm)
return {"raw": raw, "names": names,
"homebrew_ids": hb_ids, "homebrew_names": hb_names}
def find_umd_iso(root): def find_umd_iso(root):