import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import test from 'node:test'; import { comparePs5FihRoute, inspectPs5FihPackage } from '../../scripts/autonomous/inspect-ps5-fih-route.mjs'; function writeFixture(root, name, { contentFlags = 0x00020000, outerFileCount = 1, extraEntry = false } = {}) { const filename = path.join(root, name); const pfsOffset = 0x10000; const pfsSize = 0x10000; const cntOffset = pfsOffset + pfsSize; const imageKeyOffset = 0x1000; const entryCount = extraEntry ? 2 : 1; const bytes = Buffer.alloc(cntOffset + imageKeyOffset + 0x800); Buffer.from([0x7f, 0x46, 0x49, 0x48]).copy(bytes, 0); bytes[4] = 1; bytes.writeUInt16LE(3, 6); bytes.writeBigUInt64LE(BigInt(pfsOffset), 0x10); bytes.writeBigUInt64LE(BigInt(pfsSize), 0x18); bytes.writeUInt32LE(outerFileCount, 0xf0); bytes.writeBigUInt64LE(BigInt(cntOffset), 0x58); bytes.writeBigUInt64LE(2n, pfsOffset); bytes.writeUInt32LE(20_130_315, pfsOffset + 8); bytes.writeUInt16LE(0x0d, pfsOffset + 0x1c); bytes.writeUInt32LE(1, pfsOffset + 0x24); Buffer.from([0x7f, 0x43, 0x4e, 0x54]).copy(bytes, cntOffset); bytes.writeUInt32BE(0x81000000, cntOffset + 4); bytes.writeUInt32BE(entryCount, cntOffset + 0x10); bytes.writeUInt16BE(1, cntOffset + 0x14); bytes.writeUInt16BE(entryCount, cntOffset + 0x16); bytes.writeUInt32BE(0x100, cntOffset + 0x18); bytes.writeBigUInt64BE(0x1000n, cntOffset + 0x20); Buffer.from('IV9999-FAKE00000_00-ROUTEINSPECT0001').copy(bytes, cntOffset + 0x40); bytes.writeUInt32BE(0, cntOffset + 0x70); bytes.writeUInt32BE(0x20, cntOffset + 0x74); bytes.writeUInt32BE(contentFlags, cntOffset + 0x78); bytes.writeUInt32BE(1, cntOffset + 0x404); bytes.writeBigUInt64BE(BigInt(pfsOffset), cntOffset + 0x410); bytes.writeBigUInt64BE(BigInt(pfsSize), cntOffset + 0x418); bytes.writeUInt32BE(0x20, cntOffset + 0x100); bytes.writeUInt32BE(0, cntOffset + 0x104); bytes.writeUInt32BE(0x60000000, cntOffset + 0x108); bytes.writeUInt32BE(0, cntOffset + 0x10c); bytes.writeUInt32BE(imageKeyOffset, cntOffset + 0x110); bytes.writeUInt32BE(0x800, cntOffset + 0x114); if (extraEntry) { bytes.writeUInt32BE(0x402, cntOffset + 0x120); bytes.writeUInt32BE(1, cntOffset + 0x124); bytes.writeUInt32BE(0x08000000, cntOffset + 0x128); bytes.writeUInt32BE(0, cntOffset + 0x12c); bytes.writeUInt32BE(0x1800, cntOffset + 0x130); bytes.writeUInt32BE(0, cntOffset + 0x134); } bytes.fill(0xa5, cntOffset + imageKeyOffset, cntOffset + imageKeyOffset + 0x800); fs.writeFileSync(filename, bytes); return filename; } test('route inspector separates trust-profile fields from content accounting and asset entries', () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-fih-route-')); try { const reference = writeFixture(root, 'reference.pkg'); const candidate = writeFixture(root, 'candidate.pkg', { contentFlags: 0x02000000, outerFileCount: 71, extraEntry: true, }); const inspected = inspectPs5FihPackage(reference); assert.equal(inspected.cnt.header.contentFlags, '0x00020000'); assert.equal(inspected.fih.accounting.outerFileCount, 1); const comparison = comparePs5FihRoute(reference, candidate); assert.equal(comparison.schemaVersion, 2); assert.deepEqual(comparison.routeGate.mismatches.map((item) => item.field), ['cnt.header.contentFlags']); assert.deepEqual(comparison.contentAccountingDifferences.map((item) => item.field), ['fih.accounting.outerFileCount']); assert.deepEqual(comparison.entrySetDifferences.onlyInCandidate.map((item) => item.id), ['0x00000402']); } finally { fs.rmSync(root, { recursive: true, force: true }); } });