aboutsummaryrefslogtreecommitdiff
path: root/analyzer/parser/fight_results
diff options
context:
space:
mode:
authorMaciej Borowiec <81316152+Mosder@users.noreply.github.com>2026-07-23 20:17:15 +0200
committerGitHub <noreply@github.com>2026-07-23 20:17:15 +0200
commit3419cad016b3c34d0630456d4390c70063462d91 (patch)
tree3a1aa4416e45f134d38f83d4ea2285f1972f9391 /analyzer/parser/fight_results
parent2e3f7fe39791cd671a68330f7ba7a5bb5fc6f9ae (diff)
parent682f9fc8288fdd01de1d88af87495d9e43f56b06 (diff)
LGTM
Diffstat (limited to 'analyzer/parser/fight_results')
-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
7 files changed, 561 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)},
+};