minerender 2.1.0-alpha.90 → 2.1.0-alpha.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3177,7 +3177,7 @@ declare class AnvilParser {
3177
3177
  x: number;
3178
3178
  z: number;
3179
3179
  }[];
3180
- /** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
3180
+ /** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
3181
3181
  static parse(data: RegionInput): Promise<AnvilRegion>;
3182
3182
  /**
3183
3183
  * Decodes one chunk for {@link MineRenderWorld.placeChunk}.
@@ -3177,7 +3177,7 @@ declare class AnvilParser {
3177
3177
  x: number;
3178
3178
  z: number;
3179
3179
  }[];
3180
- /** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
3180
+ /** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
3181
3181
  static parse(data: RegionInput): Promise<AnvilRegion>;
3182
3182
  /**
3183
3183
  * Decodes one chunk for {@link MineRenderWorld.placeChunk}.
@@ -29066,13 +29066,131 @@ var ChunkData = class _ChunkData {
29066
29066
  }
29067
29067
  };
29068
29068
 
29069
+ // src/world/_compression/Lz4.ts
29070
+ init_MineRenderError();
29071
+ function decodeAnvilLz4(data) {
29072
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
29073
+ const blocks = [];
29074
+ let cursor = 0, totalSize = 0;
29075
+ while (true) {
29076
+ if (cursor + 21 > data.length) throw invalid("truncated block header or missing terminator");
29077
+ if (view.getUint32(cursor) !== 1280980034 || view.getUint32(cursor + 4) !== 1819239275) {
29078
+ throw invalid("invalid block signature");
29079
+ }
29080
+ const token = data[cursor + 8];
29081
+ const method = token & 240;
29082
+ const size = view.getInt32(cursor + 9, true);
29083
+ const decodedSize = view.getInt32(cursor + 13, true);
29084
+ const checksum = view.getUint32(cursor + 17, true);
29085
+ cursor += 21;
29086
+ if (method !== 16 && method !== 32) throw invalid("unsupported block method");
29087
+ if (size < 0 || decodedSize < 0 || decodedSize > 2 ** (10 + (token & 15)) || size === 0 !== (decodedSize === 0) || method === 16 && size !== decodedSize) {
29088
+ throw invalid("invalid block lengths");
29089
+ }
29090
+ if (size === 0) {
29091
+ if (checksum !== 0 || cursor !== data.length) throw invalid("invalid terminator or trailing bytes");
29092
+ break;
29093
+ }
29094
+ if (cursor + size > data.length || decodedSize > size * 255) throw invalid("invalid block lengths");
29095
+ blocks.push({ start: cursor, size, decodedSize, raw: method === 16, checksum });
29096
+ totalSize += decodedSize;
29097
+ cursor += size;
29098
+ }
29099
+ const output = new Uint8Array(totalSize);
29100
+ let offset = 0;
29101
+ for (const block of blocks) {
29102
+ const source = data.subarray(block.start, block.start + block.size);
29103
+ const target = output.subarray(offset, offset + block.decodedSize);
29104
+ if (block.raw) target.set(source);
29105
+ else decodeBlock(source, target);
29106
+ if (checksum32(target) !== block.checksum) throw invalid("block checksum mismatch");
29107
+ offset += block.decodedSize;
29108
+ }
29109
+ return output;
29110
+ }
29111
+ function decodeBlock(source, target) {
29112
+ let input = 0, output = 0, matched = false;
29113
+ const length = (nibble) => {
29114
+ let value = nibble;
29115
+ if (value === 15) {
29116
+ let extension;
29117
+ do {
29118
+ if (input === source.length) throw invalid("truncated sequence length");
29119
+ extension = source[input++];
29120
+ value += extension;
29121
+ } while (extension === 255);
29122
+ }
29123
+ return value;
29124
+ };
29125
+ while (input < source.length) {
29126
+ const token = source[input++];
29127
+ const literals = length(token >>> 4);
29128
+ if (input + literals > source.length || output + literals > target.length) {
29129
+ throw invalid("literal sequence exceeds block bounds");
29130
+ }
29131
+ target.set(source.subarray(input, input + literals), output);
29132
+ input += literals;
29133
+ output += literals;
29134
+ if (input === source.length) {
29135
+ if (output !== target.length || matched && literals < 5) throw invalid("invalid final literal sequence");
29136
+ return;
29137
+ }
29138
+ if (input + 2 > source.length) throw invalid("truncated match offset");
29139
+ const distance = source[input] | source[input + 1] << 8;
29140
+ input += 2;
29141
+ if (distance === 0 || distance > output) throw invalid("match offset exceeds block history");
29142
+ const count = length(token & 15) + 4;
29143
+ if (output > target.length - 12 || output + count > target.length - 5) {
29144
+ throw invalid("match exceeds block bounds");
29145
+ }
29146
+ for (let end = output + count; output < end; output++) target[output] = target[output - distance];
29147
+ matched = true;
29148
+ }
29149
+ throw invalid("missing final literal sequence");
29150
+ }
29151
+ function checksum32(data) {
29152
+ const prime1 = 2654435761, prime2 = 2246822519, prime3 = 3266489917, prime4 = 668265263, prime5 = 374761393;
29153
+ const seed = 2538058380;
29154
+ const rotate = (value, bits) => value << bits | value >>> 32 - bits;
29155
+ const round = (value, word) => Math.imul(rotate(value + Math.imul(word, prime2), 13), prime1);
29156
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
29157
+ let cursor = 0, hash;
29158
+ if (data.length >= 16) {
29159
+ let a = seed + prime1 + prime2, b = seed + prime2, c = seed, d = seed - prime1;
29160
+ while (cursor + 16 <= data.length) {
29161
+ a = round(a, view.getUint32(cursor, true));
29162
+ b = round(b, view.getUint32(cursor + 4, true));
29163
+ c = round(c, view.getUint32(cursor + 8, true));
29164
+ d = round(d, view.getUint32(cursor + 12, true));
29165
+ cursor += 16;
29166
+ }
29167
+ hash = rotate(a, 1) + rotate(b, 7) + rotate(c, 12) + rotate(d, 18);
29168
+ } else {
29169
+ hash = seed + prime5;
29170
+ }
29171
+ hash += data.length;
29172
+ while (cursor + 4 <= data.length) {
29173
+ hash = Math.imul(rotate(hash + Math.imul(view.getUint32(cursor, true), prime3), 17), prime4);
29174
+ cursor += 4;
29175
+ }
29176
+ while (cursor < data.length) {
29177
+ hash = Math.imul(rotate(hash + Math.imul(data[cursor++], prime5), 11), prime1);
29178
+ }
29179
+ hash = Math.imul(hash ^ hash >>> 15, prime2);
29180
+ hash = Math.imul(hash ^ hash >>> 13, prime3);
29181
+ return (hash ^ hash >>> 16) & 268435455;
29182
+ }
29183
+ function invalid(detail) {
29184
+ return new MineRenderError(`Invalid Anvil LZ4 stream: ${detail}`);
29185
+ }
29186
+
29069
29187
  // src/world/AnvilParser.ts
29070
29188
  var AnvilParser = class {
29071
29189
  /** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
29072
29190
  static getChunkList(data) {
29073
29191
  return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
29074
29192
  }
29075
- /** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
29193
+ /** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
29076
29194
  static async parse(data) {
29077
29195
  const bytes = this.bytes(data);
29078
29196
  const chunks = [];
@@ -29120,11 +29238,13 @@ var AnvilParser = class {
29120
29238
  static async readChunk(data, location) {
29121
29239
  const compression = data[location.offset + 4];
29122
29240
  if (compression & 128) throw new MineRenderError("External Anvil .mcc chunks are not supported");
29123
- if (compression !== 1 && compression !== 2 && compression !== 3) {
29124
- throw new MineRenderError(`Unsupported Anvil compression ${compression}${compression === 4 ? " (LZ4)" : ""}`);
29241
+ if (compression !== 1 && compression !== 2 && compression !== 3 && compression !== 4) {
29242
+ throw new MineRenderError(`Unsupported Anvil compression ${compression}`);
29125
29243
  }
29126
29244
  let payload = data.subarray(location.offset + 5, location.offset + 4 + location.length);
29127
- if (compression !== 3) {
29245
+ if (compression === 4) {
29246
+ payload = decodeAnvilLz4(payload);
29247
+ } else if (compression !== 3) {
29128
29248
  const stream = new Blob([payload]).stream().pipeThrough(new DecompressionStream(compression === 1 ? "gzip" : "deflate"));
29129
29249
  payload = new Uint8Array(await new Response(stream).arrayBuffer());
29130
29250
  }