SWF 标签:时间轴、资源与脚本记录
PlaceObject2 为什么总是一千多个?顺着 SWF Tag 往下走,看看时间轴、素材和脚本怎样拼成一部作品。
Flash 保存计划 · 最近整理于 2026年7月30日
越过 SWF 文件头,后面基本就是一串按顺序排好的 Tag(标签记录)。有的把形状、字体和声音收进素材库,有的把素材摆上舞台、推进一帧、执行脚本,还有的只是留下编译器信息。播放器沿路读下去,直到顶层 End。
作品页的“标签构成”,就是把这串记录数一遍。它像一张 SWF 的用料侧影:能看出时间轴忙不忙、有没有流式声音、脚本标签多不多,但不会替作品的精细程度打分。
这张直方图数的是什么
提取器顺着解压后的 顶层标签流 往下走。每完整跨过一条记录,就在对应名称下记一笔;总数进入 tag_count,出现过多少个名称就显示多少“种”。所以“17 种 / 4094 个”读起来很直白:4094 条顶层记录,分属 17 种。列表按次数排列,先展开前 8 种。最长的柱子定为 100%,其余跟它相比,柱长不是占总数的百分比。
读图时记住三件事就够了:
- 大小不论,一条就是一条。零字节的
ShowFrame和装着大量边线的DefineShape都记 1;DoABC里面有多少类和方法也一样。 - 扫描器目前不钻进
DefineSprite。一段再复杂的影片剪辑,在图上还是一条DefineSprite,内部帧与动作先收着不展开。 - 多数正文只需按长度跨过去。若记录头或正文半途断掉,扫描便停在那儿,前面已经走完的仍然保留。
于是,PlaceObject2 很多,多半是显示列表一直在变;SoundStreamBlock 一长排,常见原因是音轨随帧切块;ShowFrame 则踩着主时间轴往前走。单看这些数,已经能摸到作品的节奏,但还看不到里面每个素材有多复杂。
一条标签怎样编码
每条标签都顶着一个小端序的 RECORDHEADER。开头 16 位塞了两样东西:标签编号和正文长度。
15 6 5 0
+---------------------------+-----------------------+
| TagCode(10 位) | Length(6 位) |
+---------------------------+-----------------------+
高 10 位是 TagCode(读取器里的 tagWord >> 6),低 6 位是正文长度(tagWord & 0x3f),记录头自己不算在内。正文只有 0–62 字节时,到这里就够了,整个头共 2 字节。没有正文的 ShowFrame 和 End 都是这种短头。
低 6 位若是 0x3f(63),意思是“长度写不下了,往后看”:接下来的 4 字节无符号小端整数才是真正长度,记录头也就变成 6 字节。正文恰好 63 字节时同样走长头。解析器靠这个数跳到下一条;它若写坏了,后面的标签都会跟着错位,所以提取器还守着解压尺寸和标签总数两道上限。
标签序列、角色字典与时间轴
把标签摊开,大致能分成两拨。定义标签(definition tag) 把形状、位图、字体、声音、按钮或影片剪辑做成角色,以 CharacterID 收进字典;这时它们还没上台。控制标签(control tag) 再来引用这些角色,放置、修改或移除实例,也管推进帧、启动声音和播放流程。
脚本、调试、导入导出和元数据又各有一套用途,不过外壳仍是同样的“记录头 + 正文”。顺序也不能乱:角色得先定义再引用,流式声音先有头再有数据块;SWF 8 起 FileAttributes 放在最前,顶层 End 收尾。
DefineSprite 很特别。它自己是定义标签,肚子里却又装着一套控制标签,形成影片剪辑的独立时间轴。也就是说,一只 SWF 不是一条扁平长清单,而是根时间轴下面还挂着许多小时间轴。
最常见的时间轴标签
PlaceObject2(26)
PlaceObject2 是时间轴上最忙碌的标签之一。它用标志位挑选正文中的字段:可以把角色放到指定深度,也可以挪动、更新已有实例,顺手改矩阵、颜色变换、形变比例、实例名、遮罩深度或剪辑动作。同一个实例经过许多关键帧,就会留下一路 PlaceObject2;数量高,往往只是台上的调度很频繁。
ShowFrame(1)
ShowFrame 没有正文,像时间轴上的一次落槌:显示当前舞台,播放头再向前走一帧。主时间轴里数到多少个 ShowFrame,通常会接近文件头的 FrameCount。前者是一路实际读到的记录,后者是作者工具预先写下的帧数;文件截断时两边可能对不上,影片剪辑内部的帧也不在当前直方图里。示例中的 862 对 862,说明这两处正好互相印证。
SoundStreamBlock(19)
SoundStreamBlock 是时间轴流式音频的数据块。前面的 SoundStreamHead 或 SoundStreamHead2 先交代回放与压缩格式,后面的块才一段段送来编码数据。
制作工具常把长音轨按帧切块,所以 SoundStreamBlock 可能与 ShowFrame 一样多。862 个块是一条流式音轨的分块,不是 862 个声音资源;事件声音另由 DefineSound 统计。格式、采样率和两种声音模型见 SWF 音频。
RemoveObject2(28)
RemoveObject2 按深度让实例退场。旧版 RemoveObject 还要同时写角色 ID 与深度;到这里,指定深度上的实例已经很明确,只写深度就够了。
高频 RemoveObject2 常见于实例频繁退场、替换或重组的时间轴。操作只影响显示列表,角色定义仍在,后续帧可以再次放置。
DefineShape(2)、DefineShape2(22)与 DefineShape3(32)
这三代标签都在定义矢量形状,差别主要落在样式能力上:
DefineShape是最早的形状格式;DefineShape2扩展了样式数量和在形状记录中追加样式表的能力;DefineShape3把颜色扩展为 RGBA,可表达透明度。
一条形状标签里可以藏着大量填充、线条和曲线,在直方图上仍只占一格。作品页的“形状”资源数把 DefineShape 1–4 合在一起,标签构成则保留各代名字。
DefineSprite(39)
DefineSprite 做出一个影片剪辑角色。正文先写角色 ID 和帧数,接着就是一段以内部 End 收尾的控制标签:放置、移除、ShowFrame、动作和声音都可以进来,新的定义标签则留在外面。
本站目前不往这层里面钻。于是,一个 500 帧的影片剪辑在顶层图上仍是 1 条 DefineSprite、1 个“影片剪辑”资源,那 500 个内部 ShowFrame 全都收在里面。
动作与字节码标签
DoAction(12)
DoAction 装着一串 AVM1 动作,常见于传统时间轴脚本、按钮行为和早期 ActionScript。有时里面只有一个 Stop,有时则是长长的控制流、字符串与函数;直方图只会看到外面的盒子。
只要读到 DoAction 或 DoInitAction,本站就把它放进传统 AVM1,也就是页面的 AS2 一栏。这个名字为了展示方便,会一并收下“ActionScript 2.0”叫法出现之前的早期 AVM1 文件;来龙去脉见 SWF 版本。
RawDoABC(72)与 DoABC(82)
ABC 是 AVM2 使用的字节码容器,通常对应 ActionScript 3。本站把编号 72 叫作 RawDoABC,编号 82 则沿用 DoABC:
RawDoABC是历史文件里能见到、却没有写进 Adobe SWF 19 标签表的原始 ABC 形式;Ruffle 读取时保留其原始字节。DoABC的正文含Flags、一个名称字符串和ABCData;标志可要求在定义延迟初始化时再执行。
一条 ABC 里可以装许多常量、类、脚本、方法和方法体,所以“DoABC 3 个”说的是三个容器。两种形式遇到任何一种,页面都会显示 AS3;元数据扫描走到这里便收手,不会接着反编译代码。
ProductInfo(41)
ProductInfo 是个颇有档案味道的标签。mxmlc 常会写下它,Adobe SWF 19 的公开标签表却没有收录。Ruffle 仍认得这套结构,本站也会读出产品 ID、edition、主次版本、64 位 build 和 64 位编译时间。
产品 ID 1、2、3 分别对应 Macromedia Flex for J2EE、Macromedia Flex for .NET 和 Adobe Flex。作品页于是可能浮出“Adobe Flex 3.1”这样的信息——这是文件留下的制作工具线索,和文件头里的 SWF 版本是两回事。
编译时间也只是工具当时写进去的值,不能拿来代替文件修改或上传时间。第三方工具可以省略、复制或改写它;正文若不足 26 字节,本站便不拼凑这项信息。
完整标签词典
下面这份词典适合随手翻查,覆盖提取器目前叫得出名字的全部标签。前面细讲过的项目可以从锚点跳回去;括号里是 SWF TagCode。
文件、时间轴与显示列表
- End(0) — 结束当前标签序列。顶层播放器在这里停止读取;
DefineSprite内部也用自己的End结束嵌套时间轴。 - PlaceObject(4) — 最早的放置格式,在给定深度创建角色实例,并可附带矩阵和颜色变换;后来的可选能力主要由
PlaceObject2/3扩展。 - RemoveObject(5) — 用角色 ID 和深度移除实例,不改动角色字典。
- SetBackgroundColor(9) — 设置舞台背景的 RGB 颜色,作品页上的背景色就从这三个字节而来。
- FrameLabel(43) — 给当前帧起名,供脚本或导航按名称跳转;本站会收下顶层可读标签名。
- SetTabIndex(66) — 为显示列表深度指定键盘 Tab 顺序,用于交互与可访问性控制。
- PlaceObject3(70) — 在
PlaceObject2上继续扩展类名、滤镜、混合模式、位图缓存等字段;后续 SWF 版本又允许可见性与不透明背景字段。 - DefineSceneAndFrameLabelData(86) — 用紧凑的变长整数表记录场景偏移和帧标签;本站从中提取场景名与帧名。
PlaceObject2、ShowFrame、RemoveObject2 的详细解释分别见 PlaceObject2、ShowFrame 与 RemoveObject2。
形状、变形与九宫格
- DefineShape4(83) — 扩展
DefineShape3的描边能力,包括线帽、连接、缩放规则和填充描边。 - DefineMorphShape(46) — 定义起点与终点形状及其样式,实例通过形变比例插值得到中间画面。
- DefineMorphShape2(84) — 扩展变形形状的边界和线条能力,与
DefineShape4世代的高级描边配套。 - DefineScalingGrid(78) — 指定九宫格缩放区域,让边框、角部和中间采用不同策略。
前三代形状与影片剪辑见 DefineShape 家族 和 DefineSprite。
JPEG、无损位图与共享表
- DefineBits(6) — 早期 JPEG 位图定义,图像数据依赖单独的
JPEGTables共享表。 - JPEGTables(8) — 为早期
DefineBits提供全文件共享的 JPEG 编码表;较新的 JPEG 标签不再依赖这种拆分。 - DefineBitsLossless(20) — 保存 zlib 压缩的无损调色板或像素位图,不提供完整 alpha 通道。
- DefineBitsJPEG2(21) — 在一条标签中保存完整 JPEG,也可承载规范允许的其他图像形式,不再依赖全局
JPEGTables。 - DefineBitsJPEG3(35) — 在 JPEG 图像数据之后附加单独 zlib 压缩的 alpha 通道,支持逐像素透明度。
- DefineBitsLossless2(36) — 无损位图的透明版本,颜色表示支持 alpha。
- DefineBitsJPEG4(90) — 延续 JPEG + alpha 结构,并加入去块滤镜参数。
这里数的是位图定义记录。至于每张图有多少像素、占多少字节,要到标签里面再看。
按钮与文字
- DefineButton(7) — 定义早期按钮及其 Up、Over、Down、HitTest 状态,可附带传统按钮动作。
- DefineButtonCxform(23) — 为旧式按钮角色提供颜色变换。
- DefineButtonSound(17) — 把按钮状态转换映射到声音角色与播放参数。
- DefineButton2(34) — 新一代按钮格式,扩展状态、菜单式跟踪、颜色变换与条件动作等能力。
- DefineText(11) — 定义静态排版文字角色,正文以字体 ID、字形索引和位置记录文本,颜色使用 RGB。
- DefineText2(33) —
DefineText的透明颜色版本,文字颜色使用 RGBA。 - DefineEditText(37) — 定义动态或输入文本框,可含变量名、初始文本、字体、布局、HTML 标志和输入限制;本站提取可读文本与变量名。
静态文字留下的是字形与布局,不总能还原源文件字符串;动态文字到了运行时,还可能被脚本换掉。
字体与文字渲染
- DefineFont(10) — 最早的嵌入字体轮廓表,没有自带字体名称;名称可能来自另一个
DefineFontInfo。 - DefineFontInfo(13) — 为早期字体补充名称、字符编码映射和样式标志;本站从中提取字体名。
- DefineFont2(48) — 自包含的字体定义,可包括名称、宽字符码、布局度量、字距与样式。
- DefineFontInfo2(62) — 在
DefineFontInfo上加入语言代码等信息。 - DefineFontAlignZones(73) — 提供字形对齐区域,帮助小字号渲染和抗锯齿。
- CSMTextSettings(74) — 为文字角色记录高级抗锯齿渲染参数,如厚度与锐度。
- DefineFont3(75) — 高分辨率字形坐标版本,服务于更精细的高级文字渲染。
- DefineFontName(88) — 给字体 ID 附加字体全名和版权等元数据;本站读取可用名称。
- DefineFont4(91) — 较新的字体定义,可记录字体名、粗体/斜体标志,并选择是否嵌入 CFF 字体数据。
“字体”资源数跟着定义标签走,名称却可能写在 DefineFontInfo*、DefineFontName 或定义本身。两张清单对不上,并不稀奇。
声音
- DefineSound(14) — 定义可被事件触发的声音角色,包括压缩格式、采样率、位深、声道与样本数;本站据此识别音频格式。
- StartSound(15) — 按声音角色 ID 启动、停止或控制事件声音,可带循环、入点、出点和包络参数。
- SoundStreamHead(18) — 声明时间轴流式声音的回放格式和压缩格式,后续
SoundStreamBlock提供数据。 - SoundStreamHead2(45) — 与前代结构接近,Adobe 规范说明它主要为授权 MP3 编码器提供支持。
- StartSound2(89) — 通过声音类名而不是角色 ID 启动或控制声音,适合与 AS3 类绑定配合。
SoundStreamBlock 见 流式音频块,按钮声音见 DefineButtonSound。
视频
- DefineVideoStream(60) — 定义内嵌视频角色,包括尺寸、帧数、去块与 codec ID;作品页的视频资源和编码从这里读出。
- VideoFrame(61) — 给视频角色提供带帧号的编码包;其次数与时间轴总帧数、关键帧数均无直接对应。
H.263、Screen Video、VP6 与透明视频的区别见 SWF 视频编码。
资源链接、类与二进制数据
- ExportAssets(56) — 把本文件的角色 ID 绑到可导出的符号名;本站把这些名字记下来。
- ImportAssets(57) — 从指定 URL 的另一 SWF 导入符号;本站只记录 URL,不顺路抓取外部文件。
- ImportAssets2(71) —
ImportAssets的后续形式,增加保留字段并服务于较新的导入规则。 - SymbolClass(76) — 把角色 ID 映射到 AS3 类名;角色 ID 0 的映射是文档主类,本站会分别提取类名与主类。
- DefineBinaryData(87) — 把任意字节保存为角色,可通过类绑定作为二进制资源访问;一条记录的正文可能很大。
外部导入留下的是一条依赖线索。归档时目标可能早已失联,也不会混进本体的资源计数。
脚本、初始化与运行限制
- DoInitAction(59) — 为指定影片剪辑角色保存 AVM1 初始化动作,通常在该角色第一次实例化前执行一次。
- ScriptLimits(65) — 设置 AVM1 最大递归深度和脚本超时秒数;作品页会列出这两个值。
- FileAttributes(69) — 文件级行为标志,包括 AS3、网络沙箱、元数据、GPU 和 Direct Blit。SWF 8 起,它排在标签流最前。
DoAction、RawDoABC 和 DoABC 的容器差异见 动作与字节码标签。
保护、调试与元数据
- Protect(24) — 请求创作工具避免导入或编辑 SWF,可附带口令散列。它不加密标签;本站显示保护标志和口令数据是否存在。
- EnableDebugger(58) — 启用旧式远程调试,可含调试口令信息;出现时本站记录 debug 标志。
- EnableDebugger2(64) — 后来的远程调试标签,页面同样会因此显示 debug。
- Metadata(77) — 保存 XMP XML 包,可含 Dublin Core 标题、作者和描述等档案信息;它通常不影响舞台播放。
ProductInfo 也是元数据,但因其对“Adobe Flex 3.1”之类编译工具显示很重要,已在 ProductInfo 单独展开。
未知标签为何显示为 TagN
Flash 流传多年,标签编号表也不是在每个工具和实现里都长得一模一样。本站这份名称表主要服务元数据提取和页面链接,遇到表外编号仍照常计数,只是先用 Tag 加十进制编号称呼它,例如 Tag93。
看到 TagN,意思只是“这条记录读得过去,词典暂时没给它名字”。Ruffle 的编号表里也有本站尚未收录的项目。可能的编号很多,本页不为它们逐一预建锚点,次数则会好好保留,等词典日后补上。
怎样解读一组常见分布
回到开头那组数据:PlaceObject2 1853 / ShowFrame 862 / SoundStreamBlock 862 / RemoveObject2 253 / DefineShape 151 / DefineSprite 67。主时间轴走过 862 次帧提交,流式声音块也正好 862 段;放置和更新远多于角色定义,可以看出素材在时间轴上反复使用。151 条 DefineShape 各自还可能藏着许多矢量边,67 个影片剪辑也各有没展开的小时间轴。
至于这到底是一只游戏、动画、广告还是课件,还是得接着看 SWF 版本、音视频、资源、舞台和脚本,最后把它真正播放起来。直方图只是很好的第一眼。