config/data folders improvements

linux: look for legacy ~/.reicast and ~/.reicast/data
else look for ~/.config/flycast and ~/.local/share/flycast
and ~/.config/reicast and ~/.local/share/reicast
(defaults to flycast)

look for bios files in home folder and data folders (android, windows
,macos), then in game folder.
on linux, search in /usr/share/flycast and /usr/local/share/flycast and
legacy locations
This commit is contained in:
Flyinghead 2020-11-26 16:45:57 +01:00
parent d485da19b7
commit 8f77a5482a
25 changed files with 292 additions and 214 deletions

View File

@ -81,7 +81,7 @@ bool cfgOpen()
// Config dir not set (android onboarding)
return false;
const char* filename = "/emu.cfg";
const char* filename = "emu.cfg";
std::string config_path_read = get_readonly_config_path(filename);
cfgPath = get_writable_config_path(filename);

View File

@ -4,7 +4,6 @@
void AICA_Sample();
void AICA_Sample32();
//u32 ReadChannelReg(u32 channel,u32 reg);
void WriteChannelReg(u32 channel, u32 reg, int size);
void sgc_Init();

View File

@ -5,6 +5,7 @@
#pragma once
#include <cmath>
#include "types.h"
#include "stdclass.h"
struct MemChip
{
@ -75,54 +76,41 @@ struct MemChip
}
}
bool Load(const std::string& root, const std::string& prefix, const std::string& names_ro, const std::string& title)
bool Load(const std::string& prefix, const std::string& names_ro, const std::string& title)
{
char base[512];
char temp[512];
char names[512];
// FIXME: Data loss if buffer is too small
strncpy(names,names_ro.c_str(), sizeof(names));
names[sizeof(names) - 1] = '\0';
sprintf(base,"%s",root.c_str());
char* curr=names;
char* next;
do
const size_t npos = std::string::npos;
size_t start = 0;
while (start < names_ro.size())
{
next=strstr(curr,";");
if(next) *next=0;
if (curr[0]=='%')
{
sprintf(temp,"%s%s%s",base,prefix.c_str(),curr+1);
}
else
{
sprintf(temp,"%s%s",base,curr);
}
curr=next+1;
size_t semicolon = names_ro.find(';', start);
std::string name = names_ro.substr(start, semicolon == npos ? semicolon : semicolon - start);
if (Load(temp))
size_t percent = name.find('%');
if (percent != npos)
name = name.replace(percent, 1, prefix);
std::string fullpath = get_readonly_data_path(name);
if (file_exists(fullpath) && Load(fullpath))
{
INFO_LOG(FLASHROM, "Loaded %s as %s", temp, title.c_str());
INFO_LOG(FLASHROM, "Loaded %s as %s", fullpath.c_str(), title.c_str());
return true;
}
} while(next);
start = semicolon;
if (start != npos)
start++;
}
return false;
}
void Save(const std::string& root, const std::string& prefix, const std::string& name_ro, const std::string& title)
{
char path[512];
sprintf(path,"%s%s%s",root.c_str(),prefix.c_str(),name_ro.c_str());
void Save(const std::string& prefix, const std::string& name_ro, const std::string& title)
{
std::string path = get_writable_data_path(prefix + name_ro);
Save(path);
INFO_LOG(FLASHROM, "Saved %s as %s", path, title.c_str());
INFO_LOG(FLASHROM, "Saved %s as %s", path.c_str(), title.c_str());
}
virtual void Reset() {}
virtual bool Serialize(void **data, unsigned int *total_size) { return true; }
virtual bool Unserialize(void **data, unsigned int *total_size) { return true; }

View File

@ -164,11 +164,11 @@ void FixUpFlash()
}
}
static bool nvmem_load(const std::string& root)
static bool nvmem_load()
{
bool rc;
if (settings.platform.system == DC_PLATFORM_DREAMCAST)
rc = sys_nvmem->Load(root, getRomPrefix(), "%nvmem.bin", "nvram");
rc = sys_nvmem->Load(getRomPrefix(), "%nvmem.bin", "nvram");
else
rc = sys_nvmem->Load(get_game_save_prefix() + ".nvmem");
if (!rc)
@ -180,12 +180,12 @@ static bool nvmem_load(const std::string& root)
return true;
}
bool LoadRomFiles(const std::string& root)
bool LoadRomFiles()
{
nvmem_load(root);
nvmem_load();
if (settings.platform.system != DC_PLATFORM_ATOMISWAVE)
{
if (sys_rom->Load(root, getRomPrefix(), "%boot.bin;%boot.bin.bin;%bios.bin;%bios.bin.bin", "bootrom"))
if (sys_rom->Load(getRomPrefix(), "%boot.bin;%boot.bin.bin;%bios.bin;%bios.bin.bin", "bootrom"))
bios_loaded = true;
else if (settings.platform.system == DC_PLATFORM_DREAMCAST)
return false;
@ -194,19 +194,19 @@ bool LoadRomFiles(const std::string& root)
return true;
}
void SaveRomFiles(const std::string& root)
void SaveRomFiles()
{
if (settings.platform.system == DC_PLATFORM_DREAMCAST)
sys_nvmem->Save(root, getRomPrefix(), "nvmem.bin", "nvmem");
sys_nvmem->Save(getRomPrefix(), "nvmem.bin", "nvmem");
else
sys_nvmem->Save(get_game_save_prefix() + ".nvmem");
if (settings.platform.system == DC_PLATFORM_ATOMISWAVE)
sys_rom->Save(get_game_save_prefix() + ".nvmem2");
}
bool LoadHle(const std::string& root)
bool LoadHle()
{
if (!nvmem_load(root))
if (!nvmem_load())
WARN_LOG(FLASHROM, "No nvmem loaded\n");
reios_reset(sys_rom->data);

View File

@ -7,4 +7,9 @@ void map_area0(u32 base);
//Init/Res/Term
void sh4_area0_Init();
void sh4_area0_Reset(bool Manual);
void sh4_area0_Term();
void sh4_area0_Term();
bool LoadRomFiles();
void SaveRomFiles();
bool LoadHle();
void FixUpFlash();

View File

@ -482,8 +482,11 @@ struct maple_sega_vmu: maple_base
memset(flash_data, 0, sizeof(flash_data));
memset(lcd_data, 0, sizeof(lcd_data));
char tempy[512];
sprintf(tempy, "/vmu_save_%s.bin", logical_port);
std::string apath = get_writable_data_path(tempy);
sprintf(tempy, "vmu_save_%s.bin", logical_port);
// 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);
file = fopen(apath.c_str(), "rb+");
if (!file)
@ -492,25 +495,19 @@ struct maple_sega_vmu: maple_base
file = fopen(apath.c_str(), "wb");
if (file) {
if (!init_emptyvmu())
INFO_LOG(MAPLE, "Failed to initialize an empty VMU, you should reformat it using the BIOS");
WARN_LOG(MAPLE, "Failed to initialize an empty VMU, you should reformat it using the BIOS");
fwrite(flash_data, sizeof(flash_data), 1, file);
fseek(file, 0, SEEK_SET);
}
else
{
INFO_LOG(MAPLE, "Unable to create VMU!");
ERROR_LOG(MAPLE, "Failed to create VMU save file \"%s\"", apath.c_str());
}
}
if (!file)
{
INFO_LOG(MAPLE, "Failed to create VMU save file \"%s\"", apath.c_str());
}
else
{
if (file != nullptr)
fread(flash_data, 1, sizeof(flash_data), file);
}
u8 sum = 0;
for (u32 i = 0; i < sizeof(flash_data); i++)
@ -521,20 +518,15 @@ struct maple_sega_vmu: maple_base
if (init_emptyvmu())
{
if (!file)
file = fopen(apath.c_str(), "wb");
if (file) {
if (file != nullptr)
{
fwrite(flash_data, sizeof(flash_data), 1, file);
fseek(file, 0, SEEK_SET);
}
else {
INFO_LOG(MAPLE, "Unable to create VMU!");
}
}
else
{
INFO_LOG(MAPLE, "Failed to initialize an empty VMU, you should reformat it using the BIOS");
WARN_LOG(MAPLE, "Failed to initialize an empty VMU, you should reformat it using the BIOS");
}
}
@ -3001,7 +2993,7 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou
jvs_length = length - 2;
u8 calc_crc = 0;
for (int i = 1; i < length; i++)
for (u32 i = 1; i < length; i++)
calc_crc = ((calc_crc + buffer_out[i]) & 0xFF);
JVS_OUT(calc_crc);

View File

@ -76,8 +76,12 @@ static bool naomi_LoadBios(const char *filename, Archive *child_archive, Archive
struct BIOS_t *bios = &BIOS[biosid];
std::string basepath = get_readonly_data_path(DATA_PATH);
std::unique_ptr<Archive> bios_archive(OpenArchive((basepath + filename).c_str()));
std::string arch_name(filename);
std::string path = get_readonly_data_path(arch_name + ".zip");
if (!file_exists(path.c_str()))
path = get_readonly_data_path(arch_name + ".7z");
DEBUG_LOG(NAOMI, "Loading BIOS from %s", path.c_str());
std::unique_ptr<Archive> bios_archive(OpenArchive(path.c_str()));
bool found_region = false;
@ -232,7 +236,7 @@ static void naomi_cart_LoadZip(const char *filename)
{
// If a specific BIOS is needed for this game, fail.
if (game->bios != NULL || !bios_loaded)
throw NaomiCartException(std::string("Error: cannot load BIOS ") + (game->bios != NULL ? game->bios : "naomi.zip") + " in " + get_readonly_data_path(DATA_PATH));
throw NaomiCartException(std::string("Error: cannot load BIOS ") + (game->bios != NULL ? game->bios : "naomi.zip"));
// otherwise use the default BIOS
}

View File

@ -652,7 +652,7 @@ void print_blocks()
if (print_stats)
{
f=fopen(get_writable_data_path("/blkmap.lst").c_str(),"w");
f=fopen(get_writable_data_path("blkmap.lst").c_str(),"w");
print_stats=0;
INFO_LOG(DYNAREC, "Writing blocks to %p", f);
@ -680,9 +680,7 @@ void print_blocks()
fprintf(f,"host_opcodes: %d\n",blk->host_opcodes);
fprintf(f,"il_opcodes: %zd\n",blk->oplist.size());
u32 hcode=0;
s32 gcode=-1;
u8* pucode=(u8*)blk->code;
size_t j=0;

View File

@ -223,12 +223,6 @@ void mem_Term()
sh4_mmr_term();
sh4_area0_Term();
// done by emulator thread
//SaveRomFiles(get_writable_data_path(DATA_PATH));
//mem_b.Term(); // handled by vmem
//vmem
_vmem_term();
}

View File

@ -85,8 +85,4 @@ u8* GetMemPtr(u32 Addr,u32 size);
bool IsOnRam(u32 addr);
bool LoadRomFiles(const std::string& root);
void SaveRomFiles(const std::string& root);
bool LoadHle(const std::string& root);
void FixUpFlash();
void SetMemoryHandlers();

View File

@ -205,7 +205,7 @@ std::shared_ptr<InputMapping> InputMapping::LoadMapping(const char *name)
if (it != loaded_mappings.end())
return it->second;
std::string path = get_writable_config_path((std::string("/mappings/") + name).c_str());
std::string path = get_readonly_config_path((std::string("mappings/") + name).c_str());
FILE *fp = fopen(path.c_str(), "r");
if (fp == NULL)
return NULL;
@ -222,9 +222,9 @@ bool InputMapping::save(const char *name)
if (!dirty)
return true;
std::string path = get_writable_config_path("/mappings/");
std::string path = get_writable_config_path("mappings/");
make_directory(path);
path = get_writable_config_path((std::string("/mappings/") + name).c_str());
path = get_writable_config_path((std::string("mappings/") + name).c_str());
FILE *fp = fopen(path.c_str(), "w");
if (fp == NULL)
{

View File

@ -151,89 +151,139 @@ void* rend_thread(void* p);
}
#endif
// Find the user config directory.
// The following folders are checked in this order:
// $HOME/.reicast
// $HOME/.config/flycast
// $HOME/.config/reicast
// If no folder exists, $HOME/.config/flycast is created and used.
std::string find_user_config_dir()
{
struct stat info;
std::string home = "";
if(getenv("HOME") != NULL)
std::string xdg_home;
if (getenv("HOME") != NULL)
{
// Support for the legacy config dir at "$HOME/.reicast"
std::string legacy_home = (std::string)getenv("HOME") + "/.reicast";
if((stat(legacy_home.c_str(), &info) == 0) && (info.st_mode & S_IFDIR))
{
std::string legacy_home = (std::string)getenv("HOME") + "/.reicast/";
if (stat(legacy_home.c_str(), &info) == 0 && (info.st_mode & S_IFDIR))
// "$HOME/.reicast" already exists, let's use it!
return legacy_home;
}
/* If $XDG_CONFIG_HOME is not set, we're supposed to use "$HOME/.config" instead.
* Consult the XDG Base Directory Specification for details:
* http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
*/
home = (std::string)getenv("HOME") + "/.config/reicast";
xdg_home = (std::string)getenv("HOME") + "/.config";
}
if(getenv("XDG_CONFIG_HOME") != NULL)
{
if (getenv("XDG_CONFIG_HOME") != NULL)
// If XDG_CONFIG_HOME is set explicitly, we'll use that instead of $HOME/.config
home = (std::string)getenv("XDG_CONFIG_HOME") + "/reicast";
}
xdg_home = (std::string)getenv("XDG_CONFIG_HOME");
if(!home.empty())
if (!xdg_home.empty())
{
if((stat(home.c_str(), &info) != 0) || !(info.st_mode & S_IFDIR))
{
// If the directory doesn't exist yet, create it!
mkdir(home.c_str(), 0755);
}
return home;
std::string fullpath = xdg_home + "/flycast/";
if (stat(fullpath.c_str(), &info) == 0 && (info.st_mode & S_IFDIR))
// Found .config/flycast
return fullpath;
fullpath = xdg_home + "/reicast/";
if (stat(fullpath.c_str(), &info) == 0 && (info.st_mode & S_IFDIR))
// Found .config/reicast
return fullpath;
// Create .config/flycast
fullpath = xdg_home + "/flycast/";
mkdir(fullpath.c_str(), 0755);
return fullpath;
}
// Unable to detect config dir, use the current folder
return ".";
}
// Find the user data directory.
// The following folders are checked in this order:
// $HOME/.reicast/data
// $HOME/.local/share/flycast
// $HOME/.local/share/reicast
// If no folder exists, $HOME/.local/share/flycast is created and used.
std::string find_user_data_dir()
{
struct stat info;
std::string data = "";
if(getenv("HOME") != NULL)
std::string xdg_home;
if (getenv("HOME") != NULL)
{
// Support for the legacy config dir at "$HOME/.reicast"
std::string legacy_data = (std::string)getenv("HOME") + "/.reicast";
if((stat(legacy_data.c_str(), &info) == 0) && (info.st_mode & S_IFDIR))
{
// "$HOME/.reicast" already exists, let's use it!
// Support for the legacy config dir at "$HOME/.reicast/data"
std::string legacy_data = (std::string)getenv("HOME") + "/.reicast/data/";
if (stat(legacy_data.c_str(), &info) == 0 && (info.st_mode & S_IFDIR))
// "$HOME/.reicast/data" already exists, let's use it!
return legacy_data;
}
/* If $XDG_DATA_HOME is not set, we're supposed to use "$HOME/.local/share" instead.
* Consult the XDG Base Directory Specification for details:
* http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
*/
data = (std::string)getenv("HOME") + "/.local/share/reicast";
xdg_home = (std::string)getenv("HOME") + "/.local/share";
}
if(getenv("XDG_DATA_HOME") != NULL)
if (getenv("XDG_DATA_HOME") != NULL)
// If XDG_DATA_HOME is set explicitly, we'll use that instead of $HOME/.local/share
xdg_home = (std::string)getenv("XDG_DATA_HOME");
if (!xdg_home.empty())
{
// If XDG_DATA_HOME is set explicitly, we'll use that instead of $HOME/.config
data = (std::string)getenv("XDG_DATA_HOME") + "/reicast";
std::string fullpath = xdg_home + "/flycast/";
if (stat(fullpath.c_str(), &info) == 0 && (info.st_mode & S_IFDIR))
// Found .local/share/flycast
return fullpath;
fullpath = xdg_home + "/reicast/";
if (stat(fullpath.c_str(), &info) == 0 && (info.st_mode & S_IFDIR))
// Found .local/share/reicast
return fullpath;
// Create .local/share/flycast
fullpath = xdg_home + "/flycast/";
mkdir(fullpath.c_str(), 0755);
return fullpath;
}
if(!data.empty())
{
if((stat(data.c_str(), &info) != 0) || !(info.st_mode & S_IFDIR))
{
// If the directory doesn't exist yet, create it!
mkdir(data.c_str(), 0755);
}
return data;
}
// Unable to detect config dir, use the current folder
// Unable to detect data dir, use the current folder
return ".";
}
// Find a file in the user and system config directories.
// The following folders are checked in this order:
// $HOME/.reicast
// $HOME/.config/flycast
// $HOME/.config/reicast
// if XDG_CONFIG_DIRS is defined:
// <$XDG_CONFIG_DIRS>/flycast
// <$XDG_CONFIG_DIRS>/reicast
// else
// /etc/flycast/
// /etc/xdg/flycast/
// .
std::vector<std::string> find_system_config_dirs()
{
std::vector<std::string> dirs;
std::string xdg_home;
if (getenv("HOME") != NULL)
{
// Support for the legacy config dir at "$HOME/.reicast"
dirs.push_back((std::string)getenv("HOME") + "/.reicast/");
xdg_home = (std::string)getenv("HOME") + "/.config";
}
if (getenv("XDG_CONFIG_HOME") != NULL)
// If XDG_CONFIG_HOME is set explicitly, we'll use that instead of $HOME/.config
xdg_home = (std::string)getenv("XDG_CONFIG_HOME");
if (!xdg_home.empty())
{
// XDG config locations
dirs.push_back(xdg_home + "/flycast/");
dirs.push_back(xdg_home + "/reicast/");
}
if (getenv("XDG_CONFIG_DIRS") != NULL)
{
std::string s = (std::string)getenv("XDG_CONFIG_DIRS");
@ -242,24 +292,61 @@ std::vector<std::string> find_system_config_dirs()
std::string::size_type n = s.find(':', pos);
while(n != std::string::npos)
{
dirs.push_back(s.substr(pos, n-pos) + "/reicast");
dirs.push_back(s.substr(pos, n-pos) + "/flycast/");
dirs.push_back(s.substr(pos, n-pos) + "/reicast/");
pos = n + 1;
n = s.find(':', pos);
}
// Separator not found
dirs.push_back(s.substr(pos) + "/reicast");
dirs.push_back(s.substr(pos) + "/flycast/");
dirs.push_back(s.substr(pos) + "/reicast/");
}
else
{
dirs.push_back("/etc/reicast"); // This isn't part of the XDG spec, but much more common than /etc/xdg/
dirs.push_back("/etc/xdg/reicast");
dirs.push_back("/etc/flycast/"); // This isn't part of the XDG spec, but much more common than /etc/xdg/
dirs.push_back("/etc/xdg/flycast/");
}
dirs.push_back("./");
return dirs;
}
// Find a file in the user data directories.
// The following folders are checked in this order:
// $HOME/.reicast/data
// $HOME/.local/share/flycast
// $HOME/.local/share/reicast
// if XDG_DATA_DIRS is defined:
// <$XDG_DATA_DIRS>/flycast
// <$XDG_DATA_DIRS>/reicast
// else
// /usr/local/share/flycast
// /usr/share/flycast
// /usr/local/share/reicast
// /usr/share/reicast
// ./data
std::vector<std::string> find_system_data_dirs()
{
std::vector<std::string> dirs;
std::string xdg_home;
if (getenv("HOME") != NULL)
{
// Support for the legacy data dir at "$HOME/.reicast/data"
dirs.push_back((std::string)getenv("HOME") + "/.reicast/data/");
xdg_home = (std::string)getenv("HOME") + "/.local/share";
}
if (getenv("XDG_DATA_HOME") != NULL)
// If XDG_DATA_HOME is set explicitly, we'll use that instead of $HOME/.local/share
xdg_home = (std::string)getenv("XDG_DATA_HOME");
if (!xdg_home.empty())
{
// XDG data locations
dirs.push_back(xdg_home + "/flycast/");
dirs.push_back(xdg_home + "/reicast/");
dirs.push_back(xdg_home + "/reicast/data/");
}
if (getenv("XDG_DATA_DIRS") != NULL)
{
std::string s = (std::string)getenv("XDG_DATA_DIRS");
@ -268,18 +355,25 @@ std::vector<std::string> find_system_data_dirs()
std::string::size_type n = s.find(':', pos);
while(n != std::string::npos)
{
dirs.push_back(s.substr(pos, n-pos) + "/reicast");
dirs.push_back(s.substr(pos, n-pos) + "/flycast/");
dirs.push_back(s.substr(pos, n-pos) + "/reicast/");
pos = n + 1;
n = s.find(':', pos);
}
// Separator not found
dirs.push_back(s.substr(pos) + "/reicast");
dirs.push_back(s.substr(pos) + "/flycast/");
dirs.push_back(s.substr(pos) + "/reicast/");
}
else
{
dirs.push_back("/usr/local/share/reicast");
dirs.push_back("/usr/share/reicast");
dirs.push_back("/usr/local/share/flycast/");
dirs.push_back("/usr/share/flycast/");
dirs.push_back("/usr/local/share/reicast/");
dirs.push_back("/usr/share/reicast/");
}
dirs.push_back("./");
dirs.push_back("data/");
return dirs;
}
@ -291,22 +385,15 @@ int main(int argc, char* argv[])
signal(SIGKILL, clean_exit);
#endif
/* Set directories */
// Set directories
set_user_config_dir(find_user_config_dir());
set_user_data_dir(find_user_data_dir());
std::vector<std::string> dirs;
dirs = find_system_config_dirs();
for (std::size_t i = 0; i < dirs.size(); i++)
{
add_system_data_dir(dirs[i]);
}
dirs = find_system_data_dirs();
for (std::size_t i = 0; i < dirs.size(); i++)
{
add_system_data_dir(dirs[i]);
}
INFO_LOG(BOOT, "Config dir is: %s", get_writable_config_path("/").c_str());
INFO_LOG(BOOT, "Data dir is: %s", get_writable_data_path("/").c_str());
for (const auto& dir : find_system_config_dirs())
add_system_config_dir(dir);
for (const auto& dir : find_system_data_dirs())
add_system_data_dir(dir);
INFO_LOG(BOOT, "Config dir is: %s", get_writable_config_path("").c_str());
INFO_LOG(BOOT, "Data dir is: %s", get_writable_data_path("").c_str());
#if defined(USE_SDL)
// init video now: on rpi3 it installs a sigsegv handler(?)

View File

@ -121,7 +121,7 @@ static int allocate_shared_filemem(unsigned size) {
// if shmem does not work (or using OSX) fallback to a regular file on disk
if (fd < 0) {
std::string path = get_writable_data_path("/dcnzorz_mem");
std::string path = get_writable_data_path("dcnzorz_mem");
fd = open(path.c_str(), O_CREAT|O_RDWR|O_TRUNC, S_IRWXU|S_IRWXG|S_IRWXO);
unlink(path.c_str());
}

View File

@ -131,7 +131,7 @@ LogManager::LogManager()
if (cfgLoadBool("log", "LogToFile", false))
{
#ifdef __ANDROID__
std::string logPath = get_writable_data_path("/flycast.log");
std::string logPath = get_writable_data_path("flycast.log");
#else
std::string logPath = "flycast.log";
#endif

View File

@ -15,6 +15,7 @@
#include "hw/maple/maple_cfg.h"
#include "hw/sh4/sh4_mem.h"
#include "hw/holly/sb_mem.h"
#include "hw/naomi/naomi_cart.h"
#include "reios/reios.h"
@ -551,15 +552,14 @@ static void dc_start_game(const char *path)
dc_reset(true);
LoadSettings(false);
std::string data_path = get_readonly_data_path(DATA_PATH);
if (settings.platform.system == DC_PLATFORM_DREAMCAST)
{
if ((settings.bios.UseReios && !forced_bios_file) || !LoadRomFiles(data_path))
if ((settings.bios.UseReios && !forced_bios_file) || !LoadRomFiles())
{
if (forced_bios_file)
throw ReicastException("No BIOS file found");
if (!LoadHle(data_path))
if (!LoadHle())
throw ReicastException("Failed to initialize HLE BIOS");
NOTICE_LOG(BOOT, "Did not load BIOS, using reios");
@ -567,7 +567,7 @@ static void dc_start_game(const char *path)
}
else
{
LoadRomFiles(data_path);
LoadRomFiles();
}
if (settings.platform.system == DC_PLATFORM_DREAMCAST)
{
@ -591,7 +591,7 @@ static void dc_start_game(const char *path)
// Content load failed. Boot the BIOS
settings.imgread.ImagePath[0] = '\0';
forced_bios_file = true;
if (!LoadRomFiles(data_path))
if (!LoadRomFiles())
throw ReicastException("No BIOS file found");
InitDrive();
}
@ -656,7 +656,7 @@ void* dc_run(void*)
sh4_cpu.Run();
SaveRomFiles(get_writable_data_path(DATA_PATH));
SaveRomFiles();
if (reset_requested)
{
dc_reset(false);
@ -933,7 +933,7 @@ static void LoadCustom()
if (*p == '\0')
return;
}
else if (settings.platform.system == DC_PLATFORM_NAOMI || settings.platform.system == DC_PLATFORM_ATOMISWAVE)
else
{
reios_id = naomi_game_id;
}
@ -951,9 +951,9 @@ void SaveSettings()
{
cfgSetAutoSave(false);
cfgSaveBool("config", "Dynarec.Enabled", settings.dynarec.Enable);
if (forced_game_cable == -1 || forced_game_cable != settings.dreamcast.cable)
if (forced_game_cable == -1 || forced_game_cable != (int)settings.dreamcast.cable)
cfgSaveInt("config", "Dreamcast.Cable", settings.dreamcast.cable);
if (forced_game_region == -1 || forced_game_region != settings.dreamcast.region)
if (forced_game_region == -1 || forced_game_region != (int)settings.dreamcast.region)
cfgSaveInt("config", "Dreamcast.Region", settings.dreamcast.region);
cfgSaveInt("config", "Dreamcast.Broadcast", settings.dreamcast.broadcast);
cfgSaveBool("config", "Dreamcast.ForceWindowsCE", settings.dreamcast.ForceWindowsCE);
@ -1064,7 +1064,7 @@ static void cleanup_serialize(void *data)
free(data) ;
}
static std::string get_savestate_file_path()
static std::string get_savestate_file_path(bool writable)
{
std::string state_file = settings.imgread.ImagePath;
size_t lastindex = state_file.find_last_of('/');
@ -1081,7 +1081,10 @@ static std::string get_savestate_file_path()
if (lastindex != std::string::npos)
state_file = state_file.substr(0, lastindex);
state_file = state_file + ".state";
return get_writable_data_path(DATA_PATH) + state_file;
if (writable)
return get_writable_data_path(state_file);
else
return get_readonly_data_path(state_file);
}
void dc_savestate()
@ -1121,7 +1124,7 @@ void dc_savestate()
return;
}
filename = get_savestate_file_path();
filename = get_savestate_file_path(true);
f = fopen(filename.c_str(), "wb") ;
if ( f == NULL )
@ -1150,7 +1153,7 @@ void dc_loadstate()
dc_stop();
filename = get_savestate_file_path();
filename = get_savestate_file_path(false);
f = fopen(filename.c_str(), "rb") ;
if ( f == NULL )

View File

@ -739,7 +739,7 @@ void reios_reset(u8* rom)
// 7078 24 × 24 pixels (72 bytes) characters
// 129 32 × 32 pixels (128 bytes) characters
memset(pFont, 0, 536496);
FILE *font = fopen(get_readonly_data_path(DATA_PATH "font.bin").c_str(), "rb");
FILE *font = fopen(get_readonly_data_path("font.bin").c_str(), "rb");
if (font == NULL)
{
INFO_LOG(REIOS, "font.bin not found. Using built-in font");

View File

@ -102,7 +102,7 @@ bool CustomTexture::Init()
std::string game_id = GetGameId();
if (game_id.length() > 0)
{
textures_path = get_readonly_data_path(DATA_PATH) + "textures/" + game_id + "/";
textures_path = get_readonly_data_path("textures/" + game_id) + "/";
DIR *dir = opendir(textures_path.c_str());
if (dir != NULL)
@ -156,7 +156,7 @@ void CustomTexture::LoadCustomTextureAsync(BaseTextureCacheData *texture_data)
void CustomTexture::DumpTexture(u32 hash, int w, int h, TextureType textype, void *src_buffer)
{
std::string base_dump_dir = get_writable_data_path(DATA_PATH "texdump/");
std::string base_dump_dir = get_writable_data_path("texdump/");
if (!file_exists(base_dump_dir))
make_directory(base_dump_dir);
std::string game_id = GetGameId();

View File

@ -1674,8 +1674,10 @@ static void systemdir_selected_callback(bool cancelled, std::string selection)
{
if (!cancelled)
{
selection += "/";
set_user_config_dir(selection);
set_user_data_dir(selection);
add_system_data_dir(selection);
set_user_data_dir(selection + "data/");
if (cfgOpen())
{
LoadSettings(false);

View File

@ -150,7 +150,7 @@ u8 *loadOSDButtons(int &width, int &height)
{
int n;
stbi_set_flip_vertically_on_load(1);
u8 *image_data = stbi_load(get_readonly_data_path(DATA_PATH "buttons.png").c_str(), &width, &height, &n, STBI_rgb_alpha);
u8 *image_data = stbi_load(get_readonly_data_path("buttons.png").c_str(), &width, &height, &n, STBI_rgb_alpha);
if (image_data == nullptr)
{
if (DefaultOSDButtons.empty())

View File

@ -33,7 +33,7 @@
VulkanContext *VulkanContext::contextInstance;
static const char *PipelineCacheFileName = DATA_PATH "vulkan_pipeline.cache";
static const char *PipelineCacheFileName = "vulkan_pipeline.cache";
#ifndef __ANDROID__
VKAPI_ATTR static VkBool32 VKAPI_CALL debugUtilsMessengerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes,
@ -450,7 +450,7 @@ bool VulkanContext::InitDevice()
10000, ARRAY_SIZE(pool_sizes), pool_sizes));
std::string cachePath = get_writable_data_path(PipelineCacheFileName);
std::string cachePath = get_readonly_data_path(PipelineCacheFileName);
FILE *f = fopen(cachePath.c_str(), "rb");
if (f == nullptr)
pipelineCache = device->createPipelineCacheUnique(vk::PipelineCacheCreateInfo());

View File

@ -20,10 +20,10 @@
#include <unistd.h>
#endif
std::string user_config_dir;
std::string user_data_dir;
std::vector<std::string> system_config_dirs;
std::vector<std::string> system_data_dirs;
static std::string user_config_dir;
static std::string user_data_dir;
static std::vector<std::string> system_config_dirs;
static std::vector<std::string> system_data_dirs;
bool file_exists(const std::string& filename)
{
@ -55,24 +55,20 @@ std::string get_writable_config_path(const std::string& filename)
/* Only stuff in the user_config_dir is supposed to be writable,
* so we always return that.
*/
return (user_config_dir + filename);
return user_config_dir + filename;
}
std::string get_readonly_config_path(const std::string& filename)
{
std::string user_filepath = get_writable_config_path(filename);
if(file_exists(user_filepath))
{
if (file_exists(user_filepath))
return user_filepath;
}
std::string filepath;
for (size_t i = 0; i < system_config_dirs.size(); i++) {
filepath = system_config_dirs[i] + filename;
for (const auto& config_dir : system_config_dirs)
{
std::string filepath = config_dir + filename;
if (file_exists(filepath))
{
return filepath;
}
}
// Not found, so we return the user variant
@ -84,31 +80,31 @@ std::string get_writable_data_path(const std::string& filename)
/* Only stuff in the user_data_dir is supposed to be writable,
* so we always return that.
*/
return (user_data_dir + filename);
return user_data_dir + filename;
}
std::string get_readonly_data_path(const std::string& filename)
{
std::string user_filepath = get_writable_data_path(filename);
if(file_exists(user_filepath))
{
if (file_exists(user_filepath))
return user_filepath;
}
std::string filepath;
for (size_t i = 0; i < system_data_dirs.size(); i++) {
filepath = system_data_dirs[i] + filename;
for (const auto& data_dir : system_data_dirs)
{
std::string filepath = data_dir + filename;
if (file_exists(filepath))
{
return filepath;
}
}
// Try the game directory
std::string filepath = get_game_dir() + filename;
if (file_exists(filepath))
return filepath;
// Not found, so we return the user variant
return user_filepath;
}
static size_t get_last_slash_pos(const std::string& path)
size_t get_last_slash_pos(const std::string& path)
{
size_t lastindex = path.find_last_of('/');
#ifdef _WIN32
@ -127,7 +123,7 @@ std::string get_game_save_prefix()
size_t lastindex = get_last_slash_pos(save_file);
if (lastindex != std::string::npos)
save_file = save_file.substr(lastindex + 1);
return get_writable_data_path(DATA_PATH) + save_file;
return get_writable_data_path(save_file);
}
std::string get_game_basename()

View File

@ -49,12 +49,6 @@ public :
void Wait(); //Wait for signal , then reset[if auto]
};
#if !defined(TARGET_IPHONE)
#define DATA_PATH "/data/"
#else
#define DATA_PATH "/"
#endif
//Set the path !
void set_user_config_dir(const std::string& dir);
void set_user_data_dir(const std::string& dir);
@ -72,6 +66,7 @@ bool make_directory(const std::string& path);
std::string get_game_save_prefix();
std::string get_game_basename();
std::string get_game_dir();
size_t get_last_slash_pos(const std::string& path);
bool mem_region_lock(void *start, std::size_t len);
bool mem_region_unlock(void *start, std::size_t len);

View File

@ -192,11 +192,16 @@ LONG ExeptionHandler(EXCEPTION_POINTERS *ExceptionInfo)
void SetupPath()
{
char fname[512];
GetModuleFileName(0,fname,512);
GetModuleFileName(0, fname, sizeof(fname));
std::string fn = std::string(fname);
fn=fn.substr(0,fn.find_last_of('\\'));
size_t pos = get_last_slash_pos(fn);
if (pos != std::string::npos)
fn = fn.substr(0, pos) + "\\";
else
fn = ".\\";
set_user_config_dir(fn);
set_user_data_dir(fn);
add_system_data_dir(fn);
set_user_data_dir(fn + "data\\");
}
static Win32KeyboardDevice keyboard(0);

View File

@ -195,13 +195,22 @@ JNIEXPORT jstring JNICALL Java_com_reicast_emulator_emu_JNIdc_initEnvironment(JN
saveAndroidSettingsMid = env->GetMethodID(env->GetObjectClass(emulator), "SaveAndroidSettings", "(Ljava/lang/String;)V");
}
// Set home directory based on User config
const char* path = homeDirectory != NULL ? env->GetStringUTFChars(homeDirectory, 0) : "";
set_user_config_dir(path);
set_user_data_dir(path);
if (homeDirectory != NULL)
{
const char *jchar = env->GetStringUTFChars(homeDirectory, 0);
std::string path = jchar;
if (!path.empty())
{
if (path.back() != '/')
path += '/';
set_user_config_dir(path);
add_system_data_dir(path);
set_user_data_dir(path + "data/");
}
env->ReleaseStringUTFChars(homeDirectory, jchar);
}
INFO_LOG(BOOT, "Config dir is: %s", get_writable_config_path("").c_str());
INFO_LOG(BOOT, "Data dir is: %s", get_writable_data_path("").c_str());
if (homeDirectory != NULL)
env->ReleaseStringUTFChars(homeDirectory, path);
if (first_init)
{

View File

@ -134,27 +134,32 @@ extern "C" void emu_gles_init(int width, int height) {
char *home = getenv("HOME");
if (home != NULL)
{
std::string config_dir = std::string(home) + "/.reicast";
std::string config_dir = std::string(home) + "/.reicast/";
if (!file_exists(config_dir))
config_dir = std::string(home) + "/.flycast/";
int instanceNumber = (int)[[NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.reicast.Flycast"] count];
if (instanceNumber > 1){
config_dir += "/" + std::to_string(instanceNumber);
config_dir += std::to_string(instanceNumber) + "/";
[[NSApp dockTile] setBadgeLabel:@(instanceNumber).stringValue];
}
mkdir(config_dir.c_str(), 0755); // create the directory if missing
set_user_config_dir(config_dir);
add_system_data_dir(config_dir);
config_dir += "data/";
mkdir(config_dir.c_str(), 0755);
set_user_data_dir(config_dir);
}
else
{
set_user_config_dir(".");
set_user_data_dir(".");
set_user_config_dir("./");
set_user_data_dir("./");
}
// Add bundle resources path
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
char path[PATH_MAX];
if (CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
add_system_data_dir(std::string(path));
add_system_data_dir(std::string(path) + "/");
CFRelease(resourcesURL);
CFRelease(mainBundle);