icondumper2/lzari.js

261 lines
6.8 KiB
JavaScript

// based on Luigi Auriemma's memory2memory LZARI modification. LZARI was created by Haruhiko Okumura.
// yellows111 modifications: forced all magic constants to be their actual values,
// static calculations have been squashed, some functions have been transcluded into others
/** @copyright MIT license:
* Based on a work by Haruhiko Okumura dated 1989-07-04.
* Copyright 2023 yellows111
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
//TODO: privatize variables and document the library
var inputData = null;
var inputLocation = 0;
var low = 0;
var high = 131072;
var value = 0;
var text_buffer = new Array(4155);
var characterToSymbol = new Array(314);
var symbolToCharacter = new Array(315);
var symbolFrequency = new Array(315);
var symbolCumulative = new Array(315);
var positionCumulative = new Array(4097);
var bit_Buffer = 0;
var bit_Mask = 0;
function GetBit() {
//partial xgetc modification
if(inputLocation >= inputData.length) {return -1};
if((bit_Mask >>= 1) === 0) {
bit_Buffer = inputData[inputLocation++];
bit_Mask = 128;
}
return +((bit_Buffer & bit_Mask) !== 0);
}
function BinarySearchSym(x) {
let i = 1;
let j = 314;
while (i < j) {
let k = ((i + j) / 2)|0;
if (symbolCumulative[k] > x) {
i = k + 1;
} else {
j = k;
}
}
return i;
}
function BinarySearchPos(x) {
let i = 1;
let j = 4096;
while (i < j) {
let k = ((i + j) / 2)|0;
if (positionCumulative[k] > x) {
i = k + 1;
} else {
j = k;
}
}
return i - 1;
}
function DecodeChar() {
var range = high - low;
var sym = BinarySearchSym((((value - low + 1) * symbolCumulative[0] - 1) / range)|0);
high = low + ((range * symbolCumulative[sym - 1]) / symbolCumulative[0])|0;
low += ((range * symbolCumulative[sym ]) / symbolCumulative[0])|0;
for ( ; ; ) {
if (low >= 65536) {
value -= 65536;
low -= 65536;
high -= 65536;
} else if (low >= 32768 && high <= 98304) {
value -= 32768;
low -= 32768;
high -= 32768;
} else if (high > 65536) { break };
low += low;
high += high;
value = 2 * value + GetBit();
}
//transcluded UpdateModel
let character = symbolToCharacter[sym];
// do not remove above, will be overwritten otherwise!
let i;
if(symbolCumulative[0] >= 32767) {
let chr = 0;
for (i = 314; i > 0; i--) {
symbolCumulative[i] = chr;
chr += (symbolFrequency[i] = (symbolFrequency[i] + 1) >> 1);
}
symbolCumulative[0] = chr;
}
for(i = sym; symbolFrequency[i] === symbolFrequency[i - 1]; i--) {};
if (i < sym) {
let ch_i = symbolToCharacter[i];
let ch_sym = symbolToCharacter[sym];
symbolToCharacter[i] = ch_sym;
symbolToCharacter[sym] = ch_i;
characterToSymbol[ch_i] = sym;
characterToSymbol[ch_sym] = i;
//i would change these vars...
//but it looks so darn cool...
}
symbolFrequency[i]++;
while (--i >= 0) {
symbolCumulative[i]++;
}
//end transclusion
return character;
}
function DecodePosition() {
var range = high - low;
var position = BinarySearchPos((((value - low + 1) * positionCumulative[0] - 1) / range)|0);
high = low + ((range * positionCumulative[position ]) / positionCumulative[0])|0;
low += ((range * positionCumulative[position + 1]) / positionCumulative[0])|0;
for ( ; ; ) {
if (low >= 65536) {
value -= 65536;
low -= 65536;
high -= 65536;
} else if (low >= 32768 && high <= 98304) {
value -= 32768;
low -= 32768;
high -= 32768;
} else if (high > 65536) { break };
low += low;
high += high;
value = 2 * value + GetBit();
}
return position;
}
/**
* Decompresses LZARI-formatted data.
* @param {Uint8Array} input - source data
* @returns {Uint8Array} output - uncompressed data
* @access public
*/
function decodeLzari(input) {
//transcluded reset function.
inputData = null;
inputLocation = 0;
low = 0;
high = 131072;
value = 0;
text_buffer = new Array(4155);
characterToSymbol = new Array(314);
symbolToCharacter = new Array(315);
symbolFrequency = new Array(315);
symbolCumulative = new Array(315);
positionCumulative = new Array(4097);
bit_Buffer = 0;
bit_Mask = 0;
//end transclusion
inputData = input;
inputLocation = 4;
let dataSize = new DataView(input.buffer).getInt32(0,1);
if (dataSize == 0) return(0);
if (dataSize < 0) return(-1);
let outputLength = dataSize;
let outputData = new Uint8Array(dataSize);
let outputLocation = 0;
//transcluded StartDecode
for (let i = 0; i < 17; i++) {
value = 2 * value + GetBit();
}
//transcluded StartModel
symbolCumulative[314] = 0;
for (let sym = 314; sym >= 1; sym--) {
let ch = sym - 1;
characterToSymbol[ch] = sym;
symbolToCharacter[sym] = ch;
symbolFrequency[sym] = 1;
symbolCumulative[sym - 1] = (symbolCumulative[sym] + symbolFrequency[sym]);
}
symbolFrequency[0] = 0;
positionCumulative[4096] = 0;
for (let i = 4096; i >= 1; i--) {
positionCumulative[i - 1] = (positionCumulative[i] + (10000 / (i + 200))|0);
}
//end transclusion
//normal Decode process
for (let i = 0; i < 4036; i++) {
text_buffer[i] = 32;
}
var r = 4036;
for (let count = 0; count < dataSize; ) {
if(inputLocation >= inputData.length) {break};
let c = DecodeChar();
if (c < 256) {
outputData[outputLocation++] = c;
text_buffer[r++] = c;
r &= (4095);
count++;
} else {
let i = (r - DecodePosition() - 1) & 4095;
let j = c - 253;
for (let k = 0; k < j; k++) {
c = text_buffer[(i + k) & 4095];
outputData[outputLocation++] = c;
text_buffer[r++] = c;
r &= (4095);
count++;
}
}
}
//console.debug("LZARI I/O", {inputData, outputData});
return(outputData);
}
/**
* Define (module.)exports with all public functions.
* @exports icondumper2/lzari
*/ // start c6js
if(typeof exports !== "object") {
exports = {
decodeLzari
};
} else {
exports.decodeLzari = decodeLzari;
}
if(typeof module !== "undefined") {
module.exports = exports;
}
//end c6js
//start esm
/*export {
decodeLzari
};*/
//end esm