unity-agentic-tools 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1200 -201
- package/dist/index.js +948 -137
- package/native/unity-file-tools.darwin-arm64.node +0 -0
- package/native/unity-file-tools.darwin-x64.node +0 -0
- package/native/unity-file-tools.linux-x64-gnu.node +0 -0
- package/native/unity-file-tools.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -205,7 +205,7 @@ var require_package = __commonJS((exports2, module2) => {
|
|
|
205
205
|
module2.exports = {
|
|
206
206
|
name: "unity-agentic-tools",
|
|
207
207
|
packageManager: "bun@latest",
|
|
208
|
-
version: "0.4.
|
|
208
|
+
version: "0.4.2",
|
|
209
209
|
description: "Fast, token-efficient Unity YAML parser for AI agents",
|
|
210
210
|
exports: {
|
|
211
211
|
".": {
|
|
@@ -1517,6 +1517,7 @@ function search_project(options) {
|
|
|
1517
1517
|
const files = walk_project_files(project_path, extensions);
|
|
1518
1518
|
const scanner = new UnityScanner;
|
|
1519
1519
|
const matches = [];
|
|
1520
|
+
let files_with_errors = 0;
|
|
1520
1521
|
for (const file of files) {
|
|
1521
1522
|
try {
|
|
1522
1523
|
let gameObjects;
|
|
@@ -1603,6 +1604,7 @@ function search_project(options) {
|
|
|
1603
1604
|
if (max_matches !== undefined && matches.length >= max_matches)
|
|
1604
1605
|
break;
|
|
1605
1606
|
} catch {
|
|
1607
|
+
files_with_errors++;
|
|
1606
1608
|
continue;
|
|
1607
1609
|
}
|
|
1608
1610
|
}
|
|
@@ -1611,6 +1613,7 @@ function search_project(options) {
|
|
|
1611
1613
|
project_path,
|
|
1612
1614
|
total_files_scanned: files.length,
|
|
1613
1615
|
total_matches: matches.length,
|
|
1616
|
+
files_with_errors: files_with_errors > 0 ? files_with_errors : undefined,
|
|
1614
1617
|
cursor: 0,
|
|
1615
1618
|
truncated: max_matches !== undefined && matches.length >= max_matches,
|
|
1616
1619
|
matches
|
|
@@ -2333,9 +2336,75 @@ function resolveScriptGuid(script, projectPath) {
|
|
|
2333
2336
|
}
|
|
2334
2337
|
} catch {}
|
|
2335
2338
|
}
|
|
2339
|
+
try {
|
|
2340
|
+
const assetsDir = path3.join(projectPath, "Assets");
|
|
2341
|
+
if (import_fs8.existsSync(assetsDir)) {
|
|
2342
|
+
const entries = import_fs8.readdirSync(assetsDir, { recursive: true, withFileTypes: false });
|
|
2343
|
+
for (const entry of entries) {
|
|
2344
|
+
if (!entry.endsWith(".cs"))
|
|
2345
|
+
continue;
|
|
2346
|
+
const fileName = path3.basename(entry, ".cs").toLowerCase();
|
|
2347
|
+
if (fileName === scriptNameLower || classNameOnly && fileName === classNameOnly) {
|
|
2348
|
+
const fullPath = path3.join(assetsDir, entry);
|
|
2349
|
+
const guid = extractGuidFromMeta(fullPath + ".meta");
|
|
2350
|
+
if (guid) {
|
|
2351
|
+
return { guid, path: path3.join("Assets", entry) };
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
} catch {}
|
|
2336
2357
|
}
|
|
2337
2358
|
return null;
|
|
2338
2359
|
}
|
|
2360
|
+
var ASSET_PPTR_MAP = {
|
|
2361
|
+
".inputactions": { fileID: 11400000, type: 2 },
|
|
2362
|
+
".asset": { fileID: 11400000, type: 2 },
|
|
2363
|
+
".mat": { fileID: 2100000, type: 2 },
|
|
2364
|
+
".prefab": { fileID: 100100000, type: 2 },
|
|
2365
|
+
".controller": { fileID: 9100000, type: 2 },
|
|
2366
|
+
".anim": { fileID: 7400000, type: 2 }
|
|
2367
|
+
};
|
|
2368
|
+
var ASSET_EXTENSIONS = new Set(Object.keys(ASSET_PPTR_MAP));
|
|
2369
|
+
function resolveAssetPathToPPtr(value, file_path, project_path) {
|
|
2370
|
+
if (value.startsWith("{fileID:") || value.startsWith("{"))
|
|
2371
|
+
return;
|
|
2372
|
+
const ext = path3.extname(value).toLowerCase();
|
|
2373
|
+
if (!ASSET_EXTENSIONS.has(ext))
|
|
2374
|
+
return;
|
|
2375
|
+
const resolved_project = project_path || find_unity_project_root(path3.dirname(file_path));
|
|
2376
|
+
if (!resolved_project)
|
|
2377
|
+
return null;
|
|
2378
|
+
const mapping = ASSET_PPTR_MAP[ext];
|
|
2379
|
+
let guid = null;
|
|
2380
|
+
const basename_val = path3.basename(value, ext);
|
|
2381
|
+
if (value.includes("/") || value.includes("\\")) {
|
|
2382
|
+
const abs = path3.isAbsolute(value) ? value : path3.join(resolved_project, value);
|
|
2383
|
+
guid = extractGuidFromMeta(abs + ".meta");
|
|
2384
|
+
}
|
|
2385
|
+
if (!guid) {
|
|
2386
|
+
const cache = load_guid_cache_for_file(file_path, resolved_project);
|
|
2387
|
+
if (cache) {
|
|
2388
|
+
const found = cache.find_by_name(basename_val, ext);
|
|
2389
|
+
if (found)
|
|
2390
|
+
guid = found.guid;
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
if (!guid) {
|
|
2394
|
+
const candidates = [
|
|
2395
|
+
path3.join(resolved_project, "Assets", value),
|
|
2396
|
+
path3.join(resolved_project, value)
|
|
2397
|
+
];
|
|
2398
|
+
for (const candidate of candidates) {
|
|
2399
|
+
guid = extractGuidFromMeta(candidate + ".meta");
|
|
2400
|
+
if (guid)
|
|
2401
|
+
break;
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
if (!guid)
|
|
2405
|
+
return null;
|
|
2406
|
+
return `{fileID: ${mapping.fileID}, guid: ${guid}, type: ${mapping.type}}`;
|
|
2407
|
+
}
|
|
2339
2408
|
function resolve_script_with_fields(script, project_path) {
|
|
2340
2409
|
const resolved = resolveScriptGuid(script, project_path);
|
|
2341
2410
|
if (!resolved)
|
|
@@ -2439,8 +2508,8 @@ function build_type_lookup(project_path) {
|
|
|
2439
2508
|
const assetsDir = path3.join(project_path, "Assets");
|
|
2440
2509
|
const csIndex = new Map;
|
|
2441
2510
|
try {
|
|
2442
|
-
const { readdirSync:
|
|
2443
|
-
const entries =
|
|
2511
|
+
const { readdirSync: readdirSync5 } = require("fs");
|
|
2512
|
+
const entries = readdirSync5(assetsDir, { recursive: true, withFileTypes: false });
|
|
2444
2513
|
for (const entry of entries) {
|
|
2445
2514
|
if (typeof entry === "string" && entry.endsWith(".cs")) {
|
|
2446
2515
|
const basename4 = entry.substring(entry.lastIndexOf("/") + 1).replace(/\.cs$/, "");
|
|
@@ -2892,7 +2961,9 @@ ${element_indent2}- ${value}`);
|
|
|
2892
2961
|
if (!header) {
|
|
2893
2962
|
throw new Error(`Invalid Unity YAML block header in replacement text: "${new_text.slice(0, 80)}"`);
|
|
2894
2963
|
}
|
|
2895
|
-
this._raw = new_text
|
|
2964
|
+
this._raw = new_text.endsWith(`
|
|
2965
|
+
`) ? new_text : new_text + `
|
|
2966
|
+
`;
|
|
2896
2967
|
this._header = header;
|
|
2897
2968
|
this._dirty = true;
|
|
2898
2969
|
this._format_cache.clear();
|
|
@@ -2902,6 +2973,11 @@ ${element_indent2}- ${value}`);
|
|
|
2902
2973
|
}
|
|
2903
2974
|
_check_dirty(old_raw) {
|
|
2904
2975
|
if (this._raw !== old_raw) {
|
|
2976
|
+
if (!this._raw.endsWith(`
|
|
2977
|
+
`)) {
|
|
2978
|
+
this._raw += `
|
|
2979
|
+
`;
|
|
2980
|
+
}
|
|
2905
2981
|
this._dirty = true;
|
|
2906
2982
|
return true;
|
|
2907
2983
|
}
|
|
@@ -3963,14 +4039,82 @@ function read_project_version(projectPath) {
|
|
|
3963
4039
|
}
|
|
3964
4040
|
|
|
3965
4041
|
// src/editor/create.ts
|
|
4042
|
+
function md4(data) {
|
|
4043
|
+
function F(x, y, z) {
|
|
4044
|
+
return x & y | ~x & z;
|
|
4045
|
+
}
|
|
4046
|
+
function G(x, y, z) {
|
|
4047
|
+
return x & y | x & z | y & z;
|
|
4048
|
+
}
|
|
4049
|
+
function H(x, y, z) {
|
|
4050
|
+
return x ^ y ^ z;
|
|
4051
|
+
}
|
|
4052
|
+
function rotl(x, n) {
|
|
4053
|
+
return (x << n | x >>> 32 - n) >>> 0;
|
|
4054
|
+
}
|
|
4055
|
+
const len = data.length;
|
|
4056
|
+
const bitLen = len * 8;
|
|
4057
|
+
const padded_len = len + 9 + 63 & ~63;
|
|
4058
|
+
const buf = Buffer.alloc(padded_len);
|
|
4059
|
+
data.copy(buf);
|
|
4060
|
+
buf[len] = 128;
|
|
4061
|
+
buf.writeUInt32LE(bitLen >>> 0, padded_len - 8);
|
|
4062
|
+
buf.writeUInt32LE(Math.floor(bitLen / 4294967296) >>> 0, padded_len - 4);
|
|
4063
|
+
let a0 = 1732584193;
|
|
4064
|
+
let b0 = 4023233417;
|
|
4065
|
+
let c0 = 2562383102;
|
|
4066
|
+
let d0 = 271733878;
|
|
4067
|
+
const R1_K = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
|
4068
|
+
const R1_S = [3, 7, 11, 19, 3, 7, 11, 19, 3, 7, 11, 19, 3, 7, 11, 19];
|
|
4069
|
+
const R2_K = [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15];
|
|
4070
|
+
const R2_S = [3, 5, 9, 13, 3, 5, 9, 13, 3, 5, 9, 13, 3, 5, 9, 13];
|
|
4071
|
+
const R3_K = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15];
|
|
4072
|
+
const R3_S = [3, 9, 11, 15, 3, 9, 11, 15, 3, 9, 11, 15, 3, 9, 11, 15];
|
|
4073
|
+
for (let i = 0;i < padded_len; i += 64) {
|
|
4074
|
+
const X = [];
|
|
4075
|
+
for (let j = 0;j < 16; j++)
|
|
4076
|
+
X[j] = buf.readUInt32LE(i + j * 4);
|
|
4077
|
+
let a = a0, b = b0, c = c0, d = d0;
|
|
4078
|
+
for (let j = 0;j < 16; j++) {
|
|
4079
|
+
const t = a + F(b, c, d) + X[R1_K[j]] >>> 0;
|
|
4080
|
+
a = d;
|
|
4081
|
+
d = c;
|
|
4082
|
+
c = b;
|
|
4083
|
+
b = rotl(t, R1_S[j]);
|
|
4084
|
+
}
|
|
4085
|
+
for (let j = 0;j < 16; j++) {
|
|
4086
|
+
const t = a + G(b, c, d) + X[R2_K[j]] + 1518500249 >>> 0;
|
|
4087
|
+
a = d;
|
|
4088
|
+
d = c;
|
|
4089
|
+
c = b;
|
|
4090
|
+
b = rotl(t, R2_S[j]);
|
|
4091
|
+
}
|
|
4092
|
+
for (let j = 0;j < 16; j++) {
|
|
4093
|
+
const t = a + H(b, c, d) + X[R3_K[j]] + 1859775393 >>> 0;
|
|
4094
|
+
a = d;
|
|
4095
|
+
d = c;
|
|
4096
|
+
c = b;
|
|
4097
|
+
b = rotl(t, R3_S[j]);
|
|
4098
|
+
}
|
|
4099
|
+
a0 = a0 + a >>> 0;
|
|
4100
|
+
b0 = b0 + b >>> 0;
|
|
4101
|
+
c0 = c0 + c >>> 0;
|
|
4102
|
+
d0 = d0 + d >>> 0;
|
|
4103
|
+
}
|
|
4104
|
+
const result = Buffer.alloc(16);
|
|
4105
|
+
result.writeUInt32LE(a0, 0);
|
|
4106
|
+
result.writeUInt32LE(b0, 4);
|
|
4107
|
+
result.writeUInt32LE(c0, 8);
|
|
4108
|
+
result.writeUInt32LE(d0, 12);
|
|
4109
|
+
return result;
|
|
4110
|
+
}
|
|
3966
4111
|
function compute_dll_script_file_id(namespace, class_name) {
|
|
3967
|
-
const { createHash } = require("crypto");
|
|
3968
4112
|
const full_name = namespace ? `${namespace}.${class_name}` : class_name;
|
|
3969
4113
|
const name_bytes = Buffer.from(full_name, "utf-8");
|
|
3970
4114
|
const input = Buffer.alloc(4 + name_bytes.length);
|
|
3971
4115
|
input.writeInt32LE(114, 0);
|
|
3972
4116
|
name_bytes.copy(input, 4);
|
|
3973
|
-
const hash =
|
|
4117
|
+
const hash = md4(input);
|
|
3974
4118
|
const a = hash.readInt32LE(0);
|
|
3975
4119
|
const b = hash.readInt32LE(4);
|
|
3976
4120
|
const c = hash.readInt32LE(8);
|
|
@@ -3983,13 +4127,13 @@ function get_layer_from_parent(doc, parentTransformId) {
|
|
|
3983
4127
|
const parentTransform = doc.find_by_file_id(parentTransformId);
|
|
3984
4128
|
if (!parentTransform)
|
|
3985
4129
|
return 0;
|
|
3986
|
-
const goMatch = parentTransform.raw.match(/m_GameObject
|
|
4130
|
+
const goMatch = parentTransform.raw.match(/m_GameObject:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
3987
4131
|
if (!goMatch)
|
|
3988
4132
|
return 0;
|
|
3989
4133
|
const parentGo = doc.find_by_file_id(goMatch[1]);
|
|
3990
4134
|
if (!parentGo)
|
|
3991
4135
|
return 0;
|
|
3992
|
-
const layerMatch = parentGo.raw.match(/m_Layer
|
|
4136
|
+
const layerMatch = parentGo.raw.match(/m_Layer:[ \t]*(\d+)/);
|
|
3993
4137
|
return layerMatch ? parseInt(layerMatch[1], 10) : 0;
|
|
3994
4138
|
}
|
|
3995
4139
|
function createGameObjectYAML(gameObjectId, transformId, name, parentTransformId = "0", rootOrder = 0, layer = 0) {
|
|
@@ -4126,48 +4270,320 @@ function find_game_object_in_variant(doc, name, file_path, project_path) {
|
|
|
4126
4270
|
}
|
|
4127
4271
|
return null;
|
|
4128
4272
|
}
|
|
4129
|
-
function
|
|
4130
|
-
const
|
|
4131
|
-
if (!
|
|
4132
|
-
return;
|
|
4133
|
-
const
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4273
|
+
function parsePrefabRef(refText) {
|
|
4274
|
+
const fileIdMatch = refText.match(/fileID:[ \t]*(-?\d+)/i);
|
|
4275
|
+
if (!fileIdMatch)
|
|
4276
|
+
return null;
|
|
4277
|
+
const guidMatch = refText.match(/guid:[ \t]*([a-f0-9]{32})/i);
|
|
4278
|
+
const typeMatch = refText.match(/type:[ \t]*(-?\d+)/i);
|
|
4279
|
+
return {
|
|
4280
|
+
file_id: fileIdMatch[1],
|
|
4281
|
+
guid: guidMatch ? guidMatch[1].toLowerCase() : undefined,
|
|
4282
|
+
type: typeMatch ? typeMatch[1] : undefined
|
|
4283
|
+
};
|
|
4284
|
+
}
|
|
4285
|
+
function extractPrefabSourceGuid(prefabInstanceRaw) {
|
|
4286
|
+
const sourceMatch = prefabInstanceRaw.match(/m_SourcePrefab:[ \t]*\{[^}]*guid:[ \t]*([a-f0-9]{32})/i);
|
|
4287
|
+
return sourceMatch ? sourceMatch[1].toLowerCase() : null;
|
|
4288
|
+
}
|
|
4289
|
+
function normalizePrefabRef(refText, sourceGuid) {
|
|
4290
|
+
const parsed = parsePrefabRef(refText);
|
|
4291
|
+
if (!parsed) {
|
|
4292
|
+
return { error: `Invalid reference "${refText}". Expected format: {fileID: N, guid: ..., type: T}` };
|
|
4293
|
+
}
|
|
4294
|
+
const guid = parsed.guid ?? sourceGuid;
|
|
4295
|
+
if (!guid) {
|
|
4296
|
+
return { error: `Reference "${refText}" is missing guid and source prefab guid could not be inferred.` };
|
|
4143
4297
|
}
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
raw = raw.replace(existingPattern, `$1${entry}`);
|
|
4147
|
-
piBlock.replace_raw(raw);
|
|
4298
|
+
if (!/^[a-f0-9]{32}$/i.test(guid)) {
|
|
4299
|
+
return { error: `Invalid guid in reference "${refText}". Expected 32-character hex GUID.` };
|
|
4148
4300
|
}
|
|
4301
|
+
const type = parsed.type ?? "3";
|
|
4302
|
+
if (!/^-?\d+$/.test(type)) {
|
|
4303
|
+
return { error: `Invalid type in reference "${refText}". Expected an integer type value.` };
|
|
4304
|
+
}
|
|
4305
|
+
return {
|
|
4306
|
+
ref: {
|
|
4307
|
+
file_id: parsed.file_id,
|
|
4308
|
+
guid,
|
|
4309
|
+
type
|
|
4310
|
+
}
|
|
4311
|
+
};
|
|
4149
4312
|
}
|
|
4150
|
-
function
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
const
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4313
|
+
function serializePrefabRef(ref) {
|
|
4314
|
+
return `{fileID: ${ref.file_id}, guid: ${ref.guid}, type: ${ref.type}}`;
|
|
4315
|
+
}
|
|
4316
|
+
function ensurePrefabModificationSerializedVersion(prefabInstanceBlock) {
|
|
4317
|
+
const raw = prefabInstanceBlock.raw;
|
|
4318
|
+
const updated = raw.replace(/(^([ \t]*)m_Modification:[ \t]*\n)(?!\2[ \t]+serializedVersion:[ \t]*\d+)/m, (_match, prefix, indent) => `${prefix}${indent} serializedVersion: 3
|
|
4319
|
+
`);
|
|
4320
|
+
if (updated === raw) {
|
|
4321
|
+
return false;
|
|
4322
|
+
}
|
|
4323
|
+
prefabInstanceBlock.replace_raw(updated);
|
|
4324
|
+
return true;
|
|
4325
|
+
}
|
|
4326
|
+
function addedOverrideEntryKey(entry) {
|
|
4327
|
+
return `${entry.target.file_id}|${entry.target.guid}|${entry.target.type}|${entry.insert_index}|${entry.added_object_file_id}`;
|
|
4328
|
+
}
|
|
4329
|
+
function splitPrefabArraySection(rawText, key) {
|
|
4330
|
+
const lines = rawText.split(`
|
|
4331
|
+
`);
|
|
4332
|
+
const keyPattern = new RegExp(`^([ ]*)${key}:[ ]*(.*)$`);
|
|
4333
|
+
let keyIndex = -1;
|
|
4334
|
+
let indent = "";
|
|
4335
|
+
for (let i = 0;i < lines.length; i++) {
|
|
4336
|
+
const match = lines[i].match(keyPattern);
|
|
4337
|
+
if (match) {
|
|
4338
|
+
keyIndex = i;
|
|
4339
|
+
indent = match[1];
|
|
4340
|
+
break;
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
if (keyIndex === -1) {
|
|
4344
|
+
return null;
|
|
4345
|
+
}
|
|
4346
|
+
const keyIndentLen = indent.length;
|
|
4347
|
+
let sectionEnd = keyIndex + 1;
|
|
4348
|
+
for (let i = keyIndex + 1;i < lines.length; i++) {
|
|
4349
|
+
const line = lines[i];
|
|
4350
|
+
const trimmed = line.trim();
|
|
4351
|
+
if (trimmed.length === 0) {
|
|
4352
|
+
sectionEnd = i + 1;
|
|
4353
|
+
continue;
|
|
4354
|
+
}
|
|
4355
|
+
const indentLen = (line.match(/^[ \t]*/) || [""])[0].length;
|
|
4356
|
+
if (indentLen < keyIndentLen) {
|
|
4357
|
+
break;
|
|
4358
|
+
}
|
|
4359
|
+
if (indentLen === keyIndentLen && !trimmed.startsWith("-") && /^[A-Za-z_][A-Za-z0-9_]*:/.test(trimmed)) {
|
|
4360
|
+
break;
|
|
4361
|
+
}
|
|
4362
|
+
sectionEnd = i + 1;
|
|
4363
|
+
}
|
|
4364
|
+
return {
|
|
4365
|
+
lines,
|
|
4366
|
+
key_index: keyIndex,
|
|
4367
|
+
section_end: sectionEnd,
|
|
4368
|
+
indent
|
|
4369
|
+
};
|
|
4370
|
+
}
|
|
4371
|
+
function collectAddedOverrideEntries(lines, key_index, section_end, sourceGuid) {
|
|
4372
|
+
const sectionLines = lines.slice(key_index + 1, section_end);
|
|
4373
|
+
const dedup = new Map;
|
|
4374
|
+
let i = 0;
|
|
4375
|
+
while (i < sectionLines.length) {
|
|
4376
|
+
const line = sectionLines[i];
|
|
4377
|
+
const trimmed = line.trimStart();
|
|
4378
|
+
if (!trimmed.startsWith("-")) {
|
|
4379
|
+
i++;
|
|
4380
|
+
continue;
|
|
4381
|
+
}
|
|
4382
|
+
const entryIndent = (line.match(/^[ \t]*/) || [""])[0].length;
|
|
4383
|
+
const entryLines = [line];
|
|
4384
|
+
i++;
|
|
4385
|
+
while (i < sectionLines.length) {
|
|
4386
|
+
const nextLine = sectionLines[i];
|
|
4387
|
+
const nextTrimmed = nextLine.trim();
|
|
4388
|
+
const nextIndent = (nextLine.match(/^[ \t]*/) || [""])[0].length;
|
|
4389
|
+
if (nextTrimmed.length > 0 && nextIndent === entryIndent && nextTrimmed.startsWith("-")) {
|
|
4390
|
+
break;
|
|
4391
|
+
}
|
|
4392
|
+
entryLines.push(nextLine);
|
|
4393
|
+
i++;
|
|
4394
|
+
}
|
|
4395
|
+
const entryText = entryLines.join(`
|
|
4396
|
+
`);
|
|
4397
|
+
const targetMatch = entryText.match(/targetCorrespondingSourceObject:[ \t]*(\{[^}]+\})/);
|
|
4398
|
+
const insertMatch = entryText.match(/insertIndex:[ \t]*(-?\d+)/);
|
|
4399
|
+
const addedObjectMatch = entryText.match(/addedObject:[ \t]*\{[^}]*fileID:[ \t]*(-?\d+)/);
|
|
4400
|
+
if (!targetMatch || !addedObjectMatch) {
|
|
4401
|
+
continue;
|
|
4402
|
+
}
|
|
4403
|
+
const normalizedTarget = normalizePrefabRef(targetMatch[1], sourceGuid).ref;
|
|
4404
|
+
if (!normalizedTarget) {
|
|
4405
|
+
continue;
|
|
4406
|
+
}
|
|
4407
|
+
const insertIndex = insertMatch ? Number.parseInt(insertMatch[1], 10) : -1;
|
|
4408
|
+
const entry = {
|
|
4409
|
+
target: normalizedTarget,
|
|
4410
|
+
insert_index: Number.isNaN(insertIndex) ? -1 : insertIndex,
|
|
4411
|
+
added_object_file_id: addedObjectMatch[1]
|
|
4412
|
+
};
|
|
4413
|
+
const dedupKey = addedOverrideEntryKey(entry);
|
|
4414
|
+
if (!dedup.has(dedupKey)) {
|
|
4415
|
+
dedup.set(dedupKey, entry);
|
|
4416
|
+
}
|
|
4164
4417
|
}
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4418
|
+
return [...dedup.values()];
|
|
4419
|
+
}
|
|
4420
|
+
function appendToAddedOverrideArray(doc, piId, key, targetSourceRef, newObjectId) {
|
|
4421
|
+
const piBlock = doc.find_by_file_id(piId);
|
|
4422
|
+
if (!piBlock) {
|
|
4423
|
+
return { success: false, error: `PrefabInstance with fileID ${piId} not found` };
|
|
4424
|
+
}
|
|
4425
|
+
ensurePrefabModificationSerializedVersion(piBlock);
|
|
4426
|
+
const split = splitPrefabArraySection(piBlock.raw, key);
|
|
4427
|
+
if (!split) {
|
|
4428
|
+
return { success: false, error: `${key} property not found in PrefabInstance ${piId}` };
|
|
4429
|
+
}
|
|
4430
|
+
const sourceGuid = extractPrefabSourceGuid(piBlock.raw);
|
|
4431
|
+
const normalizedTarget = normalizePrefabRef(targetSourceRef, sourceGuid);
|
|
4432
|
+
if (!normalizedTarget.ref) {
|
|
4433
|
+
return { success: false, error: normalizedTarget.error };
|
|
4434
|
+
}
|
|
4435
|
+
const candidate = {
|
|
4436
|
+
target: normalizedTarget.ref,
|
|
4437
|
+
insert_index: -1,
|
|
4438
|
+
added_object_file_id: newObjectId
|
|
4439
|
+
};
|
|
4440
|
+
const candidateKey = addedOverrideEntryKey(candidate);
|
|
4441
|
+
const existing = collectAddedOverrideEntries(split.lines, split.key_index, split.section_end, sourceGuid);
|
|
4442
|
+
if (existing.some((entry) => addedOverrideEntryKey(entry) === candidateKey)) {
|
|
4443
|
+
return { success: true, changed: false };
|
|
4444
|
+
}
|
|
4445
|
+
const entryLines = [
|
|
4446
|
+
`${split.indent}- targetCorrespondingSourceObject: ${serializePrefabRef(candidate.target)}`,
|
|
4447
|
+
`${split.indent} insertIndex: ${candidate.insert_index}`,
|
|
4448
|
+
`${split.indent} addedObject: {fileID: ${candidate.added_object_file_id}}`
|
|
4449
|
+
];
|
|
4450
|
+
const keyLine = split.lines[split.key_index].trim();
|
|
4451
|
+
let insertIndex = split.section_end;
|
|
4452
|
+
while (insertIndex > split.key_index + 1 && split.lines[insertIndex - 1].trim().length === 0) {
|
|
4453
|
+
insertIndex--;
|
|
4454
|
+
}
|
|
4455
|
+
let updatedLines;
|
|
4456
|
+
if (keyLine === `${key}: []`) {
|
|
4457
|
+
const replacement = [`${split.indent}${key}:`, ...entryLines];
|
|
4458
|
+
updatedLines = [
|
|
4459
|
+
...split.lines.slice(0, split.key_index),
|
|
4460
|
+
...replacement,
|
|
4461
|
+
...split.lines.slice(split.section_end)
|
|
4462
|
+
];
|
|
4463
|
+
} else {
|
|
4464
|
+
updatedLines = [
|
|
4465
|
+
...split.lines.slice(0, insertIndex),
|
|
4466
|
+
...entryLines,
|
|
4467
|
+
...split.lines.slice(insertIndex)
|
|
4468
|
+
];
|
|
4169
4469
|
}
|
|
4470
|
+
piBlock.replace_raw(updatedLines.join(`
|
|
4471
|
+
`));
|
|
4472
|
+
return { success: true, changed: true };
|
|
4473
|
+
}
|
|
4474
|
+
function appendToAddedGameObjects(doc, piId, targetSourceRef, newGoId) {
|
|
4475
|
+
return appendToAddedOverrideArray(doc, piId, "m_AddedGameObjects", targetSourceRef, newGoId);
|
|
4170
4476
|
}
|
|
4477
|
+
function appendToAddedComponents(doc, piId, targetSourceRef, newComponentId) {
|
|
4478
|
+
return appendToAddedOverrideArray(doc, piId, "m_AddedComponents", targetSourceRef, newComponentId);
|
|
4479
|
+
}
|
|
4480
|
+
var COMPONENT_FULL_TEMPLATES = {
|
|
4481
|
+
65: (gameObjectId) => ` m_GameObject: {fileID: ${gameObjectId}}
|
|
4482
|
+
m_Material: {fileID: 0}
|
|
4483
|
+
m_IncludeLayers:
|
|
4484
|
+
serializedVersion: 2
|
|
4485
|
+
m_Bits: 0
|
|
4486
|
+
m_ExcludeLayers:
|
|
4487
|
+
serializedVersion: 2
|
|
4488
|
+
m_Bits: 0
|
|
4489
|
+
m_LayerOverridePriority: 0
|
|
4490
|
+
m_IsTrigger: 0
|
|
4491
|
+
m_ProvidesContacts: 0
|
|
4492
|
+
m_Enabled: 1
|
|
4493
|
+
serializedVersion: 3
|
|
4494
|
+
m_Size: {x: 1, y: 1, z: 1}
|
|
4495
|
+
m_Center: {x: 0, y: 0, z: 0}`,
|
|
4496
|
+
82: (gameObjectId) => ` m_GameObject: {fileID: ${gameObjectId}}
|
|
4497
|
+
m_Enabled: 1
|
|
4498
|
+
serializedVersion: 4
|
|
4499
|
+
OutputAudioMixerGroup: {fileID: 0}
|
|
4500
|
+
m_audioClip: {fileID: 0}
|
|
4501
|
+
m_PlayOnAwake: 1
|
|
4502
|
+
m_Volume: 1
|
|
4503
|
+
m_Pitch: 1
|
|
4504
|
+
Loop: 0
|
|
4505
|
+
Mute: 0
|
|
4506
|
+
Spatialize: 0
|
|
4507
|
+
SpatializePostEffects: 0
|
|
4508
|
+
Priority: 128
|
|
4509
|
+
DopplerLevel: 1
|
|
4510
|
+
MinDistance: 1
|
|
4511
|
+
MaxDistance: 500
|
|
4512
|
+
Pan2D: 0
|
|
4513
|
+
rolloffMode: 0
|
|
4514
|
+
BypassEffects: 0
|
|
4515
|
+
BypassListenerEffects: 0
|
|
4516
|
+
BypassReverbZones: 0
|
|
4517
|
+
rolloffCustomCurve:
|
|
4518
|
+
serializedVersion: 2
|
|
4519
|
+
m_Curve:
|
|
4520
|
+
- serializedVersion: 3
|
|
4521
|
+
time: 0
|
|
4522
|
+
value: 1
|
|
4523
|
+
inSlope: 0
|
|
4524
|
+
outSlope: 0
|
|
4525
|
+
tangentMode: 0
|
|
4526
|
+
weightedMode: 0
|
|
4527
|
+
inWeight: 0.33333334
|
|
4528
|
+
outWeight: 0.33333334
|
|
4529
|
+
- serializedVersion: 3
|
|
4530
|
+
time: 1
|
|
4531
|
+
value: 0
|
|
4532
|
+
inSlope: 0
|
|
4533
|
+
outSlope: 0
|
|
4534
|
+
tangentMode: 0
|
|
4535
|
+
weightedMode: 0
|
|
4536
|
+
inWeight: 0.33333334
|
|
4537
|
+
outWeight: 0.33333334
|
|
4538
|
+
m_PreInfinity: 2
|
|
4539
|
+
m_PostInfinity: 2
|
|
4540
|
+
m_RotationOrder: 4
|
|
4541
|
+
panLevelCustomCurve:
|
|
4542
|
+
serializedVersion: 2
|
|
4543
|
+
m_Curve:
|
|
4544
|
+
- serializedVersion: 3
|
|
4545
|
+
time: 0
|
|
4546
|
+
value: 0
|
|
4547
|
+
inSlope: 0
|
|
4548
|
+
outSlope: 0
|
|
4549
|
+
tangentMode: 0
|
|
4550
|
+
weightedMode: 0
|
|
4551
|
+
inWeight: 0.33333334
|
|
4552
|
+
outWeight: 0.33333334
|
|
4553
|
+
m_PreInfinity: 2
|
|
4554
|
+
m_PostInfinity: 2
|
|
4555
|
+
m_RotationOrder: 4
|
|
4556
|
+
spreadCustomCurve:
|
|
4557
|
+
serializedVersion: 2
|
|
4558
|
+
m_Curve:
|
|
4559
|
+
- serializedVersion: 3
|
|
4560
|
+
time: 0
|
|
4561
|
+
value: 0
|
|
4562
|
+
inSlope: 0
|
|
4563
|
+
outSlope: 0
|
|
4564
|
+
tangentMode: 0
|
|
4565
|
+
weightedMode: 0
|
|
4566
|
+
inWeight: 0.33333334
|
|
4567
|
+
outWeight: 0.33333334
|
|
4568
|
+
m_PreInfinity: 2
|
|
4569
|
+
m_PostInfinity: 2
|
|
4570
|
+
m_RotationOrder: 4
|
|
4571
|
+
reverbZoneMixCustomCurve:
|
|
4572
|
+
serializedVersion: 2
|
|
4573
|
+
m_Curve:
|
|
4574
|
+
- serializedVersion: 3
|
|
4575
|
+
time: 0
|
|
4576
|
+
value: 1
|
|
4577
|
+
inSlope: 0
|
|
4578
|
+
outSlope: 0
|
|
4579
|
+
tangentMode: 0
|
|
4580
|
+
weightedMode: 0
|
|
4581
|
+
inWeight: 0.33333334
|
|
4582
|
+
outWeight: 0.33333334
|
|
4583
|
+
m_PreInfinity: 2
|
|
4584
|
+
m_PostInfinity: 2
|
|
4585
|
+
m_RotationOrder: 4`
|
|
4586
|
+
};
|
|
4171
4587
|
var COMPONENT_DEFAULTS = {
|
|
4172
4588
|
20: ` serializedVersion: 2
|
|
4173
4589
|
m_ClearFlags: 1
|
|
@@ -4294,6 +4710,17 @@ var COMPONENT_DEFAULTS = {
|
|
|
4294
4710
|
m_IgnoreParentGroups: 0`
|
|
4295
4711
|
};
|
|
4296
4712
|
function createGenericComponentYAML(componentName, classId, componentId, gameObjectId) {
|
|
4713
|
+
const fullTemplate = COMPONENT_FULL_TEMPLATES[classId];
|
|
4714
|
+
if (fullTemplate) {
|
|
4715
|
+
return `--- !u!${classId} &${componentId}
|
|
4716
|
+
${componentName}:
|
|
4717
|
+
m_ObjectHideFlags: 0
|
|
4718
|
+
m_CorrespondingSourceObject: {fileID: 0}
|
|
4719
|
+
m_PrefabInstance: {fileID: 0}
|
|
4720
|
+
m_PrefabAsset: {fileID: 0}
|
|
4721
|
+
${fullTemplate(gameObjectId)}
|
|
4722
|
+
`;
|
|
4723
|
+
}
|
|
4297
4724
|
const defaults = COMPONENT_DEFAULTS[classId] ? `
|
|
4298
4725
|
` + COMPONENT_DEFAULTS[classId] + `
|
|
4299
4726
|
` : "";
|
|
@@ -4310,11 +4737,14 @@ ${defaults}`;
|
|
|
4310
4737
|
function addComponentToGameObject(doc, gameObjectId, componentId) {
|
|
4311
4738
|
const goBlock = doc.find_by_file_id(gameObjectId);
|
|
4312
4739
|
if (!goBlock)
|
|
4313
|
-
return;
|
|
4314
|
-
|
|
4315
|
-
|
|
4740
|
+
return false;
|
|
4741
|
+
const raw = goBlock.raw;
|
|
4742
|
+
const updated = raw.replace(/(m_Component:[ \t]*\n(?:[ \t]*-[ \t]*component:[ \t]*\{fileID:[ \t]*\d+\}[ \t]*\n)*)/, `$1 - component: {fileID: ${componentId}}
|
|
4316
4743
|
`);
|
|
4317
|
-
|
|
4744
|
+
if (updated === raw)
|
|
4745
|
+
return false;
|
|
4746
|
+
goBlock.replace_raw(updated);
|
|
4747
|
+
return true;
|
|
4318
4748
|
}
|
|
4319
4749
|
function createMonoBehaviourYAML(componentId, gameObjectId, scriptGuid, fields, version, scriptFileId = 11500000, type_lookup) {
|
|
4320
4750
|
const field_yaml = fields && fields.length > 0 ? generate_field_yaml(fields, version, " ", type_lookup) : `
|
|
@@ -4386,7 +4816,7 @@ function createGameObject(options) {
|
|
|
4386
4816
|
if (parentBlock.class_id === 4) {
|
|
4387
4817
|
parentTransformIdStr = parentIdStr;
|
|
4388
4818
|
} else if (parentBlock.class_id === 1) {
|
|
4389
|
-
const compMatch = parentBlock.raw.match(/m_Component
|
|
4819
|
+
const compMatch = parentBlock.raw.match(/m_Component:[ \t]*\n[ \t]*-[ \t]*component:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
4390
4820
|
if (compMatch) {
|
|
4391
4821
|
parentTransformIdStr = compMatch[1];
|
|
4392
4822
|
} else {
|
|
@@ -4443,7 +4873,14 @@ function createGameObject(options) {
|
|
|
4443
4873
|
doc.add_child_to_parent(parentTransformIdStr, transformIdStr);
|
|
4444
4874
|
}
|
|
4445
4875
|
if (variantPiId && strippedSourceRef) {
|
|
4446
|
-
appendToAddedGameObjects(doc, variantPiId, strippedSourceRef, gameObjectIdStr);
|
|
4876
|
+
const appendResult = appendToAddedGameObjects(doc, variantPiId, strippedSourceRef, gameObjectIdStr);
|
|
4877
|
+
if (!appendResult.success) {
|
|
4878
|
+
return {
|
|
4879
|
+
success: false,
|
|
4880
|
+
file_path,
|
|
4881
|
+
error: appendResult.error || `Failed to update m_AddedGameObjects for PrefabInstance ${variantPiId}`
|
|
4882
|
+
};
|
|
4883
|
+
}
|
|
4447
4884
|
}
|
|
4448
4885
|
const saveResult = doc.save();
|
|
4449
4886
|
if (!saveResult.success) {
|
|
@@ -4926,6 +5363,7 @@ PrefabInstance:
|
|
|
4926
5363
|
m_ObjectHideFlags: 0
|
|
4927
5364
|
serializedVersion: 2
|
|
4928
5365
|
m_Modification:
|
|
5366
|
+
serializedVersion: 3
|
|
4929
5367
|
m_TransformParent: {fileID: 0}
|
|
4930
5368
|
m_Modifications:
|
|
4931
5369
|
- target: {fileID: ${rootInfo.game_object.file_id}, guid: ${sourceGuid}, type: 3}
|
|
@@ -5212,6 +5650,8 @@ function is_valid_component_base(base_class, project_path, depth = 0) {
|
|
|
5212
5650
|
}
|
|
5213
5651
|
if (!fullPath.endsWith(".cs") || !import_fs10.existsSync(fullPath)) {
|
|
5214
5652
|
if (match.kind === "class") {
|
|
5653
|
+
if (fullPath.includes("Assembly-CSharp.dll"))
|
|
5654
|
+
return true;
|
|
5215
5655
|
return is_likely_component_base_name(base_class);
|
|
5216
5656
|
}
|
|
5217
5657
|
}
|
|
@@ -5296,7 +5736,7 @@ function addComponent(options) {
|
|
|
5296
5736
|
let duplicateWarning;
|
|
5297
5737
|
const goBlock = doc.find_by_file_id(gameObjectIdStr);
|
|
5298
5738
|
if (goBlock && classId !== null) {
|
|
5299
|
-
const compRefs = [...goBlock.raw.matchAll(/component
|
|
5739
|
+
const compRefs = [...goBlock.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(\d+)\}/g)].map((m) => m[1]);
|
|
5300
5740
|
for (const refId of compRefs) {
|
|
5301
5741
|
const compBlock = doc.find_by_file_id(refId);
|
|
5302
5742
|
if (compBlock && compBlock.class_id === classId) {
|
|
@@ -5356,11 +5796,11 @@ function addComponent(options) {
|
|
|
5356
5796
|
};
|
|
5357
5797
|
}
|
|
5358
5798
|
if (resolved.base_class && !COMPONENT_BASE_CLASSES.has(resolved.base_class) && !is_valid_component_base(resolved.base_class, project_path)) {
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5799
|
+
const warn = `"${component_type}" extends ${resolved.base_class}, which does not inherit from MonoBehaviour. Adding anyway, but ensure the script is a valid component. (Re-run "setup" if the inheritance graph is stale).`;
|
|
5800
|
+
if (!extractionError)
|
|
5801
|
+
extractionError = warn;
|
|
5802
|
+
else
|
|
5803
|
+
extractionError += " " + warn;
|
|
5364
5804
|
}
|
|
5365
5805
|
let version;
|
|
5366
5806
|
if (project_path) {
|
|
@@ -5376,14 +5816,50 @@ function addComponent(options) {
|
|
|
5376
5816
|
componentYAML = createMonoBehaviourYAML(componentId, gameObjectId, resolved.guid, resolved.fields, version, scriptFileId, type_lookup_comp);
|
|
5377
5817
|
scriptGuid = resolved.guid;
|
|
5378
5818
|
scriptPath = resolved.path || undefined;
|
|
5379
|
-
|
|
5819
|
+
if (resolved.extraction_error) {
|
|
5820
|
+
if (!extractionError)
|
|
5821
|
+
extractionError = resolved.extraction_error;
|
|
5822
|
+
else
|
|
5823
|
+
extractionError += " " + resolved.extraction_error;
|
|
5824
|
+
}
|
|
5380
5825
|
}
|
|
5381
5826
|
if (variantInfo) {
|
|
5382
|
-
appendToAddedComponents(doc, variantInfo.prefab_instance_id, variantInfo.source_ref, componentIdStr);
|
|
5827
|
+
const appendResult = appendToAddedComponents(doc, variantInfo.prefab_instance_id, variantInfo.source_ref, componentIdStr);
|
|
5828
|
+
if (!appendResult.success) {
|
|
5829
|
+
return {
|
|
5830
|
+
success: false,
|
|
5831
|
+
file_path,
|
|
5832
|
+
error: appendResult.error || `Failed to update m_AddedComponents for PrefabInstance ${variantInfo.prefab_instance_id}`
|
|
5833
|
+
};
|
|
5834
|
+
}
|
|
5383
5835
|
} else {
|
|
5384
|
-
addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
|
|
5836
|
+
const added = addComponentToGameObject(doc, gameObjectIdStr, componentIdStr);
|
|
5837
|
+
if (!added) {
|
|
5838
|
+
return {
|
|
5839
|
+
success: false,
|
|
5840
|
+
file_path,
|
|
5841
|
+
error: `Failed to add component reference to GameObject ${gameObjectIdStr}: m_Component array not found or malformed`
|
|
5842
|
+
};
|
|
5843
|
+
}
|
|
5385
5844
|
}
|
|
5386
5845
|
doc.append_raw(componentYAML);
|
|
5846
|
+
if (!doc.validate()) {
|
|
5847
|
+
return {
|
|
5848
|
+
success: false,
|
|
5849
|
+
file_path,
|
|
5850
|
+
error: "Validation failed after adding component. The generated YAML may be malformed."
|
|
5851
|
+
};
|
|
5852
|
+
}
|
|
5853
|
+
if (!variantInfo) {
|
|
5854
|
+
const goBlockAfter = doc.find_by_file_id(gameObjectIdStr);
|
|
5855
|
+
if (!goBlockAfter || !goBlockAfter.raw.includes(`component: {fileID: ${componentIdStr}}`)) {
|
|
5856
|
+
return {
|
|
5857
|
+
success: false,
|
|
5858
|
+
file_path,
|
|
5859
|
+
error: `Component block appended but reference not found in GameObject ${gameObjectIdStr}'s m_Component list`
|
|
5860
|
+
};
|
|
5861
|
+
}
|
|
5862
|
+
}
|
|
5387
5863
|
const saveResult = doc.save();
|
|
5388
5864
|
if (!saveResult.success) {
|
|
5389
5865
|
return {
|
|
@@ -5439,8 +5915,11 @@ function copyComponent(options) {
|
|
|
5439
5915
|
const newIdStr = doc.generate_file_id();
|
|
5440
5916
|
const newId = parseInt(newIdStr, 10);
|
|
5441
5917
|
let clonedBlock = sourceBlock.raw.replace(new RegExp(`^(--- !u!${sourceBlock.class_id} &)${source_file_id}`), `$1${newId}`);
|
|
5442
|
-
clonedBlock = clonedBlock.replace(/m_GameObject
|
|
5443
|
-
addComponentToGameObject(doc, targetGoIdStr, newIdStr);
|
|
5918
|
+
clonedBlock = clonedBlock.replace(/m_GameObject:[ \t]*\{fileID:[ \t]*\d+\}/, `m_GameObject: {fileID: ${targetGoId}}`);
|
|
5919
|
+
const added = addComponentToGameObject(doc, targetGoIdStr, newIdStr);
|
|
5920
|
+
if (!added) {
|
|
5921
|
+
return { success: false, file_path, error: `Failed to wire component to target GameObject ${targetGoIdStr}: m_Component array not found or malformed` };
|
|
5922
|
+
}
|
|
5444
5923
|
doc.append_raw(clonedBlock);
|
|
5445
5924
|
if (!doc.validate()) {
|
|
5446
5925
|
return { success: false, file_path, error: "Validation failed after copying component" };
|
|
@@ -5490,7 +5969,17 @@ function validate_value_type(current_value, new_value) {
|
|
|
5490
5969
|
const incoming = new_value.trim();
|
|
5491
5970
|
if (/^\{fileID:/.test(current)) {
|
|
5492
5971
|
if (/^\{fileID:[ \t]*0[ \t]*\}$/.test(current)) {
|
|
5493
|
-
|
|
5972
|
+
if (/^\{fileID:/.test(incoming))
|
|
5973
|
+
return null;
|
|
5974
|
+
if (/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(incoming))
|
|
5975
|
+
return null;
|
|
5976
|
+
if (/^(true|false|yes|no|on|off)$/i.test(incoming))
|
|
5977
|
+
return null;
|
|
5978
|
+
if (/^'[^']*'$/.test(incoming) || /^"[^"]*"$/.test(incoming))
|
|
5979
|
+
return null;
|
|
5980
|
+
if (/^\{.*:.*\}$/.test(incoming))
|
|
5981
|
+
return null;
|
|
5982
|
+
return `Current value is a null reference ({fileID: 0}). New value "${incoming}" is not a valid reference, number, boolean, quoted string, or compound value.`;
|
|
5494
5983
|
}
|
|
5495
5984
|
if (!/^\{fileID:/.test(incoming)) {
|
|
5496
5985
|
return `Expected a reference value ({fileID: ...}), got "${incoming}"`;
|
|
@@ -5700,7 +6189,19 @@ function editProperty(options) {
|
|
|
5700
6189
|
return result;
|
|
5701
6190
|
}
|
|
5702
6191
|
function editComponentByFileId(options) {
|
|
5703
|
-
const { file_path, file_id, property,
|
|
6192
|
+
const { file_path, file_id, property, project_path } = options;
|
|
6193
|
+
let { new_value } = options;
|
|
6194
|
+
const resolved = resolveAssetPathToPPtr(new_value, file_path, project_path);
|
|
6195
|
+
if (resolved === null) {
|
|
6196
|
+
return {
|
|
6197
|
+
success: false,
|
|
6198
|
+
file_path,
|
|
6199
|
+
error: `Could not resolve "${new_value}" to asset reference. Ensure GUID cache exists (run "setup" first, or "setup -p <path>").`
|
|
6200
|
+
};
|
|
6201
|
+
}
|
|
6202
|
+
if (resolved !== undefined) {
|
|
6203
|
+
new_value = resolved;
|
|
6204
|
+
}
|
|
5704
6205
|
const pathError = validate_file_path(file_path, "write");
|
|
5705
6206
|
if (pathError) {
|
|
5706
6207
|
return { success: false, file_path, error: pathError };
|
|
@@ -6160,6 +6661,193 @@ function findPrefabInstanceBlock(doc, identifier) {
|
|
|
6160
6661
|
}
|
|
6161
6662
|
return null;
|
|
6162
6663
|
}
|
|
6664
|
+
function parsePrefabRef2(refText) {
|
|
6665
|
+
const fileIdMatch = refText.match(/fileID:[ \t]*(-?\d+)/i);
|
|
6666
|
+
if (!fileIdMatch)
|
|
6667
|
+
return null;
|
|
6668
|
+
const guidMatch = refText.match(/guid:[ \t]*([a-f0-9]{32})/i);
|
|
6669
|
+
const typeMatch = refText.match(/type:[ \t]*(-?\d+)/i);
|
|
6670
|
+
return {
|
|
6671
|
+
file_id: fileIdMatch[1],
|
|
6672
|
+
guid: guidMatch ? guidMatch[1].toLowerCase() : undefined,
|
|
6673
|
+
type: typeMatch ? typeMatch[1] : undefined
|
|
6674
|
+
};
|
|
6675
|
+
}
|
|
6676
|
+
function extractPrefabSourceGuid2(prefabInstanceRaw) {
|
|
6677
|
+
const sourceMatch = prefabInstanceRaw.match(/m_SourcePrefab:[ \t]*\{[^}]*guid:[ \t]*([a-f0-9]{32})/i);
|
|
6678
|
+
return sourceMatch ? sourceMatch[1].toLowerCase() : null;
|
|
6679
|
+
}
|
|
6680
|
+
function normalizePrefabRef2(refText, sourceGuid) {
|
|
6681
|
+
const parsed = parsePrefabRef2(refText);
|
|
6682
|
+
if (!parsed) {
|
|
6683
|
+
return { error: `Invalid reference "${refText}". Expected format: {fileID: N, guid: ..., type: T}` };
|
|
6684
|
+
}
|
|
6685
|
+
const guid = parsed.guid ?? sourceGuid;
|
|
6686
|
+
if (!guid) {
|
|
6687
|
+
return { error: `Reference "${refText}" is missing guid and source prefab guid could not be inferred.` };
|
|
6688
|
+
}
|
|
6689
|
+
if (!/^[a-f0-9]{32}$/i.test(guid)) {
|
|
6690
|
+
return { error: `Invalid guid in reference "${refText}". Expected 32-character hex GUID.` };
|
|
6691
|
+
}
|
|
6692
|
+
const type = parsed.type ?? "3";
|
|
6693
|
+
if (!/^-?\d+$/.test(type)) {
|
|
6694
|
+
return { error: `Invalid type in reference "${refText}". Expected an integer type value.` };
|
|
6695
|
+
}
|
|
6696
|
+
return {
|
|
6697
|
+
ref: {
|
|
6698
|
+
file_id: parsed.file_id,
|
|
6699
|
+
guid,
|
|
6700
|
+
type
|
|
6701
|
+
}
|
|
6702
|
+
};
|
|
6703
|
+
}
|
|
6704
|
+
function parsePrefabRefMatcher(refText) {
|
|
6705
|
+
const parsed = parsePrefabRef2(refText);
|
|
6706
|
+
if (!parsed) {
|
|
6707
|
+
return { error: `Invalid reference "${refText}". Expected format: {fileID: N, guid: ..., type: T}` };
|
|
6708
|
+
}
|
|
6709
|
+
if (parsed.guid && !/^[a-f0-9]{32}$/i.test(parsed.guid)) {
|
|
6710
|
+
return { error: `Invalid guid in reference "${refText}". Expected 32-character hex GUID.` };
|
|
6711
|
+
}
|
|
6712
|
+
if (parsed.type && !/^-?\d+$/.test(parsed.type)) {
|
|
6713
|
+
return { error: `Invalid type in reference "${refText}". Expected an integer type value.` };
|
|
6714
|
+
}
|
|
6715
|
+
return { matcher: parsed };
|
|
6716
|
+
}
|
|
6717
|
+
function prefabRefKey(ref) {
|
|
6718
|
+
return `${ref.file_id}|${ref.guid}|${ref.type}`;
|
|
6719
|
+
}
|
|
6720
|
+
function serializePrefabRef2(ref) {
|
|
6721
|
+
return `{fileID: ${ref.file_id}, guid: ${ref.guid}, type: ${ref.type}}`;
|
|
6722
|
+
}
|
|
6723
|
+
function splitPrefabArraySection2(rawText, key) {
|
|
6724
|
+
const lines = rawText.split(`
|
|
6725
|
+
`);
|
|
6726
|
+
const keyPattern = new RegExp(`^([ ]*)${key}:[ ]*(.*)$`);
|
|
6727
|
+
let keyIndex = -1;
|
|
6728
|
+
let indent = "";
|
|
6729
|
+
for (let i = 0;i < lines.length; i++) {
|
|
6730
|
+
const match = lines[i].match(keyPattern);
|
|
6731
|
+
if (match) {
|
|
6732
|
+
keyIndex = i;
|
|
6733
|
+
indent = match[1];
|
|
6734
|
+
break;
|
|
6735
|
+
}
|
|
6736
|
+
}
|
|
6737
|
+
if (keyIndex === -1) {
|
|
6738
|
+
return null;
|
|
6739
|
+
}
|
|
6740
|
+
const keyIndentLen = indent.length;
|
|
6741
|
+
let sectionEnd = keyIndex + 1;
|
|
6742
|
+
for (let i = keyIndex + 1;i < lines.length; i++) {
|
|
6743
|
+
const line = lines[i];
|
|
6744
|
+
const trimmed = line.trim();
|
|
6745
|
+
if (trimmed.length === 0) {
|
|
6746
|
+
sectionEnd = i + 1;
|
|
6747
|
+
continue;
|
|
6748
|
+
}
|
|
6749
|
+
const indentLen = (line.match(/^[ \t]*/) || [""])[0].length;
|
|
6750
|
+
if (indentLen < keyIndentLen) {
|
|
6751
|
+
break;
|
|
6752
|
+
}
|
|
6753
|
+
if (indentLen === keyIndentLen && !trimmed.startsWith("-") && /^[A-Za-z_][A-Za-z0-9_]*:/.test(trimmed)) {
|
|
6754
|
+
break;
|
|
6755
|
+
}
|
|
6756
|
+
sectionEnd = i + 1;
|
|
6757
|
+
}
|
|
6758
|
+
return {
|
|
6759
|
+
lines,
|
|
6760
|
+
key_index: keyIndex,
|
|
6761
|
+
section_end: sectionEnd,
|
|
6762
|
+
indent
|
|
6763
|
+
};
|
|
6764
|
+
}
|
|
6765
|
+
function collectNormalizedRefsFromSection(lines, key_index, section_end, sourceGuid) {
|
|
6766
|
+
const sectionText = lines.slice(key_index, section_end).join(`
|
|
6767
|
+
`);
|
|
6768
|
+
const refMatches = sectionText.match(/\{[^}]*\}/g) || [];
|
|
6769
|
+
const dedup = new Map;
|
|
6770
|
+
for (const token of refMatches) {
|
|
6771
|
+
const parsed = parsePrefabRef2(token);
|
|
6772
|
+
if (!parsed)
|
|
6773
|
+
continue;
|
|
6774
|
+
const guid = parsed.guid ?? sourceGuid;
|
|
6775
|
+
if (!guid || !/^[a-f0-9]{32}$/i.test(guid))
|
|
6776
|
+
continue;
|
|
6777
|
+
const type = parsed.type ?? "3";
|
|
6778
|
+
if (!/^-?\d+$/.test(type))
|
|
6779
|
+
continue;
|
|
6780
|
+
const normalized = {
|
|
6781
|
+
file_id: parsed.file_id,
|
|
6782
|
+
guid: guid.toLowerCase(),
|
|
6783
|
+
type
|
|
6784
|
+
};
|
|
6785
|
+
const key = prefabRefKey(normalized);
|
|
6786
|
+
if (!dedup.has(key)) {
|
|
6787
|
+
dedup.set(key, normalized);
|
|
6788
|
+
}
|
|
6789
|
+
}
|
|
6790
|
+
return [...dedup.values()];
|
|
6791
|
+
}
|
|
6792
|
+
function mutatePrefabReferenceList(options) {
|
|
6793
|
+
const { target_block, key, ref_text, action, item_label } = options;
|
|
6794
|
+
const split = splitPrefabArraySection2(target_block.raw, key);
|
|
6795
|
+
if (!split) {
|
|
6796
|
+
return { success: false, error: `${key} property not found in PrefabInstance` };
|
|
6797
|
+
}
|
|
6798
|
+
const sourceGuid = extractPrefabSourceGuid2(target_block.raw);
|
|
6799
|
+
const existing = collectNormalizedRefsFromSection(split.lines, split.key_index, split.section_end, sourceGuid);
|
|
6800
|
+
let next = existing;
|
|
6801
|
+
if (action === "add") {
|
|
6802
|
+
const normalized = normalizePrefabRef2(ref_text, sourceGuid);
|
|
6803
|
+
if (!normalized.ref) {
|
|
6804
|
+
return { success: false, error: normalized.error };
|
|
6805
|
+
}
|
|
6806
|
+
const keyText = prefabRefKey(normalized.ref);
|
|
6807
|
+
if (existing.some((entry) => prefabRefKey(entry) === keyText)) {
|
|
6808
|
+
return { success: true, changed: false };
|
|
6809
|
+
}
|
|
6810
|
+
next = [...existing, normalized.ref];
|
|
6811
|
+
} else {
|
|
6812
|
+
const matcherResult = parsePrefabRefMatcher(ref_text);
|
|
6813
|
+
const matcher = matcherResult.matcher;
|
|
6814
|
+
if (!matcher) {
|
|
6815
|
+
return { success: false, error: matcherResult.error };
|
|
6816
|
+
}
|
|
6817
|
+
next = existing.filter((entry) => {
|
|
6818
|
+
if (entry.file_id !== matcher.file_id)
|
|
6819
|
+
return true;
|
|
6820
|
+
if (matcher.guid && entry.guid !== matcher.guid.toLowerCase())
|
|
6821
|
+
return true;
|
|
6822
|
+
if (matcher.type && entry.type !== matcher.type)
|
|
6823
|
+
return true;
|
|
6824
|
+
return false;
|
|
6825
|
+
});
|
|
6826
|
+
if (next.length === existing.length) {
|
|
6827
|
+
return {
|
|
6828
|
+
success: false,
|
|
6829
|
+
error: `${item_label} reference "${ref_text}" not found in ${key}`
|
|
6830
|
+
};
|
|
6831
|
+
}
|
|
6832
|
+
}
|
|
6833
|
+
const replacement = [];
|
|
6834
|
+
if (next.length === 0) {
|
|
6835
|
+
replacement.push(`${split.indent}${key}: []`);
|
|
6836
|
+
} else {
|
|
6837
|
+
replacement.push(`${split.indent}${key}:`);
|
|
6838
|
+
for (const entry of next) {
|
|
6839
|
+
replacement.push(`${split.indent}- ${serializePrefabRef2(entry)}`);
|
|
6840
|
+
}
|
|
6841
|
+
}
|
|
6842
|
+
const updatedLines = [
|
|
6843
|
+
...split.lines.slice(0, split.key_index),
|
|
6844
|
+
...replacement,
|
|
6845
|
+
...split.lines.slice(split.section_end)
|
|
6846
|
+
];
|
|
6847
|
+
target_block.replace_raw(updatedLines.join(`
|
|
6848
|
+
`));
|
|
6849
|
+
return { success: true, changed: true };
|
|
6850
|
+
}
|
|
6163
6851
|
function editArray(options) {
|
|
6164
6852
|
const { file_path, file_id, array_property, action, value, index } = options;
|
|
6165
6853
|
if (!import_fs11.existsSync(file_path)) {
|
|
@@ -6370,9 +7058,6 @@ function addRemovedComponent(options) {
|
|
|
6370
7058
|
if (!import_fs11.existsSync(file_path)) {
|
|
6371
7059
|
return { success: false, file_path, error: `File not found: ${file_path}` };
|
|
6372
7060
|
}
|
|
6373
|
-
if (!/^\{fileID:\s*-?\d+/.test(component_ref)) {
|
|
6374
|
-
return { success: false, file_path, error: `Invalid component reference "${component_ref}". Expected format: {fileID: N, guid: ..., type: T}` };
|
|
6375
|
-
}
|
|
6376
7061
|
let doc;
|
|
6377
7062
|
try {
|
|
6378
7063
|
doc = UnityDocument.from_file(file_path);
|
|
@@ -6383,19 +7068,16 @@ function addRemovedComponent(options) {
|
|
|
6383
7068
|
if (!targetBlock) {
|
|
6384
7069
|
return { success: false, file_path, error: `PrefabInstance "${prefab_instance}" not found` };
|
|
6385
7070
|
}
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
} else {
|
|
6396
|
-
return { success: false, file_path, error: "m_RemovedComponents property not found in PrefabInstance" };
|
|
7071
|
+
const mutation = mutatePrefabReferenceList({
|
|
7072
|
+
target_block: targetBlock,
|
|
7073
|
+
key: "m_RemovedComponents",
|
|
7074
|
+
ref_text: component_ref,
|
|
7075
|
+
action: "add",
|
|
7076
|
+
item_label: "Component"
|
|
7077
|
+
});
|
|
7078
|
+
if (!mutation.success) {
|
|
7079
|
+
return { success: false, file_path, error: mutation.error };
|
|
6397
7080
|
}
|
|
6398
|
-
targetBlock.replace_raw(rawText);
|
|
6399
7081
|
if (!doc.validate()) {
|
|
6400
7082
|
return { success: false, file_path, error: "Validation failed after adding removed component" };
|
|
6401
7083
|
}
|
|
@@ -6424,13 +7106,16 @@ function removeRemovedComponent(options) {
|
|
|
6424
7106
|
if (!targetBlock) {
|
|
6425
7107
|
return { success: false, file_path, error: `PrefabInstance "${prefab_instance}" not found` };
|
|
6426
7108
|
}
|
|
6427
|
-
const
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
7109
|
+
const mutation = mutatePrefabReferenceList({
|
|
7110
|
+
target_block: targetBlock,
|
|
7111
|
+
key: "m_RemovedComponents",
|
|
7112
|
+
ref_text: component_ref,
|
|
7113
|
+
action: "remove",
|
|
7114
|
+
item_label: "Component"
|
|
7115
|
+
});
|
|
7116
|
+
if (!mutation.success) {
|
|
7117
|
+
return { success: false, file_path, error: mutation.error };
|
|
6431
7118
|
}
|
|
6432
|
-
const updatedRaw = targetBlock.raw.replace(refPattern, "");
|
|
6433
|
-
targetBlock.replace_raw(updatedRaw);
|
|
6434
7119
|
if (!doc.validate()) {
|
|
6435
7120
|
return { success: false, file_path, error: "Validation failed after removing component" };
|
|
6436
7121
|
}
|
|
@@ -6449,9 +7134,6 @@ function addRemovedGameObject(options) {
|
|
|
6449
7134
|
if (!import_fs11.existsSync(file_path)) {
|
|
6450
7135
|
return { success: false, file_path, error: `File not found: ${file_path}` };
|
|
6451
7136
|
}
|
|
6452
|
-
if (!/^\{fileID:\s*-?\d+/.test(component_ref)) {
|
|
6453
|
-
return { success: false, file_path, error: `Invalid GameObject reference "${component_ref}". Expected format: {fileID: N, guid: ..., type: T}` };
|
|
6454
|
-
}
|
|
6455
7137
|
let doc;
|
|
6456
7138
|
try {
|
|
6457
7139
|
doc = UnityDocument.from_file(file_path);
|
|
@@ -6462,19 +7144,16 @@ function addRemovedGameObject(options) {
|
|
|
6462
7144
|
if (!targetBlock) {
|
|
6463
7145
|
return { success: false, file_path, error: `PrefabInstance "${prefab_instance}" not found` };
|
|
6464
7146
|
}
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
} else {
|
|
6475
|
-
return { success: false, file_path, error: "m_RemovedGameObjects property not found in PrefabInstance" };
|
|
7147
|
+
const mutation = mutatePrefabReferenceList({
|
|
7148
|
+
target_block: targetBlock,
|
|
7149
|
+
key: "m_RemovedGameObjects",
|
|
7150
|
+
ref_text: component_ref,
|
|
7151
|
+
action: "add",
|
|
7152
|
+
item_label: "GameObject"
|
|
7153
|
+
});
|
|
7154
|
+
if (!mutation.success) {
|
|
7155
|
+
return { success: false, file_path, error: mutation.error };
|
|
6476
7156
|
}
|
|
6477
|
-
targetBlock.replace_raw(rawText);
|
|
6478
7157
|
if (!doc.validate()) {
|
|
6479
7158
|
return { success: false, file_path, error: "Validation failed after adding removed GameObject" };
|
|
6480
7159
|
}
|
|
@@ -6503,13 +7182,16 @@ function removeRemovedGameObject(options) {
|
|
|
6503
7182
|
if (!targetBlock) {
|
|
6504
7183
|
return { success: false, file_path, error: `PrefabInstance "${prefab_instance}" not found` };
|
|
6505
7184
|
}
|
|
6506
|
-
const
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
7185
|
+
const mutation = mutatePrefabReferenceList({
|
|
7186
|
+
target_block: targetBlock,
|
|
7187
|
+
key: "m_RemovedGameObjects",
|
|
7188
|
+
ref_text: component_ref,
|
|
7189
|
+
action: "remove",
|
|
7190
|
+
item_label: "GameObject"
|
|
7191
|
+
});
|
|
7192
|
+
if (!mutation.success) {
|
|
7193
|
+
return { success: false, file_path, error: mutation.error };
|
|
6510
7194
|
}
|
|
6511
|
-
const updatedRaw = targetBlock.raw.replace(refPattern, "");
|
|
6512
|
-
targetBlock.replace_raw(updatedRaw);
|
|
6513
7195
|
if (!doc.validate()) {
|
|
6514
7196
|
return { success: false, file_path, error: "Validation failed after removing GameObject" };
|
|
6515
7197
|
}
|
|
@@ -6608,7 +7290,8 @@ function removeComponent(options) {
|
|
|
6608
7290
|
return { success: false, file_path, error: "Cannot remove a Transform with remove-component. Use delete to remove the entire GameObject." };
|
|
6609
7291
|
}
|
|
6610
7292
|
const resolved_file_id = found.file_id;
|
|
6611
|
-
|
|
7293
|
+
let removed_added_component_overrides = 0;
|
|
7294
|
+
const goMatch = found.raw.match(/m_GameObject:[ \t]*\{fileID:[ \t]*(-?\d+)\}/);
|
|
6612
7295
|
if (goMatch) {
|
|
6613
7296
|
const parentGoId = goMatch[1];
|
|
6614
7297
|
const goBlock = doc.find_by_file_id(parentGoId);
|
|
@@ -6616,7 +7299,29 @@ function removeComponent(options) {
|
|
|
6616
7299
|
const escaped = resolved_file_id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6617
7300
|
const compLinePattern = new RegExp(`^[ \\t]*- component: \\{fileID: ${escaped}\\}[ \\t]*\\n`, "m");
|
|
6618
7301
|
const modifiedRaw = goBlock.raw.replace(compLinePattern, "");
|
|
7302
|
+
if (modifiedRaw === goBlock.raw) {}
|
|
6619
7303
|
goBlock.replace_raw(modifiedRaw);
|
|
7304
|
+
if (goBlock.is_stripped) {
|
|
7305
|
+
const piMatch = goBlock.raw.match(/m_PrefabInstance:[ \t]*\{fileID:[ \t]*(-?\d+)\}/);
|
|
7306
|
+
if (piMatch) {
|
|
7307
|
+
const piBlock = doc.find_by_file_id(piMatch[1]);
|
|
7308
|
+
if (piBlock && piBlock.class_id === 1001) {
|
|
7309
|
+
const updated = removeAddedComponentOverrideEntries(piBlock.raw, resolved_file_id);
|
|
7310
|
+
if (updated.removed_count > 0) {
|
|
7311
|
+
piBlock.replace_raw(updated.updated_raw);
|
|
7312
|
+
removed_added_component_overrides += updated.removed_count;
|
|
7313
|
+
}
|
|
7314
|
+
}
|
|
7315
|
+
}
|
|
7316
|
+
}
|
|
7317
|
+
}
|
|
7318
|
+
}
|
|
7319
|
+
const dangling = [];
|
|
7320
|
+
for (const block of doc.blocks) {
|
|
7321
|
+
if (block.file_id === resolved_file_id)
|
|
7322
|
+
continue;
|
|
7323
|
+
if (block.raw.includes(`{fileID: ${resolved_file_id}}`)) {
|
|
7324
|
+
dangling.push(block.file_id);
|
|
6620
7325
|
}
|
|
6621
7326
|
}
|
|
6622
7327
|
doc.remove_block(resolved_file_id);
|
|
@@ -6631,7 +7336,8 @@ function removeComponent(options) {
|
|
|
6631
7336
|
success: true,
|
|
6632
7337
|
file_path,
|
|
6633
7338
|
removed_file_id: resolved_file_id,
|
|
6634
|
-
removed_class_id: found.class_id
|
|
7339
|
+
removed_class_id: found.class_id,
|
|
7340
|
+
warning: dangling.length > 0 ? `Dangling references to deleted component found in blocks: ${dangling.join(", ")}${removed_added_component_overrides > 0 ? ` (removed ${removed_added_component_overrides} m_AddedComponents override entr${removed_added_component_overrides === 1 ? "y" : "ies"})` : ""}` : undefined
|
|
6635
7341
|
};
|
|
6636
7342
|
}
|
|
6637
7343
|
function removeComponentBatch(options) {
|
|
@@ -6692,7 +7398,7 @@ function deleteGameObject(options) {
|
|
|
6692
7398
|
const goBlock = goResult;
|
|
6693
7399
|
const goId = goBlock.file_id;
|
|
6694
7400
|
const componentIds = new Set;
|
|
6695
|
-
const compMatches = goBlock.raw.matchAll(/component
|
|
7401
|
+
const compMatches = goBlock.raw.matchAll(/component:[ \t]*\{fileID:[ \t]*(-?\d+)\}/g);
|
|
6696
7402
|
for (const cm of compMatches) {
|
|
6697
7403
|
componentIds.add(cm[1]);
|
|
6698
7404
|
}
|
|
@@ -6702,7 +7408,7 @@ function deleteGameObject(options) {
|
|
|
6702
7408
|
const block = doc.find_by_file_id(compId);
|
|
6703
7409
|
if (block && block.class_id === 4) {
|
|
6704
7410
|
transformId = compId;
|
|
6705
|
-
const fatherMatch = block.raw.match(/m_Father
|
|
7411
|
+
const fatherMatch = block.raw.match(/m_Father:[ \t]*\{fileID:[ \t]*(-?\d+)\}/);
|
|
6706
7412
|
if (fatherMatch) {
|
|
6707
7413
|
fatherId = fatherMatch[1];
|
|
6708
7414
|
}
|
|
@@ -6749,6 +7455,124 @@ function findPrefabInstanceBlock2(doc, identifier) {
|
|
|
6749
7455
|
}
|
|
6750
7456
|
return null;
|
|
6751
7457
|
}
|
|
7458
|
+
function splitPrefabArraySection3(rawText, key) {
|
|
7459
|
+
const lines = rawText.split(`
|
|
7460
|
+
`);
|
|
7461
|
+
const keyPattern = new RegExp(`^[ ]*${key}:[ ]*(.*)$`);
|
|
7462
|
+
let keyIndex = -1;
|
|
7463
|
+
let keyIndentLen = 0;
|
|
7464
|
+
for (let i = 0;i < lines.length; i++) {
|
|
7465
|
+
const match = lines[i].match(keyPattern);
|
|
7466
|
+
if (match) {
|
|
7467
|
+
keyIndex = i;
|
|
7468
|
+
keyIndentLen = (lines[i].match(/^[ \t]*/) || [""])[0].length;
|
|
7469
|
+
break;
|
|
7470
|
+
}
|
|
7471
|
+
}
|
|
7472
|
+
if (keyIndex === -1) {
|
|
7473
|
+
return null;
|
|
7474
|
+
}
|
|
7475
|
+
let sectionEnd = keyIndex + 1;
|
|
7476
|
+
for (let i = keyIndex + 1;i < lines.length; i++) {
|
|
7477
|
+
const line = lines[i];
|
|
7478
|
+
const trimmed = line.trim();
|
|
7479
|
+
if (trimmed.length === 0) {
|
|
7480
|
+
sectionEnd = i + 1;
|
|
7481
|
+
continue;
|
|
7482
|
+
}
|
|
7483
|
+
const indentLen = (line.match(/^[ \t]*/) || [""])[0].length;
|
|
7484
|
+
if (indentLen < keyIndentLen) {
|
|
7485
|
+
break;
|
|
7486
|
+
}
|
|
7487
|
+
if (indentLen === keyIndentLen && !trimmed.startsWith("-") && /^[A-Za-z_][A-Za-z0-9_]*:/.test(trimmed)) {
|
|
7488
|
+
break;
|
|
7489
|
+
}
|
|
7490
|
+
sectionEnd = i + 1;
|
|
7491
|
+
}
|
|
7492
|
+
return {
|
|
7493
|
+
lines,
|
|
7494
|
+
key_index: keyIndex,
|
|
7495
|
+
section_end: sectionEnd
|
|
7496
|
+
};
|
|
7497
|
+
}
|
|
7498
|
+
function collectAddedObjectFileIdsFromArray(rawText, key) {
|
|
7499
|
+
const section = splitPrefabArraySection3(rawText, key);
|
|
7500
|
+
if (!section)
|
|
7501
|
+
return [];
|
|
7502
|
+
const keyLine = section.lines[section.key_index].trim();
|
|
7503
|
+
if (new RegExp(`^${key}:[ ]*[]$`).test(keyLine)) {
|
|
7504
|
+
return [];
|
|
7505
|
+
}
|
|
7506
|
+
const sectionText = section.lines.slice(section.key_index, section.section_end).join(`
|
|
7507
|
+
`);
|
|
7508
|
+
const fileIds = new Set;
|
|
7509
|
+
const addedObjectPattern = /addedObject:[ \t]*\{[^}]*fileID:[ \t]*(-?\d+)/g;
|
|
7510
|
+
let match;
|
|
7511
|
+
while ((match = addedObjectPattern.exec(sectionText)) !== null) {
|
|
7512
|
+
fileIds.add(match[1]);
|
|
7513
|
+
}
|
|
7514
|
+
if (fileIds.size === 0) {
|
|
7515
|
+
const inlineEntryPattern = /^[ \t]*-[ \t]*\{[^}]*fileID:[ \t]*(-?\d+)/gm;
|
|
7516
|
+
while ((match = inlineEntryPattern.exec(sectionText)) !== null) {
|
|
7517
|
+
fileIds.add(match[1]);
|
|
7518
|
+
}
|
|
7519
|
+
}
|
|
7520
|
+
return [...fileIds];
|
|
7521
|
+
}
|
|
7522
|
+
function removeAddedComponentOverrideEntries(rawText, componentId) {
|
|
7523
|
+
const section = splitPrefabArraySection3(rawText, "m_AddedComponents");
|
|
7524
|
+
if (!section)
|
|
7525
|
+
return { updated_raw: rawText, removed_count: 0 };
|
|
7526
|
+
const keyLine = section.lines[section.key_index].trim();
|
|
7527
|
+
if (/^m_AddedComponents:[ \t]*\[\]$/.test(keyLine)) {
|
|
7528
|
+
return { updated_raw: rawText, removed_count: 0 };
|
|
7529
|
+
}
|
|
7530
|
+
const bodyLines = section.lines.slice(section.key_index + 1, section.section_end);
|
|
7531
|
+
const keptEntries = [];
|
|
7532
|
+
let removed = 0;
|
|
7533
|
+
let i = 0;
|
|
7534
|
+
while (i < bodyLines.length) {
|
|
7535
|
+
const line = bodyLines[i];
|
|
7536
|
+
const trimmed = line.trimStart();
|
|
7537
|
+
if (!trimmed.startsWith("-")) {
|
|
7538
|
+
i++;
|
|
7539
|
+
continue;
|
|
7540
|
+
}
|
|
7541
|
+
const entryIndent = (line.match(/^[ \t]*/) || [""])[0].length;
|
|
7542
|
+
const entryLines = [line];
|
|
7543
|
+
i++;
|
|
7544
|
+
while (i < bodyLines.length) {
|
|
7545
|
+
const nextLine = bodyLines[i];
|
|
7546
|
+
const nextTrimmed = nextLine.trim();
|
|
7547
|
+
const nextIndent = (nextLine.match(/^[ \t]*/) || [""])[0].length;
|
|
7548
|
+
if (nextTrimmed.length > 0 && nextIndent === entryIndent && nextTrimmed.startsWith("-")) {
|
|
7549
|
+
break;
|
|
7550
|
+
}
|
|
7551
|
+
entryLines.push(nextLine);
|
|
7552
|
+
i++;
|
|
7553
|
+
}
|
|
7554
|
+
const entryText = entryLines.join(`
|
|
7555
|
+
`);
|
|
7556
|
+
const addedObjectMatch = entryText.match(/addedObject:[ \t]*\{[^}]*fileID:[ \t]*(-?\d+)/);
|
|
7557
|
+
if (addedObjectMatch && addedObjectMatch[1] === componentId) {
|
|
7558
|
+
removed++;
|
|
7559
|
+
continue;
|
|
7560
|
+
}
|
|
7561
|
+
keptEntries.push(entryLines);
|
|
7562
|
+
}
|
|
7563
|
+
if (removed === 0) {
|
|
7564
|
+
return { updated_raw: rawText, removed_count: 0 };
|
|
7565
|
+
}
|
|
7566
|
+
const keyIndent = (section.lines[section.key_index].match(/^[ \t]*/) || [""])[0];
|
|
7567
|
+
const replacement = keptEntries.length === 0 ? [`${keyIndent}m_AddedComponents: []`] : [`${keyIndent}m_AddedComponents:`, ...keptEntries.flat()];
|
|
7568
|
+
const updatedLines = [
|
|
7569
|
+
...section.lines.slice(0, section.key_index),
|
|
7570
|
+
...replacement,
|
|
7571
|
+
...section.lines.slice(section.section_end)
|
|
7572
|
+
];
|
|
7573
|
+
return { updated_raw: updatedLines.join(`
|
|
7574
|
+
`), removed_count: removed };
|
|
7575
|
+
}
|
|
6752
7576
|
function deletePrefabInstance(options) {
|
|
6753
7577
|
const { file_path, prefab_instance } = options;
|
|
6754
7578
|
const pathError = validate_file_path(file_path, "write");
|
|
@@ -6770,44 +7594,31 @@ function deletePrefabInstance(options) {
|
|
|
6770
7594
|
}
|
|
6771
7595
|
const piId = piBlock.file_id;
|
|
6772
7596
|
const allToRemove = new Set([piId]);
|
|
6773
|
-
const piRefPattern = new RegExp(`m_PrefabInstance
|
|
7597
|
+
const piRefPattern = new RegExp(`m_PrefabInstance:[ \\t]*\\{fileID:[ \\t]*${piId}\\}`);
|
|
6774
7598
|
for (const block of doc.blocks) {
|
|
6775
7599
|
if (block.is_stripped && piRefPattern.test(block.raw)) {
|
|
6776
7600
|
allToRemove.add(block.file_id);
|
|
6777
7601
|
}
|
|
6778
7602
|
}
|
|
6779
|
-
const
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
if (compMatch) {
|
|
6791
|
-
const transformId = compMatch[1];
|
|
6792
|
-
allToRemove.add(transformId);
|
|
6793
|
-
const descendants = doc.collect_hierarchy(transformId);
|
|
6794
|
-
for (const id of descendants) {
|
|
6795
|
-
allToRemove.add(id);
|
|
6796
|
-
}
|
|
7603
|
+
for (const goId of collectAddedObjectFileIdsFromArray(piBlock.raw, "m_AddedGameObjects")) {
|
|
7604
|
+
allToRemove.add(goId);
|
|
7605
|
+
const goBlock = doc.find_by_file_id(goId);
|
|
7606
|
+
if (goBlock) {
|
|
7607
|
+
const compMatch = goBlock.raw.match(/component:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
7608
|
+
if (compMatch) {
|
|
7609
|
+
const transformId = compMatch[1];
|
|
7610
|
+
allToRemove.add(transformId);
|
|
7611
|
+
const descendants = doc.collect_hierarchy(transformId);
|
|
7612
|
+
for (const id of descendants) {
|
|
7613
|
+
allToRemove.add(id);
|
|
6797
7614
|
}
|
|
6798
7615
|
}
|
|
6799
7616
|
}
|
|
6800
7617
|
}
|
|
6801
|
-
const
|
|
6802
|
-
|
|
6803
|
-
if (addedCompMatch) {
|
|
6804
|
-
const addedCompSection = addedCompMatch[1];
|
|
6805
|
-
const compMatches = addedCompSection.matchAll(/fileID:\s*(\d+)/g);
|
|
6806
|
-
for (const match of compMatches) {
|
|
6807
|
-
allToRemove.add(match[1]);
|
|
6808
|
-
}
|
|
7618
|
+
for (const componentId of collectAddedObjectFileIdsFromArray(piBlock.raw, "m_AddedComponents")) {
|
|
7619
|
+
allToRemove.add(componentId);
|
|
6809
7620
|
}
|
|
6810
|
-
const parentMatch = piBlock.raw.match(/m_TransformParent
|
|
7621
|
+
const parentMatch = piBlock.raw.match(/m_TransformParent:[ \t]*\{fileID:[ \t]*(\d+)\}/);
|
|
6811
7622
|
const parentTransformId = parentMatch ? parentMatch[1] : "0";
|
|
6812
7623
|
let strippedRootTransformId = null;
|
|
6813
7624
|
for (const id of allToRemove) {
|