Files
mangavalkandClaude Opus 5.5 35240e5dfc Add Lian Li SL-INF wireless tools and SignalRGB plugin
Proof-of-concept tools for the UNI FAN SL-INF Wireless kit (SLV3 TX/RX
dongles 0416:8040/8041): a Python scan/colour script, tinyuz helpers, a
JavaScript tinyuz encoder, the SignalRGB plugin and tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 00:12:23 +01:00

116 lines
2.4 KiB
JavaScript

// Minimal tinyuz encoder (greedy LZ77), stream format of github.com/sisong/tinyuz.
// Output decodes with the reference tuz_decompress_mem.
const kMinDictMatchLen = 2;
const kBigPosForLen = (1 << 11) + (1 << 9) + (1 << 7) - 1;
const kMaxDist = 4096;
const kCtrlStreamEnd = 3;
function tuzCompress(data) {
const code = [0, 0, 0, 0]; // dict size, patched at the end
let typesIndex = 0;
let typeCount = 0;
let haveDataBack = false;
let posBack = 1;
let dictSizeMax = 1;
const outType = (bit) => {
if (typeCount === 0) {
typesIndex = code.length;
code.push(0);
}
code[typesIndex] |= bit << typeCount;
typeCount = (typeCount + 1) & 7;
};
const outLen = (v, packBit) => {
let count = 1;
while (v >= 2 ** (count * packBit)) {
v -= 2 ** (count * packBit);
count++;
}
while (count--) {
for (let i = 0; i < packBit; i++) {
outType(Math.floor(v / 2 ** (count * packBit + i)) & 1);
}
outType(count > 0 ? 1 : 0);
}
};
const outDictPos = (pos) => {
const big = pos >= 128 ? 1 : 0;
if (big) {
pos -= 128;
}
code.push((pos & 0x7f) | (big << 7));
if (big) {
outLen(pos >> 7, 2);
}
};
const outLiteral = (b) => {
outType(1);
code.push(b);
haveDataBack = true;
};
const outDict = (len, dist) => {
outType(0);
const same = haveDataBack && dist === posBack;
let saved = len - kMinDictMatchLen;
if (!same && dist > kBigPosForLen) {
saved--;
}
outLen(saved, 1);
if (haveDataBack) {
outType(same ? 1 : 0);
}
if (!same) {
outDictPos(dist);
}
dictSizeMax = Math.max(dictSizeMax, dist);
haveDataBack = false;
posBack = dist;
};
let i = 0;
while (i < data.length) {
let bestLen = 0;
let bestDist = 0;
for (let dist = 1; dist <= Math.min(i, kMaxDist); dist++) {
let len = 0;
while (i + len < data.length && data[i + len] === data[i + len - dist]) {
len++;
}
if (len > bestLen) {
bestLen = len;
bestDist = dist;
}
}
const minLen = bestDist > kBigPosForLen ? 3 : kMinDictMatchLen;
if (bestLen >= minLen && (bestLen > 2 || bestDist < 128)) {
outDict(bestLen, bestDist);
i += bestLen;
} else {
outLiteral(data[i]);
i++;
}
}
// stream end control code
outType(0);
outLen(kCtrlStreamEnd, 1);
if (haveDataBack) {
outType(0);
}
outDictPos(0);
for (let k = 0; k < 4; k++) {
code[k] = (dictSizeMax >> (8 * k)) & 0xff;
}
return code;
}
if (typeof module !== "undefined") {
module.exports = { tuzCompress };
}