cOMS/asset/AssetArchive.h

201 lines
6.5 KiB
C

/**
* Jingga
*
* @copyright Jingga
* @license License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_ASSET_ARCHIVE_H
#define TOS_ASSET_ARCHIVE_H
#include "../stdlib/Types.h"
#include "../utils/StringUtils.h"
#include "../utils/EndianUtils.h"
#include "../utils/Utils.h"
#include "../stdlib/simd/SIMD_I32.h"
#include "../memory/RingMemory.h"
#include "../memory/BufferMemory.h"
#include "AssetManagementSystem.h"
#if _WIN32
#include <windows.h>
#include "../platform/win32/FileUtils.cpp"
#elif __linux__
#include "../platform/win32/FileUtils.cpp"
#endif
struct AssetArchiveElement {
int32 type;
int32 start;
int32 length;
int32 dependency_start; // actual index for asset_dependencies
int32 dependency_count;
};
struct AssetArchiveHeader {
int32 version;
uint32 asset_count;
uint32 asset_dependency_count;
AssetArchiveElement* asset_element; // is not the owner of the data
int32* asset_dependencies; // is not the owner of the data
};
struct AssetArchive {
AssetArchiveHeader header;
byte* data; // owner of the data
FileHandler fd;
};
// Calculates how large the header memory has to be to hold all its information
int32 asset_archive_header_size(AssetArchive* archive, byte* data)
{
data += sizeof(archive->header.version);
int32 asset_count = SWAP_ENDIAN_LITTLE(*((uint32 *) data));
data += sizeof(archive->header.asset_count);
int32 asset_dependency_count = SWAP_ENDIAN_LITTLE(*((uint32 *) data));
data += sizeof(archive->header.asset_dependency_count);
return sizeof(archive->header.version)
+ sizeof(archive->header.asset_count)
+ sizeof(archive->header.asset_dependency_count)
+ asset_count * sizeof(AssetArchiveElement)
+ asset_dependency_count * sizeof(int32);
}
void asset_archive_header_load(AssetArchiveHeader* header, byte* data, int32 steps = 8)
{
header->version = SWAP_ENDIAN_LITTLE(*((int32 *) data));
data += sizeof(header->version);
header->asset_count = SWAP_ENDIAN_LITTLE(*((uint32 *) data));
data += sizeof(header->asset_count);
header->asset_dependency_count = SWAP_ENDIAN_LITTLE(*((uint32 *) data));
data += sizeof(header->asset_dependency_count);
memcpy(header->asset_element, data, header->asset_count * sizeof(AssetArchiveElement));
data += header->asset_count * sizeof(AssetArchiveElement);
SWAP_ENDIAN_LITTLE_SIMD(
(int32 *) header->asset_element,
(int32 *) header->asset_element,
header->asset_count * sizeof(AssetArchiveElement) / 4, // everything is 4 bytes -> super easy to swap
steps
);
header->asset_dependencies = (int32 *) ((byte *) header->asset_element + header->asset_count * sizeof(AssetArchiveElement));
memcpy(header->asset_dependencies, data, header->asset_dependency_count * sizeof(int32));
SWAP_ENDIAN_LITTLE_SIMD(
(int32 *) header->asset_dependencies,
(int32 *) header->asset_dependencies,
header->asset_count * header->asset_dependency_count, // everything is 4 bytes -> super easy to swap
steps
);
}
inline
AssetArchiveElement* asset_archive_element_find(const AssetArchive* archive, int32 id)
{
return &archive->header.asset_element[id];
}
void asset_archive_load(AssetArchive* archive, const char* path, BufferMemory* buf, RingMemory* ring, int32 steps = 8)
{
// Get file handle
archive->fd = file_read_async_handle(path);
if (!archive->fd) {
return;
}
FileBody file;
file.size = 64;
// Find header size
file.content = ring_get_memory(ring, file.size);
file_read(archive->fd, &file, 0, file.size);
file.size = asset_archive_header_size(archive, file.content);
// Reserve memory for the header
archive->data = buffer_get_memory(
buf,
file.size
- sizeof(archive->header.version)
- sizeof(archive->header.asset_count)
- sizeof(archive->header.asset_dependency_count),
4
);
// Read entire header
file.content = ring_get_memory(ring, file.size);
file_read(archive->fd, &file, 0, file.size);
asset_archive_header_load(&archive->header, file.content, steps);
}
// @performance This can probably be done much faster by handling the loading of dependencies faster
void asset_archive_asset_load(const AssetArchive* archive, int32 id, AssetManagementSystem* ams_array, RingMemory* ring)
{
AssetArchiveElement* element = &archive->header.asset_element[id];
AssetManagementSystem* ams = element->type > 0
? &ams_array[element->type]
: &ams_array[0];
uint64 hash = hash_djb2((const char *) &id);
// @performance I think we could optimize the ams_reserver_asset in a way so we don't have to lock it the entire time
pthread_mutex_lock(&ams->mutex);
// @bug this is not how this function works
if (hashmap_get_entry(&ams->hash_map, (const char *) &id, hash)) {
pthread_mutex_unlock(&ams->mutex);
}
if (element->type == 0) {
// @bug We can't just do this, this won't work. Check if we might want to change the asset management directly to hash indices or at least int values
Asset* asset = ams_reserve_asset(ams, (const char *) &id, ams_calculate_chunks(ams, element->length));
asset->self = (byte *) (asset + 1);
FileBody file = {};
file.content = asset->self;
// We are directly reading into the correct destination
file_read(archive->fd, &file, element->start, element->length);
} else {
// We are reading into temp memory since we have to perform transformations on the data
FileBodyAsync file = {};
file_read_async(archive->fd, &file, element->start, element->length, ring);
// This happens while the file system loads the data
Asset* asset = ams_reserve_asset(ams, (const char *) &id, ams_calculate_chunks(ams, element->length));
asset->self = (byte *) (asset + 1);
byte* data = ring_get_memory(ring, element->length, 64);
size_t data_size = 0;
// @todo create platform wrapper
GetOverlappedResult(archive->fd, &file.ov, NULL, true);
switch (element->type) {
case 1: {
} break;
default: {
}
}
memcpy(asset->self, data, data_size);
}
pthread_mutex_unlock(&ams->mutex);
// @performance maybe do in worker threads?
for (int32 i = 0; i < element->dependency_count; ++i) {
asset_archive_asset_load(archive, id, ams, ring);
}
}
#endif