From 65739fd6e94c291fb7d73fa6b133b7421dc7c776 Mon Sep 17 00:00:00 2001 From: kdx Date: Thu, 28 Dec 2023 04:07:13 +0100 Subject: fuck artgames --- Tupfile | 4 +- compile_flags.txt | 1 + inc/cell.h | 19 ++++ inc/config.h | 17 ++++ inc/world.h | 20 ++++ src/cell.c | 102 ++++++++++++++++++++ src/config.c | 95 +++++++++++++++++++ src/main.c | 5 + src/world.c | 128 +++++++++++++++++++++++++ vendors/_.c | 150 +++++++++++++++++++++++++++++ vendors/_.h | 52 +++++++++- vendors/ini.c | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ vendors/ini.h | 20 ++++ 13 files changed, 887 insertions(+), 3 deletions(-) create mode 100644 inc/cell.h create mode 100644 inc/config.h create mode 100644 inc/world.h create mode 100644 src/cell.c create mode 100644 src/config.c create mode 100644 src/world.c create mode 100644 vendors/ini.c create mode 100644 vendors/ini.h diff --git a/Tupfile b/Tupfile index d33ea47..6e2369c 100644 --- a/Tupfile +++ b/Tupfile @@ -1,9 +1,9 @@ CC = gcc LD = gcc SRC = src/*.c vendors/*.c -CFLAGS = -std=c2x -Wall -Wextra -Wno-override-init -iquoteinc -includevendors/_.h -O3 +CFLAGS = -std=c2x -Wall -Wextra -Wno-override-init -iquoteinc -iquotevendors -includevendors/_.h -O3 LDFLAGS = -O3 -s -LIBS = -lSDL2 -lSDL2_mixer +LIBS = -lm -lSDL2 -lSDL2_mixer NAME = 007 : foreach $(SRC) |> $(CC) -c -o %o $(CFLAGS) %f |> build/%B.o diff --git a/compile_flags.txt b/compile_flags.txt index 2225c3e..99f31f5 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -3,4 +3,5 @@ -Wextra -Wno-initializer-overrides -iquoteinc +-iquotevendors -includevendors/_.h diff --git a/inc/cell.h b/inc/cell.h new file mode 100644 index 0000000..eceb45c --- /dev/null +++ b/inc/cell.h @@ -0,0 +1,19 @@ +#pragma once + +typedef struct Cell Cell; +struct Cell { + int id; + int x; + int y; + int width; + int height; + int *data; + Cell *next; +}; + +extern float g_shake; + +Cell *cell_load(const char *pattern, int id, int x, int y); +void cell_destroy(Cell *this, bool recurse); +void cell_draw(Cell *this, int x, int y); +int2 cell_find(Cell *this, int tile); diff --git a/inc/config.h b/inc/config.h new file mode 100644 index 0000000..e5fe194 --- /dev/null +++ b/inc/config.h @@ -0,0 +1,17 @@ +#pragma once + +typedef struct Config { + int tile_width; + int tile_height; + int cell_width; + int cell_height; + int world_width; + int world_height; + char *tileset_path; + char *world_path; +} Config; + +extern Config cfg; + +void config_init(const char *path); +void config_deinit(void); diff --git a/inc/world.h b/inc/world.h new file mode 100644 index 0000000..d4c56f0 --- /dev/null +++ b/inc/world.h @@ -0,0 +1,20 @@ +#pragma once +#include "cell.h" + +typedef struct World { + Cell *root_cell; + int width; + int height; + Cell **cells; + int x; + int y; + bool polarity; +} World; + +extern World g_world; + +void world_init(const char *path, const char *cell_pattern); +void world_deinit(void); +void world_draw(void); +int world_get(int x, int y); +int2 world_find(int tile); diff --git a/src/cell.c b/src/cell.c new file mode 100644 index 0000000..b90d1ee --- /dev/null +++ b/src/cell.c @@ -0,0 +1,102 @@ +float g_shake = 0.0f; + +#define expect(X) if (!(X)) { \ + perr("expect failed: "STR(X)); \ + fclose(fp); \ + cell_destroy(this, false); \ + return NULL; \ +} + +Cell * +cell_load(const char *pattern, int id, int x, int y) +{ + assert(pattern != NULL); + assert(id >= 0); + + char buf[64] = {0}; + snprintf(buf, sizeof(buf) - 1, pattern, id); + + FILE *fp = fopen(buf, "rb"); + if (fp == NULL) { + perr("failed to open '%s': %s", buf, strerror(errno)); + return NULL; + } + + Cell *this = NULL; + int width = 0, height = 0; + fscanf(fp, "%d[^,\n]", &width); + fscanf(fp, "%*c"); + fscanf(fp, "%d[^,\n]", &height); + fscanf(fp, "%*c"); + + expect(width > 0); + expect(height > 0); + + this = alloc(sizeof(Cell)); + expect(this != NULL); + + this->id = id; + this->x = x; + this->y = y; + this->width = width; + this->height = height; + this->data = alloc(sizeof(this->data[0]) * this->width * this->height); + expect(this->data != NULL); + + rfor (i, 0, this->width * this->height) { + fscanf(fp, "%d[^,\n]", &this->data[i]); + fscanf(fp, "%*c"); + } + + fclose(fp); + return this; +} + +void +cell_destroy(Cell *this, bool recurse) +{ + if (this == NULL) + return; + if (recurse) + cell_destroy(this->next, true); + if (this->data != NULL) + free(this->data); + free(this); +} + +void +cell_draw(Cell *this, int x, int y) +{ + const TZR_Uint tset = TZR_RES("res/tset.bmp"); + if (tset == 0) + return; + + const int tset_width = TZR_GetImageWidth(tset) / cfg.tile_width; + rfor (ty, 0, this->height) { + rfor (tx, 0, this->width) { + const int tile = this->data[tx + ty * this->width]; + if (tile == 0) + continue; + int dy = y + ty * cfg.tile_height; + int dx = x + tx * cfg.tile_width; + const int ix = (tile-1) % tset_width * cfg.tile_width; + int iy = (tile-1) / tset_width * cfg.tile_height; + const int r = rand(); + TZR_DrawImage(tset, + dx, dy, + ix, iy, + cfg.tile_width, cfg.tile_height, + .flip_x=r&1, .flip_y=(r&2)!=0); + } + } +} + +int2 +cell_find(Cell *this, int tile) +{ + rfor (y, 0, this->height) + rfor (x, 0, this->width) + if (this->data[x + y * this->width] == tile) + return I2(x, y); + return I2R(-1); +} diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..9a673ee --- /dev/null +++ b/src/config.c @@ -0,0 +1,95 @@ +#include "ini.h" + +Config cfg = {0}; + +#define expect(X) if (!(X)) { \ + perr("expect failed: "STR(X)); \ + if (ini != NULL) ini_free(ini); \ + config_deinit(); \ +} + +void +config_init(const char *path) +{ + ini_t *ini = NULL; + + ini = ini_load(path); + expect(ini != NULL); + + const char *tileset = ini_get(ini, "tileset", "path"); + if (tileset == NULL) { + pwrn("[tileset] path unspecified, using default"); + tileset = "tileset.png"; + } + cfg.tileset_path = alloc(strlen(tileset) + 1); + expect(cfg.tileset_path != NULL); + strcpy(cfg.tileset_path, tileset); + + const char *world_path = ini_get(ini, "world", "path"); + if (world_path == NULL) { + pwrn("[world] path unspecified, using default"); + world_path = "world.csv"; + } + cfg.world_path = alloc(strlen(world_path) + 1); + expect(cfg.world_path != NULL); + strcpy(cfg.world_path, world_path); + + with (tile_width, ini_get(ini, "tile", "width")) { + cfg.tile_width = atoi(tile_width); + } else { + pwrn("[tile] width unspecified, using default"); + cfg.tile_width = 16; + } + expect(cfg.tile_width > 0); + + with (tile_height, ini_get(ini, "tile", "height")) { + cfg.tile_height = atoi(tile_height); + } else { + pwrn("[tile] height unspecified, using default"); + cfg.tile_height = 16; + } + expect(cfg.tile_height > 0); + + with (cell_width, ini_get(ini, "cell", "width")) { + cfg.cell_width = atoi(cell_width); + } else { + pwrn("[cell] width unspecified, using default"); + cfg.cell_width = 25; + } + expect(cfg.cell_width > 0); + + with (cell_height, ini_get(ini, "cell", "height")) { + cfg.cell_height = atoi(cell_height); + } else { + pwrn("[cell] height unspecified, using default"); + cfg.cell_height = 14; + } + expect(cfg.cell_height > 0); + + with (world_width, ini_get(ini, "world", "width")) { + cfg.world_width = atoi(world_width); + } else { + pwrn("[world] width unspecified, using default"); + cfg.world_width = 16; + } + expect(cfg.world_width > 0); + + with (world_height, ini_get(ini, "world", "height")) { + cfg.world_height = atoi(world_height); + } else { + pwrn("[world] height unspecified, using default"); + cfg.world_height = 16; + } + expect(cfg.world_height > 0); + + ini_free(ini); +} + +void +config_deinit(void) +{ + if (cfg.world_path != NULL) + free(cfg.world_path); + if (cfg.tileset_path != NULL) + free(cfg.tileset_path); +} diff --git a/src/main.c b/src/main.c index 3298b03..de7b5ff 100644 --- a/src/main.c +++ b/src/main.c @@ -13,6 +13,10 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char **argv) { srand(time(nullptr)); defer(wdeinit); + config_init("res/world/aancyk.ini"); + defer(config_deinit); + world_init("res/world/world.csv", "res/world/%d.csv"); + defer(world_deinit); assert(TZR_Init(.interlace=true, .width=640, .height=640, @@ -55,6 +59,7 @@ _main_loop([[maybe_unused]] void *udata) .sx=1.0+randf()*.1, .sy=1.0+randf()*.1); } + world_draw(); //map_draw(); //player_draw(); } else { diff --git a/src/world.c b/src/world.c new file mode 100644 index 0000000..a24c7a6 --- /dev/null +++ b/src/world.c @@ -0,0 +1,128 @@ +World g_world = {0}; + +#define expect(X) if (!(X)) { \ + perr("expect failed: "STR(X)); \ + if (fp != NULL) fclose(fp); \ + world_deinit(); \ + exit(EXIT_FAILURE); \ +} + +void +world_init(const char *path, const char *cell_pattern) +{ + assert(path != NULL); + assert(cell_pattern != NULL); + + FILE *fp = fopen(path, "rw"); + expect(fp != NULL); + + int cell_count = 0; + fscanf(fp, "%d[^,\n", &cell_count); + fscanf(fp, "%*c"); + expect(cell_count > 0); + + // skip x and y + fscanf(fp, "%*d[^,\n]"); + fscanf(fp, "%*c"); + fscanf(fp, "%*d[^,\n]"); + fscanf(fp, "%*c"); + + int min_x = INT_MAX, min_y = INT_MAX, max_x = INT_MIN, max_y = INT_MIN; + rfor (i, 0, cell_count) { + int id = 0, x = 0, y = 0; + fscanf(fp, "%d[^,\n]", &id); + fscanf(fp, "%*c"); + fscanf(fp, "%d[^,\n]", &x); + fscanf(fp, "%*c"); + fscanf(fp, "%d[^,\n]", &y); + fscanf(fp, "%*c"); + + min_x = min(x, min_x); + min_y = min(x, min_y); + max_x = max(x, max_x); + max_y = max(x, max_y); + + Cell *const cell = cell_load(cell_pattern, id, x, y); + expect(cell != NULL); + cell->next = g_world.root_cell; + g_world.root_cell = cell; + } + expect(g_world.root_cell != NULL); + + fclose(fp); + fp = NULL; + + // flatten the cell structure + g_world.width = max_x - min_x + 1; + g_world.height = max_y - min_y + 1; + g_world.cells = alloc(sizeof(Cell*) * g_world.width * g_world.height); + expect(g_world.cells != NULL); + + foreach (cell, g_world.root_cell) { + const int cell_x = cell->x - min_x; + const int cell_y = cell->y - min_y; + g_world.cells[cell_x + cell_y * g_world.width] = cell; + } +} + +void +world_deinit(void) +{ + if (g_world.cells != NULL) { + free(g_world.cells); + g_world.cells = NULL; + } + cell_destroy(g_world.root_cell, true); +} + +void +world_draw(void) +{ + with (cell, g_world.cells[g_world.x + g_world.y * g_world.width]) + cell_draw(cell, 0, 0); +} + +int +world_get(int x, int y) +{ + int wx = g_world.x; + int wy = g_world.y; + + if (x < 0) { + x += cfg.tile_width * cfg.cell_width; + wx -= 1; + } else if (x >= cfg.tile_width * cfg.cell_width) { + x -= cfg.tile_width * cfg.cell_width; + wx += 1; + } + if (y < 0) { + y += cfg.tile_height * cfg.cell_height; + wy -= 1; + } else if (y >= cfg.tile_height * cfg.cell_height) { + y -= cfg.tile_height * cfg.cell_height; + wy += 1; + } + + const int i = x / cfg.tile_width + (y / cfg.tile_height) * cfg.cell_width; + with (cell, g_world.cells[wx + wy * g_world.width]) + return cell->data[i] ? (cell->data[i] - 1) : 0; + return 0; // oob tile +} + +int2 +world_find(int tile) +{ + rfor (y, 0, g_world.height) { + rfor (x, 0, g_world.width) { + Cell *const cell = g_world.cells[x + y * g_world.width]; + if (cell == NULL) + continue; + const int2 spawn = cell_find(cell, tile + 1); + if (spawn.x < 0) + continue; + return I2(x, y); + } + } + + return I2(-1, -1); +} diff --git a/vendors/_.c b/vendors/_.c index 9092bad..fc7ce07 100644 --- a/vendors/_.c +++ b/vendors/_.c @@ -1,4 +1,5 @@ #include "_.h" +#include #undef free #undef malloc #undef calloc @@ -77,3 +78,152 @@ _realloc(void *ptr, size_t size) panic("fucked up realloc, have fun debugging"); __builtin_unreachable(); } + +// mathematical vectors + +float2 +tofloat2(int2 a) +{ + return F2(a.x, a.y); +} + +int2 +toint2(float2 a) +{ + return I2(a.x, a.y); +} + +float2 +float2_sub(float2 a, float2 b) +{ + return F2(a.x - b.x, a.y - b.y); +} + +float2 +float2_add(float2 a, float2 b) +{ + return F2(a.x + b.x, a.y + b.y); +} + +float2 +float2_mul(float2 a, float x) +{ + return F2(a.x * x, a.y * x); +} + +float2 +float2_div(float2 a, float x) +{ + return F2(a.x / x, a.y / x); +} + +float +float2_length(float2 a) +{ + return sqrtf(a.x * a.x + a.y * a.y); +} + +float2 +float2_normalize(float2 a) +{ + const auto len = float2_length(a); + if (len < 1.0) + return a; + return F2(a.x / len, a.y / len); +} + +float +float2_angle(float2 a) +{ + return atanf(a.y / a.x) / 3.125 / 2 + 0.5 * (a.x < 0); +} + +int2 +float2_round(float2 a) +{ + return I2(roundf(a.x), roundf(a.y)); +} + +float2 +float2_clamp(float2 min, float2 a, float2 max) +{ + rfor (i, 0, 2) { + if (a.a[i] < min.a[i]) + a.a[i] = min.a[i]; + if (a.a[i] > max.a[i]) + a.a[i] = max.a[i]; + } + return a; +} + +float2 +float2_swp(float2 a) +{ + return F2(a.y, a.x); +} + +float2 +float2_lerp(float2 a, float2 b, float x) +{ + return float2_add(a, float2_mul(float2_sub(b, a), x)); +} + +int2 +int2_sub(int2 a, int2 b) +{ + return I2(a.x - b.x, a.y - b.y); +} + +int2 +int2_add(int2 a, int2 b) +{ + return I2(a.x + b.x, a.y + b.y); +} + +int2 +int2_mul(int2 a, float x) +{ + return I2(a.x * x, a.y * x); +} + +int2 +int2_div(int2 a, float x) +{ + return I2(a.x / x, a.y / x); +} + +static int +___abs(int a) +{ + return (a < 0) ? (-a) : (a); +} + +int2 +int2_abs(int2 a) +{ + return I2(___abs(a.x), ___abs(a.y)); +} + +int2 +int2_swp(int2 a) +{ + return I2(a.y, a.x); +} + +int2 +int2_inv(int2 a) +{ + return I2(-a.x, -a.y); +} + +int2 +int4_xy(int4 a) +{ + return I2(a.x, a.y); +} + +int2 +int4_zw(int4 a) +{ + return I2(a.z, a.w); +} diff --git a/vendors/_.h b/vendors/_.h index ef10927..544ff29 100644 --- a/vendors/_.h +++ b/vendors/_.h @@ -5,7 +5,6 @@ #include #include #include -#include "TZR.h" void wdeinit(void); [[nodiscard]] void *alloc(size_t size); @@ -53,3 +52,54 @@ void *_realloc(void *ptr, size_t size); #define pwrn(...) pgeneric("93mWRN", __VA_ARGS__) #define panic(...) perr(__VA_ARGS__), exit(EXIT_FAILURE) #define assert(X) if (!(X)) panic("assert failed: "STR(X)) + +// mathematical vectors + +#define UNPACK(V) (V).x, (V).y +#define I2(X, Y) (int2){{(X), (Y)}} +#define I2R(X) I2((X), (X)) +#define I2Z I2(0, 0) +#define F2(X, Y) (float2){{(X), (Y)}} +#define F2R(X) F2((X), (X)) +#define F2Z F2(0.0f, 0.0f) +#define I4(X, Y, Z, W) (int4){{(X), (Y), (Z), (W)}} +#define I4Z I4(0, 0, 0, 0) +#define I21 I2R(1) +#define F21 F2R(1) + +typedef union { struct { int x, y; }; int a[2]; } int2; +typedef union { struct { float x, y; }; float a[2]; } float2; +typedef union { struct { int x, y, z, w; }; int a[4]; } int4; + +float2 tofloat2(int2 a); +int2 toint2(float2 a); + +float2 float2_add(float2 a, float2 b); +float2 float2_sub(float2 a, float2 b); +float2 float2_mul(float2 a, float x); +float2 float2_div(float2 a, float x); +float float2_length(float2 a); +float2 float2_normalize(float2 a); +float float2_angle(float2 a); +int2 float2_round(float2 a); +float2 float2_clamp(float2 min, float2 a, float2 max); +float2 float2_swp(float2 a); +float2 float2_lerp(float2 a, float2 b, float x); + +int2 int2_sub(int2 a, int2 b); +int2 int2_add(int2 a, int2 b); +int2 int2_mul(int2 a, float x); +int2 int2_div(int2 a, float x); +int2 int2_abs(int2 a); +int2 int2_swp(int2 a); +int2 int2_inv(int2 a); + +int2 int4_xy(int4 a); +int2 int4_zw(int4 a); + +#define int2_log(L, A) log_##L("%d %d", (A).x, (A).y) +#define float2_log(L, A) log_##L("%f %f", (A).x, (A).y) + +#include "TZR.h" +#include "config.h" +#include "world.h" diff --git a/vendors/ini.c b/vendors/ini.c new file mode 100644 index 0000000..d6f5b75 --- /dev/null +++ b/vendors/ini.c @@ -0,0 +1,277 @@ +/** + * Copyright (c) 2016 rxi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#include "ini.h" + +struct ini_t { + char *data; + char *end; +}; + + +/* Case insensitive string compare */ +static int strcmpci(const char *a, const char *b) { + for (;;) { + int d = tolower(*a) - tolower(*b); + if (d != 0 || !*a) { + return d; + } + a++, b++; + } +} + +/* Returns the next string in the split data */ +static char* next(ini_t *ini, char *p) { + p += strlen(p); + while (p < ini->end && *p == '\0') { + p++; + } + return p; +} + +static void trim_back(ini_t *ini, char *p) { + while (p >= ini->data && (*p == ' ' || *p == '\t' || *p == '\r')) { + *p-- = '\0'; + } +} + +static char* discard_line(ini_t *ini, char *p) { + while (p < ini->end && *p != '\n') { + *p++ = '\0'; + } + return p; +} + + +static char *unescape_quoted_value(ini_t *ini, char *p) { + /* Use `q` as write-head and `p` as read-head, `p` is always ahead of `q` + * as escape sequences are always larger than their resultant data */ + char *q = p; + p++; + while (p < ini->end && *p != '"' && *p != '\r' && *p != '\n') { + if (*p == '\\') { + /* Handle escaped char */ + p++; + switch (*p) { + default : *q = *p; break; + case 'r' : *q = '\r'; break; + case 'n' : *q = '\n'; break; + case 't' : *q = '\t'; break; + case '\r' : + case '\n' : + case '\0' : goto end; + } + + } else { + /* Handle normal char */ + *q = *p; + } + q++, p++; + } +end: + return q; +} + + +/* Splits data in place into strings containing section-headers, keys and + * values using one or more '\0' as a delimiter. Unescapes quoted values */ +static void split_data(ini_t *ini) { + char *value_start, *line_start; + char *p = ini->data; + + while (p < ini->end) { + switch (*p) { + case '\r': + case '\n': + case '\t': + case ' ': + *p = '\0'; + /* Fall through */ + + case '\0': + p++; + break; + + case '[': + p += strcspn(p, "]\n"); + *p = '\0'; + break; + + case ';': + p = discard_line(ini, p); + break; + + default: + line_start = p; + p += strcspn(p, "=\n"); + + /* Is line missing a '='? */ + if (*p != '=') { + p = discard_line(ini, line_start); + break; + } + trim_back(ini, p - 1); + + /* Replace '=' and whitespace after it with '\0' */ + do { + *p++ = '\0'; + } while (*p == ' ' || *p == '\r' || *p == '\t'); + + /* Is a value after '=' missing? */ + if (*p == '\n' || *p == '\0') { + p = discard_line(ini, line_start); + break; + } + + if (*p == '"') { + /* Handle quoted string value */ + value_start = p; + p = unescape_quoted_value(ini, p); + + /* Was the string empty? */ + if (p == value_start) { + p = discard_line(ini, line_start); + break; + } + + /* Discard the rest of the line after the string value */ + p = discard_line(ini, p); + + } else { + /* Handle normal value */ + p += strcspn(p, "\n"); + trim_back(ini, p - 1); + } + break; + } + } +} + + + +ini_t* ini_load(const char *filename) { + ini_t *ini = NULL; + FILE *fp = NULL; + int n, sz; + + /* Init ini struct */ + ini = malloc(sizeof(*ini)); + if (!ini) { + goto fail; + } + memset(ini, 0, sizeof(*ini)); + + /* Open file */ + fp = fopen(filename, "rb"); + if (!fp) { + goto fail; + } + + /* Get file size */ + fseek(fp, 0, SEEK_END); + sz = ftell(fp); + rewind(fp); + + /* Load file content into memory, null terminate, init end var */ + ini->data = malloc(sz + 1); + if (!ini->data) { + goto fail; + } + ini->data[sz] = '\0'; + ini->end = ini->data + sz; + n = fread(ini->data, 1, sz, fp); + if (n != sz) { + goto fail; + } + + /* Prepare data */ + split_data(ini); + + /* Clean up and return */ + fclose(fp); + return ini; + +fail: + if (fp) fclose(fp); + if (ini) ini_free(ini); + return NULL; +} + + +void ini_free(ini_t *ini) { + if (ini->data) free(ini->data); + free(ini); +} + + +const char* ini_get(ini_t *ini, const char *section, const char *key) { + char *current_section = ""; + char *val; + char *p = ini->data; + + if (*p == '\0') { + p = next(ini, p); + } + + while (p < ini->end) { + if (*p == '[') { + /* Handle section */ + current_section = p + 1; + + } else { + /* Handle key */ + val = next(ini, p); + if (!section || !strcmpci(section, current_section)) { + if (!strcmpci(p, key)) { + return val; + } + } + p = val; + } + + p = next(ini, p); + } + + return NULL; +} + + +int ini_sget( + ini_t *ini, const char *section, const char *key, + const char *scanfmt, void *dst +) { + const char *val = ini_get(ini, section, key); + if (!val) { + return 0; + } + if (scanfmt) { + sscanf(val, scanfmt, dst); + } else { + *((const char**) dst) = val; + } + return 1; +} diff --git a/vendors/ini.h b/vendors/ini.h new file mode 100644 index 0000000..cd6af9f --- /dev/null +++ b/vendors/ini.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2016 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `ini.c` for details. + */ + +#ifndef INI_H +#define INI_H + +#define INI_VERSION "0.1.1" + +typedef struct ini_t ini_t; + +ini_t* ini_load(const char *filename); +void ini_free(ini_t *ini); +const char* ini_get(ini_t *ini, const char *section, const char *key); +int ini_sget(ini_t *ini, const char *section, const char *key, const char *scanfmt, void *dst); + +#endif -- cgit v1.2.3