z_mirror/sdl-osu

git clone https://git.raiguard.me/z_mirror/sdl-osu

Caleb HeuerUse custom log categories instead of internal SDL categories4bbec0e

trunk
5.4 KiB148 linesraw
1#include "beatmapreader.h"
2#include "osustring.h"
3#include "util.h"
4#include <ctype.h>
5#include <SDL3/SDL.h>
6#include <stdio.h>
7
8// Enumerates beatmap data from a stream. Calls keyValueCallback for key-value pairs and csvCallback for CSV rows.
9// Returns true if the read completed without errors.
10bool Osu_EnumerateBeatmapData(const char* data, Osu_ReadBeatmapKeyValueCallback* keyValueCallback, Osu_ReadBeatmapCSVCallback* csvCallback, void* userdata) {
11    // TODO: Ask about what changes between versions of the file format.
12    // if (!Osu_HasPrefix(data, "osu file format v14"))
13    // {
14    //   OSU_LOG_TRACE("Invalid file version");
15    //   return false;
16    // }
17    char* dataEnd = strchr(data, 0);
18    char* cursor = strchr(data, '\n') + 1;
19    SDL_assert(cursor < dataEnd);
20    struct Osu_String* section = Osu_CreateString();
21    struct Osu_String* key = Osu_CreateString();
22    struct Osu_String* value = Osu_CreateString();
23    while (cursor < dataEnd) {
24        if (!*cursor) {
25            break;
26        }
27        char* lineEnd = strchr(cursor, '\n');
28        if (!lineEnd) {
29            break;
30        }
31        while (isspace(*cursor) && cursor < dataEnd) {
32            cursor++;
33        }
34        if (lineEnd < cursor) {
35            continue;
36        }
37        while (isspace(*lineEnd) && lineEnd > cursor) {
38            lineEnd--;
39        }
40        if (cursor >= lineEnd) {
41            continue;
42        }
43        if (strncmp(cursor, "//", 2) == 0) {
44            cursor = lineEnd + 1;
45            continue;
46        }
47        if (*cursor == '[') {
48            if (*lineEnd != ']') {
49                SDL_LogError(OSU_LOG_CATEGORY_BEATMAP_PARSER, "Section end is '%c'", *lineEnd);
50                Osu_DestroyString(section);
51                Osu_DestroyString(key);
52                Osu_DestroyString(value);
53                return false;
54            }
55            Osu_SetStringData(section, cursor + 1, lineEnd - cursor - 1);
56            OSU_LOG_TRACE(section->buf);
57            cursor = lineEnd + 1;
58            continue;
59        }
60        SDL_assert(cursor < lineEnd);
61        SDL_assert(cursor > data);
62        SDL_assert(lineEnd < dataEnd);
63
64        if (strcmp(section->buf, "Events") == 0 || strcmp(section->buf, "HitObjects") == 0) {
65            char** values = NULL;
66            size_t valuesLen = 0;
67            char* sectionEnd = cursor;
68            while (sectionEnd <= lineEnd + 1) {
69                if (*sectionEnd == ',' || sectionEnd == lineEnd + 1) {
70                    valuesLen++;
71                    if (values) {
72                        values = realloc(values, sizeof(char*) * valuesLen); // TODO: Reallocating every single time is slow!
73                    } else {
74                        values = malloc(sizeof(char*) * valuesLen);
75                    }
76                    values[valuesLen - 1] = malloc(sectionEnd - cursor + 1);
77                    strncpy(values[valuesLen - 1], cursor, sectionEnd - cursor);
78                    values[valuesLen - 1][sectionEnd - cursor] = 0;
79                    cursor = sectionEnd + 1;
80                }
81                sectionEnd++;
82            }
83            if (valuesLen > 0) {
84                switch (csvCallback(section->buf, values, valuesLen, userdata)) {
85                case SDL_ENUM_CONTINUE:
86                    break;
87                case SDL_ENUM_SUCCESS:
88                    Osu_DestroyString(section);
89                    Osu_DestroyString(key);
90                    Osu_DestroyString(value);
91                    return true;
92                case SDL_ENUM_FAILURE:
93                    Osu_DestroyString(section);
94                    Osu_DestroyString(key);
95                    Osu_DestroyString(value);
96                    return false;
97                }
98                for (size_t i = 0; i < valuesLen; i++) {
99                    free(values[i]);
100                }
101                free(values);
102            }
103        } else {
104            char* delim = memchr(cursor, ':', lineEnd - cursor + 1);
105            if (!delim || delim >= lineEnd) {
106                OSU_LOG_TRACE("Colon delimiter is beyond line end");
107                cursor = lineEnd + 1;
108                continue;
109            }
110            char* keyEnd = delim - 1;
111            while (keyEnd > cursor && isspace(*keyEnd)) {
112                keyEnd--;
113            }
114            SDL_assert(keyEnd > cursor);
115            char* valueStart = delim + 1;
116            while (valueStart < lineEnd && isspace(*valueStart)) {
117                valueStart++;
118            }
119            SDL_assert(valueStart <= lineEnd);
120            Osu_SetStringData(key, cursor, keyEnd - cursor + 1);
121            Osu_SetStringData(value, valueStart, lineEnd - valueStart + 1);
122
123            if (keyValueCallback) {
124                switch (keyValueCallback(section->buf, key->buf, value->buf, userdata)) {
125                case SDL_ENUM_CONTINUE:
126                    break;
127                case SDL_ENUM_SUCCESS:
128                    Osu_DestroyString(section);
129                    Osu_DestroyString(key);
130                    Osu_DestroyString(value);
131                    return true;
132                case SDL_ENUM_FAILURE:
133                    Osu_DestroyString(section);
134                    Osu_DestroyString(key);
135                    Osu_DestroyString(value);
136                    return false;
137                }
138            }
139        }
140
141        cursor = lineEnd + 1;
142    }
143
144    Osu_DestroyString(section);
145    Osu_DestroyString(key);
146    Osu_DestroyString(value);
147    return true;
148}