diff --git a/arm9/data/config_template.ini b/arm9/data/config_template.ini index 697b4fe..1dd2da7 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 bd7761f..ec5e039 100644 --- a/arm9/source/config.c +++ b/arm9/source/config.c @@ -65,6 +65,7 @@ static const char *singleOptionIniNamesBoot[] = { "app_syscore_threads_on_core_2", "show_system_settings_string", "show_gba_boot_screen", + "force_headphone_output", }; static const char *singleOptionIniNamesMisc[] = { @@ -581,7 +582,7 @@ static size_t saveLumaIniConfigToStr(char *out) (int)CONFIG(AUTOBOOTEMU), (int)CONFIG(USEEMUFIRM), (int)CONFIG(LOADEXTFIRMSANDMODULES), (int)CONFIG(PATCHGAMES), (int)CONFIG(REDIRECTAPPTHREADS), (int)CONFIG(PATCHVERSTRING), - (int)CONFIG(SHOWGBABOOT), + (int)CONFIG(SHOWGBABOOT), (int)CONFIG(FORCEHEADPHONEOUTPUT), 1 + (int)MULTICONFIG(DEFAULTEMU), 4 - (int)MULTICONFIG(BRIGHTNESS), splashPosStr, (unsigned int)cfg->splashDurationMsec, @@ -759,6 +760,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) "( ) Redirect app. syscore threads to core2", "( ) Show NAND or user string in System Settings", "( ) Show GBA boot screen in patched AGB_FIRM", + "( ) Force routing audio output to headphones" }; static const char *optionsDescription[] = { "Select the default EmuNAND.\n\n" @@ -830,8 +832,8 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) "of patched code binaries, exHeaders,\n" "IPS code patches and LayeredFS\n" "for specific games.\n\n" - "Also makes certain DLCs\n" - "for out-of-region games work.\n\n" + "Also makes certain DLCs for out-of-\n" + "region games work.\n\n" "Refer to the wiki for instructions.", "Redirect app. threads that would spawn\n" @@ -857,6 +859,15 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) "Enable showing the GBA boot screen\n" "when booting GBA games.", + + "Force audio output to headphones.\n\n" + "Currently only for NATIVE_FIRM.\n\n" + "Due to software limitations, this gets\n" + "undone if you actually insert then\n" + "remove HPs (just enter then exit sleep\n" + "mode if this happens).\n\n" + "Also gets bypassed for camera shutter\n" + "sound.", }; FirmwareSource nandType = FIRMWARE_SYSNAND; @@ -892,6 +903,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) { .visible = ISN3DS }, { .visible = true }, { .visible = true }, + { .visible = true }, }; //Calculate the amount of the various kinds of options and pre-select the first single one diff --git a/arm9/source/config.h b/arm9/source/config.h index 7e7dae0..00de4c1 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 4 +#define CONFIG_VERSIONMINOR 5 #define BOOTCFG_NAND BOOTCONFIG(0, 7) #define BOOTCFG_FIRM BOOTCONFIG(3, 7) @@ -62,6 +62,7 @@ enum singleOptions REDIRECTAPPTHREADS, PATCHVERSTRING, SHOWGBABOOT, + FORCEHEADPHONEOUTPUT, PATCHUNITINFO, DISABLEARM11EXCHANDLERS, ENABLESAFEFIRMROSALINA, diff --git a/k11_extension/include/config.h b/k11_extension/include/config.h index 07d12de..b367b38 100644 --- a/k11_extension/include/config.h +++ b/k11_extension/include/config.h @@ -33,6 +33,7 @@ enum singleOptions REDIRECTAPPTHREADS, PATCHVERSTRING, SHOWGBABOOT, + FORCEHEADPHONEOUTPUT, PATCHUNITINFO, DISABLEARM11EXCHANDLERS, ENABLESAFEFIRMROSALINA, diff --git a/sysmodules/loader/source/patcher.h b/sysmodules/loader/source/patcher.h index 86b32eb..9472431 100644 --- a/sysmodules/loader/source/patcher.h +++ b/sysmodules/loader/source/patcher.h @@ -36,6 +36,7 @@ enum singleOptions REDIRECTAPPTHREADS, PATCHVERSTRING, SHOWGBABOOT, + FORCEHEADPHONEOUTPUT, PATCHUNITINFO, DISABLEARM11EXCHANDLERS, ENABLESAFEFIRMROSALINA, diff --git a/sysmodules/pm/source/luma.h b/sysmodules/pm/source/luma.h index 54e6c9e..d73a610 100644 --- a/sysmodules/pm/source/luma.h +++ b/sysmodules/pm/source/luma.h @@ -30,6 +30,7 @@ enum singleOptions REDIRECTAPPTHREADS, PATCHVERSTRING, SHOWGBABOOT, + FORCEHEADPHONEOUTPUT, PATCHUNITINFO, DISABLEARM11EXCHANDLERS, ENABLESAFEFIRMROSALINA, diff --git a/sysmodules/rosalina/data/config_template.ini b/sysmodules/rosalina/data/config_template.ini index 697b4fe..1dd2da7 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/luma_config.h b/sysmodules/rosalina/include/luma_config.h index 81ea50a..2a5a44e 100644 --- a/sysmodules/rosalina/include/luma_config.h +++ b/sysmodules/rosalina/include/luma_config.h @@ -39,6 +39,7 @@ enum singleOptions REDIRECTAPPTHREADS, PATCHVERSTRING, SHOWGBABOOT, + FORCEHEADPHONEOUTPUT, PATCHUNITINFO, DISABLEARM11EXCHANDLERS, ENABLESAFEFIRMROSALINA, diff --git a/sysmodules/rosalina/include/shell.h b/sysmodules/rosalina/include/shell.h new file mode 100644 index 0000000..aa95dca --- /dev/null +++ b/sysmodules/rosalina/include/shell.h @@ -0,0 +1,29 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2023 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include <3ds/types.h> + +void handleShellOpened(void); diff --git a/sysmodules/rosalina/source/luma_config.c b/sysmodules/rosalina/source/luma_config.c index 1cba607..a2cb809 100644 --- a/sysmodules/rosalina/source/luma_config.c +++ b/sysmodules/rosalina/source/luma_config.c @@ -150,7 +150,7 @@ static size_t LumaConfig_SaveLumaIniConfigToStr(char *out, const CfgData *cfg) (int)CONFIG(AUTOBOOTEMU), (int)CONFIG(USEEMUFIRM), (int)CONFIG(LOADEXTFIRMSANDMODULES), (int)CONFIG(PATCHGAMES), (int)CONFIG(REDIRECTAPPTHREADS), (int)CONFIG(PATCHVERSTRING), - (int)CONFIG(SHOWGBABOOT), + (int)CONFIG(SHOWGBABOOT), (int)CONFIG(FORCEHEADPHONEOUTPUT), 1 + (int)MULTICONFIG(DEFAULTEMU), 4 - (int)MULTICONFIG(BRIGHTNESS), splashPosStr, (unsigned int)cfg->splashDurationMsec, diff --git a/sysmodules/rosalina/source/main.c b/sysmodules/rosalina/source/main.c index 6bd77cb..eb0bbef 100644 --- a/sysmodules/rosalina/source/main.c +++ b/sysmodules/rosalina/source/main.c @@ -40,6 +40,7 @@ #include "minisoc.h" #include "draw.h" #include "bootdiag.h" +#include "shell.h" #include "task_runner.h" @@ -165,13 +166,7 @@ static void handleShellNotification(u32 notificationId) // Note that this notification is also fired on system init. // Sequence goes like this: MCU fires notif. 0x200 on shell open // and shell close, then NS demuxes it and fires 0x213 and 0x214. - - // We need to check here if GSP has done its init stuff, in particular - // clock and reset, otherwise we'll cause core1 to be in a waitstate - // forever (if we access a GPU reg while the GPU block's clock is off). - // (GSP does its init before registering its services) - if (isServiceUsable("gsp::Gpu")) - ScreenFiltersMenu_RestoreSettings(); + handleShellOpened(); menuShouldExit = false; } else { // Shell closed diff --git a/sysmodules/rosalina/source/menu.c b/sysmodules/rosalina/source/menu.c index 9ebc4a2..f3aafe0 100644 --- a/sysmodules/rosalina/source/menu.c +++ b/sysmodules/rosalina/source/menu.c @@ -36,6 +36,7 @@ #include "menus/cheats.h" #include "minisoc.h" #include "menus/screen_filters.h" +#include "shell.h" u32 menuCombo = 0; bool isHidInitialized = false; @@ -261,10 +262,11 @@ void menuThreadMain(void) if(isN3DS) N3DSMenu_UpdateStatus(); - while (!isServiceUsable("ac:u") || !isServiceUsable("hid:USER") || !isServiceUsable("gsp::Gpu")) + while (!isServiceUsable("ac:u") || !isServiceUsable("hid:USER") || !isServiceUsable("gsp::Gpu") || !isServiceUsable("cdc:CHK")) svcSleepThread(250 * 1000 * 1000LL); ScreenFiltersMenu_LoadConfig(); + handleShellOpened(); hidInit(); // assume this doesn't fail isHidInitialized = true; diff --git a/sysmodules/rosalina/source/menus/screen_filters.c b/sysmodules/rosalina/source/menus/screen_filters.c index f80d984..20aa629 100644 --- a/sysmodules/rosalina/source/menus/screen_filters.c +++ b/sysmodules/rosalina/source/menus/screen_filters.c @@ -227,8 +227,6 @@ void ScreenFiltersMenu_LoadConfig(void) svcGetSystemInfo(&out, 0x10000, 0x10C); bottomScreenFilter.invert = (bool)out; - - ScreenFiltersMenu_RestoreSettings(); } DEF_CCT_SETTER(6500, Default) diff --git a/sysmodules/rosalina/source/shell.c b/sysmodules/rosalina/source/shell.c new file mode 100644 index 0000000..b04d702 --- /dev/null +++ b/sysmodules/rosalina/source/shell.c @@ -0,0 +1,82 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2023 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include <3ds.h> +#include "shell.h" +#include "utils.h" +#include "screen_filters.h" +#include "luma_config.h" + +static void forceHeadphoneOutput(void) +{ + // DSP/Codec sysmodule already have a way to force headphone output, + // but it's only for when the shell is closed (applied on shell close, + // cleared on shell opened); that mechanism is usually used by apps + // which have a "jukebox" feature (e.g Pokémon SMD). + + // This whole thing here is fragile and doesn't mesh well with the "codec" + // sysmodule. For example, inserting then removing HPs will undo what this + // function does. + + // TODO: stop opening and closing cdc:CHK (and mcu::HWC), which + // unecessarily spawns and despawns threads. + + // Wait for CSND to do its job + svcSleepThread(20 * 1000 * 1000LL); + + Handle *cdcChkHandlePtr = cdcChkGetSessionHandle(); + *cdcChkHandlePtr = 0; + + Result res = srvGetServiceHandle(cdcChkHandlePtr, "cdc:CHK"); + // Try to steal the handle if some other process is using the service (custom SVC) + if (R_FAILED(res)) + res = svcControlService(SERVICEOP_STEAL_CLIENT_SESSION, cdcChkHandlePtr, "cdc:CHK"); + + if (R_FAILED(res)) + return; + + u8 reg = 0x30; // Enable override selection (always set), then select HP. + res = CDCCHK_WriteRegisters2(100, 69, ®, 1); + + svcCloseHandle(*cdcChkHandlePtr); +} + +void handleShellOpened(void) +{ + s64 out = 0; + svcGetSystemInfo(&out, 0x10000, 3); + u32 config = (u32)out; + + // We need to check here if GSP has done its init stuff, in particular + // clock and reset, otherwise we'll cause core1 to be in a waitstate + // forever (if we access a GPU reg while the GPU block's clock is off). + // (GSP does its init before registering its services) + if (isServiceUsable("gsp::Gpu")) + ScreenFiltersMenu_RestoreSettings(); + + if ((config & BIT(FORCEHEADPHONEOUTPUT)) != 0 && isServiceUsable("cdc:CHK")) + forceHeadphoneOutput(); +}