Add NoPayStation themes, DLC and minis support
This commit is contained in:
+95
-11
@@ -1681,7 +1681,7 @@ class StorePage(QWidget):
|
||||
self.source_combo.currentIndexChanged.connect(self._on_source_changed)
|
||||
bar.addWidget(self.source_combo)
|
||||
self.nps_type = QComboBox()
|
||||
self.nps_type.addItems(["PSP Games", "PS1 Classics"])
|
||||
self.nps_type.addItems(["PSP Games", "PS1 Classics", "PSP Themes", "PSP DLC"])
|
||||
self.nps_type.setVisible(False)
|
||||
self.nps_type.currentIndexChanged.connect(self._on_nps_type_changed)
|
||||
bar.addWidget(self.nps_type)
|
||||
@@ -1788,7 +1788,7 @@ class StorePage(QWidget):
|
||||
if self.source_combo.currentIndex() == 1:
|
||||
return transfers.ArchiveOrgSource(self._archive_id, self._archive_sig, self._archive_user)
|
||||
if self.source_combo.currentIndex() == 3:
|
||||
ct = "psp" if self.nps_type.currentIndex() == 0 else "psx"
|
||||
ct = ("psp", "psx", "theme", "dlc")[self.nps_type.currentIndex()]
|
||||
return transfers.NoPayStationSource(ct, pkg2zip_path=self._pkg2zip)
|
||||
return transfers.LocalShareSource(self._store_dir)
|
||||
|
||||
@@ -3266,11 +3266,13 @@ def _ps1_library_items(ps1_dir, output_dir, overrides, dates):
|
||||
|
||||
|
||||
def _nps_library_items(nps_dir, overrides, dates):
|
||||
"""NoPayStation-downloaded PSP games and PS1 Classics, merged into the library.
|
||||
"""NoPayStation-downloaded content, merged into the library.
|
||||
|
||||
Scans ``<nps>/PSP/*/EBOOT.PBP`` (PSP PSN games) and ``<nps>/PSX/*/EBOOT.PBP``
|
||||
(PS1 Classics). PSP games are ``system="games"`` with ``psp_kind="eboot"``;
|
||||
PSX games are ``system="ps1"`` with ``ps1_kind="eboot"``.
|
||||
Scans ``<nps>/PSP/*/EBOOT.PBP`` (PSP PSN games), ``<nps>/PSX/*/EBOOT.PBP``
|
||||
(PS1 Classics), ``<nps>/THEME/*/*.ptf`` (themes) and ``<nps>/DLC/*/*.edat``
|
||||
(DLC). PSP games are ``system="games"`` with ``psp_kind="eboot"``; PSX games
|
||||
are ``system="ps1"`` with ``ps1_kind="eboot"``; themes/DLC use
|
||||
``psp_kind="theme"/"dlc"``.
|
||||
"""
|
||||
if not nps_dir:
|
||||
return []
|
||||
@@ -3308,9 +3310,43 @@ def _nps_library_items(nps_dir, overrides, dates):
|
||||
else:
|
||||
item["psp_kind"] = "eboot"
|
||||
out.append(item)
|
||||
|
||||
# Themes: <nps>/THEME/<TITLE_ID>/<name>.ptf (extension case varies)
|
||||
theme_dir = root / "THEME"
|
||||
if theme_dir.is_dir():
|
||||
for ptf in sorted(theme_dir.rglob("*")):
|
||||
if ptf.is_file() and ptf.suffix.lower() == ".ptf":
|
||||
out.append(_nps_extra_item(ptf, "theme", ptf.stem))
|
||||
|
||||
# DLC: <nps>/DLC/<GAME_ID>/<name>.edat
|
||||
dlc_dir = root / "DLC"
|
||||
if dlc_dir.is_dir():
|
||||
for edat in sorted(dlc_dir.rglob("*")):
|
||||
if edat.is_file() and edat.suffix.lower() == ".edat":
|
||||
out.append(_nps_extra_item(edat, "dlc", edat.stem))
|
||||
return out
|
||||
|
||||
|
||||
def _nps_extra_item(path, psp_kind, name):
|
||||
try:
|
||||
size = path.stat().st_size
|
||||
except OSError:
|
||||
size = 0
|
||||
rid = covers.stable_key(name)
|
||||
return {
|
||||
"name": name,
|
||||
"release_id": rid,
|
||||
"system": psp_kind,
|
||||
"canonical": name,
|
||||
"serial": "",
|
||||
"region": None,
|
||||
"release_date": "",
|
||||
"path": str(path),
|
||||
"size": size,
|
||||
"psp_kind": psp_kind,
|
||||
}
|
||||
|
||||
|
||||
class LibraryWorker(QObject):
|
||||
result = Signal(list)
|
||||
finished = Signal()
|
||||
@@ -3957,7 +3993,7 @@ class MainWindow(QMainWindow):
|
||||
self.library_search.textChanged.connect(self._apply_library_filter)
|
||||
bar.addWidget(self.library_search, 1)
|
||||
self.library_type = QComboBox()
|
||||
self.library_type.addItems(["All types", "Games", "Videos", "PS1"])
|
||||
self.library_type.addItems(["All types", "Games", "Videos", "PS1", "Themes", "DLC"])
|
||||
self.library_type.currentTextChanged.connect(self._apply_library_filter)
|
||||
bar.addWidget(self.library_type)
|
||||
self.library_psp = QComboBox()
|
||||
@@ -4227,6 +4263,10 @@ class MainWindow(QMainWindow):
|
||||
continue
|
||||
if typ == "PS1" and it["system"] != "ps1":
|
||||
continue
|
||||
if typ == "Themes" and it["system"] != "theme":
|
||||
continue
|
||||
if typ == "DLC" and it["system"] != "dlc":
|
||||
continue
|
||||
if q and q not in (it["canonical"] or it["name"]).lower():
|
||||
continue
|
||||
if psp_f == "On PSP" and not it.get("on_psp"):
|
||||
@@ -4252,7 +4292,8 @@ class MainWindow(QMainWindow):
|
||||
title.setIcon(self._psp_dot_icon())
|
||||
title.setToolTip("Installed on PSP")
|
||||
self.library_table.setItem(row, 0, title)
|
||||
kind_label = {"games": "Game", "videos": "Video", "ps1": "PS1"}.get(it["system"], "Game")
|
||||
kind_label = {"games": "Game", "videos": "Video", "ps1": "PS1",
|
||||
"theme": "Theme", "dlc": "DLC"}.get(it["system"], "Game")
|
||||
self.library_table.setItem(row, 1, QTableWidgetItem(kind_label))
|
||||
self.library_table.setItem(row, 2, QTableWidgetItem(it.get("region") or ""))
|
||||
self.library_table.setItem(row, 3, QTableWidgetItem(it.get("serial") or ""))
|
||||
@@ -4410,7 +4451,7 @@ class MainWindow(QMainWindow):
|
||||
if not self._nps_dir:
|
||||
QMessageBox.warning(self, "No NPS output dir", "Set the NoPayStation output dir in Settings → Store.")
|
||||
return
|
||||
sub = "PSP" if src.content_type == "psp" else "PSX"
|
||||
sub = {"psp": "PSP", "psx": "PSX", "theme": "THEME", "dlc": "DLC"}.get(src.content_type, "PSP")
|
||||
dest_dir = Path(self._nps_dir) / sub
|
||||
kind = "nps-download"
|
||||
else:
|
||||
@@ -4457,6 +4498,13 @@ class MainWindow(QMainWindow):
|
||||
self._maybe_auto_sync()
|
||||
|
||||
def _on_psp(self, item):
|
||||
psp_kind = item.get("psp_kind")
|
||||
if psp_kind in ("theme", "dlc") and self._psp_root:
|
||||
name = Path(item.get("path") or "").name
|
||||
if psp_kind == "theme":
|
||||
return (Path(self._psp_root) / "PSP" / "THEME" / name).exists()
|
||||
game_id = Path(item.get("path") or "").parent.name
|
||||
return (Path(self._psp_root) / "PSP" / "GAME" / game_id / name).exists()
|
||||
if not self._psp_index:
|
||||
return False
|
||||
stem = Path(item.get("canonical") or item.get("name") or "").stem
|
||||
@@ -4508,8 +4556,8 @@ class MainWindow(QMainWindow):
|
||||
return # UMD Video discs can't be played in PPSSPP
|
||||
if item.get("system") == "ps1" and item.get("ps1_kind") != "eboot":
|
||||
return # raw PS1 discs are converted, not played
|
||||
if item.get("psp_kind") == "eboot":
|
||||
return # encrypted PSP PSN EBOOTs can't run in PPSSPP
|
||||
if item.get("psp_kind") in ("eboot", "theme", "dlc"):
|
||||
return # encrypted PSN EBOOTs/themes/DLC can't run in PPSSPP
|
||||
path = item.get("path")
|
||||
if not path or not Path(path).exists():
|
||||
QMessageBox.warning(self, "Missing file", f"File not found:\n{path}")
|
||||
@@ -4582,6 +4630,23 @@ class MainWindow(QMainWindow):
|
||||
psp_root = Path(self._psp_root)
|
||||
if system == "videos":
|
||||
dest_dir = psp_root / "ISO" / "VIDEO"
|
||||
elif item.get("psp_kind") == "theme":
|
||||
# Themes install as a single .ptf under /PSP/THEME.
|
||||
dest_name = Path(item["path"]).name
|
||||
dest_dir = psp_root / "PSP" / "THEME"
|
||||
src = transfers.FileCopySource(item["path"])
|
||||
log.info("copying theme to psp: %s -> %s", item["path"], dest_dir / dest_name)
|
||||
self.queue.enqueue(src, dest_dir, {"dest_name": dest_name}, dest_name, kind="psp-install")
|
||||
return True
|
||||
elif item.get("psp_kind") == "dlc":
|
||||
# DLC installs as a single .edat into /PSP/GAME/<GAME_ID>.
|
||||
game_id = Path(item["path"]).parent.name
|
||||
dest_name = Path(item["path"]).name
|
||||
dest_dir = psp_root / "PSP" / "GAME" / game_id
|
||||
src = transfers.FileCopySource(item["path"])
|
||||
log.info("copying dlc to psp: %s -> %s", item["path"], dest_dir / dest_name)
|
||||
self.queue.enqueue(src, dest_dir, {"dest_name": dest_name}, dest_name, kind="psp-install")
|
||||
return True
|
||||
elif system == "ps1" or item.get("psp_kind") == "eboot":
|
||||
# PS1 EBOOTs and NoPayStation PSP games install as a whole game
|
||||
# folder under /PSP/GAME (optionally nested in a Category Lite
|
||||
@@ -4674,6 +4739,25 @@ class MainWindow(QMainWindow):
|
||||
QMessageBox.information(self, "Not on PSP", "That disc is not installed on the PSP.")
|
||||
return
|
||||
stem = Path(item.get("canonical") or item.get("name") or "").stem
|
||||
psp_kind = item.get("psp_kind")
|
||||
if psp_kind in ("theme", "dlc"):
|
||||
name = Path(item.get("path") or "").name
|
||||
target = (Path(self._psp_root) / "PSP" / "THEME" / name) if psp_kind == "theme" \
|
||||
else (Path(self._psp_root) / "PSP" / "GAME" / Path(item.get("path") or "").parent.name / name)
|
||||
if not target.exists():
|
||||
QMessageBox.warning(self, "Not found", f"Could not find the installed copy on the PSP:\n{name}")
|
||||
return
|
||||
res = QMessageBox.question(
|
||||
self, "Delete from PSP",
|
||||
f"Delete this from the PSP?\n\n{name}\n\n{target}",
|
||||
QMessageBox.Yes | QMessageBox.Cancel, QMessageBox.Cancel,
|
||||
)
|
||||
if res != QMessageBox.Yes:
|
||||
return
|
||||
if not transfers.remove_path(target):
|
||||
QMessageBox.warning(self, "Delete incomplete", f"Could not delete:\n{target}")
|
||||
self._refresh_psp()
|
||||
return
|
||||
if item.get("system") == "ps1" or item.get("psp_kind") == "eboot":
|
||||
# PS1 EBOOTs / NoPayStation PSP games: delete the whole game folder,
|
||||
# matched by the source folder id (or title).
|
||||
|
||||
Reference in New Issue
Block a user