添加 readArray() 辅助函数,对四处顶层数组键 (ObjectList, ImageFileList,
LayerList, GroupList) 显式校验,缺失或非数组时即刻抛 SourceError,
不再用 ?? [] 蒙混。遵循 CLAUDE.md 第二准则:数据缺失/非法一律显式暴露。
新增四个测试用例分别验证各键缺失时的错误行为。删除测试文件中未使用的
mkdirSync 导入。条目级缺省值 (entry.Event ?? {}, ObjectList ?? []) 保留,
因为单个对象/层没有事件/对象是合理的合法缺省。
真实数据护栏数字不变: 991 对象 / 55 界面 / 80 组 / 439 图 / 139 多帧图。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
67 lines
2.1 KiB
JavaScript
67 lines
2.1 KiB
JavaScript
import { readFileSync, existsSync } from 'node:fs';
|
||
import { join } from 'node:path';
|
||
|
||
/** 源数据非法时抛出。第二准则:缺失/非法一律显式暴露,不兜底。 */
|
||
export class SourceError extends Error {
|
||
constructor(message) { super(message); this.name = 'SourceError'; }
|
||
}
|
||
|
||
function readJson(dir, name) {
|
||
const p = join(dir, name);
|
||
if (!existsSync(p)) throw new SourceError(`缺少源数据文件: ${p}`);
|
||
try {
|
||
return JSON.parse(readFileSync(p, 'utf8'));
|
||
} catch (e) {
|
||
throw new SourceError(`源数据 JSON 解析失败: ${p} — ${e.message}`);
|
||
}
|
||
}
|
||
|
||
function readArray(dir, fileName, key) {
|
||
const json = readJson(dir, fileName);
|
||
const arr = json[key];
|
||
if (!Array.isArray(arr)) {
|
||
throw new SourceError(
|
||
`源数据 ${fileName} 缺少顶层数组 "${key}"(实际为 ${typeof arr})`,
|
||
);
|
||
}
|
||
return arr;
|
||
}
|
||
|
||
/**
|
||
* 读取并校验 gameabc 导出数据。
|
||
* 源数组用 `{}` 占位空槽(下标即 ID),须跳过。
|
||
*/
|
||
export function loadSource(outputDir) {
|
||
const objects = new Map();
|
||
for (const entry of readArray(outputDir, 'gameabc_Object.json', 'ObjectList')) {
|
||
if (!entry || !entry.Property) continue;
|
||
const p = entry.Property;
|
||
objects.set(p.ObjectID, { ...p, events: Object.keys(entry.Event ?? {}) });
|
||
}
|
||
|
||
const images = new Map();
|
||
for (const i of readArray(outputDir, 'gameabc_Image.json', 'ImageFileList')) {
|
||
if (!i || i.id === undefined) continue;
|
||
if (i.w * i.h !== i.frame_all) {
|
||
throw new SourceError(
|
||
`图 ${i.bmp} 的网格与帧数不符: ${i.w}x${i.h}=${i.w * i.h} != frame_all=${i.frame_all}`,
|
||
);
|
||
}
|
||
images.set(i.id, i);
|
||
}
|
||
|
||
const layers = readArray(outputDir, 'gameabc_Layer.json', 'LayerList')
|
||
.filter((l) => l && l.Property)
|
||
.map((l) => ({
|
||
layerId: l.Property.LayerID,
|
||
name: l.Property.LayerName,
|
||
objectIds: l.ObjectList ?? [],
|
||
}));
|
||
|
||
const groups = readArray(outputDir, 'gameabc_GroupList.json', 'GroupList')
|
||
.filter((g) => g && g.GroupID !== undefined)
|
||
.map((g) => ({ groupId: g.GroupID, objectIds: g.ObjectList ?? [] }));
|
||
|
||
return { objects, images, layers, groups };
|
||
}
|