Major GUI API Cleanups. Most likely buggy as all hell.

* Moved most of the shared code between the two GUIs into platform independent source modules (System.cpp, Saveslots.cpp, and RecoverySystem.cpp)
 * Created two new namespaces which house functions that need to be implemented by OS/platforms: HostGui and HostSys.  HostGui is useful -- HostSys I'm not sure I'll keep around in the long run.
 * Moved keyEvent struct from PS2Edefs.h to PS2Etypes.h
 * Many improvements to the logic flow of the GUI (should be a little less fallible).

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@675 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-03-04 11:33:45 +00:00
parent 900d4bd485
commit cc930f86c5
38 changed files with 1637 additions and 1711 deletions

View File

@ -95,15 +95,6 @@ char* CALLBACK PS2EgetLibName(void);
linux: the XK_XXX will be used (XFree86)
*/
// event values:
#define KEYPRESS 1
#define KEYRELEASE 2
typedef struct _keyEvent {
u32 key;
u32 evt;
} keyEvent;
// for 64bit compilers
typedef char __keyEvent_Size__[(sizeof(keyEvent) == 8)?1:-1];

View File

@ -219,6 +219,15 @@ typedef struct {
s8 *data;
} freezeData;
// event values:
#define KEYPRESS 1
#define KEYRELEASE 2
typedef struct _keyEvent {
u32 key;
u32 evt;
} keyEvent;
/* common defines */
#ifndef C_ASSERT
#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1]

View File

@ -21,28 +21,6 @@
#include "PS2Etypes.h"
struct TESTRUNARGS
{
u8 enabled;
u8 jpgcapture;
int frame; // if < 0, frame is unlimited (run until crash).
int numimages;
int curimage;
u32 autopad; // mask for auto buttons
bool efile;
int snapdone;
const char* ptitle;
const char* pimagename;
const char* plogname;
const char* pgsdll, *pcdvddll, *pspudll;
const char* ppad1dll, *ppad2dll, *pdev9dll;
};
extern TESTRUNARGS g_TestRun;
#define BIAS 2 // Bus is half of the actual ps2 speed
//#define PS2CLK 36864000 /* 294.912 mhz */
//#define PSXCLK 9216000 /* 36.864 Mhz */

View File

@ -137,7 +137,7 @@ void rcntInit() {
// debug code, used for stats
int g_nCounters[4];
static int iFrame = 0;
static uint iFrame = 0;
#ifndef _WIN32
#include <sys/time.h>
@ -270,81 +270,6 @@ u32 UpdateVSyncRate()
extern u32 vu0time;
void vSyncDebugStuff() {
#ifdef PCSX2_DEVBUILD
if( g_TestRun.enabled && g_TestRun.frame > 0 ) {
if( iFrame > g_TestRun.frame ) {
// take a snapshot
if( g_TestRun.pimagename != NULL && GSmakeSnapshot2 != NULL ) {
if( g_TestRun.snapdone ) {
g_TestRun.curimage++;
g_TestRun.snapdone = 0;
g_TestRun.frame += 20;
if( g_TestRun.curimage >= g_TestRun.numimages ) {
// exit
SysClose();
exit(0);
}
}
else {
// query for the image
GSmakeSnapshot2(g_TestRun.pimagename, &g_TestRun.snapdone, g_TestRun.jpgcapture);
}
}
else {
// exit
SysClose();
exit(0);
}
}
}
GSVSYNC();
if( g_SaveGSStream == 1 ) {
freezeData fP;
g_SaveGSStream = 2;
g_fGSSave->gsFreeze();
if (GSfreeze(FREEZE_SIZE, &fP) == -1) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
}
else {
fP.data = (s8*)malloc(fP.size);
if (fP.data == NULL) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
}
else {
if (GSfreeze(FREEZE_SAVE, &fP) == -1) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
}
else {
g_fGSSave->Freeze( fP.size );
if (fP.size) {
g_fGSSave->FreezeMem( fP.data, fP.size );
free(fP.data);
}
}
}
}
}
else if( g_SaveGSStream == 2 ) {
if( --g_nLeftGSFrames <= 0 ) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
Console::WriteLn("Done saving GS stream");
}
}
#endif
}
void frameLimitReset()
{
m_iStart = GetCPUTicks();
@ -401,7 +326,7 @@ static __forceinline void frameLimit()
static __forceinline void VSyncStart(u32 sCycle)
{
EECNT_LOG( "///////// EE COUNTER VSYNC START \\\\\\\\\\\\\\\\\\\\ (frame: %d)\n", iFrame );
vSyncDebugStuff(); // EE Profiling and Debug code
vSyncDebugStuff( iFrame ); // EE Profiling and Debug code
if ((CSRw & 0x8)) GSCSRr|= 0x8;
if (!(GSIMR&0x800)) gsIrq();
@ -512,7 +437,6 @@ __forceinline bool rcntUpdate_vSync()
counters[5].modeval = MODE_VRENDER;
return true;
// SysUpdate(); // check for and handle keyevents
}
else // VSYNC end / VRENDER begin
{

View File

@ -540,7 +540,7 @@ int loadElfFile(const char *filename)
// Reset all recompilers prior to initiating a BIOS or new ELF. The cleaner the
// slate, the happier the recompiler!
SysResetExecutionState();
SysClearExecutionCache();
if( filename == NULL || filename[0] == 0 )
{

94
pcsx2/HostGui.h Normal file
View File

@ -0,0 +1,94 @@
/* 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
*/
#pragma once
//////////////////////////////////////////////////////////////////////////////////////////
// TestRun Parameters.
struct TESTRUNARGS
{
u8 enabled;
u8 jpgcapture;
uint frame; // if == 0, frame is unlimited (run until crash).
int numimages;
int curimage;
u32 autopad; // mask for auto buttons
bool efile;
int snapdone;
const char* ptitle;
const char* pimagename;
const char* plogname;
const char* pgsdll, *pcdvddll, *pspudll;
const char* ppad1dll, *ppad2dll, *pdev9dll;
};
extern TESTRUNARGS g_TestRun;
//////////////////////////////////////////////////////////////////////////////////////////
// Core Gui APIs (shared by all platforms)
//
// Most of these are implemented in SystemGui.cpp
extern void States_Load( const string& file );
extern void States_Save( const string& file );
extern void States_Load( int num );
extern void States_Save( int num );
extern bool States_isSlotUsed(int num);
extern bool g_EmulationInProgress; // Set TRUE if a game is actively running (set to false on reset)
//////////////////////////////////////////////////////////////////////////////////////////
// External Gui APIs (platform specific)
//
// The following section contains API declarations for GUI callback functions that the
// Pcsx2 core expects and needs to be implemented by Pcsx2 platform dependent code
// (Win32/Linux). If anything in this header comes up as a missing external during link,
// it means that the necessary platform dependent files are not being compiled, or the
// platform code is incomplete.
//
// These APIs have been namespaced to help simplify and organize the process of implementing
// them and resolving linker errors.
//
// Namespace housing gui-level implementations relating to events and signals such
// as keyboard events, menu/status updates, and cpu execution invocation.
namespace HostGui
{
// Signal for informing the GUI that the saveslot status has been altered.
// The guis hould re-enumerate the slot information displayed in the menu, or wherever.
extern void ResetMenuSlots();
// Signals to the GUI that execution of the emulator should begin. This can be either
// a blocking or non-blocking (threaded) action.
extern void BeginExecution();
// Signals the gui with a keystroke. Handle or discard or dispatch, or enjoy its
// pleasant flavor.
extern void __fastcall KeyEvent( keyEvent* ev );
// For issuing notices to both the status bar and the console at the same time.
// Single-line text only please! Multi-line msgs should be directed to the
// console directly, thanks.
extern void Notice( const std::string& text );
// sets the contents of the pcsx2 window status bar.
extern void SetStatusMsg( const std::string& text );
};

View File

@ -27,12 +27,6 @@ const char* g_pRunGSState = NULL;
int efile = 0;
char elfname[g_MaxPath];
bool Slots[5] = { false, false, false, false, false };
#ifdef PCSX2_DEVBUILD
TESTRUNARGS g_TestRun;
#endif
char MAIN_DIR[g_MaxPath];
int main(int argc, char *argv[])
@ -275,7 +269,7 @@ void StartGui()
gtk_widget_destroy(lookup_widget(MainWindow, "GtkMenuItem_Debug"));
#endif
CheckSlots();
ResetMenuSlots();
gtk_widget_show_all(MainWindow);
gtk_window_activate_focus(GTK_WINDOW(MainWindow));
@ -312,7 +306,7 @@ void OnLanguage(GtkMenuItem *menuitem, gpointer user_data)
void OnFile_RunCD(GtkMenuItem *menuitem, gpointer user_data)
{
SysReset();
RunExecute(NULL);
SysPrepareExecution(NULL);
}
void OnRunElf_Ok(GtkButton* button, gpointer user_data)
@ -323,7 +317,7 @@ void OnRunElf_Ok(GtkButton* button, gpointer user_data)
strcpy(elfname, File);
gtk_widget_destroy(FileSel);
RunExecute(elfname);
SysPrepareExecution(elfname);
}
void OnRunElf_Cancel(GtkButton* button, gpointer user_data)
@ -400,11 +394,7 @@ void OnFile_Exit(GtkMenuItem *menuitem, gpointer user_data)
void OnEmu_Run(GtkMenuItem *menuitem, gpointer user_data)
{
if (g_EmulationInProgress)
ExecuteCpu();
else
RunExecute(NULL, true); // boots bios if no savestate is to be recovered
SysPrepareExecution(NULL, true); // boots bios if no savestate is to be recovered
}
void OnEmu_Reset(GtkMenuItem *menuitem, gpointer user_data)
@ -412,46 +402,6 @@ void OnEmu_Reset(GtkMenuItem *menuitem, gpointer user_data)
SysReset();
}
void ResetMenuSlots()
{
GtkWidget *Item;
char str[g_MaxPath], str2[g_MaxPath];
int i;
for (i = 0; i < 5; i++)
{
sprintf(str, "load_slot_%d", i);
sprintf(str2, "save_slot_%d", i);
Item = lookup_widget(MainWindow, str);
if GTK_IS_WIDGET(Item)
gtk_widget_set_sensitive(Item, Slots[i]);
else
Console::Error("No such widget: %s", params str);
Item = lookup_widget(MainWindow, str2);
gtk_widget_set_sensitive(Item, (ElfCRC != 0));
}
}
void CheckSlots()
{
int i = 0;
if (ElfCRC == 0) Console::Notice("Disabling game slots until a game is loaded.");
for (i=0; i<5; i++)
{
if (isSlotUsed(i))
Slots[i] = true;
else
Slots[i] = false;
}
ResetMenuSlots();
}
//2002-09-28 (Florin)
void OnArguments_Ok(GtkButton *button, gpointer user_data)
{

View File

@ -20,22 +20,13 @@
#define __LNXMAIN_H__
#include "Linux.h"
#include "HostGui.h"
extern bool applychanges;
extern SafeArray<u8>* g_RecoveryState;
extern bool g_EmulationInProgress; // Set TRUE if a game is actively running (set to false on reset)
extern void RunExecute(const char* elf_file, bool use_bios = false);
extern void ExecuteCpu();
extern void CheckSlots();
extern bool isSlotUsed(int num);
extern bool ParseCommandLine(int argc, char *argv[], char *file);
extern void MemoryCard_Init();
extern void StatusBar_Notice( const std::string& text );
extern void StatusBar_SetMsg( const std::string& text );
void OnLanguage(GtkMenuItem *menuitem, gpointer user_data);
void InitLanguages();
char *GetLanguageNext();

View File

@ -18,16 +18,9 @@
#include "Linux.h"
#include "LnxSysExec.h"
#include "R5900Exceptions.h"
bool UseGui = true;
SafeArray<u8>* g_RecoveryState = NULL;
SafeArray<u8>* g_gsRecoveryState = NULL;
bool g_ReturnToGui = false; // set to exit the execution of the emulator and return control to the GUI
bool g_EmulationInProgress = false; // Set TRUE if a game is actively running (set to false on reset)
static bool sinit = false;
GtkWidget *FileSel;
@ -69,25 +62,6 @@ void SysPageFaultExceptionFilter( int signal, siginfo_t *info, void * )
mmap_ClearCpuBlock( offset & ~m_pagemask );
}
// For issuing notices to both the status bar and the console at the same time.
// Single-line text only please! Mutli-line msgs should be directed to the
// console directly, thanks.
void StatusBar_Notice( const std::string& text )
{
// mirror output to the console!
Console::Status( text.c_str() );
// don't try this in Visual C++ folks!
gtk_statusbar_push(GTK_STATUSBAR(pStatusBar), 0, text.c_str());
}
// Sets the status bar message without mirroring the output to the console.
void StatusBar_SetMsg( const std::string& text )
{
// don't try this in Visual C++ folks!
gtk_statusbar_push(GTK_STATUSBAR(pStatusBar), 0, text.c_str());
}
bool ParseCommandLine(int argc, char *argv[], char *file)
{
int i = 1;
@ -206,410 +180,11 @@ void SysPrintf(const char *fmt, ...)
Console::Write(msg);
}
static void KeyEvent(keyEvent* ev);
void SysUpdate()
{
KeyEvent(PAD1keyEvent());
KeyEvent(PAD2keyEvent());
}
static void TryRecoverFromGsState()
{
if( g_gsRecoveryState != NULL )
{
s32 dummylen;
memLoadingState eddie( *g_gsRecoveryState );
eddie.FreezePlugin( "GS", gsSafeFreeze );
eddie.Freeze( dummylen ); // reads the length value recorded earlier.
eddie.gsFreeze();
}
}
void ExecuteCpu()
{
// Make sure any left-over recovery states are cleaned up.
safe_delete( g_RecoveryState );
// Just in case they weren't initialized earlier (no harm in calling this multiple times)
if (OpenPlugins(NULL) == -1) return;
// this needs to be called for every new game!
// (note: sometimes launching games through bios will give a crc of 0)
if( GSsetGameCRC != NULL ) GSsetGameCRC(ElfCRC, g_ZeroGSOptions);
TryRecoverFromGsState();
safe_delete( g_gsRecoveryState );
// Destroy the window. Ugly thing.
gtk_widget_destroy(MainWindow);
gtk_main_quit();
while (gtk_events_pending()) gtk_main_iteration();
g_EmulationInProgress = true;
g_ReturnToGui = false;
signal(SIGINT, SignalExit);
signal(SIGPIPE, SignalExit);
// Optimization: We hardcode two versions of the EE here -- one for recs and one for ints.
// This is because recs are performance critical, and being able to inline them into the
// function here helps a small bit (not much but every small bit counts!).
try
{
if (CHECK_EEREC)
{
while (!g_ReturnToGui)
{
recExecute();
SysUpdate();
}
}
else
{
while (!g_ReturnToGui)
{
Cpu->Execute();
SysUpdate();
}
}
}
catch( R5900Exception::BaseExcept& ex )
{
Console::Error( ex.cMessage() );
Console::Error( fmt_string( "(EE) PC: 0x%8.8x \tCycle:0x8.8x", ex.cpuState.pc, ex.cpuState.cycle ).c_str() );
}
}
void RunGui()
{
StartGui();
}
// Runs an ELF image directly (ISO or ELF program or BIN)
// Used by Run::FromCD and such
void RunExecute(const char* elf_file, bool use_bios)
{
// (air notes:)
// If you want to use the new to-memory savestate feature, take a look at the new
// RunExecute in WinMain.c, and secondly the CpuDlg.c or AdvancedDlg.cpp. The
// objects used are SafeArray, memLoadingState, and memSavingState.
// It's important to make sure to reset the CPU and the plugins correctly, which is
// where the new RunExecute comes into play. It can be kind of tricky knowing
// when to call cpuExecuteBios and loadElfFile, and with what parameters.
// (or, as an alternative maybe we should switch to wxWidgets and have a unified
// cross platform gui?) - Air
// Sounds like a good idea, at this point.
try
{
cpuReset();
}
catch( Exception::BaseException& ex )
{
Msgbox::Alert( ex.cMessage() );
return;
}
if (OpenPlugins(NULL) == -1)
{
RunGui();
return;
}
if (elf_file == NULL)
{
if (g_RecoveryState != NULL)
{
try
{
memLoadingState(*g_RecoveryState).FreezeAll();
}
catch (std::runtime_error& ex)
{
Msgbox::Alert(
"Gamestate recovery failed. Your game progress will be lost (sorry!)\n"
"\nError: %s\n", params ex.what());
// Take the user back to the GUI...
safe_delete(g_RecoveryState);
ClosePlugins( true );
return;
}
}
else if( g_gsRecoveryState == NULL )
{
// Not recovering a state, so need to execute the bios and load the ELF information.
// if the elf_file is null we use the CDVD elf file.
// But if the elf_file is an empty string then we boot the bios instead.
char ename[g_MaxPath];
ename[0] = 0;
if (!use_bios) GetPS2ElfName(ename);
loadElfFile(ename);
}
}
else
{
// Custom ELF specified (not using CDVD).
// Run the BIOS and load the ELF.
loadElfFile(elf_file);
}
ExecuteCpu();
}
class RecoveryMemSavingState : public memSavingState, Sealed
{
public:
virtual ~RecoveryMemSavingState() { }
RecoveryMemSavingState() : memSavingState( *g_RecoveryState )
{
}
void gsFreeze()
{
if (g_gsRecoveryState != NULL)
{
// just copy the data from src to dst.
// the normal savestate doesn't expect a length prefix for internal structures,
// so don't copy that part.
const u32 pluginlen = *((u32*)g_gsRecoveryState->GetPtr());
const u32 gslen = *((u32*)g_gsRecoveryState->GetPtr(pluginlen+4));
memcpy( m_memory.GetPtr(m_idx), g_gsRecoveryState->GetPtr(pluginlen+8), gslen );
m_idx += gslen;
}
else
memSavingState::gsFreeze();
}
void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) )
{
if ((freezer == gsSafeFreeze) && (g_gsRecoveryState != NULL))
{
// Gs data is already in memory, so just copy from src to dest:
// length of the GS data is stored as the first u32, so use that to run the copy:
const u32 len = *((u32*)g_gsRecoveryState->GetPtr());
memcpy( m_memory.GetPtr(m_idx), g_gsRecoveryState->GetPtr(), len+4 );
m_idx += len+4;
}
else
memSavingState::FreezePlugin( name, freezer );
}
};
class RecoveryZipSavingState : public gzSavingState, Sealed
{
public:
virtual ~RecoveryZipSavingState() { }
RecoveryZipSavingState( const string& filename ) : gzSavingState( filename )
{
}
void gsFreeze()
{
if (g_gsRecoveryState != NULL)
{
// read data from the gsRecoveryState allocation instead of the GS, since the gs
// info was invalidated when the plugin was shut down.
// the normal savestate doesn't expect a length prefix for internal structures,
// so don't copy that part.
u32& pluginlen = *((u32*)g_gsRecoveryState->GetPtr(0));
u32& gslen = *((u32*)g_gsRecoveryState->GetPtr(pluginlen+4));
gzwrite( m_file, g_gsRecoveryState->GetPtr(pluginlen+4), gslen );
}
else
gzSavingState::gsFreeze();
}
void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) )
{
if ((freezer == gsSafeFreeze) && (g_gsRecoveryState != NULL))
{
// Gs data is already in memory, so just copy from there into the gzip file.
// length of the GS data is stored as the first u32, so use that to run the copy:
u32& len = *((u32*)g_gsRecoveryState->GetPtr());
gzwrite( m_file, g_gsRecoveryState->GetPtr(), len+4 );
}
else
gzSavingState::FreezePlugin( name, freezer );
}
};
bool isSlotUsed(int num)
{
if (ElfCRC == 0)
return false;
else
return Path::isFile(SaveState::GetFilename( num ));
}
void States_Load(const string& file, int num = -1)
{
if( !Path::isFile( file ) )
{
Console::Notice( "Saveslot %d is empty.", params num );
return;
}
try
{
char Text[g_MaxPath];
gzLoadingState joe( file ); // this'll throw an StateLoadError_Recoverable.
// Make sure the cpu and plugins are ready to be state-ified!
cpuReset();
OpenPlugins( NULL );
joe.FreezeAll();
if( num != -1 )
sprintf (Text, _("*PCSX2*: Loaded State %d"), num);
else
sprintf (Text, _("*PCSX2*: Loaded State %s"), file.c_str());
StatusBar_Notice( Text );
if( GSsetGameCRC != NULL ) GSsetGameCRC(ElfCRC, g_ZeroGSOptions);
}
catch( Exception::StateLoadError_Recoverable& ex)
{
if( num != -1 )
Console::Notice( "Could not load savestate from slot %d.\n\n%s", params num, ex.cMessage() );
else
Console::Notice( "Could not load savestate file: %s.\n\n%s", params file.c_str(), ex.cMessage() );
// At this point the cpu hasn't been reset, so we can return
// control to the user safely... (that's why we use a console notice instead of a popup)
return;
}
catch( Exception::BaseException& ex )
{
// The emulation state is ruined. Might as well give them a popup and start the gui.
string message;
if( num != -1 )
ssprintf( message,
"Encountered an error while loading savestate from slot %d.\n", num );
else
ssprintf( message,
"Encountered an error while loading savestate from file: %s.\n", file.c_str() );
if( g_EmulationInProgress )
message += "Since the savestate load was incomplete, the emulator has been reset.\n";
message += "\nError: " + ex.Message();
Msgbox::Alert( message.c_str() );
SysClose();
return;
}
// Start emulating!
ExecuteCpu();
}
void States_Load(int num)
{
States_Load( SaveState::GetFilename( num ), num );
}
void States_Save( const string& file, int num = -1 )
{
try
{
string text;
RecoveryZipSavingState( file ).FreezeAll();
if( num != -1 )
ssprintf( text, _( "State saved to slot %d" ), num );
else
ssprintf( text, _( "State saved to file: %s" ), file.c_str() );
StatusBar_Notice( text );
}
catch( Exception::BaseException& ex )
{
string message;
if( num != -1 )
ssprintf( message, "An error occurred while trying to save to slot %d\n", num );
else
ssprintf( message, "An error occurred while trying to save to file: %s\n", file.c_str() );
message += "Your emulation state has not been saved!\n\nError: " + ex.Message();
Console::Error( message.c_str() );
}
CheckSlots();
}
void States_Save(int num)
{
if( g_RecoveryState != NULL )
{
// State is already saved into memory, and the emulator (and in-progress flag)
// have likely been cleared out. So save from the Recovery buffer instead of
// doing a "standard" save:
string text( SaveState::GetFilename( num ) );
gzFile fileptr = gzopen( text.c_str(), "wb" );
if( fileptr == NULL )
{
Msgbox::Alert( _("File permissions error while trying to save to slot %d"), params num );
return;
}
gzwrite( fileptr, &g_SaveVersion, sizeof( u32 ) );
gzwrite( fileptr, g_RecoveryState->GetPtr(), g_RecoveryState->GetSizeInBytes() );
gzclose( fileptr );
return;
}
if( !g_EmulationInProgress )
{
Msgbox::Alert( "You need to start a game first before you can save it's state." );
return;
}
States_Save( SaveState::GetFilename( num ), num );
}
class JustGsSavingState : public memSavingState, Sealed
{
public:
virtual ~JustGsSavingState() { }
JustGsSavingState() : memSavingState( *g_gsRecoveryState )
{
}
// This special override saves the gs info to m_idx+4, and then goes back and
// writes in the length of data saved.
void gsFreeze()
{
int oldmidx = m_idx;
m_idx += 4;
memSavingState::gsFreeze();
if( IsSaving() )
{
s32& len = *((s32*)m_memory.GetPtr( oldmidx ));
len = (m_idx - oldmidx)-4;
}
}
};
void OnStates_Load(GtkMenuItem *menuitem, gpointer user_data)
{
char *name;
@ -626,7 +201,8 @@ void OnStates_Load(GtkMenuItem *menuitem, gpointer user_data)
}
sscanf(name, "Slot %d", &i);
States_Load(i);
if( States_Load(i) )
ExecuteCpu();
}
void OnLoadOther_Ok(GtkButton* button, gpointer user_data)
@ -638,7 +214,8 @@ void OnLoadOther_Ok(GtkButton* button, gpointer user_data)
strcpy(str, File);
gtk_widget_destroy(FileSel);
States_Load(str);
if( States_Load(str) )
ExecuteCpu();
}
void OnLoadOther_Cancel(GtkButton* button, gpointer user_data)
@ -726,156 +303,6 @@ void OnStates_SaveOther(GtkMenuItem *menuitem, gpointer user_data)
#define ALT_EVT(evt) ((evt == XK_Alt_L) || (evt == XK_Alt_R))
#define CAPS_LOCK_EVT(evt) (evt == XK_Caps_Lock)
void KeyEvent(keyEvent* ev)
{
static int shift = 0;
if (ev == NULL) return;
if (GSkeyEvent != NULL) GSkeyEvent(ev);
if (ev->evt == KEYPRESS)
{
if (SHIFT_EVT(ev->key))
shift = 1;
if (CAPS_LOCK_EVT(ev->key))
{
//Set up anything we want to happen while caps lock is down.
}
switch (ev->key)
{
case XK_F1:
case XK_F2:
case XK_F3:
case XK_F4:
case XK_F5:
case XK_F6:
case XK_F7:
case XK_F8:
case XK_F9:
case XK_F10:
case XK_F11:
case XK_F12:
try
{
ProcessFKeys(ev->key - XK_F1 + 1, shift);
}
catch (Exception::CpuStateShutdown&)
{
// Woops! Something was unrecoverable. Bummer.
// Let's give the user a RunGui!
g_EmulationInProgress = false;
g_ReturnToGui = true;
}
break;
case XK_Tab:
CycleFrameLimit(0);
break;
case XK_Escape:
signal(SIGINT, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
#ifdef PCSX2_DEVBUILD
if (g_SaveGSStream >= 3)
{
g_SaveGSStream = 4;// gs state
break;
}
#endif
if( Config.closeGSonEsc )
{
safe_delete( g_gsRecoveryState );
safe_delete( g_RecoveryState );
g_gsRecoveryState = new SafeArray<u8>();
JustGsSavingState eddie;
eddie.FreezePlugin( "GS", gsSafeFreeze ) ;
eddie.gsFreeze();
PluginsResetGS();
}
ClosePlugins( Config.closeGSonEsc );
if (!UseGui) exit(0);
// fixme: The GUI is now capable of receiving control back from the
// emulator. Which means that when I set g_ReturnToGui here, the emulation
// loop in ExecuteCpu() will exit. You should be able to set it up so
// that it returns control to the existing GTK event loop, instead of
// always starting a new one via RunGui(). (but could take some trial and
// error) -- (air)
// Easier said then done; running gtk in two threads at the same time can't be
// done, and working around that is pretty fiddly.
g_ReturnToGui = true;
RunGui();
break;
default:
GSkeyEvent(ev);
break;
}
}
else if (ev->evt == KEYRELEASE)
{
if (SHIFT_EVT(ev->key))
shift = 0;
if (CAPS_LOCK_EVT(ev->key))
{
//Release caps lock
}
}
return;
}
void SysRestorableReset()
{
// already reset? and saved?
if( !g_EmulationInProgress ) return;
if( g_RecoveryState != NULL ) return;
try
{
g_RecoveryState = new SafeArray<u8>( "Memory Savestate Recovery" );
RecoveryMemSavingState().FreezeAll();
safe_delete( g_gsRecoveryState );
g_EmulationInProgress = false;
}
catch( Exception::RuntimeError& ex )
{
Msgbox::Alert(
"Pcsx2 gamestate recovery failed. Some options may have been reverted to protect your game's state.\n"
"Error: %s", params ex.cMessage() );
safe_delete( g_RecoveryState );
}
}
void SysReset()
{
if (!sinit) return;
StatusBar_Notice(_("Resetting..."));
//Console::SetTitle(_("Resetting..."));
g_EmulationInProgress = false;
safe_delete( g_RecoveryState );
safe_delete( g_gsRecoveryState );
ResetPlugins();
ElfCRC = 0;
// Note : No need to call cpuReset() here. It gets called automatically before the
// emulator resumes execution.
StatusBar_Notice(_("Ready"));
//Console::SetTitle(_("*PCSX2* Emulation state is reset."));
}
bool SysInit()
{
if (sinit) return true;
@ -928,34 +355,35 @@ void SysClose()
emuLog = NULL;
}
sinit = false;
// Precautionary extra shutdown stuff.
SysEndExecution();
g_EmulationInProgress = false;
}
void *SysLoadLibrary(const char *lib)
namespace HostSys
{
void *LoadLibrary(const char *lib)
{
return dlopen(lib, RTLD_NOW);
}
void *SysLoadSym(void *lib, const char *sym)
void *LoadSym(void *lib, const char *sym)
{
return dlsym(lib, sym);
}
const char *SysLibError()
const char *LibError()
{
return dlerror();
}
void SysCloseLibrary(void *lib)
void CloseLibrary(void *lib)
{
dlclose(lib);
}
void SysRunGui()
{
RunGui();
}
void *SysMmap(uptr base, u32 size)
void *Mmap(uptr base, u32 size)
{
u8 *Mem;
Mem = (u8*)mmap((uptr*)base, size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
@ -964,12 +392,12 @@ void *SysMmap(uptr base, u32 size)
return Mem;
}
void SysMunmap(uptr base, u32 size)
void Munmap(uptr base, u32 size)
{
munmap((uptr*)base, size);
}
void SysMemProtect( void* baseaddr, size_t size, PageProtectionMode mode, bool allowExecution )
void MemProtect( void* baseaddr, size_t size, PageProtectionMode mode, bool allowExecution )
{
int lnxmode = 0;
@ -986,3 +414,153 @@ void SysMemProtect( void* baseaddr, size_t size, PageProtectionMode mode, bool a
if( allowExecution ) lnxmode |= PROT_EXEC;
mprotect( baseaddr, size, lnxmode );
}
}
namespace HostGui
{
// Sets the status bar message without mirroring the output to the console.
void SetStatusMsg( const string& text )
{
// don't try this in Visual C++ folks!
gtk_statusbar_push(GTK_STATUSBAR(pStatusBar), 0, text.c_str());
}
void Notice( const string& text )
{
// mirror output to the console!
Console::Status( text.c_str() );
SetStatusMsg( test );
}
void ResetMenuSlots()
{
GtkWidget *Item;
char str[g_MaxPath], str2[g_MaxPath];
for (int i = 0; i < 5; i++)
{
sprintf(str, "load_slot_%d", i);
sprintf(str2, "save_slot_%d", i);
Item = lookup_widget(MainWindow, str);
if GTK_IS_WIDGET(Item)
gtk_widget_set_sensitive(Item, Slots[i]);
else
Console::Error("No such widget: %s", params str);
Item = lookup_widget(MainWindow, str2);
gtk_widget_set_sensitive(Item, (ElfCRC != 0));
}
}
void BeginExecution()
{
// Destroy the window. Ugly thing.
gtk_widget_destroy(MainWindow);
gtk_main_quit();
while (gtk_events_pending()) gtk_main_iteration();
signal(SIGINT, SignalExit);
signal(SIGPIPE, SignalExit);
// no try/catch needed since no cleanup needed.. ?
SysExecute();
}
void __fastcall KeyEvent(keyEvent* ev)
{
static int shift = 0;
if (ev == NULL) return;
if (GSkeyEvent != NULL) GSkeyEvent(ev);
if (ev->evt == KEYPRESS)
{
if (SHIFT_EVT(ev->key))
shift = 1;
if (CAPS_LOCK_EVT(ev->key))
{
//Set up anything we want to happen while caps lock is down.
}
switch (ev->key)
{
case XK_F1:
case XK_F2:
case XK_F3:
case XK_F4:
case XK_F5:
case XK_F6:
case XK_F7:
case XK_F8:
case XK_F9:
case XK_F10:
case XK_F11:
case XK_F12:
try
{
ProcessFKeys(ev->key - XK_F1 + 1, shift);
}
catch (Exception::CpuStateShutdown&)
{
// Woops! Something was unrecoverable. Bummer.
// Let's give the user a RunGui!
g_EmulationInProgress = false;
SysEndExecution();
}
break;
case XK_Tab:
CycleFrameLimit(0);
break;
case XK_Escape:
signal(SIGINT, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
#ifdef PCSX2_DEVBUILD
if (g_SaveGSStream >= 3)
{
g_SaveGSStream = 4;// gs state
break;
}
#endif
SysEndExecution();
if (!UseGui) exit(0);
// fixme: The GUI is now capable of receiving control back from the
// emulator. Which means that when we call SysEscapeExecute() here, the
// emulation loop in ExecuteCpu() will exit. You should be able to set it
// up so that it returns control to the existing GTK event loop, instead of
// always starting a new one via RunGui(). (but could take some trial and
// error) -- (air)
// Easier said then done; running gtk in two threads at the same time can't be
// done, and working around that is pretty fiddly.
RunGui();
break;
default:
GSkeyEvent(ev);
break;
}
}
else if (ev->evt == KEYRELEASE)
{
if (SHIFT_EVT(ev->key))
shift = 0;
if (CAPS_LOCK_EVT(ev->key))
{
//Release caps lock
}
}
return;
}
}

View File

@ -10,7 +10,8 @@ Memory.cpp MemoryCard.cpp Misc.cpp Patch.cpp PathUtils.cpp Plugins.cpp Precompil
R3000AInterpreter.cpp R3000AOpcodeTables.cpp R5900.cpp R5900OpcodeImpl.cpp R5900OpcodeTables.cpp \
SPR.cpp SaveState.cpp Sif.cpp Sio.cpp SourceLog.cpp Stats.cpp System.cpp ThreadTools.cpp \
VU0.cpp VU0micro.cpp VU0microInterp.cpp VU1micro.cpp VU1microInterp.cpp VUflags.cpp VUmicroMem.cpp VUops.cpp \
Vif.cpp VifDma.cpp vssprintf.cpp vtlb.cpp xmlpatchloader.cpp AlignedMalloc.cpp
Vif.cpp VifDma.cpp vssprintf.cpp vtlb.cpp xmlpatchloader.cpp AlignedMalloc.cpp \
RecoverySystem.cpp Saveslots.cpp
libpcsx2_a_SOURCES += \
@ -18,6 +19,6 @@ CDVD.h CDVDiso.h CDVDisodrv.h CDVDlib.h COP0.h Cache.h CdRom.h Common.h Counters
Elfheader.h Exceptions.h GS.h Hw.h IopBios.h IopBios2.h IopCounters.h IopDma.h IopHw.h IopMem.h IopSio2.h Memcpyfast.h \
Memory.h MemoryCard.h Misc.h Patch.h Paths.h Plugins.h PrecompiledHeader.h PsxCommon.h R3000A.h R5900.h R5900OpcodeTables.h \
SPR.h SamplProf.h SaveState.h Sif.h Sifcmd.h Sio.h SafeArray.h Stats.h StringUtils.h System.h Threading.h \
VU.h VUflags.h VUmicro.h VUops.h Vif.h VifDma.h cheatscpp.h vtlb.h NakedAsm.h R5900Exceptions.h
VU.h VUflags.h VUmicro.h VUops.h Vif.h VifDma.h cheatscpp.h vtlb.h NakedAsm.h R5900Exceptions.h HostGui.h
SUBDIRS = x86 . DebugTools IPU RDebug tinyxml Linux

View File

@ -651,7 +651,7 @@ void memReset()
{
// VTLB Protection Preparations.
SysMemProtect( m_psAllMem, m_allMemSize, Protect_ReadWrite );
HostSys::MemProtect( m_psAllMem, m_allMemSize, Protect_ReadWrite );
// Note!! Ideally the vtlb should only be initialized once, and then subsequent
// resets of the system hardware would only clear vtlb mappings, but since the
@ -821,7 +821,7 @@ int mmap_GetRamPageInfo(void* ptr)
void mmap_MarkCountedRamPage(void* ptr,u32 vaddr)
{
SysMemProtect( ptr, 1, Protect_ReadOnly );
HostSys::MemProtect( ptr, 1, Protect_ReadOnly );
u32 offset=((u8*)ptr-psM);
offset>>=12;
@ -842,12 +842,12 @@ void mmap_ResetBlockTracking()
{
psMPWVA[i].clear();
}
SysMemProtect( psM, Ps2MemSize::Base, Protect_ReadWrite );
HostSys::MemProtect( psM, Ps2MemSize::Base, Protect_ReadWrite );
}
void mmap_ClearCpuBlock( uint offset )
{
SysMemProtect( &psM[offset], 1, Protect_ReadWrite );
HostSys::MemProtect( &psM[offset], 1, Protect_ReadWrite );
offset>>=12;
psMPWC[(offset/32)]|=(1<<(offset&31));

View File

@ -63,7 +63,7 @@ const char *LabelAuthors = { N_(
"\n"
"Betatesting: Bositman, ChaosCode,\n"
"CKemu, crushtest, GeneralPlot,\n"
"Krakatos, Paorotaku, Rudy_X\n"
"Krakatos, Parotaku, Rudy_X\n"
"\n"
"Webmasters: CKemu, Falcon4ever"
)
@ -611,7 +611,7 @@ void ProcessFKeys(int fkey, int shift)
{
gzLoadingState joe( SaveState::GetFilename( StatesC ) ); // throws exception on version mismatch
cpuReset();
SysResetExecutionState();
SysClearExecutionCache();
joe.FreezeAll();
}
catch( Exception::StateLoadError_Recoverable& )
@ -647,7 +647,7 @@ void ProcessFKeys(int fkey, int shift)
// note: VK_F5-VK_F7 are reserved for GS
case 8:
GSmakeSnapshot("snaps/");
GSmakeSnapshot( SNAPSHOTS_DIR "/" );
break;
#ifdef PCSX2_DEVBUILD

View File

@ -18,6 +18,7 @@ extern char MAIN_DIR[g_MaxPath];
#define SSTATES_DIR "sstates"
#define LANGS_DIR "Langs"
#define LOGS_DIR "logs"
#define SNAPSHOTS_DIR "snaps"
#define DEFAULT_MEMCARD1 "Mcd001.ps2"
#define DEFAULT_MEMCARD2 "Mcd002.ps2"

View File

@ -649,6 +649,7 @@ void ShutdownPlugins()
{
if( !initp ) return;
mtgsWaitGS();
ClosePlugins( true );
if( GSshutdown != NULL )
@ -848,15 +849,6 @@ void ClosePlugins( bool closegs )
}
}
void ResetPlugins()
{
mtgsWaitGS();
ShutdownPlugins();
InitPlugins();
}
void ReleasePlugins()
{
if (!loadp) return;

View File

@ -34,11 +34,7 @@ int OpenPlugins(const char* pTitleFilename);
void ClosePlugins( bool closegs );
int InitPlugins();
// Completely shuts down all plugins and re-initializes them. (clean slate)
// Plugins are not unloaded, so changes to Config.Plugins values will not
// take effect. Use a manual set oc alls to ReleasePlugins and LoadPlugins for that.
void ResetPlugins();
void ShutdownPlugins();
void PluginsResetGS();

View File

@ -58,31 +58,11 @@ bool eeEventTestIsActive = false;
R5900Exception::BaseExcept::~BaseExcept() throw (){}
// A run-once procedure for initializing the emulation state.
// Can be done anytime after allocating memory, and before calling Cpu->Execute().
// Multiple calls to this function are automatically ignored.
/*void cpuInit()
{
DevCon::WriteLn( "cpuInit > %s", params cpuIsInitialized ? "Initializing..." : "Skipping (already initialized)" );
if( cpuIsInitialized ) return;
cpuIsInitialized = true;
// non memInit() currently since we don't support soft resets. memory is initialized in full
// instead during cpuReset() [using memReset()]
//memInit();
}*/
void cpuReset()
{
mtgsWaitGS(); // GS better be done processing before we reset the EE, just in case.
//cpuInit(); // more just-in-caseness!
//if( !cpuIsInitialized )
{
cpuIsInitialized = true;
}
memReset();
psxMemReset();
@ -118,9 +98,6 @@ void cpuReset()
void cpuShutdown()
{
//if( !cpuIsInitialized ) return;
//cpuIsInitialized = false;
mtgsWaitGS();
hwShutdown();

290
pcsx2/RecoverySystem.cpp Normal file
View File

@ -0,0 +1,290 @@
/* 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"
#include "Common.h"
#include "HostGui.h"
//////////////////////////////////////////////////////////////////////////////////////////
// RecoverySystem.cpp -- houses code for recovering from on-the-fly changes to the emu
// configuration, and for saving/restoring the GS state (for more seamless exiting of
// fullscreen GS operation).
//
// The following handful of local classes are implemented att he bottom of this file.
static SafeArray<u8>* g_RecoveryState = NULL;
static SafeArray<u8>* g_gsRecoveryState = NULL;
// This class type creates a memory savestate using the existing Recovery information
// (if present) to generate the savestate material. If no recovery data is present,
// the current emulation state is used instead.
class RecoveryMemSavingState : public memSavingState, Sealed
{
public:
virtual ~RecoveryMemSavingState() { }
RecoveryMemSavingState();
void gsFreeze();
void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) );
};
// This class type creates an on-disk (zipped) savestate using the existing Recovery
// information (if present) to generate the savestate material. If no recovery data is
// present, the current emulation state is used instead.
class RecoveryZipSavingState : public gzSavingState, Sealed
{
public:
virtual ~RecoveryZipSavingState() { }
RecoveryZipSavingState( const string& filename );
void gsFreeze();
void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) );
};
// Special helper class used to save *just* the GS-relevant state information.
class JustGsSavingState : public memSavingState, Sealed
{
public:
virtual ~JustGsSavingState() { }
JustGsSavingState();
// This special override saves the gs info to m_idx+4, and then goes back and
// writes in the length of data saved.
void gsFreeze();
};
namespace StateRecovery {
bool HasState()
{
return g_RecoveryState != NULL || g_gsRecoveryState != NULL;
}
// Exceptions:
void Recover()
{
// Just in case they weren't initialized earlier (no harm in calling this multiple times)
if( OpenPlugins(NULL) == -1 ) return;
if( g_RecoveryState != NULL )
{
Console::Status( "Resuming execution from full memory state..." );
memLoadingState( *g_RecoveryState ).FreezeAll();
}
else if( g_gsRecoveryState != NULL )
{
s32 dummylen;
Console::Status( "Resuming execution from gsState..." );
memLoadingState eddie( *g_gsRecoveryState );
eddie.FreezePlugin( "GS", gsSafeFreeze );
eddie.Freeze( dummylen ); // reads the length value recorded earlier.
eddie.gsFreeze();
}
StateRecovery::Clear();
// this needs to be called for every new game!
// (note: sometimes launching games through bios will give a crc of 0)
if( GSsetGameCRC != NULL )
GSsetGameCRC(ElfCRC, g_ZeroGSOptions);
}
// Saves recovery state info to the given filename, or saves the active emulation state
// (if one exists) and no recovery data was found. This is needed because when a recovery
// state is made, the emulation state is usually reset so the only persisting state is
// the one in the memory save. :)
void SaveToFile( const string& file )
{
if( g_RecoveryState != NULL )
{
// State is already saved into memory, and the emulator (and in-progress flag)
// have likely been cleared out. So save from the Recovery buffer instead of
// doing a "standard" save:
gzFile fileptr = gzopen( file.c_str(), "wb" );
if( fileptr == NULL )
{
Msgbox::Alert( _("File permissions error while trying to save to file:\n\t%ts"), params &file );
return;
}
gzwrite( fileptr, &g_SaveVersion, sizeof( u32 ) );
gzwrite( fileptr, g_RecoveryState->GetPtr(), g_RecoveryState->GetSizeInBytes() );
gzclose( fileptr );
}
else if( g_gsRecoveryState != NULL )
{
RecoveryZipSavingState( file ).FreezeAll();
}
else
{
if( !g_EmulationInProgress )
{
Msgbox::Alert( "You need to start a game first before you can save it's state." );
return;
}
States_Save( file );
}
}
// Saves recovery state info to the given saveslot, or saves the active emulation state
// (if one exists) and no recovery data was found. This is needed because when a recovery
// state is made, the emulation state is usually reset so the only persisting state is
// the one in the memory save. :)
void SaveToSlot( uint num )
{
SaveToFile( SaveState::GetFilename( num ) );
}
// This method will override any existing recovery states, so call it with caution, if you
// think that there could be existing important state info in the recovery buffers (but
// really there shouldn't be, unless you're calling this function when it's not intended
// to be called).
void MakeGsOnly()
{
StateRecovery::Clear();
if( !g_EmulationInProgress ) return;
g_gsRecoveryState = new SafeArray<u8>();
JustGsSavingState eddie;
eddie.FreezePlugin( "GS", gsSafeFreeze ) ;
eddie.gsFreeze();
}
// Creates a full recovery of the entire emulation state (CPU and all plugins).
// If a current recovery state is already present, then nothing is done (the
// existing recovery state takes precedence).
void MakeFull()
{
if( g_RecoveryState != NULL ) return;
try
{
g_RecoveryState = new SafeArray<u8>( "Memory Savestate Recovery" );
RecoveryMemSavingState().FreezeAll();
safe_delete( g_gsRecoveryState );
g_EmulationInProgress = false;
}
catch( Exception::RuntimeError& ex )
{
Msgbox::Alert(
"Pcsx2 gamestate recovery failed. Some options may have been reverted to protect your game's state.\n"
"Error: %s", params ex.cMessage() );
safe_delete( g_RecoveryState );
}
}
// Clears and deallocates any recovery states.
void Clear()
{
safe_delete( g_RecoveryState );
safe_delete( g_gsRecoveryState );
}
}
RecoveryMemSavingState::RecoveryMemSavingState() : memSavingState( *g_RecoveryState )
{
}
void RecoveryMemSavingState::gsFreeze()
{
if( g_gsRecoveryState != NULL )
{
// just copy the data from src to dst.
// the normal savestate doesn't expect a length prefix for internal structures,
// so don't copy that part.
const u32 pluginlen = *((u32*)g_gsRecoveryState->GetPtr());
const u32 gslen = *((u32*)g_gsRecoveryState->GetPtr(pluginlen+4));
memcpy( m_memory.GetPtr(m_idx), g_gsRecoveryState->GetPtr(pluginlen+8), gslen );
m_idx += gslen;
}
else
memSavingState::gsFreeze();
}
void RecoveryMemSavingState::FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) )
{
if( (freezer == gsSafeFreeze) && (g_gsRecoveryState != NULL) )
{
// Gs data is already in memory, so just copy from src to dest:
// length of the GS data is stored as the first u32, so use that to run the copy:
const u32 len = *((u32*)g_gsRecoveryState->GetPtr());
memcpy( m_memory.GetPtr(m_idx), g_gsRecoveryState->GetPtr(), len+4 );
m_idx += len+4;
}
else
memSavingState::FreezePlugin( name, freezer );
}
RecoveryZipSavingState::RecoveryZipSavingState( const string& filename ) : gzSavingState( filename )
{
}
void RecoveryZipSavingState::gsFreeze()
{
if( g_gsRecoveryState != NULL )
{
// read data from the gsRecoveryState allocation instead of the GS, since the gs
// info was invalidated when the plugin was shut down.
// the normal savestate doesn't expect a length prefix for internal structures,
// so don't copy that part.
u32& pluginlen = *((u32*)g_gsRecoveryState->GetPtr(0));
u32& gslen = *((u32*)g_gsRecoveryState->GetPtr(pluginlen+4));
gzwrite( m_file, g_gsRecoveryState->GetPtr(pluginlen+4), gslen );
}
else
gzSavingState::gsFreeze();
}
void RecoveryZipSavingState::FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) )
{
if( (freezer == gsSafeFreeze) && (g_gsRecoveryState != NULL) )
{
// Gs data is already in memory, so just copy from there into the gzip file.
// length of the GS data is stored as the first u32, so use that to run the copy:
u32& len = *((u32*)g_gsRecoveryState->GetPtr());
gzwrite( m_file, g_gsRecoveryState->GetPtr(), len+4 );
}
else
gzSavingState::FreezePlugin( name, freezer );
}
JustGsSavingState::JustGsSavingState() : memSavingState( *g_gsRecoveryState )
{
}
// This special override saves the gs info to m_idx+4, and then goes back and
// writes in the length of data saved.
void JustGsSavingState::gsFreeze()
{
int oldmidx = m_idx;
m_idx += 4;
memSavingState::gsFreeze();
if( IsSaving() )
{
s32& len = *((s32*)m_memory.GetPtr( oldmidx ));
len = (m_idx - oldmidx)-4;
}
}

View File

@ -62,7 +62,7 @@ extern void pcsx2_aligned_free(void* pmem);
#define SafeSysMunmap( ptr, size ) \
if( ptr != NULL ) { \
SysMunmap( (uptr)ptr, size ); \
HostSys::Munmap( (uptr)ptr, size ); \
ptr = NULL; \
}

View File

@ -32,7 +32,6 @@
#include "COP0.h"
#include "Cache.h"
using namespace R5900;
extern int g_psxWriteOk;
@ -41,7 +40,7 @@ extern void recResetIOP();
static void PreLoadPrep()
{
SysResetExecutionState();
SysClearExecutionCache();
}
static void PostLoadPrep()

View File

@ -200,4 +200,15 @@ public:
bool IsSaving() const { return false; }
};
namespace StateRecovery
{
extern bool HasState();
extern void Recover();
extern void SaveToFile( const string& file );
extern void SaveToSlot( uint num );
extern void MakeGsOnly();
extern void MakeFull();
extern void Clear();
}
#endif

251
pcsx2/Saveslots.cpp Normal file
View File

@ -0,0 +1,251 @@
/* 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"
#include "Common.h"
#include "HostGui.h"
#include "GS.h"
TESTRUNARGS g_TestRun;
//////////////////////////////////////////////////////////////////////////////////////////
// Save Slot Detection System
static int Slots[5] = { -1, -1, -1, -1, -1 };
bool States_isSlotUsed(int num)
{
if (ElfCRC == 0)
return false;
else
return Path::isFile(SaveState::GetFilename( num ));
}
//////////////////////////////////////////////////////////////////////////////////////////
// Save state load-from-file (or slot) helpers.
// Internal use state loading function which does not trap exceptions.
// The calling function should trap ahnd handle exceptions as needed.
static void _loadStateOrExcept( const string& file )
{
gzLoadingState joe( file ); // this'll throw an StateLoadError_Recoverable.
// Make sure the cpu and plugins are ready to be state-ified!
cpuReset();
OpenPlugins( NULL );
joe.FreezeAll();
if( GSsetGameCRC != NULL )
GSsetGameCRC(ElfCRC, g_ZeroGSOptions);
}
// returns true if the new state was loaded, or false if nothing happened.
void States_Load( const string& file )
{
try
{
_loadStateOrExcept( file );
HostGui::Notice( fmt_string(_("*PCSX2*: Loaded State %s"), file) );
}
catch( Exception::StateLoadError_Recoverable& ex)
{
Console::Notice( "Could not load savestate file: %s.\n\n%s", params file, ex.cMessage() );
// At this point the cpu hasn't been reset, so we can return
// control to the user safely... (that's why we use a console notice instead of a popup)
return;
}
catch( Exception::BaseException& ex )
{
// The emulation state is ruined. Might as well give them a popup and start the gui.
string message( fmt_string(
"Encountered an error while loading savestate from file: %s.\n", file ) );
if( g_EmulationInProgress )
message += "Since the savestate load was incomplete, the emulator must reset.\n";
message += "\nError: " + ex.Message();
Msgbox::Alert( message.c_str() );
SysReset();
return;
}
HostGui::BeginExecution();
}
void States_Load(int num)
{
string file( SaveState::GetFilename( num ) );
if( !Path::isFile( file ) )
{
Console::Notice( "Saveslot %d is empty.", params num );
return;
}
try
{
_loadStateOrExcept( file );
HostGui::Notice( fmt_string( _("*PCSX2*: Loaded State %d"), num ) );
}
catch( Exception::StateLoadError_Recoverable& ex)
{
Console::Notice( "Could not load savestate slot %d.\n\n%s", params num, ex.cMessage() );
// At this point the cpu hasn't been reset, so we can return
// control to the user safely... (that's why we use a console notice instead of a popup)
return;
}
catch( Exception::BaseException& ex )
{
// The emulation state is ruined. Might as well give them a popup and start the gui.
string message( fmt_string(
"Encountered an error while loading savestate from slot %d.\n", num ) );
if( g_EmulationInProgress )
message += "Since the savestate load was incomplete, the emulator has been reset.\n";
message += "\nError: " + ex.Message();
Msgbox::Alert( message.c_str() );
SysEndExecution();
return;
}
HostGui::BeginExecution();
}
//////////////////////////////////////////////////////////////////////////////////////////
// Save state save-to-file (or slot) helpers.
void States_Save( const string& file )
{
try
{
StateRecovery::SaveToFile( file );
HostGui::Notice( fmt_string( _( "State saved to file: %s" ), file ) );
}
catch( Exception::BaseException& ex )
{
Console::Error( (fmt_string(
"An error occurred while trying to save to file %s\n", file ) +
"Your emulation state has not been saved!\n\nError: " + ex.Message()).c_str()
);
}
// Filename could be a valid slot, so still need to update
HostGui::ResetMenuSlots();
}
void States_Save(int num)
{
try
{
StateRecovery::SaveToSlot( num );
HostGui::Notice( fmt_string( _( "State saved to slot %d" ) , num ) );
}
catch( Exception::BaseException& ex )
{
Console::Error( (fmt_string(
"An error occurred while trying to save to slot %d\n", num ) +
"Your emulation state has not been saved!\n\nError: " + ex.Message()).c_str()
);
}
HostGui::ResetMenuSlots();
}
//////////////////////////////////////////////////////////////////////////////////////////
//
void vSyncDebugStuff( uint frame )
{
#ifdef PCSX2_DEVBUILD
if( g_TestRun.enabled && g_TestRun.frame > 0 ) {
if( frame > g_TestRun.frame ) {
// take a snapshot
if( g_TestRun.pimagename != NULL && GSmakeSnapshot2 != NULL ) {
if( g_TestRun.snapdone ) {
g_TestRun.curimage++;
g_TestRun.snapdone = 0;
g_TestRun.frame += 20;
if( g_TestRun.curimage >= g_TestRun.numimages ) {
// exit
SysEndExecution();
}
}
else {
// query for the image
GSmakeSnapshot2(g_TestRun.pimagename, &g_TestRun.snapdone, g_TestRun.jpgcapture);
}
}
else {
// exit
SysEndExecution();
}
}
}
GSVSYNC();
if( g_SaveGSStream == 1 ) {
freezeData fP;
g_SaveGSStream = 2;
g_fGSSave->gsFreeze();
if (GSfreeze(FREEZE_SIZE, &fP) == -1) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
}
else {
fP.data = (s8*)malloc(fP.size);
if (fP.data == NULL) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
}
else {
if (GSfreeze(FREEZE_SAVE, &fP) == -1) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
}
else {
g_fGSSave->Freeze( fP.size );
if (fP.size) {
g_fGSSave->FreezeMem( fP.data, fP.size );
free(fP.data);
}
}
}
}
}
else if( g_SaveGSStream == 2 ) {
if( --g_nLeftGSFrames <= 0 ) {
safe_delete( g_fGSSave );
g_SaveGSStream = 0;
Console::WriteLn("Done saving GS stream");
}
}
#endif
}

View File

@ -19,14 +19,16 @@
#include "PrecompiledHeader.h"
#include "Common.h"
#include "VUmicro.h"
#include "Threading.h"
#include "HostGui.h"
#include "VUmicro.h"
#include "iR5900.h"
#include "R3000A.h"
#include "IopMem.h"
#include "iVUzerorec.h" // for SuperVUReset
#include "R5900Exceptions.h"
using namespace std;
using namespace Console;
@ -279,12 +281,16 @@ void SysShutdownDynarecs()
recCpu.Shutdown();
}
// Resets all PS2 cpu execution states, which does not affect that actual PS2 state/condition.
bool g_ReturnToGui = false; // set to exit the execution of the emulator and return control to the GUI
bool g_EmulationInProgress = false; // Set TRUE if a game is actively running (set to false on reset)
// Resets all PS2 cpu execution caches, which does not affect that actual PS2 state/condition.
// This can be called at any time outside the context of a Cpu->Execute() block without
// bad things happening (recompilers will slow down for a brief moment since rec code blocks
// are dumped).
// Use this method to reset the recs when important global pointers like the MTGS are re-assigned.
void SysResetExecutionState()
void SysClearExecutionCache()
{
if( CHECK_EEREC )
{
@ -306,9 +312,146 @@ void SysResetExecutionState()
vu1MicroDisableSkip();
}
__forceinline void SysUpdate()
{
keyEvent* ev1 = PAD1keyEvent();
keyEvent* ev2 = PAD2keyEvent();
HostGui::KeyEvent( (ev1 != NULL) ? ev1 : ev2);
}
void SysExecute()
{
g_EmulationInProgress = true;
g_ReturnToGui = false;
// Optimization: We hardcode two versions of the EE here -- one for recs and one for ints.
// This is because recs are performance critical, and being able to inline them into the
// function here helps a small bit (not much but every small bit counts!).
try
{
if( CHECK_EEREC )
{
while( !g_ReturnToGui )
{
recExecute();
SysUpdate();
}
}
else
{
while( !g_ReturnToGui )
{
Cpu->Execute();
SysUpdate();
}
}
}
catch( R5900Exception::BaseExcept& ex )
{
Console::Error( ex.cMessage() );
Console::Error( fmt_string( "(EE) PC: 0x%8.8x \tCycle:0x8.8x", ex.cpuState.pc, ex.cpuState.cycle ).c_str() );
}
}
// Function provided to escape the emulation state, by shutting down plugins and saving
// the GS state. The execution state is effectively preserved, and can be resumed with a
// call to SysExecute.
void SysEndExecution()
{
if( Config.closeGSonEsc )
StateRecovery::MakeGsOnly();
ClosePlugins( Config.closeGSonEsc );
g_ReturnToGui = true;
}
// Runs an ELF image directly (ISO or ELF program or BIN)
// Used by Run::FromCD, and Run->Execute when no active emulation state is present.
// elf_file - if NULL, the CDVD plugin is queried for the ELF file.
// use_bios - forces the game to boot through the PS2 bios, instead of bypassing it.
void SysPrepareExecution( const char* elf_file, bool use_bios )
{
if( !g_EmulationInProgress )
{
try
{
cpuReset();
}
catch( Exception::BaseException& ex )
{
Msgbox::Alert( ex.cMessage() );
return;
}
if (OpenPlugins(g_TestRun.ptitle) == -1)
return;
if( elf_file == NULL )
{
if( !StateRecovery::HasState() )
{
// Not recovering a state, so need to execute the bios and load the ELF information.
// (note: gsRecoveries are done from ExecuteCpu)
// if the elf_file is null we use the CDVD elf file.
// But if the elf_file is an empty string then we boot the bios instead.
char ename[g_MaxPath];
ename[0] = 0;
if( !use_bios )
GetPS2ElfName( ename );
loadElfFile( ename );
}
}
else
{
// Custom ELF specified (not using CDVD).
// Run the BIOS and load the ELF.
loadElfFile( elf_file );
}
}
StateRecovery::Recover();
HostGui::BeginExecution();
}
void SysRestorableReset()
{
if( !g_EmulationInProgress ) return;
StateRecovery::MakeFull();
}
void SysReset()
{
// fixme - this code sets the statusbar but never returns control to the window message pump
// so the status bar won't receive the WM_PAINT messages needed to update itself anyway.
// Oops! (air)
HostGui::Notice(_("Resetting..."));
Console::SetTitle(_("Resetting..."));
g_EmulationInProgress = false;
StateRecovery::Clear();
cpuShutdown();
ShutdownPlugins();
ElfCRC = 0;
// Note : No need to call cpuReset() here. It gets called automatically before the
// emulator resumes execution.
HostGui::Notice(_("Ready"));
Console::SetTitle(_("*PCSX2* Emulation state is reset."));
}
u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller)
{
u8 *Mem = (u8*)SysMmap( base, size );
u8 *Mem = (u8*)HostSys::Mmap( base, size );
if( (Mem == NULL) || (bounds != 0 && (((uptr)Mem + size) > bounds)) )
{
@ -319,7 +462,7 @@ u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller)
SafeSysMunmap( Mem, size );
Mem = (u8*)SysMmap( NULL, size );
Mem = (u8*)HostSys::Mmap( NULL, size );
if( bounds != 0 && (((uptr)Mem + size) > bounds) )
{
DevCon::Error( "Fatal Error:\n\tSecond try failed allocating %s, block ptr 0x%x does not meet required criteria.", params caller, Mem );
@ -331,3 +474,7 @@ u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller)
return Mem;
}
void *SysLoadLibrary(const char *lib) { return HostSys::LoadLibrary( lib ); }
void *SysLoadSym(void *lib, const char *sym) { return HostSys::LoadSym( lib, sym ); }
const char *SysLibError() { return HostSys::LibError(); }
void SysCloseLibrary(void *lib) { HostSys::CloseLibrary( lib ); }

View File

@ -25,41 +25,6 @@
#include "MemcpyFast.h"
#include "SafeArray.h"
void SysDetect(); // Detects cpu type and fills cpuInfo structs.
bool SysInit(); // Init logfiles, directories, critical memory resources, and other OS-specific one-time
void SysReset(); // Resets the various PS2 cpus, sub-systems, and recompilers.
void SysUpdate(); // Called on VBlank (to update i.e. pads)
void SysClose(); // Close mem and plugins
bool SysAllocateMem(); // allocates memory for all PS2 systems; returns FALSe on critical error.
void SysAllocateDynarecs(); // allocates memory for all dynarecs, and force-disables any failures.
void SysShutdownDynarecs();
void SysShutdownMem();
void SysResetExecutionState();
void *SysLoadLibrary(const char *lib); // Loads Library
void *SysLoadSym(void *lib, const char *sym); // Loads Symbol from Library
const char *SysLibError(); // Gets previous error loading symbols
void SysCloseLibrary(void *lib); // Closes Library
// Maps a block of memory for use as a recompiled code buffer.
// The allocated block has code execution privileges.
// Returns NULL on allocation failure.
void *SysMmap(uptr base, u32 size);
// Maps a block of memory for use as a recompiled code buffer, and ensures that the
// allocation is below a certain memory address (specified in "bounds" parameter).
// The allocated block has code execution privileges.
// Returns NULL on allocation failure.
u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller="Unnamed");
// Unmaps a block allocated by SysMmap
void SysMunmap(uptr base, u32 size);
static __forceinline void SysMunmap( void* base, u32 size )
{
SysMunmap( (uptr)base, size );
}
enum PageProtectionMode
{
@ -68,7 +33,72 @@ enum PageProtectionMode
Protect_ReadWrite
};
void SysMemProtect( void* baseaddr, size_t size, PageProtectionMode mode, bool allowExecution=false );
//////////////////////////////////////////////////////////////////////////////////////////
// HostSys - Namespace housing general system-level implementations relating to loading
// plugins and allocating memory. For now, these functions are all accessed via Sys*
// versions defined in System.h/cpp.
namespace HostSys
{
// Damn windows.h namespace pollution!!
#undef LoadLibrary
extern void *LoadLibrary(const char *lib); // Loads Library
extern void *LoadSym(void *lib, const char *sym); // Loads Symbol from Library
extern const char *LibError(); // Gets previous error loading symbols
extern void CloseLibrary(void *lib); // Closes Library
// Maps a block of memory for use as a recompiled code buffer.
// The allocated block has code execution privileges.
// Returns NULL on allocation failure.
extern void *Mmap(uptr base, u32 size);
// Unmaps a block allocated by SysMmap
extern void Munmap(uptr base, u32 size);
extern void MemProtect( void* baseaddr, size_t size, PageProtectionMode mode, bool allowExecution=false );
static __forceinline void Munmap( void* base, u32 size )
{
Munmap( (uptr)base, size );
}
}
extern void SysDetect(); // Detects cpu type and fills cpuInfo structs.
extern void SysReset(); // Resets the various PS2 cpus, sub-systems, and recompilers.
extern void SysUpdate(); // Called on VBlank (to update i.e. pads)
extern bool SysAllocateMem(); // allocates memory for all PS2 systems; returns FALSe on critical error.
extern void SysAllocateDynarecs(); // allocates memory for all dynarecs, and force-disables any failures.
extern void SysShutdownDynarecs();
extern void SysShutdownMem();
extern void SysRestorableReset(); // Saves the current emulation state prior to spu reset.
extern void SysClearExecutionCache(); // clears recompiled execution caches!
extern void SysEndExecution(); // terminates plugins, saves GS state (if enabled), and signals emulation loop to end.
extern void SysPrepareExecution( const char* elf_file, bool use_bios=false );
// initiates high-speed execution of the emulation state. This function is currently
// designed to be run from an event loop, but will eventually be re-tooled with threading
// in mindunder the new GUI (assuming Linux approves!), which will make life easier *and*
// improve overall performance too!
extern void SysExecute();
// Library Helpers for HostSys functions, left in for now for convenience.
extern void *SysLoadLibrary(const char *lib); // Loads Library
extern void *SysLoadSym(void *lib, const char *sym); // Loads Symbol from Library
extern const char *SysLibError(); // Gets previous error loading symbols
extern void SysCloseLibrary(void *lib); // Closes Library
// Maps a block of memory for use as a recompiled code buffer, and ensures that the
// allocation is below a certain memory address (specified in "bounds" parameter).
// The allocated block has code execution privileges.
// Returns NULL on allocation failure.
extern u8 *SysMmapEx(uptr base, u32 size, uptr bounds, const char *caller="Unnamed");
extern void vSyncDebugStuff( uint frame );
// Writes text to the console.
// *DEPRECIATED* Use Console namespace methods instead.

View File

@ -581,7 +581,7 @@ void vtlb_free( void* pmem, uint size )
SafeSysMunmap( pmem, size );
#else
// Make sure and unprotect memory first, since CrtDebug will try to write to it.
SysMemProtect( pmem, size, Protect_ReadWrite );
HostSys::MemProtect( pmem, size, Protect_ReadWrite );
safe_aligned_free( pmem );
#endif
}

View File

@ -21,28 +21,29 @@
#include "AboutDlg.h"
#include "Common.h"
#include "Hyperlinks.h"
#define IDC_STATIC (-1)
HWND hW;
HBITMAP hBMP, hSilverBMP;
static HWND hW;
static HBITMAP hSilverBMP;
LRESULT WINAPI AboutDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
//hBMP = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(SPLASH_LOGO));
hSilverBMP = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_PS2SILVER));
/*hW = CreateWindow("STATIC", "", WS_VISIBLE | WS_CHILD | SS_BITMAP,
230, 10, 211, 110, hDlg, (HMENU)IDC_STATIC, GetModuleHandle(NULL), NULL);
SendMessage(hW, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBMP);*/
SetWindowText(hDlg, _("About PCSX2"));
Button_SetText(GetDlgItem(hDlg, IDOK), _("OK"));
Static_SetText(GetDlgItem(hDlg, IDC_PCSX_ABOUT_AUTHORS), _(LabelAuthors));
Static_SetText(GetDlgItem(hDlg, IDC_PCSX_ABOUT_GREETS), _(LabelGreets));
ConvertStaticToHyperlink( hDlg, IDC_LINK_GOOGLECODE );
ConvertStaticToHyperlink( hDlg, IDC_LINK_WEBSITE );
return TRUE;
case WM_COMMAND:
@ -51,6 +52,16 @@ LRESULT WINAPI AboutDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
case IDOK:
EndDialog( hDlg, TRUE );
return TRUE;
case IDC_LINK_WEBSITE:
ShellExecute(hDlg, "open", "http://www.pcsx2.net/",
NULL, NULL, SW_SHOWNORMAL);
return TRUE;
case IDC_LINK_GOOGLECODE:
ShellExecute(hDlg, "open", "http://code.google.com/p/pcsx2",
NULL, NULL, SW_SHOWNORMAL);
return TRUE;
}
return FALSE;

View File

@ -63,7 +63,7 @@ struct ComboInitializer
bool LoadNextLibrary()
{
string tmpStr( Path::Combine( Config.PluginsDir, FindData.cFileName ) );
Lib = LoadLibrary( tmpStr.c_str() );
Lib = HostSys::LoadLibrary( tmpStr.c_str() );
if (Lib == NULL)
{
Console::Error( "Plugin load failure: %hs\n\tSysLibError Message: %s", params &tmpStr, SysLibError() );

View File

@ -0,0 +1,167 @@
// Hyperlinks.cpp
//
// Copyright 2002 Neal Stublen
// All rights reserved.
//
// http://www.awesoftware.com
//
// This taken as found on Codeguru: http://www.codeguru.com/cpp/controls/staticctrl/article.php/c5803
#include "Win32.h"
#include "Hyperlinks.h"
#define PROP_ORIGINAL_FONT TEXT("_Hyperlink_Original_Font_")
#define PROP_ORIGINAL_PROC TEXT("_Hyperlink_Original_Proc_")
#define PROP_STATIC_HYPERLINK TEXT("_Hyperlink_From_Static_")
#define PROP_UNDERLINE_FONT TEXT("_Hyperlink_Underline_Font_")
LRESULT CALLBACK _HyperlinkParentProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
WNDPROC pfnOrigProc = (WNDPROC) GetProp(hwnd, PROP_ORIGINAL_PROC);
switch (message)
{
case WM_CTLCOLORSTATIC:
{
HDC hdc = (HDC) wParam;
HWND hwndCtl = (HWND) lParam;
BOOL fHyperlink = (NULL != GetProp(hwndCtl, PROP_STATIC_HYPERLINK));
if (fHyperlink)
{
LRESULT lr = CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
SetTextColor(hdc, RGB(0, 0, 192));
return lr;
}
break;
}
case WM_DESTROY:
{
SetWindowLong(hwnd, GWL_WNDPROC, (LONG) pfnOrigProc);
RemoveProp(hwnd, PROP_ORIGINAL_PROC);
break;
}
}
return CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
}
LRESULT CALLBACK _HyperlinkProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
WNDPROC pfnOrigProc = (WNDPROC) GetProp(hwnd, PROP_ORIGINAL_PROC);
switch (message)
{
case WM_DESTROY:
{
SetWindowLong(hwnd, GWL_WNDPROC, (LONG) pfnOrigProc);
RemoveProp(hwnd, PROP_ORIGINAL_PROC);
HFONT hOrigFont = (HFONT) GetProp(hwnd, PROP_ORIGINAL_FONT);
SendMessage(hwnd, WM_SETFONT, (WPARAM) hOrigFont, 0);
RemoveProp(hwnd, PROP_ORIGINAL_FONT);
HFONT hFont = (HFONT) GetProp(hwnd, PROP_UNDERLINE_FONT);
DeleteObject(hFont);
RemoveProp(hwnd, PROP_UNDERLINE_FONT);
RemoveProp(hwnd, PROP_STATIC_HYPERLINK);
break;
}
case WM_MOUSEMOVE:
{
if (GetCapture() != hwnd)
{
HFONT hFont = (HFONT) GetProp(hwnd, PROP_UNDERLINE_FONT);
SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, FALSE);
InvalidateRect(hwnd, NULL, FALSE);
SetCapture(hwnd);
}
else
{
RECT rect;
GetWindowRect(hwnd, &rect);
POINT pt = { LOWORD(lParam), HIWORD(lParam) };
ClientToScreen(hwnd, &pt);
if (!PtInRect(&rect, pt))
{
HFONT hFont = (HFONT) GetProp(hwnd, PROP_ORIGINAL_FONT);
SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, FALSE);
InvalidateRect(hwnd, NULL, FALSE);
ReleaseCapture();
}
}
break;
}
case WM_SETCURSOR:
{
// Since IDC_HAND is not available on all operating systems,
// we will load the arrow cursor if IDC_HAND is not present.
HCURSOR hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND));
if (NULL == hCursor)
{
hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
}
SetCursor(hCursor);
return TRUE;
}
}
return CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
}
BOOL ConvertStaticToHyperlink(HWND hwndCtl)
{
// Subclass the parent so we can color the controls as we desire.
HWND hwndParent = GetParent(hwndCtl);
if (NULL != hwndParent)
{
WNDPROC pfnOrigProc = (WNDPROC) GetWindowLong(hwndParent, GWL_WNDPROC);
if (pfnOrigProc != _HyperlinkParentProc)
{
SetProp(hwndParent, PROP_ORIGINAL_PROC, (HANDLE) pfnOrigProc);
SetWindowLong(hwndParent, GWL_WNDPROC, (LONG) (WNDPROC) _HyperlinkParentProc);
}
}
// Make sure the control will send notifications.
DWORD dwStyle = GetWindowLong(hwndCtl, GWL_STYLE);
SetWindowLong(hwndCtl, GWL_STYLE, dwStyle | SS_NOTIFY);
// Subclass the existing control.
WNDPROC pfnOrigProc = (WNDPROC) GetWindowLong(hwndCtl, GWL_WNDPROC);
SetProp(hwndCtl, PROP_ORIGINAL_PROC, (HANDLE) pfnOrigProc);
SetWindowLong(hwndCtl, GWL_WNDPROC, (LONG) (WNDPROC) _HyperlinkProc);
// Create an updated font by adding an underline.
HFONT hOrigFont = (HFONT) SendMessage(hwndCtl, WM_GETFONT, 0, 0);
SetProp(hwndCtl, PROP_ORIGINAL_FONT, (HANDLE) hOrigFont);
LOGFONT lf;
GetObject(hOrigFont, sizeof(lf), &lf);
lf.lfUnderline = TRUE;
HFONT hFont = CreateFontIndirect(&lf);
SetProp(hwndCtl, PROP_UNDERLINE_FONT, (HANDLE) hFont);
// Set a flag on the control so we know what color it should be.
SetProp(hwndCtl, PROP_STATIC_HYPERLINK, (HANDLE) 1);
return TRUE;
}
BOOL ConvertStaticToHyperlink(HWND hwndParent, UINT uiCtlId)
{
return ConvertStaticToHyperlink(GetDlgItem(hwndParent, uiCtlId));
}

View File

@ -0,0 +1,10 @@
// Hyperlinks.h
//
// Copyright 2002 Neal Stublen
// All rights reserved.
//
// http://www.awesoftware.com
//
BOOL ConvertStaticToHyperlink(HWND hwndCtl);
BOOL ConvertStaticToHyperlink(HWND hwndParent, UINT uiCtlId);

View File

@ -305,10 +305,6 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\AboutDlg.h"
>
</File>
<File
RelativePath="..\AdvancedDlg.cpp"
>
@ -433,6 +429,37 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\Hyperlinks.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
PrecompiledHeaderThrough="Win32.h"
PrecompiledHeaderFile="$(IntDir)\win32.pch"
/>
</FileConfiguration>
<FileConfiguration
Name="Devel|Win32"
>
<Tool
Name="VCCLCompilerTool"
PrecompiledHeaderThrough="Win32.h"
PrecompiledHeaderFile="$(IntDir)\win32.pch"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
PrecompiledHeaderThrough="Win32.h"
PrecompiledHeaderFile="$(IntDir)\win32.pch"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\ini.cpp"
>
@ -520,10 +547,6 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\McdsDlg.h"
>
</File>
<File
RelativePath="..\PatchBrowser.cpp"
>
@ -578,11 +601,7 @@
</FileConfiguration>
</File>
<File
RelativePath="..\RDebugger.h"
>
</File>
<File
RelativePath="..\Win32.h"
RelativePath="..\..\Saveslots.cpp"
>
</File>
<File
@ -839,10 +858,42 @@
RelativePath="..\ps2_silver.bmp"
>
</File>
</Filter>
<Filter
Name="Include"
>
<File
RelativePath="..\AboutDlg.h"
>
</File>
<File
RelativePath="..\..\HostGui.h"
>
</File>
<File
RelativePath="..\Hyperlinks.h"
>
</File>
<File
RelativePath="..\McdsDlg.h"
>
</File>
<File
RelativePath="..\RDebugger.h"
>
</File>
<File
RelativePath="..\resource.h"
>
</File>
<File
RelativePath="..\Win32.h"
>
</File>
<File
RelativePath="..\WinDebugResource.h"
>
</File>
</Filter>
</Filter>
<Filter
@ -2287,14 +2338,6 @@
>
</File>
</Filter>
<Filter
Name="Dynarec"
>
<File
RelativePath="..\..\x86\iIPU.cpp"
>
</File>
</Filter>
</Filter>
<Filter
Name="memory"
@ -2343,38 +2386,6 @@
RelativePath="..\..\MTGS.cpp"
>
</File>
<Filter
Name="Dynarec"
>
<File
RelativePath="..\..\x86\iGS.cpp"
>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Devel|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
</File>
</Filter>
</Filter>
<Filter
Name="VUmicro"
@ -2595,42 +2606,6 @@
RelativePath="..\..\SPR.h"
>
</File>
<Filter
Name="Dynarec"
>
<File
RelativePath="..\..\x86\iHw.cpp"
>
</File>
<File
RelativePath="..\..\x86\iPsxHw.cpp"
>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Devel|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
</File>
</Filter>
</Filter>
<Filter
Name="Sif"
@ -2732,6 +2707,10 @@
RelativePath="..\..\Plugins.cpp"
>
</File>
<File
RelativePath="..\..\RecoverySystem.cpp"
>
</File>
<File
RelativePath="..\SamplProf.cpp"
>
@ -3096,7 +3075,7 @@
>
</File>
<File
RelativePath="..\WinDebugResource.h"
RelativePath="..\WinDebugResource"
>
</File>
</Files>

View File

@ -28,6 +28,7 @@
#include <tchar.h>
#include "Misc.h"
#include "HostGui.h"
#include "resource.h"
#include "WinDebugResource.h"
@ -121,43 +122,35 @@ public:
void EnumEntry( const string& var, int& value, const char* const* enumArray, const int defvalue=0 );
};
LRESULT WINAPI MainWndProc(HWND, UINT, WPARAM, LPARAM);
void CreateMainWindow();
void RunGui();
extern LRESULT WINAPI MainWndProc(HWND, UINT, WPARAM, LPARAM);
extern void CreateMainWindow();
extern void RunGui();
extern bool HostGuiInit();
BOOL Pcsx2Configure(HWND hWnd);
void InitLanguages();
char *GetLanguageNext();
void CloseLanguages();
void ChangeLanguage(char *lang);
extern BOOL Pcsx2Configure(HWND hWnd);
extern void InitLanguages();
extern char *GetLanguageNext();
extern void CloseLanguages();
extern void ChangeLanguage(char *lang);
void SysRestorableReset();
extern void WinClose();
extern void ExecuteCpu();
extern void OnStates_LoadOther();
extern void OnStates_SaveOther();
extern int ParseCommandLine( int tokenCount, TCHAR *const *const tokens );
extern void strcatz(char *dst, char *src);
void WinClose();
void States_Load( const string& file, int num=-1 );
void States_Save( const string& file, int num=-1 );
void States_Load(int num);
void States_Save(int num);
void OnStates_LoadOther();
void OnStates_SaveOther();
int ParseCommandLine( int tokenCount, TCHAR *const *const tokens );
void RunExecute( const char* elf_file, bool use_bios=false );
void ExecuteCpu();
void strcatz(char *dst, char *src);
BOOL CALLBACK PatchBDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK CpuDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK AdvancedOptionsProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK HacksProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
extern BOOL CALLBACK PatchBDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam);
extern BOOL CALLBACK CpuDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam);
extern BOOL CALLBACK AdvancedOptionsProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam);
extern BOOL CALLBACK HacksProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
extern AppData gApp;
extern HWND hStatusWnd;
extern PcsxConfig winConfig; // local storage of the configuration options.
extern bool g_ReturnToGui; // set to exit the execution of the emulator and return control to the GUI
extern bool g_EmulationInProgress; // Set TRUE if a game is actively running (set to false on reset)
extern int UseGui;
extern int nDisableSC; // screensaver
extern bool UseGui;
extern bool nDisableSC; // screensaver
extern unsigned int langsMax;
extern SafeArray<u8>* g_RecoveryState;
@ -165,11 +158,6 @@ extern SafeArray<u8>* g_gsRecoveryState;
extern const char* g_pRunGSState;
extern int g_SaveGSStream;
// sets the contents of the Pcsx2 status bar...
extern void StatusBar_Notice( const std::string& text );
extern void StatusBar_SetMsg( const std::string& text );
// Throws an exception based on the value returned from GetLastError.
// Performs an option return value success/fail check on hresult.
extern void StreamException_ThrowLastError( const string& streamname, HANDLE result=INVALID_HANDLE_VALUE );

View File

@ -6,6 +6,7 @@
#define IDD_BPCNT 105
#define IDD_DEBUG 108
#define IDD_VU0REGS 111
#define IDD_JUMP 112
#define IDD_VU0INTEGER 113
#define IDD_GPREGS 114
#define IDD_CP0REGS 115
@ -172,6 +173,7 @@
#define IDC_VU1_VF26 1097
#define IDC_DEBUG_DUMP 1097
#define IDC_CP04 1097
#define IDC_JUMP_PC 1097
#define IDC_VU1_VF27 1098
#define IDC_DUMP_END 1098
#define IDC_DEBUG_MEMORY 1098

View File

@ -59,8 +59,6 @@ void strcatz(char *dst, char *src)
BOOL APIENTRY CmdlineProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);//forward def
//-------------------
TESTRUNARGS g_TestRun;
static const char* phelpmsg =
"pcsx2 [options] [file]\n\n"
"-cfg [file] {configuration file}\n"
@ -175,7 +173,7 @@ static PTCHAR* _CommandLineToArgv( const TCHAR *CmdLine, int* _argc )
void WinClose()
{
SysClose();
cpuShutdown();
// Don't check Config.Profiler here -- the Profiler will know if it's running or not.
ProfilerTerm();
@ -197,9 +195,9 @@ static bool TestRunMode()
if( IsDevBuild && (g_TestRun.enabled || g_TestRun.ptitle != NULL) )
{
// run without ui
UseGui = 0;
UseGui = false;
PCSX2_MEM_PROTECT_BEGIN();
RunExecute( g_TestRun.efile ? g_TestRun.ptitle : NULL );
SysPrepareExecution( g_TestRun.efile ? g_TestRun.ptitle : NULL );
PCSX2_MEM_PROTECT_END();
return true;
}
@ -347,7 +345,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
// Important! Always allocate dynarecs before loading plugins, to give the dynarecs
// the best possible chance of claiming ideal memory space!
SysInit();
HostGuiInit();
if( needsToConfig )
{
@ -630,22 +628,24 @@ LRESULT WINAPI MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
case ID_FILEOPEN:
{
std::string outstr;
string outstr;
if( Open_File_Proc( outstr ) )
RunExecute( outstr.c_str() );
{
SysReset();
SysPrepareExecution( outstr.c_str() );
}
}
break;
case ID_RUN_EXECUTE:
if( g_EmulationInProgress )
ExecuteCpu();
else
RunExecute( NULL, true ); // boots bios if no savestate is to be recovered
// Execute without reset -- resumes existing states or runs the BIOS if
// the state is cleared/reset.
SysPrepareExecution( NULL, true );
break;
case ID_FILE_RUNCD:
SysReset();
RunExecute( NULL );
SysPrepareExecution( NULL );
break;
case ID_RUN_RESET:
@ -745,7 +745,7 @@ LRESULT WINAPI MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
if (remoteDebugBios)
{
cpuReset();
SysResetExecutionState();
SysClearExecutionCache();
cpuExecuteBios();
DialogBox(gApp.hInstance, MAKEINTRESOURCE(IDD_RDEBUG), NULL, (DLGPROC)RemoteDebuggerProc);
@ -870,19 +870,6 @@ LRESULT WINAPI MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int Slots[5] = { -1, -1, -1, -1, -1 };
void ResetMenuSlots() {
int i;
for (i=0; i<5; i++) {
if (Slots[i] == -1)
EnableMenuItem(GetSubMenu(gApp.hMenu, 0), ID_FILE_STATES_LOAD_SLOT1+i, MF_GRAYED);
else
EnableMenuItem(GetSubMenu(gApp.hMenu, 0), ID_FILE_STATES_LOAD_SLOT1+i, MF_ENABLED);
}
}
// fixme - this looks like the beginnings of a dynamic "list of valid saveslots"
// feature. Too bad it's never called and CheckState was old/dead code.
/*void UpdateMenuSlots() {
@ -1040,7 +1027,7 @@ void CreateMainWindow()
RECT rect;
int w, h;
g_ReturnToGui = true;
//g_ReturnToGui = true;
#ifdef _MSC_VER
sprintf(COMPILER, "(VC%d)", (_MSC_VER+100)/200);//hacky:) works for VC6 & VC.NET
@ -1080,7 +1067,7 @@ void CreateMainWindow()
);
gApp.hWnd = hWnd;
ResetMenuSlots();
HostGui::ResetMenuSlots();
CreateMainMenu();
SetMenu(gApp.hWnd, gApp.hMenu);
@ -1100,7 +1087,7 @@ void CreateMainWindow()
MoveWindow(hWnd, 60, 60, w, h, TRUE);
SendMessage( hStatusWnd, WM_SIZE, 0, 0 );
StatusBar_SetMsg("F1 - save, F2 - next state, Shift+F2 - prev state, F3 - load, F8 - snapshot");
HostGui::SetStatusMsg("F1 - save, F2 - next state, Shift+F2 - prev state, F3 - load, F8 - snapshot");
ShowWindow(hWnd, true);
SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);

View File

@ -20,20 +20,14 @@
#include <winnt.h>
#include "Common.h"
//#include "PsxCommon.h"
#include "VUmicro.h"
#include "iR5900.h"
int UseGui = 1;
int nDisableSC = 0; // screensaver
static bool sinit = false;
bool UseGui = true;
bool nDisableSC = false; // screensaver
SafeArray<u8>* g_RecoveryState = NULL;
SafeArray<u8>* g_gsRecoveryState = NULL;
bool g_ReturnToGui = false; // set to exit the execution of the emulator and return control to the GUI
bool g_EmulationInProgress = false; // Set TRUE if a game is actively running (set to false on reset)
// This instance is not modified by command line overrides so
// that command line plugins and stuff won't be saved into the
@ -50,7 +44,6 @@ const char* g_pRunGSState = NULL;
int SysPageFaultExceptionFilter( EXCEPTION_POINTERS* eps )
{
const _EXCEPTION_RECORD& ExceptionRecord = *eps->ExceptionRecord;
//const _CONTEXT& ContextRecord = *eps->ContextRecord;
if (ExceptionRecord.ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
{
@ -69,25 +62,6 @@ int SysPageFaultExceptionFilter( EXCEPTION_POINTERS* eps )
}
// For issuing notices to both the status bar and the console at the same time.
// Single-line text only please! Mutli-line msgs should be directed to the
// console directly, thanks.
void StatusBar_Notice( const std::string& text )
{
// mirror output to the console!
Console::Status( text.c_str() );
// don't try this in GCC folks!
SendMessage(hStatusWnd, SB_SETTEXT, 0, (LPARAM)text.c_str() );
}
// Sets the status bar message without mirroring the output to the console.
void StatusBar_SetMsg( const std::string& text )
{
// don't try this in GCC folks!
SendMessage(hStatusWnd, SB_SETTEXT, 0, (LPARAM)text.c_str() );
}
// returns 1 if the user requested help (show help and exit)
// returns zero on success.
// returns -1 on failure (bad command line argument)
@ -116,7 +90,7 @@ int ParseCommandLine( int tokenCount, TCHAR *const *const tokens )
return -1;
}
else if( CmdSwitchIs( "nogui" ) ) {
UseGui = 0;
UseGui = false;
}
#ifdef PCSX2_DEVBUILD
else if( CmdSwitchIs( "jpg" ) ) {
@ -205,376 +179,6 @@ void SysPrintf(const char *fmt, ...)
Console::Write( msg );
}
static void __fastcall KeyEvent(keyEvent* ev);
__forceinline void SysUpdate() {
keyEvent* ev1 = PAD1keyEvent();
keyEvent* ev2 = PAD2keyEvent();
KeyEvent( (ev1 != NULL) ? ev1 : ev2);
}
static void TryRecoverFromGsState()
{
if( g_gsRecoveryState != NULL )
{
s32 dummylen;
memLoadingState eddie( *g_gsRecoveryState );
eddie.FreezePlugin( "GS", gsSafeFreeze );
eddie.Freeze( dummylen ); // reads the length value recorded earlier.
eddie.gsFreeze();
}
}
#include "R5900Exceptions.h"
void ExecuteCpu()
{
// Make sure any left-over recovery states are cleaned up.
safe_delete( g_RecoveryState );
// Just in case they weren't initialized earlier (no harm in calling this multiple times)
if (OpenPlugins(NULL) == -1) return;
// this needs to be called for every new game!
// (note: sometimes launching games through bios will give a crc of 0)
if( GSsetGameCRC != NULL )
GSsetGameCRC(ElfCRC, g_ZeroGSOptions);
TryRecoverFromGsState();
safe_delete( g_gsRecoveryState );
// ... and hide the window. Ugly thing.
ShowWindow( gApp.hWnd, SW_HIDE );
g_EmulationInProgress = true;
g_ReturnToGui = false;
// Optimization: We hardcode two versions of the EE here -- one for recs and one for ints.
// This is because recs are performance critical, and being able to inline them into the
// function here helps a small bit (not much but every small bit counts!).
timeBeginPeriod( 1 );
try
{
if( CHECK_EEREC )
{
while( !g_ReturnToGui )
{
recExecute();
SysUpdate();
}
}
else
{
while( !g_ReturnToGui )
{
Cpu->Execute();
SysUpdate();
}
}
}
catch( R5900Exception::BaseExcept& ex )
{
Console::Error( ex.cMessage() );
Console::Error( fmt_string( "(EE) PC: 0x%8.8x \tCycle:0x8.8x", ex.cpuState.pc, ex.cpuState.cycle ).c_str() );
if( !Config.PsxOut )
{
// TODO : no console opened, so use a popup to msg the user.
// Need to take care to shut down the GS first, or else it'll cause ugliness on fullscreen execution.
}
}
timeEndPeriod( 1 );
ShowWindow( gApp.hWnd, SW_SHOW );
SetForegroundWindow( gApp.hWnd );
}
// Runs and ELF image directly (ISO or ELF program or BIN)
// Used by Run::FromCD and such
void RunExecute( const char* elf_file, bool use_bios )
{
nDisableSC = 1;
try
{
cpuReset();
}
catch( Exception::BaseException& ex )
{
Msgbox::Alert( ex.cMessage() );
return;
}
if (OpenPlugins(g_TestRun.ptitle) == -1)
return;
if( elf_file == NULL )
{
if(g_RecoveryState != NULL)
{
try
{
memLoadingState( *g_RecoveryState ).FreezeAll();
}
catch( std::runtime_error& ex )
{
Msgbox::Alert(
"Gamestate recovery failed. Your game progress will be lost (sorry!)\n"
"\nError: %s\n", params ex.what() );
// Take the user back to the GUI...
safe_delete( g_RecoveryState );
ClosePlugins( true );
return;
}
}
else if( g_gsRecoveryState == NULL )
{
// Not recovering a state, so need to execute the bios and load the ELF information.
// (note: gsRecoveries are done from ExecuteCpu)
// if the elf_file is null we use the CDVD elf file.
// But if the elf_file is an empty string then we boot the bios instead.
char ename[g_MaxPath];
ename[0] = 0;
if( !use_bios )
GetPS2ElfName( ename );
loadElfFile( ename );
}
}
else
{
// Custom ELF specified (not using CDVD).
// Run the BIOS and load the ELF.
loadElfFile( elf_file );
}
ExecuteCpu();
}
class RecoveryMemSavingState : public memSavingState, Sealed
{
public:
virtual ~RecoveryMemSavingState() { }
RecoveryMemSavingState() : memSavingState( *g_RecoveryState )
{
}
void gsFreeze()
{
if( g_gsRecoveryState != NULL )
{
// just copy the data from src to dst.
// the normal savestate doesn't expect a length prefix for internal structures,
// so don't copy that part.
const u32 pluginlen = *((u32*)g_gsRecoveryState->GetPtr());
const u32 gslen = *((u32*)g_gsRecoveryState->GetPtr(pluginlen+4));
memcpy( m_memory.GetPtr(m_idx), g_gsRecoveryState->GetPtr(pluginlen+8), gslen );
m_idx += gslen;
}
else
memSavingState::gsFreeze();
}
void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) )
{
if( (freezer == gsSafeFreeze) && (g_gsRecoveryState != NULL) )
{
// Gs data is already in memory, so just copy from src to dest:
// length of the GS data is stored as the first u32, so use that to run the copy:
const u32 len = *((u32*)g_gsRecoveryState->GetPtr());
memcpy( m_memory.GetPtr(m_idx), g_gsRecoveryState->GetPtr(), len+4 );
m_idx += len+4;
}
else
memSavingState::FreezePlugin( name, freezer );
}
};
class RecoveryZipSavingState : public gzSavingState, Sealed
{
public:
virtual ~RecoveryZipSavingState() { }
RecoveryZipSavingState( const string& filename ) : gzSavingState( filename )
{
}
void gsFreeze()
{
if( g_gsRecoveryState != NULL )
{
// read data from the gsRecoveryState allocation instead of the GS, since the gs
// info was invalidated when the plugin was shut down.
// the normal savestate doesn't expect a length prefix for internal structures,
// so don't copy that part.
u32& pluginlen = *((u32*)g_gsRecoveryState->GetPtr(0));
u32& gslen = *((u32*)g_gsRecoveryState->GetPtr(pluginlen+4));
gzwrite( m_file, g_gsRecoveryState->GetPtr(pluginlen+4), gslen );
}
else
gzSavingState::gsFreeze();
}
void FreezePlugin( const char* name, s32 (CALLBACK* freezer)(int mode, freezeData *data) )
{
if( (freezer == gsSafeFreeze) && (g_gsRecoveryState != NULL) )
{
// Gs data is already in memory, so just copy from there into the gzip file.
// length of the GS data is stored as the first u32, so use that to run the copy:
u32& len = *((u32*)g_gsRecoveryState->GetPtr());
gzwrite( m_file, g_gsRecoveryState->GetPtr(), len+4 );
}
else
gzSavingState::FreezePlugin( name, freezer );
}
};
void States_Load( const string& file, int num )
{
if( !Path::isFile( file ) )
{
Console::Notice( "Saveslot %d is empty.", params num );
return;
}
try
{
char Text[g_MaxPath];
gzLoadingState joe( file ); // this'll throw an StateLoadError_Recoverable.
// Make sure the cpu and plugins are ready to be state-ified!
cpuReset();
OpenPlugins( NULL );
joe.FreezeAll();
if( num != -1 )
sprintf (Text, _("*PCSX2*: Loaded State %d"), num);
else
sprintf (Text, _("*PCSX2*: Loaded State %s"), file);
StatusBar_Notice( Text );
if( GSsetGameCRC != NULL )
GSsetGameCRC(ElfCRC, g_ZeroGSOptions);
}
catch( Exception::StateLoadError_Recoverable& ex)
{
if( num != -1 )
Console::Notice( "Could not load savestate from slot %d.\n\n%s", params num, ex.cMessage() );
else
Console::Notice( "Could not load savestate file: %s.\n\n%s", params file, ex.cMessage() );
// At this point the cpu hasn't been reset, so we can return
// control to the user safely... (that's why we use a console notice instead of a popup)
return;
}
catch( Exception::BaseException& ex )
{
// The emulation state is ruined. Might as well give them a popup and start the gui.
string message;
if( num != -1 )
ssprintf( message,
"Encountered an error while loading savestate from slot %d.\n", num );
else
ssprintf( message,
"Encountered an error while loading savestate from file: %s.\n", file );
if( g_EmulationInProgress )
message += "Since the savestate load was incomplete, the emulator has been reset.\n";
message += "\nError: " + ex.Message();
Msgbox::Alert( message.c_str() );
SysClose();
return;
}
// Start emulating!
ExecuteCpu();
}
void States_Save( const string& file, int num )
{
try
{
string text;
RecoveryZipSavingState( file ).FreezeAll();
if( num != -1 )
ssprintf( text, _( "State saved to slot %d" ), num );
else
ssprintf( text, _( "State saved to file: %s" ), file );
StatusBar_Notice( text );
}
catch( Exception::BaseException& ex )
{
string message;
if( num != -1 )
ssprintf( message, "An error occurred while trying to save to slot %d\n", num );
else
ssprintf( message, "An error occurred while trying to save to file: %s\n", file );
message += "Your emulation state has not been saved!\n\nError: " + ex.Message();
Console::Error( message.c_str() );
}
}
void States_Load(int num)
{
States_Load( SaveState::GetFilename( num ), num );
}
void States_Save(int num)
{
if( g_RecoveryState != NULL )
{
// State is already saved into memory, and the emulator (and in-progress flag)
// have likely been cleared out. So save from the Recovery buffer instead of
// doing a "standard" save:
string text( SaveState::GetFilename( num ) );
gzFile fileptr = gzopen( text.c_str(), "wb" );
if( fileptr == NULL )
{
Msgbox::Alert( _("File permissions error while trying to save to slot %d"), params num );
return;
}
gzwrite( fileptr, &g_SaveVersion, sizeof( u32 ) );
gzwrite( fileptr, g_RecoveryState->GetPtr(), g_RecoveryState->GetSizeInBytes() );
gzclose( fileptr );
return;
}
if( !g_EmulationInProgress )
{
Msgbox::Alert( "You need to start a game first before you can save it's state." );
return;
}
States_Save( SaveState::GetFilename( num ), num );
}
void OnStates_LoadOther()
{
OPENFILENAME ofn;
@ -639,171 +243,14 @@ void OnStates_SaveOther()
States_Save( szFileName );
}
class JustGsSavingState : public memSavingState, Sealed
{
public:
virtual ~JustGsSavingState() { }
JustGsSavingState() : memSavingState( *g_gsRecoveryState )
{
}
// This special override saves the gs info to m_idx+4, and then goes back and
// writes in the length of data saved.
void gsFreeze()
{
int oldmidx = m_idx;
m_idx += 4;
memSavingState::gsFreeze();
if( IsSaving() )
{
s32& len = *((s32*)m_memory.GetPtr( oldmidx ));
len = (m_idx - oldmidx)-4;
}
}
};
static int shiftkey = 0;
static void __fastcall KeyEvent(keyEvent* ev)
{
if (ev == NULL) return;
if (ev->evt == KEYRELEASE) {
switch (ev->key) {
case VK_SHIFT: shiftkey = 0; break;
}
GSkeyEvent(ev); return;
}
if (ev->evt != KEYPRESS)
return;
//some pad plugins don't give a key released event for shift, so this is needed
//shiftkey = GetKeyState(VK_SHIFT)&0x8000;
//Well SSXPad breaks with your code, thats why my code worked and your makes reg dumping impossible
//So i suggest you fix the plugins that dont.
switch (ev->key) {
case VK_SHIFT: shiftkey = 1; break;
case VK_F1: case VK_F2: case VK_F3: case VK_F4:
case VK_F5: case VK_F6: case VK_F7: case VK_F8:
case VK_F9: case VK_F10: case VK_F11: case VK_F12:
try
{
ProcessFKeys(ev->key-VK_F1 + 1, shiftkey);
}
catch( Exception::CpuStateShutdown& )
{
// Woops! Something was unrecoverable. Bummer.
// Let's give the user a RunGui!
g_EmulationInProgress = false;
g_ReturnToGui = true;
}
break;
case VK_TAB:
CycleFrameLimit(0);
break;
case VK_ESCAPE:
#ifdef PCSX2_DEVBUILD
if( g_SaveGSStream >= 3 ) {
// gs state
g_SaveGSStream = 4;
break;
}
#endif
g_ReturnToGui = true;
if( CHECK_ESCAPE_HACK )
{
//PostMessage(GetForegroundWindow(), WM_CLOSE, 0, 0);
DestroyWindow( gApp.hWnd );
}
else
{
if( !UseGui ) {
// not using GUI and user just quit, so exit
WinClose();
}
if( Config.closeGSonEsc )
{
safe_delete( g_gsRecoveryState );
safe_delete( g_RecoveryState );
g_gsRecoveryState = new SafeArray<u8>();
JustGsSavingState eddie;
eddie.FreezePlugin( "GS", gsSafeFreeze ) ;
eddie.gsFreeze();
}
ClosePlugins( Config.closeGSonEsc );
nDisableSC = 0;
}
break;
default:
GSkeyEvent(ev);
break;
}
}
static bool sinit=false;
void SysRestorableReset()
{
// already reset? and saved?
if( !g_EmulationInProgress ) return;
if( g_RecoveryState != NULL ) return;
try
{
g_RecoveryState = new SafeArray<u8>( "Memory Savestate Recovery" );
RecoveryMemSavingState().FreezeAll();
safe_delete( g_gsRecoveryState );
g_EmulationInProgress = false;
}
catch( Exception::RuntimeError& ex )
{
Msgbox::Alert(
"Pcsx2 gamestate recovery failed. Some options may have been reverted to protect your game's state.\n"
"Error: %s", params ex.cMessage() );
safe_delete( g_RecoveryState );
}
}
void SysReset()
{
if (!sinit) return;
// fixme - this code sets the statusbar but never returns control to the window message pump
// so the status bar won't receive the WM_PAINT messages needed to update itself anyway.
// Oops! (air)
StatusBar_Notice(_("Resetting..."));
Console::SetTitle(_("Resetting..."));
g_EmulationInProgress = false;
safe_delete( g_RecoveryState );
safe_delete( g_gsRecoveryState );
ResetPlugins();
ElfCRC = 0;
// Note : No need to call cpuReset() here. It gets called automatically before the
// emulator resumes execution.
StatusBar_Notice(_("Ready"));
Console::SetTitle(_("*PCSX2* Emulation state is reset."));
}
bool SysInit()
bool HostGuiInit()
{
if( sinit ) return true;
sinit = true;
CreateDirectory(MEMCARDS_DIR, NULL);
CreateDirectory(SSTATES_DIR, NULL);
CreateDirectory(SNAPSHOTS_DIR, NULL);
// Set the compression attribute on the Memcards folder.
// Memcards generally compress very well via NTFS compression.
@ -827,32 +274,27 @@ bool SysInit()
return true;
}
// completely shuts down the emulator's cpu state, and unloads all plugins from memory.
void SysClose()
{
if (!sinit) return;
cpuShutdown();
ClosePlugins( true );
ReleasePlugins();
sinit = false;
}
static const char *err = N_("Error Loading Symbol");
static int errval;
void *SysLoadLibrary(const char *lib) {
return LoadLibrary(lib);
namespace HostSys
{
void *LoadLibrary(const char *lib)
{
return LoadLibraryA( lib );
}
void *SysLoadSym(void *lib, const char *sym) {
void *LoadSym(void *lib, const char *sym)
{
void *tmp = GetProcAddress((HINSTANCE)lib, sym);
if (tmp == NULL) errval = GetLastError();
else errval = 0;
return tmp;
}
const char *SysLibError() {
const char *LibError()
{
if( errval )
{
static char perr[4096];
@ -865,24 +307,24 @@ const char *SysLibError() {
return NULL;
}
void SysCloseLibrary(void *lib)
void CloseLibrary(void *lib)
{
FreeLibrary((HINSTANCE)lib);
}
void *SysMmap(uptr base, u32 size)
void *Mmap(uptr base, u32 size)
{
return VirtualAlloc((void*)base, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
}
void SysMunmap(uptr base, u32 size)
void Munmap(uptr base, u32 size)
{
if( base == NULL ) return;
VirtualFree((void*)base, size, MEM_DECOMMIT);
VirtualFree((void*)base, 0, MEM_RELEASE);
}
void SysMemProtect( void* baseaddr, size_t size, PageProtectionMode mode, bool allowExecution )
void MemProtect( void* baseaddr, size_t size, PageProtectionMode mode, bool allowExecution )
{
DWORD winmode = 0;
@ -904,3 +346,136 @@ void SysMemProtect( void* baseaddr, size_t size, PageProtectionMode mode, bool a
DWORD OldProtect; // enjoy my uselessness, yo!
VirtualProtect( baseaddr, size, winmode, &OldProtect );
}
}
namespace HostGui
{
// Sets the status bar message without mirroring the output to the console.
void SetStatusMsg( const string& text )
{
// don't try this in GCC folks!
SendMessage(hStatusWnd, SB_SETTEXT, 0, (LPARAM)text.c_str() );
}
void Notice( const string& text )
{
// mirror output to the console!
Console::Status( text.c_str() );
SetStatusMsg( text );
}
void ResetMenuSlots()
{
for( int i=0; i<5; i++ )
{
EnableMenuItem( GetSubMenu(gApp.hMenu, 0),
ID_FILE_STATES_LOAD_SLOT1+i,
States_isSlotUsed(i) ? MF_ENABLED : MF_GRAYED
);
}
}
// Because C++ lacks the finally clause >_<
static void _executeCpuFinally()
{
timeEndPeriod( 1 );
ShowWindow( gApp.hWnd, SW_SHOW );
SetForegroundWindow( gApp.hWnd );
nDisableSC = false;
}
void BeginExecution()
{
nDisableSC = true;
// ... and hide the window. Ugly thing.
ShowWindow( gApp.hWnd, SW_HIDE );
timeBeginPeriod( 1 ); // improves multithreaded responsiveness
try
{
SysExecute();
}
catch( Exception::BaseException& )
{
_executeCpuFinally();
throw;
}
_executeCpuFinally();
}
void __fastcall KeyEvent( keyEvent* ev )
{
static int shiftkey = 0;
if (ev == NULL) return;
if (ev->evt == KEYRELEASE)
{
switch (ev->key)
{
case VK_SHIFT: shiftkey = 0; break;
}
GSkeyEvent(ev); return;
}
if (ev->evt != KEYPRESS) return;
switch (ev->key)
{
case VK_SHIFT: shiftkey = 1; break;
case VK_F1: case VK_F2: case VK_F3: case VK_F4:
case VK_F5: case VK_F6: case VK_F7: case VK_F8:
case VK_F9: case VK_F10: case VK_F11: case VK_F12:
try
{
ProcessFKeys(ev->key-VK_F1 + 1, shiftkey);
}
catch( Exception::CpuStateShutdown& )
{
// Woops! Something was unrecoverable (like state load). Bummer.
// Let's give the user a RunGui!
g_EmulationInProgress = false;
SysEndExecution();
}
break;
case VK_TAB:
CycleFrameLimit(0);
break;
case VK_ESCAPE:
#ifdef PCSX2_DEVBUILD
if( g_SaveGSStream >= 3 ) {
// gs state
g_SaveGSStream = 4;
break;
}
#endif
if( CHECK_ESCAPE_HACK )
{
g_EmulationInProgress = false;
DestroyWindow( gApp.hWnd );
}
else
{
if( !UseGui ) {
// not using GUI and user just quit, so exit
WinClose();
}
nDisableSC = 0;
}
SysEndExecution();
break;
default:
GSkeyEvent(ev);
break;
}
}
}

View File

@ -8,7 +8,6 @@
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxresmw.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
@ -731,6 +730,17 @@ BEGIN
EDITTEXT IDC_CP031,191,218,51,12,ES_READONLY
END
IDD_JUMP DIALOGEX 0, 0, 175, 64
STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Jump to specific address"
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
EDITTEXT IDC_JUMP_PC,73,14,82,13
DEFPUSHBUTTON "OK",IDOK,25,43,50,14
PUSHBUTTON "Cancel",IDCANCEL,101,42,50,14
LTEXT "Enter new PC Address:",IDC_STATIC,3,16,65,8
END
/////////////////////////////////////////////////////////////////////////////
//
@ -890,7 +900,6 @@ END
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@ -129,17 +129,6 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// Dialog
//
IDD_JUMP DIALOGEX 0, 0, 175, 64
STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Jump to specific address"
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
EDITTEXT IDC_JUMP_PC,73,14,82,13
DEFPUSHBUTTON "OK",IDOK,25,43,50,14
PUSHBUTTON "Cancel",IDCANCEL,101,42,50,14
LTEXT "Enter new PC Address:",IDC_STATIC,3,16,65,8
END
IDD_MEMORY DIALOG 0, 0, 317, 270
STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Memory"
@ -235,10 +224,7 @@ BEGIN
GROUPBOX "Clamp Mode",IDC_STATIC,281,80,236,127,BS_LEFT
GROUPBOX "Other Options",IDC_STATIC,281,210,237,34,BS_LEFT
LTEXT "These options specify how PCSX2's recompilers will clamp Infinities and NaN (Not a Number) values in the opcode instructions.",IDC_STATIC,286,94,224,19
LTEXT "*None* - No clamping. (Fastest Mode)\n*Normal* - Clamps the result.\n*Extra* - Clamps the operands, the result, and anywhere in between.\n*Extra + Preserve Sign* - Same as ""Extra"", except preserves NaN's sign when clamping the operands.",IDC_STATIC,286,114,224,48
LTEXT "*Full* - Attempts to emulate large numbers correctly for the EE's FPU. VU's clamp mode should be set to ""Extra + Preserve Sign"" for this to work best. (but still works for most games even with ""Normal"" VU clamping)",IDC_STATIC,287,163,214,36
LTEXT "Flush to Zero - Makes floating point underflows become zero.\nDenormals are Zero - Makes floating point denormals become zero.",IDC_STATIC,287,222,224,18
END
LTEXT "*None* - No clamping. (Fastest Mode)\n*Normal* - Clamps the result.\n*Extra* - Clamps the operands, the result, and anywhere in between.\n*Extra + Preserve Sign* - Same as ""Extra"", except preserves NaN's sign when clamping the operands.",IDC_STATIC,286,114,224,48 LTEXT "*Full* - Attempts to emulate large numbers correctly for the EE's FPU. VU's clamp mode should be set to ""Extra + Preserve Sign"" for this to work best. (but still works for most games even with ""Normal"" VU clamping)",IDC_STATIC,287,163,214,36 LTEXT "Flush to Zero - Makes floating point underflows become zero.\nDenormals are Zero - Makes floating point denormals become zero.",IDC_STATIC,287,222,224,18END
IDD_CONF_MEMCARD DIALOGEX 0, 0, 451, 215
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
@ -343,11 +329,13 @@ FONT 8, "MS Shell Dlg", 400, 0, 0x0
BEGIN
DEFPUSHBUTTON "OK",IDOK,205,274,50,14
CTEXT "PCSX2, a PS2 Emulator...",IDC_PCSX_ABOUT_AUTHORS,9,10,135,127,0,WS_EX_TRANSPARENT
CTEXT "Greets to...",IDC_PCSX_ABOUT_GREETS,94,178,319,77
CTEXT "Greets to...",IDC_PCSX_ABOUT_GREETS,94,186,319,77
GROUPBOX "",IDC_STATIC,5,3,145,141
GROUPBOX "",IDC_STATIC,87,169,333,91
CONTROL 132,IDC_PS2SILVER_RECT,"Static",SS_BITMAP | SS_SUNKEN,10,179,71,75
GROUPBOX "",IDC_STATIC,87,177,333,91
CONTROL 132,IDC_PS2SILVER_RECT,"Static",SS_BITMAP | SS_SUNKEN,10,187,71,75
CONTROL 113,IDC_STATIC,"Static",SS_BITMAP,162,7,259,137,WS_EX_CLIENTEDGE
CTEXT "Pcsx2 Official Svn Repository @ Googlecode",IDC_LINK_GOOGLECODE,79,164,273,10,SS_NOPREFIX | SS_NOTIFY
CTEXT "Pcsx2 Official Website and Forums",IDC_LINK_WEBSITE,79,151,273,10,SS_NOPREFIX | SS_NOTIFY
END
IDD_HACKS DIALOGEX 0, 0, 335, 263

View File

@ -8,7 +8,6 @@
#define ABOUT_DIALOG 104
#define IDI_ICON 108
#define IDD_MEMORY 110
#define IDD_JUMP 112
#define SPLASH_LOGO 113
#define IDD_LOGGING 119
#define IDD_CMDLINE 120
@ -69,8 +68,10 @@
#define IDC_LISTCDVD 1054
#define IDC_DEBUG_L17 1055
#define IDC_LISTBIOS 1055
#define IDC_LINK_WEBSITE 1055
#define IDC_DEBUG_L18 1056
#define IDC_CONFIGGS 1056
#define IDC_LINK_GOOGLECODE 1056
#define IDC_DEBUG_L19 1057
#define IDC_TESTGS 1057
#define IDC_DEBUG_L20 1058
@ -139,7 +140,6 @@
#define IDC_DEBUG_R27 1094
#define IDC_DEBUG_R28 1095
#define IDC_DEBUG_R29 1096
#define IDC_JUMP_PC 1097
#define IDC_DEBUG_LOGGING 1098
#define IDC_CMDLINE 1155
#define IDC_PCSX_ABOUT_GREETS 1163

View File

@ -389,14 +389,14 @@ void cpudetectInit()
// intrinsic equivalents available. So we use our own ix86 emitter to generate
// some code and run it that way. :)
u8* recSSE = (u8*)SysMmap( NULL, 0x1000 );
u8* recSSE = (u8*)HostSys::Mmap( NULL, 0x1000 );
if( recSSE != NULL )
{
x86SetPtr(recSSE);
SSE3_MOVSLDUP_XMM_to_XMM(XMM0, XMM0);
RET();
cpudetectSSE3(recSSE);
SysMunmap( recSSE, 0x1000 );
HostSys::Munmap( recSSE, 0x1000 );
}
//////////////////////////////////////

View File

@ -77,7 +77,7 @@ microVUt(void) mVUclose() {
microVU* mVU = mVUx;
if ( mVU->cache ) { SysMunmap( mVU->cache, mVU->cacheSize ); mVU->cache = NULL; }
if ( mVU->cache ) { HostSys::Munmap( mVU->cache, mVU->cacheSize ); mVU->cache = NULL; }
// Delete Block Managers
for (int i; i <= mVU->prog.max; i++) {