{"version":3,"file":"puz-DFRqy_p0.mjs","names":[],"sources":["../src/binary-reader.ts","../src/puz.ts"],"sourcesContent":["/**\n * Generic binary reader for parsing binary file formats\n */\n\nimport { BinaryParseError } from \"./errors\";\n\n/**\n * A utility class for reading binary data from buffers with automatic offset tracking.\n */\nexport class BinaryReader {\n  private _buffer: Buffer;\n  private _offset: number;\n\n  constructor(data: Buffer | ArrayBuffer | Uint8Array) {\n    if (data instanceof ArrayBuffer) {\n      this._buffer = Buffer.from(data);\n    } else if (data instanceof Uint8Array) {\n      this._buffer = Buffer.from(data);\n    } else {\n      this._buffer = data;\n    }\n    this._offset = 0;\n  }\n\n  /**\n   * Read an unsigned 8-bit integer and advance the offset\n   */\n  readUInt8(): number {\n    if (this._offset + 1 > this._buffer.length) {\n      throw new BinaryParseError(\n        `Cannot read byte at offset ${this._offset}: buffer too short (length: ${this._buffer.length})`,\n      );\n    }\n    const value = this._buffer.readUInt8(this._offset);\n    this._offset += 1;\n    return value;\n  }\n\n  /**\n   * Read an unsigned 16-bit integer (little-endian) and advance the offset\n   */\n  readUInt16LE(): number {\n    if (this._offset + 2 > this._buffer.length) {\n      throw new BinaryParseError(\n        `Cannot read 16-bit value at offset ${this._offset}: buffer too short (length: ${this._buffer.length})`,\n      );\n    }\n    const value = this._buffer.readUInt16LE(this._offset);\n    this._offset += 2;\n    return value;\n  }\n\n  /**\n   * Read a specified number of bytes and advance the offset\n   */\n  readBytes(length: number): Buffer {\n    if (this._offset + length > this._buffer.length) {\n      throw new BinaryParseError(\n        `Cannot read ${length} bytes at offset ${this._offset}: buffer too short (length: ${this._buffer.length})`,\n      );\n    }\n    const value = this._buffer.slice(this._offset, this._offset + length);\n    this._offset += length;\n    return value;\n  }\n\n  /**\n   * Read a string of specified length with optional null trimming\n   * @param length Number of bytes to read\n   * @param trimNull Whether to trim at first null byte (default: true)\n   * @param encoding Character encoding (default: 'latin1')\n   */\n  readString(\n    length: number,\n    trimNull: boolean = true,\n    encoding: BufferEncoding = \"latin1\",\n  ): string {\n    const bytes = this.readBytes(length);\n    if (trimNull) {\n      // Remove trailing nulls\n      let end = bytes.indexOf(0);\n      if (end === -1) end = length;\n      return bytes.toString(encoding, 0, end);\n    }\n    return bytes.toString(encoding);\n  }\n\n  /**\n   * Read a null-terminated string and advance past the null terminator\n   * @param encoding Character encoding (default: 'latin1')\n   */\n  readNullTerminatedString(encoding: BufferEncoding = \"latin1\"): string {\n    const start = this._offset;\n    while (\n      this._offset < this._buffer.length &&\n      this._buffer[this._offset] !== 0\n    ) {\n      this._offset++;\n    }\n    if (this._offset >= this._buffer.length) {\n      throw new BinaryParseError(\n        `Cannot read null-terminated string at offset ${start}: buffer ended without null terminator`,\n      );\n    }\n    const str = this._buffer.toString(encoding, start, this._offset);\n    this._offset++; // Skip null terminator\n    return str;\n  }\n\n  /**\n   * Move the read position to a specific offset\n   */\n  seek(position: number): void {\n    if (position < 0 || position > this._buffer.length) {\n      throw new BinaryParseError(\n        `Cannot seek to position ${position}: out of bounds (buffer length: ${this._buffer.length})`,\n      );\n    }\n    this._offset = position;\n  }\n\n  /**\n   * Get the current read position\n   */\n  get position(): number {\n    return this._offset;\n  }\n\n  /**\n   * Get the total length of the buffer\n   */\n  get length(): number {\n    return this._buffer.length;\n  }\n\n  /**\n   * Get the underlying buffer\n   */\n  get buffer(): Buffer {\n    return this._buffer;\n  }\n\n  /**\n   * Check if there's more data to read\n   */\n  hasMore(): boolean {\n    return this._offset < this._buffer.length;\n  }\n\n  /**\n   * Get the number of bytes remaining to read\n   */\n  get remaining(): number {\n    return this._buffer.length - this._offset;\n  }\n}\n","/**\n * Parser for PUZ crossword format (Across Lite)\n * Based on reverse-engineered specification\n */\n\nimport { InvalidFileError, PuzParseError, BinaryParseError } from \"./errors\";\nimport { ErrorCode } from \"./types\";\nimport type {\n  Puzzle,\n  Grid,\n  Cell as UnifiedCell,\n  Clues,\n  ParseOptions,\n} from \"./types\";\nimport { BinaryReader } from \"./binary-reader\";\nimport {\n  MAX_GRID_WIDTH,\n  MAX_GRID_HEIGHT,\n  PUZ_MAGIC_STRING,\n  PUZ_HEADER_SIZE,\n  PUZ_SECTION_GRBS,\n  PUZ_SECTION_RTBL,\n  PUZ_SECTION_GEXT,\n  PUZ_SECTION_LTIM,\n  PUZ_CIRCLED_CELL_FLAG,\n} from \"./constants\";\n\nexport interface PuzMetadata {\n  title?: string;\n  author?: string;\n  copyright?: string;\n  notes?: string;\n}\n\nexport interface PuzClue {\n  number: number;\n  text: string;\n}\n\nexport interface PuzCell {\n  solution?: string;\n  playerState?: string;\n  isBlack: boolean;\n  isCircled?: boolean;\n  hasRebus?: boolean;\n  rebusKey?: number;\n}\n\nexport interface PuzPuzzle {\n  width: number;\n  height: number;\n  metadata: PuzMetadata;\n  grid: PuzCell[][];\n  across: PuzClue[];\n  down: PuzClue[];\n  rebusTable?: Map<number, string>;\n  isScrambled?: boolean;\n  timer?: {\n    elapsed: number;\n    running: boolean;\n  };\n}\n\n// PUZ file header structure\ninterface PuzHeader {\n  checksum: number;\n  magic: string;\n  cibChecksum: number;\n  maskedLowChecksum: number;\n  maskedHighChecksum: number;\n  version: string;\n  reserved1: number;\n  scrambledChecksum: number;\n  reserved2: Buffer;\n  width: number;\n  height: number;\n  numClues: number;\n  puzzleType: number;\n  scrambledTag: number;\n}\n\nfunction readHeader(reader: BinaryReader): PuzHeader {\n  // Search for the magic string \"ACROSS&DOWN\" in the file\n  const magicBytes = Buffer.from(PUZ_MAGIC_STRING, \"latin1\");\n  let magicOffset = -1;\n\n  // Search for the magic string in the buffer\n  for (let i = 0; i <= reader.length - magicBytes.length; i++) {\n    let found = true;\n    for (let j = 0; j < magicBytes.length; j++) {\n      if (reader.buffer[i + j] !== magicBytes[j]) {\n        found = false;\n        break;\n      }\n    }\n    if (found) {\n      magicOffset = i;\n      break;\n    }\n  }\n\n  if (magicOffset === -1) {\n    throw new PuzParseError(\n      `Invalid PUZ file: magic string \"${PUZ_MAGIC_STRING}\" not found`,\n      ErrorCode.PUZ_INVALID_HEADER,\n    );\n  }\n\n  // Position reader at the start of the actual PUZ data (2 bytes before magic string)\n  const headerStart = magicOffset - 2;\n  if (headerStart < 0) {\n    throw new PuzParseError(\n      \"Invalid PUZ file: magic string found too early in file\",\n      ErrorCode.PUZ_INVALID_HEADER,\n    );\n  }\n\n  // Ensure there's enough data for the full header\n  if (headerStart + PUZ_HEADER_SIZE > reader.length) {\n    throw new PuzParseError(\n      \"Invalid PUZ file: insufficient data for header\",\n      ErrorCode.PUZ_INVALID_HEADER,\n    );\n  }\n\n  reader.seek(headerStart);\n\n  // Read header fields according to PUZ specification\n  const checksum = reader.readUInt16LE(); // 0x00-0x01\n  const magic = reader.readString(12, true, \"latin1\"); // 0x02-0x0D\n  const cibChecksum = reader.readUInt16LE(); // 0x0E-0x0F\n  const maskedLowChecksum = reader.readUInt16LE(); // 0x10-0x11\n  const maskedHighChecksum = reader.readUInt16LE(); // 0x12-0x13\n  const version = reader.readString(4, true, \"latin1\"); // 0x14-0x17\n  const reserved1 = reader.readUInt16LE(); // 0x18-0x19\n  const scrambledChecksum = reader.readUInt16LE(); // 0x1A-0x1B\n  const reserved2 = reader.readBytes(12); // 0x1C-0x27\n  reader.readBytes(4); // 0x28-0x2B (skip unknown bytes)\n  const width = reader.readUInt8(); // 0x2C\n  const height = reader.readUInt8(); // 0x2D\n  const numClues = reader.readUInt16LE(); // 0x2E-0x2F\n  const puzzleType = reader.readUInt16LE(); // 0x30-0x31\n  const scrambledTag = reader.readUInt16LE(); // 0x32-0x33\n\n  const header: PuzHeader = {\n    checksum,\n    magic,\n    cibChecksum,\n    maskedLowChecksum,\n    maskedHighChecksum,\n    version,\n    reserved1,\n    scrambledChecksum,\n    reserved2,\n    width,\n    height,\n    numClues,\n    puzzleType,\n    scrambledTag,\n  };\n\n  // Verify we read the magic string correctly\n  if (header.magic !== PUZ_MAGIC_STRING) {\n    throw new InvalidFileError(\n      \"PUZ\",\n      `magic string mismatch after positioning. Expected \"${PUZ_MAGIC_STRING}\", got \"${header.magic}\"`,\n    );\n  }\n\n  return header;\n}\n\nfunction parseGrid(\n  solution: string,\n  playerState: string,\n  width: number,\n  height: number,\n): PuzCell[][] {\n  const grid: PuzCell[][] = [];\n\n  for (let row = 0; row < height; row++) {\n    const cells: PuzCell[] = [];\n    for (let col = 0; col < width; col++) {\n      const index = row * width + col;\n      const solutionChar = solution[index];\n      const playerChar = playerState[index];\n\n      cells.push({\n        solution: solutionChar === \".\" ? undefined : solutionChar,\n        playerState:\n          playerChar === \"-\" || playerChar === \".\" ? undefined : playerChar,\n        isBlack: solutionChar === \".\",\n      });\n    }\n    grid.push(cells);\n  }\n\n  return grid;\n}\n\nfunction assignClueNumbers(grid: PuzCell[][]): Map<string, number> {\n  const cluePositions = new Map<string, number>();\n  let clueNumber = 1;\n  const height = grid.length;\n  const width = grid[0]?.length || 0;\n\n  for (let row = 0; row < height; row++) {\n    for (let col = 0; col < width; col++) {\n      if (grid[row]?.[col]?.isBlack) continue;\n\n      const hasAcross =\n        (col === 0 || grid[row]?.[col - 1]?.isBlack) &&\n        col < width - 1 &&\n        !grid[row]?.[col + 1]?.isBlack;\n\n      const hasDown =\n        (row === 0 || grid[row - 1]?.[col]?.isBlack) &&\n        row < height - 1 &&\n        !grid[row + 1]?.[col]?.isBlack;\n\n      if (hasAcross || hasDown) {\n        if (hasAcross) {\n          cluePositions.set(`A${row},${col}`, clueNumber);\n        }\n        if (hasDown) {\n          cluePositions.set(`D${row},${col}`, clueNumber);\n        }\n        clueNumber++;\n      }\n    }\n  }\n\n  return cluePositions;\n}\n\nfunction parseClues(\n  clueStrings: string[],\n  cluePositions: Map<string, number>,\n): { across: PuzClue[]; down: PuzClue[] } {\n  const across: PuzClue[] = [];\n  const down: PuzClue[] = [];\n  let clueIndex = 0;\n\n  // Sort positions to ensure correct clue assignment\n  const sortedPositions = Array.from(cluePositions.entries()).sort((a, b) => {\n    const [aType, aPos] = a[0].split(\",\");\n    const [bType, bPos] = b[0].split(\",\");\n    const aRow = parseInt(aType?.substring(1) || \"0\");\n    const bRow = parseInt(bType?.substring(1) || \"0\");\n    const aCol = parseInt(aPos || \"0\");\n    const bCol = parseInt(bPos || \"0\");\n\n    if (aRow !== bRow) return aRow - bRow;\n    return aCol - bCol;\n  });\n\n  // First pass: collect all across clues\n  for (const [key, number] of sortedPositions) {\n    if (key.startsWith(\"A\") && clueIndex < clueStrings.length) {\n      across.push({\n        number,\n        text: clueStrings[clueIndex++] || \"\",\n      });\n    }\n  }\n\n  // Second pass: collect all down clues\n  for (const [key, number] of sortedPositions) {\n    if (key.startsWith(\"D\") && clueIndex < clueStrings.length) {\n      down.push({\n        number,\n        text: clueStrings[clueIndex++] || \"\",\n      });\n    }\n  }\n\n  return { across, down };\n}\n\nfunction parseExtraSections(\n  reader: BinaryReader,\n  grid: PuzCell[][],\n): {\n  rebusTable?: Map<number, string>;\n  timer?: { elapsed: number; running: boolean };\n} {\n  const result: {\n    rebusTable?: Map<number, string>;\n    timer?: { elapsed: number; running: boolean };\n  } = {};\n\n  while (reader.hasMore()) {\n    // Check if we have enough bytes for a section header\n    if (reader.position + 8 > reader.length) break;\n\n    // Skip null padding bytes between sections\n    while (reader.hasMore() && reader.buffer[reader.position] === 0) {\n      reader.readUInt8();\n    }\n\n    // Check again after skipping padding\n    if (reader.position + 8 > reader.length) break;\n\n    const sectionTitle = reader.readString(4, true, \"latin1\");\n    const dataLength = reader.readUInt16LE();\n    reader.readUInt16LE(); // checksum (unused)\n\n    // Skip if not a valid section\n    if (dataLength > reader.length - reader.position) break;\n\n    const sectionData = reader.readBytes(dataLength);\n\n    switch (sectionTitle) {\n      case PUZ_SECTION_GRBS: {\n        // Rebus grid - one byte per square\n        const height = grid.length;\n        const width = grid[0]?.length || 0;\n        for (let row = 0; row < height; row++) {\n          for (let col = 0; col < width; col++) {\n            const index = row * width + col;\n            if (index < sectionData.length) {\n              const rebusKey = sectionData[index];\n              if (rebusKey && rebusKey > 0 && grid[row]?.[col]) {\n                grid[row]![col]!.hasRebus = true;\n                grid[row]![col]!.rebusKey = rebusKey - 1;\n              }\n            }\n          }\n        }\n        break;\n      }\n\n      case PUZ_SECTION_RTBL: {\n        // Rebus table - semicolon-separated values with key:value pairs\n        const tableStr = sectionData.toString(\"latin1\");\n        const entries = tableStr.split(\";\");\n        result.rebusTable = new Map();\n\n        for (const entry of entries) {\n          if (entry.includes(\":\")) {\n            const [key, value] = entry.split(\":\");\n            const keyNum = Number.parseInt(key || \"0\", 10);\n            if (!Number.isNaN(keyNum)) {\n              result.rebusTable.set(keyNum, value || \"\");\n            }\n          }\n        }\n        break;\n      }\n\n      case PUZ_SECTION_GEXT: {\n        // Grid extras - circled squares, etc.\n        const height = grid.length;\n        const width = grid[0]?.length || 0;\n        for (let row = 0; row < height; row++) {\n          for (let col = 0; col < width; col++) {\n            const index = row * width + col;\n            if (index < sectionData.length && grid[row]?.[col]) {\n              const flags = sectionData[index];\n              if (flags && flags & PUZ_CIRCLED_CELL_FLAG) {\n                grid[row]![col]!.isCircled = true;\n              }\n            }\n          }\n        }\n        break;\n      }\n\n      case PUZ_SECTION_LTIM: {\n        // Timer data\n        const timerStr = sectionData.toString(\"latin1\");\n        const [elapsed, running] = timerStr.split(\",\");\n        result.timer = {\n          elapsed: parseInt(elapsed || \"0\") || 0,\n          running: running === \"0\" ? false : true,\n        };\n        break;\n      }\n    }\n  }\n\n  return result;\n}\n\n/**\n * Parse a PUZ format crossword puzzle.\n *\n * @param data - The PUZ binary data as Buffer, ArrayBuffer, Uint8Array, or string\n * @param options - Optional parsing options\n * @param options.maxGridSize - Maximum allowed grid dimensions\n * @returns A PuzPuzzle object containing all puzzle data\n * @throws {PuzParseError} When the content is not valid PUZ format\n * @throws {InvalidFileError} When the file is corrupted or has invalid checksums\n *\n * @example\n * ```typescript\n * import { parsePuz } from 'xword-parser';\n * import { readFileSync } from 'fs';\n *\n * const puzData = readFileSync('puzzle.puz');\n * const puzzle = parsePuz(puzData);\n * ```\n */\nexport function parsePuz(\n  data: Buffer | ArrayBuffer | Uint8Array | string,\n  options?: ParseOptions,\n): PuzPuzzle {\n  let buffer: Buffer;\n\n  if (typeof data === \"string\") {\n    // If string is passed, assume it's base64 encoded\n    buffer = Buffer.from(data, \"base64\");\n  } else if (data instanceof Buffer) {\n    buffer = data;\n  } else {\n    buffer = Buffer.from(data as ArrayBuffer);\n  }\n\n  try {\n    const reader = new BinaryReader(buffer);\n    const header = readHeader(reader);\n\n    // Validate dimensions\n    if (header.width <= 0 || header.height <= 0) {\n      throw new PuzParseError(\n        `Invalid puzzle dimensions: width=${header.width}, height=${header.height}`,\n        ErrorCode.PUZ_INVALID_GRID,\n      );\n    }\n\n    // Check against maxGridSize if provided, otherwise use defaults\n    const maxWidth = options?.maxGridSize?.width ?? MAX_GRID_WIDTH;\n    const maxHeight = options?.maxGridSize?.height ?? MAX_GRID_HEIGHT;\n    if (header.width > maxWidth || header.height > maxHeight) {\n      throw new PuzParseError(\n        `Grid dimensions too large: ${header.width}x${header.height}. Maximum supported size is ${maxWidth}x${maxHeight}`,\n        ErrorCode.PUZ_INVALID_GRID,\n      );\n    }\n\n    // Read puzzle data\n    const gridSize = header.width * header.height;\n    const solution = reader.readString(gridSize, true, \"latin1\");\n    const playerState = reader.readString(gridSize, true, \"latin1\");\n\n    // Read strings\n    const title = reader.readNullTerminatedString(\"latin1\");\n    const author = reader.readNullTerminatedString(\"latin1\");\n    const copyright = reader.readNullTerminatedString(\"latin1\");\n\n    // Read clues\n    const clueStrings: string[] = [];\n    for (let i = 0; i < header.numClues; i++) {\n      clueStrings.push(reader.readNullTerminatedString(\"latin1\"));\n    }\n\n    const notes = reader.readNullTerminatedString(\"latin1\");\n\n    // Parse grid\n    const grid = parseGrid(solution, playerState, header.width, header.height);\n\n    // Assign clue numbers and parse clues\n    const cluePositions = assignClueNumbers(grid);\n    const { across, down } = parseClues(clueStrings, cluePositions);\n\n    // Parse extra sections\n    const extras = parseExtraSections(reader, grid);\n\n    return {\n      width: header.width,\n      height: header.height,\n      metadata: {\n        title: title || undefined,\n        author: author || undefined,\n        copyright: copyright || undefined,\n        notes: notes || undefined,\n      },\n      grid,\n      across,\n      down,\n      rebusTable: extras.rebusTable,\n      isScrambled: header.scrambledTag !== 0,\n      timer: extras.timer,\n    };\n  } catch (error) {\n    // Convert BinaryParseError to PuzParseError\n    if (error instanceof BinaryParseError) {\n      throw new PuzParseError(error.message, ErrorCode.PUZ_PARSE_ERROR);\n    }\n    // Re-throw other errors as-is\n    throw error;\n  }\n}\n\n/**\n * Convert a PUZ puzzle to the unified Puzzle format.\n *\n * @param puzzle - The PuzPuzzle object to convert\n * @returns A unified Puzzle object\n *\n * @example\n * ```typescript\n * import { parsePuz, convertPuzToUnified } from 'xword-parser';\n * const puzPuzzle = parsePuz(puzData);\n * const unifiedPuzzle = convertPuzToUnified(puzPuzzle);\n * ```\n */\nexport function convertPuzToUnified(puzzle: PuzPuzzle): Puzzle {\n  const grid: Grid = {\n    width: puzzle.width,\n    height: puzzle.height,\n    cells: [],\n  };\n\n  // Convert grid and assign numbers\n  let cellNumber = 1;\n  for (let y = 0; y < puzzle.height; y++) {\n    const row: UnifiedCell[] = [];\n    for (let x = 0; x < puzzle.width; x++) {\n      const puzCell = puzzle.grid[y]?.[x];\n\n      // Determine if this cell should have a number\n      let number: number | undefined;\n      if (puzCell && !puzCell.isBlack) {\n        const needsNumber =\n          // Start of across word\n          ((x === 0 || puzzle.grid[y]?.[x - 1]?.isBlack) &&\n            x < puzzle.width - 1 &&\n            !puzzle.grid[y]?.[x + 1]?.isBlack) ||\n          // Start of down word\n          ((y === 0 || puzzle.grid[y - 1]?.[x]?.isBlack) &&\n            y < puzzle.height - 1 &&\n            !puzzle.grid[y + 1]?.[x]?.isBlack);\n\n        if (needsNumber) {\n          number = cellNumber++;\n        }\n      }\n\n      const cell: UnifiedCell = {\n        solution: puzCell?.solution,\n        number,\n        isBlack: puzCell?.isBlack || false,\n      };\n\n      // Add cell-specific metadata\n      if (puzCell?.isCircled) {\n        cell.isCircled = true;\n      }\n      if (puzCell?.hasRebus) {\n        cell.hasRebus = true;\n        if (puzCell.rebusKey !== undefined) {\n          cell.rebusKey = puzCell.rebusKey;\n        }\n      }\n\n      row.push(cell);\n    }\n    grid.cells.push(row);\n  }\n\n  // Convert clues\n  const clues: Clues = {\n    across: puzzle.across.map((c) => ({\n      number: c.number,\n      text: c.text,\n    })),\n    down: puzzle.down.map((c) => ({\n      number: c.number,\n      text: c.text,\n    })),\n  };\n\n  const result: Puzzle = {\n    title: puzzle.metadata.title,\n    author: puzzle.metadata.author,\n    copyright: puzzle.metadata.copyright,\n    notes: puzzle.metadata.notes,\n    grid,\n    clues,\n    rebusTable: puzzle.rebusTable,\n  };\n\n  // Add puzzle-level metadata if present\n  const additionalProps: Record<string, unknown> = {};\n  if (puzzle.isScrambled) {\n    additionalProps.isScrambled = puzzle.isScrambled;\n  }\n  if (puzzle.timer) {\n    additionalProps.timer = puzzle.timer;\n  }\n\n  if (Object.keys(additionalProps).length > 0) {\n    result.additionalProperties = additionalProps;\n  }\n\n  return result;\n}\n"],"mappings":"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"}