diff options
Diffstat (limited to 'analyzer/parser/fight_results')
| -rw-r--r-- | analyzer/parser/fight_results/fight_results.c | 392 | ||||
| -rw-r--r-- | analyzer/parser/fight_results/fight_results.h | 452 |
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 |
