diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index 9b1c5c3e3..79a5d5306 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -5,6 +5,7 @@ #include "hw/pvr/spg.h" #include "audio/audiostream.h" #include "oslib/oslib.h" +#include "oslib/storage.h" #include "hw/aica/sgc_if.h" #include "cfg/option.h" #include @@ -361,10 +362,25 @@ struct maple_sega_vmu: maple_base break; } } + + bool fullSave() + { + if (file == nullptr) + return false; + if (std::fseek(file, 0, SEEK_SET) != 0) { + ERROR_LOG(MAPLE, "VMU %s: I/O error", logical_port); + return false; + } + if (std::fwrite(flash_data, sizeof(flash_data), 1, file) != 1) { + ERROR_LOG(MAPLE, "Failed to write the VMU %s to disk", logical_port); + return false; + } + return true; + } void initializeVmu() { - INFO_LOG(MAPLE, "Initialising empty VMU..."); + INFO_LOG(MAPLE, "Initialising empty VMU %s...", logical_port); uLongf dec_sz = sizeof(flash_data); int rv = uncompress(flash_data, &dec_sz, vmu_default, sizeof(vmu_default)); @@ -372,34 +388,44 @@ struct maple_sega_vmu: maple_base verify(rv == Z_OK); verify(dec_sz == sizeof(flash_data)); - if (file != nullptr) - { - if (std::fwrite(flash_data, sizeof(flash_data), 1, file) != 1) - WARN_LOG(MAPLE, "Failed to write the VMU to disk"); - if (std::fseek(file, 0, SEEK_SET) != 0) - WARN_LOG(MAPLE, "VMU: I/O error"); - } + fullSave(); } void OnSetup() override { memset(flash_data, 0, sizeof(flash_data)); memset(lcd_data, 0, sizeof(lcd_data)); - std::string apath = hostfs::getVmuPath(logical_port); - - file = nowide::fopen(apath.c_str(), "rb+"); - if (file == nullptr) - { - INFO_LOG(MAPLE, "Unable to open VMU save file \"%s\", creating new file", apath.c_str()); - file = nowide::fopen(apath.c_str(), "wb+"); - if (file == nullptr) - ERROR_LOG(MAPLE, "Failed to create VMU save file \"%s\"", apath.c_str()); - initializeVmu(); - } - - if (file != nullptr) - if (std::fread(flash_data, sizeof(flash_data), 1, file) != 1) - WARN_LOG(MAPLE, "Failed to read the VMU from disk"); + + // Load existing vmu file if found + std::string rpath = hostfs::getVmuPath(logical_port, false); + // this might be a storage url + FILE *rfile = hostfs::storage().openFile(rpath, "rb"); + if (rfile == nullptr) { + INFO_LOG(MAPLE, "Unable to open VMU file \"%s\", creating new file", rpath.c_str()); + } + else + { + if (std::fread(flash_data, sizeof(flash_data), 1, rfile) != 1) + WARN_LOG(MAPLE, "Failed to read the VMU file \"%s\" from disk", rpath.c_str()); + std::fclose(rfile); + } + // Open or create the vmu file to save to + std::string wpath = hostfs::getVmuPath(logical_port, true); + file = nowide::fopen(wpath.c_str(), "rb+"); + if (file == nullptr) + { + file = nowide::fopen(wpath.c_str(), "wb+"); + if (file == nullptr) { + ERROR_LOG(MAPLE, "Failed to create VMU save file \"%s\"", wpath.c_str()); + } + else if (rfile != nullptr) + { + // VMU file is being renamed so save it fully now + // and delete the old file + if (fullSave()) + nowide::remove(rpath.c_str()); + } + } u8 sum = 0; for (u32 i = 0; i < sizeof(flash_data); i++) diff --git a/core/oslib/oslib.cpp b/core/oslib/oslib.cpp index 05d33657d..fa7a3793e 100644 --- a/core/oslib/oslib.cpp +++ b/core/oslib/oslib.cpp @@ -44,18 +44,49 @@ namespace hostfs { -std::string getVmuPath(const std::string& port) +std::string getVmuPath(const std::string& port, bool save) { - if (port == "A1" && config::PerGameVmu && !settings.content.path.empty()) - return get_game_save_prefix() + "_vmu_save_A1.bin"; + if (port == "A1" && config::PerGameVmu) + { + if (settings.platform.isConsole() && !settings.content.gameId.empty()) + { + constexpr std::string_view INVALID_CHARS { " /\\:*?|<>" }; + std::string vmuName = settings.content.gameId; + for (char &c: vmuName) + if (INVALID_CHARS.find(c) != INVALID_CHARS.npos) + c = '_'; + vmuName += "_vmu_save_A1.bin"; + std::string wpath = get_writable_data_path(vmuName); + if (save || file_exists(wpath)) + return wpath; + std::string rpath = get_readonly_data_path(vmuName); + if (hostfs::storage().exists(rpath)) + return rpath; + if (!settings.content.path.empty()) + { + // Legacy path using the rom file name + rpath = get_game_save_prefix() + "_vmu_save_A1.bin"; + if (file_exists(rpath)) + return rpath; + } + return wpath; + } + if (!settings.content.path.empty()) + return get_game_save_prefix() + "_vmu_save_A1.bin"; + } - char tempy[512]; - sprintf(tempy, "vmu_save_%s.bin", port.c_str()); + std::string vmuName = "vmu_save_" + port + ".bin"; + std::string wpath = get_writable_data_path(vmuName); + if (save || file_exists(wpath)) + return wpath; + std::string rpath = get_readonly_data_path(vmuName); + if (hostfs::storage().exists(rpath)) + return rpath; // VMU saves used to be stored in .reicast, not in .reicast/data - std::string apath = get_writable_config_path(tempy); - if (!file_exists(apath)) - apath = get_writable_data_path(tempy); - return apath; + rpath = get_readonly_config_path(vmuName); + if (file_exists(rpath)) + return rpath; + return wpath; } std::string getArcadeFlashPath() diff --git a/core/oslib/oslib.h b/core/oslib/oslib.h index 916b33e98..7074c8346 100644 --- a/core/oslib/oslib.h +++ b/core/oslib/oslib.h @@ -47,7 +47,7 @@ u32 static inline bitscanrev(u32 v) namespace hostfs { - std::string getVmuPath(const std::string& port); + std::string getVmuPath(const std::string& port, bool save); std::string getArcadeFlashPath(); diff --git a/shell/libretro/oslib.cpp b/shell/libretro/oslib.cpp index 17c3957c9..736cd8315 100644 --- a/shell/libretro/oslib.cpp +++ b/shell/libretro/oslib.cpp @@ -34,21 +34,35 @@ extern std::string arcadeFlashPath; namespace hostfs { -std::string getVmuPath(const std::string& port) +std::string getVmuPath(const std::string& port, bool save) { - char filename[PATH_MAX + 8]; - - if ((per_content_vmus == 1 && port == "A1") - || per_content_vmus == 2) - { - sprintf(filename, "%s.%s.bin", content_name, port.c_str()); - return std::string(vmu_dir_no_slash) + std::string(path_default_slash()) + filename; - } - else - { - sprintf(filename, "vmu_save_%s.bin", port.c_str()); - return std::string(game_dir_no_slash) + std::string(path_default_slash()) + filename; - } + if ((per_content_vmus == 1 && port == "A1") + || per_content_vmus == 2) + { + std::string vmuDir = vmu_dir_no_slash + std::string(path_default_slash()); + if (settings.platform.isConsole() && !settings.content.gameId.empty()) + { + constexpr std::string_view INVALID_CHARS { " /\\:*?|<>" }; + std::string vmuName = settings.content.gameId; + for (char &c: vmuName) + if (INVALID_CHARS.find(c) != INVALID_CHARS.npos) + c = '_'; + vmuName += "." + port + ".bin"; + std::string wpath = vmuDir + vmuName; + if (save || file_exists(wpath.c_str())) + return wpath; + // Legacy path with rom name + std::string rpath = vmuDir + std::string(content_name) + "." + port + ".bin"; + if (file_exists(rpath.c_str())) + return rpath; + else + return wpath; + } + return vmuDir + std::string(content_name) + "." + port + ".bin"; + } + else { + return std::string(game_dir_no_slash) + std::string(path_default_slash()) + "vmu_save_" + port + ".bin"; + } } std::string getArcadeFlashPath()