commit
9e7bf705f2
@ -121,6 +121,24 @@ file.read_combined_object(path: str) -> array
|
|||||||
|
|
||||||
Combines objects from JSON files of different packs.
|
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.create_zip(directory: str, output_file: str) --> str
|
||||||
|
```
|
||||||
|
|
||||||
|
Creates a ZIP archive from the contents of the specified directory.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
file.name(path: str) --> str
|
file.name(path: str) --> str
|
||||||
```
|
```
|
||||||
|
|||||||
@ -121,6 +121,24 @@ file.read_combined_object(путь: str) -> массив
|
|||||||
|
|
||||||
Совмещает объекты из JSON файлов разных паков.
|
Совмещает объекты из JSON файлов разных паков.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
file.mount(путь: str) --> str
|
||||||
|
```
|
||||||
|
|
||||||
|
Монтирует ZIP-архив как файловой системе. Возвращает имя точки входа.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
file.unmount(точка_входа: str) --> str
|
||||||
|
```
|
||||||
|
|
||||||
|
Размонтирует точку входа.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
file.create_zip(директория: str, выходной_файл: str) --> str
|
||||||
|
```
|
||||||
|
|
||||||
|
Создаёт ZIP-архив из содержимого указанной директории.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
file.name(путь: str) --> str
|
file.name(путь: str) --> str
|
||||||
```
|
```
|
||||||
|
|||||||
@ -173,6 +173,20 @@ ZipFileDevice::ZipFileDevice(
|
|||||||
entries[entry.fileName] = std::move(entry);
|
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) {
|
for (auto& [_, entry] : entries) {
|
||||||
findBlob(entry);
|
findBlob(entry);
|
||||||
}
|
}
|
||||||
@ -243,6 +257,9 @@ bool ZipFileDevice::exists(std::string_view path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ZipFileDevice::isdir(std::string_view path) {
|
bool ZipFileDevice::isdir(std::string_view path) {
|
||||||
|
if (path.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const auto& found = entries.find(std::string(path));
|
const auto& found = entries.find(std::string(path));
|
||||||
if (found == entries.end()) {
|
if (found == entries.end()) {
|
||||||
return false;
|
return false;
|
||||||
@ -375,7 +392,7 @@ static size_t write_zip(
|
|||||||
) {
|
) {
|
||||||
size_t entries = 0;
|
size_t entries = 0;
|
||||||
for (const auto& entry : io::directory_iterator(folder)) {
|
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);
|
auto last_write_time = io::last_write_time(entry);
|
||||||
if (io::is_directory(entry)) {
|
if (io::is_directory(entry)) {
|
||||||
name = name + "/";
|
name = name + "/";
|
||||||
|
|||||||
@ -10,9 +10,24 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "io/devices/StdfsDevice.hpp"
|
#include "io/devices/StdfsDevice.hpp"
|
||||||
|
#include "io/devices/ZipFileDevice.hpp"
|
||||||
#include "world/files/WorldFiles.hpp"
|
#include "world/files/WorldFiles.hpp"
|
||||||
#include "debug/Logger.hpp"
|
#include "debug/Logger.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include "maths/util.hpp"
|
||||||
|
|
||||||
|
template<int n>
|
||||||
|
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;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
static debug::Logger logger("engine-paths");
|
static debug::Logger logger("engine-paths");
|
||||||
@ -150,8 +165,32 @@ void EnginePaths::setCurrentWorldFolder(io::path folder) {
|
|||||||
io::create_subdevice("world", "user", currentWorldFolder);
|
io::create_subdevice("world", "user", currentWorldFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <chrono>
|
std::string EnginePaths::mount(const io::path& file) {
|
||||||
#include "maths/util.hpp"
|
if (file.extension() == ".zip") {
|
||||||
|
auto stream = io::read(file);
|
||||||
|
auto device = std::make_unique<io::ZipFileDevice>(
|
||||||
|
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) {
|
std::string EnginePaths::createWriteablePackDevice(const std::string& name) {
|
||||||
const auto& found = writeablePacks.find(name);
|
const auto& found = writeablePacks.find(name);
|
||||||
@ -168,14 +207,7 @@ std::string EnginePaths::createWriteablePackDevice(const std::string& name) {
|
|||||||
if (folder.emptyOrInvalid()) {
|
if (folder.emptyOrInvalid()) {
|
||||||
throw std::runtime_error("pack not found");
|
throw std::runtime_error("pack not found");
|
||||||
}
|
}
|
||||||
|
auto entryPoint = std::string("W.") + generate_random_base64<6>();
|
||||||
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<ubyte*>(&number), 6);
|
|
||||||
|
|
||||||
io::create_subdevice(entryPoint, folder.entryPoint(), folder.pathPart());
|
io::create_subdevice(entryPoint, folder.entryPoint(), folder.pathPart());
|
||||||
writeablePacks[name] = entryPoint;
|
writeablePacks[name] = entryPoint;
|
||||||
return entryPoint;
|
return entryPoint;
|
||||||
@ -189,6 +221,9 @@ void EnginePaths::setContentPacks(std::vector<ContentPack>* contentPacks) {
|
|||||||
for (const auto& [_, entryPoint] : writeablePacks) {
|
for (const auto& [_, entryPoint] : writeablePacks) {
|
||||||
io::remove_device(entryPoint);
|
io::remove_device(entryPoint);
|
||||||
}
|
}
|
||||||
|
for (const auto& entryPoint : mounted) {
|
||||||
|
io::remove_device(entryPoint);
|
||||||
|
}
|
||||||
contentEntryPoints.clear();
|
contentEntryPoints.clear();
|
||||||
this->contentPacks = contentPacks;
|
this->contentPacks = contentPacks;
|
||||||
// Create content devices
|
// Create content devices
|
||||||
|
|||||||
@ -34,6 +34,9 @@ public:
|
|||||||
io::path getControlsFile() const;
|
io::path getControlsFile() const;
|
||||||
io::path getSettingsFile() 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);
|
std::string createWriteablePackDevice(const std::string& name);
|
||||||
|
|
||||||
void setContentPacks(std::vector<ContentPack>* contentPacks);
|
void setContentPacks(std::vector<ContentPack>* contentPacks);
|
||||||
@ -51,6 +54,7 @@ private:
|
|||||||
std::vector<ContentPack>* contentPacks = nullptr;
|
std::vector<ContentPack>* contentPacks = nullptr;
|
||||||
std::vector<std::string> contentEntryPoints;
|
std::vector<std::string> contentEntryPoints;
|
||||||
std::unordered_map<std::string, std::string> writeablePacks;
|
std::unordered_map<std::string, std::string> writeablePacks;
|
||||||
|
std::vector<std::string> mounted;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PathsRoot {
|
struct PathsRoot {
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include "engine/Engine.hpp"
|
#include "engine/Engine.hpp"
|
||||||
#include "io/engine_paths.hpp"
|
#include "io/engine_paths.hpp"
|
||||||
#include "io/io.hpp"
|
#include "io/io.hpp"
|
||||||
|
#include "io/devices/ZipFileDevice.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
#include "api_lua.hpp"
|
#include "api_lua.hpp"
|
||||||
#include "../lua_engine.hpp"
|
#include "../lua_engine.hpp"
|
||||||
@ -238,6 +239,27 @@ static int l_is_writeable(lua::State* L) {
|
|||||||
return lua::pushboolean(L, is_writeable(entryPoint));
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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[] = {
|
const luaL_Reg filelib[] = {
|
||||||
{"exists", lua::wrap<l_exists>},
|
{"exists", lua::wrap<l_exists>},
|
||||||
{"find", lua::wrap<l_find>},
|
{"find", lua::wrap<l_find>},
|
||||||
@ -259,5 +281,8 @@ const luaL_Reg filelib[] = {
|
|||||||
{"read_combined_list", lua::wrap<l_read_combined_list>},
|
{"read_combined_list", lua::wrap<l_read_combined_list>},
|
||||||
{"read_combined_object", lua::wrap<l_read_combined_object>},
|
{"read_combined_object", lua::wrap<l_read_combined_object>},
|
||||||
{"is_writeable", lua::wrap<l_is_writeable>},
|
{"is_writeable", lua::wrap<l_is_writeable>},
|
||||||
|
{"mount", lua::wrap<l_mount>},
|
||||||
|
{"unmount", lua::wrap<l_unmount>},
|
||||||
|
{"create_zip", lua::wrap<l_create_zip>},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -37,6 +37,12 @@ namespace util {
|
|||||||
return static_cast<int>(seed);
|
return static_cast<int>(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rand(unsigned char* dst, size_t n) {
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
dst[i] = rand();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int32_t rand32() {
|
int32_t rand32() {
|
||||||
return (rand() << 16) | rand();
|
return (rand() << 16) | rand();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user