diff options
| author | Maciej Borowiec <81316152+Mosder@users.noreply.github.com> | 2026-07-23 20:17:15 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-23 20:17:15 +0200 |
| commit | 3419cad016b3c34d0630456d4390c70063462d91 (patch) | |
| tree | 3a1aa4416e45f134d38f83d4ea2285f1972f9391 /analyzer/parser/fight_results | |
| parent | 2e3f7fe39791cd671a68330f7ba7a5bb5fc6f9ae (diff) | |
| parent | 682f9fc8288fdd01de1d88af87495d9e43f56b06 (diff) | |
LGTM
Diffstat (limited to 'analyzer/parser/fight_results')
| -rw-r--r-- | analyzer/parser/fight_results/drif.h | 52 | ||||
| -rw-r--r-- | analyzer/parser/fight_results/entity.h | 67 | ||||
| -rw-r--r-- | analyzer/parser/fight_results/fight_results.c | 260 | ||||
| -rw-r--r-- | analyzer/parser/fight_results/fight_results.h | 60 | ||||
| -rw-r--r-- | analyzer/parser/fight_results/gear.h | 45 | ||||
| -rw-r--r-- | analyzer/parser/fight_results/gear_info.h | 44 | ||||
| -rw-r--r-- | analyzer/parser/fight_results/orb.h | 33 |
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)}, +}; |
