dream-shader-decompile · git:20260921.c798454 · 2026-09-21 · sha256 fe5553a485c4ef8a

dream-shader-decompile git:20260921.c798454A

Immutable. This exact content never changes and is served at /api/v1/blob/fe5553a485c4ef8a.

---
name: dream-shader-decompile
description: Export an existing Unreal material, material function, layer, layer blend or material instance back into DreamShaderLang source headlessly, so a hand-built material graph can be migrated to a text source file. Use when asked to decompile, export, reverse, convert, or migrate a material / material function / material layer / material instance asset to .dss or .dsi (or, with -Format Legacy, to .dsm / .dsf).
---

# dream-shader-decompile `<asset>`

Read an existing asset's node graph and write the DreamShaderLang source that builds it. Since 2.0
the default output is **2.0 text** — a `.dss`, or a `.dsi` for a material instance — written
by the compiler run backwards (graph → IR → AST → printer). The 1.x exporter that writes `.dsm` /
`.dsf` is still there behind `-Format Legacy`.

This is the front half of a migration; the back half is
[`dream-shader-optimize`](../dream-shader-optimize/SKILL.md).

Paths below are relative to the plugin root, `Plugins/DreamShader/`.

## Run it

```bash
pwsh -File Plugins/DreamShader/.skill/dsc.ps1 decompile /Game/Materials/M_Steel
```

```bash
pwsh -File Plugins/DreamShader/.skill/dsc.ps1 decompile /Game/Materials/MI_Steel_Worn
```

```bash
pwsh -File Plugins/DreamShader/.skill/dsc.ps1 decompile /LGUI/Materials/LexUI_RectBlock -Out I:/Work/LexUI_RectBlock.dss -KeepAssetPath
```

Exit `0` writes the file and logs where, and in which format:

```text
LogDreamShader: Display: DreamShader decompiled '/Game/Materials/M_Steel.M_Steel' to '…/M_Steel.dss' (Dss).
dsc: OK (exit 0)
```

| Switch | |
| :-- | :-- |
| `-Out <file>` | where to write. Its extension picks the format when `-Format` is absent: `.dsm` / `.dsf` is 1.x text, anything else 2.0. |
| `-Format Dss\|Legacy\|Auto` | `Auto` is the default. A format that contradicts the extension — or a `.dss` for an instance — is `DSH9085`. |
| `-SourceFile <source>` | instead of an asset: decompile **every asset that source builds** into one file. Build the source first (`DSH9087`). |
| `-KeepAssetPath` | write `/// @name <the asset's own path>` wherever the output file's place would name another asset, so a rebuild **takes over the original**. |
| `-Readable` | prefer HLSL sugar over class-exact node calls. Easier to read; a rebuilt graph may then differ in node classes. |
| `-DiagnosticsOut <file>` | the decompile's diagnostics as JSON, written whether or not it succeeded. |

Without `-Out` the destination is computed from the asset's package path:

| Asset class | Lands in | Extension |
| :-- | :-- | :-- |
| `UMaterial` | `DShader/Decompiled/Materials/<package path>` | `.dss` |
| `UMaterialFunction` | `DShader/Decompiled/Functions/<package path>` | `.dss` |
| `UMaterialFunctionMaterialLayer` | `DShader/Decompiled/Layers/<package path>` | `.dss` |
| `UMaterialFunctionMaterialLayerBlend` | `DShader/Decompiled/LayerBlends/<package path>` | `.dss` |
| `UMaterialInstanceConstant` | `DShader/Decompiled/Instances/<package path>` | `.dsi` |

So `/Game/Materials/Metal/M_Steel` → `DShader/Decompiled/Materials/Game/Materials/Metal/M_Steel.dss`.
With `-Format Legacy` the same places get `.dsm` / `.dsf`, and an instance is refused.

## What comes out

```hlsl
// Decompiled by DreamShader from /Game/Materials/M_Steel.M_Steel
uniform float A = 1.0;
uniform float B = 2.0;

export void M_Steel(inout material m)
{
    float Sum = A + B;
    float Twice = Sum * Sum;

    m.Opacity = saturate(Twice - Sum);
    m.Roughness = A * 0.5;
}
```

- Uniforms come out in the material's display order; `/// @sort` is written only where a priority is
  not the uniform's place in that order.
- A value gets a local when it is read twice, or when the statement that made it named it; the rest
  is written inline.
- A Custom node comes back as a `/// @custom` function with its helpers; a function asset the graph
  calls becomes an `extern` prototype with `/// @asset`.
- Comment boxes are `#pragma region`s, node positions are `#pragma layout` hints.
- A material instance comes out as a [`.dsi`](../../Docs/language-v2/instances.md): its parent, the
  `#pragma instance` keys it overrides, and only the parameters that **differ from the parent**.
- A DreamShader thin-custom pair (hidden `MB_DreamThinBase_*` base + instance) is decompiled as the
  one material it is, with `Backend = ThinCustom` — pass the instance.

## After the export — do this, in order

1. **Read the `// Warning: DSHnnnn: …` lines** under the `// Decompiled by DreamShader from …`
   header. Each names something the language cannot state (`DSH9060`–`DSH9084`). They are the
   migration work list; [`Docs/diagnostics/DSH9xxx.md`](../../Docs/diagnostics/DSH9xxx.md) says what
   each one means and what to do about it.
2. **Compile it as-is**, to establish that the export is at least buildable:
   ```bash
   pwsh -File Plugins/DreamShader/.skill/dsc.ps1 compile DShader/Decompiled/Materials/…/M_Steel.dss -Force -CleanNew
   ```
   Without `-KeepAssetPath` this is safe by construction: a `.dss` builds its asset where the file
   is, so a file under `Decompiled/…` builds a *new* asset (`/Game/Decompiled/Materials/…`) and
   leaves the original alone.
3. **Clean it up** — [`dream-shader-optimize`](../dream-shader-optimize/SKILL.md).
4. **Only then** let it take over the original: move the file to the place that names the original
   asset, or add `/// @name /Game/Materials/Metal/M_Steel` to the export (what `-KeepAssetPath`
   would have written), and delete nothing until the rebuilt asset has been looked at.

## Gotchas

- **The 2.0 output is proved, the 1.x output is not.** A 2.0 decompile is parsed again before it is
  written (`DSH9089` if that fails — a decompiler defect, not an asset problem), and the round-trip
  suite compiles decompiled text and compares IRs. The 1.x exporter stays "a migration starting
  point"; read
  [`Docs/tools/decompiler.md`](../../Docs/tools/decompiler.md#known-round-trip-gaps) before trusting
  a `-Format Legacy` export.
- **What 2.0 text cannot say is named, never dropped silently.** Known today: a default expression
  wired into a function input's Preview pin (`DSH9071`), additional defines on a Custom node
  (`DSH9067`), a node class outside the builtin catalog (`DSH9065`), a function asset that no longer
  exists (`DSH9064`), a node property that is an array, a map or a delegate (`DSH9068`).
- **An instance decompile writes overrides only.** A parameter the instance sets to the parent's own
  value is not an override and is left out; layer-only parameters and overrides the language cannot
  state are named (`DSH9101`, `DSH9104`, `DSH9105`).
- **In Git Bash, a leading-slash asset path is mangled.** `/LGUI/Materials/X` becomes
  `C:/Program Files/Git/LGUI/Materials/X` and the asset "cannot be loaded". Run decompiles from
  PowerShell.
- The asset path is normalised: `\` → `/`, and a path starting with `/` that contains no `.` gets
  the short name appended, so `/Game/Path/Asset` is loaded as `/Game/Path/Asset.Asset`. Both forms
  work.
- **Layout export is a project setting** (*Export Decompiled Layout*, default on). With it off you
  lose node positions and the regenerated graph is auto-laid-out. Regions are emitted regardless.
- Comment boxes whose text begins with `DreamShader: ` are generated markers and are deliberately
  not re-emitted.
- `-Format Legacy` only: struct-, array-, map- and set-valued node properties are dropped silently,
  and material instances are rejected outright.

## Troubleshooting

| Message | Cause |
| :-- | :-- |
| `DreamShader could not load asset '…'.` | the object path is wrong, or the plugin/mount point is not loaded. Check the path in the Content Browser's *Copy Reference* |
| `DSH9085` | the format and the output file disagree: `-Format Dss` with `-Out x.dsm`, or a material instance with anything but a `.dsi` |
| `DSH9086` | the asset is not a material, function, layer, blend or material instance |
| `DSH9087` | `-SourceFile`: the source does not resolve to its products, or a product has not been built yet |
| `DSH9088` / `DSH9089` | the graph did not read into a valid module / the printed text does not parse back. Both are decompiler defects — report them with the asset |
| `MaterialFunction '…' does not expose any outputs.` | `-Format Legacy`: the function declares no outputs; nothing to export |
| exports, but the graph is full of `UE.<Class>(…)` | expected for nodes the language has no sugar for. `-Readable` trades class-exactness for readability |

## See also

- [`Docs/tools/decompiler.md`](../../Docs/tools/decompiler.md) — both decompilers, and what each reproduces
- [`Docs/language-v2/instances.md`](../../Docs/language-v2/instances.md) — the `.dsi` an instance decompiles to
- [`Docs/tools/migrate.md`](../../Docs/tools/migrate.md) — `dsc migrate`, for 1.x *sources* rather than assets
- [`dream-shader-optimize`](../dream-shader-optimize/SKILL.md) — the required next step
- [`dream-shader-verify`](../dream-shader-verify/SKILL.md) — the compile gate