Repository navigation
Releases
v1.2.2
Ship the plugin system with a build that survives parallel compilation The library was compiled twice under two global-property sets, so a parallel solution build raced on its own obj/ directory and failed with CS2012/MSB3713. The native set now comes from the library's own RuntimeIdentifier, which is what CI was tripping over. Plugins get a directory each under a common parent, loaded in dependency order, and the release ships the tracked ones inside <exe>/Plugins on both platforms -- published framework-dependent, so a package is 60 MB instead of 130 MB with three copies of the runtime. The loader identifies a native library by its header rather than its path, so the flattened layout no longer logs BadImageFormat at startup. The export gained a material for shaders the game ships none for, a material index, and a render-test route that draws every recovered shader against real pictures.
v1.2.1 — shared passes, reflection and indexed inputs
Changes
- Preserve shared compiled programs in every referencing pass and retain serialized pass order.
- Fix dynamically indexed inputs (
v[r2.x + 2]): parse complete instructions and emit indexed input reads instead of throwing an out-of-range exception. - Expose parameter-blob indices, constant-buffer reflection and texture bindings through the embedded API.
- Load embedded TMP layouts on demand in host applications.
- Restore generated ShaderLab headers (#1); identify DXBC/GLSL translation immediately above each program block.
Verification
- Full reference game: 303/303 shaders written, 964/964 programs translated, 0 skipped programs, 0 failures (48 built-in shaders excluded by the normal filter).
- FastBloom: 10/10 round-trip logic checks passed; the existing verifier reports 2 soft differences.
- Published Windows executable passed the complete game export.
- Linux binary cross-published from Windows; Linux runtime validation was not performed in this release run.
Downloads
Self-contained, trimmed, single-file .NET 10 CLR executables; no separate .NET installation required.
- ShaderRipper.exe: Windows x64.
- ShaderRipper: Linux x64 (
chmod +x ShaderRipper).
v1.2.0 - matrix folds that were silently left expanded, and output names that fit
What changed
The library was rewritten: grammar (ANTLR) → AST → HLSL → ShaderLab is now the
only route, and src/ShaderRipper.Old is a local reference copy that is no
longer built.
Correctness
- Matrix products whose closing row is not adjacent. fxc schedules the work
that feeds a product's vector components between the rows, so the completing
madsat four instructions after the third row. The scan window grows to
eight instructions but still stops at the first write of the accumulator, so a
wider window cannot swallow an unrelated product. - A component that is overwritten before it is re-read.
ADOFAI_HoldShader
readsr0.xin the first row, overwrites it with the object-to-worldw, then
reads it again in the closing row. Such a component is now pinned into a local
where it still held its old value:This is also why such a shader is deliberately not folded intofloat pin0 = r0.x; r0.x = mul(unity_ObjectToWorld, float4(v0.x, v0.y, v0.z, v0.w)).w; o0.xyzw = mul(unity_MatrixVP, float4(r0.y, pin0, r1.z, r0.x));
UnityObjectToClipPos— its clip position is built from object-spacexyz
plus thewof the object-to-world product, which is a different computation. - Texture names. The asset's texture reflection names what it can; the
registers it does not cover (a[PerRendererData]texture, or a built-in the
variant samples without a property) now take the remaining property names in
declaration order instead of degrading tot1,t2, …. A name already bound
is never handed out twice (redefinition of '_MainTex'). - ShaderLab tag casing. Tag keys and
LightModevalues are canonicalised —
LIGHTMODE/FORWARDBASE→LightMode/ForwardBase. Unity matches the key
case-insensitively but the value exactly, so the value mattered.
Output layout
- Output paths follow one rule: a fixed path wins (Resources container or
bundleAssetBundle.m_Container), else the shader's own name with/→_,
else a_1suffix. Longest file name on the reference corpus dropped from 100
to 59 characters, and the numeric suffix never fires. - Fixed paths claim first, so a shader the build placed at an explicit path keeps
it. - Bundle container paths are read now (
AssetBundle.m_Container, class 142) —
the field existed but was never assigned.
CLI
- Options are position-independent:
--shaderlab out --game <dir>is the same
run as--game <dir> --shaderlab out. Readingargs[0]as the command word
made the first form fail with "unknown option --shaderlab". --gameand--file-bareare registered as value-taking options, so their
values no longer land in the positional list where order decided their meaning.- A mistyped option reports itself instead of printing the usage text.
Packaging
linux-x64publishes embedlibdxcompiler.so/libdxil.so; before this the
Linux build carried only Windows DLLs and could not decompile anything.- Native libraries are embedded per RID again, so each binary carries only what
its platform can load.
Verification
scripts/cmp_logic.pyremoved: every invariant it checked is a subset of
verify_logic.py, and its sample count came from the compiled program (which
fxc merges by coordinate) rather than the source.gen_grammar.pyregenerates all five grammars, not justDxbcAsm.- Corpus (425 shaders / 3,263 programs):
verify_logic.py3195/3195,
scan_unwritten0,scan_nan0 escaping / 0 contained,scan_props0 leaked.
v1.1.1 — output that compiled but rendered wrong
Fix output that compiled cleanly but rendered wrong, plus output naming and speed.
Found by installing the result in Unity and by diffing recompiled assembly against the original DXBC — every one of these passed a syntax check:
- Matrices are read column-major, the way Unity uploads them. A row-major read transposed every transform and sent W to 0 (the HoldShader ribbon flew off screen).
- Comparisons emit the all-ones mask DXBC actually writes (-1), not 1. The ramps that feed the result through
iadd/itof— the classic "which interval is this value in" pattern — depend on that sign, so every such ramp came out inverted (the gray background inUI/Pause Planets). sincoswith a null first target was dropped whole (the cosine vanished), and each target now takes its own mask.- The MVP collapse no longer erases world-position writes, and output registers are zero-initialised (Unity X3578).
- Variants are paired by intersection, so no combination is emitted with a single stage (X3501).
- Samplers are named from the shader's own property table instead of the pass tags — previously every sampler came out as an unbound
t0. - Indexable temps are declared
static(X3025 on write). - The GLSL path maps
roundEvenandgl_VertexID/gl_InstanceID. - A truncated type table is reported as bad data instead of overrunning the buffer.
Also in this release:
- File names are the shader name with only the hierarchy separators replaced (
UI/Pause Planets→UI_Pause Planets.shader); when a resource path is already claimed the file is named after the shader rather than getting an opaque path id. - Parallel export — shaders are processed concurrently, and every name/path claim is resolved up front so repeated runs produce identical trees. A 418-shader game body decompiles in ~6 s on 8 cores.
--dedupkeeps one copy per shader when several data files ship the same one, preferring the copy with a fixed container path.scripts/usc_validate.pyre-checks a whole output tree with Unity's ownUnityShaderCompiler(real CGIncludes, real keyword sets): 418 shaders / ~1.4k variants in ~25 s on 8 processes.
Verification: every variant of all 418 shaders compiles under UnityShaderCompiler 6000.3.10f1 with zero failures; the self-built samples (ExtremeFlow 1328 instructions, VariantMonster 60 variants / 84,425) round-trip with 0 untranslated instructions.
v1.1.0
重大变化
项目拆分为两层
ShaderRipper(src/ShaderRipper,程序集ShaderRipper.dll)——解析/反编译核心库,可被其它工具引用ShaderRipper.Application(src/ShaderRipper.Application)——命令行前端,发布时 apphost 改名为ShaderRipper.exe
输出固定为 AssetRipper 导出树
<out>/Assets/Shader/<名字>.shader
<out>/Assets/Resources/<原资源路径>.shader
<out>/Disassembly/<shader>/ 反汇编在 Assets 之外
可直接整体复制覆盖 ExportedProject。无法确定容器路径时打印 [ar] no container path: ... 并回退到 Assets/Shader/。
新功能
--file支持多输入:可重复、逗号/分号分隔、通配符、以及 rm/cp 风格ShaderRipper.exe a.assets b.bundle c.apk --out D://out- APK / ZIP 支持:直接从 apk 解包扫描;APK 内 GUID 命名、无扩展名的数据文件也能识别
- 输入按内容嗅探:PK 头 → zip/apk,
UnityFS→ bundle,合法序列化头 → SerializedFile;扩展名可省(下载后被改名成.1也能直接读) - LZMA 压缩的 UnityFS 块已支持(LZ4 / LZ4HC / LZMA / 未压缩,块级混用均可);LZHAM 给出明确报错
- Resources 路径还原:读取
globalgamemanagers里的 ResourceManager 表(m_Container),把Resources.Load资源还原到Assets/Resources/原路径.shader;Windows / Linux 构建格式一致,APK 从 zip 内读取 - 资源路径重名时追加
_<pathID>,不再静默覆盖 - 诊断开关:
--dump-objects <file>、环境变量SHADERRIPPER_DEBUG_RESMAP(_FILE)
实测
| 输入 | 结果 |
|---|---|
| 某匿名商业游戏 APK(Unity 6000.3,GLES3) | 452 shaders → 405 导出(333 落 Assets/Resources/,72 落 Assets/Shader/),0 未翻译指令 |
| 某匿名商业游戏(Unity 2022.3,d3d11) | 63 shaders → 47 Resources + 16 Shader,0 未翻译指令 |
| 合成 LZMA bundle(70 MB payload) | 解码正确,65 shaders 正常导出 |
下载
ShaderRipper.exe:Windows NativeAOT 单文件(约 32 MB)ShaderRipper:Linux 单文件(自包含运行时,约 41 MB)
均内嵌 schema/数据资源,下载即用。
v1.0.1
修复
正确性(输出错误结果):
- 字面量通道选择:
Comps()/ImmLane()双重映射与取模回绕(dstSw 为 yz/zw/yw/xzw/yzw 时全错,如add r2.yz, r2.yyzy, l(0,2,4,0)) ushr改为无符号右移(原先有符号移位会符号扩展高位)and/or非布尔掩码退化为真实位运算(原先按 bool 启发式翻译,位打包代码静默出错)- v14+ SerializedFile 的 ScriptTypes 步长 12→16 字节(Externals 列表全部错位)
- UnityFS flags 带 0x80(blocksInfo 在尾部)的 bundle 数据块起点算错,导致整包跳过
崩溃 / 资源泄漏:
- DXC 反汇编的 HGlobal 内存与 COM 对象泄漏(每次调用确定性增长)
DecodeSlots对畸形数据的越界读- bundle 目录扫描遇无权限子目录整个进程崩溃
--mat-check报告路径参数被误吞为数据目录
次要:
- GLSL 分支补
#include "UnityCG.cginc"(使用内建 uniform 的产物原先无法编译) - StructuredBuffer 按 stage 重复声明导致重定义编译错误
- 同名 shader 的 Disassembly 目录互相覆盖(去重提前)
|reg|绝对值修饰符被静默丢弃(现在输出abs())- v22 64 位 byteStart 不再截断,损坏条目跳过而不是产出错误数据
- d3d9 SM2 的 TEXCRDOUT 寄存器名修正(oT#)
- ShaderLab 字符串转义引号;
round_ne语义差异注释
新功能
--file <path>:直接指定单个输入文件——.assets序列化文件或.bundle/.assetbundle/.unity3d容器(第一个位置参数直接给文件路径也可以)--list-shaders <file>:真正实现,列出 SerializedFile/bundle 中的全部 shader 名--schema参数接通(原先被静默忽略)
下载
ShaderRipper.exe:Windows NativeAOT 单文件(约 30 MB)ShaderRipper:Linux 单文件(自包含运行时,约 53 MB)
均内嵌 schema/数据资源,下载即用。Linux 构建已在 Unity 6000.3.10f1 数据(418 shaders)上实测。
1.0.0
English | 简体中文
ShaderRipper 1.0.0
从 Unity 游戏数据中提取 Shader,重建为可读的 ShaderLab + CGPROGRAM。纯 C#,全资源内嵌,下载即用。
支持平台:d3d11(DXBC 汇编逐条直译 Cg)、GLES3 / GLES2 / OpenGLCore(GLSL 回译 Cg)、d3d9(自研反汇编)、D3D12(DXIL,经 dxcompiler);Metal / Vulkan 仅输出参考文件。全平台反汇编均可用(Windows / Linux)。
下载
| 文件 | 平台 | 说明 |
|---|---|---|
ShaderRipper.exe |
Windows x64 | 带 CLR 的裁剪单文件(39 MB) |
ShaderRipper |
Linux x64 | 同上,chmod +x 直接运行(56 MB) |
无任何伴生文件:schema、修复表、原生库已全部嵌入可执行文件。
使用
# 最简:指定游戏数据目录(含 resources.assets)
ShaderRipper.exe --data "游戏目录/游戏名_Data"
# 带逐指令反汇编注释
ShaderRipper.exe --data "游戏名_Data" --out 输出目录 --asm详细参数见 README(中文) / README (English)。
⚠️ 兼容性注意:IL2CPP + 仅 D3D 后端的游戏不友好
对 IL2CPP 打包、且只带 d3d9 / d3d11 / d3d12 图形后端 的游戏(Steam 单机大作多为此类),本工具的输出会很受限:
- d3d9 / d3d12:只输出反汇编,不回译 CGPROGRAM
- d3d11:能重建 Cg,但 DXBC 汇编直译的可读性远不如作者源码,且丢失 HLSLcc 级别的结构信息
- 如果游戏连 GLES3 / OpenGLCore 后端都没打包,就没有 GLSL 可回译,基本只能拿到汇编
想获得更好的可读产物,请优先找带 GLES3 / OpenGLCore 后端的游戏数据(移动版、部分 Steam 版的 bundle 里常有)。
English
Extract Shaders from Unity game data and rebuild them as readable ShaderLab + CGPROGRAM. Pure C#, all resources embedded, ready to run.
Supported platforms: d3d11 (instruction-by-instruction DXBC→Cg translation), GLES3 / GLES2 / OpenGLCore (GLSL back-translation), d3d9 (self-contained disassembler), D3D12 (DXIL via dxcompiler); Metal / Vulkan are exported as reference files. All disassembly paths work on Windows and Linux.
Downloads
| File | Platform | Notes |
|---|---|---|
ShaderRipper.exe |
Windows x64 | Trimmed single-file with CLR host (39 MB) |
ShaderRipper |
Linux x64 | Same, run chmod +x (56 MB) |
No companion files needed: schemas, fix-up tables and native libraries are all embedded in the executable.
Usage
ShaderRipper.exe --data "GameDir/Game_Data"
ShaderRipper.exe --data "Game_Data" --out out_dir --asmSee the README for the full CLI reference.
⚠️ Compatibility: unfriendly to IL2CPP games shipping only D3D backends
For games packed with IL2CPP that only ship d3d9 / d3d11 / d3d12 graphics backends (typical for Steam titles), output is limited:
- d3d9 / d3d12: disassembly only, no CGPROGRAM reconstruction
- d3d11: Cg is rebuilt, but as a direct translation of DXBC assembly — far less readable than original source
- If the game ships no GLES3 / OpenGLCore backend at all, there is no GLSL to back-translate, so assembly is mostly all you get
For more readable output, prefer game data that includes GLES3 / OpenGLCore backends (mobile builds, or bundles in some Steam releases).
Full Changelog: https://github.com/StArraySharp/ShaderRipper/commits/1.0.0