Back to Powder Tool V600Billion
SOURCE / PINNED RELEASE

Made of little things.

Powder Tool V600Billion

Release
142767edcab8…
Author-recorded commit
6d92971effd0…
License
LICENSE
Author’s source reference
nostr://npub1fllw8kw0thjj55wds0uugcnp5kej2nfxd36eruq39d56wwz8r44q5q78wj/wss%3A%2F%2Fgit.napplet.soy%2F/powder-toy

Archive hash verified: ed7d6a8ea7083197…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

source/src/save/bson.js
/**
 * A strict BSON reader for Powder Toy saves: every length is checked against
 * the buffer before it is used, and nesting is bounded. Only the types the
 * game writes are accepted.
 */
export class BsonError extends Error {
  constructor(message) {
    super(message);
    this.name = 'BsonError';
  }
}

const decoder = new TextDecoder();

export function parseBson(bytes, { maxDepth = 16 } = {}) {
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
  const need = (at, count) => {
    if (at < 0 || count < 0 || at + count > bytes.length) throw new BsonError('Truncated document');
  };
  const cstring = (at) => {
    let end = at;
    while (end < bytes.length && bytes[end] !== 0) end++;
    if (end >= bytes.length) throw new BsonError('Unterminated name');
    return [decoder.decode(bytes.subarray(at, end)), end + 1];
  };
  function document(at, depth, asArray) {
    if (depth > maxDepth) throw new BsonError('Document nested too deep');
    need(at, 5);
    const size = view.getInt32(at, true);
    if (size < 5) throw new BsonError('Bad document size');
    need(at, size);
    const end = at + size - 1;
    if (bytes[end] !== 0) throw new BsonError('Document not terminated');
    const result = asArray ? [] : {};
    let cursor = at + 4;
    while (cursor < end) {
      const type = bytes[cursor++];
      let name;
      [name, cursor] = cstring(cursor);
      let value;
      switch (type) {
        case 0x01: need(cursor, 8); value = view.getFloat64(cursor, true); cursor += 8; break;
        case 0x02: {
          need(cursor, 4);
          const length = view.getInt32(cursor, true);
          if (length < 1) throw new BsonError('Bad string length');
          need(cursor + 4, length);
          value = decoder.decode(bytes.subarray(cursor + 4, cursor + 4 + length - 1));
          cursor += 4 + length;
          break;
        }
        case 0x03: case 0x04: {
          need(cursor, 4);
          const size = view.getInt32(cursor, true);
          value = document(cursor, depth + 1, type === 0x04);
          cursor += size;
          break;
        }
        case 0x05: {
          need(cursor, 5);
          const length = view.getInt32(cursor, true);
          if (length < 0) throw new BsonError('Bad binary length');
          need(cursor + 5, length);
          value = bytes.subarray(cursor + 5, cursor + 5 + length);
          cursor += 5 + length;
          break;
        }
        case 0x08: need(cursor, 1); value = bytes[cursor] !== 0; cursor += 1; break;
        case 0x0a: value = null; break;
        case 0x10: need(cursor, 4); value = view.getInt32(cursor, true); cursor += 4; break;
        case 0x09: case 0x11: case 0x12: {
          need(cursor, 8);
          const big = view.getBigInt64(cursor, true);
          value = big >= BigInt(Number.MIN_SAFE_INTEGER) && big <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(big) : big;
          cursor += 8;
          break;
        }
        default: throw new BsonError(`Unsupported BSON type 0x${type.toString(16)}`);
      }
      if (asArray) result.push(value);
      else result[name] = value;
    }
    if (cursor !== end) throw new BsonError('Document size mismatch');
    return result;
  }
  return document(0, 0, false);
}