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 0000000..3d2e267
Binary files /dev/null and b/ghost differ
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 `'
+ */
+ if ( ('<' != xml_parser_peek(parser, CURRENT_CHARACTER))
+ || ('/' != xml_parser_peek(parser, NEXT_CHARACTER))) {
+
+ if ('<' != xml_parser_peek(parser, CURRENT_CHARACTER)) {
+ xml_parser_error(parser, CURRENT_CHARACTER, "xml_parse_tag_close::expected closing tag `<'");
+ }
+ if ('/' != xml_parser_peek(parser, NEXT_CHARACTER)) {
+ xml_parser_error(parser, NEXT_CHARACTER, "xml_parse_tag_close::expected closing tag `/'");
+ }
+
+ return 0;
+ }
+ xml_parser_consume(parser, 2);
+
+ /* Consume tag name
+ */
+ return xml_parse_tag_end(parser);
+}
+
+
+
+/**
+ * [PRIVATE]
+ *
+ * Parses a tag's content
+ *
+ * ---( Example )---
+ * this is
+ * a
+ * tag {} content
+ * ---
+ *
+ * @warning CDATA etc. is _not_ and will never be supported
+ */
+static struct xml_string* xml_parse_content(struct xml_parser* parser) {
+ xml_parser_info(parser, "content");
+
+ /* Whitespace will be ignored
+ */
+ xml_skip_whitespace(parser);
+
+ size_t start = parser->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
+
+
+