diff --git a/editor/Exporter.cpp b/editor/Exporter.cpp index 68fcea1f8..5dd155715 100644 --- a/editor/Exporter.cpp +++ b/editor/Exporter.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -32,6 +33,15 @@ namespace { constexpr float BUILD_PROGRESS_START = 0.6f; constexpr float BUILD_PROGRESS_END = 0.95f; constexpr float MSBUILD_COMPILE_END = 0.9f; + constexpr char RESOURCE_PACK_MAGIC[] = {'D', 'X', 'P', 'K', '1'}; + + struct ResourcePackBuildEntry { + std::string path; + std::vector data; + uint64_t offset = 0; + uint8_t key = 0; + uint32_t shift = 0; + }; // Parses make-style "[ 47%]" and ninja-style "[123/456]" build-line prefixes // into a 0..1 fraction so the compile step can drive the progress bar. @@ -59,6 +69,51 @@ namespace { return false; } } + + void writeU8(std::ofstream& out, uint8_t value) { + out.put(static_cast(value)); + } + + void writeU16(std::ofstream& out, uint16_t value) { + out.put(static_cast(value & 0xff)); + out.put(static_cast((value >> 8) & 0xff)); + } + + void writeU32(std::ofstream& out, uint32_t value) { + for (int i = 0; i < 4; i++) { + out.put(static_cast((value >> (i * 8)) & 0xff)); + } + } + + void writeU64(std::ofstream& out, uint64_t value) { + for (int i = 0; i < 8; i++) { + out.put(static_cast((value >> (i * 8)) & 0xff)); + } + } + + void shiftRight(std::vector& data, uint32_t shift) { + if (data.empty()) return; + shift %= static_cast(data.size()); + if (shift == 0) return; + std::rotate(data.begin(), data.end() - shift, data.end()); + } + + void applyXor(std::vector& data, uint8_t key) { + for (unsigned char& byte : data) { + byte ^= key; + } + } + + bool readWholeFile(const fs::path& path, std::vector& data) { + std::ifstream in(path, std::ios::binary); + if (!in) return false; + in.seekg(0, std::ios::end); + std::streamoff size = in.tellg(); + if (size < 0) return false; + in.seekg(0, std::ios::beg); + data.resize(static_cast(size)); + return data.empty() || static_cast(in.read(reinterpret_cast(data.data()), size)); + } } editor::Exporter::Exporter() { @@ -224,6 +279,10 @@ void editor::Exporter::runExport() { if (!copyEngine()) return; if (isCancelled()) { setError("Export cancelled"); return; } if (!buildAndSaveShaders()) return; + if (config.mode == ExportMode::SourceCode && config.packNativeResources) { + if (isCancelled()) { setError("Export cancelled"); return; } + if (!collectSourceResourcePack()) return; + } if (buildMode) { if (isCancelled()) { setError("Export cancelled"); return; } @@ -583,6 +642,7 @@ bool editor::Exporter::collectDesktopArtifacts() { #endif std::error_code ec; + const fs::path projectRoot = getExportProjectRoot(); fs::path exePath = buildDir / exeName; if (!fs::exists(exePath, ec)) { // Multi-config generators (Visual Studio) place binaries in a per-config subdir. @@ -612,19 +672,41 @@ bool editor::Exporter::collectDesktopArtifacts() { return false; } - // The desktop runtime resolves "assets" and "lua" relative to the working - // directory, so ship them next to the executable. - const fs::path projectRoot = getExportProjectRoot(); - for (const char* dir : {"assets", "lua"}) { - fs::path src = projectRoot / dir; - if (!fs::exists(src, ec)) continue; - copyTree(src, config.destinationDir / dir, ec); + bool packCreated = false; + if (config.packNativeResources && !writeNativeResourcePack(config.destinationDir / "game.pak", packCreated)) { + return false; + } + if (!config.packNativeResources) { + ec.clear(); + fs::remove(config.destinationDir / "game.pak", ec); if (ec) { - setError(std::string("Failed to copy ") + dir + ": " + ec.message()); + setError("Failed to remove stale resource pack: " + ec.message()); return false; } } + if (config.packNativeResources && packCreated) { + for (const char* dir : {"assets", "lua"}) { + ec.clear(); + fs::remove_all(config.destinationDir / dir, ec); + if (ec) { + setError(std::string("Failed to remove unpacked ") + dir + ": " + ec.message()); + return false; + } + } + } else { + // Default and fallback behavior: ship resource folders next to the executable. + for (const char* dir : {"assets", "lua"}) { + fs::path src = projectRoot / dir; + if (!fs::exists(src, ec)) continue; + copyTree(src, config.destinationDir / dir, ec); + if (ec) { + setError(std::string("Failed to copy ") + dir + ": " + ec.message()); + return false; + } + } + } + #if defined(__linux__) // Linux executables cannot embed icons, and Wayland windows only get one // through an installed .desktop entry matched by app_id. When the project @@ -689,6 +771,146 @@ bool editor::Exporter::collectDesktopArtifacts() { return true; } +bool editor::Exporter::writeNativeResourcePack(const fs::path& outputPath, bool& created) { + setProgressRaw("Packing resources...", 0.95f); + + created = false; + std::vector entries; + const fs::path projectRoot = getExportProjectRoot(); + + std::error_code ec; + for (const char* rootName : {"assets", "lua"}) { + const fs::path root = projectRoot / rootName; + if (!fs::exists(root, ec)) continue; + + for (const auto& file : fs::recursive_directory_iterator(root, fs::directory_options::skip_permission_denied, ec)) { + if (!file.is_regular_file()) continue; + + fs::path relPath = fs::relative(file.path(), projectRoot, ec); + if (ec || relPath.empty()) continue; + + ResourcePackBuildEntry entry; + entry.path = relPath.generic_string(); + if (entry.path == "assets/game.pak") continue; + + if (!readWholeFile(file.path(), entry.data)) { + setError("Failed to read resource for pack: " + file.path().string()); + return false; + } + + size_t hash = std::hash{}(entry.path) ^ (entry.data.size() << 1); + entry.key = static_cast((hash % 255) + 1); + entry.shift = entry.data.empty() ? 0 : static_cast(hash % entry.data.size()); + + shiftRight(entry.data, entry.shift); + applyXor(entry.data, entry.key); + + entries.push_back(std::move(entry)); + } + } + + if (entries.empty()) { + fs::remove(outputPath, ec); + return true; + } + + std::sort(entries.begin(), entries.end(), [](const auto& a, const auto& b) { + return a.path < b.path; + }); + + uint64_t offset = sizeof(RESOURCE_PACK_MAGIC) + 4; + for (const auto& entry : entries) { + if (entry.path.size() > UINT16_MAX) { + setError("Resource path is too long for pack: " + entry.path); + return false; + } + offset += 2 + entry.path.size() + 8 + 8 + 1 + 4; + } + for (auto& entry : entries) { + entry.offset = offset; + offset += entry.data.size(); + } + + fs::create_directories(outputPath.parent_path(), ec); + if (ec) { + setError("Failed to create resource pack directory: " + ec.message()); + return false; + } + + std::ofstream out(outputPath, std::ios::binary); + if (!out) { + setError("Failed to create resource pack: " + outputPath.string()); + return false; + } + + out.write(RESOURCE_PACK_MAGIC, sizeof(RESOURCE_PACK_MAGIC)); + writeU32(out, static_cast(entries.size())); + + for (const auto& entry : entries) { + writeU16(out, static_cast(entry.path.size())); + out.write(entry.path.data(), static_cast(entry.path.size())); + writeU64(out, entry.offset); + writeU64(out, static_cast(entry.data.size())); + writeU8(out, entry.key); + writeU32(out, entry.shift); + } + + for (const auto& entry : entries) { + if (!entry.data.empty()) { + out.write(reinterpret_cast(entry.data.data()), static_cast(entry.data.size())); + } + } + + if (!out) { + setError("Failed to write resource pack: " + outputPath.string()); + return false; + } + + created = true; + return true; +} + +bool editor::Exporter::collectSourceResourcePack() { + const fs::path projectRoot = getExportProjectRoot(); + const fs::path assetsDir = projectRoot / "assets"; + const fs::path luaDir = projectRoot / "lua"; + const fs::path packPath = assetsDir / "game.pak"; + + bool packCreated = false; + if (!writeNativeResourcePack(packPath, packCreated)) { + return false; + } + + std::error_code ec; + if (!packCreated) { + return true; + } + + for (const auto& entry : fs::directory_iterator(assetsDir, ec)) { + if (ec) { + setError("Failed to read exported assets directory: " + ec.message()); + return false; + } + if (entry.path().filename() == "game.pak") { + continue; + } + + fs::remove_all(entry.path(), ec); + if (ec) { + setError("Failed to remove unpacked asset after packing: " + ec.message()); + return false; + } + } + + fs::remove_all(luaDir, ec); + if (ec) { + setError("Failed to remove unpacked Lua directory after packing: " + ec.message()); + return false; + } + + return true; +} + bool editor::Exporter::collectWebArtifacts() { setProgressRaw("Copying artifacts...", 0.95f); diff --git a/editor/Exporter.h b/editor/Exporter.h index 878cec3ac..1dc7201a5 100644 --- a/editor/Exporter.h +++ b/editor/Exporter.h @@ -54,6 +54,9 @@ namespace doriax::editor { // Desktop/Web: value passed to the exported CMake GRAPHIC_BACKEND // setting (glcore, gles3, d3d11, metal, or vulkan). std::string graphicBackend; + // Native builds: pack assets/lua into game.pak instead of copying folders. + // Experimental and disabled by default. + bool packNativeResources = false; // Web: emsdk root override ("" = auto-detect via EMSDK env / PATH) std::string emsdkPath; }; @@ -124,6 +127,8 @@ namespace doriax::editor { bool runBuild(); bool collectDesktopArtifacts(); bool collectWebArtifacts(); + bool collectSourceResourcePack(); + bool writeNativeResourcePack(const fs::path& outputPath, bool& created); bool clearGenerated(); bool loadAndSaveAllScenes(); bool copyGenerated(); diff --git a/editor/Project.cpp b/editor/Project.cpp index 341f72489..43eaddc87 100644 --- a/editor/Project.cpp +++ b/editor/Project.cpp @@ -2287,6 +2287,14 @@ unsigned int editor::Project::getCMakeBuildJobs() const{ return cmakeBuildJobs; } +void editor::Project::setPackNativeResources(bool enabled){ + packNativeResources = enabled; +} + +bool editor::Project::shouldPackNativeResources() const{ + return packNativeResources; +} + uint32_t editor::Project::getStartSceneId() const{ return startSceneId; } @@ -3448,6 +3456,7 @@ void editor::Project::resetConfigs() { cmakeCxxCompiler = ""; cmakeGenerator = ""; cmakeBuildJobs = defaultCMakeBuildJobs; + packNativeResources = defaultPackNativeResources; selectedScene = NULL_PROJECT_SCENE; selectedSceneForProperties = NULL_PROJECT_SCENE; nextSceneId = 0; diff --git a/editor/Project.h b/editor/Project.h index cebcfbc4c..1b7b90104 100644 --- a/editor/Project.h +++ b/editor/Project.h @@ -179,6 +179,7 @@ namespace doriax::editor{ // Atomic: read by the play-startup thread while the settings dialog can // write it from the UI thread. std::atomic cmakeBuildJobs{0}; + bool packNativeResources; CommandHistory projectHistory; uint32_t startSceneId; @@ -335,6 +336,7 @@ namespace doriax::editor{ static constexpr const char* defaultAssetsDir = "."; static constexpr const char* defaultLuaDir = "."; static constexpr unsigned int defaultCMakeBuildJobs = 0; + static constexpr bool defaultPackNativeResources = false; Project(); @@ -413,6 +415,8 @@ namespace doriax::editor{ std::string getCMakeGenerator() const; void setCMakeBuildJobs(unsigned int jobs); unsigned int getCMakeBuildJobs() const; + void setPackNativeResources(bool enabled); + bool shouldPackNativeResources() const; uint32_t getStartSceneId() const; void setStartSceneId(uint32_t sceneId); diff --git a/editor/Stream.cpp b/editor/Stream.cpp index 2c148ca77..4317190f9 100644 --- a/editor/Stream.cpp +++ b/editor/Stream.cpp @@ -1565,6 +1565,9 @@ YAML::Node editor::Stream::encodeProject(Project* project) { if (project->getCMakeBuildJobs() != 0) { root["cmakeBuildJobs"] = project->getCMakeBuildJobs(); } + if (project->shouldPackNativeResources() != Project::defaultPackNativeResources) { + root["packNativeResources"] = project->shouldPackNativeResources(); + } if (project->getStartSceneId() != NULL_PROJECT_SCENE) { root["startSceneId"] = project->getStartSceneId(); @@ -1752,6 +1755,9 @@ void editor::Stream::decodeProject(Project* project, const YAML::Node& node) { const long long maxJobs = static_cast(Generator::MAX_SUPPORTED_PARALLEL_BUILD_JOBS); project->setCMakeBuildJobs(static_cast(std::clamp(jobs, 0LL, maxJobs))); } + if (node["packNativeResources"].IsDefined()) { + project->setPackNativeResources(node["packNativeResources"].as()); + } if (node["startSceneId"]) { project->setStartSceneId(node["startSceneId"].as()); diff --git a/editor/window/dialog/ExportWindow.cpp b/editor/window/dialog/ExportWindow.cpp index f6e867526..8ddf3679f 100644 --- a/editor/window/dialog/ExportWindow.cpp +++ b/editor/window/dialog/ExportWindow.cpp @@ -707,6 +707,7 @@ void ExportWindow::startConfiguredExport(bool overwriteTarget) { if (m_mode == ExportMode::SourceCode) { exportConfig.targetDir = m_targetDir; + exportConfig.packNativeResources = m_project->shouldPackNativeResources(); for (const auto& entry : m_backendEntries) { if (entry.selected) { exportConfig.selectedBackends.insert(entry.backend); @@ -732,6 +733,7 @@ void ExportWindow::startConfiguredExport(bool overwriteTarget) { exportConfig.cmakeCxxCompiler = m_project->getCMakeCxxCompiler(); exportConfig.cmakeGenerator = m_project->getCMakeGenerator(); exportConfig.buildJobs = m_project->getCMakeBuildJobs(); + exportConfig.packNativeResources = m_project->shouldPackNativeResources(); } else { exportConfig.emsdkPath = m_emsdkOverride; } diff --git a/editor/window/dialog/ProjectSettingsWindow.cpp b/editor/window/dialog/ProjectSettingsWindow.cpp index 1cfd13330..e467b6951 100644 --- a/editor/window/dialog/ProjectSettingsWindow.cpp +++ b/editor/window/dialog/ProjectSettingsWindow.cpp @@ -463,6 +463,7 @@ void ProjectSettingsWindow::open(Project* project) { m_cmakePickError.clear(); refreshCMakeStatus(); m_cmakeBuildJobs = static_cast(project->getCMakeBuildJobs()); + m_packNativeResources = project->shouldPackNativeResources(); m_cmakeBuildJobsTooltip = "Maximum number of concurrent build jobs used for C++ scripts. Set to 0 to automatically use " + std::to_string(Generator::getAutomaticParallelBuildJobs()) + " detected logical CPU threads. " + @@ -868,6 +869,12 @@ void ProjectSettingsWindow::drawBuildSettings() { } drawIntSetting("Parallel Jobs", "##CMakeBuildJobs", m_cmakeBuildJobs, (int)Project::defaultCMakeBuildJobs, 0, m_cmakeBuildJobsTooltip.c_str()); + + if (beginSettingsRow("Native Resource Pack", "Experimental. Packs exported assets and Lua files into game.pak for native targets. Applies to Desktop export and Android source export. Web uses its own Emscripten resource bundle.", + m_packNativeResources != Project::defaultPackNativeResources)) { + m_packNativeResources = Project::defaultPackNativeResources; + } + ImGui::Checkbox("Pack native resources (experimental)", &m_packNativeResources); }); } @@ -914,6 +921,7 @@ void ProjectSettingsWindow::applySettings() { AppSettings::setLastCMakeKit("", "", ""); } m_project->setCMakeBuildJobs(static_cast(m_cmakeBuildJobs)); + m_project->setPackNativeResources(m_packNativeResources); m_project->saveProjectFile(); } diff --git a/editor/window/dialog/ProjectSettingsWindow.h b/editor/window/dialog/ProjectSettingsWindow.h index 56e28692e..3c3fa8556 100644 --- a/editor/window/dialog/ProjectSettingsWindow.h +++ b/editor/window/dialog/ProjectSettingsWindow.h @@ -51,6 +51,7 @@ namespace doriax::editor { std::string m_cmakePickError; // why the last pick was rejected int m_cmakeBuildJobs = 0; std::string m_cmakeBuildJobsTooltip; + bool m_packNativeResources = false; void drawSettings(); void drawGeneralSettings(); diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 2c9c1394e..5d70e27fa 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -762,6 +762,7 @@ set(DORIAX_SRCS core/io/Data.cpp core/io/File.cpp core/io/FileData.cpp + core/io/ResourcePack.cpp core/manager/BundleManager.cpp core/manager/SceneManager.cpp core/io/UserSettings.cpp diff --git a/engine/core/io/Data.cpp b/engine/core/io/Data.cpp index 1f0612d99..2711eb44b 100644 --- a/engine/core/io/Data.cpp +++ b/engine/core/io/Data.cpp @@ -8,8 +8,10 @@ // SPDX-License-Identifier: MIT #include "Data.h" +#include "ResourcePack.h" #include +#include using namespace doriax; @@ -125,6 +127,18 @@ unsigned int Data::open(const char *aFilename) { dataPtr = 0; offset = 0; + std::vector packedData; + if (ResourcePack::read(aFilename, packedData)) { + dataLength = static_cast(packedData.size()); + dataPtr = dataLength > 0 ? new unsigned char[dataLength] : nullptr; + if (dataLength > 0 && dataPtr == NULL) + return FileErrors::OUT_OF_MEMORY; + if (dataLength > 0) + memcpy(dataPtr, packedData.data(), dataLength); + dataOwned = true; + return FileErrors::FILEDATA_OK; + } + File df; int res = df.open(aFilename); if (res != 0) diff --git a/engine/core/io/ResourcePack.cpp b/engine/core/io/ResourcePack.cpp new file mode 100644 index 000000000..285729cfd --- /dev/null +++ b/engine/core/io/ResourcePack.cpp @@ -0,0 +1,254 @@ +// (c) Eduardo Doria +// SPDX-License-Identifier: MIT + +#include "ResourcePack.h" + +#include "System.h" + +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +using namespace doriax; + +namespace { + + constexpr char PACK_MAGIC[] = {'D', 'X', 'P', 'K', '1'}; + + struct PackEntry { + uint64_t offset = 0; + uint64_t size = 0; + uint8_t key = 0; + uint32_t shift = 0; + }; + + struct PackState { + bool triedLoad = false; + bool loaded = false; + std::string filename; + std::map entries; + }; + + PackState& state() { + static PackState packState; + return packState; + } + + std::string normalizeSeparators(std::string path) { + std::replace(path.begin(), path.end(), '\\', '/'); + return path; + } + + std::string simplifyPath(std::string path) { + path = normalizeSeparators(path); + while (!path.empty() && path.front() == '/') { + path.erase(path.begin()); + } + return path; + } + + bool startsWith(const std::string& value, const std::string& prefix) { + return value.rfind(prefix, 0) == 0; + } + + std::string logicalPackPath(std::string path) { + path = normalizeSeparators(path); + + if (startsWith(path, "asset://")) { + return "assets/" + simplifyPath(path.substr(8)); + } + if (startsWith(path, "lua://")) { + return "lua/" + simplifyPath(path.substr(6)); + } + if (startsWith(path, "shader://")) { + return "assets/shaders/" + simplifyPath(path.substr(9)); + } + if (startsWith(path, "data://")) { + return ""; + } + if (startsWith(path, "/") || (path.size() > 1 && path[1] == ':')) { + return ""; + } + + return "assets/" + simplifyPath(path); + } + + bool readExact(FILE* file, void* dst, size_t size) { + return std::fread(dst, 1, size, file) == size; + } + + bool readU8(FILE* file, uint8_t& value) { + return readExact(file, &value, 1); + } + + bool readU16(FILE* file, uint16_t& value) { + unsigned char bytes[2]; + if (!readExact(file, bytes, sizeof(bytes))) return false; + value = static_cast(bytes[0]) + | (static_cast(bytes[1]) << 8); + return true; + } + + bool readU32(FILE* file, uint32_t& value) { + unsigned char bytes[4]; + if (!readExact(file, bytes, sizeof(bytes))) return false; + value = static_cast(bytes[0]) + | (static_cast(bytes[1]) << 8) + | (static_cast(bytes[2]) << 16) + | (static_cast(bytes[3]) << 24); + return true; + } + + bool readU64(FILE* file, uint64_t& value) { + unsigned char bytes[8]; + if (!readExact(file, bytes, sizeof(bytes))) return false; + value = 0; + for (int i = 0; i < 8; i++) { + value |= static_cast(bytes[i]) << (i * 8); + } + return true; + } + + bool seekFile(FILE* file, uint64_t offset) { +#ifdef _WIN32 + return _fseeki64(file, static_cast<__int64>(offset), SEEK_SET) == 0; +#else + return fseeko(file, static_cast(offset), SEEK_SET) == 0; +#endif + } + + bool tryLoadPack(const std::string& filename) { + FILE* file = System::instance().platformFopen(filename.c_str(), "rb"); + if (!file) return false; + + char magic[sizeof(PACK_MAGIC)]; + uint32_t fileCount = 0; + bool ok = readExact(file, magic, sizeof(magic)) + && std::equal(std::begin(PACK_MAGIC), std::end(PACK_MAGIC), magic) + && readU32(file, fileCount); + + if (!ok) { + std::fclose(file); + return false; + } + + auto& pack = state(); + pack.entries.clear(); + pack.filename = filename; + + for (uint32_t i = 0; i < fileCount; i++) { + uint16_t pathLength = 0; + if (!readU16(file, pathLength) || pathLength == 0) { + ok = false; + break; + } + + std::string path(pathLength, '\0'); + if (!readExact(file, path.data(), pathLength)) { + ok = false; + break; + } + + PackEntry entry; + if (!readU64(file, entry.offset) + || !readU64(file, entry.size) + || !readU8(file, entry.key) + || !readU32(file, entry.shift)) { + ok = false; + break; + } + + pack.entries[simplifyPath(path)] = entry; + } + + std::fclose(file); + + if (!ok) { + pack.entries.clear(); + pack.filename.clear(); + return false; + } + + pack.loaded = true; + return true; + } + + void ensureLoaded() { + auto& pack = state(); + if (pack.triedLoad) return; + + pack.triedLoad = true; + pack.loaded = false; + + std::vector candidates = { + "game.pak", + "assets/game.pak" + }; + + std::string assetPath = simplifyPath(System::instance().getAssetPath()); + if (!assetPath.empty()) { + candidates.push_back(assetPath + "/game.pak"); + } + + for (const std::string& candidate : candidates) { + if (tryLoadPack(candidate)) return; + } + } + + void undoXorAndShift(std::vector& data, uint8_t key, uint32_t shift) { + if (data.empty()) return; + + for (unsigned char& byte : data) { + byte ^= key; + } + + uint32_t normalizedShift = shift % static_cast(data.size()); + if (normalizedShift == 0) return; + + std::rotate(data.begin(), data.begin() + normalizedShift, data.end()); + } + +} + +bool ResourcePack::read(const std::string& path, std::vector& outData) { + ensureLoaded(); + + auto& pack = state(); + if (!pack.loaded) return false; + + const std::string normalizedPath = logicalPackPath(path); + if (normalizedPath.empty()) return false; + + auto it = pack.entries.find(normalizedPath); + if (it == pack.entries.end()) return false; + + FILE* file = System::instance().platformFopen(pack.filename.c_str(), "rb"); + if (!file) return false; + + const PackEntry& entry = it->second; + if (!seekFile(file, entry.offset)) { + std::fclose(file); + return false; + } + + outData.resize(static_cast(entry.size)); + const bool ok = entry.size == 0 || readExact(file, outData.data(), static_cast(entry.size)); + std::fclose(file); + + if (!ok) { + outData.clear(); + return false; + } + + undoXorAndShift(outData, entry.key, entry.shift); + return true; +} + +void ResourcePack::reset() { + auto& pack = state(); + pack = PackState(); +} diff --git a/engine/core/io/ResourcePack.h b/engine/core/io/ResourcePack.h new file mode 100644 index 000000000..9a29959d2 --- /dev/null +++ b/engine/core/io/ResourcePack.h @@ -0,0 +1,22 @@ +// (c) Eduardo Doria +// SPDX-License-Identifier: MIT + +#ifndef RESOURCEPACK_H +#define RESOURCEPACK_H + +#include "Export.h" + +#include +#include + +namespace doriax { + + class DORIAX_API ResourcePack { + public: + static bool read(const std::string& path, std::vector& outData); + static void reset(); + }; + +} + +#endif diff --git a/engine/workspaces/xcode/Doriax.xcodeproj/project.pbxproj b/engine/workspaces/xcode/Doriax.xcodeproj/project.pbxproj index 9c0c46b7b..7039013fa 100644 --- a/engine/workspaces/xcode/Doriax.xcodeproj/project.pbxproj +++ b/engine/workspaces/xcode/Doriax.xcodeproj/project.pbxproj @@ -413,6 +413,7 @@ 713D8292259D307E00567F9F /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC2C25962A0E0075B97D /* Log.cpp */; }; 713D8293259D307F00567F9F /* Scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC2D25962A0E0075B97D /* Scene.cpp */; }; 713D8294259D307F00567F9F /* Data.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC3225962A0E0075B97D /* Data.cpp */; }; + 713D829A2E76C00100567F9F /* ResourcePack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC3A2E76C0010075B97D /* ResourcePack.cpp */; }; 713D8295259D307F00567F9F /* UserSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC3325962A0E0075B97D /* UserSettings.cpp */; }; 713D8296259D307F00567F9F /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC3525962A0E0075B97D /* File.cpp */; }; 713D8297259D307F00567F9F /* FileData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC3625962A0E0075B97D /* FileData.cpp */; }; @@ -518,6 +519,7 @@ 7162FD1225963AE00075B97D /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC2C25962A0E0075B97D /* Log.cpp */; }; 7162FD1325963AE00075B97D /* Scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC2D25962A0E0075B97D /* Scene.cpp */; }; 7162FD1625963AE00075B97D /* Data.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC3225962A0E0075B97D /* Data.cpp */; }; + 7162FD172E76C0010075B97D /* ResourcePack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC3A2E76C0010075B97D /* ResourcePack.cpp */; }; 7162FD1725963AE00075B97D /* UserSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC3325962A0E0075B97D /* UserSettings.cpp */; }; 7162FD1925963AE00075B97D /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC3525962A0E0075B97D /* File.cpp */; }; 7162FD1A25963AE00075B97D /* FileData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7162FC3625962A0E0075B97D /* FileData.cpp */; }; @@ -1969,6 +1971,8 @@ 7162FC3025962A0E0075B97D /* Data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Data.h; sourceTree = ""; }; 7162FC3125962A0E0075B97D /* File.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = File.h; sourceTree = ""; }; 7162FC3225962A0E0075B97D /* Data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Data.cpp; sourceTree = ""; }; + 7162FC392E76C0010075B97D /* ResourcePack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResourcePack.h; sourceTree = ""; }; + 7162FC3A2E76C0010075B97D /* ResourcePack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ResourcePack.cpp; sourceTree = ""; }; 7162FC3325962A0E0075B97D /* UserSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UserSettings.cpp; sourceTree = ""; }; 7162FC3425962A0E0075B97D /* UserSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserSettings.h; sourceTree = ""; }; 7162FC3525962A0E0075B97D /* File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = File.cpp; sourceTree = ""; }; @@ -3952,6 +3956,8 @@ 7162FC3125962A0E0075B97D /* File.h */, 7162FC3625962A0E0075B97D /* FileData.cpp */, 7162FC3725962A0E0075B97D /* FileData.h */, + 7162FC3A2E76C0010075B97D /* ResourcePack.cpp */, + 7162FC392E76C0010075B97D /* ResourcePack.h */, 7162FC3325962A0E0075B97D /* UserSettings.cpp */, 7162FC3425962A0E0075B97D /* UserSettings.h */, ); @@ -6899,6 +6905,7 @@ 7105A56D28B305B20092EA05 /* IOClassesLua.cpp in Sources */, 71651FF72AA804B4008D9BF5 /* Manifold2D.cpp in Sources */, 713D8294259D307F00567F9F /* Data.cpp in Sources */, + 713D829A2E76C00100567F9F /* ResourcePack.cpp in Sources */, 71E8248D2A9C23A600C8E6F2 /* Joint2D.cpp in Sources */, 71451BBF270CA16200712643 /* ActionSystem.cpp in Sources */, 71BE623D25B1DB9E006D6E02 /* LuaScript.cpp in Sources */, @@ -7107,6 +7114,7 @@ 71451BEB270CA2C900712643 /* Sprite.cpp in Sources */, 717AD98E29211976007D7DB5 /* Container.cpp in Sources */, 7162FD1625963AE00075B97D /* Data.cpp in Sources */, + 7162FD172E76C0010075B97D /* ResourcePack.cpp in Sources */, 7162FD1725963AE00075B97D /* UserSettings.cpp in Sources */, 71C33FB72A315878007A5822 /* Mesh.cpp in Sources */, 71CC839B26B8C4FD00EEBB93 /* FramebufferRender.cpp in Sources */,