aboutsummaryrefslogtreecommitdiff
path: root/analyzer/parser/fight_results
diff options
context:
space:
mode:
authorMosder <xmosder12@gmail.com>2026-07-21 20:28:29 +0200
committerMosder <xmosder12@gmail.com>2026-07-21 20:28:29 +0200
commite20946d04232cdf9d21aac3a2e7352a548ba50ae (patch)
treec30d8136c14e9576f8432e6c223af0997ed7f47b /analyzer/parser/fight_results
parentaab4922ae3dfac9e80e0328fb7dce2f40b9cffd2 (diff)
refactor: moved parser/fight_results.* into parser/fight_results/
Diffstat (limited to 'analyzer/parser/fight_results')
-rw-r--r--analyzer/parser/fight_results/fight_results.c392
-rw-r--r--analyzer/parser/fight_results/fight_results.h452
2 files changed, 844 insertions, 0 deletions
diff --git a/analyzer/parser/fight_results/fight_results.c b/analyzer/parser/fight_results/fight_results.c
new file mode 100644
index 0000000..a994674
--- /dev/null
+++ b/analyzer/parser/fight_results/fight_results.c
@@ -0,0 +1,392 @@
+#include "parser/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 "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);
+ }
+}
+
+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));
+ *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;
+}
+
+char *get_artifact_size(int artifact_size_id) {
+ return artifact_size_id < id_to_artifact_size_len ? id_to_artifact_size[artifact_size_id] : "null";
+}
+
+char *get_orb_name(int orb_name_id) {
+ return orb_name_id < id_to_orb_name_len ? id_to_orb_name[orb_name_id] : "null";
+}
+
+void get_stars(char *buffer, int incr_above_b1) {
+ char star_types[] = {'B', 'S', 'G'};
+ 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_GEARS);
+ Gear gear = get_gear(gears_str, gears_separator ? gears_separator - gears_str : strlen(gears_str));
+
+ // if there are still gears - modify gears_str pointer
+ if (gears_separator)
+ gears_str = gears_separator + strlen(SEPARATOR_GEARS);
+
+ Item item;
+
+ GearInfo gear_info = get_gear_info(gear.info, strlen(gear.info));
+ char buffer[SINGLE_ITEM_BUFFER_LEN];
+ char roman[5];
+ to_roman(roman, gear_info.rank);
+ sprintf(buffer, "[%s] %s", roman, gear_info.name);
+ switch (gear.type) {
+ case TYPE_SYNG_NORMAL:
+ // atoi should be fine, since syng_tier starts from 1 for syngs
+ if (atoi(gear.syng_tier))
+ sprintf(buffer, "%s (%d)", buffer, gear_info.lvl);
+ break;
+ default: {
+ char stars[6];
+ get_stars(stars, gear_info.stars);
+ strcat(buffer, stars);
+ break;
+ }
+ }
+ item.data = strdup(buffer);
+ destroy_gear_info(gear_info);
+
+ 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;
+ }
+
+ // 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;
+
+ // add orb to items if it exists
+ if (strlen(gear.orb)) {
+ Item item;
+ char buffer[SINGLE_ITEM_BUFFER_LEN];
+ Orb orb = get_orb(gear.orb, strlen(gear.orb));
+ sprintf(buffer, "%sorb %s", get_artifact_size(orb.size), get_orb_name(orb.id));
+ item.data = strdup(buffer);
+ destroy_orb(orb);
+ item.type = ORB;
+ // 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_gear(gear);
+ } 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));
+
+ // if there are still drifs - modify drifs_str pointer
+ if (drifs_separator)
+ drifs_str = drifs_separator + strlen(SEPARATOR_DRIFS);
+
+ Item item;
+ item.data = strdup(drif.name);
+ item.type = DRIF;
+
+ // 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);
+}
+
+char *get_item_name(int item_id) {
+ return item_id < id_to_item_name_len ? id_to_item_name[item_id] : "null";
+}
+
+void add_items(FriendlyResults *results, size_t *items_capacity, char *items_str) {
+ if (strlen(items_str) == 0)
+ return;
+
+ char *items_separator;
+ do {
+ char buffer[SINGLE_ITEM_BUFFER_LEN];
+ items_separator = strstr(items_str, SEPARATOR_ITEMS);
+
+ int item_id, item_count;
+ sscanf(items_str, "%d,%d", &item_id, &item_count);
+ sprintf(buffer, "%dx %s", item_count, get_item_name(item_id));
+
+ // if there are still items - modify items_str pointer
+ if (items_separator)
+ items_str = items_separator + strlen(SEPARATOR_ITEMS);
+
+ Item item;
+ item.data = strdup(buffer);
+ item.type = NORMAL;
+
+ // 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;
+ } while (items_separator);
+}
+
+char *get_saturation_type(int saturation_id) {
+ return saturation_id < id_to_saturation_type_len ? id_to_saturation_type[saturation_id] : "null";
+}
+
+// parse EntityResults to FriendlyResults
+FriendlyResults parse_to_friendly(EntityResults entity_results) {
+ FriendlyResults results;
+
+ results.name = strdup(entity_results.name);
+ results.exp = entity_results.exp;
+ results.gold = entity_results.gold;
+ results.level = entity_results.level;
+ results.psycho = entity_results.psycho;
+ results.splinters = entity_results.splinters;
+
+ // create items array and add to it gear, drifs and items
+ results.num_items = 0;
+ results.items = malloc(INIT_ITEMS_CAPACITY * sizeof(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);
+
+ results.saturation = 0;
+ 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(saturation_id);
+ }
+
+ return results;
+}
+
+// parse EntityResults to EnemyResults
+EnemyResults parse_to_enemy(EntityResults entity_results) {
+ EnemyResults results;
+
+ results.name = strdup(entity_results.name);
+ results.level = entity_results.level;
+
+ return results;
+}
+
+ParsedData parse_fight_results(byte_t *data, size_t length) {
+ ParsedData parsed_data;
+ parsed_data.data_type = TYPE_FIGHT_RESULTS;
+
+ // ignore start and end indicators
+ data += strlen(FIGHT_RESULTS_START);
+ length -= strlen(FIGHT_RESULTS_START) + strlen(FIGHT_RESULTS_END);
+
+ FightResults *results = malloc(sizeof(FightResults));
+ results->num_friendly = 0;
+ results->num_enemy = 0;
+
+ byte_t *entity_separator;
+ 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);
+
+ // depending on team - parse it into friendly or enemy results
+ switch (entity_results.team) {
+ case TEAM_FRIEND:
+ results->friendly_results[results->num_friendly++] = parse_to_friendly(entity_results);
+ break;
+ case TEAM_ENEMY:
+ results->enemy_results[results->num_enemy++] = parse_to_enemy(entity_results);
+ break;
+ }
+ destroy_entity_results(entity_results);
+
+ // 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;
+ parsed_data.data_cleanup = cleanup_fight_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..59d1e45
--- /dev/null
+++ b/analyzer/parser/fight_results/fight_results.h
@@ -0,0 +1,452 @@
+// 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 "[--];"
+#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
+#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 {
+ char *data;
+ 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;
+ int exp;
+ int gold;
+ Item *items;
+ size_t num_items;
+ int level;
+ int psycho;
+ int splinters;
+ char *saturation_type;
+ int saturation;
+} FriendlyResults;
+
+// fight results data for enemy
+typedef struct {
+ char *name;
+ int level;
+} EnemyResults;
+
+#define MAX_ENTITIES_PER_SIDE 8
+
+// entire fight resutls
+typedef struct {
+ size_t num_friendly;
+ FriendlyResults friendly_results[MAX_ENTITIES_PER_SIDE];
+ size_t num_enemy;
+ EnemyResults enemy_results[MAX_ENTITIES_PER_SIDE];
+} FightResults;
+
+extern find_function_t find_fight_results;
+extern parse_function_t parse_fight_results;
+
+#endif