Files
Mateusz Najdek 3c9891652f Release v1.2.2 (#7)
* fix(parser): honor PS4 base SFO category

* feat(cache): rescan after cache invalidation

* bump version to 1.2.2

* docs: document cache refresh and package fixes
2026-09-22 12:29:49 +02:00

97 lines
3.9 KiB
JavaScript

import assert from 'node:assert/strict';
import { parseLocalPkg } from '../src/utils/parseLocalPkg.js';
const bytes = new Uint8Array(0x140000);
const view = new DataView(bytes.buffer);
const encode = new TextEncoder();
bytes.set([0x7f, 0x43, 0x4e, 0x54], 0);
view.setUint32(0x10, 2, false);
view.setUint32(0x18, 0x100, false);
bytes.set(encode.encode('UP0000-GAME12345_00-1234567890123456'), 0x40);
const json = encode.encode('{"titleId":"GAME12345","titleName":"Beyond Header","category":"gp","appVersion":"1.02"}');
view.setUint32(0x100, 0x2000, false);
view.setUint32(0x110, 0x120000, false);
view.setUint32(0x114, json.length, false);
bytes.set(json, 0x120000);
view.setUint32(0x120, 0x1200, false);
view.setUint32(0x130, 0x130000, false);
view.setUint32(0x134, 9, false);
bytes.set([0x89, 0x50, 0x4e, 0x47, 13, 10, 26, 10, 0], 0x130000);
const file = new Blob([bytes]);
file.name = 'beyond.pkg';
const details = await parseLocalPkg(file);
assert.equal(details.title_name, 'Beyond Header');
assert.equal(details.title_id, 'GAME12345');
assert.equal(details.app_version, 'v1.02');
assert.equal(details.pkg_type, 'update');
assert.equal(details.icon_offset, 0x130000);
assert.equal(details.icon_size, 9);
console.log('parseLocalPkg: metadata and icon beyond first 1 MB passed');
const ps4 = new Uint8Array(0x120000);
const p4 = new DataView(ps4.buffer);
ps4.set([0x7f, 0x43, 0x4e, 0x54], 0);
p4.setUint32(0x10, 1, false);
p4.setUint32(0x18, 0x100, false);
p4.setUint32(0x100, 0x1000, false);
p4.setUint32(0x110, 0x110000, false);
const sfo = new Uint8Array(160);
const sv = new DataView(sfo.buffer);
sv.setUint32(0, 0x46535000, true);
sv.setUint32(8, 68, true);
sv.setUint32(12, 104, true);
sv.setUint32(16, 3, true);
sfo.set(encode.encode('TITLE\0TITLE_ID\0CATEGORY\0'), 68);
const ps4Title = encode.encode('PS4 Package\0');
const ps4Id = encode.encode('CUSA12345\0');
sv.setUint32(24, ps4Title.length, true);
sv.setUint32(32, 0, true);
sv.setUint16(36, 6, true);
sv.setUint32(40, ps4Id.length, true);
sv.setUint32(48, 20, true);
sv.setUint16(52, 15, true);
sv.setUint32(56, 3, true);
sv.setUint32(64, 36, true);
sfo.set(ps4Title, 104);
sfo.set(ps4Id, 124);
sfo.set(encode.encode('gd\0'), 140);
p4.setUint32(0x04, 1, false);
p4.setUint32(0x114, sfo.length, false);
ps4.set(sfo, 0x110000);
const ps4File = new Blob([ps4]);
ps4File.name = 'ps4.pkg';
const ps4Details = await parseLocalPkg(ps4File);
assert.equal(ps4Details.title_name, 'PS4 Package');
assert.equal(ps4Details.title_id, 'CUSA12345');
assert.equal(ps4Details.pkg_type, 'base');
console.log('parseLocalPkg: PS4 CATEGORY=gd overrides CNT type 1');
const dlcBytes = new Uint8Array(0x120000);
const dv = new DataView(dlcBytes.buffer);
dlcBytes.set([0x7f, 0x43, 0x4e, 0x54], 0);
dv.setUint32(0x04, 0x00020001, false);
dv.setUint32(0x10, 1, false);
dv.setUint32(0x18, 0x100, false);
dlcBytes.set(encode.encode('TEST00-TEST00000_00-DLCMETADATA0001'), 0x40);
dv.setUint32(0x100, 0x2000, false);
dv.setUint32(0x110, 0x110000, false);
const dlcJson = encode.encode('{"titleId":"TEST00000","titleName":"DLC Package","contentVersion":"01.000.000"}');
dv.setUint32(0x114, dlcJson.length, false);
dlcBytes.set(dlcJson, 0x110000);
const dlcFile = new Blob([dlcBytes]);
dlcFile.name = 'dlc-package.pkg';
const dlcDetails = await parseLocalPkg(dlcFile);
assert.equal(dlcDetails.pkg_type, 'dlc');
assert.equal(dlcDetails.title_id, 'TEST00000');
console.log('parseLocalPkg: CNT type 0x00020001 DLC detection passed');
const baseBytes = new Uint8Array(dlcBytes);
const baseView = new DataView(baseBytes.buffer);
const baseJson = encode.encode('{"titleId":"TEST00000","titleName":"Base Package","applicationDrmType":"standard","contentVersion":"01.000.000"}');
baseView.setUint32(0x114, baseJson.length, false);
baseBytes.set(baseJson, 0x110000);
const baseDetails = await parseLocalPkg(new Blob([baseBytes]));
assert.equal(baseDetails.pkg_type, 'base');
console.log('parseLocalPkg: base application metadata overrides shared CNT low byte');