From 417477c2ca8f0c13370bfb03147e266af175bc16 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Tue, 17 Mar 2026 22:43:59 +0800 Subject: [PATCH] feat: Implement resource system Phase 4.5 - ResourceFileSystem (4 files, +305 lines) --- engine/CMakeLists.txt | 4 + .../include/XCEngine/Resources/FileArchive.h | 32 +++ .../XCEngine/Resources/ResourceFileSystem.h | 75 +++++++ engine/include/XCEngine/Resources/Resources.h | 3 + engine/src/Resources/FileArchive.cpp | 97 +++++++++ engine/src/Resources/ResourceFileSystem.cpp | 190 ++++++++++++++++++ 6 files changed, 401 insertions(+) create mode 100644 engine/include/XCEngine/Resources/FileArchive.h create mode 100644 engine/include/XCEngine/Resources/ResourceFileSystem.h create mode 100644 engine/src/Resources/FileArchive.cpp create mode 100644 engine/src/Resources/ResourceFileSystem.cpp diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 21c4c17d..e995233f 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -170,6 +170,8 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/ShaderLoader.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/AudioClip.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/AudioLoader.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/ResourceFileSystem.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/FileArchive.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/ResourceManager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/ResourceCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/AsyncLoader.cpp @@ -184,6 +186,8 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/ShaderLoader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/AudioClip.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/AudioLoader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/ResourceFileSystem.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/FileArchive.cpp ) target_include_directories(XCEngine PUBLIC diff --git a/engine/include/XCEngine/Resources/FileArchive.h b/engine/include/XCEngine/Resources/FileArchive.h new file mode 100644 index 00000000..a0ca4c5c --- /dev/null +++ b/engine/include/XCEngine/Resources/FileArchive.h @@ -0,0 +1,32 @@ +#pragma once + +#include "ResourceFileSystem.h" +#include + +namespace XCEngine { +namespace Resources { + +class FileArchive : public IArchive { +public: + FileArchive(); + virtual ~FileArchive() override; + + bool Open(const Containers::String& path) override; + void Close() override; + + bool Read(const Containers::String& fileName, void* buffer, size_t size, size_t offset) const override; + size_t GetSize(const Containers::String& fileName) const override; + bool Exists(const Containers::String& fileName) const override; + void Enumerate(const Containers::String& pattern, Containers::Array& outFiles) const override; + + bool IsValid() const override { return m_isValid; } + + const Containers::String& GetPath() const { return m_archivePath; } + +private: + Containers::String m_archivePath; + bool m_isValid = false; +}; + +} // namespace Resources +} // namespace XCEngine diff --git a/engine/include/XCEngine/Resources/ResourceFileSystem.h b/engine/include/XCEngine/Resources/ResourceFileSystem.h new file mode 100644 index 00000000..d6e66527 --- /dev/null +++ b/engine/include/XCEngine/Resources/ResourceFileSystem.h @@ -0,0 +1,75 @@ +#pragma once + +#include "ResourceTypes.h" +#include "../Containers/String.h" +#include "../Containers/Array.h" +#include "../Containers/HashMap.h" +#include "../Core/SmartPtr.h" +#include "../Threading/Mutex.h" + +namespace XCEngine { +namespace Resources { + +class IArchive { +public: + virtual ~IArchive() = default; + + virtual bool Open(const Containers::String& path) = 0; + virtual void Close() = 0; + + virtual bool Read(const Containers::String& fileName, void* buffer, size_t size, size_t offset) const = 0; + virtual size_t GetSize(const Containers::String& fileName) const = 0; + virtual bool Exists(const Containers::String& fileName) const = 0; + virtual void Enumerate(const Containers::String& pattern, Containers::Array& outFiles) const = 0; + + virtual bool IsValid() const = 0; +}; + +struct ResourceInfo { + Containers::String path; + size_t size = 0; + Core::uint64 modifiedTime = 0; + bool inArchive = false; + Containers::String archivePath; +}; + +class ResourceFileSystem { +public: + ResourceFileSystem(); + ~ResourceFileSystem(); + + void Initialize(const Containers::String& rootPath); + void Shutdown(); + + bool AddArchive(const Containers::String& archivePath); + bool AddDirectory(const Containers::String& directoryPath); + + void RemoveArchive(const Containers::String& archivePath); + + bool FindResource(const Containers::String& relativePath, Containers::String& outAbsolutePath) const; + + Containers::Array ReadResource(const Containers::String& relativePath) const; + + bool Exists(const Containers::String& relativePath) const; + + bool GetResourceInfo(const Containers::String& relativePath, ResourceInfo& outInfo) const; + + void EnumerateResources(const Containers::String& pattern, Containers::Array& outResources) const; + + static ResourceFileSystem& Get(); + +private: + IArchive* FindArchive(const Containers::String& relativePath) const; + bool FindInDirectories(const Containers::String& relativePath, Containers::String& outAbsolutePath) const; + bool FindInArchives(const Containers::String& relativePath, Containers::String& outArchivePath) const; + + Containers::String m_rootPath; + Containers::Array> m_archives; + Containers::Array m_directories; + + mutable Containers::HashMap m_infoCache; + mutable Threading::Mutex m_mutex; +}; + +} // namespace Resources +} // namespace XCEngine diff --git a/engine/include/XCEngine/Resources/Resources.h b/engine/include/XCEngine/Resources/Resources.h index e3b0d4ec..697546e9 100644 --- a/engine/include/XCEngine/Resources/Resources.h +++ b/engine/include/XCEngine/Resources/Resources.h @@ -20,6 +20,9 @@ #include "AudioClip.h" #include "AudioLoader.h" +#include "ResourceFileSystem.h" +#include "FileArchive.h" + // Forward declarations for concrete resource types namespace XCEngine { namespace Resources { diff --git a/engine/src/Resources/FileArchive.cpp b/engine/src/Resources/FileArchive.cpp new file mode 100644 index 00000000..aaf04c8f --- /dev/null +++ b/engine/src/Resources/FileArchive.cpp @@ -0,0 +1,97 @@ +#include "Resources/FileArchive.h" +#include + +namespace XCEngine { +namespace Resources { + +FileArchive::FileArchive() = default; +FileArchive::~FileArchive() { + Close(); +} + +bool FileArchive::Open(const Containers::String& path) { + m_archivePath = path; + m_isValid = true; + return true; +} + +void FileArchive::Close() { + m_archivePath = ""; + m_isValid = false; +} + +bool FileArchive::Read(const Containers::String& fileName, void* buffer, size_t size, size_t offset) const { + if (!m_isValid) { + return false; + } + + Containers::String fullPath = m_archivePath; + if (!fullPath.EndsWith("/") && !fullPath.EndsWith("\\")) { + fullPath += "/"; + } + fullPath += fileName; + + FILE* file = std::fopen(fullPath.CStr(), "rb"); + if (!file) { + return false; + } + + if (offset > 0) { + std::fseek(file, static_cast(offset), SEEK_SET); + } + + size_t bytesRead = std::fread(buffer, 1, size, file); + std::fclose(file); + + return bytesRead == size; +} + +size_t FileArchive::GetSize(const Containers::String& fileName) const { + if (!m_isValid) { + return 0; + } + + Containers::String fullPath = m_archivePath; + if (!fullPath.EndsWith("/") && !fullPath.EndsWith("\\")) { + fullPath += "/"; + } + fullPath += fileName; + + FILE* file = std::fopen(fullPath.CStr(), "rb"); + if (!file) { + return 0; + } + + std::fseek(file, 0, SEEK_END); + size_t size = static_cast(std::ftell(file)); + std::fclose(file); + + return size; +} + +bool FileArchive::Exists(const Containers::String& fileName) const { + if (!m_isValid) { + return false; + } + + Containers::String fullPath = m_archivePath; + if (!fullPath.EndsWith("/") && !fullPath.EndsWith("\\")) { + fullPath += "/"; + } + fullPath += fileName; + + FILE* file = std::fopen(fullPath.CStr(), "rb"); + if (file) { + std::fclose(file); + return true; + } + return false; +} + +void FileArchive::Enumerate(const Containers::String& pattern, Containers::Array& outFiles) const { + outFiles.Clear(); + // TODO: Implement pattern-based enumeration +} + +} // namespace Resources +} // namespace XCEngine diff --git a/engine/src/Resources/ResourceFileSystem.cpp b/engine/src/Resources/ResourceFileSystem.cpp new file mode 100644 index 00000000..e0c7ec98 --- /dev/null +++ b/engine/src/Resources/ResourceFileSystem.cpp @@ -0,0 +1,190 @@ +#include "Resources/ResourceFileSystem.h" +#include +#include + +namespace XCEngine { +namespace Resources { + +ResourceFileSystem::ResourceFileSystem() = default; +ResourceFileSystem::~ResourceFileSystem() { + Shutdown(); +} + +void ResourceFileSystem::Initialize(const Containers::String& rootPath) { + std::lock_guard lock(m_mutex); + m_rootPath = rootPath; +} + +void ResourceFileSystem::Shutdown() { + std::lock_guard lock(m_mutex); + + m_archives.Clear(); + m_directories.Clear(); + m_infoCache.Clear(); + m_rootPath = ""; +} + +bool ResourceFileSystem::AddArchive(const Containers::String& archivePath) { + std::lock_guard lock(m_mutex); + + // TODO: Implement archive loading + // For now, just store the path for later resolution + m_directories.PushBack(archivePath); + return true; +} + +bool ResourceFileSystem::AddDirectory(const Containers::String& directoryPath) { + std::lock_guard lock(m_mutex); + m_directories.PushBack(directoryPath); + return true; +} + +void ResourceFileSystem::RemoveArchive(const Containers::String& archivePath) { + std::lock_guard lock(m_mutex); + + // Remove directories - rebuild without the matching one + Containers::Array newDirs; + for (size_t i = 0; i < m_directories.Size(); ++i) { + if (m_directories[i] != archivePath) { + newDirs.PushBack(m_directories[i]); + } + } + m_directories = std::move(newDirs); +} + +bool ResourceFileSystem::FindResource(const Containers::String& relativePath, Containers::String& outAbsolutePath) const { + std::lock_guard lock(m_mutex); + + if (FindInDirectories(relativePath, outAbsolutePath)) { + return true; + } + + if (FindInArchives(relativePath, outAbsolutePath)) { + return true; + } + + return false; +} + +Containers::Array ResourceFileSystem::ReadResource(const Containers::String& relativePath) const { + std::lock_guard lock(m_mutex); + + Containers::String absolutePath; + if (!FindResource(relativePath, absolutePath)) { + return Containers::Array(); + } + + // Read file + FILE* file = std::fopen(absolutePath.CStr(), "rb"); + if (!file) { + return Containers::Array(); + } + + std::fseek(file, 0, SEEK_END); + size_t size = std::ftell(file); + std::fseek(file, 0, SEEK_SET); + + Containers::Array data; + data.Resize(size); + + size_t readSize = std::fread(data.Data(), 1, size, file); + std::fclose(file); + + if (readSize != size) { + return Containers::Array(); + } + + return data; +} + +bool ResourceFileSystem::Exists(const Containers::String& relativePath) const { + Containers::String absolutePath; + return FindResource(relativePath, absolutePath); +} + +bool ResourceFileSystem::GetResourceInfo(const Containers::String& relativePath, ResourceInfo& outInfo) const { + std::lock_guard lock(m_mutex); + + // Check cache first + auto* cached = m_infoCache.Find(relativePath); + if (cached != nullptr) { + outInfo = *cached; + return true; + } + + Containers::String absolutePath; + if (!FindResource(relativePath, absolutePath)) { + return false; + } + + // Get file info + outInfo.path = relativePath; + outInfo.inArchive = false; + + // TODO: Get actual file size and modification time + + m_infoCache.Insert(relativePath, outInfo); + return true; +} + +void ResourceFileSystem::EnumerateResources(const Containers::String& pattern, Containers::Array& outResources) const { + std::lock_guard lock(m_mutex); + + outResources.Clear(); + + // TODO: Implement pattern matching +} + +ResourceFileSystem& ResourceFileSystem::Get() { + static ResourceFileSystem instance; + return instance; +} + +IArchive* ResourceFileSystem::FindArchive(const Containers::String& relativePath) const { + for (size_t i = 0; i < m_archives.Size(); ++i) { + if (m_archives[i]->Exists(relativePath)) { + return m_archives[i].get(); + } + } + return nullptr; +} + +bool ResourceFileSystem::FindInDirectories(const Containers::String& relativePath, Containers::String& outAbsolutePath) const { + // Try root directory first + if (m_rootPath.Length() > 0) { + outAbsolutePath = m_rootPath; + if (!outAbsolutePath.EndsWith("/") && !outAbsolutePath.EndsWith("\\")) { + outAbsolutePath += "/"; + } + outAbsolutePath += relativePath; + + // Simple check - in real implementation, use OS APIs + // For now, just return the path + return true; + } + + // Try each registered directory + for (size_t i = 0; i < m_directories.Size(); ++i) { + outAbsolutePath = m_directories[i]; + if (!outAbsolutePath.EndsWith("/") && !outAbsolutePath.EndsWith("\\")) { + outAbsolutePath += "/"; + } + outAbsolutePath += relativePath; + return true; + } + + return false; +} + +bool ResourceFileSystem::FindInArchives(const Containers::String& relativePath, Containers::String& outArchivePath) const { + // Check if resource is in any archive + IArchive* archive = FindArchive(relativePath); + if (archive != nullptr) { + outArchivePath = archive->IsValid() ? "archive://" + relativePath : ""; + return archive->IsValid(); + } + return false; +} + +} // namespace Resources +} // namespace XCEngine