diff --git a/src/common.cpp b/src/common.cpp new file mode 100644 index 0000000..49a98d2 --- /dev/null +++ b/src/common.cpp @@ -0,0 +1,249 @@ +/* +=========================================================================== + * Saland Adventures +Copyright (C) 2014-2018 Poul Sander + +This program 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 2 of the License, or +(at your option) any later version. + +This program 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 this program. If not, see http://www.gnu.org/licenses/ + +Source information and contacts persons can be found at +https://github.com/sago007/saland +=========================================================================== +*/ + +#include "common.h" +#include +#include +#include "os.hpp" +#include "sago/SagoMiscSdl2.hpp" +#include "sago/SagoMisc.hpp" +#include + + +bool strequals(const char* a, const char* b) { + return strcmp(a,b) == 0; +} + +void dieOnNullptr(bool ptr, const char* msg) { + if (!ptr) { + sago::SagoFatalError(msg); + } +} + +double str2double(const std::string& str2parse) { + try { + return std::stod(str2parse); + } + catch (...) { + return 0.0; + } +} + +std::string SPrintStringF(const char* fmt, ...) { + std::string ret; + char buffer[1024]; + va_list args; + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + ret = buffer; + va_end(args); + return ret; +} + +const char* SPrintCF(const char* fmt, ...) { + static char buffer[1024]; + va_list args; + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + return buffer; +} + +int str2int(const std::string& str2parse) { + try { + return std::stoi(str2parse); + } + catch (...) { + return 0; + } +} + +/** + * Takes a number of milliseconds and returns the value in commonTime format. + */ +commonTime TimeHandler::ms2ct(unsigned int milliseconds) { + commonTime ct; + ct.days = 0; + unsigned int time = milliseconds; + ct.hours = time/(1000*60*60); + time = time % (1000*60*60); + ct.minutes = time/(1000*60); + time = time % (1000*60); + ct.seconds = time/1000; + return ct; +} + +commonTime TimeHandler::getTime(const std::string& name) { + commonTime ct; + ct.days = Config::getInstance()->getInt(name+"Days"); + ct.hours = Config::getInstance()->getInt(name+"Hours"); + ct.minutes = Config::getInstance()->getInt(name+"Minutes"); + ct.seconds = Config::getInstance()->getInt(name+"Seconds"); + return ct; +} + +/** + * Returns the total runtime with toAdd added but without writing it to config file. + * Used for stats + */ +commonTime TimeHandler::peekTime(const std::string& name, const commonTime& toAdd) { + commonTime ct = getTime(name); + + ct.seconds +=toAdd.seconds; + ct.minutes +=ct.seconds/60; + ct.seconds = ct.seconds%60; + + ct.minutes += toAdd.minutes; + ct.hours += ct.minutes/60; + ct.minutes = ct.minutes%60; + + ct.hours += toAdd.hours; + ct.days += ct.hours/24; + ct.hours = ct.hours%24; + + ct.days += toAdd.days; + return ct; +} + +/** + * Same as peekTotalTime but writes the time to the config file. + * Should only be called only once! when the program shuts down + */ +commonTime TimeHandler::addTime(const std::string& name, const commonTime& toAdd) { + commonTime ct = peekTime(name,toAdd); + + Config::getInstance()->setInt(name+"Days",ct.days); + Config::getInstance()->setInt(name+"Hours",ct.hours); + Config::getInstance()->setInt(name+"Minutes",ct.minutes); + Config::getInstance()->setInt(name+"Seconds",ct.seconds); + return ct; +} + +Config* Config::instance = 0; + +Config::Config() { + configMap.clear(); + load(); + shuttingDown = 0; // Not shutting down +} + +void Config::load() { + std::string filecontent = sago::GetFileContent("configFile"); + std::stringstream inFile(filecontent); + std::string key; + std::string previuskey; + if (inFile) { + while (!inFile.eof()) { + inFile >> key; + if (key==previuskey) { //the last entry will be read 2 times if a linebreak is missing in the end + continue; + } + previuskey = key; + inFile.get(); //Read the space between the key and the content + std::string value; + std::getline(inFile, value); +#if DEBUG + std::cerr << "Config read: " << key << " with:\"" << value << "\"" << "\n"; +#endif + configMap[key] = value; + } + } +} + +Config* Config::getInstance() { + if (Config::instance==0) { + Config::instance = new Config(); + + } + return Config::instance; +} + +void Config::save() { + std::stringstream outFile; + std::map::iterator iter; + for (iter = configMap.begin(); iter != configMap.end(); ++iter) { + outFile << iter->first << " " << iter->second << "\n"; + } + outFile << "\n"; //The last entry in the file will be read double if a linebreak is missing + //This is checked on load too in case a user changes it himself. + sago::WriteFileContent("configFile", outFile.str()); +} + +bool Config::exists(const std::string& varName) const { + //Using that find returns an iterator to the end of the map if not found + return configMap.find(varName) != configMap.end(); +} + +void Config::setDefault(const std::string& varName,const std::string& content) { + if (exists(varName)) { + return; //Already exists do not change + } + setString(varName,content); +} + +void Config::setShuttingDown(long shuttingDown) { + this->shuttingDown = shuttingDown; +} + +long Config::isShuttingDown() const { + return shuttingDown; +} + +void Config::setString(const std::string& varName, const std::string& content) { + configMap[varName] = content; +} + +void Config::setInt(const std::string& varName, int content) { + configMap[varName] = std::to_string(content); +} + +void Config::setValue(const std::string& varName,double content) { + configMap[varName] = std::to_string(content); +} + +std::string Config::getString(const std::string& varName) { + if (exists(varName)) { + return configMap[varName]; + } + else { + return ""; + } +} + +int Config::getInt(const std::string& varName) { + if (exists(varName)) { + return str2int(configMap[varName]); + } + else { + return 0; + } +} + +double Config::getValue(const std::string& varName) { + if (exists(varName)) { + return str2double(configMap[varName]); + } + else { + return 0.0; + } +} diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..dc201f1 --- /dev/null +++ b/src/common.h @@ -0,0 +1,205 @@ +/* +=========================================================================== + * Saland Adventures +Copyright (C) 2014-2018 Poul Sander + +This program 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 2 of the License, or +(at your option) any later version. + +This program 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 this program. If not, see http://www.gnu.org/licenses/ + +Source information and contacts persons can be found at +https://github.com/sago007/saland +=========================================================================== +*/ + +/* + *This is the common.h + *It contains some basic functions that nearly all multi platform games are going + *to need. + */ + +#ifndef _COMMON_H +#define _COMMON_H + +#include +#include +#include +#include +#include +#include + + +#define _(String) gettext (String) + +struct commonTime +{ + unsigned int days = 0; + unsigned int hours = 0; + unsigned int minutes = 0; + unsigned int seconds = 0; +}; + +bool strequals(const char* a, const char* b); + +/** + * str2int parses a string and returns an int with the value of the string. + * if the string is not an int then 0 is returned instead of throwing an error + * in that way this function will always return a usable value. + */ +int str2int(const std::string &str2parse) __attribute__((const)); + +void dieOnNullptr(bool, const char* msg); + +/** + * str2double parses a string and returns a double with the value of the string. + * if the string is not a double then 0.0 is returned instead of throwing an error + * in that way this function will always return a usable value. + */ +double str2double(const std::string &str2parse) __attribute__((const)); + +/** + * Does the equivalent to snprintf but returns a C++ string + * @param fmt The format string + * @param ... Additional parameters for the place holders + * @return A string with the result + */ +std::string SPrintStringF(const char* fmt, ...) __attribute__ ((format (printf, 1, 2))); + +/** + * Prints to an internal C-buffer + * Because it uses an internal buffer the returned buffer is only valid until the next call + * The String is cut at 1024 chars (including the 0 terminator) + * @param fmt The format string + * @param ... Additional parameters for the place holders + * @return Pointer to an internal buffer + */ +const char* SPrintCF(const char* fmt, ...) __attribute__ ((format (printf, 1, 2))); + +class TimeHandler +{ +public: + static commonTime ms2ct(unsigned int milliseconds); + + static commonTime getTime(const std::string &name); + + static commonTime peekTime(const std::string &name, const commonTime &toAdd); + + static commonTime addTime(const std::string &name, const commonTime &toAdd); +}; + +#define MAX_VAR_LENGTH 1024 + +/*This is the Config class it is a map to hold config variables. + *It is inspired by Quake 3's CVAR system although a lot simpler. + *All variables have keys "varName" that is used to access a variable. + * + *This class is a singleton + */ +class Config +{ +private: + std::map configMap; + + static Config *instance; + + void load(); + + /* tells if the user has requested a shutdown */ + long shuttingDown = 0; + + Config(); + + +public: + /*Config is a singleton. + *It is accessed like this: + *Config::getInstance()->method2call(parameters); + */ + static Config* getInstance(); + + /*save() + *forces the config to be written to disk. This will also happened if the + *program terminates normally. + */ + void save(); + + /*getString(varName) + *Looks in the config file and returns the string that matches the key "varName" + *Returns an empty string if varName does not exist. + */ + std::string getString(const std::string &varName); + + /*getInt(varName) + *Looks in the config file and returns the int that matches the key "varName" + *Returns "0" if varName does not exist or cannot be parsed. + */ + int getInt(const std::string &varName); + + /*getValue(varName) + *Looks in the config file and returns the double that matches the key "varName" + *Returns "0.0" if varName does not exist or cannot be parsed. + */ + double getValue(const std::string &varName); + + /*setString(varName,content) + *Sets the config variable with key "varName" to the value of "content" + */ + void setString(const std::string &varName, const std::string &content); + + /*setInt(varName,content) + *Sets the config variable with key "varName" to the value of "content" + */ + void setInt(const std::string &varName, int content); + + /** + * Sets a config variable to a given (double) value + * @param varName Name of the variable to set + * @param content Value to give the variable + */ + void setValue(const std::string &varName,double content); + + /** + * returns true if the key varName exists. This is used the first time 1.4.0 + * starts so that it can see that it has to import configs from an earlier + * version. + * @param varName Name of the variable + * @return true if the variable exists + */ + bool exists(const std::string &varName) const; + + /*setDefault(varName,value) + *if the variable "varName" does not exist it will be created with value "value" + *if varName exists then this will have no effect + */ + /** + * Set default value for a variable. If the variable "varName" does not exist it will be created with value "value" + * if varName exists then this will have no effect + * @param varName Name of the variable + * @param content The default value + */ + void setDefault(const std::string &varName, const std::string &content); + + /** + * Should be set if the user has requested the program to shutdown. + * @param shuttingDown value of shutdown command. 5 = default = shutdown but allow saving + */ + void setShuttingDown(long shuttingDown = 5); + + /** + * tells if the user wants to shutdown. This can be used if the exit button is pressed deeply in the program. + * @return + */ + long isShuttingDown() const; +}; + +#endif /* _COMMON_H */ + diff --git a/src/os.cpp b/src/os.cpp new file mode 100644 index 0000000..4f6cff3 --- /dev/null +++ b/src/os.cpp @@ -0,0 +1,71 @@ +/* +=========================================================================== + * Saland Adventures +Copyright (C) 2014-2018 Poul Sander + +This program 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 2 of the License, or +(at your option) any later version. + +This program 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 this program. If not, see http://www.gnu.org/licenses/ + +Source information and contacts persons can be found at +https://github.com/sago007/saland +=========================================================================== +*/ + +#include "os.hpp" +#include +#include +#include "sago/platform_folders.h" + +static sago::PlatformFolders pf; + + +/* + *Files will be saved in: + * HOME/.local/share/"+GAMENAME (unix) + *or DOCUMENTS/My Games/GAMENAME (Windows) + */ +#define GAMENAME "saland_game" + +static std::string overrideSavePath = ""; + +/** + * Returns the path to where all settings must be saved. + * On unix-like systems this is the home-folder under: ~/.local/share/GAMENAME + * In Windows it is My Documents/My Games + * Consider changing this for Vista that has a special save games folder + */ +std::string getPathToSaveFiles() { + if (overrideSavePath.length() > 0) { + return overrideSavePath; + } + return pf.getSaveGamesFolder1()+"/"+GAMENAME; +} + +void setPathToSaveFiles(const std::string& path) { + overrideSavePath = path; +} + +void OsCreateSaveFolder() { +#if defined(__unix__) + std::string cmd = "mkdir -p '"+getPathToSaveFiles()+"/'"; + int retcode = system(cmd.c_str()); + if (retcode != 0) { + std::cerr << "Failed to create: " << getPathToSaveFiles()+"/" << "\n"; + } +#elif defined(_WIN32) + //Now for Windows NT/2k/xp/2k3 etc. + CreateDirectory(pf.getSaveGamesFolder1().c_str(), nullptr); + std::string tempA = getPathToSaveFiles(); + CreateDirectory(tempA.c_str(),nullptr); +#endif +} diff --git a/src/os.hpp b/src/os.hpp new file mode 100644 index 0000000..d1ea43c --- /dev/null +++ b/src/os.hpp @@ -0,0 +1,37 @@ +/* +=========================================================================== + * Saland Adventures +Copyright (C) 2014-2018 Poul Sander + +This program 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 2 of the License, or +(at your option) any later version. + +This program 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 this program. If not, see http://www.gnu.org/licenses/ + +Source information and contacts persons can be found at +https://github.com/sago007/saland +=========================================================================== +*/ + +#if defined(_WIN32) +#include "windows.h" +#include "shlobj.h" +#endif +#include + +std::string getPathToSaveFiles(); +#if defined(_WIN32) +std::string getMyDocumentsPath(); +#endif + +void setPathToSaveFiles(const std::string& path); + +void OsCreateSaveFolder(); diff --git a/src/saland.cpp b/src/saland.cpp index d67f314..434381a 100644 --- a/src/saland.cpp +++ b/src/saland.cpp @@ -1,7 +1,7 @@ /* =========================================================================== * Saland Adventures -Copyright (C) 2014-2017 Poul Sander +Copyright (C) 2014-2018 Poul Sander This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -45,10 +45,9 @@ https://github.com/sago007/saland #include "sagotmx/tmx_struct.h" #include "sago/SagoTextBox.hpp" -#if defined(_WIN32) -#include -#include -#endif +#include "common.h" +#include "os.hpp" + #ifndef VERSIONNUMBER #define VERSIONNUMBER "0.1.0" @@ -68,14 +67,12 @@ public: testBox.SetHolder(globalData.dataHolder); testBox.SetFontSize(16); } - + virtual bool IsActive() override { return isActive; } virtual void Draw(SDL_Renderer* target) override { - - textField.SetText("Saland Adventures - The game that has a very long subtitle to test the outline"); textField.SetOutline(3, SDL_Color{255,165,0,255}); textField.Draw(target, 10, 10); @@ -176,27 +173,6 @@ void runGame() { } -static sago::PlatformFolders pf; - -std::string getPathToSaveFiles() { - return pf.getSaveGamesFolder1()+"/"+GAMENAME; -} - -void OsCreateSaveFolder() { -#if defined(__unix__) - std::string cmd = "mkdir -p '"+getPathToSaveFiles()+"/'"; - int retcode = system(cmd.c_str()); - if (retcode != 0) { - std::cerr << "Failed to create: " << getPathToSaveFiles()+"/" << "\n"; - } -#elif defined(_WIN32) - //Now for Windows NT/2k/xp/2k3 etc. - std::string tempA = getPathToSaveFiles(); - CreateDirectory(tempA.c_str(),nullptr); -#endif -} - - int main(int argc, char* argv[]) { PHYSFS_init(argv[0]); PHYSFS_addToSearchPath((std::string(PHYSFS_getBaseDir())+"/data").c_str(), 1);