From f57066460f7c914b07a612d037eb5e57f617d5b2 Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 6 Jul 2026 10:06:10 +0200 Subject: feat: added basic structures for fight results parsing and base functionallity of finding only the necessary data. Still need to do tcp stream reassembly and the actual parsing of the data. --- analyzer/parser/fight_results.c | 18 +++++++ analyzer/parser/fight_results.h | 102 ++++++++++++++++++++++++++++++++++++++++ analyzer/parser/parser.c | 15 ++++++ analyzer/parser/parser.h | 21 +++++++++ 4 files changed, 156 insertions(+) create mode 100644 analyzer/parser/fight_results.c create mode 100644 analyzer/parser/fight_results.h create mode 100644 analyzer/parser/parser.c create mode 100644 analyzer/parser/parser.h (limited to 'analyzer/parser') diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c new file mode 100644 index 0000000..c6ad7cc --- /dev/null +++ b/analyzer/parser/fight_results.c @@ -0,0 +1,18 @@ +#include "fight_results.h" + +#include +#include + +char *find_fight_results(char *stream, int stream_len, int *length) { + char *start = memmem(stream, stream_len, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); + char *end = memmem(stream, stream_len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); + *length = start != NULL && end != NULL ? end - start + strlen(FIGHT_RESULTS_END) : -1; + return start; +} + +FightResults parse_fight_results(char *data, int length) { + // TODO: actually parse that data + printf("len=%d\n%.*s\n----------------\n", length, length, data); + FightResults tmp; + return tmp; +} diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h new file mode 100644 index 0000000..270aba8 --- /dev/null +++ b/analyzer/parser/fight_results.h @@ -0,0 +1,102 @@ +// parser/fight_results.h +// utilities for parsing the fight results data + +// Payload structure: +// FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END +// +// EntityResults structure: +// field (SEPARATOR_RESULT field)* +// fields are defined in order in the EntityResults struct + +#define FIGHT_RESULTS_START "3;19;" +#define FIGHT_RESULTS_END "[--];" +#define SEPARATOR_ENTITY "[--]" +#define SEPARATOR_RESULT "&" + +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; // possibly information who is the player - 1 for current player, 0 for others (need to double check) + int field32; + int field33; + char *field34; + char *saturation; +} EntityResults; + +// values of field "team" +#define TEAM_FRIEND 1 +#define TEAM_ENEMY 2 + +// fight results data for friendly +typedef struct { + char *name; + int exp; + int gold; + char *gear; + char *items; + int level; + int psycho; + char *drifs; + int splinters; + char *saturation_type; + int saturation; +} FriendlyResults; + +// fight results data for enemy +typedef struct { + char *name; + int level; +} EnemyResults; + +// entire fight resutls +typedef struct { + int num_friendly; + FriendlyResults *friendly_results; + int num_enemy; + EnemyResults *enemy_results; +} FightResults; + +// find the first fight results data in stream +// params: +// - stream - stream to find the fight results in +// - stream_len - length of the stream +// - length - length of the fight results data or -1 if there's no end yet +// returns: +// pointer to the beggining of fight results data or NULL if there's none +extern char *find_fight_results(char *stream, int stream_len, int *length); + +// parse given fight results +// params: +// - data - fight results data to parse +// - length - length of fight results data +// returns: +// FightResults struct containing parsed data +extern FightResults parse_fight_results(char *data, int length); diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c new file mode 100644 index 0000000..b551d8e --- /dev/null +++ b/analyzer/parser/parser.c @@ -0,0 +1,15 @@ +#include "parser.h" + +#include + +ParsedData *parse_stream(char *stream, int stream_len, int *parsed_len) { + // TODO: Need to do parsing of the entire thing (loop) and return all of the parsed data + if (stream_len > 0) { + int length; + char *start = find_fight_results(stream, stream_len, &length); + if (start != NULL) { + parse_fight_results(start, length); + } + } + return NULL; +} diff --git a/analyzer/parser/parser.h b/analyzer/parser/parser.h new file mode 100644 index 0000000..18b4b83 --- /dev/null +++ b/analyzer/parser/parser.h @@ -0,0 +1,21 @@ +// parser/parser.h +// main parser file + +#include "fight_results.h" + +// "generic" type for parsed data +typedef struct { + enum { FIGHT_RESULTS } data_type; + union { + FightResults fight_results; + } data; +} ParsedData; + +// parse a stream of TCP data +// params: +// - stream - the stream to parse +// - stream_len - length of the stream to parse +// - parsed_len - pointer to inform how much of the stream was parsed (not all if e.g. start of data is there, but the end isn't yet) +// returns: +// pointer to the array of parsed data +extern ParsedData *parse_stream(char *stream, int stream_len, int *parsed_len); -- cgit v1.2.3 From 0e9be0d902b44894ed747cabb82ea2120f2e4b45 Mon Sep 17 00:00:00 2001 From: Mosder Date: Fri, 10 Jul 2026 22:50:12 +0200 Subject: feat: added functionallity for TCP stream reassembly --- Makefile | 9 +- analyzer/analyzer.c | 45 ++++++---- analyzer/parser/fight_results.c | 10 +-- analyzer/parser/fight_results.h | 9 +- analyzer/parser/parser.c | 19 +++-- analyzer/parser/parser.h | 6 +- analyzer/utils/pcap.c | 54 ------------ analyzer/utils/pcap.h | 56 ------------- analyzer/utils/structures/heap.h | 3 + analyzer/utils/tcp.c | 172 +++++++++++++++++++++++++++++++++++++++ analyzer/utils/tcp.h | 68 ++++++++++++++++ analyzer/utils/tcp_seq_math.c | 41 ++++++++++ analyzer/utils/tcp_seq_math.h | 28 +++++++ 13 files changed, 375 insertions(+), 145 deletions(-) delete mode 100644 analyzer/utils/pcap.c delete mode 100644 analyzer/utils/pcap.h create mode 100644 analyzer/utils/tcp.c create mode 100644 analyzer/utils/tcp.h create mode 100644 analyzer/utils/tcp_seq_math.c create mode 100644 analyzer/utils/tcp_seq_math.h (limited to 'analyzer/parser') diff --git a/Makefile b/Makefile index f298b4d..e5a9c54 100644 --- a/Makefile +++ b/Makefile @@ -8,10 +8,15 @@ compile: utils capture analyzer parser gcc *.o main.c -lpcap -o branal rm *.o -utils: +utils: structures gcc -c analyzer/utils/fs.c gcc -c analyzer/utils/time.c - gcc -c analyzer/utils/pcap.c + gcc -c analyzer/utils/tcp.c + gcc -c analyzer/utils/tcp_seq_math.c + +structures: + gcc -c analyzer/utils/structures/hashmap.c + gcc -c analyzer/utils/structures/heap.c capture: gcc -c analyzer/capture.c diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index 7db377e..2e5caf8 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -1,7 +1,32 @@ #include "analyzer.h" #include "parser/parser.h" -#include "utils/pcap.h" +#include "utils/tcp.h" + +// loop through packets received from pcap_handle and analyze the TCP stream +void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle) { + // read packets from handle + struct pcap_pkthdr *packet_data; + const byte_t *packet; + while (pcap_next_ex(pcap_handle, &packet_data, &packet) == 1) { + // get new segment + TCPSegment segment = get_segment_from_packet(packet, packet_data); + + // handle the new segment + if (handle_segment(stream, segment)) { + // if it was added to the stream - parse it + size_t parsed_len; + size_t n_parsed_data; + ParsedData *parsed_data = parse_stream(stream, &parsed_len, &n_parsed_data); + + // if some bytes were parsed - remove them from the stream + if (parsed_len > 0) + remove_parsed_data(stream, parsed_len); + + // TODO: handle parsed data + } + } +} void analyze_file(char *path) { // open the file @@ -14,19 +39,9 @@ void analyze_file(char *path) { return; } - struct pcap_pkthdr *packet_data; - const byte_t *packet; - const byte_t *payload; - - // read packets from the file - while (pcap_next_ex(handle, &packet_data, &packet) == 1) { - int payload_len; - uint32_t tcp_seq; - payload = get_payload_from_packet(packet, packet_data, &payload_len, &tcp_seq); - // TODO: Stream reassembly using TCP sequence number - int parsed_len; - parse_stream((char *)payload, payload_len, &parsed_len); - } - + // analyze the file + TCPStream stream = new_stream(); + analyzer_loop(&stream, handle); + destroy_stream(stream); pcap_close(handle); } diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index c6ad7cc..1db79ea 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -3,16 +3,16 @@ #include #include -char *find_fight_results(char *stream, int stream_len, int *length) { - char *start = memmem(stream, stream_len, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); - char *end = memmem(stream, stream_len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); +byte_t *find_fight_results(TCPStream *stream, int *length) { + byte_t *start = memmem(stream->data, stream->len, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); + byte_t *end = memmem(stream->data, stream->len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); *length = start != NULL && end != NULL ? end - start + strlen(FIGHT_RESULTS_END) : -1; return start; } -FightResults parse_fight_results(char *data, int length) { +FightResults parse_fight_results(byte_t *data, size_t length) { // TODO: actually parse that data - printf("len=%d\n%.*s\n----------------\n", length, length, data); + printf("len=%zu\n%.*s\n----------------\n", length, (int)length, data); FightResults tmp; return tmp; } diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index 270aba8..b275f2b 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -1,6 +1,8 @@ // parser/fight_results.h // utilities for parsing the fight results data +#include "../utils/tcp.h" + // Payload structure: // FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END // @@ -87,11 +89,10 @@ typedef struct { // find the first fight results data in stream // params: // - stream - stream to find the fight results in -// - stream_len - length of the stream // - length - length of the fight results data or -1 if there's no end yet // returns: -// pointer to the beggining of fight results data or NULL if there's none -extern char *find_fight_results(char *stream, int stream_len, int *length); +// pointer to the beginning of fight results data or NULL if there's none +extern byte_t *find_fight_results(TCPStream *stream, int *length); // parse given fight results // params: @@ -99,4 +100,4 @@ extern char *find_fight_results(char *stream, int stream_len, int *length); // - length - length of fight results data // returns: // FightResults struct containing parsed data -extern FightResults parse_fight_results(char *data, int length); +extern FightResults parse_fight_results(byte_t *data, size_t length); diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c index b551d8e..7fb155f 100644 --- a/analyzer/parser/parser.c +++ b/analyzer/parser/parser.c @@ -2,14 +2,19 @@ #include -ParsedData *parse_stream(char *stream, int stream_len, int *parsed_len) { +ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data) { // TODO: Need to do parsing of the entire thing (loop) and return all of the parsed data - if (stream_len > 0) { - int length; - char *start = find_fight_results(stream, stream_len, &length); - if (start != NULL) { - parse_fight_results(start, length); - } + int length; + byte_t *start = find_fight_results(stream, &length); + if (start == NULL) { + *parsed_len = stream->len; + } + else if (length == -1) { + *parsed_len = 0; + } + else { + parse_fight_results(start, length); + *parsed_len = start - stream->data + length; } return NULL; } diff --git a/analyzer/parser/parser.h b/analyzer/parser/parser.h index 18b4b83..088bb5d 100644 --- a/analyzer/parser/parser.h +++ b/analyzer/parser/parser.h @@ -3,6 +3,8 @@ #include "fight_results.h" +#include "../utils/tcp.h" + // "generic" type for parsed data typedef struct { enum { FIGHT_RESULTS } data_type; @@ -14,8 +16,8 @@ typedef struct { // parse a stream of TCP data // params: // - stream - the stream to parse -// - stream_len - length of the stream to parse // - parsed_len - pointer to inform how much of the stream was parsed (not all if e.g. start of data is there, but the end isn't yet) +// - n_parsed_data - pointer to the count of parsed data that was retrieved from the stream // returns: // pointer to the array of parsed data -extern ParsedData *parse_stream(char *stream, int stream_len, int *parsed_len); +extern ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data); diff --git a/analyzer/utils/pcap.c b/analyzer/utils/pcap.c deleted file mode 100644 index d38cbff..0000000 --- a/analyzer/utils/pcap.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "pcap.h" - -#include -#include -#include - -const byte_t *get_payload_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq) { - // get Ethernet header - const struct ether_header *eth_header = (const struct ether_header *)packet; - int eth_header_len = sizeof(struct ether_header); - - // check if it's IP - if (ntohs(eth_header->ether_type) != ETHERTYPE_IP) { - *payload_len = -1; - return NULL; - } - - // get IP header - const struct ip *ip_header = (const struct ip *)(packet + eth_header_len); - int ip_header_len = ip_header->ip_hl * 4; - - // check if it's TCP - if (ip_header->ip_p != IPPROTO_TCP) { - *payload_len = -1; - return NULL; - } - - // get TCP header - const struct tcphdr *tcp_header = (const struct tcphdr *)(packet + eth_header_len + ip_header_len); - int tcp_header_len = tcp_header->th_off * 4; - - // get TCP sequence number - *tcp_seq = ntohl(tcp_header->seq); - - // get payload - int total_header_len = eth_header_len + ip_header_len + tcp_header_len; - *payload_len = packet_data->caplen - total_header_len; - return packet + total_header_len; -} - -PendingPayload new_pending(const byte_t *payload, int payload_len) { - PendingPayload tmp; - return tmp; -} - -byte_t *free_parsed_data(byte_t *stream, uint32_t stream_seq, int stream_len, int parsed_len) { - // TODO: impement - return NULL; -} - -int add_segment_to_stream(byte_t *stream, uint32_t stream_seq, int stream_len, const byte_t *payload, uint32_t payload_seq, int payload_len, HashMap *pending) { - // TODO: impement - return 0; -} diff --git a/analyzer/utils/pcap.h b/analyzer/utils/pcap.h deleted file mode 100644 index 10f01f1..0000000 --- a/analyzer/utils/pcap.h +++ /dev/null @@ -1,56 +0,0 @@ -// utils/pcap.h -// helper functions for usage of libpcap and for handling TCP segments in general - -#include - -#include "hashmap.h" - -typedef unsigned char byte_t; - -// get payload from a TCP/IP packet -// params: -// - packet - captured packet -// - packet_data - metadata about packet -// - payload_len - pointer to save the payload length to or -1 if not a correct packet -// - tcp_seq - pointer to save the TCP sequence number -// returns: -// pointer to the payload or NULL if not a correct packet -extern const byte_t *get_payload_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq); - -// struct to persistently store pending payloads -typedef struct { - byte_t *payload; - int payload_len; -} PendingPayload; - -// store persistently and return a PendingPayload, the payload is malloced and needs to be freed -// params: -// - payload - payload to store -// - payload_len - length of the payload to store -// returns: -// PendingPayload struct with stored payload -extern PendingPayload new_pending(const byte_t *payload, int payload_len); - -// remove parsed part of the stream from memory and return only unparsed part -// params: -// - stream - stream to free a part of -// - stream_seq - pointer to the current TCP sequence number at the beginning of the stream, updated to the new one in the function -// - stream_len - pointer to the current stream length, updated to the new length in the function -// - parsed_len - count of n first bytes of the stream that were parsed -// returns: -// pointer to the new, reduced stream -extern byte_t *free_parsed_data(byte_t *stream, uint32_t stream_seq, int stream_len, int parsed_len); - -// add TCP segment to stream -// params: -// - stream - current stream -// - stream_seq - TCP sequence number at the beginning of the stream -// - stream_len - current length of the stream -// - payload - payload to add to stream -// - payload_seq - payload TCP sequence number -// - payload_len - length of the payload -// - pending - hashmap (seq -> PendingPayload) of pending payloads to be added to stream -// returns: -// 1 if the payload was added to the stream, 0 if it was discarded or saved to pending -extern int -add_segment_to_stream(byte_t *stream, uint32_t stream_seq, int stream_len, const byte_t *payload, uint32_t payload_seq, int payload_len, HashMap *pending); diff --git a/analyzer/utils/structures/heap.h b/analyzer/utils/structures/heap.h index e960fb7..eb3430b 100644 --- a/analyzer/utils/structures/heap.h +++ b/analyzer/utils/structures/heap.h @@ -6,6 +6,8 @@ #define INIT_HEAP_DEPTH 7 #define INIT_HEAP_CAPACITY (1 << INIT_HEAP_DEPTH) - 1 +#ifndef HEAP_DEFINED +#define HEAP_DEFINED typedef struct { void **data; size_t n_elements; @@ -14,6 +16,7 @@ typedef struct { int (*compare)(void *element1, void *element2); void (*element_cleanup)(void *element); } Heap; +#endif // create a new heap // params: diff --git a/analyzer/utils/tcp.c b/analyzer/utils/tcp.c new file mode 100644 index 0000000..56df842 --- /dev/null +++ b/analyzer/utils/tcp.c @@ -0,0 +1,172 @@ +#include "tcp.h" + +#include +#include +#include +#include +#include + +#include "tcp_seq_math.h" + +// initalize default TCP segment +TCPSegment new_segment() { + TCPSegment segment; + segment.payload = NULL; + segment.len = -1; + segment.seq = 0; + return segment; +} + +// segments compare function to use with heap +int segments_compare(void *segment1, void *segment2) { + TCPSegment *seg1 = segment1; + TCPSegment *seg2 = segment2; + return seq_before(seg1->seq, seg2->seq); +} + +// segment cleanup function to use with heap +void segment_cleanup(void *segment) { + TCPSegment *seg = segment; + free(seg->payload); +} + +TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data) { + TCPSegment segment = new_segment(); + + // get Ethernet header + const struct ether_header *eth_header = (const struct ether_header *)packet; + int eth_header_len = sizeof(struct ether_header); + + // check if it's IP + if (ntohs(eth_header->ether_type) != ETHERTYPE_IP) + return segment; + + // get IP header + const struct ip *ip_header = (const struct ip *)(packet + eth_header_len); + int ip_header_len = ip_header->ip_hl * 4; + + // check if it's TCP + if (ip_header->ip_p != IPPROTO_TCP) + return segment; + + // get TCP header + const struct tcphdr *tcp_header = (const struct tcphdr *)(packet + eth_header_len + ip_header_len); + int tcp_header_len = tcp_header->th_off * 4; + + // get TCP sequence number + segment.seq = ntohl(tcp_header->seq); + + // get payload + int total_header_len = eth_header_len + ip_header_len + tcp_header_len; + segment.len = packet_data->caplen - total_header_len; + segment.payload = malloc(segment.len); + memcpy(segment.payload, packet + total_header_len, segment.len); + return segment; +} + +TCPStream new_stream() { + TCPStream stream; + stream.data = NULL; // NULL to differentiate between first and other segments + stream.len = 0; + stream.capacity = INIT_STREAM_CAPACITY; + stream.seq = 0; + stream.pending = heap_new(sizeof(TCPSegment), segments_compare, segment_cleanup); + return stream; +} + +void destroy_stream(TCPStream stream) { + free(stream.data); + heap_free(stream.pending); +} + +void remove_parsed_data(TCPStream *stream, size_t parsed_len) { + memmove(stream->data, stream->data + parsed_len, stream->len - parsed_len); + stream->len -= parsed_len; + stream->seq += parsed_len; +} + +// get the index of the first relevant byte in segment (its tcp sequence number is the one stream is expecting) +// return the index of the first relevant byte in payload or -1 if segment isn't directly connected to stream +int get_first_relevant_byte_index(TCPStream stream, TCPSegment segment) { + // expected tcp sequence numbers for stream and segment + uint32_t stream_expected = stream.seq + stream.len; + uint32_t segment_expected = segment.seq + segment.len; + if (seq_in_range_ie(stream_expected, segment.seq, segment_expected)) + return stream_expected - segment.seq; + return -1; +} + +// check if the segment is in acceptable range to the stream +int in_acceptable_range(TCPStream stream, TCPSegment segment) { + // expected tcp sequence number for stream and max acceptable sequence number for segment + uint32_t stream_expected = stream.seq + stream.len; + uint32_t max_acceptable_seq = stream_expected + MAX_SEQ_OFFSET; + return seq_in_range_ei(segment.seq, stream_expected, max_acceptable_seq); +} + +// expand stream capacity exponentially to fit new_len +void expand_stream_capacity(TCPStream *stream, size_t new_len) { + while (stream->capacity < new_len) + stream->capacity <<= 1; + stream->data = realloc(stream->data, stream->capacity); +} + +// add segment directly to stream +void add_segment(TCPStream *stream, TCPSegment segment, size_t data_start) { + size_t new_stream_len = stream->len + segment.len - data_start; + if (new_stream_len > stream->capacity) + expand_stream_capacity(stream, new_stream_len); + memcpy(stream->data + stream->len, segment.payload + data_start, segment.len - data_start); + stream->len = new_stream_len; +} + +// add pending segments to the stream +void add_pending_segments(TCPStream *stream) { + int top_changed = 1; + TCPSegment *top; + while (top_changed && (top = heap_top_get(stream->pending))) { + top_changed = 0; + + // add pending segment it it became directly connected + int data_start = get_first_relevant_byte_index(*stream, *top); + if (data_start != -1) { + add_segment(stream, *top, data_start); + top_changed = 1; + } + + // remove top if it was just added or no longer is in acceptable range (e.g. was contained within another one pending) + if (top_changed || !in_acceptable_range(*stream, *top)) { + heap_top_remove(stream->pending); + } + } +} + +int handle_segment(TCPStream *stream, TCPSegment segment) { + // add the segment if it's the first one + if (stream->data == NULL) { + stream->data = malloc(stream->capacity); + stream->seq = segment.seq; + add_segment(stream, segment, 0); + free(segment.payload); + return 1; + } + + // add segment directly to the stream if it's directly connected + int data_start = get_first_relevant_byte_index(*stream, segment); + if (data_start != -1) { + add_segment(stream, segment, data_start); + free(segment.payload); + add_pending_segments(stream); + return 1; + } + + // add to pending if segment is in acceptable range: + if (in_acceptable_range(*stream, segment)) { + heap_insert(stream->pending, &segment); + return 0; + } + + // if neither, discard entirely + free(segment.payload); + return 0; +} diff --git a/analyzer/utils/tcp.h b/analyzer/utils/tcp.h new file mode 100644 index 0000000..37ab4bc --- /dev/null +++ b/analyzer/utils/tcp.h @@ -0,0 +1,68 @@ +// utils/tcp.h +// functions for handling TCP streams/segments + +#include + +#include "structures/heap.h" + +typedef unsigned char byte_t; + +// struct to persistently store TCP segments +// payload field is malloced and needs to be freed +#ifndef TCP_SEGMENT_DEFINED +#define TCP_SEGMENT_DEFINED +typedef struct { + byte_t *payload; + size_t len; + uint32_t seq; +} TCPSegment; +#endif + +// get TCP segment from captured packet +// params: +// - packet - captured packet +// - packet_data - metadata about packet +// returns: +// TCPSegment from packet, payload is NULL and len is -1 if the received packet was incorrect +extern TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data); + +// struct that stores all the information about stream +#ifndef TCP_STREAM_DEFINED +#define TCP_STREAM_DEFINED +typedef struct { + byte_t *data; // bytes in the stream + size_t len; // length of the stream + size_t capacity; // capacity of the data buffer + uint32_t seq; // TCP sequence number of the first byte in the stream + Heap *pending; // min heap of segments yet to be added to stream (sorted by TCP sequence number) +} TCPStream; +#endif + +#define INIT_STREAM_CAPACITY 256 + +// initialize new stream +// returns: +// newly created TCPStream structure with initial values +extern TCPStream new_stream(); + +// cleanup memory allocations within stream +// params: +// stream - stream to destroy +extern void destroy_stream(TCPStream stream); + +// remove parsed data from the stream +// params: +// - stream - pointer to the stream to remove a part of +// - parsed_len - count of n first bytes of the stream that were parsed +extern void remove_parsed_data(TCPStream *stream, size_t parsed_len); + +// max difference between expected TCP seq and received TCP seq to still accept it into pending +#define MAX_SEQ_OFFSET 100000 + +// add TCP segment to stream, pending or completely discard it +// params: +// - stream - pointer to the current stream +// - segment - segment to handle +// returns: +// 1 if the segment was added to the stream, 0 if it was discarded or saved to pending +extern int handle_segment(TCPStream *stream, TCPSegment segment); diff --git a/analyzer/utils/tcp_seq_math.c b/analyzer/utils/tcp_seq_math.c new file mode 100644 index 0000000..272cb5a --- /dev/null +++ b/analyzer/utils/tcp_seq_math.c @@ -0,0 +1,41 @@ +#include "tcp_seq_math.h" + +int seq_after(uint32_t a, uint32_t b) { + return (int32_t)(a - b) > 0; +} + +int seq_before(uint32_t a, uint32_t b) { + return (int32_t)(a - b) < 0; +} + +int seq_after_eq(uint32_t a, uint32_t b) { + return (int32_t)(a - b) >= 0; +} + +int seq_before_eq(uint32_t a, uint32_t b) { + return (int32_t)(a - b) <= 0; +} + +int seq_in_range_ii(uint32_t value, uint32_t range_start, uint32_t range_end) { + int after_eq = seq_after_eq(value, range_start); + int before_eq = seq_before_eq(value, range_end); + return after_eq && before_eq; +} + +int seq_in_range_ee(uint32_t value, uint32_t range_start, uint32_t range_end) { + int after = seq_after(value, range_start); + int before = seq_before(value, range_end); + return after && before; +} + +int seq_in_range_ie(uint32_t value, uint32_t range_start, uint32_t range_end) { + int after_eq = seq_after_eq(value, range_start); + int before = seq_before(value, range_end); + return after_eq && before; +} + +int seq_in_range_ei(uint32_t value, uint32_t range_start, uint32_t range_end) { + int after = seq_after(value, range_start); + int before_eq = seq_before_eq(value, range_end); + return after && before_eq; +} diff --git a/analyzer/utils/tcp_seq_math.h b/analyzer/utils/tcp_seq_math.h new file mode 100644 index 0000000..c4da5f9 --- /dev/null +++ b/analyzer/utils/tcp_seq_math.h @@ -0,0 +1,28 @@ +// utils/tcp_seq_math.h +// helper functions for math regarding tcp sequence numbers which correctly handle overflow + +#include + +// check if a is after b +extern int seq_after(uint32_t a, uint32_t b); + +// check if a is before b +extern int seq_before(uint32_t a, uint32_t b); + +// check if a is after or equal to b +extern int seq_after_eq(uint32_t a, uint32_t b); + +// check if a is before or equal to b +extern int seq_before_eq(uint32_t a, uint32_t b); + +// check if value is in [range_start; range_end] +extern int seq_in_range_ii(uint32_t value, uint32_t range_start, uint32_t range_end); + +// check if value is in (range_start; range_end) +extern int seq_in_range_ee(uint32_t value, uint32_t range_start, uint32_t range_end); + +// check if value is in [range_start; range_end) +extern int seq_in_range_ie(uint32_t value, uint32_t range_start, uint32_t range_end); + +// check if value is in (range_start; range_end] +extern int seq_in_range_ei(uint32_t value, uint32_t range_start, uint32_t range_end); -- cgit v1.2.3 From 32bb25cc2666809759371436a6450574b00c0e6d Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 13 Jul 2026 15:32:27 +0200 Subject: feat: added generic functionallity for getting the parsed data from stream --- analyzer/analyzer.c | 24 +++++++++-- analyzer/parser/fight_results.c | 33 +++++++++++--- analyzer/parser/fight_results.h | 40 +++++++++-------- analyzer/parser/find_parse_functions.h | 10 +++++ analyzer/parser/parser.c | 79 ++++++++++++++++++++++++++++------ analyzer/parser/parser.h | 18 ++++---- analyzer/parser/parser_defs.h | 46 ++++++++++++++++++++ 7 files changed, 200 insertions(+), 50 deletions(-) create mode 100644 analyzer/parser/find_parse_functions.h create mode 100644 analyzer/parser/parser_defs.h (limited to 'analyzer/parser') diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index 2e5caf8..5997c6d 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -1,10 +1,12 @@ #include "analyzer.h" +#include + #include "parser/parser.h" #include "utils/tcp.h" // loop through packets received from pcap_handle and analyze the TCP stream -void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle) { +void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle, void (*parsed_data_handler)(ParsedData *parsed_data, size_t n_parsed_data)) { // read packets from handle struct pcap_pkthdr *packet_data; const byte_t *packet; @@ -23,7 +25,23 @@ void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle) { if (parsed_len > 0) remove_parsed_data(stream, parsed_len); - // TODO: handle parsed data + // if there's parsed data - call the parsed data handler + if (n_parsed_data > 0) + parsed_data_handler(parsed_data, n_parsed_data); + + // free the entire parsed data memory + free_parsed_data(parsed_data, n_parsed_data); + } + } +} + +// file analyzer parsed data handler +void file_parsed_data_handler(ParsedData *parsed_data, size_t n_parsed_data) { + for (size_t i = 0; i < n_parsed_data; i++) { + switch (parsed_data[i].data_type) { + case TYPE_FIGHT_RESULTS: + printf("Fight results\n"); + break; } } } @@ -41,7 +59,7 @@ void analyze_file(char *path) { // analyze the file TCPStream stream = new_stream(); - analyzer_loop(&stream, handle); + analyzer_loop(&stream, handle, file_parsed_data_handler); destroy_stream(stream); pcap_close(handle); } diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index 1db79ea..8275af2 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -1,18 +1,39 @@ #include "fight_results.h" #include +#include #include -byte_t *find_fight_results(TCPStream *stream, int *length) { - byte_t *start = memmem(stream->data, stream->len, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); - byte_t *end = memmem(stream->data, stream->len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); +// 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++) { + free(fight_results->friendly_results[i].name); + free(fight_results->friendly_results[i].gear); + free(fight_results->friendly_results[i].items); + free(fight_results->friendly_results[i].drifs); + } + 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, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); + byte_t *end = memmem(stream->data + offset, stream->len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); *length = start != NULL && end != NULL ? end - start + strlen(FIGHT_RESULTS_END) : -1; return start; } -FightResults parse_fight_results(byte_t *data, size_t length) { +ParsedData parse_fight_results(byte_t *data, size_t length) { + ParsedData parsed_data; + parsed_data.data_type = TYPE_FIGHT_RESULTS; + // TODO: actually parse that data printf("len=%zu\n%.*s\n----------------\n", length, (int)length, data); - FightResults tmp; - return tmp; + + parsed_data.data = NULL; + parsed_data.data_cleanup = NULL; + // parsed_data.data_cleanup = cleanup_fight_results; + return parsed_data; } diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index b275f2b..d8064ca 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -2,6 +2,7 @@ // utilities for parsing the fight results data #include "../utils/tcp.h" +#include "parser_defs.h" // Payload structure: // FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END @@ -15,6 +16,8 @@ #define SEPARATOR_ENTITY "[--]" #define SEPARATOR_RESULT "&" +#ifndef ENTITY_RESULTS_DEFINED +#define ENTITY_RESULTS_DEFINED typedef struct { int team; // friend or enemy char *name; @@ -52,12 +55,15 @@ typedef struct { char *field34; char *saturation; } EntityResults; +#endif // values of field "team" #define TEAM_FRIEND 1 #define TEAM_ENEMY 2 // fight results data for friendly +#ifndef FRIENDLY_RESULTS_DEFINED +#define FRIENDLY_RESULTS_DEFINED typedef struct { char *name; int exp; @@ -71,33 +77,29 @@ typedef struct { char *saturation_type; int saturation; } FriendlyResults; +#endif // fight results data for enemy +#ifndef ENEMY_RESULTS_DEFINED +#define ENEMY_RESULTS_DEFINED typedef struct { char *name; int level; } EnemyResults; +#endif + +#define MAX_ENTITIES_PER_SIDE 8 // entire fight resutls +#ifndef FIGHT_RESULTS_DEFINED +#define FIGHT_RESULTS_DEFINED typedef struct { - int num_friendly; - FriendlyResults *friendly_results; - int num_enemy; - EnemyResults *enemy_results; + size_t num_friendly; + FriendlyResults friendly_results[MAX_ENTITIES_PER_SIDE]; + size_t num_enemy; + EnemyResults enemy_results[MAX_ENTITIES_PER_SIDE]; } FightResults; +#endif -// find the first fight results data in stream -// params: -// - stream - stream to find the fight results in -// - length - length of the fight results data or -1 if there's no end yet -// returns: -// pointer to the beginning of fight results data or NULL if there's none -extern byte_t *find_fight_results(TCPStream *stream, int *length); - -// parse given fight results -// params: -// - data - fight results data to parse -// - length - length of fight results data -// returns: -// FightResults struct containing parsed data -extern FightResults parse_fight_results(byte_t *data, size_t length); +extern find_function_t find_fight_results; +extern parse_function_t parse_fight_results; diff --git a/analyzer/parser/find_parse_functions.h b/analyzer/parser/find_parse_functions.h new file mode 100644 index 0000000..4e53274 --- /dev/null +++ b/analyzer/parser/find_parse_functions.h @@ -0,0 +1,10 @@ +// parser/find_parse_functions.h +// array of find and parse functions for every result type + +#include "fight_results.h" + +#define RESULT_TYPE_COUNT 1 + +FindParseFunctions all_find_parse_functions[RESULT_TYPE_COUNT] = { + {.find_fn = find_fight_results, .parse_fn = parse_fight_results} +}; diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c index 7fb155f..246635c 100644 --- a/analyzer/parser/parser.c +++ b/analyzer/parser/parser.c @@ -2,19 +2,72 @@ #include -ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data) { - // TODO: Need to do parsing of the entire thing (loop) and return all of the parsed data - int length; - byte_t *start = find_fight_results(stream, &length); - if (start == NULL) { - *parsed_len = stream->len; - } - else if (length == -1) { - *parsed_len = 0; +#include "find_parse_functions.h" + +// expand parsed data exponentially to fit el_count elements +ParsedData *expand_parsed_data(ParsedData *parsed_data, size_t *capacity, size_t el_count) { + while (*capacity < el_count) + *capacity <<= 1; + return realloc(parsed_data, *capacity * sizeof(ParsedData)); +} + +// find earliest data to parse in stream +byte_t *find_earliest_data(TCPStream *stream, int stream_offset, int *earliest_length, parse_function_t **earliest_parse_fun) { + byte_t *earliest_data = NULL; + for (size_t i = 0; i < RESULT_TYPE_COUNT; i++) { + int length; + byte_t *data = all_find_parse_functions[i].find_fn(stream, stream_offset, &length); + // if it's the first found or earlier than earliest - save it + if (data && (!earliest_data || data < earliest_data)) { + earliest_data = data; + *earliest_length = length; + *earliest_parse_fun = all_find_parse_functions[i].parse_fn; + } } - else { - parse_fight_results(start, length); - *parsed_len = start - stream->data + length; + return earliest_data; +} + +ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data) { + size_t parsed_data_capacity = INIT_PARSED_DATA_CAPACITY; + ParsedData *parsed_data = malloc(parsed_data_capacity * sizeof(ParsedData)); + *n_parsed_data = 0; + + int stream_offset = 0; + byte_t *earliest_data; + do { + // find earliest data to parse + int earliest_length; + parse_function_t *earliest_parse_fun; + earliest_data = find_earliest_data(stream, stream_offset, &earliest_length, &earliest_parse_fun); + + // if start of data to parse was found, but not the end, then finish here + // set parsed_len, so that the beginning of that data is at the beginning of the stream + if (earliest_data && earliest_length == -1) { + *parsed_len = earliest_data - stream->data; + return parsed_data; + } + + // if data to parse was found, parse it and update the offset + if (earliest_data) { + // if no capacity in parsed data array - expand it + if (*n_parsed_data >= parsed_data_capacity) + parsed_data = expand_parsed_data(parsed_data, &parsed_data_capacity, *n_parsed_data + 1); + + parsed_data[(*n_parsed_data)++] = earliest_parse_fun(earliest_data, earliest_length); + stream_offset = earliest_data - stream->data + earliest_length; + } + } while (earliest_data); + + // if all was parsed - set parsed_len to stream_len and return the data + *parsed_len = stream->len; + return parsed_data; +} + +void free_parsed_data(ParsedData *parsed_data, size_t n_parsed_data) { + for (size_t i = 0; i < n_parsed_data; i++) { + if (parsed_data[i].data_cleanup) + parsed_data[i].data_cleanup(parsed_data[i].data); + free(parsed_data[i].data); } - return NULL; + free(parsed_data); } diff --git a/analyzer/parser/parser.h b/analyzer/parser/parser.h index 088bb5d..e2d5c5d 100644 --- a/analyzer/parser/parser.h +++ b/analyzer/parser/parser.h @@ -1,17 +1,11 @@ // parser/parser.h // main parser file -#include "fight_results.h" - #include "../utils/tcp.h" +#include "fight_results.h" +#include "parser_defs.h" -// "generic" type for parsed data -typedef struct { - enum { FIGHT_RESULTS } data_type; - union { - FightResults fight_results; - } data; -} ParsedData; +#define INIT_PARSED_DATA_CAPACITY 4 // parse a stream of TCP data // params: @@ -21,3 +15,9 @@ typedef struct { // returns: // pointer to the array of parsed data extern ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data); + +// free the entire parsed data array from parse_stream function +// params: +// - parsed_data - parsed data to free +// - n_parsed_data - count of parsed data +extern void free_parsed_data(ParsedData *parsed_data, size_t n_parsed_data); diff --git a/analyzer/parser/parser_defs.h b/analyzer/parser/parser_defs.h new file mode 100644 index 0000000..f99c2f3 --- /dev/null +++ b/analyzer/parser/parser_defs.h @@ -0,0 +1,46 @@ +// parser/parser_defs.h +// useful definitions for parser files + +#include "../utils/tcp.h" + +#ifndef RESULTS_ENUM_DEFINED +#define RESULTS_ENUM_DEFINED +typedef enum { TYPE_FIGHT_RESULTS } ResultsEnum; +#endif + +#ifndef PARSED_DATA_DEFINED +#define PARSED_DATA_DEFINED +// "generic" type for parsed data +typedef struct { + ResultsEnum data_type; // type of data represented by an enum + void *data; // pointer to the parsed data + void (*data_cleanup)(void *data); // function to cleanup internal allocations inside data + // if no internal allocations - this should be NULL +} ParsedData; +#endif + +// find the first results data of specific type in stream +// params: +// - stream - stream to find the data in +// - offset - offset to start finding data from +// - length - length of the data or -1 if there's no end yet +// returns: +// pointer to the beginning of data or NULL if there's none +typedef byte_t *find_function_t(TCPStream *stream, int offset, int *length); + +// parse given data of specifc type +// params: +// - data - data to parse +// - length - length of data +// returns: +// ParsedData struct including the parsed data +typedef ParsedData parse_function_t(byte_t *data, size_t length); + +// struct for holding the find and parse function for every result type +#ifndef FIND_PARSE_FUNCTIONS_DEFINED +#define FIND_PARSE_FUNCTIONS_DEFINED +typedef struct { + find_function_t *find_fn; + parse_function_t *parse_fn; +} FindParseFunctions; +#endif -- cgit v1.2.3 From 2a45b1096325ac6743787b6f60fef4800223b2a6 Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 13 Jul 2026 19:39:06 +0200 Subject: feat: added basic parsing of fight results data --- analyzer/analyzer.c | 32 +++++++++- analyzer/parser/fight_results.c | 130 ++++++++++++++++++++++++++++++++++++++-- analyzer/parser/fight_results.h | 76 +++++++++++++++++++++++ analyzer/parser/parser_defs.h | 5 ++ 4 files changed, 237 insertions(+), 6 deletions(-) (limited to 'analyzer/parser') diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index 5997c6d..03ecd2a 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -1,6 +1,7 @@ #include "analyzer.h" #include +#include #include "parser/parser.h" #include "utils/tcp.h" @@ -35,12 +36,41 @@ void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle, void (*parsed_data_ha } } +// prints fight resutls +void print_fight_results(FightResults results) { + printf("Enemies:\n\t"); + for (size_t i = 0; i < results.num_enemy; i++) { + EnemyResults enemy = results.enemy_results[i]; + printf("%s (%d)%s", enemy.name, enemy.level, i < results.num_enemy - 1 ? ", " : "\n"); + } + printf("Player rewards:\n"); + for (size_t i = 0; i < results.num_friendly; i++) { + FriendlyResults friendly = results.friendly_results[i]; + printf("\t%s (%d):\n", friendly.name, friendly.level); + printf("\t\texp: %d\n", friendly.exp); + printf("\t\tgold: %d\n", friendly.gold); + if (friendly.psycho > 0) + printf("\t\tpsycho: %d\n", friendly.psycho); + if (strlen(friendly.items) > 0) + printf("\t\titems: %s\n", friendly.items); + if (strlen(friendly.gear) > 0) + printf("\t\tgear: %s\n", friendly.gear); + if (strlen(friendly.drifs) > 0) + printf("\t\tdrifs: %s\n", friendly.drifs); + if (friendly.splinters > 0) + printf("\t\tsplinters: %d\n", friendly.splinters); + if (friendly.saturation > 0) + printf("\t\tsaturation: %d (%s)\n", friendly.saturation, friendly.saturation_type); + } + printf("\n"); +} + // file analyzer parsed data handler void file_parsed_data_handler(ParsedData *parsed_data, size_t n_parsed_data) { for (size_t i = 0; i < n_parsed_data; i++) { switch (parsed_data[i].data_type) { case TYPE_FIGHT_RESULTS: - printf("Fight results\n"); + print_fight_results(*(FightResults *)parsed_data[i].data); break; } } diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index 8275af2..4fbb0f8 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -25,15 +25,135 @@ byte_t *find_fight_results(TCPStream *stream, int offset, int *length) { return start; } +void destroy_entity_results(EntityResults results) { + free(results.name); + free(results.field7); + free(results.gear); + free(results.items); + free(results.drifs); + free(results.field29); + free(results.field34); + free(results.saturation); +} + +// 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); + + for (size_t i = 0; i < ENTITY_FIELD_COUNT; i++) { + // get string buffer of given result + byte_t *result_separator = memmem(data, length, SEPARATOR_RESULT, strlen(SEPARATOR_RESULT)); + size_t len = result_separator ? result_separator - data : length; + char *str = malloc(len + 1); + memcpy(str, data, len); + str[len] = '\0'; + + // parse depending on variable type + switch (field_types[i]) { + case INT: + sscanf(str, "%d", (int *)field_pointers[i]); + free(str); + break; + case FLOAT: + sscanf(str, "%f", (float *)field_pointers[i]); + free(str); + break; + case DOUBLE: + sscanf(str, "%lf", (double *)field_pointers[i]); + free(str); + break; + case STRING: + *(char **)field_pointers[i] = str; + break; + } + + // update data pointer and length + if (result_separator) { + length -= result_separator - data + strlen(SEPARATOR_RESULT); + data = result_separator + strlen(SEPARATOR_RESULT); + } + } + + return results; +} + +// 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; + // TODO: parse that to more readable format + results.gear = strdup(entity_results.gear); + // TODO: parse that to more readable format, determine types from ids + results.items = strdup(entity_results.items); + results.level = entity_results.level; + results.psycho = entity_results.psycho; + // TODO: parse that to more readable format + results.drifs = strdup(entity_results.drifs); + results.splinters = entity_results.splinters; + + results.saturation = 0; + if (strlen(entity_results.saturation) > 0) { + int saturation_id; + sscanf(entity_results.saturation, "%d,%d", &saturation_id, &results.saturation); + // TODO: determine type + results.saturation_type = "typ"; + } + + 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; - // TODO: actually parse that data - printf("len=%zu\n%.*s\n----------------\n", length, (int)length, data); + // 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 = NULL; - parsed_data.data_cleanup = NULL; - // parsed_data.data_cleanup = cleanup_fight_results; + parsed_data.data = results; + parsed_data.data_cleanup = cleanup_fight_results; return parsed_data; } diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index d8064ca..2712638 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -16,6 +16,82 @@ #define SEPARATOR_ENTITY "[--]" #define SEPARATOR_RESULT "&" +#define ENTITY_FIELD_COUNT 35 +#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 \ +} + #ifndef ENTITY_RESULTS_DEFINED #define ENTITY_RESULTS_DEFINED typedef struct { diff --git a/analyzer/parser/parser_defs.h b/analyzer/parser/parser_defs.h index f99c2f3..b76b7e6 100644 --- a/analyzer/parser/parser_defs.h +++ b/analyzer/parser/parser_defs.h @@ -3,6 +3,11 @@ #include "../utils/tcp.h" +#ifndef TYPE_ENUM_DEFINED +#define TYPE_ENUM_DEFINED +typedef enum { INT, FLOAT, DOUBLE, STRING } TypeEnum; +#endif + #ifndef RESULTS_ENUM_DEFINED #define RESULTS_ENUM_DEFINED typedef enum { TYPE_FIGHT_RESULTS } ResultsEnum; -- cgit v1.2.3 From df568228d1a125f909cb25790255c0719f8ec7b1 Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 13 Jul 2026 19:43:05 +0200 Subject: refactor: fixed some whitespace indent --- analyzer/parser/fight_results.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'analyzer/parser') diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index 2712638..4e1ea60 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -56,15 +56,15 @@ } #define ENTITY_FIELD_ADDRESSES(entity_results) {\ &entity_results.team, \ - &entity_results.name, \ + &entity_results.name, \ &entity_results.exp, \ &entity_results.field4, \ &entity_results.gold, \ &entity_results.field6, \ - &entity_results.field7, \ - &entity_results.gear, \ + &entity_results.field7, \ + &entity_results.gear, \ &entity_results.field9, \ - &entity_results.items, \ + &entity_results.items, \ &entity_results.field11, \ &entity_results.field12, \ &entity_results.field13, \ @@ -80,16 +80,16 @@ &entity_results.field23, \ &entity_results.field24, \ &entity_results.psycho, \ - &entity_results.drifs, \ + &entity_results.drifs, \ &entity_results.splinters, \ &entity_results.field28, \ - &entity_results.field29, \ + &entity_results.field29, \ &entity_results.field30, \ &entity_results.field31, \ &entity_results.field32, \ &entity_results.field33, \ - &entity_results.field34, \ - &entity_results.saturation \ + &entity_results.field34, \ + &entity_results.saturation \ } #ifndef ENTITY_RESULTS_DEFINED -- cgit v1.2.3 From 106e1f7a579a66c46cb68356f585917fe7883310 Mon Sep 17 00:00:00 2001 From: Mosder Date: Tue, 14 Jul 2026 11:08:32 +0200 Subject: refactor: generalized Makefile by adding CC and CFLAGS variables. Changed from realtive to absolute paths for includes --- Makefile | 31 ++++----- analyzer/parser/fight_results.c | 2 +- analyzer/parser/fight_results.h | 4 +- analyzer/parser/find_parse_functions.h | 2 +- analyzer/parser/parser.c | 4 +- analyzer/parser/parser.h | 6 +- analyzer/parser/parser_defs.h | 2 +- analyzer/structures/hashmap.c | 112 +++++++++++++++++++++++++++++++++ analyzer/structures/hashmap.h | 81 ++++++++++++++++++++++++ analyzer/structures/heap.c | 109 ++++++++++++++++++++++++++++++++ analyzer/structures/heap.h | 58 +++++++++++++++++ analyzer/utils/fs.c | 2 +- analyzer/utils/structures/hashmap.c | 108 ------------------------------- analyzer/utils/structures/hashmap.h | 77 ----------------------- analyzer/utils/structures/heap.c | 109 -------------------------------- analyzer/utils/structures/heap.h | 58 ----------------- analyzer/utils/tcp.c | 4 +- analyzer/utils/tcp_seq_math.c | 2 +- analyzer/utils/time.c | 2 +- 19 files changed, 392 insertions(+), 381 deletions(-) create mode 100644 analyzer/structures/hashmap.c create mode 100644 analyzer/structures/hashmap.h create mode 100644 analyzer/structures/heap.c create mode 100644 analyzer/structures/heap.h delete mode 100644 analyzer/utils/structures/hashmap.c delete mode 100644 analyzer/utils/structures/hashmap.h delete mode 100644 analyzer/utils/structures/heap.c delete mode 100644 analyzer/utils/structures/heap.h (limited to 'analyzer/parser') diff --git a/Makefile b/Makefile index e5a9c54..2455ece 100644 --- a/Makefile +++ b/Makefile @@ -1,31 +1,34 @@ +CC=gcc +CFLAGS=-iquote ./analyzer + global: compile sudo mv branal /usr/bin local: compile mv branal ${HOME}/.local/bin -compile: utils capture analyzer parser - gcc *.o main.c -lpcap -o branal +compile: structures utils capture analyzer parser + $(CC) $(CFLAGS) -lpcap *.o main.c -o branal rm *.o -utils: structures - gcc -c analyzer/utils/fs.c - gcc -c analyzer/utils/time.c - gcc -c analyzer/utils/tcp.c - gcc -c analyzer/utils/tcp_seq_math.c - structures: - gcc -c analyzer/utils/structures/hashmap.c - gcc -c analyzer/utils/structures/heap.c + $(CC) $(CFLAGS) -c analyzer/structures/hashmap.c + $(CC) $(CFLAGS) -c analyzer/structures/heap.c + +utils: + $(CC) $(CFLAGS) -c analyzer/utils/fs.c + $(CC) $(CFLAGS) -c analyzer/utils/time.c + $(CC) $(CFLAGS) -c analyzer/utils/tcp.c + $(CC) $(CFLAGS) -c analyzer/utils/tcp_seq_math.c capture: - gcc -c analyzer/capture.c + $(CC) $(CFLAGS) -c analyzer/capture.c analyzer: - gcc -c analyzer/analyzer.c + $(CC) $(CFLAGS) -c analyzer/analyzer.c parser: - gcc -c analyzer/parser/parser.c - gcc -c analyzer/parser/fight_results.c + $(CC) $(CFLAGS) -c analyzer/parser/parser.c + $(CC) $(CFLAGS) -c analyzer/parser/fight_results.c .PHONY: analyzer diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index 4fbb0f8..65e72ea 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -1,4 +1,4 @@ -#include "fight_results.h" +#include "parser/fight_results.h" #include #include diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index 4e1ea60..de7f525 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -1,8 +1,8 @@ // parser/fight_results.h // utilities for parsing the fight results data -#include "../utils/tcp.h" -#include "parser_defs.h" +#include "parser/parser_defs.h" +#include "utils/tcp.h" // Payload structure: // FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END diff --git a/analyzer/parser/find_parse_functions.h b/analyzer/parser/find_parse_functions.h index 4e53274..dd7579e 100644 --- a/analyzer/parser/find_parse_functions.h +++ b/analyzer/parser/find_parse_functions.h @@ -1,7 +1,7 @@ // parser/find_parse_functions.h // array of find and parse functions for every result type -#include "fight_results.h" +#include "parser/fight_results.h" #define RESULT_TYPE_COUNT 1 diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c index 246635c..3ef48b5 100644 --- a/analyzer/parser/parser.c +++ b/analyzer/parser/parser.c @@ -1,8 +1,8 @@ -#include "parser.h" +#include "parser/parser.h" #include -#include "find_parse_functions.h" +#include "parser/find_parse_functions.h" // expand parsed data exponentially to fit el_count elements ParsedData *expand_parsed_data(ParsedData *parsed_data, size_t *capacity, size_t el_count) { diff --git a/analyzer/parser/parser.h b/analyzer/parser/parser.h index e2d5c5d..4b28aa7 100644 --- a/analyzer/parser/parser.h +++ b/analyzer/parser/parser.h @@ -1,9 +1,9 @@ // parser/parser.h // main parser file -#include "../utils/tcp.h" -#include "fight_results.h" -#include "parser_defs.h" +#include "parser/fight_results.h" +#include "parser/parser_defs.h" +#include "utils/tcp.h" #define INIT_PARSED_DATA_CAPACITY 4 diff --git a/analyzer/parser/parser_defs.h b/analyzer/parser/parser_defs.h index b76b7e6..6af6059 100644 --- a/analyzer/parser/parser_defs.h +++ b/analyzer/parser/parser_defs.h @@ -1,7 +1,7 @@ // parser/parser_defs.h // useful definitions for parser files -#include "../utils/tcp.h" +#include "utils/tcp.h" #ifndef TYPE_ENUM_DEFINED #define TYPE_ENUM_DEFINED diff --git a/analyzer/structures/hashmap.c b/analyzer/structures/hashmap.c new file mode 100644 index 0000000..ed2eb4b --- /dev/null +++ b/analyzer/structures/hashmap.c @@ -0,0 +1,112 @@ +#include "structures/hashmap.h" + +#include +#include + +HashMap *hashmap_new( + size_t key_size, + size_t value_size, + size_t (*hash)(void *key), + int (*compare)(void *key1, void *key2), + void (*key_cleanup)(void *key), + void (*value_cleanup)(void *key) +) { + HashMap *map = malloc(sizeof(HashMap)); + // initialize NULLs + memset(map->table, 0, sizeof(HashMapNode *) * HASHMAP_TABLE_SIZE); + map->key_size = key_size; + map->value_size = value_size; + map->hash = hash; + map->compare = compare; + map->key_cleanup = key_cleanup; + map->value_cleanup = value_cleanup; + return map; +} + +void hashmap_put(HashMap *map, void *key, void *value) { + size_t key_hash = map->hash(key); + HashMapNode *node = map->table[key_hash]; + + // check if it exists + while (node) { + // if it exists - update node + if (map->compare(key, node->key)) { + // ensure no hanging memory allocation + if (map->value_cleanup) { + map->value_cleanup(node); + } + memcpy(node->value, value, map->value_size); + return; + } + node = node->next; + } + + // if it doesn't exist - create new node + HashMapNode *new_node = malloc(sizeof(HashMapNode)); + new_node->key = malloc(map->key_size); + new_node->value = malloc(map->value_size); + memcpy(new_node->key, key, map->key_size); + memcpy(new_node->value, value, map->value_size); + + // put in front of the linked list + new_node->next = map->table[key_hash]; + map->table[key_hash] = new_node; +} + +void *hashmap_get(HashMap *map, void *key) { + size_t key_hash = map->hash(key); + HashMapNode *node = map->table[key_hash]; + + // check if it exists + while (node) { + // if it exists - return the value pointer + if (map->compare(key, node->key)) { + return node->value; + } + node = node->next; + } + + // if it doesn't exist - return NULL + return NULL; +} + +void free_node_without_next(HashMap *map, HashMapNode *node) { + if (map->key_cleanup) { + map->key_cleanup(node); + } + if (map->value_cleanup) { + map->value_cleanup(node); + } + free(node->key); + free(node->value); + free(node); +} + +void hashmap_delete(HashMap *map, void *key) { + size_t key_hash = map->hash(key); + HashMapNode **p_node = &map->table[key_hash]; + + // check if it exists + while (*p_node) { + // if it exists - delete it + if (map->compare(key, (*p_node)->key)) { + HashMapNode *next = (*p_node)->next; + free_node_without_next(map, *p_node); + *p_node = next; + return; + } + p_node = &(*p_node)->next; + } +} + +void hashmap_free(HashMap *map) { + for (size_t i = 0; i < HASHMAP_TABLE_SIZE; i++) { + HashMapNode *curr = map->table[i]; + while (curr) { + HashMapNode *next = curr->next; + free_node_without_next(map, curr); + curr = next; + } + } + free(map); +} diff --git a/analyzer/structures/hashmap.h b/analyzer/structures/hashmap.h new file mode 100644 index 0000000..528f24a --- /dev/null +++ b/analyzer/structures/hashmap.h @@ -0,0 +1,81 @@ +// structures/hashmap.h +// a simple implementation of hashmap using linked list + +#include + +#define HASHMAP_TABLE_SIZE 128 + +typedef struct HashMapNode { + void *key; + void *value; + struct HashMapNode *next; +} HashMapNode; + +typedef struct { + HashMapNode *table[HASHMAP_TABLE_SIZE]; + size_t key_size; + size_t value_size; + size_t (*hash)(void *key); + int (*compare)(void *key1, void *key2); + void (*key_cleanup)(void *key); + void (*value_cleanup)(void *value); +} HashMap; + +// create a new hash map +// params: +// - key_size - size of the key type +// - value_size - size of the value type +// - hash - function for hashing keys +// params: +// - key - pointer to the key to hash +// returns: +// hash of the key (size_t in [0; HASHMAP_TABLE_SIZE)) +// - compare - function for comparing keys +// params: +// - key1 - pointer to the first of the keys to compare +// - key2 - pointer to the second of the keys to compare +// returns: +// truthy if the keys are identical, falsy if they are different +// - key_cleanup - function for cleaning up any internal allocations inside node->key (not the key itself) +// if it has no heap allocations, this should be NULL +// params: +// - key - pointer to the key to cleanup +// - value_cleanup - function for cleaning up any internal allocations inside node->value (not the value itself) +// if it has no heap allocations, this should be NULL +// params: +// - value - pointer to the value to cleanup +// returns: +// pointer to the new hash map +extern HashMap *hashmap_new( + size_t key_size, + size_t value_size, + size_t (*hash)(void *key), + int (*compare)(void *key1, void *key2), + void (*key_cleanup)(void *key), + void (*value_cleanup)(void *value) +); + +// create/update an entry in a hash map +// params: +// - map - hash map to insert to +// - key - pointer to the key +// - value - pointer to the value +extern void hashmap_put(HashMap *map, void *key, void *value); + +// get the value of given key from a hash map +// params: +// - map - hash map to get from +// - key - pointer to the key of the value to get +// returns: +// pointer to the value of specified key or NULL if it doesn't exist +extern void *hashmap_get(HashMap *map, void *key); + +// delete an entry from a hash map +// params: +// - map - hash map to delete from +// - key - pointer to the key of the entry to delete +extern void hashmap_delete(HashMap *map, void *key); + +// free the hash map +// - map - hash map to free +extern void hashmap_free(HashMap *map); diff --git a/analyzer/structures/heap.c b/analyzer/structures/heap.c new file mode 100644 index 0000000..690aac1 --- /dev/null +++ b/analyzer/structures/heap.c @@ -0,0 +1,109 @@ +#include "structures/heap.h" + +#include +#include + +Heap *heap_new(size_t element_size, int (*compare)(void *element1, void *element2), void (*element_cleanup)(void *element)) { + Heap *heap = malloc(sizeof(Heap)); + heap->data = malloc(INIT_HEAP_CAPACITY * sizeof(void *)); + heap->n_elements = 0; + heap->element_size = element_size; + heap->capacity = INIT_HEAP_CAPACITY; + heap->compare = compare; + heap->element_cleanup = element_cleanup; + return heap; +} + +// return index of parent in heap +size_t parent(size_t i) { + return (i - 1) / 2; +} + +// return index of left child in heap +size_t left(size_t i) { + return 2 * i + 1; +} + +// return index of right child in heap +size_t right(size_t i) { + return 2 * i + 2; +} + +// swap two elements +void swap(void **p_el1, void **p_el2) { + void *tmp = *p_el1; + *p_el1 = *p_el2; + *p_el2 = tmp; +} + +// expand heap exponentially to fit el_count elements +void expand_heap(Heap *heap, size_t el_count) { + while (heap->capacity < el_count) + heap->capacity = (heap->capacity << 1) | 1; + heap->data = realloc(heap->data, heap->capacity * sizeof(void *)); +} + +void heap_insert(Heap *heap, void *element) { + // expand heap if capacity is too small + if (heap->n_elements >= heap->capacity) + expand_heap(heap, heap->n_elements + 1); + + // insert into the last position + size_t i = heap->n_elements; + heap->data[i] = malloc(heap->element_size); + memcpy(heap->data[i], element, heap->element_size); + heap->n_elements++; + + // heapify up + while (i > 0 && heap->compare(heap->data[i], heap->data[parent(i)])) { + swap(&heap->data[i], &heap->data[parent(i)]); + i = parent(i); + } +} + +void *heap_top_get(Heap *heap) { + return heap->n_elements <= 0 ? NULL : heap->data[0]; +} + +void free_heap_element(Heap *heap, size_t index) { + if (heap->element_cleanup) + heap->element_cleanup(heap->data[index]); + free(heap->data[index]); +} + +void heap_top_remove(Heap *heap) { + if (heap->n_elements <= 0) + return; + + free_heap_element(heap, 0); + + // move last element to root + heap->data[0] = heap->data[--heap->n_elements]; + + // heapify down + size_t i = 0; + int changed; + do { + size_t l = left(i); + size_t r = right(i); + size_t to_be_parent = i; + + if (l < heap->n_elements && heap->compare(heap->data[l], heap->data[to_be_parent])) + to_be_parent = l; + if (r < heap->n_elements && heap->compare(heap->data[r], heap->data[to_be_parent])) + to_be_parent = r; + + if ((changed = to_be_parent != i)) { + swap(&heap->data[to_be_parent], &heap->data[i]); + i = to_be_parent; + } + } while (changed); +} + +void heap_free(Heap *heap) { + for (size_t i = 0; i < heap->n_elements; i++) { + free_heap_element(heap, i); + } + free(heap->data); + free(heap); +} diff --git a/analyzer/structures/heap.h b/analyzer/structures/heap.h new file mode 100644 index 0000000..8a23164 --- /dev/null +++ b/analyzer/structures/heap.h @@ -0,0 +1,58 @@ +// structures/heap.h +// a simple implementation of heap + +#include + +#define INIT_HEAP_DEPTH 7 +#define INIT_HEAP_CAPACITY (1 << INIT_HEAP_DEPTH) - 1 + +#ifndef HEAP_DEFINED +#define HEAP_DEFINED +typedef struct { + void **data; + size_t n_elements; + size_t element_size; + size_t capacity; + int (*compare)(void *element1, void *element2); + void (*element_cleanup)(void *element); +} Heap; +#endif + +// create a new heap +// params: +// - element_size - size of the heap element +// - compare - function for comparing elements +// params: +// - element1 - pointer to the first of the elements to compare +// - element2 - pointer to the second of the elements to compare +// returns: +// truthy if element1 should be element2's parent (e.g. element1 > element2 for max heap), falsy otherwise +// - element_cleanup - function for cleaning up any internal allocations inside element (not the element itself) +// if it has no heap allocations, this should be NULL +// params: +// - element - pointer to the element to cleanup +// returns: +// pointer to the new heap +extern Heap *heap_new(size_t element_size, int (*compare)(void *element1, void *element2), void (*element_cleanup)(void *element)); + +// insert new element into the heap +// params: +// - heap - heap to insert to +// - element - pointer to the element to insert +extern void heap_insert(Heap *heap, void *element); + +// get the top element from the heap +// params: +// - heap - heap to get the top element from +// returns: +// pointer to the top element in the heap or NULL if heap is empty +extern void *heap_top_get(Heap *heap); + +// remove the top element from the heap +// params: +// - heap - heap to remove the top element from +extern void heap_top_remove(Heap *heap); + +// free the heap +// - heap - heap to free +extern void heap_free(Heap *heap); diff --git a/analyzer/utils/fs.c b/analyzer/utils/fs.c index 2dd8191..5e3eb1c 100644 --- a/analyzer/utils/fs.c +++ b/analyzer/utils/fs.c @@ -1,4 +1,4 @@ -#include "fs.h" +#include "utils/fs.h" #include #include diff --git a/analyzer/utils/structures/hashmap.c b/analyzer/utils/structures/hashmap.c deleted file mode 100644 index 5c7fdf9..0000000 --- a/analyzer/utils/structures/hashmap.c +++ /dev/null @@ -1,108 +0,0 @@ -#include "hashmap.h" - -#include -#include - -HashMap *hashmap_new( - size_t key_size, size_t value_size, size_t (*hash)(void *key), int (*compare)(void *key1, void *key2), void (*key_cleanup)(void *key), - void (*value_cleanup)(void *key) -) { - HashMap *map = malloc(sizeof(HashMap)); - // initialize NULLs - memset(map->table, 0, sizeof(HashMapNode *) * HASHMAP_TABLE_SIZE); - map->key_size = key_size; - map->value_size = value_size; - map->hash = hash; - map->compare = compare; - map->key_cleanup = key_cleanup; - map->value_cleanup = value_cleanup; - return map; -} - -void hashmap_put(HashMap *map, void *key, void *value) { - size_t key_hash = map->hash(key); - HashMapNode *node = map->table[key_hash]; - - // check if it exists - while (node) { - // if it exists - update node - if (map->compare(key, node->key)) { - // ensure no hanging memory allocation - if (map->value_cleanup) { - map->value_cleanup(node); - } - memcpy(node->value, value, map->value_size); - return; - } - node = node->next; - } - - // if it doesn't exist - create new node - HashMapNode *new_node = malloc(sizeof(HashMapNode)); - new_node->key = malloc(map->key_size); - new_node->value = malloc(map->value_size); - memcpy(new_node->key, key, map->key_size); - memcpy(new_node->value, value, map->value_size); - - // put in front of the linked list - new_node->next = map->table[key_hash]; - map->table[key_hash] = new_node; -} - -void *hashmap_get(HashMap *map, void *key) { - size_t key_hash = map->hash(key); - HashMapNode *node = map->table[key_hash]; - - // check if it exists - while (node) { - // if it exists - return the value pointer - if (map->compare(key, node->key)) { - return node->value; - } - node = node->next; - } - - // if it doesn't exist - return NULL - return NULL; -} - -void free_node_without_next(HashMap *map, HashMapNode *node) { - if (map->key_cleanup) { - map->key_cleanup(node); - } - if (map->value_cleanup) { - map->value_cleanup(node); - } - free(node->key); - free(node->value); - free(node); -} - -void hashmap_delete(HashMap *map, void *key) { - size_t key_hash = map->hash(key); - HashMapNode **p_node = &map->table[key_hash]; - - // check if it exists - while (*p_node) { - // if it exists - delete it - if (map->compare(key, (*p_node)->key)) { - HashMapNode *next = (*p_node)->next; - free_node_without_next(map, *p_node); - *p_node = next; - return; - } - p_node = &(*p_node)->next; - } -} - -void hashmap_free(HashMap *map) { - for (size_t i = 0; i < HASHMAP_TABLE_SIZE; i++) { - HashMapNode *curr = map->table[i]; - while (curr) { - HashMapNode *next = curr->next; - free_node_without_next(map, curr); - curr = next; - } - } - free(map); -} diff --git a/analyzer/utils/structures/hashmap.h b/analyzer/utils/structures/hashmap.h deleted file mode 100644 index 4f8e24a..0000000 --- a/analyzer/utils/structures/hashmap.h +++ /dev/null @@ -1,77 +0,0 @@ -// utils/structures/hashmap.h -// a simple implementation of hashmap using linked list - -#include - -#define HASHMAP_TABLE_SIZE 128 - -typedef struct HashMapNode { - void *key; - void *value; - struct HashMapNode *next; -} HashMapNode; - -typedef struct { - HashMapNode *table[HASHMAP_TABLE_SIZE]; - size_t key_size; - size_t value_size; - size_t (*hash)(void *key); - int (*compare)(void *key1, void *key2); - void (*key_cleanup)(void *key); - void (*value_cleanup)(void *value); -} HashMap; - -// create a new hash map -// params: -// - key_size - size of the key type -// - value_size - size of the value type -// - hash - function for hashing keys -// params: -// - key - pointer to the key to hash -// returns: -// hash of the key (size_t in [0; HASHMAP_TABLE_SIZE)) -// - compare - function for comparing keys -// params: -// - key1 - pointer to the first of the keys to compare -// - key2 - pointer to the second of the keys to compare -// returns: -// truthy if the keys are identical, falsy if they are different -// - key_cleanup - function for cleaning up any internal allocations inside node->key (not the key itself) -// if it has no heap allocations, this should be NULL -// params: -// - key - pointer to the key to cleanup -// - value_cleanup - function for cleaning up any internal allocations inside node->value (not the value itself) -// if it has no heap allocations, this should be NULL -// params: -// - value - pointer to the value to cleanup -// returns: -// pointer to the new hash map -extern HashMap *hashmap_new( - size_t key_size, size_t value_size, size_t (*hash)(void *key), int (*compare)(void *key1, void *key2), void (*key_cleanup)(void *key), - void (*value_cleanup)(void *value) -); - -// create/update an entry in a hash map -// params: -// - map - hash map to insert to -// - key - pointer to the key -// - value - pointer to the value -extern void hashmap_put(HashMap *map, void *key, void *value); - -// get the value of given key from a hash map -// params: -// - map - hash map to get from -// - key - pointer to the key of the value to get -// returns: -// pointer to the value of specified key or NULL if it doesn't exist -extern void *hashmap_get(HashMap *map, void *key); - -// delete an entry from a hash map -// params: -// - map - hash map to delete from -// - key - pointer to the key of the entry to delete -extern void hashmap_delete(HashMap *map, void *key); - -// free the hash map -// - map - hash map to free -extern void hashmap_free(HashMap *map); diff --git a/analyzer/utils/structures/heap.c b/analyzer/utils/structures/heap.c deleted file mode 100644 index 5310299..0000000 --- a/analyzer/utils/structures/heap.c +++ /dev/null @@ -1,109 +0,0 @@ -#include "heap.h" - -#include -#include - -Heap *heap_new(size_t element_size, int (*compare)(void *element1, void *element2), void (*element_cleanup)(void *element)) { - Heap *heap = malloc(sizeof(Heap)); - heap->data = malloc(INIT_HEAP_CAPACITY * sizeof(void *)); - heap->n_elements = 0; - heap->element_size = element_size; - heap->capacity = INIT_HEAP_CAPACITY; - heap->compare = compare; - heap->element_cleanup = element_cleanup; - return heap; -} - -// return index of parent in heap -size_t parent(size_t i) { - return (i - 1) / 2; -} - -// return index of left child in heap -size_t left(size_t i) { - return 2 * i + 1; -} - -// return index of right child in heap -size_t right(size_t i) { - return 2 * i + 2; -} - -// swap two elements -void swap(void **p_el1, void **p_el2) { - void *tmp = *p_el1; - *p_el1 = *p_el2; - *p_el2 = tmp; -} - -// expand heap exponentially to fit el_count elements -void expand_heap(Heap *heap, size_t el_count) { - while (heap->capacity < el_count) - heap->capacity = (heap->capacity << 1) | 1; - heap->data = realloc(heap->data, heap->capacity * sizeof(void *)); -} - -void heap_insert(Heap *heap, void *element) { - // expand heap if capacity is too small - if (heap->n_elements >= heap->capacity) - expand_heap(heap, heap->n_elements + 1); - - // insert into the last position - size_t i = heap->n_elements; - heap->data[i] = malloc(heap->element_size); - memcpy(heap->data[i], element, heap->element_size); - heap->n_elements++; - - // heapify up - while (i > 0 && heap->compare(heap->data[i], heap->data[parent(i)])) { - swap(&heap->data[i], &heap->data[parent(i)]); - i = parent(i); - } -} - -void *heap_top_get(Heap *heap) { - return heap->n_elements <= 0 ? NULL : heap->data[0]; -} - -void free_heap_element(Heap *heap, size_t index) { - if (heap->element_cleanup) - heap->element_cleanup(heap->data[index]); - free(heap->data[index]); -} - -void heap_top_remove(Heap *heap) { - if (heap->n_elements <= 0) - return; - - free_heap_element(heap, 0); - - // move last element to root - heap->data[0] = heap->data[--heap->n_elements]; - - // heapify down - size_t i = 0; - int changed; - do { - size_t l = left(i); - size_t r = right(i); - size_t to_be_parent = i; - - if (l < heap->n_elements && heap->compare(heap->data[l], heap->data[to_be_parent])) - to_be_parent = l; - if (r < heap->n_elements && heap->compare(heap->data[r], heap->data[to_be_parent])) - to_be_parent = r; - - if ((changed = to_be_parent != i)) { - swap(&heap->data[to_be_parent], &heap->data[i]); - i = to_be_parent; - } - } while (changed); -} - -void heap_free(Heap *heap) { - for (size_t i = 0; i < heap->n_elements; i++) { - free_heap_element(heap, i); - } - free(heap->data); - free(heap); -} diff --git a/analyzer/utils/structures/heap.h b/analyzer/utils/structures/heap.h deleted file mode 100644 index eb3430b..0000000 --- a/analyzer/utils/structures/heap.h +++ /dev/null @@ -1,58 +0,0 @@ -// utils/structures/heap.h -// a simple implementation of heap - -#include - -#define INIT_HEAP_DEPTH 7 -#define INIT_HEAP_CAPACITY (1 << INIT_HEAP_DEPTH) - 1 - -#ifndef HEAP_DEFINED -#define HEAP_DEFINED -typedef struct { - void **data; - size_t n_elements; - size_t element_size; - size_t capacity; - int (*compare)(void *element1, void *element2); - void (*element_cleanup)(void *element); -} Heap; -#endif - -// create a new heap -// params: -// - element_size - size of the heap element -// - compare - function for comparing elements -// params: -// - element1 - pointer to the first of the elements to compare -// - element2 - pointer to the second of the elements to compare -// returns: -// truthy if element1 should be element2's parent (e.g. element1 > element2 for max heap), falsy otherwise -// - element_cleanup - function for cleaning up any internal allocations inside element (not the element itself) -// if it has no heap allocations, this should be NULL -// params: -// - element - pointer to the element to cleanup -// returns: -// pointer to the new heap -extern Heap *heap_new(size_t element_size, int (*compare)(void *element1, void *element2), void (*element_cleanup)(void *element)); - -// insert new element into the heap -// params: -// - heap - heap to insert to -// - element - pointer to the element to insert -extern void heap_insert(Heap *heap, void *element); - -// get the top element from the heap -// params: -// - heap - heap to get the top element from -// returns: -// pointer to the top element in the heap or NULL if heap is empty -extern void *heap_top_get(Heap *heap); - -// remove the top element from the heap -// params: -// - heap - heap to remove the top element from -extern void heap_top_remove(Heap *heap); - -// free the heap -// - heap - heap to free -extern void heap_free(Heap *heap); diff --git a/analyzer/utils/tcp.c b/analyzer/utils/tcp.c index 56df842..ab5303e 100644 --- a/analyzer/utils/tcp.c +++ b/analyzer/utils/tcp.c @@ -1,4 +1,4 @@ -#include "tcp.h" +#include "utils/tcp.h" #include #include @@ -6,7 +6,7 @@ #include #include -#include "tcp_seq_math.h" +#include "utils/tcp_seq_math.h" // initalize default TCP segment TCPSegment new_segment() { diff --git a/analyzer/utils/tcp_seq_math.c b/analyzer/utils/tcp_seq_math.c index 272cb5a..2cc6e3f 100644 --- a/analyzer/utils/tcp_seq_math.c +++ b/analyzer/utils/tcp_seq_math.c @@ -1,4 +1,4 @@ -#include "tcp_seq_math.h" +#include "utils/tcp_seq_math.h" int seq_after(uint32_t a, uint32_t b) { return (int32_t)(a - b) > 0; diff --git a/analyzer/utils/time.c b/analyzer/utils/time.c index 72c0bc7..d95da65 100644 --- a/analyzer/utils/time.c +++ b/analyzer/utils/time.c @@ -1,4 +1,4 @@ -#include "time.h" +#include "utils/time.h" #include #include -- cgit v1.2.3 From 189fd56b24859a89d71899e070f885e93ffabd97 Mon Sep 17 00:00:00 2001 From: Mosder Date: Tue, 14 Jul 2026 17:25:04 +0200 Subject: feat: created converter from CSV files to a C header files containing static arrays. Used for creating ID to STR conversion for saturations and items. --- .gitignore | 1 + Makefile | 8 ++++- analyzer/parser/fight_results.c | 14 ++++++-- csv/items.csv | 4 +++ csv/saturations.csv | 8 +++++ csv_to_headers.c | 79 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 csv/items.csv create mode 100644 csv/saturations.csv create mode 100644 csv_to_headers.c (limited to 'analyzer/parser') diff --git a/.gitignore b/.gitignore index dc2bf7f..802b083 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ compile_commands.json sample_data/ analyzer/headers/server.h +analyzer/csv/ diff --git a/Makefile b/Makefile index e9cec60..ff12d96 100644 --- a/Makefile +++ b/Makefile @@ -7,10 +7,15 @@ global: compile local: compile mv branal ${HOME}/.local/bin -compile: structures utils parser capture analyzer +compile: csv structures utils parser capture analyzer $(CC) $(CFLAGS) -lpcap *.o main.c -o branal rm *.o +csv: + gcc csv_to_headers.c -o csv_to_headers + ./csv_to_headers + rm csv_to_headers + structures: $(CC) $(CFLAGS) -c analyzer/structures/hashmap.c $(CC) $(CFLAGS) -c analyzer/structures/heap.c @@ -32,3 +37,4 @@ analyzer: $(CC) $(CFLAGS) -c analyzer/analyzer.c .PHONY: analyzer +.PHONY: csv diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index 65e72ea..b3227e2 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -4,6 +4,9 @@ #include #include +#include "csv/id_to_item_name.h" +#include "csv/id_to_saturation_type.h" + // function to cleanup fight results to be used in ParsedData struct void cleanup_fight_results(void *data) { FightResults *fight_results = data; @@ -79,6 +82,14 @@ EntityResults get_entity_results(byte_t *data, size_t length) { return results; } +char *get_item_name(int item_id) { + return item_id < id_to_item_name_len ? id_to_item_name[item_id] : "item_id out of bounds"; +} + +char *get_saturation_type(int saturation_id) { + return saturation_id < id_to_saturation_type_len ? id_to_saturation_type[saturation_id] : "saturation_id out of bounds"; +} + // parse EntityResults to FriendlyResults FriendlyResults parse_to_friendly(EntityResults entity_results) { FriendlyResults results; @@ -100,8 +111,7 @@ FriendlyResults parse_to_friendly(EntityResults entity_results) { if (strlen(entity_results.saturation) > 0) { int saturation_id; sscanf(entity_results.saturation, "%d,%d", &saturation_id, &results.saturation); - // TODO: determine type - results.saturation_type = "typ"; + results.saturation_type = get_saturation_type(saturation_id); } return results; diff --git a/csv/items.csv b/csv/items.csv new file mode 100644 index 0000000..904454d --- /dev/null +++ b/csv/items.csv @@ -0,0 +1,4 @@ +id,item_name +1222,Daimonit +1219,Trop trwogi +986,Kryształ czasu diff --git a/csv/saturations.csv b/csv/saturations.csv new file mode 100644 index 0000000..7179ab8 --- /dev/null +++ b/csv/saturations.csv @@ -0,0 +1,8 @@ +id,saturation_type +1,type1 +2,type2 +3,type3 +4,type4 +5,type5 +6,type6 +7,type7 diff --git a/csv_to_headers.c b/csv_to_headers.c new file mode 100644 index 0000000..5845daf --- /dev/null +++ b/csv_to_headers.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +#define EXT ".csv" +#define EXT_LEN strlen(".csv") +#define CSV_PATH "csv" +#define HEADER_PATH "analyzer/csv" + +#define BUFFER_SIZE 256 +#define MAX_ARR_SIZE 4096 + +void create_header(char *file_name) { + // open input file + char input_path[BUFFER_SIZE]; + sprintf(input_path, "%s/%s", CSV_PATH, file_name); + FILE *input_fp = fopen(input_path, "r"); + + // read field names + char from[BUFFER_SIZE], to[BUFFER_SIZE]; + fscanf(input_fp, "%[^,],%[^\n]", from, to); + + // read values from CSV + char from_to_array[MAX_ARR_SIZE][BUFFER_SIZE] = {""}; + size_t max_index = 0; + size_t val1; + char val2[BUFFER_SIZE]; + while (fscanf(input_fp, "%zu,%[^\n]", &val1, val2) == 2) { + strcpy(from_to_array[val1], val2); + if (val1 > max_index) + max_index = val1; + } + + // close the input file + fclose(input_fp); + + // create output file + char output_path[BUFFER_SIZE]; + sprintf(output_path, "%s/%s_to_%s.h", HEADER_PATH, from, to); + FILE *output_fp = fopen(output_path, "w"); + + // write comments and beginning of output array + fprintf(output_fp, "// %s\n", input_path); + fprintf(output_fp, "// array translating %s to %s - generated from csv file\n\n", from, to); + fprintf(output_fp, "char *%s_to_%s[] = {\n", from, to); + + // write values + for (size_t i = 0; i <= max_index; i++) { + if (strlen(from_to_array[i]) > 0) + fprintf(output_fp, " \"%s\",\n", from_to_array[i]); + else + fprintf(output_fp, " \"%zu\",\n", i); + } + + // write the end of array and len + fprintf(output_fp, "};\n"); + fprintf(output_fp, "int %s_to_%s_len = %zu;", from, to, max_index + 1); + + // close the output file + fclose(output_fp); +} + +int main() { + // make directory for csv header files + mkdir(HEADER_PATH, 0755); + + DIR *csv_dir = opendir(CSV_PATH); + struct dirent *entry; + while ((entry = readdir(csv_dir))) { + char *file_name = entry->d_name; + size_t file_name_len = strlen(file_name); + // create header for each *.csv file + if (file_name_len > EXT_LEN && strcmp(file_name + file_name_len - EXT_LEN, EXT) == 0) + create_header(entry->d_name); + } + closedir(csv_dir); + return 0; +} -- cgit v1.2.3 From 58fcae82b2ad6333af29f7d4f4ac5fba249349dc Mon Sep 17 00:00:00 2001 From: Mosder Date: Tue, 14 Jul 2026 20:06:57 +0200 Subject: refactor: removed comment that was unfortunately wrong --- analyzer/parser/fight_results.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'analyzer/parser') diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index de7f525..17c2f05 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -125,7 +125,7 @@ typedef struct { int field28; char *field29; int field30; - int field31; // possibly information who is the player - 1 for current player, 0 for others (need to double check) + int field31; int field32; int field33; char *field34; -- cgit v1.2.3 From 640532f1d0b42323333cabc029af91da6d982d42 Mon Sep 17 00:00:00 2001 From: Mosder Date: Thu, 16 Jul 2026 16:09:12 +0200 Subject: feat: added "prettier" printing of fight results. still need to better parse items, drifs and gear --- Makefile | 5 +- analyzer/analyzer.c | 33 +--------- analyzer/parser/fight_results.c | 4 +- analyzer/render.c | 142 ++++++++++++++++++++++++++++++++++++++++ analyzer/render.h | 60 +++++++++++++++++ 5 files changed, 211 insertions(+), 33 deletions(-) create mode 100644 analyzer/render.c create mode 100644 analyzer/render.h (limited to 'analyzer/parser') diff --git a/Makefile b/Makefile index cb40a5a..dcdabe2 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ global: compile local: compile mv branal ${HOME}/.local/bin -compile: csv structures utils parser capture analyzer +compile: csv structures utils parser capture analyzer render $(CC) $(CFLAGS) -lpcap *.o main.c -o branal rm *.o @@ -36,5 +36,8 @@ capture: analyzer: $(CC) $(CFLAGS) -c analyzer/analyzer.c +render: + $(CC) $(CFLAGS) -c analyzer/render.c + .PHONY: analyzer .PHONY: csv diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index 7def1e6..9ca2d6a 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -4,6 +4,7 @@ #include #include "parser/parser.h" +#include "render.h" #include "utils/tcp.h" // loop through packets received from pcap_handle and analyze the TCP stream @@ -36,41 +37,13 @@ void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle, void (*parsed_data_ha } } -// prints fight resutls -void print_fight_results(FightResults results) { - printf("Enemies:\n\t"); - for (size_t i = 0; i < results.num_enemy; i++) { - EnemyResults enemy = results.enemy_results[i]; - printf("%s (%d)%s", enemy.name, enemy.level, i < results.num_enemy - 1 ? ", " : "\n"); - } - printf("Player rewards:\n"); - for (size_t i = 0; i < results.num_friendly; i++) { - FriendlyResults friendly = results.friendly_results[i]; - printf("\t%s (%d):\n", friendly.name, friendly.level); - printf("\t\texp: %d\n", friendly.exp); - printf("\t\tgold: %d\n", friendly.gold); - if (friendly.psycho > 0) - printf("\t\tpsycho: %d\n", friendly.psycho); - if (strlen(friendly.items) > 0) - printf("\t\titems: %s\n", friendly.items); - if (strlen(friendly.gear) > 0) - printf("\t\tgear: %s\n", friendly.gear); - if (strlen(friendly.drifs) > 0) - printf("\t\tdrifs: %s\n", friendly.drifs); - if (friendly.splinters > 0) - printf("\t\tsplinters: %d\n", friendly.splinters); - if (friendly.saturation > 0) - printf("\t\tsaturation: %d (%s)\n", friendly.saturation, friendly.saturation_type); - } - printf("\n"); -} - // file analyzer parsed data handler void file_parsed_data_handler(ParsedData *parsed_data, size_t n_parsed_data) { for (size_t i = 0; i < n_parsed_data; i++) { switch (parsed_data[i].data_type) { case TYPE_FIGHT_RESULTS: - print_fight_results(*(FightResults *)parsed_data[i].data); + printf("\nFIGHT:\n"); + render_fight_results(*(FightResults *)parsed_data[i].data); break; } } diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index b3227e2..603d66d 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -83,11 +83,11 @@ EntityResults get_entity_results(byte_t *data, size_t length) { } char *get_item_name(int item_id) { - return item_id < id_to_item_name_len ? id_to_item_name[item_id] : "item_id out of bounds"; + return item_id < id_to_item_name_len ? id_to_item_name[item_id] : "null"; } char *get_saturation_type(int saturation_id) { - return saturation_id < id_to_saturation_type_len ? id_to_saturation_type[saturation_id] : "saturation_id out of bounds"; + return saturation_id < id_to_saturation_type_len ? id_to_saturation_type[saturation_id] : "null"; } // parse EntityResults to FriendlyResults diff --git a/analyzer/render.c b/analyzer/render.c new file mode 100644 index 0000000..0c3df56 --- /dev/null +++ b/analyzer/render.c @@ -0,0 +1,142 @@ +#include "render.h" + +#include +#include + +// strlen counting utf-8 characters as one +size_t utf8_strlen(char *s) { + size_t len = 0; + while (*s) { + // if the byte is not a continuation byte (10xxxxxx), it's a new character + if ((*s & 0xC0) != 0x80) + len++; + s++; + } + return len; +} + +void print_row_separator() { + for (size_t i = 0; i < TABLE_LINE_WIDTH; i++) + printf("%c", TABLE_ROW_SEPARATOR_CHAR); + printf("\n"); +} + +// print string with given color +void print_color(char *str, int color) { + printf("\033[%dm%s\033[0m", color, str); +} + +void print_center(char *text, int color, size_t width, char padding_char) { + size_t post_char_count = (width - strlen(text)) / 2; + size_t pre_char_count = width - post_char_count - strlen(text); + + for (size_t i = 0; i < pre_char_count; i++) + printf("%c", padding_char); + + print_color(text, color); + + for (size_t i = 0; i < post_char_count; i++) + printf("%c", padding_char); +} + +void print_enemy_list(EnemyResults *results, size_t n_enemies) { + printf(TABLE_LINE_START); + size_t line_length = TABLE_LINE_START_WIDTH; + + for (size_t i = 0; i < n_enemies; i++) { + EnemyResults enemy = results[i]; + char enemy_buffer[TABLE_LINE_WIDTH + 1]; + sprintf(enemy_buffer, "%s (%d)%s", enemy.name, enemy.level, i < n_enemies - 1 ? ", " : ""); + + // break line if length would be too big + if (line_length + utf8_strlen(enemy_buffer) > TABLE_LINE_WIDTH - TABLE_LINE_END_WIDTH) { + printf("%*s\n", (int)(TABLE_LINE_WIDTH - line_length), TABLE_LINE_END); + printf(TABLE_LINE_START); + line_length = TABLE_LINE_START_WIDTH; + } + + printf("%s", enemy_buffer); + line_length += utf8_strlen(enemy_buffer); + } + + printf("%*s\n", (int)(TABLE_LINE_WIDTH - line_length), TABLE_LINE_END); +} + +void print_column_names() { + char *names[TABLE_COLUMN_COUNT] = TABLE_COLUMN_NAMES; + int colors[TABLE_COLUMN_COUNT] = TABLE_COLUMN_COLORS; + size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; + + printf(TABLE_LINE_START); + for (size_t i = 0; i < TABLE_COLUMN_COUNT; i++) { + print_center(names[i], colors[i], widths[i], ' '); + printf(i < TABLE_COLUMN_COUNT - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); + } + printf("\n"); +} + +void print_player_rewards(FriendlyResults results) { + char buffers[TABLE_COLUMN_COUNT][TABLE_LINE_WIDTH]; + size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; + int colors[TABLE_COLUMN_COUNT] = TABLE_COLUMN_COLORS; + + sprintf(buffers[0], "%s (%d)", results.name, results.level); + sprintf(buffers[1], "%d", results.exp); + sprintf(buffers[2], "%d", results.gold); + + if (results.psycho > 0) + sprintf(buffers[3], "%d", results.psycho); + else + sprintf(buffers[3], "-"); + + if (results.saturation > 0) + sprintf(buffers[4], "%d (%s)", results.saturation, results.saturation_type); + else + sprintf(buffers[4], "-"); + + if (results.splinters > 0) + sprintf(buffers[5], "%d", results.splinters); + else + sprintf(buffers[5], "-"); + + printf(TABLE_LINE_START); + for (size_t i = 0; i < TABLE_COLUMN_COUNT; i++) { + char to_color[widths[i] + 1]; + sprintf(to_color, "%*s", (int)widths[i], buffers[i]); + print_color(to_color, colors[i]); + printf(i < TABLE_COLUMN_COUNT - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); + } + printf("\n"); + + // TODO: change this when I parse the items etc. to a readable format + // needs to utilize utf8_strlen, because right now utf8 characters mess the table up + char big_buf[1024]; + sprintf(big_buf, "%s - %s - %s", results.items, results.gear, results.drifs); + if (strlen(big_buf) > 6) { + printf(TABLE_LINE_START); + print_center("ITEMS", COLOR_WHITE, TABLE_ITEMS_WIDTH, ' '); + printf(TABLE_LINE_END "\n"); + + for (size_t i = 0; i < strlen(big_buf); i += TABLE_ITEMS_WIDTH) { + printf(TABLE_LINE_START "%-*.*s" TABLE_LINE_END "\n", (int)TABLE_ITEMS_WIDTH, (int)TABLE_ITEMS_WIDTH, big_buf + i); + } + } +} + +void render_fight_results(FightResults results) { + print_center("ENEMIES", COLOR_RED, TABLE_LINE_WIDTH, TABLE_ROW_SEPARATOR_CHAR); + printf("\n"); + + print_enemy_list(results.enemy_results, results.num_enemy); + + print_center("PLAYER REWARDS", COLOR_GREEN, TABLE_LINE_WIDTH, TABLE_ROW_SEPARATOR_CHAR); + printf("\n"); + + print_column_names(); + print_row_separator(); + + for (size_t i = 0; i < results.num_friendly; i++) { + print_player_rewards(results.friendly_results[i]); + print_row_separator(); + } +} diff --git a/analyzer/render.h b/analyzer/render.h new file mode 100644 index 0000000..d54015a --- /dev/null +++ b/analyzer/render.h @@ -0,0 +1,60 @@ +// render.h +// functions for "rendering" data onto the terminal + +#include "parser/fight_results.h" + +// ANSI escape sequence colors +#define COLOR_RED 91 +#define COLOR_GREEN 92 +#define COLOR_WHITE 97 +#define COLOR_YELLOW 93 +#define COLOR_AQUA 96 +#define COLOR_BLUE 94 + +#define TABLE_ROW_SEPARATOR_CHAR '-' + +#define TABLE_LINE_START "| " +#define TABLE_LINE_END " |" +#define TABLE_LINE_SEPARATOR " | " +#define TABLE_LINE_START_WIDTH (sizeof(TABLE_LINE_START) - 1) +#define TABLE_LINE_END_WIDTH (sizeof(TABLE_LINE_END) - 1) +#define TABLE_LINE_SEPARATOR_WIDTH (sizeof(TABLE_LINE_SEPARATOR) - 1) + +#define TABLE_COLUMN_COUNT 6 + +#define TABLE_NAME_LVL_WIDTH 17 +#define TABLE_EXP_WIDTH 7 +#define TABLE_GOLD_WIDTH 7 +#define TABLE_PSYCHO_WIDTH 6 +#define TABLE_SATURATION_WIDTH 13 +#define TABLE_SPLINTERS_WIDTH 9 + +#define TABLE_COLUMN_NAMES {"Nick (LVL)", "EXP", "Gold", "Psycho", "Saturation", "Splinters"} +#define TABLE_COLUMN_COLORS {COLOR_WHITE, COLOR_WHITE, COLOR_YELLOW, COLOR_AQUA, COLOR_GREEN, COLOR_BLUE} +#define TABLE_COLUMN_WIDTHS { \ + TABLE_NAME_LVL_WIDTH, \ + TABLE_EXP_WIDTH, \ + TABLE_GOLD_WIDTH, \ + TABLE_PSYCHO_WIDTH, \ + TABLE_SATURATION_WIDTH, \ + TABLE_SPLINTERS_WIDTH \ +} + +#define TABLE_LINE_WIDTH ( \ + TABLE_LINE_START_WIDTH + \ + TABLE_NAME_LVL_WIDTH + \ + TABLE_EXP_WIDTH + \ + TABLE_GOLD_WIDTH + \ + TABLE_PSYCHO_WIDTH + \ + TABLE_SATURATION_WIDTH + \ + TABLE_SPLINTERS_WIDTH + \ + (TABLE_COLUMN_COUNT - 1) * TABLE_LINE_SEPARATOR_WIDTH + \ + TABLE_LINE_END_WIDTH \ +) + +#define TABLE_ITEMS_WIDTH (TABLE_LINE_WIDTH - TABLE_LINE_START_WIDTH - TABLE_LINE_END_WIDTH) + +// render fight results onto the terminal +// params: +// - results - fight results to render +extern void render_fight_results(FightResults results); -- cgit v1.2.3 From fe7f277dc14b4d3060195a854d933d43a4967051 Mon Sep 17 00:00:00 2001 From: Mosder Date: Thu, 16 Jul 2026 17:45:44 +0200 Subject: refactor: removed redundant parentheses --- analyzer/parser/fight_results.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'analyzer/parser') diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index 603d66d..d7b185a 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -148,7 +148,7 @@ ParsedData parse_fight_results(byte_t *data, size_t 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); + 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); -- cgit v1.2.3 From e0bf82fe2af66ef55872569ae15733f70b4913d4 Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 20 Jul 2026 15:39:23 +0200 Subject: feat: added parsing of normal items --- analyzer/parser/fight_results.c | 76 +++++++++++++++++++++++++++++++++++------ analyzer/parser/fight_results.h | 10 ++++-- analyzer/render.c | 23 ++++++++----- csv/items.csv | 1 + 4 files changed, 88 insertions(+), 22 deletions(-) (limited to 'analyzer/parser') diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index d7b185a..5cda649 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -11,10 +11,11 @@ void cleanup_fight_results(void *data) { FightResults *fight_results = data; for (size_t i = 0; i < fight_results->num_friendly; i++) { - free(fight_results->friendly_results[i].name); - free(fight_results->friendly_results[i].gear); - free(fight_results->friendly_results[i].items); - free(fight_results->friendly_results[i].drifs); + FriendlyResults friendly = fight_results->friendly_results[i]; + free(friendly.name); + for (size_t j = 0; j < friendly.num_items; j++) + free(friendly.items[j]); + free(friendly.items); } for (size_t i = 0; i < fight_results->num_enemy; i++) { free(fight_results->enemy_results[i].name); @@ -86,6 +87,59 @@ char *get_item_name(int item_id) { return item_id < id_to_item_name_len ? id_to_item_name[item_id] : "null"; } +char **expand_items(char **items, size_t *items_capacity, size_t to_fit) { + while (*items_capacity < to_fit) + *items_capacity <<= 1; + return realloc(items, *items_capacity * sizeof(char *)); +} + +void add_gear(FriendlyResults *results, size_t *items_capacity, char *gear_str) { + if (strlen(gear_str) == 0) + return; + + // TODO: parse + if (results->num_items >= *items_capacity) + results->items = expand_items(results->items, items_capacity, results->num_items + 1); + + results->items[results->num_items++] = strdup(gear_str); +} + +void add_drifs(FriendlyResults *results, size_t *items_capacity, char *drifs_str) { + if (strlen(drifs_str) == 0) + return; + + // TODO: parse + if (results->num_items >= *items_capacity) + results->items = expand_items(results->items, items_capacity, results->num_items + 1); + + results->items[results->num_items++] = strdup(drifs_str); +} + +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); + + // 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++] = strdup(buffer); + } 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"; } @@ -97,16 +151,18 @@ FriendlyResults parse_to_friendly(EntityResults entity_results) { results.name = strdup(entity_results.name); results.exp = entity_results.exp; results.gold = entity_results.gold; - // TODO: parse that to more readable format - results.gear = strdup(entity_results.gear); - // TODO: parse that to more readable format, determine types from ids - results.items = strdup(entity_results.items); results.level = entity_results.level; results.psycho = entity_results.psycho; - // TODO: parse that to more readable format - results.drifs = strdup(entity_results.drifs); 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(char *)); + 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; diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index 17c2f05..ca45348 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -133,6 +133,11 @@ typedef struct { } EntityResults; #endif +// items/gear/drifs fields parsing info +#define INIT_ITEMS_CAPACITY 8 +#define SINGLE_ITEM_BUFFER_LEN 128 +#define SEPARATOR_ITEMS "[-]" + // values of field "team" #define TEAM_FRIEND 1 #define TEAM_ENEMY 2 @@ -144,11 +149,10 @@ typedef struct { char *name; int exp; int gold; - char *gear; - char *items; + char **items; + size_t num_items; int level; int psycho; - char *drifs; int splinters; char *saturation_type; int saturation; diff --git a/analyzer/render.c b/analyzer/render.c index 0c3df56..402fcb5 100644 --- a/analyzer/render.c +++ b/analyzer/render.c @@ -108,18 +108,23 @@ void print_player_rewards(FriendlyResults results) { } printf("\n"); - // TODO: change this when I parse the items etc. to a readable format - // needs to utilize utf8_strlen, because right now utf8 characters mess the table up - char big_buf[1024]; - sprintf(big_buf, "%s - %s - %s", results.items, results.gear, results.drifs); - if (strlen(big_buf) > 6) { + if (results.num_items > 0) { printf(TABLE_LINE_START); print_center("ITEMS", COLOR_WHITE, TABLE_ITEMS_WIDTH, ' '); - printf(TABLE_LINE_END "\n"); - - for (size_t i = 0; i < strlen(big_buf); i += TABLE_ITEMS_WIDTH) { - printf(TABLE_LINE_START "%-*.*s" TABLE_LINE_END "\n", (int)TABLE_ITEMS_WIDTH, (int)TABLE_ITEMS_WIDTH, big_buf + i); + printf(TABLE_LINE_END "\n" TABLE_LINE_START); + + size_t line_len = 0; + for (size_t i = 0; i < results.num_items; i++) { + char *sep = i < results.num_items - 1 ? ", " : ""; + size_t item_len = utf8_strlen(results.items[i]) + strlen(sep); + if (line_len + item_len > TABLE_ITEMS_WIDTH) { + printf("%*s" TABLE_LINE_END "\n" TABLE_LINE_START, (int)(TABLE_ITEMS_WIDTH - line_len), ""); + line_len = 0; + } + printf("%s%s", results.items[i], sep); + line_len += item_len; } + printf("%*s" TABLE_LINE_END "\n", (int)(TABLE_ITEMS_WIDTH - line_len), ""); } } diff --git a/csv/items.csv b/csv/items.csv index de9a7ce..0265384 100644 --- a/csv/items.csv +++ b/csv/items.csv @@ -3,3 +3,4 @@ id,item_name 1219,Trop trwogi 986,Kryształ czasu 703,Glif arcymistrzowski +697,Glif bakalarski -- cgit v1.2.3 From c182f2f4f850589c23b2241e11dab143e1003ff2 Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 20 Jul 2026 15:48:51 +0200 Subject: refactor: changed #ifndef guards, so that there is one per file --- analyzer/parser/fight_results.h | 17 +++++------------ analyzer/parser/parser_defs.h | 15 ++++----------- analyzer/structures/heap.h | 8 +++++--- analyzer/utils/tcp.h | 11 +++++------ 4 files changed, 19 insertions(+), 32 deletions(-) (limited to 'analyzer/parser') diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index ca45348..3aadf09 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -4,6 +4,9 @@ #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 // @@ -92,8 +95,6 @@ &entity_results.saturation \ } -#ifndef ENTITY_RESULTS_DEFINED -#define ENTITY_RESULTS_DEFINED typedef struct { int team; // friend or enemy char *name; @@ -131,7 +132,6 @@ typedef struct { char *field34; char *saturation; } EntityResults; -#endif // items/gear/drifs fields parsing info #define INIT_ITEMS_CAPACITY 8 @@ -143,8 +143,6 @@ typedef struct { #define TEAM_ENEMY 2 // fight results data for friendly -#ifndef FRIENDLY_RESULTS_DEFINED -#define FRIENDLY_RESULTS_DEFINED typedef struct { char *name; int exp; @@ -157,29 +155,24 @@ typedef struct { char *saturation_type; int saturation; } FriendlyResults; -#endif // fight results data for enemy -#ifndef ENEMY_RESULTS_DEFINED -#define ENEMY_RESULTS_DEFINED typedef struct { char *name; int level; } EnemyResults; -#endif #define MAX_ENTITIES_PER_SIDE 8 // entire fight resutls -#ifndef FIGHT_RESULTS_DEFINED -#define FIGHT_RESULTS_DEFINED 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; -#endif extern find_function_t find_fight_results; extern parse_function_t parse_fight_results; + +#endif diff --git a/analyzer/parser/parser_defs.h b/analyzer/parser/parser_defs.h index 6af6059..aa39a4c 100644 --- a/analyzer/parser/parser_defs.h +++ b/analyzer/parser/parser_defs.h @@ -3,18 +3,13 @@ #include "utils/tcp.h" -#ifndef TYPE_ENUM_DEFINED -#define TYPE_ENUM_DEFINED +#ifndef PARSER_DEFS_H +#define PARSER_DEFS_H + typedef enum { INT, FLOAT, DOUBLE, STRING } TypeEnum; -#endif -#ifndef RESULTS_ENUM_DEFINED -#define RESULTS_ENUM_DEFINED typedef enum { TYPE_FIGHT_RESULTS } ResultsEnum; -#endif -#ifndef PARSED_DATA_DEFINED -#define PARSED_DATA_DEFINED // "generic" type for parsed data typedef struct { ResultsEnum data_type; // type of data represented by an enum @@ -22,7 +17,6 @@ typedef struct { void (*data_cleanup)(void *data); // function to cleanup internal allocations inside data // if no internal allocations - this should be NULL } ParsedData; -#endif // find the first results data of specific type in stream // params: @@ -42,10 +36,9 @@ typedef byte_t *find_function_t(TCPStream *stream, int offset, int *length); typedef ParsedData parse_function_t(byte_t *data, size_t length); // struct for holding the find and parse function for every result type -#ifndef FIND_PARSE_FUNCTIONS_DEFINED -#define FIND_PARSE_FUNCTIONS_DEFINED typedef struct { find_function_t *find_fn; parse_function_t *parse_fn; } FindParseFunctions; + #endif diff --git a/analyzer/structures/heap.h b/analyzer/structures/heap.h index 8a23164..1ae72c0 100644 --- a/analyzer/structures/heap.h +++ b/analyzer/structures/heap.h @@ -3,11 +3,12 @@ #include +#ifndef HEAP_H +#define HEAP_H + #define INIT_HEAP_DEPTH 7 #define INIT_HEAP_CAPACITY (1 << INIT_HEAP_DEPTH) - 1 -#ifndef HEAP_DEFINED -#define HEAP_DEFINED typedef struct { void **data; size_t n_elements; @@ -16,7 +17,6 @@ typedef struct { int (*compare)(void *element1, void *element2); void (*element_cleanup)(void *element); } Heap; -#endif // create a new heap // params: @@ -56,3 +56,5 @@ extern void heap_top_remove(Heap *heap); // free the heap // - heap - heap to free extern void heap_free(Heap *heap); + +#endif diff --git a/analyzer/utils/tcp.h b/analyzer/utils/tcp.h index 9c6a949..13cbb15 100644 --- a/analyzer/utils/tcp.h +++ b/analyzer/utils/tcp.h @@ -5,19 +5,19 @@ #include "structures/heap.h" +#ifndef TCP_H +#define TCP_H + typedef unsigned char byte_t; // struct to persistently store TCP segments // payload field is malloced and needs to be freed -#ifndef TCP_SEGMENT_DEFINED -#define TCP_SEGMENT_DEFINED typedef struct { byte_t *payload; // payload in the segment size_t len; // length of the payload uint32_t seq; // TCP sequence number of the segment uint16_t port; // destination (client) port of connection the segment was received from } TCPSegment; -#endif // get TCP segment from captured packet // params: @@ -28,8 +28,6 @@ typedef struct { extern TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data); // struct that stores all the information about stream -#ifndef TCP_STREAM_DEFINED -#define TCP_STREAM_DEFINED typedef struct { byte_t *data; // bytes in the stream size_t len; // length of the stream @@ -38,7 +36,6 @@ typedef struct { uint16_t port; // destination (client) port of current analyzed connection Heap *pending; // min heap of segments yet to be added to stream (sorted by TCP sequence number) } TCPStream; -#endif #define INIT_STREAM_CAPACITY 256 @@ -68,3 +65,5 @@ extern void remove_parsed_data(TCPStream *stream, size_t parsed_len); // returns: // 1 if the segment was added to the stream, 0 if it was discarded or saved to pending extern int handle_segment(TCPStream *stream, TCPSegment segment); + +#endif -- cgit v1.2.3 From f73349115edbc75021885eefd33ee33ee504d9a7 Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 20 Jul 2026 17:58:02 +0200 Subject: feat: added drif parsing and coloring of items depending on type --- analyzer/parser/fight_results.c | 144 ++++++++++++++++++++++++++-------------- analyzer/parser/fight_results.h | 91 +++++++++++++++++++++++-- analyzer/render.c | 23 ++++++- analyzer/render.h | 7 +- 4 files changed, 205 insertions(+), 60 deletions(-) (limited to 'analyzer/parser') diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index 5cda649..f201fb1 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -14,7 +14,7 @@ void cleanup_fight_results(void *data) { FriendlyResults friendly = fight_results->friendly_results[i]; free(friendly.name); for (size_t j = 0; j < friendly.num_items; j++) - free(friendly.items[j]); + free(friendly.items[j].data); free(friendly.items); } for (size_t i = 0; i < fight_results->num_enemy; i++) { @@ -29,6 +29,47 @@ byte_t *find_fight_results(TCPStream *stream, int offset, int *length) { 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(SEPARATOR_RESULT); + data = sep + strlen(SEPARATOR_RESULT); + } + } +} + void destroy_entity_results(EntityResults results) { free(results.name); free(results.field7); @@ -45,52 +86,14 @@ 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); - - for (size_t i = 0; i < ENTITY_FIELD_COUNT; i++) { - // get string buffer of given result - byte_t *result_separator = memmem(data, length, SEPARATOR_RESULT, strlen(SEPARATOR_RESULT)); - size_t len = result_separator ? result_separator - data : length; - char *str = malloc(len + 1); - memcpy(str, data, len); - str[len] = '\0'; - - // parse depending on variable type - switch (field_types[i]) { - case INT: - sscanf(str, "%d", (int *)field_pointers[i]); - free(str); - break; - case FLOAT: - sscanf(str, "%f", (float *)field_pointers[i]); - free(str); - break; - case DOUBLE: - sscanf(str, "%lf", (double *)field_pointers[i]); - free(str); - break; - case STRING: - *(char **)field_pointers[i] = str; - break; - } - - // update data pointer and length - if (result_separator) { - length -= result_separator - data + strlen(SEPARATOR_RESULT); - data = result_separator + strlen(SEPARATOR_RESULT); - } - } - + get_struct_data(ENTITY_FIELD_COUNT, field_types, field_pointers, SEPARATOR_RESULT, (char *)data, length); return results; } -char *get_item_name(int item_id) { - return item_id < id_to_item_name_len ? id_to_item_name[item_id] : "null"; -} - -char **expand_items(char **items, size_t *items_capacity, size_t to_fit) { +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(char *)); + return realloc(items, *items_capacity * sizeof(Item)); } void add_gear(FriendlyResults *results, size_t *items_capacity, char *gear_str) { @@ -101,18 +104,58 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gear_str) if (results->num_items >= *items_capacity) results->items = expand_items(results->items, items_capacity, results->num_items + 1); - results->items[results->num_items++] = strdup(gear_str); + Item item; + item.data = strdup(gear_str); + item.type = RARE; + results->items[results->num_items++] = item; +} + +void destroy_drif(Drif drif) { + free(drif.field1); + free(drif.field4); + free(drif.field11); + free(drif.field12); + free(drif.name); + free(drif.field18); + free(drif.field19); +} + +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_INFO, data, length); + return drif; } void add_drifs(FriendlyResults *results, size_t *items_capacity, char *drifs_str) { if (strlen(drifs_str) == 0) return; - // TODO: parse - if (results->num_items >= *items_capacity) - results->items = expand_items(results->items, items_capacity, results->num_items + 1); + 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)); - results->items[results->num_items++] = strdup(drifs_str); + // if there are still drifs - modify drifs_str pointer + if (drifs_separator) + drifs_str = drifs_separator + strlen(SEPARATOR_DRIFS); + + // expand if needed + if (results->num_items >= *items_capacity) + results->items = expand_items(results->items, items_capacity, results->num_items + 1); + + Item item; + item.data = strdup(drif.name); + item.type = DRIF; + 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) { @@ -136,7 +179,10 @@ void add_items(FriendlyResults *results, size_t *items_capacity, char *items_str if (results->num_items >= *items_capacity) results->items = expand_items(results->items, items_capacity, results->num_items + 1); - results->items[results->num_items++] = strdup(buffer); + Item item; + item.data = strdup(buffer); + item.type = NORMAL; + results->items[results->num_items++] = item; } while (items_separator); } @@ -157,7 +203,7 @@ FriendlyResults parse_to_friendly(EntityResults entity_results) { // create items array and add to it gear, drifs and items results.num_items = 0; - results.items = malloc(INIT_ITEMS_CAPACITY * sizeof(char *)); + 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); diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index 3aadf09..76083ab 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -9,17 +9,16 @@ // Payload structure: // FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END -// -// EntityResults structure: -// field (SEPARATOR_RESULT field)* -// fields are defined in order in the EntityResults struct - #define FIGHT_RESULTS_START "3;19;" #define FIGHT_RESULTS_END "[--];" #define SEPARATOR_ENTITY "[--]" #define SEPARATOR_RESULT "&" +// 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, \ @@ -94,7 +93,7 @@ &entity_results.field34, \ &entity_results.saturation \ } - +// clang-format on typedef struct { int team; // friend or enemy char *name; @@ -137,6 +136,84 @@ typedef struct { #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_DRIF_INFO "," +// Item struct - for keeping item string and type +typedef enum { NORMAL, DRIF, SYNG, SET, RARE, EPIC } ItemType; +typedef struct { + char *data; + ItemType type; +} Item; + +// Drif structure: +// field (SEPARATOR_DRIF field)* +// fields are defined in order in the Drif struct +#define DRIF_FIELD_COUNT 19 +// clang-format off +#define DRIF_FIELD_TYPES { \ + /* field1 */ STRING, \ + /* field2 */ INT, \ + /* field3 */ INT, \ + /* field4 */ STRING, \ + /* field5 */ INT, \ + /* field6 */ INT, \ + /* field7 */ FLOAT, \ + /* field8 */ FLOAT, \ + /* field9 */ INT, \ + /* field10 */ INT, \ + /* field11 */ STRING, \ + /* field12 */ STRING, \ + /* field13 */ INT, \ + /* field14 */ INT, \ + /* field15 */ INT, \ + /* field16 */ INT, \ + /* name */ STRING, \ + /* field18 */ STRING, \ + /* field19 */ STRING, \ +} +#define DRIF_FIELD_ADDRESSES(drif) {\ + &drif.field1, \ + &drif.field2, \ + &drif.field3, \ + &drif.field4, \ + &drif.field5, \ + &drif.field6, \ + &drif.field7, \ + &drif.field8, \ + &drif.field9, \ + &drif.field10, \ + &drif.field11, \ + &drif.field12, \ + &drif.field13, \ + &drif.field14, \ + &drif.field15, \ + &drif.field16, \ + &drif.name, \ + &drif.field18, \ + &drif.field19, \ +} +// clang-format on +typedef struct { + char *field1; + int field2; + int field3; + char *field4; + int field5; + int field6; + float field7; + float field8; + int field9; + int field10; + char *field11; + char *field12; + int field13; + int field14; + int field15; + int field16; + char *name; // drif + char *field18; + char *field19; +} Drif; // values of field "team" #define TEAM_FRIEND 1 @@ -147,7 +224,7 @@ typedef struct { char *name; int exp; int gold; - char **items; + Item *items; size_t num_items; int level; int psycho; diff --git a/analyzer/render.c b/analyzer/render.c index 402fcb5..28d1673 100644 --- a/analyzer/render.c +++ b/analyzer/render.c @@ -75,6 +75,24 @@ void print_column_names() { printf("\n"); } +int item_type_to_color(ItemType type) { + switch (type) { + case NORMAL: + return COLOR_WHITE; + case DRIF: + return COLOR_YELLOW; + case SYNG: + return COLOR_RED; + case SET: + return COLOR_GREEN; + case RARE: + return COLOR_AQUA; + case EPIC: + return COLOR_PURPLE; + } + return COLOR_WHITE; +} + void print_player_rewards(FriendlyResults results) { char buffers[TABLE_COLUMN_COUNT][TABLE_LINE_WIDTH]; size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; @@ -116,12 +134,13 @@ void print_player_rewards(FriendlyResults results) { size_t line_len = 0; for (size_t i = 0; i < results.num_items; i++) { char *sep = i < results.num_items - 1 ? ", " : ""; - size_t item_len = utf8_strlen(results.items[i]) + strlen(sep); + size_t item_len = utf8_strlen(results.items[i].data) + strlen(sep); if (line_len + item_len > TABLE_ITEMS_WIDTH) { printf("%*s" TABLE_LINE_END "\n" TABLE_LINE_START, (int)(TABLE_ITEMS_WIDTH - line_len), ""); line_len = 0; } - printf("%s%s", results.items[i], sep); + print_color(results.items[i].data, item_type_to_color(results.items[i].type)); + printf("%s", sep); line_len += item_len; } printf("%*s" TABLE_LINE_END "\n", (int)(TABLE_ITEMS_WIDTH - line_len), ""); diff --git a/analyzer/render.h b/analyzer/render.h index d54015a..97958d1 100644 --- a/analyzer/render.h +++ b/analyzer/render.h @@ -6,10 +6,11 @@ // ANSI escape sequence colors #define COLOR_RED 91 #define COLOR_GREEN 92 -#define COLOR_WHITE 97 #define COLOR_YELLOW 93 -#define COLOR_AQUA 96 #define COLOR_BLUE 94 +#define COLOR_PURPLE 95 +#define COLOR_AQUA 96 +#define COLOR_WHITE 97 #define TABLE_ROW_SEPARATOR_CHAR '-' @@ -29,6 +30,7 @@ #define TABLE_SATURATION_WIDTH 13 #define TABLE_SPLINTERS_WIDTH 9 +// clang-format off #define TABLE_COLUMN_NAMES {"Nick (LVL)", "EXP", "Gold", "Psycho", "Saturation", "Splinters"} #define TABLE_COLUMN_COLORS {COLOR_WHITE, COLOR_WHITE, COLOR_YELLOW, COLOR_AQUA, COLOR_GREEN, COLOR_BLUE} #define TABLE_COLUMN_WIDTHS { \ @@ -51,6 +53,7 @@ (TABLE_COLUMN_COUNT - 1) * TABLE_LINE_SEPARATOR_WIDTH + \ TABLE_LINE_END_WIDTH \ ) +// clang-format on #define TABLE_ITEMS_WIDTH (TABLE_LINE_WIDTH - TABLE_LINE_START_WIDTH - TABLE_LINE_END_WIDTH) -- cgit v1.2.3 From 5c0e713713143bad475a92d20ee5fe215b5bafd3 Mon Sep 17 00:00:00 2001 From: Mosder Date: Tue, 21 Jul 2026 17:26:09 +0200 Subject: feat: added parsing for gear --- Makefile | 1 + analyzer/parser/fight_results.c | 169 +++++++++++++++--- analyzer/parser/fight_results.h | 375 ++++++++++++++++++++++++++++++---------- analyzer/render.c | 2 + analyzer/render.h | 24 +-- analyzer/utils/roman.c | 16 ++ analyzer/utils/roman.h | 12 ++ csv/artifact_sizes.csv | 5 + csv/orbs.csv | 3 + 9 files changed, 482 insertions(+), 125 deletions(-) create mode 100644 analyzer/utils/roman.c create mode 100644 analyzer/utils/roman.h create mode 100644 csv/artifact_sizes.csv create mode 100644 csv/orbs.csv (limited to 'analyzer/parser') diff --git a/Makefile b/Makefile index afb61fd..72f94e0 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,7 @@ utils: $(CC) $(CFLAGS) -c analyzer/utils/time.c $(CC) $(CFLAGS) -c analyzer/utils/tcp.c $(CC) $(CFLAGS) -c analyzer/utils/tcp_seq_math.c + $(CC) $(CFLAGS) -c analyzer/utils/roman.c parser: $(CC) $(CFLAGS) -c analyzer/parser/parser.c diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index f201fb1..c1d6420 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -4,8 +4,11 @@ #include #include +#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) { @@ -70,15 +73,18 @@ void get_struct_data(size_t field_count, TypeEnum *field_types, void **field_poi } } +// 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) { - free(results.name); - free(results.field7); - free(results.gear); - free(results.items); - free(results.drifs); - free(results.field29); - free(results.field34); - free(results.saturation); + 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 @@ -96,35 +102,148 @@ Item *expand_items(Item *items, size_t *items_capacity, size_t to_fit) { return realloc(items, *items_capacity * sizeof(Item)); } -void add_gear(FriendlyResults *results, size_t *items_capacity, char *gear_str) { - if (strlen(gear_str) == 0) +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'}; + buffer[0] = star_types[incr_above_b1 / 3]; + buffer[1] = incr_above_b1 % 3 + 1 + '0'; + buffer[2] = '\0'; +} + +void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) { + if (strlen(gears_str) == 0) return; - // TODO: parse - if (results->num_items >= *items_capacity) - results->items = expand_items(results->items, items_capacity, results->num_items + 1); + 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_DRIFS); + + // expand if needed + if (results->num_items >= *items_capacity) + results->items = expand_items(results->items, items_capacity, results->num_items + 1); + + 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[3]; + get_stars(stars, gear_info.stars); + sprintf(buffer, "%s (%s)", buffer, stars); + break; + } + } + item.data = strdup(buffer); + destroy_gear_info(gear_info); - Item item; - item.data = strdup(gear_str); - item.type = RARE; - results->items[results->num_items++] = item; + 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[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; + results->items[results->num_items++] = item; + } + + destroy_gear(gear); + } while (gears_separator); } void destroy_drif(Drif drif) { - free(drif.field1); - free(drif.field4); - free(drif.field11); - free(drif.field12); - free(drif.name); - free(drif.field18); - free(drif.field19); + 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_INFO, data, length); + get_struct_data(DRIF_FIELD_COUNT, field_types, field_pointers, SEPARATOR_DRIF_FIELDS, data, length); return drif; } diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index 76083ab..59d1e45 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -14,6 +14,10 @@ #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 @@ -54,44 +58,44 @@ /* field32 */ INT, \ /* field33 */ INT, \ /* field34 */ STRING, \ - /* saturation */ 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 \ + &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 { @@ -137,87 +141,280 @@ typedef struct { #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_DRIF_INFO "," +#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, SYNG, SET, RARE, EPIC } ItemType; +typedef enum { NORMAL, DRIF, ORB, SYNG, SET, RARE, EPIC } ItemType; typedef struct { char *data; ItemType type; } Item; // Drif structure: -// field (SEPARATOR_DRIF field)* +// field (SEPARATOR_DRIF_FIELDS field)* // fields are defined in order in the Drif struct -#define DRIF_FIELD_COUNT 19 +#define DRIF_FIELD_COUNT 18 +#define SEPARATOR_DRIF_FIELDS "," // clang-format off #define DRIF_FIELD_TYPES { \ - /* field1 */ STRING, \ - /* field2 */ INT, \ - /* field3 */ INT, \ - /* field4 */ STRING, \ - /* field5 */ INT, \ - /* field6 */ INT, \ - /* field7 */ FLOAT, \ - /* field8 */ FLOAT, \ - /* field9 */ INT, \ - /* field10 */ INT, \ - /* field11 */ STRING, \ - /* field12 */ STRING, \ - /* field13 */ INT, \ - /* field14 */ INT, \ - /* field15 */ INT, \ - /* field16 */ INT, \ - /* name */ STRING, \ - /* field18 */ STRING, \ - /* field19 */ STRING, \ + /* 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.field3, \ - &drif.field4, \ - &drif.field5, \ - &drif.field6, \ - &drif.field7, \ - &drif.field8, \ - &drif.field9, \ - &drif.field10, \ - &drif.field11, \ - &drif.field12, \ - &drif.field13, \ - &drif.field14, \ - &drif.field15, \ - &drif.field16, \ - &drif.name, \ - &drif.field18, \ - &drif.field19, \ + &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; + char *field1; // just says "drif" int field2; - int field3; + int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy char *field4; int field5; - int field6; - float field7; - float field8; + 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 *field12; + char *size_str; // sub - small, medium - bi, big - magni, ??? - arcy int field13; - int field14; + int power; // how much space it takes int field15; int field16; char *name; // drif char *field18; - char *field19; } Drif; -// values of field "team" -#define TEAM_FRIEND 1 -#define TEAM_ENEMY 2 +// 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 { diff --git a/analyzer/render.c b/analyzer/render.c index 28d1673..0fdd334 100644 --- a/analyzer/render.c +++ b/analyzer/render.c @@ -81,6 +81,8 @@ int item_type_to_color(ItemType type) { return COLOR_WHITE; case DRIF: return COLOR_YELLOW; + case ORB: + return COLOR_GOLD; case SYNG: return COLOR_RED; case SET: diff --git a/analyzer/render.h b/analyzer/render.h index 97958d1..843cea5 100644 --- a/analyzer/render.h +++ b/analyzer/render.h @@ -4,6 +4,8 @@ #include "parser/fight_results.h" // ANSI escape sequence colors +#define COLOR_GOLD 33 +#define COLOR_GRAY 90 #define COLOR_RED 91 #define COLOR_GREEN 92 #define COLOR_YELLOW 93 @@ -34,24 +36,24 @@ #define TABLE_COLUMN_NAMES {"Nick (LVL)", "EXP", "Gold", "Psycho", "Saturation", "Splinters"} #define TABLE_COLUMN_COLORS {COLOR_WHITE, COLOR_WHITE, COLOR_YELLOW, COLOR_AQUA, COLOR_GREEN, COLOR_BLUE} #define TABLE_COLUMN_WIDTHS { \ - TABLE_NAME_LVL_WIDTH, \ - TABLE_EXP_WIDTH, \ - TABLE_GOLD_WIDTH, \ - TABLE_PSYCHO_WIDTH, \ + TABLE_NAME_LVL_WIDTH, \ + TABLE_EXP_WIDTH, \ + TABLE_GOLD_WIDTH, \ + TABLE_PSYCHO_WIDTH, \ TABLE_SATURATION_WIDTH, \ - TABLE_SPLINTERS_WIDTH \ + TABLE_SPLINTERS_WIDTH, \ } #define TABLE_LINE_WIDTH ( \ TABLE_LINE_START_WIDTH + \ - TABLE_NAME_LVL_WIDTH + \ - TABLE_EXP_WIDTH + \ - TABLE_GOLD_WIDTH + \ - TABLE_PSYCHO_WIDTH + \ + TABLE_NAME_LVL_WIDTH + \ + TABLE_EXP_WIDTH + \ + TABLE_GOLD_WIDTH + \ + TABLE_PSYCHO_WIDTH + \ TABLE_SATURATION_WIDTH + \ - TABLE_SPLINTERS_WIDTH + \ + TABLE_SPLINTERS_WIDTH + \ (TABLE_COLUMN_COUNT - 1) * TABLE_LINE_SEPARATOR_WIDTH + \ - TABLE_LINE_END_WIDTH \ + TABLE_LINE_END_WIDTH \ ) // clang-format on diff --git a/analyzer/utils/roman.c b/analyzer/utils/roman.c new file mode 100644 index 0000000..9ea2bbb --- /dev/null +++ b/analyzer/utils/roman.c @@ -0,0 +1,16 @@ +#include "utils/roman.h" + +#include +#include + +void to_roman(char *buffer, int number) { + buffer[0] = '\0'; + int nums[ROMAN_CHAR_COUNT] = ROMAN_NUMS; + char *chars[ROMAN_CHAR_COUNT] = ROMAN_CHARS; + for (size_t i = 0; i < ROMAN_CHAR_COUNT; i++) { + for (size_t j = 0; j < number / nums[i]; j++) { + strcat(buffer, chars[i]); + } + number %= nums[i]; + } +} diff --git a/analyzer/utils/roman.h b/analyzer/utils/roman.h new file mode 100644 index 0000000..b4f5d3f --- /dev/null +++ b/analyzer/utils/roman.h @@ -0,0 +1,12 @@ +// utils/roman.h +// functions for roman numbers + +#define ROMAN_CHAR_COUNT 13 +#define ROMAN_NUMS {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1} +#define ROMAN_CHARS {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"} + +// convert a arabic number to roman number +// params: +// buffer - buffer to save the roman number to +// number - arabic number to convert +extern void to_roman(char *buffer, int number); diff --git a/csv/artifact_sizes.csv b/csv/artifact_sizes.csv new file mode 100644 index 0000000..a9af916 --- /dev/null +++ b/csv/artifact_sizes.csv @@ -0,0 +1,5 @@ +id,artifact_size +1,Sub +2,Bi +3,Magni +4,Arcy diff --git a/csv/orbs.csv b/csv/orbs.csv new file mode 100644 index 0000000..02dfde4 --- /dev/null +++ b/csv/orbs.csv @@ -0,0 +1,3 @@ +id,orb_name +21,Ha'at +12,Se'at -- cgit v1.2.3 From c8a69da758ce89913fbb0c6a1918eecd0ccabf2f Mon Sep 17 00:00:00 2001 From: Mosder Date: Tue, 21 Jul 2026 19:11:51 +0200 Subject: fix: fixed memmem having incorrect length of haystack --- analyzer/parser/fight_results.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'analyzer/parser') diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index c1d6420..70d937f 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -26,8 +26,8 @@ void cleanup_fight_results(void *data) { } byte_t *find_fight_results(TCPStream *stream, int offset, int *length) { - byte_t *start = memmem(stream->data + offset, stream->len, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); - byte_t *end = memmem(stream->data + offset, stream->len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); + byte_t *start = memmem(stream->data + offset, stream->len - offset, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); + byte_t *end = memmem(stream->data + offset, stream->len - offset, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); *length = start != NULL && end != NULL ? end - start + strlen(FIGHT_RESULTS_END) : -1; return start; } -- cgit v1.2.3 From aab4922ae3dfac9e80e0328fb7dce2f40b9cffd2 Mon Sep 17 00:00:00 2001 From: Mosder Date: Tue, 21 Jul 2026 19:31:53 +0200 Subject: fix: fixed some bugs in parser/fight_results.c --- analyzer/parser/fight_results.c | 46 +++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 22 deletions(-) (limited to 'analyzer/parser') diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index 70d937f..a994674 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -27,8 +27,10 @@ void cleanup_fight_results(void *data) { 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 = memmem(stream->data + offset, stream->len - offset, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); - *length = start != NULL && end != NULL ? end - start + strlen(FIGHT_RESULTS_END) : -1; + 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; } @@ -67,8 +69,8 @@ void get_struct_data(size_t field_count, TypeEnum *field_types, void **field_poi // update data pointer and length if (sep) { - data_len -= sep - data + strlen(SEPARATOR_RESULT); - data = sep + strlen(SEPARATOR_RESULT); + data_len -= sep - data + strlen(data_separator); + data = sep + strlen(data_separator); } } } @@ -154,9 +156,7 @@ char *get_orb_name(int orb_name_id) { void get_stars(char *buffer, int incr_above_b1) { char star_types[] = {'B', 'S', 'G'}; - buffer[0] = star_types[incr_above_b1 / 3]; - buffer[1] = incr_above_b1 % 3 + 1 + '0'; - buffer[2] = '\0'; + 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) { @@ -170,11 +170,7 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) // if there are still gears - modify gears_str pointer if (gears_separator) - gears_str = gears_separator + strlen(SEPARATOR_DRIFS); - - // expand if needed - if (results->num_items >= *items_capacity) - results->items = expand_items(results->items, items_capacity, results->num_items + 1); + gears_str = gears_separator + strlen(SEPARATOR_GEARS); Item item; @@ -190,9 +186,9 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) sprintf(buffer, "%s (%d)", buffer, gear_info.lvl); break; default: { - char stars[3]; + char stars[6]; get_stars(stars, gear_info.stars); - sprintf(buffer, "%s (%s)", buffer, stars); + strcat(buffer, stars); break; } } @@ -215,6 +211,9 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) 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 @@ -226,6 +225,9 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) 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; } @@ -260,13 +262,13 @@ void add_drifs(FriendlyResults *results, size_t *items_capacity, char *drifs_str if (drifs_separator) drifs_str = drifs_separator + strlen(SEPARATOR_DRIFS); - // expand if needed - if (results->num_items >= *items_capacity) - results->items = expand_items(results->items, items_capacity, results->num_items + 1); - 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); @@ -294,13 +296,13 @@ void add_items(FriendlyResults *results, size_t *items_capacity, char *items_str if (items_separator) items_str = items_separator + strlen(SEPARATOR_ITEMS); - // expand if needed - if (results->num_items >= *items_capacity) - results->items = expand_items(results->items, items_capacity, results->num_items + 1); - 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); } -- cgit v1.2.3 From e20946d04232cdf9d21aac3a2e7352a548ba50ae Mon Sep 17 00:00:00 2001 From: Mosder Date: Tue, 21 Jul 2026 20:28:29 +0200 Subject: refactor: moved parser/fight_results.* into parser/fight_results/ --- analyzer/parser/fight_results.c | 392 ---------------------- analyzer/parser/fight_results.h | 452 -------------------------- analyzer/parser/fight_results/fight_results.c | 392 ++++++++++++++++++++++ analyzer/parser/fight_results/fight_results.h | 452 ++++++++++++++++++++++++++ analyzer/parser/find_parse_functions.h | 2 +- analyzer/parser/parser.h | 2 +- 6 files changed, 846 insertions(+), 846 deletions(-) delete mode 100644 analyzer/parser/fight_results.c delete mode 100644 analyzer/parser/fight_results.h create mode 100644 analyzer/parser/fight_results/fight_results.c create mode 100644 analyzer/parser/fight_results/fight_results.h (limited to 'analyzer/parser') diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c deleted file mode 100644 index a994674..0000000 --- a/analyzer/parser/fight_results.c +++ /dev/null @@ -1,392 +0,0 @@ -#include "parser/fight_results.h" - -#include -#include -#include - -#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.h b/analyzer/parser/fight_results.h deleted file mode 100644 index 59d1e45..0000000 --- a/analyzer/parser/fight_results.h +++ /dev/null @@ -1,452 +0,0 @@ -// 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; // drif - 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 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 +#include +#include + +#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; // drif + 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 diff --git a/analyzer/parser/find_parse_functions.h b/analyzer/parser/find_parse_functions.h index dd7579e..1b6ce9e 100644 --- a/analyzer/parser/find_parse_functions.h +++ b/analyzer/parser/find_parse_functions.h @@ -1,7 +1,7 @@ // parser/find_parse_functions.h // array of find and parse functions for every result type -#include "parser/fight_results.h" +#include "parser/fight_results/fight_results.h" #define RESULT_TYPE_COUNT 1 diff --git a/analyzer/parser/parser.h b/analyzer/parser/parser.h index 4b28aa7..e7c46e8 100644 --- a/analyzer/parser/parser.h +++ b/analyzer/parser/parser.h @@ -1,7 +1,7 @@ // parser/parser.h // main parser file -#include "parser/fight_results.h" +#include "parser/fight_results/fight_results.h" #include "parser/parser_defs.h" #include "utils/tcp.h" -- cgit v1.2.3 From 1f664d5b6396fb24c8ce4990ba8882118abb8dcd Mon Sep 17 00:00:00 2001 From: Mosder Date: Tue, 21 Jul 2026 22:48:20 +0200 Subject: refactor: improved csv table generations --- Makefile | 5 +- analyzer/parser/fight_results/fight_results.c | 24 +----- analyzer/parser/fight_results/fight_results.h | 2 +- analyzer/render.h | 2 +- csv_to_headers.c | 104 ++++++++++++++++++-------- 5 files changed, 82 insertions(+), 55 deletions(-) (limited to 'analyzer/parser') diff --git a/Makefile b/Makefile index 72f94e0..f45b9e6 100644 --- a/Makefile +++ b/Makefile @@ -15,9 +15,10 @@ compile: csv structures utils parser capture analyzer render rm *.o csv: - $(CC) csv_to_headers.c -o csv_to_headers + $(CC) $(CFLAGS) csv_to_headers.c -o csv_to_headers ./csv_to_headers rm csv_to_headers + $(CC) $(CFLAGS) -c analyzer/csv/*.c structures: $(CC) $(CFLAGS) -c analyzer/structures/hashmap.c @@ -32,7 +33,7 @@ utils: parser: $(CC) $(CFLAGS) -c analyzer/parser/parser.c - $(CC) $(CFLAGS) -c analyzer/parser/fight_results.c + $(CC) $(CFLAGS) -c analyzer/parser/fight_results/fight_results.c capture: $(CC) $(CFLAGS) -c analyzer/capture.c diff --git a/analyzer/parser/fight_results/fight_results.c b/analyzer/parser/fight_results/fight_results.c index a994674..4f0f3a1 100644 --- a/analyzer/parser/fight_results/fight_results.c +++ b/analyzer/parser/fight_results/fight_results.c @@ -1,4 +1,4 @@ -#include "parser/fight_results.h" +#include "parser/fight_results/fight_results.h" #include #include @@ -146,14 +146,6 @@ Orb get_orb(char *data, size_t 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'); @@ -221,7 +213,7 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) 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)); + sprintf(buffer, "%sorb %s", get_artifact_size_from_id(orb.size), get_orb_name_from_id(orb.id)); item.data = strdup(buffer); destroy_orb(orb); item.type = ORB; @@ -275,10 +267,6 @@ void add_drifs(FriendlyResults *results, size_t *items_capacity, char *drifs_str } 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; @@ -290,7 +278,7 @@ void add_items(FriendlyResults *results, size_t *items_capacity, char *items_str 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)); + 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) @@ -307,10 +295,6 @@ void add_items(FriendlyResults *results, size_t *items_capacity, char *items_str } 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; @@ -334,7 +318,7 @@ FriendlyResults parse_to_friendly(EntityResults entity_results) { 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); + results.saturation_type = get_saturation_type_from_id(saturation_id); } return results; diff --git a/analyzer/parser/fight_results/fight_results.h b/analyzer/parser/fight_results/fight_results.h index 59d1e45..0618bf2 100644 --- a/analyzer/parser/fight_results/fight_results.h +++ b/analyzer/parser/fight_results/fight_results.h @@ -426,7 +426,7 @@ typedef struct { int level; int psycho; int splinters; - char *saturation_type; + const char *saturation_type; int saturation; } FriendlyResults; diff --git a/analyzer/render.h b/analyzer/render.h index 843cea5..1015eb9 100644 --- a/analyzer/render.h +++ b/analyzer/render.h @@ -1,7 +1,7 @@ // render.h // functions for "rendering" data onto the terminal -#include "parser/fight_results.h" +#include "parser/fight_results/fight_results.h" // ANSI escape sequence colors #define COLOR_GOLD 33 diff --git a/csv_to_headers.c b/csv_to_headers.c index 5595713..51a1c58 100644 --- a/csv_to_headers.c +++ b/csv_to_headers.c @@ -6,74 +6,116 @@ #define EXT ".csv" #define EXT_LEN strlen(".csv") #define CSV_PATH "csv" -#define HEADER_PATH "analyzer/" CSV_PATH +#define OUT_PATH "analyzer/" CSV_PATH #define BUFFER_SIZE 256 #define MAX_ARR_SIZE 4096 -void create_header(char *file_name) { - // open input file - char input_path[BUFFER_SIZE]; - sprintf(input_path, "%s/%s", CSV_PATH, file_name); - FILE *input_fp = fopen(input_path, "r"); +// read values from input csv and save them to buffers +// returns max_index of the array +int read_csv(char *path, char *from, char *to, char from_to_array[][BUFFER_SIZE]) { + FILE *fp = fopen(path, "r"); // read field names - char from[BUFFER_SIZE], to[BUFFER_SIZE]; - fscanf(input_fp, "%[^,],%[^\n]", from, to); + fscanf(fp, "%[^,],%[^\n]", from, to); - // read values from CSV - char from_to_array[MAX_ARR_SIZE][BUFFER_SIZE] = {""}; + // read values size_t max_index = 0; size_t val1; char val2[BUFFER_SIZE]; - while (fscanf(input_fp, "%zu,%[^\n]", &val1, val2) == 2) { + while (fscanf(fp, "%zu,%[^\n]", &val1, val2) == 2) { strcpy(from_to_array[val1], val2); if (val1 > max_index) max_index = val1; } - // close the input file - fclose(input_fp); + fclose(fp); + + return max_index; +} + +// create output .h file +void create_h(char *path, char *from, char *to) { + FILE *fp = fopen(path, "w"); + + // write comments and function prototype + fprintf(fp, "// %s\n", strstr(path, CSV_PATH)); + fprintf(fp, "// translate %s to %s - generated from csv\n\n", from, to); + fprintf(fp, "// get %s from %s\n", to, from); + fprintf(fp, "// params:\n"); + fprintf(fp, "// - %s - %s to translate from\n", from, from); + fprintf(fp, "// returns:\n"); + fprintf(fp, "// %s corresponding to %s\n", to, from); + fprintf(fp, "extern const char *get_%s_from_%s(int %s);", to, from, from); + + fclose(fp); +} + +// create output .c file +void create_c(char *path, char *h_path, char *from, char *to, char from_to_array[][BUFFER_SIZE], size_t max_index) { + FILE *fp = fopen(path, "w"); - // create output file - char output_path[BUFFER_SIZE]; - sprintf(output_path, "%s/%s_to_%s.h", HEADER_PATH, from, to); - FILE *output_fp = fopen(output_path, "w"); + char arr_name[BUFFER_SIZE], arr_len_name[BUFFER_SIZE]; + sprintf(arr_name, "%s_to_%s", from, to); + sprintf(arr_len_name, "%s_to_%s_len", from, to); - // write comments and beginning of output array - fprintf(output_fp, "// %s\n", input_path); - fprintf(output_fp, "// array translating %s to %s - generated from csv file\n\n", from, to); - fprintf(output_fp, "char *%s_to_%s[] = {\n", from, to); + // write include and beginning of array + fprintf(fp, "#include \"%s\"\n\n", strstr(h_path, CSV_PATH)); + fprintf(fp, "static const char *%s[] = {\n", arr_name); // write values for (size_t i = 0; i <= max_index; i++) { if (strlen(from_to_array[i]) > 0) - fprintf(output_fp, " \"%s\",\n", from_to_array[i]); + fprintf(fp, " \"%s\",\n", from_to_array[i]); else - fprintf(output_fp, " \"%zu\",\n", i); + fprintf(fp, " \"%zu\",\n", i); } // write the end of array and len - fprintf(output_fp, "};\n"); - fprintf(output_fp, "int %s_to_%s_len = %zu;", from, to, max_index + 1); + fprintf(fp, "};\n"); + fprintf(fp, "int %s = %zu;\n\n", arr_len_name, max_index + 1); - // close the output file - fclose(output_fp); + // write the getter + fprintf(fp, "const char *get_%s_from_%s(int %s) {\n", to, from, from); + fprintf(fp, " return %s >= 0 && %s < %s ? %s[%s] : \"null\";\n", from, from, arr_len_name, arr_name, from); + fprintf(fp, "}"); + + fclose(fp); +} + +void create_table(char *file_name) { + // read from input file + char input_path[BUFFER_SIZE]; + sprintf(input_path, "%s/%s", CSV_PATH, file_name); + char from[BUFFER_SIZE], to[BUFFER_SIZE]; + char from_to_array[MAX_ARR_SIZE][BUFFER_SIZE] = {0}; + size_t max_index = read_csv(input_path, from, to, from_to_array); + + // create output .h file + char h_path[BUFFER_SIZE]; + sprintf(h_path, "%s/%s_to_%s.h", OUT_PATH, from, to); + create_h(h_path, from, to); + + // create output .c file + char c_path[BUFFER_SIZE]; + sprintf(c_path, "%s/%s_to_%s.c", OUT_PATH, from, to); + create_c(c_path, h_path, from, to, from_to_array, max_index); } int main() { - // make directory for csv header files - mkdir(HEADER_PATH, 0755); + // make directory for output files + mkdir(OUT_PATH, 0755); DIR *csv_dir = opendir(CSV_PATH); struct dirent *entry; while ((entry = readdir(csv_dir))) { char *file_name = entry->d_name; size_t file_name_len = strlen(file_name); - // create header for each *.csv file + // create lookup tables for each *.csv file if (file_name_len > EXT_LEN && strcmp(file_name + file_name_len - EXT_LEN, EXT) == 0) - create_header(entry->d_name); + create_table(entry->d_name); } closedir(csv_dir); + return 0; } -- cgit v1.2.3 From 5d463a7f4c721629a1996be6d19634338290a539 Mon Sep 17 00:00:00 2001 From: Mosder Date: Wed, 22 Jul 2026 01:16:45 +0200 Subject: refactor: generalized parser structs with offsetof and put them into separate files --- Makefile | 1 + analyzer/parser/fight_results/drif.h | 52 ++++ analyzer/parser/fight_results/entity.h | 67 +++++ analyzer/parser/fight_results/fight_results.c | 177 +++--------- analyzer/parser/fight_results/fight_results.h | 396 +------------------------- analyzer/parser/fight_results/gear.h | 45 +++ analyzer/parser/fight_results/gear_info.h | 44 +++ analyzer/parser/fight_results/orb.h | 33 +++ analyzer/parser/find_parse_functions.h | 6 +- analyzer/parser/parser.c | 3 +- analyzer/parser/parser_defs.h | 2 - analyzer/parser/structs.c | 49 ++++ analyzer/parser/structs.h | 33 +++ analyzer/render.c | 24 +- analyzer/render.h | 53 ++-- analyzer/utils/memory.h | 4 + analyzer/utils/roman.c | 13 +- analyzer/utils/roman.h | 23 +- 18 files changed, 429 insertions(+), 596 deletions(-) create mode 100644 analyzer/parser/fight_results/drif.h create mode 100644 analyzer/parser/fight_results/entity.h create mode 100644 analyzer/parser/fight_results/gear.h create mode 100644 analyzer/parser/fight_results/gear_info.h create mode 100644 analyzer/parser/fight_results/orb.h create mode 100644 analyzer/parser/structs.c create mode 100644 analyzer/parser/structs.h create mode 100644 analyzer/utils/memory.h (limited to 'analyzer/parser') diff --git a/Makefile b/Makefile index f45b9e6..31c1275 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ utils: parser: $(CC) $(CFLAGS) -c analyzer/parser/parser.c + $(CC) $(CFLAGS) -c analyzer/parser/structs.c $(CC) $(CFLAGS) -c analyzer/parser/fight_results/fight_results.c capture: 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; // drif + 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 index 4f0f3a1..e7c7123 100644 --- a/analyzer/parser/fight_results/fight_results.c +++ b/analyzer/parser/fight_results/fight_results.c @@ -8,6 +8,13 @@ #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 @@ -29,126 +36,20 @@ 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)); + 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; } -// 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; -} - 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'); + 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) { @@ -157,16 +58,19 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) 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)); + 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_GEARS); + gears_str = gears_separator + strlen(SEPARATOR_GEAR); Item item; - GearInfo gear_info = get_gear_info(gear.info, strlen(gear.info)); + 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); char buffer[SINGLE_ITEM_BUFFER_LEN]; char roman[5]; to_roman(roman, gear_info.rank); @@ -174,8 +78,11 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) 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); + if (atoi(gear.syng_tier)) { + char lvl[7]; + sprintf(lvl, " (%d)", gear_info.lvl); + strcat(buffer, lvl); + } break; default: { char stars[6]; @@ -185,7 +92,7 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) } } item.data = strdup(buffer); - destroy_gear_info(gear_info); + cleanup_struct(&gear_info, gear_info_fields, ARR_LEN(gear_info_fields)); switch (gear.type) { case TYPE_SYNG_NORMAL: @@ -212,10 +119,11 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) if (strlen(gear.orb)) { Item item; char buffer[SINGLE_ITEM_BUFFER_LEN]; - Orb orb = get_orb(gear.orb, strlen(gear.orb)); + Orb orb = {0}; + fill_struct(&orb, orb_fields, ARR_LEN(orb_fields), gear.orb, strlen(gear.orb), SEPARATOR_ORB_FIELD); sprintf(buffer, "%sorb %s", get_artifact_size_from_id(orb.size), get_orb_name_from_id(orb.id)); item.data = strdup(buffer); - destroy_orb(orb); + cleanup_struct(&orb, orb_fields, ARR_LEN(orb_fields)); item.type = ORB; // expand if needed if (results->num_items >= *items_capacity) @@ -223,47 +131,35 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) results->items[results->num_items++] = item; } - destroy_gear(gear); + cleanup_struct(&gear, gear_fields, ARR_LEN(gear_fields)); } 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)); + 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_DRIFS); + drifs_str = drifs_separator + strlen(SEPARATOR_DRIF); Item item; item.data = strdup(drif.name); item.type = DRIF; + cleanup_struct(&drif, drif_fields, ARR_LEN(drif_fields)); + // 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); } @@ -350,7 +246,9 @@ ParsedData parse_fight_results(byte_t *data, size_t length) { 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); + 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) { @@ -361,7 +259,8 @@ ParsedData parse_fight_results(byte_t *data, size_t length) { results->enemy_results[results->num_enemy++] = parse_to_enemy(entity_results); break; } - destroy_entity_results(entity_results); + + cleanup_struct(&entity_results, entity_fields, ARR_LEN(entity_fields)); // if there are still entities - modify data pointer and length if (entity_separator) { diff --git a/analyzer/parser/fight_results/fight_results.h b/analyzer/parser/fight_results/fight_results.h index 0618bf2..04add96 100644 --- a/analyzer/parser/fight_results/fight_results.h +++ b/analyzer/parser/fight_results/fight_results.h @@ -11,137 +11,12 @@ // 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 +// items 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 { @@ -149,273 +24,6 @@ typedef struct { 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; // drif - 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; diff --git a/analyzer/parser/fight_results/gear.h b/analyzer/parser/fight_results/gear.h new file mode 100644 index 0000000..66f52f3 --- /dev/null +++ b/analyzer/parser/fight_results/gear.h @@ -0,0 +1,45 @@ +// parser/fight_results/gear.h +// Gear generic struct data + +#include "parser/structs.h" + +// Gear structure: +// field (SEPARATOR_GEAR_FIELD field)* +// fields are defined in order in the Gear struct +#define SEPARATOR_GEAR "???" // TODO: change it to correct one which idk at this moment +#define SEPARATOR_GEAR_FIELD "$" + +// values of field "type" in Gear +#define TYPE_SYNG_NORMAL 0 +#define TYPE_SET 1 +#define TYPE_RARE 2 + +typedef struct { + char *stats; // strength, dexterity, power, knowledge, hp, mana, stamina + char *resistances; // slashing, crushing, piercing, fire, cold, energy, mental + char *info; // GearInfo struct data + char *reqs; // requirements (str, dex, pow, know), weight, something else + char *field5; + char *psycho; // psycho effects (id,%) for syngs + char *field7; + char *field8; + char *field9; + char *syng_tier; // lvl 20 - 1, lvl 30 - 2, etc..., null for non-syngs + int field11; + int field12; + int field13; + char *field14; + int type; // 0 - normal item or syng, 1 - set, 2 - rare + int field16; // 10 for rare, -1 for others ??? + char *orb; // orb info + char *field18; + char *field19; +} Gear; + +static const StructField gear_fields[] = { + {STRING, offsetof(Gear, stats)}, {STRING, offsetof(Gear, resistances)}, {STRING, offsetof(Gear, info)}, {STRING, offsetof(Gear, reqs)}, + {STRING, offsetof(Gear, field5)}, {STRING, offsetof(Gear, psycho)}, {STRING, offsetof(Gear, field7)}, {STRING, offsetof(Gear, field8)}, + {STRING, offsetof(Gear, field9)}, {STRING, offsetof(Gear, syng_tier)}, {INT, offsetof(Gear, field11)}, {INT, offsetof(Gear, field12)}, + {INT, offsetof(Gear, field13)}, {STRING, offsetof(Gear, field14)}, {INT, offsetof(Gear, type)}, {INT, offsetof(Gear, field16)}, + {STRING, offsetof(Gear, orb)}, {STRING, offsetof(Gear, field18)}, {STRING, offsetof(Gear, field19)}, +}; diff --git a/analyzer/parser/fight_results/gear_info.h b/analyzer/parser/fight_results/gear_info.h new file mode 100644 index 0000000..f0cf83a --- /dev/null +++ b/analyzer/parser/fight_results/gear_info.h @@ -0,0 +1,44 @@ +// parser/fight_results/gear_info.h +// GearInfo generic struct data + +#include "parser/structs.h" + +// GearInfo structure: +// field (SEPARATOR_GEAR_INFO_FIELD field)* +// fields are defined in order in the GearInfo struct +#define SEPARATOR_GEAR_INFO_FIELD "," + +typedef struct { + char *name; // item name + int field2; + int lvl; // level requirement + int price; // gold price value + int field5; + int field6; + int field7; + int rank; // I-XII + int field9; + int field10; + int field11; + int durability; // in % + char *field13; + int field14; + int field15; + float charges; // charge count + char *field17; + int field18; + int stars; // incrustation level: 0 - b1, 1 - b2, ... (-100 for syngs) + int field20; + int field21; + int field22; + int field23; +} GearInfo; + +static const StructField gear_info_fields[] = { + {STRING, offsetof(GearInfo, name)}, {INT, offsetof(GearInfo, field2)}, {INT, offsetof(GearInfo, lvl)}, {INT, offsetof(GearInfo, price)}, + {INT, offsetof(GearInfo, field5)}, {INT, offsetof(GearInfo, field6)}, {INT, offsetof(GearInfo, field7)}, {INT, offsetof(GearInfo, rank)}, + {INT, offsetof(GearInfo, field9)}, {INT, offsetof(GearInfo, field10)}, {INT, offsetof(GearInfo, field11)}, {INT, offsetof(GearInfo, durability)}, + {STRING, offsetof(GearInfo, field13)}, {INT, offsetof(GearInfo, field14)}, {INT, offsetof(GearInfo, field15)}, {FLOAT, offsetof(GearInfo, charges)}, + {STRING, offsetof(GearInfo, field17)}, {INT, offsetof(GearInfo, field18)}, {INT, offsetof(GearInfo, stars)}, {INT, offsetof(GearInfo, field20)}, + {INT, offsetof(GearInfo, field21)}, {INT, offsetof(GearInfo, field22)}, {INT, offsetof(GearInfo, field23)}, +}; diff --git a/analyzer/parser/fight_results/orb.h b/analyzer/parser/fight_results/orb.h new file mode 100644 index 0000000..dcf146f --- /dev/null +++ b/analyzer/parser/fight_results/orb.h @@ -0,0 +1,33 @@ +// parser/fight_results/orb.h +// Orb generic struct data + +#include "parser/structs.h" + +// Orb structure: +// field (SEPARATOR_ORB_FIELD field)* +// fields are defined in order in the Orb struct +#define SEPARATOR_ORB_FIELD "," + +typedef struct { + char *field1; // just says "orb" + int field2; + int field3; + char *field4; + int field5; + int id; // id of orb type + int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy + float value; // value in % you gain from the orb + char *field9; +} Orb; + +static const StructField orb_fields[] = { + {STRING, offsetof(Orb, field1)}, + {INT, offsetof(Orb, field2)}, + {INT, offsetof(Orb, field3)}, + {STRING, offsetof(Orb, field4)}, + {INT, offsetof(Orb, field5)}, + {INT, offsetof(Orb, id)}, + {INT, offsetof(Orb, size)}, + {FLOAT, offsetof(Orb, value)}, + {STRING, offsetof(Orb, field9)}, +}; diff --git a/analyzer/parser/find_parse_functions.h b/analyzer/parser/find_parse_functions.h index 1b6ce9e..e2c2acb 100644 --- a/analyzer/parser/find_parse_functions.h +++ b/analyzer/parser/find_parse_functions.h @@ -3,8 +3,4 @@ #include "parser/fight_results/fight_results.h" -#define RESULT_TYPE_COUNT 1 - -FindParseFunctions all_find_parse_functions[RESULT_TYPE_COUNT] = { - {.find_fn = find_fight_results, .parse_fn = parse_fight_results} -}; +FindParseFunctions all_find_parse_functions[] = {{find_fight_results, parse_fight_results}}; diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c index 3ef48b5..87ab4c0 100644 --- a/analyzer/parser/parser.c +++ b/analyzer/parser/parser.c @@ -3,6 +3,7 @@ #include #include "parser/find_parse_functions.h" +#include "utils/memory.h" // expand parsed data exponentially to fit el_count elements ParsedData *expand_parsed_data(ParsedData *parsed_data, size_t *capacity, size_t el_count) { @@ -14,7 +15,7 @@ ParsedData *expand_parsed_data(ParsedData *parsed_data, size_t *capacity, size_t // find earliest data to parse in stream byte_t *find_earliest_data(TCPStream *stream, int stream_offset, int *earliest_length, parse_function_t **earliest_parse_fun) { byte_t *earliest_data = NULL; - for (size_t i = 0; i < RESULT_TYPE_COUNT; i++) { + for (size_t i = 0; i < ARR_LEN(all_find_parse_functions); i++) { int length; byte_t *data = all_find_parse_functions[i].find_fn(stream, stream_offset, &length); // if it's the first found or earlier than earliest - save it diff --git a/analyzer/parser/parser_defs.h b/analyzer/parser/parser_defs.h index aa39a4c..75b0366 100644 --- a/analyzer/parser/parser_defs.h +++ b/analyzer/parser/parser_defs.h @@ -6,8 +6,6 @@ #ifndef PARSER_DEFS_H #define PARSER_DEFS_H -typedef enum { INT, FLOAT, DOUBLE, STRING } TypeEnum; - typedef enum { TYPE_FIGHT_RESULTS } ResultsEnum; // "generic" type for parsed data diff --git a/analyzer/parser/structs.c b/analyzer/parser/structs.c new file mode 100644 index 0000000..c59fa82 --- /dev/null +++ b/analyzer/parser/structs.c @@ -0,0 +1,49 @@ +#include "parser/structs.h" + +#include +#include +#include + +void fill_struct(void *obj, const StructField *fields, size_t n_fields, const char *data, size_t data_len, const char *sep) { + for (size_t i = 0; i < n_fields; i++) { + char *next_sep = memmem(data, data_len, sep, strlen(sep)); + size_t len = next_sep ? next_sep - data : data_len; + + char *str = malloc(len + 1); + memcpy(str, data, len); + str[len] = '\0'; + + // parse depending on variable type + void *p_field = (char *)obj + fields[i].offset; + switch (fields[i].type) { + case INT: + sscanf(str, "%d", (int *)p_field); + free(str); + break; + case FLOAT: + sscanf(str, "%f", (float *)p_field); + free(str); + break; + case DOUBLE: + sscanf(str, "%lf", (double *)p_field); + free(str); + break; + case STRING: + *(char **)p_field = str; + break; + } + + // update data pointer and length + if (next_sep) { + data_len -= next_sep - data + strlen(sep); + data = next_sep + strlen(sep); + } + } +} + +void cleanup_struct(void *obj, const StructField *fields, size_t n_fields) { + for (size_t i = 0; i < n_fields; i++) { + if (fields[i].type == STRING) + free(*(char **)((char *)obj + fields[i].offset)); + } +} diff --git a/analyzer/parser/structs.h b/analyzer/parser/structs.h new file mode 100644 index 0000000..023c19b --- /dev/null +++ b/analyzer/parser/structs.h @@ -0,0 +1,33 @@ +// parser/structs.h +// generic struct functionality for parsing + +#include + +#ifndef PARSER_STRUCTS_H +#define PARSER_STRUCTS_H + +typedef enum { INT, FLOAT, DOUBLE, STRING } TypeEnum; + +typedef struct { + TypeEnum type; + size_t offset; +} StructField; + +// fill generic struct with given data +// params: +// - obj - pointer to the struct to fill +// - fields - field types and offsets of the struct +// - n_fields - count of fields in the struct +// - data - data to fill the struct with +// - data_len - length of the data +// - sep - separator seperating the fields in data +extern void fill_struct(void *obj, const StructField *fields, size_t n_fields, const char *data, size_t data_len, const char *sep); + +// cleanup allocations inside generic struct +// params: +// - obj - pointer to the struct to cleanup +// - fields - field types and offsets of the struct +// - n_fields - count of fields in the struct +extern void cleanup_struct(void *obj, const StructField *fields, size_t n_fields); + +#endif diff --git a/analyzer/render.c b/analyzer/render.c index 0fdd334..5c4b2d0 100644 --- a/analyzer/render.c +++ b/analyzer/render.c @@ -63,14 +63,10 @@ void print_enemy_list(EnemyResults *results, size_t n_enemies) { } void print_column_names() { - char *names[TABLE_COLUMN_COUNT] = TABLE_COLUMN_NAMES; - int colors[TABLE_COLUMN_COUNT] = TABLE_COLUMN_COLORS; - size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; - printf(TABLE_LINE_START); - for (size_t i = 0; i < TABLE_COLUMN_COUNT; i++) { - print_center(names[i], colors[i], widths[i], ' '); - printf(i < TABLE_COLUMN_COUNT - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); + for (size_t i = 0; i < ARR_LEN(columns); i++) { + print_center(columns[i].name, columns[i].color, columns[i].width, ' '); + printf(i < ARR_LEN(columns) - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); } printf("\n"); } @@ -96,9 +92,7 @@ int item_type_to_color(ItemType type) { } void print_player_rewards(FriendlyResults results) { - char buffers[TABLE_COLUMN_COUNT][TABLE_LINE_WIDTH]; - size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; - int colors[TABLE_COLUMN_COUNT] = TABLE_COLUMN_COLORS; + char buffers[ARR_LEN(columns)][TABLE_LINE_WIDTH]; sprintf(buffers[0], "%s (%d)", results.name, results.level); sprintf(buffers[1], "%d", results.exp); @@ -120,11 +114,11 @@ void print_player_rewards(FriendlyResults results) { sprintf(buffers[5], "-"); printf(TABLE_LINE_START); - for (size_t i = 0; i < TABLE_COLUMN_COUNT; i++) { - char to_color[widths[i] + 1]; - sprintf(to_color, "%*s", (int)widths[i], buffers[i]); - print_color(to_color, colors[i]); - printf(i < TABLE_COLUMN_COUNT - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); + for (size_t i = 0; i < ARR_LEN(columns); i++) { + char to_color[columns[i].width + 1]; + sprintf(to_color, "%*s", (int)columns[i].width, buffers[i]); + print_color(to_color, columns[i].color); + printf(i < ARR_LEN(columns) - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); } printf("\n"); diff --git a/analyzer/render.h b/analyzer/render.h index 1015eb9..3604bde 100644 --- a/analyzer/render.h +++ b/analyzer/render.h @@ -2,6 +2,7 @@ // functions for "rendering" data onto the terminal #include "parser/fight_results/fight_results.h" +#include "utils/memory.h" // ANSI escape sequence colors #define COLOR_GOLD 33 @@ -23,40 +24,32 @@ #define TABLE_LINE_END_WIDTH (sizeof(TABLE_LINE_END) - 1) #define TABLE_LINE_SEPARATOR_WIDTH (sizeof(TABLE_LINE_SEPARATOR) - 1) -#define TABLE_COLUMN_COUNT 6 - -#define TABLE_NAME_LVL_WIDTH 17 -#define TABLE_EXP_WIDTH 7 -#define TABLE_GOLD_WIDTH 7 -#define TABLE_PSYCHO_WIDTH 6 -#define TABLE_SATURATION_WIDTH 13 -#define TABLE_SPLINTERS_WIDTH 9 +// column info +typedef struct { + char *name; + size_t width; + int color; +} ColumnInfo; +static const ColumnInfo columns[] = { + {"Nick (LVL)", 17, COLOR_WHITE}, + {"EXP", 7, COLOR_WHITE}, + {"Gold", 7, COLOR_YELLOW}, + {"Psycho", 6, COLOR_AQUA}, + {"Saturation", 13, COLOR_GREEN}, + {"Splinters", 9, COLOR_BLUE}, +}; // clang-format off -#define TABLE_COLUMN_NAMES {"Nick (LVL)", "EXP", "Gold", "Psycho", "Saturation", "Splinters"} -#define TABLE_COLUMN_COLORS {COLOR_WHITE, COLOR_WHITE, COLOR_YELLOW, COLOR_AQUA, COLOR_GREEN, COLOR_BLUE} -#define TABLE_COLUMN_WIDTHS { \ - TABLE_NAME_LVL_WIDTH, \ - TABLE_EXP_WIDTH, \ - TABLE_GOLD_WIDTH, \ - TABLE_PSYCHO_WIDTH, \ - TABLE_SATURATION_WIDTH, \ - TABLE_SPLINTERS_WIDTH, \ -} - -#define TABLE_LINE_WIDTH ( \ - TABLE_LINE_START_WIDTH + \ - TABLE_NAME_LVL_WIDTH + \ - TABLE_EXP_WIDTH + \ - TABLE_GOLD_WIDTH + \ - TABLE_PSYCHO_WIDTH + \ - TABLE_SATURATION_WIDTH + \ - TABLE_SPLINTERS_WIDTH + \ - (TABLE_COLUMN_COUNT - 1) * TABLE_LINE_SEPARATOR_WIDTH + \ - TABLE_LINE_END_WIDTH \ -) +#define SUM_COL_WIDTHS ({ \ + size_t sum = 0; \ + for (size_t i = 0; i < ARR_LEN(columns); i++) \ + sum += columns[i].width; \ + sum; \ +}) // clang-format on +#define TABLE_LINE_WIDTH (TABLE_LINE_START_WIDTH + SUM_COL_WIDTHS + (ARR_LEN(columns) - 1) * TABLE_LINE_SEPARATOR_WIDTH + TABLE_LINE_END_WIDTH) + #define TABLE_ITEMS_WIDTH (TABLE_LINE_WIDTH - TABLE_LINE_START_WIDTH - TABLE_LINE_END_WIDTH) // render fight results onto the terminal diff --git a/analyzer/utils/memory.h b/analyzer/utils/memory.h new file mode 100644 index 0000000..1de58b9 --- /dev/null +++ b/analyzer/utils/memory.h @@ -0,0 +1,4 @@ +// utils/memory.h +// helper functions for dealing with memory management + +#define ARR_LEN(arr) (sizeof(arr) / sizeof(arr[0])) diff --git a/analyzer/utils/roman.c b/analyzer/utils/roman.c index 9ea2bbb..4fbe5c0 100644 --- a/analyzer/utils/roman.c +++ b/analyzer/utils/roman.c @@ -1,16 +1,15 @@ #include "utils/roman.h" -#include #include +#include "utils/memory.h" + void to_roman(char *buffer, int number) { buffer[0] = '\0'; - int nums[ROMAN_CHAR_COUNT] = ROMAN_NUMS; - char *chars[ROMAN_CHAR_COUNT] = ROMAN_CHARS; - for (size_t i = 0; i < ROMAN_CHAR_COUNT; i++) { - for (size_t j = 0; j < number / nums[i]; j++) { - strcat(buffer, chars[i]); + for (size_t i = 0; i < ARR_LEN(roman_symbols); i++) { + for (size_t j = 0; j < number / roman_symbols[i].num; j++) { + strcat(buffer, roman_symbols[i].symbol); } - number %= nums[i]; + number %= roman_symbols[i].num; } } diff --git a/analyzer/utils/roman.h b/analyzer/utils/roman.h index b4f5d3f..cebd16e 100644 --- a/analyzer/utils/roman.h +++ b/analyzer/utils/roman.h @@ -1,9 +1,26 @@ // utils/roman.h // functions for roman numbers -#define ROMAN_CHAR_COUNT 13 -#define ROMAN_NUMS {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1} -#define ROMAN_CHARS {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"} +typedef struct { + int num; + char *symbol; +} RomanSymbol; + +static const RomanSymbol roman_symbols[] = { + {1000, "M"}, + {900, "CM"}, + {500, "D"}, + {400, "CD"}, + {100, "C"}, + {90, "XC"}, + {50, "L"}, + {40, "XL"}, + {10, "X"}, + {9, "IX"}, + {5, "V"}, + {4, "IV"}, + {1, "I"}, +}; // convert a arabic number to roman number // params: -- cgit v1.2.3 From 8317c3b33b9b1c70e5ae4c87c7efe33d9b9ce09a Mon Sep 17 00:00:00 2001 From: Mosder Date: Thu, 23 Jul 2026 17:35:04 +0200 Subject: feat: added helper functions for dealing with memory management and refactored the codebase to use them --- Makefile | 1 + analyzer/parser/fight_results/fight_results.c | 109 +++++++++++--------------- analyzer/parser/fight_results/fight_results.h | 6 +- analyzer/parser/parser.c | 16 +--- analyzer/parser/structs.c | 4 +- analyzer/structures/hashmap.c | 12 +-- analyzer/structures/heap.c | 10 ++- analyzer/utils/memory.c | 62 +++++++++++++++ analyzer/utils/memory.h | 42 ++++++++++ analyzer/utils/tcp.c | 46 +++++------ 10 files changed, 195 insertions(+), 113 deletions(-) create mode 100644 analyzer/utils/memory.c (limited to 'analyzer/parser') diff --git a/Makefile b/Makefile index fa3a294..cc5cb87 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ utils: $(CC) $(CFLAGS) -c analyzer/utils/tcp.c $(CC) $(CFLAGS) -c analyzer/utils/tcp_seq_math.c $(CC) $(CFLAGS) -c analyzer/utils/roman.c + $(CC) $(CFLAGS) -c analyzer/utils/memory.c parser: $(CC) $(CFLAGS) -c analyzer/parser/parser.c diff --git a/analyzer/parser/fight_results/fight_results.c b/analyzer/parser/fight_results/fight_results.c index e7c7123..efc4b36 100644 --- a/analyzer/parser/fight_results/fight_results.c +++ b/analyzer/parser/fight_results/fight_results.c @@ -30,6 +30,8 @@ void cleanup_fight_results(void *data) { 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) { @@ -41,12 +43,6 @@ byte_t *find_fight_results(TCPStream *stream, int offset, int *length) { return start; } -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 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); @@ -67,10 +63,11 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) if (gears_separator) gears_str = gears_separator + strlen(SEPARATOR_GEAR); - Item item; - + // 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); @@ -91,9 +88,9 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) break; } } - item.data = strdup(buffer); - cleanup_struct(&gear_info, gear_info_fields, ARR_LEN(gear_info_fields)); + // 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 @@ -109,26 +106,22 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) item.type = EPIC; break; } + results->items = array_append(results->items, sizeof(Item), &results->num_items, items_capacity, &item); - // 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; + cleanup_struct(&gear_info, gear_info_fields, ARR_LEN(gear_info_fields)); // add orb to items if it exists if (strlen(gear.orb)) { - Item item; - char buffer[SINGLE_ITEM_BUFFER_LEN]; 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.data = strdup(buffer); + + 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)); - 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; } cleanup_struct(&gear, gear_fields, ARR_LEN(gear_fields)); @@ -150,16 +143,10 @@ void add_drifs(FriendlyResults *results, size_t *items_capacity, char *drifs_str if (drifs_separator) drifs_str = drifs_separator + strlen(SEPARATOR_DRIF); - Item item; - item.data = strdup(drif.name); - item.type = 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)); - - // 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 (drifs_separator); } @@ -180,37 +167,31 @@ void add_items(FriendlyResults *results, size_t *items_capacity, char *items_str 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; + 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; - - 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; + 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.num_items = 0; - results.items = malloc(INIT_ITEMS_CAPACITY * sizeof(Item)); + 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); - results.saturation = 0; if (strlen(entity_results.saturation) > 0) { int saturation_id; sscanf(entity_results.saturation, "%d,%d", &saturation_id, &results.saturation); @@ -222,25 +203,26 @@ FriendlyResults parse_to_friendly(EntityResults entity_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; - + 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; - parsed_data.data_type = TYPE_FIGHT_RESULTS; + 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 = malloc(sizeof(FightResults)); + 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 { @@ -252,12 +234,16 @@ ParsedData parse_fight_results(byte_t *data, size_t 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); + 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: - results->enemy_results[results->num_enemy++] = parse_to_enemy(entity_results); + } + 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)); @@ -270,6 +256,5 @@ ParsedData parse_fight_results(byte_t *data, size_t length) { } 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 index 04add96..ec90394 100644 --- a/analyzer/parser/fight_results/fight_results.h +++ b/analyzer/parser/fight_results/fight_results.h @@ -44,14 +44,14 @@ typedef struct { int level; } EnemyResults; -#define MAX_ENTITIES_PER_SIDE 8 +#define INIT_ENTITIES_PER_SIDE 6 // entire fight resutls typedef struct { size_t num_friendly; - FriendlyResults friendly_results[MAX_ENTITIES_PER_SIDE]; + FriendlyResults *friendly_results; size_t num_enemy; - EnemyResults enemy_results[MAX_ENTITIES_PER_SIDE]; + EnemyResults *enemy_results; } FightResults; extern find_function_t find_fight_results; diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c index 87ab4c0..d00cd97 100644 --- a/analyzer/parser/parser.c +++ b/analyzer/parser/parser.c @@ -5,13 +5,6 @@ #include "parser/find_parse_functions.h" #include "utils/memory.h" -// expand parsed data exponentially to fit el_count elements -ParsedData *expand_parsed_data(ParsedData *parsed_data, size_t *capacity, size_t el_count) { - while (*capacity < el_count) - *capacity <<= 1; - return realloc(parsed_data, *capacity * sizeof(ParsedData)); -} - // find earliest data to parse in stream byte_t *find_earliest_data(TCPStream *stream, int stream_offset, int *earliest_length, parse_function_t **earliest_parse_fun) { byte_t *earliest_data = NULL; @@ -30,7 +23,7 @@ byte_t *find_earliest_data(TCPStream *stream, int stream_offset, int *earliest_l ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data) { size_t parsed_data_capacity = INIT_PARSED_DATA_CAPACITY; - ParsedData *parsed_data = malloc(parsed_data_capacity * sizeof(ParsedData)); + ParsedData *parsed_data = safe_malloc(parsed_data_capacity * sizeof(ParsedData)); *n_parsed_data = 0; int stream_offset = 0; @@ -50,11 +43,8 @@ ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed // if data to parse was found, parse it and update the offset if (earliest_data) { - // if no capacity in parsed data array - expand it - if (*n_parsed_data >= parsed_data_capacity) - parsed_data = expand_parsed_data(parsed_data, &parsed_data_capacity, *n_parsed_data + 1); - - parsed_data[(*n_parsed_data)++] = earliest_parse_fun(earliest_data, earliest_length); + ParsedData new_parsed = earliest_parse_fun(earliest_data, earliest_length); + parsed_data = array_append(parsed_data, sizeof(ParsedData), n_parsed_data, &parsed_data_capacity, &new_parsed); stream_offset = earliest_data - stream->data + earliest_length; } } while (earliest_data); diff --git a/analyzer/parser/structs.c b/analyzer/parser/structs.c index c59fa82..dedf0d1 100644 --- a/analyzer/parser/structs.c +++ b/analyzer/parser/structs.c @@ -4,12 +4,14 @@ #include #include +#include "utils/memory.h" + void fill_struct(void *obj, const StructField *fields, size_t n_fields, const char *data, size_t data_len, const char *sep) { for (size_t i = 0; i < n_fields; i++) { char *next_sep = memmem(data, data_len, sep, strlen(sep)); size_t len = next_sep ? next_sep - data : data_len; - char *str = malloc(len + 1); + char *str = safe_malloc(len + 1); memcpy(str, data, len); str[len] = '\0'; diff --git a/analyzer/structures/hashmap.c b/analyzer/structures/hashmap.c index 84cc289..5eb27ae 100644 --- a/analyzer/structures/hashmap.c +++ b/analyzer/structures/hashmap.c @@ -3,6 +3,8 @@ #include #include +#include "utils/memory.h" + HashMap *hashmap_new( size_t key_size, size_t value_size, @@ -11,9 +13,9 @@ HashMap *hashmap_new( void (*key_cleanup)(void *key), void (*value_cleanup)(void *key) ) { - HashMap *map = malloc(sizeof(HashMap)); + HashMap *map = safe_malloc(sizeof(HashMap)); // initialize NULLs - memset(map->table, 0, sizeof(HashMapNode *) * HASHMAP_TABLE_SIZE); + memset(map->table, 0, HASHMAP_TABLE_SIZE * sizeof(HashMapNode *)); map->key_size = key_size; map->value_size = value_size; map->hash = hash; @@ -42,9 +44,9 @@ void hashmap_put(HashMap *map, void *key, void *value) { } // if it doesn't exist - create new node - HashMapNode *new_node = malloc(sizeof(HashMapNode)); - new_node->key = malloc(map->key_size); - new_node->value = malloc(map->value_size); + HashMapNode *new_node = safe_malloc(sizeof(HashMapNode)); + new_node->key = safe_malloc(map->key_size); + new_node->value = safe_malloc(map->value_size); memcpy(new_node->key, key, map->key_size); memcpy(new_node->value, value, map->value_size); diff --git a/analyzer/structures/heap.c b/analyzer/structures/heap.c index 6102940..1267668 100644 --- a/analyzer/structures/heap.c +++ b/analyzer/structures/heap.c @@ -3,9 +3,11 @@ #include #include +#include "utils/memory.h" + Heap *heap_new(size_t element_size, int (*compare)(void *element1, void *element2), void (*element_cleanup)(void *element)) { - Heap *heap = malloc(sizeof(Heap)); - heap->data = malloc(INIT_HEAP_CAPACITY * sizeof(void *)); + Heap *heap = safe_malloc(sizeof(Heap)); + heap->data = safe_malloc(INIT_HEAP_CAPACITY * sizeof(void *)); heap->n_elements = 0; heap->element_size = element_size; heap->capacity = INIT_HEAP_CAPACITY; @@ -40,7 +42,7 @@ void swap(void **p_el1, void **p_el2) { void expand_heap(Heap *heap, size_t el_count) { while (heap->capacity < el_count) heap->capacity = (heap->capacity << 1) | 1; - heap->data = realloc(heap->data, heap->capacity * sizeof(void *)); + heap->data = safe_realloc(heap->data, heap->capacity * sizeof(void *)); } void heap_insert(Heap *heap, void *element) { @@ -50,7 +52,7 @@ void heap_insert(Heap *heap, void *element) { // insert into the last position size_t i = heap->n_elements; - heap->data[i] = malloc(heap->element_size); + heap->data[i] = safe_malloc(heap->element_size); memcpy(heap->data[i], element, heap->element_size); heap->n_elements++; diff --git a/analyzer/utils/memory.c b/analyzer/utils/memory.c new file mode 100644 index 0000000..0a43bd3 --- /dev/null +++ b/analyzer/utils/memory.c @@ -0,0 +1,62 @@ +#include "memory.h" + +#include +#include +#include +#include + +void *safe_malloc(size_t size) { + for (size_t i = 0; i < MAX_ALLOC_ATTEMPTS; i++) { + void *p = malloc(size); + if (p) + return p; + if (i < MAX_ALLOC_ATTEMPTS - 1) { + printf("Failed malloc. Retrying in 1s...\n"); + sleep(1); + } + } + + printf("ERROR: Failed malloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS); + exit(EXIT_FAILURE); +} + +void *safe_realloc(void *ptr, size_t size) { + if (!ptr) + return safe_malloc(size); + + for (size_t i = 0; i < MAX_ALLOC_ATTEMPTS; i++) { + void *p = realloc(ptr, size); + if (p) + return p; + if (i < MAX_ALLOC_ATTEMPTS - 1) { + printf("Failed realloc. Retrying in 1s...\n"); + sleep(1); + } + } + + printf("ERROR: Failed realloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS); + exit(EXIT_FAILURE); +} + +// expand an array +// returns pointer to the expanded array +void *array_expand(void *arr, size_t el_size, size_t *cap, size_t to_fit) { + while (*cap < to_fit) + *cap *= 2; + return safe_realloc(arr, *cap * el_size); +} + +void *array_append(void *arr, size_t el_size, size_t *len, size_t *cap, void *val) { + if (*len >= *cap) + arr = array_expand(arr, el_size, cap, *len + 1); + memcpy((char *)arr + (*len)++ * el_size, val, el_size); + return arr; +} + +void *array_concat(void *arr1, size_t el_size, size_t *len1, size_t *cap, void *arr2, size_t len2) { + if (*len1 + len2 > *cap) + arr1 = array_expand(arr1, el_size, cap, *len1 + len2); + memcpy((char *)arr1 + *len1 * el_size, arr2, len2 * el_size); + *len1 += len2; + return arr1; +} diff --git a/analyzer/utils/memory.h b/analyzer/utils/memory.h index 1de58b9..0973623 100644 --- a/analyzer/utils/memory.h +++ b/analyzer/utils/memory.h @@ -1,4 +1,46 @@ // utils/memory.h // helper functions for dealing with memory management +#include + #define ARR_LEN(arr) (sizeof(arr) / sizeof(arr[0])) + +#define MAX_ALLOC_ATTEMPTS 3 + +// tries malloc MAX_ALLOC_ATTEMPTS times, if all fail - exits the program +// params: +// - size - count of bytes to allocate +// returns: +// pointer to the allocated memory +extern void *safe_malloc(size_t size); + +// tries realloc MAX_ALLOC_ATTEMPTS times, if all fail - exits the program +// params: +// - ptr - pointer to the memory to reallocate +// - size - new size of the memory +// returns: +// pointer to the reallocated memory +extern void *safe_realloc(void *ptr, size_t size); + +// append to array, expand if necessary +// params: +// - arr - array to append to +// - el_size - size of an array element +// - len - pointer to the length of the array +// - cap - pointer to the capacity of the array +// - val - pointer to the value to append to the array +// returns: +// new pointer to the array (different if it was realloced) +extern void *array_append(void *arr, size_t el_size, size_t *len, size_t *cap, void *val); + +// concatenate 2 arrays, expand if necessary +// params: +// - arr1 - first array (destination) +// - el_size - size of an array element +// - len1 - pointer to the length of arr1 +// - cap - pointer to the capacity of arr1 +// - arr2 - second array +// - len2 - length of arr2 +// returns: +// new pointer to the array (different if it was realloced) +extern void *array_concat(void *arr1, size_t el_size, size_t *len1, size_t *cap, void *arr2, size_t len2); diff --git a/analyzer/utils/tcp.c b/analyzer/utils/tcp.c index b925ecf..6309e64 100644 --- a/analyzer/utils/tcp.c +++ b/analyzer/utils/tcp.c @@ -6,15 +6,19 @@ #include #include +#include "utils/memory.h" #include "utils/tcp_seq_math.h" // initalize default TCP segment TCPSegment new_segment() { - TCPSegment segment; - segment.payload = NULL; - segment.len = 0; - segment.seq = 0; - segment.port = 0; + // clang-format off + TCPSegment segment = { + .payload = NULL, + .len = 0, + .seq = 0, + .port = 0 + }; + // clang-format on return segment; } @@ -61,19 +65,22 @@ TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthd // get payload int total_header_len = eth_header_len + ip_header_len + tcp_header_len; segment.len = packet_data->caplen - total_header_len; - segment.payload = malloc(segment.len); + segment.payload = safe_malloc(segment.len); memcpy(segment.payload, packet + total_header_len, segment.len); return segment; } TCPStream new_stream() { - TCPStream stream; - stream.data = malloc(INIT_STREAM_CAPACITY); - stream.len = 0; - stream.capacity = INIT_STREAM_CAPACITY; - stream.seq = 0; - stream.port = 0; - stream.pending = NULL; + // clang-format off + TCPStream stream = { + .data = safe_malloc(INIT_STREAM_CAPACITY), + .len = 0, + .capacity = INIT_STREAM_CAPACITY, + .seq = 0, + .port = 0, + .pending = NULL + }; + // clang-format on return stream; } @@ -107,20 +114,9 @@ int in_acceptable_range(TCPStream stream, TCPSegment segment) { return seq_in_range_ei(segment.seq, stream_expected, max_acceptable_seq); } -// expand stream capacity exponentially to fit new_len -void expand_stream_capacity(TCPStream *stream, size_t new_len) { - while (stream->capacity < new_len) - stream->capacity <<= 1; - stream->data = realloc(stream->data, stream->capacity); -} - // add segment directly to stream void add_segment(TCPStream *stream, TCPSegment segment, size_t data_start) { - size_t new_stream_len = stream->len + segment.len - data_start; - if (new_stream_len > stream->capacity) - expand_stream_capacity(stream, new_stream_len); - memcpy(stream->data + stream->len, segment.payload + data_start, segment.len - data_start); - stream->len = new_stream_len; + stream->data = array_concat(stream->data, 1, &stream->len, &stream->capacity, segment.payload + data_start, segment.len - data_start); } // add pending segments to the stream -- cgit v1.2.3