Skip to content

Releases

v1.2.2

Choose a tag to compare

@StArraySharp StArraySharp released this 06 Nov 16:28
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

Choose a tag to compare

@StArraySharp StArraySharp released this 29 Oct 13:46

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

Choose a tag to compare

@StArraySharp StArraySharp released this 28 Oct 02:41

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
    mad sat 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
    reads r0.x in the first row, overwrites it with the object-to-world w, then
    reads it again in the closing row. Such a component is now pinned into a local
    where it still held its old value:
    float 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));
    This is also why such a shader is deliberately not folded into
    UnityObjectToClipPos — its clip position is built from object-space xyz
    plus the w of 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 to t1, t2, …. A name already bound
    is never handed out twice (redefinition of '_MainTex').
  • ShaderLab tag casing. Tag keys and LightMode values 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
    bundle AssetBundle.m_Container), else the shader's own name with / → _,
    else a _1 suffix. 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. Reading args[0] as the command word
    made the first form fail with "unknown option --shaderlab".
  • --game and --file-bare are 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-x64 publishes embed libdxcompiler.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.py removed: 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.py regenerates all five grammars, not just DxbcAsm.
  • Corpus (425 shaders / 3,263 programs): verify_logic.py 3195/3195,
    scan_unwritten 0, scan_nan 0 escaping / 0 contained, scan_props 0 leaked.

v1.1.1 — output that compiled but rendered wrong

Choose a tag to compare

@StArraySharp StArraySharp released this 21 Oct 11:09

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 in UI/Pause Planets).
  • sincos with 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 roundEven and gl_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.
  • --dedup keeps one copy per shader when several data files ship the same one, preferring the copy with a fixed container path.
  • scripts/usc_validate.py re-checks a whole output tree with Unity's own UnityShaderCompiler (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

Choose a tag to compare

@StArraySharp StArraySharp released this 21 Oct 02:26

重大变化

项目拆分为两层

  • 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

Choose a tag to compare

@StArraySharp StArraySharp released this 20 Oct 21:48

修复

正确性(输出错误结果):

  • 字面量通道选择: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

Choose a tag to compare

@StArraySharp StArraySharp released this 20 Oct 12:18

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 --asm

See 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