diff --git a/CMakeLists.txt b/CMakeLists.txt
index b46d33d..f42b561 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,9 +1,20 @@
+# Prevent in-source builds
+if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
+ message(FATAL_ERROR "In-source builds prohibited. Call cmake from the build directory.")
+endif()
+
cmake_minimum_required(VERSION 3.7)
# Set compiler flags
-set(CMAKE_CXX_FLAGS "-Wall -Wextra")
-set(CMAKE_CXX_FLAGS_DEBUG "-g -O3 -DDEBUG")
-set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
+if(CMAKE_COMPILER_IS_GNUCC)
+ set(CMAKE_CXX_FLAGS "-Wall -Wextra")
+ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -g")
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -O3")
+elseif(MSVC)
+ set(CMAKE_CXX_FLAGS "/W3 /MP /MD")
+ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS}")
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} /Ox")
+endif()
# Include project macro
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/project.cmake)
@@ -30,6 +41,12 @@ set(EXECUTABLE_TARGET ${PROJECT_NAME}-executable)
add_executable(${EXECUTABLE_TARGET} ${SOURCE_FILES})
set_target_properties(${EXECUTABLE_TARGET} PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
+# Set link flags to show console window on debug builds and hide it on release builds
+if(MSVC)
+ set_target_properties(${EXECUTABLE_TARGET} PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
+ set_target_properties(${EXECUTABLE_TARGET} PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:\"mainCRTStartup\"")
+endif(MSVC)
+
# Set include directories
target_include_directories(${EXECUTABLE_TARGET}
PUBLIC
diff --git a/build/.gitignore b/build/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/build/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/src/entity/systems/sound-system.cpp b/src/entity/systems/sound-system.cpp
index e109c65..5b4f43e 100644
--- a/src/entity/systems/sound-system.cpp
+++ b/src/entity/systems/sound-system.cpp
@@ -55,7 +55,7 @@ SoundSystem::SoundSystem(ComponentManager* componentManager):
// Load wav file
{
- const char* filename = "/home/cjhoward/projects/antkeeper/modules/antkeeper-data/sounds/shutter.wav";
+ const char* filename = "shutter.wav";
unsigned int channels;
unsigned int sampleRate;
drwav_uint64 frameCount;
diff --git a/src/filesystem.cpp b/src/filesystem.cpp
new file mode 100644
index 0000000..f2e93ee
--- /dev/null
+++ b/src/filesystem.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2017-2019 Christopher J. Howard
+ *
+ * This file is part of Antkeeper Source Code.
+ *
+ * Antkeeper Source Code 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.
+ *
+ * Antkeeper Source Code 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 Antkeeper Source Code. If not, see .
+ */
+
+#include "filesystem.hpp"
+#include
+#include
+#include
+#include
+
+#if defined(_WIN32)
+ #include
+ #include
+#else
+ #include
+ #include
+ #include
+ #include
+#endif
+
+#if defined(_WIN32)
+ std::string narrow(const std::wstring& wstring)
+ {
+ std::string string(WideCharToMultiByte(CP_UTF8, 0, &wstring[0], static_cast(wstring.size()), nullptr, 0, nullptr, nullptr), '\0');
+ WideCharToMultiByte(CP_UTF8, 0, &wstring[0], static_cast(wstring.size()), &string[0], static_cast(string.size()), nullptr, nullptr);
+ return string;
+ }
+
+ std::wstring widen(const std::string& string)
+ {
+ std::wstring wstring(MultiByteToWideChar(CP_UTF8, 0, &string[0], static_cast(string.size()), nullptr, 0), L'\0');
+ MultiByteToWideChar(CP_UTF8, 0, &string[0], static_cast(string.size()), &wstring[0], static_cast(wstring.size()));
+ return wstring;
+ }
+#endif
+
+std::string getExecutablePath()
+{
+ std::string executablePath;
+
+ #if defined(_WIN32)
+ // Get executable path on Windows
+ HMODULE hModule = GetModuleHandleW(nullptr);
+ std::wstring wpath(MAX_PATH, L'\0');
+ GetModuleFileNameW(hModule, &wpath[0], MAX_PATH);
+ wpath.erase(std::find(wpath.begin(), wpath.end(), L'\0'), wpath.end());
+ executablePath = narrow(wpath);
+ #else
+ // Get executable path on Linux
+ char path[PATH_MAX];
+ ssize_t length = ::readlink("/proc/self/exe", path, sizeof(path) - 1);
+ if (length != -1)
+ {
+ path[length] = '\0';
+ executablePath = path;
+ }
+ #endif
+
+ return executablePath;
+}
+
+std::string getDataPath(const std::string& applicationName)
+{
+ std::string dataPath;
+
+ #if defined(_WIN32)
+ std::string executablePath = getExecutablePath();
+ std::size_t delimeter = executablePath.find_last_of("\\/") + 1;
+ dataPath = executablePath.substr(0, delimeter);
+ #else
+ std::string executablePath = getExecutablePath();
+ std::size_t delimeter = executablePath.find_last_of("\\/") + 1;
+ dataPath = executablePath.substr(0, delimeter) + std::string("../share/") + applicationName + std::string("/");
+ #endif
+
+ return dataPath;
+}
+
+std::string getConfigPath(const std::string& applicationName)
+{
+ std::string configPath;
+
+ #if defined(_WIN32)
+ std::wstring wpath(MAX_PATH, L'\0');
+ if (SHGetSpecialFolderPathW(nullptr, &wpath[0], CSIDL_LOCAL_APPDATA, FALSE))
+ {
+ wpath.erase(std::find(wpath.begin(), wpath.end(), L'\0'), wpath.end());
+ configPath = narrow(wpath);
+ configPath += std::string("\\") + applicationName + std::string("\\");
+ }
+ #else
+ // Determine home path
+ std::string homePath = std::string(getpwuid(getuid())->pw_dir);
+
+ // Determine config path
+ char* xdgConfigHome = std::getenv("XDG_CONFIG_HOME");
+ if (!xdgConfigHome)
+ {
+ // Default to $HOME/.config/ as per:
+ // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
+ configPath = homePath + std::string("/.config/") + applicationName + std::string("/");
+ }
+ else
+ {
+ configPath = xdgConfigHome + std::string("/") + applicationName + std::string("/");
+ }
+ #endif
+
+ return configPath;
+}
+
+bool pathExists(const std::string& path)
+{
+ #if defined(_WIN32)
+ std::wstring wpath = widen(path);
+ DWORD attributes = GetFileAttributesW(wpath.c_str());
+ return (attributes != INVALID_FILE_ATTRIBUTES);
+ #else
+ struct stat info;
+ return (stat(path.c_str(), &info) == 0);
+ #endif
+}
+
+bool createDirectory(const std::string& path)
+{
+ #if defined(_WIN32)
+ std::wstring wpath = widen(path);
+ return (CreateDirectoryW(wpath.c_str(), nullptr) != 0);
+ #else
+ return (mkdir(path.c_str(), 0777) == 0);
+ #endif
+}
diff --git a/src/filesystem.hpp b/src/filesystem.hpp
new file mode 100644
index 0000000..1d420f1
--- /dev/null
+++ b/src/filesystem.hpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017-2019 Christopher J. Howard
+ *
+ * This file is part of Antkeeper Source Code.
+ *
+ * Antkeeper Source Code 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.
+ *
+ * Antkeeper Source Code 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 Antkeeper Source Code. If not, see .
+ */
+
+#ifndef FILESYSTEM_HPP
+#define FILESYSTEM_HPP
+
+#include
+
+/**
+ * Returns the absolute path to the current executable.
+ *
+ * @return Path to the application's executable.
+ */
+std::string getExecutablePath();
+
+/**
+ * Returns the absolute path to the directory containing application data.
+ *
+ * Windows: executableDirectory
+ * GNU/Linux: executableDirectory/../share/applicationName/
+ *
+ * @param applicationName Name of the application.
+ * @return Path to the application's data directory.
+ */
+std::string getDataPath(const std::string& applicationName);
+
+/**
+ * Returns the absolute path to the directory containing user-specific application data.
+ *
+ * Windows: %LOCALAPPDATA%\applicationName\
+ * GNU/Linux: $XDG_CONFIG_HOME/applicationName/ or ~/.config/applicationName/ if $XDG_CONFIG_HOME is not set.
+ *
+ * @param applicationName Name of the application.
+ * @return Path to the application's config directory.
+ */
+std::string getConfigPath(const std::string& applicationName);
+
+/// Checks if a file or directory exists
+bool pathExists(const std::string& path);
+
+/// Creates a directory
+bool createDirectory(const std::string& path);
+
+#endif // FILESYSTEM_HPP
+
diff --git a/src/game.cpp b/src/game.cpp
index c2f56bb..b3a557b 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -22,7 +22,8 @@
#include "states/game-state.hpp"
#include "states/splash-state.hpp"
#include "states/sandbox-state.hpp"
-#include "paths.hpp"
+#include "filesystem.hpp"
+#include "timestamp.hpp"
#include "ui/ui.hpp"
#include "graphics/ui-render-pass.hpp"
#include "graphics/shadow-map-render-pass.hpp"
@@ -57,9 +58,7 @@
#include "stb/stb_image_write.h"
#include
#include
-#include
#include
-#include
#include
#include
#include
@@ -160,10 +159,21 @@ Game::Game(int argc, char* argv[]):
currentState(nullptr),
window(nullptr)
{
+ // Determine application name
+ std::string applicationName;
+ #if defined(_WIN32)
+ applicationName = "Antkeeper";
+ #else
+ applicationName = "antkeeper";
+ #endif
+
// Form resource paths
- dataPath = getDataPath();
- configPath = getConfigPath();
+ dataPath = getDataPath(applicationName);
+ configPath = getConfigPath(applicationName);
controlsPath = configPath + "/controls/";
+
+ std::cout << "Data path: " << dataPath << std::endl;
+ std::cout << "Config path: " << configPath << std::endl;
// Create nonexistent config directories
std::vector configPaths;
@@ -773,8 +783,7 @@ void Game::setupUI()
// Character set test
std::set charset;
- charset.emplace(U'方');
- charset.emplace(U'蕴');
+ charset.emplace(U'A');
labelTypeface->loadCharset(labelFont, UnicodeRange::BASIC_LATIN);
labelTypeface->loadCharset(labelFont, charset);
@@ -2064,11 +2073,6 @@ void Game::screenshot()
// Convert title to lowercase
std::transform(title.begin(), title.end(), title.begin(), ::tolower);
- // Get system time
- auto now = std::chrono::system_clock::now();
- std::time_t tt = std::chrono::system_clock::to_time_t(now);
- std::size_t ms = (std::chrono::duration_cast(now.time_since_epoch()) % 1000).count();
-
// Create screenshot directory if it doesn't exist
std::string screenshotDirectory = configPath + std::string("/screenshots/");
if (!pathExists(screenshotDirectory))
@@ -2077,13 +2081,7 @@ void Game::screenshot()
}
// Build screenshot file name
- std::stringstream stream;
- stream << screenshotDirectory;
- stream << title;
- stream << std::put_time(std::localtime(&tt), "-%Y%m%d-%H%M%S-");
- stream << std::setfill('0') << std::setw(3) << ms;
- stream << ".png";
- std::string filename = stream.str();
+ std::string filename = screenshotDirectory + title + "-" + timestamp() + ".png";
// Write screenshot to file in separate thread
std::thread screenshotThread(Game::saveScreenshot, filename, w, h, pixels);
diff --git a/src/paths.cpp b/src/paths.cpp
deleted file mode 100644
index ce6b646..0000000
--- a/src/paths.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2017-2019 Christopher J. Howard
- *
- * This file is part of Antkeeper Source Code.
- *
- * Antkeeper Source Code 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.
- *
- * Antkeeper Source Code 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 Antkeeper Source Code. If not, see .
- */
-
-#include "paths.hpp"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-static std::string getExecutablePath()
-{
- char path[PATH_MAX];
- ssize_t length = ::readlink("/proc/self/exe", path, sizeof(path) - 1);
- if (length != -1)
- {
- path[length] = '\0';
- return std::string(path);
- }
-
- return std::string();
-}
-
-std::string getDataPath()
-{
- std::string executablePath = getExecutablePath();
- std::size_t delimeter = executablePath.find_last_of("\\/") + 1;
- std::string executableName = executablePath.substr(delimeter, executablePath.size() - delimeter + 1);
- return executablePath.substr(0, delimeter) + std::string("../share/") + executableName + std::string("/");
-}
-
-std::string getConfigPath()
-{
- std::string executablePath = getExecutablePath();
- std::size_t delimeter = executablePath.find_last_of("\\/") + 1;
- std::string executableName = executablePath.substr(delimeter, executablePath.size() - delimeter + 1);
- std::string configPath;
-
- // Determine home path
- std::string homePath = std::string(getpwuid(getuid())->pw_dir);
-
- // Determine config path
- char* xdgConfigHome = std::getenv("XDG_CONFIG_HOME");
- if (!xdgConfigHome)
- {
- // Default to $HOME/.config/ as per:
- // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
- configPath = homePath + std::string("/.config/") + executableName + std::string("/");
- }
- else
- {
- configPath = xdgConfigHome + std::string("/") + executableName + std::string("/");
- }
-
- return configPath;
-}
-
-bool pathExists(const std::string& path)
-{
- struct stat info;
- return (stat(path.c_str(), &info) == 0);
-}
-
-bool createDirectory(const std::string& path)
-{
- int error = 0;
- #if defined(_WIN32)
- error = _mkdir(path.c_str());
- #else
- error = mkdir(path.c_str(), 0777);
- #endif
-
- return (error == 0);
-}
-
diff --git a/src/timestamp.cpp b/src/timestamp.cpp
new file mode 100644
index 0000000..f7b4e87
--- /dev/null
+++ b/src/timestamp.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017-2019 Christopher J. Howard
+ *
+ * This file is part of Antkeeper Source Code.
+ *
+ * Antkeeper Source Code 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.
+ *
+ * Antkeeper Source Code 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 Antkeeper Source Code. If not, see .
+ */
+
+#include "timestamp.hpp"
+#include
+#include
+#include
+#include
+
+std::string timestamp()
+{
+ auto now = std::chrono::system_clock::now();
+ std::time_t tt = std::chrono::system_clock::to_time_t(now);
+ std::size_t ms = (std::chrono::duration_cast(now.time_since_epoch()) % 1000).count();
+ struct std::tm timeinfo;
+ localtime_s(&timeinfo, &tt);
+
+ std::stringstream stream;
+ stream << std::put_time(&timeinfo, "%Y%m%d-%H%M%S-");
+ stream << std::setfill('0') << std::setw(3) << ms;
+
+ return stream.str();
+}
\ No newline at end of file
diff --git a/src/paths.hpp b/src/timestamp.hpp
similarity index 68%
rename from src/paths.hpp
rename to src/timestamp.hpp
index cc9d805..0998bbb 100644
--- a/src/paths.hpp
+++ b/src/timestamp.hpp
@@ -17,22 +17,14 @@
* along with Antkeeper Source Code. If not, see .
*/
-#ifndef PATHS_HPP
-#define PATHS_HPP
+#ifndef TIMESTAMP_HPP
+#define TIMESTAMP_HPP
#include
-/// Returns the path to the executable's data
-std::string getDataPath();
-
-/// Returns the path to the executable's config files
-std::string getConfigPath();
-
-/// Checks if a file or directory exists
-bool pathExists(const std::string& path);
-
-/// Creates a directory
-bool createDirectory(const std::string& path);
-
-#endif // PATHS_HPP
+/**
+ * Returns a string containing the current time, formatted as "YYYYMMDD-HHMMSS-mmm".
+ */
+std::string timestamp();
+#endif // TIMESTAMP_HPP