# PKG Manager - Architecture & Implementation Details This document provides an in-depth architectural guide to the **PKG Manager** daemon, its internal subsystems, virtual HTTP streaming engine, Direct Install upload path, PlayStation 5 system installer integration, and storage paradigms. --- ## 1. Architectural Overview PKG Manager is designed as a persistent native daemon (`pkgmgr.elf`) running in userland on PlayStation 5 (Prospero OS). It couples an embedded web-based control application with an HTTP range-streaming engine. A fresh helper process calls Sony's proprietary package installer subsystem (`libSceAppInstUtil.sprx`) for each install. ```mermaid flowchart TB subgraph UI ["User Interface"] BROWSER["Web Browser / PS5 WebKit
React Single-Page Application"] end subgraph STOR ["Media & Package Sources"] direction TB USB["USB Storage
/mnt/usb0-7"] DISC["Optical Disc
/mnt/disc"] SMB["Network Shares
Samba / SMB (libsmb2)"] end subgraph CTRL ["PKG Manager Control Subsystem (Port 8844)"] direction TB HTTP8844["Control & Web Server (:8844)
libmicrohttpd • REST API & UI"] subgraph CTRL_WORKERS ["Daemon Subsystems"] direction LR APPINFO["App Inspector
app_info.c"] INSTALLER["Installation Controller
installer.c"] SCANNER["Package Scanner & Parser
pkg_scanner.c / pkg_parser.c"] end end subgraph STRM ["Virtual Streaming Pipeline (Ports 18841 / 18842)"] direction LR SOCKET["HTTP Range Server (:18841)
stream_server.c (Raw BSD Sockets)"] WS["Direct Install Upload (:18842)
ws_upload.c (WebSocket)"] VSTREAM["Virtual Stream Engine
multipart.c (Multi-part Slices & SMB)"] end subgraph PS5 ["PlayStation 5 Native System Layer"] direction TB SCE["libSceAppInstUtil.sprx
sceAppInstUtilInstallByPackage"] RECEIVER["PS5 Background Installer
System Package Receiver"] APPDB[("App Database
app.db")] NOTIF["Notifications
libkernel (SceNotification)"] end %% UI Interaction BROWSER <-->|"HTTP REST / Static Assets"| HTTP8844 BROWSER -->|"WebSocket PKG Upload"| WS %% Control Plane routing HTTP8844 -->|"Install Command"| INSTALLER HTTP8844 -->|"Scan Trigger"| SCANNER INSTALLER -->|"Check Versions"| APPINFO %% Left: PS5 OS interactions APPINFO <-->|"SQLite Query"| APPDB INSTALLER -->|"Embedded ELF upload :9021"| ELFLDR["elfldr"] ELFLDR -->|"Launch"| HELPER["pkgmgr-inst.elf"] INSTALLER <-->|"Loopback IPC :18843"| HELPER HELPER -->|"One install per process"| SCE INSTALLER -.->|"Disc Swap Prompts"| NOTIF SCE -->|"Forward Stream URL"| RECEIVER RECEIVER -->|"HTTP 206 Range Requests"| SOCKET %% Center: Stream session mount INSTALLER -->|"Mount Session"| VSTREAM SOCKET -->|"Fetch Slices"| VSTREAM WS -->|"RAM-backed Live Session"| VSTREAM %% Right: Storage reads STOR -->|"Scan Files & Headers"| SCANNER STOR -->|"pread() / smb2_read()"| VSTREAM %% Styling classDef client fill:#e3f2fd,stroke:#1565c0,stroke-width:1.5px,color:#0d47a1; classDef ctrl fill:#ede7f6,stroke:#512da8,stroke-width:1.5px,color:#311b92; classDef stor fill:#e8f5e9,stroke:#2e7d32,stroke-width:1.5px,color:#1b5e20; classDef strm fill:#f3e5f5,stroke:#7b1fa2,stroke-width:1.5px,color:#4a148c; classDef ps5 fill:#fff3e0,stroke:#e65100,stroke-width:1.5px,color:#bf360c; class BROWSER client; class HTTP8844,APPINFO,INSTALLER,SCANNER ctrl; class USB,DISC,SMB stor; class SOCKET,WS,VSTREAM strm; class ELFLDR,HELPER,SCE,RECEIVER,APPDB,NOTIF ps5; ``` --- ## 2. Network Services Architecture The daemon exposes three network services on separate TCP ports to isolate interactive UI/API traffic from package streaming and Direct Install uploads. During helper launch it also opens a loopback-only IPC listener on port 18843; elfldr is a separate service on port 9021. | Port | Implementation | Primary Role | Features | |:---|:---|:---|:---| | **8844** | `libmicrohttpd` (MHD) | UI & REST API | Serves bundled React SPA, handles JSON REST endpoints, and serves cached package icons. | | **18841** | Raw BSD Sockets (`stream_server.c`) | Virtual Stream Pipeline | Serves HTTP/1.1 206 Partial Content byte ranges directly to the PS5 background installer with custom socket timeouts and `MSG_NOSIGNAL` sends. | | **18842** | Raw WebSocket listener (`ws_upload.c`) | Direct Install Upload | Receives PKG chunks from a LAN browser and feeds the RAM-backed live stream session used by the installer. | ### REST API Endpoints (Port 8844) The primary web server handles all interactive user requests: - **UI & Static Assets**: `/`, `/index.html`, `/favicon.svg`, `/icon.png`, `/cache.appcache`. - **System & Version**: `/api/version`, `/api/storage`, `/api/settings` (GET & POST), `/api/log`, `/api/debug`. - **Package Discovery**: `/api/drives`, `/api/packages`, `/api/packages/quick-scan`, `/api/packages/refresh`, `/api/scan/status`. - **Package Icons**: `/api/icon` (extracts or serves cached `icon0.png`), `/api/icon-error`. - **Installation Control**: `/api/install` (starts installation), `/api/cancel` (aborts current installation), `/api/poll` / `/api/status` (live progress), `/api/shortcut/install`. - **Direct Install Control**: `/api/upload/init`, `/api/upload/status`, `/api/upload/icon`, `/api/upload/finish`, and `/api/upload/cancel`. - **SMB & Metadata Cache**: `/api/smb/test`, `/api/smb/shares` (enumerate shares on a server), `/api/smb/browse` (64 folders/PKGs per page, with an `after` cursor and `next_cursor` response), `/api/smb/inspect` (metadata and install eligibility for one file), `/api/cache/stats`, `/api/cache/clear`. - **Leftovers Cleanup**: `/api/leftovers` (scans unlinked patches/DLCs), `/api/leftovers/delete` (purges selected orphans). ### Range Streaming Server (Port 18841) The dedicated stream server (`stream_server.c`) handles package delivery to the PS5 background installer: - Listens on raw BSD sockets at port 18841. - Serves HTTP/1.1 `206 Partial Content` (and `HEAD`) responses directly via the virtual stream engine (`virtual_stream_read()`). - Uses socket timeouts (`SO_RCVTIMEO`/`SO_SNDTIMEO`) and `MSG_NOSIGNAL` to handle client disconnects cleanly without process interruption. - Session name pinning: each installation pins an exact session filename (e.g. `package--.pkg`). Any stray or unexpected requests immediately return `404 Not Found`. - Operates completely independently from `libmicrohttpd` on port 8844, preventing high-bandwidth installer range requests from degrading UI responsiveness. --- ## 3. Storage Model & Installation Execution PKG Manager defaults to using its virtual HTTP range-streaming endpoint on port 18841 for all package sources (USB, optical disc, SMB network shares, and browser WebSocket uploads): ```text http://127.0.0.1:18841/stream/install/package--.pkg ``` ### Streaming and Offline Installs When the console has an active network connection, PKG Manager streams packages and shows installation progress in the app. USB and disc packages can also be installed without a network connection. In that case, the PS5 system installer handles the package directly from storage. It does not provide progress to PKG Manager, so the app shows a status message and progress can be followed in PS5 Notifications. Cancellation is unavailable while the system installer handles the install. If the network connection is lost during a stream install, PKG Manager can switch USB and disc installs to direct storage mode. ### Key Advantages 1. **No 2x Storage Requirement**: Packages do not need to be staged on internal storage before installation. The console installer extracts data directly to its target location, requiring only the space needed for the installed application itself (1x space) rather than keeping both the raw package and the installed files on disk (2x space). 2. **Full Offline Support**: Consoles without any network connection can still install USB and optical disc packages without failure via the automatic fallback mechanism. 3. **Direct Installation from Network Shares (SMB)**: The native PS5 package installer cannot access Samba/SMB network shares directly. The virtual stream engine bridges network reads into standard HTTP range responses, enabling direct network installation without mounting shares in the OS or copying packages locally first. 4. **Direct Install from a LAN Device**: A browser on a PC or other LAN device uploads a local PKG over WebSocket port 18842. The daemon keeps the upload in a bounded RAM-backed live session and exposes it through the same range-streaming path used by regular installs. 5. **Multi-Part & Optical Disc Swapping**: For packages split across multiple files or optical discs (BD-R, DVD), the virtual stream translates byte offsets across parts on the fly using `pread()`, allowing multi-part installations without pre-reassembling the files. --- ## 4. Multi-Part Format (`PS5MPKG1`) & Virtual Stream Engine The multi-part splitting format allows splitting large packages across standard optical media (BD-R, DVD) or FAT32/exFAT drives. ### Header Specification (4096 bytes) All multi-part slices share a 4KB binary header defined in `include/multipart.h`: ```c #pragma pack(push, 1) typedef struct { char magic[8]; /* "PS5MPKG1" */ uint32_t header_version; /* 1 */ uint32_t part_index; /* 1-indexed (1, 2, ...) */ uint32_t total_parts; /* Total part count */ uint32_t compression_type; /* 0 = raw uncompressed sequential slice */ uint32_t chunk_size; /* Nominal slice block size (e.g. 2MB) */ uint32_t num_chunks_in_part; /* 0 for uncompressed */ uint64_t part_data_size; /* Raw byte length of package slice in this file */ uint64_t total_pkg_size; /* Total size of the original uncompressed PKG */ uint64_t total_archive_size; /* Combined byte size of all part files */ char pkg_filename[256]; /* Original package filename */ char title_id[32]; /* Extracted Title ID (e.g. "CUSA00000") */ char title_name[256]; /* Application Name */ char content_id[64]; /* Content ID */ uint8_t package_uuid[16]; /* Unique 128-bit UUID shared by all parts */ uint32_t icon_offset; /* Offset of embedded icon0.png (4096 in Part 1) */ uint32_t icon_size; /* Length of embedded icon0.png */ char app_version[32]; /* App version (e.g. "v01.00") */ char pkg_type[16]; /* "base", "update", or "dlc" */ uint64_t part_offset; /* Offset in original package where slice starts */ uint32_t data_offset; /* File offset where raw package data begins */ uint8_t reserved[3348]; /* Padding to 4096 bytes */ } multipart_header_t; #pragma pack(pop) ``` ### Storage Strategy Multi-part slices are stored as **raw uncompressed sequential slices** (`compression_type = 0`) to allow direct random-access byte range streaming without decompression overhead. ### Optical Disc Swapping Pipeline When a package spans across multiple optical discs: 1. The virtual stream engine reads slices from Disc 1 (`/mnt/disc`). 2. When the requested offset crosses into Part 2 (which is not mounted), `virtual_stream_read()` triggers the `g_stream_wait_disc_fn` callback. 3. The installer state enters `waiting_disc`, logs the event, and issues a PS5 system notification: `"Please insert Disc 2 of N for "` 4. A background poll checks for optical disc remounting. 5. As soon as Disc 2 is inserted and detected (verified via UUID and Part index match), the stream automatically opens the new part's file descriptor and resumes streaming seamlessly. --- ## 5. PlayStation 5 System Installer Integration The application interfaces directly with the PS5 OS package manager via `/system/common/lib/libSceAppInstUtil.sprx`. ### Function Signatures & Data Layout ```c typedef struct { char languages[30][8]; char playgo_scenario_ids[64][3]; char content_ids[64][48]; unsigned char unknown[6480]; } playgo_info_t; typedef struct pkg_info { char content_id[48]; int type; int platform; } pkg_info_t; typedef struct pkg_metadata { const char *uri; const char *ex_uri; const char *playgo_scenario_id; const char *content_id; const char *content_name; const char *icon_url; } pkg_metadata_t; int sceAppInstUtilInitialize(void); int sceAppInstUtilTerminate(void); int sceAppInstUtilInstallByPackage(const pkg_metadata_t *meta, pkg_info_t *info, playgo_info_t *playgo); int sceAppInstUtilGetInstallStatus(const char* content_id, SceAppInstallStatusInstalled* status); ``` ### Package Metadata & Installation Pipeline Every package submission runs in a fresh `pkgmgr-inst.elf` helper. The helper is built separately and embedded in `pkgmgr.elf`. `install_process.c` sends it to elfldr on `127.0.0.1:9021`, then accepts its callback on `127.0.0.1:18843`. `install_service.c` exchanges fixed-size, pointer-free messages over that TCP connection; the helper owns initialization, one package submission, status polling, and termination of its AppInstUtil session. Retries and queued updates receive new processes. The daemon retains the HTTP/SMB/WebSocket servers, live upload buffers, install state, and browser connection throughout. The upload socket is half-closed after the ELF bytes are sent so elfldr can finish loading while helper stdout remains readable. IPC deadlines, a parent-disconnect watcher, and child termination/reaping handle failed or canceled operations. Shortcut registration uses a separate helper; existing DLC queries and leftover removal retain their independent AppInstUtil client in the daemon. When invoking `sceAppInstUtilInstallByPackage`, `pkg_metadata_t` is populated as follows: - **`uri`**: Defaults to a unique per-install streaming URL (`http://127.0.0.1:18841/stream/install/package-<unixtime>-<seq>.pkg`) for all sources. If an offline fallback occurs for a USB or disc package (`0x80B21121`), the direct filesystem path (e.g. `/mnt/usb0/pkg/game.pkg`) is passed instead. Timestamping prevents URI collisions across successive stream installations. - **`content_name`**: Formatted as `"<Title ID> (<Kind>)"` (e.g. `"CUSA00000 (Base)"` or `"CUSA00000 (Update)"`). - **`content_id`**: Passed as an empty string `""`; the system installer reads the initial package header to populate `pkg_info.content_id`. - **`ex_uri`**, **`playgo_scenario_id`**: Passed as empty strings `""`. - **`icon_url`**: The HTTP icon URL `http://127.0.0.1:18841/stream/install/icon-<unixtime>-<seq>.png` for stream installs when an icon is extracted, or `""` for direct storage installs (preventing offline `0x80B21121` PlayGo network errors and using the internal `icon0.png`). ### Host-Side PS5 Stream Simulator To develop install methods (websocket client, direct stream creation) without a console, the PS5's request pattern against the stream server is reproduced on the host. The pattern, reverse-engineered from the captures in `.for_reference/stream_debug/`, has three phases: 1. **Header acquisition** — a burst of short-lived connections, each re-reading the first 64 KiB (`Range: bytes=0-65535`) so the installer can parse / re-validate the package header. 2. **Sidecar CRC probe** — a single Range-less GET of `<content_id>.crc` that must return `404` when there is no companion file. 3. **Bulk transfer** — two parallel long-lived connections serving contiguous 16 MiB byte-ranges across `[65536, end)` in an A/B ping-pong. Every request carries the query the console appends (`?product=0287&serverIpAddr=127.0.0.1&r=00000000`) against `:18841`. The pure client half is `tests/ps5_sim.c` (raw HTTP/1.1, no dependency on the server). `tests/test_stream_sim.c` pairs it against the real `src/stream_server.c` and asserts the model holds; it is part of `make test`. The standalone CLI (`tools/ps5_installer_sim.c`, launched via `tools/run_stream_sim.sh`) can either start its own local server or point the same replay at any live server — exactly the shape a future websocket / remote install method needs. A correct HTTP/1.1 keep-alive reader is essential here: absorbing body bytes into the header buffer desyncs a persistent connection and makes the exact body read block on bytes the server already sent. ## 6. Direct-Install Upload Path ``` LAN browser (DirectInstallView) --ws://:18842--> ws_upload.c --RAM ring--> virtual_stream ("live:<id>") --:18841--> system installer (submitted by fresh helper) ``` The browser's `segmentSender.js` keeps up to two segments in flight when the server advertises `upload_window: 2`, otherwise one. It also prepares the next file read while uploads await acknowledgement. Servers advertise `demand_window` in the ready reply: the browser sends the pinned header first, then waits for installer seeks. Each seek queues up to eight segments starting at the requested segment (window = min(8, max(1, RAM slots / 4))). Explicit seeks take priority; speculative queued segments are capped at twice the window, and already ACKed segments are only resent on explicit demand. Older servers without the field retain sequential upload. A ping/pong heartbeat keeps demand-mode pauses alive. Seeks for any in-flight segment are coalesced, while seeks arriving after its acknowledgement are honored because the RAM cache may have evicted it. Busy responses retry the same buffer, even when that segment was acknowledged during an earlier upload. Debug `WS_CACHE` rows report cumulative byte counters, sampled at most once per second and flushed at close: `duplicate_bytes` counts accepted uploads already resident; `reloaded_bytes` counts accepted uploads previously stored but evicted; `unread_evicted_bytes` counts evicted segments with no bytes read during that residency. A partially read segment is not counted as unread. These distinguish cache churn from physical receive throughput without per-segment log spam. `WS_SENDER` records cumulative browser timings once per second: `read_wait_us` is time actually awaiting file data in the sender (a prepared read usually adds almost zero); `ack_latency_us` sums send-to-successful-ACK latency across `ack_count` uploads, including transfer time. These latencies overlap with two segments in flight and are not an elapsed-time breakdown or a pure network RTT. `sent_count` includes retries, and `window` records the negotiated upload limit. Counters are scoped to the browser sender and reset on reconnect. The log flushes the latest received snapshot at close; browser reports after log closure cannot be included, so counts may omit the final acknowledgements. `src/ws_upload.c` is transport only; `src/ws_stream.c` owns the bytes (1 MB pinned header + configurable ring, 64 MB by default, `ws_live_*` symbols, no load-time side effects, abort/timeout on every wait). The only touch points in existing code are additive: a `live:` scheme branch in `virtual_stream_open`/`read` (`multipart.c`), one `pkg_parser_parse_mem()` function reusing the in-file sub-parsers, one `installer_start_live()` entry plus abort/destroy hooks (`installer.c`), and a guarded `/api/upload/` REST branch plus `live:` URI routing in `/api/install` (`http_server.c`). `src/stream_server.c` has zero diff, so the pull contract verified by `test_stream_sim` still holds byte-for-byte. Host proof: `test_ws_stream`, `test_parse_mem`, `test_ws_upload`, `test_direct_install_e2e`, and `tools/run_direct_install_sim.sh --demo` (fragmented socket push racing the standard `ps5_sim` pull replay, byte-exact). --- ## 7. Package Parser & Metadata Engine (`pkg_parser.c`) The package parser inspects packages directly on storage media using random-access `pread()`: - **Format Detection**: - `\x7fCNT` (PS4 standard package header): parsed directly at file offset 0. - `\x7fFIH` (PS5 native package header): outer container format. Locates the internal CNT header at offset `0x58` or by scanning FIH table entries. - `PS5MPKG1` (Multi-part package header): parsed at offset 0. - **Title ID & Content ID**: Read directly from the CNT header (offset `0x40`) or multi-part header. - **SFO & JSON Parser**: Reads `param.sfo` (for PS4 titles) or `param.json` (for PS5 native titles) from the CNT entry directory table to extract localized Title Name, Application Version, and Category. - **Package Category Classification**: - **Update**: `category` starting with `gp`, presence of playgo chunk patch (`app/playgo-chunk.dat` / entry `0x1008`), delta patch entries (`0x0407`, `0x0408`), or delta content type magic (`0x1E`, `0x41000000`). - **DLC / Extra Content**: `category` starting with `ac`, `al`, equal to `addcont`, or a CNT type whose low byte is `1` when `param.json` does not contain the standard base-application metadata fields. This avoids treating normal base packages that share the CNT value as DLC. - **Base App**: `category` starting with `gd`, `bd`, `gc`, `wt`, or default fallback when no update/DLC indicators are present. - **Icon Extraction**: Locates uncompressed `icon0.png` data (entry type `0x1200` or named `icon0.png`) and serves it via `/api/icon?path=...`. --- ## 8. Storage & Drive Scanning (`pkg_scanner.c`) The scanner monitors all mount points and builds the unified catalog: 1. **Drive Discovery**: - Checks USB mount points `/mnt/usb0` through `/mnt/usb7`. - Checks optical disc mount point `/mnt/disc`. - Scans configured SMB network shares via `smb_client.c`. 2. **Scan Scope**: - Regular files directly in the drive root (`/mnt/usbX/*.pkg`). - Recursive scan into the designated `/pkg/` subfolder (`/mnt/usbX/pkg/...`). - Files located elsewhere on external drives are excluded to avoid scanning entire personal drives. 3. **Multi-Part Deduplication**: - Scans and validates multi-part slices. - Displays only Part 1 in the UI package list with a "Disc 1 of N" badge. Secondary parts (`.part2..N`) are indexed internally for disc swapping but hidden from the main list. 4. **Install Capacity**: - Reports available capacity for internal, M.2, and extended USB storage (`/mnt/ext0`). - Applies the supported storage targets when checking whether a package can be installed; PS4 packages can use internal, M.2, or extended USB storage, while PS5 packages use internal or M.2 storage. --- ## 9. Installed Application Database (`app_info.c`) To prevent duplicate installations and guide the user: - Queries the PS5 system SQLite database at `/system_data/priv/mms/app.db` (table `tbl_appinfo`). - Checks base application files in `/user/app/<TitleID>/` (`sce_sys/param.sfo`, `sce_sys/param.json`, `eboot.bin`), `/system_ex/app/<TitleID>/`, and external storage (`/mnt/ext0/user/app/`). *(Note: Does not treat `/user/appmeta/` as proof of base installation, as appmeta directory trees persist after game uninstallation to preserve UI icons and trophy metadata caches).* - For DLCs, queries `sceAppInstUtilGetAddcontInstalledStatus`, `/system_data/priv/mms/addcont.db`, or checks `/user/addcont/<TitleID>/`. - **UI Logic**: - If a base package is already installed, the scanner compares the installed version with the package version: - If the package version is newer (`installed_version < pkg->app_version`), installation is allowed (`can_install = 1`) to support cumulative base upgrades. - If the installed version is the same or newer, installation is disabled with `"Installed version is same or newer"`. - If version information is unavailable, installation is disabled with `"Application is already installed"`. - If an update is detected, the UI verifies that the base package is installed and compares the update version against the currently installed version. --- ## 10. Manifest Caching & Quick Rescan (`pkg_scanner.c`) To eliminate slow cold scans upon daemon restart and drive navigation: 1. **Manifest Persistence**: The discovered catalog of drives, files, and parsed metadata is serialized to `/data/pkgmgr/manifest.json` (or `/tmp/pkgmgr_manifest.json`). 2. **Instant Warm Startup**: When the daemon launches, `pkg_scanner_init()` checks for a valid manifest and immediately restores the full catalog without reading external media. 3. **Quick Rescan (`scan_quick_single_source`)**: - Gathers file names, sizes, and modification timestamps via `stat()` / `readdir()`. - Compares the file list against recorded entries: - If identical: skips re-parsing entirely. - If modified or added: re-parses only the affected packages. - If removed: purges the missing entries from the in-memory catalog and manifest. 4. **Background Rescan Polling**: The frontend periodically checks individual local drives. SMB and all-sources views are excluded from the 15-second timer. Startup, navigation and resume can still trigger a quick scan; `browse_only` shares are skipped in all scan paths. 5. **Full Scan Jobs**: `POST /api/packages/refresh` returns HTTP 202 with `{status:"accepted", started:true|false}`. Admission is shared with quick scans; an active job is never queued again. Poll `/api/scan/status` until `is_scanning` is false and check `failed_sources`. Status becomes active before counting. SMB enumeration finishes before metadata parsing; the catalog mutex is held only for catalog access, not network reads. Browser reloads attach to status polling. 6. **Cache Invalidation**: The frontend compares the backend version from `/api/version` with the last version it saw. After an update, it clears the local cache through `/api/cache/clear` and runs a full rescan. A manual cache clear also refreshes the package catalog. --- ## 11. Orphaned Leftover Detection & Cleanup (`leftovers.c`) When base games are deleted, or after interrupted uninstalls, unreferenced patch or DLC data can remain on storage without being tracked by `app.db`: 1. **Detection**: - Scans candidate title directories exclusively in `/user/patch`, `/user/patch0`, and `/user/addcont`. Candidate scanning strictly avoids `/user/appmeta/` because appmeta caches trophy data for every title ever launched on the console. - For each discovered title ID, verifies whether the base game is actually installed on disk (in `/user/app/`, `/system_ex/app/`, `/mnt/ext0/user/app/`) or registered in `app.db`. - If the base game is absent, it identifies the orphaned patch, addcont, and associated appmeta directory trees for that specific title. 2. **Safety Gates**: - Deletion is strictly blocked if the base package or title record is active in `app.db` or exists on disk. - The user is provided with exact byte sizes and paths before confirming deletion. --- ## 12. SMB Network Share Storage & Caching (`smb_client.c`, `pkg_cache.c`) PKG Manager includes an embedded SMB2 client (`libsmb2`) allowing direct package installation over the local network without mounting network drives: 1. **Share Discovery & Connection**: Connects to user-configured SMB shares with credentials stored in `/data/pkgmgr/settings.json`. 2. **Local Metadata & Icon Cache (`pkg_cache.c`)**: - Computes a CRC32 checksum over the package header. - Caches parsed metadata (`meta.json`) and extracted `icon0.png` locally on the console in `/data/pkgmgr/cache/<checksum>/` (or `/tmp/pkgmgr/cache/`). - Remote SMB shares are treated as read-only; no cache files are written to network shares. - Subsequent scans load metadata and icons instantly from the local cache, avoiding network roundtrips. 3. **Streaming Installation**: Packages on SMB shares are streamed via virtual range requests directly into the installation pipeline using `smb_file_session_read()`. Reads are pipelined and credit-aware to reduce network latency; ideal local-network installations have reached approximately 110 MB/s.