mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 10:56:20 +08:00
[PlayGo] Derive the installed chunk set from the pak files on disk (#438)
A title that ships no PlayGo sidecar was reported as a single-chunk package. That is wrong for any package whose content is split across chunks: the title is told everything past chunk 0 is not installed, even though a locally dumped title has all of its data present. The Invincible (PPSA06426) ships pakchunk0..8 and asks PlayGo which of those are available. Receiving BAD_CHUNK_ID for chunks 1..8, it re-queried scePlayGoGetLocus for the same chunk in a tight loop that never terminated (observed ~1000 consecutive dispatches with identical arguments). Discover the chunk ids from the pakchunk<N>-<platform>.pak files present under the app0 root instead. Those N are exactly the chunks the package has, so the answer is derived from the install rather than assumed. Chunk 0 is always included, so a title with no pak files at all keeps the previous single-chunk behaviour, and ids outside the discovered set still return BAD_CHUNK_ID so title-side chunk enumeration still terminates. Verified on The Invincible: the discovered set is [0..8], matching the nine pak files, and the GetLocus retry loop no longer occurs. No regression on Dead Cells (PPSA15552): still reaches AGC rendering and presents frames. The existing metadata-free contract test still passes -- its app0 fixture has no pak files, so the discovered set stays [0].
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using SharpEmu.HLE;
|
||||
@@ -698,14 +698,22 @@ public static class PlayGoExports
|
||||
var hasMetadata = File.Exists(playGoDat) || File.Exists(scenarioJson) || File.Exists(chunkDefsXml);
|
||||
if (!hasMetadata)
|
||||
{
|
||||
// No PlayGo sidecar: report a fully-installed single chunk. Available must
|
||||
// stay true or scePlayGoOpen fails with NotSupportPlayGo (fatal PS5-component
|
||||
// init failure for UE titles); chunk 0 reports LocalFast and every other id
|
||||
// returns BAD_CHUNK_ID, terminating title-side chunk enumeration.
|
||||
TracePlayGo("metadata_missing; fully-installed single chunk");
|
||||
// No PlayGo sidecar: derive the installed chunk set from the pak files
|
||||
// actually present on disk. A locally dumped title has all of its data
|
||||
// installed, and a package that splits content across chunks names them
|
||||
// pakchunk<N>-<platform>.pak, so those N are exactly the chunks that
|
||||
// exist. Reporting only chunk 0 told such a title its remaining content
|
||||
// was missing: The Invincible (PPSA06426) ships pakchunk0..8 and spun
|
||||
// forever re-querying scePlayGoGetLocus for a chunk that never became
|
||||
// available. Available must stay true or scePlayGoOpen fails with
|
||||
// NotSupportPlayGo (fatal PS5-component init failure for UE titles).
|
||||
// Ids outside the discovered set still return BAD_CHUNK_ID, so
|
||||
// title-side chunk enumeration still terminates.
|
||||
var installedChunkIds = DiscoverInstalledChunkIds(app0Root);
|
||||
TracePlayGo($"metadata_missing; fully-installed chunks=[{string.Join(',', installedChunkIds)}]");
|
||||
return new PlayGoMetadata(
|
||||
true,
|
||||
[(ushort)0],
|
||||
installedChunkIds,
|
||||
PlayGoChunkIdKnowledge.Authoritative);
|
||||
}
|
||||
|
||||
@@ -718,6 +726,41 @@ public static class PlayGoExports
|
||||
: PlayGoChunkIdKnowledge.Authoritative);
|
||||
}
|
||||
|
||||
// Chunk ids for a title that ships no PlayGo sidecar, taken from the
|
||||
// pakchunk<N>-<platform>.pak files on disk. Chunk 0 is always included: it
|
||||
// is the base chunk and must resolve even for a title with no pak files at
|
||||
// all (which keeps the single-chunk behaviour for such titles).
|
||||
private static ushort[] DiscoverInstalledChunkIds(string app0Root)
|
||||
{
|
||||
var ids = new SortedSet<ushort> { 0 };
|
||||
try
|
||||
{
|
||||
foreach (var pakFile in Directory.EnumerateFiles(app0Root, "pakchunk*.pak", SearchOption.AllDirectories))
|
||||
{
|
||||
var name = Path.GetFileNameWithoutExtension(pakFile);
|
||||
var digits = name.AsSpan("pakchunk".Length);
|
||||
var length = 0;
|
||||
while (length < digits.Length && char.IsAsciiDigit(digits[length]))
|
||||
{
|
||||
length++;
|
||||
}
|
||||
|
||||
if (length > 0 && ushort.TryParse(digits[..length], out var chunkId))
|
||||
{
|
||||
ids.Add(chunkId);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
}
|
||||
|
||||
return ids.ToArray();
|
||||
}
|
||||
|
||||
private static ushort[] LoadChunkIds(string chunkDefsXml)
|
||||
{
|
||||
if (!File.Exists(chunkDefsXml))
|
||||
|
||||
Reference in New Issue
Block a user