/[pcsx2_0.9.7]/trunk/pcsx2/gui/SysState.cpp
ViewVC logotype

Contents of /trunk/pcsx2/gui/SysState.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 280 - (show annotations) (download)
Thu Dec 23 12:02:12 2010 UTC (9 years, 2 months ago) by william
File size: 12148 byte(s)
re-commit (had local access denied errors when committing)
1 /* PCSX2 - PS2 Emulator for PCs
2 * Copyright (C) 2002-2010 PCSX2 Dev Team
3 *
4 * PCSX2 is free software: you can redistribute it and/or modify it under the terms
5 * of the GNU Lesser General Public License as published by the Free Software Found-
6 * ation, either version 3 of te License, or (at your option) any later version.
7 *
8 * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10 * PURPOSE. See the GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along with PCSX2.
13 * If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #include "PrecompiledHeader.h"
17 #include "App.h"
18
19 #include "System/SysThreads.h"
20 #include "SaveState.h"
21
22 #include "ZipTools/ThreadedZipTools.h"
23
24 // Used to hold the current state backup (fullcopy of PS2 memory and plugin states).
25 //static VmStateBuffer state_buffer( L"Public Savestate Buffer" );
26
27 static const char SavestateIdentString[] = "PCSX2 Savestate";
28 static const uint SavestateIdentLen = sizeof(SavestateIdentString);
29
30 static void SaveStateFile_WriteHeader( IStreamWriter& thr )
31 {
32 thr.Write( SavestateIdentString );
33 thr.Write( g_SaveVersion );
34 }
35
36 static void SaveStateFile_ReadHeader( IStreamReader& thr )
37 {
38 char ident[SavestateIdentLen] = {0};
39
40 thr.Read( ident );
41
42 if( strcmp(SavestateIdentString, ident) )
43 throw Exception::SaveStateLoadError( thr.GetStreamName() )
44 .SetDiagMsg(wxsFormat( L"Unrecognized file signature while loading savestate."))
45 .SetUserMsg(_("This is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2."));
46
47 u32 savever;
48 thr.Read( savever );
49
50 // Major version mismatch. Means we can't load this savestate at all. Support for it
51 // was removed entirely.
52 if( savever > g_SaveVersion )
53 throw Exception::SaveStateLoadError( thr.GetStreamName() )
54 .SetDiagMsg(wxsFormat( L"Savestate uses an unsupported or unknown savestate version.\n(PCSX2 ver=%x, state ver=%x)", g_SaveVersion, savever ))
55 .SetUserMsg(_("Cannot load this savestate. The state is from an incompatible edition of PCSX2 that is either newer than this version, or is no longer supported."));
56
57 // check for a "minor" version incompatibility; which happens if the savestate being loaded is a newer version
58 // than the emulator recognizes. 99% chance that trying to load it will just corrupt emulation or crash.
59 if( (savever >> 16) != (g_SaveVersion >> 16) )
60 throw Exception::SaveStateLoadError( thr.GetStreamName() )
61 .SetDiagMsg(wxsFormat( L"Savestate uses an unknown (future?!) savestate version.\n(PCSX2 ver=%x, state ver=%x)", g_SaveVersion, savever ))
62 .SetUserMsg(_("Cannot load this savestate. The state is an unsupported version, likely created by a newer edition of PCSX2."));
63 };
64
65 // --------------------------------------------------------------------------------------
66 // gzipReader
67 // --------------------------------------------------------------------------------------
68 // Interface for reading data from a gzip stream.
69 //
70 class gzipReader : public IStreamReader
71 {
72 DeclareNoncopyableObject(gzipReader);
73
74 protected:
75 wxString m_filename;
76 gzFile m_gzfp;
77
78 public:
79 gzipReader( const wxString& filename )
80 : m_filename( filename )
81 {
82 if( NULL == (m_gzfp = gzopen( m_filename.ToUTF8(), "rb" )) )
83 throw Exception::CannotCreateStream( m_filename ).SetDiagMsg(L"Cannot open file for reading.");
84
85 #if defined(ZLIB_VERNUM) && (ZLIB_VERNUM >= 0x1240)
86 gzbuffer(m_gzfp, 0x100000); // 1mb buffer for zlib internal operations
87 #endif
88 }
89
90 virtual ~gzipReader() throw ()
91 {
92 if( m_gzfp ) gzclose( m_gzfp );
93 }
94
95 wxString GetStreamName() const { return m_filename; }
96
97 void Read( void* dest, size_t size )
98 {
99 int result = gzread( m_gzfp, dest, size );
100 if( result == -1)
101 //throw Exception::gzError( m_filename );
102 throw Exception::BadStream( m_filename ).SetBothMsgs(wxLt("Data read failed: Invalid or corrupted gzip archive."));
103
104 if( (size_t)result < size )
105 throw Exception::EndOfStream( m_filename );
106 }
107 };
108
109 //static bool IsSavingOrLoading = false;
110
111
112 // --------------------------------------------------------------------------------------
113 // SysExecEvent_DownloadState
114 // --------------------------------------------------------------------------------------
115 // Pauses core emulation and downloads the savestate into the state_buffer.
116 //
117 class SysExecEvent_DownloadState : public SysExecEvent
118 {
119 protected:
120 VmStateBuffer* m_dest_buffer;
121
122 public:
123 wxString GetEventName() const { return L"VM_Download"; }
124
125 virtual ~SysExecEvent_DownloadState() throw() {}
126 SysExecEvent_DownloadState* Clone() const { return new SysExecEvent_DownloadState( *this ); }
127 SysExecEvent_DownloadState( VmStateBuffer* dest=NULL )
128 {
129 m_dest_buffer = dest;
130 }
131
132 bool IsCriticalEvent() const { return true; }
133 bool AllowCancelOnExit() const { return false; }
134
135 protected:
136 void InvokeEvent()
137 {
138 ScopedCoreThreadPause paused_core;
139
140 if( !SysHasValidState() )
141 throw Exception::RuntimeError()
142 .SetDiagMsg(L"SysExecEvent_DownloadState: Cannot freeze/download an invalid VM state!")
143 .SetUserMsg(L"There is no active virtual machine state to download or save." );
144
145 memSavingState(m_dest_buffer).FreezeAll();
146
147 UI_EnableStateActions();
148 paused_core.AllowResume();
149 }
150 };
151
152 // It's bad mojo to have savestates trying to read and write from the same file at the
153 // same time. To prevent that we use this mutex lock, which is used by both the
154 // CompressThread and the UnzipFromDisk events. (note that CompressThread locks the
155 // mutex during OnStartInThread, which ensures that the ZipToDisk event blocks; preventing
156 // the SysExecutor's Idle Event from re-enabing savestates and slots.)
157 //
158 static Mutex mtx_CompressToDisk;
159
160 // --------------------------------------------------------------------------------------
161 // CompressThread_VmState
162 // --------------------------------------------------------------------------------------
163 class VmStateZipThread : public CompressThread_gzip
164 {
165 typedef CompressThread_gzip _parent;
166
167 protected:
168 ScopedLock m_lock_Compress;
169
170 public:
171 VmStateZipThread( const wxString& file, VmStateBuffer* srcdata )
172 : _parent( file, srcdata, SaveStateFile_WriteHeader )
173 {
174 m_lock_Compress.Assign(mtx_CompressToDisk);
175 }
176
177 VmStateZipThread( const wxString& file, ScopedPtr<VmStateBuffer>& srcdata )
178 : _parent( file, srcdata, SaveStateFile_WriteHeader )
179 {
180 m_lock_Compress.Assign(mtx_CompressToDisk);
181 }
182
183 virtual ~VmStateZipThread() throw()
184 {
185
186 }
187
188 protected:
189 void OnStartInThread()
190 {
191 _parent::OnStartInThread();
192 m_lock_Compress.Acquire();
193 }
194
195 void OnCleanupInThread()
196 {
197 m_lock_Compress.Release();
198 _parent::OnCleanupInThread();
199 }
200 };
201
202 // --------------------------------------------------------------------------------------
203 // SysExecEvent_ZipToDisk
204 // --------------------------------------------------------------------------------------
205 class SysExecEvent_ZipToDisk : public SysExecEvent
206 {
207 protected:
208 VmStateBuffer* m_src_buffer;
209 wxString m_filename;
210
211 public:
212 wxString GetEventName() const { return L"VM_ZipToDisk"; }
213
214 virtual ~SysExecEvent_ZipToDisk() throw()
215 {
216 delete m_src_buffer;
217 }
218
219 SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk( *this ); }
220
221 SysExecEvent_ZipToDisk( ScopedPtr<VmStateBuffer>& src, const wxString& filename )
222 : m_filename( filename )
223 {
224 m_src_buffer = src.DetachPtr();
225 }
226
227 SysExecEvent_ZipToDisk( VmStateBuffer* src, const wxString& filename )
228 : m_filename( filename )
229 {
230 m_src_buffer = src;
231 }
232
233 bool IsCriticalEvent() const { return true; }
234 bool AllowCancelOnExit() const { return false; }
235
236 protected:
237 void InvokeEvent()
238 {
239 (new VmStateZipThread( m_filename, m_src_buffer ))->Start();
240 m_src_buffer = NULL;
241 }
242 };
243
244 // --------------------------------------------------------------------------------------
245 // SysExecEvent_UnzipFromDisk
246 // --------------------------------------------------------------------------------------
247 // Note: Unzipping always goes directly into the SysCoreThread's static VM state, and is
248 // always a blocking action on the SysExecutor thread (the system cannot execute other
249 // commands while states are unzipping or uploading into the system).
250 //
251 class SysExecEvent_UnzipFromDisk : public SysExecEvent
252 {
253 protected:
254 wxString m_filename;
255
256 public:
257 wxString GetEventName() const { return L"VM_UnzipFromDisk"; }
258
259 virtual ~SysExecEvent_UnzipFromDisk() throw() {}
260 SysExecEvent_UnzipFromDisk* Clone() const { return new SysExecEvent_UnzipFromDisk( *this ); }
261 SysExecEvent_UnzipFromDisk( const wxString& filename )
262 : m_filename( filename )
263 {
264 }
265
266 wxString GetStreamName() const { return m_filename; }
267
268 protected:
269 void InvokeEvent()
270 {
271 ScopedLock lock( mtx_CompressToDisk );
272 gzipReader m_gzreader(m_filename );
273 SaveStateFile_ReadHeader( m_gzreader );
274
275 // We use direct Suspend/Resume control here, since it's desirable that emulation
276 // *ALWAYS* start execution after the new savestate is loaded.
277
278 GetCoreThread().Pause();
279
280 // fixme: should start initially with the file size, and then grow from there.
281
282 static const int BlockSize = 0x100000;
283
284 VmStateBuffer buffer( 0x800000, L"StateBuffer_UnzipFromDisk" ); // start with an 8 meg buffer to avoid frequent reallocation.
285 int curidx = 0;
286
287 try {
288 while(true) {
289 buffer.MakeRoomFor( curidx+BlockSize );
290 m_gzreader.Read( buffer.GetPtr(curidx), BlockSize );
291 curidx += BlockSize;
292 Threading::pxTestCancel();
293 }
294 }
295 catch( Exception::EndOfStream& )
296 {
297 // This exception actually means success! Any others we let get sent
298 // to the main event handler/thread for handling.
299 }
300
301 // Optional shutdown of plugins when loading states? I'm not implementing it yet because some
302 // things, like the SPU2-recovery trick, rely on not resetting the plugins prior to loading
303 // the new savestate data.
304 //if( ShutdownOnStateLoad ) GetCoreThread().Cancel();
305
306
307 GetCoreThread().UploadStateCopy( buffer );
308 GetCoreThread().Resume(); // force resume regardless of emulation state earlier.
309 }
310 };
311
312 // =====================================================================================================
313 // StateCopy Public Interface
314 // =====================================================================================================
315
316 void StateCopy_SaveToFile( const wxString& file )
317 {
318 UI_DisableStateActions();
319
320 ScopedPtr<VmStateBuffer> zipbuf(new VmStateBuffer( L"Zippable Savestate" ));
321 GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState( zipbuf ));
322 GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk( zipbuf, file ));
323 }
324
325 void StateCopy_LoadFromFile( const wxString& file )
326 {
327 UI_DisableSysActions();
328 GetSysExecutorThread().PostEvent(new SysExecEvent_UnzipFromDisk( file ));
329 }
330
331 // Saves recovery state info to the given saveslot, or saves the active emulation state
332 // (if one exists) and no recovery data was found. This is needed because when a recovery
333 // state is made, the emulation state is usually reset so the only persisting state is
334 // the one in the memory save. :)
335 void StateCopy_SaveToSlot( uint num )
336 {
337 const wxString file( SaveStateBase::GetFilename( num ) );
338
339 // Backup old Savestate if one exists.
340 if( wxFileExists( file ) && EmuConfig.BackupSavestate )
341 {
342 const wxString copy( SaveStateBase::GetFilename( num ) + pxsFmt( L".backup") );
343
344 Console.Indent().WriteLn( Color_StrongGreen, L"Backing up existing state in slot %d.", num);
345 wxCopyFile( file, copy );
346 }
347
348 Console.WriteLn( Color_StrongGreen, "Saving savestate to slot %d...", num );
349 Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() );
350
351 StateCopy_SaveToFile( file );
352 }
353
354 void StateCopy_LoadFromSlot( uint slot )
355 {
356 wxString file( SaveStateBase::GetFilename( slot ) );
357
358 if( !wxFileExists( file ) )
359 {
360 Console.Warning( "Savestate slot %d is empty.", slot );
361 return;
362 }
363
364 Console.WriteLn( Color_StrongGreen, "Loading savestate from slot %d...", slot );
365 Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() );
366
367 StateCopy_LoadFromFile( file );
368 }

  ViewVC Help
Powered by ViewVC 1.1.22