mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-02-14 17:38:40 +00:00
started to implement more custom file formats, 100% nothing is working at the moment
This commit is contained in:
parent
653a14442f
commit
0c4bd343fc
|
|
@ -25,6 +25,11 @@ struct AssetManagementSystem {
|
||||||
// @question is this even necessary or could we integrate this directly into the system here?
|
// @question is this even necessary or could we integrate this directly into the system here?
|
||||||
HashMap hash_map;
|
HashMap hash_map;
|
||||||
|
|
||||||
|
uint64 ram_size;
|
||||||
|
uint64 vram_size;
|
||||||
|
uint64 asset_count;
|
||||||
|
bool has_changed;
|
||||||
|
|
||||||
// The indices of asset_memory and asset_data_memory are always linked
|
// The indices of asset_memory and asset_data_memory are always linked
|
||||||
|
|
||||||
// General asset memory
|
// General asset memory
|
||||||
|
|
@ -71,7 +76,7 @@ int32 ams_calculate_chunks(AssetManagementSystem* ams, int32 byte_size)
|
||||||
inline
|
inline
|
||||||
int64 ams_get_buffer_size(int count, int chunk_size)
|
int64 ams_get_buffer_size(int count, int chunk_size)
|
||||||
{
|
{
|
||||||
return hashmap_get_buffer_size(count, sizeof(HashEntryInt64)) // hash map
|
return hashmap_size(count, sizeof(HashEntryInt64)) // hash map
|
||||||
+ sizeof(Asset) * count + CEIL_DIV(count, 64) * sizeof(uint64) // asset_memory
|
+ sizeof(Asset) * count + CEIL_DIV(count, 64) * sizeof(uint64) // asset_memory
|
||||||
+ chunk_size * count + CEIL_DIV(count, 64) * sizeof(uint64); // asset_data_memory
|
+ chunk_size * count + CEIL_DIV(count, 64) * sizeof(uint64); // asset_data_memory
|
||||||
}
|
}
|
||||||
|
|
@ -103,14 +108,55 @@ void ams_create(AssetManagementSystem* ams, byte* buf, int chunk_size, int count
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
uint64 ams_get_vram_usage(AssetManagementSystem* ams)
|
void ams_update_stats(AssetManagementSystem* ams)
|
||||||
{
|
{
|
||||||
uint64 size = 0;
|
// @bug We should check the hash map or the memory status, we could still have old values in here
|
||||||
for (int32 i = 0; i < ams->asset_memory.count; ++i) {
|
|
||||||
size += ((Asset *) (ams->asset_memory.memory))[i].vram_size;
|
ams->vram_size = 0;
|
||||||
|
ams->ram_size = 0;
|
||||||
|
ams->asset_count = 0;
|
||||||
|
|
||||||
|
Asset* temp_asset = ams->first;
|
||||||
|
|
||||||
|
while (temp_asset) {
|
||||||
|
ams->vram_size += temp_asset->vram_size;
|
||||||
|
ams->ram_size += temp_asset->ram_size;
|
||||||
|
++ams->asset_count;
|
||||||
|
|
||||||
|
temp_asset = temp_asset->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return size;
|
ams->has_changed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
uint64 ams_get_asset_count(AssetManagementSystem* ams)
|
||||||
|
{
|
||||||
|
if (ams->has_changed) {
|
||||||
|
ams_update_stats(ams);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ams->asset_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
uint64 ams_get_vram_usage(AssetManagementSystem* ams)
|
||||||
|
{
|
||||||
|
if (ams->has_changed) {
|
||||||
|
ams_update_stats(ams);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ams->vram_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
uint64 ams_get_ram_usage(AssetManagementSystem* ams)
|
||||||
|
{
|
||||||
|
if (ams->has_changed) {
|
||||||
|
ams_update_stats(ams);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ams->ram_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ams_free_asset(AssetManagementSystem* ams, Asset* asset)
|
void ams_free_asset(AssetManagementSystem* ams, Asset* asset)
|
||||||
|
|
@ -124,6 +170,8 @@ void ams_free_asset(AssetManagementSystem* ams, Asset* asset)
|
||||||
chunk_free_element(&ams->asset_memory, asset->internal_id + i);
|
chunk_free_element(&ams->asset_memory, asset->internal_id + i);
|
||||||
chunk_free_element(&ams->asset_data_memory, asset->internal_id + i);
|
chunk_free_element(&ams->asset_data_memory, asset->internal_id + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ams->has_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
|
|
@ -208,6 +256,8 @@ Asset* ams_reserve_asset(AssetManagementSystem* ams, const char* name, uint32 el
|
||||||
ams->last = asset;
|
ams->last = asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ams->has_changed = true;
|
||||||
|
|
||||||
return asset;
|
return asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ uint32 crc_table[256] =
|
||||||
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
|
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32 calculate_crc32_checksum(uint8 *p, uint32 length)
|
uint32 crc32_checksum_calculate(uint8 *p, uint32 length)
|
||||||
{
|
{
|
||||||
uint32 crc = 0xFFFFFFFF;
|
uint32 crc = 0xFFFFFFFF;
|
||||||
while (length-- != 0) {
|
while (length-- != 0) {
|
||||||
|
|
@ -58,7 +58,7 @@ uint32 calculate_crc32_checksum(uint8 *p, uint32 length)
|
||||||
return (crc ^ 0xFFFFFFFF);
|
return (crc ^ 0xFFFFFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fill_crc32_table(uint32 *table){
|
void crc32_table_fill(uint32 *table){
|
||||||
uint8 index = 0,z;
|
uint8 index = 0,z;
|
||||||
do {
|
do {
|
||||||
table[index] = index;
|
table[index] = index;
|
||||||
|
|
|
||||||
229
compression/Huffman.h
Normal file
229
compression/Huffman.h
Normal file
|
|
@ -0,0 +1,229 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_COMPRESSION_HUFFMAN_H
|
||||||
|
#define TOS_COMPRESSION_HUFFMAN_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../stdlib/Types.h"
|
||||||
|
#include "../utils/BitUtils.h"
|
||||||
|
#include "../utils/EndianUtils.h"
|
||||||
|
#include "../utils/Utils.h"
|
||||||
|
|
||||||
|
struct HuffmanNode {
|
||||||
|
HuffmanNode* left;
|
||||||
|
HuffmanNode* right;
|
||||||
|
int32 frequency;
|
||||||
|
byte character;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Huffman {
|
||||||
|
HuffmanNode pool[512];
|
||||||
|
HuffmanNode priority_queue[511];
|
||||||
|
HuffmanNode** pq;
|
||||||
|
int32 node_count;
|
||||||
|
int32 pq_end;
|
||||||
|
|
||||||
|
// Contains the actual table data
|
||||||
|
char buffer[1024];
|
||||||
|
char* code[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
HuffmanNode* huffman_node_create(Huffman* hf, int32 frequency, byte character, HuffmanNode* left, HuffmanNode* right)
|
||||||
|
{
|
||||||
|
HuffmanNode* node = hf->pool + hf->node_count++;
|
||||||
|
if (frequency) {
|
||||||
|
node->character = character;
|
||||||
|
node->frequency = frequency;
|
||||||
|
} else {
|
||||||
|
node->left = left;
|
||||||
|
node->right = right;
|
||||||
|
node->frequency = left->frequency + right->frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void huffman_node_insert(Huffman* hf, HuffmanNode* node)
|
||||||
|
{
|
||||||
|
int32 child_id;
|
||||||
|
int32 parent_id = hf->pq_end++;
|
||||||
|
|
||||||
|
while ((child_id = parent_id / 2)) {
|
||||||
|
if (hf->pq[child_id]->frequency <= node->frequency) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
hf->pq[parent_id] = hf->pq[child_id];
|
||||||
|
parent_id = child_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
hf->pq[parent_id] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
HuffmanNode* huffman_node_remove(Huffman* hf)
|
||||||
|
{
|
||||||
|
int32 parent_id = 1;
|
||||||
|
int32 left_child_id;
|
||||||
|
HuffmanNode* min_node = hf->pq[parent_id];
|
||||||
|
|
||||||
|
if (hf->pq_end < 2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
--hf->pq_end;
|
||||||
|
|
||||||
|
while ((left_child_id = parent_id * 2) < hf->pq_end) {
|
||||||
|
// Branchless increment
|
||||||
|
left_child_id += (int32) (left_child_id + 1 < hf->pq_end
|
||||||
|
&& hf->pq[left_child_id + 1]->frequency < hf->pq[left_child_id]->frequency
|
||||||
|
);
|
||||||
|
|
||||||
|
hf->pq[parent_id] = hf->pq[left_child_id];
|
||||||
|
parent_id = left_child_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
hf->pq[parent_id] = hf->pq[hf->pq_end];
|
||||||
|
|
||||||
|
return min_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64 huffman_code_build(Huffman* hf, HuffmanNode* root, char* code, int32 length, char* code_buffer, int32* buffer_position)
|
||||||
|
{
|
||||||
|
if (root->character) {
|
||||||
|
code[length] = 0;
|
||||||
|
strcpy(&code_buffer[*buffer_position], code);
|
||||||
|
hf->code[root->character] = &code_buffer[*buffer_position];
|
||||||
|
*buffer_position += length + 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
code[length] = '0'; huffman_code_build(hf, root->left, code, length + 1, code_buffer, buffer_position);
|
||||||
|
code[length] = '1'; huffman_code_build(hf, root->right, code, length + 1, code_buffer, buffer_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void huffman_init(Huffman* hf, const byte* in)
|
||||||
|
{
|
||||||
|
int32 frequency[256] = {0};
|
||||||
|
char temp_code[16];
|
||||||
|
int32 buffer_position = 0;
|
||||||
|
|
||||||
|
// We artificially force the root element (usually the 0 element) to have the index 1.
|
||||||
|
hf->pq = (HuffmanNode **) (hf->priority_queue - 1);
|
||||||
|
|
||||||
|
while (*in) frequency[(byte) *in++]++;
|
||||||
|
|
||||||
|
for (int32 i = 0; i < 256; ++i) {
|
||||||
|
if (frequency[i]) {
|
||||||
|
huffman_node_insert(hf, huffman_node_create(hf, frequency[i], i, NULL, NULL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (hf->pq_end > 2) {
|
||||||
|
huffman_node_insert(hf, huffman_node_create(hf, 0, 0, huffman_node_remove(hf), huffman_node_remove(hf)));
|
||||||
|
}
|
||||||
|
|
||||||
|
huffman_code_build(hf, hf->pq[1], temp_code, 0, hf->buffer, &buffer_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void huffman_dump(const Huffman* hf, byte* out)
|
||||||
|
{
|
||||||
|
// dump the char -> code relations as relative indeces
|
||||||
|
for (int32 i = 0; i < ARRAY_COUNT(hf->code); ++i) {
|
||||||
|
if (hf->code[i]) {
|
||||||
|
*((int64 *) out) = SWAP_ENDIAN_LITTLE(hf->code[i] - hf->buffer);
|
||||||
|
} else {
|
||||||
|
*((int64 *) out) = SWAP_ENDIAN_LITTLE(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
out += sizeof(int64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// dump the table codes
|
||||||
|
memcpy(out, hf->buffer, sizeof(char) * ARRAY_COUNT(hf->buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
void huffman_load(Huffman* hf, const byte* in)
|
||||||
|
{
|
||||||
|
// load the char -> code relations and convert relative indeces to pointers
|
||||||
|
for (int32 i = 0; i < ARRAY_COUNT(hf->code); ++i) {
|
||||||
|
int64 value = SWAP_ENDIAN_LITTLE(*((int64 *) in));
|
||||||
|
in += sizeof(value);
|
||||||
|
|
||||||
|
if (value > -1) {
|
||||||
|
hf->code[i] = hf->buffer + value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the table codes
|
||||||
|
memcpy(hf->buffer, in, sizeof(char) * ARRAY_COUNT(hf->buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
int64 huffman_encode(Huffman* hf, const byte* in, byte* out)
|
||||||
|
{
|
||||||
|
uint64 bit_length = 0;
|
||||||
|
int32 pos_bit = 0;
|
||||||
|
|
||||||
|
while (*in) {
|
||||||
|
const char* code = hf->code[*in++];
|
||||||
|
|
||||||
|
while (*code) {
|
||||||
|
if (*code == '1') {
|
||||||
|
BIT_SET_L2R(*out, pos_bit, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
++code;
|
||||||
|
++bit_length;
|
||||||
|
++pos_bit;
|
||||||
|
|
||||||
|
if (pos_bit > 7) {
|
||||||
|
++out;
|
||||||
|
pos_bit = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bit_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64 huffman_decode(Huffman* hf, const byte* in, byte* out, uint64 bit_length)
|
||||||
|
{
|
||||||
|
HuffmanNode* current = hf->pq[1];
|
||||||
|
int32 pos_bit = 0;
|
||||||
|
int64 out_length = 0;
|
||||||
|
|
||||||
|
byte* start = out;
|
||||||
|
|
||||||
|
while (pos_bit < bit_length) {
|
||||||
|
if (BITS_GET_8_L2R(*in, pos_bit++, 1)) {
|
||||||
|
current = current->right;
|
||||||
|
} else {
|
||||||
|
current = current->left;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current->character) {
|
||||||
|
*out++ = current->character;
|
||||||
|
current = hf->pq[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos_bit > 7) {
|
||||||
|
++in;
|
||||||
|
pos_bit = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = '\0';
|
||||||
|
|
||||||
|
// -1 for the \0 character which is not part of the length
|
||||||
|
return out - start - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
|
|
||||||
uint32 encode_lzp(const byte* in, size_t length, byte* out)
|
uint32 lzp_encode(const byte* in, size_t length, byte* out)
|
||||||
{
|
{
|
||||||
byte buf[9];
|
byte buf[9];
|
||||||
byte table[1 << 16] = {0};
|
byte table[1 << 16] = {0};
|
||||||
|
|
@ -58,7 +58,7 @@ uint32 encode_lzp(const byte* in, size_t length, byte* out)
|
||||||
return out_pos;
|
return out_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 decode_lzp(const byte* in, size_t length, byte* out)
|
uint32 lzp_decode(const byte* in, size_t length, byte* out)
|
||||||
{
|
{
|
||||||
byte buf[8];
|
byte buf[8];
|
||||||
byte table[1 << 16] = {0};
|
byte table[1 << 16] = {0};
|
||||||
|
|
@ -126,7 +126,7 @@ int32 find_longest_match(char *window, int32 window_start, char *buffer, int32 b
|
||||||
return best_length;
|
return best_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 encode_lzp3(const byte* in, size_t length, byte* out) {
|
uint32 lzp3_encode(const byte* in, size_t length, byte* out) {
|
||||||
char window[4096] = {0};
|
char window[4096] = {0};
|
||||||
int32 window_start = 0;
|
int32 window_start = 0;
|
||||||
|
|
||||||
|
|
@ -157,7 +157,7 @@ uint32 encode_lzp3(const byte* in, size_t length, byte* out) {
|
||||||
return out_size;
|
return out_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 decode_lzp3(const byte* in, size_t length, byte* out) {
|
uint32 lzp3_decode(const byte* in, size_t length, byte* out) {
|
||||||
char window[4096] = {0};
|
char window[4096] = {0};
|
||||||
int32 window_start = 0;
|
int32 window_start = 0;
|
||||||
|
|
||||||
|
|
|
||||||
66
compression/RLE.h
Normal file
66
compression/RLE.h
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_COMPRESSION_RLE_H
|
||||||
|
#define TOS_COMPRESSION_RLE_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../stdlib/Types.h"
|
||||||
|
#include "../utils/StringUtils.h"
|
||||||
|
|
||||||
|
// max out length = length * 2 + 1
|
||||||
|
uint64 rle_encode(const char* in, size_t length, char* out)
|
||||||
|
{
|
||||||
|
uint64 count;
|
||||||
|
uint64 j = 0;
|
||||||
|
|
||||||
|
for (uint64 i = 0; i < length; i++) {
|
||||||
|
count = 1;
|
||||||
|
while (i + 1 < length && in[i] == in[i + 1]) {
|
||||||
|
++count;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
out[j++] = in[i];
|
||||||
|
|
||||||
|
j += int_to_str(count, &out[j], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
out[j] = '\0';
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 rle_decode(const char* in, size_t length, char* out)
|
||||||
|
{
|
||||||
|
uint64 j = 0;
|
||||||
|
|
||||||
|
for (int64 i = 0; i < length; i++) {
|
||||||
|
char current_char = in[i];
|
||||||
|
++i;
|
||||||
|
|
||||||
|
int32 count = 0;
|
||||||
|
while (i < length && in[i] >= '0' && in[i] <= '9') {
|
||||||
|
count = count * 10 + (in[i] - '0');
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
--i;
|
||||||
|
|
||||||
|
for (int32 k = 0; k < count; k++) {
|
||||||
|
out[j++] = current_char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out[j] = '\0';
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
185
font/Font.h
185
font/Font.h
|
|
@ -3,19 +3,8 @@
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../memory/BufferMemory.h"
|
#include "../memory/BufferMemory.h"
|
||||||
|
#include "../utils/EndianUtils.h"
|
||||||
// @todo Move this somewhere else, it doesn't belong here
|
#include "../stdlib/simd/SIMD_I32.h"
|
||||||
enum UIAlignH {
|
|
||||||
UI_ALIGN_H_LEFT,
|
|
||||||
UI_ALIGN_H_CENTER,
|
|
||||||
UI_ALIGN_H_RIGHT,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum UIAlignV {
|
|
||||||
UI_ALIGN_V_BOTTOM,
|
|
||||||
UI_ALIGN_V_CENTER,
|
|
||||||
UI_ALIGN_V_TOP,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GlyphMetrics {
|
struct GlyphMetrics {
|
||||||
f32 width; // Width of the glyph
|
f32 width; // Width of the glyph
|
||||||
|
|
@ -25,11 +14,9 @@ struct GlyphMetrics {
|
||||||
f32 advance_x; // Horizontal advance after drawing the glyph
|
f32 advance_x; // Horizontal advance after drawing the glyph
|
||||||
};
|
};
|
||||||
|
|
||||||
// @question Do we even need all this information? x2 and y2 follow from width and height, no?
|
|
||||||
struct GlyphTextureCoords {
|
struct GlyphTextureCoords {
|
||||||
f32 x1;
|
f32 x1;
|
||||||
f32 y1;
|
f32 y1;
|
||||||
|
|
||||||
f32 x2;
|
f32 x2;
|
||||||
f32 y2;
|
f32 y2;
|
||||||
};
|
};
|
||||||
|
|
@ -50,9 +37,9 @@ struct Font {
|
||||||
};
|
};
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void font_init(BufferMemory* buf, Font* font, int count)
|
void font_init(Font* font, byte* data, int count)
|
||||||
{
|
{
|
||||||
font->glyphs = (Glyph *) buffer_get_memory(buf, sizeof(Glyph) * count);
|
font->glyphs = (Glyph *) data;
|
||||||
font->glyph_count = count;
|
font->glyph_count = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,4 +55,168 @@ Glyph* font_glyph_find(Font* font, uint32 codepoint)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void font_from_file_txt(
|
||||||
|
RingMemory* ring,
|
||||||
|
const char* path,
|
||||||
|
Font* font
|
||||||
|
)
|
||||||
|
{
|
||||||
|
FileBody file;
|
||||||
|
file_read(path, &file, ring);
|
||||||
|
|
||||||
|
char* pos = (char *) file.content;
|
||||||
|
|
||||||
|
bool start = true;
|
||||||
|
char block_name[32];
|
||||||
|
|
||||||
|
int32 glyph_index = 0;
|
||||||
|
|
||||||
|
int32 image_width = 0;
|
||||||
|
int32 image_height = 0;
|
||||||
|
|
||||||
|
while (*pos != '\0') {
|
||||||
|
if (start) {
|
||||||
|
// Parsing general data
|
||||||
|
int32 i = 0;
|
||||||
|
while (*pos != '\0' && *pos != ' ' && *pos != '\n' && i < 31) {
|
||||||
|
block_name[i] = *pos;
|
||||||
|
++pos;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to value
|
||||||
|
while (*pos == ' ' || *pos == '\t') {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(block_name, "font_size") == 0) {
|
||||||
|
font->size = strtof(pos, &pos);
|
||||||
|
} else if (strcmp(block_name, "line_height") == 0) {
|
||||||
|
font->line_height = strtoul(pos, &pos, 10);
|
||||||
|
} else if (strcmp(block_name, "image_width") == 0) {
|
||||||
|
image_width = strtoul(pos, &pos, 10);
|
||||||
|
} else if (strcmp(block_name, "image_height") == 0) {
|
||||||
|
image_height = strtoul(pos, &pos, 10);
|
||||||
|
} else if (strcmp(block_name, "glyph_count") == 0) {
|
||||||
|
// glyph_count has to be the last general element
|
||||||
|
font->glyph_count = strtoul(pos, &pos, 10);
|
||||||
|
start = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to next line
|
||||||
|
while (*pos++ != '\n') {};
|
||||||
|
++pos;
|
||||||
|
} else {
|
||||||
|
// Parsing glyphs
|
||||||
|
// In the text file we don't have to define width and height of the character, we calculate that here
|
||||||
|
font->glyphs[glyph_index] = {
|
||||||
|
strtoul(pos, &pos, 10),
|
||||||
|
{0.0f, 0.0f, strtof(++pos, &pos), strtof(++pos, &pos), strtof(++pos, &pos)},
|
||||||
|
{strtof(++pos, &pos), strtof(++pos, &pos), strtof(++pos, &pos), strtof(++pos, &pos)}
|
||||||
|
};
|
||||||
|
|
||||||
|
font->glyphs[glyph_index].metrics.width = font->glyphs[glyph_index].coords.x2 - font->glyphs[glyph_index].coords.x1;
|
||||||
|
font->glyphs[glyph_index].metrics.height = font->glyphs[glyph_index].coords.y2 - font->glyphs[glyph_index].coords.y1;
|
||||||
|
|
||||||
|
++glyph_index;
|
||||||
|
|
||||||
|
// Go to next line
|
||||||
|
while (*pos != '\n' && *pos != '\0') { ++pos; };
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculates the required size for representing a font definition in memory
|
||||||
|
inline
|
||||||
|
uint64 font_size_from_file(const byte* data)
|
||||||
|
{
|
||||||
|
return SWAP_ENDIAN_LITTLE(*((uint32 *) data)) * sizeof(Glyph);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
uint64 font_size(const Font* font)
|
||||||
|
{
|
||||||
|
// We have to remove the size of the pointer which will not be stored
|
||||||
|
return sizeof(font) - sizeof(Glyph*)
|
||||||
|
+ font->glyph_count * sizeof(Glyph);
|
||||||
|
}
|
||||||
|
|
||||||
|
void font_from_file(
|
||||||
|
Font* font,
|
||||||
|
const byte* data,
|
||||||
|
int32 size = 8
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const byte* pos = data;
|
||||||
|
|
||||||
|
// Read count
|
||||||
|
font->glyph_count = SWAP_ENDIAN_LITTLE(*((uint32 *) pos));
|
||||||
|
pos += sizeof(font->glyph_count);
|
||||||
|
|
||||||
|
// Read font size
|
||||||
|
font->size = SWAP_ENDIAN_LITTLE(*((f32 *) pos));
|
||||||
|
pos += sizeof(font->size);
|
||||||
|
|
||||||
|
// Read line height
|
||||||
|
font->line_height = SWAP_ENDIAN_LITTLE(*((uint32 *) pos));
|
||||||
|
pos += sizeof(font->line_height);
|
||||||
|
|
||||||
|
memcpy(font->glyphs, pos, font->glyph_count * sizeof(Glyph));
|
||||||
|
|
||||||
|
SWAP_ENDIAN_LITTLE_SIMD(
|
||||||
|
(int32 *) font->glyphs,
|
||||||
|
(int32 *) font->glyphs,
|
||||||
|
font->glyph_count * sizeof(Glyph) / 4, // everything in here is 4 bytes -> super easy to swap
|
||||||
|
steps
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
int64 font_size_from_font(Font* font)
|
||||||
|
{
|
||||||
|
return font->glyph_count * sizeof(Glyph) + sizeof(Font);
|
||||||
|
}
|
||||||
|
|
||||||
|
void font_to_file(
|
||||||
|
RingMemory* ring,
|
||||||
|
const char* path,
|
||||||
|
const Font* font,
|
||||||
|
int32 steps = 8
|
||||||
|
)
|
||||||
|
{
|
||||||
|
FileBody file;
|
||||||
|
file.size = font->glyph_count * sizeof(Glyph) + sizeof(Font);
|
||||||
|
file.content = ring_get_memory(ring, file.size, 64);
|
||||||
|
|
||||||
|
byte* pos = file.content;
|
||||||
|
|
||||||
|
// Glyph count
|
||||||
|
*((uint32 *) pos) = font->glyph_count;
|
||||||
|
pos += sizeof(font->glyph_count);
|
||||||
|
|
||||||
|
// Font size
|
||||||
|
*((f32 *) pos) = font->size;
|
||||||
|
pos += sizeof(font->size);
|
||||||
|
|
||||||
|
// Line height
|
||||||
|
*((uint32 *) pos) = font->line_height;
|
||||||
|
pos += sizeof(font->line_height);
|
||||||
|
|
||||||
|
// The glyphs are naturally tightly packed -> we can just store the memory
|
||||||
|
memcpy(pos, font->glyphs, font->glyph_count * sizeof(Glyph));
|
||||||
|
pos += font->glyph_count * sizeof(Glyph);
|
||||||
|
|
||||||
|
file.size = pos - file.content;
|
||||||
|
|
||||||
|
SWAP_ENDIAN_LITTLE_SIMD(
|
||||||
|
(int32 *) file.content,
|
||||||
|
(int32 *) file.content,
|
||||||
|
file.size / 4, // everything in here is 4 bytes -> super easy to swap
|
||||||
|
steps
|
||||||
|
);
|
||||||
|
|
||||||
|
file_write(path, &file);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -16,6 +16,12 @@
|
||||||
#include "../math/matrix/MatrixFloat32.h"
|
#include "../math/matrix/MatrixFloat32.h"
|
||||||
#include "../font/Font.h"
|
#include "../font/Font.h"
|
||||||
#include "../object/Vertex.h"
|
#include "../object/Vertex.h"
|
||||||
|
#include "../ui/UITheme.h"
|
||||||
|
#include "../ui/UIElement.h"
|
||||||
|
#include "../ui/UIAlignment.h"
|
||||||
|
|
||||||
|
// @performance Create improved vertice generation for components (input + button, chat, ...) where we don't use as many
|
||||||
|
// degenerate triangled
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void vertex_degenerate_create(
|
void vertex_degenerate_create(
|
||||||
|
|
@ -370,7 +376,7 @@ void text_calculate_dimensions(
|
||||||
*height = y;
|
*height = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vertex_text_create(
|
f32 vertex_text_create(
|
||||||
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||||
f32 x, f32 y, f32 width, f32 height, int32 align_h, int32 align_v,
|
f32 x, f32 y, f32 width, f32 height, int32 align_h, int32 align_v,
|
||||||
const Font* __restrict font, const char* __restrict text, f32 size, uint32 color_index = 0
|
const Font* __restrict font, const char* __restrict text, f32 size, uint32 color_index = 0
|
||||||
|
|
@ -433,6 +439,189 @@ void vertex_text_create(
|
||||||
// @todo If width or height (usually just width) > 0 we use those values for automatic wrapping
|
// @todo If width or height (usually just width) > 0 we use those values for automatic wrapping
|
||||||
// This way we can ensure no overflow easily
|
// This way we can ensure no overflow easily
|
||||||
// @todo implement line alignment, currently only total alignment is considered
|
// @todo implement line alignment, currently only total alignment is considered
|
||||||
|
|
||||||
|
return offset_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 ui_text_create(
|
||||||
|
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||||
|
UITheme* theme, UIElement* element
|
||||||
|
) {
|
||||||
|
if (element->vertex_count > 0) {
|
||||||
|
memcpy(vertices + *index, element->vertices, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashEntryVoidP* entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, element->name, element->id);
|
||||||
|
UIAttributeGroup* group = (UIAttributeGroup *) entry->value;
|
||||||
|
|
||||||
|
UIAttribute* x;
|
||||||
|
UIAttribute* y;
|
||||||
|
|
||||||
|
UIAttribute* parent = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_PARENT);
|
||||||
|
if (parent) {
|
||||||
|
HashEntryVoidP* parent_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, parent->value_str);
|
||||||
|
UIAttributeGroup* parent_group = (UIAttributeGroup *) parent_entry->value;
|
||||||
|
|
||||||
|
x = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||||
|
y = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||||
|
|
||||||
|
// @question Do we have more values which can be inherited from the parent?
|
||||||
|
// We don't want to inherit implicit stuff like size, background etc. These things should be defined explicitly
|
||||||
|
} else {
|
||||||
|
x = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||||
|
y = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
UIAttribute* width = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH);
|
||||||
|
UIAttribute* height = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT);
|
||||||
|
UIAttribute* align_h = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_ALIGN_H);
|
||||||
|
UIAttribute* align_v = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_ALIGN_V);
|
||||||
|
UIAttribute* text = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_CONTENT);
|
||||||
|
UIAttribute* size = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_FONT_SIZE);
|
||||||
|
UIAttribute* color_index = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_FONT_COLOR);
|
||||||
|
|
||||||
|
int32 length = utf8_strlen(text->value_str);
|
||||||
|
float scale = size->value_float / theme->font.size;
|
||||||
|
|
||||||
|
// If we do a different alignment we need to pre-calculate the width and height
|
||||||
|
if (align_h != NULL || align_v != NULL) {
|
||||||
|
f32 tmp_width = width->value_int;
|
||||||
|
f32 tmp_height = height->value_int;
|
||||||
|
|
||||||
|
text_calculate_dimensions(&tmp_width, &tmp_height, &theme->font, text->value_str, scale, length);
|
||||||
|
|
||||||
|
if (align_h->value_int == UI_ALIGN_H_RIGHT) {
|
||||||
|
x -= width->value_int;
|
||||||
|
} else if (align_h->value_int == UI_ALIGN_H_CENTER) {
|
||||||
|
x -= width->value_int / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (align_v->value_int == UI_ALIGN_V_TOP) {
|
||||||
|
y -= height->value_int;
|
||||||
|
} else if (align_v->value_int == UI_ALIGN_V_CENTER) {
|
||||||
|
y -= height->value_int / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 start = *index;
|
||||||
|
f32 offset_x = x->value_int;
|
||||||
|
f32 offset_y = y->value_int;
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
int32 character = utf8_get_char_at(text->value_str, i);
|
||||||
|
|
||||||
|
if (character == '\n') {
|
||||||
|
offset_y += theme->font.line_height * scale;
|
||||||
|
offset_x = x->value_int;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Glyph* glyph = NULL;
|
||||||
|
for (int j = 0; j < theme->font.glyph_count; ++j) {
|
||||||
|
if (theme->font.glyphs[j].codepoint == character) {
|
||||||
|
glyph = &theme->font.glyphs[j];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!glyph) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex_rect_create(
|
||||||
|
vertices, index, zindex,
|
||||||
|
offset_x, offset_y, glyph->metrics.width * scale, glyph->metrics.height * scale, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||||
|
color_index->value_int, glyph->coords.x1, glyph->coords.y1, glyph->coords.x2, glyph->coords.y2
|
||||||
|
);
|
||||||
|
|
||||||
|
offset_x += (glyph->metrics.width + glyph->metrics.offset_x) * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
element->vertex_count = *index - start;
|
||||||
|
memcpy(element->vertices, vertices + start, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||||
|
|
||||||
|
// @todo See todo of vertex_text function
|
||||||
|
// @performance use elements->vertices and also cache result in there
|
||||||
|
|
||||||
|
return offset_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui_button_create(
|
||||||
|
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||||
|
UITheme* theme, UIElement* element
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// @todo handle different states and ongoing animations
|
||||||
|
// We cannot return early in such cases
|
||||||
|
if (element->vertex_count > 0) {
|
||||||
|
memcpy(vertices + *index, element->vertices, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashEntryVoidP* entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, element->name, element->id);
|
||||||
|
UIAttributeGroup* group = (UIAttributeGroup *) entry->value;
|
||||||
|
|
||||||
|
UIAttribute* x;
|
||||||
|
UIAttribute* y;
|
||||||
|
|
||||||
|
UIAttribute* parent = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_PARENT);
|
||||||
|
if (parent) {
|
||||||
|
HashEntryVoidP* parent_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, parent->value_str);
|
||||||
|
UIAttributeGroup* parent_group = (UIAttributeGroup *) parent_entry->value;
|
||||||
|
|
||||||
|
x = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||||
|
y = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||||
|
|
||||||
|
// @question Do we have more values which can be inherited from the parent?
|
||||||
|
// We don't want to inherit implicit stuff like size, background etc. These things should be defined explicitly
|
||||||
|
} else {
|
||||||
|
x = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||||
|
y = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
UIAttribute* width = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH);
|
||||||
|
UIAttribute* height = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT);
|
||||||
|
UIAttribute* align_h = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_ALIGN_H);
|
||||||
|
UIAttribute* align_v = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_ALIGN_V);
|
||||||
|
UIAttribute* text = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_CONTENT);
|
||||||
|
UIAttribute* size = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_FONT_SIZE);
|
||||||
|
UIAttribute* color_index = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_FONT_COLOR);
|
||||||
|
|
||||||
|
// @todo Above we only handle the default values, what about state dependent values like hover, active?
|
||||||
|
// Simply check the state here and load the child_entries based on the state
|
||||||
|
// However, for that we need the current state of the button... should this be in a separate button object,
|
||||||
|
// that also holds position information for hover checks etc. Or should that state be stored in the theme data?
|
||||||
|
// Right now we could make these checks right here anyway but in the future we don't want to update the rendering data
|
||||||
|
// every frame if we don't have to. We don't want immediate mode! We only want to update the UI if there is a change.
|
||||||
|
// If a change (or state change like hover) triggers a complete update of all elements or just a sub region update
|
||||||
|
// remains TBD
|
||||||
|
|
||||||
|
int32 start = *index;
|
||||||
|
|
||||||
|
vertex_rect_border_create(
|
||||||
|
vertices, index, zindex,
|
||||||
|
x->value_int, y->value_int, width->value_int, height->value_int, 1, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||||
|
12, 0.0f, 0.0f
|
||||||
|
);
|
||||||
|
|
||||||
|
vertex_rect_create(
|
||||||
|
vertices, index, zindex,
|
||||||
|
x->value_int + 1, y->value_int + 1, width->value_int - 2, height->value_int - 2, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||||
|
14, 0.0f, 0.0f
|
||||||
|
);
|
||||||
|
|
||||||
|
zindex = nextafterf(zindex, INFINITY);
|
||||||
|
|
||||||
|
vertex_text_create(
|
||||||
|
vertices, index, zindex,
|
||||||
|
x->value_int, y->value_int, width->value_int, height->value_int, align_h->value_int, align_v->value_int,
|
||||||
|
&theme->font, text->value_str, size->value_int, color_index->value_int
|
||||||
|
);
|
||||||
|
|
||||||
|
element->vertex_count = *index - start;
|
||||||
|
memcpy(element->vertices, vertices + start, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
|
|
|
||||||
|
|
@ -105,4 +105,107 @@ uint32 hash_ejb(const char* str)
|
||||||
return h % PRIME2;
|
return h % PRIME2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CONSTEXPR
|
||||||
|
|
||||||
|
constexpr
|
||||||
|
uint64 hash_djb2_const(const char* key) {
|
||||||
|
uint64 hash = 5381;
|
||||||
|
int32 c;
|
||||||
|
|
||||||
|
while ((c = *key++)) {
|
||||||
|
hash = ((hash << 5) + hash) + c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr
|
||||||
|
uint64 hash_sdbm_const(const byte* key)
|
||||||
|
{
|
||||||
|
uint64 hash = 0;
|
||||||
|
int32 c;
|
||||||
|
|
||||||
|
while (c = *key++) {
|
||||||
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr
|
||||||
|
uint64 hash_lose_lose_const(const byte* key)
|
||||||
|
{
|
||||||
|
uint64 hash = 0;
|
||||||
|
int32 c;
|
||||||
|
|
||||||
|
while (c = *key++) {
|
||||||
|
hash += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr
|
||||||
|
uint64 hash_polynomial_rolling_const(const char* str) {
|
||||||
|
const int32 p = 31;
|
||||||
|
const int32 m = 1000000009;
|
||||||
|
uint64 hash = 0;
|
||||||
|
uint64 p_pow = 1;
|
||||||
|
|
||||||
|
while (*str) {
|
||||||
|
hash = (hash + (*str - 'a' + 1) * p_pow) % m;
|
||||||
|
p_pow = (p_pow * p) % m;
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr
|
||||||
|
uint64 hash_fnv1a_const(const char* str) {
|
||||||
|
const uint64 FNV_OFFSET_BASIS = 14695981039346656037UL;
|
||||||
|
const uint64 FNV_PRIME = 1099511628211UL;
|
||||||
|
uint64 hash = FNV_OFFSET_BASIS;
|
||||||
|
|
||||||
|
while (*str) {
|
||||||
|
hash ^= (byte) *str;
|
||||||
|
hash *= FNV_PRIME;
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr
|
||||||
|
uint32 hash_oat_const(const char* str)
|
||||||
|
{
|
||||||
|
uint32 h = 0;
|
||||||
|
|
||||||
|
while(*str) {
|
||||||
|
h += *str++;
|
||||||
|
h += (h << 10);
|
||||||
|
h ^= (h >> 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
h += (h << 3);
|
||||||
|
h ^= (h >> 11);
|
||||||
|
h += (h << 15);
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr
|
||||||
|
uint32 hash_ejb_const(const char* str)
|
||||||
|
{
|
||||||
|
const uint32 PRIME1 = 37;
|
||||||
|
const uint32 PRIME2 = 1048583;
|
||||||
|
uint32 h = 0;
|
||||||
|
|
||||||
|
while (*str) {
|
||||||
|
h = h * PRIME1 ^ (*str++ - ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
return h % PRIME2;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
130
localization/Language.h
Normal file
130
localization/Language.h
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
#ifndef TOS_UI_LANGUAGE_H
|
||||||
|
#define TOS_UI_LANGUAGE_H
|
||||||
|
|
||||||
|
#include "../stdlib/Types.h"
|
||||||
|
#include "../memory/RingMemory.h"
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include "../platform/win32/UtilsWin32.h"
|
||||||
|
#else
|
||||||
|
#include "../platform/linux/UtilsLinux.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Language {
|
||||||
|
// WARNING: the actual start of data is data -= sizeof(count); see file loading below
|
||||||
|
byte* data;
|
||||||
|
|
||||||
|
int32 count;
|
||||||
|
char** lang;
|
||||||
|
};
|
||||||
|
|
||||||
|
void language_from_file_txt(
|
||||||
|
RingMemory* ring,
|
||||||
|
const char* path,
|
||||||
|
Language* language
|
||||||
|
) {
|
||||||
|
FileBody file;
|
||||||
|
file.size = file_size(path);
|
||||||
|
file.content = ring_get_memory(ring, file.size);
|
||||||
|
|
||||||
|
file_read(path, &file);
|
||||||
|
|
||||||
|
// count elements
|
||||||
|
language->count = 1;
|
||||||
|
for (int32 i = 0; i < file.size - 1; ++i) {
|
||||||
|
if (file.content[i] == '\n' && file.content[i + 1] == '\n') {
|
||||||
|
++language->count;
|
||||||
|
file.content[i] = '\0';
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
language->lang = (char **) language->data;
|
||||||
|
memcpy(language->data + language->count * sizeof(char *), file.content, file.size);
|
||||||
|
|
||||||
|
// First element = 0
|
||||||
|
*language->lang = (char *) file.content;
|
||||||
|
++language->lang;
|
||||||
|
|
||||||
|
for (int32 i = 1; i < file.size - 1; ++i) {
|
||||||
|
if (file.content[i] == '\0') {
|
||||||
|
// We have to move by 2 since every text element is separated by 2 \n
|
||||||
|
// 1 \n is a valid char for a single text element
|
||||||
|
// @performance This also means that we have one additional byte for
|
||||||
|
// every text element even in the binary version.
|
||||||
|
*language->lang = (char *) &file.content[i + 2];
|
||||||
|
++language->lang;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// File layout - binary
|
||||||
|
// offsets for start of strings
|
||||||
|
// actual string data
|
||||||
|
void language_from_file(
|
||||||
|
const char* path,
|
||||||
|
Language* language
|
||||||
|
) {
|
||||||
|
FileBody file;
|
||||||
|
file.content = language->data;
|
||||||
|
|
||||||
|
file_read(path, &file);
|
||||||
|
|
||||||
|
byte* pos = language->data;
|
||||||
|
|
||||||
|
// Count
|
||||||
|
language->count = SWAP_ENDIAN_LITTLE(*((int32 *) pos));
|
||||||
|
pos += sizeof(language->count);
|
||||||
|
|
||||||
|
language->lang = (char **) pos;
|
||||||
|
|
||||||
|
byte* start = pos;
|
||||||
|
|
||||||
|
// Load pointers/offsets
|
||||||
|
for (int32 i = 0; i < language->count; ++i) {
|
||||||
|
*language->lang = (char *) (start + SWAP_ENDIAN_LITTLE(*((uint64 *) pos)));
|
||||||
|
++language->lang;
|
||||||
|
pos += sizeof(uint64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't have to load the actual strings, they are already in ->data due to the file reading
|
||||||
|
}
|
||||||
|
|
||||||
|
void language_to_file(
|
||||||
|
RingMemory* ring,
|
||||||
|
const char* path,
|
||||||
|
Language* language
|
||||||
|
) {
|
||||||
|
FileBody file;
|
||||||
|
|
||||||
|
// Temporary file size for buffer
|
||||||
|
// @todo This is a bad placeholder, The problem is we don't know how much we actually need without stepping through the elements
|
||||||
|
// I also don't want to add a size variable to the theme as it is useless in all other cases
|
||||||
|
file.size = MEGABYTE * 32;
|
||||||
|
|
||||||
|
file.content = ring_get_memory(ring, file.size, 64);
|
||||||
|
byte* pos = file.content;
|
||||||
|
|
||||||
|
// Count
|
||||||
|
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(language->count);
|
||||||
|
pos += sizeof(language->count);
|
||||||
|
|
||||||
|
byte* start = pos;
|
||||||
|
|
||||||
|
// Save pointers
|
||||||
|
for (int32 i = 0; i < language->count; ++i) {
|
||||||
|
*((uint64 *) pos) = SWAP_ENDIAN_LITTLE(pos - start);
|
||||||
|
pos += sizeof(uint64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save actual strings
|
||||||
|
for (int32 i = 0; i < language->count; ++i) {
|
||||||
|
strcpy((char *) pos, language->lang[i]);
|
||||||
|
pos += strlen(language->lang[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
file.size = pos - file.content;
|
||||||
|
file_write(path, &file);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -6,6 +6,15 @@
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "TimingStat.h"
|
#include "TimingStat.h"
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
void setup_performance_count() {
|
||||||
|
LARGE_INTEGER perf_counter;
|
||||||
|
QueryPerformanceFrequency(&perf_counter);
|
||||||
|
debug_container->performance_count_frequency = perf_counter.QuadPart;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// IMPORTANT: This function should only be called when you actually use this data
|
// IMPORTANT: This function should only be called when you actually use this data
|
||||||
// e.g. log to display or file
|
// e.g. log to display or file
|
||||||
inline
|
inline
|
||||||
|
|
@ -15,10 +24,28 @@ void update_timing_stat(uint32 stat, const char* function)
|
||||||
|
|
||||||
debug_container->perf_stats[stat].function = function;
|
debug_container->perf_stats[stat].function = function;
|
||||||
debug_container->perf_stats[stat].delta_tick = new_tick_count - debug_container->perf_stats[stat].old_tick_count;
|
debug_container->perf_stats[stat].delta_tick = new_tick_count - debug_container->perf_stats[stat].old_tick_count;
|
||||||
debug_container->perf_stats[stat].delta_time = (double) debug_container->perf_stats[stat].delta_tick / (double) performance_count_frequency;
|
debug_container->perf_stats[stat].delta_time = (double) debug_container->perf_stats[stat].delta_tick / (double) debug_container->performance_count_frequency;
|
||||||
debug_container->perf_stats[stat].old_tick_count = new_tick_count;
|
debug_container->perf_stats[stat].old_tick_count = new_tick_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void reset_counter(int32 id)
|
||||||
|
{
|
||||||
|
debug_container->counter[id] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void log_increment(int32 id)
|
||||||
|
{
|
||||||
|
++debug_container->counter[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void log_counter(int32 id, int32 value)
|
||||||
|
{
|
||||||
|
debug_container->counter[id] = value;
|
||||||
|
}
|
||||||
|
|
||||||
// @todo don't use a pointer to this should be in a global together with other logging data (see Log.h)
|
// @todo don't use a pointer to this should be in a global together with other logging data (see Log.h)
|
||||||
inline
|
inline
|
||||||
DebugMemory* debug_memory_find(uint64 start, uint64 size)
|
DebugMemory* debug_memory_find(uint64 start, uint64 size)
|
||||||
|
|
@ -160,9 +187,7 @@ byte* log_get_memory(uint64 size, byte aligned = 1, bool zeroed = false)
|
||||||
|
|
||||||
if (aligned > 1) {
|
if (aligned > 1) {
|
||||||
uintptr_t address = (uintptr_t) debug_container->log_memory.memory;
|
uintptr_t address = (uintptr_t) debug_container->log_memory.memory;
|
||||||
int64 adjustment = (aligned - ((address + debug_container->log_memory.pos) & (aligned - 1))) % aligned;
|
debug_container->log_memory.pos += (aligned - ((address + debug_container->log_memory.pos) & (aligned - 1))) % aligned;
|
||||||
|
|
||||||
debug_container->log_memory.pos += adjustment;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size = ROUND_TO_NEAREST(size, aligned);
|
size = ROUND_TO_NEAREST(size, aligned);
|
||||||
|
|
@ -171,9 +196,7 @@ byte* log_get_memory(uint64 size, byte aligned = 1, bool zeroed = false)
|
||||||
|
|
||||||
if (aligned > 1) {
|
if (aligned > 1) {
|
||||||
uintptr_t address = (uintptr_t) debug_container->log_memory.memory;
|
uintptr_t address = (uintptr_t) debug_container->log_memory.memory;
|
||||||
int64 adjustment = (aligned - ((address + debug_container->log_memory.pos) & (aligned - 1))) % aligned;
|
debug_container->log_memory.pos += (aligned - ((address + debug_container->log_memory.pos) & (aligned - 1))) % aligned;
|
||||||
|
|
||||||
debug_container->log_memory.pos += adjustment;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
10
log/Debug.h
10
log/Debug.h
|
|
@ -16,9 +16,19 @@
|
||||||
|
|
||||||
struct DebugContainer {
|
struct DebugContainer {
|
||||||
DebugMemoryContainer dmc;
|
DebugMemoryContainer dmc;
|
||||||
|
|
||||||
|
// Used for logging timings for different sections
|
||||||
TimingStat* perf_stats;
|
TimingStat* perf_stats;
|
||||||
|
|
||||||
|
// Required to calculate the "fps"
|
||||||
|
uint64 performance_count_frequency;
|
||||||
|
|
||||||
|
// Used to log memory access (read, write)
|
||||||
LogMemory log_memory;
|
LogMemory log_memory;
|
||||||
|
|
||||||
|
// Used to log general int values (e.g. counter for draw calls etc.)
|
||||||
|
int32* counter;
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
HANDLE log_fp;
|
HANDLE log_fp;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -54,16 +54,24 @@ struct LogMemory {
|
||||||
void log_to_file();
|
void log_to_file();
|
||||||
void log(const char* str, bool should_log, bool save, const char* file, const char* function, int32 line);
|
void log(const char* str, bool should_log, bool save, const char* file, const char* function, int32 line);
|
||||||
void log(const char* format, LogDataType data_type, void* data, bool should_log, bool save, const char* file, const char* function, int32 line);
|
void log(const char* format, LogDataType data_type, void* data, bool should_log, bool save, const char* file, const char* function, int32 line);
|
||||||
|
void log_increment(int32);
|
||||||
|
void log_counter(int32, int32);
|
||||||
|
|
||||||
#if (LOG_LEVEL == 0)
|
#if (LOG_LEVEL == 0)
|
||||||
// Don't perform any logging at log level 0
|
// Don't perform any logging at log level 0
|
||||||
#define LOG(str, should_log, save) ((void) 0)
|
#define LOG(str, should_log, save) ((void) 0)
|
||||||
#define LOG_FORMAT(format, data_type, data, should_log, save) ((void) 0)
|
#define LOG_FORMAT(format, data_type, data, should_log, save) ((void) 0)
|
||||||
#define LOG_TO_FILE() ((void) 0)
|
#define LOG_TO_FILE() ((void) 0)
|
||||||
|
#define LOG_INCREMENT(a) ((void) 0)
|
||||||
|
#define LOG_COUNTER(a, b) ((void) 0)
|
||||||
|
#define RESET_COUNTER(a) ((void) 0)
|
||||||
#else
|
#else
|
||||||
#define LOG(str, should_log, save) log((str), (should_log), (save), __FILE__, __func__, __LINE__)
|
#define LOG(str, should_log, save) log((str), (should_log), (save), __FILE__, __func__, __LINE__)
|
||||||
#define LOG_FORMAT(format, data_type, data, should_log, save) log((format), (data_type), (data), (should_log), (save), __FILE__, __func__, __LINE__)
|
#define LOG_FORMAT(format, data_type, data, should_log, save) log((format), (data_type), (data), (should_log), (save), __FILE__, __func__, __LINE__)
|
||||||
#define LOG_TO_FILE() log_to_file()
|
#define LOG_TO_FILE() log_to_file()
|
||||||
|
#define LOG_INCREMENT(a) log_increment((a))
|
||||||
|
#define LOG_COUNTER(a, b) log_counter((a), (b))
|
||||||
|
#define RESET_COUNTER(a) reset_counter((a))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,6 @@
|
||||||
#include <x86intrin.h>
|
#include <x86intrin.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
global_persist uint64 performance_count_frequency;
|
|
||||||
|
|
||||||
struct TimingStat {
|
struct TimingStat {
|
||||||
const char* function;
|
const char* function;
|
||||||
uint64 old_tick_count;
|
uint64 old_tick_count;
|
||||||
|
|
@ -32,6 +30,7 @@ struct TimingStat {
|
||||||
// Sometimes we want to only do logging in debug mode.
|
// Sometimes we want to only do logging in debug mode.
|
||||||
// In such cases use the following macro.
|
// In such cases use the following macro.
|
||||||
#if DEBUG || INTERNAL
|
#if DEBUG || INTERNAL
|
||||||
|
void update_timing_stat(uint32, const char*);
|
||||||
#define UPDATE_TIMING_STAT(stat) update_timing_stat(stat, __func__)
|
#define UPDATE_TIMING_STAT(stat) update_timing_stat(stat, __func__)
|
||||||
#else
|
#else
|
||||||
#define UPDATE_TIMING_STAT(stat) ((void) 0)
|
#define UPDATE_TIMING_STAT(stat) ((void) 0)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../utils/MathUtils.h"
|
#include "../utils/MathUtils.h"
|
||||||
|
#include "../utils/EndianUtils.h"
|
||||||
#include "../utils/TestUtils.h"
|
#include "../utils/TestUtils.h"
|
||||||
#include "Allocation.h"
|
#include "Allocation.h"
|
||||||
#include "../log/DebugMemory.h"
|
#include "../log/DebugMemory.h"
|
||||||
|
|
@ -35,6 +36,7 @@ void buffer_alloc(BufferMemory* buf, uint64 size, int32 alignment = 64)
|
||||||
: (byte *) playform_alloc_aligned(size, alignment);
|
: (byte *) playform_alloc_aligned(size, alignment);
|
||||||
|
|
||||||
buf->alignment = alignment;
|
buf->alignment = alignment;
|
||||||
|
buf->element_alignment = 0;
|
||||||
buf->size = size;
|
buf->size = size;
|
||||||
|
|
||||||
DEBUG_MEMORY_INIT((uint64) buf->memory, size);
|
DEBUG_MEMORY_INIT((uint64) buf->memory, size);
|
||||||
|
|
@ -51,6 +53,20 @@ void buffer_free(BufferMemory* buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void buffer_init(BufferMemory* buf, byte* data, uint64 size, int32 alignment = 64)
|
||||||
|
{
|
||||||
|
// @bug what if an alignment is defined?
|
||||||
|
buf->memory = data;
|
||||||
|
|
||||||
|
buf->size = size;
|
||||||
|
buf->pos = 0;
|
||||||
|
buf->alignment = alignment;
|
||||||
|
buf->element_alignment = 0;
|
||||||
|
|
||||||
|
DEBUG_MEMORY_INIT((uint64) buf->memory, buf->size);
|
||||||
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void buffer_reset(BufferMemory* buf)
|
void buffer_reset(BufferMemory* buf)
|
||||||
{
|
{
|
||||||
|
|
@ -88,4 +104,53 @@ byte* buffer_get_memory(BufferMemory* buf, uint64 size, int32 aligned = 0, bool
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
int64 buffer_dump(const BufferMemory* buf, byte* data)
|
||||||
|
{
|
||||||
|
byte* start = data;
|
||||||
|
|
||||||
|
// Size
|
||||||
|
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(buf->size);
|
||||||
|
data += sizeof(buf->size);
|
||||||
|
|
||||||
|
// Pos
|
||||||
|
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(buf->pos);
|
||||||
|
data += sizeof(buf->pos);
|
||||||
|
|
||||||
|
// Alignment
|
||||||
|
*((int32 *) data) = SWAP_ENDIAN_LITTLE(buf->alignment);
|
||||||
|
data += sizeof(buf->alignment);
|
||||||
|
|
||||||
|
*((int32 *) data) = SWAP_ENDIAN_LITTLE(buf->element_alignment);
|
||||||
|
data += sizeof(buf->element_alignment);
|
||||||
|
|
||||||
|
// All memory is handled in the buffer -> simply copy the buffer
|
||||||
|
memcpy(data, buf->memory, buf->size);
|
||||||
|
data += buf->size;
|
||||||
|
|
||||||
|
return data - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
int64 buffer_load(BufferMemory* buf, const byte* data)
|
||||||
|
{
|
||||||
|
// Size
|
||||||
|
buf->size = SWAP_ENDIAN_LITTLE(*((uint64 *) data));
|
||||||
|
data += sizeof(buf->size);
|
||||||
|
|
||||||
|
// Pos
|
||||||
|
buf->pos = SWAP_ENDIAN_LITTLE(*((uint64 *) data));
|
||||||
|
data += sizeof(buf->pos);
|
||||||
|
|
||||||
|
// Alignment
|
||||||
|
buf->alignment = SWAP_ENDIAN_LITTLE(*((int32 *) data));
|
||||||
|
data += sizeof(buf->alignment);
|
||||||
|
|
||||||
|
buf->element_alignment = SWAP_ENDIAN_LITTLE(*((int32 *) data));
|
||||||
|
data += sizeof(buf->element_alignment);
|
||||||
|
|
||||||
|
memcpy(buf->memory, data, buf->size);
|
||||||
|
data += buf->size;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../utils/MathUtils.h"
|
#include "../utils/MathUtils.h"
|
||||||
|
#include "../utils/EndianUtils.h"
|
||||||
#include "Allocation.h"
|
#include "Allocation.h"
|
||||||
#include "../log/DebugMemory.h"
|
#include "../log/DebugMemory.h"
|
||||||
|
|
||||||
|
|
@ -37,10 +38,12 @@ void chunk_alloc(ChunkMemory* buf, uint64 count, uint64 chunk_size, int32 alignm
|
||||||
: (byte *) playform_alloc_aligned(count * chunk_size + sizeof(buf->free) * CEIL_DIV(count, 64), alignment);
|
: (byte *) playform_alloc_aligned(count * chunk_size + sizeof(buf->free) * CEIL_DIV(count, 64), alignment);
|
||||||
|
|
||||||
buf->count = count;
|
buf->count = count;
|
||||||
buf->size = chunk_size + sizeof(buf->free) * CEIL_DIV(count, 64);
|
buf->size = count * chunk_size + sizeof(buf->free) * CEIL_DIV(count, 64);
|
||||||
buf->chunk_size = chunk_size;
|
buf->chunk_size = chunk_size;
|
||||||
buf->last_pos = -1;
|
buf->last_pos = -1;
|
||||||
buf->alignment = alignment;
|
buf->alignment = alignment;
|
||||||
|
|
||||||
|
// @question Could it be beneficial to have this before the element data?
|
||||||
buf->free = (uint64 *) (buf->memory + count * chunk_size);
|
buf->free = (uint64 *) (buf->memory + count * chunk_size);
|
||||||
|
|
||||||
DEBUG_MEMORY_INIT((uint64) buf->memory, buf->size);
|
DEBUG_MEMORY_INIT((uint64) buf->memory, buf->size);
|
||||||
|
|
@ -57,6 +60,24 @@ void chunk_free(ChunkMemory* buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void chunk_init(ChunkMemory* buf, byte* data, uint64 count, uint64 chunk_size, int32 alignment = 64)
|
||||||
|
{
|
||||||
|
// @bug what if an alignment is defined?
|
||||||
|
buf->memory = data;
|
||||||
|
|
||||||
|
buf->count = count;
|
||||||
|
buf->size = chunk_size + sizeof(buf->free) * CEIL_DIV(count, 64);
|
||||||
|
buf->chunk_size = chunk_size;
|
||||||
|
buf->last_pos = -1;
|
||||||
|
buf->alignment = alignment;
|
||||||
|
|
||||||
|
// @question Could it be beneficial to have this before the element data?
|
||||||
|
buf->free = (uint64 *) (buf->memory + count * chunk_size);
|
||||||
|
|
||||||
|
DEBUG_MEMORY_INIT((uint64) buf->memory, buf->size);
|
||||||
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
byte* chunk_get_element(ChunkMemory* buf, uint64 element, bool zeroed = false)
|
byte* chunk_get_element(ChunkMemory* buf, uint64 element, bool zeroed = false)
|
||||||
{
|
{
|
||||||
|
|
@ -228,4 +249,66 @@ void chunk_free_element(ChunkMemory* buf, uint64 element)
|
||||||
buf->free[byte_index] &= ~(1 << bit_index);
|
buf->free[byte_index] &= ~(1 << bit_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
int64 chunk_dump(const ChunkMemory* buf, byte* data)
|
||||||
|
{
|
||||||
|
byte* start = data;
|
||||||
|
|
||||||
|
// Count
|
||||||
|
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(buf->count);
|
||||||
|
data += sizeof(buf->count);
|
||||||
|
|
||||||
|
// Size
|
||||||
|
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(buf->size);
|
||||||
|
data += sizeof(buf->size);
|
||||||
|
|
||||||
|
// Chunk Size
|
||||||
|
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(buf->chunk_size);
|
||||||
|
data += sizeof(buf->chunk_size);
|
||||||
|
|
||||||
|
// Last pos
|
||||||
|
*((int64 *) data) = SWAP_ENDIAN_LITTLE(buf->last_pos);
|
||||||
|
data += sizeof(buf->last_pos);
|
||||||
|
|
||||||
|
// Alignment
|
||||||
|
*((int32 *) data) = SWAP_ENDIAN_LITTLE(buf->alignment);
|
||||||
|
data += sizeof(buf->alignment);
|
||||||
|
|
||||||
|
// All memory is handled in the buffer -> simply copy the buffer
|
||||||
|
// This also includes the free array
|
||||||
|
memcpy(data, buf->memory, buf->size);
|
||||||
|
data += buf->size;
|
||||||
|
|
||||||
|
return data - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
int64 chunk_load(ChunkMemory* buf, const byte* data)
|
||||||
|
{
|
||||||
|
// Count
|
||||||
|
buf->count = SWAP_ENDIAN_LITTLE(*((uint64 *) data));
|
||||||
|
data += sizeof(buf->count);
|
||||||
|
|
||||||
|
// Size
|
||||||
|
buf->size = SWAP_ENDIAN_LITTLE(*((uint64 *) data));
|
||||||
|
data += sizeof(buf->size);
|
||||||
|
|
||||||
|
// Chunk Size
|
||||||
|
buf->chunk_size = SWAP_ENDIAN_LITTLE(*((uint64 *) data));
|
||||||
|
data += sizeof(buf->chunk_size);
|
||||||
|
|
||||||
|
// Last pos
|
||||||
|
buf->last_pos = SWAP_ENDIAN_LITTLE(*((int64 *) data));
|
||||||
|
data += sizeof(buf->last_pos);
|
||||||
|
|
||||||
|
// Alignment
|
||||||
|
buf->alignment = SWAP_ENDIAN_LITTLE(*((int32 *) data));
|
||||||
|
data += sizeof(buf->alignment);
|
||||||
|
|
||||||
|
memcpy(buf->memory, data, buf->size);
|
||||||
|
data += buf->size;
|
||||||
|
|
||||||
|
buf->free = (uint64 *) (buf->memory + buf->count * buf->chunk_size);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../utils/MathUtils.h"
|
#include "../utils/MathUtils.h"
|
||||||
|
#include "../utils/EndianUtils.h"
|
||||||
#include "../utils/TestUtils.h"
|
#include "../utils/TestUtils.h"
|
||||||
|
|
||||||
#include "Allocation.h"
|
#include "Allocation.h"
|
||||||
|
|
@ -49,6 +50,7 @@ void ring_alloc(RingMemory* ring, uint64 size, int32 alignment = 64)
|
||||||
ring->size = size;
|
ring->size = size;
|
||||||
ring->pos = 0;
|
ring->pos = 0;
|
||||||
ring->alignment = alignment;
|
ring->alignment = alignment;
|
||||||
|
ring->element_alignment = 0;
|
||||||
ring->start = 0;
|
ring->start = 0;
|
||||||
ring->end = 0;
|
ring->end = 0;
|
||||||
|
|
||||||
|
|
@ -56,13 +58,30 @@ void ring_alloc(RingMemory* ring, uint64 size, int32 alignment = 64)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void ring_create(RingMemory* ring, BufferMemory* buf, uint64 size, int32 alignment = 64)
|
void ring_init(RingMemory* ring, BufferMemory* buf, uint64 size, int32 alignment = 64)
|
||||||
{
|
{
|
||||||
ring->memory = buffer_get_memory(buf, size, alignment);
|
ring->memory = buffer_get_memory(buf, size, alignment);
|
||||||
|
|
||||||
ring->size = size;
|
ring->size = size;
|
||||||
ring->pos = 0;
|
ring->pos = 0;
|
||||||
ring->alignment = alignment;
|
ring->alignment = alignment;
|
||||||
|
ring->element_alignment = 0;
|
||||||
|
ring->start = 0;
|
||||||
|
ring->end = 0;
|
||||||
|
|
||||||
|
DEBUG_MEMORY_INIT((uint64) ring->memory, ring->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void ring_init(RingMemory* ring, byte* buf, uint64 size, int32 alignment = 64)
|
||||||
|
{
|
||||||
|
// @bug what if an alignment is defined?
|
||||||
|
ring->memory = buf;
|
||||||
|
|
||||||
|
ring->size = size;
|
||||||
|
ring->pos = 0;
|
||||||
|
ring->alignment = alignment;
|
||||||
|
ring->element_alignment = 0;
|
||||||
ring->start = 0;
|
ring->start = 0;
|
||||||
ring->end = 0;
|
ring->end = 0;
|
||||||
|
|
||||||
|
|
@ -88,9 +107,7 @@ uint64 ring_calculate_position(const RingMemory* ring, uint64 pos, uint64 size,
|
||||||
|
|
||||||
if (aligned) {
|
if (aligned) {
|
||||||
uintptr_t address = (uintptr_t) ring->memory;
|
uintptr_t address = (uintptr_t) ring->memory;
|
||||||
int64 adjustment = (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
|
pos += (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
|
||||||
|
|
||||||
pos += adjustment;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size = ROUND_TO_NEAREST(size, aligned);
|
size = ROUND_TO_NEAREST(size, aligned);
|
||||||
|
|
@ -99,9 +116,7 @@ uint64 ring_calculate_position(const RingMemory* ring, uint64 pos, uint64 size,
|
||||||
|
|
||||||
if (aligned > 1) {
|
if (aligned > 1) {
|
||||||
uintptr_t address = (uintptr_t) ring->memory;
|
uintptr_t address = (uintptr_t) ring->memory;
|
||||||
int64 adjustment = (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
|
pos += (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
|
||||||
|
|
||||||
pos += adjustment;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,9 +140,7 @@ byte* ring_get_memory(RingMemory* ring, uint64 size, byte aligned = 0, bool zero
|
||||||
|
|
||||||
if (aligned > 1) {
|
if (aligned > 1) {
|
||||||
uintptr_t address = (uintptr_t) ring->memory;
|
uintptr_t address = (uintptr_t) ring->memory;
|
||||||
int64 adjustment = (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
|
ring->pos += (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
|
||||||
|
|
||||||
ring->pos += adjustment;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size = ROUND_TO_NEAREST(size, aligned);
|
size = ROUND_TO_NEAREST(size, aligned);
|
||||||
|
|
@ -136,9 +149,7 @@ byte* ring_get_memory(RingMemory* ring, uint64 size, byte aligned = 0, bool zero
|
||||||
|
|
||||||
if (aligned > 1) {
|
if (aligned > 1) {
|
||||||
uintptr_t address = (uintptr_t) ring->memory;
|
uintptr_t address = (uintptr_t) ring->memory;
|
||||||
int64 adjustment = (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
|
ring->pos += (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
|
||||||
|
|
||||||
ring->pos += adjustment;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,4 +194,38 @@ bool ring_commit_safe(const RingMemory* ring, uint64 size, byte aligned = 0)
|
||||||
: pos < ring->start;
|
: pos < ring->start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
int64 ring_dump(const RingMemory* ring, byte* data)
|
||||||
|
{
|
||||||
|
byte* start = data;
|
||||||
|
|
||||||
|
// Size
|
||||||
|
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(ring->size);
|
||||||
|
data += sizeof(ring->size);
|
||||||
|
|
||||||
|
// Pos
|
||||||
|
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(ring->pos);
|
||||||
|
data += sizeof(ring->pos);
|
||||||
|
|
||||||
|
// Alignment
|
||||||
|
*((int32 *) data) = SWAP_ENDIAN_LITTLE(ring->alignment);
|
||||||
|
data += sizeof(ring->alignment);
|
||||||
|
|
||||||
|
*((int32 *) data) = SWAP_ENDIAN_LITTLE(ring->element_alignment);
|
||||||
|
data += sizeof(ring->element_alignment);
|
||||||
|
|
||||||
|
// Start/End
|
||||||
|
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(ring->start);
|
||||||
|
data += sizeof(ring->start);
|
||||||
|
|
||||||
|
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(ring->end);
|
||||||
|
data += sizeof(ring->end);
|
||||||
|
|
||||||
|
// All memory is handled in the buffer -> simply copy the buffer
|
||||||
|
memcpy(data, ring->memory, ring->size);
|
||||||
|
data += ring->size;
|
||||||
|
|
||||||
|
return data - start;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -75,5 +75,7 @@
|
||||||
#define SETTING_TYPE_UNLIMITED 0x00
|
#define SETTING_TYPE_UNLIMITED 0x00
|
||||||
|
|
||||||
#define SETTING_UI_VISIBILITY_FPS 1
|
#define SETTING_UI_VISIBILITY_FPS 1
|
||||||
|
#define SETTING_UI_VISIBILITY_DEBUG 2
|
||||||
|
#define SETTING_UI_VISIBILITY_WIREFRAME 4
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -60,13 +60,12 @@ void object_from_file_txt(
|
||||||
uint32 temp_color_count = 0;
|
uint32 temp_color_count = 0;
|
||||||
|
|
||||||
while (*pos != '\0') {
|
while (*pos != '\0') {
|
||||||
while (*pos == ' ') {
|
while (*pos == ' ' || *pos == '\t' || *pos == '\n') {
|
||||||
++pos;
|
++pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*pos == '\n') {
|
if (*pos == '\0') {
|
||||||
++pos;
|
break;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse type
|
// Parse type
|
||||||
|
|
@ -373,8 +372,8 @@ int32 object_from_file(
|
||||||
byte* pos = file.content;
|
byte* pos = file.content;
|
||||||
|
|
||||||
// Read version
|
// Read version
|
||||||
//mesh->version = *((int32 *) pos);
|
mesh->version = *((int32 *) pos);
|
||||||
//pos += sizeof(mesh->version);
|
pos += sizeof(mesh->version);
|
||||||
|
|
||||||
// Read base data
|
// Read base data
|
||||||
mesh->vertex_type = *((int32 *) pos);
|
mesh->vertex_type = *((int32 *) pos);
|
||||||
|
|
@ -552,7 +551,11 @@ void object_to_file(
|
||||||
FileBody file;
|
FileBody file;
|
||||||
|
|
||||||
// Temporary file size for buffer
|
// Temporary file size for buffer
|
||||||
file.size = sizeof(mesh) + sizeof(Vertex3D) * mesh->vertex_count + sizeof(f32) * 12 * mesh->vertex_count + 4096;
|
// @todo check the actual size, we are currently more or less guessing
|
||||||
|
file.size = sizeof(mesh)
|
||||||
|
+ sizeof(Vertex3D) * mesh->vertex_count
|
||||||
|
+ sizeof(f32) * 12 * mesh->vertex_count
|
||||||
|
+ 4096;
|
||||||
|
|
||||||
file.content = ring_get_memory(ring, file.size, 64);
|
file.content = ring_get_memory(ring, file.size, 64);
|
||||||
byte* pos = file.content;
|
byte* pos = file.content;
|
||||||
|
|
@ -940,7 +943,7 @@ void object_to_file(
|
||||||
SWAP_ENDIAN_LITTLE_SIMD(
|
SWAP_ENDIAN_LITTLE_SIMD(
|
||||||
(int32 *) file.content,
|
(int32 *) file.content,
|
||||||
(int32 *) file.content,
|
(int32 *) file.content,
|
||||||
(pos - file.content) / 4, // everything in here is 4 bytes -> super easy to swap
|
file.size / 4, // everything in here is 4 bytes -> super easy to swap
|
||||||
steps
|
steps
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ struct WindowState {
|
||||||
uint16 width;
|
uint16 width;
|
||||||
uint16 height;
|
uint16 height;
|
||||||
|
|
||||||
int32 x;
|
uint16 x;
|
||||||
int32 y;
|
uint16 y;
|
||||||
uint64 style;
|
uint64 style;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -30,8 +30,8 @@ struct Window {
|
||||||
uint16 width;
|
uint16 width;
|
||||||
uint16 height;
|
uint16 height;
|
||||||
|
|
||||||
int32 x;
|
uint16 x;
|
||||||
int32 y;
|
uint16 y;
|
||||||
|
|
||||||
// 1. position
|
// 1. position
|
||||||
// 2. focus
|
// 2. focus
|
||||||
|
|
|
||||||
160
stdlib/HashMap.h
160
stdlib/HashMap.h
|
|
@ -74,48 +74,49 @@ struct HashMap {
|
||||||
// WARNING: element_size = element size + remaining HashEntry data size
|
// WARNING: element_size = element size + remaining HashEntry data size
|
||||||
void hashmap_create(HashMap* hm, int32 count, int32 element_size, RingMemory* ring)
|
void hashmap_create(HashMap* hm, int32 count, int32 element_size, RingMemory* ring)
|
||||||
{
|
{
|
||||||
hm->table = (void **) ring_get_memory(ring, count * sizeof(void *));
|
byte* data = ring_get_memory(
|
||||||
|
ring,
|
||||||
|
count * (sizeof(void *) + element_size)
|
||||||
|
+ CEIL_DIV(count, 64) * sizeof(hm->buf.free)
|
||||||
|
);
|
||||||
|
|
||||||
hm->buf.memory = ring_get_memory(ring, count * element_size);
|
hm->table = (void **) data;
|
||||||
hm->buf.free = (uint64 *) ring_get_memory(ring, CEIL_DIV(count, 64) * sizeof(hm->buf.free));
|
chunk_init(&hm->buf, data + sizeof(void *) * count, count, element_size, 1);
|
||||||
hm->buf.count = count;
|
|
||||||
hm->buf.chunk_size = element_size;
|
|
||||||
hm->buf.last_pos = -1;
|
|
||||||
hm->buf.alignment = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: element_size = element size + remaining HashEntry data size
|
// WARNING: element_size = element size + remaining HashEntry data size
|
||||||
void hashmap_create(HashMap* hm, int32 count, int32 element_size, BufferMemory* buf)
|
void hashmap_create(HashMap* hm, int32 count, int32 element_size, BufferMemory* buf)
|
||||||
{
|
{
|
||||||
hm->table = (void **) buffer_get_memory(buf, count * sizeof(void *));
|
byte* data = buffer_get_memory(
|
||||||
|
buf,
|
||||||
|
count * (sizeof(void *) + element_size)
|
||||||
|
+ CEIL_DIV(count, 64) * sizeof(hm->buf.free)
|
||||||
|
);
|
||||||
|
|
||||||
hm->buf.memory = buffer_get_memory(buf, count * element_size);
|
hm->table = (void **) data;
|
||||||
hm->buf.free = (uint64 *) buffer_get_memory(buf, CEIL_DIV(count, 64) * sizeof(hm->buf.free));
|
chunk_init(&hm->buf, data + sizeof(void *) * count, count, element_size, 1);
|
||||||
hm->buf.count = count;
|
|
||||||
hm->buf.chunk_size = element_size;
|
|
||||||
hm->buf.last_pos = -1;
|
|
||||||
hm->buf.alignment = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
int64 hashmap_get_buffer_size(int count, int32 element_size)
|
|
||||||
{
|
|
||||||
return sizeof(void *) * count // table
|
|
||||||
+ count * element_size // elements
|
|
||||||
+ sizeof(uint64) * CEIL_DIV(count, 64); // free
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: element_size = element size + remaining HashEntry data size
|
// WARNING: element_size = element size + remaining HashEntry data size
|
||||||
void hashmap_create(HashMap* hm, int32 count, int32 element_size, byte* buf)
|
void hashmap_create(HashMap* hm, int32 count, int32 element_size, byte* buf)
|
||||||
{
|
{
|
||||||
hm->table = (void **) buf;
|
hm->table = (void **) buf;
|
||||||
|
chunk_init(&hm->buf, buf + sizeof(void *) * count, count, element_size, 1);
|
||||||
|
}
|
||||||
|
|
||||||
hm->buf.memory = buf + sizeof(void *) * count;
|
// Calculates how large a hashmap will be
|
||||||
hm->buf.free = (uint64 *) (hm->buf.memory + count * element_size);
|
inline
|
||||||
hm->buf.count = count;
|
int64 hashmap_size(int count, int32 element_size)
|
||||||
hm->buf.chunk_size = element_size;
|
{
|
||||||
hm->buf.last_pos = -1;
|
return count * sizeof(element_size) // table
|
||||||
hm->buf.alignment = 1;
|
+ count * element_size // elements
|
||||||
|
+ sizeof(uint64) * CEIL_DIV(count, 64); // free
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
int64 hashmap_size(const HashMap* hm)
|
||||||
|
{
|
||||||
|
return hm->buf.count * sizeof(hm->table) + hm->buf.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hashmap_insert(HashMap* hm, const char* key, int32 value) {
|
void hashmap_insert(HashMap* hm, const char* key, int32 value) {
|
||||||
|
|
@ -243,6 +244,23 @@ HashEntry* hashmap_get_entry(HashMap* hm, const char* key) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function only saves one step (omission of the hash function)
|
||||||
|
// The reason for this is in some cases we can use compile time hashing
|
||||||
|
HashEntry* hashmap_get_entry(HashMap* hm, const char* key, uint64 index) {
|
||||||
|
index %= hm->buf.count;
|
||||||
|
HashEntry* entry = (HashEntry *) hm->table[index];
|
||||||
|
|
||||||
|
while (entry != NULL) {
|
||||||
|
if (strncmp(entry->key, key, MAX_KEY_LENGTH) == 0) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = (HashEntry *) entry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void hashmap_delete_entry(HashMap* hm, const char* key) {
|
void hashmap_delete_entry(HashMap* hm, const char* key) {
|
||||||
uint64 index = hash_djb2(key);
|
uint64 index = hash_djb2(key);
|
||||||
HashEntry* entry = (HashEntry *) hm->table[index];
|
HashEntry* entry = (HashEntry *) hm->table[index];
|
||||||
|
|
@ -266,4 +284,90 @@ void hashmap_delete_entry(HashMap* hm, const char* key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @bug We cannot know if the data needs endian swap (it coult be int/float, but also some other 4/8 byte value)
|
||||||
|
// -> if we save this to a file and load it on a different system we will have "corrupt" data
|
||||||
|
inline
|
||||||
|
int64 hashmap_dump(const HashMap* hm, byte* data)
|
||||||
|
{
|
||||||
|
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(hm->buf.count);
|
||||||
|
data += sizeof(uint64);
|
||||||
|
|
||||||
|
uint64 next_count_total = 0;
|
||||||
|
|
||||||
|
// Dump the table content where the elements are relative indeces/pointers
|
||||||
|
for (int32 i = 0; i < hm->buf.count; ++i) {
|
||||||
|
*((uint64 *) data) = SWAP_ENDIAN_LITTLE((uintptr_t) hm->table[i] - (uintptr_t) hm->buf.memory);
|
||||||
|
data += sizeof(uint64);
|
||||||
|
|
||||||
|
// Also dump the next pointer
|
||||||
|
// Count how many next elements we have
|
||||||
|
HashEntry* entry = ((HashEntry *) hm->table[i])->next;
|
||||||
|
int32 next_count = 0;
|
||||||
|
while (entry) {
|
||||||
|
++next_count;
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_count_total += next_count;
|
||||||
|
|
||||||
|
*((int32 *) data) = SWAP_ENDIAN_LITTLE(next_count);
|
||||||
|
data += sizeof(next_count);
|
||||||
|
|
||||||
|
if (next_count > 0) {
|
||||||
|
entry = ((HashEntry *) hm->table[i])->next;
|
||||||
|
while (entry) {
|
||||||
|
*((uint64 *) data) = SWAP_ENDIAN_LITTLE((uintptr_t) entry - (uintptr_t) hm->buf.memory);
|
||||||
|
data += sizeof(uint64);
|
||||||
|
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @performance chunk_dump() below contains some data we already output above
|
||||||
|
// (next pointer but it is useless, since we need relative positions)
|
||||||
|
// Maybe we should manually re-create the chunk_dump here and omit the already dumped data for the next pointer?
|
||||||
|
|
||||||
|
// How many bytes were written (+ dump the chunk memory)
|
||||||
|
return sizeof(hm->buf.count)
|
||||||
|
+ hm->buf.count * sizeof(uint64) // table content
|
||||||
|
+ hm->buf.count * sizeof(int32) // counter for the next pointer (one for every element)
|
||||||
|
+ next_count_total * sizeof(uint64) // next pointer offset
|
||||||
|
+ chunk_dump(&hm->buf, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
int64 hashmap_load(HashMap* hm, const byte* data)
|
||||||
|
{
|
||||||
|
uint64 count = SWAP_ENDIAN_LITTLE(*((uint64 *) data));
|
||||||
|
data += sizeof(uint64);
|
||||||
|
|
||||||
|
uint64 next_count_total = 0;
|
||||||
|
|
||||||
|
// Load the table content, we also need to convert from relative indeces to pointers
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
hm->table[i] = hm->buf.memory + SWAP_ENDIAN_LITTLE(*((uint64 *) data));
|
||||||
|
data += sizeof(uint64);
|
||||||
|
|
||||||
|
// Also load the next pointer
|
||||||
|
// Count how many next elements we have
|
||||||
|
int32 next_count = SWAP_ENDIAN_LITTLE(*((int32 *) data));
|
||||||
|
data += sizeof(next_count);
|
||||||
|
|
||||||
|
HashEntry* entry = ((HashEntry *) hm->table[i]);
|
||||||
|
for (int32 j = 0; j < next_count; ++j) {
|
||||||
|
entry->next = (HashEntry *) (hm->buf.memory + SWAP_ENDIAN_LITTLE(*((uint64 *) data)));
|
||||||
|
data += sizeof(uint64);
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// How many bytes was read from data
|
||||||
|
return sizeof(count)
|
||||||
|
+ hm->buf.count * sizeof(uint64) // table content
|
||||||
|
+ hm->buf.count * sizeof(int32) // counter for the next pointer (one for every element)
|
||||||
|
+ next_count_total * sizeof(uint64) // next pointer offset
|
||||||
|
+ chunk_load(&hm->buf, data);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
#ifndef TOS_UI_ANCHOR_POINT_H
|
|
||||||
#define TOS_UI_ANCHOR_POINT_H
|
|
||||||
|
|
||||||
enum UIAnchor {
|
|
||||||
UI_ANCHOR_GLOBAL,
|
|
||||||
UI_ANCHOR_PANEL,
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
|
|
||||||
struct UIAttribute {
|
struct UIAttribute {
|
||||||
// Attributes use ids instead of strings
|
// Attributes use ids (=type name) instead of strings
|
||||||
int32 attribute_id;
|
int32 attribute_id;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
|
@ -16,11 +16,19 @@ struct UIAttribute {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UIAttributeGroup {
|
struct UIAttributeGroup {
|
||||||
int32 attribute_count;
|
int32 attribute_size;
|
||||||
UIAttribute* attributes;
|
UIAttribute* attributes;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum UIAttributeType {
|
enum UIAttributeType {
|
||||||
|
UI_ATTRIBUTE_TYPE_TYPE,
|
||||||
|
UI_ATTRIBUTE_TYPE_STYLE,
|
||||||
|
|
||||||
|
UI_ATTRIBUTE_TYPE_CONTENT,
|
||||||
|
UI_ATTRIBUTE_TYPE_CONTENT_ALIGN_H,
|
||||||
|
UI_ATTRIBUTE_TYPE_CONTENT_ALIGN_V,
|
||||||
|
|
||||||
|
UI_ATTRIBUTE_TYPE_FONT_COLOR_INDEX,
|
||||||
UI_ATTRIBUTE_TYPE_FONT_COLOR,
|
UI_ATTRIBUTE_TYPE_FONT_COLOR,
|
||||||
UI_ATTRIBUTE_TYPE_FONT_SIZE,
|
UI_ATTRIBUTE_TYPE_FONT_SIZE,
|
||||||
UI_ATTRIBUTE_TYPE_FONT_WEIGHT,
|
UI_ATTRIBUTE_TYPE_FONT_WEIGHT,
|
||||||
|
|
@ -31,6 +39,14 @@ enum UIAttributeType {
|
||||||
|
|
||||||
UI_ATTRIBUTE_TYPE_ZINDEX,
|
UI_ATTRIBUTE_TYPE_ZINDEX,
|
||||||
|
|
||||||
|
UI_ATTRIBUTE_TYPE_POSITION_X,
|
||||||
|
UI_ATTRIBUTE_TYPE_POSITION_Y,
|
||||||
|
UI_ATTRIBUTE_TYPE_PARENT,
|
||||||
|
|
||||||
|
UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH,
|
||||||
|
UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT,
|
||||||
|
|
||||||
|
UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR_INDEX,
|
||||||
UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR,
|
UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR,
|
||||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG,
|
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG,
|
||||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_OPACITY,
|
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_OPACITY,
|
||||||
|
|
@ -38,6 +54,7 @@ enum UIAttributeType {
|
||||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_H,
|
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_H,
|
||||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_STYLE,
|
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_STYLE,
|
||||||
|
|
||||||
|
UI_ATTRIBUTE_TYPE_BORDER_COLOR_INDEX,
|
||||||
UI_ATTRIBUTE_TYPE_BORDER_COLOR,
|
UI_ATTRIBUTE_TYPE_BORDER_COLOR,
|
||||||
UI_ATTRIBUTE_TYPE_BORDER_WIDTH,
|
UI_ATTRIBUTE_TYPE_BORDER_WIDTH,
|
||||||
UI_ATTRIBUTE_TYPE_BORDER_TOP_COLOR,
|
UI_ATTRIBUTE_TYPE_BORDER_TOP_COLOR,
|
||||||
|
|
@ -55,23 +72,43 @@ enum UIAttributeType {
|
||||||
UI_ATTRIBUTE_TYPE_PADDING_BOTTOM,
|
UI_ATTRIBUTE_TYPE_PADDING_BOTTOM,
|
||||||
UI_ATTRIBUTE_TYPE_PADDING_LEFT,
|
UI_ATTRIBUTE_TYPE_PADDING_LEFT,
|
||||||
|
|
||||||
|
UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR_INDEX,
|
||||||
UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR,
|
UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR,
|
||||||
UI_ATTRIBUTE_TYPE_SHADOW_INNER_ANGLE,
|
UI_ATTRIBUTE_TYPE_SHADOW_INNER_ANGLE,
|
||||||
UI_ATTRIBUTE_TYPE_SHADOW_INNER_DISTANCE,
|
UI_ATTRIBUTE_TYPE_SHADOW_INNER_DISTANCE,
|
||||||
|
|
||||||
|
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR_INDEX,
|
||||||
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR,
|
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR,
|
||||||
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_ANGLE,
|
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_ANGLE,
|
||||||
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_DISTANCE,
|
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_DISTANCE,
|
||||||
|
|
||||||
|
// @todo This isn't enough, we can have many animations (position, size, colors, ...)
|
||||||
|
// Maybe we need to define an animation child which overwrites the defined values
|
||||||
|
// Maybe it should use the same system as state dependent values like hover, active, ...
|
||||||
UI_ATTRIBUTE_TYPE_TRANSITION_ANIMATION,
|
UI_ATTRIBUTE_TYPE_TRANSITION_ANIMATION,
|
||||||
UI_ATTRIBUTE_TYPE_TRANSITION_DURATION,
|
UI_ATTRIBUTE_TYPE_TRANSITION_DURATION,
|
||||||
|
|
||||||
UI_ATTRIBUTE_TYPE_SIZE,
|
UI_ATTRIBUTE_TYPE_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
UIAttribute* ui_attribute_from_group(UIAttributeGroup* group, UIAttributeType type)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < UI_ATTRIBUTE_TYPE_SIZE && i <= type; ++i) {
|
||||||
|
if (group->attributes[i].attribute_id == type) {
|
||||||
|
return &group->attributes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr const char* ui_attribute_type_to_string(int32 e)
|
constexpr const char* ui_attribute_type_to_string(int32 e)
|
||||||
{
|
{
|
||||||
switch (e) {
|
switch (e) {
|
||||||
|
case UI_ATTRIBUTE_TYPE_TYPE:
|
||||||
|
return "type";
|
||||||
|
case UI_ATTRIBUTE_TYPE_STYLE:
|
||||||
|
return "style";
|
||||||
case UI_ATTRIBUTE_TYPE_FONT_COLOR:
|
case UI_ATTRIBUTE_TYPE_FONT_COLOR:
|
||||||
return "font_color";
|
return "font_color";
|
||||||
case UI_ATTRIBUTE_TYPE_FONT_SIZE:
|
case UI_ATTRIBUTE_TYPE_FONT_SIZE:
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
#ifndef TOS_UI_BUTTON_H
|
|
||||||
#define TOS_UI_BUTTON_H
|
|
||||||
|
|
||||||
#include "UIElement.h"
|
|
||||||
#include "UILayout.h"
|
|
||||||
|
|
||||||
#include "../animation/AnimationEaseType.h"
|
|
||||||
|
|
||||||
enum ButtonState {
|
|
||||||
BUTTON_STATE_DEFAULT,
|
|
||||||
BUTTON_STATE_HOVER,
|
|
||||||
BUTTON_STATE_CLICK
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UIButton {
|
|
||||||
UIElement element;
|
|
||||||
|
|
||||||
ButtonState state_old;
|
|
||||||
ButtonState state_new;
|
|
||||||
|
|
||||||
// Is pointer becuase we probably want to have a global layout style that we re-use over and over
|
|
||||||
UILayout* layout_default;
|
|
||||||
|
|
||||||
UILayout* layout_hover;
|
|
||||||
f32 layout_hover_anim_duration;
|
|
||||||
AnimationEaseType layout_hover_anim_style;
|
|
||||||
int hover_sound;
|
|
||||||
|
|
||||||
UILayout* layout_click;
|
|
||||||
f32 layout_click_anim_duration;
|
|
||||||
AnimationEaseType layout_click_anim_style;
|
|
||||||
int click_sound;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
#ifndef TOS_UI_ELEMENT_H
|
#ifndef TOS_UI_ELEMENT_H
|
||||||
#define TOS_UI_ELEMENT_H
|
#define TOS_UI_ELEMENT_H
|
||||||
|
|
||||||
#include "UIElementType.h"
|
|
||||||
#include "UIAlignment.h"
|
|
||||||
#include "UIAnchor.h"
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
|
#include "UIElementType.h"
|
||||||
|
#include "../object/Vertex.h"
|
||||||
|
|
||||||
struct UIElementDimension {
|
struct UIElementDimension {
|
||||||
int16 x1;
|
int16 x1;
|
||||||
|
|
@ -13,27 +12,37 @@ struct UIElementDimension {
|
||||||
int16 y2;
|
int16 y2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define UI_ELEMENT_STATE_VISIBLE 1
|
||||||
|
#define UI_ELEMENT_STATE_ACTIVE 2
|
||||||
|
#define UI_ELEMENT_STATE_FOCUSED 4
|
||||||
|
#define UI_ELEMENT_STATE_CLICKED 8
|
||||||
|
#define UI_ELEMENT_STATE_ANIMATION 16
|
||||||
|
|
||||||
struct UIElement {
|
struct UIElement {
|
||||||
int id;
|
const char* name;
|
||||||
|
int32 id;
|
||||||
UIElementType type;
|
UIElementType type;
|
||||||
|
|
||||||
int window_id;
|
int16 window_id;
|
||||||
int panel_id;
|
int16 panel_id;
|
||||||
|
|
||||||
UIElementDimension dimension;
|
UIElementDimension dimension;
|
||||||
|
|
||||||
UIAlignH align_h;
|
int32 state_flag;
|
||||||
UIAlignV align_v;
|
|
||||||
UIAnchor anchor;
|
|
||||||
|
|
||||||
bool is_visible;
|
f32 anim_elapsed;
|
||||||
bool is_active;
|
|
||||||
bool is_focused;
|
|
||||||
|
|
||||||
int16 scroll_x;
|
int16 scroll_x;
|
||||||
int16 scroll_y;
|
int16 scroll_y;
|
||||||
|
|
||||||
// @todo animation state
|
// @todo animation state
|
||||||
|
|
||||||
|
// @todo cache vertex result for default, hover etc.
|
||||||
|
int32 vertex_count;
|
||||||
|
Vertex3DTextureColorIndex* vertices; // WARNING: This is not the official holder of the memory, its in UILayout
|
||||||
|
|
||||||
|
// @todo We could even have a pointer that points into the complete ui array making it possible to simply replace this section
|
||||||
|
// This is something we wanted to do anyway when updating sub regions on the gpu memory
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -2,22 +2,35 @@
|
||||||
#define TOS_UI_LAYOUT_H
|
#define TOS_UI_LAYOUT_H
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
|
#include "../stdlib/HashMap.h"
|
||||||
#include "UIElement.h"
|
#include "UIElement.h"
|
||||||
#include "UILocation.h"
|
|
||||||
|
|
||||||
|
// Modified for every scene
|
||||||
struct UILayout {
|
struct UILayout {
|
||||||
int32 ui_deadzone_count = 5;
|
int32 ui_deadzone_size = 5;
|
||||||
UIElementDimension ui_deadzone[5];
|
UIElementDimension ui_deadzone[5];
|
||||||
|
|
||||||
int32 element_hoverable_count;
|
int32 element_hoverable_size;
|
||||||
|
int32 element_hoverable_pos;
|
||||||
UIElementDimension* elements_hoverable;
|
UIElementDimension* elements_hoverable;
|
||||||
|
|
||||||
int32 element_interactible_count;
|
int32 element_interactible_size;
|
||||||
|
int32 element_interactible_pos;
|
||||||
UIElementDimension* elements_interactible;
|
UIElementDimension* elements_interactible;
|
||||||
|
|
||||||
int32 element_count;
|
// @question Since we use a hashmap below, do we even need the size?
|
||||||
UIElement* element;
|
// Isn't the size exactly the same as the hash_map buf size
|
||||||
|
int32 element_size;
|
||||||
|
int32 element_pos;
|
||||||
|
HashMap hash_map; // Used to directly find element by name
|
||||||
|
|
||||||
|
// @question Do we even need this or should the hashmap values be the elements directly?
|
||||||
|
// In other places (e.g. theme) we simply define a byte* data variable which actually holds the info.
|
||||||
|
UIElement* elements;
|
||||||
|
|
||||||
|
int32 vertex_size;
|
||||||
|
int32 vertex_pos;
|
||||||
|
Vertex3DTextureColorIndex* vertices;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
#ifndef TOS_UI_LOCATION_H
|
|
||||||
#define TOS_UI_LOCATION_H
|
|
||||||
|
|
||||||
enum UILocation {
|
|
||||||
UI_LOCATION_LEFT,
|
|
||||||
UI_LOCATION_CENTER,
|
|
||||||
UI_LOCATION_RIGHT,
|
|
||||||
|
|
||||||
UI_LOCATION_FLEX_ROW,
|
|
||||||
UI_LOCATION_FLEX_COL
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
#ifndef TOS_UI_POSITION_H
|
|
||||||
#define TOS_UI_POSITION_H
|
|
||||||
|
|
||||||
enum UIPosition {
|
|
||||||
UI_POSITION_RELATIVE,
|
|
||||||
UI_POSITION_ABSOLUTE
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
298
ui/UITheme.h
298
ui/UITheme.h
|
|
@ -18,35 +18,87 @@
|
||||||
|
|
||||||
#define UI_THEME_VERSION 1
|
#define UI_THEME_VERSION 1
|
||||||
|
|
||||||
struct UITheme {
|
// @question Currently there is some data duplication in here and in the UIElement.
|
||||||
|
// Not sure if this is how we want this to be or if we want to change this in the future
|
||||||
|
// Modified for every scene
|
||||||
|
// WARNING: Make sure the order of this struct and UITheme is the same for the first elements
|
||||||
|
// This allows us to cast between both
|
||||||
|
struct UIThemeStyle {
|
||||||
byte* data;
|
byte* data;
|
||||||
|
|
||||||
int32 version;
|
int32 version;
|
||||||
char name[32];
|
|
||||||
bool is_active;
|
|
||||||
|
|
||||||
// Every element has all the style attributes
|
|
||||||
UIAttribute styles_global[UI_ELEMENT_TYPE_SIZE * UI_ATTRIBUTE_TYPE_SIZE];
|
|
||||||
|
|
||||||
// A theme may have N named styles
|
// A theme may have N named styles
|
||||||
// @todo We should probably create a hashmap
|
// The hashmap contains the offset where the respective style can be found
|
||||||
// key = name
|
|
||||||
// value = pointer to group (in the file we store the offset when we load it into memory convert it to pointer)
|
|
||||||
HashMap hash_map;
|
HashMap hash_map;
|
||||||
|
};
|
||||||
|
|
||||||
int32 style_group_count;
|
// General theme for all scenes
|
||||||
UIAttributeGroup* style_groups;
|
struct UITheme {
|
||||||
|
// This one usually remains unchanged unless someone changes the theme
|
||||||
|
UIThemeStyle ui_general;
|
||||||
|
|
||||||
|
// This one is scene specific and is loaded with the scene
|
||||||
|
// We have a pointer that references the currently active scene
|
||||||
|
// The other two elements contain the actual data.
|
||||||
|
// This allows us to easily pre-fetch scene styles and the pointer allows us to easily switch between them
|
||||||
|
// When loading a new scene we simply use the style that is currently not pointed to by primary_scene
|
||||||
|
UIThemeStyle* primary_scene;
|
||||||
|
UIThemeStyle ui_scene1;
|
||||||
|
UIThemeStyle ui_scene2;
|
||||||
|
|
||||||
|
// @question This basically means we only support 1 font for the UI ?!
|
||||||
|
// This is probably something to re-consider
|
||||||
Font font;
|
Font font;
|
||||||
|
|
||||||
// @todo add cursor styles
|
// @todo add cursor styles
|
||||||
|
// @todo what about ui audio?
|
||||||
|
|
||||||
|
char name[32];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// @performance Consider to replace HashEntryInt64 with HashEntryVoidP.
|
||||||
|
// This way we wouldn't have to do theme->data + entry->value and could just use entry->value
|
||||||
|
// Of course this means during the saving and loading we need to convert to and from offsets
|
||||||
|
// The problem is that the actual dumping and loading for that part doesn't happen in the hashmap but in the chunk_memory
|
||||||
|
// The chunk_memory doesn't know how the value look like -> cannot do the conversion
|
||||||
|
inline
|
||||||
|
UIAttributeGroup* theme_style_group(UIThemeStyle* theme, const char* group_name)
|
||||||
|
{
|
||||||
|
HashEntryInt64* entry = (HashEntryInt64 *) hashmap_get_entry(&theme->hash_map, group_name);
|
||||||
|
return (UIAttributeGroup *) (theme->data + entry->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
UIAttributeGroup* theme_style_group(UIThemeStyle* theme, const char* group_name, int32 group_id)
|
||||||
|
{
|
||||||
|
HashEntryInt64* entry = (HashEntryInt64 *) hashmap_get_entry(&theme->hash_map, group_name, group_id);
|
||||||
|
return (UIAttributeGroup *) (theme->data + entry->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int compare_by_attribute_id(const void* a, const void* b) {
|
||||||
|
UIAttribute* attr_a = (UIAttribute *) a;
|
||||||
|
UIAttribute* attr_b = (UIAttribute *) b;
|
||||||
|
|
||||||
|
return attr_a->attribute_id - attr_b->attribute_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// File layout - text
|
||||||
|
// version
|
||||||
|
// #group_name
|
||||||
|
// attributes ...
|
||||||
|
// attributes ...
|
||||||
|
// attributes ...
|
||||||
|
// #group_name
|
||||||
|
// attributes ...
|
||||||
|
// attributes ...
|
||||||
|
// attributes ...
|
||||||
|
|
||||||
// WARNING: theme needs to have memory already reserved and asigned to data
|
// WARNING: theme needs to have memory already reserved and asigned to data
|
||||||
void theme_from_file_txt(
|
void theme_from_file_txt(
|
||||||
RingMemory* ring,
|
RingMemory* ring,
|
||||||
const char* path,
|
const char* path,
|
||||||
UITheme* theme
|
UIThemeStyle* theme
|
||||||
) {
|
) {
|
||||||
FileBody file;
|
FileBody file;
|
||||||
file_read(path, &file, ring);
|
file_read(path, &file, ring);
|
||||||
|
|
@ -59,6 +111,39 @@ void theme_from_file_txt(
|
||||||
char attribute_name[32];
|
char attribute_name[32];
|
||||||
bool last_token_newline = false;
|
bool last_token_newline = false;
|
||||||
|
|
||||||
|
// We have to find how many groups are defined in the theme file.
|
||||||
|
// Therefore we have to do an initial iteration
|
||||||
|
int32 temp_group_count = 0;
|
||||||
|
while (*pos != '\0') {
|
||||||
|
// Skip all white spaces
|
||||||
|
while (*pos == ' ' || *pos == '\t' || *pos == '\n') {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is group name
|
||||||
|
if (*pos == '#' || *pos == '.') {
|
||||||
|
++temp_group_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to the end of the line
|
||||||
|
while (*pos != '\n' && *pos != '\0') {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to next line
|
||||||
|
if (*pos != '\0') {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @performance This is probably horrible since we are not using a perfect hashing function (1 hash -> 1 index)
|
||||||
|
// I wouldn't be surprised if we have a 50% hash overlap (2 hashes -> 1 index)
|
||||||
|
hashmap_create(&theme->hash_map, temp_group_count, sizeof(HashEntryInt64), theme->data);
|
||||||
|
int64 data_offset = hashmap_size(&theme->hash_map);
|
||||||
|
|
||||||
|
UIAttributeGroup* temp_group = NULL;
|
||||||
|
|
||||||
|
pos = (char *) file.content;
|
||||||
while (*pos != '\0') {
|
while (*pos != '\0') {
|
||||||
while (*pos == ' ' || *pos == '\t') {
|
while (*pos == ' ' || *pos == '\t') {
|
||||||
++pos;
|
++pos;
|
||||||
|
|
@ -90,9 +175,21 @@ void theme_from_file_txt(
|
||||||
|
|
||||||
block_name[i] = '\0';
|
block_name[i] = '\0';
|
||||||
|
|
||||||
if (*block_name == '#') {
|
// All blocks need to start with #. In the past this wasn't the case and may not be in the future. This is why we keep this if here.
|
||||||
|
if (*block_name == '#' || *block_name == '.') {
|
||||||
// Named style
|
// Named style
|
||||||
// @todo create new style
|
if (temp_group != NULL) {
|
||||||
|
// Before we insert a new group we have to sort the attributes
|
||||||
|
// since this makes searching them later on more efficient.
|
||||||
|
qsort(temp_group->attributes, temp_group->attribute_size, sizeof(UIAttribute), compare_by_attribute_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert new group
|
||||||
|
hashmap_insert(&theme->hash_map, block_name, data_offset);
|
||||||
|
|
||||||
|
temp_group = (UIAttributeGroup *) (theme->data + data_offset);
|
||||||
|
temp_group->attribute_size = 0;
|
||||||
|
data_offset += sizeof(temp_group->attribute_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -115,8 +212,37 @@ void theme_from_file_txt(
|
||||||
ASSERT_SIMPLE((*pos != '\0' && *pos != '\n'));
|
ASSERT_SIMPLE((*pos != '\0' && *pos != '\n'));
|
||||||
|
|
||||||
// Handle different attribute types
|
// Handle different attribute types
|
||||||
UIAttribute attribute;
|
UIAttribute attribute = {};
|
||||||
if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_FONT_COLOR), attribute_name) == 0) {
|
if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_TYPE), attribute_name) == 0) {
|
||||||
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_TYPE;
|
||||||
|
|
||||||
|
char str[32];
|
||||||
|
char* temp = str;
|
||||||
|
while (*pos != '\n' && *pos != '\0') {
|
||||||
|
*temp++ = *pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*temp = '\0';
|
||||||
|
|
||||||
|
for (int32 i = 0; i < UI_ELEMENT_TYPE_SIZE; ++i) {
|
||||||
|
if (strcmp(str, ui_attribute_type_to_string(i)) == 0) {
|
||||||
|
attribute.value_int = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++pos;
|
||||||
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_STYLE), attribute_name) == 0) {
|
||||||
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_STYLE;
|
||||||
|
|
||||||
|
char* temp = attribute.value_str;
|
||||||
|
while (*pos != '\n' && *pos != '\0') {
|
||||||
|
*temp++ = *pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*temp = '\0';
|
||||||
|
++pos;
|
||||||
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_FONT_COLOR), attribute_name) == 0) {
|
||||||
++pos; // Skip '#'
|
++pos; // Skip '#'
|
||||||
|
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_COLOR;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_COLOR;
|
||||||
|
|
@ -306,47 +432,131 @@ void theme_from_file_txt(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Again, currently this if check is redundant but it wasn't in the past and we may need it again in the future.
|
||||||
if (block_name[0] == '#') {
|
if (block_name[0] == '#') {
|
||||||
// Named block
|
// Named block
|
||||||
} else {
|
|
||||||
// Global default elements and their attributes
|
|
||||||
int32 element_type = -1;
|
|
||||||
for (int j = 0; j < UI_ELEMENT_TYPE_SIZE; ++j) {
|
|
||||||
if (strcmp(ui_element_type_to_string((UIElementType) j), block_name) == 0) {
|
|
||||||
element_type = j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element_type < 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(
|
memcpy(
|
||||||
theme->styles_global + element_type * UI_ATTRIBUTE_TYPE_SIZE,
|
temp_group->attributes + temp_group->attribute_size,
|
||||||
&attribute,
|
&attribute,
|
||||||
sizeof(attribute)
|
sizeof(attribute)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
data_offset += sizeof(attribute);
|
||||||
|
++temp_group->attribute_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We still need to sort the last group
|
||||||
|
qsort(temp_group->attributes, temp_group->attribute_size, sizeof(UIAttribute), compare_by_attribute_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory layout (data) - This is not the layout of the file itself, just how we represent it in memory
|
||||||
|
// Hashmap
|
||||||
|
// UIAttributeGroup - This is where the pointers point to (or what the offset represents)
|
||||||
|
// size
|
||||||
|
// Attributes ...
|
||||||
|
// Attributes ...
|
||||||
|
// Attributes ...
|
||||||
|
// UIAttributeGroup
|
||||||
|
// size
|
||||||
|
// Attributes ...
|
||||||
|
// Attributes ...
|
||||||
|
// Attributes ...
|
||||||
|
|
||||||
|
// The size of theme->data should be the file size.
|
||||||
|
// Yes, this means we have a little too much data but not by a lot
|
||||||
|
void theme_from_file(
|
||||||
|
UIThemeStyle* theme,
|
||||||
|
const byte* data
|
||||||
|
) {
|
||||||
|
const byte* pos = data;
|
||||||
|
|
||||||
|
theme->version = *((int32 *) pos);
|
||||||
|
pos += sizeof(theme->version);
|
||||||
|
|
||||||
|
// Prepare hashmap (incl. reserve memory) by initializing it the same way we originally did
|
||||||
|
// Of course we still need to populate the data using hashmap_load()
|
||||||
|
hashmap_create(&theme->hash_map, SWAP_ENDIAN_LITTLE(*((uint64 *) pos)), sizeof(HashEntryInt64), theme->data);
|
||||||
|
pos += hashmap_load(&theme->hash_map, pos);
|
||||||
|
|
||||||
|
// theme data
|
||||||
|
// Layout: first load the size of the group, then load the individual attributes
|
||||||
|
for (int32 i = 0; i < theme->hash_map.buf.count; ++i) {
|
||||||
|
HashEntryInt64* entry = (HashEntryInt64 *) theme->hash_map.table[i];
|
||||||
|
UIAttributeGroup* group = (UIAttributeGroup *) (theme->data + entry->value);
|
||||||
|
|
||||||
|
group->attribute_size = SWAP_ENDIAN_LITTLE(*((int32 *) pos));
|
||||||
|
pos += sizeof(group->attribute_size);
|
||||||
|
|
||||||
|
// @performance The UIAttribute contains a char array which makes this WAY larger than it needs to be in 99% of the cases
|
||||||
|
memcpy(group->attributes, pos, group->attribute_size * sizeof(UIAttribute));
|
||||||
|
pos += group->attribute_size * sizeof(UIAttribute);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void theme_from_file(
|
// Calculates the maximum theme size
|
||||||
RingMemory* ring,
|
// Not every group has all the attributes (most likely only a small subset)
|
||||||
const char* path,
|
// However, an accurate calculation is probably too slow and not needed most of the time
|
||||||
UITheme* theme
|
inline
|
||||||
) {
|
int64 theme_size(const UIThemeStyle* theme)
|
||||||
// version
|
{
|
||||||
// global definitions
|
return hashmap_size(&theme->hash_map)
|
||||||
// names count
|
+ theme->hash_map.buf.count * UI_ATTRIBUTE_TYPE_SIZE * sizeof(UIAttribute);
|
||||||
//
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void theme_to_file(RingMemory* ring,
|
// File layout - binary
|
||||||
const char* path,
|
// version
|
||||||
const UITheme* theme
|
// hashmap size
|
||||||
) {
|
// Hashmap (includes offsets to the individual groups)
|
||||||
|
// #group_name (this line doesn't really exist in file, it's more like the pointer offset in the hashmap)
|
||||||
|
// size
|
||||||
|
// attributes ...
|
||||||
|
// attributes ...
|
||||||
|
// attributes ...
|
||||||
|
// #group_name
|
||||||
|
// size
|
||||||
|
// attributes ...
|
||||||
|
// attributes ...
|
||||||
|
// attributes ...
|
||||||
|
|
||||||
|
void theme_to_file(
|
||||||
|
RingMemory* ring,
|
||||||
|
const char* path,
|
||||||
|
const UIThemeStyle* theme
|
||||||
|
) {
|
||||||
|
FileBody file;
|
||||||
|
|
||||||
|
// Temporary file size for buffer
|
||||||
|
// @todo This is a bad placeholder, The problem is we don't know how much we actually need without stepping through the elements
|
||||||
|
// I also don't want to add a size variable to the theme as it is useless in all other cases
|
||||||
|
file.size = theme_size(theme);
|
||||||
|
|
||||||
|
file.content = ring_get_memory(ring, file.size, 64);
|
||||||
|
byte* pos = file.content;
|
||||||
|
|
||||||
|
// version
|
||||||
|
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(theme->version);
|
||||||
|
pos += sizeof(theme->version);
|
||||||
|
|
||||||
|
// hashmap
|
||||||
|
pos += hashmap_dump(&theme->hash_map, pos);
|
||||||
|
|
||||||
|
// theme data
|
||||||
|
// Layout: first save the size of the group, then save the individual attributes
|
||||||
|
for (int32 i = 0; i < theme->hash_map.buf.count; ++i) {
|
||||||
|
HashEntryInt64* entry = (HashEntryInt64 *) theme->hash_map.table[i];
|
||||||
|
UIAttributeGroup* group = (UIAttributeGroup *) (theme->data + entry->value);
|
||||||
|
|
||||||
|
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(group->attribute_size);
|
||||||
|
pos += sizeof(group->attribute_size);
|
||||||
|
|
||||||
|
// @performance The UIAttribute contains a char array which makes this WAY larger than it needs to be in 99% of the cases
|
||||||
|
memcpy(pos, group->attributes, group->attribute_size * sizeof(UIAttribute));
|
||||||
|
pos += group->attribute_size * sizeof(UIAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
file.size = pos - file.content;
|
||||||
|
file_write(path, &file);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
#ifndef TOS_UI_WINDOW_H
|
|
||||||
#define TOS_UI_WINDOW_H
|
|
||||||
|
|
||||||
#include "UIElement.h"
|
|
||||||
#include "UILayout.h"
|
|
||||||
|
|
||||||
#include "../animation/AnimationEaseType.h"
|
|
||||||
|
|
||||||
enum WindowState {
|
|
||||||
WINDOW_STATE_ACIVE,
|
|
||||||
WINDOW_STATE_INACTIVE,
|
|
||||||
WINDOW_STATE_FOCUS,
|
|
||||||
|
|
||||||
WINDOW_STATE_MOVING,
|
|
||||||
WINDOW_STATE_OPENING,
|
|
||||||
WINDOW_STATE_CLOSING
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UIWindow {
|
|
||||||
UIElement element;
|
|
||||||
|
|
||||||
WindowState state_old;
|
|
||||||
WindowState state_new;
|
|
||||||
|
|
||||||
bool is_minimizable;
|
|
||||||
bool is_maximizable;
|
|
||||||
bool is_movable;
|
|
||||||
bool is_resizable;
|
|
||||||
|
|
||||||
// window is only movable when holding this area
|
|
||||||
int32 movable_area[4];
|
|
||||||
|
|
||||||
UILayout* layout_default;
|
|
||||||
f32 layout_open_anim_duration;
|
|
||||||
f32 layout_close_anim_duration;
|
|
||||||
f32 layout_min_anim_duration;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
|
|
||||||
constexpr
|
constexpr
|
||||||
size_t strlen_compile_time(const char* str) {
|
size_t strlen_const(const char* str) {
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
while (str[len] != '\0') {
|
while (str[len] != '\0') {
|
||||||
++len;
|
++len;
|
||||||
|
|
@ -259,6 +259,7 @@ str_concat(
|
||||||
*dst = '\0';
|
*dst = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
char* strtok(char* str, const char* __restrict delim, char* *key) {
|
char* strtok(char* str, const char* __restrict delim, char* *key) {
|
||||||
char* result;
|
char* result;
|
||||||
if (str == NULL) {
|
if (str == NULL) {
|
||||||
|
|
@ -283,38 +284,42 @@ char* strtok(char* str, const char* __restrict delim, char* *key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void format_number_render(int32 length, char* buffer, const char thousands = ',')
|
int32 int_to_str(int64 number, char *str, const char thousands = ',') {
|
||||||
{
|
int32 i = 0;
|
||||||
int32 count = (int32) (length / 3) - (length % 3 == 0 ? 1 : 0);
|
int64 sign = number;
|
||||||
|
int32 digit_count = 0;
|
||||||
|
|
||||||
int32 j = -1;
|
if (number == 0) {
|
||||||
for (int32 i = length; i > 0; --i) {
|
str[i++] = '0';
|
||||||
++j;
|
} else if (number < 0) {
|
||||||
|
number = -number;
|
||||||
if (j % 3 == 0 && j != 0) {
|
|
||||||
buffer[i + count] = buffer[i];
|
|
||||||
--count;
|
|
||||||
buffer[i + count] = thousands;
|
|
||||||
} else {
|
|
||||||
buffer[i + count] = buffer[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
char* format_number(size_t number, char* buffer, const char thousands = ',')
|
while (number > 0) {
|
||||||
{
|
if (thousands
|
||||||
int32 length = snprintf(buffer, 32, "%zu", number);
|
&& (digit_count == 3 || digit_count == 6 || digit_count == 9 || digit_count == 12 || digit_count == 15)
|
||||||
format_number_render(length, buffer, thousands);
|
) {
|
||||||
|
str[i++] = thousands;
|
||||||
|
}
|
||||||
|
|
||||||
return buffer;
|
str[i++] = number % 10 + '0';
|
||||||
}
|
number /= 10;
|
||||||
|
++digit_count;
|
||||||
|
}
|
||||||
|
|
||||||
char* format_number(int32 number, char* buffer, const char thousands = ',')
|
if (sign < 0) {
|
||||||
{
|
str[i++] = '-';
|
||||||
int32 length = snprintf(buffer, 32, "%i", number);
|
}
|
||||||
format_number_render(length, buffer, thousands);
|
|
||||||
|
|
||||||
return buffer;
|
str[i] = '\0';
|
||||||
|
|
||||||
|
for (int32 j = 0, k = i - 1; j < k; ++j, --k) {
|
||||||
|
char temp = str[j];
|
||||||
|
str[j] = str[k];
|
||||||
|
str[k] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char toupper_ascii(char c)
|
char toupper_ascii(char c)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user