mirror of https://github.com/PCSX2/pcsx2.git
842 lines
21 KiB
C++
842 lines
21 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
|
|
*/
|
|
|
|
#include "PrecompiledHeader.h"
|
|
|
|
#ifdef _WIN32
|
|
#include "RDebug/deci2.h"
|
|
#else
|
|
#include <sys/time.h>
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "Common.h"
|
|
#include "PsxCommon.h"
|
|
#include "SaveState.h"
|
|
|
|
#include "CDVDisodrv.h"
|
|
#include "VUmicro.h"
|
|
#include "VU.h"
|
|
#include "iCore.h"
|
|
#include "iVUzerorec.h"
|
|
#include "BaseblockEx.h" // included for devbuild block dumping (which may or may not work anymore?)
|
|
|
|
#include "GS.h"
|
|
#include "COP0.h"
|
|
#include "Cache.h"
|
|
|
|
#include "Paths.h"
|
|
|
|
using namespace std;
|
|
using namespace R5900;
|
|
|
|
PcsxConfig Config;
|
|
u32 BiosVersion;
|
|
char CdromId[12];
|
|
static int g_Pcsx2Recording = 0; // true 1 if recording video and sound
|
|
|
|
const char *LabelAuthors = { N_(
|
|
"PCSX2, a PS2 emulator\n\n"
|
|
"Active Devs: Arcum42, Refraction,\n"
|
|
"drk||raziel, cottonvibes, gigaherz,\n"
|
|
"rama, Jake.Stine, saqib, Tmkk\n"
|
|
"\n"
|
|
"Inactive devs: Alexey silinov, Aumatt,\n"
|
|
"Florin, goldfinger, Linuzappz, loser,\n"
|
|
"Nachbrenner, shadow, Zerofrog\n"
|
|
"\n"
|
|
"Betatesting: Bositman, ChaosCode,\n"
|
|
"CKemu, crushtest, GeneralPlot,\n"
|
|
"Krakatos, Paorotaku, Rudy_X\n"
|
|
"\n"
|
|
"Webmasters: CKemu, Falcon4ever"
|
|
)
|
|
};
|
|
|
|
const char *LabelGreets = { N_(
|
|
"Contributors: Hiryu and Sjeep for libcvd (the iso parsing and\n"
|
|
"filesystem driver code), nneeve, pseudonym\n"
|
|
"\n"
|
|
"Plugin Specialists: ChickenLiver (Lilypad), Efp (efp),\n"
|
|
"Gabest (Gsdx, Cdvdolio, Xpad)\n"
|
|
"\n"
|
|
"Special thanks to: black_wd, Belmont, BGome, _Demo_, Dreamtime,\n"
|
|
"F|RES, MrBrown, razorblade, Seta-san, Skarmeth"
|
|
)
|
|
};
|
|
|
|
static struct {
|
|
const char *name;
|
|
u32 size;
|
|
} ioprps[]={
|
|
{"IOPRP14", 43845},
|
|
{"IOPRP142", 48109},
|
|
{"IOPRP143", 58317},
|
|
{"IOPRP144", 58525},
|
|
{"IOPRP15", 82741},
|
|
{"IOPRP151", 82917},
|
|
{"IOPRP153", 82949},
|
|
{"IOPRP16", 91909},
|
|
{"IOPRP165", 98901},
|
|
{"IOPRP20", 109809},
|
|
{"IOPRP202", 110993},
|
|
{"IOPRP205", 119797},
|
|
{"IOPRP21", 126857},
|
|
{"IOPRP211", 129577},
|
|
{"IOPRP213", 129577},
|
|
{"IOPRP214", 140945},
|
|
{"IOPRP22", 199257},
|
|
{"IOPRP221", 196937},
|
|
{"IOPRP222", 198233},
|
|
{"IOPRP224", 201065},
|
|
{"IOPRP23", 230329},
|
|
{"IOPRP234", 247641},
|
|
{"IOPRP24", 251065},
|
|
{"IOPRP241", 251049},
|
|
{"IOPRP242", 252409},
|
|
{"IOPRP243", 253201},
|
|
{"IOPRP250", 264897},
|
|
{"IOPRP252", 265233},
|
|
{"IOPRP253", 267217},
|
|
{"IOPRP254", 264449},
|
|
{"IOPRP255", 264449},
|
|
{"IOPRP260", 248945},
|
|
{"IOPRP270", 249121},
|
|
{"IOPRP271", 266817},
|
|
{"IOPRP280", 269889},
|
|
{"IOPRP300", 275345},
|
|
{"DNAS280", 272753},
|
|
{"DNAS270", 251729},
|
|
{"DNAS271", 268977},
|
|
{"DNAS300", 278641},
|
|
{"DNAS280", 272705},
|
|
{"DNAS255", 264945},
|
|
{NULL, 0}
|
|
};
|
|
|
|
void GetRPCVersion(char *ioprp, char *rpcver){
|
|
char *p=ioprp;
|
|
int i;
|
|
struct TocEntry te;
|
|
|
|
if (p && (CDVD_findfile(p+strlen("cdromN:"), &te) != -1)){
|
|
for (i=0; ioprps[i].size>0; i++)
|
|
if (te.fileSize==ioprps[i].size)
|
|
break;
|
|
if (ioprps[i].size>0)
|
|
p=(char *)ioprps[i].name;
|
|
}
|
|
// fixme - Is p really supposed to be set in the middle of an if statement?
|
|
if (p && (p=strstr(p, "IOPRP")+strlen("IOPRP"))){
|
|
for (i=0;(i<4) && p && (*p>='0') && (*p<='9');i++, p++) rpcver[i]=*p;
|
|
for ( ; i<4 ;i++ ) rpcver[i]='0';
|
|
}
|
|
}
|
|
|
|
u32 GetBiosVersion() {
|
|
unsigned int fileOffset=0;
|
|
s8 *ROMVER;
|
|
char vermaj[8];
|
|
char vermin[8];
|
|
struct romdir *rd;
|
|
u32 version;
|
|
int i;
|
|
|
|
for (i=0; i<512*1024; i++) {
|
|
rd = (struct romdir*)&psRu8(i);
|
|
if (strncmp(rd->fileName, "RESET", 5) == 0)
|
|
break; /* found romdir */
|
|
}
|
|
if (i == 512*1024) return -1;
|
|
|
|
while(strlen(rd->fileName) > 0){
|
|
if (strcmp(rd->fileName, "ROMVER") == 0){ // found romver
|
|
ROMVER = &psRs8(fileOffset);
|
|
|
|
strncpy(vermaj, (char *)(ROMVER+ 0), 2); vermaj[2] = 0;
|
|
strncpy(vermin, (char *)(ROMVER+ 2), 2); vermin[2] = 0;
|
|
version = strtol(vermaj, (char**)NULL, 0) << 8;
|
|
version|= strtol(vermin, (char**)NULL, 0);
|
|
|
|
return version;
|
|
}
|
|
|
|
if ((rd->fileSize % 0x10)==0)
|
|
fileOffset += rd->fileSize;
|
|
else
|
|
fileOffset += (rd->fileSize + 0x10) & 0xfffffff0;
|
|
|
|
rd++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
//2002-09-22 (Florin)
|
|
int IsBIOS(char *filename, char *description)
|
|
{
|
|
string Bios;
|
|
char ROMVER[14+1], zone[12+1];
|
|
FILE *fp;
|
|
unsigned int fileOffset=0, found=FALSE;
|
|
struct romdir rd;
|
|
|
|
Path::Combine( Bios, Config.BiosDir, filename );
|
|
|
|
int biosFileSize = Path::getFileSize( Bios );
|
|
if( biosFileSize <= 0) return FALSE;
|
|
|
|
fp = fopen(Bios.c_str(), "rb");
|
|
if (fp == NULL) return FALSE;
|
|
|
|
while ((ftell(fp)<512*1024) && (fread(&rd, DIRENTRY_SIZE, 1, fp)==1))
|
|
if (strcmp(rd.fileName, "RESET") == 0)
|
|
break; /* found romdir */
|
|
|
|
if ((strcmp(rd.fileName, "RESET") != 0) || (rd.fileSize == 0)) {
|
|
fclose(fp);
|
|
return FALSE; //Unable to locate ROMDIR structure in file or a ioprpXXX.img
|
|
}
|
|
|
|
while(strlen(rd.fileName) > 0){
|
|
if (strcmp(rd.fileName, "ROMVER") == 0){ // found romver
|
|
unsigned int filepos=ftell(fp);
|
|
fseek(fp, fileOffset, SEEK_SET);
|
|
if (fread(&ROMVER, 14, 1, fp) == 0) break;
|
|
fseek(fp, filepos, SEEK_SET);//go back
|
|
|
|
switch(ROMVER[4]){
|
|
case 'T':sprintf(zone, "T10K "); break;
|
|
case 'X':sprintf(zone, "Test ");break;
|
|
case 'J':sprintf(zone, "Japan "); break;
|
|
case 'A':sprintf(zone, "USA "); break;
|
|
case 'E':sprintf(zone, "Europe"); break;
|
|
case 'H':sprintf(zone, "HK "); break;
|
|
case 'P':sprintf(zone, "Free "); break;
|
|
case 'C':sprintf(zone, "China "); break;
|
|
default: sprintf(zone, "%c ",ROMVER[4]); break;//shoudn't show
|
|
}
|
|
sprintf(description, "%s vXX.XX(XX/XX/XXXX) %s", zone,
|
|
ROMVER[5]=='C'?"Console":ROMVER[5]=='D'?"Devel":"");
|
|
strncpy(description+ 8, ROMVER+ 0, 2);//ver major
|
|
strncpy(description+11, ROMVER+ 2, 2);//ver minor
|
|
strncpy(description+14, ROMVER+12, 2);//day
|
|
strncpy(description+17, ROMVER+10, 2);//month
|
|
strncpy(description+20, ROMVER+ 6, 4);//year
|
|
found = TRUE;
|
|
}
|
|
|
|
if ((rd.fileSize % 0x10)==0)
|
|
fileOffset += rd.fileSize;
|
|
else
|
|
fileOffset += (rd.fileSize + 0x10) & 0xfffffff0;
|
|
|
|
if (fread(&rd, DIRENTRY_SIZE, 1, fp)==0) break;
|
|
}
|
|
fileOffset-=((rd.fileSize + 0x10) & 0xfffffff0) - rd.fileSize;
|
|
|
|
fclose(fp);
|
|
|
|
if (found)
|
|
{
|
|
char percent[6];
|
|
|
|
if ( biosFileSize < (int)fileOffset)
|
|
{
|
|
sprintf(percent, " %d%%", biosFileSize*100/(int)fileOffset);
|
|
strcat(description, percent);//we force users to have correct bioses,
|
|
//not that lame scph10000 of 513KB ;-)
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE; //fail quietly
|
|
}
|
|
|
|
// LOAD STUFF
|
|
|
|
// fixme - Is there any reason why we shouldn't delete this define, and replace the array lengths
|
|
// with the actual numbers?
|
|
#define ISODCL(from, to) (to - from + 1)
|
|
|
|
struct iso_directory_record {
|
|
char length [ISODCL (1, 1)]; /* length[1]; 711 */
|
|
char ext_attr_length [ISODCL (2, 2)]; /* ext_attr_length[1]; 711 */
|
|
char extent [ISODCL (3, 10)]; /* extent[8]; 733 */
|
|
char size [ISODCL (11, 18)]; /* size[8]; 733 */
|
|
char date [ISODCL (19, 25)]; /* date[7]; 7 by 711 */
|
|
char flags [ISODCL (26, 26)]; /* flags[1]; */
|
|
char file_unit_size [ISODCL (27, 27)]; /* file_unit_size[1]; 711 */
|
|
char interleave [ISODCL (28, 28)]; /* interleave[1]; 711 */
|
|
char volume_sequence_number [ISODCL (29, 32)]; /* volume_sequence_number[3]; 723 */
|
|
unsigned char name_len [ISODCL (33, 33)]; /* name_len[1]; 711 */
|
|
char name [1];
|
|
};
|
|
|
|
int LoadCdrom() {
|
|
return 0;
|
|
}
|
|
|
|
int CheckCdrom() {
|
|
u8 *buf;
|
|
|
|
if (CDVDreadTrack(16, CDVD_MODE_2352) == -1)
|
|
return -1;
|
|
buf = CDVDgetBuffer();
|
|
if (buf == NULL)
|
|
return -1;
|
|
|
|
strncpy(CdromId, (char*)buf+52, 10);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int GetPS2ElfName(char *name){
|
|
int f;
|
|
char buffer[g_MaxPath];//if a file is longer...it should be shorter :D
|
|
char *pos;
|
|
TocEntry tocEntry;
|
|
|
|
CDVDFS_init();
|
|
|
|
// check if the file exists
|
|
if (CDVD_findfile("SYSTEM.CNF;1", &tocEntry) != TRUE){
|
|
Console::Error("Boot Error > SYSTEM.CNF not found");
|
|
return 0;//could not find; not a PS/PS2 cdvd
|
|
}
|
|
|
|
f=CDVDFS_open("SYSTEM.CNF;1", 1);
|
|
CDVDFS_read(f, buffer, g_MaxPath);
|
|
CDVDFS_close(f);
|
|
|
|
buffer[tocEntry.fileSize]='\0';
|
|
|
|
pos=strstr(buffer, "BOOT2");
|
|
if (pos==NULL){
|
|
pos=strstr(buffer, "BOOT");
|
|
if (pos==NULL) {
|
|
Console::Error("Boot Error > This is not a PS2 game!");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
pos+=strlen("BOOT2");
|
|
while (pos && *pos && pos<=&buffer[255]
|
|
&& (*pos<'A' || (*pos>'Z' && *pos<'a') || *pos>'z'))
|
|
pos++;
|
|
if (!pos || *pos==0)
|
|
return 0;
|
|
|
|
sscanf(pos, "%s", name);
|
|
|
|
if (strncmp("cdrom0:\\", name, 8) == 0) {
|
|
strncpy(CdromId, name+8, 11); CdromId[11] = 0;
|
|
}
|
|
|
|
#ifdef PCSX2_DEVBUILD
|
|
FILE *fp;
|
|
int i;
|
|
|
|
// inifile_read(CdromId);
|
|
fp = fopen("System.map", "r");
|
|
if( fp == NULL ) return 2;
|
|
|
|
u32 addr;
|
|
|
|
Console::WriteLn("Loading System.map...");
|
|
while (!feof(fp)) {
|
|
fseek(fp, 8, SEEK_CUR);
|
|
buffer[0] = '0'; buffer[1] = 'x';
|
|
for (i=2; i<10; i++) buffer[i] = fgetc(fp); buffer[i] = 0;
|
|
addr = strtoul(buffer, (char**)NULL, 0);
|
|
fseek(fp, 3, SEEK_CUR);
|
|
for (i=0; i<g_MaxPath; i++) {
|
|
buffer[i] = fgetc(fp);
|
|
if (buffer[i] == '\n' || buffer[i] == 0) break;
|
|
}
|
|
if (buffer[i] == 0) break;
|
|
buffer[i] = 0;
|
|
|
|
disR5900AddSym(addr, buffer);
|
|
}
|
|
fclose(fp);
|
|
#endif
|
|
|
|
return 2;
|
|
}
|
|
|
|
#ifdef PCSX2_DEVBUILD
|
|
|
|
void SaveGSState(const string& file)
|
|
{
|
|
if( g_SaveGSStream ) return;
|
|
|
|
Console::WriteLn( _("Saving GS State...") );
|
|
Console::WriteLn( "\t%hs", params file.c_str() );
|
|
|
|
g_fGSSave = new gzSavingState( file );
|
|
|
|
g_SaveGSStream = 1;
|
|
g_nLeftGSFrames = 2;
|
|
|
|
g_fGSSave->Freeze( g_nLeftGSFrames );
|
|
}
|
|
|
|
extern uptr pDsp;
|
|
void LoadGSState(const string& file)
|
|
{
|
|
int ret;
|
|
gzLoadingState* f;
|
|
|
|
Console::Status( "Loading GS State..." );
|
|
|
|
try
|
|
{
|
|
f = new gzLoadingState( file );
|
|
}
|
|
catch( Exception::FileNotFound& )
|
|
{
|
|
// file not found? try prefixing with sstates folder:
|
|
if( !Path::isRooted( file ) )
|
|
{
|
|
string strfile;
|
|
Path::Combine( strfile, SSTATES_DIR, file );
|
|
f = new gzLoadingState( strfile.c_str() );
|
|
|
|
// If this load attempt fails, then let the exception bubble up to
|
|
// the caller to deal with...
|
|
}
|
|
}
|
|
|
|
// Always set gsIrq callback -- GS States are always exclusionary of MTGS mode
|
|
GSirqCallback( gsIrq );
|
|
|
|
ret = GSopen(&pDsp, "PCSX2", 0);
|
|
if (ret != 0)
|
|
{
|
|
delete f;
|
|
throw Exception::PluginFailure( "GS" );
|
|
}
|
|
|
|
ret = PAD1open((void *)&pDsp);
|
|
|
|
f->Freeze(g_nLeftGSFrames);
|
|
f->gsFreeze();
|
|
|
|
f->FreezePlugin( "GS", gsSafeFreeze );
|
|
|
|
RunGSState( *f );
|
|
|
|
delete( f );
|
|
|
|
GSclose();
|
|
PAD1close();
|
|
}
|
|
|
|
#endif
|
|
|
|
struct LangDef {
|
|
char id[8];
|
|
char name[64];
|
|
};
|
|
|
|
LangDef sLangs[] = {
|
|
{ "ar_AR", N_("Arabic") },
|
|
{ "bg_BG", N_("Bulgarian") },
|
|
{ "ca_CA", N_("Catalan") },
|
|
{ "cz_CZ", N_("Czech") },
|
|
{ "du_DU", N_("Dutch") },
|
|
{ "de_DE", N_("German") },
|
|
{ "el_EL", N_("Greek") },
|
|
{ "en_US", N_("English") },
|
|
{ "fr_FR", N_("French") },
|
|
{ "hb_HB" , N_("Hebrew") },
|
|
{ "hu_HU", N_("Hungarian") },
|
|
{ "it_IT", N_("Italian") },
|
|
{ "ja_JA", N_("Japanese") },
|
|
{ "pe_PE", N_("Persian") },
|
|
{ "po_PO", N_("Portuguese") },
|
|
{ "po_BR", N_("Portuguese BR") },
|
|
{ "pl_PL" , N_("Polish") },
|
|
{ "ro_RO", N_("Romanian") },
|
|
{ "ru_RU", N_("Russian") },
|
|
{ "es_ES", N_("Spanish") },
|
|
{ "sh_SH" , N_("S-Chinese") },
|
|
{ "sw_SW", N_("Swedish") },
|
|
{ "tc_TC", N_("T-Chinese") },
|
|
{ "tr_TR", N_("Turkish") },
|
|
{ "", "" },
|
|
};
|
|
|
|
|
|
char *ParseLang(char *id) {
|
|
int i=0;
|
|
|
|
while (sLangs[i].id[0] != 0) {
|
|
if (!strcmp(id, sLangs[i].id))
|
|
return _(sLangs[i].name);
|
|
i++;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
#define NUM_STATES 10
|
|
int StatesC = 0;
|
|
|
|
extern char strgametitle[256];
|
|
|
|
char* mystrlwr( char* string )
|
|
{
|
|
assert( string != NULL );
|
|
while ( 0 != ( *string++ = (char)tolower( *string ) ) );
|
|
return string;
|
|
}
|
|
|
|
static void GetGSStateFilename( string& dest )
|
|
{
|
|
string gsText;
|
|
ssprintf( gsText, "/%8.8X.%d.gs", ElfCRC, StatesC);
|
|
Path::Combine( dest, SSTATES_DIR, gsText );
|
|
}
|
|
|
|
void CycleFrameLimit(int dir)
|
|
{
|
|
const char* limitMsg;
|
|
u32 newOptions;
|
|
u32 curFrameLimit = Config.Options & PCSX2_FRAMELIMIT_MASK;
|
|
u32 newFrameLimit;
|
|
static u32 oldFrameLimit = PCSX2_FRAMELIMIT_LIMIT;
|
|
|
|
if( dir == 0 ) {
|
|
// turn off limit or restore previous limit mode
|
|
if (curFrameLimit) {
|
|
oldFrameLimit = curFrameLimit;
|
|
newFrameLimit = 0;
|
|
} else
|
|
newFrameLimit = oldFrameLimit;
|
|
} else if (dir > 0) {
|
|
// next
|
|
newFrameLimit = (curFrameLimit + PCSX2_FRAMELIMIT_LIMIT) & PCSX2_FRAMELIMIT_MASK;
|
|
} else {
|
|
// previous
|
|
newFrameLimit = (curFrameLimit + PCSX2_FRAMELIMIT_VUSKIP) & PCSX2_FRAMELIMIT_MASK;
|
|
}
|
|
|
|
newOptions = (Config.Options & ~PCSX2_FRAMELIMIT_MASK) | newFrameLimit;
|
|
|
|
gsResetFrameSkip();
|
|
|
|
switch(newFrameLimit) {
|
|
case PCSX2_FRAMELIMIT_NORMAL:
|
|
limitMsg = "None/Normal";
|
|
break;
|
|
case PCSX2_FRAMELIMIT_LIMIT:
|
|
limitMsg = "Limit";
|
|
break;
|
|
case PCSX2_FRAMELIMIT_SKIP:
|
|
case PCSX2_FRAMELIMIT_VUSKIP:
|
|
if( GSsetFrameSkip == NULL )
|
|
{
|
|
newOptions &= ~PCSX2_FRAMELIMIT_MASK;
|
|
Console::Notice("Notice: GS Plugin does not support frameskipping.");
|
|
limitMsg = "None/Normal";
|
|
}
|
|
else
|
|
{
|
|
// When enabling Skipping we have to make sure Skipper (GS) and Limiter (EE)
|
|
// are properly synchronized.
|
|
gsDynamicSkipEnable();
|
|
limitMsg = ((newOptions & PCSX2_FRAMELIMIT_MASK) == PCSX2_FRAMELIMIT_SKIP) ? "Skip" : "VUSkip";
|
|
}
|
|
|
|
break;
|
|
}
|
|
Threading::AtomicExchange( Config.Options, newOptions );
|
|
|
|
Console::Notice("Frame Limit Mode Changed: %s", params limitMsg );
|
|
|
|
// [Air]: Do we really want to save runtime changes to frameskipping?
|
|
//SaveConfig();
|
|
}
|
|
|
|
void ProcessFKeys(int fkey, int shift)
|
|
{
|
|
string Text;
|
|
|
|
assert(fkey >= 1 && fkey <= 12 );
|
|
|
|
switch(fkey) {
|
|
case 1:
|
|
try
|
|
{
|
|
SaveState::GetFilename( Text, StatesC );
|
|
gzSavingState( Text ).FreezeAll();
|
|
}
|
|
catch( Exception::BaseException& ex )
|
|
{
|
|
// 99% of the time this is a file permission error and the
|
|
// cpu state is intact so just display a passive msg to console.
|
|
|
|
Console::Error( _( "Error > Could not save state to slot %d" ), params StatesC );
|
|
Console::Error( ex.cMessage() );
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
if( shift )
|
|
StatesC = (StatesC+NUM_STATES-1) % NUM_STATES;
|
|
else
|
|
StatesC = (StatesC+1) % NUM_STATES;
|
|
|
|
Console::Notice( _( " > Selected savestate slot %d" ), params StatesC);
|
|
|
|
if( GSchangeSaveState != NULL ) {
|
|
SaveState::GetFilename(Text, StatesC);
|
|
GSchangeSaveState(StatesC, Text.c_str());
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
try
|
|
{
|
|
SaveState::GetFilename( Text, StatesC );
|
|
gzLoadingState joe( Text ); // throws exception on version mismatch
|
|
cpuReset();
|
|
SysResetExecutionState();
|
|
joe.FreezeAll();
|
|
}
|
|
catch( Exception::StateLoadError_Recoverable& )
|
|
{
|
|
// At this point the cpu hasn't been reset, so we can return
|
|
// control to the user safely... (and silently)
|
|
}
|
|
catch( Exception::FileNotFound& )
|
|
{
|
|
Console::Notice( _("Saveslot %d cannot be loaded; slot does not exist (file not found)"), params StatesC );
|
|
}
|
|
catch( Exception::RuntimeError& ex )
|
|
{
|
|
// This is the bad one. Chances are the cpu has been reset, so emulation has
|
|
// to be aborted. Sorry user! We'll give you some info for your trouble:
|
|
|
|
Console::Error( _("An error occured while trying to load saveslot %d"), params StatesC );
|
|
Console::Error( ex.cMessage() );
|
|
Msgbox::Alert(
|
|
"Pcsx2 encountered an error while trying to load the savestate\n"
|
|
"and emulation had to be aborted." );
|
|
|
|
ClosePlugins( true );
|
|
|
|
throw Exception::CpuStateShutdown(
|
|
"Saveslot load failed; PS2 emulated state had to be shut down." ); // let the GUI handle the error "gracefully"
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
CycleFrameLimit(shift ? -1 : 1);
|
|
break;
|
|
|
|
// note: VK_F5-VK_F7 are reserved for GS
|
|
case 8:
|
|
GSmakeSnapshot("snaps/");
|
|
break;
|
|
|
|
#ifdef PCSX2_DEVBUILD
|
|
case 10:
|
|
{
|
|
int num;
|
|
FILE* f;
|
|
BASEBLOCKEX** ppblocks = GetAllBaseBlocks(&num, 0);
|
|
|
|
f = fopen("perflog.txt", "w");
|
|
while(num-- > 0 ) {
|
|
if( ppblocks[0]->visited > 0 ) {
|
|
fprintf(f, "%u %u %u %u\n", ppblocks[0]->startpc, (u32)(ppblocks[0]->ltime.QuadPart / ppblocks[0]->visited), ppblocks[0]->visited, ppblocks[0]->size);
|
|
}
|
|
ppblocks[0]->visited = 0;
|
|
ppblocks[0]->ltime.QuadPart = 0;
|
|
ppblocks++;
|
|
}
|
|
fclose(f);
|
|
Console::Status( "perflog.txt written" );
|
|
break;
|
|
}
|
|
|
|
case 11:
|
|
if( mtgsThread != NULL ) {
|
|
Console::Notice( "Cannot make gsstates in MTGS mode" );
|
|
}
|
|
else {
|
|
if( strgametitle[0] != 0 ) {
|
|
// only take the first two words
|
|
char name[256], *tok;
|
|
string gsText;
|
|
|
|
tok = strtok(strgametitle, " ");
|
|
sprintf(name, "%s_", mystrlwr(tok));
|
|
tok = strtok(NULL, " ");
|
|
if( tok != NULL ) strcat(name, tok);
|
|
|
|
ssprintf( gsText, "%s.%d.gs", name, StatesC);
|
|
Path::Combine( Text, SSTATES_DIR, gsText );
|
|
}
|
|
else
|
|
GetGSStateFilename( Text );
|
|
|
|
SaveGSState(Text);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case 12:
|
|
if( shift ) {
|
|
#ifdef PCSX2_DEVBUILD
|
|
iDumpRegisters(cpuRegs.pc, 0);
|
|
Console::Notice("hardware registers dumped EE:%x, IOP:%x\n", params cpuRegs.pc, psxRegs.pc);
|
|
#endif
|
|
}
|
|
else {
|
|
g_Pcsx2Recording ^= 1;
|
|
if( mtgsThread != NULL ) {
|
|
mtgsThread->SendSimplePacket(GS_RINGTYPE_RECORD, g_Pcsx2Recording, 0, 0);
|
|
}
|
|
else {
|
|
if( GSsetupRecording != NULL ) GSsetupRecording(g_Pcsx2Recording, NULL);
|
|
}
|
|
if( SPU2setupRecording != NULL ) SPU2setupRecording(g_Pcsx2Recording, NULL);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void injectIRX(const char *filename)
|
|
{
|
|
string path;
|
|
char name[260], *p, *q;
|
|
struct romdir *rd;
|
|
int iROMDIR=-1, iIOPBTCONF=-1, iBLANK=-1, i, filesize;
|
|
FILE *fp;
|
|
|
|
strcpy(name, filename);
|
|
for (i=0; name[i] && name[i]!='.' && i<10; i++) name[i]=toupper(name[i]);name[i]=0;
|
|
|
|
//phase 1: find ROMDIR in bios
|
|
for (p=(char*)PS2MEM_ROM; p<(char*)PS2MEM_ROM+0x80000; p++)
|
|
if (strncmp(p, "RESET", 5)==0)
|
|
break;
|
|
rd=(struct romdir*)p;
|
|
|
|
for (i=0; rd[i].fileName[0]; i++)if (strncmp(rd[i].fileName, name, strlen(name))==0)break;
|
|
if (rd[i].fileName[0])return;//already in;)
|
|
|
|
//phase 2: make room in IOPBTCONF & ROMDIR
|
|
for (i=0; rd[i].fileName[0]; i++)if (strncmp(rd[i].fileName, "ROMDIR", 6)==0)iROMDIR=i;
|
|
for (i=0; rd[i].fileName[0]; i++)if (strncmp(rd[i].fileName, "IOPBTCONF", 9)==0)iIOPBTCONF=i;
|
|
|
|
for (i=0; rd[i].fileName[0]; i++)if (rd[i].fileName[0]=='-')break; iBLANK=i;
|
|
rd[iBLANK].fileSize-=DIRENTRY_SIZE+DIRENTRY_SIZE;
|
|
p=(char*)PS2MEM_ROM;for (i=0; i<iBLANK; i++)p+=(rd[i].fileSize+0xF)&(~0xF);p+=DIRENTRY_SIZE;
|
|
|
|
// fixme - brevity, yes, but at the expense of readability?
|
|
q=(char*)PS2MEM_ROM;for (i=0; i<=iIOPBTCONF; i++) q+=(rd[i].fileSize+0xF)&(~0xF);
|
|
while (p-16>q){*((u64*)p)=*((u64*)p-4);*((u64*)p+1)=*((u64*)p-3);p-=DIRENTRY_SIZE;}
|
|
*((u64*)p)=*((u64*)p+1)=0;p-=DIRENTRY_SIZE;rd[iIOPBTCONF].fileSize+=DIRENTRY_SIZE;
|
|
|
|
q=(char*)PS2MEM_ROM;for (i=0; i<=iROMDIR; i++) q+=(rd[i].fileSize+0xF)&(~0xF);
|
|
while (p >q){*((u64*)p)=*((u64*)p-2);*((u64*)p+1)=*((u64*)p-1);p-=DIRENTRY_SIZE;}
|
|
*((u64*)p)=*((u64*)p+1)=0;p-=DIRENTRY_SIZE;rd[iROMDIR].fileSize+=DIRENTRY_SIZE;
|
|
|
|
//phase 3: add the name to the end of IOPBTCONF
|
|
p=(char*)PS2MEM_ROM;for (i=0; i<iIOPBTCONF; i++) p+=(rd[i].fileSize+0xF)&(~0xF);while(*p) p++;//go to end of file
|
|
strcpy(p, name);p[strlen(name)]=0xA;
|
|
|
|
//phase 4: find file
|
|
Path::Combine( path, Config.BiosDir, filename );
|
|
|
|
if( !Path::isFile( path ) )
|
|
{
|
|
Msgbox::Alert("Unable to hack in %s%s\n", params Config.BiosDir, filename);
|
|
return;
|
|
}
|
|
|
|
//phase 5: add the file to the end of the bios
|
|
p=(char*)PS2MEM_ROM;for (i=0; rd[i].fileName[0]; i++)p+=(rd[i].fileSize+0xF)&(~0xF);
|
|
|
|
fp=fopen(path.c_str(), "rb");
|
|
fseek(fp, 0, SEEK_END);
|
|
filesize=ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
fread(p, 1, filesize, fp);
|
|
fclose(fp);
|
|
|
|
//phase 6: register it in ROMDIR
|
|
memset(rd[i].fileName, 0, 10);
|
|
memcpy(rd[i].fileName, name, strlen(name));
|
|
rd[i].fileSize=filesize;
|
|
rd[i].extInfoSize=0;
|
|
}
|
|
|
|
|
|
// [TODO] I'd like to move the following functions to their own module eventually.
|
|
// It might even be a good idea to just go ahead and move them into Win32/Linux
|
|
// specific files since they're all #ifdef'd that way anyways.
|
|
|
|
#ifdef _WIN32
|
|
static LARGE_INTEGER lfreq;
|
|
#endif
|
|
|
|
void InitCPUTicks()
|
|
{
|
|
#ifdef _WIN32
|
|
QueryPerformanceFrequency(&lfreq);
|
|
#endif
|
|
}
|
|
|
|
u64 GetTickFrequency()
|
|
{
|
|
#ifdef _WIN32
|
|
return lfreq.QuadPart;
|
|
#else
|
|
return 1000000; // unix measures in microseconds
|
|
#endif
|
|
}
|
|
|
|
u64 GetCPUTicks()
|
|
{
|
|
#ifdef _WIN32
|
|
LARGE_INTEGER count;
|
|
QueryPerformanceCounter(&count);
|
|
return count.QuadPart;
|
|
#else
|
|
struct timeval t;
|
|
gettimeofday(&t, NULL);
|
|
return ((u64)t.tv_sec*GetTickFrequency())+t.tv_usec;
|
|
#endif
|
|
}
|
|
|
|
void _memset16_unaligned( void* dest, u16 data, size_t size )
|
|
{
|
|
jASSUME( (size & 0x1) == 0 );
|
|
|
|
u16* dst = (u16*)dest;
|
|
for(int i=size; i; --i, ++dst )
|
|
*dst = data;
|
|
}
|