mirror of
https://github.com/itsPLK/ps5-pkg-manager.git
synced 2026-10-22 11:53:08 -05:00
* 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
97 lines
3.9 KiB
JavaScript
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');
|