mirror of
https://github.com/asphyxia-core/plugins.git
synced 2026-03-21 17:34:46 -05:00
212 lines
6.4 KiB
TypeScript
212 lines
6.4 KiB
TypeScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
export function unpackS3P(directory, filePath, names) {
|
|
const stream = fs.readFileSync(filePath);
|
|
if (stream.slice(0, 4).toString() !== 'S3P0') {
|
|
throw new Error('Invalid S3P file');
|
|
}
|
|
|
|
let offset = 4;
|
|
const entries = stream.readUInt32LE(offset);
|
|
offset += 4;
|
|
|
|
const offsets = [];
|
|
for (let i = 0; i < entries; i++) {
|
|
const offsetValue = stream.readUInt32LE(offset);
|
|
offset += 4;
|
|
const length = stream.readUInt32LE(offset);
|
|
offset += 4;
|
|
offsets.push([offsetValue, length]);
|
|
}
|
|
|
|
|
|
|
|
for (let n = 0; n < offsets.length; n++) {
|
|
const [offsetValue, length] = offsets[n];
|
|
offset = offsetValue;
|
|
if (stream.slice(offset, offset + 4).toString() !== 'S3V0') {
|
|
throw new Error('Invalid S3V0 header');
|
|
}
|
|
offset += 4;
|
|
const hlen = stream.readUInt32LE(offset);
|
|
offset += 4;
|
|
const headerExtra = stream.slice(offset, offset + hlen - 8);
|
|
offset += hlen - 8;
|
|
// const [wmaFileLength, , , , , , ,] = new Uint32Array(headerExtra.buffer);
|
|
|
|
const data = stream.slice(offset, offset + length - hlen);
|
|
offset += length - hlen;
|
|
|
|
const outPath = path.join(directory, `${names[n] || n}.wma`);
|
|
console.log(`I: ${outPath}`);
|
|
fs.writeFileSync(outPath, data);
|
|
}
|
|
}
|
|
|
|
export function packS3P(directory, output, names) {
|
|
let paths = fs.readdirSync(directory);
|
|
if (names) {
|
|
const namesBack = {};
|
|
for (const key in names) {
|
|
namesBack[names[key]] = key;
|
|
}
|
|
paths = paths.filter((i) => namesBack[i.split('.')[0]]);
|
|
paths.sort((a, b) => namesBack[a.split('.')[0]] - namesBack[b.split('.')[0]]);
|
|
} else {
|
|
paths.sort((a, b) => parseInt(a.split('.')[0]) - parseInt(b.split('.')[0]));
|
|
}
|
|
|
|
let offset = 0;
|
|
const out = Buffer.alloc(4 + 4 + 8 * paths.length);
|
|
|
|
out.write('S3P0', offset, 'binary');
|
|
offset += 4;
|
|
|
|
out.writeUInt32LE(paths.length, offset);
|
|
offset += 4;
|
|
|
|
for (let i = 0; i < paths.length; i++) {
|
|
out.writeUInt32LE(0, offset);
|
|
offset += 4;
|
|
out.writeUInt32LE(0, offset);
|
|
offset += 4;
|
|
}
|
|
|
|
const offsets = [];
|
|
for (const i of paths) {
|
|
const data = fs.readFileSync(path.join(directory, i));
|
|
offsets.push([offset, data.length + 32]);
|
|
|
|
out.write('S3V0', offset, 'binary');
|
|
offset += 4;
|
|
|
|
out.writeUInt32LE(32, offset);
|
|
offset += 4;
|
|
|
|
const headerExtra = Buffer.alloc(20);
|
|
headerExtra.writeUInt32LE(data.length, 0);
|
|
headerExtra.writeUInt32LE(0, 4); // You might need to adjust this value
|
|
headerExtra.writeUInt32LE(64130, 8);
|
|
out.writeUInt16LE(0, 12); // Short
|
|
out.writeUInt16LE(0, 14); // Short
|
|
out.writeUInt16LE(0, 16); // Short
|
|
out.writeUInt16LE(0, 18); // Short
|
|
out.writeUInt32LE(0, 20); // Reserved
|
|
headerExtra.copy(out, offset + 4, 0, 20); // Copy headerExtra excluding the first 4 bytes
|
|
offset += 24;
|
|
|
|
data.copy(out, offset, 0, data.length);
|
|
offset += data.length;
|
|
}
|
|
|
|
// Re-write tracks table
|
|
offset = 8;
|
|
for (const [offsetValue, length] of offsets) {
|
|
out.writeUInt32LE(offsetValue, offset);
|
|
offset += 4;
|
|
out.writeUInt32LE(length, offset);
|
|
offset += 4;
|
|
}
|
|
|
|
fs.writeFileSync(output, out);
|
|
}
|
|
|
|
|
|
function usage() {
|
|
console.log(`Usage: node ${process.argv[1]} pack <output.s3p> <directory> [enum/def path]`);
|
|
console.log(` node ${process.argv[1]} unpack <intput.s3p> <directory> [enum/def path]`);
|
|
process.exit(1);
|
|
}
|
|
|
|
function loadNames(filePath) {
|
|
const base = path.join(path.dirname(filePath), path.basename(filePath, path.extname(filePath)));
|
|
const filenames = {};
|
|
|
|
if (fs.existsSync(base + '.def')) {
|
|
const defData = fs.readFileSync(base + '.def', 'utf8');
|
|
const lines = defData.split('\n');
|
|
for (const line of lines) {
|
|
if (line.trim().startsWith('#define')) {
|
|
const [, name, id] = line.trim().split(/\s+/);
|
|
filenames[parseInt(id)] = name;
|
|
}
|
|
}
|
|
} else if (fs.existsSync(base + '.enum')) {
|
|
const enumData = fs.readFileSync(base + '.enum', 'utf8');
|
|
if (enumData.startsWith('enum struct ')) {
|
|
const lines = enumData.split('\n');
|
|
for (let i = 1; i < lines.length; i++) {
|
|
const line = lines[i].trim();
|
|
if (line === '};') {
|
|
break;
|
|
}
|
|
filenames[i - 1] = line.replace(',', '').trim();
|
|
}
|
|
}
|
|
}
|
|
|
|
return filenames;
|
|
}
|
|
|
|
// function main() {
|
|
// if (process.argv.length !== 5 && process.argv.length !== 6) {
|
|
// usage();
|
|
// }
|
|
// if (process.argv[2] !== 'pack' && process.argv[2] !== 'unpack') {
|
|
// usage();
|
|
// }
|
|
|
|
// const s3p = process.argv[3];
|
|
// const directory = process.argv[4];
|
|
|
|
// let names = {};
|
|
// if (process.argv.length === 6) {
|
|
// names = loadNames(process.argv[5]);
|
|
// } else {
|
|
// names = loadNames(s3p);
|
|
// }
|
|
|
|
// if (!names) {
|
|
// console.log('W: Filenames not loaded');
|
|
// }
|
|
|
|
// if (process.argv[2] === 'pack') {
|
|
// if (!fs.existsSync(directory)) {
|
|
// console.error(`F: No such file or directory ${directory}`);
|
|
// process.exit(1);
|
|
// }
|
|
|
|
// const files = fs.readdirSync(directory);
|
|
// if (!files.every((i) => /^\d+\.wma$/.test(i) || Object.values(names).includes(i.split('.')[0]))) {
|
|
// console.error('F: Files must all be [number].wma');
|
|
// process.exit(1);
|
|
// }
|
|
|
|
// const dirname = path.dirname(s3p);
|
|
// if (dirname) {
|
|
// fs.mkdirSync(dirname, { recursive: true });
|
|
// }
|
|
|
|
// packS3P(directory, s3p, names);
|
|
// console.log(`I: ${s3p}`);
|
|
// } else {
|
|
// if (!fs.existsSync(s3p)) {
|
|
// console.error(`F: No such file or directory ${s3p}`);
|
|
// process.exit(1);
|
|
// }
|
|
|
|
// if (fs.existsSync(directory) && !fs.lstatSync(directory).isDirectory()) {
|
|
// console.error('F: Output is not a directory');
|
|
// process.exit(1);
|
|
// }
|
|
|
|
// fs.mkdirSync(directory, { recursive: true });
|
|
// unpackS3P(directory, s3p, names);
|
|
// }
|
|
// }
|
|
|
|
// if (require.main === module) {
|
|
// main();
|
|
// }
|