pyflashkit 1.0.0__py3-none-any.whl

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.
Files changed (48) hide show
  1. flashkit/__init__.py +54 -0
  2. flashkit/abc/__init__.py +79 -0
  3. flashkit/abc/builder.py +847 -0
  4. flashkit/abc/constants.py +198 -0
  5. flashkit/abc/disasm.py +364 -0
  6. flashkit/abc/parser.py +434 -0
  7. flashkit/abc/types.py +275 -0
  8. flashkit/abc/writer.py +230 -0
  9. flashkit/analysis/__init__.py +28 -0
  10. flashkit/analysis/call_graph.py +317 -0
  11. flashkit/analysis/inheritance.py +267 -0
  12. flashkit/analysis/references.py +371 -0
  13. flashkit/analysis/strings.py +299 -0
  14. flashkit/cli/__init__.py +75 -0
  15. flashkit/cli/_util.py +52 -0
  16. flashkit/cli/build.py +36 -0
  17. flashkit/cli/callees.py +30 -0
  18. flashkit/cli/callers.py +30 -0
  19. flashkit/cli/class_cmd.py +83 -0
  20. flashkit/cli/classes.py +71 -0
  21. flashkit/cli/disasm.py +77 -0
  22. flashkit/cli/extract.py +36 -0
  23. flashkit/cli/info.py +41 -0
  24. flashkit/cli/packages.py +30 -0
  25. flashkit/cli/refs.py +31 -0
  26. flashkit/cli/strings.py +58 -0
  27. flashkit/cli/tags.py +32 -0
  28. flashkit/cli/tree.py +52 -0
  29. flashkit/errors.py +33 -0
  30. flashkit/info/__init__.py +31 -0
  31. flashkit/info/class_info.py +176 -0
  32. flashkit/info/member_info.py +275 -0
  33. flashkit/info/package_info.py +60 -0
  34. flashkit/search/__init__.py +16 -0
  35. flashkit/search/search.py +456 -0
  36. flashkit/swf/__init__.py +66 -0
  37. flashkit/swf/builder.py +283 -0
  38. flashkit/swf/parser.py +164 -0
  39. flashkit/swf/tags.py +120 -0
  40. flashkit/workspace/__init__.py +20 -0
  41. flashkit/workspace/resource.py +189 -0
  42. flashkit/workspace/workspace.py +232 -0
  43. pyflashkit-1.0.0.dist-info/METADATA +281 -0
  44. pyflashkit-1.0.0.dist-info/RECORD +48 -0
  45. pyflashkit-1.0.0.dist-info/WHEEL +5 -0
  46. pyflashkit-1.0.0.dist-info/entry_points.txt +2 -0
  47. pyflashkit-1.0.0.dist-info/licenses/LICENSE +21 -0
  48. pyflashkit-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,283 @@
1
+ """
2
+ SWF container builder.
3
+
4
+ Provides functions to serialize SWF tags, rebuild complete SWF files,
5
+ create new DoABC2 tags, and build SWF files from scratch.
6
+
7
+ Usage — modify an existing SWF::
8
+
9
+ from flashkit.swf.parser import parse_swf
10
+ from flashkit.swf.builder import rebuild_swf, make_doabc2_tag
11
+
12
+ header, tags, version, length = parse_swf(swf_bytes)
13
+ new_tag = make_doabc2_tag("MyCode", abc_bytes)
14
+ tags.insert(-1, new_tag)
15
+ output = rebuild_swf(header, tags, compress=True)
16
+
17
+ Usage — build a SWF from scratch::
18
+
19
+ from flashkit.swf.builder import SwfBuilder
20
+
21
+ swf = SwfBuilder(version=40, width=800, height=600, fps=30)
22
+ swf.add_abc("MainCode", abc_bytes)
23
+ swf.set_document_class("com.example.Main")
24
+ output = swf.build(compress=True)
25
+
26
+ Reference: SWF File Format Specification v19, Chapter 2.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import struct
32
+ import zlib
33
+
34
+ from .tags import SWFTag, TAG_DO_ABC, TAG_DO_ABC2, TAG_END, TAG_SYMBOL_CLASS
35
+
36
+
37
+ def build_tag_bytes(tag: SWFTag) -> bytes:
38
+ """Serialize a single SWFTag to its binary representation.
39
+
40
+ Uses short headers (2 bytes) for small non-ABC tags and long headers
41
+ (6 bytes) for large payloads or DoABC/DoABC2 tags.
42
+
43
+ Args:
44
+ tag: The SWFTag to serialize.
45
+
46
+ Returns:
47
+ Raw bytes for this tag (header + payload).
48
+ """
49
+ payload_len = len(tag.payload)
50
+ if payload_len < 0x3F and tag.tag_type not in (TAG_DO_ABC, TAG_DO_ABC2):
51
+ # Short header — only for small non-ABC tags
52
+ header = struct.pack("<H", (tag.tag_type << 6) | payload_len)
53
+ return header + tag.payload
54
+ else:
55
+ # Long header
56
+ header = struct.pack("<H", (tag.tag_type << 6) | 0x3F)
57
+ header += struct.pack("<I", payload_len)
58
+ return header + tag.payload
59
+
60
+
61
+ def rebuild_swf(
62
+ header_bytes: bytes,
63
+ tags: list[SWFTag],
64
+ compress: bool = True,
65
+ ) -> bytes:
66
+ """Rebuild a complete SWF file from header and tags.
67
+
68
+ Recomputes the file length field and optionally zlib-compresses
69
+ the output.
70
+
71
+ Args:
72
+ header_bytes: Original SWF header (from ``parse_swf()``).
73
+ tags: List of SWFTag objects.
74
+ compress: If True, output CWS (zlib-compressed). Default True.
75
+
76
+ Returns:
77
+ Complete SWF file bytes.
78
+ """
79
+ body = b""
80
+ for tag in tags:
81
+ body += build_tag_bytes(tag)
82
+
83
+ raw = header_bytes + body
84
+ # Update file length field (bytes 4-7)
85
+ raw = raw[:4] + struct.pack("<I", len(raw)) + raw[8:]
86
+
87
+ if compress:
88
+ return b"CWS" + raw[3:8] + zlib.compress(raw[8:], 9)
89
+ else:
90
+ return raw
91
+
92
+
93
+ def make_doabc2_tag(
94
+ name: str,
95
+ abc_data: bytes,
96
+ lazy_init: bool = True,
97
+ ) -> SWFTag:
98
+ """Create a DoABC2 tag (type 82).
99
+
100
+ DoABC2 tags contain flags, a null-terminated name, and the ABC
101
+ bytecode data.
102
+
103
+ Args:
104
+ name: Name string for the ABC block.
105
+ abc_data: Raw ABC bytecode bytes.
106
+ lazy_init: If True, set the lazy initialization flag. Default True.
107
+
108
+ Returns:
109
+ A new SWFTag with tag_type=82.
110
+ """
111
+ flags = struct.pack("<I", 1 if lazy_init else 0)
112
+ name_bytes = name.encode("utf-8") + b"\x00"
113
+ payload = flags + name_bytes + abc_data
114
+ return SWFTag(tag_type=TAG_DO_ABC2, payload=payload, name=name)
115
+
116
+
117
+ def make_symbol_class_tag(symbols: list[tuple[int, str]]) -> SWFTag:
118
+ """Create a SymbolClass tag (type 76).
119
+
120
+ Maps character IDs to class names. Character ID 0 with a class
121
+ name defines the document class.
122
+
123
+ Args:
124
+ symbols: List of (character_id, class_name) pairs.
125
+
126
+ Returns:
127
+ A new SWFTag with tag_type=76.
128
+ """
129
+ payload = struct.pack("<H", len(symbols))
130
+ for char_id, name in symbols:
131
+ payload += struct.pack("<H", char_id)
132
+ payload += name.encode("utf-8") + b"\x00"
133
+ return SWFTag(tag_type=TAG_SYMBOL_CLASS, payload=payload)
134
+
135
+
136
+ def make_end_tag() -> SWFTag:
137
+ """Create an End tag (type 0)."""
138
+ return SWFTag(tag_type=TAG_END, payload=b"")
139
+
140
+
141
+ class SwfBuilder:
142
+ """High-level builder for constructing SWF files from scratch.
143
+
144
+ Handles the header, RECT, frame rate, and tag assembly.
145
+ Add ABC blocks and symbol mappings, then call ``build()``.
146
+
147
+ Args:
148
+ version: SWF version number. Default 40.
149
+ width: Stage width in pixels. Default 800.
150
+ height: Stage height in pixels. Default 600.
151
+ fps: Frame rate. Default 24.
152
+ frame_count: Total frames. Default 1.
153
+ """
154
+
155
+ def __init__(
156
+ self,
157
+ version: int = 40,
158
+ width: int = 800,
159
+ height: int = 600,
160
+ fps: int = 24,
161
+ frame_count: int = 1,
162
+ ) -> None:
163
+ self.version = version
164
+ self.width = width
165
+ self.height = height
166
+ self.fps = fps
167
+ self.frame_count = frame_count
168
+ self._tags: list[SWFTag] = []
169
+ self._symbols: list[tuple[int, str]] = []
170
+
171
+ def add_tag(self, tag: SWFTag) -> None:
172
+ """Add a raw SWF tag.
173
+
174
+ Args:
175
+ tag: Any SWFTag to include before the End tag.
176
+ """
177
+ self._tags.append(tag)
178
+
179
+ def add_abc(self, name: str, abc_data: bytes,
180
+ lazy_init: bool = True) -> None:
181
+ """Add an ABC bytecode block as a DoABC2 tag.
182
+
183
+ Args:
184
+ name: Name for the ABC block.
185
+ abc_data: Serialized ABC bytes.
186
+ lazy_init: Whether to set the lazy init flag.
187
+ """
188
+ self._tags.append(make_doabc2_tag(name, abc_data, lazy_init))
189
+
190
+ def add_symbol(self, char_id: int, class_name: str) -> None:
191
+ """Map a character ID to a class name.
192
+
193
+ Args:
194
+ char_id: Character ID (0 for document class).
195
+ class_name: Fully qualified class name.
196
+ """
197
+ self._symbols.append((char_id, class_name))
198
+
199
+ def set_document_class(self, class_name: str) -> None:
200
+ """Set the document class (character ID 0).
201
+
202
+ Args:
203
+ class_name: Fully qualified class name.
204
+ """
205
+ self.add_symbol(0, class_name)
206
+
207
+ def _build_header(self) -> bytes:
208
+ """Build the SWF header bytes (before tags)."""
209
+ # Encode RECT in twips (1 pixel = 20 twips)
210
+ xmax = self.width * 20
211
+ ymax = self.height * 20
212
+ # Calculate nbits needed
213
+ max_val = max(xmax, ymax)
214
+ nbits = max_val.bit_length() if max_val > 0 else 1
215
+
216
+ # Pack RECT as bits: 5-bit nbits + 4 fields of nbits each
217
+ # Xmin=0, Xmax, Ymin=0, Ymax
218
+ total_bits = 5 + 4 * nbits
219
+ total_bytes = (total_bits + 7) // 8
220
+ rect = bytearray(total_bytes)
221
+
222
+ # Write bits
223
+ bit_pos = 0
224
+
225
+ def write_bits(value: int, count: int) -> None:
226
+ nonlocal bit_pos
227
+ for i in range(count - 1, -1, -1):
228
+ byte_idx = bit_pos // 8
229
+ bit_idx = 7 - (bit_pos % 8)
230
+ if value & (1 << i):
231
+ rect[byte_idx] |= (1 << bit_idx)
232
+ bit_pos += 1
233
+
234
+ write_bits(nbits, 5)
235
+ write_bits(0, nbits) # Xmin
236
+ write_bits(xmax, nbits) # Xmax
237
+ write_bits(0, nbits) # Ymin
238
+ write_bits(ymax, nbits) # Ymax
239
+
240
+ # Frame rate (8.8 fixed point) + frame count
241
+ frame_rate = struct.pack("<BB", 0, self.fps)
242
+ frame_count = struct.pack("<H", self.frame_count)
243
+
244
+ header = bytearray()
245
+ header += b"FWS"
246
+ header += bytes([self.version])
247
+ header += struct.pack("<I", 0) # file length (filled later)
248
+ header += rect
249
+ header += frame_rate
250
+ header += frame_count
251
+ return bytes(header)
252
+
253
+ def build(self, compress: bool = True) -> bytes:
254
+ """Build the complete SWF file.
255
+
256
+ Args:
257
+ compress: If True, produce CWS (zlib-compressed). Default True.
258
+
259
+ Returns:
260
+ Complete SWF file bytes.
261
+ """
262
+ header = self._build_header()
263
+
264
+ # Assemble tags
265
+ tags_bytes = bytearray()
266
+ for tag in self._tags:
267
+ tags_bytes += build_tag_bytes(tag)
268
+
269
+ # Add SymbolClass if symbols were defined
270
+ if self._symbols:
271
+ tags_bytes += build_tag_bytes(make_symbol_class_tag(self._symbols))
272
+
273
+ # End tag
274
+ tags_bytes += build_tag_bytes(make_end_tag())
275
+
276
+ # Combine and set file length
277
+ raw = bytearray(header) + tags_bytes
278
+ struct.pack_into("<I", raw, 4, len(raw))
279
+
280
+ if compress:
281
+ return b"CWS" + bytes(raw[3:8]) + zlib.compress(bytes(raw[8:]), 9)
282
+ else:
283
+ return bytes(raw)
flashkit/swf/parser.py ADDED
@@ -0,0 +1,164 @@
1
+ """
2
+ SWF container parser.
3
+
4
+ Parses SWF files (compressed or uncompressed) into a header and a list
5
+ of ``SWFTag`` objects. Handles CWS (zlib-compressed) and FWS (uncompressed)
6
+ signatures.
7
+
8
+ Usage::
9
+
10
+ from flashkit.swf.parser import parse_swf
11
+
12
+ with open("application.swf", "rb") as f:
13
+ header, tags, version, file_length = parse_swf(f.read())
14
+
15
+ for tag in tags:
16
+ print(f"{tag.type_name}: {len(tag.payload)} bytes")
17
+
18
+ Reference: SWF File Format Specification v19, Chapter 1-2.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import struct
24
+ import zlib
25
+
26
+ from ..errors import SWFParseError
27
+ from .tags import SWFTag, TAG_NAMES, TAG_END, TAG_DO_ABC, TAG_DO_ABC2, TAG_SYMBOL_CLASS
28
+
29
+
30
+ def _parse_rect_size(data: bytes, offset: int) -> int:
31
+ """Calculate how many bytes a RECT structure occupies.
32
+
33
+ The RECT is a bit-packed structure where the first 5 bits encode
34
+ the number of bits per field (Xmin, Xmax, Ymin, Ymax).
35
+
36
+ Args:
37
+ data: Raw SWF bytes.
38
+ offset: Byte offset where the RECT starts.
39
+
40
+ Returns:
41
+ Number of bytes the RECT occupies.
42
+ """
43
+ nbits = (data[offset] >> 3) & 0x1F
44
+ total_bits = 5 + 4 * nbits
45
+ return (total_bits + 7) // 8
46
+
47
+
48
+ def parse_swf(data: bytes) -> tuple[bytes, list[SWFTag], int, int]:
49
+ """Parse a SWF file (compressed or uncompressed).
50
+
51
+ Handles both CWS (zlib-compressed) and FWS (uncompressed) formats.
52
+ The returned header bytes include everything up to the first tag
53
+ (signature, version, file length, RECT, frame rate, frame count).
54
+
55
+ Args:
56
+ data: Raw SWF file bytes.
57
+
58
+ Returns:
59
+ Tuple of (header_bytes, tags, version, file_length).
60
+
61
+ Raises:
62
+ SWFParseError: If the data is not a valid SWF file.
63
+ """
64
+ if not data:
65
+ raise SWFParseError("SWF data is empty")
66
+ if len(data) < 8:
67
+ raise SWFParseError(
68
+ f"SWF data too short ({len(data)} bytes, minimum 8)")
69
+
70
+ sig = data[:3]
71
+ if sig == b"CWS":
72
+ try:
73
+ raw = data[:8] + zlib.decompress(data[8:])
74
+ except zlib.error as e:
75
+ raise SWFParseError(
76
+ f"Failed to decompress CWS data: {e}") from e
77
+ raw = b"FWS" + raw[3:] # fix signature to uncompressed
78
+ elif sig == b"FWS":
79
+ raw = data
80
+ else:
81
+ raise SWFParseError(f"Not a SWF file (signature: {sig!r})")
82
+
83
+ try:
84
+ version = raw[3]
85
+ file_length = struct.unpack_from("<I", raw, 4)[0]
86
+
87
+ # RECT + frame rate (2 bytes) + frame count (2 bytes)
88
+ rect_size = _parse_rect_size(raw, 8)
89
+ header_end = 8 + rect_size + 4
90
+ header_bytes = raw[:header_end]
91
+
92
+ # Parse tags
93
+ tags: list[SWFTag] = []
94
+ pos = header_end
95
+ while pos < len(raw) - 1:
96
+ tag_raw = struct.unpack_from("<H", raw, pos)[0]
97
+ tag_type = (tag_raw >> 6) & 0x3FF
98
+ tag_len = tag_raw & 0x3F
99
+ header_size = 2
100
+
101
+ if tag_len == 0x3F:
102
+ # Extended length: next 4 bytes are the actual length
103
+ tag_len = struct.unpack_from("<I", raw, pos + 2)[0]
104
+ header_size = 6
105
+
106
+ payload = raw[pos + header_size: pos + header_size + tag_len]
107
+ tag = SWFTag(tag_type=tag_type, payload=payload)
108
+
109
+ # Extract name from DoABC2 tags (4-byte flags + null-terminated name)
110
+ if tag_type == TAG_DO_ABC2 and len(payload) > 4:
111
+ null_idx = payload.index(0, 4)
112
+ tag.name = payload[4:null_idx].decode("utf-8", errors="replace")
113
+
114
+ tags.append(tag)
115
+
116
+ if tag_type == TAG_END:
117
+ break
118
+ pos += header_size + tag_len
119
+
120
+ except SWFParseError:
121
+ raise
122
+ except (IndexError, struct.error, ValueError, OverflowError) as e:
123
+ raise SWFParseError(f"Corrupted SWF data: {e}") from e
124
+
125
+ return header_bytes, tags, version, file_length
126
+
127
+
128
+ def print_tags(tags: list[SWFTag]) -> None:
129
+ """Pretty-print a SWF tag list to stdout.
130
+
131
+ Shows tag index, type, name, size, and extra info for known tag types
132
+ (ABC version for DoABC, symbol names for SymbolClass, etc.).
133
+
134
+ Args:
135
+ tags: List of SWFTag objects from ``parse_swf()``.
136
+ """
137
+ for i, tag in enumerate(tags):
138
+ extra = ""
139
+ if tag.tag_type == TAG_DO_ABC2:
140
+ extra = f' name="{tag.name}"'
141
+ elif tag.tag_type == TAG_DO_ABC:
142
+ if len(tag.payload) >= 4:
143
+ minor, major = struct.unpack_from("<HH", tag.payload, 0)
144
+ extra = f" ABC v{minor}.{major}"
145
+ elif tag.tag_type == TAG_SYMBOL_CLASS:
146
+ if len(tag.payload) >= 2:
147
+ count = struct.unpack_from("<H", tag.payload, 0)[0]
148
+ extra = f" {count} symbol(s)"
149
+ off = 2
150
+ for j in range(min(count, 5)):
151
+ cid = struct.unpack_from("<H", tag.payload, off)[0]
152
+ off += 2
153
+ null_idx = tag.payload.index(0, off)
154
+ sname = tag.payload[off:null_idx].decode(
155
+ "utf-8", errors="replace")
156
+ off = null_idx + 1
157
+ doc = " [DOCUMENT CLASS]" if cid == 0 else ""
158
+ extra += (
159
+ f'\n CharID={cid} -> "{sname}"{doc}')
160
+
161
+ size_str = f"{len(tag.payload):>10,}"
162
+ print(
163
+ f" [{i:2d}] Tag {tag.tag_type:3d} "
164
+ f"({tag.type_name:<30s}) {size_str} bytes{extra}")
flashkit/swf/tags.py ADDED
@@ -0,0 +1,120 @@
1
+ """
2
+ SWF tag definitions and constants.
3
+
4
+ Defines the ``SWFTag`` dataclass and well-known SWF tag type IDs.
5
+ The tag type constants eliminate magic numbers when working with
6
+ parsed SWF tag lists.
7
+
8
+ Reference: SWF File Format Specification v19, Chapter 2 (SWF structure).
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass
14
+
15
+
16
+ # ── Tag type constants ──────────────────────────────────────────────────────
17
+
18
+ TAG_END = 0
19
+ TAG_SHOW_FRAME = 1
20
+ TAG_SET_BACKGROUND_COLOR = 9
21
+ TAG_DEFINE_SHAPE = 2
22
+ TAG_DEFINE_BITS = 6
23
+ TAG_DEFINE_BUTTON = 7
24
+ TAG_JPEG_TABLES = 8
25
+ TAG_DEFINE_TEXT = 11
26
+ TAG_DO_ACTION = 12
27
+ TAG_DEFINE_SOUND = 14
28
+ TAG_DEFINE_BITS_LOSSLESS = 20
29
+ TAG_DEFINE_BITS_JPEG2 = 21
30
+ TAG_PLACE_OBJECT2 = 26
31
+ TAG_REMOVE_OBJECT2 = 28
32
+ TAG_DEFINE_SHAPE3 = 32
33
+ TAG_DEFINE_TEXT2 = 33
34
+ TAG_DEFINE_BITS_JPEG3 = 35
35
+ TAG_DEFINE_BITS_LOSSLESS2 = 36
36
+ TAG_DEFINE_EDIT_TEXT = 37
37
+ TAG_DEFINE_SPRITE = 39
38
+ TAG_FRAME_LABEL = 43
39
+ TAG_DEFINE_MORPH_SHAPE = 46
40
+ TAG_DEFINE_FONT2 = 48
41
+ TAG_EXPORT_ASSETS = 56
42
+ TAG_IMPORT_ASSETS = 57
43
+ TAG_DO_INIT_ACTION = 59
44
+ TAG_DEFINE_VIDEO_STREAM = 60
45
+ TAG_DEFINE_FONT3 = 75
46
+ TAG_SCRIPT_LIMITS = 65
47
+ TAG_FILE_ATTRIBUTES = 69
48
+ TAG_PLACE_OBJECT3 = 70
49
+ TAG_DO_ABC = 72 # Raw ABC bytecode (AVM2)
50
+ TAG_SYMBOL_CLASS = 76 # Maps character IDs to class names
51
+ TAG_DEFINE_BINARY_DATA = 77
52
+ TAG_DO_ABC2 = 82 # Named ABC bytecode (flags + name + ABC)
53
+ TAG_DEFINE_SCENE_AND_FRAME_LABEL = 86
54
+ TAG_DEBUG_ID = 255
55
+
56
+ # ── Human-readable tag names ────────────────────────────────────────────────
57
+
58
+ TAG_NAMES: dict[int, str] = {
59
+ TAG_END: "End",
60
+ TAG_SHOW_FRAME: "ShowFrame",
61
+ TAG_SET_BACKGROUND_COLOR: "SetBackgroundColor",
62
+ TAG_DEFINE_SHAPE: "DefineShape",
63
+ TAG_DEFINE_BITS: "DefineBits",
64
+ TAG_DEFINE_BUTTON: "DefineButton",
65
+ TAG_JPEG_TABLES: "JPEGTables",
66
+ TAG_DEFINE_TEXT: "DefineText",
67
+ TAG_DO_ACTION: "DoAction",
68
+ TAG_DEFINE_SOUND: "DefineSound",
69
+ TAG_DEFINE_BITS_LOSSLESS: "DefineBitsLossless",
70
+ TAG_DEFINE_BITS_JPEG2: "DefineBitsJPEG2",
71
+ TAG_PLACE_OBJECT2: "PlaceObject2",
72
+ TAG_REMOVE_OBJECT2: "RemoveObject2",
73
+ TAG_DEFINE_SHAPE3: "DefineShape3",
74
+ TAG_DEFINE_TEXT2: "DefineText2",
75
+ TAG_DEFINE_BITS_JPEG3: "DefineBitsJPEG3",
76
+ TAG_DEFINE_BITS_LOSSLESS2: "DefineBitsLossless2",
77
+ TAG_DEFINE_EDIT_TEXT: "DefineEditText",
78
+ TAG_DEFINE_SPRITE: "DefineSprite",
79
+ TAG_FRAME_LABEL: "FrameLabel",
80
+ TAG_DEFINE_MORPH_SHAPE: "DefineMorphShape",
81
+ TAG_DEFINE_FONT2: "DefineFont2",
82
+ TAG_EXPORT_ASSETS: "ExportAssets",
83
+ TAG_IMPORT_ASSETS: "ImportAssets",
84
+ TAG_DO_INIT_ACTION: "DoInitAction",
85
+ TAG_DEFINE_VIDEO_STREAM: "DefineVideoStream",
86
+ TAG_DEFINE_FONT3: "DefineFont3",
87
+ TAG_SCRIPT_LIMITS: "ScriptLimits",
88
+ TAG_FILE_ATTRIBUTES: "FileAttributes",
89
+ TAG_PLACE_OBJECT3: "PlaceObject3",
90
+ TAG_DO_ABC: "DoABC",
91
+ TAG_SYMBOL_CLASS: "SymbolClass",
92
+ TAG_DEFINE_BINARY_DATA: "DefineBinaryData",
93
+ TAG_DO_ABC2: "DoABC2",
94
+ TAG_DEFINE_SCENE_AND_FRAME_LABEL: "DefineSceneAndFrameLabelData",
95
+ TAG_DEBUG_ID: "DebugID",
96
+ }
97
+
98
+
99
+ # ── SWFTag dataclass ────────────────────────────────────────────────────────
100
+
101
+ @dataclass
102
+ class SWFTag:
103
+ """A single tag from a SWF file.
104
+
105
+ SWF files are a sequence of typed tags. Each tag has a type ID,
106
+ a payload, and optionally a name (for DoABC2 tags).
107
+
108
+ Attributes:
109
+ tag_type: Numeric tag type ID (see TAG_* constants).
110
+ payload: Raw tag payload bytes.
111
+ name: Tag name string (populated for DoABC2 tags).
112
+ """
113
+ tag_type: int
114
+ payload: bytes
115
+ name: str = ""
116
+
117
+ @property
118
+ def type_name(self) -> str:
119
+ """Human-readable tag type name."""
120
+ return TAG_NAMES.get(self.tag_type, f"Unknown({self.tag_type})")
@@ -0,0 +1,20 @@
1
+ """
2
+ Workspace and resource management.
3
+
4
+ A ``Workspace`` loads one or more SWF/SWZ files, extracts their ABC
5
+ bytecode, parses it, and builds a unified index of all classes, methods,
6
+ and strings. It is the top-level entry point for analysis.
7
+
8
+ A ``Resource`` represents a single loaded file (SWF or SWZ) with its
9
+ parsed tags and ABC content.
10
+ """
11
+
12
+ from .resource import Resource, load_swf, load_swz
13
+ from .workspace import Workspace
14
+
15
+ __all__ = [
16
+ "Workspace",
17
+ "Resource",
18
+ "load_swf",
19
+ "load_swz",
20
+ ]