Super Mario 64 has been decompiled

Discussion in 'Other Consoles & Oldies' started by Ecchi95, Jul 7, 2019.

  1. Ecchi95
    OP

    Ecchi95 Advanced Member

    Newcomer
    2
    Jul 7, 2019
    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, Jul 17, 2019 at 3:57 AM
  2. 8BitWonder

    8BitWonder Small Homebrew Dev

    Member
    10
    Jan 23, 2016
    United States
    47 4F 54 20 45 45 4D
    Grabbed from the same thread:

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

    pasc GBATemps GBA Freak

    Member
    5
    Sep 9, 2006
    Germany
    Germany
    How the heck did they do that ?
     
    kikongokiller likes this.
  4. Ecchi95
    OP

    Ecchi95 Advanced Member

    Newcomer
    2
    Jul 7, 2019
    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.
     
    IncredulousP likes this.
  5. 8BitWonder

    8BitWonder Small Homebrew Dev

    Member
    10
    Jan 23, 2016
    United States
    47 4F 54 20 45 45 4D
    Whoops, must've missed that one.
    My bad.
     
  6. Ecchi95
    OP

    Ecchi95 Advanced Member

    Newcomer
    2
    Jul 7, 2019
    United States
    As I said:

     
  7. lincruste

    lincruste GBAtemp Fan

    Member
    5
    Jan 13, 2008
    Antarctica
    france
    Couldn't find a single download link for this.
     
  8. uyjulian

    uyjulian Homebrewer

    Member
    9
    Nov 26, 2012
    United States
    United States
     
  9. Ecchi95
    OP

    Ecchi95 Advanced Member

    Newcomer
    2
    Jul 7, 2019
    United States
    It's at the top of the thread on warosu.org. The anonfile link.

    I've been looking at it. That's how I made the file list from one of the src directories.
     
    lincruste likes this.
  10. FAST6191

    FAST6191 Techromancer

    pip Reporter
    23
    Nov 21, 2005
    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.
     
    x65943 likes this.
  11. Ecchi95
    OP

    Ecchi95 Advanced Member

    Newcomer
    2
    Jul 7, 2019
    United States
    And yes, it uses the official SDK.

    Apparently Kaze Emanuar and Pannenkoek2012 are just two of the people who made this a reality.
     
  12. lincruste

    lincruste GBAtemp Fan

    Member
    5
    Jan 13, 2008
    Antarctica
    france
    Found. Thank you.
     
  13. Orangy57

    Orangy57 bruh

    Member
    6
    Aug 17, 2015
    United States
    New Jersey
    So is this basically like how that one guy rewrote Diablo from scratch using only instruction names?
     
    Vorde and BigBoiHackerPerson like this.
  14. Concas_

    Concas_ Newbie

    Newcomer
    1
    Jul 7, 2019
    Italy
    May this lead to a homebrew source port of the game?
     
  15. FAST6191

    FAST6191 Techromancer

    pip Reporter
    23
    Nov 21, 2005
    United Kingdom
    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).
     
    LonelyPhantom, Dartz150 and Orangy57 like this.
  16. Ecchi95
    OP

    Ecchi95 Advanced Member

    Newcomer
    2
    Jul 7, 2019
    United States
    Nintendo didn't use any compiler optimizations, so it was "easy."

    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, Jul 7, 2019
    Dartz150 and paulloeduardo like this.
  17. Ecchi95
    OP

    Ecchi95 Advanced Member

    Newcomer
    2
    Jul 7, 2019
    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.
     
  18. uyjulian

    uyjulian Homebrewer

    Member
    9
    Nov 26, 2012
    United States
    United States
    I don't think that much people are interested in incomplete work.
     
  19. Ecchi95
    OP

    Ecchi95 Advanced Member

    Newcomer
    2
    Jul 7, 2019
    United States
    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;
    }
    
     
    cearp likes this.
  20. uyjulian

    uyjulian Homebrewer

    Member
    9
    Nov 26, 2012
    United States
    United States
    It's still incomplete work.
     
Loading...