2017-12-22 12:46:46 +01:00
/*
* This file is part of Luma3DS
2019-02-25 02:04:32 +01:00
* Copyright ( C ) 2016 - 2019 Aurora Wright , TuxSH
2017-12-22 12:46:46 +01:00
*
* 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 .
*/
/* This file was entirely written by Duckbill */
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 ) ) )
2018-01-13 11:51:28 +01:00
typedef struct CheatProcessInfo
{
u32 pid ;
u64 titleId ;
2017-12-21 18:14:04 +01:00
} CheatProcessInfo ;
2018-01-13 11:51:28 +01:00
typedef struct CheatDescription
{
u32 active ;
u32 valid ;
2019-04-15 08:12:14 +02:00
char hasKeyCode ;
char name [ 39 ] ;
2018-01-13 11:51:28 +01:00
u32 codesCount ;
u64 codes [ 0 ] ;
2017-12-21 18:14:04 +01:00
} CheatDescription ;
2019-04-16 00:14:53 +02:00
typedef struct BufferedFile
{
IFile file ;
u64 curPos ;
u64 maxPos ;
char buffer [ 512 ] ;
} BufferedFile ;
2017-12-22 10:17:45 +01:00
CheatDescription * cheats [ 1024 ] = { 0 } ;
2019-04-16 00:14:53 +02:00
u8 cheatBuffer [ 32768 ] = { 0 } ;
2017-12-22 10:17:45 +01:00
2017-12-21 18:14:04 +01:00
static CheatProcessInfo cheatinfo [ 0x40 ] = { 0 } ;
2018-01-13 11:51:28 +01:00
static s32 Cheats_FetchProcessInfo ( void )
{
u32 pidList [ 0x40 ] ;
s32 processAmount ;
2017-12-21 18:14:04 +01:00
2018-01-13 11:51:28 +01:00
s64 sa , textTotalRoundedSize , rodataTotalRoundedSize , dataTotalRoundedSize ;
2017-12-21 18:14:04 +01:00
2018-01-13 11:51:28 +01:00
svcGetProcessList ( & processAmount , pidList , 0x40 ) ;
2017-12-21 18:14:04 +01:00
2018-01-13 11:51:28 +01:00
for ( s32 i = 0 ; i < processAmount ; i + + )
{
Handle processHandle ;
Result res = svcOpenProcess ( & processHandle , pidList [ i ] ) ;
if ( R_FAILED ( res ) ) continue ;
2017-12-21 18:14:04 +01:00
2018-01-13 11:51:28 +01:00
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 ) ;
2017-12-21 18:14:04 +01:00
2018-01-13 11:51:28 +01:00
svcCloseHandle ( processHandle ) ;
}
2017-12-21 18:14:04 +01:00
2018-01-13 11:51:28 +01:00
return processAmount ;
2017-12-21 18:14:04 +01:00
}
2018-01-13 11:51:28 +01:00
typedef struct CheatState
{
u32 index ;
u32 offset ;
u32 data ;
u8 typeELine ;
u8 typeEIdx ;
2017-12-21 18:14:04 +01:00
2018-01-13 11:51:28 +01:00
s8 loopLine ;
u32 loopCount ;
2017-12-21 18:14:04 +01:00
2018-01-13 11:51:28 +01:00
u32 ifStack ;
u32 storedStack ;
u8 ifCount ;
u8 storedIfCount ;
2017-12-21 18:14:04 +01:00
} CheatState ;
CheatState cheat_state = { 0 } ;
u8 cheatCount = 0 ;
u64 cheatTitleInfo = - 1ULL ;
char failureReason [ 64 ] ;
2018-03-20 01:11:49 +01:00
static bool Cheat_IsValidAddress ( const Handle processHandle , u32 address , u32 size )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
MemInfo info ;
PageInfo out ;
Result res = svcQueryDebugProcessMemory ( & info , & out , processHandle , address ) ;
if ( R_SUCCEEDED ( res ) & & info . state ! = MEMSTATE_FREE & & info . base_addr > 0 & & info . base_addr < = address & & address < = info . base_addr + info . size - size ) {
2018-01-13 11:51:28 +01:00
return true ;
}
return false ;
2017-12-21 18:14:04 +01:00
}
2018-01-02 12:23:45 +01:00
2018-03-20 01:11:49 +01:00
static u32 ReadWriteBuffer32 = 0 ;
static u16 ReadWriteBuffer16 = 0 ;
static u8 ReadWriteBuffer8 = 0 ;
static bool Cheat_Write8 ( const Handle processHandle , u32 offset , u8 value )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
if ( Cheat_IsValidAddress ( processHandle , cheat_state . offset + offset , 1 ) )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
* ( ( u8 * ) ( & ReadWriteBuffer8 ) ) = value ;
return R_SUCCEEDED ( svcWriteProcessMemory ( processHandle , & ReadWriteBuffer8 , cheat_state . offset + offset , 1 ) ) ;
2018-01-13 11:51:28 +01:00
}
return false ;
2017-12-21 18:14:04 +01:00
}
2018-03-20 01:11:49 +01:00
static bool Cheat_Write16 ( const Handle processHandle , u32 offset , u16 value )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
if ( Cheat_IsValidAddress ( processHandle , cheat_state . offset + offset , 2 ) )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
* ( ( u16 * ) ( & ReadWriteBuffer16 ) ) = value ;
return R_SUCCEEDED ( svcWriteProcessMemory ( processHandle , & ReadWriteBuffer16 , cheat_state . offset + offset , 2 ) ) ;
2018-01-13 11:51:28 +01:00
}
return false ;
2017-12-21 18:14:04 +01:00
}
2018-03-20 01:11:49 +01:00
static bool Cheat_Write32 ( const Handle processHandle , u32 offset , u32 value )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
if ( Cheat_IsValidAddress ( processHandle , cheat_state . offset + offset , 4 ) )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
* ( ( u32 * ) ( & ReadWriteBuffer32 ) ) = value ;
return R_SUCCEEDED ( svcWriteProcessMemory ( processHandle , & ReadWriteBuffer32 , cheat_state . offset + offset , 4 ) ) ;
2018-01-13 11:51:28 +01:00
}
return false ;
2017-12-21 18:14:04 +01:00
}
2018-03-20 01:11:49 +01:00
static bool Cheat_Read8 ( const Handle processHandle , u32 offset , u8 * retValue )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
if ( Cheat_IsValidAddress ( processHandle , cheat_state . offset + offset , 1 ) )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
Result res = svcReadProcessMemory ( & ReadWriteBuffer8 , processHandle , cheat_state . offset + offset , 1 ) ;
* retValue = * ( ( u8 * ) ( & ReadWriteBuffer8 ) ) ;
return R_SUCCEEDED ( res ) ;
2018-01-13 11:51:28 +01:00
}
return false ;
2017-12-21 18:14:04 +01:00
}
2018-03-20 01:11:49 +01:00
static bool Cheat_Read16 ( const Handle processHandle , u32 offset , u16 * retValue )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
if ( Cheat_IsValidAddress ( processHandle , cheat_state . offset + offset , 2 ) )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
Result res = svcReadProcessMemory ( & ReadWriteBuffer16 , processHandle , cheat_state . offset + offset , 2 ) ;
* retValue = * ( ( u16 * ) ( & ReadWriteBuffer16 ) ) ;
return R_SUCCEEDED ( res ) ;
2018-01-13 11:51:28 +01:00
}
return false ;
2017-12-21 18:14:04 +01:00
}
2018-03-20 01:11:49 +01:00
static bool Cheat_Read32 ( const Handle processHandle , u32 offset , u32 * retValue )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
if ( Cheat_IsValidAddress ( processHandle , cheat_state . offset + offset , 4 ) )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
Result res = svcReadProcessMemory ( & ReadWriteBuffer32 , processHandle , cheat_state . offset + offset , 4 ) ;
* retValue = * ( ( u32 * ) ( & ReadWriteBuffer32 ) ) ;
return R_SUCCEEDED ( res ) ;
2018-01-13 11:51:28 +01:00
}
return false ;
2017-12-21 18:14:04 +01:00
}
2018-01-13 11:51:28 +01:00
static u8 typeEMapping [ ] = { 4 < < 3 , 5 < < 3 , 6 < < 3 , 7 < < 3 , 0 < < 3 , 1 < < 3 , 2 < < 3 , 3 < < 3 } ;
static u8 Cheat_GetNextTypeE ( const CheatDescription * cheat )
{
if ( cheat_state . typeEIdx = = 7 )
{
cheat_state . typeEIdx = 0 ;
cheat_state . typeELine + + ;
}
else
{
cheat_state . typeEIdx + + ;
}
return ( u8 ) ( ( cheat - > codes [ cheat_state . typeELine ] > > ( typeEMapping [ cheat_state . typeEIdx ] ) ) & 0xFF ) ;
2017-12-21 18:14:04 +01:00
}
2018-03-20 01:11:49 +01:00
static u32 Cheat_ApplyCheat ( const Handle processHandle , const CheatDescription * const cheat )
2018-01-13 11:51:28 +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 ;
while ( cheat_state . index < cheat - > codesCount )
{
u32 skipExecution = cheat_state . ifStack & 0x00000001 ;
u32 arg0 = ( u32 ) ( ( cheat - > codes [ cheat_state . index ] > > 32 ) & 0x00000000FFFFFFFFULL ) ;
u32 arg1 = ( u32 ) ( ( cheat - > codes [ cheat_state . index ] ) & 0x00000000FFFFFFFFULL ) ;
if ( arg0 = = 0 & & arg1 = = 0 )
{
return 0 ;
}
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 )
{
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Write32 ( processHandle , ( arg0 & 0x0FFFFFFF ) , arg1 ) ) return 0 ;
2018-01-13 11:51:28 +01:00
}
break ;
case 0x1 :
// 1 Type
// Format: 1XXXXXXX 0000YYYY
// Description: 16bit write of YYYY to 0XXXXXXX.
if ( ! skipExecution )
{
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Write16 ( processHandle , ( arg0 & 0x0FFFFFFF ) , ( u16 ) ( arg1 & 0xFFFF ) ) ) return 0 ;
2018-01-13 11:51:28 +01:00
}
break ;
case 0x2 :
// 2 Type
// Format: 2XXXXXXX 000000YY
// Description: 8bit write of YY to 0XXXXXXX.
if ( ! skipExecution )
{
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Write8 ( processHandle , ( arg0 & 0x0FFFFFFF ) , ( u8 ) ( arg1 & 0xFF ) ) ) return 0 ;
2018-01-13 11:51:28 +01:00
}
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 ;
u32 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x0FFFFFFF , & value ) ) return 0 ;
2018-01-13 11:51:28 +01:00
if ( value < 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 ;
u32 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x0FFFFFFF , & value ) ) return 0 ;
2018-01-13 11:51:28 +01:00
if ( value > 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 ;
u32 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x0FFFFFFF , & value ) ) return 0 ;
2018-01-13 11:51:28 +01:00
if ( value = = 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 ;
u32 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x0FFFFFFF , & value ) ) return 0 ;
2018-01-13 11:51:28 +01:00
if ( value ! = 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 ) ;
u16 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read16 ( processHandle , arg0 & 0x0FFFFFFF , & value ) ) return 0 ;
2018-01-13 11:51:28 +01:00
if ( ( value & ( ~ 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 ) ;
u16 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read16 ( processHandle , arg0 & 0x0FFFFFFF , & value ) ) return 0 ;
2018-01-13 11:51:28 +01:00
if ( ( value & ( ~ 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 ) ;
u16 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read16 ( processHandle , arg0 & 0x0FFFFFFF , & value ) ) return 0 ;
2018-01-13 11:51:28 +01:00
if ( ( value & ( ~ 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 ) ;
u16 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read16 ( processHandle , arg0 & 0x0FFFFFFF , & value ) ) return 0 ;
2018-01-13 11:51:28 +01:00
if ( ( value & ( ~ 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
2018-08-09 14:21:16 +02:00
// Description: Loads offset register with value at given XXXXXXX
2018-01-13 11:51:28 +01:00
if ( ! skipExecution )
{
2018-08-07 12:50:48 +02:00
u32 value ;
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x0FFFFFFF , & value ) ) return 0 ;
cheat_state . offset = value ;
2018-01-13 11:51:28 +01:00
}
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 )
{
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Write32 ( processHandle , arg1 , cheat_state . data ) ) return 0 ;
2018-01-13 11:51:28 +01:00
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 )
{
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Write16 ( processHandle , arg1 , ( u16 ) ( cheat_state . data & 0xFFFF ) ) ) return 0 ;
2018-01-13 11:51:28 +01:00
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 )
{
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Write8 ( processHandle , arg1 , ( u8 ) ( cheat_state . data & 0xFF ) ) ) return 0 ;
2018-01-13 11:51:28 +01:00
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 )
{
u32 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read32 ( processHandle , arg1 , & value ) ) return 0 ;
2018-01-13 11:51:28 +01:00
cheat_state . data = value ;
}
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 )
{
u16 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read16 ( processHandle , arg1 , & value ) ) return 0 ;
2018-01-13 11:51:28 +01:00
cheat_state . data = value ;
}
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 )
{
u8 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read8 ( processHandle , arg1 , & value ) ) return 0 ;
2018-01-13 11:51:28 +01:00
cheat_state . data = value ;
}
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 ( arg1 = = 0 | | ( HID_PAD & arg1 ) = = arg1 )
{
newSkip = 0 ;
}
else
{
newSkip = 1 ;
}
cheat_state . ifStack < < = 1 ;
cheat_state . ifStack | = ( ( newSkip | skipExecution ) & 0x1 ) ;
cheat_state . ifCount + + ;
}
break ;
default :
return 0 ;
}
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 ( cheat ) ;
if ( ! skipExecution )
{
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Write8 ( processHandle , beginOffset + i , byte ) ) return 0 ;
2018-01-13 11:51:28 +01:00
}
}
cheat_state . index = cheat_state . typeELine ;
}
break ;
default :
return 0 ;
}
cheat_state . index + + ;
}
return 1 ;
2017-12-21 18:14:04 +01:00
}
2018-03-20 01:11:49 +01:00
static void Cheat_EatEvents ( Handle debug )
{
DebugEventInfo info ;
Result r ;
while ( true )
{
if ( ( r = svcGetProcessDebugEvent ( & info , debug ) ) ! = 0 )
{
if ( r = = ( s32 ) ( 0xd8402009 ) )
{
break ;
}
}
svcContinueDebugEvent ( debug , 3 ) ;
}
}
2018-01-13 11:51:28 +01:00
static Result Cheat_MapMemoryAndApplyCheat ( u32 pid , CheatDescription * const cheat )
{
Handle processHandle ;
2018-03-20 01:11:49 +01:00
Handle debugHandle ;
2018-01-13 11:51:28 +01:00
Result res ;
res = svcOpenProcess ( & processHandle , pid ) ;
if ( R_SUCCEEDED ( res ) )
{
2018-03-20 01:11:49 +01:00
res = svcDebugActiveProcess ( & debugHandle , pid ) ;
if ( R_SUCCEEDED ( res ) )
2018-01-16 07:54:55 +01:00
{
2018-03-20 01:11:49 +01:00
Cheat_EatEvents ( debugHandle ) ;
cheat - > valid = Cheat_ApplyCheat ( debugHandle , cheat ) ;
2018-01-13 11:51:28 +01:00
2018-03-20 01:11:49 +01:00
svcCloseHandle ( debugHandle ) ;
2018-01-13 11:51:28 +01:00
svcCloseHandle ( processHandle ) ;
cheat - > active = 1 ;
}
else
{
2019-04-15 08:12:14 +02:00
sprintf ( failureReason , " Debug process failed " ) ;
2018-01-13 11:51:28 +01:00
svcCloseHandle ( processHandle ) ;
}
}
else
{
sprintf ( failureReason , " Open process failed " ) ;
}
return res ;
2017-12-22 10:17:45 +01:00
}
2018-01-13 11:51:28 +01:00
static CheatDescription * Cheat_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 ) ) ;
}
cheat - > active = 0 ;
cheat - > valid = 1 ;
cheat - > codesCount = 0 ;
2019-04-15 08:12:14 +02:00
cheat - > hasKeyCode = 0 ;
2018-01-13 11:51:28 +01:00
cheat - > name [ 0 ] = ' \0 ' ;
cheats [ cheatCount ] = cheat ;
cheatCount + + ;
return cheat ;
2017-12-22 12:46:46 +01:00
}
2018-01-13 11:51:28 +01:00
static void Cheat_AddCode ( CheatDescription * cheat , u64 code )
{
if ( cheat )
{
cheat - > codes [ cheat - > codesCount ] = code ;
( cheat - > codesCount ) + + ;
}
2017-12-22 12:46:46 +01:00
}
2019-04-16 00:14:53 +02:00
static Result BufferedFile_Open ( BufferedFile * file , FS_ArchiveID archiveId , FS_Path archivePath , FS_Path filePath , u32 flags )
{
Result res = 0 ;
memset ( file - > buffer , ' \0 ' , sizeof ( file - > buffer ) ) ;
res = IFile_Open ( & file - > file , archiveId , archivePath , filePath , flags ) ;
if ( R_SUCCEEDED ( res ) )
{
file - > curPos = 0 ;
res = IFile_Read ( & file - > file , & file - > maxPos , file - > buffer , sizeof ( file - > buffer ) ) ;
}
return res ;
}
static Result BufferedFile_Read ( BufferedFile * file , u64 * totalRead , void * buffer , u32 len )
{
Result res = 0 ;
if ( len = = 0 )
{
* totalRead = 0 ;
return 0 ;
}
else if ( file - > curPos + len < file - > maxPos )
{
memcpy ( buffer , file - > buffer + file - > curPos , len ) ;
file - > curPos + = len ;
* totalRead = len ;
}
else
{
* totalRead = 0 ;
while ( R_SUCCEEDED ( res ) & & file - > maxPos ! = 0 & & * totalRead < len )
{
u32 toRead = file - > maxPos - file - > curPos < len - * totalRead ? file - > maxPos - file - > curPos : len - * totalRead ;
memcpy ( buffer + * totalRead , file - > buffer + file - > curPos , toRead ) ;
* totalRead + = toRead ;
file - > curPos + = toRead ;
if ( file - > curPos > = file - > maxPos )
{
res = IFile_Read ( & file - > file , & file - > maxPos , file - > buffer , sizeof ( file - > buffer ) ) ;
file - > curPos = 0 ;
}
}
}
return res ;
}
static Result Cheat_ReadLine ( BufferedFile * file , char * line , u32 lineSize )
2018-01-13 11:51:28 +01:00
{
Result res = 0 ;
u32 idx = 0 ;
2019-04-16 00:14:53 +02:00
u64 total = 0 ;
bool lastWasCarriageReturn = false ;
while ( R_SUCCEEDED ( res ) & & idx < lineSize )
2018-01-13 11:51:28 +01:00
{
2019-04-16 00:14:53 +02:00
res = BufferedFile_Read ( file , & total , line + idx , 1 ) ;
if ( total = = 0 )
{
line [ idx ] = ' \0 ' ;
return - 1 ;
}
if ( R_SUCCEEDED ( res ) )
2018-01-13 11:51:28 +01:00
{
2019-04-16 00:14:53 +02:00
if ( line [ idx ] = = ' \r ' )
2019-04-15 08:12:14 +02:00
{
2019-04-16 00:14:53 +02:00
lastWasCarriageReturn = true ;
2019-04-15 08:12:14 +02:00
}
2019-04-16 00:14:53 +02:00
else if ( line [ idx ] = = ' \n ' )
2018-01-13 11:51:28 +01:00
{
2019-04-16 00:14:53 +02:00
if ( lastWasCarriageReturn )
{
line [ - - idx ] = ' \0 ' ;
return idx ;
}
else
{
line [ idx ] = ' \0 ' ;
return idx ;
}
2018-01-13 11:51:28 +01:00
}
2019-04-16 00:14:53 +02:00
else if ( line [ idx ] = = ' \0 ' )
2018-01-13 11:51:28 +01:00
{
2019-04-16 00:14:53 +02:00
return - 1 ;
2018-01-13 11:51:28 +01:00
}
2019-04-16 00:14:53 +02:00
else
2018-01-13 11:51:28 +01:00
{
2019-04-16 00:14:53 +02:00
lastWasCarriageReturn = false ;
2018-01-13 11:51:28 +01:00
}
2019-04-16 00:14:53 +02:00
idx + + ;
2018-01-13 11:51:28 +01:00
}
}
return res ;
2017-12-22 12:46:46 +01:00
}
2018-01-13 11:51:28 +01:00
static bool Cheat_IsCodeLine ( const char * line )
{
s32 len = strnlen ( line , 1023 ) ;
if ( len ! = 17 )
{
return false ;
}
if ( line [ 8 ] ! = ' ' )
{
return false ;
}
int i ;
for ( i = 0 ; i < 8 ; i + + )
{
char c = line [ i ] ;
if ( ! ( ( ' 0 ' < = c & & c < = ' 9 ' ) | | ( ' A ' < = c & & c < = ' F ' ) | | ( ' a ' < = c & & c < = ' f ' ) ) )
{
return false ;
}
}
for ( i = 9 ; i < 17 ; i + + )
{
char c = line [ i ] ;
if ( ! ( ( ' 0 ' < = c & & c < = ' 9 ' ) | | ( ' A ' < = c & & c < = ' F ' ) | | ( ' a ' < = c & & c < = ' f ' ) ) )
{
return false ;
}
}
return true ;
2017-12-22 12:46:46 +01:00
}
2018-01-13 11:51:28 +01:00
static u64 Cheat_GetCode ( const char * line )
{
u64 tmp = 0 ;
int i ;
for ( i = 0 ; i < 8 ; i + + )
{
char c = line [ i ] ;
u8 code = 0 ;
if ( ' 0 ' < = c & & c < = ' 9 ' )
{
code = c - ' 0 ' ;
}
if ( ' A ' < = c & & c < = ' F ' )
{
code = c - ' A ' + 10 ;
}
if ( ' a ' < = c & & c < = ' f ' )
{
code = c - ' a ' + 10 ;
}
tmp < < = 4 ;
tmp | = ( code & 0xF ) ;
}
for ( i = 9 ; i < 17 ; i + + )
{
char c = line [ i ] ;
u8 code = 0 ;
if ( ' 0 ' < = c & & c < = ' 9 ' )
{
code = c - ' 0 ' ;
}
if ( ' A ' < = c & & c < = ' F ' )
{
code = c - ' A ' + 10 ;
}
if ( ' a ' < = c & & c < = ' f ' )
{
code = c - ' a ' + 10 ;
}
tmp < < = 4 ;
tmp | = ( code & 0xF ) ;
}
return tmp ;
2017-12-22 10:17:45 +01:00
}
2019-04-16 00:14:53 +02:00
static char * stripWhitespace ( char * in )
{
char * ret = in ;
while ( * ret = = ' ' | | * ret = = ' \t ' )
{
ret + + ;
}
int back = strlen ( ret ) - 1 ;
while ( back > 0 & & ( ret [ back ] = = ' ' | | ret [ back ] = = ' \t ' ) )
{
back - - ;
}
ret [ back + 1 ] = ' \0 ' ;
return ret ;
}
2018-01-13 11:51:28 +01:00
static void Cheat_LoadCheatsIntoMemory ( u64 titleId )
{
cheatCount = 0 ;
cheatTitleInfo = titleId ;
char path [ 64 ] = { 0 } ;
sprintf ( path , " /luma/titles/%016llX/cheats.txt " , titleId ) ;
2019-04-16 00:14:53 +02:00
BufferedFile file ;
2018-01-13 11:51:28 +01:00
2019-04-16 00:14:53 +02:00
if ( R_FAILED ( BufferedFile_Open ( & file , ARCHIVE_SDMC , fsMakePath ( PATH_EMPTY , " " ) , fsMakePath ( PATH_ASCII , path ) , FS_OPEN_READ ) ) )
2018-01-13 11:51:28 +01:00
{
// OK, let's try another source
sprintf ( path , " /cheats/%016llX.txt " , titleId ) ;
2019-04-16 00:14:53 +02:00
if ( R_FAILED ( BufferedFile_Open ( & file , ARCHIVE_SDMC , fsMakePath ( PATH_EMPTY , " " ) , fsMakePath ( PATH_ASCII , path ) , FS_OPEN_READ ) ) ) return ;
2018-01-13 11:51:28 +01:00
} ;
char line [ 1024 ] = { 0 } ;
Result res = 0 ;
CheatDescription * cheat = 0 ;
2019-04-16 00:14:53 +02:00
u32 cheatSize = 0 ;
2018-01-13 11:51:28 +01:00
do
{
2019-04-16 00:14:53 +02:00
res = Cheat_ReadLine ( & file , line , 1024 ) ;
2018-01-13 11:51:28 +01:00
if ( R_SUCCEEDED ( res ) )
{
2019-04-16 00:14:53 +02:00
char * strippedLine = stripWhitespace ( line ) ;
s32 lineLen = strnlen ( strippedLine , 1023 ) ;
2018-01-13 11:51:28 +01:00
if ( ! lineLen )
{
continue ;
}
2019-04-16 00:14:53 +02:00
if ( strippedLine [ 0 ] = = ' # ' )
2018-01-13 11:51:28 +01:00
{
continue ;
}
2019-04-16 00:14:53 +02:00
if ( Cheat_IsCodeLine ( strippedLine ) )
2018-01-13 11:51:28 +01:00
{
2019-04-16 00:14:53 +02:00
if ( cheatSize + sizeof ( u64 ) > = sizeof ( cheatBuffer ) )
{
cheatCount - - ;
break ;
}
2018-01-13 11:51:28 +01:00
if ( cheat )
{
2019-04-16 00:14:53 +02:00
u64 tmp = Cheat_GetCode ( strippedLine ) ;
2018-01-13 11:51:28 +01:00
Cheat_AddCode ( cheat , tmp ) ;
2019-04-16 00:14:53 +02:00
cheatSize + = sizeof ( u64 ) ;
2018-01-13 11:51:28 +01:00
if ( ( ( tmp > > 32 ) & 0xFFFFFFFF ) = = 0xDD000000 )
{
2019-04-15 08:12:14 +02:00
cheat - > hasKeyCode = 1 ;
2018-01-13 11:51:28 +01:00
}
}
}
else
{
2019-04-16 00:14:53 +02:00
if ( ! cheat | | cheat - > codesCount > 0 )
2018-01-13 11:51:28 +01:00
{
2019-04-16 00:14:53 +02:00
if ( cheatSize + sizeof ( CheatDescription ) > = sizeof ( cheatBuffer ) )
2018-01-13 11:51:28 +01:00
{
2019-04-16 00:14:53 +02:00
break ;
2018-01-13 11:51:28 +01:00
}
2019-04-16 00:14:53 +02:00
cheat = Cheat_AllocCheat ( ) ;
cheatSize + = sizeof ( CheatDescription ) ;
2018-01-13 11:51:28 +01:00
}
2018-05-24 00:55:38 +02:00
strncpy ( cheat - > name , line , 38 ) ;
cheat - > name [ 38 ] = ' \0 ' ;
2018-01-13 11:51:28 +01:00
}
}
2019-04-16 00:14:53 +02:00
} while ( R_SUCCEEDED ( res ) & & cheatSize < sizeof ( cheatBuffer ) ) ;
IFile_Close ( & file . file ) ;
2018-01-13 11:51:28 +01:00
if ( ( cheatCount > 0 ) & & ( cheats [ cheatCount - 1 ] - > codesCount = = 0 ) )
{
cheatCount - - ; // Remove last empty cheat
}
}
2017-12-21 18:14:04 +01:00
2018-01-13 11:51:28 +01:00
static u32 Cheat_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 ;
}
2017-12-21 18:14:04 +01:00
}
2018-08-09 14:21:16 +02:00
void Cheat_ApplyCheats ( void )
2018-01-13 11:51:28 +01:00
{
if ( ! cheatCount )
{
return ;
}
u64 titleId = 0 ;
u32 pid = Cheat_GetCurrentPID ( & titleId ) ;
if ( ! titleId )
{
cheatCount = 0 ;
return ;
}
if ( titleId ! = cheatTitleInfo )
{
cheatCount = 0 ;
return ;
}
for ( int i = 0 ; i < cheatCount ; i + + )
{
2019-04-15 08:12:14 +02:00
if ( cheats [ i ] - > active )
2018-01-13 11:51:28 +01:00
{
Cheat_MapMemoryAndApplyCheat ( pid , cheats [ i ] ) ;
2018-08-09 14:21:16 +02:00
}
2018-01-13 11:51:28 +01:00
}
2017-12-21 18:14:04 +01:00
}
2018-01-13 11:51:28 +01:00
void RosalinaMenu_Cheats ( void )
{
u64 titleId = 0 ;
u32 pid = Cheat_GetCurrentPID ( & titleId ) ;
if ( titleId ! = 0 )
{
if ( cheatTitleInfo ! = titleId | | cheatCount = = 0 )
{
Cheat_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 ;
Result r = 0 ;
do
{
Draw_Lock ( ) ;
if ( page ! = pagePrev | | R_FAILED ( r ) )
{
Draw_ClearFramebuffer ( ) ;
}
if ( R_SUCCEEDED ( r ) )
{
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 } ;
s32 j = page * CHEATS_PER_MENU_PAGE + i ;
const char * checkbox = ( cheats [ j ] - > active ? " (x) " : " ( ) " ) ;
2019-04-15 08:12:14 +02:00
const char * keyAct = ( cheats [ j ] - > hasKeyCode ? " * " : " " ) ;
2018-01-13 11:51:28 +01:00
sprintf ( buf , " %s%s%s " , checkbox , keyAct , cheats [ j ] - > name ) ;
Draw_DrawString ( 30 , 30 + i * SPACING_Y , cheats [ j ] - > valid ? COLOR_WHITE : COLOR_RED , buf ) ;
Draw_DrawCharacter ( 10 , 30 + i * SPACING_Y , COLOR_TITLE , j = = selected ? ' > ' : ' ' ) ;
}
}
else
{
2018-05-24 00:55:38 +02:00
Draw_DrawFormattedString ( 10 , 10 , COLOR_TITLE , " ERROR: %08lx " , r ) ;
2018-01-13 11:51:28 +01:00
Draw_DrawFormattedString ( 10 , 30 , COLOR_RED , failureReason ) ;
}
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 ) & & R_SUCCEEDED ( r ) )
{
if ( cheats [ selected ] - > active )
{
cheats [ selected ] - > active = 0 ;
}
else
{
r = Cheat_MapMemoryAndApplyCheat ( pid , cheats [ selected ] ) ;
}
}
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 ) ;
}
2017-12-21 18:14:04 +01:00
}