跳到主内容

SWF 标签:时间轴、资源与脚本记录

PlaceObject2 为什么总是一千多个?顺着 SWF Tag 往下走,看看时间轴、素材和脚本怎样拼成一部作品。

Flash 保存计划 · 最近整理于 2026年7月30日

本文目录 · 8 节
  1. 这张直方图数的是什么
  2. 一条标签怎样编码
  3. 标签序列、角色字典与时间轴
  4. 最常见的时间轴标签
  5. 动作与字节码标签
  6. 完整标签词典
  7. 未知标签为何显示为 TagN
  8. 怎样解读一组常见分布

越过 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 字节。没有正文的 ShowFrameEnd 都是这种短头。

低 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 是时间轴流式音频的数据块。前面的 SoundStreamHeadSoundStreamHead2 先交代回放与压缩格式,后面的块才一段段送来编码数据。

制作工具常把长音轨按帧切块,所以 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,有时则是长长的控制流、字符串与函数;直方图只会看到外面的盒子。

只要读到 DoActionDoInitAction,本站就把它放进传统 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 J2EEMacromedia Flex for .NETAdobe 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) — 用紧凑的变长整数表记录场景偏移和帧标签;本站从中提取场景名与帧名。

PlaceObject2ShowFrameRemoveObject2 的详细解释分别见 PlaceObject2ShowFrameRemoveObject2

形状、变形与九宫格

  • 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 起,它排在标签流最前。

DoActionRawDoABCDoABC 的容器差异见 动作与字节码标签

保护、调试与元数据

  • 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 版本、音视频、资源、舞台和脚本,最后把它真正播放起来。直方图只是很好的第一眼。

这些资料从哪来

  1. Adobe SWF File Format Specification, Version 19
  2. Ruffle:SWF tag code definitions
  3. Ruffle:SWF tag reader
  4. Ruffle:SWF data types and ProductInfo