feat: Implement resource system Phase 4.5 - ResourceFileSystem (4 files, +305 lines)

This commit is contained in:
2026-03-17 22:43:59 +08:00
parent 967c64faf8
commit 417477c2ca
6 changed files with 401 additions and 0 deletions

View File

@@ -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

View File

@@ -0,0 +1,32 @@
#pragma once
#include "ResourceFileSystem.h"
#include <cstdio>
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<Containers::String>& 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

View File

@@ -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<Containers::String>& 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<Core::uint8> 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<ResourceInfo>& 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<Core::UniqueRef<IArchive>> m_archives;
Containers::Array<Containers::String> m_directories;
mutable Containers::HashMap<Containers::String, ResourceInfo> m_infoCache;
mutable Threading::Mutex m_mutex;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -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 {

View File

@@ -0,0 +1,97 @@
#include "Resources/FileArchive.h"
#include <cstdio>
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<long>(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<size_t>(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<Containers::String>& outFiles) const {
outFiles.Clear();
// TODO: Implement pattern-based enumeration
}
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,190 @@
#include "Resources/ResourceFileSystem.h"
#include <algorithm>
#include <cstdio>
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<Containers::String> 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<Core::uint8> ResourceFileSystem::ReadResource(const Containers::String& relativePath) const {
std::lock_guard lock(m_mutex);
Containers::String absolutePath;
if (!FindResource(relativePath, absolutePath)) {
return Containers::Array<Core::uint8>();
}
// Read file
FILE* file = std::fopen(absolutePath.CStr(), "rb");
if (!file) {
return Containers::Array<Core::uint8>();
}
std::fseek(file, 0, SEEK_END);
size_t size = std::ftell(file);
std::fseek(file, 0, SEEK_SET);
Containers::Array<Core::uint8> data;
data.Resize(size);
size_t readSize = std::fread(data.Data(), 1, size, file);
std::fclose(file);
if (readSize != size) {
return Containers::Array<Core::uint8>();
}
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<ResourceInfo>& 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