#include "wmap_parser.h" /* Helper macros */ #define IS_WHITESPACE(c) ((c) == ' ' || (c) == '\t') #define IS_DIGIT(c) ((c) >= '0' && (c) <= '9') #define IS_NEWLINE(c) ((c) == '\n' || (c) == '\r') /* Forward declarations */ static void wmap_skip_whitespace(const char** p, const char* end); static int wmap_parse_float(const char** p, const char* end, float* result); static int wmap_case_insensitive_compare(const char* a, const char* b, int len); static int wmap_is_keyword(const char* str, int len, const char* keyword); static wmap_shape_t wmap_parse_shape_name(const char* str, int len); static wmap_stage_t wmap_parse_stage_name(const char* str, int len); static void wmap_parse_line(wmap_parser_t* parser, const char* line, int len); /* Skip whitespace in a line */ static void wmap_skip_whitespace(const char** p, const char* end) { while (*p < end && IS_WHITESPACE(**p)) { (*p)++; } } /* Parse a float number */ static int wmap_parse_float(const char** p, const char* end, float* result) { const char* start; char buffer[64]; int len; wmap_skip_whitespace(p, end); start = *p; /* Handle optional leading dot or sign */ if (*p < end && (**p == '.' || **p == '-' || **p == '+')) { (*p)++; } /* Read digits */ while (*p < end && (IS_DIGIT(**p) || **p == '.')) { (*p)++; } len = *p - start; if (len == 0 || len >= 64) { return 0; } memcpy(buffer, start, len); buffer[len] = '\0'; *result = (float)atof(buffer); return 1; } /* Case-insensitive string comparison */ static int wmap_case_insensitive_compare(const char* a, const char* b, int len) { int i; for (i = 0; i < len; i++) { char ca = a[i]; char cb = b[i]; if (ca >= 'A' && ca <= 'Z') ca += 32; if (cb >= 'A' && cb <= 'Z') cb += 32; if (ca != cb) return 0; } return 1; } /* Check if string matches keyword (case-insensitive) */ static int wmap_is_keyword(const char* str, int len, const char* keyword) { int klen = strlen(keyword); if (len != klen) return 0; return wmap_case_insensitive_compare(str, keyword, len); } /* Parse shape name */ static wmap_shape_t wmap_parse_shape_name(const char* str, int len) { if (wmap_is_keyword(str, len, "x")) return WMAP_SHAPE_X; if (wmap_is_keyword(str, len, "square")) return WMAP_SHAPE_SQUARE; if (wmap_is_keyword(str, len, "triangle")) return WMAP_SHAPE_TRIANGLE; if (wmap_is_keyword(str, len, "circle")) return WMAP_SHAPE_CIRCLE; return WMAP_SHAPE_NONE; } /* Parse stage name */ static wmap_stage_t wmap_parse_stage_name(const char* str, int len) { if (wmap_is_keyword(str, len, "i")) return WMAP_STAGE_I; if (wmap_is_keyword(str, len, "ii")) return WMAP_STAGE_II; if (wmap_is_keyword(str, len, "iii")) return WMAP_STAGE_III; if (wmap_is_keyword(str, len, "iv")) return WMAP_STAGE_IV; return 0; } /* Parse coordinates (x, y) */ static int wmap_parse_coordinates(const char** p, const char* end, float* x, float* y) { wmap_skip_whitespace(p, end); if (*p >= end || **p != '(') return 0; (*p)++; if (!wmap_parse_float(p, end, x)) return 0; wmap_skip_whitespace(p, end); if (*p >= end || **p != ',') return 0; (*p)++; if (!wmap_parse_float(p, end, y)) return 0; wmap_skip_whitespace(p, end); if (*p >= end || **p != ')') return 0; (*p)++; return 1; } /* Parse component line: name (x, y) [shape] */ static int wmap_parse_component_line(wmap_parser_t* parser, const char* line, int len) { const char* p; const char* end; const char* name_start; const char* name_end; const char* shape_start; int name_len, shape_len, i; wmap_component_t* comp; if (parser->map->component_count >= WMAP_MAX_COMPONENTS) { return 0; } p = line; end = line + len; wmap_skip_whitespace(&p, end); if (p >= end) return 0; /* Find component name (everything before '(') */ name_start = p; while (p < end && *p != '(') { p++; } name_end = p; /* Trim trailing whitespace from name */ while (name_end > name_start && IS_WHITESPACE(name_end[-1])) { name_end--; } name_len = name_end - name_start; if (name_len == 0 || name_len >= WMAP_MAX_NAME_LEN) { return 0; } comp = &parser->map->components[parser->map->component_count]; /* Copy name */ for (i = 0; i < name_len; i++) { comp->name[i] = name_start[i]; } comp->name[name_len] = '\0'; /* Parse coordinates */ if (!wmap_parse_coordinates(&p, end, &comp->x, &comp->y)) { return 0; } /* Optional shape */ comp->shape = WMAP_SHAPE_NONE; wmap_skip_whitespace(&p, end); if (p < end && *p == '[') { p++; shape_start = p; while (p < end && *p != ']') { p++; } shape_len = p - shape_start; if (shape_len > 0) { comp->shape = wmap_parse_shape_name(shape_start, shape_len); } } parser->map->component_count++; return 1; } /* Parse dependency line: from -> to or from -- to */ static int wmap_parse_dependency_line(wmap_parser_t* parser, const char* line, int len) { const char* p; const char* end; const char* from_start; const char* from_end; const char* to_start; const char* to_end; int from_len, to_len, is_arrow, i; wmap_dependency_t* dep; if (parser->map->dependency_count >= WMAP_MAX_DEPENDENCIES) { return 0; } p = line; end = line + len; wmap_skip_whitespace(&p, end); from_start = p; /* Find arrow or dash */ while (p < end) { if (*p == '-') { if (p + 1 < end && p[1] == '>') { from_end = p; is_arrow = 1; p += 2; goto found_separator; } else if (p + 1 < end && p[1] == '-') { from_end = p; is_arrow = 0; p += 2; goto found_separator; } } p++; } return 0; /* No separator found */ found_separator: /* Trim trailing whitespace from 'from' */ while (from_end > from_start && IS_WHITESPACE(from_end[-1])) { from_end--; } from_len = from_end - from_start; if (from_len == 0 || from_len >= WMAP_MAX_NAME_LEN) { return 0; } wmap_skip_whitespace(&p, end); to_start = p; /* Find end of 'to' */ while (p < end && !IS_WHITESPACE(*p)) { p++; } to_end = p; to_len = to_end - to_start; if (to_len == 0 || to_len >= WMAP_MAX_NAME_LEN) { return 0; } dep = &parser->map->dependencies[parser->map->dependency_count]; /* Copy 'from' */ for (i = 0; i < from_len; i++) { dep->from[i] = from_start[i]; } dep->from[from_len] = '\0'; /* Copy 'to' */ for (i = 0; i < to_len; i++) { dep->to[i] = to_start[i]; } dep->to[to_len] = '\0'; dep->is_arrow = is_arrow; parser->map->dependency_count++; return 1; } /* Parse note line: [note] (x, y) text */ static int wmap_parse_note_line(wmap_parser_t* parser, const char* line, int len) { const char* p; const char* end; const char* text_start; int text_len, i; wmap_note_t* note; if (parser->map->note_count >= WMAP_MAX_NOTES) { return 0; } p = line; end = line + len; note = &parser->map->notes[parser->map->note_count]; /* Parse coordinates */ if (!wmap_parse_coordinates(&p, end, ¬e->x, ¬e->y)) { return 0; } /* Get remaining text */ wmap_skip_whitespace(&p, end); text_start = p; text_len = end - text_start; if (text_len >= WMAP_MAX_TEXT_LEN) { text_len = WMAP_MAX_TEXT_LEN - 1; } for (i = 0; i < text_len; i++) { note->text[i] = text_start[i]; } note->text[text_len] = '\0'; parser->map->note_count++; return 1; } /* Parse stage line: [i|ii|iii|iv] value */ static int wmap_parse_stage_line(wmap_parser_t* parser, const char* stage_name, int name_len, const char* line, int len) { const char* p; const char* end; wmap_stage_data_t* stage; if (parser->map->stage_count >= WMAP_MAX_STAGES) { return 0; } stage = &parser->map->stages[parser->map->stage_count]; stage->stage = wmap_parse_stage_name(stage_name, name_len); if (stage->stage == 0) { return 0; } p = line; end = line + len; if (!wmap_parse_float(&p, end, &stage->value)) { return 0; } parser->map->stage_count++; return 1; } /* Parse group line: [group] member1, member2, ... */ static int wmap_parse_group_line(wmap_parser_t* parser, const char* line, int len) { const char* p; const char* end; const char* member_start; const char* member_end; int member_len, i; wmap_group_t* group; if (parser->map->group_count >= WMAP_MAX_GROUPS) { return 0; } group = &parser->map->groups[parser->map->group_count]; group->member_count = 0; p = line; end = line + len; while (p < end && group->member_count < WMAP_MAX_GROUP_MEMBERS) { wmap_skip_whitespace(&p, end); member_start = p; /* Find end of member (comma or end of line) */ while (p < end && *p != ',') { p++; } member_end = p; /* Trim trailing whitespace */ while (member_end > member_start && IS_WHITESPACE(member_end[-1])) { member_end--; } member_len = member_end - member_start; if (member_len > 0 && member_len < WMAP_MAX_NAME_LEN) { for (i = 0; i < member_len; i++) { group->members[group->member_count][i] = member_start[i]; } group->members[group->member_count][member_len] = '\0'; group->member_count++; } /* Skip comma */ if (p < end && *p == ',') { p++; } } if (group->member_count > 0) { parser->map->group_count++; return 1; } return 0; } /* Parse inertia line: [inertia] name */ static int wmap_parse_inertia_line(wmap_parser_t* parser, const char* line, int len) { const char* p; const char* end; const char* name_start; const char* name_end; int name_len, i; wmap_inertia_t* inertia; if (parser->map->inertia_count >= WMAP_MAX_INERTIAS) { return 0; } p = line; end = line + len; wmap_skip_whitespace(&p, end); name_start = p; name_end = end; /* Trim trailing whitespace */ while (name_end > name_start && IS_WHITESPACE(name_end[-1])) { name_end--; } name_len = name_end - name_start; if (name_len == 0 || name_len >= WMAP_MAX_NAME_LEN) { return 0; } inertia = &parser->map->inertias[parser->map->inertia_count]; for (i = 0; i < name_len; i++) { inertia->name[i] = name_start[i]; } inertia->name[name_len] = '\0'; parser->map->inertia_count++; return 1; } /* Parse evolution line: [evolution] name +/- value */ static int wmap_parse_evolution_line(wmap_parser_t* parser, const char* line, int len) { const char* p; const char* end; const char* name_start; const char* name_end; int name_len, sign, i; float value; wmap_evolution_t* evolution; if (parser->map->evolution_count >= WMAP_MAX_EVOLUTIONS) { return 0; } p = line; end = line + len; wmap_skip_whitespace(&p, end); name_start = p; /* Find sign */ while (p < end && *p != '+' && *p != '-') { p++; } name_end = p; /* Trim trailing whitespace from name */ while (name_end > name_start && IS_WHITESPACE(name_end[-1])) { name_end--; } name_len = name_end - name_start; if (name_len == 0 || name_len >= WMAP_MAX_NAME_LEN || p >= end) { return 0; } sign = (*p == '+') ? 1 : -1; p++; if (!wmap_parse_float(&p, end, &value)) { return 0; } evolution = &parser->map->evolutions[parser->map->evolution_count]; for (i = 0; i < name_len; i++) { evolution->name[i] = name_start[i]; } evolution->name[name_len] = '\0'; evolution->change = sign * value; parser->map->evolution_count++; return 1; } /* Parse keyword-based line: [keyword] ... */ static int wmap_parse_keyword_line(wmap_parser_t* parser, const char* line, int len) { const char* p; const char* end; const char* keyword_start; const char* keyword_end; const char* content_start; int keyword_len, content_len; p = line; end = line + len; wmap_skip_whitespace(&p, end); if (p >= end || *p != '[') return 0; p++; keyword_start = p; while (p < end && *p != ']') { p++; } keyword_end = p; keyword_len = keyword_end - keyword_start; if (keyword_len == 0 || p >= end) return 0; p++; /* Skip ']' */ wmap_skip_whitespace(&p, end); content_start = p; content_len = end - content_start; /* Check keyword type */ if (wmap_is_keyword(keyword_start, keyword_len, "note")) { return wmap_parse_note_line(parser, content_start, content_len); } else if (wmap_is_keyword(keyword_start, keyword_len, "group")) { return wmap_parse_group_line(parser, content_start, content_len); } else if (wmap_is_keyword(keyword_start, keyword_len, "inertia")) { return wmap_parse_inertia_line(parser, content_start, content_len); } else if (wmap_is_keyword(keyword_start, keyword_len, "evolution")) { return wmap_parse_evolution_line(parser, content_start, content_len); } else { return wmap_parse_stage_line(parser, keyword_start, keyword_len, content_start, content_len); } } /* Determine if line contains dependency or component */ static int wmap_line_has_dependency(const char* line, int len) { const char* p; const char* end; p = line; end = line + len; while (p < end) { if (*p == '(') { return 0; /* Found '(' first - it's a component */ } if (*p == '-' && p + 1 < end && (p[1] == '>' || p[1] == '-')) { return 1; /* Found dependency marker */ } p++; } return 0; } /* Parse a single line */ static void wmap_parse_line(wmap_parser_t* parser, const char* line, int len) { const char* p; const char* end; if (len == 0) return; p = line; end = line + len; /* Skip leading whitespace */ wmap_skip_whitespace(&p, end); if (p >= end) return; /* Check for keyword line */ if (*p == '[') { if (!wmap_parse_keyword_line(parser, line, len)) { parser->error_count++; } return; } /* Check for dependency or component */ if (wmap_line_has_dependency(line, len)) { if (!wmap_parse_dependency_line(parser, line, len)) { parser->error_count++; } } else { if (!wmap_parse_component_line(parser, line, len)) { parser->error_count++; } } } /* Create parser - simplified without line array allocation */ static wmap_parser_t* wmap_parser_create(const char* input) { wmap_parser_t* parser; parser = (wmap_parser_t*)malloc(sizeof(wmap_parser_t)); if (!parser) return NULL; parser->input = input; parser->error_count = 0; parser->map = (wmap_map_t*)malloc(sizeof(wmap_map_t)); if (!parser->map) { free(parser); return NULL; } memset(parser->map, 0, sizeof(wmap_map_t)); return parser; } /* Destroy parser - simplified without line array */ static void wmap_parser_destroy(wmap_parser_t* parser) { if (parser) { if (parser->map) { free(parser->map); } free(parser); } } /* Parse entry point - now just calls direct parsing */ static int wmap_parser_parse(wmap_parser_t* parser) { const char* p; const char* line_start; int line_len; p = parser->input; line_start = p; while (*p != '\0') { if (*p == '\n' || *p == '\r') { /* Parse the line if non-empty */ line_len = p - line_start; if (line_len > 0) { wmap_parse_line(parser, line_start, line_len); } /* Handle \r\n */ if (*p == '\r' && p[1] == '\n') { p++; } p++; line_start = p; } else { p++; } } /* Parse final line if not empty */ line_len = p - line_start; if (line_len > 0) { wmap_parse_line(parser, line_start, line_len); } return parser->error_count == 0; } /* Public API implementation */ wmap_map_t* wmap_parse_string(const char* input) { wmap_parser_t* parser; wmap_map_t* result; if (!input) return NULL; if (!wmap_validate_input(input, 1024 * 1024)) { return NULL; } parser = wmap_parser_create(input); if (!parser) return NULL; wmap_parser_parse(parser); result = parser->map; parser->map = NULL; wmap_parser_destroy(parser); return result; } wmap_map_t* wmap_parse_file(const char* filename) { FILE* file; long size; char* buffer; wmap_map_t* result; file = fopen(filename, "r"); if (!file) return NULL; fseek(file, 0, SEEK_END); size = ftell(file); fseek(file, 0, SEEK_SET); if (size < 0 || size > 10 * 1024 * 1024) { fclose(file); return NULL; } buffer = (char*)malloc(size + 1); if (!buffer) { fclose(file); return NULL; } fread(buffer, 1, size, file); buffer[size] = '\0'; fclose(file); result = wmap_parse_string(buffer); free(buffer); return result; } void wmap_map_free(wmap_map_t* map) { if (map) { free(map); } } int wmap_validate_input(const char* input, size_t max_size) { size_t len; if (!input) { return 0; } len = strlen(input); if (len > max_size) { return 0; } return 1; }