127 lines
4.8 KiB
JavaScript
127 lines
4.8 KiB
JavaScript
const fs = require('fs');
|
|
const execFile = require('child_process').execFile;
|
|
const path = require('path');
|
|
|
|
const rimraf = require('rimraf');
|
|
rimraf.sync('tmp');
|
|
|
|
const mkdirp = require('mkdirp');
|
|
mkdirp.sync('tmp');
|
|
mkdirp.sync('mogg_input');
|
|
mkdirp.sync('mogg_output');
|
|
|
|
mkdirp.sync('game_input');
|
|
mkdirp.sync('game_output');
|
|
|
|
const fileExists = async path => !!(await fs.promises.stat(path).catch(e => false));
|
|
const readFile = async path => await fs.promises.readFile(path);
|
|
|
|
async function moggify() {
|
|
const files = await fs.promises.readdir('mogg_input');
|
|
|
|
for(const file of files) {
|
|
console.log(file);
|
|
|
|
const from = path.join('mogg_input', file);
|
|
const tmp = path.join('tmp', path.parse(file).name + '.mogg');
|
|
const to = path.join('mogg_output', path.parse(file).name + '.mogg');
|
|
|
|
await new Promise(resolve => {
|
|
execFile('ogg2mogg.exe', [from, tmp], () => {
|
|
execFile('MoggcryptCpp.exe', [tmp, '-e', to], () => {
|
|
resolve();
|
|
});
|
|
});
|
|
})
|
|
}
|
|
}
|
|
|
|
async function replace() {
|
|
if (process.argv.length !== 4) {
|
|
console.error('Usage: node replace.js <input_mogg_file> <input_game_file>');
|
|
console.log('(No file extensions)');
|
|
return;
|
|
}
|
|
|
|
const mogg_file = process.argv[2] + '.mogg';
|
|
const game_file = process.argv[3];
|
|
|
|
const mogg_exists = await fileExists(`mogg_output/${mogg_file}`);
|
|
const uexp_exists = await fileExists(`game_input/${game_file}.uexp`);
|
|
const uasset_exists = await fileExists(`game_input/${game_file}.uasset`);
|
|
|
|
if (!mogg_exists) {
|
|
console.error(`Couldn't find the .mogg file '${mogg_file}'.`);
|
|
return;
|
|
}
|
|
|
|
if (!uexp_exists || !uasset_exists) {
|
|
console.error(`Couldn't find a .uexp and .uasset combo for '${game_file}'.`);
|
|
return;
|
|
}
|
|
|
|
const out_uexp = path.join('game_output', game_file + '.uexp');
|
|
const out_uasset = path.join('game_output', game_file + '.uasset');
|
|
|
|
// Read .mogg
|
|
const mogg_buffer = await readFile(`mogg_output/${mogg_file}`);
|
|
|
|
// Modify .uexp
|
|
const data_buffer = await readFile(`game_input/${game_file}.uexp`);
|
|
const old_uexp_length = data_buffer.length;
|
|
|
|
// Get the start offset by getting the first index of MoggSampleResource, then offset by 19
|
|
const start_offset = data_buffer.indexOf('MoggSampleResource') + 19;
|
|
|
|
// Write new MOGG Buffer length + 32 to offset start_offset
|
|
data_buffer.writeInt32LE(mogg_buffer.length + 32, start_offset);
|
|
|
|
// Write new MOGG Buffer length to offset start_offset + 36, store old for buffering
|
|
const old_1_length = data_buffer.readInt32LE(start_offset + 36);
|
|
data_buffer.writeInt32LE(mogg_buffer.length, start_offset + 36);
|
|
|
|
// Insert MOGG at start_offset + 36 + 4, then offset to the new position
|
|
const pre_buffer_1 = data_buffer.slice(0, start_offset + 36 + 4);
|
|
const post_buffer_1 = data_buffer.slice(start_offset + 36 + 4 + old_1_length);
|
|
|
|
// Concat buffers
|
|
const song_1_insert_buffer = Buffer.concat([pre_buffer_1, mogg_buffer, post_buffer_1]);
|
|
|
|
// Only replace 2nd MoggSampleResource if numResourceFiles === 3
|
|
let final_buffer = song_1_insert_buffer;
|
|
const filenameLength = final_buffer.readInt16LE(28);
|
|
const numResourceFiles = final_buffer.readInt16LE(28 + filenameLength + 12);
|
|
if (numResourceFiles === 3) {
|
|
// Get the start offset by getting the first index of MoggSampleResource after our mogg_buffer, then offset by 19
|
|
const new_offset = song_1_insert_buffer.indexOf('MoggSampleResource', mogg_buffer.length) + 19;
|
|
|
|
// Write new MOGG Buffer length + 32 to offset new_offset
|
|
song_1_insert_buffer.writeInt32LE(mogg_buffer.length + 32, new_offset);
|
|
|
|
// Write new MOGG Buffer length to offset new_offset + 36, store old for buffering
|
|
const old_2_length = song_1_insert_buffer.readInt32LE(new_offset + 36);
|
|
song_1_insert_buffer.writeInt32LE(mogg_buffer.length, new_offset + 36);
|
|
|
|
// Insert MOGG at new_offset + 36 + 4, then offset to the new position
|
|
const pre_buffer_2 = song_1_insert_buffer.slice(0, new_offset + 36 + 4);
|
|
const post_buffer_2 = song_1_insert_buffer.slice(new_offset + 36 + 4 + old_2_length);
|
|
|
|
// Concat buffers
|
|
final_buffer = Buffer.concat([pre_buffer_2, mogg_buffer, post_buffer_2]);
|
|
}
|
|
|
|
// Finally, write
|
|
await fs.promises.writeFile(out_uexp, final_buffer);
|
|
|
|
// Modify .uasset
|
|
const uasset_buffer = await readFile(`game_input/${game_file}.uasset`);
|
|
|
|
const update_size_offset = uasset_buffer.lastIndexOf(old_uexp_length - 4);
|
|
uasset_buffer.writeInt32LE(final_buffer.length - 4, update_size_offset);
|
|
|
|
// Finally, write
|
|
await fs.promises.writeFile(out_uasset, uasset_buffer);
|
|
}
|
|
|
|
moggify().then(replace);
|