2017-12-22 12:46:46 +01:00
/*
* This file is part of Luma3DS
2020-04-25 14:26:21 +02:00
* Copyright ( C ) 2016 - 2020 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 .
*/
2017-12-21 18:14:04 +01:00
# include <3ds.h>
2019-06-03 00:50:27 +02:00
# include <stdlib.h>
2017-12-21 18:14:04 +01:00
# include "menus/cheats.h"
# include "memory.h"
# include "draw.h"
# include "menu.h"
# include "utils.h"
# include "fmt.h"
# include "ifile.h"
2020-04-26 12:27:55 +02:00
# include "pmdbgext.h"
2017-12-21 18:14:04 +01:00
# define MAKE_QWORD(hi,low) \
( ( u64 ) ( ( ( ( u64 ) ( hi ) ) < < 32 ) | ( low ) ) )
2018-01-13 11:51:28 +01:00
typedef struct CheatDescription
{
2019-06-03 00:50:27 +02:00
struct {
2019-06-24 21:42:32 +02:00
u8 active : 1 ;
u8 valid : 1 ;
2019-06-03 00:50:27 +02:00
u8 hasKeyCode : 1 ;
u8 activeStorage : 1 ;
} ;
2019-04-15 08:12:14 +02:00
char name [ 39 ] ;
2018-01-13 11:51:28 +01:00
u32 codesCount ;
2019-06-03 00:50:27 +02:00
u32 storage1 ;
u32 storage2 ;
2018-01-13 11:51:28 +01:00
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 } ;
2019-06-03 00:50:27 +02:00
u8 cheatPage [ 0x1000 ] = { 0 } ;
2017-12-22 10:17:45 +01:00
2018-01-13 11:51:28 +01:00
typedef struct CheatState
{
u32 index ;
2019-06-03 00:50:27 +02:00
u32 offset1 ;
u32 offset2 ;
u32 data1 ;
u32 data2 ;
struct {
u8 activeOffset : 1 ;
u8 activeData : 1 ;
u8 conditionalMode : 3 ;
u8 data1Mode : 1 ;
u8 data2Mode : 1 ;
u8 floatMode : 1 ;
} ;
2018-01-13 11:51:28 +01:00
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 ;
2020-04-26 12:47:15 +02:00
u64 cheatRngState = 0 ;
2017-12-21 18:14:04 +01:00
2019-06-03 00:50:27 +02:00
static inline u32 * activeOffset ( )
{
return cheat_state . activeOffset ? & cheat_state . offset2 : & cheat_state . offset1 ;
}
static inline u32 * activeData ( )
{
return cheat_state . activeData ? & cheat_state . data2 : & cheat_state . data1 ;
}
static inline u32 * activeStorage ( CheatDescription * desc )
{
return desc - > activeStorage ? & desc - > storage2 : & desc - > storage1 ;
}
2017-12-21 18:14:04 +01:00
char failureReason [ 64 ] ;
2020-04-26 12:47:15 +02:00
static u32 Cheat_GetRandomNumber ( void )
{
cheatRngState = 0x5D588B656C078965ULL * cheatRngState + 0x0000000000269EC3ULL ;
return ( u32 ) ( cheatRngState > > 32 ) ;
}
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
{
2019-06-03 00:50:27 +02:00
u32 addr = * activeOffset ( ) + offset ;
2019-08-12 06:58:16 +02:00
if ( addr > = 0x01E81000 & & addr < 0x01E82000 )
2019-06-03 00:50:27 +02:00
{
cheatPage [ addr - 0x01E81000 ] = value ;
return true ;
}
if ( Cheat_IsValidAddress ( processHandle , addr , 1 ) )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
* ( ( u8 * ) ( & ReadWriteBuffer8 ) ) = value ;
2019-06-03 00:50:27 +02:00
return R_SUCCEEDED ( svcWriteProcessMemory ( processHandle , & ReadWriteBuffer8 , addr , 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
{
2019-06-03 00:50:27 +02:00
u32 addr = * activeOffset ( ) + offset ;
2019-08-12 06:58:16 +02:00
if ( addr > = 0x01E81000 & & addr + 1 < 0x01E82000 )
2019-06-03 00:50:27 +02:00
{
* ( u16 * ) ( cheatPage + addr - 0x01E81000 ) = value ;
return true ;
}
if ( Cheat_IsValidAddress ( processHandle , addr , 2 ) )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
* ( ( u16 * ) ( & ReadWriteBuffer16 ) ) = value ;
2019-06-03 00:50:27 +02:00
return R_SUCCEEDED ( svcWriteProcessMemory ( processHandle , & ReadWriteBuffer16 , addr , 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
{
2019-06-03 00:50:27 +02:00
u32 addr = * activeOffset ( ) + offset ;
2019-08-12 06:58:16 +02:00
if ( addr > = 0x01E81000 & & addr + 3 < 0x01E82000 )
2019-06-03 00:50:27 +02:00
{
* ( u32 * ) ( cheatPage + addr - 0x01E81000 ) = value ;
return true ;
}
if ( Cheat_IsValidAddress ( processHandle , addr , 4 ) )
2018-01-13 11:51:28 +01:00
{
2018-03-20 01:11:49 +01:00
* ( ( u32 * ) ( & ReadWriteBuffer32 ) ) = value ;
2019-06-03 00:50:27 +02:00
return R_SUCCEEDED ( svcWriteProcessMemory ( processHandle , & ReadWriteBuffer32 , addr , 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
{
2019-06-03 00:50:27 +02:00
u32 addr = * activeOffset ( ) + offset ;
2019-08-12 06:58:16 +02:00
if ( addr > = 0x01E81000 & & addr < 0x01E82000 )
2019-06-03 00:50:27 +02:00
{
* retValue = cheatPage [ addr - 0x01E81000 ] ;
return true ;
}
if ( Cheat_IsValidAddress ( processHandle , addr , 1 ) )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
Result res = svcReadProcessMemory ( & ReadWriteBuffer8 , processHandle , addr , 1 ) ;
2018-03-20 01:11:49 +01:00
* 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
{
2019-06-03 00:50:27 +02:00
u32 addr = * activeOffset ( ) + offset ;
2019-08-12 06:58:16 +02:00
if ( addr > = 0x01E81000 & & addr + 1 < 0x01E82000 )
2019-06-03 00:50:27 +02:00
{
* retValue = * ( u16 * ) ( cheatPage + addr - 0x01E81000 ) ;
return true ;
}
if ( Cheat_IsValidAddress ( processHandle , addr , 2 ) )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
Result res = svcReadProcessMemory ( & ReadWriteBuffer16 , processHandle , addr , 2 ) ;
2018-03-20 01:11:49 +01:00
* 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
{
2019-06-03 00:50:27 +02:00
u32 addr = * activeOffset ( ) + offset ;
2019-08-12 06:58:16 +02:00
if ( addr > = 0x01E81000 & & addr + 3 < 0x01E82000 )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
* retValue = * ( u32 * ) ( cheatPage + addr - 0x01E81000 ) ;
return true ;
}
if ( Cheat_IsValidAddress ( processHandle , addr , 4 ) )
{
Result res = svcReadProcessMemory ( & ReadWriteBuffer32 , processHandle , addr , 4 ) ;
2018-03-20 01:11:49 +01:00
* 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
}
2019-06-03 00:50:27 +02:00
static u32 Cheat_ApplyCheat ( const Handle processHandle , CheatDescription * const cheat )
2018-01-13 11:51:28 +01:00
{
cheat_state . index = 0 ;
2019-06-03 00:50:27 +02:00
cheat_state . offset1 = 0 ;
cheat_state . offset2 = 0 ;
cheat_state . data1 = 0 ;
cheat_state . data2 = 0 ;
cheat_state . activeOffset = 0 ;
cheat_state . activeData = 0 ;
cheat_state . conditionalMode = 0 ;
cheat_state . data1Mode = 0 ;
cheat_state . data2Mode = 0 ;
cheat_state . floatMode = 0 ;
2018-01-13 11:51:28 +01:00
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 )
{
2019-06-03 00:58:07 +02:00
bool skipExecution = ( cheat_state . ifStack & 0x00000001 ) ! = 0 ;
2018-01-13 11:51:28 +01:00
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 ) ;
2019-06-03 00:50:27 +02:00
u32 codeArg = arg0 & 0x0F ;
2018-01-13 11:51:28 +01:00
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 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x0FFFFFFF , & value ) ) return 0 ;
2019-06-03 00:50:27 +02:00
cheat_state . ifStack < < = 1 ;
2018-01-13 11:51:28 +01:00
if ( value < arg1 )
{
2019-06-03 00:50:27 +02:00
cheat_state . ifStack | = skipExecution ? 1 : 0 ;
2018-01-13 11:51:28 +01:00
}
else
{
2019-06-03 00:50:27 +02:00
cheat_state . ifStack | = 1 ;
2018-01-13 11:51:28 +01:00
}
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 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x0FFFFFFF , & value ) ) return 0 ;
2019-06-03 00:50:27 +02:00
cheat_state . ifStack < < = 1 ;
2018-01-13 11:51:28 +01:00
if ( value > arg1 )
{
2019-06-03 00:50:27 +02:00
cheat_state . ifStack | = skipExecution ? 1 : 0 ;
2018-01-13 11:51:28 +01:00
}
else
{
2019-06-03 00:50:27 +02:00
cheat_state . ifStack | = 1 ;
2018-01-13 11:51:28 +01:00
}
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 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x0FFFFFFF , & value ) ) return 0 ;
2019-06-03 00:50:27 +02:00
cheat_state . ifStack < < = 1 ;
2018-01-13 11:51:28 +01:00
if ( value = = arg1 )
{
2019-06-03 00:50:27 +02:00
cheat_state . ifStack | = skipExecution ? 1 : 0 ;
2018-01-13 11:51:28 +01:00
}
else
{
2019-06-03 00:50:27 +02:00
cheat_state . ifStack | = 1 ;
2018-01-13 11:51:28 +01:00
}
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 value = 0 ;
2018-03-20 01:11:49 +01:00
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x0FFFFFFF , & value ) ) return 0 ;
2019-06-03 00:50:27 +02:00
cheat_state . ifStack < < = 1 ;
2018-01-13 11:51:28 +01:00
if ( value ! = arg1 )
{
2019-06-03 00:50:27 +02:00
cheat_state . ifStack | = skipExecution ? 1 : 0 ;
2018-01-13 11:51:28 +01:00
}
else
{
2019-06-03 00:50:27 +02:00
cheat_state . ifStack | = 1 ;
2018-01-13 11:51:28 +01:00
}
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
{
2019-06-03 00:50:27 +02:00
bool newSkip ;
2018-01-13 11:51:28 +01:00
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 ;
2019-06-03 00:50:27 +02:00
switch ( cheat_state . conditionalMode )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
case 0x0 :
newSkip = ! ( ( value & ( ~ mask ) ) < ( arg1 & 0xFFFF ) ) ;
break ;
case 0x1 :
newSkip = ! ( ( value & ( ~ mask ) ) < ( * activeData ( ) & ( ~ mask ) ) ) ;
break ;
case 0x2 :
newSkip = ! ( ( * activeData ( ) & ( ~ mask ) ) < ( arg1 & 0xFFFF ) ) ;
break ;
case 0x3 :
newSkip = ! ( ( * activeStorage ( cheat ) & ( ~ mask ) ) < ( arg1 & 0xFFFF ) ) ;
break ;
case 0x4 :
newSkip = ! ( ( * activeData ( ) & ( ~ mask ) ) < ( * activeStorage ( cheat ) & ( ~ mask ) ) ) ;
break ;
default :
return 0 ;
2018-01-13 11:51:28 +01:00
}
cheat_state . ifStack < < = 1 ;
2019-06-03 00:50:27 +02:00
cheat_state . ifStack | = ( newSkip | | skipExecution ) ? 1 : 0 ;
2018-01-13 11:51:28 +01:00
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
{
2019-06-03 00:50:27 +02:00
bool newSkip ;
2018-01-13 11:51:28 +01:00
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 ;
2019-06-03 00:50:27 +02:00
switch ( cheat_state . conditionalMode )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
case 0x0 :
newSkip = ! ( ( value & ( ~ mask ) ) > ( arg1 & 0xFFFF ) ) ;
break ;
case 0x1 :
newSkip = ! ( ( value & ( ~ mask ) ) > ( * activeData ( ) & ( ~ mask ) ) ) ;
break ;
case 0x2 :
newSkip = ! ( ( * activeData ( ) & ( ~ mask ) ) > ( arg1 & 0xFFFF ) ) ;
break ;
case 0x3 :
newSkip = ! ( ( * activeStorage ( cheat ) & ( ~ mask ) ) > ( arg1 & 0xFFFF ) ) ;
break ;
case 0x4 :
newSkip = ! ( ( * activeData ( ) & ( ~ mask ) ) > ( * activeStorage ( cheat ) & ( ~ mask ) ) ) ;
break ;
default :
return 0 ;
2018-01-13 11:51:28 +01:00
}
cheat_state . ifStack < < = 1 ;
2019-06-03 00:50:27 +02:00
cheat_state . ifStack | = ( newSkip | | skipExecution ) ? 1 : 0 ;
2018-01-13 11:51:28 +01:00
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
{
2019-06-03 00:50:27 +02:00
bool newSkip ;
2018-01-13 11:51:28 +01:00
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 ;
2019-06-03 00:50:27 +02:00
switch ( cheat_state . conditionalMode )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
case 0x0 :
newSkip = ! ( ( value & ( ~ mask ) ) = = ( arg1 & 0xFFFF ) ) ;
break ;
case 0x1 :
newSkip = ! ( ( value & ( ~ mask ) ) = = ( * activeData ( ) & ( ~ mask ) ) ) ;
break ;
case 0x2 :
newSkip = ! ( ( * activeData ( ) & ( ~ mask ) ) = = ( arg1 & 0xFFFF ) ) ;
break ;
case 0x3 :
newSkip = ! ( ( * activeStorage ( cheat ) & ( ~ mask ) ) = = ( arg1 & 0xFFFF ) ) ;
break ;
case 0x4 :
newSkip = ! ( ( * activeData ( ) & ( ~ mask ) ) = = ( * activeStorage ( cheat ) & ( ~ mask ) ) ) ;
break ;
default :
return 0 ;
2018-01-13 11:51:28 +01:00
}
cheat_state . ifStack < < = 1 ;
2019-06-03 00:50:27 +02:00
cheat_state . ifStack | = ( newSkip | | skipExecution ) ? 1 : 0 ;
2018-01-13 11:51:28 +01:00
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
{
2019-06-03 00:50:27 +02:00
bool newSkip ;
2018-01-13 11:51:28 +01:00
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 ;
2019-06-03 00:50:27 +02:00
switch ( cheat_state . conditionalMode )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
case 0x0 :
newSkip = ! ( ( value & ( ~ mask ) ) ! = ( arg1 & 0xFFFF ) ) ;
break ;
case 0x1 :
newSkip = ! ( ( value & ( ~ mask ) ) ! = ( * activeData ( ) & ( ~ mask ) ) ) ;
break ;
case 0x2 :
newSkip = ! ( ( * activeData ( ) & ( ~ mask ) ) ! = ( arg1 & 0xFFFF ) ) ;
break ;
case 0x3 :
newSkip = ! ( ( * activeStorage ( cheat ) & ( ~ mask ) ) ! = ( arg1 & 0xFFFF ) ) ;
break ;
case 0x4 :
newSkip = ! ( ( * activeData ( ) & ( ~ mask ) ) ! = ( * activeStorage ( cheat ) & ( ~ mask ) ) ) ;
break ;
default :
return 0 ;
2018-01-13 11:51:28 +01:00
}
cheat_state . ifStack < < = 1 ;
2019-06-03 00:50:27 +02:00
cheat_state . ifStack | = ( newSkip | | skipExecution ) ? 1 : 0 ;
2018-01-13 11:51:28 +01:00
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 ;
2019-06-03 00:50:27 +02:00
* activeOffset ( ) = 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
2019-06-03 00:50:27 +02:00
switch ( subcode )
{
case 0x00 :
cheat_state . loopLine = cheat_state . index ;
cheat_state . loopCount = arg1 ;
cheat_state . storedStack = cheat_state . ifStack ;
cheat_state . storedIfCount = cheat_state . ifCount ;
break ;
case 0x01 :
cheat_state . loopLine = cheat_state . index ;
cheat_state . loopCount = cheat_state . data1 ;
cheat_state . storedStack = cheat_state . ifStack ;
cheat_state . storedIfCount = cheat_state . ifCount ;
break ;
case 0x02 :
cheat_state . loopLine = cheat_state . index ;
cheat_state . loopCount = cheat_state . data2 ;
cheat_state . storedStack = cheat_state . ifStack ;
cheat_state . storedIfCount = cheat_state . ifCount ;
break ;
}
2018-01-13 11:51:28 +01:00
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.
2019-06-03 00:50:27 +02:00
if ( arg1 = = 0 )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
if ( cheat_state . loopLine ! = - 1 )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
if ( cheat_state . ifCount > 0 & & cheat_state . ifCount > cheat_state . storedIfCount )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
cheat_state . ifStack > > = 1 ;
cheat_state . ifCount - - ;
}
else
{
if ( cheat_state . loopCount > 0 )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
cheat_state . loopCount - - ;
if ( cheat_state . loopCount = = 0 )
{
cheat_state . loopLine = - 1 ;
}
else if ( cheat_state . loopLine ! = - 1 )
2018-01-13 11:51:28 +01:00
{
cheat_state . index = cheat_state . loopLine ;
}
}
}
}
2019-06-03 00:50:27 +02:00
else
{
if ( cheat_state . ifCount > 0 )
{
cheat_state . ifStack > > = 1 ;
cheat_state . ifCount - - ;
}
}
2018-01-13 11:51:28 +01:00
}
2019-06-03 00:50:27 +02:00
// D0000000 00000001
// Loop break
else if ( ! skipExecution & & arg1 = = 1 )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
cheat_state . loopCount = 0 ;
cheat_state . loopLine = - 1 ;
cheat_state . index + + ;
while ( cheat_state . index < cheat - > codesCount )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
u64 code = cheat - > codes [ cheat_state . index + + ] ;
2019-06-03 00:58:07 +02:00
if ( code = = 0xD100000000000000ull | | code = = 0xD200000000000000ull )
2019-06-03 00:50:27 +02:00
{
break ;
}
2018-01-13 11:51:28 +01:00
}
}
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.
2019-06-03 00:50:27 +02:00
if ( arg1 = = 0 )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
if ( cheat_state . loopCount > 0 )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
cheat_state . loopCount - - ;
if ( cheat_state . loopCount = = 0 )
{
* activeData ( ) = 0 ;
* activeOffset ( ) = 0 ;
cheat_state . loopLine = - 1 ;
2018-01-13 11:51:28 +01:00
2019-06-03 00:50:27 +02:00
cheat_state . ifStack = 0 ;
cheat_state . ifCount = 0 ;
}
else
{
if ( cheat_state . loopLine ! = - 1 )
{
cheat_state . index = cheat_state . loopLine ;
}
}
2018-01-13 11:51:28 +01:00
}
else
{
2019-06-03 00:50:27 +02:00
* activeData ( ) = 0 ;
* activeOffset ( ) = 0 ;
cheat_state . ifStack = 0 ;
cheat_state . ifCount = 0 ;
2018-01-13 11:51:28 +01:00
}
}
2019-06-03 00:50:27 +02:00
// D2000000 00000001
// Return
else if ( ! skipExecution & & arg1 = = 1 )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
cheat_state . index = cheat - > codesCount ;
2018-01-13 11:51:28 +01:00
}
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 )
{
2019-06-03 00:50:27 +02:00
if ( codeArg = = 0 )
{
cheat_state . offset1 = arg1 ;
}
else if ( codeArg = = 1 )
{
cheat_state . offset2 = arg1 ;
}
2018-01-13 11:51:28 +01:00
}
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 )
{
2019-06-03 00:50:27 +02:00
if ( codeArg = = 0 )
{
* activeData ( ) + = arg1 ;
}
else if ( codeArg = = 1 )
{
cheat_state . data1 + = arg1 + cheat_state . data2 ;
}
else if ( codeArg = = 2 )
{
cheat_state . data2 + = arg1 + cheat_state . data1 ;
}
2018-01-13 11:51:28 +01:00
}
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 )
{
2019-06-03 00:50:27 +02:00
if ( codeArg = = 0 )
{
* activeData ( ) = arg1 ;
}
else if ( codeArg = = 1 )
{
cheat_state . data1 = arg1 ;
}
else if ( codeArg = = 2 )
{
cheat_state . data2 = arg1 ;
}
2018-01-13 11:51:28 +01:00
}
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 )
{
2019-06-03 00:50:27 +02:00
if ( codeArg = = 0 )
{
if ( ! Cheat_Write32 ( processHandle , arg1 , * activeData ( ) ) ) return 0 ;
* activeOffset ( ) + = 4 ;
}
else if ( codeArg = = 1 )
{
if ( ! Cheat_Write32 ( processHandle , arg1 , cheat_state . data1 ) ) return 0 ;
* activeOffset ( ) + = 4 ;
}
else if ( codeArg = = 2 )
{
if ( ! Cheat_Write32 ( processHandle , arg1 , cheat_state . data2 ) ) return 0 ;
* activeOffset ( ) + = 4 ;
}
2018-01-13 11:51:28 +01:00
}
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 )
{
2019-06-03 00:50:27 +02:00
if ( codeArg = = 0 )
{
if ( ! Cheat_Write16 ( processHandle , arg1 , ( u16 ) ( * activeData ( ) & 0xFFFF ) ) ) return 0 ;
* activeOffset ( ) + = 2 ;
}
else if ( codeArg = = 1 )
{
if ( ! Cheat_Write16 ( processHandle , arg1 , ( u16 ) ( cheat_state . data1 & 0xFFFF ) ) ) return 0 ;
* activeOffset ( ) + = 2 ;
}
else if ( codeArg = = 2 )
{
if ( ! Cheat_Write16 ( processHandle , arg1 , ( u16 ) ( cheat_state . data2 & 0xFFFF ) ) ) return 0 ;
* activeOffset ( ) + = 2 ;
}
2018-01-13 11:51:28 +01:00
}
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 )
{
2019-06-03 00:50:27 +02:00
if ( codeArg = = 0 )
{
if ( ! Cheat_Write8 ( processHandle , arg1 , ( u8 ) ( * activeData ( ) & 0xFF ) ) ) return 0 ;
* activeOffset ( ) + = 1 ;
}
else if ( codeArg = = 1 )
{
if ( ! Cheat_Write8 ( processHandle , arg1 , ( u8 ) ( cheat_state . data1 & 0xFF ) ) ) return 0 ;
* activeOffset ( ) + = 1 ;
}
else if ( codeArg = = 2 )
{
if ( ! Cheat_Write8 ( processHandle , arg1 , ( u8 ) ( cheat_state . data2 & 0xFF ) ) ) return 0 ;
* activeOffset ( ) + = 1 ;
}
2018-01-13 11:51:28 +01:00
}
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 )
{
2019-06-03 00:50:27 +02:00
if ( codeArg = = 0 )
{
u32 value = 0 ;
if ( ! Cheat_Read32 ( processHandle , arg1 , & value ) ) return 0 ;
* activeData ( ) = value ;
}
else if ( codeArg = = 1 )
{
u32 value = 0 ;
if ( ! Cheat_Read32 ( processHandle , arg1 , & value ) ) return 0 ;
cheat_state . data1 = value ;
}
else if ( codeArg = = 2 )
{
u32 value = 0 ;
if ( ! Cheat_Read32 ( processHandle , arg1 , & value ) ) return 0 ;
cheat_state . data2 = value ;
}
2018-01-13 11:51:28 +01:00
}
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 )
{
2019-06-03 00:50:27 +02:00
if ( codeArg = = 0 )
{
u16 value = 0 ;
if ( ! Cheat_Read16 ( processHandle , arg1 , & value ) ) return 0 ;
* activeData ( ) = value ;
}
else if ( codeArg = = 1 )
{
u16 value = 0 ;
if ( ! Cheat_Read16 ( processHandle , arg1 , & value ) ) return 0 ;
cheat_state . data1 = value ;
}
else if ( codeArg = = 2 )
{
u16 value = 0 ;
if ( ! Cheat_Read16 ( processHandle , arg1 , & value ) ) return 0 ;
cheat_state . data2 = value ;
}
2018-01-13 11:51:28 +01:00
}
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 )
{
2019-06-03 00:50:27 +02:00
if ( codeArg = = 0 )
{
u8 value = 0 ;
if ( ! Cheat_Read8 ( processHandle , arg1 , & value ) ) return 0 ;
* activeData ( ) = value ;
}
else if ( codeArg = = 1 )
{
u8 value = 0 ;
if ( ! Cheat_Read8 ( processHandle , arg1 , & value ) ) return 0 ;
cheat_state . data1 = value ;
}
else if ( codeArg = = 2 )
{
u8 value = 0 ;
if ( ! Cheat_Read8 ( processHandle , arg1 , & value ) ) return 0 ;
cheat_state . data2 = value ;
}
2018-01-13 11:51:28 +01:00
}
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 )
{
2019-06-03 00:50:27 +02:00
* activeOffset ( ) + = arg1 ;
2018-01-13 11:51:28 +01:00
}
break ;
case 0x0D :
// DD Type
{
2019-06-03 00:50:27 +02:00
bool newSkip = ! ( arg1 = = 0 | | ( HID_PAD & arg1 ) = = arg1 ) ;
cheat_state . ifStack < < = 1 ;
cheat_state . ifStack | = ( newSkip | | skipExecution ) ? 1 : 0 ; ;
cheat_state . ifCount + + ;
}
break ;
case 0x0E :
// Touchpad conditional
// DE000000 AAAABBBB: AAAA >= X position >= BBBB
// DE000001 AAAABBBB: AAAA >= Y position >= BBBB
{
bool newSkip ;
u32 highBound = arg1 > > 16 ;
u32 lowBound = arg1 & 0xFFFF ;
touchPosition touch ;
hidTouchRead ( & touch ) ;
if ( codeArg = = 0 )
2018-01-13 11:51:28 +01:00
{
2019-06-03 00:50:27 +02:00
newSkip = ! ( lowBound < = touch . px & & highBound > = touch . px ) ;
}
else if ( codeArg = = 1 )
{
newSkip = ! ( lowBound < = touch . py & & highBound > = touch . py ) ;
2018-01-13 11:51:28 +01:00
}
else
{
2019-06-03 00:50:27 +02:00
return 0 ;
2018-01-13 11:51:28 +01:00
}
cheat_state . ifStack < < = 1 ;
2019-06-03 00:50:27 +02:00
cheat_state . ifStack | = ( newSkip | | skipExecution ) ? 1 : 0 ;
2018-01-13 11:51:28 +01:00
cheat_state . ifCount + + ;
}
break ;
2019-06-03 00:50:27 +02:00
case 0x0F :
{
switch ( codeArg )
{
case 0x00 :
{
if ( arg1 & 0x00010000 )
{
if ( arg1 & 0x1 )
{
cheat_state . offset2 = cheat_state . offset1 ;
}
else
{
cheat_state . offset1 = cheat_state . offset2 ;
}
}
else if ( arg1 & 0x00020000 )
{
if ( arg1 & 0x1 )
{
cheat_state . data2 = cheat_state . offset2 ;
}
else
{
cheat_state . data1 = cheat_state . offset1 ;
}
}
else
{
cheat_state . activeOffset = arg1 & 0x1 ;
}
}
break ;
case 0x01 :
{
if ( arg1 & 0x00010000 )
{
if ( arg1 & 0x1 )
{
cheat_state . data2 = cheat_state . data1 ;
}
else
{
cheat_state . data1 = cheat_state . data2 ;
}
}
else if ( arg1 & 0x00020000 )
{
if ( arg1 & 0x1 )
{
cheat_state . offset2 = cheat_state . data2 ;
}
else
{
cheat_state . offset1 = cheat_state . data1 ;
}
}
else
{
cheat_state . activeData = arg1 & 0x1 ;
}
}
break ;
case 0x02 :
{
if ( arg1 & 0x00010000 )
{
if ( arg1 & 0x1 )
{
cheat_state . data2 = cheat - > storage2 ;
}
else
{
cheat_state . data1 = cheat - > storage1 ;
}
}
else if ( arg1 & 0x00020000 )
{
if ( arg1 & 0x1 )
{
cheat - > storage2 = cheat_state . data2 ;
}
else
{
cheat - > storage1 = cheat_state . data1 ;
}
}
else
{
cheat - > activeStorage = arg1 & 0x1 ;
}
}
break ;
case 0x0E :
{
if ( cheat_state . activeData )
{
switch ( arg1 )
{
case 0x0 :
{
cheat_state . data2Mode = 0 ;
}
break ;
case 0x1 :
{
cheat_state . data2Mode = 1 ;
}
break ;
case 0x10 :
{
cheat_state . data2Mode = 0 ;
float val ;
memcpy ( & val , & cheat_state . data2 , sizeof ( float ) ) ;
cheat_state . data2 = val ;
}
break ;
case 0x11 :
{
cheat_state . data2Mode = 1 ;
float val = cheat_state . data2 ;
memcpy ( & cheat_state . data2 , & val , sizeof ( float ) ) ;
}
break ;
default :
return 0 ;
}
}
else
{
switch ( arg1 )
{
case 0x0 :
{
cheat_state . data1Mode = 0 ;
}
break ;
case 0x1 :
{
cheat_state . data1Mode = 1 ;
}
break ;
case 0x10 :
{
cheat_state . data1Mode = 0 ;
float val ;
memcpy ( & val , & cheat_state . data1 , sizeof ( float ) ) ;
cheat_state . data1 = val ;
}
break ;
case 0x11 :
{
cheat_state . data1Mode = 1 ;
float val = cheat_state . data1 ;
memcpy ( & cheat_state . data1 , & val , sizeof ( float ) ) ;
}
break ;
default :
return 0 ;
}
}
}
break ;
case 0x0F :
{
if ( arg1 < 5 )
{
cheat_state . conditionalMode = ( u8 ) arg1 ;
}
else
{
return 0 ;
}
}
break ;
default :
return 0 ;
}
}
break ;
2018-01-13 11:51:28 +01:00
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 ;
2019-06-03 00:50:27 +02:00
case 0xF :
{
if ( arg0 = = 0xF0F00000 )
{
// I have no clue how to implement this, or if it's even possible. Needs research.
return 0 ;
}
else
{
switch ( subcode )
{
case 0x0 :
{
cheat_state . floatMode = arg1 & 0x1 ;
}
break ;
case 0x1 :
{
if ( cheat_state . floatMode )
{
u32 tmp ;
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x00FFFFFF , & tmp ) )
{
return 0 ;
}
float value ;
memcpy ( & value , & tmp , sizeof ( float ) ) ;
value + = arg1 ;
memcpy ( & tmp , & value , sizeof ( u32 ) ) ;
if ( ! Cheat_Write32 ( processHandle , arg0 & 0x00FFFFFF , tmp ) )
{
return 0 ;
}
}
else
{
u32 tmp ;
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x00FFFFFF , & tmp ) )
{
return 0 ;
}
tmp + = arg1 ;
if ( ! Cheat_Write32 ( processHandle , arg0 & 0x00FFFFFF , tmp ) )
{
return 0 ;
}
}
}
break ;
case 0x2 :
{
if ( cheat_state . floatMode )
{
u32 tmp ;
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x00FFFFFF , & tmp ) )
{
return 0 ;
}
float value ;
memcpy ( & value , & tmp , sizeof ( float ) ) ;
value * = arg1 ;
memcpy ( & tmp , & value , sizeof ( u32 ) ) ;
if ( ! Cheat_Write32 ( processHandle , arg0 & 0x00FFFFFF , tmp ) )
{
return 0 ;
}
}
else
{
u32 tmp ;
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x00FFFFFF , & tmp ) )
{
return 0 ;
}
tmp * = arg1 ;
if ( ! Cheat_Write32 ( processHandle , arg0 & 0x00FFFFFF , tmp ) )
{
return 0 ;
}
}
}
break ;
case 0x3 :
{
if ( cheat_state . floatMode )
{
u32 tmp ;
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x00FFFFFF , & tmp ) )
{
return 0 ;
}
float value ;
memcpy ( & value , & tmp , sizeof ( float ) ) ;
value / = arg1 ;
memcpy ( & tmp , & value , sizeof ( u32 ) ) ;
if ( ! Cheat_Write32 ( processHandle , arg0 & 0x00FFFFFF , tmp ) )
{
return 0 ;
}
}
else
{
u32 tmp ;
if ( ! Cheat_Read32 ( processHandle , arg0 & 0x00FFFFFF , & tmp ) )
{
return 0 ;
}
tmp / = arg1 ;
if ( ! Cheat_Write32 ( processHandle , arg0 & 0x00FFFFFF , tmp ) )
{
return 0 ;
}
}
}
break ;
case 0x4 :
{
if ( cheat_state . data1Mode )
{
float value ;
memcpy ( & value , activeData ( ) , sizeof ( float ) ) ;
value * = arg1 ;
memcpy ( activeData ( ) , & value , sizeof ( float ) ) ;
}
else
{
* activeData ( ) * = arg1 ;
}
}
break ;
case 0x5 :
{
if ( cheat_state . data1Mode )
{
float value ;
memcpy ( & value , activeData ( ) , sizeof ( float ) ) ;
value / = arg1 ;
memcpy ( activeData ( ) , & value , sizeof ( float ) ) ;
}
else
{
* activeData ( ) / = arg1 ;
}
}
break ;
case 0x6 :
{
* activeData ( ) & = arg1 ;
}
break ;
case 0x7 :
{
* activeData ( ) | = arg1 ;
}
break ;
case 0x8 :
{
* activeData ( ) ^ = arg1 ;
}
break ;
case 0x9 :
{
* activeData ( ) = ~ * activeData ( ) ;
}
break ;
case 0xA :
{
* activeData ( ) < < = arg1 ;
}
break ;
case 0xB :
{
* activeData ( ) > > = arg1 ;
}
break ;
case 0xC :
{
u8 origActiveOffset = cheat_state . activeOffset ;
for ( size_t i = 0 ; i < arg1 ; i + + )
{
u8 data ;
cheat_state . activeOffset = 1 ;
if ( ! Cheat_Read8 ( processHandle , 0 , & data ) )
{
return 0 ;
}
cheat_state . activeOffset = 0 ;
if ( ! Cheat_Write8 ( processHandle , 0 , data ) )
{
return 0 ;
}
}
cheat_state . activeOffset = origActiveOffset ;
}
break ;
// Search for pattern
case 0xE :
{
u32 searchSize = arg0 & 0xFFFF ;
if ( searchSize < = arg1 & & searchSize + cheat_state . index < cheat - > codesCount )
{
bool newSkip = true ;
if ( ! skipExecution ) // Don't do an expensive operation if we don't have to
{
u8 * searchData = ( u8 * ) ( cheat - > codes + cheat_state . index + 1 ) ;
cheat_state . index + = searchSize / 8 ;
if ( searchSize & 0x7 )
{
cheat_state . index + + ;
}
for ( size_t i = 0 ; i < arg1 - searchSize ; i + + )
{
u8 curVal ;
newSkip = false ;
for ( size_t j = 0 ; j < searchSize ; j + + )
{
if ( ! Cheat_Read8 ( processHandle , i + j , & curVal ) )
{
return 0 ;
}
if ( curVal ! = searchData [ j ] )
{
newSkip = 1 ;
break ;
}
}
if ( ! newSkip )
{
break ;
}
}
}
cheat_state . ifStack < < = 1 ;
cheat_state . ifStack | = ( newSkip | | skipExecution ) ? 1 : 0 ;
cheat_state . ifCount + + ;
}
else
{
return 0 ;
}
}
break ;
case 0xF :
{
u32 range = arg1 - ( arg0 & 0xFFFFFF ) ;
2020-04-26 12:47:15 +02:00
u32 number = Cheat_GetRandomNumber ( ) % range ;
2019-06-03 00:50:27 +02:00
* activeData ( ) = ( arg0 & 0xFFFFFF ) + number ;
}
break ;
default :
return 0 ;
}
}
}
break ;
// This should now not be possible
2018-01-13 11:51:28 +01:00
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 ;
2019-06-03 00:50:27 +02:00
cheat - > storage1 = 0 ;
cheat - > storage2 = 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
}
2019-06-03 00:50:27 +02:00
memset ( cheatPage , 0 , 0x1000 ) ;
2018-01-13 11:51:28 +01:00
}
2017-12-21 18:14:04 +01:00
2020-04-27 18:59:51 +02:00
static u32 Cheat_GetCurrentProcessAndTitleId ( u64 * titleId )
2018-01-13 11:51:28 +01:00
{
2020-04-26 12:27:55 +02:00
u32 pid ;
Result res = PMDBG_GetCurrentAppTitleIdAndPid ( titleId , & pid ) ;
if ( R_FAILED ( res ) ) {
2018-01-13 11:51:28 +01:00
* titleId = 0 ;
2020-04-27 18:59:51 +02:00
return 0xFFFFFFFF ;
2018-01-13 11:51:28 +01:00
}
2020-04-27 18:59:51 +02:00
return pid ;
2017-12-21 18:14:04 +01:00
}
2020-04-26 12:47:15 +02:00
void Cheat_SeedRng ( u64 seed )
{
cheatRngState = seed ;
}
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 ;
2020-04-27 18:59:51 +02:00
u32 pid = Cheat_GetCurrentProcessAndTitleId ( & titleId ) ;
2018-01-13 11:51:28 +01:00
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 ;
2020-04-27 18:59:51 +02:00
u32 pid = Cheat_GetCurrentProcessAndTitleId ( & titleId ) ;
2018-01-13 11:51:28 +01:00
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
}