Browse Source

Integrate PhysicsFS to read data from zipped archives and support mods

master
C. J. Howard 3 years ago
parent
commit
323823a1e2
17 changed files with 313 additions and 191 deletions
  1. +4
    -12
      CMakeLists.txt
  2. +119
    -39
      src/application.cpp
  3. +3
    -0
      src/application.hpp
  4. +10
    -3
      src/resources/behavior-tree-loader.cpp
  5. +2
    -2
      src/resources/entity-archetype-loader.cpp
  6. +5
    -5
      src/resources/image-loader.cpp
  7. +2
    -2
      src/resources/material-loader.cpp
  8. +6
    -2
      src/resources/mesh-loader.cpp
  9. +6
    -2
      src/resources/model-loader.cpp
  10. +43
    -0
      src/resources/resource-loader.cpp
  11. +9
    -7
      src/resources/resource-loader.hpp
  12. +5
    -6
      src/resources/resource-manager.cpp
  13. +74
    -63
      src/resources/resource-manager.hpp
  14. +3
    -18
      src/resources/shader-program-loader.cpp
  15. +12
    -14
      src/resources/string-table-loader.cpp
  16. +8
    -13
      src/resources/text-file-loader.cpp
  17. +2
    -3
      src/resources/texture-2d-loader.cpp

+ 4
- 12
CMakeLists.txt View File

@ -4,17 +4,6 @@ option(VERSION_STRING "Project version string" "0.0.0")
project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX) 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 dependency packages
find_package(vmq REQUIRED CONFIG) find_package(vmq REQUIRED CONFIG)
find_package(dr_wav REQUIRED CONFIG) find_package(dr_wav REQUIRED CONFIG)
@ -24,6 +13,8 @@ find_package(EnTT REQUIRED CONFIG)
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
find_package(SDL2 REQUIRED COMPONENTS SDL2::SDL2-static SDL2::SDL2main CONFIG) find_package(SDL2 REQUIRED COMPONENTS SDL2::SDL2-static SDL2::SDL2main CONFIG)
find_package(OpenAL REQUIRED CONFIG) find_package(OpenAL REQUIRED CONFIG)
find_library(physfs REQUIRED NAMES physfs-static PATHS "${CMAKE_PREFIX_PATH}/lib")
# Determine dependencies # Determine dependencies
set(STATIC_LIBS set(STATIC_LIBS
@ -34,7 +25,8 @@ set(STATIC_LIBS
EnTT EnTT
SDL2::SDL2-static SDL2::SDL2-static
SDL2::SDL2main SDL2::SDL2main
OpenAL::OpenAL)
OpenAL::OpenAL
${physfs})
set(SHARED_LIBS set(SHARED_LIBS
${OPENGL_gl_LIBRARY}) ${OPENGL_gl_LIBRARY})

+ 119
- 39
src/application.cpp View File

@ -36,6 +36,7 @@
#include <glad/glad.h> #include <glad/glad.h>
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include "stb/stb_image_write.h" #include "stb/stb_image_write.h"
#include <physfs.h>
// Debug // Debug
#include "debug/ansi-codes.hpp" #include "debug/ansi-codes.hpp"
@ -99,6 +100,7 @@
#include "systems/tool-system.hpp" #include "systems/tool-system.hpp"
#include "systems/control-system.hpp" #include "systems/control-system.hpp"
#include "systems/ui-system.hpp" #include "systems/ui-system.hpp"
#include <dirent.h>
// Entity components // Entity components
#include "entity/components/cavity-component.hpp" #include "entity/components/cavity-component.hpp"
@ -109,6 +111,11 @@ application::application(int argc, char** argv):
closed(false), closed(false),
exit_status(EXIT_SUCCESS) 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 // Determine application name
std::string application_name; std::string application_name;
#if defined(_WIN32) #if defined(_WIN32)
@ -119,30 +126,21 @@ application::application(int argc, char** argv):
// Detect resource paths // Detect resource paths
data_path = get_data_path(application_name) + "data/"; 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); config_path = get_config_path(application_name);
mods_path = config_path + "mods/";
saves_path = config_path + "saves/";
screenshots_path = config_path + "screenshots/"; 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 data path as \"" + data_path + "\"");
logger.log("Detected config path as \"" + config_path + "\""); logger.log("Detected config path as \"" + config_path + "\"");
// Create nonexistent config directories // Create nonexistent config directories
std::vector<std::string> config_paths; std::vector<std::string> config_paths;
config_paths.push_back(config_path); config_paths.push_back(config_path);
config_paths.push_back(mods_path);
config_paths.push_back(saves_path);
config_paths.push_back(screenshots_path); config_paths.push_back(screenshots_path);
for (const std::string& path: config_paths) 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::string()>(std::bind(&cc::exit, this)));
cli.register_command("scrot", std::function<std::string()>(std::bind(&cc::scrot, this)));
cli.register_command("cue", std::function<std::string(float, std::string)>(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 // Setup resource manager
resource_manager = new ::resource_manager(); resource_manager = new ::resource_manager();
resource_manager->set_logger(&logger); resource_manager->set_logger(&logger);
// Include resource search paths in order of priority // 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 // Get SDL compiled version
SDL_version sdl_compiled_version; SDL_version sdl_compiled_version;
@ -300,7 +358,7 @@ application::application(int argc, char** argv):
} }
// Set v-sync mode // Set v-sync mode
int swap_interval = 1;
int swap_interval = 0;
logger.push_task((swap_interval) ? "Enabling v-sync" : "Disabling v-sync"); logger.push_task((swap_interval) ? "Enabling v-sync" : "Disabling v-sync");
if (SDL_GL_SetSwapInterval(swap_interval) != 0) if (SDL_GL_SetSwapInterval(swap_interval) != 0)
{ {
@ -759,7 +817,7 @@ application::application(int argc, char** argv):
spotlight.set_active(false); spotlight.set_active(false);
underworld_ambient_light.set_color({1, 1, 1}); 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(); underworld_ambient_light.update_tweens();
lantern.set_model(resource_manager->load<model>("lantern.obj")); lantern.set_model(resource_manager->load<model>("lantern.obj"));
@ -824,8 +882,8 @@ application::application(int argc, char** argv):
underworld_scene.add_object(&underworld_camera); underworld_scene.add_object(&underworld_camera);
underworld_scene.add_object(&underworld_ambient_light); underworld_scene.add_object(&underworld_ambient_light);
//underworld_scene.add_object(&lantern); //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<model>("larva.obj")); //model_instance* larva = new model_instance(resource_manager->load<model>("larva.obj"));
//underworld_scene.add_object(larva); //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()); get_ui_scene()->add_object(radial_transition_outer->get_billboard());
animator->add_animation(radial_transition_outer->get_animation()); animator->add_animation(radial_transition_outer->get_animation());
// Register CLI commands
cli.register_command("echo", cc::echo);
cli.register_command("exit", std::function<std::string()>(std::bind(&cc::exit, this)));
cli.register_command("scrot", std::function<std::string()>(std::bind(&cc::scrot, this)));
cli.register_command("cue", std::function<std::string(float, std::string)>(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 // Determine initial state
initial_state = &splash_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) for (int i = 0; i < argc; ++i)
{ {
if (no_splash_flag == argv[i]) if (no_splash_flag == argv[i])
@ -889,6 +957,18 @@ application::application(int argc, char** argv):
application::~application() 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 // Destroy the SDL window
SDL_DestroyWindow(window); SDL_DestroyWindow(window);

+ 3
- 0
src/application.hpp View File

@ -208,7 +208,10 @@ private:
// Paths // Paths
std::string data_path; std::string data_path;
std::string data_package_path;
std::string config_path; std::string config_path;
std::string mods_path;
std::string saves_path;
std::string screenshots_path; std::string screenshots_path;
// Resources // Resources

+ 10
- 3
src/resources/behavior-tree-loader.cpp View File

@ -27,6 +27,7 @@
#include <iostream> #include <iostream>
#include <type_traits> #include <type_traits>
#include <sstream> #include <sstream>
#include <physfs.h>
template <class T> template <class T>
void parse_argument(T& value, const std::string& string) 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 <> template <>
ebt::node* resource_loader<ebt::node>::load(resource_manager* resource_manager, std::istream* is)
ebt::node* resource_loader<ebt::node>::load(resource_manager* resource_manager, PHYSFS_File* file)
{ {
nlohmann::json json;
(*is) >> json;
// Read file into buffer
std::size_t size = static_cast<int>(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) if (json.size() != 1)
{ {

+ 2
- 2
src/resources/entity-archetype-loader.cpp View File

@ -169,12 +169,12 @@ static bool load_component(archetype& archetype, resource_manager& resource_mana
} }
template <> template <>
archetype* resource_loader<archetype>::load(resource_manager* resource_manager, std::istream* is)
archetype* resource_loader<archetype>::load(resource_manager* resource_manager, PHYSFS_File* file)
{ {
ecs::archetype* archetype = new ecs::archetype(resource_manager->get_archetype_registry()); ecs::archetype* archetype = new ecs::archetype(resource_manager->get_archetype_registry());
// Load string table from input stream // Load string table from input stream
string_table* table = resource_loader<string_table>::load(resource_manager, is);
string_table* table = resource_loader<string_table>::load(resource_manager, file);
// Ensure table is not empty. // Ensure table is not empty.
if (!table || table->empty()) if (!table || table->empty())

+ 5
- 5
src/resources/image-loader.cpp View File

@ -21,9 +21,11 @@
#include "stb/stb_image.h" #include "stb/stb_image.h"
#include "resources/image.hpp" #include "resources/image.hpp"
#include <cstring> #include <cstring>
#include <stdexcept>
#include <physfs.h>
template <> template <>
image* resource_loader<image>::load(resource_manager* resource_manager, std::istream* is)
image* resource_loader<image>::load(resource_manager* resource_manager, PHYSFS_File* file)
{ {
unsigned char* buffer; unsigned char* buffer;
int size; int size;
@ -34,11 +36,9 @@ image* resource_loader::load(resource_manager* resource_manager, std::ist
void* pixels; void* pixels;
// Read input stream into buffer // Read input stream into buffer
is->seekg(0, is->end);
size = static_cast<int>(is->tellg());
size = static_cast<int>(PHYSFS_fileLength(file));
buffer = new unsigned char[size]; buffer = new unsigned char[size];
is->seekg(0, is->beg);
is->read(reinterpret_cast<char*>(&buffer[0]), size);
PHYSFS_readBytes(file, buffer, size);
// Determine if image is in an HDR format // Determine if image is in an HDR format
hdr = (stbi_is_hdr_from_memory(buffer, size) != 0); hdr = (stbi_is_hdr_from_memory(buffer, size) != 0);

+ 2
- 2
src/resources/material-loader.cpp View File

@ -378,10 +378,10 @@ static bool load_material_property(material* material, const string_table_row& r
} }
template <> template <>
material* resource_loader<material>::load(resource_manager* resource_manager, std::istream* is)
material* resource_loader<material>::load(resource_manager* resource_manager, PHYSFS_File* file)
{ {
// Load string table from input stream // Load string table from input stream
string_table* table = resource_loader<string_table>::load(resource_manager, is);
string_table* table = resource_loader<string_table>::load(resource_manager, file);
// Ensure table is not empty. // Ensure table is not empty.
if (!table || table->empty()) if (!table || table->empty())

+ 6
- 2
src/resources/mesh-loader.cpp View File

@ -22,16 +22,20 @@
#include "geometry/mesh-functions.hpp" #include "geometry/mesh-functions.hpp"
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <physfs.h>
template <> template <>
mesh* resource_loader<mesh>::load(resource_manager* resource_manager, std::istream* is)
mesh* resource_loader<mesh>::load(resource_manager* resource_manager, PHYSFS_File* file)
{ {
std::string line; std::string line;
std::vector<float3> vertices; std::vector<float3> vertices;
std::vector<std::array<std::uint_fast32_t, 3>> triangles; std::vector<std::array<std::uint_fast32_t, 3>> triangles;
while (is->good() && std::getline(*is, line))
while (!PHYSFS_eof(file))
{ {
// Read line
physfs_getline(file, line);
// Tokenize line // Tokenize line
std::vector<std::string> tokens; std::vector<std::string> tokens;
std::string token; std::string token;

+ 6
- 2
src/resources/model-loader.cpp View File

@ -26,6 +26,7 @@
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <limits> #include <limits>
#include <physfs.h>
#include <vmq/vmq.hpp> #include <vmq/vmq.hpp>
using namespace vmq::types; using namespace vmq::types;
@ -46,7 +47,7 @@ static const float3 barycentric_coords[3] =
}; };
template <> template <>
model* resource_loader<model>::load(resource_manager* resource_manager, std::istream* is)
model* resource_loader<model>::load(resource_manager* resource_manager, PHYSFS_File* file)
{ {
std::string line; std::string line;
std::vector<float3> positions; std::vector<float3> positions;
@ -61,8 +62,11 @@ model* resource_loader::load(resource_manager* resource_manager, std::ist
{-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity()} {-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity()}
}; };
while (is->good() && std::getline(*is, line))
while (!PHYSFS_eof(file))
{ {
// Read line
physfs_getline(file, line);
// Tokenize line // Tokenize line
std::vector<std::string> tokens; std::vector<std::string> tokens;
std::string token; std::string token;

+ 43
- 0
src/resources/resource-loader.cpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "resource-loader.hpp"
#include <physfs.h>
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));
}

+ 9
- 7
src/resources/resource-loader.hpp View File

@ -20,10 +20,10 @@
#ifndef RESOURCE_LOADER_HPP #ifndef RESOURCE_LOADER_HPP
#define RESOURCE_LOADER_HPP #define RESOURCE_LOADER_HPP
#include <istream>
#include <ostream>
#include <string>
class resource_manager; class resource_manager;
struct PHYSFS_File;
/** /**
* Templated resource loader. * Templated resource loader.
@ -38,20 +38,22 @@ public:
* Loads resource data. * Loads resource data.
* *
* @param resourceManager Pointer to a resource manager which will manage this resource. * @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. * @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. * Saves resource data.
* *
* @param resourceManager Pointer to a resource manager. * @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. * @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

+ 5
- 6
src/resources/resource-manager.cpp View File

@ -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 // 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 (it != resource_cache.end())
{ {
// Decrement the resource handle reference count // Decrement the resource handle reference count
@ -45,7 +45,7 @@ void resource_manager::unload(const std::string& path)
{ {
if (logger) if (logger)
{ {
logger->push_task("Unloading resource \"" + path + "\"");
logger->push_task("Unloading resource \"" + name + "\"");
} }
delete it->second; 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) void resource_manager::set_logger(::logger* logger)
{ {
this->logger = logger; this->logger = logger;
} }

+ 74
- 63
src/resources/resource-manager.hpp View File

@ -29,6 +29,7 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <entt/entt.hpp> #include <entt/entt.hpp>
#include <physfs.h>
/** /**
* Loads resources. * Loads resources.
@ -61,14 +62,14 @@ public:
* @return Pointer to the requested resource, or nullptr if the resource could not be found nor loaded. * @return Pointer to the requested resource, or nullptr if the resource could not be found nor loaded.
*/ */
template <typename T> template <typename T>
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. * 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. * @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. * Saves the specified resource.
@ -86,22 +87,24 @@ public:
private: private:
std::map<std::string, resource_handle_base*> resource_cache; std::map<std::string, resource_handle_base*> resource_cache;
std::list<std::string> paths;
std::list<std::string> search_paths;
entt::registry archetype_registry; entt::registry archetype_registry;
::logger* logger; ::logger* logger;
}; };
template <typename T> template <typename T>
T* resource_manager::load(const std::string& path)
T* resource_manager::load(const std::string& name)
{ {
// Check if resource is in the cache // 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 (it != resource_cache.end())
{ {
/*
if (logger) if (logger)
{ {
logger->log("Fetched resource \"" + path + "\"");
logger->log("Fetched resource \"" + name + "\"");
} }
*/
// Resource found // Resource found
resource_handle<T>* resource = static_cast<resource_handle<T>*>(it->second); resource_handle<T>* resource = static_cast<resource_handle<T>*>(it->second);
@ -115,60 +118,61 @@ T* resource_manager::load(const std::string& path)
if (logger) 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; 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<T>::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; break;
} }
if (!opened)
// Load opened file
try
{
data = resource_loader<T>::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<T>(): 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<T>(): 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 // Create a resource handle for the resource data
@ -177,7 +181,7 @@ T* resource_manager::load(const std::string& path)
resource->reference_count = 1; resource->reference_count = 1;
// Add resource to the cache // Add resource to the cache
resource_cache[path] = resource;
resource_cache[name] = resource;
if (logger) if (logger)
{ {
@ -190,30 +194,37 @@ T* resource_manager::load(const std::string& path)
template <typename T> template <typename T>
void resource_manager::save(const T* resource, const std::string& path) 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<T>(): 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 try
{ {
resource_loader<T>::save(this, &fs, resource);
resource_loader<T>::save(this, file, resource);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
std::string error = std::string("resource_manager::load<T>(): 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() inline entt::registry& resource_manager::get_archetype_registry()

+ 3
- 18
src/resources/shader-program-loader.cpp View File

@ -172,25 +172,10 @@ static std::string generate_source_buffer(const std::vector& source
} }
template <> template <>
shader_program* resource_loader<shader_program>::load(resource_manager* resource_manager, std::istream* is)
shader_program* resource_loader<shader_program>::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<text_file>::load(resource_manager, file);
// Handle `#pragma include` directives // Handle `#pragma include` directives
handle_includes(source, resource_manager); handle_includes(source, resource_manager);

+ 12
- 14
src/resources/string-table-loader.cpp View File

@ -19,6 +19,7 @@
#include "resource-loader.hpp" #include "resource-loader.hpp"
#include "string-table.hpp" #include "string-table.hpp"
#include <physfs.h>
static string_table_row parse_row(const std::string& line) 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 <> template <>
string_table* resource_loader<string_table>::load(resource_manager* resource_manager, std::istream* is)
string_table* resource_loader<string_table>::load(resource_manager* resource_manager, PHYSFS_File* file)
{ {
string_table* table = new string_table(); string_table* table = new string_table();
std::string line; 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)); table->push_back(parse_row(line));
} }
@ -112,8 +108,11 @@ string_table* resource_loader::load(resource_manager* resource_man
} }
template <> template <>
void resource_loader<string_table>::save(resource_manager* resource_manager, std::ostream* os, const string_table* table)
void resource_loader<string_table>::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) for (std::size_t i = 0; i < table->size(); ++i)
{ {
const string_table_row& row = (*table)[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) for (std::size_t j = 0; j < row.size(); ++j)
{ {
const std::string& column = row[j]; const std::string& column = row[j];
(*os) << column;
PHYSFS_writeBytes(file, column.data(), column.length());
if (j < row.size() - 1) if (j < row.size() - 1)
{ {
(*os) << ",";
PHYSFS_writeBytes(file, delimeter, 1);
} }
} }
if (i < table->size() - 1) if (i < table->size() - 1)
{ {
(*os) << std::endl;
PHYSFS_writeBytes(file, newline, 1);
} }
} }
} }

+ 8
- 13
src/resources/text-file-loader.cpp View File

@ -19,24 +19,19 @@
#include "resources/resource-loader.hpp" #include "resources/resource-loader.hpp"
#include "resources/text-file.hpp" #include "resources/text-file.hpp"
#include <physfs.h>
template <> template <>
text_file* resource_loader<text_file>::load(resource_manager* resource_manager, std::istream* is)
text_file* resource_loader<text_file>::load(resource_manager* resource_manager, PHYSFS_File* file)
{ {
text_file* file = new text_file();
text_file* text = new text_file();
std::string line; 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;
} }

+ 2
- 3
src/resources/texture-2d-loader.cpp View File

@ -25,10 +25,10 @@
#include <sstream> #include <sstream>
template <> template <>
texture_2d* resource_loader<texture_2d>::load(resource_manager* resource_manager, std::istream* is)
texture_2d* resource_loader<texture_2d>::load(resource_manager* resource_manager, PHYSFS_File* file)
{ {
// Load image // Load image
::image* image = resource_loader<::image>::load(resource_manager, is);
::image* image = resource_loader<::image>::load(resource_manager, file);
// Determine pixel type // Determine pixel type
pixel_type type = (image->is_hdr()) ? pixel_type::float_32 : pixel_type::uint_8; 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; return texture;
} }

Loading…
Cancel
Save