You've already forked HackerSM64
mirror of
https://github.com/HackerN64/HackerSM64.git
synced 2026-01-21 10:35:32 -08:00
Compare commits
18 Commits
old/2.0.0
...
develop/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba3717e4a6 | ||
|
|
7c0582f7ce | ||
|
|
37214d6059 | ||
|
|
30a75bb1d8 | ||
|
|
ef10d68513 | ||
|
|
c187386e57 | ||
|
|
ef3b7e6347 | ||
|
|
931a5fea31 | ||
|
|
a0b93715c1 | ||
|
|
4ef1d81a2f | ||
|
|
1314fd8607 | ||
|
|
239b98ba62 | ||
|
|
09a69f9186 | ||
|
|
b1ac2a174e | ||
|
|
08614793fd | ||
|
|
dd7fdcdfb9 | ||
|
|
e42dfd295c | ||
|
|
a94e58e636 |
1
.vscode/c_cpp_properties.json
vendored
1
.vscode/c_cpp_properties.json
vendored
@@ -6,7 +6,6 @@
|
||||
"${workspaceFolder}/include/n64/**",
|
||||
"${workspaceFolder}/include/**",
|
||||
"${workspaceFolder}/textures/**",
|
||||
"${workspaceFolder}/build/us_n64/**",
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"forcedInclude": [
|
||||
|
||||
66
README.md
66
README.md
@@ -1,43 +1,43 @@
|
||||
#  HackerSM64 
|
||||
|
||||
**AFTER CLONING THE REPO, CHECK OUT THE `include/config` FOLDER BEFORE ANYTHING ELSE! THERE'S A LOT OF STUFF IN THIS REPO THAT CAN BE TOGGLED THERE.**
|
||||
**AFTER CLONING THE REPO, CHECK OUT THE `include/config.h` FILE BEFORE ANYTHING ELSE! IT THERE'S A LOT OF STUFF IN THIS REPO THAT CAN BE TOGGLED THERE.**
|
||||
|
||||
HackerSM64 now has a discord server! https://discord.gg/brETAakcXr
|
||||
|
||||
This repo requires BOTH a US ROM and a JP ROM in order to build. Place baserom.us.z64 in the repo as usual and ALSO include baserom.jp.z64.
|
||||
This repo needs BOTH an US ROM and a JP ROM in order to build. Place baserom.us.z64 in the repo as usual and ALSO place baserom.jp.z64
|
||||
|
||||
This repo needs gcc in order to be able to build it. To install it, run `sudo apt install gcc-mips-linux-gnu`
|
||||
|
||||
This is a fork of the ultrasm64 repo by CrashOveride which includes the following commonly used patches (patches marked with `*` are toggleable in the config files):
|
||||
This is a fork of the ultrasm64 repo by CrashOveride which includes the following commonly used patches (patches marked with `*` are toggleable in `config.h`):
|
||||
|
||||
**Credits**
|
||||
- **ArcticJaguar725**: Most audio configuration and layout changes, colored ia4 text, floombas, various bugfixes, and more
|
||||
- **ArcticJaguar725**: Most audio configuration and layout changes, colored ia4 text, floombas, a bunch of random bugfixes, and more
|
||||
- **CowQuack**: Adjustable skybox sizes, area-specific skybox function
|
||||
- **thecozies**: Water surface types, general maintenance, and time
|
||||
- **MrComit**: General use object defines, JUMP_KICK_FIX, LEDGE_GRABS_CHECK_SLOPE_ANGLE
|
||||
- **aglab2**: Bugfixes (particularly puppycam), refactor stuff
|
||||
- **aglab2**: bugfixes (particularly puppycam), refactor stuff
|
||||
- **someone2639**: math.s and crash screen disam, stack trace, map packing, shiftable segments 2, S2DEX engine
|
||||
- **Arthurtilly**: Platform Displacement 2
|
||||
- **Fazana**: PuppyLib, ucode swapping, audio load time optimisations (with Arctic), general hacker qol improvements, visual debug
|
||||
- **Reonu**: Starting the project/repo, widescreen, reonucam, various defines for hacker QoL
|
||||
- **JoshDuMan**: Decomp guy, general assistance
|
||||
- **Arceveti**: Silhouette, shadow optimisation, better hanging, breath meter
|
||||
- **Fazana**: PuppyLib, ucode swapping, Audio load time optimisations (with Arctic), general hacker qol improvements, visual debug
|
||||
- **Reonu**: Starting the project/repo, widescreen, various defines for hacker QoL
|
||||
- **JoshDuMan**: decomp guy, general assistance
|
||||
- **Arceveti**: silhouette, shadow optimisation, better hanging, breath meter
|
||||
- **axollyon**: Console testing, bugfixes, idea-guying, and had a hand in silhouettes
|
||||
- **Wiseguy**: World scale reimplementation, silhouette, graph node optimisations, instant input patch, cake screen fix, segmented code support, and various optimizations/fixes
|
||||
- **Wiseguy**: silhouette, graph node optimisations, instant input patch, cake screen fix, segmented code support, and various optimizations/fixes
|
||||
- **Kaze**: Graph node optimisations, automatic optimal collision distance
|
||||
- **Pyro Jay**: Texture improvements, repo banner art, some QoL stuff
|
||||
- **CrashOveride**: creating the [ultrasm64](https://github.com/CrashOveride95/ultrasm64) repo
|
||||
- **falcobuster**: Original coordinate overflow fix (world scale), ASM version of extended bounds
|
||||
- **CrashOveride**: creating the `ultrasm64` repo
|
||||
- **falcobuster**: coordinate overflow fix (world scale), ASM version of extended bounds
|
||||
- **anonymous_moose**: porting falco's extended bounds to decomp
|
||||
|
||||
Thanks to Frame#5375 and AloXado320 for also helping with silhouette stuff
|
||||
|
||||
**Lighting Engine by Wiseguy**
|
||||
- Lighting Engine is available on a separate branch ([base/lighting-engine](https://github.com/Reonu/HackerSM64/tree/base/lighting-engine)). Instructions on how to use it are in the readme of that branch.
|
||||
- Lighting Engine is available on a separate branch `(lighting-engine)`. Instructions on how to use it are in the readme of that branch.
|
||||
- Alternatively, the main repo has `Puppylights` available, which is a more lightweight, but limited lighting library intended to be used to modify existing light properties. You can look at `puppylights.c` to find out how to use it.
|
||||
|
||||
**Puppycam**
|
||||
- Puppycam is available on the master branch now, you can toggle it in `config/config_camera.h`. *
|
||||
- Puppycam is available on the master branch now, you can toogle it in config.h. *
|
||||
|
||||
**Collision:**
|
||||
- Slope fix and exposed ceilings fix
|
||||
@@ -45,11 +45,11 @@ Thanks to Frame#5375 and AloXado320 for also helping with silhouette stuff
|
||||
- Jump kick fix *
|
||||
- Configurable wallkick angle, in degrees *
|
||||
- Possibility of disabling BLJs *
|
||||
- Hanging fix (Mario can grab hangable ceilings from any state, instead of only jump or double jump) *
|
||||
- Hanging fix (mario can grab hangable ceilings from any state, instead of only jump or double jump) *
|
||||
- Increased maximum pole length (The game will read bparam1 and bparam2 together as a single value, so you can have a very long pole) *
|
||||
- Platform Displacement 2 by Arthurtilly *
|
||||
- Water Surface Type patch by thecozies
|
||||
- Better wall collision with rounded corners by FramePerfection, merged by Cheezepin & Arceveti
|
||||
- Better Wall Collision With Rounded Corners by FramePerfection, merged by Cheezepin & Arceveti
|
||||
- Automatically calculate the optimal collision distance for an object based on its vertices, by Kaze *
|
||||
|
||||
**Common Hack Changes:**
|
||||
@@ -62,8 +62,8 @@ Thanks to Frame#5375 and AloXado320 for also helping with silhouette stuff
|
||||
- Removed course-specific camera processing *
|
||||
- You can increase the number of frames that you have to perform a firsty *
|
||||
- Ability to set Mario's movement speed when hanging from a ceiling *
|
||||
- Tighter hanging controls (Mario will face the direction of the analog stick directly while hanging from a ceiling) *
|
||||
- reonucam3: custom camera by Reonu. This is included as a .patch file in the enhancements folder, you need to apply it if you want this camera.
|
||||
- Tighter hanging controls (mario will face the direction of the analog stick directly while hanging from a ceiling) *
|
||||
- reonucam3: custom camera by me. This is included as a .patch file in the enhancements folder, you need to apply it if you want this camera.
|
||||
This video shows a rundown of the features: https://youtu.be/TQNkznX9Z3k
|
||||
- Ability to disable Mario getting suck in snow or sand
|
||||
|
||||
@@ -72,34 +72,34 @@ Thanks to Frame#5375 and AloXado320 for also helping with silhouette stuff
|
||||
- Debug mode: prints mario's coordinates, angle and speed, and a FPS counter.
|
||||
- Automatic console/emulator detection. If emulator is detected, LODs are disabled. *
|
||||
- Ability to configure whether there's a 100 coin star at all and how many coins are required to spawn it *
|
||||
- Ability to easily change the warp that EXIT COURSE takes you to via `config/config_menu.h`, or disable it entirely. *
|
||||
- 16 bit model IDs by someone2639. This means you can have up to 65536 models (lol). You can set the maximum number of model IDs in `config/config_game.h`.
|
||||
- Ability to easily change the warp that EXIT COURSE takes you to via config.h, or disable it entirely. *
|
||||
- 16 bit model IDs by someone2639. This means you can have up to 65536 models (lol). You can set the maximum number of model IDs in `config.h`.
|
||||
- Apply_patch.sh improved
|
||||
- Removed the ifdef hell in `file_select.c` and `ingame_menu.c`
|
||||
- Added Blake's custom function for object model stuff: `obj_set_model` and `obj_has_model`
|
||||
- Added function to get the model ID from an object: `obj_get_model_id` (by Arceveti)
|
||||
- The "far" variable is now u16, allowing you to increase the farclip (the max distance at which geometry is rendered). However, when increasing the farclip, make sure to increase the nearclip by the same ratio, or rendering will break on console and LLE plugins.
|
||||
- Many general use defines for object struct members, meant for use in custom object behaviors. Check `include/object_fields.h` for more info on this. (By MrComit)
|
||||
- Many general use defines for object struct members, meant for use in custom object behaviors. Check `object_fields.h` for more info on this. (By MrComit)
|
||||
- Included `actors/group0.c` in `behavior_data.c`
|
||||
- The internal ROM name is now set with a define in `config/config_rom.h` to make it simpler
|
||||
- The internal ROM name is now set with a define in `config.h` to make it simpler
|
||||
- There is a `gIsConsole` variable that is 1 when running on console and 0 when running on emulator. This way you can wrap your code in a console check.
|
||||
- Expanded audio heap allows for a larger concurrent note count and the importing of more m64 sequences and sound banks (By ArcticJaguar725) *
|
||||
- You can set a test level in `config/config_debug.h` in order to boot straight into it, so you can quickly test the level you're working on. *
|
||||
- Allow all surfaces in the game to have a `force` parameter. Activating this doesn't REQUIRE you to set `force` for every surface: If you don't set, it will default to 0x0000 rather than crashing. Increases RAM usage of collision. *
|
||||
- You can set a test level in config.h in order to boot straight into it, so you can quickly test the level you're working on. *
|
||||
- Allows all surfaces in the game to have a `force` parameter. Activating this doesn't REQUIRE you to set `force` for every surface: If you don't set, it will default to 0x0000 rather than crashing. Increases RAM usage of collision. *
|
||||
- The clown font includes the entire English alphabet.
|
||||
- Colored ia4 text support. Format: `"@XXXXXXXX[YOUR TEXT]@--------"` (By ArcticJaguar725)
|
||||
- Example Text: `"@FF0000FFRED @00FF00FFGREEN @0000FFFFBLUE @FFFFFF00INVISIBLE @--------NORMAL"`
|
||||
- NOTE: It is not mandatory to reset the text color with `"@--------"`, but text will need to be recolored each time it scrolls in a dialog box, or the custom color will reset.
|
||||
- Toggle visiblity of collision surfaces and object hitboxes with Visual Surface Debug. `config/config_debug.h` has VISUAL_DEBUG which can be turned on to enable this feature.
|
||||
- Toggle visiblity of collision surfaces and object hitboxes with Visual Surface Debug. `config.h` has VISUAL_DEBUG which can be turned on to enable this feature.
|
||||
- Workaround for infinite death loops caused by using the wrong warp type for death warps. Mario's HP will be restored when being warped to any warp if (and only if) he was warped while dead. *
|
||||
|
||||
**Other Bugfixes:**
|
||||
- Castle music fix (fixes the castle music sometimes triggering after getting a dialog) *
|
||||
- bparam4 fix (the game no longer uses bparam4 to check if an object is Mario and therefore you can safely use it)
|
||||
- Castle music fix (Fixes the castle music sometimes triggering after getting a dialog) *
|
||||
- bparam4 fix (the game no longer uses bparam4 to check if an object is mario and therefore you can safely use it)
|
||||
- Instant warp offset fix (makes the instant warp offset work even when warping to a different area) *
|
||||
- haveyourcake, also known as cake screen fix. Made by Wiseguy and ported/PR'd by Cheezepin
|
||||
- Tree particle fix (Whether a tree uses snow particles or not is decided via the model IDs instead of the course number) *
|
||||
- Adjustable world scale. You can change the geometry scaling of your level, which allow large levels to render correctly on console and LLE emulators while not hurting anything on HLE plugins.
|
||||
- Coordinate overflow fix by falcobuster. Your levels will render correctly on console and LLE emulators even when using 2x or 4x bounds, while not hurting anything on HLE plugins. *This is automatic now, you don't have to set WORLD_SCALE manually.*
|
||||
- A couple vanilla texture fixes
|
||||
- Smoke fix (the smoke texture uses the correct texture format)
|
||||
|
||||
@@ -108,14 +108,14 @@ Thanks to Frame#5375 and AloXado320 for also helping with silhouette stuff
|
||||
- This means that you'll have to do your framebuffer effects on buffer 0 for emulator, but NOT for console. You can use the `gIsConsole` variable to check for console when doing your framebuffer effects.
|
||||
- Widescreen (16:9) support toggleable by pressing `L` in the pause menu. *
|
||||
- S2DEX engine by someone2639! To use it, compile with `make TEXT_ENGINE=s2dex_text_engine` or just set `TEXT_ENGINE` to `s2dex_text_engine` in the makefile.
|
||||
- ia8 (64x64) coins, the vanilla coin texture is upgraded to accomodate. *
|
||||
- ia8 (64x64) 30 FPS coins (Textures by InTheBeef, cleaned up by Arceveti). *
|
||||
- Floombas! Simply just retextured goombas with customizable behaviors (does not overwrite standard goombas). *
|
||||
- ia8 coins (64x64), the vanilla coin texture is upgraded to accomodate. *
|
||||
- ia8 30 FPS (64x64) coins (Textures by InTheBeef, cleaned up by Arceveti). *
|
||||
- Floombas! Simply just retextured goombas with custom behaviors (does not overwrite standard goombas). *
|
||||
- HD texture support for intro splash screen (with floombas if enabled). *
|
||||
- Mario's silhouette is shown when behind most surfaces (By Frame#5375, Axollyon, AloXado320, Wiseguy, Arceveti) *
|
||||
- Skybox size modifier. You can have 2x, 3x and 4x size skyboxes (you can select the skybox size in `config/config_graphics.h`.) Please note that this might affect console performance, especially 4x mode. 2x or 3x mode is recommended if aiming for console. By CowQuack *
|
||||
- Skybox size modifier. You can have 2x, 3x and 4x size skyboxes (you can select the skybox size in `config.h`.) Please note that this might affect console performance, especially 4x mode. 2x or 3x mode is recommended if aiming for console. By CowQuack *
|
||||
- You can set the black border size to different values for console and emulator. It's set to 0 by default for both. *
|
||||
- This repo supports a much better implementation of reverb over vanilla's fake echo reverb. Great for caves or eerie levels, as well as just a better audio experience in general. See `audio/synthesis.c` for more details and configuration info. (By ArcticJaguar725) *
|
||||
- This repo supports much better implementation of reverb over vanilla's fake echo reverb. Great for caves or eerie levels, as well as just a better audio experience in general. See `audio/synthesis.c` for more configuration info. (By ArcticJaguar725) *
|
||||
- Fazana's "puppyprint" text engine. *
|
||||
- Use `print_small_text` to print normal text. The two last params are aligment and how many characters to print (-1 means PRINT_ALL).
|
||||
- Use `render_multi_image` to draw large texture rectangles consisting of multiple images on the screen.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
v2.0.0
|
||||
48
asm/math.s
Normal file
48
asm/math.s
Normal file
@@ -0,0 +1,48 @@
|
||||
# assembler directives
|
||||
.set gp=64
|
||||
|
||||
.include "macros.inc"
|
||||
|
||||
|
||||
.section .text, "ax"
|
||||
|
||||
.balign 32
|
||||
glabel mtxf_to_mtx_asm
|
||||
li $v0, 1
|
||||
li.s $f4, 65536.0
|
||||
1:
|
||||
lwc1 $f0, ($a1)
|
||||
lwc1 $f2, 0x04($a1)
|
||||
|
||||
andi $t0, $v0, (1 << 1)
|
||||
mul.s $f0, $f4
|
||||
trunc.w.s $f0, $f0
|
||||
mfc1 $t3, $f0
|
||||
addiu $a1, 8
|
||||
sra $t4, $t3, 16
|
||||
sh $t4, 0x00($a0)
|
||||
sh $t3, 0x20($a0)
|
||||
|
||||
addiu $v0, 2
|
||||
bnez $t0, storezero
|
||||
|
||||
mul.s $f2, $f4
|
||||
trunc.w.s $f2, $f2
|
||||
mfc1 $t3, $f2
|
||||
sra $t4, $t3, 16
|
||||
sh $t4, 0x02($a0)
|
||||
sh $t3, 0x22($a0)
|
||||
loopend:
|
||||
.set noreorder
|
||||
bnel $v0, 0x11, 1b
|
||||
addiu $a0, 4
|
||||
.set reorder
|
||||
|
||||
li $t1, 1
|
||||
sh $t1, 0x02($a0)
|
||||
jr $ra
|
||||
storezero:
|
||||
sh $zero, 0x02($a0)
|
||||
sh $zero, 0x22($a0)
|
||||
j loopend
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
53
enhancements/README.md
Normal file
53
enhancements/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Super Mario 64 Enhancements
|
||||
|
||||
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.
|
||||
184
enhancements/RecordDemo.js
Normal file
184
enhancements/RecordDemo.js
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* This is a companion file for the record_demo.inc.c enhancement.
|
||||
*
|
||||
* You will need the PJ64 javascript API to get this to work, so
|
||||
* you should download a nightly build from here (Windows only atm):
|
||||
* https://www.pj64-emu.com/nightly-builds
|
||||
*
|
||||
* Place this .js file into the /Scripts/ folder in the PJ64 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 PJ64 directory.
|
||||
*/
|
||||
|
||||
var RAM_SIZE = 4 * 1048576 // 4 MB
|
||||
|
||||
// Get a copy of the first 4MB of memory.
|
||||
var RAM = mem.getblock(0x80000000, RAM_SIZE)
|
||||
|
||||
// Create SM64_DEMOS Directory if it already doesn't exist.
|
||||
fs.mkdir("SM64_DEMOS/");
|
||||
|
||||
// string "DEMORECVARS"
|
||||
var pattern = [0x44, 0x45, 0x4D, 0x4F, 0x52, 0x45, 0x43, 0x56, 0x41, 0x52, 0x53, 0x00]
|
||||
|
||||
var matches = find_matches_fast(pattern)
|
||||
|
||||
if(matches.length > 1) {
|
||||
console.log('Error: More than 1 instance of "DEMORECVARS" was found. Abort!')
|
||||
} else if(matches.length < 1) {
|
||||
console.log('Error: No instance of "DEMORECVARS" was found. Abort!')
|
||||
} else {
|
||||
console.clear()
|
||||
var demoRecVarsLocation = 0x80000000 + matches[0] + 12
|
||||
|
||||
// Control variables addresses
|
||||
var gRecordingStatus_vaddr = demoRecVarsLocation + 0
|
||||
var gDoneDelay_vaddr = demoRecVarsLocation + 4
|
||||
var gNumOfRecordedInputs_vaddr = demoRecVarsLocation + 8
|
||||
var gRecordedInputsPtr_vaddr = demoRecVarsLocation + 12
|
||||
|
||||
console.log('Recording variables were found at address 0x' + demoRecVarsLocation.toString(16))
|
||||
console.log('Initialization successful! Press L in-game to ready the demo recording before entering in a level.')
|
||||
|
||||
// This runs every frame that is drawn.
|
||||
events.ondraw(function() {
|
||||
var gRecordingStatus = mem.u32[gRecordingStatus_vaddr]
|
||||
|
||||
if(gRecordingStatus == 3) { // gRecordingStatus == DEMOREC_STATUS_STOPPING
|
||||
var gNumOfRecordedInputs = mem.u32[gNumOfRecordedInputs_vaddr]
|
||||
|
||||
if(gNumOfRecordedInputs < 1) {
|
||||
console.log('Error: No inputs could be recorded!')
|
||||
} else {
|
||||
var gRecordedInputsPtr = mem.u32[gRecordedInputsPtr_vaddr]
|
||||
|
||||
console.log('Recorded ' + gNumOfRecordedInputs + ' demo inputs.')
|
||||
|
||||
// Grab demo data from RAM.
|
||||
var demo_data = mem.getblock(gRecordedInputsPtr, (gNumOfRecordedInputs + 1) * 4)
|
||||
|
||||
// Create filename with random id added onto it.
|
||||
var filename = 'SM64_DEMOS/demo_' + get_random_int(0, 0xFFFFFFFF).toString(16) + '.bin'
|
||||
|
||||
// Dump demo data to file.
|
||||
var file = fs.open(filename, 'wb');
|
||||
fs.write(file, demo_data);
|
||||
fs.close(file);
|
||||
|
||||
console.log('Dumped data to file ' + filename)
|
||||
}
|
||||
|
||||
// Set status to DEMOREC_STATUS_DONE
|
||||
mem.u32[gRecordingStatus_vaddr] = 4;
|
||||
|
||||
// Decomp memes
|
||||
console.log('OK');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function get_random_int(min, max) {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds a byte pattern that is 4-byte aligned.
|
||||
*
|
||||
* The javascript api is pretty slow when reading memory directly,
|
||||
* so I made this to search a copy of RAM to make things a little faster.
|
||||
*/
|
||||
function find_matches_fast(pattern) {
|
||||
var targetLength = pattern.length
|
||||
var targetLengthMinusOne = targetLength - 1
|
||||
var matches = []
|
||||
var matching = 0
|
||||
|
||||
// Increments by 8 to speed things up.
|
||||
for(var i = 0; i < RAM_SIZE; i += 8) {
|
||||
if(RAM[i] == pattern[matching])
|
||||
matching++
|
||||
else
|
||||
matching = 0
|
||||
if(matching == targetLength) {
|
||||
matches.push(i - targetLengthMinusOne)
|
||||
matching = 0
|
||||
}
|
||||
if(matching > 0) {
|
||||
if(RAM[i + 1] == pattern[matching])
|
||||
matching++
|
||||
else
|
||||
matching = 0
|
||||
if(matching == targetLength) {
|
||||
matches.push(i + 1 - targetLengthMinusOne)
|
||||
matching = 0
|
||||
}
|
||||
if(matching > 1) {
|
||||
if(RAM[i + 2] == pattern[matching])
|
||||
matching++
|
||||
else
|
||||
matching = 0
|
||||
if(matching == targetLength) {
|
||||
matches.push(i + 2 - targetLengthMinusOne)
|
||||
matching = 0
|
||||
}
|
||||
if(matching > 2) {
|
||||
if(RAM[i + 3] == pattern[matching])
|
||||
matching++
|
||||
else
|
||||
matching = 0
|
||||
if(matching == targetLength) {
|
||||
matches.push(i + 3 - targetLengthMinusOne)
|
||||
matching = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(RAM[i + 4] == pattern[matching])
|
||||
matching++
|
||||
else
|
||||
matching = 0
|
||||
if(matching == targetLength) {
|
||||
matches.push(i + 4 - targetLengthMinusOne)
|
||||
matching = 0
|
||||
}
|
||||
if(matching > 0) {
|
||||
if(RAM[i + 5] == pattern[matching])
|
||||
matching++
|
||||
else
|
||||
matching = 0
|
||||
if(matching == targetLength) {
|
||||
matches.push(i + 5 - targetLengthMinusOne)
|
||||
matching = 0
|
||||
}
|
||||
if(matching > 1) {
|
||||
if(RAM[i + 6] == pattern[matching])
|
||||
matching++
|
||||
else
|
||||
matching = 0
|
||||
if(matching == targetLength) {
|
||||
matches.push(i + 6 - targetLengthMinusOne)
|
||||
matching = 0
|
||||
}
|
||||
if(matching > 2) {
|
||||
if(RAM[i + 7] == pattern[matching])
|
||||
matching++
|
||||
else
|
||||
matching = 0
|
||||
if(matching == targetLength) {
|
||||
matches.push(i + 7 - targetLengthMinusOne)
|
||||
matching = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
485
enhancements/crash.patch
Normal file
485
enhancements/crash.patch
Normal file
@@ -0,0 +1,485 @@
|
||||
diff --git a/asm/crash.s b/asm/crash.s
|
||||
new file mode 100644
|
||||
index 00000000..033bf952
|
||||
--- /dev/null
|
||||
+++ b/asm/crash.s
|
||||
@@ -0,0 +1,153 @@
|
||||
+// SM64 Crash Handler
|
||||
+// See Readme below.
|
||||
+
|
||||
+#include "macros.inc"
|
||||
+
|
||||
+/* ---------------------------------------------------------------
|
||||
+ * IMPORTANT README:
|
||||
+ * ---------------------------------------------------------------
|
||||
+ * Frame buffer emulation is required. To enable it in GlideN64,
|
||||
+ * check "Emulate frame buffer" and "Render frame buffer to output"
|
||||
+ * in the "Frame buffer" tab.
|
||||
+ *
|
||||
+ * Your emulator's CPU core style should be set to interpreter for best results.
|
||||
+ *
|
||||
+ * See the DEBUG_ASSERT macro on how to call the crash screen for
|
||||
+ * detected exceptions.
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+.set noat
|
||||
+.set noreorder
|
||||
+.set gp=64
|
||||
+
|
||||
+.set COP0_CAUSE, $13
|
||||
+.set COP0_EPC, $14
|
||||
+.set COP0_BADVADDR, $8
|
||||
+
|
||||
+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, COP0_CAUSE
|
||||
+
|
||||
+glabel cop0_get_epc
|
||||
+ jr $ra
|
||||
+ mfc0 $v0, COP0_EPC
|
||||
+
|
||||
+glabel cop0_get_badvaddr
|
||||
+ jr $ra
|
||||
+ mfc0 $v0, 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
|
||||
+ mfc0 $k1, COP0_CAUSE
|
||||
+ andi $k1, $k1, (0x1F << 2)
|
||||
+ beqzl $k1, .end2 // exit if ExCode is 0
|
||||
+ lui $k0, %hi(__osException)
|
||||
+ 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 $fp, 0x0F8 ($k0)
|
||||
+ sd $ra, 0x100 ($k0)
|
||||
+ // cop unusable exception fired twice on startup so we'll ignore it for now
|
||||
+ li $t0, (0x0B << 2)
|
||||
+ beq $k1, $t0, .end
|
||||
+ nop
|
||||
+ jal show_crash_screen_and_hang
|
||||
+ nop
|
||||
+ .end:
|
||||
+ 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 $fp, 0x0F8 ($k0)
|
||||
+ ld $ra, 0x100 ($k0)
|
||||
+ lui $k0, %hi(__osException)
|
||||
+ .end2:
|
||||
+ addiu $k0, $k0, %lo(__osException)
|
||||
+ jr $k0 // run the original handler
|
||||
+ nop
|
||||
diff --git a/lib/asm/__osExceptionPreamble.s b/lib/asm/__osExceptionPreamble.s
|
||||
index c3b97993..c552a485 100644
|
||||
--- a/lib/asm/__osExceptionPreamble.s
|
||||
+++ b/lib/asm/__osExceptionPreamble.s
|
||||
@@ -11,8 +11,8 @@
|
||||
#endif
|
||||
|
||||
glabel __osExceptionPreamble
|
||||
- lui $k0, %hi(__osException)
|
||||
- addiu $k0, %lo(__osException)
|
||||
+ lui $k0, %hi(__crash_handler_entry)
|
||||
+ addiu $k0, %lo(__crash_handler_entry)
|
||||
jr $k0
|
||||
nop
|
||||
|
||||
diff --git a/sm64.ld b/sm64.ld
|
||||
index 7d9b5b4a..c7bb81b9 100755
|
||||
--- a/sm64.ld
|
||||
+++ b/sm64.ld
|
||||
@@ -117,6 +117,7 @@ SECTIONS
|
||||
BUILD_DIR/src/game/rendering_graph_node.o(.text);
|
||||
BUILD_DIR/src/game/profiler.o(.text);
|
||||
BUILD_DIR/asm/decompress.o(.text);
|
||||
+ BUILD_DIR/asm/crash.o(.text);
|
||||
BUILD_DIR/src/game/camera.o(.text);
|
||||
BUILD_DIR/src/game/object_list_processor.o(.text);
|
||||
diff --git a/src/game/crash.c b/src/game/crash.c
|
||||
new file mode 100644
|
||||
index 00000000..716adfbd
|
||||
--- /dev/null
|
||||
+++ b/src/game/crash.c
|
||||
@@ -0,0 +1,260 @@
|
||||
+/* SM64 Crash Handler */
|
||||
+
|
||||
+#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 };
|
||||
+
|
||||
+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(95, 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(145, 50, "VA");
|
||||
+ fb_print_int_hex(160, 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;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/game/crash.h b/src/game/crash.h
|
||||
new file mode 100644
|
||||
index 00000000..1386930d
|
||||
--- /dev/null
|
||||
+++ b/src/game/crash.h
|
||||
@@ -0,0 +1,28 @@
|
||||
+#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 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_ */
|
||||
BIN
enhancements/crash_font.bin
Normal file
BIN
enhancements/crash_font.bin
Normal file
Binary file not shown.
340
enhancements/debug_box.patch
Normal file
340
enhancements/debug_box.patch
Normal file
@@ -0,0 +1,340 @@
|
||||
diff --git a/src/game/area.c b/src/game/area.c
|
||||
index af9d0156..c68a7f6e 100644
|
||||
--- a/src/game/area.c
|
||||
+++ b/src/game/area.c
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "save_file.h"
|
||||
#include "level_table.h"
|
||||
#include "dialog_ids.h"
|
||||
+#include "debug_box.h"
|
||||
|
||||
struct SpawnInfo gPlayerSpawnInfos[1];
|
||||
struct GraphNode *gGraphNodePointers[0x100];
|
||||
@@ -363,6 +364,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..1ffe0288
|
||||
--- /dev/null
|
||||
+++ b/src/game/debug_box.c
|
||||
@@ -0,0 +1,281 @@
|
||||
+#include <ultra64.h>
|
||||
+
|
||||
+#include "sm64.h"
|
||||
+#include "game/game_init.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 {
|
||||
+ u32 color;
|
||||
+ Vec3s center;
|
||||
+ Vec3s bounds;
|
||||
+ s16 yaw;
|
||||
+};
|
||||
+
|
||||
+struct DebugBox sBoxes[MAX_DEBUG_BOXES];
|
||||
+s16 sNumBoxes = 0;
|
||||
+
|
||||
+extern Mat4 gMatStack[32]; //XXX: Hack
|
||||
+
|
||||
+/**
|
||||
+ * The debug boxes' default transparency
|
||||
+ */
|
||||
+#define DBG_BOX_ALPHA 0x7F
|
||||
+/**
|
||||
+ * The debug boxes' default color. sCurBoxColor is reset to this every frame.
|
||||
+ */
|
||||
+#define DBG_BOX_DEF_COLOR 0xFF0000
|
||||
+
|
||||
+/**
|
||||
+ * The color that new boxes will be drawn with.
|
||||
+ */
|
||||
+u32 sCurBoxColor = DBG_BOX_ALPHA << 24 | DBG_BOX_DEF_COLOR;
|
||||
+
|
||||
+/**
|
||||
+ * The allocated size of a rotated box's dl
|
||||
+ */
|
||||
+#define DBG_BOX_ROT_DLSIZE ((s32)(6 * sizeof(Gfx) + 8 * sizeof(Vtx)))
|
||||
+/**
|
||||
+ * The allocated size of a normal box's dl
|
||||
+ */
|
||||
+#define DBG_BOX_DLSIZE ((s32)(2 * sizeof(Gfx) + 8 * sizeof(Vtx)))
|
||||
+
|
||||
+/**
|
||||
+ * 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(5, 4, 6, 0x0, 5, 6, 7, 0x0), // front
|
||||
+ gsSP2Triangles(0, 1, 2, 0x0, 2, 1, 3, 0x0), // back
|
||||
+
|
||||
+ gsSP2Triangles(4, 0, 2, 0x0, 2, 6, 4, 0x0), // left
|
||||
+ gsSP2Triangles(1, 5, 3, 0x0, 3, 5, 7, 0x0), // right
|
||||
+
|
||||
+ gsSP2Triangles(1, 0, 4, 0x0, 1, 4, 5, 0x0), // top
|
||||
+ gsSP2Triangles(2, 3, 6, 0x0, 6, 3, 7, 0x0), // bottom
|
||||
+
|
||||
+ 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) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ vec3f_to_vec3s(sBoxes[sNumBoxes].center, center);
|
||||
+ vec3f_to_vec3s(sBoxes[sNumBoxes].bounds, bounds);
|
||||
+
|
||||
+ sBoxes[sNumBoxes].yaw = yaw;
|
||||
+ sBoxes[sNumBoxes].color = sCurBoxColor;
|
||||
+
|
||||
+ ++sNumBoxes;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Draw new boxes with the given color.
|
||||
+ * Color format is 32-bit ARGB.
|
||||
+ * If the alpha component is zero, DBG_BOX_ALPHA (0x7f) will be used instead.
|
||||
+ * Ex: 0xFF0000 becomes 0x7FFF0000
|
||||
+ */
|
||||
+void debug_box_color(u32 color)
|
||||
+{
|
||||
+ if ((color >> 24) == 0) color |= (DBG_BOX_ALPHA << 24);
|
||||
+ sCurBoxColor = color;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * 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(int index)
|
||||
+{
|
||||
+ Vtx *verts;
|
||||
+ Mtx *translate;
|
||||
+ Mtx *rotate;
|
||||
+ Mtx *translateback;
|
||||
+ struct DebugBox *box = &sBoxes[index];
|
||||
+ u32 color = box->color;
|
||||
+
|
||||
+ 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 (box->yaw != 0 && (Gfx*)gGfxPoolEnd - gDisplayListHead < DBG_BOX_ROT_DLSIZE)
|
||||
+ return;
|
||||
+ else if ((Gfx*)gGfxPoolEnd - gDisplayListHead < DBG_BOX_DLSIZE)
|
||||
+ return;
|
||||
+
|
||||
+ verts = alloc_display_list(8 * sizeof(Vtx));
|
||||
+
|
||||
+ 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_COL /**/ (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff, (color >> 24) & 0xff
|
||||
+#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;
|
||||
+
|
||||
+ debug_box_color(DBG_BOX_DEF_COLOR);
|
||||
+
|
||||
+ if (sNumBoxes == 0) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ mtx = alloc_display_list(sizeof(Mtx));
|
||||
+ // Don't draw if there isn't space for the configuration + at least one box
|
||||
+ if (mtx == NULL || ((Gfx*)gGfxPoolEnd - gDisplayListHead) <= (s32)(2 * sizeof(Gfx)) + DBG_BOX_DLSIZE) {
|
||||
+ 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(i);
|
||||
+ }
|
||||
+
|
||||
+ sNumBoxes = 0;
|
||||
+}
|
||||
diff --git a/src/game/debug_box.h b/src/game/debug_box.h
|
||||
new file mode 100644
|
||||
index 00000000..540c85e4
|
||||
--- /dev/null
|
||||
+++ b/src/game/debug_box.h
|
||||
@@ -0,0 +1,26 @@
|
||||
+#ifndef DEBUG_BOX_H
|
||||
+#define DEBUG_BOX_H
|
||||
+
|
||||
+/**
|
||||
+ * @file debug_box.h
|
||||
+ * Draws debug boxes, see debug_box.c for details
|
||||
+ */
|
||||
+
|
||||
+#include "types.h"
|
||||
+
|
||||
+/**
|
||||
+ * The max amount of debug boxes before debug_box() just returns.
|
||||
+ * You can set this to something higher, but you might run out of space in the gfx pool.
|
||||
+ */
|
||||
+#define MAX_DEBUG_BOXES 512
|
||||
+
|
||||
+void debug_box_color(u32 color);
|
||||
+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_BOX_H */
|
||||
60
enhancements/fps.patch
Normal file
60
enhancements/fps.patch
Normal file
@@ -0,0 +1,60 @@
|
||||
diff --git a/src/game/game_init.c b/src/game/game_init.c
|
||||
index b961ca52..531231cf 100644
|
||||
--- a/src/game/game_init.c
|
||||
+++ b/src/game/game_init.c
|
||||
@@ -82,6 +82,47 @@ struct DemoInput gRecordedDemoInput = { 0 };
|
||||
// Display
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
|
||||
+// 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;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* Sets the initial RDP (Reality Display Processor) rendering settings.
|
||||
*/
|
||||
@@ -694,5 +735,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..58e7d4f9
|
||||
--- /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 990cb11f..22756e91 100644
|
||||
--- a/lib/src/__osViSwapContext.c
|
||||
+++ b/lib/src/__osViSwapContext.c
|
||||
@@ -54,7 +54,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);
|
||||
__osViNext = __osViCurr;
|
||||
__osViCurr = s1;
|
||||
*__osViNext = *__osViCurr;
|
||||
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 ea784b2c..116dae2d 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 u8 _osLastSentSiCmd;
|
||||
|
||||
@@ -42,33 +43,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);
|
||||
- _osLastSentSiCmd = 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);
|
||||
+ _osLastSentSiCmd = 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 1a86477b..a94f8721 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>
|
||||
|
||||
#ifndef AVOID_UB
|
||||
ALIGNED8 u32 D_80365E00[15];
|
||||
@@ -52,36 +53,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);
|
||||
- _osLastSentSiCmd = 5;
|
||||
- osRecvMesg(mq, NULL, OS_MESG_BLOCK);
|
||||
+ D_80365E3C = 0;
|
||||
+ sp34 = __osSiRawStartDma(OS_READ, D_80365E00);
|
||||
+ _osLastSentSiCmd = 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_IQUE) {
|
||||
+ 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 ba73024b..6deaf407 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
|
||||
|
||||
@@ -51,6 +52,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 7d9b5b4a..be853a3b 100755
|
||||
--- a/sm64.ld
|
||||
+++ b/sm64.ld
|
||||
@@ -306,6 +306,8 @@ SECTIONS
|
||||
#if ENABLE_RUMBLE
|
||||
BUILD_DIR/libultra.a:unk_shindou_file_3.o(.text);
|
||||
#endif
|
||||
+ BUILD_DIR/libultra.a:consoleType.o(.text)
|
||||
+ BUILD_DIR/libultra.a:skGetId.o(.text)
|
||||
BUILD_DIR/lib/rsp.o(.text);
|
||||
#else
|
||||
BUILD_DIR/src/game*.o(.text);
|
||||
@@ -428,6 +430,8 @@ SECTIONS
|
||||
#if ENABLE_RUMBLE
|
||||
BUILD_DIR/libultra.a:unk_shindou_file_3.o(.text);
|
||||
#endif
|
||||
+ BUILD_DIR/libultra.a:consoleType.o(.text)
|
||||
+ BUILD_DIR/libultra.a:skGetId.o(.text)
|
||||
BUILD_DIR/lib/rsp.o(.text);
|
||||
#endif
|
||||
|
||||
298
enhancements/mem_error_screen.patch
Normal file
298
enhancements/mem_error_screen.patch
Normal file
@@ -0,0 +1,298 @@
|
||||
diff --git a/Makefile b/Makefile
|
||||
index f50b7622..124c7ec6 100644
|
||||
--- a/Makefile
|
||||
+++ b/Makefile
|
||||
@@ -478,6 +478,7 @@ $(BUILD_DIR)/include/text_strings.h: $(BUILD_DIR)/include/text_menu_strings.h
|
||||
$(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h
|
||||
$(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h
|
||||
$(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h
|
||||
+$(BUILD_DIR)/src/boot/mem_error_screen.o: $(BUILD_DIR)/include/text_strings.h
|
||||
|
||||
|
||||
#==============================================================================#
|
||||
diff --git a/include/segments.h b/include/segments.h
|
||||
index a97d6ee8..186c968e 100644
|
||||
--- a/include/segments.h
|
||||
+++ b/include/segments.h
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
+/* Use expansion pack RAM */
|
||||
+#define USE_EXT_RAM 1
|
||||
+
|
||||
/*
|
||||
* Memory addresses for segments. Ideally, this header file would not be
|
||||
* needed, and the addresses would be defined in sm64.ld and linker-inserted
|
||||
diff --git a/include/text_strings.h.in b/include/text_strings.h.in
|
||||
index 749179b1..2f6f7a3c 100644
|
||||
--- a/include/text_strings.h.in
|
||||
+++ b/include/text_strings.h.in
|
||||
@@ -25,6 +25,11 @@
|
||||
#define TEXT_PAUSE _("PAUSE") // Pause text, Castle Courses
|
||||
#define TEXT_HUD_CONGRATULATIONS _("CONGRATULATIONS") // Course Complete Text, Bowser Courses
|
||||
|
||||
+// Memory Expansion Error Screen
|
||||
+#define TEXT_CONSOLE_8MB _("If you're using an N64 console, then you will need to buy an\nExpansion Pak to play this ROM hack.")
|
||||
+#define TEXT_PJ64 _("If you are using PJ64 1.6, go to:\nOptions > Settings > Rom Settings Tab > Memory Size\nthen select 8 MB from the drop-down box.")
|
||||
+#define TEXT_PJ64_2 _("If you are using PJ64 2.X, go to:\nOptions > Settings > Config: > Memory Size, select 8 MB")
|
||||
+
|
||||
#if defined(VERSION_JP) || defined(VERSION_SH)
|
||||
|
||||
/**
|
||||
diff --git a/levels/entry.c b/levels/entry.c
|
||||
index 17c773ed..677a5ae9 100644
|
||||
--- a/levels/entry.c
|
||||
+++ b/levels/entry.c
|
||||
@@ -15,3 +15,12 @@ const LevelScript level_script_entry[] = {
|
||||
EXECUTE(/*seg*/ SEGMENT_MENU_INTRO, /*script*/ _introSegmentRomStart, /*scriptEnd*/ _introSegmentRomEnd, /*entry*/ level_intro_splash_screen),
|
||||
JUMP(/*target*/ level_script_entry),
|
||||
};
|
||||
+
|
||||
+const LevelScript level_script_entry_error_screen[] = {
|
||||
+ INIT_LEVEL(),
|
||||
+ SLEEP(/*frames*/ 2),
|
||||
+ BLACKOUT(/*active*/ FALSE),
|
||||
+ SET_REG(/*value*/ 0),
|
||||
+ EXECUTE(/*seg*/ SEGMENT_MENU_INTRO, /*script*/ _introSegmentRomStart, /*scriptEnd*/ _introSegmentRomEnd, /*entry*/ level_intro_entry_error_screen),
|
||||
+ JUMP(/*target*/ level_script_entry_error_screen),
|
||||
+};
|
||||
diff --git a/levels/intro/geo.c b/levels/intro/geo.c
|
||||
index 30a87806..6bf7b79a 100644
|
||||
--- a/levels/intro/geo.c
|
||||
+++ b/levels/intro/geo.c
|
||||
@@ -15,6 +15,24 @@
|
||||
|
||||
#include "levels/intro/header.h"
|
||||
|
||||
+const GeoLayout intro_geo_error_screen[] = {
|
||||
+ GEO_NODE_SCREEN_AREA(0, SCREEN_CENTER_X, SCREEN_CENTER_Y, SCREEN_CENTER_X, SCREEN_CENTER_Y),
|
||||
+ GEO_OPEN_NODE(),
|
||||
+ GEO_ZBUFFER(0),
|
||||
+ GEO_OPEN_NODE(),
|
||||
+ GEO_NODE_ORTHO(100),
|
||||
+ GEO_OPEN_NODE(),
|
||||
+ GEO_BACKGROUND_COLOR(0x0001),
|
||||
+ GEO_CLOSE_NODE(),
|
||||
+ GEO_CLOSE_NODE(),
|
||||
+ GEO_ZBUFFER(0),
|
||||
+ GEO_OPEN_NODE(),
|
||||
+ GEO_ASM(0, geo18_display_error_message),
|
||||
+ GEO_CLOSE_NODE(),
|
||||
+ GEO_CLOSE_NODE(),
|
||||
+ GEO_END(),
|
||||
+};
|
||||
+
|
||||
// 0x0E0002D0
|
||||
const GeoLayout intro_geo_splash_screen[] = {
|
||||
GEO_NODE_SCREEN_AREA(0, SCREEN_CENTER_X, SCREEN_CENTER_Y, SCREEN_CENTER_X, SCREEN_CENTER_Y),
|
||||
diff --git a/levels/intro/header.h b/levels/intro/header.h
|
||||
index 99277e86..04797cd7 100644
|
||||
--- a/levels/intro/header.h
|
||||
+++ b/levels/intro/header.h
|
||||
@@ -26,4 +26,8 @@ extern const LevelScript script_intro_main_level_entry_stop_music[];
|
||||
extern const LevelScript script_intro_main_level_entry[];
|
||||
extern const LevelScript script_intro_splash_screen[];
|
||||
|
||||
+extern const GeoLayout intro_geo_error_screen[];
|
||||
+extern const LevelScript level_intro_entry_error_screen[];
|
||||
+extern Gfx *geo18_display_error_message(u32 run, UNUSED struct GraphNode *sp44, UNUSED u32 sp48);
|
||||
+
|
||||
#endif
|
||||
diff --git a/levels/intro/script.c b/levels/intro/script.c
|
||||
index 04b8fc4c..ca9058c4 100644
|
||||
--- a/levels/intro/script.c
|
||||
+++ b/levels/intro/script.c
|
||||
@@ -18,6 +18,21 @@
|
||||
#include "make_const_nonconst.h"
|
||||
#include "levels/intro/header.h"
|
||||
|
||||
+const LevelScript level_intro_entry_error_screen[] = {
|
||||
+ INIT_LEVEL(),
|
||||
+ FIXED_LOAD(/*loadAddr*/ _goddardSegmentStart, /*romStart*/ _goddardSegmentRomStart, /*romEnd*/ _goddardSegmentRomEnd),
|
||||
+ LOAD_YAY0(/*seg*/ SEGMENT_LEVEL_DATA, _intro_segment_7SegmentRomStart, _intro_segment_7SegmentRomEnd),
|
||||
+ ALLOC_LEVEL_POOL(),
|
||||
+
|
||||
+ AREA(/*index*/ 1, intro_geo_error_screen),
|
||||
+ END_AREA(),
|
||||
+
|
||||
+ FREE_LEVEL_POOL(),
|
||||
+ LOAD_AREA(/*area*/ 1),
|
||||
+ SLEEP(/*frames*/ 32767),
|
||||
+ EXIT_AND_EXECUTE(/*seg*/ SEGMENT_MENU_INTRO, _introSegmentRomStart, _introSegmentRomEnd, level_intro_entry_error_screen),
|
||||
+};
|
||||
+
|
||||
const LevelScript level_intro_splash_screen[] = {
|
||||
INIT_LEVEL(),
|
||||
FIXED_LOAD(/*loadAddr*/ _goddardSegmentStart, /*romStart*/ _goddardSegmentRomStart, /*romEnd*/ _goddardSegmentRomEnd),
|
||||
diff --git a/src/engine/level_script.h b/src/engine/level_script.h
|
||||
index d41a91c8..7d047236 100644
|
||||
--- a/src/engine/level_script.h
|
||||
+++ b/src/engine/level_script.h
|
||||
@@ -6,6 +6,7 @@
|
||||
struct LevelCommand;
|
||||
|
||||
extern u8 level_script_entry[];
|
||||
+extern u8 level_script_entry_error_screen[];
|
||||
|
||||
struct LevelCommand *level_script_execute(struct LevelCommand *cmd);
|
||||
|
||||
diff --git a/src/boot/main.c b/src/boot/main.c
|
||||
index 1a9d9e7e..f4f7a9e5 100644
|
||||
--- a/src/boot/main.c
|
||||
+++ b/src/boot/main.c
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "segments.h"
|
||||
#include "game/main.h"
|
||||
#include "game/rumble_init.h"
|
||||
+#include "mem_error_screen.h"
|
||||
|
||||
// Message IDs
|
||||
#define MESG_SP_COMPLETE 100
|
||||
@@ -131,6 +132,10 @@ void alloc_pool(void) {
|
||||
void *start = (void *) SEG_POOL_START;
|
||||
void *end = (void *) SEG_POOL_END;
|
||||
|
||||
+ // Detect memory size
|
||||
+ if (does_pool_end_lie_out_of_bounds(end))
|
||||
+ end = (void *)SEG_POOL_END_4MB;
|
||||
+
|
||||
main_pool_init(start, end);
|
||||
gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT);
|
||||
}
|
||||
@@ -336,7 +341,10 @@ void thread3_main(UNUSED void *arg) {
|
||||
create_thread(&gSoundThread, 4, thread4_sound, NULL, gThread4Stack + 0x2000, 20);
|
||||
osStartThread(&gSoundThread);
|
||||
|
||||
- create_thread(&gGameLoopThread, 5, thread5_game_loop, NULL, gThread5Stack + 0x2000, 10);
|
||||
+ if (!gNotEnoughMemory)
|
||||
+ create_thread(&gGameLoopThread, 5, thread5_game_loop, NULL, gThread5Stack + 0x2000, 10);
|
||||
+ else
|
||||
+ create_thread(&gGameLoopThread, 5, thread5_mem_error_message_loop, NULL, gThread5Stack + 0x2000, 10);
|
||||
osStartThread(&gGameLoopThread);
|
||||
|
||||
while (TRUE) {
|
||||
diff --git a/src/boot/mem_error_screen.c b/src/boot/mem_error_screen.c
|
||||
new file mode 100644
|
||||
index 00000000..f432927c
|
||||
--- /dev/null
|
||||
+++ b/src/boot/mem_error_screen.c
|
||||
@@ -0,0 +1,104 @@
|
||||
+/* clang-format off */
|
||||
+/*
|
||||
+ * mem_error_screen.inc.c
|
||||
+ *
|
||||
+ * This enhancement should be used for ROM hacks that require the expansion pak.
|
||||
+ *
|
||||
+ */
|
||||
+/* clang-format on */
|
||||
+
|
||||
+#include <types.h>
|
||||
+#include "segments.h"
|
||||
+#include "text_strings.h"
|
||||
+#include "game_init.h"
|
||||
+#include "main.h"
|
||||
+#include "print.h"
|
||||
+#include "ingame_menu.h"
|
||||
+#include "segment2.h"
|
||||
+#include "../engine/level_script.h"
|
||||
+
|
||||
+// Ensure that USE_EXT_RAM is defined.
|
||||
+#ifndef USE_EXT_RAM
|
||||
+#error You have to define USE_EXT_RAM in 'include/segments.h'
|
||||
+#endif
|
||||
+
|
||||
+// Require 8 MB of RAM, even if the pool doesn't go into extended memory.
|
||||
+// Change the '8' to whatever MB limit you want.
|
||||
+// Note: only special emulators allow for RAM sizes above 8 MB.
|
||||
+#define REQUIRED_MIN_MEM_SIZE 1048576 * 8
|
||||
+
|
||||
+u8 gNotEnoughMemory = FALSE;
|
||||
+u8 gDelayForErrorMessage = 0;
|
||||
+
|
||||
+u8 does_pool_end_lie_out_of_bounds(void *end) {
|
||||
+ u32 endPhy = ((u32) end) & 0x1FFFFFFF;
|
||||
+ u32 memSize = *((u32 *) 0x80000318);
|
||||
+
|
||||
+ if (endPhy > memSize) {
|
||||
+ gNotEnoughMemory = TRUE;
|
||||
+ return TRUE;
|
||||
+ } else {
|
||||
+ if (memSize < REQUIRED_MIN_MEM_SIZE) {
|
||||
+ gNotEnoughMemory = TRUE;
|
||||
+ }
|
||||
+ return FALSE;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// If you're using an N64 console, then you will need to buy an\nexpansion pak to play this ROM hack.
|
||||
+u8 text_console_8mb[] = { TEXT_CONSOLE_8MB };
|
||||
+
|
||||
+// If you are using PJ64 1.6, go to: Options â–ş Settings â–ş Rom Settings Tab â–ş Memory Size then select 8
|
||||
+// MB from the drop-down box.
|
||||
+u8 text_pj64[] = { TEXT_PJ64 };
|
||||
+
|
||||
+// If you are using PJ64 2.X, go to: Options â–ş Settings â–ş Config: â–ş Memory Size, select 8 MB
|
||||
+u8 text_pj64_2[] = { TEXT_PJ64_2 };
|
||||
+
|
||||
+Gfx *geo18_display_error_message(u32 run, UNUSED struct GraphNode *sp44, UNUSED u32 sp48) {
|
||||
+ if (run) {
|
||||
+ if (gDelayForErrorMessage > 0) {
|
||||
+ // Draw color text title.
|
||||
+ print_text(10, 210, "ERROR Need more memory");
|
||||
+
|
||||
+ // Init generic text rendering
|
||||
+ create_dl_ortho_matrix();
|
||||
+ gSPDisplayList(gDisplayListHead++,
|
||||
+ dl_ia_text_begin); // Init rendering stuff for generic text
|
||||
+
|
||||
+ // Set text color to white
|
||||
+ gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
|
||||
+
|
||||
+ print_generic_string(8, 170, text_console_8mb);
|
||||
+ print_generic_string(8, 120, text_pj64);
|
||||
+ print_generic_string(8, 54, text_pj64_2);
|
||||
+
|
||||
+ // Cleanup
|
||||
+ gSPDisplayList(gDisplayListHead++,
|
||||
+ dl_ia_text_end); // Reset back to default render settings.
|
||||
+ gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
|
||||
+ } else {
|
||||
+ gDelayForErrorMessage += 1;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+// Basic main loop for the error screen. Note that controllers are not enabled here.
|
||||
+void thread5_mem_error_message_loop(UNUSED void *arg) {
|
||||
+ struct LevelCommand *addr;
|
||||
+
|
||||
+ setup_game_memory();
|
||||
+ set_vblank_handler(2, &gGameVblankHandler, &gGameVblankQueue, (OSMesg) 1);
|
||||
+
|
||||
+ addr = segmented_to_virtual(level_script_entry_error_screen);
|
||||
+
|
||||
+ render_init();
|
||||
+
|
||||
+ while (1) {
|
||||
+ select_gfx_pool();
|
||||
+ addr = level_script_execute(addr);
|
||||
+ display_and_vsync();
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/game/mem_error_screen.h b/src/game/mem_error_screen.h
|
||||
new file mode 100644
|
||||
index 00000000..9fbff34c
|
||||
--- /dev/null
|
||||
+++ b/src/game/mem_error_screen.h
|
||||
@@ -0,0 +1,8 @@
|
||||
+#ifndef MEM_ERROR_SCREEN_H
|
||||
+#define MEM_ERROR_SCREEN_H
|
||||
+
|
||||
+extern u8 gNotEnoughMemory;
|
||||
+void thread5_mem_error_message_loop(UNUSED void *arg);
|
||||
+u8 does_pool_end_lie_out_of_bounds(void *end);
|
||||
+
|
||||
+#endif
|
||||
185
enhancements/record_demo.patch
Normal file
185
enhancements/record_demo.patch
Normal file
@@ -0,0 +1,185 @@
|
||||
diff --git a/src/game/game_init.c b/src/game/game_init.c
|
||||
index b961ca52..adfde049 100644
|
||||
--- a/src/game/game_init.c
|
||||
+++ b/src/game/game_init.c
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "game_init.h"
|
||||
#include "main.h"
|
||||
#include "memory.h"
|
||||
+#include "object_list_processor.h"
|
||||
#include "profiler.h"
|
||||
#include "save_file.h"
|
||||
#include "seq_ids.h"
|
||||
@@ -386,6 +387,45 @@ void display_and_vsync(void) {
|
||||
// Controls
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
|
||||
+/*
|
||||
+ * This enhancement allows you to record gameplay demos for the mario head screen.
|
||||
+ *
|
||||
+ * Note:
|
||||
+ * This enhancement does require the lastest versions of PJ64 from the nightly builds,
|
||||
+ * because it uses the javascript API to automatically dump the demo files from RAM
|
||||
+ * once the demo is completed. See enhancements/RecordDemo.js for more info
|
||||
+ *
|
||||
+*/
|
||||
+
|
||||
+#include "mario.h"
|
||||
+
|
||||
+#define DEMOREC_STATUS_NOT_RECORDING 0
|
||||
+#define DEMOREC_STATUS_PREPARING 1
|
||||
+#define DEMOREC_STATUS_RECORDING 2
|
||||
+#define DEMOREC_STATUS_STOPPING 3
|
||||
+#define DEMOREC_STATUS_DONE 4
|
||||
+
|
||||
+#define DEMOREC_PRINT_X 10
|
||||
+#define DEMOREC_PRINT_Y 10
|
||||
+
|
||||
+#define DEMOREC_DONE_DELAY 60 // Show "DONE" message for 2 seconds.
|
||||
+
|
||||
+#define DEMOREC_MAX_INPUTS 1025 // Max number of recorded inputs.
|
||||
+
|
||||
+/*
|
||||
+ DO NOT REMOVE, MODIFY, OR MAKE A COPY OF THIS EXACT STRING!
|
||||
+ This is here so that the js dump script can find the control variables easily.
|
||||
+*/
|
||||
+char gDemoRecTag[] = "DEMORECVARS";
|
||||
+
|
||||
+// Control variables. It is easier if they are each 4 byte aligned, which is why they are u32.
|
||||
+u32 gRecordingStatus = DEMOREC_STATUS_NOT_RECORDING;
|
||||
+u32 gDoneDelay = 0;
|
||||
+u32 gNumOfRecordedInputs = 0;
|
||||
+struct DemoInput gRecordedInputs[DEMOREC_MAX_INPUTS];
|
||||
+struct DemoInput* gRecordedInputsPtr = (struct DemoInput*)gRecordedInputs;
|
||||
+struct DemoInput gRecordedDemoInputCopy;
|
||||
+
|
||||
/**
|
||||
* 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.
|
||||
@@ -420,6 +460,118 @@ UNUSED static void record_demo(void) {
|
||||
gRecordedDemoInput.timer++;
|
||||
}
|
||||
|
||||
+void record_new_demo_input(void) {
|
||||
+ if (gRecordedDemoInput.timer == 1 && gRecordedDemoInputCopy.timer > 0) {
|
||||
+ gRecordedInputs[gNumOfRecordedInputs].timer = gRecordedDemoInputCopy.timer;
|
||||
+ gRecordedInputs[gNumOfRecordedInputs + 1].timer = 0;
|
||||
+ gRecordedInputs[gNumOfRecordedInputs].rawStickX = gRecordedDemoInputCopy.rawStickX;
|
||||
+ gRecordedInputs[gNumOfRecordedInputs + 1].rawStickX = gRecordedDemoInputCopy.rawStickX;
|
||||
+ gRecordedInputs[gNumOfRecordedInputs].rawStickY = gRecordedDemoInputCopy.rawStickY;
|
||||
+ gRecordedInputs[gNumOfRecordedInputs + 1].rawStickY = gRecordedDemoInputCopy.rawStickY;
|
||||
+ gRecordedInputs[gNumOfRecordedInputs].buttonMask = gRecordedDemoInputCopy.buttonMask;
|
||||
+ gRecordedInputs[gNumOfRecordedInputs + 1].buttonMask = gRecordedDemoInputCopy.buttonMask;
|
||||
+ gNumOfRecordedInputs++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// Self explanitory
|
||||
+void copy_gRecordedDemoInput(void) {
|
||||
+ gRecordedDemoInputCopy.timer = gRecordedDemoInput.timer;
|
||||
+ gRecordedDemoInputCopy.rawStickX = gRecordedDemoInput.rawStickX;
|
||||
+ gRecordedDemoInputCopy.rawStickY = gRecordedDemoInput.rawStickY;
|
||||
+ gRecordedDemoInputCopy.buttonMask = gRecordedDemoInput.buttonMask;
|
||||
+}
|
||||
+
|
||||
+// Runs when the demo is recording.
|
||||
+void recording(void) {
|
||||
+
|
||||
+ // Force-stop when someone makes too many inputs.
|
||||
+ if (gNumOfRecordedInputs + 1 > DEMOREC_MAX_INPUTS) {
|
||||
+ gRecordingStatus = DEMOREC_STATUS_STOPPING;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ copy_gRecordedDemoInput();
|
||||
+ record_demo();
|
||||
+ record_new_demo_input();
|
||||
+}
|
||||
+
|
||||
+// Makes sure the last demo input is zeroed out, to make it look more clean.
|
||||
+void record_cleanup(void) {
|
||||
+ gRecordedInputs[gNumOfRecordedInputs].timer = 0;
|
||||
+ gRecordedInputs[gNumOfRecordedInputs].rawStickX = 0;
|
||||
+ gRecordedInputs[gNumOfRecordedInputs].rawStickY = 0;
|
||||
+ gRecordedInputs[gNumOfRecordedInputs].buttonMask = 0;
|
||||
+
|
||||
+ // Make sure the done delay is reset before moving to DONE status.
|
||||
+ gDoneDelay = 0;
|
||||
+}
|
||||
+
|
||||
+void record_run(void) {
|
||||
+ switch(gRecordingStatus) {
|
||||
+ case DEMOREC_STATUS_NOT_RECORDING:
|
||||
+ break;
|
||||
+ case DEMOREC_STATUS_PREPARING:
|
||||
+ if (gMarioObject != NULL && gCurrLevelNum != LEVEL_NONE) { // If the game is in an active level
|
||||
+ gRecordingStatus = DEMOREC_STATUS_RECORDING;
|
||||
+
|
||||
+ // First 4 values in demo inputs are used to define level ID
|
||||
+ gNumOfRecordedInputs = 1;
|
||||
+ gRecordedInputs[0].timer = gCurrLevelNum;
|
||||
+ gRecordedInputs[0].rawStickX = 0;
|
||||
+ gRecordedInputs[0].rawStickY = 0;
|
||||
+ gRecordedInputs[0].buttonMask = 0;
|
||||
+ }
|
||||
+ break;
|
||||
+ case DEMOREC_STATUS_RECORDING:
|
||||
+ recording();
|
||||
+ break;
|
||||
+ case DEMOREC_STATUS_DONE:
|
||||
+ if (gDoneDelay > DEMOREC_DONE_DELAY)
|
||||
+ gRecordingStatus = DEMOREC_STATUS_NOT_RECORDING;
|
||||
+ else
|
||||
+ gDoneDelay++;
|
||||
+ break;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// Prints the status on the bottom-left side of the screen in colorful text.
|
||||
+void print_status(void) {
|
||||
+ switch(gRecordingStatus) {
|
||||
+ case DEMOREC_STATUS_PREPARING:
|
||||
+ print_text(DEMOREC_PRINT_X, DEMOREC_PRINT_Y, "READY");
|
||||
+ break;
|
||||
+ case DEMOREC_STATUS_RECORDING:
|
||||
+ print_text(DEMOREC_PRINT_X, DEMOREC_PRINT_Y, "REC");
|
||||
+ break;
|
||||
+ case DEMOREC_STATUS_STOPPING:
|
||||
+ print_text(DEMOREC_PRINT_X, DEMOREC_PRINT_Y, "WAIT");
|
||||
+ break;
|
||||
+ case DEMOREC_STATUS_DONE:
|
||||
+ print_text(DEMOREC_PRINT_X, DEMOREC_PRINT_Y, "DONE");
|
||||
+ break;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// Main function that should be called from thread5_game_loop()
|
||||
+void recordingDemo(void) {
|
||||
+ // Mario needs to enter directly into a level and not from a warp,
|
||||
+ // so the debug level select is used for that.
|
||||
+ gDebugLevelSelect = TRUE;
|
||||
+
|
||||
+ if (gPlayer1Controller->buttonPressed & L_TRIG) {
|
||||
+ if (gRecordingStatus == DEMOREC_STATUS_NOT_RECORDING) {
|
||||
+ gRecordingStatus = DEMOREC_STATUS_PREPARING;
|
||||
+ } else if (gRecordingStatus == DEMOREC_STATUS_RECORDING) {
|
||||
+ gRecordingStatus = DEMOREC_STATUS_STOPPING;
|
||||
+ record_cleanup();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ record_run();
|
||||
+ print_status();
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* Take the updated controller struct and calculate the new x, y, and distance floats.
|
||||
*/
|
||||
@@ -684,6 +836,7 @@ void thread5_game_loop(UNUSED void *arg) {
|
||||
audio_game_loop_tick();
|
||||
select_gfx_pool();
|
||||
read_controller_inputs();
|
||||
+ recordingDemo();
|
||||
addr = level_script_execute(addr);
|
||||
|
||||
display_and_vsync();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -452,7 +452,7 @@ extern const BehaviorScript bhvGoomba[];
|
||||
extern const BehaviorScript bhvGoombaTripletSpawner[];
|
||||
#ifdef FLOOMBAS
|
||||
extern const BehaviorScript bhvFloomba[];
|
||||
#ifdef INTRO_FLOOMBAS
|
||||
#ifdef HD_INTRO_TEXTURES
|
||||
extern const BehaviorScript bhvFloombaStartup[];
|
||||
#endif
|
||||
extern const BehaviorScript bhvFloombaTripletSpawner[];
|
||||
|
||||
@@ -19,7 +19,3 @@
|
||||
#include "config/config_movement.h"
|
||||
#include "config/config_objects.h"
|
||||
#include "config/config_rom.h"
|
||||
|
||||
|
||||
/* WARNING: Compatibility safeguards - don't remove this file unless you know what you're doing */
|
||||
#include "config/config_safeguards.h"
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
#define EXPAND_AUDIO_HEAP
|
||||
|
||||
// Use a much better implementation of reverb over vanilla's fake echo reverb. Great for caves or eerie levels, as well as just a better audio experience in general.
|
||||
// Reverb parameters can be configured in audio/synthesis.c to meet desired aesthetic/performance needs. Currently US/JP only. Hurts emulator and console performance.
|
||||
// Reverb parameters can be configured in audio/synthesis.c to meet desired aesthetic/performance needs. Currently US/JP only.
|
||||
//#define BETTER_REVERB
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// #define ENABLE_CREDITS_BENCHMARK
|
||||
|
||||
#ifdef ENABLE_CREDITS_BENCHMARK
|
||||
#define DEBUG_ALL
|
||||
#define ENABLE_VANILLA_LEVEL_SPECIFIC_CHECKS
|
||||
#define TEST_LEVEL LEVEL_CASTLE_GROUNDS
|
||||
#define DEBUG_ALL
|
||||
#define ENABLE_VANILLA_LEVEL_SPECIFIC_CHECKS
|
||||
#define TEST_LEVEL LEVEL_CASTLE_GROUNDS
|
||||
#endif
|
||||
|
||||
@@ -33,10 +33,9 @@
|
||||
// Enables Puppy Camera 2, a rewritten camera that can be freely configured and modified.
|
||||
// #define PUPPYCAM
|
||||
|
||||
// Note: Reonucam is available, but because we had no time to test it properly, it's included as a patch rather than being in the code by default.
|
||||
// Run this command to apply the patch if you want to use it:
|
||||
// tools/apply_patch.sh enhancements/reonucam.patch
|
||||
// Consider it a beta, but it should work fine. Please report any bugs with it. Applying the patch will simply add a define here, so you can still turn it off even after patching.
|
||||
// Enables Reonucam, a custom camera that aims to be a more feature-rich "aglabcam" that also uses less buttons.
|
||||
// An explanation the features can be seen here: https://www.youtube.com/watch?v=TQNkznX9Z3k (please note that the analog feature shown at the end is no longer present)
|
||||
#define REONUCAM
|
||||
|
||||
/**********************************/
|
||||
/***** Vanilla config options *****/
|
||||
@@ -50,5 +49,42 @@
|
||||
// This is automatically enabled when ENABLE_VANILLA_LEVEL_SPECIFIC_CHECKS is enabled,
|
||||
// but feel free to override it if you really want to for some reason
|
||||
#ifdef ENABLE_VANILLA_LEVEL_SPECIFIC_CHECKS
|
||||
#define ENABLE_VANILLA_CAM_PROCESSING
|
||||
#define ENABLE_VANILLA_CAM_PROCESSING
|
||||
#endif
|
||||
|
||||
|
||||
/**************************************/
|
||||
/****** Compatibility safeguards ******/
|
||||
/**************************************/
|
||||
// Don't change these unless you know what you're doing
|
||||
#ifdef FORCED_CAMERA_MODE
|
||||
#define USE_COURSE_DEFAULT_MODE // Forced camera mode overwrites the default mode
|
||||
#endif
|
||||
#ifndef WATER_SURFACE_CAMERA_MODE
|
||||
#define WATER_SURFACE_CAMERA_MODE CAMERA_MODE_WATER_SURFACE
|
||||
#endif
|
||||
#ifndef DEEP_WATER_CAMERA_MODE
|
||||
#define DEEP_WATER_CAMERA_MODE CAMERA_MODE_BEHIND_MARIO
|
||||
#endif
|
||||
#ifndef FLYING_CAMERA_MODE
|
||||
#define FLYING_CAMERA_MODE CAMERA_MODE_BEHIND_MARIO
|
||||
#endif
|
||||
|
||||
// Reonucam overrides
|
||||
#ifdef REONUCAM
|
||||
// Use course default mode
|
||||
#ifndef USE_COURSE_DEFAULT_MODE
|
||||
#define USE_COURSE_DEFAULT_MODE
|
||||
#endif
|
||||
|
||||
// Force camera mode to 8 Dir
|
||||
#ifdef FORCED_CAMERA_MODE
|
||||
#undef FORCED_CAMERA_MODE
|
||||
#endif
|
||||
#define FORCED_CAMERA_MODE CAMERA_MODE_8_DIRECTIONS
|
||||
|
||||
// Disable vanilla cam processing
|
||||
#ifdef ENABLE_VANILLA_CAM_PROCESSING
|
||||
#undef ENABLE_VANILLA_CAM_PROCESSING
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user