Skip to main content

Decompile, unpack, recover, and repack tools for Murder Engine games

Project description

murder-unpack

Reverse-engineer exported Murder Engine games back into editor-openable projects. Extracts sprites, dialogues, world data, and more.

Features

  • Project recovery — Reconstruct a Murder Engine editor project from an exported game
  • C# decompilation — Full source recovery from managed single-file bundles via bundled per-type decompiler (ilspycmd fallback)
  • C# stub generation — Fallback: auto-generate typed C# classes from packed JSON data (NativeAOT games)
  • Per-game fixes — Auto-detected decompiler artifact fixes with extensible registry
  • Asset extraction — Unpack .gz data files into individual JSON assets
  • Sprite extraction — Extract individual sprites from texture atlas sheets as PNG
  • Dialogue export — Reconstruct .gum scripts and export to markdown
  • Localization export — Export localization CSV files matching Murder's editor format
  • Engine version detection — Per-game lookup for known games, fingerprint detection for unknown games
  • macOS .app support — All commands accept .app bundle paths directly
  • Binary analysis — Detect .NET deployment format (NativeAOT, single-file, self-contained)
  • Repacking — Repack modified assets back into .gz format
  • Plugin system — Extend with drop-in .py files or pip-installable packages

Quick Start

Requirements

  • Python 3.11+
  • uv (recommended) or pip
  • Git (for engine cloning)
  • .NET SDK matching the game's engine version (net7.0 or net8.0; for building recovered projects and the bundled decompiler)

Install

# With uv (recommended)
uv tool install murder-unpack

# Or from source
git clone https://github.com/yuna0x0/murder-unpack.git
cd murder-unpack
uv sync

Usage

# Show game info and detected engine version
murder-unpack info "path/to/game"

# Extract all data, sprites, dialogues, and localization
murder-unpack extract-all "path/to/game" output/

# Recover into a full editor project
murder-unpack recover "path/to/game" recovered/

# List assets with optional filters
murder-unpack list-assets "path/to/game" --type WorldAsset

Commands

Command Description
info Show game info, asset counts, detected engine version
extract-all Full extraction: data, sprites, dialogues, localization
extract-data Dump all .gz data files as plain JSON
extract-sprites Extract sprites from atlas sheets as PNG
extract-dialogue Export dialogues as .gum scripts, markdown, or both
list-assets List assets with --type and --name filters
decode-qoi Convert a single QOI image to PNG
recover Full editor project recovery
engine-versions List available Murder Engine branches and tags
repack Repack modified assets back into .gz format
analyze-binary Detect .NET format, extract types, decompile
plugins List loaded plugins and plugin directories

Recovery Options

murder-unpack recover "path/to/game" recovered/ \
    --engine-version rel/11.0 \    # Override auto-detected version (branch/tag/commit)
    --engine-repo https://...  \   # Use a fork or custom engine repo
    --game-name MyGame \           # Project name (auto-detected)
    --engine-path /path/to/murder  # Use existing engine clone
    --skip-engine \                # Don't clone engine
    --no-stubs \                   # Skip C# stub/decompilation
    --decompile-timeout 1200       # Decompilation timeout (default: 600)
    --game-fix neverway            # Per-game fix (auto-detected, 'none' to skip)

Per-Game Fixes

Decompiled code sometimes has game-specific issues that can't be fixed generically (lost tuple element names, readonly field assignments, duplicate local functions). The fix registry auto-detects the game and applies known fixes.

Detection uses: assembly name, game namespace, Steam App ID, or game_config $type. Each game fix can also provide the exact engine commit for accurate recovery.

Adding a fix for a new game

Create murder_unpack/fixes/my_game.py:

from murder_unpack.fixes import GameFix, Replacement

FIX = GameFix(
    id="my-game",
    name="My Game",
    assembly_names=["MyGame"],
    steam_app_ids=["123456"],
    engine_version="abc123...",  # exact engine commit (optional)
    replacements=[
        Replacement(
            file_glob="**/SomeFile.cs",
            old="broken code",
            new="fixed code",
            description="CS1234: description of the issue",
        ),
    ],
)

Register in murder_unpack/fixes/__init__.py:

def _load_builtin_fixes() -> None:
    from murder_unpack.fixes import my_game
    _registry.register(my_game.FIX)

Or register via plugin:

def register(registry):
    from murder_unpack.fixes import get_registry
    get_registry().register(my_fix)

Plugin System

Plugins extend murder-unpack with custom asset handlers, extractors, commands, and hooks.

Drop-in plugins — Place .py files in ~/.murder-unpack/plugins/ or ./plugins/:

# plugins/my_plugin.py
def register(registry):
    registry.asset_handlers["my_handler"] = MyHandler()

class MyHandler:
    name = "my_handler"
    asset_types = ["Custom.Assets.MyAsset"]

    def export(self, asset, output_path):
        output_path.write_text(str(asset))

Pip-installable plugins — Use entry points in pyproject.toml:

[project.entry-points."murder_unpack.asset_handlers"]
my_handler = "my_plugin:MyHandler"

[project.entry-points."murder_unpack.commands"]
my_cmd = "my_plugin.cli:my_command"

Available extension points: asset_handlers, extractors, commands, hooks (pre_extract, post_extract, pre_recover, post_recover)

Limitations

C# Source Recovery

  • Managed single-file bundles — Full source recovery via bundled decompile-helper (per-type decompilation with timeouts). Falls back to ilspycmd, then to stub generation.
  • NativeAOT binaries — Cannot be decompiled. Recovery generates C# stubs for compilation but without behavior.

With full decompilation, the recovery uses the decompiled game class directly and applies targeted compatibility fixes: initset on engine types that cause CS8852 errors (detected via trial build), readonly removal on class fields for JSON deserialization, and JsonStringEnumConverter for string-based enum keys.

Engine Version Detection

For known games, the fix registry provides the exact engine commit. For unknown games, fingerprint detection covers rel/3.6 through rel/11.0 (some ranges are indistinguishable, e.g. rel/8.0-10.0 default to rel/10.0). Use --engine-version to override with a branch, tag, or commit hash.

Dialogue Reconstruction

.gum script reconstruction from compiled dialogue graphs is best-effort. Semantic content is preserved; original formatting may differ.

Development

git clone https://github.com/yuna0x0/murder-unpack.git
cd murder-unpack
uv sync

AI Disclosure

AI was used to assist in the creation of some of this tool's base code.

License

MIT - yuna0x0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

murder_unpack-0.2.4.tar.gz (88.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

murder_unpack-0.2.4-py3-none-any.whl (72.0 kB view details)

Uploaded Python 3

File details

Details for the file murder_unpack-0.2.4.tar.gz.

File metadata

  • Download URL: murder_unpack-0.2.4.tar.gz
  • Upload date:
  • Size: 88.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for murder_unpack-0.2.4.tar.gz
Algorithm Hash digest
SHA256 3a3960e145366e9a4de8cb60c10e11bfa1a23d9a11d541275aad8a110e292291
MD5 58cd22af4376d6f2b2a6e1e4c94cae5a
BLAKE2b-256 f587ba5b6522b379ba4daf2e18964c6005650d72565257459f3aa9f00770b76b

See more details on using hashes here.

Provenance

The following attestation bundles were made for murder_unpack-0.2.4.tar.gz:

Publisher: release.yml on yuna0x0/murder-unpack

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file murder_unpack-0.2.4-py3-none-any.whl.

File metadata

  • Download URL: murder_unpack-0.2.4-py3-none-any.whl
  • Upload date:
  • Size: 72.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for murder_unpack-0.2.4-py3-none-any.whl
Algorithm Hash digest
SHA256 9953b30a1c1bbc4fd3bac7739a4e0419fe38045091ac0ad8b356aaa40a8cd507
MD5 209679a35bfdbe4ed402109815b7cf25
BLAKE2b-256 3d2bc416549a22e36c8195ea9263e399df6f71584a45702e0a1439396da9155c

See more details on using hashes here.

Provenance

The following attestation bundles were made for murder_unpack-0.2.4-py3-none-any.whl:

Publisher: release.yml on yuna0x0/murder-unpack

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page