From e2ac176fba6eae389b0a560c6055c7f1cdc812f5 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Tue, 12 Mar 2019 05:04:17 +0800 Subject: [PATCH] Fix MSVC build --- CMakeLists.txt | 23 ++++- build/.gitignore | 2 + src/entity/systems/sound-system.cpp | 2 +- src/filesystem.cpp | 147 ++++++++++++++++++++++++++++ src/filesystem.hpp | 61 ++++++++++++ src/game.cpp | 36 ++++--- src/paths.cpp | 95 ------------------ src/timestamp.cpp | 39 ++++++++ src/{paths.hpp => timestamp.hpp} | 22 ++--- 9 files changed, 294 insertions(+), 133 deletions(-) create mode 100644 build/.gitignore create mode 100644 src/filesystem.cpp create mode 100644 src/filesystem.hpp delete mode 100644 src/paths.cpp create mode 100644 src/timestamp.cpp rename src/{paths.hpp => timestamp.hpp} (68%) 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