Add Dockerfile + add cross build for WIn32 + Add create node

This commit is contained in:
Anthony 2023-12-26 17:13:53 +01:00
parent 5710356308
commit a25112d170
14 changed files with 663 additions and 335 deletions

28
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,28 @@
{
// Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
// Pointez pour afficher la description des attributs existants.
// Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug Story Editor (GDB)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/story-editor/build/story-editor", // Remplacez par le chemin de votre exécutable
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/story-editor",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

9
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,9 @@
{
"cmake.sourceDirectory": [
"${workspaceFolder}/story-editor",
"${workspaceFolder}/story-player",
"${workspaceFolder}/software"
]
}

Binary file not shown.

Binary file not shown.

View file

@ -9,19 +9,22 @@ set (CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Version du projet
set(PROJECT_VERSION_MAJOR 1)
set(PROJECT_VERSION_MINOR 0)
set(PROJECT_VERSION_PATCH 0)
# set(CMAKE_VERBOSE_MAKEFILE on)
if(POLICY CMP0072)
cmake_policy(SET CMP0072 NEW)
endif()
find_package(OpenGL REQUIRED)
set(IMGUI_VERSION 1.90)
include(FetchContent)
# =========================================================================================================================
# IMGUI and plugins
# =========================================================================================================================
@ -29,8 +32,8 @@ FetchContent_Declare(imgui
URL https://github.com/ocornut/imgui/archive/refs/tags/v${IMGUI_VERSION}-docking.zip
)
FetchContent_GetProperties(imgui)
if(NOT imgui_POPULATED)
set(FETCHCONTENT_QUIET NO)
FetchContent_Populate(imgui)
@ -42,11 +45,9 @@ add_compile_definitions(CUSTOM_IMGUIFILEDIALOG_CONFIG="${CMAKE_SOURCE_DIR}/src/C
add_compile_definitions(IMGUI_INCLUDE="imgui.h")
add_subdirectory(libs/ImGuiFileDialog)
# =========================================================================================================================
# SDL
# =========================================================================================================================
Set(FETCHCONTENT_QUIET FALSE)
FetchContent_Declare(
@ -60,10 +61,10 @@ FetchContent_Declare(
set(BUILD_SHARED_LIBS TRUE)
set(SDL_STATIC TRUE)
FetchContent_MakeAvailable(sdl2)
# add_subdirectory(libs/SDL)
# include_directories(libs/SDL/include)
# =========================================================================================================================
# SDL3-Image
# =========================================================================================================================
@ -81,8 +82,6 @@ FetchContent_MakeAvailable(sdl2)
# # END ADDITION
# FetchContent_MakeAvailable(SDL2_image)
set(SRCS
src/main.cpp
@ -147,7 +146,6 @@ set(SRCS
${imgui_SOURCE_DIR}/imgui_tables.cpp
${imgui_SOURCE_DIR}/imgui_draw.cpp
../software/chip32/chip32_assembler.cpp
../software/chip32/chip32_vm.c
../software/library/audio_player.cpp
@ -191,8 +189,6 @@ target_include_directories(${STORY_EDITOR_PROJECT} PUBLIC
../software/chip32/
)
add_definitions(-DIMGUI_USE_WCHAR32)
add_link_options(-static-libgcc -static-libstdc++)
@ -202,6 +198,8 @@ target_compile_definitions(${STORY_EDITOR_PROJECT} PUBLIC "$<$<CONFIG:DEBUG>:DEB
target_link_directories(${STORY_EDITOR_PROJECT} PUBLIC ${sdl2_BINARY_DIR})
message(${sdl2_BINARY_DIR})
set(SDL2_BIN_DIR ${sdl2_BINARY_DIR})
if(UNIX)
target_link_libraries(${STORY_EDITOR_PROJECT}
pthread
@ -211,17 +209,37 @@ target_link_libraries(${STORY_EDITOR_PROJECT}
SDL2
)
elseif(WIN32)
#target_compile_features("-Wl,-subsystem,windows")
target_link_libraries(${STORY_EDITOR_PROJECT}
OpenGL::GL
# SDL2::SDL2main
SDL2
ws2_32.lib psapi.lib setupapi.lib cfgmgr32.lib advapi32.lib Dbghelp.lib
ws2_32.lib psapi.lib setupapi.lib cfgmgr32.lib advapi32.lib
)
#set_target_properties(${STORY_EDITOR_PROJECT} PROPERTIES
#LINK_FLAGS /SUBSYSTEM:CONSOLE
#)
endif()
# =========================================================================================================================
# CPACK INSTALLER
# =========================================================================================================================
install(TARGETS ${STORY_EDITOR_PROJECT} RUNTIME DESTINATION ".")
# Personnaliser les options d'installation
set(CPACK_PACKAGE_NAME "Open-Story-Editor")
set(CPACK_PACKAGE_DESCRIPTION "Open Story Teller - Node based editor")
set(CPACK_PACKAGE_VENDOR "D8S")
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
# install(DIRECTORY "${PROJECT_SOURCE_DIR}/assets/" DESTINATION "assets")
install(DIRECTORY "${PROJECT_SOURCE_DIR}/fonts/" DESTINATION "fonts")
if(WIN32)
install_files("." FILES "${SDL2_BIN_DIR}/SDL2.dll")
endif()
# Personnaliser l'icône pour les installateurs Windows
if(WIN32)
set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/story-editor-logo.ico")
endif()
include(CPack)

16
story-editor/Dockerfile Normal file
View file

@ -0,0 +1,16 @@
FROM ubuntu:22.04
LABEL Description="Build environment"
ENV HOME /root
SHELL ["/bin/bash", "-c"]
RUN mkdir /workspace
RUN apt-get update && apt-get -y --no-install-recommends install \
build-essential \
cmake \
nsis \
mingw-w64 \
git \
wget

21
story-editor/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Anthony Rabine
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.

65
story-editor/README.md Normal file
View file

@ -0,0 +1,65 @@
# Story Editor
## How to generate a Windows executable and setup executable on Ubuntu
All the commands listed here are invoked from this `story-editor` root directory.
Make sure to have Docker installed:
```
sudo apt install docker.io
```
Build the Docker image that contains all the necessary development tools:
```
docker build -t cpp-dev .
```
Run it:
```
docker run -it -v $(pwd)/..:/workspace cpp-dev
```
Make sure to use the POSIX version of MinGW:
```
update-alternatives --config x86_64-w64-mingw32-g++
Selection Path Priority Status
------------------------------------------------------------
0 /usr/bin/x86_64-w64-mingw32-g++-win32 60 auto mode
* 1 /usr/bin/x86_64-w64-mingw32-g++-posix 30 manual mode
2 /usr/bin/x86_64-w64-mingw32-g++-win32 60 manual mode
update-alternatives --config x86_64-w64-mingw32-gcc
Selection Path Priority Status
------------------------------------------------------------
0 /usr/bin/x86_64-w64-mingw32-gcc-win32 60 auto mode
* 1 /usr/bin/x86_64-w64-mingw32-gcc-posix 30 manual mode
2 /usr/bin/x86_64-w64-mingw32-gcc-win32 60 manual mode
```
Cross build, first generate the Makefile:
```
git config --global http.sslverify false # avoid error during clone
cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw-w64-x86_64.cmake ..
```
Then build the executable and then the installer:
```
make -j4
make package
```

View file

@ -0,0 +1,25 @@
# Sample toolchain file for building for Windows from an Ubuntu Linux system.
#
# Typical usage:
# *) install cross compiler: `sudo apt-get install mingw-w64`
# *) cd build
# *) cmake -DCMAKE_TOOLCHAIN_FILE=~/mingw-w64-x86_64.cmake ..
# This is free and unencumbered software released into the public domain.
set(CMAKE_SYSTEM_NAME Windows)
set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)
# cross compilers to use for C, C++ and Fortran
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++)
set(CMAKE_Fortran_COMPILER ${TOOLCHAIN_PREFIX}-gfortran)
set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres)
# target environment on the build host system
set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX})
# modify default behavior of FIND_XXX() commands
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

View file

@ -1,5 +1,4 @@
#ifndef CONSOLEWINDOW_H
#define CONSOLEWINDOW_H
#pragma once
#include "gui.h"
#include <string>
@ -8,7 +7,6 @@
#include "window_base.h"
// Demonstrate creating a simple console window, with scrolling, filtering, completion and history.
// For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions.
struct ConsoleWindow : public WindowBase
@ -18,17 +16,48 @@ public:
~ConsoleWindow();
// Portable helpers
static int Stricmp(const char* s1, const char* s2) { int d; while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; }
static int Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; n--; } return d; }
static char* Strdup(const char* s) { IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = malloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); }
static void Strtrim(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; }
static int Stricmp(const char *s1, const char *s2)
{
int d;
while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1)
{
s1++;
s2++;
}
return d;
}
static int Strnicmp(const char *s1, const char *s2, int n)
{
int d = 0;
while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1)
{
s1++;
s2++;
n--;
}
return d;
}
static char *Strdup(const char *s)
{
IM_ASSERT(s);
size_t len = strlen(s) + 1;
void *buf = malloc(len);
IM_ASSERT(buf);
return (char *)memcpy(buf, (const void *)s, len);
}
static void Strtrim(char *s)
{
char *str_end = s + strlen(s);
while (str_end > s && str_end[-1] == ' ')
str_end--;
*str_end = 0;
}
void ClearLog();
void AddLog(const std::string &text, uint32_t type)
{
// FIXME-OPT
Entry e{text, type};
std::scoped_lock<std::mutex> mutex(mLogMutex);
@ -41,10 +70,9 @@ public:
virtual void Draw() override;
private:
struct Entry {
struct Entry
{
std::string text;
uint32_t type;
};
@ -56,7 +84,4 @@ private:
ImGuiTextFilter Filter;
bool AutoScroll;
bool ScrollToBottom;
};
#endif // CONSOLEWINDOW_H

View file

@ -334,7 +334,7 @@ void MainWindow::DrawMainMenuBar()
std::string home = pf::getUserHome() + "/";
#ifdef DEBUG
home = "/home/anthony/ostproj/ba869e4b-03d6-4249-9202-85b4cec767a7/";
home = "/mnt/work/git/open-stories/ba869e4b-03d6-4249-9202-85b4cec767a7/";
#endif
ImGuiFileDialog::Instance()->OpenDialog("OpenProjectDlgKey", "Choose File", ".json", home, 1, nullptr, ImGuiFileDialogFlags_Modal);

View file

@ -9,6 +9,13 @@
#include "media_node.h"
#include "gui.h"
#include <stdexcept> // for std::runtime_error
#define JSON_ASSERT(x) \
if (!(x)) { \
throw std::runtime_error("Assertion failed: " #x); \
}
#include "json.hpp"
NodeEditorWindow::NodeEditorWindow(IStoryManager &proj)
: WindowBase("Node editor")
@ -35,6 +42,7 @@ void NodeEditorWindow::Initialize()
void NodeEditorWindow::Clear()
{
m_nodes.clear();
m_ids.clear();
}
@ -57,6 +65,8 @@ void NodeEditorWindow::LoadNode(const nlohmann::json &nodeJson)
n->SetPosition(posJson["x"].get<float>(), posJson["y"].get<float>());
n->FromJson(internalDataJson);
m_ids.insert(restoredNodeId);
m_nodes.push_back(n);
}
else
@ -71,6 +81,18 @@ void NodeEditorWindow::LoadNode(const nlohmann::json &nodeJson)
}
int NodeEditorWindow::GenerateNodeId()
{
int max = 1;
if (m_ids.size() > 0)
{
auto max = *m_ids.rbegin();
max++;
m_ids.insert(max);
}
return max;
}
ed::PinId NodeEditorWindow::GetInputPin(unsigned long modelNodeId, int pinIndex)
{
@ -409,6 +431,41 @@ void NodeEditorWindow::Draw()
ed::EndDelete(); // Wrap up deletion action
auto openPopupPosition = ImGui::GetMousePos();
ed::Suspend();
if (ed::ShowBackgroundContextMenu())
{
ImGui::OpenPopup("Create New Node");
}
if (ImGui::BeginPopup("Create New Node"))
{
auto newNodePostion = openPopupPosition;
Node* node = nullptr;
if (ImGui::MenuItem("Media Node"))
{
auto n = createNode("media-node", "", m_story);
if (n)
{
n->SetType("media-node"); // FIXME: set type in createNode factory?
n->SetId(GenerateNodeId());
n->SetPosition(newNodePostion.x, newNodePostion.y);
m_nodes.push_back(n);
}
}
// if (node)
// {
// ed::SetNodePosition(node->ID, newNodePostion);
// }
ImGui::EndPopup();
}
ed::Resume();
ed::End();
ed::SetCurrentEditor(nullptr);

View file

@ -1,18 +1,16 @@
#pragma once
#include <vector>
#include <map>
#include <mutex>
#include <set>
#include <set>
#include <imgui_node_editor.h>
#include "base_node.h"
#include "window_base.h"
#include "i_story_manager.h"
#include "story_project.h"
#include "json.hpp"
namespace ed = ax::NodeEditor;
@ -72,6 +70,7 @@ private:
std::list<std::shared_ptr<LinkInfo>> m_links; // List of live links. It is dynamic unless you want to create read-only view over nodes.
void ToolbarUI();
std::set<int> m_ids;
void BuildNode(Node* node)
{
@ -117,5 +116,6 @@ private:
ed::PinId GetInputPin(unsigned long modelNodeId, int pinIndex);
ed::PinId GetOutputPin(unsigned long modelNodeId, int pinIndex);
uint32_t FindFirstNode() const;
int GenerateNodeId();
};

View file

@ -43,11 +43,13 @@ SOFTWARE.
* 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() {
static std::string getHome()
{
std::string res;
int uid = getuid();
const char *homeEnv = std::getenv("HOME");
if ( uid != 0 && homeEnv) {
if (uid != 0 && homeEnv)
{
// We only acknowlegde HOME if not root.
res = homeEnv;
return res;
@ -55,17 +57,20 @@ static std::string getHome() {
struct passwd *pw = nullptr;
struct passwd pwd;
long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (bufsize < 0) {
if (bufsize < 0)
{
bufsize = 16384;
}
std::vector<char> buffer;
buffer.resize(bufsize);
int error_code = getpwuid_r(uid, &pwd, buffer.data(), buffer.size(), &pw);
if (error_code) {
if (error_code)
{
throw std::runtime_error("Unable to get passwd struct.");
}
const char *tempRes = pw->pw_dir;
if (!tempRes) {
if (!tempRes)
{
throw std::runtime_error("User has no home directory");
}
res = tempRes;
@ -85,23 +90,30 @@ static std::string getHome() {
#include <winerror.h>
// For WideCharToMultiByte
#include <stringapiset.h>
#define _WIN32_WINNT 0x0600
// For SHGetFolderPathW and various CSIDL "magic numbers"
#include <shlobj.h>
namespace pf {
namespace internal {
namespace pf
{
namespace internal
{
std::string win32_utf16_to_utf8(const wchar_t* wstr) {
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, nullptr, 0, nullptr, nullptr);
if (actualSize > 0) {
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], static_cast<int>(buffer.size()), nullptr, nullptr);
res = buffer.data();
}
if (actualSize == 0) {
if (actualSize == 0)
{
// WideCharToMultiByte return 0 for errors.
throw std::runtime_error("UTF16 to UTF8 failed with error code: " + std::to_string(GetLastError()));
}
@ -111,36 +123,44 @@ std::string win32_utf16_to_utf8(const wchar_t* wstr) {
} // namesapce internal
} // namespace pf
class FreeCoTaskMemory {
class FreeCoTaskMemory
{
LPWSTR pointer = NULL;
public:
explicit FreeCoTaskMemory(LPWSTR pointer) : pointer(pointer){};
~FreeCoTaskMemory() {
~FreeCoTaskMemory()
{
CoTaskMemFree(pointer);
}
};
static std::string GetKnownWindowsFolder(REFKNOWNFOLDERID folderId, const char* errorMsg) {
static std::string GetKnownWindowsFolder(REFKNOWNFOLDERID folderId, const char *errorMsg)
{
LPWSTR wszPath = NULL;
HRESULT hr;
hr = SHGetKnownFolderPath(folderId, KF_FLAG_CREATE, NULL, &wszPath);
FreeCoTaskMemory scopeBoundMemory(wszPath);
if (!SUCCEEDED(hr)) {
if (!SUCCEEDED(hr))
{
throw std::runtime_error(errorMsg);
}
return pf::internal::win32_utf16_to_utf8(wszPath);
}
static std::string GetAppData() {
static std::string GetAppData()
{
return GetKnownWindowsFolder(FOLDERID_RoamingAppData, "RoamingAppData could not be found");
}
static std::string GetAppDataCommon() {
static std::string GetAppDataCommon()
{
return GetKnownWindowsFolder(FOLDERID_ProgramData, "ProgramData could not be found");
}
static std::string GetAppDataLocal() {
static std::string GetAppDataLocal()
{
return GetKnownWindowsFolder(FOLDERID_LocalAppData, "LocalAppData could not be found");
}
#elif defined(__APPLE__)
@ -153,20 +173,22 @@ static std::string GetAppDataLocal() {
#include <sstream>
// 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] != '/') {
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);
}
}
static std::string getLinuxFolderDefault(const char* envName, const char* defaultRelativePath) {
static std::string getLinuxFolderDefault(const char *envName, const char *defaultRelativePath)
{
std::string res;
const char *tempRes = std::getenv(envName);
if (tempRes) {
if (tempRes)
{
throwOnRelative(envName, tempRes);
res = tempRes;
return res;
@ -175,9 +197,11 @@ static std::string getLinuxFolderDefault(const char* envName, const char* defaul
return res;
}
static void appendExtraFolders(const char* envName, const char* defaultValue, std::vector<std::string>& folders) {
static void appendExtraFolders(const char *envName, const char *defaultValue, std::vector<std::string> &folders)
{
const char *envValue = std::getenv(envName);
if (!envValue) {
if (!envValue)
{
envValue = defaultValue;
}
pf::internal::appendExtraFoldersTokenizer(envName, envValue, folders);
@ -185,19 +209,24 @@ static void appendExtraFolders(const char* envName, const char* defaultValue, st
#endif
namespace pf {
namespace pf
{
#if !defined(_WIN32) && !defined(__APPLE__)
namespace internal {
void appendExtraFoldersTokenizer(const char* envName, const char* envValue, std::vector<std::string>& folders) {
namespace internal
{
void appendExtraFoldersTokenizer(const char *envName, const char *envValue, std::vector<std::string> &folders)
{
std::stringstream ss(envValue);
std::string value;
while (std::getline(ss, value, ':')) {
if (value[0] == '/') {
while (std::getline(ss, value, ':'))
{
if (value[0] == '/')
{
folders.push_back(value);
}
else {
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 \"" << value << "\" in \"" << envName << "\" because it does not start with a \"/\"\n";
@ -207,7 +236,8 @@ void appendExtraFoldersTokenizer(const char* envName, const char* envValue, std:
}
#endif
std::string getDataHome() {
std::string getDataHome()
{
#ifdef _WIN32
return GetAppData();
#elif defined(__APPLE__)
@ -217,7 +247,8 @@ std::string getDataHome() {
#endif
}
std::string getConfigHome() {
std::string getConfigHome()
{
#ifdef _WIN32
return GetAppData();
#elif defined(__APPLE__)
@ -227,7 +258,8 @@ std::string getConfigHome() {
#endif
}
std::string getCacheDir() {
std::string getCacheDir()
{
#ifdef _WIN32
return GetAppDataLocal();
#elif defined(__APPLE__)
@ -246,8 +278,8 @@ std::string getUserHome()
#endif
}
std::string getStateDir() {
std::string getStateDir()
{
#ifdef _WIN32
return GetAppDataLocal();
#elif defined(__APPLE__)
@ -257,7 +289,8 @@ std::string getStateDir() {
#endif
}
void appendAdditionalDataDirectories(std::vector<std::string>& homes) {
void appendAdditionalDataDirectories(std::vector<std::string> &homes)
{
#ifdef _WIN32
homes.push_back(GetAppDataCommon());
#elif !defined(__APPLE__)
@ -265,7 +298,8 @@ void appendAdditionalDataDirectories(std::vector<std::string>& homes) {
#endif
}
void appendAdditionalConfigDirectories(std::vector<std::string>& homes) {
void appendAdditionalConfigDirectories(std::vector<std::string> &homes)
{
#ifdef _WIN32
homes.push_back(GetAppDataCommon());
#elif !defined(__APPLE__)
@ -274,18 +308,23 @@ void appendAdditionalConfigDirectories(std::vector<std::string>& homes) {
}
#if !defined(_WIN32) && !defined(__APPLE__)
struct PlatformFolders::PlatformFoldersData {
struct PlatformFolders::PlatformFoldersData
{
std::map<std::string, std::string> folders;
};
static void PlatformFoldersAddFromFile(const std::string& filename, 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) {
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 {
try
{
std::size_t splitPos = line.find('=');
std::string key = line.substr(0, splitPos);
std::size_t valueStart = line.find('"', splitPos);
@ -293,14 +332,16 @@ static void PlatformFoldersAddFromFile(const std::string& filename, std::map<std
std::string value = line.substr(valueStart + 1, valueEnd - valueStart - 1);
folders[key] = value;
}
catch (std::exception& e) {
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) {
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";
@ -310,35 +351,42 @@ static void PlatformFoldersFillData(std::map<std::string, std::string>& folders)
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 ) {
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) {
if (value.compare(0, 5, "$HOME") == 0)
{
value = getHome() + value.substr(5, std::string::npos);
}
}
}
#endif
PlatformFolders::PlatformFolders() {
PlatformFolders::PlatformFolders()
{
#if !defined(_WIN32) && !defined(__APPLE__)
this->data = new PlatformFolders::PlatformFoldersData();
try {
try
{
PlatformFoldersFillData(data->folders);
}
catch (...) {
catch (...)
{
delete this->data;
throw;
}
#endif
}
PlatformFolders::~PlatformFolders() {
PlatformFolders::~PlatformFolders()
{
#if !defined(_WIN32) && !defined(__APPLE__)
delete this->data;
#endif
}
std::string PlatformFolders::getDocumentsFolder() const {
std::string PlatformFolders::getDocumentsFolder() const
{
#ifdef _WIN32
return GetKnownWindowsFolder(FOLDERID_Documents, "Failed to find My Documents folder");
#elif defined(__APPLE__)
@ -348,7 +396,8 @@ std::string PlatformFolders::getDocumentsFolder() const {
#endif
}
std::string PlatformFolders::getDesktopFolder() const {
std::string PlatformFolders::getDesktopFolder() const
{
#ifdef _WIN32
return GetKnownWindowsFolder(FOLDERID_Desktop, "Failed to find Desktop folder");
#elif defined(__APPLE__)
@ -358,7 +407,8 @@ std::string PlatformFolders::getDesktopFolder() const {
#endif
}
std::string PlatformFolders::getPicturesFolder() const {
std::string PlatformFolders::getPicturesFolder() const
{
#ifdef _WIN32
return GetKnownWindowsFolder(FOLDERID_Pictures, "Failed to find My Pictures folder");
#elif defined(__APPLE__)
@ -368,7 +418,8 @@ std::string PlatformFolders::getPicturesFolder() const {
#endif
}
std::string PlatformFolders::getPublicFolder() const {
std::string PlatformFolders::getPublicFolder() const
{
#ifdef _WIN32
return GetKnownWindowsFolder(FOLDERID_Public, "Failed to find the Public folder");
#elif defined(__APPLE__)
@ -378,7 +429,8 @@ std::string PlatformFolders::getPublicFolder() const {
#endif
}
std::string PlatformFolders::getDownloadFolder1() const {
std::string PlatformFolders::getDownloadFolder1() const
{
#ifdef _WIN32
return GetKnownWindowsFolder(FOLDERID_Downloads, "Failed to find My Downloads folder");
#elif defined(__APPLE__)
@ -388,7 +440,8 @@ std::string PlatformFolders::getDownloadFolder1() const {
#endif
}
std::string PlatformFolders::getMusicFolder() const {
std::string PlatformFolders::getMusicFolder() const
{
#ifdef _WIN32
return GetKnownWindowsFolder(FOLDERID_Music, "Failed to find My Music folder");
#elif defined(__APPLE__)
@ -398,7 +451,8 @@ std::string PlatformFolders::getMusicFolder() const {
#endif
}
std::string PlatformFolders::getVideoFolder() const {
std::string PlatformFolders::getVideoFolder() const
{
#ifdef _WIN32
return GetKnownWindowsFolder(FOLDERID_Videos, "Failed to find My Video folder");
#elif defined(__APPLE__)
@ -408,7 +462,8 @@ std::string PlatformFolders::getVideoFolder() const {
#endif
}
std::string PlatformFolders::getSaveGamesFolder1() const {
std::string PlatformFolders::getSaveGamesFolder1() const
{
#ifdef _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
@ -420,43 +475,53 @@ std::string PlatformFolders::getSaveGamesFolder1() const {
#endif
}
std::string getDesktopFolder() {
std::string getDesktopFolder()
{
return PlatformFolders().getDesktopFolder();
}
std::string getDocumentsFolder() {
std::string getDocumentsFolder()
{
return PlatformFolders().getDocumentsFolder();
}
std::string getDownloadFolder() {
std::string getDownloadFolder()
{
return PlatformFolders().getDownloadFolder1();
}
std::string getDownloadFolder1() {
std::string getDownloadFolder1()
{
return getDownloadFolder();
}
std::string getPicturesFolder() {
std::string getPicturesFolder()
{
return PlatformFolders().getPicturesFolder();
}
std::string getPublicFolder() {
std::string getPublicFolder()
{
return PlatformFolders().getPublicFolder();
}
std::string getMusicFolder() {
std::string getMusicFolder()
{
return PlatformFolders().getMusicFolder();
}
std::string getVideoFolder() {
std::string getVideoFolder()
{
return PlatformFolders().getVideoFolder();
}
std::string getSaveGamesFolder1() {
std::string getSaveGamesFolder1()
{
return PlatformFolders().getSaveGamesFolder1();
}
std::string getSaveGamesFolder2() {
std::string getSaveGamesFolder2()
{
#ifdef _WIN32
return GetKnownWindowsFolder(FOLDERID_SavedGames, "Failed to find Saved Games folder");
#else
@ -464,5 +529,4 @@ std::string getSaveGamesFolder2() {
#endif
}
} // namespace pf