Converted tooling to C.

This commit is contained in:
2021-11-07 23:34:05 -08:00
parent a2a839cbac
commit a6bae07e38
18 changed files with 637 additions and 394 deletions

View File

@ -1,12 +0,0 @@
const args = process.argv.splice(2).reduce((x,y) => {
const bits = y.split('=');
const name = bits[0].replace('--', '');
const val = bits[1];
x[name] = val;
return x;
}, {});
module.exports = {
args
};

View File

@ -10,4 +10,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdbool.h>
#include <string.h>
#include <stb_image.h>
#include <stb_image_resize.h>
#include <stb_image_write.h>

View File

@ -55,4 +55,67 @@ void assetReadString(FILE *file, char *buffer) {
length = ftell(file);// Get our current position (the end)
fseek(file, 0, SEEK_SET);// Reset the seek
fread(buffer, 1, length, file);// Read all the bytes
}
void fileGetDirectory(char *file, char* buffer) {
char *c, *p;
int32_t i;
p = strrchr(file, FILE_PATH_SEP);
c = file;
i = 0;
do {
buffer[i++] = *c;
} while(++c < p);
buffer[i] = '\0';
}
bool fileListChildren(
char *directory,
char *buffer,
int32_t *count,
uint8_t *types,
char **children
) {
#if defined(_MSC_VER)
WIN32_FIND_DATA fdFile;
HANDLE hFind = NULL;
char sPath[2048];
int32_t i;
// Append wildcard
sprintf(sPath, "%s\\*.*", directory);
// Scan first
if((hFind = FindFirstFile(sPath, &fdFile)) == INVALID_HANDLE_VALUE) {
printf("Path not found: [%s]\n", directory);
return false;
}
// Iterate
i = 0;
do {
if(
strcmp(fdFile.cFileName, ".") == 0 ||
strcmp(fdFile.cFileName, "..") == 0
) continue;
// Get Full path.
sprintf(sPath, "%s\\%s", directory, fdFile.cFileName);
//Is the entity a File or Folder?
if(fdFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
types[i] = FILE_CHILD_TYPE_DIR;
} else {
types[i] = FILE_CHILD_TYPE_FILE;
}
children[i] = buffer + (i * FILE_CHILD_NAME_MAX);
strcpy(children[i], fdFile.cFileName);
i++;
} while(FindNextFile(hFind, &fdFile));
*count = i;
return true;
#else
#endif
}

View File

@ -8,8 +8,14 @@
#pragma once
#include "common.h"
#define FILE_CHILD_TYPE_DIR 0x00
#define FILE_CHILD_TYPE_FILE 0x01
#define FILE_CHILD_NAME_MAX 512
#define FILE_CHILD_COUNT_MAX 64
#if defined(_MSC_VER)
#include <direct.h>
#include <windows.h>
#define getcwd _getcwd
#define FILE_PATH_SEP '\\'
#elif defined(__GNUC__)
@ -21,4 +27,14 @@ void fileNormalizeSlashes(char *string);
void fileMkdirp(char *path);
void assetReadString(FILE *file, char *buffer);
void assetReadString(FILE *file, char *buffer);
void fileGetDirectory(char *file, char* buffer);
bool fileListChildren(
char *directory,
char *buffer,
int32_t *count,
uint8_t *types,
char **children
);

View File

@ -1,19 +0,0 @@
const path = require('path');
const fs = require('fs');
const mkdirp = dir => {
const resolved = path.resolve(dir);
const resolvedDir = path.dirname(resolved);
const bits = resolvedDir.split(path.sep);
let running = '';
bits.forEach(bit => {
running += bit;
if(!fs.existsSync(running)) fs.mkdirSync(running);
running += path.sep;
});
}
module.exports = {
mkdirp
}

55
tools/utils/image.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 "image.h"
#ifndef STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#endif
#ifndef STB_IMAGE_RESIZE_IMPLEMENTATION
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include <stb_image_resize.h>
#endif
#ifndef STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>
#endif
void imageCopy(
uint8_t *source, int32_t sourceWidth, int32_t sourceHeight,
uint8_t *dest, int32_t destWidth, int32_t destHeight,
int32_t cropX, int32_t cropY, int32_t cropWidth, int32_t cropHeight,
int32_t pasteX, int32_t pasteY,
int32_t channels
) {
int32_t x, y, c;
int32_t absX, absY;
int32_t sourceIndex, targetIndex;
if(cropX == -1) cropX = 0;
if(cropY == -1) cropY = 0;
if(cropWidth == -1) cropWidth = sourceWidth;
if(cropHeight == -1) cropHeight = sourceHeight;
if(pasteX == -1) pasteX = 0;
if(pasteY == -1) pasteY = 0;
for(x = cropX; x < cropX + cropWidth; x++) {
for(y = cropY; y < cropY + cropHeight; y++) {
absX = x - cropX + pasteX;
absY = y - cropY + pasteY;
if(absX >= destWidth || absY >= destHeight || absX < 0 || absY < 0)continue;
targetIndex = absY * destWidth + absX;
sourceIndex = y * sourceWidth + x;
for(c = 0; c < channels; c++) {
dest[(targetIndex*channels) + c] = source[(sourceIndex*channels) + c];
}
}
}
}

View File

@ -6,3 +6,13 @@
*/
#pragma once
#include "common.h"
#include "file.h"
void imageCopy(
uint8_t *source, int32_t sourceWidth, int32_t sourceHeight,
uint8_t *dest, int32_t destWidth, int32_t destHeight,
int32_t cropX, int32_t cropY, int32_t cropWidth, int32_t cropHeight,
int32_t pasteX, int32_t pasteY,
int32_t channels
);

View File

@ -1,103 +0,0 @@
const { PNG } = require("pngjs");
const path = require('path');
const fs = require('fs');
/**
* Loads an image into memory.
* @param image Image to load
* @returns A promise that resolves to the loaded image.
*/
const imageLoad = (image) => new Promise(resolve => {
fs.createReadStream(image)
.pipe(new PNG({ filterType: 4 }))
.on("parsed", function () {
// Normalize
const pixels = [];
for(let y = 0; y < this.height; y++) {
for(let x = 0; x < this.width; x++) {
const idx = (this.width * y + x) << 2;
const r = this.data[idx];
const g = this.data[idx + 1];
const b = this.data[idx + 2];
const a = this.data[idx + 3];
pixels.push({ r, g, b, a });
}
}
resolve({ pixels, width: this.width, height: this.height });
})
;
});
/**
* Writes an image to an output file.
* @param image Image to write.
* @param file File to write to.
* @returns A promise that, when resolved, has saved the image.
*/
const imageWrite = (image, file) => new Promise(resolve => {
const png = new PNG({ width: image.width, height: image.height });
png.width = image.width;
png.height = image.height;
for(let y = 0; y < image.height; y++) {
for(let x = 0; x < image.width; x++) {
const i = (image.width * y + x);
const idx = i << 2;
const pixel = image.pixels[i];
png.data[idx] = pixel.r;
png.data[idx + 1] = pixel.g;
png.data[idx + 2] = pixel.b;
png.data[idx + 3] = pixel.a;
}
}
png.pack().pipe(fs.createWriteStream(file))
.on('close', () => resolve(true))
;
});
/**
* Creates a blank image
* @param width Width of the image.
* @param height Height of the image.
* @param fill Optional pixel to fill with, defaults to 0,0,0,0
* @returns The newly created image.
*/
const imageCreate = (width, height, pixel) => {
if(!pixel || !pixel.r) pixel = { r:0, g:0, b:0, a:0 };
const pixels = [];
for(let i = 0; i < width * height; i++) pixels.push({ ...pixel });
return { pixels, width, height };
}
/**
* Copies an area of a source image into a target image.
* @param target Target image to copy into.
* @param source Source image to copy from.
* @param tx Target X position to copy into
* @param ty Target Y position to copy into
* @param sub Optional source area to use, defined as { x, y, width, height }.
*/
const imageCopy = (target, source, tx, ty, sub) => {
if(!sub) sub = { x: 0, y: 0, width: source.width, height: source.height };
for(let x = sub.x; x < sub.x+sub.width; x++) {
for(let y = sub.y; y < sub.y+sub.height; y++) {
let absX = x - sub.x + tx;
let absY = y - sub.y + ty;
if(absX > target.width || absY > target.height) continue;
let ti = absY * target.width + absX;
let si = y * source.width + x;
target.pixels[ti] = { ...source.pixels[si] };
}
}
}
module.exports = {
imageWrite,
imageCreate,
imageLoad,
imageCopy
}

View File

@ -7,88 +7,204 @@
#include "xml.h"
void xmlParseElement(xmlnode_t *node, char *string) {
int32_t xmlLoadChild(xml_t *xml, char *data, int32_t i) {
char c;
int32_t i, j;
uint8_t state;
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;
node->attributeCount = 0;
xml->value = NULL;
xml->attributeCount = 0;
i = 0;
state = XML_STATE_NOTHING;
while(c = string[i++]) {
switch(state) {
case XML_STATE_NOTHING:
if(c != '<') continue;
node->start = string + (i - 1);
state = XML_STATE_PARSING_NAME;
break;
xml->children = malloc(sizeof(xml_t) * XML_CHILD_COUNT_MAX);
xml->childrenCount = 0;
case XML_STATE_PARSING_NAME:
if(c == ' ' || c == '\n' || c == '\r') continue;
j = i - 1;
while(c = string[j++]) {
if(c == ' ') break;
node->name[j] = c;
}
i = j;
state = XML_STATE_PARSING_ATTRIBUTES;
break;
case XML_STATE_PARSING_ATTRIBUTES:
if(c == ' ' || c == '\n' || c == '\r') continue;
if(c == '>') {
node->internal = string + i;
break;
continue;
}
// Parse Name
node->attributeNames[node->attributeCount] = string + (i - 1);
node->attributeNameLengths[node->attributeCount] = 0;
while(c == ' ' && c == '\n' && c == '\r' && c != '>' && c != '=' && c != '\0') {
c = string[i++];
node->attributeNameLengths[node->attributeCount]++;
}
if(c == '>') {
i--;
node->attributeValues[node->attributeCount] = NULL;
node->attributeValueLengths[node->attributeCount] = 0;
node->attributeCount++;
continue;
}
// Wait for = sign
while(c == ' ' || c == '\n' || c == '\r') c = string[i++];
// Handle booleans
if(c != '=') {
node->attributeValues[node->attributeCount] = NULL;
node->attributeValueLengths[node->attributeCount] = 0;
node->attributeCount++;
i--;
continue;
}
node->attributeValues[node->attributeCount] = string + i;
node->attributeValueLengths[node->attributeCount] = 0;
do {
c = string[i++];
node->attributeNameLengths[node->attributeCount]++;
if(c == '\0' || c == '"') break;
if(c == '\\') {
i++;
node->attributeNameLengths[node->attributeCount]++;
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;
}
} while(c);
node->attributeCount++;
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';
}

View File

@ -9,26 +9,59 @@
#include "common.h"
#include "file.h"
#define XML_NODE_CHILD_MAX 32
#define XML_NODE_NAME_MAX 32
#define XML_NODE_ATTRIBUTES_MAX 32
#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_STATE_NOTHING 0x00
#define XML_STATE_PARSING_NAME 0x01
#define XML_STATE_PARSING_ATTRIBUTES 0x02
#define XML_TEXT_BUFFER_MAX 256
#define XML_CHILD_COUNT_MAX 16
#define XML_ATTRIBUTE_MAX 16
typedef struct {
char *start;
char *internal;
char name[XML_NODE_NAME_MAX];
typedef struct _xml_t xml_t;
char *attributeNames[XML_NODE_ATTRIBUTES_MAX];
uint8_t attributeNameLengths[XML_NODE_ATTRIBUTES_MAX];
char *attributeValues[XML_NODE_ATTRIBUTES_MAX];
uint8_t attributeValueLengths[XML_NODE_ATTRIBUTES_MAX];
typedef struct _xml_t {
char *node;
char *value;
char *attributeNames[XML_ATTRIBUTE_MAX];
char *attributeDatas[XML_ATTRIBUTE_MAX];
uint8_t attributeCount;
} xmlnode_t;
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);
void xmlParseElement(xmlnode_t *node, char *string);
/**
* 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);