feat: Implement resource system Phase 4.5 - ResourceFileSystem (4 files, +305 lines)
This commit is contained in:
@@ -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
|
||||
|
||||
32
engine/include/XCEngine/Resources/FileArchive.h
Normal file
32
engine/include/XCEngine/Resources/FileArchive.h
Normal 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
|
||||
75
engine/include/XCEngine/Resources/ResourceFileSystem.h
Normal file
75
engine/include/XCEngine/Resources/ResourceFileSystem.h
Normal 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
|
||||
@@ -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 {
|
||||
|
||||
97
engine/src/Resources/FileArchive.cpp
Normal file
97
engine/src/Resources/FileArchive.cpp
Normal 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
|
||||
190
engine/src/Resources/ResourceFileSystem.cpp
Normal file
190
engine/src/Resources/ResourceFileSystem.cpp
Normal 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
|
||||
Reference in New Issue
Block a user