From 6286096b574e5dd2b6d80a3884ed43e0db9ee011 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 17 Mar 2025 23:00:36 +0300 Subject: [PATCH 1/5] fix ZipFileDevice::list --- src/io/devices/ZipFileDevice.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/io/devices/ZipFileDevice.cpp b/src/io/devices/ZipFileDevice.cpp index 15b8809c..947ef975 100644 --- a/src/io/devices/ZipFileDevice.cpp +++ b/src/io/devices/ZipFileDevice.cpp @@ -173,6 +173,20 @@ ZipFileDevice::ZipFileDevice( entries[entry.fileName] = std::move(entry); } + for (auto& [name, _] : entries) { + io::path path = name; + + while (!(path = path.parent()).pathPart().empty()) { + if (entries.find(path.pathPart()) != entries.end()) { + continue; + } + Entry entry {}; + entry.isDirectory = true; + entries[path.pathPart()] = entry; + } + break; + } + for (auto& [_, entry] : entries) { findBlob(entry); } @@ -243,6 +257,9 @@ bool ZipFileDevice::exists(std::string_view path) { } bool ZipFileDevice::isdir(std::string_view path) { + if (path.empty()) { + return true; + } const auto& found = entries.find(std::string(path)); if (found == entries.end()) { return false; From e8153f3393a6e05076bde279f91f73fc06cd6434 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 17 Mar 2025 23:01:44 +0300 Subject: [PATCH 2/5] add file.mount(...) and file.unmount(...) --- src/io/engine_paths.cpp | 55 +++++++++++++++++++----- src/io/engine_paths.hpp | 4 ++ src/logic/scripting/lua/libs/libfile.cpp | 13 ++++++ src/maths/util.hpp | 6 +++ 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/src/io/engine_paths.cpp b/src/io/engine_paths.cpp index 03973d60..d580e139 100644 --- a/src/io/engine_paths.cpp +++ b/src/io/engine_paths.cpp @@ -10,9 +10,24 @@ #include #include "io/devices/StdfsDevice.hpp" +#include "io/devices/ZipFileDevice.hpp" #include "world/files/WorldFiles.hpp" #include "debug/Logger.hpp" +#include +#include "maths/util.hpp" + +template +static std::string generate_random_base64() { + auto now = std::chrono::high_resolution_clock::now(); + auto seed = now.time_since_epoch().count(); + + util::PseudoRandom random(seed); // fixme: replace with safe random + ubyte bytes[n]; + random.rand(bytes, n); + return util::base64_urlsafe_encode(bytes, n); +} + namespace fs = std::filesystem; static debug::Logger logger("engine-paths"); @@ -150,8 +165,32 @@ void EnginePaths::setCurrentWorldFolder(io::path folder) { io::create_subdevice("world", "user", currentWorldFolder); } -#include -#include "maths/util.hpp" +std::string EnginePaths::mount(const io::path& file) { + if (file.extension() == ".zip") { + auto stream = io::read(file); + auto device = std::make_unique( + std::move(stream), [file]() { return io::read(file); } + ); + std::string name; + do { + name = std::string("M.") + generate_random_base64<6>(); + } while (std::find(mounted.begin(), mounted.end(), name) != mounted.end()); + + io::set_device(name, std::move(device)); + mounted.push_back(name); + return name; + } + throw std::runtime_error("unable to mount " + file.string()); +} + +void EnginePaths::unmount(const std::string& name) { + const auto& found = std::find(mounted.begin(), mounted.end(), name); + if (found == mounted.end()) { + throw std::runtime_error(name + " is not mounted"); + } + io::remove_device(name); + mounted.erase(found); +} std::string EnginePaths::createWriteablePackDevice(const std::string& name) { const auto& found = writeablePacks.find(name); @@ -168,14 +207,7 @@ std::string EnginePaths::createWriteablePackDevice(const std::string& name) { if (folder.emptyOrInvalid()) { throw std::runtime_error("pack not found"); } - - auto now = std::chrono::high_resolution_clock::now(); - auto seed = now.time_since_epoch().count(); - - util::PseudoRandom random(seed); // fixme: replace with safe random - auto number = random.rand64(); - auto entryPoint = std::string("W.") + util::base64_urlsafe_encode(reinterpret_cast(&number), 6); - + auto entryPoint = std::string("W.") + generate_random_base64<6>(); io::create_subdevice(entryPoint, folder.entryPoint(), folder.pathPart()); writeablePacks[name] = entryPoint; return entryPoint; @@ -189,6 +221,9 @@ void EnginePaths::setContentPacks(std::vector* contentPacks) { for (const auto& [_, entryPoint] : writeablePacks) { io::remove_device(entryPoint); } + for (const auto& entryPoint : mounted) { + io::remove_device(entryPoint); + } contentEntryPoints.clear(); this->contentPacks = contentPacks; // Create content devices diff --git a/src/io/engine_paths.hpp b/src/io/engine_paths.hpp index 579d2fe2..b7e6ebe2 100644 --- a/src/io/engine_paths.hpp +++ b/src/io/engine_paths.hpp @@ -34,6 +34,9 @@ public: io::path getControlsFile() const; io::path getSettingsFile() const; + std::string mount(const io::path& file); + void unmount(const std::string& name); + std::string createWriteablePackDevice(const std::string& name); void setContentPacks(std::vector* contentPacks); @@ -51,6 +54,7 @@ private: std::vector* contentPacks = nullptr; std::vector contentEntryPoints; std::unordered_map writeablePacks; + std::vector mounted; }; struct PathsRoot { diff --git a/src/logic/scripting/lua/libs/libfile.cpp b/src/logic/scripting/lua/libs/libfile.cpp index ba9a9f86..dea4a233 100644 --- a/src/logic/scripting/lua/libs/libfile.cpp +++ b/src/logic/scripting/lua/libs/libfile.cpp @@ -238,6 +238,17 @@ static int l_is_writeable(lua::State* L) { return lua::pushboolean(L, is_writeable(entryPoint)); } +static int l_mount(lua::State* L) { + auto& paths = engine->getPaths(); + return lua::pushstring(L, paths.mount(lua::require_string(L, 1))); +} + +static int l_unmount(lua::State* L) { + auto& paths = engine->getPaths(); + paths.unmount(lua::require_string(L, 1)); + return 0; +} + const luaL_Reg filelib[] = { {"exists", lua::wrap}, {"find", lua::wrap}, @@ -259,5 +270,7 @@ const luaL_Reg filelib[] = { {"read_combined_list", lua::wrap}, {"read_combined_object", lua::wrap}, {"is_writeable", lua::wrap}, + {"mount", lua::wrap}, + {"unmount", lua::wrap}, {NULL, NULL} }; diff --git a/src/maths/util.hpp b/src/maths/util.hpp index f417384d..00345942 100644 --- a/src/maths/util.hpp +++ b/src/maths/util.hpp @@ -37,6 +37,12 @@ namespace util { return static_cast(seed); } + void rand(unsigned char* dst, size_t n) { + for (size_t i = 0; i < n; i++) { + dst[i] = rand(); + } + } + int32_t rand32() { return (rand() << 16) | rand(); } From 5f71c82cc44815dfb26f5f42de430be9998b41f5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 17 Mar 2025 23:08:13 +0300 Subject: [PATCH 3/5] update doc/*/scripting/builtins/libfile.md --- doc/en/scripting/builtins/libfile.md | 12 ++++++++++++ doc/ru/scripting/builtins/libfile.md | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/doc/en/scripting/builtins/libfile.md b/doc/en/scripting/builtins/libfile.md index 21da09e7..4fa36d9e 100644 --- a/doc/en/scripting/builtins/libfile.md +++ b/doc/en/scripting/builtins/libfile.md @@ -121,6 +121,18 @@ file.read_combined_object(path: str) -> array Combines objects from JSON files of different packs. +```lua +file.mount(path: str) --> str +``` + +Mounts a ZIP archive to the filesystem. Returns the entry point name. + +```lua +file.unmount(entry_point: str) --> str +``` + +Unmounts the entry point. + ```lua file.name(path: str) --> str ``` diff --git a/doc/ru/scripting/builtins/libfile.md b/doc/ru/scripting/builtins/libfile.md index 1ac818d9..02f7917a 100644 --- a/doc/ru/scripting/builtins/libfile.md +++ b/doc/ru/scripting/builtins/libfile.md @@ -121,6 +121,18 @@ file.read_combined_object(путь: str) -> массив Совмещает объекты из JSON файлов разных паков. +```lua +file.mount(путь: str) --> str +``` + +Монтирует ZIP-архив как файловой системе. Возвращает имя точки входа. + +```lua +file.unmount(точка_входа: str) --> str +``` + +Размонтирует точку входа. + ```lua file.name(путь: str) --> str ``` From a086b726c6b9572a0686e1ccaee3baaaea6cf590 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 17 Mar 2025 23:16:50 +0300 Subject: [PATCH 4/5] add file.create_zip(...) --- src/io/devices/ZipFileDevice.cpp | 2 +- src/logic/scripting/lua/libs/libfile.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/io/devices/ZipFileDevice.cpp b/src/io/devices/ZipFileDevice.cpp index 947ef975..80021ba2 100644 --- a/src/io/devices/ZipFileDevice.cpp +++ b/src/io/devices/ZipFileDevice.cpp @@ -392,7 +392,7 @@ static size_t write_zip( ) { size_t entries = 0; for (const auto& entry : io::directory_iterator(folder)) { - auto name = entry.pathPart().substr(root.length() + 1); + auto name = entry.pathPart().substr(root.length()); auto last_write_time = io::last_write_time(entry); if (io::is_directory(entry)) { name = name + "/"; diff --git a/src/logic/scripting/lua/libs/libfile.cpp b/src/logic/scripting/lua/libs/libfile.cpp index dea4a233..495ec1f8 100644 --- a/src/logic/scripting/lua/libs/libfile.cpp +++ b/src/logic/scripting/lua/libs/libfile.cpp @@ -6,6 +6,7 @@ #include "engine/Engine.hpp" #include "io/engine_paths.hpp" #include "io/io.hpp" +#include "io/devices/ZipFileDevice.hpp" #include "util/stringutil.hpp" #include "api_lua.hpp" #include "../lua_engine.hpp" @@ -249,6 +250,16 @@ static int l_unmount(lua::State* L) { return 0; } +static int l_create_zip(lua::State* L) { + io::path folder = lua::require_string(L, 1); + io::path outFile = lua::require_string(L, 2); + if (!is_writeable(outFile.entryPoint())) { + throw std::runtime_error("access denied"); + } + io::write_zip(folder, outFile); + return 0; +} + const luaL_Reg filelib[] = { {"exists", lua::wrap}, {"find", lua::wrap}, @@ -272,5 +283,6 @@ const luaL_Reg filelib[] = { {"is_writeable", lua::wrap}, {"mount", lua::wrap}, {"unmount", lua::wrap}, + {"create_zip", lua::wrap}, {NULL, NULL} }; From 35ce256c5030b5ed976581edb5ceef2d0f7aba40 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 17 Mar 2025 23:18:44 +0300 Subject: [PATCH 5/5] update doc/*/scripting/builtins/libfile.md --- doc/en/scripting/builtins/libfile.md | 6 ++++++ doc/ru/scripting/builtins/libfile.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/doc/en/scripting/builtins/libfile.md b/doc/en/scripting/builtins/libfile.md index 4fa36d9e..80f7752b 100644 --- a/doc/en/scripting/builtins/libfile.md +++ b/doc/en/scripting/builtins/libfile.md @@ -133,6 +133,12 @@ file.unmount(entry_point: str) --> str Unmounts the entry point. +```lua +file.create_zip(directory: str, output_file: str) --> str +``` + +Creates a ZIP archive from the contents of the specified directory. + ```lua file.name(path: str) --> str ``` diff --git a/doc/ru/scripting/builtins/libfile.md b/doc/ru/scripting/builtins/libfile.md index 02f7917a..f764ed56 100644 --- a/doc/ru/scripting/builtins/libfile.md +++ b/doc/ru/scripting/builtins/libfile.md @@ -133,6 +133,12 @@ file.unmount(точка_входа: str) --> str Размонтирует точку входа. +```lua +file.create_zip(директория: str, выходной_файл: str) --> str +``` + +Создаёт ZIP-архив из содержимого указанной директории. + ```lua file.name(путь: str) --> str ```