aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMosder <xmosder12@gmail.com>2026-07-22 01:16:45 +0200
committerMosder <xmosder12@gmail.com>2026-07-22 01:16:45 +0200
commit5d463a7f4c721629a1996be6d19634338290a539 (patch)
tree5c681a524990ad8b181dea567d6dc7629d01e8c9
parent1f664d5b6396fb24c8ce4990ba8882118abb8dcd (diff)
refactor: generalized parser structs with offsetof and put them into separate files
-rw-r--r--Makefile1
-rw-r--r--analyzer/parser/fight_results/drif.h52
-rw-r--r--analyzer/parser/fight_results/entity.h67
-rw-r--r--analyzer/parser/fight_results/fight_results.c177
-rw-r--r--analyzer/parser/fight_results/fight_results.h396
-rw-r--r--analyzer/parser/fight_results/gear.h45
-rw-r--r--analyzer/parser/fight_results/gear_info.h44
-rw-r--r--analyzer/parser/fight_results/orb.h33
-rw-r--r--analyzer/parser/find_parse_functions.h6
-rw-r--r--analyzer/parser/parser.c3
-rw-r--r--analyzer/parser/parser_defs.h2
-rw-r--r--analyzer/parser/structs.c49
-rw-r--r--analyzer/parser/structs.h33
-rw-r--r--analyzer/render.c24
-rw-r--r--analyzer/render.h53
-rw-r--r--analyzer/utils/memory.h4
-rw-r--r--analyzer/utils/roman.c13
-rw-r--r--analyzer/utils/roman.h23
18 files changed, 429 insertions, 596 deletions
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; // <size>drif <name>
+ 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; // <size>drif <name>
- 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 <stdlib.h>
#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+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 <stddef.h>
+
+#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 <stddef.h>
#include <string.h>
+#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: