2017-12-21 18:14:04 +01:00
# 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 ;
2017-12-22 10:17:45 +01:00
u64 codes [ 0 ] ;
2017-12-21 18:14:04 +01:00
} CheatDescription ;
2017-12-22 10:17:45 +01:00
CheatDescription * cheats [ 1024 ] = { 0 } ;
u8 cheatBuffer [ 65536 ] = { 0 } ;
2017-12-21 18:14:04 +01:00
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 {
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 ;
2017-12-22 10:17:45 +01:00
//CheatDescription cheatDescriptions[0x20] = { 0 };
2017-12-21 18:14:04 +01:00
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 } ;
2017-12-22 10:17:45 +01:00
u8 Cheat_getNextTypeE ( const CheatDescription * cheat ) {
2017-12-21 18:14:04 +01:00
if ( cheat_state . typeEIdx = = 7 ) {
cheat_state . typeEIdx = 0 ;
cheat_state . typeELine + + ;
} else {
cheat_state . typeEIdx + + ;
}
2017-12-22 10:17:45 +01:00
return ( u8 ) ( ( cheat - > codes [ cheat_state . typeELine ]
2017-12-21 18:14:04 +01:00
> > ( typeEMapping [ cheat_state . typeEIdx ] ) ) & 0xFF ) ;
}
2017-12-22 10:39:09 +01:00
void Cheat_applyCheat ( const CheatDescription * const cheat ) {
2017-12-21 18:14:04 +01:00
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 ;
2017-12-22 10:17:45 +01:00
while ( cheat_state . index < cheat - > codesCount ) {
2017-12-21 18:14:04 +01:00
u32 skipExecution = cheat_state . ifStack & 0x00000001 ;
2017-12-22 10:17:45 +01:00
u32 arg0 = ( u32 ) ( ( cheat - > codes [ cheat_state . index ] > > 32 )
& 0x00000000FFFFFFFFULL ) ;
u32 arg1 = ( u32 ) ( ( cheat - > codes [ cheat_state . index ] )
& 0x00000000FFFFFFFFULL ) ;
2017-12-21 18:14:04 +01:00
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
{
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 + + ) {
2017-12-22 10:17:45 +01:00
u8 byte = Cheat_getNextTypeE ( cheat ) ;
2017-12-21 18:14:04 +01:00
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 : ;
}
2017-12-22 10:17:45 +01:00
Result Cheat_mapMemoryAndApplyCheat ( u32 pid , CheatDescription * const cheat ) {
2017-12-21 18:14:04 +01:00
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 ) ) {
2017-12-22 10:17:45 +01:00
Cheat_applyCheat ( cheat ) ;
2017-12-21 18:14:04 +01:00
if ( R_SUCCEEDED ( codeRes ) )
svcUnmapProcessMemoryEx ( processHandle , codeDestAddress ,
codeTotalSize ) ;
if ( R_SUCCEEDED ( heapRes ) )
svcUnmapProcessMemoryEx ( processHandle , heapDestAddress ,
heapTotalSize ) ;
svcCloseHandle ( processHandle ) ;
2017-12-22 10:17:45 +01:00
cheat - > active = 1 ;
2017-12-21 18:14:04 +01:00
} 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 ) ) ;
}
2017-12-22 10:17:45 +01:00
CheatDescription * Cheats_allocCheat ( ) {
CheatDescription * cheat ;
if ( cheatCount = = 0 ) {
cheat = ( CheatDescription * ) cheatBuffer ;
} else {
CheatDescription * prev = cheats [ cheatCount - 1 ] ;
cheat = ( CheatDescription * ) ( ( u8 * ) ( prev ) + sizeof ( CheatDescription )
+ sizeof ( u64 ) * ( prev - > codesCount ) ) ;
}
cheats [ cheatCount ] = cheat ;
cheatCount + + ;
return cheat ;
}
void Cheats_addCode ( CheatDescription * cheat , u64 code ) {
cheat - > codes [ cheat - > codesCount ] = code ;
( cheat - > codesCount ) + + ;
}
2017-12-21 18:14:04 +01:00
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 ) ;
2017-12-22 10:17:45 +01:00
u8 cc = buffer [ 0 ] ;
for ( u8 i = 0 ; i < cc ; i + + ) {
CheatDescription * cheat = Cheats_allocCheat ( ) ;
cheat - > active = 0 ;
2017-12-21 18:14:04 +01:00
IFile_Read ( & file , & total , buffer , 1 ) ;
u8 nameLen = buffer [ 0 ] ;
2017-12-22 10:17:45 +01:00
IFile_Read ( & file , & total , cheat - > name , nameLen ) ;
cheat - > name [ nameLen ] = ' \0 ' ;
2017-12-21 18:14:04 +01:00
IFile_Read ( & file , & total , buffer , 1 ) ;
2017-12-22 10:17:45 +01:00
u8 codeCount = buffer [ 0 ] ;
cheat - > codesCount = 0 ;
cheat - > keyActivated = 0 ;
cheat - > keyCombo = 0 ;
for ( u8 j = 0 ; j < codeCount ; j + + ) {
2017-12-21 18:14:04 +01:00
IFile_Read ( & file , & total , buffer , 8 ) ;
u64 tmp = buffer [ 0 ] ;
for ( u8 k = 1 ; k < 8 ; k + + ) {
tmp = ( tmp < < 8 ) + buffer [ k ] ;
}
2017-12-22 10:17:45 +01:00
Cheats_addCode ( cheat , tmp ) ;
2017-12-22 10:39:09 +01:00
if ( ( ( tmp > > 32 ) & 0xFFFFFFFF ) = = 0xDD000000 ) {
cheat - > keyCombo | = ( tmp & 0xFFF ) ;
2017-12-22 10:17:45 +01:00
cheat - > keyActivated = 1 ;
2017-12-21 18:14:04 +01:00
}
}
}
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 + + ) {
2017-12-22 10:17:45 +01:00
if ( cheats [ i ] - > active & & cheats [ i ] - > keyActivated
& & ( cheats [ i ] - > keyCombo & keys ) = = keys ) {
Cheat_mapMemoryAndApplyCheat ( pid , cheats [ i ] ) ;
2017-12-21 18:14:04 +01:00
}
}
}
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 } ;
2017-12-22 10:17:45 +01:00
const char * checkbox = ( cheats [ i ] - > active ? " (x) " : " ( ) " ) ;
const char * keyAct = ( cheats [ i ] - > keyActivated ? " * " : " " ) ;
sprintf ( buf , " %s%s%s " , checkbox , keyAct , cheats [ i ] - > name ) ;
2017-12-21 18:14:04 +01:00
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 ) {
2017-12-22 10:17:45 +01:00
if ( cheats [ selected ] - > active ) {
cheats [ selected ] - > active = 0 ;
2017-12-21 18:14:04 +01:00
} else {
2017-12-22 10:17:45 +01:00
Cheat_mapMemoryAndApplyCheat ( pid , cheats [ selected ] ) ;
2017-12-21 18:14:04 +01:00
}
hasKeyActivated = 0 ;
for ( int i = 0 ; i < cheatCount ; i + + ) {
2017-12-22 10:17:45 +01:00
if ( cheats [ i ] - > active & & cheats [ i ] - > keyActivated ) {
2017-12-21 18:14:04 +01:00
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 ) ;
}
}