pcsx2/pcsx2/Patch.cpp
Jake.Stine fe6f45e45e Updated the Copyright to reflect the passing of another year. :)
Updated the pcsx2_suite_2008.sln; changed SPU2ghz to SPU2-X.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@502 96395faa-99c1-11dd-bbfe-3dabce05a288
2009-02-15 23:23:46 +00:00

691 lines
16 KiB
C++

/* Pcsx2 - Pc Ps2 Emulator
* Copyright (C) 2002-2009 Pcsx2 Team
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
//
// Includes
//
#include "PrecompiledHeader.h"
#include "PsxCommon.h"
#include "Paths.h"
#include "Patch.h"
#include "VU.h"
#ifdef _WIN32
#include "windows/cheats/cheats.h"
#else
#include <stdio.h>
#include <ctype.h>
#endif
#ifdef _MSC_VER
#pragma warning(disable:4996) //ignore the stricmp deprecated warning
#endif
IniPatch patch[ MAX_PATCH ];
u32 SkipCount=0, IterationCount=0;
u32 IterationIncrement=0, ValueIncrement=0;
u32 PrevCheatType=0, PrevCheataddr = 0,LastType = 0;
int g_ZeroGSOptions=0, patchnumber;
char strgametitle[256]= {0};
//
// Variables
//
PatchTextTable commands[] =
{
{ "comment", 1, patchFunc_comment },
{ "gametitle", 2, patchFunc_gametitle },
{ "patch", 3, patchFunc_patch },
{ "fastmemory", 4, patchFunc_fastmemory }, // enable for faster but bugger mem (mvc2 is faster)
{ "roundmode", 5, patchFunc_roundmode }, // changes rounding mode for floating point
// syntax: roundmode=X,Y
// possible values for X,Y: NEAR, DOWN, UP, CHOP
// X - EE rounding mode (default is NEAR)
// Y - VU rounding mode (default is CHOP)
{ "zerogs", 6, patchFunc_zerogs }, // zerogs=hex
{ "path3hack", 7, patchFunc_path3hack },
{ "vunanmode",8, patchFunc_vunanmode },
{ "ffxhack",9, patchFunc_ffxhack},
{ "xkickdelay",10, patchFunc_xkickdelay},
{ "", 0, NULL }
};
PatchTextTable dataType[] =
{
{ "byte", 1, NULL },
{ "short", 2, NULL },
{ "word", 3, NULL },
{ "double", 4, NULL },
{ "extended", 5, NULL },
{ "", 0, NULL }
};
PatchTextTable cpuCore[] =
{
{ "EE", 1, NULL },
{ "IOP", 2, NULL },
{ "", 0, NULL }
};
//
// Function Implementations
//
int PatchTableExecute( char * text1, char * text2, PatchTextTable * Table )
{
int i = 0;
while ( Table[ i ].text[ 0 ] )
{
if ( !strcmp( Table[ i ].text, text1 ) )
{
if ( Table[ i ].func ) Table[ i ].func( text1, text2 );
break;
}
i++;
}
return Table[ i ].code;
}
void writeCheat()
{
switch (LastType)
{
case 0x0:
memWrite8(PrevCheataddr,IterationIncrement & 0xFF);
break;
case 0x1:
memWrite16(PrevCheataddr,IterationIncrement & 0xFFFF);
break;
case 0x2:
memWrite32(PrevCheataddr,IterationIncrement);
break;
default:
break;
}
}
void handle_extended_t( IniPatch *p)
{
u8 u8Val=0;
u16 u16Val=0;
u32 u32Val=0;
u32 i;
if (SkipCount > 0)
{
SkipCount--;
}
else switch (PrevCheatType)
{
case 0x3040: // vvvvvvvv 00000000 Inc
memRead32(PrevCheataddr,&u32Val);
memWrite32(PrevCheataddr, u32Val+(p->addr));
PrevCheatType = 0;
break;
case 0x3050: // vvvvvvvv 00000000 Dec
memRead32(PrevCheataddr,&u32Val);
memWrite32(PrevCheataddr, u32Val-(p->addr));
PrevCheatType = 0;
break;
case 0x4000: // vvvvvvvv iiiiiiii
for(i=0;i<IterationCount;i++)
{
memWrite32((u32)(PrevCheataddr+(i*IterationIncrement)),(u32)(p->addr+((u32)p->data*i)));
}
PrevCheatType = 0;
break;
case 0x5000: // dddddddd iiiiiiii
for(i=0;i<IterationCount;i++)
{
memRead8(PrevCheataddr+i,&u8Val);
memWrite8(((u32)p->data)+i,u8Val);
}
PrevCheatType = 0;
break;
case 0x6000: // 000Xnnnn iiiiiiii
// Get Number of pointers
if (IterationCount == 0) IterationCount = (u32)p->addr&0x0000FFFF;
// Read first pointer
LastType = ((u32)p->addr&0x000F0000)/0x10000;
memRead32(PrevCheataddr,&u32Val);
PrevCheataddr = u32Val+(u32)p->data;
IterationCount--;
// Check if needed to read another pointer
if (IterationCount == 0)
{
PrevCheatType = 0;
writeCheat();
}
else
PrevCheatType = 0x6001;
case 0x6001: // 000Xnnnn iiiiiiii
// Read first pointer
memRead32(PrevCheataddr,&u32Val);
PrevCheataddr =u32Val+(u32)p->addr;
IterationCount--;
// Check if needed to read another pointer
if (IterationCount == 0)
{
PrevCheatType = 0;
writeCheat();
}
else
{
memRead32(PrevCheataddr,&u32Val);
PrevCheataddr =u32Val+(u32)p->data;
IterationCount--;
if (IterationCount == 0)
{
PrevCheatType = 0;
writeCheat();
}
}
default:
if ((p->addr&0xF0000000) == 0x00000000) // 0aaaaaaa 0000000vv
{
memWrite8(p->addr&0x0FFFFFFF, (u8)p->data&0x000000FF);
PrevCheatType = 0;
}
else if ((p->addr&0xF0000000) == 0x10000000) // 0aaaaaaa 0000vvvv
{
memWrite16(p->addr&0x0FFFFFFF, (u16)p->data&0x0000FFFF);
PrevCheatType = 0;
}
else if ((p->addr&0xF0000000) == 0x20000000) // 0aaaaaaa vvvvvvvv
{
memWrite32(p->addr&0x0FFFFFFF, (u32)p->data);
PrevCheatType = 0;
}
else if ((p->addr&0xFFFF0000) == 0x30000000) // 300000vv 0aaaaaaa Inc
{
memRead8((u32)p->data,&u8Val);
memWrite8((u32)p->data, u8Val+(p->addr&0x000000FF));
PrevCheatType = 0;
}
else if ((p->addr&0xFFFF0000) == 0x30100000) // 301000vv 0aaaaaaa Dec
{
memRead8((u32)p->data,&u8Val);
memWrite8((u32)p->data, u8Val-(p->addr&0x000000FF));
PrevCheatType = 0;
}
else if ((p->addr&0xFFFF0000) == 0x30200000) // 3020vvvv 0aaaaaaa Inc
{
memRead16((u32)p->data,&u16Val);
memWrite16((u32)p->data, u16Val+(p->addr&0x0000FFFF));
PrevCheatType = 0;
}
else if ((p->addr&0xFFFF0000) == 0x30300000) // 3030vvvv 0aaaaaaa Dec
{
memRead16((u32)p->data,&u16Val);
memWrite16((u32)p->data, u16Val-(p->addr&0x0000FFFF));
PrevCheatType = 0;
}
else if ((p->addr&0xFFFF0000) == 0x30400000) // 30400000 0aaaaaaa Inc + Another line
{
PrevCheatType= 0x3040;
PrevCheataddr= (u32)p->data;
}
else if ((p->addr&0xFFFF0000) == 0x30500000) // 30500000 0aaaaaaa Inc + Another line
{
PrevCheatType= 0x3050;
PrevCheataddr= (u32)p->data;
}
else if ((p->addr&0xF0000000) == 0x40000000) // 4aaaaaaa nnnnssss + Another line
{
IterationCount=((u32)p->data&0xFFFF0000)/0x10000;
IterationIncrement=((u32)p->data&0x0000FFFF)*4;
PrevCheataddr=(u32)p->addr&0x0FFFFFFF;
PrevCheatType= 0x4000;
}
else if ((p->addr&0xF0000000) == 0x50000000) // 5sssssss nnnnnnnn + Another line
{
PrevCheataddr = (u32)p->addr&0x0FFFFFFF;
IterationCount=((u32)p->data);
PrevCheatType= 0x5000;
}
else if ((p->addr&0xF0000000) == 0x60000000) // 6aaaaaaa 000000vv + Another line/s
{
PrevCheataddr = (u32)p->addr&0x0FFFFFFF;
IterationIncrement=((u32)p->data);
IterationCount=0;
PrevCheatType= 0x6000;
}
else if ((p->addr&0xF0000000) == 0x70000000)
{
if ((p->data&0x00F00000) == 0x00000000) // 7aaaaaaa 000000vv
{
memRead8((u32)p->addr&0x0FFFFFFF,&u8Val);
memWrite8((u32)p->addr&0x0FFFFFFF,(u8)(u8Val|(p->data&0x000000FF)));
}
else if ((p->data&0x00F00000) == 0x00100000) // 7aaaaaaa 0010vvvv
{
memRead16((u32)p->addr&0x0FFFFFFF,&u16Val);
memWrite16((u32)p->addr&0x0FFFFFFF,(u16)(u16Val|(p->data&0x0000FFFF)));
}
else if ((p->data&0x00F00000) == 0x00200000) // 7aaaaaaa 002000vv
{
memRead8((u32)p->addr&0x0FFFFFFF,&u8Val);
memWrite8((u32)p->addr&0x0FFFFFFF,(u8)(u8Val&(p->data&0x000000FF)));
}
else if ((p->data&0x00F00000) == 0x00300000) // 7aaaaaaa 0030vvvv
{
memRead16((u32)p->addr&0x0FFFFFFF,&u16Val);
memWrite16((u32)p->addr&0x0FFFFFFF,(u16)(u16Val&(p->data&0x0000FFFF)));
}
else if ((p->data&0x00F00000) == 0x00400000) // 7aaaaaaa 004000vv
{
memRead8((u32)p->addr&0x0FFFFFFF,&u8Val);
memWrite8((u32)p->addr&0x0FFFFFFF,(u8)(u8Val^(p->data&0x000000FF)));
}
else if ((p->data&0x00F00000) == 0x00500000) // 7aaaaaaa 0050vvvv
{
memRead16((u32)p->addr&0x0FFFFFFF,&u16Val);
memWrite16((u32)p->addr&0x0FFFFFFF,(u16)(u16Val^(p->data&0x0000FFFF)));
}
}
else if (p->addr < 0xE0000000)
{
if ((((u32)p->data & 0xFFFF0000)==0x00000000) ||
(((u32)p->data & 0xFFFF0000)==0x00100000) ||
(((u32)p->data & 0xFFFF0000)==0x00200000) ||
(((u32)p->data & 0xFFFF0000)==0x00300000))
{
memRead16((u32)p->addr&0x0FFFFFFF,&u16Val);
if (u16Val != (0x0000FFFF&(u32)p->data)) SkipCount = 1;
PrevCheatType= 0;
}
}
else if (p->addr < 0xF0000000)
{
if ((((u32)p->data&0xF0000000)==0x00000000) ||
(((u32)p->data&0xF0000000)==0x10000000) ||
(((u32)p->data&0xF0000000)==0x20000000) ||
(((u32)p->data&0xF0000000)==0x30000000))
{
memRead16((u32)p->data&0x0FFFFFFF,&u16Val);
if (u16Val != (0x0000FFFF&(u32)p->addr)) SkipCount = ((u32)p->addr&0xFFF0000)/0x10000;
PrevCheatType= 0;
}
}
}
}
void _applypatch(int place, IniPatch *p)
{
if (p->placetopatch != place) return;
if (p->enabled == 0) return;
switch (p->cpu)
{
case CPU_EE:
switch (p->type)
{
case BYTE_T:
memWrite8(p->addr, (u8)p->data);
break;
case SHORT_T:
memWrite16(p->addr, (u16)p->data);
break;
case WORD_T:
memWrite32(p->addr, (u32)p->data);
break;
case DOUBLE_T:
memWrite64(p->addr, &p->data);
break;
case EXTENDED_T:
handle_extended_t(p);
break;
default:
break;
}
break;
case CPU_IOP:
switch (p->type)
{
case BYTE_T:
psxMemWrite8(p->addr, (u8)p->data);
break;
case SHORT_T:
psxMemWrite16(p->addr, (u16)p->data);
break;
case WORD_T:
psxMemWrite32(p->addr, (u32)p->data);
break;
default:
break;
}
break;
default:
break;
}
}
//this is for apply patches directly to memory
void applypatch(int place)
{
int i;
if (place == 0) Console::WriteLn(" patchnumber: %d", params patchnumber);
for ( i = 0; i < patchnumber; i++ )
{
_applypatch(place, &patch[i]);
}
}
void patchFunc_comment( char * text1, char * text2 )
{
Console::WriteLn( "comment: %s", params text2 );
}
void patchFunc_gametitle( char * text1, char * text2 )
{
Console::WriteLn( "gametitle: %s", params text2 );
sprintf(strgametitle,"%s",text2);
Console::SetTitle(strgametitle);
}
void patchFunc_patch( char * cmd, char * param )
{
char * pText;
if ( patchnumber >= MAX_PATCH )
{
Console::Error( "Patch ERROR: Maximum number of patches reached: %s=%s", params cmd, param );
return;
}
pText = strtok( param, "," );
pText = param;
patch[ patchnumber ].placetopatch = strtol( pText, (char **)NULL, 0 );
pText = strtok( NULL, "," );
inifile_trim( pText );
patch[ patchnumber ].cpu = (patch_cpu_type)PatchTableExecute( pText, NULL, cpuCore );
if ( patch[ patchnumber ].cpu == 0 )
{
Console::Error( "Unrecognized patch '%s'", params pText );
return;
}
pText = strtok( NULL, "," );
inifile_trim( pText );
sscanf( pText, "%X", &patch[ patchnumber ].addr );
pText = strtok( NULL, "," );
inifile_trim( pText );
patch[ patchnumber ].type = (patch_data_type)PatchTableExecute( pText, NULL, dataType );
if ( patch[ patchnumber ].type == 0 )
{
Console::Error( "Unrecognized patch '%s'", params pText );
return;
}
pText = strtok( NULL, "," );
inifile_trim( pText );
sscanf( pText, "%I64X", &patch[ patchnumber ].data );
patch[ patchnumber ].enabled = 1;
patchnumber++;
}
//this routine is for executing the commands of the ini file
void inifile_command( char * cmd )
{
int code;
char command[ 256 ];
char parameter[ 256 ];
// extract param part (after '=')
char * pEqual = strchr( cmd, '=' );
if ( ! pEqual ) pEqual = cmd+strlen(cmd); // fastmemory doesn't have =
memzero_obj( command );
memzero_obj( parameter );
strncpy( command, cmd, pEqual - cmd );
strncpy( parameter, pEqual + 1, sizeof( parameter ) );
inifile_trim( command );
inifile_trim( parameter );
code = PatchTableExecute( command, parameter, commands );
}
void inifile_trim( char * buffer )
{
char * pInit = buffer;
char * pEnd = NULL;
while ( ( *pInit == ' ' ) || ( *pInit == '\t' ) ) //skip space
{
pInit++;
}
if ( ( pInit[ 0 ] == '/' ) && ( pInit[ 1 ] == '/' ) ) //remove comment
{
buffer[ 0 ] = '\0';
return;
}
pEnd = pInit + strlen( pInit ) - 1;
if ( pEnd <= pInit )
{
buffer[ 0 ] = '\0';
return;
}
while ( ( *pEnd == '\r' ) || ( *pEnd == '\n' ) || ( *pEnd == ' ' ) || ( *pEnd == '\t' ) )
{
pEnd--;
}
if ( pEnd <= pInit )
{
buffer[ 0 ] = '\0';
return;
}
memmove( buffer, pInit, pEnd - pInit + 1 );
buffer[ pEnd - pInit + 1 ] = '\0';
}
void inisection_process( FILE * f1 )
{
char buffer[ 1024 ];
while( fgets( buffer, sizeof( buffer ), f1 ) )
{
inifile_trim( buffer );
if ( buffer[ 0 ] ) inifile_command( buffer );
}
}
//this routine is for reading the ini file
void inifile_read( const char * name )
{
FILE * f1;
char buffer[ 1024 ];
patchnumber = 0;
#ifdef _WIN32
sprintf( buffer, PATCHES_DIR "\\%s.pnach", name );
#else
sprintf( buffer, PATCHES_DIR "/%s.pnach", name );
#endif
f1 = fopen( buffer, "rt" );
#ifndef _WIN32
if( !f1 )
{
// try all upper case because linux is case sensitive
char* pstart = buffer+8;
char* pend = buffer+strlen(buffer);
while(pstart != pend )
{
// stop at the first . since we only want to update the hex
if( *pstart == '.' ) break;
*pstart++ = toupper(*pstart);
}
f1 = fopen(buffer, "rt");
}
#endif
if( !f1 )
{
Console::WriteLn("No patch found.Resuming execution without a patch (this is NOT an error)." );
return;
}
inisection_process( f1 );
fclose( f1 );
}
void resetpatch( void )
{
patchnumber = 0;
}
int AddPatch(int Mode, int Place, int Address, int Size, u64 data)
{
if ( patchnumber >= MAX_PATCH )
{
Console::Error( "Patch ERROR: Maximum number of patches reached.");
return -1;
}
patch[patchnumber].placetopatch = Mode;
patch[patchnumber].cpu = (patch_cpu_type)Place;
patch[patchnumber].addr = Address;
patch[patchnumber].type = (patch_data_type)Size;
patch[patchnumber].data = data;
return patchnumber++;
}
void patchFunc_ffxhack( char * cmd, char * param )
{
g_FFXHack = 1;
}
void patchFunc_xkickdelay( char * cmd, char * param )
{
g_VUGameFixes |= VUFIX_XGKICKDELAY2;
}
void patchFunc_fastmemory( char * cmd, char * param )
{
// only valid for recompilers, and only in the vm build
SetFastMemory(1);
}
void patchFunc_vunanmode( char * cmd, char * param )
{
// Doesn't do anything anymore
}
void patchFunc_path3hack( char * cmd, char * param )
{
path3hack = 1;
}
void patchFunc_roundmode( char * cmd, char * param )
{
int index;
char * pText;
u32 eetype = (Config.sseMXCSR & 0x6000);
u32 vutype = (Config.sseVUMXCSR & 0x6000);
index = 0;
pText = strtok( param, ", " );
while(pText != NULL)
{
u32 type = 0xffff;
if( stricmp(pText, "near") == 0 )
type = 0x0000;
else if( stricmp(pText, "down") == 0 )
type = 0x2000;
else if( stricmp(pText, "up") == 0 )
type = 0x4000;
else if( stricmp(pText, "chop") == 0 )
type = 0x6000;
if( type == 0xffff )
{
Console::WriteLn("bad argument (%s) to round mode! skipping...\n", params pText);
break;
}
if( index == 0 )
eetype=type;
else
vutype=type;
if( index == 1 )
break;
index++;
pText = strtok(NULL, ", ");
}
SetRoundMode(eetype,vutype);
}
void patchFunc_zerogs(char* cmd, char* param)
{
sscanf(param, "%x", &g_ZeroGSOptions);
}
void SetRoundMode(u32 ee, u32 vu)
{
SetCPUState( (Config.sseMXCSR & 0x9fff) | ee, (Config.sseVUMXCSR & 0x9fff) | vu);
}