aboutsummaryrefslogtreecommitdiff
path: root/analyzer/parser
diff options
context:
space:
mode:
Diffstat (limited to 'analyzer/parser')
-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.c260
-rw-r--r--analyzer/parser/fight_results/fight_results.h60
-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.c64
-rw-r--r--analyzer/parser/parser.h23
-rw-r--r--analyzer/parser/parser_defs.h42
-rw-r--r--analyzer/parser/structs.c51
-rw-r--r--analyzer/parser/structs.h33
13 files changed, 780 insertions, 0 deletions
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
new file mode 100644
index 0000000..efc4b36
--- /dev/null
+++ b/analyzer/parser/fight_results/fight_results.c
@@ -0,0 +1,260 @@
+#include "parser/fight_results/fight_results.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 "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
+void cleanup_fight_results(void *data) {
+ FightResults *fight_results = data;
+ for (size_t i = 0; i < fight_results->num_friendly; i++) {
+ FriendlyResults friendly = fight_results->friendly_results[i];
+ free(friendly.name);
+ for (size_t j = 0; j < friendly.num_items; j++)
+ free(friendly.items[j].data);
+ free(friendly.items);
+ }
+ for (size_t i = 0; i < fight_results->num_enemy; i++) {
+ free(fight_results->enemy_results[i].name);
+ }
+ free(fight_results->friendly_results);
+ free(fight_results->enemy_results);
+}
+
+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 - (start - stream->data), FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END));
+ *length = end ? end - start + strlen(FIGHT_RESULTS_END) : -1;
+ return start;
+}
+
+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);
+}
+
+void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) {
+ if (strlen(gears_str) == 0)
+ return;
+
+ char *gears_separator;
+ do {
+ 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_GEAR);
+
+ // get gear info for rank, name and stars / syng level
+ 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);
+
+ // create the buffer of information about gear
+ 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)) {
+ char lvl[7];
+ sprintf(lvl, " (%d)", gear_info.lvl);
+ strcat(buffer, lvl);
+ }
+ break;
+ default: {
+ char stars[6];
+ get_stars(stars, gear_info.stars);
+ strcat(buffer, stars);
+ break;
+ }
+ }
+
+ // add gear with specific type
+ Item item = {.data = strdup(buffer)};
+ 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 = array_append(results->items, sizeof(Item), &results->num_items, items_capacity, &item);
+
+ cleanup_struct(&gear_info, gear_info_fields, ARR_LEN(gear_info_fields));
+
+ // add orb to items if it exists
+ if (strlen(gear.orb)) {
+ Orb orb = {0};
+ fill_struct(&orb, orb_fields, ARR_LEN(orb_fields), gear.orb, strlen(gear.orb), SEPARATOR_ORB_FIELD);
+
+ char buffer[SINGLE_ITEM_BUFFER_LEN];
+ sprintf(buffer, "%sorb %s", get_artifact_size_from_id(orb.size), get_orb_name_from_id(orb.id));
+
+ Item item = {.data = strdup(buffer), .type = ORB};
+ results->items = array_append(results->items, sizeof(Item), &results->num_items, items_capacity, &item);
+
+ cleanup_struct(&orb, orb_fields, ARR_LEN(orb_fields));
+ }
+
+ cleanup_struct(&gear, gear_fields, ARR_LEN(gear_fields));
+ } while (gears_separator);
+}
+
+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_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_DRIF);
+
+ Item item = {.data = strdup(drif.name), .type = DRIF};
+ results->items = array_append(results->items, sizeof(Item), &results->num_items, items_capacity, &item);
+
+ cleanup_struct(&drif, drif_fields, ARR_LEN(drif_fields));
+ } while (drifs_separator);
+}
+
+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_from_id(item_id));
+
+ // if there are still items - modify items_str pointer
+ if (items_separator)
+ items_str = items_separator + strlen(SEPARATOR_ITEMS);
+
+ Item item = {.data = strdup(buffer), .type = NORMAL};
+ results->items = array_append(results->items, sizeof(Item), &results->num_items, items_capacity, &item);
+ } while (items_separator);
+}
+
+// parse EntityResults to FriendlyResults
+FriendlyResults parse_to_friendly(EntityResults entity_results) {
+ FriendlyResults results = {
+ .name = strdup(entity_results.name),
+ .exp = entity_results.exp,
+ .gold = entity_results.gold,
+ .level = entity_results.level,
+ .psycho = entity_results.psycho,
+ .splinters = entity_results.splinters,
+ .num_items = 0,
+ .saturation = 0
+ };
+
+ // create items array and add to it gear, drifs and items
+ results.items = safe_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);
+ add_items(&results, &items_cap, entity_results.items);
+
+ if (strlen(entity_results.saturation) > 0) {
+ int saturation_id;
+ sscanf(entity_results.saturation, "%d,%d", &saturation_id, &results.saturation);
+ results.saturation_type = get_saturation_type_from_id(saturation_id);
+ }
+
+ return results;
+}
+
+// parse EntityResults to EnemyResults
+EnemyResults parse_to_enemy(EntityResults entity_results) {
+ EnemyResults results = {.name = strdup(entity_results.name), .level = entity_results.level};
+ return results;
+}
+
+ParsedData parse_fight_results(byte_t *data, size_t length) {
+ ParsedData parsed_data = {.data_type = TYPE_FIGHT_RESULTS, .data_cleanup = cleanup_fight_results};
+
+ // ignore start and end indicators
+ data += strlen(FIGHT_RESULTS_START);
+ length -= strlen(FIGHT_RESULTS_START) + strlen(FIGHT_RESULTS_END);
+
+ FightResults *results = safe_malloc(sizeof(FightResults));
+
+ results->friendly_results = safe_malloc(INIT_ENTITIES_PER_SIDE * sizeof(FriendlyResults));
+ results->num_friendly = 0;
+ size_t friendly_cap = INIT_ENTITIES_PER_SIDE;
+
+ results->enemy_results = safe_malloc(INIT_ENTITIES_PER_SIDE * sizeof(EnemyResults));
+ results->num_enemy = 0;
+ size_t enemy_cap = INIT_ENTITIES_PER_SIDE;
+
+ byte_t *entity_separator;
+ do {
+ // get generic entity results
+ entity_separator = memmem(data, length, SEPARATOR_ENTITY, strlen(SEPARATOR_ENTITY));
+ 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) {
+ case TEAM_FRIEND: {
+ FriendlyResults res = parse_to_friendly(entity_results);
+ results->friendly_results = array_append(results->friendly_results, sizeof(FriendlyResults), &results->num_friendly, &friendly_cap, &res);
+ break;
+ }
+ case TEAM_ENEMY: {
+ EnemyResults res = parse_to_enemy(entity_results);
+ results->enemy_results = array_append(results->enemy_results, sizeof(EnemyResults), &results->num_enemy, &enemy_cap, &res);
+ break;
+ }
+ }
+
+ cleanup_struct(&entity_results, entity_fields, ARR_LEN(entity_fields));
+
+ // if there are still entities - modify data pointer and length
+ if (entity_separator) {
+ length -= entity_separator - data + strlen(SEPARATOR_ENTITY);
+ data = entity_separator + strlen(SEPARATOR_ENTITY);
+ }
+ } while (entity_separator);
+
+ parsed_data.data = results;
+ return parsed_data;
+}
diff --git a/analyzer/parser/fight_results/fight_results.h b/analyzer/parser/fight_results/fight_results.h
new file mode 100644
index 0000000..ec90394
--- /dev/null
+++ b/analyzer/parser/fight_results/fight_results.h
@@ -0,0 +1,60 @@
+// parser/fight_results.h
+// utilities for parsing the fight results data
+
+#include "parser/parser_defs.h"
+#include "utils/tcp.h"
+
+#ifndef FIGHT_RESULTS_H
+#define FIGHT_RESULTS_H
+
+// Payload structure:
+// FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END
+#define FIGHT_RESULTS_START "3;19;"
+#define FIGHT_RESULTS_END "[--];"
+
+// items parsing info
+#define INIT_ITEMS_CAPACITY 8
+#define SINGLE_ITEM_BUFFER_LEN 128
+#define SEPARATOR_ITEMS "[-]"
+
+// Item struct - for keeping item string and type
+typedef enum { NORMAL, DRIF, ORB, SYNG, SET, RARE, EPIC } ItemType;
+typedef struct {
+ char *data;
+ ItemType type;
+} Item;
+
+// fight results data for friendly
+typedef struct {
+ char *name;
+ int exp;
+ int gold;
+ Item *items;
+ size_t num_items;
+ int level;
+ int psycho;
+ int splinters;
+ const char *saturation_type;
+ int saturation;
+} FriendlyResults;
+
+// fight results data for enemy
+typedef struct {
+ char *name;
+ int level;
+} EnemyResults;
+
+#define INIT_ENTITIES_PER_SIDE 6
+
+// entire fight resutls
+typedef struct {
+ size_t num_friendly;
+ FriendlyResults *friendly_results;
+ size_t num_enemy;
+ EnemyResults *enemy_results;
+} FightResults;
+
+extern find_function_t find_fight_results;
+extern parse_function_t parse_fight_results;
+
+#endif
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
new file mode 100644
index 0000000..e2c2acb
--- /dev/null
+++ b/analyzer/parser/find_parse_functions.h
@@ -0,0 +1,6 @@
+// parser/find_parse_functions.h
+// array of find and parse functions for every result type
+
+#include "parser/fight_results/fight_results.h"
+
+FindParseFunctions all_find_parse_functions[] = {{find_fight_results, parse_fight_results}};
diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c
new file mode 100644
index 0000000..d00cd97
--- /dev/null
+++ b/analyzer/parser/parser.c
@@ -0,0 +1,64 @@
+#include "parser/parser.h"
+
+#include <stdlib.h>
+
+#include "parser/find_parse_functions.h"
+#include "utils/memory.h"
+
+// 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 < 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
+ if (data && (!earliest_data || data < earliest_data)) {
+ earliest_data = data;
+ *earliest_length = length;
+ *earliest_parse_fun = all_find_parse_functions[i].parse_fn;
+ }
+ }
+ return earliest_data;
+}
+
+ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data) {
+ size_t parsed_data_capacity = INIT_PARSED_DATA_CAPACITY;
+ ParsedData *parsed_data = safe_malloc(parsed_data_capacity * sizeof(ParsedData));
+ *n_parsed_data = 0;
+
+ int stream_offset = 0;
+ byte_t *earliest_data;
+ do {
+ // find earliest data to parse
+ int earliest_length;
+ parse_function_t *earliest_parse_fun;
+ earliest_data = find_earliest_data(stream, stream_offset, &earliest_length, &earliest_parse_fun);
+
+ // if start of data to parse was found, but not the end, then finish here
+ // set parsed_len, so that the beginning of that data is at the beginning of the stream
+ if (earliest_data && earliest_length == -1) {
+ *parsed_len = earliest_data - stream->data;
+ return parsed_data;
+ }
+
+ // if data to parse was found, parse it and update the offset
+ if (earliest_data) {
+ ParsedData new_parsed = earliest_parse_fun(earliest_data, earliest_length);
+ parsed_data = array_append(parsed_data, sizeof(ParsedData), n_parsed_data, &parsed_data_capacity, &new_parsed);
+ stream_offset = earliest_data - stream->data + earliest_length;
+ }
+ } while (earliest_data);
+
+ // if all was parsed - set parsed_len to stream_len and return the data
+ *parsed_len = stream->len;
+ return parsed_data;
+}
+
+void free_parsed_data(ParsedData *parsed_data, size_t n_parsed_data) {
+ for (size_t i = 0; i < n_parsed_data; i++) {
+ if (parsed_data[i].data_cleanup)
+ parsed_data[i].data_cleanup(parsed_data[i].data);
+ free(parsed_data[i].data);
+ }
+ free(parsed_data);
+}
diff --git a/analyzer/parser/parser.h b/analyzer/parser/parser.h
new file mode 100644
index 0000000..e7c46e8
--- /dev/null
+++ b/analyzer/parser/parser.h
@@ -0,0 +1,23 @@
+// parser/parser.h
+// main parser file
+
+#include "parser/fight_results/fight_results.h"
+#include "parser/parser_defs.h"
+#include "utils/tcp.h"
+
+#define INIT_PARSED_DATA_CAPACITY 4
+
+// parse a stream of TCP data
+// params:
+// - stream - the stream to parse
+// - parsed_len - pointer to inform how much of the stream was parsed (not all if e.g. start of data is there, but the end isn't yet)
+// - n_parsed_data - pointer to the count of parsed data that was retrieved from the stream
+// returns:
+// pointer to the array of parsed data
+extern ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data);
+
+// free the entire parsed data array from parse_stream function
+// params:
+// - parsed_data - parsed data to free
+// - n_parsed_data - count of parsed data
+extern void free_parsed_data(ParsedData *parsed_data, size_t n_parsed_data);
diff --git a/analyzer/parser/parser_defs.h b/analyzer/parser/parser_defs.h
new file mode 100644
index 0000000..75b0366
--- /dev/null
+++ b/analyzer/parser/parser_defs.h
@@ -0,0 +1,42 @@
+// parser/parser_defs.h
+// useful definitions for parser files
+
+#include "utils/tcp.h"
+
+#ifndef PARSER_DEFS_H
+#define PARSER_DEFS_H
+
+typedef enum { TYPE_FIGHT_RESULTS } ResultsEnum;
+
+// "generic" type for parsed data
+typedef struct {
+ ResultsEnum data_type; // type of data represented by an enum
+ void *data; // pointer to the parsed data
+ void (*data_cleanup)(void *data); // function to cleanup internal allocations inside data
+ // if no internal allocations - this should be NULL
+} ParsedData;
+
+// find the first results data of specific type in stream
+// params:
+// - stream - stream to find the data in
+// - offset - offset to start finding data from
+// - length - length of the data or -1 if there's no end yet
+// returns:
+// pointer to the beginning of data or NULL if there's none
+typedef byte_t *find_function_t(TCPStream *stream, int offset, int *length);
+
+// parse given data of specifc type
+// params:
+// - data - data to parse
+// - length - length of data
+// returns:
+// ParsedData struct including the parsed data
+typedef ParsedData parse_function_t(byte_t *data, size_t length);
+
+// struct for holding the find and parse function for every result type
+typedef struct {
+ find_function_t *find_fn;
+ parse_function_t *parse_fn;
+} FindParseFunctions;
+
+#endif
diff --git a/analyzer/parser/structs.c b/analyzer/parser/structs.c
new file mode 100644
index 0000000..dedf0d1
--- /dev/null
+++ b/analyzer/parser/structs.c
@@ -0,0 +1,51 @@
+#include "parser/structs.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils/memory.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 = safe_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