Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM ghcr.io/wiiu-env/devkitppc:20241128

COPY --from=ghcr.io/wiiu-env/libbuttoncombo:20241226-12d1594 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libbuttoncombo:20241231-a2f949b /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20241012 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20240424 /artifacts $DEVKITPRO

Expand Down
11 changes: 8 additions & 3 deletions source/ButtonComboInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,27 @@ bool ButtonComboInfoIF::getMetaOptions(const ButtonComboModule_MetaOptionsOut &o
return false;
}

void ButtonComboInfoIF::setMetaOptions(ButtonComboModule_MetaOptions options) {
void ButtonComboInfoIF::setMetaOptions(const ButtonComboModule_MetaOptions options) {
mLabel = options.label;
DEBUG_FUNCTION_LINE("Updated label to: \"%s\", for %08X", mLabel.c_str(), getHandle().handle);
}

ButtonComboModule_CallbackOptions ButtonComboInfoIF::getCallbackOptions() const {
return {.callback = mCallback, .context = mContext};
}

void ButtonComboInfoIF::setCallbackOptions(ButtonComboModule_CallbackOptions options) {
void ButtonComboInfoIF::setCallbackOptions(const ButtonComboModule_CallbackOptions options) {
mCallback = options.callback;
mContext = options.context;
DEBUG_FUNCTION_LINE("Updated callback to: %08X(%08X), for %s %08X", mCallback, mContext, mLabel.c_str(), getHandle().handle);
}

uint32_t ButtonComboInfoIF::getCombo() const {
return mCombo;
}
void ButtonComboInfoIF::setCombo(const ButtonComboModule_Buttons combo) {
mCombo = combo;
DEBUG_FUNCTION_LINE("Updated combo to: %08X, for %s %08X", mCombo, mLabel.c_str(), getHandle().handle);
}

ButtonComboModule_ComboStatus ButtonComboInfoIF::getStatus() const {
Expand All @@ -66,14 +69,16 @@ ButtonComboModule_ComboStatus ButtonComboInfoIF::getStatus() const {

void ButtonComboInfoIF::setStatus(const ButtonComboModule_ComboStatus status) {
mStatus = status;
DEBUG_FUNCTION_LINE("Updated status to: %08X, for %s %08X", mStatus, mLabel.c_str(), getHandle().handle);
}

ButtonComboModule_ControllerTypes ButtonComboInfoIF::getControllerMask() const {
return mControllerMask;
}

void ButtonComboInfoIF::setControllerMask(ButtonComboModule_ControllerTypes mask) {
void ButtonComboInfoIF::setControllerMask(const ButtonComboModule_ControllerTypes mask) {
mControllerMask = mask;
DEBUG_FUNCTION_LINE("Updated controllerMask to: %08X, for %s %08X", mControllerMask, mLabel.c_str(), getHandle().handle);
}

bool ButtonComboInfoIF::conflictsWith(const ButtonComboModule_ButtonComboOptions &other) const {
Expand Down
17 changes: 13 additions & 4 deletions source/ButtonComboInfoDown.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ ButtonComboInfoDown::ButtonComboInfoDown(
const ButtonComboModule_ComboCallback callback,
void *context,
const bool observer) : ButtonComboInfoIF(std::move(label), controllerMask, combo, callback, context, observer) {
DEBUG_FUNCTION_LINE_INFO("Created ButtonComboInfoDown: \"%s\", combo: %08X, controllerMask: %08X. Observer %d", mLabel.c_str(), mCombo, mControllerMask, observer);
}

ButtonComboInfoDown::~ButtonComboInfoDown() {
DEBUG_FUNCTION_LINE_INFO("Deleted ButtonComboInfoDown: \"%s\", combo: %08X, controllerMask: %08X. Observer %d", mLabel.c_str(), mCombo, mControllerMask, mIsObserver);
}

void ButtonComboInfoDown::UpdateInput(
Expand All @@ -18,14 +23,17 @@ void ButtonComboInfoDown::UpdateInput(
return;
}

DEBUG_FUNCTION_LINE_VERBOSE("[PRESS DOWN] Check button combo %08X on controller %08X (lastItem im pressedButtons (size %d) is %08X) for %s [%08X]", mCombo, controller, pressedButtons.size(), pressedButtons.back(), mLabel.c_str(), getHandle().handle);

for (const auto &pressedButton : pressedButtons) {
const bool prevButtonsIncludedCombo = (mPrevButtonPress & mCombo) == mCombo; // Make sure the combo can't be triggered on releasing
const bool buttonsPressedChanged = mPrevButtonPress != pressedButton; // Avoid "holding" the combo
const bool buttonsPressedMatchCombo = pressedButton == mCombo; // detect the actual combo

if (buttonsPressedChanged && buttonsPressedMatchCombo && !prevButtonsIncludedCombo) {
if (mCallback != nullptr) {
mCallback(getHandle(), mContext);
DEBUG_FUNCTION_LINE("Calling callback [%08X](controller: %08X, context: %08X) for \"%s\" [handle: %08X], pressed down %08X", mCallback, controller, mContext, mLabel.c_str(), getHandle().handle, mCombo);
mCallback(controller, getHandle(), mContext);
} else {
DEBUG_FUNCTION_LINE_WARN("Callback was null for combo %08X", getHandle());
}
Expand All @@ -37,8 +45,9 @@ void ButtonComboInfoDown::UpdateInput(
ButtonComboModule_Error ButtonComboInfoDown::setHoldDuration(uint32_t) {
return BUTTON_COMBO_MODULE_ERROR_SUCCESS;
}

ButtonComboModule_ButtonComboInfoEx ButtonComboInfoDown::getComboInfoEx() const {
return {.type = mIsObserver ? BUTTON_COMBO_MODULE_TYPE_PRESS_DOWN_OBSERVER : BUTTON_COMBO_MODULE_TYPE_PRESS_DOWN,
.basicCombo = {.controllerMask = mControllerMask, .combo = mCombo},
.optionalHoldForXFrames = 0};
return {.type = mIsObserver ? BUTTON_COMBO_MODULE_TYPE_PRESS_DOWN_OBSERVER : BUTTON_COMBO_MODULE_TYPE_PRESS_DOWN,
.basicCombo = {.controllerMask = mControllerMask, .combo = mCombo},
.optionalHoldForXMs = 0};
}
1 change: 1 addition & 0 deletions source/ButtonComboInfoDown.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class ButtonComboInfoDown final : public ButtonComboInfoIF {
ButtonComboModule_ComboCallback callback,
void *context,
bool observer);
~ButtonComboInfoDown() override;

private:
void UpdateInput(ButtonComboModule_ControllerTypes controller, std::span<uint32_t> pressedButtons) override;
Expand Down
41 changes: 27 additions & 14 deletions source/ButtonComboInfoHold.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ namespace {
ButtonComboInfoHold::ButtonComboInfoHold(std::string label,
const ButtonComboModule_ControllerTypes controllerMask,
const ButtonComboModule_Buttons combo,
const uint32_t targetDuration,
const uint32_t targetDurationInMs,
const ButtonComboModule_ComboCallback callback,
void *context,
const bool observer) : ButtonComboInfoIF(std::move(label),
Expand All @@ -52,33 +52,45 @@ ButtonComboInfoHold::ButtonComboInfoHold(std::string label,
callback,
context,
observer),
mTargetDurationInFrames(targetDuration) {
mTargetDurationInMs(targetDurationInMs) {
DEBUG_FUNCTION_LINE_INFO("Created ButtonComboInfoDown: \"%s\", combo: %08X, targetDurationInMs: %d ms, controllerMask: %08X", mLabel.c_str(), mCombo, mTargetDurationInMs, mControllerMask);
}

void ButtonComboInfoHold::UpdateInput(const ButtonComboModule_ControllerTypes controller, std::span<uint32_t> pressedButtons) {
ButtonComboInfoHold::~ButtonComboInfoHold() {
DEBUG_FUNCTION_LINE_INFO("Deleted ButtonComboInfoDown: \"%s\", combo: %08X, targetDurationInMs: %d ms, controllerMask: %08X", mLabel.c_str(), mCombo, mTargetDurationInMs, mControllerMask);
}

void ButtonComboInfoHold::UpdateInput(const ButtonComboModule_ControllerTypes controller, const std::span<uint32_t> pressedButtons) {
if ((mControllerMask & controller) == 0) {
return;
}
const auto chanIndex = controllerTypeToChanIndex(controller);
if (chanIndex < 0 || static_cast<uint32_t>(chanIndex) >= pressedButtons.size()) {
if (chanIndex < 0 || static_cast<uint32_t>(chanIndex) >= std::size(mHoldInformation)) {
DEBUG_FUNCTION_LINE_WARN("ChanIndex is out of bounds %d", chanIndex);
return;
}

auto &holdInformation = mHoldInformation[chanIndex];
const auto latestButtonPress = pressedButtons.back();

DEBUG_FUNCTION_LINE_VERBOSE("[HOLD ] Check button combo %08X on controller %08X (lastItem im pressedButtons (size %d) is %08X) for %s [%08X]", mCombo, controller, pressedButtons.size(), latestButtonPress, mLabel.c_str(), getHandle().handle);

const bool prevButtonsIncludedCombo = (holdInformation.prevButtonCombo & mCombo) == mCombo; // Make sure the combo can't be triggered on releasing
const bool buttonsPressedChanged = holdInformation.prevButtonCombo != latestButtonPress; // Avoid "holding" the combo
const bool buttonsPressedMatchCombo = latestButtonPress == mCombo; // detect the actual combo

holdInformation.prevButtonCombo = latestButtonPress;

if (!(buttonsPressedChanged && prevButtonsIncludedCombo) && (holdInformation.durationInFrames > 0 || buttonsPressedChanged) && buttonsPressedMatchCombo) {
holdInformation.durationInFrames++;
if (holdInformation.durationInFrames > mTargetDurationInFrames && !holdInformation.callbackTriggered) {
if (!(buttonsPressedChanged && prevButtonsIncludedCombo) && (holdInformation.holdStartedAt > 0 || buttonsPressedChanged) && buttonsPressedMatchCombo) {
if (holdInformation.holdStartedAt == 0) {
holdInformation.holdStartedAt = OSGetTime();
}
const auto intervalInMs = static_cast<uint32_t>(OSTicksToMilliseconds(OSGetTime() - holdInformation.holdStartedAt));

if (intervalInMs > mTargetDurationInMs && !holdInformation.callbackTriggered) {
if (mCallback != nullptr) {
mCallback(getHandle(), mContext);
DEBUG_FUNCTION_LINE("Calling callback [%08X](controller: %08X context: %08X) for \"%s\" [handle: %08X], hold %08X for %d ms", mCallback, controller, mContext, mLabel.c_str(), getHandle().handle, mCombo, intervalInMs);
mCallback(controller, getHandle(), mContext);

} else {
DEBUG_FUNCTION_LINE_WARN("Callback was null for combo %08X", getHandle());
Expand All @@ -87,17 +99,18 @@ void ButtonComboInfoHold::UpdateInput(const ButtonComboModule_ControllerTypes co
}
} else {
holdInformation.callbackTriggered = false;
holdInformation.durationInFrames = 0;
holdInformation.holdStartedAt = 0;
}
}

ButtonComboModule_Error ButtonComboInfoHold::setHoldDuration(const uint32_t holdDurationInFrames) {
mTargetDurationInFrames = holdDurationInFrames;
ButtonComboModule_Error ButtonComboInfoHold::setHoldDuration(const uint32_t holdDurationInMs) {
DEBUG_FUNCTION_LINE("Setting holdDurationInMs to %d for %s [%08X]", holdDurationInMs, mLabel.c_str(), getHandle().handle);
mTargetDurationInMs = holdDurationInMs;
return BUTTON_COMBO_MODULE_ERROR_SUCCESS;
}

ButtonComboModule_ButtonComboInfoEx ButtonComboInfoHold::getComboInfoEx() const {
return {.type = mIsObserver ? BUTTON_COMBO_MODULE_TYPE_HOLD_OBSERVER : BUTTON_COMBO_MODULE_TYPE_HOLD,
.basicCombo = {.controllerMask = mControllerMask, .combo = mCombo},
.optionalHoldForXFrames = 0};
return {.type = mIsObserver ? BUTTON_COMBO_MODULE_TYPE_HOLD_OBSERVER : BUTTON_COMBO_MODULE_TYPE_HOLD,
.basicCombo = {.controllerMask = mControllerMask, .combo = mCombo},
.optionalHoldForXMs = mTargetDurationInMs};
}
12 changes: 8 additions & 4 deletions source/ButtonComboInfoHold.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <span>
#include <string>

#include <coreinit/time.h>
#include <cstdint>

class ButtonComboInfoHold final : public ButtonComboInfoIF {
Expand All @@ -16,24 +17,27 @@ class ButtonComboInfoHold final : public ButtonComboInfoIF {
ButtonComboInfoHold(std::string label,
ButtonComboModule_ControllerTypes controllerMask,
ButtonComboModule_Buttons combo,
uint32_t targetDuration,
uint32_t targetDurationInMs,
ButtonComboModule_ComboCallback callback,
void *context,
bool observer);

~ButtonComboInfoHold() override;

private:
void UpdateInput(ButtonComboModule_ControllerTypes controller, std::span<uint32_t> pressedButtons) override;

ButtonComboModule_Error setHoldDuration(uint32_t holdDurationInFrames) override;
ButtonComboModule_Error setHoldDuration(uint32_t holdDurationInMs) override;

[[nodiscard]] ButtonComboModule_ButtonComboInfoEx getComboInfoEx() const override;

private:
typedef struct {
uint32_t durationInFrames;
OSTime holdStartedAt;
uint32_t prevButtonCombo;
bool callbackTriggered;
} HoldInformation;

uint32_t mTargetDurationInFrames = {};
uint32_t mTargetDurationInMs = {};
HoldInformation mHoldInformation[9] = {}; // one for each controller
};
40 changes: 23 additions & 17 deletions source/ButtonComboManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@
#include <vector>

namespace {
template<typename T, class Allocator, class Predicate>
bool remove_first_if(std::vector<T, Allocator> &list, Predicate pred) {
auto it = list.begin();
while (it != list.end()) {
if (pred(*it)) {
list.erase(it);
template<typename Container, typename Predicate>
std::enable_if_t<std::is_same_v<Container, std::forward_list<typename Container::value_type>>, bool>
remove_first_if(Container &container, Predicate pred) {
auto it = container.before_begin();

for (auto prev = it, current = ++it; current != container.end(); ++prev, ++current) {
if (pred(*current)) {
container.erase_after(prev);
return true;
}
++it;
}

return false;
Expand Down Expand Up @@ -283,16 +284,15 @@ std::optional<std::shared_ptr<ButtonComboInfoIF>> ButtonComboManager::CreateComb
observer = true;
__attribute__((fallthrough));
case BUTTON_COMBO_MODULE_TYPE_HOLD: {

if (options.buttonComboOptions.optionalHoldForXFrames == 0) {
if (options.buttonComboOptions.optionalHoldForXMs == 0) {
err = BUTTON_COMBO_MODULE_ERROR_DURATION_MISSING;
return std::nullopt;
}
err = BUTTON_COMBO_MODULE_ERROR_SUCCESS;
return std::make_shared<ButtonComboInfoHold>(options.metaOptions.label,
options.buttonComboOptions.basicCombo.controllerMask,
options.buttonComboOptions.basicCombo.combo,
options.buttonComboOptions.optionalHoldForXFrames,
options.buttonComboOptions.optionalHoldForXMs,
options.callbackOptions.callback,
options.callbackOptions.context,
observer);
Expand Down Expand Up @@ -345,7 +345,7 @@ void ButtonComboManager::AddCombo(std::shared_ptr<ButtonComboInfoIF> newComboInf
outStatus = CheckComboStatus(*newComboInfo);
newComboInfo->setStatus(outStatus);
outHandle = newComboInfo->getHandle();
mCombos.push_back(std::move(newComboInfo));
mCombos.emplace_front(std::move(newComboInfo));

const auto block = hasActiveComboWithTVButton();
VPADSetTVMenuInvalid(VPAD_CHAN_0, block);
Expand All @@ -358,6 +358,7 @@ ButtonComboModule_Error ButtonComboManager::RemoveCombo(ButtonComboModule_ComboH
DEBUG_FUNCTION_LINE_WARN("Failed to remove combo by handle %08X", handle);
} else {
const auto block = hasActiveComboWithTVButton();

VPADSetTVMenuInvalid(VPAD_CHAN_0, block);
VPADSetTVMenuInvalid(VPAD_CHAN_1, block);
}
Expand Down Expand Up @@ -415,8 +416,12 @@ void ButtonComboManager::UpdateInputVPAD(const VPADChan chan, const VPADStatus *
}

void ButtonComboManager::UpdateInputWPAD(const WPADChan chan, WPADStatus *data) {
if (chan < WPAD_CHAN_0 || chan > WPAD_CHAN_6 || !data || data->error || data->extensionType == 0xFF) {
DEBUG_FUNCTION_LINE_ERR("Invalid WPADChan or data state");
if (chan < WPAD_CHAN_0 || chan > WPAD_CHAN_6) {
DEBUG_FUNCTION_LINE_WARN("Invalid WPADChan %d", chan);
return;
}
if (!data || data->error || data->extensionType == 0xFF) {
DEBUG_FUNCTION_LINE_VERBOSE("Invalid data or state");
return;
}
const auto controller = convert(chan);
Expand Down Expand Up @@ -454,7 +459,7 @@ void ButtonComboManager::UpdateInputWPAD(const WPADChan chan, WPADStatus *data)
}
}

ButtonComboInfoIF *ButtonComboManager::GetComboInfoForHandle(const ButtonComboModule_ComboHandle handle) {
ButtonComboInfoIF *ButtonComboManager::GetComboInfoForHandle(const ButtonComboModule_ComboHandle handle) const {
for (const auto &combo : mCombos) {
if (combo->getHandle() == handle) {
return combo.get();
Expand Down Expand Up @@ -499,7 +504,6 @@ ButtonComboModule_Error ButtonComboManager::UpdateControllerMask(const ButtonCom
comboInfo->setControllerMask(controllerMask);

// check if we have a conflict.
comboInfo->setControllerMask(controllerMask);
comboInfo->setStatus(CheckComboStatus(*comboInfo));
outComboStatus = comboInfo->getStatus();

Expand Down Expand Up @@ -554,7 +558,7 @@ ButtonComboModule_Error ButtonComboManager::GetButtonComboMeta(const ButtonCombo
ButtonComboModule_Error ButtonComboManager::GetButtonComboCallback(const ButtonComboModule_ComboHandle handle,
ButtonComboModule_CallbackOptions &outOptions) {
std::lock_guard lock(mMutex);
auto *comboInfo = GetComboInfoForHandle(handle);
const auto *comboInfo = GetComboInfoForHandle(handle);
if (!comboInfo) {
return BUTTON_COMBO_MODULE_ERROR_INVALID_ARGUMENT;
}
Expand All @@ -566,7 +570,7 @@ ButtonComboModule_Error ButtonComboManager::GetButtonComboCallback(const ButtonC
ButtonComboModule_Error ButtonComboManager::GetButtonComboInfoEx(const ButtonComboModule_ComboHandle handle,
ButtonComboModule_ButtonComboInfoEx &outOptions) {
std::lock_guard lock(mMutex);
auto *comboInfo = GetComboInfoForHandle(handle);
const auto *comboInfo = GetComboInfoForHandle(handle);
if (!comboInfo) {
DEBUG_FUNCTION_LINE_ERR("ButtonComboModule_GetButtonComboInfo failed to get manager for handle %08X", handle);
return BUTTON_COMBO_MODULE_ERROR_INVALID_ARGUMENT;
Expand Down Expand Up @@ -650,10 +654,12 @@ ButtonComboModule_Error ButtonComboManager::DetectButtonCombo_Blocking(const But
lastHold = buttonsHold;

if (holdFor >= holdAbortTarget && lastHold == abortButton) {
DEBUG_FUNCTION_LINE("Aborted button combo detection");
return BUTTON_COMBO_MODULE_ERROR_ABORTED;
}

if (holdFor >= holdForTarget) {
DEBUG_FUNCTION_LINE_INFO("Detected button combo %08X", lastHold);
outButtonCombo = static_cast<ButtonComboModule_Buttons>(lastHold);
break;
}
Expand Down
Loading