Compare commits

..
10 Commits
Author SHA1 Message Date
ManosandDevin 6501452103 docs: tidy code comments
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-10 11:49:00 +03:00
ManosandDevin 367e57ca1f v7.3.0: full k-stuff compatibility + power/launch fixes
- Full k-stuff support: removed all etaHEN gates — mount/unmount,
  launch game, controller info/lightbar, PKG install, suspend/resume/kill
  now work on both loaders
- Launch Game: handle async daemon launch — poll for target eboot.bin
  after error codes, close running game via sceLncUtilKillApp and retry
  (fixes issue where launch "failed" but game actually started)
- Launch kill-heuristic: running game = comm=="eboot.bin" only;
  numeric-title system daemons are never killed
- Power: reboot via sceSystemServiceRequestReboot, shutdown via
  sceSystemStateMgrTurnOff + ShellCore fallbacks; raw reboot() syscall
  only as last resort (was leaving console in zombie state)
- Controllers: ShellCore/ShellUI remote pad bridge works without
  etaHEN — info, state and lightbar verified live on k-stuff
- Mount/unmount: ShellCore code-cave bridge (installTitleDir /
  AppUnInstall) — icons appear/disappear without home-screen kills
- Removed Capture Screen: sceScreenShotCapture needs a registered
  app-thread context (0x80C1B001 local / 0x80BE0001 remote); Screenshots
  gallery (list/download/delete) unaffected
- Unmount All: only touches suite mounts (mount.lnk / active nullfs),
  real installs never affected

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-10 11:39:12 +03:00
ManosandDevin 80b75d30eb Fix kstuff mount/unmount: refresh home only on real changes
Mount/unmount under kstuff wrote app.db directly and killed SceShellUI
on every invocation — including repeat mounts that changed nothing.

- Skip path now heals only genuinely missing registration rows, and
  separately bumps stale icon rows whose lastAccessIndex is 0 (they
  sorted past the home row's visible edge, so mounted games never
  showed on screen).
- New registrations get lastAccessIndex = MAX+1 so tiles land at the
  front like real installs.
- ShellUI restart now triggers only when mounts/cleanups/heals actually
  changed state; unmount refreshes only if a registry row existed.

Verified on 13.60 kstuff: repeat mount = no refresh, unmount clears
mount+dirs+all three app.db tables, remount registers at index 450.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-10 00:30:19 +03:00
ManosandDevin 1f80816b5a Trophies: pin earned state at file level for progressive trophies
Verified live under kstuff: the daemon awards non-progressive trophies
from _UnlockTrophy UDS posts (firing the real system toast), but ignores
every path for progressive:true trophies — custom counter events,
_UpdateTrophyProgress and _UnlockTrophy all return 0 yet the trophy
stays progress-only (flag 0x10, no mask bit).

trp_unlock_file() now mirrors trp_lock_file(): sets the bit in the
trophy's owning group mask (groupId parsed from tropconf.json), updates
the 0x800 row to the exact daemon layout (flag 0x11, 0x2000 marker,
big-endian µs-since-0001 timestamp), refreshes union-mask copies, and
keeps a .bak of the pre-edit file. uds_trophy_unlock() runs it after the
UDS posts — a no-op when the daemon already awarded, so legitimate
earns and their toasts are untouched.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-09 19:42:52 +03:00
ManosandDevin fa4635cadb Trophies: fix list/sidebar mismatch on multi-group sets + lock backup
The TRPTITLE.DAT fallback branch (no single window matching the union
popcount — happens when unlocks span multiple 0x700 group records) applied
only 0x800 row flags to per-trophy state while the sidebar still counted
the union mask. Result: sidebar showed e.g. 39/40 while the list showed
dozens of mask-unlocked trophies as "Locked" — the exact mismatch reported
in issue #11. The union of group masks is the authoritative state, so it
is now applied directly in that path.

Also: trp_lock_file() now keeps a TRPTITLE.DAT.bak of the pre-edit file so
a daemon-side regeneration can't silently wipe the user's trophy data.

Verified live on K-Stuff (Astro Bot): single unlock posts correctly,
lock clears only the target bit, .bak is created, and closing/relaunching
the game does not regenerate the file.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-09 19:05:02 +03:00
ManosandDevin 49faaf1ba6 Fix port documentation: injection default is 9021, server scans 9113-9118
- README said 9020; the actual default is 9021 (etaHEN listener)
- suite server range corrected to 9113-9118
- send-payload failure message now mentions etaHEN/GoldHEN generically

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-08 12:26:50 +03:00
ManosandDevin b2261ee5f8 README: remove removed Unlock All, fix mojibake headers
- drop the "Unlock All" bullet from Trophies (feature removed —
  per-trophy unlock/lock only)
- repair three corrupted characters in section headers and the
  Beeper bullet

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-08 12:24:03 +03:00
ManosandDevin 6dce564861 Switch donation link to Ko-fi
Replace Buy Me a Coffee with Ko-fi (0% platform fees):
- in-app support button now opens ko-fi.com/manosmourtzakis
  with Ko-fi red styling
- README badge swapped to Ko-fi

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-08 11:53:45 +03:00
ManosandDevin 05315d6966 README: add Ko-fi donation badge
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-08 11:46:49 +03:00
ManosandDevin 33d44f429d README: add Discord community badge
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-08 10:25:17 +03:00
25 changed files with 3972 additions and 2825 deletions

No files matched your search

+25
View File
@@ -4,6 +4,31 @@ All notable changes to **PS5 Suite** (client app + on-console payload).
---
## v7.3.0 (October 2026)
### New Features
- **Unmount All button** — removes every mounted game in one action, with confirmation dialog and per-title progress. Only suite-mounted games are touched (verified by `mount.lnk` marker or active `/system_ex/app` overlay) — real installed titles are never affected.
### Major Improvements
- **Full k-stuff compatibility** — every feature previously gated behind etaHEN now works on k-stuff too: mount/unmount, launch game, controller info + lightbar, PKG installs, suspend/resume/kill.
- **Mount/unmount no longer kills the home screen** — on kstuff firmwares 12.00+, game registration now runs through SceShellCore's own `installTitleDir` via an injected code-cave bridge (offsets verified per-firmware). The daemon writes the registry rows and notifies the UI itself, so icons appear and disappear instantly with zero renderer restarts.
- **Unregister via `sceAppInstUtilAppUnInstall`** — official daemon IPC removes the icon live; direct SQLite writes remain only as a watchdog-guarded fallback.
- **Launch Game works on k-stuff** — removed the etaHEN-only gate. `sceLncUtilLaunchApp` error codes are now handled correctly: the daemon launches asynchronously, so the payload polls for the target's `eboot.bin` process (up to ~8s) and reports real success with the actual app_id. If another game holds the foreground, it is closed via `sceLncUtilKillApp` and the launch is retried — same as the PS5 UI's "close game?" flow.
- **Power actions use official daemon paths** — reboot/shutdown no longer call the raw `reboot()` syscall (which halted the kernel but left the console in a zombie state that needed a power-plug pull). Reboot now goes through `sceSystemServiceRequestReboot`; shutdown through `sceSystemStateMgrTurnOff` with `sceShellCoreUtilRequestShutdown`/`RequestPowerOff` fallbacks. Raw syscall kept only as last resort.
- **Controller info + lightbar on k-stuff** — removed the etaHEN gate; the ShellCore/ShellUI remote pad bridge (ptrace + remote calls inside a process that owns a real pad session) works on both loaders. Verified live with real analog/button data.
### Bug Fixes
- Fixed mounted-game icons landing past the visible edge of the home row (`lastAccessIndex` now uses MAX+1 like real installs; stale index-0 rows are healed automatically).
- Fixed `proc_find_name` ENOMEM race that could fail process lookups.
- Stale ShellCore bridge hooks are detected and recovered instead of failing the registration.
- Fixed launch kill-heuristic matching system daemons with numeric title IDs — a running game is now identified strictly by `comm == "eboot.bin"`; system processes (SceCdlgApp, ShellUI) are never touched.
- Trophy unlock/re-lock fixes from v7.2.x verified under k-stuff (fixes issue #11).
### Removed
- **Capture Screen** — removed from payload and both clients. `sceScreenShotCapture` requires a registered app-thread context the payload cannot satisfy: local calls return `0x80C1B001` and remote calls inside ShellUI/ShellCore/capture daemons/`eboot.bin` all return `0x80BE0001`. Rather than a button that can never work, the feature is gone; browsing/downloading/deleting existing captures in the Screenshots tab is unaffected.
---
## v7.2.4 (October 2026)
### Bug Fixes
+12 -10
View File
@@ -10,8 +10,11 @@ All-in-one management suite for jailbroken PS5 consoles. One app — on **Window
**This project is built for the PS5 scene, for free — if it saved you time or you just like what we do, consider buying us a coffee. Every cup goes straight back into new features, fixes and payload wizardry.** 🍻
[![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://buymeacoffee.com/manos555555)
**Got questions, ideas or found a bug? Join the community on Discord — we hang out there!**
[![Ko-fi](https://img.shields.io/badge/Ko--fi-FF5E5B?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/manosmourtzakis)
[![Follow on X](https://img.shields.io/badge/Follow%20on%20X-000000?style=for-the-badge&logo=x&logoColor=white)](https://x.com/manos_moyrtzis)
[![Join our Discord](https://img.shields.io/badge/Join%20our%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/eRXG2Csmpy)
</div>
@@ -46,8 +49,8 @@ All-in-one management suite for jailbroken PS5 consoles. One app — on **Window
## 🚀 Setup (2 minutes)
1. Load a jailbreak / payload loader on the PS5 — tested with **etaHEN** and **Kstuff** (any payload loader that accepts ELFs works).
2. Open the app → expand **⚙️ Payload Settings** → **📂 Browse** → pick `payload/ps5_suite_server.elf` → **📤 Send Payload** (default port 9020).
1. Load a jailbreak / payload loader on the PS5 — **fully compatible with both etaHEN and Kstuff** (any payload loader that accepts ELFs works). All features including mount/unmount, launch game and DualSense control work on k-stuff.
2. Open the app → expand **⚙️ Payload Settings** → **📂 Browse** → pick `payload/ps5_suite_server.elf` → **📤 Send Payload** (default port 9021).
3. The payload installs itself and starts the suite server (ports 9113–9116).
4. Press **🔍** to auto-discover the console on your LAN, or type its IP → **🔌 Connect**.
5. Done. Future payload updates go through **🔄 Self-Update Payload** (pushes the new ELF through the running server) or **⬆️ Update from GitHub**.
@@ -63,8 +66,8 @@ All-in-one management suite for jailbroken PS5 consoles. One app — on **Window
- **Auto-discover (🔍)** — broadcasts on the LAN and finds the PS5 automatically.
- **Profiles** — save multiple consoles by name, delete them, reconnect instantly.
- **Storage Info panel** — free/total space on the console's drive, **🔄 Refresh Storage** button.
- **Payload Settings** — pick the ELF, set the injection port (default 9020), **Send Payload**, **Self-Update Payload** (updates the running server in place), **Update from GitHub** (checks for new suite releases).
- **Buy Me a Coffee** link in the header. ❤️
- **Payload Settings** — pick the ELF, set the injection port (default 9021), **Send Payload**, **Self-Update Payload** (updates the running server in place), **Update from GitHub** (checks for new suite releases).
- **Ko-fi support link** in the header. ❤️
---
@@ -104,7 +107,7 @@ Two side-by-side panes: your PC on the left, the PS5 on the right.
---
### � GAMES → Homebrew Store
### 🛍️ GAMES → Homebrew Store
- Built-in **pkg-zone.com** catalog: browse/search every public PS5 homebrew package.
- Covers, version + author info, one-tap **📥 Install** — the package streams straight to the console installer.
@@ -123,7 +126,6 @@ Two side-by-side panes: your PC on the left, the PS5 on the right.
- Lists every trophy set on the console (per game) with real data parsed from TRPTITLE.DAT — names, descriptions, grades (bronze/silver/gold/platinum), icons, hidden status and earned timestamps.
- **🔓 Unlock** — awards a trophy live through the running game (the matching title must be running — the app tells you if it isn't).
- **🔒 Lock** — re-locks a trophy by rewriting the set's unlock bitmasks in place (group masks, row flags and timestamps cleared) — as if it was never earned.
- **📥 Unlock All** — batch-unlock every trophy in the set in one click.
- Search/filter across sets, live refresh after every change.
> ⚠️ **Warning:** unlocking many trophies with unrealistically short/identical timestamps can look suspicious if the console ever syncs to PSN. **I take no responsibility for any ban on your account or console** — use this feature offline and at your own risk.
@@ -131,7 +133,7 @@ Two side-by-side panes: your PC on the left, the PS5 on the right.
---
### �💾 SAVES & MEDIA → Saves
### 💾 SAVES & MEDIA → Saves
- Enumerates all save data on the console (per user, per title) with size/type badges.
- **🔓 Mount (decrypt)** — mounts the save decrypted on the PS5 so its files are readable.
@@ -183,7 +185,7 @@ Everything is read **live from the console** — nothing hardcoded; values the c
- **🎮 Controller (DualSense)** — live pad state + controller info read through a remote bridge into `SceShellUI` (the payload gets no pad session of its own, so it resolves the real logged-in user remotely — nothing hardcoded). **Lightbar**: set any RGB color.
- **💡 Console LED** — real `/dev/icc_indicator` interface with three hardware channels (blue `0x01` / white `0x11` / amber `0x21` — verified on hardware): pick a color, run effects (breathe, sunrise, blink, chase…), `auto` hands control back to the system.
- **🔔 Notify** — push a custom notification to the PS5 screen.
- ** Beeper** — console beep / mute.
- **🔊 Beeper** — console beep / mute.
---
@@ -261,7 +263,7 @@ Everything is read **live from the console** — nothing hardcoded; values the c
| On-console payload | C, built with the **PS5 Payload SDK** (`prospero-clang`) |
| FPKG engine | **LibProsperoPkg** (bundled) — CNT/PFS/PFSC/PFSv3/FIH build, read & verify |
| Transfer protocol | Raw TCP, chunked + parallel streams |
| Suite server ports | 9113–9116 |
| Suite server ports | 9113–9118 (scans until it finds the live server) |
| Local PKG streamer ports | 18990–19009 |
| Payload proxy port | 13801+ |
@@ -5,8 +5,8 @@
<SupportedOSPlatformVersion>23</SupportedOSPlatformVersion>
<Nullable>enable</Nullable>
<ApplicationId>com.manos.ps5suite</ApplicationId>
<ApplicationVersion>8</ApplicationVersion>
<ApplicationDisplayVersion>7.2.4</ApplicationDisplayVersion>
<ApplicationVersion>9</ApplicationVersion>
<ApplicationDisplayVersion>7.3.0</ApplicationDisplayVersion>
<ApplicationTitle>PS5 Suite</ApplicationTitle>
<AndroidPackageFormat>apk</AndroidPackageFormat>
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
@@ -3,7 +3,7 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<Version>7.2.4</Version>
<Version>7.3.0</Version>
</PropertyGroup>
<ItemGroup>
@@ -85,7 +85,6 @@ namespace PS5Upload
PowerAction = 0x77,
UsbList = 0x78,
PadInfo = 0x79,
Screenshot = 0x7B,
Notify = 0x7C,
PadAction = 0x7D,
IccControl = 0x7E,
@@ -2623,26 +2622,6 @@ namespace PS5Upload
}
}
public async Task<(bool success, string message)> CaptureScreenshotAsync()
{
await _commandLock.WaitAsync();
try
{
await SendCommandAsync(Command.Screenshot);
var (response, data) = await ReceiveResponseAsync(20000);
string msg = data.Length > 0 ? Encoding.UTF8.GetString(data).TrimEnd('\0', '\n', '\r') : response.ToString();
return (response == Response.Ok, msg);
}
catch (Exception ex)
{
return (false, ex.Message);
}
finally
{
_commandLock.Release();
}
}
public Task<(bool success, string message)> NotifyAsync(string text)
=> SendTextCommandAsync(Command.Notify, text);
@@ -278,18 +278,16 @@ public partial class MainView
}
if (found == null)
{
set.EarnedFallback = unionPop;
if (rowState.Count == 0) return;
foreach (var t in set.Trophies)
if (rowState.TryGetValue(t.Id, out var rs))
{
t.StateKnown = true;
t.IsUnlocked = rs.unlocked;
t.UnlockedTime = rs.when == default ? null : rs.when;
}
return;
// Multi-group sets: no single window carries the union, so the
// popcount scan misses. The union of the 0x700 group masks IS
// the authoritative per-trophy state — apply it directly
// instead of rows-only (which made list disagree with sidebar).
mask = unionMask;
}
else
{
mask = found;
}
mask = found;
}
set.UnlockMask = mask;
@@ -415,8 +415,6 @@
<TextBlock Text="📸 Screenshots" Foreground="#0E9CFF" FontWeight="Bold" FontSize="14" VerticalAlignment="Center"/>
<Button Grid.Column="1" Content="🔄" Background="#3E3E42" Padding="8" Click="ScreenshotsRefreshButton_Click"/>
</Grid>
<Button Content="📸 Capture Screenshot" Background="#007ACC" Foreground="White"
Padding="12" HorizontalAlignment="Stretch" Click="CaptureScreenshotButton_Click"/>
<ListBox x:Name="ScreenshotsListBox" Background="#1E1E1E" MaxHeight="260">
<ListBox.ItemTemplate>
<DataTemplate>
@@ -644,19 +644,6 @@ public partial class MainView : UserControl
catch (Exception ex) { UpdateStatus($"Screenshots error: {ex.Message}"); }
}
private async void CaptureScreenshotButton_Click(object? sender, RoutedEventArgs e)
{
if (!RequireConnection()) return;
UpdateStatus("Capturing screenshot on PS5...");
try
{
var (ok, msg) = await _protocol!.CaptureScreenshotAsync();
UpdateStatus(ok ? $"📸 {msg}" : $"Screenshot failed: {msg}");
if (ok) await Task.Delay(1500).ContinueWith(_ => Dispatcher.UIThread.Post(() => ScreenshotsRefreshButton_Click(null, null!)));
}
catch (Exception ex) { UpdateStatus($"Screenshot error: {ex.Message}"); }
}
private async void ScreenshotDeleteButton_Click(object? sender, RoutedEventArgs e)
{
if (!RequireConnection()) return;
+4 -18
View File
@@ -318,7 +318,7 @@ namespace PS5Upload
else
{
Log("❌ Failed to send payload");
await ShowMessageAsync($"Failed to send payload to {ipAddress}:{_payloadPort}\n\nMake sure GoldHEN is running.", "Connection Failed");
await ShowMessageAsync($"Failed to send payload to {ipAddress}:{_payloadPort}\n\nMake sure a payload loader (etaHEN/GoldHEN) is listening on that port.", "Connection Failed");
}
SendPayloadButton.IsEnabled = true;
}
@@ -639,13 +639,13 @@ namespace PS5Upload
}
// ============================================================
// BUY ME A COFFEE
// KO-FI SUPPORT LINK
// ============================================================
private void BuyMeCoffeeButton_Click(object? sender, RoutedEventArgs e)
{
try
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo { FileName = "https://buymeacoffee.com/manos555555", UseShellExecute = true });
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo { FileName = "https://ko-fi.com/manosmourtzakis", UseShellExecute = true });
Log("☕ Thank you for your support!");
}
catch (Exception ex) { Log($"❌ Failed to open link: {ex.Message}"); }
@@ -1559,7 +1559,7 @@ namespace PS5Upload
}
// ============================================================
// LIGHT BAR / VIBRATION / SCREENSHOT / NOTIFY / LED / BEEPER
// LIGHT BAR / VIBRATION / NOTIFY / LED / BEEPER
// ============================================================
private async void LightBar_Click(object? sender, RoutedEventArgs e)
{
@@ -1573,20 +1573,6 @@ namespace PS5Upload
catch (Exception ex) { Log($"❌ Light bar error: {ex.Message}"); }
}
private async void Screenshot_Click(object? sender, RoutedEventArgs e)
{
if (!_protocol.IsConnected) { await ShowMessageAsync("Not connected to PS5", "Error"); return; }
try
{
Log("📸 Requesting screenshot...");
var (success, message) = await _protocol.CaptureScreenshotAsync();
Log(success
? $"✅ Screenshot captured ({message}) — find it under Saves & Media → Screenshots"
: $"❌ Screenshot failed: {message}");
}
catch (Exception ex) { Log($"❌ Screenshot error: {ex.Message}"); }
}
private async void SendNotify_Click(object? sender, RoutedEventArgs e)
{
if (!_protocol.IsConnected) { await ShowMessageAsync("Not connected to PS5", "Error"); return; }
+22
View File
@@ -497,6 +497,28 @@ namespace PS5Upload
}
}
private async void UnmountAllGamesButton_Click(object? sender, RoutedEventArgs e)
{
if (!_protocol.IsConnected) { await ShowMessageAsync("Not connected to PS5", "Error"); return; }
if (!await ShowConfirmAsync("Unmount ALL games?\n\nEvery mounted game will be removed from the PS5 home screen.")) return;
UnmountAllGamesButton.IsEnabled = false;
UnmountAllGamesButton.Content = "⏳ Unmounting...";
Log("🗑️ Unmount All: Starting...");
try
{
var (success, message) = await _protocol.UnmountAllGamesAsync(msg =>
Dispatcher.UIThread.Post(() => Log($" {msg}")));
if (success) { Log($"✅ {message}"); await RefreshGameListAsync(); }
else { Log($"❌ Failed: {message}"); await ShowMessageAsync($"Unmount all failed:\n{message}", "Error"); }
}
finally
{
UnmountAllGamesButton.Content = "🗑️ Unmount All";
UnmountAllGamesButton.IsEnabled = true;
}
}
private async void OpenGamePathMenuItem_Click(object? sender, RoutedEventArgs e)
{
if (MountedGamesListBox.SelectedItem is PS5MountedGame game)
+11 -14
View File
@@ -309,21 +309,18 @@ namespace PS5Upload
}
if (found == null)
{
// Counts known, per-trophy flags unknown — keep "—" per row.
set.EarnedFallback = unionPop;
// Still apply verified per-row flags from the 0x800 table
// where present (Witcher file: flags matched the mask 100%).
if (rowState.Count == 0) return;
foreach (var t in set.Trophies)
if (rowState.TryGetValue(t.Id, out var rs))
{
t.StateKnown = true;
t.IsUnlocked = rs.unlocked;
t.UnlockedTime = rs.when == default ? null : rs.when;
}
return;
// Multi-group sets: no single window carries the union, so
// the popcount scan misses. The union of the 0x700 group
// masks IS the authoritative per-trophy state (it is what
// the console reads) — apply it directly instead of
// falling back to rows-only, which made the list disagree
// with the sidebar count (sidebar=unionPop, list=rows).
mask = unionMask;
}
else
{
mask = found;
}
mask = found;
}
set.UnlockMask = mask;
+4 -7
View File
@@ -161,11 +161,11 @@
<!-- Bottom Actions -->
<StackPanel Grid.Row="3" Margin="10,10,10,15">
<Button x:Name="BuyMeCoffeeButton" Background="#FFDD00" Foreground="#000000"
<Button x:Name="BuyMeCoffeeButton" Background="#FF5E5B" Foreground="#FFFFFF"
Click="BuyMeCoffeeButton_Click" HorizontalAlignment="Stretch" Padding="8,6" FontSize="12">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Text="☕" FontSize="14" Margin="0,0,6,0"/>
<TextBlock Text="Buy me a coffee" FontSize="12" Foreground="#000000"/>
<TextBlock Text="Support me on Ko-fi" FontSize="12" Foreground="#FFFFFF"/>
</StackPanel>
</Button>
@@ -390,6 +390,7 @@
<TextBlock x:Name="GameCountText" Text="(0 games)" FontSize="12" Foreground="#999999" VerticalAlignment="Center" Margin="0,0,15,0"/>
<Button x:Name="RefreshGameListButton" Content="🔄 Refresh" Click="RefreshGameListButton_Click" Padding="10,5" Margin="0,0,8,0"/>
<Button x:Name="MountGamesFromListButton" Content="📥 Mount All" Click="MountGamesButton_Click" Padding="10,5" Background="#28A745"/>
<Button x:Name="UnmountAllGamesButton" Content="🗑️ Unmount All" Click="UnmountAllGamesButton_Click" Padding="10,5" Margin="8,0,0,0" Background="#8B0000"/>
</WrapPanel>
</Border>
@@ -1320,13 +1321,9 @@
</StackPanel>
</Border>
<TextBlock Text="📸 Screenshot &amp; 🔔 Notification" FontWeight="Bold" FontSize="16" Foreground="#0E9CFF" Margin="0,0,0,15"/>
<TextBlock Text="🔔 Notification" FontWeight="Bold" FontSize="16" Foreground="#0E9CFF" Margin="0,0,0,15"/>
<Border Background="#252526" BorderBrush="#3E3E42" BorderThickness="1" CornerRadius="6" Padding="15" Margin="0,0,0,15">
<StackPanel>
<WrapPanel Margin="0,0,0,10">
<Button Content="📸 Capture Screen" Click="Screenshot_Click" Padding="12,6" Background="#007ACC" Margin="0,0,10,0"/>
<TextBlock Text="Saved to PS5 — grab it from Saves &amp; Media" Foreground="#707070" FontSize="11" VerticalAlignment="Center"/>
</WrapPanel>
<Grid ColumnDefinitions="*,Auto">
<TextBox x:Name="NotifyTextBox" PlaceholderText="Notification text..." Padding="8,6" Margin="0,0,10,0"/>
<Button Grid.Column="1" Content="🔔 Send" Click="SendNotify_Click" Padding="12,6" Background="#28A745"/>
+1 -1
View File
@@ -10,7 +10,7 @@
<AssemblyName>PS5Suite</AssemblyName>
<RootNamespace>PS5Upload</RootNamespace>
<ApplicationIcon>ps5_logo.ico</ApplicationIcon>
<Version>7.2.4</Version>
<Version>7.3.0</Version>
<Authors>Manos</Authors>
<Description>PS5 Suite - Complete PS5 Management Platform</Description>
<Copyright>Copyright © 2026 Manos</Copyright>
+38 -21
View File
@@ -80,6 +80,7 @@ namespace PS5Upload
MemRegions = 0x6B,
MemSearch = 0x6C,
KlogRead = 0x6D,
UnmountAll = 0x6E,
MountGame = 0x6F,
// App Manager v2
AppListV2 = 0x70,
@@ -92,7 +93,6 @@ namespace PS5Upload
PowerAction = 0x77,
UsbList = 0x78,
PadInfo = 0x79,
Screenshot = 0x7B,
Notify = 0x7C,
PadAction = 0x7D,
IccControl = 0x7E,
@@ -1257,26 +1257,6 @@ namespace PS5Upload
}
}
public async Task<(bool success, string message)> CaptureScreenshotAsync()
{
await _commandLock.WaitAsync();
try
{
await SendCommandAsync(Command.Screenshot);
var (response, data) = await ReceiveResponseAsync(20000);
string msg = data.Length > 0 ? Encoding.UTF8.GetString(data).TrimEnd('\0', '\n', '\r') : response.ToString();
return (response == Response.Ok, msg);
}
catch (Exception ex)
{
return (false, ex.Message);
}
finally
{
_commandLock.Release();
}
}
public Task<(bool success, string message)> NotifyAsync(string text)
=> SendTextCommandAsync(Command.Notify, text);
@@ -1551,6 +1531,43 @@ namespace PS5Upload
}
}
/// <summary>Unmount every mounted game (same async worker pipeline).</summary>
public async Task<(bool success, string message)> UnmountAllGamesAsync(Action<string>? onProgress = null)
{
await _commandLock.WaitAsync();
try
{
await SendCommandAsync(Command.UnmountAll);
var deadline = DateTime.UtcNow + TimeSpan.FromMinutes(10);
while (true)
{
int remainingMs = (int)(deadline - DateTime.UtcNow).TotalMilliseconds;
if (remainingMs <= 0) return (false, "Unmount-all timed out after 10 minutes");
var (response, respData) = await ReceiveResponseAsync(remainingMs);
string message = respData != null ? Encoding.UTF8.GetString(respData).TrimEnd('\0') : "";
if (response == Response.Progress)
{
if (!string.IsNullOrEmpty(message)) onProgress?.Invoke(message);
continue;
}
if (response == Response.Ok) return (true, message);
if (response == Response.Error) return (false, message);
return (false, $"Unexpected response: {response}");
}
}
catch (Exception ex)
{
return (false, ex.Message);
}
finally
{
_commandLock.Release();
}
}
// NEW: Launch a game by title ID
public async Task<(bool success, string message)> LaunchGameAsync(string titleId)
{
+102 -102
View File
@@ -1,102 +1,102 @@
<#
.SYNOPSIS
Builds standalone single-file executables of PS5Suite for all major platforms.
.DESCRIPTION
Produces self-contained, single-file binaries (no .NET runtime required on target machine) for:
- Windows x64 -> dist/win-x64/PS5Suite.exe
- Linux x64 -> dist/linux-x64/PS5Suite
- Linux ARM64 -> dist/linux-arm64/PS5Suite
- macOS x64 (Intel) -> dist/osx-x64/PS5Suite
- macOS ARM64 (M1+) -> dist/osx-arm64/PS5Suite
.PARAMETER Targets
Optional list of RIDs to build. Defaults to all 5.
Example: .\publish.ps1 -Targets win-x64,linux-x64
.PARAMETER Clean
Wipe the dist/ folder before building.
.EXAMPLE
.\publish.ps1
.\publish.ps1 -Targets win-x64
.\publish.ps1 -Clean
#>
param(
[string[]] $Targets = @('win-x64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64'),
[switch] $Clean
)
$ErrorActionPreference = 'Stop'
$ProjectRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$Project = Join-Path $ProjectRoot 'PS5Suite.csproj'
$DistRoot = Join-Path $ProjectRoot 'dist'
if (-not (Test-Path $Project)) {
Write-Error "Project file not found: $Project"
exit 1
}
if ($Clean -and (Test-Path $DistRoot)) {
Write-Host "Cleaning $DistRoot ..." -ForegroundColor Yellow
Remove-Item $DistRoot -Recurse -Force
}
New-Item -ItemType Directory -Path $DistRoot -Force | Out-Null
$summary = @()
foreach ($rid in $Targets) {
$outDir = Join-Path $DistRoot $rid
Write-Host ""
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host " Publishing $rid" -ForegroundColor Cyan
Write-Host "============================================================" -ForegroundColor Cyan
$args = @(
'publish', $Project,
'-c', 'Release',
'-r', $rid,
'--self-contained', 'true',
'-p:PublishSingleFile=true',
'-p:IncludeNativeLibrariesForSelfExtract=true',
'-p:EnableCompressionInSingleFile=true',
'-p:DebugType=None',
'-p:DebugSymbols=false',
'-o', $outDir
)
& dotnet @args
if ($LASTEXITCODE -ne 0) {
Write-Error "Publish failed for $rid"
$summary += [pscustomobject]@{ RID = $rid; Status = 'FAILED'; Path = '-'; SizeMB = '-' }
continue
}
# Locate the produced binary
$exeName = if ($rid -like 'win-*') { 'PS5Suite.exe' } else { 'PS5Suite' }
$exePath = Join-Path $outDir $exeName
if (Test-Path $exePath) {
$sizeMB = [math]::Round((Get-Item $exePath).Length / 1MB, 2)
Write-Host (" -> {0} ({1} MB)" -f $exePath, $sizeMB) -ForegroundColor Green
$summary += [pscustomobject]@{ RID = $rid; Status = 'OK'; Path = $exePath; SizeMB = $sizeMB }
} else {
Write-Warning "Binary not found at expected path: $exePath"
$summary += [pscustomobject]@{ RID = $rid; Status = 'NO BINARY'; Path = $outDir; SizeMB = '-' }
}
}
Write-Host ""
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host " Build summary" -ForegroundColor Cyan
Write-Host "============================================================" -ForegroundColor Cyan
$summary | Format-Table -AutoSize
Write-Host ""
Write-Host "Notes:" -ForegroundColor Yellow
Write-Host " - Linux/macOS users may need: chmod +x ./PS5Suite"
Write-Host " - macOS may also need: xattr -dr com.apple.quarantine ./PS5Suite"
Write-Host " - All builds are self-contained; no .NET runtime needed on the target system."
<#
.SYNOPSIS
Builds standalone single-file executables of PS5Suite for all major platforms.
.DESCRIPTION
Produces self-contained, single-file binaries (no .NET runtime required on target machine) for:
- Windows x64 -> dist/win-x64/PS5Suite.exe
- Linux x64 -> dist/linux-x64/PS5Suite
- Linux ARM64 -> dist/linux-arm64/PS5Suite
- macOS x64 (Intel) -> dist/osx-x64/PS5Suite
- macOS ARM64 (M1+) -> dist/osx-arm64/PS5Suite
.PARAMETER Targets
Optional list of RIDs to build. Defaults to all 5.
Example: .\publish.ps1 -Targets win-x64,linux-x64
.PARAMETER Clean
Wipe the dist/ folder before building.
.EXAMPLE
.\publish.ps1
.\publish.ps1 -Targets win-x64
.\publish.ps1 -Clean
#>
param(
[string[]] $Targets = @('win-x64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64'),
[switch] $Clean
)
$ErrorActionPreference = 'Stop'
$ProjectRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$Project = Join-Path $ProjectRoot 'PS5Suite.csproj'
$DistRoot = Join-Path $ProjectRoot 'dist'
if (-not (Test-Path $Project)) {
Write-Error "Project file not found: $Project"
exit 1
}
if ($Clean -and (Test-Path $DistRoot)) {
Write-Host "Cleaning $DistRoot ..." -ForegroundColor Yellow
Remove-Item $DistRoot -Recurse -Force
}
New-Item -ItemType Directory -Path $DistRoot -Force | Out-Null
$summary = @()
foreach ($rid in $Targets) {
$outDir = Join-Path $DistRoot $rid
Write-Host ""
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host " Publishing $rid" -ForegroundColor Cyan
Write-Host "============================================================" -ForegroundColor Cyan
$args = @(
'publish', $Project,
'-c', 'Release',
'-r', $rid,
'--self-contained', 'true',
'-p:PublishSingleFile=true',
'-p:IncludeNativeLibrariesForSelfExtract=true',
'-p:EnableCompressionInSingleFile=true',
'-p:DebugType=None',
'-p:DebugSymbols=false',
'-o', $outDir
)
& dotnet @args
if ($LASTEXITCODE -ne 0) {
Write-Error "Publish failed for $rid"
$summary += [pscustomobject]@{ RID = $rid; Status = 'FAILED'; Path = '-'; SizeMB = '-' }
continue
}
# Locate the produced binary
$exeName = if ($rid -like 'win-*') { 'PS5Suite.exe' } else { 'PS5Suite' }
$exePath = Join-Path $outDir $exeName
if (Test-Path $exePath) {
$sizeMB = [math]::Round((Get-Item $exePath).Length / 1MB, 2)
Write-Host (" -> {0} ({1} MB)" -f $exePath, $sizeMB) -ForegroundColor Green
$summary += [pscustomobject]@{ RID = $rid; Status = 'OK'; Path = $exePath; SizeMB = $sizeMB }
} else {
Write-Warning "Binary not found at expected path: $exePath"
$summary += [pscustomobject]@{ RID = $rid; Status = 'NO BINARY'; Path = $outDir; SizeMB = '-' }
}
}
Write-Host ""
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host " Build summary" -ForegroundColor Cyan
Write-Host "============================================================" -ForegroundColor Cyan
$summary | Format-Table -AutoSize
Write-Host ""
Write-Host "Notes:" -ForegroundColor Yellow
Write-Host " - Linux/macOS users may need: chmod +x ./PS5Suite"
Write-Host " - macOS may also need: xattr -dr com.apple.quarantine ./PS5Suite"
Write-Host " - All builds are self-contained; no .NET runtime needed on the target system."
+32 -32
View File
@@ -1,32 +1,32 @@
# PS5 Suite Server - Makefile
# Uses the maintained ps5-payload-dev/sdk (2026 release, v0.43+)
PS5_PAYLOAD_SDK ?= /opt/ps5-payload-sdk
PS5_IP ?= 192.168.0.160
CC := $(PS5_PAYLOAD_SDK)/bin/prospero-clang
CXX := $(PS5_PAYLOAD_SDK)/bin/prospero-clang++
LD := $(PS5_PAYLOAD_SDK)/bin/prospero-lld
DEPLOY := $(PS5_PAYLOAD_SDK)/bin/prospero-deploy
ELF := ps5_suite_server.elf
CFLAGS := -Wall -O3
all: $(ELF)
# Static link like the proven game_mounter/working-v6 model — libkernel_sys
# (NOT kernel_web: externs like sceKernelGetCpuTemperature don't exist in the
# runtime web variant and would make the ELF fail to launch).
$(ELF): main.c
$(CC) $(CFLAGS) -o $@ $^ -lkernel_sys -lSceSystemService -lSceUserService -lSceAppInstUtil -lSceNetCtl -lScePad -lsqlite3
$(PS5_PAYLOAD_SDK)/bin/llvm-strip $@
# Push the freshly built payload straight to the console (elfldr must be
# running; uses the http:// URI support added in SDK v0.36.1).
deploy: $(ELF)
$(DEPLOY) http://$(PS5_IP):9021 $(ELF)
clean:
rm -f $(ELF)
.PHONY: all clean deploy
# PS5 Suite Server - Makefile
# Uses the maintained ps5-payload-dev/sdk (2026 release, v0.43+)
PS5_PAYLOAD_SDK ?= /opt/ps5-payload-sdk
PS5_IP ?= 192.168.0.160
CC := $(PS5_PAYLOAD_SDK)/bin/prospero-clang
CXX := $(PS5_PAYLOAD_SDK)/bin/prospero-clang++
LD := $(PS5_PAYLOAD_SDK)/bin/prospero-lld
DEPLOY := $(PS5_PAYLOAD_SDK)/bin/prospero-deploy
ELF := ps5_suite_server.elf
CFLAGS := -Wall -O3
all: $(ELF)
# Static link like the proven game_mounter/working-v6 model — libkernel_sys
# (NOT kernel_web: externs like sceKernelGetCpuTemperature don't exist in the
# runtime web variant and would make the ELF fail to launch).
$(ELF): main.c
$(CC) $(CFLAGS) -o $@ $^ -lkernel_sys -lSceSystemService -lSceUserService -lSceAppInstUtil -lSceNetCtl -lScePad -lsqlite3
$(PS5_PAYLOAD_SDK)/bin/llvm-strip $@
# Push the freshly built payload straight to the console (elfldr must be
# running; uses the http:// URI support added in SDK v0.36.1).
deploy: $(ELF)
$(DEPLOY) http://$(PS5_IP):9021 $(ELF)
clean:
rm -f $(ELF)
.PHONY: all clean deploy
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
export PS5_PAYLOAD_SDK=/opt/ps5-payload-sdk
export PATH=/opt/ps5-payload-sdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
cd "$(dirname "$0")"
make clean
make
+5 -5
View File
@@ -1,6 +1,6 @@
[
"EP0700-PPSA10737_00-LITTLENIGHTMARES",
"UP0700-PPSA10738_00-LITTLENIGHTMARES",
"HP5028-PPSA10740_00-LITTLENIGHTMARES",
"JP0700-PPSA10739_00-LITTLENIGHTMARES"
[
"EP0700-PPSA10737_00-LITTLENIGHTMARES",
"UP0700-PPSA10738_00-LITTLENIGHTMARES",
"HP5028-PPSA10740_00-LITTLENIGHTMARES",
"JP0700-PPSA10739_00-LITTLENIGHTMARES"
]
+2164 -2164
View File
File diff suppressed because it is too large. Load diff
+1162 -125
View File
File diff suppressed because it is too large. Load diff
+137 -137
View File
@@ -1,137 +1,137 @@
{
"addcont": {
"serviceIdForSharing": [
" ",
" ",
" ",
" ",
" ",
" ",
" "
]
},
"ageLevel": {
"AE": 16,
"AR": 13,
"AT": 16,
"AU": 15,
"BE": 16,
"BG": 16,
"BH": 16,
"BO": 13,
"BR": 14,
"CA": 13,
"CH": 16,
"CL": 13,
"CO": 13,
"CR": 13,
"CY": 16,
"CZ": 16,
"DE": 12,
"DK": 16,
"EC": 13,
"ES": 16,
"FI": 16,
"FR": 16,
"GB": 16,
"GR": 16,
"GT": 13,
"HN": 13,
"HR": 16,
"HU": 16,
"IE": 16,
"IL": 16,
"IN": 16,
"IS": 16,
"IT": 16,
"JP": 15,
"KR": 15,
"KW": 16,
"LB": 16,
"LU": 16,
"MT": 16,
"MX": 13,
"NI": 13,
"NL": 16,
"NO": 16,
"NZ": 15,
"OM": 16,
"PA": 13,
"PE": 13,
"PL": 16,
"PT": 16,
"PY": 13,
"QA": 16,
"RO": 16,
"RU": 16,
"SA": 16,
"SE": 16,
"SI": 16,
"SK": 16,
"SV": 13,
"TR": 16,
"TW": 15,
"UA": 16,
"US": 13,
"UY": 13,
"ZA": 16,
"default": 16
},
"applicationCategoryType": 0,
"applicationDrmType": "standard",
"attribute": 0,
"attribute2": 0,
"attribute3": 4194304,
"conceptId": "223059",
"contentBadgeType": 1,
"contentId": "EP0700-PPSA10737_00-LITTLENIGHTMARES",
"contentVersion": "01.004.000",
"downloadDataSize": 0,
"gameIntent": {
"permittedIntents": [
{
"intentType": "launchActivity"
}
]
},
"localizedParameters": {
"de-DE": {
"titleName": "Little Nightmares Enhanced Edition"
},
"defaultLanguage": "en-US",
"en-US": {
"titleName": "Little Nightmares Enhanced Edition"
},
"es-ES": {
"titleName": "Little Nightmares Enhanced Edition"
},
"fr-FR": {
"titleName": "Little Nightmares Enhanced Edition"
},
"it-IT": {
"titleName": "Little Nightmares Enhanced Edition"
},
"pl-PL": {
"titleName": "Little Nightmares Enhanced Edition"
},
"ru-RU": {
"titleName": "Little Nightmares Enhanced Edition"
},
"sv-SE": {
"titleName": "Little Nightmares Enhanced Edition"
}
},
"masterVersion": "01.00",
"originContentVersion": "01.000.000",
"pubtools": {
"creationDate": "2025-11-24 11:19:04",
"submission": true,
"toolVersion": "3.13"
},
"requiredSystemSoftwareVersion": "0x1240000000000000",
"sdkVersion": "0x0900000000000000",
"targetContentVersion": "01.003.000",
"titleId": "PPSA10737",
"userDefinedParam1": 0,
"versionFileUri": "https://sgst.prod.dl.playstation.net/sgst/prod/00/np/PPSA10737_00/f530dd33-21e0-48c9-8816-878774f65d8d-version.xml "
}
{
"addcont": {
"serviceIdForSharing": [
" ",
" ",
" ",
" ",
" ",
" ",
" "
]
},
"ageLevel": {
"AE": 16,
"AR": 13,
"AT": 16,
"AU": 15,
"BE": 16,
"BG": 16,
"BH": 16,
"BO": 13,
"BR": 14,
"CA": 13,
"CH": 16,
"CL": 13,
"CO": 13,
"CR": 13,
"CY": 16,
"CZ": 16,
"DE": 12,
"DK": 16,
"EC": 13,
"ES": 16,
"FI": 16,
"FR": 16,
"GB": 16,
"GR": 16,
"GT": 13,
"HN": 13,
"HR": 16,
"HU": 16,
"IE": 16,
"IL": 16,
"IN": 16,
"IS": 16,
"IT": 16,
"JP": 15,
"KR": 15,
"KW": 16,
"LB": 16,
"LU": 16,
"MT": 16,
"MX": 13,
"NI": 13,
"NL": 16,
"NO": 16,
"NZ": 15,
"OM": 16,
"PA": 13,
"PE": 13,
"PL": 16,
"PT": 16,
"PY": 13,
"QA": 16,
"RO": 16,
"RU": 16,
"SA": 16,
"SE": 16,
"SI": 16,
"SK": 16,
"SV": 13,
"TR": 16,
"TW": 15,
"UA": 16,
"US": 13,
"UY": 13,
"ZA": 16,
"default": 16
},
"applicationCategoryType": 0,
"applicationDrmType": "standard",
"attribute": 0,
"attribute2": 0,
"attribute3": 4194304,
"conceptId": "223059",
"contentBadgeType": 1,
"contentId": "EP0700-PPSA10737_00-LITTLENIGHTMARES",
"contentVersion": "01.004.000",
"downloadDataSize": 0,
"gameIntent": {
"permittedIntents": [
{
"intentType": "launchActivity"
}
]
},
"localizedParameters": {
"de-DE": {
"titleName": "Little Nightmares Enhanced Edition"
},
"defaultLanguage": "en-US",
"en-US": {
"titleName": "Little Nightmares Enhanced Edition"
},
"es-ES": {
"titleName": "Little Nightmares Enhanced Edition"
},
"fr-FR": {
"titleName": "Little Nightmares Enhanced Edition"
},
"it-IT": {
"titleName": "Little Nightmares Enhanced Edition"
},
"pl-PL": {
"titleName": "Little Nightmares Enhanced Edition"
},
"ru-RU": {
"titleName": "Little Nightmares Enhanced Edition"
},
"sv-SE": {
"titleName": "Little Nightmares Enhanced Edition"
}
},
"masterVersion": "01.00",
"originContentVersion": "01.000.000",
"pubtools": {
"creationDate": "2025-11-24 11:19:04",
"submission": true,
"toolVersion": "3.13"
},
"requiredSystemSoftwareVersion": "0x1240000000000000",
"sdkVersion": "0x0900000000000000",
"targetContentVersion": "01.003.000",
"titleId": "PPSA10737",
"userDefinedParam1": 0,
"versionFileUri": "https://sgst.prod.dl.playstation.net/sgst/prod/00/np/PPSA10737_00/f530dd33-21e0-48c9-8816-878774f65d8d-version.xml "
}
+137 -137
View File
@@ -1,137 +1,137 @@
{
"addcont": {
"serviceIdForSharing": [
" ",
" ",
" ",
" ",
" ",
" ",
" "
]
},
"ageLevel": {
"AE": 16,
"AR": 13,
"AT": 16,
"AU": 15,
"BE": 16,
"BG": 16,
"BH": 16,
"BO": 13,
"BR": 14,
"CA": 13,
"CH": 16,
"CL": 13,
"CO": 13,
"CR": 13,
"CY": 16,
"CZ": 16,
"DE": 12,
"DK": 16,
"EC": 13,
"ES": 16,
"FI": 16,
"FR": 16,
"GB": 16,
"GR": 16,
"GT": 13,
"HN": 13,
"HR": 16,
"HU": 16,
"IE": 16,
"IL": 16,
"IN": 16,
"IS": 16,
"IT": 16,
"JP": 15,
"KR": 15,
"KW": 16,
"LB": 16,
"LU": 16,
"MT": 16,
"MX": 13,
"NI": 13,
"NL": 16,
"NO": 16,
"NZ": 15,
"OM": 16,
"PA": 13,
"PE": 13,
"PL": 16,
"PT": 16,
"PY": 13,
"QA": 16,
"RO": 16,
"RU": 16,
"SA": 16,
"SE": 16,
"SI": 16,
"SK": 16,
"SV": 13,
"TR": 16,
"TW": 15,
"UA": 16,
"US": 13,
"UY": 13,
"ZA": 16,
"default": 16
},
"applicationCategoryType": 0,
"applicationDrmType": "standard",
"attribute": 0,
"attribute2": 0,
"attribute3": 4194304,
"conceptId": "223059",
"contentBadgeType": 1,
"contentId": "EP0700-PPSA10737_00-LITTLENIGHTMARES",
"contentVersion": "01.004.000",
"downloadDataSize": 0,
"gameIntent": {
"permittedIntents": [
{
"intentType": "launchActivity"
}
]
},
"localizedParameters": {
"de-DE": {
"titleName": "Little Nightmares Enhanced Edition"
},
"defaultLanguage": "en-US",
"en-US": {
"titleName": "Little Nightmares Enhanced Edition"
},
"es-ES": {
"titleName": "Little Nightmares Enhanced Edition"
},
"fr-FR": {
"titleName": "Little Nightmares Enhanced Edition"
},
"it-IT": {
"titleName": "Little Nightmares Enhanced Edition"
},
"pl-PL": {
"titleName": "Little Nightmares Enhanced Edition"
},
"ru-RU": {
"titleName": "Little Nightmares Enhanced Edition"
},
"sv-SE": {
"titleName": "Little Nightmares Enhanced Edition"
}
},
"masterVersion": "01.00",
"originContentVersion": "01.000.000",
"pubtools": {
"creationDate": "2025-11-24 11:19:04",
"submission": true,
"toolVersion": "3.13"
},
"requiredSystemSoftwareVersion": "0x1240000000000000",
"sdkVersion": "0x0900000000000000",
"targetContentVersion": "01.003.000",
"titleId": "PPSA10737",
"userDefinedParam1": 0,
"versionFileUri": "https://sgst.prod.dl.playstation.net/sgst/prod/00/np/PPSA10737_00/f530dd33-21e0-48c9-8816-878774f65d8d-version.xml "
}
{
"addcont": {
"serviceIdForSharing": [
" ",
" ",
" ",
" ",
" ",
" ",
" "
]
},
"ageLevel": {
"AE": 16,
"AR": 13,
"AT": 16,
"AU": 15,
"BE": 16,
"BG": 16,
"BH": 16,
"BO": 13,
"BR": 14,
"CA": 13,
"CH": 16,
"CL": 13,
"CO": 13,
"CR": 13,
"CY": 16,
"CZ": 16,
"DE": 12,
"DK": 16,
"EC": 13,
"ES": 16,
"FI": 16,
"FR": 16,
"GB": 16,
"GR": 16,
"GT": 13,
"HN": 13,
"HR": 16,
"HU": 16,
"IE": 16,
"IL": 16,
"IN": 16,
"IS": 16,
"IT": 16,
"JP": 15,
"KR": 15,
"KW": 16,
"LB": 16,
"LU": 16,
"MT": 16,
"MX": 13,
"NI": 13,
"NL": 16,
"NO": 16,
"NZ": 15,
"OM": 16,
"PA": 13,
"PE": 13,
"PL": 16,
"PT": 16,
"PY": 13,
"QA": 16,
"RO": 16,
"RU": 16,
"SA": 16,
"SE": 16,
"SI": 16,
"SK": 16,
"SV": 13,
"TR": 16,
"TW": 15,
"UA": 16,
"US": 13,
"UY": 13,
"ZA": 16,
"default": 16
},
"applicationCategoryType": 0,
"applicationDrmType": "standard",
"attribute": 0,
"attribute2": 0,
"attribute3": 4194304,
"conceptId": "223059",
"contentBadgeType": 1,
"contentId": "EP0700-PPSA10737_00-LITTLENIGHTMARES",
"contentVersion": "01.004.000",
"downloadDataSize": 0,
"gameIntent": {
"permittedIntents": [
{
"intentType": "launchActivity"
}
]
},
"localizedParameters": {
"de-DE": {
"titleName": "Little Nightmares Enhanced Edition"
},
"defaultLanguage": "en-US",
"en-US": {
"titleName": "Little Nightmares Enhanced Edition"
},
"es-ES": {
"titleName": "Little Nightmares Enhanced Edition"
},
"fr-FR": {
"titleName": "Little Nightmares Enhanced Edition"
},
"it-IT": {
"titleName": "Little Nightmares Enhanced Edition"
},
"pl-PL": {
"titleName": "Little Nightmares Enhanced Edition"
},
"ru-RU": {
"titleName": "Little Nightmares Enhanced Edition"
},
"sv-SE": {
"titleName": "Little Nightmares Enhanced Edition"
}
},
"masterVersion": "01.00",
"originContentVersion": "01.000.000",
"pubtools": {
"creationDate": "2025-11-24 11:19:04",
"submission": true,
"toolVersion": "3.13"
},
"requiredSystemSoftwareVersion": "0x1240000000000000",
"sdkVersion": "0x0900000000000000",
"targetContentVersion": "01.003.000",
"titleId": "PPSA10737",
"userDefinedParam1": 0,
"versionFileUri": "https://sgst.prod.dl.playstation.net/sgst/prod/00/np/PPSA10737_00/f530dd33-21e0-48c9-8816-878774f65d8d-version.xml "
}
Binary file not shown.
+96
View File
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
"""FTP-stage + self-update + probe + run a trophy command on the PS5 suite."""
import socket, ftplib, sys, time, struct
IP = "192.168.0.53"
DIR = "/mnt/c/Users/HACKMAN/Desktop/ps5 test/my_projects/ps5_upload_suite/payload"
ELF = f"{DIR}/ps5_suite_server.elf"
CMD_SELF_UPDATE = 0x68
CMD_TROPHY_UNLOCK = 0x82
CMD_PING = 0x01
def rpc(cmd, payload=b"", timeout=20, port=None):
for p in ([port] if port else range(9113, 9145)):
try:
s = socket.create_connection((IP, p), timeout=4)
s.settimeout(timeout)
s.sendall(bytes([cmd]) + struct.pack("<I", len(payload)) + payload)
hdr = s.recv(5)
if len(hdr) < 5:
s.close(); continue
ln = struct.unpack("<I", hdr[1:5])[0]
data = b""
while len(data) < ln:
c = s.recv(min(65536, ln - len(data)))
if not c: break
data += c
s.close()
return p, hdr[0], data
except OSError:
continue
return None, None, b""
def find_server():
for p in range(9113, 9145):
try:
s = socket.create_connection((IP, p), timeout=2)
s.settimeout(4)
s.sendall(bytes([CMD_PING]) + struct.pack("<I", 0))
hdr = s.recv(5)
if len(hdr) == 5:
ln = struct.unpack("<I", hdr[1:5])[0]
data = s.recv(min(ln, 512)) if ln else b""
s.close()
return p, data
s.close()
except OSError:
continue
return None, b""
def ftp_put(local, remote):
ftp = ftplib.FTP()
ftp.connect(IP, 1337, timeout=15)
try:
ftp.login('anonymous', 'anonymous')
except Exception:
ftp.login('', '')
with open(local, 'rb') as f:
ftp.storbinary(f"STOR {remote}", f)
ftp.quit()
if __name__ == "__main__":
op = sys.argv[1] if len(sys.argv) > 1 else "full"
if op in ("full", "deploy"):
print("[1] ftp stage...")
ftp_put(ELF, "/data/ps5suite/update.elf")
print(" staged")
print("[2] self-update cmd...")
port, typ, d = rpc(CMD_SELF_UPDATE, b"/data/ps5suite/update.elf", timeout=30)
print(" ->", port, hex(typ or 0), d[:200])
if op == "deploy":
sys.exit(0)
print("[3] wait for new server...")
time.sleep(5)
if op in ("full", "probe"):
for i in range(6):
p, d = find_server()
if p:
print(" ALIVE on", p, "->", d[:80])
SRV = p
break
time.sleep(4)
else:
print(" NO SERVER"); sys.exit(1)
if op == "full" and len(sys.argv) > 2:
arg = sys.argv[2].encode()
p, typ, d = rpc(CMD_TROPHY_UNLOCK, arg, timeout=90, port=SRV)
print("[cmd]", arg.decode(), "->", hex(typ or 0), d[:1500])
elif op == "cmd":
p, _ = find_server()
arg = " ".join(sys.argv[2:]).encode()
p, typ, d = rpc(CMD_TROPHY_UNLOCK, arg, timeout=90, port=p)
print("->", hex(typ or 0), d[:1500])
+2 -2
View File
@@ -1414,7 +1414,7 @@ static int handle_fan_set_threshold(int sock, int temp_c) {
// 2. Try AppInstallTitleDir first (handles duplicates internally)
// 3. Fallback to AppInstallAll for FW 12.00+
// Note: v7.2.4 removed pre-mount Initialize+UnInstall calls — they caused
// home screen refresh. the mount bridge doesn't use them either.
// home screen refresh and aren't needed.
// install_app - dump_installer style registration
// Returns 0 on success, negative on failure
@@ -3520,7 +3520,7 @@ static int process_game(const char* game_path, char* game_name_out, size_t name_
// v7.2.4: Removed pre-mount Initialize + UnInstall calls.
// These daemon IPC calls triggered ShellCore to refresh the home screen,
// causing visible flicker even when mounting a single game.
// the mount bridge doesn't use them — AppInstallTitleDir handles duplicates
// They're unnecessary — AppInstallTitleDir handles duplicates
// internally, and our direct SQLite path already DELETEs before INSERT.
// The old calls were:
// sceAppInstUtilInitialize()