Jump to content

N64 Function Decompiler


Twili
 Share

Recommended Posts

UPDATE IN THE LATEST POST.

 

Download decompile.zip by clicking on the blue Download button here:


 

Features:

+Currently supports 18 MIPS opcodes.

+Supports F3DEX GBI 2 RDP command detection.

 

This tool will interpret MIPS opcodes and print C-like syntax to the console window. Results may be inaccurate until there's full opcode support.

It will also print comments. "^_^" means that something is unnecessary to print because it sets the high 16 bits of a pointer that's shown whole further down.

 

Usage:

1. Click and drag a file into decompile.exe. The engine file for OoT debug (code) is included for testing purposes. ***IT MUST BE AN EXTRACTED FILE THAT YOU KNOW THE PROGRAM COUNTER FOR. NOT A WHOLE ROM.**

2. It will ask for a program counter. For the included file, type 1ce60 and hit Enter.

3. It will ask for a function offset. Type one and hit Enter.

 

There will be future updates to this tool. Source code is included.

Link to comment
Share on other sites

This is pretty awesome, but I'm not really sure you can call it 'decompilation'...Disassembly with more human-readable mnemonics, maybe. Now, if you're really going for decompilation, some suggestions I'd offer:

 

  • Scan each of the code branches, adding any argument registers (a0, a1, a2, a3) to a whitelist if you encounter an instruction that reads or writes to them. This will let you detect when you've encountered a function that takes less than 4 arguments.
  • Check when an instruction tries to read or write a value on the stack that exists in a location before the stack pointer has been incremented. Iirc, the MIPS calling convention has the caller do cleanup, so any space that the callee allocates on the stack should only be used for local variables. Combined with the previous option, you may be able to use this determine the exact number of arguments for each function.
  • Check for cases when lui/addiu and lui/ori instructions are used together. This usually means a 32-bit value is being loaded into a register.
  • Check if conditional branches or jumps are jumping to a location within the same function. This implies that there is a loop.

In any case, true decompilation is really difficult to perform effectively. Some of the more advanced ones start using heuristics and whatnot.

Link to comment
Share on other sites

Version 2 can be downloaded here: https://www.the-gcn.com/files/file/76-n64-function-decompiler/

 

Improvements:

+More MIPS opcodes supported.

+NOW DETECTS LOOPS AND PRINTS THEM AS WHILE-LOOPS.

+Now detects every RDP GBI command. *The hardwired ones and programmable ones exclusive to F3DEX_GBI_2. DMA GBI commands unsupported because their low range is too ambiguous with other values.*

 

I've completely mapped out the source code file strings left in OoT debug's "code" file now: (as function offsets for the decompiler, as before)

 

09a4  z_en_a_keep.c
20d0  z_en_item00.c
4100  z_eff_blure.c
775c  z_eff_shield_particle.c
81a0  z_eff_spark.c
93d0  z_eff_ss_dead.c
a450  z_effect_soft_sprite.c
b120  z_effect_soft_sprite_old_init.c
dc50  flg_set.c
e248  z_DLF.c
e3a0  z_actor.c
18400 z_cheap_proc.c
1b920 z_bgcheck.c
3ad38 z_camera.c
3e44c z_collision_check.c
46860 z_debug.c
471d0 z_debug_display.c
4bddc z_demo.c
4c684 z_draw.c
4ed60 z_elf_message.c
4f918 z_fcurve_data_skelanime.c
5028c z_horse.c
515b8 z_jpeg.c
52000 z_kanfont.c
52e28 z_kankyo.c
5c0bc z_lifemeter.c
5d09c z_lights.c
5e430 z_map_mark.c
5eb44 z_moji.c
5f9f0 z_onepointdemo.c
63c54 z_map_exp.c
67c0c z_parameter.c
72610 z_player_lib.c
754d0 z_prenmi.c
76510 z_rcp.c
78c54 z_room.c
7a7a4 z_sample.c
7ada0 z_scene.c
7c6f0 z_scene_table.c
83a40 z_skelanime.c
88b2c z_skin.c
897dc z_skin_awb.c
8ce74 z_sram.c
8d398 z_view.c
8f1d0 z_vimode.c
901f4 z_vismono.c
923b8 z_vr_box.c
941d0 z_vr_box_draw.c
94a50 z_fbdemo.c
9b978 db_camera.c
9ee30 z_kaleido_manager.c
9f1f0 z_kaleido_scope_call.c
9f630 z_play.c
a40c8 PreRender.c
a788c game.c
a8850 gamealloc.c
a8dcc graph.c
a9fc0 main.c
aa5ac padmgr.c
ab440 sched.c
acae0 speed_meter.c
ad540 sys_cfb.c
b39b0 sys_matrix.c
b63c0 irqmgr.c
df1b0 loadfragment2.c
df260 mtxuty-cvt.c
ecb08 z_message_PAL.c
ef53c z_message.c
f3b50 z_construct.c
 
Here's how z_lights.c looks now:
 
Program counter? 0x1ce60
Function offset? 0x5d09c
0005d09c: $sp=0xFFFFFF98;
0005d0a0: stack[(-104)+24]=$s1;
0005d0a4: $s1=0x00000000;                      /* $a0|$r0 */
0005d0a8: stack[(-104)+28]=$ra;
0005d0ac: stack[(-104)+20]=$s0;
0005d0b0:                                      /* ^_^ */
0005d0b4: $s0=0x00000000;                      /* $a1|$r0 */
0005d0b8: $a2=0x8013C8A0;                      /* 0011FA40 in your file */
0005d0bc: $a0=0xFFFFFFE4;
0005d0c0:
0005d0c4: $a3=0x00000153;
0005d0c0: function_0c6ac4();                   /* 000A9C64 in your file */
0005d0c8: $v1=mem[$s0+0x02c0];
0005d0cc: $a0=0xDB020000;                      /* G_MOVEWORD        */
0005d0d0: $a1=0x00000018;
0005d0d4: $t6=0x00000008;
0005d0d8: mem[$s0+0x02c0]=$t6;
0005d0dc: mem[$v1+0x0000]=$a0;
0005d0e0: (unsigned char)$t7=mem[$s1+0x0000];
0005d0e4: $a3=0x00000000;                      /* $r0|$r0 */
0005d0e8: $t0=0xDC080000;                      /* G_MOVEMEM         */
0005d0ec: $lo = $t7 * $a1;
0005d0f0: $t8=$lo;
0005d0f4: mem[$v1+0x0004]=$t8;
0005d0f8: $v1=mem[$s0+0x02d0];
0005d0fc: $t9=0x00000008;
0005d100: mem[$s0+0x02d0]=$t9;
0005d104: mem[$v1+0x0000]=$a0;
0005d108: (unsigned char)$t1=mem[$s1+0x0000];
0005d10c: $a0=0x00000010;
0005d110: $lo = $t1 * $a1;
0005d114: $a1=0x00000018;
0005d118: $t2=$lo;
0005d11c: mem[$v1+0x0004]=$t2;
0005d120: (unsigned char)$t3=mem[$s1+0x0000];
0005d124: if($t3<=0)
          {
0005d128:     $a1=0x00000000;                      /* $a3<<2 */
0005d124:     goto 0005d198;
          }
0005d12c: $v1=mem[$s0+0x02c0];
0005d130: while($at!=$r0)
          {
          $a1=0x00000018;
0005d134: $a3=0x00000001;
0005d138: $t4=0x00000008;
0005d13c: mem[$s0+0x02c0]=$t4;
0005d140: $v0=0x00000000;                      /* $v1|$r0 */
0005d144:
0005d148: $t5=0xE0000003;                      /* $a1>>3 (fill empty bits) */
0005d144: if($a1>=0)
          {
              goto 0005d154;
          }
0005d14c: $at=0x0000001F;
0005d150: $t5=0xE0000003;                      /* $at>>3 (fill empty bits) */
0005d154: $t6=0x00000003;
0005d158: $t7=0x00000300;
0005d15c:                                      /* -_- */
0005d160: $a2=0xDC08030A;                      /* G_MOVEMEM         */
0005d164: mem[$v0+0x0000]=$a2;
0005d168: mem[$v0+0x0004]=$a0;
0005d16c: $v1=mem[$s0+0x02d0];
0005d170: $t8=0x00000008;
0005d174: mem[$s0+0x02d0]=$t8;
0005d178: mem[$v1+0x0004]=$a0;
0005d17c: mem[$v1+0x0000]=$a2;
0005d180: (unsigned char)$t9=mem[$s1+0x0000];
0005d184: $a0=0x00000020;
0005d188: if($a3<$t9)
          {
              $at=1;
          }
          else
          {
              $at=0;
          }
0005d18c:
0005d190: $v1=mem[$s0+0x02c0];
0005d18c:
0005d194: }
          $a1=0x00000004;
0005d198: $a1=0x00000003;                      /* $a1-$a3 */
0005d19c: $a1=0x00000018;
0005d1a0: $v1=mem[$s0+0x02c0];
0005d1a4: $a1=0x00000048;
0005d1a8: $t0=0xDC080000;                      /* G_MOVEMEM         */
0005d1ac: $t1=0x00000008;
0005d1b0: mem[$s0+0x02c0]=$t1;
0005d1b4: $v0=0x00000000;                      /* $v1|$r0 */
0005d1b8:
0005d1bc: $t2=0xE0000009;                      /* $a1>>3 (fill empty bits) */
0005d1b8: if($a1>=0)
          {
              goto 0005d1c8;
          }
0005d1c0: $at=0x0000004F;
0005d1c4: $t2=0xE0000009;                      /* $at>>3 (fill empty bits) */
0005d1c8: $t3=0x00000009;
0005d1cc: $t4=0x00000900;
0005d1d0:                                      /* -_- */
0005d1d4: $a2=0xDC08090A;                      /* G_MOVEMEM         */
0005d1d8: $a0=0x00000008;
0005d1dc: mem[$v0+0x0004]=$a0;
0005d1e0: mem[$v0+0x0000]=$a2;
0005d1e4: $v1=mem[$s0+0x02d0];
0005d1e8: $a1=0x00000000;                      /* $s0|$r0 */
0005d1ec: $a3=0x00000160;
0005d1f0: $t5=0x00000008;
0005d1f4: mem[$s0+0x02d0]=$t5;
0005d1f8: mem[$v1+0x0000]=$a2;
0005d1fc:                                      /* ^_^ */
0005d200: mem[$v1+0x0004]=$a0;
0005d204: $a0=0xFFFFFFE4;
0005d208:
0005d20c: $a2=0x8013C8B0;                      /* 0011FA50 in your file */
0005d208: function_0c6b54();                   /* 000A9CF4 in your file */
0005d210: $ra=stack[(-104)+28];
0005d214: $s0=stack[(-104)+20];
0005d218: $s1=stack[(-104)+24];
0005d21c:
Press any key to continue . . .
 
See the while loop?
  • Like 3
Link to comment
Share on other sites

How does one calculate or find the PC? I could really get some use out of this with Quest 64. 

1. Find a display list in the ROM with some vertex data.

2. Identify which command is loading that vertex data and get the last 3 bytes of the pointer from it.

3. Subtract the ROM offset of the data from that.

4. ???

5. PC.

 

Also once you get the PC, subtract it from that pointer.

Then subtract the result from the ROM offset of the data to get the start of the file and you can use it with the decompiler.

 

This only works if the file has machine code in it and the pointer starts with 0x80.

 

That doesn't work, actually. I'd have to show how to do it if you paste data.

Link to comment
Share on other sites

 Share

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.