mirror of
https://github.com/itsPLK/ps5-pkg-manager.git
synced 2026-10-22 11:53:08 -05:00
* ws: direct-install streaming over WebSocket (RAM-only, no disk spool) Browser pushes .pkg bytes over ws://:8846 while the PS5 installer pulls ranges over :8845 concurrently through a demand-paged 1MB-segment ring (64 slots, seg 0 pinned). Install can start at header_ready, mid-upload. Backend (src/ws_stream.c, src/ws_upload.c): - ws_live_* ring with waiter shielding, served-past / waited+acked / peak>0+acked LRU eviction, single store_seg_locked() truth shared by blocking write and non-blocking try_write (0/-1/-2 busy). - Conn worker never blocks on storage (busy reply, browser retries); socket idle kill suppressed while readers are parked; stall diagnostics when a write parks. - http_server.c: guarded /api/upload/* REST branch; multipart.c live: scheme adapter (frozen pull path untouched); installer_start_live. Frontend: DirectInstallView + useDirectUpload pusher (FIFO seek queue for parallel installer workers, busy requeue+retry, demand-paging standby until install finalizes, silent-socket watchdog), wired into the history router (#/direct-install, Circle-back closes it). Parser: pkg_parser_parse_mem reuses in-file sub-parsers for live header parsing (differential-tested). Tests: ring unit (test_ws_stream), far-seek/pivot/busy/waiter unit (test_ws_stream_far), transport (test_ws_upload, incl. socket busy->retry), concurrent e2e incl. 90MB/64-slot parallel-pivot replay of the production capture (test_direct_install_e2e), PS5 pull-replay simulator (ps5_sim) + CLI tools. Proven on console: 1GB PKG installs at status='playable' with zero idle kills. Full host suite (13 binaries + CSS) green. * perf(smb): pipeline reads for faster package streaming Use a private SMB session per stream connection and keep up to eight credit-aware reads in flight, with requests up to 512 KiB. Reduce each request to the credits available in the current SMB2 window, including queued requests, so libsmb2 cannot silently shorten a request and leave a gap in the stream. Increase stream and socket buffers, and leave signing optional while still honoring servers that require it. Add SMB timing and throughput logs plus negotiated session diagnostics to separate SMB read latency from HTTP streaming overhead. Include the SMB logger in PS5 and host builds and update the mock async API used by tests. * fix(ws): harden direct install sessions and package preview Tie uploads to a browser owner and one authenticated WebSocket. Resume from acknowledged segments so cache eviction does not lose progress. Move Direct Install into media sources, show local PKG title and icon before installation, and warn before leaving an active transfer. Pass package metadata to the live installer and cover ownership and metadata parsing with tests. * fix(smb): honor package install debug setting Gate SMB debug file creation and per-read diagnostics behind pkg_install_debug, matching WebSocket stream logging behavior. Keep regular SMB package streaming unchanged. * fix(ui): return direct install to drives route Make the Direct Install Back to drives action replace the current route and restore the media source view directly. This prevents browsers that opened the page from leaving the app for an unrelated previous history entry. * fix(ui): restore deep-linked pages on reload Apply the initial hash route to the visible view during startup, including storage media and package details. Load package data for the linked drive so a direct package URL opens its detail page after a reload. * fix(ui): accept package drops across the app Handle package drops at the application level, open Direct Install, and pass the file through the existing package preview flow. Keep active uploads and installations protected from replacement. * fix(direct): check package eligibility before install Share the scanner's installed state and version rules with Direct Install. Check selected package metadata before enabling Install and again before starting a new transfer, while preserving resumed sessions. Show storage and active install blockers on the direct install page. * fix(direct): share live install icon across browsers Upload the parsed package icon once to the active direct install session and serve it from RAM through the existing icon API. Other browsers can resolve the live session path shown in installer status. Clear icon data when the session is canceled or replaced. * fix(direct): cancel install when uploader closes Use sendBeacon during browser teardown to call the authenticated Direct Install cancel endpoint for the sending session. Keep observer browsers unaffected and avoid duplicate cancellation requests. * fix(direct): cancel when uploader socket closes Treat an unexpected close of the authenticated uploader WebSocket before the final finish message as an install cancellation. Preserve normal completed sessions and reduce repetitive seek, eviction, and read-short logging in the main log. * bump version to 1.1.1 * fix(ui): stop duplicate direct upload polling Keep the observer watchdog stable across renders, disable it for the owning upload tab, and prevent overlapping status requests in both polling paths. * Add PS4 and PS5 package badges * feat: support platform-aware install storage * fix: preserve catalog during provisional scans * bump version to 1.2.0 * docs: add changelog for 1.2.0 * chore: remove stale refactor references * feat: move streaming services to dedicated ports * docs: update 1.2.0 documentation
107 lines
4.4 KiB
C
107 lines
4.4 KiB
C
#ifndef PKG_PARSER_H
|
|
#define PKG_PARSER_H
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
|
|
#define PKG_TITLE_ID_LEN 32
|
|
#define PKG_TITLE_NAME_LEN 256
|
|
#define PKG_LOCALIZED_TITLES_LEN 2048
|
|
#define PKG_DEFAULT_LANG_LEN 32
|
|
#define PKG_CONTENT_ID_LEN 64
|
|
#define PKG_PATH_LEN 512
|
|
|
|
typedef enum {
|
|
PKG_TYPE_UNKNOWN = 0,
|
|
PKG_TYPE_BASE, /* Base application / package */
|
|
PKG_TYPE_UPDATE, /* Application update / patch */
|
|
PKG_TYPE_DLC /* Additional content / DLC */
|
|
} pkg_type_t;
|
|
|
|
typedef struct {
|
|
char path[PKG_PATH_LEN];
|
|
char filename[256];
|
|
char title_id[PKG_TITLE_ID_LEN];
|
|
char title_name[PKG_TITLE_NAME_LEN];
|
|
char localized_titles[PKG_LOCALIZED_TITLES_LEN]; /* JSON object string: {"ar-AE":"...","en-US":"..."} */
|
|
char default_language[PKG_DEFAULT_LANG_LEN]; /* e.g. "en-US" */
|
|
char content_id[PKG_CONTENT_ID_LEN];
|
|
char app_version[32];
|
|
uint64_t file_size;
|
|
uint64_t total_pkg_size;
|
|
uint64_t icon_offset;
|
|
uint32_t icon_size;
|
|
int has_icon;
|
|
int is_valid;
|
|
int is_multipart;
|
|
uint32_t part_index;
|
|
uint32_t total_parts;
|
|
pkg_type_t pkg_type;
|
|
char pkg_type_str[16]; /* "base", "update", "dlc", "unknown" */
|
|
char category[16]; /* e.g. "gd", "gp", "ac", etc. */
|
|
uint64_t mtime; /* File modification timestamp */
|
|
char blurhash[64]; /* BlurHash placeholder for icon0.png ("" if none) */
|
|
} pkg_detail_t;
|
|
|
|
/**
|
|
* Resolves the best-matching title from a localized_titles JSON object given
|
|
* an Accept-Language header string (or comma-separated list of languages).
|
|
* Falls back to default_lang, English, or the first localized title.
|
|
* Returns 0 on success and populates out, or negative on failure.
|
|
*/
|
|
int pkg_parser_resolve_localized_title(const char *loc_json, const char *default_lang,
|
|
const char *accept_lang, char *out, size_t out_max);
|
|
|
|
/**
|
|
* Parses PS5 param.json buffer to extract titleId, category, version,
|
|
* localized titles map, default language, and best default title.
|
|
*/
|
|
void pkg_parser_parse_param_json(const char *json_buf, size_t data_sz,
|
|
char *out_title_id, size_t tid_max,
|
|
char *out_title_name, size_t tname_max,
|
|
char *out_category, size_t cat_max,
|
|
char *out_version, size_t ver_max,
|
|
char *out_localized_titles, size_t loc_max,
|
|
char *out_default_lang, size_t def_lang_max);
|
|
|
|
/**
|
|
* Parses PS4 param.sfo buffer to extract titleId, category, version,
|
|
* localized titles map, default language, and default title.
|
|
*/
|
|
void pkg_parser_parse_param_sfo(const uint8_t *sfo, size_t sfo_len, char *out_title, size_t title_max,
|
|
char *out_title_id, size_t title_id_max,
|
|
char *out_version, size_t version_max,
|
|
char *out_category, size_t category_max,
|
|
char *out_localized_titles, size_t loc_max,
|
|
char *out_default_lang, size_t def_lang_max);
|
|
|
|
/**
|
|
* Parses a PKG file (PS5 FIH or PS4 CNT format) and extracts metadata:
|
|
* title_id, title_name, content_id, and icon0.png offset/size.
|
|
* Returns 0 on success, negative value on error.
|
|
*/
|
|
int pkg_parser_parse(const char *file_path, pkg_detail_t *out);
|
|
|
|
/**
|
|
* Parses PKG metadata from an in-memory prefix (live RAM session): same
|
|
* fields as pkg_parser_parse but bounded to [data, data+data_len) instead
|
|
* of pread(). Anything addressed outside the prefix fails closed (with a
|
|
* stage code in *out_stage when non-NULL: 1 short header, 2 no CNT,
|
|
* 3 CNT header, 4 content_id, 5 entry header, 6 table past prefix).
|
|
* Icon extraction is skipped (has_icon = 0); total sizes come from
|
|
* total_size. Purely additive: the file-based parser above is untouched.
|
|
*/
|
|
int pkg_parser_parse_mem(const uint8_t *data, size_t data_len,
|
|
uint64_t total_size, const char *filename,
|
|
pkg_detail_t *out, int *out_stage);
|
|
|
|
/**
|
|
* Extracts raw icon bytes from the PKG file.
|
|
* Allocates *out_data with malloc, which caller must free().
|
|
* Returns 0 on success, negative on error.
|
|
*/
|
|
int pkg_parser_get_icon(const char *file_path, uint64_t offset, uint32_t size,
|
|
uint8_t **out_data, size_t *out_size);
|
|
|
|
#endif /* PKG_PARSER_H */
|