Files
OpenFrontIO/src/core/PatternDecoder.ts
T
Scott Anderson 4680b04656 Refactor cosmetics.json (#1263)
## Description:

- Refactor cosmetics.json to use base64 as the lookup key, to reduce the
complexity of lookup.
- Add refine() logic to PatternSchema to check if the pattern is valid.
- Split PatternDecoder class out of Cosmetic.ts to resolve temporal
deadzone caused by static parsing of cosmetics.json with the new
refine().

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors
2025-06-23 20:14:36 -07:00

62 lines
1.6 KiB
TypeScript

import { base64url } from "jose";
export class PatternDecoder {
private bytes: Uint8Array;
private tileWidth: number;
private tileHeight: number;
private scale: number;
constructor(base64: string) {
this.bytes = base64url.decode(base64);
if (this.bytes.length < 3) {
throw new Error(
"Pattern data is too short to contain required metadata.",
);
}
const version = this.bytes[0];
if (version !== 0) {
throw new Error(`Unrecognized pattern version ${version}.`);
}
const byte1 = this.bytes[1];
const byte2 = this.bytes[2];
this.scale = byte1 & 0x07;
this.tileWidth = (((byte2 & 0x03) << 5) | ((byte1 >> 3) & 0x1f)) + 2;
this.tileHeight = ((byte2 >> 2) & 0x3f) + 2;
const expectedBits = this.tileWidth * this.tileHeight;
const expectedBytes = (expectedBits + 7) >> 3; // Equivalent to: ceil(expectedBits / 8);
if (this.bytes.length - 3 < expectedBytes) {
throw new Error(
"Pattern data is too short for the specified dimensions.",
);
}
}
getTileWidth(): number {
return this.tileWidth;
}
getTileHeight(): number {
return this.tileHeight;
}
getScale(): number {
return this.scale;
}
isSet(x: number, y: number): boolean {
const px = (x >> this.scale) % this.tileWidth;
const py = (y >> this.scale) % this.tileHeight;
const idx = py * this.tileWidth + px;
const byteIndex = idx >> 3;
const bitIndex = idx & 7;
const byte = this.bytes[3 + byteIndex];
if (byte === undefined) throw new Error("Invalid pattern");
return (byte & (1 << bitIndex)) !== 0;
}
}