diff --git a/BUILDING.md b/BUILDING.md index f68794783..4abfabe05 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -167,6 +167,27 @@ cmake --build build-switch --target soh_nro # To develop the project open the repository in VSCode (or your preferred editor) ``` +## Wii U +1. Requires that your build machine is setup with the tools necessary for your platform above +2. Requires that you have the Wii U build tools installed +3. Clone the Ship of Harkinian repository +4. Place one or more [compatible](#compatible-roms) roms in the `OTRExporter` directory with namings of your choice + +```bash +cd Shipwright +# Setup cmake project for your host machine +cmake -H. -Bbuild-cmake -GNinja +# Extract assets & generate OTR (run this anytime you need to regenerate OTR) +cmake --build build-cmake --target ExtractAssets +# Setup cmake project for building for Wii U +cmake -H. -Bbuild-wiiu -GNinja -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/WiiU.cmake # -DCMAKE_BUILD_TYPE:STRING=Release (if you're packaging) +# Build project and generate rpx +cmake --build build-wiiu --target soh + +# Now you can run the executable in ./build-wiiu/soh/soh.rpx +# To develop the project open the repository in VSCode (or your preferred editor) +``` + # Compatible Roms ``` OOT_PAL_GC checksum 0x09465AC3 diff --git a/CMakeLists.txt b/CMakeLists.txt index ef21271f5..4dacee36a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,7 +122,7 @@ add_subdirectory(ZAPDTR/ZAPD ${CMAKE_BINARY_DIR}/ZAPD) add_subdirectory(ZAPDTR/ZAPDUtils ${CMAKE_BINARY_DIR}/ZAPDUtils) add_subdirectory(OTRExporter) add_subdirectory(soh) -if(NOT CMAKE_SYSTEM_NAME MATCHES "Darwin|NintendoSwitch") +if(NOT CMAKE_SYSTEM_NAME MATCHES "Darwin|NintendoSwitch|CafeOS") add_subdirectory(OTRGui) endif() @@ -179,13 +179,13 @@ add_custom_target(CreateOSXIcons add_dependencies(soh CreateOSXIcons) endif() -if(CMAKE_SYSTEM_NAME MATCHES "Windows|NintendoSwitch") +if(CMAKE_SYSTEM_NAME MATCHES "Windows|NintendoSwitch|CafeOS") INSTALL(FILES ${CMAKE_SOURCE_DIR}/README.md DESTINATION . COMPONENT ship RENAME readme.txt ) endif() if(CMAKE_SYSTEM_NAME MATCHES "Linux") set(CPACK_GENERATOR "External") -elseif(CMAKE_SYSTEM_NAME MATCHES "Windows|NintendoSwitch") +elseif(CMAKE_SYSTEM_NAME MATCHES "Windows|NintendoSwitch|CafeOS") set(CPACK_GENERATOR "ZIP") elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin") set(CPACK_GENERATOR "Bundle") diff --git a/Dockerfile b/Dockerfile index 1c23dafa4..3115970bb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,12 +56,13 @@ RUN \ echo "deb [signed-by=/usr/local/share/keyring/devkitpro-pub.gpg] https://apt.devkitpro.org stable main" > /etc/apt/sources.list.d/devkitpro.list && \ apt-get update -y && \ apt-get install -y devkitpro-pacman && \ - yes | dkp-pacman -Syu switch-dev switch-portlibs --noconfirm + yes | dkp-pacman -Syu switch-dev switch-portlibs wiiu-dev wiiu-portlibs --noconfirm ENV DEVKITPRO=/opt/devkitpro ENV DEVKITARM=/opt/devkitpro/devkitARM ENV DEVKITPPC=/opt/devkitpro/devkitPPC -ENV PATH=$PATH:/opt/devkitpro/portlibs/switch/bin/ +ENV PATH=$PATH:/opt/devkitpro/portlibs/switch/bin/:$DEVKITPPC/bin +ENV WUT_ROOT=$DEVKITPRO/wut RUN mkdir /soh WORKDIR /soh diff --git a/Jenkinsfile b/Jenkinsfile index c61fb682c..ca6c9aef5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -185,6 +185,43 @@ pipeline { } } } + stage ('Build Wii U') { + agent { + label "SoH-Linux-Builders" + } + steps { + checkout([ + $class: 'GitSCM', + branches: scm.branches, + doGenerateSubmoduleConfigurations: scm.doGenerateSubmoduleConfigurations, + extensions: scm.extensions, + userRemoteConfigs: scm.userRemoteConfigs + ]) + catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { + unstash 'assets' + sh ''' + if docker ps -aq --filter "name=sohwiiucont" | grep -q .; then docker rm -f sohwiiucont; fi + docker build . -t sohwiiu + docker run --name sohwiiucont -dit --rm -v $(pwd):/soh sohwiiu /bin/bash + docker exec sohwiiucont scripts/wiiu/build.sh + + mv build-wiiu/soh/*.rpx soh.rpx + mv README.md readme.txt + + 7z a soh-wiiu.7z soh.rpx readme.txt + + ''' + } + archiveArtifacts artifacts: 'soh-wiiu.7z', followSymlinks: false, onlyIfSuccessful: true + } + post { + always { + sh 'sudo docker container stop sohwiiucont' + sh 'docker images --quiet --filter=dangling=true | xargs --no-run-if-empty docker rmi' // Clean dangling docker images + step([$class: 'WsCleanup']) // Clean workspace + } + } + } } } } diff --git a/StormLib/src/FileStream.cpp b/StormLib/src/FileStream.cpp index da1d0438d..323a91f13 100644 --- a/StormLib/src/FileStream.cpp +++ b/StormLib/src/FileStream.cpp @@ -34,7 +34,12 @@ // Local functions - platform-specific functions #ifndef STORMLIB_WINDOWS + +#ifndef STORMLIB_WIIU // WIIU doesn't support thread_local static thread_local DWORD dwLastError = ERROR_SUCCESS; +#else +static DWORD dwLastError = ERROR_SUCCESS; +#endif DWORD GetLastError() { diff --git a/StormLib/src/SBaseCommon.cpp b/StormLib/src/SBaseCommon.cpp index e8b3ef182..79ae4b9b5 100644 --- a/StormLib/src/SBaseCommon.cpp +++ b/StormLib/src/SBaseCommon.cpp @@ -2005,7 +2005,7 @@ void ConvertTMPQHeader(void *header, uint16_t version) TMPQHeader * theHeader = (TMPQHeader *)header; // Swap header part version 1 - if(version == MPQ_FORMAT_VERSION_1) + if(version >= MPQ_FORMAT_VERSION_1) { theHeader->dwID = SwapUInt32(theHeader->dwID); theHeader->dwHeaderSize = SwapUInt32(theHeader->dwHeaderSize); @@ -2018,21 +2018,21 @@ void ConvertTMPQHeader(void *header, uint16_t version) theHeader->dwBlockTableSize = SwapUInt32(theHeader->dwBlockTableSize); } - if(version == MPQ_FORMAT_VERSION_2) + if(version >= MPQ_FORMAT_VERSION_2) { theHeader->HiBlockTablePos64 = SwapUInt64(theHeader->HiBlockTablePos64); theHeader->wHashTablePosHi = SwapUInt16(theHeader->wHashTablePosHi); theHeader->wBlockTablePosHi = SwapUInt16(theHeader->wBlockTablePosHi); } - if(version == MPQ_FORMAT_VERSION_3) + if(version >= MPQ_FORMAT_VERSION_3) { theHeader->ArchiveSize64 = SwapUInt64(theHeader->ArchiveSize64); theHeader->BetTablePos64 = SwapUInt64(theHeader->BetTablePos64); theHeader->HetTablePos64 = SwapUInt64(theHeader->HetTablePos64); } - if(version == MPQ_FORMAT_VERSION_4) + if(version >= MPQ_FORMAT_VERSION_4) { theHeader->HashTableSize64 = SwapUInt64(theHeader->HashTableSize64); theHeader->BlockTableSize64 = SwapUInt64(theHeader->BlockTableSize64); diff --git a/StormLib/src/SFileAttributes.cpp b/StormLib/src/SFileAttributes.cpp index e43781324..eb9c9ce07 100644 --- a/StormLib/src/SFileAttributes.cpp +++ b/StormLib/src/SFileAttributes.cpp @@ -180,7 +180,7 @@ static DWORD LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAtt if((pbAttrPtr + cbArraySize) > pbAttrFileEnd) return ERROR_FILE_CORRUPT; - BSWAP_ARRAY32_UNSIGNED(ArrayCRC32, cbCRC32Size); + BSWAP_ARRAY32_UNSIGNED(ArrayCRC32, cbArraySize); for(i = 0; i < dwAttributesEntries; i++) ha->pFileTable[i].dwCrc32 = ArrayCRC32[i]; pbAttrPtr += cbArraySize; @@ -196,7 +196,7 @@ static DWORD LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAtt if((pbAttrPtr + cbArraySize) > pbAttrFileEnd) return ERROR_FILE_CORRUPT; - BSWAP_ARRAY64_UNSIGNED(ArrayFileTime, cbFileTimeSize); + BSWAP_ARRAY64_UNSIGNED(ArrayFileTime, cbArraySize); for(i = 0; i < dwAttributesEntries; i++) ha->pFileTable[i].FileTime = ArrayFileTime[i]; pbAttrPtr += cbArraySize; diff --git a/StormLib/src/SFilePatchArchives.cpp b/StormLib/src/SFilePatchArchives.cpp index d8c104f7c..03d567441 100644 --- a/StormLib/src/SFilePatchArchives.cpp +++ b/StormLib/src/SFilePatchArchives.cpp @@ -480,7 +480,7 @@ static bool IsMatchingPatchFile( { // Load the patch header SFileReadFile(hFile, &PatchHeader, sizeof(MPQ_PATCH_HEADER), &dwTransferred, NULL); - BSWAP_ARRAY32_UNSIGNED(pPatchHeader, sizeof(DWORD) * 6); + BSWAP_ARRAY32_UNSIGNED(&PatchHeader, sizeof(DWORD) * 6); // If the file contains an incremental patch, // compare the "MD5 before patching" with the base file MD5 diff --git a/StormLib/src/StormLib.h b/StormLib/src/StormLib.h index 4652ecad4..7391e9ccf 100644 --- a/StormLib/src/StormLib.h +++ b/StormLib/src/StormLib.h @@ -631,8 +631,8 @@ typedef struct _TMPQHash #else - BYTE Platform; BYTE Reserved; + BYTE Platform; USHORT lcLocale; #endif diff --git a/StormLib/src/StormPort.h b/StormLib/src/StormPort.h index 77195f88d..e7ae39d61 100644 --- a/StormLib/src/StormPort.h +++ b/StormLib/src/StormPort.h @@ -254,6 +254,34 @@ #endif +//----------------------------------------------------------------------------- +// Defines for Wii U platform + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__WIIU__) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #undef STORMLIB_LITTLE_ENDIAN // Wii U is always big endian + + #define STORMLIB_MAC // Use Mac compatible code + #define STORMLIB_WIIU + #define STORMLIB_PLATFORM_DEFINED + +#endif + //----------------------------------------------------------------------------- // Assumption: If the platform is not defined, assume a Linux-like platform diff --git a/ZAPDTR/ZAPD/CMakeLists.txt b/ZAPDTR/ZAPD/CMakeLists.txt index 748793885..cf49ef6d1 100644 --- a/ZAPDTR/ZAPD/CMakeLists.txt +++ b/ZAPDTR/ZAPD/CMakeLists.txt @@ -465,6 +465,12 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") PNG::PNG Threads::Threads ) +elseif(CMAKE_SYSTEM_NAME STREQUAL "CafeOS") + set(ADDITIONAL_LIBRARY_DEPENDENCIES + "ZAPDUtils;" + "libultraship;" + PNG::PNG + ) else() set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) @@ -478,7 +484,7 @@ else() ) endif() -if(CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") +if(CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|CafeOS") add_library(pathconf OBJECT pathconf.c) target_link_libraries(${PROJECT_NAME} PRIVATE "${ADDITIONAL_LIBRARY_DEPENDENCIES}" $ ) else() diff --git a/ZAPDTR/ZAPDUtils/Utils/BinaryReader.cpp b/ZAPDTR/ZAPDUtils/Utils/BinaryReader.cpp index 5916ce982..092124a41 100644 --- a/ZAPDTR/ZAPDUtils/Utils/BinaryReader.cpp +++ b/ZAPDTR/ZAPDUtils/Utils/BinaryReader.cpp @@ -130,7 +130,13 @@ float BinaryReader::ReadSingle() stream->Read((char*)&result, sizeof(float)); if (endianness != Endianness::Native) - result = BitConverter::ToFloatBE((uint8_t*)&result, 0); + { + float tmp; + char* dst = (char*)&tmp; + char* src = (char*)&result; + dst[3] = src[0]; dst[2] = src[1]; dst[1] = src[2]; dst[0] = src[3]; + result = tmp; + } if (std::isnan(result)) throw std::runtime_error("BinaryReader::ReadSingle(): Error reading stream"); @@ -145,7 +151,14 @@ double BinaryReader::ReadDouble() stream->Read((char*)&result, sizeof(double)); if (endianness != Endianness::Native) - result = BitConverter::ToDoubleBE((uint8_t*)&result, 0); + { + double tmp; + char* dst = (char*)&tmp; + char* src = (char*)&result; + dst[7] = src[0]; dst[6] = src[1]; dst[5] = src[2]; dst[4] = src[3]; + dst[3] = src[4]; dst[2] = src[5]; dst[1] = src[6]; dst[0] = src[7]; + result = tmp; + } if (std::isnan(result)) throw std::runtime_error("BinaryReader::ReadDouble(): Error reading stream"); diff --git a/ZAPDTR/ZAPDUtils/Utils/BinaryWriter.cpp b/ZAPDTR/ZAPDUtils/Utils/BinaryWriter.cpp index aa7840f01..34bcad0f3 100644 --- a/ZAPDTR/ZAPDUtils/Utils/BinaryWriter.cpp +++ b/ZAPDTR/ZAPDUtils/Utils/BinaryWriter.cpp @@ -107,7 +107,13 @@ void BinaryWriter::Write(uint64_t value) void BinaryWriter::Write(float value) { if (endianness != Endianness::Native) - value = BitConverter::ToFloatBE((uint8_t*)&value, 0); + { + float tmp; + char* dst = (char*)&tmp; + char* src = (char*)&value; + dst[3] = src[0]; dst[2] = src[1]; dst[1] = src[2]; dst[0] = src[3]; + value = tmp; + } stream->Write((char*)&value, sizeof(float)); } @@ -115,7 +121,14 @@ void BinaryWriter::Write(float value) void BinaryWriter::Write(double value) { if (endianness != Endianness::Native) - value = BitConverter::ToDoubleBE((uint8_t*)&value, 0); + { + double tmp; + char* dst = (char*)&tmp; + char* src = (char*)&value; + dst[7] = src[0]; dst[6] = src[1]; dst[5] = src[2]; dst[4] = src[3]; + dst[3] = src[4]; dst[2] = src[5]; dst[1] = src[6]; dst[0] = src[7]; + value = tmp; + } stream->Write((char*)&value, sizeof(double)); } diff --git a/ZAPDTR/ZAPDUtils/Utils/StringHelper.cpp b/ZAPDTR/ZAPDUtils/Utils/StringHelper.cpp index 051e9a87d..26a8af38f 100644 --- a/ZAPDTR/ZAPDUtils/Utils/StringHelper.cpp +++ b/ZAPDTR/ZAPDUtils/Utils/StringHelper.cpp @@ -64,7 +64,11 @@ void StringHelper::ReplaceOriginal(std::string& str, const std::string& from, co bool StringHelper::StartsWith(const std::string& s, const std::string& input) { +#if __cplusplus >= 202002L + return s.starts_with(input.c_str()); +#else return s.rfind(input, 0) == 0; +#endif } bool StringHelper::Contains(const std::string& s, const std::string& input) diff --git a/libultraship/libultraship/CMakeLists.txt b/libultraship/libultraship/CMakeLists.txt index 6995fa3fb..d1017387b 100644 --- a/libultraship/libultraship/CMakeLists.txt +++ b/libultraship/libultraship/CMakeLists.txt @@ -86,11 +86,17 @@ set(Source_Files__Controller "InputEditor.h" "KeyboardController.cpp" "KeyboardController.h" - "SDLController.cpp" - "SDLController.h" "UltraController.h" "DummyController.h" ) + +if (NOT CMAKE_SYSTEM_NAME STREQUAL "CafeOS") +list(APPEND Source_Files__Controller + "SDLController.cpp" + "SDLController.h" +) +endif() + source_group("Source Files\\Controller" FILES ${Source_Files__Controller}) set(Source_Files__Controller__Attachment @@ -150,16 +156,21 @@ source_group("Source Files\\Lib" FILES ${Source_Files__Lib}) set(Source_Files__Lib__Fast3D "Lib/Fast3D/gfx_cc.cpp" "Lib/Fast3D/gfx_cc.h" - "Lib/Fast3D/gfx_opengl.cpp" - "Lib/Fast3D/gfx_opengl.h" "Lib/Fast3D/gfx_pc.cpp" "Lib/Fast3D/gfx_pc.h" "Lib/Fast3D/gfx_rendering_api.h" "Lib/Fast3D/gfx_screen_config.h" - "Lib/Fast3D/gfx_sdl.h" - "Lib/Fast3D/gfx_sdl2.cpp" "Lib/Fast3D/gfx_window_manager_api.h" ) + +if (NOT CMAKE_SYSTEM_NAME STREQUAL "CafeOS") +list(APPEND Source_Files__Lib__Fast3D + "Lib/Fast3D/gfx_opengl.cpp" + "Lib/Fast3D/gfx_opengl.h" + "Lib/Fast3D/gfx_sdl.h" + "Lib/Fast3D/gfx_sdl2.cpp" +) +endif() source_group("Source Files\\Lib\\Fast3D" FILES ${Source_Files__Lib__Fast3D}) if (CMAKE_SYSTEM_NAME STREQUAL "Windows") @@ -179,14 +190,19 @@ set(Source_Files__Lib__Fast3D__extra "Lib/Fast3D/gfx_glx.cpp" "Lib/Fast3D/gfx_glx.h" ) +elseif (CMAKE_SYSTEM_NAME STREQUAL "CafeOS") +set(Source_Files__Lib__Fast3D__extra + "Lib/Fast3D/gfx_wiiu.cpp" + "Lib/Fast3D/gfx_wiiu.h" + "Lib/Fast3D/gfx_gx2.cpp" + "Lib/Fast3D/gfx_gx2.h" + "Lib/Fast3D/gx2_shader_gen.c" + "Lib/Fast3D/gx2_shader_gen.h" +) endif() source_group("Source Files\\Lib\\Fast3D\\extra" FILES ${Source_Files__Lib__Fast3D__extra}) set(Source_Files__Lib__ImGui - "Lib/ImGui/backends/imgui_impl_opengl3.cpp" - "Lib/ImGui/backends/imgui_impl_opengl3.h" - "Lib/ImGui/backends/imgui_impl_sdl.cpp" - "Lib/ImGui/backends/imgui_impl_sdl.h" "Lib/ImGui/imconfig.h" "Lib/ImGui/imgui.cpp" "Lib/ImGui/imgui.h" @@ -199,6 +215,16 @@ set(Source_Files__Lib__ImGui "Lib/ImGui/imstb_textedit.h" "Lib/ImGui/imstb_truetype.h" ) + +if (NOT CMAKE_SYSTEM_NAME STREQUAL "CafeOS") +list(APPEND Source_Files__Lib__ImGui + "Lib/ImGui/backends/imgui_impl_opengl3.cpp" + "Lib/ImGui/backends/imgui_impl_opengl3.h" + "Lib/ImGui/backends/imgui_impl_sdl.cpp" + "Lib/ImGui/backends/imgui_impl_sdl.h" +) +endif() + if (CMAKE_SYSTEM_NAME STREQUAL "Windows") set(Source_Files__Lib__ImGui__Windows "Lib/ImGui/backends/imgui_impl_dx11.cpp" @@ -206,8 +232,19 @@ set(Source_Files__Lib__ImGui__Windows "Lib/ImGui/backends/imgui_impl_win32.cpp" "Lib/ImGui/backends/imgui_impl_win32.h" ) +elseif (CMAKE_SYSTEM_NAME STREQUAL "CafeOS") +set(Source_Files__Lib__ImGui__WiiU + "Lib/ImGui/backends/wiiu/imgui_impl_wiiu.cpp" + "Lib/ImGui/backends/wiiu/imgui_impl_wiiu.h" + "Lib/ImGui/backends/wiiu/imgui_impl_gx2.cpp" + "Lib/ImGui/backends/wiiu/imgui_impl_gx2.h" +) endif () -source_group("Source Files\\Lib\\ImGui" FILES ${Source_Files__Lib__ImGui} ${Source_Files__Lib__ImGui__Windows}) +source_group("Source Files\\Lib\\ImGui" FILES + ${Source_Files__Lib__ImGui} + ${Source_Files__Lib__ImGui__Windows} + ${Source_Files__Lib__ImGui__WiiU} +) set(Source_Files__Lib__Mercury "Lib/Mercury/Mercury.cpp" @@ -316,6 +353,18 @@ set(Source_Files__NintendoSwitch source_group("Source Files\\NintendoSwitch" FILES ${Source_Files__NintendoSwitch}) endif() +if (CMAKE_SYSTEM_NAME STREQUAL "CafeOS") +set(Source_Files__CafeOS + "WiiUController.cpp" + "WiiUController.h" + "WiiUGamepad.cpp" + "WiiUGamepad.h" + "WiiUImpl.cpp" + "WiiUImpl.h" +) +source_group("Source Files\\CafeOS" FILES ${Source_Files__CafeOS}) +endif() + set(ALL_FILES ${Header_Files__Resources__Factories} ${Header_Files__Resources__Files} @@ -332,6 +381,7 @@ set(ALL_FILES ${Source_Files__Lib__Fast3D__extra} ${Source_Files__Lib__ImGui} ${Source_Files__Lib__ImGui__Windows} + ${Source_Files__Lib__ImGui__WiiU} ${Source_Files__Lib__Mercury} ${Source_Files__Lib__stb} ${Source_Files__Lib__dr_libs} @@ -343,6 +393,7 @@ set(ALL_FILES ${Source_Files__Resources__mpq} ${Source_Files__Darwin} ${Source_Files__NintendoSwitch} + ${Source_Files__CafeOS} ) ################################################################################ @@ -445,9 +496,21 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows") STORMLIB_NO_AUTO_LINK ) endif() -endif() -if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang") +elseif (CMAKE_SYSTEM_NAME STREQUAL "CafeOS") + target_compile_definitions(${PROJECT_NAME} PRIVATE + "$<$:" + "_DEBUG" + ">" + "$<$:" + "NDEBUG" + ">" + "SPDLOG_ACTIVE_LEVEL=3;" + "SPDLOG_NO_THREAD_ID;" + "SPDLOG_NO_TLS;" + "STBI_NO_THREAD_LOCALS;" + ) +elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang") target_compile_definitions(${PROJECT_NAME} PRIVATE "$<$:" "_DEBUG" @@ -462,7 +525,7 @@ endif() ################################################################################ # Compile and link options ################################################################################ -if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows|NintendoSwitch") +if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows|NintendoSwitch|CafeOS") find_package(SDL2) find_package(GLEW) find_package(X11) @@ -485,7 +548,7 @@ if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows|NintendoSwitch") set(GLEW-INCLUDE ${GLEW_INCLUDE_DIRS}) endif() set(SDL2-INCLUDE ${SDL2_INCLUDE_DIRS}) -elseif (CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch") +elseif (CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|CafeOS") find_package(SDL2) else() set(GLEW-INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/Lib/GLEW/) @@ -627,6 +690,15 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") SDL2::SDL2 Threads::Threads ) +elseif(CMAKE_SYSTEM_NAME STREQUAL "CafeOS") + find_package(SDL2 REQUIRED) + target_link_libraries(${PROJECT_NAME} + storm + SDL2::SDL2-static + ) + target_include_directories(${PROJECT_NAME} PRIVATE + ${DEVKITPRO}/portlibs/wiiu/include/ + ) else() target_link_libraries(${PROJECT_NAME} SDL2::SDL2 diff --git a/libultraship/libultraship/ControlDeck.cpp b/libultraship/libultraship/ControlDeck.cpp index 730713597..0ab8e1371 100644 --- a/libultraship/libultraship/ControlDeck.cpp +++ b/libultraship/libultraship/ControlDeck.cpp @@ -3,11 +3,17 @@ #include "Window.h" #include "Controller.h" #include "DummyController.h" -#include "KeyboardController.h" -#include "SDLController.h" #include #include "Cvar.h" +#ifndef __WIIU__ +#include "KeyboardController.h" +#include "SDLController.h" +#else +#include "WiiUGamepad.h" +#include "WiiUController.h" +#endif + namespace Ship { void ControlDeck::Init(uint8_t* bits) { @@ -20,6 +26,7 @@ namespace Ship { virtualDevices.clear(); physicalDevices.clear(); +#ifndef __WIIU__ for (int i = 0; i < SDL_NumJoysticks(); i++) { if (SDL_IsGameController(i)) { auto sdl = std::make_shared(i); @@ -30,6 +37,20 @@ namespace Ship { physicalDevices.push_back(std::make_shared("Auto", "Auto", true)); physicalDevices.push_back(std::make_shared()); +#else + physicalDevices.push_back(std::make_shared("Auto", "Auto", true)); + + auto gamepad = std::make_shared(); + gamepad->Open(); + physicalDevices.push_back(gamepad); + + for (int i = 0; i < 4; i++) { + auto controller = std::make_shared((WPADChan) i); + controller->Open(); + physicalDevices.push_back(controller); + } +#endif + physicalDevices.push_back(std::make_shared("Disconnected", "None", false)); for (const auto& device : physicalDevices) { diff --git a/libultraship/libultraship/Controller.cpp b/libultraship/libultraship/Controller.cpp index 02dee6845..cb5df333f 100644 --- a/libultraship/libultraship/Controller.cpp +++ b/libultraship/libultraship/Controller.cpp @@ -21,7 +21,9 @@ namespace Ship { void Controller::Read(OSContPad* pad, int32_t virtualSlot) { ReadFromSource(virtualSlot); +#ifndef __WIIU__ SDL_PumpEvents(); +#endif // Button Inputs pad->button |= getPressedButtons(virtualSlot) & 0xFFFF; diff --git a/libultraship/libultraship/GlobalCtx2.cpp b/libultraship/libultraship/GlobalCtx2.cpp index c5597d7c9..148a062ef 100644 --- a/libultraship/libultraship/GlobalCtx2.cpp +++ b/libultraship/libultraship/GlobalCtx2.cpp @@ -11,6 +11,8 @@ #include "OSXFolderManager.h" #elif defined(__SWITCH__) #include "SwitchImpl.h" +#elif defined(__WIIU__) +#include "WiiUImpl.h" #endif namespace Ship { @@ -59,7 +61,6 @@ namespace Ship { void GlobalCtx2::InitWindow() { InitLogging(); Config = std::make_shared(GetPathRelativeToAppDirectory("shipofharkinian.json")); - Config->reload(); MainPath = Config->getString("Game.Main Archive", GetPathRelativeToAppDirectory("oot.otr")); PatchesPath = Config->getString("Game.Patches Archive", GetAppDirectoryPath() + "/mods"); @@ -73,6 +74,8 @@ namespace Ship { MessageBox(nullptr, L"Main OTR file not found!", L"Uh oh", MB_OK); #elif defined(__SWITCH__) printf("Main OTR file not found!\n"); +#elif defined(__WIIU__) + Ship::WiiU::ThrowMissingOTR(MainPath.c_str()); #else SPDLOG_ERROR("Main OTR file not found!"); #endif @@ -85,28 +88,36 @@ namespace Ship { void GlobalCtx2::InitLogging() { try { - auto logPath = GetPathRelativeToAppDirectory(("logs/" + GetName() + ".log").c_str()); - // Setup Logging spdlog::init_thread_pool(8192, 1); + std::vector Sinks; + auto SohConsoleSink = std::make_shared(); SohConsoleSink->set_level(spdlog::level::trace); -#if defined(__linux__) + Sinks.push_back(SohConsoleSink); + +#if (!defined(_WIN32) && !defined(__WIIU__)) || defined(_DEBUG) auto ConsoleSink = std::make_shared(); ConsoleSink->set_level(spdlog::level::trace); + Sinks.push_back(ConsoleSink); #endif + +#ifndef __WIIU__ + auto logPath = GetPathRelativeToAppDirectory(("logs/" + GetName() + ".log").c_str()); auto FileSink = std::make_shared(logPath, 1024 * 1024 * 10, 10); FileSink->set_level(spdlog::level::trace); - std::vector Sinks{ -#if defined(__linux__) - ConsoleSink, + Sinks.push_back(FileSink); #endif - FileSink, - SohConsoleSink - }; + Logger = std::make_shared(GetName(), Sinks.begin(), Sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block); GetLogger()->set_level(spdlog::level::trace); + +#ifndef __WIIU__ GetLogger()->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%@] [%l] %v"); +#else + GetLogger()->set_pattern("[%s:%#] [%l] %v"); +#endif + spdlog::register_logger(GetLogger()); spdlog::set_default_logger(GetLogger()); } diff --git a/libultraship/libultraship/ImGuiImpl.cpp b/libultraship/libultraship/ImGuiImpl.cpp index 870bfd532..fe5cd076d 100644 --- a/libultraship/libultraship/ImGuiImpl.cpp +++ b/libultraship/libultraship/ImGuiImpl.cpp @@ -30,6 +30,16 @@ #include "Lib/spdlog/include/spdlog/common.h" #include "UltraController.h" +#ifdef __WIIU__ +#include // GX2SetViewport / GX2SetScissor + +#include "Lib/ImGui/backends/wiiu/imgui_impl_gx2.h" +#include "Lib/ImGui/backends/wiiu/imgui_impl_wiiu.h" + +#include "Lib/Fast3D/gfx_wiiu.h" +#include "Lib/Fast3D/gfx_gx2.h" +#endif + #if __APPLE__ #include #else @@ -101,7 +111,11 @@ namespace SohImGui { bool statsWindowOpen; const char* filters[3] = { +#ifdef __WIIU__ + "", +#else "Three-Point", +#endif "Linear", "None" }; @@ -110,7 +124,11 @@ namespace SohImGui { #ifdef _WIN32 { "dx11", "DirectX" }, #endif +#ifndef __WIIU__ { "sdl", "OpenGL" } +#else + { "wiiu", "GX2" } +#endif }; @@ -178,10 +196,16 @@ namespace SohImGui { void ImGuiWMInit() { switch (impl.backend) { +#ifdef __WIIU__ + case Backend::GX2: + ImGui_ImplWiiU_Init(); + break; +#else case Backend::SDL: SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "1"); ImGui_ImplSDL2_InitForOpenGL(static_cast(impl.sdl.window), impl.sdl.context); break; +#endif #if defined(ENABLE_DX11) || defined(ENABLE_DX12) case Backend::DX11: ImGui_ImplWin32_Init(impl.dx11.window); @@ -195,6 +219,11 @@ namespace SohImGui { void ImGuiBackendInit() { switch (impl.backend) { +#ifdef __WIIU__ + case Backend::GX2: + ImGui_ImplGX2_Init(); + break; +#else case Backend::SDL: #if defined(__APPLE__) ImGui_ImplOpenGL3_Init("#version 410 core"); @@ -202,6 +231,7 @@ namespace SohImGui { ImGui_ImplOpenGL3_Init("#version 120"); #endif break; +#endif #if defined(ENABLE_DX11) || defined(ENABLE_DX12) case Backend::DX11: @@ -215,9 +245,17 @@ namespace SohImGui { void ImGuiProcessEvent(EventImpl event) { switch (impl.backend) { +#ifdef __WIIU__ + case Backend::GX2: + if (!ImGui_ImplWiiU_ProcessInput((ImGui_ImplWiiU_ControllerInput*)event.gx2.input)) { + + } + break; +#else case Backend::SDL: ImGui_ImplSDL2_ProcessEvent(static_cast(event.sdl.event)); break; +#endif #if defined(ENABLE_DX11) || defined(ENABLE_DX12) case Backend::DX11: ImGui_ImplWin32_WndProcHandler(static_cast(event.win32.handle), event.win32.msg, event.win32.wparam, event.win32.lparam); @@ -230,9 +268,14 @@ namespace SohImGui { void ImGuiWMNewFrame() { switch (impl.backend) { +#ifdef __WIIU__ + case Backend::GX2: + break; +#else case Backend::SDL: ImGui_ImplSDL2_NewFrame(static_cast(impl.sdl.window)); break; +#endif #if defined(ENABLE_DX11) || defined(ENABLE_DX12) case Backend::DX11: ImGui_ImplWin32_NewFrame(); @@ -245,9 +288,16 @@ namespace SohImGui { void ImGuiBackendNewFrame() { switch (impl.backend) { +#ifdef __WIIU__ + case Backend::GX2: + io->DeltaTime = (float) frametime / 1000.0f / 1000.0f; + ImGui_ImplGX2_NewFrame(); + break; +#else case Backend::SDL: ImGui_ImplOpenGL3_NewFrame(); break; +#endif #if defined(ENABLE_DX11) || defined(ENABLE_DX12) case Backend::DX11: ImGui_ImplDX11_NewFrame(); @@ -260,9 +310,20 @@ namespace SohImGui { void ImGuiRenderDrawData(ImDrawData* data) { switch (impl.backend) { +#ifdef __WIIU__ + case Backend::GX2: + ImGui_ImplGX2_RenderDrawData(data); + + // Reset viewport and scissor for drawing the keyboard + GX2SetViewport(0.0f, 0.0f, io->DisplaySize.x, io->DisplaySize.y, 0.0f, 1.0f); + GX2SetScissor(0, 0, io->DisplaySize.x, io->DisplaySize.y); + ImGui_ImplWiiU_DrawKeyboardOverlay(); + break; +#else case Backend::SDL: ImGui_ImplOpenGL3_RenderDrawData(data); break; +#endif #if defined(ENABLE_DX11) || defined(ENABLE_DX12) case Backend::DX11: ImGui_ImplDX11_RenderDrawData(data); @@ -395,9 +456,19 @@ namespace SohImGui { Ship::Switch::SetupFont(io->Fonts); #endif + #ifdef __WIIU__ + // Scale everything by 2 for the Wii U + ImGui::GetStyle().ScaleAllSizes(2.0f); + io->FontGlobalScale = 2.0f; + + // Setup display sizes + io->DisplaySize.x = window_impl.gx2.width; + io->DisplaySize.y = window_impl.gx2.height; + #endif + lastBackendID = GetBackendID(GlobalCtx2::GetInstance()->GetConfig()); if (CVar_GetS32("gOpenMenuBar", 0) != 1) { - #ifdef __SWITCH__ + #if defined(__SWITCH__) || defined(__WIIU__) SohImGui::overlay->TextDrawNotification(30.0f, true, "Press - to access enhancements menu"); #else SohImGui::overlay->TextDrawNotification(30.0f, true, "Press F1 to access enhancements menu"); @@ -412,6 +483,18 @@ namespace SohImGui { if (UseViewports()) { io->ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; } + + #ifdef __SWITCH__ + bool enableControllerNavigation = true; + #else + bool enableControllerNavigation = CVar_GetS32("gControlNav", 0); + #endif + if (enableControllerNavigation && CVar_GetS32("gOpenMenuBar", 0)) { + io->ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad | ImGuiConfigFlags_NavEnableKeyboard; + } else { + io->ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad; + } + console->Init(); overlay->Init(); controller->Init(); @@ -616,7 +699,11 @@ namespace SohImGui { ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); } if (PlusMinusButton) { + #ifdef __WIIU__ + ImGui::PushItemWidth(ImGui::GetWindowSize().x - 79.0f * 2); + #else ImGui::PushItemWidth(ImGui::GetWindowSize().x - 79.0f); + #endif } if (ImGui::SliderFloat(id, &val, min, max, format)) { @@ -846,12 +933,8 @@ namespace SohImGui { #else bool enableControllerNavigation = CVar_GetS32("gControlNav", 0); #endif - if (enableControllerNavigation) { - if (CVar_GetS32("gOpenMenuBar", 0)) { - io->ConfigFlags |=ImGuiConfigFlags_NavEnableGamepad | ImGuiConfigFlags_NavEnableKeyboard; - } else { - io->ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad; - } + if (enableControllerNavigation && CVar_GetS32("gOpenMenuBar", 0)) { + io->ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad | ImGuiConfigFlags_NavEnableKeyboard; } else { io->ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad; } @@ -875,13 +958,18 @@ namespace SohImGui { if (DefaultAssets.contains("Game_Icon")) { #ifdef __SWITCH__ ImVec2 iconSize = ImVec2(20.0f, 20.0f); + float posScale = 1.0f; + #elif defined(__WIIU__) + ImVec2 iconSize = ImVec2(16.0f * 2, 16.0f * 2); + float posScale = 2.0f; #else ImVec2 iconSize = ImVec2(16.0f, 16.0f); + float posScale = 1.0f; #endif - ImGui::SetCursorPos(ImVec2(5, 2.5f)); + ImGui::SetCursorPos(ImVec2(5, 2.5f) * posScale); ImGui::Image(GetTextureByID(DefaultAssets["Game_Icon"]->textureId), iconSize); ImGui::SameLine(); - ImGui::SetCursorPos(ImVec2(25, 0)); + ImGui::SetCursorPos(ImVec2(25, 0) * posScale); } static ImVec2 windowPadding(8.0f, 8.0f); @@ -957,9 +1045,11 @@ namespace SohImGui { Tooltip("Multiplies your output resolution by the value inputted, as a more intensive but effective form of anti-aliasing"); gfx_current_dimensions.internal_mul = CVar_GetFloat("gInternalResolution", 1); #endif + #ifndef __WIIU__ PaddedEnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, "", 1, false, true, false); Tooltip("Activates multi-sample anti-aliasing when above 1x up to 8x for 8 samples for every pixel"); gfx_msaa_level = CVar_GetS32("gMSAAValue", 1); + #endif if (impl.backend == Backend::DX11) { @@ -1434,7 +1524,7 @@ namespace SohImGui { const char* fps_cvar = "gInterpolationFPS"; { - #ifdef __SWITCH__ + #if defined(__SWITCH__) || defined(__WIIU__) int minFps = 20; int maxFps = 60; #else @@ -1444,6 +1534,12 @@ namespace SohImGui { int val = CVar_GetS32(fps_cvar, minFps); val = MAX(MIN(val, maxFps), 20); + + #ifdef __WIIU__ + // only support divisors of 60 on the Wii U + val = 60 / (60 / val); + #endif + int fps = val; if (fps == 20) @@ -1458,15 +1554,28 @@ namespace SohImGui { std::string MinusBTNFPSI = " - ##FPSInterpolation"; std::string PlusBTNFPSI = " + ##FPSInterpolation"; if (ImGui::Button(MinusBTNFPSI.c_str())) { + #ifdef __WIIU__ + if (val >= 60) val = 30; + else val = 20; + #else val--; + #endif CVar_SetS32(fps_cvar, val); needs_save = true; } ImGui::SameLine(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); + #ifdef __WIIU__ + ImGui::PushItemWidth(ImGui::GetWindowSize().x - 79.0f * 2); + #else ImGui::PushItemWidth(ImGui::GetWindowSize().x - 79.0f); + #endif if (ImGui::SliderInt("##FPSInterpolation", &val, minFps, maxFps, "", ImGuiSliderFlags_AlwaysClamp)) { + #ifdef __WIIU__ + // only support divisors of 60 on the Wii U + val = 60 / (60 / val); + #endif if (val > 360) { val = 360; @@ -1490,7 +1599,12 @@ namespace SohImGui { ImGui::SameLine(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f); if (ImGui::Button(PlusBTNFPSI.c_str())) { + #ifdef __WIIU__ + if (val <= 20) val = 30; + else val = 60; + #else val++; + #endif CVar_SetS32(fps_cvar, val); needs_save = true; } @@ -1791,14 +1905,18 @@ namespace SohImGui { ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); ImGui::Begin("Debug Stats", &statsWindowOpen, ImGuiWindowFlags_NoFocusOnAppearing); -#ifdef _WIN32 +#if defined(_WIN32) ImGui::Text("Platform: Windows"); -#elif __APPLE__ +#elif defined(__APPLE__) ImGui::Text("Platform: macOS"); #elif defined(__SWITCH__) ImGui::Text("Platform: Nintendo Switch"); -#else +#elif defined(__WIIU__) + ImGui::Text("Platform: Nintendo Wii U"); +#elif defined(__linux__) ImGui::Text("Platform: Linux"); +#else + ImGui::Text("Platform: Unknown"); #endif ImGui::Text("Status: %.3f ms/frame (%.1f FPS)", 1000.0f / framerate, framerate); ImGui::End(); @@ -2281,6 +2399,13 @@ namespace SohImGui { return gfx_d3d11_get_texture_by_id(id); } #endif +#ifdef __WIIU__ + if (impl.backend == Backend::GX2) + { + return gfx_gx2_texture_for_imgui(id); + } +#endif + return reinterpret_cast(id); } diff --git a/libultraship/libultraship/ImGuiImpl.h b/libultraship/libultraship/ImGuiImpl.h index 8e20d415c..3d6c6e4f9 100644 --- a/libultraship/libultraship/ImGuiImpl.h +++ b/libultraship/libultraship/ImGuiImpl.h @@ -24,7 +24,8 @@ struct GameAsset { namespace SohImGui { enum class Backend { DX11, - SDL + SDL, + GX2, }; enum class Dialogues { @@ -45,6 +46,10 @@ namespace SohImGui { void* window; void* context; } sdl; + struct { + uint32_t width; + uint32_t height; + } gx2; }; } WindowImpl; @@ -58,6 +63,9 @@ namespace SohImGui { struct { void* event; } sdl; + struct { + void* input; + } gx2; } EventImpl; extern WindowImpl impl; diff --git a/libultraship/libultraship/InputEditor.cpp b/libultraship/libultraship/InputEditor.cpp index b557f3cc4..6239e041a 100644 --- a/libultraship/libultraship/InputEditor.cpp +++ b/libultraship/libultraship/InputEditor.cpp @@ -45,6 +45,9 @@ namespace Ship { if(btn != -1) { backend->SetButtonMapping(CurrentPort, n64Btn, btn); BtnReading = -1; + + // avoid immediately triggering another button during gamepad nav + ImGui::SetKeyboardFocusHere(0); } } @@ -145,9 +148,17 @@ namespace Ship { ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); - ImGui::BeginChild("##MSInput", ImVec2(90, 50), false); +#ifdef __WIIU__ + ImGui::BeginChild("##MSInput", ImVec2(90 * 2, 50 * 2), false); +#else + ImGui::BeginChild("##MSInput", ImVec2(90 , 50), false); +#endif ImGui::Text("Deadzone"); + #ifdef __WIIU__ + ImGui::PushItemWidth(80 * 2); + #else ImGui::PushItemWidth(80); + #endif ImGui::InputFloat("##MDZone", &profile->AxisDeadzones[0] /* This is the SDL value for left stick X axis */, 1.0f, 0.0f, "%.0f"); ImGui::PopItemWidth(); ImGui::EndChild(); @@ -176,13 +187,25 @@ namespace Ship { ImGui::SameLine(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); +#ifdef __WIIU__ + ImGui::BeginChild("##CSInput", ImVec2(90 * 2, 85 * 2), false); +#else ImGui::BeginChild("##CSInput", ImVec2(90, 85), false); +#endif ImGui::Text("Deadzone"); + #ifdef __WIIU__ + ImGui::PushItemWidth(80 * 2); + #else ImGui::PushItemWidth(80); - ImGui::InputFloat("##MDZone", &profile->AxisDeadzones[2] /* This is the SDL value for left stick X axis */, 1.0f, 0.0f, "%.0f"); + #endif + ImGui::InputFloat("##MDZone", &profile->AxisDeadzones[2] /* This is the SDL value for right stick X axis */, 1.0f, 0.0f, "%.0f"); ImGui::PopItemWidth(); ImGui::Text("Sensitivity"); + #ifdef __WIIU__ + ImGui::PushItemWidth(80 * 2); + #else ImGui::PushItemWidth(80); + #endif ImGui::InputFloat("##MSensitivity", &profile->AxisSensitivities[2] /* This is the SDL value for right stick X axis */, 1.0f, 0.0f, "%.0f"); profile->AxisSensitivities[3] = profile->AxisSensitivities[2]; ImGui::PopItemWidth(); @@ -195,15 +218,20 @@ namespace Ship { } if(Backend->CanGyro()) { + #ifndef __WIIU__ ImGui::SameLine(); - + #endif SohImGui::BeginGroupPanel("Gyro Options", ImVec2(175, 20)); float cursorX = ImGui::GetCursorPosX() + 5; ImGui::SetCursorPosX(cursorX); ImGui::Checkbox("Enable Gyro", &profile->UseGyro); ImGui::SetCursorPosX(cursorX); ImGui::Text("Gyro Sensitivity: %d%%", static_cast(100.0f * profile->GyroData[GYRO_SENSITIVITY])); + #ifdef __WIIU__ + ImGui::PushItemWidth(135.0f * 2); + #else ImGui::PushItemWidth(135.0f); + #endif ImGui::SetCursorPosX(cursorX); ImGui::SliderFloat("##GSensitivity", &profile->GyroData[GYRO_SENSITIVITY], 0.0f, 1.0f, ""); ImGui::PopItemWidth(); @@ -218,13 +246,25 @@ namespace Ship { ImGui::SameLine(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); + #ifdef __WIIU__ + ImGui::BeginChild("##GyInput", ImVec2(90 * 2, 85 * 2), false); + #else ImGui::BeginChild("##GyInput", ImVec2(90, 85), false); + #endif ImGui::Text("Drift X"); + #ifdef __WIIU__ + ImGui::PushItemWidth(80 * 2); + #else ImGui::PushItemWidth(80); + #endif ImGui::InputFloat("##GDriftX", &profile->GyroData[DRIFT_X], 1.0f, 0.0f, "%.1f"); ImGui::PopItemWidth(); ImGui::Text("Drift Y"); + #ifdef __WIIU__ + ImGui::PushItemWidth(80 * 2); + #else ImGui::PushItemWidth(80); + #endif ImGui::InputFloat("##GDriftY", &profile->GyroData[DRIFT_Y], 1.0f, 0.0f, "%.1f"); ImGui::PopItemWidth(); ImGui::EndChild(); @@ -250,6 +290,8 @@ namespace Ship { ImGui::SetCursorPosX(cursor.x); #ifdef __SWITCH__ ImGui::SetCursorPosY(cursor.y + 167); + #elif defined(__WIIU__) + ImGui::SetCursorPosY(cursor.y + 120 * 2); #else ImGui::SetCursorPosY(cursor.y + 120); #endif @@ -261,7 +303,11 @@ namespace Ship { ImGui::SetCursorPosX(cursorX); ImGui::Text("Rumble Force: %d%%", static_cast(100.0f * profile->RumbleStrength)); ImGui::SetCursorPosX(cursorX); + #ifdef __WIIU__ + ImGui::PushItemWidth(135.0f * 2); + #else ImGui::PushItemWidth(135.0f); + #endif ImGui::SliderFloat("##RStrength", &profile->RumbleStrength, 0.0f, 1.0f, ""); ImGui::PopItemWidth(); } @@ -279,6 +325,9 @@ namespace Ship { #ifdef __SWITCH__ ImVec2 minSize = ImVec2(641, 250); ImVec2 maxSize = ImVec2(2200, 505); +#elif defined(__WIIU__) + ImVec2 minSize = ImVec2(641 * 2, 250 * 2); + ImVec2 maxSize = ImVec2(1200 * 2, 290 * 2); #else ImVec2 minSize = ImVec2(641, 250); ImVec2 maxSize = ImVec2(1200, 290); diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_gx2.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_gx2.cpp new file mode 100644 index 000000000..dddc0fa85 --- /dev/null +++ b/libultraship/libultraship/Lib/Fast3D/gfx_gx2.cpp @@ -0,0 +1,826 @@ +#ifdef __WIIU__ + +#include "../../Window.h" + +#include +#include +#include +#include + +#include + +#ifndef _LANGUAGE_C +#define _LANGUAGE_C +#endif +#include "PR/ultra64/gbi.h" + +#include "gfx_cc.h" +#include "gfx_rendering_api.h" +#include "../../GlobalCtx2.h" +#include "gfx_pc.h" +#include "gfx_wiiu.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gx2_shader_gen.h" + +#include +#include + +#include "../ImGui/backends/wiiu/imgui_impl_gx2.h" + +#define ALIGN(x, align) (((x) + ((align) -1)) & ~((align) -1)) + +struct ShaderProgram { + struct ShaderGroup group; + uint8_t num_inputs; + bool used_textures[2]; + bool used_noise; + uint32_t window_params_offset; + uint32_t samplers_location[2]; +}; + +struct Texture { + GX2Texture texture; + bool texture_uploaded; + + GX2Sampler sampler; + bool sampler_set; + + // For ImGui rendering + ImGui_ImplGX2_Texture imtex; +}; + +struct Framebuffer { + GX2ColorBuffer color_buffer; + bool colorBufferMem1; + GX2DepthBuffer depth_buffer; + bool depthBufferMem1; + + GX2Texture texture; + GX2Sampler sampler; + + // For ImGui rendering + ImGui_ImplGX2_Texture imtex; +}; + +static struct Framebuffer main_framebuffer; +static GX2DepthBuffer depthReadBuffer; +static struct Framebuffer *current_framebuffer; + +static std::map, struct ShaderProgram> shader_program_pool; +static struct ShaderProgram *current_shader_program; + +static struct Texture *current_texture; +static int current_tile; + +// 96 Mb (should be more than enough to draw everything without waiting for the GPU) +#define DRAW_BUFFER_SIZE 0x6000000 +static uint8_t *draw_buffer = nullptr; +static uint8_t *draw_ptr = nullptr; + +static uint32_t frame_count; +static float current_noise_scale; +static FilteringMode current_filter_mode = FILTER_LINEAR; + +static BOOL current_depth_test = TRUE; +static BOOL current_depth_write = TRUE; +static GX2CompareFunction current_depth_compare_function = GX2_COMPARE_FUNC_LESS; + +static float current_viewport_x = 0.0f; +static float current_viewport_y = 0.0f; +static float current_viewport_width = WIIU_DEFAULT_FB_WIDTH; +static float current_viewport_height = WIIU_DEFAULT_FB_HEIGHT; + +static uint32_t current_scissor_x = 0; +static uint32_t current_scissor_y = 0; +static uint32_t current_scissor_width = WIIU_DEFAULT_FB_WIDTH; +static uint32_t current_scissor_height = WIIU_DEFAULT_FB_HEIGHT; + +static bool current_zmode_decal = false; +static bool current_use_alpha = false; + +static inline GX2SamplerVar *GX2GetPixelSamplerVar(const GX2PixelShader *shader, const char *name) +{ + for (uint32_t i = 0; i < shader->samplerVarCount; ++i) { + if (strcmp(name, shader->samplerVars[i].name) == 0) { + return &shader->samplerVars[i]; + } + } + + return nullptr; +} + +static inline int32_t GX2GetPixelSamplerVarLocation(const GX2PixelShader *shader, const char *name) +{ + GX2SamplerVar *sampler = GX2GetPixelSamplerVar(shader, name); + return sampler ? sampler->location : -1; +} + +static inline int32_t GX2GetPixelUniformVarOffset(const GX2PixelShader *shader, const char *name) +{ + GX2UniformVar *uniform = GX2GetPixelUniformVar(shader, name); + return uniform ? uniform->offset : -1; +} + +static void gfx_gx2_init_framebuffer(struct Framebuffer *buffer, uint32_t width, uint32_t height) { + memset(&buffer->color_buffer, 0, sizeof(GX2ColorBuffer)); + buffer->color_buffer.surface.use = GX2_SURFACE_USE_TEXTURE_COLOR_BUFFER_TV; + buffer->color_buffer.surface.dim = GX2_SURFACE_DIM_TEXTURE_2D; + buffer->color_buffer.surface.width = width; + buffer->color_buffer.surface.height = height; + buffer->color_buffer.surface.depth = 1; + buffer->color_buffer.surface.mipLevels = 1; + buffer->color_buffer.surface.format = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8; + buffer->color_buffer.surface.aa = GX2_AA_MODE1X; + buffer->color_buffer.surface.tileMode = GX2_TILE_MODE_DEFAULT; + buffer->color_buffer.viewNumSlices = 1; + + memset(&buffer->depth_buffer, 0, sizeof(GX2DepthBuffer)); + buffer->depth_buffer.surface.use = GX2_SURFACE_USE_DEPTH_BUFFER | GX2_SURFACE_USE_TEXTURE; + buffer->depth_buffer.surface.dim = GX2_SURFACE_DIM_TEXTURE_2D; + buffer->depth_buffer.surface.width = width; + buffer->depth_buffer.surface.height = height; + buffer->depth_buffer.surface.depth = 1; + buffer->depth_buffer.surface.mipLevels = 1; + buffer->depth_buffer.surface.format = GX2_SURFACE_FORMAT_FLOAT_R32; + buffer->depth_buffer.surface.aa = GX2_AA_MODE1X; + buffer->depth_buffer.surface.tileMode = GX2_TILE_MODE_DEFAULT; + buffer->depth_buffer.viewNumSlices = 1; + buffer->depth_buffer.depthClear = 1.0f; +} + +static struct GfxClipParameters gfx_gx2_get_clip_parameters(void) { + return { false, false }; +} + +static void gfx_gx2_set_uniforms(struct ShaderProgram *prg) { + if (prg->used_noise) { + float window_params_array[2] = { current_noise_scale, (float) frame_count }; + + GX2SetPixelUniformReg(prg->window_params_offset, 2, window_params_array); + } +} + +static void gfx_gx2_unload_shader(struct ShaderProgram *old_prg) { + current_shader_program = nullptr; +} + +static void gfx_gx2_load_shader(struct ShaderProgram *new_prg) { + current_shader_program = new_prg; + + GX2SetFetchShader(&new_prg->group.fetchShader); + GX2SetVertexShader(&new_prg->group.vertexShader); + GX2SetPixelShader(&new_prg->group.pixelShader); + + gfx_gx2_set_uniforms(new_prg); +} + +static struct ShaderProgram* gfx_gx2_create_and_load_new_shader(uint64_t shader_id0, uint32_t shader_id1) { + struct CCFeatures cc_features; + gfx_cc_get_features(shader_id0, shader_id1, &cc_features); + + struct ShaderProgram* prg = &shader_program_pool[std::make_pair(shader_id0, shader_id1)]; + + printf("Generating shader: %016llx-%08x\n", shader_id0, shader_id1); + if (gx2GenerateShaderGroup(&prg->group, &cc_features) != 0) { + printf("Failed to generate shader\n"); + current_shader_program = nullptr; + return nullptr; + } + + prg->num_inputs = cc_features.num_inputs; + prg->used_textures[0] = cc_features.used_textures[0]; + prg->used_textures[1] = cc_features.used_textures[1]; + + gfx_gx2_load_shader(prg); + + prg->window_params_offset = GX2GetPixelUniformVarOffset(&prg->group.pixelShader, "window_params"); + prg->samplers_location[0] = GX2GetPixelSamplerVarLocation(&prg->group.pixelShader, "uTex0"); + prg->samplers_location[1] = GX2GetPixelSamplerVarLocation(&prg->group.pixelShader, "uTex1"); + + prg->used_noise = cc_features.opt_alpha && cc_features.opt_noise; + + printf("Generated and loaded shader\n"); + + return prg; +} + +static struct ShaderProgram *gfx_gx2_lookup_shader(uint64_t shader_id0, uint32_t shader_id1) { + auto it = shader_program_pool.find(std::make_pair(shader_id0, shader_id1)); + return it == shader_program_pool.end() ? nullptr : &it->second; +} + +static void gfx_gx2_shader_get_info(struct ShaderProgram *prg, uint8_t *num_inputs, bool used_textures[2]) { + *num_inputs = prg->num_inputs; + used_textures[0] = prg->used_textures[0]; + used_textures[1] = prg->used_textures[1]; +} + +static uint32_t gfx_gx2_new_texture(void) { + // some 32-bit trickery :P + struct Texture *tex = (struct Texture *) calloc(1, sizeof(struct Texture)); + + tex->imtex.Texture = &tex->texture; + tex->imtex.Sampler = &tex->sampler; + + return (uint32_t) tex; +} + +static void gfx_gx2_delete_texture(uint32_t texture_id) { + struct Texture *tex = (struct Texture *) texture_id; + + if (tex->texture.surface.image) { + free(tex->texture.surface.image); + } + + free((void *) tex); +} + +static void gfx_gx2_select_texture(int tile, uint32_t texture_id) { + struct Texture *tex = (struct Texture *) texture_id; + current_texture = tex; + current_tile = tile; + + if (current_shader_program) { + uint32_t sampler_location = current_shader_program->samplers_location[tile]; + + if (tex->texture_uploaded) { + GX2SetPixelTexture(&tex->texture, sampler_location); + } + + if (tex->sampler_set) { + GX2SetPixelSampler(&tex->sampler, sampler_location); + } + } +} + +static void gfx_gx2_upload_texture(const uint8_t *rgba32_buf, uint32_t width, uint32_t height) { + struct Texture *tex = current_texture; + assert(tex); + + if ((tex->texture.surface.width != width) || + (tex->texture.surface.height != height) || + !tex->texture.surface.image) { + + if (tex->texture.surface.image) { + free(tex->texture.surface.image); + tex->texture.surface.image = nullptr; + } + + memset(&tex->texture, 0, sizeof(GX2Texture)); + tex->texture.surface.use = GX2_SURFACE_USE_TEXTURE; + tex->texture.surface.dim = GX2_SURFACE_DIM_TEXTURE_2D; + tex->texture.surface.width = width; + tex->texture.surface.height = height; + tex->texture.surface.depth = 1; + tex->texture.surface.mipLevels = 1; + tex->texture.surface.format = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8; + tex->texture.surface.aa = GX2_AA_MODE1X; + tex->texture.surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED; + tex->texture.viewFirstMip = 0; + tex->texture.viewNumMips = 1; + tex->texture.viewFirstSlice = 0; + tex->texture.viewNumSlices = 1; + tex->texture.compMap = GX2_COMP_MAP(GX2_SQ_SEL_R, GX2_SQ_SEL_G, GX2_SQ_SEL_B, GX2_SQ_SEL_A); + + GX2CalcSurfaceSizeAndAlignment(&tex->texture.surface); + GX2InitTextureRegs(&tex->texture); + + tex->texture.surface.image = memalign(tex->texture.surface.alignment, tex->texture.surface.imageSize); + } + + uint8_t* buf = (uint8_t *) tex->texture.surface.image; + assert(buf); + + for (uint32_t y = 0; y < height; ++y) { + memcpy(buf + (y * tex->texture.surface.pitch * 4), rgba32_buf + (y * width * 4), width * 4); + } + + GX2Invalidate(GX2_INVALIDATE_MODE_CPU_TEXTURE, tex->texture.surface.image, tex->texture.surface.imageSize); + + if (current_shader_program) { + GX2SetPixelTexture(&tex->texture, current_shader_program->samplers_location[current_tile]); + } + + tex->texture_uploaded = true; +} + +static GX2TexClampMode gfx_cm_to_gx2(uint32_t val) { + switch (val) { + case G_TX_NOMIRROR | G_TX_CLAMP: + return GX2_TEX_CLAMP_MODE_CLAMP; + case G_TX_MIRROR | G_TX_WRAP: + return GX2_TEX_CLAMP_MODE_MIRROR; + case G_TX_MIRROR | G_TX_CLAMP: + return GX2_TEX_CLAMP_MODE_MIRROR_ONCE; + case G_TX_NOMIRROR | G_TX_WRAP: + return GX2_TEX_CLAMP_MODE_WRAP; + } + + return GX2_TEX_CLAMP_MODE_WRAP; +} + +static void gfx_gx2_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) { + struct Texture *tex = current_texture; + assert(tex); + + current_tile = tile; + + GX2InitSampler(&tex->sampler, GX2_TEX_CLAMP_MODE_CLAMP, + (linear_filter && current_filter_mode == FILTER_LINEAR) ? + GX2_TEX_XY_FILTER_MODE_LINEAR : GX2_TEX_XY_FILTER_MODE_POINT); + + GX2InitSamplerClamping(&tex->sampler, gfx_cm_to_gx2(cms), gfx_cm_to_gx2(cmt), GX2_TEX_CLAMP_MODE_WRAP); + + if (current_shader_program) { + GX2SetPixelSampler(&tex->sampler, current_shader_program->samplers_location[tile]); + } + + tex->sampler_set = true; +} + +static void gfx_gx2_set_depth_test_and_mask(bool depth_test, bool z_upd) { + current_depth_test = depth_test || z_upd; + current_depth_write = z_upd; + current_depth_compare_function = depth_test ? GX2_COMPARE_FUNC_LEQUAL : GX2_COMPARE_FUNC_ALWAYS; + + GX2SetDepthOnlyControl(current_depth_test, current_depth_write, current_depth_compare_function); +} + +static void gfx_gx2_set_zmode_decal(bool zmode_decal) { + current_zmode_decal = zmode_decal; + if (zmode_decal) { + GX2SetPolygonControl(GX2_FRONT_FACE_CCW, FALSE, FALSE, TRUE, + GX2_POLYGON_MODE_TRIANGLE, GX2_POLYGON_MODE_TRIANGLE, + TRUE, TRUE, FALSE); + GX2SetPolygonOffset(-2.0f, -2.0f, -2.0f, -2.0f, 0.0f); + } else { + GX2SetPolygonControl(GX2_FRONT_FACE_CCW, FALSE, FALSE, FALSE, + GX2_POLYGON_MODE_TRIANGLE, GX2_POLYGON_MODE_TRIANGLE, + FALSE, FALSE, FALSE); + GX2SetPolygonOffset(0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + } +} + +static void gfx_gx2_set_viewport(int x, int y, int width, int height) { + uint32_t buffer_height = current_framebuffer->color_buffer.surface.height; + + current_viewport_x = x; + current_viewport_y = buffer_height - y - height; + current_viewport_width = width; + current_viewport_height = height; + + GX2SetViewport(current_viewport_x, current_viewport_y, current_viewport_width, current_viewport_height, 0.0f, 1.0f); +} + +static void gfx_gx2_set_scissor(int x, int y, int width, int height) { + uint32_t buffer_height = current_framebuffer->color_buffer.surface.height; + uint32_t buffer_width = current_framebuffer->color_buffer.surface.width; + + current_scissor_x = std::min((uint32_t) width, (uint32_t) x); + current_scissor_y = std::min((uint32_t) height, buffer_height - y - height); + current_scissor_width = std::min((uint32_t) width, buffer_width); + current_scissor_height = std::min((uint32_t) height, buffer_height); + + GX2SetScissor(current_scissor_x, current_scissor_y, current_scissor_width, current_scissor_height); +} + +static void gfx_gx2_set_use_alpha(bool use_alpha) { + current_use_alpha = use_alpha; + GX2SetColorControl(GX2_LOGIC_OP_COPY, use_alpha ? 0xff : 0, FALSE, TRUE); +} + +static void gfx_gx2_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) { + if (!current_shader_program) { + return; + } + + size_t vbo_len = sizeof(float) * buf_vbo_len; + + if (draw_ptr + vbo_len >= draw_buffer + DRAW_BUFFER_SIZE) { + printf("Waiting on GPU!!!\n"); + GX2DrawDone(); + draw_ptr = draw_buffer; + } + + float* new_vbo = (float *) draw_ptr; + draw_ptr += ALIGN(vbo_len, GX2_VERTEX_BUFFER_ALIGNMENT); + + OSBlockMove(new_vbo, buf_vbo, vbo_len, FALSE); + GX2Invalidate(GX2_INVALIDATE_MODE_CPU_ATTRIBUTE_BUFFER, new_vbo, vbo_len); + + GX2SetAttribBuffer(0, vbo_len, current_shader_program->group.stride, new_vbo); + GX2DrawEx(GX2_PRIMITIVE_MODE_TRIANGLES, 3 * buf_vbo_num_tris, 0, 1); +} + +static void gfx_gx2_init(void) { + // Init the default framebuffer + gfx_gx2_init_framebuffer(&main_framebuffer, WIIU_DEFAULT_FB_WIDTH, WIIU_DEFAULT_FB_HEIGHT); + + GX2CalcSurfaceSizeAndAlignment(&main_framebuffer.color_buffer.surface); + GX2InitColorBufferRegs(&main_framebuffer.color_buffer); + + main_framebuffer.color_buffer.surface.image = gfx_wiiu_alloc_mem1(main_framebuffer.color_buffer.surface.imageSize, main_framebuffer.color_buffer.surface.alignment); + assert(main_framebuffer.color_buffer.surface.image); + + GX2CalcSurfaceSizeAndAlignment(&main_framebuffer.depth_buffer.surface); + GX2InitDepthBufferRegs(&main_framebuffer.depth_buffer); + + main_framebuffer.depth_buffer.surface.image = gfx_wiiu_alloc_mem1(main_framebuffer.depth_buffer.surface.imageSize, main_framebuffer.depth_buffer.surface.alignment); + assert(main_framebuffer.depth_buffer.surface.image); + + main_framebuffer.imtex.Texture = &main_framebuffer.texture; + main_framebuffer.imtex.Sampler = &main_framebuffer.sampler; + + // create a linear aligned copy of the depth buffer to read pixels to + memcpy(&depthReadBuffer, &main_framebuffer.depth_buffer, sizeof(GX2DepthBuffer)); + + depthReadBuffer.surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED; + depthReadBuffer.surface.width = 1; + depthReadBuffer.surface.height = 1; + + GX2CalcSurfaceSizeAndAlignment(&depthReadBuffer.surface); + + depthReadBuffer.surface.image = gfx_wiiu_alloc_mem1(depthReadBuffer.surface.alignment, depthReadBuffer.surface.imageSize); + assert(depthReadBuffer.surface.image); + GX2Invalidate(GX2_INVALIDATE_MODE_CPU | GX2_INVALIDATE_MODE_DEPTH_BUFFER, depthReadBuffer.surface.image, depthReadBuffer.surface.imageSize); + + GX2SetColorBuffer(&main_framebuffer.color_buffer, GX2_RENDER_TARGET_0); + GX2SetDepthBuffer(&main_framebuffer.depth_buffer); + + current_framebuffer = &main_framebuffer; + + // allocate draw buffer + draw_buffer = (uint8_t *) memalign(GX2_VERTEX_BUFFER_ALIGNMENT, DRAW_BUFFER_SIZE); + assert(draw_buffer); + draw_ptr = draw_buffer; + + GX2SetRasterizerClipControl(TRUE, FALSE); + + GX2SetBlendControl(GX2_RENDER_TARGET_0, + GX2_BLEND_MODE_SRC_ALPHA, + GX2_BLEND_MODE_INV_SRC_ALPHA, + GX2_BLEND_COMBINE_MODE_ADD, + FALSE, + GX2_BLEND_MODE_ZERO, + GX2_BLEND_MODE_ZERO, + GX2_BLEND_COMBINE_MODE_ADD); +} + +void gfx_gx2_shutdown(void) { + if (has_foreground) { + GX2DrawDone(); + + if (depthReadBuffer.surface.image) { + gfx_wiiu_free_mem1(depthReadBuffer.surface.image); + depthReadBuffer.surface.image = nullptr; + } + + if (main_framebuffer.color_buffer.surface.image) { + gfx_wiiu_free_mem1(main_framebuffer.color_buffer.surface.image); + main_framebuffer.color_buffer.surface.image = nullptr; + } + + if (main_framebuffer.depth_buffer.surface.image) { + gfx_wiiu_free_mem1(main_framebuffer.depth_buffer.surface.image); + main_framebuffer.depth_buffer.surface.image = nullptr; + } + } + + if (draw_buffer) { + free(draw_buffer); + draw_buffer = nullptr; + draw_ptr = nullptr; + } +} + +static void gfx_gx2_on_resize(void) { +} + +static void gfx_gx2_start_frame(void) { + // Restore state since ImGui modified it when rendering + GX2SetViewport(current_viewport_x, current_viewport_y, current_viewport_width, current_viewport_height, 0.0f, 1.0f); + GX2SetScissor(current_scissor_x, current_scissor_y, current_scissor_width, current_scissor_height); + + GX2SetColorControl(GX2_LOGIC_OP_COPY, current_use_alpha ? 0xff : 0, FALSE, TRUE); + + GX2SetBlendControl(GX2_RENDER_TARGET_0, + GX2_BLEND_MODE_SRC_ALPHA, + GX2_BLEND_MODE_INV_SRC_ALPHA, + GX2_BLEND_COMBINE_MODE_ADD, + FALSE, + GX2_BLEND_MODE_ZERO, + GX2_BLEND_MODE_ZERO, + GX2_BLEND_COMBINE_MODE_ADD); + + GX2SetDepthOnlyControl(current_depth_test, current_depth_write, current_depth_compare_function); + + if (current_zmode_decal) { + GX2SetPolygonControl(GX2_FRONT_FACE_CCW, FALSE, FALSE, TRUE, + GX2_POLYGON_MODE_TRIANGLE, GX2_POLYGON_MODE_TRIANGLE, + TRUE, TRUE, FALSE); + GX2SetPolygonOffset(-2.0f, -2.0f, -2.0f, -2.0f, 0.0f); + } else { + GX2SetPolygonControl(GX2_FRONT_FACE_CCW, FALSE, FALSE, FALSE, + GX2_POLYGON_MODE_TRIANGLE, GX2_POLYGON_MODE_TRIANGLE, + FALSE, FALSE, FALSE); + GX2SetPolygonOffset(0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + } + + frame_count++; +} + +static void gfx_gx2_end_frame(void) { + draw_ptr = draw_buffer; + + GX2CopyColorBufferToScanBuffer(&main_framebuffer.color_buffer, GX2_SCAN_TARGET_TV); + GX2CopyColorBufferToScanBuffer(&main_framebuffer.color_buffer, GX2_SCAN_TARGET_DRC); +} + +static void gfx_gx2_finish_render(void) { +} + +static int gfx_gx2_create_framebuffer(void) { + struct Framebuffer *buffer = (struct Framebuffer *) calloc(1, sizeof(struct Framebuffer)); + assert(buffer); + + GX2InitSampler(&buffer->sampler, GX2_TEX_CLAMP_MODE_WRAP, GX2_TEX_XY_FILTER_MODE_LINEAR); + + buffer->imtex.Texture = &buffer->texture; + buffer->imtex.Sampler = &buffer->sampler; + + // some more 32-bit shenanigans :D + return (int) buffer; +} + +static void gfx_gx2_update_framebuffer_parameters(int fb, uint32_t width, uint32_t height, uint32_t msaa_level, bool opengl_invert_y, bool render_target, bool has_depth_buffer, bool can_extract_depth) { + struct Framebuffer *buffer = (struct Framebuffer *) fb; + + // we don't support updating the main buffer (fb 0) + if (!buffer) { + return; + } + + if (buffer->texture.surface.width == width && + buffer->texture.surface.height == height) { + return; + } + + // make sure the GPU no longer writes to the buffer + GX2DrawDone(); + + if (buffer->texture.surface.image) { + if (buffer->colorBufferMem1) { + gfx_wiiu_free_mem1(buffer->texture.surface.image); + } else { + free(buffer->texture.surface.image); + } + buffer->texture.surface.image = nullptr; + } + + if (buffer->depth_buffer.surface.image) { + if (buffer->depthBufferMem1) { + gfx_wiiu_free_mem1(buffer->depth_buffer.surface.image); + } else { + free(buffer->depth_buffer.surface.image); + } + buffer->depth_buffer.surface.image = nullptr; + } + + gfx_gx2_init_framebuffer(buffer, width, height); + + GX2CalcSurfaceSizeAndAlignment(&buffer->depth_buffer.surface); + GX2InitDepthBufferRegs(&buffer->depth_buffer); + + buffer->depth_buffer.surface.image = gfx_wiiu_alloc_mem1(buffer->depth_buffer.surface.imageSize, buffer->depth_buffer.surface.alignment); + // fall back to mem2 + if (!buffer->depth_buffer.surface.image) { + buffer->depth_buffer.surface.image = memalign(buffer->depth_buffer.surface.alignment, buffer->depth_buffer.surface.imageSize); + buffer->depthBufferMem1 = false; + } else { + buffer->depthBufferMem1 = true; + } + assert(buffer->depth_buffer.surface.image); + + GX2CalcSurfaceSizeAndAlignment(&buffer->color_buffer.surface); + GX2InitColorBufferRegs(&buffer->color_buffer); + + memset(&buffer->texture, 0, sizeof(GX2Texture)); + buffer->texture.surface.use = GX2_SURFACE_USE_TEXTURE; + buffer->texture.surface.dim = GX2_SURFACE_DIM_TEXTURE_2D; + buffer->texture.surface.width = width; + buffer->texture.surface.height = height; + buffer->texture.surface.depth = 1; + buffer->texture.surface.mipLevels = 1; + buffer->texture.surface.format = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8; + buffer->texture.surface.aa = GX2_AA_MODE1X; + buffer->texture.surface.tileMode = GX2_TILE_MODE_DEFAULT; + buffer->texture.viewFirstMip = 0; + buffer->texture.viewNumMips = 1; + buffer->texture.viewFirstSlice = 0; + buffer->texture.viewNumSlices = 1; + buffer->texture.compMap = GX2_COMP_MAP(GX2_SQ_SEL_R, GX2_SQ_SEL_G, GX2_SQ_SEL_B, GX2_SQ_SEL_A); + + GX2CalcSurfaceSizeAndAlignment(&buffer->texture.surface); + GX2InitTextureRegs(&buffer->texture); + + // the texture and color buffer share a buffer + assert(buffer->color_buffer.surface.imageSize == buffer->texture.surface.imageSize); + + buffer->texture.surface.image = gfx_wiiu_alloc_mem1(buffer->texture.surface.imageSize, buffer->texture.surface.alignment); + // fall back to mem2 + if (!buffer->texture.surface.image) { + buffer->texture.surface.image = memalign(buffer->texture.surface.alignment, buffer->texture.surface.imageSize); + buffer->colorBufferMem1 = false; + } else { + buffer->colorBufferMem1 = true; + } + assert(buffer->texture.surface.image); + + buffer->color_buffer.surface.image = buffer->texture.surface.image; +} + +void gfx_gx2_start_draw_to_framebuffer(int fb, float noise_scale) { + struct Framebuffer *buffer = (struct Framebuffer *) fb; + + // fb 0 = main buffer + if (!buffer) { + buffer = &main_framebuffer; + } + + if (noise_scale != 0.0f) { + current_noise_scale = 1.0f / noise_scale; + } + + GX2SetColorBuffer(&buffer->color_buffer, GX2_RENDER_TARGET_0); + GX2SetDepthBuffer(&buffer->depth_buffer); + + current_framebuffer = buffer; +} + +void gfx_gx2_clear_framebuffer(void) { + struct Framebuffer *buffer = current_framebuffer; + + GX2ClearColor(&buffer->color_buffer, 0.0f, 0.0f, 0.0f, 1.0f); + GX2ClearDepthStencilEx(&buffer->depth_buffer, + buffer->depth_buffer.depthClear, + buffer->depth_buffer.stencilClear, GX2_CLEAR_FLAGS_BOTH); + + gfx_wiiu_set_context_state(); +} + +void gfx_gx2_resolve_msaa_color_buffer(int fb_id_target, int fb_id_source) { + struct Framebuffer *src_buffer = (struct Framebuffer *) fb_id_source; + struct Framebuffer *target_buffer = (struct Framebuffer *) fb_id_target; + + // fb 0 = main buffer + if (!src_buffer) { + src_buffer = &main_framebuffer; + } + if (!target_buffer) { + target_buffer = &main_framebuffer; + } + + if (src_buffer->color_buffer.surface.aa == GX2_AA_MODE1X) { + GX2CopySurface(&src_buffer->color_buffer.surface, + src_buffer->color_buffer.viewMip, + src_buffer->color_buffer.viewFirstSlice, + &target_buffer->color_buffer.surface, + target_buffer->color_buffer.viewMip, + target_buffer->color_buffer.viewFirstSlice); + } else { + GX2ResolveAAColorBuffer(&src_buffer->color_buffer, + &target_buffer->color_buffer.surface, + target_buffer->color_buffer.viewMip, + target_buffer->color_buffer.viewFirstSlice); + } +} + +void *gfx_gx2_get_framebuffer_texture_id(int fb_id) { + struct Framebuffer *buffer = (struct Framebuffer *) fb_id; + + // fb 0 = main buffer + if (!buffer) { + buffer = &main_framebuffer; + } + + return &buffer->imtex; +} + +void gfx_gx2_select_texture_fb(int fb) { + struct Framebuffer *buffer = (struct Framebuffer *) fb; + assert(buffer); + + assert(current_shader_program); + uint32_t location = current_shader_program->samplers_location[0]; + GX2SetPixelTexture(&buffer->texture, location); + GX2SetPixelSampler(&buffer->sampler, location); +} + +static std::unordered_map, uint16_t, hash_pair_ff> gfx_gx2_get_pixel_depth(int fb_id, const std::set>& coordinates) { + struct Framebuffer *buffer = (struct Framebuffer *) fb_id; + + // fb 0 = main buffer + if (!buffer) { + buffer = &main_framebuffer; + } + + std::unordered_map, uint16_t, hash_pair_ff> res; + + for (const auto& c : coordinates) { + // bug? coordinates sometimes read from oob + if ((c.first < 0.0f) || (c.first > (float) buffer->depth_buffer.surface.width) + || (c.second < 0.0f) || (c.second > (float) buffer->depth_buffer.surface.height)) { + res.emplace(c, 0); + continue; + } + + GX2Invalidate(GX2_INVALIDATE_MODE_CPU | GX2_INVALIDATE_MODE_DEPTH_BUFFER, depthReadBuffer.surface.image, depthReadBuffer.surface.imageSize); + + // copy the pixel to the depthReadBuffer + GX2Rect srcRect = { + (int32_t) c.first, + (int32_t) buffer->depth_buffer.surface.height - (int32_t) c.second, + (int32_t) c.first + 1, + (int32_t) (buffer->depth_buffer.surface.height - (int32_t) c.second) + 1 + }; + GX2Point dstPoint = { 0, 0 }; + GX2CopySurfaceEx(&buffer->depth_buffer.surface, 0, 0, &depthReadBuffer.surface, 0, 0, 1, &srcRect, &dstPoint); + GX2DrawDone(); + + gfx_wiiu_set_context_state(); + + // read the pixel from the depthReadBuffer + uint32_t tmp = __builtin_bswap32(*(uint32_t *)depthReadBuffer.surface.image); + float val = *(float *)&tmp; + + res.emplace(c, val * 65532.0f); + } + + return res; +} + +void gfx_gx2_set_texture_filter(FilteringMode mode) { + // three-point is not implemented in the shaders yet + if (mode == FILTER_THREE_POINT) { + mode = FILTER_LINEAR; + } + + current_filter_mode = mode; + gfx_texture_cache_clear(); +} + +FilteringMode gfx_gx2_get_texture_filter(void) { + return current_filter_mode; +} + +ImGui_ImplGX2_Texture* gfx_gx2_texture_for_imgui(uint32_t texture_id) { + struct Texture *tex = (struct Texture *) texture_id; + return &tex->imtex; +} + +struct GfxRenderingAPI gfx_gx2_api = { + gfx_gx2_get_clip_parameters, + gfx_gx2_unload_shader, + gfx_gx2_load_shader, + gfx_gx2_create_and_load_new_shader, + gfx_gx2_lookup_shader, + gfx_gx2_shader_get_info, + gfx_gx2_new_texture, + gfx_gx2_select_texture, + gfx_gx2_upload_texture, + gfx_gx2_set_sampler_parameters, + gfx_gx2_set_depth_test_and_mask, + gfx_gx2_set_zmode_decal, + gfx_gx2_set_viewport, + gfx_gx2_set_scissor, + gfx_gx2_set_use_alpha, + gfx_gx2_draw_triangles, + gfx_gx2_init, + gfx_gx2_on_resize, + gfx_gx2_start_frame, + gfx_gx2_end_frame, + gfx_gx2_finish_render, + gfx_gx2_create_framebuffer, + gfx_gx2_update_framebuffer_parameters, + gfx_gx2_start_draw_to_framebuffer, + gfx_gx2_clear_framebuffer, + gfx_gx2_resolve_msaa_color_buffer, + gfx_gx2_get_pixel_depth, + gfx_gx2_get_framebuffer_texture_id, + gfx_gx2_select_texture_fb, + gfx_gx2_delete_texture, + gfx_gx2_set_texture_filter, + gfx_gx2_get_texture_filter +}; + +#endif diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_gx2.h b/libultraship/libultraship/Lib/Fast3D/gfx_gx2.h new file mode 100644 index 000000000..1eeae4255 --- /dev/null +++ b/libultraship/libultraship/Lib/Fast3D/gfx_gx2.h @@ -0,0 +1,12 @@ +#ifndef GFX_GX2_H +#define GFX_GX2_H + +#include "gfx_rendering_api.h" + +void gfx_gx2_shutdown(void); + +struct ImGui_ImplGX2_Texture* gfx_gx2_texture_for_imgui(uint32_t texture_id); + +extern struct GfxRenderingAPI gfx_gx2_api; + +#endif diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp index 8aebec9e2..fc35f6f7f 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_pc.cpp @@ -1382,12 +1382,25 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo buf_vbo[buf_vbo_len++] = u / tex_width[t]; buf_vbo[buf_vbo_len++] = v / tex_height[t]; - if (tm & (1 << 2 * t)) { + bool clampS = tm & (1 << 2 * t); + bool clampT = tm & (1 << 2 * t + 1); + + if (clampS) { buf_vbo[buf_vbo_len++] = (tex_width2[t] - 0.5f) / tex_width[t]; } - if (tm & (1 << 2 * t + 1)) { +#ifdef __WIIU__ + else { + buf_vbo[buf_vbo_len++] = 0.0f; + } +#endif + if (clampT) { buf_vbo[buf_vbo_len++] = (tex_height2[t] - 0.5f) / tex_height[t]; } +#ifdef __WIIU__ + else { + buf_vbo[buf_vbo_len++] = 0.0f; + } +#endif } if (use_fog) { @@ -1464,6 +1477,12 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo buf_vbo[buf_vbo_len++] = color->r / 255.0f; buf_vbo[buf_vbo_len++] = color->g / 255.0f; buf_vbo[buf_vbo_len++] = color->b / 255.0f; +#ifdef __WIIU__ + // padding + if (!use_alpha) { + buf_vbo[buf_vbo_len++] = 1.0f; + } +#endif } else { if (use_fog && color == &v_arr[i]->color) { @@ -2658,8 +2677,10 @@ void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi, co gfx_current_dimensions.internal_mul = CVar_GetFloat("gInternalResolution", 1); #endif gfx_msaa_level = CVar_GetS32("gMSAAValue", 1); +#ifndef __WIIU__ // Wii U overrides dimentions in gfx_wapi->init to match framebuffer size gfx_current_dimensions.width = width; gfx_current_dimensions.height = height; +#endif game_framebuffer = gfx_rapi->create_framebuffer(); game_framebuffer_msaa_resolved = gfx_rapi->create_framebuffer(); diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_wiiu.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_wiiu.cpp new file mode 100644 index 000000000..345d40f6e --- /dev/null +++ b/libultraship/libultraship/Lib/Fast3D/gfx_wiiu.cpp @@ -0,0 +1,467 @@ +#ifdef __WIIU__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifndef _LANGUAGE_C +#define _LANGUAGE_C +#endif +#include "PR/ultra64/gbi.h" + +#include "gfx_window_manager_api.h" +#include "gfx_pc.h" +#include "gfx_gx2.h" +#include "gfx_wiiu.h" + +#include "Lib/ImGui/backends/wiiu/imgui_impl_wiiu.h" +#include "../../WiiUImpl.h" +#include "../../ImGuiImpl.h" +#include "../../Hooks.h" + +static MEMHeapHandle heap_MEM1 = nullptr; +static MEMHeapHandle heap_foreground = nullptr; + +bool has_foreground = false; +static void *mem1_storage = nullptr; +static void *command_buffer_pool = nullptr; +GX2ContextState *context_state = nullptr; + +static GX2TVRenderMode tv_render_mode; +static void *tv_scan_buffer = nullptr; +static uint32_t tv_scan_buffer_size = 0; +static uint32_t tv_width; +static uint32_t tv_height; + +static GX2DrcRenderMode drc_render_mode; +static void *drc_scan_buffer = nullptr; +static uint32_t drc_scan_buffer_size = 0; + +static int frame_divisor = 1; + +// for ImGui DeltaTime +// (initialized to 1 to not trigger imguis assert on initial draw) +uint32_t frametime = 1; + +bool gfx_wiiu_init_mem1(void) { + MEMHeapHandle heap = MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM1); + uint32_t size; + void *base; + + size = MEMGetAllocatableSizeForFrmHeapEx(heap, 4); + if (!size) { + printf("%s: MEMGetAllocatableSizeForFrmHeapEx == 0", __FUNCTION__); + return false; + } + + base = MEMAllocFromFrmHeapEx(heap, size, 4); + if (!base) { + printf("%s: MEMAllocFromFrmHeapEx(heap, 0x%X, 4) failed", __FUNCTION__, size); + return false; + } + + heap_MEM1 = MEMCreateExpHeapEx(base, size, 0); + if (!heap_MEM1) { + printf("%s: MEMCreateExpHeapEx(%p, 0x%X, 0) failed", __FUNCTION__, base, size); + return false; + } + + return true; +} + +void gfx_wiiu_destroy_mem1(void) { + MEMHeapHandle heap = MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM1); + + if (heap_MEM1) { + MEMDestroyExpHeap(heap_MEM1); + heap_MEM1 = NULL; + } +} + +bool gfx_wiiu_init_foreground(void) { + MEMHeapHandle heap = MEMGetBaseHeapHandle(MEM_BASE_HEAP_FG); + uint32_t size; + void *base; + + size = MEMGetAllocatableSizeForFrmHeapEx(heap, 4); + if (!size) { + printf("%s: MEMAllocFromFrmHeapEx(heap, 0x%X, 4)", __FUNCTION__, size); + return false; + } + + base = MEMAllocFromFrmHeapEx(heap, size, 4); + if (!base) { + printf("%s: MEMGetAllocatableSizeForFrmHeapEx == 0", __FUNCTION__); + return false; + } + + heap_foreground = MEMCreateExpHeapEx(base, size, 0); + if (!heap_foreground) { + printf("%s: MEMCreateExpHeapEx(%p, 0x%X, 0)", __FUNCTION__, base, size); + return false; + } + + return true; +} + +void gfx_wiiu_destroy_foreground(void) { + MEMHeapHandle foreground = MEMGetBaseHeapHandle(MEM_BASE_HEAP_FG); + + if (heap_foreground) { + MEMDestroyExpHeap(heap_foreground); + heap_foreground = NULL; + } + + MEMFreeToFrmHeap(foreground, MEM_FRM_HEAP_FREE_ALL); +} + +void *gfx_wiiu_alloc_mem1(uint32_t size, uint32_t alignment) { + void *block; + + if (!heap_MEM1) { + return NULL; + } + + if (alignment < 4) { + alignment = 4; + } + + block = MEMAllocFromExpHeapEx(heap_MEM1, size, alignment); + return block; +} + +void gfx_wiiu_free_mem1(void *block) { + if (!heap_MEM1) { + return; + } + + MEMFreeToExpHeap(heap_MEM1, block); +} + +void *gfx_wiiu_alloc_foreground(uint32_t size, uint32_t alignment) { + void *block; + + if (!heap_foreground) { + return NULL; + } + + if (alignment < 4) { + alignment = 4; + } + + block = MEMAllocFromExpHeapEx(heap_foreground, size, alignment); + return block; +} + +void gfx_wiiu_free_foreground(void *block) { + if (!heap_foreground) { + return; + } + + MEMFreeToExpHeap(heap_foreground, block); +} + +static uint32_t gfx_wiiu_proc_callback_acquired(void *context) { + has_foreground = true; + + bool result = gfx_wiiu_init_foreground(); + assert(result); + + tv_scan_buffer = gfx_wiiu_alloc_foreground(tv_scan_buffer_size, GX2_SCAN_BUFFER_ALIGNMENT); + assert(tv_scan_buffer); + + GX2Invalidate(GX2_INVALIDATE_MODE_CPU, tv_scan_buffer, tv_scan_buffer_size); + GX2SetTVBuffer(tv_scan_buffer, tv_scan_buffer_size, tv_render_mode, GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8, GX2_BUFFERING_MODE_DOUBLE); + + drc_scan_buffer = gfx_wiiu_alloc_foreground(drc_scan_buffer_size, GX2_SCAN_BUFFER_ALIGNMENT); + assert(drc_scan_buffer); + + GX2Invalidate(GX2_INVALIDATE_MODE_CPU, drc_scan_buffer, drc_scan_buffer_size); + GX2SetDRCBuffer(drc_scan_buffer, drc_scan_buffer_size, drc_render_mode, GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8, GX2_BUFFERING_MODE_DOUBLE); + + return 0; +} + +static uint32_t gfx_wiiu_proc_callback_released(void* context) { + if (tv_scan_buffer) { + gfx_wiiu_free_foreground(tv_scan_buffer); + tv_scan_buffer = nullptr; + } + + if (drc_scan_buffer) { + gfx_wiiu_free_foreground(drc_scan_buffer); + drc_scan_buffer = nullptr; + } + + gfx_wiiu_destroy_foreground(); + + has_foreground = false; + + return 0; +} + +static void gfx_wiiu_init(const char *game_name, bool start_in_fullscreen, uint32_t width, uint32_t height) { + WHBProcInit(); + + uint32_t mem1_addr, mem1_size; + OSGetMemBound(OS_MEM1, &mem1_addr, &mem1_size); + mem1_storage = memalign(0x40, mem1_size); + assert(mem1_storage); + + ProcUISetMEM1Storage(mem1_storage, mem1_size); + + bool result = gfx_wiiu_init_mem1(); + assert(result); + + command_buffer_pool = memalign(GX2_COMMAND_BUFFER_ALIGNMENT, 0x400000); + assert(command_buffer_pool); + + uint32_t initAttribs[] = { + GX2_INIT_CMD_BUF_BASE, (uintptr_t) command_buffer_pool, + GX2_INIT_CMD_BUF_POOL_SIZE, 0x400000, + GX2_INIT_ARGC, 0, + GX2_INIT_ARGV, 0, + GX2_INIT_END + }; + GX2Init(initAttribs); + + switch(GX2GetSystemTVScanMode()) { + case GX2_TV_SCAN_MODE_480I: + case GX2_TV_SCAN_MODE_480P: + tv_render_mode = GX2_TV_RENDER_MODE_WIDE_480P; + tv_width = 854; + tv_height = 480; + break; + case GX2_TV_SCAN_MODE_1080I: + case GX2_TV_SCAN_MODE_1080P: + tv_render_mode = GX2_TV_RENDER_MODE_WIDE_1080P; + tv_width = 1920; + tv_height = 1080; + break; + case GX2_TV_SCAN_MODE_720P: + default: + tv_render_mode = GX2_TV_RENDER_MODE_WIDE_720P; + tv_width = 1280; + tv_height = 720; + break; + } + + drc_render_mode = GX2GetSystemDRCScanMode(); + + uint32_t unk; + GX2CalcTVSize(tv_render_mode, GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8, GX2_BUFFERING_MODE_DOUBLE, &tv_scan_buffer_size, &unk); + GX2CalcDRCSize(drc_render_mode, GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8, GX2_BUFFERING_MODE_DOUBLE, &drc_scan_buffer_size, &unk); + + ProcUIRegisterCallback(PROCUI_CALLBACK_ACQUIRE, gfx_wiiu_proc_callback_acquired, nullptr, 100); + ProcUIRegisterCallback(PROCUI_CALLBACK_RELEASE, gfx_wiiu_proc_callback_released, nullptr, 100); + + gfx_wiiu_proc_callback_acquired(nullptr); + + context_state = (GX2ContextState *) memalign(GX2_CONTEXT_STATE_ALIGNMENT, sizeof(GX2ContextState)); + assert(context_state); + + GX2SetupContextStateEx(context_state, TRUE); + GX2SetContextState(context_state); + + GX2SetTVScale(WIIU_DEFAULT_FB_WIDTH, WIIU_DEFAULT_FB_HEIGHT); + GX2SetDRCScale(WIIU_DEFAULT_FB_WIDTH, WIIU_DEFAULT_FB_HEIGHT); + + GX2SetSwapInterval(frame_divisor); + + gfx_current_dimensions.width = gfx_current_game_window_viewport.width = WIIU_DEFAULT_FB_WIDTH; + gfx_current_dimensions.height = gfx_current_game_window_viewport.height = WIIU_DEFAULT_FB_HEIGHT; + + SohImGui::WindowImpl window_impl; + window_impl.backend = SohImGui::Backend::GX2; + window_impl.gx2.width = WIIU_DEFAULT_FB_WIDTH; + window_impl.gx2.height = WIIU_DEFAULT_FB_HEIGHT; + SohImGui::Init(window_impl); +} + +static void gfx_wiiu_shutdown(void) { + if (has_foreground) { + gfx_wiiu_proc_callback_released(nullptr); + gfx_wiiu_destroy_mem1(); + } + + GX2Shutdown(); + + if (context_state) { + free(context_state); + context_state = nullptr; + } + + if (command_buffer_pool) { + free(command_buffer_pool); + command_buffer_pool = nullptr; + } + + ProcUISetMEM1Storage(nullptr, 0); + free(mem1_storage); +} + +void gfx_wiiu_set_context_state(void) { + GX2SetContextState(context_state); +} + +static void gfx_wiiu_set_fullscreen_changed_callback(void (*on_fullscreen_changed)(bool is_now_fullscreen)) { +} + +static void gfx_wiiu_set_fullscreen(bool enable) { +} + +static void gfx_wiiu_show_cursor(bool hide) { +} + +static void gfx_wiiu_set_keyboard_callbacks(bool (*on_key_down)(int scancode), bool (*on_key_up)(int scancode), void (*on_all_keys_up)(void)) { +} + +static void gfx_wiiu_main_loop(void (*run_one_game_iter)(void)) { + while (WHBProcIsRunning()) { + run_one_game_iter(); + } + + Ship::ExecuteHooks(); + Ship::WiiU::Exit(); + + gfx_gx2_shutdown(); + gfx_wiiu_shutdown(); + WHBProcShutdown(); +} + +static void gfx_wiiu_get_dimensions(uint32_t *width, uint32_t *height) { + *width = WIIU_DEFAULT_FB_WIDTH; + *height = WIIU_DEFAULT_FB_HEIGHT; +} + +static void gfx_wiiu_handle_events(void) { + Ship::WiiU::Update(); + + ImGui_ImplWiiU_ControllerInput input{}; + + VPADReadError vpad_error; + input.vpad = Ship::WiiU::GetVPADStatus(&vpad_error); + if (vpad_error != VPAD_READ_SUCCESS) { + input.vpad = nullptr; + } + + KPADError kpad_error; + for (int i = 0; i < 4; i++) { + input.kpad[i] = Ship::WiiU::GetKPADStatus((WPADChan) i, &kpad_error); + if (kpad_error != KPAD_ERROR_OK) { + input.kpad[i] = nullptr; + } + } + + SohImGui::EventImpl event_impl; + event_impl.gx2.input = &input; + SohImGui::Update(event_impl); +} + +static bool gfx_wiiu_start_frame(void) { + uint32_t swap_count, flip_count; + OSTime last_flip, last_vsync; + uint32_t wait_count = 0; + + while (true) { + GX2GetSwapStatus(&swap_count, &flip_count, &last_flip, &last_vsync); + + if (flip_count >= swap_count) { + break; + } + + if (wait_count >= 10) { + // GPU timed out, drop frame + return false; + } + + wait_count++; + GX2WaitForVsync(); + } + + return true; +} + +static void gfx_wiiu_swap_buffers_begin(void) { + GX2SwapScanBuffers(); + GX2Flush(); + + gfx_wiiu_set_context_state(); + + GX2SetTVEnable(TRUE); + GX2SetDRCEnable(TRUE); +} + +static void gfx_wiiu_swap_buffers_end(void) { + static OSTick tick = 0; + frametime = OSTicksToMicroseconds(OSGetSystemTick() - tick); + tick = OSGetSystemTick(); +} + +static double gfx_wiiu_get_time(void) { + return 0.0; +} + +static void gfx_wiiu_set_target_fps(int fps) { + // use the nearest divisor + int divisor = 60 / fps; + if (divisor < 1) { + divisor = 1; + } + + if (frame_divisor != divisor) { + GX2SetSwapInterval(divisor); + frame_divisor = divisor; + } +} + +static void gfx_wiiu_set_maximum_frame_latency(int latency) { +} + +static float gfx_wiiu_get_detected_hz(void) { + return 0; +} + +struct GfxWindowManagerAPI gfx_wiiu = { + gfx_wiiu_init, + gfx_wiiu_set_keyboard_callbacks, + gfx_wiiu_set_fullscreen_changed_callback, + gfx_wiiu_set_fullscreen, + gfx_wiiu_show_cursor, + gfx_wiiu_main_loop, + gfx_wiiu_get_dimensions, + gfx_wiiu_handle_events, + gfx_wiiu_start_frame, + gfx_wiiu_swap_buffers_begin, + gfx_wiiu_swap_buffers_end, + gfx_wiiu_get_time, + gfx_wiiu_set_target_fps, + gfx_wiiu_set_maximum_frame_latency, + gfx_wiiu_get_detected_hz, +}; + +#endif diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_wiiu.h b/libultraship/libultraship/Lib/Fast3D/gfx_wiiu.h new file mode 100644 index 000000000..43da7d36f --- /dev/null +++ b/libultraship/libultraship/Lib/Fast3D/gfx_wiiu.h @@ -0,0 +1,38 @@ +#ifndef GFX_WIIU_H +#define GFX_WIIU_H +#ifdef __WIIU__ + +#include +#include + +#include "gfx_window_manager_api.h" + +// make the default fb always 1080p to not mess with scaling +#define WIIU_DEFAULT_FB_WIDTH 1920 +#define WIIU_DEFAULT_FB_HEIGHT 1080 + +extern bool has_foreground; +extern uint32_t frametime; + +bool gfx_wiiu_init_mem1(void); + +void gfx_wiiu_destroy_mem1(void); + +bool gfx_wiiu_init_foreground(void); + +void gfx_wiiu_destroy_foreground(void); + +void *gfx_wiiu_alloc_mem1(uint32_t size, uint32_t alignment); + +void gfx_wiiu_free_mem1(void *block); + +void *gfx_wiiu_alloc_foreground(uint32_t size, uint32_t alignment); + +void gfx_wiiu_free_foreground(void *block); + +void gfx_wiiu_set_context_state(void); + +extern struct GfxWindowManagerAPI gfx_wiiu; + +#endif +#endif diff --git a/libultraship/libultraship/Lib/Fast3D/gx2_shader_gen.c b/libultraship/libultraship/Lib/Fast3D/gx2_shader_gen.c new file mode 100644 index 000000000..9fb676ca1 --- /dev/null +++ b/libultraship/libultraship/Lib/Fast3D/gx2_shader_gen.c @@ -0,0 +1,810 @@ +#ifdef __WIIU__ + +#include "gx2_shader_gen.h" +#include "gx2_shader_inl.h" + +#include +#include + +#define ROUNDUP(x, align) (((x) + ((align) -1)) & ~((align) -1)) + +#define FRAG_COORD_REG _R0 +#define TEXEL_REG _R1 +#define FOG_REG _R3 +#define GRAYSCALE_REG _R4 + +enum { + SHADER_TEXINFO0 = SHADER_COMBINED + 1, + SHADER_TEXINFO1, +}; + +static uint8_t get_reg(struct CCFeatures *cc_features, uint8_t c) { + if (c == SHADER_0) { + return ALU_SRC_0; + } + if (c == SHADER_1) { + return ALU_SRC_1; + } + + if (c == SHADER_COMBINED) { + return TEXEL_REG; + } + + if (c >= SHADER_INPUT_1 && c <= SHADER_INPUT_7) { + return _R(5 + (c - SHADER_INPUT_1)); + } + + uint8_t input_last = (cc_features->num_inputs + 5) - 1; + + if (c == SHADER_TEXEL0 || c == SHADER_TEXEL0A) { + // reuse unused regs + if (!cc_features->opt_noise) { + return FRAG_COORD_REG; + } else if (!cc_features->opt_fog) { + return FOG_REG; + } else if (!cc_features->opt_grayscale) { + return GRAYSCALE_REG; + } + + return _R(input_last + 1); + } + if (c == SHADER_TEXEL1 || c == SHADER_TEXEL1A) { + // if the shader doesn't use texture 0 we can reuse it for texture 1 + if (!cc_features->used_textures[0]) { + return get_reg(cc_features, SHADER_TEXEL0); + } + // reuse unused regs which tex 0 doesn't use yet + else if (!cc_features->opt_fog && get_reg(cc_features, SHADER_TEXEL0) != FOG_REG) { + return FOG_REG; + } else if (!cc_features->opt_grayscale && get_reg(cc_features, SHADER_TEXEL0) != GRAYSCALE_REG) { + return GRAYSCALE_REG; + } + + return _R(input_last + 2); + } + + // reuse the regs above + if (c == SHADER_TEXINFO0) { + return get_reg(cc_features, SHADER_TEXEL0); + } + if (c == SHADER_TEXINFO1) { + return get_reg(cc_features, SHADER_TEXEL1); + } + + return 0; +} + +static uint8_t get_num_regs(struct CCFeatures *cc_features) { + uint8_t input_count = cc_features->num_inputs + 5; + + uint8_t last_tex_reg; + if (cc_features->used_textures[1]) { + last_tex_reg = get_reg(cc_features, SHADER_TEXEL1) + 1; + } else if (cc_features->used_textures[0]) { + last_tex_reg = get_reg(cc_features, SHADER_TEXEL0) + 1; + } else { + return input_count; + } + + return (last_tex_reg < input_count) ? input_count : last_tex_reg; +} + +#define ADD_INSTR(...) \ + uint64_t tmp[] = {__VA_ARGS__}; \ + memcpy(*alu_ptr, tmp, sizeof(tmp)); \ + *alu_ptr += sizeof(tmp) / sizeof(uint64_t) + +static inline void add_tex_clamp_S_T(struct CCFeatures *cc_features, uint64_t **alu_ptr, uint8_t tex) { + uint8_t texinfo_reg = get_reg(cc_features, (tex == 0) ? SHADER_TEXINFO0 : SHADER_TEXINFO1); + uint8_t texcoord_reg = (tex == 0) ? _R1 : _R2; + + ADD_INSTR( + /* R127.xy = (float) texinfo.xy */ + ALU_INT_TO_FLT(_R127, _x, texinfo_reg, _x) SCL_210 + ALU_LAST, + + ALU_INT_TO_FLT(_R127, _y, texinfo_reg, _y) SCL_210 + ALU_LAST, + + /* R127.xy = 0.5f / texSize */ + ALU_RECIP_IEEE(__, _x, _R127, _x) SCL_210 + ALU_LAST, + + ALU_MUL_IEEE(_R127, _x, ALU_SRC_PS, _x, ALU_SRC_0_5, _x), + ALU_RECIP_IEEE(__, _y, _R127, _y) SCL_210 + ALU_LAST, + + ALU_MUL_IEEE(_R127, _y, ALU_SRC_PS, _y, ALU_SRC_0_5, _x) + ALU_LAST, + + /* texCoord.xy = clamp(texCoord.xy, R127.xy, texClamp.xy) */ + ALU_MAX(__, _x, texcoord_reg, _x, _R127, _x), + ALU_MAX(__, _y, texcoord_reg, _y, _R127, _y) + ALU_LAST, + + ALU_MIN(texcoord_reg, _x, ALU_SRC_PV, _x, texcoord_reg, _z), + ALU_MIN(texcoord_reg, _y, ALU_SRC_PV, _y, texcoord_reg, _w) + ALU_LAST, + ); +} + +static inline void add_tex_clamp_S(struct CCFeatures *cc_features, uint64_t **alu_ptr, uint8_t tex) { + uint8_t texinfo_reg = get_reg(cc_features, (tex == 0) ? SHADER_TEXINFO0 : SHADER_TEXINFO1); + uint8_t texcoord_reg = (tex == 0) ? _R1 : _R2; + + ADD_INSTR( + /* R127.x = (float) texinfo.x */ + ALU_INT_TO_FLT(_R127, _x, texinfo_reg, _x) SCL_210 + ALU_LAST, + + /* R127.x = 0.5f / texSize */ + ALU_RECIP_IEEE(__, _x, _R127, _x) SCL_210 + ALU_LAST, + + ALU_MUL_IEEE(_R127, _x, ALU_SRC_PS, _x, ALU_SRC_0_5, _x) + ALU_LAST, + + /* texCoord.xy = clamp(texCoord.xy, R127.xy, texClamp.xy) */ + ALU_MAX(__, _x, texcoord_reg, _x, _R127, _x) + ALU_LAST, + + ALU_MIN(texcoord_reg, _x, ALU_SRC_PV, _x, texcoord_reg, _z) + ALU_LAST, + ); +} + +static inline void add_tex_clamp_T(struct CCFeatures *cc_features, uint64_t **alu_ptr, uint8_t tex) { + uint8_t texinfo_reg = get_reg(cc_features, (tex == 0) ? SHADER_TEXINFO0 : SHADER_TEXINFO1); + uint8_t texcoord_reg = (tex == 0) ? _R1 : _R2; + + ADD_INSTR( + /* R127.y = (float) texinfo.y */ + ALU_INT_TO_FLT(_R127, _y, texinfo_reg, _y) SCL_210 + ALU_LAST, + + /* R127.y = 0.5f / texSize */ + ALU_RECIP_IEEE(__, _x, _R127, _y) SCL_210 + ALU_LAST, + + ALU_MUL_IEEE(_R127, _y, ALU_SRC_PS, _x, ALU_SRC_0_5, _x) + ALU_LAST, + + /* texCoord.xy = clamp(texCoord.xy, R127.xy, texClamp.xy) */ + ALU_MAX(__, _y, texcoord_reg, _y, _R127, _y) + ALU_LAST, + + ALU_MIN(texcoord_reg, _y, ALU_SRC_PV, _y, texcoord_reg, _w) + ALU_LAST, + ); +} + +static inline void add_mov(struct CCFeatures *cc_features, uint64_t **alu_ptr, uint8_t src, bool single) { + bool src_alpha = (src == SHADER_TEXEL0A) || (src == SHADER_TEXEL1A); + src = get_reg(cc_features, src); + + /* texel = src */ + if (single) { + ADD_INSTR( + ALU_MOV(TEXEL_REG, _w, src, _w) + ALU_LAST, + ); + } else { + ADD_INSTR( + ALU_MOV(TEXEL_REG, _x, src, src_alpha ? _w :_x), + ALU_MOV(TEXEL_REG, _y, src, src_alpha ? _w :_y), + ALU_MOV(TEXEL_REG, _z, src, src_alpha ? _w :_z) + ALU_LAST, + ); + } +} + +static inline void add_mul(struct CCFeatures *cc_features, uint64_t **alu_ptr, uint8_t src0, uint8_t src1, bool single) { + bool src0_alpha = (src0 == SHADER_TEXEL0A) || (src0 == SHADER_TEXEL1A); + bool src1_alpha = (src1 == SHADER_TEXEL0A) || (src1 == SHADER_TEXEL1A); + src0 = get_reg(cc_features, src0); + src1 = get_reg(cc_features, src1); + + /* texel = src0 * src1 */ + if (single) { + ADD_INSTR( + ALU_MUL(TEXEL_REG, _w, src0, _w, src1, _w) + ALU_LAST, + ); + } else { + ADD_INSTR( + ALU_MUL(TEXEL_REG, _x, src0, src0_alpha ? _w : _x, src1, src1_alpha ? _w : _x), + ALU_MUL(TEXEL_REG, _y, src0, src0_alpha ? _w : _y, src1, src1_alpha ? _w : _y), + ALU_MUL(TEXEL_REG, _z, src0, src0_alpha ? _w : _z, src1, src1_alpha ? _w : _z) + ALU_LAST, + ); + } +} + +static inline void add_mix(struct CCFeatures *cc_features, uint64_t **alu_ptr, uint8_t src0, uint8_t src1, uint8_t src2, uint8_t src3, bool single) { + bool src0_alpha = (src0 == SHADER_TEXEL0A) || (src0 == SHADER_TEXEL1A); + bool src1_alpha = (src1 == SHADER_TEXEL0A) || (src1 == SHADER_TEXEL1A); + bool src2_alpha = (src2 == SHADER_TEXEL0A) || (src2 == SHADER_TEXEL1A); + bool src3_alpha = (src3 == SHADER_TEXEL0A) || (src3 == SHADER_TEXEL1A); + src0 = get_reg(cc_features, src0); + src1 = get_reg(cc_features, src1); + src2 = get_reg(cc_features, src2); + src3 = get_reg(cc_features, src3); + + /* texel = (src0 - src1) * src2 - src3 */ + if (single) { + ADD_INSTR( + ALU_ADD(__, _w, src0, _w, src1 _NEG, _w) + ALU_LAST, + + ALU_MULADD(TEXEL_REG, _w, ALU_SRC_PV, _w, src2, _w, src3, _w) + ALU_LAST, + ); + } else { + ADD_INSTR( + ALU_ADD(__, _x, src0, src0_alpha ? _w : _x, src1 _NEG, src1_alpha ? _w : _x), + ALU_ADD(__, _y, src0, src0_alpha ? _w : _y, src1 _NEG, src1_alpha ? _w : _y), + ALU_ADD(__, _z, src0, src0_alpha ? _w : _z, src1 _NEG, src1_alpha ? _w : _z) + ALU_LAST, + + ALU_MULADD(TEXEL_REG, _x, ALU_SRC_PV, _x, src2, src2_alpha ? _w : _x, src3, src3_alpha ? _w : _x), + ALU_MULADD(TEXEL_REG, _y, ALU_SRC_PV, _y, src2, src2_alpha ? _w : _y, src3, src3_alpha ? _w : _y), + ALU_MULADD(TEXEL_REG, _z, ALU_SRC_PV, _z, src2, src2_alpha ? _w : _z, src3, src3_alpha ? _w : _z) + ALU_LAST, + ); + } +} +#undef ADD_INSTR + +static void append_tex_clamp(struct CCFeatures *cc_features, uint64_t **alu_ptr, uint8_t tex, bool s, bool t) { + if (s && t) { + add_tex_clamp_S_T(cc_features, alu_ptr, tex); + } else if (s) { + add_tex_clamp_S(cc_features, alu_ptr, tex); + } else { + add_tex_clamp_T(cc_features, alu_ptr, tex); + } +} + +static void append_formula(struct CCFeatures *cc_features, uint64_t **alu_ptr, uint8_t c[2][4], bool do_single, bool do_multiply, bool do_mix, bool only_alpha) { + if (do_single) { + add_mov(cc_features, alu_ptr, c[only_alpha][3], only_alpha); + } else if (do_multiply) { + add_mul(cc_features, alu_ptr, c[only_alpha][0], c[only_alpha][2], only_alpha); + } else if (do_mix) { + add_mix(cc_features, alu_ptr, c[only_alpha][0], c[only_alpha][1], c[only_alpha][2], c[only_alpha][1], only_alpha); + } else { + add_mix(cc_features, alu_ptr, c[only_alpha][0], c[only_alpha][1], c[only_alpha][2], c[only_alpha][3], only_alpha); + } +} + +static const uint64_t noise_instructions[] = { + /* R127 = floor(gl_FragCoord.xy * window_params.x) */ + ALU_MUL(__, _x, FRAG_COORD_REG, _x, _C(0), _x), + ALU_MUL(__, _y, FRAG_COORD_REG, _y, _C(0), _x) + ALU_LAST, + + ALU_FLOOR(_R127, _x, ALU_SRC_PV, _x), + ALU_FLOOR(_R127, _y, ALU_SRC_PV, _y) + ALU_LAST, + + /* R127 = sin(vec3(R127.x, R127.y, window_params.y)) */ + ALU_MULADD(_R127, _x, _R127, _x, ALU_SRC_LITERAL, _x, ALU_SRC_0_5, _x), + ALU_MULADD(_R127, _y, _R127, _y, ALU_SRC_LITERAL, _x, ALU_SRC_0_5, _x), + ALU_MULADD(_R127, _z, _C(0), _y, ALU_SRC_LITERAL, _x, ALU_SRC_0_5, _x) + ALU_LAST, + ALU_LITERAL(0x3E22F983 /* 0.1591549367f (radians -> revolutions) */), + + ALU_FRACT(__, _x, _R127, _x), + ALU_FRACT(__, _y, _R127, _y), + ALU_FRACT(__, _z, _R127, _z) + ALU_LAST, + + ALU_MULADD(_R127, _x, ALU_SRC_PV, _x, ALU_SRC_LITERAL, _x, ALU_SRC_LITERAL, _y), + ALU_MULADD(_R127, _y, ALU_SRC_PV, _y, ALU_SRC_LITERAL, _x, ALU_SRC_LITERAL, _y), + ALU_MULADD(_R127, _z, ALU_SRC_PV, _z, ALU_SRC_LITERAL, _x, ALU_SRC_LITERAL, _y) + ALU_LAST, + ALU_LITERAL2(0x40C90FDB /* 6.283185482f (tau) */, 0xC0490FDB /* -3.141592741f (-pi) */), + + ALU_MUL(_R127, _x, ALU_SRC_PV, _x, ALU_SRC_LITERAL, _x), + ALU_MUL(_R127, _y, ALU_SRC_PV, _y, ALU_SRC_LITERAL, _x), + ALU_MUL(_R127, _z, ALU_SRC_PV, _z, ALU_SRC_LITERAL, _x) + ALU_LAST, + ALU_LITERAL(0x3E22F983 /* 0.1591549367f (radians -> revolutions) */), + + ALU_SIN(_R127, _x, _R127, _x) SCL_210 + ALU_LAST, + + ALU_SIN(_R127, _y, _R127, _y) SCL_210 + ALU_LAST, + + ALU_SIN(_R127, _z, _R127, _z) SCL_210 + ALU_LAST, + + /* R127.x = dot(R127.xyz, vec3(12.9898, 78.233, 37.719)); */ + ALU_DOT4(_R127, _x, _R127, _x, ALU_SRC_LITERAL, _x), + ALU_DOT4(__, _y, _R127, _y, ALU_SRC_LITERAL, _y), + ALU_DOT4(__, _z, _R127, _z, ALU_SRC_LITERAL, _z), + ALU_DOT4(__, _w, ALU_SRC_LITERAL, _w, ALU_SRC_0, _x) + ALU_LAST, + ALU_LITERAL4(0x414FD639 /* 12.9898f */, 0x429C774C /* 78.233f */, 0x4216E042 /* 37.719f */, 0x80000000 /* -0.0f */), + + /* R127.x = fract(sin(R127.x) * 143758.5453); */ + ALU_MULADD(_R127, _x, _R127, _x, ALU_SRC_LITERAL, _x, ALU_SRC_0_5, _x) + ALU_LAST, + ALU_LITERAL(0x3E22F983 /* 0.1591549367f (radians -> revolutions) */), + + ALU_FRACT(__, _x, _R127, _x) + ALU_LAST, + + ALU_MULADD(_R127, _x, ALU_SRC_PV, _x, ALU_SRC_LITERAL, _x, ALU_SRC_LITERAL, _y) + ALU_LAST, + ALU_LITERAL2(0x40C90FDB /* 6.283185482f (tau) */, 0xC0490FDB /* -3.141592741f (-pi) */), + + ALU_SIN(_R127, _x, _R127, _x) SCL_210 + ALU_LAST, + + ALU_MUL(__, _x, _R127, _x, ALU_SRC_LITERAL, _x) + ALU_LAST, + ALU_LITERAL(0x480C63A3 /* 143758.5453f */), + + ALU_FRACT( _R127, _x, ALU_SRC_PV, _x) + ALU_LAST, + + /* texel.a *= floor(R127.x + 0.5); */ + ALU_ADD(__, _x, _R127, _x, ALU_SRC_0_5, _x) + ALU_LAST, + + ALU_FLOOR(__, _x, ALU_SRC_PV, _x) + ALU_LAST, + + ALU_MUL(TEXEL_REG, _w, TEXEL_REG, _w, ALU_SRC_PV, _x) + ALU_LAST, +}; + +static GX2UniformVar uniformVars[] = { + { "window_params", GX2_SHADER_VAR_TYPE_FLOAT2, 1, 0, -1, }, +}; + +static GX2SamplerVar samplerVars[] = { + { "uTex0", GX2_SAMPLER_VAR_TYPE_SAMPLER_2D, 0 }, + { "uTex1", GX2_SAMPLER_VAR_TYPE_SAMPLER_2D, 1 }, +}; + +#define ADD_INSTR(...) \ + do { \ + uint64_t tmp[] = {__VA_ARGS__}; \ + memcpy(cur_buf, tmp, sizeof(tmp)); \ + cur_buf += sizeof(tmp) / sizeof(uint64_t); \ + } while (0) + +static int generatePixelShader(GX2PixelShader *psh, struct CCFeatures *cc_features) { + static const size_t max_program_buf_size = 512 * sizeof(uint64_t); + uint64_t *program_buf = memalign(GX2_SHADER_PROGRAM_ALIGNMENT, max_program_buf_size); + if (!program_buf) { + return -1; + } + + memset(program_buf, 0, max_program_buf_size); + + // start placing alus at offset 32 + static const uint32_t base_alu_offset = 32; + uint64_t *cur_buf = NULL; + + // check if we need to clamp + bool texclamp[2] = { false, false }; + for (int i = 0; i < 2; i++) { + if (cc_features->used_textures[i]) { + if (cc_features->clamp[i][0] || cc_features->clamp[i][1]) { + texclamp[i] = true; + } + } + } + + uint32_t texclamp_alu_offset = base_alu_offset; + uint32_t texclamp_alu_size = 0; + uint32_t texclamp_alu_cnt = 0; + + if (texclamp[0] || texclamp[1]) { + // texclamp alu + cur_buf = program_buf + texclamp_alu_offset; + + for (int i = 0; i < 2; i++) { + if (cc_features->used_textures[i] && texclamp[i]) { + append_tex_clamp(cc_features, &cur_buf, i, cc_features->clamp[i][0], cc_features->clamp[i][1]); + } + } + + texclamp_alu_size = (uintptr_t) cur_buf - ((uintptr_t) (program_buf + texclamp_alu_offset)); + texclamp_alu_cnt = texclamp_alu_size / sizeof(uint64_t); + } + + // main alu0 + uint32_t main_alu0_offset = texclamp_alu_offset + texclamp_alu_cnt; + cur_buf = program_buf + main_alu0_offset; + + for (int c = 0; c < (cc_features->opt_2cyc ? 2 : 1); c++) { + append_formula(cc_features, &cur_buf, cc_features->c[c], cc_features->do_single[c][0], cc_features->do_multiply[c][0], cc_features->do_mix[c][0], false); + if (cc_features->opt_alpha) { + append_formula(cc_features, &cur_buf, cc_features->c[c], cc_features->do_single[c][1], cc_features->do_multiply[c][1], cc_features->do_mix[c][1], true); + } + } + + if (cc_features->opt_fog) { + ADD_INSTR( + /* texel.rgb = mix(texel.rgb, vFog.rgb, vFog.a); */ + ALU_ADD(__, _x, FOG_REG, _x, _R1 _NEG, _x), + ALU_ADD(__, _y, FOG_REG, _y, _R1 _NEG, _y), + ALU_ADD(__, _z, FOG_REG, _z, _R1 _NEG, _z) + ALU_LAST, + + ALU_MULADD(TEXEL_REG, _x, ALU_SRC_PV, _x, FOG_REG, _w, TEXEL_REG, _x), + ALU_MULADD(TEXEL_REG, _y, ALU_SRC_PV, _y, FOG_REG, _w, TEXEL_REG, _y), + ALU_MULADD(TEXEL_REG, _z, ALU_SRC_PV, _z, FOG_REG, _w, TEXEL_REG, _z) + ALU_LAST, + ); + } + + if (cc_features->opt_texture_edge && cc_features->opt_alpha) { + ADD_INSTR( + /* if (texel.a > 0.19) texel.a = 1.0; else discard; */ + ALU_KILLGT(__, _x, ALU_SRC_LITERAL, _x, TEXEL_REG, _w), + ALU_MOV(TEXEL_REG, _w, ALU_SRC_1, _x) + ALU_LAST, + ALU_LITERAL(0x3e428f5c /*0.19f*/), + ); + } + + const uint32_t main_alu0_size = (uintptr_t) cur_buf - ((uintptr_t) (program_buf + main_alu0_offset)); + const uint32_t main_alu0_cnt = main_alu0_size / sizeof(uint64_t); + + // main alu1 + // place the following instructions into a new alu, in case the other alu uses KILL + const uint32_t main_alu1_offset = main_alu0_offset + main_alu0_cnt; + cur_buf = program_buf + main_alu1_offset; + + if (cc_features->opt_alpha && cc_features->opt_noise) { + memcpy(cur_buf, noise_instructions, sizeof(noise_instructions)); + cur_buf += sizeof(noise_instructions) / sizeof(uint64_t); + } + + if (cc_features->opt_grayscale) { + ADD_INSTR( + /* texel.r + texel.g + texel.b */ + ALU_ADD(__, _x, TEXEL_REG, _x, TEXEL_REG, _y) + ALU_LAST, + + ALU_ADD(__, _x, ALU_SRC_PV, _x, TEXEL_REG, _z) + ALU_LAST, + + /* PV.x / 3 */ + ALU_MUL_IEEE(__, _x, ALU_SRC_PV, _x, ALU_SRC_LITERAL, _x) + ALU_LAST, + ALU_LITERAL(0x3eaaaaab /*0.3333333433f*/), + + /* texel.rgb = mix(texel.rgb, vGrayscaleColor.rgb * intensity, vGrayscaleColor.a); */ + ALU_MULADD(_R127, _x, GRAYSCALE_REG, _x, ALU_SRC_PV, _x, _R1 _NEG, _x), + ALU_MULADD(_R127, _y, GRAYSCALE_REG, _y, ALU_SRC_PV, _x, _R1 _NEG, _y), + ALU_MULADD(_R127, _z, GRAYSCALE_REG, _z, ALU_SRC_PV, _x, _R1 _NEG, _z) + ALU_LAST, + + ALU_MULADD(TEXEL_REG, _x, ALU_SRC_PV, _x, GRAYSCALE_REG, _w, TEXEL_REG, _x), + ALU_MULADD(TEXEL_REG, _y, ALU_SRC_PV, _y, GRAYSCALE_REG, _w, TEXEL_REG, _y), + ALU_MULADD(TEXEL_REG, _z, ALU_SRC_PV, _z, GRAYSCALE_REG, _w, TEXEL_REG, _z) + ALU_LAST, + ); + } + + if (cc_features->opt_alpha) { + if (cc_features->opt_alpha_threshold) { + ADD_INSTR( + /* if (texel.a < 8.0 / 256.0) discard; */ + ALU_KILLGT(__, _x, ALU_SRC_LITERAL, _x, TEXEL_REG, _w) + ALU_LAST, + ALU_LITERAL(0x3d000000 /*0.03125f*/), + ); + } + + if (cc_features->opt_invisible) { + ADD_INSTR( + /* texel.a = 0.0; */ + ALU_MOV(TEXEL_REG, _w, ALU_SRC_0, _x) + ALU_LAST, + ); + } + } + + const uint32_t main_alu1_size = (uintptr_t) cur_buf - ((uintptr_t) (program_buf + main_alu1_offset)); + const uint32_t main_alu1_cnt = main_alu1_size / sizeof(uint64_t); + + // tex + uint32_t num_textures = cc_features->used_textures[0] + cc_features->used_textures[1]; + uint32_t num_texinfo = texclamp[0] + texclamp[1]; + + uint32_t texinfo_offset = ROUNDUP(main_alu1_offset + main_alu1_cnt, 16); + uint32_t cur_tex_offset = texinfo_offset; + + for (int i = 0; i < 2; i++) { + if (cc_features->used_textures[i] && texclamp[i]) { + uint8_t dst_reg = get_reg(cc_features, (i == 0) ? SHADER_TEXINFO0 : SHADER_TEXINFO1); + + uint64_t texinfo_buf[] = { + TEX_GET_TEXTURE_INFO(dst_reg, _x, _y, _m, _m, _R1, _0, _0, _0, _0, _t(i), _s(i)) + }; + + memcpy(program_buf + cur_tex_offset, texinfo_buf, sizeof(texinfo_buf)); + cur_tex_offset += sizeof(texinfo_buf) / sizeof(uint64_t); + } + } + + uint32_t texsample_offset = cur_tex_offset; + + for (int i = 0; i < 2; i++) { + if (cc_features->used_textures[i]) { + uint8_t texcoord_reg = (i == 0) ? _R1 : _R2; + uint8_t dst_reg = get_reg(cc_features, (i == 0) ? SHADER_TEXEL0 : SHADER_TEXEL1); + + uint64_t tex_buf[] = { + TEX_SAMPLE(dst_reg, _x, _y, _z, _w, texcoord_reg, _x, _y, _0, _x, _t(i), _s(i)) + }; + + memcpy(program_buf + cur_tex_offset, tex_buf, sizeof(tex_buf)); + cur_tex_offset += sizeof(tex_buf) / sizeof(uint64_t); + } + } + + // make sure we didn't overflow the buffer + const uint32_t total_program_size = cur_tex_offset * sizeof(uint64_t); + assert(total_program_size <= max_program_buf_size); + + // cf + uint32_t cur_cf_offset = 0; + + // if we use texclamp place those alus first + if (texclamp[0] || texclamp[1]) { + program_buf[cur_cf_offset++] = TEX(texinfo_offset, num_texinfo); + program_buf[cur_cf_offset++] = ALU(texclamp_alu_offset, texclamp_alu_cnt); + } + + if (num_textures > 0) { + program_buf[cur_cf_offset++] = TEX(texsample_offset, num_textures) VALID_PIX; + } + + program_buf[cur_cf_offset++] = ALU(main_alu0_offset, main_alu0_cnt); + + if (main_alu1_cnt > 0) { + program_buf[cur_cf_offset++] = ALU(main_alu1_offset, main_alu1_cnt); + } + + if (cc_features->opt_alpha) { + program_buf[cur_cf_offset++] = EXP_DONE(PIX0, TEXEL_REG, _x, _y, _z, _w) END_OF_PROGRAM; + } else { + program_buf[cur_cf_offset++] = EXP_DONE(PIX0, TEXEL_REG, _x, _y, _z, _1) END_OF_PROGRAM; + } + + // regs + const uint32_t num_ps_inputs = 4 + cc_features->num_inputs; + + psh->regs.sq_pgm_resources_ps = get_num_regs(cc_features); // num_gprs + psh->regs.sq_pgm_exports_ps = 2; // export_mode + psh->regs.spi_ps_in_control_0 = (num_ps_inputs + 1) // num_interp + | (1 << 8) // position_ena + | (1 << 26) // persp_gradient_ena + | (1 << 28); // baryc_sample_cntl + + psh->regs.num_spi_ps_input_cntl = num_ps_inputs + 1; + + // frag pos + psh->regs.spi_ps_input_cntls[0] = 0 | (1 << 8); + + // inputs + for (int i = 0; i < num_ps_inputs; i++) { + psh->regs.spi_ps_input_cntls[i + 1] = i | (1 << 8); + } + + psh->regs.cb_shader_mask = 0xf; // output0_enable + psh->regs.cb_shader_control = 1; // rt0_enable + psh->regs.db_shader_control = (1 << 4) // z_order + | (1 << 6); // kill_enable + + // program + psh->size = total_program_size; + psh->program = program_buf; + + psh->mode = GX2_SHADER_MODE_UNIFORM_REGISTER; + + // uniform vars + psh->uniformVars = uniformVars; + psh->uniformVarCount = sizeof(uniformVars) / sizeof(GX2UniformVar); + + // samplers + psh->samplerVars = samplerVars; + psh->samplerVarCount = sizeof(samplerVars) / sizeof(GX2SamplerVar); + + return 0; +} + +static GX2AttribVar attribVars[] = { + { "aVtxPos", GX2_SHADER_VAR_TYPE_FLOAT4, 0, 0 }, + { "aTexCoord0", GX2_SHADER_VAR_TYPE_FLOAT4, 0, 1 }, + { "aTexCoord1", GX2_SHADER_VAR_TYPE_FLOAT4, 0, 2 }, + { "aFog", GX2_SHADER_VAR_TYPE_FLOAT4, 0, 3 }, + { "aGrayscaleColor", GX2_SHADER_VAR_TYPE_FLOAT4, 0, 4 }, + { "aInput1", GX2_SHADER_VAR_TYPE_FLOAT4, 0, 5 }, + { "aInput2", GX2_SHADER_VAR_TYPE_FLOAT4, 0, 6 }, + { "aInput3", GX2_SHADER_VAR_TYPE_FLOAT4, 0, 7 }, + { "aInput4", GX2_SHADER_VAR_TYPE_FLOAT4, 0, 8 }, + { "aInput5", GX2_SHADER_VAR_TYPE_FLOAT4, 0, 9 }, + { "aInput6", GX2_SHADER_VAR_TYPE_FLOAT4, 0, 10 }, + { "aInput7", GX2_SHADER_VAR_TYPE_FLOAT4, 0, 11 }, +}; + +static int generateVertexShader(GX2VertexShader *vsh, struct CCFeatures *cc_features) { + static const size_t max_program_buf_size = 16 * sizeof(uint64_t); + uint64_t *program_buf = memalign(GX2_SHADER_PROGRAM_ALIGNMENT, max_program_buf_size); + if (!program_buf) { + return -1; + } + + const uint32_t num_ps_inputs = 4 + cc_features->num_inputs; + + uint64_t *cur_buf = program_buf; + + // aVtxPos + ADD_INSTR( + CALL_FS NO_BARRIER, + EXP_DONE(POS0, _R1, _x, _y, _z, _w), + ); + + // params + for (int i = 0; i < num_ps_inputs - 1; i++) { + ADD_INSTR( + EXP(PARAM(i), _R(i + 2), _x, _y, _z, _w) NO_BARRIER, + ); + } + + // last param + ADD_INSTR( + (EXP_DONE(PARAM(num_ps_inputs - 1), _R(num_ps_inputs + 1), _x, _y, _z, _w) NO_BARRIER) + END_OF_PROGRAM, + ); + + const uint32_t program_size = (uintptr_t) cur_buf - ((uintptr_t) program_buf); + assert(program_size <= max_program_buf_size); + + // regs + vsh->regs.sq_pgm_resources_vs = (num_ps_inputs + 2) // num_gprs + | (1 << 8); // stack_size + + // num outputs minus 1 + vsh->regs.spi_vs_out_config = ((num_ps_inputs - 1) << 1); + + vsh->regs.num_spi_vs_out_id = 3; + memset(vsh->regs.spi_vs_out_id, 0xff, sizeof(vsh->regs.spi_vs_out_id)); + vsh->regs.spi_vs_out_id[0] = (0) | (1 << 8) | (2 << 16) | (3 << 24); + vsh->regs.spi_vs_out_id[1] = (4) | (5 << 8) | (6 << 16) | (7 << 24); + vsh->regs.spi_vs_out_id[2] = (8) | (9 << 8) | (10 << 16) | (0xff << 24); + + vsh->regs.sq_vtx_semantic_clear = ~((1 << 12) - 1); + vsh->regs.num_sq_vtx_semantic = 12; + memset(vsh->regs.sq_vtx_semantic, 0xff, sizeof(vsh->regs.sq_vtx_semantic)); + // aVtxPos + vsh->regs.sq_vtx_semantic[0] = 0; + // aTexCoord0 + vsh->regs.sq_vtx_semantic[1] = 1; + // aTexCoord1 + vsh->regs.sq_vtx_semantic[2] = 2; + // aFog + vsh->regs.sq_vtx_semantic[3] = 3; + // aGrayscaleColor + vsh->regs.sq_vtx_semantic[4] = 4; + // aInput1 + vsh->regs.sq_vtx_semantic[5] = 5; + // aInput2 + vsh->regs.sq_vtx_semantic[6] = 6; + // aInput3 + vsh->regs.sq_vtx_semantic[7] = 7; + // aInput4 + vsh->regs.sq_vtx_semantic[8] = 8; + // aInput5 + vsh->regs.sq_vtx_semantic[9] = 9; + // aInput6 + vsh->regs.sq_vtx_semantic[10] = 10; + // aInput7 + vsh->regs.sq_vtx_semantic[11] = 11; + + vsh->regs.vgt_vertex_reuse_block_cntl = 14; // vtx_reuse_depth + vsh->regs.vgt_hos_reuse_depth = 16; // reuse_depth + + // program + vsh->program = program_buf; + vsh->size = program_size; + + vsh->mode = GX2_SHADER_MODE_UNIFORM_REGISTER; + + // attribs + vsh->attribVarCount = sizeof(attribVars) / sizeof(GX2AttribVar); + vsh->attribVars = attribVars; + + return 0; +} +#undef ADD_INSTR + +int gx2GenerateShaderGroup(struct ShaderGroup *group, struct CCFeatures *cc_features) { + memset(group, 0, sizeof(struct ShaderGroup)); + + // generate the pixel shader + if (generatePixelShader(&group->pixelShader, cc_features) != 0) { + gx2FreeShaderGroup(group); + return -1; + } + + // generate the vertex shader + if (generateVertexShader(&group->vertexShader, cc_features) != 0) { + gx2FreeShaderGroup(group); + return -1; + } + + uint32_t attribOffset = 0; + + // aVtxPos + group->attributes[group->numAttributes++] = + (GX2AttribStream) { 0, 0, attribOffset, GX2_ATTRIB_FORMAT_FLOAT_32_32_32_32, GX2_ATTRIB_INDEX_PER_VERTEX, 0, GX2_COMP_SEL(_x, _y, _z, _w), GX2_ENDIAN_SWAP_DEFAULT }; + attribOffset += 4 * sizeof(float); + + for (int i = 0; i < 2; i++) { + if (cc_features->used_textures[i]) { + // aTexCoordX + group->attributes[group->numAttributes++] = + (GX2AttribStream) { 1 + i, 0, attribOffset, GX2_ATTRIB_FORMAT_FLOAT_32_32_32_32, GX2_ATTRIB_INDEX_PER_VERTEX, 0, GX2_COMP_SEL(_x, _y, _z, _w), GX2_ENDIAN_SWAP_DEFAULT }; + attribOffset += 4 * sizeof(float); + } + } + + // aFog + if (cc_features->opt_fog) { + group->attributes[group->numAttributes++] = + (GX2AttribStream) { 3, 0, attribOffset, GX2_ATTRIB_FORMAT_FLOAT_32_32_32_32, GX2_ATTRIB_INDEX_PER_VERTEX, 0, GX2_COMP_SEL(_x, _y, _z, _w), GX2_ENDIAN_SWAP_DEFAULT }; + attribOffset += 4 * sizeof(float); + } + + // aGrayscaleColor + if (cc_features->opt_grayscale) { + group->attributes[group->numAttributes++] = + (GX2AttribStream) { 4, 0, attribOffset, GX2_ATTRIB_FORMAT_FLOAT_32_32_32_32, GX2_ATTRIB_INDEX_PER_VERTEX, 0, GX2_COMP_SEL(_x, _y, _z, _w), GX2_ENDIAN_SWAP_DEFAULT }; + attribOffset += 4 * sizeof(float); + } + + // aInput + for (int i = 0; i < cc_features->num_inputs; i++) { + group->attributes[group->numAttributes++] = + (GX2AttribStream) { 5 + i, 0, attribOffset, GX2_ATTRIB_FORMAT_FLOAT_32_32_32_32, GX2_ATTRIB_INDEX_PER_VERTEX, 0, GX2_COMP_SEL(_x, _y, _z, _w), GX2_ENDIAN_SWAP_DEFAULT }; + attribOffset += 4 * sizeof(float); + } + + group->stride = attribOffset; + + // init the fetch shader + group->fetchShader.size = GX2CalcFetchShaderSizeEx(group->numAttributes, GX2_FETCH_SHADER_TESSELLATION_NONE, GX2_TESSELLATION_MODE_DISCRETE); + group->fetchShader.program = memalign(GX2_SHADER_PROGRAM_ALIGNMENT, group->fetchShader.size); + if (!group->fetchShader.program) { + gx2FreeShaderGroup(group); + return -1; + } + + GX2InitFetchShaderEx(&group->fetchShader, group->fetchShader.program, group->numAttributes, group->attributes, GX2_FETCH_SHADER_TESSELLATION_NONE, GX2_TESSELLATION_MODE_DISCRETE); + + // invalidate all programs + GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, group->vertexShader.program, group->vertexShader.size); + GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, group->pixelShader.program, group->pixelShader.size); + GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, group->fetchShader.program, group->fetchShader.size); + + return 0; +} + +void gx2FreeShaderGroup(struct ShaderGroup *group) { + free(group->vertexShader.program); + free(group->pixelShader.program); + free(group->fetchShader.program); +} + +#endif diff --git a/libultraship/libultraship/Lib/Fast3D/gx2_shader_gen.h b/libultraship/libultraship/Lib/Fast3D/gx2_shader_gen.h new file mode 100644 index 000000000..e15f9c4c9 --- /dev/null +++ b/libultraship/libultraship/Lib/Fast3D/gx2_shader_gen.h @@ -0,0 +1,27 @@ +#pragma once + +#include "gfx_cc.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ShaderGroup { + GX2VertexShader vertexShader; + GX2PixelShader pixelShader; + GX2FetchShader fetchShader; + + uint32_t stride; + + uint32_t numAttributes; + GX2AttribStream attributes[13]; +}; + +int gx2GenerateShaderGroup(struct ShaderGroup *group, struct CCFeatures *cc_features); + +void gx2FreeShaderGroup(struct ShaderGroup *group); + +#ifdef __cplusplus +} +#endif diff --git a/libultraship/libultraship/Lib/Fast3D/gx2_shader_inl.h b/libultraship/libultraship/Lib/Fast3D/gx2_shader_inl.h new file mode 100644 index 000000000..aab5688e8 --- /dev/null +++ b/libultraship/libultraship/Lib/Fast3D/gx2_shader_inl.h @@ -0,0 +1,624 @@ +/* + * Copyright (C) 2014-2016 - Ali Bouhlel + * Copyright (C) 2022 - GaryOderNichts + * + * Licensed as MIT. + */ + +#ifndef GX2_SHADER_INL_H +#define GX2_SHADER_INL_H + +#define to_QWORD(w0, w1) (((uint64_t)(w0) << 32ull) | (w1)) +#define to_LE(x) (__builtin_bswap32(x)) + +/* CF */ +#define CF_DWORD0(addr) to_LE(addr) + +#define CF_DWORD1(popCount, cfConst, cond, count, callCount, inst) \ + to_LE(popCount | (cfConst << 3) | (cond << 8) | (count << 10) | (callCount << 13) | (inst << 23) | (1 << 31)) + +#define CF_ALU_WORD0(addr, kcacheBank0, kcacheBank1, kcacheMode0) \ + to_LE(addr | (kcacheBank0 << 22) | (kcacheBank1 << 26) | (kcacheMode0 << 30)) +#define CF_ALU_WORD1(kcacheMode1, kcacheAddr0, kcacheAddr1, count, altConst, inst) \ + to_LE(kcacheMode1 | (kcacheAddr0 << 2) | (kcacheAddr1 << 10) | (count << 18) | (altConst << 25) | (inst << 26) | (1 << 31)) + +#define CF_EXP_WORD0(dstReg_and_type, srcReg, srcRel, indexGpr, elemSize)\ + to_LE(dstReg_and_type | (srcReg << 15) | (srcRel << 22) | (indexGpr << 23) | (elemSize << 30)) + +#define CF_EXP_WORD1(srcSelX, srcSelY, srcSelZ, srcSelW, validPixelMode, inst) \ + to_LE(srcSelX | (srcSelY << 3) | (srcSelZ << 6) | (srcSelW << 9) | (validPixelMode << 22) | (inst << 23) | (1 << 31)) + +#define CF_ALLOC_EXPORT_WORD0(arrayBase, type, dstReg, dstRel, indexGpr, elemSize) \ + to_LE(arrayBase | (type << 13) | (dstReg << 15) | (dstRel << 22) | (indexGpr << 23) | (elemSize << 30)) + +#define CF_ALLOC_EXPORT_WORD1_BUF(arraySize, writeMask, inst) \ + to_LE(arraySize | (writeMask << 12) | (inst << 23) | (1 << 31)) + +#define ALU_SRC_KCACHE0_BASE 0x80 +#define ALU_SRC_KCACHE1_BASE 0xA0 +#define CF_KCACHE_BANK_LOCK_1 0x1 +#define CB1 0x1 +#define CB2 0x2 +#define _0_15 CF_KCACHE_BANK_LOCK_1 + +#define KC0(x) (x + ALU_SRC_KCACHE0_BASE) +#define KC1(x) (x + ALU_SRC_KCACHE1_BASE) + +#define NO_BARRIER & (~to_QWORD(0,to_LE(1 << 31))) +#define END_OF_PROGRAM | to_QWORD(0,to_LE(1 << 21)) +#define VALID_PIX | to_QWORD(0,to_LE(1 << 22)) +#define WHOLE_QUAD_MODE | to_QWORD(0,to_LE(1 << 30)) +#define BURSTCNT(x) | to_QWORD(0,to_LE(x << 17)) +#define WRITE(x) (x >> 2) +#define ARRAY_SIZE(x) x +#define ELEM_SIZE(x) x +#define KCACHE0(bank, mode) | to_QWORD(CF_ALU_WORD0(0, bank, 0, mode), 0) +#define KCACHE1(bank, mode) | to_QWORD(CF_ALU_WORD0(0, 0, bank, 0), CF_ALU_WORD1(mode,0, 0, 0, 0, 0)) + +#define DEACTIVATE 1 +#define UPDATE_EXEC_MASK(mode) | to_QWORD(0, to_LE(mode << 2)) +#define UPDATE_PRED | to_QWORD(0, to_LE(1ull << 3)) +#define CLAMP | to_QWORD(0, to_LE(1ull << 31)) +#define ALU_LAST | to_QWORD(to_LE(1ull << 31), 0) + +/* ALU */ + +#define ALU_WORD0(src0Sel, src0Rel, src0Chan, src0Neg, src1Sel, src1Rel, src1Chan, src1Neg, indexMode, predSel) \ + to_LE(src0Sel | ((src0Rel) << 9) | ((src0Chan) << 10) | ((src0Neg) << 12) | ((src1Sel) << 13) | ((src1Rel) << 22) \ + | ((src1Chan) << 23) | ((src1Neg) << 25) | ((indexMode) << 26) | ((predSel) << 29)) + +#define ALU_WORD1_OP2(src0Abs, src1Abs, updateExecuteMask, updatePred, writeMask, omod, inst, encoding, bankSwizzle, dstGpr, dstRel, dstChan, clamp) \ + to_LE(src0Abs | (src1Abs << 1) | (updateExecuteMask << 2) | (updatePred << 3) | (writeMask << 4) | (omod << 5) | (inst << 7) | \ + (encoding << 15) | (bankSwizzle << 18) | ((dstGpr&0x7F) << 21) | (dstRel << 28) | ((dstChan&0x3) << 29) | (clamp << 31)) + +#define ALU_WORD1_OP3(src2Sel, src2Rel, src2Chan, src2Neg, inst, bankSwizzle, dstGpr, dstRel, dstChan, clamp) \ + to_LE(src2Sel | (src2Rel << 9) | (src2Chan << 10) | (src2Neg << 12) | (inst << 13) | \ + (bankSwizzle << 18) | ((dstGpr&0x7F) << 21) | (dstRel << 28) | ((dstChan&0x3) << 29) | (clamp << 31)) + +/* TEX */ +#define TEX_WORD0(inst, bcFracMode, fetchWholeQuad, resourceID, srcReg, srcRel, altConst) \ + to_LE(inst | (bcFracMode << 5) | (fetchWholeQuad << 7) | (resourceID << 8) | (srcReg << 16) | (srcRel << 23) | (altConst << 24)) + +#define TEX_WORD1(dstReg, dstRel, dstSelX, dstSelY, dstSelZ, dstSelW, lodBias, coordTypeX, coordTypeY, coordTypeZ, coordTypeW) \ + to_LE(dstReg | (dstRel << 7) | (dstSelX << 9) | (dstSelY << 12) | (dstSelZ << 15) | (dstSelW << 18) | \ + (lodBias << 21) | (coordTypeX << 28) | (coordTypeY << 29) | (coordTypeZ << 30) | (coordTypeW << 31)) + +#define TEX_WORD2(offsetX, offsetY, offsetZ, samplerID, srcSelX, srcSelY, srcSelZ, srcSelW) \ + to_LE(offsetX | (offsetY << 5) | (offsetZ << 10) | (samplerID << 15) | (srcSelX << 20) | (srcSelY << 23) | (srcSelZ << 26) | (srcSelW << 29)) + +#define VTX_WORD0(inst, type, buffer_id, srcReg, srcSelX, mega) \ + to_LE(inst | (type << 5) | (buffer_id << 8) | (srcReg << 16) | (srcSelX << 24) | (mega << 26)) + +#define VTX_WORD1(dstReg, dstSelX, dstSelY, dstSelZ, dstSelW) \ + to_LE(dstReg | (dstSelX << 9) | (dstSelY << 12) | (dstSelZ << 15) | (dstSelW << 18) | (1 << 21)) + +#define VTX_WORD2(offset, ismega) \ + to_LE(offset| (ismega << 19)) + +#define _x 0 +#define _y 1 +#define _z 2 +#define _w 3 +#define _0 4 +#define _1 5 +#define _m 7 /*mask*/ + +#define _xyzw 0b1111 +#define _xy__ 0b0011 + +#define GX2_COMP_SEL(c0, c1, c2, c3) (((c0) << 24) | ((c1) << 16) | ((c2) << 8) | (c3)) + +#define ALU_LITERAL(v) to_QWORD(to_LE(v), 0) +#define ALU_LITERAL2(v0,v1) to_QWORD(to_LE(v0), to_LE(v1)) +#define ALU_LITERAL3(v0,v1,v2) ALU_LITERAL2(v0,v1),ALU_LITERAL(v2) +#define ALU_LITERAL4(v0,v1,v2,v3) ALU_LITERAL2(v0,v1),ALU_LITERAL2(v2,v3) +#define ALU_LITERAL5(v0,v1,v2,v3,v5) ALU_LITERAL4(v0,v1,v2,v3),ALU_LITERAL(v4) + +/* SRCx_SEL special constants */ +#define ALU_SRC_1_DBL_L 0xF4 +#define ALU_SRC_1_DBL_M 0xF5 +#define ALU_SRC_0_5_DBL_L 0xF6 +#define ALU_SRC_0_5_DBL_M 0xF7 +#define ALU_SRC_0 0xF8 +#define ALU_SRC_1 0xF9 +#define ALU_SRC_1_INT 0xFA +#define ALU_SRC_M_1_INT 0xFB +#define ALU_SRC_0_5 0xFC +#define ALU_SRC_LITERAL 0xFD +#define ALU_SRC_PV 0xFE +#define ALU_SRC_PS 0xFF + +#define _NEG | (1 << 12) +#define _ABS | (1 << 13) + +#define ALU_OMOD_OFF 0x0 +#define ALU_OMOD_M2 0x1 +#define ALU_OMOD_M4 0x2 +#define ALU_OMOD_D2 0x3 + +#define ALU_VEC_012 0x0 +#define ALU_VEC_021 0x1 +#define ALU_VEC_120 0x2 +#define ALU_VEC_102 0x3 +#define ALU_VEC_201 0x4 +#define ALU_VEC_210 0x5 +#define VEC_012 | to_QWORD(0, to_LE(ALU_VEC_012 << 18)) +#define VEC_021 | to_QWORD(0, to_LE(ALU_VEC_021 << 18)) +#define VEC_120 | to_QWORD(0, to_LE(ALU_VEC_120 << 18)) +#define VEC_102 | to_QWORD(0, to_LE(ALU_VEC_102 << 18)) +#define VEC_201 | to_QWORD(0, to_LE(ALU_VEC_201 << 18)) +#define VEC_210 | to_QWORD(0, to_LE(ALU_VEC_210 << 18)) + +#define VALID_PIX | to_QWORD(0,to_LE(1 << 22)) + +#define ALU_SCL_210 0x0 +#define ALU_SCL_122 0x1 +#define ALU_SCL_212 0x2 +#define ALU_SCL_221 0x3 + +#define SCL_210 | to_QWORD(0, to_LE(ALU_SCL_210 << 18)) +#define SCL_122 | to_QWORD(0, to_LE(ALU_SCL_122 << 18)) +#define SCL_212 | to_QWORD(0, to_LE(ALU_SCL_212 << 18)) +#define SCL_221 | to_QWORD(0, to_LE(ALU_SCL_221 << 18)) + +#define FETCH_TYPE(x) x +#define MINI(x) ((x) - 1) +#define MEGA(x) (MINI(x) | 0x80000000) +#define OFFSET(x) x + +#define VERTEX_DATA 0 +#define INSTANCE_DATA 1 +#define NO_INDEX_OFFSET 2 + +/* CF defines */ +#define CF_COND_ACTIVE 0x0 +#define CF_COND_FALSE 0x1 +#define CF_COND_BOOL 0x2 +#define CF_COND_NOT_BOOL 0x3 + +/* TEX defines */ +#define TEX_UNNORMALIZED 0x0 +#define TEX_NORMALIZED 0x1 + +/* instructions */ +/* CF */ +#define CF_INST_TEX 0x01 +#define CF_INST_VTX 0x02 +#define CF_INST_LOOP_END 0x05 +#define CF_INST_LOOP_START_DX10 0x06 +#define CF_INST_JUMP 0x0A +#define CF_INST_ELSE 0x0D +#define CF_INST_POP 0x0E +#define CF_INST_CALL_FS 0x13 +#define CF_INST_EMIT_VERTEX 0x15 +#define CF_INST_MEM_RING 0x26 + +#define CF_INST_ALU 0x08 +#define CF_INST_ALU_PUSH_BEFORE 0x09 +#define CF_INST_ALU_POP_AFTER 0x0A +#define CF_INST_ALU_POP2_AFTER 0x0B +#define CF_INST_ALU_BREAK 0x0E +#define CF_INST_ALU_ELSE_AFTER 0x0F +/* ALU */ +#define OP2_INST_ADD 0x0 +#define OP2_INST_MUL 0x1 +#define OP2_INST_MUL_IEEE 0x2 +#define OP2_INST_MIN 0x04 +#define OP2_INST_MAX 0x03 +#define OP2_INST_MAX_DX10 0x05 +#define OP2_INST_FRACT 0x10 +#define OP2_INST_SETGT 0x09 +#define OP2_INST_SETE_DX10 0x0C +#define OP2_INST_SETGT_DX10 0x0D +#define OP2_INST_SETGE_DX10 0x0E +#define OP2_INST_FLOOR 0x14 +#define OP2_INST_MOVA_INT 0x18 +#define OP2_INST_MOV 0x19 +#define OP2_INST_NOP 0x1A +#define OP2_INST_PRED_SETGT 0x21 +#define OP2_INST_KILLGT 0x2D +#define OP2_INST_AND_INT 0x30 +#define OP2_INST_OR_INT 0x31 +#define OP2_INST_NOT_INT 0x33 +#define OP2_INST_ADD_INT 0x34 +#define OP2_INST_SETE_INT 0x3A +#define OP2_INST_SETGT_INT 0x3B +#define OP2_INST_SETGE_INT 0x3C +#define OP2_INST_SETNE_INT 0x3D +#define OP2_INST_PRED_SETE_INT 0x42 +#define OP2_INST_PRED_SETGT_INT 0x43 +#define OP2_INST_PRED_SETGE_INT 0x44 +#define OP2_INST_PRED_SETNE_INT 0x45 +#define OP2_INST_KILLE_INT 0x46 +#define OP2_INST_KILLGT_INT 0x47 +#define OP2_INST_KILLGE_INT 0x48 +#define OP2_INST_KILLNE_INT 0x49 +#define OP2_INST_DOT4 0x50 +#define OP2_INST_DOT4_IEEE 0x51 +#define OP2_INST_EXP_IEEE 0x61 +#define OP2_INST_LOG_CLAMPED 0x62 +#define OP2_INST_RECIP_IEEE 0x66 +#define OP2_INST_RECIPSQRT_IEEE 0x69 +#define OP2_INST_SQRT_IEEE 0x6A +#define OP2_INST_FLT_TO_INT 0x6B +#define OP2_INST_INT_TO_FLT 0x6C +#define OP2_INST_SIN 0x6E +#define OP2_INST_COS 0x6F +#define OP2_INST_LSHR_INT 0x71 +#define OP2_INST_MULLO_INT 0x73 +#define OP2_INST_LSHL_INT 0x72 +#define OP2_INST_FLT_TO_UINT 0x79 + +#define OP3_INST_MULADD 0x10 +#define OP3_INST_MULADD_D2 0x13 +#define OP3_INST_CNDGT 0x19 +#define OP3_INST_CNDE_INT 0x1C +/* EXP */ +#define CF_INST_EXP 0x27 +#define CF_INST_EXP_DONE 0x28 + +/* TEX */ +#define TEX_INST_LD 0x3 +#define TEX_INST_GET_TEXTURE_INFO 0x4 +#define TEX_INST_GET_GRADIENTS_H 0x07 +#define TEX_INST_GET_GRADIENTS_V 0x08 +#define TEX_INST_SAMPLE 0x10 +/* VTX */ +#define VTX_INST_FETCH 0x0 + +/* EXPORT_TYPE */ +#define EXPORT_TYPE_PIXEL 0x0 +#define EXPORT_TYPE_POS 0x1 +#define EXPORT_TYPE_PARAM 0x2 + +#define EXPORT_ARRAY_BASE_POS(id) (0x3C + id) /* [0, 3] */ +#define EXPORT_ARRAY_BASE_PARAM(id) id /* [0, 31] */ +#define EXPORT_ARRAY_BASE_PIX(id) id + +/* exports */ +#define POS(id) EXPORT_ARRAY_BASE_POS(id) | (EXPORT_TYPE_POS << 13) +#define PARAM(id) EXPORT_ARRAY_BASE_PARAM(id) | (EXPORT_TYPE_PARAM << 13) +#define PIX(id) EXPORT_ARRAY_BASE_PIX(id) | (EXPORT_TYPE_PIXEL << 13) +#define POS0 POS(0) +#define PARAM0 PARAM(0) +#define PARAM1 PARAM(1) +#define PARAM2 PARAM(2) +#define PARAM3 PARAM(3) +#define PARAM4 PARAM(4) +#define PARAM5 PARAM(5) +#define PARAM6 PARAM(6) +#define PARAM7 PARAM(7) +#define PARAM8 PARAM(8) +#define PARAM9 PARAM(9) +#define PARAM10 PARAM(10) +#define PARAM11 PARAM(11) +#define PIX0 PIX(0) + +/* registers */ +#define __ (0x80) /* invalid regitser (write mask off) */ +#define _R(x) x +#define _R0 _R(0x0) +#define _R1 _R(0x1) +#define _R2 _R(0x2) +#define _R3 _R(0x3) +#define _R4 _R(0x4) +#define _R5 _R(0x5) +#define _R6 _R(0x6) +#define _R7 _R(0x7) +#define _R8 _R(0x8) +#define _R9 _R(0x9) +#define _R10 _R(0xA) +#define _R11 _R(0xB) +#define _R12 _R(0xC) +#define _R13 _R(0xD) +#define _R14 _R(0xE) +#define _R15 _R(0xF) +#define _R16 _R(0x10) +#define _R17 _R(0x11) +#define _R18 _R(0x12) +#define _R19 _R(0x13) +#define _R20 _R(0x14) +#define _R21 _R(0x15) +#define _R22 _R(0x16) +#define _R23 _R(0x17) +#define _R24 _R(0x18) +#define _R25 _R(0x19) +#define _R26 _R(0x1A) +#define _R27 _R(0x1B) +#define _R28 _R(0x1C) +#define _R29 _R(0x1D) +#define _R30 _R(0x1E) +#define _R31 _R(0x1F) + +#define _R120 _R(0x78) +#define _R121 _R(0x79) +#define _R122 _R(0x7A) +#define _R123 _R(0x7B) +#define _R124 _R(0x7C) +#define _R125 _R(0x7D) +#define _R126 _R(0x7E) +#define _R127 _R(0x7F) + +/* texture */ +#define _t(x) x +#define _t0 _t(0x0) +#define _t1 _t(0x1) + +/* sampler */ +#define _s(x) x +#define _s0 _s(0x0) +#define _s1 _s(0x1) + +#define _b(x) x + +/* const files */ +#define ALU_SRC_CONST_FILE_BASE 0x100 +#define _C(x) (ALU_SRC_CONST_FILE_BASE + (x)) + +#define CALL_FS to_QWORD(CF_DWORD0(0), CF_DWORD1(0,0,0,0,0,CF_INST_CALL_FS)) + +#define TEX(addr, cnt) to_QWORD(CF_DWORD0(addr), CF_DWORD1(0x0, 0x0, CF_COND_ACTIVE, (cnt - 1), 0x0, CF_INST_TEX)) +#define VTX(addr, cnt) to_QWORD(CF_DWORD0(addr), CF_DWORD1(0x0, 0x0, CF_COND_ACTIVE, (cnt - 1), 0x0, CF_INST_VTX)) +#define LOOP_END(addr) to_QWORD(CF_DWORD0(addr), CF_DWORD1(0x0, 0x0, CF_COND_ACTIVE, 0x0, 0x0, CF_INST_LOOP_END)) +#define LOOP_START_DX10(addr) to_QWORD(CF_DWORD0(addr), CF_DWORD1(0x0, 0x0, CF_COND_ACTIVE, 0x0, 0x0, CF_INST_LOOP_START_DX10)) +#define JUMP(popCount, addr) to_QWORD(CF_DWORD0(addr), CF_DWORD1(popCount, 0x0, CF_COND_ACTIVE, 0x0, 0x0, CF_INST_JUMP)) +#define ELSE(popCount, addr) to_QWORD(CF_DWORD0(addr), CF_DWORD1(popCount, 0x0, CF_COND_ACTIVE, 0x0, 0x0, CF_INST_ELSE)) +#define POP(popCount, addr) to_QWORD(CF_DWORD0(addr), CF_DWORD1(popCount, 0x0, CF_COND_ACTIVE, 0x0, 0x0, CF_INST_POP)) + +#define ALU(addr, cnt) to_QWORD(CF_ALU_WORD0(addr, 0x0, 0x0, 0x0), CF_ALU_WORD1(0x0, 0x0, 0x0, (cnt - 1), 0x0, CF_INST_ALU)) +#define ALU_PUSH_BEFORE(addr, cnt) to_QWORD(CF_ALU_WORD0(addr, 0x0, 0x0, 0x0), CF_ALU_WORD1(0x0, 0x0, 0x0, (cnt - 1), 0x0, CF_INST_ALU_PUSH_BEFORE)) +#define ALU_POP_AFTER(addr, cnt) to_QWORD(CF_ALU_WORD0(addr, 0x0, 0x0, 0x0), CF_ALU_WORD1(0x0, 0x0, 0x0, (cnt - 1), 0x0, CF_INST_ALU_POP_AFTER)) +#define ALU_POP2_AFTER(addr, cnt) to_QWORD(CF_ALU_WORD0(addr, 0x0, 0x0, 0x0), CF_ALU_WORD1(0x0, 0x0, 0x0, (cnt - 1), 0x0, CF_INST_ALU_POP2_AFTER)) +#define ALU_BREAK(addr, cnt) to_QWORD(CF_ALU_WORD0(addr, 0x0, 0x0, 0x0), CF_ALU_WORD1(0x0, 0x0, 0x0, (cnt - 1), 0x0, CF_INST_ALU_BREAK)) +#define ALU_ELSE_AFTER(addr, cnt) to_QWORD(CF_ALU_WORD0(addr, 0x0, 0x0, 0x0), CF_ALU_WORD1(0x0, 0x0, 0x0, (cnt - 1), 0x0, CF_INST_ALU_ELSE_AFTER)) + +#define EXP_DONE(dstReg_and_type, srcReg, srcSelX, srcSelY, srcSelZ, srcSelW) to_QWORD(CF_EXP_WORD0(dstReg_and_type, srcReg, 0x0, 0x0, 0x0), \ + CF_EXP_WORD1(srcSelX, srcSelY, srcSelZ, srcSelW, 0x0, CF_INST_EXP_DONE)) + +#define EXP(dstReg_and_type, srcReg, srcSelX, srcSelY, srcSelZ, srcSelW) to_QWORD(CF_EXP_WORD0(dstReg_and_type, srcReg, 0x0, 0x0, 0x0), \ + CF_EXP_WORD1(srcSelX, srcSelY, srcSelZ, srcSelW, 0x0, CF_INST_EXP)) + +#define MEM_RING(arrayBase, dstReg, writeMask, arraySize, elemSize) \ + to_QWORD(CF_ALLOC_EXPORT_WORD0(arrayBase, 0x00, dstReg, 0x00, 0x00, elemSize), \ + CF_ALLOC_EXPORT_WORD1_BUF(arraySize, writeMask, CF_INST_MEM_RING)) + +#define EMIT_VERTEX to_QWORD(0, CF_DWORD1(0, 0, 0, 0, 0, CF_INST_EMIT_VERTEX)) + +#define ALU_OP2(inst, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, omod) \ + to_QWORD(ALU_WORD0(((src0Sel) & ((1 << 13) - 1)), 0x0, src0Chan, 0x0, ((src1Sel) & ((1 << 13) - 1)), 0x0, src1Chan, 0x0, 0x0, 0x0), \ + ALU_WORD1_OP2(((src0Sel) >> 13), ((src1Sel) >> 13), 0x0, 0x0, (((dstGpr&__) >> 7) ^ 0x1), omod, inst, 0x0, 0x0, dstGpr, 0x0, dstChan, 0x0)) + +#define ALU_OP3(inst, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, src2Sel, src2Chan) \ + to_QWORD(ALU_WORD0(src0Sel, 0x0, src0Chan, 0x0, src1Sel, 0x0, src1Chan, 0x0, 0x0, 0x0), \ + ALU_WORD1_OP3(src2Sel, 0x0, src2Chan, 0x0, inst, 0x0, dstGpr, 0x0, dstChan, 0x0)) + +#define ALU_NOP(dstGpr, dstChan) \ + ALU_OP2(OP2_INST_NOP, dstGpr, dstChan, ALU_SRC_PV, dstChan, ALU_SRC_PV, dstChan, ALU_OMOD_OFF) + +#define ALU_ADD(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_ADD, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_ADD_x2(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_ADD, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_M2) + +#define ALU_ADD_D2(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_ADD, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_D2) + +#define ALU_MUL(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_MUL, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_MUL_x2(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_MUL, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_M2) + +#define ALU_MUL_x4(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_MUL, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_M4) + +#define ALU_MUL_IEEE(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_MUL_IEEE, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_MUL_IEEE_x2(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_MUL_IEEE, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_M2) + +#define ALU_MUL_IEEE_x4(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_MUL_IEEE, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_M4) + +#define ALU_FRACT(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_FRACT, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_FLOOR(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_FLOOR, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_SQRT_IEEE(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_SQRT_IEEE, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_SQRT_IEEE_D2(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_SQRT_IEEE, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_D2) + +#define ALU_MOVA_INT(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_MOVA_INT, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_MOV(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_MOV, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_MOV_D2(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_MOV, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_D2) + +#define ALU_MOV_x2(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_MOV, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_M2) + +#define ALU_MOV_x4(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_MOV, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_M4) + +#define ALU_DOT4_IEEE(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_DOT4_IEEE, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_DOT4(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_DOT4, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_PRED_SETGT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_PRED_SETGT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_KILLGT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_KILLGT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_SETE_DX10(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_SETE_DX10, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_SETGT_DX10(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_SETGT_DX10, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_SETGE_DX10(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_SETGE_DX10, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_SETGT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_SETGT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_PRED_SETE_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_PRED_SETE_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_PRED_SETGT_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_PRED_SETGT_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_PRED_SETGE_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_PRED_SETGE_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_KILLGE_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_KILLGE_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_SETGT_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_SETGT_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_SETGE_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_SETGE_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_ADD_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_ADD_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_PRED_SETNE_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_PRED_SETNE_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_MIN(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_MIN, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_MAX(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_MAX, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_MAX_DX10(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_MAX_DX10, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_LSHR_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_LSHR_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_MULLO_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_MULLO_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_LSHL_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_LSHL_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_AND_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_AND_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_SETE_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_SETE_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_KILLE_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_KILLE_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_KILLGT_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_KILLGT_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_KILLNE_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_KILLNE_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_SETNE_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_SETNE_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_OR_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan) \ + ALU_OP2(OP2_INST_OR_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, ALU_OMOD_OFF) + +#define ALU_INT_TO_FLT(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_INT_TO_FLT, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_FLT_TO_UINT(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_FLT_TO_UINT, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_FLT_TO_INT(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_FLT_TO_INT, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_EXP_IEEE(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_EXP_IEEE, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_LOG_CLAMPED(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_LOG_CLAMPED, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_RECIP_IEEE(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_RECIP_IEEE, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_RECIPSQRT_IEEE(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_RECIPSQRT_IEEE, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_SIN(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_SIN, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_COS(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_COS, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + +#define ALU_COS_D2(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_COS, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_D2) + +#define ALU_NOT_INT(dstGpr, dstChan, src0Sel, src0Chan) \ + ALU_OP2(OP2_INST_NOT_INT, dstGpr, dstChan, src0Sel, src0Chan, ALU_SRC_0, 0x0, ALU_OMOD_OFF) + + +#define ALU_MULADD(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, src2Sel, src2Chan) \ + ALU_OP3(OP3_INST_MULADD, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, src2Sel, src2Chan) + +#define ALU_MULADD_D2(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, src2Sel, src2Chan) \ + ALU_OP3(OP3_INST_MULADD_D2, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, src2Sel, src2Chan) + +#define ALU_CNDGT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, src2Sel, src2Chan) \ + ALU_OP3(OP3_INST_CNDGT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, src2Sel, src2Chan) + +#define ALU_CNDE_INT(dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, src2Sel, src2Chan) \ + ALU_OP3(OP3_INST_CNDE_INT, dstGpr, dstChan, src0Sel, src0Chan, src1Sel, src1Chan, src2Sel, src2Chan) + +#define TEX_LD(dstReg, dstSelX, dstSelY, dstSelZ, dstSelW, srcReg, srcSelX, srcSelY, srcSelZ, srcSelW, resourceID, samplerID)\ + to_QWORD(TEX_WORD0(TEX_INST_LD, 0x0, 0x0, resourceID, srcReg, 0x0, 0x0), \ + TEX_WORD1(dstReg, 0x0, dstSelX, dstSelY, dstSelZ, dstSelW, 0x0, TEX_UNNORMALIZED, TEX_UNNORMALIZED, TEX_UNNORMALIZED, TEX_UNNORMALIZED)), \ + to_QWORD(TEX_WORD2(0x0, 0x0, 0x0, samplerID, srcSelX, srcSelY, srcSelZ, srcSelW), 0x00000000) + +#define TEX_SAMPLE(dstReg, dstSelX, dstSelY, dstSelZ, dstSelW, srcReg, srcSelX, srcSelY, srcSelZ, srcSelW, resourceID, samplerID)\ + to_QWORD(TEX_WORD0(TEX_INST_SAMPLE, 0x0, 0x0, resourceID, srcReg, 0x0, 0x0), \ + TEX_WORD1(dstReg, 0x0, dstSelX, dstSelY, dstSelZ, dstSelW, 0x0, TEX_NORMALIZED, TEX_NORMALIZED, TEX_NORMALIZED, TEX_NORMALIZED)), \ + to_QWORD(TEX_WORD2(0x0, 0x0, 0x0, samplerID, _x, _y, _0, _x), 0x00000000) + +#define TEX_GET_GRADIENTS_H(dstReg, dstSelX, dstSelY, dstSelZ, dstSelW, srcReg, srcSelX, srcSelY, srcSelZ, srcSelW, resourceID, samplerID)\ + to_QWORD(TEX_WORD0(TEX_INST_GET_GRADIENTS_H, 0x0, 0x0, resourceID, srcReg, 0x0, 0x0), \ + TEX_WORD1(dstReg, 0x0, dstSelX, dstSelY, dstSelZ, dstSelW, 0x0, TEX_NORMALIZED, TEX_NORMALIZED, TEX_NORMALIZED, TEX_NORMALIZED)), \ + to_QWORD(TEX_WORD2(0x0, 0x0, 0x0, samplerID, _x, _y, _z, _x), 0x00000000) + +#define TEX_GET_GRADIENTS_V(dstReg, dstSelX, dstSelY, dstSelZ, dstSelW, srcReg, srcSelX, srcSelY, srcSelZ, srcSelW, resourceID, samplerID)\ + to_QWORD(TEX_WORD0(TEX_INST_GET_GRADIENTS_V, 0x0, 0x0, resourceID, srcReg, 0x0, 0x0), \ + TEX_WORD1(dstReg, 0x0, dstSelX, dstSelY, dstSelZ, dstSelW, 0x0, TEX_NORMALIZED, TEX_NORMALIZED, TEX_NORMALIZED, TEX_NORMALIZED)), \ + to_QWORD(TEX_WORD2(0x0, 0x0, 0x0, samplerID, _x, _y, _z, _x), 0x00000000) + +#define TEX_GET_TEXTURE_INFO(dstReg, dstSelX, dstSelY, dstSelZ, dstSelW, srcReg, srcSelX, srcSelY, srcSelZ, srcSelW, resourceID, samplerID)\ + to_QWORD(TEX_WORD0(TEX_INST_GET_TEXTURE_INFO, 0x0, 0x0, resourceID, srcReg, 0x0, 0x0), \ + TEX_WORD1(dstReg, 0x0, dstSelX, dstSelY, dstSelZ, dstSelW, 0x0, TEX_NORMALIZED, TEX_NORMALIZED, TEX_NORMALIZED, TEX_NORMALIZED)), \ + to_QWORD(TEX_WORD2(0x0, 0x0, 0x0, samplerID, srcSelX, srcSelY, srcSelZ, srcSelW), 0x00000000) + + +#define VTX_FETCH(dstReg, dstSelX, dstSelY, dstSelZ, dstSelW, srcReg, srcSelX, buffer_id, type, mega, offset) \ + to_QWORD(VTX_WORD0(VTX_INST_FETCH, type, buffer_id, srcReg, srcSelX, mega), VTX_WORD1(dstReg, dstSelX, dstSelY, dstSelZ, dstSelW)) , \ + to_QWORD(VTX_WORD2(offset, (mega >> 31)), 0x00000000) + +#define _x2(v) v, v +#define _x4(v) _x2(v), _x2(v) +#define _x8(v) _x4(v), _x4(v) +#define _x16(v) _x8(v), _x8(v) + +#define _x9(v) _x8(v), v +#define _x30(v) _x16(v), _x8(v), _x4(v),_x2(v) +#define _x31(v) _x30(v), v + +#endif /* GX2_SHADER_INL_H */ diff --git a/libultraship/libultraship/Lib/ImGui/backends/wiiu/imgui_impl_gx2.cpp b/libultraship/libultraship/Lib/ImGui/backends/wiiu/imgui_impl_gx2.cpp new file mode 100644 index 000000000..8d93e67cb --- /dev/null +++ b/libultraship/libultraship/Lib/ImGui/backends/wiiu/imgui_impl_gx2.cpp @@ -0,0 +1,334 @@ +// dear imgui: Renderer Backend for the Nintendo Wii U using GX2 +#include "imgui.h" +#include "imgui_impl_gx2.h" +#include +#include // intptr_t +#include // memalign + +// GX2 includes +#include +#include +#include +#include +#include +#include + +// Include shader data +#include "shaders/shader.h" + +// GX2 Data +struct ImGui_ImplGX2_Data +{ + uint32_t VertexBufferSize; + void* VertexBuffer; + uint32_t IndexBufferSize; + void* IndexBuffer; + + ImGui_ImplGX2_Texture* FontTexture; + + WHBGfxShaderGroup* ShaderGroup; + + ImGui_ImplGX2_Data() { memset(this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendRendererUserData +static ImGui_ImplGX2_Data* ImGui_ImplGX2_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplGX2_Data*)ImGui::GetIO().BackendRendererUserData : NULL; +} + +// Functions +bool ImGui_ImplGX2_Init() +{ + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + + ImGui_ImplGX2_Data* bd = IM_NEW(ImGui_ImplGX2_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_gx2"; + + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + + return true; +} + +void ImGui_ImplGX2_Shutdown() +{ + ImGui_ImplGX2_Data* bd = ImGui_ImplGX2_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + ImGui_ImplGX2_DestroyDeviceObjects(); + io.BackendRendererName = NULL; + io.BackendRendererUserData = NULL; + IM_DELETE(bd); +} + +void ImGui_ImplGX2_NewFrame() +{ + ImGui_ImplGX2_Data* bd = ImGui_ImplGX2_GetBackendData(); + IM_ASSERT(bd != NULL && "Did you call ImGui_ImplGX2_Init()?"); + + if (!bd->ShaderGroup) + ImGui_ImplGX2_CreateDeviceObjects(); +} + +static void ImGui_ImplGX2_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height) +{ + ImGui_ImplGX2_Data* bd = ImGui_ImplGX2_GetBackendData(); + + // Setup render state: alpha-blending enabled, no face culling, no depth testing + GX2SetColorControl(GX2_LOGIC_OP_COPY, 0xFF, FALSE, TRUE); + GX2SetBlendControl(GX2_RENDER_TARGET_0, + GX2_BLEND_MODE_SRC_ALPHA, + GX2_BLEND_MODE_INV_SRC_ALPHA, + GX2_BLEND_COMBINE_MODE_ADD, + TRUE, + GX2_BLEND_MODE_ONE, + GX2_BLEND_MODE_INV_SRC_ALPHA, + GX2_BLEND_COMBINE_MODE_ADD); + GX2SetCullOnlyControl(GX2_FRONT_FACE_CCW, FALSE, FALSE); + GX2SetDepthOnlyControl(FALSE, FALSE, GX2_COMPARE_FUNC_NEVER); + + // Setup viewport, orthographic projection matrix + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + GX2SetViewport(0, 0, (float)fb_width, (float)fb_height, 0.0f, 1.0f); + + GX2SetFetchShader(&bd->ShaderGroup->fetchShader); + GX2SetVertexShader(bd->ShaderGroup->vertexShader); + GX2SetPixelShader(bd->ShaderGroup->pixelShader); + + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; + const float ortho_projection[4][4] = + { + { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, + { 0.0f, 0.0f, -1.0f, 0.0f }, + { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, + }; + + GX2SetVertexUniformReg(0, sizeof(ortho_projection) / sizeof(float), &ortho_projection[0][0]); +} + +void ImGui_ImplGX2_RenderDrawData(ImDrawData* draw_data) +{ + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); + if (fb_width <= 0 || fb_height <= 0) + return; + + ImGui_ImplGX2_Data* bd = ImGui_ImplGX2_GetBackendData(); + + ImGui_ImplGX2_SetupRenderState(draw_data, fb_width, fb_height); + + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) + + // Create continuous vertex/index buffers + uint32_t vtx_buffer_size = (uint32_t)draw_data->TotalVtxCount * (int)sizeof(ImDrawVert); + uint32_t idx_buffer_size = (uint32_t)draw_data->TotalIdxCount * (int)sizeof(ImDrawIdx); + + // Grow buffers if needed + if (bd->VertexBufferSize < vtx_buffer_size) + { + bd->VertexBufferSize = vtx_buffer_size; + free(bd->VertexBuffer); + bd->VertexBuffer = memalign(GX2_VERTEX_BUFFER_ALIGNMENT, vtx_buffer_size); + } + if (bd->IndexBufferSize < idx_buffer_size) + { + bd->IndexBufferSize = idx_buffer_size; + free(bd->IndexBuffer); + bd->IndexBuffer = memalign(GX2_INDEX_BUFFER_ALIGNMENT, idx_buffer_size); + } + + // Copy data into continuous buffers + uint8_t* vtx_dst = (uint8_t*)bd->VertexBuffer; + uint8_t* idx_dst = (uint8_t*)bd->IndexBuffer; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + + memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); + vtx_dst += cmd_list->VtxBuffer.Size * sizeof(ImDrawVert); + + memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); + idx_dst += cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx); + } + + // Flush memory + GX2Invalidate(GX2_INVALIDATE_MODE_CPU_ATTRIBUTE_BUFFER, bd->VertexBuffer, vtx_buffer_size); + GX2Invalidate(GX2_INVALIDATE_MODE_CPU, bd->IndexBuffer, idx_buffer_size); + + GX2SetAttribBuffer(0, vtx_buffer_size, sizeof(ImDrawVert), bd->VertexBuffer); + + // Render command lists + // (Because we merged all buffers into a single one, we maintain our own offset into them) + int global_vtx_offset = 0; + int global_idx_offset = 0; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback != NULL) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + ImGui_ImplGX2_SetupRenderState(draw_data, fb_width, fb_height); + else + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; + + // Discard invalid draws to avoid GPU crash + if (clip_min.x < 0.0f || clip_min.y < 0.0f || clip_max.x > fb_width || clip_max.y > fb_height || !pcmd->ElemCount) + continue; + + // Apply scissor/clipping rectangle + GX2SetScissor((uint32_t)clip_min.x, (uint32_t)clip_min.y, (uint32_t)(clip_max.x - clip_min.x), (uint32_t)(clip_max.y - clip_min.y)); + + // Bind texture, Draw + ImGui_ImplGX2_Texture* tex = (ImGui_ImplGX2_Texture*) pcmd->GetTexID(); + IM_ASSERT(tex && "TextureID cannot be NULL"); + + GX2SetPixelTexture(tex->Texture, 0); + GX2SetPixelSampler(tex->Sampler, 0); + + GX2DrawIndexedEx(GX2_PRIMITIVE_MODE_TRIANGLES, pcmd->ElemCount, + sizeof(ImDrawIdx) == 2 ? GX2_INDEX_TYPE_U16 : GX2_INDEX_TYPE_U32, + (uint8_t*) bd->IndexBuffer + (pcmd->IdxOffset + global_idx_offset) * sizeof(ImDrawIdx), + global_vtx_offset + pcmd->VtxOffset, 1); + } + } + global_idx_offset += cmd_list->IdxBuffer.Size; + global_vtx_offset += cmd_list->VtxBuffer.Size; + } +} + +bool ImGui_ImplGX2_CreateFontsTexture() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplGX2_Data* bd = ImGui_ImplGX2_GetBackendData(); + + if (bd->FontTexture) + { + return false; + } + + // Build texture atlas + unsigned char* src_pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&src_pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. + + bd->FontTexture = IM_NEW(ImGui_ImplGX2_Texture)(); + + GX2Texture* tex = IM_NEW(GX2Texture)(); + memset(tex, 0, sizeof(GX2Texture)); + bd->FontTexture->Texture = tex; + + tex->surface.dim = GX2_SURFACE_DIM_TEXTURE_2D; + tex->surface.use = GX2_SURFACE_USE_TEXTURE; + tex->surface.width = width; + tex->surface.height = height; + tex->surface.depth = 1; + tex->surface.mipLevels = 1; + tex->surface.format = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8; + tex->surface.aa = GX2_AA_MODE1X; + tex->surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED; + tex->viewNumSlices = 1; + tex->viewNumMips = 1; + // swapped for endianness + tex->compMap = GX2_COMP_MAP(GX2_SQ_SEL_A, GX2_SQ_SEL_B, GX2_SQ_SEL_G, GX2_SQ_SEL_R); + + GX2RCreateSurface(&tex->surface, GX2R_RESOURCE_BIND_TEXTURE | GX2R_RESOURCE_USAGE_CPU_WRITE | GX2R_RESOURCE_USAGE_GPU_READ); + GX2InitTextureRegs(tex); + + unsigned char* dst_pixels = (unsigned char*) GX2RLockSurfaceEx(&tex->surface, 0, GX2R_RESOURCE_BIND_NONE); + + for (int y = 0; y < height; y++) { + memcpy(dst_pixels + (y * tex->surface.pitch * 4), src_pixels + (y * width * 4), width * 4); + } + + GX2RUnlockSurfaceEx(&tex->surface, 0, GX2R_RESOURCE_BIND_NONE); + + bd->FontTexture->Sampler = IM_NEW(GX2Sampler)(); + GX2InitSampler(bd->FontTexture->Sampler, GX2_TEX_CLAMP_MODE_CLAMP, GX2_TEX_XY_FILTER_MODE_LINEAR); + + // Store our identifier + io.Fonts->SetTexID((ImTextureID) bd->FontTexture); + + return true; +} + +void ImGui_ImplGX2_DestroyFontsTexture() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplGX2_Data* bd = ImGui_ImplGX2_GetBackendData(); + if (bd->FontTexture) + { + GX2RDestroySurfaceEx(&bd->FontTexture->Texture->surface, GX2R_RESOURCE_BIND_NONE); + io.Fonts->SetTexID(0); + IM_DELETE(bd->FontTexture->Texture); + IM_DELETE(bd->FontTexture->Sampler); + IM_DELETE(bd->FontTexture); + bd->FontTexture = NULL; + } +} + +bool ImGui_ImplGX2_CreateDeviceObjects() +{ + ImGui_ImplGX2_Data* bd = ImGui_ImplGX2_GetBackendData(); + bd->ShaderGroup = IM_NEW(WHBGfxShaderGroup)(); + + if (!WHBGfxLoadGFDShaderGroup(bd->ShaderGroup, 0, shader_gsh)) + { + IM_DELETE(bd->ShaderGroup); + return false; + } + + WHBGfxInitShaderAttribute(bd->ShaderGroup, "Position", 0, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32); + WHBGfxInitShaderAttribute(bd->ShaderGroup, "UV", 0, 8, GX2_ATTRIB_FORMAT_FLOAT_32_32); + WHBGfxInitShaderAttribute(bd->ShaderGroup, "Color", 0, 16, GX2_ATTRIB_TYPE_8_8_8_8); + + if (!WHBGfxInitFetchShader(bd->ShaderGroup)) + { + IM_DELETE(bd->ShaderGroup); + return false; + } + + ImGui_ImplGX2_CreateFontsTexture(); + + return true; +} + +void ImGui_ImplGX2_DestroyDeviceObjects() +{ + ImGui_ImplGX2_Data* bd = ImGui_ImplGX2_GetBackendData(); + + free(bd->VertexBuffer); + bd->VertexBuffer = NULL; + + free(bd->IndexBuffer); + bd->IndexBuffer = NULL; + + WHBGfxFreeShaderGroup(bd->ShaderGroup); + IM_DELETE(bd->ShaderGroup); + bd->ShaderGroup = NULL; + + ImGui_ImplGX2_DestroyFontsTexture(); +} diff --git a/libultraship/libultraship/Lib/ImGui/backends/wiiu/imgui_impl_gx2.h b/libultraship/libultraship/Lib/ImGui/backends/wiiu/imgui_impl_gx2.h new file mode 100644 index 000000000..60669fe7a --- /dev/null +++ b/libultraship/libultraship/Lib/ImGui/backends/wiiu/imgui_impl_gx2.h @@ -0,0 +1,25 @@ +// dear imgui: Renderer Backend for the Nintendo Wii U using GX2 +#pragma once +#include "imgui.h" // IMGUI_IMPL_API + +// GX2 Texture / contains a texture and sampler +// Can be used as a ImTextureID with the GX2 backend +struct ImGui_ImplGX2_Texture +{ + struct GX2Texture* Texture; + struct GX2Sampler* Sampler; + + ImGui_ImplGX2_Texture() { memset(this, 0, sizeof(*this)); } +}; + +// Backend API +IMGUI_IMPL_API bool ImGui_ImplGX2_Init(); +IMGUI_IMPL_API void ImGui_ImplGX2_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplGX2_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplGX2_RenderDrawData(ImDrawData* draw_data); + +// (Optional) Called by Init/NewFrame/Shutdown +IMGUI_IMPL_API bool ImGui_ImplGX2_CreateFontsTexture(); +IMGUI_IMPL_API void ImGui_ImplGX2_DestroyFontsTexture(); +IMGUI_IMPL_API bool ImGui_ImplGX2_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplGX2_DestroyDeviceObjects(); diff --git a/libultraship/libultraship/Lib/ImGui/backends/wiiu/imgui_impl_wiiu.cpp b/libultraship/libultraship/Lib/ImGui/backends/wiiu/imgui_impl_wiiu.cpp new file mode 100644 index 000000000..ca718b53b --- /dev/null +++ b/libultraship/libultraship/Lib/ImGui/backends/wiiu/imgui_impl_wiiu.cpp @@ -0,0 +1,294 @@ +// dear imgui: Platform Backend for the Wii U +#include "imgui.h" +#include "imgui_impl_wiiu.h" +#include // malloc/free + +// Software keyboard +#include + +// Wii U Data +struct ImGui_ImplWiiU_Data +{ + nn::swkbd::CreateArg CreateArg; + nn::swkbd::AppearArg AppearArg; + nn::swkbd::ControllerType LastController; + + bool WantedTextInput; + bool WasTouched; + + ImGui_ImplWiiU_Data() { memset((void*)this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendPlatformUserData +static ImGui_ImplWiiU_Data* ImGui_ImplWiiU_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplWiiU_Data*)ImGui::GetIO().BackendPlatformUserData : NULL; +} + +bool ImGui_ImplWiiU_Init() +{ + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); + + // Setup backend data + ImGui_ImplWiiU_Data* bd = IM_NEW(ImGui_ImplWiiU_Data)(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_wiiu"; + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + + // Initialize and create software keyboard + nn::swkbd::CreateArg createArg; + + createArg.workMemory = malloc(nn::swkbd::GetWorkMemorySize(0)); + createArg.fsClient = (FSClient*) malloc(sizeof(FSClient)); + if (!createArg.workMemory || !createArg.fsClient) + { + free(createArg.workMemory); + free(createArg.fsClient); + return false; + } + + FSAddClient(createArg.fsClient, FS_ERROR_FLAG_NONE); + + if (!nn::swkbd::Create(createArg)) + return false; + + nn::swkbd::AppearArg appearArg; + bd->CreateArg = createArg; + bd->AppearArg = appearArg; + + return true; +} + +void ImGui_ImplWiiU_Shutdown() +{ + ImGui_ImplWiiU_Data* bd = ImGui_ImplWiiU_GetBackendData(); + IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + // Destroy software keyboard + nn::swkbd::Destroy(); + free(bd->CreateArg.workMemory); + bd->CreateArg.workMemory = NULL; + + if (bd->CreateArg.fsClient) + { + FSDelClient(bd->CreateArg.fsClient, FS_ERROR_FLAG_NONE); + free(bd->CreateArg.fsClient); + bd->CreateArg.fsClient = NULL; + } + + io.BackendPlatformName = NULL; + io.BackendPlatformUserData = NULL; + IM_DELETE(bd); +} + +static void ImGui_ImplWiiU_UpdateKeyboardInput(ImGui_ImplWiiU_ControllerInput* input) +{ + ImGuiIO& io = ImGui::GetIO(); + + VPADGetTPCalibratedPoint(VPAD_CHAN_0, &input->vpad->tpNormal, &input->vpad->tpNormal); + + nn::swkbd::ControllerInfo controllerInfo; + controllerInfo.vpad = input->vpad; + for (int i = 0; i < 4; i++) + controllerInfo.kpad[i] = input->kpad[i]; + + nn::swkbd::Calc(controllerInfo); + + if (nn::swkbd::IsNeedCalcSubThreadFont()) + nn::swkbd::CalcSubThreadFont(); + + if (nn::swkbd::IsNeedCalcSubThreadPredict()) + nn::swkbd::CalcSubThreadPredict(); + + if (nn::swkbd::IsDecideOkButton(NULL)) + { + // Add entered text + const char16_t* string = nn::swkbd::GetInputFormString(); + for (int i = 0; *string; string++) + io.AddInputCharacterUTF16(string[i]); + + // close keyboard + nn::swkbd::DisappearInputForm(); + } + + if (nn::swkbd::IsDecideCancelButton(NULL)) + nn::swkbd::DisappearInputForm(); +} + +static void ImGui_ImplWiiU_UpdateTouchInput(ImGui_ImplWiiU_ControllerInput* input) +{ + if (!input->vpad) + return; + + ImGui_ImplWiiU_Data* bd = ImGui_ImplWiiU_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); + + VPADTouchData touch; + VPADGetTPCalibratedPoint(VPAD_CHAN_0, &touch, &input->vpad->tpNormal); + + if (touch.touched) + { + float scale_x = (io.DisplaySize.x / io.DisplayFramebufferScale.x) / 1280.0f; + float scale_y = (io.DisplaySize.y / io.DisplayFramebufferScale.y) / 720.0f; + io.AddMousePosEvent(touch.x * scale_x, touch.y * scale_y); + } + + if (touch.touched != bd->WasTouched) + { + io.AddMouseButtonEvent(ImGuiMouseButton_Left, touch.touched); + bd->WasTouched = touch.touched; + bd->LastController = nn::swkbd::ControllerType::DrcGamepad; + } +} + +#define IM_CLAMP(V, MN, MX) ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V)) + +static void ImGui_ImplWiiU_UpdateControllerInput(ImGui_ImplWiiU_ControllerInput* input) +{ + ImGui_ImplWiiU_Data* bd = ImGui_ImplWiiU_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); + + // SoH removal to make opening the menu easier + // if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) + // return; + + uint32_t vpad_buttons = input->vpad ? input->vpad->hold : 0; + uint32_t wpad_buttons = 0; + uint32_t classic_buttons = 0; + uint32_t pro_buttons = 0; + + float stick_l_x = input->vpad ? input->vpad->leftStick.x : 0.0f; + float stick_l_y = input->vpad ? input->vpad->leftStick.y : 0.0f; + float stick_r_x = input->vpad ? input->vpad->rightStick.x : 0.0f; + float stick_r_y = input->vpad ? input->vpad->rightStick.y : 0.0f; + + for (int i = 0; i < 4; i++) + { + KPADStatus* kpad = input->kpad[i]; + if (!kpad) + continue; + + switch (kpad->extensionType) + { + case WPAD_EXT_CORE: + case WPAD_EXT_NUNCHUK: + case WPAD_EXT_MPLUS: + case WPAD_EXT_MPLUS_NUNCHUK: + wpad_buttons |= kpad->hold; + break; + case WPAD_EXT_CLASSIC: + case WPAD_EXT_MPLUS_CLASSIC: + classic_buttons |= kpad->classic.hold; + if (classic_buttons & WPAD_CLASSIC_BUTTON_Y) + bd->LastController = (nn::swkbd::ControllerType) i; + + stick_l_x += kpad->classic.leftStick.x; + stick_l_y += kpad->classic.leftStick.y; + stick_r_x += kpad->classic.rightStick.x; + stick_r_y += kpad->classic.rightStick.y; + break; + case WPAD_EXT_PRO_CONTROLLER: + pro_buttons |= kpad->pro.hold; + if (pro_buttons & WPAD_PRO_BUTTON_Y) + bd->LastController = (nn::swkbd::ControllerType) i; + + stick_l_x += kpad->pro.leftStick.x; + stick_l_y += kpad->pro.leftStick.y; + stick_r_x += kpad->pro.rightStick.x; + stick_r_y += kpad->pro.rightStick.y; + break; + } + } + + if (vpad_buttons & VPAD_BUTTON_Y) + bd->LastController = nn::swkbd::ControllerType::DrcGamepad; + + io.AddKeyEvent(ImGuiKey_GamepadStart, (vpad_buttons & VPAD_BUTTON_PLUS) || (wpad_buttons & WPAD_BUTTON_PLUS) || (classic_buttons & WPAD_CLASSIC_BUTTON_PLUS) || (pro_buttons & WPAD_PRO_BUTTON_PLUS)); + io.AddKeyEvent(ImGuiKey_GamepadBack, (vpad_buttons & VPAD_BUTTON_MINUS) || (wpad_buttons & WPAD_BUTTON_MINUS) || (classic_buttons & WPAD_CLASSIC_BUTTON_MINUS) || (pro_buttons & WPAD_PRO_BUTTON_MINUS)); + io.AddKeyEvent(ImGuiKey_GamepadFaceLeft, (vpad_buttons & VPAD_BUTTON_X) || (classic_buttons & WPAD_CLASSIC_BUTTON_X) || (pro_buttons & WPAD_PRO_BUTTON_X)); + io.AddKeyEvent(ImGuiKey_GamepadFaceRight, (vpad_buttons & VPAD_BUTTON_B) || (wpad_buttons & WPAD_BUTTON_B) || (classic_buttons & WPAD_CLASSIC_BUTTON_B) || (pro_buttons & WPAD_PRO_BUTTON_B)); + io.AddKeyEvent(ImGuiKey_GamepadFaceUp, (vpad_buttons & VPAD_BUTTON_Y) || (classic_buttons & WPAD_CLASSIC_BUTTON_Y) || (pro_buttons & WPAD_PRO_BUTTON_Y)); + io.AddKeyEvent(ImGuiKey_GamepadFaceDown, (vpad_buttons & VPAD_BUTTON_A) || (wpad_buttons & WPAD_BUTTON_A) || (classic_buttons & WPAD_CLASSIC_BUTTON_A) || (pro_buttons & WPAD_PRO_BUTTON_A)); + io.AddKeyEvent(ImGuiKey_GamepadDpadLeft, (vpad_buttons & VPAD_BUTTON_LEFT) || (wpad_buttons & WPAD_BUTTON_LEFT) || (classic_buttons & WPAD_CLASSIC_BUTTON_LEFT) || (pro_buttons & WPAD_PRO_BUTTON_LEFT)); + io.AddKeyEvent(ImGuiKey_GamepadDpadRight, (vpad_buttons & VPAD_BUTTON_RIGHT) || (wpad_buttons & WPAD_BUTTON_RIGHT) || (classic_buttons & WPAD_CLASSIC_BUTTON_RIGHT) || (pro_buttons & WPAD_PRO_BUTTON_RIGHT)); + io.AddKeyEvent(ImGuiKey_GamepadDpadUp, (vpad_buttons & VPAD_BUTTON_UP) || (wpad_buttons & WPAD_BUTTON_UP) || (classic_buttons & WPAD_CLASSIC_BUTTON_UP) || (pro_buttons & WPAD_PRO_BUTTON_UP)); + io.AddKeyEvent(ImGuiKey_GamepadDpadDown, (vpad_buttons & VPAD_BUTTON_DOWN) || (wpad_buttons & WPAD_BUTTON_DOWN) || (classic_buttons & WPAD_CLASSIC_BUTTON_DOWN) || (pro_buttons & WPAD_PRO_BUTTON_DOWN)); + io.AddKeyEvent(ImGuiKey_GamepadL1, (vpad_buttons & VPAD_BUTTON_L) || (classic_buttons & WPAD_CLASSIC_BUTTON_L) || (pro_buttons & WPAD_PRO_TRIGGER_L)); + io.AddKeyEvent(ImGuiKey_GamepadR1, (vpad_buttons & VPAD_BUTTON_R) || (classic_buttons & WPAD_CLASSIC_BUTTON_R) || (pro_buttons & WPAD_PRO_TRIGGER_R)); + io.AddKeyEvent(ImGuiKey_GamepadL2, (vpad_buttons & VPAD_BUTTON_ZL) || (classic_buttons & WPAD_CLASSIC_BUTTON_ZL) || (pro_buttons & WPAD_PRO_TRIGGER_ZL)); + io.AddKeyEvent(ImGuiKey_GamepadR2, (vpad_buttons & VPAD_BUTTON_ZR) || (classic_buttons & WPAD_CLASSIC_BUTTON_ZR) || (pro_buttons & WPAD_PRO_TRIGGER_ZR)); + io.AddKeyEvent(ImGuiKey_GamepadL3, (vpad_buttons & VPAD_BUTTON_STICK_L) || (pro_buttons & WPAD_PRO_BUTTON_STICK_L)); + io.AddKeyEvent(ImGuiKey_GamepadR3, (vpad_buttons & VPAD_BUTTON_STICK_R) || (pro_buttons & WPAD_PRO_BUTTON_STICK_R)); + + stick_l_x = IM_CLAMP(stick_l_x, -1.0f, 1.0f); + io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickLeft, stick_l_x < -0.1f, (stick_l_x < -0.1f) ? (stick_l_x * -1.0f) : 0.0f); + io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickRight, stick_l_x > 0.1f, (stick_l_x > 0.1f) ? stick_l_x : 0.0f); + + stick_l_y = IM_CLAMP(stick_l_y, -1.0f, 1.0f); + io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickUp, stick_l_y > 0.1f, (stick_l_y > 0.1f) ? stick_l_y : 0.0f); + io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickDown, stick_l_y < -0.1f, (stick_l_y < -0.1f) ? (stick_l_y * -1.0f) : 0.0f); + + stick_r_x = IM_CLAMP(stick_r_x, -1.0f, 1.0f); + io.AddKeyAnalogEvent(ImGuiKey_GamepadRStickLeft, stick_r_x < -0.1f, (stick_r_x < -0.1f) ? (stick_r_x * -1.0f) : 0.0f); + io.AddKeyAnalogEvent(ImGuiKey_GamepadRStickRight, stick_r_x > 0.1f, (stick_r_x > 0.1f) ? stick_r_x : 0.0f); + + stick_r_y = IM_CLAMP(stick_r_y, -1.0f, 1.0f); + io.AddKeyAnalogEvent(ImGuiKey_GamepadRStickUp, stick_r_y > 0.1f, (stick_r_y > 0.1f) ? stick_r_y : 0.0f); + io.AddKeyAnalogEvent(ImGuiKey_GamepadRStickDown, stick_r_y < -0.1f, (stick_r_y < -0.1f) ? (stick_r_y * -1.0f) : 0.0f); +} + +bool ImGui_ImplWiiU_ProcessInput(ImGui_ImplWiiU_ControllerInput* input) +{ + ImGui_ImplWiiU_Data* bd = ImGui_ImplWiiU_GetBackendData(); + IM_ASSERT(bd != NULL && "Did you call ImGui_ImplWiiU_Init()?"); + ImGuiIO& io = ImGui::GetIO(); + + // Show keyboard if wanted + if (io.WantTextInput && !bd->WantedTextInput) + { + // Open the keyboard for the controller which requested the text input + bd->AppearArg.keyboardArg.configArg.controllerType = bd->LastController; + + if (nn::swkbd::GetStateInputForm() == nn::swkbd::State::Hidden) + nn::swkbd::AppearInputForm(bd->AppearArg); + } + bd->WantedTextInput = io.WantTextInput; + + // Update keyboard input + if (nn::swkbd::GetStateInputForm() != nn::swkbd::State::Hidden) + { + ImGui_ImplWiiU_UpdateKeyboardInput(input); + return true; + } + + // Update touch screen + ImGui_ImplWiiU_UpdateTouchInput(input); + + // Update gamepads + ImGui_ImplWiiU_UpdateControllerInput(input); + + return false; +} + +void ImGui_ImplWiiU_DrawKeyboardOverlay(ImGui_ImplWiiU_KeyboardOverlayType type) +{ + ImGui_ImplWiiU_Data* bd = ImGui_ImplWiiU_GetBackendData(); + IM_ASSERT(bd != NULL && "Did you call ImGui_ImplWiiU_Init()?"); + + if (nn::swkbd::GetStateInputForm() != nn::swkbd::State::Hidden) + { + if (type == ImGui_KeyboardOverlay_Auto) + { + if (bd->LastController == nn::swkbd::ControllerType::DrcGamepad) + nn::swkbd::DrawDRC(); + else + nn::swkbd::DrawTV(); + } + else if (type == ImGui_KeyboardOverlay_DRC) + nn::swkbd::DrawDRC(); + else if (type == ImGui_KeyboardOverlay_TV) + nn::swkbd::DrawTV(); + } +} diff --git a/libultraship/libultraship/Lib/ImGui/backends/wiiu/imgui_impl_wiiu.h b/libultraship/libultraship/Lib/ImGui/backends/wiiu/imgui_impl_wiiu.h new file mode 100644 index 000000000..b48fb9a59 --- /dev/null +++ b/libultraship/libultraship/Lib/ImGui/backends/wiiu/imgui_impl_wiiu.h @@ -0,0 +1,29 @@ +// dear imgui: Platform Backend for the Wii U +#pragma once +#include "imgui.h" // IMGUI_IMPL_API + +// GamePad Input +#include +// Controller Input +#include + +struct ImGui_ImplWiiU_ControllerInput +{ + VPADStatus* vpad = nullptr; + KPADStatus* kpad[4] = { nullptr }; +}; + +enum ImGui_ImplWiiU_KeyboardOverlayType +{ + //! Draw for the DRC + ImGui_KeyboardOverlay_DRC, + //! Draw for the TV + ImGui_KeyboardOverlay_TV, + //! Draw for the controller which requested the keyboard + ImGui_KeyboardOverlay_Auto +}; + +IMGUI_IMPL_API bool ImGui_ImplWiiU_Init(); +IMGUI_IMPL_API void ImGui_ImplWiiU_Shutdown(); +IMGUI_IMPL_API bool ImGui_ImplWiiU_ProcessInput(ImGui_ImplWiiU_ControllerInput* input); +IMGUI_IMPL_API void ImGui_ImplWiiU_DrawKeyboardOverlay(ImGui_ImplWiiU_KeyboardOverlayType type = ImGui_KeyboardOverlay_Auto); diff --git a/libultraship/libultraship/Lib/ImGui/backends/wiiu/shaders/build-shaders.sh b/libultraship/libultraship/Lib/ImGui/backends/wiiu/shaders/build-shaders.sh new file mode 100755 index 000000000..13262ad0c --- /dev/null +++ b/libultraship/libultraship/Lib/ImGui/backends/wiiu/shaders/build-shaders.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# to build shaders you need to place a copy of latte-assembler into the current directory +# latte-assembler is part of decaf-emu + +# shader +./latte-assembler assemble --vsh=shader.vsh --psh=shader.psh shader.gsh +xxd -i shader.gsh > shader.h diff --git a/libultraship/libultraship/Lib/ImGui/backends/wiiu/shaders/shader.h b/libultraship/libultraship/Lib/ImGui/backends/wiiu/shaders/shader.h new file mode 100644 index 000000000..74572b585 --- /dev/null +++ b/libultraship/libultraship/Lib/ImGui/backends/wiiu/shaders/shader.h @@ -0,0 +1,148 @@ +unsigned char shader_gsh[] = { + 0x47, 0x66, 0x78, 0x32, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4c, 0x4b, 0x7b, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0xd8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x01, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd0, 0x60, 0x01, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xd0, 0x60, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xca, 0x70, 0x01, 0x78, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xca, 0x70, 0x01, 0x80, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xca, 0x70, 0x01, 0x8c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0xca, 0x70, 0x01, 0x90, 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x72, 0x6f, 0x6a, + 0x4d, 0x74, 0x78, 0x00, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x55, 0x56, 0x00, 0x00, 0x43, 0x6f, 0x6c, 0x6f, + 0x72, 0x00, 0x00, 0x00, 0xd0, 0x60, 0x00, 0xe8, 0xd0, 0x60, 0x01, 0x08, + 0xca, 0x70, 0x01, 0x34, 0xca, 0x70, 0x01, 0x48, 0xca, 0x70, 0x01, 0x58, + 0xca, 0x70, 0x01, 0x68, 0x7d, 0x42, 0x4c, 0x4b, 0x00, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x98, 0xd0, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0xd0, 0x60, 0x01, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0xd0, 0x60, 0x01, 0x98, 0x42, 0x4c, 0x4b, 0x7b, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x01, 0x70, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x09, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xa0, 0x3c, 0xa0, 0x00, 0x00, + 0x88, 0x06, 0x00, 0x94, 0x00, 0x40, 0x01, 0x00, 0x08, 0x09, 0x80, 0x13, + 0x01, 0xc0, 0x01, 0x00, 0x88, 0x06, 0x20, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x60, 0x20, 0x00, + 0x80, 0x00, 0x00, 0x00, 0xfd, 0x64, 0xa0, 0x00, 0x80, 0x00, 0x00, 0x20, + 0xfd, 0x68, 0x20, 0x01, 0x80, 0x00, 0x00, 0x40, 0xfd, 0x6c, 0xa0, 0x81, + 0x80, 0x00, 0x00, 0x60, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x01, 0x24, 0x20, 0x00, + 0xfe, 0x00, 0xe2, 0x0f, 0x01, 0x24, 0xa0, 0x00, 0xfe, 0x04, 0xe2, 0x2f, + 0x01, 0x24, 0x20, 0x01, 0xfe, 0x08, 0xe2, 0x4f, 0x01, 0x24, 0xa0, 0x81, + 0xfe, 0x0c, 0xe2, 0x6f, 0x01, 0x00, 0x20, 0x00, 0xfe, 0x00, 0x22, 0x00, + 0x01, 0x00, 0xa0, 0x00, 0xfe, 0x04, 0x22, 0x20, 0x01, 0x00, 0x20, 0x01, + 0xfe, 0x08, 0x22, 0x40, 0x01, 0x00, 0xa0, 0x81, 0xfe, 0x0c, 0x22, 0x60, + 0x42, 0x4c, 0x4b, 0x7b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x2c, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0xd0, 0x60, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xca, 0x70, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0xd0, 0x60, 0x00, 0xd4, + 0xca, 0x70, 0x00, 0xe8, 0x7d, 0x42, 0x4c, 0x4b, 0x00, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xd0, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0xd0, 0x60, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0xd0, 0x60, 0x00, 0xfc, 0x42, 0x4c, 0x4b, 0x7b, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0x90, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xa0, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x06, 0x20, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0x01, + 0x90, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x90, 0x00, 0x00, 0x20, + 0x00, 0x28, 0x80, 0x00, 0x90, 0x00, 0x00, 0x40, 0x00, 0x2c, 0x00, 0x80, + 0x90, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0d, 0xf0, + 0x00, 0x00, 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4c, 0x4b, 0x7b, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00 +}; +unsigned int shader_gsh_len = 1732; diff --git a/libultraship/libultraship/Lib/ImGui/backends/wiiu/shaders/shader.psh b/libultraship/libultraship/Lib/ImGui/backends/wiiu/shaders/shader.psh new file mode 100644 index 000000000..20363ac09 --- /dev/null +++ b/libultraship/libultraship/Lib/ImGui/backends/wiiu/shaders/shader.psh @@ -0,0 +1,25 @@ +; $MODE = "UniformRegister" + +; $NUM_SPI_PS_INPUT_CNTL = 2 +; Frag_UV R0 +; $SPI_PS_INPUT_CNTL[0].SEMANTIC = 0 +; $SPI_PS_INPUT_CNTL[0].DEFAULT_VAL = 1 +; Frag_Color R1 +; $SPI_PS_INPUT_CNTL[1].SEMANTIC = 1 +; $SPI_PS_INPUT_CNTL[1].DEFAULT_VAL = 1 + +; $SAMPLER_VARS[0].name = "Texture" +; $SAMPLER_VARS[0].type = "SAMPLER2D" +; $SAMPLER_VARS[0].location = 0 + +; Note: R1 is swapped for endianness + +00 TEX: ADDR(48) CNT(1) VALID_PIX + 0 SAMPLE R0, R0.xy0x, t0, s0 +01 ALU: ADDR(32) CNT(4) + 1 x: MUL R0.x, R0.x, R1.w + y: MUL R0.y, R0.y, R1.z + z: MUL R0.z, R0.z, R1.y + w: MUL R0.w, R0.w, R1.x +02 EXP_DONE: PIX0, R0 +END_OF_PROGRAM diff --git a/libultraship/libultraship/Lib/ImGui/backends/wiiu/shaders/shader.vsh b/libultraship/libultraship/Lib/ImGui/backends/wiiu/shaders/shader.vsh new file mode 100644 index 000000000..3b321d238 --- /dev/null +++ b/libultraship/libultraship/Lib/ImGui/backends/wiiu/shaders/shader.vsh @@ -0,0 +1,47 @@ +; $MODE = "UniformRegister" + +; $SPI_VS_OUT_CONFIG.VS_EXPORT_COUNT = 1 +; $NUM_SPI_VS_OUT_ID = 1 +; uv +; $SPI_VS_OUT_ID[0].SEMANTIC_0 = 0 +; color +; $SPI_VS_OUT_ID[0].SEMANTIC_1 = 1 + +; C0 +; $UNIFORM_VARS[0].name = "ProjMtx" +; $UNIFORM_VARS[0].type = "mat4" +; $UNIFORM_VARS[0].count = 1 +; $UNIFORM_VARS[0].block = -1 +; $UNIFORM_VARS[0].offset = 0 + +; R1 +; $ATTRIB_VARS[0].name = "Position" +; $ATTRIB_VARS[0].type = "vec2" +; $ATTRIB_VARS[0].location = 0 +; R2 +; $ATTRIB_VARS[1].name = "UV" +; $ATTRIB_VARS[1].type = "vec2" +; $ATTRIB_VARS[1].location = 1 +; R3 +; $ATTRIB_VARS[2].name = "Color" +; $ATTRIB_VARS[2].type = "vec4" +; $ATTRIB_VARS[2].location = 2 + +00 CALL_FS NO_BARRIER +01 ALU: ADDR(32) CNT(14) + 0 x: MUL ____, 1.0f, C3.x + y: MUL ____, 1.0f, C3.y + z: MUL ____, 1.0f, C3.z + w: MUL ____, 1.0f, C3.w + 1 x: MULADD R127.x, R1.y, C1.x, PV0.x + y: MULADD R127.y, R1.y, C1.y, PV0.y + z: MULADD R127.z, R1.y, C1.z, PV0.z + w: MULADD R127.w, R1.y, C1.w, PV0.w + 2 x: MULADD R1.x, R1.x, C0.x, PV0.x + y: MULADD R1.y, R1.x, C0.y, PV0.y + z: MULADD R1.z, R1.x, C0.z, PV0.z + w: MULADD R1.w, R1.x, C0.w, PV0.w +02 EXP_DONE: POS0, R1 +03 EXP: PARAM0, R2.xy00 NO_BARRIER +04 EXP_DONE: PARAM1, R3 NO_BARRIER +END_OF_PROGRAM diff --git a/libultraship/libultraship/Lib/ImGui/imgui_draw.cpp b/libultraship/libultraship/Lib/ImGui/imgui_draw.cpp index 82e1925a5..f768f6c7c 100644 --- a/libultraship/libultraship/Lib/ImGui/imgui_draw.cpp +++ b/libultraship/libultraship/Lib/ImGui/imgui_draw.cpp @@ -761,7 +761,12 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 // Temporary buffer // The first items are normals at each line point, then after that there are either 2 or 4 temp points for each line point +#ifdef __WIIU__ + // avoid using alloca on the Wii U + ImVec2 temp_normals[points_count * ((use_texture || !thick_line) ? 3 : 5) * sizeof(ImVec2)]; //-V630 +#else ImVec2* temp_normals = (ImVec2*)alloca(points_count * ((use_texture || !thick_line) ? 3 : 5) * sizeof(ImVec2)); //-V630 +#endif ImVec2* temp_points = temp_normals + points_count; // Calculate normals (tangents) for each line segment @@ -1009,7 +1014,12 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun } // Compute normals +#ifdef __WIIU__ + // Avoid using alloca on the Wii U + ImVec2 temp_normals[points_count * sizeof(ImVec2)]; //-V630 +#else ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); //-V630 +#endif for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++) { const ImVec2& p0 = points[i0]; diff --git a/libultraship/libultraship/Lib/Mercury/Mercury.cpp b/libultraship/libultraship/Lib/Mercury/Mercury.cpp index 009b9c0f9..17e199484 100644 --- a/libultraship/libultraship/Lib/Mercury/Mercury.cpp +++ b/libultraship/libultraship/Lib/Mercury/Mercury.cpp @@ -111,6 +111,12 @@ void Mercury::reload() { return; } std::ifstream ifs(this->path_); + +#ifdef __WIIU__ + alignas(0x40) char buffer[8192]; + ifs.rdbuf()->pubsetbuf(buffer, sizeof(buffer)); +#endif + try { this->rjson = json::parse(ifs); this->vjson = this->rjson.flatten(); @@ -122,5 +128,11 @@ void Mercury::reload() { void Mercury::save() const { std::ofstream file(this->path_); + +#ifdef __WIIU__ + alignas(0x40) char buffer[8192]; + file.rdbuf()->pubsetbuf(buffer, sizeof(buffer)); +#endif + file << this->vjson.unflatten().dump(4); } diff --git a/libultraship/libultraship/Lib/spdlog/include/spdlog/details/os-inl.h b/libultraship/libultraship/Lib/spdlog/include/spdlog/details/os-inl.h index 4602782c8..a514413ff 100644 --- a/libultraship/libultraship/Lib/spdlog/include/spdlog/details/os-inl.h +++ b/libultraship/libultraship/Lib/spdlog/include/spdlog/details/os-inl.h @@ -286,7 +286,7 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) return offset; #else -# if defined(sun) || defined(__sun) || defined(_AIX) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE)) +# if defined(sun) || defined(__sun) || defined(_AIX) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE)) || defined(__WIIU__) // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris struct helper { diff --git a/libultraship/libultraship/WiiUController.cpp b/libultraship/libultraship/WiiUController.cpp new file mode 100644 index 000000000..fde41f2cc --- /dev/null +++ b/libultraship/libultraship/WiiUController.cpp @@ -0,0 +1,509 @@ +#ifdef __WIIU__ +#include "WiiUController.h" +#include "GlobalCtx2.h" +#include "Window.h" +#include "ImGuiImpl.h" + +#include "WiiUImpl.h" + +namespace Ship { + WiiUController::WiiUController(WPADChan chan) : Controller(), chan(chan) { + connected = false; + extensionType = (WPADExtensionType) -1; + + controllerName = std::string("Wii U Controller ") + std::to_string((int) chan) + " (Disconnected)"; + } + + bool WiiUController::Open() { + KPADError error; + KPADStatus* status = Ship::WiiU::GetKPADStatus(chan, &error); + if (!status || error != KPAD_ERROR_OK) { + Close(); + return false; + } + + connected = true; + extensionType = (WPADExtensionType) status->extensionType; + + // Create a GUID and name based on extension and channel + GUID = std::string("WiiU") + GetControllerExtensionName() + std::to_string((int) chan); + controllerName = std::string("Wii U ") + GetControllerExtensionName() + std::string(" ") + std::to_string((int) chan); + + return true; + } + + void WiiUController::Close() { + connected = false; + extensionType = (WPADExtensionType) -1; + + for (int i = 0; i < MAXCONTROLLERS; i++) { + getPressedButtons(i) = 0; + getLeftStickX(i) = 0; + getLeftStickY(i) = 0; + getRightStickX(i) = 0; + getRightStickY(i) = 0; + getGyroX(i) = 0; + getGyroY(i) = 0; + } + } + + void WiiUController::ReadFromSource(int32_t virtualSlot) { + auto profile = getProfile(virtualSlot); + + KPADError error; + KPADStatus* status = Ship::WiiU::GetKPADStatus(chan, &error); + if (!status) { + Close(); + return; + } + + // Make sure the controller type doesn't change after opening + if (status->extensionType != extensionType) { + Close(); + return; + } + + getPressedButtons(virtualSlot) = 0; + getLeftStickX(virtualSlot) = 0; + getLeftStickY(virtualSlot) = 0; + getRightStickX(virtualSlot) = 0; + getRightStickY(virtualSlot) = 0; + getGyroX(virtualSlot) = 0; + getGyroY(virtualSlot) = 0; + + if (error != KPAD_ERROR_OK) { + return; + } + + int16_t stickX = 0; + int16_t stickY = 0; + int16_t camX = 0; + int16_t camY = 0; + + switch (extensionType) { + case WPAD_EXT_PRO_CONTROLLER: + for (uint32_t i = WPAD_PRO_BUTTON_UP; i <= WPAD_PRO_STICK_R_EMULATION_UP; i <<= 1) { + if (profile->Mappings.contains(i)) { + // check if the stick is mapped to an analog stick + if (i >= WPAD_PRO_STICK_L_EMULATION_LEFT) { + float axisX = i >= WPAD_PRO_STICK_R_EMULATION_LEFT ? status->pro.rightStick.x : status->pro.leftStick.x; + float axisY = i >= WPAD_PRO_STICK_R_EMULATION_LEFT ? status->pro.rightStick.y : status->pro.leftStick.y; + + if (profile->Mappings[i] == BTN_STICKRIGHT || profile->Mappings[i] == BTN_STICKLEFT) { + stickX = axisX * 85; + continue; + } else if (profile->Mappings[i] == BTN_STICKDOWN || profile->Mappings[i] == BTN_STICKUP) { + stickY = axisY * 85; + continue; + } else if (profile->Mappings[i] == BTN_VSTICKRIGHT || profile->Mappings[i] == BTN_VSTICKLEFT) { + camX = axisX * 85; + continue; + } else if (profile->Mappings[i] == BTN_VSTICKDOWN || profile->Mappings[i] == BTN_VSTICKUP) { + camY = axisY * 85; + continue; + } + } + + if (status->pro.hold & i) { + getPressedButtons(virtualSlot) |= profile->Mappings[i]; + } + } + } + break; + case WPAD_EXT_CLASSIC: + case WPAD_EXT_MPLUS_CLASSIC: + for (uint32_t i = WPAD_CLASSIC_BUTTON_UP; i <= WPAD_CLASSIC_STICK_R_EMULATION_UP; i <<= 1) { + if (profile->Mappings.contains(i)) { + // check if the stick is mapped to an analog stick + if (i >= WPAD_CLASSIC_STICK_L_EMULATION_LEFT) { + float axisX = i >= WPAD_CLASSIC_STICK_R_EMULATION_LEFT ? status->classic.rightStick.x : status->classic.leftStick.x; + float axisY = i >= WPAD_CLASSIC_STICK_R_EMULATION_LEFT ? status->classic.rightStick.y : status->classic.leftStick.y; + + if (profile->Mappings[i] == BTN_STICKRIGHT || profile->Mappings[i] == BTN_STICKLEFT) { + stickX = axisX * 85; + continue; + } else if (profile->Mappings[i] == BTN_STICKDOWN || profile->Mappings[i] == BTN_STICKUP) { + stickY = axisY * 85; + continue; + } else if (profile->Mappings[i] == BTN_VSTICKRIGHT || profile->Mappings[i] == BTN_VSTICKLEFT) { + camX = axisX * 85; + continue; + } else if (profile->Mappings[i] == BTN_VSTICKDOWN || profile->Mappings[i] == BTN_VSTICKUP) { + camY = axisY * 85; + continue; + } + } + + if (status->classic.hold & i) { + getPressedButtons(virtualSlot) |= profile->Mappings[i]; + } + } + } + break; + case WPAD_EXT_NUNCHUK: + case WPAD_EXT_MPLUS_NUNCHUK: + case WPAD_EXT_MPLUS: + case WPAD_EXT_CORE: + for (uint32_t i = WPAD_BUTTON_LEFT; i <= WPAD_BUTTON_HOME; i <<= 1) { + if (profile->Mappings.contains(i)) { + if (status->hold & i) { + getPressedButtons(virtualSlot) |= profile->Mappings[i]; + } + } + } + stickX += status->nunchuck.stick.x * 85; + stickY += status->nunchuck.stick.y * 85; + break; + } + + if (stickX || stickY) { + NormalizeStickAxis(virtualSlot, stickX, stickY, profile->AxisDeadzones[0], false); + } + + if (camX || camY) { + NormalizeStickAxis(virtualSlot, camX, camY, profile->AxisDeadzones[2], true); + } + } + + void WiiUController::WriteToSource(int32_t virtualSlot, ControllerCallback* controller) { + if (getProfile(virtualSlot)->UseRumble) { + WPADControlMotor(chan, controller->rumble); + } + } + + void WiiUController::ClearRawPress() { + // Clear already triggered buttons + KPADError error; + KPADStatus* status = Ship::WiiU::GetKPADStatus(chan, &error); + if (status) { + switch (extensionType) { + case WPAD_EXT_PRO_CONTROLLER: + status->pro.trigger = 0; + break; + case WPAD_EXT_CLASSIC: + case WPAD_EXT_MPLUS_CLASSIC: + status->classic.trigger = 0; + break; + case WPAD_EXT_NUNCHUK: + case WPAD_EXT_MPLUS_NUNCHUK: + case WPAD_EXT_MPLUS: + case WPAD_EXT_CORE: + status->trigger = 0; + break; + } + } + } + + int32_t WiiUController::ReadRawPress() { + KPADError error; + KPADStatus* status = Ship::WiiU::GetKPADStatus(chan, &error); + if (!status || error != KPAD_ERROR_OK) { + return -1; + } + + switch (extensionType) { + case WPAD_EXT_PRO_CONTROLLER: + for (uint32_t i = WPAD_PRO_BUTTON_UP; i <= WPAD_PRO_STICK_R_EMULATION_UP; i <<= 1) { + if (status->pro.trigger & i) { + return i; + } + } + + if (status->pro.leftStick.x > 0.7f) { + return WPAD_PRO_STICK_L_EMULATION_RIGHT; + } + if (status->pro.leftStick.x < -0.7f) { + return WPAD_PRO_STICK_L_EMULATION_LEFT; + } + if (status->pro.leftStick.y > 0.7f) { + return WPAD_PRO_STICK_L_EMULATION_UP; + } + if (status->pro.leftStick.y < -0.7f) { + return WPAD_PRO_STICK_L_EMULATION_DOWN; + } + + if (status->pro.rightStick.x > 0.7f) { + return WPAD_PRO_STICK_R_EMULATION_RIGHT; + } + if (status->pro.rightStick.x < -0.7f) { + return WPAD_PRO_STICK_R_EMULATION_LEFT; + } + if (status->pro.rightStick.y > 0.7f) { + return WPAD_PRO_STICK_R_EMULATION_UP; + } + if (status->pro.rightStick.y < -0.7f) { + return WPAD_PRO_STICK_R_EMULATION_DOWN; + } + break; + case WPAD_EXT_CLASSIC: + case WPAD_EXT_MPLUS_CLASSIC: + for (uint32_t i = WPAD_CLASSIC_BUTTON_UP; i <= WPAD_CLASSIC_STICK_R_EMULATION_UP; i <<= 1) { + if (status->classic.trigger & i) { + return i; + } + } + + if (status->classic.leftStick.x > 0.7f) { + return WPAD_CLASSIC_STICK_L_EMULATION_RIGHT; + } + if (status->classic.leftStick.x < -0.7f) { + return WPAD_CLASSIC_STICK_L_EMULATION_LEFT; + } + if (status->classic.leftStick.y > 0.7f) { + return WPAD_CLASSIC_STICK_L_EMULATION_UP; + } + if (status->classic.leftStick.y < -0.7f) { + return WPAD_CLASSIC_STICK_L_EMULATION_DOWN; + } + + if (status->classic.rightStick.x > 0.7f) { + return WPAD_CLASSIC_STICK_R_EMULATION_RIGHT; + } + if (status->classic.rightStick.x < -0.7f) { + return WPAD_CLASSIC_STICK_R_EMULATION_LEFT; + } + if (status->classic.rightStick.y > 0.7f) { + return WPAD_CLASSIC_STICK_R_EMULATION_UP; + } + if (status->classic.rightStick.y < -0.7f) { + return WPAD_CLASSIC_STICK_R_EMULATION_DOWN; + } + break; + case WPAD_EXT_NUNCHUK: + case WPAD_EXT_MPLUS_NUNCHUK: + case WPAD_EXT_MPLUS: + case WPAD_EXT_CORE: + for (uint32_t i = WPAD_BUTTON_LEFT; i <= WPAD_BUTTON_HOME; i <<= 1) { + if (status->trigger & i) { + return i; + } + } + break; + } + + return -1; + } + + const std::string WiiUController::GetButtonName(int32_t virtualSlot, int n64Button) { + std::map& Mappings = getProfile(virtualSlot)->Mappings; + const auto find = std::find_if(Mappings.begin(), Mappings.end(), [n64Button](const std::pair& pair) { + return pair.second == n64Button; + }); + + if (find == Mappings.end()) return "Unknown"; + + uint32_t btn = find->first; + + switch (extensionType) { + case WPAD_EXT_PRO_CONTROLLER: + switch (btn) { + case WPAD_PRO_BUTTON_A: return "A"; + case WPAD_PRO_BUTTON_B: return "B"; + case WPAD_PRO_BUTTON_X: return "X"; + case WPAD_PRO_BUTTON_Y: return "Y"; + case WPAD_PRO_BUTTON_LEFT: return "D-pad Left"; + case WPAD_PRO_BUTTON_RIGHT: return "D-pad Right"; + case WPAD_PRO_BUTTON_UP: return "D-pad Up"; + case WPAD_PRO_BUTTON_DOWN: return "D-pad Down"; + case WPAD_PRO_TRIGGER_ZL: return "ZL"; + case WPAD_PRO_TRIGGER_ZR: return "ZR"; + case WPAD_PRO_TRIGGER_L: return "L"; + case WPAD_PRO_TRIGGER_R: return "R"; + case WPAD_PRO_BUTTON_PLUS: return "+ (START)"; + case WPAD_PRO_BUTTON_MINUS: return "- (SELECT)"; + case WPAD_PRO_BUTTON_STICK_R: return "Stick Button R"; + case WPAD_PRO_BUTTON_STICK_L: return "Stick Button L"; + case WPAD_PRO_STICK_R_EMULATION_LEFT: return "Right Stick Left"; + case WPAD_PRO_STICK_R_EMULATION_RIGHT: return "Right Stick Right"; + case WPAD_PRO_STICK_R_EMULATION_UP: return "Right Stick Up"; + case WPAD_PRO_STICK_R_EMULATION_DOWN: return "Right Stick Down"; + case WPAD_PRO_STICK_L_EMULATION_LEFT: return "Left Stick Left"; + case WPAD_PRO_STICK_L_EMULATION_RIGHT: return "Left Stick Right"; + case WPAD_PRO_STICK_L_EMULATION_UP: return "Left Stick Up"; + case WPAD_PRO_STICK_L_EMULATION_DOWN: return "Left Stick Down"; + } + break; + case WPAD_EXT_CLASSIC: + case WPAD_EXT_MPLUS_CLASSIC: + switch (btn) { + case WPAD_CLASSIC_BUTTON_A: return "A"; + case WPAD_CLASSIC_BUTTON_B: return "B"; + case WPAD_CLASSIC_BUTTON_X: return "X"; + case WPAD_CLASSIC_BUTTON_Y: return "Y"; + case WPAD_CLASSIC_BUTTON_LEFT: return "D-pad Left"; + case WPAD_CLASSIC_BUTTON_RIGHT: return "D-pad Right"; + case WPAD_CLASSIC_BUTTON_UP: return "D-pad Up"; + case WPAD_CLASSIC_BUTTON_DOWN: return "D-pad Down"; + case WPAD_CLASSIC_BUTTON_ZL: return "ZL"; + case WPAD_CLASSIC_BUTTON_ZR: return "ZR"; + case WPAD_CLASSIC_BUTTON_L: return "L"; + case WPAD_CLASSIC_BUTTON_R: return "R"; + case WPAD_CLASSIC_BUTTON_PLUS: return "+ (START)"; + case WPAD_CLASSIC_BUTTON_MINUS: return "- (SELECT)"; + case WPAD_CLASSIC_STICK_R_EMULATION_LEFT: return "Right Stick Left"; + case WPAD_CLASSIC_STICK_R_EMULATION_RIGHT: return "Right Stick Right"; + case WPAD_CLASSIC_STICK_R_EMULATION_UP: return "Right Stick Up"; + case WPAD_CLASSIC_STICK_R_EMULATION_DOWN: return "Right Stick Down"; + case WPAD_CLASSIC_STICK_L_EMULATION_LEFT: return "Left Stick Left"; + case WPAD_CLASSIC_STICK_L_EMULATION_RIGHT: return "Left Stick Right"; + case WPAD_CLASSIC_STICK_L_EMULATION_UP: return "Left Stick Up"; + case WPAD_CLASSIC_STICK_L_EMULATION_DOWN: return "Left Stick Down"; + } + break; + case WPAD_EXT_NUNCHUK: + case WPAD_EXT_MPLUS_NUNCHUK: + case WPAD_EXT_MPLUS: + case WPAD_EXT_CORE: + switch (btn) { + case WPAD_BUTTON_A: return "A"; + case WPAD_BUTTON_B: return "B"; + case WPAD_BUTTON_1: return "1"; + case WPAD_BUTTON_2: return "2"; + case WPAD_BUTTON_LEFT: return "D-pad Left"; + case WPAD_BUTTON_RIGHT: return "D-pad Right"; + case WPAD_BUTTON_UP: return "D-pad Up"; + case WPAD_BUTTON_DOWN: return "D-pad Down"; + case WPAD_BUTTON_Z: return "Z"; + case WPAD_BUTTON_C: return "C"; + case WPAD_BUTTON_PLUS: return "+ (START)"; + case WPAD_BUTTON_MINUS: return "- (SELECT)"; + } + break; + } + + return "Unknown"; + } + + const std::string WiiUController::GetControllerName() { + return controllerName; + } + + void WiiUController::NormalizeStickAxis(int32_t virtualSlot, float x, float y, uint16_t threshold, bool isRightStick) { + auto profile = getProfile(virtualSlot); + + //create scaled circular dead-zone in range {-15 ... +15} + auto len = sqrt(x * x + y * y); + if (len < threshold) { + len = 0; + } + else if (len > 85.0) { + len = 85.0 / len; + } + else { + len = (len - threshold) * 85.0 / (85.0 - threshold) / len; + } + x *= len; + y *= len; + + //bound diagonals to an octagonal range {-68 ... +68} + if (x != 0.0 && y != 0.0) { + auto slope = y / x; + auto edgex = copysign(85.0 / (fabs(slope) + 16.0 / 69.0), x); + auto edgey = copysign(std::min(fabs(edgex * slope), 85.0 / (1.0 / fabs(slope) + 16.0 / 69.0)), y); + edgex = edgey / slope; + + auto scale = sqrt(edgex * edgex + edgey * edgey) / 85.0; + x *= scale; + y *= scale; + } + + if (isRightStick) { + getRightStickX(virtualSlot) = x * profile->AxisSensitivities[2]; + getRightStickY(virtualSlot) = y * profile->AxisSensitivities[3]; + } else { + getLeftStickX(virtualSlot) = x * profile->AxisSensitivities[0]; + getLeftStickY(virtualSlot) = y * profile->AxisSensitivities[1]; + } + } + + void WiiUController::CreateDefaultBinding(int32_t virtualSlot) { + auto profile = getProfile(virtualSlot); + profile->Mappings.clear(); + + profile->UseRumble = true; + profile->RumbleStrength = 1.0f; + profile->UseGyro = false; + + switch (extensionType) { + case WPAD_EXT_PRO_CONTROLLER: + profile->Mappings[WPAD_PRO_STICK_R_EMULATION_RIGHT] = BTN_CRIGHT; + profile->Mappings[WPAD_PRO_STICK_R_EMULATION_LEFT] = BTN_CLEFT; + profile->Mappings[WPAD_PRO_STICK_R_EMULATION_DOWN] = BTN_CDOWN; + profile->Mappings[WPAD_PRO_STICK_R_EMULATION_UP] = BTN_CUP; + profile->Mappings[WPAD_PRO_TRIGGER_ZR] = BTN_R; + profile->Mappings[WPAD_PRO_TRIGGER_L] = BTN_L; + profile->Mappings[WPAD_PRO_BUTTON_RIGHT] = BTN_DRIGHT; + profile->Mappings[WPAD_PRO_BUTTON_LEFT] = BTN_DLEFT; + profile->Mappings[WPAD_PRO_BUTTON_DOWN] = BTN_DDOWN; + profile->Mappings[WPAD_PRO_BUTTON_UP] = BTN_DUP; + profile->Mappings[WPAD_PRO_BUTTON_PLUS] = BTN_START; + profile->Mappings[WPAD_PRO_TRIGGER_ZL] = BTN_Z; + profile->Mappings[WPAD_PRO_BUTTON_B] = BTN_B; + profile->Mappings[WPAD_PRO_BUTTON_A] = BTN_A; + profile->Mappings[WPAD_PRO_STICK_L_EMULATION_RIGHT] = BTN_STICKRIGHT; + profile->Mappings[WPAD_PRO_STICK_L_EMULATION_LEFT] = BTN_STICKLEFT; + profile->Mappings[WPAD_PRO_STICK_L_EMULATION_DOWN] = BTN_STICKDOWN; + profile->Mappings[WPAD_PRO_STICK_L_EMULATION_UP] = BTN_STICKUP; + break; + case WPAD_EXT_CLASSIC: + case WPAD_EXT_MPLUS_CLASSIC: + profile->Mappings[WPAD_CLASSIC_STICK_R_EMULATION_RIGHT] = BTN_CRIGHT; + profile->Mappings[WPAD_CLASSIC_STICK_R_EMULATION_LEFT] = BTN_CLEFT; + profile->Mappings[WPAD_CLASSIC_STICK_R_EMULATION_DOWN] = BTN_CDOWN; + profile->Mappings[WPAD_CLASSIC_STICK_R_EMULATION_UP] = BTN_CUP; + profile->Mappings[WPAD_CLASSIC_BUTTON_ZR] = BTN_R; + profile->Mappings[WPAD_CLASSIC_BUTTON_L] = BTN_L; + profile->Mappings[WPAD_CLASSIC_BUTTON_RIGHT] = BTN_DRIGHT; + profile->Mappings[WPAD_CLASSIC_BUTTON_LEFT] = BTN_DLEFT; + profile->Mappings[WPAD_CLASSIC_BUTTON_DOWN] = BTN_DDOWN; + profile->Mappings[WPAD_CLASSIC_BUTTON_UP] = BTN_DUP; + profile->Mappings[WPAD_CLASSIC_BUTTON_PLUS] = BTN_START; + profile->Mappings[WPAD_CLASSIC_BUTTON_ZL] = BTN_Z; + profile->Mappings[WPAD_CLASSIC_BUTTON_B] = BTN_B; + profile->Mappings[WPAD_CLASSIC_BUTTON_A] = BTN_A; + profile->Mappings[WPAD_CLASSIC_STICK_L_EMULATION_RIGHT] = BTN_STICKRIGHT; + profile->Mappings[WPAD_CLASSIC_STICK_L_EMULATION_LEFT] = BTN_STICKLEFT; + profile->Mappings[WPAD_CLASSIC_STICK_L_EMULATION_DOWN] = BTN_STICKDOWN; + profile->Mappings[WPAD_CLASSIC_STICK_L_EMULATION_UP] = BTN_STICKUP; + break; + case WPAD_EXT_NUNCHUK: + case WPAD_EXT_MPLUS_NUNCHUK: + case WPAD_EXT_MPLUS: + case WPAD_EXT_CORE: + profile->Mappings[WPAD_BUTTON_1] = BTN_R; + profile->Mappings[WPAD_BUTTON_2] = BTN_L; + profile->Mappings[WPAD_BUTTON_RIGHT] = BTN_DRIGHT; + profile->Mappings[WPAD_BUTTON_LEFT] = BTN_DLEFT; + profile->Mappings[WPAD_BUTTON_DOWN] = BTN_DDOWN; + profile->Mappings[WPAD_BUTTON_UP] = BTN_DUP; + profile->Mappings[WPAD_BUTTON_PLUS] = BTN_START; + profile->Mappings[WPAD_BUTTON_MINUS] = BTN_Z; + profile->Mappings[WPAD_BUTTON_B] = BTN_B; + profile->Mappings[WPAD_BUTTON_A] = BTN_A; + break; + } + + for (int i = 0; i < 4; i++) { + profile->AxisSensitivities[i] = 1.0f; + profile->AxisDeadzones[i] = 0.0f; + } + } + + std::string WiiUController::GetControllerExtensionName() { + switch (extensionType) { + case WPAD_EXT_PRO_CONTROLLER: + return "ProController"; + case WPAD_EXT_CLASSIC: + case WPAD_EXT_MPLUS_CLASSIC: + return "ClassicController"; + case WPAD_EXT_NUNCHUK: + case WPAD_EXT_MPLUS_NUNCHUK: + case WPAD_EXT_MPLUS: + case WPAD_EXT_CORE: + return "WiiRemote"; + } + + return "Controller"; + } +} +#endif diff --git a/libultraship/libultraship/WiiUController.h b/libultraship/libultraship/WiiUController.h new file mode 100644 index 000000000..31006ecc2 --- /dev/null +++ b/libultraship/libultraship/WiiUController.h @@ -0,0 +1,38 @@ +#pragma once +#include "Controller.h" +#include + +#include + +namespace Ship { + class WiiUController : public Controller { + public: + WiiUController(WPADChan chan); + bool Open(); + void Close(); + + void ReadFromSource(int32_t virtualSlot) override; + void WriteToSource(int32_t virtualSlot, ControllerCallback* controller) override; + bool Connected() const override { return connected; }; + bool CanGyro() const override { return false; } + bool CanRumble() const override { return true; }; + + void ClearRawPress() override; + int32_t ReadRawPress() override; + + const std::string GetButtonName(int32_t virtualSlot, int n64Button) override; + const std::string GetControllerName() override; + + protected: + void NormalizeStickAxis(int32_t virtualSlot, float x, float y, uint16_t threshold, bool isRightStick); + void CreateDefaultBinding(int32_t virtualSlot) override; + + private: + std::string GetControllerExtensionName(); + std::string controllerName; + + bool connected; + WPADChan chan; + WPADExtensionType extensionType; + }; +} diff --git a/libultraship/libultraship/WiiUGamepad.cpp b/libultraship/libultraship/WiiUGamepad.cpp new file mode 100644 index 000000000..5d4747039 --- /dev/null +++ b/libultraship/libultraship/WiiUGamepad.cpp @@ -0,0 +1,329 @@ +#ifdef __WIIU__ +#include "WiiUGamepad.h" +#include "GlobalCtx2.h" +#include "ImGuiImpl.h" + +#include "WiiUImpl.h" + +namespace Ship { + WiiUGamepad::WiiUGamepad() : Controller(), connected(true), rumblePatternStrength(1.0f) { + memset(rumblePattern, 0xff, sizeof(rumblePattern)); + + GUID = "WiiUGamepad"; + } + + bool WiiUGamepad::Open() { + VPADReadError error; + VPADStatus* status = Ship::WiiU::GetVPADStatus(&error); + if (!status || error == VPAD_READ_INVALID_CONTROLLER) { + Close(); + return false; + } + + return true; + } + + void WiiUGamepad::Close() { + connected = false; + + for (int i = 0; i < MAXCONTROLLERS; i++) { + getPressedButtons(i) = 0; + getLeftStickX(i) = 0; + getLeftStickY(i) = 0; + getRightStickX(i) = 0; + getRightStickY(i) = 0; + getGyroX(i) = 0; + getGyroY(i) = 0; + } + } + + void WiiUGamepad::ReadFromSource(int32_t virtualSlot) { + auto profile = getProfile(virtualSlot); + + VPADReadError error; + VPADStatus* status = Ship::WiiU::GetVPADStatus(&error); + if (!status) { + Close(); + return; + } + + getPressedButtons(virtualSlot) = 0; + getLeftStickX(virtualSlot) = 0; + getLeftStickY(virtualSlot) = 0; + getRightStickX(virtualSlot) = 0; + getRightStickY(virtualSlot) = 0; + getGyroX(virtualSlot) = 0; + getGyroY(virtualSlot) = 0; + + if (error != VPAD_READ_SUCCESS) { + return; + } + + int16_t stickX = 0; + int16_t stickY = 0; + int16_t camX = 0; + int16_t camY = 0; + + for (uint32_t i = VPAD_BUTTON_SYNC; i <= VPAD_STICK_L_EMULATION_LEFT; i <<= 1) { + if (profile->Mappings.contains(i)) { + // check if the stick is mapped to an analog stick + if (i >= VPAD_STICK_R_EMULATION_DOWN) { + float axisX = i >= VPAD_STICK_L_EMULATION_DOWN ? status->leftStick.x : status->rightStick.x; + float axisY = i >= VPAD_STICK_L_EMULATION_DOWN ? status->leftStick.y : status->rightStick.y; + + if (profile->Mappings[i] == BTN_STICKRIGHT || profile->Mappings[i] == BTN_STICKLEFT) { + stickX = axisX * 85; + continue; + } else if (profile->Mappings[i] == BTN_STICKDOWN || profile->Mappings[i] == BTN_STICKUP) { + stickY = axisY * 85; + continue; + } else if (profile->Mappings[i] == BTN_VSTICKRIGHT || profile->Mappings[i] == BTN_VSTICKLEFT) { + camX = axisX * 85; + continue; + } else if (profile->Mappings[i] == BTN_VSTICKDOWN || profile->Mappings[i] == BTN_VSTICKUP) { + camY = axisY * 85; + continue; + } + } + + if (status->hold & i) { + getPressedButtons(virtualSlot) |= profile->Mappings[i]; + } + } + } + + if (stickX || stickY) { + NormalizeStickAxis(virtualSlot, stickX, stickY, profile->AxisDeadzones[0], false); + } + + if (camX || camY) { + NormalizeStickAxis(virtualSlot, camX, camY, profile->AxisDeadzones[2], true); + } + + if (profile->UseGyro) { + float gyroX = status->gyro.x * -8.0f; + float gyroY = status->gyro.z * 8.0f; + + float gyro_drift_x = profile->GyroData[DRIFT_X] / 100.0f; + float gyro_drift_y = profile->GyroData[DRIFT_Y] / 100.0f; + const float gyro_sensitivity = profile->GyroData[GYRO_SENSITIVITY]; + + if (gyro_drift_x == 0) { + gyro_drift_x = gyroX; + } + + if (gyro_drift_y == 0) { + gyro_drift_y = gyroY; + } + + profile->GyroData[DRIFT_X] = gyro_drift_x * 100.0f; + profile->GyroData[DRIFT_Y] = gyro_drift_y * 100.0f; + + getGyroX(virtualSlot) = gyroX - gyro_drift_x; + getGyroY(virtualSlot) = gyroY - gyro_drift_y; + + getGyroX(virtualSlot) *= gyro_sensitivity; + getGyroY(virtualSlot) *= gyro_sensitivity; + } + } + + void WiiUGamepad::WriteToSource(int32_t virtualSlot, ControllerCallback* controller) { + auto profile = getProfile(virtualSlot); + + if (profile->UseRumble) { + int patternSize = sizeof(rumblePattern) * 8; + + // update rumble pattern if strength changed + if (rumblePatternStrength != profile->RumbleStrength) { + rumblePatternStrength = profile->RumbleStrength; + if (rumblePatternStrength > 1.0f) { + rumblePatternStrength = 1.0f; + } else if (rumblePatternStrength < 0.0f) { + rumblePatternStrength = 0.0f; + } + + memset(rumblePattern, 0, sizeof(rumblePattern)); + + // distribute wanted amount of bits equally in pattern + float scale = (rumblePatternStrength * (1.0f - 0.3f)) + 0.3f; + int bitcnt = patternSize * scale; + for (int i = 0; i < bitcnt; i++) { + int bitpos = ((i * patternSize) / bitcnt) % patternSize; + rumblePattern[bitpos / 8] |= 1 << (bitpos % 8); + } + } + + VPADControlMotor(VPAD_CHAN_0, rumblePattern, controller->rumble ? patternSize : 0); + } + } + + void WiiUGamepad::ClearRawPress() { + // Clear already triggered buttons + VPADReadError error; + VPADStatus* status = Ship::WiiU::GetVPADStatus(&error); + if (status) { + status->trigger = 0; + } + } + + int32_t WiiUGamepad::ReadRawPress() { + VPADReadError error; + VPADStatus* status = Ship::WiiU::GetVPADStatus(&error); + if (!status || error != VPAD_READ_SUCCESS) { + return -1; + } + + for (uint32_t i = VPAD_BUTTON_SYNC; i <= VPAD_BUTTON_STICK_L; i <<= 1) { + if (status->trigger & i) { + return i; + } + } + + if (status->leftStick.x > 0.7f) { + return VPAD_STICK_L_EMULATION_RIGHT; + } + if (status->leftStick.x < -0.7f) { + return VPAD_STICK_L_EMULATION_LEFT; + } + if (status->leftStick.y > 0.7f) { + return VPAD_STICK_L_EMULATION_UP; + } + if (status->leftStick.y < -0.7f) { + return VPAD_STICK_L_EMULATION_DOWN; + } + + if (status->rightStick.x > 0.7f) { + return VPAD_STICK_R_EMULATION_RIGHT; + } + if (status->rightStick.x < -0.7f) { + return VPAD_STICK_R_EMULATION_LEFT; + } + if (status->rightStick.y > 0.7f) { + return VPAD_STICK_R_EMULATION_UP; + } + if (status->rightStick.y < -0.7f) { + return VPAD_STICK_R_EMULATION_DOWN; + } + + return -1; + } + + const std::string WiiUGamepad::GetButtonName(int32_t virtualSlot, int n64Button) { + std::map& Mappings = getProfile(virtualSlot)->Mappings; + const auto find = std::find_if(Mappings.begin(), Mappings.end(), [n64Button](const std::pair& pair) { + return pair.second == n64Button; + }); + + if (find == Mappings.end()) return "Unknown"; + + uint32_t btn = find->first; + switch (btn) { + case VPAD_BUTTON_A: return "A"; + case VPAD_BUTTON_B: return "B"; + case VPAD_BUTTON_X: return "X"; + case VPAD_BUTTON_Y: return "Y"; + case VPAD_BUTTON_LEFT: return "D-pad Left"; + case VPAD_BUTTON_RIGHT: return "D-pad Right"; + case VPAD_BUTTON_UP: return "D-pad Up"; + case VPAD_BUTTON_DOWN: return "D-pad Down"; + case VPAD_BUTTON_ZL: return "ZL"; + case VPAD_BUTTON_ZR: return "ZR"; + case VPAD_BUTTON_L: return "L"; + case VPAD_BUTTON_R: return "R"; + case VPAD_BUTTON_PLUS: return "+ (START)"; + case VPAD_BUTTON_MINUS: return "- (SELECT)"; + case VPAD_BUTTON_STICK_R: return "Stick Button R"; + case VPAD_BUTTON_STICK_L: return "Stick Button L"; + case VPAD_STICK_R_EMULATION_LEFT: return "Right Stick Left"; + case VPAD_STICK_R_EMULATION_RIGHT: return "Right Stick Right"; + case VPAD_STICK_R_EMULATION_UP: return "Right Stick Up"; + case VPAD_STICK_R_EMULATION_DOWN: return "Right Stick Down"; + case VPAD_STICK_L_EMULATION_LEFT: return "Left Stick Left"; + case VPAD_STICK_L_EMULATION_RIGHT: return "Left Stick Right"; + case VPAD_STICK_L_EMULATION_UP: return "Left Stick Up"; + case VPAD_STICK_L_EMULATION_DOWN: return "Left Stick Down"; + } + + return "Unknown"; + } + + const std::string WiiUGamepad::GetControllerName() { + return Connected() ? "Wii U GamePad" : "Wii U GamePad (Disconnected)"; + } + + void WiiUGamepad::NormalizeStickAxis(int32_t virtualSlot, float x, float y, uint16_t threshold, bool isRightStick) { + auto profile = getProfile(virtualSlot); + + //create scaled circular dead-zone in range {-15 ... +15} + auto len = sqrt(x * x + y * y); + if (len < threshold) { + len = 0; + } + else if (len > 85.0) { + len = 85.0 / len; + } + else { + len = (len - threshold) * 85.0 / (85.0 - threshold) / len; + } + x *= len; + y *= len; + + //bound diagonals to an octagonal range {-68 ... +68} + if (x != 0.0 && y != 0.0) { + auto slope = y / x; + auto edgex = copysign(85.0 / (fabs(slope) + 16.0 / 69.0), x); + auto edgey = copysign(std::min(fabs(edgex * slope), 85.0 / (1.0 / fabs(slope) + 16.0 / 69.0)), y); + edgex = edgey / slope; + + auto scale = sqrt(edgex * edgex + edgey * edgey) / 85.0; + x *= scale; + y *= scale; + } + + if (isRightStick) { + getRightStickX(virtualSlot) = x * profile->AxisSensitivities[2]; + getRightStickY(virtualSlot) = y * profile->AxisSensitivities[3]; + } else { + getLeftStickX(virtualSlot) = x * profile->AxisSensitivities[0]; + getLeftStickY(virtualSlot) = y * profile->AxisSensitivities[1]; + } + } + + void WiiUGamepad::CreateDefaultBinding(int32_t virtualSlot) { + auto profile = getProfile(virtualSlot); + profile->Mappings.clear(); + + profile->UseRumble = true; + profile->RumbleStrength = 1.0f; + profile->UseGyro = false; + + profile->Mappings[VPAD_STICK_R_EMULATION_RIGHT] = BTN_CRIGHT; + profile->Mappings[VPAD_STICK_R_EMULATION_LEFT] = BTN_CLEFT; + profile->Mappings[VPAD_STICK_R_EMULATION_DOWN] = BTN_CDOWN; + profile->Mappings[VPAD_STICK_R_EMULATION_UP] = BTN_CUP; + profile->Mappings[VPAD_BUTTON_ZR] = BTN_R; + profile->Mappings[VPAD_BUTTON_L] = BTN_L; + profile->Mappings[VPAD_BUTTON_RIGHT] = BTN_DRIGHT; + profile->Mappings[VPAD_BUTTON_LEFT] = BTN_DLEFT; + profile->Mappings[VPAD_BUTTON_DOWN] = BTN_DDOWN; + profile->Mappings[VPAD_BUTTON_UP] = BTN_DUP; + profile->Mappings[VPAD_BUTTON_PLUS] = BTN_START; + profile->Mappings[VPAD_BUTTON_ZL] = BTN_Z; + profile->Mappings[VPAD_BUTTON_B] = BTN_B; + profile->Mappings[VPAD_BUTTON_A] = BTN_A; + profile->Mappings[VPAD_STICK_L_EMULATION_RIGHT] = BTN_STICKRIGHT; + profile->Mappings[VPAD_STICK_L_EMULATION_LEFT] = BTN_STICKLEFT; + profile->Mappings[VPAD_STICK_L_EMULATION_DOWN] = BTN_STICKDOWN; + profile->Mappings[VPAD_STICK_L_EMULATION_UP] = BTN_STICKUP; + + for (int i = 0; i < 4; i++) { + profile->AxisSensitivities[i] = 1.0f; + profile->AxisDeadzones[i] = 0.0f; + } + + profile->GyroData[GYRO_SENSITIVITY] = 1.0f; + profile->GyroData[DRIFT_X] = 0.0f; + profile->GyroData[DRIFT_Y] = 0.0f; + } +} +#endif diff --git a/libultraship/libultraship/WiiUGamepad.h b/libultraship/libultraship/WiiUGamepad.h new file mode 100644 index 000000000..cdead5c90 --- /dev/null +++ b/libultraship/libultraship/WiiUGamepad.h @@ -0,0 +1,34 @@ +#pragma once +#include "Controller.h" +#include + +namespace Ship { + class WiiUGamepad : public Controller { + public: + WiiUGamepad(); + + bool Open(); + void Close(); + + void ReadFromSource(int32_t virtualSlot) override; + void WriteToSource(int32_t virtualSlot, ControllerCallback* controller) override; + bool Connected() const override { return connected; }; + bool CanGyro() const override { return true; } + bool CanRumble() const override { return true; }; + + void ClearRawPress() override; + int32_t ReadRawPress() override; + + const std::string GetButtonName(int32_t virtualSlot, int n64Button) override; + const std::string GetControllerName() override; + + protected: + void NormalizeStickAxis(int32_t virtualSlot, float x, float y, uint16_t threshold, bool isRightStick); + void CreateDefaultBinding(int32_t virtualSlot) override; + + private: + bool connected = true; + float rumblePatternStrength; + uint8_t rumblePattern[15]; + }; +} diff --git a/libultraship/libultraship/WiiUImpl.cpp b/libultraship/libultraship/WiiUImpl.cpp new file mode 100644 index 000000000..06cd3ec60 --- /dev/null +++ b/libultraship/libultraship/WiiUImpl.cpp @@ -0,0 +1,134 @@ +#ifdef __WIIU__ +#include "WiiUImpl.h" + +#include +#include +#include + +#include +#include +#include + +#include "Window.h" + +namespace Ship { +namespace WiiU { + +static bool hasVpad = false; +static VPADReadError vpadError; +static VPADStatus vpadStatus; + +static bool hasKpad[4] = { false }; +static KPADError kpadError[4] = { KPAD_ERROR_OK }; +static KPADStatus kpadStatus[4]; + +#ifdef _DEBUG +extern "C" { +void __wrap_abort() { + printf("Abort called.\n"); + // force a stack trace + *(uint32_t*)0xdeadc0de = 0xcafebabe; + while(1); +} + +static ssize_t wiiu_log_write(struct _reent* r, void* fd, const char* ptr, size_t len) { + char buf[1024]; + snprintf(buf, sizeof(buf), "%*.*s", len, len, ptr); + OSReport(buf); + WHBLogWritef("%*.*s", len, len, ptr); + return len; +} + +static const devoptab_t dotab_stdout = { + .name = "stdout_whb", + .write_r = wiiu_log_write, +}; +}; +#endif + +void Init() { +#ifdef _DEBUG + WHBLogUdpInit(); + WHBLogPrint("Hello World!"); + + devoptab_list[STD_OUT] = &dotab_stdout; + devoptab_list[STD_ERR] = &dotab_stdout; +#endif + + // make sure the required folders exist + mkdir("/vol/external01/wiiu/", 0755); + mkdir("/vol/external01/wiiu/apps/", 0755); + mkdir("/vol/external01/wiiu/apps/soh/", 0755); + + chdir("/vol/external01/wiiu/apps/soh/"); + + KPADInit(); + WPADEnableURCC(true); +} + +void Exit() { + KPADShutdown(); + + WHBLogUdpDeinit(); +} + +void ThrowMissingOTR(const char* otrPath) { + // TODO handle this better in the future + OSFatal("Main OTR file not found!"); +} + +void Update() { + bool rescan = false; + + VPADRead(VPAD_CHAN_0, &vpadStatus, 1, &vpadError); + if (vpadError == VPAD_READ_SUCCESS) { + if (!hasVpad) { + rescan = true; + } + + hasVpad = true; + } else if (vpadError != VPAD_READ_NO_SAMPLES) { + if (hasVpad) { + rescan = true; + } + + hasVpad = false; + } + + for (int i = 0; i < 4; i++) { + KPADReadEx((KPADChan) i, &kpadStatus[i], 1, &kpadError[i]); + if (kpadError[i] == KPAD_ERROR_OK && kpadStatus[i].extensionType != 255) { + if (!hasKpad[i]) { + rescan = true; + } + + hasKpad[i] = true; + } else if (kpadError[i] != KPAD_ERROR_NO_SAMPLES) { + if (hasKpad[i]) { + rescan = true; + } + + hasKpad[i] = false; + } + } + + // rescan devices if connection state changed + if (rescan) { + Ship::GlobalCtx2::GetInstance()->GetWindow()->GetControlDeck()->ScanPhysicalDevices(); + } +} + +VPADStatus *GetVPADStatus(VPADReadError *error) { + *error = vpadError; + return hasVpad ? &vpadStatus : nullptr; +} + +KPADStatus *GetKPADStatus(WPADChan chan, KPADError *error) { + *error = kpadError[chan]; + return hasKpad[chan] ? &kpadStatus[chan] : nullptr; +} + +}; +}; + +#endif diff --git a/libultraship/libultraship/WiiUImpl.h b/libultraship/libultraship/WiiUImpl.h new file mode 100644 index 000000000..8c8e64b54 --- /dev/null +++ b/libultraship/libultraship/WiiUImpl.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace Ship { +namespace WiiU { + +void Init(); + +void Exit(); + +void ThrowMissingOTR(const char* otrPath); + +void Update(); + +VPADStatus *GetVPADStatus(VPADReadError *error); + +KPADStatus *GetKPADStatus(WPADChan chan, KPADError *error); + +}; +}; diff --git a/libultraship/libultraship/Window.cpp b/libultraship/libultraship/Window.cpp index b302fbfec..f32ef02bf 100644 --- a/libultraship/libultraship/Window.cpp +++ b/libultraship/libultraship/Window.cpp @@ -33,6 +33,7 @@ extern "C" { int32_t osContInit(OSMesgQueue* mq, uint8_t* controllerBits, OSContStatus* status) { *controllerBits = 0; +#ifndef __WIIU__ if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0) { SPDLOG_ERROR("Failed to initialize SDL game controllers ({})", SDL_GetError()); exit(EXIT_FAILURE); @@ -47,6 +48,7 @@ extern "C" { SPDLOG_ERROR("Failed add SDL game controller mappings from \"{}\" ({})", controllerDb, SDL_GetError()); } #endif +#endif Ship::GlobalCtx2::GetInstance()->GetWindow()->GetControlDeck()->Init(controllerBits); diff --git a/libultraship/libultraship/WindowShim.cpp b/libultraship/libultraship/WindowShim.cpp index ca9357575..c892566ab 100644 --- a/libultraship/libultraship/WindowShim.cpp +++ b/libultraship/libultraship/WindowShim.cpp @@ -6,6 +6,8 @@ #include "Lib/Fast3D/gfx_opengl.h" #include "Lib/Fast3D/gfx_direct3d11.h" #include "Lib/Fast3D/gfx_direct3d12.h" +#include "Lib/Fast3D/gfx_wiiu.h" +#include "Lib/Fast3D/gfx_gx2.h" #include "Lib/Fast3D/gfx_window_manager_api.h" #include @@ -38,6 +40,10 @@ void SetWindowManager(struct GfxWindowManagerAPI** WmApi, struct GfxRenderingAPI *RenderingApi = &gfx_direct3d11_api; *WmApi = &gfx_dxgi_api; #endif +#ifdef __WIIU__ + *RenderingApi = &gfx_gx2_api; + *WmApi = &gfx_wiiu; +#endif // Config can override #ifdef ENABLE_DX11 diff --git a/scripts/wiiu/build.sh b/scripts/wiiu/build.sh new file mode 100755 index 000000000..c9c54e5f6 --- /dev/null +++ b/scripts/wiiu/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +cmake -H. -Bbuild-wiiu -GNinja -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/WiiU.cmake -DCMAKE_BUILD_TYPE:STRING=Release +cmake --build build-wiiu --target soh --config Release diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 5342374f0..5398e1cf5 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -1696,9 +1696,20 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows") STORMLIB_NO_AUTO_LINK ) endif() -endif() - -if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang") +elseif (CMAKE_SYSTEM_NAME STREQUAL "CafeOS") + target_compile_definitions(${PROJECT_NAME} PRIVATE + "$<$:" + "_DEBUG" + ">" + "$<$:" + "NDEBUG" + ">" + "SPDLOG_ACTIVE_LEVEL=3;" + "SPDLOG_NO_THREAD_ID;" + "SPDLOG_NO_TLS;" + "STBI_NO_THREAD_LOCALS;" + ) +elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang") target_compile_definitions(${PROJECT_NAME} PRIVATE "$<$:" "_DEBUG" @@ -1825,6 +1836,26 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") target_link_options(${PROJECT_NAME} PRIVATE -pthread ) + elseif (CMAKE_SYSTEM_NAME STREQUAL "CafeOS") + target_compile_options(${PROJECT_NAME} PRIVATE + -O2 + + # disable some warnings to not clutter output + -Wno-multichar + -Wno-return-type + -Wno-narrowing + -Wno-switch-outside-range + $<$: + -Wno-incompatible-pointer-types + -Wno-discarded-array-qualifiers + -Wno-discarded-qualifiers + -Wno-int-conversion + -Wno-implicit-function-declaration + -Wno-builtin-declaration-mismatch + -Wno-switch-unreachable + -Wno-stringop-overflow + > + ) else() if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") set(CPU_OPTION -msse2 -mfpmath=sse) @@ -1908,6 +1939,18 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") -lglad Threads::Threads ) +elseif(CMAKE_SYSTEM_NAME STREQUAL "CafeOS") + find_package(SDL2 REQUIRED) + set(ADDITIONAL_LIBRARY_DEPENDENCIES + "libultraship;" + "ZAPDUtils;" + SDL2::SDL2-static + + "$<$:-Wl,--wrap=abort>" + ) + target_include_directories(${PROJECT_NAME} PRIVATE + ${DEVKITPRO}/portlibs/wiiu/include/ + ) else() find_package(SDL2) set(THREADS_PREFER_PTHREAD_FLAG ON) @@ -1961,7 +2004,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows") endif() endif() -if(NOT CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") +if(NOT CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|CafeOS") INSTALL(TARGETS soh DESTINATION . COMPONENT ship) endif() @@ -1970,7 +2013,7 @@ execute_process(COMMAND ${CURL} -sSfL https://raw.githubusercontent.com/gabomdq/ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") INSTALL(FILES ${CMAKE_BINARY_DIR}/gamecontrollerdb.txt DESTINATION ../MacOS COMPONENT ship) -elseif(NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoSwitch") +elseif(NOT "${CMAKE_SYSTEM_NAME}" MATCHES "NintendoSwitch|CafeOS") INSTALL(FILES ${CMAKE_BINARY_DIR}/gamecontrollerdb.txt DESTINATION . COMPONENT ship) endif() @@ -1982,9 +2025,9 @@ install(CODE " ") endif() -if(CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") +if(CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|CafeOS") if (NOT TARGET pathconf) - add_library(pathconf OBJECT switch/pathconf.c) + add_library(pathconf OBJECT platform/pathconf.c) endif() target_link_libraries(${PROJECT_NAME} PRIVATE "${ADDITIONAL_LIBRARY_DEPENDENCIES}" $ ) else() @@ -2006,4 +2049,10 @@ nx_create_nro(soh INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/soh.nro DESTINATION . COMPONENT ship) +elseif(CMAKE_SYSTEM_NAME MATCHES "CafeOS") + +wut_create_rpx(${PROJECT_NAME}) + +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/soh.rpx DESTINATION . COMPONENT ship) + endif() diff --git a/soh/include/functions.h b/soh/include/functions.h index 0bea29da2..db0db0da7 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -60,7 +60,9 @@ void Locale_ResetRegion(void); u32 func_80001F48(void); u32 func_80001F8C(void); u32 Locale_IsRegionNative(void); -#if !defined(__APPLE__) && !defined(__SWITCH__) +#ifdef __WIIU__ +void _assert(const char* exp, const char* file, s32 line); +#elif !defined(__APPLE__) && !defined(__SWITCH__) void __assert(const char* exp, const char* file, s32 line); #endif #if defined(__APPLE__) && defined(NDEBUG) diff --git a/soh/include/macros.h b/soh/include/macros.h index f2e339e34..1822cb1d6 100644 --- a/soh/include/macros.h +++ b/soh/include/macros.h @@ -233,7 +233,11 @@ extern GraphicsContext* __gfxCtx; #define VTX_T(x,y,z,s,t,cr,cg,cb,a) { { x, y, z }, 0, { s, t }, { cr, cg, cb, a } } +#ifdef __WIIU__ +#define ASSERT(expression) (void)((!!(expression)) || (_assert(#expression, __FILE__, (unsigned)(__LINE__)), 0)) +#else #define ASSERT(expression) (void)((!!(expression)) || (__assert(#expression, __FILE__, (unsigned)(__LINE__)), 0)) +#endif #define gDPSetTileCustom(pkt, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ do { \ diff --git a/soh/switch/pathconf.c b/soh/platform/pathconf.c similarity index 100% rename from soh/switch/pathconf.c rename to soh/platform/pathconf.c diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index ba61805f0..7fcf1e03d 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -49,6 +49,8 @@ #ifdef __SWITCH__ #include "SwitchImpl.h" +#elif defined(__WIIU__) +#include "WiiUImpl.h" #endif #include @@ -171,6 +173,8 @@ extern "C" void OTRExtScanner() { extern "C" void InitOTR() { #ifdef __SWITCH__ Ship::Switch::Init(Ship::PreInitPhase); +#elif defined(__WIIU__) + Ship::WiiU::Init(); #endif OTRGlobals::Instance = new OTRGlobals(); SaveManager::Instance = new SaveManager(); @@ -235,6 +239,7 @@ extern "C" void Graph_ProcessFrame(void (*run_one_game_iter)(void)) { } extern "C" void Graph_StartFrame() { +#ifndef __WIIU__ // Why -1? int32_t dwScancode = OTRGlobals::Instance->context->GetWindow()->lastScancode; OTRGlobals::Instance->context->GetWindow()->lastScancode = -1; @@ -292,6 +297,7 @@ extern "C" void Graph_StartFrame() { break; } } +#endif OTRGlobals::Instance->context->GetWindow()->StartFrame(); } diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 4fe0b1cfd..9b7f5126a 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -152,6 +152,12 @@ void SaveManager::Init() { // If the global save file exist, load it. Otherwise, create it. if (std::filesystem::exists(sGlobalPath)) { std::ifstream input(sGlobalPath); + +#ifdef __WIIU__ + alignas(0x40) char buffer[8192]; + input.rdbuf()->pubsetbuf(buffer, sizeof(buffer)); +#endif + nlohmann::json globalBlock; input >> globalBlock; @@ -474,6 +480,12 @@ void SaveManager::SaveFile(int fileNum) { } std::ofstream output(GetFileName(fileNum)); + +#ifdef __WIIU__ + alignas(0x40) char buffer[8192]; + output.rdbuf()->pubsetbuf(buffer, sizeof(buffer)); +#endif + output << std::setw(4) << baseBlock << std::endl; InitMeta(fileNum); @@ -486,6 +498,12 @@ void SaveManager::SaveGlobal() { globalBlock["zTargetSetting"] = gSaveContext.zTargetSetting; globalBlock["language"] = gSaveContext.language; std::ofstream output("Save/global.sav"); + +#ifdef __WIIU__ + alignas(0x40) char buffer[8192]; + output.rdbuf()->pubsetbuf(buffer, sizeof(buffer)); +#endif + output << std::setw(4) << globalBlock << std::endl; } @@ -494,6 +512,12 @@ void SaveManager::LoadFile(int fileNum) { InitFile(false); std::ifstream input(GetFileName(fileNum)); + +#ifdef __WIIU__ + alignas(0x40) char buffer[8192]; + input.rdbuf()->pubsetbuf(buffer, sizeof(buffer)); +#endif + nlohmann::json saveBlock; input >> saveBlock; if (!saveBlock.contains("version")) { @@ -1125,10 +1149,41 @@ void SaveManager::LoadStruct(const std::string& name, LoadStructFunc func) { } } +#ifdef __WIIU__ +// std::filesystem::copy_file doesn't work properly with the Wii U's toolchain atm +int copy_file(const char* src, const char* dst) +{ + alignas(0x40) uint8_t buf[4096]; + FILE* r = fopen(src, "r"); + if (!r) { + return -1; + } + FILE* w = fopen(dst, "w"); + if (!w) { + return -2; + } + + size_t res; + while ((res = fread(buf, 1, sizeof(buf), r)) > 0) { + if (fwrite(buf, 1, res, w) != res) { + break; + } + } + + fclose(r); + fclose(w); + return res >= 0 ? 0 : res; +} +#endif + void SaveManager::CopyZeldaFile(int from, int to) { assert(std::filesystem::exists(GetFileName(from))); DeleteZeldaFile(to); +#ifdef __WIIU__ + assert(copy_file(GetFileName(from).c_str(), GetFileName(to).c_str()) == 0); +#else std::filesystem::copy_file(GetFileName(from), GetFileName(to)); +#endif fileMetaInfo[to].valid = true; fileMetaInfo[to].deaths = fileMetaInfo[from].deaths; for (int i = 0; i < ARRAY_COUNT(fileMetaInfo[to].playerName); i++) { @@ -1457,6 +1512,12 @@ void SaveManager::ConvertFromUnversioned() { #define SLOT_OFFSET(index) (SRAM_HEADER_SIZE + 0x10 + (index * SLOT_SIZE)) std::ifstream input("oot_save.sav", std::ios::binary); + +#ifdef __WIIU__ + alignas(0x40) char buffer[8192]; + input.rdbuf()->pubsetbuf(buffer, sizeof(buffer)); +#endif + std::vector data(std::istreambuf_iterator(input), {}); input.close(); diff --git a/soh/src/boot/assert.c b/soh/src/boot/assert.c index 5a44a9308..f84f4d46f 100644 --- a/soh/src/boot/assert.c +++ b/soh/src/boot/assert.c @@ -1,7 +1,11 @@ #include "global.h" #ifndef __SWITCH__ +#ifdef __WIIU__ +void _assert(const char* exp, const char* file, s32 line) { +#else void __assert(const char* exp, const char* file, s32 line) { +#endif char msg[256]; osSyncPrintf("Assertion failed: %s, file %s, line %d, thread %d\n", exp, file, line, osGetThreadId(NULL)); diff --git a/soh/src/code/z_bgcheck.c b/soh/src/code/z_bgcheck.c index 6b5c2f613..a141d9f13 100644 --- a/soh/src/code/z_bgcheck.c +++ b/soh/src/code/z_bgcheck.c @@ -397,6 +397,16 @@ s32 CollisionPoly_LineVsPoly(CollisionPoly* poly, Vec3s* vtxList, Vec3f* posA, V (poly->normal.x * posB->x + poly->normal.y * posB->y + poly->normal.z * posB->z) * COLPOLY_NORMAL_FRAC + plane.originDist; +#ifdef __WIIU__ + // on some platforms this ends up as very small numbers due to rounding issues + if (IS_ZERO(planeDistA)) { + planeDistA = 0.0f; + } + if (IS_ZERO(planeDistB)) { + planeDistB = 0.0f; + } +#endif + planeDistDelta = planeDistA - planeDistB; if ((planeDistA >= 0.0f && planeDistB >= 0.0f) || (planeDistA < 0.0f && planeDistB < 0.0f) || (chkOneFace && planeDistA < 0.0f && planeDistB > 0.0f) || IS_ZERO(planeDistDelta)) {