diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index 8c7fc2dc..dfb2c7df 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -20,6 +20,8 @@ #include "Assets.hpp" #include "assetload_funcs.hpp" +namespace fs = std::filesystem; + static debug::Logger logger("assets-loader"); AssetsLoader::AssetsLoader(Assets* assets, const ResPaths* paths) @@ -83,22 +85,22 @@ void AssetsLoader::loadNext() { } } -void addLayouts( +static void add_layouts( const scriptenv& env, const std::string& prefix, - const fs::path& folder, + const io::path& folder, AssetsLoader& loader ) { - if (!fs::is_directory(folder)) { + if (!io::is_directory(folder)) { return; } - for (auto& entry : fs::directory_iterator(folder)) { - const fs::path& file = entry.path(); - if (file.extension().u8string() != ".xml") continue; - std::string name = prefix + ":" + file.stem().u8string(); + for (auto& entry : fs::directory_iterator(io::resolve(folder))) { + io::path file = folder / entry.path().filename().u8string(); + if (file.extension() != ".xml") continue; + std::string name = prefix + ":" + file.stem(); loader.add( AssetType::LAYOUT, - file.u8string(), + file.string(), name, std::make_shared(env) ); @@ -186,7 +188,7 @@ void AssetsLoader::processPreloadList(AssetType tag, const dv::value& list) { } } -void AssetsLoader::processPreloadConfig(const fs::path& file) { +void AssetsLoader::processPreloadConfig(const io::path& file) { auto root = io::read_json(file); processPreloadList(AssetType::ATLAS, root["atlases"]); processPreloadList(AssetType::FONT, root["fonts"]); @@ -198,8 +200,8 @@ void AssetsLoader::processPreloadConfig(const fs::path& file) { } void AssetsLoader::processPreloadConfigs(const Content* content) { - auto preloadFile = paths->getMainRoot() / fs::path("preload.json"); - if (fs::exists(preloadFile)) { + auto preloadFile = paths->getMainRoot() / "preload.json"; + if (io::exists(preloadFile)) { processPreloadConfig(preloadFile); } if (content == nullptr) { @@ -210,8 +212,8 @@ void AssetsLoader::processPreloadConfigs(const Content* content) { continue; } const auto& pack = entry.second; - auto preloadFile = pack->getInfo().folder / fs::path("preload.json"); - if (fs::exists(preloadFile)) { + auto preloadFile = pack->getInfo().folder / "preload.json"; + if (io::exists(preloadFile)) { processPreloadConfig(preloadFile); } } @@ -230,8 +232,8 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) { for (auto& entry : content->getPacks()) { auto pack = entry.second.get(); auto& info = pack->getInfo(); - fs::path folder = info.folder / fs::path("layouts"); - addLayouts(pack->getEnvironment(), info.id, folder, loader); + io::path folder = info.folder / "layouts"; + add_layouts(pack->getEnvironment(), info.id, folder, loader); } for (auto& entry : content->getSkeletons()) { @@ -274,20 +276,20 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) { bool AssetsLoader::loadExternalTexture( Assets* assets, const std::string& name, - const std::vector& alternatives + const std::vector& alternatives ) { if (assets->get(name) != nullptr) { return true; } for (auto& path : alternatives) { - if (fs::exists(path)) { + if (io::exists(path)) { try { auto image = imageio::read(path); assets->store(Texture::from(image.get()), name); return true; } catch (const std::exception& err) { logger.error() << "error while loading external " - << path.u8string() << ": " << err.what(); + << path.string() << ": " << err.what(); } } } diff --git a/src/assets/AssetsLoader.hpp b/src/assets/AssetsLoader.hpp index cd6db950..bf117a3d 100644 --- a/src/assets/AssetsLoader.hpp +++ b/src/assets/AssetsLoader.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -14,6 +13,7 @@ #include "typedefs.hpp" #include "Assets.hpp" #include "data/dv.hpp" +#include "io/fwd.hpp" class ResPaths; class AssetsLoader; @@ -73,7 +73,7 @@ class AssetsLoader { AssetType tag, const std::string& name, const dv::value& map ); void processPreloadList(AssetType tag, const dv::value& list); - void processPreloadConfig(const std::filesystem::path& file); + void processPreloadConfig(const io::path& file); void processPreloadConfigs(const Content* content); public: AssetsLoader(Assets* assets, const ResPaths* paths); @@ -109,6 +109,6 @@ public: static bool loadExternalTexture( Assets* assets, const std::string& name, - const std::vector& alternatives + const std::vector& alternatives ); }; diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index 929d5e62..f8bcd84d 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -48,16 +48,14 @@ assetload::postfunc assetload::texture( const std::string& name, const std::shared_ptr& ) { - auto actualFile = paths->find(filename + ".png").u8string(); + auto actualFile = paths->find(filename + ".png"); try { - std::shared_ptr image( - imageio::read(fs::u8path(actualFile)).release() - ); + std::shared_ptr image(imageio::read(actualFile).release()); return [name, image, actualFile](auto assets) { assets->store(Texture::from(image.get()), name); }; } catch (const std::runtime_error& err) { - logger.error() << actualFile << ": " << err.what(); + logger.error() << actualFile.string() << ": " << err.what(); return [](auto) {}; } } @@ -69,8 +67,8 @@ assetload::postfunc assetload::shader( const std::string& name, const std::shared_ptr& ) { - fs::path vertexFile = paths->find(filename + ".glslv"); - fs::path fragmentFile = paths->find(filename + ".glslf"); + io::path vertexFile = paths->find(filename + ".glslv"); + io::path fragmentFile = paths->find(filename + ".glslf"); std::string vertexSource = io::read_string(vertexFile); std::string fragmentSource = io::read_string(fragmentFile); @@ -82,8 +80,8 @@ assetload::postfunc assetload::shader( return [=](auto assets) { assets->store( Shader::create( - vertexFile.u8string(), - fragmentFile.u8string(), + vertexFile.string(), + fragmentFile.string(), vertexSource, fragmentSource ), @@ -92,8 +90,8 @@ assetload::postfunc assetload::shader( }; } -static bool append_atlas(AtlasBuilder& atlas, const fs::path& file) { - std::string name = file.stem().string(); +static bool append_atlas(AtlasBuilder& atlas, const io::path& file) { + std::string name = file.stem(); // skip duplicates if (atlas.has(name)) { return false; @@ -114,19 +112,19 @@ assetload::postfunc assetload::atlas( auto atlasConfig = std::dynamic_pointer_cast(config); if (atlasConfig && atlasConfig->type == AtlasType::SEPARATE) { for (const auto& file : paths->listdir(directory)) { - if (!imageio::is_read_supported(file.extension().u8string())) + if (!imageio::is_read_supported(file.extension())) continue; loader->add( AssetType::TEXTURE, - directory + "/" + file.stem().u8string(), - name + "/" + file.stem().u8string() + directory + "/" + file.stem(), + name + "/" + file.stem() ); } return [](auto){}; } AtlasBuilder builder; for (const auto& file : paths->listdir(directory)) { - if (!imageio::is_read_supported(file.extension().u8string())) continue; + if (!imageio::is_read_supported(file.extension())) continue; if (!append_atlas(builder, file)) continue; } std::set names = builder.getNames(); @@ -151,7 +149,7 @@ assetload::postfunc assetload::font( for (size_t i = 0; i <= 1024; i++) { std::string pagefile = filename + "_" + std::to_string(i) + ".png"; auto file = paths->find(pagefile); - if (fs::exists(file)) { + if (io::exists(file)) { pages->push_back(imageio::read(file)); } else if (i == 0) { throw std::runtime_error("font must have page 0"); @@ -222,13 +220,13 @@ assetload::postfunc assetload::sound( extension = extensions[i]; // looking for 'sound_name' as base sound auto soundFile = paths->find(file + extension); - if (fs::exists(soundFile)) { + if (io::exists(soundFile)) { baseSound = audio::load_sound(soundFile, keepPCM); break; } // looking for 'sound_name_0' as base sound auto variantFile = paths->find(file + "_0" + extension); - if (fs::exists(variantFile)) { + if (io::exists(variantFile)) { baseSound = audio::load_sound(variantFile, keepPCM); break; } @@ -241,7 +239,7 @@ assetload::postfunc assetload::sound( for (uint i = 1;; i++) { auto variantFile = paths->find(file + "_" + std::to_string(i) + extension); - if (!fs::exists(variantFile)) { + if (!io::exists(variantFile)) { break; } baseSound->variants.emplace_back(audio::load_sound(variantFile, keepPCM)); @@ -272,9 +270,9 @@ assetload::postfunc assetload::model( const std::shared_ptr& ) { auto path = paths->find(file + ".vec3"); - if (fs::exists(path)) { + if (io::exists(path)) { auto bytes = io::read_bytes_buffer(path); - auto modelVEC3 = std::make_shared(vec3::load(path.u8string(), bytes)); + auto modelVEC3 = std::make_shared(vec3::load(path.string(), bytes)); return [loader, name, modelVEC3=std::move(modelVEC3)](Assets* assets) { for (auto& [modelName, model] : modelVEC3->models) { request_textures(loader, model.model); @@ -294,7 +292,7 @@ assetload::postfunc assetload::model( path = paths->find(file + ".obj"); auto text = io::read_string(path); try { - auto model = obj::parse(path.u8string(), text).release(); + auto model = obj::parse(path.string(), text).release(); return [=](Assets* assets) { request_textures(loader, *model); assets->store(std::unique_ptr(model), name); @@ -394,21 +392,21 @@ static bool load_animation( std::string animsDir = directory + "/animation"; for (const auto& folder : paths->listdir(animsDir)) { - if (!fs::is_directory(folder)) continue; - if (folder.filename().u8string() != name) continue; - if (fs::is_empty(folder)) continue; + if (!io::is_directory(folder)) continue; + if (folder.name() != name) continue; + //FIXME: if (fs::is_empty(folder)) continue; AtlasBuilder builder; append_atlas(builder, paths->find(directory + "/" + name + ".png")); std::vector> frameList; - std::string animFile = folder.u8string() + "/animation.json"; - if (fs::exists(animFile)) { + std::string animFile = folder.string() + "/animation.json"; + if (io::exists(animFile)) { read_anim_file(animFile, frameList); } for (const auto& file : paths->listdir(animsDir + "/" + name)) { if (!frameList.empty() && - !contains(frameList, file.stem().u8string())) { + !contains(frameList, file.stem())) { continue; } if (!append_atlas(builder, file)) continue; diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 8ac8b713..761557af 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -4,6 +4,7 @@ #include #include +#include "io/io.hpp" #include "coders/ogg.hpp" #include "coders/wav.hpp" #include "AL/ALAudio.hpp" @@ -181,11 +182,11 @@ void audio::initialize(bool enabled, AudioSettings& settings) { } } -std::unique_ptr audio::load_PCM(const fs::path& file, bool headerOnly) { - if (!fs::exists(file)) { - throw std::runtime_error("file not found '" + file.u8string() + "'"); +std::unique_ptr audio::load_PCM(const io::path& file, bool headerOnly) { + if (!io::exists(file)) { + throw std::runtime_error("file not found '" + file.string() + "'"); } - std::string ext = file.extension().u8string(); + std::string ext = file.extension(); if (ext == ".wav" || ext == ".WAV") { return wav::load_pcm(file, headerOnly); } else if (ext == ".ogg" || ext == ".OGG") { @@ -194,7 +195,7 @@ std::unique_ptr audio::load_PCM(const fs::path& file, bool headerOnly) { throw std::runtime_error("unsupported audio format"); } -std::unique_ptr audio::load_sound(const fs::path& file, bool keepPCM) { +std::unique_ptr audio::load_sound(const io::path& file, bool keepPCM) { std::shared_ptr pcm( load_PCM(file, !keepPCM && backend->isDummy()).release() ); @@ -207,8 +208,8 @@ std::unique_ptr audio::create_sound( return backend->createSound(std::move(pcm), keepPCM); } -std::unique_ptr audio::open_PCM_stream(const fs::path& file) { - std::string ext = file.extension().u8string(); +std::unique_ptr audio::open_PCM_stream(const io::path& file) { + std::string ext = file.extension(); if (ext == ".wav" || ext == ".WAV") { return wav::create_stream(file); } else if (ext == ".ogg" || ext == ".OGG") { @@ -218,7 +219,7 @@ std::unique_ptr audio::open_PCM_stream(const fs::path& file) { } std::unique_ptr audio::open_stream( - const fs::path& file, bool keepSource + const io::path& file, bool keepSource ) { if (!keepSource && backend->isDummy()) { auto header = load_PCM(file, true); @@ -338,7 +339,7 @@ speakerid_t audio::play( } speakerid_t audio::play_stream( - const fs::path& file, + const io::path& file, glm::vec3 position, bool relative, float volume, diff --git a/src/audio/audio.hpp b/src/audio/audio.hpp index 37ae8e00..5893c334 100644 --- a/src/audio/audio.hpp +++ b/src/audio/audio.hpp @@ -1,14 +1,12 @@ #pragma once -#include #include #include #include #include "typedefs.hpp" #include "settings.hpp" - -namespace fs = std::filesystem; +#include "io/fwd.hpp" namespace audio { /// @brief playing speaker uid @@ -365,7 +363,7 @@ namespace audio { /// @param headerOnly read header only /// @throws std::runtime_error if I/O error ocurred or format is unknown /// @return PCM audio data - std::unique_ptr load_PCM(const fs::path& file, bool headerOnly); + std::unique_ptr load_PCM(const io::path& file, bool headerOnly); /// @brief Load sound from file /// @param file audio file path @@ -373,7 +371,7 @@ namespace audio { /// Sound::getPCM /// @throws std::runtime_error if I/O error ocurred or format is unknown /// @return new Sound instance - std::unique_ptr load_sound(const fs::path& file, bool keepPCM); + std::unique_ptr load_sound(const io::path& file, bool keepPCM); /// @brief Create new sound from PCM data /// @param pcm PCM data @@ -386,14 +384,14 @@ namespace audio { /// @param file audio file path /// @throws std::runtime_error if I/O error ocurred or format is unknown /// @return new PCMStream instance - std::unique_ptr open_PCM_stream(const fs::path& file); + std::unique_ptr open_PCM_stream(const io::path& file); /// @brief Open new audio stream from file /// @param file audio file path /// @param keepSource store PCMStream in stream to make it accessible with /// Stream::getSource /// @return new Stream instance - std::unique_ptr open_stream(const fs::path& file, bool keepSource); + std::unique_ptr open_stream(const io::path& file, bool keepSource); /// @brief Open new audio stream from source /// @param stream PCM data source @@ -464,7 +462,7 @@ namespace audio { /// @param channel channel index /// @return speaker id or 0 speakerid_t play_stream( - const fs::path& file, + const io::path& file, glm::vec3 position, bool relative, float volume, diff --git a/src/coders/GLSLExtension.cpp b/src/coders/GLSLExtension.cpp index c7f45cd7..b36e7a07 100644 --- a/src/coders/GLSLExtension.cpp +++ b/src/coders/GLSLExtension.cpp @@ -6,12 +6,9 @@ #include #include "io/engine_paths.hpp" -#include "io/io.hpp" #include "typedefs.hpp" #include "util/stringutil.hpp" -namespace fs = std::filesystem; - void GLSLExtension::setVersion(std::string version) { this->version = std::move(version); } @@ -21,7 +18,7 @@ void GLSLExtension::setPaths(const ResPaths* paths) { } void GLSLExtension::loadHeader(const std::string& name) { - fs::path file = paths->find("shaders/lib/" + name + ".glsl"); + io::path file = paths->find("shaders/lib/" + name + ".glsl"); std::string source = io::read_string(file); addHeader(name, ""); addHeader(name, process(file, source, true)); @@ -66,7 +63,7 @@ void GLSLExtension::undefine(const std::string& name) { } inline std::runtime_error parsing_error( - const fs::path& file, uint linenum, const std::string& message + const io::path& file, uint linenum, const std::string& message ) { return std::runtime_error( "file " + file.string() + ": " + message + " at line " + @@ -75,7 +72,7 @@ inline std::runtime_error parsing_error( } inline void parsing_warning( - const fs::path& file, uint linenum, const std::string& message + const io::path& file, uint linenum, const std::string& message ) { std::cerr << "file " + file.string() + ": warning: " + message + " at line " + std::to_string(linenum) @@ -87,7 +84,7 @@ inline void source_line(std::stringstream& ss, uint linenum) { } std::string GLSLExtension::process( - const fs::path& file, const std::string& source, bool header + const io::path& file, const std::string& source, bool header ) { std::stringstream ss; size_t pos = 0; diff --git a/src/coders/GLSLExtension.hpp b/src/coders/GLSLExtension.hpp index 6187d7d4..c619c9d0 100644 --- a/src/coders/GLSLExtension.hpp +++ b/src/coders/GLSLExtension.hpp @@ -1,10 +1,11 @@ #pragma once -#include #include #include #include +#include "io/io.hpp" + class ResPaths; class GLSLExtension { @@ -29,7 +30,7 @@ public: bool hasDefine(const std::string& name) const; std::string process( - const std::filesystem::path& file, + const io::path& file, const std::string& source, bool header = false ); diff --git a/src/coders/imageio.cpp b/src/coders/imageio.cpp index 5f082abc..36e1f76d 100644 --- a/src/coders/imageio.cpp +++ b/src/coders/imageio.cpp @@ -1,6 +1,5 @@ #include "imageio.hpp" -#include #include #include @@ -8,8 +7,6 @@ #include "io/io.hpp" #include "png.hpp" -namespace fs = std::filesystem; - using image_reader = std::function(const ubyte*, size_t)>; using image_writer = std::function; @@ -30,33 +27,29 @@ bool imageio::is_write_supported(const std::string& extension) { return writers.find(extension) != writers.end(); } -inline std::string extensionOf(const std::string& filename) { - return fs::u8path(filename).extension().u8string(); -} - -std::unique_ptr imageio::read(const fs::path& filename) { - auto found = readers.find(extensionOf(filename.u8string())); +std::unique_ptr imageio::read(const io::path& file) { + auto found = readers.find(file.extension()); if (found == readers.end()) { throw std::runtime_error( - "file format is not supported (read): " + filename.u8string() + "file format is not supported (read): " + file.string() ); } - auto bytes = io::read_bytes_buffer(filename); + auto bytes = io::read_bytes_buffer(file); try { return std::unique_ptr(found->second(bytes.data(), bytes.size())); } catch (const std::runtime_error& err) { throw std::runtime_error( - "could not to load image " + filename.u8string() + ": " + err.what() + "could not to load image " + file.string() + ": " + err.what() ); } } -void imageio::write(const std::string& filename, const ImageData* image) { - auto found = writers.find(extensionOf(filename)); +void imageio::write(const io::path& file, const ImageData* image) { + auto found = writers.find(file.extension()); if (found == writers.end()) { throw std::runtime_error( - "file format is not supported (write): " + filename + "file format is not supported (write): " + file.string() ); } - return found->second(filename, image); + return found->second(io::resolve(file).u8string(), image); } diff --git a/src/coders/imageio.hpp b/src/coders/imageio.hpp index beab88ad..0e59ae37 100644 --- a/src/coders/imageio.hpp +++ b/src/coders/imageio.hpp @@ -2,7 +2,8 @@ #include #include -#include + +#include "io/fwd.hpp" class ImageData; @@ -12,6 +13,6 @@ namespace imageio { bool is_read_supported(const std::string& extension); bool is_write_supported(const std::string& extension); - std::unique_ptr read(const std::filesystem::path& file); - void write(const std::string& filename, const ImageData* image); + std::unique_ptr read(const io::path& file); + void write(const io::path& file, const ImageData* image); } diff --git a/src/coders/ogg.cpp b/src/coders/ogg.cpp index df5f9826..c5c27f49 100644 --- a/src/coders/ogg.cpp +++ b/src/coders/ogg.cpp @@ -5,6 +5,7 @@ #include +#include "io/io.hpp" #include "audio/audio.hpp" #include "debug/Logger.hpp" #include "typedefs.hpp" @@ -43,11 +44,11 @@ static inline std::string vorbis_error_message(int code) { } std::unique_ptr ogg::load_pcm( - const fs::path& file, bool headerOnly + const io::path& file, bool headerOnly ) { OggVorbis_File vf; int code; - if ((code = ov_fopen(file.u8string().c_str(), &vf))) { + if ((code = ov_fopen(io::resolve(file).u8string().c_str(), &vf))) { throw std::runtime_error("vorbis: " + vorbis_error_message(code)); } std::vector data; @@ -166,10 +167,10 @@ public: } }; -std::unique_ptr ogg::create_stream(const fs::path& file) { +std::unique_ptr ogg::create_stream(const io::path& file) { OggVorbis_File vf; int code; - if ((code = ov_fopen(file.u8string().c_str(), &vf))) { + if ((code = ov_fopen(io::resolve(file).u8string().c_str(), &vf))) { throw std::runtime_error("vorbis: " + vorbis_error_message(code)); } return std::make_unique(vf); diff --git a/src/coders/ogg.hpp b/src/coders/ogg.hpp index 67ea2458..86a807e7 100644 --- a/src/coders/ogg.hpp +++ b/src/coders/ogg.hpp @@ -1,6 +1,8 @@ #pragma once -#include +#include + +#include "io/fwd.hpp" namespace audio { struct PCM; @@ -9,9 +11,9 @@ namespace audio { namespace ogg { std::unique_ptr load_pcm( - const std::filesystem::path& file, bool headerOnly + const io::path& file, bool headerOnly ); std::unique_ptr create_stream( - const std::filesystem::path& file + const io::path& file ); } diff --git a/src/coders/png.cpp b/src/coders/png.cpp index 6671922c..ddf5fea8 100644 --- a/src/coders/png.cpp +++ b/src/coders/png.cpp @@ -212,7 +212,7 @@ std::unique_ptr png::load_texture(const ubyte* bytes, size_t size) { } std::unique_ptr png::load_texture(const std::string& filename) { - auto bytes = io::read_bytes_buffer(fs::u8path(filename)); + auto bytes = io::read_bytes_buffer(filename); try { return load_texture(bytes.data(), bytes.size()); } catch (const std::runtime_error& err) { diff --git a/src/coders/wav.cpp b/src/coders/wav.cpp index 6ba337fd..1e260a0c 100644 --- a/src/coders/wav.cpp +++ b/src/coders/wav.cpp @@ -6,6 +6,7 @@ #include #include +#include "io/io.hpp" #include "audio/audio.hpp" #include "debug/Logger.hpp" @@ -118,11 +119,11 @@ public: } }; -std::unique_ptr wav::create_stream(const fs::path& file) { - std::ifstream in(file, std::ios::binary); +std::unique_ptr wav::create_stream(const io::path& file) { + std::ifstream in(io::resolve(file), std::ios::binary); if (!in.is_open()) { throw std::runtime_error( - "could not to open file '" + file.u8string() + "'" + "could not to open file '" + file.string() + "'" ); } @@ -234,7 +235,7 @@ std::unique_ptr wav::create_stream(const fs::path& file) { } std::unique_ptr wav::load_pcm( - const fs::path& file, bool headerOnly + const io::path& file, bool headerOnly ) { auto stream = wav::create_stream(file); diff --git a/src/coders/wav.hpp b/src/coders/wav.hpp index 5f5270c2..218b2447 100644 --- a/src/coders/wav.hpp +++ b/src/coders/wav.hpp @@ -1,6 +1,8 @@ #pragma once -#include +#include + +#include "io/fwd.hpp" namespace audio { struct PCM; @@ -9,9 +11,9 @@ namespace audio { namespace wav { std::unique_ptr load_pcm( - const std::filesystem::path& file, bool headerOnly + const io::path& file, bool headerOnly ); std::unique_ptr create_stream( - const std::filesystem::path& file + const io::path& file ); } diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 1603334d..3c993786 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -43,55 +43,57 @@ ContentLoader::ContentLoader( } static void detect_defs( - const fs::path& folder, + const io::path& folder, const std::string& prefix, std::vector& detected ) { - if (fs::is_directory(folder)) { - for (const auto& entry : fs::directory_iterator(folder)) { - const fs::path& file = entry.path(); - std::string name = file.stem().string(); - if (name[0] == '_') { - continue; - } - if (fs::is_regular_file(file) && io::is_data_file(file)) { - auto map = io::read_object(file); - std::string id = prefix.empty() ? name : prefix + ":" + name; - detected.emplace_back(id); - } else if (fs::is_directory(file) && - file.extension() != fs::u8path(".files")) { - detect_defs(file, name, detected); - } + if (!io::is_directory(folder)) { + return; + } + for (const auto& entry : std::filesystem::directory_iterator(io::resolve(folder))) { + io::path file = folder / entry.path().filename().u8string(); + std::string name = file.stem(); + if (name[0] == '_') { + continue; + } + if (io::is_regular_file(file) && io::is_data_file(file)) { + auto map = io::read_object(file); + std::string id = prefix.empty() ? name : prefix + ":" + name; + detected.emplace_back(id); + } else if (io::is_directory(file) && + file.extension() != fs::u8path(".files")) { + detect_defs(file, name, detected); } } } static void detect_defs_pairs( - const fs::path& folder, + const io::path& folder, const std::string& prefix, std::vector>& detected ) { - if (fs::is_directory(folder)) { - for (const auto& entry : fs::directory_iterator(folder)) { - const fs::path& file = entry.path(); - std::string name = file.stem().string(); - if (name[0] == '_') { - continue; - } - if (fs::is_regular_file(file) && io::is_data_file(file)) { - try { - auto map = io::read_object(file); - auto id = prefix.empty() ? name : prefix + ":" + name; - auto caption = util::id_to_caption(id); - map.at("caption").get(caption); - detected.emplace_back(id, name); - } catch (const std::runtime_error& err) { - logger.error() << err.what(); - } - } else if (fs::is_directory(file) && - file.extension() != fs::u8path(".files")) { - detect_defs_pairs(file, name, detected); + if (!io::is_directory(folder)) { + return; + } + for (const auto& entry : fs::directory_iterator(io::resolve(folder))) { + io::path file = folder / entry.path().filename().u8string(); + std::string name = file.stem(); + if (name[0] == '_') { + continue; + } + if (io::is_regular_file(file) && io::is_data_file(file)) { + try { + auto map = io::read_object(file); + auto id = prefix.empty() ? name : prefix + ":" + name; + auto caption = util::id_to_caption(id); + map.at("caption").get(caption); + detected.emplace_back(id, name); + } catch (const std::runtime_error& err) { + logger.error() << err.what(); } + } else if (io::is_directory(file) && + file.extension() != fs::u8path(".files")) { + detect_defs_pairs(file, name, detected); } } } @@ -106,7 +108,7 @@ std::vector> ContentLoader::scanContent( } bool ContentLoader::fixPackIndices( - const fs::path& folder, + const io::path& folder, dv::value& indicesRoot, const std::string& contentSection ) { @@ -146,7 +148,7 @@ void ContentLoader::fixPackIndices() { auto entitiesFolder = folder / ContentPack::ENTITIES_FOLDER; dv::value root; - if (fs::is_regular_file(contentFile)) { + if (io::is_regular_file(contentFile)) { root = io::read_json(contentFile); } else { root = dv::object(); @@ -213,7 +215,7 @@ static void process_method( } void ContentLoader::loadBlock( - Block& def, const std::string& name, const fs::path& file + Block& def, const std::string& name, const io::path& file ) { auto root = io::read_json(file); if (def.properties == nullptr) { @@ -402,7 +404,7 @@ void ContentLoader::loadBlock( } void ContentLoader::loadItem( - ItemDef& def, const std::string& name, const fs::path& file + ItemDef& def, const std::string& name, const io::path& file ) { auto root = io::read_json(file); def.properties = root; @@ -446,7 +448,7 @@ void ContentLoader::loadItem( } void ContentLoader::loadEntity( - EntityDef& def, const std::string& name, const fs::path& file + EntityDef& def, const std::string& name, const io::path& file ) { auto root = io::read_json(file); @@ -518,16 +520,16 @@ void ContentLoader::loadEntity( EntityDef& def, const std::string& full, const std::string& name ) { auto folder = pack->folder; - auto configFile = folder / fs::path("entities/" + name + ".json"); - if (fs::exists(configFile)) loadEntity(def, full, configFile); + auto configFile = folder / ("entities/" + name + ".json"); + if (io::exists(configFile)) loadEntity(def, full, configFile); } void ContentLoader::loadBlock( Block& def, const std::string& full, const std::string& name ) { auto folder = pack->folder; - auto configFile = folder / fs::path("blocks/" + name + ".json"); - if (fs::exists(configFile)) loadBlock(def, full, configFile); + auto configFile = folder / ("blocks/" + name + ".json"); + if (io::exists(configFile)) loadBlock(def, full, configFile); if (!def.hidden) { bool created; @@ -549,8 +551,8 @@ void ContentLoader::loadItem( ItemDef& def, const std::string& full, const std::string& name ) { auto folder = pack->folder; - auto configFile = folder / fs::path("items/" + name + ".json"); - if (fs::exists(configFile)) loadItem(def, full, configFile); + auto configFile = folder / ("items/" + name + ".json"); + if (io::exists(configFile)) loadItem(def, full, configFile); } static std::tuple create_unit_id( @@ -566,7 +568,7 @@ static std::tuple create_unit_id( } void ContentLoader::loadBlockMaterial( - BlockMaterial& def, const fs::path& file + BlockMaterial& def, const io::path& file ) { auto root = io::read_json(file); root.at("steps-sound").get(def.stepsSound); @@ -577,9 +579,9 @@ void ContentLoader::loadBlockMaterial( void ContentLoader::loadContent(const dv::value& root) { std::vector> pendingDefs; auto getJsonParent = [this](const std::string& prefix, const std::string& name) { - auto configFile = pack->folder / fs::path(prefix + "/" + name + ".json"); + auto configFile = pack->folder / (prefix + "/" + name + ".json"); std::string parent; - if (fs::exists(configFile)) { + if (io::exists(configFile)) { auto root = io::read_json(configFile); root.at("parent").get(parent); } @@ -740,16 +742,17 @@ void ContentLoader::loadContent(const dv::value& root) { } static inline void foreach_file( - const fs::path& dir, std::function handler + const io::path& dir, std::function handler ) { - if (fs::is_directory(dir)) { - for (const auto& entry : fs::directory_iterator(dir)) { - const auto& path = entry.path(); - if (fs::is_directory(path)) { - continue; - } - handler(path); + if (!io::is_directory(dir)) { + return; + } + for (const auto& entry : fs::directory_iterator(io::resolve(dir))) { + io::path path = dir / entry.path().filename().u8string(); + if (io::is_directory(path)) { + continue; } + handler(path); } } @@ -761,11 +764,10 @@ void ContentLoader::load() { auto folder = pack->folder; // Load world generators - fs::path generatorsDir = folder / fs::u8path("generators"); - foreach_file(generatorsDir, [this](const fs::path& file) { - std::string name = file.stem().u8string(); - auto [packid, full, filename] = - create_unit_id(pack->id, file.stem().u8string()); + io::path generatorsDir = folder / fs::u8path("generators"); + foreach_file(generatorsDir, [this](const io::path& file) { + std::string name = file.stem(); + auto [packid, full, filename] = create_unit_id(pack->id, name); auto& def = builder.generators.create(full); try { @@ -776,8 +778,8 @@ void ContentLoader::load() { }); // Load pack resources.json - fs::path resourcesFile = folder / fs::u8path("resources.json"); - if (fs::exists(resourcesFile)) { + io::path resourcesFile = folder / "resources.json"; + if (io::exists(resourcesFile)) { auto resRoot = io::read_json(resourcesFile); for (const auto& [key, arr] : resRoot.asObject()) { if (auto resType = ResourceType_from(key)) { @@ -790,8 +792,8 @@ void ContentLoader::load() { } // Load pack resources aliases - fs::path aliasesFile = folder / fs::u8path("resource-aliases.json"); - if (fs::exists(aliasesFile)) { + io::path aliasesFile = folder / "resource-aliases.json"; + if (io::exists(aliasesFile)) { auto resRoot = io::read_json(aliasesFile); for (const auto& [key, arr] : resRoot.asObject()) { if (auto resType = ResourceType_from(key)) { @@ -804,32 +806,32 @@ void ContentLoader::load() { } // Load block materials - fs::path materialsDir = folder / fs::u8path("block_materials"); - if (fs::is_directory(materialsDir)) { - for (const auto& entry : fs::directory_iterator(materialsDir)) { - const auto& file = entry.path(); + io::path materialsDir = folder / "block_materials"; + if (io::is_directory(materialsDir)) { + for (const auto& entry : fs::directory_iterator(io::resolve(materialsDir))) { + io::path file = materialsDir / entry.path().filename().u8string(); auto [packid, full, filename] = - create_unit_id(pack->id, file.stem().u8string()); + create_unit_id(pack->id, file.stem()); loadBlockMaterial( builder.createBlockMaterial(full), - materialsDir / fs::u8path(filename + ".json") + materialsDir / (filename + ".json") ); } } // Load skeletons - fs::path skeletonsDir = folder / fs::u8path("skeletons"); - foreach_file(skeletonsDir, [this](const fs::path& file) { - std::string name = pack->id + ":" + file.stem().u8string(); + io::path skeletonsDir = folder / fs::u8path("skeletons"); + foreach_file(skeletonsDir, [this](const io::path& file) { + std::string name = pack->id + ":" + file.stem(); std::string text = io::read_string(file); builder.add( - rigging::SkeletonConfig::parse(text, file.u8string(), name) + rigging::SkeletonConfig::parse(text, file.string(), name) ); }); // Process content.json and load defined content units auto contentFile = pack->getContentFile(); - if (fs::exists(contentFile)) { + if (io::exists(contentFile)) { loadContent(io::read_json(contentFile)); } } @@ -844,8 +846,8 @@ static void load_scripts(Content& content, ContentUnitDefs& units) { const auto runtime = content.getPackRuntime(name.substr(0, pos)); const auto& pack = runtime->getInfo(); const auto& folder = pack.folder; - auto scriptfile = folder / fs::path("scripts/" + def->scriptName + ".lua"); - if (fs::is_regular_file(scriptfile)) { + auto scriptfile = folder / ("scripts/" + def->scriptName + ".lua"); + if (io::is_regular_file(scriptfile)) { scripting::load_content_script( runtime->getEnvironment(), name, @@ -866,8 +868,8 @@ void ContentLoader::loadScripts(Content& content) { const auto& folder = pack.folder; // Load main world script - fs::path scriptFile = folder / fs::path("scripts/world.lua"); - if (fs::is_regular_file(scriptFile)) { + io::path scriptFile = folder / "scripts/world.lua"; + if (io::is_regular_file(scriptFile)) { scripting::load_world_script( runtime->getEnvironment(), pack.id, @@ -877,13 +879,13 @@ void ContentLoader::loadScripts(Content& content) { ); } // Load entity components - fs::path componentsDir = folder / fs::u8path("scripts/components"); - foreach_file(componentsDir, [&pack](const fs::path& file) { - auto name = pack.id + ":" + file.stem().u8string(); + io::path componentsDir = folder / fs::u8path("scripts/components"); + foreach_file(componentsDir, [&pack](const io::path& file) { + auto name = pack.id + ":" + file.stem(); scripting::load_entity_component( name, file, - pack.id + ":scripts/components/" + file.filename().u8string() + pack.id + ":scripts/components/" + file.name() ); }); } diff --git a/src/content/ContentLoader.hpp b/src/content/ContentLoader.hpp index d99aa622..f1135ee3 100644 --- a/src/content/ContentLoader.hpp +++ b/src/content/ContentLoader.hpp @@ -1,14 +1,12 @@ #pragma once -#include #include #include +#include "io/io.hpp" #include "content_fwd.hpp" #include "data/dv.hpp" -namespace fs = std::filesystem; - class Block; struct BlockMaterial; struct ItemDef; @@ -43,15 +41,15 @@ class ContentLoader { GeneratorDef& def, const std::string& full, const std::string& name ); - static void loadBlockMaterial(BlockMaterial& def, const fs::path& file); + static void loadBlockMaterial(BlockMaterial& def, const io::path& file); void loadBlock( - Block& def, const std::string& name, const fs::path& file + Block& def, const std::string& name, const io::path& file ); void loadItem( - ItemDef& def, const std::string& name, const fs::path& file + ItemDef& def, const std::string& name, const io::path& file ); void loadEntity( - EntityDef& def, const std::string& name, const fs::path& file + EntityDef& def, const std::string& name, const io::path& file ); void loadResources(ResourceType type, const dv::value& list); void loadResourceAliases(ResourceType type, const dv::value& aliases); @@ -66,7 +64,7 @@ public: // Refresh pack content.json static bool fixPackIndices( - const fs::path& folder, + const io::path& folder, dv::value& indicesRoot, const std::string& contentSection ); diff --git a/src/content/ContentPack.cpp b/src/content/ContentPack.cpp index 9bcec23c..51960609 100644 --- a/src/content/ContentPack.cpp +++ b/src/content/ContentPack.cpp @@ -15,7 +15,7 @@ namespace fs = std::filesystem; ContentPack ContentPack::createCore(const EnginePaths& paths) { return ContentPack { - "core", "Core", ENGINE_VERSION_STRING, "", "", paths.getResourcesFolder(), "res:", {} + "core", "Core", ENGINE_VERSION_STRING, "", "", "res:", "res:", {} }; } @@ -23,7 +23,7 @@ const std::vector ContentPack::RESERVED_NAMES = { "res", "abs", "local", "core", "user", "world", "none", "null"}; contentpack_error::contentpack_error( - std::string packId, fs::path folder, const std::string& message + std::string packId, io::path folder, const std::string& message ) : std::runtime_error(message), packId(std::move(packId)), @@ -33,19 +33,19 @@ contentpack_error::contentpack_error( std::string contentpack_error::getPackId() const { return packId; } -fs::path contentpack_error::getFolder() const { +io::path contentpack_error::getFolder() const { return folder; } -fs::path ContentPack::getContentFile() const { - return folder / fs::path(CONTENT_FILENAME); +io::path ContentPack::getContentFile() const { + return folder / CONTENT_FILENAME; } -bool ContentPack::is_pack(const fs::path& folder) { - return fs::is_regular_file(folder / fs::path(PACKAGE_FILENAME)); +bool ContentPack::is_pack(const io::path& folder) { + return io::is_regular_file(folder / PACKAGE_FILENAME); } -static void checkContentPackId(const std::string& id, const fs::path& folder) { +static void checkContentPackId(const std::string& id, const io::path& folder) { if (id.length() < 2 || id.length() > 24) throw contentpack_error( id, folder, "content-pack id length is out of range [2, 24]" @@ -70,8 +70,8 @@ static void checkContentPackId(const std::string& id, const fs::path& folder) { } } -ContentPack ContentPack::read(const std::string& path, const fs::path& folder) { - auto root = io::read_json(folder / fs::path(PACKAGE_FILENAME)); +ContentPack ContentPack::read(const std::string& path, const io::path& folder) { + auto root = io::read_json(folder / PACKAGE_FILENAME); ContentPack pack; root.at("id").get(pack.id); root.at("title").get(pack.title); @@ -124,21 +124,21 @@ ContentPack ContentPack::read(const std::string& path, const fs::path& folder) { } void ContentPack::scanFolder( - const std::string& path, const fs::path& folder, std::vector& packs + const std::string& path, const io::path& folder, std::vector& packs ) { - if (!fs::is_directory(folder)) { + if (!io::is_directory(folder)) { return; } - for (const auto& entry : fs::directory_iterator(folder)) { - const fs::path& packFolder = entry.path(); - if (!fs::is_directory(packFolder)) continue; + for (const auto& entry : fs::directory_iterator(io::resolve(folder))) { + io::path packFolder = folder / entry.path().filename().u8string(); + if (!io::is_directory(packFolder)) continue; if (!is_pack(packFolder)) continue; try { packs.push_back( - read(path + "/" + packFolder.filename().string(), packFolder) + read(path + "/" + packFolder.name(), packFolder) ); } catch (const contentpack_error& err) { - std::cerr << "package.json error at " << err.getFolder().u8string(); + std::cerr << "package.json error at " << err.getFolder().string(); std::cerr << ": " << err.what() << std::endl; } catch (const std::runtime_error& err) { std::cerr << err.what() << std::endl; @@ -146,26 +146,26 @@ void ContentPack::scanFolder( } } -std::vector ContentPack::worldPacksList(const fs::path& folder) { - fs::path listfile = folder / fs::path("packs.list"); - if (!fs::is_regular_file(listfile)) { +std::vector ContentPack::worldPacksList(const io::path& folder) { + io::path listfile = folder / "packs.list"; + if (!io::is_regular_file(listfile)) { throw std::runtime_error("missing file 'packs.list'"); } return io::read_list(listfile); } -fs::path ContentPack::findPack( - const EnginePaths* paths, const fs::path& worldDir, const std::string& name +io::path ContentPack::findPack( + const EnginePaths* paths, const io::path& worldDir, const std::string& name ) { - fs::path folder = worldDir / fs::path("content") / fs::path(name); - if (fs::is_directory(folder)) { + io::path folder = worldDir / "content" / name; + if (io::is_directory(folder)) { return folder; } - folder = paths->getUserFilesFolder() / fs::path("content") / fs::path(name); - if (fs::is_directory(folder)) { + folder = io::path("user:content") / name; + if (io::is_directory(folder)) { return folder; } - return paths->getResourcesFolder() / fs::path("content") / fs::path(name); + return io::path("res:content") / name; } ContentPackRuntime::ContentPackRuntime(ContentPack info, scriptenv env) diff --git a/src/content/ContentPack.hpp b/src/content/ContentPack.hpp index e4360bdc..ed7c3eb8 100644 --- a/src/content/ContentPack.hpp +++ b/src/content/ContentPack.hpp @@ -1,27 +1,27 @@ #pragma once -#include #include #include #include #include "typedefs.hpp" #include "content_fwd.hpp" +#include "io/io.hpp" class EnginePaths; class contentpack_error : public std::runtime_error { std::string packId; - std::filesystem::path folder; + io::path folder; public: contentpack_error( std::string packId, - std::filesystem::path folder, + io::path folder, const std::string& message ); std::string getPackId() const; - std::filesystem::path getFolder() const; + io::path getFolder() const; }; enum class DependencyLevel { @@ -42,52 +42,52 @@ struct ContentPack { std::string version = "0.0"; std::string creator = ""; std::string description = "no description"; - std::filesystem::path folder; + io::path folder; std::string path; std::vector dependencies; std::string source = ""; - std::filesystem::path getContentFile() const; + io::path getContentFile() const; static inline const std::string PACKAGE_FILENAME = "package.json"; static inline const std::string CONTENT_FILENAME = "content.json"; - static inline const std::filesystem::path BLOCKS_FOLDER = "blocks"; - static inline const std::filesystem::path ITEMS_FOLDER = "items"; - static inline const std::filesystem::path ENTITIES_FOLDER = "entities"; - static inline const std::filesystem::path GENERATORS_FOLDER = "generators"; + static inline const io::path BLOCKS_FOLDER = "blocks"; + static inline const io::path ITEMS_FOLDER = "items"; + static inline const io::path ENTITIES_FOLDER = "entities"; + static inline const io::path GENERATORS_FOLDER = "generators"; static const std::vector RESERVED_NAMES; - static bool is_pack(const std::filesystem::path& folder); + static bool is_pack(const io::path& folder); static ContentPack read( - const std::string& path, const std::filesystem::path& folder + const std::string& path, const io::path& folder ); static void scanFolder( const std::string& path, - const std::filesystem::path& folder, + const io::path& folder, std::vector& packs ); static std::vector worldPacksList( - const std::filesystem::path& folder + const io::path& folder ); - static std::filesystem::path findPack( + static io::path findPack( const EnginePaths* paths, - const std::filesystem::path& worldDir, + const io::path& worldDir, const std::string& name ); static ContentPack createCore(const EnginePaths&); - static inline std::filesystem::path getFolderFor(ContentType type) { + static inline io::path getFolderFor(ContentType type) { switch (type) { case ContentType::BLOCK: return ContentPack::BLOCKS_FOLDER; case ContentType::ITEM: return ContentPack::ITEMS_FOLDER; case ContentType::ENTITY: return ContentPack::ENTITIES_FOLDER; case ContentType::GENERATOR: return ContentPack::GENERATORS_FOLDER; - case ContentType::NONE: return std::filesystem::u8path(""); - default: return std::filesystem::u8path(""); + case ContentType::NONE: return ""; + default: return ""; } } }; diff --git a/src/content/ContentReport.cpp b/src/content/ContentReport.cpp index c9043507..0d8a20e2 100644 --- a/src/content/ContentReport.cpp +++ b/src/content/ContentReport.cpp @@ -67,7 +67,7 @@ static void process_blocks_data( std::shared_ptr ContentReport::create( const std::shared_ptr& worldFiles, - const fs::path& filename, + const io::path& filename, const Content* content ) { auto worldInfo = worldFiles->readWorldInfo(); diff --git a/src/content/ContentReport.hpp b/src/content/ContentReport.hpp index 62893527..325e5f29 100644 --- a/src/content/ContentReport.hpp +++ b/src/content/ContentReport.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -10,10 +9,10 @@ #include "data/dv.hpp" #include "typedefs.hpp" #include "Content.hpp" +#include "io/io.hpp" #include "data/StructLayout.hpp" #include "world/files/world_regions_fwd.hpp" -namespace fs = std::filesystem; enum class ContentIssueType { REORDER, @@ -139,7 +138,7 @@ public: static std::shared_ptr create( const std::shared_ptr& worldFiles, - const fs::path& filename, + const io::path& filename, const Content* content ); diff --git a/src/content/PacksManager.cpp b/src/content/PacksManager.cpp index fdf39bb3..49c3014c 100644 --- a/src/content/PacksManager.cpp +++ b/src/content/PacksManager.cpp @@ -7,7 +7,7 @@ PacksManager::PacksManager() = default; -void PacksManager::setSources(std::vector> sources) { +void PacksManager::setSources(std::vector> sources) { this->sources = std::move(sources); } @@ -42,7 +42,7 @@ std::vector PacksManager::getAll( for (auto& name : names) { auto found = packs.find(name); if (found == packs.end()) { - throw contentpack_error(name, fs::path(""), "pack not found"); + throw contentpack_error(name, io::path(), "pack not found"); } packsList.push_back(found->second); } @@ -92,7 +92,7 @@ static bool resolve_dependencies( bool exists = found != packs.end(); if (!exists && dep.level == DependencyLevel::required) { throw contentpack_error( - dep.id, fs::path(), "dependency of '" + pack->id + "'" + dep.id, io::path(), "dependency of '" + pack->id + "'" ); } if (!exists) { @@ -127,7 +127,7 @@ std::vector PacksManager::assemble( for (auto& name : names) { auto found = packs.find(name); if (found == packs.end()) { - throw contentpack_error(name, fs::path(""), "pack not found"); + throw contentpack_error(name, io::path(), "pack not found"); } queue.push(&found->second); } diff --git a/src/content/PacksManager.hpp b/src/content/PacksManager.hpp index b60b06ce..32ac43d6 100644 --- a/src/content/PacksManager.hpp +++ b/src/content/PacksManager.hpp @@ -1,21 +1,19 @@ #pragma once -#include #include #include +#include "io/io.hpp" #include "ContentPack.hpp" -namespace fs = std::filesystem; - class PacksManager { std::unordered_map packs; - std::vector> sources; + std::vector> sources; public: PacksManager(); /// @brief Set content packs sources (search folders) - void setSources(std::vector> sources); + void setSources(std::vector> sources); /// @brief Scan sources and collect all found packs excluding duplication. /// Scanning order depends on sources order diff --git a/src/content/loading/GeneratorLoader.cpp b/src/content/loading/GeneratorLoader.cpp index 2e903426..b14b0b2f 100644 --- a/src/content/loading/GeneratorLoader.cpp +++ b/src/content/loading/GeneratorLoader.cpp @@ -132,18 +132,18 @@ static VoxelStructureMeta load_structure_meta( } static std::vector> load_structures( - const dv::value& map, const fs::path& filesFolder, const ResPaths& paths + const dv::value& map, const io::path& filesFolder, const ResPaths& paths ) { - auto structuresDir = filesFolder / fs::path("fragments"); + auto structuresDir = filesFolder / "fragments"; std::vector> structures; for (auto& [name, config] : map.asObject()) { - auto structFile = structuresDir / fs::u8path(name + ".vox"); - structFile = paths.find(structFile.u8string()); - logger.debug() << "loading voxel fragment " << structFile.u8string(); - if (!fs::exists(structFile)) { + auto structFile = structuresDir / (name + ".vox"); + structFile = paths.find(structFile.string()); + logger.debug() << "loading voxel fragment " << structFile.string(); + if (!io::exists(structFile)) { throw std::runtime_error("structure file does not exist (" + - structFile.u8string()); + structFile.string()); } auto fragment = std::make_unique(); fragment->deserialize(io::read_binary_json(structFile)); @@ -162,7 +162,7 @@ static std::vector> load_structures( static void load_structures( GeneratorDef& def, const dv::value& map, - const fs::path& filesFolder, + const io::path& filesFolder, const ResPaths& paths ) { auto rawStructures = load_structures(map, filesFolder, paths); @@ -178,9 +178,9 @@ static void load_structures( } } -static inline const auto STRUCTURES_FILE = fs::u8path("structures.toml"); -static inline const auto BIOMES_FILE = fs::u8path("biomes.toml"); -static inline const auto GENERATORS_DIR = fs::u8path("generators"); +static inline const io::path STRUCTURES_FILE = "structures.toml"; +static inline const io::path BIOMES_FILE = "biomes.toml"; +static inline const io::path GENERATORS_DIR = "generators"; static void load_biomes(GeneratorDef& def, const dv::value& root) { for (const auto& [biomeName, biomeMap] : root.asObject()) { @@ -198,11 +198,11 @@ void ContentLoader::loadGenerator( ) { auto packDir = pack->folder; auto generatorsDir = packDir / GENERATORS_DIR; - auto generatorFile = generatorsDir / fs::u8path(name + ".toml"); - if (!fs::exists(generatorFile)) { + auto generatorFile = generatorsDir / (name + ".toml"); + if (!io::exists(generatorFile)) { return; } - auto map = io::read_toml(generatorsDir / fs::u8path(name + ".toml")); + auto map = io::read_toml(generatorsDir / (name + ".toml")); map.at("caption").get(def.caption); map.at("biome-parameters").get(def.biomeParameters); map.at("biome-bpd").get(def.biomesBPD); @@ -233,15 +233,15 @@ void ContentLoader::loadGenerator( logger.warning() << "generator has heightmap-inputs but biomes-bpd " "is not equal to heights-bpd, generator will work slower!"; } - auto folder = generatorsDir / fs::u8path(name + ".files"); - auto scriptFile = folder / fs::u8path("script.lua"); + auto folder = generatorsDir / (name + ".files"); + auto scriptFile = folder / "script.lua"; - auto structuresFile = GENERATORS_DIR / fs::u8path(name + ".files") / STRUCTURES_FILE; - auto structuresMap = paths.readCombinedObject(structuresFile.u8string()); - load_structures(def, structuresMap, structuresFile.parent_path(), paths); + auto structuresFile = GENERATORS_DIR / (name + ".files") / STRUCTURES_FILE; + auto structuresMap = paths.readCombinedObject(structuresFile.string()); + load_structures(def, structuresMap, structuresFile.parent(), paths); - auto biomesFile = GENERATORS_DIR / fs::u8path(name + ".files") / BIOMES_FILE; - auto biomesMap = paths.readCombinedObject(biomesFile.u8string()); + auto biomesFile = GENERATORS_DIR / (name + ".files") / BIOMES_FILE; + auto biomesMap = paths.readCombinedObject(biomesFile.string()); if (biomesMap.empty()) { throw std::runtime_error( "generator " + util::quote(def.name) + diff --git a/src/core_defs.cpp b/src/core_defs.cpp index 04ef4101..def96d69 100644 --- a/src/core_defs.cpp +++ b/src/core_defs.cpp @@ -11,7 +11,7 @@ #include "voxels/Block.hpp" // All in-game definitions (blocks, items, etc..) -void corecontent::setup(const EnginePaths& paths, ContentBuilder& builder) { +void corecontent::setup(ContentBuilder& builder) { { Block& block = builder.blocks.create(CORE_AIR); block.replaceable = true; @@ -28,10 +28,10 @@ void corecontent::setup(const EnginePaths& paths, ContentBuilder& builder) { item.iconType = ItemIconType::NONE; } - auto bindsFile = paths.getResourcesFolder()/fs::path("bindings.toml"); - if (fs::is_regular_file(bindsFile)) { + auto bindsFile = "res:bindings.toml"; + if (io::is_regular_file(bindsFile)) { Events::loadBindings( - bindsFile.u8string(), io::read_string(bindsFile), BindType::BIND + bindsFile, io::read_string(bindsFile), BindType::BIND ); } diff --git a/src/core_defs.hpp b/src/core_defs.hpp index 8042ca36..f269af10 100644 --- a/src/core_defs.hpp +++ b/src/core_defs.hpp @@ -28,9 +28,8 @@ inline const std::string BIND_PLAYER_FAST_INTERACTOIN = "player.fast_interaction"; inline const std::string BIND_HUD_INVENTORY = "hud.inventory"; -class EnginePaths; class ContentBuilder; namespace corecontent { - void setup(const EnginePaths& paths, ContentBuilder& builder); + void setup(ContentBuilder& builder); } diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index a9aa2b98..7be65cd1 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -52,10 +52,10 @@ static debug::Logger logger("engine"); namespace fs = std::filesystem; -static std::unique_ptr load_icon(const fs::path& resdir) { +static std::unique_ptr load_icon() { try { - auto file = resdir / fs::u8path("textures/misc/icon.png"); - if (fs::exists(file)) { + auto file = "res:textures/misc/icon.png"; + if (io::exists(file)) { return imageio::read(file); } } catch (const std::exception& err) { @@ -101,7 +101,7 @@ void Engine::initialize(CoreParameters coreParameters) { throw initialize_error("could not initialize window"); } time.set(Window::time()); - if (auto icon = load_icon(resdir)) { + if (auto icon = load_icon()) { icon->flipY(); Window::setIcon(icon.get()); } @@ -118,7 +118,7 @@ void Engine::initialize(CoreParameters coreParameters) { if (langNotSet) { settings.ui.language.set(langs::locale_by_envlocale( platform::detect_locale(), - paths.getResourcesFolder() + "res:" )); } scripting::initialize(this); @@ -128,12 +128,12 @@ void Engine::initialize(CoreParameters coreParameters) { keepAlive(settings.ui.language.observe([this](auto lang) { setLanguage(lang); }, true)); - basePacks = io::read_list(resdir/fs::path("config/builtins.list")); + basePacks = io::read_list("res:config/builtins.list"); } void Engine::loadSettings() { - fs::path settings_file = paths.getSettingsFile(); - if (fs::is_regular_file(settings_file)) { + io::path settings_file = paths.getSettingsFile(); + if (io::is_regular_file(settings_file)) { logger.info() << "loading settings"; std::string text = io::read_string(settings_file); try { @@ -146,11 +146,11 @@ void Engine::loadSettings() { } void Engine::loadControls() { - fs::path controls_file = paths.getControlsFile(); - if (fs::is_regular_file(controls_file)) { + io::path controls_file = paths.getControlsFile(); + if (io::is_regular_file(controls_file)) { logger.info() << "loading controls"; std::string text = io::read_string(controls_file); - Events::loadBindings(controls_file.u8string(), text, BindType::BIND); + Events::loadBindings(controls_file.string(), text, BindType::BIND); } } @@ -171,9 +171,9 @@ void Engine::updateHotkeys() { void Engine::saveScreenshot() { auto image = Window::takeScreenshot(); image->flipY(); - fs::path filename = paths.getNewScreenshotFile("png"); + io::path filename = paths.getNewScreenshotFile("png"); imageio::write(filename.string(), image.get()); - logger.info() << "saved screenshot as " << filename.u8string(); + logger.info() << "saved screenshot as " << filename.string(); } void Engine::run() { @@ -264,12 +264,12 @@ cmd::CommandsInterpreter* Engine::getCommandsInterpreter() { return interpreter.get(); } -PacksManager Engine::createPacksManager(const fs::path& worldFolder) { +PacksManager Engine::createPacksManager(const io::path& worldFolder) { PacksManager manager; manager.setSources({ - {"world:content", worldFolder.empty() ? worldFolder : worldFolder/fs::path("content")}, - {"user:content", paths.getUserFilesFolder()/fs::path("content")}, - {"res:content", paths.getResourcesFolder()/fs::path("content")} + {"world:content", worldFolder.empty() ? worldFolder : worldFolder / "content"}, + {"user:content", "user:content"}, + {"res:content", "res:content"} }); return manager; } @@ -325,12 +325,12 @@ void Engine::loadAssets() { } } -static void load_configs(const fs::path& root) { - auto configFolder = root/fs::path("config"); - auto bindsFile = configFolder/fs::path("bindings.toml"); - if (fs::is_regular_file(bindsFile)) { +static void load_configs(const io::path& root) { + auto configFolder = root/io::path("config"); + auto bindsFile = configFolder/io::path("bindings.toml"); + if (io::is_regular_file(bindsFile)) { Events::loadBindings( - bindsFile.u8string(), io::read_string(bindsFile), BindType::BIND + bindsFile.string(), io::read_string(bindsFile), BindType::BIND ); } } @@ -338,15 +338,13 @@ static void load_configs(const fs::path& root) { void Engine::loadContent() { scripting::cleanup(); - auto resdir = paths.getResourcesFolder(); - std::vector names; for (auto& pack : contentPacks) { names.push_back(pack.id); } ContentBuilder contentBuilder; - corecontent::setup(paths, contentBuilder); + corecontent::setup(contentBuilder); paths.setContentPacks(&contentPacks); PacksManager manager = createPacksManager(paths.getCurrentWorldFolder()); @@ -363,7 +361,7 @@ void Engine::loadContent() { for (auto& pack : contentPacks) { resRoots.push_back({pack.id, pack.folder}); } - resPaths = std::make_unique(resdir, resRoots); + resPaths = std::make_unique("res:", resRoots); // Load content { @@ -380,7 +378,7 @@ void Engine::loadContent() { ContentLoader::loadScripts(*content); - langs::setup(resdir, langs::current->getId(), contentPacks); + langs::setup("res:", langs::current->getId(), contentPacks); if (!isHeadless()) { loadAssets(); onAssetsLoaded(); @@ -389,23 +387,22 @@ void Engine::loadContent() { void Engine::resetContent() { scripting::cleanup(); - auto resdir = paths.getResourcesFolder(); std::vector resRoots; { auto pack = ContentPack::createCore(paths); resRoots.push_back({"core", pack.folder}); load_configs(pack.folder); } - auto manager = createPacksManager(fs::path()); + auto manager = createPacksManager(io::path()); manager.scan(); for (const auto& pack : manager.getAll(basePacks)) { resRoots.push_back({pack.id, pack.folder}); } - resPaths = std::make_unique(resdir, resRoots); + resPaths = std::make_unique("res:", resRoots); contentPacks.clear(); content.reset(); - langs::setup(resdir, langs::current->getId(), contentPacks); + langs::setup("res:", langs::current->getId(), contentPacks); if (!isHeadless()) { loadAssets(); onAssetsLoaded(); @@ -414,15 +411,14 @@ void Engine::resetContent() { contentPacks = manager.getAll(basePacks); } -void Engine::loadWorldContent(const fs::path& folder) { +void Engine::loadWorldContent(const io::path& folder) { contentPacks.clear(); auto packNames = ContentPack::worldPacksList(folder); PacksManager manager; manager.setSources( - {{"world:content", - folder.empty() ? folder : folder / fs::path("content")}, - {"user:content", paths.getUserFilesFolder() / fs::path("content")}, - {"res:content", paths.getResourcesFolder() / fs::path("content")}} + {{"world:content", folder.empty() ? folder : folder / "content"}, + {"user:content", "user:content"}, + {"res:content", "res:content"}} ); manager.scan(); contentPacks = manager.getAll(manager.assemble(packNames)); @@ -445,7 +441,7 @@ void Engine::setScreen(std::shared_ptr screen) { } void Engine::setLanguage(std::string locale) { - langs::setup(paths.getResourcesFolder(), std::move(locale), contentPacks); + langs::setup("res:", std::move(locale), contentPacks); } void Engine::onWorldOpen(std::unique_ptr level, int64_t localPlayer) { diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index fd6cb1c3..b05881f6 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -14,7 +14,6 @@ #include "PostRunnables.hpp" #include "Time.hpp" -#include #include #include #include @@ -48,8 +47,8 @@ public: struct CoreParameters { bool headless = false; bool testMode = false; - std::filesystem::path resFolder {"res"}; - std::filesystem::path userFolder {"."}; + std::filesystem::path resFolder = "res"; + std::filesystem::path userFolder = "."; std::filesystem::path scriptFile; }; @@ -122,7 +121,7 @@ public: /// @brief Collect world content-packs and load content /// @see loadContent /// @param folder world folder - void loadWorldContent(const fs::path& folder); + void loadWorldContent(const io::path& folder); /// @brief Collect all available content-packs from res/content void loadAllPacks(); @@ -172,7 +171,7 @@ public: EngineController* getController(); cmd::CommandsInterpreter* getCommandsInterpreter(); - PacksManager createPacksManager(const fs::path& worldFolder); + PacksManager createPacksManager(const io::path& worldFolder); void setLevelConsumer(OnWorldOpen levelConsumer); diff --git a/src/engine/ServerMainloop.cpp b/src/engine/ServerMainloop.cpp index 954c2a11..504deb9e 100644 --- a/src/engine/ServerMainloop.cpp +++ b/src/engine/ServerMainloop.cpp @@ -34,8 +34,10 @@ void ServerMainloop::run() { setLevel(std::move(level)); }); - logger.info() << "starting test " << coreParams.scriptFile; - auto process = scripting::start_coroutine(coreParams.scriptFile); + logger.info() << "starting test " << coreParams.scriptFile.string(); + auto process = scripting::start_coroutine( + "script:" + coreParams.scriptFile.filename().u8string() + ); double targetDelta = 1.0 / static_cast(TPS); double delta = targetDelta; @@ -76,7 +78,7 @@ void ServerMainloop::run() { void ServerMainloop::setLevel(std::unique_ptr level) { if (level == nullptr) { controller->onWorldQuit(); - engine.getPaths().setCurrentWorldFolder(fs::path()); + engine.getPaths().setCurrentWorldFolder(""); controller = nullptr; } else { controller = std::make_unique( diff --git a/src/frontend/UiDocument.cpp b/src/frontend/UiDocument.cpp index 91c1e0a0..1dfa18aa 100644 --- a/src/frontend/UiDocument.cpp +++ b/src/frontend/UiDocument.cpp @@ -56,22 +56,22 @@ scriptenv UiDocument::getEnvironment() const { std::unique_ptr UiDocument::read( const scriptenv& penv, const std::string& name, - const fs::path& file, + const io::path& file, const std::string& fileName ) { const std::string text = io::read_string(file); - auto xmldoc = xml::parse(file.u8string(), text); + auto xmldoc = xml::parse(file.string(), text); auto env = penv == nullptr ? scripting::create_doc_environment(scripting::get_root_environment(), name) : scripting::create_doc_environment(penv, name); gui::UiXmlReader reader(env); - auto view = reader.readXML(file.u8string(), *xmldoc->getRoot()); + auto view = reader.readXML(file.string(), *xmldoc->getRoot()); view->setId("root"); uidocscript script {}; - auto scriptFile = fs::path(file.u8string()+".lua"); - if (fs::is_regular_file(scriptFile)) { + auto scriptFile = io::path(file.string()+".lua"); + if (io::is_regular_file(scriptFile)) { scripting::load_layout_script( env, name, scriptFile, fileName + ".lua", script ); @@ -80,8 +80,8 @@ std::unique_ptr UiDocument::read( } std::shared_ptr UiDocument::readElement( - const fs::path& file, const std::string& fileName + const io::path& file, const std::string& fileName ) { - auto document = read(nullptr, file.filename().u8string(), file, fileName); + auto document = read(nullptr, file.name(), file, fileName); return document->getRoot(); } diff --git a/src/frontend/UiDocument.hpp b/src/frontend/UiDocument.hpp index dbf91ef1..298e63bc 100644 --- a/src/frontend/UiDocument.hpp +++ b/src/frontend/UiDocument.hpp @@ -4,10 +4,9 @@ #include #include -#include #include -namespace fs = std::filesystem; +#include "io/fwd.hpp" namespace gui { class UINode; @@ -48,10 +47,10 @@ public: static std::unique_ptr read( const scriptenv& parent_env, const std::string& name, - const fs::path& file, + const io::path& file, const std::string& fileName ); static std::shared_ptr readElement( - const fs::path& file, const std::string& fileName + const io::path& file, const std::string& fileName ); }; diff --git a/src/frontend/locale.cpp b/src/frontend/locale.cpp index bc60cc38..d199cb49 100644 --- a/src/frontend/locale.cpp +++ b/src/frontend/locale.cpp @@ -69,8 +69,8 @@ namespace { }; } -void langs::loadLocalesInfo(const fs::path& resdir, std::string& fallback) { - auto file = resdir/fs::u8path(langs::TEXTS_FOLDER)/fs::u8path("langs.json"); +void langs::loadLocalesInfo(const io::path& resdir, std::string& fallback) { + auto file = resdir / langs::TEXTS_FOLDER / "langs.json"; auto root = io::read_json(file); langs::locales_info.clear(); @@ -94,7 +94,7 @@ void langs::loadLocalesInfo(const fs::path& resdir, std::string& fallback) { } } -std::string langs::locale_by_envlocale(const std::string& envlocale, const fs::path& resdir){ +std::string langs::locale_by_envlocale(const std::string& envlocale, const io::path& resdir){ std::string fallback = FALLBACK_DEFAULT; if (locales_info.size() == 0) { loadLocalesInfo(resdir, fallback); @@ -115,21 +115,21 @@ std::string langs::locale_by_envlocale(const std::string& envlocale, const fs::p } } -void langs::load(const fs::path& resdir, +void langs::load(const io::path& resdir, const std::string& locale, const std::vector& packs, Lang& lang) { - fs::path filename = fs::path(TEXTS_FOLDER)/fs::path(locale + LANG_FILE_EXT); - fs::path core_file = resdir/filename; + io::path filename = io::path(TEXTS_FOLDER) / (locale + LANG_FILE_EXT); + io::path core_file = resdir / filename; - if (fs::is_regular_file(core_file)) { + if (io::is_regular_file(core_file)) { std::string text = io::read_string(core_file); Reader reader(core_file.string(), text); reader.read(lang, ""); } for (auto pack : packs) { - fs::path file = pack.folder/filename; - if (fs::is_regular_file(file)) { + io::path file = pack.folder / filename; + if (io::is_regular_file(file)) { std::string text = io::read_string(file); Reader reader(file.string(), text); reader.read(lang, ""); @@ -137,7 +137,7 @@ void langs::load(const fs::path& resdir, } } -void langs::load(const fs::path& resdir, +void langs::load(const io::path& resdir, const std::string& locale, const std::string& fallback, const std::vector& packs) { @@ -149,7 +149,7 @@ void langs::load(const fs::path& resdir, current = std::move(lang); } -void langs::setup(const fs::path& resdir, +void langs::setup(const io::path& resdir, std::string locale, const std::vector& packs) { std::string fallback = langs::FALLBACK_DEFAULT; diff --git a/src/frontend/locale.hpp b/src/frontend/locale.hpp index b3e200c9..1daaca21 100644 --- a/src/frontend/locale.hpp +++ b/src/frontend/locale.hpp @@ -3,9 +3,10 @@ #include #include #include -#include #include +#include "io/fwd.hpp" + struct ContentPack; namespace langs { @@ -44,17 +45,17 @@ namespace langs { extern std::unordered_map locales_info; extern void loadLocalesInfo( - const std::filesystem::path& resdir, + const io::path& resdir, std::string& fallback); extern std::string locale_by_envlocale(const std::string& envlocale, - const std::filesystem::path& resdir); + const io::path& resdir); - extern void load(const std::filesystem::path& resdir, + extern void load(const io::path& resdir, const std::string& locale, const std::vector& packs, Lang& lang); - extern void load(const std::filesystem::path& resdir, + extern void load(const io::path& resdir, const std::string& locale, const std::string& fallback, const std::vector& packs); @@ -63,7 +64,7 @@ namespace langs { extern const std::wstring& get(const std::wstring& key, const std::wstring& context); - extern void setup(const std::filesystem::path& resdir, + extern void setup(const io::path& resdir, std::string locale, const std::vector& packs); } diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index fe9156cc..d1690747 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -105,8 +105,8 @@ void LevelScreen::initializeContent() { void LevelScreen::initializePack(ContentPackRuntime* pack) { const ContentPack& info = pack->getInfo(); - fs::path scriptFile = info.folder/fs::path("scripts/hud.lua"); - if (fs::is_regular_file(scriptFile)) { + io::path scriptFile = info.folder / "scripts/hud.lua"; + if (io::is_regular_file(scriptFile)) { scripting::load_hud_script( pack->getEnvironment(), info.id, @@ -124,7 +124,7 @@ LevelScreen::~LevelScreen() { // unblock all bindings Events::enableBindings(); controller->onWorldQuit(); - engine.getPaths().setCurrentWorldFolder(fs::path()); + engine.getPaths().setCurrentWorldFolder(""); } void LevelScreen::saveWorldPreview() { @@ -147,7 +147,7 @@ void LevelScreen::saveWorldPreview() { worldRenderer->draw(ctx, camera, false, true, 0.0f, postProcessing.get()); auto image = postProcessing->toImage(); image->flipY(); - imageio::write(paths.resolve("world:preview.png").u8string(), image.get()); + imageio::write(paths.resolve("world:preview.png").string(), image.get()); } catch (const std::exception& err) { logger.error() << err.what(); } diff --git a/src/io/devices/Device.hpp b/src/io/devices/Device.hpp new file mode 100644 index 00000000..39794d80 --- /dev/null +++ b/src/io/devices/Device.hpp @@ -0,0 +1,76 @@ +#pragma once + +#include +#include + +#include "../path.hpp" + +namespace io { + class Device { + public: + virtual ~Device() = default; + + virtual std::filesystem::path resolve(std::string_view path) = 0; + + virtual void write(std::string_view path, const void* data, size_t size) = 0; + virtual void read(std::string_view path, void* data, size_t size) = 0; + + virtual size_t size(std::string_view path) = 0; + + virtual bool exists(std::string_view path) = 0; + virtual bool isdir(std::string_view path) = 0; + virtual bool isfile(std::string_view path) = 0; + virtual void mkdirs(std::string_view path) = 0; + virtual bool remove(std::string_view path) = 0; + virtual uint64_t removeAll(std::string_view path) = 0; + }; + + class SubDevice : public Device { + public: + SubDevice(std::shared_ptr parent, const std::string& path) + : parent(std::move(parent)), root(path) {} + + std::filesystem::path resolve(std::string_view path) override { + return parent->resolve((root / path).pathPart()); + } + + void write(std::string_view path, const void* data, size_t size) override { + parent->write((root / path).pathPart(), data, size); + } + + void read(std::string_view path, void* data, size_t size) override { + parent->read((root / path).pathPart(), data, size); + } + + size_t size(std::string_view path) override { + return parent->size((root / path).pathPart()); + } + + bool exists(std::string_view path) override { + return parent->exists((root / path).pathPart()); + } + + bool isdir(std::string_view path) override { + return parent->isdir((root / path).pathPart()); + } + + bool isfile(std::string_view path) override { + return parent->isfile((root / path).pathPart()); + } + + void mkdirs(std::string_view path) override { + parent->mkdirs((root / path).pathPart()); + } + + bool remove(std::string_view path) override { + return parent->remove((root / path).pathPart()); + } + + uint64_t removeAll(std::string_view path) override { + return parent->removeAll((root / path).pathPart()); + } + private: + std::shared_ptr parent; + path root; + }; +} diff --git a/src/io/devices/StdfsDevice.cpp b/src/io/devices/StdfsDevice.cpp new file mode 100644 index 00000000..7078b394 --- /dev/null +++ b/src/io/devices/StdfsDevice.cpp @@ -0,0 +1,63 @@ +#include "StdfsDevice.hpp" + +#include +#include + +using namespace io; + +std::filesystem::path StdfsDevice::resolve(std::string_view path) { + return root / std::filesystem::u8path(path); +} + +void StdfsDevice::write(std::string_view path, const void* data, size_t size) { + auto resolved = resolve(path); + std::ofstream output(resolved, std::ios::binary); + if (!output.is_open()) { + throw std::runtime_error("could not to open file " + resolved.u8string()); + } + output.write((const char*)data, size); +} + +void StdfsDevice::read(std::string_view path, void* data, size_t size) { + auto resolved = resolve(path); + std::ifstream input(resolved, std::ios::binary); + if (!input.is_open()) { + throw std::runtime_error("could not to open file " + resolved.u8string()); + } + input.read((char*)data, size); +} + +size_t StdfsDevice::size(std::string_view path) { + auto resolved = resolve(path); + return std::filesystem::file_size(resolved); +} + +bool StdfsDevice::exists(std::string_view path) { + auto resolved = resolve(path); + return std::filesystem::exists(resolved); +} + +bool StdfsDevice::isdir(std::string_view path) { + auto resolved = resolve(path); + return std::filesystem::is_directory(resolved); +} + +bool StdfsDevice::isfile(std::string_view path) { + auto resolved = resolve(path); + return std::filesystem::is_regular_file(resolved); +} + +void StdfsDevice::mkdirs(std::string_view path) { + auto resolved = resolve(path); + std::filesystem::create_directories(resolved); +} + +bool StdfsDevice::remove(std::string_view path) { + auto resolved = resolve(path); + return std::filesystem::remove(resolved); +} + +uint64_t StdfsDevice::removeAll(std::string_view path) { + auto resolved = resolve(path); + return std::filesystem::remove_all(resolved); +} diff --git a/src/io/devices/StdfsDevice.hpp b/src/io/devices/StdfsDevice.hpp new file mode 100644 index 00000000..6c88949f --- /dev/null +++ b/src/io/devices/StdfsDevice.hpp @@ -0,0 +1,21 @@ +#include "Device.hpp" + +namespace io { + class StdfsDevice : public Device { + public: + StdfsDevice(std::filesystem::path root) : root(std::move(root)) {} + + std::filesystem::path resolve(std::string_view path) override; + void write(std::string_view path, const void* data, size_t size) override; + void read(std::string_view path, void* data, size_t size) override; + size_t size(std::string_view path) override; + bool exists(std::string_view path) override; + bool isdir(std::string_view path) override; + bool isfile(std::string_view path) override; + void mkdirs(std::string_view path) override; + bool remove(std::string_view path) override; + uint64_t removeAll(std::string_view path) override; + private: + std::filesystem::path root; + }; +} diff --git a/src/io/engine_paths.cpp b/src/io/engine_paths.cpp index df5b8347..88fb98ca 100644 --- a/src/io/engine_paths.cpp +++ b/src/io/engine_paths.cpp @@ -9,9 +9,12 @@ #include "util/stringutil.hpp" #include +#include "io/devices/StdfsDevice.hpp" #include "world/files/WorldFiles.hpp" #include "debug/Logger.hpp" +namespace fs = std::filesystem; + static debug::Logger logger("engine-paths"); static inline auto SCREENSHOTS_FOLDER = std::filesystem::u8path("screenshots"); @@ -22,15 +25,16 @@ static inline auto EXPORT_FOLDER = std::filesystem::u8path("export"); static inline auto CONTROLS_FILE = std::filesystem::u8path("controls.toml"); static inline auto SETTINGS_FILE = std::filesystem::u8path("settings.toml"); -static std::filesystem::path toCanonic(std::filesystem::path path) { +static io::path toCanonic(io::path path) { std::stack parts; - path = path.lexically_normal(); + + path = std::filesystem::u8path(path.string()).lexically_normal().string(); do { - parts.push(path.filename().u8string()); - path = path.parent_path(); + parts.push(path.name()); + path = path.parent(); } while (!path.empty()); - path = fs::u8path(""); + path = ""; while (!parts.empty()) { const std::string part = parts.top(); @@ -48,44 +52,47 @@ static std::filesystem::path toCanonic(std::filesystem::path path) { } void EnginePaths::prepare() { - if (!fs::is_directory(resourcesFolder)) { + io::set_device("res", std::make_shared(resourcesFolder)); + io::set_device("user", std::make_shared(userFilesFolder)); + + if (!io::is_directory("res:")) { throw std::runtime_error( - resourcesFolder.u8string() + " is not a directory" + resourcesFolder.string() + " is not a directory" ); } - if (!fs::is_directory(userFilesFolder)) { - fs::create_directories(userFilesFolder); + if (!io::is_directory("user:")) { + io::create_directories("user:"); } logger.info() << "resources folder: " << fs::canonical(resourcesFolder).u8string(); logger.info() << "user files folder: " << fs::canonical(userFilesFolder).u8string(); - auto contentFolder = userFilesFolder / CONTENT_FOLDER; - if (!fs::is_directory(contentFolder)) { - fs::create_directories(contentFolder); + auto contentFolder = io::path("user:") / CONTENT_FOLDER; + if (!io::is_directory(contentFolder)) { + io::create_directories(contentFolder); } - auto exportFolder = userFilesFolder / EXPORT_FOLDER; - if (!fs::is_directory(exportFolder)) { - fs::create_directories(exportFolder); + auto exportFolder = io::path("user:") / EXPORT_FOLDER; + if (!io::is_directory(exportFolder)) { + io::create_directories(exportFolder); } - auto configFolder = userFilesFolder / CONFIG_FOLDER; - if (!fs::is_directory(configFolder)) { - fs::create_directories(configFolder); + auto configFolder = io::path("user:") / CONFIG_FOLDER; + if (!io::is_directory(configFolder)) { + io::create_directories(configFolder); } } -std::filesystem::path EnginePaths::getUserFilesFolder() const { +const std::filesystem::path& EnginePaths::getUserFilesFolder() const { return userFilesFolder; } -std::filesystem::path EnginePaths::getResourcesFolder() const { +const std::filesystem::path& EnginePaths::getResourcesFolder() const { return resourcesFolder; } -std::filesystem::path EnginePaths::getNewScreenshotFile(const std::string& ext) { - auto folder = userFilesFolder / SCREENSHOTS_FOLDER; - if (!fs::is_directory(folder)) { - fs::create_directory(folder); +io::path EnginePaths::getNewScreenshotFile(const std::string& ext) { + auto folder = io::path("user:") / SCREENSHOTS_FOLDER; + if (!io::is_directory(folder)) { + io::create_directories(folder); } auto t = std::time(nullptr); @@ -96,55 +103,55 @@ std::filesystem::path EnginePaths::getNewScreenshotFile(const std::string& ext) ss << std::put_time(&tm, format); std::string datetimestr = ss.str(); - auto filename = folder / fs::u8path("screenshot-" + datetimestr + "." + ext); + auto file = folder / ("screenshot-" + datetimestr + "." + ext); uint index = 0; - while (fs::exists(filename)) { - filename = folder / fs::u8path( - "screenshot-" + datetimestr + "-" + - std::to_string(index) + "." + ext - ); + while (io::exists(file)) { + file = folder / fs::u8path( + "screenshot-" + datetimestr + "-" + + std::to_string(index) + "." + ext + ); index++; } - return filename; + return file; } -std::filesystem::path EnginePaths::getWorldsFolder() const { - return userFilesFolder / WORLDS_FOLDER; +io::path EnginePaths::getWorldsFolder() const { + return io::path("user:") / WORLDS_FOLDER; } -std::filesystem::path EnginePaths::getConfigFolder() const { - return userFilesFolder / CONFIG_FOLDER; +io::path EnginePaths::getConfigFolder() const { + return io::path("user:") / CONFIG_FOLDER; } -std::filesystem::path EnginePaths::getCurrentWorldFolder() { +io::path EnginePaths::getCurrentWorldFolder() { return currentWorldFolder; } -std::filesystem::path EnginePaths::getWorldFolderByName(const std::string& name) { +io::path EnginePaths::getWorldFolderByName(const std::string& name) { return getWorldsFolder() / std::filesystem::path(name); } -std::filesystem::path EnginePaths::getControlsFile() const { - return userFilesFolder / CONTROLS_FILE; +io::path EnginePaths::getControlsFile() const { + return io::path("user:") / CONTROLS_FILE; } -std::filesystem::path EnginePaths::getSettingsFile() const { - return userFilesFolder / SETTINGS_FILE; +io::path EnginePaths::getSettingsFile() const { + return io::path("user:") / SETTINGS_FILE; } -std::vector EnginePaths::scanForWorlds() const { - std::vector folders; +std::vector EnginePaths::scanForWorlds() const { + std::vector folders; auto folder = getWorldsFolder(); - if (!fs::is_directory(folder)) return folders; + if (!io::is_directory(folder)) return folders; - for (const auto& entry : fs::directory_iterator(folder)) { + for (const auto& entry : std::filesystem::directory_iterator(io::resolve(folder))) { if (!entry.is_directory()) { continue; } - const auto& worldFolder = entry.path(); - auto worldFile = worldFolder / fs::u8path(WorldFiles::WORLD_FILE); - if (!fs::is_regular_file(worldFile)) { + io::path worldFolder = folder / entry.path().filename().u8string(); + auto worldFile = worldFolder / WorldFiles::WORLD_FILE; + if (!io::is_regular_file(worldFile)) { continue; } folders.push_back(worldFolder); @@ -152,10 +159,11 @@ std::vector EnginePaths::scanForWorlds() const { std::sort( folders.begin(), folders.end(), - [](std::filesystem::path a, std::filesystem::path b) { - a = a / fs::u8path(WorldFiles::WORLD_FILE); - b = b / fs::u8path(WorldFiles::WORLD_FILE); - return fs::last_write_time(a) > fs::last_write_time(b); + [](io::path a, io::path b) { + a = a / WorldFiles::WORLD_FILE; + b = b / WorldFiles::WORLD_FILE; + return fs::last_write_time(io::resolve(a)) > + fs::last_write_time(io::resolve(b)); } ); return folders; @@ -170,11 +178,13 @@ void EnginePaths::setResourcesFolder(std::filesystem::path folder) { } void EnginePaths::setScriptFolder(std::filesystem::path folder) { + io::set_device("script", std::make_shared(folder)); this->scriptFolder = std::move(folder); } -void EnginePaths::setCurrentWorldFolder(std::filesystem::path folder) { +void EnginePaths::setCurrentWorldFolder(io::path folder) { this->currentWorldFolder = std::move(folder); + io::create_subdevice("world", "user", currentWorldFolder); } void EnginePaths::setContentPacks(std::vector* contentPacks) { @@ -191,65 +201,64 @@ std::tuple EnginePaths::parsePath(std::string_view pat return {prefix, filename}; } -std::filesystem::path EnginePaths::resolve( +// TODO: remove +io::path EnginePaths::resolve( const std::string& path, bool throwErr ) const { auto [prefix, filename] = EnginePaths::parsePath(path); if (prefix.empty()) { throw files_access_error("no entry point specified"); } - filename = toCanonic(fs::u8path(filename)).u8string(); + filename = toCanonic(filename).string(); - if (prefix == "res" || prefix == "core") { - return resourcesFolder / fs::u8path(filename); + if (prefix == "core") { + return io::path("res:") / filename; } - if (prefix == "user") { - return userFilesFolder / fs::u8path(filename); + + if (prefix == "res" || prefix == "user" || prefix == "script") { + return prefix + ":" + filename; } if (prefix == "config") { - return getConfigFolder() / fs::u8path(filename); + return getConfigFolder() / filename; } if (prefix == "world") { - return currentWorldFolder / fs::u8path(filename); + return currentWorldFolder / filename; } if (prefix == "export") { - return userFilesFolder / EXPORT_FOLDER / fs::u8path(filename); - } - if (prefix == "script" && scriptFolder) { - return scriptFolder.value() / fs::u8path(filename); + return io::path("user:") / EXPORT_FOLDER / filename; } if (contentPacks) { for (auto& pack : *contentPacks) { if (pack.id == prefix) { - return pack.folder / fs::u8path(filename); + return pack.folder / filename; } } } if (throwErr) { throw files_access_error("unknown entry point '" + prefix + "'"); } - return std::filesystem::path(filename); + return filename; } -ResPaths::ResPaths(std::filesystem::path mainRoot, std::vector roots) +ResPaths::ResPaths(io::path mainRoot, std::vector roots) : mainRoot(std::move(mainRoot)), roots(std::move(roots)) { } -std::filesystem::path ResPaths::find(const std::string& filename) const { +io::path ResPaths::find(const std::string& filename) const { for (int i = roots.size() - 1; i >= 0; i--) { auto& root = roots[i]; - auto file = root.path / fs::u8path(filename); - if (fs::exists(file)) { + auto file = root.path / filename; + if (io::exists(file)) { return file; } } - return mainRoot / fs::u8path(filename); + return mainRoot / filename; } std::string ResPaths::findRaw(const std::string& filename) const { for (int i = roots.size() - 1; i >= 0; i--) { auto& root = roots[i]; - if (fs::exists(root.path / std::filesystem::path(filename))) { + if (io::exists(root.path / filename)) { return root.name + ":" + filename; } } @@ -261,8 +270,8 @@ std::vector ResPaths::listdirRaw(const std::string& folderName) con for (int i = roots.size() - 1; i >= 0; i--) { auto& root = roots[i]; auto folder = root.path / fs::u8path(folderName); - if (!fs::is_directory(folder)) continue; - for (const auto& entry : fs::directory_iterator(folder)) { + if (!io::is_directory(folder)) continue; + for (const auto& entry : fs::directory_iterator(io::resolve(folder))) { auto name = entry.path().filename().u8string(); entries.emplace_back(root.name + ":" + folderName + "/" + name); } @@ -270,16 +279,16 @@ std::vector ResPaths::listdirRaw(const std::string& folderName) con return entries; } -std::vector ResPaths::listdir( +std::vector ResPaths::listdir( const std::string& folderName ) const { - std::vector entries; + std::vector entries; for (int i = roots.size() - 1; i >= 0; i--) { auto& root = roots[i]; - std::filesystem::path folder = root.path / fs::u8path(folderName); - if (!fs::is_directory(folder)) continue; - for (const auto& entry : fs::directory_iterator(folder)) { - entries.push_back(entry.path()); + io::path folder = root.path / folderName; + if (!io::is_directory(folder)) continue; + for (const auto& entry : fs::directory_iterator(io::resolve(folder))) { + entries.push_back(folder / entry.path().filename().u8string()); } } return entries; @@ -288,8 +297,8 @@ std::vector ResPaths::listdir( dv::value ResPaths::readCombinedList(const std::string& filename) const { dv::value list = dv::list(); for (const auto& root : roots) { - auto path = root.path / fs::u8path(filename); - if (!fs::exists(path)) { + auto path = root.path / filename; + if (!io::exists(path)) { continue; } try { @@ -313,8 +322,8 @@ dv::value ResPaths::readCombinedList(const std::string& filename) const { dv::value ResPaths::readCombinedObject(const std::string& filename) const { dv::value object = dv::object(); for (const auto& root : roots) { - auto path = root.path / fs::u8path(filename); - if (!fs::exists(path)) { + auto path = root.path / filename; + if (!io::exists(path)) { continue; } try { @@ -335,6 +344,6 @@ dv::value ResPaths::readCombinedObject(const std::string& filename) const { return object; } -const std::filesystem::path& ResPaths::getMainRoot() const { +const io::path& ResPaths::getMainRoot() const { return mainRoot; } diff --git a/src/io/engine_paths.hpp b/src/io/engine_paths.hpp index 5ec34704..920b01aa 100644 --- a/src/io/engine_paths.hpp +++ b/src/io/engine_paths.hpp @@ -1,16 +1,15 @@ #pragma once -#include #include #include #include #include #include +#include "io.hpp" #include "data/dv.hpp" #include "content/ContentPack.hpp" - class files_access_error : public std::runtime_error { public: files_access_error(const std::string& msg) : std::runtime_error(msg) { @@ -22,29 +21,29 @@ public: void prepare(); void setUserFilesFolder(std::filesystem::path folder); - std::filesystem::path getUserFilesFolder() const; + const std::filesystem::path& getUserFilesFolder() const; void setResourcesFolder(std::filesystem::path folder); - std::filesystem::path getResourcesFolder() const; + const std::filesystem::path& getResourcesFolder() const; void setScriptFolder(std::filesystem::path folder); - std::filesystem::path getWorldFolderByName(const std::string& name); - std::filesystem::path getWorldsFolder() const; - std::filesystem::path getConfigFolder() const; + io::path getWorldFolderByName(const std::string& name); + io::path getWorldsFolder() const; + io::path getConfigFolder() const; - void setCurrentWorldFolder(std::filesystem::path folder); - std::filesystem::path getCurrentWorldFolder(); + void setCurrentWorldFolder(io::path folder); + io::path getCurrentWorldFolder(); - std::filesystem::path getNewScreenshotFile(const std::string& ext); - std::filesystem::path getControlsFile() const; - std::filesystem::path getSettingsFile() const; + io::path getNewScreenshotFile(const std::string& ext); + io::path getControlsFile() const; + io::path getSettingsFile() const; void setContentPacks(std::vector* contentPacks); - std::vector scanForWorlds() const; + std::vector scanForWorlds() const; - std::filesystem::path resolve(const std::string& path, bool throwErr = true) const; + io::path resolve(const std::string& path, bool throwErr = true) const; static std::tuple parsePath(std::string_view view); @@ -53,23 +52,23 @@ public: private: std::filesystem::path userFilesFolder {"."}; std::filesystem::path resourcesFolder {"res"}; - std::filesystem::path currentWorldFolder; + io::path currentWorldFolder; std::optional scriptFolder; std::vector* contentPacks = nullptr; }; struct PathsRoot { std::string name; - std::filesystem::path path; + io::path path; }; class ResPaths { public: - ResPaths(std::filesystem::path mainRoot, std::vector roots); + ResPaths(io::path mainRoot, std::vector roots); - std::filesystem::path find(const std::string& filename) const; + io::path find(const std::string& filename) const; std::string findRaw(const std::string& filename) const; - std::vector listdir(const std::string& folder) const; + std::vector listdir(const std::string& folder) const; std::vector listdirRaw(const std::string& folder) const; /// @brief Read all found list versions from all packs and combine into a @@ -79,9 +78,9 @@ public: dv::value readCombinedObject(const std::string& file) const; - const std::filesystem::path& getMainRoot() const; + const io::path& getMainRoot() const; private: - std::filesystem::path mainRoot; + io::path mainRoot; std::vector roots; }; diff --git a/src/io/fwd.hpp b/src/io/fwd.hpp new file mode 100644 index 00000000..d6ce72f0 --- /dev/null +++ b/src/io/fwd.hpp @@ -0,0 +1,5 @@ +#pragma once + +namespace io { + class path; +} diff --git a/src/io/io.cpp b/src/io/io.cpp index 9979b950..356357c5 100644 --- a/src/io/io.cpp +++ b/src/io/io.cpp @@ -1,7 +1,7 @@ #include "io.hpp" +#include #include - #include #include #include @@ -13,10 +13,44 @@ #include "coders/toml.hpp" #include "util/stringutil.hpp" +#include "devices/Device.hpp" + namespace fs = std::filesystem; -io::rafile::rafile(const fs::path& filename) - : file(filename, std::ios::binary | std::ios::ate) { +static std::map> devices; + +void io::set_device(const std::string& name, std::shared_ptr device) { + devices[name] = device; +} + +std::shared_ptr io::get_device(const std::string& name) { + const auto& found = devices.find(name); + if (found == devices.end()) { + return nullptr; + } + return found->second; +} + +io::Device& io::require_device(const std::string& name) { + auto device = get_device(name); + if (!device) { + throw std::runtime_error("io-device not found: " + name); + } + return *device; +} + +void io::create_subdevice( + const std::string& name, const std::string& parent, const io::path& root +) { + auto parentDevice = get_device(parent); + if (!parentDevice) { + throw std::runtime_error("parent device not found for entry-point: " + parent); + } + set_device(name, std::make_shared(parentDevice, root.pathPart())); +} + +io::rafile::rafile(const io::path& filename) + : file(io::resolve(filename), std::ios::binary | std::ios::ate) { if (!file) { throw std::runtime_error("could not to open file " + filename.string()); } @@ -37,121 +71,92 @@ void io::rafile::read(char* buffer, std::streamsize size) { } bool io::write_bytes( - const fs::path& filename, const ubyte* data, size_t size + const io::path& filename, const ubyte* data, size_t size ) { - std::ofstream output(filename, std::ios::binary); - if (!output.is_open()) return false; - output.write((const char*)data, size); - output.close(); + auto device = io::get_device(filename.entryPoint()); + if (device == nullptr) { + return false; + } + device->write(filename.pathPart(), data, size); return true; } -uint io::append_bytes( - const fs::path& filename, const ubyte* data, size_t size -) { - std::ofstream output(filename, std::ios::binary | std::ios::app); - if (!output.is_open()) return 0; - uint position = output.tellp(); - output.write((const char*)data, size); - output.close(); - return position; -} - -bool io::read(const fs::path& filename, char* data, size_t size) { - std::ifstream output(filename, std::ios::binary); - if (!output.is_open()) return false; - output.read(data, size); - output.close(); +bool io::read(const io::path& filename, char* data, size_t size) { + auto device = io::get_device(filename.entryPoint()); + if (device == nullptr) { + return false; + } + device->read(filename.pathPart(), data, size); return true; } -util::Buffer io::read_bytes_buffer(const fs::path& path) { +util::Buffer io::read_bytes_buffer(const path& file) { size_t size; - auto bytes = io::read_bytes(path, size); + auto bytes = io::read_bytes(file, size); return util::Buffer(std::move(bytes), size); } std::unique_ptr io::read_bytes( - const fs::path& filename, size_t& length + const io::path& filename, size_t& length ) { - std::ifstream input(filename, std::ios::binary); - if (!input.is_open()) { - throw std::runtime_error( - "could not to load file '" + filename.string() + "'" - ); - } - input.seekg(0, std::ios_base::end); - length = input.tellg(); - input.seekg(0, std::ios_base::beg); - + auto& device = io::require_device(filename.entryPoint()); + length = device.size(filename.pathPart()); auto data = std::make_unique(length); - input.read((char*)data.get(), length); - input.close(); + device.read(filename.pathPart(), data.get(), length); return data; } -std::vector io::read_bytes(const fs::path& filename) { - std::ifstream input(filename, std::ios::binary); - if (!input.is_open()) return {}; - input.seekg(0, std::ios_base::end); - size_t length = input.tellg(); - input.seekg(0, std::ios_base::beg); - +std::vector io::read_bytes(const path& filename) { + auto& device = io::require_device(filename.entryPoint()); + size_t length = device.size(filename.pathPart()); std::vector data(length); - data.resize(length); - input.read((char*)data.data(), length); - input.close(); + device.read(filename.pathPart(), data.data(), length); return data; } -std::string io::read_string(const fs::path& filename) { +std::string io::read_string(const path& filename) { size_t size; auto bytes = read_bytes(filename, size); return std::string((const char*)bytes.get(), size); } -bool io::write_string(const fs::path& filename, std::string_view content) { - std::ofstream file(filename); - if (!file) { - return false; - } - file << content; - return true; +bool io::write_string(const io::path& file, std::string_view content) { + return io::write_bytes(file, (const ubyte*)content.data(), content.size()); } bool io::write_json( - const fs::path& filename, const dv::value& obj, bool nice + const io::path& file, const dv::value& obj, bool nice ) { - return io::write_string(filename, json::stringify(obj, nice, " ")); + return io::write_string(file, json::stringify(obj, nice, " ")); } bool io::write_binary_json( - const fs::path& filename, const dv::value& obj, bool compression + const io::path& file, const dv::value& obj, bool compression ) { auto bytes = json::to_binary(obj, compression); - return io::write_bytes(filename, bytes.data(), bytes.size()); + return io::write_bytes(file, bytes.data(), bytes.size()); } -dv::value io::read_json(const fs::path& filename) { +dv::value io::read_json(const path& filename) { std::string text = io::read_string(filename); return json::parse(filename.string(), text); } -dv::value io::read_binary_json(const fs::path& file) { +dv::value io::read_binary_json(const path& file) { size_t size; auto bytes = io::read_bytes(file, size); return json::from_binary(bytes.get(), size); } -dv::value io::read_toml(const fs::path& file) { - return toml::parse(file.u8string(), io::read_string(file)); +dv::value io::read_toml(const path& file) { + return toml::parse(file.string(), io::read_string(file)); } -std::vector io::read_list(const fs::path& filename) { - std::ifstream file(filename); +std::vector io::read_list(const io::path& filename) { + std::ifstream file(resolve(filename)); // FIXME if (!file) { throw std::runtime_error( - "could not to open file " + filename.u8string() + "could not to open file " + filename.string() ); } std::vector lines; @@ -165,6 +170,71 @@ std::vector io::read_list(const fs::path& filename) { return lines; } +bool io::is_regular_file(const io::path& file) { + if (file.empty()) { + return false; + } + auto device = io::get_device(file.entryPoint()); + if (device == nullptr) { + return false; + } + return device->isfile(file.pathPart()); +} + +bool io::is_directory(const io::path& file) { + if (file.empty()) { + return false; + } + auto device = io::get_device(file.entryPoint()); + if (device == nullptr) { + return false; + } + return device->isdir(file.pathPart()); +} + +bool io::exists(const io::path& file) { + if (file.empty()) { + return false; + } + auto device = io::get_device(file.entryPoint()); + if (device == nullptr) { + return false; + } + return device->exists(file.pathPart()); +} + +bool io::create_directories(const io::path& file) { + auto& device = io::require_device(file.entryPoint()); + if (device.isdir(file.pathPart())) { + return false; + } + device.mkdirs(file.pathPart()); + return true; +} + +bool io::remove(const io::path& file) { + auto& device = io::require_device(file.entryPoint()); + return device.remove(file.pathPart()); +} + +uint64_t io::remove_all(const io::path& file) { + auto& device = io::require_device(file.entryPoint()); + return device.removeAll(file.pathPart()); +} + +size_t io::file_size(const io::path& file) { + auto& device = io::require_device(file.entryPoint()); + return device.size(file.pathPart()); +} + +std::filesystem::path io::resolve(const io::path& file) { + auto device = io::get_device(file.entryPoint()); + if (device == nullptr) { + return {}; + } + return device->resolve(file.pathPart()); +} + #include #include "coders/json.hpp" @@ -177,22 +247,22 @@ static std::map data_decoders { {fs::u8path(".toml"), toml::parse}, }; -bool io::is_data_file(const fs::path& file) { +bool io::is_data_file(const io::path& file) { return is_data_interchange_format(file.extension()); } -bool io::is_data_interchange_format(const fs::path& ext) { +bool io::is_data_interchange_format(const std::string& ext) { return data_decoders.find(ext) != data_decoders.end(); } -dv::value io::read_object(const fs::path& file) { +dv::value io::read_object(const path& file) { const auto& found = data_decoders.find(file.extension()); if (found == data_decoders.end()) { throw std::runtime_error("unknown file format"); } auto text = read_string(file); try { - return found->second(file.u8string(), text); + return found->second(file.string(), text); } catch (const parsing_error& err) { throw std::runtime_error(err.errorLog()); } diff --git a/src/io/io.hpp b/src/io/io.hpp index 5ea4c93e..1cdc8198 100644 --- a/src/io/io.hpp +++ b/src/io/io.hpp @@ -9,16 +9,25 @@ #include "typedefs.hpp" #include "data/dv.hpp" #include "util/Buffer.hpp" - -namespace fs = std::filesystem; +#include "path.hpp" namespace io { + class Device; + + void set_device(const std::string& name, std::shared_ptr device); + std::shared_ptr get_device(const std::string& name); + Device& require_device(const std::string& name); + + void create_subdevice( + const std::string& name, const std::string& parent, const path& root + ); + /// @brief Read-only random access file class rafile { std::ifstream file; size_t filelength; public: - rafile(const fs::path& filename); + rafile(const path& filename); void seekg(std::streampos pos); void read(char* buffer, std::streamsize size); @@ -29,52 +38,56 @@ namespace io { /// @param file target file /// @param data data bytes array /// @param size size of data bytes array - bool write_bytes(const fs::path& file, const ubyte* data, size_t size); - - /// @brief Append bytes array to the file without any extra data - /// @param file target file - /// @param data data bytes array - /// @param size size of data bytes array - uint append_bytes(const fs::path& file, const ubyte* data, size_t size); + bool write_bytes(const io::path& file, const ubyte* data, size_t size); /// @brief Write string to the file - bool write_string(const fs::path& filename, std::string_view content); + bool write_string(const io::path& file, std::string_view content); /// @brief Write dynamic data to the JSON file /// @param nice if true, human readable format will be used, otherwise /// minimal bool write_json( - const fs::path& filename, const dv::value& obj, bool nice = true + const io::path& file, const dv::value& obj, bool nice = true ); /// @brief Write dynamic data to the binary JSON file /// (see src/coders/binary_json_spec.md) /// @param compressed use gzip compression bool write_binary_json( - const fs::path& filename, + const io::path& file, const dv::value& obj, bool compressed = false ); - bool read(const fs::path&, char* data, size_t size); - util::Buffer read_bytes_buffer(const fs::path&); - std::unique_ptr read_bytes(const fs::path&, size_t& length); - std::vector read_bytes(const fs::path&); - std::string read_string(const fs::path& filename); + bool read(const io::path& file, char* data, size_t size); + util::Buffer read_bytes_buffer(const path& file); + std::unique_ptr read_bytes(const path& file, size_t& length); + std::vector read_bytes(const path& file); + std::string read_string(const path& file); /// @brief Read JSON or BJSON file /// @param file *.json or *.bjson file - dv::value read_json(const fs::path& file); + dv::value read_json(const path& file); - dv::value read_binary_json(const fs::path& file); + dv::value read_binary_json(const path& file); /// @brief Read TOML file /// @param file *.toml file - dv::value read_toml(const fs::path& file); + dv::value read_toml(const path& file); - std::vector read_list(const fs::path& file); + std::vector read_list(const io::path& file); - bool is_data_file(const fs::path& file); - bool is_data_interchange_format(const fs::path& ext); - dv::value read_object(const fs::path& file); + bool is_regular_file(const io::path& file); + bool is_directory(const io::path& file); + bool exists(const io::path& file); + bool create_directories(const io::path& file); + bool remove(const io::path& file); + uint64_t remove_all(const io::path& file); + size_t file_size(const io::path& file); + + std::filesystem::path resolve(const io::path& file); + + bool is_data_file(const io::path& file); + bool is_data_interchange_format(const std::string& ext); + dv::value read_object(const path& file); } diff --git a/src/io/path.cpp b/src/io/path.cpp new file mode 100644 index 00000000..e1dd5a98 --- /dev/null +++ b/src/io/path.cpp @@ -0,0 +1,11 @@ +#include "path.hpp" + +#include + +using namespace io; + +void path::checkValid() const { + if (colonPos == std::string::npos) { + throw std::runtime_error("path entry point is not specified: " + str); + } +} diff --git a/src/io/path.hpp b/src/io/path.hpp new file mode 100644 index 00000000..454ff161 --- /dev/null +++ b/src/io/path.hpp @@ -0,0 +1,138 @@ +#pragma once + +#include +#include + +namespace io { + /// @brief std::filesystem::path project-specific alternative having + /// `entry_point:path` scheme and solving std::filesystem::path problems: + /// - implicit std::string conversions depending on compiler + /// - unicode path construction must be done with std::filesystem::u8path + class path { + public: + path() = default; + + path(std::string str) : str(std::move(str)) { + colonPos = this->str.find(':'); + + size_t len = this->str.length(); + for (size_t i = 0; i < len; i++) { + if (this->str[i] == '\\') { + this->str[i] = '/'; + } + } + } + + path(const char* str) : path(std::string(str)) {} + + bool operator==(const std::string& other) const { + return str == other; + } + + bool operator<(const path& other) const { + return str < other.str; + } + + bool operator==(const path& other) const { + return str == other.str; + } + + bool operator==(const char* other) const { + return str == other; + } + + path operator/(const char* child) const { + if (str.empty() || str[str.length()-1] == ':') { + return str + std::string(child); + } + return str + "/" + std::string(child); + } + + path operator/(const std::string& child) const { + if (str.empty() || str[str.length()-1] == ':') { + return str + child; + } + return str + "/" + child; + } + + path operator/(std::string_view child) const { + if (str.empty() || str[str.length()-1] == ':') { + return str + std::string(child); + } + return str + "/" + std::string(child); + } + + path operator/(const path& child) const { + if (str.empty() || str[str.length()-1] == ':') { + return str + child.pathPart(); + } + return str + "/" + child.pathPart(); + } + + std::string pathPart() const { + if (colonPos == std::string::npos) { + return str; + } + return str.substr(colonPos + 1); + } + + std::string name() const { + size_t slashpos = str.rfind('/'); + if (slashpos == std::string::npos) { + return colonPos == std::string::npos ? str + : str.substr(colonPos + 1); + } + return str.substr(slashpos + 1); + } + + std::string stem() const { + return name().substr(0, name().rfind('.')); + } + + /// @brief Get extension + std::string extension() const { + size_t slashpos = str.rfind('/'); + size_t dotpos = str.rfind('.'); + if (dotpos == std::string::npos || + (slashpos != std::string::npos && dotpos < slashpos)) { + return ""; + } + return str.substr(dotpos); + } + + /// @brief Get entry point + std::string entryPoint() const { + checkValid(); + return str.substr(0, colonPos); + } + + /// @brief Get parent path + path parent() const { + size_t slashpos = str.rfind('/'); + if (slashpos == std::string::npos) { + return colonPos == std::string::npos + ? path() + : path(str.substr(0, colonPos)); + } + return colonPos == std::string::npos + ? path(str.substr(0, slashpos)) + : path(str.substr(0, colonPos) + str.substr(slashpos)); + } + + std::string string() const { + return str; + } + + /// @brief Check if path is not initialized with 'entry_point:path' + bool empty() const { + return str.empty(); + } + private: + /// @brief UTF-8 string contains entry_point:path or empty string + std::string str; + /// @brief Precalculated position of colon character + size_t colonPos = std::string::npos; + + void checkValid() const; + }; +} diff --git a/src/logic/EngineController.cpp b/src/logic/EngineController.cpp index fc0b1cb0..72ce6e90 100644 --- a/src/logic/EngineController.cpp +++ b/src/logic/EngineController.cpp @@ -32,11 +32,11 @@ EngineController::EngineController(Engine& engine) : engine(engine) { } void EngineController::deleteWorld(const std::string& name) { - fs::path folder = engine.getPaths().getWorldFolderByName(name); + io::path folder = engine.getPaths().getWorldFolderByName(name); auto deletion = [this, folder]() { - logger.info() << "deleting " << folder; - fs::remove_all(folder); + logger.info() << "deleting " << folder.string(); + io::remove_all(folder); if (!engine.isHeadless()) { engine.getGUI()->getMenu()->back(); } @@ -49,7 +49,7 @@ void EngineController::deleteWorld(const std::string& name) { guiutil::confirm( engine, langs::get(L"delete-confirm", L"world") + L" (" + - util::str2wstr_utf8(folder.u8string()) + L")", + util::str2wstr_utf8(folder.string()) + L")", deletion ); } @@ -128,7 +128,7 @@ static void show_convert_request( ); } -static bool load_world_content(Engine& engine, const fs::path& folder) { +static bool load_world_content(Engine& engine, const io::path& folder) { if (engine.isHeadless()) { engine.loadWorldContent(folder); return true; @@ -192,10 +192,10 @@ void EngineController::onMissingContent(const std::shared_ptr& re void EngineController::openWorld(const std::string& name, bool confirmConvert) { const auto& paths = engine.getPaths(); - auto folder = paths.getWorldsFolder() / fs::u8path(name); - auto worldFile = folder / fs::u8path("world.json"); - if (!fs::exists(worldFile)) { - throw std::runtime_error(worldFile.u8string() + " does not exists"); + auto folder = paths.getWorldsFolder() / name; + auto worldFile = folder / "world.json"; + if (!io::exists(worldFile)) { + throw std::runtime_error(worldFile.string() + " does not exists"); } if (!load_world_content(engine, folder)) { @@ -257,7 +257,7 @@ void EngineController::createWorld( uint64_t seed = str2seed(seedstr); EnginePaths& paths = engine.getPaths(); - auto folder = paths.getWorldsFolder() / fs::u8path(name); + auto folder = paths.getWorldsFolder() / name; if (engine.isHeadless()) { engine.loadContent(); @@ -288,7 +288,7 @@ void EngineController::setLocalPlayer(int64_t player) { } void EngineController::reopenWorld(World* world) { - std::string name = world->wfile->getFolder().filename().u8string(); + std::string name = world->wfile->getFolder().name(); engine.onWorldClosed(); openWorld(name, true); } @@ -318,7 +318,7 @@ void EngineController::reconfigPacks( runnable removeFunc = [this, controller, packsToAdd, packsToRemove]() { if (controller == nullptr) { try { - auto manager = engine.createPacksManager(fs::path("")); + auto manager = engine.createPacksManager(""); manager.scan(); auto names = PacksManager::getNames(engine.getContentPacks()); for (const auto& id : packsToAdd) { diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index 3566e990..4c1e5dc6 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -260,7 +260,7 @@ static int l_load_texture(lua::State* L) { static int l_open_folder(lua::State* L) { auto path = engine->getPaths().resolve(lua::require_string(L, 1)); - platform::open_folder(path); + platform::open_folder(io::resolve(path)); return 0; } diff --git a/src/logic/scripting/lua/libs/libfile.cpp b/src/logic/scripting/lua/libs/libfile.cpp index cedef89b..5c7dd046 100644 --- a/src/logic/scripting/lua/libs/libfile.cpp +++ b/src/logic/scripting/lua/libs/libfile.cpp @@ -13,13 +13,13 @@ namespace fs = std::filesystem; using namespace scripting; -static fs::path resolve_path(const std::string& path) { +static io::path resolve_path(const std::string& path) { return engine->getPaths().resolve(path); } -static fs::path resolve_path_soft(const std::string& path) { +static io::path resolve_path_soft(const std::string& path) { if (path.find(':') == std::string::npos) { - return fs::u8path(""); + return ""; } return engine->getPaths().resolve(path, false); } @@ -34,17 +34,17 @@ static int l_find(lua::State* L) { } static int l_resolve(lua::State* L) { - fs::path path = resolve_path(lua::require_string(L, 1)); - return lua::pushstring(L, path.u8string()); + io::path path = resolve_path(lua::require_string(L, 1)); + return lua::pushstring(L, path.string()); } static int l_read(lua::State* L) { - fs::path path = resolve_path(lua::require_string(L, 1)); - if (fs::is_regular_file(path)) { + io::path path = resolve_path(lua::require_string(L, 1)); + if (io::is_regular_file(path)) { return lua::pushstring(L, io::read_string(path)); } throw std::runtime_error( - "file does not exists " + util::quote(path.u8string()) + "file does not exists " + util::quote(path.string()) ); } @@ -52,9 +52,9 @@ static std::set writeable_entry_points { "world", "export", "config" }; -static fs::path get_writeable_path(lua::State* L) { +static io::path get_writeable_path(lua::State* L) { std::string rawpath = lua::require_string(L, 1); - fs::path path = resolve_path(rawpath); + io::path path = resolve_path(rawpath); auto entryPoint = rawpath.substr(0, rawpath.find(':')); if (writeable_entry_points.find(entryPoint) == writeable_entry_points.end()) { throw std::runtime_error("access denied"); @@ -63,7 +63,7 @@ static fs::path get_writeable_path(lua::State* L) { } static int l_write(lua::State* L) { - fs::path path = get_writeable_path(L); + io::path path = get_writeable_path(L); std::string text = lua::require_string(L, 2); io::write_string(path, text); return 1; @@ -71,62 +71,62 @@ static int l_write(lua::State* L) { static int l_remove(lua::State* L) { std::string rawpath = lua::require_string(L, 1); - fs::path path = resolve_path(rawpath); + io::path path = resolve_path(rawpath); auto entryPoint = rawpath.substr(0, rawpath.find(':')); if (writeable_entry_points.find(entryPoint) == writeable_entry_points.end()) { throw std::runtime_error("access denied"); } - return lua::pushboolean(L, fs::remove(path)); + return lua::pushboolean(L, io::remove(path)); } static int l_remove_tree(lua::State* L) { std::string rawpath = lua::require_string(L, 1); - fs::path path = resolve_path(rawpath); + io::path path = resolve_path(rawpath); auto entryPoint = rawpath.substr(0, rawpath.find(':')); if (writeable_entry_points.find(entryPoint) == writeable_entry_points.end()) { throw std::runtime_error("access denied"); } - return lua::pushinteger(L, fs::remove_all(path)); + return lua::pushinteger(L, io::remove_all(path)); } static int l_exists(lua::State* L) { - fs::path path = resolve_path_soft(lua::require_string(L, 1)); - return lua::pushboolean(L, fs::exists(path)); + io::path path = resolve_path_soft(lua::require_string(L, 1)); + return lua::pushboolean(L, io::exists(path)); } static int l_isfile(lua::State* L) { - fs::path path = resolve_path_soft(lua::require_string(L, 1)); - return lua::pushboolean(L, fs::is_regular_file(path)); + io::path path = resolve_path_soft(lua::require_string(L, 1)); + return lua::pushboolean(L, io::is_regular_file(path)); } static int l_isdir(lua::State* L) { - fs::path path = resolve_path_soft(lua::require_string(L, 1)); - return lua::pushboolean(L, fs::is_directory(path)); + io::path path = resolve_path_soft(lua::require_string(L, 1)); + return lua::pushboolean(L, io::is_directory(path)); } static int l_length(lua::State* L) { - fs::path path = resolve_path(lua::require_string(L, 1)); - if (fs::exists(path)) { - return lua::pushinteger(L, fs::file_size(path)); + io::path path = resolve_path(lua::require_string(L, 1)); + if (io::exists(path)) { + return lua::pushinteger(L, io::file_size(path)); } else { return lua::pushinteger(L, -1); } } static int l_mkdir(lua::State* L) { - fs::path path = resolve_path(lua::require_string(L, 1)); - return lua::pushboolean(L, fs::create_directory(path)); + io::path path = resolve_path(lua::require_string(L, 1)); + return lua::pushboolean(L, io::create_directories(path)); // FIXME } static int l_mkdirs(lua::State* L) { - fs::path path = resolve_path(lua::require_string(L, 1)); - return lua::pushboolean(L, fs::create_directories(path)); + io::path path = resolve_path(lua::require_string(L, 1)); + return lua::pushboolean(L, io::create_directories(path)); } static int l_read_bytes(lua::State* L) { - fs::path path = resolve_path(lua::require_string(L, 1)); - if (fs::is_regular_file(path)) { - size_t length = static_cast(fs::file_size(path)); + io::path path = resolve_path(lua::require_string(L, 1)); + if (io::is_regular_file(path)) { + size_t length = static_cast(io::file_size(path)); auto bytes = io::read_bytes(path, length); @@ -140,12 +140,12 @@ static int l_read_bytes(lua::State* L) { return 1; } throw std::runtime_error( - "file does not exists " + util::quote(path.u8string()) + "file does not exists " + util::quote(path.string()) ); } static int l_write_bytes(lua::State* L) { - fs::path path = get_writeable_path(L); + io::path path = get_writeable_path(L); if (auto bytearray = lua::touserdata(L, 2)) { auto& bytes = bytearray->data(); @@ -176,15 +176,15 @@ static int l_list(lua::State* L) { if (dirname.find(':') == std::string::npos) { return l_list_all_res(L, dirname); } - fs::path path = resolve_path(dirname); - if (!fs::is_directory(path)) { + io::path path = resolve_path(dirname); + if (!io::is_directory(path)) { throw std::runtime_error( - util::quote(path.u8string()) + " is not a directory" + util::quote(path.string()) + " is not a directory" ); } lua::createtable(L, 0, 0); size_t index = 1; - for (auto& entry : fs::directory_iterator(path)) { + for (auto& entry : fs::directory_iterator(io::resolve(path))) { auto name = entry.path().filename().u8string(); auto file = dirname + "/" + name; lua::pushstring(L, file); @@ -240,7 +240,7 @@ static int l_read_combined_object(lua::State* L) { static int l_is_writeable(lua::State* L) { std::string rawpath = lua::require_string(L, 1); - fs::path path = resolve_path(rawpath); + io::path path = resolve_path(rawpath); auto entryPoint = rawpath.substr(0, rawpath.find(':')); if (writeable_entry_points.find(entryPoint) == writeable_entry_points.end()) { return lua::pushboolean(L, false); diff --git a/src/logic/scripting/lua/libs/libgeneration.cpp b/src/logic/scripting/lua/libs/libgeneration.cpp index 3f4bc570..196dae74 100644 --- a/src/logic/scripting/lua/libs/libgeneration.cpp +++ b/src/logic/scripting/lua/libs/libgeneration.cpp @@ -38,8 +38,8 @@ static int l_load_fragment(lua::State* L) { const auto& paths = engine->getPaths(); auto filename = lua::require_string(L, 1); auto path = paths.resolve(filename); - if (!std::filesystem::exists(path)) { - throw std::runtime_error("file "+path.u8string()+" does not exist"); + if (!io::exists(path)) { + throw std::runtime_error("file "+path.string()+" does not exist"); } auto map = io::read_binary_json(path); diff --git a/src/logic/scripting/lua/libs/libinput.cpp b/src/logic/scripting/lua/libs/libinput.cpp index 6796ade2..09f762ef 100644 --- a/src/logic/scripting/lua/libs/libinput.cpp +++ b/src/logic/scripting/lua/libs/libinput.cpp @@ -135,12 +135,12 @@ static int l_is_pressed(lua::State* L) { } } -static void resetPackBindings(fs::path& packFolder) { - auto configFolder = packFolder/fs::path("config"); - auto bindsFile = configFolder/fs::path("bindings.toml"); - if (fs::is_regular_file(bindsFile)) { +static void reset_pack_bindings(const io::path& packFolder) { + auto configFolder = packFolder / "config"; + auto bindsFile = configFolder / "bindings.toml"; + if (io::is_regular_file(bindsFile)) { Events::loadBindings( - bindsFile.u8string(), + bindsFile.string(), io::read_string(bindsFile), BindType::REBIND ); @@ -148,10 +148,9 @@ static void resetPackBindings(fs::path& packFolder) { } static int l_reset_bindings(lua::State*) { - auto resFolder = engine->getPaths().getResourcesFolder(); - resetPackBindings(resFolder); + reset_pack_bindings("res:"); for (auto& pack : engine->getContentPacks()) { - resetPackBindings(pack.folder); + reset_pack_bindings(pack.folder); } return 0; } diff --git a/src/logic/scripting/lua/libs/libpack.cpp b/src/logic/scripting/lua/libs/libpack.cpp index 99561538..dcff659a 100644 --- a/src/logic/scripting/lua/libs/libpack.cpp +++ b/src/logic/scripting/lua/libs/libpack.cpp @@ -21,7 +21,7 @@ static int l_pack_get_folder(lua::State* L) { for (auto& pack : packs) { if (pack.id == packName) { - return lua::pushstring(L, pack.folder.u8string() + "/"); + return lua::pushstring(L, pack.folder.string() + "/"); } } return lua::pushstring(L, ""); @@ -40,7 +40,7 @@ static int l_pack_get_installed(lua::State* L) { /// @brief pack.get_available() -> array static int l_pack_get_available(lua::State* L) { - fs::path worldFolder(""); + io::path worldFolder; if (level) { worldFolder = level->getWorld()->wfile->getFolder(); } @@ -88,7 +88,7 @@ static int l_pack_get_info( auto assets = engine->getAssets(); std::string icon = pack.id + ".icon"; if (!AssetsLoader::loadExternalTexture( - assets, icon, {pack.folder / fs::path("icon.png")} + assets, icon, {pack.folder / "icon.png"} )) { icon = "gui/no_icon"; } @@ -146,7 +146,7 @@ static int pack_get_infos(lua::State* L) { } } if (!ids.empty()) { - fs::path worldFolder(""); + io::path worldFolder; if (level) { worldFolder = level->getWorld()->wfile->getFolder(); } @@ -188,7 +188,7 @@ static int l_pack_get_info(lua::State* L) { return pack.id == packid; }); if (found == packs.end()) { - fs::path worldFolder(""); + io::path worldFolder; if (level) { worldFolder = level->getWorld()->wfile->getFolder(); } @@ -225,7 +225,7 @@ static int l_pack_assemble(lua::State* L) { ids.push_back(lua::require_string(L, -1)); lua::pop(L); } - fs::path worldFolder(""); + io::path worldFolder; if (level) { worldFolder = level->getWorld()->wfile->getFolder(); } diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 07c64feb..9f640039 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -49,7 +49,7 @@ static int l_get_list(lua::State* L) { int versionMajor = versionMap["major"].asInteger(); int versionMinor = versionMap["minor"].asInteger(); - auto name = folder.filename().u8string(); + auto name = folder.name(); lua::pushstring(L, name); lua::setfield(L, "name"); @@ -105,7 +105,7 @@ static int l_get_seed(lua::State* L) { static int l_exists(lua::State* L) { auto name = lua::require_string(L, 1); auto worldsDir = engine->getPaths().getWorldFolderByName(name); - return lua::pushboolean(L, fs::is_directory(worldsDir)); + return lua::pushboolean(L, io::is_directory(worldsDir)); } static int l_is_day(lua::State* L) { diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 6a494aa2..59b9cc39 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -125,7 +125,7 @@ void lua::initialize(const EnginePaths& paths, const CoreParameters& params) { main_thread = create_state( paths, params.headless ? StateType::SCRIPT : StateType::BASE ); - lua::pushstring(main_thread, params.scriptFile.stem().u8string()); + lua::pushstring(main_thread, params.scriptFile.stem()); lua::setglobal(main_thread, "__VC_SCRIPT_NAME"); } @@ -159,8 +159,7 @@ State* lua::create_state(const EnginePaths& paths, StateType stateType) { } init_state(L, stateType); - auto resDir = paths.getResourcesFolder(); - auto file = resDir / fs::u8path("scripts/stdmin.lua"); + auto file = "res:scripts/stdmin.lua"; auto src = io::read_string(file); lua::pop(L, lua::execute(L, 0, src, "core:scripts/stdmin.lua")); return L; diff --git a/src/logic/scripting/lua/lua_extensions.cpp b/src/logic/scripting/lua/lua_extensions.cpp index e1d7a230..37ed5d01 100644 --- a/src/logic/scripting/lua/lua_extensions.cpp +++ b/src/logic/scripting/lua/lua_extensions.cpp @@ -1,4 +1,5 @@ #include +#include #include "libs/api_lua.hpp" #include "debug/Logger.hpp" diff --git a/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp index 84aadbc0..983f9ba3 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp @@ -68,7 +68,7 @@ static int l_dump(lua::State* L) { raster[i*3 + 2] = val; } } - imageio::write(file.u8string(), &image); + imageio::write(file, &image); } return 0; } diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index ba39e334..92ed66a2 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -41,12 +41,11 @@ const ContentIndices* scripting::indices = nullptr; BlocksController* scripting::blocks = nullptr; LevelController* scripting::controller = nullptr; -void scripting::load_script(const fs::path& name, bool throwable) { - const auto& paths = scripting::engine->getPaths(); - fs::path file = paths.getResourcesFolder() / fs::path("scripts") / name; +void scripting::load_script(const io::path& name, bool throwable) { + io::path file = io::path("res:scripts") / name; std::string src = io::read_string(file); auto L = lua::get_main_state(); - lua::loadbuffer(L, 0, src, "core:scripts/"+name.u8string()); + lua::loadbuffer(L, 0, src, "core:scripts/"+name.string()); if (throwable) { lua::call(L, 0, 0); } else { @@ -57,11 +56,11 @@ void scripting::load_script(const fs::path& name, bool throwable) { int scripting::load_script( int env, const std::string& type, - const fs::path& file, + const io::path& file, const std::string& fileName ) { std::string src = io::read_string(file); - logger.info() << "script (" << type << ") " << file.u8string(); + logger.info() << "script (" << type << ") " << file.string(); return lua::execute(lua::get_main_state(), env, src, fileName); } @@ -69,8 +68,8 @@ void scripting::initialize(Engine* engine) { scripting::engine = engine; lua::initialize(engine->getPaths(), engine->getCoreParameters()); - load_script(fs::path("stdlib.lua"), true); - load_script(fs::path("classes.lua"), true); + load_script(io::path("stdlib.lua"), true); + load_script(io::path("classes.lua"), true); } class LuaCoroutine : public Process { @@ -113,12 +112,12 @@ public: }; std::unique_ptr scripting::start_coroutine( - const std::filesystem::path& script + const io::path& script ) { auto L = lua::get_main_state(); if (lua::getglobal(L, "__vc_start_coroutine")) { auto source = io::read_string(script); - lua::loadbuffer(L, 0, source, script.filename().u8string()); + lua::loadbuffer(L, 0, source, script.name()); if (lua::call(L, 1)) { int id = lua::tointeger(L, -1); lua::pop(L, 2); @@ -253,8 +252,8 @@ void scripting::on_content_load(Content* content) { lua::setfield(L, "properties"); lua::pop(L); } - load_script(fs::path("post_content.lua"), true); - load_script(fs::path("stdcmd.lua"), true); + load_script("post_content.lua", true); + load_script("stdcmd.lua", true); } void scripting::on_world_load(LevelController* controller) { @@ -829,7 +828,7 @@ int scripting::get_values_on_stack() { void scripting::load_content_script( const scriptenv& senv, const std::string& prefix, - const fs::path& file, + const io::path& file, const std::string& fileName, BlockFuncsSet& funcsset ) { @@ -854,7 +853,7 @@ void scripting::load_content_script( void scripting::load_content_script( const scriptenv& senv, const std::string& prefix, - const fs::path& file, + const io::path& file, const std::string& fileName, ItemFuncsSet& funcsset ) { @@ -869,11 +868,11 @@ void scripting::load_content_script( } void scripting::load_entity_component( - const std::string& name, const fs::path& file, const std::string& fileName + const std::string& name, const io::path& file, const std::string& fileName ) { auto L = lua::get_main_state(); std::string src = io::read_string(file); - logger.info() << "script (component) " << file.u8string(); + logger.info() << "script (component) " << file.string(); lua::loadbuffer(L, 0, src, fileName); lua::store_in(L, lua::CHUNKS_TABLE, name); } @@ -881,7 +880,7 @@ void scripting::load_entity_component( void scripting::load_world_script( const scriptenv& senv, const std::string& prefix, - const fs::path& file, + const io::path& file, const std::string& fileName, WorldFuncsSet& funcsset ) { @@ -917,7 +916,7 @@ void scripting::load_world_script( void scripting::load_layout_script( const scriptenv& senv, const std::string& prefix, - const fs::path& file, + const io::path& file, const std::string& fileName, uidocscript& script ) { diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index b447de28..bdd5d5d2 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -1,11 +1,11 @@ #pragma once -#include #include #include #include #include +#include "io/fwd.hpp" #include "data/dv.hpp" #include "delegates.hpp" #include "typedefs.hpp" @@ -61,7 +61,7 @@ namespace scripting { void process_post_runnables(); std::unique_ptr start_coroutine( - const std::filesystem::path& script + const io::path& script ); void on_world_load(LevelController* controller); @@ -150,7 +150,7 @@ namespace scripting { void load_content_script( const scriptenv& env, const std::string& prefix, - const std::filesystem::path& file, + const io::path& file, const std::string& fileName, BlockFuncsSet& funcsset ); @@ -164,7 +164,7 @@ namespace scripting { void load_content_script( const scriptenv& env, const std::string& prefix, - const std::filesystem::path& file, + const io::path& file, const std::string& fileName, ItemFuncsSet& funcsset ); @@ -175,13 +175,13 @@ namespace scripting { /// @param fileName script file path using the engine format void load_entity_component( const std::string& name, - const std::filesystem::path& file, + const io::path& file, const std::string& fileName ); std::unique_ptr load_generator( const GeneratorDef& def, - const std::filesystem::path& file, + const io::path& file, const std::string& dirPath ); @@ -193,7 +193,7 @@ namespace scripting { void load_world_script( const scriptenv& env, const std::string& packid, - const std::filesystem::path& file, + const io::path& file, const std::string& fileName, WorldFuncsSet& funcsset ); @@ -207,7 +207,7 @@ namespace scripting { void load_layout_script( const scriptenv& env, const std::string& prefix, - const std::filesystem::path& file, + const io::path& file, const std::string& fileName, uidocscript& script ); diff --git a/src/logic/scripting/scripting_commons.hpp b/src/logic/scripting/scripting_commons.hpp index 3bc31825..493e115c 100644 --- a/src/logic/scripting/scripting_commons.hpp +++ b/src/logic/scripting/scripting_commons.hpp @@ -1,15 +1,16 @@ #pragma once #include -#include + +#include "io/fwd.hpp" namespace scripting { - void load_script(const std::filesystem::path& name, bool throwable); + void load_script(const io::path& name, bool throwable); [[nodiscard]] int load_script( int env, const std::string& type, - const std::filesystem::path& file, + const io::path& file, const std::string& fileName ); } diff --git a/src/logic/scripting/scripting_hud.cpp b/src/logic/scripting/scripting_hud.cpp index 66b14387..d6f618cb 100644 --- a/src/logic/scripting/scripting_hud.cpp +++ b/src/logic/scripting/scripting_hud.cpp @@ -19,11 +19,11 @@ Hud* scripting::hud = nullptr; WorldRenderer* scripting::renderer = nullptr; static void load_script(const std::string& name) { - auto file = engine->getPaths().getResourcesFolder() / "scripts" / name; + auto file = io::path("res:scripts") / name; std::string src = io::read_string(file); - logger.info() << "loading script " << file.u8string(); + logger.info() << "loading script " << file.string(); - lua::execute(lua::get_main_state(), 0, src, file.u8string()); + lua::execute(lua::get_main_state(), 0, src, file.string()); } void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) { @@ -80,12 +80,12 @@ void scripting::on_frontend_close() { void scripting::load_hud_script( const scriptenv& senv, const std::string& packid, - const fs::path& file, + const io::path& file, const std::string& fileName ) { int env = *senv; std::string src = io::read_string(file); - logger.info() << "loading script " << file.u8string(); + logger.info() << "loading script " << file.string(); lua::execute(lua::get_main_state(), env, src, fileName); diff --git a/src/logic/scripting/scripting_hud.hpp b/src/logic/scripting/scripting_hud.hpp index f6829976..1df47df2 100644 --- a/src/logic/scripting/scripting_hud.hpp +++ b/src/logic/scripting/scripting_hud.hpp @@ -1,13 +1,11 @@ #pragma once -#include #include #include #include #include "typedefs.hpp" - -namespace fs = std::filesystem; +#include "io/fwd.hpp" class Hud; class WorldRenderer; @@ -33,7 +31,7 @@ namespace scripting { void load_hud_script( const scriptenv& env, const std::string& packid, - const fs::path& file, + const io::path& file, const std::string& fileName ); diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index 8a19e1b0..1e34122b 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -26,10 +26,15 @@ class LuaGeneratorScript : public GeneratorScript { const GeneratorDef& def; scriptenv env = nullptr; - fs::path file; + io::path file; std::string dirPath; public: - LuaGeneratorScript(State* L, const GeneratorDef& def, const fs::path& file, const std::string& dirPath) + LuaGeneratorScript( + State* L, + const GeneratorDef& def, + const io::path& file, + const std::string& dirPath + ) : L(L), def(def), file(file), dirPath(dirPath) { } @@ -54,10 +59,10 @@ public: pop(L); - if (fs::exists(file)) { + if (io::exists(file)) { std::string src = io::read_string(file); - logger.info() << "script (generator) " << file.u8string(); - pop(L, execute(L, *env, src, file.u8string())); + logger.info() << "script (generator) " << file.string(); + pop(L, execute(L, *env, src, file.string())); } else { // Use default (empty) script pop(L, execute(L, *env, "", "")); @@ -252,7 +257,7 @@ public: std::unique_ptr scripting::load_generator( const GeneratorDef& def, - const fs::path& file, + const io::path& file, const std::string& dirPath ) { auto L = create_state(engine->getPaths(), StateType::GENERATOR); diff --git a/src/util/command_line.cpp b/src/util/command_line.cpp index 6b579c01..ee0ace92 100644 --- a/src/util/command_line.cpp +++ b/src/util/command_line.cpp @@ -1,6 +1,5 @@ #include "command_line.hpp" -#include #include #include "io/engine_paths.hpp" @@ -14,10 +13,10 @@ static bool perform_keyword( ) { if (keyword == "--res") { auto token = reader.next(); - params.resFolder = fs::u8path(token); + params.resFolder = token; } else if (keyword == "--dir") { auto token = reader.next(); - params.userFolder = fs::u8path(token); + params.userFolder = token; } else if (keyword == "--help" || keyword == "-h") { std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n"; std::cout << "command-line arguments:\n"; @@ -38,11 +37,11 @@ static bool perform_keyword( } else if (keyword == "--test") { auto token = reader.next(); params.testMode = true; - params.scriptFile = fs::u8path(token); + params.scriptFile = token; } else if (keyword == "--script") { auto token = reader.next(); params.testMode = false; - params.scriptFile = fs::u8path(token); + params.scriptFile = token; } else { throw std::runtime_error("unknown argument " + keyword); } diff --git a/src/world/World.cpp b/src/world/World.cpp index a04e5e9b..313f75cd 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -78,7 +78,7 @@ void World::write(Level* level) { std::unique_ptr World::create( const std::string& name, const std::string& generator, - const fs::path& directory, + const io::path& directory, uint64_t seed, EngineSettings& settings, const Content& content, @@ -98,7 +98,7 @@ std::unique_ptr World::create( logger.info() << "created nameless world"; } else { logger.info() << "created world '" << name << "' (" - << directory.u8string() << ")"; + << directory.string() << ")"; } logger.info() << "world seed: " << seed << " generator: " << generator; return std::make_unique(std::move(world), content, settings); @@ -116,7 +116,7 @@ std::unique_ptr World::load( throw world_load_error("could not to find world.json"); } logger.info() << "loading world " << info->name << " (" - << worldFilesPtr->getFolder().u8string() << ")"; + << worldFilesPtr->getFolder().string() << ")"; logger.info() << "world version: " << info->major << "." << info->minor << " seed: " << info->seed << " generator: " << info->generator; @@ -129,8 +129,8 @@ std::unique_ptr World::load( auto level = std::make_unique(std::move(world), content, settings); - fs::path file = wfile->getPlayerFile(); - if (!fs::is_regular_file(file)) { + io::path file = wfile->getPlayerFile(); + if (!io::is_regular_file(file)) { logger.warning() << "player.json does not exists"; level->players->create(); } else { @@ -149,8 +149,8 @@ std::unique_ptr World::load( std::shared_ptr World::checkIndices( const std::shared_ptr& worldFiles, const Content* content ) { - fs::path indicesFile = worldFiles->getIndicesFile(); - if (fs::is_regular_file(indicesFile)) { + io::path indicesFile = worldFiles->getIndicesFile(); + if (io::is_regular_file(indicesFile)) { return ContentReport::create(worldFiles, indicesFile, content); } return nullptr; diff --git a/src/world/World.hpp b/src/world/World.hpp index da1ebd81..531e7084 100644 --- a/src/world/World.hpp +++ b/src/world/World.hpp @@ -1,10 +1,10 @@ #pragma once -#include #include #include #include +#include "io/fwd.hpp" #include "content/ContentPack.hpp" #include "interfaces/Serializable.hpp" #include "typedefs.hpp" @@ -16,8 +16,6 @@ class Level; class ContentReport; struct EngineSettings; -namespace fs = std::filesystem; - class world_load_error : public std::runtime_error { public: world_load_error(const std::string& message); @@ -100,7 +98,7 @@ public: static std::unique_ptr create( const std::string& name, const std::string& generator, - const fs::path& directory, + const io::path& directory, uint64_t seed, EngineSettings& settings, const Content& content, diff --git a/src/world/files/RegionsLayer.cpp b/src/world/files/RegionsLayer.cpp index 027d6bf3..f5c5b696 100644 --- a/src/world/files/RegionsLayer.cpp +++ b/src/world/files/RegionsLayer.cpp @@ -6,8 +6,8 @@ #define REGION_FORMAT_MAGIC ".VOXREG" -static fs::path get_region_filename(int x, int z) { - return fs::path(std::to_string(x) + "_" + std::to_string(z) + ".bin"); +static io::path get_region_filename(int x, int z) { + return std::to_string(x) + "_" + std::to_string(z) + ".bin"; } /// @brief Read missing chunks data (null pointers) from region file @@ -25,7 +25,7 @@ static void fetch_chunks(WorldRegion* region, int x, int z, regfile* file) { } } -regfile::regfile(fs::path filename) : file(std::move(filename)) { +regfile::regfile(io::path filename) : file(std::move(filename)) { if (file.length() < REGION_HEADER_SIZE) throw std::runtime_error("incomplete region file header"); char header[REGION_HEADER_SIZE]; @@ -98,8 +98,8 @@ regfile_ptr RegionsLayer::getRegFile(glm::ivec2 coord, bool create) { } regfile_ptr RegionsLayer::createRegFile(glm::ivec2 coord) { - auto file = folder / get_region_filename(coord[0], coord[1]); - if (!fs::exists(file)) { + auto file = folder / get_region_filename(coord[0], coord[1]); + if (!io::exists(file)) { return nullptr; } if (openRegFiles.size() == MAX_OPEN_REGION_FILES) { @@ -138,7 +138,7 @@ WorldRegion* RegionsLayer::getRegion(int x, int z) { return found->second.get(); } -fs::path RegionsLayer::getRegionFilePath(int x, int z) const { +io::path RegionsLayer::getRegionFilePath(int x, int z) const { return folder / get_region_filename(x, z); } @@ -179,7 +179,7 @@ ubyte* RegionsLayer::getData(int x, int z, uint32_t& size, uint32_t& srcSize) { } void RegionsLayer::writeRegion(int x, int z, WorldRegion* entry) { - fs::path filename = folder / get_region_filename(x, z); + io::path filename = folder / get_region_filename(x, z); glm::ivec2 regcoord(x, z); if (auto regfile = getRegFile(regcoord, false)) { @@ -193,7 +193,7 @@ void RegionsLayer::writeRegion(int x, int z, WorldRegion* entry) { char header[REGION_HEADER_SIZE] = REGION_FORMAT_MAGIC; header[8] = REGION_FORMAT_VERSION; header[9] = static_cast(compression); // FIXME - std::ofstream file(filename, std::ios::out | std::ios::binary); + std::ofstream file(io::resolve(filename), std::ios::out | std::ios::binary); file.write(header, REGION_HEADER_SIZE); size_t offset = REGION_HEADER_SIZE; diff --git a/src/world/files/WorldConverter.cpp b/src/world/files/WorldConverter.cpp index 287999de..3361334e 100644 --- a/src/world/files/WorldConverter.cpp +++ b/src/world/files/WorldConverter.cpp @@ -39,17 +39,17 @@ void WorldConverter::addRegionsTasks( ) { const auto& regions = wfile->getRegions(); auto regionsFolder = regions.getRegionsFolder(layerid); - if (!fs::is_directory(regionsFolder)) { + if (!io::is_directory(regionsFolder)) { return; } - for (const auto& file : fs::directory_iterator(regionsFolder)) { + for (const auto& file : fs::directory_iterator(io::resolve(regionsFolder))) { int x, z; std::string name = file.path().stem().string(); if (!WorldRegions::parseRegionFilename(name, x, z)) { logger.error() << "could not parse region name " << name; continue; } - tasks.push(ConvertTask {taskType, file.path(), x, z, layerid}); + tasks.push(ConvertTask {taskType, file.path().u8string(), x, z, layerid}); } } @@ -182,7 +182,7 @@ std::shared_ptr WorldConverter::startTask( } void WorldConverter::upgradeRegion( - const fs::path& file, int x, int z, RegionLayerIndex layer + const io::path& file, int x, int z, RegionLayerIndex layer ) const { auto path = wfile->getRegions().getRegionFilePath(layer, x, z); auto bytes = io::read_bytes_buffer(path); @@ -190,7 +190,7 @@ void WorldConverter::upgradeRegion( io::write_bytes(path, buffer.data(), buffer.size()); } -void WorldConverter::convertVoxels(const fs::path& file, int x, int z) const { +void WorldConverter::convertVoxels(const io::path& file, int x, int z) const { logger.info() << "converting voxels region " << x << "_" << z; wfile->getRegions().processRegion(x, z, REGION_LAYER_VOXELS, [=](std::unique_ptr data, uint32_t*) { @@ -199,15 +199,15 @@ void WorldConverter::convertVoxels(const fs::path& file, int x, int z) const { }); } -void WorldConverter::convertInventories(const fs::path& file, int x, int z) const { +void WorldConverter::convertInventories(const io::path& file, int x, int z) const { logger.info() << "converting inventories region " << x << "_" << z; wfile->getRegions().processInventories(x, z, [=](Inventory* inventory) { inventory->convert(report.get()); }); } -void WorldConverter::convertPlayer(const fs::path& file) const { - logger.info() << "converting player " << file.u8string(); +void WorldConverter::convertPlayer(const io::path& file) const { + logger.info() << "converting player " << file.string(); auto map = io::read_json(file); Player::convert(map, report.get()); io::write_json(file, map); @@ -242,7 +242,7 @@ void WorldConverter::convertBlocksData(int x, int z, const ContentReport& report } void WorldConverter::convert(const ConvertTask& task) const { - if (!fs::is_regular_file(task.file)) return; + if (!io::is_regular_file(task.file)) return; switch (task.type) { case ConvertTaskType::UPGRADE_REGION: diff --git a/src/world/files/WorldConverter.hpp b/src/world/files/WorldConverter.hpp index 499fba3c..8c065c47 100644 --- a/src/world/files/WorldConverter.hpp +++ b/src/world/files/WorldConverter.hpp @@ -1,16 +1,14 @@ #pragma once -#include #include #include #include "delegates.hpp" #include "interfaces/Task.hpp" +#include "io/io.hpp" #include "world/files/world_regions_fwd.hpp" #include "typedefs.hpp" -namespace fs = std::filesystem; - class Content; class ContentReport; class WorldFiles; @@ -30,7 +28,7 @@ enum class ConvertTaskType { struct ConvertTask { ConvertTaskType type; - fs::path file; + io::path file; /// @brief region coords int x, z; @@ -53,10 +51,10 @@ class WorldConverter : public Task { ConvertMode mode; void upgradeRegion( - const fs::path& file, int x, int z, RegionLayerIndex layer) const; - void convertPlayer(const fs::path& file) const; - void convertVoxels(const fs::path& file, int x, int z) const; - void convertInventories(const fs::path& file, int x, int z) const; + const io::path& file, int x, int z, RegionLayerIndex layer) const; + void convertPlayer(const io::path& file) const; + void convertVoxels(const io::path& file, int x, int z) const; + void convertInventories(const io::path& file, int x, int z) const; void convertBlocksData(int x, int z, const ContentReport& report) const; void addRegionsTasks( diff --git a/src/world/files/WorldFiles.cpp b/src/world/files/WorldFiles.cpp index 73132f33..3617ad7c 100644 --- a/src/world/files/WorldFiles.cpp +++ b/src/world/files/WorldFiles.cpp @@ -36,11 +36,11 @@ static debug::Logger logger("world-files"); -WorldFiles::WorldFiles(const fs::path& directory) +WorldFiles::WorldFiles(const io::path& directory) : directory(directory), regions(directory) { } -WorldFiles::WorldFiles(const fs::path& directory, const DebugSettings& settings) +WorldFiles::WorldFiles(const io::path& directory, const DebugSettings& settings) : WorldFiles(directory) { generatorTestMode = settings.generatorTestMode.get(); doWriteLights = settings.doWriteLights.get(); @@ -51,28 +51,28 @@ WorldFiles::WorldFiles(const fs::path& directory, const DebugSettings& settings) WorldFiles::~WorldFiles() = default; void WorldFiles::createDirectories() { - fs::create_directories(directory / fs::path("data")); - fs::create_directories(directory / fs::path("content")); + io::create_directories(directory / "data"); + io::create_directories(directory / "content"); } -fs::path WorldFiles::getPlayerFile() const { - return directory / fs::path("player.json"); +io::path WorldFiles::getPlayerFile() const { + return directory / "player.json"; } -fs::path WorldFiles::getResourcesFile() const { - return directory / fs::path("resources.json"); +io::path WorldFiles::getResourcesFile() const { + return directory / "resources.json"; } -fs::path WorldFiles::getWorldFile() const { - return directory / fs::path(WORLD_FILE); +io::path WorldFiles::getWorldFile() const { + return directory / WORLD_FILE; } -fs::path WorldFiles::getIndicesFile() const { - return directory / fs::path("indices.json"); +io::path WorldFiles::getIndicesFile() const { + return directory / "indices.json"; } -fs::path WorldFiles::getPacksFile() const { - return directory / fs::path("packs.list"); +io::path WorldFiles::getPacksFile() const { + return directory / "packs.list"; } void WorldFiles::write( @@ -80,7 +80,7 @@ void WorldFiles::write( ) { if (world) { writeWorldInfo(world->getInfo()); - if (!fs::exists(getPacksFile())) { + if (!io::exists(getPacksFile())) { writePacks(world->getPacks()); } } @@ -147,8 +147,8 @@ void WorldFiles::writeWorldInfo(const WorldInfo& info) { } std::optional WorldFiles::readWorldInfo() { - fs::path file = getWorldFile(); - if (!fs::is_regular_file(file)) { + io::path file = getWorldFile(); + if (!io::is_regular_file(file)) { logger.warning() << "world.json does not exists"; return std::nullopt; } @@ -175,8 +175,8 @@ static void read_resources_data( } bool WorldFiles::readResourcesData(const Content& content) { - fs::path file = getResourcesFile(); - if (!fs::is_regular_file(file)) { + io::path file = getResourcesFile(); + if (!io::is_regular_file(file)) { logger.warning() << "resources.json does not exists"; return false; } @@ -192,9 +192,9 @@ bool WorldFiles::readResourcesData(const Content& content) { } void WorldFiles::patchIndicesFile(const dv::value& map) { - fs::path file = getIndicesFile(); - if (!fs::is_regular_file(file)) { - logger.error() << file.filename().u8string() << " does not exists"; + io::path file = getIndicesFile(); + if (!io::is_regular_file(file)) { + logger.error() << file.name() << " does not exists"; return; } auto root = io::read_json(file); @@ -230,6 +230,6 @@ void WorldFiles::removeIndices(const std::vector& packs) { io::write_json(getIndicesFile(), root); } -fs::path WorldFiles::getFolder() const { +io::path WorldFiles::getFolder() const { return directory; } diff --git a/src/world/files/WorldFiles.hpp b/src/world/files/WorldFiles.hpp index 7c4fb2cd..f14c3c33 100644 --- a/src/world/files/WorldFiles.hpp +++ b/src/world/files/WorldFiles.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -24,28 +23,26 @@ class World; struct WorldInfo; struct DebugSettings; -namespace fs = std::filesystem; - class WorldFiles { - fs::path directory; + io::path directory; WorldRegions regions; bool generatorTestMode = false; bool doWriteLights = true; - fs::path getWorldFile() const; - fs::path getPacksFile() const; + io::path getWorldFile() const; + io::path getPacksFile() const; void writeWorldInfo(const WorldInfo& info); void writeIndices(const ContentIndices* indices); public: - WorldFiles(const fs::path& directory); - WorldFiles(const fs::path& directory, const DebugSettings& settings); + WorldFiles(const io::path& directory); + WorldFiles(const io::path& directory, const DebugSettings& settings); ~WorldFiles(); - fs::path getPlayerFile() const; - fs::path getIndicesFile() const; - fs::path getResourcesFile() const; + io::path getPlayerFile() const; + io::path getIndicesFile() const; + io::path getResourcesFile() const; void createDirectories(); std::optional readWorldInfo(); @@ -70,7 +67,7 @@ public: void removeIndices(const std::vector& packs); /// @return world folder - fs::path getFolder() const; + io::path getFolder() const; WorldRegions& getRegions() { return regions; diff --git a/src/world/files/WorldRegions.cpp b/src/world/files/WorldRegions.cpp index 54a25ee9..7c1ab08c 100644 --- a/src/world/files/WorldRegions.cpp +++ b/src/world/files/WorldRegions.cpp @@ -57,24 +57,24 @@ glm::u32vec2 WorldRegion::getChunkDataSize(uint x, uint z) { return sizes[z * REGION_SIZE + x]; } -WorldRegions::WorldRegions(const fs::path& directory) : directory(directory) { +WorldRegions::WorldRegions(const io::path& directory) : directory(directory) { for (size_t i = 0; i < REGION_LAYERS_COUNT; i++) { layers[i].layer = static_cast(i); } auto& voxels = layers[REGION_LAYER_VOXELS]; - voxels.folder = directory / fs::path("regions"); + voxels.folder = directory / "regions"; voxels.compression = compression::Method::EXTRLE16; auto& lights = layers[REGION_LAYER_LIGHTS]; - lights.folder = directory / fs::path("lights"); + lights.folder = directory / "lights"; lights.compression = compression::Method::EXTRLE8; layers[REGION_LAYER_INVENTORIES].folder = - directory / fs::path("inventories"); - layers[REGION_LAYER_ENTITIES].folder = directory / fs::path("entities"); + directory / "inventories"; + layers[REGION_LAYER_ENTITIES].folder = directory / "entities"; auto& blocksData = layers[REGION_LAYER_BLOCKS_DATA]; - blocksData.folder = directory / fs::path("blocksdata"); + blocksData.folder = directory / "blocksdata"; } WorldRegions::~WorldRegions() = default; @@ -388,17 +388,17 @@ void WorldRegions::processRegion( } } -const fs::path& WorldRegions::getRegionsFolder(RegionLayerIndex layerid) const { +const io::path& WorldRegions::getRegionsFolder(RegionLayerIndex layerid) const { return layers[layerid].folder; } -fs::path WorldRegions::getRegionFilePath(RegionLayerIndex layerid, int x, int z) const { +io::path WorldRegions::getRegionFilePath(RegionLayerIndex layerid, int x, int z) const { return layers[layerid].getRegionFilePath(x, z); } void WorldRegions::writeAll() { for (auto& layer : layers) { - fs::create_directories(layer.folder); + io::create_directories(layer.folder); layer.writeAll(); } } @@ -409,9 +409,9 @@ void WorldRegions::deleteRegion(RegionLayerIndex layerid, int x, int z) { throw std::runtime_error("region file is currently in use"); } auto file = layer.getRegionFilePath(x, z); - if (fs::exists(file)) { - logger.info() << "remove region file " << file.u8string(); - fs::remove(file); + if (io::exists(file)) { + logger.info() << "remove region file " << file.string(); + io::remove(file); } } diff --git a/src/world/files/WorldRegions.hpp b/src/world/files/WorldRegions.hpp index 3f2805b1..a50fc819 100644 --- a/src/world/files/WorldRegions.hpp +++ b/src/world/files/WorldRegions.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -19,8 +18,6 @@ #define GLM_ENABLE_EXPERIMENTAL #include -namespace fs = std::filesystem; - inline constexpr uint REGION_HEADER_SIZE = 10; inline constexpr uint REGION_SIZE_BIT = 5; @@ -58,7 +55,7 @@ struct regfile { int version; bool inUse = false; - regfile(fs::path filename); + regfile(io::path filename); regfile(const regfile&) = delete; std::unique_ptr read(int index, uint32_t& size, uint32_t& srcSize); @@ -121,7 +118,7 @@ struct RegionsLayer { RegionLayerIndex layer; /// @brief Regions layer folder - fs::path folder; + io::path folder; compression::Method compression = compression::Method::NONE; @@ -146,7 +143,7 @@ struct RegionsLayer { WorldRegion* getRegion(int x, int z); WorldRegion* getOrCreateRegion(int x, int z); - fs::path getRegionFilePath(int x, int z) const; + io::path getRegionFilePath(int x, int z) const; /// @brief Get chunk data. Read from file if not loaded yet. /// @param x chunk x coord @@ -178,14 +175,14 @@ struct RegionsLayer { class WorldRegions { /// @brief World directory - fs::path directory; + io::path directory; RegionsLayer layers[REGION_LAYERS_COUNT] {}; public: bool generatorTestMode = false; bool doWriteLights = true; - WorldRegions(const fs::path& directory); + WorldRegions(const io::path& directory); WorldRegions(const WorldRegions&) = delete; ~WorldRegions(); @@ -241,9 +238,9 @@ public: /// @brief Get regions directory by layer index /// @param layerid layer index /// @return directory path - const fs::path& getRegionsFolder(RegionLayerIndex layerid) const; + const io::path& getRegionsFolder(RegionLayerIndex layerid) const; - fs::path getRegionFilePath(RegionLayerIndex layerid, int x, int z) const; + io::path getRegionFilePath(RegionLayerIndex layerid, int x, int z) const; /// @brief Write all region layers void writeAll(); diff --git a/test/coders/lua_parsing.cpp b/test/coders/lua_parsing.cpp index bf35965e..64b15d44 100644 --- a/test/coders/lua_parsing.cpp +++ b/test/coders/lua_parsing.cpp @@ -7,7 +7,7 @@ TEST(lua_parsing, Tokenizer) { auto filename = "../../res/scripts/stdlib.lua"; - auto source = io::read_string(std::filesystem::u8path(filename)); + auto source = io::read_string(filename); try { auto tokens = lua::tokenize(filename, source); for (const auto& token : tokens) { diff --git a/test/coders/vec3.cpp b/test/coders/vec3.cpp index 6dc5e724..b7c8e37e 100644 --- a/test/coders/vec3.cpp +++ b/test/coders/vec3.cpp @@ -4,9 +4,7 @@ #include "io/io.hpp" TEST(VEC3, Decode) { - auto file = std::filesystem::u8path( - "res/models/block.vec3" - ); + io::path file = "res/models/block.vec3"; auto bytes = io::read_bytes_buffer(file); - auto model = vec3::load(file.u8string(), bytes); + auto model = vec3::load(file.string(), bytes); } diff --git a/test/io/path.cpp b/test/io/path.cpp new file mode 100644 index 00000000..86d5efe3 --- /dev/null +++ b/test/io/path.cpp @@ -0,0 +1,14 @@ +#include + +#include "io/path.hpp" + +TEST(Path, Path) { + io::path p("entry_point:path/file.ext"); + EXPECT_EQ(p, "entry_point:path/file.ext"); + EXPECT_EQ(p.pathPart(), "path/file.ext"); + EXPECT_EQ(p.name(), "file.ext"); + EXPECT_EQ(p.extension(), ".ext"); + EXPECT_EQ(p.entryPoint(), "entry_point"); + EXPECT_EQ(p / "child", "entry_point:path/file.ext/child"); + EXPECT_EQ(p.parent(), "entry_point:path"); +}