A bit more code cleanup

This commit is contained in:
2021-11-22 20:25:45 -08:00
parent a4198e8396
commit 6c9eb8b685
217 changed files with 206 additions and 184 deletions

104
src/file/asset.c Normal file
View File

@ -0,0 +1,104 @@
/**
* Copyright (c) 2021 Dominic Msters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "asset.h"
size_t assetRawLoad(char *assetName, uint8_t *buffer) {
assetbuffer_t *asset;
size_t length, read;
// Open a buffer.
asset = assetBufferOpen(assetName);
if(asset == NULL) return 0;
// Read the count of bytes in the file
assetBufferEnd(asset);
length = assetBufferGetCurrentPosition(asset);// Get our current position (the end)
// Are we only reading the size?
if(buffer == NULL) {
assetBufferClose(asset);
return length;
}
// Reset to start
assetBufferStart(asset);
// Read and seal the string.
read = assetBufferRead(asset, buffer, length);
assetBufferClose(asset); // Close the buffer.
// Did we read successfully?
if(read < length) return 0;
return read;
}
char * assetStringLoad(char *assetName) {
size_t length;
char *string;
length = assetRawLoad(assetName, NULL);
if(length == 0) return NULL;
string = malloc(length + 1);// +1 for null terminator
if(string == NULL) return NULL;
length = assetRawLoad(assetName, string);
if(length == 0) {
free(string);
return NULL;
}
string[length] = '\0';// Null terminate
return string;
}
assetbuffer_t * assetBufferOpen(char *assetName) {
// Get the directory based on the raw input by creating a new string.
FILE *fptr;
char filename[512];
// Prep filename
filename[0] = '\0';//Start at null
strcat(filename, ASSET_PREFIX);//Add prefix
strcat(filename, assetName);//Add body
printf("Opening up %s\n", filename);
// Open the file pointer now.
fptr = fopen(filename, "rb");
if(fptr == NULL) {
printf("Error opening %s: %s\n", filename, strerror(errno));
return NULL;// File available?
}
return (assetbuffer_t *)fptr;
}
bool assetBufferClose(assetbuffer_t *buffer) {
return fclose((FILE *)buffer);
}
int32_t assetBufferRead(assetbuffer_t *buffer, char *data, size_t size) {
return (int32_t)fread(data, 1, size, (FILE *)buffer);
}
int32_t assetBufferEnd(assetbuffer_t *buffer) {
// return feof((FILE *)buffer);
return fseek((FILE *)buffer, 0, SEEK_END);// Seek to the end
}
int32_t assetBufferStart(assetbuffer_t *buffer) {
return fseek((FILE *)buffer, 0, SEEK_SET);
}
int32_t assetBufferSkip(assetbuffer_t *buffer, long n) {
return fseek((FILE *)buffer, n, SEEK_CUR);
}
size_t assetBufferGetCurrentPosition(assetbuffer_t *buffer) {
return ftell((FILE *)buffer);
}

90
src/file/asset.h Normal file
View File

@ -0,0 +1,90 @@
/**
* Copyright (c) 2021 Dominic Msters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "../libs.h"
#include "../display/shader.h"
#include "../display/texture.h"
#include "../display/font.h"
#if !defined(ASSET_PREFIX)
#error Asset Prefix has not been defined.
#endif
/** Definition of an asset ready to be buffered */
typedef FILE assetbuffer_t;
/**
* Buffer an asset from the file system into memory.
*
* @param assetName Path to the asset to buffer.
* @param buffer Place to buffer the data in to, or NULL to simply read the size
* @return The length (in bytes) of the file.
*/
size_t assetRawLoad(char *assetName, uint8_t *buffer);
/**
* Load a string from an asset into memory.
*
* @param assetName Asset to load
* @return A newly loaded string (malloced into existance.)
*/
char * assetStringLoad(char *assetName);
/**
* Platform-centric method to open a file buffer to an asset.
* @param assetName The asset name to open a buffer for.
* @return Pointer to a buffer, NULL if unsuccessfuil.
*/
assetbuffer_t * assetBufferOpen(char *assetName);
/**
* Closes a previously opened asset buffer.
* @param buffer Buffer to close.
* @return True if successful, otherwise false.
*/
bool assetBufferClose(assetbuffer_t *buffer);
/**
* Read bytes from buffer.
* @param buffer The buffer pointing to an asset.
* @param data Pointer to a ubyte array to buffer data into.
* @param size Length of the data buffer. Represents how many bytes can be read.
* @return The count of bytes read. Complete when less than data array size.
*/
int32_t assetBufferRead(assetbuffer_t *buffer, char *data, size_t size);
/**
* Skip to the end of the buffer, useful to find the length of the buffer.
* @param Buffer The buffer pointing to an asset.
* @return How many bytes were skipped
*/
int32_t assetBufferEnd(assetbuffer_t *buffer);
/**
* Rewinds an asset buffer to the start.
*
* @param buffer Buffer to rewind
* @return 0 if successful, otherwise unsuccessful.
*/
int32_t assetBufferStart(assetbuffer_t *buffer);
/**
* Method to skip n bytes in the buffer
* @param buffer The buffer pointing to an asset.
* @param n Count of bytes to skip.
* @return 0 if successful, otherwise unsuccessful.
*/
int32_t assetBufferSkip(assetbuffer_t *buffer, long n);
/**
* Retreive the current byte position within the asset that the head is at.
*
* @param buffer Buffer to get the position of.
* @return Position (in bytes) that the current seek is at.
*/
size_t assetBufferGetCurrentPosition(assetbuffer_t *buffer);

240
src/file/assetmanager.c Normal file
View File

@ -0,0 +1,240 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assetmanager.h"
assetmanagerloaderdefinition_t ASSET_MANAGER_LOADERS[] = {
{
&_assetManagerLoaderTextureAsync,
&_assetManagerLoaderTextureSync,
&_assetManagerLoaderTextureDispose
},
{
&_assetManagerLoaderFontAsync,
&_assetManagerLoaderFontSync,
&_assetManagerLoaderFontDispose
},
{
&_assetManagerLoaderShaderAsync,
&_assetManagerLoaderShaderSync,
&_assetManagerLoaderShaderDispose
},
{
&_assetManagerLoaderScaledTextureAsync,
&_assetManagerLoaderScaledTextureSync,
&_assetManagerLoaderScaledTextureDispose
}
};
void assetManagerInit(assetmanager_t *manager) {
threadInit(&manager->thread, &_assetManagerThread);
manager->thread.user = manager;
manager->itemCount = 0;
manager->finished = false;
manager->holderCount = 0;
manager->running = false;
}
float assetManagerProgressGet(assetmanager_t *manager) {
float done;
uint8_t i;
assetmanageritem_t *item;
done = 0.0f;
for(i = 0; i < manager->itemCount; i++) {
item = manager->items + i;
done += assetManagerItemIsFinished(
item, ASSET_MANAGER_LOADERS + item->type
) ? 1 : 0;
}
return done / ((float)manager->itemCount);
}
float assetManagerProgressGetForHolder(
assetmanager_t *manager, assetmanagerowner_t hold
) {
float done;
uint8_t i, j, c;
assetmanageritem_t *item;
done = 0.0f;
c = 0x00;
for(i = 0; i < manager->itemCount; i++) {
//Is this held by this holder?
item = manager->items + i;
for(j = 0; j < item->holderCount; j++) {
if(item->holders[j] != hold) continue;
c++;
done += assetManagerItemIsFinished(
item, ASSET_MANAGER_LOADERS + item->type
) ? 1 : 0;
break;
}
}
// Is this thing even holding any assets to begin with?
if(c == 0x00) return 1.0f;
return done / (float)c;
}
assetmanagerowner_t assetManagerHolderCreate(assetmanager_t *man) {
uint8_t i;
// Find first available number.
for(i = 0; i < 0xFF; i++) {
if(arrayFind(
sizeof(assetmanagerowner_t), man->holders, man->holderCount, &i
) == -1) break;
}
man->holders[man->holderCount++] = i;
return i;// No slots left.
}
void assetManagerHolderRelease(assetmanager_t *man, assetmanagerowner_t hold) {
int32_t i;
uint8_t j;
assetmanageritem_t *item;
size_t s;
s = sizeof(assetmanagerowner_t);
i = arrayFind(s, man->holders,man->holderCount,&hold);
if(i == -1) return;
arraySplice(sizeof(uint8_t), man->holders, i, 1, man->holderCount);
man->holderCount--;
for(j = 0; j < man->itemCount; j++) {
item = man->items + j;
i = arrayFind(s, item->holders, item->holderCount, &hold);
if(i == -1) continue;
arraySplice(s, item->holders, i, 1, item->holderCount);
item->holderCount--;
}
}
void assetManagerDisposeReleased(assetmanager_t *man) {
uint8_t i;
assetmanagerloader_t *disp;
assetmanageritem_t *item;
for(i = 0; i < man->itemCount; i++) {
item = man->items + i;
disp = ASSET_MANAGER_LOADERS[item->type].dispose;
if(item->holderCount > 0) continue;
if(disp != NULL) disp(item);
arraySplice(sizeof(assetmanageritem_t), man->items, i, 1, man->itemCount);
man->itemCount--;
}
}
void assetManagerDispose(assetmanager_t *man) {
uint8_t i;
assetmanagerloader_t *disp;
assetmanageritem_t *item;
for(i = 0; i < man->itemCount; i++) {
item = man->items + i;
disp = ASSET_MANAGER_LOADERS[item->type].dispose;
if(item->holderCount > 0) continue;
if(disp != NULL) disp(item);
}
man->itemCount = 0;
man->holderCount = 0;
}
// Thread Management
void assetManagerStart(assetmanager_t *manager) {
if(manager->running) return;
manager->running = true;
threadStart(&manager->thread);
}
int32_t _assetManagerThread(thread_t *thread) {
// TODO: Can I allow multiple threads to run?
uint8_t i;
assetmanager_t *manager;
assetmanageritem_t *item;
assetmanagerloaderdefinition_t *definition;
bool result;
manager = thread->user;
for(i = 0; i < manager->itemCount; i++) {
item = manager->items + i;
definition = ASSET_MANAGER_LOADERS + item->type;
// Only bother with ASYNC items
if(definition->loadAsync == NULL) continue;
// Are we already loading, not ready, or already tried to load?
if(item->state != ASSET_MANAGER_STATE_PENDING) continue;
// Begin loading
item->state = ASSET_MANAGER_STATE_ASYNC_LOADING;
result = definition->loadAsync(item);
// Finish Loading
if(!result) {
item->state = ASSET_MANAGER_STATE_ASYNC_ERROR;
} else {
item->state = ASSET_MANAGER_STATE_ASYNC_DONE;
}
}
manager->finished = assetManagerProgressGet(manager) >= 1.0f;
manager->running = false;
return 0;
}
void assetManagerUpdate(assetmanager_t *manager) {
uint8_t i;
assetmanageritem_t *item;
assetmanagerloaderdefinition_t *definition;
bool result;
// Autostart
if(assetManagerProgressGet(manager) < 1.0f && !manager->running) {
assetManagerStart(manager);
}
for(i = 0; i < manager->itemCount; i++) {
item = manager->items + i;
definition = ASSET_MANAGER_LOADERS + item->type;
// Update not ready state (Synchronously)
if(item->state == ASSET_MANAGER_STATE_NOT_READY) {
item->state = ASSET_MANAGER_STATE_PENDING;
continue;
}
// If requires ASYNC loading, then confirm it has finished loading.
if(definition->loadAsync != NULL) {
if(item->state != ASSET_MANAGER_STATE_ASYNC_DONE) continue;
} else if(item->state != ASSET_MANAGER_STATE_PENDING) {
continue;
}
if(definition->loadSync == NULL) {
continue;
}
// Begin sync loading
item->state = ASSET_MANAGER_STATE_SYNC_LOADING;
result = definition->loadSync(item);
// Finish loading
if(!result) {
item->state = ASSET_MANAGER_STATE_SYNC_ERROR;
} else {
item->state = ASSET_MANAGER_STATE_SYNC_DONE;
}
}
manager->finished = assetManagerProgressGet(manager) >= 1.0f;
}

100
src/file/assetmanager.h Normal file
View File

@ -0,0 +1,100 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "../libs.h"
#include "types/common.h"
#include "loaders/font.h"
#include "loaders/scaledtexture.h"
#include "loaders/shader.h"
#include "loaders/texture.h"
#include "asset.h"
// Constants
extern assetmanagerloaderdefinition_t ASSET_MANAGER_LOADERS[];
/**
* Initialize the asset manager
*
* @param manager Manager to initialize.
*/
void assetManagerInit(assetmanager_t *manager);
/**
* Gets the progress of the asset manager as a representation of 0-1 as a % that
* has loaded.
*
* @param manager Manager to check the state of.
* @return The progress percent as a representation of 0-1
*/
float assetManagerProgressGet(assetmanager_t *manager);
/**
* Gets the progress of the assets for only those held by a specific holder.
*
* @param manager Manager to get the progress from
* @param hold Holder to get the items progress from.
* @return The percentage (0-1) of the loaded assets.
*/
float assetManagerProgressGetForHolder(
assetmanager_t *manager, assetmanagerowner_t hold
);
/**
* Creates a holder for a given asset manager. Asset holders are kept track of
* so that those who requests assets are responsible for those who free them. It
* also ensures only the assets that are actually necessary are kept loaded in
* memory at all times.
*
* @param man Asset manager in question
* @return An asset manager owner ID.
*/
assetmanagerowner_t assetManagerHolderCreate(assetmanager_t *man);
/**
* Release a previously reserved asset manager holder. This will (in turn) cause
* all of the assets that manager was holding to also be released.
*
* @param man Asset manager holder to release for.
* @param hold Holder to release.
*/
void assetManagerHolderRelease(assetmanager_t *man, assetmanagerowner_t hold);
/**
* Disposes all assets that are not currently held (released assets). This can
* only happen from the main thread due to synchronous assets such as Textures.
*
* @param man Manager to dispose.
*/
void assetManagerDisposeReleased(assetmanager_t *man);
/**
* Completely dispose an asset manager. This will also completely dispose all
* of the assets that this asset manager is holding.
*
* @param man Asset manager to dispose
*/
void assetManagerDispose(assetmanager_t *man);
/**
* Begin asynchronously loading all of the assets
*
* @param manager Manager to begin async loading.
*/
void assetManagerStart(assetmanager_t *manager);
/** Private Thread that is executed asynchronously */
int32_t _assetManagerThread(thread_t *thread);
/**
* Synchronously tick the asset manager. Some assets require some form of sync
* operations, such as buffering to the GPU, so make sure this is called at a
* good time for that task, such as right at the end of a frame.
*
* @param manager Manager to synchronously tick.
*/
void assetManagerUpdate(assetmanager_t *manager);

223
src/file/csv.c Normal file
View File

@ -0,0 +1,223 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "csv.h"
csvbufferresult_t csvBuffer(
assetbuffer_t *asset, csvbuffercallback_t *callback, void *user
) {
int32_t cellChar, read, i, currentColumnCount;
char buffer[CSV_BUFFER_SIZE];
char cell[CSV_CELL_SIZE_MAX];
char c;
bool callbackResponse;
bool insideEncapsulation = false;
csvbufferresult_t result = {
.cellCount = 0,
.rowCount = 0,
.columnCount = 0
};
// Init the cell Char Index.
cellChar = 0;
currentColumnCount = 0;
// Begin buffering.
while(true) {
// Read n bytes into our buffer
read = assetBufferRead(asset, buffer, CSV_BUFFER_SIZE);
// Now read back those bytes.
for(i = 0; i < read; i++) {
c = buffer[i];
// Characters we flat out ignore
if(c == '\r') continue;
// Handle quote marks.
if(c == '"') {
if(buffer[i+1] == '"') {// "" means a single quote (double-escaped)
i++;
cell[cellChar++] = c;
} else if(insideEncapsulation) {
insideEncapsulation = false;
} else {
insideEncapsulation = true;
}
continue;
}
// Is the start of a new cell/row?
if(c == '\0' || (!insideEncapsulation && (c == ',' || c == '\n'))) {
cell[cellChar] = '\0';// Terminate Cell string
// Fire off the callback
if(callback != NULL) {
callbackResponse = callback(asset, user, result.rowCount, currentColumnCount, cell);
if(!callbackResponse) return result;
}
// Prepare for next row/cell
currentColumnCount++;
result.columnCount = mathMax(currentColumnCount, result.columnCount);
if(c == '\n') {
result.rowCount++;
currentColumnCount = 0;
}
result.cellCount++;// Only count cells with
cellChar = 0;
continue;// Skip
}
// Add character to the cell.
cell[cellChar++] = c;
}
if(read < CSV_BUFFER_SIZE) break;
}
// If this is an empty row we don't count it, otherwise we do.
if(currentColumnCount != 0) result.rowCount++;
return result;
}
bool _csvBufferRowParserCallback(
assetbuffer_t *asset, void *user, int32_t row, int32_t column, char *data
) {
csvbufferrowdata_t *rowData = (csvbufferrowdata_t *)user;
// Now did we change rows?
if(row != rowData->row) {
// Yes we did, let's buffer the previous row.
if(rowData->callback != NULL) {
if(!rowData->callback(
asset, rowData->user, rowData->row, &rowData->rowCurrent
)) return false;
}
// Begin next row
rowData->row = row;
rowData->rowCurrent.columnCount = 0;
}
// Determine string info for the cell
int32_t length = (int32_t)strlen(data);
int32_t offset = (column * CSV_CELL_SIZE_MAX);
// Now copy the string data to the buffer
arrayCopy(sizeof(char), data, length + 1, rowData->rowCurrent.data + offset);
// Update the pointer to the string
rowData->rowCurrent.columns[column] = rowData->rowCurrent.data + offset;
rowData->rowCurrent.columnCount++;
return true;
}
csvbufferresult_t csvBufferRow(
assetbuffer_t *asset, csvbufferrowcallback_t *callback, void *user
) {
csvbufferrowdata_t data;
csvbufferresult_t result;
data.row = 0;
data.user = user;
data.callback = callback;
data.rowCurrent.columnCount = 0;
// Perform a per-cell buffer and run the parser callback.
result = csvBuffer(asset, &_csvBufferRowParserCallback, &data);
// Because the buffer may not fire for the last row we handle it here.
if(data.rowCurrent.columnCount > 0 && callback != NULL) {
if(!callback(asset, user, data.row, &data.rowCurrent)) return result;
}
return result;
}
bool _csvBufferRowWithHeadersCallback(
assetbuffer_t *asset, void *user, int32_t row, csvrow_t *csv
) {
csvbufferrowwithheadersdata_t *data = (csvbufferrowwithheadersdata_t *)user;
// Take the headers for row 0
if(row == 0) {
csvRowPopulate(csv, &data->headerRow);
return true;
}
// Fire the callback
return data->callback(asset, data->user, row, &data->headerRow, csv);
}
csvbufferresult_t csvBufferRowWithHeaders(
assetbuffer_t *asset, csvbufferrowwitheaderscallback_t *callback, void *user
) {
csvbufferrowwithheadersdata_t data;
data.user = user;
data.callback = callback;
return csvBufferRow(asset, &_csvBufferRowWithHeadersCallback, &data);
}
void csvRowPopulate(csvrow_t *source, csvrow_t *dest) {
int32_t i;
dest->columnCount = source->columnCount;
// Copy the raw characters from the source buffer.
arrayCopy(sizeof(char), source->data, CSV_ROW_CHARACTERS_MAX, dest->data);
// Now update the destination pointers to reference the data buffer.
for(i = 0; i < source->columnCount; i++) {
dest->columns[i] = dest->data + (i * CSV_CELL_SIZE_MAX);
}
}
bool _csvHeadersGetCallback(
assetbuffer_t *asset, void *user, int32_t row, csvrow_t *current
) {
csvrow_t *rowData = (csvrow_t *)user;
csvRowPopulate(current, rowData);
return false;// False to break the loop
}
csvbufferresult_t csvHeadersGet(assetbuffer_t *asset, csvrow_t *row) {
return csvBufferRow(asset, &_csvHeadersGetCallback, row);
}
int32_t csvColumnGetIndex(csvrow_t *row, char *key) {
return arrayFindString(row->columns, row->columnCount, key);
}
bool _csvRowSearchCallback(
assetbuffer_t *asset, void *user, int32_t row, csvrow_t *csv
) {
csvsearchdata_t *data = (csvsearchdata_t *)user;
// Does the search match?
if(strcmp(csv->columns[data->column], data->value) != 0) return true;
// Matched, copy and end.
csvRowPopulate(csv, data->row);
data->rowIndex = row;
return false;
}
int32_t csvRowSearch(
assetbuffer_t *asset, csvrow_t *row, int32_t column, char *value
) {
csvsearchdata_t data = {
.column = column,
.row = row,
.rowIndex = -1,
.value = value
};
csvBufferRow(asset, &_csvRowSearchCallback, &data);
return data.rowIndex;
}

191
src/file/csv.h Normal file
View File

@ -0,0 +1,191 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "../libs.h"
#include "asset.h"
#include "../util/array.h"
/** Maximum characters that a cell can support */
#define CSV_BUFFER_SIZE 32
/** Maximum characters in any given cell */
#define CSV_CELL_SIZE_MAX 1024
/** Maximum number of columns/cells in a given row */
#define CSV_ROW_COLUMNS_MAX 16
/** Count of characters maximum that a row can support */
#define CSV_ROW_CHARACTERS_MAX CSV_CELL_SIZE_MAX * CSV_ROW_COLUMNS_MAX
/** Result of a CSV buffer operation. */
typedef struct {
/** How many rows within the CSV */
int32_t rowCount;
/** Count of columns in the CSV, this is the longest row in the CSV. */
int32_t columnCount;
/** How many cells within the CSV */
int32_t cellCount;
} csvbufferresult_t;
/** Callback to receive data for each cell in a CSV being buffered */
typedef bool csvbuffercallback_t(
assetbuffer_t *asset, void *user, int32_t row, int32_t column, char *data
);
/** Representation of a CSV Row's complete data. */
typedef struct {
/** Characters within the row */
char data[CSV_ROW_CHARACTERS_MAX];
/** Pointer to the start of each string within the row */
char *columns[CSV_ROW_COLUMNS_MAX];
/** How many columns within the row */
int32_t columnCount;
} csvrow_t;
/** Callback to receive buffer data for a CSV row */
typedef bool csvbufferrowcallback_t(
assetbuffer_t *asset, void *user, int32_t row, csvrow_t *csv
);
/** Callback to receive buffer data for a CSV row, but includes CSV headers. */
typedef bool csvbufferrowwitheaderscallback_t(
assetbuffer_t *asset, void *user, int32_t row,
csvrow_t *header, csvrow_t *current
);
/** Data used by the cell callback for when the row buffering is progressing */
typedef struct {
/** Which row the current buffer is on */
int32_t row;
/** Information about the current row being parsed */
csvrow_t rowCurrent;
/** Pointer to custom user data */
void *user;
/** Pointer to custom user callback */
csvbufferrowcallback_t *callback;
} csvbufferrowdata_t;
/** Data used by the row callback for when the header row is parsed */
typedef struct {
/** Information about the header row */
csvrow_t headerRow;
/** Pointer to custom user data */
void *user;
/** Pointer to custom user callback */
csvbufferrowwitheaderscallback_t *callback;
} csvbufferrowwithheadersdata_t;
/** Data used while searching a CSV */
typedef struct {
/** Row to store the data in */
csvrow_t *row;
/** Row's index */
int32_t rowIndex;
/** Column to check */
int32_t column;
/** Value to check row */
char *value;
} csvsearchdata_t;
/**
* Buffer each cell within a CSV Asset Buffer with a callback.
*
* @param asset Asset to buffer the CSV from.
* @param callback Callback to fire for each cell parsed.
* @param user Pointer to any custom user data.
* @return The result of the CSV Buffer Operation.
*/
csvbufferresult_t csvBuffer(
assetbuffer_t *asset, csvbuffercallback_t *callback, void *user
);
/** Callback for when the CSV Row Buffer is parsing a row */
bool _csvBufferRowParserCallback(
assetbuffer_t *asset, void *user, int32_t row, int32_t column, char *data
);
/**
* Buffer each row within a CSV Asset Buffer with a callback.
*
* @param asset Asset to buffer the CSV from.
* @param callback Callback to fire when a row is parsed.
* @param user Pointer to any custom user data.
* @return The result of the CSV Buffer Operation.
*/
csvbufferresult_t csvBufferRow(
assetbuffer_t *asset, csvbufferrowcallback_t *callback, void *user
);
/** Parser that the header parser buffer uses. */
bool _csvBufferRowWithHeadersCallback(
assetbuffer_t *asset, void *user, int32_t row, csvrow_t *csv
);
/**
* Buffer each row within a CSV Asset Buffer with a callback. This will also
* provide the callback with the header row (The first row), already parsed.
*
* @param asset
* @param callback
* @param user
* @return csvbufferresult_t
*/
csvbufferresult_t csvBufferRowWithHeaders(
assetbuffer_t *asset, csvbufferrowwitheaderscallback_t *callback, void *user
);
/**
* Essentially a CSV Row Cloner, copies data from one CSV row to another and
* updates the char pointers for you.
*
* @param source The source CSV row to copy from.
* @param dest The destination CSV row to copy to.
*/
void csvRowPopulate(csvrow_t *source, csvrow_t *dest);
/** Callback used to parse and get the headers row */
bool _csvHeadersGetCallback(
assetbuffer_t *asset, void *user, int32_t row, csvrow_t *current
);
/**
* Parses just the headers row from a CSV and stores the output into the
* provided buffers.
*
* @param asset Asset to get the headers from.
* @param row The CSV Row to store the headers data in.
* @return The result of the buffer operation.
*/
csvbufferresult_t csvHeadersGet(assetbuffer_t *asset, csvrow_t *row);
/**
* Gets the column index within the CSV Row for the specific key.
* @param row Row to get from.
* @param key Key to search for
* @return The column index for the key, or -1 if not found.
*/
int32_t csvColumnGetIndex(csvrow_t *row, char *key);
/** Callback to use while scanning the CSV to find the matching row */
bool _csvRowSearchCallback(
assetbuffer_t *asset, void *user, int32_t row, csvrow_t *csv
);
/**
* Search the CSV for a matching row. Will return data only if the given column
* matches the given value.
*
* @param asset Asset buffer to search.
* @param row Where the returned row will be stored.
* @param column Column to check.
* @param value Value to check in that column
* @return The row index that the result was found on, or -1 if not found.
*/
int32_t csvRowSearch(
assetbuffer_t *asset, csvrow_t *row, int32_t column, char *value
);

39
src/file/loaders/font.c Normal file
View File

@ -0,0 +1,39 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "font.h"
assetmanageritem_t * assetManagerLoadFont(
assetmanager_t *manager, assetmanagerowner_t owner, char *fileName
) {
assetmanageritem_t *item;
item = assetManagerItemGet(manager, fileName);
if(item == NULL) {
item = assetManagerItemAdd(manager, fileName);
item->type = ASSET_MANAGER_TYPE_FONT;
item->data.font.fileName = fileName;
}
assetManagerItemGetOrAddHolder(item, owner);
return item;
}
bool _assetManagerLoaderFontAsync(assetmanageritem_t *item) {
item->data.font.data = assetStringLoad(item->data.font.fileName);
return item->data.font.data != NULL;
}
bool _assetManagerLoaderFontSync(assetmanageritem_t *item) {
fontInit(&item->data.font.font, item->data.font.data);
free(item->data.font.data);
return true;
}
bool _assetManagerLoaderFontDispose(assetmanageritem_t *item) {
fontDispose(&item->data.font.font);
return true;
}

27
src/file/loaders/font.h Normal file
View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "item.h"
#include "../asset.h"
/**
* Queue a font load onto the asset manager buffer.
*
* @param manager Manager to queue on to.
* @param owner Owner ID requesting to load this resource.
* @param font Font to push the result in to.
* @param fileName Filename of the asset to load.
* @return A pointer to the asset manager item for tracking.
*/
assetmanageritem_t * assetManagerLoadFont(
assetmanager_t *manager, assetmanagerowner_t owner, char *fileName
);
bool _assetManagerLoaderFontAsync(assetmanageritem_t *item);
bool _assetManagerLoaderFontSync(assetmanageritem_t *item);
bool _assetManagerLoaderFontDispose(assetmanageritem_t *item);

57
src/file/loaders/item.c Normal file
View File

@ -0,0 +1,57 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "item.h"
assetmanageritem_t * assetManagerItemGet(assetmanager_t *man, char *key) {
uint8_t i;
assetmanageritem_t *item;
for(i = 0; i < man->itemCount; i++) {
item = man->items + i;
if(strcmp(item->key, key) == 0) return item;
}
return NULL;
}
assetmanageritem_t * assetManagerItemAdd(assetmanager_t *manager, char *key) {
// Check if key already exists.
assetmanageritem_t *item = manager->items + manager->itemCount++;
item->state = ASSET_MANAGER_STATE_NOT_READY;
memcpy(item->key, key, strlen(key) + 1);
item->holderCount = 0x00;
return item;
}
uint8_t assetManagerItemGetOrAddHolder(
assetmanageritem_t *item, assetmanagerowner_t owner
) {
uint8_t i, firstEmpty;
firstEmpty = 0xFF;
for(i = 0; i < item->holderCount; i++) {
if(item->holders[i] == owner) return i;
if(firstEmpty == 0xFF && item->holders[i] == 0xFF) {
firstEmpty = i;
}
}
if(firstEmpty == 0xFF) firstEmpty = item->holderCount++;
item->holders[firstEmpty] = owner;
return firstEmpty;
}
bool assetManagerItemIsFinished(assetmanageritem_t *item, assetmanagerloaderdefinition_t *def) {
// Sync done is always done
if(item->state == ASSET_MANAGER_STATE_SYNC_DONE) return true;
// Only check if ASYNC is done.
if(item->state != ASSET_MANAGER_STATE_ASYNC_DONE) return false;
// Does it need to still sync load?
if(def->loadSync == NULL) return true;
return false;
}

49
src/file/loaders/item.h Normal file
View File

@ -0,0 +1,49 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "../types/common.h"
/**
* Retreive an exisitng asset manager item by its key.
*
* @param man Manager to get from
* @param key Key to search for.
* @return The matching asset manager item, or NULL if not found.
*/
assetmanageritem_t * assetManagerItemGet(assetmanager_t *man, char *key);
/**
* Private method, simply adds an item to the manager and resets the state.
*
* @param manager Manager to add to.
* @param key Key to use when adding.
* @return The added and reset item.
*/
assetmanageritem_t * assetManagerItemAdd(assetmanager_t *manager, char *key);
/**
* Add or get the index that a given holder has as a manager item.
*
* @param i Asset Item to check.
* @param o Owner to get/add.
* @return The index within the item that the owner is at.
*/
uint8_t assetManagerItemGetOrAddHolder(
assetmanageritem_t *i, assetmanagerowner_t o
);
/**
* Checks if a given asset item is finished or not.
*
* @param item Item to check.
* @param def Item type definition used for loading.
* @return True if finished, otherwise false.
*/
bool assetManagerItemIsFinished(
assetmanageritem_t *item, assetmanagerloaderdefinition_t *def
);

View File

@ -0,0 +1,123 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "scaledtexture.h"
assetmanageritem_t * assetManagerLoadScaledTexture(
assetmanager_t *manager, assetmanagerowner_t owner,
char *path, char *file, uint8_t scale
) {
assetmanageritem_t *item;
texturescale_t *st;
char buffer[ASSET_MANAGER_ITEM_NAME_MAX];
sprintf(buffer, "%s/%s_%u", path, file, scale);
item = assetManagerItemGet(manager, buffer);
if(item == NULL) {
item = assetManagerItemAdd(manager, buffer);
item->type = ASSET_MANAGER_TYPE_SCALED_TEXTURE;
item->data.scaledTexture.scale = scale;
st = &item->data.scaledTexture.textureScale;
st->scaleCount = 0;
st->baseWidth = 0;
st->baseHeight = 0;
st->path = path;
st->file = file;
}
assetManagerItemGetOrAddHolder(item, owner);
return item;
}
bool _assetManagerLoaderScaledTextureAsync(assetmanageritem_t *item) {
char buffer[128];
char *xmlData;
xml_t xml;
xml_t *child;
int16_t i, j;
texturescale_t *st;
texturescalescale_t *sts;
size_t length;
// TODO: This can be improved if we allow both asset dependencies and
// dependency sibling adding
st = &item->data.scaledTexture.textureScale;
// Begin loading texture XML
sprintf(buffer, "%s/%s.xml", st->path, st->file);
xmlData = assetStringLoad(buffer);
if(xmlData == NULL) return false;
xmlLoad(&xml, xmlData);
free(xmlData);
// Parse root texture info
i = xmlGetAttributeByName(&xml, "channels");
st->channels = (uint8_t)atoi(xml.attributeDatas[i]);
i = xmlGetAttributeByName(&xml, "width");
st->baseWidth = (int16_t)atoi(xml.attributeDatas[i]);
i = xmlGetAttributeByName(&xml, "height");
st->baseHeight = (int16_t)atoi(xml.attributeDatas[i]);
for(j = 0; j < xml.childrenCount; j++) {
child = xml.children + j;
i = xmlGetAttributeByName(child, "scale");
st->scales[st->scaleCount].scale = (uint8_t)atoi(child->attributeDatas[i]);
i = xmlGetAttributeByName(child, "width");
st->scales[st->scaleCount].width = (int16_t)atoi(child->attributeDatas[i]);
i = xmlGetAttributeByName(child, "height");
st->scales[st->scaleCount].height = (int16_t)atoi(child->attributeDatas[i]);
st->scaleCount++;
}
// Cleanup XML
xmlDispose(&xml);
// Get the scale
sts = st->scales + item->data.scaledTexture.scale;
// Get filename
sprintf(buffer, "%s/%s_%i.texture", st->path, st->file, sts->scale);
// Create some space
length = assetRawLoad(buffer, NULL);
if(length == 0) return false;
item->data.scaledTexture.data = malloc(sizeof(pixel_t) * length);
// Load
length = assetRawLoad(buffer, (uint8_t *)item->data.scaledTexture.data);
if(length == 0) {
free(item->data.scaledTexture.data);
return false;
}
return true;
}
bool _assetManagerLoaderScaledTextureSync(assetmanageritem_t *item) {
texturescale_t *st;
texturescalescale_t *sts;
st = &item->data.scaledTexture.textureScale;
sts = st->scales + item->data.scaledTexture.scale;
textureInit(
&item->data.scaledTexture.texture,
sts->width, sts->height,
item->data.scaledTexture.data
);
free(item->data.scaledTexture.data);
return true;
}
bool _assetManagerLoaderScaledTextureDispose(assetmanageritem_t *item) {
textureDispose(&item->data.scaledTexture.texture);
return true;
}

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "item.h"
#include "../xml.h"
#include "../asset.h"
/**
* Load the given texture scale for a scaled texture.
*
* @param manager Manager to queue on to.
* @param owner Owner ID requesting to load this resource.
* @param path Path of the texture size sets
* @param file Name of the texture that was generated.
* @param scale Scale to load.
* @return A pointer to the asset manager item for tracking.
*/
assetmanageritem_t * assetManagerLoadScaledTexture(
assetmanager_t *manager, assetmanagerowner_t owner,
char *path, char *file, uint8_t scale
);
bool _assetManagerLoaderScaledTextureAsync(assetmanageritem_t *item);
bool _assetManagerLoaderScaledTextureSync(assetmanageritem_t *item);
bool _assetManagerLoaderScaledTextureDispose(assetmanageritem_t *item);

55
src/file/loaders/shader.c Normal file
View File

@ -0,0 +1,55 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "shader.h"
assetmanageritem_t * assetManagerLoadShader(
assetmanager_t *manager, assetmanagerowner_t owner,
char *fileVert, char *fileFrag
) {
assetmanageritem_t *item;
char buffer[ASSET_MANAGER_ITEM_NAME_MAX];
sprintf(buffer, "%s/%s", fileVert, fileFrag);
item = assetManagerItemGet(manager, buffer);
if(item == NULL) {
item = assetManagerItemAdd(manager, buffer);
item->type = ASSET_MANAGER_TYPE_SHADER;
item->data.shader.fileVert = fileVert;
item->data.shader.fileFrag = fileFrag;
}
assetManagerItemGetOrAddHolder(item, owner);
return item;
}
bool _assetManagerLoaderShaderAsync(assetmanageritem_t *item) {
item->data.shader.dataVert = assetStringLoad(item->data.shader.fileVert);
if(item->data.shader.dataVert == NULL) return false;
item->data.shader.dataFrag = assetStringLoad(item->data.shader.fileFrag);
if(item->data.shader.dataFrag == NULL) {
free(item->data.shader.fileVert);
return false;
}
return true;
}
bool _assetManagerLoaderShaderSync(assetmanageritem_t *item) {
shaderInit(
&item->data.shader.shader,
item->data.shader.dataVert,
item->data.shader.dataFrag
);
free(item->data.shader.dataFrag);
free(item->data.shader.dataVert);
return true;
}
bool _assetManagerLoaderShaderDispose(assetmanageritem_t *item) {
shaderDispose(&item->data.shader.shader);
return true;
}

29
src/file/loaders/shader.h Normal file
View File

@ -0,0 +1,29 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "item.h"
#include "../asset.h"
/**
* Queues a shader load onto the asset manager buffer.
*
* @param manager Manager to queue on to.
* @param owner Owner ID requesting to load this resource.
* @param shader Shader to push the result in to.
* @param fileVert Vertex file in question to load.
* @param fileFrag Fragment file in question to load.
* @return A pointer to the asset manager item for tracking.
*/
assetmanageritem_t * assetManagerLoadShader(
assetmanager_t *manager, assetmanagerowner_t owner,
char *fileVert, char *fileFrag
);
bool _assetManagerLoaderShaderAsync(assetmanageritem_t *item);
bool _assetManagerLoaderShaderSync(assetmanageritem_t *item);
bool _assetManagerLoaderShaderDispose(assetmanageritem_t *item);

View File

@ -0,0 +1,69 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "texture.h"
assetmanageritem_t * assetManagerLoadTexture(
assetmanager_t *manager, assetmanagerowner_t owner, char *fileName
) {
assetmanageritem_t *item;
item = assetManagerItemGet(manager, fileName);
if(item == NULL) {
item = assetManagerItemAdd(manager, fileName);
item->type = ASSET_MANAGER_TYPE_TEXTURE;
item->data.texture.fileName = fileName;
}
assetManagerItemGetOrAddHolder(item, owner);
return item;
}
bool _assetManagerLoaderTextureAsync(assetmanageritem_t *item) {
assetbuffer_t *buffer;
int channels;
stbi_io_callbacks OPENGL_STBI_CALLBACKS;
buffer = assetBufferOpen(item->data.texture.fileName);
if(buffer == NULL) return false;
// Setup the interface for STBI
OPENGL_STBI_CALLBACKS.read = &assetBufferRead;
OPENGL_STBI_CALLBACKS.skip = &assetBufferSkip;
OPENGL_STBI_CALLBACKS.eof = &assetBufferEnd;
// Buffer the image
channels = 0;
item->data.texture.data = (pixel_t *)stbi_load_from_callbacks(
&OPENGL_STBI_CALLBACKS, buffer,
&item->data.texture.width, &item->data.texture.height,
&channels, STBI_rgb_alpha
);
// Close the buffer
assetBufferClose(buffer);
if(item->data.texture.data == NULL) return false;
return true;
}
bool _assetManagerLoaderTextureSync(assetmanageritem_t *item) {
// Turn into a texture.
textureInit(
&item->data.texture.texture,
item->data.texture.width,
item->data.texture.height,
item->data.texture.data
);
// Cleanup
stbi_image_free(item->data.texture.data);
return true;
}
bool _assetManagerLoaderTextureDispose(assetmanageritem_t *item) {
textureDispose(&item->data.texture.texture);
return true;
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "item.h"
#include "../asset.h"
/**
* Queue a texture load onto the asset manager buffer.
*
* @param manager Manager to queue on to.
* @param owner Owner ID requesting to load this resource.
* @param texture Texture to push the result in to.
* @param fileName Texture filename to load.
* @return A pointer to the asset manager item for tracking.
*/
assetmanageritem_t * assetManagerLoadTexture(
assetmanager_t *manager, assetmanagerowner_t owner, char *fileName
);
bool _assetManagerLoaderTextureAsync(assetmanageritem_t *item);
bool _assetManagerLoaderTextureSync(assetmanageritem_t *item);
bool _assetManagerLoaderTextureDispose(assetmanageritem_t *item);

80
src/file/types/common.h Normal file
View File

@ -0,0 +1,80 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "../../libs.h"
#include "../../engine/thread.h"
#include "texture.h"
#include "font.h"
#include "shader.h"
#include "scaledtexture.h"
#define ASSET_MANAGER_ITEMS_MAX 64
#define ASSET_MANAGER_ITEM_NAME_MAX 96
#define ASSET_MANAGER_HOLDERS_MAX 8
/** States */
#define ASSET_MANAGER_STATE_NOT_READY 0x00
#define ASSET_MANAGER_STATE_PENDING 0x01
#define ASSET_MANAGER_STATE_ASYNC_LOADING 0x02
#define ASSET_MANAGER_STATE_ASYNC_ERROR 0x03
#define ASSET_MANAGER_STATE_ASYNC_DONE 0x04
#define ASSET_MANAGER_STATE_SYNC_LOADING 0x05
#define ASSET_MANAGER_STATE_SYNC_ERROR 0x06
#define ASSET_MANAGER_STATE_SYNC_DONE 0x07
/** Type IDs */
#define ASSET_MANAGER_TYPE_TEXTURE 0x00
#define ASSET_MANAGER_TYPE_FONT 0x01
#define ASSET_MANAGER_TYPE_SHADER 0x02
#define ASSET_MANAGER_TYPE_SCALED_TEXTURE 0x03
// Owner
typedef uint8_t assetmanagerowner_t;
// Union of all the manager types
typedef union {
assetmanagertexture_t texture;
assetmanagershader_t shader;
assetmanagerfont_t font;
assetmanagerscaledtexture_t scaledTexture;
} assetmanagerassetdata_t;
// Item Type
typedef struct {
uint8_t type;
uint8_t state;
char key[ASSET_MANAGER_ITEM_NAME_MAX];
assetmanagerassetdata_t data;
assetmanagerowner_t holders[ASSET_MANAGER_HOLDERS_MAX];
uint8_t holderCount;
} assetmanageritem_t;
// Loader
typedef bool assetmanagerloader_t(assetmanageritem_t *item);
// Loader Definition
typedef struct {
assetmanagerloader_t *loadAsync;
assetmanagerloader_t *loadSync;
assetmanagerloader_t *dispose;
} assetmanagerloaderdefinition_t;
// Manager
typedef struct {
thread_t thread;
bool finished;
bool running;
assetmanageritem_t items[ASSET_MANAGER_ITEMS_MAX];
uint8_t itemCount;
assetmanagerowner_t holders[ASSET_MANAGER_HOLDERS_MAX];
uint8_t holderCount;
} assetmanager_t;

15
src/file/types/font.h Normal file
View File

@ -0,0 +1,15 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "../../display/font.h"
typedef struct {
font_t font;
char *fileName;
char *data;
} assetmanagerfont_t;

View File

@ -0,0 +1,18 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "../../libs.h"
#include "../../display/texture.h"
#include "../../display/texturescale.h"
typedef struct {
texturescale_t textureScale;
texture_t texture;
uint8_t scale;
pixel_t *data;
} assetmanagerscaledtexture_t;

16
src/file/types/shader.h Normal file
View File

@ -0,0 +1,16 @@
// Copyright (c) 2021 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "../../libs.h"
#include "../../display/shader.h"
typedef struct {
shader_t shader;
char *fileVert;
char *fileFrag;
char *dataVert;
char *dataFrag;
} assetmanagershader_t;

15
src/file/types/texture.h Normal file
View File

@ -0,0 +1,15 @@
// Copyright (c) 2021 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "../../libs.h"
#include "../../display/texture.h"
typedef struct {
texture_t texture;
char *fileName;
int32_t width, height;
pixel_t *data;
} assetmanagertexture_t;

210
src/file/xml.c Normal file
View File

@ -0,0 +1,210 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "xml.h"
int32_t xmlLoadChild(xml_t *xml, char *data, int32_t i) {
char c;
int32_t level = 0;
uint8_t doing = XML_DOING_NOTHING;
bool insideTag = false;
char* buffer = malloc(sizeof(char) * XML_TEXT_BUFFER_MAX);
int32_t bufferLength = 0;
xml->value = NULL;
xml->attributeCount = 0;
xml->children = malloc(sizeof(xml_t) * XML_CHILD_COUNT_MAX);
xml->childrenCount = 0;
while(c = data[i++]) {
switch(doing) {
case XML_DOING_NOTHING:
// Look for either an opening tag (<) or a word for a value.
if(c == '>') continue;
if(c == '<') {
if(insideTag) {
i = xmlLoadChild(xml->children + xml->childrenCount++, data, i-1);
doing = XML_PARSING_CHILD;
} else {
doing = XML_PARSING_TAG_NAME;
level++;
insideTag = true;
}
continue;
}
if(xmlIsWhitespace(c)) continue;
doing = XML_PARSING_VALUE;
buffer[bufferLength++] = c;
break;
case XML_PARSING_TAG_NAME:
// Just keep reading until we either hit a space (end of the tag name)
// or a closing tag value, either / or >
if(xmlIsWhitespace(c) || c == '>' || c == '/') {
buffer[bufferLength] = '\0';
xml->node = buffer;
buffer = malloc(sizeof(char) * XML_TEXT_BUFFER_MAX);
bufferLength = 0;
if(c == '/') {
level--;
insideTag = false;
doing = XML_PARSING_CLOSE;
} else {
doing = c == '>' ? XML_DOING_NOTHING : XML_LOOKING_FOR_ATTRIBUTE;
}
continue;
}
buffer[bufferLength++] = c;
break;
case XML_LOOKING_FOR_ATTRIBUTE:
// Look until we hit either the end of a tag, or the attribute itself
if(xmlIsWhitespace(c) || c == '>' || c == '/' || c == '=') {
if(c == '>' || c == '/') {
doing = XML_DOING_NOTHING;
if(c == '/') {
level--;
insideTag = false;
doing = XML_PARSING_CLOSE;
}
} else if(c == '=') {
doing = XML_LOOKING_FOR_ATTRIBUTE_VALUE;
} else {
doing = XML_LOOKING_FOR_ATTRIBUTE;
}
if(bufferLength > 0) {
buffer[bufferLength] = '\0';
xml->attributeNames[xml->attributeCount++] = buffer;
xml->attributeDatas[xml->attributeCount] = NULL;
buffer = malloc(sizeof(char) * XML_TEXT_BUFFER_MAX);
bufferLength = 0;
}
continue;
}
buffer[bufferLength++] = c;
break;
case XML_LOOKING_FOR_ATTRIBUTE_VALUE:
// Keep looking until we find a quote mark
if(xmlIsWhitespace(c)) continue;
if(c == '>' || c == '/') {
doing = XML_DOING_NOTHING;
insideTag = false;
continue;
}
if(c != '"') continue;
doing = XML_PARSING_ATTRIBUTE_VALUE;
break;
case XML_PARSING_ATTRIBUTE_VALUE:
// Parse the attribute value until we find a quote mark.
if(c == '"') {
doing = XML_LOOKING_FOR_ATTRIBUTE;
buffer[bufferLength] = '\0';
xml->attributeDatas[xml->attributeCount - 1] = buffer;
buffer = malloc(sizeof(char) * XML_TEXT_BUFFER_MAX);
bufferLength = 0;
continue;
}
buffer[bufferLength++] = c;
break;
case XML_PARSING_VALUE:
// Keep parsing child until we find a < for an opening/closing tag.
if(c == '<') {
// In HTML Spec there could be a child here but not in XML spec.
doing = XML_PARSING_CLOSE;
buffer[bufferLength] = '\0';
bufferLength = 0;
xml->value = buffer;
buffer = malloc(sizeof(char) * XML_TEXT_BUFFER_MAX);
continue;
}
buffer[bufferLength++] = c;
break;
case XML_PARSING_CHILD:
if(c == '<') {
// Read ahead and confirm this is a close or not
if(data[i] == '/') {
doing = XML_PARSING_CLOSE;
continue;
}
// Likely another child.
i = xmlLoadChild(xml->children + xml->childrenCount++, data, i-1);
}
if(xmlIsWhitespace(c)) continue;
// In HTML Spec there's a chance for there to be a value here, but not
// in the XML spec.
break;
case XML_PARSING_CLOSE:
// Just keep parsing until the tag closer finishes.
if(c != '>') continue;
doing = XML_DOING_NOTHING;
//TODO: Return index or something?
free(buffer);
return i;
default:
break;
}
}
free(buffer);
return i;
}
void xmlLoad(xml_t *xml, char *data) {
xmlLoadChild(xml, data, 0);
}
void xmlDispose(xml_t *xml) {
uint8_t i;
// Dispose children recursively
for(i = 0; i < xml->childrenCount; i++) {
xmlDispose(xml->children + i);
}
// Free children array.
free(xml->children);
// Dispose attributes
for(i = 0; i < xml->attributeCount; i++) {
free(xml->attributeNames[i]);
if((xml->attributeDatas + i) != NULL) {
free(xml->attributeDatas[i]);
}
}
free(xml->node);
if(xml-> value != NULL) free(xml->value);
}
int16_t xmlGetAttributeByName(xml_t *xml, char *name) {
int16_t i;
for(i = 0; i < xml->attributeCount; i++) {
if(strcmp(xml->attributeNames[i], name) == 0) return i;
}
return -1;
}
bool xmlIsWhitespace(char c) {
return c == ' ' || c == '\r' || c == '\n' || c == '\t';
}

66
src/file/xml.h Normal file
View File

@ -0,0 +1,66 @@
/**
* Copyright (c) 2021 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "../libs.h"
#define XML_DOING_NOTHING 0x00
#define XML_PARSING_TAG_NAME 0x01
#define XML_LOOKING_FOR_ATTRIBUTE 0x02
#define XML_PARSING_ATTRIBUTE_NAME 0x03
#define XML_LOOKING_FOR_ATTRIBUTE_VALUE 0x04
#define XML_PARSING_ATTRIBUTE_VALUE 0x05
#define XML_PARSING_VALUE 0x06
#define XML_PARSING_CHILD 0x07
#define XML_PARSING_CLOSE 0x08
#define XML_TEXT_BUFFER_MAX 256
#define XML_CHILD_COUNT_MAX 16
#define XML_ATTRIBUTE_MAX 16
typedef struct _xml_t xml_t;
typedef struct _xml_t {
char *node;
char *value;
char *attributeNames[XML_ATTRIBUTE_MAX];
char *attributeDatas[XML_ATTRIBUTE_MAX];
uint8_t attributeCount;
xml_t *children;
uint8_t childrenCount;
} xml_t;
/**
* Load an XML child from a string buffer.
*
* @param xml XML to load.
* @param data Data to parse
* @param i Character index within the data
* @return The index in the data string this XML node ends.
*/
int32_t xmlLoadChild(xml_t *xml, char *data, int32_t i);
/**
* Load an XML String into an XML memory.
*
* @param xml XML to load into.
* @param data XML string.
*/
void xmlLoad(xml_t *xml, char *data);
/**
* Dispose a previously loaded XML.
*
* @param xml XML to dispose.
*/
void xmlDispose(xml_t *xml);
int16_t xmlGetAttributeByName(xml_t *xml, char *name);
bool xmlIsWhitespace(char c);