From 640532f1d0b42323333cabc029af91da6d982d42 Mon Sep 17 00:00:00 2001 From: Mosder Date: Thu, 16 Jul 2026 16:09:12 +0200 Subject: feat: added "prettier" printing of fight results. still need to better parse items, drifs and gear --- analyzer/render.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 analyzer/render.c (limited to 'analyzer/render.c') diff --git a/analyzer/render.c b/analyzer/render.c new file mode 100644 index 0000000..0c3df56 --- /dev/null +++ b/analyzer/render.c @@ -0,0 +1,142 @@ +#include "render.h" + +#include +#include + +// strlen counting utf-8 characters as one +size_t utf8_strlen(char *s) { + size_t len = 0; + while (*s) { + // if the byte is not a continuation byte (10xxxxxx), it's a new character + if ((*s & 0xC0) != 0x80) + len++; + s++; + } + return len; +} + +void print_row_separator() { + for (size_t i = 0; i < TABLE_LINE_WIDTH; i++) + printf("%c", TABLE_ROW_SEPARATOR_CHAR); + printf("\n"); +} + +// print string with given color +void print_color(char *str, int color) { + printf("\033[%dm%s\033[0m", color, str); +} + +void print_center(char *text, int color, size_t width, char padding_char) { + size_t post_char_count = (width - strlen(text)) / 2; + size_t pre_char_count = width - post_char_count - strlen(text); + + for (size_t i = 0; i < pre_char_count; i++) + printf("%c", padding_char); + + print_color(text, color); + + for (size_t i = 0; i < post_char_count; i++) + printf("%c", padding_char); +} + +void print_enemy_list(EnemyResults *results, size_t n_enemies) { + printf(TABLE_LINE_START); + size_t line_length = TABLE_LINE_START_WIDTH; + + for (size_t i = 0; i < n_enemies; i++) { + EnemyResults enemy = results[i]; + char enemy_buffer[TABLE_LINE_WIDTH + 1]; + sprintf(enemy_buffer, "%s (%d)%s", enemy.name, enemy.level, i < n_enemies - 1 ? ", " : ""); + + // break line if length would be too big + if (line_length + utf8_strlen(enemy_buffer) > TABLE_LINE_WIDTH - TABLE_LINE_END_WIDTH) { + printf("%*s\n", (int)(TABLE_LINE_WIDTH - line_length), TABLE_LINE_END); + printf(TABLE_LINE_START); + line_length = TABLE_LINE_START_WIDTH; + } + + printf("%s", enemy_buffer); + line_length += utf8_strlen(enemy_buffer); + } + + printf("%*s\n", (int)(TABLE_LINE_WIDTH - line_length), TABLE_LINE_END); +} + +void print_column_names() { + char *names[TABLE_COLUMN_COUNT] = TABLE_COLUMN_NAMES; + int colors[TABLE_COLUMN_COUNT] = TABLE_COLUMN_COLORS; + size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; + + printf(TABLE_LINE_START); + for (size_t i = 0; i < TABLE_COLUMN_COUNT; i++) { + print_center(names[i], colors[i], widths[i], ' '); + printf(i < TABLE_COLUMN_COUNT - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); + } + printf("\n"); +} + +void print_player_rewards(FriendlyResults results) { + char buffers[TABLE_COLUMN_COUNT][TABLE_LINE_WIDTH]; + size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; + int colors[TABLE_COLUMN_COUNT] = TABLE_COLUMN_COLORS; + + sprintf(buffers[0], "%s (%d)", results.name, results.level); + sprintf(buffers[1], "%d", results.exp); + sprintf(buffers[2], "%d", results.gold); + + if (results.psycho > 0) + sprintf(buffers[3], "%d", results.psycho); + else + sprintf(buffers[3], "-"); + + if (results.saturation > 0) + sprintf(buffers[4], "%d (%s)", results.saturation, results.saturation_type); + else + sprintf(buffers[4], "-"); + + if (results.splinters > 0) + sprintf(buffers[5], "%d", results.splinters); + else + sprintf(buffers[5], "-"); + + printf(TABLE_LINE_START); + for (size_t i = 0; i < TABLE_COLUMN_COUNT; i++) { + char to_color[widths[i] + 1]; + sprintf(to_color, "%*s", (int)widths[i], buffers[i]); + print_color(to_color, colors[i]); + printf(i < TABLE_COLUMN_COUNT - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); + } + printf("\n"); + + // TODO: change this when I parse the items etc. to a readable format + // needs to utilize utf8_strlen, because right now utf8 characters mess the table up + char big_buf[1024]; + sprintf(big_buf, "%s - %s - %s", results.items, results.gear, results.drifs); + if (strlen(big_buf) > 6) { + printf(TABLE_LINE_START); + print_center("ITEMS", COLOR_WHITE, TABLE_ITEMS_WIDTH, ' '); + printf(TABLE_LINE_END "\n"); + + for (size_t i = 0; i < strlen(big_buf); i += TABLE_ITEMS_WIDTH) { + printf(TABLE_LINE_START "%-*.*s" TABLE_LINE_END "\n", (int)TABLE_ITEMS_WIDTH, (int)TABLE_ITEMS_WIDTH, big_buf + i); + } + } +} + +void render_fight_results(FightResults results) { + print_center("ENEMIES", COLOR_RED, TABLE_LINE_WIDTH, TABLE_ROW_SEPARATOR_CHAR); + printf("\n"); + + print_enemy_list(results.enemy_results, results.num_enemy); + + print_center("PLAYER REWARDS", COLOR_GREEN, TABLE_LINE_WIDTH, TABLE_ROW_SEPARATOR_CHAR); + printf("\n"); + + print_column_names(); + print_row_separator(); + + for (size_t i = 0; i < results.num_friendly; i++) { + print_player_rewards(results.friendly_results[i]); + print_row_separator(); + } +} -- cgit v1.2.3 From e0bf82fe2af66ef55872569ae15733f70b4913d4 Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 20 Jul 2026 15:39:23 +0200 Subject: feat: added parsing of normal items --- analyzer/parser/fight_results.c | 76 +++++++++++++++++++++++++++++++++++------ analyzer/parser/fight_results.h | 10 ++++-- analyzer/render.c | 23 ++++++++----- csv/items.csv | 1 + 4 files changed, 88 insertions(+), 22 deletions(-) (limited to 'analyzer/render.c') diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index d7b185a..5cda649 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -11,10 +11,11 @@ void cleanup_fight_results(void *data) { FightResults *fight_results = data; for (size_t i = 0; i < fight_results->num_friendly; i++) { - free(fight_results->friendly_results[i].name); - free(fight_results->friendly_results[i].gear); - free(fight_results->friendly_results[i].items); - free(fight_results->friendly_results[i].drifs); + FriendlyResults friendly = fight_results->friendly_results[i]; + free(friendly.name); + for (size_t j = 0; j < friendly.num_items; j++) + free(friendly.items[j]); + free(friendly.items); } for (size_t i = 0; i < fight_results->num_enemy; i++) { free(fight_results->enemy_results[i].name); @@ -86,6 +87,59 @@ char *get_item_name(int item_id) { return item_id < id_to_item_name_len ? id_to_item_name[item_id] : "null"; } +char **expand_items(char **items, size_t *items_capacity, size_t to_fit) { + while (*items_capacity < to_fit) + *items_capacity <<= 1; + return realloc(items, *items_capacity * sizeof(char *)); +} + +void add_gear(FriendlyResults *results, size_t *items_capacity, char *gear_str) { + if (strlen(gear_str) == 0) + return; + + // TODO: parse + if (results->num_items >= *items_capacity) + results->items = expand_items(results->items, items_capacity, results->num_items + 1); + + results->items[results->num_items++] = strdup(gear_str); +} + +void add_drifs(FriendlyResults *results, size_t *items_capacity, char *drifs_str) { + if (strlen(drifs_str) == 0) + return; + + // TODO: parse + if (results->num_items >= *items_capacity) + results->items = expand_items(results->items, items_capacity, results->num_items + 1); + + results->items[results->num_items++] = strdup(drifs_str); +} + +void add_items(FriendlyResults *results, size_t *items_capacity, char *items_str) { + if (strlen(items_str) == 0) + return; + + char *items_separator; + do { + char buffer[SINGLE_ITEM_BUFFER_LEN]; + items_separator = strstr(items_str, SEPARATOR_ITEMS); + + int item_id, item_count; + sscanf(items_str, "%d,%d", &item_id, &item_count); + sprintf(buffer, "%dx %s", item_count, get_item_name(item_id)); + + // if there are still items - modify items_str pointer + if (items_separator) + items_str = items_separator + strlen(SEPARATOR_ITEMS); + + // expand if needed + if (results->num_items >= *items_capacity) + results->items = expand_items(results->items, items_capacity, results->num_items + 1); + + results->items[results->num_items++] = strdup(buffer); + } while (items_separator); +} + char *get_saturation_type(int saturation_id) { return saturation_id < id_to_saturation_type_len ? id_to_saturation_type[saturation_id] : "null"; } @@ -97,16 +151,18 @@ FriendlyResults parse_to_friendly(EntityResults entity_results) { results.name = strdup(entity_results.name); results.exp = entity_results.exp; results.gold = entity_results.gold; - // TODO: parse that to more readable format - results.gear = strdup(entity_results.gear); - // TODO: parse that to more readable format, determine types from ids - results.items = strdup(entity_results.items); results.level = entity_results.level; results.psycho = entity_results.psycho; - // TODO: parse that to more readable format - results.drifs = strdup(entity_results.drifs); results.splinters = entity_results.splinters; + // create items array and add to it gear, drifs and items + results.num_items = 0; + results.items = malloc(INIT_ITEMS_CAPACITY * sizeof(char *)); + size_t items_cap = INIT_ITEMS_CAPACITY; + add_gear(&results, &items_cap, entity_results.gear); + add_drifs(&results, &items_cap, entity_results.drifs); + add_items(&results, &items_cap, entity_results.items); + results.saturation = 0; if (strlen(entity_results.saturation) > 0) { int saturation_id; diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index 17c2f05..ca45348 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -133,6 +133,11 @@ typedef struct { } EntityResults; #endif +// items/gear/drifs fields parsing info +#define INIT_ITEMS_CAPACITY 8 +#define SINGLE_ITEM_BUFFER_LEN 128 +#define SEPARATOR_ITEMS "[-]" + // values of field "team" #define TEAM_FRIEND 1 #define TEAM_ENEMY 2 @@ -144,11 +149,10 @@ typedef struct { char *name; int exp; int gold; - char *gear; - char *items; + char **items; + size_t num_items; int level; int psycho; - char *drifs; int splinters; char *saturation_type; int saturation; diff --git a/analyzer/render.c b/analyzer/render.c index 0c3df56..402fcb5 100644 --- a/analyzer/render.c +++ b/analyzer/render.c @@ -108,18 +108,23 @@ void print_player_rewards(FriendlyResults results) { } printf("\n"); - // TODO: change this when I parse the items etc. to a readable format - // needs to utilize utf8_strlen, because right now utf8 characters mess the table up - char big_buf[1024]; - sprintf(big_buf, "%s - %s - %s", results.items, results.gear, results.drifs); - if (strlen(big_buf) > 6) { + if (results.num_items > 0) { printf(TABLE_LINE_START); print_center("ITEMS", COLOR_WHITE, TABLE_ITEMS_WIDTH, ' '); - printf(TABLE_LINE_END "\n"); - - for (size_t i = 0; i < strlen(big_buf); i += TABLE_ITEMS_WIDTH) { - printf(TABLE_LINE_START "%-*.*s" TABLE_LINE_END "\n", (int)TABLE_ITEMS_WIDTH, (int)TABLE_ITEMS_WIDTH, big_buf + i); + printf(TABLE_LINE_END "\n" TABLE_LINE_START); + + size_t line_len = 0; + for (size_t i = 0; i < results.num_items; i++) { + char *sep = i < results.num_items - 1 ? ", " : ""; + size_t item_len = utf8_strlen(results.items[i]) + strlen(sep); + if (line_len + item_len > TABLE_ITEMS_WIDTH) { + printf("%*s" TABLE_LINE_END "\n" TABLE_LINE_START, (int)(TABLE_ITEMS_WIDTH - line_len), ""); + line_len = 0; + } + printf("%s%s", results.items[i], sep); + line_len += item_len; } + printf("%*s" TABLE_LINE_END "\n", (int)(TABLE_ITEMS_WIDTH - line_len), ""); } } diff --git a/csv/items.csv b/csv/items.csv index de9a7ce..0265384 100644 --- a/csv/items.csv +++ b/csv/items.csv @@ -3,3 +3,4 @@ id,item_name 1219,Trop trwogi 986,KrysztaƂ czasu 703,Glif arcymistrzowski +697,Glif bakalarski -- cgit v1.2.3 From f73349115edbc75021885eefd33ee33ee504d9a7 Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 20 Jul 2026 17:58:02 +0200 Subject: feat: added drif parsing and coloring of items depending on type --- analyzer/parser/fight_results.c | 144 ++++++++++++++++++++++++++-------------- analyzer/parser/fight_results.h | 91 +++++++++++++++++++++++-- analyzer/render.c | 23 ++++++- analyzer/render.h | 7 +- 4 files changed, 205 insertions(+), 60 deletions(-) (limited to 'analyzer/render.c') diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index 5cda649..f201fb1 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -14,7 +14,7 @@ void cleanup_fight_results(void *data) { FriendlyResults friendly = fight_results->friendly_results[i]; free(friendly.name); for (size_t j = 0; j < friendly.num_items; j++) - free(friendly.items[j]); + free(friendly.items[j].data); free(friendly.items); } for (size_t i = 0; i < fight_results->num_enemy; i++) { @@ -29,6 +29,47 @@ byte_t *find_fight_results(TCPStream *stream, int offset, int *length) { return start; } +// save to a field in struct depending on the type +void save_to_struct(TypeEnum type, void *field, char *src, size_t src_len) { + char *str = malloc(src_len + 1); + memcpy(str, src, src_len); + str[src_len] = '\0'; + + // parse depending on variable type + switch (type) { + case INT: + sscanf(str, "%d", (int *)field); + free(str); + break; + case FLOAT: + sscanf(str, "%f", (float *)field); + free(str); + break; + case DOUBLE: + sscanf(str, "%lf", (double *)field); + free(str); + break; + case STRING: + *(char **)field = str; + break; + } +} + +// fill struct fields from given data of given length +void get_struct_data(size_t field_count, TypeEnum *field_types, void **field_pointers, char *data_separator, char *data, size_t data_len) { + for (size_t i = 0; i < field_count; i++) { + char *sep = memmem(data, data_len, data_separator, strlen(data_separator)); + size_t len = sep ? sep - data : data_len; + save_to_struct(field_types[i], field_pointers[i], (char *)data, len); + + // update data pointer and length + if (sep) { + data_len -= sep - data + strlen(SEPARATOR_RESULT); + data = sep + strlen(SEPARATOR_RESULT); + } + } +} + void destroy_entity_results(EntityResults results) { free(results.name); free(results.field7); @@ -45,52 +86,14 @@ EntityResults get_entity_results(byte_t *data, size_t length) { EntityResults results; TypeEnum field_types[ENTITY_FIELD_COUNT] = ENTITY_FIELD_TYPES; void *field_pointers[ENTITY_FIELD_COUNT] = ENTITY_FIELD_ADDRESSES(results); - - for (size_t i = 0; i < ENTITY_FIELD_COUNT; i++) { - // get string buffer of given result - byte_t *result_separator = memmem(data, length, SEPARATOR_RESULT, strlen(SEPARATOR_RESULT)); - size_t len = result_separator ? result_separator - data : length; - char *str = malloc(len + 1); - memcpy(str, data, len); - str[len] = '\0'; - - // parse depending on variable type - switch (field_types[i]) { - case INT: - sscanf(str, "%d", (int *)field_pointers[i]); - free(str); - break; - case FLOAT: - sscanf(str, "%f", (float *)field_pointers[i]); - free(str); - break; - case DOUBLE: - sscanf(str, "%lf", (double *)field_pointers[i]); - free(str); - break; - case STRING: - *(char **)field_pointers[i] = str; - break; - } - - // update data pointer and length - if (result_separator) { - length -= result_separator - data + strlen(SEPARATOR_RESULT); - data = result_separator + strlen(SEPARATOR_RESULT); - } - } - + get_struct_data(ENTITY_FIELD_COUNT, field_types, field_pointers, SEPARATOR_RESULT, (char *)data, length); return results; } -char *get_item_name(int item_id) { - return item_id < id_to_item_name_len ? id_to_item_name[item_id] : "null"; -} - -char **expand_items(char **items, size_t *items_capacity, size_t to_fit) { +Item *expand_items(Item *items, size_t *items_capacity, size_t to_fit) { while (*items_capacity < to_fit) *items_capacity <<= 1; - return realloc(items, *items_capacity * sizeof(char *)); + return realloc(items, *items_capacity * sizeof(Item)); } void add_gear(FriendlyResults *results, size_t *items_capacity, char *gear_str) { @@ -101,18 +104,58 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gear_str) if (results->num_items >= *items_capacity) results->items = expand_items(results->items, items_capacity, results->num_items + 1); - results->items[results->num_items++] = strdup(gear_str); + Item item; + item.data = strdup(gear_str); + item.type = RARE; + results->items[results->num_items++] = item; +} + +void destroy_drif(Drif drif) { + free(drif.field1); + free(drif.field4); + free(drif.field11); + free(drif.field12); + free(drif.name); + free(drif.field18); + free(drif.field19); +} + +Drif get_drif(char *data, size_t length) { + Drif drif; + TypeEnum field_types[DRIF_FIELD_COUNT] = DRIF_FIELD_TYPES; + void *field_pointers[DRIF_FIELD_COUNT] = DRIF_FIELD_ADDRESSES(drif); + get_struct_data(DRIF_FIELD_COUNT, field_types, field_pointers, SEPARATOR_DRIF_INFO, data, length); + return drif; } void add_drifs(FriendlyResults *results, size_t *items_capacity, char *drifs_str) { if (strlen(drifs_str) == 0) return; - // TODO: parse - if (results->num_items >= *items_capacity) - results->items = expand_items(results->items, items_capacity, results->num_items + 1); + char *drifs_separator; + do { + drifs_separator = strstr(drifs_str, SEPARATOR_DRIFS); + Drif drif = get_drif(drifs_str, drifs_separator ? drifs_separator - drifs_str : strlen(drifs_str)); - results->items[results->num_items++] = strdup(drifs_str); + // if there are still drifs - modify drifs_str pointer + if (drifs_separator) + drifs_str = drifs_separator + strlen(SEPARATOR_DRIFS); + + // expand if needed + if (results->num_items >= *items_capacity) + results->items = expand_items(results->items, items_capacity, results->num_items + 1); + + Item item; + item.data = strdup(drif.name); + item.type = DRIF; + results->items[results->num_items++] = item; + + destroy_drif(drif); + } while (drifs_separator); +} + +char *get_item_name(int item_id) { + return item_id < id_to_item_name_len ? id_to_item_name[item_id] : "null"; } void add_items(FriendlyResults *results, size_t *items_capacity, char *items_str) { @@ -136,7 +179,10 @@ void add_items(FriendlyResults *results, size_t *items_capacity, char *items_str if (results->num_items >= *items_capacity) results->items = expand_items(results->items, items_capacity, results->num_items + 1); - results->items[results->num_items++] = strdup(buffer); + Item item; + item.data = strdup(buffer); + item.type = NORMAL; + results->items[results->num_items++] = item; } while (items_separator); } @@ -157,7 +203,7 @@ FriendlyResults parse_to_friendly(EntityResults entity_results) { // create items array and add to it gear, drifs and items results.num_items = 0; - results.items = malloc(INIT_ITEMS_CAPACITY * sizeof(char *)); + results.items = malloc(INIT_ITEMS_CAPACITY * sizeof(Item)); size_t items_cap = INIT_ITEMS_CAPACITY; add_gear(&results, &items_cap, entity_results.gear); add_drifs(&results, &items_cap, entity_results.drifs); diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index 3aadf09..76083ab 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -9,17 +9,16 @@ // Payload structure: // FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END -// -// EntityResults structure: -// field (SEPARATOR_RESULT field)* -// fields are defined in order in the EntityResults struct - #define FIGHT_RESULTS_START "3;19;" #define FIGHT_RESULTS_END "[--];" #define SEPARATOR_ENTITY "[--]" #define SEPARATOR_RESULT "&" +// EntityResults structure: +// field (SEPARATOR_RESULT field)* +// fields are defined in order in the EntityResults struct #define ENTITY_FIELD_COUNT 35 +// clang-format off #define ENTITY_FIELD_TYPES { \ /* team */ INT, \ /* name */ STRING, \ @@ -94,7 +93,7 @@ &entity_results.field34, \ &entity_results.saturation \ } - +// clang-format on typedef struct { int team; // friend or enemy char *name; @@ -137,6 +136,84 @@ typedef struct { #define INIT_ITEMS_CAPACITY 8 #define SINGLE_ITEM_BUFFER_LEN 128 #define SEPARATOR_ITEMS "[-]" +#define SEPARATOR_DRIFS "???" // TODO: change it to correct one which idk at this moment +#define SEPARATOR_DRIF_INFO "," +// Item struct - for keeping item string and type +typedef enum { NORMAL, DRIF, SYNG, SET, RARE, EPIC } ItemType; +typedef struct { + char *data; + ItemType type; +} Item; + +// Drif structure: +// field (SEPARATOR_DRIF field)* +// fields are defined in order in the Drif struct +#define DRIF_FIELD_COUNT 19 +// clang-format off +#define DRIF_FIELD_TYPES { \ + /* field1 */ STRING, \ + /* field2 */ INT, \ + /* field3 */ INT, \ + /* field4 */ STRING, \ + /* field5 */ INT, \ + /* field6 */ INT, \ + /* field7 */ FLOAT, \ + /* field8 */ FLOAT, \ + /* field9 */ INT, \ + /* field10 */ INT, \ + /* field11 */ STRING, \ + /* field12 */ STRING, \ + /* field13 */ INT, \ + /* field14 */ INT, \ + /* field15 */ INT, \ + /* field16 */ INT, \ + /* name */ STRING, \ + /* field18 */ STRING, \ + /* field19 */ STRING, \ +} +#define DRIF_FIELD_ADDRESSES(drif) {\ + &drif.field1, \ + &drif.field2, \ + &drif.field3, \ + &drif.field4, \ + &drif.field5, \ + &drif.field6, \ + &drif.field7, \ + &drif.field8, \ + &drif.field9, \ + &drif.field10, \ + &drif.field11, \ + &drif.field12, \ + &drif.field13, \ + &drif.field14, \ + &drif.field15, \ + &drif.field16, \ + &drif.name, \ + &drif.field18, \ + &drif.field19, \ +} +// clang-format on +typedef struct { + char *field1; + int field2; + int field3; + char *field4; + int field5; + int field6; + float field7; + float field8; + int field9; + int field10; + char *field11; + char *field12; + int field13; + int field14; + int field15; + int field16; + char *name; // drif + char *field18; + char *field19; +} Drif; // values of field "team" #define TEAM_FRIEND 1 @@ -147,7 +224,7 @@ typedef struct { char *name; int exp; int gold; - char **items; + Item *items; size_t num_items; int level; int psycho; diff --git a/analyzer/render.c b/analyzer/render.c index 402fcb5..28d1673 100644 --- a/analyzer/render.c +++ b/analyzer/render.c @@ -75,6 +75,24 @@ void print_column_names() { printf("\n"); } +int item_type_to_color(ItemType type) { + switch (type) { + case NORMAL: + return COLOR_WHITE; + case DRIF: + return COLOR_YELLOW; + case SYNG: + return COLOR_RED; + case SET: + return COLOR_GREEN; + case RARE: + return COLOR_AQUA; + case EPIC: + return COLOR_PURPLE; + } + return COLOR_WHITE; +} + void print_player_rewards(FriendlyResults results) { char buffers[TABLE_COLUMN_COUNT][TABLE_LINE_WIDTH]; size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; @@ -116,12 +134,13 @@ void print_player_rewards(FriendlyResults results) { size_t line_len = 0; for (size_t i = 0; i < results.num_items; i++) { char *sep = i < results.num_items - 1 ? ", " : ""; - size_t item_len = utf8_strlen(results.items[i]) + strlen(sep); + size_t item_len = utf8_strlen(results.items[i].data) + strlen(sep); if (line_len + item_len > TABLE_ITEMS_WIDTH) { printf("%*s" TABLE_LINE_END "\n" TABLE_LINE_START, (int)(TABLE_ITEMS_WIDTH - line_len), ""); line_len = 0; } - printf("%s%s", results.items[i], sep); + print_color(results.items[i].data, item_type_to_color(results.items[i].type)); + printf("%s", sep); line_len += item_len; } printf("%*s" TABLE_LINE_END "\n", (int)(TABLE_ITEMS_WIDTH - line_len), ""); diff --git a/analyzer/render.h b/analyzer/render.h index d54015a..97958d1 100644 --- a/analyzer/render.h +++ b/analyzer/render.h @@ -6,10 +6,11 @@ // ANSI escape sequence colors #define COLOR_RED 91 #define COLOR_GREEN 92 -#define COLOR_WHITE 97 #define COLOR_YELLOW 93 -#define COLOR_AQUA 96 #define COLOR_BLUE 94 +#define COLOR_PURPLE 95 +#define COLOR_AQUA 96 +#define COLOR_WHITE 97 #define TABLE_ROW_SEPARATOR_CHAR '-' @@ -29,6 +30,7 @@ #define TABLE_SATURATION_WIDTH 13 #define TABLE_SPLINTERS_WIDTH 9 +// clang-format off #define TABLE_COLUMN_NAMES {"Nick (LVL)", "EXP", "Gold", "Psycho", "Saturation", "Splinters"} #define TABLE_COLUMN_COLORS {COLOR_WHITE, COLOR_WHITE, COLOR_YELLOW, COLOR_AQUA, COLOR_GREEN, COLOR_BLUE} #define TABLE_COLUMN_WIDTHS { \ @@ -51,6 +53,7 @@ (TABLE_COLUMN_COUNT - 1) * TABLE_LINE_SEPARATOR_WIDTH + \ TABLE_LINE_END_WIDTH \ ) +// clang-format on #define TABLE_ITEMS_WIDTH (TABLE_LINE_WIDTH - TABLE_LINE_START_WIDTH - TABLE_LINE_END_WIDTH) -- cgit v1.2.3 From 5c0e713713143bad475a92d20ee5fe215b5bafd3 Mon Sep 17 00:00:00 2001 From: Mosder Date: Tue, 21 Jul 2026 17:26:09 +0200 Subject: feat: added parsing for gear --- Makefile | 1 + analyzer/parser/fight_results.c | 169 +++++++++++++++--- analyzer/parser/fight_results.h | 375 ++++++++++++++++++++++++++++++---------- analyzer/render.c | 2 + analyzer/render.h | 24 +-- analyzer/utils/roman.c | 16 ++ analyzer/utils/roman.h | 12 ++ csv/artifact_sizes.csv | 5 + csv/orbs.csv | 3 + 9 files changed, 482 insertions(+), 125 deletions(-) create mode 100644 analyzer/utils/roman.c create mode 100644 analyzer/utils/roman.h create mode 100644 csv/artifact_sizes.csv create mode 100644 csv/orbs.csv (limited to 'analyzer/render.c') diff --git a/Makefile b/Makefile index afb61fd..72f94e0 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,7 @@ utils: $(CC) $(CFLAGS) -c analyzer/utils/time.c $(CC) $(CFLAGS) -c analyzer/utils/tcp.c $(CC) $(CFLAGS) -c analyzer/utils/tcp_seq_math.c + $(CC) $(CFLAGS) -c analyzer/utils/roman.c parser: $(CC) $(CFLAGS) -c analyzer/parser/parser.c diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index f201fb1..c1d6420 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -4,8 +4,11 @@ #include #include +#include "csv/id_to_artifact_size.h" #include "csv/id_to_item_name.h" +#include "csv/id_to_orb_name.h" #include "csv/id_to_saturation_type.h" +#include "utils/roman.h" // function to cleanup fight results to be used in ParsedData struct void cleanup_fight_results(void *data) { @@ -70,15 +73,18 @@ void get_struct_data(size_t field_count, TypeEnum *field_types, void **field_poi } } +// free struct fields which are STRING +void destroy_struct(size_t field_count, TypeEnum *field_types, void **field_pointers) { + for (size_t i = 0; i < field_count; i++) { + if (field_types[i] == STRING) + free(*(char **)field_pointers[i]); + } +} + void destroy_entity_results(EntityResults results) { - free(results.name); - free(results.field7); - free(results.gear); - free(results.items); - free(results.drifs); - free(results.field29); - free(results.field34); - free(results.saturation); + TypeEnum field_types[ENTITY_FIELD_COUNT] = ENTITY_FIELD_TYPES; + void *field_pointers[ENTITY_FIELD_COUNT] = ENTITY_FIELD_ADDRESSES(results); + destroy_struct(ENTITY_FIELD_COUNT, field_types, field_pointers); } // get entity results from the string @@ -96,35 +102,148 @@ Item *expand_items(Item *items, size_t *items_capacity, size_t to_fit) { return realloc(items, *items_capacity * sizeof(Item)); } -void add_gear(FriendlyResults *results, size_t *items_capacity, char *gear_str) { - if (strlen(gear_str) == 0) +void destroy_gear(Gear gear) { + TypeEnum field_types[GEAR_FIELD_COUNT] = GEAR_FIELD_TYPES; + void *field_pointers[GEAR_FIELD_COUNT] = GEAR_FIELD_ADDRESSES(gear); + destroy_struct(GEAR_FIELD_COUNT, field_types, field_pointers); +} + +Gear get_gear(char *data, size_t length) { + Gear gear; + TypeEnum field_types[GEAR_FIELD_COUNT] = GEAR_FIELD_TYPES; + void *field_pointers[GEAR_FIELD_COUNT] = GEAR_FIELD_ADDRESSES(gear); + get_struct_data(GEAR_FIELD_COUNT, field_types, field_pointers, SEPARATOR_GEAR_FIELDS, data, length); + return gear; +} + +void destroy_gear_info(GearInfo gear_info) { + TypeEnum field_types[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_TYPES; + void *field_pointers[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_ADDRESSES(gear_info); + destroy_struct(GEAR_INFO_FIELD_COUNT, field_types, field_pointers); +} + +GearInfo get_gear_info(char *data, size_t length) { + GearInfo gear_info; + TypeEnum field_types[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_TYPES; + void *field_pointers[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_ADDRESSES(gear_info); + get_struct_data(GEAR_INFO_FIELD_COUNT, field_types, field_pointers, SEPARATOR_GEAR_INFO_FIELDS, data, length); + return gear_info; +} + +void destroy_orb(Orb orb) { + TypeEnum field_types[ORB_FIELD_COUNT] = ORB_FIELD_TYPES; + void *field_pointers[ORB_FIELD_COUNT] = ORB_FIELD_ADDRESSES(orb); + destroy_struct(ORB_FIELD_COUNT, field_types, field_pointers); +} + +Orb get_orb(char *data, size_t length) { + Orb orb; + TypeEnum field_types[ORB_FIELD_COUNT] = ORB_FIELD_TYPES; + void *field_pointers[ORB_FIELD_COUNT] = ORB_FIELD_ADDRESSES(orb); + get_struct_data(ORB_FIELD_COUNT, field_types, field_pointers, SEPARATOR_ORB_FIELDS, data, length); + return orb; +} + +char *get_artifact_size(int artifact_size_id) { + return artifact_size_id < id_to_artifact_size_len ? id_to_artifact_size[artifact_size_id] : "null"; +} + +char *get_orb_name(int orb_name_id) { + return orb_name_id < id_to_orb_name_len ? id_to_orb_name[orb_name_id] : "null"; +} + +void get_stars(char *buffer, int incr_above_b1) { + char star_types[] = {'B', 'S', 'G'}; + buffer[0] = star_types[incr_above_b1 / 3]; + buffer[1] = incr_above_b1 % 3 + 1 + '0'; + buffer[2] = '\0'; +} + +void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) { + if (strlen(gears_str) == 0) return; - // TODO: parse - if (results->num_items >= *items_capacity) - results->items = expand_items(results->items, items_capacity, results->num_items + 1); + char *gears_separator; + do { + gears_separator = strstr(gears_str, SEPARATOR_GEARS); + Gear gear = get_gear(gears_str, gears_separator ? gears_separator - gears_str : strlen(gears_str)); + + // if there are still gears - modify gears_str pointer + if (gears_separator) + gears_str = gears_separator + strlen(SEPARATOR_DRIFS); + + // expand if needed + if (results->num_items >= *items_capacity) + results->items = expand_items(results->items, items_capacity, results->num_items + 1); + + Item item; + + GearInfo gear_info = get_gear_info(gear.info, strlen(gear.info)); + char buffer[SINGLE_ITEM_BUFFER_LEN]; + char roman[5]; + to_roman(roman, gear_info.rank); + sprintf(buffer, "[%s] %s", roman, gear_info.name); + switch (gear.type) { + case TYPE_SYNG_NORMAL: + // atoi should be fine, since syng_tier starts from 1 for syngs + if (atoi(gear.syng_tier)) + sprintf(buffer, "%s (%d)", buffer, gear_info.lvl); + break; + default: { + char stars[3]; + get_stars(stars, gear_info.stars); + sprintf(buffer, "%s (%s)", buffer, stars); + break; + } + } + item.data = strdup(buffer); + destroy_gear_info(gear_info); - Item item; - item.data = strdup(gear_str); - item.type = RARE; - results->items[results->num_items++] = item; + switch (gear.type) { + case TYPE_SYNG_NORMAL: + // atoi should be fine, since syng_tier starts from 1 for syngs + item.type = atoi(gear.syng_tier) ? SYNG : NORMAL; + break; + case TYPE_SET: + item.type = SET; + break; + case TYPE_RARE: + item.type = RARE; + break; + default: + item.type = EPIC; + break; + } + + results->items[results->num_items++] = item; + + // add orb to items if it exists + if (strlen(gear.orb)) { + Item item; + char buffer[SINGLE_ITEM_BUFFER_LEN]; + Orb orb = get_orb(gear.orb, strlen(gear.orb)); + sprintf(buffer, "%sorb %s", get_artifact_size(orb.size), get_orb_name(orb.id)); + item.data = strdup(buffer); + destroy_orb(orb); + item.type = ORB; + results->items[results->num_items++] = item; + } + + destroy_gear(gear); + } while (gears_separator); } void destroy_drif(Drif drif) { - free(drif.field1); - free(drif.field4); - free(drif.field11); - free(drif.field12); - free(drif.name); - free(drif.field18); - free(drif.field19); + TypeEnum field_types[DRIF_FIELD_COUNT] = DRIF_FIELD_TYPES; + void *field_pointers[DRIF_FIELD_COUNT] = DRIF_FIELD_ADDRESSES(drif); + destroy_struct(DRIF_FIELD_COUNT, field_types, field_pointers); } Drif get_drif(char *data, size_t length) { Drif drif; TypeEnum field_types[DRIF_FIELD_COUNT] = DRIF_FIELD_TYPES; void *field_pointers[DRIF_FIELD_COUNT] = DRIF_FIELD_ADDRESSES(drif); - get_struct_data(DRIF_FIELD_COUNT, field_types, field_pointers, SEPARATOR_DRIF_INFO, data, length); + get_struct_data(DRIF_FIELD_COUNT, field_types, field_pointers, SEPARATOR_DRIF_FIELDS, data, length); return drif; } diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index 76083ab..59d1e45 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -14,6 +14,10 @@ #define SEPARATOR_ENTITY "[--]" #define SEPARATOR_RESULT "&" +// values of field "team" in EntityResults +#define TEAM_FRIEND 1 +#define TEAM_ENEMY 2 + // EntityResults structure: // field (SEPARATOR_RESULT field)* // fields are defined in order in the EntityResults struct @@ -54,44 +58,44 @@ /* field32 */ INT, \ /* field33 */ INT, \ /* field34 */ STRING, \ - /* saturation */ STRING \ + /* saturation */ STRING, \ } #define ENTITY_FIELD_ADDRESSES(entity_results) {\ - &entity_results.team, \ - &entity_results.name, \ - &entity_results.exp, \ - &entity_results.field4, \ - &entity_results.gold, \ - &entity_results.field6, \ - &entity_results.field7, \ - &entity_results.gear, \ - &entity_results.field9, \ - &entity_results.items, \ - &entity_results.field11, \ - &entity_results.field12, \ - &entity_results.field13, \ - &entity_results.field14, \ - &entity_results.field15, \ - &entity_results.level, \ - &entity_results.field17, \ - &entity_results.field18, \ - &entity_results.field19, \ - &entity_results.field20, \ - &entity_results.field21, \ - &entity_results.field22, \ - &entity_results.field23, \ - &entity_results.field24, \ - &entity_results.psycho, \ - &entity_results.drifs, \ - &entity_results.splinters, \ - &entity_results.field28, \ - &entity_results.field29, \ - &entity_results.field30, \ - &entity_results.field31, \ - &entity_results.field32, \ - &entity_results.field33, \ - &entity_results.field34, \ - &entity_results.saturation \ + &entity_results.team, \ + &entity_results.name, \ + &entity_results.exp, \ + &entity_results.field4, \ + &entity_results.gold, \ + &entity_results.field6, \ + &entity_results.field7, \ + &entity_results.gear, \ + &entity_results.field9, \ + &entity_results.items, \ + &entity_results.field11, \ + &entity_results.field12, \ + &entity_results.field13, \ + &entity_results.field14, \ + &entity_results.field15, \ + &entity_results.level, \ + &entity_results.field17, \ + &entity_results.field18, \ + &entity_results.field19, \ + &entity_results.field20, \ + &entity_results.field21, \ + &entity_results.field22, \ + &entity_results.field23, \ + &entity_results.field24, \ + &entity_results.psycho, \ + &entity_results.drifs, \ + &entity_results.splinters, \ + &entity_results.field28, \ + &entity_results.field29, \ + &entity_results.field30, \ + &entity_results.field31, \ + &entity_results.field32, \ + &entity_results.field33, \ + &entity_results.field34, \ + &entity_results.saturation, \ } // clang-format on typedef struct { @@ -137,87 +141,280 @@ typedef struct { #define SINGLE_ITEM_BUFFER_LEN 128 #define SEPARATOR_ITEMS "[-]" #define SEPARATOR_DRIFS "???" // TODO: change it to correct one which idk at this moment -#define SEPARATOR_DRIF_INFO "," +#define SEPARATOR_GEARS "???" // TODO: change it to correct one which idk at this moment // Item struct - for keeping item string and type -typedef enum { NORMAL, DRIF, SYNG, SET, RARE, EPIC } ItemType; +typedef enum { NORMAL, DRIF, ORB, SYNG, SET, RARE, EPIC } ItemType; typedef struct { char *data; ItemType type; } Item; // Drif structure: -// field (SEPARATOR_DRIF field)* +// field (SEPARATOR_DRIF_FIELDS field)* // fields are defined in order in the Drif struct -#define DRIF_FIELD_COUNT 19 +#define DRIF_FIELD_COUNT 18 +#define SEPARATOR_DRIF_FIELDS "," // clang-format off #define DRIF_FIELD_TYPES { \ - /* field1 */ STRING, \ - /* field2 */ INT, \ - /* field3 */ INT, \ - /* field4 */ STRING, \ - /* field5 */ INT, \ - /* field6 */ INT, \ - /* field7 */ FLOAT, \ - /* field8 */ FLOAT, \ - /* field9 */ INT, \ - /* field10 */ INT, \ - /* field11 */ STRING, \ - /* field12 */ STRING, \ - /* field13 */ INT, \ - /* field14 */ INT, \ - /* field15 */ INT, \ - /* field16 */ INT, \ - /* name */ STRING, \ - /* field18 */ STRING, \ - /* field19 */ STRING, \ + /* field1 */ STRING, \ + /* field2 */ INT, \ + /* size */ INT, \ + /* field4 */ STRING, \ + /* field5 */ INT, \ + /* id */ INT, \ + /* value1 */ FLOAT, \ + /* value2 */ FLOAT, \ + /* field9 */ INT, \ + /* field10 */ INT, \ + /* field11 */ STRING, \ + /* size_str */ STRING, \ + /* field13 */ INT, \ + /* power */ INT, \ + /* field15 */ INT, \ + /* field16 */ INT, \ + /* name */ STRING, \ + /* field18 */ STRING, \ } #define DRIF_FIELD_ADDRESSES(drif) {\ - &drif.field1, \ - &drif.field2, \ - &drif.field3, \ - &drif.field4, \ - &drif.field5, \ - &drif.field6, \ - &drif.field7, \ - &drif.field8, \ - &drif.field9, \ - &drif.field10, \ - &drif.field11, \ - &drif.field12, \ - &drif.field13, \ - &drif.field14, \ - &drif.field15, \ - &drif.field16, \ - &drif.name, \ - &drif.field18, \ - &drif.field19, \ + &drif.field1, \ + &drif.field2, \ + &drif.size, \ + &drif.field4, \ + &drif.field5, \ + &drif.id, \ + &drif.value1, \ + &drif.value2, \ + &drif.field9, \ + &drif.field10, \ + &drif.field11, \ + &drif.size_str, \ + &drif.field13, \ + &drif.power, \ + &drif.field15, \ + &drif.field16, \ + &drif.name, \ + &drif.field18, \ } // clang-format on typedef struct { - char *field1; + char *field1; // just says "drif" int field2; - int field3; + int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy char *field4; int field5; - int field6; - float field7; - float field8; + int id; // id of orb type + float value1; // value1, value2 (in %) - one is start value, the other is value per lvl (both are the same) + float value2; // value1, value2 (in %) - one is start value, the other is value per lvl (both are the same) int field9; int field10; char *field11; - char *field12; + char *size_str; // sub - small, medium - bi, big - magni, ??? - arcy int field13; - int field14; + int power; // how much space it takes int field15; int field16; char *name; // drif char *field18; - char *field19; } Drif; -// values of field "team" -#define TEAM_FRIEND 1 -#define TEAM_ENEMY 2 +// Gear structure: +// field (SEPARATOR_GEAR_FIELDS field)* +// fields are defined in order in the Gear struct +#define GEAR_FIELD_COUNT 19 +#define SEPARATOR_GEAR_FIELDS "$" +// values of field "type" in Gear +#define TYPE_SYNG_NORMAL 0 +#define TYPE_SET 1 +#define TYPE_RARE 2 +// clang-format off +#define GEAR_FIELD_TYPES { \ + /* stats */ STRING, \ + /* resistances */ STRING, \ + /* info */ STRING, \ + /* reqs */ STRING, \ + /* field5 */ STRING, \ + /* psycho */ STRING, \ + /* field7 */ STRING, \ + /* field8 */ STRING, \ + /* field9 */ STRING, \ + /* syng_tier */ STRING, \ + /* field11 */ INT, \ + /* field12 */ INT, \ + /* field13 */ INT, \ + /* field14 */ STRING, \ + /* type */ INT, \ + /* field16 */ INT, \ + /* orb */ STRING, \ + /* field18 */ STRING, \ + /* field19 */ STRING, \ +} +#define GEAR_FIELD_ADDRESSES(gear) {\ + &gear.stats, \ + &gear.resistances, \ + &gear.info, \ + &gear.reqs, \ + &gear.field5, \ + &gear.psycho, \ + &gear.field7, \ + &gear.field8, \ + &gear.field9, \ + &gear.syng_tier, \ + &gear.field11, \ + &gear.field12, \ + &gear.field13, \ + &gear.field14, \ + &gear.type, \ + &gear.field16, \ + &gear.orb, \ + &gear.field18, \ + &gear.field19, \ +} +// clang-format on +typedef struct { + char *stats; // strength, dexterity, power, knowledge, hp, mana, stamina + char *resistances; // slashing, crushing, piercing, fire, cold, energy, mental + char *info; // GearInfo struct data + char *reqs; // requirements (str, dex, pow, know), weight, something else + char *field5; + char *psycho; // psycho effects (id,%) for syngs + char *field7; + char *field8; + char *field9; + char *syng_tier; // lvl 20 - 1, lvl 30 - 2, etc..., null for non-syngs + int field11; + int field12; + int field13; + char *field14; + int type; // 0 - normal item or syng, 1 - set, 2 - rare + int field16; // 10 for rare, -1 for others ??? + char *orb; // orb info + char *field18; + char *field19; +} Gear; + +// GearInfo structure: +// field (SEPARATOR_GEAR_INFO_FIELDS field)* +// fields are defined in order in the GearInfo struct +#define GEAR_INFO_FIELD_COUNT 23 +#define SEPARATOR_GEAR_INFO_FIELDS "," +// clang-format off +#define GEAR_INFO_FIELD_TYPES { \ + /* name */ STRING, \ + /* field2 */ INT, \ + /* lvl */ INT, \ + /* price */ INT, \ + /* field5 */ INT, \ + /* field6 */ INT, \ + /* field7 */ INT, \ + /* rank */ INT, \ + /* field9 */ INT, \ + /* field10 */ INT, \ + /* field11 */ INT, \ + /* durability */ INT, \ + /* field13 */ STRING, \ + /* field14 */ INT, \ + /* field15 */ INT, \ + /* charges */ FLOAT, \ + /* field17 */ STRING, \ + /* field18 */ INT, \ + /* stars */ INT, \ + /* field20 */ INT, \ + /* field21 */ INT, \ + /* field22 */ INT, \ + /* field23 */ INT, \ +} +#define GEAR_INFO_FIELD_ADDRESSES(gear_info) {\ + &gear_info.name, \ + &gear_info.field2, \ + &gear_info.lvl, \ + &gear_info.price, \ + &gear_info.field5, \ + &gear_info.field6, \ + &gear_info.field7, \ + &gear_info.rank, \ + &gear_info.field9, \ + &gear_info.field10, \ + &gear_info.field11, \ + &gear_info.durability, \ + &gear_info.field13, \ + &gear_info.field14, \ + &gear_info.field15, \ + &gear_info.charges, \ + &gear_info.field17, \ + &gear_info.field18, \ + &gear_info.stars, \ + &gear_info.field20, \ + &gear_info.field21, \ + &gear_info.field22, \ + &gear_info.field23, \ +} +// clang-format on +typedef struct { + char *name; // item name + int field2; + int lvl; // level requirement + int price; // gold price value + int field5; + int field6; + int field7; + int rank; // I-XII + int field9; + int field10; + int field11; + int durability; // in % + char *field13; + int field14; + int field15; + float charges; // charge count + char *field17; + int field18; + int stars; // incrustation level: 0 - b1, 1 - b2, ... (-100 for syngs) + int field20; + int field21; + int field22; + int field23; +} GearInfo; + +// Orb structure: +// field (SEPARATOR_ORB_FIELDS field)* +// fields are defined in order in the Orb struct +#define ORB_FIELD_COUNT 9 +#define SEPARATOR_ORB_FIELDS "," +// clang-format off +#define ORB_FIELD_TYPES { \ + /* field1 */ STRING, \ + /* field2 */ INT, \ + /* field3 */ INT, \ + /* field4 */ STRING, \ + /* field5 */ INT, \ + /* id */ INT, \ + /* size */ INT, \ + /* value */ FLOAT, \ + /* field9 */ STRING, \ +} +#define ORB_FIELD_ADDRESSES(orb) {\ + &orb.field1, \ + &orb.field2, \ + &orb.field3, \ + &orb.field4, \ + &orb.field5, \ + &orb.id, \ + &orb.size, \ + &orb.value, \ + &orb.field9, \ +} +// clang-format on +typedef struct { + char *field1; // just says "orb" + int field2; + int field3; + char *field4; + int field5; + int id; // id of orb type + int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy + float value; // value in % you gain from the orb + char *field9; +} Orb; // fight results data for friendly typedef struct { diff --git a/analyzer/render.c b/analyzer/render.c index 28d1673..0fdd334 100644 --- a/analyzer/render.c +++ b/analyzer/render.c @@ -81,6 +81,8 @@ int item_type_to_color(ItemType type) { return COLOR_WHITE; case DRIF: return COLOR_YELLOW; + case ORB: + return COLOR_GOLD; case SYNG: return COLOR_RED; case SET: diff --git a/analyzer/render.h b/analyzer/render.h index 97958d1..843cea5 100644 --- a/analyzer/render.h +++ b/analyzer/render.h @@ -4,6 +4,8 @@ #include "parser/fight_results.h" // ANSI escape sequence colors +#define COLOR_GOLD 33 +#define COLOR_GRAY 90 #define COLOR_RED 91 #define COLOR_GREEN 92 #define COLOR_YELLOW 93 @@ -34,24 +36,24 @@ #define TABLE_COLUMN_NAMES {"Nick (LVL)", "EXP", "Gold", "Psycho", "Saturation", "Splinters"} #define TABLE_COLUMN_COLORS {COLOR_WHITE, COLOR_WHITE, COLOR_YELLOW, COLOR_AQUA, COLOR_GREEN, COLOR_BLUE} #define TABLE_COLUMN_WIDTHS { \ - TABLE_NAME_LVL_WIDTH, \ - TABLE_EXP_WIDTH, \ - TABLE_GOLD_WIDTH, \ - TABLE_PSYCHO_WIDTH, \ + TABLE_NAME_LVL_WIDTH, \ + TABLE_EXP_WIDTH, \ + TABLE_GOLD_WIDTH, \ + TABLE_PSYCHO_WIDTH, \ TABLE_SATURATION_WIDTH, \ - TABLE_SPLINTERS_WIDTH \ + TABLE_SPLINTERS_WIDTH, \ } #define TABLE_LINE_WIDTH ( \ TABLE_LINE_START_WIDTH + \ - TABLE_NAME_LVL_WIDTH + \ - TABLE_EXP_WIDTH + \ - TABLE_GOLD_WIDTH + \ - TABLE_PSYCHO_WIDTH + \ + TABLE_NAME_LVL_WIDTH + \ + TABLE_EXP_WIDTH + \ + TABLE_GOLD_WIDTH + \ + TABLE_PSYCHO_WIDTH + \ TABLE_SATURATION_WIDTH + \ - TABLE_SPLINTERS_WIDTH + \ + TABLE_SPLINTERS_WIDTH + \ (TABLE_COLUMN_COUNT - 1) * TABLE_LINE_SEPARATOR_WIDTH + \ - TABLE_LINE_END_WIDTH \ + TABLE_LINE_END_WIDTH \ ) // clang-format on diff --git a/analyzer/utils/roman.c b/analyzer/utils/roman.c new file mode 100644 index 0000000..9ea2bbb --- /dev/null +++ b/analyzer/utils/roman.c @@ -0,0 +1,16 @@ +#include "utils/roman.h" + +#include +#include + +void to_roman(char *buffer, int number) { + buffer[0] = '\0'; + int nums[ROMAN_CHAR_COUNT] = ROMAN_NUMS; + char *chars[ROMAN_CHAR_COUNT] = ROMAN_CHARS; + for (size_t i = 0; i < ROMAN_CHAR_COUNT; i++) { + for (size_t j = 0; j < number / nums[i]; j++) { + strcat(buffer, chars[i]); + } + number %= nums[i]; + } +} diff --git a/analyzer/utils/roman.h b/analyzer/utils/roman.h new file mode 100644 index 0000000..b4f5d3f --- /dev/null +++ b/analyzer/utils/roman.h @@ -0,0 +1,12 @@ +// utils/roman.h +// functions for roman numbers + +#define ROMAN_CHAR_COUNT 13 +#define ROMAN_NUMS {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1} +#define ROMAN_CHARS {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"} + +// convert a arabic number to roman number +// params: +// buffer - buffer to save the roman number to +// number - arabic number to convert +extern void to_roman(char *buffer, int number); diff --git a/csv/artifact_sizes.csv b/csv/artifact_sizes.csv new file mode 100644 index 0000000..a9af916 --- /dev/null +++ b/csv/artifact_sizes.csv @@ -0,0 +1,5 @@ +id,artifact_size +1,Sub +2,Bi +3,Magni +4,Arcy diff --git a/csv/orbs.csv b/csv/orbs.csv new file mode 100644 index 0000000..02dfde4 --- /dev/null +++ b/csv/orbs.csv @@ -0,0 +1,3 @@ +id,orb_name +21,Ha'at +12,Se'at -- cgit v1.2.3 From 5d463a7f4c721629a1996be6d19634338290a539 Mon Sep 17 00:00:00 2001 From: Mosder Date: Wed, 22 Jul 2026 01:16:45 +0200 Subject: refactor: generalized parser structs with offsetof and put them into separate files --- Makefile | 1 + analyzer/parser/fight_results/drif.h | 52 ++++ analyzer/parser/fight_results/entity.h | 67 +++++ analyzer/parser/fight_results/fight_results.c | 177 +++--------- analyzer/parser/fight_results/fight_results.h | 396 +------------------------- analyzer/parser/fight_results/gear.h | 45 +++ analyzer/parser/fight_results/gear_info.h | 44 +++ analyzer/parser/fight_results/orb.h | 33 +++ analyzer/parser/find_parse_functions.h | 6 +- analyzer/parser/parser.c | 3 +- analyzer/parser/parser_defs.h | 2 - analyzer/parser/structs.c | 49 ++++ analyzer/parser/structs.h | 33 +++ analyzer/render.c | 24 +- analyzer/render.h | 53 ++-- analyzer/utils/memory.h | 4 + analyzer/utils/roman.c | 13 +- analyzer/utils/roman.h | 23 +- 18 files changed, 429 insertions(+), 596 deletions(-) create mode 100644 analyzer/parser/fight_results/drif.h create mode 100644 analyzer/parser/fight_results/entity.h create mode 100644 analyzer/parser/fight_results/gear.h create mode 100644 analyzer/parser/fight_results/gear_info.h create mode 100644 analyzer/parser/fight_results/orb.h create mode 100644 analyzer/parser/structs.c create mode 100644 analyzer/parser/structs.h create mode 100644 analyzer/utils/memory.h (limited to 'analyzer/render.c') diff --git a/Makefile b/Makefile index f45b9e6..31c1275 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ utils: parser: $(CC) $(CFLAGS) -c analyzer/parser/parser.c + $(CC) $(CFLAGS) -c analyzer/parser/structs.c $(CC) $(CFLAGS) -c analyzer/parser/fight_results/fight_results.c capture: diff --git a/analyzer/parser/fight_results/drif.h b/analyzer/parser/fight_results/drif.h new file mode 100644 index 0000000..1f8b3e2 --- /dev/null +++ b/analyzer/parser/fight_results/drif.h @@ -0,0 +1,52 @@ +// parser/fight_results/drif.h +// Drif generic struct data + +#include "parser/structs.h" + +// Drif structure: +// field (SEPARATOR_DRIF_FIELD field)* +// fields are defined in order in the Drif struct +#define SEPARATOR_DRIF "???" // TODO: change it to correct one which idk at this moment +#define SEPARATOR_DRIF_FIELD "," + +typedef struct { + char *field1; // just says "drif" + int field2; + int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy + char *field4; + int field5; + int id; // id of orb type + float value1; // value1, value2 (in %) - one is start value, the other is value per lvl (both are the same) + float value2; // value1, value2 (in %) - one is start value, the other is value per lvl (both are the same) + int field9; + int field10; + char *field11; + char *size_str; // sub - small, medium - bi, big - magni, ??? - arcy + int field13; + int power; // how much space it takes + int field15; + int field16; + char *name; // drif + char *field18; +} Drif; + +static const StructField drif_fields[] = { + {STRING, offsetof(Drif, field1)}, + {INT, offsetof(Drif, field2)}, + {INT, offsetof(Drif, size)}, + {STRING, offsetof(Drif, field4)}, + {INT, offsetof(Drif, field5)}, + {INT, offsetof(Drif, id)}, + {FLOAT, offsetof(Drif, value1)}, + {FLOAT, offsetof(Drif, value2)}, + {INT, offsetof(Drif, field9)}, + {INT, offsetof(Drif, field10)}, + {STRING, offsetof(Drif, field11)}, + {STRING, offsetof(Drif, size_str)}, + {INT, offsetof(Drif, field13)}, + {INT, offsetof(Drif, power)}, + {INT, offsetof(Drif, field15)}, + {INT, offsetof(Drif, field16)}, + {STRING, offsetof(Drif, name)}, + {STRING, offsetof(Drif, field18)}, +}; diff --git a/analyzer/parser/fight_results/entity.h b/analyzer/parser/fight_results/entity.h new file mode 100644 index 0000000..8e082ec --- /dev/null +++ b/analyzer/parser/fight_results/entity.h @@ -0,0 +1,67 @@ +// parser/fight_results/entity.h +// EntityResults generic struct data + +#include "parser/structs.h" + +// EntityResults structure: +// field (SEPARATOR_ENTITY_FIELD field)* +// fields are defined in order in the EntityResults struct +#define SEPARATOR_ENTITY "[--]" +#define SEPARATOR_ENTITY_FIELD "&" + +// values of field "team" in EntityResults +#define TEAM_FRIEND 1 +#define TEAM_ENEMY 2 + +typedef struct { + int team; // friend or enemy + char *name; + int exp; + float field4; + int gold; + float field6; + char *field7; + char *gear; // rares, sets, syngs, orbs + int field9; + char *items; // regular items + int field11; + int field12; + float field13; + float field14; // some multiplier most likely exp/gold/psycho/something else idk + float field15; + int level; + double field17; + float field18; + int field19; + int field20; + float field21; + float field22; + int field23; + int field24; + int psycho; + char *drifs; + int splinters; + int field28; + char *field29; + int field30; + int field31; + int field32; + int field33; + char *field34; + char *saturation; +} EntityResults; + +static const StructField entity_fields[] = { + {INT, offsetof(EntityResults, team)}, {STRING, offsetof(EntityResults, name)}, {INT, offsetof(EntityResults, exp)}, + {FLOAT, offsetof(EntityResults, field4)}, {INT, offsetof(EntityResults, gold)}, {FLOAT, offsetof(EntityResults, field6)}, + {STRING, offsetof(EntityResults, field7)}, {STRING, offsetof(EntityResults, gear)}, {INT, offsetof(EntityResults, field9)}, + {STRING, offsetof(EntityResults, items)}, {INT, offsetof(EntityResults, field11)}, {INT, offsetof(EntityResults, field12)}, + {FLOAT, offsetof(EntityResults, field13)}, {FLOAT, offsetof(EntityResults, field14)}, {FLOAT, offsetof(EntityResults, field15)}, + {INT, offsetof(EntityResults, level)}, {DOUBLE, offsetof(EntityResults, field17)}, {FLOAT, offsetof(EntityResults, field18)}, + {INT, offsetof(EntityResults, field19)}, {INT, offsetof(EntityResults, field20)}, {FLOAT, offsetof(EntityResults, field21)}, + {FLOAT, offsetof(EntityResults, field22)}, {INT, offsetof(EntityResults, field23)}, {INT, offsetof(EntityResults, field24)}, + {INT, offsetof(EntityResults, psycho)}, {STRING, offsetof(EntityResults, drifs)}, {INT, offsetof(EntityResults, splinters)}, + {INT, offsetof(EntityResults, field28)}, {STRING, offsetof(EntityResults, field29)}, {INT, offsetof(EntityResults, field30)}, + {INT, offsetof(EntityResults, field31)}, {INT, offsetof(EntityResults, field32)}, {INT, offsetof(EntityResults, field33)}, + {STRING, offsetof(EntityResults, field34)}, {STRING, offsetof(EntityResults, saturation)}, +}; diff --git a/analyzer/parser/fight_results/fight_results.c b/analyzer/parser/fight_results/fight_results.c index 4f0f3a1..e7c7123 100644 --- a/analyzer/parser/fight_results/fight_results.c +++ b/analyzer/parser/fight_results/fight_results.c @@ -8,6 +8,13 @@ #include "csv/id_to_item_name.h" #include "csv/id_to_orb_name.h" #include "csv/id_to_saturation_type.h" +#include "parser/fight_results/drif.h" +#include "parser/fight_results/entity.h" +#include "parser/fight_results/gear.h" +#include "parser/fight_results/gear_info.h" +#include "parser/fight_results/orb.h" +#include "parser/structs.h" +#include "utils/memory.h" #include "utils/roman.h" // function to cleanup fight results to be used in ParsedData struct @@ -29,126 +36,20 @@ byte_t *find_fight_results(TCPStream *stream, int offset, int *length) { byte_t *start = memmem(stream->data + offset, stream->len - offset, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); byte_t *end = NULL; if (start) - end = memmem(start, stream->len - offset, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); + end = memmem(start, stream->len - (start - stream->data), FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); *length = end ? end - start + strlen(FIGHT_RESULTS_END) : -1; return start; } -// save to a field in struct depending on the type -void save_to_struct(TypeEnum type, void *field, char *src, size_t src_len) { - char *str = malloc(src_len + 1); - memcpy(str, src, src_len); - str[src_len] = '\0'; - - // parse depending on variable type - switch (type) { - case INT: - sscanf(str, "%d", (int *)field); - free(str); - break; - case FLOAT: - sscanf(str, "%f", (float *)field); - free(str); - break; - case DOUBLE: - sscanf(str, "%lf", (double *)field); - free(str); - break; - case STRING: - *(char **)field = str; - break; - } -} - -// fill struct fields from given data of given length -void get_struct_data(size_t field_count, TypeEnum *field_types, void **field_pointers, char *data_separator, char *data, size_t data_len) { - for (size_t i = 0; i < field_count; i++) { - char *sep = memmem(data, data_len, data_separator, strlen(data_separator)); - size_t len = sep ? sep - data : data_len; - save_to_struct(field_types[i], field_pointers[i], (char *)data, len); - - // update data pointer and length - if (sep) { - data_len -= sep - data + strlen(data_separator); - data = sep + strlen(data_separator); - } - } -} - -// free struct fields which are STRING -void destroy_struct(size_t field_count, TypeEnum *field_types, void **field_pointers) { - for (size_t i = 0; i < field_count; i++) { - if (field_types[i] == STRING) - free(*(char **)field_pointers[i]); - } -} - -void destroy_entity_results(EntityResults results) { - TypeEnum field_types[ENTITY_FIELD_COUNT] = ENTITY_FIELD_TYPES; - void *field_pointers[ENTITY_FIELD_COUNT] = ENTITY_FIELD_ADDRESSES(results); - destroy_struct(ENTITY_FIELD_COUNT, field_types, field_pointers); -} - -// get entity results from the string -EntityResults get_entity_results(byte_t *data, size_t length) { - EntityResults results; - TypeEnum field_types[ENTITY_FIELD_COUNT] = ENTITY_FIELD_TYPES; - void *field_pointers[ENTITY_FIELD_COUNT] = ENTITY_FIELD_ADDRESSES(results); - get_struct_data(ENTITY_FIELD_COUNT, field_types, field_pointers, SEPARATOR_RESULT, (char *)data, length); - return results; -} - Item *expand_items(Item *items, size_t *items_capacity, size_t to_fit) { while (*items_capacity < to_fit) *items_capacity <<= 1; return realloc(items, *items_capacity * sizeof(Item)); } -void destroy_gear(Gear gear) { - TypeEnum field_types[GEAR_FIELD_COUNT] = GEAR_FIELD_TYPES; - void *field_pointers[GEAR_FIELD_COUNT] = GEAR_FIELD_ADDRESSES(gear); - destroy_struct(GEAR_FIELD_COUNT, field_types, field_pointers); -} - -Gear get_gear(char *data, size_t length) { - Gear gear; - TypeEnum field_types[GEAR_FIELD_COUNT] = GEAR_FIELD_TYPES; - void *field_pointers[GEAR_FIELD_COUNT] = GEAR_FIELD_ADDRESSES(gear); - get_struct_data(GEAR_FIELD_COUNT, field_types, field_pointers, SEPARATOR_GEAR_FIELDS, data, length); - return gear; -} - -void destroy_gear_info(GearInfo gear_info) { - TypeEnum field_types[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_TYPES; - void *field_pointers[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_ADDRESSES(gear_info); - destroy_struct(GEAR_INFO_FIELD_COUNT, field_types, field_pointers); -} - -GearInfo get_gear_info(char *data, size_t length) { - GearInfo gear_info; - TypeEnum field_types[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_TYPES; - void *field_pointers[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_ADDRESSES(gear_info); - get_struct_data(GEAR_INFO_FIELD_COUNT, field_types, field_pointers, SEPARATOR_GEAR_INFO_FIELDS, data, length); - return gear_info; -} - -void destroy_orb(Orb orb) { - TypeEnum field_types[ORB_FIELD_COUNT] = ORB_FIELD_TYPES; - void *field_pointers[ORB_FIELD_COUNT] = ORB_FIELD_ADDRESSES(orb); - destroy_struct(ORB_FIELD_COUNT, field_types, field_pointers); -} - -Orb get_orb(char *data, size_t length) { - Orb orb; - TypeEnum field_types[ORB_FIELD_COUNT] = ORB_FIELD_TYPES; - void *field_pointers[ORB_FIELD_COUNT] = ORB_FIELD_ADDRESSES(orb); - get_struct_data(ORB_FIELD_COUNT, field_types, field_pointers, SEPARATOR_ORB_FIELDS, data, length); - return orb; -} - void get_stars(char *buffer, int incr_above_b1) { char star_types[] = {'B', 'S', 'G'}; - sprintf(buffer, " (%c%d)", star_types[incr_above_b1 / 3], incr_above_b1 % 3 + '1'); + sprintf(buffer, " (%c%d)", star_types[incr_above_b1 / 3], incr_above_b1 % 3 + 1); } void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) { @@ -157,16 +58,19 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) char *gears_separator; do { - gears_separator = strstr(gears_str, SEPARATOR_GEARS); - Gear gear = get_gear(gears_str, gears_separator ? gears_separator - gears_str : strlen(gears_str)); + gears_separator = strstr(gears_str, SEPARATOR_GEAR); + size_t len = gears_separator ? gears_separator - gears_str : strlen(gears_str); + Gear gear = {0}; + fill_struct(&gear, gear_fields, ARR_LEN(gear_fields), gears_str, len, SEPARATOR_GEAR_FIELD); // if there are still gears - modify gears_str pointer if (gears_separator) - gears_str = gears_separator + strlen(SEPARATOR_GEARS); + gears_str = gears_separator + strlen(SEPARATOR_GEAR); Item item; - GearInfo gear_info = get_gear_info(gear.info, strlen(gear.info)); + GearInfo gear_info = {0}; + fill_struct(&gear_info, gear_info_fields, ARR_LEN(gear_info_fields), gear.info, strlen(gear.info), SEPARATOR_GEAR_INFO_FIELD); char buffer[SINGLE_ITEM_BUFFER_LEN]; char roman[5]; to_roman(roman, gear_info.rank); @@ -174,8 +78,11 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) switch (gear.type) { case TYPE_SYNG_NORMAL: // atoi should be fine, since syng_tier starts from 1 for syngs - if (atoi(gear.syng_tier)) - sprintf(buffer, "%s (%d)", buffer, gear_info.lvl); + if (atoi(gear.syng_tier)) { + char lvl[7]; + sprintf(lvl, " (%d)", gear_info.lvl); + strcat(buffer, lvl); + } break; default: { char stars[6]; @@ -185,7 +92,7 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) } } item.data = strdup(buffer); - destroy_gear_info(gear_info); + cleanup_struct(&gear_info, gear_info_fields, ARR_LEN(gear_info_fields)); switch (gear.type) { case TYPE_SYNG_NORMAL: @@ -212,10 +119,11 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) if (strlen(gear.orb)) { Item item; char buffer[SINGLE_ITEM_BUFFER_LEN]; - Orb orb = get_orb(gear.orb, strlen(gear.orb)); + Orb orb = {0}; + fill_struct(&orb, orb_fields, ARR_LEN(orb_fields), gear.orb, strlen(gear.orb), SEPARATOR_ORB_FIELD); sprintf(buffer, "%sorb %s", get_artifact_size_from_id(orb.size), get_orb_name_from_id(orb.id)); item.data = strdup(buffer); - destroy_orb(orb); + cleanup_struct(&orb, orb_fields, ARR_LEN(orb_fields)); item.type = ORB; // expand if needed if (results->num_items >= *items_capacity) @@ -223,47 +131,35 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) results->items[results->num_items++] = item; } - destroy_gear(gear); + cleanup_struct(&gear, gear_fields, ARR_LEN(gear_fields)); } while (gears_separator); } -void destroy_drif(Drif drif) { - TypeEnum field_types[DRIF_FIELD_COUNT] = DRIF_FIELD_TYPES; - void *field_pointers[DRIF_FIELD_COUNT] = DRIF_FIELD_ADDRESSES(drif); - destroy_struct(DRIF_FIELD_COUNT, field_types, field_pointers); -} - -Drif get_drif(char *data, size_t length) { - Drif drif; - TypeEnum field_types[DRIF_FIELD_COUNT] = DRIF_FIELD_TYPES; - void *field_pointers[DRIF_FIELD_COUNT] = DRIF_FIELD_ADDRESSES(drif); - get_struct_data(DRIF_FIELD_COUNT, field_types, field_pointers, SEPARATOR_DRIF_FIELDS, data, length); - return drif; -} - void add_drifs(FriendlyResults *results, size_t *items_capacity, char *drifs_str) { if (strlen(drifs_str) == 0) return; char *drifs_separator; do { - drifs_separator = strstr(drifs_str, SEPARATOR_DRIFS); - Drif drif = get_drif(drifs_str, drifs_separator ? drifs_separator - drifs_str : strlen(drifs_str)); + drifs_separator = strstr(drifs_str, SEPARATOR_DRIF); + size_t len = drifs_separator ? drifs_separator - drifs_str : strlen(drifs_str); + Drif drif = {0}; + fill_struct(&drif, drif_fields, ARR_LEN(drif_fields), drifs_str, len, SEPARATOR_DRIF_FIELD); // if there are still drifs - modify drifs_str pointer if (drifs_separator) - drifs_str = drifs_separator + strlen(SEPARATOR_DRIFS); + drifs_str = drifs_separator + strlen(SEPARATOR_DRIF); Item item; item.data = strdup(drif.name); item.type = DRIF; + cleanup_struct(&drif, drif_fields, ARR_LEN(drif_fields)); + // expand if needed if (results->num_items >= *items_capacity) results->items = expand_items(results->items, items_capacity, results->num_items + 1); results->items[results->num_items++] = item; - - destroy_drif(drif); } while (drifs_separator); } @@ -350,7 +246,9 @@ ParsedData parse_fight_results(byte_t *data, size_t length) { do { // get generic entity results entity_separator = memmem(data, length, SEPARATOR_ENTITY, strlen(SEPARATOR_ENTITY)); - EntityResults entity_results = get_entity_results(data, entity_separator ? entity_separator - data : length); + size_t len = entity_separator ? entity_separator - data : length; + EntityResults entity_results = {0}; + fill_struct(&entity_results, entity_fields, ARR_LEN(entity_fields), (char *)data, len, SEPARATOR_ENTITY_FIELD); // depending on team - parse it into friendly or enemy results switch (entity_results.team) { @@ -361,7 +259,8 @@ ParsedData parse_fight_results(byte_t *data, size_t length) { results->enemy_results[results->num_enemy++] = parse_to_enemy(entity_results); break; } - destroy_entity_results(entity_results); + + cleanup_struct(&entity_results, entity_fields, ARR_LEN(entity_fields)); // if there are still entities - modify data pointer and length if (entity_separator) { diff --git a/analyzer/parser/fight_results/fight_results.h b/analyzer/parser/fight_results/fight_results.h index 0618bf2..04add96 100644 --- a/analyzer/parser/fight_results/fight_results.h +++ b/analyzer/parser/fight_results/fight_results.h @@ -11,137 +11,12 @@ // FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END #define FIGHT_RESULTS_START "3;19;" #define FIGHT_RESULTS_END "[--];" -#define SEPARATOR_ENTITY "[--]" -#define SEPARATOR_RESULT "&" -// values of field "team" in EntityResults -#define TEAM_FRIEND 1 -#define TEAM_ENEMY 2 - -// EntityResults structure: -// field (SEPARATOR_RESULT field)* -// fields are defined in order in the EntityResults struct -#define ENTITY_FIELD_COUNT 35 -// clang-format off -#define ENTITY_FIELD_TYPES { \ - /* team */ INT, \ - /* name */ STRING, \ - /* exp */ INT, \ - /* field4 */ FLOAT, \ - /* gold */ INT, \ - /* field6 */ FLOAT, \ - /* field7 */ STRING, \ - /* gear */ STRING, \ - /* field9 */ INT, \ - /* items */ STRING, \ - /* field11 */ INT, \ - /* field12 */ INT, \ - /* field13 */ FLOAT, \ - /* field14 */ FLOAT, \ - /* field15 */ FLOAT, \ - /* level */ INT, \ - /* field17 */ DOUBLE, \ - /* field18 */ FLOAT, \ - /* field19 */ INT, \ - /* field20 */ INT, \ - /* field21 */ FLOAT, \ - /* field22 */ FLOAT, \ - /* field23 */ INT, \ - /* field24 */ INT, \ - /* psycho */ INT, \ - /* drifs */ STRING, \ - /* splinters */ INT, \ - /* field28 */ INT, \ - /* field29 */ STRING, \ - /* field30 */ INT, \ - /* field31 */ INT, \ - /* field32 */ INT, \ - /* field33 */ INT, \ - /* field34 */ STRING, \ - /* saturation */ STRING, \ -} -#define ENTITY_FIELD_ADDRESSES(entity_results) {\ - &entity_results.team, \ - &entity_results.name, \ - &entity_results.exp, \ - &entity_results.field4, \ - &entity_results.gold, \ - &entity_results.field6, \ - &entity_results.field7, \ - &entity_results.gear, \ - &entity_results.field9, \ - &entity_results.items, \ - &entity_results.field11, \ - &entity_results.field12, \ - &entity_results.field13, \ - &entity_results.field14, \ - &entity_results.field15, \ - &entity_results.level, \ - &entity_results.field17, \ - &entity_results.field18, \ - &entity_results.field19, \ - &entity_results.field20, \ - &entity_results.field21, \ - &entity_results.field22, \ - &entity_results.field23, \ - &entity_results.field24, \ - &entity_results.psycho, \ - &entity_results.drifs, \ - &entity_results.splinters, \ - &entity_results.field28, \ - &entity_results.field29, \ - &entity_results.field30, \ - &entity_results.field31, \ - &entity_results.field32, \ - &entity_results.field33, \ - &entity_results.field34, \ - &entity_results.saturation, \ -} -// clang-format on -typedef struct { - int team; // friend or enemy - char *name; - int exp; - float field4; - int gold; - float field6; - char *field7; - char *gear; // rares, sets, syngs, orbs - int field9; - char *items; // regular items - int field11; - int field12; - float field13; - float field14; // some multiplier most likely exp/gold/psycho/something else idk - float field15; - int level; - double field17; - float field18; - int field19; - int field20; - float field21; - float field22; - int field23; - int field24; - int psycho; - char *drifs; - int splinters; - int field28; - char *field29; - int field30; - int field31; - int field32; - int field33; - char *field34; - char *saturation; -} EntityResults; - -// items/gear/drifs fields parsing info +// items parsing info #define INIT_ITEMS_CAPACITY 8 #define SINGLE_ITEM_BUFFER_LEN 128 #define SEPARATOR_ITEMS "[-]" -#define SEPARATOR_DRIFS "???" // TODO: change it to correct one which idk at this moment -#define SEPARATOR_GEARS "???" // TODO: change it to correct one which idk at this moment + // Item struct - for keeping item string and type typedef enum { NORMAL, DRIF, ORB, SYNG, SET, RARE, EPIC } ItemType; typedef struct { @@ -149,273 +24,6 @@ typedef struct { ItemType type; } Item; -// Drif structure: -// field (SEPARATOR_DRIF_FIELDS field)* -// fields are defined in order in the Drif struct -#define DRIF_FIELD_COUNT 18 -#define SEPARATOR_DRIF_FIELDS "," -// clang-format off -#define DRIF_FIELD_TYPES { \ - /* field1 */ STRING, \ - /* field2 */ INT, \ - /* size */ INT, \ - /* field4 */ STRING, \ - /* field5 */ INT, \ - /* id */ INT, \ - /* value1 */ FLOAT, \ - /* value2 */ FLOAT, \ - /* field9 */ INT, \ - /* field10 */ INT, \ - /* field11 */ STRING, \ - /* size_str */ STRING, \ - /* field13 */ INT, \ - /* power */ INT, \ - /* field15 */ INT, \ - /* field16 */ INT, \ - /* name */ STRING, \ - /* field18 */ STRING, \ -} -#define DRIF_FIELD_ADDRESSES(drif) {\ - &drif.field1, \ - &drif.field2, \ - &drif.size, \ - &drif.field4, \ - &drif.field5, \ - &drif.id, \ - &drif.value1, \ - &drif.value2, \ - &drif.field9, \ - &drif.field10, \ - &drif.field11, \ - &drif.size_str, \ - &drif.field13, \ - &drif.power, \ - &drif.field15, \ - &drif.field16, \ - &drif.name, \ - &drif.field18, \ -} -// clang-format on -typedef struct { - char *field1; // just says "drif" - int field2; - int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy - char *field4; - int field5; - int id; // id of orb type - float value1; // value1, value2 (in %) - one is start value, the other is value per lvl (both are the same) - float value2; // value1, value2 (in %) - one is start value, the other is value per lvl (both are the same) - int field9; - int field10; - char *field11; - char *size_str; // sub - small, medium - bi, big - magni, ??? - arcy - int field13; - int power; // how much space it takes - int field15; - int field16; - char *name; // drif - char *field18; -} Drif; - -// Gear structure: -// field (SEPARATOR_GEAR_FIELDS field)* -// fields are defined in order in the Gear struct -#define GEAR_FIELD_COUNT 19 -#define SEPARATOR_GEAR_FIELDS "$" -// values of field "type" in Gear -#define TYPE_SYNG_NORMAL 0 -#define TYPE_SET 1 -#define TYPE_RARE 2 -// clang-format off -#define GEAR_FIELD_TYPES { \ - /* stats */ STRING, \ - /* resistances */ STRING, \ - /* info */ STRING, \ - /* reqs */ STRING, \ - /* field5 */ STRING, \ - /* psycho */ STRING, \ - /* field7 */ STRING, \ - /* field8 */ STRING, \ - /* field9 */ STRING, \ - /* syng_tier */ STRING, \ - /* field11 */ INT, \ - /* field12 */ INT, \ - /* field13 */ INT, \ - /* field14 */ STRING, \ - /* type */ INT, \ - /* field16 */ INT, \ - /* orb */ STRING, \ - /* field18 */ STRING, \ - /* field19 */ STRING, \ -} -#define GEAR_FIELD_ADDRESSES(gear) {\ - &gear.stats, \ - &gear.resistances, \ - &gear.info, \ - &gear.reqs, \ - &gear.field5, \ - &gear.psycho, \ - &gear.field7, \ - &gear.field8, \ - &gear.field9, \ - &gear.syng_tier, \ - &gear.field11, \ - &gear.field12, \ - &gear.field13, \ - &gear.field14, \ - &gear.type, \ - &gear.field16, \ - &gear.orb, \ - &gear.field18, \ - &gear.field19, \ -} -// clang-format on -typedef struct { - char *stats; // strength, dexterity, power, knowledge, hp, mana, stamina - char *resistances; // slashing, crushing, piercing, fire, cold, energy, mental - char *info; // GearInfo struct data - char *reqs; // requirements (str, dex, pow, know), weight, something else - char *field5; - char *psycho; // psycho effects (id,%) for syngs - char *field7; - char *field8; - char *field9; - char *syng_tier; // lvl 20 - 1, lvl 30 - 2, etc..., null for non-syngs - int field11; - int field12; - int field13; - char *field14; - int type; // 0 - normal item or syng, 1 - set, 2 - rare - int field16; // 10 for rare, -1 for others ??? - char *orb; // orb info - char *field18; - char *field19; -} Gear; - -// GearInfo structure: -// field (SEPARATOR_GEAR_INFO_FIELDS field)* -// fields are defined in order in the GearInfo struct -#define GEAR_INFO_FIELD_COUNT 23 -#define SEPARATOR_GEAR_INFO_FIELDS "," -// clang-format off -#define GEAR_INFO_FIELD_TYPES { \ - /* name */ STRING, \ - /* field2 */ INT, \ - /* lvl */ INT, \ - /* price */ INT, \ - /* field5 */ INT, \ - /* field6 */ INT, \ - /* field7 */ INT, \ - /* rank */ INT, \ - /* field9 */ INT, \ - /* field10 */ INT, \ - /* field11 */ INT, \ - /* durability */ INT, \ - /* field13 */ STRING, \ - /* field14 */ INT, \ - /* field15 */ INT, \ - /* charges */ FLOAT, \ - /* field17 */ STRING, \ - /* field18 */ INT, \ - /* stars */ INT, \ - /* field20 */ INT, \ - /* field21 */ INT, \ - /* field22 */ INT, \ - /* field23 */ INT, \ -} -#define GEAR_INFO_FIELD_ADDRESSES(gear_info) {\ - &gear_info.name, \ - &gear_info.field2, \ - &gear_info.lvl, \ - &gear_info.price, \ - &gear_info.field5, \ - &gear_info.field6, \ - &gear_info.field7, \ - &gear_info.rank, \ - &gear_info.field9, \ - &gear_info.field10, \ - &gear_info.field11, \ - &gear_info.durability, \ - &gear_info.field13, \ - &gear_info.field14, \ - &gear_info.field15, \ - &gear_info.charges, \ - &gear_info.field17, \ - &gear_info.field18, \ - &gear_info.stars, \ - &gear_info.field20, \ - &gear_info.field21, \ - &gear_info.field22, \ - &gear_info.field23, \ -} -// clang-format on -typedef struct { - char *name; // item name - int field2; - int lvl; // level requirement - int price; // gold price value - int field5; - int field6; - int field7; - int rank; // I-XII - int field9; - int field10; - int field11; - int durability; // in % - char *field13; - int field14; - int field15; - float charges; // charge count - char *field17; - int field18; - int stars; // incrustation level: 0 - b1, 1 - b2, ... (-100 for syngs) - int field20; - int field21; - int field22; - int field23; -} GearInfo; - -// Orb structure: -// field (SEPARATOR_ORB_FIELDS field)* -// fields are defined in order in the Orb struct -#define ORB_FIELD_COUNT 9 -#define SEPARATOR_ORB_FIELDS "," -// clang-format off -#define ORB_FIELD_TYPES { \ - /* field1 */ STRING, \ - /* field2 */ INT, \ - /* field3 */ INT, \ - /* field4 */ STRING, \ - /* field5 */ INT, \ - /* id */ INT, \ - /* size */ INT, \ - /* value */ FLOAT, \ - /* field9 */ STRING, \ -} -#define ORB_FIELD_ADDRESSES(orb) {\ - &orb.field1, \ - &orb.field2, \ - &orb.field3, \ - &orb.field4, \ - &orb.field5, \ - &orb.id, \ - &orb.size, \ - &orb.value, \ - &orb.field9, \ -} -// clang-format on -typedef struct { - char *field1; // just says "orb" - int field2; - int field3; - char *field4; - int field5; - int id; // id of orb type - int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy - float value; // value in % you gain from the orb - char *field9; -} Orb; - // fight results data for friendly typedef struct { char *name; diff --git a/analyzer/parser/fight_results/gear.h b/analyzer/parser/fight_results/gear.h new file mode 100644 index 0000000..66f52f3 --- /dev/null +++ b/analyzer/parser/fight_results/gear.h @@ -0,0 +1,45 @@ +// parser/fight_results/gear.h +// Gear generic struct data + +#include "parser/structs.h" + +// Gear structure: +// field (SEPARATOR_GEAR_FIELD field)* +// fields are defined in order in the Gear struct +#define SEPARATOR_GEAR "???" // TODO: change it to correct one which idk at this moment +#define SEPARATOR_GEAR_FIELD "$" + +// values of field "type" in Gear +#define TYPE_SYNG_NORMAL 0 +#define TYPE_SET 1 +#define TYPE_RARE 2 + +typedef struct { + char *stats; // strength, dexterity, power, knowledge, hp, mana, stamina + char *resistances; // slashing, crushing, piercing, fire, cold, energy, mental + char *info; // GearInfo struct data + char *reqs; // requirements (str, dex, pow, know), weight, something else + char *field5; + char *psycho; // psycho effects (id,%) for syngs + char *field7; + char *field8; + char *field9; + char *syng_tier; // lvl 20 - 1, lvl 30 - 2, etc..., null for non-syngs + int field11; + int field12; + int field13; + char *field14; + int type; // 0 - normal item or syng, 1 - set, 2 - rare + int field16; // 10 for rare, -1 for others ??? + char *orb; // orb info + char *field18; + char *field19; +} Gear; + +static const StructField gear_fields[] = { + {STRING, offsetof(Gear, stats)}, {STRING, offsetof(Gear, resistances)}, {STRING, offsetof(Gear, info)}, {STRING, offsetof(Gear, reqs)}, + {STRING, offsetof(Gear, field5)}, {STRING, offsetof(Gear, psycho)}, {STRING, offsetof(Gear, field7)}, {STRING, offsetof(Gear, field8)}, + {STRING, offsetof(Gear, field9)}, {STRING, offsetof(Gear, syng_tier)}, {INT, offsetof(Gear, field11)}, {INT, offsetof(Gear, field12)}, + {INT, offsetof(Gear, field13)}, {STRING, offsetof(Gear, field14)}, {INT, offsetof(Gear, type)}, {INT, offsetof(Gear, field16)}, + {STRING, offsetof(Gear, orb)}, {STRING, offsetof(Gear, field18)}, {STRING, offsetof(Gear, field19)}, +}; diff --git a/analyzer/parser/fight_results/gear_info.h b/analyzer/parser/fight_results/gear_info.h new file mode 100644 index 0000000..f0cf83a --- /dev/null +++ b/analyzer/parser/fight_results/gear_info.h @@ -0,0 +1,44 @@ +// parser/fight_results/gear_info.h +// GearInfo generic struct data + +#include "parser/structs.h" + +// GearInfo structure: +// field (SEPARATOR_GEAR_INFO_FIELD field)* +// fields are defined in order in the GearInfo struct +#define SEPARATOR_GEAR_INFO_FIELD "," + +typedef struct { + char *name; // item name + int field2; + int lvl; // level requirement + int price; // gold price value + int field5; + int field6; + int field7; + int rank; // I-XII + int field9; + int field10; + int field11; + int durability; // in % + char *field13; + int field14; + int field15; + float charges; // charge count + char *field17; + int field18; + int stars; // incrustation level: 0 - b1, 1 - b2, ... (-100 for syngs) + int field20; + int field21; + int field22; + int field23; +} GearInfo; + +static const StructField gear_info_fields[] = { + {STRING, offsetof(GearInfo, name)}, {INT, offsetof(GearInfo, field2)}, {INT, offsetof(GearInfo, lvl)}, {INT, offsetof(GearInfo, price)}, + {INT, offsetof(GearInfo, field5)}, {INT, offsetof(GearInfo, field6)}, {INT, offsetof(GearInfo, field7)}, {INT, offsetof(GearInfo, rank)}, + {INT, offsetof(GearInfo, field9)}, {INT, offsetof(GearInfo, field10)}, {INT, offsetof(GearInfo, field11)}, {INT, offsetof(GearInfo, durability)}, + {STRING, offsetof(GearInfo, field13)}, {INT, offsetof(GearInfo, field14)}, {INT, offsetof(GearInfo, field15)}, {FLOAT, offsetof(GearInfo, charges)}, + {STRING, offsetof(GearInfo, field17)}, {INT, offsetof(GearInfo, field18)}, {INT, offsetof(GearInfo, stars)}, {INT, offsetof(GearInfo, field20)}, + {INT, offsetof(GearInfo, field21)}, {INT, offsetof(GearInfo, field22)}, {INT, offsetof(GearInfo, field23)}, +}; diff --git a/analyzer/parser/fight_results/orb.h b/analyzer/parser/fight_results/orb.h new file mode 100644 index 0000000..dcf146f --- /dev/null +++ b/analyzer/parser/fight_results/orb.h @@ -0,0 +1,33 @@ +// parser/fight_results/orb.h +// Orb generic struct data + +#include "parser/structs.h" + +// Orb structure: +// field (SEPARATOR_ORB_FIELD field)* +// fields are defined in order in the Orb struct +#define SEPARATOR_ORB_FIELD "," + +typedef struct { + char *field1; // just says "orb" + int field2; + int field3; + char *field4; + int field5; + int id; // id of orb type + int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy + float value; // value in % you gain from the orb + char *field9; +} Orb; + +static const StructField orb_fields[] = { + {STRING, offsetof(Orb, field1)}, + {INT, offsetof(Orb, field2)}, + {INT, offsetof(Orb, field3)}, + {STRING, offsetof(Orb, field4)}, + {INT, offsetof(Orb, field5)}, + {INT, offsetof(Orb, id)}, + {INT, offsetof(Orb, size)}, + {FLOAT, offsetof(Orb, value)}, + {STRING, offsetof(Orb, field9)}, +}; diff --git a/analyzer/parser/find_parse_functions.h b/analyzer/parser/find_parse_functions.h index 1b6ce9e..e2c2acb 100644 --- a/analyzer/parser/find_parse_functions.h +++ b/analyzer/parser/find_parse_functions.h @@ -3,8 +3,4 @@ #include "parser/fight_results/fight_results.h" -#define RESULT_TYPE_COUNT 1 - -FindParseFunctions all_find_parse_functions[RESULT_TYPE_COUNT] = { - {.find_fn = find_fight_results, .parse_fn = parse_fight_results} -}; +FindParseFunctions all_find_parse_functions[] = {{find_fight_results, parse_fight_results}}; diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c index 3ef48b5..87ab4c0 100644 --- a/analyzer/parser/parser.c +++ b/analyzer/parser/parser.c @@ -3,6 +3,7 @@ #include #include "parser/find_parse_functions.h" +#include "utils/memory.h" // expand parsed data exponentially to fit el_count elements ParsedData *expand_parsed_data(ParsedData *parsed_data, size_t *capacity, size_t el_count) { @@ -14,7 +15,7 @@ ParsedData *expand_parsed_data(ParsedData *parsed_data, size_t *capacity, size_t // find earliest data to parse in stream byte_t *find_earliest_data(TCPStream *stream, int stream_offset, int *earliest_length, parse_function_t **earliest_parse_fun) { byte_t *earliest_data = NULL; - for (size_t i = 0; i < RESULT_TYPE_COUNT; i++) { + for (size_t i = 0; i < ARR_LEN(all_find_parse_functions); i++) { int length; byte_t *data = all_find_parse_functions[i].find_fn(stream, stream_offset, &length); // if it's the first found or earlier than earliest - save it diff --git a/analyzer/parser/parser_defs.h b/analyzer/parser/parser_defs.h index aa39a4c..75b0366 100644 --- a/analyzer/parser/parser_defs.h +++ b/analyzer/parser/parser_defs.h @@ -6,8 +6,6 @@ #ifndef PARSER_DEFS_H #define PARSER_DEFS_H -typedef enum { INT, FLOAT, DOUBLE, STRING } TypeEnum; - typedef enum { TYPE_FIGHT_RESULTS } ResultsEnum; // "generic" type for parsed data diff --git a/analyzer/parser/structs.c b/analyzer/parser/structs.c new file mode 100644 index 0000000..c59fa82 --- /dev/null +++ b/analyzer/parser/structs.c @@ -0,0 +1,49 @@ +#include "parser/structs.h" + +#include +#include +#include + +void fill_struct(void *obj, const StructField *fields, size_t n_fields, const char *data, size_t data_len, const char *sep) { + for (size_t i = 0; i < n_fields; i++) { + char *next_sep = memmem(data, data_len, sep, strlen(sep)); + size_t len = next_sep ? next_sep - data : data_len; + + char *str = malloc(len + 1); + memcpy(str, data, len); + str[len] = '\0'; + + // parse depending on variable type + void *p_field = (char *)obj + fields[i].offset; + switch (fields[i].type) { + case INT: + sscanf(str, "%d", (int *)p_field); + free(str); + break; + case FLOAT: + sscanf(str, "%f", (float *)p_field); + free(str); + break; + case DOUBLE: + sscanf(str, "%lf", (double *)p_field); + free(str); + break; + case STRING: + *(char **)p_field = str; + break; + } + + // update data pointer and length + if (next_sep) { + data_len -= next_sep - data + strlen(sep); + data = next_sep + strlen(sep); + } + } +} + +void cleanup_struct(void *obj, const StructField *fields, size_t n_fields) { + for (size_t i = 0; i < n_fields; i++) { + if (fields[i].type == STRING) + free(*(char **)((char *)obj + fields[i].offset)); + } +} diff --git a/analyzer/parser/structs.h b/analyzer/parser/structs.h new file mode 100644 index 0000000..023c19b --- /dev/null +++ b/analyzer/parser/structs.h @@ -0,0 +1,33 @@ +// parser/structs.h +// generic struct functionality for parsing + +#include + +#ifndef PARSER_STRUCTS_H +#define PARSER_STRUCTS_H + +typedef enum { INT, FLOAT, DOUBLE, STRING } TypeEnum; + +typedef struct { + TypeEnum type; + size_t offset; +} StructField; + +// fill generic struct with given data +// params: +// - obj - pointer to the struct to fill +// - fields - field types and offsets of the struct +// - n_fields - count of fields in the struct +// - data - data to fill the struct with +// - data_len - length of the data +// - sep - separator seperating the fields in data +extern void fill_struct(void *obj, const StructField *fields, size_t n_fields, const char *data, size_t data_len, const char *sep); + +// cleanup allocations inside generic struct +// params: +// - obj - pointer to the struct to cleanup +// - fields - field types and offsets of the struct +// - n_fields - count of fields in the struct +extern void cleanup_struct(void *obj, const StructField *fields, size_t n_fields); + +#endif diff --git a/analyzer/render.c b/analyzer/render.c index 0fdd334..5c4b2d0 100644 --- a/analyzer/render.c +++ b/analyzer/render.c @@ -63,14 +63,10 @@ void print_enemy_list(EnemyResults *results, size_t n_enemies) { } void print_column_names() { - char *names[TABLE_COLUMN_COUNT] = TABLE_COLUMN_NAMES; - int colors[TABLE_COLUMN_COUNT] = TABLE_COLUMN_COLORS; - size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; - printf(TABLE_LINE_START); - for (size_t i = 0; i < TABLE_COLUMN_COUNT; i++) { - print_center(names[i], colors[i], widths[i], ' '); - printf(i < TABLE_COLUMN_COUNT - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); + for (size_t i = 0; i < ARR_LEN(columns); i++) { + print_center(columns[i].name, columns[i].color, columns[i].width, ' '); + printf(i < ARR_LEN(columns) - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); } printf("\n"); } @@ -96,9 +92,7 @@ int item_type_to_color(ItemType type) { } void print_player_rewards(FriendlyResults results) { - char buffers[TABLE_COLUMN_COUNT][TABLE_LINE_WIDTH]; - size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; - int colors[TABLE_COLUMN_COUNT] = TABLE_COLUMN_COLORS; + char buffers[ARR_LEN(columns)][TABLE_LINE_WIDTH]; sprintf(buffers[0], "%s (%d)", results.name, results.level); sprintf(buffers[1], "%d", results.exp); @@ -120,11 +114,11 @@ void print_player_rewards(FriendlyResults results) { sprintf(buffers[5], "-"); printf(TABLE_LINE_START); - for (size_t i = 0; i < TABLE_COLUMN_COUNT; i++) { - char to_color[widths[i] + 1]; - sprintf(to_color, "%*s", (int)widths[i], buffers[i]); - print_color(to_color, colors[i]); - printf(i < TABLE_COLUMN_COUNT - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); + for (size_t i = 0; i < ARR_LEN(columns); i++) { + char to_color[columns[i].width + 1]; + sprintf(to_color, "%*s", (int)columns[i].width, buffers[i]); + print_color(to_color, columns[i].color); + printf(i < ARR_LEN(columns) - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); } printf("\n"); diff --git a/analyzer/render.h b/analyzer/render.h index 1015eb9..3604bde 100644 --- a/analyzer/render.h +++ b/analyzer/render.h @@ -2,6 +2,7 @@ // functions for "rendering" data onto the terminal #include "parser/fight_results/fight_results.h" +#include "utils/memory.h" // ANSI escape sequence colors #define COLOR_GOLD 33 @@ -23,40 +24,32 @@ #define TABLE_LINE_END_WIDTH (sizeof(TABLE_LINE_END) - 1) #define TABLE_LINE_SEPARATOR_WIDTH (sizeof(TABLE_LINE_SEPARATOR) - 1) -#define TABLE_COLUMN_COUNT 6 - -#define TABLE_NAME_LVL_WIDTH 17 -#define TABLE_EXP_WIDTH 7 -#define TABLE_GOLD_WIDTH 7 -#define TABLE_PSYCHO_WIDTH 6 -#define TABLE_SATURATION_WIDTH 13 -#define TABLE_SPLINTERS_WIDTH 9 +// column info +typedef struct { + char *name; + size_t width; + int color; +} ColumnInfo; +static const ColumnInfo columns[] = { + {"Nick (LVL)", 17, COLOR_WHITE}, + {"EXP", 7, COLOR_WHITE}, + {"Gold", 7, COLOR_YELLOW}, + {"Psycho", 6, COLOR_AQUA}, + {"Saturation", 13, COLOR_GREEN}, + {"Splinters", 9, COLOR_BLUE}, +}; // clang-format off -#define TABLE_COLUMN_NAMES {"Nick (LVL)", "EXP", "Gold", "Psycho", "Saturation", "Splinters"} -#define TABLE_COLUMN_COLORS {COLOR_WHITE, COLOR_WHITE, COLOR_YELLOW, COLOR_AQUA, COLOR_GREEN, COLOR_BLUE} -#define TABLE_COLUMN_WIDTHS { \ - TABLE_NAME_LVL_WIDTH, \ - TABLE_EXP_WIDTH, \ - TABLE_GOLD_WIDTH, \ - TABLE_PSYCHO_WIDTH, \ - TABLE_SATURATION_WIDTH, \ - TABLE_SPLINTERS_WIDTH, \ -} - -#define TABLE_LINE_WIDTH ( \ - TABLE_LINE_START_WIDTH + \ - TABLE_NAME_LVL_WIDTH + \ - TABLE_EXP_WIDTH + \ - TABLE_GOLD_WIDTH + \ - TABLE_PSYCHO_WIDTH + \ - TABLE_SATURATION_WIDTH + \ - TABLE_SPLINTERS_WIDTH + \ - (TABLE_COLUMN_COUNT - 1) * TABLE_LINE_SEPARATOR_WIDTH + \ - TABLE_LINE_END_WIDTH \ -) +#define SUM_COL_WIDTHS ({ \ + size_t sum = 0; \ + for (size_t i = 0; i < ARR_LEN(columns); i++) \ + sum += columns[i].width; \ + sum; \ +}) // clang-format on +#define TABLE_LINE_WIDTH (TABLE_LINE_START_WIDTH + SUM_COL_WIDTHS + (ARR_LEN(columns) - 1) * TABLE_LINE_SEPARATOR_WIDTH + TABLE_LINE_END_WIDTH) + #define TABLE_ITEMS_WIDTH (TABLE_LINE_WIDTH - TABLE_LINE_START_WIDTH - TABLE_LINE_END_WIDTH) // render fight results onto the terminal diff --git a/analyzer/utils/memory.h b/analyzer/utils/memory.h new file mode 100644 index 0000000..1de58b9 --- /dev/null +++ b/analyzer/utils/memory.h @@ -0,0 +1,4 @@ +// utils/memory.h +// helper functions for dealing with memory management + +#define ARR_LEN(arr) (sizeof(arr) / sizeof(arr[0])) diff --git a/analyzer/utils/roman.c b/analyzer/utils/roman.c index 9ea2bbb..4fbe5c0 100644 --- a/analyzer/utils/roman.c +++ b/analyzer/utils/roman.c @@ -1,16 +1,15 @@ #include "utils/roman.h" -#include #include +#include "utils/memory.h" + void to_roman(char *buffer, int number) { buffer[0] = '\0'; - int nums[ROMAN_CHAR_COUNT] = ROMAN_NUMS; - char *chars[ROMAN_CHAR_COUNT] = ROMAN_CHARS; - for (size_t i = 0; i < ROMAN_CHAR_COUNT; i++) { - for (size_t j = 0; j < number / nums[i]; j++) { - strcat(buffer, chars[i]); + for (size_t i = 0; i < ARR_LEN(roman_symbols); i++) { + for (size_t j = 0; j < number / roman_symbols[i].num; j++) { + strcat(buffer, roman_symbols[i].symbol); } - number %= nums[i]; + number %= roman_symbols[i].num; } } diff --git a/analyzer/utils/roman.h b/analyzer/utils/roman.h index b4f5d3f..cebd16e 100644 --- a/analyzer/utils/roman.h +++ b/analyzer/utils/roman.h @@ -1,9 +1,26 @@ // utils/roman.h // functions for roman numbers -#define ROMAN_CHAR_COUNT 13 -#define ROMAN_NUMS {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1} -#define ROMAN_CHARS {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"} +typedef struct { + int num; + char *symbol; +} RomanSymbol; + +static const RomanSymbol roman_symbols[] = { + {1000, "M"}, + {900, "CM"}, + {500, "D"}, + {400, "CD"}, + {100, "C"}, + {90, "XC"}, + {50, "L"}, + {40, "XL"}, + {10, "X"}, + {9, "IX"}, + {5, "V"}, + {4, "IV"}, + {1, "I"}, +}; // convert a arabic number to roman number // params: -- cgit v1.2.3