/*
 *
 * Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <cstdio>
#include <cstring>
#include <vector>

#include "../format.h"
#include "config_info_strings.h"
#include "fatal_gui.h"
#include "labels.h"
#include "misc_gui.h"
#include "ult_ext.h"
#include "main_gui.h"

// This workaround *may* not be nessasary, but it seems to help with reducing stutter
static void kipDataThreadFunc(void *) {
    hocClkIpcSetKipData();
}

static Thread s_kipThread;
static bool s_kipThreadPending = false;

static void sendKipData() {
    if (s_kipThreadPending) {
        threadWaitForExit(&s_kipThread);
        threadClose(&s_kipThread);
        s_kipThreadPending = false;
    }
    if (R_SUCCEEDED(threadCreate(&s_kipThread, kipDataThreadFunc, nullptr, nullptr, 0x1000, 0x2C, -2))) {
        threadStart(&s_kipThread);
        s_kipThreadPending = true;
    }
}
#if IS_MINIMAL == 1
    #pragma message("Compiling with minimal features")
#endif

#define A_BTN "\ue0e0"
#define R_ARROW "\u2192"

class GeneralSettingsSubMenuGui;
class GovernorSettingsSubMenuGui;
class DisplaySubMenuGui;
class SafetySubMenuGui;
class RamSubmenuGui;
class RamTimingsSubmenuGui;
class RamLatenciesSubmenuGui;
class SocCustomTableSubmenuGui;
class CpuSubmenuGui;
class GpuSubmenuGui;
class GpuCustomTableSubmenuGui;
class ExperimentalSettingsSubMenuGui;

MiscGui::MiscGui() {
    this->configList = new HocClkConfigValueList{};
}

MiscGui::~MiscGui() {
    if (shouldSaveKip) {
        sendKipData();
        shouldSaveKip = false;
    }
    if (s_kipThreadPending) {
        threadWaitForExit(&s_kipThread);
        threadClose(&s_kipThread);
        s_kipThreadPending = false;
    }
    delete this->configList;
    this->configToggles.clear();
    this->configTrackbars.clear();
    this->configButtons.clear();
    this->configRanges.clear();
}

void MiscGui::addConfigToggle(HocClkConfigValue configVal, const char *altName, bool kip) {
    const char *configName = altName ? altName : hocclkFormatConfigValue(configVal, true);
    auto infoStrings = ConfigInfoStrings(configVal, IsMariko(), IsHoag());

    struct YAwareToggle : tsl::elm::ToggleListItem {
        std::vector<std::string> m_info;
        std::string m_title;
        YAwareToggle(const char *text, bool state, std::string title, std::vector<std::string> info)
            : tsl::elm::ToggleListItem(text, state), m_info(std::move(info)), m_title(std::move(title)) {
        }
        bool onClick(u64 keys) override {
            if (!m_info.empty() && (keys & HidNpadButton_Y) && !(keys & ~HidNpadButton_Y)) {
                tsl::changeTo<InfoGui>(m_title, m_info);
                return true;
            }
            return tsl::elm::ToggleListItem::onClick(keys);
        }
    };

    auto *toggle = new YAwareToggle(configName, this->configList->values[configVal], configName, std::move(infoStrings));
    if (!kip)
        toggle->setTextColor(tsl::Color(120, 235, 255, 255));
    toggle->setStateChangedListener([this, configVal, kip](bool state) {
        this->configList->values[configVal] = uint64_t(state);
        Result rc = hocclkIpcSetConfigValues(this->configList);
        if (R_FAILED(rc)) {
            FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
        } else if (kip) {
            shouldSaveKip = true;
        }
        this->lastContextUpdate = armGetSystemTick();
    });
    this->listElement->addItem(toggle);
    this->configToggles[configVal] = toggle;
}

void MiscGui::addConfigTrackbar(HocClkConfigValue configVal, const char *altName, const ValueRange &range, bool kip) {
    auto infoStrings = ConfigInfoStrings(configVal, IsMariko(), IsHoag());
    struct IndexedBar : tsl::elm::NamedStepTrackBar {
        std::vector<std::string> m_info;
        std::string m_title;
        IndexedBar(const char *label, const ValueRange &r, std::string title, std::vector<std::string> info)
            : tsl::elm::NamedStepTrackBar("", { "" }, true, label), m_info(std::move(info)), m_title(std::move(title)) {
            m_stepDescriptions.clear();
            u32 numSteps = (r.max - r.min) / r.step + 1;
            for (u32 i = 0; i < numSteps; i++) {
                u32 disp = (r.min + i * r.step) / r.divisor;
                std::string s = std::to_string(disp);
                if (!r.suffix.empty())
                    s += " " + r.suffix;
                m_stepDescriptions.push_back(s);
            }
            m_numSteps = (u8)m_stepDescriptions.size();
            m_selection = m_stepDescriptions[0];
        }
        bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                         HidAnalogStickState rightJoyStick) override {
            if (!m_info.empty() && (keysDown & HidNpadButton_Y) && !(keysDown & ~HidNpadButton_Y)) {
                tsl::changeTo<InfoGui>(m_title, m_info);
                return true;
            }
            return tsl::elm::NamedStepTrackBar::handleInput(keysDown, keysHeld, touchPos, leftJoyStick, rightJoyStick);
        }
    };
    const char *name = altName ? altName : hocclkFormatConfigValue(configVal, true);
    auto *bar = new IndexedBar(name, range, name, std::move(infoStrings));
    u32 cur = (u32)this->configList->values[configVal];
    u16 curStep = 0;
    if (cur >= range.min && cur <= range.max && range.step > 0 && (cur - range.min) % range.step == 0)
        curStep = (u16)((cur - range.min) / range.step);
    bar->setProgress(curStep);
    bar->setValueChangedListener([this, configVal, kip, range](u16 v) {
        this->configList->values[configVal] = range.min + (u32)v * range.step;
        Result rc = hocclkIpcSetConfigValues(this->configList);
        if (R_FAILED(rc))
            FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
        if (kip)
            shouldSaveKip = true;
    });
    this->listElement->addItem(bar);
}

void MiscGui::addMappedConfigTrackbar(HocClkConfigValue configVal, const char *altName, std::vector<u32> vals,
                                      std::initializer_list<std::string> names, bool kip) {
    const char *name = altName ? altName : hocclkFormatConfigValue(configVal, true);
    auto infoStrings = ConfigInfoStrings(configVal, IsMariko(), IsHoag());

    struct YAwareTrackBar : tsl::elm::NamedStepTrackBar {
        std::vector<std::string> m_info;
        std::string m_title;
        YAwareTrackBar(const char *label, std::initializer_list<std::string> steps, std::string title, std::vector<std::string> info)
            : tsl::elm::NamedStepTrackBar("", steps, true, label), m_info(std::move(info)), m_title(std::move(title)) {
        }
        bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                         HidAnalogStickState rightJoyStick) override {
            if (!m_info.empty() && (keysDown & HidNpadButton_Y) && !(keysDown & ~HidNpadButton_Y)) {
                tsl::changeTo<InfoGui>(m_title, m_info);
                return true;
            }
            return tsl::elm::NamedStepTrackBar::handleInput(keysDown, keysHeld, touchPos, leftJoyStick, rightJoyStick);
        }
    };

    auto *bar = new YAwareTrackBar(name, names, name, std::move(infoStrings));
    u32 cur = (u32)this->configList->values[configVal];
    u16 curIdx = 0;
    for (u16 i = 0; i < (u16)vals.size(); i++) {
        if (vals[i] == cur) {
            curIdx = i;
            break;
        }
    }
    bar->setProgress(curIdx);
    bar->setValueChangedListener([this, configVal, kip, vals](u16 idx) {
        if (idx < (u16)vals.size())
            this->configList->values[configVal] = vals[idx];
        Result rc = hocclkIpcSetConfigValues(this->configList);
        if (R_FAILED(rc))
            FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
        if (kip)
            shouldSaveKip = true;
    });
    this->listElement->addItem(bar);
}

void MiscGui::addConfigButton(HocClkConfigValue configVal, const char *altName, const ValueRange &range, const std::string &categoryName,
                              const ValueThresholds *thresholds, const std::map<uint32_t, std::string> &labels,
                              const std::vector<NamedValue> &namedValues, bool showDefaultValue, bool kip) {
    const char *configName = altName ? altName : hocclkFormatConfigValue(configVal, true);
    auto infoStrings = ConfigInfoStrings(configVal, IsMariko(), IsHoag());

    tsl::elm::ListItem *listItem = new tsl::elm::ListItem(configName);
    if (!kip)
        listItem->setTextColor(tsl::Color(120, 235, 255, 255));

    uint64_t currentValue = this->configList->values[configVal];
    char valueText[32];
    if (currentValue == 0 && showDefaultValue) {
        snprintf(valueText, sizeof(valueText), "%s", VALUE_DEFAULT_TEXT);
    } else {
        bool foundNamedValue = false;
        for (const auto &namedValue : namedValues) {
            if (currentValue == namedValue.value) {
                snprintf(valueText, sizeof(valueText), "%s", namedValue.name.c_str());
                foundNamedValue = true;
                break;
            }
        }

        if (!foundNamedValue) {
            uint64_t displayValue = currentValue / range.divisor;
            if (!range.suffix.empty()) {
                snprintf(valueText, sizeof(valueText), "%lu %s", displayValue, range.suffix.c_str());
            } else {
                snprintf(valueText, sizeof(valueText), "%lu", displayValue);
            }
        }
    }
    listItem->setValue(valueText);

    ValueThresholds thresholdsCopy = (thresholds ? *thresholds : ValueThresholds{});

    listItem->setClickListener([this, configVal, range, categoryName, thresholdsCopy, labels, showDefaultValue, kip,
                                infoStrings = std::move(infoStrings), configName = std::string(configName)](u64 keys) {
        if (!infoStrings.empty() && (keys & HidNpadButton_Y) && !(keys & ~HidNpadButton_Y)) {
            tsl::changeTo<InfoGui>(configName, infoStrings);
            return true;
        }

        if ((keys & HidNpadButton_A) == 0)
            return false;

        std::uint32_t currentValue = this->configList->values[configVal];

        // Look up live namedValues so relabeling in refresh() is reflected
        auto nvIt = this->configNamedValues.find(configVal);
        const std::vector<NamedValue> &liveNamedValues = (nvIt != this->configNamedValues.end()) ? nvIt->second : std::vector<NamedValue>();

        if (thresholdsCopy.warning != 0 || thresholdsCopy.danger != 0) {

            tsl::changeTo<ValueChoiceGui>(
                currentValue, range, categoryName,
                [this, configVal, kip](std::uint32_t value) {
                    this->configList->values[configVal] = value;
                    Result rc = hocclkIpcSetConfigValues(this->configList);
                    if (R_FAILED(rc)) {
                        FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
                        return false;
                    }
                    if (kip) {
                        shouldSaveKip = true;
                    }
                    this->lastContextUpdate = armGetSystemTick();
                    return true;
                },
                thresholdsCopy, true, labels, liveNamedValues, showDefaultValue);
        } else {

            tsl::changeTo<ValueChoiceGui>(
                currentValue, range, categoryName,
                [this, configVal, kip](std::uint32_t value) {
                    this->configList->values[configVal] = value;
                    Result rc = hocclkIpcSetConfigValues(this->configList);
                    if (R_FAILED(rc)) {
                        FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
                        return false;
                    }
                    if (kip) {
                        shouldSaveKip = true;
                    }
                    this->lastContextUpdate = armGetSystemTick();
                    return true;
                },
                ValueThresholds(), false, labels, liveNamedValues, showDefaultValue);
        }

        return true;
    });

    this->listElement->addItem(listItem);
    this->configButtons[configVal] = listItem;
    this->configRanges[configVal] = range;
    this->configNamedValues[configVal] = namedValues;
}

void MiscGui::addConfigButtonS(HocClkConfigValue configVal, const char *altName, const ValueRange &range, const std::string &categoryName,
                               const ValueThresholds *thresholds, const std::map<uint32_t, std::string> &labels,
                               const std::vector<NamedValue> &namedValues, bool showDefaultValue, const char *subText, bool kip) {
    const char *configName = altName ? altName : hocclkFormatConfigValue(configVal, true);
    auto infoStrings = ConfigInfoStrings(configVal, IsMariko(), IsHoag());
    tsl::elm::ListItem *listItem = new tsl::elm::ListItem("");
    if (!kip)
        listItem->setTextColor(tsl::Color(120, 235, 255, 255));

    uint64_t currentValue = this->configList->values[configVal];
    char valueText[32];
    if (currentValue == 0 && showDefaultValue) {
        snprintf(valueText, sizeof(valueText), "%s", VALUE_DEFAULT_TEXT);
    } else {
        bool foundNamedValue = false;
        for (const auto &namedValue : namedValues) {
            if (currentValue == namedValue.value) {
                snprintf(valueText, sizeof(valueText), "%s", namedValue.name.c_str());
                foundNamedValue = true;
                break;
            }
        }

        if (!foundNamedValue) {
            uint64_t displayValue = currentValue / range.divisor;
            if (!range.suffix.empty()) {
                snprintf(valueText, sizeof(valueText), "%lu %s", displayValue, range.suffix.c_str());
            } else {
                snprintf(valueText, sizeof(valueText), "%lu", displayValue);
            }
        }
    }

    listItem->setText(valueText);
    listItem->setValue(subText ? subText : "");

    ValueThresholds thresholdsCopy = (thresholds ? *thresholds : ValueThresholds{});

    listItem->setClickListener([this, configVal, range, categoryName, thresholdsCopy, labels, showDefaultValue, kip,
                                infoStrings = std::move(infoStrings), configName = std::string(configName)](u64 keys) {
        if (!infoStrings.empty() && (keys & HidNpadButton_Y) && !(keys & ~HidNpadButton_Y)) {
            tsl::changeTo<InfoGui>(configName, infoStrings);
            return true;
        }

        if ((keys & HidNpadButton_A) == 0)
            return false;

        std::uint32_t currentValue = this->configList->values[configVal];

        // Look up live namedValues so relabeling in refresh() is reflected
        auto nvIt = this->configNamedValues.find(configVal);
        const std::vector<NamedValue> &liveNamedValues = (nvIt != this->configNamedValues.end()) ? nvIt->second : std::vector<NamedValue>();

        if (thresholdsCopy.warning != 0 || thresholdsCopy.danger != 0) {

            tsl::changeTo<ValueChoiceGui>(
                currentValue, range, categoryName,
                [this, configVal, kip](std::uint32_t value) {
                    this->configList->values[configVal] = value;
                    Result rc = hocclkIpcSetConfigValues(this->configList);
                    if (R_FAILED(rc)) {
                        FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
                        return false;
                    }
                    if (kip) {
                        shouldSaveKip = true;
                    }
                    this->lastContextUpdate = armGetSystemTick();
                    return true;
                },
                thresholdsCopy, true, labels, liveNamedValues, showDefaultValue);
        } else {

            tsl::changeTo<ValueChoiceGui>(
                currentValue, range, categoryName,
                [this, configVal, kip](std::uint32_t value) {
                    this->configList->values[configVal] = value;
                    Result rc = hocclkIpcSetConfigValues(this->configList);
                    if (R_FAILED(rc)) {
                        FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
                        return false;
                    }
                    if (kip) {
                        shouldSaveKip = true;
                    }
                    this->lastContextUpdate = armGetSystemTick();
                    return true;
                },
                ValueThresholds(), false, labels, liveNamedValues, showDefaultValue);
        }

        return true;
    });

    this->listElement->addItem(listItem);
    this->configButtons[configVal] = listItem;
    this->configRanges[configVal] = range;
    this->configNamedValues[configVal] = namedValues;
    this->configButtonSKeys.insert(configVal);
    if (subText)
        this->configButtonSSubtext[configVal] = std::string(subText);
}

void MiscGui::updateConfigToggles() {
    for (const auto &[value, toggle] : this->configToggles) {
        if (toggle != nullptr)
            toggle->setState(this->configList->values[value]);
    }
}

void MiscGui::addFreqButton(HocClkConfigValue configVal, const char *altName, HocClkModule module, const std::map<uint32_t, std::string> &labels) {
    const char *configName = altName ? altName : hocclkFormatConfigValue(configVal, true);
    auto infoStrings = ConfigInfoStrings(configVal, IsMariko(), IsHoag());

    tsl::elm::ListItem *listItem = new tsl::elm::ListItem(configName);

    uint64_t currentMHz = this->configList->values[configVal];
    char valueText[32];
    snprintf(valueText, sizeof(valueText), "%lu MHz", currentMHz);
    listItem->setValue(valueText);

    listItem->setClickListener(
        [this, configVal, module, labels, infoStrings = std::move(infoStrings), configName = std::string(configName)](u64 keys) {
            if (!infoStrings.empty() && (keys & HidNpadButton_Y) && !(keys & ~HidNpadButton_Y)) {
                tsl::changeTo<InfoGui>(configName, infoStrings);
                return true;
            }

            if ((keys & HidNpadButton_A) == 0)
                return false;

            std::uint32_t hzList[HOCCLK_FREQ_LIST_MAX];
            std::uint32_t hzCount;

            Result rc = hocclkIpcGetFreqList(module, hzList, HOCCLK_FREQ_LIST_MAX, &hzCount);
            if (R_FAILED(rc)) {
                FatalGui::openWithResultCode("hocclkIpcGetFreqList", rc);
                return false;
            }

            std::uint32_t currentHz = this->configList->values[configVal] * 1'000'000;

            tsl::changeTo<FreqChoiceGui>(
                currentHz, hzList, hzCount, module,
                [this, configVal](std::uint32_t hz) {
                    uint64_t mhz = hz / 1'000'000;
                    this->configList->values[configVal] = mhz;

                    Result rc = hocclkIpcSetConfigValues(this->configList);
                    if (R_FAILED(rc)) {
                        FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
                        return false;
                    }

                    this->lastContextUpdate = armGetSystemTick();
                    return true;
                },
                false, labels);

            return true;
        });

    this->listElement->addItem(listItem);
    this->configButtons[configVal] = listItem;

    this->configRanges[configVal] = ValueRange(0, 0, 0, "MHz", 1);
}

void MiscGui::addGpuFreqVoltageButton(HocClkConfigValue configVal, const char *altName, uint32_t freqHz, const ValueRange &range,
                                      const std::string &categoryName, const ValueThresholds *thresholds,
                                      const std::map<uint32_t, std::string> &labels, const std::vector<NamedValue> &namedValues,
                                      bool showDefaultValue, bool kip) {
    const char *configName = altName ? altName : hocclkFormatConfigValue(configVal, true);
    auto infoStrings = ConfigInfoStrings(configVal, IsMariko(), IsHoag());

    tsl::elm::ListItem *listItem = new tsl::elm::ListItem(configName);
    if (!kip || this->configList->values[HocClkConfigValue_LiveGpuVoltage])
        listItem->setTextColor(tsl::Color(120, 235, 255, 255));

    uint64_t currentValue = this->configList->values[configVal];
    char valueText[32];
    if (currentValue == 0 && showDefaultValue) {
        snprintf(valueText, sizeof(valueText), "%s", VALUE_DEFAULT_TEXT);
    } else {
        bool foundNamedValue = false;
        for (const auto &namedValue : namedValues) {
            if (currentValue == namedValue.value) {
                snprintf(valueText, sizeof(valueText), "%s", namedValue.name.c_str());
                foundNamedValue = true;
                break;
            }
        }
        if (!foundNamedValue) {
            uint64_t displayValue = currentValue / range.divisor;
            if (!range.suffix.empty()) {
                snprintf(valueText, sizeof(valueText), "%lu %s", displayValue, range.suffix.c_str());
            } else {
                snprintf(valueText, sizeof(valueText), "%lu", displayValue);
            }
        }
    }
    listItem->setValue(valueText);

    ValueThresholds thresholdsCopy = (thresholds ? *thresholds : ValueThresholds{});

    listItem->setClickListener([this, configVal, range, categoryName, thresholdsCopy, labels, showDefaultValue, kip, freqHz,
                                infoStrings = std::move(infoStrings), configName = std::string(configName)](u64 keys) {
        if (!infoStrings.empty() && (keys & HidNpadButton_Y) && !(keys & ~HidNpadButton_Y)) {
            tsl::changeTo<InfoGui>(configName, infoStrings);
            return true;
        }

        if ((keys & HidNpadButton_Y) && !(keys & ~HidNpadButton_Y)) {
            if (!this->configList->values[HocClkConfigValue_LiveGpuVoltage])
                return false;

            std::uint32_t currentValue = this->configList->values[configVal];

            auto nvIt = this->configNamedValues.find(configVal);
            const std::vector<NamedValue> &liveNamedValues = (nvIt != this->configNamedValues.end()) ? nvIt->second : std::vector<NamedValue>();

            std::string liveCategoryName = categoryName + " (Live)";

            auto liveCallback = [this, configVal, kip, freqHz](std::uint32_t value) {
                this->configList->values[configVal] = value;
                Result rc = hocclkIpcSetConfigValues(this->configList);
                if (R_FAILED(rc)) {
                    FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
                    return false;
                }
                if (kip) {
                    shouldSaveKip = true;
                }
                this->lastContextUpdate = armGetSystemTick();

                Result rcVolt = hocClkIpcRequestGpuVoltage((uint32_t)value, freqHz);
                if (R_FAILED(rcVolt)) {
                    FatalGui::openWithResultCode("hocClkIpcRequestGpuVoltage", rcVolt);
                    return false;
                }
                return true;
            };

            if (thresholdsCopy.warning != 0 || thresholdsCopy.danger != 0) {
                tsl::changeTo<ValueChoiceGui>(
                    currentValue, range, liveCategoryName,
                    liveCallback,
                    thresholdsCopy, true, labels, liveNamedValues, showDefaultValue);
            } else {
                tsl::changeTo<ValueChoiceGui>(
                    currentValue, range, liveCategoryName,
                    liveCallback,
                    ValueThresholds(), false, labels, liveNamedValues, showDefaultValue);
            }

            return true;
        }

        if ((keys & HidNpadButton_A) == 0)
            return false;

        std::uint32_t currentValue = this->configList->values[configVal];

        auto nvIt = this->configNamedValues.find(configVal);
        const std::vector<NamedValue> &liveNamedValues = (nvIt != this->configNamedValues.end()) ? nvIt->second : std::vector<NamedValue>();

        if (thresholdsCopy.warning != 0 || thresholdsCopy.danger != 0) {
            tsl::changeTo<ValueChoiceGui>(
                currentValue, range, categoryName,
                [this, configVal, kip](std::uint32_t value) {
                    this->configList->values[configVal] = value;
                    Result rc = hocclkIpcSetConfigValues(this->configList);
                    if (R_FAILED(rc)) {
                        FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
                        return false;
                    }
                    if (kip) {
                        shouldSaveKip = true;
                    }
                    this->lastContextUpdate = armGetSystemTick();
                    return true;
                },
                thresholdsCopy, true, labels, liveNamedValues, showDefaultValue);
        } else {
            tsl::changeTo<ValueChoiceGui>(
                currentValue, range, categoryName,
                [this, configVal, kip](std::uint32_t value) {
                    this->configList->values[configVal] = value;
                    Result rc = hocclkIpcSetConfigValues(this->configList);
                    if (R_FAILED(rc)) {
                        FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
                        return false;
                    }
                    if (kip) {
                        shouldSaveKip = true;
                    }
                    this->lastContextUpdate = armGetSystemTick();
                    return true;
                },
                ValueThresholds(), false, labels, liveNamedValues, showDefaultValue);
        }

        return true;
    });

    this->listElement->addItem(listItem);
    this->configButtons[configVal] = listItem;
    this->configRanges[configVal] = range;
    this->configNamedValues[configVal] = namedValues;
}

void MiscGui::listUI() {
    Result rc = hocclkIpcGetConfigValues(configList);
    if (R_FAILED(rc)) [[unlikely]] {
        FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
        return;
    }

    ValueThresholds thresholdsDisabled(0, 0);
    std::vector<NamedValue> noNamedValues = {};

    this->listElement->addItem(new CompactCategoryHeader("Settings"));

    tsl::elm::CustomDrawer *rebootSetWarning = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
        renderer->drawString("\uE150 Settings marked in blue", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
        renderer->drawString("don't require a reboot to apply!", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
        renderer->drawString("You can also press \ue0e3 to show", false, x + 20, y + 70, 18, tsl::style::color::ColorText);
        renderer->drawString("information about each setting.", false, x + 20, y + 90, 18, tsl::style::color::ColorText);
    });
    rebootSetWarning->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 110);
    this->listElement->addItem(rebootSetWarning);

    tsl::elm::ListItem *sysmoduleSettingsSubMenu = new tsl::elm::ListItem("General Settings");
    sysmoduleSettingsSubMenu->setClickListener([](u64 keys) {
        if (keys & HidNpadButton_A) {
            tsl::swapTo<GeneralSettingsSubMenuGui>();
            return true;
        }
        return false;
    });
    sysmoduleSettingsSubMenu->setValue(R_ARROW);
    this->listElement->addItem(sysmoduleSettingsSubMenu);

    tsl::elm::ListItem *governorSettingsSubMenu = new tsl::elm::ListItem("Governor Settings");
    governorSettingsSubMenu->setClickListener([](u64 keys) {
        if (keys & HidNpadButton_A) {
            tsl::swapTo<GovernorSettingsSubMenuGui>();
            return true;
        }
        return false;
    });
    governorSettingsSubMenu->setValue(R_ARROW);
    this->listElement->addItem(governorSettingsSubMenu);

    tsl::elm::ListItem *safetySubmenu = new tsl::elm::ListItem("Safety Settings");
    safetySubmenu->setClickListener([](u64 keys) {
        if (keys & HidNpadButton_A) {
            tsl::swapTo<SafetySubMenuGui>();
            return true;
        }
        return false;
    });
    safetySubmenu->setValue(R_ARROW);
    this->listElement->addItem(safetySubmenu);

    tsl::elm::ListItem *ramSubmenu = new tsl::elm::ListItem("RAM Settings");
    ramSubmenu->setClickListener([](u64 keys) {
        if (keys & HidNpadButton_A) {
            tsl::swapTo<RamSubmenuGui>();
            return true;
        }
        return false;
    });
    ramSubmenu->setValue(R_ARROW);
    this->listElement->addItem(ramSubmenu);

    tsl::elm::ListItem *cpuSubmenu = new tsl::elm::ListItem("CPU Settings");
    cpuSubmenu->setClickListener([](u64 keys) {
        if (keys & HidNpadButton_A) {
            tsl::swapTo<CpuSubmenuGui>();
            return true;
        }
        return false;
    });
    cpuSubmenu->setValue(R_ARROW);
    this->listElement->addItem(cpuSubmenu);

    tsl::elm::ListItem *gpuSubmenu = new tsl::elm::ListItem("GPU Settings");
    gpuSubmenu->setClickListener([](u64 keys) {
        if (keys & HidNpadButton_A) {
            tsl::swapTo<GpuSubmenuGui>();
            return true;
        }
        return false;
    });
    gpuSubmenu->setValue(R_ARROW);
    this->listElement->addItem(gpuSubmenu);

    tsl::elm::ListItem *displaySubMenu = new tsl::elm::ListItem("Display Settings");
    displaySubMenu->setClickListener([](u64 keys) {
        if (keys & HidNpadButton_A) {
            tsl::swapTo<DisplaySubMenuGui>();
            return true;
        }
        return false;
    });
    displaySubMenu->setValue(R_ARROW);
    this->listElement->addItem(displaySubMenu);

    if (this->configList->values[HocClkConfigValue_EnableExperimentalSettings]) {
        tsl::elm::ListItem *experimentalSubMenu = new tsl::elm::ListItem("Experimental Settings");
        experimentalSubMenu->setClickListener([](u64 keys) {
            if (keys & HidNpadButton_A) {
                tsl::swapTo<ExperimentalSettingsSubMenuGui>();
                return true;
            }
            return false;
        });
        experimentalSubMenu->setValue(R_ARROW);
        this->listElement->addItem(experimentalSubMenu);
    }

    if (!lastItemName.empty()) {
        this->listElement->jumpToItem(lastItemName);
    }
    lastItemName = "";
}

class GeneralSettingsSubMenuGui : public MiscGui {
    public:
    GeneralSettingsSubMenuGui() {
    }

    bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                      HidAnalogStickState rightJoyStick) override {
        if (keysDown & KEY_B) {
            triggerExitFeedback();
            lastItemName = "General Settings";
            tsl::swapTo<MiscGui>();
            return true;
        }
        return false;
    }

    protected:
    void listUI() override {
        Result rc = hocclkIpcGetConfigValues(this->configList);
        if (R_FAILED(rc)) [[unlikely]] {
            FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
            return;
        }
        this->listElement->addItem(new CompactCategoryHeader("General Settings"));

        ValueThresholds thresholdsDisabled(0, 0);
        std::vector<NamedValue> ramVoltDispModes = {
            NamedValue("VDD2", RamDisplayMode_VDD2),
            NamedValue("VDDQ", RamDisplayMode_VDDQ),
        };

        if (IsMariko()) {
            addConfigButton(HocClkConfigValue_RAMVoltDisplayMode, "RAM Voltage Display Mode", ValueRange(0, 12, 1, "", 0), "RAM Voltage Display Mode",
                            &thresholdsDisabled, {}, ramVoltDispModes, false);
        }

        std::vector<NamedValue> RamDisplayUnitValues = {
            NamedValue("MHz", RamDisplayUnit_MHz),
            NamedValue("MT/s", RamDisplayUnit_MTs),
            NamedValue("MHz and MT/s", RamDisplayUnit_MHzMTs),
        };
        addConfigButton(HocClkConfigValue_RamDisplayUnit, "RAM Display Unit", ValueRange(0, 0, 2, "", 0), "RAM Display Unit", &thresholdsDisabled, {},
                        RamDisplayUnitValues, false

        );

        addConfigButton(HocClkConfigValue_PollingIntervalMs, "Polling Interval", ValueRange(50, 1000, 50, "ms", 1), "Polling Interval",
                        &thresholdsDisabled, {}, {}, false);

        tsl::elm::CustomDrawer *exSetWarning = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
            renderer->drawString("\uE150 Experimental Settings are incomplete ", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
            renderer->drawString("and may not work correctly or at all!", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
            renderer->drawString("Here be dragons!", false, x + 20, y + 70, 18, tsl::style::color::ColorText);
        });
        exSetWarning->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 90);
        this->listElement->addItem(exSetWarning);

        addConfigToggle(HocClkConfigValue_EnableExperimentalSettings, nullptr);
    }
};

class ExperimentalSettingsSubMenuGui : public MiscGui {
    public:
    ExperimentalSettingsSubMenuGui() {
    }

    bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                      HidAnalogStickState rightJoyStick) override {
        if (keysDown & KEY_B) {
            triggerExitFeedback();
            lastItemName = "Experimental Settings";
            tsl::swapTo<MiscGui>();
            return true;
        }
        return false;
    }

    protected:
    void listUI() override {
        Result rc = hocclkIpcGetConfigValues(this->configList);
        if (R_FAILED(rc)) [[unlikely]] {
            FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
            return;
        }
        this->listElement->addItem(new CompactCategoryHeader("Experimental Settings"));
        ValueThresholds thresholdsDisabled(0, 0);
        if (IsMariko()) {
            addConfigToggle(HocClkConfigValue_MarikoMiddleFreqs, nullptr, true);
            addConfigToggle(HocClkConfigValue_LiveCpuUv, nullptr);
        }

        addConfigToggle(HocClkConfigValue_LiveGpuVoltage, "Live GPU Volt Request", false);

        std::vector<NamedValue> gpuSchedMethodValues = {
            NamedValue("INI", GpuSchedulingOverrideMethod_Ini),
            NamedValue("NV Service", GpuSchedulingOverrideMethod_NvService),
        };
        addConfigButton(HocClkConfigValue_GPUSchedulingMethod, "GPU Scheduling Override Method", ValueRange(0, 0, 1, "", 0),
                        "GPU Scheduling Override Method", &thresholdsDisabled, {}, gpuSchedMethodValues, false);

        std::vector<NamedValue> ramRFMeasurementMethods = {
            NamedValue("PLL", MemoryFrequencyMeasurementMode_PLL),
            NamedValue("Actmon", MemoryFrequencyMeasurementMode_Actmon),
        };
        addConfigButton(HocClkConfigValue_MemoryFrequencyMeasurementMode, "Memory Frequency Measurement Mode", ValueRange(0, 0, 1, "", 0),
                        "Memory Frequency Measurement Mode", &thresholdsDisabled, {}, ramRFMeasurementMethods, false);

        tsl::elm::CustomDrawer *chargeWarningText = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
            renderer->drawString("\uE150 Overriding the charge current", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
            renderer->drawString("can be dangerous and may cause", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
            renderer->drawString("damage to your battery or charger!", false, x + 20, y + 70, 18, tsl::style::color::ColorText);
        });
        chargeWarningText->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 90);
        this->listElement->addItem(chargeWarningText);

        if (!IsHoag()) {
            std::vector<NamedValue> chargerCurrents = {
                NamedValue("Disabled", 0),  NamedValue("1024mA", 1024), NamedValue("1280mA", 1280), NamedValue("1536mA", 1536),
                NamedValue("1792mA", 1792), NamedValue("2048mA", 2048), NamedValue("2304mA", 2304), NamedValue("2560mA", 2560),
                NamedValue("2816mA", 2816), NamedValue("3072mA", 3072),
            };

            ValueThresholds chargerThresholds(2048, 2049);

            addConfigButton(HocClkConfigValue_BatteryChargeCurrent, "Charge Current Override", ValueRange(0, 0, 1, "", 0), "Charge Current Override",
                            &chargerThresholds, {}, chargerCurrents, false);
        } else {
            std::vector<NamedValue> chargerCurrents = {
                NamedValue("Disabled", 0),  NamedValue("1024mA", 1024), NamedValue("1280mA", 1280), NamedValue("1536mA", 1536),
                NamedValue("1664mA", 1664),  // Why Nintendo?
                NamedValue("1792mA", 1792), NamedValue("2048mA", 2048), NamedValue("2304mA", 2304), NamedValue("2560mA", 2560),
            };

            ValueThresholds chargerThresholds(1664, 1793);

            addConfigButton(HocClkConfigValue_BatteryChargeCurrent, "Charge Current Override", ValueRange(0, 0, 1, "", 0), "Charge Current Override",
                            &chargerThresholds, {}, chargerCurrents, false);
        }

        tsl::elm::CustomDrawer *inputLimitWarningText = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
            renderer->drawString("\uE150 Overriding the input current", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
            renderer->drawString("limit increases power draw from", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
            renderer->drawString("your charger. And board stress ", false, x + 20, y + 70, 18, tsl::style::color::ColorText);
            renderer->drawString("use 1500mA max.", false, x + 20, y + 90, 18, tsl::style::color::ColorText);
        });
        inputLimitWarningText->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 110);
        this->listElement->addItem(inputLimitWarningText);

        // having an option for hoag would be cool and disabling 100-500 and 2000+
        std::vector<NamedValue> inputCurrentLimits = {
            NamedValue("Disabled", 0),
            NamedValue("100mA", 100),
            NamedValue("150mA", 150),
            NamedValue("500mA", 500),
            NamedValue("900mA", 900, "Hoag Default"),
            NamedValue("1200mA", 1200, "Default"),
            NamedValue("1500mA", 1500),
            NamedValue("2000mA", 2000),
            NamedValue("3000mA", 3000),
        };

        ValueThresholds inputLimitThresholds(1500, 1501);

        addConfigButton(HocClkConfigValue_InputCurrentLimit, "Input Current Limit Override", ValueRange(0, 0, 1, "", 0),
                        "Input Current Limit Override", &inputLimitThresholds, {}, inputCurrentLimits, false);

        if (IsAula()) {
            std::vector<NamedValue> displayClrPreset = {
                NamedValue("Do Not Override", AulaDisplayColorMode_DoNotOverride),
                NamedValue("Basic", AulaDisplayColorMode_Basic),
                NamedValue("Saturated", AulaDisplayColorMode_Saturated),
                NamedValue("Washed", AulaDisplayColorMode_Washed),
                NamedValue("Natural", AulaDisplayColorMode_Natural),
                NamedValue("Vivid", AulaDisplayColorMode_Vivid),
                NamedValue("Washed", AulaDisplayColorMode_Night0, "Night"),
                NamedValue("Basic", AulaDisplayColorMode_Night1, "Night"),
                NamedValue("Natural", AulaDisplayColorMode_Night2, "Night"),
                NamedValue("Vivid", AulaDisplayColorMode_Night3, "Night"),
            };

            addConfigButton(HocClkConfigValue_AulaDisplayColorPreset, "Display Color Preset", ValueRange(0, 1, 1, "", 0), "Display Color Preset",
                            &thresholdsDisabled, {}, displayClrPreset, false, false);
        }
    }
};

class GovernorSettingsSubMenuGui : public MiscGui {
    public:
    GovernorSettingsSubMenuGui() {
    }

    bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                      HidAnalogStickState rightJoyStick) override {
        if (keysDown & KEY_B) {
            triggerExitFeedback();
            lastItemName = "Governor Settings";
            tsl::swapTo<MiscGui>();
            return true;
        }
        return false;
    }

    protected:
    void listUI() override {
        Result rc = hocclkIpcGetConfigValues(this->configList);
        if (R_FAILED(rc)) [[unlikely]] {
            FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
            return;
        }
        this->listElement->addItem(new CompactCategoryHeader("Governor Settings"));
        ValueThresholds thresholdsDisabled(0, 0);

        std::vector<NamedValue> GovernorMinHz = {
            NamedValue("510 MHz", 510000000), NamedValue("612 MHz", 612000000), NamedValue("714 MHz", 714000000),
            NamedValue("816 MHz", 816000000), NamedValue("918 MHz", 918000000), NamedValue("1020 MHz", 1020000000),
        };

        addConfigButton(HocClkConfigValue_CpuGovernorMinimumFreq, "CPU Governor Minimum Frequency", ValueRange(0, 0, 1, "", 0),
                        "CPU Governor Minimum Frequency", &thresholdsDisabled, {}, GovernorMinHz, false);
    }
};

class DisplaySubMenuGui : public MiscGui {
    public:
    DisplaySubMenuGui() {
    }

    bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                      HidAnalogStickState rightJoyStick) override {
        if (keysDown & KEY_B) {
            triggerExitFeedback();
            lastItemName = "Display Settings";
            tsl::swapTo<MiscGui>();
            return true;
        }
        return false;
    }

    protected:
    void listUI() override {
        Result rc = hocclkIpcGetConfigValues(this->configList);
        if (R_FAILED(rc)) [[unlikely]] {
            FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
            return;
        }
        ValueThresholds thresholdsDisabled(0, 0);

        BaseMenuGui::refresh();  // get latest context
        if (!this->context)
            return;

        this->listElement->addItem(new CompactCategoryHeader("Display Settings"));
        addConfigToggle(HocClkConfigValue_OverwriteRefreshRate, nullptr);
        if (!this->context->isUsingRetroSuper) {
            tsl::elm::CustomDrawer *warningText = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
                renderer->drawString("\uE150 Usage of unsafe display", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
                renderer->drawString("refresh rates may cause stress", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
                renderer->drawString("or damage to your display! ", false, x + 20, y + 70, 18, tsl::style::color::ColorText);
                renderer->drawString("Proceed at your own risk!", false, x + 20, y + 90, 18, tsl::style::color::ColorText);
            });

            warningText->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 110);
            this->listElement->addItem(warningText);
            ValueThresholds displayThresholds(60, 65);
            addConfigButton(HocClkConfigValue_MaxDisplayClockH, "Max Handheld Display Hz", ValueRange(60, IsAula() ? 65 : 75, 1, " Hz", 1),
                            "Display Clock", &displayThresholds, {}, {}, false);
        }
        if (!IsAula()) {
            tsl::elm::CustomDrawer *warningTextDV = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
                renderer->drawString("\uE150 Adjust the display voltage", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
                renderer->drawString("with caution to avoid damage", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
                renderer->drawString("to your display panel! ", false, x + 20, y + 70, 18, tsl::style::color::ColorText);
                renderer->drawString("Proceed at your own risk!", false, x + 20, y + 90, 18, tsl::style::color::ColorText);
            });
            warningTextDV->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 110);
            this->listElement->addItem(warningTextDV);
            addConfigButton(HocClkConfigValue_DisplayVoltage, "Display Voltage", ValueRange(800, 1200, 25, " mV", 1), "Display Voltage",
                            &thresholdsDisabled, {}, {}, false);
        }
    }
};

class SafetySubMenuGui : public MiscGui {
    public:
    SafetySubMenuGui() {
    }

    bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                      HidAnalogStickState rightJoyStick) override {
        if (keysDown & KEY_B) {
            triggerExitFeedback();
            lastItemName = "Safety Settings";
            tsl::swapTo<MiscGui>();
            return true;
        }
        return false;
    }

    protected:
    void listUI() override {
        Result rc = hocclkIpcGetConfigValues(this->configList);
        if (R_FAILED(rc)) [[unlikely]] {
            FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
            return;
        }
        this->listElement->addItem(new CompactCategoryHeader("Safety Settings"));
        addConfigToggle(HocClkConfigValue_UncappedClocks, nullptr);
        addConfigToggle(HocClkConfigValue_ThermalThrottle, nullptr);

#if IS_MINIMAL == 0
        ValueThresholds throttleThresholds(70, 80);
        addConfigButton(HocClkConfigValue_ThermalThrottleThreshold, "Thermal Throttle Limit", ValueRange(50, 85, 1, "°C", 1), "Temp",
                        &throttleThresholds);
#endif
    }
};

class RamSubmenuGui : public MiscGui {
    public:
    RamSubmenuGui() {
    }

    bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                      HidAnalogStickState rightJoyStick) override {
        if (keysDown & KEY_B) {
            triggerExitFeedback();
            lastItemName = "RAM Settings";
            tsl::swapTo<MiscGui>();
            return true;
        }
        return false;
    }

    protected:
    void listUI() override {
        BaseMenuGui::refresh();
        if (!this->context)
            return;

        Result rc = hocclkIpcGetConfigValues(this->configList);
        if (R_FAILED(rc)) [[unlikely]] {
            FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
            return;
        }
        ValueThresholds thresholdsDisabled(0, 0);
        std::vector<NamedValue> noNamedValues = {};

        this->listElement->addItem(new CompactCategoryHeader("RAM Settings"));

        addMappedConfigTrackbar(KipConfigValue_emcDvbShift, "DVB Shift",
                                { 0xFFFFFFFCu, 0xFFFFFFFDu, 0xFFFFFFFEu, 0xFFFFFFFFu, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u },
                                { "-4", "-3", "-2", "-1", " 0", "1", "2", "3", "4", "5", "6", "7", "8" });

        if (IsMariko()) {
            u32 socSpeedo = this->context->speedos[HocClkSpeedo_SOC];
            std::string autoText = "1000 mV";
            if (socSpeedo <= 1597) {
                autoText = "1050 mV";
            } else if (socSpeedo <= 1708) {
                autoText = "1025 mV";
            } else if (socSpeedo >= 1709) {
                autoText = "1000 mV";
            }

            std::vector<NamedValue> marikovmaxconf = {
                NamedValue("Do not override", 0, autoText),
                NamedValue("1000 mV", 1000),
                NamedValue("1025 mV", 1025),
                NamedValue("1050 mV", 1050),
                NamedValue("1075 mV", 1075),
                NamedValue("1100 mV", 1100),
                NamedValue("1125 mV", 1125),
                NamedValue("1150 mV", 1150),
                NamedValue("1175 mV", 1175),
                NamedValue("1200 mV", 1200),
            };
            ValueThresholds marikovmaxT(1075, 1150);

            addConfigButton(KipConfigValue_marikoSocVmax, "SoC Max Volt", ValueRange(0, 12, 1, "", 0), "SoC Max Volt", &marikovmaxT, {},
                            marikovmaxconf, false, true);
        }

        addConfigToggle(KipConfigValue_hpMode, "HP Mode", true);

        std::map<uint32_t, std::string> emc_voltage_label = {
            { 1100000, "Default (Mariko)" },  { 1125000, "Default (Erista)" },  { 1175000, "Rating" },
            { 1212500, "Safe Max (Mariko)" }, { 1237500, "Safe Max (Erista)" }, { 1250000, "Unsafe Max" },
        };

        ValueThresholds vdd2Thresholds(IsMariko() ? 1212500 : 1237500, IsMariko() ? 1250000 : 1275000);
        addConfigButton(KipConfigValue_commonEmcMemVolt, "RAM VDD2 Voltage", ValueRange(912500, 1350000, 12500, "mV", 1000, 1), "Voltage",
                        &vdd2Thresholds, emc_voltage_label, noNamedValues, false, true);

        if (IsMariko()) {
            ValueThresholds vddqThresholds(675000, 725000);
            addConfigButton(KipConfigValue_marikoEmcVddqVolt, "RAM VDDQ Voltage", ValueRange(400000, 750000, 5000, "mV", 1000), "RAM VDDQ Voltage",
                            &vddqThresholds, {}, {}, false, true);
        }

        if (IsMariko()) {
            std::vector<NamedValue> stepMode = {
                NamedValue("66MHz", 0),
                NamedValue("100MHz", 1),
                NamedValue("133MHz", 3),  // Mantain compatability
                NamedValue("JEDEC.", 2),
            };

            addConfigButton(KipConfigValue_stepMode, "Step Mode", ValueRange(0, 0, 2, "", 0), "Step Mode", &thresholdsDisabled, {}, stepMode, false,
                            true);
        }

        std::vector<NamedValue> emcMaxClock = {};
        RamDisplayUnit unit = (RamDisplayUnit)this->configList->values[HocClkConfigValue_RamDisplayUnit];

        if (IsErista()) {
            emcMaxClock = {
                NamedValue("Disabled", 1600000),
                NamedValue("1633 MHz", 1633000),
                NamedValue("1666 MHz", 1666000),
                NamedValue("1700 MHz", 1700000),
                NamedValue("1733 MHz", 1733000),
                // NamedValue("1766 MHz", 1766000),
                NamedValue("1800 MHz", 1800000),
                NamedValue("1833 MHz", 1833000),
                NamedValue("1862 MHz", 1862400, "JEDEC."),
                NamedValue("1881 MHz", 1881600),
                NamedValue("1900 MHz", 1900800),
                NamedValue("1920 MHz", 1920000),
                NamedValue("1939 MHz", 1939200),
                NamedValue("1958 MHz", 1958400),
                NamedValue("1977 MHz", 1977600),
                NamedValue("1996 MHz", 1996800, "JEDEC."),
                NamedValue("2016 MHz", 2016000),
                NamedValue("2035 MHz", 2035200),
                NamedValue("2054 MHz", 2054400),
                NamedValue("2073 MHz", 2073600),
                NamedValue("2092 MHz", 2092800),
                NamedValue("2112 MHz", 2112000),
                NamedValue("2131 MHz", 2131200, "JEDEC."),
                NamedValue("2150 MHz", 2150400),
                NamedValue("2169 MHz", 2169600),
                NamedValue("2188 MHz", 2188800),
                NamedValue("2208 MHz", 2208000),
                NamedValue("2227 MHz", 2227200),
                NamedValue("2246 MHz", 2246400),
                NamedValue("2265 MHz", 2265600),
                NamedValue("2284 MHz", 2284800),
                NamedValue("2304 MHz", 2304000),
                NamedValue("2323 MHz", 2323200),
                NamedValue("2342 MHz", 2342400),
                NamedValue("2361 MHz", 2361600),
                NamedValue("2380 MHz", 2380800),
                NamedValue("2400 MHz", 2400000, "JEDEC."),
            };
        } else {
            emcMaxClock = {
                NamedValue("1600 MHz", 1600000),
                NamedValue("1633 MHz", 1633000),
                NamedValue("1666 MHz", 1666000),
                NamedValue("1700 MHz", 1700000),
                NamedValue("1733 MHz", 1733000),
                NamedValue("1766 MHz", 1766000),
                NamedValue("1800 MHz", 1800000),
                NamedValue("1833 MHz", 1833000),
                NamedValue("1866 MHz", 1866000, "JEDEC."),
                NamedValue("1900 MHz", 1900000),
                NamedValue("1933 MHz", 1933000),
                NamedValue("1966 MHz", 1966000),
                NamedValue("1996 MHz", 1996800, "JEDEC."),
                NamedValue("2000 MHz", 2000000),
                NamedValue("2033 MHz", 2033000),
                NamedValue("2066 MHz", 2066000),
                NamedValue("2100 MHz", 2100000),
                NamedValue("2133 MHz", 2133000, "JEDEC."),
                NamedValue("2166 MHz", 2166000),
                NamedValue("2200 MHz", 2200000),
                NamedValue("2233 MHz", 2233000),
                NamedValue("2266 MHz", 2266000),
                NamedValue("2300 MHz", 2300000),
                NamedValue("2333 MHz", 2333000),
                NamedValue("2366 MHz", 2366000),
                NamedValue("2400 MHz", 2400000, "JEDEC."),
                NamedValue("2433 MHz", 2433000),
                NamedValue("2466 MHz", 2466000),
                NamedValue("2500 MHz", 2500000),
                NamedValue("2533 MHz", 2533000),
                NamedValue("2566 MHz", 2566000),
                NamedValue("2600 MHz", 2600000),
                NamedValue("2633 MHz", 2633000),
                NamedValue("2666 MHz", 2666000, "JEDEC."),
                NamedValue("2700 MHz", 2700000),
                NamedValue("2733 MHz", 2733000),
                NamedValue("2766 MHz", 2766000),
                NamedValue("2800 MHz", 2800000),
                NamedValue("2833 MHz", 2833000),
                NamedValue("2866 MHz", 2866000),
                NamedValue("2900 MHz", 2900000),
                NamedValue("2933 MHz", 2933000, "JEDEC."),
                NamedValue("2966 MHz", 2966000),
                NamedValue("3000 MHz", 3000000),
                NamedValue("3033 MHz", 3033000),
                NamedValue("3066 MHz", 3066000),
                NamedValue("3100 MHz", 3100000),
                NamedValue("3133 MHz", 3133000),
                NamedValue("3166 MHz", 3166000),
                NamedValue("3200 MHz", 3200000, "JEDEC."),
                NamedValue("3233 MHz", 3233000, "High speedo needed!"),
                NamedValue("3266 MHz", 3266000, "High speedo needed!"),
                NamedValue("3300 MHz", 3300000, "High speedo needed!"),
            };
        }

        for (auto &nv : emcMaxClock) {
            if (nv.name != "Disabled") {
                nv.name = formatMemClockKhzLabel(nv.value, unit);
            }
        }

        if (IsMariko()) {
            addConfigButton(KipConfigValue_marikoEmcMaxClock, "Ram Max Clock", ValueRange(0, 1, 1, "", 1), "Ram Max Clock", &thresholdsDisabled, {},
                            emcMaxClock, false, true);
        } else {
            addConfigButton(KipConfigValue_eristaEmcMaxClock, "Ram Max Clock", ValueRange(0, 1, 1, "", 1), "Ram Max Clock", &thresholdsDisabled, {},
                            emcMaxClock, false, true);
        }

        tsl::elm::ListItem *latenciesSubmenu = new tsl::elm::ListItem("RAM Latency Editor");
        latenciesSubmenu->setClickListener([](u64 keys) {
            if (keys & HidNpadButton_A) {
                tsl::swapTo<RamLatenciesSubmenuGui>();
                return true;
            }
            return false;
        });
        latenciesSubmenu->setValue(R_ARROW);
        this->listElement->addItem(latenciesSubmenu);

        tsl::elm::ListItem *timingsSubmenu = new tsl::elm::ListItem("RAM Timing Reductions");
        timingsSubmenu->setClickListener([](u64 keys) {
            if (keys & HidNpadButton_A) {
                tsl::swapTo<RamTimingsSubmenuGui>();
                return true;
            }
            return false;
        });
        timingsSubmenu->setValue(R_ARROW);
        this->listElement->addItem(timingsSubmenu);

        if (IsMariko()) {
            tsl::elm::ListItem *socVoltageTable = new tsl::elm::ListItem("SOC Voltage Table");
            socVoltageTable->setClickListener([](u64 keys) {
                if (keys & HidNpadButton_A) {
                    tsl::swapTo<SocCustomTableSubmenuGui>();
                    return true;
                }
                return false;
            });
            socVoltageTable->setValue(R_ARROW);
            this->listElement->addItem(socVoltageTable);
        }

        if (!lastItemName.empty()) {
            this->listElement->jumpToItem(lastItemName);
        }
        lastItemName = "";
    }
};

class RamTimingsSubmenuGui : public MiscGui {
    public:
    RamTimingsSubmenuGui() {
    }

    bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                      HidAnalogStickState rightJoyStick) override {
        if (keysDown & KEY_B) {
            triggerExitFeedback();
            lastItemName = "RAM Timing Reductions";
            tsl::swapTo<RamSubmenuGui>();
            return true;
        }
        return false;
    }

    protected:
    void listUI() override {
        Result rc = hocclkIpcGetConfigValues(this->configList);
        if (R_FAILED(rc)) [[unlikely]] {
            FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
            return;
        }
        this->listElement->addItem(new CompactCategoryHeader("Memory Timings"));

        addConfigTrackbar(KipConfigValue_t1_tRCD, "t1 tRCD", ValueRange(0, 7, 1));
        addConfigTrackbar(KipConfigValue_t2_tRP, "t2 tRP", ValueRange(0, 7, 1));
        addConfigTrackbar(KipConfigValue_t3_tRAS, "t3 tRAS", ValueRange(0, 9, 1));
        addConfigTrackbar(KipConfigValue_t4_tRRD, "t4 tRRD", ValueRange(0, 6, 1));
        addConfigTrackbar(KipConfigValue_t5_tRFC, "t5 tRFC", ValueRange(0, IsErista() ? 5u : 10u, 1));
        addConfigTrackbar(KipConfigValue_t6_tRTW, "t6 tRTW", ValueRange(0, 9, 1));
        addConfigTrackbar(KipConfigValue_t7_tWTR, "t7 tWTR", ValueRange(0, 9, 1));
        addConfigTrackbar(KipConfigValue_t8_tREFI, "t8 tREFI", ValueRange(0, 6, 1));

        /* Yes this is duplicated code, yes I don't care. */
        std::vector<NamedValue> timingTbreakFreqs = {
            NamedValue("Disabled", 0),
            NamedValue("1633 MHz", 1633000),
            NamedValue("1666 MHz", 1666000),
            NamedValue("1700 MHz", 1700000),
            NamedValue("1733 MHz", 1733000),
            NamedValue("1766 MHz", 1766000),
            NamedValue("1800 MHz", 1800000),
            NamedValue("1833 MHz", 1833000),
            NamedValue("1866 MHz", 1866000, "JEDEC."),
            NamedValue("1900 MHz", 1900000),
            NamedValue("1933 MHz", 1933000),
            NamedValue("1966 MHz", 1966000),
            NamedValue("1996 MHz", 1996800, "JEDEC."),
            NamedValue("2000 MHz", 2000000),
            NamedValue("2033 MHz", 2033000),
            NamedValue("2066 MHz", 2066000),
            NamedValue("2100 MHz", 2100000),
            NamedValue("2133 MHz", 2133000, "JEDEC."),
            NamedValue("2166 MHz", 2166000),
            NamedValue("2200 MHz", 2200000),
            NamedValue("2233 MHz", 2233000),
            NamedValue("2266 MHz", 2266000),
            NamedValue("2300 MHz", 2300000),
            NamedValue("2333 MHz", 2333000),
            NamedValue("2366 MHz", 2366000),
            NamedValue("2400 MHz", 2400000, "JEDEC."),
            NamedValue("2433 MHz", 2433000),
            NamedValue("2466 MHz", 2466000),
            NamedValue("2500 MHz", 2500000),
            NamedValue("2533 MHz", 2533000),
            NamedValue("2566 MHz", 2566000),
            NamedValue("2600 MHz", 2600000),
            NamedValue("2633 MHz", 2633000),
            NamedValue("2666 MHz", 2666000, "JEDEC."),
            NamedValue("2700 MHz", 2700000),
            NamedValue("2733 MHz", 2733000),
            NamedValue("2766 MHz", 2766000),
            NamedValue("2800 MHz", 2800000),
            NamedValue("2833 MHz", 2833000),
            NamedValue("2866 MHz", 2866000),
            NamedValue("2900 MHz", 2900000),
            NamedValue("2933 MHz", 2933000, "JEDEC."),
            NamedValue("2966 MHz", 2966000),
            NamedValue("3000 MHz", 3000000),
            NamedValue("3033 MHz", 3033000),
            NamedValue("3066 MHz", 3066000),
            NamedValue("3100 MHz", 3100000),
            NamedValue("3133 MHz", 3133000),
            NamedValue("3166 MHz", 3166000),
            NamedValue("3200 MHz", 3200000, "JEDEC."),
            NamedValue("3233 MHz", 3233000, "High speedo needed"),
            NamedValue("3266 MHz", 3266000, "High speedo needed!"),
            NamedValue("3300 MHz", 3300000, "High speedo needed!"),
            // NamedValue("3333MHz (Needs extreme Speedo/PLL)", 3333000),
            // NamedValue("3366MHz (Needs extreme Speedo/PLL)", 3366000),
            // NamedValue("3400MHz (Needs extreme Speedo/PLL)", 3400000),
            // NamedValue("3433MHz (Needs ridiculous Speedo/PLL)", 3433000),
            // NamedValue("3466MHz (Needs ridiculous Speedo/PLL)", 3466000),
            // NamedValue("3500MHz (Needs ridiculous Speedo/PLL)", 3500000),
        };
        RamDisplayUnit unit = (RamDisplayUnit)this->configList->values[HocClkConfigValue_RamDisplayUnit];

        for (size_t i = 1; i < timingTbreakFreqs.size(); ++i) {
            auto &nv = timingTbreakFreqs[i];
            nv.name = formatMemClockKhzLabel(nv.value, unit);
        }

        ValueThresholds thresholdsDisabled(0, 0);
        this->listElement->addItem(new CompactCategoryHeader("Advanced"));
        if (IsMariko()) {
            // tBreak / low-high timing graph (live, reads config each frame)
            {
                HocClkConfigValueList *cfgPtr = this->configList;
                auto *tbreakGraph = new tsl::elm::CustomDrawer([cfgPtr](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
                    const s32 t6 = (s32)cfgPtr->values[KipConfigValue_t6_tRTW];
                    const s32 t7 = (s32)cfgPtr->values[KipConfigValue_t7_tWTR];
                    const s32 lt6 = (s32)cfgPtr->values[KipConfigValue_low_t6_tRTW];
                    const s32 lt7 = (s32)cfgPtr->values[KipConfigValue_low_t7_tWTR];
                    const s32 t2c = (s32)cfgPtr->values[KipConfigValue_t2_tRP_cap];
                    const uint32_t tbk = (uint32_t)cfgPtr->values[KipConfigValue_timingEmcTbreak];

                    const tsl::Color cT6 = tsl::Color(4, 14, 15, 15);
                    const tsl::Color cT7 = tsl::Color(15, 9, 2, 15);
                    const tsl::Color cT2 = tsl::Color(12, 4, 15, 15);
                    const tsl::Color cAxis = tsl::Color(5, 5, 5, 15);
                    const tsl::Color cTbk = tsl::Color(7, 7, 7, 10);

                    const s32 gx = x + 52;
                    const s32 gw = w - 64;
                    const s32 gy = y + 14;
                    const s32 gh = 72;
                    const s32 axisY = gy + gh;

                    // Y: value 0 = bottom, value 9 = top
                    auto valY = [&](s32 v) -> s32 { return axisY - v * gh / 9; };

                    constexpr uint32_t kRMin = 1600000u, kRMax = 3300000u;
                    auto freqX = [&](uint32_t kHz) -> s32 {
                        if (kHz <= kRMin)
                            return gx;
                        if (kHz >= kRMax)
                            return gx + gw;
                        return gx + (s32)((uint64_t)(kHz - kRMin) * (uint32_t)gw / (kRMax - kRMin));
                    };

                    // Y-axis guide lines at 0, 3, 6, 9
                    for (int v : { 0, 3, 6, 9 }) {
                        char buf[4];
                        snprintf(buf, sizeof(buf), "%d", v);
                        renderer->drawString(buf, false, x + 4, valY(v) + 5, 12, cAxis);
                        renderer->drawRect(gx, valY(v), gw, 1, tsl::Color(3, 3, 3, 15));
                    }
                    renderer->drawRect(gx, gy, 1, gh + 1, cAxis);
                    renderer->drawRect(gx, axisY, gw, 1, cAxis);

                    // tBreak vertical divider
                    if (tbk != 0) {
                        s32 tx = freqX(tbk);
                        renderer->drawRect(tx, gy, 1, gh, cTbk);
                    }

                    // Step line: lowVal below tBreak, hiVal at/above tBreak
                    auto drawTimingLine = [&](s32 lowVal, s32 hiVal, const tsl::Color &c) {
                        if (tbk == 0 || tbk <= kRMin) {
                            s32 yy = valY(hiVal) + 1;
                            renderer->drawRect(gx, yy, gw, 2, c);
                            renderer->drawCircle(gx, yy + 1, 3, true, c);
                            renderer->drawCircle(gx + gw - 1, yy + 1, 3, true, c);
                        } else {
                            s32 tx = freqX(tbk);
                            s32 yLow = valY(lowVal) + 1;
                            s32 yHi = valY(hiVal) + 1;
                            renderer->drawRect(gx, yLow, tx - gx, 2, c);
                            renderer->drawRect(tx, yHi, gx + gw - tx, 2, c);
                            if (yLow != yHi) {
                                s32 topY = yLow < yHi ? yLow : yHi;
                                s32 botY = yLow > yHi ? yLow : yHi;
                                renderer->drawRect(tx, topY, 2, botY - topY + 2, c);
                            }
                            renderer->drawCircle(gx, yLow + 1, 3, true, c);
                            renderer->drawCircle(tx, yLow + 1, 3, true, c);
                            renderer->drawCircle(tx, yHi + 1, 3, true, c);
                            renderer->drawCircle(gx + gw - 1, yHi + 1, 3, true, c);
                        }
                    };

                    drawTimingLine(lt6, t6, cT6);
                    drawTimingLine(lt7, t7, cT7);

                    // t2 tRP cap: constant line
                    s32 yT2 = valY(t2c) + 1;
                    renderer->drawRect(gx, yT2, gw, 2, cT2);
                    renderer->drawCircle(gx, yT2 + 1, 3, true, cT2);
                    renderer->drawCircle(gx + gw - 1, yT2 + 1, 3, true, cT2);

                    // X-axis ruler with sideways bitmap-font labels
                    static const uint8_t kDigBmp[10][5] = {
                        { 7, 5, 5, 5, 7 }, { 6, 2, 2, 2, 7 }, { 7, 1, 7, 4, 7 }, { 7, 1, 3, 1, 7 }, { 5, 5, 7, 1, 1 },
                        { 7, 4, 7, 1, 7 }, { 7, 4, 7, 5, 7 }, { 7, 1, 1, 2, 2 }, { 7, 5, 7, 5, 7 }, { 7, 5, 7, 1, 7 },
                    };
                    const s32 pix = 2, charH = 3 * pix, charW = 5 * pix, charGap = 1;
                    auto drawSidewaysMHz = [&](uint32_t mhz, s32 cx, s32 startY, const tsl::Color &c) {
                        char buf[8];
                        snprintf(buf, sizeof(buf), "%u", mhz);
                        s32 ox = cx - charW / 2;
                        for (int ci = 0; buf[ci]; ci++) {
                            int d = buf[ci] - '0';
                            if (d < 0 || d > 9)
                                continue;
                            s32 cy = startY + ci * (charH + charGap);
                            for (int r = 0; r < 5; r++)
                                for (int col = 0; col < 3; col++)
                                    if ((kDigBmp[d][r] >> (2 - col)) & 1)
                                        renderer->drawRect(ox + (4 - r) * pix, cy + col * pix, pix, pix, c);
                        }
                    };
                    static const uint32_t kRulerMHz[] = {
                        1600, 1733, 1866, 2000, 2133, 2266, 2400, 2533, 2666, 2800, 2933, 3066, 3200, 3300,
                    };
                    for (uint32_t mhz : kRulerMHz) {
                        s32 fx = freqX(mhz * 1000u);
                        renderer->drawRect(fx, axisY, 1, 4, cAxis);
                        drawSidewaysMHz(mhz, fx, axisY + 6, cAxis);
                    }

                    // Legend
                    s32 ly = y + h - 14;
                    renderer->drawRect(gx, ly, 14, 3, cT6);
                    renderer->drawString("t6 tRTW", false, gx + 17, ly + 5, 12, cT6);
                    renderer->drawRect(gx + 80, ly, 14, 3, cT7);
                    renderer->drawString("t7 tWTR", false, gx + 97, ly + 5, 12, cT7);
                    renderer->drawRect(gx + 165, ly, 14, 3, cT2);
                    renderer->drawString("t2 cap", false, gx + 182, ly + 5, 12, cT2);
                });
                tbreakGraph->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 150);
                this->listElement->addItem(tbreakGraph);
            }

            addConfigButton(KipConfigValue_timingEmcTbreak, "RAM-Timing tBreak", ValueRange(0, 1, 1, "", 1), "tBreak", &thresholdsDisabled, {},
                            timingTbreakFreqs, false, true);
            addConfigTrackbar(KipConfigValue_low_t1_tRCD, "Low t1 tRCD", ValueRange(0, 7, 1));
            addConfigTrackbar(KipConfigValue_low_t2_tRP, "Low t2 tRP", ValueRange(0, 7, 1));
            addConfigTrackbar(KipConfigValue_low_t3_tRAS, "Low t3 tRAS", ValueRange(0, 9, 1));
            addConfigTrackbar(KipConfigValue_low_t4_tRRD, "Low t4 tRRD", ValueRange(0, 6, 1));
            addConfigTrackbar(KipConfigValue_low_t5_tRFC, "Low t5 tRFC", ValueRange(0, 10, 1));
            addConfigTrackbar(KipConfigValue_low_t6_tRTW, "Low t6 tRTW", ValueRange(0, 9, 1));
            addConfigTrackbar(KipConfigValue_low_t7_tWTR, "Low t7 tWTR", ValueRange(0, 9, 1));
            addConfigTrackbar(KipConfigValue_low_t8_tREFI, "Low t8 tREFI", ValueRange(0, 6, 1));
            {
                auto *spacer = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *, s32, s32, s32, s32) {});
                spacer->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 8);
                this->listElement->addItem(spacer);
            }
            addConfigTrackbar(KipConfigValue_t2_tRP_cap, "1333WL t2 RP Cap", ValueRange(0, 8, 1));
        }
        addMappedConfigTrackbar(KipConfigValue_t6_tRTW_fine_tune, "t6 tRTW Fine Tune", { 0xFFFFFFFEu, 0xFFFFFFFFu, 0u, 1u, 2u },
                                { "-2", "-1", " 0", "+1", "+2" });
        addMappedConfigTrackbar(KipConfigValue_t7_tWTR_fine_tune, "t7 tWTR Fine Tune", { 0xFFFFFFFDu, 0xFFFFFFFEu, 0xFFFFFFFFu, 0u, 1u, 2u, 3u },
                                { "-3", "-2", "-1", " 0", "+1", "+2", "+3" });
    }
};

class RamLatenciesSubmenuGui : public MiscGui {
    public:
    RamLatenciesSubmenuGui() {
    }

    bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                      HidAnalogStickState rightJoyStick) override {
        if (keysDown & KEY_B) {
            triggerExitFeedback();
            lastItemName = "RAM Latency Editor";
            tsl::swapTo<RamSubmenuGui>();
            return true;
        }
        return false;
    }

    tsl::elm::Element *baseUI() override {
        auto *list = new TopAnchoredList();
        this->listElement = list;
        this->listUI();
        return list;
    }

    protected:
    void normalizeLatencies(const HocClkConfigValue keysArr[4]) {
        uint32_t maxClock = IsMariko() ? (uint32_t)this->configList->values[KipConfigValue_marikoEmcMaxClock]
                                       : (uint32_t)this->configList->values[KipConfigValue_eristaEmcMaxClock];
        uint32_t vals[4];

        for (int i = 0; i < 4; i++) {
            vals[i] = (uint32_t)this->configList->values[keysArr[i]];
            if (vals[i] == 0xFFFFFFFFu)
                vals[i] = maxClock;
        }

        uint32_t currentLimit = 0;
        for (int i = 3; i >= 0; i--) {
            if (vals[i] != 0) {
                if (currentLimit != 0 && vals[i] > currentLimit) {
                    vals[i] = currentLimit;
                }
                currentLimit = vals[i];
            }
        }

        uint32_t last = 0;
        for (int i = 0; i < 4; i++) {
            if (vals[i] == 0)
                continue;

            if (vals[i] < last)
                vals[i] = last;
            if (vals[i] > maxClock)
                vals[i] = maxClock;

            last = vals[i];
        }

        for (int i = 0; i < 4; i++) {
            this->configList->values[keysArr[i]] = vals[i];
        }
    }

    void listUI() override {
        ValueThresholds thresholdsDisabled(0, 0);
        Result rc = hocclkIpcGetConfigValues(this->configList);
        if (R_FAILED(rc)) [[unlikely]] {
            FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
            return;
        }

        uint32_t maxClock = IsMariko() ? (uint32_t)this->configList->values[KipConfigValue_marikoEmcMaxClock]
                                       : (uint32_t)this->configList->values[KipConfigValue_eristaEmcMaxClock];
        RamDisplayUnit unit = (RamDisplayUnit)this->configList->values[HocClkConfigValue_RamDisplayUnit];

        static std::vector<uint32_t> kFreqOptions = {};
        if (IsMariko()) {
            kFreqOptions = {
                1633000, 1666000, 1700000, 1733000, 1766000, 1800000, 1833000, 1866000, 1900000, 1933000, 1966000, 1996800, 2000000,
                2033000, 2066000, 2100000, 2133000, 2166000, 2200000, 2233000, 2266000, 2300000, 2333000, 2366000, 2400000, 2433000,
                2466000, 2500000, 2533000, 2566000, 2600000, 2633000, 2666000, 2700000, 2733000, 2766000, 2800000, 2833000, 2866000,
                2900000, 2933000, 2966000, 3000000, 3033000, 3066000, 3100000, 3133000, 3166000, 3200000, 3233000, 3266000, 3300000,
            };
        } else {
            kFreqOptions = {
                1633000, 1666000, 1700000, 1733000, 1800000, 1833000, 1862400, 1881600, 1900800, 1920000, 1939200, 1958400,
                1977600, 1996800, 2016000, 2035200, 2054400, 2073600, 2092800, 2112000, 2131200, 2150400, 2169600, 2188800,
                2208000, 2227200, 2246400, 2265600, 2284800, 2304000, 2323200, 2342400, 2361600, 2380800, 2400000,
            };
        }

        static const HocClkConfigValue kLatencyRKeys[4] = {
            KipConfigValue_read_latency_1333,
            KipConfigValue_read_latency_1600,
            KipConfigValue_read_latency_1866,
            KipConfigValue_read_latency_2133,
        };
        static const HocClkConfigValue kLatencyWKeys[4] = {
            KipConfigValue_write_latency_1333,
            KipConfigValue_write_latency_1600,
            KipConfigValue_write_latency_1866,
            KipConfigValue_write_latency_2133,
        };

        static const char *kTierLabels[4] = { "1333 Latency Max", "1600 Latency Max", "1866 Latency Max", "2133 Latency Max" };

        auto buildNamedValues = [&](int tierIdx) -> std::vector<NamedValue> {
            std::vector<NamedValue> nv;
            nv.push_back(NamedValue("-", 0u));
            if (tierIdx == 3) {
                nv.push_back(NamedValue(formatMemClockKhzLabel(maxClock, unit), maxClock));
                nv.push_back(NamedValue(formatMemClockKhzLabel(maxClock, unit), 0xFFFFFFFFu));
            } else {
                for (uint32_t freq : kFreqOptions) {
                    if (freq > maxClock)
                        continue;
                    nv.push_back(NamedValue(formatMemClockKhzLabel(freq, unit), freq));
                }
                nv.push_back(NamedValue(formatMemClockKhzLabel(maxClock, unit), maxClock));
                nv.push_back(NamedValue(formatMemClockKhzLabel(maxClock, unit), 0xFFFFFFFFu));
            }
            return nv;
        };

        auto makeValueText = [&](uint32_t rawVal) -> std::string {
            if (rawVal == 0)
                return "-";
            if (rawVal == 0xFFFFFFFFu)
                return formatMemClockKhzLabel(maxClock, unit);
            return formatMemClockKhzLabel(rawVal, unit);
        };

        auto addLatencyRow = [&](const char *label, int tierIdx, const HocClkConfigValue keysArr[4]) {
            HocClkConfigValue thisKey = keysArr[tierIdx];
            uint32_t currentVal = (uint32_t)this->configList->values[thisKey];

            tsl::elm::ListItem *item = new tsl::elm::ListItem(label);
            item->setValue(makeValueText(currentVal));

            item->setClickListener([this, tierIdx, thisKey, keysArr, label](u64 keys) -> bool {
                auto infoStrings = ConfigInfoStrings(thisKey, IsMariko(), IsHoag());
                if (!infoStrings.empty() && (keys & HidNpadButton_Y) && !(keys & ~HidNpadButton_Y)) {
                    tsl::changeTo<InfoGui>(std::string(label), infoStrings);
                    return true;
                }
                if ((keys & HidNpadButton_A) == 0)
                    return false;

                uint32_t vals[4];
                for (int i = 0; i < 4; i++)
                    vals[i] = (uint32_t)this->configList->values[keysArr[i]];

                uint32_t maxClock = IsMariko() ? (uint32_t)this->configList->values[KipConfigValue_marikoEmcMaxClock]
                                               : (uint32_t)this->configList->values[KipConfigValue_eristaEmcMaxClock];
                RamDisplayUnit unit = (RamDisplayUnit)this->configList->values[HocClkConfigValue_RamDisplayUnit];

                auto resolveVal = [maxClock](uint32_t v) -> uint32_t { return (v == 0xFFFFFFFFu) ? maxClock : v; };

                if (tierIdx == 3) {
                    bool maxOccupied = false;
                    for (int i = 0; i < 3; i++) {
                        if (resolveVal(vals[i]) == maxClock) {
                            maxOccupied = true;
                            break;
                        }
                    }

                    std::vector<NamedValue> opts;
                    opts.push_back(NamedValue("-", 0u));

                    if (!maxOccupied) {
                        opts.push_back(NamedValue(formatMemClockKhzLabel(maxClock, unit), maxClock));
                    }

                    uint32_t displayCurrent = resolveVal(vals[3]);
                    if (maxOccupied && displayCurrent == maxClock) {
                        displayCurrent = 0;
                    }

                    tsl::changeTo<ValueChoiceGui>(
                        displayCurrent, ValueRange(0, 0, 1, "", 1), std::string("2133 Latency Max"),
                        [this, thisKey, keysArr](uint32_t chosen) -> bool {
                            this->configList->values[thisKey] = chosen;
                            Result rc = hocclkIpcSetConfigValues(this->configList);
                            if (R_FAILED(rc)) {
                                FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
                                return false;
                            }
                            shouldSaveKip = true;
                            this->lastContextUpdate = armGetSystemTick();
                            return true;
                        },
                        ValueThresholds(), false, std::map<uint32_t, std::string>{}, opts, false, false);
                    return true;
                }

                uint32_t lowerBound = 0;
                for (int i = 0; i < tierIdx; i++) {
                    uint32_t v = resolveVal(vals[i]);
                    if (v != 0 && v > lowerBound)
                        lowerBound = v;
                }

                uint32_t upperBound = 0;
                for (int i = tierIdx + 1; i < 4; i++) {
                    uint32_t v;
                    if (i == 3) {
                        uint32_t r = resolveVal(vals[i]);
                        v = (r != 0) ? maxClock : 0;
                    } else {
                        v = resolveVal(vals[i]);
                    }
                    if (v != 0 && (upperBound == 0 || v < upperBound))
                        upperBound = v;
                }

                std::vector<NamedValue> opts;
                opts.push_back(NamedValue("-", 0u));
                for (uint32_t freq : kFreqOptions) {
                    if (freq <= lowerBound)
                        continue;
                    if (freq > maxClock)
                        continue;
                    if (upperBound != 0 && freq >= upperBound)
                        continue;
                    opts.push_back(NamedValue(formatMemClockKhzLabel(freq, unit), freq));
                }

                uint32_t displayCurrent = resolveVal(vals[tierIdx]);
                bool currentInList = false;
                for (auto &nv : opts)
                    if (nv.value == displayCurrent) {
                        currentInList = true;
                        break;
                    }
                if (!currentInList)
                    displayCurrent = 0;

                tsl::changeTo<ValueChoiceGui>(
                    displayCurrent, ValueRange(0, 0, 1, "", 1), std::string("Latency Max"),
                    [this, thisKey, keysArr](uint32_t chosen) -> bool {
                        this->configList->values[thisKey] = chosen;
                        normalizeLatencies(keysArr);
                        Result rc = hocclkIpcSetConfigValues(this->configList);
                        if (R_FAILED(rc)) {
                            FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
                            return false;
                        }
                        shouldSaveKip = true;
                        this->lastContextUpdate = armGetSystemTick();
                        return true;
                    },
                    ValueThresholds(), false, std::map<uint32_t, std::string>{}, opts, false, false);
                return true;
            });

            this->listElement->addItem(item);
            this->configButtons[thisKey] = item;
            this->configRanges[thisKey] = ValueRange(0, 0, 1, "", 1);
            this->configNamedValues[thisKey] = buildNamedValues(tierIdx);
        };

        this->listElement->addItem(new CompactCategoryHeader("Latency Graph"));

        {
            HocClkConfigValueList *cfgPtr = this->configList;
            bool mariko = IsMariko();

            auto *graph = new tsl::elm::CustomDrawer([cfgPtr, mariko](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
                static const HocClkConfigValue kR[4] = {
                    KipConfigValue_read_latency_1333,
                    KipConfigValue_read_latency_1600,
                    KipConfigValue_read_latency_1866,
                    KipConfigValue_read_latency_2133,
                };
                static const HocClkConfigValue kW[4] = {
                    KipConfigValue_write_latency_1333,
                    KipConfigValue_write_latency_1600,
                    KipConfigValue_write_latency_1866,
                    KipConfigValue_write_latency_2133,
                };

                uint32_t capMax =
                    mariko ? (uint32_t)cfgPtr->values[KipConfigValue_marikoEmcMaxClock] : (uint32_t)cfgPtr->values[KipConfigValue_eristaEmcMaxClock];

                uint32_t rv[4], wv[4];
                for (int i = 0; i < 4; i++) {
                    rv[i] = (uint32_t)cfgPtr->values[kR[i]];
                    wv[i] = (uint32_t)cfgPtr->values[kW[i]];
                    if (rv[i] == 0xFFFFFFFFu)
                        rv[i] = capMax;
                    if (wv[i] == 0xFFFFFFFFu)
                        wv[i] = capMax;
                }

                const tsl::Color cRead = tsl::Color(4, 14, 15, 15);
                const tsl::Color cWrite = tsl::Color(15, 9, 2, 15);
                const tsl::Color cMerge = tsl::Color(5, 15, 4, 15);
                const tsl::Color cAxis = tsl::Color(5, 5, 5, 15);

                const s32 gx = x + (mariko ? 52 : 47);
                const s32 gw = w - (mariko ? 64 : 59);
                const s32 gy = y + 14;
                const s32 gh = 54;
                const s32 th = gh / 3;
                const s32 axisY = gy + gh;

                auto tierY = [&](int i) -> s32 { return gy + gh - i * th; };
                const uint32_t kRMin = 1600000u;
                const uint32_t kRMax = mariko ? 3300000u : 2400000u;
                auto freqX = [&](uint32_t kHz) -> s32 {
                    if (kHz <= kRMin)
                        return gx;
                    if (kHz >= kRMax)
                        return gx + gw;
                    return gx + (s32)((uint64_t)(kHz - kRMin) * (uint32_t)gw / (kRMax - kRMin));
                };

                const char *tierLabels[4] = { "1333", "1600", "1866", "2133" };
                for (int i = 0; i < 4; i++) {
                    renderer->drawString(tierLabels[i], false, x + 4, tierY(i) + 5, 12, cAxis);
                    renderer->drawRect(gx, tierY(i), gw, 1, cAxis);
                }
                renderer->drawRect(gx, gy, 1, gh + 1, cAxis);
                renderer->drawRect(gx, axisY, gw, 1, cAxis);

                struct LatSeg {
                    int tier;
                    uint32_t start, end;
                };
                auto buildSegs = [&](const uint32_t *vals) -> std::vector<LatSeg> {
                    struct Pt {
                        int tier;
                        uint32_t freq;
                    };
                    Pt pts[4];
                    int n = 0;
                    for (int i = 0; i < 4; i++)
                        if (vals[i] != 0)
                            pts[n++] = { i, vals[i] };
                    if (n == 0)
                        return {};
                    std::vector<LatSeg> segs;
                    uint32_t prev = kRMin;
                    for (int k = 0; k < n; k++) {
                        if (pts[k].freq > prev)
                            segs.push_back({ pts[k].tier, prev, pts[k].freq });
                        prev = pts[k].freq;
                    }
                    if (prev < kRMax)
                        segs.push_back({ pts[n - 1].tier, prev, kRMax });
                    return segs;
                };

                auto rSegs = buildSegs(rv);
                auto wSegs = buildSegs(wv);

                auto drawSeriesSegs = [&](const std::vector<LatSeg> &segs, const std::vector<LatSeg> &other, const tsl::Color &c, s32 yOff) {
                    for (const auto &seg : segs) {
                        s32 ty = tierY(seg.tier) + yOff;
                        s32 tyMrg = tierY(seg.tier);
                        struct Iv {
                            uint32_t s, e;
                        };
                        Iv ovlp[4];
                        int no = 0;
                        for (const auto &os : other) {
                            if (os.tier != seg.tier)
                                continue;
                            uint32_t s = seg.start > os.start ? seg.start : os.start;
                            uint32_t e = seg.end < os.end ? seg.end : os.end;
                            if (s < e)
                                ovlp[no++] = { s, e };
                        }
                        for (int a = 1; a < no; a++)
                            for (int b = a; b > 0 && ovlp[b - 1].s > ovlp[b].s; b--) {
                                auto t = ovlp[b];
                                ovlp[b] = ovlp[b - 1];
                                ovlp[b - 1] = t;
                            }
                        uint32_t cur = seg.start;
                        for (int oi = 0; oi < no; oi++) {
                            if (cur < ovlp[oi].s) {
                                s32 x0 = freqX(cur), x1 = freqX(ovlp[oi].s);
                                if (x1 > x0)
                                    renderer->drawRect(x0, ty, x1 - x0, 2, c);
                            }
                            s32 x0 = freqX(ovlp[oi].s), x1 = freqX(ovlp[oi].e);
                            if (x1 > x0)
                                renderer->drawRect(x0, tyMrg, x1 - x0, 2, cMerge);
                            cur = ovlp[oi].e;
                        }
                        if (cur < seg.end) {
                            s32 x0 = freqX(cur), x1 = freqX(seg.end);
                            if (x1 > x0)
                                renderer->drawRect(x0, ty, x1 - x0, 2, c);
                        }
                    }
                    for (int k = 0; k + 1 < (int)segs.size(); k++) {
                        if (segs[k].end != segs[k + 1].start)
                            continue;
                        uint32_t transFreq = segs[k].end;
                        bool otherHere = false;
                        for (int j = 0; j + 1 < (int)other.size(); j++)
                            if (other[j].end == transFreq && other[j + 1].start == transFreq) {
                                otherHere = true;
                                break;
                            }
                        s32 fx = freqX(transFreq);
                        s32 y1 = tierY(segs[k].tier) + yOff;
                        s32 y2 = tierY(segs[k + 1].tier) + yOff;
                        s32 topY = y1 < y2 ? y1 : y2;
                        s32 botY = y1 > y2 ? y1 : y2;
                        if (botY > topY)
                            renderer->drawRect(fx, topY, 2, botY - topY + 2, otherHere ? cMerge : c);
                    }
                };

                drawSeriesSegs(rSegs, wSegs, cRead, 0);
                drawSeriesSegs(wSegs, rSegs, cWrite, 0);

                static const uint8_t kDigBmp[10][5] = {
                    { 7, 5, 5, 5, 7 },  // 0
                    { 6, 2, 2, 2, 7 },  // 1
                    { 7, 1, 7, 4, 7 },  // 2
                    { 7, 1, 3, 1, 7 },  // 3
                    { 5, 5, 7, 1, 1 },  // 4
                    { 7, 4, 7, 1, 7 },  // 5
                    { 7, 4, 7, 5, 7 },  // 6
                    { 7, 1, 1, 2, 2 },  // 7
                    { 7, 5, 7, 5, 7 },  // 8
                    { 7, 5, 7, 1, 7 },  // 9
                };
                const s32 charGap = 1;

                auto drawSidewaysMHz = [&](uint32_t mhz, s32 cx, s32 startY, const tsl::Color &c, s32 pixSize) {
                    char buf[8];
                    snprintf(buf, sizeof(buf), "%u", mhz);
                    s32 cW = 5 * pixSize;
                    s32 cH = 3 * pixSize;
                    s32 originX = cx - cW / 2;
                    for (int ci = 0; buf[ci]; ci++) {
                        int d = buf[ci] - '0';
                        if (d < 0 || d > 9)
                            continue;
                        s32 cy = startY + ci * (cH + charGap);
                        for (int r = 0; r < 5; r++) {
                            for (int col = 0; col < 3; col++) {
                                if (!((kDigBmp[d][r] >> (2 - col)) & 1))
                                    continue;
                                renderer->drawRect(originX + (4 - r) * pixSize, cy + col * pixSize, pixSize, pixSize, c);
                            }
                        }
                    }
                };

                if (mariko) {
                    static const uint32_t kRulerMHz[] = {
                        1600, 1733, 1866, 2000, 2133, 2266, 2400, 2533, 2666, 2800, 2933, 3066, 3200, 3300,
                    };
                    for (uint32_t mhz : kRulerMHz) {
                        s32 fx = freqX(mhz * 1000u);
                        renderer->drawRect(fx, axisY, 1, 4, cAxis);
                        drawSidewaysMHz(mhz, fx, axisY + 6, cAxis, 2);
                    }
                } else {
                    static const uint32_t kEristaRulerMHz[] = {
                        1600, 1666, 1733, 1800, 1866, 1933, 2000, 2066, 2133, 2200, 2266, 2333, 2400,
                    };
                    for (uint32_t mhz : kEristaRulerMHz) {
                        s32 fx = freqX(mhz * 1000u);
                        renderer->drawRect(fx, axisY, 1, 4, cAxis);
                        drawSidewaysMHz(mhz, fx, axisY + 6, cAxis, 2);
                    }
                }

                // Breakpoint dots
                for (int i = 0; i < 4; i++) {
                    s32 ty = tierY(i) + 1;
                    bool merged = (rv[i] != 0 && rv[i] == wv[i]);
                    if (merged) {
                        renderer->drawCircle(freqX(rv[i]), ty, 4, true, cMerge);
                    } else {
                        if (rv[i])
                            renderer->drawCircle(freqX(rv[i]), ty, 4, true, cRead);
                        if (wv[i])
                            renderer->drawCircle(freqX(wv[i]), ty, 4, true, cWrite);
                    }
                }

                const s32 ly = axisY + 46;
                renderer->drawRect(gx, ly, 14, 3, cRead);
                renderer->drawString("Read", false, gx + 17, ly + 5, 12, cRead);
                renderer->drawRect(gx + 60, ly, 14, 3, cWrite);
                renderer->drawString("Write", false, gx + 77, ly + 5, 12, cWrite);
                renderer->drawRect(gx + 125, ly, 14, 3, cMerge);
                renderer->drawString("Same", false, gx + 142, ly + 5, 12, cMerge);
            });
            graph->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 150);
            this->listElement->addItem(graph);
        }

        this->listElement->addItem(new CompactCategoryHeader("Read Latency"));
        for (int i = 0; i < 4; i++)
            addLatencyRow(kTierLabels[i], i, kLatencyRKeys);

        this->listElement->addItem(new CompactCategoryHeader("Write Latency"));
        for (int i = 0; i < 4; i++)
            addLatencyRow(kTierLabels[i], i, kLatencyWKeys);
    }
};

class CpuSubmenuGui : public MiscGui {
    public:
    CpuSubmenuGui() {
    }

    bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                      HidAnalogStickState rightJoyStick) override {
        if (keysDown & KEY_B) {
            triggerExitFeedback();
            lastItemName = "CPU Settings";
            tsl::swapTo<MiscGui>();
            return true;
        }
        return false;
    }

    protected:
    void listUI() override {
        Result rc = hocclkIpcGetConfigValues(this->configList);  // populate config list early otherwise wont work
        if (R_FAILED(rc)) [[unlikely]] {
            FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
            return;
        }

        ValueThresholds thresholdsDisabled(0, 0);
        ValueThresholds mCpuClockThresholds(1963500, 2397000);
        ValueThresholds mCpuClockThresholdsUV(2397000, 2499000);
        ValueThresholds eCpuClockThresholds(1785000, 2091000);
        ValueThresholds eCpuClockThresholdsUV(2091000, 2193000);

        this->listElement->addItem(new CompactCategoryHeader("CPU Settings"));
        if (IsMariko()) {
            addConfigTrackbar(KipConfigValue_marikoCpuUVLow, "CPU Low UV", ValueRange(0, 8, 1));
            addConfigTrackbar(KipConfigValue_marikoCpuUVHigh, "CPU High UV", ValueRange(0, 12, 1));

            std::vector<NamedValue> marikoTableConf = { // NamedValue("Auto", 0),
                                                        NamedValue("Default", 1), NamedValue("1581MHz Tbreak", 2), NamedValue("1683MHz Tbreak", 3),
                                                        NamedValue("Extreme UV Table", 4)
            };

            addConfigButton(KipConfigValue_tableConf, "CPU UV Table", ValueRange(0, 12, 1, "", 0), "CPU UV Table", &thresholdsDisabled, {},
                            marikoTableConf, false, true);

            addConfigButton(KipConfigValue_marikoCpuLowVmin, "CPU Low VMIN", ValueRange(550, 750, 5, "mV", 1), "CPU VMIN", &thresholdsDisabled, {},
                            {}, false, true);

            addConfigButton(KipConfigValue_marikoCpuHighVmin, "CPU High VMIN", ValueRange(650, 900, 5, "mV", 1), "CPU VMIN", &thresholdsDisabled, {},
                            {}, false, true);

            ValueThresholds mCpuVoltThresholds(1160, 1180);
            addConfigButton(KipConfigValue_marikoCpuMaxVolt, "CPU Max Voltage", ValueRange(1000, 1200, 5, "mV", 1), "CPU Max Voltage",
                            &mCpuVoltThresholds, {}, {}, false, true);

            std::vector<NamedValue> maxClkOptions = {
                NamedValue("1963 MHz", 1963500, "Rating"), NamedValue("2091 MHz", 2091000), NamedValue("2193 MHz", 2193000), NamedValue("2295 MHz", 2295000),
                NamedValue("2397 MHz", 2397000, "Safe Max"), NamedValue("2499 MHz", 2499000, "Unsafe Max"), NamedValue("2601 MHz", 2601000), NamedValue("2703 MHz", 2703000, "Absolute Max"),
            };

            addConfigButton(KipConfigValue_marikoCpuMaxClock, "CPU Max Clock", ValueRange(0, 0, 1, "", 1), "CPU Max Clock",
                            this->configList->values[KipConfigValue_marikoCpuUVHigh] ? &mCpuClockThresholdsUV : &mCpuClockThresholds, {},
                            maxClkOptions, false, true);

            std::vector<NamedValue> clkOptionsRamOc = {
                NamedValue("1122 MHz", 1122000), NamedValue("1224 MHz", 1224000), NamedValue("1326 MHz", 1326000), NamedValue("1428 MHz", 1428000),
                NamedValue("1581 MHz", 1581000), NamedValue("1683 MHz", 1683000, "Default"), NamedValue("1785 MHz", 1785000), NamedValue("1887 MHz", 1887000),
                NamedValue("1963 MHz", 1963500), NamedValue("2091 MHz", 2091000), NamedValue("2193 MHz", 2193000), NamedValue("2295 MHz", 2295000),
                NamedValue("2397 MHz", 2397000), NamedValue("2499 MHz", 2499000), NamedValue("2601 MHz", 2601000), NamedValue("2703 MHz", 2703000),
            };

            addConfigButton(KipConfigValue_marikoCpuBoostClock, "CPU Boost Clock", ValueRange(0, 0, 1, "", 1), "CPU Boost Clock",
                            this->configList->values[KipConfigValue_marikoCpuUVHigh] ? &mCpuClockThresholdsUV : &mCpuClockThresholds, {}, maxClkOptions,
                            false, true);

            std::vector<NamedValue> emcMaxClock = {
                NamedValue("1600 MHz", 1600000),
                NamedValue("1633 MHz", 1633000),
                NamedValue("1666 MHz", 1666000),
                NamedValue("1700 MHz", 1700000),
                NamedValue("1733 MHz", 1733000),
                NamedValue("1766 MHz", 1766000),
                NamedValue("1800 MHz", 1800000),
                NamedValue("1833 MHz", 1833000),
                NamedValue("1866 MHz", 1866000, "JEDEC."),
                NamedValue("1900 MHz", 1900000),
                NamedValue("1933 MHz", 1933000),
                NamedValue("1966 MHz", 1966000),
                NamedValue("1996 MHz", 1996800, "JEDEC."),
                NamedValue("2000 MHz", 2000000),
                NamedValue("2033 MHz", 2033000),
                NamedValue("2066 MHz", 2066000),
                NamedValue("2100 MHz", 2100000),
                NamedValue("2133 MHz", 2133000, "JEDEC."),
                NamedValue("2166 MHz", 2166000),
                NamedValue("2200 MHz", 2200000),
                NamedValue("2233 MHz", 2233000),
                NamedValue("2266 MHz", 2266000),
                NamedValue("2300 MHz", 2300000),
                NamedValue("2333 MHz", 2333000),
                NamedValue("2366 MHz", 2366000),
                NamedValue("2400 MHz", 2400000, "JEDEC."),
                NamedValue("2433 MHz", 2433000),
                NamedValue("2466 MHz", 2466000),
                NamedValue("2500 MHz", 2500000),
                NamedValue("2533 MHz", 2533000),
                NamedValue("2566 MHz", 2566000),
                NamedValue("2600 MHz", 2600000),
                NamedValue("2633 MHz", 2633000),
                NamedValue("2666 MHz", 2666000, "JEDEC."),
                NamedValue("2700 MHz", 2700000),
                NamedValue("2733 MHz", 2733000),
                NamedValue("2766 MHz", 2766000),
                NamedValue("2800 MHz", 2800000),
                NamedValue("2833 MHz", 2833000),
                NamedValue("2866 MHz", 2866000),
                NamedValue("2900 MHz", 2900000),
                NamedValue("2933 MHz", 2933000, "JEDEC."),
                NamedValue("2966 MHz", 2966000),
                NamedValue("3000 MHz", 3000000),
                NamedValue("3033 MHz", 3033000),
                NamedValue("3066 MHz", 3066000),
                NamedValue("3100 MHz", 3100000),
                NamedValue("3133 MHz", 3133000),
                NamedValue("3166 MHz", 3166000),
                NamedValue("3200 MHz", 3200000, "JEDEC."),
                NamedValue("3233 MHz", 3233000, "High speedo needed!"),
                NamedValue("3266 MHz", 3266000, "High speedo needed!"),
                NamedValue("3300 MHz", 3300000, "High speedo needed!"),
            };
            addConfigToggle(HocClkConfigValue_AutoRAMCPUOverclock, "Auto CPU RAM OC");
            addConfigButton(HocClkConfigValue_AutoRamCpuCpuOCFreq, "Auto CPU RAM OC CPU clock", ValueRange(0, 0, 1, "", 1), "CPU Clock",
                            &thresholdsDisabled, {}, clkOptionsRamOc, false, false);
            addConfigButton(HocClkConfigValue_AutoRamCpuRamOCThreshold, "Auto CPU RAM OC Threshold", ValueRange(0, 0, 1, "", 1), "RAM Clock",
                            &thresholdsDisabled, {}, emcMaxClock, false, false);
        } else {
            addConfigTrackbar(KipConfigValue_eristaCpuUV, "CPU UV", ValueRange(0, 5, 1));

            addConfigToggle(KipConfigValue_eristaCpuUnlock, "CPU Unlock", true);

            addConfigButton(KipConfigValue_eristaCpuVmin, "CPU VMIN", ValueRange(750, 900, 25, "mV", 1), "CPU VMIN", &thresholdsDisabled, {}, {},
                            false, true);

            ValueThresholds eCpuVoltThresholds(1235, 1260);
            addConfigButton(KipConfigValue_eristaCpuMaxVolt, "CPU Max Voltage", ValueRange(1120, 1260, 5, "mV", 1), "CPU Max Voltage",
                            &eCpuVoltThresholds, {}, {}, false, true);

            std::vector<NamedValue> maxClkOptions = {
                NamedValue("1785 MHz", 1785, "Rating"), NamedValue("1887 MHz", 1887), NamedValue("1989 MHz", 1989), NamedValue("2091 MHz", 2091),
                NamedValue("2193 MHz", 2193), NamedValue("2295 MHz", 2295), NamedValue("2397 MHz", 2397),
            };
            ValueThresholds eCpuMaxClockThresholds(1785, 2091);
            addConfigButton(HocClkConfigValue_EristaMaxCpuClock, "CPU Max Clock", ValueRange(0, 0, 1, "", 1), "CPU Max Clock",
                            &eCpuMaxClockThresholds, {}, maxClkOptions, false);
            std::vector<NamedValue> ClkOptionsE = {
                NamedValue("1785 MHz", 1785000), NamedValue("1887 MHz", 1887000), NamedValue("1989 MHz", 1989000), NamedValue("2091 MHz", 2091000),
                NamedValue("2193 MHz", 2193000), NamedValue("2295 MHz", 2295000), NamedValue("2397 MHz", 2397000),
            };
            addConfigButton(KipConfigValue_eristaCpuBoostClock, "CPU Boost Clock", ValueRange(0, 0, 1, "", 1), "CPU Boost Clock",
                            this->configList->values[KipConfigValue_eristaCpuUV] ? &eCpuClockThresholdsUV : &eCpuClockThresholds, {}, ClkOptionsE,
                            false, true);
            addConfigToggle(HocClkConfigValue_LiveCpuUv, nullptr);
        }
        addConfigToggle(HocClkConfigValue_OverwriteBoostMode, nullptr);
    }
};

class GpuSubmenuGui : public MiscGui {
    public:
    GpuSubmenuGui() {
    }

    bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                      HidAnalogStickState rightJoyStick) override {
        if (keysDown & KEY_B) {
            triggerExitFeedback();
            lastItemName = "GPU Settings";
            tsl::swapTo<MiscGui>();
            return true;
        }
        return false;
    }

    protected:
    void listUI() override {
        Result rc = hocclkIpcGetConfigValues(this->configList);
        if (R_FAILED(rc)) [[unlikely]] {
            FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
            return;
        }
        ValueThresholds thresholdsDisabled(0, 0);
        std::vector<NamedValue> noNamedValues = {};

        this->listElement->addItem(new CompactCategoryHeader("GPU Settings"));

        std::vector<NamedValue> gpuUvConfM = {
            NamedValue("No Undervolt", 0, "HOS Default"),
            NamedValue("SLT Table", 1),
            NamedValue("HiOPT Table", 2, "HOC Default"),
            NamedValue("HiOPT - 15mV", 3),
            NamedValue("High UV Table", 4),
        };

        std::vector<NamedValue> gpuUvConfE = {
            NamedValue("No Undervolt", 0),
            NamedValue("SLT Table", 1),
            NamedValue("HiOPT Table", 2),
        };
        std::vector<NamedValue> mGpuVoltsVmin = {
            NamedValue("480mV", 480), NamedValue("485mV", 485), NamedValue("490mV", 490), NamedValue("495mV", 495), NamedValue("500mV", 500),
            NamedValue("505mV", 505), NamedValue("510mV", 510), NamedValue("515mV", 515), NamedValue("520mV", 520), NamedValue("525mV", 525),
            NamedValue("530mV", 530), NamedValue("535mV", 535), NamedValue("540mV", 540), NamedValue("545mV", 545), NamedValue("550mV", 550),
            NamedValue("555mV", 555), NamedValue("560mV", 560), NamedValue("565mV", 565), NamedValue("570mV", 570), NamedValue("575mV", 575),
            NamedValue("580mV", 580), NamedValue("585mV", 585), NamedValue("590mV", 590), NamedValue("595mV", 595), NamedValue("600mV", 600),
            NamedValue("605mV", 605), NamedValue("610mV", 610), NamedValue("615mV", 615), NamedValue("620mV", 620), NamedValue("625mV", 625),
            NamedValue("630mV", 630), NamedValue("635mV", 635), NamedValue("640mV", 640), NamedValue("645mV", 645), NamedValue("650mV", 650),
            NamedValue("655mV", 655), NamedValue("660mV", 660), NamedValue("665mV", 665), NamedValue("670mV", 670), NamedValue("675mV", 675),
            NamedValue("680mV", 680), NamedValue("685mV", 685), NamedValue("690mV", 690), NamedValue("695mV", 695), NamedValue("700mV", 700),
            NamedValue("705mV", 705), NamedValue("710mV", 710), NamedValue("715mV", 715), NamedValue("720mV", 720), NamedValue("725mV", 725),
            NamedValue("730mV", 730), NamedValue("735mV", 735), NamedValue("740mV", 740), NamedValue("745mV", 745), NamedValue("750mV", 750),
            NamedValue("755mV", 755), NamedValue("760mV", 760), NamedValue("765mV", 765), NamedValue("770mV", 770), NamedValue("775mV", 775),
            NamedValue("780mV", 780), NamedValue("785mV", 785), NamedValue("790mV", 790), NamedValue("795mV", 795)
        };

        if (IsErista()) {
            addConfigButton(KipConfigValue_eristaGpuUV, "GPU Undervolt Table", ValueRange(0, 1, 1, "", 1), "GPU Undervolt Table", &thresholdsDisabled,
                            {}, gpuUvConfE, false, true);
            addConfigButton(KipConfigValue_eristaGpuVmin, "GPU Minimum Voltage", ValueRange(675, 875, 5, "mV", 1), "GPU Minimum Voltage",
                            &thresholdsDisabled, {}, {}, false, true);
        } else {
            addConfigButton(KipConfigValue_marikoGpuUV, "GPU Undervolt Table", ValueRange(0, 1, 1, "", 1), "GPU Undervolt Table", &thresholdsDisabled,
                            {}, gpuUvConfM, false, true);

            // tsl::elm::ListItem* vminCalcBtn = new tsl::elm::ListItem("Calculate GPU Vmin");
            // vminCalcBtn->setClickListener([this](u64 keys) {
            //     if (keys & HidNpadButton_A) {
            //         Result rc = hocClkIpcCalculateGpuVmin();
            //         if (R_FAILED(rc)) {
            //             FatalGui::openWithResultCode("hocClkIpcCalculateGpuVmin", rc);
            //             return false;
            //         }
            //         return true;
            //     }
            //     return false;
            // });

            addConfigButton(KipConfigValue_marikoGpuVmin, "GPU VMIN", ValueRange(0, 0, 0, "0", 1), "GPU VMIN", &thresholdsDisabled, {}, mGpuVoltsVmin,
                            false, true);
            ValueThresholds MgpuVmaxThresholds(805, 850);
            addConfigButton(KipConfigValue_marikoGpuVmax, "GPU Maximum Voltage", ValueRange(800, 960, 5, "mV", 1), "GPU Maximum Voltage",
                            &MgpuVmaxThresholds, {}, {}, false, true);
        }

        std::vector<NamedValue> gpuOffset = {
            NamedValue("-50 mV", static_cast<u32>(-50)),
            NamedValue("-45 mV", static_cast<u32>(-45)),
            NamedValue("-40 mV", static_cast<u32>(-40)),
            NamedValue("-35 mV", static_cast<u32>(-35)),
            NamedValue("-30 mV", static_cast<u32>(-30)),
            NamedValue("-25 mV", static_cast<u32>(-25)),
            NamedValue("-20 mV", static_cast<u32>(-20)),
            NamedValue("-15 mV", static_cast<u32>(-15)),
            NamedValue("-10 mV", static_cast<u32>(-10)),
            NamedValue("-5 mV", static_cast<u32>(-5)),
            NamedValue("0 mV", 0),
            NamedValue("5 mV", 5),
            NamedValue("10 mV", 10),
            NamedValue("15 mV", 15),
            NamedValue("20 mV", 20),
            NamedValue("25 mV", 25),
            NamedValue("30 mV", 30),
            NamedValue("35 mV", 35),
            NamedValue("40 mV", 40),
            NamedValue("45 mV", 45),
            NamedValue("50 mV", 50),
        };

        addConfigButton(KipConfigValue_commonGpuVoltOffset, "GPU Voltage Offset", ValueRange(0, 50, 5, "mV", 1), "GPU Voltage Offset",
                        &thresholdsDisabled, {}, gpuOffset, false, true);

        std::vector<NamedValue> gpuSchedValues = {
            NamedValue("Do not override", GpuSchedulingMode_DoNotOverride),
            NamedValue("Enabled (Default)", GpuSchedulingMode_Enabled, "96.6% limit"),
            NamedValue("Disabled", GpuSchedulingMode_Disabled, "99.7% limit"),
        };

        addConfigButton(HocClkConfigValue_GPUScheduling, "GPU Scheduling Override", ValueRange(0, 0, 1, "", 0), "GPU Scheduling Override",
                        &thresholdsDisabled, {}, gpuSchedValues, false);

        std::vector<NamedValue> dvfsOffset = {
            NamedValue("-80 mV", 0xFFFFFFB0), NamedValue("-75 mV", 0xFFFFFFB5), NamedValue("-70 mV", 0xFFFFFFBA), NamedValue("-65 mV", 0xFFFFFFBF),
            NamedValue("-60 mV", 0xFFFFFFC4), NamedValue("-55 mV", 0xFFFFFFC9), NamedValue("-50 mV", 0xFFFFFFCE), NamedValue("-45 mV", 0xFFFFFFD3),
            NamedValue("-40 mV", 0xFFFFFFD8), NamedValue("-35 mV", 0xFFFFFFDD), NamedValue("-30 mV", 0xFFFFFFE2), NamedValue("-25 mV", 0xFFFFFFE7),
            NamedValue("-20 mV", 0xFFFFFFEC), NamedValue("-15 mV", 0xFFFFFFF1), NamedValue("-10 mV", 0xFFFFFFF6), NamedValue(" -5 mV", 0xFFFFFFFB),
            NamedValue("Disabled", 0),        NamedValue(" +5 mV", 5),          NamedValue("+10 mV", 10),         NamedValue("+15 mV", 15),
            NamedValue("+20 mV", 20),
        };

        std::vector<NamedValue> dvfsValues = {
            NamedValue("Disabled", DVFSMode_Disabled),
            NamedValue("PCV Hijack", DVFSMode_Hijack),
            // NamedValue("Official Service", DVFSMode_OfficialService),
        };

        addConfigButton(HocClkConfigValue_DVFSMode, "GPU DVFS Mode", ValueRange(0, 0, 1, "", 0), "GPU DVFS Mode", &thresholdsDisabled, {}, dvfsValues,
                        false);

        addConfigButton(HocClkConfigValue_DVFSOffset, "GPU DVFS Offset", ValueRange(0, 12, 1, "", 0), "GPU DVFS Offset", &thresholdsDisabled, {},
                        dvfsOffset, false);

        tsl::elm::ListItem *customTableSubmenu = new tsl::elm::ListItem("GPU Voltage Table");
        customTableSubmenu->setClickListener([](u64 keys) {
            if (keys & HidNpadButton_A) {
                tsl::swapTo<GpuCustomTableSubmenuGui>();
                return true;
            }
            return false;
        });
        customTableSubmenu->setValue(R_ARROW);
        this->listElement->addItem(customTableSubmenu);

        if (!lastItemName.empty()) {
            this->listElement->jumpToItem(lastItemName);
        }
        lastItemName = "";
    }
};

class GpuCustomTableSubmenuGui : public MiscGui {
    public:
    GpuCustomTableSubmenuGui() {
    }

    bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                      HidAnalogStickState rightJoyStick) override {
        if (keysDown & KEY_B) {
            triggerExitFeedback();
            lastItemName = "GPU Voltage Table";
            tsl::swapTo<GpuSubmenuGui>();
            return true;
        }
        return false;
    }

    protected:
    void listUI() override {

        Result rc = hocclkIpcGetConfigValues(this->configList);  // populate config list early otherwise wont work
        if (R_FAILED(rc)) [[unlikely]] {
            FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
            return;
        }

        this->listElement->addItem(new CompactCategoryHeader("GPU Custom Table (mV)"));

        ValueThresholds MgpuVmaxThresholds(800, 850);
        ValueThresholds EgpuVmaxThresholds(950, 975);

        std::vector<NamedValue> mGpuVolts = {
            NamedValue("Disabled", 2000), NamedValue("Auto", 0),    NamedValue("480mV", 480), NamedValue("485mV", 485), NamedValue("490mV", 490),
            NamedValue("495mV", 495),     NamedValue("500mV", 500), NamedValue("505mV", 505), NamedValue("510mV", 510), NamedValue("515mV", 515),
            NamedValue("520mV", 520),     NamedValue("525mV", 525), NamedValue("530mV", 530), NamedValue("535mV", 535), NamedValue("540mV", 540),
            NamedValue("545mV", 545),     NamedValue("550mV", 550), NamedValue("555mV", 555), NamedValue("560mV", 560), NamedValue("565mV", 565),
            NamedValue("570mV", 570),     NamedValue("575mV", 575), NamedValue("580mV", 580), NamedValue("585mV", 585), NamedValue("590mV", 590),
            NamedValue("595mV", 595),     NamedValue("600mV", 600), NamedValue("605mV", 605), NamedValue("610mV", 610), NamedValue("615mV", 615),
            NamedValue("620mV", 620),     NamedValue("625mV", 625), NamedValue("630mV", 630), NamedValue("635mV", 635), NamedValue("640mV", 640),
            NamedValue("645mV", 645),     NamedValue("650mV", 650), NamedValue("655mV", 655), NamedValue("660mV", 660), NamedValue("665mV", 665),
            NamedValue("670mV", 670),     NamedValue("675mV", 675), NamedValue("680mV", 680), NamedValue("685mV", 685), NamedValue("690mV", 690),
            NamedValue("695mV", 695),     NamedValue("700mV", 700), NamedValue("705mV", 705), NamedValue("710mV", 710), NamedValue("715mV", 715),
            NamedValue("720mV", 720),     NamedValue("725mV", 725), NamedValue("730mV", 730), NamedValue("735mV", 735), NamedValue("740mV", 740),
            NamedValue("745mV", 745),     NamedValue("750mV", 750), NamedValue("755mV", 755), NamedValue("760mV", 760), NamedValue("765mV", 765),
            NamedValue("770mV", 770),     NamedValue("775mV", 775), NamedValue("780mV", 780), NamedValue("785mV", 785), NamedValue("790mV", 790),
            NamedValue("795mV", 795),     NamedValue("800mV", 800), NamedValue("805mV", 805), NamedValue("810mV", 810), NamedValue("815mV", 815),
            NamedValue("820mV", 820),     NamedValue("825mV", 825), NamedValue("830mV", 830), NamedValue("835mV", 835), NamedValue("840mV", 840),
            NamedValue("845mV", 845),     NamedValue("850mV", 850), NamedValue("855mV", 855), NamedValue("860mV", 860), NamedValue("865mV", 865),
            NamedValue("870mV", 870),     NamedValue("875mV", 875), NamedValue("880mV", 880), NamedValue("885mV", 885), NamedValue("890mV", 890),
            NamedValue("895mV", 895),     NamedValue("900mV", 900), NamedValue("905mV", 905), NamedValue("910mV", 910), NamedValue("915mV", 915),
            NamedValue("920mV", 920),     NamedValue("925mV", 925), NamedValue("930mV", 930), NamedValue("935mV", 935), NamedValue("940mV", 940),
            NamedValue("945mV", 945),     NamedValue("950mV", 950), NamedValue("955mV", 955), NamedValue("960mV", 960),
        };

        std::vector<NamedValue> eGpuVolts = {
            NamedValue("Disabled", 2000), NamedValue("Auto", 0),    NamedValue("675mV", 675), NamedValue("680mV", 680), NamedValue("685mV", 685),
            NamedValue("690mV", 690),     NamedValue("695mV", 695), NamedValue("700mV", 700), NamedValue("705mV", 705), NamedValue("710mV", 710),
            NamedValue("715mV", 715),     NamedValue("720mV", 720), NamedValue("725mV", 725), NamedValue("730mV", 730), NamedValue("735mV", 735),
            NamedValue("740mV", 740),     NamedValue("745mV", 745), NamedValue("750mV", 750), NamedValue("755mV", 755), NamedValue("760mV", 760),
            NamedValue("765mV", 765),     NamedValue("770mV", 770), NamedValue("775mV", 775), NamedValue("780mV", 780), NamedValue("785mV", 785),
            NamedValue("790mV", 790),     NamedValue("795mV", 795), NamedValue("800mV", 800), NamedValue("805mV", 805), NamedValue("810mV", 810),
            NamedValue("815mV", 815),     NamedValue("820mV", 820), NamedValue("825mV", 825), NamedValue("830mV", 830), NamedValue("835mV", 835),
            NamedValue("840mV", 840),     NamedValue("845mV", 845), NamedValue("850mV", 850), NamedValue("855mV", 855), NamedValue("860mV", 860),
            NamedValue("865mV", 865),     NamedValue("870mV", 870), NamedValue("875mV", 875), NamedValue("880mV", 880), NamedValue("885mV", 885),
            NamedValue("890mV", 890),     NamedValue("895mV", 895), NamedValue("900mV", 900), NamedValue("905mV", 905), NamedValue("910mV", 910),
            NamedValue("915mV", 915),     NamedValue("920mV", 920), NamedValue("925mV", 925), NamedValue("930mV", 930), NamedValue("935mV", 935),
            NamedValue("940mV", 940),     NamedValue("945mV", 945), NamedValue("950mV", 950), NamedValue("955mV", 955), NamedValue("960mV", 960),
            NamedValue("965mV", 965),     NamedValue("970mV", 970), NamedValue("975mV", 975), NamedValue("980mV", 980), NamedValue("985mV", 985),
            NamedValue("990mV", 990),     NamedValue("995mV", 995),
        };

        std::vector<NamedValue> mGpuVolts_noAuto = {
            NamedValue("Disabled", 2000), NamedValue("480mV", 480), NamedValue("485mV", 485), NamedValue("490mV", 490), NamedValue("495mV", 495),
            NamedValue("500mV", 500),     NamedValue("505mV", 505), NamedValue("510mV", 510), NamedValue("515mV", 515), NamedValue("520mV", 520),
            NamedValue("525mV", 525),     NamedValue("530mV", 530), NamedValue("535mV", 535), NamedValue("540mV", 540), NamedValue("545mV", 545),
            NamedValue("550mV", 550),     NamedValue("555mV", 555), NamedValue("560mV", 560), NamedValue("565mV", 565), NamedValue("570mV", 570),
            NamedValue("575mV", 575),     NamedValue("580mV", 580), NamedValue("585mV", 585), NamedValue("590mV", 590), NamedValue("595mV", 595),
            NamedValue("600mV", 600),     NamedValue("605mV", 605), NamedValue("610mV", 610), NamedValue("615mV", 615), NamedValue("620mV", 620),
            NamedValue("625mV", 625),     NamedValue("630mV", 630), NamedValue("635mV", 635), NamedValue("640mV", 640), NamedValue("645mV", 645),
            NamedValue("650mV", 650),     NamedValue("655mV", 655), NamedValue("660mV", 660), NamedValue("665mV", 665), NamedValue("670mV", 670),
            NamedValue("675mV", 675),     NamedValue("680mV", 680), NamedValue("685mV", 685), NamedValue("690mV", 690), NamedValue("695mV", 695),
            NamedValue("700mV", 700),     NamedValue("705mV", 705), NamedValue("710mV", 710), NamedValue("715mV", 715), NamedValue("720mV", 720),
            NamedValue("725mV", 725),     NamedValue("730mV", 730), NamedValue("735mV", 735), NamedValue("740mV", 740), NamedValue("745mV", 745),
            NamedValue("750mV", 750),     NamedValue("755mV", 755), NamedValue("760mV", 760), NamedValue("765mV", 765), NamedValue("770mV", 770),
            NamedValue("775mV", 775),     NamedValue("780mV", 780), NamedValue("785mV", 785), NamedValue("790mV", 790), NamedValue("795mV", 795),
            NamedValue("800mV", 800),     NamedValue("805mV", 805), NamedValue("810mV", 810), NamedValue("815mV", 815), NamedValue("820mV", 820),
            NamedValue("825mV", 825),     NamedValue("830mV", 830), NamedValue("835mV", 835), NamedValue("840mV", 840), NamedValue("845mV", 845),
            NamedValue("850mV", 850),     NamedValue("855mV", 855), NamedValue("860mV", 860), NamedValue("865mV", 865), NamedValue("870mV", 870),
            NamedValue("875mV", 875),     NamedValue("880mV", 880), NamedValue("885mV", 885), NamedValue("890mV", 890), NamedValue("895mV", 895),
            NamedValue("900mV", 900),     NamedValue("905mV", 905), NamedValue("910mV", 910), NamedValue("915mV", 915), NamedValue("920mV", 920),
            NamedValue("925mV", 925),     NamedValue("930mV", 930), NamedValue("935mV", 935), NamedValue("940mV", 940), NamedValue("945mV", 945),
            NamedValue("950mV", 950),     NamedValue("955mV", 955), NamedValue("960mV", 960),
        };

        std::vector<NamedValue> eGpuVolts_noAuto = {
            NamedValue("Disabled", 2000), NamedValue("700mV", 700), NamedValue("705mV", 705), NamedValue("710mV", 710), NamedValue("715mV", 715),
            NamedValue("720mV", 720),     NamedValue("725mV", 725), NamedValue("730mV", 730), NamedValue("735mV", 735), NamedValue("740mV", 740),
            NamedValue("745mV", 745),     NamedValue("750mV", 750), NamedValue("755mV", 755), NamedValue("760mV", 760), NamedValue("765mV", 765),
            NamedValue("770mV", 770),     NamedValue("775mV", 775), NamedValue("780mV", 780), NamedValue("785mV", 785), NamedValue("790mV", 790),
            NamedValue("795mV", 795),     NamedValue("800mV", 800), NamedValue("805mV", 805), NamedValue("810mV", 810), NamedValue("815mV", 815),
            NamedValue("820mV", 820),     NamedValue("825mV", 825), NamedValue("830mV", 830), NamedValue("835mV", 835), NamedValue("840mV", 840),
            NamedValue("845mV", 845),     NamedValue("850mV", 850), NamedValue("855mV", 855), NamedValue("860mV", 860), NamedValue("865mV", 865),
            NamedValue("870mV", 870),     NamedValue("875mV", 875), NamedValue("880mV", 880), NamedValue("885mV", 885), NamedValue("890mV", 890),
            NamedValue("895mV", 895),     NamedValue("900mV", 900), NamedValue("905mV", 905), NamedValue("910mV", 910), NamedValue("915mV", 915),
            NamedValue("920mV", 920),     NamedValue("925mV", 925), NamedValue("930mV", 930), NamedValue("935mV", 935), NamedValue("940mV", 940),
            NamedValue("945mV", 945),     NamedValue("950mV", 950), NamedValue("955mV", 955), NamedValue("960mV", 960), NamedValue("965mV", 965),
            NamedValue("970mV", 970),     NamedValue("975mV", 975), NamedValue("980mV", 980), NamedValue("985mV", 985), NamedValue("990mV", 990),
            NamedValue("995mV", 995),
        };

        if (IsMariko()) {
            tsl::elm::CustomDrawer *warningText = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
                renderer->drawString("\uE150 Setting GPU Clocks past", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
                renderer->drawString("1305MHz without a proper undervolt", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
                renderer->drawString("can cause degradation or damage", false, x + 20, y + 70, 18, tsl::style::color::ColorText);
                renderer->drawString("to your console!", false, x + 20, y + 90, 18, tsl::style::color::ColorText);
                renderer->drawString("Proceed at your own risk!", false, x + 20, y + 110, 18, tsl::style::color::ColorText);
            });
            warningText->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 130);
            this->listElement->addItem(warningText);

            addGpuFreqVoltageButton(KipConfigValue_g_volt_76800, "76.8MHz", 76'800'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_153600, "153.6MHz", 153'600'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_230400, "230.4MHz", 230'400'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_307200, "307.2MHz", 307'200'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_384000, "384.0MHz", 384'000'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_460800, "460.8MHz", 460'800'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_537600, "537.6MHz", 537'600'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_614400, "614.4MHz", 614'400'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_691200, "691.2MHz", 691'200'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_768000, "768.0MHz", 768'000'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_844800, "844.8MHz", 844'800'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_921600, "921.6MHz", 921'600'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_998400, "998.4MHz", 998'400'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &MgpuVmaxThresholds, {}, mGpuVolts, false, true);

            if (this->configList->values[KipConfigValue_marikoGpuUV] >= GPUUVLevel_SLT) {
                addGpuFreqVoltageButton(KipConfigValue_g_volt_1075200, "1075.2MHz", 1'075'200'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                        &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
                if (this->configList->values[KipConfigValue_marikoGpuUV] >= GPUUVLevel_HiOPT)
                    addGpuFreqVoltageButton(KipConfigValue_g_volt_1152000, "1152.0MHz", 1'152'000'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                            &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
                if (this->configList->values[KipConfigValue_marikoGpuUV] >= GPUUVLevel_HighUV) {
                    addGpuFreqVoltageButton(KipConfigValue_g_volt_1228800, "1228.8MHz", 1'228'800'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                            &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
                    addGpuFreqVoltageButton(KipConfigValue_g_volt_1267200, "1267.2MHz", 1'267'200'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                            &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
                    addGpuFreqVoltageButton(KipConfigValue_g_volt_1305600, "1305.6MHz", 1'305'600'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                            &MgpuVmaxThresholds, {}, mGpuVolts, false, true);
                    addGpuFreqVoltageButton(KipConfigValue_g_volt_1344000, "1344.0MHz", 1'344'000'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                            &MgpuVmaxThresholds, {}, mGpuVolts_noAuto, false, true);
                    addGpuFreqVoltageButton(KipConfigValue_g_volt_1382400, "1382.4MHz", 1'382'400'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                            &MgpuVmaxThresholds, {}, mGpuVolts_noAuto, false, true);
                    addGpuFreqVoltageButton(KipConfigValue_g_volt_1420800, "1420.8MHz", 1'420'800'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                            &MgpuVmaxThresholds, {}, mGpuVolts_noAuto, false, true);
                    addGpuFreqVoltageButton(KipConfigValue_g_volt_1459200, "1459.2MHz", 1'459'200'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                            &MgpuVmaxThresholds, {}, mGpuVolts_noAuto, false, true);
                    addGpuFreqVoltageButton(KipConfigValue_g_volt_1497600, "1497.6MHz", 1'497'600'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                            &MgpuVmaxThresholds, {}, mGpuVolts_noAuto, false, true);
                    addGpuFreqVoltageButton(KipConfigValue_g_volt_1536000, "1536.0MHz", 1'536'000'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                            &MgpuVmaxThresholds, {}, mGpuVolts_noAuto, false, true);
                }
            }
        } else {
            tsl::elm::CustomDrawer *warningText = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
                renderer->drawString("\uE150 Setting GPU Clocks past", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
                renderer->drawString("921MHz without a proper undervolt", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
                renderer->drawString("can cause degradation or damage", false, x + 20, y + 70, 18, tsl::style::color::ColorText);
                renderer->drawString("to your console!", false, x + 20, y + 90, 18, tsl::style::color::ColorText);
                renderer->drawString("Proceed at your own risk!", false, x + 20, y + 110, 18, tsl::style::color::ColorText);
            });
            warningText->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 130);
            this->listElement->addItem(warningText);

            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_76800, "76.8MHz", 76'800'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_115200, "115.2MHz", 115'200'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_153600, "153.6MHz", 153'600'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_192000, "192.0MHz", 192'000'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_230400, "230.4MHz", 230'400'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_268800, "268.8MHz", 268'800'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_307200, "307.2MHz", 307'200'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_345600, "345.6MHz", 345'600'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_384000, "384.0MHz", 384'000'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_422400, "422.4MHz", 422'400'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_460800, "460.8MHz", 460'800'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_499200, "499.2MHz", 499'200'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_537600, "537.6MHz", 537'600'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_576000, "576.0MHz", 576'000'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_614400, "614.4MHz", 614'400'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_652800, "652.8MHz", 652'800'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_691200, "691.2MHz", 691'200'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_729600, "729.6MHz", 729'600'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_768000, "768.0MHz", 768'000'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_806400, "806.4MHz", 806'400'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_844800, "844.8MHz", 844'800'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_883200, "883.2MHz", 883'200'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            addGpuFreqVoltageButton(KipConfigValue_g_volt_e_921600, "921.6MHz", 921'600'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                    &EgpuVmaxThresholds, {}, eGpuVolts, false, true);

            if (this->configList->values[KipConfigValue_eristaGpuUV] >= GPUUVLevel_SLT)
                addGpuFreqVoltageButton(KipConfigValue_g_volt_e_960000, "960.0MHz", 960'000'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                        &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
            if (this->configList->values[KipConfigValue_eristaGpuUV] >= GPUUVLevel_HiOPT) {
                addGpuFreqVoltageButton(KipConfigValue_g_volt_e_998400, "998.4MHz", 998'400'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                        &EgpuVmaxThresholds, {}, eGpuVolts, false, true);
                addGpuFreqVoltageButton(KipConfigValue_g_volt_e_1036800, "1036.8MHz", 1'036'800'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                        &EgpuVmaxThresholds, {}, eGpuVolts_noAuto, false, true);
                addGpuFreqVoltageButton(KipConfigValue_g_volt_e_1075200, "1075.2MHz", 1'075'200'000, ValueRange(0, 0, 0, "0", 1), "Voltage",
                                        &EgpuVmaxThresholds, {}, eGpuVolts_noAuto, false, true);
            }
        }
    }
};

class SocCustomTableSubmenuGui : public MiscGui {
    public:
    SocCustomTableSubmenuGui() {}

    bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState leftJoyStick,
                      HidAnalogStickState rightJoyStick) override {
        if (keysDown & KEY_B) {
            triggerExitFeedback();
            lastItemName = "SOC Voltage Table";
            tsl::swapTo<RamSubmenuGui>();
            return true;
        }
        return false;
    }

    protected:
    void listUI() override {

        Result rc = hocclkIpcGetConfigValues(this->configList);  // populate config list early otherwise wont work
        if (R_FAILED(rc)) [[unlikely]] {
            FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
            return;
        }

        this->listElement->addItem(new CompactCategoryHeader("SOC Custom Voltages"));

        ValueThresholds voltageThresholds(1075, 1150);

        std::vector<NamedValue> socVolts = {
            NamedValue("No Override", 0),

            NamedValue("637mV", 637),   NamedValue("650mV", 650),   NamedValue("662mV", 662),   NamedValue("675mV", 675),   NamedValue("687mV", 687),
            NamedValue("700mV", 700),   NamedValue("712mV", 712),   NamedValue("725mV", 725),   NamedValue("737mV", 737),   NamedValue("750mV", 750),
            NamedValue("762mV", 762),   NamedValue("775mV", 775),   NamedValue("787mV", 787),   NamedValue("800mV", 800),   NamedValue("812mV", 812),
            NamedValue("825mV", 825),   NamedValue("837mV", 837),   NamedValue("850mV", 850),   NamedValue("862mV", 862),   NamedValue("875mV", 875),
            NamedValue("887mV", 887),   NamedValue("900mV", 900),   NamedValue("912mV", 912),   NamedValue("925mV", 925),   NamedValue("937mV", 937),
            NamedValue("950mV", 950),   NamedValue("962mV", 962),   NamedValue("975mV", 975),   NamedValue("987mV", 987),   NamedValue("1000mV", 1000),
            NamedValue("1012mV", 1012), NamedValue("1025mV", 1025), NamedValue("1037mV", 1037), NamedValue("1050mV", 1050), NamedValue("1062mV", 1062),
            NamedValue("1075mV", 1075), NamedValue("1087mV", 1087), NamedValue("1100mV", 1100), NamedValue("1112mV", 1112), NamedValue("1125mV", 1125),
            NamedValue("1137mV", 1137), NamedValue("1150mV", 1150),
        };

        addConfigButton(KipConfigValue_g_soc_volt_1866000, "1866MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2000000, "2000MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2133000, "2133MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2200000, "2200MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2266000, "2266MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2333000, "2333MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2400000, "2400MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2433000, "2433MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2466000, "2466MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2533000, "2533MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2566000, "2566MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2600000, "2600MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2666000, "2666MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2700000, "2700MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2733000, "2733MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2766000, "2766MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2800000, "2800MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2833000, "2833MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2900000, "2900MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_2933000, "2933MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_3000000, "3000MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_3033000, "3033MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_3100000, "3100MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_3133000, "3133MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_3166000, "3166MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_3200000, "3200MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_3266000, "3266MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
        addConfigButton(KipConfigValue_g_soc_volt_3333000, "3333MHz", ValueRange(0, 0, 0, "0", 1), "Voltage", &voltageThresholds, {}, socVolts, false, true);
    }
};

static std::string getValueDisplayText(uint64_t currentValue, const ValueRange &range, const std::vector<NamedValue> &namedValues) {
    char valueText[32];

    for (const auto &namedValue : namedValues) {
        if (currentValue == namedValue.value) {
            return namedValue.name;
        }
    }

    if (currentValue == 0) {
        snprintf(valueText, sizeof(valueText), "%s", VALUE_DEFAULT_TEXT);
    } else {
        uint64_t displayValue = currentValue / range.divisor;
        if (!range.suffix.empty()) {
            snprintf(valueText, sizeof(valueText), "%lu %s", displayValue, range.suffix.c_str());
        } else {
            snprintf(valueText, sizeof(valueText), "%lu", displayValue);
        }
    }
    return std::string(valueText);
}

void MiscGui::refresh() {
    BaseMenuGui::refresh();

    if (this->context && ++frameCounter >= 60) {
        frameCounter = 0;

        Result rc = hocclkIpcGetConfigValues(this->configList);
        if (R_FAILED(rc)) [[unlikely]] {
            FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
            return;
        }
        updateConfigToggles();

        // relabel when display unit changes
        RamDisplayUnit unit = (RamDisplayUnit)this->configList->values[HocClkConfigValue_RamDisplayUnit];
        constexpr HocClkConfigValue emcKeys[] = {
            KipConfigValue_marikoEmcMaxClock,
            KipConfigValue_eristaEmcMaxClock,
        };
        for (auto key : emcKeys) {
            auto it = this->configNamedValues.find(key);
            if (it != this->configNamedValues.end()) {
                for (auto &nv : it->second)
                    if (nv.name != "Disabled")
                        nv.name = formatMemClockKhzLabel(nv.value, unit);
            }
        }

        for (const auto &[configVal, button] : this->configButtons) {
            uint64_t currentValue = this->configList->values[configVal];
            const ValueRange &range = this->configRanges[configVal];

            auto namedValuesIt = this->configNamedValues.find(configVal);
            const std::vector<NamedValue> &namedValues =
                (namedValuesIt != this->configNamedValues.end()) ? namedValuesIt->second : std::vector<NamedValue>();

            char valueText[32];

            bool foundNamedValue = false;
            for (const auto &namedValue : namedValues) {
                if (currentValue == namedValue.value) {
                    snprintf(valueText, sizeof(valueText), "%s", namedValue.name.c_str());
                    foundNamedValue = true;
                    break;
                }
            }

            if (!foundNamedValue) {
                uint64_t displayValue = currentValue / range.divisor;
                if (!range.suffix.empty()) {
                    snprintf(valueText, sizeof(valueText), "%lu %s", displayValue, range.suffix.c_str());
                } else {
                    snprintf(valueText, sizeof(valueText), "%lu", displayValue);
                }
            }

            if (this->configButtonSKeys.count(configVal)) {
                button->setText(valueText);
                auto subtextIt = this->configButtonSSubtext.find(configVal);
                if (subtextIt != this->configButtonSSubtext.end())
                    button->setValue(subtextIt->second);
                else
                    button->setValue("");
            } else {
                button->setValue(valueText);
            }
        }
    }
}

std::string MiscGui::getJumpToItemName() {
    return "Settings";
}