{"version":3,"sources":["../src/index.ts","../src/grf-base.ts","../src/des.ts","../src/decoder.ts","../src/grf-browser.ts","../src/grf-node.ts","../src/buffer-pool.ts"],"sourcesContent":["export {GrfBrowser} from './grf-browser';\nexport {GrfNode, GrfNodeOptions} from './grf-node';\nexport type {\n  TFileEntry,\n  FilenameEncoding,\n  GrfOptions,\n  FindOptions,\n  ResolveResult,\n  GrfStats\n} from './grf-base';\nexport {GrfError, GRF_ERROR_CODES} from './grf-base';\nexport {bufferPool} from './buffer-pool';\n\n// Encoding utilities\nexport {\n  isMojibake,\n  fixMojibake,\n  toMojibake,\n  normalizeFilename,\n  normalizePath as normalizeEncodingPath,\n  countBadChars,\n  countC1ControlChars,\n  countReplacementChars,\n  hasIconvLite\n} from './decoder';\n","import pako from 'pako';\nimport jDataview from 'jdataview';\nimport {decodeFull, decodeHeader} from './des';\nimport {\n  decodeBytes,\n  detectBestKoreanEncoding,\n  countBadChars,\n  countReplacementChars,\n  countC1ControlChars,\n  hasIconvLite\n} from './decoder';\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface TFileEntry {\n  type: number;\n  offset: number;\n  realSize: number;\n  compressedSize: number;\n  lengthAligned: number;\n  /** Raw filename bytes for re-decoding if needed */\n  rawNameBytes?: Uint8Array;\n}\n\n/** Supported filename encodings */\nexport type FilenameEncoding = 'utf-8' | 'euc-kr' | 'cp949' | 'latin1' | 'auto';\n\n/** GRF loader options */\nexport interface GrfOptions {\n  /** Encoding for filenames (default: 'auto') */\n  filenameEncoding?: FilenameEncoding;\n  /** Threshold for auto-detection: if % of U+FFFD exceeds this, try Korean encodings (default: 0.01 = 1%) */\n  autoDetectThreshold?: number;\n  /** Maximum uncompressed size per file in bytes (default: 256MB) */\n  maxFileUncompressedBytes?: number;\n  /** Maximum total entries allowed (default: 500000) */\n  maxEntries?: number;\n}\n\n/** Search/find options */\nexport interface FindOptions {\n  /** Filter by file extension (without dot, e.g., 'spr', 'act') */\n  ext?: string;\n  /** Filter by substring in path */\n  contains?: string;\n  /** Filter by path ending */\n  endsWith?: string;\n  /** Filter by regex pattern */\n  regex?: RegExp;\n  /** Maximum results to return (default: unlimited) */\n  limit?: number;\n}\n\n/** Result of path resolution */\nexport interface ResolveResult {\n  status: 'found' | 'not_found' | 'ambiguous';\n  /** The exact matched path (if found) */\n  matchedPath?: string;\n  /** All candidate paths (if ambiguous) */\n  candidates?: string[];\n}\n\n/** GRF statistics */\nexport interface GrfStats {\n  /** Total file count */\n  fileCount: number;\n  /** Number of filenames with replacement character (U+FFFD) */\n  badNameCount: number;\n  /** Number of normalized key collisions */\n  collisionCount: number;\n  /** Extension statistics: ext -> count */\n  extensionStats: Map<string, number>;\n  /** Detected encoding used */\n  detectedEncoding: FilenameEncoding;\n}\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\nexport const GRF_ERROR_CODES = {\n  INVALID_MAGIC: 'GRF_INVALID_MAGIC',\n  UNSUPPORTED_VERSION: 'GRF_UNSUPPORTED_VERSION',\n  NOT_LOADED: 'GRF_NOT_LOADED',\n  FILE_NOT_FOUND: 'GRF_FILE_NOT_FOUND',\n  AMBIGUOUS_PATH: 'GRF_AMBIGUOUS_PATH',\n  DECOMPRESS_FAIL: 'GRF_DECOMPRESS_FAIL',\n  CORRUPT_TABLE: 'GRF_CORRUPT_TABLE',\n  LIMIT_EXCEEDED: 'GRF_LIMIT_EXCEEDED',\n  INVALID_OFFSET: 'GRF_INVALID_OFFSET',\n  DECRYPT_REQUIRED: 'GRF_DECRYPT_REQUIRED',\n} as const;\n\nexport class GrfError extends Error {\n  constructor(\n    public code: keyof typeof GRF_ERROR_CODES,\n    message: string,\n    public context?: Record<string, unknown>\n  ) {\n    super(message);\n    this.name = 'GrfError';\n  }\n}\n\nconst FILELIST_TYPE_FILE = 0x01;\nconst FILELIST_TYPE_ENCRYPT_MIXED = 0x02; // encryption mode 0 (header DES + periodic DES/shuffle)\nconst FILELIST_TYPE_ENCRYPT_HEADER = 0x04; // encryption mode 1 (header DES only)\n\nconst HEADER_SIGNATURE = 'Master of Magic';\nconst HEADER_SIZE = 46;\nconst FILE_TABLE_SIZE = Uint32Array.BYTES_PER_ELEMENT * 2;\n\n// Default limits\nconst DEFAULT_MAX_FILE_UNCOMPRESSED_BYTES = 256 * 1024 * 1024; // 256MB\nconst DEFAULT_MAX_ENTRIES = 500000;\nconst DEFAULT_AUTO_DETECT_THRESHOLD = 0.01; // 1%\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Normalize a path for case-insensitive, slash-agnostic lookup\n */\nfunction normalizePath(path: string): string {\n  return path.toLowerCase().replace(/\\\\/g, '/');\n}\n\n/**\n * Get file extension from path (lowercase, without dot)\n */\nfunction getExtension(path: string): string {\n  const lastDot = path.lastIndexOf('.');\n  if (lastDot === -1 || lastDot === path.length - 1) return '';\n  return path.substring(lastDot + 1).toLowerCase();\n}\n\n// Note: countReplacementChars, countBadChars, countC1ControlChars, decodeBytes,\n// and detectBestKoreanEncoding are imported from './decoder'\n\n/**\n * Decode filename bytes with specified encoding.\n * Uses iconv-lite for Korean encodings in Node.js for proper CP949 support.\n */\nfunction decodeFilenameBytes(bytes: Uint8Array, encoding: FilenameEncoding): string {\n  // For Korean encodings, always use 'cp949' as it's a superset of euc-kr\n  // This ensures proper handling of extended Korean characters\n  const actualEncoding = (encoding === 'euc-kr' || encoding === 'cp949') ? 'cp949' : encoding;\n  return decodeBytes(bytes, actualEncoding);\n}\n\n// ============================================================================\n// GrfBase Class\n// ============================================================================\n\nexport abstract class GrfBase<T> {\n  public version = 0x200;\n  public fileCount = 0;\n  public loaded = false;\n\n  /** Map of exact filename -> entry */\n  public files = new Map<string, TFileEntry>();\n\n  /** Map of normalized path -> array of exact filenames (supports collisions) */\n  private normalizedIndex = new Map<string, string[]>();\n\n  /** Map of extension -> array of exact filenames (for fast extension lookup) */\n  private extensionIndex = new Map<string, string[]>();\n\n  private fileTableOffset = 0;\n  private cache = new Map<string, Uint8Array>();\n  private cacheMaxSize = 50;\n  private cacheOrder: string[] = [];\n\n  // Options\n  protected options: Required<GrfOptions>;\n\n  // Statistics\n  private _stats: GrfStats = {\n    fileCount: 0,\n    badNameCount: 0,\n    collisionCount: 0,\n    extensionStats: new Map(),\n    detectedEncoding: 'utf-8'\n  };\n\n  constructor(private fd: T, options?: GrfOptions) {\n    this.options = {\n      filenameEncoding: options?.filenameEncoding ?? 'auto',\n      autoDetectThreshold: options?.autoDetectThreshold ?? DEFAULT_AUTO_DETECT_THRESHOLD,\n      maxFileUncompressedBytes: options?.maxFileUncompressedBytes ?? DEFAULT_MAX_FILE_UNCOMPRESSED_BYTES,\n      maxEntries: options?.maxEntries ?? DEFAULT_MAX_ENTRIES\n    };\n  }\n\n  abstract getStreamBuffer(\n    fd: T,\n    offset: number,\n    length: number\n  ): Promise<Uint8Array>;\n\n  public async getStreamReader(\n    offset: number,\n    length: number\n  ): Promise<jDataview> {\n    const buffer = await this.getStreamBuffer(this.fd, offset, length);\n\n    return new jDataview(buffer, void 0, void 0, true);\n  }\n\n  public async load(): Promise<void> {\n    if (!this.loaded) {\n      await this.parseHeader();\n      await this.parseFileList();\n      this.loaded = true;\n    }\n  }\n\n  private async parseHeader(): Promise<void> {\n    const reader = await this.getStreamReader(0, HEADER_SIZE);\n\n    const signature = reader.getString(15);\n    if (signature !== HEADER_SIGNATURE) {\n      throw new GrfError('INVALID_MAGIC', 'Not a GRF file (invalid signature)', { signature });\n    }\n\n    reader.skip(15);\n    // Now at offset 30. Version is always at offset 42 for both 0x200 and 0x300.\n    // Read version first to determine how to parse the rest of the header.\n    // Save position, peek version, then parse based on version.\n    const afterKey = reader.tell();\n    reader.seek(42);\n    this.version = reader.getUint32();\n\n    if (this.version !== 0x200 && this.version !== 0x300) {\n      throw new GrfError('UNSUPPORTED_VERSION', `Unsupported version \"0x${this.version.toString(16)}\"`, { version: this.version });\n    }\n\n    reader.seek(afterKey);\n\n    if (this.version === 0x200) {\n      // 0x200: [table_offset:u32][seeds:u32][filecount:u32][version:u32]\n      this.fileTableOffset = reader.getUint32() + HEADER_SIZE;\n      const reservedFiles = reader.getUint32();\n      this.fileCount = reader.getUint32() - reservedFiles - 7;\n    } else {\n      // 0x300: [table_offset:u64][filecount:u32][version:u32]\n      const low = reader.getUint32();\n      const high = reader.getUint32();\n\n      // GRFEditor heuristic: bytes 35-37 (upper 3 bytes of high word) must be zero.\n      // Protects against mis-tagged GRFs where version says 0x300 but layout is 0x200.\n      if ((high >>> 8) !== 0) {\n        // Fall back to 0x200 parsing\n        this.version = 0x200;\n        reader.seek(afterKey);\n        this.fileTableOffset = reader.getUint32() + HEADER_SIZE;\n        const reservedFiles = reader.getUint32();\n        this.fileCount = reader.getUint32() - reservedFiles - 7;\n      } else {\n        this.fileTableOffset = high * 0x100000000 + low + HEADER_SIZE;\n        this.fileCount = reader.getUint32();\n      }\n    }\n\n    // Validate entry count against limit\n    if (this.fileCount > this.options.maxEntries) {\n      throw new GrfError('LIMIT_EXCEEDED', `File count ${this.fileCount} exceeds limit ${this.options.maxEntries}`, {\n        fileCount: this.fileCount,\n        maxEntries: this.options.maxEntries\n      });\n    }\n  }\n\n  private async parseFileList(): Promise<void> {\n    // GRF 0x300 has an extra 4-byte field before the file table header\n    const tableSkip = this.version === 0x300 ? 4 : 0;\n\n    // Read table list, stored information\n    const reader = await this.getStreamReader(\n      this.fileTableOffset + tableSkip,\n      FILE_TABLE_SIZE\n    );\n    const compressedSize = reader.getUint32();\n    const realSize = reader.getUint32();\n\n    // Load the chunk and uncompress it\n    const compressed = await this.getStreamBuffer(\n      this.fd,\n      this.fileTableOffset + tableSkip + FILE_TABLE_SIZE,\n      compressedSize\n    );\n\n    let data: Uint8Array;\n    try {\n      data = pako.inflate(compressed);\n    } catch (error) {\n      throw new GrfError('CORRUPT_TABLE', 'Failed to decompress file table', {\n        compressedSize,\n        realSize,\n        error: error instanceof Error ? error.message : String(error)\n      });\n    }\n\n    // Validate decompressed size\n    if (data.length !== realSize) {\n      throw new GrfError('CORRUPT_TABLE', `File table size mismatch: expected ${realSize}, got ${data.length}`, {\n        expected: realSize,\n        actual: data.length\n      });\n    }\n\n    // Determine encoding to use\n    let detectedEncoding: FilenameEncoding = this.options.filenameEncoding;\n\n    // If auto-detect, sample filenames and use improved detection algorithm\n    if (this.options.filenameEncoding === 'auto') {\n      const sampleBytes: Uint8Array[] = [];\n      let samplePos = 0;\n      // Sample more files for better detection accuracy\n      const sampleCount = Math.min(200, this.fileCount);\n      // 0x200: 17-byte entries (4-byte offset), 0x300: 21-byte entries (8-byte offset)\n      const entryDataSize = this.version === 0x300 ? 21 : 17;\n\n      for (let i = 0; i < sampleCount && samplePos < data.length; i++) {\n        let endPos = samplePos;\n        while (data[endPos] !== 0 && endPos < data.length) endPos++;\n\n        // Only include samples with non-ASCII bytes for detection\n        const bytes = data.subarray(samplePos, endPos);\n        sampleBytes.push(bytes);\n\n        samplePos = endPos + 1 + entryDataSize;\n      }\n\n      // Use improved encoding detection algorithm from decoder module\n      // This analyzes:\n      // 1. Valid byte sequence patterns for UTF-8 vs EUC-KR/CP949\n      // 2. Replacement character ratios after decoding (including C1 control chars)\n      // 3. Uses iconv-lite in Node.js for proper CP949 support\n      detectedEncoding = detectBestKoreanEncoding(sampleBytes, this.options.autoDetectThreshold);\n    }\n\n    this._stats.detectedEncoding = detectedEncoding;\n\n    // Reset stats\n    this._stats.badNameCount = 0;\n    this._stats.collisionCount = 0;\n    this._stats.extensionStats.clear();\n\n    // 0x200: 17-byte entries (4-byte offset), 0x300: 21-byte entries (8-byte offset)\n    const entryDataSize = this.version === 0x300 ? 21 : 17;\n\n    for (let i = 0, p = 0; i < this.fileCount; ++i) {\n      // Validate position\n      if (p >= data.length) {\n        throw new GrfError('CORRUPT_TABLE', `Unexpected end of file table at entry ${i}`, {\n          position: p,\n          dataLength: data.length,\n          entryIndex: i\n        });\n      }\n\n      // Find null terminator\n      let endPos = p;\n      while (data[endPos] !== 0 && endPos < data.length) {\n        endPos++;\n      }\n\n      // Store raw bytes and decode filename using the detected encoding\n      // Uses iconv-lite for Korean encodings in Node.js for proper CP949 support\n      const rawBytes = data.slice(p, endPos); // Copy for storage\n      const filename = decodeFilenameBytes(rawBytes, detectedEncoding);\n\n      // Count bad names (including C1 control chars that indicate wrong decode)\n      if (countBadChars(filename) > 0) {\n        this._stats.badNameCount++;\n      }\n\n      p = endPos + 1;\n\n      // Validate remaining bytes for entry\n      if (p + entryDataSize > data.length) {\n        throw new GrfError('CORRUPT_TABLE', `Incomplete entry data at entry ${i}`, {\n          position: p,\n          dataLength: data.length,\n          entryIndex: i\n        });\n      }\n\n      // prettier-ignore\n      const compressedSize = data[p++] | (data[p++] << 8) | (data[p++] << 16) | (data[p++] << 24);\n      // prettier-ignore\n      const lengthAligned = data[p++] | (data[p++] << 8) | (data[p++] << 16) | (data[p++] << 24);\n      // prettier-ignore\n      const realSize = data[p++] | (data[p++] << 8) | (data[p++] << 16) | (data[p++] << 24);\n      const type = data[p++];\n\n      let offset: number;\n      if (this.version === 0x300) {\n        // 0x300: 8-byte (64-bit) offset\n        // prettier-ignore\n        const low = (data[p++] | (data[p++] << 8) | (data[p++] << 16) | (data[p++] << 24)) >>> 0;\n        // prettier-ignore\n        const high = (data[p++] | (data[p++] << 8) | (data[p++] << 16) | (data[p++] << 24)) >>> 0;\n        offset = high * 0x100000000 + low;\n      } else {\n        // 0x200: 4-byte (32-bit) offset\n        // prettier-ignore\n        offset = (data[p++] | (data[p++] << 8) | (data[p++] << 16) | (data[p++] << 24)) >>> 0;\n      }\n\n      const entry: TFileEntry = {\n        compressedSize,\n        lengthAligned,\n        realSize,\n        type,\n        offset,\n        rawNameBytes: rawBytes\n      };\n\n      // Validate sizes against limits\n      if (entry.realSize > this.options.maxFileUncompressedBytes) {\n        // Skip this entry but don't fail - just warn\n        continue;\n      }\n\n      // Only process files (not folders)\n      if (entry.type & FILELIST_TYPE_FILE) {\n        // Add to main files map\n        this.files.set(filename, entry);\n\n        // Add to normalized index (supports collisions)\n        const normalizedKey = normalizePath(filename);\n        const existingNorm = this.normalizedIndex.get(normalizedKey);\n        if (existingNorm) {\n          existingNorm.push(filename);\n          this._stats.collisionCount++;\n        } else {\n          this.normalizedIndex.set(normalizedKey, [filename]);\n        }\n\n        // Add to extension index\n        const ext = getExtension(filename);\n        if (ext) {\n          const existingExt = this.extensionIndex.get(ext);\n          if (existingExt) {\n            existingExt.push(filename);\n          } else {\n            this.extensionIndex.set(ext, [filename]);\n          }\n\n          // Update extension stats\n          this._stats.extensionStats.set(ext, (this._stats.extensionStats.get(ext) || 0) + 1);\n        }\n      }\n    }\n\n    this._stats.fileCount = this.files.size;\n  }\n\n  private decodeEntry(data: Uint8Array, entry: TFileEntry): Uint8Array {\n    // Decode the file\n    if (entry.type & FILELIST_TYPE_ENCRYPT_MIXED) {\n      decodeFull(data, entry.lengthAligned, entry.compressedSize);\n    } else if (entry.type & FILELIST_TYPE_ENCRYPT_HEADER) {\n      decodeHeader(data, entry.lengthAligned);\n    }\n\n    // No compression\n    if (entry.realSize === entry.compressedSize) {\n      return data;\n    }\n\n    // Uncompress\n    return pako.inflate(data);\n  }\n\n  private addToCache(filename: string, data: Uint8Array): void {\n    // Remove oldest if cache is full\n    if (this.cacheOrder.length >= this.cacheMaxSize) {\n      const oldest = this.cacheOrder.shift();\n      if (oldest) {\n        this.cache.delete(oldest);\n      }\n    }\n\n    // Add to cache\n    this.cache.set(filename, data);\n    this.cacheOrder.push(filename);\n  }\n\n  private getFromCache(filename: string): Uint8Array | undefined {\n    const cached = this.cache.get(filename);\n    if (cached) {\n      // Move to end (most recently used)\n      const index = this.cacheOrder.indexOf(filename);\n      if (index > -1) {\n        this.cacheOrder.splice(index, 1);\n        this.cacheOrder.push(filename);\n      }\n    }\n    return cached;\n  }\n\n  public clearCache(): void {\n    this.cache.clear();\n    this.cacheOrder = [];\n  }\n\n  public async getFile(\n    filename: string\n  ): Promise<{data: null | Uint8Array; error: null | string}> {\n    if (!this.loaded) {\n      return Promise.resolve({data: null, error: 'GRF not loaded yet'});\n    }\n\n    // Try to resolve the path (exact match first, then normalized)\n    const resolved = this.resolvePath(filename);\n\n    if (resolved.status === 'not_found') {\n      return Promise.resolve({data: null, error: `File \"${filename}\" not found`});\n    }\n\n    if (resolved.status === 'ambiguous') {\n      return Promise.resolve({\n        data: null,\n        error: `Ambiguous path \"${filename}\": ${resolved.candidates?.length} matches found. Use exact path: ${resolved.candidates?.slice(0, 5).join(', ')}${(resolved.candidates?.length || 0) > 5 ? '...' : ''}`\n      });\n    }\n\n    const path = resolved.matchedPath!;\n\n    // Check cache first\n    const cached = this.getFromCache(path);\n    if (cached) {\n      return Promise.resolve({data: cached, error: null});\n    }\n\n    const entry = this.files.get(path);\n\n    if (!entry) {\n      return { data: null, error: `File \"${path}\" not found` };\n    }\n\n    const data = await this.getStreamBuffer(\n      this.fd,\n      entry.offset + HEADER_SIZE,\n      entry.lengthAligned\n    );\n\n    try {\n      const result = this.decodeEntry(data, entry);\n\n      // Add to cache\n      this.addToCache(path, result);\n\n      return Promise.resolve({data: result, error: null});\n    } catch (error) {\n      const message =\n        error instanceof Error ? error.message : String(error);\n      return { data: null, error: message };\n    }\n  }\n\n  // ===========================================================================\n  // Path Resolution\n  // ===========================================================================\n\n  /**\n   * Resolve a path to its exact filename in the GRF.\n   * Tries exact match first, then normalized (case-insensitive, slash-agnostic).\n   */\n  public resolvePath(query: string): ResolveResult {\n    // Try exact match first\n    if (this.files.has(query)) {\n      return { status: 'found', matchedPath: query };\n    }\n\n    // Try normalized lookup\n    const normalizedQuery = normalizePath(query);\n    const candidates = this.normalizedIndex.get(normalizedQuery);\n\n    if (!candidates || candidates.length === 0) {\n      return { status: 'not_found' };\n    }\n\n    if (candidates.length === 1) {\n      return { status: 'found', matchedPath: candidates[0] };\n    }\n\n    // Multiple candidates - ambiguous\n    return { status: 'ambiguous', candidates };\n  }\n\n  /**\n   * Check if a file exists in the GRF.\n   */\n  public hasFile(filename: string): boolean {\n    const resolved = this.resolvePath(filename);\n    return resolved.status === 'found';\n  }\n\n  /**\n   * Get file entry metadata without extracting the file.\n   */\n  public getEntry(filename: string): TFileEntry | null {\n    const resolved = this.resolvePath(filename);\n    if (resolved.status !== 'found' || !resolved.matchedPath) {\n      return null;\n    }\n    return this.files.get(resolved.matchedPath) || null;\n  }\n\n  // ===========================================================================\n  // Search API\n  // ===========================================================================\n\n  /**\n   * Find files matching the given criteria.\n   */\n  public find(options: FindOptions = {}): string[] {\n    const { ext, contains, endsWith, regex, limit } = options;\n    let results: string[] = [];\n\n    // If searching by extension only, use the extension index (fast path)\n    if (ext && !contains && !endsWith && !regex) {\n      const extLower = ext.toLowerCase().replace(/^\\./, ''); // Remove leading dot if present\n      results = this.extensionIndex.get(extLower) || [];\n    } else {\n      // Full search\n      for (const filename of this.files.keys()) {\n        // Extension filter\n        if (ext) {\n          const extLower = ext.toLowerCase().replace(/^\\./, '');\n          if (getExtension(filename) !== extLower) continue;\n        }\n\n        // Contains filter (case-insensitive)\n        if (contains) {\n          const normalizedFilename = normalizePath(filename);\n          const normalizedContains = normalizePath(contains);\n          if (!normalizedFilename.includes(normalizedContains)) continue;\n        }\n\n        // EndsWith filter (case-insensitive)\n        if (endsWith) {\n          const normalizedFilename = normalizePath(filename);\n          const normalizedEndsWith = normalizePath(endsWith);\n          if (!normalizedFilename.endsWith(normalizedEndsWith)) continue;\n        }\n\n        // Regex filter\n        if (regex && !regex.test(filename)) continue;\n\n        results.push(filename);\n\n        // Limit check\n        if (limit && results.length >= limit) break;\n      }\n    }\n\n    // Apply limit if not already applied\n    if (limit && results.length > limit) {\n      results = results.slice(0, limit);\n    }\n\n    return results;\n  }\n\n  /**\n   * Get all files with a specific extension.\n   */\n  public getFilesByExtension(ext: string): string[] {\n    const extLower = ext.toLowerCase().replace(/^\\./, '');\n    return this.extensionIndex.get(extLower) || [];\n  }\n\n  /**\n   * List all unique extensions in the GRF.\n   */\n  public listExtensions(): string[] {\n    return Array.from(this.extensionIndex.keys()).sort();\n  }\n\n  /**\n   * List all files in the GRF.\n   */\n  public listFiles(): string[] {\n    return Array.from(this.files.keys());\n  }\n\n  // ===========================================================================\n  // Statistics\n  // ===========================================================================\n\n  /**\n   * Get GRF statistics.\n   */\n  public getStats(): GrfStats {\n    return { ...this._stats, extensionStats: new Map(this._stats.extensionStats) };\n  }\n\n  /**\n   * Get the detected/configured encoding used for filenames.\n   */\n  public getDetectedEncoding(): FilenameEncoding {\n    return this._stats.detectedEncoding;\n  }\n\n  // ===========================================================================\n  // Re-decoding Support\n  // ===========================================================================\n\n  /**\n   * Re-decode all filenames with a different encoding.\n   * Useful if auto-detection chose wrong or you want to try a specific encoding.\n   */\n  public async reloadWithEncoding(encoding: FilenameEncoding): Promise<void> {\n    this.options.filenameEncoding = encoding;\n    this.files.clear();\n    this.normalizedIndex.clear();\n    this.extensionIndex.clear();\n    this.clearCache();\n    this.loaded = false;\n    await this.load();\n  }\n}\n","/**\n * Ragnarok Online DES decoder implementation\n * It's a custom one with some alterations\n */\nexport {decodeFull, decodeHeader};\n\nconst mask = new Uint8Array([0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01]);\nconst tmp = new Uint8Array(8);\nconst tmp2 = new Uint8Array(8);\nconst clean = new Uint8Array(8);\n\n// prettier-ignore\nconst initialPermutationTable = new Uint8Array([\n  58, 50, 42, 34, 26, 18, 10,  2,\n  60, 52, 44, 36, 28, 20, 12,  4,\n  62, 54, 46, 38, 30, 22, 14,  6,\n  64, 56, 48, 40, 32, 24, 16,  8,\n  57, 49, 41, 33, 25, 17,  9,  1,\n  59, 51, 43, 35, 27, 19, 11,  3,\n  61, 53, 45, 37, 29, 21, 13,  5,\n  63, 55, 47, 39, 31, 23, 15,  7\n]);\n\n// prettier-ignore\nconst finalPermutationTable = new Uint8Array([\n  40,  8, 48, 16, 56, 24, 64, 32,\n  39,  7, 47, 15, 55, 23, 63, 31,\n  38,  6, 46, 14, 54, 22, 62, 30,\n  37,  5, 45, 13, 53, 21, 61, 29,\n  36,  4, 44, 12, 52, 20, 60, 28,\n  35,  3, 43, 11, 51, 19, 59, 27,\n  34,  2, 42, 10, 50, 18, 58, 26,\n  33,  1, 41,  9, 49, 17, 57, 25\n]);\n\n// prettier-ignore\nconst transpositionTable = new Uint8Array([\n  16,  7, 20, 21,\n  29, 12, 28, 17,\n   1, 15, 23, 26,\n   5, 18, 31, 10,\n   2,  8, 24, 14,\n  32, 27,  3,  9,\n  19, 13, 30,  6,\n  22, 11,  4, 25\n]);\n\n// prettier-ignore\nconst substitutionBoxTable = [\n  new Uint8Array([\n    0xef, 0x03, 0x41, 0xfd, 0xd8, 0x74, 0x1e, 0x47,  0x26, 0xef, 0xfb, 0x22, 0xb3, 0xd8, 0x84, 0x1e,\n    0x39, 0xac, 0xa7, 0x60, 0x62, 0xc1, 0xcd, 0xba,  0x5c, 0x96, 0x90, 0x59, 0x05, 0x3b, 0x7a, 0x85,\n    0x40, 0xfd, 0x1e, 0xc8, 0xe7, 0x8a, 0x8b, 0x21,  0xda, 0x43, 0x64, 0x9f, 0x2d, 0x14, 0xb1, 0x72,\n    0xf5, 0x5b, 0xc8, 0xb6, 0x9c, 0x37, 0x76, 0xec,  0x39, 0xa0, 0xa3, 0x05, 0x52, 0x6e, 0x0f, 0xd9 \n  ]),\n  new Uint8Array([\n    0xa7, 0xdd, 0x0d, 0x78, 0x9e, 0x0b, 0xe3, 0x95,  0x60, 0x36, 0x36, 0x4f, 0xf9, 0x60, 0x5a, 0xa3,\n    0x11, 0x24, 0xd2, 0x87, 0xc8, 0x52, 0x75, 0xec,  0xbb, 0xc1, 0x4c, 0xba, 0x24, 0xfe, 0x8f, 0x19,\n    0xda, 0x13, 0x66, 0xaf, 0x49, 0xd0, 0x90, 0x06,  0x8c, 0x6a, 0xfb, 0x91, 0x37, 0x8d, 0x0d, 0x78,\n    0xbf, 0x49, 0x11, 0xf4, 0x23, 0xe5, 0xce, 0x3b,  0x55, 0xbc, 0xa2, 0x57, 0xe8, 0x22, 0x74, 0xce\n  ]),\n  new Uint8Array([\n    0x2c, 0xea, 0xc1, 0xbf, 0x4a, 0x24, 0x1f, 0xc2,  0x79, 0x47, 0xa2, 0x7c, 0xb6, 0xd9, 0x68, 0x15,\n    0x80, 0x56, 0x5d, 0x01, 0x33, 0xfd, 0xf4, 0xae,  0xde, 0x30, 0x07, 0x9b, 0xe5, 0x83, 0x9b, 0x68,\n    0x49, 0xb4, 0x2e, 0x83, 0x1f, 0xc2, 0xb5, 0x7c,  0xa2, 0x19, 0xd8, 0xe5, 0x7c, 0x2f, 0x83, 0xda,\n    0xf7, 0x6b, 0x90, 0xfe, 0xc4, 0x01, 0x5a, 0x97,  0x61, 0xa6, 0x3d, 0x40, 0x0b, 0x58, 0xe6, 0x3d\n  ]),\n  new Uint8Array([\n    0x4d, 0xd1, 0xb2, 0x0f, 0x28, 0xbd, 0xe4, 0x78,  0xf6, 0x4a, 0x0f, 0x93, 0x8b, 0x17, 0xd1, 0xa4,\n    0x3a, 0xec, 0xc9, 0x35, 0x93, 0x56, 0x7e, 0xcb,  0x55, 0x20, 0xa0, 0xfe, 0x6c, 0x89, 0x17, 0x62,\n    0x17, 0x62, 0x4b, 0xb1, 0xb4, 0xde, 0xd1, 0x87,  0xc9, 0x14, 0x3c, 0x4a, 0x7e, 0xa8, 0xe2, 0x7d,\n    0xa0, 0x9f, 0xf6, 0x5c, 0x6a, 0x09, 0x8d, 0xf0,  0x0f, 0xe3, 0x53, 0x25, 0x95, 0x36, 0x28, 0xcb\n  ])\n];\n\n/**\n * Initial permutation (IP).\n */\nfunction initialPermutation(src: Uint8Array, index: number): void {\n  for (let i = 0; i < 64; ++i) {\n    const j = initialPermutationTable[i] - 1;\n    if (src[index + ((j >> 3) & 7)] & mask[j & 7]) {\n      tmp[(i >> 3) & 7] |= mask[i & 7];\n    }\n  }\n\n  src.set(tmp, index);\n  tmp.set(clean);\n}\n\n/**\n * Final permutation (IP^-1).\n */\nfunction finalPermutation(src: Uint8Array, index: number): void {\n  for (let i = 0; i < 64; ++i) {\n    const j = finalPermutationTable[i] - 1;\n    if (src[index + ((j >> 3) & 7)] & mask[j & 7]) {\n      tmp[(i >> 3) & 7] |= mask[i & 7];\n    }\n  }\n\n  src.set(tmp, index);\n  tmp.set(clean);\n}\n\n/**\n * Transposition (P-BOX).\n */\nfunction transposition(src: Uint8Array, index: number): void {\n  for (let i = 0; i < 32; ++i) {\n    const j = transpositionTable[i] - 1;\n    if (src[index + (j >> 3)] & mask[j & 7]) {\n      tmp[(i >> 3) + 4] |= mask[i & 7];\n    }\n  }\n\n  src.set(tmp, index);\n  tmp.set(clean);\n}\n\n/**\n * Expansion (E).\n * Expands upper four 8-bits (32b) into eight 6-bits (48b).\n */\nfunction expansion(src: Uint8Array, index: number): void {\n  tmp[0] = ((src[index + 7] << 5) | (src[index + 4] >> 3)) & 0x3f; // ..0 vutsr\n  tmp[1] = ((src[index + 4] << 1) | (src[index + 5] >> 7)) & 0x3f; // ..srqpo n\n  tmp[2] = ((src[index + 4] << 5) | (src[index + 5] >> 3)) & 0x3f; // ..o nmlkj\n  tmp[3] = ((src[index + 5] << 1) | (src[index + 6] >> 7)) & 0x3f; // ..kjihg f\n  tmp[4] = ((src[index + 5] << 5) | (src[index + 6] >> 3)) & 0x3f; // ..g fedcb\n  tmp[5] = ((src[index + 6] << 1) | (src[index + 7] >> 7)) & 0x3f; // ..cba98 7\n  tmp[6] = ((src[index + 6] << 5) | (src[index + 7] >> 3)) & 0x3f; // ..8 76543\n  tmp[7] = ((src[index + 7] << 1) | (src[index + 4] >> 7)) & 0x3f; // ..43210 v\n\n  src.set(tmp, index);\n  tmp.set(clean);\n}\n\n/**\n * Substitution boxes (S-boxes).\n * NOTE: This implementation was optimized to process two nibbles in one step (twice as fast).\n */\nfunction substitutionBox(src: Uint8Array, index: number): void {\n  for (let i = 0; i < 4; ++i) {\n    tmp[i] =\n      (substitutionBoxTable[i][src[i * 2 + 0 + index]] & 0xf0) |\n      (substitutionBoxTable[i][src[i * 2 + 1 + index]] & 0x0f);\n  }\n\n  src.set(tmp, index);\n  tmp.set(clean);\n}\n\n/**\n * DES round function.\n * XORs src[0..3] with TP(SBOX(E(src[4..7]))).\n */\nfunction roundFunction(src: Uint8Array, index: number): void {\n  for (let i = 0; i < 8; i++) {\n    tmp2[i] = src[index + i];\n  }\n\n  expansion(tmp2, 0);\n  substitutionBox(tmp2, 0);\n  transposition(tmp2, 0);\n\n  src[index + 0] ^= tmp2[4];\n  src[index + 1] ^= tmp2[5];\n  src[index + 2] ^= tmp2[6];\n  src[index + 3] ^= tmp2[7];\n}\n\n/**\n * DEcrypt a block\n */\nfunction decryptBlock(src: Uint8Array, index: number): void {\n  initialPermutation(src, index);\n  roundFunction(src, index);\n  finalPermutation(src, index);\n}\n\n/**\n * Decode the whole file\n */\nfunction decodeFull(\n  src: Uint8Array,\n  length: number,\n  entryLength: number\n): void {\n  // compute number of digits of the entry length\n  const digits = entryLength.toString().length;\n\n  // choose size of gap between two encrypted blocks\n  // digits:  0  1  2  3  4  5  6  7  8  9 ...\n  //  cycle:  1  1  1  4  5 14 15 22 23 24 ...\n  const cycle =\n    digits < 3\n      ? 1\n      : digits < 5\n      ? digits + 1\n      : digits < 7\n      ? digits + 9\n      : digits + 15;\n\n  const nblocks = length >> 3;\n\n  // first 20 blocks are all des-encrypted\n  for (let i = 0; i < 20 && i < nblocks; ++i) {\n    decryptBlock(src, i * 8);\n  }\n\n  for (let i = 20, j = -1; i < nblocks; ++i) {\n    // decrypt block\n    if (i % cycle === 0) {\n      decryptBlock(src, i * 8);\n      continue;\n    }\n\n    // de-shuffle block\n    if (++j && j % 7 === 0) {\n      shuffleDec(src, i * 8);\n    }\n  }\n}\n\n/**\n * Decode only the header\n */\nfunction decodeHeader(src: Uint8Array, length: number): void {\n  const count = length >> 3;\n\n  // first 20 blocks are all des-encrypted\n  for (let i = 0; i < 20 && i < count; ++i) {\n    decryptBlock(src, i * 8);\n  }\n\n  // the rest is plaintext, done.\n}\n\n/**\n * Shuffle decode\n */\nfunction shuffleDec(src: Uint8Array, index: number) {\n  tmp[0] = src[index + 3];\n  tmp[1] = src[index + 4];\n  tmp[2] = src[index + 6];\n  tmp[3] = src[index + 0];\n  tmp[4] = src[index + 1];\n  tmp[5] = src[index + 2];\n  tmp[6] = src[index + 5];\n  tmp[7] = shuffleDecTable[src[index + 7]];\n\n  src.set(tmp, index);\n  tmp.set(clean);\n}\n\n/**\n * GRF substitution table\n */\nconst shuffleDecTable = (() => {\n  // prettier-ignore\n  const list = new Uint8Array([\n    0x00, 0x2b, 0x6c, 0x80, 0x01, 0x68, 0x48,\n    0x77, 0x60, 0xff, 0xb9, 0xc0, 0xfe, 0xeb\n  ]);\n\n  const out = new Uint8Array(Array.from({length: 256}, (_, k) => k));\n  const count = list.length;\n\n  for (let i = 0; i < count; i += 2) {\n    out[list[i + 0]] = list[i + 1];\n    out[list[i + 1]] = list[i + 0];\n  }\n\n  return out;\n})();\n","/**\n * Korean encoding decoder module\n *\n * Uses iconv-lite in Node.js for proper CP949 support.\n * Falls back to TextDecoder in browser (with limitations for CP949 extended chars).\n */\n\n// Try to import iconv-lite (available in Node.js)\nlet iconv: typeof import('iconv-lite') | null = null;\n\n// Dynamic import for Node.js environment\ntry {\n  // Use require for synchronous loading in Node.js\n  // This will fail in browser environments\n  if (typeof process !== 'undefined' && process.versions?.node) {\n    // eslint-disable-next-line @typescript-eslint/no-var-requires\n    iconv = require('iconv-lite');\n  }\n} catch {\n  // iconv-lite not available (browser environment)\n  iconv = null;\n}\n\n/**\n * Check if we're in a Node.js environment with iconv-lite available\n */\nexport function hasIconvLite(): boolean {\n  return iconv !== null;\n}\n\n/**\n * Count C1 control characters (U+0080-U+009F) in a string.\n * These usually indicate incorrectly decoded Korean bytes.\n * When EUC-KR decoder encounters CP949-extended bytes (0x80-0x9F range),\n * they get decoded as C1 control characters instead of Korean characters.\n */\nexport function countC1ControlChars(str: string): number {\n  let count = 0;\n  for (const char of str) {\n    const code = char.charCodeAt(0);\n    if (code >= 0x80 && code <= 0x9F) {\n      count++;\n    }\n  }\n  return count;\n}\n\n/**\n * Count replacement characters (U+FFFD) in a string\n */\nexport function countReplacementChars(str: string): number {\n  let count = 0;\n  for (const char of str) {\n    if (char === '\\uFFFD') count++;\n  }\n  return count;\n}\n\n/**\n * Count total \"bad\" characters (replacement + C1 control)\n */\nexport function countBadChars(str: string): number {\n  return countReplacementChars(str) + countC1ControlChars(str);\n}\n\n/**\n * Decode bytes to string using the specified encoding.\n *\n * For Korean encodings (cp949, euc-kr), uses iconv-lite in Node.js\n * for proper CP949 extended character support.\n *\n * @param bytes - The bytes to decode\n * @param encoding - The encoding to use ('utf-8', 'euc-kr', 'cp949', 'latin1')\n * @returns The decoded string\n */\nexport function decodeBytes(bytes: Uint8Array, encoding: string): string {\n  // Normalize encoding name\n  const enc = encoding.toLowerCase();\n\n  // For Korean encodings, prefer iconv-lite in Node.js\n  // iconv-lite properly handles CP949 extended range (0x81-0xFE first byte)\n  // which TextDecoder('euc-kr') doesn't fully support\n  if ((enc === 'cp949' || enc === 'euc-kr') && iconv) {\n    try {\n      // Always use 'cp949' with iconv-lite as it's a superset of euc-kr\n      // This properly handles the extended range that causes C1 control chars\n      const buffer = Buffer.from(bytes);\n      return iconv.decode(buffer, 'cp949');\n    } catch {\n      // Fall through to TextDecoder\n    }\n  }\n\n  // Use TextDecoder for other encodings or as fallback\n  try {\n    // Map cp949 to euc-kr for TextDecoder (best effort, not perfect)\n    const textDecoderEncoding = enc === 'cp949' ? 'euc-kr' : enc;\n    const decoder = new TextDecoder(textDecoderEncoding, { fatal: false });\n    return decoder.decode(bytes);\n  } catch {\n    // Ultimate fallback: decode as latin1 (preserves all byte values)\n    return Array.from(bytes).map(b => String.fromCharCode(b)).join('');\n  }\n}\n\n/**\n * Try to decode bytes and check quality of the result\n */\nexport function tryDecodeWithQuality(\n  bytes: Uint8Array,\n  encoding: string\n): { text: string; badChars: number; c1Chars: number; replacementChars: number } {\n  const text = decodeBytes(bytes, encoding);\n  const c1Chars = countC1ControlChars(text);\n  const replacementChars = countReplacementChars(text);\n  const badChars = c1Chars + replacementChars;\n\n  return { text, badChars, c1Chars, replacementChars };\n}\n\n// ============================================================================\n// Mojibake Detection and Fixing\n// ============================================================================\n\n/**\n * Common mojibake patterns that indicate CP949 was misread as Windows-1252.\n * These are high-frequency Korean syllable byte sequences that produce\n * recognizable Latin character patterns when misinterpreted.\n */\nconst MOJIBAKE_PATTERNS = [\n  /[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞß][¡-þ]/,  // Common Korean lead bytes as Latin\n  /À¯/,  // 유 (very common)\n  /Àú/,  // 저\n  /ÀÎ/,  // 인\n  /Å¸/,  // 터/타\n  /Æä/,  // 페\n  /ÀÌ/,  // 이\n  /½º/,  // 스\n  /¾Æ/,  // 아\n  /¸ð/,  // 모\n  /¸®/,  // 리\n  /¿¡/,  // 에\n  /Áö/,  // 지\n  /µ¥/,  // 데\n  /ÅØ/,  // 텍\n  /½ºÆ®/,  // 스트\n  /¸ÁÅä/,  // 망토\n];\n\n/**\n * Check if a string looks like mojibake (CP949 bytes misread as Windows-1252).\n *\n * Mojibake occurs when:\n * 1. Korean text is encoded as CP949 bytes\n * 2. Those bytes are incorrectly decoded as Windows-1252/Latin-1\n *\n * Example: \"유저인터페이스\" → \"À¯ÀúÀÎÅÍÆäÀÌ½º\"\n *\n * @param str - The string to check\n * @returns true if the string appears to be mojibake\n */\nexport function isMojibake(str: string): boolean {\n  // Quick checks\n  if (!str || str.length === 0) return false;\n\n  // If string contains Korean characters, it's not mojibake\n  if (/[\\uAC00-\\uD7AF]/.test(str)) return false;\n\n  // Check for common mojibake patterns\n  for (const pattern of MOJIBAKE_PATTERNS) {\n    if (pattern.test(str)) return true;\n  }\n\n  // Check for high concentration of Latin Extended characters (0x80-0xFF)\n  // which are common in mojibake but rare in normal text\n  let highLatinCount = 0;\n  for (const char of str) {\n    const code = char.charCodeAt(0);\n    if (code >= 0x80 && code <= 0xFF) {\n      highLatinCount++;\n    }\n  }\n\n  // If more than 30% of characters are in the Latin Extended range,\n  // it's likely mojibake\n  const ratio = highLatinCount / str.length;\n  return ratio > 0.3;\n}\n\n/**\n * Fix mojibake by re-encoding as Windows-1252 and decoding as CP949.\n *\n * This reverses the common encoding error where CP949 bytes were\n * incorrectly interpreted as Windows-1252.\n *\n * Example: \"À¯ÀúÀÎÅÍÆäÀÌ½º\" → \"유저인터페이스\"\n *\n * @param garbled - The mojibake string to fix\n * @returns The corrected Korean string, or the original if unfixable\n */\nexport function fixMojibake(garbled: string): string {\n  if (!iconv) {\n    // Without iconv-lite, we can't fix mojibake\n    return garbled;\n  }\n\n  try {\n    // Encode the garbled string back to Windows-1252 bytes\n    const bytes = iconv.encode(garbled, 'windows-1252');\n    // Decode those bytes as CP949 to get the original Korean\n    const fixed = iconv.decode(bytes, 'cp949');\n\n    // Verify the fix worked by checking if:\n    // 1. The result contains Korean characters (Hangul Syllables block)\n    // 2. The result has fewer or equal bad chars\n    const hasKorean = /[\\uAC00-\\uD7AF]/.test(fixed);\n    const fixedBadChars = countBadChars(fixed);\n    const garbledBadChars = countBadChars(garbled);\n\n    if (hasKorean && fixedBadChars <= garbledBadChars) {\n      return fixed;\n    }\n\n    return garbled;\n  } catch {\n    return garbled;\n  }\n}\n\n/**\n * Convert Korean text to mojibake (for testing purposes).\n *\n * This simulates the encoding error where Korean text is encoded as CP949\n * but decoded as Windows-1252.\n *\n * Example: \"유저인터페이스\" → \"À¯ÀúÀÎÅÍÆäÀÌ½º\"\n *\n * @param korean - The Korean string to garble\n * @returns The mojibake string\n */\nexport function toMojibake(korean: string): string {\n  if (!iconv) {\n    return korean;\n  }\n\n  try {\n    const bytes = iconv.encode(korean, 'cp949');\n    return iconv.decode(bytes, 'windows-1252');\n  } catch {\n    return korean;\n  }\n}\n\n/**\n * Normalize a filename by detecting and fixing encoding issues.\n *\n * This function:\n * 1. Checks if the filename is mojibake and fixes it\n * 2. Returns the normalized filename\n *\n * @param filename - The filename to normalize\n * @returns The normalized filename\n */\nexport function normalizeFilename(filename: string): string {\n  if (isMojibake(filename)) {\n    return fixMojibake(filename);\n  }\n  return filename;\n}\n\n/**\n * Normalize a path by fixing mojibake in each segment.\n *\n * @param filepath - The full path to normalize\n * @returns The normalized path\n */\nexport function normalizePath(filepath: string): string {\n  // Split by both forward and back slashes\n  const segments = filepath.split(/[\\\\/]/);\n  const normalizedSegments = segments.map(seg => normalizeFilename(seg));\n\n  // Preserve original separator style\n  const separator = filepath.includes('\\\\') ? '\\\\' : '/';\n  return normalizedSegments.join(separator);\n}\n\n// ============================================================================\n// Encoding Detection\n// ============================================================================\n\n/**\n * Detect the best encoding for Korean GRF files by analyzing byte patterns\n * and comparing decoded results.\n *\n * This function:\n * 1. Checks if bytes contain non-ASCII characters\n * 2. Tries UTF-8 and CP949 decoding\n * 3. Compares quality (bad chars, C1 control chars)\n * 4. Returns the encoding with best quality\n */\nexport function detectBestKoreanEncoding(\n  sampleBytes: Uint8Array[],\n  threshold: number = 0.01\n): 'utf-8' | 'cp949' {\n  if (sampleBytes.length === 0) return 'utf-8';\n\n  let utf8BadTotal = 0;\n  let cp949BadTotal = 0;\n  let totalBytes = 0;\n  let samplesWithHighBytes = 0;\n\n  for (const bytes of sampleBytes) {\n    // Check if this sample has non-ASCII bytes\n    const hasHighBytes = bytes.some(b => b > 0x7F);\n    if (!hasHighBytes) continue;\n\n    samplesWithHighBytes++;\n    totalBytes += bytes.length;\n\n    const utf8Result = tryDecodeWithQuality(bytes, 'utf-8');\n    const cp949Result = tryDecodeWithQuality(bytes, 'cp949');\n\n    utf8BadTotal += utf8Result.badChars;\n    cp949BadTotal += cp949Result.badChars;\n  }\n\n  // If no high bytes found, it's pure ASCII - use UTF-8\n  if (samplesWithHighBytes === 0) {\n    return 'utf-8';\n  }\n\n  const utf8BadRatio = totalBytes > 0 ? utf8BadTotal / totalBytes : 0;\n  const cp949BadRatio = totalBytes > 0 ? cp949BadTotal / totalBytes : 0;\n\n  // If UTF-8 looks perfect, use it\n  if (utf8BadRatio < threshold) {\n    return 'utf-8';\n  }\n\n  // If CP949 produces fewer bad chars, use it\n  if (cp949BadRatio < utf8BadRatio) {\n    return 'cp949';\n  }\n\n  // Default to UTF-8\n  return 'utf-8';\n}\n","import jDataview from 'jdataview';\nimport {GrfBase, GrfOptions} from './grf-base';\n\n/**\n * Using this Browser, we work from a File or Blob object.\n * We are use the FileReader API to read only some part of the file to avoid\n * loading 2 gigas into memory\n */\nexport class GrfBrowser extends GrfBase<File | Blob> {\n  constructor(file: File | Blob, options?: GrfOptions) {\n    super(file, options);\n  }\n  public async getStreamBuffer(\n    buffer: File | Blob,\n    offset: number,\n    length: number\n  ): Promise<Uint8Array> {\n    return new Promise((resolve, reject) => {\n      const reader = new FileReader();\n      reader.onerror = reject;\n      reader.onload = () =>\n        resolve(new Uint8Array(reader.result as ArrayBuffer));\n      reader.readAsArrayBuffer(buffer.slice(offset, offset + length));\n    });\n  }\n}\n","// src/grf-node.ts\nimport { fstatSync, read as readCallback } from 'fs';\nimport { promisify } from 'util';\nimport { GrfBase, GrfOptions } from './grf-base';\nimport { bufferPool } from './buffer-pool';\n\nconst readAsync = promisify(readCallback);\n\n/** Options for GrfNode */\nexport interface GrfNodeOptions extends GrfOptions {\n  /** Use buffer pool for better performance (default: true) */\n  useBufferPool?: boolean;\n}\n\nexport class GrfNode extends GrfBase<number> {\n  private useBufferPool: boolean;\n\n  constructor(fd: number, options?: GrfNodeOptions) {\n    super(fd, options);\n\n    this.useBufferPool = options?.useBufferPool ?? true;\n\n    // Na nossa API, apenas FDs para arquivos regulares são válidos.\n    // fstatSync lança erro se o descritor não existir ou não for arquivo.\n    try {\n      const stat = fstatSync(fd);\n      if (!stat.isFile()) {\n        throw new Error('GRFNode: file descriptor must point to a regular file');\n      }\n    } catch {\n      // Converte em mensagem clara para o usuário\n      throw new Error('GRFNode: invalid file descriptor');\n    }\n  }\n\n  public async getStreamBuffer(\n    fd: number,\n    offset: number,\n    length: number\n  ): Promise<Uint8Array> {\n    // Use buffer pool for better performance\n    const buffer = this.useBufferPool\n      ? bufferPool.acquire(length)\n      : Buffer.allocUnsafe(length);\n\n    const { bytesRead } = await readAsync(fd, buffer, 0, length, offset);\n\n    if (bytesRead !== length) {\n      // Release buffer back to pool if read failed\n      if (this.useBufferPool) {\n        bufferPool.release(buffer);\n      }\n      // ERRO TYPE: GRFNode: unexpected EOF\n      throw new Error('Not a GRF file (invalid signature)');\n    }\n\n    return buffer;\n  }\n}\n","/**\n * Simple buffer pool for reducing GC pressure\n * Pools buffers of common sizes for reuse\n */\n\ninterface PoolEntry {\n  buffer: Buffer;\n  inUse: boolean;\n}\n\nclass BufferPool {\n  private pools = new Map<number, PoolEntry[]>();\n  private maxPoolSize = 10;\n\n  // Common buffer sizes to pool (in bytes)\n  private readonly poolSizes = [\n    1024,      // 1KB\n    4096,      // 4KB\n    8192,      // 8KB\n    16384,     // 16KB\n    32768,     // 32KB\n    65536,     // 64KB\n    131072,    // 128KB\n    262144,    // 256KB\n  ];\n\n  constructor() {\n    // Initialize pools for common sizes\n    for (const size of this.poolSizes) {\n      this.pools.set(size, []);\n    }\n  }\n\n  /**\n   * Get appropriate pool size for requested length\n   */\n  private getPoolSize(length: number): number | null {\n    for (const size of this.poolSizes) {\n      if (length <= size) {\n        return size;\n      }\n    }\n    return null; // Too large, don't pool\n  }\n\n  /**\n   * Acquire a buffer from the pool or create new one\n   */\n  acquire(length: number): Buffer {\n    const poolSize = this.getPoolSize(length);\n\n    // Don't pool large buffers\n    if (poolSize === null) {\n      return Buffer.allocUnsafe(length);\n    }\n\n    const pool = this.pools.get(poolSize);\n\n    if (pool) {\n      // Try to find available buffer\n      const available = pool.find(entry => !entry.inUse);\n\n      if (available) {\n        available.inUse = true;\n        return available.buffer.subarray(0, length);\n      }\n\n      // Pool is full or all in use, create new if pool not maxed\n      if (pool.length < this.maxPoolSize) {\n        const buffer = Buffer.allocUnsafe(poolSize);\n        pool.push({ buffer, inUse: true });\n        return buffer.subarray(0, length);\n      }\n    }\n\n    // Fallback: create non-pooled buffer\n    return Buffer.allocUnsafe(length);\n  }\n\n  /**\n   * Release a buffer back to the pool\n   */\n  release(buffer: Buffer): void {\n    const actualSize = buffer.buffer.byteLength;\n    const pool = this.pools.get(actualSize);\n\n    if (pool) {\n      const entry = pool.find(e => e.buffer === buffer || e.buffer.buffer === buffer.buffer);\n      if (entry) {\n        entry.inUse = false;\n      }\n    }\n  }\n\n  /**\n   * Clear all pools\n   */\n  clear(): void {\n    for (const pool of this.pools.values()) {\n      pool.length = 0;\n    }\n  }\n\n  /**\n   * Get pool statistics\n   */\n  stats(): { size: number; total: number; inUse: number }[] {\n    const stats: { size: number; total: number; inUse: number }[] = [];\n\n    for (const [size, pool] of this.pools.entries()) {\n      stats.push({\n        size,\n        total: pool.length,\n        inUse: pool.filter(e => e.inUse).length\n      });\n    }\n\n    return stats;\n  }\n}\n\n// Export singleton instance\nexport const bufferPool = new BufferPool();\n"],"mappings":"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","names":["index_exports","__export","GRF_ERROR_CODES","GrfBrowser","GrfError","GrfNode","bufferPool","countBadChars","countC1ControlChars","countReplacementChars","fixMojibake","hasIconvLite","isMojibake","normalizePath","normalizeFilename","toMojibake","__toCommonJS","import_pako","import_jdataview","mask","tmp","tmp2","clean","initialPermutationTable","finalPermutationTable","transpositionTable","substitutionBoxTable","initialPermutation","src","index","i","j","finalPermutation","transposition","expansion","substitutionBox","roundFunction","decryptBlock","decodeFull","length","entryLength","digits","cycle","nblocks","shuffleDec","decodeHeader","count","shuffleDecTable","list","out","_","k","iconv","hasIconvLite","countC1ControlChars","str","count","char","code","countReplacementChars","countBadChars","decodeBytes","bytes","encoding","enc","buffer","textDecoderEncoding","b","tryDecodeWithQuality","text","c1Chars","replacementChars","badChars","MOJIBAKE_PATTERNS","isMojibake","pattern","highLatinCount","fixMojibake","garbled","fixed","hasKorean","fixedBadChars","garbledBadChars","toMojibake","korean","normalizeFilename","filename","normalizePath","filepath","normalizedSegments","seg","separator","detectBestKoreanEncoding","sampleBytes","threshold","utf8BadTotal","cp949BadTotal","totalBytes","samplesWithHighBytes","utf8Result","cp949Result","utf8BadRatio","cp949BadRatio","GRF_ERROR_CODES","GrfError","code","message","context","FILELIST_TYPE_FILE","FILELIST_TYPE_ENCRYPT_MIXED","FILELIST_TYPE_ENCRYPT_HEADER","HEADER_SIGNATURE","HEADER_SIZE","FILE_TABLE_SIZE","DEFAULT_MAX_FILE_UNCOMPRESSED_BYTES","DEFAULT_MAX_ENTRIES","DEFAULT_AUTO_DETECT_THRESHOLD","normalizePath","path","getExtension","lastDot","decodeFilenameBytes","bytes","encoding","decodeBytes","GrfBase","fd","options","offset","length","buffer","jDataview","reader","signature","afterKey","reservedFiles","low","high","tableSkip","compressedSize","realSize","compressed","data","pako","error","detectedEncoding","sampleBytes","samplePos","sampleCount","entryDataSize","i","endPos","detectBestKoreanEncoding","p","rawBytes","filename","countBadChars","lengthAligned","type","entry","normalizedKey","existingNorm","ext","existingExt","decodeFull","decodeHeader","oldest","cached","index","resolved","result","query","normalizedQuery","candidates","contains","endsWith","regex","limit","results","extLower","normalizedFilename","normalizedContains","normalizedEndsWith","GrfBrowser","GrfBase","file","options","buffer","offset","length","resolve","reject","reader","import_fs","import_util","BufferPool","size","length","poolSize","pool","available","entry","buffer","actualSize","e","stats","bufferPool","readAsync","readCallback","GrfNode","GrfBase","fd","options","offset","length","buffer","bufferPool","bytesRead"]}