From 398988a9616701b11827da6b93684c5a137b367b Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sat, 11 Nov 2023 17:42:07 -0500 Subject: [PATCH] Added function patching, began reorganizing UI code, added native file dialog library --- .gitignore | 2 + .gitmodules | 3 + CMakeLists.txt | 50 +- assets/demo.rml | 29 -- assets/invader.rcss | 704 +-------------------------- assets/invader_spritesheet.rcss | 134 +++++ assets/launcher.rml | 22 + assets/window.rml | 19 - include/recomp_ui.h | 16 + include/rsp_vu_impl.h | 8 +- patches.toml | 9 + patches/.gitignore | 5 + patches/Makefile | 32 ++ patches/cheats.c | 139 ++++++ patches/culling.c | 47 ++ patches/patches.ld | 21 + patches/syms.ld | 7 + src/main/main.cpp | 3 +- src/ui/ui_events.cpp | 48 ++ src/{ui.cpp => ui/ui_renderer.cpp} | 125 +++-- thirdparty/nativefiledialog-extended | 1 + 21 files changed, 622 insertions(+), 802 deletions(-) delete mode 100644 assets/demo.rml create mode 100644 assets/invader_spritesheet.rcss create mode 100644 assets/launcher.rml delete mode 100644 assets/window.rml create mode 100644 patches.toml create mode 100644 patches/.gitignore create mode 100644 patches/Makefile create mode 100644 patches/cheats.c create mode 100644 patches/culling.c create mode 100644 patches/patches.ld create mode 100644 patches/syms.ld create mode 100644 src/ui/ui_events.cpp rename src/{ui.cpp => ui/ui_renderer.cpp} (89%) create mode 160000 thirdparty/nativefiledialog-extended diff --git a/.gitignore b/.gitignore index 1adbe13..1664a29 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/.gitmodules b/.gitmodules index b3cb045..ef3d8ff 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ab6e61..54615d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/assets/demo.rml b/assets/demo.rml deleted file mode 100644 index acf79bb..0000000 --- a/assets/demo.rml +++ /dev/null @@ -1,29 +0,0 @@ - - - Demo - - - - - This is a sample.
- Wiseguy was here - -
diff --git a/assets/invader.rcss b/assets/invader.rcss index a31b02f..2a978ad 100644 --- a/assets/invader.rcss +++ b/assets/invader.rcss @@ -1,703 +1 @@ -@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; -} - -body -{ - font-family: LatoLatin; - font-weight: normal; - font-style: normal; - font-size: 15dp; - color: white; -} - -body.window -{ - padding-top: 43dp; - padding-bottom: 20dp; - - min-width: 250dp; - max-width: 800dp; - - min-height: 135dp; - max-height: 700dp; -} - -div#title_bar -{ - z-index: 1; - - position: absolute; - top: 7dp; - left: 0; - - text-align: left; - vertical-align: bottom; -} - -div#title_bar div#icon -{ - position: absolute; - left: 15dp; - top: -4dp; - - width: 51dp; - height: 39dp; -} - - -div#title_bar span -{ - padding-left: 85dp; - padding-right: 25dp; - padding-top: 18dp; - padding-bottom: 43dp; - - vertical-align: top; - - line-height: 24dp; - font-size: 20dp; - font-weight: bold; - - font-effect: glow(1dp black); - decorator: tiled-horizontal( title-bar-l, title-bar-c, title-bar-r ); -} - -div#window -{ - width: auto; - height: 100%; - padding: 10dp 15dp; - - decorator: tiled-box( - window-tl, window-t, window-tr, - window-l, window-c, window-r, - window-bl, window-b, window-br - ); -} - -div#content -{ - z-index: 2; - - width: auto; - height: 100%; - - overflow: hidden auto; - - text-align: center; -} - - - -p -{ - text-align: left; - margin-bottom: 1em; -} - -h1 -{ - margin-left: 0.4em; - margin-bottom: 0.4em; - - text-align: left; - font-size: 16dp; - font-weight: bold; - - font-effect: glow(1dp 1dp 1dp 1dp #1117); -} - - - -input, -select -{ - margin-left: 20dp; -} - -input.submit -{ - margin-left: 0; -} - - - -button, -input.submit -{ - display: inline-block; - - width: 159dp; - height: 33dp; - padding-top: 12dp; - - font-size: 16dp; - text-align: center; - tab-index: auto; - - decorator: image(button); -} - -button:focus, -input.submit:focus -{ - font-effect: blur(3dp #fff); -} - -button:hover, -input.submit:hover -{ - decorator: image(button-hover); -} - -button:active, -input.submit:active -{ - decorator: image(button-active); -} - -input.submit:disabled -{ - decorator: image(button); - image-color: rgba(50, 150, 150, 120); - cursor: unavailable; -} - -input.text, input.password -{ - box-sizing: border-box; - height: 31dp; - padding: 11dp 10dp 0; - decorator: tiled-horizontal( text-l, text-c, auto ); /* Right becomes mirrored left */ - cursor: text; - text-align: left; -} - -textarea -{ - padding: 14dp 12dp 10dp; - decorator: ninepatch( textarea, textarea-inner, 1.0 ); - cursor: text; - text-align: left; -} - -input.text, -input.password, -select, -textarea -{ - color: #333; - font-size: 13dp; -} - -table input.text -{ - box-sizing: border-box; - width: 100%; - height: 18dp; - margin: 0; - padding: 0 5dp; - line-height: 1.0; - - border-width: 1dp; - border-color: black; - background-color: white; - - font-size: 15dp; - - decorator: none; -} - - - -select -{ - width: 175dp; - height: 37dp; -} - -select selectvalue -{ - width: auto; - margin-right: 30dp; - - height: 25dp; - padding: 12dp 10dp 0dp 10dp; - - decorator: image( selectvalue ); -} - -select selectarrow -{ - width: 30dp; - height: 37dp; - - decorator: image( selectarrow ); -} - -select:hover selectarrow -{ - decorator: image( selectarrow-hover ); -} - -select:active selectarrow, -select selectarrow:checked -{ - decorator: image( selectarrow-active ); -} - -select selectbox -{ - margin-left: 1dp; - margin-top: -7dp; - margin-bottom: -10dp; - width: 162dp; - padding: 1dp 4dp 4dp 4dp; -} - -select selectbox, -tbody -{ - decorator: tiled-box( - selectbox-tl, selectbox-t, selectbox-tr, - selectbox-l, selectbox-c, auto, /* auto mirrors left */ - selectbox-bl, selectbox-b, selectbox-br - ); -} - -select selectbox option -{ - width: auto; - padding: 3dp 0 3dp 6dp; - background: #DDDD; -} - -select selectbox option:nth-child(even), -tr:nth-child(even) -{ - background: #FFFFFFA0; -} -select selectbox option:checked -{ - font-weight: bold; -} -select selectbox option:hover -{ - background: #FF5D5D; -} - - - -input.radio, -input.checkbox -{ - width: 30dp; - height: 30dp; - - vertical-align: -11dp; -} - -input.radio -{ - decorator: image(radio); -} - -input.radio:hover -{ - decorator: image(radio-hover); -} - -input.radio:active -{ - decorator: image(radio-active); -} - -input.radio:checked -{ - decorator: image(radio-checked); -} - -input.radio:checked:hover -{ - decorator: image(radio-checked-hover); -} - -input.radio:checked:active -{ - decorator: image(radio-checked-active); -} - -input.checkbox -{ - decorator: image(checkbox); -} - -input.checkbox:hover -{ - decorator: image(checkbox-hover); -} - -input.checkbox:active -{ - decorator: image(checkbox-active); -} - -input.checkbox:checked -{ - decorator: image(checkbox-checked); -} - -input.checkbox:checked:hover -{ - decorator: image(checkbox-checked-hover); -} - -input.checkbox:checked:active -{ - decorator: image(checkbox-checked-active); -} - -input.range { - width: 200dp; - height: 32dp; - vertical-align: -12dp; -} -input.range slidertrack { - margin-top: 3dp; - height: 22dp; - image-color: #ecc; - decorator: ninepatch( range-track, range-track-inner, 1.0 ); -} -input.range sliderbar { - margin-left: -8dp; - margin-right: -7dp; - margin-top: -3dp; - width: 34dp; - height: 23dp; - decorator: image( range-bar ); -} -input.range sliderbar:hover, input.range slidertrack:hover + sliderbar { - image-color: #cc0; -} -input.range sliderbar:active, input.range slidertrack:active + sliderbar { - image-color: #c80; -} -input.range sliderarrowdec, input.range sliderarrowinc { - width: 17dp; - height: 17dp; - margin-top: 6dp; -} -input.range sliderarrowdec { decorator: image( range-dec ); } -input.range sliderarrowinc { decorator: image( range-inc ); } -input.range sliderarrowdec:hover { decorator: image( range-dec-hover ); } -input.range sliderarrowinc:hover { decorator: image( range-inc-hover ); } -input.range sliderarrowdec:active { decorator: image( range-dec-active ); } -input.range sliderarrowinc:active { decorator: image( range-inc-active ); } - -thead tr { - height: 35dp; - decorator: tiled-horizontal( tableheader-l, tableheader-c, tableheader-r ); -} -thead td { - padding-top: 11dp; -} - -tbody { - /* Margin left/right only affects the background positioning for the decorator, not the cell placement */ - margin-left: 5dp; - margin-right: 4dp; - /* Padding top/bottom adds extra spacing between the header row and the body, and between the body and table bottom */ - padding-top: 4dp; - padding-bottom: 4dp; -} -tbody tr { - margin-left: 9dp; - margin-right: 8dp; - color: black; -} - - -expand -{ - display: block; - - margin: 1dp 0 1dp 5dp; - height: 17dp; - width: 17dp; - - decorator: image( expand ); -} - -expand:hover -{ - decorator: image( expand-hover ); -} - -expand:active -{ - decorator: image( expand-active ); -} - -expand.collapsed -{ - decorator: image( expand-collapsed ); -} - -expand.collapsed:hover -{ - decorator: image( expand-collapsed-hover ); -} - -expand.collapsed:active -{ - decorator: image( expand-collapsed-active ); -} - - -scrollbarvertical -{ - margin-top: -6dp; - margin-bottom: -6dp; - margin-right: -11dp; - width: 27dp; -} - -scrollbarvertical slidertrack -{ - decorator: tiled-vertical( slidertrack-t, slidertrack-c, slidertrack-b ); -} -scrollbarvertical slidertrack:active -{ - image-color: #aaa; -} - -scrollbarvertical sliderbar -{ - margin-left: 4dp; - width: 23dp; - min-height: 46dp; - - decorator: tiled-vertical( sliderbar-t, sliderbar-c, sliderbar-b ); -} -scrollbarvertical sliderbar:hover -{ - decorator: tiled-vertical( sliderbar-hover-t, sliderbar-hover-c, sliderbar-hover-b ); -} -scrollbarvertical sliderbar:active -{ - decorator: tiled-vertical( sliderbar-active-t, sliderbar-active-c, sliderbar-active-b ); -} - -scrollbarvertical sliderarrowdec, -scrollbarvertical sliderarrowinc -{ - width: 27dp; - height: 24dp; -} -scrollbarvertical sliderarrowdec -{ - decorator: image( sliderarrowdec ); -} -scrollbarvertical sliderarrowdec:hover -{ - decorator: image( sliderarrowdec-hover ); -} -scrollbarvertical sliderarrowdec:active -{ - decorator: image( sliderarrowdec-active ); -} - -scrollbarvertical sliderarrowinc -{ - decorator: image( sliderarrowinc ); -} -scrollbarvertical sliderarrowinc:hover -{ - decorator: image( sliderarrowinc-hover ); -} -scrollbarvertical sliderarrowinc:active -{ - decorator: image( sliderarrowinc-active ); -} - -scrollbarhorizontal -{ - width: 0; - height: 0; -} - -textarea scrollbarvertical -{ - cursor: arrow; - margin: 10dp 0 4dp 0; - width: 12dp; -} -textarea scrollbarvertical slidertrack -{ - decorator: none; -} -textarea scrollbarvertical sliderbar -{ - margin-left: 2dp; - width: 10dp; - min-height: 16dp; -} -textarea scrollbarvertical sliderarrowdec, -textarea scrollbarvertical sliderarrowinc -{ - width: 0; - height: 0; -} - -textarea scrollbarhorizontal -{ - cursor: arrow; - margin-left: 7dp; - height: 12dp; -} -textarea scrollbarhorizontal sliderbar -{ - background-color: #BC0000CC; - height: 8dp; - min-width: 10dp; -} -textarea scrollbarhorizontal sliderbar:hover -{ - background-color: #B82500CC; -} -textarea scrollbarhorizontal sliderbar:active -{ - background-color: #770000CC; -} +body{font-family:LatoLatin;font-weight:normal;font-style:normal;font-size:20dp;color:#fff}div#title_bar{z-index:1;position:absolute;top:7dp;left:0;text-align:left;vertical-align:bottom}div#title_bar div#icon{position:absolute;left:15dp;top:-4dp;width:51dp;height:39dp}div#title_bar span{padding-left:85dp;padding-right:25dp;padding-top:18dp;padding-bottom:43dp;vertical-align:top;line-height:24dp;font-size:20dp;font-weight:bold;font-effect:glow(1dp black);decorator:tiled-horizontal(title-bar-l, title-bar-c, title-bar-r)}div#window{width:100%;height:100%;box-sizing:border-box;padding:10dp 15dp;background-color:#004164;border-color:red}div#content{z-index:2;width:auto;height:100%;overflow:hidden auto;text-align:center}p{text-align:left;margin-bottom:1em}h1{margin-left:.4em;margin-bottom:.4em;text-align:left;font-size:16dp;font-weight:bold;font-effect:glow(1dp 1dp 1dp 1dp rgba(17, 17, 17, 0.4666666667))}input,select{margin-left:20dp}input.submit{margin-left:0}button,input.submit{display:inline-block;width:159dp;height:33dp;padding-top:12dp;font-size:16dp;text-align:center;tab-index:auto;decorator:image(button)}button:focus,input.submit:focus{font-effect:blur(3dp #fff)}button:hover,input.submit:hover{decorator:image(button-hover)}button:active,input.submit:active{decorator:image(button-active)}input.submit:disabled{decorator:image(button);image-color:#329696;cursor:unavailable}input.text,input.password{box-sizing:border-box;height:31dp;padding:11dp 10dp 0;decorator:tiled-horizontal(text-l, text-c, auto);cursor:text;text-align:left}textarea{padding:14dp 12dp 10dp;decorator:ninepatch(textarea, textarea-inner, 1);cursor:text;text-align:left}input.text,input.password,select,textarea{color:#333;font-size:13dp}table input.text{box-sizing:border-box;width:100%;height:18dp;margin:0;padding:0 5dp;line-height:1;border-width:1dp;border-color:#000;background-color:#fff;font-size:15dp;decorator:none}select{width:175dp;height:37dp}select selectvalue{width:auto;margin-right:30dp;height:25dp;padding:12dp 10dp 0dp 10dp;decorator:image(selectvalue)}select selectarrow{width:30dp;height:37dp;decorator:image(selectarrow)}select:hover selectarrow{decorator:image(selectarrow-hover)}select:active selectarrow,select selectarrow:checked{decorator:image(selectarrow-active)}select selectbox{margin-left:1dp;margin-top:-7dp;margin-bottom:-10dp;width:162dp;padding:1dp 4dp 4dp 4dp}select selectbox,tbody{decorator:tiled-box(selectbox-tl, selectbox-t, selectbox-tr, selectbox-l, selectbox-c, auto, selectbox-bl, selectbox-b, selectbox-br)}select selectbox option{width:auto;padding:3dp 0 3dp 6dp;background:rgba(221,221,221,.8666666667)}select selectbox option:nth-child(even),tr:nth-child(even){background:rgba(255,255,255,.6274509804)}select selectbox option:checked{font-weight:bold}select selectbox option:hover{background:#ff5d5d}input.radio,input.checkbox{width:30dp;height:30dp;vertical-align:-11dp}input.radio{decorator:image(radio)}input.radio:hover{decorator:image(radio-hover)}input.radio:active{decorator:image(radio-active)}input.radio:checked{decorator:image(radio-checked)}input.radio:checked:hover{decorator:image(radio-checked-hover)}input.radio:checked:active{decorator:image(radio-checked-active)}input.checkbox{decorator:image(checkbox)}input.checkbox:hover{decorator:image(checkbox-hover)}input.checkbox:active{decorator:image(checkbox-active)}input.checkbox:checked{decorator:image(checkbox-checked)}input.checkbox:checked:hover{decorator:image(checkbox-checked-hover)}input.checkbox:checked:active{decorator:image(checkbox-checked-active)}input.range{width:200dp;height:32dp;vertical-align:-12dp}input.range slidertrack{margin-top:3dp;height:22dp;image-color:#ecc;decorator:ninepatch(range-track, range-track-inner, 1)}input.range sliderbar{margin-left:-8dp;margin-right:-7dp;margin-top:-3dp;width:34dp;height:23dp;decorator:image(range-bar)}input.range sliderbar:hover,input.range slidertrack:hover+sliderbar{image-color:#cc0}input.range sliderbar:active,input.range slidertrack:active+sliderbar{image-color:#c80}input.range sliderarrowdec,input.range sliderarrowinc{width:17dp;height:17dp;margin-top:6dp}input.range sliderarrowdec{decorator:image(range-dec)}input.range sliderarrowinc{decorator:image(range-inc)}input.range sliderarrowdec:hover{decorator:image(range-dec-hover)}input.range sliderarrowinc:hover{decorator:image(range-inc-hover)}input.range sliderarrowdec:active{decorator:image(range-dec-active)}input.range sliderarrowinc:active{decorator:image(range-inc-active)}thead tr{height:35dp;decorator:tiled-horizontal(tableheader-l, tableheader-c, tableheader-r)}thead td{padding-top:11dp}tbody{margin-left:5dp;margin-right:4dp;padding-top:4dp;padding-bottom:4dp}tbody tr{margin-left:9dp;margin-right:8dp;color:#000}expand{display:block;margin:1dp 0 1dp 5dp;height:17dp;width:17dp;decorator:image(expand)}expand:hover{decorator:image(expand-hover)}expand:active{decorator:image(expand-active)}expand.collapsed{decorator:image(expand-collapsed)}expand.collapsed:hover{decorator:image(expand-collapsed-hover)}expand.collapsed:active{decorator:image(expand-collapsed-active)}scrollbarvertical{margin-top:-6dp;margin-bottom:-6dp;margin-right:-11dp;width:27dp}scrollbarvertical slidertrack{decorator:tiled-vertical(slidertrack-t, slidertrack-c, slidertrack-b)}scrollbarvertical slidertrack:active{image-color:#aaa}scrollbarvertical sliderbar{margin-left:4dp;width:23dp;min-height:46dp;decorator:tiled-vertical(sliderbar-t, sliderbar-c, sliderbar-b)}scrollbarvertical sliderbar:hover{decorator:tiled-vertical(sliderbar-hover-t, sliderbar-hover-c, sliderbar-hover-b)}scrollbarvertical sliderbar:active{decorator:tiled-vertical(sliderbar-active-t, sliderbar-active-c, sliderbar-active-b)}scrollbarvertical sliderarrowdec,scrollbarvertical sliderarrowinc{width:27dp;height:24dp}scrollbarvertical sliderarrowdec{decorator:image(sliderarrowdec)}scrollbarvertical sliderarrowdec:hover{decorator:image(sliderarrowdec-hover)}scrollbarvertical sliderarrowdec:active{decorator:image(sliderarrowdec-active)}scrollbarvertical sliderarrowinc{decorator:image(sliderarrowinc)}scrollbarvertical sliderarrowinc:hover{decorator:image(sliderarrowinc-hover)}scrollbarvertical sliderarrowinc:active{decorator:image(sliderarrowinc-active)}scrollbarhorizontal{width:0;height:0}textarea scrollbarvertical{cursor:arrow;margin:10dp 0 4dp 0;width:12dp}textarea scrollbarvertical slidertrack{decorator:none}textarea scrollbarvertical sliderbar{margin-left:2dp;width:10dp;min-height:16dp}textarea scrollbarvertical sliderarrowdec,textarea scrollbarvertical sliderarrowinc{width:0;height:0}textarea scrollbarhorizontal{cursor:arrow;margin-left:7dp;height:12dp}textarea scrollbarhorizontal sliderbar{background-color:rgba(188,0,0,.8);height:8dp;min-width:10dp}textarea scrollbarhorizontal sliderbar:hover{background-color:rgba(184,37,0,.8)}textarea scrollbarhorizontal sliderbar:active{background-color:rgba(119,0,0,.8)} diff --git a/assets/invader_spritesheet.rcss b/assets/invader_spritesheet.rcss new file mode 100644 index 0000000..72be1b0 --- /dev/null +++ b/assets/invader_spritesheet.rcss @@ -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; +} \ No newline at end of file diff --git a/assets/launcher.rml b/assets/launcher.rml new file mode 100644 index 0000000..52f8991 --- /dev/null +++ b/assets/launcher.rml @@ -0,0 +1,22 @@ + + + Launcher + + + + + + +
+ + This is a sample.
+ Test text +
+ +
diff --git a/assets/window.rml b/assets/window.rml deleted file mode 100644 index e368b36..0000000 --- a/assets/window.rml +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/include/recomp_ui.h b/include/recomp_ui.h index b00daed..a297b58 100644 --- a/include/recomp_ui.h +++ b/include/recomp_ui.h @@ -1,9 +1,25 @@ #ifndef __RECOMP_UI__ #define __RECOMP_UI__ +#include + #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 make_event_listener_instancer(); + +enum class Menu { + Launcher, + None +}; + +void set_current_menu(Menu menu); + #endif diff --git a/include/rsp_vu_impl.h b/include/rsp_vu_impl.h index 8c22d14..dbf46b2 100644 --- a/include/rsp_vu_impl.h +++ b/include/rsp_vu_impl.h @@ -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)); } } diff --git a/patches.toml b/patches.toml new file mode 100644 index 0000000..829dc19 --- /dev/null +++ b/patches.toml @@ -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 diff --git a/patches/.gitignore b/patches/.gitignore new file mode 100644 index 0000000..5b5151a --- /dev/null +++ b/patches/.gitignore @@ -0,0 +1,5 @@ +*.d +*.o +*.elf +*.bin +./funcs.h diff --git a/patches/Makefile b/patches/Makefile new file mode 100644 index 0000000..c53ede8 --- /dev/null +++ b/patches/Makefile @@ -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 diff --git a/patches/cheats.c b/patches/cheats.c new file mode 100644 index 0000000..41aff89 --- /dev/null +++ b/patches/cheats.c @@ -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; +} diff --git a/patches/culling.c b/patches/culling.c new file mode 100644 index 0000000..6d83a64 --- /dev/null +++ b/patches/culling.c @@ -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); +} diff --git a/patches/patches.ld b/patches/patches.ld new file mode 100644 index 0000000..d92992a --- /dev/null +++ b/patches/patches.ld @@ -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/ : { *(*); } +} diff --git a/patches/syms.ld b/patches/syms.ld new file mode 100644 index 0000000..3dbbf77 --- /dev/null +++ b/patches/syms.ld @@ -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; diff --git a/src/main/main.cpp b/src/main/main.cpp index 8611fd1..ab7aafb 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -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()); diff --git a/src/ui/ui_events.cpp b/src/ui/ui_events.cpp new file mode 100644 index 0000000..355b4d3 --- /dev/null +++ b/src/ui/ui_events.cpp @@ -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 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 make_event_listener_instancer() { + std::unique_ptr ret = std::make_unique(); + + ret->register_event("start_game", + [](Rml::Event& event) { + Multilibultra::start_game(0); + set_current_menu(Menu::None); + } + ); + + return ret; +} diff --git a/src/ui.cpp b/src/ui/ui_renderer.cpp similarity index 89% rename from src/ui.cpp rename to src/ui/ui_renderer.cpp index 899db7f..132a26b 100644 --- a/src/ui.cpp +++ b/src/ui/ui_renderer.cpp @@ -5,6 +5,8 @@ #include #include +#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 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 upload_buffer_{}; std::unique_ptr vertex_buffer_{}; std::unique_ptr 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 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(upload_buffer_->map()); @@ -568,32 +578,60 @@ public: struct { struct UIRenderContext render; - struct { + class { + std::unordered_map documents; + Rml::ElementDocument* current_document; + public: SystemInterface_SDL system_interface; std::unique_ptr render_interface; Rml::Context* context; + std::unique_ptr 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(&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 ui_event_queue{}; @@ -649,6 +689,8 @@ bool try_deque_event(SDL_Event& out) { return ui_event_queue.try_dequeue(out); } +std::atomic 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); +} \ No newline at end of file diff --git a/thirdparty/nativefiledialog-extended b/thirdparty/nativefiledialog-extended new file mode 160000 index 0000000..75cbdf8 --- /dev/null +++ b/thirdparty/nativefiledialog-extended @@ -0,0 +1 @@ +Subproject commit 75cbdf819785d9f94855987724e30a6ba0a87e29