十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

GDScript 字节码版本演进史:gdsdecomp 如何解码 Godot 1.0 至 4.5 的全部编译脚本

GDScript 字节码版本演进史:gdsdecomp 如何解码 Godot 1.0 至 4.5 的全部编译脚本 GDScript 字节码版本演进史gdsdecomp 如何解码 Godot 1.0 至 4.5 的全部编译脚本【免费下载链接】gdsdecompGodot reverse engineering tools项目地址: https://gitcode.com/GitHub_Trending/gd/gdsdecompgdsdecompGodot RE Tools是一套面向 Godot 引擎的逆向工程工具集其核心能力之一是把导出的.gdc编译脚本还原为可读的 GDScript 源码。要做到这一点它必须先回答一个问题这份字节码是哪个 Godot 版本、哪次提交编译出来的答案就记录在仓库根目录的 BYTECODE_HISTORY.md 中——这份文档是 Godot 自 2014 年 1.0 分支诞生以来 GDScript 字节码格式全部变更的官方“年表”。本文以该文档为骨架结合仓库内字节码反编译器的源码实现、生成脚本与测试用例完整梳理 bytecode 1101 的版本演进脉络并说明 gdsdecomp 如何据此为不同时代的.gdc文件匹配正确的反编译器。为什么要维护一份“字节码版本年表”GDScript 脚本被编译成.gdc后其字节码中内嵌的 token 编号、内置函数编号与头信息布局会随 Godot 引擎的迭代而变化每当引擎新增一个关键字token、新增或删除一个全局内置函数、改变头字段的大小旧版编译器产出的字节码就可能与新版不兼容。gdsdecomp 的逆向逻辑因此不能只写一套“通吃所有版本”的反编译器而必须为每个字节码版本保存一份独立的 token 表、函数表与参数个数表。BYTECODE_HISTORY.md 正是这份对应关系的权威记录它以 Godot 官方仓库的 commit 为锚点标记了每次字节码格式变化发生的时间、所属分支、bytecode 版本号以及具体变更内容。仓库中bytecode/目录下 60 余个以 commit hash 命名的bytecode_hash.cpp/.h文件就是这份年表在代码层面的实体化——每个文件对应一个格式快照可独立完成对应版本的解码。GDScript 字节码版本完整演进表原文档按 Godot 分支Branch组织以下完整继承全部记录。表格中“Commit”列为 Godot 引擎对应提交的短 hash可据此在 Godot 官方仓库中追溯原文Bytecode 列即该提交开始使用的字节码版本号。Branch 1.0ReleaseCommitDateBytecodeChanges0b806ee2014.02.0918c1731b2014.02.152Addedloadfunction31ce3c52014.03.132Addedfuncreffunction703004f2014.06.162Addedhashfunction8cab4012014.09.152AddedYIELDtoken1.0.0e82dc402014.10.273AddedSETGETtoken1.1 branch divergeBranch 1.1ReleaseCommitDateBytecodeChanges2185c012015.02.153Addedvar2str,str2varfunctions97f34a12015.03.253Addedseed,get_instfunctionbe46be72015.04.183Functionget_instrenamed toinstance_from_id1.1.065d48d62015.05.094Addedprintsfunction2.0 branch divergeBranch 2.0ReleaseCommitDateBytecodeChanges48f1d022015.06.245AddedSIGNALtoken30c12292015.12.286AddedONREADYtoken7d2d1442015.12.297AddedBREAKPOINTtoken64872ca2015.12.318AddedColor8function61745852016.01.029AddedCONST_PItoken2.0.0 - 2.0.4-123441ec2016.01.0210Addedvar2bytes,bytes2varfunctions2.1 branch divergeBranch 2.1ReleaseCommitDateBytecodeChanges2.1.0 - 2.1.171245992016.06.1810Addedtype_existsfunction3.0 branch diverge2.1.285585c72017.01.1210AddedColorNfunction (backport from 3.0)2.1.3 - 2.1.6ed80f452017.04.0610AddedENUMtoken (backport from 3.0)Branch 3.0ReleaseCommitDateBytecodeChanges71245992016.06.1810Addedtype_existsfunction1add52b2016.08.1911AddedREMOTE,SYNC,MASTER,SLAVEtokens4ee82a22016.08.2711AddedENUMtoken513c0262016.10.0311Addedcharfunction23381a52016.12.1711AddedColorNfunction8b912d12017.01.0811AddedDOLLARtoken62273e52017.01.0812Addedvalidate_json,parse_json,to_jsonfunctionf8a7c462017.01.1112AddedMATCHtokenc24c7392017.01.2012AddedWILDCARDtoken5e938f02017.02.2812AddedCONST_INF,CONST_NANtokens015d36d2017.05.2712AddedIStokenc6120e72017.08.0712Addedlenfunctiond28da862017.08.1812Addedinverse_lerp,range_lerpfunctions216a8aa2017.10.1312Addedwrapi,wrapffunctions91ca7252017.11.1212AddedCONST_TAUtoken3.0.0 - 3.0.6054a2ac2017.11.2012Addedpolar2cartesian,cartesian2polarfunctions3.1 branch divergeBranch 3.1ReleaseCommitDateBytecodeChangesff1e7cf2018.05.0712Addedis_instance_validfunctiona56d6ff2018.05.1712Addedget_stackfunction3ea6d9f2018.05.2812Addedprint_debugfunction8e35d932018.05.2912AddedREMOTESYNC,MASTERSYNC,SLAVESYNCtokensa3f1ee52018.07.1513AddedCLASS_NAMEtoken8aab9a02018.07.2013AddedAS,VOID,FORWARD_ARROWtokensd6b31da2018.09.1513AddedPUPPETtoken, tokenSLAVESYNCrenamed toPUPPETSYNC1ca61a32018.10.3113Addedpush_error,push_warningfunction3.11a361412019.02.2013RemovedDO,CASE,SWITCHtokens3.1.1514a3fb2019.03.1913Addedsmoothstepfunction3.2 branch divergeBranch 3.2ReleaseCommitDateBytecodeChanges7f7d97f2019.04.2913Addedis_equal_approxandis_zero_approxfunctions620ec472019.05.0113Addedstep_decimalsfunctionc00427a2019.06.0113Addedmove_towardfunctiona60f2422019.07.1913Addedposmodfunction6694c112019.07.2013Addedlerp_anglefunction3.25565f552019.08.2613Addedordfunction4.0 branch diverge3.5 branch divergeBranch 3.5ReleaseCommitDateBytecodeChanges3.5a7aad782020.10.0713addeddeep_equalfunction (never added to 4.x)Branch 4.0masterGDScript 2.0 时代ReleaseCommitDateBytecodeChanges506df142020.02.1213Removeddecimalsfunctionf3f05dc2020.02.1313RemovedSYNCandSLAVEtokensGDScript 2.05d6e8532020.07.24N/ACompiled mode not implemented until 4.34.377af6ca2024.02.09100Added compiled modeb59d6be2025.05.18100AddedABSTRACTtokenee121ef2025.06.09100AddedPERIOD_PERIOD_PERIODtoken2e216b52025.06.10101Content header size changed4.5ebc36a72025-06-27101RemovedABSTRACTtoken三组关键里程碑解读把上表压缩成时间轴可以提炼出对逆向工程最重要的三组节点1. 远古时代bytecode 142014–2015Godot 1.0/1.1 时代的格式变化非常频繁——仅 2014 年 2 月到 10 月就从 1 跳到 3。这一时期的变更以全局函数新增为主load、funcref、hash、var2str等说明语言本身仍在高速扩充。注意get_inst在be46be7被改名为instance_from_id这类“改名”在逆向时尤其棘手字节码中存储的是函数索引而非函数名反编译器必须知道哪个索引对应哪个名字。2. 稳定期bytecode 5132015–2020Godot 2.0 到 3.5 的五年间字节码版本只从 5 缓慢升到 13。这期间语法层面持续增加 tokenSIGNAL、ONREADY、MATCH、IS、DOLLAR等但不少变化如 3.0 分支的ENUM、ColorN并未单独提升版本号而是并入后续版本甚至在 2.1 分支被反向 backport。这解释了为何 3.0 分支表格中7124599一行标注的 bytecode 仍是 10——版本号只随“格式不兼容”的变更而递增。3. 大断层bytecode 100/1012024–2025Godot 4.x 将 GDScript 重构为 GDScript 2.0token 体系、内置函数列表与字节码头结构全部重写。5d6e853一行特别标注“Compiled mode not implemented until 4.3”意味着从 2020 年 7 月到 2024 年 2 月Godot 4.x 根本没有可导出的 GDScript 编译字节码直到 4.377af6ca才正式推出编译模式版本号直接定为 100。随后 4.5 开发周期内快速迭代出 101内容头大小变化。这也解释了为什么 bytecode_base.h 中定义了GDSCRIPT_2_0_VERSION 100与LATEST_GDSCRIPT_VERSION 101两个常量100 是 GDScript 2.0 字节码的起点101 是当前最新格式。从年表到代码版本注册与反编译器分发年表并不是一篇孤立的说明文档它与仓库代码严格一一对应。bytecode/bytecode_versions.cpp 中静态数组decomp_versions以代码形式记录了上表每一行该文件头部明确注明“自动由bytecode_generator.py生成勿手工编辑”每条记录包含 commit、版本描述、bytecode 版本号、是否为开发版、最小/最大引擎版本以及 parent 提交。例如{ 0x77af6ca, 4.3.0-stable (77af6ca / 2024-02-09 / Bytecode version: 100) - initial version, 100, false, 4.3.0-stable, 4.4.9-stable, 0x0 },字段顺序对应 bytecode/bytecode_versions.h 中GDScriptDecompVersion结构体的成员commit、name、bytecode_version、is_dev、min_version、max_version、parent其中parent指向上一格式快照用于构建版本链。读取.gdc文件时工具通过create_decomp_for_commit()按字节码中记录的 commit hash 精确匹配反编译器实例见 bytecode/bytecode_versions.cpp 中的 switch 分支覆盖从0x0b806ee到0xebc36a7的全部快照若 commit 不在内置列表中则回退到遍历decomp_versions查找自定义版本。get_decomps_for_bytecode_ver(bytecode_version, include_dev)则支持按版本号批量取回同一 bytecode 版本下的所有反编译器开发版与稳定版可选这在不确定具体 commit 时用于穷举尝试。每个快照类如 bytecode/bytecode_77af6ca.h都通过get_function_name、get_function_index、get_global_token、get_local_token_val、get_bytecode_version、get_parent等虚函数向基类GDScriptDecompbytecode/bytecode_base.h提供该版本专属的 token/函数映射。注意基类GlobalToken枚举是一份“跨版本全局归一化”的 token 列表——不同时代的引擎 token 编号千差万别反编译器先用get_global_token()把本地 token 编号归一化到全局编号再用get_local_token_val()反查从而让上层解码逻辑只依赖全局编号屏蔽版本差异。版本快照的自动化生产与数据源手写 60 余份快照显然不现实仓库用一套脚本 数据驱动方案解决权威数据misc/bytecode_versions.json约一万行是唯一的版本事实来源。每个条目包含bytecode_revcommit hash、bytecode_version、date、engine_version、max_engine_version、engine_ver_major、variant_ver_major、parent、is_dev、added_tokens、removed_tokens、added_functions、removed_functions、renamed_functions、arg_count_changed、tokens_renamed、func_names与tk_names完整的本地 token 名列表。对比b59d6be含TK_ABSTRACT与ebc36a7removed_tokens: [TK_ABSTRACT]两条记录可以清晰看到 4.5 开发版中ABSTRACTtoken 从增加到删除的完整生命周期。生成脚本bytecode_generator.py 读取该 JSON为每个版本生成bytecode_hash.cpp/.h包含内置函数表、Token 枚举、全局/本地 token 双向映射的样板代码以及 bytecode/bytecode_versions.cpp 中的注册表与 switch 分发代码。脚本内置的builtin_func_arg_elements列表记录了每个内置函数的参数个数范围如(var2bytes, (1, 1))且注释说明 bytecode 3.1 时参数个数变为 1–2 的可变范围用于生成get_function_arg_count()的返回值。派生逻辑create_version_from_custom_def()bytecode/bytecode_versions.cpp支持从 JSON 字典动态创建自定义版本commit 使用0xf0000000起的高位前缀区分内置与自定义版本register_derived_decomp_version_custom()则允许基于某个内置版本克隆并覆盖差异字段。这套流水线意味着只要 Godot 官方出现一次字节码格式变更维护者只需在 JSON 中新增一条记录并重新运行脚本即可获得可编译的新反编译器快照年表与代码永远保持同步。测试如何印证年表中的每个版本年表每一行的准确性都由测试代码背书。tests/test_bytecode.cpp 中定义了一张ScriptToRevision映射表把每个测试脚本与其“引入该语法的 commit”绑定例如{ has_ord, 0x5565f55 }, { has_tau, 0x91ca725 }, { has_onready, 0x30c1229 }, { has_yield, 0x8cab401 },这些脚本存放在 helpers/ 目录命名即含义has_tau.gd中写入var t TAUhas_onready.gd使用onready关键字。测试流程是把某个 helper 脚本按“它应该不存在的旧版本”编译成.gdc断言其应解析失败或按“它应该存在的新版本”编译并断言反编译成功从而验证版本边界。例如 tests/test_bytecode.cpp 中has_do、has_push_error都绑定到0x1a361413.1 稳定版DO/CASE/SWITCHtoken 被移除的那次提交正是为了验证 3.1 起旧 token 的反向兼容行为。配套的 tests/test_projects/exported/ 目录存放了从 2.1.1 到 4.5.1 各引擎版本真实导出的.pck文件覆盖了年表中的绝大多数分支节点构成端到端的“真实世界”回归测试集。实战如何为一份未知.gdc定位版本结合年表与源码逆向一份未知来源的.gdc文件可按以下思路操作读取头信息Godot 编译脚本的文件头包含引擎版本与 bytecode 版本号。若头中记录的版本落在 100/101说明它来自 Godot 4.34.5 的 GDScript 2.0 编译模式若为 13则来自 Godot 3.x2.03.5 跨度极大需进一步区分。缩小范围版本号为 13 时利用年表中的分支分叉点交叉验证——例如脚本中出现deep_equal则必为 3.5该函数从未进入 4.x出现PUPPET/PUPPETSYNC则至少为 3.1-dev7包含CLASS_NAME/AS/VOID则至少为 3.1-dev5。交给工具匹配gdsdecomp 的GDScriptDecompVersion体系会自动遍历decomp_versions按min_version/max_version区间如 4.3 版本记录标注max_engine_version 4.4.9-stable与字节码版本号匹配候选反编译器开发者版is_dev true默认不参与匹配可通过参数显式启用。人工兜底若引擎版本晚于年表最新记录可用register_derived_decomp_version_custom()机制基于最接近的父版本克隆一份自定义反编译器并增量覆盖差异这正是年表结构parent字段为扩展性预留的设计。结语从 2014 年 2 月 bytecode 1 的首次定型到 2025 年 6 月 bytecode 101 的头结构变更GDScript 字节码走过了十余年、跨越了 Godot 1.x 到 4.x 的两次重大语言重构。BYTECODE_HISTORY.md 以一张张分支表格浓缩了这段历史而 misc/bytecode_versions.json、bytecode_generator.py、bytecode/bytecode_versions.cpp 与 tests/test_bytecode.cpp 则分别从数据、生成、注册与验证四个层面把年表变成了可运行的逆向工程能力。理解这份年表是驾驭 gdsdecomp 处理任意年代 Godot 游戏脚本的第一步。【免费下载链接】gdsdecompGodot reverse engineering tools项目地址: https://gitcode.com/GitHub_Trending/gd/gdsdecomp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表