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.
@@ -3178,7 +3178,7 @@ declare class AnvilParser {
3178
3178
  x: number;
3179
3179
  z: number;
3180
3180
  }[];
3181
- /** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
3181
+ /** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
3182
3182
  static parse(data: RegionInput): Promise<AnvilRegion>;
3183
3183
  /**
3184
3184
  * Decodes one chunk for {@link MineRenderWorld.placeChunk}.
@@ -3178,7 +3178,7 @@ declare class AnvilParser {
3178
3178
  x: number;
3179
3179
  z: number;
3180
3180
  }[];
3181
- /** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
3181
+ /** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
3182
3182
  static parse(data: RegionInput): Promise<AnvilRegion>;
3183
3183
  /**
3184
3184
  * Decodes one chunk for {@link MineRenderWorld.placeChunk}.
@@ -29113,13 +29113,131 @@ var ChunkData = class _ChunkData {
29113
29113
  }
29114
29114
  };
29115
29115
 
29116
+ // src/world/_compression/Lz4.ts
29117
+ init_MineRenderError();
29118
+ function decodeAnvilLz4(data) {
29119
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
29120
+ const blocks = [];
29121
+ let cursor = 0, totalSize = 0;
29122
+ while (true) {
29123
+ if (cursor + 21 > data.length) throw invalid("truncated block header or missing terminator");
29124
+ if (view.getUint32(cursor) !== 1280980034 || view.getUint32(cursor + 4) !== 1819239275) {
29125
+ throw invalid("invalid block signature");
29126
+ }
29127
+ const token = data[cursor + 8];
29128
+ const method = token & 240;
29129
+ const size = view.getInt32(cursor + 9, true);
29130
+ const decodedSize = view.getInt32(cursor + 13, true);
29131
+ const checksum = view.getUint32(cursor + 17, true);
29132
+ cursor += 21;
29133
+ if (method !== 16 && method !== 32) throw invalid("unsupported block method");
29134
+ if (size < 0 || decodedSize < 0 || decodedSize > 2 ** (10 + (token & 15)) || size === 0 !== (decodedSize === 0) || method === 16 && size !== decodedSize) {
29135
+ throw invalid("invalid block lengths");
29136
+ }
29137
+ if (size === 0) {
29138
+ if (checksum !== 0 || cursor !== data.length) throw invalid("invalid terminator or trailing bytes");
29139
+ break;
29140
+ }
29141
+ if (cursor + size > data.length || decodedSize > size * 255) throw invalid("invalid block lengths");
29142
+ blocks.push({ start: cursor, size, decodedSize, raw: method === 16, checksum });
29143
+ totalSize += decodedSize;
29144
+ cursor += size;
29145
+ }
29146
+ const output = new Uint8Array(totalSize);
29147
+ let offset = 0;
29148
+ for (const block of blocks) {
29149
+ const source = data.subarray(block.start, block.start + block.size);
29150
+ const target = output.subarray(offset, offset + block.decodedSize);
29151
+ if (block.raw) target.set(source);
29152
+ else decodeBlock(source, target);
29153
+ if (checksum32(target) !== block.checksum) throw invalid("block checksum mismatch");
29154
+ offset += block.decodedSize;
29155
+ }
29156
+ return output;
29157
+ }
29158
+ function decodeBlock(source, target) {
29159
+ let input = 0, output = 0, matched = false;
29160
+ const length = (nibble) => {
29161
+ let value = nibble;
29162
+ if (value === 15) {
29163
+ let extension;
29164
+ do {
29165
+ if (input === source.length) throw invalid("truncated sequence length");
29166
+ extension = source[input++];
29167
+ value += extension;
29168
+ } while (extension === 255);
29169
+ }
29170
+ return value;
29171
+ };
29172
+ while (input < source.length) {
29173
+ const token = source[input++];
29174
+ const literals = length(token >>> 4);
29175
+ if (input + literals > source.length || output + literals > target.length) {
29176
+ throw invalid("literal sequence exceeds block bounds");
29177
+ }
29178
+ target.set(source.subarray(input, input + literals), output);
29179
+ input += literals;
29180
+ output += literals;
29181
+ if (input === source.length) {
29182
+ if (output !== target.length || matched && literals < 5) throw invalid("invalid final literal sequence");
29183
+ return;
29184
+ }
29185
+ if (input + 2 > source.length) throw invalid("truncated match offset");
29186
+ const distance = source[input] | source[input + 1] << 8;
29187
+ input += 2;
29188
+ if (distance === 0 || distance > output) throw invalid("match offset exceeds block history");
29189
+ const count = length(token & 15) + 4;
29190
+ if (output > target.length - 12 || output + count > target.length - 5) {
29191
+ throw invalid("match exceeds block bounds");
29192
+ }
29193
+ for (let end = output + count; output < end; output++) target[output] = target[output - distance];
29194
+ matched = true;
29195
+ }
29196
+ throw invalid("missing final literal sequence");
29197
+ }
29198
+ function checksum32(data) {
29199
+ const prime1 = 2654435761, prime2 = 2246822519, prime3 = 3266489917, prime4 = 668265263, prime5 = 374761393;
29200
+ const seed = 2538058380;
29201
+ const rotate = (value, bits) => value << bits | value >>> 32 - bits;
29202
+ const round = (value, word) => Math.imul(rotate(value + Math.imul(word, prime2), 13), prime1);
29203
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
29204
+ let cursor = 0, hash;
29205
+ if (data.length >= 16) {
29206
+ let a = seed + prime1 + prime2, b = seed + prime2, c = seed, d = seed - prime1;
29207
+ while (cursor + 16 <= data.length) {
29208
+ a = round(a, view.getUint32(cursor, true));
29209
+ b = round(b, view.getUint32(cursor + 4, true));
29210
+ c = round(c, view.getUint32(cursor + 8, true));
29211
+ d = round(d, view.getUint32(cursor + 12, true));
29212
+ cursor += 16;
29213
+ }
29214
+ hash = rotate(a, 1) + rotate(b, 7) + rotate(c, 12) + rotate(d, 18);
29215
+ } else {
29216
+ hash = seed + prime5;
29217
+ }
29218
+ hash += data.length;
29219
+ while (cursor + 4 <= data.length) {
29220
+ hash = Math.imul(rotate(hash + Math.imul(view.getUint32(cursor, true), prime3), 17), prime4);
29221
+ cursor += 4;
29222
+ }
29223
+ while (cursor < data.length) {
29224
+ hash = Math.imul(rotate(hash + Math.imul(data[cursor++], prime5), 11), prime1);
29225
+ }
29226
+ hash = Math.imul(hash ^ hash >>> 15, prime2);
29227
+ hash = Math.imul(hash ^ hash >>> 13, prime3);
29228
+ return (hash ^ hash >>> 16) & 268435455;
29229
+ }
29230
+ function invalid(detail) {
29231
+ return new MineRenderError(`Invalid Anvil LZ4 stream: ${detail}`);
29232
+ }
29233
+
29116
29234
  // src/world/AnvilParser.ts
29117
29235
  var AnvilParser = class {
29118
29236
  /** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
29119
29237
  static getChunkList(data) {
29120
29238
  return this.locations(this.bytes(data)).map(({ x, z }) => ({ x, z }));
29121
29239
  }
29122
- /** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
29240
+ /** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
29123
29241
  static async parse(data) {
29124
29242
  const bytes = this.bytes(data);
29125
29243
  const chunks = [];
@@ -29167,11 +29285,13 @@ var AnvilParser = class {
29167
29285
  static async readChunk(data, location) {
29168
29286
  const compression = data[location.offset + 4];
29169
29287
  if (compression & 128) throw new MineRenderError("External Anvil .mcc chunks are not supported");
29170
- if (compression !== 1 && compression !== 2 && compression !== 3) {
29171
- throw new MineRenderError(`Unsupported Anvil compression ${compression}${compression === 4 ? " (LZ4)" : ""}`);
29288
+ if (compression !== 1 && compression !== 2 && compression !== 3 && compression !== 4) {
29289
+ throw new MineRenderError(`Unsupported Anvil compression ${compression}`);
29172
29290
  }
29173
29291
  let payload = data.subarray(location.offset + 5, location.offset + 4 + location.length);
29174
- if (compression !== 3) {
29292
+ if (compression === 4) {
29293
+ payload = decodeAnvilLz4(payload);
29294
+ } else if (compression !== 3) {
29175
29295
  const stream = new Blob([payload]).stream().pipeThrough(new DecompressionStream(compression === 1 ? "gzip" : "deflate"));
29176
29296
  payload = new Uint8Array(await new Response(stream).arrayBuffer());
29177
29297
  }