Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory Card as folder instead of a big raw file. #359

Merged
merged 44 commits into from Jul 20, 2015

Conversation

AdmiralCurtiss
Copy link
Contributor

This adds a class (and some helper structures) that implements memory card reads and writes into the host file system instead of a raw memory card file.

I was mostly doing this because I was curious if it was possible to implement the same sort of thing Dolphin added recently for the PS2 as well, and it kinda is, although the way and order the PS2 writes FAT data make it quite difficult at times. I ended up caching all modified pages of data in memory and only flushing to disk when the emulator is shutting down, which isn't great but at least works consistently. Memory Card FAT is recreated whenever it's opened, so you can freely add and remove files from the folder.

It's probably far from perfect and could use some optimization, especially when it comes to finding a good time to flush the in-memory data, but it works quite well for regular use of creating and overwriting saves. Deleting isn't implemented yet. I imagine that savestates could mess with it quite a bit, but that's probably the case with the raw files as well.

Here's a picture of it running in the PS2 file browser: https://cloud.githubusercontent.com/assets/4522237/5193663/b7cb128e-7503-11e4-8cbe-8fade86e678d.png

To test it out, comment out the FileMemoryCard impl in Component_FileMcd and comment in the FolderMemoryCardAggregator instead. Ideally, there should be an option to allow the user to switch between the two modes. A few other small GUI changes would also be needed so you can select folders instead of files as memory cards.

Tell me what you think and if this could be a good addition to the emulator with a bit more polish.

@pauldacheez
Copy link

Dolphin includes games’ individual saves in savestates, if you’re wondering how that’s handled. You'll also ideally want to handle size/file-per-card limitations in a way that doesn't prevent games from accessing files other than their own (e.g. the Ratchet & Clank series, where having data from previous games makes the guns-from-previous-games weapon shop sell everything much cheaper). Dolphin does this, but I haven't really looked at the code to know how it does so, nor do I know if it's even deterministic (which matters for things like netplay).

Anyway, code looks sufficiently clean and well-documented to me, aside from the handful of todos and the sheer length of the commit log. I'm not a skilled programmer, though, so feel free to ignore my code review. v_v

@AdmiralCurtiss
Copy link
Contributor Author

If you want me to merge some of these commits I can totally do that.

@ramapcsx2
Copy link
Member

That's pretty cool that you coded this into deliverable state even! :p

I have a suggestion for handling savestates:
Basically it's best to copy the regular behavior and eject the virtual memory card on savestate load,
then reinsert it after the game tried a few accesses to it.
This works in all games (to my knowledge) and it fully avoids corruption from illogical memory card states.
The worst that will happen is that a game might complain about the card missing a few times.
Edit: pauldacheez' suggestion is not bad either, but we know the "regular" method we use is working very well :)

Regarding the flushing:
If you can reliably detect a game writing out a savegame (and when it's fully done with it), then it would be best to flush to disk there.
Flushing at regular intervals of say, 60 seconds, is also possible.

@AdmiralCurtiss
Copy link
Contributor Author

Regarding auto-ejecting on save load, I see that the current implementation checks that by comparing memory card CRC and reinserting if it mismatches. Since it's actually just about seeing if the memory card state of the savestate and the current state match, my GetCRC() now returns a millisecond timestamp of the last time the card was written to. This should work out to the intended result, though I had to use wxGetLocalTimeMillis() instead of wxGetUTCTimeMillis() since the latter isn't available for some reason.

Regarding flushing, yes, detecting a finished write would be ideal. Something along the lines of "if no data has been written in x seconds or frames, assume the current operation is done" might work, but I'm unsure if that's reliable.

@ramapcsx2
Copy link
Member

I've done a quick test of this and it works nicely :)
Flushing after no data written for x frames is a good start, I think. I'd put in a console log whenever it
does flush and then check with a couple of games. Flushing should also be done on emulation resets and even disk changes, though it's possible the improved regular flush suffices.
In any case, this is some nice work and if you want it merged, no problem! :)

@gregory38
Copy link
Contributor

Could please fix the new warnings in your code. And could you also fix the linux error. Thanks you.

By the way, I don't understand what is the goal of this commit but is there any impact of the current memcard file (I mean from a compatibility point of view)? Or is this fully transparent.

Building CXX object pcsx2/CMakeFiles/pcsx2-dbg.dir/IPU/IPU_Fifo.cpp.o
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:580:23: error: extra qualification ‘FolderMemoryCard::’ on member ‘GetFileEntryFromFileDataCluster’ [-fpermissive]
  MemoryCardFileEntry* FolderMemoryCard::GetFileEntryFromFileDataCluster( const u32 currentCluster, const u32 searchCluster, wxFileName* fileName, const size_t originalDirCount, u32* outClusterNumber );
                       ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘void FolderMemoryCard::InitializeInternalData()’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:665:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < TotalPages; ++i ) {
                     ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘u32 FolderMemoryCard::GetFreeSystemCluster()’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:790:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < IndirectFatClusterCount; ++i ) {
                     ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:795:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < IndirectFatClusterCount; ++i ) {
                     ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:796:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
   for ( int j = 0; j < ClusterSize / 4; ++j ) {
                      ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘u8* FolderMemoryCard::GetSystemBlockPointer(u32)’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1045:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
   for ( int i = 0; i < IndirectFatClusterCount; ++i ) {
                      ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1052:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
   for ( int i = 0; i < IndirectFatClusterCount; ++i ) {
                      ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1053:23: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    for ( int j = 0; j < ClusterSize / 4; ++j ) {
                       ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘s32 FolderMemoryCard::Read(u8*, u32, int)’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1221:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
   for ( int i = 0; i < PageSize / 0x80; ++i ) {
                      ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘void FolderMemoryCard::Flush()’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1276:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < IndirectFatClusterCount; ++i ) {
                     ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1286:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < IndirectFatClusterCount; ++i ) {
                     ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1287:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
   for ( int j = 0; j < ClusterSize / 4; ++j ) {
                      ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1307:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < TotalPages; ++i ) {
                     ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘void FolderMemoryCard::FlushFileEntries(u32, u32)’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1330:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < filesInThisCluster; ++i ) {
                     ^
pcsx2/CMakeFiles/pcsx2-dbg.dir/build.make:3331: recipe for target 'pcsx2/CMakeFiles/pcsx2-dbg.dir/gui/MemoryCardFile.cpp.o' failed
make[2]: *** [pcsx2/CMakeFiles/pcsx2-dbg.dir/gui/MemoryCardFile.cpp.o] Error 1
make[2]: *** Waiting for unfinished jobs....
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MainFrame.cpp: In member function ‘void MainEmuFrame::OnCloseWindow(wxCloseEvent&)’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MainFrame.cpp:109:7: warning: variable ‘isClosing’ set but not used [-Wunused-but-set-variable]
  bool isClosing = false;
       ^
CMakeFiles/Makefile2:278: recipe for target 'pcsx2/CMakeFiles/pcsx2-dbg.dir/all' failed
make[1]: *** [pcsx2/CMakeFiles/pcsx2-dbg.dir/all] Error 2
Makefile:117: recipe for target 'all' failed
make: *** [all] Error 2
[  5%] Built target translations_pcsx2_Iconized
[ 10%] Built target translations_pcsx2_Main
[ 16%] Built target Utilities
[ 19%] Built target x86emitter
[ 19%] Building CXX object pcsx2/CMakeFiles/pcsx2-dbg.dir/gui/MemoryCardFile.cpp.o
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:580:23: error: extra qualification ‘FolderMemoryCard::’ on member ‘GetFileEntryFromFileDataCluster’ [-fpermissive]
  MemoryCardFileEntry* FolderMemoryCard::GetFileEntryFromFileDataCluster( const u32 currentCluster, const u32 searchCluster, wxFileName* fileName, const size_t originalDirCount, u32* outClusterNumber );
                       ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘void FolderMemoryCard::InitializeInternalData()’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:665:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < TotalPages; ++i ) {
                     ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘u32 FolderMemoryCard::GetFreeSystemCluster()’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:790:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < IndirectFatClusterCount; ++i ) {
                     ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:795:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < IndirectFatClusterCount; ++i ) {
                     ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:796:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
   for ( int j = 0; j < ClusterSize / 4; ++j ) {
                      ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘u8* FolderMemoryCard::GetSystemBlockPointer(u32)’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1045:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
   for ( int i = 0; i < IndirectFatClusterCount; ++i ) {
                      ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1052:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
   for ( int i = 0; i < IndirectFatClusterCount; ++i ) {
                      ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1053:23: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    for ( int j = 0; j < ClusterSize / 4; ++j ) {
                       ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘s32 FolderMemoryCard::Read(u8*, u32, int)’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1221:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
   for ( int i = 0; i < PageSize / 0x80; ++i ) {
                      ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘void FolderMemoryCard::Flush()’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1276:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < IndirectFatClusterCount; ++i ) {
                     ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1286:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < IndirectFatClusterCount; ++i ) {
                     ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1287:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
   for ( int j = 0; j < ClusterSize / 4; ++j ) {
                      ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1307:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < TotalPages; ++i ) {
                     ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘void FolderMemoryCard::FlushFileEntries(u32, u32)’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:1330:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  for ( int i = 0; i < filesInThisCluster; ++i ) {
                     ^
pcsx2/CMakeFiles/pcsx2-dbg.dir/build.make:3331: recipe for target 'pcsx2/CMakeFiles/pcsx2-dbg.dir/gui/MemoryCardFile.cpp.o' failed
make[2]: *** [pcsx2/CMakeFiles/pcsx2-dbg.dir/gui/MemoryCardFile.cpp.o] Error 1
CMakeFiles/Makefile2:278: recipe for target 'pcsx2/CMakeFiles/pcsx2-dbg.dir/all' failed

@ramapcsx2
Copy link
Member

@gregory38: All new code is for adding an additional implementation like "FileMemoryCard impl".
You can comment out "FileMemoryCard" and use "FolderMemoryCard" instead.
If you don't swap the 2, nothing will be affected. (So we could merge this right now and functionally nothing changes.)

@AdmiralCurtiss
Copy link
Contributor Author

@gregory38 Will do.

@AdmiralCurtiss
Copy link
Contributor Author

Are there any guidelines on how to implement timed events in PCSX2? Especially if they should depend on emulation time rather than real clock time.

@AdmiralCurtiss
Copy link
Contributor Author

So I tried implementing delayed post-write flushing using a wxTimer which is reset every time a write occurs, and thus would only actually trigger and call Flush() after a few seconds of no writes, but kinda ran into a brick wall because -- apparently -- wxTimers can only be started from the main thread. Is there a different timer I could use instead?

@ramapcsx2
Copy link
Member

Hm, if real world timers cause problems, you could base it off of various emulation events.
It doesn't have to be a wxTimer btw, any timing source that's universal (portable) works.

@AdmiralCurtiss
Copy link
Contributor Author

Is there an easy way to hook myself into emulation events, or do I have to explicitly insert a call into, say, VSyncEnd()? Cause the latter seems rather intrusive, especially since not all memory card implementations require it.

@ramapcsx2
Copy link
Member

Adding your timing there seems fine to me. If you look around, you should find other events also using VSyncEnd() as a timer.
It's okay when the code clearly states what it does and isn't slow.
I recommend using #defines to declare which of the 2 memory card implementations to use, then also encapsulating the new timer code in an #ifdef.

@AdmiralCurtiss
Copy link
Contributor Author

Alright then. Comment in the #define MEMORYCARD_USE_FOLDER in MemoryCardFile.h to use the FolderMemoryCard, otherwise it uses the default FileMemoryCard. Rudimentary tests (idling ingame with the frame limiter off) suggest no noticeable difference in performance. I'm not sure I'm happy with the additional method in the plugin API, but whatever, it works.

I chose 60 frames as the cutoff for assuming the current write has finished, which seems reasonable enough to me. You can change it by modifying the FramesAfterWriteUntilFlush constant in the FolderMemoryCard class.

@ramapcsx2
Copy link
Member

Pretty coding there, not bad :)
I'm not sure about adding methods to the plugin specs either so I'll ask around on that.
Otherwise this looks great and with the define, it's quick and easy to test as well.

Hm, just one "assignee" possible in Github. Ref should've gotten a mail though :p

@refractionpcsx2
Copy link
Member

Yeah i got it, ill give it a test at some point :)

@ramapcsx2
Copy link
Member

The plugin api has reserved space for these kinds of additions. I guess it's a question of finding out whether that actually works (with savestates for example).

@gregory38
Copy link
Contributor

Still no luck on linux. Be aware of Unicode. You probably need to use the WX_STR macro.

home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘void FolderMemoryCard::AddFolder(MemoryCardFileEntry*, const wxString&)’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:883:70: error: cannot pass objects of non-trivially-copyable type ‘class wxCStrData’ through ‘...’
   Console.WriteLn( L"(FolderMcd) Adding folder: %s", dirPath.c_str() );
                                                                      ^
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp: In member function ‘void FolderMemoryCard::AddFile(MemoryCardFileEntry*, const wxString&, const wxString&)’:
/home/gregory/playstation/emulateur/pcsx2_merge/pcsx2/gui/MemoryCardFile.cpp:955:90: error: cannot pass objects of non-trivially-copyable type ‘class wxCStrData’ through ‘...’
  Console.WriteLn( L"(FolderMcd) Adding file: %s", relativeFilePath.GetFullPath().c_str() );
                                                                                          ^

@AdmiralCurtiss
Copy link
Contributor Author

Hm that's weird, I made sure it compiled fine on Xubuntu after you pointed out the warnings last time. I'll try to figure it out.

@gregory38
Copy link
Contributor

Hum it could be related to wx2.8/wx3.0. IIRC .c_str() doesn't work anymore on latest wx

@AdmiralCurtiss
Copy link
Contributor Author

Yup that seems to have been it. Try now.

@gregory38
Copy link
Contributor

Good it compiled fine on linux :)

@uyjulian
Copy link
Contributor

Compiles fine on Windows vs2013...
Why isn't this branch merged... yet, I don't see anybody asking problems that need to be fixed

@ramapcsx2
Copy link
Member

Thanks for your comment, uyjulian.
Stuff will get merged when I can find some time for final tests.
It worked well here last time though, before the Linux fixes.

@awsdert
Copy link

awsdert commented Mar 14, 2015

In regards the detection of read/writes, why not simply turn the original function names into a pointers to the core handlers and then let the new handlers override the pointers and call the original function themselves when it's done what it needs to do?

…also fit into the remaining memory card space.

This avoids situations where, for example, it would only add the icon file but not the game data file because the memory card was near-full.
…ethods as const where appropriate, and some other minor things.
We're not actually deleting files though, we just rename them to prepend _pcsx2_deleted_, in case something breaks or whatever, so the user can in an emergency just restore the save by removing that part of the filename.
…perly on first write to any given page.

I'm kinda surprised this didn't horribly break things, honestly. I guess it's because memory card data is always written in blocks, but still.
…when the card is Close()d.

This mainly means that the superblock is now no longer written every single time the memory card is closed, but only when it's changed (which should be exactly once, when the memory card is formatted). It also means that you can format a memory card and then have the emulator crash later without having to reformat the card next time.
…that have actually changed since the last known memory card state on the host file system.

This means that we are now no longer touching files that haven't technically been written to. Some games use timestamp information to automatically highlight the save that was last written to, so this should fix a small but annoying bug where it would highlight the wrong one.

Do note that while there is a much simpler check that looks like this:
	// Remove (== don't flush) all memory card pages that haven't actually changed.
	for ( auto oldIt = m_oldDataCache.begin(); oldIt != m_oldDataCache.end(); ++oldIt ) {
		auto newIt = m_cache.find( oldIt->first );
		assert( newIt != m_cache.end() ); // if this isn't true something broke somewhere, the two maps should always contain the same pages
		if ( memcmp( &oldIt->second.raw[0], &newIt->second.raw[0], PageSize ) == 0 ) {
			m_cache.erase( newIt );
		}
	}
	m_oldDataCache.clear();
It can fail in edge cases that don't actually seem too unlikely. Imagine a save being deleted, and then a new save from the same game but in a different slot being created quickly afterwards. It seems quite possible that the new save's file data then occupies the exact same pages as the old save's, and since it's from the same game it might be close enough to where a page sized section (juse 0x200 bytes!) matches the data from the old save that previously resided in that location -- which would cause this code to throw away and not flush this data! It's a shame too, since this variant would be a few ms faster as well, but I feel it's better to be safe than sorry here.
@AdmiralCurtiss
Copy link
Contributor Author

Rebased. Windows build here: https://dl.dropboxusercontent.com/u/52687139/pcsx2-foldermemcard-public-test-rev7.7z

Loaded and saved in a couple games, seems fine here.

@ramapcsx2
Copy link
Member

No errors reported, from what I can see?

@AdmiralCurtiss
Copy link
Contributor Author

http://forums.pcsx2.net/Thread-New-feature-Needs-testing-Automatically-managed-Folder-Memory-Card-Public-Test?pid=469152#pid469152

Since it was basically requested, added convert options in the GUI to convert to 16MB/32MB/64MB files.

https://dl.dropboxusercontent.com/u/52687139/pcsx2-foldermemcard-public-test-rev8.7z

Identical to the previous build (rev7) otherwise.

@AdmiralCurtiss
Copy link
Contributor Author

Anything still holding up the merge of this?

@refractionpcsx2
Copy link
Member

The changes were just being tested with the new WX 3.0 stuff, as soon as i can confirm that is all good, I will merge it :) hopefully some time today

…on process once before writing the data. This prevents half-converted/corrupted cards by ensuring we crash before any actual writes occur.
refractionpcsx2 added a commit that referenced this pull request Jul 20, 2015
Memory Card as folder support by AdmiralCurtiss
@refractionpcsx2 refractionpcsx2 merged commit 539a176 into PCSX2:master Jul 20, 2015
@AdmiralCurtiss AdmiralCurtiss deleted the memcard-folder-cache branch July 20, 2015 22:20
@i30817
Copy link
Contributor

i30817 commented Jul 21, 2015

Is there any ps2 game with more than one disc? If so, they should be able to see saves from the other disc(s). Somehow...
Also i suppose any game that check for 'foreign' savedata (for easter eggs) would still work with a side by side normal memory card with that data. Probably.

@AdmiralCurtiss
Copy link
Contributor Author

Already implemented: 02ae12c

It's quite likely I missed some more obscure games so feel free to report any games that do not find saves they should be able to see.

@i30817
Copy link
Contributor

i30817 commented Jul 21, 2015

That was... a lot of work.

@awsdert
Copy link

awsdert commented Oct 24, 2021

Is there any ps2 game with more than one disc? If so, they should be able to see saves from the other disc(s). Somehow... Also i suppose any game that check for 'foreign' savedata (for easter eggs) would still work with a side by side normal memory card with that data. Probably.

Long forgot this thread, anyways to answer your question yes there are ps2 games with more than one disc, Star Ocean: Till the End of Time used 2 discs for example if I remember rightly (been a while since I booted PCSX2 to play anything)

@i30817
Copy link
Contributor

i30817 commented Oct 24, 2021

Well. Not only. Search for 'disc' and you'll see in the right hand side all of the ones with two discs - or the ones that have the possibility to import data from other games for those that don't have 'disc' on the name, which is a surprisingly large list.

@awsdert
Copy link

awsdert commented Oct 24, 2021

Never said it was the only one, merely that there are multi-disk ps2 games, since your question was on that I gave an example of such a game

@RedDevilus
Copy link
Contributor

No idea why you are guys are talking on a merged PR from 6 years ago, trying to find multi-disk games, when there is a meta: #4851
and also google.

@awsdert
Copy link

awsdert commented Oct 24, 2021

It's because I saw that no-one actually responded to i30817's question, that bugs me when a question goes ignored so I gave the response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet