mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-22 05:45:38 +00:00
Keep Supermodel files (config, nvram, saves, etc.) in a predictable path when running on Linux
This commit is contained in:
parent
db455ba5c1
commit
df7787f040
|
@ -70,6 +70,9 @@ PLATFORM_LDFLAGS = $(SDL2_LIBS) -lGL -lGLU -lz -lm -lstdc++ -lpthread -lSDL2_net
|
||||||
# Core Makefile
|
# Core Makefile
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
PLATFORM_SRC_FILES = \
|
||||||
|
Src/OSD/Unix/FileSystemPath.cpp
|
||||||
|
|
||||||
include Makefiles/Rules.inc
|
include Makefiles/Rules.inc
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|
|
@ -103,6 +103,7 @@ PLATFORM_LDFLAGS = -static -L$(sort $(PLATFORM_LIB_DIR)) $(SDL2_LIBS) $(PLATFORM
|
||||||
|
|
||||||
PLATFORM_SRC_FILES = \
|
PLATFORM_SRC_FILES = \
|
||||||
Src/OSD/Windows/DirectInputSystem.cpp \
|
Src/OSD/Windows/DirectInputSystem.cpp \
|
||||||
|
Src/OSD/Windows/FileSystemPath.cpp \
|
||||||
Src/OSD/Windows/WinOutputs.cpp
|
Src/OSD/Windows/WinOutputs.cpp
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
|
40
Src/OSD/FileSystemPath.h
Normal file
40
Src/OSD/FileSystemPath.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2003-2022 The Supermodel Team
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FileSystemPaths.h
|
||||||
|
*
|
||||||
|
* Header file for OS-dependent Supermodel files locations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_FILESYSTEMPATH_H
|
||||||
|
#define INCLUDED_FILESYSTEMPATH_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace FileSystemPath
|
||||||
|
{
|
||||||
|
bool PathExists(std::string fileSystemPath); // Checks if a directory exists (returns true if exists, false if it doesn't)
|
||||||
|
std::string GetPath(std::string pathType); // Generates a path to be used by Supermodel files
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_FILESYSTEMPATH_H
|
|
@ -66,6 +66,7 @@
|
||||||
#include "Util/Format.h"
|
#include "Util/Format.h"
|
||||||
#include "Util/NewConfig.h"
|
#include "Util/NewConfig.h"
|
||||||
#include "Util/ConfigBuilders.h"
|
#include "Util/ConfigBuilders.h"
|
||||||
|
#include "OSD/FileSystemPath.h"
|
||||||
#include "GameLoader.h"
|
#include "GameLoader.h"
|
||||||
#include "SDLInputSystem.h"
|
#include "SDLInputSystem.h"
|
||||||
#include "SDLIncludes.h"
|
#include "SDLIncludes.h"
|
||||||
|
@ -475,7 +476,7 @@ void Screenshot()
|
||||||
time_t now = std::time(nullptr);
|
time_t now = std::time(nullptr);
|
||||||
tm* ltm = std::localtime(&now);
|
tm* ltm = std::localtime(&now);
|
||||||
|
|
||||||
sprintf(file, "Screenshot %.4d-%.2d-%.2d (%.2d-%.2d-%.2d).bmp", 1900 + ltm->tm_year, 1 + ltm->tm_mon, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
|
sprintf(file, "%sScreenshot %.4d-%.2d-%.2d (%.2d-%.2d-%.2d).bmp", FileSystemPath::GetPath("Screenshots").c_str(), 1900 + ltm->tm_year, 1 + ltm->tm_mon, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
|
||||||
|
|
||||||
info += file;
|
info += file;
|
||||||
puts(info.c_str());
|
puts(info.c_str());
|
||||||
|
@ -549,7 +550,7 @@ static void TestPolygonHeaderBits(IEmulator *Emu)
|
||||||
if ((unknownPolyBits[idx] & mask))
|
if ((unknownPolyBits[idx] & mask))
|
||||||
{
|
{
|
||||||
Emu->RenderFrame();
|
Emu->RenderFrame();
|
||||||
std::string file = Util::Format() << "Analysis/" << GetFileBaseName(s_gfxStatePath) << "." << "poly" << "." << idx << "_" << Util::Hex(mask) << ".bmp";
|
std::string file = Util::Format() << s_analysisPath << GetFileBaseName(s_gfxStatePath) << "." << "poly" << "." << idx << "_" << Util::Hex(mask) << ".bmp";
|
||||||
SaveFrameBuffer(file);
|
SaveFrameBuffer(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -565,7 +566,7 @@ static void TestPolygonHeaderBits(IEmulator *Emu)
|
||||||
if ((unknownCullingNodeBits[idx] & mask))
|
if ((unknownCullingNodeBits[idx] & mask))
|
||||||
{
|
{
|
||||||
Emu->RenderFrame();
|
Emu->RenderFrame();
|
||||||
std::string file = Util::Format() << "Analysis/" << GetFileBaseName(s_gfxStatePath) << "." << "culling" << "." << idx << "_" << Util::Hex(mask) << ".bmp";
|
std::string file = Util::Format() << s_analysisPath << GetFileBaseName(s_gfxStatePath) << "." << "culling" << "." << idx << "_" << Util::Hex(mask) << ".bmp";
|
||||||
SaveFrameBuffer(file);
|
SaveFrameBuffer(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -574,7 +575,7 @@ static void TestPolygonHeaderBits(IEmulator *Emu)
|
||||||
glReadBuffer(readBuffer);
|
glReadBuffer(readBuffer);
|
||||||
|
|
||||||
// Generate the HTML GUI
|
// Generate the HTML GUI
|
||||||
std::string file = Util::Format() << "Analysis/_" << GetFileBaseName(s_gfxStatePath) << ".html";
|
std::string file = Util::Format() << s_analysisPath << "_" << GetFileBaseName(s_gfxStatePath) << ".html";
|
||||||
std::ofstream fs(file);
|
std::ofstream fs(file);
|
||||||
if (!fs.good())
|
if (!fs.good())
|
||||||
ErrorLog("Unable to open '%s' for writing.", file.c_str());
|
ErrorLog("Unable to open '%s' for writing.", file.c_str());
|
||||||
|
@ -615,7 +616,7 @@ static void SaveState(IEmulator *Model3)
|
||||||
{
|
{
|
||||||
CBlockFile SaveState;
|
CBlockFile SaveState;
|
||||||
|
|
||||||
std::string file_path = Util::Format() << "Saves/" << Model3->GetGame().name << ".st" << s_saveSlot;
|
std::string file_path = Util::Format() << FileSystemPath::GetPath("Saves") << Model3->GetGame().name << ".st" << s_saveSlot;
|
||||||
if (OKAY != SaveState.Create(file_path, "Supermodel Save State", "Supermodel Version " SUPERMODEL_VERSION))
|
if (OKAY != SaveState.Create(file_path, "Supermodel Save State", "Supermodel Version " SUPERMODEL_VERSION))
|
||||||
{
|
{
|
||||||
ErrorLog("Unable to save state to '%s'.", file_path.c_str());
|
ErrorLog("Unable to save state to '%s'.", file_path.c_str());
|
||||||
|
@ -640,7 +641,7 @@ static void LoadState(IEmulator *Model3, std::string file_path = std::string())
|
||||||
|
|
||||||
// Generate file path
|
// Generate file path
|
||||||
if (file_path.empty())
|
if (file_path.empty())
|
||||||
file_path = Util::Format() << "Saves/" << Model3->GetGame().name << ".st" << s_saveSlot;
|
file_path = Util::Format() << FileSystemPath::GetPath("Saves") << Model3->GetGame().name << ".st" << s_saveSlot;
|
||||||
|
|
||||||
// Open and check to make sure format is correct
|
// Open and check to make sure format is correct
|
||||||
if (OKAY != SaveState.Load(file_path))
|
if (OKAY != SaveState.Load(file_path))
|
||||||
|
@ -674,7 +675,7 @@ static void SaveNVRAM(IEmulator *Model3)
|
||||||
{
|
{
|
||||||
CBlockFile NVRAM;
|
CBlockFile NVRAM;
|
||||||
|
|
||||||
std::string file_path = Util::Format() << "NVRAM/" << Model3->GetGame().name << ".nv";
|
std::string file_path = Util::Format() << FileSystemPath::GetPath("NVRAM") << Model3->GetGame().name << ".nv";
|
||||||
if (OKAY != NVRAM.Create(file_path, "Supermodel NVRAM State", "Supermodel Version " SUPERMODEL_VERSION))
|
if (OKAY != NVRAM.Create(file_path, "Supermodel NVRAM State", "Supermodel Version " SUPERMODEL_VERSION))
|
||||||
{
|
{
|
||||||
ErrorLog("Unable to save NVRAM to '%s'. Make sure directory exists!", file_path.c_str());
|
ErrorLog("Unable to save NVRAM to '%s'. Make sure directory exists!", file_path.c_str());
|
||||||
|
@ -697,7 +698,7 @@ static void LoadNVRAM(IEmulator *Model3)
|
||||||
CBlockFile NVRAM;
|
CBlockFile NVRAM;
|
||||||
|
|
||||||
// Generate file path
|
// Generate file path
|
||||||
std::string file_path = Util::Format() << "NVRAM/" << Model3->GetGame().name << ".nv";
|
std::string file_path = Util::Format() << FileSystemPath::GetPath("NVRAM") << Model3->GetGame().name << ".nv";
|
||||||
|
|
||||||
// Open and check to make sure format is correct
|
// Open and check to make sure format is correct
|
||||||
if (OKAY != NVRAM.Load(file_path))
|
if (OKAY != NVRAM.Load(file_path))
|
||||||
|
@ -1448,8 +1449,10 @@ QuitError:
|
||||||
Entry Point and Command Line Procesing
|
Entry Point and Command Line Procesing
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
static const char s_configFilePath[] = { "Config/Supermodel.ini" };
|
static const std::string s_analysisPath = Util::Format() << FileSystemPath::GetPath("Analysis");
|
||||||
static const char s_gameXMLFilePath[] = { "Config/Games.xml" };
|
static const std::string s_configFilePath = Util::Format() << FileSystemPath::GetPath("Config") << "Supermodel.ini";
|
||||||
|
static const std::string s_gameXMLFilePath = Util::Format() << FileSystemPath::GetPath("Config") << "Games.xml";
|
||||||
|
static const std::string s_logFilePath = Util::Format() << FileSystemPath::GetPath("Log") << "Supermodel.log";
|
||||||
|
|
||||||
// Create and configure inputs
|
// Create and configure inputs
|
||||||
static bool ConfigureInputs(CInputs *Inputs, Util::Config::Node *fileConfig, Util::Config::Node *runtimeConfig, const Game &game, bool configure)
|
static bool ConfigureInputs(CInputs *Inputs, Util::Config::Node *fileConfig, Util::Config::Node *runtimeConfig, const Game &game, bool configure)
|
||||||
|
@ -1640,8 +1643,8 @@ static void Help(void)
|
||||||
puts("General Options:");
|
puts("General Options:");
|
||||||
puts(" -?, -h, -help, --help Print this help text");
|
puts(" -?, -h, -help, --help Print this help text");
|
||||||
puts(" -print-games List supported games and quit");
|
puts(" -print-games List supported games and quit");
|
||||||
printf(" -game-xml-file=<file> ROM set definition file [Default: %s]\n", s_gameXMLFilePath);
|
printf(" -game-xml-file=<file> ROM set definition file [Default: %s]\n", s_gameXMLFilePath.c_str());
|
||||||
puts(" -log-output=<outputs> Log output destination(s) [Default: Supermodel.log]");
|
printf(" -log-output=<outputs> Log output destination(s) [Default: %s]\n", s_logFilePath.c_str());
|
||||||
puts(" -log-level=<level> Logging threshold [Default: info]");
|
puts(" -log-level=<level> Logging threshold [Default: info]");
|
||||||
puts("");
|
puts("");
|
||||||
puts("Core Options:");
|
puts("Core Options:");
|
||||||
|
@ -1737,7 +1740,7 @@ struct ParsedCommandLine
|
||||||
{
|
{
|
||||||
// Logging is special: it is only parsed from the command line and
|
// Logging is special: it is only parsed from the command line and
|
||||||
// therefore, defaults are needed early
|
// therefore, defaults are needed early
|
||||||
config.Set("LogOutput", "Supermodel.log");
|
config.Set("LogOutput", s_logFilePath.c_str());
|
||||||
config.Set("LogLevel", "info");
|
config.Set("LogLevel", "info");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
122
Src/OSD/Unix/FileSystemPath.cpp
Normal file
122
Src/OSD/Unix/FileSystemPath.cpp
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2003-2022 The Supermodel Team
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "FileSystemPath.h"
|
||||||
|
#include "Util/Format.h"
|
||||||
|
#include <string>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace FileSystemPath
|
||||||
|
{
|
||||||
|
// Checks if a directory exists (returns true if exists, false if it doesn't)
|
||||||
|
bool PathExists(std::string fileSystemPath)
|
||||||
|
{
|
||||||
|
bool pathExists = false;
|
||||||
|
struct stat pathInfo;
|
||||||
|
|
||||||
|
if (stat(fileSystemPath.c_str(), &pathInfo) == 0 && S_ISDIR(pathInfo.st_mode))
|
||||||
|
{
|
||||||
|
pathExists = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathExists;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a path to be used by Supermodel files
|
||||||
|
std::string GetPath(std::string pathType)
|
||||||
|
{
|
||||||
|
std::string finalPath = "";
|
||||||
|
std::string homePath = "";
|
||||||
|
struct passwd* pwd = getpwuid(getuid());
|
||||||
|
|
||||||
|
// Get user's HOME directory
|
||||||
|
if (pwd)
|
||||||
|
{
|
||||||
|
homePath = pwd->pw_dir;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
homePath = getenv("HOME");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If Config path exists in current directory or the user doesn't have a HOME directory use current directory
|
||||||
|
if (FileSystemPath::PathExists("Config") || homePath.empty())
|
||||||
|
{
|
||||||
|
// Use current directory
|
||||||
|
if (pathType == "Screenshots" || pathType == "Log")
|
||||||
|
{
|
||||||
|
finalPath = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If directory doesn't exist, create it
|
||||||
|
if (!FileSystemPath::PathExists(pathType)) mkdir(pathType.c_str(), 0775);
|
||||||
|
finalPath = pathType;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// Check if $HOME/.supermodel exists
|
||||||
|
else if (FileSystemPath::PathExists(Util::Format() << homePath << "/.supermodel"))
|
||||||
|
{
|
||||||
|
// Use $HOME/.supermodel
|
||||||
|
finalPath = Util::Format() << homePath << "/.supermodel/" << pathType;
|
||||||
|
// If directory doesn't exist, create it
|
||||||
|
if (!FileSystemPath::PathExists(finalPath))
|
||||||
|
{
|
||||||
|
mkdir(finalPath.c_str(), 0775);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// On Linux one may want to follow the XDG base directory specs (https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use $HOME/.config/supermodel or $HOME/.local/share/supermodel depending on the file type
|
||||||
|
if (pathType == "Config")
|
||||||
|
{
|
||||||
|
finalPath = Util::Format() << homePath << "/.config/supermodel";
|
||||||
|
// If directory doesn't exist, create it
|
||||||
|
if (!FileSystemPath::PathExists(finalPath)) mkdir(finalPath.c_str(), 0775);
|
||||||
|
// If directory doesn't exist, create it
|
||||||
|
finalPath = Util::Format() << homePath << "/.config/supermodel/Config";
|
||||||
|
if (!FileSystemPath::PathExists(finalPath)) mkdir(finalPath.c_str(), 0775);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
finalPath = Util::Format() << homePath << "/.local/share/supermodel";
|
||||||
|
// If directory doesn't exist, create it
|
||||||
|
if (!FileSystemPath::PathExists(finalPath)) mkdir(finalPath.c_str(), 0775);
|
||||||
|
// If directory doesn't exist, create it
|
||||||
|
finalPath = Util::Format() << homePath << "/.local/share/supermodel/" << pathType;
|
||||||
|
if (!FileSystemPath::PathExists(finalPath)) mkdir(finalPath.c_str(), 0775);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalPath != "") finalPath = Util::Format() << finalPath << "/";
|
||||||
|
|
||||||
|
return finalPath;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
37
Src/OSD/Windows/FileSystemPath.cpp
Normal file
37
Src/OSD/Windows/FileSystemPath.cpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2003-2022 The Supermodel Team
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "FileSystemPath.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace FileSystemPath
|
||||||
|
{
|
||||||
|
// Generates a path to be used by Supermodel files
|
||||||
|
std::string GetPath(std::string pathType)
|
||||||
|
{
|
||||||
|
if (pathType == "Config") return "Config/";
|
||||||
|
if (pathType == "Screenshots") return "";
|
||||||
|
if (pathType == "Saves") return "Saves/";
|
||||||
|
if (pathType == "NVRAM") return "NVRAM/";
|
||||||
|
if (pathType == "Log") return "";
|
||||||
|
if (pathType == "Analysis") return "Analysis/";
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue