ps1: official-style ICON0/PIC0/PIC1 artwork with PSN templates

Replaces the letterboxed 144x80 icon + cropped PIC1 with official-looking
PS1 Classics art:
- ICON0 (80x80): cover art composited into the bundled case frame (PS logo
  + 'PlayStation.' wordmark) from data/ps1_templates/ICON0.PNG.
- PIC0 (310x180): a PSN-style info board drawn in code — title, IGDB
  description, ESRB badge (from IGDB age_ratings) and PS3/PSP badges.
- PIC1 (480x272): the bundled launch background template.

The converter now embeds PIC0, the PS1 tab previews all three images, and
ps1_art pulls IGDB description/ESRB as best effort. Templates are git-ignored
(user-supplied, like base.PBP) with a drawn-art fallback when missing.
This commit is contained in:
2026-09-07 19:23:38 -04:00
parent 2433240628
commit f6dccbec24
6 changed files with 325 additions and 72 deletions
+40 -30
View File
@@ -3366,9 +3366,9 @@ class Ps1ScanWorker(QObject):
class Ps1PreviewWorker(QObject):
"""Fetch a disc's cover art and build the EBOOT icon/background preview."""
"""Fetch a disc's cover art and build the EBOOT art preview."""
result = Signal(int, str, object, object) # gen, game_id, icon0 bytes, pic1 bytes
result = Signal(int, str, object, object, object) # gen, game_id, icon0, pic0, pic1
finished = Signal()
def __init__(self, gen, title, game_id):
@@ -3379,10 +3379,10 @@ class Ps1PreviewWorker(QObject):
def run(self):
try:
icon0, pic1 = covers.ps1_art(self.title, self.game_id)
icon0, pic0, pic1 = covers.ps1_art(self.title, self.game_id)
except Exception: # noqa: BLE001
icon0, pic1 = None, None
self.result.emit(self.gen, self.game_id, icon0, pic1)
icon0, pic0, pic1 = None, None, None
self.result.emit(self.gen, self.game_id, icon0, pic0, pic1)
self.finished.emit()
@@ -3427,8 +3427,12 @@ class Ps1Page(QWidget):
self.preview_title.setStyleSheet("font-weight: bold;")
self.preview_icon0 = QLabel()
self.preview_icon0.setAlignment(Qt.AlignCenter)
self.preview_icon0.setFixedSize(288, 160)
self.preview_icon0.setFixedSize(160, 160)
self.preview_icon0.setStyleSheet("background: #14151a; border: 1px solid #333; color: #888;")
self.preview_pic0 = QLabel()
self.preview_pic0.setAlignment(Qt.AlignCenter)
self.preview_pic0.setFixedSize(310, 180)
self.preview_pic0.setStyleSheet("background: #14151a; border: 1px solid #333; color: #888;")
self.preview_pic1 = QLabel()
self.preview_pic1.setAlignment(Qt.AlignCenter)
self.preview_pic1.setFixedSize(480, 272)
@@ -3439,16 +3443,24 @@ class Ps1Page(QWidget):
pv.setContentsMargins(8, 0, 0, 0)
pv.addWidget(QLabel("<b>Preview</b>"))
pv.addWidget(self.preview_title)
pv.addWidget(QLabel("ICON0 — XMB icon"))
pv.addWidget(QLabel("ICON0 — XMB icon (80×80)"))
pv.addWidget(self.preview_icon0)
pv.addWidget(QLabel("PIC1 — launch background"))
pv.addWidget(QLabel("PIC0 — info board (310×180)"))
pv.addWidget(self.preview_pic0)
pv.addWidget(QLabel("PIC1 — launch background (480×272)"))
pv.addWidget(self.preview_pic1)
pv.addStretch(1)
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setWidget(preview)
scroll.setFrameShape(QScrollArea.NoFrame)
splitter = QSplitter(Qt.Horizontal)
splitter.addWidget(self.table)
splitter.addWidget(preview)
splitter.addWidget(scroll)
splitter.setStretchFactor(0, 1)
splitter.setSizes([640, 500])
layout.addWidget(splitter, 1)
def _on_preview_changed(self):
@@ -3466,6 +3478,8 @@ class Ps1Page(QWidget):
self.preview_title.setText(e["name"])
self.preview_icon0.setPixmap(QPixmap())
self.preview_icon0.setText("Loading…")
self.preview_pic0.setPixmap(QPixmap())
self.preview_pic0.setText("Loading…")
self.preview_pic1.setPixmap(QPixmap())
self.preview_pic1.setText("Loading…")
self._preview_gen += 1
@@ -3484,34 +3498,30 @@ class Ps1Page(QWidget):
self._preview_threads.append(thread)
self._preview_workers.append(worker)
def _on_preview_result(self, gen, game_id, icon0, pic1):
def _on_preview_result(self, gen, game_id, icon0, pic0, pic1):
if gen != self._preview_gen:
return
self.preview_icon0.setText("")
self.preview_pic0.setText("")
self.preview_pic1.setText("")
if icon0:
pm = QPixmap()
pm.loadFromData(icon0)
self.preview_icon0.setPixmap(
pm.scaled(self.preview_icon0.width(), self.preview_icon0.height(),
Qt.KeepAspectRatio, Qt.SmoothTransformation))
else:
self.preview_icon0.setText("No art found")
if pic1:
pm = QPixmap()
pm.loadFromData(pic1)
self.preview_pic1.setPixmap(
pm.scaled(self.preview_pic1.width(), self.preview_pic1.height(),
Qt.KeepAspectRatio, Qt.SmoothTransformation))
else:
self.preview_pic1.setText("No art found — the default icon will be used")
for label, data, fallback in (
(self.preview_icon0, icon0, "No art found"),
(self.preview_pic0, pic0, "No art found"),
(self.preview_pic1, pic1, "No art found — the default icon will be used"),
):
if data:
pm = QPixmap()
pm.loadFromData(data)
label.setPixmap(pm.scaled(label.width(), label.height(),
Qt.KeepAspectRatio, Qt.SmoothTransformation))
else:
label.setText(fallback)
def _reset_preview(self, message):
self.preview_title.setText(message)
self.preview_icon0.setPixmap(QPixmap())
self.preview_icon0.setText("")
self.preview_pic1.setPixmap(QPixmap())
self.preview_pic1.setText("")
for label in (self.preview_icon0, self.preview_pic0, self.preview_pic1):
label.setPixmap(QPixmap())
label.setText("")
def set_dirs(self, ps1_dir, output_dir):
self._ps1_dir = ps1_dir