mirror of
https://github.com/Mr-Wiseguy/Zelda64Recomp.git
synced 2024-12-22 19:11:54 +01:00
Added function patching, began reorganizing UI code, added native file dialog library
This commit is contained in:
parent
91611c9c33
commit
398988a961
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,6 +7,7 @@
|
||||
|
||||
# Output C files
|
||||
RecompiledFuncs/
|
||||
RecompiledPatches/
|
||||
|
||||
# Linux build output
|
||||
build/
|
||||
@ -43,6 +44,7 @@ bld/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
vcpkg_installed/
|
||||
|
||||
# Runtime files
|
||||
imgui.ini
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -4,3 +4,6 @@
|
||||
[submodule "thirdparty/RmlUi"]
|
||||
path = thirdparty/RmlUi
|
||||
url = https://github.com/mikke89/RmlUi
|
||||
[submodule "thirdparty/nativefiledialog-extended"]
|
||||
path = thirdparty/nativefiledialog-extended
|
||||
url = https://github.com/btzy/nativefiledialog-extended
|
||||
|
@ -1,5 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(MMRecomp)
|
||||
set(CMAKE_C_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
@ -16,14 +17,13 @@ find_package(Freetype REQUIRED)
|
||||
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/../mupen_rt64/mupen64plus-video-rt64 ${CMAKE_BINARY_DIR}/rt64)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/thirdparty/RmlUi)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/thirdparty/nativefiledialog-extended)
|
||||
|
||||
target_include_directories(rt64 PRIVATE ${CMAKE_BINARY_DIR}/rt64/src)
|
||||
get_target_property(RT64_BASENAME rt64 OUTPUT_NAME)
|
||||
set(RT64_DLL ${RT64_BASENAME}${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
|
||||
file(GLOB FUNC_C_SOURCES ${CMAKE_SOURCE_DIR}/RecompiledFuncs/*.c)
|
||||
file(GLOB FUNC_CXX_SOURCES ${CMAKE_SOURCE_DIR}/RecompiledFuncs/*.cpp)
|
||||
|
||||
# RecompiledFuncs - Library containing the primary recompiler output
|
||||
add_library(RecompiledFuncs STATIC)
|
||||
|
||||
target_compile_options(RecompiledFuncs PRIVATE
|
||||
@ -35,8 +35,44 @@ target_include_directories(RecompiledFuncs PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
file(GLOB FUNC_C_SOURCES ${CMAKE_SOURCE_DIR}/RecompiledFuncs/*.c)
|
||||
file(GLOB FUNC_CXX_SOURCES ${CMAKE_SOURCE_DIR}/RecompiledFuncs/*.cpp)
|
||||
|
||||
target_sources(RecompiledFuncs PRIVATE ${FUNC_C_SOURCES} ${FUNC_CXX_SOURCES})
|
||||
|
||||
# PatchesLib - Library containing the recompiled output for any custom function patches
|
||||
add_library(PatchesLib STATIC)
|
||||
|
||||
target_compile_options(PatchesLib PRIVATE
|
||||
# -Wno-unused-but-set-variable
|
||||
-fno-strict-aliasing
|
||||
)
|
||||
|
||||
target_include_directories(PatchesLib PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
target_sources(PatchesLib PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/RecompiledPatches/patches.c
|
||||
)
|
||||
|
||||
# Build patches elf
|
||||
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/patches/patches.elf
|
||||
COMMAND make
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/patches
|
||||
BYPRODUCTS ${CMAKE_SOURCE_DIR}/patches/patches.bin}
|
||||
)
|
||||
|
||||
# Recompile patches elf into patches.c
|
||||
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/RecompiledPatches/patches.c
|
||||
COMMAND RecompPort patches.toml
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/patches/patches.elf
|
||||
)
|
||||
|
||||
# Main executable
|
||||
add_executable(MMRecomp)
|
||||
|
||||
set (SOURCES
|
||||
${CMAKE_SOURCE_DIR}/portultra/audio.cpp
|
||||
${CMAKE_SOURCE_DIR}/portultra/events.cpp
|
||||
@ -67,7 +103,9 @@ set (SOURCES
|
||||
${CMAKE_SOURCE_DIR}/src/sp.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/vi.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/main/main.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui.cpp
|
||||
|
||||
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/ui_events.cpp
|
||||
|
||||
${CMAKE_SOURCE_DIR}/rsp/aspMain.cpp
|
||||
${CMAKE_SOURCE_DIR}/rsp/njpgdspMain.cpp
|
||||
@ -75,8 +113,6 @@ set (SOURCES
|
||||
${CMAKE_SOURCE_DIR}/thirdparty/RmlUi/Backends/RmlUi_Platform_SDL.cpp
|
||||
)
|
||||
|
||||
add_executable(MMRecomp)
|
||||
|
||||
target_include_directories(MMRecomp PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/thirdparty
|
||||
@ -100,12 +136,14 @@ target_link_directories(MMRecomp PRIVATE
|
||||
)
|
||||
|
||||
target_link_libraries(MMRecomp PRIVATE
|
||||
PatchesLib
|
||||
RecompiledFuncs
|
||||
SDL2
|
||||
rt64
|
||||
Freetype::Freetype
|
||||
RmlCore
|
||||
RmlDebugger
|
||||
nfd
|
||||
)
|
||||
|
||||
# TODO fix the RT64 CMake script so that this doesn't need to be duplicated here
|
||||
|
@ -1,29 +0,0 @@
|
||||
<rml>
|
||||
<head>
|
||||
<title>Demo</title>
|
||||
<link type="text/template" href="window.rml" />
|
||||
<style>
|
||||
body
|
||||
{
|
||||
width: 300dp;
|
||||
height: 225dp;
|
||||
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
div#title_bar div#icon
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
div#content
|
||||
{
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body template="window">
|
||||
This is a sample.<br/>
|
||||
Wiseguy was here
|
||||
</body>
|
||||
</rml>
|
File diff suppressed because one or more lines are too long
134
assets/invader_spritesheet.rcss
Normal file
134
assets/invader_spritesheet.rcss
Normal file
@ -0,0 +1,134 @@
|
||||
@spritesheet theme
|
||||
{
|
||||
src: invader.tga;
|
||||
|
||||
/* For high dpi screens, designates the scaling it is intended to be shown at. */
|
||||
resolution: 1x;
|
||||
|
||||
/**
|
||||
The following specifies a list of sprite names and associated rectangles into the image given above.
|
||||
Any sprite given here can be specified in a decorator. Their names must be globally unique.
|
||||
Rectangles are specified as: x y width height. With the origin assumed to be at the top left corner.
|
||||
*/
|
||||
title-bar-l: 147px 0px 82px 85px;
|
||||
title-bar-c: 229px 0px 1px 85px;
|
||||
title-bar-r: 231px 0px 15px 85px;
|
||||
|
||||
/* huditems are vertically flipped titlebars */
|
||||
huditem-l: 147px 55px 82px -55px;
|
||||
huditem-c: 229px 55px 1px -55px;
|
||||
huditem-r: 231px 55px 15px -55px;
|
||||
|
||||
icon-help: 128px 152px 51px 39px;
|
||||
icon-invader: 179px 152px 51px 39px;
|
||||
icon-game: 230px 152px 51px 39px;
|
||||
icon-hiscore: 281px 152px 51px 39px;
|
||||
icon-waves: 332px 152px 51px 39px;
|
||||
icon-flag: 336px 191px 51px 39px;
|
||||
icon-lives: 383px 152px 51px 39px;
|
||||
icon-score: 434px 152px 51px 39px;
|
||||
|
||||
window-tl: 0px 0px 133px 140px;
|
||||
window-t: 134px 0px 1px 140px;
|
||||
window-tr: 136px 0px 10px 140px;
|
||||
window-l: 0px 139px 10px 1px;
|
||||
window-c: 11px 139px 1px 1px;
|
||||
window-r: 10px 139px -10px 1px; /* mirrored left */
|
||||
window-bl: 0px 140px 11px 11px;
|
||||
window-b: 11px 140px 1px 11px;
|
||||
window-br: 136px 140px 10px 11px;
|
||||
|
||||
button: 247px 0px 159px 45px;
|
||||
button-hover: 247px 45px 159px 45px;
|
||||
button-active: 247px 90px 159px 45px;
|
||||
|
||||
button-inner: 259px 19px 135px 1px;
|
||||
button-inner-hover: 259px 64px 135px 1px;
|
||||
button-inner-active: 259px 109px 135px 1px;
|
||||
|
||||
text-l: 162px 192px 14px 31px;
|
||||
text-c: 176px 192px 1px 31px;
|
||||
textarea: 162px 193px 145px 31px;
|
||||
textarea-inner: 173px 206px 127px 10px;
|
||||
|
||||
selectbox-tl: 281px 275px 11px 9px;
|
||||
selectbox-t: 292px 275px 1px 9px;
|
||||
selectbox-tr: 294px 275px 11px 9px;
|
||||
selectbox-l: 281px 283px 11px 1px;
|
||||
selectbox-c: 292px 283px 1px 1px;
|
||||
selectbox-bl: 281px 285px 11px 11px;
|
||||
selectbox-b: 292px 285px 1px 11px;
|
||||
selectbox-br: 294px 285px 11px 11px;
|
||||
|
||||
selectvalue: 162px 192px 145px 37px;
|
||||
selectvalue-hover: 162px 230px 145px 37px;
|
||||
selectarrow: 307px 192px 30px 37px;
|
||||
selectarrow-hover: 307px 230px 30px 37px;
|
||||
selectarrow-active: 307px 268px 30px 37px;
|
||||
|
||||
radio: 407px 0px 30px 30px;
|
||||
radio-hover: 437px 0px 30px 30px;
|
||||
radio-active: 467px 0px 30px 30px;
|
||||
radio-checked: 407px 30px 30px 30px;
|
||||
radio-checked-hover: 437px 30px 30px 30px;
|
||||
radio-checked-active: 467px 30px 30px 30px;
|
||||
|
||||
checkbox: 407px 60px 30px 30px;
|
||||
checkbox-hover: 437px 60px 30px 30px;
|
||||
checkbox-active: 467px 60px 30px 30px;
|
||||
checkbox-checked: 407px 90px 30px 30px;
|
||||
checkbox-checked-hover: 437px 90px 30px 30px;
|
||||
checkbox-checked-active: 467px 90px 30px 30px;
|
||||
|
||||
tableheader-l: 127px 192px 16px 31px;
|
||||
tableheader-c: 143px 192px 2px 31px;
|
||||
tableheader-r: 145px 192px 15px 31px;
|
||||
|
||||
expand: 3px 232px 17px 17px;
|
||||
expand-hover: 21px 232px 17px 17px;
|
||||
expand-active: 39px 232px 17px 17px;
|
||||
expand-collapsed: 3px 250px 17px 17px;
|
||||
expand-collapsed-hover: 21px 250px 17px 17px;
|
||||
expand-collapsed-active: 39px 250px 17px 17px;
|
||||
|
||||
slidertrack-t: 70px 199px 27px 2px;
|
||||
slidertrack-c: 70px 201px 27px 1px;
|
||||
slidertrack-b: 70px 202px 27px 2px;
|
||||
|
||||
sliderbar-t: 56px 152px 23px 23px;
|
||||
sliderbar-c: 56px 175px 23px 1px;
|
||||
sliderbar-b: 56px 176px 23px 22px;
|
||||
sliderbar-hover-t: 80px 152px 23px 23px;
|
||||
sliderbar-hover-c: 80px 175px 23px 1px;
|
||||
sliderbar-hover-b: 80px 176px 23px 22px;
|
||||
sliderbar-active-t: 104px 152px 23px 23px;
|
||||
sliderbar-active-c: 104px 175px 23px 1px;
|
||||
sliderbar-active-b: 104px 176px 23px 22px;
|
||||
|
||||
sliderarrowdec: 0px 152px 27px 24px;
|
||||
sliderarrowdec-hover: 0px 177px 27px 24px;
|
||||
sliderarrowdec-active: 0px 202px 27px 24px;
|
||||
|
||||
sliderarrowinc: 28px 152px 27px 24px;
|
||||
sliderarrowinc-hover: 28px 177px 27px 24px;
|
||||
sliderarrowinc-active: 28px 202px 27px 24px;
|
||||
|
||||
range-track: 219px 194px 3px 32px;
|
||||
range-track-inner: 220px 204px 1px 14px;
|
||||
range-bar: 127px 191px 34px 32px;
|
||||
range-dec: 3px 232px 17px 17px;
|
||||
range-dec-hover: 21px 232px 17px 17px;
|
||||
range-dec-active: 39px 232px 17px 17px;
|
||||
range-inc: 3px 250px 17px 17px;
|
||||
range-inc-hover: 21px 250px 17px 17px;
|
||||
range-inc-active: 39px 250px 17px 17px;
|
||||
|
||||
progress-l: 103px 267px 13px 34px;
|
||||
progress-c: 116px 267px 54px 34px;
|
||||
progress-r: 170px 267px 13px 34px;
|
||||
progress-fill-l: 110px 302px 6px 34px;
|
||||
progress-fill-c: 140px 302px 6px 34px;
|
||||
progress-fill-r: 170px 302px 6px 34px;
|
||||
gauge: 0px 271px 100px 86px;
|
||||
gauge-fill: 0px 356px 100px 86px;
|
||||
}
|
22
assets/launcher.rml
Normal file
22
assets/launcher.rml
Normal file
@ -0,0 +1,22 @@
|
||||
<rml>
|
||||
<head>
|
||||
<title>Launcher</title>
|
||||
<link type="text/rcss" href="rml.rcss"/>
|
||||
<link type="text/rcss" href="invader_spritesheet.rcss"/>
|
||||
<link type="text/rcss" href="invader.rcss"/>
|
||||
<style>
|
||||
body
|
||||
{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="window">
|
||||
<button onclick="start_game" style="align-self: center;">Start Game</button>
|
||||
This is a sample.<br/>
|
||||
Test text
|
||||
</div>
|
||||
</body>
|
||||
</rml>
|
@ -1,19 +0,0 @@
|
||||
<template name="window" content="content">
|
||||
<head>
|
||||
<link type="text/rcss" href="rml.rcss"/>
|
||||
<link type="text/rcss" href="invader.rcss"/>
|
||||
</head>
|
||||
<body class="window">
|
||||
<div id="title_bar">
|
||||
<handle move_target="#document">
|
||||
<div id="icon"></div>
|
||||
<span id="title">Title</span>
|
||||
</handle>
|
||||
</div>
|
||||
<div id="window">
|
||||
<div id="content">
|
||||
</div>
|
||||
</div>
|
||||
<handle size_target="#document" style="position: absolute; width: 16dp; height: 16dp; bottom: 0px; right: 0px; cursor: resize;"></handle>
|
||||
</body>
|
||||
</template>
|
@ -1,9 +1,25 @@
|
||||
#ifndef __RECOMP_UI__
|
||||
#define __RECOMP_UI__
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
void queue_event(const SDL_Event& event);
|
||||
bool try_deque_event(SDL_Event& out);
|
||||
|
||||
namespace Rml {
|
||||
class ElementDocument;
|
||||
class EventListenerInstancer;
|
||||
}
|
||||
|
||||
std::unique_ptr<Rml::EventListenerInstancer> make_event_listener_instancer();
|
||||
|
||||
enum class Menu {
|
||||
Launcher,
|
||||
None
|
||||
};
|
||||
|
||||
void set_current_menu(Menu menu);
|
||||
|
||||
#endif
|
||||
|
@ -125,8 +125,8 @@ auto RSP::CFC2(r32& rt, u8 rd) -> void {
|
||||
if constexpr (Accuracy::RSP::SISD) {
|
||||
rt = 0;
|
||||
for (u32 n = 0; n < 8; n++) {
|
||||
rt |= lo.get(n) << 0 + n;
|
||||
rt |= hi.get(n) << 8 + n;
|
||||
rt |= lo.get(n) << (0 + n);
|
||||
rt |= hi.get(n) << (8 + n);
|
||||
}
|
||||
rt = s16(rt);
|
||||
}
|
||||
@ -151,8 +151,8 @@ auto RSP::CTC2(cr32& rt, u8 rd) -> void {
|
||||
|
||||
if constexpr (Accuracy::RSP::SISD) {
|
||||
for (u32 n = 0; n < 8; n++) {
|
||||
lo->set(n, rt & 1 << 0 + n);
|
||||
hi->set(n, rt & 1 << 8 + n);
|
||||
lo->set(n, rt & 1 << (0 + n));
|
||||
hi->set(n, rt & 1 << (8 + n));
|
||||
}
|
||||
}
|
||||
|
||||
|
9
patches.toml
Normal file
9
patches.toml
Normal file
@ -0,0 +1,9 @@
|
||||
# Config file for recompiling patches for the Majora's Mask NTSC 1.0 Recompilation.
|
||||
|
||||
[input]
|
||||
# Paths are relative to the location of this config file.
|
||||
elf_path = "patches/patches.elf"
|
||||
output_func_path = "RecompiledPatches"
|
||||
single_file_output = true
|
||||
# Allow absolute symbols to be used as jump targets
|
||||
use_absolute_symbols = true
|
5
patches/.gitignore
vendored
Normal file
5
patches/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
*.d
|
||||
*.o
|
||||
*.elf
|
||||
*.bin
|
||||
./funcs.h
|
32
patches/Makefile
Normal file
32
patches/Makefile
Normal file
@ -0,0 +1,32 @@
|
||||
TARGET = patches.elf
|
||||
|
||||
CC := clang
|
||||
LD := ld.lld
|
||||
OBJCOPY := llvm-objcopy
|
||||
|
||||
CFLAGS := -target mips -mips2 -mabi=32 -O2 -mno-odd-spreg -fomit-frame-pointer -G0 -Wall -Wextra -Wno-incompatible-library-redeclaration -Wno-unused-parameter -Wno-unknown-pragmas -Wno-unused-variable
|
||||
CPPFLAGS := -nostdinc -D_LANGUAGE_C -I ../../mm/include -I ../../mm/src -I ../../mm/build -I ../../mm/assets
|
||||
LDFLAGS := -nostdlib -T patches.ld -T syms.ld
|
||||
BINFLAGS := -O binary
|
||||
|
||||
C_SRCS := $(wildcard *.c)
|
||||
C_OBJS := $(C_SRCS:.c=.o)
|
||||
C_DEPS := $(C_SRCS:.c=.d)
|
||||
|
||||
DATABIN := $(TARGET:.elf=.bin)
|
||||
|
||||
$(DATABIN): $(TARGET)
|
||||
$(OBJCOPY) $(BINFLAGS) $(TARGET) $@
|
||||
|
||||
$(TARGET): $(C_OBJS) patches.ld syms.ld
|
||||
$(LD) $(LDFLAGS) $(C_OBJS) -o $@
|
||||
|
||||
$(C_OBJS): %.o : %.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $< -MMD -MF $(@:.o=.d) -c -o $@
|
||||
|
||||
clean:
|
||||
rm -rf $(C_OBJS) $(TARGET) $(DATABIN)
|
||||
|
||||
-include $(C_DEPS)
|
||||
|
||||
.PHONY: clean
|
139
patches/cheats.c
Normal file
139
patches/cheats.c
Normal file
@ -0,0 +1,139 @@
|
||||
#define Audio_PlaySfx play_sound
|
||||
#include "global.h"
|
||||
|
||||
// Infinite magic
|
||||
s32 Magic_Consume(PlayState* play, s16 magicToConsume, s16 type) {
|
||||
InterfaceContext* interfaceCtx = &play->interfaceCtx;
|
||||
|
||||
magicToConsume = 0;
|
||||
|
||||
// // Magic is not acquired yet
|
||||
// if (!gSaveContext.save.saveInfo.playerData.isMagicAcquired) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// Not enough magic available to consume
|
||||
if ((gSaveContext.save.saveInfo.playerData.magic - magicToConsume) < 0) {
|
||||
if (gSaveContext.magicCapacity != 0) {
|
||||
Audio_PlaySfx(NA_SE_SY_ERROR);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case MAGIC_CONSUME_NOW:
|
||||
case MAGIC_CONSUME_NOW_ALT:
|
||||
// Drain magic immediately e.g. Deku Bubble
|
||||
if ((gSaveContext.magicState == MAGIC_STATE_IDLE) ||
|
||||
(gSaveContext.magicState == MAGIC_STATE_CONSUME_LENS)) {
|
||||
if (gSaveContext.magicState == MAGIC_STATE_CONSUME_LENS) {
|
||||
play->actorCtx.lensActive = false;
|
||||
}
|
||||
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_DRANK_CHATEAU_ROMANI)) {
|
||||
magicToConsume = 0;
|
||||
}
|
||||
gSaveContext.magicToConsume = magicToConsume;
|
||||
gSaveContext.magicState = MAGIC_STATE_CONSUME_SETUP;
|
||||
return true;
|
||||
} else {
|
||||
Audio_PlaySfx(NA_SE_SY_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
case MAGIC_CONSUME_WAIT_NO_PREVIEW:
|
||||
// Sets consume target but waits to consume.
|
||||
// No yellow magic to preview target consumption.
|
||||
if ((gSaveContext.magicState == MAGIC_STATE_IDLE) ||
|
||||
(gSaveContext.magicState == MAGIC_STATE_CONSUME_LENS)) {
|
||||
if (gSaveContext.magicState == MAGIC_STATE_CONSUME_LENS) {
|
||||
play->actorCtx.lensActive = false;
|
||||
}
|
||||
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_DRANK_CHATEAU_ROMANI)) {
|
||||
magicToConsume = 0;
|
||||
}
|
||||
gSaveContext.magicToConsume = magicToConsume;
|
||||
gSaveContext.magicState = MAGIC_STATE_METER_FLASH_3;
|
||||
return true;
|
||||
} else {
|
||||
Audio_PlaySfx(NA_SE_SY_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
case MAGIC_CONSUME_LENS:
|
||||
if (gSaveContext.magicState == MAGIC_STATE_IDLE) {
|
||||
if (gSaveContext.save.saveInfo.playerData.magic != 0) {
|
||||
interfaceCtx->magicConsumptionTimer = 80;
|
||||
gSaveContext.magicState = MAGIC_STATE_CONSUME_LENS;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (gSaveContext.magicState == MAGIC_STATE_CONSUME_LENS) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
case MAGIC_CONSUME_WAIT_PREVIEW:
|
||||
// Sets consume target but waits to consume.
|
||||
// Preview consumption with a yellow bar. e.g. Spin Attack
|
||||
if ((gSaveContext.magicState == MAGIC_STATE_IDLE) ||
|
||||
(gSaveContext.magicState == MAGIC_STATE_CONSUME_LENS)) {
|
||||
if (gSaveContext.magicState == MAGIC_STATE_CONSUME_LENS) {
|
||||
play->actorCtx.lensActive = false;
|
||||
}
|
||||
gSaveContext.magicToConsume = magicToConsume;
|
||||
gSaveContext.magicState = MAGIC_STATE_METER_FLASH_2;
|
||||
return true;
|
||||
} else {
|
||||
Audio_PlaySfx(NA_SE_SY_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
case MAGIC_CONSUME_GORON_ZORA:
|
||||
// Goron spiked rolling or Zora electric barrier
|
||||
if (gSaveContext.save.saveInfo.playerData.magic != 0) {
|
||||
interfaceCtx->magicConsumptionTimer = 10;
|
||||
gSaveContext.magicState = MAGIC_STATE_CONSUME_GORON_ZORA_SETUP;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
case MAGIC_CONSUME_GIANTS_MASK:
|
||||
// Wearing Giant's Mask
|
||||
if (gSaveContext.magicState == MAGIC_STATE_IDLE) {
|
||||
if (gSaveContext.save.saveInfo.playerData.magic != 0) {
|
||||
interfaceCtx->magicConsumptionTimer = R_MAGIC_CONSUME_TIMER_GIANTS_MASK;
|
||||
gSaveContext.magicState = MAGIC_STATE_CONSUME_GIANTS_MASK;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (gSaveContext.magicState == MAGIC_STATE_CONSUME_GIANTS_MASK) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
case MAGIC_CONSUME_DEITY_BEAM:
|
||||
// Consumes magic immediately
|
||||
if ((gSaveContext.magicState == MAGIC_STATE_IDLE) ||
|
||||
(gSaveContext.magicState == MAGIC_STATE_CONSUME_LENS)) {
|
||||
if (gSaveContext.magicState == MAGIC_STATE_CONSUME_LENS) {
|
||||
play->actorCtx.lensActive = false;
|
||||
}
|
||||
if (CHECK_WEEKEVENTREG(WEEKEVENTREG_DRANK_CHATEAU_ROMANI)) {
|
||||
magicToConsume = 0;
|
||||
}
|
||||
gSaveContext.save.saveInfo.playerData.magic -= magicToConsume;
|
||||
return true;
|
||||
} else {
|
||||
Audio_PlaySfx(NA_SE_SY_ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
47
patches/culling.c
Normal file
47
patches/culling.c
Normal file
@ -0,0 +1,47 @@
|
||||
#include "global.h"
|
||||
|
||||
// Disable frustum culling for actors, but leave distance culling intact
|
||||
s32 func_800BA2FC(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projectedW) {
|
||||
if ((-actor->uncullZoneScale < projectedPos->z) &&
|
||||
(projectedPos->z < (actor->uncullZoneForward + actor->uncullZoneScale))) {
|
||||
// f32 phi_f12;
|
||||
// f32 phi_f2 = CLAMP_MIN(projectedW, 1.0f);
|
||||
// f32 phi_f14;
|
||||
// f32 phi_f16;
|
||||
|
||||
// if (play->view.fovy != 60.0f) {
|
||||
// phi_f12 = actor->uncullZoneScale * play->projectionMtxFDiagonal.x * 0.76980036f; // sqrt(16/27)
|
||||
|
||||
// phi_f14 = play->projectionMtxFDiagonal.y * 0.57735026f; // 1 / sqrt(3)
|
||||
// phi_f16 = actor->uncullZoneScale * phi_f14;
|
||||
// phi_f14 *= actor->uncullZoneDownward;
|
||||
// } else {
|
||||
// phi_f16 = phi_f12 = actor->uncullZoneScale;
|
||||
// phi_f14 = actor->uncullZoneDownward;
|
||||
// }
|
||||
|
||||
// if (((fabsf(projectedPos->x) - phi_f12) < phi_f2) && ((-phi_f2 < (projectedPos->y + phi_f14))) &&
|
||||
// ((projectedPos->y - phi_f16) < phi_f2)) {
|
||||
return true;
|
||||
// }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Override LOD to 0
|
||||
void Player_DrawGameplay(PlayState* play, Player* this, s32 lod, Gfx* cullDList,
|
||||
OverrideLimbDrawFlex overrideLimbDraw) {
|
||||
OPEN_DISPS(play->state.gfxCtx);
|
||||
|
||||
gSPSegment(POLY_OPA_DISP++, 0x0C, cullDList);
|
||||
gSPSegment(POLY_XLU_DISP++, 0x0C, cullDList);
|
||||
|
||||
lod = 0; // Force the closest LOD
|
||||
|
||||
Player_DrawImpl(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, lod,
|
||||
this->transformation, 0, this->actor.shape.face, overrideLimbDraw, Player_PostLimbDrawGameplay,
|
||||
&this->actor);
|
||||
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
}
|
21
patches/patches.ld
Normal file
21
patches/patches.ld
Normal file
@ -0,0 +1,21 @@
|
||||
RAMBASE = 0x80800100; /* Used to hold any new symbols */
|
||||
|
||||
MEMORY {
|
||||
extram : ORIGIN = RAMBASE, LENGTH = 1M
|
||||
rom : ORIGIN = 0, LENGTH = 1M
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
.text : { *(.text*) } >extram AT >rom
|
||||
.ctors : { *(.ctors*) *(.init_array*) } >extram AT >rom
|
||||
.dtors : { *(.dtors*) } >extram AT >rom
|
||||
.rodata : { *(.rodata*) } >extram AT >rom
|
||||
.data : { *(.data*) } >extram AT >rom
|
||||
.bss (NOLOAD) : { *(.bss*) *(COMMON) } >extram
|
||||
|
||||
.symtab 0 : { *(.symtab) }
|
||||
.strtab 0 : { *(.strtab) }
|
||||
.shstrtab 0 : { *(.shstrtab) }
|
||||
|
||||
/DISCARD/ : { *(*); }
|
||||
}
|
7
patches/syms.ld
Normal file
7
patches/syms.ld
Normal file
@ -0,0 +1,7 @@
|
||||
__start = 0x80000000;
|
||||
/* TODO pull these symbols from the elf file directly */
|
||||
Player_PostLimbDrawGameplay = 0x80128BD0;
|
||||
Player_DrawImpl = 0x801246F4;
|
||||
gRegEditor = 0x801f3f60;
|
||||
play_sound = 0x8019f0c8;
|
||||
gSaveContext = 0x801ef670;
|
@ -148,6 +148,7 @@ int sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||
}
|
||||
|
||||
Multilibultra::gfx_callbacks_t::gfx_data_t create_gfx() {
|
||||
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "system");
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) > 0) {
|
||||
exit_error("Failed to initialize SDL2: %s\n", SDL_GetError());
|
||||
}
|
||||
@ -158,7 +159,7 @@ Multilibultra::gfx_callbacks_t::gfx_data_t create_gfx() {
|
||||
SDL_Window* window;
|
||||
|
||||
Multilibultra::WindowHandle create_window(Multilibultra::gfx_callbacks_t::gfx_data_t) {
|
||||
window = SDL_CreateWindow("Recomp", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_RESIZABLE);
|
||||
window = SDL_CreateWindow("Recomp", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_RESIZABLE );
|
||||
|
||||
if (window == nullptr) {
|
||||
exit_error("Failed to create window: %s\n", SDL_GetError());
|
||||
|
48
src/ui/ui_events.cpp
Normal file
48
src/ui/ui_events.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "recomp_ui.h"
|
||||
#include "../../portultra/multilibultra.hpp"
|
||||
|
||||
#include "nfd.h"
|
||||
#include "RmlUi/Core.h"
|
||||
|
||||
using event_handler_t = void(Rml::Event&);
|
||||
|
||||
class UiEventListener : public Rml::EventListener {
|
||||
event_handler_t* handler_;
|
||||
public:
|
||||
UiEventListener(event_handler_t* handler) : handler_(handler) {}
|
||||
void ProcessEvent(Rml::Event& event) override {
|
||||
handler_(event);
|
||||
}
|
||||
};
|
||||
|
||||
class UiEventListenerInstancer : public Rml::EventListenerInstancer {
|
||||
std::unordered_map<Rml::String, UiEventListener> listener_map_;
|
||||
public:
|
||||
Rml::EventListener* InstanceEventListener(const Rml::String& value, Rml::Element* element) override {
|
||||
printf("Instancing event listener for %s\n", value.c_str());
|
||||
auto find_it = listener_map_.find(value);
|
||||
|
||||
if (find_it != listener_map_.end()) {
|
||||
return &find_it->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void register_event(const Rml::String& value, event_handler_t* handler) {
|
||||
listener_map_.emplace(value, UiEventListener{ handler });
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Rml::EventListenerInstancer> make_event_listener_instancer() {
|
||||
std::unique_ptr<UiEventListenerInstancer> ret = std::make_unique<UiEventListenerInstancer>();
|
||||
|
||||
ret->register_event("start_game",
|
||||
[](Rml::Event& event) {
|
||||
Multilibultra::start_game(0);
|
||||
set_current_menu(Menu::None);
|
||||
}
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
@ -5,6 +5,8 @@
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include "recomp_ui.h"
|
||||
|
||||
#include "concurrentqueue.h"
|
||||
|
||||
#include "rt64_layer.h"
|
||||
@ -124,7 +126,7 @@ class RmlRenderInterface_RT64 : public Rml::RenderInterface {
|
||||
Rml::Matrix4f transform_ = Rml::Matrix4f::Identity();
|
||||
Rml::Matrix4f mvp_ = Rml::Matrix4f::Identity();
|
||||
std::unordered_map<Rml::TextureHandle, TextureHandle> textures_{};
|
||||
Rml::TextureHandle texture_count_ = 0;
|
||||
Rml::TextureHandle texture_count_ = 1; // Start at 1 to reserve texture 0 as the 1x1 pixel white texture
|
||||
std::unique_ptr<RT64::RenderBuffer> upload_buffer_{};
|
||||
std::unique_ptr<RT64::RenderBuffer> vertex_buffer_{};
|
||||
std::unique_ptr<RT64::RenderBuffer> index_buffer_{};
|
||||
@ -251,8 +253,22 @@ public:
|
||||
}
|
||||
|
||||
uint32_t allocate_upload_data_aligned(uint32_t num_bytes, uint32_t alignment) {
|
||||
// Check if there's enough remaining room in the upload buffer to allocate the requested bytes.
|
||||
uint32_t total_bytes = num_bytes + upload_buffer_bytes_used_;
|
||||
|
||||
// Determine the amount of padding needed to meet the target alignment.
|
||||
uint32_t padding_bytes = ((upload_buffer_bytes_used_ + alignment - 1) / alignment) * alignment - upload_buffer_bytes_used_;
|
||||
|
||||
// If there isn't enough room to allocate the required bytes plus the padding then resize the upload buffer and allocate from the start of the new one.
|
||||
if (total_bytes + padding_bytes > upload_buffer_size_) {
|
||||
resize_upload_buffer(total_bytes + total_bytes / 2);
|
||||
|
||||
upload_buffer_bytes_used_ += num_bytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Otherwise allocate the padding and required bytes and offset the allocated position by the padding size.
|
||||
return allocate_upload_data(padding_bytes + num_bytes) + padding_bytes;
|
||||
}
|
||||
|
||||
@ -373,8 +389,6 @@ public:
|
||||
std::filesystem::path image_path{ source.c_str() };
|
||||
|
||||
if (image_path.extension() == ".tga") {
|
||||
printf("Opening TGA image: %s\n", image_path.u8string().c_str());
|
||||
|
||||
std::vector<char> file_data = read_file(image_path);
|
||||
|
||||
if (file_data.empty()) {
|
||||
@ -531,7 +545,7 @@ public:
|
||||
mvp_ = projection_mtx_ * transform_;
|
||||
}
|
||||
|
||||
void start(RT64::RenderCommandList* list, uint32_t image_width, uint32_t image_height, bool reload_style) {
|
||||
void start(RT64::RenderCommandList* list, uint32_t image_width, uint32_t image_height) {
|
||||
list_ = list;
|
||||
list_->setPipeline(pipeline_.get());
|
||||
list_->setGraphicsPipelineLayout(layout_.get());
|
||||
@ -546,10 +560,6 @@ public:
|
||||
// Clear out any stale buffers from the last command list.
|
||||
stale_buffers_.clear();
|
||||
|
||||
if (reload_style) {
|
||||
load_document();
|
||||
}
|
||||
|
||||
// Reset and map the upload buffer.
|
||||
upload_buffer_bytes_used_ = 0;
|
||||
upload_buffer_mapped_data_ = reinterpret_cast<uint8_t*>(upload_buffer_->map());
|
||||
@ -568,32 +578,60 @@ public:
|
||||
|
||||
struct {
|
||||
struct UIRenderContext render;
|
||||
struct {
|
||||
class {
|
||||
std::unordered_map<Menu, Rml::ElementDocument*> documents;
|
||||
Rml::ElementDocument* current_document;
|
||||
public:
|
||||
SystemInterface_SDL system_interface;
|
||||
std::unique_ptr<RmlRenderInterface_RT64> render_interface;
|
||||
Rml::Context* context;
|
||||
std::unique_ptr<Rml::EventListenerInstancer> event_listener_instancer;
|
||||
|
||||
void swap_document(Menu menu) {
|
||||
if (current_document != nullptr) {
|
||||
current_document->Hide();
|
||||
}
|
||||
|
||||
auto find_it = documents.find(menu);
|
||||
if (find_it != documents.end()) {
|
||||
assert(find_it->second && "Document for menu not loaded!");
|
||||
current_document = find_it->second;
|
||||
current_document->Show();
|
||||
}
|
||||
else {
|
||||
current_document = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void load_documents() {
|
||||
if (!documents.empty()) {
|
||||
Rml::Factory::RegisterEventListenerInstancer(nullptr);
|
||||
for (auto doc : documents) {
|
||||
doc.second->ReloadStyleSheet();
|
||||
}
|
||||
|
||||
Rml::ReleaseTextures();
|
||||
Rml::ReleaseMemoryPools();
|
||||
|
||||
if (current_document != nullptr) {
|
||||
current_document->Hide();
|
||||
current_document->Close();
|
||||
}
|
||||
|
||||
current_document = nullptr;
|
||||
|
||||
documents.clear();
|
||||
Rml::Factory::RegisterEventListenerInstancer(event_listener_instancer.get());
|
||||
}
|
||||
|
||||
documents.emplace(Menu::Launcher, context->LoadDocument("assets/launcher.rml"));
|
||||
}
|
||||
} rml;
|
||||
} UIContext;
|
||||
|
||||
// TODO make this not be global
|
||||
extern SDL_Window* window;
|
||||
|
||||
void load_document() {
|
||||
if (UIContext.render.document) {
|
||||
UIContext.render.document->ReloadStyleSheet();
|
||||
Rml::ReleaseTextures();
|
||||
Rml::ReleaseMemoryPools();
|
||||
UIContext.render.document->Hide();
|
||||
UIContext.render.document->Close();
|
||||
// Documents are owned by RmlUi, so we don't have anything to free here.
|
||||
UIContext.render.document = nullptr;
|
||||
}
|
||||
UIContext.render.document = UIContext.rml.context->LoadDocument("assets/demo.rml");
|
||||
if (UIContext.render.document) {
|
||||
UIContext.render.document->Show();
|
||||
}
|
||||
}
|
||||
|
||||
void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
||||
printf("RT64 hook init\n");
|
||||
|
||||
@ -603,9 +641,11 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
||||
// Setup RML
|
||||
UIContext.rml.system_interface.SetWindow(window);
|
||||
UIContext.rml.render_interface = std::make_unique<RmlRenderInterface_RT64>(&UIContext.render);
|
||||
UIContext.rml.event_listener_instancer = make_event_listener_instancer();
|
||||
|
||||
Rml::SetSystemInterface(&UIContext.rml.system_interface);
|
||||
Rml::SetRenderInterface(UIContext.rml.render_interface.get());
|
||||
Rml::Factory::RegisterEventListenerInstancer(UIContext.rml.event_listener_instancer.get());
|
||||
|
||||
Rml::Initialise();
|
||||
|
||||
@ -636,7 +676,7 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
||||
}
|
||||
}
|
||||
|
||||
load_document();
|
||||
UIContext.rml.load_documents();
|
||||
}
|
||||
|
||||
moodycamel::ConcurrentQueue<SDL_Event> ui_event_queue{};
|
||||
@ -649,6 +689,8 @@ bool try_deque_event(SDL_Event& out) {
|
||||
return ui_event_queue.try_dequeue(out);
|
||||
}
|
||||
|
||||
std::atomic<Menu> open_menu = Menu::Launcher;
|
||||
|
||||
void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_chain_texture) {
|
||||
int num_keys;
|
||||
const Uint8* key_state = SDL_GetKeyboardState(&num_keys);
|
||||
@ -658,20 +700,19 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_
|
||||
bool reload_sheets = is_reload_held && !was_reload_held;
|
||||
was_reload_held = is_reload_held;
|
||||
|
||||
static bool menu_open = true;
|
||||
static bool was_toggle_menu_held = false;
|
||||
bool is_toggle_menu_held = key_state[SDL_SCANCODE_M] != 0;
|
||||
if (is_toggle_menu_held && !was_toggle_menu_held) {
|
||||
menu_open = !menu_open;
|
||||
static Menu prev_menu = Menu::None;
|
||||
Menu cur_menu = open_menu.load();
|
||||
|
||||
if (reload_sheets) {
|
||||
UIContext.rml.load_documents();
|
||||
prev_menu = Menu::None;
|
||||
}
|
||||
was_toggle_menu_held = is_toggle_menu_held;
|
||||
|
||||
static bool was_start_game_held = false;
|
||||
bool is_start_game_held = key_state[SDL_SCANCODE_SPACE] != 0;
|
||||
if (is_start_game_held && !was_start_game_held) {
|
||||
Multilibultra::start_game(0);
|
||||
|
||||
if (cur_menu != prev_menu) {
|
||||
UIContext.rml.swap_document(cur_menu);
|
||||
}
|
||||
was_start_game_held = is_start_game_held;
|
||||
|
||||
prev_menu = cur_menu;
|
||||
|
||||
SDL_Event cur_event{};
|
||||
|
||||
@ -679,11 +720,11 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_
|
||||
RmlSDL::InputEventHandler(UIContext.rml.context, cur_event);
|
||||
}
|
||||
|
||||
if (menu_open) {
|
||||
if (cur_menu != Menu::None) {
|
||||
int width, height;
|
||||
SDL_GetWindowSizeInPixels(window, &width, &height);
|
||||
|
||||
UIContext.rml.render_interface->start(command_list, width, height, reload_sheets);
|
||||
UIContext.rml.render_interface->start(command_list, width, height);
|
||||
|
||||
static int prev_width = 0;
|
||||
static int prev_height = 0;
|
||||
@ -707,3 +748,7 @@ void deinit_hook() {
|
||||
void set_rt64_hooks() {
|
||||
RT64::SetRenderHooks(init_hook, draw_hook, deinit_hook);
|
||||
}
|
||||
|
||||
void set_current_menu(Menu menu) {
|
||||
open_menu.store(menu);
|
||||
}
|
1
thirdparty/nativefiledialog-extended
vendored
Submodule
1
thirdparty/nativefiledialog-extended
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 75cbdf819785d9f94855987724e30a6ba0a87e29
|
Loading…
Reference in New Issue
Block a user