diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index dfb2c7df..14d1298f 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -94,8 +94,7 @@ static void add_layouts( if (!io::is_directory(folder)) { return; } - for (auto& entry : fs::directory_iterator(io::resolve(folder))) { - io::path file = folder / entry.path().filename().u8string(); + for (const auto& file : io::directory_iterator(folder)) { if (file.extension() != ".xml") continue; std::string name = prefix + ":" + file.stem(); loader.add( diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 3c993786..c1b3f10a 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -50,8 +50,7 @@ static void detect_defs( 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(); + for (const auto& file : io::directory_iterator(folder)) { std::string name = file.stem(); if (name[0] == '_') { continue; @@ -75,8 +74,7 @@ static void detect_defs_pairs( if (!io::is_directory(folder)) { return; } - for (const auto& entry : fs::directory_iterator(io::resolve(folder))) { - io::path file = folder / entry.path().filename().u8string(); + for (const auto& file : io::directory_iterator(folder)) { std::string name = file.stem(); if (name[0] == '_') { continue; @@ -747,8 +745,7 @@ static inline void foreach_file( if (!io::is_directory(dir)) { return; } - for (const auto& entry : fs::directory_iterator(io::resolve(dir))) { - io::path path = dir / entry.path().filename().u8string(); + for (const auto& path : io::directory_iterator(dir)) { if (io::is_directory(path)) { continue; } @@ -808,8 +805,7 @@ void ContentLoader::load() { // Load block materials 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(); + for (const auto& file : io::directory_iterator(materialsDir)) { auto [packid, full, filename] = create_unit_id(pack->id, file.stem()); loadBlockMaterial( diff --git a/src/content/ContentPack.cpp b/src/content/ContentPack.cpp index 51960609..73a9503e 100644 --- a/src/content/ContentPack.cpp +++ b/src/content/ContentPack.cpp @@ -129,8 +129,7 @@ void ContentPack::scanFolder( if (!io::is_directory(folder)) { return; } - for (const auto& entry : fs::directory_iterator(io::resolve(folder))) { - io::path packFolder = folder / entry.path().filename().u8string(); + for (const auto& packFolder : io::directory_iterator(folder)) { if (!io::is_directory(packFolder)) continue; if (!is_pack(packFolder)) continue; try { diff --git a/src/io/devices/Device.hpp b/src/io/devices/Device.hpp index 39794d80..30d885fa 100644 --- a/src/io/devices/Device.hpp +++ b/src/io/devices/Device.hpp @@ -23,6 +23,7 @@ namespace io { 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; + virtual std::unique_ptr list(std::string_view path) = 0; }; class SubDevice : public Device { @@ -69,6 +70,10 @@ namespace io { uint64_t removeAll(std::string_view path) override { return parent->removeAll((root / path).pathPart()); } + + std::unique_ptr list(std::string_view path) override { + return parent->list((root / path).pathPart()); + } private: std::shared_ptr parent; path root; diff --git a/src/io/devices/StdfsDevice.cpp b/src/io/devices/StdfsDevice.cpp index 7078b394..89c585ca 100644 --- a/src/io/devices/StdfsDevice.cpp +++ b/src/io/devices/StdfsDevice.cpp @@ -4,9 +4,10 @@ #include using namespace io; +namespace fs = std::filesystem; -std::filesystem::path StdfsDevice::resolve(std::string_view path) { - return root / std::filesystem::u8path(path); +fs::path StdfsDevice::resolve(std::string_view path) { + return root / fs::u8path(path); } void StdfsDevice::write(std::string_view path, const void* data, size_t size) { @@ -29,35 +30,58 @@ void StdfsDevice::read(std::string_view path, void* data, size_t size) { size_t StdfsDevice::size(std::string_view path) { auto resolved = resolve(path); - return std::filesystem::file_size(resolved); + return fs::file_size(resolved); } bool StdfsDevice::exists(std::string_view path) { auto resolved = resolve(path); - return std::filesystem::exists(resolved); + return fs::exists(resolved); } bool StdfsDevice::isdir(std::string_view path) { auto resolved = resolve(path); - return std::filesystem::is_directory(resolved); + return fs::is_directory(resolved); } bool StdfsDevice::isfile(std::string_view path) { auto resolved = resolve(path); - return std::filesystem::is_regular_file(resolved); + return fs::is_regular_file(resolved); } void StdfsDevice::mkdirs(std::string_view path) { auto resolved = resolve(path); - std::filesystem::create_directories(resolved); + fs::create_directories(resolved); } bool StdfsDevice::remove(std::string_view path) { auto resolved = resolve(path); - return std::filesystem::remove(resolved); + return fs::remove(resolved); } uint64_t StdfsDevice::removeAll(std::string_view path) { auto resolved = resolve(path); - return std::filesystem::remove_all(resolved); + return fs::remove_all(resolved); +} + +class StdfsPathsGenerator : public PathsGenerator { +public: + StdfsPathsGenerator(fs::path root) : root(std::move(root)) { + it = fs::directory_iterator(this->root); + } + + bool next(io::path& path) override { + if (it == fs::directory_iterator()) { + return false; + } + path = it->path().filename().u8string(); + it++; + return true; + } +private: + fs::path root; + fs::directory_iterator it; +}; + +std::unique_ptr StdfsDevice::list(std::string_view path) { + return std::make_unique(root / fs::u8path(path)); } diff --git a/src/io/devices/StdfsDevice.hpp b/src/io/devices/StdfsDevice.hpp index 6c88949f..bf284ccb 100644 --- a/src/io/devices/StdfsDevice.hpp +++ b/src/io/devices/StdfsDevice.hpp @@ -15,6 +15,7 @@ namespace io { void mkdirs(std::string_view path) override; bool remove(std::string_view path) override; uint64_t removeAll(std::string_view path) override; + std::unique_ptr list(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 88fb98ca..5dc3ddfd 100644 --- a/src/io/engine_paths.cpp +++ b/src/io/engine_paths.cpp @@ -145,11 +145,10 @@ std::vector EnginePaths::scanForWorlds() const { auto folder = getWorldsFolder(); if (!io::is_directory(folder)) return folders; - for (const auto& entry : std::filesystem::directory_iterator(io::resolve(folder))) { - if (!entry.is_directory()) { + for (const auto& worldFolder : io::directory_iterator(folder)) { + if (!io::is_directory(worldFolder)) { continue; } - io::path worldFolder = folder / entry.path().filename().u8string(); auto worldFile = worldFolder / WorldFiles::WORLD_FILE; if (!io::is_regular_file(worldFile)) { continue; @@ -271,9 +270,8 @@ std::vector ResPaths::listdirRaw(const std::string& folderName) con auto& root = roots[i]; auto folder = root.path / fs::u8path(folderName); 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); + for (const auto& file : io::directory_iterator(folder)) { + entries.emplace_back(root.name + ":" + folderName + "/" + file.name()); } } return entries; @@ -287,8 +285,8 @@ std::vector ResPaths::listdir( auto& root = roots[i]; 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()); + for (const auto& entry : io::directory_iterator(folder)) { + entries.push_back(folder / entry); } } return entries; diff --git a/src/io/io.cpp b/src/io/io.cpp index 356357c5..b8d8d7d9 100644 --- a/src/io/io.cpp +++ b/src/io/io.cpp @@ -49,6 +49,12 @@ void io::create_subdevice( set_device(name, std::make_shared(parentDevice, root.pathPart())); } +io::directory_iterator::directory_iterator(const io::path& folder) + : folder(folder) { + auto& device = io::require_device(folder.entryPoint()); + generator = device.list(folder.pathPart()); +} + io::rafile::rafile(const io::path& filename) : file(io::resolve(filename), std::ios::binary | std::ios::ate) { if (!file) { @@ -171,7 +177,7 @@ std::vector io::read_list(const io::path& filename) { } bool io::is_regular_file(const io::path& file) { - if (file.empty()) { + if (file.emptyOrInvalid()) { return false; } auto device = io::get_device(file.entryPoint()); @@ -182,7 +188,7 @@ bool io::is_regular_file(const io::path& file) { } bool io::is_directory(const io::path& file) { - if (file.empty()) { + if (file.emptyOrInvalid()) { return false; } auto device = io::get_device(file.entryPoint()); @@ -193,7 +199,7 @@ bool io::is_directory(const io::path& file) { } bool io::exists(const io::path& file) { - if (file.empty()) { + if (file.emptyOrInvalid()) { return false; } auto device = io::get_device(file.entryPoint()); diff --git a/src/io/io.hpp b/src/io/io.hpp index 1cdc8198..435753f9 100644 --- a/src/io/io.hpp +++ b/src/io/io.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -34,6 +35,75 @@ namespace io { size_t length() const; }; + class directory_iterator_impl { + public: + using iterator_category = std::input_iterator_tag; + using value_type = path; + using difference_type = std::ptrdiff_t; + using pointer = path*; + using reference = path&; + + directory_iterator_impl( + PathsGenerator& generator, const path& folder, bool end = false + ) + : generator(generator), folder(folder), isend(end) { + if (!isend && this->generator.next(current)) { + isend = false; + current = folder / current; + } else { + isend = true; + } + } + + reference operator*() { + return current; + } + + pointer operator->() { + return ¤t; + } + + directory_iterator_impl& operator++() { + if (isend) { + return *this; + } + if (generator.next(current)) { + current = folder / current; + } else { + isend = true; + } + return *this; + } + + bool operator==(const directory_iterator_impl& other) const { + return isend == other.isend; + } + + bool operator!=(const directory_iterator_impl& other) const { + return !(*this == other); + } + private: + PathsGenerator& generator; + path folder; + path current; + bool isend = false; + }; + + class directory_iterator { + std::unique_ptr generator; + path folder; + public: + directory_iterator(const path& folder); + + directory_iterator_impl begin() { + return directory_iterator_impl(*generator, folder); + } + + directory_iterator_impl end() { + return directory_iterator_impl(*generator, "", true); + } + }; + /// @brief Write bytes array to the file without any extra data /// @param file target file /// @param data data bytes array @@ -87,7 +157,11 @@ namespace io { std::filesystem::path resolve(const io::path& file); + /// @brief Check if file is one of the supported data interchange formats bool is_data_file(const io::path& file); + + /// @brief Check if file extension is one of the supported data interchange formats bool is_data_interchange_format(const std::string& ext); + dv::value read_object(const path& file); } diff --git a/src/io/path.hpp b/src/io/path.hpp index 454ff161..f106b226 100644 --- a/src/io/path.hpp +++ b/src/io/path.hpp @@ -127,6 +127,10 @@ namespace io { bool empty() const { return str.empty(); } + + bool emptyOrInvalid() const { + return str.empty() || colonPos == std::string::npos; + } private: /// @brief UTF-8 string contains entry_point:path or empty string std::string str; @@ -135,4 +139,10 @@ namespace io { void checkValid() const; }; + + class PathsGenerator { + public: + virtual ~PathsGenerator() = default; + virtual bool next(path& dst) = 0; + }; } diff --git a/src/logic/scripting/lua/libs/libfile.cpp b/src/logic/scripting/lua/libs/libfile.cpp index 5c7dd046..c1843e2f 100644 --- a/src/logic/scripting/lua/libs/libfile.cpp +++ b/src/logic/scripting/lua/libs/libfile.cpp @@ -95,7 +95,8 @@ static int l_exists(lua::State* L) { } static int l_isfile(lua::State* L) { - io::path path = resolve_path_soft(lua::require_string(L, 1)); + const char* string =lua::require_string(L, 1); + io::path path = resolve_path_soft(string); return lua::pushboolean(L, io::is_regular_file(path)); } @@ -184,10 +185,8 @@ static int l_list(lua::State* L) { } lua::createtable(L, 0, 0); size_t index = 1; - for (auto& entry : fs::directory_iterator(io::resolve(path))) { - auto name = entry.path().filename().u8string(); - auto file = dirname + "/" + name; - lua::pushstring(L, file); + for (const auto& file : io::directory_iterator(path)) { + lua::pushstring(L, file.string()); lua::rawseti(L, index); index++; } diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index 9539fbbf..656bd504 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -1,13 +1,13 @@ #pragma once +#include #include #include -#include #include #include "data/dv.hpp" -#include "lua_wrapper.hpp" #include "lua_custom_types.hpp" +#include "lua_wrapper.hpp" #define GLM_ENABLE_EXPERIMENTAL #include @@ -68,7 +68,7 @@ namespace lua { return 1; } - template + template inline int pushvec_stack(lua::State* L, const glm::vec& vec) { for (int i = 0; i < n; i++) { pushnumber(L, vec[i]); @@ -76,7 +76,7 @@ namespace lua { return n; } - template + template inline int pushivec_stack(lua::State* L, const glm::vec& vec) { for (int i = 0; i < n; i++) { pushinteger(L, vec[i]); @@ -108,10 +108,10 @@ namespace lua { inline int pushquat(lua::State* L, glm::quat quat) { createtable(L, 4, 0); - + pushnumber(L, quat.w); rawseti(L, 1); - + pushnumber(L, quat.x); rawseti(L, 2); @@ -127,7 +127,7 @@ namespace lua { pushnumber(L, quat.w); rawseti(L, 1); - + pushnumber(L, quat.x); rawseti(L, 2); @@ -287,8 +287,9 @@ namespace lua { } template - inline std::enable_if_t> - newusertype(lua::State* L) { + inline std::enable_if_t> newusertype( + lua::State* L + ) { const std::string& name = T::TYPENAME; usertypeNames[typeid(T)] = name; T::createMetatable(L); @@ -386,7 +387,7 @@ namespace lua { rawgeti(L, 4); auto z = tonumber(L, -1); pop(L); - + pop(L); return glm::quat(x, y, z, w); } @@ -445,8 +446,7 @@ namespace lua { int pushvalue(lua::State*, const dv::value& value); - [[nodiscard]] - dv::value tovalue(lua::State*, int idx); + [[nodiscard]] dv::value tovalue(lua::State*, int idx); inline bool getfield(lua::State* L, const std::string& name, int idx = -1) { lua_getfield(L, idx, name.c_str()); @@ -457,11 +457,13 @@ namespace lua { return true; } - inline int requirefield(lua::State* L, const std::string& name, int idx = -1) { + inline int requirefield( + lua::State* L, const std::string& name, int idx = -1 + ) { if (getfield(L, name, idx)) { return 1; } - throw std::runtime_error("object has no member '"+name+"'"); + throw std::runtime_error("object has no member '" + name + "'"); } inline bool hasfield(lua::State* L, const std::string& name, int idx = -1) { @@ -637,7 +639,7 @@ namespace lua { } inline const char* require_string_field( - lua::State* L, const std::string& name, int idx=-1 + lua::State* L, const std::string& name, int idx = -1 ) { requirefield(L, name, idx); auto value = require_string(L, -1); @@ -646,7 +648,7 @@ namespace lua { } inline Integer require_integer_field( - lua::State* L, const std::string& name, int idx=-1 + lua::State* L, const std::string& name, int idx = -1 ) { requirefield(L, name, idx); auto value = tointeger(L, -1); @@ -655,7 +657,7 @@ namespace lua { } inline Number require_number_field( - lua::State* L, const std::string& name, int idx=-1 + lua::State* L, const std::string& name, int idx = -1 ) { requirefield(L, name, idx); auto value = tonumber(L, -1); @@ -664,7 +666,7 @@ namespace lua { } inline bool get_boolean_field( - lua::State* L, const std::string& name, bool def, int idx=-1 + lua::State* L, const std::string& name, bool def, int idx = -1 ) { if (getfield(L, name, idx)) { bool value = toboolean(L, -1); @@ -675,7 +677,7 @@ namespace lua { } inline Integer get_integer_field( - lua::State* L, const std::string& name, Integer def, int idx=-1 + lua::State* L, const std::string& name, Integer def, int idx = -1 ) { if (getfield(L, name, idx)) { auto value = tointeger(L, -1); @@ -686,7 +688,7 @@ namespace lua { } inline Number get_number_field( - lua::State* L, const std::string& name, Number def, int idx=-1 + lua::State* L, const std::string& name, Number def, int idx = -1 ) { if (getfield(L, name, idx)) { auto value = tonumber(L, -1); @@ -697,15 +699,20 @@ namespace lua { } inline Integer get_integer_field( - lua::State* L, const std::string& name, - Integer def, Integer min, Integer max, int idx=-1 + lua::State* L, + const std::string& name, + Integer def, + Integer min, + Integer max, + int idx = -1 ) { if (getfield(L, name, idx)) { auto value = tointeger(L, -1); if (value < min || value > max) { throw std::runtime_error( - "value is out of range [" - +std::to_string(min)+", "+std::to_string(max)+"]"); + "value is out of range [" + std::to_string(min) + ", " + + std::to_string(max) + "]" + ); } pop(L); return value; diff --git a/src/world/files/WorldConverter.cpp b/src/world/files/WorldConverter.cpp index 3361334e..007ad08c 100644 --- a/src/world/files/WorldConverter.cpp +++ b/src/world/files/WorldConverter.cpp @@ -42,14 +42,14 @@ void WorldConverter::addRegionsTasks( if (!io::is_directory(regionsFolder)) { return; } - for (const auto& file : fs::directory_iterator(io::resolve(regionsFolder))) { + for (const auto& file :io::directory_iterator(regionsFolder)) { int x, z; - std::string name = file.path().stem().string(); + std::string name = file.stem(); if (!WorldRegions::parseRegionFilename(name, x, z)) { logger.error() << "could not parse region name " << name; continue; } - tasks.push(ConvertTask {taskType, file.path().u8string(), x, z, layerid}); + tasks.push(ConvertTask {taskType, file, x, z, layerid}); } }