- PSP_GAMES.tsv and PSX_GAMES.tsv use different headers (PSP adds Type/RAP
columns, PSX adds Original Name). Parse the header row and map columns by
name instead of hardcoding positions — PSP names were showing as 'Minis'/
'PC Engine' (the Type column) with broken download URLs.
- GameDetailsPanel.set_item referenced is_ps1 before it was assigned, raising
UnboundLocalError and leaving the detail panel stale.
New 'NoPayStation' option in the Store tab lists PSP_GAMES.tsv and
PSX_GAMES.tsv, downloads each item's encrypted .pkg and decrypts/extracts
it with pkg2zip into a configurable output dir (<dir>/PSP/<TITLE_ID>/ and
<dir>/PSX/<TITLE_ID>/). Downloaded PSP games and PS1 Classics appear in the
Library (games/eboot and ps1/eboot respectively) and copy to the PSP as
PSP/GAME folders.
- app/nopaystation.py: TSV fetch/parse (cached) + pkg2zip discovery/extract.
- app/transfers.py: NoPayStationSource (scan + download).
- Settings: NPS output dir + pkg2zip path (QSettings nps_dir/pkg2zip_path).
- StorePage: NPS source + content-type combo (PSP Games / PS1 Classics),
per-item cover system, TSV release dates, nps-download job kind.
- Library: _nps_library_items ingest, on-PSP detection, folder copy/delete,
PPSSPP play disabled for encrypted PSP EBOOTs.
- README: document the source, pkg2zip + npdrm_free requirements.
Add a muted, selectable filename line under the title in both details
panels, with the full path as its tooltip (archive name in the Store,
disc/ISO filename in the Library).
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.
Library covers go back to libretro -> OpenVGDB -> ScreenScraper ->
Wikipedia -> IGDB (last resort) for all systems, including PS1. The EBOOT
ICON0 (ps1_art) alone now tries IGDB first for clean banner-less box art,
falling back to the normal pipeline.
- ICON0: scale the cover to fit the case slot (no cropping), centred with
the frame's dark background behind the letterbox bars.
- PIC0: shrink the title font until the full name fits instead of cutting
it with an ellipsis (falls back to eliding only below 10px).
- Cover art: try IGDB first for PS1 so the cover is clean box art without
the PlayStation banner that libretro/ScreenScraper thumbnails bake in
(redundant with the ICON0 case frame).
- PIC0: elide long titles with an ellipsis and word-wrap the description to
3 lines with a trailing ellipsis instead of clipping/overflowing.
- PIC0: remove the PS3 badge; PSP only for now (PS3/PS4 packaging is future
work).
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.
Converted games already appear in the Library tab, so the PS1 tab now
lists only raw discs that still need converting. Auto-selects the first
pending disc so the preview loads immediately.
Selecting a pending disc in the PS1 tab now shows a live preview of the
ICON0 (144x80 XMB icon) and PIC1 (480x272 launch background) that the
converter would embed, fetched asynchronously so the UI stays responsive.
Converted discs/EBOOTs show an 'already converted' notice, and ps1_art now
falls back to a name-derived cache key when a disc has no serial.
Fetch the game's cover art (libretro / ScreenScraper / IGDB / Wikipedia,
reusing the local cover cache) and bake it into the EBOOT's ICON0 (144x80
XMB icon) and PIC1 (480x272 launch background) instead of the default PS1
icon. Best-effort: falls back to the base icon/empty background when no art
is found. convert()/convert_to_folder() gain optional icon0/pic1 params.
Add a 'Type' column to the Queue table mapping each job kind to a friendly
label, so downloads, copies, PS1 conversions, UMD dumps and save syncs are
distinguishable at a glance.
The fuzzy fallback ran difflib against every catalog release (~11k) for each
unmatched disc, dominating library load (2.7s) and startup scans. Pre-filter
candidates to those sharing a significant (>=3 char) word with the disc
filename or PARAM.SFO title before the expensive comparison, and cache the
pre-computed word sets. Library load: 2.7s -> 0.44s; scan: ~0.5s.
refresh_item matched the grid row by object identity, which silently fails
when the library list was rebuilt (background scan/store refresh) while the
scrape dialog was open. Match by release_id + system as a fallback, like
update_item_label already does, so the cover tile updates without a Reload.
- _on_ps1_convert now opens the Queue tab so the user sees the conversion
job start when converting from the Library or PS1 tab.
- Add a 'PS1 category (Category Lite)' setting (ps1_category) alongside the
existing ISO category; PS1 EBOOTs copy to /PSP/GAME/<category>/<ID>/
when set, matching the ISO category behaviour.
Fix 80020148 'game could not be started' on ARK-5: the base PBP's encrypted
~PSP loader fails to link on modern CFW. Use the bundled plain-ELF loader
(data/loader/DATA.PSP) as the DATA.PSP section instead, and write the 16-byte
NPDRM version key (data/loader/KEYS.BIN) alongside the EBOOT so CFW can
decrypt the STARTDAT PGD. Both assets are from iPoPS (MIT, attributed).
- load_data_psp: prefer the bundled ELF, fall back to the base ~PSP loader.
- load_keys_bin: return the bundled 16-byte key when present.
- convert: write KEYS.BIN next to the output EBOOT.
- README + data/loader/ATTRIBUTION.md: document the loader and its license.
- extract_startdat: locate STARTDAT at PSAR + p1 (the u32 at PSAR+0x0C)
instead of popstation's p1 + 0x50000 absolute offset, which only worked
for bases whose PSAR was aligned to 0x50000. Any PS1 classic can now be
used as a base.
- _warn_old_base: print a warning when the base PBP is a 3.xx-era classic,
since its POPS loader often fails to link on 6.60/6.61 CFW (error
80020148 'game could not be started').
- README: document the 6.60/6.61 base preference and DEFLATE compression.
POPS on real PSP hardware refuses to boot uncompressed (level-0) PSARs
(noted in pop-fe: 'Uncompressed EBOOTs don't work'), which caused 'the game
cannot be started'. Compress each 0x9300 disc block with raw DEFLATE and
switch the index table to the pop-fe layout (offset u32, length u16, flags
u8, SHA-1[16]); patch p1/p2 after the compressed size is known.
- _write_disc_block: deflate each block; store raw bytes when a block does
not shrink.
- _write_psar: reserve + patch the index table and p1/p2.
- convert/convert_to_folder/CLI: complevel parameter (default 1).
- Reuse the base PBP's ICON0 and drop generated PIC1 to match popstation.
- Self-test: add a DEFLATE round-trip with index/SHA-1 checks.
Fix the EBOOT.PBP 'data is corrupted' boot failure on CFW by making the
converter match popstation byte-for-byte where it counts:
- build_toc: A0 entry byte 8 is the raw Red Book disc-type bitfield 0x20
(CD-ROM XA), not BCD(0x20)=0x32; A-point entries use control 0x41.
- extract_data_psp: copy only prx_size (u32 at DATA.PSP+0x2C), not the
whole zero-padded section.
- PSAR_ALIGNMENT: 0x50000 (matches popstation and the base PBP).
- SFO: patch the base PBP's PARAM.SFO in place (title + DISC_ID) instead
of rebuilding, preserving integer field types (0x0404) and values.
- Cue pregap: INDEX 00 pregap is already inside the bin file, so do not
re-emit zero sectors; only a PREGAP command adds external pregap.
Track starts/lead-out now match PSXPackager's merged-cue output.