Last active
July 20, 2020 22:05
-
-
Save mayumi7/ca9e58a21459ccc76ee09873cff5000f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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