Paper Mario music editor.
Website - Open in your browser - Changelog
Mamar is a web app comprised of a React frontend, Rust supporting libraries compiled to WebAssembly, and the game's audio engine from papermario-dx, also compiled to WebAssembly. The names of the game's sounds come from Star Rod Classic's list of them. The whole thing is client-side only i.e. you can serve it with a simple static file server (the live site uses Vercel for deployments).
Why are some parts Rust? Mamar used to be a desktop application written entirely in Rust! It's also a more suitable language for the kind of encoding/decoding of binary data that Mamar needs to do.
This is a monorepo with a number of modules in it. Following is a more detailed look at each module.
This is the React frontend writen with strict-mode TypeScript. Styles are written in SCSS, with components using CSS modules for local scoping. There is also a landing page that briefly explains what Mamar is, whilst the app itself lives at /app/.
Mamar uses React Spectrum, Adobe's React component library. This means you can quickly build most views with just that and few design decisions need to be made. For more bespoke components, there's no need to fit within the Spectrum design system, so it's no issue to use the UNSAFE_className prop. The most important thing is that components are accessible and keyboard-navigable. For example, the PlaybackControls component has styling that totally overrides Spectrum in order to completely rip off Garageband's UI. If you're thinking of contributing some feature but aren't much of a designer, feel free to focus on the functionality and I'll make it look nice!
State management lives in the mamar-web/app/store directory. The general idea is that we have a single state object that is passed to components via React hooks. State is split into "docs" (documents, but named differently to differentiate from the global builtin document), each of which is an open BGM. If there are multiple docs open, the app shows tabs to switch between them. The bgm field of docs is (de)serializable with the pm64 module.
For performance, react-tracked avoids needless rerenders by figuring out exactly which properties of the state object a component depends on. Each hook also returns a dispatch function to change state. This calls reducers similar to a Redux store, but we don't actually use Redux. Additionally the entire store is wrapped with use-undoable which provides a history of state changes for undo/redo functionality. Changes to anything other than a doc's BGM does not produce a new history entry (see shouldActionCommitToHistory), but undoing will revert the rest of the state also - this results in the UI switching back to wherever the undoable action was performed.
There aren't any tests. This would be good to add in the future to prevent regressions once the design is more settled.
Generally the only browsers we care about are Chrome and Firefox on desktop. I would like to support other engines and mobile devices however WebAssembly speed is really not there yet so there isn't much point.
This is a Rust crate that provides encoding and decoding of Paper Mario's audio file formats, BGM (background music) and SBN (soundbank). BGM is for songs, while SBN is an archive format that holds all the rest of the audio files. There are other file types I'd like to support editing of in the future, specifically, BK (bank) and MSEQ (music sequence). BK holds actual sound samples, while MSEQ is similar to BGM but for the 'ambient sounds' in the game and - I think - sound effects. See audio.h for more info on these formats.
There are many doctests and unit tests in this crate. You can run them with cargo test after splitting a ROM with python3 pm64/tests/bin/extract.py.
Architecture invariant: pm64 doesn't know about the filesystem, and doesn't know about the web; it's just a library for working with Paper Mario data. (The idea is to eventualy publish this crate to crates.io - if you are interested in using pm64 in a different project, let me know and I can publish it!)
This is some fairly trivial glue code that enables interesting parts of the pm64 crate to be used by mamar-web. It uses wasm-pack for building, which is not part of the standard Rust toolchain.
mamar-wasm-bridge exported functions take and return the any type, but we can do more. This module provides TypeScript types for the main structs of the pm64 crate, so that functions like bgm_encode can have their return values typed as Bgm which brings a better developer experience!
This is an emscripten port of n64sums, a tool for calculating the CRC checksum of N64 ROMs. It's unused currently but I'm keeping it around in case I need it in the future, for example for a 'save SBN to ROM' feature.
This builds papermario-dx's audio engine, the C code that plays music in the game, to WebAssembly with clang, along with mupen64plus's emulation of the RSP microcode the engine drives. mamar-web runs it in an AudioWorklet, so songs play on the audio thread using the instruments in the user's ROM.
The engine is built straight from a papermario-dx checkout, with MAMAR_WASM defined. mamar-audio provides stand-ins for the game's headers and the N64 hardware the engine uses, and swaps the ROM's big-endian data to WebAssembly's little-endian order as the engine reads it.
Enter the Nix devshell (nix develop), or make sure you have:
- Node.js
- Yarn (rather than npm)
- Rust
- wasm-pack
- clang and lld, version 18 or later
- a papermario-dx clone
Then run:
yarn installDX_DIR=../papermario-dx yarn start, whereDX_DIRis the papermario-dx clone relative to this repository
To release a new version:
- Bump the version in
mamar-web'spackage.json - Update the changelog
- Push with tag
If you interested in contributing to Mamar, great! Check out the open issues for something to do. If you have any questions, feel free to ask in the #mamar channel. I'm also happy to help out people who are new to React or Rust but still want to contribute.
Mamar is licensed under the BSD Zero Clause License. This is a very permissive license, and you can do whatever you want with the code. If you do use Mamar to make a mod or other project, I'd appreciate a mention somewhere, but it's not required.
mamar-audio/rsp-hle is from mupen64plus and is licensed under the GPL, version 2, so builds of mamar-audio are too.
