123 lines
5.8 KiB
JavaScript
123 lines
5.8 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 { assessPs5FpkgControlMetadata, validateParam } = require('../../src/services/ps5-fpkg-control-metadata-assessment');
|
|
const { A8_DISTINCT_CONTROL } = require('../../src/services/ps5-native-pkg-builder-provider');
|
|
|
|
const TITLE_ID = A8_DISTINCT_CONTROL.titleId;
|
|
const CONTENT_ID = A8_DISTINCT_CONTROL.contentId;
|
|
|
|
function metadata(overrides = {}) {
|
|
return {
|
|
applicationCategoryType: 0,
|
|
applicationDrmType: 'free',
|
|
contentBadgeType: 2,
|
|
contentId: CONTENT_ID,
|
|
contentVersion: '01.000.000',
|
|
localizedParameters: { defaultLanguage: 'en-US', 'en-US': { titleName: 'Chimera metadata control' } },
|
|
masterVersion: '01.00',
|
|
requiredSystemSoftwareVersion: '0x0200000000000000',
|
|
sdkVersion: '0x0200000000000000',
|
|
titleId: TITLE_ID,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function fihFixture(t, param = metadata(), mutate = null) {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-control-metadata-'));
|
|
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
|
|
const raw = Buffer.from(JSON.stringify(param));
|
|
const cntOffset = 0x100;
|
|
const entryTableOffset = 0x520;
|
|
const dataOffset = 0x600;
|
|
const bytes = Buffer.alloc(cntOffset + dataOffset + raw.length);
|
|
Buffer.from([0x7f, 0x46, 0x49, 0x48]).copy(bytes);
|
|
bytes.writeBigUInt64LE(BigInt(cntOffset), 0x58);
|
|
Buffer.from([0x7f, 0x43, 0x4e, 0x54]).copy(bytes, cntOffset);
|
|
bytes.writeUInt32BE(1, cntOffset + 0x10);
|
|
bytes.writeUInt32BE(entryTableOffset, cntOffset + 0x18);
|
|
const entry = cntOffset + entryTableOffset;
|
|
bytes.writeUInt32BE(0x2000, entry);
|
|
bytes.writeUInt32BE(dataOffset, entry + 0x10);
|
|
bytes.writeUInt32BE(raw.length, entry + 0x14);
|
|
raw.copy(bytes, cntOffset + dataOffset);
|
|
if (mutate) mutate(bytes, { cntOffset, entry });
|
|
const filename = path.join(root, 'control.pkg');
|
|
fs.writeFileSync(filename, bytes);
|
|
return filename;
|
|
}
|
|
|
|
test('canonical distinct-control metadata qualifies without console or launch inference', t => {
|
|
const filename = fihFixture(t);
|
|
const result = assessPs5FpkgControlMetadata({ sourcePath: filename, expected: { titleId: TITLE_ID, contentId: CONTENT_ID } });
|
|
assert.equal(result.status, 'metadata_qualified');
|
|
assert.equal(result.qualified, true);
|
|
assert.deepEqual(result.issues, []);
|
|
assert.equal(result.consoleContacted, false);
|
|
assert.equal(result.consoleMutationPerformed, false);
|
|
assert.equal(result.installedInferred, false);
|
|
assert.equal(result.launchInferred, false);
|
|
});
|
|
|
|
test('frozen CHIM00001 artifact is rejected for the actual two-component contentVersion', () => {
|
|
const filename = path.resolve('assets/ps5-controls/a8/UP9000-CHIM00001_00-A8CONTROL0000001-A0100-V0100.pkg');
|
|
const result = assessPs5FpkgControlMetadata({
|
|
sourcePath: filename,
|
|
expected: { titleId: 'CHIM00001', contentId: 'UP9000-CHIM00001_00-A8CONTROL0000001' },
|
|
});
|
|
assert.equal(result.status, 'metadata_rejected');
|
|
assert.equal(result.qualified, false);
|
|
assert.equal(result.metadata.contentVersion, '01.00');
|
|
assert.deepEqual(result.issues.find(item => item.field === 'contentVersion'), {
|
|
field: 'contentVersion', expected: 'MM.mmm.ppp', actual: '01.00',
|
|
});
|
|
});
|
|
|
|
test('frozen CHIM00002 canonical control passes the bounded launch-metadata contract', () => {
|
|
const filename = path.resolve('assets', 'ps5-controls', 'a9', A8_DISTINCT_CONTROL.selectedArtifact.filename);
|
|
const result = assessPs5FpkgControlMetadata({
|
|
sourcePath: filename,
|
|
expected: { titleId: A8_DISTINCT_CONTROL.titleId, contentId: A8_DISTINCT_CONTROL.contentId },
|
|
});
|
|
assert.equal(result.status, 'metadata_qualified');
|
|
assert.equal(result.qualified, true);
|
|
assert.equal(result.metadata.titleId, 'CHIM00002');
|
|
assert.equal(result.metadata.contentVersion, '01.000.000');
|
|
assert.equal(result.metadata.masterVersion, '01.00');
|
|
assert.equal(result.metadata.applicationDrmType, 'free');
|
|
assert.deepEqual(result.issues, []);
|
|
assert.equal(result.consoleContacted, false);
|
|
assert.equal(result.installedInferred, false);
|
|
assert.equal(result.launchInferred, false);
|
|
});
|
|
|
|
test('metadata qualification rejects identity, launch fields and incomplete localization independently', () => {
|
|
const issues = validateParam(metadata({
|
|
titleId: 'WRONG0001', contentId: 'wrong', masterVersion: '1', applicationCategoryType: 1,
|
|
applicationDrmType: 'paid', contentBadgeType: 0, requiredSystemSoftwareVersion: '0x2', sdkVersion: null,
|
|
localizedParameters: { defaultLanguage: 'en-US', 'en-US': {} },
|
|
}), { titleId: TITLE_ID, contentId: CONTENT_ID });
|
|
for (const field of [
|
|
'titleId', 'contentId', 'masterVersion', 'applicationCategoryType', 'applicationDrmType',
|
|
'contentBadgeType', 'requiredSystemSoftwareVersion', 'sdkVersion', 'localizedParameters',
|
|
]) assert.ok(issues.some(item => item.field === field), `missing ${field} issue`);
|
|
});
|
|
|
|
test('metadata reader fails closed on encrypted, duplicate and out-of-range param entries', t => {
|
|
const encrypted = fihFixture(t, metadata(), (bytes, { entry }) => bytes.writeUInt32BE(0x80000000, entry + 0x08));
|
|
assert.throws(() => assessPs5FpkgControlMetadata({ sourcePath: encrypted }), { code: 'PS5_FPKG_CONTROL_METADATA_PARAM_ENCRYPTED' });
|
|
|
|
const duplicate = fihFixture(t, metadata(), (bytes, { cntOffset, entry }) => {
|
|
bytes.writeUInt32BE(2, cntOffset + 0x10);
|
|
bytes.copy(bytes, entry + 0x20, entry, entry + 0x20);
|
|
});
|
|
assert.throws(() => assessPs5FpkgControlMetadata({ sourcePath: duplicate }), { code: 'PS5_FPKG_CONTROL_METADATA_PARAM_COUNT' });
|
|
|
|
const outOfRange = fihFixture(t, metadata(), (bytes, { entry }) => bytes.writeUInt32BE(0xffffffff, entry + 0x14));
|
|
assert.throws(() => assessPs5FpkgControlMetadata({ sourcePath: outOfRange }), { code: 'PS5_FPKG_CONTROL_METADATA_PARAM_SIZE' });
|
|
});
|