swf-toolkit 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +125 -2
- package/README.tr.md +126 -0
- package/dist/abc/abc-file.d.ts +168 -0
- package/dist/abc/abc-file.js +656 -0
- package/dist/abc/analysis.d.ts +36 -0
- package/dist/abc/analysis.js +213 -0
- package/dist/abc/code.d.ts +59 -0
- package/dist/abc/code.js +379 -0
- package/dist/abc/constants.d.ts +80 -0
- package/dist/abc/constants.js +93 -0
- package/dist/abc/interner.d.ts +46 -0
- package/dist/abc/interner.js +168 -0
- package/dist/abc/model.d.ts +40 -0
- package/dist/abc/model.js +146 -0
- package/dist/abc/opcodes.d.ts +39 -0
- package/dist/abc/opcodes.js +196 -0
- package/dist/abc/pcode/assembler.d.ts +42 -0
- package/dist/abc/pcode/assembler.js +635 -0
- package/dist/abc/pcode/disassembler.d.ts +17 -0
- package/dist/abc/pcode/disassembler.js +147 -0
- package/dist/abc/pcode/format.d.ts +23 -0
- package/dist/abc/pcode/format.js +131 -0
- package/dist/abc/pcode/lexer.d.ts +34 -0
- package/dist/abc/pcode/lexer.js +183 -0
- package/dist/assets/assets.d.ts +26 -0
- package/dist/assets/assets.js +151 -0
- package/dist/assets/images.d.ts +31 -0
- package/dist/assets/images.js +238 -0
- package/dist/assets/png.d.ts +12 -0
- package/dist/assets/png.js +196 -0
- package/dist/assets/sounds.d.ts +37 -0
- package/dist/assets/sounds.js +236 -0
- package/dist/avm1/avm1.d.ts +55 -0
- package/dist/avm1/avm1.js +642 -0
- package/dist/avm1/project.d.ts +16 -0
- package/dist/avm1/project.js +81 -0
- package/dist/cjs/abc/abc-file.d.ts +168 -0
- package/dist/cjs/abc/abc-file.js +663 -0
- package/dist/cjs/abc/analysis.d.ts +36 -0
- package/dist/cjs/abc/analysis.js +221 -0
- package/dist/cjs/abc/code.d.ts +59 -0
- package/dist/cjs/abc/code.js +387 -0
- package/dist/cjs/abc/constants.d.ts +80 -0
- package/dist/cjs/abc/constants.js +97 -0
- package/dist/cjs/abc/interner.d.ts +46 -0
- package/dist/cjs/abc/interner.js +172 -0
- package/dist/cjs/abc/model.d.ts +40 -0
- package/dist/cjs/abc/model.js +155 -0
- package/dist/cjs/abc/opcodes.d.ts +39 -0
- package/dist/cjs/abc/opcodes.js +201 -0
- package/dist/cjs/abc/pcode/assembler.d.ts +42 -0
- package/dist/cjs/abc/pcode/assembler.js +640 -0
- package/dist/cjs/abc/pcode/disassembler.d.ts +17 -0
- package/dist/cjs/abc/pcode/disassembler.js +152 -0
- package/dist/cjs/abc/pcode/format.d.ts +23 -0
- package/dist/cjs/abc/pcode/format.js +141 -0
- package/dist/cjs/abc/pcode/lexer.d.ts +34 -0
- package/dist/cjs/abc/pcode/lexer.js +188 -0
- package/dist/cjs/assets/assets.d.ts +26 -0
- package/dist/cjs/assets/assets.js +159 -0
- package/dist/cjs/assets/images.d.ts +31 -0
- package/dist/cjs/assets/images.js +246 -0
- package/dist/cjs/assets/png.d.ts +12 -0
- package/dist/cjs/assets/png.js +202 -0
- package/dist/cjs/assets/sounds.d.ts +37 -0
- package/dist/cjs/assets/sounds.js +246 -0
- package/dist/cjs/avm1/avm1.d.ts +55 -0
- package/dist/cjs/avm1/avm1.js +649 -0
- package/dist/cjs/avm1/project.d.ts +16 -0
- package/dist/cjs/avm1/project.js +88 -0
- package/dist/cjs/compiler/ast.d.ts +285 -0
- package/dist/cjs/compiler/ast.js +3 -0
- package/dist/cjs/compiler/class-compiler.d.ts +34 -0
- package/dist/cjs/compiler/class-compiler.js +440 -0
- package/dist/cjs/compiler/codegen.d.ts +144 -0
- package/dist/cjs/compiler/codegen.js +1589 -0
- package/dist/cjs/compiler/lexer.d.ts +27 -0
- package/dist/cjs/compiler/lexer.js +254 -0
- package/dist/cjs/compiler/parser.d.ts +58 -0
- package/dist/cjs/compiler/parser.js +989 -0
- package/dist/cjs/compiler/program.d.ts +29 -0
- package/dist/cjs/compiler/program.js +36 -0
- package/dist/cjs/compiler/scope.d.ts +22 -0
- package/dist/cjs/compiler/scope.js +242 -0
- package/dist/cjs/compression/lzma.d.ts +37 -0
- package/dist/cjs/compression/lzma.js +575 -0
- package/dist/cjs/compression/swf-container.d.ts +19 -0
- package/dist/cjs/compression/swf-container.js +92 -0
- package/dist/cjs/decompiler/ast.d.ts +228 -0
- package/dist/cjs/decompiler/ast.js +455 -0
- package/dist/cjs/decompiler/cfg.d.ts +40 -0
- package/dist/cjs/decompiler/cfg.js +236 -0
- package/dist/cjs/decompiler/class-printer.d.ts +9 -0
- package/dist/cjs/decompiler/class-printer.js +173 -0
- package/dist/cjs/decompiler/method.d.ts +41 -0
- package/dist/cjs/decompiler/method.js +436 -0
- package/dist/cjs/decompiler/simulate.d.ts +55 -0
- package/dist/cjs/decompiler/simulate.js +591 -0
- package/dist/cjs/decompiler/structure.d.ts +48 -0
- package/dist/cjs/decompiler/structure.js +764 -0
- package/dist/cjs/errors.d.ts +11 -0
- package/dist/cjs/errors.js +25 -0
- package/dist/cjs/index.d.ts +41 -0
- package/dist/cjs/index.js +82 -0
- package/dist/cjs/io/reader.d.ts +48 -0
- package/dist/cjs/io/reader.js +180 -0
- package/dist/cjs/io/writer.d.ts +37 -0
- package/dist/cjs/io/writer.js +175 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/project/pcode-project.d.ts +32 -0
- package/dist/cjs/project/pcode-project.js +210 -0
- package/dist/cjs/project/source-export.d.ts +12 -0
- package/dist/cjs/project/source-export.js +51 -0
- package/dist/cjs/project/source-import.d.ts +13 -0
- package/dist/cjs/project/source-import.js +73 -0
- package/dist/cjs/swf/swf.d.ts +63 -0
- package/dist/cjs/swf/swf.js +147 -0
- package/dist/cjs/swf/tag-codes.d.ts +77 -0
- package/dist/cjs/swf/tag-codes.js +109 -0
- package/dist/cjs/swf/tags.d.ts +118 -0
- package/dist/cjs/swf/tags.js +330 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +296 -0
- package/dist/compiler/ast.d.ts +285 -0
- package/dist/compiler/ast.js +2 -0
- package/dist/compiler/class-compiler.d.ts +34 -0
- package/dist/compiler/class-compiler.js +437 -0
- package/dist/compiler/codegen.d.ts +144 -0
- package/dist/compiler/codegen.js +1585 -0
- package/dist/compiler/lexer.d.ts +27 -0
- package/dist/compiler/lexer.js +249 -0
- package/dist/compiler/parser.d.ts +58 -0
- package/dist/compiler/parser.js +984 -0
- package/dist/compiler/program.d.ts +29 -0
- package/dist/compiler/program.js +32 -0
- package/dist/compiler/scope.d.ts +22 -0
- package/dist/compiler/scope.js +238 -0
- package/dist/compression/lzma.d.ts +37 -0
- package/dist/compression/lzma.js +569 -0
- package/dist/compression/swf-container.d.ts +19 -0
- package/dist/compression/swf-container.js +87 -0
- package/dist/decompiler/ast.d.ts +228 -0
- package/dist/decompiler/ast.js +441 -0
- package/dist/decompiler/cfg.d.ts +40 -0
- package/dist/decompiler/cfg.js +231 -0
- package/dist/decompiler/class-printer.d.ts +9 -0
- package/dist/decompiler/class-printer.js +170 -0
- package/dist/decompiler/method.d.ts +41 -0
- package/dist/decompiler/method.js +427 -0
- package/dist/decompiler/simulate.d.ts +55 -0
- package/dist/decompiler/simulate.js +586 -0
- package/dist/decompiler/structure.d.ts +48 -0
- package/dist/decompiler/structure.js +760 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +20 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +49 -0
- package/dist/io/reader.d.ts +48 -0
- package/dist/io/reader.js +176 -0
- package/dist/io/writer.d.ts +37 -0
- package/dist/io/writer.js +169 -0
- package/dist/project/pcode-project.d.ts +32 -0
- package/dist/project/pcode-project.js +203 -0
- package/dist/project/source-export.d.ts +12 -0
- package/dist/project/source-export.js +47 -0
- package/dist/project/source-import.d.ts +13 -0
- package/dist/project/source-import.js +70 -0
- package/dist/swf/swf.d.ts +63 -0
- package/dist/swf/swf.js +143 -0
- package/dist/swf/tag-codes.d.ts +77 -0
- package/dist/swf/tag-codes.js +105 -0
- package/dist/swf/tags.d.ts +118 -0
- package/dist/swf/tags.js +316 -0
- package/package.json +71 -4
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export / replace SWF resources: bitmaps, sounds, binary data and edit-text strings.
|
|
3
|
+
*/
|
|
4
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { ByteReader } from '../io/reader.js';
|
|
7
|
+
import { ByteWriter } from '../io/writer.js';
|
|
8
|
+
import { TagCode } from '../swf/tag-codes.js';
|
|
9
|
+
import { DefineBinaryDataTag, RawTag, SymbolTableTag } from '../swf/tags.js';
|
|
10
|
+
import { extractImage, IMAGE_TAGS, replaceImage } from './images.js';
|
|
11
|
+
import { extractSound, replaceSound } from './sounds.js';
|
|
12
|
+
export const ALL_ASSET_KINDS = ['images', 'sounds', 'binary', 'texts'];
|
|
13
|
+
/** Class / export names per character id (SymbolClass + ExportAssets). */
|
|
14
|
+
export function symbolNames(swf) {
|
|
15
|
+
const map = new Map();
|
|
16
|
+
for (const { tag } of swf.walkTags()) {
|
|
17
|
+
if (!(tag instanceof SymbolTableTag))
|
|
18
|
+
continue;
|
|
19
|
+
for (const s of tag.symbols)
|
|
20
|
+
map.set(s.id, [...(map.get(s.id) ?? []), s.name]);
|
|
21
|
+
}
|
|
22
|
+
return map;
|
|
23
|
+
}
|
|
24
|
+
/** Initial text of a DefineEditText tag (undefined when it has none). */
|
|
25
|
+
export function editTextOf(tag) {
|
|
26
|
+
const r = new ByteReader(tag.data);
|
|
27
|
+
r.u16();
|
|
28
|
+
const bits = r.ub(5);
|
|
29
|
+
r.ub(bits * 4);
|
|
30
|
+
r.align();
|
|
31
|
+
const f1 = r.u8();
|
|
32
|
+
const f2 = r.u8();
|
|
33
|
+
if (f1 & 0x01)
|
|
34
|
+
r.u16();
|
|
35
|
+
if (f2 & 0x80)
|
|
36
|
+
r.cstring();
|
|
37
|
+
if (f1 & 0x01)
|
|
38
|
+
r.u16();
|
|
39
|
+
if (f1 & 0x04)
|
|
40
|
+
r.u32();
|
|
41
|
+
if (f1 & 0x02)
|
|
42
|
+
r.u16();
|
|
43
|
+
if (f2 & 0x20)
|
|
44
|
+
r.bytesView(9);
|
|
45
|
+
r.cstring(); // variable name
|
|
46
|
+
if (!(f1 & 0x80))
|
|
47
|
+
return undefined;
|
|
48
|
+
const offset = r.pos;
|
|
49
|
+
const text = r.cstring();
|
|
50
|
+
return { text, offset, end: r.pos };
|
|
51
|
+
}
|
|
52
|
+
/** Writes resources into `outDir/<kind>/<id>[_<symbol>].<ext>`. */
|
|
53
|
+
export async function exportAssets(swf, outDir, kinds = ALL_ASSET_KINDS) {
|
|
54
|
+
const result = { files: [], skipped: [] };
|
|
55
|
+
const names = symbolNames(swf);
|
|
56
|
+
let jpegTables;
|
|
57
|
+
for (const { tag } of swf.walkTags()) {
|
|
58
|
+
if (tag.code === TagCode.JPEGTables && tag instanceof RawTag)
|
|
59
|
+
jpegTables = tag.data;
|
|
60
|
+
}
|
|
61
|
+
const fileName = (id, ext) => {
|
|
62
|
+
const sym = names.get(id)?.[0];
|
|
63
|
+
const safe = sym ? '_' + sym.replace(/[^A-Za-z0-9_.$-]/g, '_') : '';
|
|
64
|
+
return `${id}${safe}.${ext}`;
|
|
65
|
+
};
|
|
66
|
+
const write = async (kind, name, data) => {
|
|
67
|
+
const dir = join(outDir, kind);
|
|
68
|
+
await mkdir(dir, { recursive: true });
|
|
69
|
+
const p = join(dir, name);
|
|
70
|
+
await writeFile(p, data);
|
|
71
|
+
result.files.push(p);
|
|
72
|
+
};
|
|
73
|
+
for (const { tag } of swf.walkTags()) {
|
|
74
|
+
const id = tag.characterId;
|
|
75
|
+
if (id === undefined)
|
|
76
|
+
continue;
|
|
77
|
+
try {
|
|
78
|
+
if (kinds.includes('images') && IMAGE_TAGS.has(tag.code)) {
|
|
79
|
+
const img = extractImage(tag, jpegTables);
|
|
80
|
+
if (img) {
|
|
81
|
+
await write('images', fileName(id, img.ext), img.data);
|
|
82
|
+
if (img.alphaMask)
|
|
83
|
+
await write('images', fileName(id, 'alpha.png'), img.alphaMask);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (kinds.includes('sounds') && tag.code === TagCode.DefineSound) {
|
|
87
|
+
const s = extractSound(tag);
|
|
88
|
+
if (s)
|
|
89
|
+
await write('sounds', fileName(id, s.ext), s.data);
|
|
90
|
+
}
|
|
91
|
+
else if (kinds.includes('binary') && tag instanceof DefineBinaryDataTag) {
|
|
92
|
+
await write('binary', fileName(id, 'bin'), tag.data);
|
|
93
|
+
}
|
|
94
|
+
else if (kinds.includes('texts') && tag.code === TagCode.DefineEditText && tag instanceof RawTag) {
|
|
95
|
+
const t = editTextOf(tag);
|
|
96
|
+
if (t)
|
|
97
|
+
await write('texts', fileName(id, 'txt'), t.text);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
result.skipped.push({ id, reason: e.message });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
/** Replaces a DefineEditText's initial text. */
|
|
107
|
+
export function replaceEditText(swf, id, text) {
|
|
108
|
+
for (const { tag } of swf.walkTags()) {
|
|
109
|
+
if (tag.characterId !== id || tag.code !== TagCode.DefineEditText || !(tag instanceof RawTag))
|
|
110
|
+
continue;
|
|
111
|
+
const t = editTextOf(tag);
|
|
112
|
+
const w = new ByteWriter(tag.data.length + text.length * 3);
|
|
113
|
+
if (t) {
|
|
114
|
+
w.bytes(tag.data.subarray(0, t.offset));
|
|
115
|
+
w.cstring(text);
|
|
116
|
+
w.bytes(tag.data.subarray(t.end));
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Set HasText and append the text.
|
|
120
|
+
const data = tag.data.slice();
|
|
121
|
+
const r = new ByteReader(data);
|
|
122
|
+
r.u16();
|
|
123
|
+
const bits = r.ub(5);
|
|
124
|
+
r.ub(bits * 4);
|
|
125
|
+
r.align();
|
|
126
|
+
data[r.pos] |= 0x80;
|
|
127
|
+
w.bytes(data);
|
|
128
|
+
w.cstring(text);
|
|
129
|
+
}
|
|
130
|
+
tag.data = w.toBytes();
|
|
131
|
+
return tag;
|
|
132
|
+
}
|
|
133
|
+
throw new Error(`No DefineEditText with id ${id}`);
|
|
134
|
+
}
|
|
135
|
+
/** Replaces the resource with character `id` using a file's contents (type chosen by the tag). */
|
|
136
|
+
export function replaceAsset(swf, id, file) {
|
|
137
|
+
const tag = swf.findCharacter(id);
|
|
138
|
+
if (!tag)
|
|
139
|
+
throw new Error(`No character with id ${id}`);
|
|
140
|
+
if (IMAGE_TAGS.has(tag.code))
|
|
141
|
+
return replaceImage(swf, id, file);
|
|
142
|
+
if (tag.code === TagCode.DefineSound)
|
|
143
|
+
return replaceSound(swf, id, file);
|
|
144
|
+
if (tag instanceof DefineBinaryDataTag) {
|
|
145
|
+
tag.data = file.slice();
|
|
146
|
+
return tag;
|
|
147
|
+
}
|
|
148
|
+
if (tag.code === TagCode.DefineEditText)
|
|
149
|
+
return replaceEditText(swf, id, new TextDecoder().decode(file));
|
|
150
|
+
throw new Error(`Replacing ${tag.name} (id ${id}) is not supported`);
|
|
151
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Swf } from '../swf/swf.js';
|
|
2
|
+
import { RawTag, type Tag } from '../swf/tags.js';
|
|
3
|
+
import { type RgbaImage } from './png.js';
|
|
4
|
+
export declare const IMAGE_TAGS: ReadonlySet<number>;
|
|
5
|
+
export interface ExtractedImage {
|
|
6
|
+
id: number;
|
|
7
|
+
/** File extension: png, jpg or gif. */
|
|
8
|
+
ext: 'png' | 'jpg' | 'gif';
|
|
9
|
+
data: Uint8Array;
|
|
10
|
+
/** Alpha mask (grayscale PNG) for JPEG3/4 images. */
|
|
11
|
+
alphaMask?: Uint8Array;
|
|
12
|
+
width?: number;
|
|
13
|
+
height?: number;
|
|
14
|
+
}
|
|
15
|
+
/** Decodes DefineBitsLossless(2) pixel data to straight RGBA. */
|
|
16
|
+
export declare function decodeLossless(tag: RawTag): RgbaImage;
|
|
17
|
+
/** Extracts the image stored in a bitmap tag. */
|
|
18
|
+
export declare function extractImage(tag: Tag, jpegTables?: Uint8Array): ExtractedImage | undefined;
|
|
19
|
+
/** Reads width/height from a JPEG's SOF marker. */
|
|
20
|
+
export declare function jpegSize(b: Uint8Array): {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
} | undefined;
|
|
24
|
+
/** Builds a DefineBitsLossless2 tag body from an RGBA image. */
|
|
25
|
+
export declare function encodeLossless2(id: number, img: RgbaImage): Uint8Array;
|
|
26
|
+
/**
|
|
27
|
+
* Replaces the bitmap with character `id`. PNG files become
|
|
28
|
+
* DefineBitsLossless2 (exact pixels, alpha); JPEG / GIF files are stored
|
|
29
|
+
* as DefineBitsJPEG2.
|
|
30
|
+
*/
|
|
31
|
+
export declare function replaceImage(swf: Swf, id: number, file: Uint8Array): Tag;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bitmap tags ↔ image files.
|
|
3
|
+
*
|
|
4
|
+
* - DefineBitsLossless / DefineBitsLossless2 ↔ PNG (with alpha)
|
|
5
|
+
* - DefineBits (+ JPEGTables), DefineBitsJPEG2/3/4 → original JPEG / PNG / GIF data.
|
|
6
|
+
* JPEG3/4 alpha channels are exported as a separate grayscale PNG mask.
|
|
7
|
+
*/
|
|
8
|
+
import { deflateSync, inflateSync } from 'node:zlib';
|
|
9
|
+
import { ByteReader } from '../io/reader.js';
|
|
10
|
+
import { ByteWriter } from '../io/writer.js';
|
|
11
|
+
import { TagCode } from '../swf/tag-codes.js';
|
|
12
|
+
import { RawTag } from '../swf/tags.js';
|
|
13
|
+
import { decodePng, encodePng, isPng } from './png.js';
|
|
14
|
+
export const IMAGE_TAGS = new Set([
|
|
15
|
+
TagCode.DefineBits,
|
|
16
|
+
TagCode.DefineBitsJPEG2,
|
|
17
|
+
TagCode.DefineBitsJPEG3,
|
|
18
|
+
TagCode.DefineBitsJPEG4,
|
|
19
|
+
TagCode.DefineBitsLossless,
|
|
20
|
+
TagCode.DefineBitsLossless2,
|
|
21
|
+
]);
|
|
22
|
+
function imageType(b) {
|
|
23
|
+
if (isPng(b))
|
|
24
|
+
return 'png';
|
|
25
|
+
if (b[0] === 0x47 && b[1] === 0x49 && b[2] === 0x46)
|
|
26
|
+
return 'gif';
|
|
27
|
+
return 'jpg';
|
|
28
|
+
}
|
|
29
|
+
/** Removes the erroneous `FF D9 FF D8` prefix some encoders emit and merges JPEGTables. */
|
|
30
|
+
function cleanJpeg(data, tables) {
|
|
31
|
+
let d = data;
|
|
32
|
+
if (d[0] === 0xff && d[1] === 0xd9 && d[2] === 0xff && d[3] === 0xd8)
|
|
33
|
+
d = d.subarray(4);
|
|
34
|
+
// Some files contain an EOI/SOI pair in the middle (tables + image); remove it.
|
|
35
|
+
const parts = [];
|
|
36
|
+
if (tables && tables.length > 4) {
|
|
37
|
+
let t = tables;
|
|
38
|
+
if (t[0] === 0xff && t[1] === 0xd9 && t[2] === 0xff && t[3] === 0xd8)
|
|
39
|
+
t = t.subarray(4);
|
|
40
|
+
// tables: SOI ... EOI → drop trailing EOI; image: SOI ... → drop leading SOI
|
|
41
|
+
parts.push(t[t.length - 2] === 0xff && t[t.length - 1] === 0xd9 ? t.subarray(0, -2) : t);
|
|
42
|
+
parts.push(d[0] === 0xff && d[1] === 0xd8 ? d.subarray(2) : d);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
for (let i = 2; i + 3 < d.length; i++) {
|
|
46
|
+
if (d[i] === 0xff && d[i + 1] === 0xd9 && d[i + 2] === 0xff && d[i + 3] === 0xd8) {
|
|
47
|
+
parts.push(d.subarray(0, i), d.subarray(i + 4));
|
|
48
|
+
d = new Uint8Array(0);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (d.length)
|
|
53
|
+
parts.push(d);
|
|
54
|
+
}
|
|
55
|
+
const out = new Uint8Array(parts.reduce((a, p) => a + p.length, 0));
|
|
56
|
+
let o = 0;
|
|
57
|
+
for (const p of parts) {
|
|
58
|
+
out.set(p, o);
|
|
59
|
+
o += p.length;
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
/** Decodes DefineBitsLossless(2) pixel data to straight RGBA. */
|
|
64
|
+
export function decodeLossless(tag) {
|
|
65
|
+
const r = new ByteReader(tag.data);
|
|
66
|
+
r.u16(); // id
|
|
67
|
+
const format = r.u8();
|
|
68
|
+
const width = r.u16();
|
|
69
|
+
const height = r.u16();
|
|
70
|
+
const alpha = tag.code === TagCode.DefineBitsLossless2;
|
|
71
|
+
const colorTableSize = format === 3 ? r.u8() + 1 : 0;
|
|
72
|
+
const pixels = new Uint8Array(inflateSync(r.rest()));
|
|
73
|
+
const out = new Uint8Array(width * height * 4);
|
|
74
|
+
const unpremul = (d) => {
|
|
75
|
+
const a = out[d + 3];
|
|
76
|
+
if (a > 0 && a < 255) {
|
|
77
|
+
out[d] = Math.min(255, Math.round((out[d] * 255) / a));
|
|
78
|
+
out[d + 1] = Math.min(255, Math.round((out[d + 1] * 255) / a));
|
|
79
|
+
out[d + 2] = Math.min(255, Math.round((out[d + 2] * 255) / a));
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
if (format === 3) {
|
|
83
|
+
const entry = alpha ? 4 : 3;
|
|
84
|
+
const table = pixels.subarray(0, colorTableSize * entry);
|
|
85
|
+
const stride = (width + 3) & ~3;
|
|
86
|
+
const base = colorTableSize * entry;
|
|
87
|
+
for (let y = 0; y < height; y++) {
|
|
88
|
+
for (let x = 0; x < width; x++) {
|
|
89
|
+
const idx = pixels[base + y * stride + x];
|
|
90
|
+
const d = (y * width + x) * 4;
|
|
91
|
+
out[d] = table[idx * entry] ?? 0;
|
|
92
|
+
out[d + 1] = table[idx * entry + 1] ?? 0;
|
|
93
|
+
out[d + 2] = table[idx * entry + 2] ?? 0;
|
|
94
|
+
out[d + 3] = alpha ? (table[idx * entry + 3] ?? 255) : 255;
|
|
95
|
+
if (alpha)
|
|
96
|
+
unpremul(d);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else if (format === 4) {
|
|
101
|
+
const stride = (width * 2 + 3) & ~3;
|
|
102
|
+
for (let y = 0; y < height; y++) {
|
|
103
|
+
for (let x = 0; x < width; x++) {
|
|
104
|
+
const v = (pixels[y * stride + x * 2] << 8) | pixels[y * stride + x * 2 + 1];
|
|
105
|
+
const d = (y * width + x) * 4;
|
|
106
|
+
out[d] = ((v >> 10) & 0x1f) * 255 / 31;
|
|
107
|
+
out[d + 1] = ((v >> 5) & 0x1f) * 255 / 31;
|
|
108
|
+
out[d + 2] = (v & 0x1f) * 255 / 31;
|
|
109
|
+
out[d + 3] = 255;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else if (format === 5) {
|
|
114
|
+
for (let i = 0; i < width * height; i++) {
|
|
115
|
+
const s = i * 4;
|
|
116
|
+
out[s] = pixels[s + 1];
|
|
117
|
+
out[s + 1] = pixels[s + 2];
|
|
118
|
+
out[s + 2] = pixels[s + 3];
|
|
119
|
+
out[s + 3] = alpha ? pixels[s] : 255;
|
|
120
|
+
if (alpha)
|
|
121
|
+
unpremul(s);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else
|
|
125
|
+
throw new Error(`Unsupported lossless bitmap format ${format}`);
|
|
126
|
+
return { width, height, data: out };
|
|
127
|
+
}
|
|
128
|
+
/** Extracts the image stored in a bitmap tag. */
|
|
129
|
+
export function extractImage(tag, jpegTables) {
|
|
130
|
+
if (!(tag instanceof RawTag) || !IMAGE_TAGS.has(tag.code))
|
|
131
|
+
return undefined;
|
|
132
|
+
const id = tag.characterId;
|
|
133
|
+
const r = new ByteReader(tag.data);
|
|
134
|
+
r.u16();
|
|
135
|
+
switch (tag.code) {
|
|
136
|
+
case TagCode.DefineBitsLossless:
|
|
137
|
+
case TagCode.DefineBitsLossless2: {
|
|
138
|
+
const img = decodeLossless(tag);
|
|
139
|
+
return { id, ext: 'png', data: encodePng(img), width: img.width, height: img.height };
|
|
140
|
+
}
|
|
141
|
+
case TagCode.DefineBits:
|
|
142
|
+
return { id, ext: 'jpg', data: cleanJpeg(r.rest(), jpegTables) };
|
|
143
|
+
case TagCode.DefineBitsJPEG2: {
|
|
144
|
+
const d = r.rest();
|
|
145
|
+
const ext = imageType(d);
|
|
146
|
+
return { id, ext, data: ext === 'jpg' ? cleanJpeg(d) : d.slice() };
|
|
147
|
+
}
|
|
148
|
+
case TagCode.DefineBitsJPEG3:
|
|
149
|
+
case TagCode.DefineBitsJPEG4: {
|
|
150
|
+
const alphaOffset = r.u32();
|
|
151
|
+
if (tag.code === TagCode.DefineBitsJPEG4)
|
|
152
|
+
r.u16(); // deblock
|
|
153
|
+
const d = r.bytesView(alphaOffset);
|
|
154
|
+
const ext = imageType(d);
|
|
155
|
+
const result = { id, ext, data: ext === 'jpg' ? cleanJpeg(d) : d.slice() };
|
|
156
|
+
const alphaZ = r.rest();
|
|
157
|
+
if (ext === 'jpg' && alphaZ.length) {
|
|
158
|
+
const dims = jpegSize(result.data);
|
|
159
|
+
if (dims) {
|
|
160
|
+
const a = new Uint8Array(inflateSync(alphaZ));
|
|
161
|
+
const rgba = new Uint8Array(dims.width * dims.height * 4);
|
|
162
|
+
for (let i = 0; i < dims.width * dims.height; i++) {
|
|
163
|
+
rgba[i * 4] = rgba[i * 4 + 1] = rgba[i * 4 + 2] = a[i] ?? 255;
|
|
164
|
+
rgba[i * 4 + 3] = 255;
|
|
165
|
+
}
|
|
166
|
+
result.alphaMask = encodePng({ width: dims.width, height: dims.height, data: rgba });
|
|
167
|
+
result.width = dims.width;
|
|
168
|
+
result.height = dims.height;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
/** Reads width/height from a JPEG's SOF marker. */
|
|
177
|
+
export function jpegSize(b) {
|
|
178
|
+
let i = 2;
|
|
179
|
+
while (i + 9 < b.length) {
|
|
180
|
+
if (b[i] !== 0xff) {
|
|
181
|
+
i++;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
const marker = b[i + 1];
|
|
185
|
+
if (marker === 0xd8 || marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) {
|
|
186
|
+
i += 2;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const len = (b[i + 2] << 8) | b[i + 3];
|
|
190
|
+
if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
|
|
191
|
+
return { height: (b[i + 5] << 8) | b[i + 6], width: (b[i + 7] << 8) | b[i + 8] };
|
|
192
|
+
}
|
|
193
|
+
i += 2 + len;
|
|
194
|
+
}
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
/** Builds a DefineBitsLossless2 tag body from an RGBA image. */
|
|
198
|
+
export function encodeLossless2(id, img) {
|
|
199
|
+
const { width, height, data } = img;
|
|
200
|
+
const argb = new Uint8Array(width * height * 4);
|
|
201
|
+
for (let i = 0; i < width * height; i++) {
|
|
202
|
+
const a = data[i * 4 + 3];
|
|
203
|
+
const s = i * 4;
|
|
204
|
+
argb[s] = a;
|
|
205
|
+
argb[s + 1] = Math.round((data[s] * a) / 255);
|
|
206
|
+
argb[s + 2] = Math.round((data[s + 1] * a) / 255);
|
|
207
|
+
argb[s + 3] = Math.round((data[s + 2] * a) / 255);
|
|
208
|
+
}
|
|
209
|
+
const w = new ByteWriter(argb.length / 2 + 16);
|
|
210
|
+
w.u16(id).u8(5).u16(width).u16(height);
|
|
211
|
+
w.bytes(new Uint8Array(deflateSync(argb, { level: 9 })));
|
|
212
|
+
return w.toBytes();
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Replaces the bitmap with character `id`. PNG files become
|
|
216
|
+
* DefineBitsLossless2 (exact pixels, alpha); JPEG / GIF files are stored
|
|
217
|
+
* as DefineBitsJPEG2.
|
|
218
|
+
*/
|
|
219
|
+
export function replaceImage(swf, id, file) {
|
|
220
|
+
for (const { tag, parent, index } of swf.walkTags()) {
|
|
221
|
+
if (tag.characterId !== id)
|
|
222
|
+
continue;
|
|
223
|
+
if (!IMAGE_TAGS.has(tag.code))
|
|
224
|
+
throw new Error(`Character ${id} is a ${tag.name}, not a bitmap`);
|
|
225
|
+
let next;
|
|
226
|
+
if (isPng(file))
|
|
227
|
+
next = new RawTag(TagCode.DefineBitsLossless2, encodeLossless2(id, decodePng(file)));
|
|
228
|
+
else {
|
|
229
|
+
const w = new ByteWriter(file.length + 2);
|
|
230
|
+
w.u16(id).bytes(file);
|
|
231
|
+
next = new RawTag(TagCode.DefineBitsJPEG2, w.toBytes());
|
|
232
|
+
}
|
|
233
|
+
next.longHeader = true;
|
|
234
|
+
(parent ? parent.tags : swf.tags)[index] = next;
|
|
235
|
+
return next;
|
|
236
|
+
}
|
|
237
|
+
throw new Error(`No character with id ${id}`);
|
|
238
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface RgbaImage {
|
|
2
|
+
width: number;
|
|
3
|
+
height: number;
|
|
4
|
+
/** Straight (non-premultiplied) RGBA, row-major. */
|
|
5
|
+
data: Uint8Array;
|
|
6
|
+
}
|
|
7
|
+
export declare function crc32(data: Uint8Array, crc?: number): number;
|
|
8
|
+
export declare function isPng(b: Uint8Array): boolean;
|
|
9
|
+
export declare function encodePng(img: RgbaImage, options?: {
|
|
10
|
+
level?: number;
|
|
11
|
+
}): Uint8Array;
|
|
12
|
+
export declare function decodePng(bytes: Uint8Array): RgbaImage;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal PNG encoder / decoder (8-bit RGBA output, all standard colour types
|
|
3
|
+
* with bit depth 8 on input; 1/2/4-bit palette and grayscale also accepted).
|
|
4
|
+
*/
|
|
5
|
+
import { deflateSync, inflateSync } from 'node:zlib';
|
|
6
|
+
const CRC_TABLE = (() => {
|
|
7
|
+
const t = new Uint32Array(256);
|
|
8
|
+
for (let n = 0; n < 256; n++) {
|
|
9
|
+
let c = n;
|
|
10
|
+
for (let k = 0; k < 8; k++)
|
|
11
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
12
|
+
t[n] = c >>> 0;
|
|
13
|
+
}
|
|
14
|
+
return t;
|
|
15
|
+
})();
|
|
16
|
+
export function crc32(data, crc = 0xffffffff) {
|
|
17
|
+
for (let i = 0; i < data.length; i++)
|
|
18
|
+
crc = CRC_TABLE[(crc ^ data[i]) & 0xff] ^ (crc >>> 8);
|
|
19
|
+
return crc >>> 0;
|
|
20
|
+
}
|
|
21
|
+
const SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
22
|
+
export function isPng(b) {
|
|
23
|
+
return SIGNATURE.every((v, i) => b[i] === v);
|
|
24
|
+
}
|
|
25
|
+
export function encodePng(img, options = {}) {
|
|
26
|
+
const { width, height, data } = img;
|
|
27
|
+
const stride = width * 4;
|
|
28
|
+
const raw = new Uint8Array((stride + 1) * height);
|
|
29
|
+
for (let y = 0; y < height; y++) {
|
|
30
|
+
raw[y * (stride + 1)] = 0; // filter: none
|
|
31
|
+
raw.set(data.subarray(y * stride, (y + 1) * stride), y * (stride + 1) + 1);
|
|
32
|
+
}
|
|
33
|
+
const idat = deflateSync(raw, { level: options.level ?? 9 });
|
|
34
|
+
const chunks = [];
|
|
35
|
+
const chunk = (type, body) => {
|
|
36
|
+
const out = new Uint8Array(12 + body.length);
|
|
37
|
+
const dv = new DataView(out.buffer);
|
|
38
|
+
dv.setUint32(0, body.length);
|
|
39
|
+
for (let i = 0; i < 4; i++)
|
|
40
|
+
out[4 + i] = type.charCodeAt(i);
|
|
41
|
+
out.set(body, 8);
|
|
42
|
+
dv.setUint32(8 + body.length, crc32(out.subarray(4, 8 + body.length)) ^ 0xffffffff);
|
|
43
|
+
chunks.push(out);
|
|
44
|
+
};
|
|
45
|
+
const ihdr = new Uint8Array(13);
|
|
46
|
+
const dv = new DataView(ihdr.buffer);
|
|
47
|
+
dv.setUint32(0, width);
|
|
48
|
+
dv.setUint32(4, height);
|
|
49
|
+
ihdr[8] = 8; // bit depth
|
|
50
|
+
ihdr[9] = 6; // RGBA
|
|
51
|
+
chunk('IHDR', ihdr);
|
|
52
|
+
chunk('IDAT', new Uint8Array(idat));
|
|
53
|
+
chunk('IEND', new Uint8Array(0));
|
|
54
|
+
const total = 8 + chunks.reduce((a, c) => a + c.length, 0);
|
|
55
|
+
const out = new Uint8Array(total);
|
|
56
|
+
out.set(SIGNATURE, 0);
|
|
57
|
+
let o = 8;
|
|
58
|
+
for (const c of chunks) {
|
|
59
|
+
out.set(c, o);
|
|
60
|
+
o += c.length;
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
function paeth(a, b, c) {
|
|
65
|
+
const p = a + b - c;
|
|
66
|
+
const pa = Math.abs(p - a);
|
|
67
|
+
const pb = Math.abs(p - b);
|
|
68
|
+
const pc = Math.abs(p - c);
|
|
69
|
+
return pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
|
|
70
|
+
}
|
|
71
|
+
export function decodePng(bytes) {
|
|
72
|
+
if (!isPng(bytes))
|
|
73
|
+
throw new Error('Not a PNG file');
|
|
74
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
75
|
+
let pos = 8;
|
|
76
|
+
let width = 0;
|
|
77
|
+
let height = 0;
|
|
78
|
+
let depth = 8;
|
|
79
|
+
let colorType = 6;
|
|
80
|
+
let interlace = 0;
|
|
81
|
+
let palette;
|
|
82
|
+
let trns;
|
|
83
|
+
const idat = [];
|
|
84
|
+
while (pos + 8 <= bytes.length) {
|
|
85
|
+
const len = dv.getUint32(pos);
|
|
86
|
+
const type = String.fromCharCode(...bytes.subarray(pos + 4, pos + 8));
|
|
87
|
+
const body = bytes.subarray(pos + 8, pos + 8 + len);
|
|
88
|
+
if (type === 'IHDR') {
|
|
89
|
+
width = dv.getUint32(pos + 8);
|
|
90
|
+
height = dv.getUint32(pos + 12);
|
|
91
|
+
depth = body[8];
|
|
92
|
+
colorType = body[9];
|
|
93
|
+
interlace = body[12];
|
|
94
|
+
}
|
|
95
|
+
else if (type === 'PLTE')
|
|
96
|
+
palette = body;
|
|
97
|
+
else if (type === 'tRNS')
|
|
98
|
+
trns = body;
|
|
99
|
+
else if (type === 'IDAT')
|
|
100
|
+
idat.push(body);
|
|
101
|
+
else if (type === 'IEND')
|
|
102
|
+
break;
|
|
103
|
+
pos += 12 + len;
|
|
104
|
+
}
|
|
105
|
+
if (interlace)
|
|
106
|
+
throw new Error('Interlaced PNG files are not supported');
|
|
107
|
+
if (depth === 16)
|
|
108
|
+
throw new Error('16-bit PNG files are not supported');
|
|
109
|
+
const channels = { 0: 1, 2: 3, 3: 1, 4: 2, 6: 4 }[colorType];
|
|
110
|
+
if (!channels)
|
|
111
|
+
throw new Error(`Unsupported PNG colour type ${colorType}`);
|
|
112
|
+
const total = idat.reduce((a, c) => a + c.length, 0);
|
|
113
|
+
const z = new Uint8Array(total);
|
|
114
|
+
let o = 0;
|
|
115
|
+
for (const c of idat) {
|
|
116
|
+
z.set(c, o);
|
|
117
|
+
o += c.length;
|
|
118
|
+
}
|
|
119
|
+
const raw = new Uint8Array(inflateSync(z));
|
|
120
|
+
const bpp = Math.max(1, (channels * depth) >> 3);
|
|
121
|
+
const stride = Math.ceil((width * channels * depth) / 8);
|
|
122
|
+
const pixels = new Uint8Array(stride * height);
|
|
123
|
+
let prev = new Uint8Array(stride);
|
|
124
|
+
for (let y = 0; y < height; y++) {
|
|
125
|
+
const filter = raw[y * (stride + 1)];
|
|
126
|
+
const line = raw.subarray(y * (stride + 1) + 1, (y + 1) * (stride + 1));
|
|
127
|
+
const cur = pixels.subarray(y * stride, (y + 1) * stride);
|
|
128
|
+
for (let x = 0; x < stride; x++) {
|
|
129
|
+
const a = x >= bpp ? cur[x - bpp] : 0;
|
|
130
|
+
const b = prev[x];
|
|
131
|
+
const c = x >= bpp ? prev[x - bpp] : 0;
|
|
132
|
+
let v = line[x];
|
|
133
|
+
switch (filter) {
|
|
134
|
+
case 1:
|
|
135
|
+
v += a;
|
|
136
|
+
break;
|
|
137
|
+
case 2:
|
|
138
|
+
v += b;
|
|
139
|
+
break;
|
|
140
|
+
case 3:
|
|
141
|
+
v += (a + b) >> 1;
|
|
142
|
+
break;
|
|
143
|
+
case 4:
|
|
144
|
+
v += paeth(a, b, c);
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
cur[x] = v & 0xff;
|
|
148
|
+
}
|
|
149
|
+
prev = cur;
|
|
150
|
+
}
|
|
151
|
+
const out = new Uint8Array(width * height * 4);
|
|
152
|
+
const sample = (row, i) => {
|
|
153
|
+
if (depth === 8)
|
|
154
|
+
return row[i];
|
|
155
|
+
const bit = i * depth;
|
|
156
|
+
return (row[bit >> 3] >> (8 - depth - (bit & 7))) & ((1 << depth) - 1);
|
|
157
|
+
};
|
|
158
|
+
const scale = depth === 8 ? 1 : 255 / ((1 << depth) - 1);
|
|
159
|
+
for (let y = 0; y < height; y++) {
|
|
160
|
+
const row = pixels.subarray(y * stride, (y + 1) * stride);
|
|
161
|
+
for (let x = 0; x < width; x++) {
|
|
162
|
+
const d = (y * width + x) * 4;
|
|
163
|
+
switch (colorType) {
|
|
164
|
+
case 0: {
|
|
165
|
+
const g = sample(row, x);
|
|
166
|
+
const v = Math.round(g * scale);
|
|
167
|
+
out[d] = out[d + 1] = out[d + 2] = v;
|
|
168
|
+
out[d + 3] = trns && trns.length >= 2 && ((trns[0] << 8) | trns[1]) === g ? 0 : 255;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case 2:
|
|
172
|
+
out[d] = row[x * 3];
|
|
173
|
+
out[d + 1] = row[x * 3 + 1];
|
|
174
|
+
out[d + 2] = row[x * 3 + 2];
|
|
175
|
+
out[d + 3] = 255;
|
|
176
|
+
break;
|
|
177
|
+
case 3: {
|
|
178
|
+
const idx = sample(row, x);
|
|
179
|
+
out[d] = palette?.[idx * 3] ?? 0;
|
|
180
|
+
out[d + 1] = palette?.[idx * 3 + 1] ?? 0;
|
|
181
|
+
out[d + 2] = palette?.[idx * 3 + 2] ?? 0;
|
|
182
|
+
out[d + 3] = trns && idx < trns.length ? trns[idx] : 255;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
case 4:
|
|
186
|
+
out[d] = out[d + 1] = out[d + 2] = row[x * 2];
|
|
187
|
+
out[d + 3] = row[x * 2 + 1];
|
|
188
|
+
break;
|
|
189
|
+
case 6:
|
|
190
|
+
out.set(row.subarray(x * 4, x * 4 + 4), d);
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return { width, height, data: out };
|
|
196
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Swf } from '../swf/swf.js';
|
|
2
|
+
import { RawTag, type Tag } from '../swf/tags.js';
|
|
3
|
+
export declare const SOUND_RATES: number[];
|
|
4
|
+
export interface SoundInfo {
|
|
5
|
+
id: number;
|
|
6
|
+
/** 0 native PCM, 1 ADPCM, 2 MP3, 3 PCM LE, 4-6 Nellymoser, 11 Speex. */
|
|
7
|
+
format: number;
|
|
8
|
+
rate: number;
|
|
9
|
+
bits: 8 | 16;
|
|
10
|
+
channels: 1 | 2;
|
|
11
|
+
sampleCount: number;
|
|
12
|
+
data: Uint8Array;
|
|
13
|
+
}
|
|
14
|
+
export declare function parseDefineSound(tag: RawTag): SoundInfo;
|
|
15
|
+
export declare function encodeWav(pcm: Uint8Array, rate: number, channels: number, bits: number): Uint8Array;
|
|
16
|
+
/** Decodes SWF ADPCM to 16-bit little-endian PCM. */
|
|
17
|
+
export declare function decodeAdpcm(data: Uint8Array, channels: number, sampleCount: number): Uint8Array;
|
|
18
|
+
export interface ExtractedSound {
|
|
19
|
+
id: number;
|
|
20
|
+
ext: 'mp3' | 'wav' | 'nelly' | 'speex';
|
|
21
|
+
data: Uint8Array;
|
|
22
|
+
info: Omit<SoundInfo, 'data'>;
|
|
23
|
+
}
|
|
24
|
+
export declare function extractSound(tag: Tag): ExtractedSound | undefined;
|
|
25
|
+
export interface Mp3Info {
|
|
26
|
+
rate: number;
|
|
27
|
+
channels: 1 | 2;
|
|
28
|
+
sampleCount: number;
|
|
29
|
+
/** Offset of the first frame (after ID3v2). */
|
|
30
|
+
start: number;
|
|
31
|
+
end: number;
|
|
32
|
+
}
|
|
33
|
+
export declare function parseMp3(b: Uint8Array): Mp3Info;
|
|
34
|
+
/** Builds a DefineSound body from an MP3 or WAV file. */
|
|
35
|
+
export declare function encodeDefineSound(id: number, file: Uint8Array): Uint8Array;
|
|
36
|
+
/** Replaces DefineSound `id` with an MP3 or WAV file. */
|
|
37
|
+
export declare function replaceSound(swf: Swf, id: number, file: Uint8Array): Tag;
|