This commit is contained in:
souldbminersmwc
2025-12-23 19:10:41 -05:00
parent 152f92a9a9
commit b5022ea839
43 changed files with 5143 additions and 3939 deletions

View File

@@ -76,6 +76,7 @@ typedef enum
SysClkModule_CPU = 0, SysClkModule_CPU = 0,
SysClkModule_GPU, SysClkModule_GPU,
SysClkModule_MEM, SysClkModule_MEM,
HorizonOCModule_Governor,
SysClkModule_EnumMax, SysClkModule_EnumMax,
} SysClkModule; } SysClkModule;
@@ -129,8 +130,10 @@ static inline const char* sysclkFormatModule(SysClkModule module, bool pretty)
return pretty ? "GPU" : "gpu"; return pretty ? "GPU" : "gpu";
case SysClkModule_MEM: case SysClkModule_MEM:
return pretty ? "Memory" : "mem"; return pretty ? "Memory" : "mem";
case HorizonOCModule_Governor:
return pretty ? "Governor" : "gov";
default: default:
return NULL; return "null";
} }
} }

View File

@@ -50,9 +50,6 @@ typedef enum {
HocClkConfigValue_ThermalThrottle, HocClkConfigValue_ThermalThrottle,
HocClkConfigValue_ThermalThrottleThreshold, HocClkConfigValue_ThermalThrottleThreshold,
HocClkConfigValue_HandheldGovernor,
HocClkConfigValue_DockedGovernor,
HocClkConfigValue_HandheldTDP, HocClkConfigValue_HandheldTDP,
HocClkConfigValue_HandheldTDPLimit, HocClkConfigValue_HandheldTDPLimit,
@@ -212,11 +209,6 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
case HocClkConfigValue_ThermalThrottleThreshold: case HocClkConfigValue_ThermalThrottleThreshold:
return pretty ? "Thermal Throttle Threshold" : "thermal_throttle_threshold"; return pretty ? "Thermal Throttle Threshold" : "thermal_throttle_threshold";
case HocClkConfigValue_HandheldGovernor:
return pretty ? "Handheld Governor" : "governor";
case HocClkConfigValue_DockedGovernor:
return pretty ? "Docked Governor" : "governor_docked";
case HocClkConfigValue_HandheldTDP: case HocClkConfigValue_HandheldTDP:
return pretty ? "Handheld TDP" : "handheld_tdp"; return pretty ? "Handheld TDP" : "handheld_tdp";
@@ -422,8 +414,6 @@ static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val)
return 1862ULL; return 1862ULL;
case HocClkConfigValue_ThermalThrottle: case HocClkConfigValue_ThermalThrottle:
case HocClkConfigValue_DockedGovernor:
case HocClkConfigValue_HandheldGovernor:
case HocClkConfigValue_HandheldTDP: case HocClkConfigValue_HandheldTDP:
case HocClkConfigValue_EnforceBoardLimit: case HocClkConfigValue_EnforceBoardLimit:
case HocClkConfigValue_KipEditing: case HocClkConfigValue_KipEditing:
@@ -464,8 +454,6 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
case HocClkConfigValue_UncappedClocks: case HocClkConfigValue_UncappedClocks:
case HocClkConfigValue_OverwriteBoostMode: case HocClkConfigValue_OverwriteBoostMode:
case HocClkConfigValue_ThermalThrottle: case HocClkConfigValue_ThermalThrottle:
case HocClkConfigValue_DockedGovernor:
case HocClkConfigValue_HandheldGovernor:
case HocClkConfigValue_HandheldTDP: case HocClkConfigValue_HandheldTDP:
case HocClkConfigValue_EnforceBoardLimit: case HocClkConfigValue_EnforceBoardLimit:
case HocClkConfigValue_KipEditing: case HocClkConfigValue_KipEditing:

View File

@@ -78,7 +78,7 @@ Together, these flags (-ffunction-sections, -fdata-sections, -Wl,--gc-sections,
- [Status Monitor Overlay](https://github.com/ppkantorski/Status-Monitor-Overlay) - [Status Monitor Overlay](https://github.com/ppkantorski/Status-Monitor-Overlay)
- [Edizon Overlay](https://github.com/ppkantorski/EdiZon-Overlay) - [Edizon Overlay](https://github.com/proferabg/EdiZon-Overlay)
- [Sysmodules](https://github.com/ppkantorski/ovl-sysmodules) - [Sysmodules](https://github.com/ppkantorski/ovl-sysmodules)
@@ -208,4 +208,4 @@ Contributions are welcome! If you have any ideas, suggestions, or bug reports, p
This project is licensed and distributed under [GPLv2](LICENSE) with a [custom library](libultra) utilizing [CC-BY-4.0](SUB_LICENSE). This project is licensed and distributed under [GPLv2](LICENSE) with a [custom library](libultra) utilizing [CC-BY-4.0](SUB_LICENSE).
Copyright (c) 2024 ppkantorski Copyright (c) 2023-2025 ppkantorski

View File

@@ -1520,140 +1520,85 @@ static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, in
// Force inline for maximum performance // Force inline for maximum performance
static inline int stbtt_FindGlyphIndex_impl(stbtt_uint8 *data, stbtt_uint32 index_map, int unicode_codepoint) static inline int stbtt_FindGlyphIndex_impl(stbtt_uint8 *data, stbtt_uint32 index_map, int unicode_codepoint)
{ {
// Read format once - single memory access
stbtt_uint16 format = FAST_USHORT(data + index_map); stbtt_uint16 format = FAST_USHORT(data + index_map);
stbtt_uint32 uc = (stbtt_uint32)unicode_codepoint;
// Variables used across multiple cases or frequently in loops
stbtt_uint32 low, high, mid;
stbtt_uint8 *group;
stbtt_uint32 start_char, end_char;
stbtt_uint32 uc;
// Switch for jump table optimization
switch (format) { switch (format) {
case 4: { // Windows fonts - most common case 4: {
// Early exit for out-of-range Unicode if (uc > 0xffff) return 0;
if ((unsigned)unicode_codepoint > 0xffff) return 0;
// Cache all header values in one go - burst read
stbtt_uint8 *header = data + index_map; stbtt_uint8 *header = data + index_map;
stbtt_uint16 segcount = FAST_USHORT(header + 6) >> 1; stbtt_uint16 segcount = FAST_USHORT(header + 6) >> 1;
stbtt_uint16 searchRange = FAST_USHORT(header + 8) >> 1;
stbtt_uint16 entrySelector = FAST_USHORT(header + 10);
stbtt_uint16 rangeShift = FAST_USHORT(header + 12) >> 1; stbtt_uint16 rangeShift = FAST_USHORT(header + 12) >> 1;
// Optimized binary search with fewer memory accesses
stbtt_uint32 endCount = index_map + 14; stbtt_uint32 endCount = index_map + 14;
stbtt_uint32 search = endCount; stbtt_uint32 search = endCount;
// Use the precomputed rangeShift for initial jump if (uc >= (stbtt_uint16)FAST_USHORT(data + search + (rangeShift << 1)))
if (unicode_codepoint >= FAST_USHORT(data + search + (rangeShift << 1)))
search += rangeShift << 1; search += rangeShift << 1;
search -= 2; search -= 2;
stbtt_uint16 entrySelector = FAST_USHORT(header + 10);
stbtt_uint16 searchRange = FAST_USHORT(header + 8) >> 1;
// Unrolled binary search - most critical path
while (entrySelector) { while (entrySelector) {
searchRange >>= 1; searchRange >>= 1;
stbtt_uint32 test_pos = search + (searchRange << 1); stbtt_uint32 test_pos = search + (searchRange << 1);
if (unicode_codepoint > FAST_USHORT(data + test_pos)) if (uc > (stbtt_uint16)FAST_USHORT(data + test_pos))
search = test_pos; search = test_pos;
--entrySelector; --entrySelector;
} }
search += 2; search += 2;
stbtt_uint16 item = (stbtt_uint16)((search - endCount) >> 1); stbtt_uint16 item = (stbtt_uint16)((search - endCount) >> 1);
stbtt_uint32 base = index_map + 14;
// Calculate all offsets upfront - better instruction scheduling stbtt_uint16 start = FAST_USHORT(data + base + (segcount << 1) + 2 + (item << 1));
stbtt_uint32 base1 = index_map + 14; stbtt_uint16 end = FAST_USHORT(data + endCount + (item << 1));
stbtt_uint32 startCode_offset = base1 + (segcount << 1) + 2 + (item << 1);
stbtt_uint32 endCode_offset = endCount + (item << 1);
stbtt_uint32 idRangeOffset_offset = base1 + (segcount * 6) + 2 + (item << 1);
stbtt_uint16 start = FAST_USHORT(data + startCode_offset); if ((stbtt_uint32)(uc - start) > (stbtt_uint32)(end - start))
stbtt_uint16 end = FAST_USHORT(data + endCode_offset);
// Single comparison using unsigned arithmetic trick
if ((unsigned)(unicode_codepoint - start) > (unsigned)(end - start))
return 0; return 0;
stbtt_uint16 offset = FAST_USHORT(data + idRangeOffset_offset); stbtt_uint16 offset = FAST_USHORT(data + base + (segcount * 6) + 2 + (item << 1));
if (offset == 0) { if (offset == 0)
stbtt_uint32 idDelta_offset = base1 + (segcount << 2) + 2 + (item << 1); return (stbtt_uint16)(uc + FAST_SHORT(data + base + (segcount << 2) + 2 + (item << 1)));
return (stbtt_uint16)(unicode_codepoint + FAST_SHORT(data + idDelta_offset));
return FAST_USHORT(data + offset + ((uc - start) << 1) + base + (segcount * 6) + 2 + (item << 1));
} }
return FAST_USHORT(data + offset + ((unicode_codepoint - start) << 1) + idRangeOffset_offset); case 12:
} case 13: {
stbtt_uint32 low = 0, high = FAST_ULONG(data + index_map + 12);
case 12: { // 32-bit format
stbtt_uint32 ngroups = FAST_ULONG(data + index_map + 12);
uc = (stbtt_uint32)unicode_codepoint;
// Optimized binary search with minimal memory access
low = 0; high = ngroups;
stbtt_uint8 *groups_base = data + index_map + 16; stbtt_uint8 *groups_base = data + index_map + 16;
while (low < high) { while (low < high) {
mid = (low + high) >> 1; stbtt_uint32 mid = (low + high) >> 1;
group = groups_base + (mid * 12); stbtt_uint8 *group = groups_base + (mid * 12);
stbtt_uint32 start_char = FAST_ULONG(group);
start_char = FAST_ULONG(group);
if (uc < start_char) { if (uc < start_char) {
high = mid; high = mid;
} else { } else if (uc <= FAST_ULONG(group + 4)) {
end_char = FAST_ULONG(group + 4);
if (uc <= end_char) {
stbtt_uint32 start_glyph = FAST_ULONG(group + 8); stbtt_uint32 start_glyph = FAST_ULONG(group + 8);
return start_glyph + uc - start_char; return (format == 12) ? (start_glyph + uc - start_char) : start_glyph;
}
low = mid + 1;
}
}
return 0;
}
case 13: { // 32-bit format, many-to-one mapping
stbtt_uint32 ngroups = FAST_ULONG(data + index_map + 12);
uc = (stbtt_uint32)unicode_codepoint;
low = 0; high = ngroups;
stbtt_uint8 *groups_base = data + index_map + 16;
while (low < high) {
mid = (low + high) >> 1;
group = groups_base + (mid * 12);
start_char = FAST_ULONG(group);
if (uc < start_char) {
high = mid;
} else { } else {
end_char = FAST_ULONG(group + 4);
if (uc <= end_char) {
return FAST_ULONG(group + 8); // Same glyph for all chars in range
}
low = mid + 1; low = mid + 1;
} }
} }
return 0; return 0;
} }
case 0: { // Apple byte encoding - simple and fast case 0:
stbtt_int32 bytes = FAST_USHORT(data + index_map + 2); return (uc < (stbtt_uint32)(FAST_USHORT(data + index_map + 2) - 6)) ?
return ((unsigned)unicode_codepoint < (unsigned)(bytes - 6)) ? data[index_map + 6 + uc] : 0;
data[index_map + 6 + unicode_codepoint] : 0;
}
case 6: { // Trimmed table mapping case 6: {
stbtt_uint32 first = FAST_USHORT(data + index_map + 6); stbtt_uint32 offset = uc - FAST_USHORT(data + index_map + 6);
stbtt_uint32 count = FAST_USHORT(data + index_map + 8); return (offset < (stbtt_uint32)FAST_USHORT(data + index_map + 8)) ?
uc = (stbtt_uint32)unicode_codepoint; FAST_USHORT(data + index_map + 10 + (offset << 1)) : 0;
stbtt_uint32 offset = uc - first;
return (offset < count) ? FAST_USHORT(data + index_map + 10 + (offset << 1)) : 0;
} }
default: default:
return 0; // Unsupported format return 0;
} }
} }

View File

@@ -1,8 +1,8 @@
/******************************************************************************** /********************************************************************************
* File: audio_player.hpp * File: audio.hpp
* Author: ppkantorski * Author: ppkantorski
* Description: * Description:
* This header defines the AudioPlayer class and related structures used for * This header defines the Audio class and related structures used for
* handling sound playback within the Ultrahand Overlay. It provides interfaces * handling sound playback within the Ultrahand Overlay. It provides interfaces
* for loading, caching, and playing WAV audio through libnxs audout service, * for loading, caching, and playing WAV audio through libnxs audout service,
* along with basic sound type management and synchronization support. * along with basic sound type management and synchronization support.
@@ -25,9 +25,10 @@
#include <atomic> #include <atomic>
#include <cstring> #include <cstring>
#include <mutex> #include <mutex>
#include "tsl_utils.hpp"
namespace ult { namespace ult {
class AudioPlayer { class Audio {
public: public:
enum class SoundType : uint8_t { // <- uint8_t saves space enum class SoundType : uint8_t { // <- uint8_t saves space
Navigate, Navigate,
@@ -64,7 +65,7 @@ namespace ult {
static void setMasterVolume(float volume); static void setMasterVolume(float volume);
static void setEnabled(bool enabled); static void setEnabled(bool enabled);
static bool isEnabled(); static bool isEnabled();
static bool isDocked(); //static bool isDocked();
static bool reloadIfDockedChanged(); static bool reloadIfDockedChanged();
static void reloadAllSounds(); static void reloadAllSounds();
static void unloadAllSounds(const std::initializer_list<SoundType>& excludeSounds = {}); static void unloadAllSounds(const std::initializer_list<SoundType>& excludeSounds = {});

View File

@@ -12,7 +12,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#pragma once #pragma once

View File

@@ -13,7 +13,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#pragma once #pragma once

View File

@@ -14,7 +14,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#pragma once #pragma once

View File

@@ -13,7 +13,7 @@
* altered or removed. * altered or removed.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
@@ -39,6 +39,11 @@ namespace ult {
extern const std::string ROOT_PATH; extern const std::string ROOT_PATH;
extern const std::string SETTINGS_PATH; extern const std::string SETTINGS_PATH;
extern const std::string NX_OVLLOADER_PATH;
extern const std::string OVL_HEAP_CONFIG_PATH;
extern const std::string OVL_EXIT_FLAG_PATH;
extern const std::string ULTRAHAND_CONFIG_INI_PATH; extern const std::string ULTRAHAND_CONFIG_INI_PATH;
extern const std::string TESLA_CONFIG_INI_PATH; extern const std::string TESLA_CONFIG_INI_PATH;
extern const std::string LANG_PATH; extern const std::string LANG_PATH;
@@ -63,7 +68,8 @@ namespace ult {
extern const std::string PACKAGE_FILENAME; extern const std::string PACKAGE_FILENAME;
extern const std::string DOWNLOADS_PATH; extern const std::string DOWNLOADS_PATH;
extern const std::string EXPANSION_PATH;
//extern const std::string EXPANSION_PATH;
extern const std::string FUSE_DATA_INI_PATH; extern const std::string FUSE_DATA_INI_PATH;
extern const std::string PACKAGE_PATH; extern const std::string PACKAGE_PATH;
extern const std::string OVERLAY_PATH; extern const std::string OVERLAY_PATH;
@@ -77,12 +83,13 @@ namespace ult {
extern const std::string ULTRAHAND_REPO_URL; extern const std::string ULTRAHAND_REPO_URL;
extern const std::string INCLUDED_THEME_FOLDER_URL; extern const std::string INCLUDED_THEME_FOLDER_URL;
extern const std::string LATEST_RELEASE_INFO_URL; extern const std::string LATEST_RELEASE_INFO_URL;
extern const std::string NX_OVLLOADER_ZIP_URL; extern const std::string LATEST_UPDATER_INI_URL;
extern const std::string NX_OVLLOADER_PLUS_ZIP_URL; //extern const std::string NX_OVLLOADER_ZIP_URL;
//extern const std::string NX_OVLLOADER_PLUS_ZIP_URL;
extern const std::string OLD_NX_OVLLOADER_ZIP_URL; extern const std::string OLD_NX_OVLLOADER_ZIP_URL;
extern const std::string OLD_NX_OVLLOADER_PLUS_ZIP_URL; //extern const std::string OLD_NX_OVLLOADER_PLUS_ZIP_URL;
extern const std::string UPDATER_PAYLOAD_URL; extern const std::string UPDATER_PAYLOAD_URL;
extern const std::string SOUND_EFFECTS_URL; //extern const std::string SOUND_EFFECTS_URL;
extern const std::string LAUNCH_ARGS_STR; extern const std::string LAUNCH_ARGS_STR;
extern const std::string USE_LAUNCH_ARGS_STR; extern const std::string USE_LAUNCH_ARGS_STR;
@@ -137,6 +144,7 @@ namespace ult {
extern const std::string FALSE_STR; extern const std::string FALSE_STR;
extern const std::string GLOBAL_STR; extern const std::string GLOBAL_STR;
extern const std::string DEFAULT_STR; extern const std::string DEFAULT_STR;
extern const std::string HOLD_STR;
extern const std::string SLOT_STR; extern const std::string SLOT_STR;
extern const std::string OPTION_STR; extern const std::string OPTION_STR;
extern const std::string FORWARDER_STR; extern const std::string FORWARDER_STR;
@@ -161,6 +169,7 @@ namespace ult {
extern const std::string INPROGRESS_SYMBOL; extern const std::string INPROGRESS_SYMBOL;
extern const std::string STAR_SYMBOL; extern const std::string STAR_SYMBOL;
extern const std::string DIVIDER_SYMBOL; extern const std::string DIVIDER_SYMBOL;
extern const std::string NOTIFY_HEADER;
extern const std::vector<std::string> THROBBER_SYMBOLS; extern const std::vector<std::string> THROBBER_SYMBOLS;

View File

@@ -26,12 +26,12 @@
namespace ult { namespace ult {
//extern bool rumbleInitialized; //extern bool rumbleInitialized;
extern std::atomic<bool> rumbleActive; extern std::atomic<bool> clickActive;
extern std::atomic<bool> doubleClickActive; extern std::atomic<bool> doubleClickActive;
//void initRumble(); void initHaptics();
void deinitRumble(); void deinitHaptics();
void checkAndReinitRumble(); void checkAndReinitHaptics();
void rumbleClick(); void rumbleClick();
void rumbleDoubleClick(); void rumbleDoubleClick();

View File

@@ -13,7 +13,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#pragma once #pragma once
@@ -206,6 +206,9 @@ namespace ult {
std::string extractVersionFromBinary(const std::string &filePath); std::string extractVersionFromBinary(const std::string &filePath);
std::string decodeBase64ToString(const std::string& b64);
} }
#endif #endif

View File

@@ -13,7 +13,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#pragma once #pragma once
@@ -290,7 +290,7 @@ namespace ult {
//} //}
void syncIniValue(std::map<std::string, std::map<std::string, std::string>>& packageConfigData, bool syncIniValue(std::map<std::string, std::map<std::string, std::string>>& packageConfigData,
const std::string& packageConfigIniPath, const std::string& packageConfigIniPath,
const std::string& optionName, const std::string& optionName,
const std::string& key, const std::string& key,
@@ -339,6 +339,36 @@ namespace ult {
* @param data The complete INI data structure to save. * @param data The complete INI data structure to save.
*/ */
void saveIniFileData(const std::string& filePath, const std::map<std::string, std::map<std::string, std::string>>& data); void saveIniFileData(const std::string& filePath, const std::map<std::string, std::map<std::string, std::string>>& data);
/**
* @brief Adds a key-value pair to all sections that contain a specified pattern key.
*
* If patternKey is empty, the key-value pair will be added to ALL sections.
* If patternKey is specified, only sections containing that key will be modified.
*
* @param filePath The path to the INI file.
* @param patternKey The key to search for (empty = all sections).
* @param newKey The new key to add.
* @param newValue The value for the new key.
*/
void addKeyToMatchingSections(const std::string& filePath, const std::string& patternKey,
const std::string& newKey, const std::string& newValue);
/**
* @brief Removes a key from all sections that contain a specified pattern key.
*
* If patternKey is empty, the key will be removed from ALL sections.
* If patternKey is specified, only sections containing that key will have keyToRemove deleted.
*
* @param filePath The path to the INI file.
* @param patternKey The key to search for (empty = all sections).
* @param keyToRemove The key to remove from matching sections.
*/
void removeKeyFromMatchingSections(const std::string& filePath, const std::string& patternKey,
const std::string& keyToRemove);
} }
#endif #endif

View File

@@ -12,8 +12,9 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#pragma once #pragma once
#ifndef JSON_FUNCS_HPP #ifndef JSON_FUNCS_HPP
#define JSON_FUNCS_HPP #define JSON_FUNCS_HPP

View File

@@ -14,7 +14,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
@@ -70,7 +70,7 @@ namespace ult {
// Function to read file into a vector of strings // Function to read file into a vector of strings
std::vector<std::string> readListFromFile(const std::string& filePath, size_t maxLines=0); std::vector<std::string> readListFromFile(const std::string& filePath, size_t maxLines=0, bool preserveNewlines = false);
// Function to get an entry from the list based on the index // Function to get an entry from the list based on the index
@@ -89,7 +89,7 @@ namespace ult {
// Function to read file into a set of strings // Function to read file into a set of strings
std::unordered_set<std::string> readSetFromFile(const std::string& filePath); std::unordered_set<std::string> readSetFromFile(const std::string& filePath, const std::string& packagePath = "");
// Function to write a set to a file // Function to write a set to a file

View File

@@ -13,7 +13,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2024-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#pragma once #pragma once

View File

@@ -14,7 +14,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#pragma once #pragma once
@@ -36,6 +36,7 @@
#include "get_funcs.hpp" #include "get_funcs.hpp"
#include <queue> #include <queue>
#include <mutex> #include <mutex>
#include <unordered_set>
namespace ult { namespace ult {
@@ -143,7 +144,7 @@ namespace ult {
* *
* @param pathPattern The pattern used to match and delete files or directories. * @param pathPattern The pattern used to match and delete files or directories.
*/ */
void deleteFileOrDirectoryByPattern(const std::string& pathPattern, const std::string& logSource = ""); void deleteFileOrDirectoryByPattern(const std::string& pathPattern, const std::string& logSourc = "", const std::unordered_set<std::string>* filterSet = nullptr);
void moveDirectory(const std::string& sourcePath, const std::string& destinationPath, void moveDirectory(const std::string& sourcePath, const std::string& destinationPath,
@@ -176,12 +177,16 @@ namespace ult {
* *
* This function identifies files or directories that match the `sourcePathPattern` and moves them to the `destinationPath`. * This function identifies files or directories that match the `sourcePathPattern` and moves them to the `destinationPath`.
* It processes each matching entry in the source directory pattern and moves them to the specified destination. * It processes each matching entry in the source directory pattern and moves them to the specified destination.
* Files/directories in the filterSet will be skipped.
* *
* @param sourcePathPattern The pattern used to match files or directories to be moved. * @param sourcePathPattern The pattern used to match files or directories to be moved.
* @param destinationPath The destination directory where matching files or directories will be moved. * @param destinationPath The destination directory where matching files or directories will be moved.
* @param logSource Optional log source identifier.
* @param logDestination Optional log destination identifier.
* @param filterSet Optional set of paths to exclude from moving (nullptr to move all).
*/ */
void moveFilesOrDirectoriesByPattern(const std::string& sourcePathPattern, const std::string& destinationPath, void moveFilesOrDirectoriesByPattern(const std::string& sourcePathPattern, const std::string& destinationPath,
const std::string& logSource = "", const std::string& logDestination = ""); const std::string& logSource = "", const std::string& logDestination = "", const std::unordered_set<std::string>* filterSet = nullptr);
@@ -227,12 +232,16 @@ namespace ult {
* *
* This function identifies files or directories that match the `sourcePathPattern` and copies them to the `toDirectory`. * This function identifies files or directories that match the `sourcePathPattern` and copies them to the `toDirectory`.
* It processes each matching entry in the source directory pattern and copies them to the specified destination. * It processes each matching entry in the source directory pattern and copies them to the specified destination.
* Files/directories in the filterSet will be skipped.
* *
* @param sourcePathPattern The pattern used to match files or directories to be copied. * @param sourcePathPattern The pattern used to match files or directories to be copied.
* @param toDirectory The destination directory where matching files or directories will be copied. * @param toDirectory The destination directory where matching files or directories will be copied.
* @param logSource Optional log source identifier.
* @param logDestination Optional log destination identifier.
* @param filterSet Optional set of paths to exclude from copying (nullptr to copy all).
*/ */
void copyFileOrDirectoryByPattern(const std::string& sourcePathPattern, const std::string& toDirectory, void copyFileOrDirectoryByPattern(const std::string& sourcePathPattern, const std::string& toDirectory,
const std::string& logSource = "", const std::string& logDestination = ""); const std::string& logSource = "", const std::string& logDestination = "", const std::unordered_set<std::string>* filterSet = nullptr);

View File

@@ -14,7 +14,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#pragma once #pragma once
@@ -40,7 +40,7 @@ namespace ult {
extern int stoi(const std::string& str, std::size_t* pos = nullptr, int base = 10); extern int stoi(const std::string& str, std::size_t* pos = nullptr, int base = 10);
extern float stof(const std::string& str); extern float stof(const std::string& str);
extern bool canConvertToInt(const std::string& str); //extern bool canConvertToInt(const std::string& str);
/** /**
* @brief A lightweight string stream class that mimics basic functionality of std::istringstream. * @brief A lightweight string stream class that mimics basic functionality of std::istringstream.
@@ -144,7 +144,7 @@ namespace ult {
* @param input The input string to process. * @param input The input string to process.
* @return The string with multiple slashes replaced. * @return The string with multiple slashes replaced.
*/ */
std::string replaceMultipleSlashes(const std::string& input); //std::string replaceMultipleSlashes(const std::string& input);
void resolveDirectoryTraversal(std::string& path); void resolveDirectoryTraversal(std::string& path);

View File

@@ -18,7 +18,7 @@
* altered or removed. * altered or removed.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
@@ -54,39 +54,39 @@
#include <map> #include <map>
#include <barrier> #include <barrier>
#ifndef APPROXIMATE_cos //#ifndef APPROXIMATE_cos
// Approximation for cos(x) using Taylor series around 0 //// Approximation for cos(x) using Taylor series around 0
#define APPROXIMATE_cos(x) (1 - (x) * (x) / 2 + (x) * (x) * (x) * (x) / 24) // valid for small x //#define APPROXIMATE_cos(x) (1 - (x) * (x) / 2 + (x) * (x) * (x) * (x) / 24) // valid for small x
#endif //#endif
//
//
#ifndef APPROXIMATE_ifloor //#ifndef APPROXIMATE_ifloor
#define APPROXIMATE_ifloor(x) ((int)((x) >= 0 ? (x) : (x) - 1)) // truncate toward negative infinity //#define APPROXIMATE_ifloor(x) ((int)((x) >= 0 ? (x) : (x) - 1)) // truncate toward negative infinity
#define APPROXIMATE_iceil(x) ((int)((x) == (int)(x) ? (x) : ((x) > 0 ? (int)(x) + 1 : (int)(x)))) // truncate toward positive infinity //#define APPROXIMATE_iceil(x) ((int)((x) == (int)(x) ? (x) : ((x) > 0 ? (int)(x) + 1 : (int)(x)))) // truncate toward positive infinity
#endif //#endif
//
#ifndef APPROXIMATE_sqrt //#ifndef APPROXIMATE_sqrt
// Fast approximation for sqrt using Newton's method //// Fast approximation for sqrt using Newton's method
#define APPROXIMATE_sqrt(x) ((x) <= 0 ? 0 : (x) / 2.0 * (3.0 - ((x) * (x) * 0.5))) // Approximation for x close to 1 //#define APPROXIMATE_sqrt(x) ((x) <= 0 ? 0 : (x) / 2.0 * (3.0 - ((x) * (x) * 0.5))) // Approximation for x close to 1
#define APPROXIMATE_pow(x, y) ((y) == 0 ? 1 : ((y) == 1 ? (x) : APPROXIMATE_sqrt(x))) // limited to approximate sqrt if y=0.5 //#define APPROXIMATE_pow(x, y) ((y) == 0 ? 1 : ((y) == 1 ? (x) : APPROXIMATE_sqrt(x))) // limited to approximate sqrt if y=0.5
#endif //#endif
//
#ifndef APPROXIMATE_fmod //#ifndef APPROXIMATE_fmod
#define APPROXIMATE_fmod(x, y) ((x) - ((int)((x) / (y)) * (y))) // equivalent to x - floor(x/y) * y //#define APPROXIMATE_fmod(x, y) ((x) - ((int)((x) / (y)) * (y))) // equivalent to x - floor(x/y) * y
#endif //#endif
//
#ifndef APPROXIMATE_cos //#ifndef APPROXIMATE_cos
// Approximation for cos(x) using Taylor series around 0 //// Approximation for cos(x) using Taylor series around 0
#define APPROXIMATE_cos(x) (1 - (x) * (x) / 2 + (x) * (x) * (x) * (x) / 24) // valid for small x //#define APPROXIMATE_cos(x) (1 - (x) * (x) / 2 + (x) * (x) * (x) * (x) / 24) // valid for small x
#endif //#endif
//
#ifndef APPROXIMATE_acos //#ifndef APPROXIMATE_acos
#define APPROXIMATE_acos(x) (1.5708 - (x) - (x)*(x)*(x) / 6) // limited approximation for acos in range [-1, 1] //#define APPROXIMATE_acos(x) (1.5708 - (x) - (x)*(x)*(x) / 6) // limited approximation for acos in range [-1, 1]
#endif //#endif
//
#ifndef APPROXIMATE_fabs //#ifndef APPROXIMATE_fabs
#define APPROXIMATE_fabs(x) ((x) < 0 ? -(x) : (x)) //#define APPROXIMATE_fabs(x) ((x) < 0 ? -(x) : (x))
#endif //#endif
struct OverlayCombo { struct OverlayCombo {
std::string path; // full overlay path std::string path; // full overlay path
@@ -99,6 +99,35 @@ struct SwapDepth {
}; };
namespace ult { namespace ult {
// math funcs
inline double cos(double x) {
static constexpr double PI = 3.14159265358979323846;
static constexpr double TWO_PI = 6.28318530717958647692;
static constexpr double HALF_PI = 1.57079632679489661923;
// Fast normalization using multiply instead of divide when possible
x = x - TWO_PI * static_cast<int>(x * 0.159154943091895); // 1/(2π)
if (x < 0) x += TWO_PI;
// Use symmetry to reduce range
int sign = 1;
if (x > PI) {
x -= PI;
sign = -1;
}
if (x > HALF_PI) {
x = PI - x;
sign = -sign;
}
// Horner's method for faster polynomial evaluation (fewer operations)
// 5-term minimax polynomial for [0, π/2] - accurate to ~10^-8
const double x2 = x * x;
return sign * (1.0 + x2 * (-0.5 + x2 * (0.04166666666666666 + x2 * (-0.001388888888888889 + x2 * (0.0000248015873015873 - x2 * 0.0000002755731922398589)))));
}
extern bool correctFrameSize; // for detecting the correct Overlay display size extern bool correctFrameSize; // for detecting the correct Overlay display size
extern u16 DefaultFramebufferWidth; ///< Width of the framebuffer extern u16 DefaultFramebufferWidth; ///< Width of the framebuffer
@@ -182,6 +211,9 @@ namespace ult {
extern std::atomic<float> selectWidth; extern std::atomic<float> selectWidth;
extern std::atomic<float> nextPageWidth; extern std::atomic<float> nextPageWidth;
extern std::atomic<bool> inMainMenu; extern std::atomic<bool> inMainMenu;
extern std::atomic<bool> inHiddenMode;
extern std::atomic<bool> inSettingsMenu;
extern std::atomic<bool> inSubSettingsMenu;
extern std::atomic<bool> inOverlaysPage; extern std::atomic<bool> inOverlaysPage;
extern std::atomic<bool> inPackagesPage; extern std::atomic<bool> inPackagesPage;
@@ -200,7 +232,7 @@ namespace ult {
//bool progressAnimation = false; //bool progressAnimation = false;
extern bool disableTransparency; extern bool disableTransparency;
//bool useCustomWallpaper = false; //bool useCustomWallpaper = false;
extern bool useMemoryExpansion; //extern bool useMemoryExpansion;
extern bool useOpaqueScreenshots; extern bool useOpaqueScreenshots;
extern std::atomic<bool> onTrackBar; extern std::atomic<bool> onTrackBar;
@@ -344,6 +376,7 @@ namespace ult {
extern std::string HIDE_OVERLAY; extern std::string HIDE_OVERLAY;
extern std::string HIDE_PACKAGE; extern std::string HIDE_PACKAGE;
extern std::string LAUNCH_ARGUMENTS; extern std::string LAUNCH_ARGUMENTS;
extern std::string FORCE_AMS110_SUPPORT;
extern std::string QUICK_LAUNCH; extern std::string QUICK_LAUNCH;
extern std::string BOOT_COMMANDS; extern std::string BOOT_COMMANDS;
extern std::string EXIT_COMMANDS; extern std::string EXIT_COMMANDS;
@@ -371,6 +404,8 @@ namespace ult {
extern std::string USER_GUIDE; extern std::string USER_GUIDE;
extern std::string SHOW_HIDDEN; extern std::string SHOW_HIDDEN;
extern std::string SHOW_DELETE; extern std::string SHOW_DELETE;
extern std::string SHOW_UNSUPPORTED;
extern std::string PAGE_SWAP; extern std::string PAGE_SWAP;
extern std::string RIGHT_SIDE_MODE; extern std::string RIGHT_SIDE_MODE;
extern std::string OVERLAY_VERSIONS; extern std::string OVERLAY_VERSIONS;
@@ -379,7 +414,7 @@ namespace ult {
//extern std::string VERSION_LABELS; //extern std::string VERSION_LABELS;
extern std::string KEY_COMBO; extern std::string KEY_COMBO;
extern std::string MODE; extern std::string MODE;
extern std::string MODES; extern std::string LAUNCH_MODES;
extern std::string LANGUAGE; extern std::string LANGUAGE;
extern std::string OVERLAY_INFO; extern std::string OVERLAY_INFO;
extern std::string SOFTWARE_UPDATE; extern std::string SOFTWARE_UPDATE;
@@ -394,11 +429,19 @@ namespace ult {
extern std::string VENDOR; extern std::string VENDOR;
extern std::string MODEL; extern std::string MODEL;
extern std::string STORAGE; extern std::string STORAGE;
extern std::string NOTICE; //extern std::string NOTICE;
extern std::string UTILIZES; //extern std::string UTILIZES;
extern std::string MEMORY_EXPANSION; extern std::string OVERLAY_MEMORY;
extern std::string REBOOT_REQUIRED; extern std::string NOT_ENOUGH_MEMORY;
extern std::string WALLPAPER_SUPPORT_DISABLED;
extern std::string SOUND_SUPPORT_DISABLED;
extern std::string WALLPAPER_SUPPORT_ENABLED;
extern std::string SOUND_SUPPORT_ENABLED;
extern std::string EXIT_OVERLAY_SYSTEM;
//extern std::string MEMORY_EXPANSION;
//extern std::string REBOOT_REQUIRED;
extern std::string LOCAL_IP; extern std::string LOCAL_IP;
extern std::string WALLPAPER; extern std::string WALLPAPER;
extern std::string THEME; extern std::string THEME;
@@ -443,9 +486,16 @@ namespace ult {
extern std::string ULTRAHAND_HAS_STARTED; extern std::string ULTRAHAND_HAS_STARTED;
extern std::string NEW_UPDATE_IS_AVAILABLE; extern std::string NEW_UPDATE_IS_AVAILABLE;
extern std::string REBOOT_IS_REQUIRED; //extern std::string REBOOT_IS_REQUIRED;
extern std::string HOLD_A_TO_DELETE; //extern std::string HOLD_A_TO_DELETE;
extern std::string DELETE_PACKAGE;
extern std::string DELETE_OVERLAY;
extern std::string SELECTION_IS_EMPTY; extern std::string SELECTION_IS_EMPTY;
extern std::string FORCED_SUPPORT_WARNING;
extern std::string TASK_IS_COMPLETE;
extern std::string TASK_HAS_FAILED;
//extern std::string PACKAGE_VERSIONS; //extern std::string PACKAGE_VERSIONS;
//extern std::string PROGRESS_ANIMATION; //extern std::string PROGRESS_ANIMATION;
@@ -456,6 +506,8 @@ namespace ult {
extern std::string BOOT_ENTRY; extern std::string BOOT_ENTRY;
#endif #endif
extern std::string INCOMPATIBLE_WARNING;
extern std::string SYSTEM_RAM;
extern std::string FREE; extern std::string FREE;
extern std::string DEFAULT_CHAR_WIDTH; extern std::string DEFAULT_CHAR_WIDTH;
@@ -567,7 +619,7 @@ namespace ult {
float calculateAmplitude(float x, float peakDurationFactor = 0.25f); //float calculateAmplitude(float x, float peakDurationFactor = 0.25f);
extern std::atomic<bool> refreshWallpaperNow; extern std::atomic<bool> refreshWallpaperNow;
@@ -685,9 +737,44 @@ namespace ult {
extern bool cleanVersionLabels, hideOverlayVersions, hidePackageVersions, useLibultrahandTitles, useLibultrahandVersions, usePackageTitles, usePackageVersions; extern bool cleanVersionLabels, hideOverlayVersions, hidePackageVersions, useLibultrahandTitles, useLibultrahandVersions, usePackageTitles, usePackageVersions;
// nx-ovlloader settings
enum class OverlayHeapSize : u64 {
Size_4MB = 0x400000,
Size_6MB = 0x600000,
Size_8MB = 0x800000
};
// Static cache
static struct {
bool initialized = false;
OverlayHeapSize cachedSize = OverlayHeapSize::Size_6MB;
u64 customSizeMB = 0; // NEW: store custom size in MB
} heapSizeCache;
// Helper function to convert MB to bytes
extern u64 mbToBytes(u32 mb);
// Helper function to convert bytes to MB
extern u32 bytesToMB(u64 bytes);
// Implementation
OverlayHeapSize getCurrentHeapSize();
extern OverlayHeapSize currentHeapSize;
bool setOverlayHeapSize(OverlayHeapSize heapSize);
// Implementation
bool requestOverlayExit();
extern const std::string loaderInfo; extern const std::string loaderInfo;
extern const std::string loaderTitle; extern std::string loaderTitle;
extern const bool expandedMemory; extern bool expandedMemory;
extern bool furtherExpandedMemory;
extern bool limitedMemory;
extern std::string versionLabel; extern std::string versionLabel;

View File

@@ -19,7 +19,7 @@
* altered or removed. * altered or removed.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2024-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#pragma once #pragma once
@@ -43,7 +43,7 @@
#include "download_funcs.hpp" #include "download_funcs.hpp"
#include "mod_funcs.hpp" #include "mod_funcs.hpp"
#include "tsl_utils.hpp" #include "tsl_utils.hpp"
#include "audio_player.hpp" #include "audio.hpp"
#include "haptics.hpp" #include "haptics.hpp"
#endif // ULTRA_HPP #endif // ULTRA_HPP

View File

@@ -1,5 +1,5 @@
/******************************************************************************** /********************************************************************************
* File: audio_player.cpp * File: audio.cpp
* Author: ppkantorski * Author: ppkantorski
* Description: * Description:
* Memory-optimized version with reduced allocation overhead and chunked I/O. * Memory-optimized version with reduced allocation overhead and chunked I/O.
@@ -19,17 +19,17 @@
* Copyright (c) 2025 ppkantorski * Copyright (c) 2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#include "audio_player.hpp" #include "audio.hpp"
namespace ult { namespace ult {
bool AudioPlayer::m_initialized = false; bool Audio::m_initialized = false;
std::atomic<bool> AudioPlayer::m_enabled{true}; std::atomic<bool> Audio::m_enabled{true};
float AudioPlayer::m_masterVolume = 0.6f; float Audio::m_masterVolume = 0.6f;
bool AudioPlayer::m_lastDockedState = false; bool Audio::m_lastDockedState = false;
std::vector<AudioPlayer::CachedSound> AudioPlayer::m_cachedSounds; std::vector<Audio::CachedSound> Audio::m_cachedSounds;
std::mutex AudioPlayer::m_audioMutex; std::mutex Audio::m_audioMutex;
bool AudioPlayer::initialize() { bool Audio::initialize() {
std::lock_guard<std::mutex> lock(m_audioMutex); std::lock_guard<std::mutex> lock(m_audioMutex);
if (m_initialized) return true; if (m_initialized) return true;
@@ -41,13 +41,13 @@ namespace ult {
m_initialized = true; m_initialized = true;
m_cachedSounds.resize(static_cast<uint32_t>(SoundType::Count)); m_cachedSounds.resize(static_cast<uint32_t>(SoundType::Count));
m_lastDockedState = isDocked(); m_lastDockedState = ult::consoleIsDocked();
reloadAllSounds(); reloadAllSounds();
return true; return true;
} }
void AudioPlayer::exit() { void Audio::exit() {
std::lock_guard<std::mutex> lock(m_audioMutex); std::lock_guard<std::mutex> lock(m_audioMutex);
// Free all cached sound buffers // Free all cached sound buffers
@@ -67,13 +67,13 @@ namespace ult {
} }
} }
void AudioPlayer::reloadAllSounds() { void Audio::reloadAllSounds() {
for (uint32_t i = 0; i < static_cast<uint32_t>(SoundType::Count); ++i) { for (uint32_t i = 0; i < static_cast<uint32_t>(SoundType::Count); ++i) {
loadSoundFromWav(static_cast<SoundType>(i), m_soundPaths[i]); loadSoundFromWav(static_cast<SoundType>(i), m_soundPaths[i]);
} }
} }
void AudioPlayer::unloadAllSounds(const std::initializer_list<SoundType>& excludeSounds) { void Audio::unloadAllSounds(const std::initializer_list<SoundType>& excludeSounds) {
std::lock_guard<std::mutex> lock(m_audioMutex); std::lock_guard<std::mutex> lock(m_audioMutex);
if (!m_initialized) return; if (!m_initialized) return;
@@ -95,10 +95,10 @@ namespace ult {
} }
} }
bool AudioPlayer::reloadIfDockedChanged() { bool Audio::reloadIfDockedChanged() {
if (!m_initialized) return false; if (!m_initialized) return false;
const bool currentDocked = isDocked(); const bool currentDocked = ult::consoleIsDocked();
if (currentDocked == m_lastDockedState) return false; if (currentDocked == m_lastDockedState) return false;
std::lock_guard<std::mutex> lock(m_audioMutex); std::lock_guard<std::mutex> lock(m_audioMutex);
@@ -108,7 +108,7 @@ namespace ult {
return true; return true;
} }
bool AudioPlayer::loadSoundFromWav(SoundType type, const char* path) { bool Audio::loadSoundFromWav(SoundType type, const char* path) {
const uint32_t idx = static_cast<uint32_t>(type); const uint32_t idx = static_cast<uint32_t>(type);
if (!m_initialized || idx >= static_cast<uint32_t>(SoundType::Count)) return false; if (!m_initialized || idx >= static_cast<uint32_t>(SoundType::Count)) return false;
@@ -253,7 +253,7 @@ namespace ult {
return true; return true;
} }
void AudioPlayer::playSound(SoundType type) { void Audio::playSound(SoundType type) {
// Lock-free check - SAFE with atomic // Lock-free check - SAFE with atomic
if (!m_enabled.load(std::memory_order_relaxed)) return; if (!m_enabled.load(std::memory_order_relaxed)) return;
@@ -286,27 +286,27 @@ namespace ult {
audoutPlayBuffer(&audioBuffer, &rel); audoutPlayBuffer(&audioBuffer, &rel);
} }
void AudioPlayer::setMasterVolume(float v) { void Audio::setMasterVolume(float v) {
std::lock_guard<std::mutex> lock(m_audioMutex); std::lock_guard<std::mutex> lock(m_audioMutex);
m_masterVolume = std::clamp(v, 0.0f, 1.0f); m_masterVolume = std::clamp(v, 0.0f, 1.0f);
} }
void AudioPlayer::setEnabled(bool e) { void Audio::setEnabled(bool e) {
m_enabled.store(e, std::memory_order_relaxed); m_enabled.store(e, std::memory_order_relaxed);
} }
bool AudioPlayer::isEnabled() { bool Audio::isEnabled() {
return m_enabled.load(std::memory_order_relaxed); return m_enabled.load(std::memory_order_relaxed);
} }
bool AudioPlayer::isDocked() { //bool Audio::isDocked() {
Result rc = apmInitialize(); // Result rc = apmInitialize();
if (R_FAILED(rc)) return false; // if (R_FAILED(rc)) return false;
//
ApmPerformanceMode perfMode = ApmPerformanceMode_Invalid; // ApmPerformanceMode perfMode = ApmPerformanceMode_Invalid;
rc = apmGetPerformanceMode(&perfMode); // rc = apmGetPerformanceMode(&perfMode);
apmExit(); // apmExit();
//
return R_SUCCEEDED(rc) && (perfMode == ApmPerformanceMode_Boost); // return R_SUCCEEDED(rc) && (perfMode == ApmPerformanceMode_Boost);
} //}
} }

View File

@@ -4,6 +4,15 @@
* Description: * Description:
* This source file contains the implementation of debugging functions for the * This source file contains the implementation of debugging functions for the
* Ultrahand Overlay project. * Ultrahand Overlay project.
*
* For the latest updates and contributions, visit the project's GitHub repository.
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
*
* Note: Please be aware that this notice cannot be altered or removed. It is a part
* of the project's documentation and must remain intact.
*
* Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#include "debug_funcs.hpp" #include "debug_funcs.hpp"

View File

@@ -14,7 +14,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#include "download_funcs.hpp" #include "download_funcs.hpp"
@@ -23,8 +23,8 @@
namespace ult { namespace ult {
// Base loader definitions // Base loader definitions
size_t DOWNLOAD_READ_BUFFER = 8*1024;//64 * 1024;//4096*10; size_t DOWNLOAD_READ_BUFFER = 32*1024;//64 * 1024;//4096*10;
size_t DOWNLOAD_WRITE_BUFFER = 8*1024;//64 * 1024; size_t DOWNLOAD_WRITE_BUFFER = 16*1024;//64 * 1024;
size_t UNZIP_READ_BUFFER = 32*1024;//131072*2;//4096*4; size_t UNZIP_READ_BUFFER = 32*1024;//131072*2;//4096*4;
size_t UNZIP_WRITE_BUFFER = 16*1024;//131072*2;//4096*4; size_t UNZIP_WRITE_BUFFER = 16*1024;//131072*2;//4096*4;
@@ -131,7 +131,7 @@ int progressCallback(void *ptr, curl_off_t totalToDownload, curl_off_t nowDownlo
// } // }
//} //}
std::unique_ptr<char[]> globalWriteBuffer; //std::unique_ptr<char[]> writeBuffer;
/** /**
* @brief Downloads a file from a URL to a specified destination. * @brief Downloads a file from a URL to a specified destination.
@@ -192,12 +192,12 @@ bool downloadFile(const std::string& url, const std::string& toDestination, bool
} }
// ADD THIS: Set up write buffer for better performance // ADD THIS: Set up write buffer for better performance
//std::unique_ptr<char[]> globalWriteBuffer; std::unique_ptr<char[]> writeBuffer;
if (DOWNLOAD_WRITE_BUFFER > 0) { if (DOWNLOAD_WRITE_BUFFER > 0) {
//if (!globalWriteBuffer) //if (!writeBuffer)
globalWriteBuffer = std::make_unique<char[]>(DOWNLOAD_WRITE_BUFFER); writeBuffer = std::make_unique<char[]>(DOWNLOAD_WRITE_BUFFER);
// _IOFBF = full buffering, _IOLBF = line buffering, _IONBF = no buffering // _IOFBF = full buffering, _IOLBF = line buffering, _IONBF = no buffering
setvbuf(file.get(), globalWriteBuffer.get(), _IOFBF, DOWNLOAD_WRITE_BUFFER); setvbuf(file.get(), writeBuffer.get(), _IOFBF, DOWNLOAD_WRITE_BUFFER);
} }
//setvbuf(file.get(), NULL, _IOFBF, DOWNLOAD_WRITE_BUFFER); //setvbuf(file.get(), NULL, _IOFBF, DOWNLOAD_WRITE_BUFFER);
@@ -224,7 +224,7 @@ bool downloadFile(const std::string& url, const std::string& toDestination, bool
file.close(); file.close();
#else #else
file.reset(); file.reset();
globalWriteBuffer.reset(); writeBuffer.reset();
#endif #endif
return false; return false;
} }
@@ -277,12 +277,15 @@ bool downloadFile(const std::string& url, const std::string& toDestination, bool
//const bool wasAborted = (result == CURLE_ABORTED_BY_CALLBACK || //const bool wasAborted = (result == CURLE_ABORTED_BY_CALLBACK ||
// abortDownload.load(std::memory_order_acquire)); // abortDownload.load(std::memory_order_acquire));
// Check HTTP response code BEFORE closing file/curl
long http_code = 0;
curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &http_code);
#if USING_FSTREAM_DIRECTIVE #if USING_FSTREAM_DIRECTIVE
file.close(); file.close();
#else #else
file.reset(); file.reset();
globalWriteBuffer.reset(); writeBuffer.reset();
#endif #endif
curl.reset(); curl.reset();
@@ -310,6 +313,20 @@ bool downloadFile(const std::string& url, const std::string& toDestination, bool
//socketExit(); //socketExit();
// Check for HTTP errors (404, 500, etc.)
if (result == CURLE_OK && (http_code < 200 || http_code >= 300)) {
#if USING_LOGGING_DIRECTIVE
if (!disableLogging)
logMessage("HTTP error " + std::to_string(http_code) + " downloading: " + url);
#endif
deleteFileOrDirectory(tempFilePath);
if (!noPercentagePolling) {
downloadPercentage.store(-1, std::memory_order_release);
}
return false;
}
if (result != CURLE_OK) { if (result != CURLE_OK) {
#if USING_LOGGING_DIRECTIVE #if USING_LOGGING_DIRECTIVE
if (result == CURLE_ABORTED_BY_CALLBACK) { if (result == CURLE_ABORTED_BY_CALLBACK) {
@@ -382,6 +399,7 @@ bool downloadFile(const std::string& url, const std::string& toDestination, bool
} }
moveFile(tempFilePath, destination); moveFile(tempFilePath, destination);
return true; return true;
} }
@@ -622,7 +640,7 @@ bool unzipFile(const std::string& zipFilePath, const std::string& toDestination)
const size_t bufferSize = UNZIP_WRITE_BUFFER; const size_t bufferSize = UNZIP_WRITE_BUFFER;
//std::unique_ptr<char[]> buffer = std::make_unique<char[]>(bufferSize); //std::unique_ptr<char[]> buffer = std::make_unique<char[]>(bufferSize);
globalWriteBuffer = std::make_unique<char[]>(bufferSize); std::unique_ptr<char[]> writeBuffer = std::make_unique<char[]>(bufferSize);
char filenameBuffer[512]; // Stack allocated for filename reading char filenameBuffer[512]; // Stack allocated for filename reading
@@ -758,14 +776,14 @@ bool unzipFile(const std::string& zipFilePath, const std::string& toDestination)
fileBytesProcessed = 0; fileBytesProcessed = 0;
while ((bytesRead = unzReadCurrentFile(zipFile, globalWriteBuffer.get(), bufferSize)) > 0) { while ((bytesRead = unzReadCurrentFile(zipFile, writeBuffer.get(), bufferSize)) > 0) {
if (abortUnzip.load(std::memory_order_relaxed)) { if (abortUnzip.load(std::memory_order_relaxed)) {
extractSuccess = false; extractSuccess = false;
break; // RAII will handle cleanup break; // RAII will handle cleanup
} }
// Write data to file // Write data to file
if (outputFile.write(globalWriteBuffer.get(), bytesRead) != static_cast<size_t>(bytesRead)) { if (outputFile.write(writeBuffer.get(), bytesRead) != static_cast<size_t>(bytesRead)) {
extractSuccess = false; extractSuccess = false;
break; break;
} }
@@ -845,7 +863,7 @@ bool unzipFile(const std::string& zipFilePath, const std::string& toDestination)
result = unzGoToNextFile(zipFile); result = unzGoToNextFile(zipFile);
} }
globalWriteBuffer.reset(); writeBuffer.reset();
// Check final abort state // Check final abort state
if (abortUnzip.load(std::memory_order_relaxed)) { if (abortUnzip.load(std::memory_order_relaxed)) {

View File

@@ -15,7 +15,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#include "get_funcs.hpp" #include "get_funcs.hpp"

View File

@@ -13,7 +13,7 @@
* altered or removed. * altered or removed.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#include "global_vars.hpp" #include "global_vars.hpp"
@@ -26,6 +26,10 @@ namespace ult {
const std::string TESLA_CONFIG_PATH = ROOT_PATH + "config/tesla/"; const std::string TESLA_CONFIG_PATH = ROOT_PATH + "config/tesla/";
const std::string SWITCH_PATH = ROOT_PATH + "switch/"; const std::string SWITCH_PATH = ROOT_PATH + "switch/";
const std::string NX_OVLLOADER_PATH = ROOT_PATH + "config/nx-ovlloader/";
const std::string OVL_HEAP_CONFIG_PATH = NX_OVLLOADER_PATH + "heap_size.bin";
const std::string OVL_EXIT_FLAG_PATH = NX_OVLLOADER_PATH + "exit_flag.bin";
// Filenames // Filenames
CONSTEXPR_STRING std::string CONFIG_FILENAME = "config.ini"; CONSTEXPR_STRING std::string CONFIG_FILENAME = "config.ini";
const std::string BOOT_PACKAGE_FILENAME = "boot_package.ini"; const std::string BOOT_PACKAGE_FILENAME = "boot_package.ini";
@@ -59,7 +63,8 @@ namespace ult {
std::string THEME_CONFIG_INI_PATH = BASE_CONFIG_PATH + THEME_FILENAME; std::string THEME_CONFIG_INI_PATH = BASE_CONFIG_PATH + THEME_FILENAME;
std::string WALLPAPER_PATH = BASE_CONFIG_PATH + WALLPAPER_FILENAME; std::string WALLPAPER_PATH = BASE_CONFIG_PATH + WALLPAPER_FILENAME;
const std::string DOWNLOADS_PATH = BASE_CONFIG_PATH + "downloads/"; const std::string DOWNLOADS_PATH = BASE_CONFIG_PATH + "downloads/";
const std::string EXPANSION_PATH = BASE_CONFIG_PATH + "expansion/";
//const std::string EXPANSION_PATH = BASE_CONFIG_PATH + "expansion/";
const std::string FUSE_DATA_INI_PATH = BASE_CONFIG_PATH + FUSE_FILENAME; const std::string FUSE_DATA_INI_PATH = BASE_CONFIG_PATH + FUSE_FILENAME;
const std::string PACKAGE_PATH = SWITCH_PATH + ".packages/"; const std::string PACKAGE_PATH = SWITCH_PATH + ".packages/";
const std::string OVERLAY_PATH = SWITCH_PATH + ".overlays/"; const std::string OVERLAY_PATH = SWITCH_PATH + ".overlays/";
@@ -79,12 +84,13 @@ namespace ult {
const std::string ULTRAHAND_REPO_URL = GITHUB_BASE_URL + "Ultrahand-Overlay/"; const std::string ULTRAHAND_REPO_URL = GITHUB_BASE_URL + "Ultrahand-Overlay/";
const std::string INCLUDED_THEME_FOLDER_URL = GITHUB_RAW_BASE_URL + "Ultrahand-Overlay/main/themes/"; const std::string INCLUDED_THEME_FOLDER_URL = GITHUB_RAW_BASE_URL + "Ultrahand-Overlay/main/themes/";
const std::string LATEST_RELEASE_INFO_URL = GITHUB_RAW_BASE_URL + "Ultrahand-Overlay/main/RELEASE.ini"; const std::string LATEST_RELEASE_INFO_URL = GITHUB_RAW_BASE_URL + "Ultrahand-Overlay/main/RELEASE.ini";
const std::string NX_OVLLOADER_ZIP_URL = GITHUB_BASE_URL + "nx-ovlloader/releases/latest/download/nx-ovlloader.zip"; const std::string LATEST_UPDATER_INI_URL = ULTRAHAND_REPO_URL + "releases/latest/download/update.ini";
const std::string NX_OVLLOADER_PLUS_ZIP_URL = GITHUB_BASE_URL + "nx-ovlloader/releases/latest/download/nx-ovlloader+.zip"; //const std::string NX_OVLLOADER_ZIP_URL = GITHUB_BASE_URL + "nx-ovlloader/releases/latest/download/nx-ovlloader.zip";
//const std::string NX_OVLLOADER_PLUS_ZIP_URL = GITHUB_BASE_URL + "nx-ovlloader/releases/latest/download/nx-ovlloader+.zip";
const std::string OLD_NX_OVLLOADER_ZIP_URL = GITHUB_BASE_URL + "nx-ovlloader/releases/download/v1.0.8/nx-ovlloader.zip"; const std::string OLD_NX_OVLLOADER_ZIP_URL = GITHUB_BASE_URL + "nx-ovlloader/releases/download/v1.0.8/nx-ovlloader.zip";
const std::string OLD_NX_OVLLOADER_PLUS_ZIP_URL = GITHUB_BASE_URL + "nx-ovlloader/releases/download/v1.0.8/nx-ovlloader+.zip"; //const std::string OLD_NX_OVLLOADER_PLUS_ZIP_URL = GITHUB_BASE_URL + "nx-ovlloader/releases/download/v1.0.8/nx-ovlloader+.zip";
const std::string UPDATER_PAYLOAD_URL = GITHUB_RAW_BASE_URL + "Ultrahand-Overlay/main/payloads/ultrahand_updater.bin"; const std::string UPDATER_PAYLOAD_URL = GITHUB_RAW_BASE_URL + "Ultrahand-Overlay/main/payloads/ultrahand_updater.bin";
const std::string SOUND_EFFECTS_URL = GITHUB_RAW_BASE_URL + "Ultrahand-Overlay/main/sounds/sounds.zip"; //const std::string SOUND_EFFECTS_URL = GITHUB_RAW_BASE_URL + "Ultrahand-Overlay/main/sounds/sounds.zip";
// Launch options // Launch options
const std::string LAUNCH_ARGS_STR = "launch_args"; const std::string LAUNCH_ARGS_STR = "launch_args";
@@ -140,6 +146,7 @@ namespace ult {
CONSTEXPR_STRING std::string FALSE_STR = "false"; CONSTEXPR_STRING std::string FALSE_STR = "false";
CONSTEXPR_STRING std::string GLOBAL_STR = "global"; CONSTEXPR_STRING std::string GLOBAL_STR = "global";
CONSTEXPR_STRING std::string DEFAULT_STR = "default"; CONSTEXPR_STRING std::string DEFAULT_STR = "default";
CONSTEXPR_STRING std::string HOLD_STR = "hold";
CONSTEXPR_STRING std::string SLOT_STR = "slot"; CONSTEXPR_STRING std::string SLOT_STR = "slot";
CONSTEXPR_STRING std::string OPTION_STR = "option"; CONSTEXPR_STRING std::string OPTION_STR = "option";
CONSTEXPR_STRING std::string FORWARDER_STR = "forwarder"; CONSTEXPR_STRING std::string FORWARDER_STR = "forwarder";
@@ -164,6 +171,7 @@ namespace ult {
CONSTEXPR_STRING std::string INPROGRESS_SYMBOL = "\u25CF"; CONSTEXPR_STRING std::string INPROGRESS_SYMBOL = "\u25CF";
CONSTEXPR_STRING std::string STAR_SYMBOL = "\u2605"; CONSTEXPR_STRING std::string STAR_SYMBOL = "\u2605";
CONSTEXPR_STRING std::string DIVIDER_SYMBOL = ""; CONSTEXPR_STRING std::string DIVIDER_SYMBOL = "";
CONSTEXPR_STRING std::string NOTIFY_HEADER = "";
const std::vector<std::string> THROBBER_SYMBOLS = {"", "", "", "", "", "", "", ""}; const std::vector<std::string> THROBBER_SYMBOLS = {"", "", "", "", "", "", "", ""};

View File

@@ -32,8 +32,11 @@ namespace ult {
static u64 doubleClickTick = 0; static u64 doubleClickTick = 0;
static u8 doubleClickPulse = 0; static u8 doubleClickPulse = 0;
static u32 cachedHandheldStyle = 0;
static u32 cachedPlayer1Style = 0;
// ===== Shared flags (accessible globally) ===== // ===== Shared flags (accessible globally) =====
std::atomic<bool> rumbleActive{false}; std::atomic<bool> clickActive{false};
std::atomic<bool> doubleClickActive{false}; std::atomic<bool> doubleClickActive{false};
// ===== Constants ===== // ===== Constants =====
@@ -41,144 +44,182 @@ namespace ult {
static constexpr u64 DOUBLE_CLICK_PULSE_DURATION_NS = 30'000'000ULL; static constexpr u64 DOUBLE_CLICK_PULSE_DURATION_NS = 30'000'000ULL;
static constexpr u64 DOUBLE_CLICK_GAP_NS = 100'000'000ULL; static constexpr u64 DOUBLE_CLICK_GAP_NS = 100'000'000ULL;
static constexpr HidVibrationValue clickDocked = { //static constexpr HidVibrationValue clickDocked = {
// .amp_low = 0.20f,
// .freq_low = 100.0f,
// .amp_high = 0.80f,
// .freq_high = 300.0f
//};
//
//static constexpr HidVibrationValue clickHandheld = {
// .amp_low = 0.20f,
// .freq_low = 100.0f,
// .amp_high = 0.80f,
// .freq_high = 300.0f
//};
static constexpr HidVibrationValue hapticsPreset = {
.amp_low = 0.20f, .amp_low = 0.20f,
.freq_low = 100.0f, .freq_low = 100.0f,
.amp_high = 0.80f, .amp_high = 0.80f,
.freq_high = 300.0f .freq_high = 300.0f
}; };
static constexpr HidVibrationValue clickHandheld = {
.amp_low = 0.25f,
.freq_low = 100.0f,
.amp_high = 1.0f,
.freq_high = 300.0f
};
static constexpr HidVibrationValue vibrationStop{0}; static constexpr HidVibrationValue vibrationStop{0};
// ===== Internal helpers ===== // ===== Internal helpers =====
static void initController(HidNpadIdType npad, HidVibrationDeviceHandle* handles, int count) { static inline void sendVibration(const HidVibrationValue* value) {
const u32 styleMask = hidGetNpadStyleSet(npad); if (cachedHandheldStyle)
if (styleMask)
hidInitializeVibrationDevices(handles, count, npad, static_cast<HidNpadStyleTag>(styleMask));
}
static void sendVibration(const HidVibrationValue* value) {
if (hidGetNpadStyleSet(HidNpadIdType_Handheld))
hidSendVibrationValue(vibHandheld, value); hidSendVibrationValue(vibHandheld, value);
if (hidGetNpadStyleSet(HidNpadIdType_No1)) { if (cachedPlayer1Style) {
hidSendVibrationValue(vibPlayer1Left, value); hidSendVibrationValue(vibPlayer1Left, value);
hidSendVibrationValue(vibPlayer1Right, value); hidSendVibrationValue(vibPlayer1Right, value);
} }
} }
// ===== Public API ===== static inline void sendVibration2x(const HidVibrationValue* value) {
void initRumble() { sendVibration(value);
//if (rumbleInitialized) return; sendVibration(value);
// Try to initialize whatever is available
// Don't check if controllers exist - let initController handle it
initController(HidNpadIdType_Handheld, &vibHandheld, 1);
HidVibrationDeviceHandle handles[2];
initController(HidNpadIdType_No1, handles, 2);
vibPlayer1Left = handles[0];
vibPlayer1Right = handles[1];
// Only mark as initialized if at least one controller was found
hidGetNpadStyleSet(HidNpadIdType_Handheld);
hidGetNpadStyleSet(HidNpadIdType_No1);
//rumbleInitialized = (handheldStyle || player1Style);
// If neither exist, stay uninitialized so we retry later
} }
//void deinitRumble() { // ===== Public API =====
void initHaptics() {
const u32 handheldStyle = hidGetNpadStyleSet(HidNpadIdType_Handheld);
const u32 player1Style = hidGetNpadStyleSet(HidNpadIdType_No1);
// Clear previous handles to avoid using stale handles if controllers were removed
vibHandheld = (HidVibrationDeviceHandle)0;
vibPlayer1Left = (HidVibrationDeviceHandle)0;
vibPlayer1Right = (HidVibrationDeviceHandle)0;
// Handheld
if (handheldStyle) {
hidInitializeVibrationDevices(&vibHandheld, 1,
HidNpadIdType_Handheld,
(HidNpadStyleTag)handheldStyle);
}
// Player 1 (left + right Joy-Con or Pro Controller)
if (player1Style) {
HidVibrationDeviceHandle tmp[2] = { (HidVibrationDeviceHandle)0, (HidVibrationDeviceHandle)0 };
hidInitializeVibrationDevices(tmp, 2,
HidNpadIdType_No1,
(HidNpadStyleTag)player1Style);
vibPlayer1Left = tmp[0];
vibPlayer1Right = tmp[1];
}
// Ensure cache is valid immediately after initHaptics()
cachedHandheldStyle = handheldStyle;
cachedPlayer1Style = player1Style;
}
//void deinitHaptics() {
// rumbleInitialized = false; // rumbleInitialized = false;
//} //}
void checkAndReinitRumble() { void checkAndReinitHaptics() {
static u32 lastHandheldStyle = 0; static u32 lastHandheldStyle = 0;
static u32 lastPlayer1Style = 0; static u32 lastPlayer1Style = 0;
const u32 currentHandheldStyle = hidGetNpadStyleSet(HidNpadIdType_Handheld); const u32 currentHandheldStyle = hidGetNpadStyleSet(HidNpadIdType_Handheld);
const u32 currentPlayer1Style = hidGetNpadStyleSet(HidNpadIdType_No1); const u32 currentPlayer1Style = hidGetNpadStyleSet(HidNpadIdType_No1);
// If not initialized but controllers exist, try to init // Reinitialize only if something changed (appearance/disappearance or style change)
// This handles the boot race condition where HID reports controllers //const bool changed =
// but vibration subsystem isn't ready yet // (currentHandheldStyle != lastHandheldStyle) || (currentPlayer1Style != lastPlayer1Style);
//if (!rumbleInitialized && (currentHandheldStyle || currentPlayer1Style)) {
// initRumble();
//}
// Reinit if controller configuration changed if ((currentHandheldStyle != lastHandheldStyle) || (currentPlayer1Style != lastPlayer1Style)) {
if (currentHandheldStyle != lastHandheldStyle || currentPlayer1Style != lastPlayer1Style) { initHaptics();
//rumbleInitialized = false;
initRumble();
} }
// Update last style tracking regardless
// Update last-known styles for change detection
lastHandheldStyle = currentHandheldStyle; lastHandheldStyle = currentHandheldStyle;
lastPlayer1Style = currentPlayer1Style; lastPlayer1Style = currentPlayer1Style;
// Update cached styles used by sendVibration()/rumble paths
cachedHandheldStyle = currentHandheldStyle;
cachedPlayer1Style = currentPlayer1Style;
} }
void rumbleClick() {
//if (!rumbleInitialized) {
// initRumble();
// if (!rumbleInitialized) return;
//}
sendVibration(hidGetNpadStyleSet(HidNpadIdType_Handheld) ? &clickHandheld : &clickDocked); void rumbleClick() {
rumbleActive.store(true, std::memory_order_release); // Use cached style bit instead of querying hid each call
//const HidVibrationValue* pattern = cachedHandheldStyle ? &clickHandheld : &clickDocked;
sendVibration(&vibrationStop);
//if (cachedHandheldStyle) {
// sendVibration(&clickHandheld);
// sendVibration(&clickHandheld);
//} else {
// sendVibration(&clickDocked);
// sendVibration(&clickDocked);
//}
sendVibration2x(&hapticsPreset);
clickActive.store(true, std::memory_order_release);
rumbleStartTick = armGetSystemTick(); rumbleStartTick = armGetSystemTick();
} }
void rumbleDoubleClick() { void rumbleDoubleClick() {
//if (!rumbleInitialized) { //onst HidVibrationValue* pattern = cachedHandheldStyle ? &clickHandheld : &clickDocked;
// initRumble(); sendVibration(&vibrationStop);
// if (!rumbleInitialized) return; //if (cachedHandheldStyle) {
// sendVibration(&clickHandheld);
// sendVibration(&clickHandheld);
//} else {
// sendVibration(&clickDocked);
// sendVibration(&clickDocked);
//} //}
sendVibration2x(&hapticsPreset);
sendVibration(hidGetNpadStyleSet(HidNpadIdType_Handheld) ? &clickHandheld : &clickDocked);
doubleClickActive.store(true, std::memory_order_release); doubleClickActive.store(true, std::memory_order_release);
doubleClickPulse = 1; doubleClickPulse = 1;
doubleClickTick = armGetSystemTick(); doubleClickTick = armGetSystemTick(); // Set ONCE
} }
void processRumbleStop(u64 nowNs) { void processRumbleStop(u64 nowNs) {
if (rumbleActive.load(std::memory_order_acquire) && if (clickActive.load(std::memory_order_acquire) &&
nowNs - armTicksToNs(rumbleStartTick) >= RUMBLE_DURATION_NS) { nowNs - armTicksToNs(rumbleStartTick) >= RUMBLE_DURATION_NS) {
sendVibration(&vibrationStop); sendVibration(&vibrationStop);
rumbleActive.store(false, std::memory_order_release); clickActive.store(false, std::memory_order_release);
} }
} }
void processRumbleDoubleClick(u64 nowNs) { void processRumbleDoubleClick(u64 nowNs) {
if (!doubleClickActive.load(std::memory_order_acquire)) return; if (!doubleClickActive.load(std::memory_order_acquire)) return;
const u64 elapsed = nowNs - armTicksToNs(doubleClickTick); const u64 elapsed = nowNs - armTicksToNs(doubleClickTick); // Always from original start
switch (doubleClickPulse) { switch (doubleClickPulse) {
case 1: case 1:
if (elapsed >= DOUBLE_CLICK_PULSE_DURATION_NS) { if (elapsed >= DOUBLE_CLICK_PULSE_DURATION_NS) {
sendVibration(&vibrationStop); sendVibration(&vibrationStop);
doubleClickPulse = 2; doubleClickPulse = 2;
doubleClickTick = armGetSystemTick(); // Don't reset tick!
} }
break; break;
case 2: case 2:
if (elapsed >= DOUBLE_CLICK_GAP_NS) { if (elapsed >= DOUBLE_CLICK_PULSE_DURATION_NS + DOUBLE_CLICK_GAP_NS) {
sendVibration(hidGetNpadStyleSet(HidNpadIdType_Handheld) ? &clickHandheld : &clickDocked); // Use cached style here too
//if (cachedHandheldStyle) {
// sendVibration(&clickHandheld);
// sendVibration(&clickHandheld);
//} else {
// sendVibration(&clickDocked);
// sendVibration(&clickDocked);
//}
sendVibration2x(&hapticsPreset);
doubleClickPulse = 3; doubleClickPulse = 3;
doubleClickTick = armGetSystemTick(); // Don't reset tick!
} }
break; break;
case 3: case 3:
if (elapsed >= DOUBLE_CLICK_PULSE_DURATION_NS) { if (elapsed >= (DOUBLE_CLICK_PULSE_DURATION_NS * 2) + DOUBLE_CLICK_GAP_NS) {
sendVibration(&vibrationStop); sendVibration(&vibrationStop);
doubleClickActive.store(false, std::memory_order_release); doubleClickActive.store(false, std::memory_order_release);
doubleClickPulse = 0; doubleClickPulse = 0;
@@ -187,14 +228,32 @@ namespace ult {
} }
} }
void rumbleDoubleClickStandalone() { void rumbleDoubleClickStandalone() {
sendVibration(hidGetNpadStyleSet(HidNpadIdType_Handheld) ? &clickHandheld : &clickDocked); // Standalone uses sleeps, but still use cached style for decision
//const HidVibrationValue* pattern = cachedHandheldStyle ? &clickHandheld : &clickDocked;
sendVibration(&vibrationStop);
//if (cachedHandheldStyle) {
// sendVibration(&clickHandheld);
// sendVibration(&clickHandheld);
//} else {
// sendVibration(&clickDocked);
// sendVibration(&clickDocked);
//}
sendVibration2x(&hapticsPreset);
svcSleepThread(DOUBLE_CLICK_PULSE_DURATION_NS); svcSleepThread(DOUBLE_CLICK_PULSE_DURATION_NS);
sendVibration(&vibrationStop); sendVibration(&vibrationStop);
svcSleepThread(DOUBLE_CLICK_GAP_NS); svcSleepThread(DOUBLE_CLICK_GAP_NS);
sendVibration(hidGetNpadStyleSet(HidNpadIdType_Handheld) ? &clickHandheld : &clickDocked); //if (cachedHandheldStyle) {
// sendVibration(&clickHandheld);
// sendVibration(&clickHandheld);
//} else {
// sendVibration(&clickDocked);
// sendVibration(&clickDocked);
//}
sendVibration2x(&hapticsPreset);
svcSleepThread(DOUBLE_CLICK_PULSE_DURATION_NS); svcSleepThread(DOUBLE_CLICK_PULSE_DURATION_NS);
sendVibration(&vibrationStop); sendVibration(&vibrationStop);

View File

@@ -15,7 +15,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#include "hex_funcs.hpp" #include "hex_funcs.hpp"
@@ -229,15 +229,16 @@ namespace ult {
const size_t fileSize = ftell(file); const size_t fileSize = ftell(file);
fseek(file, 0, SEEK_SET); fseek(file, 0, SEEK_SET);
std::vector<unsigned char> binaryData;
if (hexData.length() % 2 != 0) { if (hexData.length() % 2 != 0) {
fclose(file); fclose(file);
return offsets; return offsets;
} }
const size_t hexLen = hexData.length(); const size_t hexLen = hexData.length();
binaryData.resize(hexLen / 2); const size_t patternLen = hexLen / 2;
// Use heap allocation for the buffer to avoid stack overflow with large buffer sizes
std::unique_ptr<unsigned char[]> binaryData(new unsigned char[patternLen]);
const unsigned char* hexPtr = reinterpret_cast<const unsigned char*>(hexData.c_str()); const unsigned char* hexPtr = reinterpret_cast<const unsigned char*>(hexData.c_str());
// Unrolled hex conversion loop // Unrolled hex conversion loop
@@ -252,17 +253,17 @@ namespace ult {
} }
// Optimized search variables // Optimized search variables
const unsigned char* patternPtr = binaryData.data(); const unsigned char* patternPtr = binaryData.get();
const size_t patternLen = binaryData.size();
const unsigned char firstByte = patternPtr[0]; const unsigned char firstByte = patternPtr[0];
std::vector<unsigned char> buffer(HEX_BUFFER_SIZE); // Use heap allocation for the buffer to avoid stack overflow with large buffer sizes
std::unique_ptr<unsigned char[]> buffer(new unsigned char[HEX_BUFFER_SIZE]);
size_t bytesRead = 0; size_t bytesRead = 0;
size_t offset = 0; size_t offset = 0;
while ((bytesRead = fread(buffer.data(), 1, HEX_BUFFER_SIZE, file)) > 0) { while ((bytesRead = fread(buffer.get(), 1, HEX_BUFFER_SIZE, file)) > 0) {
const unsigned char* bufPtr = buffer.data(); const unsigned char* bufPtr = buffer.get();
// Optimized search with first-byte filtering and loop unrolling // Optimized search with first-byte filtering and loop unrolling
i = 0; i = 0;
@@ -322,15 +323,16 @@ namespace ult {
const size_t fileSize = file.tellg(); const size_t fileSize = file.tellg();
file.seekg(0, std::ios::beg); file.seekg(0, std::ios::beg);
std::vector<unsigned char> binaryData;
if (hexData.length() % 2 != 0) { if (hexData.length() % 2 != 0) {
file.close(); file.close();
return offsets; return offsets;
} }
const size_t hexLen = hexData.length(); const size_t hexLen = hexData.length();
binaryData.resize(hexLen / 2); const size_t patternLen = hexLen / 2;
// Use heap allocation for the buffer to avoid stack overflow with large buffer sizes
std::unique_ptr<unsigned char[]> binaryData(new unsigned char[patternLen]);
const unsigned char* hexPtr = reinterpret_cast<const unsigned char*>(hexData.c_str()); const unsigned char* hexPtr = reinterpret_cast<const unsigned char*>(hexData.c_str());
size_t i = 0; size_t i = 0;
@@ -342,17 +344,17 @@ namespace ult {
binaryData[i/2] = (hexTable[hexPtr[i]] << 4) | hexTable[hexPtr[i + 1]]; binaryData[i/2] = (hexTable[hexPtr[i]] << 4) | hexTable[hexPtr[i + 1]];
} }
const unsigned char* patternPtr = binaryData.data(); const unsigned char* patternPtr = binaryData.get();
const size_t patternLen = binaryData.size();
const unsigned char firstByte = patternPtr[0]; const unsigned char firstByte = patternPtr[0];
std::vector<unsigned char> buffer(HEX_BUFFER_SIZE); // Use heap allocation for the buffer to avoid stack overflow with large buffer sizes
std::unique_ptr<unsigned char[]> buffer(new unsigned char[HEX_BUFFER_SIZE]);
size_t bytesRead = 0; size_t bytesRead = 0;
size_t offset = 0; size_t offset = 0;
while (file.read(reinterpret_cast<char*>(buffer.data()), HEX_BUFFER_SIZE) || file.gcount() > 0) { while (file.read(reinterpret_cast<char*>(buffer.get()), HEX_BUFFER_SIZE) || file.gcount() > 0) {
bytesRead = file.gcount(); bytesRead = file.gcount();
const unsigned char* bufPtr = buffer.data(); const unsigned char* bufPtr = buffer.get();
// Same optimized search as FILE* version // Same optimized search as FILE* version
i = 0; i = 0;
@@ -418,7 +420,6 @@ namespace ult {
void hexEditByOffset(const std::string& filePath, const std::string& offsetStr, const std::string& hexData) { void hexEditByOffset(const std::string& filePath, const std::string& offsetStr, const std::string& hexData) {
// Lock file writes to prevent concurrent modifications to the same file // Lock file writes to prevent concurrent modifications to the same file
std::lock_guard<std::mutex> fileWriteLock(fileWriteMutex); std::lock_guard<std::mutex> fileWriteLock(fileWriteMutex);
const std::streampos offset = std::stoll(offsetStr); const std::streampos offset = std::stoll(offsetStr);
#if !USING_FSTREAM_DIRECTIVE #if !USING_FSTREAM_DIRECTIVE
@@ -435,7 +436,6 @@ namespace ult {
// Retrieve the file size // Retrieve the file size
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
const std::streampos fileSize = ftell(file); const std::streampos fileSize = ftell(file);
fseek(file, 0, SEEK_SET);
if (offset >= fileSize) { if (offset >= fileSize) {
#if USING_LOGGING_DIRECTIVE #if USING_LOGGING_DIRECTIVE
@@ -446,18 +446,37 @@ namespace ult {
return; return;
} }
// Convert the hex string to binary data // Validate hex data length
std::vector<unsigned char> binaryData(hexData.length() / 2); const size_t hexLen = hexData.length();
std::string byteString; if (hexLen % 2 != 0) {
for (size_t i = 0, j = 0; i < hexData.length(); i += 2, ++j) { #if USING_LOGGING_DIRECTIVE
byteString = hexData.substr(i, 2); if (!disableLogging)
binaryData[j] = static_cast<unsigned char>(ult::stoi(byteString, nullptr, 16)); logMessage("Invalid hex data length.");
#endif
fclose(file);
return;
}
// Convert the hex string to binary data using optimized lookup table
const size_t dataLen = hexLen / 2;
std::unique_ptr<unsigned char[]> binaryData(new unsigned char[dataLen]);
const unsigned char* hexPtr = reinterpret_cast<const unsigned char*>(hexData.c_str());
// Unrolled hex conversion loop (same as findHexDataOffsets)
size_t i = 0;
for (; i + 4 <= hexLen; i += 4) {
binaryData[i/2] = (hexTable[hexPtr[i]] << 4) | hexTable[hexPtr[i + 1]];
binaryData[i/2 + 1] = (hexTable[hexPtr[i + 2]] << 4) | hexTable[hexPtr[i + 3]];
}
// Handle remaining bytes
for (; i < hexLen; i += 2) {
binaryData[i/2] = (hexTable[hexPtr[i]] << 4) | hexTable[hexPtr[i + 1]];
} }
// Move to the specified offset and write the binary data directly to the file // Move to the specified offset and write the binary data directly to the file
fseek(file, offset, SEEK_SET); fseek(file, offset, SEEK_SET);
const size_t bytesWritten = fwrite(binaryData.data(), sizeof(unsigned char), binaryData.size(), file); const size_t bytesWritten = fwrite(binaryData.get(), sizeof(unsigned char), dataLen, file);
if (bytesWritten != binaryData.size()) { if (bytesWritten != dataLen) {
#if USING_LOGGING_DIRECTIVE #if USING_LOGGING_DIRECTIVE
if (!disableLogging) if (!disableLogging)
logMessage("Failed to write data to the file."); logMessage("Failed to write data to the file.");
@@ -481,7 +500,6 @@ namespace ult {
// Retrieve the file size // Retrieve the file size
file.seekg(0, std::ios::end); file.seekg(0, std::ios::end);
const std::streampos fileSize = file.tellg(); const std::streampos fileSize = file.tellg();
file.seekg(0, std::ios::beg);
if (offset >= fileSize) { if (offset >= fileSize) {
#if USING_LOGGING_DIRECTIVE #if USING_LOGGING_DIRECTIVE
@@ -491,17 +509,34 @@ namespace ult {
return; return;
} }
// Convert the hex string to binary data // Validate hex data length
std::vector<unsigned char> binaryData(hexData.length() / 2); const size_t hexLen = hexData.length();
std::string byteString; if (hexLen % 2 != 0) {
for (size_t i = 0, j = 0; i < hexData.length(); i += 2, ++j) { #if USING_LOGGING_DIRECTIVE
byteString = hexData.substr(i, 2); if (!disableLogging)
binaryData[j] = static_cast<unsigned char>(ult::stoi(byteString, nullptr, 16)); logMessage("Invalid hex data length.");
#endif
return;
}
// Convert the hex string to binary data using optimized lookup table
const size_t dataLen = hexLen / 2;
std::unique_ptr<unsigned char[]> binaryData(new unsigned char[dataLen]);
const unsigned char* hexPtr = reinterpret_cast<const unsigned char*>(hexData.c_str());
// Unrolled hex conversion loop
size_t i = 0;
for (; i + 4 <= hexLen; i += 4) {
binaryData[i/2] = (hexTable[hexPtr[i]] << 4) | hexTable[hexPtr[i + 1]];
binaryData[i/2 + 1] = (hexTable[hexPtr[i + 2]] << 4) | hexTable[hexPtr[i + 3]];
}
for (; i < hexLen; i += 2) {
binaryData[i/2] = (hexTable[hexPtr[i]] << 4) | hexTable[hexPtr[i + 1]];
} }
// Move to the specified offset and write the binary data directly to the file // Move to the specified offset and write the binary data directly to the file
file.seekp(offset); file.seekp(offset);
file.write(reinterpret_cast<const char*>(binaryData.data()), binaryData.size()); file.write(reinterpret_cast<const char*>(binaryData.get()), dataLen);
if (!file) { if (!file) {
#if USING_LOGGING_DIRECTIVE #if USING_LOGGING_DIRECTIVE
if (!disableLogging) if (!disableLogging)
@@ -676,8 +711,10 @@ namespace ult {
} }
const std::streampos totalOffset = hexSum + std::stoll(offsetStr); const std::streampos totalOffset = hexSum + std::stoll(offsetStr);
std::vector<char> hexBuffer(length);
std::vector<char> hexStream(length * 2); // Pre-allocate final string size to avoid reallocation
std::string result;
result.reserve(length * 2);
#if !USING_FSTREAM_DIRECTIVE #if !USING_FSTREAM_DIRECTIVE
FILE* file = fopen(filePath.c_str(), "rb"); FILE* file = fopen(filePath.c_str(), "rb");
@@ -698,23 +735,41 @@ namespace ult {
return ""; return "";
} }
const size_t bytesRead = fread(hexBuffer.data(), sizeof(char), length, file); // Use heap allocation for the buffer to avoid stack overflow with large buffer sizes
if (bytesRead == length) { std::unique_ptr<unsigned char[]> buffer(new unsigned char[length]);
static constexpr char hexDigits[] = "0123456789ABCDEF"; const size_t bytesRead = fread(buffer.get(), 1, length, file);
for (size_t i = 0; i < length; ++i) { fclose(file);
hexStream[i * 2] = hexDigits[(hexBuffer[i] >> 4) & 0xF];
hexStream[i * 2 + 1] = hexDigits[hexBuffer[i] & 0xF]; if (bytesRead != length) {
}
} else {
#if USING_LOGGING_DIRECTIVE #if USING_LOGGING_DIRECTIVE
if (!disableLogging) if (!disableLogging)
logMessage("Error reading data from file or end of file reached."); logMessage("Error reading data from file or end of file reached.");
#endif #endif
fclose(file);
return ""; return "";
} }
fclose(file); // Optimized hex conversion - directly build uppercase string
static constexpr char hexDigits[] = "0123456789ABCDEF";
result.resize(length * 2);
// Unrolled loop for better performance
size_t i = 0;
for (; i + 4 <= length; i += 4) {
result[i * 2] = hexDigits[(buffer[i] >> 4) & 0xF];
result[i * 2 + 1] = hexDigits[buffer[i] & 0xF];
result[i * 2 + 2] = hexDigits[(buffer[i + 1] >> 4) & 0xF];
result[i * 2 + 3] = hexDigits[buffer[i + 1] & 0xF];
result[i * 2 + 4] = hexDigits[(buffer[i + 2] >> 4) & 0xF];
result[i * 2 + 5] = hexDigits[buffer[i + 2] & 0xF];
result[i * 2 + 6] = hexDigits[(buffer[i + 3] >> 4) & 0xF];
result[i * 2 + 7] = hexDigits[buffer[i + 3] & 0xF];
}
// Handle remaining bytes
for (; i < length; ++i) {
result[i * 2] = hexDigits[(buffer[i] >> 4) & 0xF];
result[i * 2 + 1] = hexDigits[buffer[i] & 0xF];
}
#else #else
std::ifstream file(filePath, std::ios::binary); std::ifstream file(filePath, std::ios::binary);
if (!file) { if (!file) {
@@ -734,14 +789,12 @@ namespace ult {
return ""; return "";
} }
file.read(hexBuffer.data(), length); // Use heap allocation for the buffer to avoid stack overflow with large buffer sizes
if (file.gcount() == static_cast<std::streamsize>(length)) { std::unique_ptr<unsigned char[]> buffer(new unsigned char[length]);
static constexpr char hexDigits[] = "0123456789ABCDEF"; file.read(reinterpret_cast<char*>(buffer.get()), length);
for (size_t i = 0; i < length; ++i) { file.close();
hexStream[i * 2] = hexDigits[(hexBuffer[i] >> 4) & 0xF];
hexStream[i * 2 + 1] = hexDigits[hexBuffer[i] & 0xF]; if (file.gcount() != static_cast<std::streamsize>(length)) {
}
} else {
#if USING_LOGGING_DIRECTIVE #if USING_LOGGING_DIRECTIVE
if (!disableLogging) if (!disableLogging)
logMessage("Error reading data from file or end of file reached."); logMessage("Error reading data from file or end of file reached.");
@@ -749,11 +802,28 @@ namespace ult {
return ""; return "";
} }
file.close(); // Optimized hex conversion - directly build uppercase string
#endif static constexpr char hexDigits[] = "0123456789ABCDEF";
result.resize(length * 2);
std::string result(hexStream.begin(), hexStream.end()); // Unrolled loop for better performance
result = stringToUppercase(result); size_t i = 0;
for (; i + 4 <= length; i += 4) {
result[i * 2] = hexDigits[(buffer[i] >> 4) & 0xF];
result[i * 2 + 1] = hexDigits[buffer[i] & 0xF];
result[i * 2 + 2] = hexDigits[(buffer[i + 1] >> 4) & 0xF];
result[i * 2 + 3] = hexDigits[buffer[i + 1] & 0xF];
result[i * 2 + 4] = hexDigits[(buffer[i + 2] >> 4) & 0xF];
result[i * 2 + 5] = hexDigits[buffer[i + 2] & 0xF];
result[i * 2 + 6] = hexDigits[(buffer[i + 3] >> 4) & 0xF];
result[i * 2 + 7] = hexDigits[buffer[i + 3] & 0xF];
}
// Handle remaining bytes
for (; i < length; ++i) {
result[i * 2] = hexDigits[(buffer[i] >> 4) & 0xF];
result[i * 2 + 1] = hexDigits[buffer[i] & 0xF];
}
#endif
return result; return result;
} }
@@ -878,4 +948,71 @@ namespace ult {
return ""; // Return empty string if no match is found return ""; // Return empty string if no match is found
} }
// 1. Table optimization: Mark as constexpr for compile-time evaluation
static constexpr uint8_t b64_table[256] = {
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,62, 0xFF,0xFF,0xFF,63,
52,53,54,55,56,57,58,59,60,61,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
};
// 2. Optimized decode: Pre-calculate output size, reduce bounds checking
static size_t base64_decode(const char* src, uint8_t* out) {
size_t outLen = 0;
const char* p = src;
// Process 4 chars at a time (unrolled loop for better instruction pipelining)
while (*p) {
uint8_t a = b64_table[static_cast<uint8_t>(*p++)];
if (a == 0xFF) break;
uint8_t b = b64_table[static_cast<uint8_t>(*p++)];
if (b == 0xFF) break;
out[outLen++] = (a << 2) | (b >> 4);
uint8_t cChar = *p++;
if (cChar == '=' || cChar == '\0') break;
uint8_t c = b64_table[cChar];
if (c == 0xFF) break;
out[outLen++] = (b << 4) | (c >> 2);
uint8_t dChar = *p++;
if (dChar == '=' || dChar == '\0') break;
uint8_t d = b64_table[dChar];
if (d == 0xFF) break;
out[outLen++] = (c << 6) | d;
}
return outLen;
}
// 3. Optimized wrapper: Pre-calculate exact output size, avoid vector overhead
std::string decodeBase64ToString(const std::string& b64) {
// Base64 decodes to ~3/4 original size
const size_t maxOutSize = (b64.size() * 3) / 4 + 3;
std::string result(maxOutSize, '\0');
const size_t len = base64_decode(b64.c_str(), reinterpret_cast<uint8_t*>(result.data()));
result.resize(len);
return result;
}
} }

View File

@@ -12,7 +12,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#include "json_funcs.hpp" #include "json_funcs.hpp"

View File

@@ -14,7 +14,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#include <list_funcs.hpp> #include <list_funcs.hpp>
@@ -85,8 +85,8 @@ namespace ult {
} }
// Function to read file into a vector of strings with optional cap // Function to read file into a vector of strings with optional cap and newline preservation
std::vector<std::string> readListFromFile(const std::string& filePath, size_t maxLines) { std::vector<std::string> readListFromFile(const std::string& filePath, size_t maxLines, bool preserveNewlines) {
std::lock_guard<std::mutex> lock(file_access_mutex); std::lock_guard<std::mutex> lock(file_access_mutex);
std::vector<std::string> lines; std::vector<std::string> lines;
@@ -109,7 +109,11 @@ namespace ult {
break; break;
} }
// More efficient newline removal if (preserveNewlines) {
// Keep the line as-is, including newlines
lines.emplace_back(buffer);
} else {
// Remove newlines
len = strlen(buffer); len = strlen(buffer);
if (len > 0 && buffer[len - 1] == '\n') { if (len > 0 && buffer[len - 1] == '\n') {
buffer[len - 1] = '\0'; buffer[len - 1] = '\0';
@@ -119,9 +123,9 @@ namespace ult {
buffer[len - 1] = '\0'; buffer[len - 1] = '\0';
} }
} }
lines.emplace_back(buffer); lines.emplace_back(buffer);
} }
}
fclose(file); fclose(file);
#else #else
@@ -140,13 +144,18 @@ namespace ult {
break; break;
} }
if (preserveNewlines) {
// Add back the newline that getline removed
line += '\n';
lines.emplace_back(std::move(line));
} else {
// Remove carriage return if present (getline removes \n but not \r) // Remove carriage return if present (getline removes \n but not \r)
if (!line.empty() && line.back() == '\r') { if (!line.empty() && line.back() == '\r') {
line.pop_back(); line.pop_back();
} }
lines.emplace_back(std::move(line)); lines.emplace_back(std::move(line));
} }
}
file.close(); file.close();
#endif #endif
@@ -282,7 +291,7 @@ namespace ult {
// Function to read file into a set of strings // Function to read file into a set of strings
std::unordered_set<std::string> readSetFromFile(const std::string& filePath) { std::unordered_set<std::string> readSetFromFile(const std::string& filePath, const std::string& packagePath) {
std::lock_guard<std::mutex> lock(file_access_mutex); std::lock_guard<std::mutex> lock(file_access_mutex);
std::unordered_set<std::string> lines; std::unordered_set<std::string> lines;
@@ -304,7 +313,12 @@ namespace ult {
if (len > 0 && buffer[len - 1] == '\n') { if (len > 0 && buffer[len - 1] == '\n') {
buffer[len - 1] = '\0'; buffer[len - 1] = '\0';
} }
lines.insert(buffer);
std::string line = buffer;
if (!packagePath.empty()) {
preprocessPath(line, packagePath);
}
lines.insert(std::move(line));
} }
fclose(file); fclose(file);
@@ -319,6 +333,9 @@ namespace ult {
std::string line; std::string line;
while (std::getline(file, line)) { while (std::getline(file, line)) {
if (!packagePath.empty()) {
preprocessPath(line, packagePath);
}
lines.insert(std::move(line)); lines.insert(std::move(line));
} }

View File

@@ -14,7 +14,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2024-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#include <mod_funcs.hpp> #include <mod_funcs.hpp>

View File

@@ -15,7 +15,7 @@
* of the project's documentation and must remain intact. * of the project's documentation and must remain intact.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#include "string_funcs.hpp" #include "string_funcs.hpp"
@@ -226,12 +226,14 @@ namespace ult {
* @return The string with quotes removed. * @return The string with quotes removed.
*/ */
void removeQuotes(std::string& str) { void removeQuotes(std::string& str) {
if (str.size() >= 2) { const size_t len = str.size();
if (len >= 2) {
const char front = str[0]; const char front = str[0];
const char back = str[str.size() - 1]; const char back = str[len - 1];
if ((front == '\'' && back == '\'') || (front == '"' && back == '"')) { if ((front == '\'' && back == '\'') || (front == '"' && back == '"')) {
str.erase(0, 1); std::memmove(&str[0], &str[1], len - 2);
str.pop_back(); str.resize(len - 2);
} }
} }
} }
@@ -245,25 +247,25 @@ namespace ult {
* @param input The input string to process. * @param input The input string to process.
* @return The string with multiple slashes replaced. * @return The string with multiple slashes replaced.
*/ */
std::string replaceMultipleSlashes(const std::string& input) { //std::string replaceMultipleSlashes(const std::string& input) {
std::string output; // std::string output;
output.reserve(input.size()); // Reserve space for the output string // output.reserve(input.size()); // Reserve space for the output string
//
bool previousSlash = false; // bool previousSlash = false;
for (char c : input) { // for (char c : input) {
if (c == '/') { // if (c == '/') {
if (!previousSlash) { // if (!previousSlash) {
output.push_back(c); // output.push_back(c);
} // }
previousSlash = true; // previousSlash = true;
} else { // } else {
output.push_back(c); // output.push_back(c);
previousSlash = false; // previousSlash = false;
} // }
} // }
//
return output; // return output;
} //}
@@ -366,7 +368,7 @@ namespace ult {
* @param filename The input filename from which to drop the extension, passed by reference and modified in-place. * @param filename The input filename from which to drop the extension, passed by reference and modified in-place.
*/ */
void dropExtension(std::string& filename) { void dropExtension(std::string& filename) {
const size_t lastDotPos = filename.find_last_of('.'); // Single char instead of string const size_t lastDotPos = filename.rfind('.');
if (lastDotPos != std::string::npos) { if (lastDotPos != std::string::npos) {
filename.resize(lastDotPos); filename.resize(lastDotPos);
} }
@@ -510,9 +512,12 @@ namespace ult {
* @param input The input string from which to remove the tag, passed by reference and modified in-place. * @param input The input string from which to remove the tag, passed by reference and modified in-place.
*/ */
void removeTag(std::string &input) { void removeTag(std::string &input) {
const size_t pos = input.find('?'); const char* pos = static_cast<const char*>(
if (pos != std::string::npos) { std::memchr(input.data(), '?', input.size())
input.resize(pos); // Modify the string in-place to remove everything after the '?' );
if (pos) {
input.resize(pos - input.data());
} }
} }

View File

@@ -15,7 +15,7 @@
* altered or removed. * altered or removed.
* *
* Licensed under both GPLv2 and CC-BY-4.0 * Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2024 ppkantorski * Copyright (c) 2023-2025 ppkantorski
********************************************************************************/ ********************************************************************************/
#include <tsl_utils.hpp> #include <tsl_utils.hpp>
@@ -28,6 +28,7 @@ extern "C" { // assertion override
} }
namespace ult { namespace ult {
bool correctFrameSize; // for detecting the correct Overlay display size bool correctFrameSize; // for detecting the correct Overlay display size
u16 DefaultFramebufferWidth = 448; ///< Width of the framebuffer u16 DefaultFramebufferWidth = 448; ///< Width of the framebuffer
@@ -100,7 +101,7 @@ namespace ult {
value = content.substr(valueStart + 1, valueEnd - valueStart - 1); value = content.substr(valueStart + 1, valueEnd - valueStart - 1);
// 🔹 Convert escaped newlines (\\n) into real ones // Convert escaped newlines (\\n) into real ones
normalizeNewlines(key); normalizeNewlines(key);
normalizeNewlines(value); normalizeNewlines(value);
@@ -132,29 +133,14 @@ namespace ult {
u16 activeHeaderHeight = 97; u16 activeHeaderHeight = 97;
bool consoleIsDocked() { bool consoleIsDocked() {
Result rc; Result rc = apmInitialize();
if (R_FAILED(rc)) return false;
ApmPerformanceMode perfMode = ApmPerformanceMode_Invalid; ApmPerformanceMode perfMode = ApmPerformanceMode_Invalid;
// Initialize the APM service
rc = apmInitialize();
if (R_FAILED(rc)) {
return false; // Fail early if initialization fails
}
// Get the current performance mode
rc = apmGetPerformanceMode(&perfMode); rc = apmGetPerformanceMode(&perfMode);
apmExit(); // Clean up the APM service apmExit();
if (R_FAILED(rc)) { return R_SUCCEEDED(rc) && (perfMode == ApmPerformanceMode_Boost);
return false; // Fail early if performance mode check fails
}
// Check if the performance mode indicates docked state
if (perfMode == ApmPerformanceMode_Boost) {
return true; // System is docked (boost mode active)
}
return false; // Not docked (normal mode or handheld)
} }
//static bool pminfoInitialized = false; //static bool pminfoInitialized = false;
@@ -314,6 +300,9 @@ namespace ult {
std::atomic<float> selectWidth; std::atomic<float> selectWidth;
std::atomic<float> nextPageWidth; std::atomic<float> nextPageWidth;
std::atomic<bool> inMainMenu{false}; std::atomic<bool> inMainMenu{false};
std::atomic<bool> inHiddenMode{false};
std::atomic<bool> inSettingsMenu{false};
std::atomic<bool> inSubSettingsMenu{false};
std::atomic<bool> inOverlaysPage{false}; std::atomic<bool> inOverlaysPage{false};
std::atomic<bool> inPackagesPage{false}; std::atomic<bool> inPackagesPage{false};
@@ -332,7 +321,7 @@ namespace ult {
//bool progressAnimation = false; //bool progressAnimation = false;
bool disableTransparency = false; bool disableTransparency = false;
//bool useCustomWallpaper = false; //bool useCustomWallpaper = false;
bool useMemoryExpansion = false; //bool useMemoryExpansion = false;
bool useOpaqueScreenshots = false; bool useOpaqueScreenshots = false;
std::atomic<bool> onTrackBar(false); std::atomic<bool> onTrackBar(false);
@@ -468,6 +457,7 @@ namespace ult {
std::string HIDE_OVERLAY = "Hide Overlay"; std::string HIDE_OVERLAY = "Hide Overlay";
std::string HIDE_PACKAGE = "Hide Package"; std::string HIDE_PACKAGE = "Hide Package";
std::string LAUNCH_ARGUMENTS = "Launch Arguments"; std::string LAUNCH_ARGUMENTS = "Launch Arguments";
std::string FORCE_AMS110_SUPPORT = "Force AMS110+ Support";
std::string QUICK_LAUNCH = "Quick Launch"; std::string QUICK_LAUNCH = "Quick Launch";
std::string BOOT_COMMANDS = "Boot Commands"; std::string BOOT_COMMANDS = "Boot Commands";
std::string EXIT_COMMANDS = "Exit Commands"; std::string EXIT_COMMANDS = "Exit Commands";
@@ -495,6 +485,7 @@ namespace ult {
std::string USER_GUIDE = "User Guide"; std::string USER_GUIDE = "User Guide";
std::string SHOW_HIDDEN = "Show Hidden"; std::string SHOW_HIDDEN = "Show Hidden";
std::string SHOW_DELETE = "Show Delete"; std::string SHOW_DELETE = "Show Delete";
std::string SHOW_UNSUPPORTED = "Show Unsupported";
std::string PAGE_SWAP = "Page Swap"; std::string PAGE_SWAP = "Page Swap";
std::string RIGHT_SIDE_MODE = "Right-side Mode"; std::string RIGHT_SIDE_MODE = "Right-side Mode";
std::string OVERLAY_VERSIONS = "Overlay Versions"; std::string OVERLAY_VERSIONS = "Overlay Versions";
@@ -503,7 +494,7 @@ namespace ult {
//std::string VERSION_LABELS = "Version Labels"; //std::string VERSION_LABELS = "Version Labels";
std::string KEY_COMBO = "Key Combo"; std::string KEY_COMBO = "Key Combo";
std::string MODE = "Mode"; std::string MODE = "Mode";
std::string MODES = "Modes"; std::string LAUNCH_MODES = "Launch Modes";
std::string LANGUAGE = "Language"; std::string LANGUAGE = "Language";
std::string OVERLAY_INFO = "Overlay Info"; std::string OVERLAY_INFO = "Overlay Info";
std::string SOFTWARE_UPDATE = "Software Update"; std::string SOFTWARE_UPDATE = "Software Update";
@@ -518,11 +509,20 @@ namespace ult {
std::string VENDOR = "Vendor"; std::string VENDOR = "Vendor";
std::string MODEL = "Model"; std::string MODEL = "Model";
std::string STORAGE = "Storage"; std::string STORAGE = "Storage";
std::string NOTICE = "Notice"; //std::string NOTICE = "Notice";
std::string UTILIZES = "Utilizes"; //std::string UTILIZES = "Utilizes";
std::string MEMORY_EXPANSION = "Memory Expansion";
std::string REBOOT_REQUIRED = "*Reboot required."; std::string OVERLAY_MEMORY = "Overlay Memory";
std::string NOT_ENOUGH_MEMORY = "Not enough memory.";
std::string WALLPAPER_SUPPORT_DISABLED = "Wallpaper support disabled.";
std::string SOUND_SUPPORT_DISABLED = "Sound support disabled.";
std::string WALLPAPER_SUPPORT_ENABLED = "Wallpaper support enabled.";
std::string SOUND_SUPPORT_ENABLED = "Sound support enabled.";
std::string EXIT_OVERLAY_SYSTEM = "Exit Overlay System";
//std::string MEMORY_EXPANSION = "Memory Expansion";
//std::string REBOOT_REQUIRED = "*Reboot required.";
std::string LOCAL_IP = "Local IP"; std::string LOCAL_IP = "Local IP";
std::string WALLPAPER = "Wallpaper"; std::string WALLPAPER = "Wallpaper";
std::string THEME = "Theme"; std::string THEME = "Theme";
@@ -566,10 +566,15 @@ namespace ult {
std::string ULTRAHAND_HAS_STARTED = "Ultrahand has started."; std::string ULTRAHAND_HAS_STARTED = "Ultrahand has started.";
std::string NEW_UPDATE_IS_AVAILABLE = "New update is available!"; std::string NEW_UPDATE_IS_AVAILABLE = "New update is available!";
std::string REBOOT_IS_REQUIRED = "Reboot is required."; //std::string REBOOT_IS_REQUIRED = "Reboot is required.";
std::string HOLD_A_TO_DELETE = "Hold \uE0E0 to Delete"; //std::string HOLD_A_TO_DELETE = "Hold \uE0E0 to Delete";
std::string DELETE_PACKAGE = "Delete Package";
std::string DELETE_OVERLAY = "Delete Overlay";
std::string SELECTION_IS_EMPTY = "Selection is empty!"; std::string SELECTION_IS_EMPTY = "Selection is empty!";
std::string FORCED_SUPPORT_WARNING = "Forcing support can be dangerous.";
std::string TASK_IS_COMPLETE = "Task is complete!";
std::string TASK_HAS_FAILED = "Task has failed.";
//std::string PACKAGE_VERSIONS = "Package Versions"; //std::string PACKAGE_VERSIONS = "Package Versions";
@@ -581,6 +586,8 @@ namespace ult {
std::string BOOT_ENTRY = "Boot Entry"; std::string BOOT_ENTRY = "Boot Entry";
#endif #endif
std::string INCOMPATIBLE_WARNING = "Incompatible on AMS v1.10+";
std::string SYSTEM_RAM = "System RAM";
std::string FREE = "free"; std::string FREE = "free";
std::string DEFAULT_CHAR_WIDTH = "0.33"; std::string DEFAULT_CHAR_WIDTH = "0.33";
@@ -678,6 +685,7 @@ namespace ult {
HIDE_OVERLAY = "Hide Overlay"; HIDE_OVERLAY = "Hide Overlay";
HIDE_PACKAGE = "Hide Package"; HIDE_PACKAGE = "Hide Package";
LAUNCH_ARGUMENTS = "Launch Arguments"; LAUNCH_ARGUMENTS = "Launch Arguments";
FORCE_AMS110_SUPPORT = "Force AMS110+ Support";
QUICK_LAUNCH = "Quick Launch"; QUICK_LAUNCH = "Quick Launch";
BOOT_COMMANDS = "Boot Commands"; BOOT_COMMANDS = "Boot Commands";
EXIT_COMMANDS = "Exit Commands"; EXIT_COMMANDS = "Exit Commands";
@@ -704,6 +712,7 @@ namespace ult {
USER_GUIDE = "User Guide"; USER_GUIDE = "User Guide";
SHOW_HIDDEN = "Show Hidden"; SHOW_HIDDEN = "Show Hidden";
SHOW_DELETE = "Show Delete"; SHOW_DELETE = "Show Delete";
SHOW_UNSUPPORTED = "Show Unsupported";
PAGE_SWAP = "Page Swap"; PAGE_SWAP = "Page Swap";
RIGHT_SIDE_MODE = "Right-side Mode"; RIGHT_SIDE_MODE = "Right-side Mode";
OVERLAY_VERSIONS = "Overlay Versions"; OVERLAY_VERSIONS = "Overlay Versions";
@@ -712,7 +721,7 @@ namespace ult {
//VERSION_LABELS = "Version Labels"; //VERSION_LABELS = "Version Labels";
KEY_COMBO = "Key Combo"; KEY_COMBO = "Key Combo";
MODE = "Mode"; MODE = "Mode";
MODES = "Modes"; LAUNCH_MODES = "Launch Modes";
LANGUAGE = "Language"; LANGUAGE = "Language";
OVERLAY_INFO = "Overlay Info"; OVERLAY_INFO = "Overlay Info";
SOFTWARE_UPDATE = "Software Update"; SOFTWARE_UPDATE = "Software Update";
@@ -727,11 +736,21 @@ namespace ult {
VENDOR = "Vendor"; VENDOR = "Vendor";
MODEL = "Model"; MODEL = "Model";
STORAGE = "Storage"; STORAGE = "Storage";
NOTICE = "Notice"; //NOTICE = "Notice";
UTILIZES = "Utilizes"; //UTILIZES = "Utilizes";
SYSTEM_RAM = "System RAM";
FREE = "free"; FREE = "free";
MEMORY_EXPANSION = "Memory Expansion";
REBOOT_REQUIRED = "*Reboot required."; OVERLAY_MEMORY = "Overlay Memory";
NOT_ENOUGH_MEMORY = "Not enough memory.";
WALLPAPER_SUPPORT_DISABLED = "Wallpaper support disabled.";
SOUND_SUPPORT_DISABLED = "Sound support disabled.";
WALLPAPER_SUPPORT_ENABLED = "Wallpaper support enabled.";
SOUND_SUPPORT_ENABLED = "Sound support enabled.";
EXIT_OVERLAY_SYSTEM = "Exit Overlay System";
//MEMORY_EXPANSION = "Memory Expansion";
//REBOOT_REQUIRED = "*Reboot required.";
LOCAL_IP = "Local IP"; LOCAL_IP = "Local IP";
WALLPAPER = "Wallpaper"; WALLPAPER = "Wallpaper";
THEME = "Theme"; THEME = "Theme";
@@ -790,9 +809,15 @@ namespace ult {
ULTRAHAND_HAS_STARTED = "Ultrahand has started."; ULTRAHAND_HAS_STARTED = "Ultrahand has started.";
NEW_UPDATE_IS_AVAILABLE = "New update is available!"; NEW_UPDATE_IS_AVAILABLE = "New update is available!";
REBOOT_IS_REQUIRED = "Reboot is required."; //REBOOT_IS_REQUIRED = "Reboot is required.";
HOLD_A_TO_DELETE = "Hold  to Delete"; //HOLD_A_TO_DELETE = "Hold  to Delete";
DELETE_PACKAGE = "Delete Package";
DELETE_OVERLAY = "Delete Overlay";
SELECTION_IS_EMPTY = "Selection is empty!"; SELECTION_IS_EMPTY = "Selection is empty!";
FORCED_SUPPORT_WARNING = "Forcing support can be dangerous.";
INCOMPATIBLE_WARNING = "Incompatible on AMS v1.10+";
TASK_IS_COMPLETE = "Task is complete!";
TASK_HAS_FAILED = "Task has failed.";
//EMPTY = "Empty"; //EMPTY = "Empty";
@@ -860,7 +885,7 @@ namespace ult {
} }
static std::unordered_map<std::string, std::string*> configMap = { std::unordered_map<std::string, std::string*> configMap = {
#if IS_LAUNCHER_DIRECTIVE #if IS_LAUNCHER_DIRECTIVE
{"ENGLISH", &ENGLISH}, {"ENGLISH", &ENGLISH},
{"SPANISH", &SPANISH}, {"SPANISH", &SPANISH},
@@ -887,6 +912,7 @@ namespace ult {
{"HIDE_PACKAGE", &HIDE_PACKAGE}, {"HIDE_PACKAGE", &HIDE_PACKAGE},
{"HIDE_OVERLAY", &HIDE_OVERLAY}, {"HIDE_OVERLAY", &HIDE_OVERLAY},
{"LAUNCH_ARGUMENTS", &LAUNCH_ARGUMENTS}, {"LAUNCH_ARGUMENTS", &LAUNCH_ARGUMENTS},
{"FORCE_AMS110_SUPPORT", &FORCE_AMS110_SUPPORT},
{"QUICK_LAUNCH", &QUICK_LAUNCH}, {"QUICK_LAUNCH", &QUICK_LAUNCH},
{"BOOT_COMMANDS", &BOOT_COMMANDS}, {"BOOT_COMMANDS", &BOOT_COMMANDS},
{"EXIT_COMMANDS", &EXIT_COMMANDS}, {"EXIT_COMMANDS", &EXIT_COMMANDS},
@@ -914,6 +940,7 @@ namespace ult {
{"USER_GUIDE", &USER_GUIDE}, {"USER_GUIDE", &USER_GUIDE},
{"SHOW_HIDDEN", &SHOW_HIDDEN}, {"SHOW_HIDDEN", &SHOW_HIDDEN},
{"SHOW_DELETE", &SHOW_DELETE}, {"SHOW_DELETE", &SHOW_DELETE},
{"SHOW_UNSUPPORTED", &SHOW_UNSUPPORTED},
{"PAGE_SWAP", &PAGE_SWAP}, {"PAGE_SWAP", &PAGE_SWAP},
{"RIGHT_SIDE_MODE", &RIGHT_SIDE_MODE}, {"RIGHT_SIDE_MODE", &RIGHT_SIDE_MODE},
{"OVERLAY_VERSIONS", &OVERLAY_VERSIONS}, {"OVERLAY_VERSIONS", &OVERLAY_VERSIONS},
@@ -922,7 +949,7 @@ namespace ult {
//{"VERSION_LABELS", &VERSION_LABELS}, //{"VERSION_LABELS", &VERSION_LABELS},
{"KEY_COMBO", &KEY_COMBO}, {"KEY_COMBO", &KEY_COMBO},
{"MODE", &MODE}, {"MODE", &MODE},
{"MODES", &MODES}, {"LAUNCH_MODES", &LAUNCH_MODES},
{"LANGUAGE", &LANGUAGE}, {"LANGUAGE", &LANGUAGE},
{"OVERLAY_INFO", &OVERLAY_INFO}, {"OVERLAY_INFO", &OVERLAY_INFO},
{"SOFTWARE_UPDATE", &SOFTWARE_UPDATE}, {"SOFTWARE_UPDATE", &SOFTWARE_UPDATE},
@@ -937,11 +964,19 @@ namespace ult {
{"VENDOR", &VENDOR}, {"VENDOR", &VENDOR},
{"MODEL", &MODEL}, {"MODEL", &MODEL},
{"STORAGE", &STORAGE}, {"STORAGE", &STORAGE},
{"NOTICE", &NOTICE}, //{"NOTICE", &NOTICE},
{"UTILIZES", &UTILIZES}, //{"UTILIZES", &UTILIZES},
{"MEMORY_EXPANSION", &MEMORY_EXPANSION}, {"OVERLAY_MEMORY", &OVERLAY_MEMORY},
{"REBOOT_REQUIRED", &REBOOT_REQUIRED}, {"NOT_ENOUGH_MEMORY", &NOT_ENOUGH_MEMORY},
{"WALLPAPER_SUPPORT_DISABLED", &WALLPAPER_SUPPORT_DISABLED},
{"SOUND_SUPPORT_DISABLED", &SOUND_SUPPORT_DISABLED},
{"WALLPAPER_SUPPORT_ENABLED", &WALLPAPER_SUPPORT_ENABLED},
{"SOUND_SUPPORT_ENABLED", &SOUND_SUPPORT_ENABLED},
{"EXIT_OVERLAY_SYSTEM", &EXIT_OVERLAY_SYSTEM},
//{"MEMORY_EXPANSION", &MEMORY_EXPANSION},
//{"REBOOT_REQUIRED", &REBOOT_REQUIRED},
{"LOCAL_IP", &LOCAL_IP}, {"LOCAL_IP", &LOCAL_IP},
{"WALLPAPER", &WALLPAPER}, {"WALLPAPER", &WALLPAPER},
{"THEME", &THEME}, {"THEME", &THEME},
@@ -986,9 +1021,15 @@ namespace ult {
{"ULTRAHAND_HAS_STARTED", &ULTRAHAND_HAS_STARTED}, {"ULTRAHAND_HAS_STARTED", &ULTRAHAND_HAS_STARTED},
{"NEW_UPDATE_IS_AVAILABLE", &NEW_UPDATE_IS_AVAILABLE}, {"NEW_UPDATE_IS_AVAILABLE", &NEW_UPDATE_IS_AVAILABLE},
{"REBOOT_IS_REQUIRED", &REBOOT_IS_REQUIRED}, //{"REBOOT_IS_REQUIRED", &REBOOT_IS_REQUIRED},
{"HOLD_A_TO_DELETE", &HOLD_A_TO_DELETE}, //{"HOLD_A_TO_DELETE", &HOLD_A_TO_DELETE},
{"DELETE_PACKAGE", &DELETE_PACKAGE},
{"DELETE_OVERLAY", &DELETE_OVERLAY},
{"SELECTION_IS_EMPTY", &SELECTION_IS_EMPTY}, {"SELECTION_IS_EMPTY", &SELECTION_IS_EMPTY},
{"FORCED_SUPPORT_WARNING", &FORCED_SUPPORT_WARNING},
{"INCOMPATIBLE_WARNING", &INCOMPATIBLE_WARNING},
{"TASK_IS_COMPLETE", &TASK_IS_COMPLETE},
{"TASK_HAS_FAILED", &TASK_HAS_FAILED},
//{"PACKAGE_VERSIONS", &PACKAGE_VERSIONS}, //{"PACKAGE_VERSIONS", &PACKAGE_VERSIONS},
//{"PROGRESS_ANIMATION", &PROGRESS_ANIMATION}, //{"PROGRESS_ANIMATION", &PROGRESS_ANIMATION},
@@ -999,6 +1040,7 @@ namespace ult {
{"BOOT_ENTRY", &BOOT_ENTRY}, {"BOOT_ENTRY", &BOOT_ENTRY},
#endif #endif
{"SYSTEM_RAM", &SYSTEM_RAM},
{"FREE", &FREE}, {"FREE", &FREE},
{"DEFAULT_CHAR_WIDTH", &DEFAULT_CHAR_WIDTH}, {"DEFAULT_CHAR_WIDTH", &DEFAULT_CHAR_WIDTH},
@@ -1144,41 +1186,47 @@ namespace ult {
// Unified function to apply replacements // Unified function to apply replacements
void applyLangReplacements(std::string& text, bool isValue) { void applyLangReplacements(std::string& text, bool isValue) {
// Static maps for replacements if (isValue) {
#if IS_LAUNCHER_DIRECTIVE // Direct comparison for value replacements
const std::unordered_map<std::string, std::string*> launcherReplacements = { if (text.length() == 2) {
{"Reboot To", &REBOOT_TO}, if (text[0] == 'O') {
{"Boot Entry", &BOOT_ENTRY}, if (text[1] == 'n') {
{"Reboot", &REBOOT}, text = ON;
{"Shutdown", &SHUTDOWN}
};
#endif
const std::unordered_map<std::string, std::string*> valueReplacements = {
{"On", &ON},
{"Off", &OFF}
};
// Determine which map to use
const std::unordered_map<std::string, std::string*>* replacements = nullptr;
if (!isValue) {
#if IS_LAUNCHER_DIRECTIVE
replacements = &launcherReplacements;
#else
return; return;
} else if (text[1] == 'f' && text == "Off") {
text = OFF;
return;
}
}
}
}
#if IS_LAUNCHER_DIRECTIVE
else {
// Direct comparison for launcher replacements
switch (text.length()) {
case 6:
if (text == "Reboot") {
text = REBOOT;
}
break;
case 8:
if (text == "Shutdown") {
text = SHUTDOWN;
}
break;
case 9:
if (text == "Reboot To") {
text = REBOOT_TO;
}
break;
case 10:
if (text == "Boot Entry") {
text = BOOT_ENTRY;
}
break;
}
}
#endif #endif
} else {
replacements = &valueReplacements;
}
// Perform the direct replacement
if (replacements) {
auto it = replacements->find(text);
if (it != replacements->end()) {
text = *(it->second);
}
}
} }
@@ -1252,7 +1300,8 @@ namespace ult {
{"selection_value_text_color", "FF7777"}, {"selection_value_text_color", "FF7777"},
{"selection_bg_color", blackColor}, {"selection_bg_color", blackColor},
{"selection_bg_alpha", "11"}, {"selection_bg_alpha", "11"},
{"trackbar_color", "555555"}, {"scrollbar_color", "555555"},
{"scrollbar_wall_color", "AAAAAA"},
{"highlight_color_1", "2288CC"}, {"highlight_color_1", "2288CC"},
{"highlight_color_2", "88FFFF"}, {"highlight_color_2", "88FFFF"},
{"highlight_color_3", "FFFF45"}, {"highlight_color_3", "FFFF45"},
@@ -1292,23 +1341,6 @@ namespace ult {
} }
float calculateAmplitude(float x, float peakDurationFactor) {
//const float phasePeriod = 360.0f * peakDurationFactor; // One full phase period
// Convert x from radians to degrees and calculate phase within the period
const int phase = static_cast<int>(x * RAD_TO_DEG) % static_cast<int>(360.0f * peakDurationFactor);
// Check if the phase is odd using bitwise operation
if (phase & 1) {
return 1.0f; // Flat amplitude (maximum positive)
} else {
// Calculate the sinusoidal amplitude for the remaining period
return (APPROXIMATE_cos(x) + 1.0f) / 2.0f; // Cosine function expects radians
}
}
std::atomic<bool> refreshWallpaperNow(false); std::atomic<bool> refreshWallpaperNow(false);
std::atomic<bool> refreshWallpaper(false); std::atomic<bool> refreshWallpaper(false);
std::vector<u8> wallpaperData; std::vector<u8> wallpaperData;
@@ -1318,125 +1350,6 @@ namespace ult {
std::condition_variable cv; std::condition_variable cv;
// Function to load the RGBA file into memory and modify wallpaperData directly
//void loadWallpaperFile(const std::string& filePath, s32 width, s32 height) {
// const size_t originalDataSize = width * height * 4; // Original size in bytes (4 bytes per pixel)
// const size_t compressedDataSize = originalDataSize / 2; // RGBA4444 uses half the space
//
// wallpaperData.resize(compressedDataSize);
//
// if (!isFileOrDirectory(filePath)) {
// wallpaperData.clear();
// return;
// }
//
// #if !USING_FSTREAM_DIRECTIVE
// FILE* file = fopen(filePath.c_str(), "rb");
// if (!file) {
// wallpaperData.clear();
// return;
// }
//
// std::vector<uint8_t> buffer;
// //if (reducedMemory) {
// // // Reuse wallpaperData to avoid double allocation
// // buffer.swap(wallpaperData);
// // buffer.resize(originalDataSize);
// //} else {
// buffer.resize(originalDataSize);
// //}
//
// const size_t bytesRead = fread(buffer.data(), 1, originalDataSize, file);
// fclose(file);
//
// if (bytesRead != originalDataSize) {
// wallpaperData.clear();
// return;
// }
//
// #else
// std::ifstream file(filePath, std::ios::binary);
// if (!file) {
// wallpaperData.clear();
// return;
// }
//
// std::vector<uint8_t> buffer;
// //if (reducedMemory) {
// // buffer.swap(wallpaperData);
// // buffer.resize(originalDataSize);
// //} else {
// buffer.resize(originalDataSize);
// //}
//
// file.read(reinterpret_cast<char*>(buffer.data()), originalDataSize);
// if (!file) {
// wallpaperData.clear();
// return;
// }
// #endif
//
// // Compress RGBA8888 to RGBA4444
// //if (reducedMemory) {
// // // In-place compression to save memory
// // size_t writeIndex = 0;
// // for (size_t i = 0; i < originalDataSize; i += 8, writeIndex += 4) {
// // uint8_t r1 = buffer[i] >> 4;
// // uint8_t g1 = buffer[i + 1] >> 4;
// // uint8_t b1 = buffer[i + 2] >> 4;
// // uint8_t a1 = buffer[i + 3] >> 4;
// //
// // uint8_t r2 = buffer[i + 4] >> 4;
// // uint8_t g2 = buffer[i + 5] >> 4;
// // uint8_t b2 = buffer[i + 6] >> 4;
// // uint8_t a2 = buffer[i + 7] >> 4;
// //
// // buffer[writeIndex] = (r1 << 4) | g1;
// // buffer[writeIndex + 1] = (b1 << 4) | a1;
// // buffer[writeIndex + 2] = (r2 << 4) | g2;
// // buffer[writeIndex + 3] = (b2 << 4) | a2;
// // }
// // buffer.resize(compressedDataSize);
// // wallpaperData.swap(buffer);
// //} else {
// uint8_t* input = buffer.data();
// uint8_t* output = wallpaperData.data();
// //uint8_t r1, g1, b1, a1;
// //uint8_t r2, g2, b2, a2;
//
// //for (size_t i = 0, j = 0; i < originalDataSize; i += 8, j += 4) {
// // // Read 2 RGBA pixels (8 bytes)
// // const uint8_t r1 = input[i] >> 4;
// // const uint8_t g1 = input[i + 1] >> 4;
// // const uint8_t b1 = input[i + 2] >> 4;
// // const uint8_t a1 = input[i + 3] >> 4;
// //
// // const uint8_t r2 = input[i + 4] >> 4;
// // const uint8_t g2 = input[i + 5] >> 4;
// // const uint8_t b2 = input[i + 6] >> 4;
// // const uint8_t a2 = input[i + 7] >> 4;
// //
// // // Pack them into 4 bytes (2 bytes per pixel)
// // output[j] = (r1 << 4) | g1;
// // output[j + 1] = (b1 << 4) | a1;
// // output[j + 2] = (r2 << 4) | g2;
// // output[j + 3] = (b2 << 4) | a2;
// //}
//
// for (size_t i = 0, j = 0; i < originalDataSize; i += 16, j += 8) {
// output[j] = ((input[i] >> 4) << 4) | (input[i + 1] >> 4);
// output[j + 1] = ((input[i + 2] >> 4) << 4) | (input[i + 3] >> 4);
// output[j + 2] = ((input[i + 4] >> 4) << 4) | (input[i + 5] >> 4);
// output[j + 3] = ((input[i + 6] >> 4) << 4) | (input[i + 7] >> 4);
// output[j + 4] = ((input[i + 8] >> 4) << 4) | (input[i + 9] >> 4);
// output[j + 5] = ((input[i + 10] >> 4) << 4) | (input[i + 11] >> 4);
// output[j + 6] = ((input[i + 12] >> 4) << 4) | (input[i + 13] >> 4);
// output[j + 7] = ((input[i + 14] >> 4) << 4) | (input[i + 15] >> 4);
// }
// //}
//}
void loadWallpaperFile(const std::string& filePath, s32 width, s32 height) { void loadWallpaperFile(const std::string& filePath, s32 width, s32 height) {
const size_t originalDataSize = width * height * 4; const size_t originalDataSize = width * height * 4;
const size_t compressedDataSize = originalDataSize / 2; const size_t compressedDataSize = originalDataSize / 2;
@@ -1454,32 +1367,40 @@ namespace ult {
return; return;
} }
constexpr size_t chunkBytes = 64 * 1024; // 64 KB chunks setvbuf(file, nullptr, _IOFBF, 256 * 1024);
constexpr size_t chunkBytes = 128 * 1024;
uint8_t chunkBuffer[chunkBytes]; uint8_t chunkBuffer[chunkBytes];
size_t totalRead = 0; size_t totalRead = 0;
size_t writeIndex = 0; uint8_t* dst = wallpaperData.data();
const uint8x8_t mask = vdup_n_u8(0xF0);
size_t remaining, toRead, bytesRead;
while (totalRead < originalDataSize) { while (totalRead < originalDataSize) {
// Determine how much to read this iteration const size_t remaining = originalDataSize - totalRead;
remaining = originalDataSize - totalRead; const size_t toRead = remaining < chunkBytes ? remaining : chunkBytes;
toRead = remaining < chunkBytes ? remaining : chunkBytes;
bytesRead = fread(chunkBuffer, 1, toRead, file); const size_t bytesRead = fread(chunkBuffer, 1, toRead, file);
if (bytesRead == 0 || bytesRead % 8 != 0) { // must be multiple of 2 pixels if (bytesRead == 0) {
fclose(file); fclose(file);
wallpaperData.clear(); wallpaperData.clear();
return; return;
} }
// Compress each 2-pixel group in the chunk const uint8_t* src = chunkBuffer;
for (size_t i = 0; i < bytesRead; i += 8, writeIndex += 4) { size_t i = 0;
wallpaperData[writeIndex] = (chunkBuffer[i] & 0xF0) | (chunkBuffer[i + 1] >> 4);
wallpaperData[writeIndex + 1] = (chunkBuffer[i + 2] & 0xF0) | (chunkBuffer[i + 3] >> 4); // NEON: Process 16 bytes -> 8 bytes
wallpaperData[writeIndex + 2] = (chunkBuffer[i + 4] & 0xF0) | (chunkBuffer[i + 5] >> 4); for (; i + 16 <= bytesRead; i += 16) {
wallpaperData[writeIndex + 3] = (chunkBuffer[i + 6] & 0xF0) | (chunkBuffer[i + 7] >> 4); uint8x16_t data = vld1q_u8(src + i);
uint8x8x2_t sep = vuzp_u8(vget_low_u8(data), vget_high_u8(data));
vst1_u8(dst, vorr_u8(vand_u8(sep.val[0], mask), vshr_n_u8(sep.val[1], 4)));
dst += 8;
}
// Scalar fallback
for (; i + 1 < bytesRead; i += 2) {
*dst++ = (src[i] & 0xF0) | (src[i + 1] >> 4);
} }
totalRead += bytesRead; totalRead += bytesRead;
@@ -1762,8 +1683,8 @@ namespace ult {
// Time implementation // Time implementation
CONSTEXPR_STRING std::string DEFAULT_DT_FORMAT = "'%a %T'"; CONSTEXPR_STRING std::string DEFAULT_DT_FORMAT = "%a %T";
std::string datetimeFormat = "%a %T"; std::string datetimeFormat = DEFAULT_DT_FORMAT;
// Widget settings // Widget settings
@@ -1798,9 +1719,114 @@ namespace ult {
bool cleanVersionLabels, hideOverlayVersions, hidePackageVersions, useLibultrahandTitles, useLibultrahandVersions, usePackageTitles, usePackageVersions; bool cleanVersionLabels, hideOverlayVersions, hidePackageVersions, useLibultrahandTitles, useLibultrahandVersions, usePackageTitles, usePackageVersions;
// Helper function to convert MB to bytes
u64 mbToBytes(u32 mb) {
return static_cast<u64>(mb) * 0x100000;
}
// Helper function to convert bytes to MB
u32 bytesToMB(u64 bytes) {
return static_cast<u32>(bytes / 0x100000);
}
// Helper function to get version-appropriate default heap size
static OverlayHeapSize getDefaultHeapSize() {
if (hosversionAtLeast(21, 0, 0)) {
return OverlayHeapSize::Size_4MB; // HOS 21.0.0+
} else if (hosversionAtLeast(20, 0, 0)) {
return OverlayHeapSize::Size_6MB; // HOS 20.0.0+
} else {
return OverlayHeapSize::Size_8MB; // Older versions
}
}
// Implementation
OverlayHeapSize getCurrentHeapSize() {
// Fast path: return cached value if already loaded
if (heapSizeCache.initialized) {
return heapSizeCache.cachedSize;
}
// Slow path: read from file (only happens once)
FILE* f = fopen(ult::OVL_HEAP_CONFIG_PATH.c_str(), "rb");
if (!f) {
// No config file - use version-specific default
heapSizeCache.cachedSize = getDefaultHeapSize();
heapSizeCache.initialized = true;
return heapSizeCache.cachedSize;
}
u64 size;
if (fread(&size, sizeof(size), 1, f) == 1) {
constexpr u64 twoMB = 0x200000;
// Only accept multiples of 2MB, excluding 2MB itself
if (size != twoMB && size % twoMB == 0) {
heapSizeCache.cachedSize = static_cast<OverlayHeapSize>(size);
fclose(f);
heapSizeCache.initialized = true;
return heapSizeCache.cachedSize;
}
}
// Invalid or no data in config - use version-specific default
fclose(f);
heapSizeCache.cachedSize = getDefaultHeapSize();
heapSizeCache.initialized = true;
return heapSizeCache.cachedSize;
}
// Update the global default too
OverlayHeapSize currentHeapSize = getDefaultHeapSize();
bool setOverlayHeapSize(OverlayHeapSize heapSize) {
ult::createDirectory(ult::NX_OVLLOADER_PATH);
FILE* f = fopen(ult::OVL_HEAP_CONFIG_PATH.c_str(), "wb");
if (!f) return false;
const u64 size = static_cast<u64>(heapSize);
const bool success = (fwrite(&size, sizeof(size), 1, f) == 1);
fclose(f);
// Update cache on successful write
if (success) {
heapSizeCache.cachedSize = heapSize;
heapSizeCache.initialized = true;
}
return success;
}
// Implementation
bool requestOverlayExit() {
ult::createDirectory(ult::NX_OVLLOADER_PATH);
FILE* f = fopen(ult::OVL_EXIT_FLAG_PATH.c_str(), "wb");
if (!f) return false;
// Write a single byte (flag file just needs to exist)
u8 flag = 1;
bool success = (fwrite(&flag, 1, 1, f) == 1);
fclose(f);
deleteFileOrDirectory(NOTIFICATIONS_FLAG_FILEPATH);
return success;
}
const std::string loaderInfo = envGetLoaderInfo(); const std::string loaderInfo = envGetLoaderInfo();
const std::string loaderTitle = extractTitle(loaderInfo); std::string loaderTitle = extractTitle(loaderInfo);
const bool expandedMemory = (loaderTitle == "nx-ovlloader+");
bool expandedMemory = false;
bool furtherExpandedMemory = false;
bool limitedMemory = false;
std::string versionLabel; std::string versionLabel;
@@ -1834,9 +1860,10 @@ namespace ult {
// Number of renderer threads to use // Number of renderer threads to use
const unsigned numThreads = expandedMemory ? 4 : 0; const unsigned numThreads = 4;//expandedMemory ? 4 : 0;
std::vector<std::thread> renderThreads(numThreads); std::vector<std::thread> renderThreads(numThreads);
const s32 bmpChunkSize = (numThreads > 0) ? ((720 + numThreads - 1) / numThreads) : 0;
const s32 bmpChunkSize = ((720 + numThreads - 1) / numThreads);
std::atomic<s32> currentRow; std::atomic<s32> currentRow;
//std::atomic<unsigned int> barrierCounter{0}; //std::atomic<unsigned int> barrierCounter{0};

View File

@@ -97,12 +97,33 @@ void AppProfileGui::addModuleListItem(SysClkProfile profile, SysClkModule module
this->listElement->addItem(listItem); this->listElement->addItem(listItem);
} }
void AppProfileGui::addModuleListItemToggle(SysClkProfile profile, SysClkModule module)
{
const char* moduleName = sysclkFormatModule(module, true);
std::uint32_t currentValue = this->profileList->mhzMap[profile][module];
tsl::elm::ToggleListItem* toggle = new tsl::elm::ToggleListItem(moduleName, currentValue != 0);
toggle->setStateChangedListener([this, profile, module](bool state) {
this->profileList->mhzMap[profile][module] = state ? 1 : 0;
Result rc = sysclkIpcSetProfiles(this->applicationId, this->profileList);
if(R_FAILED(rc))
{
FatalGui::openWithResultCode("sysclkIpcSetProfiles", rc);
}
});
this->listElement->addItem(toggle);
}
void AppProfileGui::addProfileUI(SysClkProfile profile) void AppProfileGui::addProfileUI(SysClkProfile profile)
{ {
this->listElement->addItem(new tsl::elm::CategoryHeader(sysclkFormatProfile(profile, true) + std::string(" ") + ult::DIVIDER_SYMBOL + "  Reset")); this->listElement->addItem(new tsl::elm::CategoryHeader(sysclkFormatProfile(profile, true) + std::string(" ") + ult::DIVIDER_SYMBOL + "  Reset"));
this->addModuleListItem(profile, SysClkModule_CPU); this->addModuleListItem(profile, SysClkModule_CPU);
this->addModuleListItem(profile, SysClkModule_GPU); this->addModuleListItem(profile, SysClkModule_GPU);
this->addModuleListItem(profile, SysClkModule_MEM); this->addModuleListItem(profile, SysClkModule_MEM);
this->addModuleListItemToggle(profile, HorizonOCModule_Governor);
} }
void AppProfileGui::listUI() void AppProfileGui::listUI()

View File

@@ -40,6 +40,7 @@ class AppProfileGui : public BaseMenuGui
void openFreqChoiceGui(tsl::elm::ListItem* listItem, SysClkProfile profile, SysClkModule module); void openFreqChoiceGui(tsl::elm::ListItem* listItem, SysClkProfile profile, SysClkModule module);
void addModuleListItem(SysClkProfile profile, SysClkModule module); void addModuleListItem(SysClkProfile profile, SysClkModule module);
void addModuleListItemToggle(SysClkProfile profile, SysClkModule module);
void addProfileUI(SysClkProfile profile); void addProfileUI(SysClkProfile profile);
public: public:

View File

@@ -27,7 +27,6 @@
#include "base_gui.h" #include "base_gui.h"
#include "../elements/base_frame.h" #include "../elements/base_frame.h"
#include "logo_rgba_bin.h"
#include <tesla.hpp> #include <tesla.hpp>
#include <math.h> #include <math.h>

View File

@@ -96,16 +96,40 @@ void GlobalOverrideGui::addModuleListItem(SysClkModule module)
} }
return false; return false;
}); });
this->listElement->addItem(listItem); this->listElement->addItem(listItem);
this->listItems[module] = listItem; this->listItems[module] = listItem;
} }
void GlobalOverrideGui::addModuleToggleItem(SysClkModule module)
{
const char* moduleName = sysclkFormatModule(module, true);
bool isOn = this->listHz[module];
// Create a ToggleListItem
tsl::elm::ToggleListItem* toggle = new tsl::elm::ToggleListItem(moduleName, isOn);
toggle->setStateChangedListener([this, module, toggle](bool state) {
Result rc = sysclkIpcSetOverride(module, state ? 1 : 0);
if(R_FAILED(rc))
{
FatalGui::openWithResultCode("sysclkIpcSetProfiles", rc);
}
});
// Add to list and track
this->listElement->addItem(toggle);
this->listItems[module] = toggle;
}
void GlobalOverrideGui::listUI() void GlobalOverrideGui::listUI()
{ {
this->listElement->addItem(new tsl::elm::CategoryHeader("Temporary Overrides " + ult::DIVIDER_SYMBOL + "  Reset")); this->listElement->addItem(new tsl::elm::CategoryHeader("Temporary Overrides " + ult::DIVIDER_SYMBOL + "  Reset"));
this->addModuleListItem(SysClkModule_CPU); this->addModuleListItem(SysClkModule_CPU);
this->addModuleListItem(SysClkModule_GPU); this->addModuleListItem(SysClkModule_GPU);
this->addModuleListItem(SysClkModule_MEM); this->addModuleListItem(SysClkModule_MEM);
this->addModuleToggleItem(HorizonOCModule_Governor);
} }
void GlobalOverrideGui::refresh() void GlobalOverrideGui::refresh()
@@ -115,6 +139,8 @@ void GlobalOverrideGui::refresh()
{ {
for(std::uint16_t m = 0; m < SysClkModule_EnumMax; m++) for(std::uint16_t m = 0; m < SysClkModule_EnumMax; m++)
{ {
if(m > SysClkModule_MEM)
continue;
if(this->listItems[m] != nullptr && this->listHz[m] != this->context->overrideFreqs[m]) if(this->listItems[m] != nullptr && this->listHz[m] != this->context->overrideFreqs[m])
{ {
this->listItems[m]->setValue(formatListFreqHz(this->context->overrideFreqs[m])); this->listItems[m]->setValue(formatListFreqHz(this->context->overrideFreqs[m]));

View File

@@ -39,7 +39,7 @@ class GlobalOverrideGui : public BaseMenuGui
void openFreqChoiceGui(SysClkModule module); void openFreqChoiceGui(SysClkModule module);
void addModuleListItem(SysClkModule module); void addModuleListItem(SysClkModule module);
void addModuleToggleItem(SysClkModule module);
public: public:
GlobalOverrideGui(); GlobalOverrideGui();
~GlobalOverrideGui() {} ~GlobalOverrideGui() {}

View File

@@ -399,8 +399,6 @@ void MiscGui::listUI()
chargerCurrents, chargerCurrents,
false false
); );
addConfigToggle(HocClkConfigValue_HandheldGovernor, nullptr);
} }

BIN
Source/sys-clk/sys-clk.zip Normal file

Binary file not shown.

View File

@@ -37,7 +37,9 @@
#include "notification.h" #include "notification.h"
#define HOSPPC_HAS_BOOST (hosversionAtLeast(7,0,0)) #define HOSPPC_HAS_BOOST (hosversionAtLeast(7,0,0))
bool isGovernorEnabled = false; // to avoid thread messes
bool lastGovernorState = false;
bool hasChanged = true;
ClockManager *ClockManager::instance = NULL; ClockManager *ClockManager::instance = NULL;
Thread governorTHREAD; Thread governorTHREAD;
@@ -276,7 +278,7 @@ void ClockManager::GovernorThread(void* arg)
std::scoped_lock lock{mgr->contextMutex}; std::scoped_lock lock{mgr->contextMutex};
if (!mgr->config->GetConfigValue(HocClkConfigValue_HandheldGovernor)) if (!isGovernorEnabled)
{ {
svcSleepThread(50'000'000); svcSleepThread(50'000'000);
continue; continue;
@@ -388,7 +390,6 @@ void ClockManager::Tick()
} }
} }
if(((tmp451TempSoc() / 1000) > (int)this->config->GetConfigValue(HocClkConfigValue_ThermalThrottleThreshold)) && this->config->GetConfigValue(HocClkConfigValue_ThermalThrottle)) { if(((tmp451TempSoc() / 1000) > (int)this->config->GetConfigValue(HocClkConfigValue_ThermalThrottleThreshold)) && this->config->GetConfigValue(HocClkConfigValue_ThermalThrottle)) {
ResetToStockClocks(); ResetToStockClocks();
return; return;
@@ -406,15 +407,9 @@ void ClockManager::Tick()
ResetToStockClocks(); ResetToStockClocks();
return; return;
} }
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
{ {
if(this->config->GetConfigValue(HocClkConfigValue_HandheldGovernor)) {
noGPU = true;
} else {
noGPU = false;
}
if(noGPU && module == SysClkModule_GPU)
continue;
targetHz = this->context->overrideFreqs[module]; targetHz = this->context->overrideFreqs[module];
if (!targetHz) if (!targetHz)
{ {
@@ -423,26 +418,51 @@ void ClockManager::Tick()
targetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, (SysClkModule)module, this->context->profile); targetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, (SysClkModule)module, this->context->profile);
} }
if(module == HorizonOCModule_Governor) {
bool newGovernorState = targetHz;
if(newGovernorState != lastGovernorState) {
FileUtils::LogLine("[mgr] Governor state changed: %s", newGovernorState ? "enabled" : "disabled");
lastGovernorState = newGovernorState;
// Force a "context refresh" like on app/profile change
hasChanged = true;
this->context->enabled = this->GetConfig()->Enabled();
Board::ResetToStock(); // optional: reset clocks before re-applying
}
isGovernorEnabled = newGovernorState;
}
// Skip GPU if governor handles it
if(module > SysClkModule_MEM) {
continue;
}
if(isGovernorEnabled) {
noGPU = true;
} else {
noGPU = false;
}
if(noGPU && module == SysClkModule_GPU)
continue;
if (targetHz) if (targetHz)
{ {
maxHz = this->GetMaxAllowedHz((SysClkModule)module, this->context->profile); maxHz = this->GetMaxAllowedHz((SysClkModule)module, this->context->profile);
nearestHz = this->GetNearestHz((SysClkModule)module, targetHz, maxHz); nearestHz = this->GetNearestHz((SysClkModule)module, targetHz, maxHz);
if (nearestHz != this->context->freqs[module] && this->context->enabled) { if (nearestHz != this->context->freqs[module] && this->context->enabled) {
FileUtils::LogLine( FileUtils::LogLine(
"[mgr] %s clock set : %u.%u MHz (target = %u.%u MHz)", "[mgr] %s clock set : %u.%u MHz (target = %u.%u MHz)",
Board::GetModuleName((SysClkModule)module, true), Board::GetModuleName((SysClkModule)module, true),
nearestHz / 1000000, nearestHz / 100000 - nearestHz / 1000000 * 10, nearestHz / 1000000, nearestHz / 100000 - nearestHz / 1000000 * 10,
targetHz / 1000000, targetHz / 100000 - targetHz / 1000000 * 10); targetHz / 1000000, targetHz / 100000 - targetHz / 1000000 * 10
);
Board::SetHz((SysClkModule)module, nearestHz); Board::SetHz((SysClkModule)module, nearestHz);
this->context->freqs[module] = nearestHz; this->context->freqs[module] = nearestHz;
} }
} }
} }
} }
} }
void ClockManager::ResetToStockClocks() { void ClockManager::ResetToStockClocks() {