refactor: reorganize Resources module into Core/Asset, Core/IO and Resources subdirectories

- Split core resource architecture into Core/Asset/ (IResource, ResourceManager, ResourceCache, etc.)
- Moved IO layer into Core/IO/ (IResourceLoader, ResourceFileSystem, etc.)
- Reorganized concrete resource types into Resources/{Texture,Mesh,Material,Shader,AudioClip}/
- Updated all include paths from relative to <XCEngine/...> format
- Fixed circular dependency in Material.h (removed unnecessary ResourceManager.h include)
- Fixed malformed include syntax in ResourceManager.h and AsyncLoader.h
- Fixed glad.h path issues in CMakeLists.txt
This commit is contained in:
2026-03-24 14:46:17 +08:00
parent b1829bcfc5
commit 50c0ffdb9e
47 changed files with 0 additions and 0 deletions

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,42 @@
#include "XCEngine/Resources/IResourceLoader.h"
#include <fstream>
namespace XCEngine {
namespace Resources {
Containers::Array<Core::uint8> IResourceLoader::ReadFileData(const Containers::String& path) {
Containers::Array<Core::uint8> data;
std::ifstream file(path.CStr(), std::ios::binary | std::ios::ate);
if (!file.is_open()) {
return data;
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
data.Resize(static_cast<size_t>(size));
if (!file.read(reinterpret_cast<char*>(data.Data()), size)) {
data.Clear();
}
return data;
}
Containers::String IResourceLoader::GetExtension(const Containers::String& path) {
Containers::String ext;
size_t dotPos = Containers::String::npos;
for (size_t i = path.Length(); i > 0; --i) {
if (path[i - 1] == '.') {
dotPos = i - 1;
break;
}
}
if (dotPos != Containers::String::npos) {
ext = path.Substring(dotPos + 1);
}
return ext;
}
} // 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

View File

@@ -0,0 +1,269 @@
#include "Resources/ResourcePackage.h"
#include <cstdio>
#include <algorithm>
namespace XCEngine {
namespace Resources {
static constexpr const char* PACKAGE_MAGIC = "XCRP";
static constexpr Core::uint16 PACKAGE_VERSION = 1;
ResourcePackageBuilder::ResourcePackageBuilder() = default;
ResourcePackageBuilder::~ResourcePackageBuilder() = default;
bool ResourcePackageBuilder::AddFile(const Containers::String& sourcePath, const Containers::String& relativePath) {
FILE* file = std::fopen(sourcePath.CStr(), "rb");
if (!file) {
m_error = "Cannot open file: " + sourcePath;
return false;
}
std::fseek(file, 0, SEEK_END);
long size = std::ftell(file);
std::fseek(file, 0, SEEK_SET);
std::fclose(file);
FileEntry entry;
entry.sourcePath = sourcePath;
entry.relativePath = relativePath;
entry.size = static_cast<size_t>(size);
entry.checksum = 0;
m_files.PushBack(entry);
return true;
}
bool ResourcePackageBuilder::AddDirectory(const Containers::String& sourceDir, const Containers::String& relativeBase) {
return true;
}
bool ResourcePackageBuilder::Build() {
if (m_outputPath.Empty()) {
m_error = "Output path not set";
return false;
}
if (m_files.Empty()) {
m_error = "No files to package";
return false;
}
FILE* output = std::fopen(m_outputPath.CStr(), "wb");
if (!output) {
m_error = "Cannot create output file: " + m_outputPath;
return false;
}
size_t headerSize = 4 + 2 + 4 + 4 + 8;
size_t dataOffset = headerSize;
for (auto& file : m_files) {
file.offset = dataOffset;
dataOffset += file.size;
}
bool success = WriteHeader(output, dataOffset) &&
WriteManifest(output) &&
WriteData(output);
std::fclose(output);
if (!success) {
std::remove(m_outputPath.CStr());
}
m_progress = 1.0f;
return success;
}
Core::uint64 ResourcePackageBuilder::CalculateChecksum(const void* data, size_t size) {
const Core::uint8* bytes = static_cast<const Core::uint8*>(data);
Core::uint64 hash = 14695981039346656037ULL;
for (size_t i = 0; i < size; ++i) {
hash ^= static_cast<Core::uint64>(bytes[i]);
hash *= 1099511628211ULL;
}
return hash;
}
bool ResourcePackageBuilder::WriteHeader(FILE* file, size_t dataOffset) {
std::fwrite(PACKAGE_MAGIC, 1, 4, file);
Core::uint16 version = PACKAGE_VERSION;
std::fwrite(&version, 2, 1, file);
Core::uint32 manifestSize = 0;
std::fwrite(&manifestSize, 4, 1, file);
Core::uint32 fileCount = static_cast<Core::uint32>(m_files.Size());
std::fwrite(&fileCount, 4, 1, file);
Core::uint64 offset = static_cast<Core::uint64>(dataOffset);
std::fwrite(&offset, 8, 1, file);
return true;
}
bool ResourcePackageBuilder::WriteManifest(FILE* file) {
return true;
}
bool ResourcePackageBuilder::WriteData(FILE* file) {
for (size_t i = 0; i < m_files.Size(); ++i) {
auto& fileEntry = m_files[i];
FILE* input = std::fopen(fileEntry.sourcePath.CStr(), "rb");
if (!input) {
m_error = "Cannot open file: " + fileEntry.sourcePath;
return false;
}
Containers::Array<Core::uint8> buffer(fileEntry.size);
size_t readSize = std::fread(buffer.Data(), 1, fileEntry.size, input);
std::fclose(input);
if (readSize != fileEntry.size) {
m_error = "Failed to read file: " + fileEntry.sourcePath;
return false;
}
fileEntry.checksum = CalculateChecksum(buffer.Data(), buffer.Size());
size_t written = std::fwrite(buffer.Data(), 1, buffer.Size(), file);
if (written != buffer.Size()) {
m_error = "Failed to write to package";
return false;
}
m_progress = static_cast<float>(i + 1) / static_cast<float>(m_files.Size());
}
return true;
}
ResourcePackage::ResourcePackage() = default;
ResourcePackage::~ResourcePackage() {
Close();
}
bool ResourcePackage::Open(const Containers::String& packagePath) {
FILE* file = std::fopen(packagePath.CStr(), "rb");
if (!file) {
return false;
}
m_packagePath = packagePath;
bool success = ReadHeader(file) && ReadManifest(file);
std::fclose(file);
if (!success) {
Close();
return false;
}
m_isValid = true;
return true;
}
void ResourcePackage::Close() {
m_isValid = false;
m_packagePath = "";
m_entries.Clear();
m_dataOffset = 0;
}
bool ResourcePackage::Exists(const Containers::String& relativePath) const {
for (const auto& entry : m_entries) {
if (entry.path == relativePath) {
return true;
}
}
return false;
}
Containers::Array<Core::uint8> ResourcePackage::Read(const Containers::String& relativePath) const {
Containers::Array<Core::uint8> data;
for (const auto& entry : m_entries) {
if (entry.path == relativePath) {
FILE* file = std::fopen(m_packagePath.CStr(), "rb");
if (!file) {
return data;
}
std::fseek(file, static_cast<long>(m_dataOffset + entry.offset), SEEK_SET);
data.Resize(entry.size);
size_t read = std::fread(data.Data(), 1, entry.size, file);
std::fclose(file);
if (read != entry.size) {
data.Clear();
}
break;
}
}
return data;
}
size_t ResourcePackage::GetSize(const Containers::String& relativePath) const {
for (const auto& entry : m_entries) {
if (entry.path == relativePath) {
return entry.size;
}
}
return 0;
}
void ResourcePackage::Enumerate(const Containers::String& pattern, Containers::Array<Containers::String>& outFiles) const {
outFiles.Clear();
for (const auto& entry : m_entries) {
outFiles.PushBack(entry.path);
}
}
bool ResourcePackage::ReadHeader(FILE* file) {
char magic[5] = {0};
if (std::fread(magic, 1, 4, file) != 4 || std::strncmp(magic, PACKAGE_MAGIC, 4) != 0) {
return false;
}
Core::uint16 version;
if (std::fread(&version, 2, 1, file) != 1 || version != PACKAGE_VERSION) {
return false;
}
Core::uint32 manifestSize;
if (std::fread(&manifestSize, 4, 1, file) != 1) {
return false;
}
Core::uint32 fileCount;
if (std::fread(&fileCount, 4, 1, file) != 1) {
return false;
}
Core::uint64 dataOffset;
if (std::fread(&dataOffset, 8, 1, file) != 1) {
return false;
}
m_info.path = m_packagePath;
m_info.version = version;
m_info.fileCount = fileCount;
m_info.totalSize = 0;
m_dataOffset = static_cast<size_t>(dataOffset);
return true;
}
bool ResourcePackage::ReadManifest(FILE* file) {
return true;
}
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,136 @@
#include <XCEngine/Resources/ResourcePath.h>
#include <algorithm>
namespace XCEngine {
namespace Resources {
static size_t FindLastOf(const Containers::String& str, char ch) {
for (size_t i = str.Length(); i > 0; --i) {
if (str[i - 1] == ch) {
return i - 1;
}
}
return Containers::String::npos;
}
ResourcePath::ResourcePath(const char* path)
: m_path(path) {}
ResourcePath::ResourcePath(const Containers::String& path)
: m_path(path) {}
Containers::String ResourcePath::GetExtension() const {
if (m_path.Empty()) {
return Containers::String();
}
size_t dotPos = FindLastOf(m_path, '.');
if (dotPos == Containers::String::npos) {
return Containers::String();
}
return m_path.Substring(dotPos);
}
Containers::String ResourcePath::GetStem() const {
if (m_path.Empty()) {
return Containers::String();
}
size_t slashPos = FindLastOf(m_path, '/');
size_t backslashPos = FindLastOf(m_path, '\\');
size_t nameStart = (slashPos != Containers::String::npos && backslashPos != Containers::String::npos)
? std::max(slashPos, backslashPos)
: (slashPos != Containers::String::npos ? slashPos : backslashPos);
if (nameStart == Containers::String::npos) {
nameStart = 0;
} else {
nameStart += 1;
}
size_t dotPos = FindLastOf(m_path, '.');
if (dotPos == Containers::String::npos || dotPos < nameStart) {
return m_path.Substring(nameStart);
}
return m_path.Substring(nameStart, dotPos - nameStart);
}
Containers::String ResourcePath::GetFullPath() const {
return m_path;
}
Containers::String ResourcePath::GetFileName() const {
if (m_path.Empty()) {
return Containers::String();
}
size_t slashPos = FindLastOf(m_path, '/');
size_t backslashPos = FindLastOf(m_path, '\\');
size_t nameStart = (slashPos != Containers::String::npos && backslashPos != Containers::String::npos)
? std::max(slashPos, backslashPos)
: (slashPos != Containers::String::npos ? slashPos : backslashPos);
if (nameStart == Containers::String::npos) {
return m_path;
}
return m_path.Substring(nameStart + 1);
}
Containers::String ResourcePath::GetDirectory() const {
if (m_path.Empty()) {
return Containers::String();
}
size_t slashPos = FindLastOf(m_path, '/');
size_t backslashPos = FindLastOf(m_path, '\\');
size_t dirEnd = (slashPos != Containers::String::npos && backslashPos != Containers::String::npos)
? std::max(slashPos, backslashPos)
: (slashPos != Containers::String::npos ? slashPos : backslashPos);
if (dirEnd == Containers::String::npos) {
return Containers::String();
}
return m_path.Substring(0, dirEnd);
}
Containers::String ResourcePath::GetRelativePath() const {
return m_path;
}
ResourceGUID ResourcePath::ToGUID() const {
return ResourceGUID::Generate(m_path);
}
bool ResourcePath::HasExtension(const char* ext) const {
if (!ext || m_path.Empty()) {
return false;
}
Containers::String pathExt = GetExtension();
if (pathExt.Empty()) {
return false;
}
return pathExt == ext;
}
bool ResourcePath::HasAnyExtension(const char* const* extensions, Core::uint32 count) const {
if (!extensions || count == 0 || m_path.Empty()) {
return false;
}
for (Core::uint32 i = 0; i < count; ++i) {
if (HasExtension(extensions[i])) {
return true;
}
}
return false;
}
} // namespace Resources
} // namespace XCEngine