{"id":19478942,"url":"https://github.com/braidenpsiuk/spm-level-dumper","last_synced_at":"2026-06-11T02:31:36.082Z","repository":{"id":209781422,"uuid":"724923762","full_name":"BraidenPsiuk/spm-level-dumper","owner":"BraidenPsiuk","description":"Dump Super Paper Mario level data to .obj files!","archived":false,"fork":false,"pushed_at":"2023-11-29T04:42:37.000Z","size":6046,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-30T12:23:12.919Z","etag":null,"topics":["dumper","extraction","extractor","level","level-extraction","level-extractor","map","map-extraction","mario","models","nintendo","nodejs","obj","paper","python","script","super","super-paper-mario","tool","wii"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BraidenPsiuk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-11-29T04:11:21.000Z","updated_at":"2024-04-11T13:10:48.000Z","dependencies_parsed_at":"2023-11-29T05:40:35.320Z","dependency_job_id":null,"html_url":"https://github.com/BraidenPsiuk/spm-level-dumper","commit_stats":null,"previous_names":["braidenpsiuk/spm-level-dumper"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/BraidenPsiuk/spm-level-dumper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BraidenPsiuk%2Fspm-level-dumper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BraidenPsiuk%2Fspm-level-dumper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BraidenPsiuk%2Fspm-level-dumper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BraidenPsiuk%2Fspm-level-dumper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BraidenPsiuk","download_url":"https://codeload.github.com/BraidenPsiuk/spm-level-dumper/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BraidenPsiuk%2Fspm-level-dumper/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34180147,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dumper","extraction","extractor","level","level-extraction","level-extractor","map","map-extraction","mario","models","nintendo","nodejs","obj","paper","python","script","super","super-paper-mario","tool","wii"],"created_at":"2024-11-10T19:52:08.905Z","updated_at":"2026-06-11T02:31:36.064Z","avatar_url":"https://github.com/BraidenPsiuk.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Super Paper Mario - Level Dumper\nThis is a Node.js script that **reads a .wbfs archive** of Super Paper Mario **and dumps the level geometry for each level into a .obj file**, so that the levels/maps can be opened with other tools and programs, such as Blender.\n\n**To use this script, just follow the directions below. You will provide your .wbfs file, and the script will generate a folder called \"output\" filled with 300+ levels in .obj format. Enjoy!**\n\nRight now, only geometry and collision data are converted. No vertex colors/textures are applied to the models, and camera data is ignored.\n\nIn the future, I hope to create (or help to create) a new version of this tool which dumps colors, textures, camera info, animations, and more- and switch to exporting in .glTF instead of .obj format. But until then, I hope some of you find this script useful!\n\n\u003cimg src=\"./screenshots/blender/full.png\" style=\"width: 100vw\"\u003e\n\u003c!-- \u003cimg src=\"./screenshots/sample-level/orthographic.png\" style=\"width: 40vw\"\u003e\n\u003cimg src=\"./screenshots/sample-level/perspective.png\" style=\"width: 40vw\"\u003e\n\u003cimg src=\"./screenshots/blender/cropped.png\" style=\"width: 40vw\"\u003e --\u003e\n\n## System requirements:\n- Free disk space: **1.25GB** - Only needed while the script is running (to unpack the .wbfs archive). The resulting collection of .obj files will only take up 127MB.\n- OS: **Tested with x86_64 Debian-based Linux** - This script only works on Linux systems with BASH shell (ZSH probably works too). Feel free to use WSL or a VM like VirtualBox (Windows) or UTM (MacOS) if you want to run it on another system.\n- Installed tools: **Node.js and Python3** - Tested and working with Node 18.13.0 and Python 3.11.2, but any realtively recent version of Node and Python should work fine. You don't need to use any package managers or install any dependencies. Just make sure Python is available in your path. (As long as you can open a terminal and type \"python\" or \"python3\" to launch the interpreter, you're all good here!)\n\n## Usage\n1. Make sure you are using Linux and that a recent copy of Node.js and Python 3 are installed\n2. Obtain a .wbfs copy of Super Paper Mario (in a legitimate manor)\n3. Rename the .wbfs file to \"**spm.wbfs**\"\n4. Download this repository (and extract it if you need to), or clone it:\n```shell\ngit clone https://github.com/BraidenPsiuk/spm-level-dumper.git\n```\n5. Move your **spm.wbfs** file into the root of the repository\n6. Open the repo using a terminal (**cd** into it)\n7. Set the following permissions on bin-utils:\n```shell\nsudo chmod +x ./utils/bin-utils/*\n```\n8. Make sure you are still inside the repo, then run the script (no need to install any dependencies first, there are none):\n```shell\nnode ./spm-level-dumper.mjs\n```\nThat's it! Please note that the script may take a few minutes to complete. When it's finished, you'll find all of the levels in the \"output\" folder in the root of the repository. You might also find [this document](https://docs.google.com/document/d/10w4CS5oNBOHHYtM9OrNUYM7GIqNxIaR-b_Sr8FSG7Pk/edit#heading=h.r3koedvnma3t) handy, it provides IDs and descriptions for each level.\n\nThanks to all the hard work by Andrew Ekstedt (magical), PistonMiner, Wiimm, and everyone else on the SPM/TTYD scene! Please see the \"Credits\" section below for more information about the scripts used in this repo.\n\n## Troubleshooting\n- If you get a **\"python: not found\"** error, make sure Python 3+ is installed and available in your path. You might need to open the script in an editor and change the constant \"PYTHON_BIN_NAME\" from \"python3\" to \"python\".\n- For a **\"Permission denied\"** error, make sure you've first set the proper permissions on the binaries inside \"./utils/bin-utils\". See step 7 under \"Usage\" above.\n- If wit produces **\"ERROR #76 [CAN'T OPEN FILE]\"**, you probably just need to ensure you've provided the .wbfs file as described in steps 3 and 5 under \"Usage\" above.\n\n## Can I just download the .obj files from somewhere?\nFor legal reasons, I don't feel comfortable distributing them. Please obtain your own .wbfs copy of the game and run this script to dump the levels yourself.\n\n## Credits\nThis script, simply put, is just a bunch of various community scripts hot-glued together to form one cohesive way to dump levels. I did not write any of the lower-level scripts that actually perform the raw decompression, unarchiving, and conversion. Here are the following scripts used, and their authors:\n- Andrew Ekstedt's (magical's) [nlzss](https://github.com/magical/nlzss): Specifically, their [lzss3.py](https://github.com/magical/nlzss/blob/master/lzss3.py)\n- PistonMiner's [ttyd-tools](https://github.com/PistonMiner/ttyd-tools): Specifically, their [ttydview.py](https://github.com/PistonMiner/ttyd-tools/blob/master/ttyd-tools/ttydview/ttydview.py)\n- Wiimm's ISO and SZS tools: Specifically, their [wit](https://wit.wiimm.de/wit/) ISO manipulation tool and [wszst](https://szs.wiimm.de/wszst/) SZS tool.\n\nI have copied and included the above scripts and binaries in this repo to make it convenient for users to download everything all at once. However, I want to make it clear that I had no part in writing any script other than *spm-level-dumper.mjs*. They are only included here in this repo for convenience and archival purposes. (If you have any problem with this, please open an issue.)\n\n## Safety / Security\nThis script has two instances where it uses \"rm -rf\" (a destructive operation). All file paths referenced in the script are relative to the repository root, so it should be safe as long as you run the script from inside the repository folder, and don't store anything important in the repository folder. But still, use caution. To avoid potential data loss, only run scripts you've inspected yourself first.\n\n\n\n\u003c!--\nI commented out this information as it might not all be acurate, I just typed this stuff up as I was figuring out this process. I left it here because it has a bit more useful info.\n\nThe bloated output, containing all ttydview.py files, objs, and base wbfs data takes up 1085184 bytes (1.1GB).\nWith all ttydview.py files removed, it takes up 1080600 bytes (still 1.1GB), so not much of a difference removing those.\nWith all the tmp stuff deleted, and just the obj files left, the result is only 129276 bytes (127MB!)\n\n---\nIf you're just here for the .obj level files, the above information is all you need. However, if you're interested in how this all works, continue reading below!\n\n\n\n## How to do this all manually\nThere are a few parts to this!\n1. Grab a WBFS archive copy of Super Paper Mario\n2. Extract this archive\n3. Decompress the maps\n4. extract the maps\n\n## More detailed instructions:\n1. Extract the wbfs rom file:\n```shell\nwit EXTRACT \"Super Paper Mario.wbfs\" ./output\n```\n2. Enter the files/map directory, extract each of these .bin files (we're gonna tackle just one here to test with, ) (they are LZ10 compressed, https://github.com/magical/nlzss)\n```shell\npython3 lzss3.py aa1_01.bin \u003e aa1_01.lz10_extracted\n```\n3. (OPTIONAL) Use this command to list contents of extracted map file:\n```shell\nwszst list aa1_01.lz10_extracted\n```\nThis just lets you look at the contents to make sure you can view them.\n4. Now we're even closer to the finish line! Use this:\nwszst extract aa1_01.lz10_extracted --dest wszst_extracted_content\n\nCool! Now we have some more files. But, everything is still in a weird format. Ignore the newly created \"wszst-setup.txt\", it's useful if we want to reconstruct the archive later, but we aren't interested in that now. (You can even delete it if you want.) Just navigate into \"dvd\". Lets tackle how to view textures first, then we'll figure out how to import the level geometry/mesh data into another tool... Like Blender!\n\n5. To turn the weird .tpl texture files into usable PNGs, replace the above \"extract\" with \"xall\" (this is supposed to mean \"extract all\")\nwszst xall aa1_01.lz10_extracted --dest wszst_extracted_content\nYou'll probably get ERROR #64 [FILE ALREADY EXISTS], just delete the folder previously generated and run the new \"xall\" command and it'll work!\n\nThanks to noclip.website, we can determine the map name for the very iconic \"Lineland Road\". This just happens to be called \"he1_01.bin\". If you'd like to follow along from this point but don't have access to the wbfs archive file, you can try with just this single map file by grabbing it from noclip.website's server. Download from here: https://noclip.beyond3d.com/spm/he1_01.bin Note that this file only contains a single map's texture and geometry/mesh data, and is not enough to reconstruct the actual game. Although it contains assets created by Nintendo, it shouldn't be viewed as an act of piracy, rather a means to educate by using something recognizable and graspable. If you consider this condoning piracy, consider the fact that Nintendo has not pushed for a takedown of noclip.website. It is a digital museum run and maintained by a supportive community and does not distribute the core game itself.\n\nAnyway... now using \"he1_01.bin\" (Lineland Road). Let's move it to it's own isolated directory and combine what we learned above to extract it.\n```shell\npython3 lzss3.py he1_01.bin \u003e he1_01.bin_DECOMPRESSED \u0026\u0026 wszst xall he1_01.bin_DECOMPRESSED --dest he1_01.bin_EXTRACTED\n```\n\nCool. So we've got it decompressed, extracted, and now we can view textures as PNGs. But... what next? Trying to use the lzss decompression python script on \"dvd/map/he1_01/map.dat\" or \"dvd/setup/he1_01.dat\" gets us nowhere, as apparently these files aren't compressed. Maybe they're raw obj/ply files, so lets try tacking on an extension and opening them in Blender! That lets us test four possible combinations:\nmap.dat \u003e map.dat.obj\nmap.dat \u003e map.dat.ply\nhe1_01.dat \u003e he1_01.dat.obj\nhe1_01.dat \u003e he1_01.dat.ply\n\nDo any of them result in success? Well, map.dat.obj kinda gives us some \"mesh\" data? We are provided with a series of vertices, but they're all located at the same position... Not very helpful. But hey, now we can count the number of vertices in the file maybe? Kind of helpful? Well, there seem to be only 7 vertices in this file so it almost certainly isn't raw level geometry. The rest of the file and importer combinations get us nowhere, they don't even produce objects in Blender. We can't drag any of these files thus far into noclip.website either. Okay! So... Let's try \"ttydview.py\". This script is a little quirky. It expects two folders to be created, side by side. These are called \"map_data\" and \"obj_files\". The first argument is the raw map file and the second is the output file. \"map/he1_01/map.dat\" is seemingly the file we are after... WOO! Lineland road appears in Blender! Granted, it isn't pretty. We don't have textures applied, but everything is there. All the data is split up into vertex groups so maybe it's possible to turn parts of it on and off. (This isn't true? I could have sworn when I first tried this, I saw a bunch of vertex groups, but now I can't seem to find that data. I don't even know if OBJ's can contain that sort of information.)\n\nThere's also still another juicy-looking file in files/setup with the same map name but with a \".dat\" extension instead of \".bin\". Hopefully we'll figure out what that is soon! --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbraidenpsiuk%2Fspm-level-dumper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbraidenpsiuk%2Fspm-level-dumper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbraidenpsiuk%2Fspm-level-dumper/lists"}