Crusader_Decomp/scripts/_tmp_probe_psx_section16.js

132 lines
3.9 KiB
JavaScript
Raw Normal View History

2026-04-07 00:15:44 +02:00
const fs = require("fs");
function readU32LE(buffer, offset) {
return buffer.readUInt32LE(offset);
}
function readU16LE(buffer, offset) {
return buffer.readUInt16LE(offset);
}
function parseLsetWdl(data) {
const headerSize = readU32LE(data, 0);
if (headerSize !== 0x34 || headerSize > data.length) {
throw new Error(`unexpected header size ${headerSize}`);
}
const headerWords = [];
for (let offset = 0; offset < headerSize; offset += 4) {
headerWords.push(readU32LE(data, offset));
}
const audioSize = headerWords[1];
const sectionSizes = [];
for (let offset = 0x08; offset < 0x38; offset += 4) {
sectionSizes.push(readU32LE(data, offset));
}
const sections = [];
let cursor = headerSize + audioSize;
for (let index = 0; index < sectionSizes.length; index += 1) {
const size = sectionSizes[index];
if (size <= 0 || cursor + size > data.length) {
break;
}
sections.push({
index,
name: `post_audio_section_${String(index).padStart(2, "0")}`,
offset: cursor,
size
});
cursor += size;
}
return { sections };
}
function parseTypedSection16(data, section, startOffset) {
const bytes = data.subarray(section.offset + startOffset, section.offset + section.size);
if (bytes.length < 8) {
return null;
}
const recordCount = readU32LE(bytes, 0);
const payloadBytes = readU32LE(bytes, 4);
const headerOffset = 8 + payloadBytes;
if (recordCount <= 0 || recordCount > 0x400) {
return null;
}
if (payloadBytes < 0 || headerOffset + recordCount * 16 > bytes.length) {
return null;
}
let payloadCursor = 8;
const records = [];
for (let index = 0; index < recordCount; index += 1) {
const descriptorOffset = headerOffset + index * 16;
const d4Size = readU32LE(bytes, descriptorOffset);
const ccSize = readU32LE(bytes, descriptorOffset + 4);
const d0Size = readU32LE(bytes, descriptorOffset + 8);
const typeId = readU16LE(bytes, descriptorOffset + 12);
const variantTypeId = readU16LE(bytes, descriptorOffset + 14);
const endOffset = payloadCursor + ccSize + d0Size + d4Size;
if (endOffset > headerOffset) {
return null;
}
records.push({ index, typeId, variantTypeId, ccSize, d0Size, d4Size, payloadCursor });
payloadCursor = endOffset;
}
return {
sectionName: section.name,
startOffset,
recordCount,
payloadBytes,
headerOffset,
records
};
}
function summarizeCandidate(candidate, wantedTypes) {
return {
sectionName: candidate.sectionName,
startOffset: `0x${candidate.startOffset.toString(16)}`,
recordCount: candidate.recordCount,
payloadBytes: `0x${candidate.payloadBytes.toString(16)}`,
wanted: candidate.records
.filter((record) => wantedTypes.has(record.typeId) || wantedTypes.has(record.variantTypeId))
.map((record) => ({
index: record.index,
typeId: `0x${record.typeId.toString(16)}`,
variantTypeId: `0x${record.variantTypeId.toString(16)}`,
ccSize: `0x${record.ccSize.toString(16)}`,
d0Size: `0x${record.d0Size.toString(16)}`,
d4Size: `0x${record.d4Size.toString(16)}`,
payloadCursor: `0x${record.payloadCursor.toString(16)}`
}))
};
}
function main() {
const filePath = process.argv[2];
const wantedTypes = new Set(process.argv.slice(3).map((value) => Number.parseInt(value, 16)));
const data = fs.readFileSync(filePath);
const parsed = parseLsetWdl(data);
const candidates = [];
for (const section of parsed.sections) {
for (let startOffset = 0; startOffset < Math.min(section.size, 0x800); startOffset += 4) {
const candidate = parseTypedSection16(data, section, startOffset);
if (!candidate) {
continue;
}
const summary = summarizeCandidate(candidate, wantedTypes);
if (summary.wanted.length > 0) {
candidates.push(summary);
}
}
}
console.log(JSON.stringify(candidates, null, 2));
}
main();