Wii U support (#1097)

* Wii U support

* [WiiU] Combined Dockerfile

* [WiiU] Combined Dockerfile

* [WiiU] Combined Dockerfile

* Add Jenkins support

* wiiu: fix scissor clamp

* wiiu: improve button remapping

* wiiu: fix scaling issues

* Update Dockerfile after merge

* Pull assets before build

* Only stop container once

* Adjust logging sinks

* wiiu: Change button mapping to match PC version

* wiiu: Implement controller changes

* wiiu: Update BUILDING.md

Co-authored-by: qurious-pixel <62252937+qurious-pixel@users.noreply.github.com>
Co-authored-by: David Chavez <david@dcvz.io>
This commit is contained in:
GaryOderNichts 2022-08-15 04:57:24 +02:00 committed by GitHub
parent b989ef4f7f
commit 68e7f2e6c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 5460 additions and 73 deletions

View File

@ -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

View File

@ -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")

View File

@ -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

37
Jenkinsfile vendored
View File

@ -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
}
}
}
}
}
}

View File

@ -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()
{

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -631,8 +631,8 @@ typedef struct _TMPQHash
#else
BYTE Platform;
BYTE Reserved;
BYTE Platform;
USHORT lcLocale;
#endif

View File

@ -254,6 +254,34 @@
#endif
//-----------------------------------------------------------------------------
// Defines for Wii U platform
#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__WIIU__)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#include <malloc.h>
#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

View File

@ -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}" $<TARGET_OBJECTS:pathconf> )
else()

View File

@ -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");

View File

@ -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));
}

View File

@ -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)

View File

@ -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
"$<$<CONFIG:Debug>:"
"_DEBUG"
">"
"$<$<CONFIG:Release>:"
"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
"$<$<CONFIG:Debug>:"
"_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

View File

@ -3,11 +3,17 @@
#include "Window.h"
#include "Controller.h"
#include "DummyController.h"
#include "KeyboardController.h"
#include "SDLController.h"
#include <Utils/StringHelper.h>
#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<SDLController>(i);
@ -30,6 +37,20 @@ namespace Ship {
physicalDevices.push_back(std::make_shared<DummyController>("Auto", "Auto", true));
physicalDevices.push_back(std::make_shared<KeyboardController>());
#else
physicalDevices.push_back(std::make_shared<DummyController>("Auto", "Auto", true));
auto gamepad = std::make_shared<Ship::WiiUGamepad>();
gamepad->Open();
physicalDevices.push_back(gamepad);
for (int i = 0; i < 4; i++) {
auto controller = std::make_shared<Ship::WiiUController>((WPADChan) i);
controller->Open();
physicalDevices.push_back(controller);
}
#endif
physicalDevices.push_back(std::make_shared<DummyController>("Disconnected", "None", false));
for (const auto& device : physicalDevices) {

View File

@ -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;

View File

@ -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<Mercury>(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<spdlog::sink_ptr> Sinks;
auto SohConsoleSink = std::make_shared<spdlog::sinks::soh_sink_mt>();
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<spdlog::sinks::stdout_color_sink_mt>();
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<spdlog::sinks::rotating_file_sink_mt>(logPath, 1024 * 1024 * 10, 10);
FileSink->set_level(spdlog::level::trace);
std::vector<spdlog::sink_ptr> Sinks{
#if defined(__linux__)
ConsoleSink,
Sinks.push_back(FileSink);
#endif
FileSink,
SohConsoleSink
};
Logger = std::make_shared<spdlog::async_logger>(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());
}

View File

@ -30,6 +30,16 @@
#include "Lib/spdlog/include/spdlog/common.h"
#include "UltraController.h"
#ifdef __WIIU__
#include <gx2/registers.h> // 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 <SDL_hints.h>
#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<SDL_Window*>(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<const SDL_Event*>(event.sdl.event));
break;
#endif
#if defined(ENABLE_DX11) || defined(ENABLE_DX12)
case Backend::DX11:
ImGui_ImplWin32_WndProcHandler(static_cast<HWND>(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<SDL_Window*>(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<ImTextureID>(id);
}

View File

@ -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;

View File

@ -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<int>(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<int>(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);

View File

@ -0,0 +1,826 @@
#ifdef __WIIU__
#include "../../Window.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <malloc.h>
#include <map>
#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 <gx2/texture.h>
#include <gx2/draw.h>
#include <gx2/clear.h>
#include <gx2/state.h>
#include <gx2/swap.h>
#include <gx2/event.h>
#include <gx2/utils.h>
#include <gx2/mem.h>
#include <gx2/registers.h>
#include <gx2/display.h>
#include "gx2_shader_gen.h"
#include <proc_ui/procui.h>
#include <coreinit/memory.h>
#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<std::pair<uint64_t, uint32_t>, 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<std::pair<float, float>, uint16_t, hash_pair_ff> gfx_gx2_get_pixel_depth(int fb_id, const std::set<std::pair<float, float>>& coordinates) {
struct Framebuffer *buffer = (struct Framebuffer *) fb_id;
// fb 0 = main buffer
if (!buffer) {
buffer = &main_framebuffer;
}
std::unordered_map<std::pair<float, float>, 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

View File

@ -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

View File

@ -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();

View File

@ -0,0 +1,467 @@
#ifdef __WIIU__
#include <stdio.h>
#include <time.h>
#include <malloc.h>
#include <coreinit/time.h>
#include <coreinit/thread.h>
#include <coreinit/foreground.h>
#include <coreinit/memory.h>
#include <coreinit/memheap.h>
#include <coreinit/memdefaultheap.h>
#include <coreinit/memexpheap.h>
#include <coreinit/memfrmheap.h>
#include <gx2/state.h>
#include <gx2/context.h>
#include <gx2/display.h>
#include <gx2/event.h>
#include <gx2/swap.h>
#include <gx2/mem.h>
#include <gx2r/mem.h>
#include <whb/proc.h>
#include <proc_ui/procui.h>
#include <proc_ui/memory.h>
#include <vpad/input.h>
#include <padscore/kpad.h>
#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::ExitGame>();
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

View File

@ -0,0 +1,38 @@
#ifndef GFX_WIIU_H
#define GFX_WIIU_H
#ifdef __WIIU__
#include <vpad/input.h>
#include <padscore/kpad.h>
#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

View File

@ -0,0 +1,810 @@
#ifdef __WIIU__
#include "gx2_shader_gen.h"
#include "gx2_shader_inl.h"
#include <malloc.h>
#include <gx2/mem.h>
#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

View File

@ -0,0 +1,27 @@
#pragma once
#include "gfx_cc.h"
#include <gx2/shaders.h>
#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

View File

@ -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 */

View File

@ -0,0 +1,334 @@
// dear imgui: Renderer Backend for the Nintendo Wii U using GX2
#include "imgui.h"
#include "imgui_impl_gx2.h"
#include <stdio.h>
#include <stdint.h> // intptr_t
#include <malloc.h> // memalign
// GX2 includes
#include <whb/gfx.h>
#include <gx2/registers.h>
#include <gx2/draw.h>
#include <gx2/utils.h>
#include <gx2/mem.h>
#include <gx2r/surface.h>
// 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();
}

View File

@ -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();

View File

@ -0,0 +1,294 @@
// dear imgui: Platform Backend for the Wii U
#include "imgui.h"
#include "imgui_impl_wiiu.h"
#include <stdlib.h> // malloc/free
// Software keyboard
#include <nn/swkbd.h>
// 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();
}
}

View File

@ -0,0 +1,29 @@
// dear imgui: Platform Backend for the Wii U
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
// GamePad Input
#include <vpad/input.h>
// Controller Input
#include <padscore/kpad.h>
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);

View File

@ -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 <https://github.com/decaf-emu/decaf-emu>
# shader
./latte-assembler assemble --vsh=shader.vsh --psh=shader.psh shader.gsh
xxd -i shader.gsh > shader.h

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -761,7 +761,12 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
// Temporary buffer
// The first <points_count> 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];

View File

@ -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);
}

View File

@ -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
{

View File

@ -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<int32_t, int32_t>& Mappings = getProfile(virtualSlot)->Mappings;
const auto find = std::find_if(Mappings.begin(), Mappings.end(), [n64Button](const std::pair<int32_t, int32_t>& 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

View File

@ -0,0 +1,38 @@
#pragma once
#include "Controller.h"
#include <string>
#include <padscore/wpad.h>
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;
};
}

View File

@ -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<int32_t, int32_t>& Mappings = getProfile(virtualSlot)->Mappings;
const auto find = std::find_if(Mappings.begin(), Mappings.end(), [n64Button](const std::pair<int32_t, int32_t>& 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

View File

@ -0,0 +1,34 @@
#pragma once
#include "Controller.h"
#include <string>
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];
};
}

View File

@ -0,0 +1,134 @@
#ifdef __WIIU__
#include "WiiUImpl.h"
#include <stdio.h>
#include <unistd.h>
#include <sys/iosupport.h>
#include <whb/log.h>
#include <whb/log_udp.h>
#include <coreinit/debug.h>
#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

View File

@ -0,0 +1,22 @@
#pragma once
#include <vpad/input.h>
#include <padscore/kpad.h>
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);
};
};

View File

@ -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);

View File

@ -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 <string>
@ -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

4
scripts/wiiu/build.sh Executable file
View File

@ -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

View File

@ -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
"$<$<CONFIG:Debug>:"
"_DEBUG"
">"
"$<$<CONFIG:Release>:"
"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
"$<$<CONFIG:Debug>:"
"_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
$<$<COMPILE_LANGUAGE:C>:
-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
"$<$<CONFIG:Debug>:-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}" $<TARGET_OBJECTS:pathconf> )
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()

View File

@ -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)

View File

@ -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 { \

View File

@ -49,6 +49,8 @@
#ifdef __SWITCH__
#include "SwitchImpl.h"
#elif defined(__WIIU__)
#include "WiiUImpl.h"
#endif
#include <Audio.h>
@ -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();
}

View File

@ -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<char> data(std::istreambuf_iterator<char>(input), {});
input.close();

View File

@ -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));

View File

@ -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)) {