Skip to content
ThanukamaxPublic

About

Cut Unreal Engine game folder size by astronomical amounts. Tauri v2 + Rust. Drop a game folder, get a smaller game folder.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

shrinkray

Cut Unreal Engine game folder size by trimming what you don't need.

Drop a folder. Get a smaller folder. Refuse to write without a backup.


Stack Sidecar Tests Build License


analyze · audit · strip languages · trim paks · recompress loose files · backup & restore · cooked-asset inspection · mip-strip projection · Δ-Codec residual compression


Note

What this is. An integrated analyze → trim → smaller game workflow for UE4/UE5 installs, with a backup-or-refuse policy from day one.

What it isn't. A pak recompressor, a transcoder, or a magic wand for AAA UE5 IoStore titles. Honest scope below — see What shrinkray will NOT do.


At a glance

Best real-world result so far

Pamali (UE4 demo, 591 MB install)

185 textures identified
739 MB total texture data
570 MB reclaimable  (77 %)
   ↑ at a 1024 px mip-0 cap

Top wins are 4 K hair masks cooked at full res that nobody zooms in on. Read-only projection today; byte-exact write lands in v0.6.

What ships in v0.5.0

  • 13-detector read-only bloat audit
  • Cooked .uasset inspector (mip table, export list, custom-version fingerprint)
  • Mip-strip projection with per-format BC/ASTC byte formula
  • L10N strip + pak trim + loose-file recompress
  • Differential backup + restore with hash verification
  • Win7 Aero UI, custom title bar, in-app file dialog
  • CLI: shrinkray audit <path>

Screenshots

shrinkray main window — Win7 Aero chrome over the procedurally-generated wallpaper, preview-mode pill on the header, pick-folder + Inspect-a-pak + Texture-mip-strip panels stacked vertically
Main window — Aero chrome, preview-mode default-on, the three Phase 2 entry panels (pick folder · Inspect a pak · Texture mip strip)

More panels (analyze report, bloat audit, asset inspector, mip-strip projection, in-app file dialog) land in docs/screenshots/ as we capture them.


Table of contents


Why

UE games ship with predictable bloat — but not the bloat most tools promise to fix. A research pass (see research/SUMMARY.md) found modern UE5 already ships near-optimal codecs (Oodle Kraken, Bink Audio, BCn textures) that a third-party tool cannot meaningfully beat. The real size wins come from dropping content, not transcoding it:

  • Unused languages — a fully-voiced AAA ships 4–8 dub languages; you only play one. Often 30–60 % of the audio bucket alone, zero quality loss.
  • Orphan pak entries — dev/debug paks, duplicated shader caches, leftover platform variants.
  • Loose marketplace / modded assets — uncooked .png/.wav/.flac re-encodable with no parser needed.
  • Asset-flip / RPG-Maker-on-UE bloat — sometimes 80 % of the install is content that's never referenced.
  • Oversized mip 0 textures — 4 K hair masks, 4 K decals, anything where the source artist forgot to set a max LOD.

Existing tooling (UnrealPak, umodel, FModel, UAssetGUI, repak) reads UE assets but doesn't apply optimizations. shrinkray is the integrated workflow on top.


The pipeline

flowchart LR
    A[Drop a folder] --> B[Analyze<br/>census · L10N · paks · top 50]
    A --> C[Bloat audit<br/>13 detectors · 0-100 score]
    B --> D[Asset Inspector<br/>cooked .uasset drill-down]
    D --> E[Mip-strip projection<br/>per-format byte formula]
    B --> F{Preview mode?}
    F -->|yes| G[Diff report only]
    F -->|no| H[Differential backup<br/>SHA-256 manifest]
    H --> I[Apply ops<br/>L10N strip · pak trim · recompress]
    I --> J[Smaller folder]
    G -.->|preview only| J
    H -.->|restore| K[shrinkray restore]
    K --> A

    style A fill:#0ea5e9,stroke:#0369a1,color:#fff
    style J fill:#16a34a,stroke:#15803d,color:#fff
    style H fill:#f59e0b,stroke:#b45309,color:#fff
    style F fill:#7c3aed,stroke:#5b21b6,color:#fff
    style K fill:#dc2626,stroke:#991b1b,color:#fff
Loading

Every destructive op is preceded by a differential backup entry. Preview mode is default-on for first-time users — every "Apply" button is hard-disabled until you opt out.


Realistic targets

Game type Typical savings Where they come from
Asset-flip indie / RPG Maker UE ports 50–80 % Loose-file recompression + orphan trimming
Heavily-modded titles 30–60 % Uncompressed audio re-encode + language stripping
Mid-tier indie 20–40 % Language stripping + dev/debug pak removal
UE4 game w/ readable paks (Pamali class) up to 77 % of texture bytes Mip-strip projection (v0.6 will apply it)
Already-optimized AAA UE5 (IoStore) 5–15 % Language stripping (the bulk) + orphan trimming

These are the upper-realistic numbers. The lower bound on a modern UE5 single-language AAA install is often <5 %. shrinkray surfaces the predicted delta in the diff report before any write.


Δ-Codec (research artifact)

Separate from the trim/backup workflow, shrinkray ships Δ-Codec — a texture compression scheme that puts both an AI-fast prediction and a byte-exact residual in one bitstream. Restore re-runs the predictor and adds the residual back: byte-exact in RGBA at quant_step == 1, verified by a per-texture SHA-256 receipt (the anti-cheat-safe property AI upscalers can't offer).

What the validation actually says (paper-grade, not marketing):

  • 126 / 126 byte-exact restores across 3 independent bench runs — the lossless invariant holds.
  • Bilinear is a shockingly strong baseline for 1K PBR game content: median residual sidecar is 24.5 % of a full backup. PBR is engineered smooth + tileable, so bilinear is near-optimal by construction.
  • ESRGAN wins only the niche (11 % of textures — industrial/fine-detail normals). A perfect per-texture oracle beats forced-bilinear by just 5.3 %, so bilinear ships as the default predictor; ESRGAN is opt-in.

The bitstream is predictor-agnostic (PredictorId enum) — future game-content-trained predictors slot in without a decoder recompile.

→ Spec: docs/delta-codec-spec.md · Diagrams: docs/architecture-diagrams.md · Standalone crate: delta-mip (Apache-2.0)


What shrinkray will NOT do (v1)

Important

Honesty about scope, because half the existing UE-tool ecosystem overpromises.

Click to expand — the deliberate "no" list
  • No pak recompression. Oodle Kraken (the UE5 default) beats Zstd on both ratio and decode speed for game data. Free Oodle encoders don't legally exist. Transcoding to Zstd would actually inflate or stutter.
  • No cooked .uasset/.uexp/.ubulk surgery (write-side). Correctly round-tripping cooked UE assets needs a serializer that tracks ~50 per-subsystem custom versions across every engine release. Only CUE4Parse (read) and UAssetAPI (read/write) do that today. Read-side ships in v0.5; write-side lands v0.6 via a bundled UAssetAPI sidecar.
  • No Vorbis re-encode by default. Vorbis → Opus is realistically ~20–25 % per file with a lossy → lossy quality cost — opt-in only in v2.
  • No Bink Audio anything. No free encoder, no Rust binding.
  • No BC3 → BC7 transcode. Same 8 bpp, 0 % size win.
  • No IoStore .utoc/.ucas rewriting in v1. Most UE5 AAA install bytes live in IoStore; v1 reports them but leaves them alone. v0.7 wires in retoc.

If the size you wanted came from those features, v1 is not for you yet.


Safety model

Warning

shrinkray refuses to write without a backup.

On first run it offers:

  • Differential backup (default) — records only the bytes about to be overwritten. Typically 5–15 % of the folder.
  • Full copy — safe but expensive on a 100 GB AAA folder.
  • Abort.
shrinkray restore <folder>   # replays the manifest in reverse with hash verification

Signed paks (.sig sibling present) and undecryptable AES paks are skipped, never partially modified. The restore button is wired into the diff report from day one.


Roadmap

Phase What State
0 Scaffold + folder census by extension, rough savings estimate
1 Scan & report + differential backup/restore + loose-file recompression + L10N stripping + pak trimming + dry-run + diff report UI
1.5 Read-only multi-detector bloat audit (13 detectors), Win7 Aero UI, in-app file dialog, custom title bar
2 read-side Cooked-asset inspection via bundled .NET sidecar (CUE4Parse master, vendored): mip-strip projection (byte-exact), per-package inspection
2 write-side Cooked .uasset/.uexp/.ubulk rewrite — texture mip strip apply, audio L10N replacement inside paks. Needs UAssetAPI bundle.
2 IoStore retoc integration for UE5 AAA .utoc/.ucas containers. AES key handling.
3+ Vorbis → Opus opt-in, SSIM validation, mesh LOD strip, orphan-asset cross-reference, screenshot-diff launch validation

The original 6-phase roadmap was rewritten after the research pass — see research/SUMMARY.md for the full reasoning.


Quick start

# Clone WITH submodules (CUE4Parse lives under sidecar/external/CUE4Parse)
git clone --recurse-submodules https://github.com/Thanukamax/shrinkray
cd shrinkray

# If you already cloned without --recurse-submodules:
git submodule update --init

bun install
cd src-tauri && cargo fetch && cd ..

# Build the .NET sidecar (requires dotnet 8 SDK)
bash scripts/build-sidecar.sh

python3 scripts/generate_icons.py    # placeholder icons; one-time
bun run tauri dev                    # window at localhost:1420
bun run tauri build                  # production binary
System tool deps (Linux)
  • Required: dotnet-sdk-8.0, opus-tools (for opusenc), libz-ng.so.2
    • Fedora / Nobara: ships by default
    • Debian / Ubuntu: apt install libz-ng opus-tools
  • Optional: oxipng (via cargo install oxipng or your distro)
  • Recommended for sidecar native helpers: cmake + a C/C++ toolchain. The native blob is optional — Linux builds work without it.

Building the AppImage (Linux)

bun run tauri build --bundles appimage will produce target/release/bundle/appimage/shrinkray_<version>_amd64.AppImage (~109 MB, self-contained, runs on any glibc-based x86_64 distro from ~2022 onward).

Required system packages (Fedora 41+/Nobara 43, names approximate on other distros):

sudo dnf install \
  webkit2gtk4.1-devel gtk3-devel libsoup3-devel \
  openssl-devel librsvg2-devel \
  fuse fuse-libs file desktop-file-utils \
  patchelf gobject-introspection-devel

Then build with NO_STRIP=true to skip linuxdeploy's bundled strip binary, which is too old to handle modern ELF .relr.dyn relocation sections (present in every Fedora 41+/Nobara 43 system library):

NO_STRIP=true bun run tauri build --bundles appimage

Without NO_STRIP=true, the bundle step prints hundreds of unknown type [0x13] section .relr.dyn errors and exits non-zero — the AppDir is still fully populated, but linuxdeploy never invokes its appimage plugin. The flag is harmless: shipped libs come pre-stripped from /lib64, and the final AppImage is squashfs-compressed regardless.

Known limitations:

  • The .NET sidecar (src-tauri/binaries/sidecar/) and AI models (src-tauri/binaries/ai-models/) are not yet bundled into the AppImage — features that depend on them (asset inspector, AI restore) will need the sidecar installed alongside or a follow-up resources entry in src-tauri/tauri.conf.json.
  • AppImage runtime needs FUSE2 (fuse-libs on Fedora). On hosts without FUSE, run with --appimage-extract-and-run.

Layout


What this build does

Analyze

Folder census + L10N detection + pak inventory (signed / encrypted / readable / IoStore stub) + top-50 fattest files. CEF locale .pak files correctly filtered out (so they don't inflate the "unreadable" count).

Bloat audit (read-only)

13 detectors, 0–100 bloat score, Markdown + JSON output. Works on encrypted installs where content surgery is impossible:

  • v0.4: patch_overlay, stale_version_dir, sharded_videos, large_chunk, encryption, editor_leftovers, launcher_satellite
  • v0.5: shader_rhi_redundancy, redist_installer, platform_siblings, mod_manager_artifacts, duplicate_content (SHA-256), cef_locales

Asset Inspector (v0.5)

Drill into a single cooked .uasset via the .NET sidecar: full export list, class names, custom-version fingerprint, mip table per texture (per-mip dimensions + byte sizes). Pagination, search, payload/package filters.

Mip-strip projection (v0.5)

Walk every UTexture-derived export in a readable pak, project savings from capping mip 0 dimension. Per-format BC/ASTC byte formula. Pamali UE4 demo: 185 textures → 570 MB save at 1024 px cap (77 %). Read-only; write-side lands v0.6 via UAssetAPI.

L10N strip + pak trim

Drop dub languages from loose files and from inside paks (preserves version / mount / path-hash-seed via repak::into_pakwriter). Empty-after-trim paks deleted; signed + encrypted paks skipped.

Loose-file recompression

PNG via oxipng, WAV / FLAC → Opus via opusenc. Both detected at runtime; missing tools surface install hints in the UI.

Differential backup + restore

Every destructive op is preceded by a shrinkray_backup/ entry. Restore replays the manifest in reverse with hash verification.

Preview mode

Default-on for first-time users; hard-disables every apply button. Toggle is localStorage-backed.

Win7 Aero UI (v0.5)

Custom title bar (Tauri OS decorations off), 7.css window chrome, frosted-glass title bar, in-app Win7-style file dialog, procedurally-generated Aero wallpaper.

CLI

shrinkray audit <path> [--json] [--out FILE]

Run from a terminal, share the markdown, paste it into a bug report.


Stack

  • Tauri v2 + Bun + Vite + React 19 + TypeScript (mirrors the vn2apk + wgpu-shader-explorer pattern)
  • Rust core: walkdir, tauri-plugin-dialog, repak 0.2.3, sha2, dirs. Plus image_dds, intel_tex_2, symphonia, opus, image-compare, rayon.
  • Phase 2 sidecar: .NET 8 self-contained CLI vendoring CUE4Parse master (Apache 2.0) as a Git submodule at sidecar/external/CUE4Parse/. JSON-over-stdin IPC. ~75 MB published binary. Three additive patches to CUE4Parse handle UE4.13-era cook layouts — see CHANGELOG.md v0.5.0.
  • Frontend: 7.css for native Win7 Aero chrome (.window, .title-bar, fieldset / button styling).
  • Shell-out binaries: oxipng. Optional: mozjpeg, cwebp.
  • Not used (yet): FFmpeg, unreal_asset crate, Bink anything. UAssetAPI bundle pending v0.6 write-side.

Project layout

Click to expand the tree
src/                                # React frontend
  App.tsx                           #   top-level shell + sections
  TitleBar.tsx                      #   custom Aero title bar           (v0.5)
  OpenDialog.tsx                    #   in-app Win7 Open dialog         (v0.5)
  AssetInspector.tsx                #   cooked .uasset drill-down       (v0.5)
  MipStripPanel.tsx                 #   texture mip-strip projection    (v0.5)
  assets/wallpaper.jpg              #   procedurally-generated Aero bg  (v0.5)

crates/
  shrinkray-core/src/               # destructive-write subsystems
    analyze.rs                      #   folder census + L10N + CEF filter
    pak.rs                          #   repak wrapper + pak classification
    backup.rs                       #   differential backup + restore
    strip.rs                        #   L10N stripping + pak trimming
    recompress.rs                   #   PNG / WAV / FLAC recompression
  shrinkray-audit/src/              # read-only bloat audit
    detectors/                      #   one .rs per detector, 13 total
  shrinkray-sidecar/src/            # Rust IPC client + types
  shrinkray-cli/src/                # CLI binary

sidecar/
  ShrinkraySidecar/                 # .NET 8 sidecar (CUE4Parse host)
    AssetInspector.cs               #   per-package inspection + mip table
    AssetLister.cs                  #   pak entry enumeration
    StripMipsPlanner.cs             #   walk + project mip-strip savings (v0.5)
    Program.cs                      #   JSON-over-stdin dispatcher
  external/CUE4Parse/               # Git submodule — master + 3 patches

src-tauri/src/
  lib.rs                            # tauri::commands wiring

research/                           # research notes A-E + SUMMARY (read first)
scripts/                            # generate_icons.py, build-sidecar.sh, lyra-smoke.sh
docs/screenshots/                   # README screenshots
.github/workflows/                  # CI (cargo test + clippy + bun build)

Validation against real games

Unit tests build synthetic paks via repak's writer and round-trip them through the trim + backup + restore pipeline — 223 tests, all in CI. That gates correctness of the plumbing, not that shrinkray-modified games still launch.

The one corpus test that would prove launch-safety — Lyra Starter Game cooked PC build, optimize, boot, watch for crash — needs an Epic account to download Lyra source and a full Unreal Engine install to cook a binary, neither of which fits free GitHub runners. Until a self-hosted runner with a pre-cooked corpus exists, Lyra validation is a manual pre-release step: see scripts/lyra-smoke.sh.

GameEngineUse asState
Pamali demoUE4 (4.13-era)readable-pak target — primary mip-strip subject185 textures, 570 MB save at 1024 px
Stellar BladeUE5IoStore corner case (60 GB AAA)Detected: 64 paks all IoStore, retoc Phase 2 needed
AUGUST NIGHTUE5IoStore at small scale (1 pak)Reproduces the IoStore branch
Wuthering WavesUE5fully-encrypted targetAudit-only; content surgery blocked
Lyra Starter GameUE5launch-validation targetManual pre-release; not in CI

License

Source-available, no redistribution — see LICENSE.

You may clone this repo, read the source, and build shrinkray for your own use. You may not redistribute the source or binaries, mirror the repo, or publish forks / modified versions without prior written permission.

Earlier commits (v0.3.x and prior) were MIT-licensed and remain so for anyone who obtained them under that license; this restriction applies to commits from the relicense onward. The copyright holder reserves the right to relicense future versions under permissive terms at any time.


Built with Tauri v2, Rust, React 19, .NET 8, and an unreasonable amount of patience for cooked UE4 textures.

About

Cut Unreal Engine game folder size by astronomical amounts. Tauri v2 + Rust. Drop a game folder, get a smaller game folder.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages