Skip to content

Commit

Permalink
maple: base vmu file name on game ID for multidisk games
Browse files Browse the repository at this point in the history
Build vmu file name with game ID so that all disks of multidisk games
share the same vmu (A1 only, or all with libretro when enabled).
Rename existing vmu file to new format if none exists.
Issue #1556
  • Loading branch information
flyinghead committed Nov 3, 2024
1 parent 037dc3b commit 5fc84ac
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 47 deletions.
72 changes: 49 additions & 23 deletions core/hw/maple/maple_devs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <zlib.h>
Expand Down Expand Up @@ -361,45 +362,70 @@ 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));

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++)
Expand Down
49 changes: 40 additions & 9 deletions core/oslib/oslib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion core/oslib/oslib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
42 changes: 28 additions & 14 deletions shell/libretro/oslib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 5fc84ac

Please sign in to comment.