From 78cdbe6a053895304ed39e30c9ed2edadce63339 Mon Sep 17 00:00:00 2001 From: sandyx Date: Fri, 3 Jan 2025 11:49:41 -0600 Subject: [PATCH] scrolling --- 8uwa.lss | 2077 ++++++++++++++++++++++++++++++++++++++++ ghost | Bin 0 -> 37744 bytes makefile | 3 +- shaders/frag.glsl | 2 +- src/controller.c | 50 +- src/controller.h | 7 +- src/file.h | 6 + src/fmt.c | 44 + src/fmt.h | 7 + src/gsl_reader.c | 11 - src/gsl_reader.h | 1 - src/layout_mgr.h | 21 - src/loader.c | 5 - src/main.c | 70 +- src/parser.c | 155 --- src/parser.h | 18 - src/segment_renderer.c | 60 +- src/segment_renderer.h | 17 +- src/splits.c | 298 ++++-- src/splits.c.no | 104 ++ src/splits.h | 13 +- src/timer.c | 58 +- src/timer.h | 40 +- src/timer_renderer.c | 8 +- src/timer_renderer.h | 10 +- src/xml.c | 1129 ++++++++++++++++++++++ src/xml.h | 196 ++++ test.xml | 2076 +++++++++++++++++++++++++++++++++++++++ 28 files changed, 6072 insertions(+), 414 deletions(-) create mode 100644 8uwa.lss create mode 100755 ghost create mode 100644 src/file.h create mode 100644 src/fmt.c create mode 100644 src/fmt.h delete mode 100644 src/gsl_reader.c delete mode 100644 src/gsl_reader.h delete mode 100644 src/layout_mgr.h delete mode 100644 src/loader.c delete mode 100644 src/parser.c delete mode 100644 src/parser.h create mode 100644 src/splits.c.no create mode 100644 src/xml.c create mode 100644 src/xml.h create mode 100644 test.xml diff --git a/8uwa.lss b/8uwa.lss new file mode 100644 index 0000000..809ab5d --- /dev/null +++ b/8uwa.lss @@ -0,0 +1,2077 @@ + + + + The Elder Scrolls IV: Oblivion + No OoB + + + + + + + + + + + 00:00:00 + 189 + + + + + + + + 00:35:20.4000000 + 00:27:32.9320000 + + + + + + + + 00:32:45.8710000 + 00:25:16.8550000 + + + + + 00:31:12.9250000 + 00:24:21.1100000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 00:31:02.4160000 + 00:23:50.2160000 + + + + + + 00:30:41.8080000 + 00:23:35.6660000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 00:30:48.2640000 + 00:23:30.8130000 + + + + + 00:30:43.1570000 + 00:23:27.2370000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 00:30:27.0050000 + 00:23:11.0680000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 00:00:35.9230000 + + + + + + + + + + + 00:31:24.1760000 + 00:23:51.4940000 + + + + + + + + + + + + + + 00:00:44.7560000 + + + + + Punch Glenroy + + + + 00:06:58.1680000 + 00:06:45.7900000 + + + 00:06:44.9600000 + + + + 00:06:51.2420000 + 00:06:38.9250000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skooma + + + + 00:07:45.8320000 + 00:07:03.8250000 + + + 00:07:02.1400000 + + + + 00:00:45.0800000 + 00:00:15.9720000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Weynon Priory + + + + 00:08:14.6520000 + 00:07:26.0710000 + + + 00:07:26.0300000 + + + + 00:00:28.8200000 + 00:00:22.2460000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Chairs + + + + 00:08:45.8860000 + 00:07:46.8270000 + + + 00:07:47.4700000 + + + + 00:00:30.2980000 + 00:00:20.1520000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Oblivion + + + + 00:09:51.1870000 + 00:08:40.9120000 + + + 00:08:36.8900000 + + + + 00:01:00.8990000 + 00:00:49.4230000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Kvatch + + + + 00:10:32.6330000 + 00:09:13.6970000 + + + 00:09:10.4100000 + + + + 00:00:39.4250000 + 00:00:30.7470000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Miscarcand + + + + 00:12:08.9650000 + 00:10:39.3980000 + + + 00:10:45.0800000 + + + + 00:01:36.3320000 + 00:01:25.7010000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Weynon Priory 2 + + + + 00:12:46.8130000 + 00:11:03.3240000 + + + 00:11:08.2800000 + + + + 00:00:35.7580000 + 00:00:22.4660000 + + + + + + + + + + + + + + + + + + + + + + + + + + + Umbra + + + + 00:14:18.8740000 + 00:12:18.9770000 + + + 00:12:25.4900000 + + + + 00:01:31.9270000 + 00:01:15.2850000 + + + + + + + + + + + + + + + + + + + + I'm Phintias + + + + 00:16:00.6760000 + 00:13:18.3120000 + + + 00:13:21.1600000 + + + + 00:01:36.6290000 + 00:00:55.6790000 + + + + + + Books + + + + 00:17:51.6930000 + 00:14:08.9510000 + + + 00:14:13.0600000 + + + + 00:01:51.0170000 + 00:00:50.6390000 + + + + + + + + + + + + + + + + + + + The Master's Vigilance + + + + 00:18:54.1190000 + 00:14:58.7450000 + + + 00:15:01.1800000 + + + + 00:01:00.4470000 + 00:00:47.8400000 + + + + + + + + + + + + + + + + + + + Double Homicide + + + + 00:19:57.5350000 + 00:15:28.0080000 + + + 00:15:30.5200000 + + + + 00:01:03.4160000 + 00:00:29.2630000 + + + + + + + + + + + + + + + + + + + Captain Burd + + + + 00:20:55.7630000 + 00:15:57.1280000 + + + 00:16:01.0600000 + + + + 00:00:56.3740000 + 00:00:28.6580000 + + + + + + + + + + + + + + + Sancre Tor + + + + 00:22:58.5080000 + 00:17:26.9280000 + + + 00:17:22.9700000 + + + + 00:01:49.3420000 + 00:01:21.9150000 + + + + + + + + + + + + + + + Great Gate + + + + 00:27:19.5360000 + 00:21:06.6700000 + + + 00:21:03.2100000 + + + + 00:04:20.8720000 + 00:03:39.7420000 + + + + + + + + + + + + + + + Chairadise + + + + 00:28:48.4380000 + 00:22:08.3830000 + + + 00:22:05.6900000 + + + + 00:01:28.9020000 + 00:01:01.1140000 + + + + + + + + + + + + + + + Save Hyrule + + + + 00:30:27.0050000 + 00:23:11.0680000 + + + 00:23:08.6900000 + + + + 00:01:38.5670000 + 00:01:02.6850000 + + + + + + + + + + + + + + + + 1.5 + C:\Users\a1086\Speedrun\LiveSplit\Components\Oblivion.asl + + + diff --git a/ghost b/ghost new file mode 100755 index 0000000000000000000000000000000000000000..3d2e267f6f80d6885f003b9c8feb1c673d53c3b2 GIT binary patch literal 37744 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≫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 LW4I}UDXRP@K>gSU literal 0 HcmV?d00001 diff --git a/makefile b/makefile index d4a2021..d9297f1 100755 --- a/makefile +++ b/makefile @@ -9,11 +9,10 @@ BUILD=build C_FILES := $(shell find $(SRC) -name '*.c') O_FILES += $(patsubst $(SRC)/%.c, $(BUILD)/%.o, $(C_FILES)) -CFLAGS := -O0 -march=native -std=gnu99 +CFLAGS := -O1 -march=native -std=gnu99 INCLUDE := LIB := -lraylib - ghost: $(APP_NAME) $(APP_NAME): $(O_FILES) diff --git a/shaders/frag.glsl b/shaders/frag.glsl index f7eb40c..1824356 100644 --- a/shaders/frag.glsl +++ b/shaders/frag.glsl @@ -9,5 +9,5 @@ void main() { //float mixval = distance(st, vec2(0, 1)); //vec3 color = mix(color1, color2, mixval); - gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); + gl_FragColor = vec4(0.0, 1.0, 1.0, 0.4); } diff --git a/src/controller.c b/src/controller.c index 8733191..f2eaa35 100644 --- a/src/controller.c +++ b/src/controller.c @@ -1,45 +1,62 @@ #include "controller.h" +#include "segment_renderer.h" int *splits = 0; -void ctrl_pause(GHTimer *timer) { - if (timer->paused) { - ghtimer_resume(timer); +//pause for igt only +inline void ctrl_pause_igt(ghost timer) { + if (timer.igt == NULL) + return; + + if (timer.igt->paused) { + ghtimer_resume(timer.igt); + } else { + ghtimer_pause(timer.igt); + } +} + +//pause for rta and igt +inline void ctrl_pause(ghost timer) { + if (timer.rta->paused) { + ghtimer_resume(timer.rta); } else { - ghtimer_pause(timer); + ghtimer_pause(timer.rta); } + + ctrl_pause_igt(timer); } void ctrl_start(ctrl_binder *cb) { - if (cb->timer_rta->running) { + if (cb->timer.rta->running) { if (cb->rec_sl != NULL) { - if (cb->current_segment < cb->live_sl->cnt) { - cb->live_sl->list[cb->current_segment]->realtime = ghtimer_time(cb->timer_rta); + if (cb->current_segment < cb->live_sl->count) { + cb->live_sl->segments[cb->current_segment].realtime = ghtimer_time(cb->timer.rta); //need to add a condition for igt + //cb->sr->scroller = scroll(cb->sr->scroller); + } else { goto STOP; } - cb->current_segment++; cb->sr->current = cb->current_segment; + scroll_down(cb->sr->scroller); } else { STOP: - //increment run count - //ask user to save splits? cb->current_segment = 0; cb->sr->running = false; cb->sr->current = cb->current_segment; - ghtimer_stop(cb->timer_rta); //stop or reset, save splits + ghtimer_stop(cb->timer.rta); //stop or reset, save splits + scroll_reset(cb->sr->scroller); } //pause timer if last split - if (cb->current_segment == cb->live_sl->cnt) { + if (cb->current_segment == cb->live_sl->count) { cb->sr->running = false; - ghtimer_pause(cb->timer_rta); + ghtimer_pause(cb->timer.rta); return; } } else { - ghtimer_start(cb->timer_rta); + ghost_start(cb->timer); cb->sr->running = true; } @@ -53,9 +70,10 @@ void ctrl_bind_segment_renderer(ctrl_binder *cb, segment_renderer *segmer) { cb->sr = segmer; } -ctrl_binder *create_ctrl_binder(GHTimer *rta, segment_list *sl) { +ctrl_binder *create_ctrl_binder(ghost timer, segment_list *sl) { ctrl_binder *binder = calloc(1, sizeof(ctrl_binder)); - binder->timer_rta = rta; + binder->timer = timer; + binder->rec_sl = sl; binder->live_sl = sl; return binder; diff --git a/src/controller.h b/src/controller.h index 7a7d558..3378d13 100644 --- a/src/controller.h +++ b/src/controller.h @@ -11,16 +11,17 @@ //and to the autosplitter typedef struct ctrl_binder { - GHTimer *timer_rta, *timer_igt; + ghost timer; segment_list *rec_sl, *live_sl; timer_renderer *tr; segment_renderer *sr; int current_segment; } ctrl_binder; -ctrl_binder *create_ctrl_binder(GHTimer *rta, segment_list *sl); +ctrl_binder *create_ctrl_binder(ghost timer, segment_list *sl); void ctrl_bind_timer_renderer(ctrl_binder *cb, timer_renderer *timer); void ctrl_bind_segment_renderer(ctrl_binder *cb, segment_renderer *segmer); void ctrl_start(ctrl_binder *cb); -void ctrl_pause(GHTimer *timer); +void ctrl_pause(ghost timer); +void ctrl_pause_igt(ghost timer); #endif diff --git a/src/file.h b/src/file.h new file mode 100644 index 0000000..a271204 --- /dev/null +++ b/src/file.h @@ -0,0 +1,6 @@ +#ifndef FILE_H +#define FILE_H + + + +#endif diff --git a/src/fmt.c b/src/fmt.c new file mode 100644 index 0000000..fa41ca2 --- /dev/null +++ b/src/fmt.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include "fmt.h" + +//maybe should just move the whole function here +extern double timespec_to_double(struct timespec ts); + +struct timespec parse_time(char *str) { + struct timespec time; + + time_t hour_t; + time_t min_t; + time_t sec_t; + time_t nsec_t; + + sscanf(str, "%ld:%ld:%ld.%ld", &hour_t, &min_t, &sec_t, &nsec_t); + + time.tv_sec = (hour_t * 3600) + (min_t * 60) + (sec_t); + time.tv_nsec = nsec_t * 1e+7f; + + //printf("%lf\n", timespec_to_double(time)); + //180.500_000_000 + + return time; +} + +char *time_unparse(char *buffer, size_t len, struct timespec *ts) { + struct tm *t = gmtime(&ts->tv_sec); + if (t->tm_hour == 0 && t->tm_min != 0) { + strftime(buffer, len, "%M:%S.", gmtime(&ts->tv_sec)); + } else if (t->tm_hour == 0 && t->tm_min == 0) { + strftime(buffer, len, "%S.", gmtime(&ts->tv_sec)); + } else { + strftime(buffer, len, "%T.", gmtime(&ts->tv_sec)); + } + + double ns = timespec_to_double(*ts); + ns = ns - (long)ns; + snprintf(&buffer[strlen(buffer)], len, "%.2ld", (long)(ns*100)); + + return buffer; +} diff --git a/src/fmt.h b/src/fmt.h new file mode 100644 index 0000000..b5ce336 --- /dev/null +++ b/src/fmt.h @@ -0,0 +1,7 @@ +#ifndef FMT_H +#define FMT_H + +struct timespec parse_time(char *str); +char *time_unparse(char *buffer, size_t len, struct timespec *ts); + +#endif diff --git a/src/gsl_reader.c b/src/gsl_reader.c deleted file mode 100644 index 2c5fd97..0000000 --- a/src/gsl_reader.c +++ /dev/null @@ -1,11 +0,0 @@ -//gsl_reader.c -#include "parser.h" - -void init_autosplit(const char *path) { - char *file_buffer = load_file(path); - char *line; - - for (line = get_next_line(file_buffer, 0); line != NULL; line = get_next_line(file_buffer, 0)) { - printf("%s\n", line); - } -} diff --git a/src/gsl_reader.h b/src/gsl_reader.h deleted file mode 100644 index e7fb046..0000000 --- a/src/gsl_reader.h +++ /dev/null @@ -1 +0,0 @@ -#ifndef GSL_READER diff --git a/src/layout_mgr.h b/src/layout_mgr.h deleted file mode 100644 index a282f7e..0000000 --- a/src/layout_mgr.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef LAYOUT_MGR_H -#define LAYOUT_MGR_H - -#include -#include -#include - -extern int windowWidth; -extern int windowHeight; - -typedef struct layout_background { - Texture2D texture; - Shader shader; -} layout_background; - -layout_background *layout_background_new(Color color) { - Image blank = GenImageColor(windowWidth, windowHeight, BLANK); - Texture2D texture = LoadTextureFromImage(blank); - UnloadImage(blank); -} -#endif diff --git a/src/loader.c b/src/loader.c deleted file mode 100644 index 6d064cd..0000000 --- a/src/loader.c +++ /dev/null @@ -1,5 +0,0 @@ -#include - -void load_module(const char *path) { - -} diff --git a/src/main.c b/src/main.c index c4a353c..2374619 100644 --- a/src/main.c +++ b/src/main.c @@ -1,10 +1,12 @@ #include #include "timer.h" #include "timer_renderer.h" +//#include "fmt.h" #include "segment_renderer.h" #include "controller.h" //#include "scanner.h" #include "splits.h" +//#include "parser.c" #include #if defined(PLATFORM_DESKTOP) @@ -13,6 +15,8 @@ #define GLSL_VERSION 100 #endif +#define ERROR "Error: %s\n" + //global variables int windowWidth = 420; int windowHeight = 640; @@ -21,6 +25,13 @@ int remote_state = 0; Font font; +//extern this for other files +void error(const char *msg) { + fprintf(stderr, ERROR, msg); + exit(EXIT_FAILURE); +} + +//extern void print_xml(const char *path); //testing control function void control(void) { static int last_state; @@ -32,43 +43,51 @@ void control(void) { } int main(int argc, char *argv[]) { + + //font = LoadFontEx("/usr/share/fonts/CozetteVector.ttf", 400, 0, 0); + // GenTextureMipmaps(&font.texture); + + if (argc < 2) { + error("gimme file"); + exit(EXIT_FAILURE); + } + + segment_list segs = open_splits_file(argv[1]); + //size of window can be determined by height of all components visible InitWindow(windowWidth, windowHeight, "Ghost"); SetTargetFPS(60); - //use libxml or something - segment_list segments = open_splits_file("test/splits.ghs"); - GHTimer *timer = ghtimer_new(); - - //make a layout manager for these - timer_renderer *tr = create_timer_renderer(timer, (Vector2){0.0f, 90.0f}, (Vector2){(float)windowWidth, (float)windowHeight/8}); - segment_renderer *sr = create_segment_renderer(&segments, 0.0f, 0.0f); - - //layout manager - Image blank = GenImageColor(windowWidth, windowHeight, BLANK); - Texture2D texture = LoadTextureFromImage(blank); + Image blank = GenImageColor(640, 420, BLANK); + Texture2D texture = LoadTextureFromImage(blank); Shader shader = LoadShader(0, "shaders/frag.glsl"); UnloadImage(blank); - set_segment_renderer_shader(sr, &shader); - set_timer_shader(tr, &shader); - - ctrl_binder *cb = create_ctrl_binder(timer, &segments); - ctrl_bind_timer_renderer(cb, tr); - ctrl_bind_segment_renderer(cb, sr); - - font = LoadFontEx("/usr/share/fonts/CozetteVector.ttf", 400, 0, 0); + font = LoadFontEx("/usr/share/fonts/CozetteVector.ttf", 400, 0, 0); GenTextureMipmaps(&font.texture); + + ghost ghost = ghost_new(); + + ctrl_binder *binder = create_ctrl_binder(ghost, &segs); + + //make a layout manager for these + timer_renderer *tr = create_timer_renderer(ghost, (Vector2){0.0f, windowHeight/8 * 6}, (Vector2){(float)windowWidth, (float)windowHeight/8}); + segment_renderer *sr = create_segment_renderer(segs, 0.0f, 0.0f); - float fontSize = (float)font.baseSize; + ctrl_bind_timer_renderer(binder, tr); + ctrl_bind_segment_renderer(binder, sr); + + //uint8_t buffer[1024]; + // float fontSize = (float)font.baseSize; while (!WindowShouldClose()) { - ghtimer_tick(timer); + ghost_tick(ghost); + //ghtimer_timestring(ghost.rta, buffer); if (IsKeyPressed(KEY_P)) { - ctrl_start(cb); + ctrl_start(binder); } - + BeginDrawing(); //BeginShaderMode(shader); ClearBackground(BLACK); @@ -78,10 +97,11 @@ int main(int argc, char *argv[]) { //need to make this not weird render_segments(sr); render_timer(tr); - EndShaderMode(); + //EndShaderMode(); + //printf("cunt\n"); EndDrawing(); } - ghtimer_delete(timer); + //ghost_delete(ghost); CloseWindow(); } diff --git a/src/parser.c b/src/parser.c deleted file mode 100644 index e7b4004..0000000 --- a/src/parser.c +++ /dev/null @@ -1,155 +0,0 @@ -#include "parser.h" - -/* - I've separated this out into its own file - so it can be used anywhere, and be compiled - with any C standard that supports strtok_r. - -*/ - -size_t get_file_len(FILE *file) { - fseek(file, 0, SEEK_END); - unsigned int len = ftell(file); - fseek(file, 0, SEEK_SET); - return len; -} - -/* - loads a files and mallocates a buffer - and returns a pointer to it. - - remember to free the buffer when you're done. -*/ -char *load_file(const char *path) { - FILE *file = fopen(path, "r"); - if (file == NULL) { - printf("%s\n", "Could not open file."); - return NULL; - } - - size_t len = get_file_len(file); - - char *buffer = malloc(len); - - #pragma GCC diagnostic ignored "-Wunused-result" - fread(buffer, len, 1, file); - fclose(file); - return buffer; -} - -char *get_next_line(char* buffer, bool clear) { - static char *saveptr; - static char *line; - - if (clear) { - saveptr = NULL; - line = NULL; - return NULL; - } - - if (saveptr == NULL) { - line = strtok_r(buffer, "\n", &saveptr); - } else { - if (line == NULL) { - saveptr = NULL; - return NULL; - } - - line = strtok_r(NULL, "\n", &saveptr); - } - - return line; -} - -char *get_next_token(char *buffer, char *delim, bool clear) { - static char *saveptr; - static char *token; - - if (clear) { - saveptr = NULL; - token = NULL; - return NULL; - } - - if (saveptr == NULL) { - token = strtok_r(buffer, delim, &saveptr); - } else { - if (token == NULL) { - saveptr = NULL; - return NULL; - } - - token = strtok_r(NULL, delim, &saveptr); - } - - return token; -} - -//find first non space char; -//copy that index to beginning of string -//replace last space with null -char *strip(char *str, char c) { - char *idx; - int len = strlen(str); - - //find index of first non space char - for (int i = 0; i < len; i++) { - if (str[i] != c) { - idx = &str[i]; - break; - } - } - - //voodoo math - memcpy(str, idx, len+1 - (idx - str)); - return str; -} - -//cut off the front -char *cut_front(char *str, char c) { - char *idx; - int len = strlen(str); - - //find index of first matching char - for (int i = 0; i < len; i++) { - if (str[i] == c) { - idx = &str[i]; - - break; - } - } - - return idx + 1; -} - -//cut off the back -char *cut_back(char *str, char c) { - char *idx; - char *cpy = strdup(str); - int len = strlen(str); - - //find index of first matching char - for (int i = 0; i < len; i++) { - if (cpy[i] == c) { - cpy[i] = 0; - - break; - } - } - - return cpy; -} - -//strip a string in place -char *strip_all(char *str) { - char *write = str; - char *read = str; - - do { - if (*read != ' ') { - *write++ = *read; - } - } while (*read++ && *read != ' '); - - return str; -} diff --git a/src/parser.h b/src/parser.h deleted file mode 100644 index ca7d0db..0000000 --- a/src/parser.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef PARSER_H -#define PARSER_H - -#include -#include -#include -#include - -size_t get_file_len(FILE *file); -char *load_file(const char *path); -char *get_next_line(char* buffer, bool clear); -char *get_next_token(char *buffer, char *delim, bool clear); -char *strip_all(char *str); -char *strip(char *str, char c); -char *cut_front(char *str, char c); -char *cut_back(char *str, char c); - -#endif diff --git a/src/segment_renderer.c b/src/segment_renderer.c index 60770a1..9b6ffe8 100644 --- a/src/segment_renderer.c +++ b/src/segment_renderer.c @@ -4,15 +4,48 @@ extern int windowHeight; extern int windowWidth; extern char *time_unparse(char *buffer, size_t len, struct timespec *ts); -segment_renderer *create_segment_renderer(segment_list *sl, float y, float h) { - segment_renderer *sr = calloc(1, sizeof(segment_renderer)); +scroller *create_scroller(int c, segment_list sl) { + scroller *s = calloc(1, sizeof(*s)); + s->sl = sl; + s->index = 0; + s->view_index = 0; + s->count = c; + s->segs = &sl.segments[s->view_index]; + + return s; +} + +void scroll_down(scroller *s) { + if (s->index < s->count - 1) { + s->index++; + return; + } + + if (s->view_index >= s->sl.count - s->count) { + return; + } + s->view_index++; + s->segs = &s->sl.segments[s->view_index]; + //printf("%d %d\n", s->view_index, s->sl.count - s->count); +} + +void scroll_reset(scroller *s) { + s->index = 0; + s->view_index = 0; + s->segs = &s->sl.segments[s->view_index]; +} + +segment_renderer *create_segment_renderer(segment_list sl, float y, float h) { + segment_renderer *sr = (segment_renderer *)calloc(1, sizeof(*sr)); sr->pos = y; sr->height = h; - sr->seglist = sl; - sr->count = sl->cnt; + sr->sl = sl; + sr->count = sl.count; sr->current = 0; sr->running = false; sr->shader = NULL; + sr->scroller = create_scroller(13, sl); + //printf("cunt %p\n%p\n", s, sr->scroller); return sr; } @@ -29,25 +62,20 @@ void destroy_segment_renderer(segment_renderer *sr) { } void render_segments(segment_renderer *sr) { - if (sr->shader != NULL) { - BeginShaderMode(*sr->shader); - } + //need to not draw it if its the last split //or if the timer isnt running if (sr->running) { - DrawRectangle(0, (sr->current) * 30, windowWidth, (float)font.baseSize/16, BLUE); + DrawRectangle(0, (sr->scroller->index) * 30, windowWidth, (float)font.baseSize/16, BLUE); } //draw a big rectangle the size of the segment thing - DrawRectangle(0, 0, windowWidth, 90, WHITE); - - if (sr->shader != NULL) { - EndShaderMode(); - } + //DrawRectangle(0, 0, windowWidth, 90, BLACK); - for (int i = 0; i < sr->count; i++) { - DrawTextEx(font, sr->seglist->list[i]->name, (Vector2){10, 30 * i}, (float)font.baseSize/16, 2, BLACK); - DrawTextEx(font, time_unparse(sr->strfmt, FMT_SIZE, &sr->seglist->list[i]->realtime), (Vector2){200, 30 * i}, (float)font.baseSize/16, 2, BLACK); + for (int i = 0; i < sr->scroller->count; i++) { + //DrawText(sr->scroller->segs[i].name, (Vector2){10, 30 * i}.x, (Vector2){10, 30 * i}.y, 20, WHITE); + DrawTextEx(font, sr->scroller->segs[i].name, (Vector2){10, 30 * i}, 20, 2, WHITE); + DrawTextEx(font, time_unparse(sr->strfmt, FMT_SIZE, &sr->scroller->segs[i].realtime), (Vector2){350, 30 * i}, (float)font.baseSize/16, 2, WHITE); } } diff --git a/src/segment_renderer.h b/src/segment_renderer.h index fda96f0..1ec38ae 100644 --- a/src/segment_renderer.h +++ b/src/segment_renderer.h @@ -13,18 +13,31 @@ extern Font font; //need to implement scrolling when total_splits > visible +typedef struct scroller { + int count; + int index; //where the bar should be on the splits + int view_index; //the first split displayed + segment *segs; + segment_list sl; +} scroller; + +scroller *create_scroller(int c, segment_list sl); +void scroll_down(scroller *s); +void scroll_reset(scroller *s); + typedef struct segment_renderer { float pos; float height; - segment_list *seglist; + segment_list sl; int count; int current; + scroller *scroller; bool running; Shader *shader; char strfmt[FMT_SIZE]; } segment_renderer; -segment_renderer *create_segment_renderer(segment_list *sl, float y, float h); +segment_renderer *create_segment_renderer(segment_list sl, float y, float h); void set_segment_renderer_shader(segment_renderer *sr, Shader *s); void set_segment_count(segment_renderer *sr, int c); void destroy_segment_renderer(segment_renderer *sr); diff --git a/src/splits.c b/src/splits.c index f92fb47..142c1e7 100644 --- a/src/splits.c +++ b/src/splits.c @@ -1,143 +1,233 @@ +#include +#include +#include + #include "splits.h" -#include "parser.h" #include "timer.h" -#include +#include "xml.h" +#include "fmt.h" -#include extern Font font; +extern void error(const char *msg); void print_segment(segment *seg) { printf("%s\n", seg->name); //printf("%ld.%ld\n", seg->realtime.tv_sec, seg->realtime.tv_nsec); } -struct timespec parse_time(char *str) { - struct timespec time; +//utils +int get_file_size(FILE *file) { + int size; + fseek(file, 0L, SEEK_END); + size = ftell(file); + fseek(file, 0L, SEEK_SET); + return size; +} - time_t hour_t; - time_t min_t; - time_t sec_t; - time_t nsec_t; +void log_node(struct xml_node *node) { + //struct xml_node *root = xml_document_root(document); + struct xml_string *name = xml_node_name(node); + struct xml_string *content = xml_node_content(node); + int name_len = xml_string_length(name); + int content_len = xml_string_length(content); + uint8_t *name_buffer = calloc(name_len + 1, sizeof(uint8_t)); + uint8_t *content_buffer = calloc(content_len + 1, sizeof(uint8_t)); + xml_string_copy(name, name_buffer, name_len); + xml_string_copy(content, content_buffer, content_len); + printf("%s: %s\n", name_buffer, content_buffer); + free(name_buffer); + free(content_buffer); +} - sscanf(str, "%ld:%ld:%ld.%ld", &hour_t, &min_t, &sec_t, &nsec_t); +//print children of node +void enum_node(struct xml_node *node) { + int children; + children = xml_node_children(node); - time.tv_sec = (hour_t * 3600) + (min_t * 60) + (sec_t); - time.tv_nsec = nsec_t * 1e+7f; + for (int i = 0; i < children; i++) { + struct xml_node *child = xml_node_child(node, i); + log_node(child); + //enum_node(child); + } +} - //printf("%lf\n", timespec_to_double(time)); - //180.500_000_000 +void enum_node_recursive(struct xml_node *node) { + int children; + children = xml_node_children(node); - return time; + for (int i = 0; i < children; i++) { + struct xml_node *child = xml_node_child(node, i); + log_node(child); + enum_node(child); + } } -char *time_unparse(char *buffer, size_t len, struct timespec *ts) { - struct tm *t = gmtime(&ts->tv_sec); - if (t->tm_hour == 0 && t->tm_min != 0) { - strftime(buffer, len, "%M:%S.", gmtime(&ts->tv_sec)); - } else if (t->tm_hour == 0 && t->tm_min == 0) { - strftime(buffer, len, "%S.", gmtime(&ts->tv_sec)); - } else { - strftime(buffer, len, "%T.", gmtime(&ts->tv_sec)); - } - - double ns = timespec_to_double(*ts); - ns = ns - (long)ns; - snprintf(&buffer[strlen(buffer)], len, "%.2ld", (long)(ns*100)); - - return buffer; +bool xml_name_compare(struct xml_node *node, const char *str) { + bool equal; + struct xml_string *name = xml_node_name(node); + int len = xml_string_length(name); + + uint8_t *buffer = calloc(len + 1, sizeof(uint8_t)); + xml_string_copy(name, buffer, len); + equal = !strcmp(buffer, str); + free(buffer); + return equal; } -segment *read_segment(char *file, char *line) { - segment *seg = calloc(1, sizeof(segment)); - - while (strcmp(line, "end")) { - char *key = strip(cut_back(line, ':'), ' '); - char *value = cut_front(strip(line, ' '), ' '); - +typedef struct segment_nodes { + struct xml_node **nodes; + int count; +} segment_nodes; + +segment_nodes get_node_children(struct xml_node *node) { + segment_nodes segments; + + segments.count = xml_node_children(node); + printf("get_node_children: %d\n", segments.count); + segments.nodes = calloc(segments.count, sizeof(struct xml_node *)); + + for (int i = 0; i < segments.count; i++) { + segments.nodes[i] = xml_node_child(node, i); + } + + return segments; +} - if (!strcmp(key, "name")) { - seg->name = strdup(value); - } +uint8_t *convert(struct xml_string *xml) { + int len = xml_string_length(xml); + uint8_t *buffer = calloc(len + 1, sizeof(uint8_t)); + xml_string_copy(xml, buffer, len); + return buffer; +} - if (!strcmp(key, "gametime")) { - seg->gametime = parse_time(value); - } +struct xml_string *get_name_string(struct xml_node *node) { + for (int i = 0; i < xml_node_children(node); i++) { + struct xml_node *child = xml_node_child(node, i); + if (xml_name_compare(child, "Name")) { + return xml_node_content(child); + } + } - if (!strcmp(key, "realtime")) { - seg->realtime = parse_time(value); - } + return NULL; +} - if (!strcmp(key, "best")) { - seg->best = parse_time(value); - } +uint8_t **extract(segment_nodes segments, struct xml_string *(operation)(struct xml_node *)) { + struct xml_string **xml_strings = calloc(segments.count, sizeof(struct xml_string *)); + + for (int i = 0; i < segments.count; i++) { + xml_strings[i] = operation(segments.nodes[i]); + } - free(key); - line = get_next_line(file, 0); - } + uint8_t **strings = calloc(segments.count, sizeof(uint8_t *)); + + for (int i = 0; i < segments.count; i++) { + strings[i] = convert(xml_strings[i]); + } - return seg; + return strings; } -segment_list open_splits_file(const char *path) { - char *file = load_file(path); - int idx = 0; - int cnt = 0; - segment_list segments = {0}; - - char *line = get_next_line(file, 0); - - //enumerate segments - while (line != NULL) { - if (!strcmp(line, "segment")) { - segments.cnt++; - } - line = get_next_line(file, 0); - } - - //reset strtok_r and create fresh file since it modified it - get_next_line(NULL, 1); - free(file); file = load_file(path); - - //make an extra one so the last segment->next == NULL; - segments.list = calloc(cnt + 1, sizeof(segment *)); - - line = get_next_line(file, 0); - - //create segments - while (line != NULL) { - if (!strcmp(line, "segment")) { - segments.list[idx] = read_segment(file, line); - idx++; - } - - line = get_next_line(file, 0); - } +struct xml_node *get_segments_node(struct xml_node *root) { + struct xml_node *child; + + for (int i = 0; i < xml_node_children(root); i++) { + struct xml_node *child = xml_node_child(root, i); + if (xml_name_compare(child, "Segments")) { + return child; + } + } + + return NULL; +} - for (int i = 0; i < cnt; i++) { - segments.list[i]->next = segments.list[i+1]; - } +struct xml_string **get_segment_names(struct xml_node *segments) { + int segments_count; + struct xml_string **segment_names; - //check - /*for (int i = 0; i < idx; i++) { - printf("%p\n", segments[i]); - }*/ - - //no leaky - free(file); + segments_count = xml_node_children(segments); + +} + +void print_all_segment_names(struct xml_node *node) { + struct xml_node *segments = get_segments_node(node); + if (!segments) { + error("couldnt get em"); + } - return segments; + enum_node(segments); } -/* -void render_splits(segment_list *segments) { +segment *create_segment(char *name) { + segment *seg = calloc(1, sizeof(segment)); + seg->name = name; + return seg; +} + +segment_list open_splits_file(const char *path) { + FILE *xml_file = fopen(path, "r"); + if (!xml_file) { + error("Could not open file"); + } + + int xml_file_size = get_file_size(xml_file); + uint8_t *buffer = calloc(sizeof(uint8_t), xml_file_size); + int no = fread(buffer, sizeof(uint8_t), xml_file_size, xml_file); + + struct xml_document *xml = xml_parse_document(buffer, xml_file_size); + if (!xml) { + error("Could not parse xml"); + } + + printf("splits.c: parsed %s successfully\n", path); + + int children = 0; + struct xml_node *root = xml_document_root(xml); + + struct xml_node *segments_node = get_segments_node(root); + segment_nodes segments = get_node_children(segments_node); + uint8_t **segment_names = extract(segments, get_name_string); + + printf("open_splits_file: %d\n", segments.count); + + for (int i = 0; i < segments.count; i++) { + printf("%s\n", segment_names[i]); + } + + segment_list seglist = {0}; + printf("open_splits_file: %d\n", segments.count); + + printf("%ld\n", segments.count * sizeof(segment)); + long long what = segments.count * sizeof(segment); + printf("what: %lld\n", what); + segment *segs = malloc(1008); + printf("?\n"); + + for (int i = 0; i < segments.count; i++) { + segs[i].name = segment_names[i]; + //segs[i].realtime = (struct timespec){0}; + } + + seglist.segments = segs; + seglist.count = segments.count; + return seglist; +} + + +/*void render_splits(segment_list *segments) { char buffer[100]; - for (int i = 0; i < segments->cnt; i++) { - DrawTextEx(font, segments->list[i]->name, (Vector2){10, 30 * i}, (float)font.baseSize/16, 2, BLACK); - DrawTextEx(font, time_unparse(buffer, 100, &segments->list[i]->realtime), (Vector2){200, 30 * i}, (float)font.baseSize/16, 2, BLACK); + for (int i = 0; i < segments->count; i++) { + DrawTextEx(font, segments->segments[i].name, (Vector2){10, 30 * i}, (float)font.baseSize/16, 2, WHITE); + //DrawTextEx(font, time_unparse(buffer, 100, &segments->segments[i].realtime), (Vector2){200, 30 * i}, (float)font.baseSize/16, 2, BLACK); } +}*/ + +void debug_print_list(segment_list *segments) { + for (int i = 0; i < segments->count; i++) { + printf("fucker %d\n", i); + printf("%s\n", segments->segments[i].name); + } } -*/ //probably need a thing to free all the segments diff --git a/src/splits.c.no b/src/splits.c.no new file mode 100644 index 0000000..0c4d39f --- /dev/null +++ b/src/splits.c.no @@ -0,0 +1,104 @@ +#include "splits.h" +//#include "parser.h" +#include "timer.h" +#include "xml.h" +#include + +#include +extern Font font; +extern void error(const char *msg); + +void print_segment(segment *seg) { + printf("%s\n", seg->name); + //printf("%ld.%ld\n", seg->realtime.tv_sec, seg->realtime.tv_nsec); +} + +/*struct timespec parse_time(char *str) { + struct timespec time; + + time_t hour_t; + time_t min_t; + time_t sec_t; + time_t nsec_t; + + sscanf(str, "%ld:%ld:%ld.%ld", &hour_t, &min_t, &sec_t, &nsec_t); + + time.tv_sec = (hour_t * 3600) + (min_t * 60) + (sec_t); + time.tv_nsec = nsec_t * 1e+7f; + + //printf("%lf\n", timespec_to_double(time)); + //180.500_000_000 + + return time; +}*/ + +/*char *time_unparse(char *buffer, size_t len, struct timespec *ts) { + struct tm *t = gmtime(&ts->tv_sec); + if (t->tm_hour == 0 && t->tm_min != 0) { + strftime(buffer, len, "%M:%S.", gmtime(&ts->tv_sec)); + } else if (t->tm_hour == 0 && t->tm_min == 0) { + strftime(buffer, len, "%S.", gmtime(&ts->tv_sec)); + } else { + strftime(buffer, len, "%T.", gmtime(&ts->tv_sec)); + } + + double ns = timespec_to_double(*ts); + ns = ns - (long)ns; + snprintf(&buffer[strlen(buffer)], len, "%.2ld", (long)(ns*100)); + + return buffer; +}*/ + +//utils +int get_file_size(FILE *file) { + int size; + fseek(file, 0L, SEEK_END); + size = ftell(file); + fseek(file, 0L, SEEK_SET); + return size; +} + +void log_node(struct xml_node *node) { + //struct xml_node *root = xml_document_root(document); + struct xml_string *string = xml_node_name(node); + int len = xml_string_length(string); + uint8_t *buffer = calloc(len + 1, sizeof(uint8_t)); + xml_string_copy(string, buffer, len); + printf("%s\n", buffer); + free(buffer); +} + +segment_list open_splits_file(const char *path) { + FILE *xml_file = fopen(path, "r"); + if (!xml_file) { + error("Could not open file"); + } + + int xml_file_size = get_file_size(xml_file); + uint8_t *buffer = calloc(sizeof(uint8_t), xml_file_size); + fread(buffer, sizeof(uint8_t), xml_file_size, xml_file); + + + struct xml_document *xml = xml_parse_document(buffer, xml_file_size); + if (!xml) { + error("Could not parse xml"); + } + + printf("parsed %s successfully\n", path); + + return (segment_list){0}; +} + +/* +void render_splits(segment_list *segments) { + char buffer[100]; + + for (int i = 0; i < segments->cnt; i++) { + DrawTextEx(font, segments->list[i]->name, (Vector2){10, 30 * i}, (float)font.baseSize/16, 2, BLACK); + DrawTextEx(font, time_unparse(buffer, 100, &segments->list[i]->realtime), (Vector2){200, 30 * i}, (float)font.baseSize/16, 2, BLACK); + } +} +*/ + +//probably need a thing to free all the segments + diff --git a/src/splits.h b/src/splits.h index 590c672..190f59b 100644 --- a/src/splits.h +++ b/src/splits.h @@ -11,15 +11,22 @@ struct segment { struct timespec gametime; struct timespec best; char *name; - segment *next; + //segment *next; }; typedef struct segment_list { - segment **list; - int cnt; + segment *segments; + int count; } segment_list; +//binds two lists of segments together +typedef struct segment_binder { + segment_list *rta; + segment_list *igt; +} segment_binder; + segment_list open_splits_file(const char *path); void render_splits(segment_list *segments); +void debug_print_list(segment_list *segments); #endif diff --git a/src/timer.c b/src/timer.c index ba8fcfd..d569269 100644 --- a/src/timer.c +++ b/src/timer.c @@ -20,8 +20,8 @@ double timespec_to_double(struct timespec ts) { } -GHTimer *ghtimer_new(void) { - GHTimer *timer = calloc(1, sizeof(GHTimer)); +ghtimer *ghtimer_new(void) { + ghtimer *timer = calloc(1, sizeof(ghtimer)); timer->running = false; timer->paused = false; clock_gettime(CLOCK_REALTIME, &timer->pause_start); @@ -31,27 +31,27 @@ GHTimer *ghtimer_new(void) { return timer; } -inline void ghtimer_delete(GHTimer *timer) { +inline void ghtimer_delete(ghtimer *timer) { free(timer); } -void ghtimer_start(GHTimer *timer) { +void ghtimer_start(ghtimer *timer) { clock_gettime(CLOCK_REALTIME, &timer->start); timer->running = true; } -void ghtimer_pause(GHTimer *timer) { +void ghtimer_pause(ghtimer *timer) { clock_gettime(CLOCK_REALTIME, &timer->pause_start); timer->paused = true; } -void ghtimer_resume(GHTimer *timer) { +void ghtimer_resume(ghtimer *timer) { struct timespec t = subts(timer->pause_current, timer->pause_start); timer->diff = addts(timer->diff, t); timer->paused = false; } -void ghtimer_stop(GHTimer *timer) { +void ghtimer_stop(ghtimer *timer) { timer->running = false; timer->paused = false; clock_gettime(CLOCK_REALTIME, &timer->pause_start); @@ -61,13 +61,13 @@ void ghtimer_stop(GHTimer *timer) { timer->diff = (struct timespec) {0}; } -void ghtimer_reset(GHTimer *timer) { +void ghtimer_reset(ghtimer *timer) { timer->running = false; timer->start = (struct timespec) {0}; timer->current = timer->start; } -void ghtimer_tick(GHTimer *timer) { +void ghtimer_tick(ghtimer *timer) { if (!timer->running) { return; } @@ -79,13 +79,49 @@ void ghtimer_tick(GHTimer *timer) { } } -struct timespec ghtimer_time(GHTimer *timer) { +struct timespec ghtimer_time(ghtimer *timer) { struct timespec time = subts(timer->current, timer->start); return subts(time, timer->diff); } -void ghtimer_timestring(GHTimer *timer, char *buffer) { +void ghtimer_timestring(ghtimer *timer, char *buffer) { struct timespec time = ghtimer_time(timer); double dbl = timespec_to_double(time); sprintf(buffer, "%.02lf", dbl); } + +ghost ghost_new(void) { + return (ghost) { + .rta = ghtimer_new(), + .igt = ghtimer_new() + }; +} + +ghost ghost_rta_new(void) { + return (ghost) { + .rta = ghtimer_new(), + NULL + }; +} + +//free and set to NULL +void ghost_delete(ghost ghost) { + ghtimer_delete(ghost.rta); + ghtimer_delete(ghost.igt); + ghost.rta = NULL; + ghost.igt = NULL; +} + +void ghost_tick(ghost ghost) { + ghtimer_tick(ghost.rta); + + if (ghost.igt == NULL) + return; + + ghtimer_tick(ghost.igt); +} + +void ghost_start(ghost ghost) { + ghtimer_start(ghost.rta); + ghtimer_start(ghost.igt); +} diff --git a/src/timer.h b/src/timer.h index 07dec84..86229ab 100644 --- a/src/timer.h +++ b/src/timer.h @@ -15,7 +15,7 @@ double timespec_to_double(struct timespec ts); //appended GH just incase theres a conflict with anything //else named "Timer" -typedef struct GHTimer { +typedef struct ghtimer { struct timespec start; struct timespec current; struct timespec pause_start; @@ -23,18 +23,30 @@ typedef struct GHTimer { struct timespec diff; int running; int paused; -} GHTimer; - -GHTimer *ghtimer_new(void); -void ghtimer_delete(GHTimer *timer); -void ghtimer_start(GHTimer *timer); -void ghtimer_pause(GHTimer *timer); -void ghtimer_resume(GHTimer *timer); -void ghtimer_stop(GHTimer *timer); -void ghtimer_reset(GHTimer *timer); -void ghtimer_tick(GHTimer *timer); - -struct timespec ghtimer_time(GHTimer *timer); -void ghtimer_timestring(GHTimer *timer, char *buffer); +} ghtimer; + +//two timers tied together +typedef struct ghost { + ghtimer *rta; + ghtimer *igt; +} ghost; + +ghtimer *ghtimer_new(void); +void ghtimer_delete(ghtimer *timer); +void ghtimer_start(ghtimer *timer); +void ghtimer_pause(ghtimer *timer); +void ghtimer_resume(ghtimer *timer); +void ghtimer_stop(ghtimer *timer); +void ghtimer_reset(ghtimer *timer); +void ghtimer_tick(ghtimer *timer); + +struct timespec ghtimer_time(ghtimer *timer); +void ghtimer_timestring(ghtimer *timer, char *buffer); + +ghost ghost_new(void); +ghost ghost_rta_new(void); +void ghost_delete(ghost ghost); //free and set to NULL +void ghost_tick(ghost ghost); +void ghost_start(ghost ghost); #endif diff --git a/src/timer_renderer.c b/src/timer_renderer.c index 1657ce6..e55a228 100644 --- a/src/timer_renderer.c +++ b/src/timer_renderer.c @@ -1,6 +1,6 @@ #include "timer_renderer.h" -timer_renderer *create_timer_renderer(GHTimer *t, Vector2 p, Vector2 sz) { +timer_renderer *create_timer_renderer(ghost t, Vector2 p, Vector2 sz) { timer_renderer *tr = calloc(1, sizeof(timer_renderer)); tr->timer = t; tr->pos = p; @@ -22,7 +22,7 @@ void destroy_timer_renderer(timer_renderer *tr) { } void render_timer(timer_renderer *tr) { - struct timespec ts = ghtimer_time(tr->timer); + struct timespec ts = ghtimer_time(tr->mode ? tr->timer.igt : tr->timer.rta); time_unparse(tr->strfmt, FMT_SIZE, &ts); Vector2 text_size = MeasureTextEx(font, tr->strfmt, (float)font.baseSize/4, 2); @@ -32,11 +32,11 @@ void render_timer(timer_renderer *tr) { } //draw the timer background - DrawRectangle(tr->pos.x, tr->pos.y, tr->size.x, tr->size.y, WHITE); + DrawRectangle(tr->pos.x, tr->pos.y, tr->size.x, tr->size.y, BLACK); if (tr->shader != NULL) { EndShaderMode(); } - DrawTextEx(font, tr->strfmt , (Vector2){(float)windowWidth - text_size.x - 2.0f, tr->pos.y}, (float)font.baseSize / 4, 2, BLACK); + DrawTextEx(font, tr->strfmt , (Vector2){(float)windowWidth - text_size.x - 2.0f, tr->pos.y}, (float)font.baseSize / 4, 2, WHITE); } diff --git a/src/timer_renderer.h b/src/timer_renderer.h index 7180e1b..04fffe3 100644 --- a/src/timer_renderer.h +++ b/src/timer_renderer.h @@ -15,8 +15,14 @@ extern int windowHeight; extern char *time_unparse(char *buffer, size_t len, struct timespec *ts); #define FMT_SIZE 64 +enum mode { + RTA = 0, + IGT = 1, +}; + typedef struct timer_renderer { - GHTimer *timer; + ghost timer; + int mode; Font font; Vector2 pos; Vector2 size; @@ -25,7 +31,7 @@ typedef struct timer_renderer { char strfmt[FMT_SIZE]; } timer_renderer; -timer_renderer *create_timer_renderer(GHTimer *t, Vector2 p, Vector2 sz); +timer_renderer *create_timer_renderer(ghost t, Vector2 p, Vector2 sz); void set_timer_shader(timer_renderer *tr, Shader *s); void set_timer_pos(timer_renderer *tr, Vector2 p); //maybe just destroy_renderer() or just free() diff --git a/src/xml.c b/src/xml.c new file mode 100644 index 0000000..8b6b559 --- /dev/null +++ b/src/xml.c @@ -0,0 +1,1129 @@ +/** + * Copyright (c) 2012 ooxi/xml.c + * https://github.com/ooxi/xml.c + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the + * use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + */ +#include "xml.h" + +#ifdef XML_PARSER_VERBOSE +#include +#endif + +#include + +#ifndef __MACH__ +#include +#endif + +#include +#include +#include +#include + + + + + +/* + * public domain strtok_r() by Charlie Gordon + * + * from comp.lang.c 9/14/2007 + * + * http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 + * + * (Declaration that it's public domain): + * http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c + */ +static char* xml_strtok_r(char *str, const char *delim, char **nextp) { + char *ret; + + if (str == NULL) { + str = *nextp; + } + + str += strspn(str, delim); + + if (*str == '\0') { + return NULL; + } + + ret = str; + + str += strcspn(str, delim); + + if (*str) { + *str++ = '\0'; + } + + *nextp = str; + + return ret; +} + + + + + + +/** + * [OPAQUE API] + * + * UTF-8 text + */ +struct xml_string { + uint8_t const* buffer; + size_t length; +}; + +/** + * [OPAQUE API] + * + * An xml_attribute may contain text content. + */ +struct xml_attribute { + struct xml_string* name; + struct xml_string* content; +}; + +/** + * [OPAQUE API] + * + * An xml_node will always contain a tag name, a 0-terminated list of attributes + * and a 0-terminated list of children. Moreover it may contain text content. + */ +struct xml_node { + struct xml_string* name; + struct xml_string* content; + struct xml_attribute** attributes; + struct xml_node** children; +}; + +/** + * [OPAQUE API] + * + * An xml_document simply contains the root node and the underlying buffer + */ +struct xml_document { + struct { + uint8_t* buffer; + size_t length; + } buffer; + + struct xml_node* root; +}; + + + + + +/** + * [PRIVATE] + * + * Parser context + */ +struct xml_parser { + uint8_t* buffer; + size_t position; + size_t length; +}; + +/** + * [PRIVATE] + * + * Character offsets + */ +enum xml_parser_offset { + NO_CHARACTER = -1, + CURRENT_CHARACTER = 0, + NEXT_CHARACTER = 1, +}; + + + + + +/** + * [PRIVATE] + * + * @return Number of attributes in 0-terminated array + */ +static size_t get_zero_terminated_array_attributes(struct xml_attribute** attributes) { + size_t elements = 0; + + while (attributes[elements]) { + ++elements; + } + + return elements; +} + + + +/** + * [PRIVATE] + * + * @return Number of nodes in 0-terminated array + */ +static size_t get_zero_terminated_array_nodes(struct xml_node** nodes) { + size_t elements = 0; + + while (nodes[elements]) { + ++elements; + } + + return elements; +} + + + +/** + * [PRIVATE] + * + * @warning No UTF conversions will be attempted + * + * @return true iff a == b + */ +static _Bool xml_string_equals(struct xml_string* a, struct xml_string* b) { + + if (a->length != b->length) { + return false; + } + + size_t i = 0; for (; i < a->length; ++i) { + if (a->buffer[i] != b->buffer[i]) { + return false; + } + } + + return true; +} + + + +/** + * [PRIVATE] + */ +static uint8_t* xml_string_clone(struct xml_string* s) { + if (!s) { + return 0; + } + + uint8_t* clone = calloc(s->length + 1, sizeof(uint8_t)); + + xml_string_copy(s, clone, s->length); + clone[s->length] = 0; + + return clone; +} + + + +/** + * [PRIVATE] + * + * Frees the resources allocated by the string + * + * @warning `buffer` must _not_ be freed, since it is a reference to the + * document's buffer + */ +static void xml_string_free(struct xml_string* string) { + free(string); +} + + + +/** + * [PRIVATE] + * + * Frees the resources allocated by the attribute + */ +static void xml_attribute_free(struct xml_attribute* attribute) { + if(attribute->name) { + xml_string_free(attribute->name); + } + if(attribute->content) { + xml_string_free(attribute->content); + } + free(attribute); +} + +/** + * [PRIVATE] + * + * Frees the resources allocated by the node + */ +static void xml_node_free(struct xml_node* node) { + xml_string_free(node->name); + + if (node->content) { + xml_string_free(node->content); + } + + struct xml_attribute** at = node->attributes; + while(*at) { + xml_attribute_free(*at); + ++at; + } + free(node->attributes); + + struct xml_node** it = node->children; + while (*it) { + xml_node_free(*it); + ++it; + } + free(node->children); + + free(node); +} + + + +/** + * [PRIVATE] + * + * Echos the parsers call stack for debugging purposes + */ +#ifdef XML_PARSER_VERBOSE +static void xml_parser_info(struct xml_parser* parser, char const* message) { + fprintf(stdout, "xml_parser_info %s\n", message); +} +#else +#define xml_parser_info(parser, message) {} +#endif + + + +/** + * [PRIVATE] + * + * Echos an error regarding the parser's source to the console + */ +static void xml_parser_error(struct xml_parser* parser, enum xml_parser_offset offset, char const* message) { + int row = 0; + int column = 0; + + #define min(X,Y) ((X) < (Y) ? (X) : (Y)) + #define max(X,Y) ((X) > (Y) ? (X) : (Y)) + size_t character = max(0, min(parser->length, parser->position + offset)); + #undef min + #undef max + + size_t position = 0; for (; position < character; ++position) { + column++; + + if ('\n' == parser->buffer[position]) { + row++; + column = 0; + } + } + + if (NO_CHARACTER != offset) { + fprintf(stderr, "xml_parser_error at %i:%i (is %c): %s\n", + row + 1, column, parser->buffer[character], message + ); + } else { + fprintf(stderr, "xml_parser_error at %i:%i: %s\n", + row + 1, column, message + ); + } +} + + + +/** + * [PRIVATE] + * + * Returns the n-th not-whitespace byte in parser and 0 if such a byte does not + * exist + */ +static uint8_t xml_parser_peek(struct xml_parser* parser, size_t n) { + size_t position = parser->position; + + while (position < parser->length) { + if (!isspace(parser->buffer[position])) { + if (n == 0) { + return parser->buffer[position]; + } else { + --n; + } + } + + position++; + } + + return 0; +} + + + +/** + * [PRIVATE] + * + * Moves the parser's position n bytes. If the new position would be out of + * bounds, it will be converted to the bounds itself + */ +static void xml_parser_consume(struct xml_parser* parser, size_t n) { + + /* Debug information + */ + #ifdef XML_PARSER_VERBOSE + #define min(X,Y) ((X) < (Y) ? (X) : (Y)) + char* consumed = alloca((n + 1) * sizeof(char)); + memcpy(consumed, &parser->buffer[parser->position], min(n, parser->length - parser->position)); + consumed[n] = 0; + #undef min + + size_t message_buffer_length = 512; + char* message_buffer = alloca(512 * sizeof(char)); + snprintf(message_buffer, message_buffer_length, "Consuming %li bytes \"%s\"", (long)n, consumed); + message_buffer[message_buffer_length - 1] = 0; + + xml_parser_info(parser, message_buffer); + #endif + + + /* Move the position forward + */ + parser->position += n; + + /* Don't go too far + * + * @warning Valid because parser->length must be greater than 0 + */ + if (parser->position >= parser->length) { + parser->position = parser->length - 1; + } +} + + + +/** + * [PRIVATE] + * + * Skips to the next non-whitespace character + */ +static void xml_skip_whitespace(struct xml_parser* parser) { + xml_parser_info(parser, "whitespace"); + + while (isspace(parser->buffer[parser->position])) { + if (parser->position + 1 >= parser->length) { + return; + } else { + parser->position++; + } + } +} + + + +/** + * [PRIVATE] + * + * Finds and creates all attributes on the given node. + * + * @author Blake Felt + * @see https://github.com/Molorius + */ +static struct xml_attribute** xml_find_attributes(struct xml_parser* parser, struct xml_string* tag_open) { + xml_parser_info(parser, "find_attributes"); + char* tmp; + char* rest = NULL; + char* token; + char* str_name; + char* str_content; + const unsigned char* start_name; + const unsigned char* start_content; + size_t old_elements; + size_t new_elements; + struct xml_attribute* new_attribute; + struct xml_attribute** attributes; + int position; + + attributes = calloc(1, sizeof(struct xml_attribute*)); + attributes[0] = 0; + + tmp = (char*) xml_string_clone(tag_open); + + token = xml_strtok_r(tmp, " ", &rest); // skip the first value + if(token == NULL) { + goto cleanup; + } + tag_open->length = strlen(token); + + for(token=xml_strtok_r(NULL," ", &rest); token!=NULL; token=xml_strtok_r(NULL," ", &rest)) { + str_name = malloc(strlen(token)+1); + str_content = malloc(strlen(token)+1); + // %s=\"%s\" wasn't working for some reason, ugly hack to make it work + if(sscanf(token, "%[^=]=\"%[^\"]", str_name, str_content) != 2) { + if(sscanf(token, "%[^=]=\'%[^\']", str_name, str_content) != 2) { + free(str_name); + free(str_content); + continue; + } + } + position = token-tmp; + start_name = &tag_open->buffer[position]; + start_content = &tag_open->buffer[position + strlen(str_name) + 2]; + + new_attribute = malloc(sizeof(struct xml_attribute)); + new_attribute->name = malloc(sizeof(struct xml_string)); + new_attribute->name->buffer = (unsigned char*)start_name; + new_attribute->name->length = strlen(str_name); + new_attribute->content = malloc(sizeof(struct xml_string)); + new_attribute->content->buffer = (unsigned char*)start_content; + new_attribute->content->length = strlen(str_content); + + old_elements = get_zero_terminated_array_attributes(attributes); + new_elements = old_elements + 1; + attributes = realloc(attributes, (new_elements+1)*sizeof(struct xml_attribute*)); + + attributes[new_elements-1] = new_attribute; + attributes[new_elements] = 0; + + + free(str_name); + free(str_content); + } + +cleanup: + free(tmp); + return attributes; +} + + + +/** + * [PRIVATE] + * + * Parses the name out of the an XML tag's ending + * + * ---( Example )--- + * tag_name> + * --- + */ +static struct xml_string* xml_parse_tag_end(struct xml_parser* parser) { + xml_parser_info(parser, "tag_end"); + size_t start = parser->position; + size_t length = 0; + + /* Parse until `>' or a whitespace is reached + */ + while (start + length < parser->length) { + uint8_t current = xml_parser_peek(parser, CURRENT_CHARACTER); + + if (('>' == current) || isspace(current)) { + break; + } else { + xml_parser_consume(parser, 1); + length++; + } + } + + /* Consume `>' + */ + if ('>' != xml_parser_peek(parser, CURRENT_CHARACTER)) { + xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_tag_end::expected tag end"); + return 0; + } + xml_parser_consume(parser, 1); + + /* Return parsed tag name + */ + struct xml_string* name = malloc(sizeof(struct xml_string)); + name->buffer = &parser->buffer[start]; + name->length = length; + return name; +} + + + +/** + * [PRIVATE] + * + * Parses an opening XML tag without attributes + * + * ---( Example )--- + * + * --- + */ +static struct xml_string* xml_parse_tag_open(struct xml_parser* parser) { + xml_parser_info(parser, "tag_open"); + xml_skip_whitespace(parser); + + /* Consume `<' + */ + if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) { + xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_tag_open::expected opening tag"); + return 0; + } + xml_parser_consume(parser, 1); + + /* Consume tag name + */ + return xml_parse_tag_end(parser); +} + + + +/** + * [PRIVATE] + * + * Parses an closing XML tag without attributes + * + * ---( Example )--- + * + * --- + */ +static struct xml_string* xml_parse_tag_close(struct xml_parser* parser) { + xml_parser_info(parser, "tag_close"); + xml_skip_whitespace(parser); + + /* Consume `position; + size_t length = 0; + + /* Consume until `<' is reached + */ + while (start + length < parser->length) { + uint8_t current = xml_parser_peek(parser, CURRENT_CHARACTER); + + if ('<' == current) { + break; + } else { + xml_parser_consume(parser, 1); + length++; + } + } + + /* Next character must be an `<' or we have reached end of file + */ + if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) { + xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_content::expected <"); + return 0; + } + + /* Ignore tailing whitespace + */ + while ((length > 0) && isspace(parser->buffer[start + length - 1])) { + length--; + } + + /* Return text + */ + struct xml_string* content = malloc(sizeof(struct xml_string)); + content->buffer = &parser->buffer[start]; + content->length = length; + return content; +} + + + +/** + * [PRIVATE] + * + * Parses an XML fragment node + * + * ---( Example without children )--- + * Text + * --- + * + * ---( Example with children )--- + * + * Text + * Text + * Content + * + * --- + */ +static struct xml_node* xml_parse_node(struct xml_parser* parser) { + xml_parser_info(parser, "node"); + + /* Setup variables + */ + struct xml_string* tag_open = 0; + struct xml_string* tag_close = 0; + struct xml_string* content = 0; + + size_t original_length; + struct xml_attribute** attributes; + + struct xml_node** children = calloc(1, sizeof(struct xml_node*)); + children[0] = 0; + + + /* Parse open tag + */ + tag_open = xml_parse_tag_open(parser); + if (!tag_open) { + xml_parser_error(parser, NO_CHARACTER, "xml_parse_node::tag_open"); + goto exit_failure; + } + + original_length = tag_open->length; + attributes = xml_find_attributes(parser, tag_open); + + /* If tag ends with `/' it's self closing, skip content lookup */ + if (tag_open->length > 0 && '/' == tag_open->buffer[original_length - 1]) { + /* Drop `/' + */ + goto node_creation; + } + + /* If the content does not start with '<', a text content is assumed + */ + if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) { + content = xml_parse_content(parser); + + if (!content) { + xml_parser_error(parser, 0, "xml_parse_node::content"); + goto exit_failure; + } + + + /* Otherwise children are to be expected + */ + } else while ('/' != xml_parser_peek(parser, NEXT_CHARACTER)) { + + /* Parse child node + */ + struct xml_node* child = xml_parse_node(parser); + if (!child) { + xml_parser_error(parser, NEXT_CHARACTER, "xml_parse_node::child"); + goto exit_failure; + } + + /* Grow child array :) + */ + size_t old_elements = get_zero_terminated_array_nodes(children); + size_t new_elements = old_elements + 1; + children = realloc(children, (new_elements + 1) * sizeof(struct xml_node*)); + + /* Save child + */ + children[new_elements - 1] = child; + children[new_elements] = 0; + } + + + /* Parse close tag + */ + tag_close = xml_parse_tag_close(parser); + if (!tag_close) { + xml_parser_error(parser, NO_CHARACTER, "xml_parse_node::tag_close"); + goto exit_failure; + } + + + /* Close tag has to match open tag + */ + if (!xml_string_equals(tag_open, tag_close)) { + xml_parser_error(parser, NO_CHARACTER, "xml_parse_node::tag missmatch"); + goto exit_failure; + } + + + /* Return parsed node + */ + xml_string_free(tag_close); + +node_creation:; + struct xml_node* node = malloc(sizeof(struct xml_node)); + node->name = tag_open; + node->content = content; + node->attributes = attributes; + node->children = children; + return node; + + + /* A failure occured, so free all allocalted resources + */ +exit_failure: + if (tag_open) { + xml_string_free(tag_open); + } + if (tag_close) { + xml_string_free(tag_close); + } + if (content) { + xml_string_free(content); + } + + struct xml_node** it = children; + while (*it) { + xml_node_free(*it); + ++it; + } + free(children); + + return 0; +} + + + + + +/** + * [PUBLIC API] + */ +struct xml_document* xml_parse_document(uint8_t* buffer, size_t length) { + + /* Initialize parser + */ + struct xml_parser parser = { + .buffer = buffer, + .position = 0, + .length = length + }; + + /* An empty buffer can never contain a valid document + */ + if (!length) { + xml_parser_error(&parser, NO_CHARACTER, "xml_parse_document::length equals zero"); + return 0; + } + + /* Parse the root node + */ + struct xml_node* root = xml_parse_node(&parser); + if (!root) { + xml_parser_error(&parser, NO_CHARACTER, "xml_parse_document::parsing document failed"); + return 0; + } + + /* Return parsed document + */ + struct xml_document* document = malloc(sizeof(struct xml_document)); + document->buffer.buffer = buffer; + document->buffer.length = length; + document->root = root; + + return document; +} + + + +/** + * [PUBLIC API] + */ +struct xml_document* xml_open_document(FILE* source) { + + /* Prepare buffer + */ + size_t const read_chunk = 1; // TODO 4096; + + size_t document_length = 0; + size_t buffer_size = 1; // TODO 4069 + uint8_t* buffer = malloc(buffer_size * sizeof(uint8_t)); + + /* Read hole file into buffer + */ + while (!feof(source)) { + + /* Reallocate buffer + */ + if (buffer_size - document_length < read_chunk) { + buffer = realloc(buffer, buffer_size + 2 * read_chunk); + buffer_size += 2 * read_chunk; + } + + size_t read = fread( + &buffer[document_length], + sizeof(uint8_t), read_chunk, + source + ); + + document_length += read; + } + fclose(source); + + /* Try to parse buffer + */ + struct xml_document* document = xml_parse_document(buffer, document_length); + + if (!document) { + free(buffer); + return 0; + } + return document; +} + + + +/** + * [PUBLIC API] + */ +void xml_document_free(struct xml_document* document, bool free_buffer) { + xml_node_free(document->root); + + if (free_buffer) { + free(document->buffer.buffer); + } + free(document); +} + + + +/** + * [PUBLIC API] + */ +struct xml_node* xml_document_root(struct xml_document* document) { + return document->root; +} + + + +/** + * [PUBLIC API] + */ +struct xml_string* xml_node_name(struct xml_node* node) { + return node->name; +} + + + +/** + * [PUBLIC API] + */ +struct xml_string* xml_node_content(struct xml_node* node) { + return node->content; +} + + + +/** + * [PUBLIC API] + * + * @warning O(n) + */ +size_t xml_node_children(struct xml_node* node) { + return get_zero_terminated_array_nodes(node->children); +} + + + +/** + * [PUBLIC API] + */ +struct xml_node* xml_node_child(struct xml_node* node, size_t child) { + if (child >= xml_node_children(node)) { + return 0; + } + + return node->children[child]; +} + + + +/** + * [PUBLIC API] + */ +size_t xml_node_attributes(struct xml_node* node) { + return get_zero_terminated_array_attributes(node->attributes); +} + + + +/** + * [PUBLIC API] + */ +struct xml_string* xml_node_attribute_name(struct xml_node* node, size_t attribute) { + if(attribute >= xml_node_attributes(node)) { + return 0; + } + + return node->attributes[attribute]->name; +} + + + +/** + * [PUBLIC API] + */ +struct xml_string* xml_node_attribute_content(struct xml_node* node, size_t attribute) { + if(attribute >= xml_node_attributes(node)) { + return 0; + } + + return node->attributes[attribute]->content; +} + + + +/** + * [PUBLIC API] + */ +struct xml_node* xml_easy_child(struct xml_node* node, uint8_t const* child_name, ...) { + + /* Find children, one by one + */ + struct xml_node* current = node; + + va_list arguments; + va_start(arguments, child_name); + + + /* Descent to current.child + */ + while (child_name) { + + /* Convert child_name to xml_string for easy comparison + */ + struct xml_string cn = { + .buffer = child_name, + .length = strlen(child_name) + }; + + /* Interate through all children + */ + struct xml_node* next = 0; + + size_t i = 0; for (; i < xml_node_children(current); ++i) { + struct xml_node* child = xml_node_child(current, i); + + if (xml_string_equals(xml_node_name(child), &cn)) { + if (!next) { + next = child; + + /* Two children with the same name + */ + } else { + va_end(arguments); + return 0; + } + } + } + + /* No child with that name found + */ + if (!next) { + va_end(arguments); + return 0; + } + current = next; + + /* Find name of next child + */ + child_name = va_arg(arguments, uint8_t const*); + } + va_end(arguments); + + + /* Return current element + */ + return current; +} + + + +/** + * [PUBLIC API] + */ +uint8_t* xml_easy_name(struct xml_node* node) { + if (!node) { + return 0; + } + + return xml_string_clone(xml_node_name(node)); +} + + + +/** + * [PUBLIC API] + */ +uint8_t* xml_easy_content(struct xml_node* node) { + if (!node) { + return 0; + } + + return xml_string_clone(xml_node_content(node)); +} + + + +/** + * [PUBLIC API] + */ +size_t xml_string_length(struct xml_string* string) { + if (!string) { + return 0; + } + return string->length; +} + + + +/** + * [PUBLIC API] + */ +void xml_string_copy(struct xml_string* string, uint8_t* buffer, size_t length) { + if (!string) { + return; + } + + #define min(X,Y) ((X) < (Y) ? (X) : (Y)) + length = min(length, string->length); + #undef min + + memcpy(buffer, string->buffer, length); +} + diff --git a/src/xml.h b/src/xml.h new file mode 100644 index 0000000..688a4be --- /dev/null +++ b/src/xml.h @@ -0,0 +1,196 @@ +/** + * Copyright (c) 2012 ooxi/xml.c + * https://github.com/ooxi/xml.c + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the + * use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + */ +#ifndef HEADER_XML +#define HEADER_XML + + +/** + * Includes + */ +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Opaque structure holding the parsed xml document + */ +struct xml_document; +struct xml_node; +struct xml_attribute; + +/** + * Internal character sequence representation + */ +struct xml_string; + + + +/** + * Tries to parse the XML fragment in buffer + * + * @param buffer Chunk to parse + * @param length Size of the buffer + * + * @warning `buffer` will be referenced by the document, you may not free it + * until you free the xml_document + * @warning You have to call xml_document_free after you finished using the + * document + * + * @return The parsed xml fragment iff parsing was successful, 0 otherwise + */ +struct xml_document* xml_parse_document(uint8_t* buffer, size_t length); + + + +/** + * Tries to read an XML document from disk + * + * @param source File that will be read into an xml document. Will be closed + * + * @warning You have to call xml_document_free with free_buffer = true after you + * finished using the document + * + * @return The parsed xml fragment iff parsing was successful, 0 otherwise + */ +struct xml_document* xml_open_document(FILE* source); + + + +/** + * Frees all resources associated with the document. All xml_node and xml_string + * references obtained through the document will be invalidated + * + * @param document xml_document to free + * @param free_buffer iff true the internal buffer supplied via xml_parse_buffer + * will be freed with the `free` system call + */ +void xml_document_free(struct xml_document* document, bool free_buffer); + + +/** + * @return xml_node representing the document root + */ +struct xml_node* xml_document_root(struct xml_document* document); + + + +/** + * @return The xml_node's tag name + */ +struct xml_string* xml_node_name(struct xml_node* node); + + + +/** + * @return The xml_node's string content (if available, otherwise NULL) + */ +struct xml_string* xml_node_content(struct xml_node* node); + + + +/** + * @return Number of child nodes + */ +size_t xml_node_children(struct xml_node* node); + + + +/** + * @return The n-th child or 0 if out of range + */ +struct xml_node* xml_node_child(struct xml_node* node, size_t child); + + + +/** + * @return Number of attribute nodes + */ +size_t xml_node_attributes(struct xml_node* node); + + + +/** + * @return the n-th attribute name or 0 if out of range + */ +struct xml_string* xml_node_attribute_name(struct xml_node* node, size_t attribute); + + + +/** + * @return the n-th attribute content or 0 if out of range + */ +struct xml_string* xml_node_attribute_content(struct xml_node* node, size_t attribute); + + + +/** + * @return The node described by the path or 0 if child cannot be found + * @warning Each element on the way must be unique + * @warning Last argument must be 0 + */ +struct xml_node* xml_easy_child(struct xml_node* node, uint8_t const* child, ...); + + + +/** + * @return 0-terminated copy of node name + * @warning User must free the result + */ +uint8_t* xml_easy_name(struct xml_node* node); + + + +/** + * @return 0-terminated copy of node content + * @warning User must free the result + */ +uint8_t* xml_easy_content(struct xml_node* node); + + + +/** + * @return Length of the string + */ +size_t xml_string_length(struct xml_string* string); + + + +/** + * Copies the string into the supplied buffer + * + * @warning String will not be 0-terminated + * @warning Will write at most length bytes, even if the string is longer + */ +void xml_string_copy(struct xml_string* string, uint8_t* buffer, size_t length); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/test.xml b/test.xml new file mode 100644 index 0000000..6b84452 --- /dev/null +++ b/test.xml @@ -0,0 +1,2076 @@ + + + The Elder Scrolls IV: Oblivion + No OoB + + + + + + + + + + + 00:00:00 + 189 + + + + + + + + 00:35:20.4000000 + 00:27:32.9320000 + + + + + + + + 00:32:45.8710000 + 00:25:16.8550000 + + + + + 00:31:12.9250000 + 00:24:21.1100000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 00:31:02.4160000 + 00:23:50.2160000 + + + + + + 00:30:41.8080000 + 00:23:35.6660000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 00:30:48.2640000 + 00:23:30.8130000 + + + + + 00:30:43.1570000 + 00:23:27.2370000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 00:30:27.0050000 + 00:23:11.0680000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 00:00:35.9230000 + + + + + + + + + + + 00:31:24.1760000 + 00:23:51.4940000 + + + + + + + + + + + + + + 00:00:44.7560000 + + + + + Punch Glenroy + + + + 00:06:58.1680000 + 00:06:45.7900000 + + + 00:06:44.9600000 + + + + 00:06:51.2420000 + 00:06:38.9250000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skooma + + + + 00:07:45.8320000 + 00:07:03.8250000 + + + 00:07:02.1400000 + + + + 00:00:45.0800000 + 00:00:15.9720000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Weynon Priory + + + + 00:08:14.6520000 + 00:07:26.0710000 + + + 00:07:26.0300000 + + + + 00:00:28.8200000 + 00:00:22.2460000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Chairs + + + + 00:08:45.8860000 + 00:07:46.8270000 + + + 00:07:47.4700000 + + + + 00:00:30.2980000 + 00:00:20.1520000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Oblivion + + + + 00:09:51.1870000 + 00:08:40.9120000 + + + 00:08:36.8900000 + + + + 00:01:00.8990000 + 00:00:49.4230000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Kvatch + + + + 00:10:32.6330000 + 00:09:13.6970000 + + + 00:09:10.4100000 + + + + 00:00:39.4250000 + 00:00:30.7470000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Miscarcand + + + + 00:12:08.9650000 + 00:10:39.3980000 + + + 00:10:45.0800000 + + + + 00:01:36.3320000 + 00:01:25.7010000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Weynon Priory 2 + + + + 00:12:46.8130000 + 00:11:03.3240000 + + + 00:11:08.2800000 + + + + 00:00:35.7580000 + 00:00:22.4660000 + + + + + + + + + + + + + + + + + + + + + + + + + + + Umbra + + + + 00:14:18.8740000 + 00:12:18.9770000 + + + 00:12:25.4900000 + + + + 00:01:31.9270000 + 00:01:15.2850000 + + + + + + + + + + + + + + + + + + + + I'm Phintias + + + + 00:16:00.6760000 + 00:13:18.3120000 + + + 00:13:21.1600000 + + + + 00:01:36.6290000 + 00:00:55.6790000 + + + + + + Books + + + + 00:17:51.6930000 + 00:14:08.9510000 + + + 00:14:13.0600000 + + + + 00:01:51.0170000 + 00:00:50.6390000 + + + + + + + + + + + + + + + + + + + The Master's Vigilance + + + + 00:18:54.1190000 + 00:14:58.7450000 + + + 00:15:01.1800000 + + + + 00:01:00.4470000 + 00:00:47.8400000 + + + + + + + + + + + + + + + + + + + Double Homicide + + + + 00:19:57.5350000 + 00:15:28.0080000 + + + 00:15:30.5200000 + + + + 00:01:03.4160000 + 00:00:29.2630000 + + + + + + + + + + + + + + + + + + + Captain Burd + + + + 00:20:55.7630000 + 00:15:57.1280000 + + + 00:16:01.0600000 + + + + 00:00:56.3740000 + 00:00:28.6580000 + + + + + + + + + + + + + + + Sancre Tor + + + + 00:22:58.5080000 + 00:17:26.9280000 + + + 00:17:22.9700000 + + + + 00:01:49.3420000 + 00:01:21.9150000 + + + + + + + + + + + + + + + Great Gate + + + + 00:27:19.5360000 + 00:21:06.6700000 + + + 00:21:03.2100000 + + + + 00:04:20.8720000 + 00:03:39.7420000 + + + + + + + + + + + + + + + Chairadise + + + + 00:28:48.4380000 + 00:22:08.3830000 + + + 00:22:05.6900000 + + + + 00:01:28.9020000 + 00:01:01.1140000 + + + + + + + + + + + + + + + Save Hyrule + + + + 00:30:27.0050000 + 00:23:11.0680000 + + + 00:23:08.6900000 + + + + 00:01:38.5670000 + 00:01:02.6850000 + + + + + + + + + + + + + + + + 1.5 + C:\Users\a1086\Speedrun\LiveSplit\Components\Oblivion.asl + + +