You've already forked Microtransactions64
mirror of
https://github.com/Print-and-Panic/Microtransactions64.git
synced 2026-01-21 10:17:19 -08:00
refresh 6
This commit is contained in:
@@ -1,3 +1,53 @@
|
||||
# IMPORTANT NOTICE ABOUT THIS FOLDER
|
||||
# Super Mario 64 Enhancements
|
||||
|
||||
Everything in this folder is non-canon to the source code in the rest of the repository. As such, it is not to be considered when looking for more information on Super Mario 64's functionality.
|
||||
This directory contains unofficial patches to the source code that provide various features
|
||||
and enhancements.
|
||||
|
||||
To apply a patch, run `tools/apply_patch.sh [patch]` where `[patch]` is the name of the
|
||||
.patch file you wish to apply. This will perform all of the patch's changes
|
||||
to the source code.
|
||||
|
||||
Likewise, to undo the changes from a patch you applied, run
|
||||
`tools/revert_patch.sh` with the name of the .patch file you wish to undo.
|
||||
|
||||
To create your own enhancement patch, switch to the `master` Git
|
||||
branch, make your changes to the code (but do not commit), then run `tools/create_patch.sh`. Your changes will be stored in the .patch file you specify.
|
||||
|
||||
The following enhancements are included in this directory:
|
||||
|
||||
## Crash Screen - `crash.patch`
|
||||
|
||||
This enhancement provides a crash screen that is displayed when the code throws a hardware exception. This may be useful for diagnosing crashes in game code.
|
||||
|
||||
## Debug Box - `debug_box.patch`
|
||||
|
||||
This allows you to draw 3D boxes for debugging purposes.
|
||||
|
||||
Call the `debug_box` function whenever you want to draw one. `debug_box` by default takes two arguments: a center and bounds vec3f. This will draw a box starting from the point (center - bounds) to (center + bounds).
|
||||
Use `debug_box_rot` to draw a box rotated in the xz-plane. If you want to draw a box by specifying min and max points, use `debug_box_pos` instead.
|
||||
|
||||
## FPS Counter - `fps.patch`
|
||||
|
||||
This patch provides an in-game FPS counter to measure the frame rate.
|
||||
|
||||
## iQue Player Support - `ique_support.patch`
|
||||
|
||||
This enhancement allows the same ROM to work on both the Nintendo 64 and the iQue Player.
|
||||
|
||||
## Memory Expansion Pak Error Screen - `mem_error_screen.patch`
|
||||
|
||||
Use this patch if your game requires over 4 MB of memory and requires the
|
||||
Expansion Pak. If the Expansion Pak is not present, an error message will be
|
||||
shown on startup.
|
||||
|
||||
## Demo Input Recorder - `record_demo.patch`
|
||||
|
||||
This patch allows you to record gameplay demos for the attract screen. It requires the latest nightly versions of Project64, and uses the Project64 JavaScript API to dump the demo input data from RAM and write it to a file.
|
||||
|
||||
Place the `enhancements/RecordDemo.js` file in the `/Scripts/` folder in the Project64 directory.
|
||||
|
||||
In the Scripts window, double click on "RecordDemo" on the list on the left side.
|
||||
|
||||
When this is done, it should turn green which lets you know that it has started.
|
||||
|
||||
When your demo has been recorded, it will be dumped to the newly created `/SM64_DEMOS/` folder within the Project64 directory.
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef _CRASH_H_
|
||||
#define _CRASH_H_
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#define CRASH_SCREEN_INCLUDED 1
|
||||
|
||||
extern u32 cop0_get_cause(void);
|
||||
extern u32 cop0_get_epc(void);
|
||||
extern u32 cop0_get_badvaddr(void);
|
||||
|
||||
extern void _n64_assert(const char* pFile, int nLine, const char *pExpression, int nStopProgram);
|
||||
|
||||
extern u8 __crash_handler_entry[];
|
||||
|
||||
void generate_exception_preambles(void *entryPoint);
|
||||
void show_crash_screen_and_hang(void);
|
||||
u8 ascii_to_idx(char c);
|
||||
void fb_set_address(void *address);
|
||||
void fb_swap(void);
|
||||
void fb_fill(int baseX, int baseY, int width, int height);
|
||||
void fb_draw_char(int x, int y, u8 idx);
|
||||
void fb_draw_char_shaded(int x, int y, u8 idx);
|
||||
int fb_print_str(int x, int y, const char *str);
|
||||
int fb_print_uint(int x, int y, u32 value);
|
||||
void fb_print_int_hex(int x, int y, u32 value, int nbits);
|
||||
void fb_print_gpr_states(int x, int y, const char* regStrs[], u32 *regContext);
|
||||
|
||||
#endif /* _CRASH_H_ */
|
||||
@@ -1,292 +0,0 @@
|
||||
/* SM64 Crash Handler */
|
||||
/* See Readme in crash.inc.s for details. */
|
||||
|
||||
#include <sm64.h>
|
||||
|
||||
#include "crash.h"
|
||||
|
||||
extern u32 exceptionRegContext[];
|
||||
|
||||
extern char *pAssertFile;
|
||||
extern int nAssertLine;
|
||||
extern char *pAssertExpression;
|
||||
extern int nAssertStopProgram;
|
||||
|
||||
u16 fbFillColor = 0xFFFF;
|
||||
u16 fbShadeColor = 0x0000;
|
||||
u16 *fbAddress = NULL;
|
||||
|
||||
extern u8 crashFont[];
|
||||
|
||||
const char *szErrCodes[] = {
|
||||
"INTERRUPT",
|
||||
"TLB MOD",
|
||||
"UNMAPPED LOAD ADDR",
|
||||
"UNMAPPED STORE ADDR",
|
||||
"BAD LOAD ADDR",
|
||||
"BAD STORE ADDR",
|
||||
"BUS ERR ON INSTR FETCH",
|
||||
"BUS ERR ON LOADSTORE",
|
||||
"SYSCALL",
|
||||
"BREAKPOINT",
|
||||
"UNKNOWN INSTR",
|
||||
"COP UNUSABLE",
|
||||
"ARITHMETIC OVERFLOW",
|
||||
"TRAP EXC",
|
||||
"VIRTUAL COHERENCY INSTR",
|
||||
"FLOAT EXC",
|
||||
};
|
||||
|
||||
const char *szGPRegisters1[] = { "R0", "AT", "V0", "V1", "A0", "A1", "A2", "A3",
|
||||
"T0", "T1", "T2", "T3", "T4", "T5", "T6", NULL };
|
||||
|
||||
const char *szGPRegisters2[] = { "T7", "S0", "S1", "S2", "S3", "S4",
|
||||
"S5", "S6", "S7", "T8", "T9", /*"K0", "K1",*/
|
||||
"GP", "SP", "FP", "RA", NULL };
|
||||
|
||||
/*
|
||||
Generates new preamble code at the exception vectors (0x000, 0x180)
|
||||
|
||||
eg: generate_exception_preambles(crash_handler_entry);
|
||||
|
||||
000: lui k0, hi(crash_handler_entry)
|
||||
004: addiu k0, k0, lo(crash_handler_entry)
|
||||
008: jr k0
|
||||
00C: nop
|
||||
*/
|
||||
void generate_exception_preambles(void *entryPoint) {
|
||||
u8 *mem = (u8 *) 0xA0000000;
|
||||
int offs = 0;
|
||||
int i;
|
||||
|
||||
u16 hi = (u32) entryPoint >> 16;
|
||||
u16 lo = (u32) entryPoint & 0xFFFF;
|
||||
|
||||
if (lo & 0x8000) {
|
||||
hi++;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
*(u32 *) &mem[offs + 0x00] = 0x3C1A0000 | hi;
|
||||
*(u32 *) &mem[offs + 0x04] = 0x275A0000 | lo;
|
||||
*(u32 *) &mem[offs + 0x08] = 0x03400008;
|
||||
*(u32 *) &mem[offs + 0x0C] = 0x00000000;
|
||||
offs += 0x180;
|
||||
}
|
||||
}
|
||||
|
||||
int crash_strlen(char *str) {
|
||||
int len = 0;
|
||||
while (*str++) {
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void show_crash_screen_and_hang(void) {
|
||||
u32 cause;
|
||||
u32 epc;
|
||||
u8 errno;
|
||||
|
||||
fb_set_address((void *) (*(u32 *) 0xA4400004 | 0x80000000)); // replace me
|
||||
|
||||
cause = cop0_get_cause();
|
||||
epc = cop0_get_epc();
|
||||
|
||||
errno = (cause >> 2) & 0x1F;
|
||||
|
||||
if (nAssertStopProgram == 0) {
|
||||
fbFillColor = 0x6253;
|
||||
fb_fill(10, 10, 300, 220);
|
||||
|
||||
fb_print_str(80, 20, "AN ERROR HAS OCCURRED!");
|
||||
fb_print_int_hex(80, 30, errno, 8);
|
||||
fb_print_str(107, 30, szErrCodes[errno]);
|
||||
|
||||
if (errno >= 2 && errno <= 5) {
|
||||
/*
|
||||
2 UNMAPPED LOAD ADDR
|
||||
3 UNMAPPED STORE ADDR
|
||||
4 BAD LOAD ADDR
|
||||
5 BAD STORE ADDR
|
||||
*/
|
||||
u32 badvaddr = cop0_get_badvaddr();
|
||||
|
||||
fb_print_str(188, 50, "VA");
|
||||
fb_print_int_hex(215, 50, badvaddr, 32);
|
||||
}
|
||||
} else {
|
||||
int afterFileX;
|
||||
int exprBoxWidth;
|
||||
fbFillColor = 0x5263;
|
||||
fb_fill(10, 10, 300, 220);
|
||||
|
||||
fb_print_str(80, 20, "ASSERTION FAILED!");
|
||||
|
||||
afterFileX = fb_print_str(80, 30, pAssertFile);
|
||||
fb_print_str(afterFileX, 30, ":");
|
||||
fb_print_uint(afterFileX + 5, 30, nAssertLine);
|
||||
|
||||
exprBoxWidth = (crash_strlen(pAssertExpression) * 5) + 2;
|
||||
fbFillColor = 0x0001;
|
||||
fb_fill(80 - 1, 40 - 1, exprBoxWidth, 10);
|
||||
fb_print_str(80, 40, pAssertExpression);
|
||||
}
|
||||
|
||||
fb_print_str(80, 50, "PC");
|
||||
fb_print_int_hex(95, 50, epc, 32);
|
||||
|
||||
fb_print_gpr_states(80, 70, szGPRegisters1, &exceptionRegContext[6 + 0]);
|
||||
fb_print_gpr_states(145, 70, szGPRegisters2, &exceptionRegContext[6 + 15 * 2]);
|
||||
|
||||
fb_swap();
|
||||
osWritebackDCacheAll();
|
||||
|
||||
while (1) // hang forever
|
||||
{
|
||||
UNUSED volatile int t = 0; // keep pj64 happy
|
||||
}
|
||||
}
|
||||
|
||||
u8 ascii_to_idx(char c) {
|
||||
return c - 0x20;
|
||||
}
|
||||
|
||||
void fb_set_address(void *address) {
|
||||
fbAddress = (u16 *) address;
|
||||
}
|
||||
|
||||
void fb_swap() {
|
||||
// update VI frame buffer register
|
||||
// todo other registers
|
||||
*(u32 *) (0xA4400004) = (u32) fbAddress & 0x00FFFFFF;
|
||||
}
|
||||
|
||||
void fb_fill(int baseX, int baseY, int width, int height) {
|
||||
int y, x;
|
||||
|
||||
for (y = baseY; y < baseY + height; y++) {
|
||||
for (x = baseX; x < baseX + width; x++) {
|
||||
fbAddress[y * 320 + x] = fbFillColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fb_draw_char(int x, int y, u8 idx) {
|
||||
u16 *out = &fbAddress[y * 320 + x];
|
||||
const u8 *in = &crashFont[idx * 3];
|
||||
int nbyte;
|
||||
int nrow;
|
||||
int ncol;
|
||||
|
||||
for (nbyte = 0; nbyte < 3; nbyte++) {
|
||||
u8 curbyte = in[nbyte];
|
||||
for (nrow = 0; nrow < 2; nrow++) {
|
||||
for (ncol = 0; ncol < 4; ncol++) {
|
||||
u8 px = curbyte & (1 << 7 - (nrow * 4 + ncol));
|
||||
if (px != 0) {
|
||||
out[ncol] = fbFillColor;
|
||||
}
|
||||
}
|
||||
out += 320;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fb_draw_char_shaded(int x, int y, u8 idx) {
|
||||
fbFillColor = 0x0001;
|
||||
fb_draw_char(x - 1, y + 1, idx);
|
||||
|
||||
fbFillColor = 0xFFFF;
|
||||
fb_draw_char(x, y, idx);
|
||||
}
|
||||
|
||||
int fb_print_str(int x, int y, const char *str) {
|
||||
while (1) {
|
||||
int yoffs = 0;
|
||||
u8 idx;
|
||||
char c = *str++;
|
||||
|
||||
if (c == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == ' ') {
|
||||
x += 5;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 'j':
|
||||
case 'g':
|
||||
case 'p':
|
||||
case 'q':
|
||||
case 'y':
|
||||
case 'Q':
|
||||
yoffs = 1;
|
||||
break;
|
||||
case ',':
|
||||
yoffs = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
idx = ascii_to_idx(c);
|
||||
fb_draw_char_shaded(x, y + yoffs, idx);
|
||||
x += 5;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
void fb_print_int_hex(int x, int y, u32 value, int nbits) {
|
||||
nbits -= 4;
|
||||
|
||||
while (nbits >= 0) {
|
||||
int nib = ((value >> nbits) & 0xF);
|
||||
u8 idx;
|
||||
|
||||
if (nib > 9) {
|
||||
idx = ('A' - 0x20) + (nib - 0xa);
|
||||
} else {
|
||||
idx = ('0' - 0x20) + nib;
|
||||
}
|
||||
|
||||
fb_draw_char_shaded(x, y, idx);
|
||||
x += 5;
|
||||
|
||||
nbits -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
int fb_print_uint(int x, int y, u32 value) {
|
||||
int nchars = 0;
|
||||
|
||||
int v = value;
|
||||
int i;
|
||||
while (v /= 10) {
|
||||
nchars++;
|
||||
}
|
||||
|
||||
x += nchars * 5;
|
||||
|
||||
for (i = nchars; i >= 0; i--) {
|
||||
fb_draw_char_shaded(x, y, ('0' - 0x20) + (value % 10));
|
||||
value /= 10;
|
||||
x -= 5;
|
||||
}
|
||||
|
||||
return (x + nchars * 5);
|
||||
}
|
||||
|
||||
void fb_print_gpr_states(int x, int y, const char *regNames[], u32 *regContext) {
|
||||
int i;
|
||||
for (i = 0;; i++) {
|
||||
if (regNames[i] == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
fb_print_str(x, y, regNames[i]);
|
||||
fb_print_int_hex(x + 15, y, regContext[i * 2 + 1], 32);
|
||||
y += 10;
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
# SM64 Crash Handler
|
||||
# See Readme below.
|
||||
|
||||
.set COP0_CAUSE, 13
|
||||
.set COP0_EPC, 14
|
||||
.set COP0_BADVADDR, 8
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* IMPORTANT README:
|
||||
* ---------------------------------------------------------------
|
||||
* To use this crash screen, in lib/__osExceptionPreamble.s, change
|
||||
* the function to use the following assembly:
|
||||
*
|
||||
* lui $k0, %hi(__crash_handler_entry)
|
||||
* addiu $k0, $k0, %lo(__crash_handler_entry)
|
||||
* jr $k0
|
||||
* nop
|
||||
*
|
||||
* Doing just a jal __crash_handler_entry will cause mupen recompiler
|
||||
* errors, so be sure to use the original exception style assembly
|
||||
* above!
|
||||
*
|
||||
* Be sure to add #include "../../enhancements/crash.inc.c" to
|
||||
* the top of game.c. Add .include "../enhancements/crash.inc.s" to
|
||||
* the bottom of asm/decompress.s. Add "../enhancements/crash.h" to
|
||||
* the top of sm64.h, above the CRASH_SCREEN_INCLUDED condition.
|
||||
*
|
||||
* See the DEBUG_ASSERT macro on how to call the crash screen for
|
||||
* detected exceptions.
|
||||
*/
|
||||
|
||||
glabel crashFont
|
||||
.incbin "enhancements/crash_font.bin"
|
||||
.align 4
|
||||
|
||||
glabel exceptionRegContext
|
||||
.fill 0x108
|
||||
|
||||
glabel pAssertFile
|
||||
.dword 0
|
||||
glabel nAssertLine
|
||||
.dword 0
|
||||
glabel pAssertExpression
|
||||
.dword 0
|
||||
glabel nAssertStopProgram
|
||||
.dword 0
|
||||
|
||||
glabel _n64_assert
|
||||
lui $at, %hi(pAssertFile)
|
||||
sw $a0, %lo(pAssertFile)($at)
|
||||
lui $at, %hi(nAssertLine)
|
||||
sw $a1, %lo(nAssertLine)($at)
|
||||
lui $at, %hi(pAssertExpression)
|
||||
sw $a2, %lo(pAssertExpression)($at)
|
||||
lui $at, %hi(nAssertStopProgram)
|
||||
sw $a3, %lo(nAssertStopProgram)($at)
|
||||
beqz $a3, .end_2
|
||||
nop
|
||||
syscall # trigger crash screen
|
||||
.end_2:
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
glabel cop0_get_cause
|
||||
jr $ra
|
||||
mfc0 $v0, $13 # COP0_CAUSE
|
||||
|
||||
glabel cop0_get_epc
|
||||
jr $ra
|
||||
mfc0 $v0, $14 # COP0_EPC
|
||||
|
||||
glabel cop0_get_badvaddr
|
||||
jr $ra
|
||||
mfc0 $v0, $8 # COP0_BADVADDR
|
||||
|
||||
# If the error code field of cop0's cause register is non-zero,
|
||||
# draw crash details to the screen and hang
|
||||
#
|
||||
# If there wasn't an error, continue to the original handler
|
||||
|
||||
glabel __crash_handler_entry
|
||||
la $k0, exceptionRegContext
|
||||
sd $zero, 0x018 ($k0)
|
||||
sd $at, 0x020 ($k0)
|
||||
sd $v0, 0x028 ($k0)
|
||||
sd $v1, 0x030 ($k0)
|
||||
sd $a0, 0x038 ($k0)
|
||||
sd $a1, 0x040 ($k0)
|
||||
sd $a2, 0x048 ($k0)
|
||||
sd $a3, 0x050 ($k0)
|
||||
sd $t0, 0x058 ($k0)
|
||||
sd $t1, 0x060 ($k0)
|
||||
sd $t2, 0x068 ($k0)
|
||||
sd $t3, 0x070 ($k0)
|
||||
sd $t4, 0x078 ($k0)
|
||||
sd $t5, 0x080 ($k0)
|
||||
sd $t6, 0x088 ($k0)
|
||||
sd $t7, 0x090 ($k0)
|
||||
sd $s0, 0x098 ($k0)
|
||||
sd $s1, 0x0A0 ($k0)
|
||||
sd $s2, 0x0A8 ($k0)
|
||||
sd $s3, 0x0B0 ($k0)
|
||||
sd $s4, 0x0B8 ($k0)
|
||||
sd $s5, 0x0C0 ($k0)
|
||||
sd $s6, 0x0C8 ($k0)
|
||||
sd $s7, 0x0D0 ($k0)
|
||||
sd $t8, 0x0D8 ($k0)
|
||||
sd $t9, 0x0E0 ($k0)
|
||||
sd $gp, 0x0E8 ($k0)
|
||||
sd $sp, 0x0F0 ($k0)
|
||||
sd $s8, 0x0F8 ($k0)
|
||||
sd $ra, 0x100 ($k0)
|
||||
mfc0 $t0, $13 # COP0_CAUSE
|
||||
srl $t0, $t0, 2
|
||||
andi $t0, $t0, 0x1F
|
||||
beqz $t0, .end
|
||||
nop
|
||||
# cop unusable exception fired twice on startup so we'll ignore it for now
|
||||
li $at, 0x0B
|
||||
beq $t0, $at, .end
|
||||
nop
|
||||
jal show_crash_screen_and_hang
|
||||
nop
|
||||
.end:
|
||||
ld $zero, 0x018 ($k0)
|
||||
ld $at, 0x020 ($k0)
|
||||
ld $v0, 0x028 ($k0)
|
||||
ld $v1, 0x030 ($k0)
|
||||
ld $a0, 0x038 ($k0)
|
||||
ld $a1, 0x040 ($k0)
|
||||
ld $a2, 0x048 ($k0)
|
||||
ld $a3, 0x050 ($k0)
|
||||
ld $t0, 0x058 ($k0)
|
||||
ld $t1, 0x060 ($k0)
|
||||
ld $t2, 0x068 ($k0)
|
||||
ld $t3, 0x070 ($k0)
|
||||
ld $t4, 0x078 ($k0)
|
||||
ld $t5, 0x080 ($k0)
|
||||
ld $t6, 0x088 ($k0)
|
||||
ld $t7, 0x090 ($k0)
|
||||
ld $s0, 0x098 ($k0)
|
||||
ld $s1, 0x0A0 ($k0)
|
||||
ld $s2, 0x0A8 ($k0)
|
||||
ld $s3, 0x0B0 ($k0)
|
||||
ld $s4, 0x0B8 ($k0)
|
||||
ld $s5, 0x0C0 ($k0)
|
||||
ld $s6, 0x0C8 ($k0)
|
||||
ld $s7, 0x0D0 ($k0)
|
||||
ld $t8, 0x0D8 ($k0)
|
||||
ld $t9, 0x0E0 ($k0)
|
||||
ld $gp, 0x0E8 ($k0)
|
||||
ld $sp, 0x0F0 ($k0)
|
||||
ld $s8, 0x0F8 ($k0)
|
||||
ld $ra, 0x100 ($k0)
|
||||
lui $k0, %hi(__osException)
|
||||
addiu $k0, $k0, %lo(__osException)
|
||||
jr $k0 # run the original handler
|
||||
nop
|
||||
529
enhancements/crash.patch
Normal file
529
enhancements/crash.patch
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,23 +0,0 @@
|
||||
#ifndef _DEBUG_DRAW_CUBE_H
|
||||
#define _DEBUG_DRAW_CUBE_H
|
||||
|
||||
/**
|
||||
* @file debug_box.h
|
||||
* Draws debug boxes, see debug_box.inc.c for details
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/**
|
||||
* The max amount of debug boxes before debug_box() just returns.
|
||||
* You can set this to something higher like 1000, but things like text will stop rendering.
|
||||
*/
|
||||
#define MAX_DEBUG_BOXES 100
|
||||
|
||||
void debug_box(Vec3f center, Vec3f bounds);
|
||||
void debug_box_rot(Vec3f center, Vec3f bounds, s16 yaw);
|
||||
|
||||
void debug_box_pos(Vec3f pMin, Vec3f pMax);
|
||||
void debug_box_pos_rot(Vec3f pMin, Vec3f pMax, s16 yaw);
|
||||
|
||||
#endif /* _DEBUG_DRAW_CUBE_H */
|
||||
@@ -1,248 +0,0 @@
|
||||
#include <ultra64.h>
|
||||
|
||||
#include "sm64.h"
|
||||
#include "game/game.h"
|
||||
#include "game/geo_misc.h"
|
||||
#include "engine/math_util.h"
|
||||
|
||||
#include "debug_box.h"
|
||||
|
||||
/**
|
||||
* @file debug_box.inc.c
|
||||
* Draws 3D boxes for debugging purposes.
|
||||
*
|
||||
* How to use:
|
||||
*
|
||||
* In area.c, add this to the list of includes:
|
||||
*
|
||||
* #include "enhancements/debug_box.inc.c"
|
||||
*
|
||||
* Then in render_game() in the same file, add a call to render_debug_boxes():
|
||||
*
|
||||
* void render_game(void) {
|
||||
* if (gCurrentArea != NULL && !gWarpTransition.pauseRendering) {
|
||||
* geo_process_root(...);
|
||||
*
|
||||
* render_debug_boxes(); // add here
|
||||
*
|
||||
* gSPViewport(...);
|
||||
* gDPSetScissor(...);
|
||||
* //...
|
||||
*
|
||||
* Now just call debug_box() whenever you want to draw one!
|
||||
*
|
||||
* debug_box by default takes two arguments: a center and bounds vec3f.
|
||||
* This will draw a box starting from the point (center - bounds) to (center + bounds).
|
||||
*
|
||||
* Use debug_box_rot to draw a box rotated in the xz-plane.
|
||||
*
|
||||
* If you want to draw a box by specifying min and max points, use debug_box_pos() instead.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Internal struct containing box info
|
||||
*/
|
||||
struct DebugBox {
|
||||
Vec3s center;
|
||||
Vec3s bounds;
|
||||
s16 yaw;
|
||||
};
|
||||
|
||||
struct DebugBox *sBoxes[MAX_DEBUG_BOXES];
|
||||
s16 sNumBoxes = 0;
|
||||
|
||||
extern Mat4 gMatStack[32]; //XXX: Hack
|
||||
|
||||
/**
|
||||
* The debug boxes' transparency
|
||||
*/
|
||||
#define DBG_BOX_ALPHA 0x7F
|
||||
/**
|
||||
* The debug boxes' color
|
||||
*/
|
||||
#define DBG_BOX_COL 0xFF, 0x00, 0x00, DBG_BOX_ALPHA
|
||||
|
||||
/**
|
||||
* Sets up the RCP for drawing the boxes
|
||||
*/
|
||||
static const Gfx dl_debug_box_begin[] = {
|
||||
gsDPPipeSync(),
|
||||
#if DBG_BOX_ALPHA < 0xFF
|
||||
gsDPSetRenderMode(G_RM_ZB_XLU_SURF, G_RM_NOOP2),
|
||||
#else
|
||||
gsDPSetRenderMode(G_RM_ZB_OPA_SURF, G_RM_NOOP2),
|
||||
#endif
|
||||
gsSPClearGeometryMode(G_LIGHTING | G_CULL_BACK),
|
||||
gsSPSetGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH),
|
||||
gsSPTexture(0, 0, 0, 0, G_OFF),
|
||||
gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE),
|
||||
gsSPEndDisplayList(),
|
||||
};
|
||||
|
||||
/**
|
||||
* Actually draws the box
|
||||
*/
|
||||
static const Gfx dl_debug_draw_box[] = {
|
||||
gsSP2Triangles( 0, 1, 2, 0x0, 2, 1, 3, 0x0),
|
||||
gsSP2Triangles( 2, 3, 6, 0x0, 6, 3, 7, 0x0),
|
||||
|
||||
gsSP2Triangles( 4, 0, 2, 0x0, 2, 6, 4, 0x0),
|
||||
gsSP2Triangles( 1, 5, 3, 0x0, 3, 5, 7, 0x0),
|
||||
|
||||
gsSP2Triangles( 1, 0, 4, 0x0, 1, 4, 5, 0x0),
|
||||
gsSP2Triangles( 5, 4, 6, 0x0, 5, 6, 7, 0x0),
|
||||
|
||||
gsSPEndDisplayList(),
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a box to the list to be rendered this frame.
|
||||
*
|
||||
* If there are already MAX_DEBUG_BOXES boxes, does nothing.
|
||||
*/
|
||||
static void append_debug_box(Vec3f center, Vec3f bounds, s16 yaw)
|
||||
{
|
||||
if (sNumBoxes >= MAX_DEBUG_BOXES ||
|
||||
(sBoxes[sNumBoxes] = mem_pool_alloc(gEffectsMemoryPool, sizeof(struct DebugBox))) == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
vec3f_to_vec3s(sBoxes[sNumBoxes]->center, center);
|
||||
vec3f_to_vec3s(sBoxes[sNumBoxes]->bounds, bounds);
|
||||
|
||||
sBoxes[sNumBoxes]->yaw = yaw;
|
||||
|
||||
++sNumBoxes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a debug box from (center - bounds) to (center + bounds)
|
||||
* To draw a rotated box, use debug_box_rot()
|
||||
*
|
||||
* @see debug_box_rot()
|
||||
*/
|
||||
void debug_box(Vec3f center, Vec3f bounds)
|
||||
{
|
||||
append_debug_box(center, bounds, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a debug box from (center - bounds) to (center + bounds), rotating it by `yaw`
|
||||
*/
|
||||
void debug_box_rot(Vec3f center, Vec3f bounds, s16 yaw)
|
||||
{
|
||||
append_debug_box(center, bounds, yaw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a debug box from pMin to pMax
|
||||
* To draw a rotated box this way, use debug_box_pos_rot()
|
||||
*
|
||||
* @see debug_box_pos_rot()
|
||||
*/
|
||||
void debug_box_pos(Vec3f pMin, Vec3f pMax)
|
||||
{
|
||||
debug_box_pos_rot(pMin, pMax, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a debug box from pMin to pMax, rotating it in the xz-plane by `yaw`
|
||||
*/
|
||||
void debug_box_pos_rot(Vec3f pMin, Vec3f pMax, s16 yaw)
|
||||
{
|
||||
Vec3f center, bounds;
|
||||
|
||||
bounds[0] = pMax[0] - pMin[0] / 2.0f;
|
||||
bounds[1] = pMax[1] - pMin[1] / 2.0f;
|
||||
bounds[2] = pMax[2] - pMin[2] / 2.0f;
|
||||
|
||||
center[0] = pMin[0] + bounds[0];
|
||||
center[1] = pMin[1] + bounds[1];
|
||||
center[2] = pMin[2] + bounds[2];
|
||||
|
||||
append_debug_box(center, bounds, yaw);
|
||||
}
|
||||
|
||||
static void render_box(struct DebugBox *box)
|
||||
{
|
||||
Vtx *verts = alloc_display_list(8 * sizeof(Vtx));
|
||||
Mtx *translate;
|
||||
Mtx *rotate;
|
||||
Mtx *translateback;
|
||||
s32 x0 = box->center[0],
|
||||
y0 = box->center[1],
|
||||
z0 = box->center[2];
|
||||
|
||||
s32 xb = box->bounds[0],
|
||||
yb = box->bounds[1],
|
||||
zb = box->bounds[2];
|
||||
|
||||
if (verts != NULL) {
|
||||
if (box->yaw != 0) {
|
||||
// Translate to the origin, rotate, then translate back, effectively rotating the box about
|
||||
// its center
|
||||
translate = alloc_display_list(sizeof(Mtx));
|
||||
rotate = alloc_display_list(sizeof(Mtx));
|
||||
translateback = alloc_display_list(sizeof(Mtx));
|
||||
|
||||
guTranslate(translate, box->center[0], box->center[1], box->center[2]);
|
||||
guRotate(rotate, box->yaw / (float)0x10000 * 360.0f, 0, 1.0f, 0);
|
||||
guTranslate(translateback, -box->center[0], -box->center[1], -box->center[2]);
|
||||
|
||||
gSPMatrix(gDisplayListHead++, translate, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH);
|
||||
gSPMatrix(gDisplayListHead++, rotate, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);
|
||||
gSPMatrix(gDisplayListHead++, translateback, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);
|
||||
}
|
||||
|
||||
#define DBG_BOX_VTX(i, x, y, z) make_vertex(verts, i, x, y, z, 0, 0, DBG_BOX_COL)
|
||||
DBG_BOX_VTX(0, x0 - xb, y0 + yb, z0 - zb);
|
||||
DBG_BOX_VTX(1, x0 + xb, y0 + yb, z0 - zb);
|
||||
DBG_BOX_VTX(2, x0 - xb, y0 - yb, z0 - zb);
|
||||
DBG_BOX_VTX(3, x0 + xb, y0 - yb, z0 - zb);
|
||||
DBG_BOX_VTX(4, x0 - xb, y0 + yb, z0 + zb);
|
||||
DBG_BOX_VTX(5, x0 + xb, y0 + yb, z0 + zb);
|
||||
DBG_BOX_VTX(6, x0 - xb, y0 - yb, z0 + zb);
|
||||
DBG_BOX_VTX(7, x0 + xb, y0 - yb, z0 + zb);
|
||||
#undef DBG_BOX_VTX
|
||||
|
||||
gSPVertex(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(verts), 8, 0);
|
||||
|
||||
gSPDisplayList(gDisplayListHead++, dl_debug_draw_box);
|
||||
|
||||
if (box->yaw != 0) {
|
||||
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void render_debug_boxes()
|
||||
{
|
||||
s32 i;
|
||||
Mtx *mtx;
|
||||
|
||||
if (sNumBoxes == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
mtx = alloc_display_list(sizeof(Mtx));
|
||||
if (mtx == NULL) {
|
||||
for (i = 0; i < sNumBoxes; ++i) {
|
||||
mem_pool_free(gEffectsMemoryPool, sBoxes[i]);
|
||||
}
|
||||
sNumBoxes = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
//XXX: This is hacky. Ths camera's look-at matrix is stored in gMatStack[1], so this is a simple way
|
||||
// of using it without reconstructing the matrix.
|
||||
mtxf_to_mtx(mtx, gMatStack[1]);
|
||||
gSPMatrix(gDisplayListHead++, mtx, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
|
||||
gSPDisplayList(gDisplayListHead++, dl_debug_box_begin);
|
||||
|
||||
for (i = 0; i < sNumBoxes; ++i) {
|
||||
render_box(sBoxes[i]);
|
||||
mem_pool_free(gEffectsMemoryPool, sBoxes[i]);
|
||||
}
|
||||
|
||||
sNumBoxes = 0;
|
||||
}
|
||||
302
enhancements/debug_box.patch
Normal file
302
enhancements/debug_box.patch
Normal file
@@ -0,0 +1,302 @@
|
||||
diff --git a/src/game/area.c b/src/game/area.c
|
||||
index 240605d8..88c1a314 100644
|
||||
--- a/src/game/area.c
|
||||
+++ b/src/game/area.c
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "level_update.h"
|
||||
#include "engine/geo_layout.h"
|
||||
#include "save_file.h"
|
||||
+#include "debug_box.h"
|
||||
|
||||
struct SpawnInfo gPlayerSpawnInfos[1];
|
||||
struct GraphNode *D_8033A160[0x100];
|
||||
@@ -352,6 +353,8 @@ void render_game(void) {
|
||||
if (gCurrentArea != NULL && !gWarpTransition.pauseRendering) {
|
||||
geo_process_root(gCurrentArea->unk04, D_8032CE74, D_8032CE78, gFBSetColor);
|
||||
|
||||
+ render_debug_boxes();
|
||||
+
|
||||
gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&D_8032CF00));
|
||||
|
||||
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH,
|
||||
diff --git a/src/game/debug_box.c b/src/game/debug_box.c
|
||||
new file mode 100644
|
||||
index 00000000..0ee87ec7
|
||||
--- /dev/null
|
||||
+++ b/src/game/debug_box.c
|
||||
@@ -0,0 +1,244 @@
|
||||
+#include <ultra64.h>
|
||||
+
|
||||
+#include "sm64.h"
|
||||
+#include "game/game.h"
|
||||
+#include "game/geo_misc.h"
|
||||
+#include "engine/math_util.h"
|
||||
+
|
||||
+#include "debug_box.h"
|
||||
+
|
||||
+/**
|
||||
+ * @file debug_box.c
|
||||
+ * Draws 3D boxes for debugging purposes.
|
||||
+ *
|
||||
+ * How to use:
|
||||
+ *
|
||||
+ * In render_game() in area.c, add a call to render_debug_boxes():
|
||||
+ *
|
||||
+ * void render_game(void) {
|
||||
+ * if (gCurrentArea != NULL && !gWarpTransition.pauseRendering) {
|
||||
+ * geo_process_root(...);
|
||||
+ *
|
||||
+ * render_debug_boxes(); // add here
|
||||
+ *
|
||||
+ * gSPViewport(...);
|
||||
+ * gDPSetScissor(...);
|
||||
+ * //...
|
||||
+ *
|
||||
+ * Now just call debug_box() whenever you want to draw one!
|
||||
+ *
|
||||
+ * debug_box by default takes two arguments: a center and bounds vec3f.
|
||||
+ * This will draw a box starting from the point (center - bounds) to (center + bounds).
|
||||
+ *
|
||||
+ * Use debug_box_rot to draw a box rotated in the xz-plane.
|
||||
+ *
|
||||
+ * If you want to draw a box by specifying min and max points, use debug_box_pos() instead.
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * Internal struct containing box info
|
||||
+ */
|
||||
+struct DebugBox {
|
||||
+ Vec3s center;
|
||||
+ Vec3s bounds;
|
||||
+ s16 yaw;
|
||||
+};
|
||||
+
|
||||
+struct DebugBox *sBoxes[MAX_DEBUG_BOXES];
|
||||
+s16 sNumBoxes = 0;
|
||||
+
|
||||
+extern Mat4 gMatStack[32]; //XXX: Hack
|
||||
+
|
||||
+/**
|
||||
+ * The debug boxes' transparency
|
||||
+ */
|
||||
+#define DBG_BOX_ALPHA 0x7F
|
||||
+/**
|
||||
+ * The debug boxes' color
|
||||
+ */
|
||||
+#define DBG_BOX_COL 0xFF, 0x00, 0x00, DBG_BOX_ALPHA
|
||||
+
|
||||
+/**
|
||||
+ * Sets up the RCP for drawing the boxes
|
||||
+ */
|
||||
+static const Gfx dl_debug_box_begin[] = {
|
||||
+ gsDPPipeSync(),
|
||||
+#if DBG_BOX_ALPHA < 0xFF
|
||||
+ gsDPSetRenderMode(G_RM_ZB_XLU_SURF, G_RM_NOOP2),
|
||||
+#else
|
||||
+ gsDPSetRenderMode(G_RM_ZB_OPA_SURF, G_RM_NOOP2),
|
||||
+#endif
|
||||
+ gsSPClearGeometryMode(G_LIGHTING | G_CULL_BACK),
|
||||
+ gsSPSetGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH),
|
||||
+ gsSPTexture(0, 0, 0, 0, G_OFF),
|
||||
+ gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE),
|
||||
+ gsSPEndDisplayList(),
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * Actually draws the box
|
||||
+ */
|
||||
+static const Gfx dl_debug_draw_box[] = {
|
||||
+ gsSP2Triangles( 0, 1, 2, 0x0, 2, 1, 3, 0x0),
|
||||
+ gsSP2Triangles( 2, 3, 6, 0x0, 6, 3, 7, 0x0),
|
||||
+
|
||||
+ gsSP2Triangles( 4, 0, 2, 0x0, 2, 6, 4, 0x0),
|
||||
+ gsSP2Triangles( 1, 5, 3, 0x0, 3, 5, 7, 0x0),
|
||||
+
|
||||
+ gsSP2Triangles( 1, 0, 4, 0x0, 1, 4, 5, 0x0),
|
||||
+ gsSP2Triangles( 5, 4, 6, 0x0, 5, 6, 7, 0x0),
|
||||
+
|
||||
+ gsSPEndDisplayList(),
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * Adds a box to the list to be rendered this frame.
|
||||
+ *
|
||||
+ * If there are already MAX_DEBUG_BOXES boxes, does nothing.
|
||||
+ */
|
||||
+static void append_debug_box(Vec3f center, Vec3f bounds, s16 yaw)
|
||||
+{
|
||||
+ if (sNumBoxes >= MAX_DEBUG_BOXES ||
|
||||
+ (sBoxes[sNumBoxes] = mem_pool_alloc(gEffectsMemoryPool, sizeof(struct DebugBox))) == NULL) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ vec3f_to_vec3s(sBoxes[sNumBoxes]->center, center);
|
||||
+ vec3f_to_vec3s(sBoxes[sNumBoxes]->bounds, bounds);
|
||||
+
|
||||
+ sBoxes[sNumBoxes]->yaw = yaw;
|
||||
+
|
||||
+ ++sNumBoxes;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Draws a debug box from (center - bounds) to (center + bounds)
|
||||
+ * To draw a rotated box, use debug_box_rot()
|
||||
+ *
|
||||
+ * @see debug_box_rot()
|
||||
+ */
|
||||
+void debug_box(Vec3f center, Vec3f bounds)
|
||||
+{
|
||||
+ append_debug_box(center, bounds, 0);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Draws a debug box from (center - bounds) to (center + bounds), rotating it by `yaw`
|
||||
+ */
|
||||
+void debug_box_rot(Vec3f center, Vec3f bounds, s16 yaw)
|
||||
+{
|
||||
+ append_debug_box(center, bounds, yaw);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Draws a debug box from pMin to pMax
|
||||
+ * To draw a rotated box this way, use debug_box_pos_rot()
|
||||
+ *
|
||||
+ * @see debug_box_pos_rot()
|
||||
+ */
|
||||
+void debug_box_pos(Vec3f pMin, Vec3f pMax)
|
||||
+{
|
||||
+ debug_box_pos_rot(pMin, pMax, 0);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Draws a debug box from pMin to pMax, rotating it in the xz-plane by `yaw`
|
||||
+ */
|
||||
+void debug_box_pos_rot(Vec3f pMin, Vec3f pMax, s16 yaw)
|
||||
+{
|
||||
+ Vec3f center, bounds;
|
||||
+
|
||||
+ bounds[0] = pMax[0] - pMin[0] / 2.0f;
|
||||
+ bounds[1] = pMax[1] - pMin[1] / 2.0f;
|
||||
+ bounds[2] = pMax[2] - pMin[2] / 2.0f;
|
||||
+
|
||||
+ center[0] = pMin[0] + bounds[0];
|
||||
+ center[1] = pMin[1] + bounds[1];
|
||||
+ center[2] = pMin[2] + bounds[2];
|
||||
+
|
||||
+ append_debug_box(center, bounds, yaw);
|
||||
+}
|
||||
+
|
||||
+static void render_box(struct DebugBox *box)
|
||||
+{
|
||||
+ Vtx *verts = alloc_display_list(8 * sizeof(Vtx));
|
||||
+ Mtx *translate;
|
||||
+ Mtx *rotate;
|
||||
+ Mtx *translateback;
|
||||
+ s32 x0 = box->center[0],
|
||||
+ y0 = box->center[1],
|
||||
+ z0 = box->center[2];
|
||||
+
|
||||
+ s32 xb = box->bounds[0],
|
||||
+ yb = box->bounds[1],
|
||||
+ zb = box->bounds[2];
|
||||
+
|
||||
+ if (verts != NULL) {
|
||||
+ if (box->yaw != 0) {
|
||||
+ // Translate to the origin, rotate, then translate back, effectively rotating the box about
|
||||
+ // its center
|
||||
+ translate = alloc_display_list(sizeof(Mtx));
|
||||
+ rotate = alloc_display_list(sizeof(Mtx));
|
||||
+ translateback = alloc_display_list(sizeof(Mtx));
|
||||
+
|
||||
+ guTranslate(translate, box->center[0], box->center[1], box->center[2]);
|
||||
+ guRotate(rotate, box->yaw / (float)0x10000 * 360.0f, 0, 1.0f, 0);
|
||||
+ guTranslate(translateback, -box->center[0], -box->center[1], -box->center[2]);
|
||||
+
|
||||
+ gSPMatrix(gDisplayListHead++, translate, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH);
|
||||
+ gSPMatrix(gDisplayListHead++, rotate, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);
|
||||
+ gSPMatrix(gDisplayListHead++, translateback, G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH);
|
||||
+ }
|
||||
+
|
||||
+#define DBG_BOX_VTX(i, x, y, z) make_vertex(verts, i, x, y, z, 0, 0, DBG_BOX_COL)
|
||||
+ DBG_BOX_VTX(0, x0 - xb, y0 + yb, z0 - zb);
|
||||
+ DBG_BOX_VTX(1, x0 + xb, y0 + yb, z0 - zb);
|
||||
+ DBG_BOX_VTX(2, x0 - xb, y0 - yb, z0 - zb);
|
||||
+ DBG_BOX_VTX(3, x0 + xb, y0 - yb, z0 - zb);
|
||||
+ DBG_BOX_VTX(4, x0 - xb, y0 + yb, z0 + zb);
|
||||
+ DBG_BOX_VTX(5, x0 + xb, y0 + yb, z0 + zb);
|
||||
+ DBG_BOX_VTX(6, x0 - xb, y0 - yb, z0 + zb);
|
||||
+ DBG_BOX_VTX(7, x0 + xb, y0 - yb, z0 + zb);
|
||||
+#undef DBG_BOX_VTX
|
||||
+
|
||||
+ gSPVertex(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(verts), 8, 0);
|
||||
+
|
||||
+ gSPDisplayList(gDisplayListHead++, dl_debug_draw_box);
|
||||
+
|
||||
+ if (box->yaw != 0) {
|
||||
+ gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void render_debug_boxes(void)
|
||||
+{
|
||||
+ s32 i;
|
||||
+ Mtx *mtx;
|
||||
+
|
||||
+ if (sNumBoxes == 0) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ mtx = alloc_display_list(sizeof(Mtx));
|
||||
+ if (mtx == NULL) {
|
||||
+ for (i = 0; i < sNumBoxes; ++i) {
|
||||
+ mem_pool_free(gEffectsMemoryPool, sBoxes[i]);
|
||||
+ }
|
||||
+ sNumBoxes = 0;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ //XXX: This is hacky. Ths camera's look-at matrix is stored in gMatStack[1], so this is a simple way
|
||||
+ // of using it without reconstructing the matrix.
|
||||
+ mtxf_to_mtx(mtx, gMatStack[1]);
|
||||
+ gSPMatrix(gDisplayListHead++, mtx, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
|
||||
+ gSPDisplayList(gDisplayListHead++, dl_debug_box_begin);
|
||||
+
|
||||
+ for (i = 0; i < sNumBoxes; ++i) {
|
||||
+ render_box(sBoxes[i]);
|
||||
+ mem_pool_free(gEffectsMemoryPool, sBoxes[i]);
|
||||
+ }
|
||||
+
|
||||
+ sNumBoxes = 0;
|
||||
+}
|
||||
diff --git a/src/game/debug_box.h b/src/game/debug_box.h
|
||||
new file mode 100644
|
||||
index 00000000..cdb3dc9d
|
||||
--- /dev/null
|
||||
+++ b/src/game/debug_box.h
|
||||
@@ -0,0 +1,25 @@
|
||||
+#ifndef _DEBUG_DRAW_CUBE_H
|
||||
+#define _DEBUG_DRAW_CUBE_H
|
||||
+
|
||||
+/**
|
||||
+ * @file debug_box.h
|
||||
+ * Draws debug boxes, see debug_box.inc.c for details
|
||||
+ */
|
||||
+
|
||||
+#include "types.h"
|
||||
+
|
||||
+/**
|
||||
+ * The max amount of debug boxes before debug_box() just returns.
|
||||
+ * You can set this to something higher like 1000, but things like text will stop rendering.
|
||||
+ */
|
||||
+#define MAX_DEBUG_BOXES 100
|
||||
+
|
||||
+void debug_box(Vec3f center, Vec3f bounds);
|
||||
+void debug_box_rot(Vec3f center, Vec3f bounds, s16 yaw);
|
||||
+
|
||||
+void debug_box_pos(Vec3f pMin, Vec3f pMax);
|
||||
+void debug_box_pos_rot(Vec3f pMin, Vec3f pMax, s16 yaw);
|
||||
+
|
||||
+void render_debug_boxes(void);
|
||||
+
|
||||
+#endif /* _DEBUG_DRAW_CUBE_H */
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* This file demonstrates how to manipulate display list data from C. *
|
||||
* To use it, #include "../../enhancements/dyn_light.inc.c" and hook it on to a function that is called
|
||||
* once per frame.
|
||||
*/
|
||||
|
||||
#ifndef _DYNLIGHTS_H
|
||||
#define _DYNLIGHTS_H
|
||||
|
||||
#include "../src/game/area.h" /* Get level info */
|
||||
#include "../src/game/level_update.h" /* gMarioState */
|
||||
#include "../src/game/memory.h"
|
||||
#include "../src/game/print.h"
|
||||
|
||||
/*
|
||||
* Common values for shading, which are manipulated with the SHADE_* defines
|
||||
* in combination with the set_mario_shade_light function.
|
||||
*/
|
||||
|
||||
/* Shading levels for Mario */
|
||||
|
||||
#define SHADE_100 1
|
||||
#define SHADE_75 0.75
|
||||
#define SHADE_50 0.50
|
||||
#define SHADE_25 0.25
|
||||
|
||||
/* Mario light values */
|
||||
|
||||
#define VAL1 0x7F
|
||||
#define VAL2 0x39
|
||||
#define VAL3 0X0E
|
||||
#define VAL4 0x07
|
||||
#define VAL5 0x3C
|
||||
#define VAL6 0x60
|
||||
#define VAL7 0x03
|
||||
|
||||
/* Custom structs used for setting light values. */
|
||||
|
||||
typedef struct /* Normal shade lights */
|
||||
{
|
||||
unsigned char byte[7];
|
||||
} Light2;
|
||||
|
||||
typedef struct /* Ambient lights */
|
||||
{
|
||||
unsigned char byte[15];
|
||||
} Light3;
|
||||
|
||||
/* ! GLABEL THESE: actors/mario/model.s ! */
|
||||
|
||||
extern Light2 *mario_amb_light_group1;
|
||||
extern Light2 *mario_amb_light_group2;
|
||||
extern Light2 *mario_amb_light_group3;
|
||||
extern Light2 *mario_amb_light_group4;
|
||||
extern Light2 *mario_amb_light_group6;
|
||||
|
||||
/* OPTIONAL: Add level lights here--just like Mario they have to be glabel'd. */
|
||||
|
||||
/* Used to set mario's shading to one of the four predetermined modes. */
|
||||
|
||||
void set_mario_shade_light(f32 mode) {
|
||||
Light2 *ptr;
|
||||
ptr = segmented_to_virtual(&mario_amb_light_group1);
|
||||
ptr->byte[2] = VAL1 * mode;
|
||||
ptr->byte[6] = VAL1 * mode;
|
||||
ptr = segmented_to_virtual(&mario_amb_light_group2);
|
||||
ptr->byte[0] = VAL1 * mode;
|
||||
ptr->byte[4] = VAL1 * mode;
|
||||
ptr = segmented_to_virtual(&mario_amb_light_group3);
|
||||
ptr->byte[0] = VAL1 * mode;
|
||||
ptr->byte[1] = VAL1 * mode;
|
||||
ptr->byte[2] = VAL1 * mode;
|
||||
ptr->byte[4] = VAL1 * mode;
|
||||
ptr->byte[5] = VAL1 * mode;
|
||||
ptr->byte[6] = VAL1 * mode;
|
||||
ptr = segmented_to_virtual(&mario_amb_light_group4);
|
||||
ptr->byte[0] = VAL2 * mode;
|
||||
ptr->byte[1] = VAL3 * mode;
|
||||
ptr->byte[2] = VAL4 * mode;
|
||||
ptr->byte[4] = VAL2 * mode;
|
||||
ptr->byte[5] = VAL3 * mode;
|
||||
ptr->byte[6] = VAL4 * mode;
|
||||
ptr = segmented_to_virtual(&mario_amb_light_group6);
|
||||
ptr->byte[0] = VAL2 * mode;
|
||||
ptr->byte[1] = VAL7 * mode;
|
||||
ptr->byte[4] = VAL2 * mode;
|
||||
ptr->byte[5] = VAL7 * mode;
|
||||
}
|
||||
|
||||
/* Creates a point light with its origin at x, y, and z. */
|
||||
|
||||
void point_light(s16 x, s16 y, s16 z, s16 size, f32 shade, s16 size1, f32 shade1) {
|
||||
/* Outer region */
|
||||
|
||||
if (gMarioState->pos[0] >= (x - size1) && gMarioState->pos[0] <= x + size1
|
||||
&& gMarioState->pos[1] >= (y - size1) && gMarioState->pos[1] <= y + size1
|
||||
&& gMarioState->pos[2] >= (z - size1) && gMarioState->pos[2] <= z + size1) {
|
||||
set_mario_shade_light(shade1);
|
||||
}
|
||||
|
||||
/* Inner region */
|
||||
|
||||
if (gMarioState->pos[0] >= (x - size) && gMarioState->pos[0] <= x + size
|
||||
&& gMarioState->pos[1] >= (y - size) && gMarioState->pos[1] <= y + size
|
||||
&& gMarioState->pos[2] >= (z - size) && gMarioState->pos[2] <= z + size) {
|
||||
set_mario_shade_light(shade);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets up the automatic shading for levels and sets up point lights.
|
||||
* BBH is already done for you.
|
||||
*/
|
||||
|
||||
void set_level_shading(void) {
|
||||
#ifdef DEBUG
|
||||
print_text_fmt_int(40, 80, "AREA: %d", gCurrAreaIndex);
|
||||
print_text_fmt_int(40, 60, "X: %d", gMarioState->pos[0]);
|
||||
print_text_fmt_int(40, 40, "Y: %d", gMarioState->pos[1]);
|
||||
print_text_fmt_int(40, 20, "Z: %d", gMarioState->pos[2]);
|
||||
#endif
|
||||
switch (gCurrLevelNum) {
|
||||
case LEVEL_BBH:
|
||||
set_mario_shade_light(SHADE_25);
|
||||
|
||||
point_light(200, 0, 2300, 200, SHADE_100, 400, SHADE_50);
|
||||
point_light(1000, 0, 2300, 200, SHADE_100, 400, SHADE_50);
|
||||
|
||||
point_light(200, 0, 1500, 150, SHADE_75, 200, SHADE_50);
|
||||
point_light(200, 0, 700, 150, SHADE_75, 200, SHADE_50);
|
||||
|
||||
point_light(450, 0, 300, 150, SHADE_75, 200, SHADE_50);
|
||||
point_light(1550, 0, 300, 150, SHADE_75, 200, SHADE_50);
|
||||
point_light(1800, 0, 1500, 150, SHADE_75, 200, SHADE_50);
|
||||
|
||||
point_light(1500, 0, 1750, 100, SHADE_50, 150, SHADE_25); /* Window moonlight */
|
||||
break;
|
||||
case LEVEL_HMC:
|
||||
set_mario_shade_light(SHADE_25);
|
||||
break;
|
||||
case LEVEL_CASTLE:
|
||||
set_mario_shade_light(SHADE_75);
|
||||
if (gCurrAreaIndex == 3) {
|
||||
set_mario_shade_light(SHADE_50);
|
||||
}
|
||||
break;
|
||||
case LEVEL_SSL:
|
||||
set_mario_shade_light(SHADE_100);
|
||||
if (gCurrAreaIndex == 2 || gCurrAreaIndex == 3) { /* Both pyramid areas */
|
||||
set_mario_shade_light(SHADE_25);
|
||||
}
|
||||
break;
|
||||
case LEVEL_JRB:
|
||||
case LEVEL_DDD:
|
||||
case LEVEL_SA:
|
||||
set_mario_shade_light(SHADE_50);
|
||||
break;
|
||||
case LEVEL_CCM:
|
||||
case LEVEL_SL:
|
||||
case LEVEL_PSS:
|
||||
set_mario_shade_light(SHADE_75);
|
||||
if (gCurrAreaIndex == 2) {
|
||||
set_mario_shade_light(SHADE_50);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
set_mario_shade_light(SHADE_100);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Framerate counter
|
||||
*
|
||||
* Calculates the game's current framerate by using osGetTime() and
|
||||
* prints it out on the lower left side of the screen.
|
||||
*
|
||||
* HOW TO USE:
|
||||
*
|
||||
* Add this include statement to the top of game.c
|
||||
*
|
||||
* #include "../../enhancements/fps.inc.c"
|
||||
*
|
||||
* Then, at the end of the while(1) loop in the function thread5_game_loop()
|
||||
* add the function call render_fps()
|
||||
*
|
||||
* That's it! Build the rom file and press L when the game boots up
|
||||
* to toggle the FPS counter.
|
||||
*/
|
||||
|
||||
// SDK states that 1 cycle takes about 21.33 nanoseconds
|
||||
#define SECONDS_PER_CYCLE 0.00000002133f
|
||||
|
||||
#define FPS_COUNTER_X_POS 24
|
||||
#define FPS_COUNTER_Y_POS 190
|
||||
|
||||
OSTime gLastOSTime = 0;
|
||||
float gFrameTime = 0.0f;
|
||||
u16 gFrames = 0;
|
||||
u16 gFPS = 0;
|
||||
u8 gRenderFPS = FALSE;
|
||||
|
||||
void calculate_frameTime_from_OSTime(OSTime diff) {
|
||||
gFrameTime += diff * SECONDS_PER_CYCLE;
|
||||
gFrames++;
|
||||
}
|
||||
|
||||
void render_fps(void) {
|
||||
// Toggle rendering framerate with the L button.
|
||||
if (gPlayer1Controller->buttonPressed & L_TRIG) {
|
||||
gRenderFPS ^= 1;
|
||||
}
|
||||
|
||||
if (gRenderFPS) {
|
||||
OSTime newTime = osGetTime();
|
||||
|
||||
calculate_frameTime_from_OSTime(newTime - gLastOSTime);
|
||||
|
||||
// If frame time is longer or equal to a second, update FPS counter.
|
||||
if (gFrameTime >= 1.0f) {
|
||||
gFPS = gFrames;
|
||||
gFrames = 0;
|
||||
gFrameTime -= 1.0f;
|
||||
}
|
||||
|
||||
print_text_fmt_int(FPS_COUNTER_X_POS, FPS_COUNTER_Y_POS, "FPS %d", gFPS);
|
||||
|
||||
gLastOSTime = newTime;
|
||||
}
|
||||
}
|
||||
60
enhancements/fps.patch
Normal file
60
enhancements/fps.patch
Normal file
@@ -0,0 +1,60 @@
|
||||
diff --git a/src/game/game.c b/src/game/game.c
|
||||
index ad800839..4a37549b 100644
|
||||
--- a/src/game/game.c
|
||||
+++ b/src/game/game.c
|
||||
@@ -52,6 +52,47 @@ struct DemoInput *gCurrDemoInput = NULL; // demo input sequence
|
||||
u16 gDemoInputListID = 0;
|
||||
struct DemoInput gRecordedDemoInput = { 0 }; // possibly removed in EU. TODO: Check
|
||||
|
||||
+// SDK states that 1 cycle takes about 21.33 nanoseconds
|
||||
+#define SECONDS_PER_CYCLE 0.00000002133f
|
||||
+
|
||||
+#define FPS_COUNTER_X_POS 24
|
||||
+#define FPS_COUNTER_Y_POS 190
|
||||
+
|
||||
+static OSTime gLastOSTime = 0;
|
||||
+static float gFrameTime = 0.0f;
|
||||
+static u16 gFrames = 0;
|
||||
+static u16 gFPS = 0;
|
||||
+static u8 gRenderFPS = FALSE;
|
||||
+
|
||||
+static void calculate_frameTime_from_OSTime(OSTime diff) {
|
||||
+ gFrameTime += diff * SECONDS_PER_CYCLE;
|
||||
+ gFrames++;
|
||||
+}
|
||||
+
|
||||
+static void render_fps(void) {
|
||||
+ // Toggle rendering framerate with the L button.
|
||||
+ if (gPlayer1Controller->buttonPressed & L_TRIG) {
|
||||
+ gRenderFPS ^= 1;
|
||||
+ }
|
||||
+
|
||||
+ if (gRenderFPS) {
|
||||
+ OSTime newTime = osGetTime();
|
||||
+
|
||||
+ calculate_frameTime_from_OSTime(newTime - gLastOSTime);
|
||||
+
|
||||
+ // If frame time is longer or equal to a second, update FPS counter.
|
||||
+ if (gFrameTime >= 1.0f) {
|
||||
+ gFPS = gFrames;
|
||||
+ gFrames = 0;
|
||||
+ gFrameTime -= 1.0f;
|
||||
+ }
|
||||
+
|
||||
+ print_text_fmt_int(FPS_COUNTER_X_POS, FPS_COUNTER_Y_POS, "FPS %d", gFPS);
|
||||
+
|
||||
+ gLastOSTime = newTime;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
// this function records distinct inputs over a 255-frame interval to RAM locations and was likely
|
||||
// used to record the demo sequences seen in the final game. This function is unused.
|
||||
static void record_demo(void) {
|
||||
@@ -334,5 +375,7 @@ void thread5_game_loop(UNUSED void *arg) {
|
||||
// amount of free space remaining.
|
||||
print_text_fmt_int(180, 20, "BUF %d", gGfxPoolEnd - (u8 *) gDisplayListHead);
|
||||
}
|
||||
+
|
||||
+ render_fps();
|
||||
}
|
||||
}
|
||||
312
enhancements/ique_support.patch
Normal file
312
enhancements/ique_support.patch
Normal file
@@ -0,0 +1,312 @@
|
||||
diff --git a/include/PR/console_type.h b/include/PR/console_type.h
|
||||
new file mode 100644
|
||||
index 00000000..e60550ab
|
||||
--- /dev/null
|
||||
+++ b/include/PR/console_type.h
|
||||
@@ -0,0 +1,7 @@
|
||||
+enum ConsoleType {
|
||||
+ CONSOLE_N64,
|
||||
+ CONSOLE_IQUE
|
||||
+};
|
||||
+
|
||||
+extern enum ConsoleType gConsoleType;
|
||||
+extern enum ConsoleType get_console_type(void);
|
||||
diff --git a/lib/asm/skGetId.s b/lib/asm/skGetId.s
|
||||
new file mode 100644
|
||||
index 00000000..8fb4c449
|
||||
--- /dev/null
|
||||
+++ b/lib/asm/skGetId.s
|
||||
@@ -0,0 +1,18 @@
|
||||
+# Code by stuckpixel
|
||||
+
|
||||
+.set noreorder
|
||||
+.set gp=64
|
||||
+
|
||||
+.include "macros.inc"
|
||||
+
|
||||
+glabel skGetId
|
||||
+ li $v0, 0
|
||||
+ li $t0, 0xA4300014
|
||||
+ lw $t1, 0x00($t0)
|
||||
+ nop
|
||||
+ jr $ra
|
||||
+ nop
|
||||
+ nop
|
||||
+ nop
|
||||
+ nop
|
||||
+ nop
|
||||
diff --git a/lib/src/__osViSwapContext.c b/lib/src/__osViSwapContext.c
|
||||
index d7741994..9aced7cf 100644
|
||||
--- a/lib/src/__osViSwapContext.c
|
||||
+++ b/lib/src/__osViSwapContext.c
|
||||
@@ -52,7 +52,9 @@ void __osViSwapContext() {
|
||||
HW_REG(VI_INTR_REG, u32) = s0->fldRegs[field].vIntr;
|
||||
HW_REG(VI_X_SCALE_REG, u32) = s1->unk20;
|
||||
HW_REG(VI_Y_SCALE_REG, u32) = s1->unk2c;
|
||||
- HW_REG(VI_CONTROL_REG, u32) = s1->features;
|
||||
+ /* Make sure bit 13 is cleared. Otherwise, graphics will be corrupted on
|
||||
+ * iQue Player. This has no effect on N64. */
|
||||
+ HW_REG(VI_CONTROL_REG, u32) = s1->features & ~(1 << 13);
|
||||
D_80334914 = D_80334910;
|
||||
D_80334910 = s1;
|
||||
*D_80334914 = *D_80334910;
|
||||
diff --git a/lib/src/consoleType.c b/lib/src/consoleType.c
|
||||
new file mode 100644
|
||||
index 00000000..ef08d1ef
|
||||
--- /dev/null
|
||||
+++ b/lib/src/consoleType.c
|
||||
@@ -0,0 +1,12 @@
|
||||
+#include "libultra_internal.h"
|
||||
+#include <PR/console_type.h>
|
||||
+
|
||||
+enum ConsoleType gConsoleType;
|
||||
+
|
||||
+void skGetId(u32 *out);
|
||||
+
|
||||
+enum ConsoleType get_console_type(void) {
|
||||
+ u32 id = 0;
|
||||
+ skGetId(&id);
|
||||
+ return (id == 0) ? CONSOLE_N64 : CONSOLE_IQUE;
|
||||
+}
|
||||
diff --git a/lib/src/osEepromProbe.c b/lib/src/osEepromProbe.c
|
||||
index d550b846..bbaf2bcc 100644
|
||||
--- a/lib/src/osEepromProbe.c
|
||||
+++ b/lib/src/osEepromProbe.c
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "libultra_internal.h"
|
||||
+#include <PR/console_type.h>
|
||||
|
||||
// TODO: merge with osEepromWrite
|
||||
typedef struct {
|
||||
@@ -13,11 +14,23 @@ s32 osEepromProbe(OSMesgQueue *mq) {
|
||||
unkStruct sp18;
|
||||
|
||||
__osSiGetAccess();
|
||||
- status = __osEepStatus(mq, &sp18);
|
||||
- if (status == 0 && (sp18.unk00 & 0x8000) != 0) {
|
||||
- status = 1;
|
||||
- } else {
|
||||
- status = 0;
|
||||
+ if (gConsoleType == CONSOLE_N64) {
|
||||
+ status = __osEepStatus(mq, &sp18);
|
||||
+ if (status == 0 && (sp18.unk00 & 0x8000) != 0) {
|
||||
+ status = 1;
|
||||
+ } else {
|
||||
+ status = 0;
|
||||
+ }
|
||||
+ } else if (gConsoleType == CONSOLE_IQUE) {
|
||||
+ s32 __osBbEepromSize = * (s32*) 0x80000360;
|
||||
+
|
||||
+ if (__osBbEepromSize == 0x200) {
|
||||
+ status = 1;
|
||||
+ }
|
||||
+
|
||||
+ if (__osBbEepromSize == 0x800) {
|
||||
+ status = 2;
|
||||
+ }
|
||||
}
|
||||
__osSiRelAccess();
|
||||
return status;
|
||||
diff --git a/lib/src/osEepromRead.c b/lib/src/osEepromRead.c
|
||||
index 905eff74..23f34dd5 100644
|
||||
--- a/lib/src/osEepromRead.c
|
||||
+++ b/lib/src/osEepromRead.c
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "libultra_internal.h"
|
||||
+#include <PR/console_type.h>
|
||||
|
||||
extern u32 D_80365E00[15];
|
||||
extern u32 D_80365E3C;
|
||||
@@ -44,33 +45,44 @@ s32 osEepromRead(OSMesgQueue *mq, u8 address, u8 *buffer) {
|
||||
return -1;
|
||||
}
|
||||
__osSiGetAccess();
|
||||
- sp34 = __osEepStatus(mq, &sp28);
|
||||
- if (sp34 != 0 || sp28.unk00 != 0x8000) {
|
||||
+ if (gConsoleType == CONSOLE_N64) {
|
||||
+ sp34 = __osEepStatus(mq, &sp28);
|
||||
+ if (sp34 != 0 || sp28.unk00 != 0x8000) {
|
||||
|
||||
- return 8;
|
||||
- }
|
||||
- while (sp28.unk02 & 0x80) {
|
||||
- __osEepStatus(mq, &sp28);
|
||||
- }
|
||||
- __osPackEepReadData(address);
|
||||
- sp34 = __osSiRawStartDma(OS_WRITE, &D_80365E00);
|
||||
- osRecvMesg(mq, NULL, OS_MESG_BLOCK);
|
||||
- for (sp30 = 0; sp30 < 0x10; sp30++) {
|
||||
- (D_80365E00)[sp30] = 255;
|
||||
- }
|
||||
- D_80365E3C = 0;
|
||||
- sp34 = __osSiRawStartDma(OS_READ, D_80365E00);
|
||||
- D_80365D20 = 4;
|
||||
- osRecvMesg(mq, NULL, OS_MESG_BLOCK);
|
||||
- for (sp30 = 0; sp30 < 4; sp30++) {
|
||||
- sp2c++;
|
||||
- }
|
||||
- sp20 = *(unkStruct2 *) sp2c;
|
||||
- sp34 = (sp20.unk01 & 0xc0) >> 4;
|
||||
- if (sp34 == 0) {
|
||||
- for (sp30 = 0; sp30 < 8; sp30++) {
|
||||
- *buffer++ = ((u8 *) &sp20.unk04)[sp30];
|
||||
+ return 8;
|
||||
+ }
|
||||
+ while (sp28.unk02 & 0x80) {
|
||||
+ __osEepStatus(mq, &sp28);
|
||||
+ }
|
||||
+ __osPackEepReadData(address);
|
||||
+ sp34 = __osSiRawStartDma(OS_WRITE, &D_80365E00);
|
||||
+ osRecvMesg(mq, NULL, OS_MESG_BLOCK);
|
||||
+ for (sp30 = 0; sp30 < 0x10; sp30++) {
|
||||
+ (D_80365E00)[sp30] = 255;
|
||||
}
|
||||
+ D_80365E3C = 0;
|
||||
+ sp34 = __osSiRawStartDma(OS_READ, D_80365E00);
|
||||
+ D_80365D20 = 4;
|
||||
+ osRecvMesg(mq, NULL, OS_MESG_BLOCK);
|
||||
+ for (sp30 = 0; sp30 < 4; sp30++) {
|
||||
+ sp2c++;
|
||||
+ }
|
||||
+ sp20 = *(unkStruct2 *) sp2c;
|
||||
+ sp34 = (sp20.unk01 & 0xc0) >> 4;
|
||||
+ if (sp34 == 0) {
|
||||
+ for (sp30 = 0; sp30 < 8; sp30++) {
|
||||
+ *buffer++ = ((u8 *) &sp20.unk04)[sp30];
|
||||
+ }
|
||||
+ }
|
||||
+ } else if (gConsoleType == CONSOLE_IQUE) {
|
||||
+ u8 *__osBbEepromAddress = * (u8**) 0x8000035C;
|
||||
+ s32 i;
|
||||
+
|
||||
+ for (i = 0; i < 8; i++) {
|
||||
+ buffer[i] = __osBbEepromAddress[(address << 3) + i];
|
||||
+ }
|
||||
+
|
||||
+ sp34 = 0;
|
||||
}
|
||||
__osSiRelAccess();
|
||||
return sp34;
|
||||
diff --git a/lib/src/osEepromWrite.c b/lib/src/osEepromWrite.c
|
||||
index 71d0b7d6..c855cc20 100644
|
||||
--- a/lib/src/osEepromWrite.c
|
||||
+++ b/lib/src/osEepromWrite.c
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "libultra_internal.h"
|
||||
#include "osContInternal.h"
|
||||
+#include <PR/console_type.h>
|
||||
|
||||
u32 D_80365E00[0x3c >> 2];
|
||||
u32 D_80365E3C;
|
||||
@@ -46,36 +47,47 @@ s32 osEepromWrite(OSMesgQueue *mq, u8 address, u8 *buffer) {
|
||||
}
|
||||
|
||||
__osSiGetAccess();
|
||||
- sp34 = __osEepStatus(mq, &sp1c);
|
||||
+ if (gConsoleType == CONSOLE_N64) {
|
||||
+ sp34 = __osEepStatus(mq, &sp1c);
|
||||
|
||||
- if (sp34 != 0 || sp1c.unk00 != 0x8000) {
|
||||
- return 8;
|
||||
- }
|
||||
+ if (sp34 != 0 || sp1c.unk00 != 0x8000) {
|
||||
+ return 8;
|
||||
+ }
|
||||
|
||||
- while (sp1c.unk02 & 0x80) {
|
||||
- __osEepStatus(mq, &sp1c);
|
||||
- }
|
||||
+ while (sp1c.unk02 & 0x80) {
|
||||
+ __osEepStatus(mq, &sp1c);
|
||||
+ }
|
||||
|
||||
- __osPackEepWriteData(address, buffer);
|
||||
+ __osPackEepWriteData(address, buffer);
|
||||
|
||||
- sp34 = __osSiRawStartDma(OS_WRITE, &D_80365E00);
|
||||
- osRecvMesg(mq, NULL, OS_MESG_BLOCK);
|
||||
+ sp34 = __osSiRawStartDma(OS_WRITE, &D_80365E00);
|
||||
+ osRecvMesg(mq, NULL, OS_MESG_BLOCK);
|
||||
|
||||
- for (sp30 = 0; sp30 < 0x10; sp30++) {
|
||||
- (D_80365E00)[sp30] = 255;
|
||||
- }
|
||||
+ for (sp30 = 0; sp30 < 0x10; sp30++) {
|
||||
+ (D_80365E00)[sp30] = 255;
|
||||
+ }
|
||||
|
||||
- D_80365E3C = 0;
|
||||
- sp34 = __osSiRawStartDma(OS_READ, D_80365E00);
|
||||
- D_80365D20 = 5;
|
||||
- osRecvMesg(mq, NULL, OS_MESG_BLOCK);
|
||||
+ D_80365E3C = 0;
|
||||
+ sp34 = __osSiRawStartDma(OS_READ, D_80365E00);
|
||||
+ D_80365D20 = 5;
|
||||
+ osRecvMesg(mq, NULL, OS_MESG_BLOCK);
|
||||
|
||||
- for (sp30 = 0; sp30 < 4; sp30++) {
|
||||
- sp2c++;
|
||||
- }
|
||||
+ for (sp30 = 0; sp30 < 4; sp30++) {
|
||||
+ sp2c++;
|
||||
+ }
|
||||
+
|
||||
+ sp20 = *(unkStruct2 *) sp2c;
|
||||
+ sp34 = (sp20.unk01 & 0xc0) >> 4;
|
||||
+ } else if (gConsoleType == CONSOLE_N64) {
|
||||
+ u8 *__osBbEepromAddress = * (u8**) 0x8000035C;
|
||||
+ s32 i;
|
||||
|
||||
- sp20 = *(unkStruct2 *) sp2c;
|
||||
- sp34 = (sp20.unk01 & 0xc0) >> 4;
|
||||
+ for (i = 0; i < 8; i++) {
|
||||
+ __osBbEepromAddress[(address << 3) + i] = buffer[i];
|
||||
+ }
|
||||
+
|
||||
+ sp34 = 0;
|
||||
+ }
|
||||
__osSiRelAccess();
|
||||
return sp34;
|
||||
}
|
||||
diff --git a/lib/src/osInitialize.c b/lib/src/osInitialize.c
|
||||
index 0b9f7128..660d1991 100644
|
||||
--- a/lib/src/osInitialize.c
|
||||
+++ b/lib/src/osInitialize.c
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "libultra_internal.h"
|
||||
#include "hardware.h"
|
||||
#include <macros.h>
|
||||
+#include <PR/console_type.h>
|
||||
|
||||
#define PIF_ADDR_START (void *) 0x1FC007FC
|
||||
|
||||
@@ -46,6 +47,7 @@ void osInitialize(void) {
|
||||
UNUSED u32 eu_sp30;
|
||||
#endif
|
||||
UNUSED u32 sp2c;
|
||||
+ gConsoleType = get_console_type();
|
||||
D_80365CD0 = TRUE;
|
||||
__osSetSR(__osGetSR() | 0x20000000);
|
||||
__osSetFpcCsr(0x01000800);
|
||||
diff --git a/sm64.ld b/sm64.ld
|
||||
index 59a5a2a6..c8976649 100755
|
||||
--- a/sm64.ld
|
||||
+++ b/sm64.ld
|
||||
@@ -256,6 +256,8 @@ SECTIONS
|
||||
BUILD_DIR/libultra.a:func_802F7140.o(.text)
|
||||
BUILD_DIR/libultra.a:func_802F71A0.o(.text)
|
||||
BUILD_DIR/libultra.a:func_802F71F0.o(.text)
|
||||
+ BUILD_DIR/libultra.a:consoleType.o(.text)
|
||||
+ BUILD_DIR/libultra.a:skGetId.o(.text)
|
||||
|
||||
BUILD_DIR/lib/rsp.o(.text);
|
||||
|
||||
@@ -369,6 +371,8 @@ SECTIONS
|
||||
BUILD_DIR/libultra.a:__osGetCause.o(.text);
|
||||
BUILD_DIR/libultra.a:__osAtomicDec.o(.text);
|
||||
BUILD_DIR/libultra.a:guLookAtRef.o(.text); /* Fast3DEX2 only */
|
||||
+ BUILD_DIR/libultra.a:consoleType.o(.text);
|
||||
+ BUILD_DIR/libultra.a:skGetId.o(.text);
|
||||
BUILD_DIR/lib/rsp.o(.text);
|
||||
#endif
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
# iQue Player Support Enhancement
|
||||
|
||||
This enhancement allows the same ROM to work on both the N64 and the iQue Player.
|
||||
|
||||
## Instructions
|
||||
|
||||
### Fix video corruption
|
||||
|
||||
In `lib/src/__osViSwapContext.c`, change the line
|
||||
```c
|
||||
HW_REG(VI_CONTROL_REG, u32) = s1->features;
|
||||
```
|
||||
to
|
||||
```c
|
||||
HW_REG(VI_CONTROL_REG, u32) = s1->features & ~(1 << 13);
|
||||
```
|
||||
This has no effect on the N64, but prevents video corruption on iQue. Games will be unplayable on iQue unless you do this modification.
|
||||
|
||||
### Add console detection
|
||||
|
||||
Copy `enhancements/ique_support/skGetId.s` into `lib/asm/`.
|
||||
Copy `enhancements/ique_support/consoleType.c` into `lib/src/`.
|
||||
Copy `enhancements/ique_support/console_type.h` into `include/PR/`.
|
||||
|
||||
Add the line `#include <PR/console_type.h>` to the bottom of the block of `#include`s in `include/ultra64.h`.
|
||||
Add the line `gConsoleType = get_console_type();` as the first line of code in `osInitialize()` in `lib/src/osInitialize.c` (i.e. right before the line `D_80365CD0 = TRUE;`)
|
||||
|
||||
### Patch libultra EEPROM functions
|
||||
|
||||
Console detection needs to be added for this modification.
|
||||
|
||||
In `lib/src/osEepromRead.c`, `lib/src/osEepromWrite.c`, and `lib/src/osEepromProbe.c`:
|
||||
|
||||
Add
|
||||
```c
|
||||
if (gConsoleType == CONSOLE_N64) {
|
||||
```
|
||||
directly after the `__osSiGetAccess` call.
|
||||
|
||||
Add
|
||||
```c
|
||||
} else if (gConsoleType == CONSOLE_IQUE) {
|
||||
#include "../../enhancements/ique_support/FILENAME.inc.c"
|
||||
}
|
||||
```
|
||||
directly before the `__osSiRelAccess` call; replace `FILENAME` with the filename, i.e. if `lib/src/osEepromRead.c` put `#include "../../enhancements/ique_support/osEepromRead.inc.c"`.
|
||||
@@ -1,11 +0,0 @@
|
||||
#include "libultra_internal.h"
|
||||
|
||||
enum ConsoleType gConsoleType;
|
||||
|
||||
void skGetId(u32 *out);
|
||||
|
||||
enum ConsoleType get_console_type(void) {
|
||||
u32 id = 0;
|
||||
skGetId(&id);
|
||||
return (id == 0) ? CONSOLE_N64 : CONSOLE_IQUE;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
enum ConsoleType {
|
||||
CONSOLE_N64,
|
||||
CONSOLE_IQUE
|
||||
};
|
||||
|
||||
extern enum ConsoleType gConsoleType;
|
||||
extern enum ConsoleType get_console_type(void);
|
||||
@@ -1,9 +0,0 @@
|
||||
s32 __osBbEepromSize = * (s32*) 0x80000360;
|
||||
|
||||
if (__osBbEepromSize == 0x200) {
|
||||
status = 1;
|
||||
}
|
||||
|
||||
if (__osBbEepromSize == 0x800) {
|
||||
status = 2;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
u8 *__osBbEepromAddress = * (u8**) 0x8000035C;
|
||||
s32 i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
buffer[i] = __osBbEepromAddress[(address << 3) + i];
|
||||
}
|
||||
|
||||
sp34 = 0;
|
||||
@@ -1,8 +0,0 @@
|
||||
u8 *__osBbEepromAddress = * (u8**) 0x8000035C;
|
||||
s32 i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
__osBbEepromAddress[(address << 3) + i] = buffer[i];
|
||||
}
|
||||
|
||||
sp34 = 0;
|
||||
@@ -1,18 +0,0 @@
|
||||
# Code by stuckpixel
|
||||
|
||||
.set noreorder
|
||||
.set gp=64
|
||||
|
||||
.include "macros.inc"
|
||||
|
||||
glabel skGetId
|
||||
li $v0, 0
|
||||
li $t0, 0xA4300014
|
||||
lw $t1, 0x00($t0)
|
||||
nop
|
||||
jr $ra
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user