Skip to content

Instantly share code, notes, and snippets.

@mayumi7
Last active July 20, 2020 22:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mayumi7/ca9e58a21459ccc76ee09873cff5000f to your computer and use it in GitHub Desktop.
Save mayumi7/ca9e58a21459ccc76ee09873cff5000f to your computer and use it in GitHub Desktop.
// Usage: boundlessDat.decode(uint8array)
boundlessDat = {
decode : function(uint8Array) {
let decoder = new DatDecoder();
return decoder.decode(uint8Array);
}
};
class DatDecoder {
decode(uint8Array) {
this.uint8Array = uint8Array;
let dataView = new DataView(this.uint8Array.buffer);
this.dataView = dataView;
let curPos = 0;
let metalsMaxIndex = dataView.getUint8(curPos); // metals-palette max index
curPos += 1;
let itemTypeCount = dataView.getUint16(curPos, true); // number of encoded ItemType's
curPos += 2;
let itemTypes = [];
for (let i = 0; i < itemTypeCount; i++) {
itemTypes.push([
dataView.getUint16(i * 3 + curPos, true), // ItemType
dataView.getUint8(i * 3 + curPos + 2) // subtitle-index
]);
}
let subtitleMaxIndex = Math.max(...itemTypes.map((a) => a[1]));
curPos += itemTypeCount * 3;
let languageCount = dataView.getUint8(curPos); // number of encoded languages
curPos += 1;
let languagePointers = {}
for (let i = 0; i < languageCount; i++) {
let languageNameLength = dataView.getUint8(curPos); // length of language identifier
let languageName = this.decodeASCII(uint8Array.subarray(curPos + 1, curPos + 1 + languageNameLength)); // identifier [ no null terminator ] ascii
let languageAddress = dataView.getUint32(curPos + 1 + languageNameLength, true); // offset in buffer to start of language encoding
languagePointers[languageName] = languageAddress;
curPos += 5 + languageNameLength;
}
let languageStrings = {};
for(let langName in languagePointers) {
curPos = languagePointers[langName];
let colorStringsPointer = dataView.getUint32(curPos, true); // offset in buffer to start of color strings
let metalStringsPointer = dataView.getUint32(curPos + 4, true); // offset in buffer to start of metal strings
let itemStringsPointer = dataView.getUint32(curPos + 8, true); // offset in buffer to start of item-title strings
let subtitlePointer = curPos + 12;
let itemSubtitles = this.decodeEncodings(subtitlePointer, subtitleMaxIndex);
//let colorStrings = this.decodeEncodings(colorStringsPointer, 255);
//let metalStrings = this.decodeEncodings(metalStringsPointer, metalsMaxIndex);
let itemStrings = this.decodeEncodings(itemStringsPointer, itemTypeCount);
let items = {};
for(let i in itemTypes) {
items[itemTypes[i][0]] = [
itemStrings[i],
itemSubtitles[itemTypes[i][1]]
];
}
languageStrings[langName] = items;
}
return languageStrings;
}
decodeEncodings(pointer, indiceCount) {
let lengthLookup = [3,6,3,10];
let dataView = this.dataView;
let uint8Array = this.uint8Array;
let curPos = pointer;
let encodingsPointer = dataView.getUint32(curPos, true); // offset in buffer to start of "encodings"
let wordsIndexPointer = dataView.getUint32(curPos + 4, true); // offset in buffer to start of words-index
let wordsPointer = dataView.getUint32(curPos + 8, true); // offset in buffer to start of "words"
curPos += 12;
let encodingIndexBitsPerValue = dataView.getUint8(curPos); // bit-count for encodings-index values
let encodingIndex = [];
for(let i = 0; i < indiceCount; i++) {
encodingIndex.push(this.getUintN(curPos + 1, i, encodingIndexBitsPerValue));
}
let wordsMaxIndex = 0;
let encodings = [];
for(let i in encodingIndex) {
let encodingOffset = 1;
let encodingPointer = encodingsPointer + encodingIndex[i];
let wordCountLength = dataView.getUint8(encodingPointer) % 4;
wordCountLength = lengthLookup[wordCountLength];
if(wordCountLength > 3) { // offset more if it's a long string (10 or 11)
encodingOffset++;
}
let wordCount = (dataView.getUint32(encodingPointer, true) >>> encodingOffset) % Math.pow(2, wordCountLength);
encodingOffset += wordCountLength;
let words = [];
while(wordCount > 0) {
let wordOffset = 1;
let wordPointer = encodingPointer + Math.floor((wordOffset + encodingOffset) / 8);
let wordLength = (dataView.getUint32(wordPointer, true) >>> (encodingOffset % 8)) % 4;
wordLength = lengthLookup[wordLength];
if(wordLength > 3) {
wordOffset++;
}
let wordIndex = ((dataView.getUint32(wordPointer, true) >>> ((wordOffset + encodingOffset) % 8)) % Math.pow(2, wordLength));
wordsMaxIndex = (wordsMaxIndex < wordIndex ? wordIndex : wordsMaxIndex);
words.push(wordIndex);
encodingOffset += wordOffset + wordLength; // add the word offset and length to the main offset
wordCount--;
}
encodings.push(words);
}
wordsMaxIndex++; // add the end() iterator
let wordsIndexBitsPerValue = dataView.getUint8(wordsIndexPointer); // bit-count for words-index values
let wordsIndex = [];
for(let i = 0; i < wordsMaxIndex; i++) {
wordsIndex.push(this.getUintN(wordsIndexPointer + 1, i, wordsIndexBitsPerValue));
}
let words = [];
for(let i = 0; i < wordsIndex.length; i++) {
let fromLen = wordsIndex[i];
let toLen = wordsIndex[i+1];
if(toLen !== undefined) { // it's not the last element
let string = this.decodeISO8859(uint8Array.subarray(wordsPointer + fromLen, wordsPointer + toLen));
words.push(string);
}
}
// map the strings to encodings
let finalStrings = [];
for(let i in encodings) {
finalStrings.push(encodings[i].map((a) => words[a]));
}
finalStrings = finalStrings.map((a) => a.join(' '));
return finalStrings;
}
getUintN(startPointer, index, valueBitLength) {
let pointerOffset = Math.floor(index * valueBitLength / 8);
let pointer = startPointer + pointerOffset;
let bitOffset = index * valueBitLength % 8;
let value = this.dataView.getUint32(pointer, true);
value = (value >>> bitOffset) % Math.pow(2, valueBitLength);
return value;
}
decodeISO8859(data) {
let isoDecoder = new TextDecoder('iso8859-1');
let str = isoDecoder.decode(data);
return str;
}
decodeASCII(data) {
return String.fromCharCode(...data);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment