v0.18
This commit is contained in:
@@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 + ((unicode_codepoint - start) << 1) + idRangeOffset_offset);
|
return FAST_USHORT(data + offset + ((uc - start) << 1) + base + (segcount * 6) + 2 + (item << 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 12: { // 32-bit format
|
case 12:
|
||||||
stbtt_uint32 ngroups = FAST_ULONG(data + index_map + 12);
|
case 13: {
|
||||||
uc = (stbtt_uint32)unicode_codepoint;
|
stbtt_uint32 low = 0, high = FAST_ULONG(data + index_map + 12);
|
||||||
|
|
||||||
// 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 if (uc <= FAST_ULONG(group + 4)) {
|
||||||
|
stbtt_uint32 start_glyph = FAST_ULONG(group + 8);
|
||||||
|
return (format == 12) ? (start_glyph + uc - start_char) : start_glyph;
|
||||||
} else {
|
} else {
|
||||||
end_char = FAST_ULONG(group + 4);
|
|
||||||
if (uc <= end_char) {
|
|
||||||
stbtt_uint32 start_glyph = FAST_ULONG(group + 8);
|
|
||||||
return start_glyph + uc - start_char;
|
|
||||||
}
|
|
||||||
low = mid + 1;
|
low = mid + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 13: { // 32-bit format, many-to-one mapping
|
case 0:
|
||||||
stbtt_uint32 ngroups = FAST_ULONG(data + index_map + 12);
|
return (uc < (stbtt_uint32)(FAST_USHORT(data + index_map + 2) - 6)) ?
|
||||||
uc = (stbtt_uint32)unicode_codepoint;
|
data[index_map + 6 + uc] : 0;
|
||||||
|
|
||||||
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 {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0: { // Apple byte encoding - simple and fast
|
case 6: {
|
||||||
stbtt_int32 bytes = FAST_USHORT(data + index_map + 2);
|
stbtt_uint32 offset = uc - FAST_USHORT(data + index_map + 6);
|
||||||
return ((unsigned)unicode_codepoint < (unsigned)(bytes - 6)) ?
|
return (offset < (stbtt_uint32)FAST_USHORT(data + index_map + 8)) ?
|
||||||
data[index_map + 6 + unicode_codepoint] : 0;
|
FAST_USHORT(data + index_map + 10 + (offset << 1)) : 0;
|
||||||
}
|
|
||||||
|
|
||||||
case 6: { // Trimmed table mapping
|
|
||||||
stbtt_uint32 first = FAST_USHORT(data + index_map + 6);
|
|
||||||
stbtt_uint32 count = FAST_USHORT(data + index_map + 8);
|
|
||||||
uc = (stbtt_uint32)unicode_codepoint;
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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 libnx’s audout service,
|
* for loading, caching, and playing WAV audio through libnx’s 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 = {});
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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,24 +67,24 @@ 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;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < m_cachedSounds.size(); ++i) {
|
for (uint32_t i = 0; i < m_cachedSounds.size(); ++i) {
|
||||||
SoundType current = static_cast<SoundType>(i);
|
SoundType current = static_cast<SoundType>(i);
|
||||||
|
|
||||||
// Skip if this sound is in the exclude list
|
// Skip if this sound is in the exclude list
|
||||||
if (std::find(excludeSounds.begin(), excludeSounds.end(), current) != excludeSounds.end()) {
|
if (std::find(excludeSounds.begin(), excludeSounds.end(), current) != excludeSounds.end()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& cached = m_cachedSounds[i];
|
auto& cached = m_cachedSounds[i];
|
||||||
if (cached.buffer) {
|
if (cached.buffer) {
|
||||||
free(cached.buffer);
|
free(cached.buffer);
|
||||||
@@ -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,28 +108,28 @@ 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;
|
||||||
|
|
||||||
// Free existing buffer
|
// Free existing buffer
|
||||||
free(m_cachedSounds[idx].buffer);
|
free(m_cachedSounds[idx].buffer);
|
||||||
m_cachedSounds[idx] = { nullptr, 0, 0 };
|
m_cachedSounds[idx] = { nullptr, 0, 0 };
|
||||||
|
|
||||||
FILE* f = fopen(path, "rb");
|
FILE* f = fopen(path, "rb");
|
||||||
if (!f) return false;
|
if (!f) return false;
|
||||||
|
|
||||||
// Parse WAV header
|
// Parse WAV header
|
||||||
char hdr[12];
|
char hdr[12];
|
||||||
if (fread(hdr, 1, 12, f) != 12 || memcmp(hdr, "RIFF", 4) || memcmp(hdr + 8, "WAVE", 4)) {
|
if (fread(hdr, 1, 12, f) != 12 || memcmp(hdr, "RIFF", 4) || memcmp(hdr + 8, "WAVE", 4)) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 fmt = 0, ch = 0, bits = 0;
|
u16 fmt = 0, ch = 0, bits = 0;
|
||||||
u32 rate = 0, dSize = 0;
|
u32 rate = 0, dSize = 0;
|
||||||
long dPos = 0;
|
long dPos = 0;
|
||||||
|
|
||||||
// Find fmt and data chunks
|
// Find fmt and data chunks
|
||||||
while (fread(hdr, 1, 8, f) == 8) {
|
while (fread(hdr, 1, 8, f) == 8) {
|
||||||
const u32 sz = *(u32*)(hdr + 4);
|
const u32 sz = *(u32*)(hdr + 4);
|
||||||
@@ -148,13 +148,13 @@ namespace ult {
|
|||||||
fseek(f, sz, SEEK_CUR);
|
fseek(f, sz, SEEK_CUR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate format
|
// Validate format
|
||||||
if (!dSize || fmt != 1 || ch == 0 || ch > 2 || (bits != 8 && bits != 16)) {
|
if (!dSize || fmt != 1 || ch == 0 || ch > 2 || (bits != 8 && bits != 16)) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate buffer sizes
|
// Calculate buffer sizes
|
||||||
// Note: audout REQUIRES stereo (2 channels), so we must duplicate mono
|
// Note: audout REQUIRES stereo (2 channels), so we must duplicate mono
|
||||||
const bool mono = (ch == 1);
|
const bool mono = (ch == 1);
|
||||||
@@ -165,14 +165,14 @@ namespace ult {
|
|||||||
// Use smaller alignment to reduce waste (256 bytes instead of 4KB)
|
// Use smaller alignment to reduce waste (256 bytes instead of 4KB)
|
||||||
const uint32_t align = 0x100;
|
const uint32_t align = 0x100;
|
||||||
const uint32_t bufSize = (outSize + align - 1) & ~(align - 1);
|
const uint32_t bufSize = (outSize + align - 1) & ~(align - 1);
|
||||||
|
|
||||||
// Allocate output buffer
|
// Allocate output buffer
|
||||||
void* buf = aligned_alloc(align, bufSize);
|
void* buf = aligned_alloc(align, bufSize);
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(f, dPos, SEEK_SET);
|
fseek(f, dPos, SEEK_SET);
|
||||||
s16* out = (s16*)buf;
|
s16* out = (s16*)buf;
|
||||||
|
|
||||||
@@ -182,7 +182,7 @@ namespace ult {
|
|||||||
effectiveVolume *= 0.5f;
|
effectiveVolume *= 0.5f;
|
||||||
}
|
}
|
||||||
const float scale = std::clamp(effectiveVolume, 0.0f, 1.0f);
|
const float scale = std::clamp(effectiveVolume, 0.0f, 1.0f);
|
||||||
|
|
||||||
// Process audio in chunks to minimize memory usage
|
// Process audio in chunks to minimize memory usage
|
||||||
// This eliminates the need for temporary vectors
|
// This eliminates the need for temporary vectors
|
||||||
constexpr uint32_t CHUNK_SIZE = 512;
|
constexpr uint32_t CHUNK_SIZE = 512;
|
||||||
@@ -241,25 +241,25 @@ namespace ult {
|
|||||||
remaining -= toRead;
|
remaining -= toRead;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
// Zero-fill any padding
|
// Zero-fill any padding
|
||||||
if (outSize < bufSize) {
|
if (outSize < bufSize) {
|
||||||
memset((u8*)buf + outSize, 0, bufSize - outSize);
|
memset((u8*)buf + outSize, 0, bufSize - outSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_cachedSounds[idx] = { buf, bufSize, outSize };
|
m_cachedSounds[idx] = { buf, bufSize, outSize };
|
||||||
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;
|
||||||
|
|
||||||
const uint32_t idx = static_cast<uint32_t>(type);
|
const uint32_t idx = static_cast<uint32_t>(type);
|
||||||
if (idx >= static_cast<uint32_t>(SoundType::Count)) return;
|
if (idx >= static_cast<uint32_t>(SoundType::Count)) return;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(m_audioMutex);
|
std::lock_guard<std::mutex> lock(m_audioMutex);
|
||||||
|
|
||||||
// Check again under lock
|
// Check again under lock
|
||||||
@@ -267,12 +267,12 @@ namespace ult {
|
|||||||
|
|
||||||
auto& cached = m_cachedSounds[idx];
|
auto& cached = m_cachedSounds[idx];
|
||||||
if (!cached.buffer) return;
|
if (!cached.buffer) return;
|
||||||
|
|
||||||
// Release any finished buffers
|
// Release any finished buffers
|
||||||
AudioOutBuffer* releasedBuffers = nullptr;
|
AudioOutBuffer* releasedBuffers = nullptr;
|
||||||
u32 releasedCount = 0;
|
u32 releasedCount = 0;
|
||||||
audoutGetReleasedAudioOutBuffer(&releasedBuffers, &releasedCount);
|
audoutGetReleasedAudioOutBuffer(&releasedBuffers, &releasedCount);
|
||||||
|
|
||||||
// Static buffer is safe with mutex protection
|
// Static buffer is safe with mutex protection
|
||||||
static AudioOutBuffer audioBuffer = {};
|
static AudioOutBuffer audioBuffer = {};
|
||||||
audioBuffer = {};
|
audioBuffer = {};
|
||||||
@@ -281,32 +281,32 @@ namespace ult {
|
|||||||
audioBuffer.data_size = cached.dataSize;
|
audioBuffer.data_size = cached.dataSize;
|
||||||
audioBuffer.data_offset = 0;
|
audioBuffer.data_offset = 0;
|
||||||
audioBuffer.next = nullptr;
|
audioBuffer.next = nullptr;
|
||||||
|
|
||||||
AudioOutBuffer* rel = nullptr;
|
AudioOutBuffer* rel = nullptr;
|
||||||
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);
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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();
|
||||||
@@ -309,6 +312,20 @@ bool downloadFile(const std::string& url, const std::string& toDestination, bool
|
|||||||
//cleanupCurl();
|
//cleanupCurl();
|
||||||
|
|
||||||
//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
|
||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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 = {"", "", "", "", "", "", "", ""};
|
||||||
|
|
||||||
|
|||||||
@@ -30,10 +30,13 @@ namespace ult {
|
|||||||
static HidVibrationDeviceHandle vibPlayer1Right;
|
static HidVibrationDeviceHandle vibPlayer1Right;
|
||||||
static u64 rumbleStartTick = 0;
|
static u64 rumbleStartTick = 0;
|
||||||
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() {
|
void rumbleClick() {
|
||||||
//if (!rumbleInitialized) {
|
// Use cached style bit instead of querying hid each call
|
||||||
// initRumble();
|
//const HidVibrationValue* pattern = cachedHandheldStyle ? &clickHandheld : &clickDocked;
|
||||||
// if (!rumbleInitialized) return;
|
sendVibration(&vibrationStop);
|
||||||
|
//if (cachedHandheldStyle) {
|
||||||
|
// sendVibration(&clickHandheld);
|
||||||
|
// sendVibration(&clickHandheld);
|
||||||
|
//} else {
|
||||||
|
// sendVibration(&clickDocked);
|
||||||
|
// sendVibration(&clickDocked);
|
||||||
//}
|
//}
|
||||||
|
sendVibration2x(&hapticsPreset);
|
||||||
sendVibration(hidGetNpadStyleSet(HidNpadIdType_Handheld) ? &clickHandheld : &clickDocked);
|
clickActive.store(true, std::memory_order_release);
|
||||||
rumbleActive.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,16 +228,34 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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,12 +802,29 @@ namespace ult {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
file.close();
|
// 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];
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string result(hexStream.begin(), hexStream.end());
|
|
||||||
result = stringToUppercase(result);
|
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -84,9 +84,9 @@ 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,18 +109,22 @@ namespace ult {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// More efficient newline removal
|
if (preserveNewlines) {
|
||||||
len = strlen(buffer);
|
// Keep the line as-is, including newlines
|
||||||
if (len > 0 && buffer[len - 1] == '\n') {
|
lines.emplace_back(buffer);
|
||||||
buffer[len - 1] = '\0';
|
} else {
|
||||||
--len;
|
// Remove newlines
|
||||||
// Also remove carriage return if present
|
len = strlen(buffer);
|
||||||
if (len > 0 && buffer[len - 1] == '\r') {
|
if (len > 0 && buffer[len - 1] == '\n') {
|
||||||
buffer[len - 1] = '\0';
|
buffer[len - 1] = '\0';
|
||||||
|
--len;
|
||||||
|
// Also remove carriage return if present
|
||||||
|
if (len > 0 && buffer[len - 1] == '\r') {
|
||||||
|
buffer[len - 1] = '\0';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
lines.emplace_back(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.emplace_back(buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(file);
|
fclose(file);
|
||||||
@@ -140,12 +144,17 @@ namespace ult {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove carriage return if present (getline removes \n but not \r)
|
if (preserveNewlines) {
|
||||||
if (!line.empty() && line.back() == '\r') {
|
// Add back the newline that getline removed
|
||||||
line.pop_back();
|
line += '\n';
|
||||||
|
lines.emplace_back(std::move(line));
|
||||||
|
} else {
|
||||||
|
// Remove carriage return if present (getline removes \n but not \r)
|
||||||
|
if (!line.empty() && line.back() == '\r') {
|
||||||
|
line.pop_back();
|
||||||
|
}
|
||||||
|
lines.emplace_back(std::move(line));
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.emplace_back(std::move(line));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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()
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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]));
|
||||||
|
|||||||
@@ -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() {}
|
||||||
|
|||||||
@@ -399,8 +399,6 @@ void MiscGui::listUI()
|
|||||||
chargerCurrents,
|
chargerCurrents,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
addConfigToggle(HocClkConfigValue_HandheldGovernor, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
Source/sys-clk/sys-clk.zip
Normal file
BIN
Source/sys-clk/sys-clk.zip
Normal file
Binary file not shown.
@@ -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;
|
||||||
@@ -402,47 +403,66 @@ void ClockManager::Tick()
|
|||||||
std::uint32_t maxHz = 0;
|
std::uint32_t maxHz = 0;
|
||||||
std::uint32_t nearestHz = 0;
|
std::uint32_t nearestHz = 0;
|
||||||
|
|
||||||
if(apmExtIsBoostMode(mode) && !this->config->GetConfigValue(HocClkConfigValue_OverwriteBoostMode)) {
|
if(apmExtIsBoostMode(mode) && !this->config->GetConfigValue(HocClkConfigValue_OverwriteBoostMode)) {
|
||||||
ResetToStockClocks();
|
ResetToStockClocks();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
|
||||||
|
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||||
|
{
|
||||||
|
targetHz = this->context->overrideFreqs[module];
|
||||||
|
if (!targetHz)
|
||||||
{
|
{
|
||||||
if(this->config->GetConfigValue(HocClkConfigValue_HandheldGovernor)) {
|
targetHz = this->config->GetAutoClockHz(this->context->applicationId, (SysClkModule)module, this->context->profile);
|
||||||
noGPU = true;
|
if(!targetHz)
|
||||||
} else {
|
targetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, (SysClkModule)module, this->context->profile);
|
||||||
noGPU = false;
|
}
|
||||||
}
|
|
||||||
if(noGPU && module == SysClkModule_GPU)
|
|
||||||
continue;
|
|
||||||
targetHz = this->context->overrideFreqs[module];
|
|
||||||
if (!targetHz)
|
|
||||||
{
|
|
||||||
targetHz = this->config->GetAutoClockHz(this->context->applicationId, (SysClkModule)module, this->context->profile);
|
|
||||||
if(!targetHz)
|
|
||||||
targetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, (SysClkModule)module, this->context->profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetHz)
|
if(module == HorizonOCModule_Governor) {
|
||||||
{
|
bool newGovernorState = targetHz;
|
||||||
|
if(newGovernorState != lastGovernorState) {
|
||||||
|
FileUtils::LogLine("[mgr] Governor state changed: %s", newGovernorState ? "enabled" : "disabled");
|
||||||
|
lastGovernorState = newGovernorState;
|
||||||
|
|
||||||
maxHz = this->GetMaxAllowedHz((SysClkModule)module, this->context->profile);
|
// Force a "context refresh" like on app/profile change
|
||||||
nearestHz = this->GetNearestHz((SysClkModule)module, targetHz, maxHz);
|
hasChanged = true;
|
||||||
if (nearestHz != this->context->freqs[module] && this->context->enabled) {
|
this->context->enabled = this->GetConfig()->Enabled();
|
||||||
FileUtils::LogLine(
|
Board::ResetToStock(); // optional: reset clocks before re-applying
|
||||||
"[mgr] %s clock set : %u.%u MHz (target = %u.%u MHz)",
|
}
|
||||||
Board::GetModuleName((SysClkModule)module, true),
|
isGovernorEnabled = newGovernorState;
|
||||||
nearestHz / 1000000, nearestHz / 100000 - nearestHz / 1000000 * 10,
|
}
|
||||||
targetHz / 1000000, targetHz / 100000 - targetHz / 1000000 * 10);
|
|
||||||
|
|
||||||
Board::SetHz((SysClkModule)module, nearestHz);
|
// Skip GPU if governor handles it
|
||||||
this->context->freqs[module] = nearestHz;
|
if(module > SysClkModule_MEM) {
|
||||||
}
|
continue;
|
||||||
}
|
}
|
||||||
|
if(isGovernorEnabled) {
|
||||||
|
noGPU = true;
|
||||||
|
} else {
|
||||||
|
noGPU = false;
|
||||||
|
}
|
||||||
|
if(noGPU && module == SysClkModule_GPU)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (targetHz)
|
||||||
|
{
|
||||||
|
maxHz = this->GetMaxAllowedHz((SysClkModule)module, this->context->profile);
|
||||||
|
nearestHz = this->GetNearestHz((SysClkModule)module, targetHz, maxHz);
|
||||||
|
|
||||||
|
if (nearestHz != this->context->freqs[module] && this->context->enabled) {
|
||||||
|
FileUtils::LogLine(
|
||||||
|
"[mgr] %s clock set : %u.%u MHz (target = %u.%u MHz)",
|
||||||
|
Board::GetModuleName((SysClkModule)module, true),
|
||||||
|
nearestHz / 1000000, nearestHz / 100000 - nearestHz / 1000000 * 10,
|
||||||
|
targetHz / 1000000, targetHz / 100000 - targetHz / 1000000 * 10
|
||||||
|
);
|
||||||
|
|
||||||
|
Board::SetHz((SysClkModule)module, nearestHz);
|
||||||
|
this->context->freqs[module] = nearestHz;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClockManager::ResetToStockClocks() {
|
void ClockManager::ResetToStockClocks() {
|
||||||
|
|||||||
BIN
dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp
vendored
BIN
dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp
vendored
Binary file not shown.
Reference in New Issue
Block a user