GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, u16 sizeMb, bool ascii, DiscIO::IVolume::ECountry card_region, int gameId) : MemoryCardBase(slot, sizeMb) , m_GameId(gameId) , m_LastBlock(-1) , m_hdr(slot, sizeMb, ascii) , m_bat1(sizeMb) , m_saves(0) , m_SaveDirectory(directory) , m_exiting(false) { // Use existing header data if available if (File::Exists(m_SaveDirectory + MC_HDR)) { File::IOFile hdrfile((m_SaveDirectory + MC_HDR), "rb"); hdrfile.ReadBytes(&m_hdr, BLOCK_SIZE); } File::FSTEntry FST_Temp; File::ScanDirectoryTree(m_SaveDirectory, FST_Temp); CFileSearch::XStringVector Directory; Directory.push_back(m_SaveDirectory); CFileSearch::XStringVector Extensions; Extensions.push_back("*.gci"); CFileSearch FileSearch(Extensions, Directory); const CFileSearch::XStringVector& rFilenames = FileSearch.GetFileNames(); if (rFilenames.size() > 112) { Core::DisplayMessage( StringFromFormat("WARNING: There are more than 112 save files on this memorycards"\ "\n Only loading the first 112 in the folder, unless the gameid is the current games id"), 4000); } for (auto gciFile : rFilenames) { if (m_saves.size() == DIRLEN) { PanicAlertT("There are too many gci files in the folder\n%s\nOnly the first 127 will be available", m_SaveDirectory.c_str()); break; } int index = LoadGCI(gciFile, card_region, m_saves.size() > 112 ); if (index != NO_INDEX) { m_loaded_saves.push_back(m_saves.at(index).m_gci_header.GCI_FileName()); } } m_loaded_saves.clear(); m_dir1.fixChecksums(); m_dir2 = m_dir1; m_bat2 = m_bat1; m_flush_thread = std::thread(&GCMemcardDirectory::FlushThread, this); }
GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, u16 sizeMb, bool ascii, DiscIO::Country card_region, int gameId) : MemoryCardBase(slot, sizeMb), m_GameId(gameId), m_LastBlock(-1), m_hdr(slot, sizeMb, ascii), m_bat1(sizeMb), m_saves(0), m_SaveDirectory(directory), m_exiting(false) { // Use existing header data if available if (File::Exists(m_SaveDirectory + MC_HDR)) { File::IOFile hdrfile((m_SaveDirectory + MC_HDR), "rb"); hdrfile.ReadBytes(&m_hdr, BLOCK_SIZE); } std::vector<std::string> rFilenames = DoFileSearch({".gci"}, {m_SaveDirectory}); if (rFilenames.size() > 112) { Core::DisplayMessage("Warning: There are more than 112 save files on this memory card.\n" " Only loading the first 112 in the folder, unless the game ID is the " "same as the current game's ID", 4000); } for (const std::string& gciFile : rFilenames) { if (m_saves.size() == DIRLEN) { PanicAlertT( "There are too many GCI files in the folder\n%s.\nOnly the first 127 will be available", m_SaveDirectory.c_str()); break; } int index = LoadGCI(gciFile, card_region, m_saves.size() > 112); if (index != NO_INDEX) { m_loaded_saves.push_back(m_saves.at(index).m_gci_header.GCI_FileName()); } } m_loaded_saves.clear(); m_dir1.fixChecksums(); m_dir2 = m_dir1; m_bat2 = m_bat1; m_flush_thread = std::thread(&GCMemcardDirectory::FlushThread, this); }
void GCMemcardDirectory::FlushToFile() { std::unique_lock<std::mutex> l(m_write_mutex); int errors = 0; DEntry invalid; for (u16 i = 0; i < m_saves.size(); ++i) { if (m_saves[i].m_dirty) { if (BE32(m_saves[i].m_gci_header.Gamecode) != 0xFFFFFFFF) { m_saves[i].m_dirty = false; if (m_saves[i].m_filename.empty()) { std::string defaultSaveName = m_SaveDirectory + m_saves[i].m_gci_header.GCI_FileName(); // Check to see if another file is using the same name // This seems unlikely except in the case of file corruption // otherwise what user would name another file this way? for (int j = 0; File::Exists(defaultSaveName) && j < 10; ++j) { defaultSaveName.insert(defaultSaveName.end() - 4, '0'); } if (File::Exists(defaultSaveName)) PanicAlertT("Failed to find new filename\n %s\n will be overwritten", defaultSaveName.c_str()); m_saves[i].m_filename = defaultSaveName; } File::IOFile GCI(m_saves[i].m_filename, "wb"); if (GCI) { GCI.WriteBytes(&m_saves[i].m_gci_header, DENTRY_SIZE); GCI.WriteBytes(m_saves[i].m_save_data.data(), BLOCK_SIZE * m_saves[i].m_save_data.size()); if (GCI.IsGood()) { Core::DisplayMessage( StringFromFormat("Wrote save contents to %s", m_saves[i].m_filename.c_str()), 4000); } else { ++errors; Core::DisplayMessage( StringFromFormat("Failed to write save contents to %s", m_saves[i].m_filename.c_str()), 4000); ERROR_LOG(EXPANSIONINTERFACE, "Failed to save data to %s", m_saves[i].m_filename.c_str()); } } } else if (m_saves[i].m_filename.length() != 0) { m_saves[i].m_dirty = false; std::string &oldname = m_saves[i].m_filename; std::string deletedname = oldname + ".deleted"; if (File::Exists(deletedname)) File::Delete(deletedname); File::Rename(oldname, deletedname); m_saves[i].m_filename.clear(); m_saves[i].m_save_data.clear(); m_saves[i].m_used_blocks.clear(); } } // Unload the save data for any game that is not running // we could use !m_dirty, but some games have multiple gci files and may not write to them simultaneously // this ensures that the save data for all of the current games gci files are stored in the savestate u32 gamecode = BE32(m_saves[i].m_gci_header.Gamecode); if (gamecode != m_GameId && gamecode != 0xFFFFFFFF && m_saves[i].m_save_data.size()) { INFO_LOG(EXPANSIONINTERFACE, "Flushing savedata to disk for %s", m_saves[i].m_filename.c_str()); m_saves[i].m_save_data.clear(); } } #if _WRITE_MC_HEADER u8 mc[BLOCK_SIZE * MC_FST_BLOCKS]; Read(0, BLOCK_SIZE * MC_FST_BLOCKS, mc); File::IOFile hdrfile(m_SaveDirectory + MC_HDR, "wb"); hdrfile.WriteBytes(mc, BLOCK_SIZE * MC_FST_BLOCKS); #endif }