started to implement more custom file formats, 100% nothing is working at the moment

This commit is contained in:
Dennis Eichhorn 2024-10-21 03:58:35 +02:00
parent 653a14442f
commit 0c4bd343fc
39 changed files with 1719 additions and 290 deletions

View File

@ -25,6 +25,11 @@ struct AssetManagementSystem {
// @question is this even necessary or could we integrate this directly into the system here?
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
// General asset memory
@ -71,7 +76,7 @@ int32 ams_calculate_chunks(AssetManagementSystem* ams, int32 byte_size)
inline
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
+ 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
uint64 ams_get_vram_usage(AssetManagementSystem* ams)
void ams_update_stats(AssetManagementSystem* ams)
{
uint64 size = 0;
for (int32 i = 0; i < ams->asset_memory.count; ++i) {
size += ((Asset *) (ams->asset_memory.memory))[i].vram_size;
// @bug We should check the hash map or the memory status, we could still have old values in here
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)
@ -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_data_memory, asset->internal_id + i);
}
ams->has_changed = true;
}
inline
@ -208,6 +256,8 @@ Asset* ams_reserve_asset(AssetManagementSystem* ams, const char* name, uint32 el
ams->last = asset;
}
ams->has_changed = true;
return asset;
}

View File

@ -47,7 +47,7 @@ uint32 crc_table[256] =
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;
while (length-- != 0) {
@ -58,7 +58,7 @@ uint32 calculate_crc32_checksum(uint8 *p, uint32 length)
return (crc ^ 0xFFFFFFFF);
}
void fill_crc32_table(uint32 *table){
void crc32_table_fill(uint32 *table){
uint8 index = 0,z;
do {
table[index] = index;

229
compression/Huffman.h Normal file
View 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

View File

@ -14,7 +14,7 @@
#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 table[1 << 16] = {0};
@ -58,7 +58,7 @@ uint32 encode_lzp(const byte* in, size_t length, byte* out)
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 table[1 << 16] = {0};
@ -126,7 +126,7 @@ int32 find_longest_match(char *window, int32 window_start, char *buffer, int32 b
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};
int32 window_start = 0;
@ -157,7 +157,7 @@ uint32 encode_lzp3(const byte* in, size_t length, byte* out) {
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};
int32 window_start = 0;

66
compression/RLE.h Normal file
View 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

View File

@ -3,19 +3,8 @@
#include "../stdlib/Types.h"
#include "../memory/BufferMemory.h"
// @todo Move this somewhere else, it doesn't belong here
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,
};
#include "../utils/EndianUtils.h"
#include "../stdlib/simd/SIMD_I32.h"
struct GlyphMetrics {
f32 width; // Width of the glyph
@ -25,11 +14,9 @@ struct GlyphMetrics {
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 {
f32 x1;
f32 y1;
f32 x2;
f32 y2;
};
@ -50,9 +37,9 @@ struct Font {
};
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;
}
@ -68,4 +55,168 @@ Glyph* font_glyph_find(Font* font, uint32 codepoint)
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

View File

@ -16,6 +16,12 @@
#include "../math/matrix/MatrixFloat32.h"
#include "../font/Font.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
void vertex_degenerate_create(
@ -370,7 +376,7 @@ void text_calculate_dimensions(
*height = y;
}
void vertex_text_create(
f32 vertex_text_create(
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
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
@ -433,6 +439,189 @@ void vertex_text_create(
// @todo If width or height (usually just width) > 0 we use those values for automatic wrapping
// This way we can ensure no overflow easily
// @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

View File

@ -105,4 +105,107 @@ uint32 hash_ejb(const char* str)
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

130
localization/Language.h Normal file
View 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

View File

@ -6,6 +6,15 @@
#include "Log.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
// e.g. log to display or file
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].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;
}
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)
inline
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) {
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 += adjustment;
debug_container->log_memory.pos += (aligned - ((address + debug_container->log_memory.pos) & (aligned - 1))) % 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) {
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 += adjustment;
debug_container->log_memory.pos += (aligned - ((address + debug_container->log_memory.pos) & (aligned - 1))) % aligned;
}
}

View File

@ -16,9 +16,19 @@
struct DebugContainer {
DebugMemoryContainer dmc;
// Used for logging timings for different sections
TimingStat* perf_stats;
// Required to calculate the "fps"
uint64 performance_count_frequency;
// Used to log memory access (read, write)
LogMemory log_memory;
// Used to log general int values (e.g. counter for draw calls etc.)
int32* counter;
#if _WIN32
HANDLE log_fp;
#endif

View File

@ -54,16 +54,24 @@ struct LogMemory {
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* 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)
// Don't perform any logging at log level 0
#define LOG(str, 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_INCREMENT(a) ((void) 0)
#define LOG_COUNTER(a, b) ((void) 0)
#define RESET_COUNTER(a) ((void) 0)
#else
#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_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
#if DEBUG

View File

@ -20,8 +20,6 @@
#include <x86intrin.h>
#endif
global_persist uint64 performance_count_frequency;
struct TimingStat {
const char* function;
uint64 old_tick_count;
@ -32,6 +30,7 @@ struct TimingStat {
// Sometimes we want to only do logging in debug mode.
// In such cases use the following macro.
#if DEBUG || INTERNAL
void update_timing_stat(uint32, const char*);
#define UPDATE_TIMING_STAT(stat) update_timing_stat(stat, __func__)
#else
#define UPDATE_TIMING_STAT(stat) ((void) 0)

View File

@ -12,6 +12,7 @@
#include <string.h>
#include "../stdlib/Types.h"
#include "../utils/MathUtils.h"
#include "../utils/EndianUtils.h"
#include "../utils/TestUtils.h"
#include "Allocation.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);
buf->alignment = alignment;
buf->element_alignment = 0;
buf->size = 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
void buffer_reset(BufferMemory* buf)
{
@ -88,4 +104,53 @@ byte* buffer_get_memory(BufferMemory* buf, uint64 size, int32 aligned = 0, bool
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

View File

@ -12,6 +12,7 @@
#include <string.h>
#include "../stdlib/Types.h"
#include "../utils/MathUtils.h"
#include "../utils/EndianUtils.h"
#include "Allocation.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);
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->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);
@ -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
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);
}
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

View File

@ -13,6 +13,7 @@
#include "../stdlib/Types.h"
#include "../utils/MathUtils.h"
#include "../utils/EndianUtils.h"
#include "../utils/TestUtils.h"
#include "Allocation.h"
@ -49,6 +50,7 @@ void ring_alloc(RingMemory* ring, uint64 size, int32 alignment = 64)
ring->size = size;
ring->pos = 0;
ring->alignment = alignment;
ring->element_alignment = 0;
ring->start = 0;
ring->end = 0;
@ -56,13 +58,30 @@ void ring_alloc(RingMemory* ring, uint64 size, int32 alignment = 64)
}
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->size = size;
ring->pos = 0;
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->end = 0;
@ -88,9 +107,7 @@ uint64 ring_calculate_position(const RingMemory* ring, uint64 pos, uint64 size,
if (aligned) {
uintptr_t address = (uintptr_t) ring->memory;
int64 adjustment = (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
pos += adjustment;
pos += (aligned - ((address + ring->pos) & (aligned - 1))) % 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) {
uintptr_t address = (uintptr_t) ring->memory;
int64 adjustment = (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
pos += adjustment;
pos += (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
}
}
@ -125,9 +140,7 @@ byte* ring_get_memory(RingMemory* ring, uint64 size, byte aligned = 0, bool zero
if (aligned > 1) {
uintptr_t address = (uintptr_t) ring->memory;
int64 adjustment = (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
ring->pos += adjustment;
ring->pos += (aligned - ((address + ring->pos) & (aligned - 1))) % 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) {
uintptr_t address = (uintptr_t) ring->memory;
int64 adjustment = (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
ring->pos += adjustment;
ring->pos += (aligned - ((address + ring->pos) & (aligned - 1))) % aligned;
}
}
@ -183,4 +194,38 @@ bool ring_commit_safe(const RingMemory* ring, uint64 size, byte aligned = 0)
: 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

View File

@ -75,5 +75,7 @@
#define SETTING_TYPE_UNLIMITED 0x00
#define SETTING_UI_VISIBILITY_FPS 1
#define SETTING_UI_VISIBILITY_DEBUG 2
#define SETTING_UI_VISIBILITY_WIREFRAME 4
#endif

View File

@ -60,13 +60,12 @@ void object_from_file_txt(
uint32 temp_color_count = 0;
while (*pos != '\0') {
while (*pos == ' ') {
while (*pos == ' ' || *pos == '\t' || *pos == '\n') {
++pos;
}
if (*pos == '\n') {
++pos;
continue;
if (*pos == '\0') {
break;
}
// Parse type
@ -373,8 +372,8 @@ int32 object_from_file(
byte* pos = file.content;
// Read version
//mesh->version = *((int32 *) pos);
//pos += sizeof(mesh->version);
mesh->version = *((int32 *) pos);
pos += sizeof(mesh->version);
// Read base data
mesh->vertex_type = *((int32 *) pos);
@ -552,7 +551,11 @@ void object_to_file(
FileBody file;
// 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);
byte* pos = file.content;
@ -940,7 +943,7 @@ void object_to_file(
SWAP_ENDIAN_LITTLE_SIMD(
(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
);

View File

@ -16,8 +16,8 @@ struct WindowState {
uint16 width;
uint16 height;
int32 x;
int32 y;
uint16 x;
uint16 y;
uint64 style;
};
@ -30,8 +30,8 @@ struct Window {
uint16 width;
uint16 height;
int32 x;
int32 y;
uint16 x;
uint16 y;
// 1. position
// 2. focus

View File

@ -74,48 +74,49 @@ struct HashMap {
// WARNING: element_size = element size + remaining HashEntry data size
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->buf.free = (uint64 *) ring_get_memory(ring, CEIL_DIV(count, 64) * sizeof(hm->buf.free));
hm->buf.count = count;
hm->buf.chunk_size = element_size;
hm->buf.last_pos = -1;
hm->buf.alignment = 1;
hm->table = (void **) data;
chunk_init(&hm->buf, data + sizeof(void *) * count, count, element_size, 1);
}
// WARNING: element_size = element size + remaining HashEntry data size
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->buf.free = (uint64 *) buffer_get_memory(buf, CEIL_DIV(count, 64) * sizeof(hm->buf.free));
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
hm->table = (void **) data;
chunk_init(&hm->buf, data + sizeof(void *) * count, count, element_size, 1);
}
// WARNING: element_size = element size + remaining HashEntry data size
void hashmap_create(HashMap* hm, int32 count, int32 element_size, byte* buf)
{
hm->table = (void **) buf;
chunk_init(&hm->buf, buf + sizeof(void *) * count, count, element_size, 1);
}
hm->buf.memory = buf + sizeof(void *) * count;
hm->buf.free = (uint64 *) (hm->buf.memory + count * element_size);
hm->buf.count = count;
hm->buf.chunk_size = element_size;
hm->buf.last_pos = -1;
hm->buf.alignment = 1;
// Calculates how large a hashmap will be
inline
int64 hashmap_size(int count, int32 element_size)
{
return count * sizeof(element_size) // table
+ 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) {
@ -243,6 +244,23 @@ HashEntry* hashmap_get_entry(HashMap* hm, const char* key) {
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) {
uint64 index = hash_djb2(key);
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

View File

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

View File

@ -4,7 +4,7 @@
#include "../stdlib/Types.h"
struct UIAttribute {
// Attributes use ids instead of strings
// Attributes use ids (=type name) instead of strings
int32 attribute_id;
union {
@ -16,11 +16,19 @@ struct UIAttribute {
};
struct UIAttributeGroup {
int32 attribute_count;
int32 attribute_size;
UIAttribute* attributes;
};
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_SIZE,
UI_ATTRIBUTE_TYPE_FONT_WEIGHT,
@ -31,6 +39,14 @@ enum UIAttributeType {
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_IMG,
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_OPACITY,
@ -38,6 +54,7 @@ enum UIAttributeType {
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_H,
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_STYLE,
UI_ATTRIBUTE_TYPE_BORDER_COLOR_INDEX,
UI_ATTRIBUTE_TYPE_BORDER_COLOR,
UI_ATTRIBUTE_TYPE_BORDER_WIDTH,
UI_ATTRIBUTE_TYPE_BORDER_TOP_COLOR,
@ -55,23 +72,43 @@ enum UIAttributeType {
UI_ATTRIBUTE_TYPE_PADDING_BOTTOM,
UI_ATTRIBUTE_TYPE_PADDING_LEFT,
UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR_INDEX,
UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR,
UI_ATTRIBUTE_TYPE_SHADOW_INNER_ANGLE,
UI_ATTRIBUTE_TYPE_SHADOW_INNER_DISTANCE,
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR_INDEX,
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR,
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_ANGLE,
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_DURATION,
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)
{
switch (e) {
case UI_ATTRIBUTE_TYPE_TYPE:
return "type";
case UI_ATTRIBUTE_TYPE_STYLE:
return "style";
case UI_ATTRIBUTE_TYPE_FONT_COLOR:
return "font_color";
case UI_ATTRIBUTE_TYPE_FONT_SIZE:

View File

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

View File

@ -1,10 +1,9 @@
#ifndef TOS_UI_ELEMENT_H
#define TOS_UI_ELEMENT_H
#include "UIElementType.h"
#include "UIAlignment.h"
#include "UIAnchor.h"
#include "../stdlib/Types.h"
#include "UIElementType.h"
#include "../object/Vertex.h"
struct UIElementDimension {
int16 x1;
@ -13,27 +12,37 @@ struct UIElementDimension {
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 {
int id;
const char* name;
int32 id;
UIElementType type;
int window_id;
int panel_id;
int16 window_id;
int16 panel_id;
UIElementDimension dimension;
UIAlignH align_h;
UIAlignV align_v;
UIAnchor anchor;
int32 state_flag;
bool is_visible;
bool is_active;
bool is_focused;
f32 anim_elapsed;
int16 scroll_x;
int16 scroll_y;
// @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

View File

View File

@ -2,22 +2,35 @@
#define TOS_UI_LAYOUT_H
#include "../stdlib/Types.h"
#include "../stdlib/HashMap.h"
#include "UIElement.h"
#include "UILocation.h"
// Modified for every scene
struct UILayout {
int32 ui_deadzone_count = 5;
int32 ui_deadzone_size = 5;
UIElementDimension ui_deadzone[5];
int32 element_hoverable_count;
int32 element_hoverable_size;
int32 element_hoverable_pos;
UIElementDimension* elements_hoverable;
int32 element_interactible_count;
int32 element_interactible_size;
int32 element_interactible_pos;
UIElementDimension* elements_interactible;
int32 element_count;
UIElement* element;
// @question Since we use a hashmap below, do we even need the size?
// 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

View File

View File

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

View File

View File

@ -1,9 +0,0 @@
#ifndef TOS_UI_POSITION_H
#define TOS_UI_POSITION_H
enum UIPosition {
UI_POSITION_RELATIVE,
UI_POSITION_ABSOLUTE
};
#endif

View File

View File

View File

View File

View File

View File

@ -18,35 +18,87 @@
#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;
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
// @todo We should probably create a hashmap
// key = name
// value = pointer to group (in the file we store the offset when we load it into memory convert it to pointer)
// The hashmap contains the offset where the respective style can be found
HashMap hash_map;
};
int32 style_group_count;
UIAttributeGroup* style_groups;
// General theme for all scenes
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;
// @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
void theme_from_file_txt(
RingMemory* ring,
const char* path,
UITheme* theme
UIThemeStyle* theme
) {
FileBody file;
file_read(path, &file, ring);
@ -59,6 +111,39 @@ void theme_from_file_txt(
char attribute_name[32];
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 == ' ' || *pos == '\t') {
++pos;
@ -90,9 +175,21 @@ void theme_from_file_txt(
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
// @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;
@ -115,8 +212,37 @@ void theme_from_file_txt(
ASSERT_SIMPLE((*pos != '\0' && *pos != '\n'));
// Handle different attribute types
UIAttribute attribute;
if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_FONT_COLOR), attribute_name) == 0) {
UIAttribute attribute = {};
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 '#'
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_COLOR;
@ -306,47 +432,131 @@ void theme_from_file_txt(
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] == '#') {
// 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(
theme->styles_global + element_type * UI_ATTRIBUTE_TYPE_SIZE,
temp_group->attributes + temp_group->attribute_size,
&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
// Not every group has all the attributes (most likely only a small subset)
// However, an accurate calculation is probably too slow and not needed most of the time
inline
int64 theme_size(const UIThemeStyle* theme)
{
return hashmap_size(&theme->hash_map)
+ theme->hash_map.buf.count * UI_ATTRIBUTE_TYPE_SIZE * sizeof(UIAttribute);
}
// File layout - binary
// version
// 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,
UITheme* theme
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
// global definitions
// names count
//
}
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(theme->version);
pos += sizeof(theme->version);
void theme_to_file(RingMemory* ring,
const char* path,
const UITheme* theme
) {
// 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

View File

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

View File

@ -16,7 +16,7 @@
#include "../stdlib/Types.h"
constexpr
size_t strlen_compile_time(const char* str) {
size_t strlen_const(const char* str) {
size_t len = 0;
while (str[len] != '\0') {
++len;
@ -259,6 +259,7 @@ str_concat(
*dst = '\0';
}
inline
char* strtok(char* str, const char* __restrict delim, char* *key) {
char* result;
if (str == NULL) {
@ -283,38 +284,42 @@ char* strtok(char* str, const char* __restrict delim, char* *key) {
}
inline
void format_number_render(int32 length, char* buffer, const char thousands = ',')
{
int32 count = (int32) (length / 3) - (length % 3 == 0 ? 1 : 0);
int32 int_to_str(int64 number, char *str, const char thousands = ',') {
int32 i = 0;
int64 sign = number;
int32 digit_count = 0;
int32 j = -1;
for (int32 i = length; i > 0; --i) {
++j;
if (j % 3 == 0 && j != 0) {
buffer[i + count] = buffer[i];
--count;
buffer[i + count] = thousands;
} else {
buffer[i + count] = buffer[i];
if (number == 0) {
str[i++] = '0';
} else if (number < 0) {
number = -number;
}
while (number > 0) {
if (thousands
&& (digit_count == 3 || digit_count == 6 || digit_count == 9 || digit_count == 12 || digit_count == 15)
) {
str[i++] = thousands;
}
}
char* format_number(size_t number, char* buffer, const char thousands = ',')
{
int32 length = snprintf(buffer, 32, "%zu", number);
format_number_render(length, buffer, thousands);
str[i++] = number % 10 + '0';
number /= 10;
++digit_count;
}
return buffer;
}
if (sign < 0) {
str[i++] = '-';
}
char* format_number(int32 number, char* buffer, const char thousands = ',')
{
int32 length = snprintf(buffer, 32, "%i", number);
format_number_render(length, buffer, thousands);
str[i] = '\0';
return buffer;
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)