popntowav/twodx.js

88 lines
2.7 KiB
JavaScript
Raw Normal View History

2020-03-17 20:40:39 +01:00
const fs = require("fs");
2020-03-17 21:41:05 +01:00
//This is adapted from https://github.com/mon/SDVX-Song-Extractor/blob/master/bm2dx.py.
//I'm personally not very good at data exploration.
class Sample {
2020-03-17 20:40:39 +01:00
constructor(data, offset, key_no) {
const header = data.toString("ascii", offset, offset+4);
offset += 4;
const header_lead = data.readUInt32LE(offset);
offset += 4;
if (header !== "2DX9" || header_lead != 24) {
throw "Invalid 2DX header.";
}
const size = data.readUInt32LE(offset);
offset += 6;
2020-03-17 21:41:05 +01:00
//Previously the keysound number was stored here.
//In popn files this doesn't appear to be the case.
//So we assume keysounds are sequential in these files.
2020-03-17 20:40:39 +01:00
this.key_no = key_no;
2020-03-17 21:41:05 +01:00
//These two bytes are set to 0000 on keysounds which are
//used as background tracks. This keysound needs to be
//identified as it should be at the start of the container.
2020-03-17 20:40:39 +01:00
this.is_bg = data.toString("hex", offset, offset+2) == "0000";
offset += 2;
2020-03-17 21:41:05 +01:00
2020-03-17 20:40:39 +01:00
//These values were for attenuation and loop point in SDVX 2dxs.
//I have no clue how to make use of these.
this.unk1 = data.readUInt16LE(offset);
offset += 2;
this.unk2 = data.readUInt16LE(offset);
offset += 6;
2020-03-17 21:41:05 +01:00
2020-03-17 20:40:39 +01:00
this.data = data.slice(offset, offset+size);
}
}
class Twodx {
2020-03-17 21:41:05 +01:00
2020-03-17 20:40:39 +01:00
constructor(path) {
this.path = path;
2020-03-17 21:41:05 +01:00
this.data = fs.readFileSync(path);
2020-03-17 20:40:39 +01:00
2020-03-17 21:41:05 +01:00
this.offset = 0;
2020-03-17 20:40:39 +01:00
2020-03-17 21:41:05 +01:00
this.name = this.data.toString("ascii", 0, 16);
this.offset += 16;
this.header_len = this.data.readUInt32LE(this.offset);
this.offset += 4;
this.file_count = this.data.readUInt32LE(this.offset);
this.offset += 52;
2020-03-17 20:40:39 +01:00
2020-03-17 21:41:05 +01:00
const offsets = this.generateOffsets();
this.keysounds = this.generateSamples(offsets);
}
2020-03-17 20:40:39 +01:00
2020-03-17 21:41:05 +01:00
generateOffsets() {
return [...Array(this.file_count).keys()].map((_) => {
const ind = this.data.readUInt32LE(this.offset);
this.offset += 4;
2020-03-17 20:40:39 +01:00
return ind;
});
2020-03-17 21:41:05 +01:00
}
2020-03-17 20:40:39 +01:00
2020-03-17 21:41:05 +01:00
generateSamples(offsets) {
const keysounds = [];
for (let i = 0; i<offsets.length; i++) {
const keysound = new Sample(this.data, offsets[i]);
2020-03-17 20:40:39 +01:00
if (keysound.is_bg) {
2020-03-17 21:41:05 +01:00
//BG tracks are placed at the start of the list
//as this makes it easier to deal with keysound
//indices in chart files.
2020-03-17 20:40:39 +01:00
this.late_bg = i != 0;
2020-03-17 21:41:05 +01:00
keysounds.unshift(keysound);
2020-03-17 20:40:39 +01:00
} else {
2020-03-17 21:41:05 +01:00
keysounds.push(keysound);
2020-03-17 20:40:39 +01:00
}
}
2020-03-17 21:41:05 +01:00
return keysounds;
2020-03-17 20:40:39 +01:00
}
}
module.exports = Twodx;