@openfairygui/functions 0.2.0-alpha.19 → 0.2.0-alpha.20
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/README.md +7 -2
- package/dist/index.cjs +3 -920
- package/dist/index.d.cts +2 -46
- package/dist/index.d.ts +3 -47
- package/dist/index.js +2 -919
- package/dist/node.cjs +138 -10
- package/dist/node.d.cts +7 -2
- package/dist/node.d.ts +8 -3
- package/dist/node.js +138 -11
- package/dist/{publish-DsPK0SJ1.js → publish-BJ_eelME.js} +1973 -2070
- package/dist/{publish-D7268_u4.cjs → publish-xFWT9Slz.cjs} +1973 -2070
- package/dist/restore-BW2xacB3.cjs +936 -0
- package/dist/{codegen-B8ZM1F4j.d.cts → restore-BeWaJNjR.d.cts} +49 -3
- package/dist/restore-Clk62n0O.js +931 -0
- package/dist/{codegen-CfbDHuFt.d.ts → restore-Dh0-Nvms.d.ts} +50 -4
- package/dist/web.cjs +4 -2
- package/dist/web.d.ts +1 -1
- package/dist/web.js +4 -2
- package/package.json +5 -2
- package/src/adapters/node/restore.ts +187 -0
- package/src/adapters/web/publish.ts +1 -247
- package/src/adapters/web/raster.ts +251 -0
- package/src/atlas/font.ts +95 -0
- package/src/atlas/inputs.ts +515 -0
- package/src/atlas/jta.ts +211 -0
- package/src/atlas/packing.ts +767 -0
- package/src/atlas.ts +23 -1639
- package/src/node.ts +4 -0
- package/src/publish/external-resources.ts +117 -0
- package/src/publish/options.ts +180 -0
- package/src/publish/package-context.ts +608 -0
- package/src/publish/resource-references.ts +210 -0
- package/src/publish.ts +21 -1130
- package/src/restore-internals/font.ts +100 -0
- package/src/restore-internals/movie-clip.ts +104 -0
- package/src/restore-internals/output-transaction.ts +164 -0
- package/src/restore.ts +14 -329
- /package/dist/{atlas-CDn6TirX.d.ts → atlas-C6tbl7nn.d.ts} +0 -0
package/src/restore.ts
CHANGED
|
@@ -7,6 +7,18 @@ import {
|
|
|
7
7
|
ProjectType,
|
|
8
8
|
ProjectWriter,
|
|
9
9
|
} from '@openfairygui/core';
|
|
10
|
+
import {
|
|
11
|
+
assertRestoreOutputDir,
|
|
12
|
+
basename,
|
|
13
|
+
commitRestoreOutput,
|
|
14
|
+
createRestoreStagingDir,
|
|
15
|
+
isPathWithin,
|
|
16
|
+
normalizeRestoreOutputDir,
|
|
17
|
+
resolveOutputProjectPath,
|
|
18
|
+
trimTrailingSlashes,
|
|
19
|
+
} from './restore-internals/output-transaction.js';
|
|
20
|
+
import { serializeFont, type RestorableFontGlyph } from './restore-internals/font.js';
|
|
21
|
+
import { serializeMovieClip, type RestorableMovieFrame } from './restore-internals/movie-clip.js';
|
|
10
22
|
|
|
11
23
|
export interface RestoreImageCropInput {
|
|
12
24
|
sourcePath: string;
|
|
@@ -120,29 +132,6 @@ interface RestorableSprite {
|
|
|
120
132
|
getOriginalHeight(): number;
|
|
121
133
|
}
|
|
122
134
|
|
|
123
|
-
interface RestorableMovieFrame {
|
|
124
|
-
getRectX(): number;
|
|
125
|
-
getRectY(): number;
|
|
126
|
-
getRectWidth(): number;
|
|
127
|
-
getRectHeight(): number;
|
|
128
|
-
getAddDelay(): number;
|
|
129
|
-
getSpriteId(): string;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
interface RestorableFontGlyph {
|
|
133
|
-
getAdvance(): number;
|
|
134
|
-
getChannel(): number;
|
|
135
|
-
getChar(): string;
|
|
136
|
-
getCharId(): number;
|
|
137
|
-
getHeight(): number;
|
|
138
|
-
getImg(): string;
|
|
139
|
-
getWidth(): number;
|
|
140
|
-
getX(): number;
|
|
141
|
-
getXOffset(): number;
|
|
142
|
-
getY(): number;
|
|
143
|
-
getYOffset(): number;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
135
|
type RestorableFontResource = RestorableResource & {
|
|
147
136
|
listGlyphs(): RestorableFontGlyph[];
|
|
148
137
|
getBranch?(): string;
|
|
@@ -164,9 +153,6 @@ interface RestorableDisplayObject {
|
|
|
164
153
|
setFont?(font: string): unknown;
|
|
165
154
|
}
|
|
166
155
|
|
|
167
|
-
const JTA_FILE_MARK = 'yytou';
|
|
168
|
-
const JTA_VERSION = 102;
|
|
169
|
-
const JTA_DEFAULT_FPS = 24;
|
|
170
156
|
const TRANSPARENT_PNG_1X1 = Uint8Array.from([
|
|
171
157
|
137, 80, 78, 71, 13, 10, 26, 10,
|
|
172
158
|
0, 0, 0, 13, 73, 72, 68, 82,
|
|
@@ -352,59 +338,6 @@ function findImageResource(pkg: Package, itemId: string): RestorableResource | n
|
|
|
352
338
|
}) ?? null;
|
|
353
339
|
}
|
|
354
340
|
|
|
355
|
-
function fontGlyphCharId(glyph: RestorableFontGlyph): number {
|
|
356
|
-
const charId = glyph.getCharId();
|
|
357
|
-
if (charId > 0) return charId;
|
|
358
|
-
const char = glyph.getChar();
|
|
359
|
-
return char ? (char.codePointAt(0) ?? 0) : 0;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function scaledFrameDelay(milliseconds: number): number {
|
|
363
|
-
return milliseconds <= 0 ? 0 : Math.max(1, Math.round(milliseconds / (1000 / JTA_DEFAULT_FPS)));
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
function jtaSpeed(interval: number): number {
|
|
367
|
-
return interval <= 0 ? 1 : Math.max(1, Math.round(interval / (1000 / JTA_DEFAULT_FPS)));
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
function writeInt16(value: number): Uint8Array {
|
|
371
|
-
const data = new Uint8Array(2);
|
|
372
|
-
new DataView(data.buffer).setInt16(0, value);
|
|
373
|
-
return data;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
function writeUint16(value: number): Uint8Array {
|
|
377
|
-
const data = new Uint8Array(2);
|
|
378
|
-
new DataView(data.buffer).setUint16(0, value);
|
|
379
|
-
return data;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function writeInt32(value: number): Uint8Array {
|
|
383
|
-
const data = new Uint8Array(4);
|
|
384
|
-
new DataView(data.buffer).setInt32(0, value);
|
|
385
|
-
return data;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
function writeByte(value: number): Uint8Array {
|
|
389
|
-
return new Uint8Array([value & 0xff]);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
function concatBytes(chunks: Uint8Array[]): Uint8Array {
|
|
393
|
-
const length = chunks.reduce((total, chunk) => total + chunk.byteLength, 0);
|
|
394
|
-
const data = new Uint8Array(length);
|
|
395
|
-
let offset = 0;
|
|
396
|
-
for (const chunk of chunks) {
|
|
397
|
-
data.set(chunk, offset);
|
|
398
|
-
offset += chunk.byteLength;
|
|
399
|
-
}
|
|
400
|
-
return data;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
function encodeJtaUtf(value: string): Uint8Array {
|
|
404
|
-
const bytes = new TextEncoder().encode(value);
|
|
405
|
-
return concatBytes([writeUint16(bytes.byteLength), bytes]);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
341
|
function isPublishedBinaryFile(fileName: string): boolean {
|
|
409
342
|
return /_fui\.bytes$/i.test(fileName) || /\.fui$/i.test(fileName) || /\.bin$/i.test(fileName);
|
|
410
343
|
}
|
|
@@ -415,168 +348,6 @@ function inferPackageName(fileName: string): string {
|
|
|
415
348
|
return fileName.replace(/\.bin$/i, '');
|
|
416
349
|
}
|
|
417
350
|
|
|
418
|
-
function trimTrailingSlashes(value: string): string {
|
|
419
|
-
return value.replace(/[/\\]+$/, '');
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
function normalizeComparablePath(value: string): string {
|
|
423
|
-
const normalized = trimTrailingSlashes(value).replace(/\\/g, '/');
|
|
424
|
-
const driveMatch = normalized.match(/^([a-z]:)(?:\/(.*))?$/i);
|
|
425
|
-
const drivePrefix = driveMatch?.[1].toLowerCase() ?? '';
|
|
426
|
-
const remainder = driveMatch ? (driveMatch[2] ?? '') : normalized;
|
|
427
|
-
const hasRoot = driveMatch ? true : remainder.startsWith('/');
|
|
428
|
-
const rawSegments = remainder.split('/').filter((segment) => segment.length > 0);
|
|
429
|
-
const segments: string[] = [];
|
|
430
|
-
|
|
431
|
-
for (const segment of rawSegments) {
|
|
432
|
-
if (segment === '.') continue;
|
|
433
|
-
if (segment === '..') {
|
|
434
|
-
if (segments.length > 0 && segments[segments.length - 1] !== '..') {
|
|
435
|
-
segments.pop();
|
|
436
|
-
} else if (!hasRoot) {
|
|
437
|
-
segments.push('..');
|
|
438
|
-
}
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
segments.push(segment);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
const joined = segments.join('/');
|
|
445
|
-
const comparable = drivePrefix
|
|
446
|
-
? `${drivePrefix}/${joined}`.replace(/\/$/, '')
|
|
447
|
-
: hasRoot
|
|
448
|
-
? `/${joined}`.replace(/\/$/, '')
|
|
449
|
-
: joined || '.';
|
|
450
|
-
// Restore prefers a conservative same-directory guard: false positives are safer than
|
|
451
|
-
// missing a Windows-style case-only path alias and deleting the source publish dir.
|
|
452
|
-
return comparable.toLowerCase();
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
function isPathWithin(root: string, candidate: string): boolean {
|
|
456
|
-
const normalizedRoot = normalizeComparablePath(root);
|
|
457
|
-
const normalizedCandidate = normalizeComparablePath(candidate);
|
|
458
|
-
return normalizedCandidate.startsWith(`${normalizedRoot}/`);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
function basename(filePath: string): string {
|
|
462
|
-
const trimmed = trimTrailingSlashes(filePath);
|
|
463
|
-
const match = trimmed.match(/([^/\\]+)$/);
|
|
464
|
-
return match?.[1] ?? '';
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
function normalizeRestoreOutputDir(output: string): string {
|
|
468
|
-
const normalized = trimTrailingSlashes(output);
|
|
469
|
-
const name = basename(normalized);
|
|
470
|
-
if (!normalized || /\.fairy$/i.test(normalized) || !name || name === '.' || name === '..' || /^[a-z]:$/iu.test(name)) {
|
|
471
|
-
throw new Error('restore: Output must be a non-root project directory, not a .fairy file.');
|
|
472
|
-
}
|
|
473
|
-
return normalized;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function resolveOutputProjectPath(outputDir: string, fs: Pick<RestoreFileSystem, 'join'>): string {
|
|
477
|
-
return fs.join(outputDir, `${basename(outputDir)}.fairy`);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
async function resolvePathForContainment(filePath: string, fs: RestoreFileSystem): Promise<string> {
|
|
481
|
-
const missingSegments: string[] = [];
|
|
482
|
-
let existingPath = filePath;
|
|
483
|
-
while (!(await fs.exists(existingPath))) {
|
|
484
|
-
const parentPath = fs.dirname(existingPath);
|
|
485
|
-
if (!parentPath || parentPath === existingPath) {
|
|
486
|
-
return Promise.resolve(fs.resolvePath(filePath));
|
|
487
|
-
}
|
|
488
|
-
missingSegments.unshift(basename(existingPath));
|
|
489
|
-
existingPath = parentPath;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const resolvedExistingPath = await Promise.resolve(fs.resolvePath(existingPath));
|
|
493
|
-
return missingSegments.reduce((resolvedPath, segment) => fs.join(resolvedPath, segment), resolvedExistingPath);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
async function assertRestoreOutputDir(
|
|
497
|
-
inputDir: string,
|
|
498
|
-
outputDir: string,
|
|
499
|
-
fs: RestoreFileSystem,
|
|
500
|
-
force: boolean,
|
|
501
|
-
): Promise<void> {
|
|
502
|
-
const [resolvedInputDir, resolvedOutputDir] = await Promise.all([
|
|
503
|
-
resolvePathForContainment(inputDir, fs),
|
|
504
|
-
resolvePathForContainment(outputDir, fs),
|
|
505
|
-
]);
|
|
506
|
-
const normalizedInputDir = normalizeComparablePath(resolvedInputDir);
|
|
507
|
-
const normalizedOutputDir = normalizeComparablePath(resolvedOutputDir);
|
|
508
|
-
if (
|
|
509
|
-
normalizedInputDir === normalizedOutputDir ||
|
|
510
|
-
isPathWithin(normalizedInputDir, normalizedOutputDir) ||
|
|
511
|
-
isPathWithin(normalizedOutputDir, normalizedInputDir)
|
|
512
|
-
) {
|
|
513
|
-
throw new Error('Restore output directory must be independent from the published input directory.');
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
if (!(await fs.exists(outputDir))) return;
|
|
517
|
-
|
|
518
|
-
let entries: string[];
|
|
519
|
-
try {
|
|
520
|
-
entries = await fs.readdir(outputDir);
|
|
521
|
-
} catch {
|
|
522
|
-
throw new Error(`Restore output path is not a directory: ${outputDir}`);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
if (entries.length === 0) return;
|
|
526
|
-
if (!force) {
|
|
527
|
-
throw new Error(`Restore output directory is not empty: ${outputDir}. Use --force to overwrite it.`);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
async function createRestoreStagingDir(outputDir: string, fs: RestoreFileSystem): Promise<string> {
|
|
532
|
-
const parentDir = fs.dirname(outputDir) || '.';
|
|
533
|
-
await fs.mkdir(parentDir);
|
|
534
|
-
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
535
|
-
const stagingDir = fs.join(parentDir, `.${basename(outputDir)}.restore-${generateId()}`);
|
|
536
|
-
if (await fs.exists(stagingDir)) continue;
|
|
537
|
-
await fs.mkdir(stagingDir);
|
|
538
|
-
return stagingDir;
|
|
539
|
-
}
|
|
540
|
-
throw new Error(`restore: Could not allocate a staging directory beside ${outputDir}.`);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
async function commitRestoreOutput(
|
|
544
|
-
stagingDir: string,
|
|
545
|
-
outputDir: string,
|
|
546
|
-
fs: RestoreFileSystem,
|
|
547
|
-
): Promise<string | null> {
|
|
548
|
-
if (!(await fs.exists(outputDir))) {
|
|
549
|
-
await fs.rename(stagingDir, outputDir);
|
|
550
|
-
return null;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
const parentDir = fs.dirname(outputDir) || '.';
|
|
554
|
-
let backupDir = '';
|
|
555
|
-
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
556
|
-
const candidate = fs.join(parentDir, `.${basename(outputDir)}.restore-backup-${generateId()}`);
|
|
557
|
-
if (!(await fs.exists(candidate))) {
|
|
558
|
-
backupDir = candidate;
|
|
559
|
-
break;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
if (!backupDir) throw new Error(`restore: Could not allocate a backup directory beside ${outputDir}.`);
|
|
563
|
-
|
|
564
|
-
// ponytail: two-step rename preserves rollback; use a platform directory-exchange primitive if zero reader gap matters.
|
|
565
|
-
await fs.rename(outputDir, backupDir);
|
|
566
|
-
try {
|
|
567
|
-
await fs.rename(stagingDir, outputDir);
|
|
568
|
-
} catch (error) {
|
|
569
|
-
await fs.rename(backupDir, outputDir);
|
|
570
|
-
throw error;
|
|
571
|
-
}
|
|
572
|
-
try {
|
|
573
|
-
await fs.rm(backupDir, { recursive: true, force: true });
|
|
574
|
-
return null;
|
|
575
|
-
} catch {
|
|
576
|
-
return `restore: Previous output retained at ${backupDir}; remove it after checking the restored project.`;
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
351
|
export async function restore(options: RestoreOptions): Promise<RestoreResult> {
|
|
581
352
|
const sourceDir = trimTrailingSlashes(options.inputDir);
|
|
582
353
|
const outputDir = normalizeRestoreOutputDir(options.output);
|
|
@@ -1166,52 +937,7 @@ class RestoreWorkflow {
|
|
|
1166
937
|
|
|
1167
938
|
const outputPath = this._resourceOutputPath(outputProjectPath, pkg, resource, fileName);
|
|
1168
939
|
await this._mkdirForFile(outputPath);
|
|
1169
|
-
await this._fs.writeFile(outputPath,
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
private _serializeFont(pkg: Package, resource: RestorableResource, glyphs: RestorableFontGlyph[]): string {
|
|
1173
|
-
const isTtf = resource.getTtf?.() === true;
|
|
1174
|
-
const lines = isTtf
|
|
1175
|
-
? this._serializeTtfFontHeader(pkg, resource, glyphs)
|
|
1176
|
-
: [
|
|
1177
|
-
'info creator=UIBuilder',
|
|
1178
|
-
`common lineHeight=${resource.getLineHeight?.() ?? 0}`,
|
|
1179
|
-
];
|
|
1180
|
-
|
|
1181
|
-
for (const glyph of glyphs) {
|
|
1182
|
-
const charId = fontGlyphCharId(glyph);
|
|
1183
|
-
if (isTtf) {
|
|
1184
|
-
lines.push(
|
|
1185
|
-
`char id=${charId} x=${glyph.getX()} y=${glyph.getY()} width=${glyph.getWidth()} height=${glyph.getHeight()} `
|
|
1186
|
-
+ `xoffset=${glyph.getXOffset()} yoffset=${glyph.getYOffset()} xadvance=${glyph.getAdvance()} page=0 chnl=${glyph.getChannel()}`,
|
|
1187
|
-
);
|
|
1188
|
-
} else {
|
|
1189
|
-
lines.push(
|
|
1190
|
-
`char id=${charId} img=${glyph.getImg()} xoffset=${glyph.getXOffset()} yoffset=${glyph.getYOffset()} xadvance=${glyph.getAdvance()}`,
|
|
1191
|
-
);
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
return `${lines.join('\n')}\n`;
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
private _serializeTtfFontHeader(pkg: Package, resource: RestorableResource, glyphs: RestorableFontGlyph[]): string[] {
|
|
1199
|
-
const fileName = resourceFileName(resource);
|
|
1200
|
-
const face = stripExtension(fileName) || resource.getName?.() || 'Font';
|
|
1201
|
-
const lineHeight = resource.getLineHeight?.() ?? 0;
|
|
1202
|
-
const fontSize = resource.getFontSize?.() ?? lineHeight;
|
|
1203
|
-
const textureId = resource.getTextureId?.() ?? '';
|
|
1204
|
-
const textureResource = textureId ? pkg.getResourceById(textureId) as RestorableResource | null : null;
|
|
1205
|
-
const textureName = textureResource ? resourceFileName(textureResource) : `${face}_atlas.png`;
|
|
1206
|
-
const scaleW = textureResource?.getWidth?.() ?? 256;
|
|
1207
|
-
const scaleH = textureResource?.getHeight?.() ?? 256;
|
|
1208
|
-
const base = Math.max(Math.min(fontSize, lineHeight) - 6, 0);
|
|
1209
|
-
return [
|
|
1210
|
-
`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`,
|
|
1211
|
-
`common lineHeight=${lineHeight} base=${base} scaleW=${scaleW} scaleH=${scaleH} pages=1 packed=0 alphaChnl=${resource.getTint?.() ? 1 : 0} redChnl=0 greenChnl=0 blueChnl=0`,
|
|
1212
|
-
`page id=0 file="${textureName}"`,
|
|
1213
|
-
`chars count=${glyphs.length}`,
|
|
1214
|
-
];
|
|
940
|
+
await this._fs.writeFile(outputPath, serializeFont(pkg, resource, glyphs));
|
|
1215
941
|
}
|
|
1216
942
|
|
|
1217
943
|
private async _writeMovieClipFile(
|
|
@@ -1258,7 +984,7 @@ class RestoreWorkflow {
|
|
|
1258
984
|
|
|
1259
985
|
const outputPath = this._resourceOutputPath(options.outputProjectPath, pkg, resource, fileName);
|
|
1260
986
|
await this._mkdirForFile(outputPath);
|
|
1261
|
-
await this._fs.writeFileRaw(outputPath,
|
|
987
|
+
await this._fs.writeFileRaw(outputPath, serializeMovieClip(resource, frames, textures));
|
|
1262
988
|
}
|
|
1263
989
|
|
|
1264
990
|
private async _writeSyntheticFontGlyphImages(pkg: Package, outputProjectPath: string): Promise<void> {
|
|
@@ -1288,47 +1014,6 @@ class RestoreWorkflow {
|
|
|
1288
1014
|
return sprites;
|
|
1289
1015
|
}
|
|
1290
1016
|
|
|
1291
|
-
private _serializeMovieClip(
|
|
1292
|
-
resource: RestorableResource,
|
|
1293
|
-
frames: RestorableMovieFrame[],
|
|
1294
|
-
textures: Uint8Array[],
|
|
1295
|
-
): Uint8Array {
|
|
1296
|
-
const chunks: Uint8Array[] = [
|
|
1297
|
-
encodeJtaUtf(JTA_FILE_MARK),
|
|
1298
|
-
writeInt32(JTA_VERSION),
|
|
1299
|
-
writeByte(0),
|
|
1300
|
-
writeByte(0),
|
|
1301
|
-
writeByte(0),
|
|
1302
|
-
writeByte(0),
|
|
1303
|
-
writeUint16(0),
|
|
1304
|
-
writeUint16(0),
|
|
1305
|
-
writeUint16(resource.getWidth?.() ?? 0),
|
|
1306
|
-
writeUint16(resource.getHeight?.() ?? 0),
|
|
1307
|
-
writeByte(jtaSpeed(resource.getInterval?.() ?? 0)),
|
|
1308
|
-
writeByte(scaledFrameDelay(resource.getRepeatDelay?.() ?? 0)),
|
|
1309
|
-
writeByte(resource.getSwing?.() ? 1 : 0),
|
|
1310
|
-
writeInt16(frames.length),
|
|
1311
|
-
];
|
|
1312
|
-
|
|
1313
|
-
for (const [index, frame] of frames.entries()) {
|
|
1314
|
-
chunks.push(
|
|
1315
|
-
writeInt16(scaledFrameDelay(frame.getAddDelay())),
|
|
1316
|
-
writeInt16(frame.getRectX()),
|
|
1317
|
-
writeInt16(frame.getRectY()),
|
|
1318
|
-
writeInt16(frame.getRectWidth()),
|
|
1319
|
-
writeInt16(frame.getRectHeight()),
|
|
1320
|
-
writeInt16(textures[index]?.byteLength === 0 ? -1 : index),
|
|
1321
|
-
);
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
chunks.push(writeInt16(textures.length));
|
|
1325
|
-
for (const texture of textures) {
|
|
1326
|
-
chunks.push(writeInt32(texture.byteLength), texture);
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
return concatBytes(chunks);
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
1017
|
private _sourceFileCandidates(pkg: Package, fileName: string, outputFileName = fileName): string[] {
|
|
1333
1018
|
const publishName = pkg.getPublishName() || pkg.getName();
|
|
1334
1019
|
assertSafeRestoreSegment(publishName, 'package publish name');
|
|
File without changes
|