From 781b4a49713fa73226f3c9b1b726f56ac0e0d9d8 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Tue, 5 Mar 2019 23:41:04 +0800 Subject: [PATCH] Add support for saving resources and added the CSV table saving function --- src/game.cpp | 190 +++++++++++++++++++++++++++-- src/resources/csv-table-loader.cpp | 26 ++++ src/resources/csv-table.hpp | 3 +- src/resources/resource-loader.hpp | 18 ++- src/resources/resource-manager.hpp | 41 ++++++- 5 files changed, 266 insertions(+), 12 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index d5c6ec0..9d0cd7f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -460,7 +460,7 @@ void Game::render() void Game::exit() { - + saveControlProfile(); } void Game::handleEvent(const WindowResizedEvent& event) @@ -1412,13 +1412,6 @@ void Game::loadStrings() void Game::loadControlProfile() { - // Create control directory if it doesn't exist - std::string controlsPath = getConfigPath() + "/controls/"; - if (!pathExists(controlsPath)) - { - createDirectory(controlsPath); - } - // Load control profile std::string controlProfilePath = "/controls/" + controlProfileName + ".csv"; CSVTable* controlProfile = resourceManager->load(controlProfilePath); @@ -1599,6 +1592,7 @@ void Game::loadControlProfile() } // Map controls + /* inputMapper->map(&exitControl, keyboard, Scancode::ESCAPE); inputMapper->map(&toggleFullscreenControl, keyboard, Scancode::F11); inputMapper->map(&screenshotControl, keyboard, Scancode::F12); @@ -1617,10 +1611,188 @@ void Game::loadControlProfile() inputMapper->map(&dragCameraControl, mouse, 3); inputMapper->map(&toggleWireframeControl, keyboard, Scancode::V); inputMapper->map(&toggleEditModeControl, keyboard, Scancode::TAB); + */ } void Game::saveControlProfile() -{} +{ + // Build control profile CSV table + CSVTable* table = new CSVTable(); + for (auto it = controlNameMap.begin(); it != controlNameMap.end(); ++it) + { + // Get control name + const std::string& controlName = it->first; + + // Get pointer to the control + Control* control = it->second; + + // Look up list of mappings for the control + const std::list* mappings = inputMapper->getMappings(control); + if (!mappings) + { + continue; + } + + // For each input mapping + for (const InputMapping* mapping: *mappings) + { + // Add row to the table + table->push_back(CSVRow()); + CSVRow* row = &table->back(); + + // Add control name column + row->push_back(controlName); + + switch (mapping->getType()) + { + case InputMappingType::KEY: + { + const KeyMapping* keyMapping = static_cast(mapping); + row->push_back("keyboard"); + row->push_back("key"); + + std::string scancodeName; + std::stringstream stream; + stream << static_cast(keyMapping->scancode); + stream >> scancodeName; + row->push_back(scancodeName); + break; + } + + case InputMappingType::MOUSE_MOTION: + { + const MouseMotionMapping* mouseMotionMapping = static_cast(mapping); + row->push_back("mouse"); + row->push_back("motion"); + + std::string axisName; + if (mouseMotionMapping->axis == MouseMotionAxis::POSITIVE_X) + { + axisName = "+x"; + } + else if (mouseMotionMapping->axis == MouseMotionAxis::NEGATIVE_X) + { + axisName = "-x"; + } + else if (mouseMotionMapping->axis == MouseMotionAxis::POSITIVE_Y) + { + axisName = "+y"; + } + else + { + axisName = "-y"; + } + + row->push_back(axisName); + + break; + } + + case InputMappingType::MOUSE_WHEEL: + { + const MouseWheelMapping* mouseWheelMapping = static_cast(mapping); + + row->push_back("mouse"); + row->push_back("wheel"); + + std::string axisName; + if (mouseWheelMapping->axis == MouseWheelAxis::POSITIVE_X) + { + axisName = "+x"; + } + else if (mouseWheelMapping->axis == MouseWheelAxis::NEGATIVE_X) + { + axisName = "-x"; + } + else if (mouseWheelMapping->axis == MouseWheelAxis::POSITIVE_Y) + { + axisName = "+y"; + } + else + { + axisName = "-y"; + } + + row->push_back(axisName); + break; + } + + case InputMappingType::MOUSE_BUTTON: + { + const MouseButtonMapping* mouseButtonMapping = static_cast(mapping); + + row->push_back("mouse"); + row->push_back("button"); + + std::string buttonName; + std::stringstream stream; + stream << static_cast(mouseButtonMapping->button); + stream >> buttonName; + row->push_back(buttonName); + break; + } + + case InputMappingType::GAMEPAD_AXIS: + { + const GamepadAxisMapping* gamepadAxisMapping = static_cast(mapping); + + row->push_back("gamepad"); + row->push_back("axis"); + + std::stringstream stream; + if (gamepadAxisMapping->negative) + { + stream << "-"; + } + else + { + stream << "+"; + } + stream << gamepadAxisMapping->axis; + + std::string axisName; + stream >> axisName; + row->push_back(axisName); + break; + } + + case InputMappingType::GAMEPAD_BUTTON: + { + const GamepadButtonMapping* gamepadButtonMapping = static_cast(mapping); + + row->push_back("gamepad"); + row->push_back("button"); + + std::string buttonName; + std::stringstream stream; + stream << static_cast(gamepadButtonMapping->button); + stream >> buttonName; + row->push_back(buttonName); + break; + } + + default: + break; + } + } + } + + // Create controls directory if it doesn't exist + std::string controlsPath = getConfigPath() + "/controls/"; + if (!pathExists(controlsPath)) + { + createDirectory(controlsPath); + } + + // Form full path to control profile file + std::string controlProfilePath = controlsPath + controlProfileName + ".csv"; + + // Save control profile + resourceManager->save(table, controlProfilePath); + + // Free control profile CSV table + delete table; +} void Game::resizeUI(int w, int h) { diff --git a/src/resources/csv-table-loader.cpp b/src/resources/csv-table-loader.cpp index 9c34009..da9580e 100644 --- a/src/resources/csv-table-loader.cpp +++ b/src/resources/csv-table-loader.cpp @@ -111,3 +111,29 @@ CSVTable* ResourceLoader::load(ResourceManager* resourceManager, std:: return table; } +template <> +void ResourceLoader::save(ResourceManager* resourceManager, std::ostream* os, const CSVTable* table) +{ + for (std::size_t i = 0; i < table->size(); ++i) + { + const CSVRow& row = (*table)[i]; + + for (std::size_t j = 0; j < row.size(); ++j) + { + const CSVColumn& column = row[j]; + + (*os) << column; + + if (j < row.size() - 1) + { + (*os) << ","; + } + } + + if (i < table->size() - 1) + { + (*os) << std::endl; + } + } +} + diff --git a/src/resources/csv-table.hpp b/src/resources/csv-table.hpp index c257bc7..6b5fd0a 100644 --- a/src/resources/csv-table.hpp +++ b/src/resources/csv-table.hpp @@ -23,7 +23,8 @@ #include #include -typedef std::vector CSVRow; +typedef std::string CSVColumn; +typedef std::vector CSVRow; typedef std::vector CSVTable; #endif // CSV_TABLE_HPP diff --git a/src/resources/resource-loader.hpp b/src/resources/resource-loader.hpp index 32fa45f..0bb5ea7 100644 --- a/src/resources/resource-loader.hpp +++ b/src/resources/resource-loader.hpp @@ -21,11 +21,14 @@ #define RESOURCE_LOADER_HPP #include +#include class ResourceManager; /** - * Templated resource class. Different resources types should specialize the constructor and destructor to load and free resource data, respectively. + * Templated resource loader. + * + * @tparam Type of resource which this loader handles. */ template class ResourceLoader @@ -33,8 +36,21 @@ class ResourceLoader public: /** * Loads resource data. + * + * @param resourceManager Pointer to a resource manager which will manage this resource. + * @param is Input stream containing the resource data. + * @return Pointer to the loaded resource. */ static T* load(ResourceManager* resourceManager, std::istream* is); + + /** + * Saves resource data. + * + * @param resourceManager Pointer to a resource manager. + * @param os Output stream which will contain the resource data. + * @param resource Pointer to the resource data. + */ + static void save(ResourceManager* resourceManager, std::ostream* os, const T* resource); }; #endif // RESOURCE_LOADER_HPP diff --git a/src/resources/resource-manager.hpp b/src/resources/resource-manager.hpp index beaaf1d..4116d3d 100644 --- a/src/resources/resource-manager.hpp +++ b/src/resources/resource-manager.hpp @@ -68,6 +68,16 @@ public: */ void unload(const std::string& path); + /** + * Saves the specified resource. + * + * @tparam T Resource type. + * @param resource Pointer to the resource. + * @param path Path to the resource. + */ + template + void save(const T* resource, const std::string& path); + private: std::map resourceCache; std::list paths; @@ -123,7 +133,7 @@ T* ResourceManager::load(const std::string& path) if (!opened) { - throw std::runtime_error("Unable to open file."); + throw std::runtime_error("ResourceManager::load(): Unable to open file \"" + path + "\""); } } catch (const std::exception& e) @@ -143,5 +153,34 @@ T* ResourceManager::load(const std::string& path) return resource->data; } +template +void ResourceManager::save(const T* resource, const std::string& path) +{ + // Attempt to open file + std::ofstream fs; + fs.open(path.c_str(), std::ios::out | std::ios::binary); + + // If unable to open file + if (!fs.is_open() || !fs.good()) + { + if (fs.is_open()) + { + fs.close(); + } + + throw std::runtime_error("ResourceManager::save(): Unable to open file \"" + path + "\""); + } + + try + { + ResourceLoader::save(this, &fs, resource); + } + catch (const std::exception& e) + { + std::string error = std::string("ResourceManager::load(): Failed to save resource \"") + path + std::string("\": \"") + e.what() + std::string("\""); + throw std::runtime_error(error.c_str()); + } +} + #endif // RESOURCE_MANAGER_HPP