diff --git a/README.md b/README.md index e858baf..95c4609 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ make -j8 ./build/OpenGlassBox ``` +For Mac OS X users a bundle application is also created inside the build folder. + ## Notes concerning the portage Here are the current changes made from the original code source: diff --git a/demo/Demo.cpp b/demo/Demo.cpp index 1bb6659..dc0717d 100644 --- a/demo/Demo.cpp +++ b/demo/Demo.cpp @@ -13,10 +13,10 @@ #endif //------------------------------------------------------------------------------ -bool GlassBox::initSimulation() +bool GlassBox::initSimulation(std::string const& simfile) { m_simulation.setListener(*this); - if (!m_simulation.parse("data/Simulations/TestCity.txt")) + if (!m_simulation.parse(simfile)) return false; // --- Paris city diff --git a/demo/Display/DataPath.cpp b/demo/Display/DataPath.cpp new file mode 100644 index 0000000..84bd88d --- /dev/null +++ b/demo/Display/DataPath.cpp @@ -0,0 +1,249 @@ +//===================================================================== +// MyLogger: A basic logger. +// Copyright 2018 Quentin Quadrat +// +// This file is part of MyLogger. +// +// MyLogger 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. +// +// 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 MyLogger. If not, see . +//===================================================================== + +#include "Display/DataPath.hpp" +#include +#include + +#ifdef __APPLE__ +# include +#endif + +//------------------------------------------------------------------------------ +#ifdef __APPLE__ +std::string osx_get_resources_dir(std::string const& file) +{ + struct stat exists; // folder exists ? + std::string path; + + CFURLRef resourceURL = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle()); + char resourcePath[PATH_MAX]; + if (CFURLGetFileSystemRepresentation(resourceURL, true, + reinterpret_cast(resourcePath), + PATH_MAX)) + { + if (resourceURL != NULL) + { + CFRelease(resourceURL); + } + + path = std::string(resourcePath) + "/" + file; + if (stat(path.c_str(), &exists) == 0) + { + return path; + } + } + + path = "data/" + file; + if (stat(path.c_str(), &exists) == 0) + { + return path; + } + + return file; +} +#endif + +//------------------------------------------------------------------------------ +DataPath::DataPath(std::string const& path, char const delimiter) + : m_delimiter(delimiter) +{ + add(path); +} + +//------------------------------------------------------------------------------ +void DataPath::add(std::string const& path) +{ + if (!path.empty()) + { + split(path); + } +} + +//------------------------------------------------------------------------------ +void DataPath::reset(std::string const& path) +{ + m_search_paths.clear(); + split(path); +} + +//------------------------------------------------------------------------------ +void DataPath::clear() +{ + m_search_paths.clear(); +} + +//------------------------------------------------------------------------------ +void DataPath::remove(std::string const& path) +{ + m_search_paths.remove(path); +} + +//------------------------------------------------------------------------------ +bool DataPath::exist(std::string const& path) const +{ + struct stat buffer; + return stat(path.c_str(), &buffer) == 0; +} + +//------------------------------------------------------------------------------ +std::pair DataPath::find(std::string const& filename) const +{ + if (DataPath::exist(filename)) + return std::make_pair(filename, true); + + for (auto const& it: m_search_paths) + { + std::string file(it + filename); + if (DataPath::exist(file)) + return std::make_pair(file, true); + } + + // Not found + return std::make_pair(std::string(), false); +} + +//------------------------------------------------------------------------------ +std::string DataPath::expand(std::string const& filename) const +{ + for (auto const& it: m_search_paths) + { + std::string file(it + filename); + if (DataPath::exist(file)) + return file; + } + + return filename; +} + +//------------------------------------------------------------------------------ +bool DataPath::open(std::string& filename, std::ifstream& ifs, std::ios_base::openmode mode) const +{ + ifs.open(filename.c_str(), mode); + if (ifs) + return true; + + for (auto const& it: m_search_paths) + { + std::string file(it + filename); + ifs.open(file.c_str(), mode); + if (ifs) + { + filename = file; + return true; + } + } + + // Not found + return false; +} + +//------------------------------------------------------------------------------ +bool DataPath::open(std::string& filename, std::ofstream& ofs, std::ios_base::openmode mode) const +{ + ofs.open(filename.c_str(), mode); + if (ofs) + return true; + + for (auto const& it: m_search_paths) + { + std::string file(it + filename); + ofs.open(file.c_str(), mode); + if (ofs) + { + filename = file; + return true; + } + } + + // Not found + return false; +} + +//------------------------------------------------------------------------------ +bool DataPath::open(std::string& filename, std::fstream& fs, std::ios_base::openmode mode) const +{ + fs.open(filename.c_str(), mode); + if (fs) + return true; + + for (auto const& it: m_search_paths) + { + std::string file(it + filename); + fs.open(filename.c_str(), mode); + if (fs) + { + filename = file; + return true; + } + } + + // Not found + return false; +} + +//------------------------------------------------------------------------------ +std::vector DataPath::pathes() const +{ + std::vector res; + res.reserve(m_search_paths.size()); + for (auto const& it: m_search_paths) + { + res.push_back(it); + } + + return res; +} + +//------------------------------------------------------------------------------ +std::string DataPath::toString() const +{ + std::string string_path; + + string_path += "."; + string_path += m_delimiter; + + for (auto const& it: m_search_paths) + { + string_path += it; + string_path.pop_back(); // Remove the '/' char + string_path += m_delimiter; + } + + return string_path; +} + +//------------------------------------------------------------------------------ +void DataPath::split(std::string const& path) +{ + std::stringstream ss(path); + std::string directory; + + while (std::getline(ss, directory, m_delimiter)) + { + if (directory.empty()) + continue ; + + if ((*directory.rbegin() == '\\') || (*directory.rbegin() == '/')) + m_search_paths.push_back(directory); + else + m_search_paths.push_back(directory + "/"); + } +} diff --git a/demo/Display/DataPath.hpp b/demo/Display/DataPath.hpp new file mode 100644 index 0000000..71f4cfb --- /dev/null +++ b/demo/Display/DataPath.hpp @@ -0,0 +1,133 @@ +//===================================================================== +// MyLogger: A basic logger. +// Copyright 2018 Quentin Quadrat +// +// This file is part of MyLogger. +// +// MyLogger 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. +// +// 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 MyLogger. If not, see . +//===================================================================== + +#ifndef DATA_PATH_HPP +# define DATA_PATH_HPP + +# include "project_info.hpp" // generated by MyMakefile +# include +# include +# include +# include + +//------------------------------------------------------------------------------ +# if defined(__APPLE__) +std::string osx_get_resources_dir(std::string const& file); +# endif + +// ***************************************************************************** +//! \brief Class manipulating a set of paths for searching files in the same +//! idea of the Unix environment variable $PATH. Paths are separated by ':' and +//! search is made left to right. Ie. "/foo/bar:/usr/lib/" +// ***************************************************************************** +class DataPath +{ +public: + + //-------------------------------------------------------------------------- + //! \brief Constructor with a given path. Directories shall be separated + //! by the character given by the param delimiter. + //! Example: Path("/foo/bar:/usr/lib/", ':'). + //-------------------------------------------------------------------------- + DataPath(std::string const& path, char const delimiter = ':'); + + //-------------------------------------------------------------------------- + //! \brief Destructor. + //-------------------------------------------------------------------------- + ~DataPath() = default; + + //-------------------------------------------------------------------------- + //! \brief Append a new path. Directories are separated by the delimiter char + //! (by default ':'). Example: add("/foo/bar:/usr/lib/"). + //-------------------------------------------------------------------------- + void add(std::string const& path); + + //-------------------------------------------------------------------------- + //! \brief Replace the path state by a new one. Directories are separated by + //! the delimiter char (by default ':'). Example: reset("/foo/bar:/usr/lib/"). + //-------------------------------------------------------------------------- + void reset(std::string const& path); + + //-------------------------------------------------------------------------- + //! \brief Erase the path. + //-------------------------------------------------------------------------- + void clear(); + + //-------------------------------------------------------------------------- + //! \brief Erase the given directory from the path if found. + //-------------------------------------------------------------------------- + void remove(std::string const& path); + + //-------------------------------------------------------------------------- + //! \brief Find if a file exists in the search path. Note that you have to + //! check again the existence of this file when opening it (with functions + //! such as iofstream, fopen, open ...). Indeed the file may have been + //! suppress since this method have bee called. + //! + //! \return the full path (if found) and the existence of this path. + //! + //-------------------------------------------------------------------------- + std::pair find(std::string const& filename) const; + + //-------------------------------------------------------------------------- + //! \brief Return the full path for the file (if found) else return itself. + //! Beware of race condition: even if found the file may have suppress after + //! this function has been called. + //-------------------------------------------------------------------------- + std::string expand(std::string const& filename) const; + + //-------------------------------------------------------------------------- + //! \brief Return the container of path + //-------------------------------------------------------------------------- + std::vector pathes() const; + + //-------------------------------------------------------------------------- + //! \brief Return pathes as string. The first path is always ".:" + //-------------------------------------------------------------------------- + std::string toString() const; + + bool open(std::string& filename, std::ifstream& ifs, + std::ios_base::openmode mode = std::ios_base::in) const; + bool open(std::string& filename, std::ofstream& ifs, + std::ios_base::openmode mode = std::ios_base::out) const; + bool open(std::string& filename, std::fstream& ifs, + std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) const; +protected: + + //-------------------------------------------------------------------------- + //! \brief Slipt paths separated by delimiter char into std::list + //-------------------------------------------------------------------------- + void split(std::string const& path); + + //-------------------------------------------------------------------------- + //! \brief Return true if the path exists. be careful the file may not + //! exist after the function ends. + //-------------------------------------------------------------------------- + bool exist(std::string const& path) const; + +protected: + + //! \brief Path separator when several pathes are given as a single string. + const char m_delimiter = ':'; + //! \brief the list of pathes. + std::list m_search_paths; +}; + +#endif // UTILS_PATH_HPP diff --git a/demo/Makefile b/demo/Makefile index 5c373ba..6dbbf21 100644 --- a/demo/Makefile +++ b/demo/Makefile @@ -29,6 +29,7 @@ VPATH += $(P) Display # Project defines # DEFINES += -DVIRTUAL= -DDESIRED_GRID_SIZE=30u +DEFINES += -DDATADIR=\"$(DATADIR):$(abspath data)/:data/\" ################################################### # Set Libraries. For knowing which libraries @@ -41,10 +42,20 @@ PKG_LIBS = sdl2 SDL2_image # THIRDPART_LIBS += $(abspath $(P)/$(BUILD)/libopenglassbox.a) +################################################### +# MacOS X +# +ifeq ($(ARCHI),Darwin) +BUILD_MACOS_APP_BUNDLE = 1 +APPLE_IDENTIFIER = lecrapouille +MACOS_BUNDLE_ICON = data/$(PROJECT).icns +LINKER_FLAGS += -framework CoreFoundation +endif + ################################################### # Compile the demo as standalone application. # -OBJS += DearImGui.o SDLHelper.o Window.o +OBJS += DearImGui.o SDLHelper.o Window.o DataPath.o OBJS += Debug.o Draw.o Listeners.o Demo.o main.o ################################################### @@ -52,5 +63,8 @@ OBJS += Debug.o Draw.o Listeners.o Demo.o main.o # all: $(TARGET) @cp $(BUILD)/$(TARGET) ../$(BUILD) +ifeq ($(ARCHI),Darwin) + @cp -r $(BUILD)/$(TARGET)$(EXT) ../$(BUILD) +endif include $(M)/Makefile.footer diff --git a/data/Fonts/font.png b/demo/data/Fonts/font.png similarity index 100% rename from data/Fonts/font.png rename to demo/data/Fonts/font.png diff --git a/demo/data/OpenGlassBox.icns b/demo/data/OpenGlassBox.icns new file mode 100644 index 0000000..a8dcd59 Binary files /dev/null and b/demo/data/OpenGlassBox.icns differ diff --git a/data/Simulations/TestCity.txt b/demo/data/Simulations/TestCity.txt similarity index 100% rename from data/Simulations/TestCity.txt rename to demo/data/Simulations/TestCity.txt diff --git a/data/Simulations/TestCity1.txt b/demo/data/Simulations/TestCity1.txt similarity index 100% rename from data/Simulations/TestCity1.txt rename to demo/data/Simulations/TestCity1.txt diff --git a/demo/main.cpp b/demo/main.cpp index e8156d0..2cc959b 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -10,9 +10,21 @@ #include "Display/DearImGui.hpp" #include "OpenGlassBox/Config.hpp" +//------------------------------------------------------------------------------ +# ifndef DATADIR +# define GET_DATA_PATH project::info::data_path +# endif +# if defined(__APPLE__) +# define GET_DATA_PATH DATADIR":" + osx_get_resources_dir("") +# elif defined(__EMSCRIPTEN__) +# define GET_DATA_PATH "data/" +# else +# define GET_DATA_PATH DATADIR +# endif + //------------------------------------------------------------------------------ GlassBox::GlassBox() - : m_simulation(12u, 12u) + : m_path(GET_DATA_PATH), m_simulation(12u, 12u) {} //------------------------------------------------------------------------------ @@ -24,10 +36,10 @@ void GlassBox::onRelease(SDL_Renderer&) //------------------------------------------------------------------------------ bool GlassBox::setupGraphics(SDL_Renderer& renderer) { - const char *file = "data/Fonts/font.png"; - m_fontTexture = IMG_LoadTexture(&renderer, file); + std::string fontpath = m_path.expand("Fonts/font.png"); + m_fontTexture = IMG_LoadTexture(&renderer, fontpath.c_str()); if (m_fontTexture == nullptr) { - std::cerr << "Failed loading texture " << file << std::endl; + std::cerr << "Failed loading font " << fontpath << std::endl; return false; } @@ -39,7 +51,7 @@ bool GlassBox::setupGraphics(SDL_Renderer& renderer) //------------------------------------------------------------------------------ bool GlassBox::onInit(SDL_Renderer& renderer) { - if (!initSimulation()) + if (!initSimulation(m_path.expand("Simulations/TestCity.txt"))) return false; return setupGraphics(renderer); } diff --git a/demo/main.hpp b/demo/main.hpp index e189576..efb23b2 100644 --- a/demo/main.hpp +++ b/demo/main.hpp @@ -10,6 +10,7 @@ # include "Display/IGame.hpp" # include "Display/Window.hpp" +# include "Display/DataPath.hpp" # include "OpenGlassBox/Simulation.hpp" //============================================================================== @@ -26,7 +27,7 @@ class GlassBox: public IGame, private: - bool initSimulation(); + bool initSimulation(std::string const& simfile); bool setupGraphics(SDL_Renderer& renderer); void debugSimulation(); @@ -66,6 +67,7 @@ class GlassBox: public IGame, private: Window m_window; + DataPath m_path; Simulation m_simulation; SDL_Texture *m_fontTexture = nullptr; bool m_debug_activated = false;