diff --git a/Runtime/CMemoryCardSys.cpp b/Runtime/CMemoryCardSys.cpp index ee1590c65..3ffc92acf 100644 --- a/Runtime/CMemoryCardSys.cpp +++ b/Runtime/CMemoryCardSys.cpp @@ -11,8 +11,9 @@ namespace urde using ECardResult = kabufuda::ECardResult; -kabufuda::SystemString g_CardImagePaths[2] = {}; -kabufuda::Card g_CardStates[2] = {}; +static kabufuda::SystemString g_CardImagePaths[2] = {}; +static kabufuda::Card g_CardStates[2] = {}; +static kabufuda::ECardResult g_OpResults[2] = {}; CSaveWorldIntermediate::CSaveWorldIntermediate(ResId mlvl, ResId savw) : x0_mlvlId(mlvl), x8_savwId(savw) @@ -62,6 +63,9 @@ bool CSaveWorldIntermediate::InitializePump() CMemoryCardSys::CMemoryCardSys() { + g_CardImagePaths[0] = ResolveDolphinCardPath(kabufuda::ECardSlot::SlotA); + g_CardImagePaths[1] = ResolveDolphinCardPath(kabufuda::ECardSlot::SlotB); + x0_hints = g_SimplePool->GetObj("HINT_Hints"); xc_memoryWorlds.reserve(16); x1c_worldInter.emplace(); @@ -261,7 +265,7 @@ ECardResult CMemoryCardSys::CCardFileInfo::PumpCardTransfer() result = GetStatus(stat); if (result != ECardResult::READY) return result; - result = CMemoryCardSys::SetStatus(GetCardPort(), GetFileNo(), stat); + result = CMemoryCardSys::SetStatus(m_handle, stat); if (result != ECardResult::READY) return result; return ECardResult::BUSY; @@ -277,7 +281,7 @@ ECardResult CMemoryCardSys::CCardFileInfo::PumpCardTransfer() ECardResult CMemoryCardSys::CCardFileInfo::GetStatus(kabufuda::CardStat& stat) const { - ECardResult result = CMemoryCardSys::GetStatus(GetCardPort(), GetFileNo(), stat); + ECardResult result = CMemoryCardSys::GetStatus(m_handle, stat); if (result != ECardResult::READY) return result; @@ -333,7 +337,9 @@ ECardResult CMemoryCardSys::CCardFileInfo::CloseFile() kabufuda::ProbeResults CMemoryCardSys::CardProbe(kabufuda::ECardSlot port) { - return kabufuda::Card::probeCardFile(g_CardImagePaths[int(port)]); + kabufuda::ProbeResults res = kabufuda::Card::probeCardFile(g_CardImagePaths[int(port)]); + g_OpResults[int(port)] = res.x0_error; + return res; } ECardResult CMemoryCardSys::MountCard(kabufuda::ECardSlot port) @@ -341,6 +347,7 @@ ECardResult CMemoryCardSys::MountCard(kabufuda::ECardSlot port) kabufuda::Card& card = g_CardStates[int(port)]; card = kabufuda::Card(g_CardImagePaths[int(port)], "GM8E", "01"); ECardResult result = card.getError(); + g_OpResults[int(port)] = result; if (result == ECardResult::READY) return ECardResult::READY; card = kabufuda::Card(); @@ -350,40 +357,61 @@ ECardResult CMemoryCardSys::MountCard(kabufuda::ECardSlot port) ECardResult CMemoryCardSys::CheckCard(kabufuda::ECardSlot port) { kabufuda::Card& card = g_CardStates[int(port)]; - return card.getError(); + ECardResult result = card.getError(); + g_OpResults[int(port)] = result; + return result; } ECardResult CMemoryCardSys::CreateFile(kabufuda::ECardSlot port, const char* name, u32 size, CardFileHandle& info) { kabufuda::Card& card = g_CardStates[int(port)]; if (CardResult err = card.getError()) + { + g_OpResults[int(port)] = err; return err; + } info.slot = port; - return card.createFile(name, size, info.handle); + ECardResult result = card.createFile(name, size, info.handle); + g_OpResults[int(port)] = result; + return result; } ECardResult CMemoryCardSys::OpenFile(kabufuda::ECardSlot port, const char* name, CardFileHandle& info) { kabufuda::Card& card = g_CardStates[int(port)]; if (CardResult err = card.getError()) + { + g_OpResults[int(port)] = err; return err; + } info.slot = port; - info.handle = card.openFile(name); - return info.handle ? ECardResult::READY : ECardResult::NOFILE; + ECardResult result = card.openFile(name, info.handle); + g_OpResults[int(port)] = result; + return result; } ECardResult CMemoryCardSys::FastOpenFile(kabufuda::ECardSlot port, int fileNo, CardFileHandle& info) { kabufuda::Card& card = g_CardStates[int(port)]; if (CardResult err = card.getError()) + { + g_OpResults[int(port)] = err; return err; + } info.slot = port; - info.handle = card.openFile(fileNo); - return info.handle ? ECardResult::READY : ECardResult::NOFILE; + ECardResult result = card.openFile(fileNo, info.handle); + g_OpResults[int(port)] = result; + return result; } ECardResult CMemoryCardSys::CloseFile(CardFileHandle& info) { + kabufuda::Card& card = g_CardStates[int(info.slot)]; + if (CardResult err = card.getError()) + { + g_OpResults[int(info.slot)] = err; + return err; + } info.handle.reset(); return ECardResult::READY; } @@ -392,9 +420,13 @@ ECardResult CMemoryCardSys::ReadFile(const CardFileHandle& info, void* buf, s32 { kabufuda::Card& card = g_CardStates[int(info.slot)]; if (CardResult err = card.getError()) + { + g_OpResults[int(info.slot)] = err; return err; + } card.seek(info, offset, kabufuda::SeekOrigin::Begin); card.read(info, buf, length); + g_OpResults[int(info.slot)] = ECardResult::READY; return ECardResult::READY; } @@ -402,9 +434,13 @@ ECardResult CMemoryCardSys::WriteFile(const CardFileHandle& info, const void* bu { kabufuda::Card& card = g_CardStates[int(info.slot)]; if (CardResult err = card.getError()) + { + g_OpResults[int(info.slot)] = err; return err; + } card.seek(info, offset, kabufuda::SeekOrigin::Begin); card.write(info, buf, length); + g_OpResults[int(info.slot)] = ECardResult::READY; return ECardResult::READY; } @@ -412,8 +448,12 @@ ECardResult CMemoryCardSys::GetNumFreeBytes(kabufuda::ECardSlot port, s32& freeB { kabufuda::Card& card = g_CardStates[int(port)]; if (CardResult err = card.getError()) + { + g_OpResults[int(port)] = err; return err; + } card.getFreeBlocks(freeBytes, freeFiles); + g_OpResults[int(port)] = ECardResult::READY; return ECardResult::READY; } @@ -421,56 +461,95 @@ ECardResult CMemoryCardSys::GetSerialNo(kabufuda::ECardSlot port, u64& serialOut { kabufuda::Card& card = g_CardStates[int(port)]; if (CardResult err = card.getError()) + { + g_OpResults[int(port)] = err; return err; + } card.getSerial(serialOut); + g_OpResults[int(port)] = ECardResult::READY; return ECardResult::READY; } ECardResult CMemoryCardSys::GetResultCode(kabufuda::ECardSlot port) { - return ECardResult::READY; + return g_OpResults[int(port)]; } -ECardResult CMemoryCardSys::GetStatus(kabufuda::ECardSlot port, int fileNo, CardStat& statOut) +ECardResult CMemoryCardSys::GetStatus(const CardFileHandle& info, CardStat& statOut) { - return ECardResult::READY; + kabufuda::Card& card = g_CardStates[int(info.slot)]; + if (CardResult err = card.getError()) + { + g_OpResults[int(info.slot)] = err; + return err; + } + ECardResult result = card.getStatus(info, statOut); + g_OpResults[int(info.slot)] = result; + return result; } -ECardResult CMemoryCardSys::SetStatus(kabufuda::ECardSlot port, int fileNo, const CardStat& stat) +ECardResult CMemoryCardSys::SetStatus(const CardFileHandle& info, const CardStat& stat) { - return ECardResult::READY; + kabufuda::Card& card = g_CardStates[int(info.slot)]; + if (CardResult err = card.getError()) + { + g_OpResults[int(info.slot)] = err; + return err; + } + ECardResult result = card.setStatus(info, stat); + g_OpResults[int(info.slot)] = result; + return result; } ECardResult CMemoryCardSys::DeleteFile(kabufuda::ECardSlot port, const char* name) { kabufuda::Card& card = g_CardStates[int(port)]; if (CardResult err = card.getError()) + { + g_OpResults[int(port)] = err; return err; - return card.deleteFile(name); + } + ECardResult result = card.deleteFile(name); + g_OpResults[int(port)] = result; + return result; } ECardResult CMemoryCardSys::FastDeleteFile(kabufuda::ECardSlot port, int fileNo) { kabufuda::Card& card = g_CardStates[int(port)]; if (CardResult err = card.getError()) + { + g_OpResults[int(port)] = err; return err; - return card.deleteFile(fileNo); + } + ECardResult result = card.deleteFile(fileNo); + g_OpResults[int(port)] = result; + return result; } ECardResult CMemoryCardSys::Rename(kabufuda::ECardSlot port, const char* oldName, const char* newName) { kabufuda::Card& card = g_CardStates[int(port)]; if (CardResult err = card.getError()) + { + g_OpResults[int(port)] = err; return err; - return card.renameFile(oldName, newName); + } + ECardResult result = card.renameFile(oldName, newName); + g_OpResults[int(port)] = result; + return result; } ECardResult CMemoryCardSys::FormatCard(kabufuda::ECardSlot port) { kabufuda::Card& card = g_CardStates[int(port)]; if (CardResult err = card.getError()) + { + g_OpResults[int(port)] = err; return err; + } card.format(port); + g_OpResults[int(port)] = ECardResult::READY; return ECardResult::READY; } diff --git a/Runtime/CMemoryCardSys.hpp b/Runtime/CMemoryCardSys.hpp index 6f9d8876c..30a390b16 100644 --- a/Runtime/CMemoryCardSys.hpp +++ b/Runtime/CMemoryCardSys.hpp @@ -58,6 +58,8 @@ class CMemoryCardSys std::experimental::optional> x1c_worldInter; /* used to be auto_ptr of vector */ std::vector> x20_scanStates; public: + static kabufuda::SystemString ResolveDolphinCardPath(kabufuda::ECardSlot slot); + using ECardResult = kabufuda::ECardResult; struct CardResult { @@ -153,8 +155,8 @@ public: static ECardResult GetNumFreeBytes(kabufuda::ECardSlot port, s32& freeBytes, s32& freeFiles); static ECardResult GetSerialNo(kabufuda::ECardSlot port, u64& serialOut); static ECardResult GetResultCode(kabufuda::ECardSlot port); - static ECardResult GetStatus(kabufuda::ECardSlot port, int fileNo, CardStat& statOut); - static ECardResult SetStatus(kabufuda::ECardSlot port, int fileNo, const CardStat& stat); + static ECardResult GetStatus(const CardFileHandle& info, CardStat& statOut); + static ECardResult SetStatus(const CardFileHandle& info, const CardStat& stat); static ECardResult DeleteFile(kabufuda::ECardSlot port, const char* name); static ECardResult FastDeleteFile(kabufuda::ECardSlot port, int fileNo); static ECardResult Rename(kabufuda::ECardSlot port, const char* oldName, const char* newName); diff --git a/Runtime/CMemoryCardSysNix.cpp b/Runtime/CMemoryCardSysNix.cpp index 6768d64bc..5e5264c65 100644 --- a/Runtime/CMemoryCardSysNix.cpp +++ b/Runtime/CMemoryCardSysNix.cpp @@ -3,4 +3,31 @@ namespace urde { +kabufuda::SystemString CMemoryCardSys::ResolveDolphinCardPath(kabufuda::ECardSlot slot) +{ + const char* home = getenv("HOME"); + if (!home || home[0] != '/') + return {}; + const char* dataHome = getenv("XDG_DATA_HOME"); + + /* XDG-selected data path */ + kabufuda::SystemString path = + (dataHome && dataHome[0] == '/') ? dataHome : (home + "/.local/share") + "/.dolphin-emu"; + path += hecl::Format("/GC/MemoryCard%c.USA.raw", + slot == kabufuda::ECardSlot::SlotA ? 'A' : 'B'); + + hecl::Sstat theStat; + if (hecl::Stat(path.c_str(), &theStat) || !S_ISREG(theStat.st_mode)) + { + /* legacy case for older dolphin versions */ + path = home; + path += hecl::Format("/.dolphin-emu/GC/MemoryCard%c.USA.raw", + slot == kabufuda::ECardSlot::SlotA ? 'A' : 'B'); + if (hecl::Stat(path.c_str(), &theStat) || !S_ISREG(theStat.st_mode)) + return {}; + } + + return path; +} + } diff --git a/Runtime/CMemoryCardSysOSX.cpp b/Runtime/CMemoryCardSysOSX.cpp index e69de29bb..87b6c13cc 100644 --- a/Runtime/CMemoryCardSysOSX.cpp +++ b/Runtime/CMemoryCardSysOSX.cpp @@ -0,0 +1,23 @@ +#include "CMemoryCardSys.hpp" + +namespace urde +{ + +kabufuda::SystemString CMemoryCardSys::ResolveDolphinCardPath(kabufuda::ECardSlot slot) +{ + const char* home = getenv("HOME"); + if (!home) + return {}; + + kabufuda::SystemString path = home; + path += hecl::Format("/Library/Application Support/Dolphin/GC/MemoryCard%c.USA.raw", + slot == kabufuda::ECardSlot::SlotA ? 'A' : 'B'); + + hecl::Sstat theStat; + if (hecl::Stat(path.c_str(), &theStat) || !S_ISREG(theStat.st_mode)) + return {}; + + return path; +} + +} diff --git a/Runtime/CMemoryCardSysWin.cpp b/Runtime/CMemoryCardSysWin.cpp index e69de29bb..a9fcafa46 100644 --- a/Runtime/CMemoryCardSysWin.cpp +++ b/Runtime/CMemoryCardSysWin.cpp @@ -0,0 +1,57 @@ +#include "CMemoryCardSys.hpp" +#include + +namespace urde +{ + +/* Partial path-selection logic from + * https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/UICommon/UICommon.cpp + * Modified to not use dolphin-binary-relative paths. */ +kabufuda::SystemString CMemoryCardSys::ResolveDolphinCardPath(kabufuda::ECardSlot slot) +{ + kabufuda::SystemString userPath; + + /* Detect where the User directory is. There are two different cases + * 1. HKCU\Software\Dolphin Emulator\UserConfigPath exists + * -> Use this as the user directory path + * 2. My Documents exists + * -> Use My Documents\Dolphin Emulator as the User directory path + */ + + /* Check our registry keys */ + HKEY hkey; + kabufuda::SystemChar configPath[MAX_PATH] = {0}; + if (RegOpenKeyEx(HKEY_CURRENT_USER, _S("Software\\Dolphin Emulator"), 0, KEY_QUERY_VALUE, + &hkey) == ERROR_SUCCESS) + { + DWORD size = MAX_PATH; + if (RegQueryValueEx(hkey, _S("UserConfigPath"), nullptr, nullptr, (LPBYTE)configPath, + &size) != ERROR_SUCCESS) + configPath[0] = 0; + RegCloseKey(hkey); + } + + /* Get My Documents path in case we need it. */ + kabufuda::SystemChar my_documents[MAX_PATH]; + bool my_documents_found = SUCCEEDED( + SHGetFolderPath(nullptr, CSIDL_MYDOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, my_documents)); + + if (configPath[0]) /* Case 1 */ + userPath = configPath; + else if (my_documents_found) /* Case 2 */ + userPath = my_documents + _S("/Dolphin Emulator"); + else /* Unable to find */ + return {}; + + kabufuda::SystemString path = userPath; + path += hecl::SysFormat(_S("/GC/MemoryCard%c.USA.raw"), + slot == kabufuda::ECardSlot::SlotA ? _S('A') : _S('B')); + + hecl::Sstat theStat; + if (hecl::Stat(path.c_str(), &theStat) || !S_ISREG(theStat.st_mode)) + return {}; + + return path; +} + +} diff --git a/Runtime/MP1/CMemoryCardDriver.cpp b/Runtime/MP1/CMemoryCardDriver.cpp index a16a3851a..494016678 100644 --- a/Runtime/MP1/CMemoryCardDriver.cpp +++ b/Runtime/MP1/CMemoryCardDriver.cpp @@ -29,7 +29,7 @@ ECardResult CMemoryCardDriver::SFileInfo::Close() ECardResult CMemoryCardDriver::SFileInfo::StartRead() { CMemoryCardSys::CardStat stat = {}; - ECardResult result = CMemoryCardSys::GetStatus(GetFileCardPort(), GetFileNo(), stat); + ECardResult result = CMemoryCardSys::GetStatus(x0_fileInfo, stat); if (result != ECardResult::READY) return result; @@ -78,7 +78,7 @@ ECardResult CMemoryCardDriver::SFileInfo::FileRead() ECardResult CMemoryCardDriver::SFileInfo::GetSaveDataOffset(u32& offOut) { CMemoryCardSys::CardStat stat = {}; - ECardResult result = CMemoryCardSys::GetStatus(GetFileCardPort(), GetFileNo(), stat); + ECardResult result = CMemoryCardSys::GetStatus(x0_fileInfo, stat); if (result != ECardResult::READY) { offOut = -1; @@ -183,7 +183,7 @@ void CMemoryCardDriver::ReadFinished() { CMemoryCardSys::CardStat stat = {}; SFileInfo& fileInfo = x100_mcFileInfos[x194_fileIdx].second; - if (CMemoryCardSys::GetStatus(x0_cardPort, fileInfo.GetFileNo(), stat) != ECardResult::READY) + if (CMemoryCardSys::GetStatus(fileInfo.x0_fileInfo, stat) != ECardResult::READY) { NoCardFound(); return; @@ -239,7 +239,7 @@ void CMemoryCardDriver::IndexFiles() else if (result == ECardResult::READY) { CMemoryCardSys::CardStat stat = {}; - if (CMemoryCardSys::GetStatus(x0_cardPort, info.second.GetFileNo(), stat) == + if (CMemoryCardSys::GetStatus(info.second.x0_fileInfo, stat) == ECardResult::READY) { u32 comment = stat.GetCommentAddr(); @@ -272,11 +272,11 @@ void CMemoryCardDriver::IndexFiles() if (x100_mcFileInfos[1].first == EFileState::File) { CMemoryCardSys::CardStat stat = {}; - if (CMemoryCardSys::GetStatus(x0_cardPort, x100_mcFileInfos[0].second.GetFileNo(), stat) == + if (CMemoryCardSys::GetStatus(x100_mcFileInfos[0].second.x0_fileInfo, stat) == ECardResult::READY) { u32 timeA = stat.GetTime(); - if (CMemoryCardSys::GetStatus(x0_cardPort, x100_mcFileInfos[1].second.GetFileNo(), stat) == + if (CMemoryCardSys::GetStatus(x100_mcFileInfos[1].second.x0_fileInfo, stat) == ECardResult::READY) { u32 timeB = stat.GetTime(); diff --git a/kabufuda b/kabufuda index 223ea9a56..7315a1ded 160000 --- a/kabufuda +++ b/kabufuda @@ -1 +1 @@ -Subproject commit 223ea9a56ebc8ad7e03a2b920dbdc9d621cda551 +Subproject commit 7315a1dedd0ff060b5266e0d7a9d783447ed5cf2