From 323823a1e22baaa287289e5a193f9e1bec4be0f9 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Sat, 8 Aug 2020 17:15:08 -0700 Subject: [PATCH] Integrate PhysicsFS to read data from zipped archives and support mods --- CMakeLists.txt | 16 +-- src/application.cpp | 158 ++++++++++++++++------ src/application.hpp | 3 + src/resources/behavior-tree-loader.cpp | 13 +- src/resources/entity-archetype-loader.cpp | 4 +- src/resources/image-loader.cpp | 10 +- src/resources/material-loader.cpp | 4 +- src/resources/mesh-loader.cpp | 8 +- src/resources/model-loader.cpp | 8 +- src/resources/resource-loader.cpp | 43 ++++++ src/resources/resource-loader.hpp | 16 ++- src/resources/resource-manager.cpp | 11 +- src/resources/resource-manager.hpp | 137 ++++++++++--------- src/resources/shader-program-loader.cpp | 21 +-- src/resources/string-table-loader.cpp | 26 ++-- src/resources/text-file-loader.cpp | 21 ++- src/resources/texture-2d-loader.cpp | 5 +- 17 files changed, 313 insertions(+), 191 deletions(-) create mode 100644 src/resources/resource-loader.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a4f2b0d..d516876 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,17 +4,6 @@ option(VERSION_STRING "Project version string" "0.0.0") project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX) -# Set compiler flags -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 /MT") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS}") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} /Ox") -endif() - # Find dependency packages find_package(vmq REQUIRED CONFIG) find_package(dr_wav REQUIRED CONFIG) @@ -24,6 +13,8 @@ find_package(EnTT REQUIRED CONFIG) find_package(OpenGL REQUIRED) find_package(SDL2 REQUIRED COMPONENTS SDL2::SDL2-static SDL2::SDL2main CONFIG) find_package(OpenAL REQUIRED CONFIG) +find_library(physfs REQUIRED NAMES physfs-static PATHS "${CMAKE_PREFIX_PATH}/lib") + # Determine dependencies set(STATIC_LIBS @@ -34,7 +25,8 @@ set(STATIC_LIBS EnTT SDL2::SDL2-static SDL2::SDL2main - OpenAL::OpenAL) + OpenAL::OpenAL + ${physfs}) set(SHARED_LIBS ${OPENGL_gl_LIBRARY}) diff --git a/src/application.cpp b/src/application.cpp index 2360392..b9b61a4 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -36,6 +36,7 @@ #include #include #include "stb/stb_image_write.h" +#include // Debug #include "debug/ansi-codes.hpp" @@ -99,6 +100,7 @@ #include "systems/tool-system.hpp" #include "systems/control-system.hpp" #include "systems/ui-system.hpp" +#include // Entity components #include "entity/components/cavity-component.hpp" @@ -109,6 +111,11 @@ application::application(int argc, char** argv): closed(false), exit_status(EXIT_SUCCESS) { + // Format log messages + logger.set_warning_prefix("Warning: "); + logger.set_error_prefix(std::string()); + logger.set_success_prefix(std::string()); + // Determine application name std::string application_name; #if defined(_WIN32) @@ -119,30 +126,21 @@ application::application(int argc, char** argv): // Detect resource paths data_path = get_data_path(application_name) + "data/"; + data_package_path = get_data_path(application_name) + "data.zip"; config_path = get_config_path(application_name); + mods_path = config_path + "mods/"; + saves_path = config_path + "saves/"; screenshots_path = config_path + "screenshots/"; - // Format log messages - logger.set_warning_prefix("Warning: "); - logger.set_error_prefix(std::string()); - logger.set_success_prefix(std::string()); - - // Redirect logger output - #if defined(DEBUG) - logger.redirect(&std::cout); - #else - std::string log_filename = config_path + "log.txt"; - log_filestream.open(log_filename.c_str()); - logger.redirect(&log_filestream); - #endif - - // Log paths + // Log resource paths logger.log("Detected data path as \"" + data_path + "\""); logger.log("Detected config path as \"" + config_path + "\""); - + // Create nonexistent config directories std::vector config_paths; config_paths.push_back(config_path); + config_paths.push_back(mods_path); + config_paths.push_back(saves_path); config_paths.push_back(screenshots_path); for (const std::string& path: config_paths) { @@ -160,30 +158,90 @@ application::application(int argc, char** argv): } } - // Register CLI commands - cli.register_command("echo", cc::echo); - cli.register_command("exit", std::function(std::bind(&cc::exit, this))); - cli.register_command("scrot", std::function(std::bind(&cc::scrot, this))); - cli.register_command("cue", std::function(std::bind(&cc::cue, this, std::placeholders::_1, std::placeholders::_2))); - //std::string cmd = "cue 20 exit"; - //logger.log(cmd); - //logger.log(cli.interpret(cmd)); + // Redirect logger output to log file on non-debug builds + #if defined(NDEBUG) + std::string log_filename = config_path + "log.txt"; + log_filestream.open(log_filename.c_str()); + logger.redirect(&log_filestream); + #endif + + // Init PhysicsFS + logger.push_task("Initializing PhysicsFS"); + if (!PHYSFS_init(argv[0])) + { + logger.error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + logger.pop_task(EXIT_FAILURE); + } + else + { + logger.pop_task(EXIT_SUCCESS); + } + + // Mount mods + struct dirent **files = nullptr; + int n = scandir (mods_path.c_str(), &files, NULL, alphasort); + if (n >= 0) + { + for (int i = 0; i < n; ++i) + { + struct dirent* file = files[i]; + + switch (file->d_type) + { + case DT_REG: + case DT_DIR: + { + std::string mod_name = file->d_name; + + // Skip hidden files and directories + if (mod_name.front() == '.') + break; + + std::string mod_path = mods_path + mod_name; + logger.push_task("Mounting mod \"" + mod_path + "\""); + if (!PHYSFS_mount(mod_path.c_str(), nullptr, 1)) + { + logger.error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + logger.pop_task(EXIT_FAILURE); + } + else + { + logger.pop_task(EXIT_SUCCESS); + } + + break; + } + + default: + break; + } + } + } + + // Mount data package + logger.push_task("Mounting data package \"" + data_package_path + "\""); + if (!PHYSFS_mount(data_package_path.c_str(), nullptr, 1)) + { + logger.error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + logger.pop_task(EXIT_FAILURE); + } + else + { + logger.pop_task(EXIT_SUCCESS); + } // Setup resource manager resource_manager = new ::resource_manager(); resource_manager->set_logger(&logger); // Include resource search paths in order of priority - resource_manager->include(config_path); - resource_manager->include(data_path); - resource_manager->include(data_path + "/shaders/include/"); - resource_manager->include(data_path + "/shaders/src/"); - resource_manager->include(data_path + "/models/"); - resource_manager->include(data_path + "/textures/"); - resource_manager->include(data_path + "/materials/"); - resource_manager->include(data_path + "/entities/"); - resource_manager->include(data_path + "/behaviors/"); - resource_manager->include(data_path + "/controls/"); + resource_manager->include("/shaders/"); + resource_manager->include("/models/"); + resource_manager->include("/textures/"); + resource_manager->include("/materials/"); + resource_manager->include("/entities/"); + resource_manager->include("/behaviors/"); + resource_manager->include("/controls/"); // Get SDL compiled version SDL_version sdl_compiled_version; @@ -300,7 +358,7 @@ application::application(int argc, char** argv): } // Set v-sync mode - int swap_interval = 1; + int swap_interval = 0; logger.push_task((swap_interval) ? "Enabling v-sync" : "Disabling v-sync"); if (SDL_GL_SetSwapInterval(swap_interval) != 0) { @@ -759,7 +817,7 @@ application::application(int argc, char** argv): spotlight.set_active(false); underworld_ambient_light.set_color({1, 1, 1}); - underworld_ambient_light.set_intensity(0.15f); + underworld_ambient_light.set_intensity(0.1f); underworld_ambient_light.update_tweens(); lantern.set_model(resource_manager->load("lantern.obj")); @@ -824,8 +882,8 @@ application::application(int argc, char** argv): underworld_scene.add_object(&underworld_camera); underworld_scene.add_object(&underworld_ambient_light); //underworld_scene.add_object(&lantern); - underworld_scene.add_object(&subterrain_light); - underworld_scene.add_object(portal_billboard); + //underworld_scene.add_object(&subterrain_light); + //underworld_scene.add_object(portal_billboard); //model_instance* larva = new model_instance(resource_manager->load("larva.obj")); //underworld_scene.add_object(larva); @@ -874,9 +932,19 @@ application::application(int argc, char** argv): get_ui_scene()->add_object(radial_transition_outer->get_billboard()); animator->add_animation(radial_transition_outer->get_animation()); + // Register CLI commands + cli.register_command("echo", cc::echo); + cli.register_command("exit", std::function(std::bind(&cc::exit, this))); + cli.register_command("scrot", std::function(std::bind(&cc::scrot, this))); + cli.register_command("cue", std::function(std::bind(&cc::cue, this, std::placeholders::_1, std::placeholders::_2))); + //std::string cmd = "cue 20 exit"; + //logger.log(cmd); + //logger.log(cli.interpret(cmd)); + + // Determine initial state // Determine initial state initial_state = &splash_state; - std::string no_splash_flag = "--skip-splash"; + std::string no_splash_flag = "--no-splash"; for (int i = 0; i < argc; ++i) { if (no_splash_flag == argv[i]) @@ -889,6 +957,18 @@ application::application(int argc, char** argv): application::~application() { + // Deinit PhysicsFS + logger.push_task("Deinitializing PhysicsFS"); + if (!PHYSFS_deinit()) + { + logger.error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + logger.pop_task(EXIT_FAILURE); + } + else + { + logger.pop_task(EXIT_SUCCESS); + } + // Destroy the SDL window SDL_DestroyWindow(window); diff --git a/src/application.hpp b/src/application.hpp index 2c8350b..ba226c0 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -208,7 +208,10 @@ private: // Paths std::string data_path; + std::string data_package_path; std::string config_path; + std::string mods_path; + std::string saves_path; std::string screenshots_path; // Resources diff --git a/src/resources/behavior-tree-loader.cpp b/src/resources/behavior-tree-loader.cpp index 456e602..afdf66c 100644 --- a/src/resources/behavior-tree-loader.cpp +++ b/src/resources/behavior-tree-loader.cpp @@ -27,6 +27,7 @@ #include #include #include +#include template void parse_argument(T& value, const std::string& string) @@ -141,10 +142,16 @@ static void load_node_children(ebt::composite_node* node, const nlohmann::json& } template <> -ebt::node* resource_loader::load(resource_manager* resource_manager, std::istream* is) +ebt::node* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) { - nlohmann::json json; - (*is) >> json; + // Read file into buffer + std::size_t size = static_cast(PHYSFS_fileLength(file)); + std::string buffer; + buffer.resize(size); + PHYSFS_readBytes(file, &buffer[0], size); + + // Parse json from file buffer + nlohmann::json json = nlohmann::json::parse(buffer); if (json.size() != 1) { diff --git a/src/resources/entity-archetype-loader.cpp b/src/resources/entity-archetype-loader.cpp index b75fdc7..9688dcd 100644 --- a/src/resources/entity-archetype-loader.cpp +++ b/src/resources/entity-archetype-loader.cpp @@ -169,12 +169,12 @@ static bool load_component(archetype& archetype, resource_manager& resource_mana } template <> -archetype* resource_loader::load(resource_manager* resource_manager, std::istream* is) +archetype* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) { ecs::archetype* archetype = new ecs::archetype(resource_manager->get_archetype_registry()); // Load string table from input stream - string_table* table = resource_loader::load(resource_manager, is); + string_table* table = resource_loader::load(resource_manager, file); // Ensure table is not empty. if (!table || table->empty()) diff --git a/src/resources/image-loader.cpp b/src/resources/image-loader.cpp index c9382f7..aa85fd6 100644 --- a/src/resources/image-loader.cpp +++ b/src/resources/image-loader.cpp @@ -21,9 +21,11 @@ #include "stb/stb_image.h" #include "resources/image.hpp" #include +#include +#include template <> -image* resource_loader::load(resource_manager* resource_manager, std::istream* is) +image* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) { unsigned char* buffer; int size; @@ -34,11 +36,9 @@ image* resource_loader::load(resource_manager* resource_manager, std::ist void* pixels; // Read input stream into buffer - is->seekg(0, is->end); - size = static_cast(is->tellg()); + size = static_cast(PHYSFS_fileLength(file)); buffer = new unsigned char[size]; - is->seekg(0, is->beg); - is->read(reinterpret_cast(&buffer[0]), size); + PHYSFS_readBytes(file, buffer, size); // Determine if image is in an HDR format hdr = (stbi_is_hdr_from_memory(buffer, size) != 0); diff --git a/src/resources/material-loader.cpp b/src/resources/material-loader.cpp index 27ec3c5..206eb0a 100644 --- a/src/resources/material-loader.cpp +++ b/src/resources/material-loader.cpp @@ -378,10 +378,10 @@ static bool load_material_property(material* material, const string_table_row& r } template <> -material* resource_loader::load(resource_manager* resource_manager, std::istream* is) +material* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) { // Load string table from input stream - string_table* table = resource_loader::load(resource_manager, is); + string_table* table = resource_loader::load(resource_manager, file); // Ensure table is not empty. if (!table || table->empty()) diff --git a/src/resources/mesh-loader.cpp b/src/resources/mesh-loader.cpp index c2675f2..4786338 100644 --- a/src/resources/mesh-loader.cpp +++ b/src/resources/mesh-loader.cpp @@ -22,16 +22,20 @@ #include "geometry/mesh-functions.hpp" #include #include +#include template <> -mesh* resource_loader::load(resource_manager* resource_manager, std::istream* is) +mesh* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) { std::string line; std::vector vertices; std::vector> triangles; - while (is->good() && std::getline(*is, line)) + while (!PHYSFS_eof(file)) { + // Read line + physfs_getline(file, line); + // Tokenize line std::vector tokens; std::string token; diff --git a/src/resources/model-loader.cpp b/src/resources/model-loader.cpp index 7b39dfa..c56f360 100644 --- a/src/resources/model-loader.cpp +++ b/src/resources/model-loader.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include using namespace vmq::types; @@ -46,7 +47,7 @@ static const float3 barycentric_coords[3] = }; template <> -model* resource_loader::load(resource_manager* resource_manager, std::istream* is) +model* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) { std::string line; std::vector positions; @@ -61,8 +62,11 @@ model* resource_loader::load(resource_manager* resource_manager, std::ist {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()} }; - while (is->good() && std::getline(*is, line)) + while (!PHYSFS_eof(file)) { + // Read line + physfs_getline(file, line); + // Tokenize line std::vector tokens; std::string token; diff --git a/src/resources/resource-loader.cpp b/src/resources/resource-loader.cpp new file mode 100644 index 0000000..9e36845 --- /dev/null +++ b/src/resources/resource-loader.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 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 "resource-loader.hpp" +#include + +void physfs_getline(PHYSFS_File* file, std::string& line) +{ + PHYSFS_sint64 bytes; + char c; + + line.clear(); + + do + { + bytes = PHYSFS_readBytes(file, &c, 1); + + if (bytes != 1 || c == '\n') + break; + + if (c == '\r') + continue; + + line.append(1, c); + } + while (!PHYSFS_eof(file)); +} diff --git a/src/resources/resource-loader.hpp b/src/resources/resource-loader.hpp index 0c9fb78..2cac121 100644 --- a/src/resources/resource-loader.hpp +++ b/src/resources/resource-loader.hpp @@ -20,10 +20,10 @@ #ifndef RESOURCE_LOADER_HPP #define RESOURCE_LOADER_HPP -#include -#include +#include class resource_manager; +struct PHYSFS_File; /** * Templated resource loader. @@ -38,20 +38,22 @@ public: * Loads resource data. * * @param resourceManager Pointer to a resource manager which will manage this resource. - * @param is Input stream containing the resource data. + * @param file PhysicsFS file handle. * @return Pointer to the loaded resource. */ - static T* load(resource_manager* resourceManager, std::istream* is); + static T* load(resource_manager* resourceManager, PHYSFS_File* file); /** * Saves resource data. * * @param resourceManager Pointer to a resource manager. - * @param os Output stream which will contain the resource data. + * @param file PhysicsFS file handle. * @param resource Pointer to the resource data. */ - static void save(resource_manager* resourceManager, std::ostream* os, const T* resource); + static void save(resource_manager* resourceManager, PHYSFS_File* file, const T* resource); }; -#endif // RESOURCE_LOADER_HPP +/// getline function for PhysicsFS file handles +void physfs_getline(PHYSFS_File* file, std::string& line); +#endif // RESOURCE_LOADER_HPP diff --git a/src/resources/resource-manager.cpp b/src/resources/resource-manager.cpp index f0adb90..fd55edc 100644 --- a/src/resources/resource-manager.cpp +++ b/src/resources/resource-manager.cpp @@ -31,10 +31,10 @@ resource_manager::~resource_manager() } } -void resource_manager::unload(const std::string& path) +void resource_manager::unload(const std::string& name) { // Check if resource is in the cache - auto it = resource_cache.find(path); + auto it = resource_cache.find(name); if (it != resource_cache.end()) { // Decrement the resource handle reference count @@ -45,7 +45,7 @@ void resource_manager::unload(const std::string& path) { if (logger) { - logger->push_task("Unloading resource \"" + path + "\""); + logger->push_task("Unloading resource \"" + name + "\""); } delete it->second; @@ -61,13 +61,12 @@ void resource_manager::unload(const std::string& path) } } -void resource_manager::include(const std::string& path) +void resource_manager::include(const std::string& search_path) { - paths.push_back(path); + search_paths.push_back(search_path); } void resource_manager::set_logger(::logger* logger) { this->logger = logger; } - diff --git a/src/resources/resource-manager.hpp b/src/resources/resource-manager.hpp index 6a98e23..1898d3d 100644 --- a/src/resources/resource-manager.hpp +++ b/src/resources/resource-manager.hpp @@ -29,6 +29,7 @@ #include #include #include +#include /** * Loads resources. @@ -61,14 +62,14 @@ public: * @return Pointer to the requested resource, or nullptr if the resource could not be found nor loaded. */ template - T* load(const std::string& path); + T* load(const std::string& name); /** * Decrements a resource's reference count and unloads the resource if it's unreferenced. * * @param path Path to the resource, relative to the search paths. */ - void unload(const std::string& path); + void unload(const std::string& name); /** * Saves the specified resource. @@ -86,22 +87,24 @@ public: private: std::map resource_cache; - std::list paths; + std::list search_paths; entt::registry archetype_registry; ::logger* logger; }; template -T* resource_manager::load(const std::string& path) +T* resource_manager::load(const std::string& name) { // Check if resource is in the cache - auto it = resource_cache.find(path); + auto it = resource_cache.find(name); if (it != resource_cache.end()) { + /* if (logger) { - logger->log("Fetched resource \"" + path + "\""); + logger->log("Fetched resource \"" + name + "\""); } + */ // Resource found resource_handle* resource = static_cast*>(it->second); @@ -115,60 +118,61 @@ T* resource_manager::load(const std::string& path) if (logger) { - logger->push_task("Loading resource \"" + path + "\""); + logger->push_task("Loading resource \"" + name + "\""); } - // Resource not found, load resource data + // Resource not cached, look for file in search paths T* data = nullptr; - try + bool found = false; + for (const std::string& search_path: search_paths) { - // For each directory in search paths - bool opened = false; - for (const std::string& directory: paths) + std::string path = search_path + name; + + // Check if file exists + if (!PHYSFS_exists(path.c_str())) { - // Attempt to open file - std::string full_path = directory + path; - std::ifstream fs; - fs.open(full_path.c_str(), std::ios::in | std::ios::binary); - - // If unable to open file - if (!fs.is_open() || !fs.good()) - { - if (fs.is_open()) - { - fs.close(); - } - - // Try again in next search path - continue; - } - - // File opened, load it - opened = true; - data = resource_loader::load(this, &fs); - fs.close(); + continue; + } + + // File found + found = true; + + // Open file for reading + PHYSFS_File* file = PHYSFS_openRead(path.c_str()); + if (!file) + { + logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); break; } - if (!opened) + // Load opened file + try + { + data = resource_loader::load(this, file); + } + catch (const std::exception& e) + { + logger->error("Failed to load resource: \"" + std::string(e.what()) + "\""); + } + + // Close opened file + if (!PHYSFS_close(file)) { - if (logger) - { - logger->pop_task(EXIT_FAILURE); - } - - throw std::runtime_error("resource_manager::load(): Unable to open file \"" + path + "\""); + logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); } + + break; } - catch (const std::exception& e) + + if (!data) { - if (logger) + if (!found) { - logger->pop_task(EXIT_FAILURE); + logger->error("File not found"); } - - std::string error = std::string("resource_manager::load(): Failed to load resource \"") + path + std::string("\": \"") + e.what() + std::string("\""); - throw std::runtime_error(error.c_str()); + + logger->pop_task(EXIT_FAILURE); + return nullptr; } // Create a resource handle for the resource data @@ -177,7 +181,7 @@ T* resource_manager::load(const std::string& path) resource->reference_count = 1; // Add resource to the cache - resource_cache[path] = resource; + resource_cache[name] = resource; if (logger) { @@ -190,30 +194,37 @@ T* resource_manager::load(const std::string& path) template void resource_manager::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()) + logger->push_task("Saving resource to \"" + path + "\""); + + // Open file for writing + PHYSFS_File* file = PHYSFS_openWrite(path.c_str()); + if (!file) { - if (fs.is_open()) - { - fs.close(); - } - - throw std::runtime_error("resource_manager::save(): Unable to open file \"" + path + "\""); + logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + logger->pop_task(EXIT_FAILURE); + return; } - + + // Save to opened file + int status = EXIT_SUCCESS; try { - resource_loader::save(this, &fs, resource); + resource_loader::save(this, file, resource); } catch (const std::exception& e) { - std::string error = std::string("resource_manager::load(): Failed to save resource \"") + path + std::string("\": \"") + e.what() + std::string("\""); - throw std::runtime_error(error.c_str()); + logger->error("Failed to save resource: \"" + std::string(e.what()) + "\""); + status = EXIT_FAILURE; } + + // Close opened file + if (!PHYSFS_close(file)) + { + logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + status = EXIT_FAILURE; + } + + logger->pop_task(status) } inline entt::registry& resource_manager::get_archetype_registry() diff --git a/src/resources/shader-program-loader.cpp b/src/resources/shader-program-loader.cpp index 63a8066..b2fbd51 100644 --- a/src/resources/shader-program-loader.cpp +++ b/src/resources/shader-program-loader.cpp @@ -172,25 +172,10 @@ static std::string generate_source_buffer(const std::vector& source } template <> -shader_program* resource_loader::load(resource_manager* resource_manager, std::istream* is) +shader_program* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) { - // Allocate shader source - text_file* source = new text_file(); - - // Read input stream into source - while (!is->eof()) - { - // For each line in input stream - std::string line; - std::getline(*is, line); - if (is->bad() || is->fail()) - { - break; - } - - // Add line to the source - source->push_back(line); - } + // Load shader source + text_file* source = resource_loader::load(resource_manager, file); // Handle `#pragma include` directives handle_includes(source, resource_manager); diff --git a/src/resources/string-table-loader.cpp b/src/resources/string-table-loader.cpp index e8302e2..c00e5e3 100644 --- a/src/resources/string-table-loader.cpp +++ b/src/resources/string-table-loader.cpp @@ -19,6 +19,7 @@ #include "resource-loader.hpp" #include "string-table.hpp" +#include static string_table_row parse_row(const std::string& line) { @@ -92,19 +93,14 @@ static string_table_row parse_row(const std::string& line) } template <> -string_table* resource_loader::load(resource_manager* resource_manager, std::istream* is) +string_table* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) { string_table* table = new string_table(); std::string line; - while (!is->eof()) + while (!PHYSFS_eof(file)) { - std::getline(*is, line); - if (is->bad() || is->fail()) - { - break; - } - + physfs_getline(file, line); table->push_back(parse_row(line)); } @@ -112,8 +108,11 @@ string_table* resource_loader::load(resource_manager* resource_man } template <> -void resource_loader::save(resource_manager* resource_manager, std::ostream* os, const string_table* table) +void resource_loader::save(resource_manager* resource_manager, PHYSFS_File* file, const string_table* table) { + const char* delimeter = ","; + const char* newline = "\n"; + for (std::size_t i = 0; i < table->size(); ++i) { const string_table_row& row = (*table)[i]; @@ -121,19 +120,18 @@ void resource_loader::save(resource_manager* resource_manager, std for (std::size_t j = 0; j < row.size(); ++j) { const std::string& column = row[j]; - - (*os) << column; + + PHYSFS_writeBytes(file, column.data(), column.length()); if (j < row.size() - 1) { - (*os) << ","; + PHYSFS_writeBytes(file, delimeter, 1); } } if (i < table->size() - 1) { - (*os) << std::endl; + PHYSFS_writeBytes(file, newline, 1); } } } - diff --git a/src/resources/text-file-loader.cpp b/src/resources/text-file-loader.cpp index 71f788a..775f283 100644 --- a/src/resources/text-file-loader.cpp +++ b/src/resources/text-file-loader.cpp @@ -19,24 +19,19 @@ #include "resources/resource-loader.hpp" #include "resources/text-file.hpp" +#include template <> -text_file* resource_loader::load(resource_manager* resource_manager, std::istream* is) +text_file* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) { - text_file* file = new text_file(); + text_file* text = new text_file(); std::string line; - - while (!is->eof()) + + while (!PHYSFS_eof(file)) { - std::getline(*is, line); - if (is->bad() || is->fail()) - { - break; - } - - file->push_back(line); + physfs_getline(file, line); + text->push_back(line); } - return file; + return text; } - diff --git a/src/resources/texture-2d-loader.cpp b/src/resources/texture-2d-loader.cpp index 1ce613b..f86887f 100644 --- a/src/resources/texture-2d-loader.cpp +++ b/src/resources/texture-2d-loader.cpp @@ -25,10 +25,10 @@ #include template <> -texture_2d* resource_loader::load(resource_manager* resource_manager, std::istream* is) +texture_2d* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) { // Load image - ::image* image = resource_loader<::image>::load(resource_manager, is); + ::image* image = resource_loader<::image>::load(resource_manager, file); // Determine pixel type pixel_type type = (image->is_hdr()) ? pixel_type::float_32 : pixel_type::uint_8; @@ -67,4 +67,3 @@ texture_2d* resource_loader::load(resource_manager* resource_manager return texture; } -