From c39676befac19ed75fb57d6bc9d984531c7c32a3 Mon Sep 17 00:00:00 2001 From: Chun-Chi Hung Date: Thu, 6 Aug 2026 21:41:13 +0800 Subject: [PATCH 1/3] [Tool] Add a DB spliter. --- split_objectdb.js | 207 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 split_objectdb.js diff --git a/split_objectdb.js b/split_objectdb.js new file mode 100644 index 00000000000..a6617360d5b --- /dev/null +++ b/split_objectdb.js @@ -0,0 +1,207 @@ +#!/usr/bin/env node +/** + * split_objectdb.mjs + * ------------------ + * 將巨大的 Lua ObjectDB 檔案依照 object ID 範圍分割成多個檔案。 + * + * 規則: + * - Range 大小優先嘗試 10000,超過 700 個 object 縮小至 1000,還超過縮小至 100 + * - 最後一段不足 20 個 object 時,單獨輸出(允許例外) + * - 輸出檔案內 object 按 ID 由小到大排序 + * - 輸出格式與原始 Lua 格式相同 + * - 檔名使用實際分割時採用的 range 區間,例如 ObjectDB_00000_09999.lua + * + * 用法: + * node split_objectdb.mjs [output_dir] + * + * 範例: + * node split_objectdb.mjs ObjectDB.lua ./output/ + */ + +import fs from "fs"; +import path from "path"; + +// ── 可調整的參數 ────────────────────────────────────────── +const MIN_OBJECTS = 20; // 每個檔案最少 object 數(末尾允許例外) +const MAX_OBJECTS = 700; // 每個檔案最多 object 數 +const RANGE_SIZES = [10000, 1000, 100]; // 嘗試順序:由大到小 +// ───────────────────────────────────────────────────────── + + +/** + * 解析 Lua ObjectDB 文字,回傳 Map(ID → 原始 block) + * 使用括號追蹤法精確抓取每個 entry 的完整內容。 + */ +function parseLuaObjectDB(text) { + const objects = new Map(); + + const pairsMatch = text.match(/for\s+\w+\s*,\s*\w+\s+in\s+pairs\s*\(\s*\{/); + if (!pairsMatch) { + throw new Error("找不到 'for ... in pairs({' 結構,請確認輸入檔案格式。"); + } + + const start = pairsMatch.index + pairsMatch[0].length; + + // 找到對應的結尾 } 位置 + let depth = 1; + let outerEnd = -1; + for (let i = start; i < text.length && depth > 0; i++) { + if (text[i] === "{") depth++; + else if (text[i] === "}") { + depth--; + if (depth === 0) { outerEnd = i; break; } + } + } + + if (outerEnd === -1) { + throw new Error("無法找到 pairs({...}) 的結尾括號,檔案可能不完整。"); + } + + const inner = text.slice(start, outerEnd); + + // 逐一解析 [ID] = { ... } + const idPattern = /\s*\[(\d+)\]\s*=\s*\{/g; + let m; + + while ((m = idPattern.exec(inner)) !== null) { + const objId = parseInt(m[1], 10); + const braceStart = m.index + m[0].length - 1; // 指向 '{' + + let d = 1; + let j = braceStart + 1; + while (j < inner.length && d > 0) { + if (inner[j] === "{") d++; + else if (inner[j] === "}") d--; + j++; + } + + // 取出 block,移除尾端多餘的逗號與空白 + const rawBlock = inner.slice(m.index, j).trimEnd().replace(/,\s*$/, ""); + objects.set(objId, rawBlock); + + idPattern.lastIndex = j; + } + + return objects; +} + + +/** + * 將排序後的 ID 陣列依照範圍規則分成多個 chunk。 + * 回傳 Array<{ ids: number[], usedRange: number, rangeStart: number, rangeEnd: number }> + */ +function computeChunks(sortedIds) { + const chunks = []; + let remaining = [...sortedIds]; + + while (remaining.length > 0) { + const minId = remaining[0]; + let found = false; + + // 嘗試各 range 大小(由大到小) + for (const r of RANGE_SIZES) { + const rangeStart = Math.floor(minId / r) * r; + const rangeEnd = rangeStart + r - 1; + const inRange = remaining.filter(x => x >= rangeStart && x <= rangeEnd); + + if (inRange.length <= MAX_OBJECTS) { + chunks.push({ ids: inRange, usedRange: r, rangeStart, rangeEnd }); + const inRangeSet = new Set(inRange); + remaining = remaining.filter(x => !inRangeSet.has(x)); + found = true; + break; + } + } + + if (!found) { + // 理論上不會發生(range=100 時最多 100 個連續 ID,不可能超過 700) + // 保險起見仍保留,強制每 MAX_OBJECTS 個切一刀 + const r = RANGE_SIZES[RANGE_SIZES.length - 1]; + const rangeStart = Math.floor(minId / r) * r; + const rangeEnd = rangeStart + r - 1; + const inRange = remaining.filter(x => x >= rangeStart && x <= rangeEnd); + for (let k = 0; k < inRange.length; k += MAX_OBJECTS) { + chunks.push({ ids: inRange.slice(k, k + MAX_OBJECTS), usedRange: r, rangeStart, rangeEnd }); + } + const inRangeSet = new Set(inRange); + remaining = remaining.filter(x => !inRangeSet.has(x)); + } + } + + return chunks; +} + + +/** + * 決定輸出檔名,格式:ObjectDB_XXXXX_YYYYY.lua + * 使用實際分割時採用的 range 起點與終點。 + */ +function getOutputFilename(rangeStart, rangeEnd) { + const pad = n => String(n).padStart(5, "0"); + return `ObjectDB_${pad(rangeStart)}_${pad(rangeEnd)}.lua`; +} + + +/** + * 將一組 ID 格式化成完整的 Lua 檔案字串。 + */ +function formatLuaFile(ids, objects) { + const lines = ["local ObjectDB = ObjectDB; for objectID,objectData in pairs({"]; + for (const id of [...ids].sort((a, b) => a - b)) { + const block = objects.get(id); + const indented = "\t" + block.replace(/\n/g, "\n\t"); + lines.push(indented + ","); + } + lines.push("})"); + lines.push("do ObjectDB[objectID] = objectData; end"); + return lines.join("\n") + "\n"; +} + + +/** + * 主流程 + */ +function splitObjectDB(inputPath, outputDir) { + console.log(`讀取檔案:${inputPath}`); + const text = fs.readFileSync(inputPath, "utf-8"); + + console.log("解析 Lua 結構中..."); + const objects = parseLuaObjectDB(text); + console.log(`共找到 ${objects.size} 個 object`); + + const sortedIds = [...objects.keys()].sort((a, b) => a - b); + console.log(`ID 範圍:${sortedIds[0]} ~ ${sortedIds[sortedIds.length - 1]}`); + + console.log("計算分割方案..."); + const chunks = computeChunks(sortedIds); + console.log(`將分割成 ${chunks.length} 個檔案\n`); + + fs.mkdirSync(outputDir, { recursive: true }); + + const total = chunks.length; + + for (let i = 0; i < total; i++) { + const { ids, usedRange, rangeStart, rangeEnd } = chunks[i]; + const filename = getOutputFilename(rangeStart, rangeEnd); + const outPath = path.join(outputDir, filename); + const content = formatLuaFile(ids, objects); + fs.writeFileSync(outPath, content, "utf-8"); + + const warning = ids.length < MIN_OBJECTS ? ` ⚠️ 不足 ${MIN_OBJECTS} 個(允許例外)` : ""; + const idx = String(i + 1).padStart(3, " "); + console.log(` [${idx}/${total}] ${filename} (${ids.length} objects, range=${usedRange})${warning}`); + } + + console.log(`\n✅ 完成!輸出目錄:${outputDir}`); +} + + +// ── Entry point ─────────────────────────────────────────── +const args = process.argv.slice(2); +if (args.length < 1) { + console.error("用法:node split_objectdb.mjs [output_dir]"); + console.error("範例:node split_objectdb.mjs ObjectDB.lua ./output/"); + process.exit(1); +} + +splitObjectDB(args[0], args[1] ?? "./output"); From 8ee58a3243dad2a52772dda28808bfbc8820d13b Mon Sep 17 00:00:00 2001 From: Chun-Chi Hung Date: Thu, 6 Aug 2026 21:54:04 +0800 Subject: [PATCH 2/3] [Tool] Translate Chinese to English. --- split_objectdb.js | 104 ++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 44 deletions(-) diff --git a/split_objectdb.js b/split_objectdb.js index a6617360d5b..2c8f82912e5 100644 --- a/split_objectdb.js +++ b/split_objectdb.js @@ -1,48 +1,57 @@ #!/usr/bin/env node /** - * split_objectdb.mjs + * split_objectdb.js * ------------------ - * 將巨大的 Lua ObjectDB 檔案依照 object ID 範圍分割成多個檔案。 + * Reads an ObjectDB table from a generated Lua file and writes the table entries + * to smaller Lua files grouped by aligned object ID ranges. * - * 規則: - * - Range 大小優先嘗試 10000,超過 700 個 object 縮小至 1000,還超過縮小至 100 - * - 最後一段不足 20 個 object 時,單獨輸出(允許例外) - * - 輸出檔案內 object 按 ID 由小到大排序 - * - 輸出格式與原始 Lua 格式相同 - * - 檔名使用實際分割時採用的 range 區間,例如 ObjectDB_00000_09999.lua + * Chunk selection: + * - Start with the lowest unassigned object ID. + * - Try aligned ranges of 10,000, 1,000, and 100 IDs, in that order. + * - Select the first range containing no more than 700 objects. + * - Repeat until every parsed object has been assigned to a file. + * - Warn when a generated file contains fewer than 20 objects. * - * 用法: - * node split_objectdb.mjs [output_dir] + * Each output file contains objects sorted by ID inside a standard ObjectDB + * assignment wrapper. Its filename records the selected range boundaries, for + * example ObjectDB_00000_09999.lua. * - * 範例: - * node split_objectdb.mjs ObjectDB.lua ./output/ + * The parser expects numeric entries in the form `[ID] = { ... }` inside a + * `for ... in pairs({ ... })` statement. Brace matching is character-based and + * does not distinguish table braces from braces inside Lua strings or comments. + * + * Usage: + * node split_objectdb.js [output_dir] + * + * Example: + * node split_objectdb.js ObjectDB.lua ./output/ */ import fs from "fs"; import path from "path"; -// ── 可調整的參數 ────────────────────────────────────────── -const MIN_OBJECTS = 20; // 每個檔案最少 object 數(末尾允許例外) -const MAX_OBJECTS = 700; // 每個檔案最多 object 數 -const RANGE_SIZES = [10000, 1000, 100]; // 嘗試順序:由大到小 +// ── Chunking configuration ────────────────────────────────── +const MIN_OBJECTS = 20; // Emit a warning below this count. +const MAX_OBJECTS = 700; // Prefer chunks at or below this count. +const RANGE_SIZES = [10000, 1000, 100]; // Candidate ranges, widest first. // ───────────────────────────────────────────────────────── /** - * 解析 Lua ObjectDB 文字,回傳 Map(ID → 原始 block) - * 使用括號追蹤法精確抓取每個 entry 的完整內容。 + * Extracts numeric ObjectDB entries from the first matching pairs({...}) table. + * Returns each object ID mapped to its matched `[ID] = { ... }` entry text. */ function parseLuaObjectDB(text) { const objects = new Map(); const pairsMatch = text.match(/for\s+\w+\s*,\s*\w+\s+in\s+pairs\s*\(\s*\{/); if (!pairsMatch) { - throw new Error("找不到 'for ... in pairs({' 結構,請確認輸入檔案格式。"); + throw new Error("Could not find a 'for ... in pairs({' construct. Check the input file format."); } const start = pairsMatch.index + pairsMatch[0].length; - // 找到對應的結尾 } 位置 + // Locate the end of the outer table by counting literal brace characters. let depth = 1; let outerEnd = -1; for (let i = start; i < text.length && depth > 0; i++) { @@ -54,18 +63,18 @@ function parseLuaObjectDB(text) { } if (outerEnd === -1) { - throw new Error("無法找到 pairs({...}) 的結尾括號,檔案可能不完整。"); + throw new Error("Could not find the closing brace for pairs({...}); the file may be incomplete."); } const inner = text.slice(start, outerEnd); - // 逐一解析 [ID] = { ... } + // Find top-level candidates using the numeric ObjectDB entry prefix. const idPattern = /\s*\[(\d+)\]\s*=\s*\{/g; let m; while ((m = idPattern.exec(inner)) !== null) { const objId = parseInt(m[1], 10); - const braceStart = m.index + m[0].length - 1; // 指向 '{' + const braceStart = m.index + m[0].length - 1; // Opening brace of the entry. let d = 1; let j = braceStart + 1; @@ -75,7 +84,7 @@ function parseLuaObjectDB(text) { j++; } - // 取出 block,移除尾端多餘的逗號與空白 + // Preserve the matched entry text, excluding a trailing separator. const rawBlock = inner.slice(m.index, j).trimEnd().replace(/,\s*$/, ""); objects.set(objId, rawBlock); @@ -87,8 +96,10 @@ function parseLuaObjectDB(text) { /** - * 將排序後的 ID 陣列依照範圍規則分成多個 chunk。 - * 回傳 Array<{ ids: number[], usedRange: number, rangeStart: number, rangeEnd: number }> + * Assigns sorted IDs to aligned numeric ranges without exceeding MAX_OBJECTS + * whenever one of the configured range sizes can satisfy that limit. + * + * Returns Array<{ ids, usedRange, rangeStart, rangeEnd }>. */ function computeChunks(sortedIds) { const chunks = []; @@ -98,7 +109,7 @@ function computeChunks(sortedIds) { const minId = remaining[0]; let found = false; - // 嘗試各 range 大小(由大到小) + // Use the widest candidate range that stays within the preferred limit. for (const r of RANGE_SIZES) { const rangeStart = Math.floor(minId / r) * r; const rangeEnd = rangeStart + r - 1; @@ -114,8 +125,8 @@ function computeChunks(sortedIds) { } if (!found) { - // 理論上不會發生(range=100 時最多 100 個連續 ID,不可能超過 700) - // 保險起見仍保留,強制每 MAX_OBJECTS 個切一刀 + // Defensive fallback: divide the narrowest range into MAX_OBJECTS-sized chunks. + // With unique integer IDs and the current settings, this branch is unreachable. const r = RANGE_SIZES[RANGE_SIZES.length - 1]; const rangeStart = Math.floor(minId / r) * r; const rangeEnd = rangeStart + r - 1; @@ -133,8 +144,8 @@ function computeChunks(sortedIds) { /** - * 決定輸出檔名,格式:ObjectDB_XXXXX_YYYYY.lua - * 使用實際分割時採用的 range 起點與終點。 + * Builds an ObjectDB__.lua filename from the selected range. + * Boundary values are padded to a minimum width of five digits. */ function getOutputFilename(rangeStart, rangeEnd) { const pad = n => String(n).padStart(5, "0"); @@ -143,7 +154,8 @@ function getOutputFilename(rangeStart, rangeEnd) { /** - * 將一組 ID 格式化成完整的 Lua 檔案字串。 + * Sorts the selected IDs and places their stored entry blocks inside a standalone + * ObjectDB assignment wrapper. */ function formatLuaFile(ids, objects) { const lines = ["local ObjectDB = ObjectDB; for objectID,objectData in pairs({"]; @@ -159,22 +171,23 @@ function formatLuaFile(ids, objects) { /** - * 主流程 + * Reads and parses the input, computes chunks, creates the output directory, and + * writes one Lua file per chunk while reporting progress. */ function splitObjectDB(inputPath, outputDir) { - console.log(`讀取檔案:${inputPath}`); + console.log(`Reading file: ${inputPath}`); const text = fs.readFileSync(inputPath, "utf-8"); - console.log("解析 Lua 結構中..."); + console.log("Parsing Lua structure..."); const objects = parseLuaObjectDB(text); - console.log(`共找到 ${objects.size} 個 object`); + console.log(`Found ${objects.size} object${objects.size === 1 ? "" : "s"}`); const sortedIds = [...objects.keys()].sort((a, b) => a - b); - console.log(`ID 範圍:${sortedIds[0]} ~ ${sortedIds[sortedIds.length - 1]}`); + console.log(`ID range: ${sortedIds[0]} ~ ${sortedIds[sortedIds.length - 1]}`); - console.log("計算分割方案..."); + console.log("Computing split plan..."); const chunks = computeChunks(sortedIds); - console.log(`將分割成 ${chunks.length} 個檔案\n`); + console.log(`Splitting into ${chunks.length} files\n`); fs.mkdirSync(outputDir, { recursive: true }); @@ -187,20 +200,23 @@ function splitObjectDB(inputPath, outputDir) { const content = formatLuaFile(ids, objects); fs.writeFileSync(outPath, content, "utf-8"); - const warning = ids.length < MIN_OBJECTS ? ` ⚠️ 不足 ${MIN_OBJECTS} 個(允許例外)` : ""; + const warning = ids.length < MIN_OBJECTS + ? ` ⚠️ Fewer than ${MIN_OBJECTS} objects (allowed exception)` + : ""; const idx = String(i + 1).padStart(3, " "); - console.log(` [${idx}/${total}] ${filename} (${ids.length} objects, range=${usedRange})${warning}`); + const objectLabel = `object${ids.length === 1 ? "" : "s"}`; + console.log(` [${idx}/${total}] ${filename} (${ids.length} ${objectLabel}, range=${usedRange})${warning}`); } - console.log(`\n✅ 完成!輸出目錄:${outputDir}`); + console.log(`\n✅ Done! Output directory: ${outputDir}`); } // ── Entry point ─────────────────────────────────────────── const args = process.argv.slice(2); if (args.length < 1) { - console.error("用法:node split_objectdb.mjs [output_dir]"); - console.error("範例:node split_objectdb.mjs ObjectDB.lua ./output/"); + console.error("Usage: node split_objectdb.js [output_dir]"); + console.error("Example: node split_objectdb.js ObjectDB.lua ./output/"); process.exit(1); } From 9fa580d3626298180bd0671e68f3d8bd3d61b17f Mon Sep 17 00:00:00 2001 From: Chun-Chi Hung Date: Fri, 7 Aug 2026 01:41:11 +0800 Subject: [PATCH 3/3] [Tool] Update comment. --- split_objectdb.js | 81 +++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/split_objectdb.js b/split_objectdb.js index 2c8f82912e5..da448466af6 100644 --- a/split_objectdb.js +++ b/split_objectdb.js @@ -2,23 +2,26 @@ /** * split_objectdb.js * ------------------ - * Reads an ObjectDB table from a generated Lua file and writes the table entries - * to smaller Lua files grouped by aligned object ID ranges. + * Splits the first ObjectDB-style pairs({...}) table in a Lua file into smaller + * Lua files. Only numeric entries written as `[ID] = { ... }` are collected. + * Duplicate IDs are stored in a Map, so the last parsed entry for an ID is kept. * - * Chunk selection: - * - Start with the lowest unassigned object ID. - * - Try aligned ranges of 10,000, 1,000, and 100 IDs, in that order. - * - Select the first range containing no more than 700 objects. - * - Repeat until every parsed object has been assigned to a file. - * - Warn when a generated file contains fewer than 20 objects. + * The chunking algorithm repeatedly starts at the lowest remaining ID and tests + * aligned ranges 10,000, 1,000, and 100 IDs wide. It selects the widest range + * containing at most 700 remaining objects, removes those objects, and repeats. + * Because each pass only considers remaining objects, a later wide range may + * overlap the numeric boundaries of an earlier narrow range. Each object ID is + * still written exactly once. * - * Each output file contains objects sorted by ID inside a standard ObjectDB - * assignment wrapper. Its filename records the selected range boundaries, for - * example ObjectDB_00000_09999.lua. + * Output filenames contain the selected aligned boundaries, padded to at least + * five digits, for example ObjectDB_00000_09999.lua. Entries are sorted by ID and + * written inside a newly generated ObjectDB assignment wrapper. Files containing + * fewer than 20 objects produce a warning, but are still written normally. * - * The parser expects numeric entries in the form `[ID] = { ... }` inside a - * `for ... in pairs({ ... })` statement. Brace matching is character-based and - * does not distinguish table braces from braces inside Lua strings or comments. + * The output directory is created when needed. Existing files with generated + * names are overwritten; other files already in the directory are not removed. + * Brace matching is character-based and does not distinguish structural braces + * from braces inside Lua strings or comments. * * Usage: * node split_objectdb.js [output_dir] @@ -30,16 +33,17 @@ import fs from "fs"; import path from "path"; -// ── Chunking configuration ────────────────────────────────── -const MIN_OBJECTS = 20; // Emit a warning below this count. -const MAX_OBJECTS = 700; // Prefer chunks at or below this count. -const RANGE_SIZES = [10000, 1000, 100]; // Candidate ranges, widest first. +// ── Splitting thresholds ──────────────────────────────────── +const MIN_OBJECTS = 20; // Warning threshold; does not affect splitting. +const MAX_OBJECTS = 700; // Maximum number of IDs accepted in a chunk. +const RANGE_SIZES = [10000, 1000, 100]; // Aligned range widths, widest first. // ───────────────────────────────────────────────────────── /** - * Extracts numeric ObjectDB entries from the first matching pairs({...}) table. - * Returns each object ID mapped to its matched `[ID] = { ... }` entry text. + * Finds the first `for , in pairs({ ... })` construct and extracts + * numeric `[ID] = { ... }` entries from its table. Returns a Map from ID to the + * original matched entry text; a later duplicate ID replaces an earlier one. */ function parseLuaObjectDB(text) { const objects = new Map(); @@ -51,7 +55,7 @@ function parseLuaObjectDB(text) { const start = pairsMatch.index + pairsMatch[0].length; - // Locate the end of the outer table by counting literal brace characters. + // Count literal braces from the opening `{` to find the outer table boundary. let depth = 1; let outerEnd = -1; for (let i = start; i < text.length && depth > 0; i++) { @@ -68,13 +72,13 @@ function parseLuaObjectDB(text) { const inner = text.slice(start, outerEnd); - // Find top-level candidates using the numeric ObjectDB entry prefix. + // Search for the next numeric entry prefix after the previously extracted block. const idPattern = /\s*\[(\d+)\]\s*=\s*\{/g; let m; while ((m = idPattern.exec(inner)) !== null) { const objId = parseInt(m[1], 10); - const braceStart = m.index + m[0].length - 1; // Opening brace of the entry. + const braceStart = m.index + m[0].length - 1; // Entry table's opening `{`. let d = 1; let j = braceStart + 1; @@ -84,7 +88,7 @@ function parseLuaObjectDB(text) { j++; } - // Preserve the matched entry text, excluding a trailing separator. + // Store the complete entry without a trailing comma or trailing whitespace. const rawBlock = inner.slice(m.index, j).trimEnd().replace(/,\s*$/, ""); objects.set(objId, rawBlock); @@ -96,10 +100,12 @@ function parseLuaObjectDB(text) { /** - * Assigns sorted IDs to aligned numeric ranges without exceeding MAX_OBJECTS - * whenever one of the configured range sizes can satisfy that limit. + * Greedily assigns sorted, unique IDs to aligned ranges. For the lowest remaining + * ID, chooses the widest configured range containing at most MAX_OBJECTS remaining + * IDs, removes that selection, and repeats until no IDs remain. * - * Returns Array<{ ids, usedRange, rangeStart, rangeEnd }>. + * Returned ranges can overlap earlier range boundaries, but their `ids` arrays are + * disjoint. Returns Array<{ ids, usedRange, rangeStart, rangeEnd }>. */ function computeChunks(sortedIds) { const chunks = []; @@ -109,7 +115,7 @@ function computeChunks(sortedIds) { const minId = remaining[0]; let found = false; - // Use the widest candidate range that stays within the preferred limit. + // Accept the first (widest) aligned range that satisfies the size limit. for (const r of RANGE_SIZES) { const rangeStart = Math.floor(minId / r) * r; const rangeEnd = rangeStart + r - 1; @@ -125,8 +131,9 @@ function computeChunks(sortedIds) { } if (!found) { - // Defensive fallback: divide the narrowest range into MAX_OBJECTS-sized chunks. - // With unique integer IDs and the current settings, this branch is unreachable. + // Defensive fallback: split the narrowest range into fixed-size slices. + // This is unreachable with unique integer IDs and the current thresholds, + // because a 100-ID range cannot contain more than 100 IDs. const r = RANGE_SIZES[RANGE_SIZES.length - 1]; const rangeStart = Math.floor(minId / r) * r; const rangeEnd = rangeStart + r - 1; @@ -144,8 +151,8 @@ function computeChunks(sortedIds) { /** - * Builds an ObjectDB__.lua filename from the selected range. - * Boundary values are padded to a minimum width of five digits. + * Builds ObjectDB__.lua from the selected aligned boundaries. + * Values longer than five digits are not truncated. */ function getOutputFilename(rangeStart, rangeEnd) { const pad = n => String(n).padStart(5, "0"); @@ -154,8 +161,8 @@ function getOutputFilename(rangeStart, rangeEnd) { /** - * Sorts the selected IDs and places their stored entry blocks inside a standalone - * ObjectDB assignment wrapper. + * Sorts a chunk's IDs, indents their stored entry text by one additional tab, and + * wraps them in a newly generated ObjectDB pairs assignment. */ function formatLuaFile(ids, objects) { const lines = ["local ObjectDB = ObjectDB; for objectID,objectData in pairs({"]; @@ -171,8 +178,8 @@ function formatLuaFile(ids, objects) { /** - * Reads and parses the input, computes chunks, creates the output directory, and - * writes one Lua file per chunk while reporting progress. + * Runs the complete read, parse, split, format, and write workflow. Creates the + * output directory recursively and overwrites generated filenames when present. */ function splitObjectDB(inputPath, outputDir) { console.log(`Reading file: ${inputPath}`); @@ -212,7 +219,7 @@ function splitObjectDB(inputPath, outputDir) { } -// ── Entry point ─────────────────────────────────────────── +// ── Command-line entry point ─────────────────────────────── const args = process.argv.slice(2); if (args.length < 1) { console.error("Usage: node split_objectdb.js [output_dir]");