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
- Asset extraction — Unpack
.gzdata files into individual JSON assets - Sprite extraction — Extract individual sprites from texture atlas sheets as PNG
- Dialogue export — Reconstruct
.gumscripts and export to markdown - Localization export — Export localization CSV files matching Murder's editor format
- Engine version detection — Auto-detect engine version from game_config fingerprinting
- Binary analysis — Detect .NET deployment format (NativeAOT, single-file, self-contained)
- C# stub generation — Auto-generate typed C# classes from packed JSON data
- Repacking — Repack modified assets back into
.gzformat - Plugin system — Extend with drop-in
.pyfiles or pip-installable packages
Quick Start
Requirements
- Python 3.11+
- uv (recommended) or pip
- Git (for engine cloning)
- .NET 8 SDK (for building recovered projects)
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 from NativeAOT binaries |
plugins list |
List loaded plugins |
plugins dir |
Show plugin directories |
Recovery Options
murder-unpack recover "path/to/game" recovered/ \
--engine-version rel/11.0 \ # Override auto-detected version
--game-name MyGame \ # Project name (auto-detected if omitted)
--skip-engine \ # Don't clone engine
--engine-path /path/to/murder # Use existing engine
--no-stubs # Skip C# stub generation
Plugin System
Place .py files in ~/.murder-unpack/plugins/ or ./plugins/:
# plugins/my_handler.py
def register(registry):
registry.asset_handlers["my_handler"] = MyHandler()
class MyHandler:
name = "my_handler"
asset_types = ["Custom.Assets.MyAsset"]
def parse(self, asset_data):
return asset_data
def export(self, asset, output_path):
output_path.write_text(str(asset))
Pip-installable plugins use entry points:
[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"
Limitations
Engine Version Detection
Murder Engine does not embed a version string in exported games. Version detection works by fingerprinting which fields are present in game_config — fields were added and removed across major releases. This means:
- Detection covers rel/3.6 through rel/11.0. Unrecognized configs fall back to
main(per Murder Engine convention). - Some version ranges are indistinguishable (e.g., rel/8.0, rel/9.0, and rel/10.0 share identical GameProfile fields — we default to rel/10.0).
- Use
--engine-versionto override if auto-detection picks the wrong version.
NativeAOT Game Recovery
Games compiled with NativeAOT (like most shipped Murder Engine games) have their game logic baked into a native binary. Recovery can extract all data and assets but cannot recover:
- Game-specific components — ECS component structs with behavior logic (e.g.,
Road.Components.*). Generated stubs are empty placeholders that allow the project to compile but have no behavior. - Game-specific systems — ECS systems that drive gameplay logic.
- State machines, interactions, services — All compiled game code.
The editor will open and you can browse assets, but world entities that depend on missing game logic may not render or behave correctly.
Dialogue Reconstruction
.gum script reconstruction from compiled dialogue graphs is best-effort. Original formatting, comments, and some control flow nuances may differ from the source. The semantic content (text, choices, conditions, actions) is preserved.
Repacking
The repack command produces .gz files compatible with Murder's format, but replacing files in a NativeAOT game binary requires additional patching that is not currently automated.
Development
git clone https://github.com/yuna0x0/murder-unpack.git
cd murder-unpack
uv sync
# Run tests
uv run pytest
License
MIT - yuna0x0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file murder_unpack-0.1.5.tar.gz.
File metadata
- Download URL: murder_unpack-0.1.5.tar.gz
- Upload date:
- Size: 65.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
85c9e43eb05ac021f202ea82292fe628f3f481f1f66729187b26c00a259ed27b
|
|
| MD5 |
5c4a26761052ca712d4380d5f290b75d
|
|
| BLAKE2b-256 |
a11e99a767b0c07ceef8629aeb4b68b80a39a266f2384c1b6ae5d4a9229a4d39
|
Provenance
The following attestation bundles were made for murder_unpack-0.1.5.tar.gz:
Publisher:
pypi-publish.yml on yuna0x0/murder-unpack
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
murder_unpack-0.1.5.tar.gz -
Subject digest:
85c9e43eb05ac021f202ea82292fe628f3f481f1f66729187b26c00a259ed27b - Sigstore transparency entry: 1282294971
- Sigstore integration time:
-
Permalink:
yuna0x0/murder-unpack@b74791001d4121a595ca19a714916ad91857d3b1 -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/yuna0x0
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@b74791001d4121a595ca19a714916ad91857d3b1 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file murder_unpack-0.1.5-py3-none-any.whl.
File metadata
- Download URL: murder_unpack-0.1.5-py3-none-any.whl
- Upload date:
- Size: 45.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1e3fcda9d521d7b7a6f0f130aace0fbe68f2eceaef08b5f5c98294c93a7d79d
|
|
| MD5 |
8ffce27528a91516ea81692da5ffb29a
|
|
| BLAKE2b-256 |
8d2e69f6f19f42e227cdb579115b96ccf100d17abe5190af98d5db2589d3e16d
|
Provenance
The following attestation bundles were made for murder_unpack-0.1.5-py3-none-any.whl:
Publisher:
pypi-publish.yml on yuna0x0/murder-unpack
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
murder_unpack-0.1.5-py3-none-any.whl -
Subject digest:
c1e3fcda9d521d7b7a6f0f130aace0fbe68f2eceaef08b5f5c98294c93a7d79d - Sigstore transparency entry: 1282294998
- Sigstore integration time:
-
Permalink:
yuna0x0/murder-unpack@b74791001d4121a595ca19a714916ad91857d3b1 -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/yuna0x0
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@b74791001d4121a595ca19a714916ad91857d3b1 -
Trigger Event:
workflow_dispatch
-
Statement type: