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/parser.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 analyzer/parser/parser.h (limited to 'analyzer/parser/parser.h') 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/parser.h') 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/parser.h') 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 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/parser.h') 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 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/parser.h') 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