Skip to content
 
 

Repository files navigation

PaintStudioDecomp

build Code Data

This is a work-in-progress decompilation of Mario Artist: Paint Studio (1999) for the 64DD

It is built directly on top of LuigiBlood's original decompilation of the game.

Required base ROM file:

  • dmpj.d64 with MD5 3546bc6d93928a28285a91478bc41b68

Convert a Mario Artist Paint Studio NDD dump to D64 with LuigiBlood's leo64dd_python tool. The RAM area should be only 0xFF. You can create the dmpj.d64 file by taking a .ndd file (md5 8485643e5830cd67ed4c0a5fd49e2491) and running python3 leo64ddfile.py d64 <path-to-ndd-file> <path-to-repo>/dmpj.d64

Quick Start

This repository uses Git submodules. Make sure to either clone the repository with the --recurse-submodules flag or to run git submodule update --init --recursive after the initial clone.

When pulling updates, you can update all submodules with git submodule update --recursive.

If dependencies are already installed and dmpj.d64 is in the repo root:

python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -r requirements.txt
python3 -m pip install -r tools/splat/requirements.txt
make setup
make

Prerequisites

  • Python 3
  • make
  • git
  • curl
  • md5sum
  • a MIPS binutils toolchain that provides one of these prefixes:
    • mips-linux-gnu-*
    • mipsel-linux-gnu-*
    • mips64-elf-*
    • mips-elf-*

make setup will:

  • initialize/update submodules
  • download IDO 7.1 into tools/ido_recomp/<host>/7.1
  • run tools/splat to regenerate asm/, bin/, and assets/ from dmpj.d64

Linux/WSL

Ubuntu/Debian:

sudo apt-get update
sudo apt-get install -y \
	binutils-mips-linux-gnu \
	gcc \
	git \
	libc-dev \
	libc6-dev \
	make \
	ninja-build \
	openssl \
	python3 \
	python3-pip \
	python3.12-venv \
	curl

macOS

Install Xcode Command Line Tools first if they are not already installed:

xcode-select --install

Download Homebrew and install the required packages using it:

brew install \
	coreutils \
	git \
	make \
	mipsel-linux-gnu-binutils \
	python

Diffing a Function

Both diffing tools below compare your current build's object files against a known-good baseline (expected/build), so the general workflow is:

  1. make setup - only needed once, or after pulling changes that touch splat.yaml/the base ROM. Regenerates asm/, bin/, and assets/ from dmpj.d64.
  2. make - rebuilds the ROM from current source. If it succeeds, md5sum -c checksum.md5 prints build/dmpj.d64: OK, confirming a bit-exact match against the original ROM regardless of which functions are matched yet (unmatched functions still compile to identical bytes via their GLOBAL_ASM stubs).
  3. cp -r build expected/build - snapshot that known-good build as the baseline. Only needs to be redone after step 1/2 change what "known-good" looks like (e.g. after make setup, or after pulling other people's matches).
  4. Diff a specific function with asm-differ or objdiff (below) to see if your in-progress C matches.

Using asm-differ

asm-differ (tools/asm-differ, a submodule), is a tool used by default on decomp.me, to compare your current C output against the target assembly for a function.

Before diffing for the first time, snapshot a known-good build as the baseline to diff against:

make
cp -r build expected/build

Then diff a function by name (or address):

python3 tools/asm-differ/diff.py -mo func_80007D64
  • -m rebuilds automatically via make before diffing
  • -o diffs the object file so symbol names resolve
  • add -w to re-run automatically whenever the source file is saved

Run python3 tools/asm-differ/diff.py --help for the full list of options.

Using objdiff

objdiff is an alternative diffing tool also used on decomp.me with a GUI, live rebuild-on-save, and a function/object browser - also the same diffing engine decomp.me uses under the hood, so a scratch that matches in the browser will match here too. The project's objdiff.json (repo root) is already configured with one unit per c-type source file.

Install it (either works):

# prebuilt binary - see https://github.com/encounter/objdiff/releases
# or, with Rust installed:
cargo install --locked --git https://github.com/encounter/objdiff.git objdiff-gui objdiff-cli

Like asm-differ, it needs a known-good baseline to diff against:

make
cp -r build expected/build

Then launch the GUI, pointing it at the repo root (it won't auto-detect the current directory):

objdiff -p /path/to/PaintStudioDecomp
  • The sidebar lists every unit from objdiff.json (one per c-type source file, e.g. src/gameboot.c).
  • Click a unit to open its diff view: target assembly on the left, your current build on the right, function by function. Matching lines are unhighlighted; mismatches are highlighted, similar to asm-differ's output.
  • Each unit also shows a match percentage in the sidebar so you can see progress at a glance.
  • With the window focused, save the source file you're working on and objdiff rebuilds and re-diffs automatically (via watch_patterns in objdiff.json) - no need to switch back to a terminal.

To check overall progress without the GUI, use the CLI instead:

objdiff-cli report generate

Using decomp.me

Functions can also be matched entirely in the browser using decomp.me, which doesn't require a local build environment. This is useful for getting help from others, or for working without a full local setup.

decomp.me uses asm-differ as its diff viewer, the same tool tools/asm-differ provides locally. Because both sides use the same diffing logic, the score and diff output you see on decomp.me are exactly what you'd get from tools/asm-differ/diff.py locally.

Go to decomp.me/new and create a new scratch:

  • Platform: Nintendo 64
  • Preset: Mario Paint Studio
  • Compiler: ido7.1
  • Diff Label: asm label from where function begins
  • Target assembly: paste the contents of the function's .s file
  • Context: paste the contents of ctx.c

About

Work-In-Progress decompilation of Mario Artist: Paint Studio (64DD, 1999)

Resources

Contributing

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages