Compare commits

...

37 Commits

Author SHA1 Message Date
Maschell
f9bb2b7cc2 Don't show audio mode on boot if it's AUDIO_MODE_MATCH_SCREEN 2025-02-09 10:35:02 +01:00
Maschell
e569a3eb64 Update Dockerfile 2025-02-08 14:52:06 +01:00
Maschell
00fd88f360 Minor code cleanup 2025-02-08 14:52:06 +01:00
Maschell
3b8db3ada6 Fix "TV left, DRC right" audio mode to actually output the TV left and the Gampad right 2025-02-08 14:52:06 +01:00
Mefiresu
39ef5f1730 Fix TV/DRC audio combine mix
The "Combine TV and GamePad sound" mode wasn't grabbing the correct
sample offset for the R channel, instead making the combined sound all
L channel (essentially mono).
2025-02-08 14:52:06 +01:00
Maschell
67be2c7232 Fix default button combo in Readme 2025-02-08 14:52:06 +01:00
Maschell
0a4f828e4e Update Dockerfile 2025-02-08 14:52:06 +01:00
Maschell
06dd4f3832 Show notification with the current screen and audio if they are not "normal" and notifications are enabled 2025-02-08 14:52:06 +01:00
Maschell
13e28e135f Fix the default button combo for swapping the screens 2025-02-08 14:52:06 +01:00
Maschell
e967126645 Update Dockerfile 2025-02-08 14:52:06 +01:00
Maschell
a2d070c68a Fix showing notification on button combo errors 2025-02-08 14:52:06 +01:00
Maschell
2f4f5cc811 Bump version 2025-02-08 14:52:06 +01:00
Maschell
808b25ae9d Use the WUPS button combo api for implemention the button combos 2025-02-08 14:52:06 +01:00
Maschell
d7ea75f62c Update README.md 2024-05-08 21:35:56 +02:00
Maschell
0178383d33 Fix button combo detection for non-gamepad inputs, fix read change screen butto combo from config 2024-05-05 20:09:53 +02:00
Maschell
11dd5cde0a Add optional button combo to change screen mode 2024-05-05 18:28:08 +02:00
Maschell
835b89e8c0 Update Dockerfile 2024-05-05 18:00:46 +02:00
Maschell
0068582247 Update README 2024-04-27 14:33:49 +02:00
Maschell
de51a480d5 asdasd 2024-04-27 14:33:49 +02:00
Maschell
26283f918b Bump version 2024-04-27 14:33:49 +02:00
Maschell
09170c6010 Bump actions/checkout from 3 to 4 2024-04-27 14:33:49 +02:00
Maschell
09b5fefefa Bump softprops/action-gh-release from 1 to 2 2024-04-27 14:33:49 +02:00
Maschell
aba9be5e42 Update Dockerfile 2024-04-27 14:33:49 +02:00
Maschell
0221a05d96 Add support for mirroring the TV/DRC 2024-04-27 14:33:49 +02:00
Maschell
709868e825 Make sure to init static variables 2024-04-27 14:33:49 +02:00
Maschell
a75839ba27 Fix button combo checking in games that use ButtonProcMode 1 2024-04-27 14:33:49 +02:00
Maschell
52ce023982 Log actuall error code on error 2024-04-27 14:33:49 +02:00
Maschell
508cc5e03e Only update storage at application end if the value actually changed 2024-04-27 14:33:49 +02:00
Maschell
039263864f Refactor the config callbacks 2024-04-27 14:33:49 +02:00
Maschell
19fa1e0fe2 Add base settings to the root of the config 2024-04-27 14:33:49 +02:00
Maschell
fa1421f33b Update .gitignore to ignore zip files 2024-04-27 14:33:49 +02:00
Maschell
68da7df659 WUPS 0.8.0 support 2024-04-27 14:33:49 +02:00
Maschell
0fad651cd2 Create dependabot.yml 2023-07-24 10:12:20 +02:00
Maschell
411cf238e3 Bump version 2023-07-19 19:34:53 +02:00
Maschell
1acb1d9bf3 Update Dockerfile 2023-07-19 15:14:31 +02:00
Maschell
115cd6286c Add check-build-with-logging action 2023-07-14 20:58:42 +02:00
Maschell
9a919829a8 Update Dockerfile 2023-07-14 20:58:42 +02:00
17 changed files with 653 additions and 619 deletions

10
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

View File

@ -9,7 +9,7 @@ jobs:
clang-format:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: clang-format
run: |
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./src --exclude ./src/utils/json.hpp
@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-22.04
needs: clang-format
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: create version.h
run: |
git_hash=$(git rev-parse --short "$GITHUB_SHA")
@ -48,7 +48,7 @@ jobs:
- name: zip artifact
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.wps
- name: Create Release
uses: "softprops/action-gh-release@v1"
uses: "softprops/action-gh-release@v2"
with:
tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
draft: false

View File

@ -6,15 +6,26 @@ jobs:
clang-format:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: clang-format
run: |
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./src --exclude ./src/utils/json.hpp
check-build-with-logging:
runs-on: ubuntu-22.04
needs: clang-format
steps:
- uses: actions/checkout@v4
- name: build binary with logging
run: |
docker build . -t builder
docker run --rm -v ${PWD}:/project builder make DEBUG=VERBOSE
docker run --rm -v ${PWD}:/project builder make clean
docker run --rm -v ${PWD}:/project builder make DEBUG=1
build-binary:
runs-on: ubuntu-22.04
needs: clang-format
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: create version.h
run: |
git_hash=$(git rev-parse --short "${{ github.event.pull_request.head.sha }}")

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ cmake-build-debug/
*.wps
*.elf
CMakeLists.txt
*.zip

View File

@ -1,7 +1,7 @@
FROM ghcr.io/wiiu-env/devkitppc:20221228
FROM ghcr.io/wiiu-env/devkitppc:20241128
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20230215 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libnotifications:20230126 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libmappedmemory:20220904 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20250208 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libnotifications:20240426 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libmappedmemory:20230621 /artifacts $DEVKITPRO
WORKDIR project

View File

@ -34,7 +34,7 @@ CFLAGS := -Wall -O2 -ffunction-sections \
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
CXXFLAGS := $(CFLAGS) -std=c++20 -fno-exceptions -fno-rtti
CXXFLAGS := $(CFLAGS) -std=c++20
ASFLAGS := -g $(ARCH)
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libmappedmemory.ld $(WUPSSPECS)

View File

@ -26,16 +26,22 @@ Via the plugin config menu (press L, DPAD Down and Minus on the GamePad, Pro Con
- Displays notifications when swapping the screens or changing the audio mode.
- Swap screens: (Default is false)
- Swaps the TV and GamePad screen when set to true.
- Screen mode: (Default is "Normal")
- Sets the screen mode. See "Screen modes" for more details.
- Audio mode: (Default is "Sound matches screen")
- Sets the audio mode. See "Audio modes" for more details.
- **Button combos**
- Enable swap screen button combo: (Default is true)
- Determines if the screen can be swapped with a button combo.
- Swap screen: (Default is the "TV" button)
- Swap screen: (Default is the **"TV" button + DPAD right**)
- Button combo to swap the TV and GamePad screen.
- Enable swap change button combo: (Default is false)
- Determines if the screen can be changed with a button combo.
- Change screen: (Default is **TV Button + "right stick button"/R3**)
- Button combo to change screen mode.
- Enable change audio mode button combo: (Default is false)
- Determines if the audio mode can be changed via a button combo.
- Change audio: (Default is "left stick button"/L3)
- Change audio: (Default is **TV Button + "left stick button"/L3**)
- Button combo to change the audio mode.
#### Audio Modes
@ -53,6 +59,19 @@ SwipSwapme does not only allow you to swap the screen, it also offers multiple a
- **Left: TV; Right: GamePad**
- Outputs the TV sound on the left speaker, and the GamePad sound on the right speaker.
#### Screen Modes
SwipSwapme does not only allow you to swap the screen, it also offers multiple screen modes:
- **Normal**
- The screen output is not touched at all.
- **Swap TV and GamePad**
- Swaps the TV and GamePad screen.
- **Mirror TV**
- Mirrors the TV screen onto the GamePad.
- **Mirror GamePad**
- Mirrors the GamePad screen onto the TV.
## Building
For building you need:

View File

@ -29,12 +29,36 @@
void UpdateAudioMode();
DECL_FUNCTION(void, GX2CopyColorBufferToScanBuffer, GX2ColorBuffer *colorBuffer, GX2ScanTarget scan_target) {
if (gEnabled && gDoScreenSwap) {
if (scan_target == GX2_SCAN_TARGET_TV) {
scan_target = GX2_SCAN_TARGET_DRC;
} else if (scan_target == GX2_SCAN_TARGET_DRC) {
scan_target = GX2_SCAN_TARGET_TV;
if (gEnabled && gCurScreenMode != SCREEN_MODE_NONE) {
switch (gCurScreenMode) {
case SCREEN_MODE_SWAP: {
if (scan_target == GX2_SCAN_TARGET_TV) {
scan_target = GX2_SCAN_TARGET_DRC;
} else if (scan_target == GX2_SCAN_TARGET_DRC) {
scan_target = GX2_SCAN_TARGET_TV;
}
break;
}
case SCREEN_MODE_MIRROR_TV: {
if (scan_target == GX2_SCAN_TARGET_TV) {
scan_target = GX2_SCAN_TARGET_TV | GX2_SCAN_TARGET_DRC;
} else if (scan_target == GX2_SCAN_TARGET_DRC) {
return;
}
break;
}
case SCREEN_MODE_MIRROR_DRC: {
if (scan_target == GX2_SCAN_TARGET_DRC) {
scan_target = GX2_SCAN_TARGET_TV | GX2_SCAN_TARGET_DRC;
} else if (scan_target == GX2_SCAN_TARGET_TV) {
return;
}
break;
}
default:
break;
}
if (colorBuffer->surface.aa != GX2_AA_MODE1X) {
// If AA is enabled, we need to resolve the AA buffer.
GX2Surface tempSurface;
@ -65,115 +89,8 @@ DECL_FUNCTION(void, GX2CopyColorBufferToScanBuffer, GX2ColorBuffer *colorBuffer,
real_GX2CopyColorBufferToScanBuffer(colorBuffer, scan_target);
}
void SwapScreens() {
gDoScreenSwap = !gDoScreenSwap;
if (gShowNotifications && gNotificationModuleInitDone) {
if (gDoScreenSwap) {
NotificationModule_AddInfoNotification("Swapping TV and GamePad screen");
} else {
NotificationModule_AddInfoNotification("Stop swapping TV and GamePad screen");
}
}
}
void SwapVoices();
extern "C" uint32_t VPADGetButtonProcMode(VPADChan chan);
static uint32_t sSwapScreenWasHoldForXFrameGamePad;
static uint32_t sSwapVoicesWasHoldForXFrameGamePad;
DECL_FUNCTION(int32_t, VPADRead, VPADChan chan, VPADStatus *buffer, uint32_t buffer_size, VPADReadError *error) {
VPADReadError real_error;
int32_t result = real_VPADRead(chan, buffer, buffer_size, &real_error);
if (gEnabled && (gSwapScreenButtonComboEnabled || gChangeAudioModeButtonComboEnabled)) {
if (result > 0 && real_error == VPAD_READ_SUCCESS) {
// Fix games like TP HD
bool checkFullBuffer = VPADGetButtonProcMode(chan) == 1;
if (gChangeAudioModeButtonComboEnabled && checkButtonComboVPAD(buffer,
checkFullBuffer ? buffer_size : 1,
gSwapAudioButtonCombo,
sSwapVoicesWasHoldForXFrameGamePad)) {
SwapVoices();
}
if (gSwapScreenButtonComboEnabled && checkButtonComboVPAD(buffer,
checkFullBuffer ? buffer_size : 1,
gSwapScreenButtonCombo,
sSwapScreenWasHoldForXFrameGamePad)) {
SwapScreens();
}
}
}
if (error) {
*error = real_error;
}
return result;
}
const char *modeToStr(SwipSwapAudioMode mode) {
switch (mode) {
case AUDIO_MODE_NONE:
return "Audio mode: Normal";
case AUDIO_MODE_SWAP:
return "Audio mode: Swap TV and GamePad";
case AUDIO_MODE_MATCH_SCREEN:
return "Audio mode: Sound matches screen";
case AUDIO_MODE_COMBINE:
return "Audio mode: Combine TV and GamePad";
case AUDIO_MODE_LEFT_TV_RIGHT_DRC:
return "Audio mode: Left speaker TV, right speaker GamePad";
case AUDIO_MODE_MAX_VALUE:
break;
}
return "Invalid audio mode";
}
void SwapVoices() {
auto val = (uint32_t) gCurAudioMode;
val++;
if (val >= AUDIO_MODE_MAX_VALUE) {
val = 0;
}
gCurAudioMode = static_cast<SwipSwapAudioMode>(val);
if (gShowNotifications && gNotificationModuleInitDone) {
NotificationModule_AddInfoNotification(modeToStr(gCurAudioMode));
}
}
static uint32_t sSwapScreenWasHoldForXFrame[4];
static uint32_t sSwapAudioWasHoldForXFrame[4];
DECL_FUNCTION(void, WPADRead, WPADChan chan, WPADStatusProController *data) {
real_WPADRead(chan, data);
if (gEnabled && (gSwapScreenButtonComboEnabled || gChangeAudioModeButtonComboEnabled) && chan >= 0 && chan < 4) {
if (data && data[0].err == 0) {
if (data[0].extensionType != 0xFF) {
uint32_t curButtonHold = 0;
if (data[0].extensionType == WPAD_EXT_CORE || data[0].extensionType == WPAD_EXT_NUNCHUK) {
// button data is in the first 2 bytes for wiimotes
curButtonHold = remapWiiMoteButtons(((uint16_t *) data)[0]);
} else if (data[0].extensionType == WPAD_EXT_CLASSIC) {
curButtonHold = remapClassicButtons(((uint32_t *) data)[10] & 0xFFFF);
} else if (data[0].extensionType == WPAD_EXT_PRO_CONTROLLER) {
curButtonHold = remapProButtons(data[0].buttons);
}
if (gSwapScreenButtonComboEnabled && checkButtonComboWPAD(curButtonHold, gSwapScreenButtonCombo, sSwapScreenWasHoldForXFrame[chan])) {
SwapScreens();
}
if (gChangeAudioModeButtonComboEnabled && checkButtonComboWPAD(curButtonHold, gSwapAudioButtonCombo, sSwapAudioWasHoldForXFrame[chan])) {
SwapVoices();
}
}
}
}
}
int16_t DRCCopy[0x120] = {};
int16_t TVCopy[0x120] = {};
static int16_t sDRCCopy[0x120] = {};
static int16_t sTVCopy[0x120] = {};
typedef void (*AIInitDMAfn)(int16_t *addr, uint32_t size);
void DoAudioMagic(int16_t *addr, uint32_t size, bool isDRC, AIInitDMAfn targetFunc, AIInitDMAfn otherFunc) {
auto sizeCpy = size > 576 ? 576 : size;
@ -188,23 +105,41 @@ void DoAudioMagic(int16_t *addr, uint32_t size, bool isDRC, AIInitDMAfn targetFu
otherFunc(addr, sizeCpy);
return;
case AUDIO_MODE_MATCH_SCREEN: {
if (gDoScreenSwap) {
otherFunc(addr, sizeCpy);
return;
switch (gCurScreenMode) {
case SCREEN_MODE_SWAP:
otherFunc(addr, sizeCpy);
return;
case SCREEN_MODE_MIRROR_TV:
case SCREEN_MODE_MIRROR_DRC: {
if (isDRC) {
memcpy(sDRCCopy, addr, sizeCpy);
} else {
memcpy(sTVCopy, addr, sizeCpy);
}
if (gCurScreenMode == SCREEN_MODE_MIRROR_TV) {
memcpy(addr, sTVCopy, sizeCpy);
} else if (gCurScreenMode == SCREEN_MODE_MIRROR_DRC) {
memcpy(addr, sDRCCopy, sizeCpy);
}
break;
}
case SCREEN_MODE_NONE:
case SCREEN_MODE_MAX_VALUE:
break;
}
break;
}
case AUDIO_MODE_COMBINE:
case AUDIO_MODE_LEFT_TV_RIGHT_DRC: {
if (isDRC) {
memcpy(DRCCopy, addr, sizeCpy);
memcpy(sDRCCopy, addr, sizeCpy);
} else {
memcpy(TVCopy, addr, sizeCpy);
memcpy(sTVCopy, addr, sizeCpy);
}
if (gCurAudioMode == AUDIO_MODE_COMBINE) {
for (uint32_t i = 0; i < 0x120; i += 2) {
// Combine left channel of TV and DRC
auto val = (((int32_t) TVCopy[i] + (int32_t) DRCCopy[i]) >> 1);
auto val = (((int32_t) sTVCopy[i] + (int32_t) sDRCCopy[i]) >> 1);
if (val > 0x7FFF) {
val = 0x7FFF;
} else if (val < -0x8000) {
@ -212,7 +147,7 @@ void DoAudioMagic(int16_t *addr, uint32_t size, bool isDRC, AIInitDMAfn targetFu
}
addr[i] = (int16_t) val;
// Combine right channel of TV and DRC
val = (((int32_t) TVCopy[i] + (int32_t) DRCCopy[i]) >> 1);
val = (((int32_t) sTVCopy[i + 1] + (int32_t) sDRCCopy[i + 1]) >> 1);
if (val > 0x7FFF) {
val = 0x7FFF;
} else if (val < -0x8000) {
@ -223,21 +158,21 @@ void DoAudioMagic(int16_t *addr, uint32_t size, bool isDRC, AIInitDMAfn targetFu
} else if (gCurAudioMode == AUDIO_MODE_LEFT_TV_RIGHT_DRC) {
for (uint32_t i = 0; i < 0x120; i += 2) {
// Mix down TV to MONO and put it in the left channel
auto val = (((int32_t) TVCopy[i] + (int32_t) TVCopy[i + 1]) >> 1);
if (val > 0x7FFF) {
val = 0x7FFF;
} else if (val < -0x8000) {
val = 0x8000;
}
addr[i] = (int16_t) val;
// Mix down DRC to MONO and put it in the right channel
val = (((int32_t) DRCCopy[i] + (int32_t) DRCCopy[i + 1]) >> 1);
auto val = (((int32_t) sTVCopy[i] + (int32_t) sTVCopy[i + 1]) >> 1);
if (val > 0x7FFF) {
val = 0x7FFF;
} else if (val < -0x8000) {
val = 0x8000;
}
addr[i + 1] = (int16_t) val;
// Mix down DRC to MONO and put it in the right channel
val = (((int32_t) sDRCCopy[i] + (int32_t) sDRCCopy[i + 1]) >> 1);
if (val > 0x7FFF) {
val = 0x7FFF;
} else if (val < -0x8000) {
val = 0x8000;
}
addr[i] = (int16_t) val;
}
}
break;
@ -288,7 +223,7 @@ DECL_FUNCTION(void, AXUpdateDeviceModes2) {
}
DECL_FUNCTION(uint32_t, AVMGetTVAudioMode, uint32_t *mode) {
auto res = real_AVMGetTVAudioMode(mode);
const auto res = real_AVMGetTVAudioMode(mode);
if (!gEnabled) {
return res;
}
@ -315,8 +250,6 @@ void UpdateAudioMode() {
}
WUPS_MUST_REPLACE(GX2CopyColorBufferToScanBuffer, WUPS_LOADER_LIBRARY_GX2, GX2CopyColorBufferToScanBuffer);
WUPS_MUST_REPLACE(VPADRead, WUPS_LOADER_LIBRARY_VPAD, VPADRead);
WUPS_MUST_REPLACE(WPADRead, WUPS_LOADER_LIBRARY_PADSCORE, WPADRead);
WUPS_MUST_REPLACE_FOR_PROCESS(AIInitDMA, WUPS_LOADER_LIBRARY_SND_CORE, AIInitDMA, WUPS_FP_TARGET_PROCESS_ALL);
WUPS_MUST_REPLACE_FOR_PROCESS(AI2InitDMA, WUPS_LOADER_LIBRARY_SND_CORE, AI2InitDMA, WUPS_FP_TARGET_PROCESS_ALL);

View File

@ -16,12 +16,13 @@
****************************************************************************/
#include "main.h"
#include "retain_vars.hpp"
#include "utils/WUPSConfigItemButtonCombo.h"
#include "utils/config.h"
#include "utils/logger.h"
#include <forward_list>
#include <notifications/notifications.h>
#include <string>
#include <wups.h>
#include <wups/button_combo/api.h>
// Mandatory plugin information.
WUPS_PLUGIN_NAME("SwipSwapMe");
@ -35,6 +36,234 @@ WUPS_USE_WUT_DEVOPTAB();
WUPS_USE_STORAGE("SwipSwapMeAroma");
namespace {
uint32_t migrateButtonCombo(const uint32_t buttons) {
uint32_t conv_buttons = 0;
if (buttons & VPAD_BUTTON_A) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_A;
}
if (buttons & VPAD_BUTTON_B) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_B;
}
if (buttons & VPAD_BUTTON_X) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_X;
}
if (buttons & VPAD_BUTTON_Y) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_Y;
}
if (buttons & VPAD_BUTTON_LEFT) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_LEFT;
}
if (buttons & VPAD_BUTTON_RIGHT) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_RIGHT;
}
if (buttons & VPAD_BUTTON_UP) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_UP;
}
if (buttons & VPAD_BUTTON_DOWN) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_DOWN;
}
if (buttons & VPAD_BUTTON_ZL) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_ZL;
}
if (buttons & VPAD_BUTTON_ZR) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_ZR;
}
if (buttons & VPAD_BUTTON_L) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_L;
}
if (buttons & VPAD_BUTTON_R) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_R;
}
if (buttons & VPAD_BUTTON_PLUS) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_PLUS;
}
if (buttons & VPAD_BUTTON_MINUS) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_MINUS;
}
if (buttons & VPAD_BUTTON_STICK_R) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_STICK_R;
}
if (buttons & VPAD_BUTTON_STICK_L) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_STICK_L;
}
if (buttons & VPAD_BUTTON_TV) {
conv_buttons |= WUPS_BUTTON_COMBO_BUTTON_TV;
}
return conv_buttons;
}
const char *screenModeToStr(const SwipSwapScreenMode mode) {
switch (mode) {
case SCREEN_MODE_NONE:
return "Screen mode: Normal";
case SCREEN_MODE_SWAP:
return "Screen mode: Swapping TV and GamePad";
case SCREEN_MODE_MIRROR_TV:
return "Screen mode: Mirror TV to GamePad";
case SCREEN_MODE_MIRROR_DRC:
return "Screen mode: Mirror GamePad to TV";
case SCREEN_MODE_MAX_VALUE:
break;
}
return "Invalid screen mode";
}
const char *audioModeToStr(const SwipSwapAudioMode mode) {
switch (mode) {
case AUDIO_MODE_NONE:
return "Audio mode: Normal";
case AUDIO_MODE_SWAP:
return "Audio mode: Swap TV and GamePad";
case AUDIO_MODE_MATCH_SCREEN:
return "Audio mode: Sound matches screen";
case AUDIO_MODE_COMBINE:
return "Audio mode: Combine TV and GamePad";
case AUDIO_MODE_LEFT_TV_RIGHT_DRC:
return "Audio mode: Left speaker TV, right speaker GamePad";
case AUDIO_MODE_MAX_VALUE:
break;
}
return "Invalid audio mode";
}
template<typename... Args>
std::string string_format(const std::string &format, Args... args) {
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
auto size = static_cast<size_t>(size_s);
auto buf = std::make_unique<char[]>(size);
std::snprintf(buf.get(), size, format.c_str(), args...);
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
}
} // namespace
void SwapScreensCallback(WUPSButtonCombo_ControllerTypes, WUPSButtonCombo_ComboHandle, void *) {
if (!gEnabled || !gSwapScreenButtonComboEnabled) { return; }
if (gCurScreenMode == SCREEN_MODE_SWAP) {
gCurScreenMode = SCREEN_MODE_NONE;
} else {
gCurScreenMode = SCREEN_MODE_SWAP;
}
if (gShowNotifications && gNotificationModuleInitDone) {
NotificationModule_AddInfoNotification(screenModeToStr(gCurScreenMode));
}
}
void ChangeScreensCallback(WUPSButtonCombo_ControllerTypes, WUPSButtonCombo_ComboHandle, void *) {
if (!gEnabled || !gChangeScreenModeButtonComboEnabled) { return; }
auto val = static_cast<uint32_t>(gCurScreenMode);
val++;
if (val >= SCREEN_MODE_MAX_VALUE) {
val = 0;
}
gCurScreenMode = static_cast<SwipSwapScreenMode>(val);
if (gShowNotifications && gNotificationModuleInitDone) {
NotificationModule_AddInfoNotification(screenModeToStr(gCurScreenMode));
}
}
void ChangeVoicesCallback(WUPSButtonCombo_ControllerTypes, WUPSButtonCombo_ComboHandle, void *) {
if (!gEnabled || !gChangeAudioModeButtonComboEnabled) { return; }
auto val = static_cast<uint32_t>(gCurAudioMode);
val++;
if (val >= AUDIO_MODE_MAX_VALUE) {
val = 0;
}
gCurAudioMode = static_cast<SwipSwapAudioMode>(val);
if (gShowNotifications && gNotificationModuleInitDone) {
NotificationModule_AddInfoNotification(audioModeToStr(gCurAudioMode));
}
}
void migrateStorage() {
uint32_t doSwap = false;
if (WUPSStorageAPI::Get(SWAP_SCREENS_CONFIG_STRING_DEPRECATED, doSwap) == WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_INFO("Found deprecated config in storage. Storage will be migrated");
if (doSwap) {
gCurScreenMode = SCREEN_MODE_SWAP;
}
if (WUPSStorageAPI::DeleteItem(SWAP_SCREENS_CONFIG_STRING_DEPRECATED) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to delete deprecated value: \"%s\" from storage", SWAP_SCREENS_CONFIG_STRING_DEPRECATED);
}
}
uint32_t oldButtonCombo = 0;
if (WUPSStorageAPI::Get(SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING_DEPRECATED, oldButtonCombo) == WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_INFO("Found deprecated config in storage. Storage will be migrated");
gSwapScreenButtonCombo = migrateButtonCombo(oldButtonCombo);
if (WUPSStorageAPI::DeleteItem(SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING_DEPRECATED) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to delete deprecated value: \"%s\" from storage", SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING_DEPRECATED);
}
}
if (WUPSStorageAPI::Get(CHANGE_AUDIO_BUTTON_COMBO_CONFIG_STRING_DEPRECATED, oldButtonCombo) == WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_INFO("Found deprecated config in storage. Storage will be migrated");
gSwapAudioButtonCombo = migrateButtonCombo(oldButtonCombo);
if (WUPSStorageAPI::DeleteItem(CHANGE_AUDIO_BUTTON_COMBO_CONFIG_STRING_DEPRECATED) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to delete deprecated value: \"%s\" from storage", CHANGE_AUDIO_BUTTON_COMBO_CONFIG_STRING_DEPRECATED);
}
}
if (WUPSStorageAPI::Get(CHANGE_SCREEN_BUTTON_COMBO_CONFIG_STRING_DEPRECATED, oldButtonCombo) == WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_INFO("Found deprecated config in storage. Storage will be migrated");
gChangeScreenButtonCombo = migrateButtonCombo(oldButtonCombo);
if (WUPSStorageAPI::DeleteItem(CHANGE_SCREEN_BUTTON_COMBO_CONFIG_STRING_DEPRECATED) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to delete deprecated value: \"%s\" from storage", CHANGE_SCREEN_BUTTON_COMBO_CONFIG_STRING_DEPRECATED);
}
}
}
void showNotificationOnFirstBoot() {
uint32_t val;
if (WUPSStorageAPI::Get<uint32_t>(SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING, val) == WUPS_STORAGE_ERROR_NOT_FOUND) {
NotificationModule_AddInfoNotification("Press \ue089+\ue07C to swap the TV and Gamepad screen! You can change this button combination at any time in the config menu");
}
}
std::forward_list<WUPSButtonComboAPI::ButtonCombo> sButtonCombos;
WUPSButtonCombo_ComboHandle RegisterButtonCombo(const std::string_view label, const WUPSButtonCombo_Buttons buttonCombo, const WUPSButtonCombo_ComboCallback callback) {
const auto buttonComboLabel = string_format("SwipSwapMe: %s", label.data());
WUPSButtonCombo_ComboStatus status = WUPS_BUTTON_COMBO_COMBO_STATUS_INVALID_STATUS;
WUPSButtonCombo_Error err = WUPS_BUTTON_COMBO_ERROR_UNKNOWN_ERROR;
auto res = WUPSButtonComboAPI::CreateComboPressDown(buttonComboLabel,
buttonCombo,
callback,
nullptr,
status,
err);
if (!res || err != WUPS_BUTTON_COMBO_ERROR_SUCCESS) {
const std::string errorMsg = string_format("SwipSwapMe: Failed to register button combo \"%s\"", label.data());
DEBUG_FUNCTION_LINE_ERR("%s", errorMsg.c_str());
NotificationModule_AddErrorNotification(errorMsg.c_str());
} else {
if (status == WUPS_BUTTON_COMBO_COMBO_STATUS_CONFLICT) {
const auto conflictMsg = string_format("SwipSwapMe: \"%s\"-combo was disabled due to a conflict. Please assign a different combo", label.data());
DEBUG_FUNCTION_LINE_INFO("%s", conflictMsg.c_str());
NotificationModule_AddInfoNotification(conflictMsg.c_str());
} else if (status != WUPS_BUTTON_COMBO_COMBO_STATUS_VALID) {
const auto conflictMsg = string_format("SwipSwapMe: Unknown error happened while registering button combo \"%s\"", label.data());
DEBUG_FUNCTION_LINE_INFO("%s", conflictMsg.c_str());
NotificationModule_AddInfoNotification(conflictMsg.c_str());
}
const auto handle = res->getHandle();
sButtonCombos.emplace_front(std::move(*res));
return handle;
}
return WUPSButtonCombo_ComboHandle(nullptr);
}
// Gets called once the loader exists.
INITIALIZE_PLUGIN() {
initLogging();
@ -46,25 +275,78 @@ INITIALIZE_PLUGIN() {
DEBUG_FUNCTION_LINE_ERR("Failed to init notification lib");
}
// Open storage to read values
WUPSStorageError storageRes = WUPS_OpenStorage();
if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to open storage %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes);
} else {
LOAD_BOOL_FROM_STORAGE(ENABLED_CONFIG_STRING, gEnabled);
LOAD_BOOL_FROM_STORAGE(SWAP_SCREENS_CONFIG_STRING, gDoScreenSwap);
LOAD_BOOL_FROM_STORAGE(ENABLED_SWAP_SCREENS_COMBO_CONFIG_STRING, gSwapScreenButtonComboEnabled);
LOAD_BOOL_FROM_STORAGE(ENABLED_CHANGE_AUDIO_COMBO_CONFIG_STRING, gChangeAudioModeButtonComboEnabled);
LOAD_BOOL_FROM_STORAGE(ENABLE_NOTIFICATIONS_CONFIG_STRING, gShowNotifications);
LOAD_INT_FROM_STORAGE(SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING, gSwapScreenButtonCombo);
LOAD_INT_FROM_STORAGE(CHANGE_AUDIO_BUTTON_COMBO_CONFIG_STRING, gSwapAudioButtonCombo);
LOAD_INT_FROM_STORAGE(SCREEN_MODE_CONFIG_STRING, gCurAudioMode);
NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO, NOTIFICATION_MODULE_DEFAULT_OPTION_KEEP_UNTIL_SHOWN, true);
NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO, NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT, 15.0f);
NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR, NOTIFICATION_MODULE_DEFAULT_OPTION_KEEP_UNTIL_SHOWN, true);
NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR, NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT, 15.0f);
// Close storage
if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to close storage");
gCurScreenMode = DEFAULT_SCREEN_MODE_CONFIG_VALUE; // migrateStorage might override this
gSwapScreenButtonCombo = DEFAULT_SWAP_SCREEN_BUTTON_COMBO_CONFIG_VALUE; // migrateStorage might override this
gSwapAudioButtonCombo = DEFAULT_CHANGE_AUDIO_BUTTON_COMBO_CONFIG_VALUE; // migrateStorage might override this
gChangeScreenButtonCombo = DEFAULT_CHANGE_SCREEN_BUTTON_COMBO_CONFIG_VALUE; // migrateStorage might override this
migrateStorage();
showNotificationOnFirstBoot();
WUPSStorageError err;
if ((err = WUPSStorageAPI::GetOrStoreDefault(ENABLED_CONFIG_STRING, gEnabled, DEFAULT_ENABLED_CONFIG_VALUE)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create item \"%s\": %s (%d)", ENABLED_CONFIG_STRING, WUPSStorageAPI_GetStatusStr(err), err);
}
if ((err = WUPSStorageAPI::GetOrStoreDefault(SCREEN_MODE_CONFIG_STRING, gCurScreenMode, gCurScreenMode)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create item \"%s\": %s (%d)", SCREEN_MODE_CONFIG_STRING, WUPSStorageAPI_GetStatusStr(err), err);
}
if ((err = WUPSStorageAPI::GetOrStoreDefault(ENABLED_SWAP_SCREENS_COMBO_CONFIG_STRING, gSwapScreenButtonComboEnabled, DEFAULT_ENABLED_SWAP_SCREENS_COMBO_CONFIG_VALUE)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create item \"%s\": %s (%d)", ENABLED_SWAP_SCREENS_COMBO_CONFIG_STRING, WUPSStorageAPI_GetStatusStr(err), err);
}
if ((err = WUPSStorageAPI::GetOrStoreDefault(ENABLED_CHANGE_AUDIO_COMBO_CONFIG_STRING, gChangeAudioModeButtonComboEnabled, DEFAULT_ENABLED_CHANGE_AUDIO_COMBO_CONFIG_VALUE)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create item \"%s\": %s (%d)", ENABLED_CHANGE_AUDIO_COMBO_CONFIG_STRING, WUPSStorageAPI_GetStatusStr(err), err);
}
if ((err = WUPSStorageAPI::GetOrStoreDefault(ENABLED_CHANGE_SCREEN_COMBO_CONFIG_STRING, gChangeScreenModeButtonComboEnabled, DEFAULT_ENABLED_CHANGE_SCREEN_COMBO_CONFIG_VALUE)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create item \"%s\": %s (%d)", ENABLED_CHANGE_SCREEN_COMBO_CONFIG_STRING, WUPSStorageAPI_GetStatusStr(err), err);
}
if ((err = WUPSStorageAPI::GetOrStoreDefault(ENABLE_NOTIFICATIONS_CONFIG_STRING, gShowNotifications, DEFAULT_ENABLE_NOTIFICATIONS_CONFIG_VALUE)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create item \"%s\": %s (%d)", ENABLE_NOTIFICATIONS_CONFIG_STRING, WUPSStorageAPI_GetStatusStr(err), err);
}
if ((err = WUPSStorageAPI::GetOrStoreDefault(SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING, gSwapScreenButtonCombo, gSwapScreenButtonCombo)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create item \"%s\": %s (%d)", SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING, WUPSStorageAPI_GetStatusStr(err), err);
}
if ((err = WUPSStorageAPI::GetOrStoreDefault(CHANGE_AUDIO_BUTTON_COMBO_CONFIG_STRING, gSwapAudioButtonCombo, gSwapAudioButtonCombo)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create item \"%s\": %s (%d)", CHANGE_AUDIO_BUTTON_COMBO_CONFIG_STRING, WUPSStorageAPI_GetStatusStr(err), err);
}
if ((err = WUPSStorageAPI::GetOrStoreDefault(CHANGE_SCREEN_BUTTON_COMBO_CONFIG_STRING, gChangeScreenButtonCombo, gChangeScreenButtonCombo)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create item \"%s\": %s (%d)", CHANGE_SCREEN_BUTTON_COMBO_CONFIG_STRING, WUPSStorageAPI_GetStatusStr(err), err);
}
if ((err = WUPSStorageAPI::GetOrStoreDefault(AUDIO_MODE_CONFIG_STRING, gCurAudioMode, DEFAULT_AUDIO_MODE_CONFIG_VALUE)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create item \"%s\": %s (%d)", AUDIO_MODE_CONFIG_STRING, WUPSStorageAPI_GetStatusStr(err), err);
}
if ((err = WUPSStorageAPI::SaveStorage()) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to save storage: %s (%d)", WUPSStorageAPI_GetStatusStr(err), err);
}
gSwapScreenButtonComboHandle = RegisterButtonCombo("Swap Screens", static_cast<WUPSButtonCombo_Buttons>(gSwapScreenButtonCombo), SwapScreensCallback);
gSwapAudioButtonComboHandle = RegisterButtonCombo("Change Audio Mode", static_cast<WUPSButtonCombo_Buttons>(gSwapAudioButtonCombo), ChangeVoicesCallback);
gChangeScreenButtonComboHandle = RegisterButtonCombo("Change Screen Mode", static_cast<WUPSButtonCombo_Buttons>(gChangeScreenButtonCombo), ChangeScreensCallback);
WUPSConfigAPIOptionsV1 configOptions = {.name = "Swip Swap Me"};
if (WUPSConfigAPI_Init(configOptions, ConfigMenuOpenedCallback, ConfigMenuClosedCallback) != WUPSCONFIG_API_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to init config api");
}
if (gEnabled && gShowNotifications && gNotificationModuleInitDone) {
NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO, NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT, 5.0f);
if (gCurScreenMode != SCREEN_MODE_NONE) {
NotificationModule_AddInfoNotification(screenModeToStr(gCurScreenMode));
}
if (gCurAudioMode != AUDIO_MODE_NONE && gCurAudioMode != AUDIO_MODE_MATCH_SCREEN) {
NotificationModule_AddInfoNotification(audioModeToStr(gCurAudioMode));
}
}
NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO, NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT, 2.0f);
NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO, NOTIFICATION_MODULE_DEFAULT_OPTION_KEEP_UNTIL_SHOWN, false);
NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR, NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT, 2.0f);
NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR, NOTIFICATION_MODULE_DEFAULT_OPTION_KEEP_UNTIL_SHOWN, false);
deinitLogging();
}
@ -73,26 +355,35 @@ DEINITIALIZE_PLUGIN() {
NotificationModule_DeInitLibrary();
gNotificationModuleInitDone = false;
}
sButtonCombos.clear();
}
static SwipSwapAudioMode sAudioModeAtStart;
static SwipSwapScreenMode sScreenModeAtStart;
// Called whenever an application was started.
ON_APPLICATION_START() {
initLogging();
sAudioModeAtStart = gCurAudioMode;
sScreenModeAtStart = gCurScreenMode;
}
ON_APPLICATION_REQUESTS_EXIT() {
// Open storage to write current config
WUPSStorageError storageRes = WUPS_OpenStorage();
if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to open storage %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes);
} else {
WUPS_StoreInt(nullptr, SCREEN_MODE_CONFIG_STRING, (int32_t) gCurAudioMode);
WUPS_StoreBool(nullptr, SWAP_SCREENS_CONFIG_STRING, (int32_t) gDoScreenSwap);
if (sAudioModeAtStart != gCurAudioMode || sScreenModeAtStart != gCurScreenMode) {
WUPSStorageError err;
if ((err = WUPSStorageAPI::Store(AUDIO_MODE_CONFIG_STRING, gCurAudioMode)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to store audio mode to storage: %s (%d)", WUPSStorageAPI_GetStatusStr(err), err);
}
// Close storage
if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to close storage");
if ((err = WUPSStorageAPI::Store(SCREEN_MODE_CONFIG_STRING, gCurScreenMode)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to store screen mode to storage: %s (%d)", WUPSStorageAPI_GetStatusStr(err), err);
}
if ((err = WUPSStorageAPI::SaveStorage()) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to save storage: %s (%d)", WUPSStorageAPI_GetStatusStr(err), err);
}
}
deinitLogging();
}

View File

@ -1,5 +1,5 @@
#pragma once
#include "version.h"
#define PLUGIN_VERSION "v0.1"
#define PLUGIN_VERSION "v0.1.3"
#define PLUGIN_VERSION_FULL PLUGIN_VERSION PLUGIN_VERSION_EXTRA

View File

@ -1,12 +1,21 @@
#include "retain_vars.hpp"
#include <vpad/input.h>
#include "utils/config.h"
uint32_t gSwapScreenButtonCombo = VPAD_BUTTON_TV;
uint32_t gSwapAudioButtonCombo = VPAD_BUTTON_STICK_L;
bool gSwapScreenButtonComboEnabled = true;
bool gChangeAudioModeButtonComboEnabled = false;
bool gEnabled = true;
bool gDoScreenSwap = false;
bool gShowNotifications = true;
bool gNotificationModuleInitDone = true;
SwipSwapAudioMode gCurAudioMode = AUDIO_MODE_MATCH_SCREEN;
#include <wups/button_combo/defines.h>
bool gEnabled = DEFAULT_ENABLED_CONFIG_VALUE;
uint32_t gSwapScreenButtonCombo = DEFAULT_SWAP_SCREEN_BUTTON_COMBO_CONFIG_VALUE;
uint32_t gSwapAudioButtonCombo = DEFAULT_CHANGE_AUDIO_BUTTON_COMBO_CONFIG_VALUE;
uint32_t gChangeScreenButtonCombo = DEFAULT_CHANGE_SCREEN_BUTTON_COMBO_CONFIG_VALUE;
bool gSwapScreenButtonComboEnabled = DEFAULT_ENABLED_SWAP_SCREENS_COMBO_CONFIG_VALUE;
bool gChangeAudioModeButtonComboEnabled = DEFAULT_ENABLED_CHANGE_AUDIO_COMBO_CONFIG_VALUE;
bool gChangeScreenModeButtonComboEnabled = DEFAULT_ENABLED_CHANGE_SCREEN_COMBO_CONFIG_VALUE;
WUPSButtonCombo_ComboHandle gSwapScreenButtonComboHandle = WUPSButtonCombo_ComboHandle(nullptr);
WUPSButtonCombo_ComboHandle gSwapAudioButtonComboHandle = WUPSButtonCombo_ComboHandle(nullptr);
WUPSButtonCombo_ComboHandle gChangeScreenButtonComboHandle = WUPSButtonCombo_ComboHandle(nullptr);
bool gShowNotifications = DEFAULT_ENABLE_NOTIFICATIONS_CONFIG_VALUE;
SwipSwapScreenMode gCurScreenMode = DEFAULT_SCREEN_MODE_CONFIG_VALUE;
SwipSwapAudioMode gCurAudioMode = DEFAULT_AUDIO_MODE_CONFIG_VALUE;
bool gNotificationModuleInitDone = true;

View File

@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include <wups/button_combo/defines.h>
typedef enum SwipSwapAudioMode {
AUDIO_MODE_NONE = 0,
@ -11,12 +12,28 @@ typedef enum SwipSwapAudioMode {
AUDIO_MODE_MAX_VALUE = 5
} SwipSwapAudioMode;
typedef enum SwipSwapScreenMode {
SCREEN_MODE_NONE = 0,
SCREEN_MODE_SWAP = 1,
SCREEN_MODE_MIRROR_TV = 2,
SCREEN_MODE_MIRROR_DRC = 3,
SCREEN_MODE_MAX_VALUE = 4
} SwipSwapScreenMode;
extern uint32_t gSwapScreenButtonCombo;
extern uint32_t gSwapAudioButtonCombo;
extern uint32_t gChangeScreenButtonCombo;
extern bool gSwapScreenButtonComboEnabled;
extern bool gChangeAudioModeButtonComboEnabled;
extern bool gChangeScreenModeButtonComboEnabled;
extern WUPSButtonCombo_ComboHandle gSwapScreenButtonComboHandle;
extern WUPSButtonCombo_ComboHandle gSwapAudioButtonComboHandle;
extern WUPSButtonCombo_ComboHandle gChangeScreenButtonComboHandle;
extern bool gEnabled;
extern bool gDoScreenSwap;
extern bool gShowNotifications;
extern bool gNotificationModuleInitDone;
extern SwipSwapScreenMode gCurScreenMode;
extern SwipSwapAudioMode gCurAudioMode;

View File

@ -1,265 +0,0 @@
#include "WUPSConfigItemButtonCombo.h"
#include "utils/input.h"
#include <coreinit/debug.h>
#include <coreinit/thread.h>
#include <coreinit/time.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vpad/input.h>
#include <wups.h>
const char *getButtonChar(VPADButtons value) {
std::string combo;
if (value & VPAD_BUTTON_A) {
return "\ue000";
}
if (value & VPAD_BUTTON_B) {
return "\ue001";
}
if (value & VPAD_BUTTON_X) {
return "\ue002";
}
if (value & VPAD_BUTTON_Y) {
return "\ue003";
}
if (value & VPAD_BUTTON_L) {
return "\ue083";
}
if (value & VPAD_BUTTON_R) {
return "\ue084";
}
if (value & VPAD_BUTTON_ZL) {
return "\ue085";
}
if (value & VPAD_BUTTON_ZR) {
return "\ue086";
}
if (value & VPAD_BUTTON_UP) {
return "\ue079";
}
if (value & VPAD_BUTTON_DOWN) {
return "\ue07A";
}
if (value & VPAD_BUTTON_LEFT) {
return "\ue07B";
}
if (value & VPAD_BUTTON_RIGHT) {
return "\ue07C";
}
if (value & VPAD_BUTTON_STICK_L) {
return "\ue081";
}
if (value & VPAD_BUTTON_STICK_R) {
return "\ue082";
}
if (value & VPAD_BUTTON_PLUS) {
return "\ue045";
}
if (value & VPAD_BUTTON_MINUS) {
return "\ue046";
}
if (value & VPAD_BUTTON_TV) {
return "\ue089";
}
if (value & VPAD_BUTTON_RESERVED_BIT) {
return "\ue01E";
}
return "";
}
std::string getComboAsString(uint32_t value) {
char comboString[60];
memset(comboString, 0, sizeof(comboString));
for (uint32_t i = 0; i < 32; i++) {
uint32_t bitMask = 1 << i;
if (value & bitMask) {
auto val = getButtonChar(static_cast<VPADButtons>(bitMask));
if (val[0] != '\0') {
strcat(comboString, val);
strcat(comboString, "+");
}
}
}
std::string res(comboString);
if (res.ends_with("+")) {
res.pop_back();
}
return res;
}
int32_t WUPSConfigItemButtonCombo_getCurrentValueDisplay(void *context, char *out_buf, int32_t out_size) {
auto *item = (ConfigItemButtonCombo *) context;
snprintf(out_buf, out_size, "%s", getComboAsString(item->value).c_str());
return 0;
}
bool WUPSConfigItemButtonCombo_callCallback(void *context) {
auto *item = (ConfigItemButtonCombo *) context;
if (item->callback != nullptr) {
((ButtonComboValueChangedCallback) (item->callback))(item, item->value);
return true;
}
return false;
}
void checkForHold(ConfigItemButtonCombo *item) {
uint32_t lastHold = 0;
uint32_t holdFor = 0;
uint32_t holdForTarget = item->holdDurationInMs >> 4;
uint32_t holdAbortTarget = item->abortButtonHoldDurationInMs >> 4;
auto mask = VPAD_BUTTON_A | VPAD_BUTTON_B | VPAD_BUTTON_X | VPAD_BUTTON_Y | VPAD_BUTTON_L | VPAD_BUTTON_R |
VPAD_BUTTON_ZL | VPAD_BUTTON_ZR | VPAD_BUTTON_UP | VPAD_BUTTON_DOWN | VPAD_BUTTON_LEFT | VPAD_BUTTON_RIGHT |
VPAD_BUTTON_STICK_L | VPAD_BUTTON_STICK_R | VPAD_BUTTON_PLUS | VPAD_BUTTON_MINUS | VPAD_BUTTON_TV | VPAD_BUTTON_RESERVED_BIT;
KPADStatus kpad_data{};
KPADError kpad_error;
while (true) {
uint32_t buttonsHold = 0;
VPADReadError vpad_error = VPAD_READ_UNINITIALIZED;
VPADStatus vpad_data;
VPADRead(VPAD_CHAN_0, &vpad_data, 1, &vpad_error);
if (vpad_error == VPAD_READ_SUCCESS) {
buttonsHold = vpad_data.hold;
}
for (int i = 0; i < 4; i++) {
memset(&kpad_data, 0, sizeof(kpad_data));
if (KPADReadEx((KPADChan) i, &kpad_data, 1, &kpad_error) > 0) {
if (kpad_error == KPAD_ERROR_OK && kpad_data.extensionType != 0xFF) {
if (kpad_data.extensionType == WPAD_EXT_CORE || kpad_data.extensionType == WPAD_EXT_NUNCHUK) {
buttonsHold |= remapWiiMoteButtons(kpad_data.hold);
} else if (kpad_data.extensionType == WPAD_EXT_PRO_CONTROLLER) {
buttonsHold |= remapProButtons(kpad_data.pro.hold);
} else {
buttonsHold |= remapClassicButtons(kpad_data.classic.hold);
}
}
}
}
buttonsHold &= mask;
if (buttonsHold == lastHold) {
if (buttonsHold != 0) {
holdFor++;
}
} else {
holdFor = 0;
}
lastHold = buttonsHold;
if (holdFor >= holdAbortTarget && lastHold == item->abortButton) {
break;
}
if (holdFor >= holdForTarget) {
item->value = lastHold;
break;
}
OSSleepTicks(OSMillisecondsToTicks(16));
}
}
void WUPSConfigItemButtonCombo_onButtonPressed(void *context, WUPSConfigButtons buttons) {
auto *item = (ConfigItemButtonCombo *) context;
if (item->state == BUTTON_COMBO_STATE_NONE) {
if ((buttons & WUPS_CONFIG_BUTTON_A) == WUPS_CONFIG_BUTTON_A) {
item->state = BUTTON_COMBO_STATE_PREPARE_FOR_HOLD;
}
}
}
bool WUPSConfigItemButtonCombo_isMovementAllowed(void *context) {
return true;
}
int32_t WUPSConfigItemButtonCombo_getCurrentValueSelectedDisplay(void *context, char *out_buf, int32_t out_size) {
auto *item = (ConfigItemButtonCombo *) context;
if (item->state == BUTTON_COMBO_STATE_PREPARE_FOR_HOLD || item->state == BUTTON_COMBO_STATE_WAIT_FOR_HOLD) {
if (item->state == BUTTON_COMBO_STATE_PREPARE_FOR_HOLD) {
item->state = BUTTON_COMBO_STATE_WAIT_FOR_HOLD;
snprintf(out_buf, out_size, "<Hold new combo for %dms; hold %s to abort>", item->holdDurationInMs, getButtonChar(item->abortButton));
return 0;
} else {
checkForHold(item);
item->state = BUTTON_COMBO_STATE_NONE;
}
}
snprintf(out_buf, out_size, "(Press \ue000 to change) %s", getComboAsString(item->value).c_str());
return 0;
}
void WUPSConfigItemButtonCombo_restoreDefault(void *context) {
auto *item = (ConfigItemButtonCombo *) context;
item->value = item->defaultValue;
}
void WUPSConfigItemButtonCombo_onSelected(void *context, bool isSelected) {
}
void WUPSConfigItemButtonCombo_onDelete(void *context) {
auto *item = (ConfigItemButtonCombo *) context;
if (item->configId) {
free(item->configId);
}
free(item);
}
extern "C" bool
WUPSConfigItemButtonComboAddToCategoryEx(WUPSConfigCategoryHandle cat, const char *configID, const char *displayName, uint32_t defaultComboInVPADButtons, uint32_t holdDurationInMs, VPADButtons abortButton, uint32_t abortButtonHoldDurationInMs, ButtonComboValueChangedCallback callback) {
if (cat == 0) {
return false;
}
auto *item = (ConfigItemButtonCombo *) malloc(sizeof(ConfigItemButtonCombo));
if (item == nullptr) {
OSReport("WUPSConfigItemButtonComboAddToCategoryEx: Failed to allocate memory for item data.\n");
return false;
}
if (configID != nullptr) {
item->configId = strdup(configID);
} else {
item->configId = nullptr;
}
item->abortButton = abortButton;
item->abortButtonHoldDurationInMs = abortButtonHoldDurationInMs;
item->holdDurationInMs = holdDurationInMs;
item->defaultValue = defaultComboInVPADButtons;
item->value = defaultComboInVPADButtons;
item->callback = (void *) callback;
item->state = BUTTON_COMBO_STATE_NONE;
WUPSConfigCallbacks_t callbacks = {
.getCurrentValueDisplay = &WUPSConfigItemButtonCombo_getCurrentValueDisplay,
.getCurrentValueSelectedDisplay = &WUPSConfigItemButtonCombo_getCurrentValueSelectedDisplay,
.onSelected = &WUPSConfigItemButtonCombo_onSelected,
.restoreDefault = &WUPSConfigItemButtonCombo_restoreDefault,
.isMovementAllowed = &WUPSConfigItemButtonCombo_isMovementAllowed,
.callCallback = &WUPSConfigItemButtonCombo_callCallback,
.onButtonPressed = &WUPSConfigItemButtonCombo_onButtonPressed,
.onDelete = &WUPSConfigItemButtonCombo_onDelete};
if (WUPSConfigItem_Create(&item->handle, configID, displayName, callbacks, item) < 0) {
OSReport("WUPSConfigItemButtonComboAddToCategoryEx: Failed to create config item.\n");
free(item);
return false;
}
if (WUPSConfigCategory_AddItem(cat, item->handle) < 0) {
OSReport("WUPSConfigItemButtonComboAddToCategoryEx: Failed to add item to category.\n");
WUPSConfigItem_Destroy(item->handle);
return false;
}
return true;
}
extern "C" bool
WUPSConfigItemButtonComboAddToCategory(WUPSConfigCategoryHandle cat, const char *configID, const char *displayName, uint32_t defaultComboInVPADButtons, ButtonComboValueChangedCallback callback) {
return WUPSConfigItemButtonComboAddToCategoryEx(cat, configID, displayName, defaultComboInVPADButtons, 2000, VPAD_BUTTON_B, 250, callback);
}

View File

@ -1,43 +0,0 @@
#include <stdint.h>
#include <vpad/input.h>
#include <wups.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum ButtonComboState {
BUTTON_COMBO_STATE_NONE,
BUTTON_COMBO_STATE_PREPARE_FOR_HOLD,
BUTTON_COMBO_STATE_WAIT_FOR_HOLD,
} ButtonComboState;
typedef struct ConfigItemButtonCombo {
char *configId;
WUPSConfigItemHandle handle;
uint32_t defaultValue;
uint32_t value;
uint32_t holdDurationInMs;
VPADButtons abortButton;
uint32_t abortButtonHoldDurationInMs;
void *callback;
ButtonComboState state;
} ConfigItemButtonCombo;
typedef void (*ButtonComboValueChangedCallback)(ConfigItemButtonCombo *item, uint32_t buttonComboInVPADButtons);
bool WUPSConfigItemButtonComboAddToCategory(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, uint32_t defaultComboInVPADButtons, ButtonComboValueChangedCallback callback);
bool WUPSConfigItemButtonComboAddToCategoryEx(WUPSConfigCategoryHandle cat, const char *configId, const char *displayName, uint32_t defaultComboInVPADButtons, uint32_t holdDurationInMs, VPADButtons abortButton, uint32_t abortButtonHoldDurationInMs, ButtonComboValueChangedCallback callback);
#define WUPSConfigItemButtonCombo_AddToCategoryHandled(__config__, __cat__, __configID__, __displayName__, __defaultComboInVPADButtons__, __callback__) \
do { \
if (!WUPSConfigItemButtonComboAddToCategory(__cat__, __configID__, __displayName__, __defaultComboInVPADButtons__, __callback__)) { \
WUPSConfig_Destroy(__config__); \
return 0; \
} \
} while (0)
#ifdef __cplusplus
}
#endif

View File

@ -1,104 +1,170 @@
#include "config.h"
#include "WUPSConfigItemButtonCombo.h"
#include "logger.h"
#include "retain_vars.hpp"
#include <map>
#include <wups/config/WUPSConfigItemBoolean.h>
#include <wups/config/WUPSConfigItemButtonCombo.h>
#include <wups/config/WUPSConfigItemMultipleValues.h>
#include <wups/storage.h>
extern void UpdateAudioMode();
void boolItemChangedConfig(ConfigItemBoolean *item, bool newValue) {
if (std::string_view(item->configId) == ENABLED_CONFIG_STRING) {
DEBUG_FUNCTION_LINE("New value in %s: %d", ENABLED_CONFIG_STRING, newValue);
gEnabled = newValue;
WUPS_StoreInt(nullptr, ENABLED_CONFIG_STRING, gEnabled);
UpdateAudioMode();
static void boolItemChangedConfig(ConfigItemBoolean *item, bool newValue) {
if (!item || !item->identifier) {
DEBUG_FUNCTION_LINE_WARN("Invalid item or identifier in bool item callback");
return;
}
PROCESS_BOOL_ITEM_CHANGED(SWAP_SCREENS_CONFIG_STRING, gDoScreenSwap);
PROCESS_BOOL_ITEM_CHANGED(ENABLED_SWAP_SCREENS_COMBO_CONFIG_STRING, gSwapScreenButtonComboEnabled);
PROCESS_BOOL_ITEM_CHANGED(ENABLED_CHANGE_AUDIO_COMBO_CONFIG_STRING, gChangeAudioModeButtonComboEnabled);
PROCESS_BOOL_ITEM_CHANGED(ENABLE_NOTIFICATIONS_CONFIG_STRING, gShowNotifications);
}
void buttonComboItemChanged(ConfigItemButtonCombo *item, uint32_t newValue) {
if (item && item->configId) {
if (std::string_view(item->configId) == SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING) {
gSwapScreenButtonCombo = newValue;
WUPS_StoreInt(nullptr, item->configId, (int32_t) gSwapScreenButtonCombo);
} else if (std::string_view(item->configId) == CHANGE_AUDIO_BUTTON_COMBO_CONFIG_STRING) {
gSwapAudioButtonCombo = newValue;
WUPS_StoreInt(nullptr, item->configId, (int32_t) gSwapAudioButtonCombo);
}
DEBUG_FUNCTION_LINE_VERBOSE("New value in %s changed: %d", item->identifier, newValue);
if (std::string_view(ENABLED_CONFIG_STRING) == item->identifier) {
gEnabled = newValue;
UpdateAudioMode();
} else if (std::string_view(ENABLED_SWAP_SCREENS_COMBO_CONFIG_STRING) == item->identifier) {
gSwapScreenButtonComboEnabled = newValue;
} else if (std::string_view(ENABLED_CHANGE_AUDIO_COMBO_CONFIG_STRING) == item->identifier) {
gChangeAudioModeButtonComboEnabled = newValue;
} else if (std::string_view(ENABLED_CHANGE_SCREEN_COMBO_CONFIG_STRING) == item->identifier) {
gChangeScreenModeButtonComboEnabled = newValue;
} else if (std::string_view(ENABLE_NOTIFICATIONS_CONFIG_STRING) == item->identifier) {
gShowNotifications = newValue;
} else {
DEBUG_FUNCTION_LINE_WARN("Unexpected boolean item: %s", item->identifier);
return;
}
WUPSStorageError err;
if ((err = WUPSStorageAPI::Store(item->identifier, newValue)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to store value %d to storage item \"%s\": %s (%d)", newValue, item->identifier, WUPSStorageAPI_GetStatusStr(err), err);
}
}
void getConfigInfoForMap(std::map<SwipSwapAudioMode, const char *> &curMap, ConfigItemMultipleValuesPair *pair, uint32_t default_lang, uint32_t *default_index, uint32_t *len) {
uint32_t i = 0;
for (auto &curEntry : curMap) {
if (default_lang == curEntry.first) {
*default_index = i;
}
pair[i].value = curEntry.first;
pair[i].valueName = (char *) curEntry.second;
i++;
static void buttonComboItemChanged(ConfigItemButtonCombo *item, uint32_t newValue) {
if (!item || !item->identifier) {
DEBUG_FUNCTION_LINE_WARN("Invalid item or identifier in button combo item callback");
return;
}
DEBUG_FUNCTION_LINE_VERBOSE("New value in %s changed: %d", item->identifier, newValue);
if (std::string_view(SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING) == item->identifier) {
gSwapScreenButtonCombo = newValue;
} else if (std::string_view(CHANGE_AUDIO_BUTTON_COMBO_CONFIG_STRING) == item->identifier) {
gSwapAudioButtonCombo = newValue;
} else if (std::string_view(CHANGE_SCREEN_BUTTON_COMBO_CONFIG_STRING) == item->identifier) {
gChangeScreenButtonCombo = newValue;
} else {
DEBUG_FUNCTION_LINE_WARN("Unexpected button combo item: %s", item->identifier);
return;
}
WUPSStorageError err;
if ((err = WUPSStorageAPI::Store(item->identifier, newValue)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to store value %d to storage item \"%s\": %s (%d)", newValue, item->identifier, WUPSStorageAPI_GetStatusStr(err), err);
}
*len = i;
}
static void multiItemChanged(ConfigItemMultipleValues *item, uint32_t newValue) {
if (!item || !item->identifier) {
DEBUG_FUNCTION_LINE_WARN("Invalid item or identifier in multi item callback");
return;
}
DEBUG_FUNCTION_LINE_VERBOSE("New value in %s changed: %d", item->identifier, newValue);
void default_lang_changed(ConfigItemMultipleValues *item, uint32_t newValue) {
DEBUG_FUNCTION_LINE("New value in %s changed: %d", item->configId, newValue);
if (strcmp(item->configId, SCREEN_MODE_CONFIG_STRING) == 0) {
if (std::string_view(AUDIO_MODE_CONFIG_STRING) == item->identifier) {
gCurAudioMode = static_cast<SwipSwapAudioMode>(newValue);
WUPS_StoreInt(nullptr, item->configId, (int32_t) gCurAudioMode);
} else if (std::string_view(SCREEN_MODE_CONFIG_STRING) == item->identifier) {
gCurScreenMode = static_cast<SwipSwapScreenMode>(newValue);
} else {
DEBUG_FUNCTION_LINE_WARN("Unexpected button combo item: %s", item->identifier);
return;
}
WUPSStorageError err;
if ((err = WUPSStorageAPI::Store(item->identifier, newValue)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to store value %d to storage item \"%s\": %s (%d)", newValue, item->identifier, WUPSStorageAPI_GetStatusStr(err), err);
}
}
WUPS_GET_CONFIG() {
// We open the storage, so we can persist the configuration the user did.
if (WUPS_OpenStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to open storage");
return 0;
WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle rootHandle) {
try {
WUPSConfigCategory root = WUPSConfigCategory(rootHandle);
root.add(WUPSConfigItemBoolean::Create(ENABLED_CONFIG_STRING,
"Plugin enabled",
DEFAULT_ENABLED_CONFIG_VALUE, gEnabled,
&boolItemChangedConfig));
root.add(WUPSConfigItemBoolean::Create(ENABLE_NOTIFICATIONS_CONFIG_STRING,
"Show notifications",
DEFAULT_ENABLE_NOTIFICATIONS_CONFIG_VALUE, gShowNotifications,
&boolItemChangedConfig));
constexpr WUPSConfigItemMultipleValues::ValuePair screenModeMap[] = {
{SCREEN_MODE_NONE, "Normal"},
{SCREEN_MODE_SWAP, "Swap TV and GamePad"},
{SCREEN_MODE_MIRROR_TV, "Mirror TV"},
{SCREEN_MODE_MIRROR_DRC, "Mirror GamePad"},
};
root.add(WUPSConfigItemMultipleValues::CreateFromValue(SCREEN_MODE_CONFIG_STRING,
"Screen mode:",
DEFAULT_SCREEN_MODE_CONFIG_VALUE, gCurScreenMode,
screenModeMap,
&multiItemChanged));
constexpr WUPSConfigItemMultipleValues::ValuePair audioModeMap[] = {
{AUDIO_MODE_NONE, "Normal"},
{AUDIO_MODE_SWAP, "Swap TV and GamePad sound"},
{AUDIO_MODE_MATCH_SCREEN, "Sound matches screen"},
{AUDIO_MODE_COMBINE, "Combine TV and GamePad sound"},
{AUDIO_MODE_LEFT_TV_RIGHT_DRC, "Left: TV; Right: GamePad"}};
root.add(WUPSConfigItemMultipleValues::CreateFromValue(AUDIO_MODE_CONFIG_STRING,
"Audio mode:",
DEFAULT_AUDIO_MODE_CONFIG_VALUE, gCurAudioMode,
audioModeMap,
&multiItemChanged));
auto buttonCombos = WUPSConfigCategory::Create("Button Combos");
buttonCombos.add(WUPSConfigItemBoolean::Create(ENABLED_SWAP_SCREENS_COMBO_CONFIG_STRING,
"Enable swap screen button combo",
DEFAULT_ENABLED_SWAP_SCREENS_COMBO_CONFIG_VALUE, gSwapScreenButtonComboEnabled,
&boolItemChangedConfig));
buttonCombos.add(WUPSConfigItemButtonCombo::Create(SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING,
"Swap screen",
DEFAULT_SWAP_SCREEN_BUTTON_COMBO_CONFIG_VALUE, gSwapScreenButtonComboHandle,
&buttonComboItemChanged));
buttonCombos.add(WUPSConfigItemBoolean::Create(ENABLED_CHANGE_SCREEN_COMBO_CONFIG_STRING,
"Enable change screen button combo",
DEFAULT_ENABLED_CHANGE_SCREEN_COMBO_CONFIG_VALUE, gChangeScreenModeButtonComboEnabled,
&boolItemChangedConfig));
buttonCombos.add(WUPSConfigItemButtonCombo::Create(CHANGE_SCREEN_BUTTON_COMBO_CONFIG_STRING,
"Change screen",
DEFAULT_CHANGE_SCREEN_BUTTON_COMBO_CONFIG_VALUE, gChangeScreenButtonComboHandle,
&buttonComboItemChanged));
buttonCombos.add(WUPSConfigItemBoolean::Create(ENABLED_CHANGE_AUDIO_COMBO_CONFIG_STRING,
"Enable change audio mode button combo",
DEFAULT_ENABLED_CHANGE_AUDIO_COMBO_CONFIG_VALUE, gChangeAudioModeButtonComboEnabled,
&boolItemChangedConfig));
buttonCombos.add(WUPSConfigItemButtonCombo::Create(CHANGE_AUDIO_BUTTON_COMBO_CONFIG_STRING,
"Change audio",
DEFAULT_CHANGE_AUDIO_BUTTON_COMBO_CONFIG_VALUE, gSwapAudioButtonComboHandle,
&buttonComboItemChanged));
root.add(std::move(buttonCombos));
} catch (std::exception &e) {
OSReport("Exception: %s\n", e.what());
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
}
WUPSConfigHandle config;
WUPSConfig_CreateHandled(&config, "SwipSwapMe");
WUPSConfigCategoryHandle setting;
WUPSConfig_AddCategoryByNameHandled(config, "Settings", &setting);
WUPSConfigCategoryHandle combos;
WUPSConfig_AddCategoryByNameHandled(config, "Button combos", &combos);
std::map<SwipSwapAudioMode, const char *> audioModeMap{
{AUDIO_MODE_NONE, "Normal"},
{AUDIO_MODE_SWAP, "Swap TV and GamePad sound"},
{AUDIO_MODE_MATCH_SCREEN, "Sound matches screen"},
{AUDIO_MODE_COMBINE, "Combine TV and GamePad sound"},
{AUDIO_MODE_LEFT_TV_RIGHT_DRC, "Left: TV; Right: GamePad"}};
ConfigItemMultipleValuesPair audioModePairs[audioModeMap.size()];
uint32_t number_values = 0;
uint32_t default_index = 0;
getConfigInfoForMap(audioModeMap, audioModePairs, gCurAudioMode, &default_index, &number_values);
WUPSConfigItemBoolean_AddToCategoryHandled(config, setting, ENABLED_CONFIG_STRING, "Plugin enabled", gEnabled, &boolItemChangedConfig);
WUPSConfigItemBoolean_AddToCategoryHandled(config, setting, ENABLE_NOTIFICATIONS_CONFIG_STRING, "Show notifications", gShowNotifications, &boolItemChangedConfig);
WUPSConfigItemBoolean_AddToCategoryHandled(config, setting, SWAP_SCREENS_CONFIG_STRING, "Swap screens", gDoScreenSwap, &boolItemChangedConfig);
WUPSConfigItemMultipleValues_AddToCategoryHandled(config, setting, SCREEN_MODE_CONFIG_STRING, "Audio mode:", default_index, audioModePairs, number_values,
&default_lang_changed);
WUPSConfigItemBoolean_AddToCategoryHandled(config, combos, ENABLED_SWAP_SCREENS_COMBO_CONFIG_STRING, "Enable swap screen button combo", gSwapScreenButtonComboEnabled, &boolItemChangedConfig);
WUPSConfigItemButtonCombo_AddToCategoryHandled(config, combos, SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING, "Swap screen", gSwapScreenButtonCombo, &buttonComboItemChanged);
WUPSConfigItemBoolean_AddToCategoryHandled(config, combos, ENABLED_CHANGE_AUDIO_COMBO_CONFIG_STRING, "Enable change audio mode button combo", gChangeAudioModeButtonComboEnabled, &boolItemChangedConfig);
WUPSConfigItemButtonCombo_AddToCategoryHandled(config, combos, CHANGE_AUDIO_BUTTON_COMBO_CONFIG_STRING, "Change audio", gSwapAudioButtonCombo, &buttonComboItemChanged);
return config;
return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS;
}
WUPS_CONFIG_CLOSED() {
void ConfigMenuClosedCallback() {
// Save all changes
if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to close storage");
WUPSStorageError err;
if ((err = WUPSStorageAPI::SaveStorage()) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to close storage: %s (%d)", WUPSStorageAPI_GetStatusStr(err), err);
}
}

View File

@ -1,53 +1,36 @@
#pragma once
#include "logger.h"
#include <string>
#include <wups/config/WUPSConfigItemBoolean.h>
#include <wups/storage.h>
#define ENABLED_CONFIG_STRING "enabled"
#define SWAP_SCREENS_CONFIG_STRING "swapScreens"
#define ENABLED_SWAP_SCREENS_COMBO_CONFIG_STRING "swapScreensComboEnabled"
#define ENABLED_CHANGE_AUDIO_COMBO_CONFIG_STRING "changeAudioComboEnabled"
#define ENABLE_NOTIFICATIONS_CONFIG_STRING "notificationsEnabled"
#define SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING "screenButtonCombo"
#define CHANGE_AUDIO_BUTTON_COMBO_CONFIG_STRING "audioButtonCombo"
#define SCREEN_MODE_CONFIG_STRING "audioMode"
#include <wups/button_combo/defines.h>
#include <wups/config.h>
#define LOAD_BOOL_FROM_STORAGE(config_name, __variable__) \
if ((storageRes = WUPS_GetBool(nullptr, config_name, &__variable__)) == WUPS_STORAGE_ERROR_NOT_FOUND) { \
if (WUPS_StoreBool(nullptr, config_name, __variable__) != WUPS_STORAGE_ERROR_SUCCESS) { \
DEBUG_FUNCTION_LINE_WARN("Failed to store bool"); \
} \
} else if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) { \
DEBUG_FUNCTION_LINE_WARN("Failed to get bool %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes); \
} \
while (0)
#define DEFAULT_ENABLED_CONFIG_VALUE true
#define DEFAULT_ENABLED_SWAP_SCREENS_COMBO_CONFIG_VALUE true
#define DEFAULT_ENABLED_CHANGE_SCREEN_COMBO_CONFIG_VALUE false
#define DEFAULT_ENABLED_CHANGE_AUDIO_COMBO_CONFIG_VALUE false
#define DEFAULT_ENABLE_NOTIFICATIONS_CONFIG_VALUE true
#define DEFAULT_SWAP_SCREEN_BUTTON_COMBO_CONFIG_VALUE ((WUPSButtonCombo_Buttons) (WUPS_BUTTON_COMBO_BUTTON_TV | WUPS_BUTTON_COMBO_BUTTON_RIGHT))
#define DEFAULT_CHANGE_AUDIO_BUTTON_COMBO_CONFIG_VALUE ((WUPSButtonCombo_Buttons) (WUPS_BUTTON_COMBO_BUTTON_TV | WUPS_BUTTON_COMBO_BUTTON_STICK_L))
#define DEFAULT_CHANGE_SCREEN_BUTTON_COMBO_CONFIG_VALUE ((WUPSButtonCombo_Buttons) (WUPS_BUTTON_COMBO_BUTTON_TV | WUPS_BUTTON_COMBO_BUTTON_STICK_R))
#define LOAD_STRING_FROM_STORAGE(config_name, __string, __string_length) \
if ((storageRes = WUPS_GetString(nullptr, config_name, __string, __string_length)) == WUPS_STORAGE_ERROR_NOT_FOUND) { \
if (WUPS_StoreString(nullptr, config_name, __string) != WUPS_STORAGE_ERROR_SUCCESS) { \
DEBUG_FUNCTION_LINE_WARN("Failed to store string"); \
} \
} else if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) { \
DEBUG_FUNCTION_LINE_WARN("Failed to get bool %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes); \
} \
while (0)
#define DEFAULT_SCREEN_MODE_CONFIG_VALUE SCREEN_MODE_NONE
#define DEFAULT_AUDIO_MODE_CONFIG_VALUE AUDIO_MODE_MATCH_SCREEN
#define LOAD_INT_FROM_STORAGE(config_name, __intValue) \
if ((storageRes = WUPS_GetInt(nullptr, config_name, (int32_t *) &__intValue)) == WUPS_STORAGE_ERROR_NOT_FOUND) { \
if (WUPS_StoreInt(nullptr, config_name, (int32_t) __intValue) != WUPS_STORAGE_ERROR_SUCCESS) { \
DEBUG_FUNCTION_LINE_WARN("Failed to store int"); \
} \
} else if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) { \
DEBUG_FUNCTION_LINE_WARN("Failed to get int %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes); \
} \
while (0)
#define ENABLED_CONFIG_STRING "enabled"
#define SWAP_SCREENS_CONFIG_STRING_DEPRECATED "swapScreens"
#define SCREEN_MODE_CONFIG_STRING "screenMode"
#define ENABLED_SWAP_SCREENS_COMBO_CONFIG_STRING "swapScreensComboEnabled"
#define ENABLED_CHANGE_SCREEN_COMBO_CONFIG_STRING "changeScreenComboEnabled"
#define ENABLED_CHANGE_AUDIO_COMBO_CONFIG_STRING "changeAudioComboEnabled"
#define ENABLE_NOTIFICATIONS_CONFIG_STRING "notificationsEnabled"
#define SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING_DEPRECATED "screenButtonCombo"
#define CHANGE_SCREEN_BUTTON_COMBO_CONFIG_STRING_DEPRECATED "screenChangeButtonCombo"
#define CHANGE_AUDIO_BUTTON_COMBO_CONFIG_STRING_DEPRECATED "audioButtonCombo"
#define SWAP_SCREEN_BUTTON_COMBO_CONFIG_STRING "screenButtonComboNew"
#define CHANGE_SCREEN_BUTTON_COMBO_CONFIG_STRING "screenChangeButtonComboNew"
#define CHANGE_AUDIO_BUTTON_COMBO_CONFIG_STRING "audioButtonComboNew"
#define AUDIO_MODE_CONFIG_STRING "audioMode"
#define PROCESS_BOOL_ITEM_CHANGED(__config__name, __variable__) \
if (std::string_view(item->configId) == __config__name) { \
DEBUG_FUNCTION_LINE_ERR("New value in %s: %d", __config__name, newValue); \
__variable__ = newValue; \
WUPS_StoreInt(nullptr, __config__name, __variable__); \
return; \
} \
while (0)
WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle rootHandle);
void ConfigMenuClosedCallback();

View File

@ -39,6 +39,7 @@ extern "C" {
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##WARN ## ", "", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##INFO ## ", "", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS);
@ -54,6 +55,7 @@ extern "C" {
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARN ## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##INFO ## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS);