Super Mario 64 has been decompiled

Ecchi95

Well-Known Member
OP
Member
Joined
Jul 7, 2019
Messages
121
Trophies
0
Age
29
XP
891
Country
United States
For help navigating the files: https://gbatemp.net/threads/super-mario-64-has-been-decompiled.542918/page-7#post-8712520

Follow the social media campaign:

BBoard

[Discussion]

Beyond3D
[Discussion]

Exclusively Games
[Discussion]

Gaming Reinvented
[Article] [Discussion]

Hacker News
[Discussion]

OCAU
[Discussion]

Official Pyra and Pandora Site
[Discussion]

Reddit
/r/gamedev

[1]

/r/n64
[1]

/r/emulation
[1]

/r/linux_gaming
[1]

/r/programming
[1]

/r/DataHoarder
[1]

/r/Games
[1]

/r/nintendo
[1][2]

/r/speedrun
[1]

/r/REGames
[1]

/r/coding
[1]

/r/retrogaming
[1]

ResetEra
[Discussion]

Twitter
[1][2]

Hi. I registered because nobody else was talking about this.

If you do a google search for "n64 decomp" in quotes, you'll get a warosu.org result (archived 4chan threads.) This thread contains a link to a full decompilation of Super Mario 64 that an IDENTICAL ROM can be built from if you have the proper environment setup. It seems it was leaked before the people involved finished renaming all the functions to be nice and clear.

It's not an ordinary decompilation generated by IDA. They actually rewrote all the functions from reading MIPS assembly and compiled it with the original compiler, adjusting the code until it produced identical output to a vanilla ROM.

These are the files contained in the src/game directory:
(src is split into audio, engine, game, and goddard)

Code:
area.c
area.h
camera.c
camera.h
debug.c
debug.h
debug_course.c
debug_course.h
decompress.h
display.c
display.h
envfx_bubbles.c
envfx_bubbles.h
envfx_snow.c
envfx_snow.h
file_select.c
file_select.h
game.c
game.h
geo_misc.c
geo_misc.h
hud.c
hud.h
ingame_menu.c
ingame_menu.h
interaction.c
interaction.h
intro_geo.c
intro_geo.h
level_geo.c
level_geo.h
level_select_menu.c
level_select_menu.h
level_update.c
level_update.h
macro_special_objects.c
macro_special_objects.h
main.c
main.h
main_entry.h
mario.c
mario.h
mario_actions_airborne.c
mario_actions_airborne.h
mario_actions_automatic.c
mario_actions_automatic.h
mario_actions_cutscene.c
mario_actions_cutscene.h
mario_actions_moving.c
mario_actions_moving.h
mario_actions_object.c
mario_actions_object.h
mario_actions_stationary.c
mario_actions_stationary.h
mario_actions_submerged.c
mario_actions_submerged.h
mario_misc.c
mario_misc.h
mario_step.c
mario_step.h
memory.c
memory.h
moving_texture.c
moving_texture.h
object_collision.c
object_collision.h
object_helpers.c
object_helpers.h
object_helpers2.h
object_list_processor.c
object_list_processor.h
obj_behaviors.c
obj_behaviors.h
obj_behaviors_2.c
obj_behaviors_2.h
paintings.c
platform_displacement.c
platform_displacement.h
print.c
print.h
profiler.c
profiler.h
rendering_graph_node.c
rendering_graph_node.h
room.c
room.h
save_file.c
save_file.h
screen_transition.c
screen_transition.h
segment2.h
segment7.h
shadow.c
shadow.h
skybox.c
skybox.h
sound_init.c
sound_init.h
spawn_object.c
spawn_object.h
spawn_sound.c
spawn_sound.h
star_select.c
star_select.h
 
Last edited by Ecchi95,

8BitWonder

Small Homebrew Dev
Member
Joined
Jan 23, 2016
Messages
2,489
Trophies
1
Location
47 4F 54 20 45 45 4D
XP
5,343
Country
United States
Grabbed from the same thread:

Revo Sat Jun 8 00:01:57 2019 No.>>5644126 >>5644132 >>5644139 >>5644190 >>5644736 >>5646686 >>5649483 >>5652952 >>5654821

Ah.. fuck. Which doofus leaked it I will smegging fucking KILL THEM.

..okay so it was definitely someone in the n64 decomp discord.

To answer your questions, yes: This is a full source code which can be recompiled with modern toolchains (gcc: ive done this already) and even target other platforms (PC) with quite a bit of work. There already exists some proof of concept wireframe stuff.

Just keep in mind that we weren't done yet. It's really only like maybe 65% finished, code and documentation wise. This codebase is an absolute treasure for preservation sake. Turns out if you compile your ROM unoptimized its really easy to get the uncompiled code from the assembly. Guess Nintendo should have double checked their CFLAGS before shipping US and JP.

So while yes it seems to be the real deal and compile-able, It's not actually finished.
 
Last edited by 8BitWonder,

Ecchi95

Well-Known Member
OP
Member
Joined
Jul 7, 2019
Messages
121
Trophies
0
Age
29
XP
891
Country
United States
It's not actually finished.
Revo also said this:
"Don't misread me. 65% just means the renamed stuff from raw variable names like func_80F00F00. and D_80F00F00. You can compile it in its current state and it will produce a working Super Mario 64 ROM."

Like I said, the only thing not finished is renaming every function.
 
  • Like
Reactions: IncredulousP

8BitWonder

Small Homebrew Dev
Member
Joined
Jan 23, 2016
Messages
2,489
Trophies
1
Location
47 4F 54 20 45 45 4D
XP
5,343
Country
United States
Revo also said this:
"Don't misread me. 65% just means the renamed stuff from raw variable names like func_80F00F00. and D_80F00F00. You can compile it in its current state and it will produce a working Super Mario 64 ROM."

Like I said, the only thing not finished is renaming every function.
Whoops, must've missed that one.
My bad.
 

FAST6191

Techromancer
Editorial Team
Joined
Nov 21, 2005
Messages
36,798
Trophies
3
XP
28,321
Country
United Kingdom
Interesting development. Wonder if the speedrun folks will have any discoveries as a result of this.

Personally I would have been content with something that could compile (and was not just kicking lots as a straight binary array to be left as is) similar to some of the disassembles we have seen on older consoles but if people want to gild the lily then "you do you" and all that.
 

FAST6191

Techromancer
Editorial Team
Joined
Nov 21, 2005
Messages
36,798
Trophies
3
XP
28,321
Country
United Kingdom
So is this basically like how that one guy rewrote Diablo from scratch using only instruction names?
For the others playing along at home that missed that one


Anyway sort of yes, but also not yes (function names are nice to have but it is not a keys to the kingdom affair) and there were some different goals (don't know entirely why they went in for 1:1 binary recreation as opposed to functional/bug for bug equivalent but to each their own). Decompilation itself is also advancing somewhat quickly as far as C family, especially C is concerned.
The end result though is/will be something you can port to different things much as you would any other C program. Depending upon what was done to play to the N64 hardware there might be an optimised build that spares us the problems dodged by clever coding or that takes advantage of more modern hardware, and beyond that someone might also come along and do a DS functions mod (and probably another to allow certain speedruns to work in lesser emulators that might play differently in some manner).
 

Ecchi95

Well-Known Member
OP
Member
Joined
Jul 7, 2019
Messages
121
Trophies
0
Age
29
XP
891
Country
United States
(don't know entirely why they went in for 1:1 binary recreation as opposed to functional/bug for bug equivalent but to each their own).
Nintendo didn't use any compiler optimizations, so it was "easy."

May this lead to a homebrew source port of the game?
Yeah, you can port it to anything. A PC port, even rival consoles! You just have to get rid of the Nintendo 64 API calls.

You can even write Ocarina of Time the way Nintendo did, starting with Super Mario 64's engine.
 
Last edited by Ecchi95,

Ecchi95

Well-Known Member
OP
Member
Joined
Jul 7, 2019
Messages
121
Trophies
0
Age
29
XP
891
Country
United States
Is anyone going to talk about the contents besides me? This is the entire code of Super Mario 64 we have here.

I want to see people analyzing it.
 
  • Like
Reactions: alivebacon

Ecchi95

Well-Known Member
OP
Member
Joined
Jul 7, 2019
Messages
121
Trophies
0
Age
29
XP
891
Country
United States
I don't think that much people are interested in incomplete work.
The code is complete, just not every symbol has been given a proper name. I told you, this wasn't a generated output. Every single function was rewritten by hand. It's proper code.

Here is all of geo_misc.c:

Code:
#include <ultra64.h>

#include "sm64.h"
#include "geo_misc.h"

#include "area.h"
#include "engine/math_util.h"
#include "level_update.h"
#include "mario_actions_cutscene.h"
#include "mario.h"
#include "memory.h"
#include "rendering_graph_node.h"
#include "save_file.h"
#include "segment2.h"

/**
 * @file geo_misc.c
 * This file contains miscellaneous geo_asm scripts. 
 * 
 * In particular, it builds:
 *   - the light that shows the player where to look for Tower of the Wing Cap, 
 *   - the flying carpets seen in Rainbow Ride, and 
 *   - the end screen displaying Peach's delicious cake.
 */

#define NUM_FLYING_CARPET_VERTICES 21
extern s16 flying_carpet_static_vertex_data[NUM_FLYING_CARPET_VERTICES];

extern Gfx dl_castle_lobby_wing_cap_light[];

extern Gfx dl_flying_carpet_begin[];
extern Gfx dl_flying_carpet_model_half[];
extern Gfx dl_flying_carpet_end[];

extern Gfx dl_cake_end_screen[];

static s16 sCurAreaTimer = 1;
static s16 sPrevAreaTimer = 0; 
static s16 sFlyingCarpetRippleTimer = 0;

s8 gFlyingCarpetState;

/**
 * Create a vertex with the given parameters and insert it into `vtx` at 
 * position `n`.
 */
void make_vertex(
    Vtx *vtx, s32 n, s16 x, s16 y, s16 z, s16 tx, s16 ty, u8 r, u8 g, u8 b, u8 a
) {
    vtx[n].v.ob[0] = x;
    vtx[n].v.ob[1] = y;
    vtx[n].v.ob[2] = z;

    vtx[n].v.flag = 0;

    vtx[n].v.tc[0] = tx;
    vtx[n].v.tc[1] = ty;

    vtx[n].v.cn[0] = r;
    vtx[n].v.cn[1] = g;
    vtx[n].v.cn[2] = b;
    vtx[n].v.cn[3] = a;
}

/**
 * Round `num` to the nearest `s16`.
 */
s16 round_float(f32 num) {
    // Note that double literals are used here, rather than float literals.
    if (num >= 0.0) {
        return num + 0.5;
    } else {
        return num - 0.5;
    }
}

/**
 * Create a display list for the light in the castle lobby that shows the
 * player where to look to enter Tower of the Wing Cap.
 */
Gfx *geo_exec_inside_castle_light(
    s32 run, 
    struct GraphNode *node, 
    UNUSED f32 mtx[4][4]
) {
    s32 flags;
    struct GraphNode12A *generatedNode;
    Gfx *displayListHead = NULL;
    Gfx *displayList = NULL;

    if (run == TRUE)
    {
        flags = save_file_get_flags();
        if (gDisplayedStars >= 10 && (flags & SAVE_FLAG_HAVE_WING_CAP) == 0)
        {
            displayList = alloc_display_list(2 * sizeof(*displayList));
           
            if (displayList == NULL) {
                return NULL;
            } else {
                displayListHead = displayList;
            }

            generatedNode = (struct GraphNode12A *) node;
            generatedNode->fnNode.node.flags = (
                generatedNode->fnNode.node.flags & 0xFF
            ) | 0x500;
           
            gSPDisplayList(displayListHead++, dl_castle_lobby_wing_cap_light);
            gSPEndDisplayList(displayListHead);
        }
    }

    return displayList;
}

/**
 * Update static timer variables that control the flying carpets' ripple effect.
 */
Gfx *geo_exec_flying_carpet_timer_update(
    s32 run, 
    UNUSED struct GraphNode *node, 
    UNUSED f32 mtx[4][4]
) {
    if (run != TRUE) {
        sFlyingCarpetRippleTimer = 0;
        sPrevAreaTimer = gAreaUpdateCounter - 1;
        sCurAreaTimer = gAreaUpdateCounter;
        gFlyingCarpetState = FLYING_CARPET_IDLE;
    } else {
        sPrevAreaTimer = sCurAreaTimer;
        sCurAreaTimer = gAreaUpdateCounter;
        if (sPrevAreaTimer != sCurAreaTimer) {
            sFlyingCarpetRippleTimer += 0x400;
        }
    }

    return NULL;
}

/**
 * Create a display list for a flying carpet with dynamic ripples.
 */
Gfx *geo_exec_flying_carpet_create(
    s32 run, 
    struct GraphNode *node, 
    UNUSED f32 mtx[4][4]
) {
    s16 n, row, col, x, y, z, tx, ty;
    Vtx *verts;
    struct GraphNode12A *generatedNode = (struct GraphNode12A *) node;

    s16 *sp64 = segmented_to_virtual(&flying_carpet_static_vertex_data);
    Gfx *displayList = NULL;
    Gfx *displayListHead = NULL;
    struct Object *curGraphNodeObject;
   

    if (run == TRUE)
    {
        verts = alloc_display_list(NUM_FLYING_CARPET_VERTICES * sizeof(*verts));
        displayList = alloc_display_list(7 * sizeof(*displayList));
        displayListHead = displayList;

        if (verts == NULL || displayList == NULL) {
            return NULL;
        }

        generatedNode->fnNode.node.flags = (
            generatedNode->fnNode.node.flags & 0xFF
        ) | 0x100;

        for (n = 0; n <= 20; n++)
        {
            row = n / 3;
            col = n % 3;

            x = sp64[n * 4 + 0];
            y = round_float(
                sins(
                    sFlyingCarpetRippleTimer + (row << 12) + (col << 14)
                ) * 20.0
            );
            z = sp64[n * 4 + 1];
            tx = sp64[n * 4 + 2];
            ty = sp64[n * 4 + 3];

            make_vertex(verts, n, x, y, z, tx, ty, 0, 127, 0, 255);
        }

        gSPDisplayList(displayListHead++, dl_flying_carpet_begin);

        // The forward half.
        gSPVertex(displayListHead++, verts, 12, 0);
        gSPDisplayList(displayListHead++, dl_flying_carpet_model_half);

        // The back half.
        gSPVertex(displayListHead++, verts + 9, 12, 0);
        gSPDisplayList(displayListHead++, dl_flying_carpet_model_half);

        gSPDisplayList(displayListHead++, dl_flying_carpet_end);
        gSPEndDisplayList(displayListHead);

        curGraphNodeObject = (struct Object *) D_8032CFA0;
        if (gMarioObject->platform == curGraphNodeObject) {
            gFlyingCarpetState = FLYING_CARPET_MOVING_WITH_MARIO;
        } else if (curGraphNodeObject->oForwardVel != 0.0) {
            gFlyingCarpetState = FLYING_CARPET_MOVING_WITHOUT_MARIO;
        } else {
            gFlyingCarpetState = FLYING_CARPET_IDLE;
        }
       
    }

    return displayList; 
}

/**
 * Create a display list for the end screen with Peach's delicious cake.
 */
Gfx *geo_exec_cake_end_screen(
    s32 run,
    struct GraphNode *node,
    UNUSED f32 mtx[4][4]
) {
    struct GraphNode12A *generatedNode = (struct GraphNode12A *) node;
    Gfx *displayList = NULL;
    Gfx *displayListHead = NULL;

    if (run == TRUE)
    {
        displayList = alloc_display_list(3 * sizeof(*displayList));
        displayListHead = displayList;

        generatedNode->fnNode.node.flags = (
            generatedNode->fnNode.node.flags & 0xFF
        ) | 0x100;

        gSPDisplayList(displayListHead++, dl_proj_mtx_fullscreen);
        gSPDisplayList(displayListHead++, dl_cake_end_screen);
        gSPEndDisplayList(displayListHead);
    }

    return displayList;
}
 
  • Like
Reactions: cearp

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
  • No one is chatting at the moment.
    SylverReZ @ SylverReZ: https://www.youtube.com/watch?v=pnRVIC7kS4s