200 lines
10 KiB
JavaScript
200 lines
10 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const test = require('node:test');
|
|
const {
|
|
A8_DISTINCT_CONTROL,
|
|
PINNED_PS5_BUILDER,
|
|
createPs5NativePkgBuilderProvider,
|
|
} = require('../../src/services/ps5-native-pkg-builder-provider');
|
|
|
|
const CLEAN_CAPABILITIES = Object.freeze({
|
|
backupToDebugFpkg: true,
|
|
dataDlcToDebugFpkg: true,
|
|
updateToDebugFpkg: false,
|
|
backupInspection: true,
|
|
packageInspection: true,
|
|
packageValidation: true,
|
|
launchReadiness: true,
|
|
licenseFreeBuild: false,
|
|
distinctTitleControlBuild: true,
|
|
finalizedFihOutput: true,
|
|
});
|
|
|
|
function fixture() {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-clean-fpkg-builder-'));
|
|
const toolRoot = path.join(root, 'tool');
|
|
const sourceRoot = path.join(root, 'source');
|
|
const workRoot = path.join(root, 'work');
|
|
fs.mkdirSync(toolRoot, { recursive: true });
|
|
fs.mkdirSync(path.join(sourceRoot, 'sce_sys'), { recursive: true });
|
|
fs.mkdirSync(workRoot, { recursive: true });
|
|
fs.writeFileSync(path.join(sourceRoot, 'sce_sys', 'param.json'), '{}');
|
|
fs.writeFileSync(path.join(sourceRoot, 'eboot.bin'), Buffer.from([0x7f, 0x45, 0x4c, 0x46]));
|
|
const executable = path.join(toolRoot, PINNED_PS5_BUILDER.executable);
|
|
fs.writeFileSync(executable, 'fixture');
|
|
return { root, toolRoot, sourceRoot, workRoot };
|
|
}
|
|
|
|
function cleanProbe(overrides = {}) {
|
|
return {
|
|
product: 'LibProsperoPkg',
|
|
version: '2.6.0',
|
|
revision: '9084c0e88cd8d7cd9c0b353b8b6b89f7d1cc4275',
|
|
abi: 'chimera-prospero-bridge-v4',
|
|
patched: true,
|
|
patch: 'chimera-stream-large-package-assembly-u2c24-v2',
|
|
capabilities: CLEAN_CAPABILITIES,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test('production builder pin is the exact streaming semantic bridge-v4 baseline', () => {
|
|
assert.equal(PINNED_PS5_BUILDER.abi, 'chimera-prospero-bridge-v4');
|
|
assert.equal(PINNED_PS5_BUILDER.version, '2.6.0');
|
|
assert.equal(PINNED_PS5_BUILDER.patched, true);
|
|
assert.equal(PINNED_PS5_BUILDER.patch, 'chimera-stream-large-package-assembly-u2c24-v2');
|
|
assert.equal(PINNED_PS5_BUILDER.patchSha256, '65e04ca3462ef4e619d296d3656c3658a6083283102a38bfb69d095f369db135');
|
|
assert.equal('patchSet' in PINNED_PS5_BUILDER, false);
|
|
});
|
|
|
|
test('probe recognizes the exact clean revision but blocks its unproven license-free converter', async t => {
|
|
const f = fixture();
|
|
t.after(() => fs.rmSync(f.root, { recursive: true, force: true }));
|
|
let report = cleanProbe();
|
|
const provider = createPs5NativePkgBuilderProvider({
|
|
toolRoot: f.toolRoot,
|
|
allowedWorkRoots: [f.workRoot],
|
|
allowedSourceRoots: [f.sourceRoot],
|
|
runner: async () => ({ code: 0, output: JSON.stringify(report) }),
|
|
});
|
|
let status = await provider.probe();
|
|
assert.equal(status.pinned, true);
|
|
assert.equal(status.clean, true);
|
|
assert.equal(status.available, false);
|
|
assert.equal(status.controlAvailable, true);
|
|
assert.equal(status.reason, 'capability-mismatch');
|
|
report = cleanProbe({ patch: 'unexpected-patch' });
|
|
assert.equal((await provider.probe()).available, false);
|
|
report = cleanProbe({ revision: '0'.repeat(40) });
|
|
assert.equal((await provider.probe()).available, false);
|
|
report = cleanProbe({ capabilities: { ...CLEAN_CAPABILITIES, licenseFreeBuild: true } });
|
|
status = await provider.probe();
|
|
assert.equal(status.available, true);
|
|
});
|
|
|
|
test('distinct control build uses one fixed non-colliding identity and exact upstream assets', async t => {
|
|
const f = fixture();
|
|
t.after(() => fs.rmSync(f.root, { recursive: true, force: true }));
|
|
fs.writeFileSync(path.join(f.sourceRoot, 'eboot.bin'), Buffer.from('fixture-eboot'));
|
|
fs.writeFileSync(path.join(f.sourceRoot, 'sce_sys', 'icon0.png'), Buffer.from('fixture-icon'));
|
|
const descriptor = {
|
|
...A8_DISTINCT_CONTROL,
|
|
assets: {
|
|
ebootSha256: require('node:crypto').createHash('sha256').update('fixture-eboot').digest('hex'),
|
|
iconSha256: require('node:crypto').createHash('sha256').update('fixture-icon').digest('hex'),
|
|
},
|
|
};
|
|
const outputPath = path.join(f.workRoot, 'control.pkg');
|
|
const workPath = path.join(f.workRoot, 'control-work');
|
|
let request;
|
|
const runner = async ({ args }) => {
|
|
if (args[0] === 'probe') return { code: 0, output: JSON.stringify(cleanProbe()) };
|
|
assert.deepEqual(args.slice(0, 2), ['build-control', '--request']);
|
|
request = JSON.parse(fs.readFileSync(args[2], 'utf8'));
|
|
fs.writeFileSync(outputPath, Buffer.concat([Buffer.from([0x7f, 0x46, 0x49, 0x48]), Buffer.alloc(64)]));
|
|
fs.writeFileSync(args[4], JSON.stringify({ ok: true, outputPath, licenseFree: true, fixedIdentity: true }));
|
|
return { code: 0, output: '' };
|
|
};
|
|
const provider = createPs5NativePkgBuilderProvider({
|
|
toolRoot: f.toolRoot, allowedWorkRoots: [f.workRoot], allowedSourceRoots: [f.sourceRoot], runner,
|
|
distinctControl: descriptor,
|
|
});
|
|
const result = await provider.buildDistinctControl({ sourcePath: f.sourceRoot, outputPath, workPath });
|
|
assert.equal(request.abi, 'chimera-prospero-bridge-v4');
|
|
assert.equal(request.operation, 'build-control');
|
|
assert.equal(request.titleId, A8_DISTINCT_CONTROL.titleId);
|
|
assert.equal(request.contentId, A8_DISTINCT_CONTROL.contentId);
|
|
assert.equal(request.componentType, 'base');
|
|
assert.equal(request.sourceAssets.ebootSha256, descriptor.assets.ebootSha256);
|
|
assert.equal(request.sourceAssets.iconSha256, descriptor.assets.iconSha256);
|
|
assert.equal(result.report.licenseFree, true);
|
|
});
|
|
|
|
test('distinct control build rejects source drift and caller-selected identity before the bridge', async t => {
|
|
const f = fixture();
|
|
t.after(() => fs.rmSync(f.root, { recursive: true, force: true }));
|
|
const provider = createPs5NativePkgBuilderProvider({
|
|
toolRoot: f.toolRoot, allowedWorkRoots: [f.workRoot], allowedSourceRoots: [f.sourceRoot],
|
|
runner: async () => ({ code: 0, output: JSON.stringify(cleanProbe()) }),
|
|
});
|
|
await assert.rejects(
|
|
() => provider.buildDistinctControl({ sourcePath: f.sourceRoot, outputPath: path.join(f.workRoot, 'control.pkg'), workPath: path.join(f.workRoot, 'work'), titleId: 'PPSA12345' }),
|
|
error => error.code === 'PS5_A8_CONTROL_IDENTITY_FIXED',
|
|
);
|
|
await assert.rejects(
|
|
() => provider.buildDistinctControl({ sourcePath: f.sourceRoot, outputPath: path.join(f.workRoot, 'control.pkg'), workPath: path.join(f.workRoot, 'work') }),
|
|
error => error.code === 'PS5_A8_CONTROL_SOURCE_DRIFT',
|
|
);
|
|
});
|
|
|
|
test('build invokes only build-backup with an immutable semantic v4 request when all clean capabilities are proven', async t => {
|
|
const f = fixture();
|
|
t.after(() => fs.rmSync(f.root, { recursive: true, force: true }));
|
|
const outputPath = path.join(f.workRoot, 'artifact.pkg');
|
|
const buildWork = path.join(f.workRoot, 'job');
|
|
let request;
|
|
let reportOverride = {};
|
|
const runner = async ({ args }) => {
|
|
if (args[0] === 'probe') return { code: 0, output: JSON.stringify(cleanProbe({ capabilities: { ...CLEAN_CAPABILITIES, licenseFreeBuild: true, backupBuildApi: 'ProsperoPackageBuilder' } })) };
|
|
assert.deepEqual(args.slice(0, 2), ['build-backup', '--request']);
|
|
request = JSON.parse(fs.readFileSync(args[2], 'utf8'));
|
|
fs.writeFileSync(outputPath, Buffer.concat([Buffer.from([0x7f, 0x46, 0x49, 0x48]), Buffer.alloc(64)]));
|
|
fs.writeFileSync(args[4], JSON.stringify({ ok: true, outputPath, buildApi: 'ProsperoPackageBuilder', licenseFree: true, validation: { accepted: true }, unresolvedModules: [], launchReadiness: { isLaunchReady: true }, ...reportOverride }));
|
|
return { code: 0, output: '' };
|
|
};
|
|
const provider = createPs5NativePkgBuilderProvider({ toolRoot: f.toolRoot, allowedWorkRoots: [f.workRoot], allowedSourceRoots: [f.sourceRoot], runner });
|
|
await provider.build({
|
|
componentType: 'base', sourcePath: f.sourceRoot, sourceSize: 6,
|
|
outputPath, workPath: buildWork, titleId: 'PPSA12345',
|
|
contentId: 'UP0001-PPSA12345_00-CHIMERAFPKG00001', version: '01.00', title: 'Fixture',
|
|
});
|
|
assert.equal(request.abi, 'chimera-prospero-bridge-v4');
|
|
assert.equal(request.operation, 'build-backup');
|
|
assert.equal(request.componentType, 'base');
|
|
assert.equal('licenseFree' in request, false);
|
|
assert.equal('fakeSignSelfModules' in request, false);
|
|
assert.equal('tuning' in request, false);
|
|
for (const invalid of [{ licenseFree: false }, { buildApi: 'ProsperoBackupConverter' }, { outputPath: path.join(f.workRoot, 'other.pkg') }, { validation: { accepted: false } }, { unresolvedModules: ['eboot.bin'] }]) {
|
|
reportOverride = invalid;
|
|
await assert.rejects(provider.build({ componentType: 'base', sourcePath: f.sourceRoot, sourceSize: 6, outputPath, workPath: buildWork, titleId: 'PPSA12345', contentId: 'UP0001-PPSA12345_00-CHIMERAFPKG00001' }), { code: 'PS5_BUILDER_OUTPUT' });
|
|
}
|
|
});
|
|
|
|
test('Docker production stage applies only the pinned streaming patch, never the historical V16 series', () => {
|
|
const docker = fs.readFileSync('Dockerfile', 'utf8');
|
|
assert.match(docker, /ARG LIBPROSPEROPKG_COMMIT=9084c0e88cd8d7cd9c0b353b8b6b89f7d1cc4275/);
|
|
assert.match(docker, /patches\/0001-stream-large-package-assembly\.patch/);
|
|
assert.match(docker, /git -C \/src\/LibProsperoPkg apply/);
|
|
const streamingPatch = fs.readFileSync('tools/chimera-prospero-bridge/patches/0001-stream-large-package-assembly.patch', 'utf8');
|
|
assert.match(streamingPatch, /BuildFromFsTreeToFile/);
|
|
assert.match(streamingPatch, /BuildForPackageFile/);
|
|
assert.match(streamingPatch, /ProsperoOuterPfsAddressingPlan/);
|
|
assert.match(streamingPatch, /WriteU2cBase24\(entry\.AsSpan\(4, 3\), nextBase\)/);
|
|
assert.match(streamingPatch, /value is < 0 or > 0xFFFFFF/);
|
|
assert.doesNotMatch(streamingPatch, /MountImageKey|RsaPkcs1EncryptKey|PfsGenEncKey|PfsGenSignKey/);
|
|
assert.doesNotMatch(docker, /CHIMERA_PATCH_SET|chimera-license-free-base-v16/);
|
|
assert.match(docker, /LibProsperoPkg-9084c0e-streaming/);
|
|
});
|
|
|
|
test('historical patches remain documented outside the production path', () => {
|
|
const legacyRoot = 'tools/chimera-prospero-bridge/legacy/v16-patches';
|
|
assert.equal(fs.existsSync(path.join(legacyRoot, '0001-parallel-fast-store.patch')), true);
|
|
const notice = fs.readFileSync(path.join(legacyRoot, 'README.md'), 'utf8');
|
|
assert.match(notice, /historical research only/i);
|
|
assert.match(notice, /not used by the production builder/i);
|
|
});
|