mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-11 11:18: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?
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
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"
|
||||
|
||||
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
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 "../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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
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 "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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
10
log/Debug.h
10
log/Debug.h
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
160
stdlib/HashMap.h
160
stdlib/HashMap.h
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
292
ui/UITheme.h
292
ui/UITheme.h
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void theme_to_file(RingMemory* ring,
|
||||
const char* path,
|
||||
const UITheme* theme
|
||||
) {
|
||||
|
||||
file.size = pos - file.content;
|
||||
file_write(path, &file);
|
||||
}
|
||||
|
||||
#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"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
char* format_number(size_t number, char* buffer, const char thousands = ',')
|
||||
{
|
||||
int32 length = snprintf(buffer, 32, "%zu", number);
|
||||
format_number_render(length, buffer, thousands);
|
||||
|
||||
return buffer;
|
||||
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(int32 number, char* buffer, const char thousands = ',')
|
||||
{
|
||||
int32 length = snprintf(buffer, 32, "%i", number);
|
||||
format_number_render(length, buffer, thousands);
|
||||
str[i++] = number % 10 + '0';
|
||||
number /= 10;
|
||||
++digit_count;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
if (sign < 0) {
|
||||
str[i++] = '-';
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user