Skip to content

Commit 5fc84ac

Browse files
committed
maple: base vmu file name on game ID for multidisk games
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
1 parent 037dc3b commit 5fc84ac

File tree

4 files changed

+118
-47
lines changed

4 files changed

+118
-47
lines changed

core/hw/maple/maple_devs.cpp

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "hw/pvr/spg.h"
66
#include "audio/audiostream.h"
77
#include "oslib/oslib.h"
8+
#include "oslib/storage.h"
89
#include "hw/aica/sgc_if.h"
910
#include "cfg/option.h"
1011
#include <zlib.h>
@@ -361,45 +362,70 @@ struct maple_sega_vmu: maple_base
361362
break;
362363
}
363364
}
365+
366+
bool fullSave()
367+
{
368+
if (file == nullptr)
369+
return false;
370+
if (std::fseek(file, 0, SEEK_SET) != 0) {
371+
ERROR_LOG(MAPLE, "VMU %s: I/O error", logical_port);
372+
return false;
373+
}
374+
if (std::fwrite(flash_data, sizeof(flash_data), 1, file) != 1) {
375+
ERROR_LOG(MAPLE, "Failed to write the VMU %s to disk", logical_port);
376+
return false;
377+
}
378+
return true;
379+
}
364380

365381
void initializeVmu()
366382
{
367-
INFO_LOG(MAPLE, "Initialising empty VMU...");
383+
INFO_LOG(MAPLE, "Initialising empty VMU %s...", logical_port);
368384

369385
uLongf dec_sz = sizeof(flash_data);
370386
int rv = uncompress(flash_data, &dec_sz, vmu_default, sizeof(vmu_default));
371387

372388
verify(rv == Z_OK);
373389
verify(dec_sz == sizeof(flash_data));
374390

375-
if (file != nullptr)
376-
{
377-
if (std::fwrite(flash_data, sizeof(flash_data), 1, file) != 1)
378-
WARN_LOG(MAPLE, "Failed to write the VMU to disk");
379-
if (std::fseek(file, 0, SEEK_SET) != 0)
380-
WARN_LOG(MAPLE, "VMU: I/O error");
381-
}
391+
fullSave();
382392
}
383393

384394
void OnSetup() override
385395
{
386396
memset(flash_data, 0, sizeof(flash_data));
387397
memset(lcd_data, 0, sizeof(lcd_data));
388-
std::string apath = hostfs::getVmuPath(logical_port);
389-
390-
file = nowide::fopen(apath.c_str(), "rb+");
391-
if (file == nullptr)
392-
{
393-
INFO_LOG(MAPLE, "Unable to open VMU save file \"%s\", creating new file", apath.c_str());
394-
file = nowide::fopen(apath.c_str(), "wb+");
395-
if (file == nullptr)
396-
ERROR_LOG(MAPLE, "Failed to create VMU save file \"%s\"", apath.c_str());
397-
initializeVmu();
398-
}
399-
400-
if (file != nullptr)
401-
if (std::fread(flash_data, sizeof(flash_data), 1, file) != 1)
402-
WARN_LOG(MAPLE, "Failed to read the VMU from disk");
398+
399+
// Load existing vmu file if found
400+
std::string rpath = hostfs::getVmuPath(logical_port, false);
401+
// this might be a storage url
402+
FILE *rfile = hostfs::storage().openFile(rpath, "rb");
403+
if (rfile == nullptr) {
404+
INFO_LOG(MAPLE, "Unable to open VMU file \"%s\", creating new file", rpath.c_str());
405+
}
406+
else
407+
{
408+
if (std::fread(flash_data, sizeof(flash_data), 1, rfile) != 1)
409+
WARN_LOG(MAPLE, "Failed to read the VMU file \"%s\" from disk", rpath.c_str());
410+
std::fclose(rfile);
411+
}
412+
// Open or create the vmu file to save to
413+
std::string wpath = hostfs::getVmuPath(logical_port, true);
414+
file = nowide::fopen(wpath.c_str(), "rb+");
415+
if (file == nullptr)
416+
{
417+
file = nowide::fopen(wpath.c_str(), "wb+");
418+
if (file == nullptr) {
419+
ERROR_LOG(MAPLE, "Failed to create VMU save file \"%s\"", wpath.c_str());
420+
}
421+
else if (rfile != nullptr)
422+
{
423+
// VMU file is being renamed so save it fully now
424+
// and delete the old file
425+
if (fullSave())
426+
nowide::remove(rpath.c_str());
427+
}
428+
}
403429

404430
u8 sum = 0;
405431
for (u32 i = 0; i < sizeof(flash_data); i++)

core/oslib/oslib.cpp

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,49 @@
4444
namespace hostfs
4545
{
4646

47-
std::string getVmuPath(const std::string& port)
47+
std::string getVmuPath(const std::string& port, bool save)
4848
{
49-
if (port == "A1" && config::PerGameVmu && !settings.content.path.empty())
50-
return get_game_save_prefix() + "_vmu_save_A1.bin";
49+
if (port == "A1" && config::PerGameVmu)
50+
{
51+
if (settings.platform.isConsole() && !settings.content.gameId.empty())
52+
{
53+
constexpr std::string_view INVALID_CHARS { " /\\:*?|<>" };
54+
std::string vmuName = settings.content.gameId;
55+
for (char &c: vmuName)
56+
if (INVALID_CHARS.find(c) != INVALID_CHARS.npos)
57+
c = '_';
58+
vmuName += "_vmu_save_A1.bin";
59+
std::string wpath = get_writable_data_path(vmuName);
60+
if (save || file_exists(wpath))
61+
return wpath;
62+
std::string rpath = get_readonly_data_path(vmuName);
63+
if (hostfs::storage().exists(rpath))
64+
return rpath;
65+
if (!settings.content.path.empty())
66+
{
67+
// Legacy path using the rom file name
68+
rpath = get_game_save_prefix() + "_vmu_save_A1.bin";
69+
if (file_exists(rpath))
70+
return rpath;
71+
}
72+
return wpath;
73+
}
74+
if (!settings.content.path.empty())
75+
return get_game_save_prefix() + "_vmu_save_A1.bin";
76+
}
5177

52-
char tempy[512];
53-
sprintf(tempy, "vmu_save_%s.bin", port.c_str());
78+
std::string vmuName = "vmu_save_" + port + ".bin";
79+
std::string wpath = get_writable_data_path(vmuName);
80+
if (save || file_exists(wpath))
81+
return wpath;
82+
std::string rpath = get_readonly_data_path(vmuName);
83+
if (hostfs::storage().exists(rpath))
84+
return rpath;
5485
// VMU saves used to be stored in .reicast, not in .reicast/data
55-
std::string apath = get_writable_config_path(tempy);
56-
if (!file_exists(apath))
57-
apath = get_writable_data_path(tempy);
58-
return apath;
86+
rpath = get_readonly_config_path(vmuName);
87+
if (file_exists(rpath))
88+
return rpath;
89+
return wpath;
5990
}
6091

6192
std::string getArcadeFlashPath()

core/oslib/oslib.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ u32 static inline bitscanrev(u32 v)
4747

4848
namespace hostfs
4949
{
50-
std::string getVmuPath(const std::string& port);
50+
std::string getVmuPath(const std::string& port, bool save);
5151

5252
std::string getArcadeFlashPath();
5353

shell/libretro/oslib.cpp

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,35 @@ extern std::string arcadeFlashPath;
3434
namespace hostfs
3535
{
3636

37-
std::string getVmuPath(const std::string& port)
37+
std::string getVmuPath(const std::string& port, bool save)
3838
{
39-
char filename[PATH_MAX + 8];
40-
41-
if ((per_content_vmus == 1 && port == "A1")
42-
|| per_content_vmus == 2)
43-
{
44-
sprintf(filename, "%s.%s.bin", content_name, port.c_str());
45-
return std::string(vmu_dir_no_slash) + std::string(path_default_slash()) + filename;
46-
}
47-
else
48-
{
49-
sprintf(filename, "vmu_save_%s.bin", port.c_str());
50-
return std::string(game_dir_no_slash) + std::string(path_default_slash()) + filename;
51-
}
39+
if ((per_content_vmus == 1 && port == "A1")
40+
|| per_content_vmus == 2)
41+
{
42+
std::string vmuDir = vmu_dir_no_slash + std::string(path_default_slash());
43+
if (settings.platform.isConsole() && !settings.content.gameId.empty())
44+
{
45+
constexpr std::string_view INVALID_CHARS { " /\\:*?|<>" };
46+
std::string vmuName = settings.content.gameId;
47+
for (char &c: vmuName)
48+
if (INVALID_CHARS.find(c) != INVALID_CHARS.npos)
49+
c = '_';
50+
vmuName += "." + port + ".bin";
51+
std::string wpath = vmuDir + vmuName;
52+
if (save || file_exists(wpath.c_str()))
53+
return wpath;
54+
// Legacy path with rom name
55+
std::string rpath = vmuDir + std::string(content_name) + "." + port + ".bin";
56+
if (file_exists(rpath.c_str()))
57+
return rpath;
58+
else
59+
return wpath;
60+
}
61+
return vmuDir + std::string(content_name) + "." + port + ".bin";
62+
}
63+
else {
64+
return std::string(game_dir_no_slash) + std::string(path_default_slash()) + "vmu_save_" + port + ".bin";
65+
}
5266
}
5367

5468
std::string getArcadeFlashPath()

0 commit comments

Comments
 (0)