Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb07a7334f | ||
|
|
748b771618 | ||
|
|
d6e72080d9 | ||
|
|
70109fed2c | ||
|
|
cf36d21daf | ||
|
|
781cd85b00 | ||
|
|
514537a983 | ||
|
|
184f4587fb | ||
|
|
e096aaabc4 | ||
|
|
ba26ae0f1c | ||
|
|
786adf0268 | ||
|
|
2af05220c2 | ||
|
|
362c4ffff1 | ||
|
|
95fd4e763b | ||
|
|
768e587b76 | ||
|
|
e3bb1c1b63 | ||
|
|
4c01bb453c | ||
|
|
dc67d438dc | ||
|
|
2d58ec4c86 | ||
|
|
555286ea47 | ||
|
|
b17eb66d55 | ||
|
|
9ca52054cf | ||
|
|
991f51831d | ||
|
|
e69f89a0d4 | ||
|
|
9411a8c186 | ||
|
|
dc4de4ce6f | ||
|
|
4e12453fff | ||
|
|
3a0418e279 | ||
|
|
1899bf377b | ||
|
|
0471002d4c | ||
|
|
704e08dc23 | ||
|
|
905837468c | ||
|
|
19d95782e1 | ||
|
|
adda19ecb2 | ||
|
|
b02d0346fd | ||
|
|
9097276a06 | ||
|
|
e99ab11c6f | ||
|
|
a564d8536a | ||
|
|
a21eee9207 | ||
|
|
71cddef78f | ||
|
|
9ae913064c | ||
|
|
a2313d1c03 | ||
|
|
22db3445a0 | ||
|
|
6417720d7d | ||
|
|
8b10906d90 | ||
|
|
0c55324d11 | ||
|
|
0b4fdc6e66 | ||
|
|
d3e62df769 | ||
|
|
04bd881cfa | ||
|
|
96799455cb | ||
|
|
814792eb91 | ||
|
|
2834bae318 | ||
|
|
037fae99d6 | ||
|
|
49c8888948 | ||
|
|
1875556f81 | ||
|
|
00850bf691 | ||
|
|
09fd199487 | ||
|
|
32c53578e0 | ||
|
|
0da90f61fc | ||
|
|
9942e8b299 | ||
|
|
daaeb97834 | ||
|
|
92da214066 | ||
|
|
0f05dd5c0a | ||
|
|
166bdbeb7d | ||
|
|
7dc2b7123b | ||
|
|
3d0ec9b785 | ||
|
|
85cfa5cba6 | ||
|
|
fdc1eaa16c | ||
|
|
d4dcf1a3e9 | ||
|
|
43fd137d55 | ||
|
|
6931eadc34 | ||
|
|
3143e7e1d0 | ||
|
|
d03396d272 | ||
|
|
c8aa2e8a89 | ||
|
|
c7a3a0278c | ||
|
|
5924f60d06 | ||
|
|
cd68b66c03 | ||
|
|
44cd3928fb | ||
|
|
8c54613e44 | ||
|
|
7dfa83b8c0 | ||
|
|
b551061264 | ||
|
|
3e228c33c9 | ||
|
|
2b23be8f44 |
19
.github/ISSUE_TEMPLATE/bug-report.md
vendored
19
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -7,13 +7,13 @@ about: Use this to report bugs you encounter with Luma3DS. Make sure you upload
|
|||||||
-- THIS IS NOT A SUPPORT FORUM! For support go here:
|
-- THIS IS NOT A SUPPORT FORUM! For support go here:
|
||||||
-- Nintendo Homebrew: https://discord.gg/MjzatM8
|
-- Nintendo Homebrew: https://discord.gg/MjzatM8
|
||||||
--
|
--
|
||||||
-- Rosalina feature requests go here: https://github.com/AuroraWright/Luma3DS/issues/752
|
-- Rosalina feature requests go here: https://github.com/LumaTeam/Luma3DS/issues/752
|
||||||
--
|
--
|
||||||
-- Also check the Wiki (https://github.com/AuroraWright/Luma3DS/wiki) before making an issue.
|
-- Also check the Wiki (https://github.com/LumaTeam/Luma3DS/wiki) before making an issue.
|
||||||
--
|
--
|
||||||
-- For GBA/DSiWare/DS/AGB_FIRM/TWL_FIRM problems: https://3ds.hacks.guide/troubleshooting
|
-- For GBA/DSiWare/DS/AGB_FIRM/TWL_FIRM problems: use https://github.com/MechanicalDragon0687/TWLFix-CFW and update your system.
|
||||||
-- If you're using an emu/redNAND try installing anything on it to sysNAND.
|
-- If you're using an emu/redNAND try installing anything on it to sysNAND.
|
||||||
-- Please make sure to read "Enable game patching" https://github.com/AuroraWright/Luma3DS/wiki/Options-and-usage before posting any issues about the "Enable game patching" option(s).
|
-- Please make sure to read "Enable game patching" https://github.com/LumaTeam/Luma3DS/wiki/Options-and-usage before posting any issues about the "Enable game patching" option(s).
|
||||||
--
|
--
|
||||||
-- Luma updaters that don't support Boot9Strap/Sighax won't work.
|
-- Luma updaters that don't support Boot9Strap/Sighax won't work.
|
||||||
-- This is due to support for non-B9S/Sighax entrypoints being dropped.
|
-- This is due to support for non-B9S/Sighax entrypoints being dropped.
|
||||||
@@ -21,7 +21,7 @@ about: Use this to report bugs you encounter with Luma3DS. Make sure you upload
|
|||||||
-- Please fill in the placeholders.-->
|
-- Please fill in the placeholders.-->
|
||||||
**System model:**
|
**System model:**
|
||||||
|
|
||||||
[e.g. 2DS, New 3DS, Old 3DS]
|
[New 2DS XL, New 3DS XL, New 3DS, Old 2DS, Old 3DS XL, Old 3DS]
|
||||||
|
|
||||||
**SysNAND version (+emu/redNAND version if applicable):**
|
**SysNAND version (+emu/redNAND version if applicable):**
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ about: Use this to report bugs you encounter with Luma3DS. Make sure you upload
|
|||||||
|
|
||||||
**Luma3DS version:**
|
**Luma3DS version:**
|
||||||
|
|
||||||
[e.g. v10.1.1 stable or if using nightly/hourly specify the commit like this https://github.com/AuroraWright/Luma3DS/commit/0543c208fd154e6326ea5da8cbf66ffcbdef010c]
|
[e.g. v10.2 stable or if using non-releases specify the commit like this https://github.com/LumaTeam/Luma3DS/commit/0543c208fd154e6326ea5da8cbf66ffcbdef010c]
|
||||||
|
|
||||||
**Luma3DS configuration/options:**
|
**Luma3DS configuration/options:**
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ Splash duration: ( )
|
|||||||
PIN lock: ( )
|
PIN lock: ( )
|
||||||
|
|
||||||
New 3DS CPU: ( )
|
New 3DS CPU: ( )
|
||||||
<!--This option is only available for New 3DS/2DS.-->
|
<!--This option is only available on New 3DS (XL)/New 2DS XL.-->
|
||||||
|
|
||||||
--
|
--
|
||||||
|
|
||||||
@@ -70,12 +70,13 @@ Show NAND or user string in System Settings: ( )
|
|||||||
|
|
||||||
Show GBA boot screen in patched AGB_FIRM: ( )
|
Show GBA boot screen in patched AGB_FIRM: ( )
|
||||||
|
|
||||||
Patch Arm9 access: ( )
|
|
||||||
|
|
||||||
Set developer UNITINFO: ( )
|
Set developer UNITINFO: ( )
|
||||||
|
|
||||||
Disable Arm11 exception handlers: ( )
|
Disable Arm11 exception handlers: ( )
|
||||||
|
|
||||||
|
Enable Rosalina on SAFE_FIRM: ( )
|
||||||
|
<!--This option is only available on New 3DS (XL)/New 2DS XL.-->
|
||||||
|
|
||||||
--
|
--
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -13,8 +13,11 @@ exceptions/arm11/build
|
|||||||
*.d
|
*.d
|
||||||
*.elf
|
*.elf
|
||||||
*.cxi
|
*.cxi
|
||||||
|
*.3dsx
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.dmp
|
*.dmp
|
||||||
.project
|
.project
|
||||||
.cproject
|
.cproject
|
||||||
.settings
|
.settings
|
||||||
|
|
||||||
|
Luma3DS*.zip
|
||||||
|
|||||||
9
Makefile
9
Makefile
@@ -15,9 +15,10 @@ release: $(NAME)$(REVISION).zip
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
@$(foreach dir, $(SUBFOLDERS), $(MAKE) -C $(dir) clean &&) true
|
@$(foreach dir, $(SUBFOLDERS), $(MAKE) -C $(dir) clean &&) true
|
||||||
@rm -rf *.firm *.zip
|
@rm -rf *.firm *.zip *.3dsx
|
||||||
|
|
||||||
$(NAME)$(REVISION).zip: boot.firm exception_dump_parser
|
# boot.3dsx comes from https://github.com/fincs/new-hbmenu/releases
|
||||||
|
$(NAME)$(REVISION).zip: boot.firm boot.3dsx
|
||||||
@zip -r $@ $^ -x "*.DS_Store*" "*__MACOSX*"
|
@zip -r $@ $^ -x "*.DS_Store*" "*__MACOSX*"
|
||||||
|
|
||||||
boot.firm: $(SUBFOLDERS)
|
boot.firm: $(SUBFOLDERS)
|
||||||
@@ -25,5 +26,9 @@ boot.firm: $(SUBFOLDERS)
|
|||||||
-A 0x18180000 -C XDMA XDMA NDMA XDMA
|
-A 0x18180000 -C XDMA XDMA NDMA XDMA
|
||||||
@echo built... $(notdir $@)
|
@echo built... $(notdir $@)
|
||||||
|
|
||||||
|
boot.3dsx:
|
||||||
|
@curl -sSL "https://github.com/fincs/new-hbmenu/releases/latest/download/boot.3dsx" -o "$@"
|
||||||
|
@echo downloaded... $(notdir $@)
|
||||||
|
|
||||||
$(SUBFOLDERS):
|
$(SUBFOLDERS):
|
||||||
@$(MAKE) -C $@ all
|
@$(MAKE) -C $@ all
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -5,9 +5,9 @@
|
|||||||
**Luma3DS** is a program to patch the system software of (New) Nintendo (2)3DS handheld consoles "on the fly", adding features such as per-game language settings, debugging capabilities for developers, and removing restrictions enforced by Nintendo such as the region lock.
|
**Luma3DS** is a program to patch the system software of (New) Nintendo (2)3DS handheld consoles "on the fly", adding features such as per-game language settings, debugging capabilities for developers, and removing restrictions enforced by Nintendo such as the region lock.
|
||||||
|
|
||||||
It also allows you to run unauthorized ("homebrew") content by removing signature checks.
|
It also allows you to run unauthorized ("homebrew") content by removing signature checks.
|
||||||
To use it, you will need a console capable of running homebrew software on the Arm9 processor. We recommend [Plailect's guide](https://3ds.hacks.guide/) for details on how to get your system ready.
|
To use it, you will need a console capable of running homebrew software on the Arm9 processor.
|
||||||
|
|
||||||
Since v8.0, Luma3DS has its own in-game menu, triggerable by <kbd>L+Down+Select</kbd> (see the [release notes](https://github.com/AuroraWright/Luma3DS/releases/tag/v8.0)).
|
Since v8.0, Luma3DS has its own in-game menu, triggerable by <kbd>L+Down+Select</kbd> (see the [release notes](https://github.com/LumaTeam/Luma3DS/releases/tag/v8.0)).
|
||||||
|
|
||||||
#
|
#
|
||||||
### Compiling
|
### Compiling
|
||||||
@@ -16,18 +16,18 @@ Since v8.0, Luma3DS has its own in-game menu, triggerable by <kbd>L+Down+Select<
|
|||||||
2. [makerom](https://github.com/jakcron/Project_CTR) in PATH
|
2. [makerom](https://github.com/jakcron/Project_CTR) in PATH
|
||||||
3. [firmtool](https://github.com/TuxSH/firmtool)
|
3. [firmtool](https://github.com/TuxSH/firmtool)
|
||||||
4. Up-to-date devkitARM+libctru
|
4. Up-to-date devkitARM+libctru
|
||||||
1. Clone the repository with `git clone https://github.com/AuroraWright/Luma3DS.git`
|
1. Clone the repository with `git clone https://github.com/LumaTeam/Luma3DS.git`
|
||||||
2. Run `make`.
|
2. Run `make`.
|
||||||
|
|
||||||
The produced `boot.firm` is meant to be copied to the root of your SD card for usage with Boot9Strap.
|
The produced `boot.firm` is meant to be copied to the root of your SD card for usage with Boot9Strap.
|
||||||
|
|
||||||
#
|
#
|
||||||
### Setup / Usage / Features
|
### Setup / Usage / Features
|
||||||
See https://github.com/AuroraWright/Luma3DS/wiki
|
See https://github.com/LumaTeam/Luma3DS/wiki
|
||||||
|
|
||||||
#
|
#
|
||||||
### Credits
|
### Credits
|
||||||
See https://github.com/AuroraWright/Luma3DS/wiki/Credits
|
See https://github.com/LumaTeam/Luma3DS/wiki/Credits
|
||||||
|
|
||||||
#
|
#
|
||||||
### Licensing
|
### Licensing
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
|||||||
"( ) Show GBA boot screen in patched AGB_FIRM",
|
"( ) Show GBA boot screen in patched AGB_FIRM",
|
||||||
"( ) Set developer UNITINFO",
|
"( ) Set developer UNITINFO",
|
||||||
"( ) Disable Arm11 exception handlers",
|
"( ) Disable Arm11 exception handlers",
|
||||||
|
"( ) Enable Rosalina on SAFE_FIRM",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *optionsDescription[] = { "Select the default EmuNAND.\n\n"
|
static const char *optionsDescription[] = { "Select the default EmuNAND.\n\n"
|
||||||
@@ -193,7 +194,16 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
|||||||
"Note: Disabling the exception handlers\n"
|
"Note: Disabling the exception handlers\n"
|
||||||
"will disqualify you from submitting\n"
|
"will disqualify you from submitting\n"
|
||||||
"issues or bug reports to the Luma3DS\n"
|
"issues or bug reports to the Luma3DS\n"
|
||||||
"GitHub repository!"
|
"GitHub repository!",
|
||||||
|
|
||||||
|
"Enables Rosalina, the kernel ext.\n"
|
||||||
|
"and sysmodule reimplementations on\n"
|
||||||
|
"SAFE_FIRM (New 3DS only).\n\n"
|
||||||
|
"Also suppresses QTM error 0xF96183FE,\n"
|
||||||
|
"allowing to use 8.1-11.3 N3DS on\n"
|
||||||
|
"New 2DS XL consoles.\n\n"
|
||||||
|
"Only select this if you know what you\n"
|
||||||
|
"are doing!",
|
||||||
};
|
};
|
||||||
|
|
||||||
FirmwareSource nandType = FIRMWARE_SYSNAND;
|
FirmwareSource nandType = FIRMWARE_SYSNAND;
|
||||||
@@ -229,7 +239,8 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
|||||||
{ .visible = true },
|
{ .visible = true },
|
||||||
{ .visible = true },
|
{ .visible = true },
|
||||||
{ .visible = true },
|
{ .visible = true },
|
||||||
{ .visible = true }
|
{ .visible = true },
|
||||||
|
{ .visible = ISN3DS },
|
||||||
};
|
};
|
||||||
|
|
||||||
//Calculate the amount of the various kinds of options and pre-select the first single one
|
//Calculate the amount of the various kinds of options and pre-select the first single one
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
#define CONFIG_FILE "config.bin"
|
#define CONFIG_FILE "config.bin"
|
||||||
#define CONFIG_VERSIONMAJOR 2
|
#define CONFIG_VERSIONMAJOR 2
|
||||||
#define CONFIG_VERSIONMINOR 3
|
#define CONFIG_VERSIONMINOR 4
|
||||||
|
|
||||||
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
|
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
|
||||||
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
|
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
|
||||||
@@ -60,7 +60,8 @@ enum singleOptions
|
|||||||
PATCHVERSTRING,
|
PATCHVERSTRING,
|
||||||
SHOWGBABOOT,
|
SHOWGBABOOT,
|
||||||
PATCHUNITINFO,
|
PATCHUNITINFO,
|
||||||
DISABLEARM11EXCHANDLERS
|
DISABLEARM11EXCHANDLERS,
|
||||||
|
ENABLESAFEFIRMROSALINA,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum ConfigurationStatus
|
typedef enum ConfigurationStatus
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
#include "buttons.h"
|
#include "buttons.h"
|
||||||
#include "arm9_exception_handlers.h"
|
#include "arm9_exception_handlers.h"
|
||||||
|
|
||||||
|
// See https://github.com/LumaTeam/luma3ds_exception_dump_parser
|
||||||
|
|
||||||
void installArm9Handlers(void)
|
void installArm9Handlers(void)
|
||||||
{
|
{
|
||||||
vu32 *dstVeneers = (vu32 *)0x08000000;
|
vu32 *dstVeneers = (vu32 *)0x08000000;
|
||||||
|
|||||||
@@ -278,7 +278,8 @@ static inline void mergeSection0(FirmwareType firmType, u32 firmVersion, bool lo
|
|||||||
srcModuleSize = moduleList[nbModules].size = ((Cxi *)src)->ncch.contentSize * 0x200;
|
srcModuleSize = moduleList[nbModules].size = ((Cxi *)src)->ncch.contentSize * 0x200;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(firmType == NATIVE_FIRM && (ISN3DS || firmVersion >= 0x1D))
|
// SAFE_FIRM only for N3DS and only if ENABLESAFEFIRMROSALINA is on
|
||||||
|
if((firmType == NATIVE_FIRM || firmType == SAFE_FIRM) && (ISN3DS || firmVersion >= 0x1D))
|
||||||
{
|
{
|
||||||
//2) Merge that info with our own modules'
|
//2) Merge that info with our own modules'
|
||||||
for(u8 *src = (u8 *)0x18180000; memcmp(((Cxi *)src)->ncch.magic, "NCCH", 4) == 0; src += srcModuleSize)
|
for(u8 *src = (u8 *)0x18180000; memcmp(((Cxi *)src)->ncch.magic, "NCCH", 4) == 0; src += srcModuleSize)
|
||||||
@@ -303,7 +304,9 @@ static inline void mergeSection0(FirmwareType firmType, u32 firmVersion, bool lo
|
|||||||
//3) Read or copy the modules
|
//3) Read or copy the modules
|
||||||
u8 *dst = firm->section[0].address;
|
u8 *dst = firm->section[0].address;
|
||||||
const char *extModuleSizeError = "The external FIRM modules are too large.";
|
const char *extModuleSizeError = "The external FIRM modules are too large.";
|
||||||
for(u32 i = 0, dstModuleSize, maxModuleSize = firmType == NATIVE_FIRM ? 0x80000 : 0x600000; i < nbModules; i++, dst += dstModuleSize, maxModuleSize -= dstModuleSize)
|
// SAFE_FIRM only for N3DS and only if ENABLESAFEFIRMROSALINA is on
|
||||||
|
u32 maxModuleSize = (firmType == NATIVE_FIRM || firmType == SAFE_FIRM) ? 0x80000 : 0x600000;
|
||||||
|
for(u32 i = 0, dstModuleSize; i < nbModules; i++, dst += dstModuleSize, maxModuleSize -= dstModuleSize)
|
||||||
{
|
{
|
||||||
if(loadFromStorage)
|
if(loadFromStorage)
|
||||||
{
|
{
|
||||||
@@ -335,7 +338,7 @@ static inline void mergeSection0(FirmwareType firmType, u32 firmVersion, bool lo
|
|||||||
memcpy(dst, moduleList[i].src, dstModuleSize);
|
memcpy(dst, moduleList[i].src, dstModuleSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
//4) Patch NATIVE_FIRM if necessary
|
//4) Patch NATIVE_FIRM/SAFE_FIRM (N3DS) if necessary
|
||||||
if(nbModules == 6)
|
if(nbModules == 6)
|
||||||
{
|
{
|
||||||
if(patchK11ModuleLoading(firm->section[0].size, dst - firm->section[0].address, (u8 *)firm + firm->section[1].offset, firm->section[1].size) != 0)
|
if(patchK11ModuleLoading(firm->section[0].size, dst - firm->section[0].address, (u8 *)firm + firm->section[1].offset, firm->section[1].size) != 0)
|
||||||
@@ -522,6 +525,32 @@ u32 patch1x2xNativeAndSafeFirm(void)
|
|||||||
ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size);
|
ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size);
|
||||||
ret += patchSvcBreak9(arm9Section, kernel9Size, (u32)firm->section[2].address);
|
ret += patchSvcBreak9(arm9Section, kernel9Size, (u32)firm->section[2].address);
|
||||||
|
|
||||||
|
if(ISN3DS && CONFIG(ENABLESAFEFIRMROSALINA))
|
||||||
|
{
|
||||||
|
u8 *arm11Section1 = (u8 *)firm + firm->section[1].offset;
|
||||||
|
//Find the Kernel11 SVC table and handler, exceptions page and free space locations
|
||||||
|
u32 baseK11VA;
|
||||||
|
u8 *freeK11Space;
|
||||||
|
u32 *arm11SvcHandler,
|
||||||
|
*arm11ExceptionsPage,
|
||||||
|
*arm11SvcTable = getKernel11Info(arm11Section1, firm->section[1].size, &baseK11VA, &freeK11Space, &arm11SvcHandler, &arm11ExceptionsPage);
|
||||||
|
|
||||||
|
ret += installK11Extension(arm11Section1, firm->section[1].size, false, baseK11VA, arm11ExceptionsPage, &freeK11Space);
|
||||||
|
ret += patchKernel11(arm11Section1, firm->section[1].size, baseK11VA, arm11SvcTable, arm11ExceptionsPage);
|
||||||
|
|
||||||
|
// Add some other patches to the mix, as we can now launch homebrew on SAFE_FIRM:
|
||||||
|
|
||||||
|
//Apply firmlaunch patches
|
||||||
|
//Or don't, this makes usm not work
|
||||||
|
//ret += patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
|
||||||
|
|
||||||
|
ret += patchKernel9Panic(arm9Section, kernel9Size);
|
||||||
|
ret += patchP9AccessChecks(process9Offset, process9Size);
|
||||||
|
|
||||||
|
mergeSection0(NATIVE_FIRM, 0x45, false); // may change in the future
|
||||||
|
firm->section[0].size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,8 @@
|
|||||||
#include "arm9_exception_handlers.h"
|
#include "arm9_exception_handlers.h"
|
||||||
#include "large_patches.h"
|
#include "large_patches.h"
|
||||||
|
|
||||||
|
#define K11EXT_VA 0x70000000
|
||||||
|
|
||||||
u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
||||||
{
|
{
|
||||||
u8 *temp = memsearch(pos, "NCCH", size, 4);
|
u8 *temp = memsearch(pos, "NCCH", size, 4);
|
||||||
@@ -108,9 +110,12 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
|
|||||||
struct KExtParameters
|
struct KExtParameters
|
||||||
{
|
{
|
||||||
u32 basePA;
|
u32 basePA;
|
||||||
|
u32 stolenSystemMemRegionSize;
|
||||||
void *originalHandlers[4];
|
void *originalHandlers[4];
|
||||||
u32 L1MMUTableAddrs[4];
|
u32 L1MMUTableAddrs[4];
|
||||||
|
|
||||||
|
volatile bool done;
|
||||||
|
|
||||||
struct CfwInfo
|
struct CfwInfo
|
||||||
{
|
{
|
||||||
char magic[4];
|
char magic[4];
|
||||||
@@ -134,8 +139,9 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
|
|||||||
static const u8 patternHook3_4[] = {0x00, 0x00, 0xA0, 0xE1, 0x03, 0xF0, 0x20, 0xE3, 0xFD, 0xFF, 0xFF, 0xEA}; //SGI0 setup code, etc.
|
static const u8 patternHook3_4[] = {0x00, 0x00, 0xA0, 0xE1, 0x03, 0xF0, 0x20, 0xE3, 0xFD, 0xFF, 0xFF, 0xEA}; //SGI0 setup code, etc.
|
||||||
|
|
||||||
//Our kernel11 extension is initially loaded in VRAM
|
//Our kernel11 extension is initially loaded in VRAM
|
||||||
u32 kextTotalSize = *(u32 *)0x18000020 - 0x40000000;
|
u32 kextTotalSize = *(u32 *)0x18000020 - K11EXT_VA;
|
||||||
u32 dstKextPA = (ISN3DS ? 0x2E000000 : 0x26C00000) - kextTotalSize;
|
u32 stolenSystemMemRegionSize = kextTotalSize; // no need to steal any more mem on N3DS. Currently, everything fits in BASE on O3DS too (?)
|
||||||
|
u32 dstKextPA = (ISN3DS ? 0x2E000000 : 0x26C00000) - stolenSystemMemRegionSize; // start of BASE memregion (note: linear heap ---> <--- the rest)
|
||||||
|
|
||||||
u32 *hookVeneers = (u32 *)*freeK11Space;
|
u32 *hookVeneers = (u32 *)*freeK11Space;
|
||||||
u32 relocBase = 0xFFFF0000 + (*freeK11Space - (u8 *)arm11ExceptionsPage);
|
u32 relocBase = 0xFFFF0000 + (*freeK11Space - (u8 *)arm11ExceptionsPage);
|
||||||
@@ -143,11 +149,11 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
|
|||||||
hookVeneers[0] = 0xE51FF004; //ldr pc, [pc, #-8+4]
|
hookVeneers[0] = 0xE51FF004; //ldr pc, [pc, #-8+4]
|
||||||
hookVeneers[1] = 0x18000004;
|
hookVeneers[1] = 0x18000004;
|
||||||
hookVeneers[2] = 0xE51FF004;
|
hookVeneers[2] = 0xE51FF004;
|
||||||
hookVeneers[3] = 0x40000000;
|
hookVeneers[3] = K11EXT_VA;
|
||||||
hookVeneers[4] = 0xE51FF004;
|
hookVeneers[4] = 0xE51FF004;
|
||||||
hookVeneers[5] = 0x40000008;
|
hookVeneers[5] = K11EXT_VA + 8;
|
||||||
hookVeneers[6] = 0xE51FF004;
|
hookVeneers[6] = 0xE51FF004;
|
||||||
hookVeneers[7] = 0x4000000C;
|
hookVeneers[7] = K11EXT_VA + 0xC;
|
||||||
|
|
||||||
(*freeK11Space) += 32;
|
(*freeK11Space) += 32;
|
||||||
|
|
||||||
@@ -175,14 +181,16 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
|
|||||||
off += 4;
|
off += 4;
|
||||||
*off = MAKE_BRANCH_LINK(baseK11VA + ((u8 *)off - pos), relocBase + 24);
|
*off = MAKE_BRANCH_LINK(baseK11VA + ((u8 *)off - pos), relocBase + 24);
|
||||||
|
|
||||||
struct KExtParameters *p = (struct KExtParameters *)(*(u32 *)0x18000024 - 0x40000000 + 0x18000000);
|
struct KExtParameters *p = (struct KExtParameters *)(*(u32 *)0x18000024 - K11EXT_VA + 0x18000000);
|
||||||
p->basePA = dstKextPA;
|
p->basePA = dstKextPA;
|
||||||
|
p->done = false;
|
||||||
|
p->stolenSystemMemRegionSize = stolenSystemMemRegionSize;
|
||||||
|
|
||||||
for(u32 i = 0; i < 4; i++)
|
for(u32 i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
u32 *handlerPos = getKernel11HandlerVAPos(pos, arm11ExceptionsPage, baseK11VA, 1 + i);
|
u32 *handlerPos = getKernel11HandlerVAPos(pos, arm11ExceptionsPage, baseK11VA, 1 + i);
|
||||||
p->originalHandlers[i] = (void *)*handlerPos;
|
p->originalHandlers[i] = (void *)*handlerPos;
|
||||||
*handlerPos = 0x40000010 + 4 * i;
|
*handlerPos = K11EXT_VA + 0x10 + 4 * i;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CfwInfo *info = &p->info;
|
struct CfwInfo *info = &p->info;
|
||||||
@@ -248,14 +256,14 @@ u32 patchKernel11(u8 *pos, u32 size, u32 baseK11VA, u32 *arm11SvcTable, u32 *arm
|
|||||||
|
|
||||||
//Redirect enableUserExceptionHandlersForCPUExc (= true)
|
//Redirect enableUserExceptionHandlersForCPUExc (= true)
|
||||||
for(off = arm11ExceptionsPage; *off != 0x96007F9; off++);
|
for(off = arm11ExceptionsPage; *off != 0x96007F9; off++);
|
||||||
off[1] = 0x40000028;
|
off[1] = K11EXT_VA + 0x28;
|
||||||
|
|
||||||
off = (u32 *)memsearch(pos, patternKThreadDebugReschedule, size, sizeof(patternKThreadDebugReschedule));
|
off = (u32 *)memsearch(pos, patternKThreadDebugReschedule, size, sizeof(patternKThreadDebugReschedule));
|
||||||
if(off == NULL)
|
if(off == NULL)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
off[-5] = 0xE51FF004;
|
off[-5] = 0xE51FF004;
|
||||||
off[-4] = 0x4000002C;
|
off[-4] = K11EXT_VA + 0x2C;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -434,7 +442,7 @@ u32 patchK11ModuleLoading(u32 section0size, u32 modulesSize, u8 *pos, u32 size)
|
|||||||
off32 += 2;
|
off32 += 2;
|
||||||
off32[1] = off32[0] + modulesSize;
|
off32[1] = off32[0] + modulesSize;
|
||||||
for(; *off32 != section0size; off32++);
|
for(; *off32 != section0size; off32++);
|
||||||
*off32 += ((modulesSize + 0x1FF) >> 9) << 9;
|
*off32 = ((modulesSize + 0x1FF) >> 9) << 9;
|
||||||
|
|
||||||
off = memsearch(pos, modulePidPattern, size, 4);
|
off = memsearch(pos, modulePidPattern, size, 4);
|
||||||
|
|
||||||
|
|||||||
@@ -1,188 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# Requires Python >= 3.2 or >= 2.7
|
|
||||||
|
|
||||||
# This file is part of Luma3DS
|
|
||||||
# Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
# Additional Terms 7.b of GPLv3 applies 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.
|
|
||||||
|
|
||||||
__author__ = "TuxSH"
|
|
||||||
__copyright__ = "Copyright (c) 2016-2020 TuxSH"
|
|
||||||
__license__ = "GPLv3"
|
|
||||||
__version__ = "v1.2"
|
|
||||||
|
|
||||||
"""
|
|
||||||
Parses Luma3DS exception dumps
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
from struct import unpack_from
|
|
||||||
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
# Source of hexdump: https://gist.github.com/ImmortalPC/c340564823f283fe530b
|
|
||||||
# Credits for hexdump go to the original authors
|
|
||||||
# Slightly edited by TuxSH
|
|
||||||
|
|
||||||
def hexdump(addr, src, length=16, sep='.' ):
|
|
||||||
'''
|
|
||||||
@brief Return {src} in hex dump.
|
|
||||||
@param[in] length {Int} Nb Bytes by row.
|
|
||||||
@param[in] sep {Char} For the text part, {sep} will be used for non ASCII char.
|
|
||||||
@return {Str} The hexdump
|
|
||||||
@note Full support for python2 and python3 !
|
|
||||||
'''
|
|
||||||
result = []
|
|
||||||
|
|
||||||
# Python3 support
|
|
||||||
try:
|
|
||||||
xrange(0,1)
|
|
||||||
except NameError:
|
|
||||||
xrange = range
|
|
||||||
|
|
||||||
for i in xrange(0, len(src), length):
|
|
||||||
subSrc = src[i:i+length]
|
|
||||||
hexa = ''
|
|
||||||
isMiddle = False
|
|
||||||
for h in xrange(0,len(subSrc)):
|
|
||||||
if h == length/2:
|
|
||||||
hexa += ' '
|
|
||||||
h = subSrc[h]
|
|
||||||
if not isinstance(h, int):
|
|
||||||
h = ord(h)
|
|
||||||
h = hex(h).replace('0x','')
|
|
||||||
if len(h) == 1:
|
|
||||||
h = '0'+h
|
|
||||||
hexa += h+' '
|
|
||||||
hexa = hexa.strip(' ')
|
|
||||||
text = ''
|
|
||||||
for c in subSrc:
|
|
||||||
if not isinstance(c, int):
|
|
||||||
c = ord(c)
|
|
||||||
if 0x20 <= c < 0x7F:
|
|
||||||
text += chr(c)
|
|
||||||
else:
|
|
||||||
text += sep
|
|
||||||
result.append(('%08x: %-'+str(length*(2+1)+1)+'s |%s|') % (addr + i, hexa, text))
|
|
||||||
|
|
||||||
return '\n'.join(result)
|
|
||||||
|
|
||||||
|
|
||||||
def makeRegisterLine(A, rA, B, rB):
|
|
||||||
return "{0:<15}{1:<20}{2:<15}{3:<20}".format(A, "{0:08x}".format(rA), B, "{0:08x}".format(rB))
|
|
||||||
|
|
||||||
handledExceptionNames = ("FIQ", "undefined instruction", "prefetch abort", "data abort")
|
|
||||||
registerNames = tuple("r{0}".format(i) for i in range(13)) + ("sp", "lr", "pc", "cpsr") + ("dfsr", "ifsr", "far") + ("fpexc", "fpinst", "fpinst2")
|
|
||||||
svcBreakReasons = ("(svcBreak: panic)", "(svcBreak: assertion failed)", "(svcBreak: user-related)")
|
|
||||||
faultStatusSources = {
|
|
||||||
0b1:'Alignment', 0b100:'Instruction cache maintenance operation fault',
|
|
||||||
0b1100:'External Abort on translation - First-level', 0b1110:'External Abort on translation - Second-level',
|
|
||||||
0b101:'Translation - Section', 0b111:'Translation - Page', 0b11:'Access bit - Section', 0b110:'Access bit - Page',
|
|
||||||
0b1001:'Domain - Section', 0b1011:'Domain - Page', 0b1101:'Permission - Section', 0b1111:'Permission - Page',
|
|
||||||
0b1000:'Precise External Abort', 0b10110:'Imprecise External Abort', 0b10:'Debug event'
|
|
||||||
}
|
|
||||||
|
|
||||||
def main(args=None):
|
|
||||||
parser = argparse.ArgumentParser(description="Parse Luma3DS exception dumps")
|
|
||||||
parser.add_argument("filename")
|
|
||||||
args = parser.parse_args()
|
|
||||||
data = b""
|
|
||||||
with open(args.filename, "rb") as f: data = f.read()
|
|
||||||
if unpack_from("<2I", data) != (0xdeadc0de, 0xdeadcafe):
|
|
||||||
raise SystemExit("Invalid file format")
|
|
||||||
|
|
||||||
version, processor, exceptionType, _, nbRegisters, codeDumpSize, stackDumpSize, additionalDataSize = unpack_from("<8I", data, 8)
|
|
||||||
nbRegisters //= 4
|
|
||||||
|
|
||||||
if version < (1 << 16) | 2:
|
|
||||||
raise SystemExit("Incompatible format version, please use the appropriate parser.")
|
|
||||||
|
|
||||||
registers = unpack_from("<{0}I".format(nbRegisters), data, 40)
|
|
||||||
codeOffset = 40 + 4 * nbRegisters
|
|
||||||
codeDump = data[codeOffset : codeOffset + codeDumpSize]
|
|
||||||
stackOffset = codeOffset + codeDumpSize
|
|
||||||
stackDump = data[stackOffset : stackOffset + stackDumpSize]
|
|
||||||
addtionalDataOffset = stackOffset + stackDumpSize
|
|
||||||
additionalData = data[addtionalDataOffset : addtionalDataOffset + additionalDataSize]
|
|
||||||
|
|
||||||
if processor == 9: print("Processor: Arm9")
|
|
||||||
else: print("Processor: Arm11 (core {0})".format(processor >> 16))
|
|
||||||
|
|
||||||
typeDetailsStr = ""
|
|
||||||
if exceptionType == 2:
|
|
||||||
if (registers[16] & 0x20) == 0 and codeDumpSize >= 4:
|
|
||||||
instr = unpack_from("<I", codeDump[-4:])[0]
|
|
||||||
if instr == 0xe12fff7e:
|
|
||||||
typeDetailsStr = " (kernel panic)"
|
|
||||||
elif instr == 0xef00003c:
|
|
||||||
typeDetailsStr = " " + (svcBreakReasons[registers[0]] if registers[0] < 3 else "(svcBreak)")
|
|
||||||
elif (registers[16] & 0x20) == 1 and codeDumpSize >= 2:
|
|
||||||
instr = unpack_from("<I", codeDump[-4:])[0]
|
|
||||||
if instr == 0xdf3c:
|
|
||||||
typeDetailsStr = " " + (svcBreakReasons[registers[0]] if registers[0] < 3 else "(svcBreak)")
|
|
||||||
|
|
||||||
elif processor != 9 and (registers[20] & 0x80000000) != 0:
|
|
||||||
typeDetailsStr = " (VFP exception)"
|
|
||||||
|
|
||||||
print("Exception type: {0}{1}".format("unknown" if exceptionType >= len(handledExceptionNames) else handledExceptionNames[exceptionType], typeDetailsStr))
|
|
||||||
|
|
||||||
if processor == 11 and exceptionType >= 2:
|
|
||||||
xfsr = registers[18] if exceptionType == 2 else registers[17]
|
|
||||||
print("Fault status: " + faultStatusSources[xfsr & 0xf])
|
|
||||||
|
|
||||||
if additionalDataSize != 0:
|
|
||||||
print("Current process: {0} ({1:016x})".format(additionalData[:8].decode("ascii"), unpack_from("<Q", additionalData, 8)[0]))
|
|
||||||
|
|
||||||
print("\nRegister dump:\n")
|
|
||||||
for i in range(0, nbRegisters - (nbRegisters % 2), 2):
|
|
||||||
if i == 16: print("")
|
|
||||||
print(makeRegisterLine(registerNames[i], registers[i], registerNames[i+1], registers[i+1]))
|
|
||||||
if nbRegisters % 2 == 1: print("{0:<15}{1:<20}".format(registerNames[nbRegisters - 1], "{0:08x}".format(registers[nbRegisters - 1])))
|
|
||||||
|
|
||||||
if processor == 11 and exceptionType == 3:
|
|
||||||
print("{0:<15}{1:<20}Access type: {2}".format("FAR", "{0:08x}".format(registers[19]), "Write" if registers[17] & (1 << 11) != 0 else "Read"))
|
|
||||||
|
|
||||||
thumb = registers[16] & 0x20 != 0
|
|
||||||
addr = registers[15] - codeDumpSize + (2 if thumb else 4)
|
|
||||||
|
|
||||||
print("\nCode dump:\n")
|
|
||||||
|
|
||||||
objdump_res = ""
|
|
||||||
try:
|
|
||||||
path = os.path.join(os.environ["DEVKITARM"], "bin", "arm-none-eabi-objdump")
|
|
||||||
|
|
||||||
if os.name == "nt" and path[0] == '/':
|
|
||||||
path = ''.join((path[1], ':', path[2:]))
|
|
||||||
|
|
||||||
objdump_res = subprocess.check_output((
|
|
||||||
path, "-marm", "-b", "binary",
|
|
||||||
"--adjust-vma="+hex(addr - codeOffset), "--start-address="+hex(addr),
|
|
||||||
"--stop-address="+hex(addr + codeDumpSize), "-D", "-z", "-M",
|
|
||||||
"reg-names-std" + (",force-thumb" if thumb else ""), args.filename
|
|
||||||
)).decode("utf-8")
|
|
||||||
objdump_res = '\n'.join(objdump_res[objdump_res.find('<.data+'):].split('\n')[1:])
|
|
||||||
except: objdump_res = ""
|
|
||||||
|
|
||||||
print(objdump_res if objdump_res != "" else hexdump(addr, codeDump))
|
|
||||||
|
|
||||||
print("\nStack dump:\n")
|
|
||||||
print(hexdump(registers[13], stackDump))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
from setuptools import setup, find_packages
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name='luma3ds_exception_dump_parser',
|
|
||||||
version='1.2',
|
|
||||||
url='https://github.com/AuroraWright/Luma3DS',
|
|
||||||
author='TuxSH',
|
|
||||||
license='GPLv3',
|
|
||||||
description='Parses Luma3DS exception dumps',
|
|
||||||
install_requires=[''],
|
|
||||||
packages=find_packages(),
|
|
||||||
entry_points={'console_scripts': ['luma3ds_exception_dump_parser=luma3ds_exception_dump_parser.__main__:main']},
|
|
||||||
)
|
|
||||||
@@ -33,5 +33,6 @@ enum singleOptions
|
|||||||
PATCHVERSTRING,
|
PATCHVERSTRING,
|
||||||
SHOWGBABOOT,
|
SHOWGBABOOT,
|
||||||
PATCHUNITINFO,
|
PATCHUNITINFO,
|
||||||
DISABLEARM11EXCHANDLERS
|
DISABLEARM11EXCHANDLERS,
|
||||||
|
ENABLESAFEFIRMROSALINA,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ typedef struct CfwInfo
|
|||||||
} CfwInfo;
|
} CfwInfo;
|
||||||
|
|
||||||
extern CfwInfo cfwInfo;
|
extern CfwInfo cfwInfo;
|
||||||
|
extern u32 kextBasePa;
|
||||||
|
extern u32 stolenSystemMemRegionSize;
|
||||||
|
|
||||||
extern vu32 rosalinaState;
|
extern vu32 rosalinaState;
|
||||||
extern bool hasStartedRosalinaNetworkFuncsOnce;
|
extern bool hasStartedRosalinaNetworkFuncsOnce;
|
||||||
|
|||||||
@@ -1126,11 +1126,11 @@ typedef union KCacheMaintenanceInterruptEvent
|
|||||||
|
|
||||||
typedef struct FcramLayout
|
typedef struct FcramLayout
|
||||||
{
|
{
|
||||||
void *applicationAddr;
|
u32 applicationAddr;
|
||||||
u32 applicationSize;
|
u32 applicationSize;
|
||||||
void *systemAddr;
|
u32 systemAddr;
|
||||||
u32 systemSize;
|
u32 systemSize;
|
||||||
void *baseAddr;
|
u32 baseAddr;
|
||||||
u32 baseSize;
|
u32 baseSize;
|
||||||
} FcramLayout;
|
} FcramLayout;
|
||||||
|
|
||||||
@@ -1138,15 +1138,15 @@ extern bool isN3DS;
|
|||||||
extern void *officialSVCs[0x7E];
|
extern void *officialSVCs[0x7E];
|
||||||
|
|
||||||
#define KPROCESSRELATED_OFFSETOFF(classname, field) (isN3DS ? offsetof(classname##N3DS, field) :\
|
#define KPROCESSRELATED_OFFSETOFF(classname, field) (isN3DS ? offsetof(classname##N3DS, field) :\
|
||||||
((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? offsetof(classname##O3DS8x, field) :\
|
((GET_VERSION_MINOR(kernelVersion) >= 44) ? offsetof(classname##O3DS8x, field) :\
|
||||||
offsetof(classname##O3DSPre8x, field)))
|
offsetof(classname##O3DSPre8x, field)))
|
||||||
|
|
||||||
#define KPROCESSRELATED_GET_PTR(obj, field) (isN3DS ? &(obj)->N3DS.field :\
|
#define KPROCESSRELATED_GET_PTR(obj, field) (isN3DS ? &(obj)->N3DS.field :\
|
||||||
((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? &(obj)->O3DS8x.field :\
|
((GET_VERSION_MINOR(kernelVersion) >= 44) ? &(obj)->O3DS8x.field :\
|
||||||
&(obj)->O3DSPre8x.field))
|
&(obj)->O3DSPre8x.field))
|
||||||
|
|
||||||
#define KPROCESSRELATED_GET_PTR_TYPE(type, obj, field) (isN3DS ? (type *)(&(obj)->N3DS.field) :\
|
#define KPROCESSRELATED_GET_PTR_TYPE(type, obj, field) (isN3DS ? (type *)(&(obj)->N3DS.field) :\
|
||||||
((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? (type *)(&(obj)->O3DS8x.field) :\
|
((GET_VERSION_MINOR(kernelVersion) >= 44) ? (type *)(&(obj)->O3DS8x.field) :\
|
||||||
(type *)(&(obj)->O3DSPre8x.field)))
|
(type *)(&(obj)->O3DSPre8x.field)))
|
||||||
|
|
||||||
#define KPROCESS_OFFSETOF(field) KPROCESSRELATED_OFFSETOFF(KProcess, field)
|
#define KPROCESS_OFFSETOF(field) KPROCESSRELATED_OFFSETOFF(KProcess, field)
|
||||||
@@ -1189,7 +1189,7 @@ static inline KDebug *debugOfProcess(KProcess *process)
|
|||||||
static inline const char *classNameOfAutoObject(KAutoObject *object)
|
static inline const char *classNameOfAutoObject(KAutoObject *object)
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
if(kernelVersion >= SYSTEM_VERSION(2, 46, 0))
|
if(GET_VERSION_MINOR(kernelVersion) >= 46)
|
||||||
{
|
{
|
||||||
KClassToken tok;
|
KClassToken tok;
|
||||||
object->vtable->GetClassToken(&tok, object);
|
object->vtable->GetClassToken(&tok, object);
|
||||||
@@ -1205,7 +1205,7 @@ extern Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Ha
|
|||||||
static inline Result createHandleForProcess(Handle *out, KProcess *process, KAutoObject *obj)
|
static inline Result createHandleForProcess(Handle *out, KProcess *process, KAutoObject *obj)
|
||||||
{
|
{
|
||||||
u8 token;
|
u8 token;
|
||||||
if(kernelVersion >= SYSTEM_VERSION(2, 46, 0))
|
if(GET_VERSION_MINOR(kernelVersion) >= 46)
|
||||||
{
|
{
|
||||||
KClassToken tok;
|
KClassToken tok;
|
||||||
obj->vtable->GetClassToken(&tok, obj);
|
obj->vtable->GetClassToken(&tok, obj);
|
||||||
|
|||||||
@@ -36,11 +36,12 @@ void executeFunctionOnCores(SGI0Handler_t func, u8 targetList, u8 targetListFilt
|
|||||||
|
|
||||||
void KScheduler__TriggerCrossCoreInterrupt(KScheduler *this);
|
void KScheduler__TriggerCrossCoreInterrupt(KScheduler *this);
|
||||||
void KThread__DebugReschedule(KThread *this, bool lock);
|
void KThread__DebugReschedule(KThread *this, bool lock);
|
||||||
bool rosalinaThreadLockPredicate(KThread *thread);
|
|
||||||
|
bool rosalinaThreadLockPredicate(KThread *thread, u32 mask);
|
||||||
void rosalinaRescheduleThread(KThread *thread, bool lock);
|
void rosalinaRescheduleThread(KThread *thread, bool lock);
|
||||||
void rosalinaLockThread(KThread *thread);
|
|
||||||
void rosalinaLockAllThreads(void);
|
void rosalinaLockThreads(u32 mask);
|
||||||
void rosalinaUnlockAllThreads(void);
|
void rosalinaUnlockThreads(u32 mask);
|
||||||
|
|
||||||
// Taken from ctrulib:
|
// Taken from ctrulib:
|
||||||
|
|
||||||
@@ -49,6 +50,11 @@ static inline void __dsb(void)
|
|||||||
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 4" :: [val] "r" (0) : "memory");
|
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 4" :: [val] "r" (0) : "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void __dmb(void)
|
||||||
|
{
|
||||||
|
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
static inline void __clrex(void)
|
static inline void __clrex(void)
|
||||||
{
|
{
|
||||||
__asm__ __volatile__("clrex" ::: "memory");
|
__asm__ __volatile__("clrex" ::: "memory");
|
||||||
|
|||||||
@@ -78,6 +78,15 @@ typedef s32 Result; ///< Function result.
|
|||||||
#define SYSTEM_VERSION(major, minor, revision) \
|
#define SYSTEM_VERSION(major, minor, revision) \
|
||||||
(((major)<<24)|((minor)<<16)|((revision)<<8))
|
(((major)<<24)|((minor)<<16)|((revision)<<8))
|
||||||
|
|
||||||
|
/// Retrieves the major version from a packed system version.
|
||||||
|
#define GET_VERSION_MAJOR(version) ((version) >>24)
|
||||||
|
|
||||||
|
/// Retrieves the minor version from a packed system version.
|
||||||
|
#define GET_VERSION_MINOR(version) (((version)>>16)&0xFF)
|
||||||
|
|
||||||
|
/// Retrieves the revision version from a packed system version.
|
||||||
|
#define GET_VERSION_REVISION(version) (((version)>> 8)&0xFF)
|
||||||
|
|
||||||
#define CUR_THREAD_HANDLE 0xFFFF8000
|
#define CUR_THREAD_HANDLE 0xFFFF8000
|
||||||
#define CUR_PROCESS_HANDLE 0xFFFF8001
|
#define CUR_PROCESS_HANDLE 0xFFFF8001
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ ENTRY(_start)
|
|||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
vram : ORIGIN = 0x18000000, LENGTH = 0x18180000 - 0x18000000 /* Up to the kernel builtins. */
|
vram : ORIGIN = 0x18000000, LENGTH = 0x18180000 - 0x18000000 /* Up to the kernel builtins. */
|
||||||
main : ORIGIN = 0x40000000, LENGTH = 1M
|
main : ORIGIN = 0x70000000, LENGTH = 1M
|
||||||
}
|
}
|
||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
@@ -15,7 +15,7 @@ PHDRS
|
|||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
PROVIDE(__start__ = 0x40000000);
|
PROVIDE(__start__ = ORIGIN(main));
|
||||||
. = ABSOLUTE(__start__);
|
. = ABSOLUTE(__start__);
|
||||||
|
|
||||||
.text :
|
.text :
|
||||||
|
|||||||
@@ -102,13 +102,14 @@ u32 nbSection0Modules;
|
|||||||
Result (*InterruptManager__MapInterrupt)(InterruptManager *manager, KBaseInterruptEvent *iEvent, u32 interruptID,
|
Result (*InterruptManager__MapInterrupt)(InterruptManager *manager, KBaseInterruptEvent *iEvent, u32 interruptID,
|
||||||
u32 coreID, u32 priority, bool disableUponReceipt, bool levelHighActive);
|
u32 coreID, u32 priority, bool disableUponReceipt, bool levelHighActive);
|
||||||
InterruptManager *interruptManager;
|
InterruptManager *interruptManager;
|
||||||
KBaseInterruptEvent *customInterruptEvent;
|
|
||||||
|
|
||||||
void (*initFPU)(void);
|
void (*initFPU)(void);
|
||||||
void (*mcuReboot)(void);
|
void (*mcuReboot)(void);
|
||||||
void (*coreBarrier)(void);
|
void (*coreBarrier)(void);
|
||||||
|
|
||||||
CfwInfo cfwInfo;
|
CfwInfo cfwInfo;
|
||||||
|
u32 kextBasePa;
|
||||||
|
u32 stolenSystemMemRegionSize;
|
||||||
|
|
||||||
vu32 rosalinaState;
|
vu32 rosalinaState;
|
||||||
bool hasStartedRosalinaNetworkFuncsOnce;
|
bool hasStartedRosalinaNetworkFuncsOnce;
|
||||||
|
|||||||
@@ -246,24 +246,24 @@ bool doErrfThrowHook(u32 *cmdbuf)
|
|||||||
u8 *srcerrbuf = (u8 *)r0_to_r7_r12_usr[(spsr & 0x20) ? 4 : 6];
|
u8 *srcerrbuf = (u8 *)r0_to_r7_r12_usr[(spsr & 0x20) ? 4 : 6];
|
||||||
const char *pname = codeSetOfProcess(currentCoreContext->objectContext.currentProcess)->processName;
|
const char *pname = codeSetOfProcess(currentCoreContext->objectContext.currentProcess)->processName;
|
||||||
|
|
||||||
static const struct
|
const struct
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
Result errCode;
|
Result errCode;
|
||||||
|
bool enabled;
|
||||||
} errorCodesToIgnore[] =
|
} errorCodesToIgnore[] =
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
If you're getting this error, you have broken your head-tracking hardware,
|
If you're getting this error, you may have broken your head-tracking hardware,
|
||||||
and should uncomment the following line:
|
and you need to enable the qtm error bypass below:
|
||||||
*/
|
*/
|
||||||
//{ "qtm", (Result)0xF96183FE },
|
{ "qtm", 0xF96183FEu, CONFIG(ENABLESAFEFIRMROSALINA)},
|
||||||
|
{ "", 0, false}, // impossible case to ensure the array has at least 1 element
|
||||||
{ "", 0 }, // impossible case to ensure the array has at least 1 element
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for(u32 i = 0; i < sizeof(errorCodesToIgnore) / sizeof(errorCodesToIgnore[0]); i++)
|
for(u32 i = 0; i < sizeof(errorCodesToIgnore) / sizeof(errorCodesToIgnore[0]); i++)
|
||||||
{
|
{
|
||||||
if(strcmp(pname, errorCodesToIgnore[i].name) == 0 && (Result)cmdbuf[2] == errorCodesToIgnore[i].errCode)
|
if(errorCodesToIgnore[i].enabled && strcmp(pname, errorCodesToIgnore[i].name) == 0 && (Result)cmdbuf[2] == errorCodesToIgnore[i].errCode)
|
||||||
{
|
{
|
||||||
srcerrbuf[0] = 5;
|
srcerrbuf[0] = 5;
|
||||||
cmdbuf[0] = 0x10040;
|
cmdbuf[0] = 0x10040;
|
||||||
|
|||||||
@@ -32,22 +32,27 @@
|
|||||||
#include "svc/ConnectToPort.h"
|
#include "svc/ConnectToPort.h"
|
||||||
#include "svcHandler.h"
|
#include "svcHandler.h"
|
||||||
|
|
||||||
|
#define K11EXT_VA 0x70000000
|
||||||
|
|
||||||
struct KExtParameters
|
struct KExtParameters
|
||||||
{
|
{
|
||||||
u32 basePA;
|
u32 basePA;
|
||||||
|
u32 stolenSystemMemRegionSize;
|
||||||
void *originalHandlers[4];
|
void *originalHandlers[4];
|
||||||
u32 L1MMUTableAddrs[4];
|
u32 L1MMUTableAddrs[4];
|
||||||
|
|
||||||
|
volatile bool done;
|
||||||
|
|
||||||
CfwInfo cfwInfo;
|
CfwInfo cfwInfo;
|
||||||
} kExtParameters = { .basePA = 0x12345678 }; // place this in .data
|
} kExtParameters = { .basePA = 0x12345678 }; // place this in .data
|
||||||
|
|
||||||
static ALIGN(1024) u32 L2TableFor0x40000000[256] = {0};
|
static ALIGN(1024) u32 g_L2Table[256] = {0};
|
||||||
|
|
||||||
void relocateAndSetupMMU(u32 coreId, u32 *L1Table)
|
void relocateAndSetupMMU(u32 coreId, u32 *L1Table)
|
||||||
{
|
{
|
||||||
struct KExtParameters *p0 = (struct KExtParameters *)((u32)&kExtParameters - 0x40000000 + 0x18000000);
|
struct KExtParameters *p0 = (struct KExtParameters *)((u32)&kExtParameters - K11EXT_VA + 0x18000000);
|
||||||
struct KExtParameters *p = (struct KExtParameters *)((u32)&kExtParameters - 0x40000000 + p0->basePA);
|
struct KExtParameters *p = (struct KExtParameters *)((u32)&kExtParameters - K11EXT_VA + p0->basePA);
|
||||||
u32 *L2Table = (u32 *)((u32)L2TableFor0x40000000 - 0x40000000 + p0->basePA);
|
u32 *L2Table = (u32 *)((u32)g_L2Table - K11EXT_VA + p0->basePA);
|
||||||
|
|
||||||
if(coreId == 0)
|
if(coreId == 0)
|
||||||
{
|
{
|
||||||
@@ -56,16 +61,32 @@ void relocateAndSetupMMU(u32 coreId, u32 *L1Table)
|
|||||||
memcpy((void *)p0->basePA, (const void *)0x18000000, __bss_start__ - __start__);
|
memcpy((void *)p0->basePA, (const void *)0x18000000, __bss_start__ - __start__);
|
||||||
memset((u32 *)(p0->basePA + (__bss_start__ - __start__)), 0, __bss_end__ - __bss_start__);
|
memset((u32 *)(p0->basePA + (__bss_start__ - __start__)), 0, __bss_end__ - __bss_start__);
|
||||||
|
|
||||||
// Map the kernel ext to 0x40000000
|
// Map the kernel ext at K11EXT_VA
|
||||||
// 4KB extended small pages: [SYS:RW USR:-- X TYP:NORMAL SHARED OUTER NOCACHE, INNER CACHED WB WA]
|
// 4KB extended small pages:
|
||||||
|
// Outer Write-Through cached, No Allocate on Write, Buffered
|
||||||
|
// Inner Cached Write-Back Write-Allocate, Buffered
|
||||||
|
// This was changed at some point (8.0 maybe?), it was outer noncached before
|
||||||
for(u32 offset = 0; offset < (u32)(__end__ - __start__); offset += 0x1000)
|
for(u32 offset = 0; offset < (u32)(__end__ - __start__); offset += 0x1000)
|
||||||
L2Table[offset >> 12] = (p0->basePA + offset) | 0x516;
|
L2Table[offset >> 12] = (p0->basePA + offset) | 0x596;
|
||||||
|
|
||||||
|
p0->done = true;
|
||||||
|
|
||||||
|
// DSB, Flush Prefetch Buffer (more or less "isb")
|
||||||
|
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" :: "r" (0) : "memory");
|
||||||
|
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c5, 4" :: "r" (0) : "memory");
|
||||||
|
|
||||||
__asm__ __volatile__ ("sev");
|
__asm__ __volatile__ ("sev");
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
__asm__ __volatile__ ("wfe");
|
do
|
||||||
|
{
|
||||||
|
__asm__ __volatile__ ("wfe");
|
||||||
|
} while(!p0->done);
|
||||||
|
|
||||||
|
// DSB, Flush Prefetch Buffer (more or less "isb")
|
||||||
|
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" :: "r" (0) : "memory");
|
||||||
|
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c5, 4" :: "r" (0) : "memory");
|
||||||
|
}
|
||||||
// bit31 idea thanks to SALT
|
// bit31 idea thanks to SALT
|
||||||
// Maps physmem so that, if addr is in physmem(0, 0x30000000), it can be accessed uncached&rwx as addr|(1<<31)
|
// Maps physmem so that, if addr is in physmem(0, 0x30000000), it can be accessed uncached&rwx as addr|(1<<31)
|
||||||
u32 attribs = 0x40C02; // supersection (rwx for all) of strongly ordered memory, shared
|
u32 attribs = 0x40C02; // supersection (rwx for all) of strongly ordered memory, shared
|
||||||
@@ -76,9 +97,13 @@ void relocateAndSetupMMU(u32 coreId, u32 *L1Table)
|
|||||||
L1Table[i + (VA >> 20)] = PA | attribs;
|
L1Table[i + (VA >> 20)] = PA | attribs;
|
||||||
}
|
}
|
||||||
|
|
||||||
L1Table[0x40000000 >> 20] = (u32)L2Table | 1;
|
L1Table[K11EXT_VA >> 20] = (u32)L2Table | 1;
|
||||||
|
|
||||||
p->L1MMUTableAddrs[coreId] = (u32)L1Table;
|
p->L1MMUTableAddrs[coreId] = (u32)L1Table;
|
||||||
|
|
||||||
|
// DSB, Flush Prefetch Buffer (more or less "isb")
|
||||||
|
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" :: "r" (0) : "memory");
|
||||||
|
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c5, 4" :: "r" (0) : "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
void bindSGI0Hook(void)
|
void bindSGI0Hook(void)
|
||||||
@@ -259,7 +284,11 @@ void main(FcramLayout *layout, KCoreContext *ctxs)
|
|||||||
u32 TTBCR_;
|
u32 TTBCR_;
|
||||||
s64 nb;
|
s64 nb;
|
||||||
|
|
||||||
layout->systemSize -= __end__ - __start__;
|
cfwInfo = p->cfwInfo;
|
||||||
|
kextBasePa = p->basePA;
|
||||||
|
stolenSystemMemRegionSize = p->stolenSystemMemRegionSize;
|
||||||
|
|
||||||
|
layout->systemSize -= stolenSystemMemRegionSize;
|
||||||
fcramLayout = *layout;
|
fcramLayout = *layout;
|
||||||
coreCtxs = ctxs;
|
coreCtxs = ctxs;
|
||||||
|
|
||||||
@@ -268,7 +297,6 @@ void main(FcramLayout *layout, KCoreContext *ctxs)
|
|||||||
isN3DS = getNumberOfCores() == 4;
|
isN3DS = getNumberOfCores() == 4;
|
||||||
memcpy(L1MMUTableAddrs, (const void *)p->L1MMUTableAddrs, 16);
|
memcpy(L1MMUTableAddrs, (const void *)p->L1MMUTableAddrs, 16);
|
||||||
exceptionStackTop = (u32 *)0xFFFF2000 + (1 << (32 - TTBCR - 20));
|
exceptionStackTop = (u32 *)0xFFFF2000 + (1 << (32 - TTBCR - 20));
|
||||||
cfwInfo = p->cfwInfo;
|
|
||||||
|
|
||||||
memcpy(originalHandlers + 1, p->originalHandlers, 16);
|
memcpy(originalHandlers + 1, p->originalHandlers, 16);
|
||||||
void **arm11SvcTable = (void**)originalHandlers[2];
|
void **arm11SvcTable = (void**)originalHandlers[2];
|
||||||
@@ -282,4 +310,8 @@ void main(FcramLayout *layout, KCoreContext *ctxs)
|
|||||||
|
|
||||||
rosalinaState = 0;
|
rosalinaState = 0;
|
||||||
hasStartedRosalinaNetworkFuncsOnce = false;
|
hasStartedRosalinaNetworkFuncsOnce = false;
|
||||||
|
|
||||||
|
// DSB, Flush Prefetch Buffer (more or less "isb")
|
||||||
|
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" :: "r" (0) : "memory");
|
||||||
|
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c5, 4" :: "r" (0) : "memory");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ void signalSvcReturn(u8 *pageEnd)
|
|||||||
void postprocessSvc(void)
|
void postprocessSvc(void)
|
||||||
{
|
{
|
||||||
KThread *currentThread = currentCoreContext->objectContext.currentThread;
|
KThread *currentThread = currentCoreContext->objectContext.currentThread;
|
||||||
if(!currentThread->shallTerminate && rosalinaThreadLockPredicate(currentThread))
|
if(!currentThread->shallTerminate && rosalinaThreadLockPredicate(currentThread, rosalinaState & 5))
|
||||||
rosalinaRescheduleThread(currentThread, true);
|
rosalinaRescheduleThread(currentThread, true);
|
||||||
|
|
||||||
officialPostProcessSvc();
|
officialPostProcessSvc();
|
||||||
|
|||||||
@@ -79,10 +79,15 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x300: // K11Ext size
|
case 0x300: // K11Ext size
|
||||||
*out = (s64)(__end__ - __start__);
|
*out = (s64)(((u64)kextBasePa << 32) | (u64)(__end__ - __start__));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x301: // stolen SYSTEM memory size
|
||||||
|
*out = stolenSystemMemRegionSize;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
*out = 0;
|
||||||
res = 0xF8C007F4; // not implemented
|
res = 0xF8C007F4; // not implemented
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -105,13 +110,16 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
|
|||||||
*out = L2C_CTRL & 1;
|
*out = L2C_CTRL & 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
*out = 0;
|
||||||
res = 0xF8C007F4;
|
res = 0xF8C007F4;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
*out = 0;
|
||||||
res = 0xF8C007F4;
|
res = 0xF8C007F4;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +136,10 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
|
|||||||
if((u32)param <= getNumberOfCores())
|
if((u32)param <= getNumberOfCores())
|
||||||
*out = L1MMUTableAddrs[param - 1];
|
*out = L1MMUTableAddrs[param - 1];
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
*out = 0;
|
||||||
res = 0xF8C007F4;
|
res = 0xF8C007F4;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -136,6 +147,13 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 0x20000:
|
||||||
|
{
|
||||||
|
*out = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
GetSystemInfo(out, type, param);
|
GetSystemInfo(out, type, param);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -104,14 +104,22 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3)
|
|||||||
__ldrex((s32 *)&rosalinaState);
|
__ldrex((s32 *)&rosalinaState);
|
||||||
}
|
}
|
||||||
while(__strex((s32 *)&rosalinaState, (s32)(rosalinaState ^ varg1)));
|
while(__strex((s32 *)&rosalinaState, (s32)(rosalinaState ^ varg1)));
|
||||||
|
__dmb();
|
||||||
|
|
||||||
if(rosalinaState & 2)
|
if(rosalinaState & 0x10)
|
||||||
hasStartedRosalinaNetworkFuncsOnce = true;
|
hasStartedRosalinaNetworkFuncsOnce = true;
|
||||||
|
|
||||||
if(rosalinaState & 1)
|
// 1: all applet/app/dsp/csnd... threads 2: gsp 4: hid/ir
|
||||||
rosalinaLockAllThreads();
|
for (u32 v = 4; v != 0; v >>= 1)
|
||||||
else if(varg1 & 1)
|
{
|
||||||
rosalinaUnlockAllThreads();
|
if (varg1 & v)
|
||||||
|
{
|
||||||
|
if (rosalinaState & v)
|
||||||
|
rosalinaLockThreads(v);
|
||||||
|
else
|
||||||
|
rosalinaUnlockThreads(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,16 @@
|
|||||||
#include "svc/SendSyncRequest.h"
|
#include "svc/SendSyncRequest.h"
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
|
|
||||||
|
static inline bool isNdmuWorkaround(const SessionInfo *info, u32 pid)
|
||||||
|
{
|
||||||
|
return info != NULL && strcmp(info->name, "ndm:u") == 0 && hasStartedRosalinaNetworkFuncsOnce && pid >= nbSection0Modules;
|
||||||
|
}
|
||||||
|
|
||||||
Result SendSyncRequestHook(Handle handle)
|
Result SendSyncRequestHook(Handle handle)
|
||||||
{
|
{
|
||||||
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
|
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
|
||||||
|
KProcessHandleTable *handleTable = handleTableOfProcess(currentProcess);
|
||||||
|
u32 pid = idOfProcess(currentProcess);
|
||||||
KClientSession *clientSession = (KClientSession *)KProcessHandleTable__ToKAutoObject(handleTable, handle);
|
KClientSession *clientSession = (KClientSession *)KProcessHandleTable__ToKAutoObject(handleTable, handle);
|
||||||
|
|
||||||
u32 *cmdbuf = (u32 *)((u8 *)currentCoreContext->objectContext.currentThread->threadLocalStorage + 0x80);
|
u32 *cmdbuf = (u32 *)((u8 *)currentCoreContext->objectContext.currentThread->threadLocalStorage + 0x80);
|
||||||
@@ -47,7 +54,7 @@ Result SendSyncRequestHook(Handle handle)
|
|||||||
case 0x10042:
|
case 0x10042:
|
||||||
{
|
{
|
||||||
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
||||||
if(info != NULL && strcmp(info->name, "ndm:u") == 0 && hasStartedRosalinaNetworkFuncsOnce)
|
if(isNdmuWorkaround(info, pid))
|
||||||
{
|
{
|
||||||
cmdbuf[0] = 0x10040;
|
cmdbuf[0] = 0x10040;
|
||||||
cmdbuf[1] = 0;
|
cmdbuf[1] = 0;
|
||||||
@@ -87,7 +94,7 @@ Result SendSyncRequestHook(Handle handle)
|
|||||||
case 0x20002:
|
case 0x20002:
|
||||||
{
|
{
|
||||||
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
||||||
if(info != NULL && strcmp(info->name, "ndm:u") == 0 && hasStartedRosalinaNetworkFuncsOnce)
|
if(isNdmuWorkaround(info, pid))
|
||||||
{
|
{
|
||||||
cmdbuf[0] = 0x20040;
|
cmdbuf[0] = 0x20040;
|
||||||
cmdbuf[1] = 0;
|
cmdbuf[1] = 0;
|
||||||
@@ -100,7 +107,7 @@ Result SendSyncRequestHook(Handle handle)
|
|||||||
case 0x50100:
|
case 0x50100:
|
||||||
{
|
{
|
||||||
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
||||||
if(info != NULL && (strcmp(info->name, "srv:") == 0 || (kernelVersion < SYSTEM_VERSION(2, 39, 4) && strcmp(info->name, "srv:pm") == 0)))
|
if(info != NULL && (strcmp(info->name, "srv:") == 0 || (GET_VERSION_MINOR(kernelVersion) < 39 && strcmp(info->name, "srv:pm") == 0)))
|
||||||
{
|
{
|
||||||
char name[9] = { 0 };
|
char name[9] = { 0 };
|
||||||
memcpy(name, cmdbuf + 1, 8);
|
memcpy(name, cmdbuf + 1, 8);
|
||||||
@@ -129,7 +136,7 @@ Result SendSyncRequestHook(Handle handle)
|
|||||||
if(!hasStartedRosalinaNetworkFuncsOnce)
|
if(!hasStartedRosalinaNetworkFuncsOnce)
|
||||||
break;
|
break;
|
||||||
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
||||||
skip = info != NULL && strcmp(info->name, "ndm:u") == 0; // SuspendScheduler
|
skip = isNdmuWorkaround(info, pid); // SuspendScheduler
|
||||||
if(skip)
|
if(skip)
|
||||||
cmdbuf[1] = 0;
|
cmdbuf[1] = 0;
|
||||||
break;
|
break;
|
||||||
@@ -140,7 +147,7 @@ Result SendSyncRequestHook(Handle handle)
|
|||||||
if(!hasStartedRosalinaNetworkFuncsOnce)
|
if(!hasStartedRosalinaNetworkFuncsOnce)
|
||||||
break;
|
break;
|
||||||
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
||||||
if(info != NULL && strcmp(info->name, "ndm:u") == 0) // ResumeScheduler
|
if(isNdmuWorkaround(info, pid)) // ResumeScheduler
|
||||||
{
|
{
|
||||||
cmdbuf[0] = 0x90040;
|
cmdbuf[0] = 0x90040;
|
||||||
cmdbuf[1] = 0;
|
cmdbuf[1] = 0;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
Result UnmapProcessMemoryEx(Handle processHandle, void *dst, u32 size)
|
Result UnmapProcessMemoryEx(Handle processHandle, void *dst, u32 size)
|
||||||
{
|
{
|
||||||
if(kernelVersion < SYSTEM_VERSION(2, 37, 0)) // < 6.x
|
if(GET_VERSION_MINOR(kernelVersion) < 37) // < 6.x
|
||||||
return UnmapProcessMemory(processHandle, dst, size); // equivalent when size <= 64MB
|
return UnmapProcessMemory(processHandle, dst, size); // equivalent when size <= 64MB
|
||||||
|
|
||||||
KProcessHwInfo *currentHwInfo = hwInfoOfProcess(currentCoreContext->objectContext.currentProcess);
|
KProcessHwInfo *currentHwInfo = hwInfoOfProcess(currentCoreContext->objectContext.currentProcess);
|
||||||
|
|||||||
@@ -66,17 +66,12 @@ void KThread__DebugReschedule(KThread *this, bool lock)
|
|||||||
KRecursiveLock__Unlock(criticalSectionLock);
|
KRecursiveLock__Unlock(criticalSectionLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rosalinaThreadLockPredicate(KThread *thread)
|
static void rosalinaLockThread(KThread *thread)
|
||||||
{
|
{
|
||||||
KProcess *process = thread->ownerProcess;
|
KThread *syncThread = synchronizationMutex->owner;
|
||||||
if(process == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
u64 titleId = codeSetOfProcess(process)->titleId;
|
if(syncThread == NULL || syncThread != thread)
|
||||||
u32 highTitleId = (u32)(titleId >> 32), lowTitleId = (u32)titleId;
|
rosalinaRescheduleThread(thread, true);
|
||||||
return
|
|
||||||
((rosalinaState & 1) && idOfProcess(process) >= nbSection0Modules &&
|
|
||||||
(highTitleId != 0x00040130 || (highTitleId == 0x00040130 && (lowTitleId == 0x1A02 || lowTitleId == 0x1C02))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rosalinaRescheduleThread(KThread *thread, bool lock)
|
void rosalinaRescheduleThread(KThread *thread, bool lock)
|
||||||
@@ -89,20 +84,45 @@ void rosalinaRescheduleThread(KThread *thread, bool lock)
|
|||||||
else
|
else
|
||||||
thread->schedulingMask &= ~0x40;
|
thread->schedulingMask &= ~0x40;
|
||||||
|
|
||||||
KScheduler__AdjustThread(currentCoreContext->objectContext.currentScheduler, thread, oldSchedulingMask);
|
if (oldSchedulingMask != thread->schedulingMask)
|
||||||
|
KScheduler__AdjustThread(currentCoreContext->objectContext.currentScheduler, thread, oldSchedulingMask);
|
||||||
|
|
||||||
KRecursiveLock__Unlock(criticalSectionLock);
|
KRecursiveLock__Unlock(criticalSectionLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rosalinaLockThread(KThread *thread)
|
bool rosalinaThreadLockPredicate(KThread *thread, u32 mask)
|
||||||
{
|
{
|
||||||
KThread *syncThread = synchronizationMutex->owner;
|
KProcess *process = thread->ownerProcess;
|
||||||
|
if(process == NULL || idOfProcess(process) < nbSection0Modules)
|
||||||
|
return false;
|
||||||
|
|
||||||
if(syncThread == NULL || syncThread != thread)
|
u64 titleId = codeSetOfProcess(process)->titleId;
|
||||||
rosalinaRescheduleThread(thread, true);
|
u32 highTitleId = (u32)(titleId >> 32), lowTitleId = (u32)(titleId & ~0xF0000001); // clear N3DS and SAFE_FIRM bits
|
||||||
|
|
||||||
|
if (mask & 1)
|
||||||
|
{
|
||||||
|
if (highTitleId != 0x00040130) // non-sysmodules
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return lowTitleId == 0x1A02 || lowTitleId == 0x2702; // dsp, csnd
|
||||||
|
}
|
||||||
|
if (mask & 2)
|
||||||
|
{
|
||||||
|
if (highTitleId != 0x00040130) // non-sysmodules
|
||||||
|
false;
|
||||||
|
return lowTitleId == 0x1C02; // gsp
|
||||||
|
}
|
||||||
|
if (mask & 4)
|
||||||
|
{
|
||||||
|
if (highTitleId != 0x00040130) // non-sysmodules
|
||||||
|
return false;
|
||||||
|
return lowTitleId == 0x1D02 || lowTitleId == 0x3302;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rosalinaLockAllThreads(void)
|
void rosalinaLockThreads(u32 mask)
|
||||||
{
|
{
|
||||||
bool currentThreadsFound = false;
|
bool currentThreadsFound = false;
|
||||||
|
|
||||||
@@ -110,7 +130,7 @@ void rosalinaLockAllThreads(void)
|
|||||||
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
||||||
{
|
{
|
||||||
KThread *thread = (KThread *)node->key;
|
KThread *thread = (KThread *)node->key;
|
||||||
if(!rosalinaThreadLockPredicate(thread))
|
if(!rosalinaThreadLockPredicate(thread, mask))
|
||||||
continue;
|
continue;
|
||||||
if(thread == coreCtxs[thread->coreId].objectContext.currentThread)
|
if(thread == coreCtxs[thread->coreId].objectContext.currentThread)
|
||||||
currentThreadsFound = true;
|
currentThreadsFound = true;
|
||||||
@@ -123,7 +143,7 @@ void rosalinaLockAllThreads(void)
|
|||||||
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
||||||
{
|
{
|
||||||
KThread *thread = (KThread *)node->key;
|
KThread *thread = (KThread *)node->key;
|
||||||
if(!rosalinaThreadLockPredicate(thread))
|
if(!rosalinaThreadLockPredicate(thread, mask))
|
||||||
continue;
|
continue;
|
||||||
if(!(thread->schedulingMask & 0x40))
|
if(!(thread->schedulingMask & 0x40))
|
||||||
{
|
{
|
||||||
@@ -145,7 +165,7 @@ void rosalinaLockAllThreads(void)
|
|||||||
KRecursiveLock__Unlock(criticalSectionLock);
|
KRecursiveLock__Unlock(criticalSectionLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rosalinaUnlockAllThreads(void)
|
void rosalinaUnlockThreads(u32 mask)
|
||||||
{
|
{
|
||||||
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
||||||
{
|
{
|
||||||
@@ -154,7 +174,7 @@ void rosalinaUnlockAllThreads(void)
|
|||||||
if((thread->schedulingMask & 0xF) == 2) // thread is terminating
|
if((thread->schedulingMask & 0xF) == 2) // thread is terminating
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if(thread->schedulingMask & 0x40)
|
if((thread->schedulingMask & 0x40) && rosalinaThreadLockPredicate(thread, mask))
|
||||||
rosalinaRescheduleThread(thread, false);
|
rosalinaRescheduleThread(thread, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,12 +121,14 @@ _safecpy_end:
|
|||||||
.section .rodata.safecpy_sz, "a", %progbits
|
.section .rodata.safecpy_sz, "a", %progbits
|
||||||
|
|
||||||
.global safecpy_sz
|
.global safecpy_sz
|
||||||
|
.type safecpy_sz, %object
|
||||||
safecpy_sz: .word _safecpy_end - safecpy
|
safecpy_sz: .word _safecpy_end - safecpy
|
||||||
|
|
||||||
.section .bss.SGI0Handler, "aw", %nobits
|
.section .bss.SGI0Handler, "aw", %nobits
|
||||||
.balign 4
|
.balign 4
|
||||||
|
|
||||||
.global SGI0Handler
|
.global SGI0Handler
|
||||||
|
.type safecpy_sz, %object
|
||||||
SGI0Handler: .skip 4 @ see synchronization.c
|
SGI0Handler: .skip 4 @ see synchronization.c
|
||||||
|
|
||||||
.balign 4
|
.balign 4
|
||||||
@@ -136,4 +138,5 @@ SGI0Handler: .skip 4 @ see synchronization.c
|
|||||||
|
|
||||||
_customInterruptEventObj: .word SGI0Handler
|
_customInterruptEventObj: .word SGI0Handler
|
||||||
.global customInterruptEvent
|
.global customInterruptEvent
|
||||||
|
.type safecpy_sz, %object
|
||||||
customInterruptEvent: .word _customInterruptEventObj
|
customInterruptEvent: .word _customInterruptEventObj
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
#include <3ds/exheader.h>
|
#include <3ds/exheader.h>
|
||||||
|
|
||||||
#define HBLDR_3DSX_TID (*(vu64 *)0x1FF81100)
|
|
||||||
|
|
||||||
Result hbldrInit(void);
|
Result hbldrInit(void);
|
||||||
void hbldrExit(void);
|
void hbldrExit(void);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "ifile.h"
|
#include "ifile.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "hbldr.h"
|
#include "hbldr.h"
|
||||||
|
#include "luma_shared_config.h"
|
||||||
|
|
||||||
extern u32 config, multiConfig, bootConfig;
|
extern u32 config, multiConfig, bootConfig;
|
||||||
extern bool isN3DS, isSdMode;
|
extern bool isN3DS, isSdMode;
|
||||||
@@ -91,6 +92,11 @@ static int lzss_decompress(u8 *end)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool hbldrIs3dsxTitle(u64 tid)
|
||||||
|
{
|
||||||
|
return Luma_SharedConfig->use_hbldr && tid == Luma_SharedConfig->hbldr_3dsx_tid;
|
||||||
|
}
|
||||||
|
|
||||||
static Result allocateSharedMem(prog_addrs_t *shared, prog_addrs_t *vaddr, int flags)
|
static Result allocateSharedMem(prog_addrs_t *shared, prog_addrs_t *vaddr, int flags)
|
||||||
{
|
{
|
||||||
u32 dummy;
|
u32 dummy;
|
||||||
@@ -169,23 +175,19 @@ static Result GetProgramInfo(ExHeader_Info *exheaderInfo, u64 programHandle)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s64 nbSection0Modules;
|
|
||||||
svcGetSystemInfo(&nbSection0Modules, 26, 0);
|
|
||||||
|
|
||||||
// Force always having sdmc:/ and nand:/rw permission
|
|
||||||
exheaderInfo->aci.local_caps.storage_info.fs_access_info |= FSACCESS_NANDRW | FSACCESS_SDMC_RW;
|
|
||||||
|
|
||||||
// Tweak 3dsx placeholder title exheaderInfo
|
// Tweak 3dsx placeholder title exheaderInfo
|
||||||
if (nbSection0Modules == 6 && exheaderInfo->aci.local_caps.title_id == HBLDR_3DSX_TID)
|
if (hbldrIs3dsxTitle(exheaderInfo->aci.local_caps.title_id))
|
||||||
{
|
{
|
||||||
assertSuccess(hbldrInit());
|
assertSuccess(hbldrInit());
|
||||||
HBLDR_PatchExHeaderInfo(exheaderInfo);
|
HBLDR_PatchExHeaderInfo(exheaderInfo);
|
||||||
hbldrExit();
|
hbldrExit();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
u64 originaltitleId = exheaderInfo->aci.local_caps.title_id;
|
{
|
||||||
if(CONFIG(PATCHGAMES) && loadTitleExheaderInfo(exheaderInfo->aci.local_caps.title_id, exheaderInfo))
|
u64 originaltitleId = exheaderInfo->aci.local_caps.title_id;
|
||||||
exheaderInfo->aci.local_caps.title_id = originaltitleId;
|
if(CONFIG(PATCHGAMES) && loadTitleExheaderInfo(exheaderInfo->aci.local_caps.title_id, exheaderInfo))
|
||||||
|
exheaderInfo->aci.local_caps.title_id = originaltitleId;
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -205,7 +207,7 @@ static Result LoadProcess(Handle *process, u64 programHandle)
|
|||||||
u64 titleId;
|
u64 titleId;
|
||||||
|
|
||||||
// make sure the cached info corrosponds to the current programHandle
|
// make sure the cached info corrosponds to the current programHandle
|
||||||
if (g_cached_programHandle != programHandle || g_exheaderInfo.aci.local_caps.title_id == HBLDR_3DSX_TID)
|
if (g_cached_programHandle != programHandle || hbldrIs3dsxTitle(g_exheaderInfo.aci.local_caps.title_id))
|
||||||
{
|
{
|
||||||
res = GetProgramInfo(&g_exheaderInfo, programHandle);
|
res = GetProgramInfo(&g_exheaderInfo, programHandle);
|
||||||
g_cached_programHandle = programHandle;
|
g_cached_programHandle = programHandle;
|
||||||
@@ -231,7 +233,7 @@ static Result LoadProcess(Handle *process, u64 programHandle)
|
|||||||
titleId = g_exheaderInfo.aci.local_caps.title_id;
|
titleId = g_exheaderInfo.aci.local_caps.title_id;
|
||||||
ExHeader_CodeSetInfo *csi = &g_exheaderInfo.sci.codeset_info;
|
ExHeader_CodeSetInfo *csi = &g_exheaderInfo.sci.codeset_info;
|
||||||
|
|
||||||
if (titleId == HBLDR_3DSX_TID)
|
if (hbldrIs3dsxTitle(titleId))
|
||||||
{
|
{
|
||||||
assertSuccess(hbldrInit());
|
assertSuccess(hbldrInit());
|
||||||
assertSuccess(HBLDR_LoadProcess(&codeset, csi->text.address, flags & 0xF00, titleId, csi->name));
|
assertSuccess(HBLDR_LoadProcess(&codeset, csi->text.address, flags & 0xF00, titleId, csi->name));
|
||||||
@@ -378,7 +380,7 @@ void loaderHandleCommands(void *ctx)
|
|||||||
break;
|
break;
|
||||||
case 4: // GetProgramInfo
|
case 4: // GetProgramInfo
|
||||||
memcpy(&programHandle, &cmdbuf[1], 8);
|
memcpy(&programHandle, &cmdbuf[1], 8);
|
||||||
if (programHandle != g_cached_programHandle || g_exheaderInfo.aci.local_caps.title_id == HBLDR_3DSX_TID)
|
if (programHandle != g_cached_programHandle || hbldrIs3dsxTitle(g_exheaderInfo.aci.local_caps.title_id))
|
||||||
{
|
{
|
||||||
res = GetProgramInfo(&g_exheaderInfo, programHandle);
|
res = GetProgramInfo(&g_exheaderInfo, programHandle);
|
||||||
g_cached_programHandle = R_SUCCEEDED(res) ? programHandle : 0;
|
g_cached_programHandle = R_SUCCEEDED(res) ? programHandle : 0;
|
||||||
|
|||||||
28
sysmodules/loader/source/luma_shared_config.h
Normal file
28
sysmodules/loader/source/luma_shared_config.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/* This paricular file is licensed under the following terms: */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable
|
||||||
|
* for any damages arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it
|
||||||
|
* and redistribute it freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||||
|
* If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||||
|
*
|
||||||
|
* Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||||
|
* This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
/// Luma shared config type.
|
||||||
|
typedef struct LumaSharedConfig {
|
||||||
|
u64 hbldr_3dsx_tid; ///< Title ID to use for 3DSX loading.
|
||||||
|
bool use_hbldr; ///< Whether or not Loader should use hb:ldr (Rosalina writes 1).
|
||||||
|
} LumaSharedConfig;
|
||||||
|
|
||||||
|
/// Luma shared config.
|
||||||
|
#define Luma_SharedConfig ((volatile LumaSharedConfig *)(OS_SHAREDCFG_VADDR + 0x800))
|
||||||
@@ -29,18 +29,18 @@ static inline void loadCFWInfo(void)
|
|||||||
{
|
{
|
||||||
s64 out;
|
s64 out;
|
||||||
|
|
||||||
assertSuccess(svcGetSystemInfo(&out, 0x10000, 3));
|
if(svcGetSystemInfo(&out, 0x20000, 0) != 1) panic(0xDEADCAFE);
|
||||||
|
|
||||||
|
svcGetSystemInfo(&out, 0x10000, 3);
|
||||||
config = (u32)out;
|
config = (u32)out;
|
||||||
assertSuccess(svcGetSystemInfo(&out, 0x10000, 4));
|
svcGetSystemInfo(&out, 0x10000, 4);
|
||||||
multiConfig = (u32)out;
|
multiConfig = (u32)out;
|
||||||
assertSuccess(svcGetSystemInfo(&out, 0x10000, 5));
|
svcGetSystemInfo(&out, 0x10000, 5);
|
||||||
bootConfig = (u32)out;
|
bootConfig = (u32)out;
|
||||||
|
|
||||||
assertSuccess(svcGetSystemInfo(&out, 0x10000, 0x201));
|
svcGetSystemInfo(&out, 0x10000, 0x201);
|
||||||
isN3DS = (bool)out;
|
isN3DS = (bool)out;
|
||||||
//assertSuccess(svcGetSystemInfo(&out, 0x10000, 0x202));
|
svcGetSystemInfo(&out, 0x10000, 0x203);
|
||||||
//needToInitSd = (bool)out;
|
|
||||||
assertSuccess(svcGetSystemInfo(&out, 0x10000, 0x203));
|
|
||||||
isSdMode = (bool)out;
|
isSdMode = (bool)out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -740,7 +740,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(progId == 0x0004013000001702LL) //CFG
|
else if((progId & ~0xF0000001ULL) == 0x0004013000001702LL) //CFG, SAFE_FIRM CFG
|
||||||
{
|
{
|
||||||
static const u8 pattern[] = {
|
static const u8 pattern[] = {
|
||||||
0x06, 0x46, 0x10, 0x48
|
0x06, 0x46, 0x10, 0x48
|
||||||
@@ -826,7 +826,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
|
|||||||
)) goto error;
|
)) goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(progId == 0x0004013000001A02LL) //DSP
|
else if((progId & ~0xF0000001ULL) == 0x0004013000001A02LL) //DSP, SAFE_FIRM DSP
|
||||||
{
|
{
|
||||||
static const u8 pattern[] = {
|
static const u8 pattern[] = {
|
||||||
0xE3, 0x10, 0x10, 0x80, 0xE2
|
0xE3, 0x10, 0x10, 0x80, 0xE2
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ enum singleOptions
|
|||||||
PATCHVERSTRING,
|
PATCHVERSTRING,
|
||||||
SHOWGBABOOT,
|
SHOWGBABOOT,
|
||||||
PATCHUNITINFO,
|
PATCHUNITINFO,
|
||||||
DISABLEARM11EXCHANDLERS
|
DISABLEARM11EXCHANDLERS,
|
||||||
|
ENABLESAFEFIRMROSALINA,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern u32 config, multiConfig, bootConfig;
|
extern u32 config, multiConfig, bootConfig;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Open source replacement of the Arm11 PM system module.
|
|||||||
This is licensed under the MIT license.
|
This is licensed under the MIT license.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/AuroraWright/Luma3DS/), build this project and copy the generated CXI file to `/luma/sysmodules/pm.cxi`.
|
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/LumaTeam/Luma3DS/), build this project and copy the generated CXI file to `/luma/sysmodules/pm.cxi`.
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
@fincs
|
@fincs
|
||||||
|
|||||||
@@ -129,16 +129,21 @@ Result GetTitleExHeaderFlags(ExHeader_Arm11CoreInfo *outCoreInfo, ExHeader_Syste
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetCurrentAppTitleIdAndPid(u64 *outTitleId, u32 *outPid)
|
Result GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags)
|
||||||
{
|
{
|
||||||
ProcessList_Lock(&g_manager.processList);
|
ProcessList_Lock(&g_manager.processList);
|
||||||
Result res;
|
Result res;
|
||||||
|
|
||||||
|
memset(outProgramInfo, 0, sizeof(FS_ProgramInfo));
|
||||||
if (g_manager.runningApplicationData != NULL) {
|
if (g_manager.runningApplicationData != NULL) {
|
||||||
*outTitleId = g_manager.runningApplicationData->titleId;
|
ProcessData *app = g_manager.runningApplicationData;
|
||||||
*outPid = g_manager.runningApplicationData->pid;
|
outProgramInfo->programId = app->titleId;
|
||||||
|
outProgramInfo->mediaType = app->mediaType;
|
||||||
|
*outPid = app->pid;
|
||||||
|
*outLaunchFlags = app->launchFlags;
|
||||||
res = 0;
|
res = 0;
|
||||||
} else {
|
} else {
|
||||||
*outTitleId = 0;
|
*outPid = 0;
|
||||||
res = MAKERESULT(RL_TEMPORARY, RS_NOTFOUND, RM_PM, 0x100);
|
res = MAKERESULT(RL_TEMPORARY, RS_NOTFOUND, RM_PM, 0x100);
|
||||||
}
|
}
|
||||||
ProcessList_Unlock(&g_manager.processList);
|
ProcessList_Unlock(&g_manager.processList);
|
||||||
|
|||||||
@@ -12,4 +12,4 @@ Result listMergeUniqueDependencies(ProcessData **procs, u64 *dependencies, u32 *
|
|||||||
Result GetTitleExHeaderFlags(ExHeader_Arm11CoreInfo *outCoreInfo, ExHeader_SystemInfoFlags *outSiFlags, const FS_ProgramInfo *programInfo);
|
Result GetTitleExHeaderFlags(ExHeader_Arm11CoreInfo *outCoreInfo, ExHeader_SystemInfoFlags *outSiFlags, const FS_ProgramInfo *programInfo);
|
||||||
|
|
||||||
// Custom
|
// Custom
|
||||||
Result GetCurrentAppTitleIdAndPid(u64 *outTitleId, u32 *outPid);
|
Result GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags);
|
||||||
|
|||||||
@@ -56,10 +56,12 @@ static Result loadWithoutDependencies(Handle *outDebug, ProcessData **outProcess
|
|||||||
process->pid = pid;
|
process->pid = pid;
|
||||||
process->titleId = exheaderInfo->aci.local_caps.title_id;;
|
process->titleId = exheaderInfo->aci.local_caps.title_id;;
|
||||||
process->programHandle = programHandle;
|
process->programHandle = programHandle;
|
||||||
|
process->launchFlags = launchFlags; // not in official PM
|
||||||
process->flags = 0; // will be filled later
|
process->flags = 0; // will be filled later
|
||||||
process->terminatedNotificationVariation = (launchFlags & 0xF0) >> 4;
|
process->terminatedNotificationVariation = (launchFlags & 0xF0) >> 4;
|
||||||
process->terminationStatus = TERMSTATUS_RUNNING;
|
process->terminationStatus = TERMSTATUS_RUNNING;
|
||||||
process->refcount = 1;
|
process->refcount = 1;
|
||||||
|
process->mediaType = programInfo->mediaType; // not in official PM
|
||||||
|
|
||||||
ProcessList_Unlock(&g_manager.processList);
|
ProcessList_Unlock(&g_manager.processList);
|
||||||
svcSignalEvent(g_manager.newProcessEvent);
|
svcSignalEvent(g_manager.newProcessEvent);
|
||||||
@@ -71,7 +73,11 @@ static Result loadWithoutDependencies(Handle *outDebug, ProcessData **outProcess
|
|||||||
u32 serviceCount;
|
u32 serviceCount;
|
||||||
for(serviceCount = 0; serviceCount < 34 && *(u64 *)localcaps->service_access[serviceCount] != 0; serviceCount++);
|
for(serviceCount = 0; serviceCount < 34 && *(u64 *)localcaps->service_access[serviceCount] != 0; serviceCount++);
|
||||||
|
|
||||||
TRY(FSREG_Register(pid, programHandle, programInfo, &localcaps->storage_info));
|
// Not in official PM: patch local caps to give access to everything
|
||||||
|
ExHeader_Arm11StorageInfo storageInfo = localcaps->storage_info;
|
||||||
|
storageInfo.fs_access_info = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
TRY(FSREG_Register(pid, programHandle, programInfo, &storageInfo));
|
||||||
TRY(SRVPM_RegisterProcess(pid, serviceCount, localcaps->service_access));
|
TRY(SRVPM_RegisterProcess(pid, serviceCount, localcaps->service_access));
|
||||||
|
|
||||||
if (localcaps->reslimit_category <= RESLIMIT_CATEGORY_OTHER) {
|
if (localcaps->reslimit_category <= RESLIMIT_CATEGORY_OTHER) {
|
||||||
@@ -135,6 +141,11 @@ static Result loadWithDependencies(Handle *outDebug, ProcessData **outProcessDat
|
|||||||
process->flags |= PROCESSFLAG_DEPENDENCIES_LOADED;
|
process->flags |= PROCESSFLAG_DEPENDENCIES_LOADED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (launchFlags & PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING) {
|
||||||
|
// See no evil
|
||||||
|
numUnique = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Official pm does this:
|
Official pm does this:
|
||||||
for each dependency:
|
for each dependency:
|
||||||
@@ -207,8 +218,9 @@ static Result launchTitleImpl(Handle *debug, ProcessData **outProcessData, const
|
|||||||
programInfoUpdate = (launchFlags & PMLAUNCHFLAG_USE_UPDATE_TITLE) ? programInfoUpdate : programInfo;
|
programInfoUpdate = (launchFlags & PMLAUNCHFLAG_USE_UPDATE_TITLE) ? programInfoUpdate : programInfo;
|
||||||
TRY(registerProgram(&programHandle, programInfo, programInfoUpdate));
|
TRY(registerProgram(&programHandle, programInfo, programInfoUpdate));
|
||||||
|
|
||||||
|
u32 coreVer = OS_KernelConfig->kernel_syscore_ver;
|
||||||
res = LOADER_GetProgramInfo(exheaderInfo, programHandle);
|
res = LOADER_GetProgramInfo(exheaderInfo, programHandle);
|
||||||
res = R_SUCCEEDED(res) && exheaderInfo->aci.local_caps.core_info.core_version != SYSCOREVER ? (Result)0xC8A05800 : res;
|
res = R_SUCCEEDED(res) && coreVer == 2 && exheaderInfo->aci.local_caps.core_info.core_version != coreVer ? (Result)0xC8A05800 : res;
|
||||||
|
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
LOADER_UnregisterProgram(programHandle);
|
LOADER_UnregisterProgram(programHandle);
|
||||||
@@ -216,21 +228,21 @@ static Result launchTitleImpl(Handle *debug, ProcessData **outProcessData, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Change APPMEMALLOC if needed
|
// Change APPMEMALLOC if needed
|
||||||
if (IS_N3DS && APPMEMTYPE == 6 && (launchFlags & PMLAUNCHFLAG_NORMAL_APPLICATION) != 0) {
|
if (IS_N3DS && OS_KernelConfig->app_memtype == 6 && (launchFlags & PMLAUNCHFLAG_NORMAL_APPLICATION) != 0) {
|
||||||
u32 limitMb;
|
u32 limitMb;
|
||||||
SystemMode n3dsSystemMode = exheaderInfo->aci.local_caps.core_info.n3ds_system_mode;
|
SystemMode n3dsSystemMode = exheaderInfo->aci.local_caps.core_info.n3ds_system_mode;
|
||||||
if ((launchFlags & PMLAUNCHFLAG_FORCE_USE_O3DS_APP_MEM) || n3dsSystemMode == SYSMODE_O3DS_PROD) {
|
bool forceO3dsAppMem = (launchFlags & PMLAUNCHFLAG_FORCE_USE_O3DS_APP_MEM) != 0;
|
||||||
if ((launchFlags & PMLAUNCHFLAG_FORCE_USE_O3DS_APP_MEM) & PMLAUNCHFLAG_FORCE_USE_O3DS_MAX_APP_MEM) {
|
if (forceO3dsAppMem && (launchFlags & PMLAUNCHFLAG_FORCE_USE_O3DS_MAX_APP_MEM) != 0) {
|
||||||
limitMb = 96;
|
setAppMemLimit(96 << 20);
|
||||||
} else {
|
} else if (forceO3dsAppMem || n3dsSystemMode == SYSMODE_O3DS_PROD) {
|
||||||
switch (exheaderInfo->aci.local_caps.core_info.o3ds_system_mode) {
|
switch (exheaderInfo->aci.local_caps.core_info.o3ds_system_mode) {
|
||||||
case SYSMODE_O3DS_PROD: limitMb = 64; break;
|
case SYSMODE_O3DS_PROD: limitMb = 64; break;
|
||||||
case SYSMODE_DEV1: limitMb = 96; break;
|
case SYSMODE_DEV1: limitMb = 96; break;
|
||||||
case SYSMODE_DEV2: limitMb = 80; break;
|
case SYSMODE_DEV2: limitMb = 80; break;
|
||||||
default: limitMb = 0; break;
|
default: limitMb = 0; break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Can be 0:
|
||||||
setAppMemLimit(limitMb << 20);
|
setAppMemLimit(limitMb << 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,9 +333,11 @@ Result LaunchTitle(u32 *outPid, const FS_ProgramInfo *programInfo, u32 launchFla
|
|||||||
|
|
||||||
u32 tidh = (u32)(programInfo->programId >> 32);
|
u32 tidh = (u32)(programInfo->programId >> 32);
|
||||||
u32 tidl = (u32)programInfo->programId;
|
u32 tidl = (u32)programInfo->programId;
|
||||||
if ((tidh == 0x00040030 || tidh == 0x00040130) && (tidl & 0xFF) != SYSCOREVER) {
|
u32 coreVer = OS_KernelConfig->kernel_syscore_ver;
|
||||||
|
if (coreVer == 2 && (tidh == 0x00040030 || tidh == 0x00040130) && (tidl & 0xFF) != coreVer) {
|
||||||
// Panic if launching SAFE_MODE sysmodules or applets (note: exheader syscorever check above only done for applications in official PM)
|
// Panic if launching SAFE_MODE sysmodules or applets (note: exheader syscorever check above only done for applications in official PM)
|
||||||
// Official PM also hardcodes SYSCOREVER = 2 here.
|
// Official PM also hardcodes SYSCOREVER = 2 here.
|
||||||
|
// NATIVE_FIRM-only.
|
||||||
panic(4);
|
panic(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,8 +523,8 @@ Result autolaunchSysmodules(void)
|
|||||||
FS_ProgramInfo programInfo = { .mediaType = MEDIATYPE_NAND };
|
FS_ProgramInfo programInfo = { .mediaType = MEDIATYPE_NAND };
|
||||||
|
|
||||||
// Launch NS
|
// Launch NS
|
||||||
if (NSTID != 0) {
|
if (OS_KernelConfig->ns_tid != 0) {
|
||||||
programInfo.programId = NSTID;
|
programInfo.programId = OS_KernelConfig->ns_tid;
|
||||||
TRY(launchTitleImplWrapper(NULL, NULL, &programInfo, &programInfo, PMLAUNCHFLAG_LOAD_DEPENDENCIES));
|
TRY(launchTitleImplWrapper(NULL, NULL, &programInfo, &programInfo, PMLAUNCHFLAG_LOAD_DEPENDENCIES));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,11 @@
|
|||||||
#include <3ds/services/fs.h>
|
#include <3ds/services/fs.h>
|
||||||
#include "process_data.h"
|
#include "process_data.h"
|
||||||
|
|
||||||
|
/// Custom launch flags for PM launch commands.
|
||||||
|
enum {
|
||||||
|
PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING = BIT(24),
|
||||||
|
};
|
||||||
|
|
||||||
Result LaunchTitle(u32 *outPid, const FS_ProgramInfo *programInfo, u32 launchFlags);
|
Result LaunchTitle(u32 *outPid, const FS_ProgramInfo *programInfo, u32 launchFlags);
|
||||||
Result LaunchTitleUpdate(const FS_ProgramInfo *programInfo, const FS_ProgramInfo *programInfoUpdate, u32 launchFlags);
|
Result LaunchTitleUpdate(const FS_ProgramInfo *programInfo, const FS_ProgramInfo *programInfoUpdate, u32 launchFlags);
|
||||||
Result LaunchApp(const FS_ProgramInfo *programInfo, u32 launchFlags);
|
Result LaunchApp(const FS_ProgramInfo *programInfo, u32 launchFlags);
|
||||||
|
|||||||
@@ -3,11 +3,24 @@
|
|||||||
#include "luma.h"
|
#include "luma.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
bool hasKExt(void)
|
||||||
|
{
|
||||||
|
s64 val;
|
||||||
|
return svcGetSystemInfo(&val, 0x20000, 0) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
u32 getKExtSize(void)
|
u32 getKExtSize(void)
|
||||||
{
|
{
|
||||||
s64 val;
|
s64 val;
|
||||||
Result res = svcGetSystemInfo(&val, 0x10000, 0x300);
|
svcGetSystemInfo(&val, 0x10000, 0x300);
|
||||||
return R_FAILED(res) ? 0 : (u32)val;
|
return (u32)val;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 getStolenSystemMemRegionSize(void)
|
||||||
|
{
|
||||||
|
s64 val;
|
||||||
|
svcGetSystemInfo(&val, 0x10000, 0x301);
|
||||||
|
return (u32)val;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isTitleLaunchPrevented(u64 titleId)
|
bool isTitleLaunchPrevented(u64 titleId)
|
||||||
@@ -15,5 +28,5 @@ bool isTitleLaunchPrevented(u64 titleId)
|
|||||||
s64 numKips = 0;
|
s64 numKips = 0;
|
||||||
|
|
||||||
svcGetSystemInfo(&numKips, 26, 0);
|
svcGetSystemInfo(&numKips, 26, 0);
|
||||||
return numKips >= 6 && (titleId & ~N3DS_TID_BIT) == 0x0004003000008A02ULL; // ErrDisp
|
return numKips >= 6 && (titleId & ~(N3DS_TID_MASK | 1)) == 0x0004003000008A02ULL; // ErrDisp
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,7 @@
|
|||||||
|
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
bool hasKExt(void);
|
||||||
u32 getKExtSize(void);
|
u32 getKExtSize(void);
|
||||||
bool isTitleLaunchPrevented(u64 titleId);
|
u32 getStolenSystemMemRegionSize(void);
|
||||||
|
bool isTitleLaunchPrevented(u64 titleId);
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ void initSystem()
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const ServiceManagerServiceEntry services[] = {
|
static const ServiceManagerServiceEntry services[] = {
|
||||||
{ "pm:app", 3, pmAppHandleCommands, false },
|
{ "pm:app", 4, pmAppHandleCommands, false },
|
||||||
{ "pm:dbg", 2, pmDbgHandleCommands, false },
|
{ "pm:dbg", 2, pmDbgHandleCommands, false },
|
||||||
{ NULL },
|
{ NULL },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,21 @@
|
|||||||
|
|
||||||
Manager g_manager;
|
Manager g_manager;
|
||||||
|
|
||||||
|
static void giveAllFsArchiveAccessToKip(u32 pid, u64 tid)
|
||||||
|
{
|
||||||
|
static const ExHeader_Arm11StorageInfo storageInfo = {
|
||||||
|
.fs_access_info = 0xFFFFFFFF,
|
||||||
|
};
|
||||||
|
static const u64 programHandle = 0xFFFF000000000000LL;
|
||||||
|
|
||||||
|
FS_ProgramInfo info = {
|
||||||
|
.programId = tid,
|
||||||
|
.mediaType = MEDIATYPE_NAND,
|
||||||
|
};
|
||||||
|
|
||||||
|
assertSuccess(FSREG_Register(pid, programHandle, &info, &storageInfo));
|
||||||
|
}
|
||||||
|
|
||||||
void Manager_Init(void *procBuf, size_t numProc)
|
void Manager_Init(void *procBuf, size_t numProc)
|
||||||
{
|
{
|
||||||
memset(&g_manager, 0, sizeof(Manager));
|
memset(&g_manager, 0, sizeof(Manager));
|
||||||
@@ -34,14 +49,23 @@ void Manager_RegisterKips(void)
|
|||||||
process->handle = processHandle;
|
process->handle = processHandle;
|
||||||
process->pid = i;
|
process->pid = i;
|
||||||
process->refcount = 1;
|
process->refcount = 1;
|
||||||
process->titleId = 0x0004000100001000ULL; // note: same TID for all builtins
|
process->titleId = 0x0004000100001000ULL; // note: same internal TID for all builtins
|
||||||
process->flags = PROCESSFLAG_KIP;
|
process->flags = PROCESSFLAG_KIP;
|
||||||
process->terminationStatus = TERMSTATUS_RUNNING;
|
process->terminationStatus = TERMSTATUS_RUNNING;
|
||||||
|
|
||||||
assertSuccess(svcSetProcessResourceLimits(processHandle, g_manager.reslimits[RESLIMIT_CATEGORY_OTHER]));
|
if (i < 5) {
|
||||||
|
// Exempt rosalina from being resource-limited at all
|
||||||
|
assertSuccess(svcSetProcessResourceLimits(processHandle, g_manager.reslimits[RESLIMIT_CATEGORY_OTHER]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessList_Unlock(&g_manager.processList);
|
ProcessList_Unlock(&g_manager.processList);
|
||||||
|
|
||||||
|
// Give full archive access to us (PM) and Rosalina (real PIDs don't matter, they just have to be unique (?))
|
||||||
|
// Loader doesn't depend on PM and has its own fs:REG handle so it must do it itself.
|
||||||
|
giveAllFsArchiveAccessToKip(2, 0x0004013000001202LL); // PM
|
||||||
|
if (numKips > 5) {
|
||||||
|
giveAllFsArchiveAccessToKip(5, 0x0004013000006902LL); // Rosalina
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UnregisterProcess(u64 titleId)
|
Result UnregisterProcess(u64 titleId)
|
||||||
@@ -66,3 +90,27 @@ Result UnregisterProcess(u64 titleId)
|
|||||||
ProcessList_Unlock(&g_manager.processList);
|
ProcessList_Unlock(&g_manager.processList);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result PrepareToChainloadHomebrew(u64 titleId)
|
||||||
|
{
|
||||||
|
// Note: I'm allowing this command to be called for non-applications, maybe that'll be useful
|
||||||
|
// in the future...
|
||||||
|
|
||||||
|
ProcessData *foundProcess = NULL;
|
||||||
|
Result res;
|
||||||
|
ProcessList_Lock(&g_manager.processList);
|
||||||
|
foundProcess = ProcessList_FindProcessByTitleId(&g_manager.processList, titleId & ~N3DS_TID_MASK);
|
||||||
|
if (foundProcess != NULL) {
|
||||||
|
// Clear the "notify on termination, don't cleanup" flag, so that for ex. APT isn't notified & no need for UnregisterProcess,
|
||||||
|
// and the "dependencies loaded" flag, so that the dependencies aren't killed (for ex. when
|
||||||
|
// booting hbmenu instead of Home Menu, in which case the same title is going to be launched...)
|
||||||
|
|
||||||
|
foundProcess->flags &= ~(PROCESSFLAG_DEPENDENCIES_LOADED | PROCESSFLAG_NOTIFY_TERMINATION);
|
||||||
|
res = 0;
|
||||||
|
} else {
|
||||||
|
res = MAKERESULT(RL_TEMPORARY, RS_NOTFOUND, RM_PM, 0x100);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessList_Unlock(&g_manager.processList);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,3 +21,4 @@ extern Manager g_manager;
|
|||||||
void Manager_Init(void *procBuf, size_t numProc);
|
void Manager_Init(void *procBuf, size_t numProc);
|
||||||
void Manager_RegisterKips(void);
|
void Manager_RegisterKips(void);
|
||||||
Result UnregisterProcess(u64 titleId);
|
Result UnregisterProcess(u64 titleId);
|
||||||
|
Result PrepareToChainloadHomebrew(u64 titleId);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "launch.h"
|
#include "launch.h"
|
||||||
#include "info.h"
|
#include "info.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "manager.h"
|
||||||
|
|
||||||
void pmDbgHandleCommands(void *ctx)
|
void pmDbgHandleCommands(void *ctx)
|
||||||
{
|
{
|
||||||
@@ -11,10 +12,10 @@ void pmDbgHandleCommands(void *ctx)
|
|||||||
u32 cmdhdr = cmdbuf[0];
|
u32 cmdhdr = cmdbuf[0];
|
||||||
|
|
||||||
FS_ProgramInfo programInfo;
|
FS_ProgramInfo programInfo;
|
||||||
Handle debug;
|
|
||||||
|
|
||||||
u64 titleId;
|
u64 titleId;
|
||||||
|
Handle debug;
|
||||||
u32 pid;
|
u32 pid;
|
||||||
|
u32 launchFlags;
|
||||||
|
|
||||||
switch (cmdhdr >> 16) {
|
switch (cmdhdr >> 16) {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -40,12 +41,11 @@ void pmDbgHandleCommands(void *ctx)
|
|||||||
|
|
||||||
// Custom
|
// Custom
|
||||||
case 0x100:
|
case 0x100:
|
||||||
titleId = 0;
|
cmdbuf[1] = GetCurrentAppInfo(&programInfo, &pid, &launchFlags);
|
||||||
pid = 0xFFFFFFFF;
|
cmdbuf[0] = IPC_MakeHeader(0x100, 7, 0);
|
||||||
cmdbuf[1] = GetCurrentAppTitleIdAndPid(&titleId, &pid);
|
memcpy(cmdbuf + 2, &programInfo, sizeof(FS_ProgramInfo));
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x100, 4, 0);
|
cmdbuf[6] = pid;
|
||||||
memcpy(cmdbuf + 2, &titleId, 8);
|
cmdbuf[7] = launchFlags;
|
||||||
cmdbuf[4] = pid;
|
|
||||||
break;
|
break;
|
||||||
case 0x101:
|
case 0x101:
|
||||||
cmdbuf[1] = DebugNextApplicationByForce(cmdbuf[1] != 0);
|
cmdbuf[1] = DebugNextApplicationByForce(cmdbuf[1] != 0);
|
||||||
@@ -59,7 +59,11 @@ void pmDbgHandleCommands(void *ctx)
|
|||||||
cmdbuf[2] = IPC_Desc_MoveHandles(1);
|
cmdbuf[2] = IPC_Desc_MoveHandles(1);
|
||||||
cmdbuf[3] = debug;
|
cmdbuf[3] = debug;
|
||||||
break;
|
break;
|
||||||
|
case 0x103:
|
||||||
|
memcpy(&titleId, cmdbuf + 1, 8);
|
||||||
|
cmdbuf[1] = PrepareToChainloadHomebrew(titleId);
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x103, 1, 0);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
|
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
|
||||||
cmdbuf[1] = 0xD900182F;
|
cmdbuf[1] = 0xD900182F;
|
||||||
|
|||||||
@@ -29,10 +29,12 @@ typedef struct ProcessData {
|
|||||||
u32 pid;
|
u32 pid;
|
||||||
u64 titleId;
|
u64 titleId;
|
||||||
u64 programHandle;
|
u64 programHandle;
|
||||||
|
u32 launchFlags;
|
||||||
u8 flags;
|
u8 flags;
|
||||||
u8 terminatedNotificationVariation;
|
u8 terminatedNotificationVariation;
|
||||||
TerminationStatus terminationStatus;
|
TerminationStatus terminationStatus;
|
||||||
u8 refcount;
|
u8 refcount;
|
||||||
|
FS_MediaType mediaType;
|
||||||
} ProcessData;
|
} ProcessData;
|
||||||
|
|
||||||
typedef struct ProcessList {
|
typedef struct ProcessList {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ static void cleanupProcess(ProcessData *process)
|
|||||||
|
|
||||||
ProcessList_Lock(&g_manager.processList);
|
ProcessList_Lock(&g_manager.processList);
|
||||||
if (g_manager.runningApplicationData != NULL && process->handle == g_manager.runningApplicationData->handle) {
|
if (g_manager.runningApplicationData != NULL && process->handle == g_manager.runningApplicationData->handle) {
|
||||||
if (IS_N3DS && APPMEMTYPE == 6) {
|
if (IS_N3DS && OS_KernelConfig->app_memtype == 6) {
|
||||||
assertSuccess(resetAppMemLimit());
|
assertSuccess(resetAppMemLimit());
|
||||||
}
|
}
|
||||||
g_manager.runningApplicationData = NULL;
|
g_manager.runningApplicationData = NULL;
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
#include "luma.h"
|
#include "luma.h"
|
||||||
|
|
||||||
|
#define CPUTIME_MULTI_MASK BIT(7)
|
||||||
|
#define CPUTIME_SINGLE_MASK 0
|
||||||
|
|
||||||
typedef s64 ReslimitValues[10];
|
typedef s64 ReslimitValues[10];
|
||||||
|
|
||||||
static const ResourceLimitType g_reslimitInitOrder[10] = {
|
static const ResourceLimitType g_reslimitInitOrder[10] = {
|
||||||
@@ -189,20 +192,23 @@ static ReslimitValues g_n3dsReslimitValues[4] = {
|
|||||||
Both modes pause threads they don't want to run in thread selection, and unpause them when needed.
|
Both modes pause threads they don't want to run in thread selection, and unpause them when needed.
|
||||||
If the threads that are intended to be paused is running an SVC, the pause will happen *after* SVC return.
|
If the threads that are intended to be paused is running an SVC, the pause will happen *after* SVC return.
|
||||||
|
|
||||||
Mode0 (unsure)
|
Mode0 "multi"
|
||||||
|
|
||||||
Starting by "sysmodule" threads, alternatively allow (if preemptible) only sysmodule threads,
|
Starting by "sysmodule" threads, alternatively allow (if preemptible) only sysmodule threads,
|
||||||
and then only application threads to run.
|
and then only application threads to run.
|
||||||
The latter has an exception; if "sysmodule" threads have run for less than 2usec, they
|
The latter has an exception; if "sysmodule" threads have run for less than 8usec (value is a kernel bug), they
|
||||||
are unpaused an allowed to run instead.
|
are unpaused an allowed to run instead.
|
||||||
|
|
||||||
This happens at a rate of 1ms * (cpuTime/100).
|
This happens at a rate of 2ms * (cpuTime/100).
|
||||||
|
|
||||||
Mode1
|
|
||||||
|
Mode1 "single"
|
||||||
|
|
||||||
|
This mode is half-broken due to a kernel bug (when "current thread" is the priority 0 kernel thread).
|
||||||
|
|
||||||
When this mode is enabled, only one application thread is allowed to be created on core1.
|
When this mode is enabled, only one application thread is allowed to be created on core1.
|
||||||
|
|
||||||
This divides the core1 time into slices of 12.5ms.
|
This divides the core1 time into slices of 25ms.
|
||||||
|
|
||||||
The "application" thread is given cpuTime% of the slice.
|
The "application" thread is given cpuTime% of the slice.
|
||||||
The "sysmodules" threads are given a total of (90 - cpuTime)% of the slice.
|
The "sysmodules" threads are given a total of (90 - cpuTime)% of the slice.
|
||||||
@@ -243,7 +249,11 @@ static ReslimitValues *fixupReslimitValues(void)
|
|||||||
{
|
{
|
||||||
// In order: APPLICATION, SYS_APPLET, LIB_APPLET, OTHER
|
// In order: APPLICATION, SYS_APPLET, LIB_APPLET, OTHER
|
||||||
// Fixup "commit" reslimit
|
// Fixup "commit" reslimit
|
||||||
u32 sysmemalloc = SYSMEMALLOC + getKExtSize();
|
|
||||||
|
// Note: we lie in the reslimit and make as if neither KExt nor Roslina existed, to avoid breakage
|
||||||
|
|
||||||
|
u32 appmemalloc = OS_KernelConfig->memregion_sz[0];
|
||||||
|
u32 sysmemalloc = OS_KernelConfig->memregion_sz[1] + (hasKExt() ? getStolenSystemMemRegionSize() : 0);
|
||||||
ReslimitValues *values = !IS_N3DS ? g_o3dsReslimitValues : g_n3dsReslimitValues;
|
ReslimitValues *values = !IS_N3DS ? g_o3dsReslimitValues : g_n3dsReslimitValues;
|
||||||
|
|
||||||
static const u32 minAppletMemAmount = 0x1200000;
|
static const u32 minAppletMemAmount = 0x1200000;
|
||||||
@@ -252,7 +262,7 @@ static ReslimitValues *fixupReslimitValues(void)
|
|||||||
u32 baseRegionSize = !IS_N3DS ? 0x1400000 : 0x2000000;
|
u32 baseRegionSize = !IS_N3DS ? 0x1400000 : 0x2000000;
|
||||||
|
|
||||||
if (sysmemalloc < minAppletMemAmount) {
|
if (sysmemalloc < minAppletMemAmount) {
|
||||||
values[1][0] = SYSMEMALLOC - minAppletMemAmount / 3;
|
values[1][0] = sysmemalloc - minAppletMemAmount / 3;
|
||||||
values[2][0] = 0;
|
values[2][0] = 0;
|
||||||
values[3][0] = baseRegionSize + otherMinOvercommitAmount;
|
values[3][0] = baseRegionSize + otherMinOvercommitAmount;
|
||||||
} else {
|
} else {
|
||||||
@@ -262,8 +272,8 @@ static ReslimitValues *fixupReslimitValues(void)
|
|||||||
values[3][0] = baseRegionSize + (otherMinOvercommitAmount + excess / 4);
|
values[3][0] = baseRegionSize + (otherMinOvercommitAmount + excess / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
values[0][0] = APPMEMALLOC;
|
values[0][0] = appmemalloc;
|
||||||
g_defaultAppMemLimit = APPMEMALLOC;
|
g_defaultAppMemLimit = appmemalloc;
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
@@ -315,22 +325,23 @@ void setAppCpuTimeLimitAndSchedModeFromDescriptor(u64 titleId, u16 descriptor)
|
|||||||
- app has a non-0 cputime descriptor in exhdr: maximum core1 cputime reslimit and scheduling
|
- app has a non-0 cputime descriptor in exhdr: maximum core1 cputime reslimit and scheduling
|
||||||
mode are set according to it. Current reslimit is set to 0. SetAppResourceLimit *is* needed
|
mode are set according to it. Current reslimit is set to 0. SetAppResourceLimit *is* needed
|
||||||
to use core1.
|
to use core1.
|
||||||
- app has a 0 cputime descriptor: maximum is set to 80.
|
- app has a 0 cputime descriptor: maximum is set to 80, scheduling mode to "single" (broken).
|
||||||
Current reslimit is set to 0, and SetAppResourceLimit *is* needed
|
Current reslimit is set to 0, and SetAppResourceLimit *is* also needed
|
||||||
to use core1, **EXCEPT** for an hardcoded set of titles.
|
to use core1, **EXCEPT** for an hardcoded set of titles.
|
||||||
*/
|
*/
|
||||||
u8 cpuTime = (u8)descriptor;
|
u8 cpuTime = (u8)descriptor;
|
||||||
assertSuccess(setAppCpuTimeLimit(0)); // remove preemption first.
|
assertSuccess(setAppCpuTimeLimit(0)); // remove preemption first.
|
||||||
|
|
||||||
g_manager.cpuTimeBase = 0;
|
g_manager.cpuTimeBase = 0;
|
||||||
|
u32 currentValueToSet = g_manager.cpuTimeBase; // 0
|
||||||
|
|
||||||
if (cpuTime != 0) {
|
if (cpuTime == 0) {
|
||||||
// Set core1 scheduling mode
|
// 2.0 apps have this exheader field correctly filled, very often to 0x9E (1.0 titles don't).
|
||||||
g_manager.maxAppCpuTime = cpuTime & 0x7F;
|
|
||||||
assertSuccess(svcKernelSetState(6, 3, (cpuTime & 0x80) ? 0LL : 1LL));
|
|
||||||
} else {
|
|
||||||
u32 titleUid = ((u32)titleId >> 8) & 0xFFFFF;
|
u32 titleUid = ((u32)titleId >> 8) & 0xFFFFF;
|
||||||
g_manager.maxAppCpuTime = 80;
|
|
||||||
|
// Default setting is 80% max "single", with a current value of 0
|
||||||
|
cpuTime = CPUTIME_SINGLE_MASK | 80;
|
||||||
|
|
||||||
static const u32 numOverrides = sizeof(g_startCpuTimeOverrides) / sizeof(g_startCpuTimeOverrides[0]);
|
static const u32 numOverrides = sizeof(g_startCpuTimeOverrides) / sizeof(g_startCpuTimeOverrides[0]);
|
||||||
|
|
||||||
if (titleUid >= g_startCpuTimeOverrides[0].titleUid && titleUid <= g_startCpuTimeOverrides[numOverrides - 1].titleUid) {
|
if (titleUid >= g_startCpuTimeOverrides[0].titleUid && titleUid <= g_startCpuTimeOverrides[numOverrides - 1].titleUid) {
|
||||||
@@ -338,15 +349,26 @@ void setAppCpuTimeLimitAndSchedModeFromDescriptor(u64 titleId, u16 descriptor)
|
|||||||
for (u32 i = 0; i < numOverrides && titleUid < g_startCpuTimeOverrides[i].titleUid; i++);
|
for (u32 i = 0; i < numOverrides && titleUid < g_startCpuTimeOverrides[i].titleUid; i++);
|
||||||
if (i < numOverrides) {
|
if (i < numOverrides) {
|
||||||
if (g_startCpuTimeOverrides[i].value > 100 && g_startCpuTimeOverrides[i].value < 200) {
|
if (g_startCpuTimeOverrides[i].value > 100 && g_startCpuTimeOverrides[i].value < 200) {
|
||||||
assertSuccess(svcKernelSetState(6, 3, 0LL));
|
cpuTime = CPUTIME_MULTI_MASK | 80; // "multi", max 80%
|
||||||
assertSuccess(setAppCpuTimeLimit(g_startCpuTimeOverrides[i].value - 100));
|
currentValueToSet = g_startCpuTimeOverrides[i].value - 100;
|
||||||
} else {
|
} else {
|
||||||
assertSuccess(svcKernelSetState(6, 3, 1LL));
|
cpuTime = CPUTIME_SINGLE_MASK | 80; // "single", max 80%
|
||||||
assertSuccess(setAppCpuTimeLimit(g_startCpuTimeOverrides[i].value));
|
currentValueToSet = g_startCpuTimeOverrides[i].value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set core1 scheduling mode
|
||||||
|
assertSuccess(svcKernelSetState(6, 3, (cpuTime & CPUTIME_MULTI_MASK) ? 0LL : 1LL));
|
||||||
|
|
||||||
|
// Set max value (limit)
|
||||||
|
g_manager.maxAppCpuTime = cpuTime & 0x7F;
|
||||||
|
|
||||||
|
// Set current value (for 1.0 apps)
|
||||||
|
if (currentValueToSet != 0) {
|
||||||
|
assertSuccess(setAppCpuTimeLimit(currentValueToSet));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result SetAppResourceLimit(u32 mbz, ResourceLimitType category, u32 value, u64 mbz2)
|
Result SetAppResourceLimit(u32 mbz, ResourceLimitType category, u32 value, u64 mbz2)
|
||||||
|
|||||||
@@ -6,30 +6,10 @@
|
|||||||
#include "exheader_info_heap.h"
|
#include "exheader_info_heap.h"
|
||||||
#include "task_runner.h"
|
#include "task_runner.h"
|
||||||
|
|
||||||
static Result fsRegSetupPermissions(void)
|
|
||||||
{
|
|
||||||
u32 pid;
|
|
||||||
Result res;
|
|
||||||
FS_ProgramInfo info;
|
|
||||||
|
|
||||||
ExHeader_Arm11StorageInfo storageInfo = {
|
|
||||||
.fs_access_info = FSACCESS_SDMC_RW,
|
|
||||||
};
|
|
||||||
|
|
||||||
info.programId = 0x0004013000001202LL; // PM's TID
|
|
||||||
info.mediaType = MEDIATYPE_NAND;
|
|
||||||
|
|
||||||
if(R_SUCCEEDED(res = svcGetProcessId(&pid, CUR_PROCESS_HANDLE)))
|
|
||||||
res = FSREG_Register(pid, 0xFFFF000000000000LL, &info, &storageInfo);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void forceMountSdCard(void)
|
void forceMountSdCard(void)
|
||||||
{
|
{
|
||||||
FS_Archive sdmcArchive;
|
FS_Archive sdmcArchive;
|
||||||
|
|
||||||
assertSuccess(fsRegSetupPermissions());
|
|
||||||
assertSuccess(fsInit());
|
assertSuccess(fsInit());
|
||||||
assertSuccess(FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")));
|
assertSuccess(FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")));
|
||||||
// No need to clean up things as we will firmlaunch straight away
|
// No need to clean up things as we will firmlaunch straight away
|
||||||
@@ -311,6 +291,11 @@ ProcessData *terminateAllProcesses(u32 callerPid, s64 timeout)
|
|||||||
|
|
||||||
ProcessList_Lock(&g_manager.processList);
|
ProcessList_Lock(&g_manager.processList);
|
||||||
|
|
||||||
|
// Send custom notification to at least Rosalina to make it relinquish some non-KIP services handles, stop the debugger, etc.
|
||||||
|
if (numKips >= 6) {
|
||||||
|
notifySubscribers(0x2000);
|
||||||
|
}
|
||||||
|
|
||||||
// Send notification 0x100 to the currently running application
|
// Send notification 0x100 to the currently running application
|
||||||
if (g_manager.runningApplicationData != NULL) {
|
if (g_manager.runningApplicationData != NULL) {
|
||||||
g_manager.runningApplicationData->flags &= ~PROCESSFLAG_DEPENDENCIES_LOADED;
|
g_manager.runningApplicationData->flags &= ~PROCESSFLAG_DEPENDENCIES_LOADED;
|
||||||
|
|||||||
@@ -8,13 +8,7 @@
|
|||||||
#define REG32(reg) (*(vu32 *)reg)
|
#define REG32(reg) (*(vu32 *)reg)
|
||||||
#define REG64(reg) (*(vu64 *)reg)
|
#define REG64(reg) (*(vu64 *)reg)
|
||||||
|
|
||||||
#define NSTID REG64(0x1FF80008)
|
#define IS_N3DS (OS_KernelConfig->app_memtype >= 6) // APPMEMTYPE. Hacky but doesn't use APT
|
||||||
#define SYSCOREVER REG32(0x1FF80010)
|
|
||||||
#define APPMEMTYPE REG32(0x1FF80030)
|
|
||||||
#define APPMEMALLOC REG32(0x1FF80040)
|
|
||||||
#define SYSMEMALLOC REG32(0x1FF80044)
|
|
||||||
|
|
||||||
#define IS_N3DS (*(vu32 *)0x1FF80030 >= 6) // APPMEMTYPE. Hacky but doesn't use APT
|
|
||||||
#define N3DS_TID_MASK 0xF0000000ULL
|
#define N3DS_TID_MASK 0xF0000000ULL
|
||||||
#define N3DS_TID_BIT 0x20000000ULL
|
#define N3DS_TID_BIT 0x20000000ULL
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Open source replacement of the Arm11 PXI system module.
|
|||||||
This is licensed under the MIT license.
|
This is licensed under the MIT license.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/AuroraWright/Luma3DS/) and copy pxi.cxi to /luma/sysmodules/.
|
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/LumaTeam/Luma3DS/) and copy pxi.cxi to /luma/sysmodules/.
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
This list is not complete at all:
|
This list is not complete at all:
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ static inline void exitPXI(void)
|
|||||||
static u8 ALIGN(8) receiverStack[THREAD_STACK_SIZE];
|
static u8 ALIGN(8) receiverStack[THREAD_STACK_SIZE];
|
||||||
static u8 ALIGN(8) senderStack[THREAD_STACK_SIZE];
|
static u8 ALIGN(8) senderStack[THREAD_STACK_SIZE];
|
||||||
static u8 ALIGN(8) PXISRV11HandlerStack[THREAD_STACK_SIZE];
|
static u8 ALIGN(8) PXISRV11HandlerStack[THREAD_STACK_SIZE];
|
||||||
|
static MyThread receiverThread = {0}, senderThread = {0}, PXISRV11HandlerThread = {0};
|
||||||
|
|
||||||
Result __sync_init(void);
|
Result __sync_init(void);
|
||||||
Result __sync_fini(void);
|
Result __sync_fini(void);
|
||||||
@@ -152,7 +153,6 @@ void initSystem(void)
|
|||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
Handle handles[10] = {0}; //notification handle + service handles
|
Handle handles[10] = {0}; //notification handle + service handles
|
||||||
MyThread receiverThread = {0}, senderThread = {0}, PXISRV11HandlerThread = {0};
|
|
||||||
|
|
||||||
for(u32 i = 0; i < 9; i++)
|
for(u32 i = 0; i < 9; i++)
|
||||||
assertSuccess(srvRegisterService(handles + 1 + i, serviceNames[i], 1));
|
assertSuccess(srvRegisterService(handles + 1 + i, serviceNames[i], 1));
|
||||||
|
|||||||
@@ -130,6 +130,8 @@ $(OUTPUT).elf : $(OFILES)
|
|||||||
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||||
@$(NM) -CSn $@ > $(notdir $*.lst)
|
@$(NM) -CSn $@ > $(notdir $*.lst)
|
||||||
|
|
||||||
|
draw.o: CFLAGS += -O3
|
||||||
|
|
||||||
$(OFILES_SRC) : $(HFILES_BIN)
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -54,9 +54,14 @@
|
|||||||
#define GPU_TRANSFER_CNT REG32(0x10400C18)
|
#define GPU_TRANSFER_CNT REG32(0x10400C18)
|
||||||
#define GPU_CMDLIST_CNT REG32(0x104018F0)
|
#define GPU_CMDLIST_CNT REG32(0x104018F0)
|
||||||
|
|
||||||
|
#define LCD_TOP_BRIGHTNESS REG32(0x10202240)
|
||||||
|
#define LCD_BOT_BRIGHTNESS REG32(0x10202A40)
|
||||||
|
|
||||||
#define FB_BOTTOM_VRAM_ADDR ((void *)0x1F48F000) // cached
|
#define FB_BOTTOM_VRAM_ADDR ((void *)0x1F48F000) // cached
|
||||||
#define FB_BOTTOM_VRAM_PA 0x1848F000
|
#define FB_BOTTOM_VRAM_PA 0x1848F000
|
||||||
#define FB_BOTTOM_SIZE (320 * 240 * 2)
|
#define FB_BOTTOM_SIZE (320 * 240 * 2)
|
||||||
|
#define FB_SCREENSHOT_SIZE (52 + 400 * 240 * 3)
|
||||||
|
|
||||||
|
|
||||||
#define SCREEN_BOT_WIDTH 320
|
#define SCREEN_BOT_WIDTH 320
|
||||||
#define SCREEN_BOT_HEIGHT 240
|
#define SCREEN_BOT_HEIGHT 240
|
||||||
@@ -72,19 +77,30 @@
|
|||||||
|
|
||||||
#define DRAW_MAX_FORMATTED_STRING_SIZE 512
|
#define DRAW_MAX_FORMATTED_STRING_SIZE 512
|
||||||
|
|
||||||
|
void Draw_Init(void);
|
||||||
|
|
||||||
void Draw_Lock(void);
|
void Draw_Lock(void);
|
||||||
void Draw_Unlock(void);
|
void Draw_Unlock(void);
|
||||||
|
|
||||||
void Draw_DrawCharacter(u32 posX, u32 posY, u32 color, char character);
|
void Draw_DrawCharacter(u32 posX, u32 posY, u32 color, char character);
|
||||||
u32 Draw_DrawString(u32 posX, u32 posY, u32 color, const char *string);
|
u32 Draw_DrawString(u32 posX, u32 posY, u32 color, const char *string);
|
||||||
|
|
||||||
|
__attribute__((format(printf,4,5)))
|
||||||
u32 Draw_DrawFormattedString(u32 posX, u32 posY, u32 color, const char *fmt, ...);
|
u32 Draw_DrawFormattedString(u32 posX, u32 posY, u32 color, const char *fmt, ...);
|
||||||
|
|
||||||
void Draw_FillFramebuffer(u32 value);
|
void Draw_FillFramebuffer(u32 value);
|
||||||
void Draw_ClearFramebuffer(void);
|
void Draw_ClearFramebuffer(void);
|
||||||
void Draw_SetupFramebuffer(void);
|
Result Draw_AllocateFramebufferCache(u32 size);
|
||||||
|
Result Draw_AllocateFramebufferCacheForScreenshot(u32 size);
|
||||||
|
void Draw_FreeFramebufferCache(void);
|
||||||
|
void *Draw_GetFramebufferCache(void);
|
||||||
|
u32 Draw_GetFramebufferCacheSize(void);
|
||||||
|
u32 Draw_SetupFramebuffer(void);
|
||||||
void Draw_RestoreFramebuffer(void);
|
void Draw_RestoreFramebuffer(void);
|
||||||
void Draw_FlushFramebuffer(void);
|
void Draw_FlushFramebuffer(void);
|
||||||
u32 Draw_GetCurrentFramebufferAddress(bool top, bool left);
|
u32 Draw_GetCurrentFramebufferAddress(bool top, bool left);
|
||||||
|
// Width is actually height as the 3ds screen is rotated 90 degrees
|
||||||
|
void Draw_GetCurrentScreenInfo(u32 *width, bool *is3d, bool top);
|
||||||
|
|
||||||
void Draw_CreateBitmapHeader(u8 *dst, u32 width, u32 heigth);
|
void Draw_CreateBitmapHeader(u8 *dst, u32 width, u32 heigth);
|
||||||
void Draw_ConvertFrameBufferLine(u8 *line, bool top, bool left, u32 y);
|
void Draw_ConvertFrameBufferLines(u8 *buf, u32 width, u32 startingLine, u32 numLines, bool top, bool left);
|
||||||
|
|||||||
@@ -31,6 +31,6 @@
|
|||||||
#include "MyThread.h"
|
#include "MyThread.h"
|
||||||
|
|
||||||
#define HBLDR_DEFAULT_3DSX_TID 0x000400000D921E00ULL
|
#define HBLDR_DEFAULT_3DSX_TID 0x000400000D921E00ULL
|
||||||
#define HBLDR_3DSX_TID (*(vu64 *)0x1FF81100)
|
|
||||||
|
|
||||||
|
void HBLDR_RestartHbApplication(void *p);
|
||||||
void HBLDR_HandleCommands(void *ctx);
|
void HBLDR_HandleCommands(void *ctx);
|
||||||
|
|||||||
@@ -36,4 +36,5 @@ extern int inputRedirectionStartResult;
|
|||||||
|
|
||||||
MyThread *inputRedirectionCreateThread(void);
|
MyThread *inputRedirectionCreateThread(void);
|
||||||
void inputRedirectionThreadMain(void);
|
void inputRedirectionThreadMain(void);
|
||||||
|
Result InputRedirection_Disable(s64 timeout);
|
||||||
Result InputRedirection_DoOrUndoPatches(void);
|
Result InputRedirection_DoOrUndoPatches(void);
|
||||||
|
|||||||
30
sysmodules/rosalina/include/luma_shared_config.h
Normal file
30
sysmodules/rosalina/include/luma_shared_config.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/* This paricular file is licensed under the following terms: */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable
|
||||||
|
* for any damages arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it
|
||||||
|
* and redistribute it freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||||
|
* If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||||
|
*
|
||||||
|
* Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||||
|
* This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
/// Luma shared config type.
|
||||||
|
typedef struct LumaSharedConfig {
|
||||||
|
u64 hbldr_3dsx_tid; ///< Title ID to use for 3DSX loading.
|
||||||
|
bool use_hbldr; ///< Whether or not Loader should use hb:ldr (Rosalina writes 1).
|
||||||
|
} LumaSharedConfig;
|
||||||
|
|
||||||
|
/// Luma shared config.
|
||||||
|
#define Luma_SharedConfig ((volatile LumaSharedConfig *)(OS_SHAREDCFG_VADDR + 0x800))
|
||||||
33
sysmodules/rosalina/include/luminance.h
Normal file
33
sysmodules/rosalina/include/luminance.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Luma3DS
|
||||||
|
* Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
u32 getMinLuminancePreset(void);
|
||||||
|
u32 getMaxLuminancePreset(void);
|
||||||
|
u32 getCurrentLuminance(bool top);
|
||||||
@@ -27,33 +27,25 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
|
#include <3ds/services/hid.h>
|
||||||
#include "MyThread.h"
|
#include "MyThread.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#define HID_PAD (REG32(0x10146000) ^ 0xFFF)
|
#define HID_PAD (REG32(0x10146000) ^ 0xFFF)
|
||||||
|
|
||||||
#define BUTTON_A (1 << 0)
|
|
||||||
#define BUTTON_B (1 << 1)
|
|
||||||
#define BUTTON_SELECT (1 << 2)
|
|
||||||
#define BUTTON_START (1 << 3)
|
|
||||||
#define BUTTON_RIGHT (1 << 4)
|
|
||||||
#define BUTTON_LEFT (1 << 5)
|
|
||||||
#define BUTTON_UP (1 << 6)
|
|
||||||
#define BUTTON_DOWN (1 << 7)
|
|
||||||
#define BUTTON_R1 (1 << 8)
|
|
||||||
#define BUTTON_L1 (1 << 9)
|
|
||||||
#define BUTTON_X (1 << 10)
|
|
||||||
#define BUTTON_Y (1 << 11)
|
|
||||||
|
|
||||||
#define DEFAULT_MENU_COMBO (BUTTON_L1 | BUTTON_DOWN | BUTTON_SELECT)
|
#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_APPLICATION 0
|
||||||
#define CORE_SYSTEM 1
|
#define CORE_SYSTEM 1
|
||||||
|
|
||||||
typedef enum MenuItemAction {
|
typedef enum MenuItemAction {
|
||||||
METHOD,
|
MENU_END = 0,
|
||||||
MENU
|
METHOD = 1,
|
||||||
|
MENU = 2,
|
||||||
} MenuItemAction;
|
} MenuItemAction;
|
||||||
|
|
||||||
typedef struct MenuItem {
|
typedef struct MenuItem {
|
||||||
const char *title;
|
const char *title;
|
||||||
|
|
||||||
@@ -62,25 +54,32 @@ typedef struct MenuItem {
|
|||||||
struct Menu *menu;
|
struct Menu *menu;
|
||||||
void (*method)(void);
|
void (*method)(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool (*visibility)(void);
|
||||||
} MenuItem;
|
} MenuItem;
|
||||||
|
|
||||||
typedef struct Menu {
|
typedef struct Menu {
|
||||||
const char *title;
|
const char *title;
|
||||||
|
|
||||||
u32 nbItems;
|
MenuItem items[16];
|
||||||
MenuItem items[0x40];
|
|
||||||
} Menu;
|
} Menu;
|
||||||
|
|
||||||
extern bool terminationRequest;
|
extern bool isN3DS;
|
||||||
extern Handle terminationRequestEvent;
|
extern bool menuShouldExit;
|
||||||
|
extern bool preTerminationRequested;
|
||||||
|
extern Handle preTerminationEvent;
|
||||||
|
|
||||||
extern u32 menuCombo;
|
extern u32 menuCombo;
|
||||||
|
|
||||||
u32 waitInputWithTimeout(u32 msec);
|
u32 waitInputWithTimeout(s32 msec);
|
||||||
u32 waitInput(void);
|
u32 waitInput(void);
|
||||||
|
|
||||||
u32 waitComboWithTimeout(u32 msec);
|
u32 waitComboWithTimeout(s32 msec);
|
||||||
u32 waitCombo(void);
|
u32 waitCombo(void);
|
||||||
|
|
||||||
|
bool menuCheckN3ds(void);
|
||||||
|
u32 menuCountItems(const Menu *menu);
|
||||||
|
|
||||||
MyThread *menuCreateThread(void);
|
MyThread *menuCreateThread(void);
|
||||||
void menuEnter(void);
|
void menuEnter(void);
|
||||||
void menuLeave(void);
|
void menuLeave(void);
|
||||||
|
|||||||
@@ -33,8 +33,12 @@
|
|||||||
extern Menu rosalinaMenu;
|
extern Menu rosalinaMenu;
|
||||||
|
|
||||||
void RosalinaMenu_TakeScreenshot(void);
|
void RosalinaMenu_TakeScreenshot(void);
|
||||||
|
void RosalinaMenu_ChangeScreenBrightness(void);
|
||||||
void RosalinaMenu_ShowCredits(void);
|
void RosalinaMenu_ShowCredits(void);
|
||||||
void RosalinaMenu_ProcessList(void);
|
void RosalinaMenu_ProcessList(void);
|
||||||
void RosalinaMenu_PowerOff(void);
|
void RosalinaMenu_PowerOff(void);
|
||||||
void RosalinaMenu_Reboot(void);
|
void RosalinaMenu_Reboot(void);
|
||||||
void RosalinaMenu_Cheats(void);
|
void RosalinaMenu_Cheats(void);
|
||||||
|
|
||||||
|
bool rosalinaMenuShouldShowDebugInfo(void);
|
||||||
|
void RosalinaMenu_ShowDebugInfo(void);
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
extern Menu debuggerMenu;
|
extern Menu debuggerMenu;
|
||||||
|
|
||||||
void debuggerFetchAndSetNextApplicationDebugHandleTask(void *argdata);
|
void debuggerFetchAndSetNextApplicationDebugHandleTask(void *argdata);
|
||||||
|
Result debuggerDisable(s64 timeout);
|
||||||
|
|
||||||
void DebuggerMenu_EnableDebugger(void);
|
void DebuggerMenu_EnableDebugger(void);
|
||||||
void DebuggerMenu_DisableDebugger(void);
|
void DebuggerMenu_DisableDebugger(void);
|
||||||
void DebuggerMenu_DebugNextApplicationByForce(void);
|
void DebuggerMenu_DebugNextApplicationByForce(void);
|
||||||
|
|||||||
@@ -30,12 +30,18 @@
|
|||||||
|
|
||||||
extern Menu screenFiltersMenu;
|
extern Menu screenFiltersMenu;
|
||||||
|
|
||||||
int screenFiltersCurrentTemperature;
|
extern int screenFiltersCurrentTemperature;
|
||||||
|
|
||||||
void screenFiltersSetDisabled(void);
|
void ScreenFiltersMenu_RestoreCct(void);
|
||||||
void screenFiltersReduceBlueLevel1(void);
|
|
||||||
void screenFiltersReduceBlueLevel2(void);
|
void ScreenFiltersMenu_SetDefault(void); // 6500K (default)
|
||||||
void screenFiltersReduceBlueLevel3(void);
|
|
||||||
void screenFiltersReduceBlueLevel4(void);
|
void ScreenFiltersMenu_SetAquarium(void); // 10000K
|
||||||
void screenFiltersReduceBlueLevel5(void);
|
void ScreenFiltersMenu_SetOvercastSky(void); // 7500K
|
||||||
void screenFiltersSetTemperature(int temperature);
|
void ScreenFiltersMenu_SetDaylight(void); // 5500K
|
||||||
|
void ScreenFiltersMenu_SetFluorescent(void); // 4200K
|
||||||
|
void ScreenFiltersMenu_SetHalogen(void); // 3400K
|
||||||
|
void ScreenFiltersMenu_SetIncandescent(void); // 2700K
|
||||||
|
void ScreenFiltersMenu_SetWarmIncandescent(void); // 2300K
|
||||||
|
void ScreenFiltersMenu_SetCandle(void); // 1900K
|
||||||
|
void ScreenFiltersMenu_SetEmber(void); // 1200K
|
||||||
|
|||||||
@@ -30,8 +30,12 @@
|
|||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
|
|
||||||
extern Menu sysconfigMenu;
|
extern Menu sysconfigMenu;
|
||||||
|
extern bool isConnectionForced;
|
||||||
|
|
||||||
|
void SysConfigMenu_UpdateStatus(bool control);
|
||||||
|
|
||||||
void SysConfigMenu_ToggleLEDs(void);
|
void SysConfigMenu_ToggleLEDs(void);
|
||||||
void SysConfigMenu_ToggleWireless(void);
|
void SysConfigMenu_ToggleWireless(void);
|
||||||
void SysConfigMenu_TogglePowerButton(void);
|
void SysConfigMenu_TogglePowerButton(void);
|
||||||
void SysConfigMenu_ControlWifi(void);
|
void SysConfigMenu_ControlWifi(void);
|
||||||
|
void SysConfigMenu_DisableForcedWifiConnection(void);
|
||||||
|
|||||||
@@ -18,13 +18,14 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#define SYNC_ERROR ENODEV
|
#define SYNC_ERROR ENODEV
|
||||||
|
|
||||||
extern Handle SOCU_handle;
|
|
||||||
extern Handle socMemhandle;
|
|
||||||
|
|
||||||
extern bool miniSocEnabled;
|
extern bool miniSocEnabled;
|
||||||
|
|
||||||
Result miniSocInit();
|
Result miniSocInit(void);
|
||||||
|
|
||||||
|
void miniSocLockState(void);
|
||||||
|
void miniSocUnlockState(bool force);
|
||||||
|
|
||||||
|
Result miniSocExitDirect(void);
|
||||||
Result miniSocExit(void);
|
Result miniSocExit(void);
|
||||||
|
|
||||||
s32 _net_convert_error(s32 sock_retval);
|
s32 _net_convert_error(s32 sock_retval);
|
||||||
|
|||||||
@@ -30,4 +30,4 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
Result ntpGetTimeStamp(time_t *outTimestamp);
|
Result ntpGetTimeStamp(time_t *outTimestamp);
|
||||||
Result ntpSetTimeDate(const struct tm *localt);
|
Result ntpSetTimeDate(time_t timestamp);
|
||||||
|
|||||||
@@ -6,6 +6,12 @@
|
|||||||
#include <3ds/services/pmapp.h>
|
#include <3ds/services/pmapp.h>
|
||||||
#include <3ds/services/pmdbg.h>
|
#include <3ds/services/pmdbg.h>
|
||||||
|
|
||||||
Result PMDBG_GetCurrentAppTitleIdAndPid(u64 *outTitleId, u32 *outPid);
|
/// Custom launch flags for PM launch commands.
|
||||||
|
enum {
|
||||||
|
PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING = BIT(24),
|
||||||
|
};
|
||||||
|
|
||||||
|
Result PMDBG_GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags);
|
||||||
Result PMDBG_DebugNextApplicationByForce(bool debug);
|
Result PMDBG_DebugNextApplicationByForce(bool debug);
|
||||||
Result PMDBG_LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInfo, u32 launchFlags);
|
Result PMDBG_LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInfo, u32 launchFlags);
|
||||||
|
Result PMDBG_PrepareToChainloadHomebrew(u64 titleId);
|
||||||
|
|||||||
@@ -29,4 +29,5 @@
|
|||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
|
|
||||||
void ProcessPatchesMenu_PatchUnpatchFSDirectly(void);
|
Result OpenProcessByName(const char *name, Handle *h);
|
||||||
|
Result PatchProcessByName(const char *name, Result (*func)(u32 size));
|
||||||
@@ -68,10 +68,11 @@ typedef struct sock_server
|
|||||||
sock_free_func free;
|
sock_free_func free;
|
||||||
|
|
||||||
Handle shall_terminate_event;
|
Handle shall_terminate_event;
|
||||||
|
Result init_result;
|
||||||
} sock_server;
|
} sock_server;
|
||||||
|
|
||||||
Result server_init(struct sock_server *serv);
|
Result server_init(struct sock_server *serv);
|
||||||
void server_bind(struct sock_server *serv, u16 port);
|
Result server_bind(struct sock_server *serv, u16 port);
|
||||||
void server_run(struct sock_server *serv);
|
void server_run(struct sock_server *serv);
|
||||||
void server_kill_connections(struct sock_server *serv);
|
void server_kill_connections(struct sock_server *serv);
|
||||||
void server_set_should_close_all(struct sock_server *serv);
|
void server_set_should_close_all(struct sock_server *serv);
|
||||||
|
|||||||
@@ -27,8 +27,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <3ds/svc.h>
|
#include <3ds/svc.h>
|
||||||
|
#include <3ds/srv.h>
|
||||||
#include <3ds/result.h>
|
#include <3ds/result.h>
|
||||||
#include "csvc.h"
|
#include "csvc.h"
|
||||||
|
#include "luma_shared_config.h"
|
||||||
|
|
||||||
// For accessing physmem uncached (and directly)
|
// For accessing physmem uncached (and directly)
|
||||||
#define PA_PTR(addr) (void *)((u32)(addr) | 1 << 31)
|
#define PA_PTR(addr) (void *)((u32)(addr) | 1 << 31)
|
||||||
@@ -56,4 +58,12 @@ static inline void *decodeArmBranch(const void *src)
|
|||||||
return (void *)((const u8 *)src + 8 + off);
|
return (void *)((const u8 *)src + 8 + off);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpenProcessByName(const char *name, Handle *h);
|
static inline bool isServiceUsable(const char *name)
|
||||||
|
{
|
||||||
|
bool r;
|
||||||
|
return R_SUCCEEDED(srvIsServiceRegistered(&r, name)) && r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void formatMemoryPermission(char *outbuf, MemPerm perm);
|
||||||
|
void formatUserMemoryState(char *outbuf, MemState state);
|
||||||
|
u32 formatMemoryMapOfProcess(char *outbuf, u32 bufLen, Handle handle);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ AccessControlInfo:
|
|||||||
CoreVersion : 2
|
CoreVersion : 2
|
||||||
DescVersion : 2
|
DescVersion : 2
|
||||||
|
|
||||||
MemoryType : System # Application / System / Base
|
MemoryType : Base # Application / System / Base
|
||||||
HandleTableSize: 150
|
HandleTableSize: 150
|
||||||
|
|
||||||
MemoryMapping:
|
MemoryMapping:
|
||||||
|
|||||||
@@ -32,22 +32,22 @@
|
|||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "csvc.h"
|
||||||
|
|
||||||
u8 framebufferCache[FB_BOTTOM_SIZE];
|
#define KERNPA2VA(a) ((a) + (GET_VERSION_MINOR(osGetKernelVersion()) < 44 ? 0xD0000000 : 0xC0000000))
|
||||||
|
|
||||||
static u32 gpuSavedFramebufferAddr1, gpuSavedFramebufferAddr2, gpuSavedFramebufferFormat, gpuSavedFramebufferStride;
|
static u32 gpuSavedFramebufferAddr1, gpuSavedFramebufferAddr2, gpuSavedFramebufferFormat, gpuSavedFramebufferStride;
|
||||||
|
static u32 framebufferCacheSize;
|
||||||
|
static void *framebufferCache;
|
||||||
static RecursiveLock lock;
|
static RecursiveLock lock;
|
||||||
|
|
||||||
|
void Draw_Init(void)
|
||||||
|
{
|
||||||
|
RecursiveLock_Init(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
void Draw_Lock(void)
|
void Draw_Lock(void)
|
||||||
{
|
{
|
||||||
static bool lockInitialized = false;
|
|
||||||
if(!lockInitialized)
|
|
||||||
{
|
|
||||||
RecursiveLock_Init(&lock);
|
|
||||||
lockInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
RecursiveLock_Lock(&lock);
|
RecursiveLock_Lock(&lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ void Draw_Unlock(void)
|
|||||||
|
|
||||||
void Draw_DrawCharacter(u32 posX, u32 posY, u32 color, char character)
|
void Draw_DrawCharacter(u32 posX, u32 posY, u32 color, char character)
|
||||||
{
|
{
|
||||||
volatile u16 *const fb = (volatile u16 *const)FB_BOTTOM_VRAM_ADDR;
|
u16 *const fb = (u16 *)FB_BOTTOM_VRAM_ADDR;
|
||||||
|
|
||||||
s32 y;
|
s32 y;
|
||||||
for(y = 0; y < 10; y++)
|
for(y = 0; y < 10; y++)
|
||||||
@@ -129,39 +129,98 @@ void Draw_ClearFramebuffer(void)
|
|||||||
Draw_FillFramebuffer(0);
|
Draw_FillFramebuffer(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Draw_SetupFramebuffer(void)
|
Result Draw_AllocateFramebufferCache(u32 size)
|
||||||
|
{
|
||||||
|
// Can't use fbs in FCRAM when Home Menu is active (AXI config related maybe?)
|
||||||
|
u32 addr = 0x0D000000;
|
||||||
|
u32 tmp;
|
||||||
|
|
||||||
|
size = (size + 0xFFF) >> 12 << 12; // round-up
|
||||||
|
|
||||||
|
if (framebufferCache != NULL)
|
||||||
|
__builtin_trap();
|
||||||
|
|
||||||
|
Result res = svcControlMemoryEx(&tmp, addr, 0, size, MEMOP_ALLOC, MEMREGION_SYSTEM | MEMPERM_READWRITE, true);
|
||||||
|
if (R_FAILED(res))
|
||||||
|
{
|
||||||
|
framebufferCache = NULL;
|
||||||
|
framebufferCacheSize = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
framebufferCache = (u32 *)addr;
|
||||||
|
framebufferCacheSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Draw_AllocateFramebufferCacheForScreenshot(u32 size)
|
||||||
|
{
|
||||||
|
u32 remaining = (u32)osGetMemRegionFree(MEMREGION_SYSTEM);
|
||||||
|
u32 sz = remaining < size ? remaining : size;
|
||||||
|
return Draw_AllocateFramebufferCache(sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw_FreeFramebufferCache(void)
|
||||||
|
{
|
||||||
|
u32 tmp;
|
||||||
|
if (framebufferCache != NULL)
|
||||||
|
svcControlMemory(&tmp, (u32)framebufferCache, 0, framebufferCacheSize, MEMOP_FREE, 0);
|
||||||
|
framebufferCacheSize = 0;
|
||||||
|
framebufferCache = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *Draw_GetFramebufferCache(void)
|
||||||
|
{
|
||||||
|
return framebufferCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 Draw_GetFramebufferCacheSize(void)
|
||||||
|
{
|
||||||
|
return framebufferCacheSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 Draw_SetupFramebuffer(void)
|
||||||
{
|
{
|
||||||
while((GPU_PSC0_CNT | GPU_PSC1_CNT | GPU_TRANSFER_CNT | GPU_CMDLIST_CNT) & 1);
|
while((GPU_PSC0_CNT | GPU_PSC1_CNT | GPU_TRANSFER_CNT | GPU_CMDLIST_CNT) & 1);
|
||||||
|
|
||||||
svcFlushEntireDataCache();
|
Draw_FlushFramebuffer();
|
||||||
memcpy(framebufferCache, FB_BOTTOM_VRAM_ADDR, FB_BOTTOM_SIZE);
|
memcpy(framebufferCache, FB_BOTTOM_VRAM_ADDR, FB_BOTTOM_SIZE);
|
||||||
|
Draw_ClearFramebuffer();
|
||||||
|
Draw_FlushFramebuffer();
|
||||||
|
|
||||||
|
u32 format = GPU_FB_BOTTOM_FMT;
|
||||||
|
|
||||||
gpuSavedFramebufferAddr1 = GPU_FB_BOTTOM_ADDR_1;
|
gpuSavedFramebufferAddr1 = GPU_FB_BOTTOM_ADDR_1;
|
||||||
gpuSavedFramebufferAddr2 = GPU_FB_BOTTOM_ADDR_2;
|
gpuSavedFramebufferAddr2 = GPU_FB_BOTTOM_ADDR_2;
|
||||||
gpuSavedFramebufferFormat = GPU_FB_BOTTOM_FMT;
|
gpuSavedFramebufferFormat = format;
|
||||||
gpuSavedFramebufferStride = GPU_FB_BOTTOM_STRIDE;
|
gpuSavedFramebufferStride = GPU_FB_BOTTOM_STRIDE;
|
||||||
|
|
||||||
|
format = (format & ~7) | GSP_RGB565_OES;
|
||||||
|
format |= 3 << 8; // set VRAM bits
|
||||||
|
|
||||||
GPU_FB_BOTTOM_ADDR_1 = GPU_FB_BOTTOM_ADDR_2 = FB_BOTTOM_VRAM_PA;
|
GPU_FB_BOTTOM_ADDR_1 = GPU_FB_BOTTOM_ADDR_2 = FB_BOTTOM_VRAM_PA;
|
||||||
GPU_FB_BOTTOM_FMT = (GPU_FB_BOTTOM_FMT & ~7) | 2;
|
GPU_FB_BOTTOM_FMT = format;
|
||||||
GPU_FB_BOTTOM_STRIDE = 240 * 2;
|
GPU_FB_BOTTOM_STRIDE = 240 * 2;
|
||||||
|
|
||||||
Draw_FlushFramebuffer();
|
return framebufferCacheSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Draw_RestoreFramebuffer(void)
|
void Draw_RestoreFramebuffer(void)
|
||||||
{
|
{
|
||||||
memcpy(FB_BOTTOM_VRAM_ADDR, framebufferCache, FB_BOTTOM_SIZE);
|
memcpy(FB_BOTTOM_VRAM_ADDR, framebufferCache, FB_BOTTOM_SIZE);
|
||||||
|
Draw_FlushFramebuffer();
|
||||||
|
|
||||||
GPU_FB_BOTTOM_ADDR_1 = gpuSavedFramebufferAddr1;
|
GPU_FB_BOTTOM_ADDR_1 = gpuSavedFramebufferAddr1;
|
||||||
GPU_FB_BOTTOM_ADDR_2 = gpuSavedFramebufferAddr2;
|
GPU_FB_BOTTOM_ADDR_2 = gpuSavedFramebufferAddr2;
|
||||||
GPU_FB_BOTTOM_FMT = gpuSavedFramebufferFormat;
|
GPU_FB_BOTTOM_FMT = gpuSavedFramebufferFormat;
|
||||||
GPU_FB_BOTTOM_STRIDE = gpuSavedFramebufferStride;
|
GPU_FB_BOTTOM_STRIDE = gpuSavedFramebufferStride;
|
||||||
|
|
||||||
Draw_FlushFramebuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Draw_FlushFramebuffer(void)
|
void Draw_FlushFramebuffer(void)
|
||||||
{
|
{
|
||||||
svcFlushProcessDataCache(CUR_PROCESS_HANDLE, FB_BOTTOM_VRAM_ADDR, FB_BOTTOM_SIZE);
|
svcFlushProcessDataCache(CUR_PROCESS_HANDLE, (u32)FB_BOTTOM_VRAM_ADDR, FB_BOTTOM_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Draw_GetCurrentFramebufferAddress(bool top, bool left)
|
u32 Draw_GetCurrentFramebufferAddress(bool top, bool left)
|
||||||
@@ -182,6 +241,21 @@ u32 Draw_GetCurrentFramebufferAddress(bool top, bool left)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Draw_GetCurrentScreenInfo(u32 *width, bool *is3d, bool top)
|
||||||
|
{
|
||||||
|
if (top)
|
||||||
|
{
|
||||||
|
bool isNormal2d = (GPU_FB_TOP_FMT & BIT(6)) != 0;
|
||||||
|
*is3d = (GPU_FB_TOP_FMT & BIT(5)) != 0;
|
||||||
|
*width = !(*is3d) && !isNormal2d ? 800 : 400;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*is3d = false;
|
||||||
|
*width = 320;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void Draw_WriteUnaligned(u8 *dst, u32 tmp, u32 size)
|
static inline void Draw_WriteUnaligned(u8 *dst, u32 tmp, u32 size)
|
||||||
{
|
{
|
||||||
memcpy(dst, &tmp, size);
|
memcpy(dst, &tmp, size);
|
||||||
@@ -203,7 +277,7 @@ void Draw_CreateBitmapHeader(u8 *dst, u32 width, u32 heigth)
|
|||||||
Draw_WriteUnaligned(dst + 0x22, 3 * width * heigth, 4);
|
Draw_WriteUnaligned(dst + 0x22, 3 * width * heigth, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void Draw_ConvertPixelToBGR8(u8 *dst, const u8 *src, GSPGPU_FramebufferFormats srcFormat)
|
static inline void Draw_ConvertPixelToBGR8(u8 *dst, const u8 *src, GSPGPU_FramebufferFormat srcFormat)
|
||||||
{
|
{
|
||||||
u8 red, green, blue;
|
u8 red, green, blue;
|
||||||
switch(srcFormat)
|
switch(srcFormat)
|
||||||
@@ -267,16 +341,38 @@ static inline void Draw_ConvertPixelToBGR8(u8 *dst, const u8 *src, GSPGPU_Frameb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Draw_ConvertFrameBufferLine(u8 *line, bool top, bool left, u32 y)
|
typedef struct FrameBufferConvertArgs {
|
||||||
|
u8 *buf;
|
||||||
|
u32 width;
|
||||||
|
u8 startingLine;
|
||||||
|
u8 numLines;
|
||||||
|
bool top;
|
||||||
|
bool left;
|
||||||
|
} FrameBufferConvertArgs;
|
||||||
|
|
||||||
|
static void Draw_ConvertFrameBufferLinesKernel(const FrameBufferConvertArgs *args)
|
||||||
{
|
{
|
||||||
GSPGPU_FramebufferFormats fmt = top ? (GSPGPU_FramebufferFormats)(GPU_FB_TOP_FMT & 7) : (GSPGPU_FramebufferFormats)(GPU_FB_BOTTOM_FMT & 7);
|
static const u8 formatSizes[] = { 4, 3, 2, 2, 2 };
|
||||||
u32 width = top ? 400 : 320;
|
|
||||||
u8 formatSizes[] = { 4, 3, 2, 2, 2 };
|
|
||||||
u32 stride = top ? GPU_FB_TOP_STRIDE : GPU_FB_BOTTOM_STRIDE;
|
|
||||||
|
|
||||||
u32 pa = Draw_GetCurrentFramebufferAddress(top, left);
|
GSPGPU_FramebufferFormat fmt = args->top ? (GSPGPU_FramebufferFormat)(GPU_FB_TOP_FMT & 7) : (GSPGPU_FramebufferFormat)(GPU_FB_BOTTOM_FMT & 7);
|
||||||
u8 *addr = (u8 *)PA_PTR(pa);
|
u32 width = args->width;
|
||||||
|
u32 stride = args->top ? GPU_FB_TOP_STRIDE : GPU_FB_BOTTOM_STRIDE;
|
||||||
|
|
||||||
for(u32 x = 0; x < width; x++)
|
u32 pa = Draw_GetCurrentFramebufferAddress(args->top, args->left);
|
||||||
Draw_ConvertPixelToBGR8(line + x * 3 , addr + x * stride + y * formatSizes[(u8)fmt], fmt);
|
u8 *addr = (u8 *)KERNPA2VA(pa);
|
||||||
|
|
||||||
|
for (u32 y = args->startingLine; y < args->startingLine + args->numLines; y++)
|
||||||
|
{
|
||||||
|
for(u32 x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
__builtin_prefetch(addr + x * stride + y * formatSizes[fmt], 0, 3);
|
||||||
|
Draw_ConvertPixelToBGR8(args->buf + (x + width * y) * 3 , addr + x * stride + y * formatSizes[fmt], fmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw_ConvertFrameBufferLines(u8 *buf, u32 width, u32 startingLine, u32 numLines, bool top, bool left)
|
||||||
|
{
|
||||||
|
FrameBufferConvertArgs args = { buf, width, (u8)startingLine, (u8)numLines, top, left };
|
||||||
|
svcCustomBackdoor(Draw_ConvertFrameBufferLinesKernel, &args);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
#include "ifile.h"
|
#include "ifile.h"
|
||||||
|
|
||||||
extern Handle terminationRequestEvent;
|
extern Handle preTerminationEvent;
|
||||||
|
|
||||||
static inline void assertSuccess(Result res)
|
static inline void assertSuccess(Result res)
|
||||||
{
|
{
|
||||||
@@ -254,7 +254,7 @@ void ERRF_HandleCommands(void)
|
|||||||
{
|
{
|
||||||
ERRF_GetErrInfo(&info, (cmdbuf + 1), sizeof(ERRF_FatalErrInfo));
|
ERRF_GetErrInfo(&info, (cmdbuf + 1), sizeof(ERRF_FatalErrInfo));
|
||||||
ERRF_SaveErrorToFile(&info);
|
ERRF_SaveErrorToFile(&info);
|
||||||
if(info.type != ERRF_ERRTYPE_LOGGED || info.procId == 0)
|
if(!menuShouldExit && (info.type != ERRF_ERRTYPE_LOGGED || info.procId == 0))
|
||||||
{
|
{
|
||||||
menuEnter();
|
menuEnter();
|
||||||
|
|
||||||
@@ -321,7 +321,7 @@ void errDispThreadMain(void)
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
handles[0] = terminationRequestEvent;
|
handles[0] = preTerminationEvent;
|
||||||
handles[1] = serverHandle;
|
handles[1] = serverHandle;
|
||||||
handles[2] = sessionHandle;
|
handles[2] = sessionHandle;
|
||||||
|
|
||||||
@@ -365,7 +365,7 @@ void errDispThreadMain(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while(!terminationRequest);
|
while(!preTerminationRequested);
|
||||||
|
|
||||||
svcCloseHandle(sessionHandle);
|
svcCloseHandle(sessionHandle);
|
||||||
svcCloseHandle(clientHandle);
|
svcCloseHandle(clientHandle);
|
||||||
|
|||||||
@@ -9,15 +9,15 @@
|
|||||||
#include "gdb/net.h"
|
#include "gdb/net.h"
|
||||||
#include "gdb/debug.h"
|
#include "gdb/debug.h"
|
||||||
|
|
||||||
extern Handle terminationRequestEvent;
|
extern Handle preTerminationEvent;
|
||||||
extern bool terminationRequest;
|
extern bool preTerminationRequested;
|
||||||
|
|
||||||
void GDB_RunMonitor(GDBServer *server)
|
void GDB_RunMonitor(GDBServer *server)
|
||||||
{
|
{
|
||||||
Handle handles[3 + MAX_DEBUG];
|
Handle handles[3 + MAX_DEBUG];
|
||||||
Result r = 0;
|
Result r = 0;
|
||||||
|
|
||||||
handles[0] = terminationRequestEvent;
|
handles[0] = preTerminationEvent;
|
||||||
handles[1] = server->super.shall_terminate_event;
|
handles[1] = server->super.shall_terminate_event;
|
||||||
handles[2] = server->statusUpdated;
|
handles[2] = server->statusUpdated;
|
||||||
|
|
||||||
@@ -81,5 +81,5 @@ void GDB_RunMonitor(GDBServer *server)
|
|||||||
RecursiveLock_Unlock(&ctx->lock);
|
RecursiveLock_Unlock(&ctx->lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while(!terminationRequest && server->super.running);
|
while(!preTerminationRequested && server->super.running);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
#include "gdb/breakpoints.h"
|
#include "gdb/breakpoints.h"
|
||||||
|
|
||||||
|
#include "../utils.h"
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
@@ -208,81 +210,19 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig)
|
|||||||
return GDB_SendHexPacket(ctx, outbuf, n);
|
return GDB_SendHexPacket(ctx, outbuf, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *FormatMemPerm(u32 perm)
|
|
||||||
{
|
|
||||||
if (perm == MEMPERM_DONTCARE)
|
|
||||||
return "???";
|
|
||||||
|
|
||||||
static char buf[4] = {0};
|
|
||||||
|
|
||||||
buf[0] = perm & MEMPERM_READ ? 'r' : '-';
|
|
||||||
buf[1] = perm & MEMPERM_WRITE ? 'w' : '-';
|
|
||||||
buf[2] = perm & MEMPERM_EXECUTE ? 'x' : '-';
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *FormatMemState(u32 state)
|
|
||||||
{
|
|
||||||
if (state > 11)
|
|
||||||
return "Unknown";
|
|
||||||
|
|
||||||
static const char *states[12] =
|
|
||||||
{
|
|
||||||
"Free",
|
|
||||||
"Reserved",
|
|
||||||
"IO",
|
|
||||||
"Static",
|
|
||||||
"Code",
|
|
||||||
"Private",
|
|
||||||
"Shared",
|
|
||||||
"Continuous",
|
|
||||||
"Aliased",
|
|
||||||
"Alias",
|
|
||||||
"AliasCode",
|
|
||||||
"Locked"
|
|
||||||
};
|
|
||||||
|
|
||||||
return states[state];
|
|
||||||
}
|
|
||||||
|
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions)
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions)
|
||||||
{
|
{
|
||||||
u32 address = 0;
|
|
||||||
u32 posInBuffer = 0;
|
u32 posInBuffer = 0;
|
||||||
u32 maxPosInBuffer = GDB_BUF_LEN / 2 - 35; ///< 35 is the maximum length of a formatted region
|
|
||||||
Handle handle;
|
Handle handle;
|
||||||
MemInfo memi;
|
|
||||||
PageInfo pagei;
|
|
||||||
char outbuf[GDB_BUF_LEN / 2 + 1];
|
char outbuf[GDB_BUF_LEN / 2 + 1];
|
||||||
|
|
||||||
if(R_FAILED(svcOpenProcess(&handle, ctx->pid)))
|
if(R_FAILED(svcOpenProcess(&handle, ctx->pid)))
|
||||||
{
|
{
|
||||||
posInBuffer = sprintf(outbuf, "Invalid process (wtf?)\n");
|
posInBuffer = sprintf(outbuf, "Invalid process (wtf?)\n");
|
||||||
goto end;
|
return GDB_SendHexPacket(ctx, outbuf, posInBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (address < 0x40000000 ///< Limit to check for regions
|
posInBuffer = formatMemoryMapOfProcess(outbuf, GDB_BUF_LEN / 2, handle);
|
||||||
&& posInBuffer < maxPosInBuffer
|
|
||||||
&& R_SUCCEEDED(svcQueryProcessMemory(&memi, &pagei, handle, address)))
|
|
||||||
{
|
|
||||||
// Update the address for next region
|
|
||||||
address = memi.base_addr + memi.size;
|
|
||||||
|
|
||||||
// If region isn't FREE then add it to the list
|
|
||||||
if (memi.state != MEMSTATE_FREE)
|
|
||||||
{
|
|
||||||
const char *perm = FormatMemPerm(memi.perm);
|
|
||||||
const char *state = FormatMemState(memi.state);
|
|
||||||
|
|
||||||
posInBuffer += sprintf(outbuf + posInBuffer, "%08lx - %08lx %s %s\n",
|
|
||||||
memi.base_addr, address, perm, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
svcCloseHandle(handle);
|
|
||||||
|
|
||||||
end:
|
|
||||||
return GDB_SendHexPacket(ctx, outbuf, posInBuffer);
|
return GDB_SendHexPacket(ctx, outbuf, posInBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,13 +74,14 @@ void GDB_DecrementServerReferenceCount(GDBServer *server)
|
|||||||
|
|
||||||
void GDB_RunServer(GDBServer *server)
|
void GDB_RunServer(GDBServer *server)
|
||||||
{
|
{
|
||||||
server_bind(&server->super, GDB_PORT_BASE);
|
Result res = server_bind(&server->super, GDB_PORT_BASE);
|
||||||
server_bind(&server->super, GDB_PORT_BASE + 1);
|
if(R_SUCCEEDED(res)) res = server_bind(&server->super, GDB_PORT_BASE + 1);
|
||||||
server_bind(&server->super, GDB_PORT_BASE + 2);
|
if(R_SUCCEEDED(res)) res = server_bind(&server->super, GDB_PORT_BASE + 2);
|
||||||
|
|
||||||
server_bind(&server->super, GDB_PORT_BASE + 3); // next application
|
if(R_SUCCEEDED(res)) res = server_bind(&server->super, GDB_PORT_BASE + 3); // next application
|
||||||
|
|
||||||
server_run(&server->super);
|
if(R_SUCCEEDED(res))
|
||||||
|
server_run(&server->super);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDB_LockAllContexts(GDBServer *server)
|
void GDB_LockAllContexts(GDBServer *server)
|
||||||
|
|||||||
@@ -85,38 +85,38 @@
|
|||||||
typedef u32 gdbhio_time_t;
|
typedef u32 gdbhio_time_t;
|
||||||
typedef int gdbhio_mode_t;
|
typedef int gdbhio_mode_t;
|
||||||
|
|
||||||
struct gdbhio_stat {
|
struct PACKED ALIGN(4) gdbhio_stat {
|
||||||
unsigned int st_dev; /* device */
|
u32 gst_dev; /* device */
|
||||||
unsigned int st_ino; /* inode */
|
u32 gst_ino; /* inode */
|
||||||
gdbhio_mode_t st_mode; /* protection */
|
gdbhio_mode_t gst_mode; /* protection */
|
||||||
unsigned int st_nlink; /* number of hard links */
|
u32 gst_nlink; /* number of hard links */
|
||||||
unsigned int st_uid; /* user ID of owner */
|
u32 gst_uid; /* user ID of owner */
|
||||||
unsigned int st_gid; /* group ID of owner */
|
u32 gst_gid; /* group ID of owner */
|
||||||
unsigned int st_rdev; /* device type (if inode device) */
|
u32 gst_rdev; /* device type (if inode device) */
|
||||||
u64 st_size; /* total size, in bytes */
|
u64 gst_size; /* total size, in bytes */
|
||||||
u64 st_blksize; /* blocksize for filesystem I/O */
|
u64 gst_blksize; /* blocksize for filesystem I/O */
|
||||||
u64 st_blocks; /* number of blocks allocated */
|
u64 gst_blocks; /* number of blocks allocated */
|
||||||
gdbhio_time_t st_atime; /* time of last access */
|
gdbhio_time_t gst_atime; /* time of last access */
|
||||||
gdbhio_time_t st_mtime; /* time of last modification */
|
gdbhio_time_t gst_mtime; /* time of last modification */
|
||||||
gdbhio_time_t st_ctime; /* time of last change */
|
gdbhio_time_t gst_ctime; /* time of last change */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void GDB_TioMakeStructStat(struct gdbhio_stat *out, const struct gdbhio_stat *in)
|
static void GDB_TioMakeStructStat(struct gdbhio_stat *out, const struct gdbhio_stat *in)
|
||||||
{
|
{
|
||||||
memset(out, 0, sizeof(struct gdbhio_stat));
|
memset(out, 0, sizeof(struct gdbhio_stat));
|
||||||
out->st_dev = __builtin_bswap32(in->st_dev);
|
out->gst_dev = __builtin_bswap32(in->gst_dev);
|
||||||
out->st_ino = __builtin_bswap32(in->st_ino);
|
out->gst_ino = __builtin_bswap32(in->gst_ino);
|
||||||
out->st_mode = __builtin_bswap32(in->st_dev);
|
out->gst_mode = __builtin_bswap32(in->gst_dev);
|
||||||
out->st_nlink = __builtin_bswap32(in->st_nlink);
|
out->gst_nlink = __builtin_bswap32(in->gst_nlink);
|
||||||
out->st_uid = __builtin_bswap32(in->st_uid);
|
out->gst_uid = __builtin_bswap32(in->gst_uid);
|
||||||
out->st_gid = __builtin_bswap32(in->st_gid);
|
out->gst_gid = __builtin_bswap32(in->gst_gid);
|
||||||
out->st_rdev = __builtin_bswap32(in->st_rdev);
|
out->gst_rdev = __builtin_bswap32(in->gst_rdev);
|
||||||
out->st_size = __builtin_bswap64(in->st_size);
|
out->gst_size = __builtin_bswap64(in->gst_size);
|
||||||
out->st_blksize = __builtin_bswap64(in->st_blksize);
|
out->gst_blksize = __builtin_bswap64(in->gst_blksize);
|
||||||
out->st_blocks = __builtin_bswap64(in->st_blocks);
|
out->gst_blocks = __builtin_bswap64(in->gst_blocks);
|
||||||
out->st_atime = __builtin_bswap32(in->st_atime);
|
out->gst_atime = __builtin_bswap32(in->gst_atime);
|
||||||
out->st_mtime = __builtin_bswap32(in->st_mtime);
|
out->gst_mtime = __builtin_bswap32(in->gst_mtime);
|
||||||
out->st_ctime = __builtin_bswap32(in->st_ctime);
|
out->gst_ctime = __builtin_bswap32(in->gst_ctime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inspired from https://github.com/smealum/ctrulib/blob/master/libctru/source/sdmc_dev.c
|
// Inspired from https://github.com/smealum/ctrulib/blob/master/libctru/source/sdmc_dev.c
|
||||||
@@ -408,8 +408,8 @@ GDB_DECLARE_TIO_HANDLER(Stat)
|
|||||||
|
|
||||||
if (err == 0)
|
if (err == 0)
|
||||||
{
|
{
|
||||||
gdbSt.st_nlink = 1;
|
gdbSt.gst_nlink = 1;
|
||||||
gdbSt.st_mode = GDBHIO_S_IFREG | GDBHIO_S_IRUSR | GDBHIO_S_IWUSR |
|
gdbSt.gst_mode = GDBHIO_S_IFREG | GDBHIO_S_IRUSR | GDBHIO_S_IWUSR |
|
||||||
GDBHIO_S_IRGRP | GDBHIO_S_IWGRP | GDBHIO_S_IROTH | GDBHIO_S_IWOTH;
|
GDBHIO_S_IRGRP | GDBHIO_S_IWGRP | GDBHIO_S_IROTH | GDBHIO_S_IWOTH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,8 +419,8 @@ GDB_DECLARE_TIO_HANDLER(Stat)
|
|||||||
if (err == 0)
|
if (err == 0)
|
||||||
{
|
{
|
||||||
FSDIR_Close(dirHandle);
|
FSDIR_Close(dirHandle);
|
||||||
gdbSt.st_nlink = 1;
|
gdbSt.gst_nlink = 1;
|
||||||
gdbSt.st_mode = GDBHIO_S_IFDIR | GDBHIO_S_IRWXU | GDBHIO_S_IRWXG | GDBHIO_S_IRWXO;
|
gdbSt.gst_mode = GDBHIO_S_IFDIR | GDBHIO_S_IRWXU | GDBHIO_S_IRWXG | GDBHIO_S_IRWXO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,6 @@
|
|||||||
#include "gdb/server.h"
|
#include "gdb/server.h"
|
||||||
#include "pmdbgext.h"
|
#include "pmdbgext.h"
|
||||||
|
|
||||||
#define MAP_BASE 0x10000000
|
|
||||||
|
|
||||||
extern GDBContext *nextApplicationGdbCtx;
|
extern GDBContext *nextApplicationGdbCtx;
|
||||||
extern GDBServer gdbServer;
|
extern GDBServer gdbServer;
|
||||||
|
|
||||||
@@ -77,16 +75,13 @@ static const char serviceList[32][8] =
|
|||||||
"y2r:u",
|
"y2r:u",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u64 dependencyList[] =
|
static const u64 dependencyListNativeFirm[] =
|
||||||
{
|
{
|
||||||
0x0004013000002402LL, //ac
|
0x0004013000002402LL, //ac
|
||||||
0x0004013000001502LL, //am
|
0x0004013000001502LL, //am
|
||||||
0x0004013000003402LL, //boss
|
|
||||||
0x0004013000001602LL, //camera
|
|
||||||
0x0004013000001702LL, //cfg
|
0x0004013000001702LL, //cfg
|
||||||
0x0004013000001802LL, //codec
|
0x0004013000001802LL, //codec
|
||||||
0x0004013000002702LL, //csnd
|
0x0004013000002702LL, //csnd
|
||||||
0x0004013000002802LL, //dlp
|
|
||||||
0x0004013000001A02LL, //dsp
|
0x0004013000001A02LL, //dsp
|
||||||
0x0004013000001B02LL, //gpio
|
0x0004013000001B02LL, //gpio
|
||||||
0x0004013000001C02LL, //gsp
|
0x0004013000001C02LL, //gsp
|
||||||
@@ -95,18 +90,50 @@ static const u64 dependencyList[] =
|
|||||||
0x0004013000001E02LL, //i2c
|
0x0004013000001E02LL, //i2c
|
||||||
0x0004013000003302LL, //ir
|
0x0004013000003302LL, //ir
|
||||||
0x0004013000001F02LL, //mcu
|
0x0004013000001F02LL, //mcu
|
||||||
0x0004013000002002LL, //mic
|
|
||||||
0x0004013000002B02LL, //ndm
|
|
||||||
0x0004013000003502LL, //news
|
|
||||||
0x0004013000002C02LL, //nim
|
0x0004013000002C02LL, //nim
|
||||||
0x0004013000002D02LL, //nwm
|
0x0004013000002D02LL, //nwm
|
||||||
0x0004013000002102LL, //pdn
|
0x0004013000002102LL, //pdn
|
||||||
0x0004013000003102LL, //ps
|
0x0004013000003102LL, //ps
|
||||||
0x0004013000002202LL, //ptm
|
0x0004013000002202LL, //ptm
|
||||||
0x0004013000003702LL, //ro
|
|
||||||
0x0004013000002E02LL, //socket
|
0x0004013000002E02LL, //socket
|
||||||
0x0004013000002302LL, //spi
|
0x0004013000002302LL, //spi
|
||||||
0x0004013000002F02LL, //ssl
|
0x0004013000002F02LL, //ssl
|
||||||
|
|
||||||
|
// Not present on SAFE_FIRM:
|
||||||
|
0x0004013000003402LL, //boss
|
||||||
|
0x0004013000001602LL, //camera
|
||||||
|
0x0004013000002802LL, //dlp
|
||||||
|
0x0004013000002002LL, //mic
|
||||||
|
0x0004013000002B02LL, //ndm
|
||||||
|
0x0004013000003502LL, //news
|
||||||
|
0x0004013000003702LL, //ro
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u64 dependencyListSafeFirm[] =
|
||||||
|
{
|
||||||
|
0x0004013000002403LL, //ac
|
||||||
|
0x0004013000001503LL, //am
|
||||||
|
0x0004013000001703LL, //cfg
|
||||||
|
0x0004013000001803LL, //codec
|
||||||
|
0x0004013000002703LL, //csnd
|
||||||
|
0x0004013000001A03LL, //dsp
|
||||||
|
0x0004013000001B03LL, //gpio
|
||||||
|
0x0004013000001C03LL, //gsp
|
||||||
|
0x0004013000001D03LL, //hid
|
||||||
|
0x0004013000002903LL, //http
|
||||||
|
0x0004013000001E03LL, //i2c
|
||||||
|
0x0004013000003303LL, //ir
|
||||||
|
0x0004013000001F03LL, //mcu
|
||||||
|
0x0004013000002C03LL, //nim
|
||||||
|
0x0004013000002D03LL, //nwm
|
||||||
|
0x0004013000002103LL, //pdn
|
||||||
|
0x0004013000003103LL, //ps
|
||||||
|
0x0004013000002203LL, //ptm
|
||||||
|
0x0004013000002E03LL, //socket
|
||||||
|
0x0004013000002303LL, //spi
|
||||||
|
0x0004013000002F03LL, //ssl
|
||||||
|
|
||||||
|
0x0004013000003203LL, //friends (wouldn't be launched otherwise)
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u32 kernelCaps[] =
|
static const u32 kernelCaps[] =
|
||||||
@@ -118,7 +145,7 @@ static const u32 kernelCaps[] =
|
|||||||
0xFF81FF78, // RW static mapping: 0x1FF78000
|
0xFF81FF78, // RW static mapping: 0x1FF78000
|
||||||
0xFF91F000, // RO static mapping: 0x1F000000
|
0xFF91F000, // RO static mapping: 0x1F000000
|
||||||
0xFF91F600, // RO static mapping: 0x1F600000
|
0xFF91F600, // RO static mapping: 0x1F600000
|
||||||
0xFF002101, // Exflags: APPLICATION memtype + "Allow debug" + "Access core2"
|
0xFF002109, // Exflags: APPLICATION memtype + "Shared page writing" + "Allow debug" + "Access core2"
|
||||||
0xFE000200, // Handle table size: 0x200
|
0xFE000200, // Handle table size: 0x200
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -147,6 +174,31 @@ static u16 *u16_strncpy(u16 *dest, const u16 *src, u32 size)
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HBLDR_RestartHbApplication(void *p)
|
||||||
|
{
|
||||||
|
(void)p;
|
||||||
|
// Don't crash if we fail
|
||||||
|
|
||||||
|
FS_ProgramInfo programInfo;
|
||||||
|
u32 pid;
|
||||||
|
u32 launchFlags;
|
||||||
|
|
||||||
|
Result res = PMDBG_GetCurrentAppInfo(&programInfo, &pid, &launchFlags);
|
||||||
|
if (R_FAILED(res)) return;
|
||||||
|
res = PMDBG_PrepareToChainloadHomebrew(programInfo.programId);
|
||||||
|
if (R_FAILED(res)) return;
|
||||||
|
res = PMAPP_TerminateCurrentApplication(3 * 1000 * 1000 *1000LL); // 3s, like what NS uses
|
||||||
|
if (R_FAILED(res)) return;
|
||||||
|
if (R_SUCCEEDED(res))
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
svcSleepThread(100 * 1000 * 1000LL);
|
||||||
|
res = PMAPP_LaunchTitle(&programInfo, PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING | launchFlags);
|
||||||
|
} while (res == (Result)0xC8A05BF0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HBLDR_HandleCommands(void *ctx)
|
void HBLDR_HandleCommands(void *ctx)
|
||||||
{
|
{
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
@@ -193,8 +245,10 @@ void HBLDR_HandleCommands(void *ctx)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// note: mappableFree doesn't do anything
|
||||||
u32 tmp = 0;
|
u32 tmp = 0;
|
||||||
res = svcControlMemoryEx(&tmp, MAP_BASE, 0, totalSize, MEMOP_ALLOC | flags, MEMPERM_READ | MEMPERM_WRITE, true);
|
u32 *addr = mappableAlloc(totalSize);
|
||||||
|
res = svcControlMemoryEx(&tmp, (u32)addr, 0, totalSize, MEMOP_ALLOC | flags, MEMPERM_READ | MEMPERM_WRITE, true);
|
||||||
if (R_FAILED(res))
|
if (R_FAILED(res))
|
||||||
{
|
{
|
||||||
IFile_Close(&file);
|
IFile_Close(&file);
|
||||||
@@ -202,12 +256,12 @@ void HBLDR_HandleCommands(void *ctx)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle hCodeset = Ldr_CodesetFrom3dsx(name, (u32*)MAP_BASE, baseAddr, &file, tid);
|
Handle hCodeset = Ldr_CodesetFrom3dsx(name, addr, baseAddr, &file, tid);
|
||||||
IFile_Close(&file);
|
IFile_Close(&file);
|
||||||
|
|
||||||
if (!hCodeset)
|
if (!hCodeset)
|
||||||
{
|
{
|
||||||
svcControlMemory(&tmp, MAP_BASE, 0, totalSize, MEMOP_FREE, 0);
|
svcControlMemory(&tmp, (u32)addr, 0, totalSize, MEMOP_FREE, 0);
|
||||||
error(cmdbuf, MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_LDR, RD_NOT_FOUND));
|
error(cmdbuf, MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_LDR, RD_NOT_FOUND));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -264,19 +318,26 @@ void HBLDR_HandleCommands(void *ctx)
|
|||||||
memcpy(exhi->sci.codeset_info.name, "3dsx_app", 8);
|
memcpy(exhi->sci.codeset_info.name, "3dsx_app", 8);
|
||||||
memcpy(&exhi->sci.codeset_info.stack_size, &stacksize, 4);
|
memcpy(&exhi->sci.codeset_info.stack_size, &stacksize, 4);
|
||||||
memset(&exhi->sci.dependencies, 0, sizeof(exhi->sci.dependencies));
|
memset(&exhi->sci.dependencies, 0, sizeof(exhi->sci.dependencies));
|
||||||
memcpy(exhi->sci.dependencies, dependencyList, sizeof(dependencyList));
|
|
||||||
|
u32 coreVer = OS_KernelConfig->kernel_syscore_ver;
|
||||||
|
if (coreVer == 2)
|
||||||
|
memcpy(exhi->sci.dependencies, dependencyListNativeFirm, sizeof(dependencyListNativeFirm));
|
||||||
|
else if (coreVer == 3)
|
||||||
|
memcpy(exhi->sci.dependencies, dependencyListSafeFirm, sizeof(dependencyListSafeFirm));
|
||||||
|
|
||||||
ExHeader_Arm11SystemLocalCapabilities* localcaps0 = &exhi->aci.local_caps;
|
ExHeader_Arm11SystemLocalCapabilities* localcaps0 = &exhi->aci.local_caps;
|
||||||
|
|
||||||
localcaps0->core_info.core_version = 2;
|
localcaps0->core_info.core_version = coreVer;
|
||||||
localcaps0->core_info.use_cpu_clockrate_804MHz = false;
|
localcaps0->core_info.use_cpu_clockrate_804MHz = false;
|
||||||
localcaps0->core_info.enable_l2c = false;
|
localcaps0->core_info.enable_l2c = false;
|
||||||
localcaps0->core_info.n3ds_system_mode = SYSMODE_N3DS_PROD;
|
|
||||||
localcaps0->core_info.ideal_processor = 0;
|
localcaps0->core_info.ideal_processor = 0;
|
||||||
localcaps0->core_info.affinity_mask = BIT(0);
|
localcaps0->core_info.affinity_mask = BIT(0);
|
||||||
localcaps0->core_info.o3ds_system_mode = SYSMODE_O3DS_PROD;
|
|
||||||
localcaps0->core_info.priority = 0x30;
|
localcaps0->core_info.priority = 0x30;
|
||||||
|
|
||||||
|
u32 appmemtype = OS_KernelConfig->app_memtype;
|
||||||
|
localcaps0->core_info.o3ds_system_mode = appmemtype < 6 ? (SystemMode)appmemtype : SYSMODE_O3DS_PROD;
|
||||||
|
localcaps0->core_info.n3ds_system_mode = appmemtype >= 6 ? (SystemMode)(appmemtype - 6 + 1) : SYSMODE_N3DS_PROD;
|
||||||
|
|
||||||
memset(localcaps0->reslimits, 0, sizeof(localcaps0->reslimits));
|
memset(localcaps0->reslimits, 0, sizeof(localcaps0->reslimits));
|
||||||
|
|
||||||
// Set mode1 preemption mode for core1, max. 89% of CPU time (default 0, requires a APT_SetAppCpuTimeLimit call)
|
// Set mode1 preemption mode for core1, max. 89% of CPU time (default 0, requires a APT_SetAppCpuTimeLimit call)
|
||||||
@@ -287,7 +348,7 @@ void HBLDR_HandleCommands(void *ctx)
|
|||||||
localcaps0->storage_info.no_romfs = true;
|
localcaps0->storage_info.no_romfs = true;
|
||||||
localcaps0->storage_info.use_extended_savedata_access = true; // Whatever
|
localcaps0->storage_info.use_extended_savedata_access = true; // Whatever
|
||||||
|
|
||||||
/* We have a patched SM, so whatever... */
|
// We have a patched SM, so whatever...
|
||||||
memset(localcaps0->service_access, 0, sizeof(localcaps0->service_access));
|
memset(localcaps0->service_access, 0, sizeof(localcaps0->service_access));
|
||||||
memcpy(localcaps0->service_access, serviceList, sizeof(serviceList));
|
memcpy(localcaps0->service_access, serviceList, sizeof(serviceList));
|
||||||
|
|
||||||
@@ -300,9 +361,9 @@ void HBLDR_HandleCommands(void *ctx)
|
|||||||
// Set kernel release version to the current kernel version
|
// Set kernel release version to the current kernel version
|
||||||
kcaps0->descriptors[0] = 0xFC000000 | (osGetKernelVersion() >> 16);
|
kcaps0->descriptors[0] = 0xFC000000 | (osGetKernelVersion() >> 16);
|
||||||
|
|
||||||
u64 lastdep = sizeof(dependencyList)/8;
|
if (GET_VERSION_MINOR(osGetKernelVersion()) >= 50 && coreVer == 2) // 9.6+ NFIRM
|
||||||
if (osGetFirmVersion() >= SYSTEM_VERSION(2,50,0)) // 9.6+ FIRM
|
|
||||||
{
|
{
|
||||||
|
u64 lastdep = sizeof(dependencyListNativeFirm)/8;
|
||||||
exhi->sci.dependencies[lastdep++] = 0x0004013000004002ULL; // nfc
|
exhi->sci.dependencies[lastdep++] = 0x0004013000004002ULL; // nfc
|
||||||
strncpy((char*)&localcaps0->service_access[0x20], "nfc:u", 8);
|
strncpy((char*)&localcaps0->service_access[0x20], "nfc:u", 8);
|
||||||
s64 dummy = 0;
|
s64 dummy = 0;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "utils.h" // for makeArmBranch
|
#include "utils.h" // for makeArmBranch
|
||||||
#include "minisoc.h"
|
#include "minisoc.h"
|
||||||
#include "input_redirection.h"
|
#include "input_redirection.h"
|
||||||
|
#include "process_patches.h"
|
||||||
#include "menus.h"
|
#include "menus.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
|
||||||
@@ -55,17 +56,38 @@ int inputRedirectionStartResult;
|
|||||||
void inputRedirectionThreadMain(void)
|
void inputRedirectionThreadMain(void)
|
||||||
{
|
{
|
||||||
Result res = 0;
|
Result res = 0;
|
||||||
|
inputRedirectionStartResult = 0;
|
||||||
|
|
||||||
res = miniSocInit();
|
res = miniSocInit();
|
||||||
if(R_FAILED(res))
|
if(R_FAILED(res))
|
||||||
|
{
|
||||||
|
// Socket services broken
|
||||||
|
inputRedirectionStartResult = res;
|
||||||
|
|
||||||
|
miniSocExit();
|
||||||
|
// Still signal the event
|
||||||
|
svcSignalEvent(inputRedirectionThreadStartedEvent);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int sock = socSocket(AF_INET, SOCK_DGRAM, 0);
|
int sock = socSocket(AF_INET, SOCK_DGRAM, 0);
|
||||||
while(sock == -1)
|
u32 tries = 15;
|
||||||
|
while(sock == -1 && --tries > 0)
|
||||||
{
|
{
|
||||||
svcSleepThread(1000 * 0000 * 0000LL);
|
svcSleepThread(100 * 1000 * 1000LL);
|
||||||
sock = socSocket(AF_INET, SOCK_DGRAM, 0);
|
sock = socSocket(AF_INET, SOCK_DGRAM, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sock < -10000 || tries == 0) {
|
||||||
|
// Socket services broken
|
||||||
|
inputRedirectionStartResult = -1;
|
||||||
|
|
||||||
|
miniSocExit();
|
||||||
|
// Still signal the event
|
||||||
|
svcSignalEvent(inputRedirectionThreadStartedEvent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct sockaddr_in saddr;
|
struct sockaddr_in saddr;
|
||||||
saddr.sin_family = AF_INET;
|
saddr.sin_family = AF_INET;
|
||||||
saddr.sin_port = htons(4950);
|
saddr.sin_port = htons(4950);
|
||||||
@@ -76,6 +98,9 @@ void inputRedirectionThreadMain(void)
|
|||||||
socClose(sock);
|
socClose(sock);
|
||||||
miniSocExit();
|
miniSocExit();
|
||||||
inputRedirectionStartResult = res;
|
inputRedirectionStartResult = res;
|
||||||
|
|
||||||
|
// Still signal the event
|
||||||
|
svcSignalEvent(inputRedirectionThreadStartedEvent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +114,7 @@ void inputRedirectionThreadMain(void)
|
|||||||
|
|
||||||
char buf[20];
|
char buf[20];
|
||||||
u32 oldSpecialButtons = 0, specialButtons = 0;
|
u32 oldSpecialButtons = 0, specialButtons = 0;
|
||||||
while(inputRedirectionEnabled && !terminationRequest)
|
while(inputRedirectionEnabled && !preTerminationRequested)
|
||||||
{
|
{
|
||||||
struct pollfd pfd;
|
struct pollfd pfd;
|
||||||
pfd.fd = sock;
|
pfd.fd = sock;
|
||||||
@@ -125,218 +150,322 @@ void inputRedirectionThreadMain(void)
|
|||||||
srvPublishToSubscriber(0x203, 0);
|
srvPublishToSubscriber(0x203, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(pollres < -10000)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inputRedirectionEnabled = false;
|
||||||
struct linger linger;
|
struct linger linger;
|
||||||
linger.l_onoff = 1;
|
linger.l_onoff = 1;
|
||||||
linger.l_linger = 0;
|
linger.l_linger = 0;
|
||||||
|
|
||||||
socSetsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(struct linger));
|
socSetsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(struct linger));
|
||||||
|
|
||||||
socClose(sock);
|
socClose(sock);
|
||||||
|
|
||||||
miniSocExit();
|
miniSocExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void hidCodePatchFunc(void);
|
void hidCodePatchFunc(void);
|
||||||
void irCodePatchFunc(void);
|
void irCodePatchFunc(void);
|
||||||
|
|
||||||
Result InputRedirection_DoOrUndoPatches(void)
|
static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatch)
|
||||||
{
|
{
|
||||||
s64 startAddress, textTotalRoundedSize, rodataTotalRoundedSize, dataTotalRoundedSize;
|
static u32* hookLoc = NULL;
|
||||||
u32 totalSize;
|
static u32* syncLoc = NULL;
|
||||||
Handle processHandle;
|
static u32* cppFlagLoc = NULL;
|
||||||
|
static u32 origIrSync = 0;
|
||||||
|
static u32 origCppFlag = 0;
|
||||||
|
|
||||||
Result res = OpenProcessByName("hid", &processHandle);
|
static bool patchPrepared = false;
|
||||||
|
|
||||||
if(R_SUCCEEDED(res))
|
static u32 irOrigReadingCode[5] = {
|
||||||
|
0xE5940000, // ldr r0, [r4]
|
||||||
|
0xE1A01005, // mov r1, r5
|
||||||
|
0xE3A03005, // mov r3, #5
|
||||||
|
0xE3A02011, // mov r2, #17
|
||||||
|
0x00000000 // (bl i2c_read_raw goes here)
|
||||||
|
};
|
||||||
|
|
||||||
|
static u32 irHook[] = {
|
||||||
|
0xE5940000, // ldr r0, [r4]
|
||||||
|
0xE1A01005, // mov r1, r5
|
||||||
|
0xE59FC000, // ldr r12, [pc] (actually +8)
|
||||||
|
0xE12FFF3C, // blx r12
|
||||||
|
0x00000000 // irCodePhys goes here
|
||||||
|
};
|
||||||
|
|
||||||
|
static u32 syncHookCode[] = {
|
||||||
|
0xE5900000, // ldr r0, [r0]
|
||||||
|
0xEF000024, // svc 0x24
|
||||||
|
0xE3A00000, // mov r0, #0
|
||||||
|
0xE51FF004, // ldr pc, [pc, #-4]
|
||||||
|
0x00000000, // (return address goes here)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find offsets for required patches
|
||||||
|
s64 startAddress, textTotalRoundedSize, rodataTotalRoundedSize, dataTotalRoundedSize;
|
||||||
|
u32 totalSize;
|
||||||
|
Result res;
|
||||||
|
|
||||||
|
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text + .data
|
||||||
|
svcGetProcessInfo(&rodataTotalRoundedSize, processHandle, 0x10003);
|
||||||
|
svcGetProcessInfo(&dataTotalRoundedSize, processHandle, 0x10004);
|
||||||
|
|
||||||
|
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
||||||
|
|
||||||
|
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
||||||
|
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
|
||||||
|
|
||||||
|
if(R_SUCCEEDED(res) && !patchPrepared)
|
||||||
{
|
{
|
||||||
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text + .data
|
static const u32 irOrigWaitSyncCode[] = {
|
||||||
svcGetProcessInfo(&rodataTotalRoundedSize, processHandle, 0x10003);
|
0xEF000024, // svc 0x24 (WaitSynchronization)
|
||||||
svcGetProcessInfo(&dataTotalRoundedSize, processHandle, 0x10004);
|
0xE1B01FA0, // movs r1, r0, lsr#31
|
||||||
|
0xE1A0A000, // mov r10, r0
|
||||||
|
}, irOrigWaitSyncCodeOld[] = {
|
||||||
|
0xE0AC6000, // adc r6, r12, r0
|
||||||
|
0xE5D70000, // ldrb r0, [r7]
|
||||||
|
}; // pattern for 8.1
|
||||||
|
|
||||||
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
static const u32 irOrigCppFlagCode[] = {
|
||||||
|
0xE3550000, // cmp r5, #0
|
||||||
|
0xE3A0B080, // mov r11, #0x80
|
||||||
|
};
|
||||||
|
|
||||||
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
u32 irDataPhys = (u32)PA_FROM_VA_PTR(irData);
|
||||||
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
|
u32 irCodePhys = (u32)PA_FROM_VA_PTR(&irCodePatchFunc);
|
||||||
|
|
||||||
if(R_SUCCEEDED(res))
|
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &irOrigReadingCode, totalSize, sizeof(irOrigReadingCode) - 4);
|
||||||
|
if(off == NULL)
|
||||||
{
|
{
|
||||||
static const u32 hidOrigRegisterAndValue[] = { 0x1EC46000, 0x4001 };
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
static const u32 hidOrigCode[] = {
|
return -1;
|
||||||
0xE92D4070, // push {r4-r6, lr}
|
}
|
||||||
0xE1A05001, // mov r5, r1
|
|
||||||
0xEE1D4F70, // mrc p15, 0, r4, c13, c0, 3
|
|
||||||
0xE3A01801, // mov r1, #0x10000
|
|
||||||
0xE5A41080, // str r1, [r4,#0x80]!
|
|
||||||
};
|
|
||||||
|
|
||||||
static u32 *hidRegPatchOffsets[2];
|
u32 *off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCode, totalSize, sizeof(irOrigWaitSyncCode));
|
||||||
static u32 *hidPatchJumpLoc;
|
if(off2 == NULL)
|
||||||
|
{
|
||||||
if(inputRedirectionEnabled)
|
off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCodeOld, totalSize, sizeof(irOrigWaitSyncCodeOld));
|
||||||
|
if(off2 == NULL)
|
||||||
{
|
{
|
||||||
memcpy(hidRegPatchOffsets[0], &hidOrigRegisterAndValue, sizeof(hidOrigRegisterAndValue));
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
memcpy(hidRegPatchOffsets[1], &hidOrigRegisterAndValue, sizeof(hidOrigRegisterAndValue));
|
return -2;
|
||||||
memcpy(hidPatchJumpLoc, &hidOrigCode, sizeof(hidOrigCode));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
u32 hidDataPhys = (u32)PA_FROM_VA_PTR(hidData);
|
|
||||||
u32 hidCodePhys = (u32)PA_FROM_VA_PTR(&hidCodePatchFunc);
|
|
||||||
u32 hidHook[] = {
|
|
||||||
0xE59F3004, // ldr r3, [pc, #4]
|
|
||||||
0xE59FC004, // ldr r12, [pc, #4]
|
|
||||||
0xE12FFF1C, // bx r12
|
|
||||||
hidDataPhys,
|
|
||||||
hidCodePhys,
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &hidOrigRegisterAndValue, totalSize, sizeof(hidOrigRegisterAndValue));
|
|
||||||
if(off == NULL)
|
|
||||||
{
|
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 *off2 = (u32 *)memsearch((u8 *)off + sizeof(hidOrigRegisterAndValue), &hidOrigRegisterAndValue, totalSize - ((u32)off - 0x00100000), sizeof(hidOrigRegisterAndValue));
|
|
||||||
if(off2 == NULL)
|
|
||||||
{
|
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &hidOrigCode, totalSize, sizeof(hidOrigCode));
|
|
||||||
if(off3 == NULL)
|
|
||||||
{
|
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
hidRegPatchOffsets[0] = off;
|
|
||||||
hidRegPatchOffsets[1] = off2;
|
|
||||||
hidPatchJumpLoc = off3;
|
|
||||||
|
|
||||||
*off = *off2 = hidDataPhys;
|
|
||||||
memcpy(off3, &hidHook, sizeof(hidHook));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res = svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &irOrigCppFlagCode, totalSize, sizeof(irOrigCppFlagCode));
|
||||||
}
|
if(off3 == NULL)
|
||||||
svcCloseHandle(processHandle);
|
|
||||||
|
|
||||||
res = OpenProcessByName("ir", &processHandle);
|
|
||||||
if(R_SUCCEEDED(res) && osGetKernelVersion() >= SYSTEM_VERSION(2, 44, 6))
|
|
||||||
{
|
|
||||||
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text + .data
|
|
||||||
svcGetProcessInfo(&rodataTotalRoundedSize, processHandle, 0x10003);
|
|
||||||
svcGetProcessInfo(&dataTotalRoundedSize, processHandle, 0x10004);
|
|
||||||
|
|
||||||
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
|
||||||
|
|
||||||
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
|
||||||
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
|
|
||||||
|
|
||||||
if(R_SUCCEEDED(res))
|
|
||||||
{
|
{
|
||||||
static bool useOldSyncCode;
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
static u32 irOrigReadingCode[5] = {
|
return -3;
|
||||||
0xE5940000, // ldr r0, [r4]
|
}
|
||||||
0xE1A01005, // mov r1, r5
|
|
||||||
0xE3A03005, // mov r3, #5
|
|
||||||
0xE3A02011, // mov r2, #17
|
|
||||||
0x00000000 // (bl i2c_read_raw goes here)
|
|
||||||
};
|
|
||||||
|
|
||||||
static const u32 irOrigWaitSyncCode[] = {
|
origIrSync = *off2;
|
||||||
0xEF000024, // svc 0x24 (WaitSynchronization)
|
origCppFlag = *off3;
|
||||||
0xE1B01FA0, // movs r1, r0, lsr#31
|
|
||||||
0xE1A0A000, // mov r10, r0
|
|
||||||
}, irOrigWaitSyncCodeOld[] = {
|
|
||||||
0xE0AC6000, // adc r6, r12, r0
|
|
||||||
0xE5D70000, // ldrb r0, [r7]
|
|
||||||
}; // pattern for 8.1
|
|
||||||
|
|
||||||
static const u32 irOrigCppFlagCode[] = {
|
*(void **)(irCodePhys + 8) = decodeArmBranch(off + 4);
|
||||||
0xE3550000, // cmp r5, #0
|
*(void **)(irCodePhys + 12) = (void*)irDataPhys;
|
||||||
0xE3A0B080, // mov r11, #0x80
|
|
||||||
};
|
|
||||||
|
|
||||||
static u32 *irHookLoc, *irWaitSyncLoc, *irCppFlagLoc;
|
irHook[4] = irCodePhys;
|
||||||
|
irOrigReadingCode[4] = off[4]; // Copy the branch.
|
||||||
|
syncHookCode[4] = (u32)off2 + 4; // Hook return address
|
||||||
|
|
||||||
if(inputRedirectionEnabled)
|
hookLoc = PA_FROM_VA_PTR(off);
|
||||||
|
syncLoc = PA_FROM_VA_PTR(off2);
|
||||||
|
cppFlagLoc = PA_FROM_VA_PTR(off3);
|
||||||
|
|
||||||
|
patchPrepared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(res))
|
||||||
|
{
|
||||||
|
if (doPatch)
|
||||||
|
{
|
||||||
|
memcpy(hookLoc, &irHook, sizeof(irHook));
|
||||||
|
|
||||||
|
// We keep the WaitSynchronization1 to avoid general slowdown because of the high cpu load
|
||||||
|
if (*syncLoc == 0xEF000024) // svc 0x24 (WaitSynchronization)
|
||||||
{
|
{
|
||||||
memcpy(irHookLoc, &irOrigReadingCode, sizeof(irOrigReadingCode));
|
syncLoc[-1] = 0xE51FF004;
|
||||||
if(useOldSyncCode)
|
syncLoc[0] = (u32)PA_FROM_VA_PTR(&syncHookCode);
|
||||||
memcpy(irWaitSyncLoc, &irOrigWaitSyncCodeOld, sizeof(irOrigWaitSyncCodeOld));
|
|
||||||
else
|
|
||||||
memcpy(irWaitSyncLoc, &irOrigWaitSyncCode, sizeof(irOrigWaitSyncCode));
|
|
||||||
memcpy(irCppFlagLoc, &irOrigCppFlagCode, sizeof(irOrigCppFlagCode));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
u32 irDataPhys = (u32)PA_FROM_VA_PTR(irData);
|
|
||||||
u32 irCodePhys = (u32)PA_FROM_VA_PTR(&irCodePatchFunc);
|
|
||||||
|
|
||||||
u32 irHook[] = {
|
|
||||||
0xE5940000, // ldr r0, [r4]
|
|
||||||
0xE1A01005, // mov r1, r5
|
|
||||||
0xE59FC000, // ldr r12, [pc] (actually +8)
|
|
||||||
0xE12FFF3C, // blx r12
|
|
||||||
irCodePhys,
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &irOrigReadingCode, totalSize, sizeof(irOrigReadingCode) - 4);
|
|
||||||
if(off == NULL)
|
|
||||||
{
|
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 *off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCode, totalSize, sizeof(irOrigWaitSyncCode));
|
|
||||||
if(off2 == NULL)
|
|
||||||
{
|
|
||||||
off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCodeOld, totalSize, sizeof(irOrigWaitSyncCodeOld));
|
|
||||||
if(off2 == NULL)
|
|
||||||
{
|
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
useOldSyncCode = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
useOldSyncCode = false;
|
|
||||||
|
|
||||||
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &irOrigCppFlagCode, totalSize, sizeof(irOrigCppFlagCode));
|
|
||||||
if(off3 == NULL)
|
|
||||||
{
|
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
|
||||||
return -6;
|
|
||||||
}
|
|
||||||
|
|
||||||
*(void **)(irCodePhys + 8) = decodeArmBranch(off + 4);
|
|
||||||
*(void **)(irCodePhys + 12) = (void*)irDataPhys;
|
|
||||||
|
|
||||||
irHookLoc = off;
|
|
||||||
irWaitSyncLoc = off2;
|
|
||||||
irCppFlagLoc = off3;
|
|
||||||
|
|
||||||
irOrigReadingCode[4] = off[4]; // Copy the branch.
|
|
||||||
|
|
||||||
memcpy(irHookLoc, &irHook, sizeof(irHook));
|
|
||||||
|
|
||||||
// This "NOP"s out a WaitSynchronisation1 (on the event bound to the 'IR' interrupt) or the check of a previous one
|
// This "NOP"s out a WaitSynchronisation1 (on the event bound to the 'IR' interrupt) or the check of a previous one
|
||||||
*irWaitSyncLoc = 0xE3A00000; // mov r0, #0
|
*syncLoc = 0xE3A00000; // mov r0, #0
|
||||||
|
|
||||||
// This NOPs out a flag check in ir:user's CPP emulation
|
|
||||||
*irCppFlagLoc = 0xE3150000; // tst r5, #0
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
res = svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
// This NOPs out a flag check in ir:user's CPP emulation
|
||||||
|
*cppFlagLoc = 0xE3150000; // tst r5, #0
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(hookLoc, irOrigReadingCode, sizeof(irOrigReadingCode));
|
||||||
|
|
||||||
|
if (*syncLoc == 0xE3A00000)
|
||||||
|
*syncLoc = origIrSync;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
syncLoc[-1] = 0xE5900000; // ldr r0, [r0]
|
||||||
|
syncLoc[0] = 0xEF000024; // svc 0x24
|
||||||
|
}
|
||||||
|
|
||||||
|
*cppFlagLoc = origCppFlag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
svcCloseHandle(processHandle);
|
|
||||||
|
svcInvalidateEntireInstructionCache();
|
||||||
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Result InputRedirection_DoUndoHidPatches(Handle processHandle, bool doPatches)
|
||||||
|
{
|
||||||
|
static const u32 hidOrigRegisterAndValue[] = { 0x1EC46000, 0x4001 };
|
||||||
|
static const u32 hidOrigCode[] = {
|
||||||
|
0xE92D4070, // push {r4-r6, lr}
|
||||||
|
0xE1A05001, // mov r5, r1
|
||||||
|
0xEE1D4F70, // mrc p15, 0, r4, c13, c0, 3
|
||||||
|
0xE3A01801, // mov r1, #0x10000
|
||||||
|
0xE5A41080, // str r1, [r4,#0x80]!
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool patchPrepared = false;
|
||||||
|
static u32 *hidRegPatchOffsets[2];
|
||||||
|
static u32 *hidPatchJumpLoc;
|
||||||
|
|
||||||
|
// Find offsets for required patches
|
||||||
|
s64 startAddress, textTotalRoundedSize, rodataTotalRoundedSize, dataTotalRoundedSize;
|
||||||
|
u32 totalSize;
|
||||||
|
Result res;
|
||||||
|
|
||||||
|
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text + .data
|
||||||
|
svcGetProcessInfo(&rodataTotalRoundedSize, processHandle, 0x10003);
|
||||||
|
svcGetProcessInfo(&dataTotalRoundedSize, processHandle, 0x10004);
|
||||||
|
|
||||||
|
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
||||||
|
|
||||||
|
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
||||||
|
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(res) && !patchPrepared)
|
||||||
|
{
|
||||||
|
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &hidOrigRegisterAndValue, totalSize, sizeof(hidOrigRegisterAndValue));
|
||||||
|
if(off == NULL)
|
||||||
|
{
|
||||||
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 *off2 = (u32 *)memsearch((u8 *)off + sizeof(hidOrigRegisterAndValue), &hidOrigRegisterAndValue, totalSize - ((u32)off - 0x00100000), sizeof(hidOrigRegisterAndValue));
|
||||||
|
if(off2 == NULL)
|
||||||
|
{
|
||||||
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &hidOrigCode, totalSize, sizeof(hidOrigCode));
|
||||||
|
if(off3 == NULL)
|
||||||
|
{
|
||||||
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
hidRegPatchOffsets[0] = off;
|
||||||
|
hidRegPatchOffsets[1] = off2;
|
||||||
|
hidPatchJumpLoc = off3;
|
||||||
|
|
||||||
|
patchPrepared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(R_SUCCEEDED(res))
|
||||||
|
{
|
||||||
|
if (doPatches)
|
||||||
|
{
|
||||||
|
u32 hidDataPhys = (u32)PA_FROM_VA_PTR(hidData);
|
||||||
|
u32 hidCodePhys = (u32)PA_FROM_VA_PTR(&hidCodePatchFunc);
|
||||||
|
u32 hidHook[] = {
|
||||||
|
0xE59F3004, // ldr r3, [pc, #4]
|
||||||
|
0xE59FC004, // ldr r12, [pc, #4]
|
||||||
|
0xE12FFF1C, // bx r12
|
||||||
|
hidDataPhys,
|
||||||
|
hidCodePhys,
|
||||||
|
};
|
||||||
|
|
||||||
|
*hidRegPatchOffsets[0] = *hidRegPatchOffsets[1] = hidDataPhys;
|
||||||
|
memcpy(hidPatchJumpLoc, &hidHook, sizeof(hidHook));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(hidRegPatchOffsets[0], &hidOrigRegisterAndValue, sizeof(hidOrigRegisterAndValue));
|
||||||
|
memcpy(hidRegPatchOffsets[1], &hidOrigRegisterAndValue, sizeof(hidOrigRegisterAndValue));
|
||||||
|
memcpy(hidPatchJumpLoc, &hidOrigCode, sizeof(hidOrigCode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InputRedirection_Disable(s64 timeout)
|
||||||
|
{
|
||||||
|
if(!inputRedirectionEnabled)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Result res = InputRedirection_DoOrUndoPatches();
|
||||||
|
if(R_FAILED(res))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
inputRedirectionEnabled = false;
|
||||||
|
res = MyThread_Join(&inputRedirectionThread, timeout);
|
||||||
|
svcCloseHandle(inputRedirectionThreadStartedEvent);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InputRedirection_DoOrUndoPatches(void)
|
||||||
|
{
|
||||||
|
static bool hidPatched = false;
|
||||||
|
static bool irPatched = false;
|
||||||
|
|
||||||
|
Handle hidProcHandle = 0, irProcHandle = 0;
|
||||||
|
|
||||||
|
// Prevent hid and ir from running, in any case
|
||||||
|
|
||||||
|
svcKernelSetState(0x10000, 4);
|
||||||
|
|
||||||
|
Result res = OpenProcessByName("hid", &hidProcHandle);
|
||||||
|
if (R_FAILED(res))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
res = OpenProcessByName("ir", &irProcHandle);
|
||||||
|
if (R_FAILED(res))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if(R_SUCCEEDED(res))
|
||||||
|
{
|
||||||
|
res = InputRedirection_DoUndoHidPatches(hidProcHandle, !hidPatched);
|
||||||
|
if (R_SUCCEEDED(res))
|
||||||
|
hidPatched = !hidPatched;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(R_SUCCEEDED(res) && GET_VERSION_MINOR(osGetKernelVersion()) >= 44)
|
||||||
|
{
|
||||||
|
res = InputRedirection_DoUndoIrPatches(irProcHandle, !irPatched);
|
||||||
|
if (R_SUCCEEDED(res))
|
||||||
|
irPatched = !irPatched;
|
||||||
|
else if (!irPatched)
|
||||||
|
{
|
||||||
|
InputRedirection_DoUndoHidPatches(hidProcHandle, false);
|
||||||
|
hidPatched = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
svcKernelSetState(0x10000, 4);
|
||||||
|
|
||||||
|
svcCloseHandle(hidProcHandle);
|
||||||
|
svcCloseHandle(irProcHandle);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|||||||
122
sysmodules/rosalina/source/luminance.c
Normal file
122
sysmodules/rosalina/source/luminance.c
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Luma3DS
|
||||||
|
* Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 <math.h>
|
||||||
|
#include "luminance.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
extern bool isN3DS;
|
||||||
|
|
||||||
|
typedef struct BlPwmData
|
||||||
|
{
|
||||||
|
float coeffs[3][3];
|
||||||
|
u8 numLevels;
|
||||||
|
u8 unk;
|
||||||
|
u16 luminanceLevels[7];
|
||||||
|
u16 brightnessMax;
|
||||||
|
u16 brightnessMin;
|
||||||
|
} BlPwmData;
|
||||||
|
|
||||||
|
// Calibration, with (dubious) default values as fallback
|
||||||
|
static BlPwmData s_blPwmData = {
|
||||||
|
.coeffs = {
|
||||||
|
{ 0.00111639f, 1.41412f, 0.07178809f },
|
||||||
|
{ 0.000418169f, 0.66567f, 0.06098654f },
|
||||||
|
{ 0.00208543f, 1.55639f, 0.0385939f }
|
||||||
|
},
|
||||||
|
.numLevels = 5,
|
||||||
|
.unk = 0,
|
||||||
|
.luminanceLevels = { 20, 43, 73, 95, 117, 172, 172 },
|
||||||
|
.brightnessMax = 512,
|
||||||
|
.brightnessMin = 13,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline float getPwmRatio(u32 brightnessMax, u32 pwmCnt)
|
||||||
|
{
|
||||||
|
u32 val = (pwmCnt & 0x10000) ? pwmCnt & 0x3FF : 511; // check pwm enabled flag
|
||||||
|
return (float)brightnessMax / (val + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nn's asm has rounding errors (originally at 10^-3)
|
||||||
|
static inline u32 luminanceToBrightness(u32 luminance, const float coeffs[3], u32 minLuminance, float pwmRatio)
|
||||||
|
{
|
||||||
|
float x = (float)luminance;
|
||||||
|
float y = coeffs[0]*x*x + coeffs[1]*x + coeffs[2];
|
||||||
|
y = (y <= minLuminance ? (float)minLuminance : y) / pwmRatio;
|
||||||
|
|
||||||
|
return (u32)(y + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 brightnessToLuminance(u32 brightness, const float coeffs[3], float pwmRatio)
|
||||||
|
{
|
||||||
|
// Find polynomial root of ax^2 + bx + c = y
|
||||||
|
|
||||||
|
float y = (float)brightness * pwmRatio;
|
||||||
|
float a = coeffs[0];
|
||||||
|
float b = coeffs[1];
|
||||||
|
float c = coeffs[2] - y;
|
||||||
|
|
||||||
|
float x0 = (-b + sqrtf(b*b - 4.0f*a*c)) / (a + a);
|
||||||
|
|
||||||
|
return (u32)(x0 + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readCalibration(void)
|
||||||
|
{
|
||||||
|
static bool calibRead = false;
|
||||||
|
|
||||||
|
if (!calibRead) {
|
||||||
|
cfguInit();
|
||||||
|
calibRead = R_SUCCEEDED(CFG_GetConfigInfoBlk8(sizeof(BlPwmData), 0x50002, &s_blPwmData));
|
||||||
|
cfguExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 getMinLuminancePreset(void)
|
||||||
|
{
|
||||||
|
readCalibration();
|
||||||
|
return s_blPwmData.luminanceLevels[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 getMaxLuminancePreset(void)
|
||||||
|
{
|
||||||
|
readCalibration();
|
||||||
|
return s_blPwmData.luminanceLevels[s_blPwmData.numLevels - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 getCurrentLuminance(bool top)
|
||||||
|
{
|
||||||
|
u32 regbase = top ? 0x10202200 : 0x10202A00;
|
||||||
|
|
||||||
|
readCalibration();
|
||||||
|
|
||||||
|
const float *coeffs = s_blPwmData.coeffs[top ? (isN3DS ? 2 : 1) : 0];
|
||||||
|
u32 brightness = REG32(regbase + 0x40);
|
||||||
|
float ratio = getPwmRatio(s_blPwmData.brightnessMax, REG32(regbase + 0x44));
|
||||||
|
|
||||||
|
return brightnessToLuminance(brightness, coeffs, ratio);
|
||||||
|
}
|
||||||
@@ -33,49 +33,19 @@
|
|||||||
#include "3dsx.h"
|
#include "3dsx.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "MyThread.h"
|
#include "MyThread.h"
|
||||||
#include "menus/process_patches.h"
|
|
||||||
#include "menus/miscellaneous.h"
|
#include "menus/miscellaneous.h"
|
||||||
#include "menus/debugger.h"
|
#include "menus/debugger.h"
|
||||||
#include "menus/screen_filters.h"
|
#include "menus/screen_filters.h"
|
||||||
#include "menus/cheats.h"
|
#include "menus/cheats.h"
|
||||||
|
#include "menus/sysconfig.h"
|
||||||
|
#include "input_redirection.h"
|
||||||
|
#include "minisoc.h"
|
||||||
|
#include "draw.h"
|
||||||
|
|
||||||
#include "task_runner.h"
|
#include "task_runner.h"
|
||||||
|
|
||||||
bool isN3DS;
|
bool isN3DS;
|
||||||
|
|
||||||
static Result stealFsReg(void)
|
|
||||||
{
|
|
||||||
Result ret = 0;
|
|
||||||
|
|
||||||
ret = svcControlService(SERVICEOP_STEAL_CLIENT_SESSION, fsRegGetSessionHandle(), "fs:REG");
|
|
||||||
while(ret == 0x9401BFE)
|
|
||||||
{
|
|
||||||
svcSleepThread(500 * 1000LL);
|
|
||||||
ret = svcControlService(SERVICEOP_STEAL_CLIENT_SESSION, fsRegGetSessionHandle(), "fs:REG");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Result fsRegSetupPermissions(void)
|
|
||||||
{
|
|
||||||
u32 pid;
|
|
||||||
Result res;
|
|
||||||
FS_ProgramInfo info;
|
|
||||||
|
|
||||||
ExHeader_Arm11StorageInfo storageInfo = {
|
|
||||||
.fs_access_info = FSACCESS_NANDRO_RW | FSACCESS_NANDRW | FSACCESS_SDMC_RW,
|
|
||||||
};
|
|
||||||
|
|
||||||
info.programId = 0x0004013000006902LL; // Rosalina TID
|
|
||||||
info.mediaType = MEDIATYPE_NAND;
|
|
||||||
|
|
||||||
if(R_SUCCEEDED(res = svcGetProcessId(&pid, CUR_PROCESS_HANDLE)))
|
|
||||||
res = FSREG_Register(pid, 0xFFFF000000000000LL, &info, &storageInfo);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result __sync_init(void);
|
Result __sync_init(void);
|
||||||
Result __sync_fini(void);
|
Result __sync_fini(void);
|
||||||
void __libc_init_array(void);
|
void __libc_init_array(void);
|
||||||
@@ -107,19 +77,20 @@ void initSystem(void)
|
|||||||
s64 out;
|
s64 out;
|
||||||
Result res;
|
Result res;
|
||||||
__sync_init();
|
__sync_init();
|
||||||
|
mappableInit(0x10000000, 0x14000000);
|
||||||
|
|
||||||
isN3DS = svcGetSystemInfo(&out, 0x10001, 0) == 0;
|
isN3DS = svcGetSystemInfo(&out, 0x10001, 0) == 0;
|
||||||
|
|
||||||
svcGetSystemInfo(&out, 0x10000, 0x100);
|
svcGetSystemInfo(&out, 0x10000, 0x100);
|
||||||
HBLDR_3DSX_TID = out == 0 ? HBLDR_DEFAULT_3DSX_TID : (u64)out;
|
Luma_SharedConfig->hbldr_3dsx_tid = out == 0 ? HBLDR_DEFAULT_3DSX_TID : (u64)out;
|
||||||
|
Luma_SharedConfig->use_hbldr = true;
|
||||||
|
|
||||||
svcGetSystemInfo(&out, 0x10000, 0x101);
|
svcGetSystemInfo(&out, 0x10000, 0x101);
|
||||||
menuCombo = out == 0 ? DEFAULT_MENU_COMBO : (u32)out;
|
menuCombo = out == 0 ? DEFAULT_MENU_COMBO : (u32)out;
|
||||||
|
|
||||||
miscellaneousMenu.items[0].title = HBLDR_3DSX_TID == HBLDR_DEFAULT_3DSX_TID ? "Switch the hb. title to the current app." :
|
miscellaneousMenu.items[0].title = Luma_SharedConfig->hbldr_3dsx_tid == HBLDR_DEFAULT_3DSX_TID ?
|
||||||
"Switch the hb. title to hblauncher_loader";
|
"Switch the hb. title to the current app." :
|
||||||
|
"Switch the hb. title to hblauncher_loader";
|
||||||
ProcessPatchesMenu_PatchUnpatchFSDirectly();
|
|
||||||
|
|
||||||
for(res = 0xD88007FA; res == (Result)0xD88007FA; svcSleepThread(500 * 1000LL))
|
for(res = 0xD88007FA; res == (Result)0xD88007FA; svcSleepThread(500 * 1000LL))
|
||||||
{
|
{
|
||||||
@@ -128,10 +99,13 @@ void initSystem(void)
|
|||||||
svcBreak(USERBREAK_PANIC);
|
svcBreak(USERBREAK_PANIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (R_FAILED(stealFsReg()) || R_FAILED(fsRegSetupPermissions()) || R_FAILED(fsInit()))
|
if (R_FAILED(pmAppInit()) || R_FAILED(pmDbgInit()))
|
||||||
svcBreak(USERBREAK_PANIC);
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
|
||||||
if (R_FAILED(pmDbgInit()))
|
if (R_FAILED(fsInit()))
|
||||||
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
|
||||||
|
if (R_FAILED(FSUSER_SetPriority(-16)))
|
||||||
svcBreak(USERBREAK_PANIC);
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
|
||||||
// **** DO NOT init services that don't come from KIPs here ****
|
// **** DO NOT init services that don't come from KIPs here ****
|
||||||
@@ -139,26 +113,98 @@ void initSystem(void)
|
|||||||
|
|
||||||
__libc_init_array();
|
__libc_init_array();
|
||||||
|
|
||||||
// ROSALINA HACKJOB BEGIN
|
|
||||||
// NORMAL APPS SHOULD NOT DO THIS, EVER
|
|
||||||
u32 *tls = (u32 *)getThreadLocalStorage();
|
|
||||||
memset(tls, 0, 0x80);
|
|
||||||
tls[0] = 0x21545624;
|
|
||||||
// ROSALINA HACKJOB END
|
// ROSALINA HACKJOB END
|
||||||
|
|
||||||
// Rosalina specific:
|
// Rosalina specific:
|
||||||
|
u32 *tls = (u32 *)getThreadLocalStorage();
|
||||||
|
memset(tls, 0, 0x80);
|
||||||
|
tls[0] = 0x21545624;
|
||||||
|
|
||||||
|
// ROSALINA HACKJOB BEGIN
|
||||||
|
// NORMAL APPS SHOULD NOT DO THIS, EVER
|
||||||
srvSetBlockingPolicy(true); // GetServiceHandle nonblocking if service port is full
|
srvSetBlockingPolicy(true); // GetServiceHandle nonblocking if service port is full
|
||||||
}
|
}
|
||||||
|
|
||||||
bool terminationRequest = false;
|
bool menuShouldExit = false;
|
||||||
Handle terminationRequestEvent;
|
bool preTerminationRequested = false;
|
||||||
|
Handle preTerminationEvent;
|
||||||
|
extern bool isHidInitialized;
|
||||||
|
|
||||||
static void handleTermNotification(u32 notificationId)
|
static void handleTermNotification(u32 notificationId)
|
||||||
{
|
{
|
||||||
(void)notificationId;
|
(void)notificationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleSleepNotification(u32 notificationId)
|
||||||
|
{
|
||||||
|
ptmSysmInit();
|
||||||
|
s32 ackValue = ptmSysmGetNotificationAckValue(notificationId);
|
||||||
|
switch (notificationId)
|
||||||
|
{
|
||||||
|
case PTMNOTIFID_SLEEP_REQUESTED:
|
||||||
|
menuShouldExit = true;
|
||||||
|
PTMSYSM_ReplyToSleepQuery(miniSocEnabled); // deny sleep request if we have network stuff running
|
||||||
|
break;
|
||||||
|
case PTMNOTIFID_GOING_TO_SLEEP:
|
||||||
|
case PTMNOTIFID_SLEEP_ALLOWED:
|
||||||
|
case PTMNOTIFID_FULLY_WAKING_UP:
|
||||||
|
case PTMNOTIFID_HALF_AWAKE:
|
||||||
|
PTMSYSM_NotifySleepPreparationComplete(ackValue);
|
||||||
|
break;
|
||||||
|
case PTMNOTIFID_SLEEP_DENIED:
|
||||||
|
case PTMNOTIFID_FULLY_AWAKE:
|
||||||
|
menuShouldExit = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ptmSysmExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleShellNotification(u32 notificationId)
|
||||||
|
{
|
||||||
|
if (notificationId == 0x213) {
|
||||||
|
// Shell opened
|
||||||
|
// Note that this notification is fired on system init
|
||||||
|
ScreenFiltersMenu_RestoreCct();
|
||||||
|
menuShouldExit = false;
|
||||||
|
} else {
|
||||||
|
// Shell closed
|
||||||
|
menuShouldExit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handlePreTermNotification(u32 notificationId)
|
||||||
|
{
|
||||||
|
(void)notificationId;
|
||||||
|
// Might be subject to a race condition, but heh.
|
||||||
|
|
||||||
|
miniSocUnlockState(true);
|
||||||
|
|
||||||
|
// Disable input redirection
|
||||||
|
InputRedirection_Disable(100 * 1000 * 1000LL);
|
||||||
|
|
||||||
|
// Ask the debugger to terminate in approx 2 * 100ms
|
||||||
|
debuggerDisable(100 * 1000 * 1000LL);
|
||||||
|
|
||||||
|
// Kill the ac session if needed
|
||||||
|
if(isConnectionForced)
|
||||||
|
{
|
||||||
|
acExit();
|
||||||
|
isConnectionForced = false;
|
||||||
|
SysConfigMenu_UpdateStatus(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Draw_Lock();
|
||||||
|
if (isHidInitialized)
|
||||||
|
hidExit();
|
||||||
|
|
||||||
// Termination request
|
// Termination request
|
||||||
terminationRequest = true;
|
menuShouldExit = true;
|
||||||
svcSignalEvent(terminationRequestEvent);
|
preTerminationRequested = true;
|
||||||
|
svcSignalEvent(preTerminationEvent);
|
||||||
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleNextApplicationDebuggedByForce(u32 notificationId)
|
static void handleNextApplicationDebuggedByForce(u32 notificationId)
|
||||||
@@ -168,14 +214,31 @@ static void handleNextApplicationDebuggedByForce(u32 notificationId)
|
|||||||
TaskRunner_RunTask(debuggerFetchAndSetNextApplicationDebugHandleTask, NULL, 0);
|
TaskRunner_RunTask(debuggerFetchAndSetNextApplicationDebugHandleTask, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handleRestartHbAppNotification(u32 notificationId)
|
||||||
|
{
|
||||||
|
(void)notificationId;
|
||||||
|
TaskRunner_RunTask(HBLDR_RestartHbApplication, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static const ServiceManagerServiceEntry services[] = {
|
static const ServiceManagerServiceEntry services[] = {
|
||||||
{ "hb:ldr", 2, HBLDR_HandleCommands, true },
|
{ "hb:ldr", 2, HBLDR_HandleCommands, true },
|
||||||
{ NULL },
|
{ NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const ServiceManagerNotificationEntry notifications[] = {
|
static const ServiceManagerNotificationEntry notifications[] = {
|
||||||
{ 0x100 , handleTermNotification },
|
{ 0x100 , handleTermNotification },
|
||||||
{ 0x1000, handleNextApplicationDebuggedByForce },
|
{ PTMNOTIFID_SLEEP_REQUESTED, handleSleepNotification },
|
||||||
|
{ PTMNOTIFID_SLEEP_DENIED, handleSleepNotification },
|
||||||
|
{ PTMNOTIFID_SLEEP_ALLOWED, handleSleepNotification },
|
||||||
|
{ PTMNOTIFID_GOING_TO_SLEEP, handleSleepNotification },
|
||||||
|
{ PTMNOTIFID_FULLY_WAKING_UP, handleSleepNotification },
|
||||||
|
{ PTMNOTIFID_FULLY_AWAKE, handleSleepNotification },
|
||||||
|
{ PTMNOTIFID_HALF_AWAKE, handleSleepNotification },
|
||||||
|
{ 0x213, handleShellNotification },
|
||||||
|
{ 0x214, handleShellNotification },
|
||||||
|
{ 0x1000, handleNextApplicationDebuggedByForce },
|
||||||
|
{ 0x2000, handlePreTermNotification },
|
||||||
|
{ 0x3000, handleRestartHbAppNotification },
|
||||||
{ 0x000, NULL },
|
{ 0x000, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -191,9 +254,10 @@ int main(void)
|
|||||||
bufPtrs[2] = IPC_Desc_StaticBuffer(sizeof(ldrArgvBuf), 1);
|
bufPtrs[2] = IPC_Desc_StaticBuffer(sizeof(ldrArgvBuf), 1);
|
||||||
bufPtrs[3] = (u32)ldrArgvBuf;
|
bufPtrs[3] = (u32)ldrArgvBuf;
|
||||||
|
|
||||||
if(R_FAILED(svcCreateEvent(&terminationRequestEvent, RESET_STICKY)))
|
if(R_FAILED(svcCreateEvent(&preTerminationEvent, RESET_STICKY)))
|
||||||
svcBreak(USERBREAK_ASSERT);
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
|
||||||
|
Draw_Init();
|
||||||
Cheat_SeedRng(svcGetSystemTick());
|
Cheat_SeedRng(svcGetSystemTick());
|
||||||
|
|
||||||
MyThread *menuThread = menuCreateThread();
|
MyThread *menuThread = menuCreateThread();
|
||||||
@@ -206,6 +270,7 @@ int main(void)
|
|||||||
TaskRunner_Terminate();
|
TaskRunner_Terminate();
|
||||||
|
|
||||||
MyThread_Join(menuThread, -1LL);
|
MyThread_Join(menuThread, -1LL);
|
||||||
|
|
||||||
MyThread_Join(taskRunnerThread, -1LL);
|
MyThread_Join(taskRunnerThread, -1LL);
|
||||||
MyThread_Join(errDispThread, -1LL);
|
MyThread_Join(errDispThread, -1LL);
|
||||||
|
|
||||||
|
|||||||
@@ -36,66 +36,85 @@
|
|||||||
#include "menus/cheats.h"
|
#include "menus/cheats.h"
|
||||||
#include "minisoc.h"
|
#include "minisoc.h"
|
||||||
|
|
||||||
u32 waitInputWithTimeout(u32 msec)
|
bool isHidInitialized = false;
|
||||||
|
|
||||||
|
// libctru redefinition:
|
||||||
|
|
||||||
|
bool hidShouldUseIrrst(void)
|
||||||
{
|
{
|
||||||
bool pressedKey = false;
|
// ir:rst exposes only two sessions :(
|
||||||
u32 key = 0;
|
return false;
|
||||||
u32 n = 0;
|
}
|
||||||
|
|
||||||
//Wait for no keys to be pressed
|
static inline u32 convertHidKeys(u32 keys)
|
||||||
while(HID_PAD && !terminationRequest && (msec == 0 || n < msec))
|
{
|
||||||
{
|
// Nothing to do yet
|
||||||
svcSleepThread(1 * 1000 * 1000LL);
|
return keys;
|
||||||
n++;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(terminationRequest || (msec != 0 && n >= msec))
|
u32 waitInputWithTimeout(s32 msec)
|
||||||
return 0;
|
{
|
||||||
|
s32 n = 0;
|
||||||
|
u32 keys;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
//Wait for a key to be pressed
|
svcSleepThread(1 * 1000 * 1000LL);
|
||||||
while(!HID_PAD && !terminationRequest && (msec == 0 || n < msec))
|
Draw_Lock();
|
||||||
|
if (!isHidInitialized || menuShouldExit)
|
||||||
{
|
{
|
||||||
svcSleepThread(1 * 1000 * 1000LL);
|
keys = 0;
|
||||||
n++;
|
Draw_Unlock();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
n++;
|
||||||
|
|
||||||
if(terminationRequest || (msec != 0 && n >= msec))
|
hidScanInput();
|
||||||
return 0;
|
keys = convertHidKeys(hidKeysDown()) | (convertHidKeys(hidKeysDownRepeat()) & DIRECTIONAL_KEYS);
|
||||||
|
Draw_Unlock();
|
||||||
|
} while (keys == 0 && !menuShouldExit && isHidInitialized && (msec < 0 || n < msec));
|
||||||
|
|
||||||
key = HID_PAD;
|
|
||||||
|
|
||||||
//Make sure it's pressed
|
return keys;
|
||||||
for(u32 i = 0x26000; i > 0; i --)
|
|
||||||
{
|
|
||||||
if(key != HID_PAD) break;
|
|
||||||
if(i == 1) pressedKey = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while(!pressedKey);
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 waitInput(void)
|
u32 waitInput(void)
|
||||||
{
|
{
|
||||||
return waitInputWithTimeout(0);
|
return waitInputWithTimeout(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 waitComboWithTimeout(u32 msec)
|
static u32 scanHeldKeys(void)
|
||||||
{
|
{
|
||||||
u32 key = 0;
|
u32 keys;
|
||||||
u32 n = 0;
|
|
||||||
|
|
||||||
//Wait for no keys to be pressed
|
Draw_Lock();
|
||||||
while(HID_PAD && !terminationRequest && (msec == 0 || n < msec))
|
|
||||||
|
if (!isHidInitialized || menuShouldExit)
|
||||||
|
keys = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hidScanInput();
|
||||||
|
keys = convertHidKeys(hidKeysHeld());
|
||||||
|
}
|
||||||
|
|
||||||
|
Draw_Unlock();
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 waitComboWithTimeout(s32 msec)
|
||||||
|
{
|
||||||
|
s32 n = 0;
|
||||||
|
u32 keys = 0;
|
||||||
|
u32 tempKeys = 0;
|
||||||
|
|
||||||
|
// Wait for nothing to be pressed
|
||||||
|
while (scanHeldKeys() != 0 && !menuShouldExit && isHidInitialized && (msec < 0 || n < msec))
|
||||||
{
|
{
|
||||||
svcSleepThread(1 * 1000 * 1000LL);
|
svcSleepThread(1 * 1000 * 1000LL);
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(terminationRequest || (msec != 0 && n >= msec))
|
if (menuShouldExit || !isHidInitialized || !(msec < 0 || n < msec))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
do
|
do
|
||||||
@@ -103,31 +122,50 @@ u32 waitComboWithTimeout(u32 msec)
|
|||||||
svcSleepThread(1 * 1000 * 1000LL);
|
svcSleepThread(1 * 1000 * 1000LL);
|
||||||
n++;
|
n++;
|
||||||
|
|
||||||
u32 tempKey = HID_PAD;
|
tempKeys = scanHeldKeys();
|
||||||
|
|
||||||
for(u32 i = 0x26000; i > 0; i--)
|
for (u32 i = 0x10000; i > 0; i--)
|
||||||
{
|
{
|
||||||
if(tempKey != HID_PAD) break;
|
if (tempKeys != scanHeldKeys()) break;
|
||||||
if(i == 1) key = tempKey;
|
if (i == 1) keys = tempKeys;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while((!key || HID_PAD) && !terminationRequest && (msec == 0 || n < msec));
|
while((keys == 0 || scanHeldKeys() != 0) && !menuShouldExit && isHidInitialized && (msec < 0 || n < msec));
|
||||||
|
|
||||||
if(terminationRequest || (msec != 0 && n >= msec))
|
return keys;
|
||||||
return 0;
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 waitCombo(void)
|
u32 waitCombo(void)
|
||||||
{
|
{
|
||||||
return waitComboWithTimeout(0);
|
return waitComboWithTimeout(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static MyThread menuThread;
|
static MyThread menuThread;
|
||||||
static u8 ALIGN(8) menuThreadStack[0x1000];
|
static u8 ALIGN(8) menuThreadStack[0x1000];
|
||||||
static u8 batteryLevel = 255;
|
static u8 batteryLevel = 255;
|
||||||
|
|
||||||
|
static inline u32 menuAdvanceCursor(u32 pos, u32 numItems, s32 displ)
|
||||||
|
{
|
||||||
|
return (pos + numItems + displ) % numItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool menuItemIsHidden(const MenuItem *item)
|
||||||
|
{
|
||||||
|
return item->visibility != NULL && !item->visibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menuCheckN3ds(void)
|
||||||
|
{
|
||||||
|
return isN3DS;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 menuCountItems(const Menu *menu)
|
||||||
|
{
|
||||||
|
u32 n;
|
||||||
|
for (n = 0; menu->items[n].action_type != MENU_END; n++);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
MyThread *menuCreateThread(void)
|
MyThread *menuCreateThread(void)
|
||||||
{
|
{
|
||||||
if(R_FAILED(MyThread_Create(&menuThread, menuThreadMain, menuThreadStack, 0x1000, 52, CORE_SYSTEM)))
|
if(R_FAILED(MyThread_Create(&menuThread, menuThreadMain, menuThreadStack, 0x1000, 52, CORE_SYSTEM)))
|
||||||
@@ -135,70 +173,71 @@ MyThread *menuCreateThread(void)
|
|||||||
return &menuThread;
|
return &menuThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern bool isN3DS;
|
|
||||||
u32 menuCombo;
|
u32 menuCombo;
|
||||||
|
|
||||||
void menuThreadMain(void)
|
void menuThreadMain(void)
|
||||||
{
|
{
|
||||||
if(!isN3DS)
|
if(isN3DS)
|
||||||
{
|
|
||||||
rosalinaMenu.nbItems--;
|
|
||||||
for(u32 i = 0; i <= rosalinaMenu.nbItems; i++)
|
|
||||||
rosalinaMenu.items[i] = rosalinaMenu.items[i+1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
N3DSMenu_UpdateStatus();
|
N3DSMenu_UpdateStatus();
|
||||||
|
|
||||||
bool isAcURegistered = false;
|
while (!isServiceUsable("ac:u") || !isServiceUsable("hid:USER"))
|
||||||
|
svcSleepThread(500 * 1000 * 1000LL);
|
||||||
|
|
||||||
while(!terminationRequest)
|
hidInit(); // assume this doesn't fail
|
||||||
|
isHidInitialized = true;
|
||||||
|
|
||||||
|
while(!preTerminationRequested)
|
||||||
{
|
{
|
||||||
if((HID_PAD & menuCombo) == menuCombo)
|
|
||||||
{
|
|
||||||
if (!isAcURegistered)
|
|
||||||
isAcURegistered = R_SUCCEEDED(srvIsServiceRegistered(&isAcURegistered, "ac:u"))
|
|
||||||
&& isAcURegistered;
|
|
||||||
|
|
||||||
if (isAcURegistered)
|
|
||||||
{
|
|
||||||
menuEnter();
|
|
||||||
if(isN3DS) N3DSMenu_UpdateStatus();
|
|
||||||
menuShow(&rosalinaMenu);
|
|
||||||
menuLeave();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Cheat_ApplyCheats();
|
|
||||||
}
|
|
||||||
svcSleepThread(50 * 1000 * 1000LL);
|
svcSleepThread(50 * 1000 * 1000LL);
|
||||||
|
if (menuShouldExit)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Cheat_ApplyCheats();
|
||||||
|
|
||||||
|
if((scanHeldKeys() & menuCombo) == menuCombo)
|
||||||
|
{
|
||||||
|
menuEnter();
|
||||||
|
if(isN3DS) N3DSMenu_UpdateStatus();
|
||||||
|
menuShow(&rosalinaMenu);
|
||||||
|
menuLeave();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static s32 menuRefCount = 0;
|
static s32 menuRefCount = 0;
|
||||||
void menuEnter(void)
|
void menuEnter(void)
|
||||||
{
|
{
|
||||||
if(AtomicPostIncrement(&menuRefCount) == 0)
|
Draw_Lock();
|
||||||
|
if(!menuShouldExit && menuRefCount == 0)
|
||||||
{
|
{
|
||||||
svcKernelSetState(0x10000, 1);
|
menuRefCount++;
|
||||||
|
svcKernelSetState(0x10000, 2 | 1);
|
||||||
svcSleepThread(5 * 1000 * 100LL);
|
svcSleepThread(5 * 1000 * 100LL);
|
||||||
Draw_SetupFramebuffer();
|
if (R_FAILED(Draw_AllocateFramebufferCache(FB_BOTTOM_SIZE)))
|
||||||
Draw_ClearFramebuffer();
|
{
|
||||||
|
// Oops
|
||||||
|
menuRefCount = 0;
|
||||||
|
svcKernelSetState(0x10000, 2 | 1);
|
||||||
|
svcSleepThread(5 * 1000 * 100LL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Draw_SetupFramebuffer();
|
||||||
}
|
}
|
||||||
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void menuLeave(void)
|
void menuLeave(void)
|
||||||
{
|
{
|
||||||
svcSleepThread(50 * 1000 * 1000);
|
svcSleepThread(50 * 1000 * 1000);
|
||||||
|
|
||||||
if(AtomicDecrement(&menuRefCount) == 0)
|
Draw_Lock();
|
||||||
|
if(--menuRefCount == 0)
|
||||||
{
|
{
|
||||||
Draw_Lock();
|
|
||||||
Draw_FlushFramebuffer();
|
|
||||||
Draw_RestoreFramebuffer();
|
Draw_RestoreFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_FreeFramebufferCache();
|
||||||
svcKernelSetState(0x10000, 1);
|
svcKernelSetState(0x10000, 2 | 1);
|
||||||
}
|
}
|
||||||
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void menuDraw(Menu *menu, u32 selected)
|
static void menuDraw(Menu *menu, u32 selected)
|
||||||
@@ -233,13 +272,17 @@ static void menuDraw(Menu *menu, u32 selected)
|
|||||||
sprintf(versionString, "v%lu.%lu.%lu", GET_VERSION_MAJOR(version), GET_VERSION_MINOR(version), GET_VERSION_REVISION(version));
|
sprintf(versionString, "v%lu.%lu.%lu", GET_VERSION_MAJOR(version), GET_VERSION_MINOR(version), GET_VERSION_REVISION(version));
|
||||||
|
|
||||||
Draw_DrawString(10, 10, COLOR_TITLE, menu->title);
|
Draw_DrawString(10, 10, COLOR_TITLE, menu->title);
|
||||||
|
u32 numItems = menuCountItems(menu);
|
||||||
|
u32 dispY = 0;
|
||||||
|
|
||||||
for(u32 i = 0; i < 15; i++)
|
for(u32 i = 0; i < numItems; i++)
|
||||||
{
|
{
|
||||||
if(i >= menu->nbItems)
|
if (menuItemIsHidden(&menu->items[i]))
|
||||||
break;
|
continue;
|
||||||
Draw_DrawString(30, 30 + i * SPACING_Y, COLOR_WHITE, menu->items[i].title);
|
|
||||||
Draw_DrawCharacter(10, 30 + i * SPACING_Y, COLOR_TITLE, i == selected ? '>' : ' ');
|
Draw_DrawString(30, 30 + dispY, COLOR_WHITE, menu->items[i].title);
|
||||||
|
Draw_DrawCharacter(10, 30 + dispY, COLOR_TITLE, i == selected ? '>' : ' ');
|
||||||
|
dispY += SPACING_Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(miniSocEnabled)
|
if(miniSocEnabled)
|
||||||
@@ -274,17 +317,33 @@ void menuShow(Menu *root)
|
|||||||
Menu *previousMenus[0x80];
|
Menu *previousMenus[0x80];
|
||||||
u32 previousSelectedItems[0x80];
|
u32 previousSelectedItems[0x80];
|
||||||
|
|
||||||
|
u32 numItems = menuCountItems(currentMenu);
|
||||||
|
if (menuItemIsHidden(¤tMenu->items[selectedItem]))
|
||||||
|
selectedItem = menuAdvanceCursor(selectedItem, numItems, 1);
|
||||||
|
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_ClearFramebuffer();
|
Draw_ClearFramebuffer();
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
|
hidSetRepeatParameters(0, 0);
|
||||||
menuDraw(currentMenu, selectedItem);
|
menuDraw(currentMenu, selectedItem);
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
|
|
||||||
|
bool menuComboReleased = false;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
u32 pressed = waitInputWithTimeout(1000);
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
numItems = menuCountItems(currentMenu);
|
||||||
|
|
||||||
if(pressed & BUTTON_A)
|
if(!menuComboReleased && (scanHeldKeys() & menuCombo) != menuCombo)
|
||||||
|
{
|
||||||
|
menuComboReleased = true;
|
||||||
|
Draw_Lock();
|
||||||
|
hidSetRepeatParameters(200, 100);
|
||||||
|
Draw_Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pressed & KEY_A)
|
||||||
{
|
{
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_ClearFramebuffer();
|
Draw_ClearFramebuffer();
|
||||||
@@ -303,6 +362,9 @@ void menuShow(Menu *root)
|
|||||||
currentMenu = currentMenu->items[selectedItem].menu;
|
currentMenu = currentMenu->items[selectedItem].menu;
|
||||||
selectedItem = 0;
|
selectedItem = 0;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
__builtin_trap(); // oops
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
@@ -310,7 +372,7 @@ void menuShow(Menu *root)
|
|||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
else if(pressed & BUTTON_B)
|
else if(pressed & KEY_B)
|
||||||
{
|
{
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_ClearFramebuffer();
|
Draw_ClearFramebuffer();
|
||||||
@@ -325,20 +387,22 @@ void menuShow(Menu *root)
|
|||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if(pressed & BUTTON_DOWN)
|
else if(pressed & KEY_DOWN)
|
||||||
{
|
{
|
||||||
if(++selectedItem >= currentMenu->nbItems)
|
selectedItem = menuAdvanceCursor(selectedItem, numItems, 1);
|
||||||
selectedItem = 0;
|
if (menuItemIsHidden(¤tMenu->items[selectedItem]))
|
||||||
|
selectedItem = menuAdvanceCursor(selectedItem, numItems, 1);
|
||||||
}
|
}
|
||||||
else if(pressed & BUTTON_UP)
|
else if(pressed & KEY_UP)
|
||||||
{
|
{
|
||||||
if(selectedItem-- <= 0)
|
selectedItem = menuAdvanceCursor(selectedItem, numItems, -1);
|
||||||
selectedItem = currentMenu->nbItems - 1;
|
if (menuItemIsHidden(¤tMenu->items[selectedItem]))
|
||||||
|
selectedItem = menuAdvanceCursor(selectedItem, numItems, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
menuDraw(currentMenu, selectedItem);
|
menuDraw(currentMenu, selectedItem);
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
while(!terminationRequest);
|
while(!menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include "menus/process_list.h"
|
#include "menus/process_list.h"
|
||||||
#include "menus/process_patches.h"
|
|
||||||
#include "menus/n3ds.h"
|
#include "menus/n3ds.h"
|
||||||
#include "menus/debugger.h"
|
#include "menus/debugger.h"
|
||||||
#include "menus/miscellaneous.h"
|
#include "menus/miscellaneous.h"
|
||||||
@@ -39,25 +38,66 @@
|
|||||||
#include "ifile.h"
|
#include "ifile.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
|
#include "process_patches.h"
|
||||||
|
#include "luminance.h"
|
||||||
|
|
||||||
Menu rosalinaMenu = {
|
Menu rosalinaMenu = {
|
||||||
"Rosalina menu",
|
"Rosalina menu",
|
||||||
.nbItems = 11,
|
|
||||||
{
|
{
|
||||||
{ "New 3DS menu...", MENU, .menu = &N3DSMenu },
|
{ "Take screenshot", METHOD, .method = &RosalinaMenu_TakeScreenshot },
|
||||||
|
{ "Change screen brightness", METHOD, .method = &RosalinaMenu_ChangeScreenBrightness },
|
||||||
{ "Cheats...", METHOD, .method = &RosalinaMenu_Cheats },
|
{ "Cheats...", METHOD, .method = &RosalinaMenu_Cheats },
|
||||||
{ "Process list", METHOD, .method = &RosalinaMenu_ProcessList },
|
{ "Process list", METHOD, .method = &RosalinaMenu_ProcessList },
|
||||||
{ "Take screenshot (slow!)", METHOD, .method = &RosalinaMenu_TakeScreenshot },
|
|
||||||
{ "Debugger options...", MENU, .menu = &debuggerMenu },
|
{ "Debugger options...", MENU, .menu = &debuggerMenu },
|
||||||
{ "System configuration...", MENU, .menu = &sysconfigMenu },
|
{ "System configuration...", MENU, .menu = &sysconfigMenu },
|
||||||
{ "Screen filters...", MENU, .menu = &screenFiltersMenu },
|
{ "Screen filters...", MENU, .menu = &screenFiltersMenu },
|
||||||
|
{ "New 3DS menu...", MENU, .menu = &N3DSMenu, .visibility = &menuCheckN3ds },
|
||||||
{ "Miscellaneous options...", MENU, .menu = &miscellaneousMenu },
|
{ "Miscellaneous options...", MENU, .menu = &miscellaneousMenu },
|
||||||
{ "Power off", METHOD, .method = &RosalinaMenu_PowerOff },
|
{ "Power off", METHOD, .method = &RosalinaMenu_PowerOff },
|
||||||
{ "Reboot", METHOD, .method = &RosalinaMenu_Reboot },
|
{ "Reboot", METHOD, .method = &RosalinaMenu_Reboot },
|
||||||
{ "Credits", METHOD, .method = &RosalinaMenu_ShowCredits }
|
{ "Credits", METHOD, .method = &RosalinaMenu_ShowCredits },
|
||||||
|
{ "Debug info", METHOD, .method = &RosalinaMenu_ShowDebugInfo, .visibility = &rosalinaMenuShouldShowDebugInfo },
|
||||||
|
{},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool rosalinaMenuShouldShowDebugInfo(void)
|
||||||
|
{
|
||||||
|
// Don't show on release builds
|
||||||
|
|
||||||
|
s64 out;
|
||||||
|
svcGetSystemInfo(&out, 0x10000, 0x200);
|
||||||
|
return out == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RosalinaMenu_ShowDebugInfo(void)
|
||||||
|
{
|
||||||
|
Draw_Lock();
|
||||||
|
Draw_ClearFramebuffer();
|
||||||
|
Draw_FlushFramebuffer();
|
||||||
|
Draw_Unlock();
|
||||||
|
|
||||||
|
char memoryMap[512];
|
||||||
|
formatMemoryMapOfProcess(memoryMap, 511, CUR_PROCESS_HANDLE);
|
||||||
|
|
||||||
|
s64 kextAddrSize;
|
||||||
|
svcGetSystemInfo(&kextAddrSize, 0x10000, 0x300);
|
||||||
|
u32 kextPa = (u32)((u64)kextAddrSize >> 32);
|
||||||
|
u32 kextSize = (u32)kextAddrSize;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Draw_Lock();
|
||||||
|
Draw_DrawString(10, 10, COLOR_TITLE, "Rosalina -- Debug info");
|
||||||
|
|
||||||
|
u32 posY = Draw_DrawString(10, 30, COLOR_WHITE, memoryMap);
|
||||||
|
Draw_DrawFormattedString(10, posY, COLOR_WHITE, "Kernel ext PA: %08lx - %08lx\n", kextPa, kextPa + kextSize);
|
||||||
|
Draw_FlushFramebuffer();
|
||||||
|
Draw_Unlock();
|
||||||
|
}
|
||||||
|
while(!(waitInput() & KEY_B) && !menuShouldExit);
|
||||||
|
}
|
||||||
|
|
||||||
void RosalinaMenu_ShowCredits(void)
|
void RosalinaMenu_ShowCredits(void)
|
||||||
{
|
{
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
@@ -81,15 +121,15 @@ void RosalinaMenu_ShowCredits(void)
|
|||||||
Draw_DrawString(10, posY, COLOR_WHITE,
|
Draw_DrawString(10, posY, COLOR_WHITE,
|
||||||
(
|
(
|
||||||
"Special thanks to:\n"
|
"Special thanks to:\n"
|
||||||
" Bond697, WinterMute, piepie62, yifanlu\n"
|
" fincs, WinterMute, mtheall, piepie62,\n"
|
||||||
" Luma3DS contributors, ctrulib contributors,\n"
|
" Luma3DS contributors, libctru contributors,\n"
|
||||||
" other people"
|
" other people"
|
||||||
));
|
));
|
||||||
|
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
while(!(waitInput() & BUTTON_B) && !terminationRequest);
|
while(!(waitInput() & KEY_B) && !menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RosalinaMenu_Reboot(void)
|
void RosalinaMenu_Reboot(void)
|
||||||
@@ -102,21 +142,120 @@ void RosalinaMenu_Reboot(void)
|
|||||||
do
|
do
|
||||||
{
|
{
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_DrawString(10, 10, COLOR_TITLE, "Rosalina menu");
|
Draw_DrawString(10, 10, COLOR_TITLE, "Reboot");
|
||||||
Draw_DrawString(10, 30, COLOR_WHITE, "Press A to reboot, press B to go back.");
|
Draw_DrawString(10, 30, COLOR_WHITE, "Press A to reboot, press B to go back.");
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
|
|
||||||
u32 pressed = waitInputWithTimeout(1000);
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
|
||||||
if(pressed & BUTTON_A)
|
if(pressed & KEY_A)
|
||||||
{
|
{
|
||||||
APT_HardwareResetAsync();
|
|
||||||
menuLeave();
|
menuLeave();
|
||||||
} else if(pressed & BUTTON_B)
|
APT_HardwareResetAsync();
|
||||||
|
return;
|
||||||
|
} else if(pressed & KEY_B)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while(!terminationRequest);
|
while(!menuShouldExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RosalinaMenu_ChangeScreenBrightness(void)
|
||||||
|
{
|
||||||
|
Draw_Lock();
|
||||||
|
Draw_ClearFramebuffer();
|
||||||
|
Draw_FlushFramebuffer();
|
||||||
|
Draw_Unlock();
|
||||||
|
|
||||||
|
// gsp:LCD GetLuminance is stubbed on O3DS so we have to implement it ourselves... damn it.
|
||||||
|
// Assume top and bottom screen luminances are the same (should be; if not, we'll set them to the same values).
|
||||||
|
u32 luminance = getCurrentLuminance(false);
|
||||||
|
u32 minLum = getMinLuminancePreset();
|
||||||
|
u32 maxLum = getMaxLuminancePreset();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Draw_Lock();
|
||||||
|
Draw_DrawString(10, 10, COLOR_TITLE, "Screen brightness");
|
||||||
|
u32 posY = 30;
|
||||||
|
posY = Draw_DrawFormattedString(
|
||||||
|
10,
|
||||||
|
posY,
|
||||||
|
COLOR_WHITE,
|
||||||
|
"Current luminance: %lu (min. %lu, max. %lu)\n\n",
|
||||||
|
luminance,
|
||||||
|
minLum,
|
||||||
|
maxLum
|
||||||
|
);
|
||||||
|
posY = Draw_DrawString(10, posY, COLOR_WHITE, "Controls: Up/Down for +-1, Right/Left for +-10.\n");
|
||||||
|
posY = Draw_DrawString(10, posY, COLOR_WHITE, "Press A to start, B to exit.\n\n");
|
||||||
|
|
||||||
|
posY = Draw_DrawString(10, posY, COLOR_RED, "WARNING: \n");
|
||||||
|
posY = Draw_DrawString(10, posY, COLOR_WHITE, " * value will be limited by the presets.\n");
|
||||||
|
posY = Draw_DrawString(10, posY, COLOR_WHITE, " * bottom framebuffer will be restored until\nyou exit.");
|
||||||
|
Draw_FlushFramebuffer();
|
||||||
|
Draw_Unlock();
|
||||||
|
|
||||||
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
|
||||||
|
if (pressed & KEY_A)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (pressed & KEY_B)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (!menuShouldExit);
|
||||||
|
|
||||||
|
Draw_Lock();
|
||||||
|
|
||||||
|
Draw_RestoreFramebuffer();
|
||||||
|
Draw_FreeFramebufferCache();
|
||||||
|
|
||||||
|
svcKernelSetState(0x10000, 2); // unblock gsp
|
||||||
|
gspLcdInit(); // assume it doesn't fail. If it does, brightness won't change, anyway.
|
||||||
|
|
||||||
|
// gsp:LCD will normalize the brightness between top/bottom screen, handle PWM, etc.
|
||||||
|
|
||||||
|
s32 lum = (s32)luminance;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
if (pressed & DIRECTIONAL_KEYS)
|
||||||
|
{
|
||||||
|
if (pressed & KEY_UP)
|
||||||
|
lum += 1;
|
||||||
|
else if (pressed & KEY_DOWN)
|
||||||
|
lum -= 1;
|
||||||
|
else if (pressed & KEY_RIGHT)
|
||||||
|
lum += 10;
|
||||||
|
else if (pressed & KEY_LEFT)
|
||||||
|
lum -= 10;
|
||||||
|
|
||||||
|
lum = lum < 0 ? 0 : lum;
|
||||||
|
|
||||||
|
// We need to call gsp here because updating the active duty LUT is a bit tedious (plus, GSP has internal state).
|
||||||
|
// This is actually SetLuminance:
|
||||||
|
GSPLCD_SetBrightnessRaw(BIT(GSP_SCREEN_TOP) | BIT(GSP_SCREEN_BOTTOM), lum);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pressed & KEY_B)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while (!menuShouldExit);
|
||||||
|
|
||||||
|
gspLcdExit();
|
||||||
|
svcKernelSetState(0x10000, 2); // block gsp again
|
||||||
|
|
||||||
|
if (R_FAILED(Draw_AllocateFramebufferCache(FB_BOTTOM_SIZE)))
|
||||||
|
{
|
||||||
|
// Shouldn't happen
|
||||||
|
__builtin_trap();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Draw_SetupFramebuffer();
|
||||||
|
|
||||||
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RosalinaMenu_PowerOff(void) // Soft shutdown.
|
void RosalinaMenu_PowerOff(void) // Soft shutdown.
|
||||||
@@ -129,32 +268,76 @@ void RosalinaMenu_PowerOff(void) // Soft shutdown.
|
|||||||
do
|
do
|
||||||
{
|
{
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_DrawString(10, 10, COLOR_TITLE, "Rosalina menu");
|
Draw_DrawString(10, 10, COLOR_TITLE, "Power off");
|
||||||
Draw_DrawString(10, 30, COLOR_WHITE, "Press A to power off, press B to go back.");
|
Draw_DrawString(10, 30, COLOR_WHITE, "Press A to power off, press B to go back.");
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
|
|
||||||
u32 pressed = waitInputWithTimeout(1000);
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
|
||||||
if(pressed & BUTTON_A)
|
if(pressed & KEY_A)
|
||||||
{
|
{
|
||||||
menuLeave();
|
menuLeave();
|
||||||
srvPublishToSubscriber(0x203, 0);
|
srvPublishToSubscriber(0x203, 0);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if(pressed & BUTTON_B)
|
else if(pressed & KEY_B)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while(!terminationRequest);
|
while(!menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern u8 framebufferCache[FB_BOTTOM_SIZE];
|
|
||||||
void RosalinaMenu_TakeScreenshot(void)
|
|
||||||
{
|
|
||||||
#define TRY(expr) if(R_FAILED(res = (expr))) goto end;
|
#define TRY(expr) if(R_FAILED(res = (expr))) goto end;
|
||||||
|
|
||||||
|
static s64 timeSpentConvertingScreenshot = 0;
|
||||||
|
static s64 timeSpentWritingScreenshot = 0;
|
||||||
|
|
||||||
|
static Result RosalinaMenu_WriteScreenshot(IFile *file, u32 width, bool top, bool left)
|
||||||
|
{
|
||||||
u64 total;
|
u64 total;
|
||||||
|
Result res = 0;
|
||||||
|
u32 lineSize = 3 * width;
|
||||||
|
u32 remaining = lineSize * 240;
|
||||||
|
|
||||||
|
TRY(Draw_AllocateFramebufferCacheForScreenshot(remaining));
|
||||||
|
|
||||||
|
u8 *framebufferCache = (u8 *)Draw_GetFramebufferCache();
|
||||||
|
u8 *framebufferCacheEnd = framebufferCache + Draw_GetFramebufferCacheSize();
|
||||||
|
|
||||||
|
u8 *buf = framebufferCache;
|
||||||
|
Draw_CreateBitmapHeader(framebufferCache, width, 240);
|
||||||
|
buf += 54;
|
||||||
|
|
||||||
|
u32 y = 0;
|
||||||
|
// Our buffer might be smaller than the size of the screenshot...
|
||||||
|
while (remaining != 0)
|
||||||
|
{
|
||||||
|
s64 t0 = svcGetSystemTick();
|
||||||
|
u32 available = (u32)(framebufferCacheEnd - buf);
|
||||||
|
u32 size = available < remaining ? available : remaining;
|
||||||
|
u32 nlines = size / lineSize;
|
||||||
|
Draw_ConvertFrameBufferLines(buf, width, y, nlines, top, left);
|
||||||
|
|
||||||
|
s64 t1 = svcGetSystemTick();
|
||||||
|
timeSpentConvertingScreenshot += t1 - t0;
|
||||||
|
TRY(IFile_Write(file, &total, framebufferCache, (y == 0 ? 54 : 0) + lineSize * nlines, 0)); // don't forget to write the header
|
||||||
|
timeSpentWritingScreenshot += svcGetSystemTick() - t1;
|
||||||
|
|
||||||
|
y += nlines;
|
||||||
|
remaining -= lineSize * nlines;
|
||||||
|
buf = framebufferCache;
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
|
||||||
|
Draw_FreeFramebufferCache();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RosalinaMenu_TakeScreenshot(void)
|
||||||
|
{
|
||||||
IFile file;
|
IFile file;
|
||||||
Result res;
|
Result res = 0;
|
||||||
|
|
||||||
char filename[64];
|
char filename[64];
|
||||||
|
|
||||||
@@ -163,15 +346,25 @@ void RosalinaMenu_TakeScreenshot(void)
|
|||||||
s64 out;
|
s64 out;
|
||||||
bool isSdMode;
|
bool isSdMode;
|
||||||
|
|
||||||
|
timeSpentConvertingScreenshot = 0;
|
||||||
|
timeSpentWritingScreenshot = 0;
|
||||||
|
|
||||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 0x203))) svcBreak(USERBREAK_ASSERT);
|
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 0x203))) svcBreak(USERBREAK_ASSERT);
|
||||||
isSdMode = (bool)out;
|
isSdMode = (bool)out;
|
||||||
|
|
||||||
archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
|
archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_RestoreFramebuffer();
|
Draw_RestoreFramebuffer();
|
||||||
|
Draw_FreeFramebufferCache();
|
||||||
|
|
||||||
svcFlushEntireDataCache();
|
svcFlushEntireDataCache();
|
||||||
|
|
||||||
|
bool is3d;
|
||||||
|
u32 topWidth, bottomWidth; // actually Y-dim
|
||||||
|
|
||||||
|
Draw_GetCurrentScreenInfo(&bottomWidth, &is3d, false);
|
||||||
|
Draw_GetCurrentScreenInfo(&topWidth, &is3d, true);
|
||||||
|
|
||||||
res = FSUSER_OpenArchive(&archive, archiveId, fsMakePath(PATH_EMPTY, ""));
|
res = FSUSER_OpenArchive(&archive, archiveId, fsMakePath(PATH_EMPTY, ""));
|
||||||
if(R_SUCCEEDED(res))
|
if(R_SUCCEEDED(res))
|
||||||
{
|
{
|
||||||
@@ -226,58 +419,30 @@ void RosalinaMenu_TakeScreenshot(void)
|
|||||||
|
|
||||||
sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_top.bmp", year, month, days, hours, minutes, seconds, milliseconds);
|
sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_top.bmp", year, month, days, hours, minutes, seconds, milliseconds);
|
||||||
TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE));
|
TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE));
|
||||||
Draw_CreateBitmapHeader(framebufferCache, 400, 240);
|
TRY(RosalinaMenu_WriteScreenshot(&file, topWidth, true, true));
|
||||||
|
|
||||||
for(u32 y = 0; y < 120; y++)
|
|
||||||
Draw_ConvertFrameBufferLine(framebufferCache + 54 + 3 * 400 * y, true, true, y);
|
|
||||||
|
|
||||||
TRY(IFile_Write(&file, &total, framebufferCache, 54 + 3 * 400 * 120, 0));
|
|
||||||
|
|
||||||
for(u32 y = 120; y < 240; y++)
|
|
||||||
Draw_ConvertFrameBufferLine(framebufferCache + 3 * 400 * (y - 120), true, true, y);
|
|
||||||
|
|
||||||
TRY(IFile_Write(&file, &total, framebufferCache, 3 * 400 * 120, 0));
|
|
||||||
TRY(IFile_Close(&file));
|
TRY(IFile_Close(&file));
|
||||||
|
|
||||||
sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_bot.bmp", year, month, days, hours, minutes, seconds, milliseconds);
|
sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_bot.bmp", year, month, days, hours, minutes, seconds, milliseconds);
|
||||||
TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE));
|
TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE));
|
||||||
Draw_CreateBitmapHeader(framebufferCache, 320, 240);
|
TRY(RosalinaMenu_WriteScreenshot(&file, bottomWidth, false, true));
|
||||||
|
|
||||||
for(u32 y = 0; y < 120; y++)
|
|
||||||
Draw_ConvertFrameBufferLine(framebufferCache + 54 + 3 * 320 * y, false, true, y);
|
|
||||||
|
|
||||||
TRY(IFile_Write(&file, &total, framebufferCache, 54 + 3 * 320 * 120, 0));
|
|
||||||
|
|
||||||
for(u32 y = 120; y < 240; y++)
|
|
||||||
Draw_ConvertFrameBufferLine(framebufferCache + 3 * 320 * (y - 120), false, true, y);
|
|
||||||
|
|
||||||
TRY(IFile_Write(&file, &total, framebufferCache, 3 * 320 * 120, 0));
|
|
||||||
TRY(IFile_Close(&file));
|
TRY(IFile_Close(&file));
|
||||||
|
|
||||||
if((GPU_FB_TOP_FMT & 0x20) && (Draw_GetCurrentFramebufferAddress(true, true) != Draw_GetCurrentFramebufferAddress(true, false)))
|
if(is3d && (Draw_GetCurrentFramebufferAddress(true, true) != Draw_GetCurrentFramebufferAddress(true, false)))
|
||||||
{
|
{
|
||||||
sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_top_right.bmp", year, month, days, hours, minutes, seconds, milliseconds);
|
sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_top_right.bmp", year, month, days, hours, minutes, seconds, milliseconds);
|
||||||
TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE));
|
TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE));
|
||||||
Draw_CreateBitmapHeader(framebufferCache, 400, 240);
|
TRY(RosalinaMenu_WriteScreenshot(&file, topWidth, true, false));
|
||||||
|
|
||||||
for(u32 y = 0; y < 120; y++)
|
|
||||||
Draw_ConvertFrameBufferLine(framebufferCache + 54 + 3 * 400 * y, true, false, y);
|
|
||||||
|
|
||||||
TRY(IFile_Write(&file, &total, framebufferCache, 54 + 3 * 400 * 120, 0));
|
|
||||||
|
|
||||||
for(u32 y = 120; y < 240; y++)
|
|
||||||
Draw_ConvertFrameBufferLine(framebufferCache + 3 * 400 * (y - 120), true, false, y);
|
|
||||||
|
|
||||||
TRY(IFile_Write(&file, &total, framebufferCache, 3 * 400 * 120, 0));
|
|
||||||
TRY(IFile_Close(&file));
|
TRY(IFile_Close(&file));
|
||||||
}
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
IFile_Close(&file);
|
IFile_Close(&file);
|
||||||
|
|
||||||
|
if (R_FAILED(Draw_AllocateFramebufferCache(FB_BOTTOM_SIZE)))
|
||||||
|
__builtin_trap(); // We're f***ed if this happens
|
||||||
|
|
||||||
svcFlushEntireDataCache();
|
svcFlushEntireDataCache();
|
||||||
Draw_SetupFramebuffer();
|
Draw_SetupFramebuffer();
|
||||||
Draw_ClearFramebuffer();
|
|
||||||
Draw_FlushFramebuffer();
|
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
|
|
||||||
do
|
do
|
||||||
@@ -287,12 +452,19 @@ end:
|
|||||||
if(R_FAILED(res))
|
if(R_FAILED(res))
|
||||||
Draw_DrawFormattedString(10, 30, COLOR_WHITE, "Operation failed (0x%08lx).", (u32)res);
|
Draw_DrawFormattedString(10, 30, COLOR_WHITE, "Operation failed (0x%08lx).", (u32)res);
|
||||||
else
|
else
|
||||||
Draw_DrawString(10, 30, COLOR_WHITE, "Operation succeeded.");
|
{
|
||||||
|
u32 t1 = (u32)(1000 * timeSpentConvertingScreenshot / SYSCLOCK_ARM11);
|
||||||
|
u32 t2 = (u32)(1000 * timeSpentWritingScreenshot / SYSCLOCK_ARM11);
|
||||||
|
u32 posY = 30;
|
||||||
|
posY = Draw_DrawString(10, posY, COLOR_WHITE, "Operation succeeded.\n\n");
|
||||||
|
posY = Draw_DrawFormattedString(10, posY, COLOR_WHITE, "Time spent converting: %5lums\n", t1);
|
||||||
|
posY = Draw_DrawFormattedString(10, posY, COLOR_WHITE, "Time spent writing files: %5lums\n", t2);
|
||||||
|
}
|
||||||
|
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
while(!(waitInput() & BUTTON_B) && !terminationRequest);
|
while(!(waitInput() & KEY_B) && !menuShouldExit);
|
||||||
|
|
||||||
#undef TRY
|
#undef TRY
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1858,15 +1858,19 @@ static void Cheat_LoadCheatsIntoMemory(u64 titleId)
|
|||||||
memset(cheatPage, 0, 0x1000);
|
memset(cheatPage, 0, 0x1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 Cheat_GetCurrentTitleId(u64* titleId)
|
static u32 Cheat_GetCurrentProcessAndTitleId(u64* titleId)
|
||||||
{
|
{
|
||||||
|
FS_ProgramInfo programInfo;
|
||||||
u32 pid;
|
u32 pid;
|
||||||
Result res = PMDBG_GetCurrentAppTitleIdAndPid(titleId, &pid);
|
u32 launchFlags;
|
||||||
|
Result res = PMDBG_GetCurrentAppInfo(&programInfo, &pid, &launchFlags);
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
*titleId = 0;
|
*titleId = 0;
|
||||||
return res;
|
return 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
return res;
|
|
||||||
|
*titleId = programInfo.programId;
|
||||||
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cheat_SeedRng(u64 seed)
|
void Cheat_SeedRng(u64 seed)
|
||||||
@@ -1882,7 +1886,7 @@ void Cheat_ApplyCheats(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 titleId = 0;
|
u64 titleId = 0;
|
||||||
u32 pid = Cheat_GetCurrentTitleId(&titleId);
|
u32 pid = Cheat_GetCurrentProcessAndTitleId(&titleId);
|
||||||
|
|
||||||
if (!titleId)
|
if (!titleId)
|
||||||
{
|
{
|
||||||
@@ -1908,7 +1912,7 @@ void Cheat_ApplyCheats(void)
|
|||||||
void RosalinaMenu_Cheats(void)
|
void RosalinaMenu_Cheats(void)
|
||||||
{
|
{
|
||||||
u64 titleId = 0;
|
u64 titleId = 0;
|
||||||
u32 pid = Cheat_GetCurrentTitleId(&titleId);
|
u32 pid = Cheat_GetCurrentProcessAndTitleId(&titleId);
|
||||||
|
|
||||||
if (titleId != 0)
|
if (titleId != 0)
|
||||||
{
|
{
|
||||||
@@ -1940,7 +1944,7 @@ void RosalinaMenu_Cheats(void)
|
|||||||
|
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
} while (!(waitInput() & BUTTON_B) && !terminationRequest);
|
} while (!(waitInput() & KEY_B) && !menuShouldExit);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1978,18 +1982,18 @@ void RosalinaMenu_Cheats(void)
|
|||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
|
|
||||||
if (terminationRequest) break;
|
if (menuShouldExit) break;
|
||||||
|
|
||||||
u32 pressed;
|
u32 pressed;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
pressed = waitInputWithTimeout(50);
|
pressed = waitInputWithTimeout(50);
|
||||||
if (pressed != 0) break;
|
if (pressed != 0) break;
|
||||||
} while (pressed == 0 && !terminationRequest);
|
} while (pressed == 0 && !menuShouldExit);
|
||||||
|
|
||||||
if (pressed & BUTTON_B)
|
if (pressed & KEY_B)
|
||||||
break;
|
break;
|
||||||
else if ((pressed & BUTTON_A) && R_SUCCEEDED(r))
|
else if ((pressed & KEY_A) && R_SUCCEEDED(r))
|
||||||
{
|
{
|
||||||
if (cheats[selected]->active)
|
if (cheats[selected]->active)
|
||||||
{
|
{
|
||||||
@@ -2000,13 +2004,13 @@ void RosalinaMenu_Cheats(void)
|
|||||||
r = Cheat_MapMemoryAndApplyCheat(pid, cheats[selected]);
|
r = Cheat_MapMemoryAndApplyCheat(pid, cheats[selected]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (pressed & BUTTON_DOWN)
|
else if (pressed & KEY_DOWN)
|
||||||
selected++;
|
selected++;
|
||||||
else if (pressed & BUTTON_UP)
|
else if (pressed & KEY_UP)
|
||||||
selected--;
|
selected--;
|
||||||
else if (pressed & BUTTON_LEFT)
|
else if (pressed & KEY_LEFT)
|
||||||
selected -= CHEATS_PER_MENU_PAGE;
|
selected -= CHEATS_PER_MENU_PAGE;
|
||||||
else if (pressed & BUTTON_RIGHT)
|
else if (pressed & KEY_RIGHT)
|
||||||
{
|
{
|
||||||
if (selected + CHEATS_PER_MENU_PAGE < cheatCount)
|
if (selected + CHEATS_PER_MENU_PAGE < cheatCount)
|
||||||
selected += CHEATS_PER_MENU_PAGE;
|
selected += CHEATS_PER_MENU_PAGE;
|
||||||
@@ -2021,7 +2025,7 @@ void RosalinaMenu_Cheats(void)
|
|||||||
|
|
||||||
pagePrev = page;
|
pagePrev = page;
|
||||||
page = selected / CHEATS_PER_MENU_PAGE;
|
page = selected / CHEATS_PER_MENU_PAGE;
|
||||||
} while (!terminationRequest);
|
} while (!menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,11 +38,11 @@
|
|||||||
|
|
||||||
Menu debuggerMenu = {
|
Menu debuggerMenu = {
|
||||||
"Debugger options menu",
|
"Debugger options menu",
|
||||||
.nbItems = 3,
|
|
||||||
{
|
{
|
||||||
{ "Enable debugger", METHOD, .method = &DebuggerMenu_EnableDebugger },
|
{ "Enable debugger", METHOD, .method = &DebuggerMenu_EnableDebugger },
|
||||||
{ "Disable debugger", METHOD, .method = &DebuggerMenu_DisableDebugger },
|
{ "Disable debugger", METHOD, .method = &DebuggerMenu_DisableDebugger },
|
||||||
{ "Force-debug next application at launch", METHOD, .method = &DebuggerMenu_DebugNextApplicationByForce },
|
{ "Force-debug next application at launch", METHOD, .method = &DebuggerMenu_DebugNextApplicationByForce },
|
||||||
|
{},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -84,6 +84,29 @@ void debuggerFetchAndSetNextApplicationDebugHandleTask(void *argdata)
|
|||||||
GDB_UnlockAllContexts(&gdbServer);
|
GDB_UnlockAllContexts(&gdbServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result debuggerDisable(s64 timeout)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
bool initialized = gdbServer.referenceCount != 0;
|
||||||
|
if(initialized)
|
||||||
|
{
|
||||||
|
svcSignalEvent(gdbServer.super.shall_terminate_event);
|
||||||
|
server_kill_connections(&gdbServer.super);
|
||||||
|
|
||||||
|
res = MyThread_Join(&debuggerDebugThread, timeout);
|
||||||
|
if(res == 0)
|
||||||
|
res = MyThread_Join(&debuggerSocketThread, timeout);
|
||||||
|
|
||||||
|
Handle dummy = 0;
|
||||||
|
PMDBG_RunQueuedProcess(&dummy);
|
||||||
|
svcCloseHandle(dummy);
|
||||||
|
PMDBG_DebugNextApplicationByForce(false);
|
||||||
|
nextApplicationGdbCtx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void DebuggerMenu_EnableDebugger(void)
|
void DebuggerMenu_EnableDebugger(void)
|
||||||
{
|
{
|
||||||
bool done = false, alreadyEnabled = gdbServer.super.running;
|
bool done = false, alreadyEnabled = gdbServer.super.running;
|
||||||
@@ -115,16 +138,18 @@ void DebuggerMenu_EnableDebugger(void)
|
|||||||
if(!done)
|
if(!done)
|
||||||
{
|
{
|
||||||
res = GDB_InitializeServer(&gdbServer);
|
res = GDB_InitializeServer(&gdbServer);
|
||||||
|
Handle handles[3] = { gdbServer.super.started_event, gdbServer.super.shall_terminate_event, preTerminationEvent };
|
||||||
|
s32 idx;
|
||||||
if(R_SUCCEEDED(res))
|
if(R_SUCCEEDED(res))
|
||||||
{
|
{
|
||||||
debuggerCreateSocketThread();
|
debuggerCreateSocketThread();
|
||||||
debuggerCreateDebugThread();
|
debuggerCreateDebugThread();
|
||||||
res = svcWaitSynchronization(gdbServer.super.started_event, 10 * 1000 * 1000 * 1000LL);
|
res = svcWaitSynchronizationN(&idx, handles, 3, false, 5 * 1000 * 1000 * 1000LL);
|
||||||
|
if(res == 0) res = gdbServer.super.init_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(res != 0)
|
if(res != 0)
|
||||||
sprintf(buf, "Starting debugger... failed (0x%08lx).", (u32)res);
|
sprintf(buf, "Starting debugger... failed (0x%08lx).", (u32)res);
|
||||||
|
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
if(res == 0)
|
if(res == 0)
|
||||||
@@ -136,33 +161,16 @@ void DebuggerMenu_EnableDebugger(void)
|
|||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
while(!(waitInput() & BUTTON_B) && !terminationRequest);
|
while(!(waitInput() & KEY_B) && !menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerMenu_DisableDebugger(void)
|
void DebuggerMenu_DisableDebugger(void)
|
||||||
{
|
{
|
||||||
bool initialized = gdbServer.referenceCount != 0;
|
bool initialized = gdbServer.referenceCount != 0;
|
||||||
Result res = 0;
|
|
||||||
|
Result res = initialized ? debuggerDisable(2 * 1000 * 1000 * 1000LL) : 0;
|
||||||
char buf[65];
|
char buf[65];
|
||||||
|
|
||||||
if(initialized)
|
|
||||||
{
|
|
||||||
svcSignalEvent(gdbServer.super.shall_terminate_event);
|
|
||||||
server_kill_connections(&gdbServer.super);
|
|
||||||
//server_set_should_close_all(&gdbServer.super);
|
|
||||||
|
|
||||||
res = MyThread_Join(&debuggerDebugThread, 2 * 1000 * 1000 * 1000LL);
|
|
||||||
if(res == 0)
|
|
||||||
res = MyThread_Join(&debuggerSocketThread, 2 * 1000 * 1000 * 1000LL);
|
|
||||||
|
|
||||||
Handle dummy = 0;
|
|
||||||
PMDBG_RunQueuedProcess(&dummy);
|
|
||||||
svcCloseHandle(dummy);
|
|
||||||
PMDBG_DebugNextApplicationByForce(false);
|
|
||||||
nextApplicationGdbCtx = NULL;
|
|
||||||
svcKernelSetState(0x10000, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(res != 0)
|
if(res != 0)
|
||||||
sprintf(buf, "Failed to disable debugger (0x%08lx).", (u32)res);
|
sprintf(buf, "Failed to disable debugger (0x%08lx).", (u32)res);
|
||||||
|
|
||||||
@@ -174,7 +182,7 @@ void DebuggerMenu_DisableDebugger(void)
|
|||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
while(!(waitInput() & BUTTON_B) && !terminationRequest);
|
while(!(waitInput() & KEY_B) && !menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerMenu_DebugNextApplicationByForce(void)
|
void DebuggerMenu_DebugNextApplicationByForce(void)
|
||||||
@@ -223,7 +231,7 @@ void DebuggerMenu_DebugNextApplicationByForce(void)
|
|||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
while(!(waitInput() & BUTTON_B) && !terminationRequest);
|
while(!(waitInput() & KEY_B) && !menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void debuggerSocketThreadMain(void)
|
void debuggerSocketThreadMain(void)
|
||||||
|
|||||||
@@ -39,29 +39,30 @@
|
|||||||
|
|
||||||
Menu miscellaneousMenu = {
|
Menu miscellaneousMenu = {
|
||||||
"Miscellaneous options menu",
|
"Miscellaneous options menu",
|
||||||
.nbItems = 5,
|
|
||||||
{
|
{
|
||||||
{ "Switch the hb. title to the current app.", METHOD, .method = &MiscellaneousMenu_SwitchBoot3dsxTargetTitle },
|
{ "Switch the hb. title to the current app.", METHOD, .method = &MiscellaneousMenu_SwitchBoot3dsxTargetTitle },
|
||||||
{ "Change the menu combo", METHOD, .method = &MiscellaneousMenu_ChangeMenuCombo },
|
{ "Change the menu combo", METHOD, .method = &MiscellaneousMenu_ChangeMenuCombo },
|
||||||
{ "Start InputRedirection", METHOD, .method = &MiscellaneousMenu_InputRedirection },
|
{ "Start InputRedirection", METHOD, .method = &MiscellaneousMenu_InputRedirection },
|
||||||
{ "Sync time and date via NTP", METHOD, .method = &MiscellaneousMenu_SyncTimeDate },
|
{ "Sync time and date via NTP", METHOD, .method = &MiscellaneousMenu_SyncTimeDate },
|
||||||
{ "Save settings", METHOD, .method = &MiscellaneousMenu_SaveSettings },
|
{ "Save settings", METHOD, .method = &MiscellaneousMenu_SaveSettings },
|
||||||
|
{},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void MiscellaneousMenu_SwitchBoot3dsxTargetTitle(void)
|
void MiscellaneousMenu_SwitchBoot3dsxTargetTitle(void)
|
||||||
{
|
{
|
||||||
Result res;
|
Result res;
|
||||||
u64 titleId = 0;
|
|
||||||
char failureReason[64];
|
char failureReason[64];
|
||||||
|
|
||||||
if(HBLDR_3DSX_TID == HBLDR_DEFAULT_3DSX_TID)
|
if(Luma_SharedConfig->hbldr_3dsx_tid == HBLDR_DEFAULT_3DSX_TID)
|
||||||
{
|
{
|
||||||
|
FS_ProgramInfo progInfo;
|
||||||
u32 pid;
|
u32 pid;
|
||||||
res = PMDBG_GetCurrentAppTitleIdAndPid(&titleId, &pid);
|
u32 launchFlags;
|
||||||
|
res = PMDBG_GetCurrentAppInfo(&progInfo, &pid, &launchFlags);
|
||||||
if(R_SUCCEEDED(res))
|
if(R_SUCCEEDED(res))
|
||||||
{
|
{
|
||||||
HBLDR_3DSX_TID = titleId;
|
Luma_SharedConfig->hbldr_3dsx_tid = progInfo.programId;
|
||||||
miscellaneousMenu.items[0].title = "Switch the hb. title to hblauncher_loader";
|
miscellaneousMenu.items[0].title = "Switch the hb. title to hblauncher_loader";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -73,7 +74,7 @@ void MiscellaneousMenu_SwitchBoot3dsxTargetTitle(void)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
res = 0;
|
res = 0;
|
||||||
HBLDR_3DSX_TID = HBLDR_DEFAULT_3DSX_TID;
|
Luma_SharedConfig->hbldr_3dsx_tid = HBLDR_DEFAULT_3DSX_TID;
|
||||||
miscellaneousMenu.items[0].title = "Switch the hb. title to the current app.";
|
miscellaneousMenu.items[0].title = "Switch the hb. title to the current app.";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,13 +95,25 @@ void MiscellaneousMenu_SwitchBoot3dsxTargetTitle(void)
|
|||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
while(!(waitInput() & BUTTON_B) && !terminationRequest);
|
while(!(waitInput() & KEY_B) && !menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MiscellaneousMenu_ConvertComboToString(char *out, u32 combo)
|
static void MiscellaneousMenu_ConvertComboToString(char *out, u32 combo)
|
||||||
{
|
{
|
||||||
static const char *keys[] = { "A", "B", "Select", "Start", "Right", "Left", "Up", "Down", "R", "L", "X", "Y" };
|
static const char *keys[] = {
|
||||||
for(s32 i = 11; i >= 0; i--)
|
"A", "B", "Select", "Start", "Right", "Left", "Up", "Down", "R", "L", "X", "Y",
|
||||||
|
"?", "?",
|
||||||
|
"ZL", "ZR",
|
||||||
|
"?", "?", "?", "?",
|
||||||
|
"Touch",
|
||||||
|
"?", "?", "?",
|
||||||
|
"CStick Right", "CStick Left", "CStick Up", "CStick Down",
|
||||||
|
"CPad Right", "CPad Left", "CPad Up", "CPad Down",
|
||||||
|
};
|
||||||
|
|
||||||
|
char *outOrig = out;
|
||||||
|
out[0] = 0;
|
||||||
|
for(s32 i = 31; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if(combo & (1 << i))
|
if(combo & (1 << i))
|
||||||
{
|
{
|
||||||
@@ -110,12 +123,13 @@ static void MiscellaneousMenu_ConvertComboToString(char *out, u32 combo)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out[-1] = 0;
|
if (out != outOrig)
|
||||||
|
out[-1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MiscellaneousMenu_ChangeMenuCombo(void)
|
void MiscellaneousMenu_ChangeMenuCombo(void)
|
||||||
{
|
{
|
||||||
char comboStrOrig[64], comboStr[64];
|
char comboStrOrig[128], comboStr[128];
|
||||||
u32 posY;
|
u32 posY;
|
||||||
|
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
@@ -131,9 +145,6 @@ void MiscellaneousMenu_ChangeMenuCombo(void)
|
|||||||
posY = Draw_DrawFormattedString(10, 30, COLOR_WHITE, "The current menu combo is: %s", comboStrOrig);
|
posY = Draw_DrawFormattedString(10, 30, COLOR_WHITE, "The current menu combo is: %s", comboStrOrig);
|
||||||
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "Please enter the new combo:");
|
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "Please enter the new combo:");
|
||||||
|
|
||||||
Draw_FlushFramebuffer();
|
|
||||||
Draw_Unlock();
|
|
||||||
|
|
||||||
menuCombo = waitCombo();
|
menuCombo = waitCombo();
|
||||||
MiscellaneousMenu_ConvertComboToString(comboStr, menuCombo);
|
MiscellaneousMenu_ConvertComboToString(comboStr, menuCombo);
|
||||||
|
|
||||||
@@ -150,7 +161,7 @@ void MiscellaneousMenu_ChangeMenuCombo(void)
|
|||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
while(!(waitInput() & BUTTON_B) && !terminationRequest);
|
while(!(waitInput() & KEY_B) && !menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MiscellaneousMenu_SaveSettings(void)
|
void MiscellaneousMenu_SaveSettings(void)
|
||||||
@@ -191,7 +202,7 @@ void MiscellaneousMenu_SaveSettings(void)
|
|||||||
configData.config = config;
|
configData.config = config;
|
||||||
configData.multiConfig = multiConfig;
|
configData.multiConfig = multiConfig;
|
||||||
configData.bootConfig = bootConfig;
|
configData.bootConfig = bootConfig;
|
||||||
configData.hbldr3dsxTitleId = HBLDR_3DSX_TID;
|
configData.hbldr3dsxTitleId = Luma_SharedConfig->hbldr_3dsx_tid;
|
||||||
configData.rosalinaMenuCombo = menuCombo;
|
configData.rosalinaMenuCombo = menuCombo;
|
||||||
|
|
||||||
FS_ArchiveID archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
|
FS_ArchiveID archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
|
||||||
@@ -216,12 +227,11 @@ void MiscellaneousMenu_SaveSettings(void)
|
|||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
while(!(waitInput() & BUTTON_B) && !terminationRequest);
|
while(!(waitInput() & KEY_B) && !menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MiscellaneousMenu_InputRedirection(void)
|
void MiscellaneousMenu_InputRedirection(void)
|
||||||
{
|
{
|
||||||
static MyThread *inputRedirectionThread = NULL;
|
|
||||||
bool done = false;
|
bool done = false;
|
||||||
|
|
||||||
Result res;
|
Result res;
|
||||||
@@ -231,11 +241,7 @@ void MiscellaneousMenu_InputRedirection(void)
|
|||||||
|
|
||||||
if(wasEnabled)
|
if(wasEnabled)
|
||||||
{
|
{
|
||||||
res = InputRedirection_DoOrUndoPatches();
|
res = InputRedirection_Disable(5 * 1000 * 1000 * 1000LL);
|
||||||
inputRedirectionEnabled = false;
|
|
||||||
res = MyThread_Join(inputRedirectionThread, 5 * 1000 * 1000 * 1000LL);
|
|
||||||
svcCloseHandle(inputRedirectionThreadStartedEvent);
|
|
||||||
|
|
||||||
if(res != 0)
|
if(res != 0)
|
||||||
sprintf(buf, "Failed to stop InputRedirection (0x%08lx).", (u32)res);
|
sprintf(buf, "Failed to stop InputRedirection (0x%08lx).", (u32)res);
|
||||||
else
|
else
|
||||||
@@ -282,13 +288,18 @@ void MiscellaneousMenu_InputRedirection(void)
|
|||||||
res = svcCreateEvent(&inputRedirectionThreadStartedEvent, RESET_STICKY);
|
res = svcCreateEvent(&inputRedirectionThreadStartedEvent, RESET_STICKY);
|
||||||
if(R_SUCCEEDED(res))
|
if(R_SUCCEEDED(res))
|
||||||
{
|
{
|
||||||
inputRedirectionThread = inputRedirectionCreateThread();
|
inputRedirectionCreateThread();
|
||||||
res = svcWaitSynchronization(inputRedirectionThreadStartedEvent, 10 * 1000 * 1000 * 1000LL);
|
res = svcWaitSynchronization(inputRedirectionThreadStartedEvent, 10 * 1000 * 1000 * 1000LL);
|
||||||
if(res == 0)
|
if(res == 0)
|
||||||
res = (Result)inputRedirectionStartResult;
|
res = (Result)inputRedirectionStartResult;
|
||||||
|
|
||||||
if(res != 0)
|
if(res != 0)
|
||||||
|
{
|
||||||
|
svcCloseHandle(inputRedirectionThreadStartedEvent);
|
||||||
InputRedirection_DoOrUndoPatches();
|
InputRedirection_DoOrUndoPatches();
|
||||||
|
inputRedirectionEnabled = false;
|
||||||
|
}
|
||||||
|
inputRedirectionStartResult = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +319,21 @@ void MiscellaneousMenu_InputRedirection(void)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(res == 0)
|
if(res == 0)
|
||||||
Draw_DrawString(10, 30, COLOR_WHITE, "InputRedirection stopped successfully.");
|
{
|
||||||
|
u32 posY = 30;
|
||||||
|
posY = Draw_DrawString(10, posY, COLOR_WHITE, "InputRedirection stopped successfully.\n\n");
|
||||||
|
if (isN3DS)
|
||||||
|
{
|
||||||
|
posY = Draw_DrawString(
|
||||||
|
10,
|
||||||
|
posY,
|
||||||
|
COLOR_WHITE,
|
||||||
|
"This might cause a key press to be repeated in\n"
|
||||||
|
"Home Menu for no reason.\n\n"
|
||||||
|
"Just pressing ZL/ZR on the console is enough to fix\nthis.\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
Draw_DrawString(10, 30, COLOR_WHITE, buf);
|
Draw_DrawString(10, 30, COLOR_WHITE, buf);
|
||||||
}
|
}
|
||||||
@@ -316,7 +341,7 @@ void MiscellaneousMenu_InputRedirection(void)
|
|||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
while(!(waitInput() & BUTTON_B) && !terminationRequest);
|
while(!(waitInput() & KEY_B) && !menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MiscellaneousMenu_SyncTimeDate(void)
|
void MiscellaneousMenu_SyncTimeDate(void)
|
||||||
@@ -330,7 +355,6 @@ void MiscellaneousMenu_SyncTimeDate(void)
|
|||||||
bool isSocURegistered;
|
bool isSocURegistered;
|
||||||
|
|
||||||
time_t t;
|
time_t t;
|
||||||
struct tm localt = {0};
|
|
||||||
|
|
||||||
res = srvIsServiceRegistered(&isSocURegistered, "soc:U");
|
res = srvIsServiceRegistered(&isSocURegistered, "soc:U");
|
||||||
cantStart = R_FAILED(res) || !isSocURegistered;
|
cantStart = R_FAILED(res) || !isSocURegistered;
|
||||||
@@ -350,16 +374,16 @@ void MiscellaneousMenu_SyncTimeDate(void)
|
|||||||
|
|
||||||
input = waitInput();
|
input = waitInput();
|
||||||
|
|
||||||
if(input & BUTTON_LEFT) utcOffset = (24 + utcOffset - 1) % 24; // ensure utcOffset >= 0
|
if(input & KEY_LEFT) utcOffset = (24 + utcOffset - 1) % 24; // ensure utcOffset >= 0
|
||||||
if(input & BUTTON_RIGHT) utcOffset = (utcOffset + 1) % 24;
|
if(input & KEY_RIGHT) utcOffset = (utcOffset + 1) % 24;
|
||||||
if(input & BUTTON_UP) utcOffsetMinute = (utcOffsetMinute + 1) % 60;
|
if(input & KEY_UP) utcOffsetMinute = (utcOffsetMinute + 1) % 60;
|
||||||
if(input & BUTTON_DOWN) utcOffsetMinute = (60 + utcOffsetMinute - 1) % 60;
|
if(input & KEY_DOWN) utcOffsetMinute = (60 + utcOffsetMinute - 1) % 60;
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
while(!(input & (BUTTON_A | BUTTON_B)) && !terminationRequest);
|
while(!(input & (KEY_A | KEY_B)) && !menuShouldExit);
|
||||||
|
|
||||||
if (input & BUTTON_B)
|
if (input & KEY_B)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
utcOffset -= 12;
|
utcOffset -= 12;
|
||||||
@@ -374,8 +398,7 @@ void MiscellaneousMenu_SyncTimeDate(void)
|
|||||||
{
|
{
|
||||||
t += 3600 * utcOffset;
|
t += 3600 * utcOffset;
|
||||||
t += 60 * utcOffsetMinute;
|
t += 60 * utcOffsetMinute;
|
||||||
gmtime_r(&t, &localt);
|
res = ntpSetTimeDate(t);
|
||||||
res = ntpSetTimeDate(&localt);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,6 +422,6 @@ void MiscellaneousMenu_SyncTimeDate(void)
|
|||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
while(!(input & BUTTON_B) && !terminationRequest);
|
while(!(input & KEY_B) && !menuShouldExit);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ static char clkRateBuf[128 + 1];
|
|||||||
|
|
||||||
Menu N3DSMenu = {
|
Menu N3DSMenu = {
|
||||||
"New 3DS menu",
|
"New 3DS menu",
|
||||||
.nbItems = 2,
|
|
||||||
{
|
{
|
||||||
{ "Enable L2 cache", METHOD, .method = &N3DSMenu_EnableDisableL2Cache },
|
{ "Enable L2 cache", METHOD, .method = &N3DSMenu_EnableDisableL2Cache },
|
||||||
{ clkRateBuf, METHOD, .method = &N3DSMenu_ChangeClockRate }
|
{ clkRateBuf, METHOD, .method = &N3DSMenu_ChangeClockRate },
|
||||||
|
{},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include <3ds.h>
|
#include <3ds.h>
|
||||||
#include "menus/process_list.h"
|
#include "menus/process_list.h"
|
||||||
|
#include "process_patches.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "csvc.h"
|
#include "csvc.h"
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
@@ -197,7 +198,7 @@ end:
|
|||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
while(!(waitInput() & BUTTON_B) && !terminationRequest);
|
while(!(waitInput() & KEY_B) && !menuShouldExit);
|
||||||
|
|
||||||
#undef TRY
|
#undef TRY
|
||||||
}
|
}
|
||||||
@@ -493,21 +494,21 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
|
|||||||
|
|
||||||
u32 pressed = waitInputWithTimeout(1000);
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
|
||||||
if(pressed & BUTTON_A)
|
if(pressed & KEY_A)
|
||||||
editing = !editing;
|
editing = !editing;
|
||||||
else if(pressed & BUTTON_X)
|
else if(pressed & KEY_X)
|
||||||
{
|
{
|
||||||
if(checkMode(MENU_MODE_GOTO))
|
if(checkMode(MENU_MODE_GOTO))
|
||||||
finishJumping();
|
finishJumping();
|
||||||
else
|
else
|
||||||
gotoAddress = __builtin_bswap32(((u32)menus[MENU_MODE_NORMAL].buf) + menus[MENU_MODE_NORMAL].selected);
|
gotoAddress = __builtin_bswap32(((u32)menus[MENU_MODE_NORMAL].buf) + menus[MENU_MODE_NORMAL].selected);
|
||||||
}
|
}
|
||||||
else if(pressed & BUTTON_Y)
|
else if(pressed & KEY_Y)
|
||||||
{
|
{
|
||||||
if(checkMode(MENU_MODE_SEARCH))
|
if(checkMode(MENU_MODE_SEARCH))
|
||||||
finishSearching();
|
finishSearching();
|
||||||
}
|
}
|
||||||
else if(pressed & BUTTON_SELECT)
|
else if(pressed & KEY_SELECT)
|
||||||
{
|
{
|
||||||
clearMenu();
|
clearMenu();
|
||||||
ProcessListMenu_DumpMemory(info->name, menus[MENU_MODE_NORMAL].buf, menus[MENU_MODE_NORMAL].max);
|
ProcessListMenu_DumpMemory(info->name, menus[MENU_MODE_NORMAL].buf, menus[MENU_MODE_NORMAL].max);
|
||||||
@@ -517,35 +518,35 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
|
|||||||
if(editing)
|
if(editing)
|
||||||
{
|
{
|
||||||
// Edit the highlighted byte
|
// Edit the highlighted byte
|
||||||
if(pressed & BUTTON_LEFT)
|
if(pressed & KEY_LEFT)
|
||||||
selectedByteAdd0x10();
|
selectedByteAdd0x10();
|
||||||
else if(pressed & BUTTON_RIGHT)
|
else if(pressed & KEY_RIGHT)
|
||||||
selectedByteSub0x10();
|
selectedByteSub0x10();
|
||||||
else if(pressed & BUTTON_UP)
|
else if(pressed & KEY_UP)
|
||||||
selectedByteIncrement();
|
selectedByteIncrement();
|
||||||
else if(pressed & BUTTON_DOWN)
|
else if(pressed & KEY_DOWN)
|
||||||
selectedByteDecrement();
|
selectedByteDecrement();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Move the cursor
|
// Move the cursor
|
||||||
if(pressed & BUTTON_LEFT)
|
if(pressed & KEY_LEFT)
|
||||||
selectedMoveLeft();
|
selectedMoveLeft();
|
||||||
else if(pressed & BUTTON_RIGHT)
|
else if(pressed & KEY_RIGHT)
|
||||||
selectedMoveRight();
|
selectedMoveRight();
|
||||||
else if(pressed & BUTTON_UP)
|
else if(pressed & KEY_UP)
|
||||||
selectedMoveUp();
|
selectedMoveUp();
|
||||||
else if(pressed & BUTTON_DOWN)
|
else if(pressed & KEY_DOWN)
|
||||||
selectedMoveDown();
|
selectedMoveDown();
|
||||||
|
|
||||||
else if(pressed & BUTTON_L1)
|
else if(pressed & KEY_L)
|
||||||
{
|
{
|
||||||
if(menuMode == MENU_MODE_NORMAL)
|
if(menuMode == MENU_MODE_NORMAL)
|
||||||
viewHeap();
|
viewHeap();
|
||||||
else if(menuMode == MENU_MODE_SEARCH)
|
else if(menuMode == MENU_MODE_SEARCH)
|
||||||
searchPatternReduce();
|
searchPatternReduce();
|
||||||
}
|
}
|
||||||
else if(pressed & BUTTON_R1)
|
else if(pressed & KEY_R)
|
||||||
{
|
{
|
||||||
if(menuMode == MENU_MODE_NORMAL)
|
if(menuMode == MENU_MODE_NORMAL)
|
||||||
viewCode();
|
viewCode();
|
||||||
@@ -554,7 +555,7 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pressed & BUTTON_B) // go back to the list, or the simple viewer
|
if(pressed & KEY_B) // go back to the list, or the simple viewer
|
||||||
{
|
{
|
||||||
if(menuMode != MENU_MODE_NORMAL)
|
if(menuMode != MENU_MODE_NORMAL)
|
||||||
{
|
{
|
||||||
@@ -568,7 +569,7 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
|
|||||||
if(menus[menuMode].selected >= menus[menuMode].max)
|
if(menus[menuMode].selected >= menus[menuMode].max)
|
||||||
menus[menuMode].selected = menus[menuMode].max - 1;
|
menus[menuMode].selected = menus[menuMode].max - 1;
|
||||||
}
|
}
|
||||||
while(!terminationRequest);
|
while(!menuShouldExit);
|
||||||
|
|
||||||
clearMenu();
|
clearMenu();
|
||||||
}
|
}
|
||||||
@@ -685,7 +686,7 @@ void RosalinaMenu_ProcessList(void)
|
|||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
|
|
||||||
if(terminationRequest)
|
if(menuShouldExit)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
u32 pressed;
|
u32 pressed;
|
||||||
@@ -698,19 +699,19 @@ void RosalinaMenu_ProcessList(void)
|
|||||||
if(memcmp(infos, infosPrev, sizeof(infos)) != 0)
|
if(memcmp(infos, infosPrev, sizeof(infos)) != 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
while(pressed == 0 && !terminationRequest);
|
while(pressed == 0 && !menuShouldExit);
|
||||||
|
|
||||||
if(pressed & BUTTON_B)
|
if(pressed & KEY_B)
|
||||||
break;
|
break;
|
||||||
else if(pressed & BUTTON_A)
|
else if(pressed & KEY_A)
|
||||||
ProcessListMenu_HandleSelected(&infos[selected]);
|
ProcessListMenu_HandleSelected(&infos[selected]);
|
||||||
else if(pressed & BUTTON_DOWN)
|
else if(pressed & KEY_DOWN)
|
||||||
selected++;
|
selected++;
|
||||||
else if(pressed & BUTTON_UP)
|
else if(pressed & KEY_UP)
|
||||||
selected--;
|
selected--;
|
||||||
else if(pressed & BUTTON_LEFT)
|
else if(pressed & KEY_LEFT)
|
||||||
selected -= PROCESSES_PER_MENU_PAGE;
|
selected -= PROCESSES_PER_MENU_PAGE;
|
||||||
else if(pressed & BUTTON_RIGHT)
|
else if(pressed & KEY_RIGHT)
|
||||||
{
|
{
|
||||||
if(selected + PROCESSES_PER_MENU_PAGE < processAmount)
|
if(selected + PROCESSES_PER_MENU_PAGE < processAmount)
|
||||||
selected += PROCESSES_PER_MENU_PAGE;
|
selected += PROCESSES_PER_MENU_PAGE;
|
||||||
@@ -728,5 +729,5 @@ void RosalinaMenu_ProcessList(void)
|
|||||||
pagePrev = page;
|
pagePrev = page;
|
||||||
page = selected / PROCESSES_PER_MENU_PAGE;
|
page = selected / PROCESSES_PER_MENU_PAGE;
|
||||||
}
|
}
|
||||||
while(!terminationRequest);
|
while(!menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,33 +32,34 @@
|
|||||||
#include "redshift/redshift.h"
|
#include "redshift/redshift.h"
|
||||||
#include "redshift/colorramp.h"
|
#include "redshift/colorramp.h"
|
||||||
|
|
||||||
#define TEMP_DEFAULT NEUTRAL_TEMP
|
typedef union {
|
||||||
|
struct {
|
||||||
int screenFiltersCurrentTemperature = TEMP_DEFAULT;
|
u8 r;
|
||||||
|
u8 g;
|
||||||
void writeLut(u32* lut)
|
u8 b;
|
||||||
{
|
u8 z;
|
||||||
GPU_FB_TOP_COL_LUT_INDEX = 0;
|
};
|
||||||
GPU_FB_BOTTOM_COL_LUT_INDEX = 0;
|
u32 raw;
|
||||||
|
|
||||||
for (int i = 0; i <= 255; i++) {
|
|
||||||
GPU_FB_TOP_COL_LUT_ELEM = *lut;
|
|
||||||
GPU_FB_BOTTOM_COL_LUT_ELEM = *lut;
|
|
||||||
lut++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
u8 r;
|
|
||||||
u8 g;
|
|
||||||
u8 b;
|
|
||||||
u8 z;
|
|
||||||
} Pixel;
|
} Pixel;
|
||||||
|
|
||||||
static u16 g_c[0x600];
|
static u16 g_c[0x600];
|
||||||
static Pixel g_px[0x400];
|
static Pixel g_px[0x400];
|
||||||
|
|
||||||
void applyColorSettings(color_setting_t* cs)
|
int screenFiltersCurrentTemperature = -1;
|
||||||
|
|
||||||
|
static void ScreenFiltersMenu_WriteLut(const Pixel* lut)
|
||||||
|
{
|
||||||
|
GPU_FB_TOP_COL_LUT_INDEX = 0;
|
||||||
|
GPU_FB_BOTTOM_COL_LUT_INDEX = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i <= 255; i++) {
|
||||||
|
GPU_FB_TOP_COL_LUT_ELEM = lut->raw;
|
||||||
|
GPU_FB_BOTTOM_COL_LUT_ELEM = lut->raw;
|
||||||
|
lut++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ScreenFiltersMenu_ApplyColorSettings(color_setting_t* cs)
|
||||||
{
|
{
|
||||||
u8 i = 0;
|
u8 i = 0;
|
||||||
|
|
||||||
@@ -86,68 +87,69 @@ void applyColorSettings(color_setting_t* cs)
|
|||||||
g_px[i].b = *(g_c + i + 0x200) >> 8;
|
g_px[i].b = *(g_c + i + 0x200) >> 8;
|
||||||
} while(++i);
|
} while(++i);
|
||||||
|
|
||||||
writeLut((u32*)g_px);
|
ScreenFiltersMenu_WriteLut(g_px);
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu screenFiltersMenu = {
|
static void ScreenFiltersMenu_SetCct(int cct)
|
||||||
"Screen filters menu",
|
|
||||||
.nbItems = 6,
|
|
||||||
{
|
|
||||||
{ "Disable", METHOD, .method = &screenFiltersSetDisabled },
|
|
||||||
{ "Reduce blue light (level 1)", METHOD, .method = &screenFiltersReduceBlueLevel1 },
|
|
||||||
{ "Reduce blue light (level 2)", METHOD, .method = &screenFiltersReduceBlueLevel2 },
|
|
||||||
{ "Reduce blue light (level 3)", METHOD, .method = &screenFiltersReduceBlueLevel3 },
|
|
||||||
{ "Reduce blue light (level 4)", METHOD, .method = &screenFiltersReduceBlueLevel4 },
|
|
||||||
{ "Reduce blue light (level 5)", METHOD, .method = &screenFiltersReduceBlueLevel5 },
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void screenFiltersSetDisabled(void)
|
|
||||||
{
|
|
||||||
screenFiltersCurrentTemperature = TEMP_DEFAULT;
|
|
||||||
screenFiltersSetTemperature(screenFiltersCurrentTemperature);
|
|
||||||
}
|
|
||||||
|
|
||||||
void screenFiltersReduceBlueLevel1(void)
|
|
||||||
{
|
|
||||||
screenFiltersCurrentTemperature = 4300;
|
|
||||||
screenFiltersSetTemperature(screenFiltersCurrentTemperature);
|
|
||||||
}
|
|
||||||
|
|
||||||
void screenFiltersReduceBlueLevel2(void)
|
|
||||||
{
|
|
||||||
screenFiltersCurrentTemperature = 3200;
|
|
||||||
screenFiltersSetTemperature(screenFiltersCurrentTemperature);
|
|
||||||
}
|
|
||||||
|
|
||||||
void screenFiltersReduceBlueLevel3(void)
|
|
||||||
{
|
|
||||||
screenFiltersCurrentTemperature = 2100;
|
|
||||||
screenFiltersSetTemperature(screenFiltersCurrentTemperature);
|
|
||||||
}
|
|
||||||
|
|
||||||
void screenFiltersReduceBlueLevel4(void)
|
|
||||||
{
|
|
||||||
screenFiltersCurrentTemperature = 1550;
|
|
||||||
screenFiltersSetTemperature(screenFiltersCurrentTemperature);
|
|
||||||
}
|
|
||||||
|
|
||||||
void screenFiltersReduceBlueLevel5(void)
|
|
||||||
{
|
|
||||||
screenFiltersCurrentTemperature = 1000;
|
|
||||||
screenFiltersSetTemperature(screenFiltersCurrentTemperature);
|
|
||||||
}
|
|
||||||
|
|
||||||
void screenFiltersSetTemperature(int temperature)
|
|
||||||
{
|
{
|
||||||
color_setting_t cs;
|
color_setting_t cs;
|
||||||
memset(&cs, 0, sizeof(cs));
|
memset(&cs, 0, sizeof(cs));
|
||||||
|
|
||||||
cs.temperature = temperature;
|
cs.temperature = cct;
|
||||||
/*cs.gamma[0] = 1.0F;
|
/*cs.gamma[0] = 1.0F;
|
||||||
cs.gamma[1] = 1.0F;
|
cs.gamma[1] = 1.0F;
|
||||||
cs.gamma[2] = 1.0F;
|
cs.gamma[2] = 1.0F;
|
||||||
cs.brightness = 1.0F;*/
|
cs.brightness = 1.0F;*/
|
||||||
|
|
||||||
applyColorSettings(&cs);
|
ScreenFiltersMenu_ApplyColorSettings(&cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Menu screenFiltersMenu = {
|
||||||
|
"Screen filters menu",
|
||||||
|
{
|
||||||
|
{ "[6500K] Default", METHOD, .method = &ScreenFiltersMenu_SetDefault },
|
||||||
|
{ "[10000K] Aquarium", METHOD, .method = &ScreenFiltersMenu_SetAquarium },
|
||||||
|
{ "[7500K] Overcast Sky", METHOD, .method = &ScreenFiltersMenu_SetOvercastSky },
|
||||||
|
{ "[5500K] Daylight", METHOD, .method = &ScreenFiltersMenu_SetDaylight },
|
||||||
|
{ "[4200K] Fluorescent", METHOD, .method = &ScreenFiltersMenu_SetFluorescent },
|
||||||
|
{ "[3400K] Halogen", METHOD, .method = &ScreenFiltersMenu_SetHalogen },
|
||||||
|
{ "[2700K] Incandescent", METHOD, .method = &ScreenFiltersMenu_SetIncandescent },
|
||||||
|
{ "[2300K] Warm Incandescent", METHOD, .method = &ScreenFiltersMenu_SetWarmIncandescent },
|
||||||
|
{ "[1900K] Candle", METHOD, .method = &ScreenFiltersMenu_SetCandle },
|
||||||
|
{ "[2700K] Ember", METHOD, .method = &ScreenFiltersMenu_SetEmber },
|
||||||
|
{},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DEF_CCT_SETTER(temp, name)\
|
||||||
|
void ScreenFiltersMenu_Set##name(void)\
|
||||||
|
{\
|
||||||
|
screenFiltersCurrentTemperature = temp;\
|
||||||
|
ScreenFiltersMenu_SetCct(temp);\
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenFiltersMenu_RestoreCct(void)
|
||||||
|
{
|
||||||
|
// Not initialized: return
|
||||||
|
if (screenFiltersCurrentTemperature == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Wait for GSP to restore the CCT table
|
||||||
|
while (GPU_FB_TOP_COL_LUT_ELEM != g_px[0].raw)
|
||||||
|
svcSleepThread(10 * 1000 * 1000LL);
|
||||||
|
|
||||||
|
svcSleepThread(10 * 1000 * 1000LL);
|
||||||
|
ScreenFiltersMenu_WriteLut(g_px);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF_CCT_SETTER(6500, Default)
|
||||||
|
|
||||||
|
DEF_CCT_SETTER(10000, Aquarium)
|
||||||
|
DEF_CCT_SETTER(7500, OvercastSky)
|
||||||
|
DEF_CCT_SETTER(5500, Daylight)
|
||||||
|
DEF_CCT_SETTER(4200, Fluorescent)
|
||||||
|
DEF_CCT_SETTER(3400, Halogen)
|
||||||
|
DEF_CCT_SETTER(2700, Incandescent)
|
||||||
|
DEF_CCT_SETTER(2300, WarmIncandescent)
|
||||||
|
DEF_CCT_SETTER(1900, Candle)
|
||||||
|
DEF_CCT_SETTER(1200, Ember)
|
||||||
|
|||||||
@@ -34,15 +34,17 @@
|
|||||||
|
|
||||||
Menu sysconfigMenu = {
|
Menu sysconfigMenu = {
|
||||||
"System configuration menu",
|
"System configuration menu",
|
||||||
.nbItems = 4,
|
|
||||||
{
|
{
|
||||||
|
{ "Control Wireless connection", METHOD, .method = &SysConfigMenu_ControlWifi },
|
||||||
{ "Toggle LEDs", METHOD, .method = &SysConfigMenu_ToggleLEDs },
|
{ "Toggle LEDs", METHOD, .method = &SysConfigMenu_ToggleLEDs },
|
||||||
{ "Toggle Wireless", METHOD, .method = &SysConfigMenu_ToggleWireless },
|
{ "Toggle Wireless", METHOD, .method = &SysConfigMenu_ToggleWireless },
|
||||||
{ "Toggle Power Button", METHOD, .method=&SysConfigMenu_TogglePowerButton },
|
{ "Toggle Power Button", METHOD, .method=&SysConfigMenu_TogglePowerButton },
|
||||||
{ "Control Wireless connection", METHOD, .method = &SysConfigMenu_ControlWifi },
|
{},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool isConnectionForced = false;
|
||||||
|
|
||||||
void SysConfigMenu_ToggleLEDs(void)
|
void SysConfigMenu_ToggleLEDs(void)
|
||||||
{
|
{
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
@@ -64,7 +66,7 @@ void SysConfigMenu_ToggleLEDs(void)
|
|||||||
|
|
||||||
u32 pressed = waitInputWithTimeout(1000);
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
|
||||||
if(pressed & BUTTON_A)
|
if(pressed & KEY_A)
|
||||||
{
|
{
|
||||||
mcuHwcInit();
|
mcuHwcInit();
|
||||||
u8 result;
|
u8 result;
|
||||||
@@ -73,10 +75,10 @@ void SysConfigMenu_ToggleLEDs(void)
|
|||||||
MCUHWC_WriteRegister(0x28, &result, 1);
|
MCUHWC_WriteRegister(0x28, &result, 1);
|
||||||
mcuHwcExit();
|
mcuHwcExit();
|
||||||
}
|
}
|
||||||
else if(pressed & BUTTON_B)
|
else if(pressed & KEY_B)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while(!terminationRequest);
|
while(!menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysConfigMenu_ToggleWireless(void)
|
void SysConfigMenu_ToggleWireless(void)
|
||||||
@@ -137,23 +139,41 @@ void SysConfigMenu_ToggleWireless(void)
|
|||||||
|
|
||||||
u32 pressed = waitInputWithTimeout(1000);
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
|
||||||
if(pressed & BUTTON_A && nwmRunning)
|
if(pressed & KEY_A && nwmRunning)
|
||||||
{
|
{
|
||||||
nwmExtInit();
|
nwmExtInit();
|
||||||
NWMEXT_ControlWirelessEnabled(!wireless);
|
NWMEXT_ControlWirelessEnabled(!wireless);
|
||||||
nwmExtExit();
|
nwmExtExit();
|
||||||
}
|
}
|
||||||
else if(pressed & BUTTON_B)
|
else if(pressed & KEY_B)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while(!terminationRequest);
|
while(!menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SysConfigMenu_ForceWifiConnection(int slot)
|
void SysConfigMenu_UpdateStatus(bool control)
|
||||||
|
{
|
||||||
|
MenuItem *item = &sysconfigMenu.items[3];
|
||||||
|
|
||||||
|
if(control)
|
||||||
|
{
|
||||||
|
item->title = "Control Wireless connection";
|
||||||
|
item->method = &SysConfigMenu_ControlWifi;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item->title = "Disable forced wireless connection";
|
||||||
|
item->method = &SysConfigMenu_DisableForcedWifiConnection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool SysConfigMenu_ForceWifiConnection(int slot)
|
||||||
{
|
{
|
||||||
char ssid[0x20 + 1] = {0};
|
char ssid[0x20 + 1] = {0};
|
||||||
|
isConnectionForced = false;
|
||||||
|
|
||||||
acInit();
|
if(R_FAILED(acInit()))
|
||||||
|
return false;
|
||||||
|
|
||||||
acuConfig config = {0};
|
acuConfig config = {0};
|
||||||
ACU_CreateDefaultConfig(&config);
|
ACU_CreateDefaultConfig(&config);
|
||||||
@@ -167,15 +187,18 @@ static void SysConfigMenu_ForceWifiConnection(int slot)
|
|||||||
bool forcedConnection = false;
|
bool forcedConnection = false;
|
||||||
if(R_SUCCEEDED(ACU_ConnectAsync(&config, connectEvent)))
|
if(R_SUCCEEDED(ACU_ConnectAsync(&config, connectEvent)))
|
||||||
{
|
{
|
||||||
if(R_SUCCEEDED(svcWaitSynchronization(connectEvent, -1)))
|
if(R_SUCCEEDED(svcWaitSynchronization(connectEvent, -1)) && R_SUCCEEDED(ACU_GetSSID(ssid)))
|
||||||
{
|
|
||||||
ACU_GetSSID(ssid);
|
|
||||||
forcedConnection = true;
|
forcedConnection = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
svcCloseHandle(connectEvent);
|
svcCloseHandle(connectEvent);
|
||||||
|
|
||||||
acExit();
|
if(forcedConnection)
|
||||||
|
{
|
||||||
|
isConnectionForced = true;
|
||||||
|
SysConfigMenu_UpdateStatus(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
acExit();
|
||||||
|
|
||||||
char infoString[80] = {0};
|
char infoString[80] = {0};
|
||||||
u32 infoStringColor = forcedConnection ? COLOR_GREEN : COLOR_RED;
|
u32 infoStringColor = forcedConnection ? COLOR_GREEN : COLOR_RED;
|
||||||
@@ -201,10 +224,12 @@ static void SysConfigMenu_ForceWifiConnection(int slot)
|
|||||||
|
|
||||||
u32 pressed = waitInputWithTimeout(1000);
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
|
||||||
if(pressed & BUTTON_B)
|
if(pressed & KEY_B)
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
while(!terminationRequest);
|
while(!menuShouldExit);
|
||||||
|
|
||||||
|
return forcedConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysConfigMenu_TogglePowerButton(void)
|
void SysConfigMenu_TogglePowerButton(void)
|
||||||
@@ -234,7 +259,7 @@ void SysConfigMenu_TogglePowerButton(void)
|
|||||||
|
|
||||||
u32 pressed = waitInputWithTimeout(1000);
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
|
||||||
if(pressed & BUTTON_A)
|
if(pressed & KEY_A)
|
||||||
{
|
{
|
||||||
mcuHwcInit();
|
mcuHwcInit();
|
||||||
MCUHWC_ReadRegister(0x18, (u8*)&mcuIRQMask, 4);
|
MCUHWC_ReadRegister(0x18, (u8*)&mcuIRQMask, 4);
|
||||||
@@ -242,10 +267,10 @@ void SysConfigMenu_TogglePowerButton(void)
|
|||||||
MCUHWC_WriteRegister(0x18, (u8*)&mcuIRQMask, 4);
|
MCUHWC_WriteRegister(0x18, (u8*)&mcuIRQMask, 4);
|
||||||
mcuHwcExit();
|
mcuHwcExit();
|
||||||
}
|
}
|
||||||
else if(pressed & BUTTON_B)
|
else if(pressed & KEY_B)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while(!terminationRequest);
|
while(!menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysConfigMenu_ControlWifi(void)
|
void SysConfigMenu_ControlWifi(void)
|
||||||
@@ -271,16 +296,20 @@ void SysConfigMenu_ControlWifi(void)
|
|||||||
|
|
||||||
u32 pressed = waitInputWithTimeout(1000);
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
|
||||||
if(pressed & BUTTON_A)
|
if(pressed & KEY_A)
|
||||||
{
|
{
|
||||||
SysConfigMenu_ForceWifiConnection(slot);
|
if(SysConfigMenu_ForceWifiConnection(slot))
|
||||||
|
{
|
||||||
|
// Connection successfully forced, return from this menu to prevent ac handle refcount leakage.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_ClearFramebuffer();
|
Draw_ClearFramebuffer();
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
else if(pressed & BUTTON_LEFT)
|
else if(pressed & KEY_LEFT)
|
||||||
{
|
{
|
||||||
slotString[slot * 4] = ' ';
|
slotString[slot * 4] = ' ';
|
||||||
slotString[(slot * 4) + 2] = ' ';
|
slotString[(slot * 4) + 2] = ' ';
|
||||||
@@ -290,7 +319,7 @@ void SysConfigMenu_ControlWifi(void)
|
|||||||
slotString[slot * 4] = '>';
|
slotString[slot * 4] = '>';
|
||||||
slotString[(slot * 4) + 2] = '<';
|
slotString[(slot * 4) + 2] = '<';
|
||||||
}
|
}
|
||||||
else if(pressed & BUTTON_RIGHT)
|
else if(pressed & KEY_RIGHT)
|
||||||
{
|
{
|
||||||
slotString[slot * 4] = ' ';
|
slotString[slot * 4] = ' ';
|
||||||
slotString[(slot * 4) + 2] = ' ';
|
slotString[(slot * 4) + 2] = ' ';
|
||||||
@@ -300,8 +329,31 @@ void SysConfigMenu_ControlWifi(void)
|
|||||||
slotString[slot * 4] = '>';
|
slotString[slot * 4] = '>';
|
||||||
slotString[(slot * 4) + 2] = '<';
|
slotString[(slot * 4) + 2] = '<';
|
||||||
}
|
}
|
||||||
else if(pressed & BUTTON_B)
|
else if(pressed & KEY_B)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while(!terminationRequest);
|
while(!menuShouldExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysConfigMenu_DisableForcedWifiConnection(void)
|
||||||
|
{
|
||||||
|
Draw_Lock();
|
||||||
|
Draw_ClearFramebuffer();
|
||||||
|
Draw_FlushFramebuffer();
|
||||||
|
Draw_Unlock();
|
||||||
|
|
||||||
|
acExit();
|
||||||
|
SysConfigMenu_UpdateStatus(true);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Draw_Lock();
|
||||||
|
Draw_DrawString(10, 10, COLOR_TITLE, "System configuration menu");
|
||||||
|
Draw_DrawString(10, 30, COLOR_WHITE, "Forced connection successfully disabled.\nNote: auto-connection may remain broken.");
|
||||||
|
|
||||||
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
if(pressed & KEY_B)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while(!menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of Luma3DS.
|
* This file is part of Luma3DS.
|
||||||
* Copyright (C) 2016-2020 Aurora Wright, TuxSH
|
* Copyright (C) 2016-2020 Aurora Wright, TuxSH
|
||||||
@@ -7,10 +8,18 @@
|
|||||||
|
|
||||||
#include "minisoc.h"
|
#include "minisoc.h"
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <3ds/ipc.h>
|
#include <3ds.h>
|
||||||
#include <3ds/os.h>
|
#include <string.h>
|
||||||
#include <3ds/synchronization.h>
|
#include "utils.h"
|
||||||
#include "memory.h"
|
|
||||||
|
s32 miniSocRefCount = 0;
|
||||||
|
static u32 socContextAddr = 0x08000000;
|
||||||
|
static u32 socContextSize = 0x60000;
|
||||||
|
static Handle miniSocHandle;
|
||||||
|
static Handle miniSocMemHandle;
|
||||||
|
static bool exclusiveStateEntered = false;
|
||||||
|
|
||||||
|
bool miniSocEnabled = false;
|
||||||
|
|
||||||
s32 _net_convert_error(s32 sock_retval);
|
s32 _net_convert_error(s32 sock_retval);
|
||||||
|
|
||||||
@@ -25,7 +34,7 @@ static Result SOCU_Initialize(Handle memhandle, u32 memsize)
|
|||||||
cmdbuf[4] = IPC_Desc_SharedHandles(1);
|
cmdbuf[4] = IPC_Desc_SharedHandles(1);
|
||||||
cmdbuf[5] = memhandle;
|
cmdbuf[5] = memhandle;
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
if(ret != 0)
|
if(ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@@ -39,22 +48,49 @@ static Result SOCU_Shutdown(void)
|
|||||||
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x19,0,0); // 0x190000
|
cmdbuf[0] = IPC_MakeHeader(0x19,0,0); // 0x190000
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
if(ret != 0)
|
if(ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return cmdbuf[1];
|
return cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
static s32 miniSocRefCount = 0;
|
// unsafe but what can I do?
|
||||||
static u32 socContextAddr = 0x08000000;
|
void miniSocLockState(void)
|
||||||
static u32 socContextSize = 0x60000;
|
{
|
||||||
// SOCU_handle from ctrulib
|
Result res = 0;
|
||||||
// socMemhandle from ctrulib
|
__dmb();
|
||||||
|
if (!exclusiveStateEntered && isServiceUsable("ndm:u"))
|
||||||
|
{
|
||||||
|
ndmuInit();
|
||||||
|
res = NDMU_EnterExclusiveState(NDM_EXCLUSIVE_STATE_INFRASTRUCTURE);
|
||||||
|
if (R_SUCCEEDED(res))
|
||||||
|
res = NDMU_LockState(); // prevents ndm from switching to StreetPass when the lid is closed
|
||||||
|
exclusiveStateEntered = R_SUCCEEDED(res);
|
||||||
|
__dmb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool miniSocEnabled = false;
|
void miniSocUnlockState(bool force)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
|
||||||
Result miniSocInit()
|
__dmb();
|
||||||
|
if (exclusiveStateEntered)
|
||||||
|
{
|
||||||
|
if (!force)
|
||||||
|
{
|
||||||
|
res = NDMU_UnlockState();
|
||||||
|
if (R_SUCCEEDED(res))
|
||||||
|
res = NDMU_LeaveExclusiveState();
|
||||||
|
}
|
||||||
|
ndmuExit();
|
||||||
|
exclusiveStateEntered = R_FAILED(res);
|
||||||
|
__dmb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result miniSocInit(void)
|
||||||
{
|
{
|
||||||
if(AtomicPostIncrement(&miniSocRefCount))
|
if(AtomicPostIncrement(&miniSocRefCount))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -71,40 +107,43 @@ Result miniSocInit()
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = srvGetServiceHandle(&SOCU_handle, "soc:U");
|
ret = srvGetServiceHandle(&miniSocHandle, "soc:U");
|
||||||
if(ret != 0) goto cleanup;
|
if(ret != 0) goto cleanup;
|
||||||
|
|
||||||
ret = svcControlMemory(&tmp, socContextAddr, 0, socContextSize, MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE);
|
ret = svcControlMemoryEx(&tmp, socContextAddr, 0, socContextSize, MEMOP_ALLOC, MEMREGION_SYSTEM | MEMPERM_READ | MEMPERM_WRITE, true);
|
||||||
if(ret != 0) goto cleanup;
|
if(ret != 0) goto cleanup;
|
||||||
|
|
||||||
socContextAddr = tmp;
|
socContextAddr = tmp;
|
||||||
|
|
||||||
ret = svcCreateMemoryBlock(&socMemhandle, (u32)socContextAddr, socContextSize, 0, 3);
|
ret = svcCreateMemoryBlock(&miniSocMemHandle, (u32)socContextAddr, socContextSize, 0, 3);
|
||||||
if(ret != 0) goto cleanup;
|
if(ret != 0) goto cleanup;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ret = SOCU_Initialize(socMemhandle, socContextSize);
|
ret = SOCU_Initialize(miniSocMemHandle, socContextSize);
|
||||||
if(ret != 0) goto cleanup;
|
if(ret != 0) goto cleanup;
|
||||||
|
|
||||||
svcKernelSetState(0x10000, 2);
|
miniSocLockState();
|
||||||
|
|
||||||
|
svcKernelSetState(0x10000, 0x10);
|
||||||
miniSocEnabled = true;
|
miniSocEnabled = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
AtomicDecrement(&miniSocRefCount);
|
AtomicDecrement(&miniSocRefCount);
|
||||||
|
|
||||||
if(socMemhandle != 0)
|
if(miniSocMemHandle != 0)
|
||||||
{
|
{
|
||||||
svcCloseHandle(socMemhandle);
|
svcCloseHandle(miniSocMemHandle);
|
||||||
socMemhandle = 0;
|
miniSocMemHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SOCU_handle != 0)
|
if(miniSocHandle != 0)
|
||||||
{
|
{
|
||||||
SOCU_Shutdown();
|
SOCU_Shutdown();
|
||||||
svcCloseHandle(SOCU_handle);
|
svcCloseHandle(miniSocHandle);
|
||||||
SOCU_handle = 0;
|
miniSocHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tmp != 0)
|
if(tmp != 0)
|
||||||
@@ -113,31 +152,38 @@ cleanup:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result miniSocExit(void)
|
Result miniSocExitDirect(void)
|
||||||
{
|
{
|
||||||
if(AtomicDecrement(&miniSocRefCount))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
|
||||||
svcCloseHandle(socMemhandle);
|
svcCloseHandle(miniSocMemHandle);
|
||||||
socMemhandle = 0;
|
miniSocMemHandle = 0;
|
||||||
|
|
||||||
ret = SOCU_Shutdown();
|
ret = SOCU_Shutdown();
|
||||||
|
|
||||||
svcCloseHandle(SOCU_handle);
|
svcCloseHandle(miniSocHandle);
|
||||||
SOCU_handle = 0;
|
miniSocHandle = 0;
|
||||||
|
|
||||||
svcControlMemory(&tmp, socContextAddr, socContextAddr, socContextSize, MEMOP_FREE, MEMPERM_DONTCARE);
|
svcControlMemory(&tmp, socContextAddr, socContextAddr, socContextSize, MEMOP_FREE, MEMPERM_DONTCARE);
|
||||||
if(ret == 0)
|
if(ret == 0)
|
||||||
{
|
{
|
||||||
svcKernelSetState(0x10000, 2);
|
miniSocUnlockState(false);
|
||||||
|
|
||||||
miniSocEnabled = false;
|
miniSocEnabled = false;
|
||||||
|
svcKernelSetState(0x10000, 0x10);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result miniSocExit(void)
|
||||||
|
{
|
||||||
|
if(!miniSocEnabled || AtomicDecrement(&miniSocRefCount))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return miniSocExitDirect();
|
||||||
|
}
|
||||||
|
|
||||||
int socSocket(int domain, int type, int protocol)
|
int socSocket(int domain, int type, int protocol)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@@ -163,11 +209,11 @@ int socSocket(int domain, int type, int protocol)
|
|||||||
cmdbuf[3] = protocol;
|
cmdbuf[3] = protocol;
|
||||||
cmdbuf[4] = IPC_Desc_CurProcessId();
|
cmdbuf[4] = IPC_Desc_CurProcessId();
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
if(ret != 0)
|
if(ret != 0)
|
||||||
{
|
{
|
||||||
//errno = SYNC_ERROR;
|
//errno = SYNC_ERROR;
|
||||||
return ret;
|
return R_FAILED(ret) ? ret : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = (int)cmdbuf[1];
|
ret = (int)cmdbuf[1];
|
||||||
@@ -213,7 +259,7 @@ int socBind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
|||||||
cmdbuf[5] = IPC_Desc_StaticBuffer(tmp_addrlen,0);
|
cmdbuf[5] = IPC_Desc_StaticBuffer(tmp_addrlen,0);
|
||||||
cmdbuf[6] = (u32)tmpaddr;
|
cmdbuf[6] = (u32)tmpaddr;
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
if(ret != 0) {
|
if(ret != 0) {
|
||||||
//errno = SYNC_ERROR;
|
//errno = SYNC_ERROR;
|
||||||
return ret;
|
return ret;
|
||||||
@@ -241,7 +287,7 @@ int socListen(int sockfd, int max_connections)
|
|||||||
cmdbuf[2] = (u32)max_connections;
|
cmdbuf[2] = (u32)max_connections;
|
||||||
cmdbuf[3] = IPC_Desc_CurProcessId();
|
cmdbuf[3] = IPC_Desc_CurProcessId();
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
if(ret != 0)
|
if(ret != 0)
|
||||||
{
|
{
|
||||||
//errno = SYNC_ERROR;
|
//errno = SYNC_ERROR;
|
||||||
@@ -283,7 +329,7 @@ int socAccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
|
|||||||
staticbufs[0] = IPC_Desc_StaticBuffer(tmp_addrlen,0);
|
staticbufs[0] = IPC_Desc_StaticBuffer(tmp_addrlen,0);
|
||||||
staticbufs[1] = (u32)tmpaddr;
|
staticbufs[1] = (u32)tmpaddr;
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
|
|
||||||
staticbufs[0] = saved_threadstorage[0];
|
staticbufs[0] = saved_threadstorage[0];
|
||||||
staticbufs[1] = saved_threadstorage[1];
|
staticbufs[1] = saved_threadstorage[1];
|
||||||
@@ -340,7 +386,7 @@ int socConnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
|||||||
cmdbuf[5] = IPC_Desc_StaticBuffer(tmp_addrlen,0);
|
cmdbuf[5] = IPC_Desc_StaticBuffer(tmp_addrlen,0);
|
||||||
cmdbuf[6] = (u32)tmpaddr;
|
cmdbuf[6] = (u32)tmpaddr;
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
if(ret != 0) return -1;
|
if(ret != 0) return -1;
|
||||||
|
|
||||||
ret = (int)cmdbuf[1];
|
ret = (int)cmdbuf[1];
|
||||||
@@ -383,13 +429,13 @@ int socPoll(struct pollfd *fds, nfds_t nfds, int timeout)
|
|||||||
staticbufs[0] = IPC_Desc_StaticBuffer(size,0);
|
staticbufs[0] = IPC_Desc_StaticBuffer(size,0);
|
||||||
staticbufs[1] = (u32)fds;
|
staticbufs[1] = (u32)fds;
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
|
|
||||||
staticbufs[0] = saved_threadstorage[0];
|
staticbufs[0] = saved_threadstorage[0];
|
||||||
staticbufs[1] = saved_threadstorage[1];
|
staticbufs[1] = saved_threadstorage[1];
|
||||||
|
|
||||||
if(ret != 0) {
|
if(ret != 0) {
|
||||||
return ret;
|
return R_FAILED(ret) ? ret : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = (int)cmdbuf[1];
|
ret = (int)cmdbuf[1];
|
||||||
@@ -408,7 +454,7 @@ int socClose(int sockfd)
|
|||||||
cmdbuf[1] = (u32)sockfd;
|
cmdbuf[1] = (u32)sockfd;
|
||||||
cmdbuf[2] = IPC_Desc_CurProcessId();
|
cmdbuf[2] = IPC_Desc_CurProcessId();
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
if(ret != 0) {
|
if(ret != 0) {
|
||||||
//errno = SYNC_ERROR;
|
//errno = SYNC_ERROR;
|
||||||
return ret;
|
return ret;
|
||||||
@@ -440,7 +486,7 @@ int socSetsockopt(int sockfd, int level, int optname, const void *optval, sockle
|
|||||||
cmdbuf[7] = IPC_Desc_StaticBuffer(optlen,9);
|
cmdbuf[7] = IPC_Desc_StaticBuffer(optlen,9);
|
||||||
cmdbuf[8] = (u32)optval;
|
cmdbuf[8] = (u32)optval;
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
if(ret != 0) {
|
if(ret != 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -464,7 +510,7 @@ long socGethostid(void)
|
|||||||
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x16,0,0); // 0x160000
|
cmdbuf[0] = IPC_MakeHeader(0x16,0,0); // 0x160000
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
if(ret != 0) {
|
if(ret != 0) {
|
||||||
//errno = SYNC_ERROR;
|
//errno = SYNC_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -506,7 +552,7 @@ static ssize_t _socuipc_cmd7(int sockfd, void *buf, size_t len, int flags, struc
|
|||||||
staticbufs[0] = IPC_Desc_StaticBuffer(tmp_addrlen,0);
|
staticbufs[0] = IPC_Desc_StaticBuffer(tmp_addrlen,0);
|
||||||
staticbufs[1] = (u32)tmpaddr;
|
staticbufs[1] = (u32)tmpaddr;
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
|
|
||||||
staticbufs[0] = saved_threadstorage[0];
|
staticbufs[0] = saved_threadstorage[0];
|
||||||
staticbufs[1] = saved_threadstorage[1];
|
staticbufs[1] = saved_threadstorage[1];
|
||||||
@@ -565,7 +611,7 @@ static ssize_t _socuipc_cmd8(int sockfd, void *buf, size_t len, int flags, struc
|
|||||||
cmdbuf[0x108>>2] = (tmp_addrlen<<14) | 2;
|
cmdbuf[0x108>>2] = (tmp_addrlen<<14) | 2;
|
||||||
cmdbuf[0x10c>>2] = (u32)tmpaddr;
|
cmdbuf[0x10c>>2] = (u32)tmpaddr;
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
if(ret != 0) {
|
if(ret != 0) {
|
||||||
//errno = SYNC_ERROR;
|
//errno = SYNC_ERROR;
|
||||||
return ret;
|
return ret;
|
||||||
@@ -631,7 +677,7 @@ static ssize_t _socuipc_cmd9(int sockfd, const void *buf, size_t len, int flags,
|
|||||||
cmdbuf[9] = IPC_Desc_Buffer(len,IPC_BUFFER_R);
|
cmdbuf[9] = IPC_Desc_Buffer(len,IPC_BUFFER_R);
|
||||||
cmdbuf[10] = (u32)buf;
|
cmdbuf[10] = (u32)buf;
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
if(ret != 0) {
|
if(ret != 0) {
|
||||||
//errno = SYNC_ERROR;
|
//errno = SYNC_ERROR;
|
||||||
return ret;
|
return ret;
|
||||||
@@ -685,7 +731,7 @@ static ssize_t _socuipc_cmda(int sockfd, const void *buf, size_t len, int flags,
|
|||||||
cmdbuf[9] = IPC_Desc_StaticBuffer(tmp_addrlen,1);
|
cmdbuf[9] = IPC_Desc_StaticBuffer(tmp_addrlen,1);
|
||||||
cmdbuf[10] = (u32)tmpaddr;
|
cmdbuf[10] = (u32)tmpaddr;
|
||||||
|
|
||||||
ret = svcSendSyncRequest(SOCU_handle);
|
ret = svcSendSyncRequest(miniSocHandle);
|
||||||
if(ret != 0) {
|
if(ret != 0) {
|
||||||
//errno = SYNC_ERROR;
|
//errno = SYNC_ERROR;
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -40,18 +40,6 @@
|
|||||||
#define NTP_IP MAKE_IPV4(51, 137, 137, 111) // time.windows.com
|
#define NTP_IP MAKE_IPV4(51, 137, 137, 111) // time.windows.com
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct RtcTime {
|
|
||||||
// From 3dbrew
|
|
||||||
u8 seconds;
|
|
||||||
u8 minutes;
|
|
||||||
u8 hours;
|
|
||||||
u8 dayofweek;
|
|
||||||
u8 dayofmonth;
|
|
||||||
u8 month;
|
|
||||||
u8 year;
|
|
||||||
u8 leapcount;
|
|
||||||
} RtcTime;
|
|
||||||
|
|
||||||
// From https://github.com/lettier/ntpclient/blob/master/source/c/main.c
|
// From https://github.com/lettier/ntpclient/blob/master/source/c/main.c
|
||||||
|
|
||||||
typedef struct NtpPacket
|
typedef struct NtpPacket
|
||||||
@@ -84,17 +72,6 @@ typedef struct NtpPacket
|
|||||||
|
|
||||||
} NtpPacket; // Total: 384 bits or 48 bytes.
|
} NtpPacket; // Total: 384 bits or 48 bytes.
|
||||||
|
|
||||||
void rtcToBcd(u8 *out, const RtcTime *in)
|
|
||||||
{
|
|
||||||
memcpy(out, in, 8);
|
|
||||||
for (u32 i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
u8 units = out[i] % 10;
|
|
||||||
u8 tens = (out[i] - units) / 10;
|
|
||||||
out[i] = (tens << 4) | units;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ntpGetTimeStamp(time_t *outTimestamp)
|
Result ntpGetTimeStamp(time_t *outTimestamp)
|
||||||
{
|
{
|
||||||
Result res = 0;
|
Result res = 0;
|
||||||
@@ -104,6 +81,11 @@ Result ntpGetTimeStamp(time_t *outTimestamp)
|
|||||||
return res;
|
return res;
|
||||||
|
|
||||||
int sock = socSocket(AF_INET, SOCK_DGRAM, 0);
|
int sock = socSocket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (sock < -10000) {
|
||||||
|
// Socket services broken
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
struct sockaddr_in servAddr = {0}; // Server address data structure.
|
struct sockaddr_in servAddr = {0}; // Server address data structure.
|
||||||
NtpPacket packet = {0};
|
NtpPacket packet = {0};
|
||||||
|
|
||||||
@@ -157,45 +139,33 @@ cleanup:
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ntpSetTimeDate(const struct tm *localt)
|
Result ntpSetTimeDate(time_t timestamp)
|
||||||
{
|
{
|
||||||
Result res = mcuHwcInit();
|
Result res = ptmSysmInit();
|
||||||
if (R_FAILED(res)) return res;
|
if (R_FAILED(res)) return res;
|
||||||
|
|
||||||
|
|
||||||
res = cfguInit();
|
res = cfguInit();
|
||||||
if (R_FAILED(res)) goto cleanup;
|
if (R_FAILED(res))
|
||||||
|
{
|
||||||
|
ptmSysmExit();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
// First, set the config RTC offset to 0
|
// First, set the config RTC offset to 0
|
||||||
u8 rtcOff = 0;
|
u8 rtcOff[8] = {0};
|
||||||
u8 rtcOff2[8] = {0};
|
res = CFG_SetConfigInfoBlk4(8, 0x30001, rtcOff);
|
||||||
res = CFG_SetConfigInfoBlk4(1, 0x10000, &rtcOff);
|
|
||||||
if (R_FAILED(res)) goto cleanup;
|
|
||||||
res = CFG_SetConfigInfoBlk4(8, 0x30001, rtcOff2);
|
|
||||||
if (R_FAILED(res)) goto cleanup;
|
if (R_FAILED(res)) goto cleanup;
|
||||||
|
|
||||||
u8 yr = (u8)(localt->tm_year - 100);
|
|
||||||
// Update the RTC
|
// Update the RTC
|
||||||
u8 bcd[8];
|
// 946684800 is the timestamp of 01/01/2000 00:00 relative to the Unix Epoch
|
||||||
RtcTime lt = {
|
s64 msY2k = (timestamp - 946684800) * 1000;
|
||||||
.seconds = (u8)localt->tm_sec,
|
res = PTMSYSM_SetRtcTime(msY2k);
|
||||||
.minutes = (u8)localt->tm_min,
|
|
||||||
.hours = (u8)localt->tm_hour,
|
|
||||||
.dayofweek = (u8)localt->tm_wday,
|
|
||||||
.dayofmonth = (u8)localt->tm_mday,
|
|
||||||
.month = (u8)(localt->tm_mon + 1),
|
|
||||||
.year = yr,
|
|
||||||
.leapcount = 0,
|
|
||||||
};
|
|
||||||
rtcToBcd(bcd, <);
|
|
||||||
|
|
||||||
res = MCUHWC_WriteRegister(0x30, bcd, 7);
|
|
||||||
if (R_FAILED(res)) goto cleanup;
|
if (R_FAILED(res)) goto cleanup;
|
||||||
|
|
||||||
// Save the config changes
|
// Save the config changes
|
||||||
res = CFG_UpdateConfigSavegame();
|
res = CFG_UpdateConfigSavegame();
|
||||||
cleanup:
|
cleanup:
|
||||||
mcuHwcExit();
|
ptmSysmExit();
|
||||||
cfguExit();
|
cfguExit();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,16 @@
|
|||||||
#include <3ds/services/pmdbg.h>
|
#include <3ds/services/pmdbg.h>
|
||||||
#include <3ds/ipc.h>
|
#include <3ds/ipc.h>
|
||||||
|
|
||||||
Result PMDBG_GetCurrentAppTitleIdAndPid(u64 *outTitleId, u32 *outPid)
|
Result PMDBG_GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags)
|
||||||
{
|
{
|
||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x100, 0, 0);
|
cmdbuf[0] = IPC_MakeHeader(0x100, 0, 0);
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(*pmDbgGetSessionHandle()))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(*pmDbgGetSessionHandle()))) return ret;
|
||||||
|
|
||||||
memcpy(outTitleId, cmdbuf + 2, 8);
|
memcpy(outProgramInfo, cmdbuf + 2, sizeof(FS_ProgramInfo));
|
||||||
*outPid = cmdbuf[4];
|
*outPid = cmdbuf[6];
|
||||||
|
*outLaunchFlags = cmdbuf[7];
|
||||||
return cmdbuf[1];
|
return cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,3 +48,16 @@ Result PMDBG_LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInf
|
|||||||
*outDebug = cmdbuf[3];
|
*outDebug = cmdbuf[3];
|
||||||
return (Result)cmdbuf[1];
|
return (Result)cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result PMDBG_PrepareToChainloadHomebrew(u64 titleId)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x103, 2, 0);
|
||||||
|
memcpy(&cmdbuf[1], &titleId, 8);
|
||||||
|
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(*pmDbgGetSessionHandle()))) return ret;
|
||||||
|
|
||||||
|
return (Result)cmdbuf[1];
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,48 +25,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <3ds.h>
|
#include <3ds.h>
|
||||||
|
#include <string.h>
|
||||||
#include "csvc.h"
|
#include "csvc.h"
|
||||||
#include "menus/process_patches.h"
|
#include "process_patches.h"
|
||||||
#include "memory.h"
|
|
||||||
#include "draw.h"
|
|
||||||
#include "hbloader.h"
|
|
||||||
#include "fmt.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
static Result ProcessPatchesMenu_DoPatchUnpatchFS(u32 textTotalRoundedSize)
|
|
||||||
{
|
|
||||||
static bool patched = false;
|
|
||||||
static u16 *off;
|
|
||||||
static u16 origData[2];
|
|
||||||
static const u16 pattern[2] = {
|
|
||||||
0x7401, // strb r1, [r0, #16]
|
|
||||||
0x2000, // movs r0, #0
|
|
||||||
};
|
|
||||||
|
|
||||||
if(patched)
|
|
||||||
{
|
|
||||||
memcpy(off, &origData, sizeof(origData));
|
|
||||||
patched = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
off = (u16 *)memsearch((u8 *)0x00100000, &pattern, textTotalRoundedSize, sizeof(pattern));
|
|
||||||
if(off == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
for(; (*off & 0xFF00) != 0xB500; off++); // Find function start
|
|
||||||
|
|
||||||
memcpy(origData, off, 4);
|
|
||||||
off[0] = 0x2001; // mov r0, #1
|
|
||||||
off[1] = 0x4770; // bx lr
|
|
||||||
|
|
||||||
patched = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//processPatchesMenu.items[1].title = patched ? "Unpatch FS for the archive checks" : "Patch FS for the archive checks";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result OpenProcessByName(const char *name, Handle *h)
|
Result OpenProcessByName(const char *name, Handle *h)
|
||||||
{
|
{
|
||||||
u32 pidList[0x40];
|
u32 pidList[0x40];
|
||||||
@@ -96,7 +59,7 @@ Result OpenProcessByName(const char *name, Handle *h)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 ProcessPatchesMenu_PatchUnpatchProcessByName(const char *name, Result (*func)(u32 size))
|
Result PatchProcessByName(const char *name, Result (*func)(u32 size))
|
||||||
{
|
{
|
||||||
Result res;
|
Result res;
|
||||||
Handle processHandle;
|
Handle processHandle;
|
||||||
@@ -113,8 +76,3 @@ static u32 ProcessPatchesMenu_PatchUnpatchProcessByName(const char *name, Result
|
|||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, textTotalRoundedSize);
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, textTotalRoundedSize);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessPatchesMenu_PatchUnpatchFSDirectly(void)
|
|
||||||
{
|
|
||||||
ProcessPatchesMenu_PatchUnpatchProcessByName("fs", &ProcessPatchesMenu_DoPatchUnpatchFS);
|
|
||||||
}
|
|
||||||
@@ -14,8 +14,8 @@
|
|||||||
#include "minisoc.h"
|
#include "minisoc.h"
|
||||||
#include "sock_util.h"
|
#include "sock_util.h"
|
||||||
|
|
||||||
extern Handle terminationRequestEvent;
|
extern Handle preTerminationEvent;
|
||||||
extern bool terminationRequest;
|
extern bool preTerminationRequested;
|
||||||
|
|
||||||
// soc's poll function is odd, and doesn't like -1 as fd.
|
// soc's poll function is odd, and doesn't like -1 as fd.
|
||||||
// so this compacts everything together
|
// so this compacts everything together
|
||||||
@@ -101,29 +101,36 @@ Result server_init(struct sock_server *serv)
|
|||||||
return svcCreateEvent(&serv->shall_terminate_event, RESET_STICKY);
|
return svcCreateEvent(&serv->shall_terminate_event, RESET_STICKY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void server_bind(struct sock_server *serv, u16 port)
|
Result server_bind(struct sock_server *serv, u16 port)
|
||||||
{
|
{
|
||||||
int server_sockfd;
|
int server_sockfd;
|
||||||
Handle handles[2] = { terminationRequestEvent, serv->shall_terminate_event };
|
Handle handles[2] = { preTerminationEvent, serv->shall_terminate_event };
|
||||||
s32 idx = -1;
|
s32 idx = -1;
|
||||||
server_sockfd = socSocket(AF_INET, SOCK_STREAM, 0);
|
server_sockfd = socSocket(AF_INET, SOCK_STREAM, 0);
|
||||||
int res;
|
|
||||||
|
|
||||||
while(server_sockfd == -1)
|
int res;
|
||||||
|
u32 tries = 15;
|
||||||
|
while(server_sockfd == -1 && --tries > 0)
|
||||||
{
|
{
|
||||||
if(svcWaitSynchronizationN(&idx, handles, 2, false, 100 * 1000 * 1000LL) == 0)
|
if(svcWaitSynchronizationN(&idx, handles, 2, false, 100 * 1000 * 1000LL) == 0)
|
||||||
return;
|
return -1;
|
||||||
|
|
||||||
server_sockfd = socSocket(AF_INET, SOCK_STREAM, 0);
|
server_sockfd = socSocket(AF_INET, SOCK_STREAM, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (server_sockfd < -10000 || tries == 0) {
|
||||||
|
// Socket services broken
|
||||||
|
serv->init_result = -1;
|
||||||
|
svcSignalEvent(serv->shall_terminate_event);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
struct sockaddr_in saddr;
|
struct sockaddr_in saddr;
|
||||||
saddr.sin_family = AF_INET;
|
saddr.sin_family = AF_INET;
|
||||||
saddr.sin_port = htons(port);
|
saddr.sin_port = htons(port);
|
||||||
saddr.sin_addr.s_addr = socGethostid();
|
saddr.sin_addr.s_addr = socGethostid();
|
||||||
|
|
||||||
res = socBind(server_sockfd, (struct sockaddr*)&saddr, sizeof(struct sockaddr_in));
|
res = socBind(server_sockfd, (struct sockaddr*)&saddr, sizeof(struct sockaddr_in));
|
||||||
|
|
||||||
if(res == 0)
|
if(res == 0)
|
||||||
{
|
{
|
||||||
res = socListen(server_sockfd, 2);
|
res = socListen(server_sockfd, 2);
|
||||||
@@ -143,11 +150,20 @@ void server_bind(struct sock_server *serv, u16 port)
|
|||||||
serv->ctx_ptrs[idx] = new_ctx;
|
serv->ctx_ptrs[idx] = new_ctx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (res != 0) {
|
||||||
|
// Socket services broken
|
||||||
|
serv->init_result = res;
|
||||||
|
svcSignalEvent(serv->shall_terminate_event);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool server_should_exit(struct sock_server *serv)
|
static bool server_should_exit(struct sock_server *serv)
|
||||||
{
|
{
|
||||||
return svcWaitSynchronization(serv->shall_terminate_event, 0) == 0 || svcWaitSynchronization(terminationRequestEvent, 0) == 0;
|
return svcWaitSynchronization(serv->shall_terminate_event, 0) == 0 || svcWaitSynchronization(preTerminationEvent, 0) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void server_run(struct sock_server *serv)
|
void server_run(struct sock_server *serv)
|
||||||
@@ -156,7 +172,7 @@ void server_run(struct sock_server *serv)
|
|||||||
|
|
||||||
serv->running = true;
|
serv->running = true;
|
||||||
svcSignalEvent(serv->started_event);
|
svcSignalEvent(serv->started_event);
|
||||||
while(serv->running && !terminationRequest)
|
while(serv->running && !preTerminationRequested)
|
||||||
{
|
{
|
||||||
if(server_should_exit(serv))
|
if(server_should_exit(serv))
|
||||||
goto abort_connections;
|
goto abort_connections;
|
||||||
@@ -171,7 +187,7 @@ void server_run(struct sock_server *serv)
|
|||||||
fds[i].revents = 0;
|
fds[i].revents = 0;
|
||||||
int pollres = socPoll(fds, serv->nfds, 50);
|
int pollres = socPoll(fds, serv->nfds, 50);
|
||||||
|
|
||||||
if(server_should_exit(serv))
|
if(server_should_exit(serv) || pollres < -10000)
|
||||||
goto abort_connections;
|
goto abort_connections;
|
||||||
|
|
||||||
for(nfds_t i = 0; pollres > 0 && i < serv->nfds; i++)
|
for(nfds_t i = 0; pollres > 0 && i < serv->nfds; i++)
|
||||||
@@ -256,6 +272,7 @@ abort_connections:
|
|||||||
server_kill_connections(serv);
|
server_kill_connections(serv);
|
||||||
serv->running = false;
|
serv->running = false;
|
||||||
svcClearEvent(serv->started_event);
|
svcClearEvent(serv->started_event);
|
||||||
|
svcSignalEvent(serv->shall_terminate_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void server_set_should_close_all(struct sock_server *serv)
|
void server_set_should_close_all(struct sock_server *serv)
|
||||||
@@ -284,7 +301,8 @@ void server_kill_connections(struct sock_server *serv)
|
|||||||
socClose(fds[i].fd);
|
socClose(fds[i].fd);
|
||||||
fds[i].fd = -1;
|
fds[i].fd = -1;
|
||||||
|
|
||||||
serv->ctx_ptrs[i]->should_close = true;
|
if(serv->ctx_ptrs[i] != NULL)
|
||||||
|
serv->ctx_ptrs[i]->should_close = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
110
sysmodules/rosalina/source/utils.c
Normal file
110
sysmodules/rosalina/source/utils.c
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Luma3DS
|
||||||
|
* Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 "utils.h"
|
||||||
|
#include "csvc.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void formatMemoryPermission(char *outbuf, MemPerm perm)
|
||||||
|
{
|
||||||
|
if (perm == MEMPERM_DONTCARE)
|
||||||
|
{
|
||||||
|
strcpy(outbuf, "???");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
outbuf[0] = perm & MEMPERM_READ ? 'r' : '-';
|
||||||
|
outbuf[1] = perm & MEMPERM_WRITE ? 'w' : '-';
|
||||||
|
outbuf[2] = perm & MEMPERM_EXECUTE ? 'x' : '-';
|
||||||
|
outbuf[3] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void formatUserMemoryState(char *outbuf, MemState state)
|
||||||
|
{
|
||||||
|
static const char *states[12] =
|
||||||
|
{
|
||||||
|
"Free",
|
||||||
|
"Reserved",
|
||||||
|
"IO",
|
||||||
|
"Static",
|
||||||
|
"Code",
|
||||||
|
"Private",
|
||||||
|
"Shared",
|
||||||
|
"Continuous",
|
||||||
|
"Aliased",
|
||||||
|
"Alias",
|
||||||
|
"AliasCode",
|
||||||
|
"Locked"
|
||||||
|
};
|
||||||
|
|
||||||
|
strcpy(outbuf, state > 11 ? "Unknown" : states[state]);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 formatMemoryMapOfProcess(char *outbuf, u32 bufLen, Handle handle)
|
||||||
|
{
|
||||||
|
u32 maxLineSize = 35 + (handle == CUR_PROCESS_HANDLE ? 15 : 0);
|
||||||
|
u32 address = 0;
|
||||||
|
u32 posInBuffer = 0;
|
||||||
|
u32 maxPosInBuffer = bufLen - maxLineSize; // 35 is the maximum length of a formatted region
|
||||||
|
MemInfo memi;
|
||||||
|
PageInfo pagei;
|
||||||
|
char pabuf[32];
|
||||||
|
char permbuf[8];
|
||||||
|
char statebuf[16];
|
||||||
|
|
||||||
|
s64 TTBCR;
|
||||||
|
svcGetSystemInfo(&TTBCR, 0x10002, 0);
|
||||||
|
|
||||||
|
while (address < (1u << (32 - (u32)TTBCR)) // Limit to check for regions
|
||||||
|
&& posInBuffer < maxPosInBuffer
|
||||||
|
&& R_SUCCEEDED(svcQueryProcessMemory(&memi, &pagei, handle, address)))
|
||||||
|
{
|
||||||
|
// Update the address for next region
|
||||||
|
address = memi.base_addr + memi.size;
|
||||||
|
|
||||||
|
// If region isn't FREE then add it to the list
|
||||||
|
if (memi.state != MEMSTATE_FREE)
|
||||||
|
{
|
||||||
|
if (handle == CUR_PROCESS_HANDLE)
|
||||||
|
{
|
||||||
|
u32 pa = svcConvertVAToPA((void *)memi.base_addr, false);
|
||||||
|
sprintf(pabuf, " (PA %08lx)", pa);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pabuf[0] = '\0';
|
||||||
|
|
||||||
|
formatMemoryPermission(permbuf, memi.perm);
|
||||||
|
formatUserMemoryState(statebuf, memi.state);
|
||||||
|
|
||||||
|
posInBuffer += sprintf(outbuf + posInBuffer, "%08lx - %08lx%s %s %s\n",
|
||||||
|
memi.base_addr, address, pabuf, permbuf, statebuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svcCloseHandle(handle);
|
||||||
|
return posInBuffer;
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ Open source replacement of the Arm11 SM system module.
|
|||||||
This is licensed under the MIT license.
|
This is licensed under the MIT license.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/AuroraWright/Luma3DS/), build this project and copy sm.cxi to /luma/sysmodules/.
|
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/LumaTeam/Luma3DS/), build this project and copy sm.cxi to /luma/sysmodules/.
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
Everyone that helped me fix some of stupid bugs I had been making: @fincs, @Hikari-chin, etc.
|
Everyone that helped me fix some of stupid bugs I had been making: @fincs, @Hikari-chin, etc.
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for
|
|||||||
#include <3ds.h>
|
#include <3ds.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define IS_PRE_7X (osGetFirmVersion() < SYSTEM_VERSION(2, 39, 4))
|
#define KERNEL_VERSION_MINOR (GET_VERSION_MINOR(osGetKernelVersion()))
|
||||||
#define IS_PRE_93 (osGetFirmVersion() < SYSTEM_VERSION(2, 48, 3))
|
|
||||||
|
#define IS_PRE_7X (KERNEL_VERSION_MINOR < 39)
|
||||||
|
#define IS_PRE_93 (KERNEL_VERSION_MINOR < 48)
|
||||||
|
|
||||||
extern u32 nbSection0Modules;
|
extern u32 nbSection0Modules;
|
||||||
extern Handle resumeGetServiceHandleOrPortRegisteredSemaphore;
|
extern Handle resumeGetServiceHandleOrPortRegisteredSemaphore;
|
||||||
|
|||||||
@@ -8,6 +8,18 @@ This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for
|
|||||||
#include "notifications.h"
|
#include "notifications.h"
|
||||||
#include "processes.h"
|
#include "processes.h"
|
||||||
|
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
|
static bool isNotificationInhibited(const ProcessData *processData, u32 notificationId)
|
||||||
|
{
|
||||||
|
(void)processData;
|
||||||
|
switch(notificationId)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool doPublishNotification(ProcessData *processData, u32 notificationId, u32 flags)
|
static bool doPublishNotification(ProcessData *processData, u32 notificationId, u32 flags)
|
||||||
{
|
{
|
||||||
if((flags & 1) && processData->nbPendingNotifications != 0) // only send if not already pending
|
if((flags & 1) && processData->nbPendingNotifications != 0) // only send if not already pending
|
||||||
@@ -118,7 +130,7 @@ Result PublishToSubscriber(u32 notificationId, u32 flags)
|
|||||||
{
|
{
|
||||||
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
|
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
|
||||||
{
|
{
|
||||||
if(!node->notificationEnabled)
|
if(!node->notificationEnabled || isNotificationInhibited(node, notificationId))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
u16 i;
|
u16 i;
|
||||||
@@ -138,7 +150,7 @@ Result PublishAndGetSubscriber(u32 *pidCount, u32 *pidList, u32 notificationId,
|
|||||||
u32 nb = 0;
|
u32 nb = 0;
|
||||||
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
|
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
|
||||||
{
|
{
|
||||||
if(!node->notificationEnabled)
|
if(!node->notificationEnabled || isNotificationInhibited(node, notificationId))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
u16 i;
|
u16 i;
|
||||||
|
|||||||
@@ -17,3 +17,5 @@ Result PublishToSubscriber(u32 notificationId, u32 flags);
|
|||||||
Result PublishAndGetSubscriber(u32 *pidCount, u32 *pidList, u32 notificationId, u32 flags);
|
Result PublishAndGetSubscriber(u32 *pidCount, u32 *pidList, u32 notificationId, u32 flags);
|
||||||
Result PublishToProcess(Handle process, u32 notificationId);
|
Result PublishToProcess(Handle process, u32 notificationId);
|
||||||
Result PublishToAll(u32 notificationId);
|
Result PublishToAll(u32 notificationId);
|
||||||
|
|
||||||
|
Result AddToNdmuWorkaroundCount(s32 count);
|
||||||
|
|||||||
Reference in New Issue
Block a user