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(); }