git repos / oastat

commit de76c825

sago007 · 2018-11-17 12:33
de76c82572e830aac03ba87c77487d12ca073c89 patch · browse files
parent 58c668c504c884bedd850ba24a3235b99bf5edb1

By default the XML backend now dumps the XML files to the cache directory.

Changed files

M src/db/Db2Xml.cpp before
M src/oastat.cpp before
M src/oastatstruct.cpp before
M src/oastatstruct.h before
A src/sago/platform_folders.cpp
A src/sago/platform_folders.h
diff --git a/src/db/Db2Xml.cpp b/src/db/Db2Xml.cpp index 74b0225..590c13c 100644 --- a/src/db/Db2Xml.cpp +++ b/src/db/Db2Xml.cpp
@@ -23,44 +23,37 @@ https://github.com/sago007/oastat/
#include "Db2Xml.hpp"
#include <boost/format.hpp>
+#include "../sago/platform_folders.h"
-using std::cout;
-using std::ofstream;
-using std::string;
-using std::stringstream;
Db2Xml::Db2Xml()
{
psoudo_playerids.clear();
nextId = 1;
- boost::format path("%1%/oastat");
- path % getenv("HOME");
- p_output_dir = path.str();
- boost::format fmkdir("mkdir -p %1%");
+ p_output_dir = sago::getCacheDir()+"/oastat/xml/";
+ boost::format fmkdir("mkdir -p \"%1%\"");
fmkdir % p_output_dir;
int fmkdir_ret = system(fmkdir.str().c_str());
if (fmkdir_ret) {
- cout << "Attemting to create \"" << path << "\" with \"" << fmkdir.str() << "\", but return code was: " << fmkdir_ret << "\n";
+ std::cout << "Attemting to create \"" << p_output_dir << "\" with \"" << fmkdir.str() << "\", but return code was: " << fmkdir_ret << "\n";
}
}
Db2Xml::Db2Xml(std::string dbargs)
{
- boost::format path("%1%/oastat");
- path % getenv("HOME");
- p_output_dir = path.str();
+ p_output_dir = sago::getCacheDir()+"/oastat/xml/";
p_postscript = "";
psoudo_playerids.clear();
nextId = 1;
//mkdir(p_output_dir.c_str());
- stringstream stream(stringstream::in | stringstream::out);
+ std::stringstream stream(std::stringstream::in | std::stringstream::out);
stream << dbargs;
- string holder;
+ std::string holder;
//stream >> holder;
while (!stream.eof()) {
stream >> holder;
if (!stream.eof()) {
- string param;
+ std::string param;
stream >> param;
if(holder == "outputdir") {
p_output_dir = param;
@@ -74,7 +67,7 @@ Db2Xml::Db2Xml(std::string dbargs)
fmkdir % p_output_dir;
int fmkdir_ret = system(fmkdir.str().c_str());
if (fmkdir_ret) {
- cout << "Attemting to create \"" << path << "\" with \"" << fmkdir.str() << "\", but return code was: " << fmkdir_ret << "\n";
+ std::cout << "Attemting to create \"" << p_output_dir << "\" with \"" << fmkdir.str() << "\", but return code was: " << fmkdir_ret << "\n";
}
}
@@ -82,7 +75,7 @@ Db2Xml::~Db2Xml()
{
}
-void Db2Xml::startGame(int gametype, const string &mapname, const string &basegame, const string &servername, const OaStatStruct &oss)
+void Db2Xml::startGame(int gametype, const std::string &mapname, const std::string &basegame, const std::string &servername, const OaStatStruct &oss)
{
isOk = true;
p_xmlcontent = "";
@@ -112,17 +105,17 @@ void Db2Xml::endGame(int second)
if (isOk) {
p_xmlcontent += (boost::format(" <second>%1%</second>\n") % second).str();
p_xmlcontent += "</OaStatGame>\n";
- string filename = (boost::format("%1%/%2%_%3%-%4%-%5%_%6%-%7%-%8%.xml") % p_output_dir % p_servername
+ std::string filename = (boost::format("%1%/%2%_%3%-%4%-%5%_%6%-%7%-%8%.xml") % p_output_dir % p_servername
% (p_gametime.tm_year+1900) % (p_gametime.tm_mon+1) % (p_gametime.tm_mday)
% (p_gametime.tm_hour) % (p_gametime.tm_min) % (p_gametime.tm_sec) ).str();
- ofstream outfile;
+ std::ofstream outfile;
outfile.open (filename.c_str());
if (outfile.fail()) {
- cout << "could not create " << filename << "\n";
+ std::cout << "could not create " << filename << "\n";
}
outfile << p_xmlcontent;
outfile.close();
- cout << "End game at " << second << "with size " << p_xmlcontent.length() << " written to " << filename << "\n";
+ std::cout << "End game at " << second << "with size " << p_xmlcontent.length() << " written to " << filename << "\n";
}
}
int Db2Xml::getGameNumber()
@@ -294,7 +287,7 @@ void Db2Xml::doNotCommit()
isOk = false;
}
-int Db2Xml::getPsoudoId(const string &guid)
+int Db2Xml::getPsoudoId(const std::string &guid)
{
int ret = psoudo_playerids[guid];
if (ret > 0) {
@@ -308,14 +301,14 @@ int Db2Xml::getPsoudoId(const string &guid)
return ret;
}
-string Db2Xml::getXmlEscaped(const string &org)
+std::string Db2Xml::getXmlEscaped(const std::string &org)
{
- string result = org;
- boost::algorithm::replace_all(result,(string)"&",(string)"&amp;");
- boost::algorithm::replace_all(result,(string)"<",(string)"&lt;");
- boost::algorithm::replace_all(result,(string)">",(string)"&gt;");
- boost::algorithm::replace_all(result,(string)"\"",(string)"&quot;");
- boost::algorithm::replace_all(result,(string)"'",(string)"&apos;");
+ std::string result = org;
+ boost::algorithm::replace_all(result, "&", "&amp;");
+ boost::algorithm::replace_all(result, "<", "&lt;");
+ boost::algorithm::replace_all(result, ">", "&gt;");
+ boost::algorithm::replace_all(result, "\"", "&quot;");
+ boost::algorithm::replace_all(result, "'", "&apos;");
return result;
}
diff --git a/src/oastat.cpp b/src/oastat.cpp index eef33f8..9acd1b9 100644 --- a/src/oastat.cpp +++ b/src/oastat.cpp
@@ -104,8 +104,7 @@ static void addCommands(std::shared_ptr<Database> &db,std::vector<std::shared_pt
static int processStdIn(std::istream &in_p, std::vector<std::shared_ptr<Struct2Db> > &commands)
{
bool done = true;
- OaStatStruct *startstruct;
- startstruct = NULL;
+ OaStatStruct *startstruct = nullptr;
do {
std::string line = "";
OaStatStruct oss;
@@ -183,7 +182,6 @@ int main (int argc, const char* argv[])
bool doIntegrationTest = false;
std::vector<std::shared_ptr<Struct2Db> > commands;
/////////////
- //dbargs = "mysql dbname oastat";
boost::format f("%1%/.openarena/baseoa/games.log");
f % getenv("HOME");
std::string filename = f.str();
@@ -195,7 +193,6 @@ int main (int argc, const char* argv[])
("dbargs", boost::program_options::value<std::string>(), "Arguments passed to the DB backend")
("filename,f", boost::program_options::value<std::string>(), "Filename to read. Providing a blank string will read from stdin")
("config,c", boost::program_options::value<std::vector<std::string> >(), "Read a config file with the values. Can be given multiple times")
- ("tail", "Use tail on the filename given to read the file. This will be ignored on Windows.")
("integration-test", "Perform integration test")
;
boost::program_options::variables_map vm;
@@ -266,7 +263,7 @@ int main (int argc, const char* argv[])
db->startGame(1,"oasago2","baseoa-mod","testserver",oss_test);
db->addGenericTeamEvent(5,2,0,"sometype","","",3,4);
std::cerr << "Test called\n";
- return 1;
+ return 0;
}
if (filename.length()>0) {
diff --git a/src/oastatstruct.cpp b/src/oastatstruct.cpp index 5f5aa0e..895b8a4 100644 --- a/src/oastatstruct.cpp +++ b/src/oastatstruct.cpp
@@ -45,19 +45,15 @@ void OaStatStruct::clear()
restOfLine = "";
}
-void OaStatStruct::parseLine(std::string line)
+void OaStatStruct::parseLine(const std::string& orgLine)
{
//Parse time
- //cout << line << endl;
- //try{
- std::string tempTimeString = line.substr(0,7);
+ std::string tempTimeString = orgLine.substr(0,7);
int posColon = tempTimeString.find(":");
int minute = atoi(tempTimeString.substr(0,posColon).c_str());
- /*time_t thetime = time(NULL);
- gmtime_r(&thetime,&_datetime);*/
second = atoi(tempTimeString.substr(posColon+1,7).c_str());
second+=minute*60;
- line = line.substr(7,line.length()); //Cut the first part of the string.
+ std::string line = orgLine.substr(7, orgLine.length()); //Cut the first part of the string.
//Parse command name:
posColon = line.find(":");
diff --git a/src/oastatstruct.h b/src/oastatstruct.h index 387fb70..3951095 100644 --- a/src/oastatstruct.h +++ b/src/oastatstruct.h
@@ -50,7 +50,7 @@ public:
std::string restOfLine;
const tm getDateTime() const;
- void parseLine(std::string line);
+ void parseLine(const std::string& line);
void clear();
/*
diff --git a/src/sago/platform_folders.cpp b/src/sago/platform_folders.cpp new file mode 100644 index 0000000..f55e4ad --- /dev/null +++ b/src/sago/platform_folders.cpp
@@ -0,0 +1,400 @@
+/*
+Its is under the MIT license, to encourage reuse by cut-and-paste.
+
+The original files are hosted here: https://github.com/sago007/PlatformFolders
+
+Copyright (c) 2015-2016 Poul Sander
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation files
+(the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "platform_folders.h"
+#include <iostream>
+#include <stdexcept>
+#include <string.h>
+#include <cstdio>
+#include <cstdlib>
+
+#if defined(_WIN32)
+#include <windows.h>
+#include <shlobj.h>
+
+static std::string win32_utf16_to_utf8(const wchar_t* wstr) {
+ std::string res;
+ // If the 6th parameter is 0 then WideCharToMultiByte returns the number of bytes needed to store the result.
+ int actualSize = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
+ if (actualSize > 0) {
+ //If the converted UTF-8 string could not be in the initial buffer. Allocate one that can hold it.
+ std::vector<char> buffer(actualSize);
+ actualSize = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &buffer[0], buffer.size(), NULL, NULL);
+ res = buffer.data();
+ }
+ if (actualSize == 0) {
+ // WideCharToMultiByte return 0 for errors.
+ const std::string errorMsg = "UTF16 to UTF8 failed with error code: " + GetLastError();
+ throw std::runtime_error(errorMsg.c_str());
+ }
+ return res;
+}
+
+static std::string GetWindowsFolder(int folderId, const char* errorMsg) {
+ wchar_t szPath[MAX_PATH];
+ szPath[0] = 0;
+ if ( !SUCCEEDED( SHGetFolderPathW( NULL, folderId, NULL, 0, szPath ) ) ) {
+ throw std::runtime_error(errorMsg);
+ }
+ return win32_utf16_to_utf8(szPath);
+}
+
+static std::string GetAppData() {
+ return GetWindowsFolder(CSIDL_APPDATA, "RoamingAppData could not be found");
+}
+
+static std::string GetAppDataCommon() {
+ return GetWindowsFolder(CSIDL_COMMON_APPDATA, "Common appdata could not be found");
+}
+
+static std::string GetAppDataLocal() {
+ return GetWindowsFolder(CSIDL_LOCAL_APPDATA, "LocalAppData could not be found");
+}
+#elif defined(__APPLE__)
+#include <CoreServices/CoreServices.h>
+
+static std::string GetMacFolder(OSType folderType, const char* errorMsg) {
+ std::string ret;
+ FSRef ref;
+ char path[PATH_MAX];
+ OSStatus err = FSFindFolder( kUserDomain, folderType, kCreateFolder, &ref );
+ if (err != noErr) {
+ throw std::runtime_error(errorMsg);
+ }
+ FSRefMakePath( &ref, (UInt8*)&path, PATH_MAX );
+ ret = path;
+ return ret;
+}
+
+#else
+#include <map>
+#include <fstream>
+#include <pwd.h>
+#include <unistd.h>
+#include <sys/types.h>
+// For strlen and strtok
+#include <cstring>
+//Typically Linux. For easy reading the comments will just say Linux but should work with most *nixes
+
+static void throwOnRelative(const char* envName, const char* envValue) {
+ if (envValue[0] != '/') {
+ char buffer[200];
+ std::snprintf(buffer, sizeof(buffer), "Environment \"%s\" does not start with an '/'. XDG specifies that the value must be absolute. The current value is: \"%s\"", envName, envValue);
+ throw std::runtime_error(buffer);
+ }
+}
+
+/**
+ * Retrives the effective user's home dir.
+ * If the user is running as root we ignore the HOME environment. It works badly with sudo.
+ * Writing to $HOME as root implies security concerns that a multiplatform program cannot be assumed to handle.
+ * @return The home directory. HOME environment is respected for non-root users if it exists.
+ */
+static std::string getHome() {
+ std::string res;
+ int uid = getuid();
+ const char* homeEnv = std::getenv("HOME");
+ if ( uid != 0 && homeEnv) {
+ //We only acknowlegde HOME if not root.
+ res = homeEnv;
+ return res;
+ }
+ struct passwd* pw = getpwuid(uid);
+ if (!pw) {
+ throw std::runtime_error("Unable to get passwd struct.");
+ }
+ const char* tempRes = pw->pw_dir;
+ if (!tempRes) {
+ throw std::runtime_error("User has no home directory");
+ }
+ res = tempRes;
+ return res;
+}
+
+static std::string getLinuxFolderDefault(const char* envName, const char* defaultRelativePath) {
+ std::string res;
+ const char* tempRes = std::getenv(envName);
+ if (tempRes) {
+ throwOnRelative(envName, tempRes);
+ res = tempRes;
+ return res;
+ }
+ res = getHome() + "/" + defaultRelativePath;
+ return res;
+}
+
+static void appendExtraFoldersTokenizer(const char* envName, const char* envValue, std::vector<std::string>& folders) {
+ std::vector<char> buffer(envValue, envValue + std::strlen(envValue) + 1);
+ char* p = std::strtok ( &buffer[0], ":");
+ while (p != NULL) {
+ if (p[0] == '/') {
+ folders.push_back(p);
+ }
+ else {
+ //Unless the system is wrongly configured this should never happen... But of course some systems will be incorectly configured.
+ //The XDG documentation indicates that the folder should be ignored but that the program should continue.
+ std::cerr << "Skipping path \"" << p << "\" in \"" << envName << "\" because it does not start with a \"/\"\n";
+ }
+ p = std::strtok (NULL, ":");
+ }
+}
+
+static void appendExtraFolders(const char* envName, const char* defaultValue, std::vector<std::string>& folders) {
+ const char* envValue = std::getenv(envName);
+ if (!envValue) {
+ envValue = defaultValue;
+ }
+ appendExtraFoldersTokenizer(envName, envValue, folders);
+}
+
+#endif
+
+
+namespace sago {
+
+std::string getDataHome() {
+#if defined(_WIN32)
+ return GetAppData();
+#elif defined(__APPLE__)
+ return GetMacFolder(kApplicationSupportFolderType, "Failed to find the Application Support Folder");
+#else
+ return getLinuxFolderDefault("XDG_DATA_HOME", ".local/share");
+#endif
+}
+
+std::string getConfigHome() {
+#if defined(_WIN32)
+ return GetAppData();
+#elif defined(__APPLE__)
+ return GetMacFolder(kApplicationSupportFolderType, "Failed to find the Application Support Folder");
+#else
+ return getLinuxFolderDefault("XDG_CONFIG_HOME", ".config");
+#endif
+}
+
+std::string getCacheDir() {
+#if defined(_WIN32)
+ return GetAppDataLocal();
+#elif defined(__APPLE__)
+ return GetMacFolder(kCachedDataFolderType, "Failed to find the Application Support Folder");
+#else
+ return getLinuxFolderDefault("XDG_CACHE_HOME", ".cache");
+#endif
+}
+
+void appendAdditionalDataDirectories(std::vector<std::string>& homes) {
+#if defined(_WIN32)
+ homes.push_back(GetAppDataCommon());
+#elif defined(__APPLE__)
+#else
+ appendExtraFolders("XDG_DATA_DIRS", "/usr/local/share/:/usr/share/", homes);
+#endif
+}
+
+void appendAdditionalConfigDirectories(std::vector<std::string>& homes) {
+#if defined(_WIN32)
+ homes.push_back(GetAppDataCommon());
+#elif defined(__APPLE__)
+#else
+ appendExtraFolders("XDG_CONFIG_DIRS", "/etc/xdg", homes);
+#endif
+}
+
+#if defined(_WIN32)
+#elif defined(__APPLE__)
+#else
+struct PlatformFolders::PlatformFoldersData {
+ std::map<std::string, std::string> folders;
+};
+
+static void PlatformFoldersAddFromFile(const std::string& filename, std::map<std::string, std::string>& folders) {
+ std::ifstream infile(filename.c_str());
+ std::string line;
+ while (std::getline(infile, line)) {
+ if (line.length() == 0 || line.at(0) == '#' || line.substr(0, 4) != "XDG_" || line.find("_DIR") == std::string::npos) {
+ continue;
+ }
+ try {
+ std::size_t splitPos = line.find('=');
+ std::string key = line.substr(0, splitPos);
+ std::size_t valueStart = line.find('"', splitPos);
+ std::size_t valueEnd = line.find('"', valueStart+1);
+ std::string value = line.substr(valueStart+1, valueEnd - valueStart - 1);
+ folders[key] = value;
+ } catch (std::exception& e) {
+ std::cerr << "WARNING: Failed to process \"" << line << "\" from \"" << filename << "\". Error: "<< e.what() << "\n";
+ continue;
+ }
+ }
+}
+
+static void PlatformFoldersFillData(std::map<std::string, std::string>& folders) {
+ folders["XDG_DOCUMENTS_DIR"] = "$HOME/Documents";
+ folders["XDG_DESKTOP_DIR"] = "$HOME/Desktop";
+ folders["XDG_DOWNLOAD_DIR"] = "$HOME/Downloads";
+ folders["XDG_MUSIC_DIR"] = "$HOME/Music";
+ folders["XDG_PICTURES_DIR"] = "$HOME/Pictures";
+ folders["XDG_PUBLICSHARE_DIR"] = "$HOME/Public";
+ folders["XDG_TEMPLATES_DIR"] = "$HOME/.Templates";
+ folders["XDG_VIDEOS_DIR"] = "$HOME/Videos";
+ PlatformFoldersAddFromFile( getConfigHome()+"/user-dirs.dirs", folders);
+ for (std::map<std::string, std::string>::iterator itr = folders.begin() ; itr != folders.end() ; ++itr ) {
+ std::string& value = itr->second;
+ if (value.compare(0, 5, "$HOME") == 0) {
+ value = getHome() + value.substr(5, std::string::npos);
+ }
+ }
+}
+#endif
+
+PlatformFolders::PlatformFolders() {
+#if defined(_WIN32)
+#elif defined(__APPLE__)
+#else
+ this->data = new PlatformFolders::PlatformFoldersData();
+ try {
+ PlatformFoldersFillData(data->folders);
+ }
+ catch (...) {
+ delete this->data;
+ throw;
+ }
+#endif
+}
+
+PlatformFolders::~PlatformFolders() {
+#if defined(_WIN32)
+#elif defined(__APPLE__)
+#else
+ delete this->data;
+#endif
+}
+
+std::string PlatformFolders::getDocumentsFolder() const {
+#if defined(_WIN32)
+ return GetWindowsFolder(CSIDL_PERSONAL, "Failed to find My Documents folder");
+#elif defined(__APPLE__)
+ return GetMacFolder(kDocumentsFolderType, "Failed to find Documents Folder");
+#else
+ return data->folders["XDG_DOCUMENTS_DIR"];
+#endif
+}
+
+std::string PlatformFolders::getDesktopFolder() const {
+#if defined(_WIN32)
+ return GetWindowsFolder(CSIDL_DESKTOP, "Failed to find Desktop folder");
+#elif defined(__APPLE__)
+ return GetMacFolder(kDesktopFolderType, "Failed to find Desktop folder");
+#else
+ return data->folders["XDG_DESKTOP_DIR"];
+#endif
+}
+
+std::string PlatformFolders::getPicturesFolder() const {
+#if defined(_WIN32)
+ return GetWindowsFolder(CSIDL_MYPICTURES, "Failed to find My Pictures folder");
+#elif defined(__APPLE__)
+ return GetMacFolder(kPictureDocumentsFolderType, "Failed to find Picture folder");
+#else
+ return data->folders["XDG_PICTURES_DIR"];
+#endif
+}
+
+std::string PlatformFolders::getDownloadFolder1() const {
+#if defined(_WIN32)
+ //Pre Vista. Files was downloaded to the desktop
+ return GetWindowsFolder(CSIDL_DESKTOP, "Failed to find My Downloads (Desktop) folder");
+#elif defined(__APPLE__)
+ return GetMacFolder(kDownloadsFolderType, "Failed to find Download folder");
+#else
+ return data->folders["XDG_DOWNLOAD_DIR"];
+#endif
+}
+
+std::string PlatformFolders::getMusicFolder() const {
+#if defined(_WIN32)
+ return GetWindowsFolder(CSIDL_MYMUSIC, "Failed to find My Music folder");
+#elif defined(__APPLE__)
+ return GetMacFolder(kMusicDocumentsFolderType, "Failed to find Music folder");
+#else
+ return data->folders["XDG_MUSIC_DIR"];
+#endif
+}
+
+std::string PlatformFolders::getVideoFolder() const {
+#if defined(_WIN32)
+ return GetWindowsFolder(CSIDL_MYVIDEO, "Failed to find My Video folder");
+#elif defined(__APPLE__)
+ return GetMacFolder(kMovieDocumentsFolderType, "Failed to find Movie folder");
+#else
+ return data->folders["XDG_VIDEOS_DIR"];
+#endif
+}
+
+std::string PlatformFolders::getSaveGamesFolder1() const {
+#if defined(_WIN32)
+ //A dedicated Save Games folder was not introduced until Vista. For XP and older save games are most often saved in a normal folder named "My Games".
+ //Data that should not be user accessible should be placed under GetDataHome() instead
+ return GetWindowsFolder(CSIDL_PERSONAL, "Failed to find My Documents folder")+"\\My Games";
+#elif defined(__APPLE__)
+ return GetMacFolder(kApplicationSupportFolderType, "Failed to find Application Support Folder");
+#else
+ return getDataHome();
+#endif
+}
+
+std::string getDesktopFolder() {
+ return PlatformFolders().getDesktopFolder();
+}
+
+std::string getDocumentsFolder() {
+ return PlatformFolders().getDocumentsFolder();
+}
+
+std::string getDownloadFolder1() {
+ return PlatformFolders().getDownloadFolder1();
+}
+
+std::string getPicturesFolder() {
+ return PlatformFolders().getPicturesFolder();
+}
+
+std::string getMusicFolder() {
+ return PlatformFolders().getMusicFolder();
+}
+
+std::string getVideoFolder() {
+ return PlatformFolders().getVideoFolder();
+}
+
+std::string getSaveGamesFolder1() {
+ return PlatformFolders().getSaveGamesFolder1();
+}
+
+
+} //namespace sago
diff --git a/src/sago/platform_folders.h b/src/sago/platform_folders.h new file mode 100644 index 0000000..bd6c0d5 --- /dev/null +++ b/src/sago/platform_folders.h
@@ -0,0 +1,234 @@
+/*
+Its is under the MIT license, to encourage reuse by cut-and-paste.
+
+The original files are hosted here: https://github.com/sago007/PlatformFolders
+
+Copyright (c) 2015 Poul Sander
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation files
+(the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef SAGO_PLATFORM_FOLDERS_H
+#define SAGO_PLATFORM_FOLDERS_H
+
+#include <vector>
+#include <string>
+
+/**
+ * The namespace I use for common function. Nothing special about it.
+ */
+namespace sago {
+
+/**
+ * Retrives the base folder for storring data files.
+ * You must add the program name yourself like this:
+ * @code{.cpp}
+ * string data_home = getDataHome()+"/My Program Name/";
+ * @endcode
+ * On Windows this defaults to %APPDATA% (Roaming profile)
+ * On Linux this defaults to ~/.local/share but can be configured
+ * @return The base folder for storring program data.
+ */
+std::string getDataHome();
+
+/**
+ * Retrives the base folder for storring config files.
+ * You must add the program name yourself like this:
+ * @code{.cpp}
+ * string data_home = getConfigHome()+"/My Program Name/";
+ * @endcode
+ * On Windows this defaults to %APPDATA% (Roaming profile)
+ * On Linux this defaults to ~/.config but can be configured
+ * @return The base folder for storring config data.
+ */
+std::string getConfigHome();
+
+/**
+ * Retrives the base folder for storring cache files.
+ * You must add the program name yourself like this:
+ * @code{.cpp}
+ * string data_home = getCacheDir()+"/My Program Name/";
+ * @endcode
+ * On Windows this defaults to %APPDATALOCAL%
+ * On Linux this defaults to ~/.cache but can be configured
+ * @return The base folder for storring data that do not need to be backed up.
+ */
+std::string getCacheDir();
+
+/**
+ * This will append extra folders that your program should be looking for data files in.
+ * This does not normally include the path returned by GetDataHome().
+ * If you want all the folders you should do something like:
+ * @code{.cpp}
+ * vector<string> folders;
+ * folders.push_back(getDataHome());
+ * appendAdditionalDataDirectories(folders);
+ * for (string s& : folders) {
+ * s+="/My Program Name/";
+ * }
+ * @endcode
+ * You must apply "/My Program Name/" to all the strings.
+ * The string at the lowest index has the highest priority.
+ * @param homes A vector that extra folders will be appended to.
+ */
+void appendAdditionalDataDirectories(std::vector<std::string>& homes);
+
+/**
+ * This will append extra folders that your program should be looking for config files in.
+ * This does not normally include the path returned by GetConfigHome().
+ * If you want all the folders you should do something like:
+ * @code{.cpp}
+ * std::vector<std::string> folders;
+ * folders.push_back(sago::getConfigHome());
+ * sago::appendAdditionalConfigDirectories(folders);
+ * for (std::string s& : folders) {
+ * s+="/My Program Name/";
+ * }
+ * @endcode
+ * You must apply "/My Program Name/" to all the strings.
+ * The string at the lowest index has the highest priority.
+ * @param homes A vector that extra folders will be appended to.
+ */
+void appendAdditionalConfigDirectories(std::vector<std::string>& homes);
+
+/**
+ * The folder that represents the desktop.
+ * Normally you should try not to use this folder.
+ * @return Absolute path to the user's desktop
+ */
+std::string getDesktopFolder();
+
+/**
+ * The folder to store user documents to
+ * @return Absolute path to the "Documents" folder
+ */
+std::string getDocumentsFolder();
+
+/**
+ * The folder where files are downloaded.
+ * @note Windows: This version is XP compatible and returns the Desktop. Vista and later has a dedicated folder.
+ * @return Absolute path to the folder where files are downloaded to.
+ */
+std::string getDownloadFolder1();
+
+/**
+ * The folder for storring the user's pictures.
+ * @return Absolute path to the "Picture" folder
+ */
+std::string getPicturesFolder();
+
+/**
+ * The folder where music is stored
+ * @return Absolute path to the music folder
+ */
+std::string getMusicFolder();
+
+/**
+ * The folder where video is stored
+ * @return Absolute path to the video folder
+ */
+std::string getVideoFolder();
+
+/**
+ * The base folder for storring saved games.
+ * You must add the program name to it like this:
+ * @code{.cpp}
+ * string saved_games_folder = sago::getSaveGamesFolder1()+"/My Program Name/";
+ * @endcode
+ * @note Windows: This is an XP compatible version and returns the path to "My Games" in Documents. Vista and later has an official folder.
+ * @note Linux: XDF does not define a folder for saved games. This will just return the same as GetDataHome()
+ * @return The folder base folder for storring save games.
+ */
+std::string getSaveGamesFolder1();
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+/**
+ * This class contains methods for finding the system depended special folders.
+ * For Windows these folders are either by convention or given by CSIDL.
+ * For Linux XDG convention is used.
+ * The Linux version has very little error checking and assumes that the config is correct
+ */
+class PlatformFolders {
+public:
+ PlatformFolders();
+ ~PlatformFolders();
+ /**
+ * The folder that represents the desktop.
+ * Normally you should try not to use this folder.
+ * @return Absolute path to the user's desktop
+ */
+ std::string getDesktopFolder() const;
+ /**
+ * The folder to store user documents to
+ * @return Absolute path to the "Documents" folder
+ */
+ std::string getDocumentsFolder() const;
+ /**
+ * The folder for storring the user's pictures.
+ * @return Absolute path to the "Picture" folder
+ */
+ std::string getPicturesFolder() const;
+ /**
+ * The folder where files are downloaded.
+ * @note Windows: This version is XP compatible and returns the Desktop. Vista and later has a dedicated folder.
+ * @return Absolute path to the folder where files are downloaded to.
+ */
+ std::string getDownloadFolder1() const;
+ /**
+ * The folder where music is stored
+ * @return Absolute path to the music folder
+ */
+ std::string getMusicFolder() const;
+ /**
+ * The folder where video is stored
+ * @return Absolute path to the video folder
+ */
+ std::string getVideoFolder() const;
+ /**
+ * The base folder for storring saved games.
+ * You must add the program name to it like this:
+ * @code{.cpp}
+ * PlatformFolders pf;
+ * string saved_games_folder = pf.getSaveGamesFolder1()+"/My Program Name/";
+ * @endcode
+ * @note Windows: This is an XP compatible version and returns the path to "My Games" in Documents. Vista and later has an official folder.
+ * @note Linux: XDF does not define a folder for saved games. This will just return the same as GetDataHome()
+ * @return The folder base folder for storring save games.
+ */
+ std::string getSaveGamesFolder1() const;
+private:
+ PlatformFolders(const PlatformFolders&);
+ PlatformFolders& operator=(const PlatformFolders&);
+#if defined(_WIN32)
+#elif defined(__APPLE__)
+#else
+ struct PlatformFoldersData;
+ PlatformFoldersData* data;
+#endif
+};
+
+#endif // skip doxygen
+
+
+} //namespace sago
+
+#endif /* PLATFORM_FOLDERS_H */