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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 280 - (show annotations) (download)
Thu Dec 23 12:02:12 2010 UTC (9 years, 1 month ago) by william
File size: 14017 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 the 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 "Utilities/SafeArray.inl"
18
19 #include "MemoryCardFile.h"
20
21 // IMPORTANT! If this gets a macro redefinition error it means PluginCallbacks.h is included
22 // in a global-scope header, and that's a BAD THING. Include it only into modules that need
23 // it, because some need to be able to alter its behavior using defines. Like this:
24
25 struct Component_FileMcd;
26 #define PS2E_THISPTR Component_FileMcd*
27
28 #include "System.h"
29 #include "AppConfig.h"
30
31 #if _MSC_VER
32 # include "svnrev.h"
33 #endif
34
35 #include <wx/ffile.h>
36
37 static const int MCD_SIZE = 1024 * 8 * 16; // Legacy PSX card default size
38
39 static const int MC2_MBSIZE = 1024 * 528 * 2; // Size of a single megabyte of card data
40 static const int MC2_SIZE = MC2_MBSIZE * 8; // PS2 card default size (8MB)
41
42 // --------------------------------------------------------------------------------------
43 // FileMemoryCard
44 // --------------------------------------------------------------------------------------
45 // Provides thread-safe direct file IO mapping.
46 //
47 class FileMemoryCard
48 {
49 protected:
50 wxFFile m_file[8];
51 u8 m_effeffs[528*16];
52 SafeArray<u8> m_currentdata;
53
54 public:
55 FileMemoryCard();
56 virtual ~FileMemoryCard() throw() {}
57
58 void Lock();
59 void Unlock();
60
61 void Open();
62 void Close();
63
64 s32 IsPresent ( uint slot );
65 void GetSizeInfo( uint slot, PS2E_McdSizeInfo& outways );
66 s32 Read ( uint slot, u8 *dest, u32 adr, int size );
67 s32 Save ( uint slot, const u8 *src, u32 adr, int size );
68 s32 EraseBlock ( uint slot, u32 adr );
69 u64 GetCRC ( uint slot );
70
71 protected:
72 bool Seek( wxFFile& f, u32 adr );
73 bool Create( const wxString& mcdFile, uint sizeInMB );
74
75 wxString GetDisabledMessage( uint slot ) const
76 {
77 return pxE( "!Notice:Mcd:HasBeenDisabled", wxsFormat(
78 L"The memory card in slot %d has been automatically disabled. You can correct the problem\n"
79 L"and re-enable the memory card at any time using Config:Memory cards from the main menu.",
80 slot
81 ) );
82 }
83 };
84
85 uint FileMcd_GetMtapPort(uint slot)
86 {
87 switch( slot )
88 {
89 case 0: case 2: case 3: case 4: return 0;
90 case 1: case 5: case 6: case 7: return 1;
91
92 jNO_DEFAULT
93 }
94
95 return 0; // technically unreachable.
96 }
97
98 // Returns the multitap slot number, range 1 to 3 (slot 0 refers to the standard
99 // 1st and 2nd player slots).
100 uint FileMcd_GetMtapSlot(uint slot)
101 {
102 switch( slot )
103 {
104 case 0: case 1:
105 pxFailDev( "Invalid parameter in call to GetMtapSlot -- specified slot is one of the base slots, not a Multitap slot." );
106 break;
107
108 case 2: case 3: case 4: return slot-1;
109 case 5: case 6: case 7: return slot-4;
110
111 jNO_DEFAULT
112 }
113
114 return 0; // technically unreachable.
115 }
116
117 bool FileMcd_IsMultitapSlot( uint slot )
118 {
119 return (slot > 1);
120 }
121
122 wxFileName FileMcd_GetSimpleName(uint slot)
123 {
124 if( FileMcd_IsMultitapSlot(slot) )
125 return g_Conf->Folders.MemoryCards + wxsFormat( L"Mcd-Multitap%u-Slot%02u.ps2", FileMcd_GetMtapPort(slot)+1, FileMcd_GetMtapSlot(slot)+1 );
126 else
127 return g_Conf->Folders.MemoryCards + wxsFormat( L"Mcd%03u.ps2", slot+1 );
128 }
129
130 wxString FileMcd_GetDefaultName(uint slot)
131 {
132 if( FileMcd_IsMultitapSlot(slot) )
133 return wxsFormat( L"Mcd-Multitap%u-Slot%02u.ps2", FileMcd_GetMtapPort(slot)+1, FileMcd_GetMtapSlot(slot)+1 );
134 else
135 return wxsFormat( L"Mcd%03u.ps2", slot+1 );
136 }
137
138 FileMemoryCard::FileMemoryCard()
139 {
140 memset8<0xff>( m_effeffs );
141 }
142
143 void FileMemoryCard::Open()
144 {
145 for( int slot=0; slot<8; ++slot )
146 {
147 if( FileMcd_IsMultitapSlot(slot) )
148 {
149 if( !EmuConfig.MultitapPort0_Enabled && (FileMcd_GetMtapPort(slot) == 0) ) continue;
150 if( !EmuConfig.MultitapPort1_Enabled && (FileMcd_GetMtapPort(slot) == 1) ) continue;
151 }
152
153 wxFileName fname( g_Conf->FullpathToMcd( slot ) );
154 wxString str( fname.GetFullPath() );
155 bool cont = false;
156
157 if( fname.GetFullName().IsEmpty() )
158 {
159 str = L"[empty filename]";
160 cont = true;
161 }
162
163 if( !g_Conf->Mcd[slot].Enabled )
164 {
165 str = L"[disabled]";
166 cont = true;
167 }
168
169 Console.WriteLn( cont ? Color_Gray : Color_Green, L"McdSlot %u: " + str, slot );
170 if( cont ) continue;
171
172 const wxULongLong fsz = fname.GetSize();
173 if( (fsz == 0) || (fsz == wxInvalidSize) )
174 {
175 // FIXME : Ideally this should prompt the user for the size of the
176 // memory card file they would like to create, instead of trying to
177 // create one automatically.
178
179 if( !Create( str, 8 ) )
180 {
181 Msgbox::Alert(
182 wxsFormat(_( "Could not create a memory card file: \n\n%s\n\n" ), str.c_str()) +
183 GetDisabledMessage( slot )
184 );
185 }
186 }
187
188 // [TODO] : Add memcard size detection and report it to the console log.
189 // (8MB, 256Mb, formatted, unformatted, etc ...)
190
191 #ifdef __WXMSW__
192 NTFS_CompressFile( str, g_Conf->McdCompressNTFS );
193 #endif
194
195 if( !m_file[slot].Open( str.c_str(), L"r+b" ) )
196 {
197 // Translation note: detailed description should mention that the memory card will be disabled
198 // for the duration of this session.
199 Msgbox::Alert(
200 wxsFormat(_( "Access denied to memory card file: \n\n%s\n\n" ), str.c_str()) +
201 GetDisabledMessage( slot )
202 );
203 }
204 }
205 }
206
207 void FileMemoryCard::Close()
208 {
209 for( int slot=0; slot<8; ++slot )
210 m_file[slot].Close();
211 }
212
213 // Returns FALSE if the seek failed (is outside the bounds of the file).
214 bool FileMemoryCard::Seek( wxFFile& f, u32 adr )
215 {
216 const u32 size = f.Length();
217
218 // If anyone knows why this filesize logic is here (it appears to be related to legacy PSX
219 // cards, perhaps hacked support for some special emulator-specific memcard formats that
220 // had header info?), then please replace this comment with something useful. Thanks! -- air
221
222 u32 offset = 0;
223
224 if( size == MCD_SIZE + 64 )
225 offset = 64;
226 else if( size == MCD_SIZE + 3904 )
227 offset = 3904;
228 else
229 {
230 // perform sanity checks here?
231 }
232
233 return f.Seek( adr + offset );
234 }
235
236 // returns FALSE if an error occurred (either permission denied or disk full)
237 bool FileMemoryCard::Create( const wxString& mcdFile, uint sizeInMB )
238 {
239 //int enc[16] = {0x77,0x7f,0x7f,0x77,0x7f,0x7f,0x77,0x7f,0x7f,0x77,0x7f,0x7f,0,0,0,0};
240
241 Console.WriteLn( L"(FileMcd) Creating new %uMB memory card: " + mcdFile, sizeInMB );
242
243 wxFFile fp( mcdFile, L"wb" );
244 if( !fp.IsOpened() ) return false;
245
246 for( uint i=0; i<(MC2_MBSIZE*sizeInMB)/sizeof(m_effeffs); i++ )
247 {
248 if( fp.Write( m_effeffs, sizeof(m_effeffs) ) == 0 )
249 return false;
250 }
251 return true;
252 }
253
254 s32 FileMemoryCard::IsPresent( uint slot )
255 {
256 return m_file[slot].IsOpened();
257 }
258
259 void FileMemoryCard::GetSizeInfo( uint slot, PS2E_McdSizeInfo& outways )
260 {
261 outways.SectorSize = 512;
262 outways.EraseBlockSizeInSectors = 16;
263
264 if( pxAssert( m_file[slot].IsOpened() ) )
265 outways.McdSizeInSectors = m_file[slot].Length() / (outways.SectorSize + outways.EraseBlockSizeInSectors);
266 else
267 outways.McdSizeInSectors = 0x4000;
268 }
269
270 s32 FileMemoryCard::Read( uint slot, u8 *dest, u32 adr, int size )
271 {
272 wxFFile& mcfp( m_file[slot] );
273 if( !mcfp.IsOpened() )
274 {
275 DevCon.Error( "(FileMcd) Ignoring attempted read from disabled card." );
276 memset(dest, 0, size);
277 return 1;
278 }
279 if( !Seek(mcfp, adr) ) return 0;
280 return mcfp.Read( dest, size ) != 0;
281 }
282
283 s32 FileMemoryCard::Save( uint slot, const u8 *src, u32 adr, int size )
284 {
285 wxFFile& mcfp( m_file[slot] );
286
287 if( !mcfp.IsOpened() )
288 {
289 DevCon.Error( "(FileMcd) Ignoring attempted save/write to disabled card." );
290 return 1;
291 }
292
293 if( !Seek(mcfp, adr) ) return 0;
294 m_currentdata.MakeRoomFor( size );
295 mcfp.Read( m_currentdata.GetPtr(), size);
296
297 for (int i=0; i<size; i++)
298 {
299 if ((m_currentdata[i] & src[i]) != src[i])
300 Console.Warning("(FileMcd) Warning: writing to uncleared data.");
301 m_currentdata[i] &= src[i];
302 }
303
304 if( !Seek(mcfp, adr) ) return 0;
305 return mcfp.Write( m_currentdata.GetPtr(), size ) != 0;
306 }
307
308 s32 FileMemoryCard::EraseBlock( uint slot, u32 adr )
309 {
310 wxFFile& mcfp( m_file[slot] );
311
312 if( !mcfp.IsOpened() )
313 {
314 DevCon.Error( "MemoryCard: Ignoring erase for disabled card." );
315 return 1;
316 }
317
318 if( !Seek(mcfp, adr) ) return 0;
319 return mcfp.Write( m_effeffs, sizeof(m_effeffs) ) != 0;
320 }
321
322 u64 FileMemoryCard::GetCRC( uint slot )
323 {
324 wxFFile& mcfp( m_file[slot] );
325 if( !mcfp.IsOpened() ) return 0;
326
327 if( !Seek( mcfp, 0 ) ) return 0;
328
329 // Process the file in 4k chunks. Speeds things up significantly.
330 u64 retval = 0;
331 u64 buffer[528*8]; // use 528 (sector size), ensures even divisibility
332
333 const uint filesize = mcfp.Length() / sizeof(buffer);
334 for( uint i=filesize; i; --i )
335 {
336 mcfp.Read( &buffer, sizeof(buffer) );
337 for( uint t=0; t<ArraySize(buffer); ++t )
338 retval ^= buffer[t];
339 }
340
341 return retval;
342 }
343
344 // --------------------------------------------------------------------------------------
345 // MemoryCard Component API Bindings
346 // --------------------------------------------------------------------------------------
347
348 struct Component_FileMcd
349 {
350 PS2E_ComponentAPI_Mcd api; // callbacks the plugin provides back to the emulator
351 FileMemoryCard impl; // class-based implementations we refer to when API is invoked
352
353 Component_FileMcd();
354 };
355
356 uint FileMcd_ConvertToSlot( uint port, uint slot )
357 {
358 if( slot == 0 ) return port;
359 if( port == 0 ) return slot+1; // multitap 1
360 return slot + 4; // multitap 2
361 }
362
363 static void PS2E_CALLBACK FileMcd_EmuOpen( PS2E_THISPTR thisptr, const PS2E_SessionInfo *session )
364 {
365 thisptr->impl.Open();
366 }
367
368 static void PS2E_CALLBACK FileMcd_EmuClose( PS2E_THISPTR thisptr )
369 {
370 thisptr->impl.Close();
371 }
372
373 static s32 PS2E_CALLBACK FileMcd_IsPresent( PS2E_THISPTR thisptr, uint port, uint slot )
374 {
375 return thisptr->impl.IsPresent( FileMcd_ConvertToSlot( port, slot ) );
376 }
377
378 static void PS2E_CALLBACK FileMcd_GetSizeInfo( PS2E_THISPTR thisptr, uint port, uint slot, PS2E_McdSizeInfo* outways )
379 {
380 thisptr->impl.GetSizeInfo( FileMcd_ConvertToSlot( port, slot ), *outways );
381 }
382
383 static s32 PS2E_CALLBACK FileMcd_Read( PS2E_THISPTR thisptr, uint port, uint slot, u8 *dest, u32 adr, int size )
384 {
385 return thisptr->impl.Read( FileMcd_ConvertToSlot( port, slot ), dest, adr, size );
386 }
387
388 static s32 PS2E_CALLBACK FileMcd_Save( PS2E_THISPTR thisptr, uint port, uint slot, const u8 *src, u32 adr, int size )
389 {
390 return thisptr->impl.Save( FileMcd_ConvertToSlot( port, slot ), src, adr, size );
391 }
392
393 static s32 PS2E_CALLBACK FileMcd_EraseBlock( PS2E_THISPTR thisptr, uint port, uint slot, u32 adr )
394 {
395 return thisptr->impl.EraseBlock( FileMcd_ConvertToSlot( port, slot ), adr );
396 }
397
398 static u64 PS2E_CALLBACK FileMcd_GetCRC( PS2E_THISPTR thisptr, uint port, uint slot )
399 {
400 return thisptr->impl.GetCRC( FileMcd_ConvertToSlot( port, slot ) );
401 }
402
403 Component_FileMcd::Component_FileMcd()
404 {
405 memzero( api );
406
407 api.Base.EmuOpen = FileMcd_EmuOpen;
408 api.Base.EmuClose = FileMcd_EmuClose;
409
410 api.McdIsPresent = FileMcd_IsPresent;
411 api.McdGetSizeInfo = FileMcd_GetSizeInfo;
412 api.McdRead = FileMcd_Read;
413 api.McdSave = FileMcd_Save;
414 api.McdEraseBlock = FileMcd_EraseBlock;
415 api.McdGetCRC = FileMcd_GetCRC;
416 }
417
418
419 // --------------------------------------------------------------------------------------
420 // Library API Implementations
421 // --------------------------------------------------------------------------------------
422 static const char* PS2E_CALLBACK FileMcd_GetName()
423 {
424 return "PlainJane Mcd";
425 }
426
427 static const PS2E_VersionInfo* PS2E_CALLBACK FileMcd_GetVersion( u32 component )
428 {
429 static const PS2E_VersionInfo version = { 0,1,0, SVN_REV };
430 return &version;
431 }
432
433 static s32 PS2E_CALLBACK FileMcd_Test( u32 component, const PS2E_EmulatorInfo* xinfo )
434 {
435 if( component != PS2E_TYPE_Mcd ) return 0;
436
437 // Check and make sure the user has a hard drive?
438 // Probably not necessary :p
439 return 1;
440 }
441
442 static PS2E_THISPTR PS2E_CALLBACK FileMcd_NewComponentInstance( u32 component )
443 {
444 if( component != PS2E_TYPE_Mcd ) return NULL;
445
446 try
447 {
448 return new Component_FileMcd();
449 }
450 catch( std::bad_alloc& )
451 {
452 Console.Error( "Allocation failed on Component_FileMcd! (out of memory?)" );
453 }
454 return NULL;
455 }
456
457 static void PS2E_CALLBACK FileMcd_DeleteComponentInstance( PS2E_THISPTR instance )
458 {
459 delete instance;
460 }
461
462 static void PS2E_CALLBACK FileMcd_SetSettingsFolder( const char* folder )
463 {
464 }
465
466 static void PS2E_CALLBACK FileMcd_SetLogFolder( const char* folder )
467 {
468 }
469
470 static const PS2E_LibraryAPI FileMcd_Library =
471 {
472 FileMcd_GetName,
473 FileMcd_GetVersion,
474 FileMcd_Test,
475 FileMcd_NewComponentInstance,
476 FileMcd_DeleteComponentInstance,
477 FileMcd_SetSettingsFolder,
478 FileMcd_SetLogFolder
479 };
480
481 // If made into an external plugin, this function should be renamed to PS2E_InitAPI, so that
482 // PCSX2 can find the export in the expected location.
483 extern "C" const PS2E_LibraryAPI* FileMcd_InitAPI( const PS2E_EmulatorInfo* emuinfo )
484 {
485 return &FileMcd_Library;
486 }
487
488 // --------------------------------------------------------------------------------------
489 // Currently Unused Superblock Header Struct
490 // --------------------------------------------------------------------------------------
491 // (provided for reference purposes)
492
493 struct superblock
494 {
495 char magic[28]; // 0x00
496 char version[12]; // 0x1c
497 u16 page_len; // 0x28
498 u16 pages_per_cluster; // 0x2a
499 u16 pages_per_block; // 0x2c
500 u16 unused; // 0x2e
501 u32 clusters_per_card; // 0x30
502 u32 alloc_offset; // 0x34
503 u32 alloc_end; // 0x38
504 u32 rootdir_cluster; // 0x3c
505 u32 backup_block1; // 0x40
506 u32 backup_block2; // 0x44
507 u32 ifc_list[32]; // 0x50
508 u32 bad_block_list[32]; // 0xd0
509 u8 card_type; // 0x150
510 u8 card_flags; // 0x151
511 };

  ViewVC Help
Powered by ViewVC 1.1.22