diff options
| author | Léo Lam <leo@innovatetechnologi.es> | 2019-05-26 11:07:43 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-05-26 11:07:43 +0200 |
| commit | 526698d2fc293aa915ed318b5cfa9bb55753b8bf (patch) | |
| tree | eacd203dd98144c5176a0288814fde8cf4685023 /Source/Core | |
| parent | 9136abf07eea764b9028188297c55431bc3bdb93 (diff) | |
| parent | 15abb1c92d1972fcecdcec9bb5edb590c2450009 (diff) | |
Merge pull request #8104 from AdmiralCurtiss/memcard-folder-small
GCMemcardDirectory: Improve logic for initial load of GCI files.
Diffstat (limited to 'Source/Core')
| -rw-r--r-- | Source/Core/Core/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | Source/Core/Core/Core.vcxproj | 2 | ||||
| -rw-r--r-- | Source/Core/Core/Core.vcxproj.filters | 6 | ||||
| -rw-r--r-- | Source/Core/Core/HW/GCMemcard/GCIFile.cpp | 102 | ||||
| -rw-r--r-- | Source/Core/Core/HW/GCMemcard/GCIFile.h | 29 | ||||
| -rw-r--r-- | Source/Core/Core/HW/GCMemcard/GCMemcard.h | 24 | ||||
| -rw-r--r-- | Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp | 251 | ||||
| -rw-r--r-- | Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.h | 4 |
8 files changed, 244 insertions, 175 deletions
diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index f72a43861d..498b430d71 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -107,6 +107,7 @@ add_library(core HW/EXI/EXI_DeviceMic.cpp HW/GCKeyboard.cpp HW/GCKeyboardEmu.cpp + HW/GCMemcard/GCIFile.cpp HW/GCMemcard/GCMemcard.cpp HW/GCMemcard/GCMemcardDirectory.cpp HW/GCMemcard/GCMemcardRaw.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 82709080f4..62b00d0f70 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -146,6 +146,7 @@ <ClCompile Include="HW\EXI\EXI_DeviceMic.cpp" /> <ClCompile Include="HW\GCKeyboard.cpp" /> <ClCompile Include="HW\GCKeyboardEmu.cpp" /> + <ClCompile Include="HW\GCMemcard\GCIFile.cpp" /> <ClCompile Include="HW\GCMemcard\GCMemcard.cpp" /> <ClCompile Include="HW\GCMemcard\GCMemcardDirectory.cpp" /> <ClCompile Include="HW\GCMemcard\GCMemcardRaw.cpp" /> @@ -414,6 +415,7 @@ <ClInclude Include="HW\EXI\EXI_DeviceMic.h" /> <ClInclude Include="HW\GCKeyboard.h" /> <ClInclude Include="HW\GCKeyboardEmu.h" /> + <ClInclude Include="HW\GCMemcard\GCIFile.h" /> <ClInclude Include="HW\GCMemcard\GCMemcard.h" /> <ClInclude Include="HW\GCMemcard\GCMemcardDirectory.h" /> <ClInclude Include="HW\GCMemcard\GCMemcardRaw.h" /> diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 62091ccacd..68a8141f87 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -462,6 +462,9 @@ <ClCompile Include="HW\Sram.cpp"> <Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter> </ClCompile> + <ClCompile Include="HW\GCMemcard\GCIFile.cpp"> + <Filter>HW %28Flipper/Hollywood%29\GCMemcard</Filter> + </ClCompile> <ClCompile Include="HW\GCMemcard\GCMemcard.cpp"> <Filter>HW %28Flipper/Hollywood%29\GCMemcard</Filter> </ClCompile> @@ -1173,6 +1176,9 @@ <ClInclude Include="HW\Sram.h"> <Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter> </ClInclude> + <ClInclude Include="HW\GCMemcard\GCIFile.h"> + <Filter>HW %28Flipper/Hollywood%29\GCMemcard</Filter> + </ClInclude> <ClInclude Include="HW\GCMemcard\GCMemcard.h"> <Filter>HW %28Flipper/Hollywood%29\GCMemcard</Filter> </ClInclude> diff --git a/Source/Core/Core/HW/GCMemcard/GCIFile.cpp b/Source/Core/Core/HW/GCMemcard/GCIFile.cpp new file mode 100644 index 0000000000..b173417b8a --- /dev/null +++ b/Source/Core/Core/HW/GCMemcard/GCIFile.cpp @@ -0,0 +1,102 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/HW/GCMemcard/GCIFile.h" + +#include <cinttypes> + +#include "Common/ChunkFile.h" +#include "Common/CommonTypes.h" +#include "Common/File.h" +#include "Common/Logging/Log.h" + +bool GCIFile::LoadHeader() +{ + if (m_filename.empty()) + return false; + + File::IOFile save_file(m_filename, "rb"); + if (!save_file) + return false; + + INFO_LOG(EXPANSIONINTERFACE, "Reading header from disk for %s", m_filename.c_str()); + if (!save_file.ReadBytes(&m_gci_header, sizeof(m_gci_header))) + { + ERROR_LOG(EXPANSIONINTERFACE, "Failed to read header for %s", m_filename.c_str()); + return false; + } + + return true; +} + +bool GCIFile::LoadSaveBlocks() +{ + if (m_save_data.empty()) + { + if (m_filename.empty()) + return false; + + File::IOFile save_file(m_filename, "rb"); + if (!save_file) + return false; + + INFO_LOG(EXPANSIONINTERFACE, "Reading savedata from disk for %s", m_filename.c_str()); + u16 num_blocks = m_gci_header.m_block_count; + + const u32 size = num_blocks * BLOCK_SIZE; + u64 file_size = save_file.GetSize(); + if (file_size != size + DENTRY_SIZE) + { + ERROR_LOG(EXPANSIONINTERFACE, + "%s\nwas not loaded because it is an invalid GCI.\n File size (0x%" PRIx64 + ") does not match the size recorded in the header (0x%x)", + m_filename.c_str(), file_size, size + DENTRY_SIZE); + return false; + } + + m_save_data.resize(num_blocks); + save_file.Seek(DENTRY_SIZE, SEEK_SET); + if (!save_file.ReadBytes(m_save_data.data(), size)) + { + ERROR_LOG(EXPANSIONINTERFACE, "Failed to read data from GCI file %s", m_filename.c_str()); + m_save_data.clear(); + return false; + } + } + return true; +} + +bool GCIFile::HasCopyProtection() const +{ + if ((strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "PSO_SYSTEM") == 0) || + (strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "PSO3_SYSTEM") == 0) || + (strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "f_zero.dat") == 0)) + return true; + return false; +} + +int GCIFile::UsesBlock(u16 block_num) +{ + for (u16 i = 0; i < m_used_blocks.size(); ++i) + { + if (m_used_blocks[i] == block_num) + return i; + } + return -1; +} + +void GCIFile::DoState(PointerWrap& p) +{ + p.DoPOD<DEntry>(m_gci_header); + p.Do(m_dirty); + p.Do(m_filename); + int num_blocks = (int)m_save_data.size(); + p.Do(num_blocks); + m_save_data.resize(num_blocks); + for (auto itr = m_save_data.begin(); itr != m_save_data.end(); ++itr) + { + p.DoPOD<GCMBlock>(*itr); + } + p.Do(m_used_blocks); +} diff --git a/Source/Core/Core/HW/GCMemcard/GCIFile.h b/Source/Core/Core/HW/GCMemcard/GCIFile.h new file mode 100644 index 0000000000..330c519612 --- /dev/null +++ b/Source/Core/Core/HW/GCMemcard/GCIFile.h @@ -0,0 +1,29 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <vector> + +#include "Common/CommonTypes.h" +#include "Core/HW/GCMemcard/GCMemcard.h" + +class PointerWrap; + +class GCIFile +{ +public: + bool LoadHeader(); + bool LoadSaveBlocks(); + bool HasCopyProtection() const; + void DoState(PointerWrap& p); + int UsesBlock(u16 blocknum); + + DEntry m_gci_header; + std::vector<GCMBlock> m_save_data; + std::vector<u16> m_used_blocks; + bool m_dirty; + std::string m_filename; +}; diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcard.h b/Source/Core/Core/HW/GCMemcard/GCMemcard.h index 75017fe28b..8d735ced1f 100644 --- a/Source/Core/Core/HW/GCMemcard/GCMemcard.h +++ b/Source/Core/Core/HW/GCMemcard/GCMemcard.h @@ -337,30 +337,6 @@ struct BlockAlloc static_assert(sizeof(BlockAlloc) == BLOCK_SIZE); #pragma pack(pop) -class GCIFile -{ -public: - bool LoadSaveBlocks(); - bool HasCopyProtection() const - { - if ((strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "PSO_SYSTEM") == - 0) || - (strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "PSO3_SYSTEM") == - 0) || - (strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "f_zero.dat") == 0)) - return true; - return false; - } - - void DoState(PointerWrap& p); - DEntry m_gci_header; - std::vector<GCMBlock> m_save_data; - std::vector<u16> m_used_blocks; - int UsesBlock(u16 blocknum); - bool m_dirty; - std::string m_filename; -}; - class GCMemcard { private: diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp index 0353e9e4b8..54fae94d24 100644 --- a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp +++ b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp @@ -32,96 +32,64 @@ const int NO_INDEX = -1; static const char* MC_HDR = "MC_SYSTEM_AREA"; -int GCMemcardDirectory::LoadGCI(const std::string& file_name, bool current_game_only) +bool GCMemcardDirectory::LoadGCI(GCIFile gci) { - File::IOFile gci_file(file_name, "rb"); - if (gci_file) + // check if any already loaded file has the same internal name as the new file + for (const GCIFile& already_loaded_gci : m_saves) { - GCIFile gci; - gci.m_filename = file_name; - gci.m_dirty = false; - if (!gci_file.ReadBytes(&(gci.m_gci_header), DENTRY_SIZE)) - { - ERROR_LOG(EXPANSIONINTERFACE, "%s failed to read header", file_name.c_str()); - return NO_INDEX; - } - - std::string gci_filename = gci.m_gci_header.GCI_FileName(); - for (u16 i = 0; i < m_loaded_saves.size(); ++i) + if (gci.m_gci_header.GCI_FileName() == already_loaded_gci.m_gci_header.GCI_FileName()) { - if (m_loaded_saves[i] == gci_filename) - { - PanicAlertT("%s\nwas not loaded because it has the same internal filename as previously " - "loaded save\n%s", - gci.m_filename.c_str(), m_saves[i].m_filename.c_str()); - return NO_INDEX; - } + ERROR_LOG(EXPANSIONINTERFACE, + "%s\nwas not loaded because it has the same internal filename as previously " + "loaded save\n%s", + gci.m_filename.c_str(), already_loaded_gci.m_filename.c_str()); + return false; } + } - u16 num_blocks = gci.m_gci_header.m_block_count; - // largest number of free blocks on a memory card - // in reality, there are not likely any valid gci files > 251 blocks - if (num_blocks > 2043) - { - PanicAlertT( - "%s\nwas not loaded because it is an invalid GCI.\n Number of blocks claimed to be %u", - gci.m_filename.c_str(), num_blocks); - return NO_INDEX; - } + // check if this file has a valid block size + // 2043 is the largest number of free blocks on a memory card + // in reality, there are not likely any valid gci files > 251 blocks + const u16 num_blocks = gci.m_gci_header.m_block_count; + if (num_blocks > 2043) + { + ERROR_LOG(EXPANSIONINTERFACE, + "%s\nwas not loaded because it is an invalid GCI.\nNumber of blocks claimed to be %u", + gci.m_filename.c_str(), num_blocks); + return false; + } - u32 size = num_blocks * BLOCK_SIZE; - u64 file_size = gci_file.GetSize(); - if (file_size != size + DENTRY_SIZE) - { - PanicAlertT("%s\nwas not loaded because it is an invalid GCI.\n File size (0x%" PRIx64 - ") does not match the size recorded in the header (0x%x)", - gci.m_filename.c_str(), file_size, size + DENTRY_SIZE); - return NO_INDEX; - } + if (!gci.LoadSaveBlocks()) + { + ERROR_LOG(EXPANSIONINTERFACE, "Failed to load data of %s", gci.m_filename.c_str()); + return false; + } - if (m_game_id == BE32(gci.m_gci_header.m_gamecode.data())) - { - gci.LoadSaveBlocks(); - } - else - { - if (current_game_only) - { - return NO_INDEX; - } - int total_blocks = m_hdr.m_size_mb * MBIT_TO_BLOCKS - MC_FST_BLOCKS; - int free_blocks = m_bat1.m_free_blocks; - if (total_blocks > free_blocks * 10) - { - PanicAlertT("%s\nwas not loaded because there is less than 10%% free blocks available on " - "the memory card\n" - "Total Blocks: %d; Free Blocks: %d", - gci.m_filename.c_str(), total_blocks, free_blocks); - return NO_INDEX; - } - } - u16 first_block = m_bat1.AssignBlocksContiguous(num_blocks); - if (first_block == 0xFFFF) - { - PanicAlertT( - "%s\nwas not loaded because there are not enough free blocks on the virtual memory card", - file_name.c_str()); - return NO_INDEX; - } - gci.m_gci_header.m_first_block = first_block; - if (gci.HasCopyProtection() && gci.LoadSaveBlocks()) - { - GCMemcard::PSO_MakeSaveGameValid(m_hdr, gci.m_gci_header, gci.m_save_data); - GCMemcard::FZEROGX_MakeSaveGameValid(m_hdr, gci.m_gci_header, gci.m_save_data); - } - int idx = (int)m_saves.size(); - m_dir1.Replace(gci.m_gci_header, idx); - m_saves.push_back(std::move(gci)); - SetUsedBlocks(idx); + // reserve storage for the save file in the BAT + u16 first_block = m_bat1.AssignBlocksContiguous(num_blocks); + if (first_block == 0xFFFF) + { + ERROR_LOG( + EXPANSIONINTERFACE, + "%s\nwas not loaded because there are not enough free blocks on the virtual memory card", + gci.m_filename.c_str()); + return false; + } + gci.m_gci_header.m_first_block = first_block; - return idx; + if (gci.HasCopyProtection()) + { + GCMemcard::PSO_MakeSaveGameValid(m_hdr, gci.m_gci_header, gci.m_save_data); + GCMemcard::FZEROGX_MakeSaveGameValid(m_hdr, gci.m_gci_header, gci.m_save_data); } - return NO_INDEX; + + // actually load save file into memory card + int idx = (int)m_saves.size(); + m_dir1.Replace(gci.m_gci_header, idx); + m_saves.push_back(std::move(gci)); + SetUsedBlocks(idx); + + return true; } // This is only used by NetPlay but it made sense to put it here to keep the relevant code together @@ -187,34 +155,69 @@ GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, u File::IOFile((m_save_directory + MC_HDR), "rb").ReadBytes(&m_hdr, BLOCK_SIZE); } + const bool current_game_only = Config::Get(Config::MAIN_GCI_FOLDER_CURRENT_GAME_ONLY); std::vector<std::string> filenames = Common::DoFileSearch({m_save_directory}, {".gci"}); - if (filenames.size() > 112) + // split up into files for current games we should definitely load, + // and files for other games that we don't care too much about + std::vector<GCIFile> gci_current_game; + std::vector<GCIFile> gci_other_games; + for (const std::string& filename : filenames) { - 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); + GCIFile gci; + gci.m_filename = filename; + gci.m_dirty = false; + if (!gci.LoadHeader()) + { + ERROR_LOG(EXPANSIONINTERFACE, "Failed to load header of %s", filename.c_str()); + continue; + } + + if (m_game_id == BE32(gci.m_gci_header.m_gamecode.data())) + gci_current_game.emplace_back(std::move(gci)); + else if (!current_game_only) + gci_other_games.emplace_back(std::move(gci)); } - for (const std::string& gci_file : filenames) + m_saves.reserve(DIRLEN); + + // load files for current game + size_t failed_loads_current_game = 0; + for (GCIFile& gci : gci_current_game) { - 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_save_directory.c_str()); - break; - } - int index = LoadGCI(gci_file, m_saves.size() > 112 || - Config::Get(Config::MAIN_GCI_FOLDER_CURRENT_GAME_ONLY)); - if (index != NO_INDEX) + if (!LoadGCI(std::move(gci))) { - m_loaded_saves.push_back(m_saves.at(index).m_gci_header.GCI_FileName()); + // keep track of how many files failed to load for the current game so we can display a + // message to the user informing them why some of their saves may not be loaded + ++failed_loads_current_game; } } - m_loaded_saves.clear(); + // leave about 10% of free space on the card if possible + const int total_blocks = m_hdr.m_size_mb * MBIT_TO_BLOCKS - MC_FST_BLOCKS; + const int reserved_blocks = total_blocks / 10; + + // load files for other games + for (GCIFile& gci : gci_other_games) + { + // leave some free file entries for new saves that might be created + if (m_saves.size() > 112) + break; + + // leave some free blocks for new saves that might be created + const int free_blocks = m_bat1.m_free_blocks; + const int gci_blocks = gci.m_gci_header.m_block_count; + if (free_blocks - gci_blocks < reserved_blocks) + continue; + + LoadGCI(std::move(gci)); + } + + if (failed_loads_current_game > 0) + { + Core::DisplayMessage("Warning: Save file(s) of the current game failed to load.", 10000); + } + m_dir1.FixChecksums(); m_dir2 = m_dir1; m_bat2 = m_bat1; @@ -693,56 +696,6 @@ void GCMemcardDirectory::DoState(PointerWrap& p) } } -bool GCIFile::LoadSaveBlocks() -{ - if (m_save_data.empty()) - { - if (m_filename.empty()) - return false; - - File::IOFile save_file(m_filename, "rb"); - if (!save_file) - return false; - - INFO_LOG(EXPANSIONINTERFACE, "Reading savedata from disk for %s", m_filename.c_str()); - save_file.Seek(DENTRY_SIZE, SEEK_SET); - u16 num_blocks = m_gci_header.m_block_count; - m_save_data.resize(num_blocks); - if (!save_file.ReadBytes(m_save_data.data(), num_blocks * BLOCK_SIZE)) - { - PanicAlertT("Failed to read data from GCI file %s", m_filename.c_str()); - m_save_data.clear(); - return false; - } - } - return true; -} - -int GCIFile::UsesBlock(u16 block_num) -{ - for (u16 i = 0; i < m_used_blocks.size(); ++i) - { - if (m_used_blocks[i] == block_num) - return i; - } - return -1; -} - -void GCIFile::DoState(PointerWrap& p) -{ - p.DoPOD<DEntry>(m_gci_header); - p.Do(m_dirty); - p.Do(m_filename); - int num_blocks = (int)m_save_data.size(); - p.Do(num_blocks); - m_save_data.resize(num_blocks); - for (auto itr = m_save_data.begin(); itr != m_save_data.end(); ++itr) - { - p.DoPOD<GCMBlock>(*itr); - } - p.Do(m_used_blocks); -} - void MigrateFromMemcardFile(const std::string& directory_name, int card_index) { File::CreateFullPath(directory_name); diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.h b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.h index ce784b8101..473c1b713c 100644 --- a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.h +++ b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.h @@ -10,6 +10,7 @@ #include <vector> #include "Common/Event.h" +#include "Core/HW/GCMemcard/GCIFile.h" #include "Core/HW/GCMemcard/GCMemcard.h" // Uncomment this to write the system data of the memorycard from directory to disc @@ -39,7 +40,7 @@ public: void DoState(PointerWrap& p) override; private: - int LoadGCI(const std::string& file_name, bool current_game_only); + bool LoadGCI(GCIFile gci); inline s32 SaveAreaRW(u32 block, bool writing = false); // s32 DirectoryRead(u32 offset, u32 length, u8* dest_address); s32 DirectoryWrite(u32 dest_address, u32 length, const u8* src_address); @@ -55,7 +56,6 @@ private: BlockAlloc m_bat1, m_bat2; std::vector<GCIFile> m_saves; - std::vector<std::string> m_loaded_saves; std::string m_save_directory; Common::Event m_flush_trigger; std::mutex m_write_mutex; |
