diff --git a/arm9/data/config_template.ini b/arm9/data/config_template.ini index 23dcf63..3e7158b 100644 Binary files a/arm9/data/config_template.ini and b/arm9/data/config_template.ini differ diff --git a/arm9/source/config.c b/arm9/source/config.c index 0319f61..d488112 100644 --- a/arm9/source/config.c +++ b/arm9/source/config.c @@ -24,6 +24,8 @@ * reasonable ways as different from the original version. */ +#define _GNU_SOURCE // for strchrnul + #include #include #include "config.h" @@ -42,6 +44,9 @@ #define MAKE_LUMA_VERSION_MCU(major, minor, build) (u16)(((major) & 0xFF) << 8 | ((minor) & 0x1F) << 5 | ((build) & 7)) +#define FLOAT_CONV_MULT 100000000ll +#define FLOAT_CONV_PRECISION 8u + CfgData configData; ConfigurationStatus needConfig; static CfgData oldConfig; @@ -95,10 +100,9 @@ static int parseBoolOption(bool *out, const char *val) } } -static int parseDecIntOption(s64 *out, const char *val, s64 minval, s64 maxval) +static int parseDecIntOptionImpl(s64 *out, const char *val, size_t numDigits, s64 minval, s64 maxval) { *out = 0; - size_t numDigits = strlen(val); s64 res = 0; size_t i = 0; @@ -130,6 +134,61 @@ static int parseDecIntOption(s64 *out, const char *val, s64 minval, s64 maxval) } } +static int parseDecIntOption(s64 *out, const char *val, s64 minval, s64 maxval) +{ + return parseDecIntOptionImpl(out, val, strlen(val), minval, maxval); +} + +static int parseDecFloatOption(s64 *out, const char *val, s64 minval, s64 maxval) +{ + char *point = strchrnul(val, '.'); + if (point == val) { + return -1; + } + + // Parse integer part, then fractional part + s64 intPart = 0; + s64 fracPart = 0; + int rc = parseDecIntOptionImpl(&intPart, val, point - val, INT64_MIN, INT64_MAX); + + if (rc != 0) { + return -1; + } + + s64 sign = intPart < 0 ? -1 : 1; + s64 intPartAbs = sign == -1 ? -intPart : intPart; + s64 res = 0; + bool of = __builtin_mul_overflow(intPartAbs, FLOAT_CONV_MULT, &res); + + if (of) { + return -1; + } + + s64 mul = FLOAT_CONV_MULT / 10; + + // Check if there's a fractional part + if (point[0] != '\0' && point[1] != '\0') { + for (char *pos = point + 1; *pos != '\0' && mul > 0; pos++) { + if (*pos < '0' || *pos > '9') { + return -1; + } + + res += (*pos - '0') * mul; + mul /= 10; + } + } + + + res = sign * (res + fracPart); + + if (res <= maxval && res >= minval && !of) { + *out = res; + return 0; + } else { + return -1; + } +} + static int parseHexIntOption(u64 *out, const char *val, u64 minval, u64 maxval) { *out = 0; @@ -222,6 +281,34 @@ static void menuComboToString(char *out, u32 combo) out[-1] = 0; } +static int encodedFloatToString(char *out, s64 val) +{ + s64 sign = val >= 0 ? 1 : -1; + + s64 intPart = (sign * val) / FLOAT_CONV_MULT; + s64 fracPart = (sign * val) % FLOAT_CONV_MULT; + + while (fracPart % 10 != 0) { + // Remove trailing zeroes + fracPart /= 10; + } + + int n = sprintf(out, "%lld", sign * intPart); + if (fracPart != 0) { + n += sprintf(out + n, ".%0*lld", (int)FLOAT_CONV_PRECISION, fracPart); + + // Remove trailing zeroes + int n2 = n - 1; + while (out[n2] == '0') { + out[n2--] = '\0'; + } + + n = n2; + } + + return n; +} + static bool hasIniParseError = false; static int iniParseErrorLine = 0; @@ -339,15 +426,35 @@ static int configIniHandler(void* user, const char* section, const char* name, c CHECK_PARSE_OPTION(parseKeyComboOption(&opt, value)); cfg->rosalinaMenuCombo = opt; return 1; + } else if (strcmp(name, "ntp_tz_offset_min") == 0) { + s64 opt; + CHECK_PARSE_OPTION(parseDecIntOption(&opt, value, -779, 899)); + cfg->ntpTzOffetMinutes = (s16)opt; + return 1; } else if (strcmp(name, "screen_filters_cct") == 0) { s64 opt; CHECK_PARSE_OPTION(parseDecIntOption(&opt, value, 1000, 25100)); cfg->screenFiltersCct = (u32)opt; return 1; - } else if (strcmp(name, "ntp_tz_offset_min") == 0) { + } else if (strcmp(name, "screen_filters_gamma") == 0) { s64 opt; - CHECK_PARSE_OPTION(parseDecIntOption(&opt, value, -779, 899)); - cfg->ntpTzOffetMinutes = (s16)opt; + CHECK_PARSE_OPTION(parseDecFloatOption(&opt, value, 0, 1411 * FLOAT_CONV_MULT)); + cfg->screenFiltersGammaEnc = opt; + return 1; + } else if (strcmp(name, "screen_filters_contrast") == 0) { + s64 opt; + CHECK_PARSE_OPTION(parseDecFloatOption(&opt, value, 0, 255 * FLOAT_CONV_MULT)); + cfg->screenFiltersContrastEnc = opt; + return 1; + } else if (strcmp(name, "screen_filters_brightness") == 0) { + s64 opt; + CHECK_PARSE_OPTION(parseDecFloatOption(&opt, value, -1 * FLOAT_CONV_MULT, 1 * FLOAT_CONV_MULT)); + cfg->screenFiltersBrightnessEnc = opt; + return 1; + } else if (strcmp(name, "screen_filters_invert") == 0) { + bool opt; + CHECK_PARSE_OPTION(parseBoolOption(&opt, value)); + cfg->screenFiltersInvert = opt; return 1; } else { @@ -423,6 +530,13 @@ static size_t saveLumaIniConfigToStr(char *out) static const int pinOptionToDigits[] = { 0, 4, 6, 8 }; int pinNumDigits = pinOptionToDigits[MULTICONFIG(PIN)]; + char screenFiltersGammaStr[32]; + char screenFiltersContrastStr[32]; + char screenFiltersBrightnessStr[32]; + encodedFloatToString(screenFiltersGammaStr, cfg->screenFiltersGammaEnc); + encodedFloatToString(screenFiltersContrastStr, cfg->screenFiltersContrastEnc); + encodedFloatToString(screenFiltersBrightnessStr, cfg->screenFiltersBrightnessEnc); + int n = sprintf( out, (const char *)config_template_ini, lumaVerStr, lumaRevSuffixStr, @@ -437,7 +551,11 @@ static size_t saveLumaIniConfigToStr(char *out) pinNumDigits, n3dsCpuStr, (int)MULTICONFIG(AUTOBOOTMODE), cfg->hbldr3dsxTitleId, rosalinaMenuComboStr, - (int)cfg->screenFiltersCct, (int)cfg->ntpTzOffetMinutes, + (int)cfg->ntpTzOffetMinutes, + + (int)cfg->screenFiltersCct, screenFiltersGammaStr, + screenFiltersContrastStr, screenFiltersBrightnessStr, + (int)cfg->screenFiltersInvert, cfg->autobootTwlTitleId, (int)cfg->autobootCtrAppmemtype, @@ -547,6 +665,8 @@ bool readConfig(void) configData.hbldr3dsxTitleId = HBLDR_DEFAULT_3DSX_TID; configData.rosalinaMenuCombo = 1u << 9 | 1u << 7 | 1u << 2; // L+Start+Select configData.screenFiltersCct = 6500; // default temp, no-op + configData.screenFiltersGammaEnc = 1 * FLOAT_CONV_MULT; // 1.0f + configData.screenFiltersContrastEnc = 1 * FLOAT_CONV_MULT; // 1.0f configData.autobootTwlTitleId = AUTOBOOT_DEFAULT_TWL_TID; ret = false; } diff --git a/arm9/source/config.h b/arm9/source/config.h index 23b4a04..7a7f162 100644 --- a/arm9/source/config.h +++ b/arm9/source/config.h @@ -36,7 +36,7 @@ #define CONFIG_FILE "config.bin" #define CONFIG_VERSIONMAJOR 3 -#define CONFIG_VERSIONMINOR 1 +#define CONFIG_VERSIONMINOR 2 #define BOOTCFG_NAND BOOTCONFIG(0, 7) #define BOOTCFG_FIRM BOOTCONFIG(3, 7) diff --git a/arm9/source/patches.c b/arm9/source/patches.c index 65916b8..93aad77 100644 --- a/arm9/source/patches.c +++ b/arm9/source/patches.c @@ -132,8 +132,12 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32 u32 splashDurationMsec; u64 hbldr3dsxTitleId; u32 rosalinaMenuCombo; - u16 screenFiltersCct; s16 ntpTzOffetMinutes; + u16 screenFiltersCct; + s64 screenFiltersGammaEnc; + s64 screenFiltersContrastEnc; + s64 screenFiltersBrightnessEnc; + bool screenFiltersInvert; u64 autobootTwlTitleId; u8 autobootCtrAppmemtype; } info; @@ -209,8 +213,12 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32 info->splashDurationMsec = configData.splashDurationMsec; info->hbldr3dsxTitleId = configData.hbldr3dsxTitleId; info->rosalinaMenuCombo = configData.rosalinaMenuCombo; - info->screenFiltersCct = configData.screenFiltersCct; info->ntpTzOffetMinutes = configData.ntpTzOffetMinutes; + info->screenFiltersCct = configData.screenFiltersCct; + info->screenFiltersGammaEnc = configData.screenFiltersGammaEnc; + info->screenFiltersContrastEnc = configData.screenFiltersContrastEnc; + info->screenFiltersBrightnessEnc = configData.screenFiltersBrightnessEnc; + info->screenFiltersInvert = configData.screenFiltersInvert; info->autobootTwlTitleId = configData.autobootTwlTitleId; info->autobootCtrAppmemtype = configData.autobootCtrAppmemtype; info->versionMajor = VERSION_MAJOR; diff --git a/arm9/source/types.h b/arm9/source/types.h index c6406b9..d1e7b97 100644 --- a/arm9/source/types.h +++ b/arm9/source/types.h @@ -69,8 +69,12 @@ typedef struct { u64 hbldr3dsxTitleId; u32 rosalinaMenuCombo; - u16 screenFiltersCct; s16 ntpTzOffetMinutes; + u16 screenFiltersCct; + s64 screenFiltersGammaEnc; + s64 screenFiltersContrastEnc; + s64 screenFiltersBrightnessEnc; + bool screenFiltersInvert; u64 autobootTwlTitleId; u8 autobootCtrAppmemtype; diff --git a/k11_extension/include/globals.h b/k11_extension/include/globals.h index 94e37a0..163a8cd 100644 --- a/k11_extension/include/globals.h +++ b/k11_extension/include/globals.h @@ -130,8 +130,12 @@ typedef struct CfwInfo u32 splashDurationMsec; u64 hbldr3dsxTitleId; u32 rosalinaMenuCombo; - u16 screenFiltersCct; s16 ntpTzOffetMinutes; + u16 screenFiltersCct; + s64 screenFiltersGammaEnc; + s64 screenFiltersContrastEnc; + s64 screenFiltersBrightnessEnc; + bool screenFiltersInvert; u64 autobootTwlTitleId; u8 autobootCtrAppmemtype; } CfwInfo; diff --git a/k11_extension/source/svc/GetSystemInfo.c b/k11_extension/source/svc/GetSystemInfo.c index 72f22ee..855caee 100644 --- a/k11_extension/source/svc/GetSystemInfo.c +++ b/k11_extension/source/svc/GetSystemInfo.c @@ -80,6 +80,18 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param) case 0x103: *out = (s64)cfwInfo.ntpTzOffetMinutes; break; + case 0x104: + *out = cfwInfo.screenFiltersGammaEnc; + break; + case 0x105: + *out = cfwInfo.screenFiltersContrastEnc; + break; + case 0x106: + *out = cfwInfo.screenFiltersBrightnessEnc; + break; + case 0x107: + *out = (s64)cfwInfo.screenFiltersInvert; + break; case 0x200: // isRelease *out = cfwInfo.flags & 1; diff --git a/sysmodules/rosalina/data/config_template.ini b/sysmodules/rosalina/data/config_template.ini index 23dcf63..3e7158b 100644 Binary files a/sysmodules/rosalina/data/config_template.ini and b/sysmodules/rosalina/data/config_template.ini differ diff --git a/sysmodules/rosalina/include/menu.h b/sysmodules/rosalina/include/menu.h index 8e5f0ae..b4d6dd6 100644 --- a/sysmodules/rosalina/include/menu.h +++ b/sysmodules/rosalina/include/menu.h @@ -33,13 +33,14 @@ #define HID_PAD (REG32(0x10146000) ^ 0xFFF) - #define DEFAULT_MENU_COMBO (KEY_L | KEY_DDOWN | KEY_SELECT) #define DIRECTIONAL_KEYS (KEY_DOWN | KEY_UP | KEY_LEFT | KEY_RIGHT) #define CORE_APPLICATION 0 #define CORE_SYSTEM 1 +#define FLOAT_CONV_MULT 1e8 // for screen filters + typedef enum MenuItemAction { MENU_END = 0, METHOD = 1, diff --git a/sysmodules/rosalina/include/menus/screen_filters.h b/sysmodules/rosalina/include/menus/screen_filters.h index 679af8f..eaba1ee 100644 --- a/sysmodules/rosalina/include/menus/screen_filters.h +++ b/sysmodules/rosalina/include/menus/screen_filters.h @@ -34,9 +34,9 @@ extern int screenFiltersCurrentTemperature; extern float screenFiltersCurrentGamma; extern float screenFiltersCurrentContrast; extern float screenFiltersCurrentBrightness; +extern bool screenFiltersCurrentInvert; -void ScreenFiltersMenu_SetCct(int cct); -void ScreenFiltersMenu_RestoreCct(void); +void ScreenFiltersMenu_RestoreSettings(void); void ScreenFiltersMenu_SetDefault(void); // 6500K (default) diff --git a/sysmodules/rosalina/source/main.c b/sysmodules/rosalina/source/main.c index eb54331..ebe6694 100644 --- a/sysmodules/rosalina/source/main.c +++ b/sysmodules/rosalina/source/main.c @@ -163,7 +163,7 @@ static void handleShellNotification(u32 notificationId) if (notificationId == 0x213) { // Shell opened // Note that this notification is fired on system init - ScreenFiltersMenu_RestoreCct(); + ScreenFiltersMenu_RestoreSettings(); menuShouldExit = false; } else { // Shell closed diff --git a/sysmodules/rosalina/source/menu.c b/sysmodules/rosalina/source/menu.c index 68638d3..d8c3f39 100644 --- a/sysmodules/rosalina/source/menu.c +++ b/sysmodules/rosalina/source/menu.c @@ -258,15 +258,32 @@ void menuThreadMain(void) while (!isServiceUsable("ac:u") || !isServiceUsable("hid:USER")) svcSleepThread(500 * 1000 * 1000LL); - s64 out; + s64 out = 0; + svcGetSystemInfo(&out, 0x10000, 0x102); screenFiltersCurrentTemperature = (int)(u32)out; if (screenFiltersCurrentTemperature < 1000 || screenFiltersCurrentTemperature > 25100) screenFiltersCurrentTemperature = 6500; - // Careful about race conditions here - if (screenFiltersCurrentTemperature != 6500) - ScreenFiltersMenu_SetCct(screenFiltersCurrentTemperature); + svcGetSystemInfo(&out, 0x10000, 0x104); + screenFiltersCurrentGamma = (float)(out / FLOAT_CONV_MULT); + if (screenFiltersCurrentGamma < 0.0f || screenFiltersCurrentGamma > 1411.0f) + screenFiltersCurrentGamma = 1.0f; + + svcGetSystemInfo(&out, 0x10000, 0x105); + screenFiltersCurrentContrast = (float)(out / FLOAT_CONV_MULT); + if (screenFiltersCurrentContrast < 0.0f || screenFiltersCurrentContrast > 255.0f) + screenFiltersCurrentContrast = 1.0f; + + svcGetSystemInfo(&out, 0x10000, 0x106); + screenFiltersCurrentBrightness = (float)(out / FLOAT_CONV_MULT); + if (screenFiltersCurrentBrightness < -1.0f || screenFiltersCurrentBrightness > 1.0f) + screenFiltersCurrentBrightness = 0.0f; + + svcGetSystemInfo(&out, 0x10000, 0x107); + screenFiltersCurrentInvert = (bool)out; + + ScreenFiltersMenu_RestoreSettings(); hidInit(); // assume this doesn't fail isHidInitialized = true; diff --git a/sysmodules/rosalina/source/menus/miscellaneous.c b/sysmodules/rosalina/source/menus/miscellaneous.c index 2c12907..7697779 100644 --- a/sysmodules/rosalina/source/menus/miscellaneous.c +++ b/sysmodules/rosalina/source/menus/miscellaneous.c @@ -98,8 +98,12 @@ typedef struct CfgData { u64 hbldr3dsxTitleId; u32 rosalinaMenuCombo; - u16 screenFiltersCct; s16 ntpTzOffetMinutes; + u16 screenFiltersCct; + float screenFiltersGamma; + float screenFiltersContrast; + float screenFiltersBrightness; + bool screenFiltersInvert; u64 autobootTwlTitleId; u8 autobootCtrAppmemtype; @@ -297,6 +301,13 @@ static size_t saveLumaIniConfigToStr(char *out, const CfgData *cfg) static const int pinOptionToDigits[] = { 0, 4, 6, 8 }; int pinNumDigits = pinOptionToDigits[MULTICONFIG(PIN)]; + char screenFiltersGammaStr[32]; + char screenFiltersContrastStr[32]; + char screenFiltersBrightnessStr[32]; + floatToString(screenFiltersGammaStr, cfg->screenFiltersGamma, 6, false); + floatToString(screenFiltersContrastStr, cfg->screenFiltersContrast, 6, false); + floatToString(screenFiltersBrightnessStr, cfg->screenFiltersBrightness, 6, false); + int n = sprintf( out, (const char *)config_template_ini, lumaVerStr, lumaRevSuffixStr, @@ -311,7 +322,11 @@ static size_t saveLumaIniConfigToStr(char *out, const CfgData *cfg) pinNumDigits, n3dsCpuStr, (int)MULTICONFIG(AUTOBOOTMODE), cfg->hbldr3dsxTitleId, rosalinaMenuComboStr, - (int)cfg->screenFiltersCct, (int)cfg->ntpTzOffetMinutes, + (int)cfg->ntpTzOffetMinutes, + + (int)cfg->screenFiltersCct, screenFiltersGammaStr, + screenFiltersContrastStr, screenFiltersBrightnessStr, + (int)cfg->screenFiltersInvert, cfg->autobootTwlTitleId, (int)cfg->autobootCtrAppmemtype, @@ -370,8 +385,12 @@ void MiscellaneousMenu_SaveSettings(void) configData.splashDurationMsec = splashDurationMsec; configData.hbldr3dsxTitleId = Luma_SharedConfig->hbldr_3dsx_tid; configData.rosalinaMenuCombo = menuCombo; - configData.screenFiltersCct = (u16)screenFiltersCurrentTemperature; configData.ntpTzOffetMinutes = (s16)lastNtpTzOffset; + configData.screenFiltersCct = (u16)screenFiltersCurrentTemperature; + configData.screenFiltersGamma = screenFiltersCurrentGamma; + configData.screenFiltersContrast = screenFiltersCurrentContrast; + configData.screenFiltersBrightness = screenFiltersCurrentBrightness; + configData.screenFiltersInvert = screenFiltersCurrentInvert; configData.autobootTwlTitleId = autobootTwlTitleId; configData.autobootCtrAppmemtype = autobootCtrAppmemtype; diff --git a/sysmodules/rosalina/source/menus/screen_filters.c b/sysmodules/rosalina/source/menus/screen_filters.c index 47ee859..a0a9376 100644 --- a/sysmodules/rosalina/source/menus/screen_filters.c +++ b/sysmodules/rosalina/source/menus/screen_filters.c @@ -81,7 +81,7 @@ static u8 ScreenFilterMenu_CalculatePolynomialColorLutComponent(const float coef level = powf(CLAMP(level, 0.0f, 1.0f), gamma); s32 levelInt = (s32)(255.0f * level + 0.5f); // round up - return (u8)CLAMP(levelInt, 0, 255); + return (u8)CLAMP(levelInt, 0, 255); // clamp again just to be sure } static void ScreenFilterMenu_WritePolynomialColorLut(const float coeffs[][3], bool invert, float gamma, u32 dim) @@ -121,7 +121,7 @@ static void ScreenFiltersMenu_ApplyColorSettings(void) ScreenFilterMenu_WritePolynomialColorLut(poly, inv, g, 1); } -void ScreenFiltersMenu_SetCct(int cct) +static void ScreenFiltersMenu_SetCct(int cct) { screenFiltersCurrentTemperature = cct; ScreenFiltersMenu_ApplyColorSettings(); @@ -151,7 +151,7 @@ void ScreenFiltersMenu_Set##name(void)\ ScreenFiltersMenu_SetCct(temp);\ } -void ScreenFiltersMenu_RestoreCct(void) +void ScreenFiltersMenu_RestoreSettings(void) { // Not initialized/default: return if (ScreenFiltersMenu_IsDefaultSettings()) diff --git a/sysmodules/rosalina/source/utils.c b/sysmodules/rosalina/source/utils.c index 5cd5232..fa42c48 100644 --- a/sysmodules/rosalina/source/utils.c +++ b/sysmodules/rosalina/source/utils.c @@ -175,7 +175,7 @@ int floatToString(char *out, float f, u32 precision, bool pad) return sprintf(out, "-inf"); static const u64 pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; - precision = precision >= 10 ? 10 : precision; + precision = precision >= 6 ? 6 : precision; // result inaccurate after 1e-6 u64 mult = pow10[precision]; double f2 = fabs((double)f) * mult + 0.5; @@ -188,5 +188,16 @@ int floatToString(char *out, float f, u32 precision, bool pad) if (pad) return sprintf(out, "%s%llu.%0*llu", sign, intPart, (int)precision, fracPart); else - return sprintf(out, "%s%llu.%llu", sign, intPart, fracPart); + { + int n = sprintf(out, "%s%llu", sign, intPart); + if (fracPart == 0) + return n; + + n += sprintf(out + n, ".%0*llu", (int)precision, fracPart); + + int n2 = n - 1; + while (out[n2] == '0') + out[n2--] = '\0'; + return n2; + } }