From b6d6cc97509a3d967128e0ec5e2dbe65749637e4 Mon Sep 17 00:00:00 2001 From: Duckbill Date: Thu, 21 Dec 2017 20:14:04 +0300 Subject: [PATCH] Cheat implementations --- .gitignore | 6 +- sysmodules/rosalina/include/menus.h | 1 + sysmodules/rosalina/include/menus/cheats.h | 35 + sysmodules/rosalina/source/menu.c | 7 + sysmodules/rosalina/source/menus.c | 3 +- sysmodules/rosalina/source/menus/cheats.c | 920 +++++++++++++++++++++ 6 files changed, 970 insertions(+), 2 deletions(-) create mode 100644 sysmodules/rosalina/include/menus/cheats.h create mode 100644 sysmodules/rosalina/source/menus/cheats.c diff --git a/.gitignore b/.gitignore index be0840d..48ed2a0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,8 @@ exceptions/arm11/build *.d *.elf *.cxi -.DS_Store \ No newline at end of file +.DS_Store +.project +.cproject +.settings + diff --git a/sysmodules/rosalina/include/menus.h b/sysmodules/rosalina/include/menus.h index 354c93c..b1da981 100644 --- a/sysmodules/rosalina/include/menus.h +++ b/sysmodules/rosalina/include/menus.h @@ -37,3 +37,4 @@ void RosalinaMenu_ShowCredits(void); void RosalinaMenu_ProcessList(void); void RosalinaMenu_PowerOff(void); void RosalinaMenu_Reboot(void); +void RosalinaMenu_Cheats(void); diff --git a/sysmodules/rosalina/include/menus/cheats.h b/sysmodules/rosalina/include/menus/cheats.h new file mode 100644 index 0000000..d082339 --- /dev/null +++ b/sysmodules/rosalina/include/menus/cheats.h @@ -0,0 +1,35 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2017 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#pragma once + +#include <3ds/types.h> + +#define CHEATS_PER_MENU_PAGE 18 + +void RosalinaMenu_Cheats(void); +u32 Cheats_GetCurrentPID(u64* titleId); +void Cheats_applyKeyCheats(); diff --git a/sysmodules/rosalina/source/menu.c b/sysmodules/rosalina/source/menu.c index ab010a4..7d46c6a 100644 --- a/sysmodules/rosalina/source/menu.c +++ b/sysmodules/rosalina/source/menu.c @@ -33,6 +33,7 @@ #include "menus.h" #include "utils.h" #include "menus/n3ds.h" +#include "menus/cheats.h" #include "minisoc.h" u32 waitInputWithTimeout(u32 msec) @@ -157,6 +158,12 @@ void menuThreadMain(void) menuShow(&rosalinaMenu); menuLeave(); } + else + { + if (HID_PAD & 0xFFF) { + Cheats_applyKeyCheats(); + } + } svcSleepThread(50 * 1000 * 1000LL); } } diff --git a/sysmodules/rosalina/source/menus.c b/sysmodules/rosalina/source/menus.c index b45aeb0..af76b55 100644 --- a/sysmodules/rosalina/source/menus.c +++ b/sysmodules/rosalina/source/menus.c @@ -41,8 +41,9 @@ Menu rosalinaMenu = { "Rosalina menu", - .nbItems = 9, + .nbItems = 10, { + { "Cheats...", METHOD, .method = &RosalinaMenu_Cheats }, { "Process list", METHOD, .method = &RosalinaMenu_ProcessList }, { "Take screenshot (slow!)", METHOD, .method = &RosalinaMenu_TakeScreenshot }, { "New 3DS menu...", MENU, .menu = &N3DSMenu }, diff --git a/sysmodules/rosalina/source/menus/cheats.c b/sysmodules/rosalina/source/menus/cheats.c new file mode 100644 index 0000000..6d147bf --- /dev/null +++ b/sysmodules/rosalina/source/menus/cheats.c @@ -0,0 +1,920 @@ +#include <3ds.h> +#include "menus/cheats.h" +#include "memory.h" +#include "draw.h" +#include "menu.h" +#include "utils.h" +#include "fmt.h" +#include "ifile.h" + +#define MAKE_QWORD(hi,low) \ + ((u64) ((((u64)(hi)) << 32) | (low))) + +typedef struct CheatProcessInfo { + u32 pid; + u64 titleId; +} CheatProcessInfo; + +typedef struct CheatDescription { + u64 titleId; + u32 active; + u32 keyActivated; + u32 keyCombo; + char name[40]; + u32 codesCount; + u64 codes[64]; +} CheatDescription; + +static CheatProcessInfo cheatinfo[0x40] = { 0 }; + +s32 Cheats_FetchProcessInfo(void) { + u32 pidList[0x40]; + s32 processAmount; + + s64 sa, textTotalRoundedSize, rodataTotalRoundedSize, dataTotalRoundedSize; + + svcGetProcessList(&processAmount, pidList, 0x40); + + for (s32 i = 0; i < processAmount; i++) { + Handle processHandle; + Result res = svcOpenProcess(&processHandle, pidList[i]); + if (R_FAILED(res)) + continue; + + cheatinfo[i].pid = pidList[i]; + svcGetProcessInfo((s64 *) &cheatinfo[i].titleId, processHandle, + 0x10001); + svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); + svcGetProcessInfo(&rodataTotalRoundedSize, processHandle, 0x10003); + svcGetProcessInfo(&dataTotalRoundedSize, processHandle, 0x10004); + svcGetProcessInfo(&sa, processHandle, 0x10005); + + svcCloseHandle(processHandle); + } + + return processAmount; +} + +typedef struct CheatState { + u8 id; + u32 index; + u32 offset; + u32 data; + u8 typeELine; + u8 typeEIdx; + + s8 loopLine; + u32 loopCount; + + u32 ifStack; + u32 storedStack; + u8 ifCount; + u8 storedIfCount; + +} CheatState; + +CheatState cheat_state = { 0 }; +u8 cheatCount = 0; +u8 hasKeyActivated = 0; +u64 cheatTitleInfo = -1ULL; +CheatDescription cheatDescriptions[0x20] = { 0 }; + +char failureReason[64]; + +void Cheat_write8(u32 offset, u8 value) { + *((u8*) (cheat_state.offset + offset)) = value; +} +void Cheat_write16(u32 offset, u16 value) { + *((u16*) (cheat_state.offset + offset)) = value; +} +void Cheat_write32(u32 offset, u32 value) { + *((u32*) (cheat_state.offset + offset)) = value; +} + +u8 Cheat_read8(u32 offset) { + return *((u8*) (cheat_state.offset + offset)); +} +u16 Cheat_read16(u32 offset) { + return *((u16*) (cheat_state.offset + offset)); +} +u32 Cheat_read32(u32 offset) { + return *((u32*) (cheat_state.offset + offset)); +} + +u8 typeEMapping[] = { 4 << 3, 5 << 3, 6 << 3, 7 << 3, 0 << 3, 1 << 3, 2 << 3, 3 + << 3 }; +u8 Cheat_getNextTypeE() { + + if (cheat_state.typeEIdx == 7) { + cheat_state.typeEIdx = 0; + cheat_state.typeELine++; + } else { + cheat_state.typeEIdx++; + } + return (u8) ((cheatDescriptions[cheat_state.id].codes[cheat_state.typeELine] + >> (typeEMapping[cheat_state.typeEIdx])) & 0xFF); +} + +void Cheat_applyCheat(u8 cheatId) { + cheat_state.id = cheatId; + cheat_state.index = 0; + cheat_state.offset = 0; + cheat_state.data = 0; + cheat_state.index = 0; + cheat_state.loopCount = 0; + cheat_state.loopLine = -1; + cheat_state.ifStack = 0; + cheat_state.storedStack = 0; + cheat_state.ifCount = 0; + cheat_state.storedIfCount = 0; + + cheatDescriptions[cheat_state.id].keyCombo = 0; + + while (cheat_state.index < cheatDescriptions[cheat_state.id].codesCount) { + u32 skipExecution = cheat_state.ifStack & 0x00000001; + u32 arg0 = + (u32) ((cheatDescriptions[cheat_state.id].codes[cheat_state.index] + >> 32) & 0x00000000FFFFFFFFULL); + u32 arg1 = + (u32) ((cheatDescriptions[cheat_state.id].codes[cheat_state.index]) + & 0x00000000FFFFFFFFULL); + if (arg0 == 0 && arg1 == 0) { + goto end_main_loop; + } + u32 code = ((arg0 >> 28) & 0x0F); + u32 subcode = ((arg0 >> 24) & 0x0F); + + switch (code) { + case 0x0: + // 0 Type + // Format: 0XXXXXXX YYYYYYYY + // Description: 32bit write of YYYYYYYY to 0XXXXXXX. + if (!skipExecution) { + Cheat_write32((arg0 & 0x0FFFFFFF), arg1); + } + break; + case 0x1: + // 1 Type + // Format: 1XXXXXXX 0000YYYY + // Description: 16bit write of YYYY to 0XXXXXXX. + if (!skipExecution) { + Cheat_write16((arg0 & 0x0FFFFFFF), (u16) (arg1 & 0xFFFF)); + } + break; + case 0x2: + // 2 Type + // Format: 2XXXXXXX 000000YY + // Description: 8bit write of YY to 0XXXXXXX. + if (!skipExecution) { + Cheat_write8((arg0 & 0x0FFFFFFF), (u8) (arg1 & 0xFF)); + } + break; + case 0x3: + // 3 Type + // Format: 3XXXXXXXX YYYYYYYY + // Description: 32bit if less than. + // Simple: If the value at address 0XXXXXXX is less than the value YYYYYYYY. + // Example: 323D6B28 10000000 + { + u32 newSkip; + if (Cheat_read32(arg0 & 0x0FFFFFFF) < arg1) { + newSkip = 0; + } else { + newSkip = 1; + } + + cheat_state.ifStack <<= 1; + cheat_state.ifStack |= ((newSkip | skipExecution) & 0x1); + cheat_state.ifCount++; + } + break; + case 0x4: + // 4 Type + // Format: 4XXXXXXXX YYYYYYYY + // Description: 32bit if greater than. + // Simple: If the value at address 0XXXXXXX is greater than the value YYYYYYYY. + // Example: 423D6B28 10000000 + { + u32 newSkip; + if (Cheat_read32(arg0 & 0x0FFFFFFF) > arg1) { + newSkip = 0; + } else { + newSkip = 1; + } + + cheat_state.ifStack <<= 1; + cheat_state.ifStack |= ((newSkip | skipExecution) & 0x1); + cheat_state.ifCount++; + } + break; + case 0x5: + // 5 Type + // Format: 5XXXXXXXX YYYYYYYY + // Description: 32bit if equal to. + // Simple: If the value at address 0XXXXXXX is equal to the value YYYYYYYY. + // Example: 523D6B28 10000000 + { + u32 newSkip; + if (Cheat_read32(arg0 & 0x0FFFFFFF) == arg1) { + newSkip = 0; + } else { + newSkip = 1; + } + + cheat_state.ifStack <<= 1; + cheat_state.ifStack |= ((newSkip | skipExecution) & 0x1); + cheat_state.ifCount++; + } + break; + case 0x6: + // 6 Type + // Format: 3XXXXXXXX YYYYYYYY + // Description: 32bit if not equal to. + // Simple: If the value at address 0XXXXXXX is not equal to the value YYYYYYYY. + // Example: 623D6B28 10000000 + { + u32 newSkip; + if (Cheat_read32(arg0 & 0x0FFFFFFF) != arg1) { + newSkip = 0; + } else { + newSkip = 1; + } + + cheat_state.ifStack <<= 1; + cheat_state.ifStack |= ((newSkip | skipExecution) & 0x1); + cheat_state.ifCount++; + } + break; + case 0x7: + // 7 Type + // Format: 7XXXXXXXX 0000YYYY + // Description: 16bit if less than. + // Simple: If the value at address 0XXXXXXX is less than the value YYYY. + // Example: 723D6B28 00005400 + { + u32 newSkip; + u16 mask = (u16) ((arg1 >> 16) & 0xFFFF); + if (mask == 0) { + mask = 0xFFFF; + } + if ((Cheat_read16(arg0 & 0x0FFFFFFF) & mask) < (arg1 & 0xFFFF)) { + newSkip = 0; + } else { + newSkip = 1; + } + + cheat_state.ifStack <<= 1; + cheat_state.ifStack |= ((newSkip | skipExecution) & 0x1); + cheat_state.ifCount++; + } + break; + case 0x8: + // 8 Type + // Format: 8XXXXXXXX 0000YYYY + // Description: 16bit if greater than. + // Simple: If the value at address 0XXXXXXX is greater than the value YYYY. + // Example: 823D6B28 00005400 + { + u32 newSkip; + u16 mask = (u16) ((arg1 >> 16) & 0xFFFF); + if (mask == 0) { + mask = 0xFFFF; + } + if ((Cheat_read16(arg0 & 0x0FFFFFFF) & mask) > (arg1 & 0xFFFF)) { + newSkip = 0; + } else { + newSkip = 1; + } + + cheat_state.ifStack <<= 1; + cheat_state.ifStack |= ((newSkip | skipExecution) & 0x1); + cheat_state.ifCount++; + } + break; + case 0x9: + // 9 Type + // Format: 9XXXXXXXX 0000YYYY + // Description: 16bit if equal to. + // Simple: If the value at address 0XXXXXXX is equal to the value YYYY. + // Example: 923D6B28 00005400 + { + u32 newSkip; + u16 mask = (u16) ((arg1 >> 16) & 0xFFFF); + if (mask == 0) { + mask = 0xFFFF; + } + if ((Cheat_read16(arg0 & 0x0FFFFFFF) & mask) == (arg1 & 0xFFFF)) { + newSkip = 0; + } else { + newSkip = 1; + } + + cheat_state.ifStack <<= 1; + cheat_state.ifStack |= ((newSkip | skipExecution) & 0x1); + cheat_state.ifCount++; + } + break; + case 0xA: + // A Type + // Format: AXXXXXXXX 0000YYYY + // Description: 16bit if not equal to. + // Simple: If the value at address 0XXXXXXX is not equal to the value YYYY. + // Example: A23D6B28 00005400 + { + u32 newSkip; + u16 mask = (u16) ((arg1 >> 16) & 0xFFFF); + if (mask == 0) { + mask = 0xFFFF; + } + if ((Cheat_read16(arg0 & 0x0FFFFFFF) & mask) != (arg1 & 0xFFFF)) { + newSkip = 0; + } else { + newSkip = 1; + } + + cheat_state.ifStack <<= 1; + cheat_state.ifStack |= ((newSkip | skipExecution) & 0x1); + cheat_state.ifCount++; + } + break; + + case 0xB: + // B Type + // Format: BXXXXXXX 00000000 + // Description: Loads offset register. + if (!skipExecution) { + cheat_state.offset = (arg0 & 0x0FFFFFFF); + } + break; + case 0xC: + // C Type + // Format: C0000000 ZZZZZZZZ + // Description: Repeat following lines at specified offset. + // Simple: used to write a value to an address, and then continues to write that value Z number of times to all addresses at an offset determined by the (D6, D7, D8, or DC) type following it. + // Note: used with the D6, D7, D8, and DC types. C types can not be nested. + // Example: + + // C0000000 00000005 + // 023D6B28 0009896C + // DC000000 00000010 + // D2000000 00000000 + + cheat_state.loopLine = cheat_state.index; + cheat_state.loopCount = arg1; + cheat_state.storedStack = cheat_state.ifStack; + cheat_state.storedIfCount = cheat_state.ifCount; + break; + case 0xD: + switch (subcode) { + case 0x00: + // D0 Type + // Format: D0000000 00000000 + // Description: ends most recent conditional. + // Simple: type 3 through A are all "conditionals," the conditional most recently executed before this line will be terminated by it. + // Example: + + // 94000130 FFFB0000 + // 74000100 FF00000C + // 023D6B28 0009896C + // D0000000 00000000 + + // The 7 type line would be terminated. + if (cheat_state.loopLine != -1) { + if (cheat_state.ifCount > 0 + && cheat_state.ifCount + > cheat_state.storedIfCount) { + cheat_state.ifStack >>= 1; + cheat_state.ifCount--; + } else { + + if (cheat_state.loopCount > 0) { + cheat_state.loopCount--; + if (cheat_state.loopCount == 0) { + cheat_state.loopLine = -1; + } else { + if (cheat_state.loopLine != -1) { + cheat_state.index = cheat_state.loopLine; + } + } + } + } + } else { + if (cheat_state.ifCount > 0) { + cheat_state.ifStack >>= 1; + cheat_state.ifCount--; + } + } + break; + case 0x01: + // D1 Type + // Format: D1000000 00000000 + // Description: ends repeat block. + // Simple: will end all conditionals within a C type code, along with the C type itself. + // Example: + + // 94000130 FFFB0000 + // C0000000 00000010 + // 8453DA0C 00000200 + // 023D6B28 0009896C + // D6000000 00000005 + // D1000000 00000000 + + // The C line, 8 line, 0 line, and D6 line would be terminated. + if (cheat_state.loopCount > 0) { + cheat_state.ifStack = cheat_state.storedStack; + cheat_state.ifCount = cheat_state.storedIfCount; + cheat_state.loopCount--; + if (cheat_state.loopCount == 0) { + cheat_state.loopLine = -1; + } else { + if (cheat_state.loopLine != -1) { + cheat_state.index = cheat_state.loopLine; + } + } + } + break; + case 0x02: + // D2 Type + // Format: D2000000 00000000 + // Description: ends all conditionals/repeats before it and sets offset and stored to zero. + // Simple: ends all lines. + // Example: + + // 94000130 FEEF0000 + // C0000000 00000010 + // 8453DA0C 00000200 + // 023D6B28 0009896C + // D6000000 00000005 + // D2000000 00000000 + + // All lines would terminate. + if (cheat_state.loopCount > 0) { + cheat_state.loopCount--; + if (cheat_state.loopCount == 0) { + cheat_state.data = 0; + cheat_state.offset = 0; + cheat_state.loopLine = -1; + + cheat_state.ifStack = 0; + cheat_state.ifCount = 0; + } else { + if (cheat_state.loopLine != -1) { + cheat_state.index = cheat_state.loopLine; + } + } + } else { + cheat_state.data = 0; + cheat_state.offset = 0; + cheat_state.ifStack = 0; + cheat_state.ifCount = 0; + } + break; + case 0x03: + // D3 Type + // Format: D3000000 XXXXXXXX + // Description: sets offset. + // Simple: loads the address X so that lines after can modify the value at address X. + // Note: used with the D4, D5, D6, D7, D8, and DC types. + // Example: D3000000 023D6B28 + if (!skipExecution) { + cheat_state.offset = arg1; + } + break; + case 0x04: + // D4 Type + // Format: D4000000 YYYYYYYY + // Description: adds to the stored address' value. + // Simple: adds to the value at the address defined by lines D3, D9, DA, and DB. + // Note: used with the D3, D9, DA, DB, DC types. + // Example: D4000000 00000025 + if (!skipExecution) { + cheat_state.data += arg1; + } + break; + case 0x05: + // D5 Type + // Format: D5000000 YYYYYYYY + // Description: sets the stored address' value. + // Simple: makes the value at the address defined by lines D3, D9, DA, and DB to YYYYYYYY. + // Note: used with the D3, D9, DA, DB, and DC types. + // Example: D5000000 34540099 + if (!skipExecution) { + cheat_state.data = arg1; + } + break; + case 0x06: + // D6 Type + // Format: D6000000 XXXXXXXX + // Description: 32bit store and increment by 4. + // Simple: stores the value at address XXXXXXXX and to addresses in increments of 4. + // Note: used with the C, D3, and D9 types. + // Example: D3000000 023D6B28 + if (!skipExecution) { + Cheat_write32(arg1, cheat_state.data); + cheat_state.offset += 4; + } + break; + case 0x07: + // D7 Type + // Format: D7000000 XXXXXXXX + // Description: 16bit store and increment by 2. + // Simple: stores 2 bytes of the value at address XXXXXXXX and to addresses in increments of 2. + // Note: used with the C, D3, and DA types. + // Example: D7000000 023D6B28 + if (!skipExecution) { + Cheat_write16(arg1, (u16) (cheat_state.data & 0xFFFF)); + cheat_state.offset += 2; + } + break; + case 0x08: + // D8 Type + // Format: D8000000 XXXXXXXX + // Description: 8bit store and increment by 1. + // Simple: stores 1 byte of the value at address XXXXXXXX and to addresses in increments of 1. + // Note: used with the C, D3, and DB types. + // Example: D8000000 023D6B28 + if (!skipExecution) { + Cheat_write8(arg1, (u8) (cheat_state.data & 0xFF)); + cheat_state.offset += 1; + } + break; + case 0x09: + // D9 Type + // Format: D9000000 XXXXXXXX + // Description: 32bit load. + // Simple: loads the value from address X. + // Note: used with the D5 and D6 types. + // Example: D9000000 023D6B28 + if (!skipExecution) { + cheat_state.data = Cheat_read32(arg1); + } + break; + case 0x0A: + // DA Type + // Format: DA000000 XXXXXXXX + // Description: 16bit load. + // Simple: loads 2 bytes from address X. + // Note: used with the D5 and D7 types. + // Example: DA000000 023D6B28 + if (!skipExecution) { + cheat_state.data = Cheat_read16(arg1); + } + break; + case 0x0B: + // DB Type + // Format: DB000000 XXXXXXXX + // Description: 8bit load. + // Simple: loads 1 byte from address X. + // Note: used with the D5 and D8 types. + // Example: DB000000 023D6B28 + if (!skipExecution) { + cheat_state.data = Cheat_read8(arg1); + } + break; + case 0x0C: + // DC Type + // Format: DC000000 VVVVVVVV + // Description: 32bit store and increment by V. + // Simple: stores the value at address(es) before it and to addresses in increments of V. + // Note: used with the C, D3, D5, D9, D8, DB types. + // Example: DC000000 00000100 + if (!skipExecution) { + cheat_state.offset += arg1; + } + break; + case 0x0D: + // DD Type + { + cheatDescriptions[cheat_state.id].keyCombo |= (arg1 & 0xFFF); + u32 newSkip; + if ((HID_PAD & arg1) == arg1) { + newSkip = 0; + } else { + newSkip = 1; + } + + cheat_state.ifStack <<= 1; + cheat_state.ifStack |= ((newSkip | skipExecution) & 0x1); + cheat_state.ifCount++; + } + break; + default: + goto end_main_loop; + } + break; + case 0xE: + // E Type + // Format: + // EXXXXXXX UUUUUUUU + // YYYYYYYY YYYYYYYY + + // Description: writes Y to X for U bytes. + + { + u32 beginOffset = (arg0 & 0x0FFFFFFF); + u32 count = arg1; + cheat_state.typeELine = cheat_state.index; + cheat_state.typeEIdx = 7; + for (u32 i = 0; i < count; i++) { + u8 byte = Cheat_getNextTypeE(); + if (!skipExecution) { + Cheat_write8(beginOffset + i, byte); + } + } + cheat_state.index = cheat_state.typeELine; + } + break; + default: + goto end_main_loop; + } + cheat_state.index++; + } + end_main_loop: ; +} + +Result Cheat_mapMemoryAndApplyCheat(u32 pid, u32 cheatIdx) { + Handle processHandle; + Result res; + res = svcOpenProcess(&processHandle, pid); + if (R_SUCCEEDED(res)) { + + u32 codeStartAddress, heapStartAddress; + u32 codeDestAddress, heapDestAddress; + u32 codeTotalSize, heapTotalSize; + + s64 textStartAddress, textTotalRoundedSize, rodataTotalRoundedSize, + dataTotalRoundedSize; + + svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); + svcGetProcessInfo(&rodataTotalRoundedSize, processHandle, 0x10003); + svcGetProcessInfo(&dataTotalRoundedSize, processHandle, 0x10004); + + svcGetProcessInfo(&textStartAddress, processHandle, 0x10005); + + codeTotalSize = (u32) (textTotalRoundedSize + rodataTotalRoundedSize + + dataTotalRoundedSize); + codeDestAddress = codeStartAddress = (u32) textStartAddress; //should be 0x00100000 + + MemInfo info; + PageInfo out; + + heapDestAddress = heapStartAddress = 0x08000000; + svcQueryProcessMemory(&info, &out, processHandle, heapStartAddress); + heapTotalSize = info.size; + + Result codeRes = svcMapProcessMemoryEx(processHandle, codeDestAddress, + codeStartAddress, codeTotalSize); + Result heapRes = svcMapProcessMemoryEx(processHandle, heapDestAddress, + heapStartAddress, heapTotalSize); + + if (R_SUCCEEDED(codeRes | heapRes)) { + Cheat_applyCheat(cheatIdx); + + if (R_SUCCEEDED(codeRes)) + svcUnmapProcessMemoryEx(processHandle, codeDestAddress, + codeTotalSize); + if (R_SUCCEEDED(heapRes)) + svcUnmapProcessMemoryEx(processHandle, heapDestAddress, + heapTotalSize); + + svcCloseHandle(processHandle); + cheatDescriptions[cheatIdx].active = 1; + } else { + svcCloseHandle(processHandle); + } + } + return res; +} + +void Cheat_progIdToStr(char *strEnd, u64 progId) { + while (progId > 0) { + static const char hexDigits[] = "0123456789ABCDEF"; + *strEnd-- = hexDigits[(u32) (progId & 0xF)]; + progId >>= 4; + } +} + +Result Cheat_fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, + int flags) { + FS_Path filePath = { PATH_ASCII, strnlen(path, 255) + 1, path }, + archivePath = { PATH_EMPTY, 1, (u8 *) "" }; + + return IFile_Open(file, archiveId, archivePath, filePath, flags); +} + +static bool Cheat_openLumaFile(IFile *file, const char *path) { + return R_SUCCEEDED(Cheat_fileOpen(file, ARCHIVE_SDMC, path, FS_OPEN_READ)); +} + +void loadCheatsIntoMemory(u64 titleId) { + cheatCount = 0; + cheatTitleInfo = titleId; + hasKeyActivated = 0; + + char path[] = "/luma/titles/0000000000000000/cheats.bin"; + Cheat_progIdToStr(path + 28, titleId); + + IFile file; + + if (!Cheat_openLumaFile(&file, path)) + return; + + u8 buffer[8]; + u64 total; + + IFile_Read(&file, &total, buffer, 1); + cheatCount = buffer[0]; + for (u8 i = 0; i < cheatCount; i++) { + cheatDescriptions[i].active = 0; + IFile_Read(&file, &total, buffer, 1); + u8 nameLen = buffer[0]; + IFile_Read(&file, &total, cheatDescriptions[i].name, nameLen); + cheatDescriptions[i].name[nameLen] = '\0'; + IFile_Read(&file, &total, buffer, 1); + cheatDescriptions[i].codesCount = buffer[0]; + cheatDescriptions[i].keyActivated = 0; + cheatDescriptions[i].keyCombo = 0; + for (u8 j = 0; j < cheatDescriptions[i].codesCount; j++) { + IFile_Read(&file, &total, buffer, 8); + u64 tmp = buffer[0]; + for (u8 k = 1; k < 8; k++) { + tmp = (tmp << 8) + buffer[k]; + } + cheatDescriptions[i].codes[j] = tmp; + if (((tmp >> 56) & 0xFF) == 0xDD) { + cheatDescriptions[i].keyActivated = 1; + } + } + } + + IFile_Close(&file); +} + +u32 Cheats_GetCurrentPID(u64* titleId) { + s32 processAmount = Cheats_FetchProcessInfo(); + + s32 index = -1; + + for (s32 i = 0; i < processAmount; i++) { + + if (((u32) (cheatinfo[i].titleId >> 32)) == 0x00040010 + || ((u32) (cheatinfo[i].titleId >> 32) == 0x00040000)) { + index = i; + + break; + } + } + + if (index != -1) { + *titleId = cheatinfo[index].titleId; + return cheatinfo[index].pid; + } else { + *titleId = 0; + return 0xFFFFFFFF; + } +} + +void Cheats_applyKeyCheats(void) { + if (!cheatCount) { + return; + } + if (!hasKeyActivated) { + return; + } + + u64 titleId = 0; + u32 pid = Cheats_GetCurrentPID(&titleId); + + if (!titleId) { + cheatCount = 0; + hasKeyActivated = 0; + return; + } + + if (titleId != cheatTitleInfo) { + cheatCount = 0; + hasKeyActivated = 0; + return; + } + + u32 keys = HID_PAD & 0xFFF; + for (int i = 0; i < cheatCount; i++) { + if (cheatDescriptions[i].active && cheatDescriptions[i].keyActivated + && (cheatDescriptions[i].keyCombo & keys) == keys) { + Cheat_mapMemoryAndApplyCheat(pid, i); + } + } +} + +void RosalinaMenu_Cheats(void) { + u64 titleId = 0; + u32 pid = Cheats_GetCurrentPID(&titleId); + + if (titleId != 0) { + if (cheatTitleInfo != titleId) { + loadCheatsIntoMemory(titleId); + } + } + + Draw_Lock(); + Draw_ClearFramebuffer(); + Draw_FlushFramebuffer(); + Draw_Unlock(); + + if (titleId == 0 || cheatCount == 0) { + do { + Draw_Lock(); + Draw_DrawString(10, 10, COLOR_TITLE, "Cheats"); + if (titleId == 0) { + Draw_DrawString(10, 30, COLOR_WHITE, "No suitable title found"); + } else { + Draw_DrawFormattedString(10, 30, COLOR_WHITE, + "No cheats found for title %016llx", titleId); + } + + Draw_FlushFramebuffer(); + Draw_Unlock(); + } while (!(waitInput() & BUTTON_B) && !terminationRequest); + } else { + s32 selected = 0, page = 0, pagePrev = 0; + + do { + Draw_Lock(); + if (page != pagePrev) { + Draw_ClearFramebuffer(); + } + Draw_DrawFormattedString(10, 10, COLOR_TITLE, "Cheat list"); + + for (s32 i = 0; + i < CHEATS_PER_MENU_PAGE + && page * CHEATS_PER_MENU_PAGE + i < cheatCount; + i++) { + char buf[65] = { 0 }; + const char * checkbox = ( + cheatDescriptions[i].active ? "(x) " : "( ) "); + const char * keyAct = ( + cheatDescriptions[i].keyActivated ? "*" : " "); + sprintf(buf, "%s%s%s", checkbox, keyAct, + cheatDescriptions[i].name); + + Draw_DrawString(30, 30 + i * SPACING_Y, COLOR_WHITE, buf); + Draw_DrawCharacter(10, 30 + i * SPACING_Y, COLOR_TITLE, + page * CHEATS_PER_MENU_PAGE + i == selected ? + '>' : ' '); + } + + Draw_FlushFramebuffer(); + Draw_Unlock(); + + if (terminationRequest) + break; + + u32 pressed; + do { + pressed = waitInputWithTimeout(50); + if (pressed != 0) + break; + } while (pressed == 0 && !terminationRequest); + + if (pressed & BUTTON_B) + break; + else if (pressed & BUTTON_A) { + if (cheatDescriptions[selected].active) { + cheatDescriptions[selected].active = 0; + } else { + Cheat_mapMemoryAndApplyCheat(pid, selected); + } + hasKeyActivated = 0; + for (int i = 0; i < cheatCount; i++) { + if (cheatDescriptions[i].active + && cheatDescriptions[i].keyActivated) { + hasKeyActivated = 1; + break; + } + } + } else if (pressed & BUTTON_DOWN) + selected++; + else if (pressed & BUTTON_UP) + selected--; + else if (pressed & BUTTON_LEFT) + selected -= CHEATS_PER_MENU_PAGE; + else if (pressed & BUTTON_RIGHT) { + if (selected + CHEATS_PER_MENU_PAGE < cheatCount) + selected += CHEATS_PER_MENU_PAGE; + else if ((cheatCount - 1) / CHEATS_PER_MENU_PAGE == page) + selected %= CHEATS_PER_MENU_PAGE; + else + selected = cheatCount - 1; + } + + if (selected < 0) + selected = cheatCount - 1; + else if (selected >= cheatCount) + selected = 0; + + pagePrev = page; + page = selected / CHEATS_PER_MENU_PAGE; + } while (!terminationRequest); + } + +}