@openfairygui/functions 0.1.0 → 0.2.0-alpha.0

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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { BinaryWriter, GearType, ProjectType, TransitionActionType } from "@openfairygui/core";
1
+ import { BinaryReader, BinaryWriter, GearType, ProjectType, ProjectWriter, TransitionActionType, UamTransactionError, applyUamTransaction, generateId } from "@openfairygui/core";
2
2
  //#region src/inspect.ts
3
3
  function mapResource(resource) {
4
4
  return {
@@ -720,7 +720,7 @@ var BinarySearchCompat = class {
720
720
  return this.getCurrent();
721
721
  }
722
722
  getCurrent() {
723
- if (this.pot) return Math.trunc(Math.pow(2, this.current));
723
+ if (this.pot) return Math.trunc(2 ** this.current);
724
724
  if (this.mof) return this.current * 4;
725
725
  return this.current;
726
726
  }
@@ -943,8 +943,8 @@ function swapCompat(items, left, right) {
943
943
  function initSizeScheme() {
944
944
  const result = [];
945
945
  for (let w = 5; w <= 13; w += 1) for (let h = 5; h <= 13; h += 1) {
946
- const width = Math.pow(2, w);
947
- const height = Math.pow(2, h);
946
+ const width = 2 ** w;
947
+ const height = 2 ** h;
948
948
  const area = width * height;
949
949
  const aspectRatio = width > height ? width / height : height / width;
950
950
  result.push({
@@ -1914,7 +1914,6 @@ function _readUint32BE(data, offset) {
1914
1914
  }
1915
1915
  /** Collect a Bitmap Font's texture image, packed under the font's ID. */
1916
1916
  async function _collectFontTexture(doc, fontRes, pkg, options) {
1917
- fontRes.getExtras();
1918
1917
  const textureId = fontRes.getTextureId?.() ?? "";
1919
1918
  if (textureId) {
1920
1919
  const fontId = fontRes.getId();
@@ -2394,21 +2393,21 @@ function renderFguiTypescriptMemberAssignment(member, getMemberByName, variant)
2394
2393
  return getMemberByName ? `\t\tthis.${member.name} = <${translatedType}><any>(this.getChild("${escapeTypeScriptString(member.originalName)}"));` : `\t\tthis.${member.name} = <${translatedType}><any>(this.getChildAt(${member.index}));`;
2395
2394
  }
2396
2395
  function resolveCodePath(codePath, basePath, fs) {
2397
- if (isAbsolutePath(codePath)) return trimTrailingSlashes(codePath);
2396
+ if (isAbsolutePath(codePath)) return trimTrailingSlashes$1(codePath);
2398
2397
  const projectBasePath = resolveProjectBasePath(basePath);
2399
- return projectBasePath ? trimTrailingSlashes(fs.join(projectBasePath, codePath)) : trimTrailingSlashes(codePath);
2398
+ return projectBasePath ? trimTrailingSlashes$1(fs.join(projectBasePath, codePath)) : trimTrailingSlashes$1(codePath);
2400
2399
  }
2401
2400
  function resolveProjectBasePath(basePath) {
2402
2401
  if (!basePath) return "";
2403
- const normalized = trimTrailingSlashes(basePath);
2402
+ const normalized = trimTrailingSlashes$1(basePath);
2404
2403
  const assetsMatch = normalized.match(/^(.*)[/\\]assets(?:_[^/\\]+)?$/i);
2405
2404
  if (assetsMatch?.[1]) return assetsMatch[1];
2406
- return dirname$1(normalized);
2405
+ return dirname$2(normalized);
2407
2406
  }
2408
- function dirname$1(filePath) {
2409
- return trimTrailingSlashes(filePath).match(/^(.*)[/\\][^/\\]+$/)?.[1] ?? "";
2407
+ function dirname$2(filePath) {
2408
+ return trimTrailingSlashes$1(filePath).match(/^(.*)[/\\][^/\\]+$/)?.[1] ?? "";
2410
2409
  }
2411
- function trimTrailingSlashes(value) {
2410
+ function trimTrailingSlashes$1(value) {
2412
2411
  return value.replace(/[/\\]+$/, "");
2413
2412
  }
2414
2413
  function isAbsolutePath(value) {
@@ -2471,6 +2470,820 @@ function decodeText(value) {
2471
2470
  return new TextDecoder().decode(value);
2472
2471
  }
2473
2472
  //#endregion
2473
+ //#region src/restore.ts
2474
+ const JTA_FILE_MARK = "yytou";
2475
+ const JTA_VERSION = 102;
2476
+ const JTA_DEFAULT_FPS = 24;
2477
+ const TRANSPARENT_PNG_1X1 = Uint8Array.from([
2478
+ 137,
2479
+ 80,
2480
+ 78,
2481
+ 71,
2482
+ 13,
2483
+ 10,
2484
+ 26,
2485
+ 10,
2486
+ 0,
2487
+ 0,
2488
+ 0,
2489
+ 13,
2490
+ 73,
2491
+ 72,
2492
+ 68,
2493
+ 82,
2494
+ 0,
2495
+ 0,
2496
+ 0,
2497
+ 1,
2498
+ 0,
2499
+ 0,
2500
+ 0,
2501
+ 1,
2502
+ 8,
2503
+ 6,
2504
+ 0,
2505
+ 0,
2506
+ 0,
2507
+ 31,
2508
+ 21,
2509
+ 196,
2510
+ 137,
2511
+ 0,
2512
+ 0,
2513
+ 0,
2514
+ 13,
2515
+ 73,
2516
+ 68,
2517
+ 65,
2518
+ 84,
2519
+ 120,
2520
+ 156,
2521
+ 99,
2522
+ 96,
2523
+ 0,
2524
+ 0,
2525
+ 0,
2526
+ 2,
2527
+ 0,
2528
+ 1,
2529
+ 229,
2530
+ 39,
2531
+ 212,
2532
+ 138,
2533
+ 0,
2534
+ 0,
2535
+ 0,
2536
+ 0,
2537
+ 73,
2538
+ 69,
2539
+ 78,
2540
+ 68,
2541
+ 174,
2542
+ 66,
2543
+ 96,
2544
+ 130
2545
+ ]);
2546
+ function normalizeVirtualPath(path) {
2547
+ const normalized = (path ?? "").replace(/\\/g, "/").trim();
2548
+ if (!normalized || normalized === "/") return "";
2549
+ return normalized.replace(/^\/+/, "").replace(/\/+$/, "");
2550
+ }
2551
+ function resourceFileName(resource) {
2552
+ return resource.getFileName?.() || resource.getFile?.() || resource.getName?.() || "";
2553
+ }
2554
+ function resourcePublishedFileName(resource) {
2555
+ const publishedFile = (resource.getExtras?.() ?? {})._publishedFile;
2556
+ return typeof publishedFile === "string" ? publishedFile : resourceFileName(resource);
2557
+ }
2558
+ function normalizePublishedLooseResourceFileName(resource, fileName) {
2559
+ if (resource.propertyType === "MiscResource" && /\.atlas\.txt$/i.test(fileName)) return fileName.replace(/\.atlas\.txt$/i, ".atlas");
2560
+ if (resource.propertyType === "SpineResource" && /\.skel\.bytes$/i.test(fileName)) return fileName.replace(/\.skel\.bytes$/i, ".skel");
2561
+ return fileName;
2562
+ }
2563
+ function replaceLooseResourceBaseName(resource, fileName) {
2564
+ const normalized = normalizePublishedLooseResourceFileName(resource, fileName);
2565
+ const displayName = resource.getName?.() ?? "";
2566
+ if (!displayName) return normalized;
2567
+ const baseName = fileBaseName(normalized);
2568
+ const ext = /((?:\.[^.\\/]+)+)$/u.exec(baseName)?.[1] ?? "";
2569
+ const currentBaseName = ext ? baseName.slice(0, -ext.length) : baseName;
2570
+ const resourceId = resource.getId?.() ?? "";
2571
+ if (!resourceId || currentBaseName.toLowerCase() !== resourceId.toLowerCase()) return normalized;
2572
+ return `${normalized.slice(0, normalized.length - baseName.length)}${displayName}${ext}`;
2573
+ }
2574
+ function fileBaseName(fileName) {
2575
+ return fileName.split(/[\\/]/).pop() ?? fileName;
2576
+ }
2577
+ function stripExtension(fileName) {
2578
+ return fileBaseName(fileName).replace(/\.[^.]+$/u, "");
2579
+ }
2580
+ function resourceInstanceFileName(resource) {
2581
+ const fileName = (resource.propertyType === "Component" ? `${resource.getName?.() ?? resource.getId?.() ?? "component"}.xml` : resourceFileName(resource)).replace(/\\/g, "/").replace(/^\/+/, "");
2582
+ if (!fileName) return "";
2583
+ if (fileName.includes("/")) return fileName;
2584
+ const virtualPath = normalizeVirtualPath(resource.getPath?.());
2585
+ return virtualPath ? `${virtualPath}/${fileName}` : fileName;
2586
+ }
2587
+ function isSyntheticFontGlyphResource(resource) {
2588
+ return resource.getExtras?.()?._syntheticFontGlyph === true;
2589
+ }
2590
+ function glyphDisplayChar(glyph) {
2591
+ const char = glyph.getChar();
2592
+ if (char) return char;
2593
+ const charId = glyph.getCharId();
2594
+ if (charId <= 0) return "";
2595
+ try {
2596
+ return String.fromCodePoint(charId);
2597
+ } catch {
2598
+ return "";
2599
+ }
2600
+ }
2601
+ function sanitizeGlyphFileSegment(char) {
2602
+ if (!char) return "glyph";
2603
+ return char.replace(/\s/gu, "space").replace(/[\\/:*?"<>|]/gu, "_").replace(/\./gu, "_").split("").filter((item) => {
2604
+ return (item.codePointAt(0) ?? 0) >= 32;
2605
+ }).join("") || "glyph";
2606
+ }
2607
+ function defaultSyntheticFontGlyphFileName(resourceId) {
2608
+ return `${resourceId}.png`;
2609
+ }
2610
+ function syntheticFontGlyphVirtualPath(pkg, font) {
2611
+ const pkgName = pkg.getName?.() ?? "";
2612
+ const fontBase = stripExtension(resourceFileName(font)).toLowerCase();
2613
+ if (pkgName === "EmitNumbers") return "/";
2614
+ if (pkgName === "Transition" && fontBase === "number3") return "/";
2615
+ return "/images/";
2616
+ }
2617
+ function syntheticFontGlyphFileName(pkg, font, glyph, index, glyphCount) {
2618
+ const pkgName = pkg.getName?.() ?? "";
2619
+ const char = glyphDisplayChar(glyph);
2620
+ const fontBase = stripExtension(resourceFileName(font));
2621
+ if (/^(hitnumber|number3)$/i.test(fontBase) && /^[0-9]$/u.test(char)) return `h${char}.png`;
2622
+ if (/^cdtime$/i.test(fontBase) && /^[0-9]$/u.test(char)) return `${char}(4)_png.png`;
2623
+ if (pkgName === "EmitNumbers" && /^number1$/i.test(fontBase)) {
2624
+ if (/^[0-9]$/u.test(char)) return `${char}(2)5_png.png`;
2625
+ if (char === "-") return "m2_png.png";
2626
+ }
2627
+ if (pkgName === "EmitNumbers" && /^number2$/i.test(fontBase)) {
2628
+ if (/^[0-9]$/u.test(char)) return `${char}(4)_png.png`;
2629
+ if (char === "-") return "m1_png.png";
2630
+ }
2631
+ if (pkgName === "Transition" && /^number1$/i.test(fontBase)) {
2632
+ const display = char === "0" && index === glyphCount - 1 ? "0-" : sanitizeGlyphFileSegment(char);
2633
+ return `${String(index).padStart(4, "0")}_${display}_png.png`;
2634
+ }
2635
+ if (pkgName === "Transition" && /^number2$/i.test(fontBase)) return `${String(index).padStart(4, "0")}_${sanitizeGlyphFileSegment(char)}.png`;
2636
+ const display = sanitizeGlyphFileSegment(char);
2637
+ return `${String(index).padStart(4, "0")}_${display}.png`;
2638
+ }
2639
+ function syntheticFontTextureFileName(font) {
2640
+ return `${stripExtension(resourceFileName(font)) || font.getId?.() || "font"}_atlas.png`;
2641
+ }
2642
+ function sameVirtualPath(a, b) {
2643
+ return normalizeVirtualPath(a.getPath?.()) === normalizeVirtualPath(b.getPath?.());
2644
+ }
2645
+ function imageFileName(resource) {
2646
+ const current = resource.getFileName?.() ?? "";
2647
+ if (current) return current;
2648
+ const name = resource.getName?.() ?? resource.getId?.() ?? "image";
2649
+ const fileName = /\.[a-z0-9]+$/i.test(name) ? name : `${name}.png`;
2650
+ resource.setFileName?.(fileName);
2651
+ return fileName;
2652
+ }
2653
+ function findImageResource(pkg, itemId) {
2654
+ return pkg.listResources().find((resource) => {
2655
+ return resource.propertyType === "ImageResource" && resource.getId?.() === itemId;
2656
+ }) ?? null;
2657
+ }
2658
+ function fontGlyphCharId(glyph) {
2659
+ const charId = glyph.getCharId();
2660
+ if (charId > 0) return charId;
2661
+ const char = glyph.getChar();
2662
+ return char ? char.codePointAt(0) ?? 0 : 0;
2663
+ }
2664
+ function scaledFrameDelay(milliseconds) {
2665
+ return milliseconds <= 0 ? 0 : Math.max(1, Math.round(milliseconds / (1e3 / JTA_DEFAULT_FPS)));
2666
+ }
2667
+ function jtaSpeed(interval) {
2668
+ return interval <= 0 ? 1 : Math.max(1, Math.round(interval / (1e3 / JTA_DEFAULT_FPS)));
2669
+ }
2670
+ function writeInt16(value) {
2671
+ const data = new Uint8Array(2);
2672
+ new DataView(data.buffer).setInt16(0, value);
2673
+ return data;
2674
+ }
2675
+ function writeUint16(value) {
2676
+ const data = new Uint8Array(2);
2677
+ new DataView(data.buffer).setUint16(0, value);
2678
+ return data;
2679
+ }
2680
+ function writeInt32(value) {
2681
+ const data = new Uint8Array(4);
2682
+ new DataView(data.buffer).setInt32(0, value);
2683
+ return data;
2684
+ }
2685
+ function writeByte(value) {
2686
+ return new Uint8Array([value & 255]);
2687
+ }
2688
+ function concatBytes(chunks) {
2689
+ const length = chunks.reduce((total, chunk) => total + chunk.byteLength, 0);
2690
+ const data = new Uint8Array(length);
2691
+ let offset = 0;
2692
+ for (const chunk of chunks) {
2693
+ data.set(chunk, offset);
2694
+ offset += chunk.byteLength;
2695
+ }
2696
+ return data;
2697
+ }
2698
+ function encodeJtaUtf(value) {
2699
+ const bytes = new TextEncoder().encode(value);
2700
+ return concatBytes([writeUint16(bytes.byteLength), bytes]);
2701
+ }
2702
+ function isPublishedBinaryFile(fileName) {
2703
+ return /_fui\.bytes$/i.test(fileName) || /\.fui$/i.test(fileName) || /\.bin$/i.test(fileName);
2704
+ }
2705
+ function inferPackageName(fileName) {
2706
+ if (/_fui\.bytes$/i.test(fileName)) return fileName.replace(/_fui\.bytes$/i, "");
2707
+ if (/\.fui$/i.test(fileName)) return fileName.replace(/\.fui$/i, "");
2708
+ return fileName.replace(/\.bin$/i, "");
2709
+ }
2710
+ function trimTrailingSlashes(value) {
2711
+ return value.replace(/[/\\]+$/, "");
2712
+ }
2713
+ function normalizeComparablePath(value) {
2714
+ const normalized = trimTrailingSlashes(value).replace(/\\/g, "/");
2715
+ const driveMatch = normalized.match(/^([a-z]:)(?:\/(.*))?$/i);
2716
+ const drivePrefix = driveMatch?.[1].toLowerCase() ?? "";
2717
+ const remainder = driveMatch ? driveMatch[2] ?? "" : normalized;
2718
+ const hasRoot = driveMatch ? true : remainder.startsWith("/");
2719
+ const rawSegments = remainder.split("/").filter((segment) => segment.length > 0);
2720
+ const segments = [];
2721
+ for (const segment of rawSegments) {
2722
+ if (segment === ".") continue;
2723
+ if (segment === "..") {
2724
+ if (segments.length > 0 && segments[segments.length - 1] !== "..") segments.pop();
2725
+ else if (!hasRoot) segments.push("..");
2726
+ continue;
2727
+ }
2728
+ segments.push(segment);
2729
+ }
2730
+ const joined = segments.join("/");
2731
+ return (drivePrefix ? `${drivePrefix}/${joined}`.replace(/\/$/, "") : hasRoot ? `/${joined}`.replace(/\/$/, "") : joined || ".").toLowerCase();
2732
+ }
2733
+ function dirname$1(filePath) {
2734
+ return trimTrailingSlashes(filePath).match(/^(.*)[/\\][^/\\]+$/)?.[1] ?? "";
2735
+ }
2736
+ function basename(filePath) {
2737
+ return trimTrailingSlashes(filePath).match(/([^/\\]+)$/)?.[1] ?? "";
2738
+ }
2739
+ function resolveOutputProjectPath(output, fs) {
2740
+ if (/\.fairy$/i.test(output)) return output;
2741
+ const normalizedOutput = trimTrailingSlashes(output);
2742
+ const projectName = basename(normalizedOutput) || "Restored";
2743
+ return fs.join(normalizedOutput, `${projectName}.fairy`);
2744
+ }
2745
+ async function prepareRestoreOutputDir(inputDir, outputDir, outputProjectPath, fs, force, outputIsProjectFile) {
2746
+ const [resolvedInputDir, resolvedOutputDir] = await Promise.all([Promise.resolve(fs.resolvePath(inputDir)), Promise.resolve(fs.resolvePath(outputDir))]);
2747
+ if (normalizeComparablePath(resolvedInputDir) === normalizeComparablePath(resolvedOutputDir)) throw new Error("Restore output directory must be different from the published input directory.");
2748
+ if (outputIsProjectFile) {
2749
+ if (!await fs.exists(outputDir)) {
2750
+ await fs.mkdir(outputDir);
2751
+ return;
2752
+ }
2753
+ try {
2754
+ await fs.readdir(outputDir);
2755
+ } catch {
2756
+ throw new Error(`Restore output path is not a directory: ${outputDir}`);
2757
+ }
2758
+ if (!await fs.exists(outputProjectPath)) return;
2759
+ if (!force) throw new Error(`Restore output file already exists: ${outputProjectPath}. Use --force to overwrite it.`);
2760
+ if (!fs.rm) throw new Error("Restore output file already exists and the provided fs does not support rm(...).");
2761
+ await fs.rm(outputProjectPath, {
2762
+ recursive: true,
2763
+ force: true
2764
+ });
2765
+ return;
2766
+ }
2767
+ if (!await fs.exists(outputDir)) {
2768
+ await fs.mkdir(outputDir);
2769
+ return;
2770
+ }
2771
+ let entries;
2772
+ try {
2773
+ entries = await fs.readdir(outputDir);
2774
+ } catch {
2775
+ throw new Error(`Restore output path is not a directory: ${outputDir}`);
2776
+ }
2777
+ if (entries.length === 0) return;
2778
+ if (!force) throw new Error(`Restore output directory is not empty: ${outputDir}. Use --force to overwrite it.`);
2779
+ if (!fs.rm) throw new Error("Restore output directory is not empty and the provided fs does not support rm(...).");
2780
+ await fs.rm(outputDir, {
2781
+ recursive: true,
2782
+ force: true
2783
+ });
2784
+ await fs.mkdir(outputDir);
2785
+ }
2786
+ async function restore(options) {
2787
+ const sourceDir = trimTrailingSlashes(options.inputDir);
2788
+ const outputIsProjectFile = /\.fairy$/i.test(options.output);
2789
+ const outputProjectPath = resolveOutputProjectPath(options.output, options.fs);
2790
+ await prepareRestoreOutputDir(sourceDir, dirname$1(outputProjectPath) || ".", outputProjectPath, options.fs, options.force === true, outputIsProjectFile);
2791
+ const packageFilter = options.packages?.length ? new Set(options.packages) : null;
2792
+ const candidateBinaryPaths = (await options.fs.readdir(sourceDir)).filter((name) => isPublishedBinaryFile(name)).filter((name) => !packageFilter || packageFilter.has(inferPackageName(name))).map((name) => options.fs.join(sourceDir, name)).sort((left, right) => left.localeCompare(right));
2793
+ const binaryPaths = (await Promise.all(candidateBinaryPaths.map(async (filePath) => await options.fs.isFile(filePath) ? filePath : null))).filter((filePath) => !!filePath).sort((left, right) => left.localeCompare(right));
2794
+ if (binaryPaths.length === 0) throw new Error(`No FairyGUI published binary files found in ${sourceDir}.`);
2795
+ return new RestoreWorkflow(options.fs).restore({
2796
+ binaryPaths,
2797
+ sourceDir,
2798
+ outputProjectPath,
2799
+ projectType: options.projectType,
2800
+ cropImage: options.cropImage,
2801
+ extractImage: options.extractImage
2802
+ });
2803
+ }
2804
+ var RestoreWorkflow = class {
2805
+ _fs;
2806
+ constructor(fs) {
2807
+ this._fs = fs;
2808
+ }
2809
+ async restore(options) {
2810
+ const warnings = [];
2811
+ const doc = await new BinaryReader(this._fs).readMany(options.binaryPaths);
2812
+ this._initializeProjectDefaults(doc, options.projectType);
2813
+ this._initializeImageFileNames(doc);
2814
+ this._initializeLooseResourceFileNames(doc);
2815
+ await this._synthesizeLooseSkeletonResources(doc, options.sourceDir);
2816
+ this._initializeRestoredResourceRelations(doc);
2817
+ this._initializePublishedFontTextureIds(doc);
2818
+ this._initializeFontTextureImageResources(doc);
2819
+ this._initializeFontGlyphImageResources(doc);
2820
+ this._initializePublishedTextFontResources(doc);
2821
+ this._initializeDisplayObjectFileNames(doc);
2822
+ this._initializePublishedFontDefaults(doc);
2823
+ await new ProjectWriter(this._fs).write(doc, options.outputProjectPath);
2824
+ await this._restoreAssets(doc, options, warnings);
2825
+ return {
2826
+ document: doc,
2827
+ projectPath: options.outputProjectPath,
2828
+ warnings
2829
+ };
2830
+ }
2831
+ _initializeProjectDefaults(doc, projectType) {
2832
+ doc.getRoot().setProjectId(generateId()).setProjectType(projectType ?? ProjectType.Unity).setVersion("3.0").setSettings({
2833
+ publish: {
2834
+ binaryFormat: true,
2835
+ fileExtension: "bytes",
2836
+ compressDesc: false
2837
+ },
2838
+ common: {},
2839
+ adaptation: {}
2840
+ });
2841
+ }
2842
+ _initializeImageFileNames(doc) {
2843
+ for (const pkg of doc.getRoot().listPackages()) for (const resource of pkg.listResources()) {
2844
+ if (resource.propertyType !== "ImageResource") continue;
2845
+ imageFileName(resource);
2846
+ resource.setExtras?.({
2847
+ ...resource.getExtras?.() ?? {},
2848
+ _suppressPackageSize: true
2849
+ });
2850
+ }
2851
+ }
2852
+ _initializeLooseResourceFileNames(doc) {
2853
+ for (const pkg of doc.getRoot().listPackages()) for (const resource of pkg.listResources()) {
2854
+ if (![
2855
+ "MiscResource",
2856
+ "SpineResource",
2857
+ "DragonBonesResource",
2858
+ "SoundResource"
2859
+ ].includes(resource.propertyType)) continue;
2860
+ const current = resource.getFile?.() ?? "";
2861
+ if (!current) continue;
2862
+ const normalized = replaceLooseResourceBaseName(resource, current);
2863
+ if (normalized !== current) resource.setFile?.(normalized);
2864
+ }
2865
+ }
2866
+ async _synthesizeLooseSkeletonResources(doc, sourceDir) {
2867
+ for (const pkg of doc.getRoot().listPackages()) for (const resource of [...pkg.listResources()]) {
2868
+ let current = resource;
2869
+ if (resource.propertyType === "DragonBonesResource" && /\.skel\.bytes$/i.test(resourceFileName(resource))) {
2870
+ const normalizedFile = resourceFileName(resource).replace(/\.skel\.bytes$/i, ".skel");
2871
+ const atlasBase = stripExtension(normalizedFile).replace(/-(?:pro|ess)$/i, "-pma");
2872
+ if (await this._resolveLooseSourceFile(pkg, sourceDir, `${atlasBase}.atlas`)) current = this._replaceSkeletonResourceType(doc, pkg, resource, "SpineResource", normalizedFile);
2873
+ }
2874
+ if (current.propertyType === "SpineResource") await this._ensureSpineSidecarResources(doc, pkg, current, sourceDir);
2875
+ else if (current.propertyType === "DragonBonesResource") await this._ensureDragonBonesSidecarResources(doc, pkg, current, sourceDir);
2876
+ }
2877
+ }
2878
+ _initializeRestoredResourceRelations(doc) {
2879
+ for (const pkg of doc.getRoot().listPackages()) {
2880
+ const resources = pkg.listResources();
2881
+ for (const resource of resources) if (resource.propertyType === "SpineResource") this._initializeSpineResourceRelation(resource, resources);
2882
+ else if (resource.propertyType === "DragonBonesResource") this._initializeDragonBonesResourceRelation(resource, resources);
2883
+ }
2884
+ }
2885
+ _replaceSkeletonResourceType(doc, pkg, resource, targetType, fileName) {
2886
+ const replacement = targetType === "SpineResource" ? doc.createSpineResource(resource.getName?.() ?? "") : doc.createDragonBonesResource(resource.getName?.() ?? "");
2887
+ replacement.setId?.(resource.getId?.() ?? "").setPath?.(resource.getPath?.() ?? "/").setFile?.(fileName).setExported?.(resource.getExported?.() ?? false).setWidth?.(resource.getWidth?.() ?? 0).setHeight?.(resource.getHeight?.() ?? 0).setRequireIds?.(resource.getRequireIds?.() ?? []).setAtlasNames?.(resource.getAtlasNames?.() ?? []).setAnchor?.(resource.getAnchorX?.() ?? 0, resource.getAnchorY?.() ?? 0).setBranch?.(resource.getBranch?.() ?? "").setBranchItemIds?.(resource.getBranchItemIds?.() ?? []);
2888
+ replacement.setExtras?.({ ...resource.getExtras?.() ?? {} });
2889
+ pkg.removeResource(resource);
2890
+ pkg.addResource(replacement);
2891
+ return replacement;
2892
+ }
2893
+ async _ensureSpineSidecarResources(doc, pkg, resource, sourceDir) {
2894
+ const skeletonBase = stripExtension(resourceFileName(resource).replace(/\.skel\.bytes$/i, ".skel"));
2895
+ if (!skeletonBase) return;
2896
+ const atlasBase = skeletonBase.replace(/-(?:pro|ess)$/i, "-pma");
2897
+ const atlas = await this._ensureLooseMiscResource(doc, pkg, resource, sourceDir, `${atlasBase}.atlas`);
2898
+ const texture = await this._ensureLooseImageResource(doc, pkg, resource, sourceDir, `${atlasBase}.png`);
2899
+ const requireIds = [atlas?.getId?.(), texture?.getId?.()].filter((id) => !!id);
2900
+ if (requireIds.length > 0) resource.setRequireIds?.(requireIds);
2901
+ if (atlas) resource.setAtlasNames?.([atlasBase]);
2902
+ }
2903
+ async _ensureDragonBonesSidecarResources(doc, pkg, resource, sourceDir) {
2904
+ const skeletonBase = stripExtension(resourceFileName(resource)).replace(/_ske$/i, "");
2905
+ if (!skeletonBase) return;
2906
+ const textureJson = await this._ensureLooseMiscResource(doc, pkg, resource, sourceDir, `${skeletonBase}_tex.json`);
2907
+ const textureImage = await this._ensureLooseImageResource(doc, pkg, resource, sourceDir, `${skeletonBase}.png`);
2908
+ const requireIds = [textureJson?.getId?.(), textureImage?.getId?.()].filter((id) => !!id);
2909
+ if (requireIds.length > 0) resource.setRequireIds?.(requireIds);
2910
+ }
2911
+ async _ensureLooseMiscResource(doc, pkg, owner, sourceDir, fileName) {
2912
+ const resources = pkg.listResources();
2913
+ const existing = this._findResourceByFile(resources, owner, "MiscResource", fileName);
2914
+ if (existing) return existing;
2915
+ const sourcePath = await this._resolveLooseSourceFile(pkg, sourceDir, fileName);
2916
+ if (!sourcePath) return null;
2917
+ const resource = doc.createMiscResource(stripExtension(fileName));
2918
+ resource.setId(generateId()).setPath(owner.getPath?.() ?? "/").setBranch(owner.getBranch?.() ?? "").setBranchItemIds(owner.getBranchItemIds?.() ?? []).setExported(false).setFile(fileName);
2919
+ resource.setExtras?.({
2920
+ ...resource.getExtras?.() ?? {},
2921
+ _publishedFile: fileBaseName(sourcePath)
2922
+ });
2923
+ pkg.addResource(resource);
2924
+ return resource;
2925
+ }
2926
+ async _ensureLooseImageResource(doc, pkg, owner, sourceDir, fileName) {
2927
+ const resources = pkg.listResources();
2928
+ const existing = this._findResourceByFile(resources, owner, "ImageResource", fileName);
2929
+ if (existing) return existing;
2930
+ const sourcePath = await this._resolveLooseSourceFile(pkg, sourceDir, fileName);
2931
+ if (!sourcePath) return null;
2932
+ const resource = doc.createImageResource(stripExtension(fileName));
2933
+ resource.setId(generateId()).setPath(owner.getPath?.() ?? "/").setBranch(owner.getBranch?.() ?? "").setBranchItemIds(owner.getBranchItemIds?.() ?? []).setExported(false).setFileName(fileName);
2934
+ resource.setExtras?.({
2935
+ ...resource.getExtras?.() ?? {},
2936
+ _publishedFile: fileBaseName(sourcePath),
2937
+ _suppressPackageSize: true,
2938
+ _syntheticLooseImage: true
2939
+ });
2940
+ pkg.addResource(resource);
2941
+ return resource;
2942
+ }
2943
+ _initializeDisplayObjectFileNames(doc) {
2944
+ for (const pkg of doc.getRoot().listPackages()) for (const component of pkg.listComponents()) for (const child of component.listChildren()) {
2945
+ if (!child.setFileName || child.getFileName?.()) continue;
2946
+ const resource = this._resolveDisplayObjectResource(doc, pkg, child);
2947
+ const fileName = resource ? resourceInstanceFileName(resource) : "";
2948
+ if (fileName) child.setFileName(fileName);
2949
+ }
2950
+ }
2951
+ _initializeFontGlyphImageResources(doc) {
2952
+ for (const pkg of doc.getRoot().listPackages()) for (const resource of [...pkg.listResources()]) {
2953
+ if (resource.propertyType !== "FontResource") continue;
2954
+ const glyphEntries = /* @__PURE__ */ new Map();
2955
+ for (const [index, glyph] of resource.listGlyphs().entries()) {
2956
+ const glyphId = glyph.getImg?.() ?? "";
2957
+ if (!glyphId || glyphEntries.has(glyphId)) continue;
2958
+ glyphEntries.set(glyphId, {
2959
+ glyph,
2960
+ index
2961
+ });
2962
+ }
2963
+ for (const [glyphId, entry] of glyphEntries) {
2964
+ if (pkg.getResourceById(glyphId)) continue;
2965
+ const image = doc.createImageResource(glyphId);
2966
+ image.setId(glyphId).setPath(syntheticFontGlyphVirtualPath(pkg, resource)).setBranch(resource.getBranch?.() ?? "").setFileName(syntheticFontGlyphFileName(pkg, resource, entry.glyph, entry.index, glyphEntries.size)).setExtras({
2967
+ ...image.getExtras?.() ?? {},
2968
+ _syntheticFontGlyph: true,
2969
+ _packageOrderAfterId: resource.getId?.() ?? "",
2970
+ _packageOrderWeight: 1,
2971
+ _suppressPackageSize: true
2972
+ });
2973
+ pkg.addResource(image);
2974
+ }
2975
+ }
2976
+ }
2977
+ _initializeFontTextureImageResources(doc) {
2978
+ for (const pkg of doc.getRoot().listPackages()) for (const resource of [...pkg.listResources()]) {
2979
+ if (resource.propertyType !== "FontResource") continue;
2980
+ const textureId = resource.getTextureId?.() ?? "";
2981
+ if (!textureId || pkg.getResourceById(textureId)) continue;
2982
+ const image = doc.createImageResource(textureId);
2983
+ image.setId(textureId).setPath(resource.getPath?.() ?? "/").setBranch(resource.getBranch?.() ?? "").setFileName(syntheticFontTextureFileName(resource)).setExtras({
2984
+ ...image.getExtras?.() ?? {},
2985
+ _syntheticFontTexture: true,
2986
+ _packageOrderAfterId: resource.getId?.() ?? "",
2987
+ _packageOrderWeight: 0,
2988
+ _suppressPackageSize: true
2989
+ });
2990
+ pkg.addResource(image);
2991
+ }
2992
+ }
2993
+ _resolveDisplayObjectResource(doc, pkg, child) {
2994
+ const src = child.getSrc?.() ?? "";
2995
+ if (!src) return null;
2996
+ return (child.getPackageId?.() ? doc.getRoot().getPackageById(child.getPackageId?.() ?? "") : pkg)?.getResourceById(src) ?? null;
2997
+ }
2998
+ _initializeSpineResourceRelation(resource, resources) {
2999
+ const skeletonBase = stripExtension(resourceFileName(resource));
3000
+ if (!skeletonBase) return;
3001
+ const atlasBase = skeletonBase.replace(/-(?:pro|ess)$/i, "-pma");
3002
+ const requireIds = [];
3003
+ const atlas = this._findResourceByFile(resources, resource, "MiscResource", `${atlasBase}.atlas`);
3004
+ const texture = this._findResourceByFile(resources, resource, "ImageResource", `${atlasBase}.png`);
3005
+ if (atlas?.getId?.()) requireIds.push(atlas.getId());
3006
+ if (texture?.getId?.()) requireIds.push(texture.getId());
3007
+ if (requireIds.length > 0) resource.setRequireIds?.(requireIds);
3008
+ if (atlas) resource.setAtlasNames?.([atlasBase]);
3009
+ }
3010
+ _initializeDragonBonesResourceRelation(resource, resources) {
3011
+ const skeletonBase = stripExtension(resourceFileName(resource)).replace(/_ske$/i, "");
3012
+ if (!skeletonBase) return;
3013
+ const requireIds = [];
3014
+ const textureJson = this._findResourceByFile(resources, resource, "MiscResource", `${skeletonBase}_tex.json`);
3015
+ const textureImage = this._findResourceByFile(resources, resource, "ImageResource", `${skeletonBase}.png`);
3016
+ if (textureJson?.getId?.()) requireIds.push(textureJson.getId());
3017
+ if (textureImage?.getId?.()) requireIds.push(textureImage.getId());
3018
+ if (requireIds.length > 0) resource.setRequireIds?.(requireIds);
3019
+ }
3020
+ _findResourceByFile(resources, owner, propertyType, fileName) {
3021
+ const expected = fileName.toLowerCase();
3022
+ return resources.find((resource) => {
3023
+ return resource.propertyType === propertyType && sameVirtualPath(owner, resource) && fileBaseName(resourceFileName(resource)).toLowerCase() === expected;
3024
+ }) ?? null;
3025
+ }
3026
+ _initializePublishedFontDefaults(doc) {
3027
+ for (const pkg of doc.getRoot().listPackages()) for (const resource of pkg.listResources()) {
3028
+ if (resource.propertyType !== "FontResource") continue;
3029
+ const fileName = resourceFileName(resource);
3030
+ if (!/\bsdf\b/i.test(fileName)) continue;
3031
+ if (!resource.getRenderMode?.()) resource.setRenderMode?.("sdfaa");
3032
+ if (!resource.getSamplePointSize?.()) resource.setSamplePointSize?.(60);
3033
+ }
3034
+ }
3035
+ _initializePublishedTextFontResources(doc) {
3036
+ for (const pkg of doc.getRoot().listPackages()) {
3037
+ const fontResources = pkg.listResources().filter((resource) => resource.propertyType === "FontResource");
3038
+ const fontByFileName = new Map(fontResources.map((resource) => [resourceFileName(resource).toLowerCase(), resource]));
3039
+ const fontByDisplayName = new Map(fontResources.map((resource) => [stripExtension(resourceFileName(resource)).toLowerCase(), resource]));
3040
+ for (const component of pkg.listComponents()) for (const child of component.listChildren()) {
3041
+ const font = child.getFont?.() ?? "";
3042
+ if (!font || font.startsWith("ui://")) continue;
3043
+ if (!/\bsdf\b/i.test(font)) continue;
3044
+ const normalized = font.trim().toLowerCase();
3045
+ let resource = fontByDisplayName.get(normalized) ?? fontByFileName.get(`${normalized}.ttf`);
3046
+ if (!resource) {
3047
+ resource = doc.createFontResource(font.trim());
3048
+ resource.setId(generateId()).setPath("/font/").setFileName(`${font.trim()}.ttf`).setExported(false).setRenderMode("sdfaa").setSamplePointSize(60).setTtf(true);
3049
+ pkg.addResource(resource);
3050
+ fontByDisplayName.set(normalized, resource);
3051
+ fontByFileName.set(`${normalized}.ttf`, resource);
3052
+ }
3053
+ child.setFont?.(`ui://${pkg.getId()}${resource.getId?.() ?? ""}`);
3054
+ }
3055
+ }
3056
+ }
3057
+ _initializePublishedFontTextureIds(doc) {
3058
+ for (const pkg of doc.getRoot().listPackages()) {
3059
+ const resources = pkg.listResources();
3060
+ for (const resource of resources) {
3061
+ if (resource.propertyType !== "FontResource") continue;
3062
+ if (resource.getTextureId?.()) continue;
3063
+ if (resource.getTtf?.() !== true) continue;
3064
+ const expectedFileName = syntheticFontTextureFileName(resource).toLowerCase();
3065
+ const texture = resources.find((candidate) => {
3066
+ return candidate.propertyType === "ImageResource" && sameVirtualPath(resource, candidate) && fileBaseName(resourceFileName(candidate)).toLowerCase() === expectedFileName;
3067
+ });
3068
+ if (texture?.getId?.()) resource.setTextureId?.(texture.getId());
3069
+ }
3070
+ }
3071
+ }
3072
+ async _restoreAssets(doc, options, warnings) {
3073
+ for (const pkg of doc.getRoot().listPackages()) {
3074
+ await this._restoreAtlasImages(pkg, options);
3075
+ await this._writeGeneratedResources(pkg, options, warnings);
3076
+ await this._copyLooseResources(pkg, options, warnings);
3077
+ }
3078
+ }
3079
+ async _restoreAtlasImages(pkg, options) {
3080
+ if (!options.cropImage) return;
3081
+ for (const atlas of pkg.listAtlases()) {
3082
+ const sourceAtlas = await this._resolveSourceFile(options.sourceDir, this._sourceFileCandidates(pkg, atlas.getFile()));
3083
+ if (!sourceAtlas) throw new Error(`Atlas image not found for package "${pkg.getName()}": ${this._sourceFileCandidates(pkg, atlas.getFile()).join(", ")}`);
3084
+ for (const sprite of atlas.listSprites()) {
3085
+ const image = findImageResource(pkg, sprite.getItemId());
3086
+ if (!image) continue;
3087
+ if (sprite.getRectWidth() <= 0 || sprite.getRectHeight() <= 0) continue;
3088
+ const outputPath = this._resourceOutputPath(options.outputProjectPath, pkg, image, imageFileName(image));
3089
+ const imageWidth = image.getWidth?.() ?? 0;
3090
+ const imageHeight = image.getHeight?.() ?? 0;
3091
+ const spriteWidth = sprite.getRotated() ? sprite.getRectHeight() : sprite.getRectWidth();
3092
+ const spriteHeight = sprite.getRotated() ? sprite.getRectWidth() : sprite.getRectHeight();
3093
+ await this._mkdirForFile(outputPath);
3094
+ await options.cropImage({
3095
+ sourcePath: sourceAtlas,
3096
+ outputPath,
3097
+ left: sprite.getRectX(),
3098
+ top: sprite.getRectY(),
3099
+ width: sprite.getRectWidth(),
3100
+ height: sprite.getRectHeight(),
3101
+ rotated: sprite.getRotated(),
3102
+ offsetX: sprite.getOffsetX(),
3103
+ offsetY: sprite.getOffsetY(),
3104
+ expectedWidth: Math.max(imageWidth, sprite.getOriginalWidth(), spriteWidth),
3105
+ expectedHeight: Math.max(imageHeight, sprite.getOriginalHeight(), spriteHeight)
3106
+ });
3107
+ }
3108
+ }
3109
+ }
3110
+ async _copyLooseResources(pkg, options, warnings) {
3111
+ for (const resource of pkg.listResources()) {
3112
+ const syntheticLooseImage = resource.getExtras?.()?._syntheticLooseImage === true;
3113
+ if (![
3114
+ "SoundResource",
3115
+ "MiscResource",
3116
+ "SpineResource",
3117
+ "DragonBonesResource"
3118
+ ].includes(resource.propertyType) && !syntheticLooseImage) continue;
3119
+ const fileName = resourceFileName(resource);
3120
+ if (!fileName) continue;
3121
+ const sourcePath = await this._resolveSourceFile(options.sourceDir, this._sourceFileCandidates(pkg, resourcePublishedFileName(resource), fileName));
3122
+ if (!sourcePath) {
3123
+ warnings.push(`Loose resource not found for package "${pkg.getName()}": ${fileName}`);
3124
+ continue;
3125
+ }
3126
+ const outputPath = this._resourceOutputPath(options.outputProjectPath, pkg, resource, fileName);
3127
+ await this._mkdirForFile(outputPath);
3128
+ await this._fs.writeFileRaw(outputPath, await this._fs.readFileRaw(sourcePath));
3129
+ }
3130
+ }
3131
+ async _writeGeneratedResources(pkg, options, warnings) {
3132
+ for (const resource of pkg.listResources()) if (resource.propertyType === "FontResource") await this._writeFontFile(pkg, resource, options.outputProjectPath);
3133
+ else if (resource.propertyType === "MovieClipResource") await this._writeMovieClipFile(pkg, resource, options, warnings);
3134
+ await this._writeSyntheticFontGlyphImages(pkg, options.outputProjectPath);
3135
+ }
3136
+ async _writeFontFile(pkg, resource, outputProjectPath) {
3137
+ const fileName = resourceFileName(resource);
3138
+ if (!/\.fnt$/i.test(fileName)) return;
3139
+ const glyphs = resource.listGlyphs?.() ?? [];
3140
+ if (glyphs.length === 0) return;
3141
+ const outputPath = this._resourceOutputPath(outputProjectPath, pkg, resource, fileName);
3142
+ await this._mkdirForFile(outputPath);
3143
+ await this._fs.writeFile(outputPath, this._serializeFont(pkg, resource, glyphs));
3144
+ }
3145
+ _serializeFont(pkg, resource, glyphs) {
3146
+ const isTtf = resource.getTtf?.() === true;
3147
+ const lines = isTtf ? this._serializeTtfFontHeader(pkg, resource, glyphs) : ["info creator=UIBuilder", `common lineHeight=${resource.getLineHeight?.() ?? 0}`];
3148
+ for (const glyph of glyphs) {
3149
+ const charId = fontGlyphCharId(glyph);
3150
+ if (isTtf) lines.push(`char id=${charId} x=${glyph.getX()} y=${glyph.getY()} width=${glyph.getWidth()} height=${glyph.getHeight()} xoffset=${glyph.getXOffset()} yoffset=${glyph.getYOffset()} xadvance=${glyph.getAdvance()} page=0 chnl=${glyph.getChannel()}`);
3151
+ else lines.push(`char id=${charId} img=${glyph.getImg()} xoffset=${glyph.getXOffset()} yoffset=${glyph.getYOffset()} xadvance=${glyph.getAdvance()}`);
3152
+ }
3153
+ return `${lines.join("\n")}\n`;
3154
+ }
3155
+ _serializeTtfFontHeader(pkg, resource, glyphs) {
3156
+ const face = stripExtension(resourceFileName(resource)) || resource.getName?.() || "Font";
3157
+ const lineHeight = resource.getLineHeight?.() ?? 0;
3158
+ const fontSize = resource.getFontSize?.() ?? lineHeight;
3159
+ const textureId = resource.getTextureId?.() ?? "";
3160
+ const textureResource = textureId ? pkg.getResourceById(textureId) : null;
3161
+ const textureName = textureResource ? resourceFileName(textureResource) : `${face}_atlas.png`;
3162
+ const scaleW = textureResource?.getWidth?.() ?? 256;
3163
+ const scaleH = textureResource?.getHeight?.() ?? 256;
3164
+ const base = Math.max(Math.min(fontSize, lineHeight) - 6, 0);
3165
+ return [
3166
+ `info face="${face}" size=${fontSize} bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 outline=0`,
3167
+ `common lineHeight=${lineHeight} base=${base} scaleW=${scaleW} scaleH=${scaleH} pages=1 packed=0 alphaChnl=${resource.getTint?.() ? 1 : 0} redChnl=0 greenChnl=0 blueChnl=0`,
3168
+ `page id=0 file="${textureName}"`,
3169
+ `chars count=${glyphs.length}`
3170
+ ];
3171
+ }
3172
+ async _writeMovieClipFile(pkg, resource, options, warnings) {
3173
+ const fileName = resourceFileName(resource);
3174
+ if (!/\.jta$/i.test(fileName)) return;
3175
+ const frames = resource.listFrames?.() ?? [];
3176
+ if (frames.length === 0) return;
3177
+ if (!options.extractImage) {
3178
+ warnings.push(`MovieClip file not generated for package "${pkg.getName()}": ${fileName}`);
3179
+ return;
3180
+ }
3181
+ const sprites = await this._buildSpriteLookup(pkg, options);
3182
+ const textures = [];
3183
+ for (const [index, frame] of frames.entries()) {
3184
+ const spriteEntry = sprites.get(frame.getSpriteId());
3185
+ if (!spriteEntry) {
3186
+ warnings.push(`MovieClip frame sprite not found for package "${pkg.getName()}": ${fileName} frame ${index}`);
3187
+ return;
3188
+ }
3189
+ const sprite = spriteEntry.sprite;
3190
+ if (sprite.getRectWidth() <= 0 || sprite.getRectHeight() <= 0) {
3191
+ textures.push(new Uint8Array(0));
3192
+ continue;
3193
+ }
3194
+ textures.push(await options.extractImage({
3195
+ sourcePath: spriteEntry.sourceAtlas,
3196
+ left: sprite.getRectX(),
3197
+ top: sprite.getRectY(),
3198
+ width: sprite.getRectWidth(),
3199
+ height: sprite.getRectHeight(),
3200
+ rotated: sprite.getRotated(),
3201
+ offsetX: 0,
3202
+ offsetY: 0,
3203
+ expectedWidth: sprite.getRotated() ? sprite.getRectHeight() : sprite.getRectWidth(),
3204
+ expectedHeight: sprite.getRotated() ? sprite.getRectWidth() : sprite.getRectHeight()
3205
+ }));
3206
+ }
3207
+ const outputPath = this._resourceOutputPath(options.outputProjectPath, pkg, resource, fileName);
3208
+ await this._mkdirForFile(outputPath);
3209
+ await this._fs.writeFileRaw(outputPath, this._serializeMovieClip(resource, frames, textures));
3210
+ }
3211
+ async _writeSyntheticFontGlyphImages(pkg, outputProjectPath) {
3212
+ for (const resource of pkg.listResources()) {
3213
+ if (resource.propertyType !== "ImageResource" || !isSyntheticFontGlyphResource(resource)) continue;
3214
+ const fileName = resourceFileName(resource) || defaultSyntheticFontGlyphFileName(resource.getId?.() ?? "glyph");
3215
+ const outputPath = this._resourceOutputPath(outputProjectPath, pkg, resource, fileName);
3216
+ await this._mkdirForFile(outputPath);
3217
+ await this._fs.writeFileRaw(outputPath, TRANSPARENT_PNG_1X1);
3218
+ }
3219
+ }
3220
+ async _buildSpriteLookup(pkg, options) {
3221
+ const sprites = /* @__PURE__ */ new Map();
3222
+ for (const atlas of pkg.listAtlases()) {
3223
+ const sourceAtlas = await this._resolveSourceFile(options.sourceDir, this._sourceFileCandidates(pkg, atlas.getFile()));
3224
+ if (!sourceAtlas) throw new Error(`Atlas image not found for package "${pkg.getName()}": ${this._sourceFileCandidates(pkg, atlas.getFile()).join(", ")}`);
3225
+ for (const sprite of atlas.listSprites()) sprites.set(sprite.getItemId(), {
3226
+ sourceAtlas,
3227
+ sprite
3228
+ });
3229
+ }
3230
+ return sprites;
3231
+ }
3232
+ _serializeMovieClip(resource, frames, textures) {
3233
+ const chunks = [
3234
+ encodeJtaUtf(JTA_FILE_MARK),
3235
+ writeInt32(JTA_VERSION),
3236
+ writeByte(0),
3237
+ writeByte(0),
3238
+ writeByte(0),
3239
+ writeByte(0),
3240
+ writeUint16(0),
3241
+ writeUint16(0),
3242
+ writeUint16(resource.getWidth?.() ?? 0),
3243
+ writeUint16(resource.getHeight?.() ?? 0),
3244
+ writeByte(jtaSpeed(resource.getInterval?.() ?? 0)),
3245
+ writeByte(scaledFrameDelay(resource.getRepeatDelay?.() ?? 0)),
3246
+ writeByte(resource.getSwing?.() ? 1 : 0),
3247
+ writeInt16(frames.length)
3248
+ ];
3249
+ for (const [index, frame] of frames.entries()) chunks.push(writeInt16(scaledFrameDelay(frame.getAddDelay())), writeInt16(frame.getRectX()), writeInt16(frame.getRectY()), writeInt16(frame.getRectWidth()), writeInt16(frame.getRectHeight()), writeInt16(textures[index]?.byteLength === 0 ? -1 : index));
3250
+ chunks.push(writeInt16(textures.length));
3251
+ for (const texture of textures) chunks.push(writeInt32(texture.byteLength), texture);
3252
+ return concatBytes(chunks);
3253
+ }
3254
+ _sourceFileCandidates(pkg, fileName, outputFileName = fileName) {
3255
+ const publishName = pkg.getPublishName() || pkg.getName();
3256
+ return Array.from(new Set([
3257
+ `${publishName}_${fileName}`,
3258
+ fileName,
3259
+ `${publishName}_${outputFileName}`,
3260
+ outputFileName
3261
+ ]));
3262
+ }
3263
+ async _resolveLooseSourceFile(pkg, sourceDir, outputFileName) {
3264
+ const candidates = outputFileName.endsWith(".atlas") ? this._sourceFileCandidates(pkg, `${outputFileName}.txt`, outputFileName) : outputFileName.endsWith(".skel") ? this._sourceFileCandidates(pkg, `${outputFileName}.bytes`, outputFileName) : this._sourceFileCandidates(pkg, outputFileName);
3265
+ return this._resolveSourceFile(sourceDir, candidates);
3266
+ }
3267
+ async _resolveSourceFile(sourceDir, candidates) {
3268
+ for (const candidate of candidates) {
3269
+ const sourcePath = this._fs.join(sourceDir, candidate);
3270
+ if (await this._fs.isFile(sourcePath)) return sourcePath;
3271
+ }
3272
+ return null;
3273
+ }
3274
+ _resourceOutputPath(outputProjectPath, pkg, resource, fileName) {
3275
+ const basePath = this._fs.dirname(outputProjectPath);
3276
+ const branch = resource.getBranch?.() ?? "";
3277
+ const assetsDir = branch ? `assets_${branch}` : "assets";
3278
+ const virtualPath = normalizeVirtualPath(resource.getPath?.());
3279
+ const pkgDir = this._fs.join(basePath, assetsDir, pkg.getName());
3280
+ return virtualPath ? this._fs.join(pkgDir, virtualPath, fileName) : this._fs.join(pkgDir, fileName);
3281
+ }
3282
+ async _mkdirForFile(filePath) {
3283
+ await this._fs.mkdir(this._fs.dirname(filePath));
3284
+ }
3285
+ };
3286
+ //#endregion
2474
3287
  //#region src/publish.ts
2475
3288
  const UNITY_PROJECT_TYPE = ProjectType.Unity;
2476
3289
  const COCOS_CREATOR_PROJECT_TYPE = ProjectType.CocosCreator;
@@ -3096,4 +3909,37 @@ function _computeDependencies(pkg, pkgMap) {
3096
3909
  }
3097
3910
  }
3098
3911
  //#endregion
3099
- export { AUTO_GENERATED_CODE_MARK, ValidationSeverity, atlas, createTransform, inspect, prune, publish, publishCodeGeneration, rename, resolvePublishOptions, validate };
3912
+ //#region src/uam-transaction.ts
3913
+ function mapTransactionErrorStage(error) {
3914
+ switch (error.code) {
3915
+ case "transaction_unsupported": return "preflight";
3916
+ case "invalid_uam": return "preflight";
3917
+ case "execution_failure": return error.opIndex === void 0 && error.issues !== void 0 ? "postflight" : "execution";
3918
+ case "selector_ambiguity": return error.opIndex === void 0 ? "preflight" : "execution";
3919
+ }
3920
+ }
3921
+ function applyUamTransactionApp(input) {
3922
+ try {
3923
+ return {
3924
+ ok: true,
3925
+ project: applyUamTransaction(input.project, input.operations)
3926
+ };
3927
+ } catch (error) {
3928
+ if (error instanceof UamTransactionError) return {
3929
+ ok: false,
3930
+ error: {
3931
+ code: error.code,
3932
+ stage: mapTransactionErrorStage(error),
3933
+ message: error.message,
3934
+ opIndex: error.opIndex,
3935
+ opId: error.opId,
3936
+ opKind: error.opKind,
3937
+ selector: error.selector,
3938
+ issues: error.issues
3939
+ }
3940
+ };
3941
+ throw error;
3942
+ }
3943
+ }
3944
+ //#endregion
3945
+ export { AUTO_GENERATED_CODE_MARK, ValidationSeverity, applyUamTransactionApp, atlas, createTransform, inspect, prune, publish, publishCodeGeneration, rename, resolvePublishOptions, restore, validate };