From d62be280a151883866554e9ea00a5efed2fa593a Mon Sep 17 00:00:00 2001 From: Mosder Date: Wed, 17 Jun 2026 22:55:55 +0200 Subject: feat: added simple reading of packets from file and printing the payloads. TODO: utilize TCP sequence number to make sure the stream is read in order and parse the packets without reassemblying the entire stream (which might be enormous), but keeping in mind that entire fight result might be accross multiple segments --- analyzer/utils/pcap.c | 36 ++++++++++++++++++++++++++++++++++++ analyzer/utils/pcap.h | 13 +++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 analyzer/utils/pcap.c create mode 100644 analyzer/utils/pcap.h (limited to 'analyzer/utils') diff --git a/analyzer/utils/pcap.c b/analyzer/utils/pcap.c new file mode 100644 index 0000000..cdefba7 --- /dev/null +++ b/analyzer/utils/pcap.c @@ -0,0 +1,36 @@ +#include "pcap.h" + +#include +#include +#include + +const unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len) { + // 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 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; +} diff --git a/analyzer/utils/pcap.h b/analyzer/utils/pcap.h new file mode 100644 index 0000000..d31b961 --- /dev/null +++ b/analyzer/utils/pcap.h @@ -0,0 +1,13 @@ +// utils/pcap.h +// helper functions for usage of libpcap + +#include + +// 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 +// returns: +// pointer to the payload or NULL if not a correct packet +extern const unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len); -- cgit v1.2.3 From f1d1feabec428bf9ffc2c786d28ce7cc95725d48 Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 6 Jul 2026 09:47:09 +0200 Subject: fix: fixed includes in utils/fs.c. also YAGNI --- analyzer/utils/fs.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'analyzer/utils') diff --git a/analyzer/utils/fs.c b/analyzer/utils/fs.c index 76fb581..2dd8191 100644 --- a/analyzer/utils/fs.c +++ b/analyzer/utils/fs.c @@ -1,8 +1,9 @@ -#include "utils.h" +#include "fs.h" -#include +#include +#include +#include #include -#include void get_home_dir(char *home_dir) { strcpy(home_dir, getenv("HOME")); @@ -35,9 +36,3 @@ void create_necessary_directories() { mkdir(capture_dir, 0755); mkdir(saves_dir, 0755); } - -void write_file(char *path, void *contents, int contents_len, int flags, mode_t permissions) { - int fd = open(path, flags, permissions); - write(fd, contents, contents_len); - close(fd); -} -- cgit v1.2.3 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. --- Makefile | 6 ++- analyzer/analyzer.c | 16 +++---- analyzer/parser/fight_results.c | 18 +++++++ analyzer/parser/fight_results.h | 102 ++++++++++++++++++++++++++++++++++++++++ analyzer/parser/parser.c | 15 ++++++ analyzer/parser/parser.h | 21 +++++++++ analyzer/utils/pcap.c | 5 +- analyzer/utils/pcap.h | 3 +- 8 files changed, 173 insertions(+), 13 deletions(-) create mode 100644 analyzer/parser/fight_results.c create mode 100644 analyzer/parser/fight_results.h create mode 100644 analyzer/parser/parser.c create mode 100644 analyzer/parser/parser.h (limited to 'analyzer/utils') diff --git a/Makefile b/Makefile index d0a760e..f298b4d 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ global: compile local: compile mv branal ${HOME}/.local/bin -compile: utils capture analyzer +compile: utils capture analyzer parser gcc *.o main.c -lpcap -o branal rm *.o @@ -19,4 +19,8 @@ capture: analyzer: gcc -c analyzer/analyzer.c +parser: + gcc -c analyzer/parser/parser.c + gcc -c analyzer/parser/fight_results.c + .PHONY: analyzer diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index 7790f41..acbd5de 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -1,7 +1,6 @@ #include "analyzer.h" -#include - +#include "parser/parser.h" #include "utils/pcap.h" void analyze_file(char *path) { @@ -22,14 +21,11 @@ void analyze_file(char *path) { // read packets from the file while (pcap_next_ex(handle, &packet_data, &packet) == 1) { int payload_len; - payload = get_payload_from_packet(packet, packet_data, &payload_len); - if (payload_len > 0) { - printf("\n----------------\n"); - printf("Payload length: %d\n", payload_len); - printf("Payload:\n"); - fflush(stdout); - write(STDOUT_FILENO, payload, 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); } pcap_close(handle); diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c new file mode 100644 index 0000000..c6ad7cc --- /dev/null +++ b/analyzer/parser/fight_results.c @@ -0,0 +1,18 @@ +#include "fight_results.h" + +#include +#include + +char *find_fight_results(char *stream, int stream_len, int *length) { + char *start = memmem(stream, stream_len, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); + char *end = memmem(stream, stream_len, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); + *length = start != NULL && end != NULL ? end - start + strlen(FIGHT_RESULTS_END) : -1; + return start; +} + +FightResults parse_fight_results(char *data, int length) { + // TODO: actually parse that data + printf("len=%d\n%.*s\n----------------\n", length, length, data); + FightResults tmp; + return tmp; +} diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h new file mode 100644 index 0000000..270aba8 --- /dev/null +++ b/analyzer/parser/fight_results.h @@ -0,0 +1,102 @@ +// parser/fight_results.h +// utilities for parsing the fight results data + +// Payload structure: +// FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END +// +// EntityResults structure: +// field (SEPARATOR_RESULT field)* +// fields are defined in order in the EntityResults struct + +#define FIGHT_RESULTS_START "3;19;" +#define FIGHT_RESULTS_END "[--];" +#define SEPARATOR_ENTITY "[--]" +#define SEPARATOR_RESULT "&" + +typedef struct { + int team; // friend or enemy + char *name; + int exp; + float field4; + int gold; + float field6; + char *field7; + char *gear; // rares, sets, syngs, orbs + int field9; + char *items; // regular items + int field11; + int field12; + float field13; + float field14; // some multiplier most likely exp/gold/psycho/something else idk + float field15; + int level; + double field17; + float field18; + int field19; + int field20; + float field21; + float field22; + int field23; + int field24; + int psycho; + char *drifs; + int splinters; + int field28; + char *field29; + int field30; + int field31; // possibly information who is the player - 1 for current player, 0 for others (need to double check) + int field32; + int field33; + char *field34; + char *saturation; +} EntityResults; + +// values of field "team" +#define TEAM_FRIEND 1 +#define TEAM_ENEMY 2 + +// fight results data for friendly +typedef struct { + char *name; + int exp; + int gold; + char *gear; + char *items; + int level; + int psycho; + char *drifs; + int splinters; + char *saturation_type; + int saturation; +} FriendlyResults; + +// fight results data for enemy +typedef struct { + char *name; + int level; +} EnemyResults; + +// entire fight resutls +typedef struct { + int num_friendly; + FriendlyResults *friendly_results; + int num_enemy; + EnemyResults *enemy_results; +} FightResults; + +// find the first fight results data in stream +// params: +// - stream - stream to find the fight results in +// - stream_len - length of the stream +// - length - length of the fight results data or -1 if there's no end yet +// returns: +// pointer to the beggining of fight results data or NULL if there's none +extern char *find_fight_results(char *stream, int stream_len, int *length); + +// parse given fight results +// params: +// - data - fight results data to parse +// - length - length of fight results data +// returns: +// FightResults struct containing parsed data +extern FightResults parse_fight_results(char *data, int length); diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c new file mode 100644 index 0000000..b551d8e --- /dev/null +++ b/analyzer/parser/parser.c @@ -0,0 +1,15 @@ +#include "parser.h" + +#include + +ParsedData *parse_stream(char *stream, int stream_len, int *parsed_len) { + // TODO: Need to do parsing of the entire thing (loop) and return all of the parsed data + if (stream_len > 0) { + int length; + char *start = find_fight_results(stream, stream_len, &length); + if (start != NULL) { + parse_fight_results(start, length); + } + } + return NULL; +} diff --git a/analyzer/parser/parser.h b/analyzer/parser/parser.h new file mode 100644 index 0000000..18b4b83 --- /dev/null +++ b/analyzer/parser/parser.h @@ -0,0 +1,21 @@ +// parser/parser.h +// main parser file + +#include "fight_results.h" + +// "generic" type for parsed data +typedef struct { + enum { FIGHT_RESULTS } data_type; + union { + FightResults fight_results; + } data; +} ParsedData; + +// parse a stream of TCP data +// params: +// - stream - the stream to parse +// - stream_len - length of the stream to parse +// - parsed_len - pointer to inform how much of the stream was parsed (not all if e.g. start of data is there, but the end isn't yet) +// returns: +// pointer to the array of parsed data +extern ParsedData *parse_stream(char *stream, int stream_len, int *parsed_len); diff --git a/analyzer/utils/pcap.c b/analyzer/utils/pcap.c index cdefba7..5e9b611 100644 --- a/analyzer/utils/pcap.c +++ b/analyzer/utils/pcap.c @@ -4,7 +4,7 @@ #include #include -const unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len) { +const unsigned char *get_payload_from_packet(const unsigned char *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); @@ -29,6 +29,9 @@ const unsigned char *get_payload_from_packet(const unsigned char *packet, const 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; diff --git a/analyzer/utils/pcap.h b/analyzer/utils/pcap.h index d31b961..16da7cd 100644 --- a/analyzer/utils/pcap.h +++ b/analyzer/utils/pcap.h @@ -8,6 +8,7 @@ // - 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 unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len); +extern const unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq); -- cgit v1.2.3 From d0d8d158f856ea4a6bfb1da25ec210f9516f67df Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 6 Jul 2026 14:14:59 +0200 Subject: fix: YAGNI cont. --- analyzer/utils/fs.h | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'analyzer/utils') diff --git a/analyzer/utils/fs.h b/analyzer/utils/fs.h index 6335799..8083ddc 100644 --- a/analyzer/utils/fs.h +++ b/analyzer/utils/fs.h @@ -1,8 +1,6 @@ // utils/fs.h // functions for operating on the file system, i.e. creating directories, writing files, etc. -#include - // get main directory used by the program // params: // - main_dir - buffer to save the directory to @@ -20,12 +18,3 @@ extern void get_saves_dir(char *saves_dir); // create directories that will be used by the program extern void create_necessary_directories(); - -// writes file to the specified path -// params: -// - path - path to write the file to -// - contents - contents of the file -// - contents_len - amount of bytes of contents to write -// - flags - flags to open the file with -// - permissions - permissions of the written file -extern void write_file(char *path, void *contents, int contents_len, int flags, mode_t permissions); -- cgit v1.2.3 From f85d1a4012487411c4742262d48ec0d62b99f8b5 Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 6 Jul 2026 22:13:07 +0200 Subject: feat: created a simple hashmap implementation --- analyzer/utils/hashmap.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++ analyzer/utils/hashmap.h | 83 +++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 analyzer/utils/hashmap.c create mode 100644 analyzer/utils/hashmap.h (limited to 'analyzer/utils') diff --git a/analyzer/utils/hashmap.c b/analyzer/utils/hashmap.c new file mode 100644 index 0000000..8af7630 --- /dev/null +++ b/analyzer/utils/hashmap.c @@ -0,0 +1,126 @@ +#include "hashmap.h" + +#include +#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)); + for (size_t i = 0; i < HASHMAP_TABLE_SIZE; i++) + map->table[i] = NULL; + 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); +} + +size_t hash_uint32(void *key) { + // MurmurHash3 + uint32_t k = *(uint32_t *)key; + k ^= k >> 16; + k *= 0x85ebca6b; + k ^= k >> 13; + k *= 0xc2b2ae35; + k ^= k >> 16; + return k % HASHMAP_TABLE_SIZE; +} + +int compare_uint32(void *key1, void *key2) { + uint32_t *k1 = key1; + uint32_t *k2 = key2; + return *k1 == *k2; +} diff --git a/analyzer/utils/hashmap.h b/analyzer/utils/hashmap.h new file mode 100644 index 0000000..96e8b58 --- /dev/null +++ b/analyzer/utils/hashmap.h @@ -0,0 +1,83 @@ +// utils/hashmap.h +// a simple implementation of hashmap using linked list + +#include + +#define HASHMAP_TABLE_SIZE 1024 + +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); + +// hash uint32_t key +extern size_t hash_uint32(void *key); + +// compare uint32_t keys +extern int compare_uint32(void *key1, void *key2); -- cgit v1.2.3 From 9c7b6c0800434aa9e320e547605ca77bd84eeb71 Mon Sep 17 00:00:00 2001 From: Mosder Date: Thu, 9 Jul 2026 16:51:16 +0200 Subject: feat: added header definitions for functions handling the stream reassembly in utils/pcap.h --- analyzer/utils/pcap.c | 18 ++++++++++++++++++ analyzer/utils/pcap.h | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) (limited to 'analyzer/utils') diff --git a/analyzer/utils/pcap.c b/analyzer/utils/pcap.c index 5e9b611..d9687b3 100644 --- a/analyzer/utils/pcap.c +++ b/analyzer/utils/pcap.c @@ -37,3 +37,21 @@ const unsigned char *get_payload_from_packet(const unsigned char *packet, const *payload_len = packet_data->caplen - total_header_len; return packet + total_header_len; } + +PendingPayload new_pending(const unsigned char *payload, int payload_len) { + // TODO: impement + PendingPayload tmp; + return tmp; +} + +unsigned char *free_parsed_data(unsigned char *stream, uint32_t stream_seq, int stream_len, int parsed_len) { + // TODO: impement + return NULL; +} + +int add_segment_to_stream( + unsigned char *stream, uint32_t stream_seq, int stream_len, const unsigned char *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 index 16da7cd..a423083 100644 --- a/analyzer/utils/pcap.h +++ b/analyzer/utils/pcap.h @@ -1,8 +1,10 @@ // utils/pcap.h -// helper functions for usage of libpcap +// helper functions for usage of libpcap and for handling TCP segments in general #include +#include "hashmap.h" + // get payload from a TCP/IP packet // params: // - packet - captured packet @@ -12,3 +14,42 @@ // returns: // pointer to the payload or NULL if not a correct packet extern const unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq); + +// struct to persistently store pending payloads +typedef struct { + unsigned char *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 unsigned char *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 unsigned char *free_parsed_data(unsigned char *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( + unsigned char *stream, uint32_t stream_seq, int stream_len, const unsigned char *payload, uint32_t payload_seq, int payload_len, HashMap *pending +); -- cgit v1.2.3 From 54fdf207352a0ee44a8264972118f6cc9d30cad1 Mon Sep 17 00:00:00 2001 From: Mosder Date: Fri, 10 Jul 2026 10:17:47 +0200 Subject: feat: defined unsigned char as byte_t --- analyzer/analyzer.c | 4 ++-- analyzer/utils/pcap.c | 11 ++++------- analyzer/utils/pcap.h | 15 ++++++++------- 3 files changed, 14 insertions(+), 16 deletions(-) (limited to 'analyzer/utils') diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index acbd5de..7db377e 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -15,8 +15,8 @@ void analyze_file(char *path) { } struct pcap_pkthdr *packet_data; - const unsigned char *packet; - const unsigned char *payload; + const byte_t *packet; + const byte_t *payload; // read packets from the file while (pcap_next_ex(handle, &packet_data, &packet) == 1) { diff --git a/analyzer/utils/pcap.c b/analyzer/utils/pcap.c index d9687b3..d38cbff 100644 --- a/analyzer/utils/pcap.c +++ b/analyzer/utils/pcap.c @@ -4,7 +4,7 @@ #include #include -const unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq) { +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); @@ -38,20 +38,17 @@ const unsigned char *get_payload_from_packet(const unsigned char *packet, const return packet + total_header_len; } -PendingPayload new_pending(const unsigned char *payload, int payload_len) { - // TODO: impement +PendingPayload new_pending(const byte_t *payload, int payload_len) { PendingPayload tmp; return tmp; } -unsigned char *free_parsed_data(unsigned char *stream, uint32_t stream_seq, int stream_len, int parsed_len) { +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( - unsigned char *stream, uint32_t stream_seq, int stream_len, const unsigned char *payload, uint32_t payload_seq, int payload_len, HashMap *pending -) { +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 index a423083..10f01f1 100644 --- a/analyzer/utils/pcap.h +++ b/analyzer/utils/pcap.h @@ -5,6 +5,8 @@ #include "hashmap.h" +typedef unsigned char byte_t; + // get payload from a TCP/IP packet // params: // - packet - captured packet @@ -13,11 +15,11 @@ // - tcp_seq - pointer to save the TCP sequence number // returns: // pointer to the payload or NULL if not a correct packet -extern const unsigned char *get_payload_from_packet(const unsigned char *packet, const struct pcap_pkthdr *packet_data, int *payload_len, uint32_t *tcp_seq); +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 { - unsigned char *payload; + byte_t *payload; int payload_len; } PendingPayload; @@ -27,7 +29,7 @@ typedef struct { // - payload_len - length of the payload to store // returns: // PendingPayload struct with stored payload -extern PendingPayload new_pending(const unsigned char *payload, int payload_len); +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: @@ -37,7 +39,7 @@ extern PendingPayload new_pending(const unsigned char *payload, int payload_len) // - parsed_len - count of n first bytes of the stream that were parsed // returns: // pointer to the new, reduced stream -extern unsigned char *free_parsed_data(unsigned char *stream, uint32_t stream_seq, int stream_len, int parsed_len); +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: @@ -50,6 +52,5 @@ extern unsigned char *free_parsed_data(unsigned char *stream, uint32_t stream_se // - 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( - unsigned char *stream, uint32_t stream_seq, int stream_len, const unsigned char *payload, uint32_t payload_seq, int payload_len, HashMap *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); -- cgit v1.2.3 From 26d8457a5da5d70226fdbba5ed92fda8cac735f6 Mon Sep 17 00:00:00 2001 From: Mosder Date: Fri, 10 Jul 2026 12:55:39 +0200 Subject: feat: added functions for iterating over a hashmap --- analyzer/utils/hashmap.c | 21 +++++++++++++++++++++ analyzer/utils/hashmap.h | 15 +++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'analyzer/utils') diff --git a/analyzer/utils/hashmap.c b/analyzer/utils/hashmap.c index 8af7630..2ab8bab 100644 --- a/analyzer/utils/hashmap.c +++ b/analyzer/utils/hashmap.c @@ -96,6 +96,27 @@ void hashmap_delete(HashMap *map, void *key) { } } +HashMapNode *hashmap_first(HashMap *map) { + for (size_t i = 0; i < HASHMAP_TABLE_SIZE; i++) { + HashMapNode *node = map->table[i]; + if (node) + return node; + } + return NULL; +} + +HashMapNode *hashmap_next(HashMap *map, HashMapNode *node) { + if (node->next) + return node->next; + + for (size_t i = map->hash(node->key) + 1; i < HASHMAP_TABLE_SIZE; i++) { + HashMapNode *node = map->table[i]; + if (node) + return node; + } + return NULL; +} + void hashmap_free(HashMap *map) { for (size_t i = 0; i < HASHMAP_TABLE_SIZE; i++) { HashMapNode *curr = map->table[i]; diff --git a/analyzer/utils/hashmap.h b/analyzer/utils/hashmap.h index 96e8b58..22c71c6 100644 --- a/analyzer/utils/hashmap.h +++ b/analyzer/utils/hashmap.h @@ -72,6 +72,21 @@ extern void *hashmap_get(HashMap *map, void *key); // - key - pointer to the key of the entry to delete extern void hashmap_delete(HashMap *map, void *key); +// get first node in the hashmap, use with hashmap_next to iterate over hashmap +// params: +// - map - hash map to get the first node from +// returns: +// pointer to the first node in the hashmap or NULL if there's no entries in it +extern HashMapNode *hashmap_first(HashMap *map); + +// get next node in the hashmap, use with hashmap_first to iterate over hashmap +// params: +// - map - hash map to get the next node from +// - node - the node preceeding the one we search for +// returns: +// pointer to the next node in the hashmap or NULL if there's no next +extern HashMapNode *hashmap_next(HashMap *map, HashMapNode *node); + // free the hash map // - map - hash map to free extern void hashmap_free(HashMap *map); -- cgit v1.2.3 From f75a39e4c476c266efff6cff44d03d6afff3a4f7 Mon Sep 17 00:00:00 2001 From: Mosder Date: Fri, 10 Jul 2026 16:31:08 +0200 Subject: refactor: moved hashmap implementation from utils to new directory utils/structures and removed unnecessary functionality from it --- analyzer/utils/hashmap.c | 147 ------------------------------------ analyzer/utils/hashmap.h | 98 ------------------------ analyzer/utils/structures/hashmap.c | 109 ++++++++++++++++++++++++++ analyzer/utils/structures/hashmap.h | 77 +++++++++++++++++++ 4 files changed, 186 insertions(+), 245 deletions(-) delete mode 100644 analyzer/utils/hashmap.c delete mode 100644 analyzer/utils/hashmap.h create mode 100644 analyzer/utils/structures/hashmap.c create mode 100644 analyzer/utils/structures/hashmap.h (limited to 'analyzer/utils') diff --git a/analyzer/utils/hashmap.c b/analyzer/utils/hashmap.c deleted file mode 100644 index 2ab8bab..0000000 --- a/analyzer/utils/hashmap.c +++ /dev/null @@ -1,147 +0,0 @@ -#include "hashmap.h" - -#include -#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)); - for (size_t i = 0; i < HASHMAP_TABLE_SIZE; i++) - map->table[i] = NULL; - 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; - } -} - -HashMapNode *hashmap_first(HashMap *map) { - for (size_t i = 0; i < HASHMAP_TABLE_SIZE; i++) { - HashMapNode *node = map->table[i]; - if (node) - return node; - } - return NULL; -} - -HashMapNode *hashmap_next(HashMap *map, HashMapNode *node) { - if (node->next) - return node->next; - - for (size_t i = map->hash(node->key) + 1; i < HASHMAP_TABLE_SIZE; i++) { - HashMapNode *node = map->table[i]; - if (node) - return node; - } - return NULL; -} - -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); -} - -size_t hash_uint32(void *key) { - // MurmurHash3 - uint32_t k = *(uint32_t *)key; - k ^= k >> 16; - k *= 0x85ebca6b; - k ^= k >> 13; - k *= 0xc2b2ae35; - k ^= k >> 16; - return k % HASHMAP_TABLE_SIZE; -} - -int compare_uint32(void *key1, void *key2) { - uint32_t *k1 = key1; - uint32_t *k2 = key2; - return *k1 == *k2; -} diff --git a/analyzer/utils/hashmap.h b/analyzer/utils/hashmap.h deleted file mode 100644 index 22c71c6..0000000 --- a/analyzer/utils/hashmap.h +++ /dev/null @@ -1,98 +0,0 @@ -// utils/hashmap.h -// a simple implementation of hashmap using linked list - -#include - -#define HASHMAP_TABLE_SIZE 1024 - -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); - -// get first node in the hashmap, use with hashmap_next to iterate over hashmap -// params: -// - map - hash map to get the first node from -// returns: -// pointer to the first node in the hashmap or NULL if there's no entries in it -extern HashMapNode *hashmap_first(HashMap *map); - -// get next node in the hashmap, use with hashmap_first to iterate over hashmap -// params: -// - map - hash map to get the next node from -// - node - the node preceeding the one we search for -// returns: -// pointer to the next node in the hashmap or NULL if there's no next -extern HashMapNode *hashmap_next(HashMap *map, HashMapNode *node); - -// free the hash map -// - map - hash map to free -extern void hashmap_free(HashMap *map); - -// hash uint32_t key -extern size_t hash_uint32(void *key); - -// compare uint32_t keys -extern int compare_uint32(void *key1, void *key2); diff --git a/analyzer/utils/structures/hashmap.c b/analyzer/utils/structures/hashmap.c new file mode 100644 index 0000000..466f258 --- /dev/null +++ b/analyzer/utils/structures/hashmap.c @@ -0,0 +1,109 @@ +#include "hashmap.h" + +#include +#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)); + for (size_t i = 0; i < HASHMAP_TABLE_SIZE; i++) + map->table[i] = NULL; + 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 new file mode 100644 index 0000000..cc2c9e0 --- /dev/null +++ b/analyzer/utils/structures/hashmap.h @@ -0,0 +1,77 @@ +// utils/hashmap.h +// a simple implementation of hashmap using linked list + +#include + +#define HASHMAP_TABLE_SIZE 1024 + +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); -- cgit v1.2.3 From 7b313dcfe2bb98f7d0905ba34f0a356a3774b1d0 Mon Sep 17 00:00:00 2001 From: Mosder Date: Fri, 10 Jul 2026 16:37:08 +0200 Subject: fix: fixed comment specifying wrong location of the hashmap --- analyzer/utils/structures/hashmap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'analyzer/utils') diff --git a/analyzer/utils/structures/hashmap.h b/analyzer/utils/structures/hashmap.h index cc2c9e0..c0ecbe1 100644 --- a/analyzer/utils/structures/hashmap.h +++ b/analyzer/utils/structures/hashmap.h @@ -1,4 +1,4 @@ -// utils/hashmap.h +// utils/structures/hashmap.h // a simple implementation of hashmap using linked list #include -- cgit v1.2.3 From df37df639f0c28e7cb155172576e7a14cdca8ed6 Mon Sep 17 00:00:00 2001 From: Mosder Date: Fri, 10 Jul 2026 17:07:47 +0200 Subject: refactor: simplified NULL initalization in hashmap to memset --- analyzer/utils/structures/hashmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'analyzer/utils') diff --git a/analyzer/utils/structures/hashmap.c b/analyzer/utils/structures/hashmap.c index 466f258..bab6572 100644 --- a/analyzer/utils/structures/hashmap.c +++ b/analyzer/utils/structures/hashmap.c @@ -9,8 +9,8 @@ HashMap *hashmap_new( void (*value_cleanup)(void *key) ) { HashMap *map = malloc(sizeof(HashMap)); - for (size_t i = 0; i < HASHMAP_TABLE_SIZE; i++) - map->table[i] = NULL; + // initialize NULLs + memset(map->table, 0, sizeof(HashMapNode *) * HASHMAP_TABLE_SIZE); map->key_size = key_size; map->value_size = value_size; map->hash = hash; -- cgit v1.2.3 From 4422f92de05948187eeb0b0067dd947e673f8f1d Mon Sep 17 00:00:00 2001 From: Mosder Date: Fri, 10 Jul 2026 20:25:56 +0200 Subject: feat: implemented a heap structure --- analyzer/utils/structures/hashmap.c | 1 - analyzer/utils/structures/hashmap.h | 2 +- analyzer/utils/structures/heap.c | 109 ++++++++++++++++++++++++++++++++++++ analyzer/utils/structures/heap.h | 55 ++++++++++++++++++ 4 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 analyzer/utils/structures/heap.c create mode 100644 analyzer/utils/structures/heap.h (limited to 'analyzer/utils') diff --git a/analyzer/utils/structures/hashmap.c b/analyzer/utils/structures/hashmap.c index bab6572..5c7fdf9 100644 --- a/analyzer/utils/structures/hashmap.c +++ b/analyzer/utils/structures/hashmap.c @@ -1,6 +1,5 @@ #include "hashmap.h" -#include #include #include diff --git a/analyzer/utils/structures/hashmap.h b/analyzer/utils/structures/hashmap.h index c0ecbe1..4f8e24a 100644 --- a/analyzer/utils/structures/hashmap.h +++ b/analyzer/utils/structures/hashmap.h @@ -3,7 +3,7 @@ #include -#define HASHMAP_TABLE_SIZE 1024 +#define HASHMAP_TABLE_SIZE 128 typedef struct HashMapNode { void *key; diff --git a/analyzer/utils/structures/heap.c b/analyzer/utils/structures/heap.c new file mode 100644 index 0000000..5310299 --- /dev/null +++ b/analyzer/utils/structures/heap.c @@ -0,0 +1,109 @@ +#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 new file mode 100644 index 0000000..e960fb7 --- /dev/null +++ b/analyzer/utils/structures/heap.h @@ -0,0 +1,55 @@ +// utils/structures/heap.h +// a simple implementation of heap + +#include + +#define INIT_HEAP_DEPTH 7 +#define INIT_HEAP_CAPACITY (1 << INIT_HEAP_DEPTH) - 1 + +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; + +// 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); -- 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/utils') 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 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/utils') 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 c7277e7654c0d8d0e7a4665373c0762c6e8a038d Mon Sep 17 00:00:00 2001 From: Mosder Date: Tue, 14 Jul 2026 13:40:17 +0200 Subject: fix: fixed not handling of different connections and bug regarding TCP SYN packet --- analyzer/analyzer.c | 4 ++-- analyzer/utils/tcp.c | 23 ++++++++++++++++------- analyzer/utils/tcp.h | 10 ++++++---- 3 files changed, 24 insertions(+), 13 deletions(-) (limited to 'analyzer/utils') diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index 03ecd2a..7def1e6 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -15,8 +15,8 @@ void analyzer_loop(TCPStream *stream, pcap_t *pcap_handle, void (*parsed_data_ha // get new segment TCPSegment segment = get_segment_from_packet(packet, packet_data); - // handle the new segment - if (handle_segment(stream, segment)) { + // handle the new segment if it's correct one and has data in it (ignore SYN) + if (segment.len > 0 && handle_segment(stream, segment)) { // if it was added to the stream - parse it size_t parsed_len; size_t n_parsed_data; diff --git a/analyzer/utils/tcp.c b/analyzer/utils/tcp.c index ab5303e..b925ecf 100644 --- a/analyzer/utils/tcp.c +++ b/analyzer/utils/tcp.c @@ -12,8 +12,9 @@ TCPSegment new_segment() { TCPSegment segment; segment.payload = NULL; - segment.len = -1; + segment.len = 0; segment.seq = 0; + segment.port = 0; return segment; } @@ -53,8 +54,9 @@ TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthd 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 + // get TCP sequence number and destination port segment.seq = ntohl(tcp_header->seq); + segment.port = ntohs(tcp_header->th_dport); // get payload int total_header_len = eth_header_len + ip_header_len + tcp_header_len; @@ -66,11 +68,12 @@ TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthd TCPStream new_stream() { TCPStream stream; - stream.data = NULL; // NULL to differentiate between first and other segments + stream.data = malloc(INIT_STREAM_CAPACITY); stream.len = 0; stream.capacity = INIT_STREAM_CAPACITY; stream.seq = 0; - stream.pending = heap_new(sizeof(TCPSegment), segments_compare, segment_cleanup); + stream.port = 0; + stream.pending = NULL; return stream; } @@ -142,10 +145,16 @@ void add_pending_segments(TCPStream *stream) { } 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); + // add the segment if port is different (new connection) + if (stream->port != segment.port) { + // create new heap for new pending segments + heap_free(stream->pending); + stream->pending = heap_new(sizeof(TCPSegment), segments_compare, segment_cleanup); + + stream->len = 0; stream->seq = segment.seq; + stream->port = segment.port; + add_segment(stream, segment, 0); free(segment.payload); return 1; diff --git a/analyzer/utils/tcp.h b/analyzer/utils/tcp.h index 37ab4bc..9c6a949 100644 --- a/analyzer/utils/tcp.h +++ b/analyzer/utils/tcp.h @@ -12,9 +12,10 @@ typedef unsigned char byte_t; #ifndef TCP_SEGMENT_DEFINED #define TCP_SEGMENT_DEFINED typedef struct { - byte_t *payload; - size_t len; - uint32_t seq; + byte_t *payload; // payload in the segment + size_t len; // length of the payload + uint32_t seq; // TCP sequence number of the segment + uint16_t port; // destination (client) port of connection the segment was received from } TCPSegment; #endif @@ -23,7 +24,7 @@ typedef struct { // - 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 +// TCPSegment from packet, payload is NULL 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 @@ -34,6 +35,7 @@ typedef struct { 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 + uint16_t port; // destination (client) port of current analyzed connection Heap *pending; // min heap of segments yet to be added to stream (sorted by TCP sequence number) } TCPStream; #endif -- cgit v1.2.3 From c182f2f4f850589c23b2241e11dab143e1003ff2 Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 20 Jul 2026 15:48:51 +0200 Subject: refactor: changed #ifndef guards, so that there is one per file --- analyzer/parser/fight_results.h | 17 +++++------------ analyzer/parser/parser_defs.h | 15 ++++----------- analyzer/structures/heap.h | 8 +++++--- analyzer/utils/tcp.h | 11 +++++------ 4 files changed, 19 insertions(+), 32 deletions(-) (limited to 'analyzer/utils') diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index ca45348..3aadf09 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -4,6 +4,9 @@ #include "parser/parser_defs.h" #include "utils/tcp.h" +#ifndef FIGHT_RESULTS_H +#define FIGHT_RESULTS_H + // Payload structure: // FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END // @@ -92,8 +95,6 @@ &entity_results.saturation \ } -#ifndef ENTITY_RESULTS_DEFINED -#define ENTITY_RESULTS_DEFINED typedef struct { int team; // friend or enemy char *name; @@ -131,7 +132,6 @@ typedef struct { char *field34; char *saturation; } EntityResults; -#endif // items/gear/drifs fields parsing info #define INIT_ITEMS_CAPACITY 8 @@ -143,8 +143,6 @@ typedef struct { #define TEAM_ENEMY 2 // fight results data for friendly -#ifndef FRIENDLY_RESULTS_DEFINED -#define FRIENDLY_RESULTS_DEFINED typedef struct { char *name; int exp; @@ -157,29 +155,24 @@ typedef struct { char *saturation_type; int saturation; } FriendlyResults; -#endif // fight results data for enemy -#ifndef ENEMY_RESULTS_DEFINED -#define ENEMY_RESULTS_DEFINED typedef struct { char *name; int level; } EnemyResults; -#endif #define MAX_ENTITIES_PER_SIDE 8 // entire fight resutls -#ifndef FIGHT_RESULTS_DEFINED -#define FIGHT_RESULTS_DEFINED typedef struct { size_t num_friendly; FriendlyResults friendly_results[MAX_ENTITIES_PER_SIDE]; size_t num_enemy; EnemyResults enemy_results[MAX_ENTITIES_PER_SIDE]; } FightResults; -#endif extern find_function_t find_fight_results; extern parse_function_t parse_fight_results; + +#endif diff --git a/analyzer/parser/parser_defs.h b/analyzer/parser/parser_defs.h index 6af6059..aa39a4c 100644 --- a/analyzer/parser/parser_defs.h +++ b/analyzer/parser/parser_defs.h @@ -3,18 +3,13 @@ #include "utils/tcp.h" -#ifndef TYPE_ENUM_DEFINED -#define TYPE_ENUM_DEFINED +#ifndef PARSER_DEFS_H +#define PARSER_DEFS_H + typedef enum { INT, FLOAT, DOUBLE, STRING } TypeEnum; -#endif -#ifndef RESULTS_ENUM_DEFINED -#define RESULTS_ENUM_DEFINED typedef enum { TYPE_FIGHT_RESULTS } ResultsEnum; -#endif -#ifndef PARSED_DATA_DEFINED -#define PARSED_DATA_DEFINED // "generic" type for parsed data typedef struct { ResultsEnum data_type; // type of data represented by an enum @@ -22,7 +17,6 @@ typedef struct { void (*data_cleanup)(void *data); // function to cleanup internal allocations inside data // if no internal allocations - this should be NULL } ParsedData; -#endif // find the first results data of specific type in stream // params: @@ -42,10 +36,9 @@ typedef byte_t *find_function_t(TCPStream *stream, int offset, int *length); typedef ParsedData parse_function_t(byte_t *data, size_t length); // struct for holding the find and parse function for every result type -#ifndef FIND_PARSE_FUNCTIONS_DEFINED -#define FIND_PARSE_FUNCTIONS_DEFINED typedef struct { find_function_t *find_fn; parse_function_t *parse_fn; } FindParseFunctions; + #endif diff --git a/analyzer/structures/heap.h b/analyzer/structures/heap.h index 8a23164..1ae72c0 100644 --- a/analyzer/structures/heap.h +++ b/analyzer/structures/heap.h @@ -3,11 +3,12 @@ #include +#ifndef HEAP_H +#define HEAP_H + #define INIT_HEAP_DEPTH 7 #define INIT_HEAP_CAPACITY (1 << INIT_HEAP_DEPTH) - 1 -#ifndef HEAP_DEFINED -#define HEAP_DEFINED typedef struct { void **data; size_t n_elements; @@ -16,7 +17,6 @@ typedef struct { int (*compare)(void *element1, void *element2); void (*element_cleanup)(void *element); } Heap; -#endif // create a new heap // params: @@ -56,3 +56,5 @@ extern void heap_top_remove(Heap *heap); // free the heap // - heap - heap to free extern void heap_free(Heap *heap); + +#endif diff --git a/analyzer/utils/tcp.h b/analyzer/utils/tcp.h index 9c6a949..13cbb15 100644 --- a/analyzer/utils/tcp.h +++ b/analyzer/utils/tcp.h @@ -5,19 +5,19 @@ #include "structures/heap.h" +#ifndef TCP_H +#define TCP_H + typedef unsigned char byte_t; // struct to persistently store TCP segments // payload field is malloced and needs to be freed -#ifndef TCP_SEGMENT_DEFINED -#define TCP_SEGMENT_DEFINED typedef struct { byte_t *payload; // payload in the segment size_t len; // length of the payload uint32_t seq; // TCP sequence number of the segment uint16_t port; // destination (client) port of connection the segment was received from } TCPSegment; -#endif // get TCP segment from captured packet // params: @@ -28,8 +28,6 @@ typedef struct { extern TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthdr *packet_data); // struct that stores all the information about stream -#ifndef TCP_STREAM_DEFINED -#define TCP_STREAM_DEFINED typedef struct { byte_t *data; // bytes in the stream size_t len; // length of the stream @@ -38,7 +36,6 @@ typedef struct { uint16_t port; // destination (client) port of current analyzed connection Heap *pending; // min heap of segments yet to be added to stream (sorted by TCP sequence number) } TCPStream; -#endif #define INIT_STREAM_CAPACITY 256 @@ -68,3 +65,5 @@ extern void remove_parsed_data(TCPStream *stream, size_t parsed_len); // returns: // 1 if the segment was added to the stream, 0 if it was discarded or saved to pending extern int handle_segment(TCPStream *stream, TCPSegment segment); + +#endif -- cgit v1.2.3 From 5c0e713713143bad475a92d20ee5fe215b5bafd3 Mon Sep 17 00:00:00 2001 From: Mosder Date: Tue, 21 Jul 2026 17:26:09 +0200 Subject: feat: added parsing for gear --- Makefile | 1 + analyzer/parser/fight_results.c | 169 +++++++++++++++--- analyzer/parser/fight_results.h | 375 ++++++++++++++++++++++++++++++---------- analyzer/render.c | 2 + analyzer/render.h | 24 +-- analyzer/utils/roman.c | 16 ++ analyzer/utils/roman.h | 12 ++ csv/artifact_sizes.csv | 5 + csv/orbs.csv | 3 + 9 files changed, 482 insertions(+), 125 deletions(-) create mode 100644 analyzer/utils/roman.c create mode 100644 analyzer/utils/roman.h create mode 100644 csv/artifact_sizes.csv create mode 100644 csv/orbs.csv (limited to 'analyzer/utils') diff --git a/Makefile b/Makefile index afb61fd..72f94e0 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,7 @@ utils: $(CC) $(CFLAGS) -c analyzer/utils/time.c $(CC) $(CFLAGS) -c analyzer/utils/tcp.c $(CC) $(CFLAGS) -c analyzer/utils/tcp_seq_math.c + $(CC) $(CFLAGS) -c analyzer/utils/roman.c parser: $(CC) $(CFLAGS) -c analyzer/parser/parser.c diff --git a/analyzer/parser/fight_results.c b/analyzer/parser/fight_results.c index f201fb1..c1d6420 100644 --- a/analyzer/parser/fight_results.c +++ b/analyzer/parser/fight_results.c @@ -4,8 +4,11 @@ #include #include +#include "csv/id_to_artifact_size.h" #include "csv/id_to_item_name.h" +#include "csv/id_to_orb_name.h" #include "csv/id_to_saturation_type.h" +#include "utils/roman.h" // function to cleanup fight results to be used in ParsedData struct void cleanup_fight_results(void *data) { @@ -70,15 +73,18 @@ void get_struct_data(size_t field_count, TypeEnum *field_types, void **field_poi } } +// free struct fields which are STRING +void destroy_struct(size_t field_count, TypeEnum *field_types, void **field_pointers) { + for (size_t i = 0; i < field_count; i++) { + if (field_types[i] == STRING) + free(*(char **)field_pointers[i]); + } +} + void destroy_entity_results(EntityResults results) { - free(results.name); - free(results.field7); - free(results.gear); - free(results.items); - free(results.drifs); - free(results.field29); - free(results.field34); - free(results.saturation); + TypeEnum field_types[ENTITY_FIELD_COUNT] = ENTITY_FIELD_TYPES; + void *field_pointers[ENTITY_FIELD_COUNT] = ENTITY_FIELD_ADDRESSES(results); + destroy_struct(ENTITY_FIELD_COUNT, field_types, field_pointers); } // get entity results from the string @@ -96,35 +102,148 @@ Item *expand_items(Item *items, size_t *items_capacity, size_t to_fit) { return realloc(items, *items_capacity * sizeof(Item)); } -void add_gear(FriendlyResults *results, size_t *items_capacity, char *gear_str) { - if (strlen(gear_str) == 0) +void destroy_gear(Gear gear) { + TypeEnum field_types[GEAR_FIELD_COUNT] = GEAR_FIELD_TYPES; + void *field_pointers[GEAR_FIELD_COUNT] = GEAR_FIELD_ADDRESSES(gear); + destroy_struct(GEAR_FIELD_COUNT, field_types, field_pointers); +} + +Gear get_gear(char *data, size_t length) { + Gear gear; + TypeEnum field_types[GEAR_FIELD_COUNT] = GEAR_FIELD_TYPES; + void *field_pointers[GEAR_FIELD_COUNT] = GEAR_FIELD_ADDRESSES(gear); + get_struct_data(GEAR_FIELD_COUNT, field_types, field_pointers, SEPARATOR_GEAR_FIELDS, data, length); + return gear; +} + +void destroy_gear_info(GearInfo gear_info) { + TypeEnum field_types[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_TYPES; + void *field_pointers[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_ADDRESSES(gear_info); + destroy_struct(GEAR_INFO_FIELD_COUNT, field_types, field_pointers); +} + +GearInfo get_gear_info(char *data, size_t length) { + GearInfo gear_info; + TypeEnum field_types[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_TYPES; + void *field_pointers[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_ADDRESSES(gear_info); + get_struct_data(GEAR_INFO_FIELD_COUNT, field_types, field_pointers, SEPARATOR_GEAR_INFO_FIELDS, data, length); + return gear_info; +} + +void destroy_orb(Orb orb) { + TypeEnum field_types[ORB_FIELD_COUNT] = ORB_FIELD_TYPES; + void *field_pointers[ORB_FIELD_COUNT] = ORB_FIELD_ADDRESSES(orb); + destroy_struct(ORB_FIELD_COUNT, field_types, field_pointers); +} + +Orb get_orb(char *data, size_t length) { + Orb orb; + TypeEnum field_types[ORB_FIELD_COUNT] = ORB_FIELD_TYPES; + void *field_pointers[ORB_FIELD_COUNT] = ORB_FIELD_ADDRESSES(orb); + get_struct_data(ORB_FIELD_COUNT, field_types, field_pointers, SEPARATOR_ORB_FIELDS, data, length); + return orb; +} + +char *get_artifact_size(int artifact_size_id) { + return artifact_size_id < id_to_artifact_size_len ? id_to_artifact_size[artifact_size_id] : "null"; +} + +char *get_orb_name(int orb_name_id) { + return orb_name_id < id_to_orb_name_len ? id_to_orb_name[orb_name_id] : "null"; +} + +void get_stars(char *buffer, int incr_above_b1) { + char star_types[] = {'B', 'S', 'G'}; + buffer[0] = star_types[incr_above_b1 / 3]; + buffer[1] = incr_above_b1 % 3 + 1 + '0'; + buffer[2] = '\0'; +} + +void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) { + if (strlen(gears_str) == 0) return; - // TODO: parse - if (results->num_items >= *items_capacity) - results->items = expand_items(results->items, items_capacity, results->num_items + 1); + char *gears_separator; + do { + gears_separator = strstr(gears_str, SEPARATOR_GEARS); + Gear gear = get_gear(gears_str, gears_separator ? gears_separator - gears_str : strlen(gears_str)); + + // if there are still gears - modify gears_str pointer + if (gears_separator) + gears_str = gears_separator + strlen(SEPARATOR_DRIFS); + + // expand if needed + if (results->num_items >= *items_capacity) + results->items = expand_items(results->items, items_capacity, results->num_items + 1); + + Item item; + + GearInfo gear_info = get_gear_info(gear.info, strlen(gear.info)); + char buffer[SINGLE_ITEM_BUFFER_LEN]; + char roman[5]; + to_roman(roman, gear_info.rank); + sprintf(buffer, "[%s] %s", roman, gear_info.name); + switch (gear.type) { + case TYPE_SYNG_NORMAL: + // atoi should be fine, since syng_tier starts from 1 for syngs + if (atoi(gear.syng_tier)) + sprintf(buffer, "%s (%d)", buffer, gear_info.lvl); + break; + default: { + char stars[3]; + get_stars(stars, gear_info.stars); + sprintf(buffer, "%s (%s)", buffer, stars); + break; + } + } + item.data = strdup(buffer); + destroy_gear_info(gear_info); - Item item; - item.data = strdup(gear_str); - item.type = RARE; - results->items[results->num_items++] = item; + switch (gear.type) { + case TYPE_SYNG_NORMAL: + // atoi should be fine, since syng_tier starts from 1 for syngs + item.type = atoi(gear.syng_tier) ? SYNG : NORMAL; + break; + case TYPE_SET: + item.type = SET; + break; + case TYPE_RARE: + item.type = RARE; + break; + default: + item.type = EPIC; + break; + } + + results->items[results->num_items++] = item; + + // add orb to items if it exists + if (strlen(gear.orb)) { + Item item; + char buffer[SINGLE_ITEM_BUFFER_LEN]; + Orb orb = get_orb(gear.orb, strlen(gear.orb)); + sprintf(buffer, "%sorb %s", get_artifact_size(orb.size), get_orb_name(orb.id)); + item.data = strdup(buffer); + destroy_orb(orb); + item.type = ORB; + results->items[results->num_items++] = item; + } + + destroy_gear(gear); + } while (gears_separator); } void destroy_drif(Drif drif) { - free(drif.field1); - free(drif.field4); - free(drif.field11); - free(drif.field12); - free(drif.name); - free(drif.field18); - free(drif.field19); + TypeEnum field_types[DRIF_FIELD_COUNT] = DRIF_FIELD_TYPES; + void *field_pointers[DRIF_FIELD_COUNT] = DRIF_FIELD_ADDRESSES(drif); + destroy_struct(DRIF_FIELD_COUNT, field_types, field_pointers); } Drif get_drif(char *data, size_t length) { Drif drif; TypeEnum field_types[DRIF_FIELD_COUNT] = DRIF_FIELD_TYPES; void *field_pointers[DRIF_FIELD_COUNT] = DRIF_FIELD_ADDRESSES(drif); - get_struct_data(DRIF_FIELD_COUNT, field_types, field_pointers, SEPARATOR_DRIF_INFO, data, length); + get_struct_data(DRIF_FIELD_COUNT, field_types, field_pointers, SEPARATOR_DRIF_FIELDS, data, length); return drif; } diff --git a/analyzer/parser/fight_results.h b/analyzer/parser/fight_results.h index 76083ab..59d1e45 100644 --- a/analyzer/parser/fight_results.h +++ b/analyzer/parser/fight_results.h @@ -14,6 +14,10 @@ #define SEPARATOR_ENTITY "[--]" #define SEPARATOR_RESULT "&" +// values of field "team" in EntityResults +#define TEAM_FRIEND 1 +#define TEAM_ENEMY 2 + // EntityResults structure: // field (SEPARATOR_RESULT field)* // fields are defined in order in the EntityResults struct @@ -54,44 +58,44 @@ /* field32 */ INT, \ /* field33 */ INT, \ /* field34 */ STRING, \ - /* saturation */ STRING \ + /* saturation */ STRING, \ } #define ENTITY_FIELD_ADDRESSES(entity_results) {\ - &entity_results.team, \ - &entity_results.name, \ - &entity_results.exp, \ - &entity_results.field4, \ - &entity_results.gold, \ - &entity_results.field6, \ - &entity_results.field7, \ - &entity_results.gear, \ - &entity_results.field9, \ - &entity_results.items, \ - &entity_results.field11, \ - &entity_results.field12, \ - &entity_results.field13, \ - &entity_results.field14, \ - &entity_results.field15, \ - &entity_results.level, \ - &entity_results.field17, \ - &entity_results.field18, \ - &entity_results.field19, \ - &entity_results.field20, \ - &entity_results.field21, \ - &entity_results.field22, \ - &entity_results.field23, \ - &entity_results.field24, \ - &entity_results.psycho, \ - &entity_results.drifs, \ - &entity_results.splinters, \ - &entity_results.field28, \ - &entity_results.field29, \ - &entity_results.field30, \ - &entity_results.field31, \ - &entity_results.field32, \ - &entity_results.field33, \ - &entity_results.field34, \ - &entity_results.saturation \ + &entity_results.team, \ + &entity_results.name, \ + &entity_results.exp, \ + &entity_results.field4, \ + &entity_results.gold, \ + &entity_results.field6, \ + &entity_results.field7, \ + &entity_results.gear, \ + &entity_results.field9, \ + &entity_results.items, \ + &entity_results.field11, \ + &entity_results.field12, \ + &entity_results.field13, \ + &entity_results.field14, \ + &entity_results.field15, \ + &entity_results.level, \ + &entity_results.field17, \ + &entity_results.field18, \ + &entity_results.field19, \ + &entity_results.field20, \ + &entity_results.field21, \ + &entity_results.field22, \ + &entity_results.field23, \ + &entity_results.field24, \ + &entity_results.psycho, \ + &entity_results.drifs, \ + &entity_results.splinters, \ + &entity_results.field28, \ + &entity_results.field29, \ + &entity_results.field30, \ + &entity_results.field31, \ + &entity_results.field32, \ + &entity_results.field33, \ + &entity_results.field34, \ + &entity_results.saturation, \ } // clang-format on typedef struct { @@ -137,87 +141,280 @@ typedef struct { #define SINGLE_ITEM_BUFFER_LEN 128 #define SEPARATOR_ITEMS "[-]" #define SEPARATOR_DRIFS "???" // TODO: change it to correct one which idk at this moment -#define SEPARATOR_DRIF_INFO "," +#define SEPARATOR_GEARS "???" // TODO: change it to correct one which idk at this moment // Item struct - for keeping item string and type -typedef enum { NORMAL, DRIF, SYNG, SET, RARE, EPIC } ItemType; +typedef enum { NORMAL, DRIF, ORB, SYNG, SET, RARE, EPIC } ItemType; typedef struct { char *data; ItemType type; } Item; // Drif structure: -// field (SEPARATOR_DRIF field)* +// field (SEPARATOR_DRIF_FIELDS field)* // fields are defined in order in the Drif struct -#define DRIF_FIELD_COUNT 19 +#define DRIF_FIELD_COUNT 18 +#define SEPARATOR_DRIF_FIELDS "," // clang-format off #define DRIF_FIELD_TYPES { \ - /* field1 */ STRING, \ - /* field2 */ INT, \ - /* field3 */ INT, \ - /* field4 */ STRING, \ - /* field5 */ INT, \ - /* field6 */ INT, \ - /* field7 */ FLOAT, \ - /* field8 */ FLOAT, \ - /* field9 */ INT, \ - /* field10 */ INT, \ - /* field11 */ STRING, \ - /* field12 */ STRING, \ - /* field13 */ INT, \ - /* field14 */ INT, \ - /* field15 */ INT, \ - /* field16 */ INT, \ - /* name */ STRING, \ - /* field18 */ STRING, \ - /* field19 */ STRING, \ + /* field1 */ STRING, \ + /* field2 */ INT, \ + /* size */ INT, \ + /* field4 */ STRING, \ + /* field5 */ INT, \ + /* id */ INT, \ + /* value1 */ FLOAT, \ + /* value2 */ FLOAT, \ + /* field9 */ INT, \ + /* field10 */ INT, \ + /* field11 */ STRING, \ + /* size_str */ STRING, \ + /* field13 */ INT, \ + /* power */ INT, \ + /* field15 */ INT, \ + /* field16 */ INT, \ + /* name */ STRING, \ + /* field18 */ STRING, \ } #define DRIF_FIELD_ADDRESSES(drif) {\ - &drif.field1, \ - &drif.field2, \ - &drif.field3, \ - &drif.field4, \ - &drif.field5, \ - &drif.field6, \ - &drif.field7, \ - &drif.field8, \ - &drif.field9, \ - &drif.field10, \ - &drif.field11, \ - &drif.field12, \ - &drif.field13, \ - &drif.field14, \ - &drif.field15, \ - &drif.field16, \ - &drif.name, \ - &drif.field18, \ - &drif.field19, \ + &drif.field1, \ + &drif.field2, \ + &drif.size, \ + &drif.field4, \ + &drif.field5, \ + &drif.id, \ + &drif.value1, \ + &drif.value2, \ + &drif.field9, \ + &drif.field10, \ + &drif.field11, \ + &drif.size_str, \ + &drif.field13, \ + &drif.power, \ + &drif.field15, \ + &drif.field16, \ + &drif.name, \ + &drif.field18, \ } // clang-format on typedef struct { - char *field1; + char *field1; // just says "drif" int field2; - int field3; + int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy char *field4; int field5; - int field6; - float field7; - float field8; + int id; // id of orb type + float value1; // value1, value2 (in %) - one is start value, the other is value per lvl (both are the same) + float value2; // value1, value2 (in %) - one is start value, the other is value per lvl (both are the same) int field9; int field10; char *field11; - char *field12; + char *size_str; // sub - small, medium - bi, big - magni, ??? - arcy int field13; - int field14; + int power; // how much space it takes int field15; int field16; char *name; // drif char *field18; - char *field19; } Drif; -// values of field "team" -#define TEAM_FRIEND 1 -#define TEAM_ENEMY 2 +// Gear structure: +// field (SEPARATOR_GEAR_FIELDS field)* +// fields are defined in order in the Gear struct +#define GEAR_FIELD_COUNT 19 +#define SEPARATOR_GEAR_FIELDS "$" +// values of field "type" in Gear +#define TYPE_SYNG_NORMAL 0 +#define TYPE_SET 1 +#define TYPE_RARE 2 +// clang-format off +#define GEAR_FIELD_TYPES { \ + /* stats */ STRING, \ + /* resistances */ STRING, \ + /* info */ STRING, \ + /* reqs */ STRING, \ + /* field5 */ STRING, \ + /* psycho */ STRING, \ + /* field7 */ STRING, \ + /* field8 */ STRING, \ + /* field9 */ STRING, \ + /* syng_tier */ STRING, \ + /* field11 */ INT, \ + /* field12 */ INT, \ + /* field13 */ INT, \ + /* field14 */ STRING, \ + /* type */ INT, \ + /* field16 */ INT, \ + /* orb */ STRING, \ + /* field18 */ STRING, \ + /* field19 */ STRING, \ +} +#define GEAR_FIELD_ADDRESSES(gear) {\ + &gear.stats, \ + &gear.resistances, \ + &gear.info, \ + &gear.reqs, \ + &gear.field5, \ + &gear.psycho, \ + &gear.field7, \ + &gear.field8, \ + &gear.field9, \ + &gear.syng_tier, \ + &gear.field11, \ + &gear.field12, \ + &gear.field13, \ + &gear.field14, \ + &gear.type, \ + &gear.field16, \ + &gear.orb, \ + &gear.field18, \ + &gear.field19, \ +} +// clang-format on +typedef struct { + char *stats; // strength, dexterity, power, knowledge, hp, mana, stamina + char *resistances; // slashing, crushing, piercing, fire, cold, energy, mental + char *info; // GearInfo struct data + char *reqs; // requirements (str, dex, pow, know), weight, something else + char *field5; + char *psycho; // psycho effects (id,%) for syngs + char *field7; + char *field8; + char *field9; + char *syng_tier; // lvl 20 - 1, lvl 30 - 2, etc..., null for non-syngs + int field11; + int field12; + int field13; + char *field14; + int type; // 0 - normal item or syng, 1 - set, 2 - rare + int field16; // 10 for rare, -1 for others ??? + char *orb; // orb info + char *field18; + char *field19; +} Gear; + +// GearInfo structure: +// field (SEPARATOR_GEAR_INFO_FIELDS field)* +// fields are defined in order in the GearInfo struct +#define GEAR_INFO_FIELD_COUNT 23 +#define SEPARATOR_GEAR_INFO_FIELDS "," +// clang-format off +#define GEAR_INFO_FIELD_TYPES { \ + /* name */ STRING, \ + /* field2 */ INT, \ + /* lvl */ INT, \ + /* price */ INT, \ + /* field5 */ INT, \ + /* field6 */ INT, \ + /* field7 */ INT, \ + /* rank */ INT, \ + /* field9 */ INT, \ + /* field10 */ INT, \ + /* field11 */ INT, \ + /* durability */ INT, \ + /* field13 */ STRING, \ + /* field14 */ INT, \ + /* field15 */ INT, \ + /* charges */ FLOAT, \ + /* field17 */ STRING, \ + /* field18 */ INT, \ + /* stars */ INT, \ + /* field20 */ INT, \ + /* field21 */ INT, \ + /* field22 */ INT, \ + /* field23 */ INT, \ +} +#define GEAR_INFO_FIELD_ADDRESSES(gear_info) {\ + &gear_info.name, \ + &gear_info.field2, \ + &gear_info.lvl, \ + &gear_info.price, \ + &gear_info.field5, \ + &gear_info.field6, \ + &gear_info.field7, \ + &gear_info.rank, \ + &gear_info.field9, \ + &gear_info.field10, \ + &gear_info.field11, \ + &gear_info.durability, \ + &gear_info.field13, \ + &gear_info.field14, \ + &gear_info.field15, \ + &gear_info.charges, \ + &gear_info.field17, \ + &gear_info.field18, \ + &gear_info.stars, \ + &gear_info.field20, \ + &gear_info.field21, \ + &gear_info.field22, \ + &gear_info.field23, \ +} +// clang-format on +typedef struct { + char *name; // item name + int field2; + int lvl; // level requirement + int price; // gold price value + int field5; + int field6; + int field7; + int rank; // I-XII + int field9; + int field10; + int field11; + int durability; // in % + char *field13; + int field14; + int field15; + float charges; // charge count + char *field17; + int field18; + int stars; // incrustation level: 0 - b1, 1 - b2, ... (-100 for syngs) + int field20; + int field21; + int field22; + int field23; +} GearInfo; + +// Orb structure: +// field (SEPARATOR_ORB_FIELDS field)* +// fields are defined in order in the Orb struct +#define ORB_FIELD_COUNT 9 +#define SEPARATOR_ORB_FIELDS "," +// clang-format off +#define ORB_FIELD_TYPES { \ + /* field1 */ STRING, \ + /* field2 */ INT, \ + /* field3 */ INT, \ + /* field4 */ STRING, \ + /* field5 */ INT, \ + /* id */ INT, \ + /* size */ INT, \ + /* value */ FLOAT, \ + /* field9 */ STRING, \ +} +#define ORB_FIELD_ADDRESSES(orb) {\ + &orb.field1, \ + &orb.field2, \ + &orb.field3, \ + &orb.field4, \ + &orb.field5, \ + &orb.id, \ + &orb.size, \ + &orb.value, \ + &orb.field9, \ +} +// clang-format on +typedef struct { + char *field1; // just says "orb" + int field2; + int field3; + char *field4; + int field5; + int id; // id of orb type + int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy + float value; // value in % you gain from the orb + char *field9; +} Orb; // fight results data for friendly typedef struct { diff --git a/analyzer/render.c b/analyzer/render.c index 28d1673..0fdd334 100644 --- a/analyzer/render.c +++ b/analyzer/render.c @@ -81,6 +81,8 @@ int item_type_to_color(ItemType type) { return COLOR_WHITE; case DRIF: return COLOR_YELLOW; + case ORB: + return COLOR_GOLD; case SYNG: return COLOR_RED; case SET: diff --git a/analyzer/render.h b/analyzer/render.h index 97958d1..843cea5 100644 --- a/analyzer/render.h +++ b/analyzer/render.h @@ -4,6 +4,8 @@ #include "parser/fight_results.h" // ANSI escape sequence colors +#define COLOR_GOLD 33 +#define COLOR_GRAY 90 #define COLOR_RED 91 #define COLOR_GREEN 92 #define COLOR_YELLOW 93 @@ -34,24 +36,24 @@ #define TABLE_COLUMN_NAMES {"Nick (LVL)", "EXP", "Gold", "Psycho", "Saturation", "Splinters"} #define TABLE_COLUMN_COLORS {COLOR_WHITE, COLOR_WHITE, COLOR_YELLOW, COLOR_AQUA, COLOR_GREEN, COLOR_BLUE} #define TABLE_COLUMN_WIDTHS { \ - TABLE_NAME_LVL_WIDTH, \ - TABLE_EXP_WIDTH, \ - TABLE_GOLD_WIDTH, \ - TABLE_PSYCHO_WIDTH, \ + TABLE_NAME_LVL_WIDTH, \ + TABLE_EXP_WIDTH, \ + TABLE_GOLD_WIDTH, \ + TABLE_PSYCHO_WIDTH, \ TABLE_SATURATION_WIDTH, \ - TABLE_SPLINTERS_WIDTH \ + TABLE_SPLINTERS_WIDTH, \ } #define TABLE_LINE_WIDTH ( \ TABLE_LINE_START_WIDTH + \ - TABLE_NAME_LVL_WIDTH + \ - TABLE_EXP_WIDTH + \ - TABLE_GOLD_WIDTH + \ - TABLE_PSYCHO_WIDTH + \ + TABLE_NAME_LVL_WIDTH + \ + TABLE_EXP_WIDTH + \ + TABLE_GOLD_WIDTH + \ + TABLE_PSYCHO_WIDTH + \ TABLE_SATURATION_WIDTH + \ - TABLE_SPLINTERS_WIDTH + \ + TABLE_SPLINTERS_WIDTH + \ (TABLE_COLUMN_COUNT - 1) * TABLE_LINE_SEPARATOR_WIDTH + \ - TABLE_LINE_END_WIDTH \ + TABLE_LINE_END_WIDTH \ ) // clang-format on diff --git a/analyzer/utils/roman.c b/analyzer/utils/roman.c new file mode 100644 index 0000000..9ea2bbb --- /dev/null +++ b/analyzer/utils/roman.c @@ -0,0 +1,16 @@ +#include "utils/roman.h" + +#include +#include + +void to_roman(char *buffer, int number) { + buffer[0] = '\0'; + int nums[ROMAN_CHAR_COUNT] = ROMAN_NUMS; + char *chars[ROMAN_CHAR_COUNT] = ROMAN_CHARS; + for (size_t i = 0; i < ROMAN_CHAR_COUNT; i++) { + for (size_t j = 0; j < number / nums[i]; j++) { + strcat(buffer, chars[i]); + } + number %= nums[i]; + } +} diff --git a/analyzer/utils/roman.h b/analyzer/utils/roman.h new file mode 100644 index 0000000..b4f5d3f --- /dev/null +++ b/analyzer/utils/roman.h @@ -0,0 +1,12 @@ +// utils/roman.h +// functions for roman numbers + +#define ROMAN_CHAR_COUNT 13 +#define ROMAN_NUMS {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1} +#define ROMAN_CHARS {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"} + +// convert a arabic number to roman number +// params: +// buffer - buffer to save the roman number to +// number - arabic number to convert +extern void to_roman(char *buffer, int number); diff --git a/csv/artifact_sizes.csv b/csv/artifact_sizes.csv new file mode 100644 index 0000000..a9af916 --- /dev/null +++ b/csv/artifact_sizes.csv @@ -0,0 +1,5 @@ +id,artifact_size +1,Sub +2,Bi +3,Magni +4,Arcy diff --git a/csv/orbs.csv b/csv/orbs.csv new file mode 100644 index 0000000..02dfde4 --- /dev/null +++ b/csv/orbs.csv @@ -0,0 +1,3 @@ +id,orb_name +21,Ha'at +12,Se'at -- cgit v1.2.3 From 5d463a7f4c721629a1996be6d19634338290a539 Mon Sep 17 00:00:00 2001 From: Mosder Date: Wed, 22 Jul 2026 01:16:45 +0200 Subject: refactor: generalized parser structs with offsetof and put them into separate files --- Makefile | 1 + analyzer/parser/fight_results/drif.h | 52 ++++ analyzer/parser/fight_results/entity.h | 67 +++++ analyzer/parser/fight_results/fight_results.c | 177 +++--------- analyzer/parser/fight_results/fight_results.h | 396 +------------------------- analyzer/parser/fight_results/gear.h | 45 +++ analyzer/parser/fight_results/gear_info.h | 44 +++ analyzer/parser/fight_results/orb.h | 33 +++ analyzer/parser/find_parse_functions.h | 6 +- analyzer/parser/parser.c | 3 +- analyzer/parser/parser_defs.h | 2 - analyzer/parser/structs.c | 49 ++++ analyzer/parser/structs.h | 33 +++ analyzer/render.c | 24 +- analyzer/render.h | 53 ++-- analyzer/utils/memory.h | 4 + analyzer/utils/roman.c | 13 +- analyzer/utils/roman.h | 23 +- 18 files changed, 429 insertions(+), 596 deletions(-) create mode 100644 analyzer/parser/fight_results/drif.h create mode 100644 analyzer/parser/fight_results/entity.h create mode 100644 analyzer/parser/fight_results/gear.h create mode 100644 analyzer/parser/fight_results/gear_info.h create mode 100644 analyzer/parser/fight_results/orb.h create mode 100644 analyzer/parser/structs.c create mode 100644 analyzer/parser/structs.h create mode 100644 analyzer/utils/memory.h (limited to 'analyzer/utils') diff --git a/Makefile b/Makefile index f45b9e6..31c1275 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ utils: parser: $(CC) $(CFLAGS) -c analyzer/parser/parser.c + $(CC) $(CFLAGS) -c analyzer/parser/structs.c $(CC) $(CFLAGS) -c analyzer/parser/fight_results/fight_results.c capture: diff --git a/analyzer/parser/fight_results/drif.h b/analyzer/parser/fight_results/drif.h new file mode 100644 index 0000000..1f8b3e2 --- /dev/null +++ b/analyzer/parser/fight_results/drif.h @@ -0,0 +1,52 @@ +// parser/fight_results/drif.h +// Drif generic struct data + +#include "parser/structs.h" + +// Drif structure: +// field (SEPARATOR_DRIF_FIELD field)* +// fields are defined in order in the Drif struct +#define SEPARATOR_DRIF "???" // TODO: change it to correct one which idk at this moment +#define SEPARATOR_DRIF_FIELD "," + +typedef struct { + char *field1; // just says "drif" + int field2; + int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy + char *field4; + int field5; + int id; // id of orb type + float value1; // value1, value2 (in %) - one is start value, the other is value per lvl (both are the same) + float value2; // value1, value2 (in %) - one is start value, the other is value per lvl (both are the same) + int field9; + int field10; + char *field11; + char *size_str; // sub - small, medium - bi, big - magni, ??? - arcy + int field13; + int power; // how much space it takes + int field15; + int field16; + char *name; // drif + char *field18; +} Drif; + +static const StructField drif_fields[] = { + {STRING, offsetof(Drif, field1)}, + {INT, offsetof(Drif, field2)}, + {INT, offsetof(Drif, size)}, + {STRING, offsetof(Drif, field4)}, + {INT, offsetof(Drif, field5)}, + {INT, offsetof(Drif, id)}, + {FLOAT, offsetof(Drif, value1)}, + {FLOAT, offsetof(Drif, value2)}, + {INT, offsetof(Drif, field9)}, + {INT, offsetof(Drif, field10)}, + {STRING, offsetof(Drif, field11)}, + {STRING, offsetof(Drif, size_str)}, + {INT, offsetof(Drif, field13)}, + {INT, offsetof(Drif, power)}, + {INT, offsetof(Drif, field15)}, + {INT, offsetof(Drif, field16)}, + {STRING, offsetof(Drif, name)}, + {STRING, offsetof(Drif, field18)}, +}; diff --git a/analyzer/parser/fight_results/entity.h b/analyzer/parser/fight_results/entity.h new file mode 100644 index 0000000..8e082ec --- /dev/null +++ b/analyzer/parser/fight_results/entity.h @@ -0,0 +1,67 @@ +// parser/fight_results/entity.h +// EntityResults generic struct data + +#include "parser/structs.h" + +// EntityResults structure: +// field (SEPARATOR_ENTITY_FIELD field)* +// fields are defined in order in the EntityResults struct +#define SEPARATOR_ENTITY "[--]" +#define SEPARATOR_ENTITY_FIELD "&" + +// values of field "team" in EntityResults +#define TEAM_FRIEND 1 +#define TEAM_ENEMY 2 + +typedef struct { + int team; // friend or enemy + char *name; + int exp; + float field4; + int gold; + float field6; + char *field7; + char *gear; // rares, sets, syngs, orbs + int field9; + char *items; // regular items + int field11; + int field12; + float field13; + float field14; // some multiplier most likely exp/gold/psycho/something else idk + float field15; + int level; + double field17; + float field18; + int field19; + int field20; + float field21; + float field22; + int field23; + int field24; + int psycho; + char *drifs; + int splinters; + int field28; + char *field29; + int field30; + int field31; + int field32; + int field33; + char *field34; + char *saturation; +} EntityResults; + +static const StructField entity_fields[] = { + {INT, offsetof(EntityResults, team)}, {STRING, offsetof(EntityResults, name)}, {INT, offsetof(EntityResults, exp)}, + {FLOAT, offsetof(EntityResults, field4)}, {INT, offsetof(EntityResults, gold)}, {FLOAT, offsetof(EntityResults, field6)}, + {STRING, offsetof(EntityResults, field7)}, {STRING, offsetof(EntityResults, gear)}, {INT, offsetof(EntityResults, field9)}, + {STRING, offsetof(EntityResults, items)}, {INT, offsetof(EntityResults, field11)}, {INT, offsetof(EntityResults, field12)}, + {FLOAT, offsetof(EntityResults, field13)}, {FLOAT, offsetof(EntityResults, field14)}, {FLOAT, offsetof(EntityResults, field15)}, + {INT, offsetof(EntityResults, level)}, {DOUBLE, offsetof(EntityResults, field17)}, {FLOAT, offsetof(EntityResults, field18)}, + {INT, offsetof(EntityResults, field19)}, {INT, offsetof(EntityResults, field20)}, {FLOAT, offsetof(EntityResults, field21)}, + {FLOAT, offsetof(EntityResults, field22)}, {INT, offsetof(EntityResults, field23)}, {INT, offsetof(EntityResults, field24)}, + {INT, offsetof(EntityResults, psycho)}, {STRING, offsetof(EntityResults, drifs)}, {INT, offsetof(EntityResults, splinters)}, + {INT, offsetof(EntityResults, field28)}, {STRING, offsetof(EntityResults, field29)}, {INT, offsetof(EntityResults, field30)}, + {INT, offsetof(EntityResults, field31)}, {INT, offsetof(EntityResults, field32)}, {INT, offsetof(EntityResults, field33)}, + {STRING, offsetof(EntityResults, field34)}, {STRING, offsetof(EntityResults, saturation)}, +}; diff --git a/analyzer/parser/fight_results/fight_results.c b/analyzer/parser/fight_results/fight_results.c index 4f0f3a1..e7c7123 100644 --- a/analyzer/parser/fight_results/fight_results.c +++ b/analyzer/parser/fight_results/fight_results.c @@ -8,6 +8,13 @@ #include "csv/id_to_item_name.h" #include "csv/id_to_orb_name.h" #include "csv/id_to_saturation_type.h" +#include "parser/fight_results/drif.h" +#include "parser/fight_results/entity.h" +#include "parser/fight_results/gear.h" +#include "parser/fight_results/gear_info.h" +#include "parser/fight_results/orb.h" +#include "parser/structs.h" +#include "utils/memory.h" #include "utils/roman.h" // function to cleanup fight results to be used in ParsedData struct @@ -29,126 +36,20 @@ byte_t *find_fight_results(TCPStream *stream, int offset, int *length) { byte_t *start = memmem(stream->data + offset, stream->len - offset, FIGHT_RESULTS_START, strlen(FIGHT_RESULTS_START)); byte_t *end = NULL; if (start) - end = memmem(start, stream->len - offset, FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); + end = memmem(start, stream->len - (start - stream->data), FIGHT_RESULTS_END, strlen(FIGHT_RESULTS_END)); *length = end ? end - start + strlen(FIGHT_RESULTS_END) : -1; return start; } -// save to a field in struct depending on the type -void save_to_struct(TypeEnum type, void *field, char *src, size_t src_len) { - char *str = malloc(src_len + 1); - memcpy(str, src, src_len); - str[src_len] = '\0'; - - // parse depending on variable type - switch (type) { - case INT: - sscanf(str, "%d", (int *)field); - free(str); - break; - case FLOAT: - sscanf(str, "%f", (float *)field); - free(str); - break; - case DOUBLE: - sscanf(str, "%lf", (double *)field); - free(str); - break; - case STRING: - *(char **)field = str; - break; - } -} - -// fill struct fields from given data of given length -void get_struct_data(size_t field_count, TypeEnum *field_types, void **field_pointers, char *data_separator, char *data, size_t data_len) { - for (size_t i = 0; i < field_count; i++) { - char *sep = memmem(data, data_len, data_separator, strlen(data_separator)); - size_t len = sep ? sep - data : data_len; - save_to_struct(field_types[i], field_pointers[i], (char *)data, len); - - // update data pointer and length - if (sep) { - data_len -= sep - data + strlen(data_separator); - data = sep + strlen(data_separator); - } - } -} - -// free struct fields which are STRING -void destroy_struct(size_t field_count, TypeEnum *field_types, void **field_pointers) { - for (size_t i = 0; i < field_count; i++) { - if (field_types[i] == STRING) - free(*(char **)field_pointers[i]); - } -} - -void destroy_entity_results(EntityResults results) { - TypeEnum field_types[ENTITY_FIELD_COUNT] = ENTITY_FIELD_TYPES; - void *field_pointers[ENTITY_FIELD_COUNT] = ENTITY_FIELD_ADDRESSES(results); - destroy_struct(ENTITY_FIELD_COUNT, field_types, field_pointers); -} - -// get entity results from the string -EntityResults get_entity_results(byte_t *data, size_t length) { - EntityResults results; - TypeEnum field_types[ENTITY_FIELD_COUNT] = ENTITY_FIELD_TYPES; - void *field_pointers[ENTITY_FIELD_COUNT] = ENTITY_FIELD_ADDRESSES(results); - get_struct_data(ENTITY_FIELD_COUNT, field_types, field_pointers, SEPARATOR_RESULT, (char *)data, length); - return results; -} - Item *expand_items(Item *items, size_t *items_capacity, size_t to_fit) { while (*items_capacity < to_fit) *items_capacity <<= 1; return realloc(items, *items_capacity * sizeof(Item)); } -void destroy_gear(Gear gear) { - TypeEnum field_types[GEAR_FIELD_COUNT] = GEAR_FIELD_TYPES; - void *field_pointers[GEAR_FIELD_COUNT] = GEAR_FIELD_ADDRESSES(gear); - destroy_struct(GEAR_FIELD_COUNT, field_types, field_pointers); -} - -Gear get_gear(char *data, size_t length) { - Gear gear; - TypeEnum field_types[GEAR_FIELD_COUNT] = GEAR_FIELD_TYPES; - void *field_pointers[GEAR_FIELD_COUNT] = GEAR_FIELD_ADDRESSES(gear); - get_struct_data(GEAR_FIELD_COUNT, field_types, field_pointers, SEPARATOR_GEAR_FIELDS, data, length); - return gear; -} - -void destroy_gear_info(GearInfo gear_info) { - TypeEnum field_types[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_TYPES; - void *field_pointers[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_ADDRESSES(gear_info); - destroy_struct(GEAR_INFO_FIELD_COUNT, field_types, field_pointers); -} - -GearInfo get_gear_info(char *data, size_t length) { - GearInfo gear_info; - TypeEnum field_types[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_TYPES; - void *field_pointers[GEAR_INFO_FIELD_COUNT] = GEAR_INFO_FIELD_ADDRESSES(gear_info); - get_struct_data(GEAR_INFO_FIELD_COUNT, field_types, field_pointers, SEPARATOR_GEAR_INFO_FIELDS, data, length); - return gear_info; -} - -void destroy_orb(Orb orb) { - TypeEnum field_types[ORB_FIELD_COUNT] = ORB_FIELD_TYPES; - void *field_pointers[ORB_FIELD_COUNT] = ORB_FIELD_ADDRESSES(orb); - destroy_struct(ORB_FIELD_COUNT, field_types, field_pointers); -} - -Orb get_orb(char *data, size_t length) { - Orb orb; - TypeEnum field_types[ORB_FIELD_COUNT] = ORB_FIELD_TYPES; - void *field_pointers[ORB_FIELD_COUNT] = ORB_FIELD_ADDRESSES(orb); - get_struct_data(ORB_FIELD_COUNT, field_types, field_pointers, SEPARATOR_ORB_FIELDS, data, length); - return orb; -} - void get_stars(char *buffer, int incr_above_b1) { char star_types[] = {'B', 'S', 'G'}; - sprintf(buffer, " (%c%d)", star_types[incr_above_b1 / 3], incr_above_b1 % 3 + '1'); + sprintf(buffer, " (%c%d)", star_types[incr_above_b1 / 3], incr_above_b1 % 3 + 1); } void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) { @@ -157,16 +58,19 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) char *gears_separator; do { - gears_separator = strstr(gears_str, SEPARATOR_GEARS); - Gear gear = get_gear(gears_str, gears_separator ? gears_separator - gears_str : strlen(gears_str)); + gears_separator = strstr(gears_str, SEPARATOR_GEAR); + size_t len = gears_separator ? gears_separator - gears_str : strlen(gears_str); + Gear gear = {0}; + fill_struct(&gear, gear_fields, ARR_LEN(gear_fields), gears_str, len, SEPARATOR_GEAR_FIELD); // if there are still gears - modify gears_str pointer if (gears_separator) - gears_str = gears_separator + strlen(SEPARATOR_GEARS); + gears_str = gears_separator + strlen(SEPARATOR_GEAR); Item item; - GearInfo gear_info = get_gear_info(gear.info, strlen(gear.info)); + GearInfo gear_info = {0}; + fill_struct(&gear_info, gear_info_fields, ARR_LEN(gear_info_fields), gear.info, strlen(gear.info), SEPARATOR_GEAR_INFO_FIELD); char buffer[SINGLE_ITEM_BUFFER_LEN]; char roman[5]; to_roman(roman, gear_info.rank); @@ -174,8 +78,11 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) switch (gear.type) { case TYPE_SYNG_NORMAL: // atoi should be fine, since syng_tier starts from 1 for syngs - if (atoi(gear.syng_tier)) - sprintf(buffer, "%s (%d)", buffer, gear_info.lvl); + if (atoi(gear.syng_tier)) { + char lvl[7]; + sprintf(lvl, " (%d)", gear_info.lvl); + strcat(buffer, lvl); + } break; default: { char stars[6]; @@ -185,7 +92,7 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) } } item.data = strdup(buffer); - destroy_gear_info(gear_info); + cleanup_struct(&gear_info, gear_info_fields, ARR_LEN(gear_info_fields)); switch (gear.type) { case TYPE_SYNG_NORMAL: @@ -212,10 +119,11 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) if (strlen(gear.orb)) { Item item; char buffer[SINGLE_ITEM_BUFFER_LEN]; - Orb orb = get_orb(gear.orb, strlen(gear.orb)); + Orb orb = {0}; + fill_struct(&orb, orb_fields, ARR_LEN(orb_fields), gear.orb, strlen(gear.orb), SEPARATOR_ORB_FIELD); sprintf(buffer, "%sorb %s", get_artifact_size_from_id(orb.size), get_orb_name_from_id(orb.id)); item.data = strdup(buffer); - destroy_orb(orb); + cleanup_struct(&orb, orb_fields, ARR_LEN(orb_fields)); item.type = ORB; // expand if needed if (results->num_items >= *items_capacity) @@ -223,47 +131,35 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) results->items[results->num_items++] = item; } - destroy_gear(gear); + cleanup_struct(&gear, gear_fields, ARR_LEN(gear_fields)); } while (gears_separator); } -void destroy_drif(Drif drif) { - TypeEnum field_types[DRIF_FIELD_COUNT] = DRIF_FIELD_TYPES; - void *field_pointers[DRIF_FIELD_COUNT] = DRIF_FIELD_ADDRESSES(drif); - destroy_struct(DRIF_FIELD_COUNT, field_types, field_pointers); -} - -Drif get_drif(char *data, size_t length) { - Drif drif; - TypeEnum field_types[DRIF_FIELD_COUNT] = DRIF_FIELD_TYPES; - void *field_pointers[DRIF_FIELD_COUNT] = DRIF_FIELD_ADDRESSES(drif); - get_struct_data(DRIF_FIELD_COUNT, field_types, field_pointers, SEPARATOR_DRIF_FIELDS, data, length); - return drif; -} - void add_drifs(FriendlyResults *results, size_t *items_capacity, char *drifs_str) { if (strlen(drifs_str) == 0) return; char *drifs_separator; do { - drifs_separator = strstr(drifs_str, SEPARATOR_DRIFS); - Drif drif = get_drif(drifs_str, drifs_separator ? drifs_separator - drifs_str : strlen(drifs_str)); + drifs_separator = strstr(drifs_str, SEPARATOR_DRIF); + size_t len = drifs_separator ? drifs_separator - drifs_str : strlen(drifs_str); + Drif drif = {0}; + fill_struct(&drif, drif_fields, ARR_LEN(drif_fields), drifs_str, len, SEPARATOR_DRIF_FIELD); // if there are still drifs - modify drifs_str pointer if (drifs_separator) - drifs_str = drifs_separator + strlen(SEPARATOR_DRIFS); + drifs_str = drifs_separator + strlen(SEPARATOR_DRIF); Item item; item.data = strdup(drif.name); item.type = DRIF; + cleanup_struct(&drif, drif_fields, ARR_LEN(drif_fields)); + // expand if needed if (results->num_items >= *items_capacity) results->items = expand_items(results->items, items_capacity, results->num_items + 1); results->items[results->num_items++] = item; - - destroy_drif(drif); } while (drifs_separator); } @@ -350,7 +246,9 @@ ParsedData parse_fight_results(byte_t *data, size_t length) { do { // get generic entity results entity_separator = memmem(data, length, SEPARATOR_ENTITY, strlen(SEPARATOR_ENTITY)); - EntityResults entity_results = get_entity_results(data, entity_separator ? entity_separator - data : length); + size_t len = entity_separator ? entity_separator - data : length; + EntityResults entity_results = {0}; + fill_struct(&entity_results, entity_fields, ARR_LEN(entity_fields), (char *)data, len, SEPARATOR_ENTITY_FIELD); // depending on team - parse it into friendly or enemy results switch (entity_results.team) { @@ -361,7 +259,8 @@ ParsedData parse_fight_results(byte_t *data, size_t length) { results->enemy_results[results->num_enemy++] = parse_to_enemy(entity_results); break; } - destroy_entity_results(entity_results); + + cleanup_struct(&entity_results, entity_fields, ARR_LEN(entity_fields)); // if there are still entities - modify data pointer and length if (entity_separator) { diff --git a/analyzer/parser/fight_results/fight_results.h b/analyzer/parser/fight_results/fight_results.h index 0618bf2..04add96 100644 --- a/analyzer/parser/fight_results/fight_results.h +++ b/analyzer/parser/fight_results/fight_results.h @@ -11,137 +11,12 @@ // FIGHT_RESULTS_START EntityResults (SEPARATOR_ENTITY EntityResults)* FIGHT_RESULTS_END #define FIGHT_RESULTS_START "3;19;" #define FIGHT_RESULTS_END "[--];" -#define SEPARATOR_ENTITY "[--]" -#define SEPARATOR_RESULT "&" -// values of field "team" in EntityResults -#define TEAM_FRIEND 1 -#define TEAM_ENEMY 2 - -// EntityResults structure: -// field (SEPARATOR_RESULT field)* -// fields are defined in order in the EntityResults struct -#define ENTITY_FIELD_COUNT 35 -// clang-format off -#define ENTITY_FIELD_TYPES { \ - /* team */ INT, \ - /* name */ STRING, \ - /* exp */ INT, \ - /* field4 */ FLOAT, \ - /* gold */ INT, \ - /* field6 */ FLOAT, \ - /* field7 */ STRING, \ - /* gear */ STRING, \ - /* field9 */ INT, \ - /* items */ STRING, \ - /* field11 */ INT, \ - /* field12 */ INT, \ - /* field13 */ FLOAT, \ - /* field14 */ FLOAT, \ - /* field15 */ FLOAT, \ - /* level */ INT, \ - /* field17 */ DOUBLE, \ - /* field18 */ FLOAT, \ - /* field19 */ INT, \ - /* field20 */ INT, \ - /* field21 */ FLOAT, \ - /* field22 */ FLOAT, \ - /* field23 */ INT, \ - /* field24 */ INT, \ - /* psycho */ INT, \ - /* drifs */ STRING, \ - /* splinters */ INT, \ - /* field28 */ INT, \ - /* field29 */ STRING, \ - /* field30 */ INT, \ - /* field31 */ INT, \ - /* field32 */ INT, \ - /* field33 */ INT, \ - /* field34 */ STRING, \ - /* saturation */ STRING, \ -} -#define ENTITY_FIELD_ADDRESSES(entity_results) {\ - &entity_results.team, \ - &entity_results.name, \ - &entity_results.exp, \ - &entity_results.field4, \ - &entity_results.gold, \ - &entity_results.field6, \ - &entity_results.field7, \ - &entity_results.gear, \ - &entity_results.field9, \ - &entity_results.items, \ - &entity_results.field11, \ - &entity_results.field12, \ - &entity_results.field13, \ - &entity_results.field14, \ - &entity_results.field15, \ - &entity_results.level, \ - &entity_results.field17, \ - &entity_results.field18, \ - &entity_results.field19, \ - &entity_results.field20, \ - &entity_results.field21, \ - &entity_results.field22, \ - &entity_results.field23, \ - &entity_results.field24, \ - &entity_results.psycho, \ - &entity_results.drifs, \ - &entity_results.splinters, \ - &entity_results.field28, \ - &entity_results.field29, \ - &entity_results.field30, \ - &entity_results.field31, \ - &entity_results.field32, \ - &entity_results.field33, \ - &entity_results.field34, \ - &entity_results.saturation, \ -} -// clang-format on -typedef struct { - int team; // friend or enemy - char *name; - int exp; - float field4; - int gold; - float field6; - char *field7; - char *gear; // rares, sets, syngs, orbs - int field9; - char *items; // regular items - int field11; - int field12; - float field13; - float field14; // some multiplier most likely exp/gold/psycho/something else idk - float field15; - int level; - double field17; - float field18; - int field19; - int field20; - float field21; - float field22; - int field23; - int field24; - int psycho; - char *drifs; - int splinters; - int field28; - char *field29; - int field30; - int field31; - int field32; - int field33; - char *field34; - char *saturation; -} EntityResults; - -// items/gear/drifs fields parsing info +// items parsing info #define INIT_ITEMS_CAPACITY 8 #define SINGLE_ITEM_BUFFER_LEN 128 #define SEPARATOR_ITEMS "[-]" -#define SEPARATOR_DRIFS "???" // TODO: change it to correct one which idk at this moment -#define SEPARATOR_GEARS "???" // TODO: change it to correct one which idk at this moment + // Item struct - for keeping item string and type typedef enum { NORMAL, DRIF, ORB, SYNG, SET, RARE, EPIC } ItemType; typedef struct { @@ -149,273 +24,6 @@ typedef struct { ItemType type; } Item; -// Drif structure: -// field (SEPARATOR_DRIF_FIELDS field)* -// fields are defined in order in the Drif struct -#define DRIF_FIELD_COUNT 18 -#define SEPARATOR_DRIF_FIELDS "," -// clang-format off -#define DRIF_FIELD_TYPES { \ - /* field1 */ STRING, \ - /* field2 */ INT, \ - /* size */ INT, \ - /* field4 */ STRING, \ - /* field5 */ INT, \ - /* id */ INT, \ - /* value1 */ FLOAT, \ - /* value2 */ FLOAT, \ - /* field9 */ INT, \ - /* field10 */ INT, \ - /* field11 */ STRING, \ - /* size_str */ STRING, \ - /* field13 */ INT, \ - /* power */ INT, \ - /* field15 */ INT, \ - /* field16 */ INT, \ - /* name */ STRING, \ - /* field18 */ STRING, \ -} -#define DRIF_FIELD_ADDRESSES(drif) {\ - &drif.field1, \ - &drif.field2, \ - &drif.size, \ - &drif.field4, \ - &drif.field5, \ - &drif.id, \ - &drif.value1, \ - &drif.value2, \ - &drif.field9, \ - &drif.field10, \ - &drif.field11, \ - &drif.size_str, \ - &drif.field13, \ - &drif.power, \ - &drif.field15, \ - &drif.field16, \ - &drif.name, \ - &drif.field18, \ -} -// clang-format on -typedef struct { - char *field1; // just says "drif" - int field2; - int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy - char *field4; - int field5; - int id; // id of orb type - float value1; // value1, value2 (in %) - one is start value, the other is value per lvl (both are the same) - float value2; // value1, value2 (in %) - one is start value, the other is value per lvl (both are the same) - int field9; - int field10; - char *field11; - char *size_str; // sub - small, medium - bi, big - magni, ??? - arcy - int field13; - int power; // how much space it takes - int field15; - int field16; - char *name; // drif - char *field18; -} Drif; - -// Gear structure: -// field (SEPARATOR_GEAR_FIELDS field)* -// fields are defined in order in the Gear struct -#define GEAR_FIELD_COUNT 19 -#define SEPARATOR_GEAR_FIELDS "$" -// values of field "type" in Gear -#define TYPE_SYNG_NORMAL 0 -#define TYPE_SET 1 -#define TYPE_RARE 2 -// clang-format off -#define GEAR_FIELD_TYPES { \ - /* stats */ STRING, \ - /* resistances */ STRING, \ - /* info */ STRING, \ - /* reqs */ STRING, \ - /* field5 */ STRING, \ - /* psycho */ STRING, \ - /* field7 */ STRING, \ - /* field8 */ STRING, \ - /* field9 */ STRING, \ - /* syng_tier */ STRING, \ - /* field11 */ INT, \ - /* field12 */ INT, \ - /* field13 */ INT, \ - /* field14 */ STRING, \ - /* type */ INT, \ - /* field16 */ INT, \ - /* orb */ STRING, \ - /* field18 */ STRING, \ - /* field19 */ STRING, \ -} -#define GEAR_FIELD_ADDRESSES(gear) {\ - &gear.stats, \ - &gear.resistances, \ - &gear.info, \ - &gear.reqs, \ - &gear.field5, \ - &gear.psycho, \ - &gear.field7, \ - &gear.field8, \ - &gear.field9, \ - &gear.syng_tier, \ - &gear.field11, \ - &gear.field12, \ - &gear.field13, \ - &gear.field14, \ - &gear.type, \ - &gear.field16, \ - &gear.orb, \ - &gear.field18, \ - &gear.field19, \ -} -// clang-format on -typedef struct { - char *stats; // strength, dexterity, power, knowledge, hp, mana, stamina - char *resistances; // slashing, crushing, piercing, fire, cold, energy, mental - char *info; // GearInfo struct data - char *reqs; // requirements (str, dex, pow, know), weight, something else - char *field5; - char *psycho; // psycho effects (id,%) for syngs - char *field7; - char *field8; - char *field9; - char *syng_tier; // lvl 20 - 1, lvl 30 - 2, etc..., null for non-syngs - int field11; - int field12; - int field13; - char *field14; - int type; // 0 - normal item or syng, 1 - set, 2 - rare - int field16; // 10 for rare, -1 for others ??? - char *orb; // orb info - char *field18; - char *field19; -} Gear; - -// GearInfo structure: -// field (SEPARATOR_GEAR_INFO_FIELDS field)* -// fields are defined in order in the GearInfo struct -#define GEAR_INFO_FIELD_COUNT 23 -#define SEPARATOR_GEAR_INFO_FIELDS "," -// clang-format off -#define GEAR_INFO_FIELD_TYPES { \ - /* name */ STRING, \ - /* field2 */ INT, \ - /* lvl */ INT, \ - /* price */ INT, \ - /* field5 */ INT, \ - /* field6 */ INT, \ - /* field7 */ INT, \ - /* rank */ INT, \ - /* field9 */ INT, \ - /* field10 */ INT, \ - /* field11 */ INT, \ - /* durability */ INT, \ - /* field13 */ STRING, \ - /* field14 */ INT, \ - /* field15 */ INT, \ - /* charges */ FLOAT, \ - /* field17 */ STRING, \ - /* field18 */ INT, \ - /* stars */ INT, \ - /* field20 */ INT, \ - /* field21 */ INT, \ - /* field22 */ INT, \ - /* field23 */ INT, \ -} -#define GEAR_INFO_FIELD_ADDRESSES(gear_info) {\ - &gear_info.name, \ - &gear_info.field2, \ - &gear_info.lvl, \ - &gear_info.price, \ - &gear_info.field5, \ - &gear_info.field6, \ - &gear_info.field7, \ - &gear_info.rank, \ - &gear_info.field9, \ - &gear_info.field10, \ - &gear_info.field11, \ - &gear_info.durability, \ - &gear_info.field13, \ - &gear_info.field14, \ - &gear_info.field15, \ - &gear_info.charges, \ - &gear_info.field17, \ - &gear_info.field18, \ - &gear_info.stars, \ - &gear_info.field20, \ - &gear_info.field21, \ - &gear_info.field22, \ - &gear_info.field23, \ -} -// clang-format on -typedef struct { - char *name; // item name - int field2; - int lvl; // level requirement - int price; // gold price value - int field5; - int field6; - int field7; - int rank; // I-XII - int field9; - int field10; - int field11; - int durability; // in % - char *field13; - int field14; - int field15; - float charges; // charge count - char *field17; - int field18; - int stars; // incrustation level: 0 - b1, 1 - b2, ... (-100 for syngs) - int field20; - int field21; - int field22; - int field23; -} GearInfo; - -// Orb structure: -// field (SEPARATOR_ORB_FIELDS field)* -// fields are defined in order in the Orb struct -#define ORB_FIELD_COUNT 9 -#define SEPARATOR_ORB_FIELDS "," -// clang-format off -#define ORB_FIELD_TYPES { \ - /* field1 */ STRING, \ - /* field2 */ INT, \ - /* field3 */ INT, \ - /* field4 */ STRING, \ - /* field5 */ INT, \ - /* id */ INT, \ - /* size */ INT, \ - /* value */ FLOAT, \ - /* field9 */ STRING, \ -} -#define ORB_FIELD_ADDRESSES(orb) {\ - &orb.field1, \ - &orb.field2, \ - &orb.field3, \ - &orb.field4, \ - &orb.field5, \ - &orb.id, \ - &orb.size, \ - &orb.value, \ - &orb.field9, \ -} -// clang-format on -typedef struct { - char *field1; // just says "orb" - int field2; - int field3; - char *field4; - int field5; - int id; // id of orb type - int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy - float value; // value in % you gain from the orb - char *field9; -} Orb; - // fight results data for friendly typedef struct { char *name; diff --git a/analyzer/parser/fight_results/gear.h b/analyzer/parser/fight_results/gear.h new file mode 100644 index 0000000..66f52f3 --- /dev/null +++ b/analyzer/parser/fight_results/gear.h @@ -0,0 +1,45 @@ +// parser/fight_results/gear.h +// Gear generic struct data + +#include "parser/structs.h" + +// Gear structure: +// field (SEPARATOR_GEAR_FIELD field)* +// fields are defined in order in the Gear struct +#define SEPARATOR_GEAR "???" // TODO: change it to correct one which idk at this moment +#define SEPARATOR_GEAR_FIELD "$" + +// values of field "type" in Gear +#define TYPE_SYNG_NORMAL 0 +#define TYPE_SET 1 +#define TYPE_RARE 2 + +typedef struct { + char *stats; // strength, dexterity, power, knowledge, hp, mana, stamina + char *resistances; // slashing, crushing, piercing, fire, cold, energy, mental + char *info; // GearInfo struct data + char *reqs; // requirements (str, dex, pow, know), weight, something else + char *field5; + char *psycho; // psycho effects (id,%) for syngs + char *field7; + char *field8; + char *field9; + char *syng_tier; // lvl 20 - 1, lvl 30 - 2, etc..., null for non-syngs + int field11; + int field12; + int field13; + char *field14; + int type; // 0 - normal item or syng, 1 - set, 2 - rare + int field16; // 10 for rare, -1 for others ??? + char *orb; // orb info + char *field18; + char *field19; +} Gear; + +static const StructField gear_fields[] = { + {STRING, offsetof(Gear, stats)}, {STRING, offsetof(Gear, resistances)}, {STRING, offsetof(Gear, info)}, {STRING, offsetof(Gear, reqs)}, + {STRING, offsetof(Gear, field5)}, {STRING, offsetof(Gear, psycho)}, {STRING, offsetof(Gear, field7)}, {STRING, offsetof(Gear, field8)}, + {STRING, offsetof(Gear, field9)}, {STRING, offsetof(Gear, syng_tier)}, {INT, offsetof(Gear, field11)}, {INT, offsetof(Gear, field12)}, + {INT, offsetof(Gear, field13)}, {STRING, offsetof(Gear, field14)}, {INT, offsetof(Gear, type)}, {INT, offsetof(Gear, field16)}, + {STRING, offsetof(Gear, orb)}, {STRING, offsetof(Gear, field18)}, {STRING, offsetof(Gear, field19)}, +}; diff --git a/analyzer/parser/fight_results/gear_info.h b/analyzer/parser/fight_results/gear_info.h new file mode 100644 index 0000000..f0cf83a --- /dev/null +++ b/analyzer/parser/fight_results/gear_info.h @@ -0,0 +1,44 @@ +// parser/fight_results/gear_info.h +// GearInfo generic struct data + +#include "parser/structs.h" + +// GearInfo structure: +// field (SEPARATOR_GEAR_INFO_FIELD field)* +// fields are defined in order in the GearInfo struct +#define SEPARATOR_GEAR_INFO_FIELD "," + +typedef struct { + char *name; // item name + int field2; + int lvl; // level requirement + int price; // gold price value + int field5; + int field6; + int field7; + int rank; // I-XII + int field9; + int field10; + int field11; + int durability; // in % + char *field13; + int field14; + int field15; + float charges; // charge count + char *field17; + int field18; + int stars; // incrustation level: 0 - b1, 1 - b2, ... (-100 for syngs) + int field20; + int field21; + int field22; + int field23; +} GearInfo; + +static const StructField gear_info_fields[] = { + {STRING, offsetof(GearInfo, name)}, {INT, offsetof(GearInfo, field2)}, {INT, offsetof(GearInfo, lvl)}, {INT, offsetof(GearInfo, price)}, + {INT, offsetof(GearInfo, field5)}, {INT, offsetof(GearInfo, field6)}, {INT, offsetof(GearInfo, field7)}, {INT, offsetof(GearInfo, rank)}, + {INT, offsetof(GearInfo, field9)}, {INT, offsetof(GearInfo, field10)}, {INT, offsetof(GearInfo, field11)}, {INT, offsetof(GearInfo, durability)}, + {STRING, offsetof(GearInfo, field13)}, {INT, offsetof(GearInfo, field14)}, {INT, offsetof(GearInfo, field15)}, {FLOAT, offsetof(GearInfo, charges)}, + {STRING, offsetof(GearInfo, field17)}, {INT, offsetof(GearInfo, field18)}, {INT, offsetof(GearInfo, stars)}, {INT, offsetof(GearInfo, field20)}, + {INT, offsetof(GearInfo, field21)}, {INT, offsetof(GearInfo, field22)}, {INT, offsetof(GearInfo, field23)}, +}; diff --git a/analyzer/parser/fight_results/orb.h b/analyzer/parser/fight_results/orb.h new file mode 100644 index 0000000..dcf146f --- /dev/null +++ b/analyzer/parser/fight_results/orb.h @@ -0,0 +1,33 @@ +// parser/fight_results/orb.h +// Orb generic struct data + +#include "parser/structs.h" + +// Orb structure: +// field (SEPARATOR_ORB_FIELD field)* +// fields are defined in order in the Orb struct +#define SEPARATOR_ORB_FIELD "," + +typedef struct { + char *field1; // just says "orb" + int field2; + int field3; + char *field4; + int field5; + int id; // id of orb type + int size; // 1 - sub, 2 - bi, 3 - magni, 4 - arcy + float value; // value in % you gain from the orb + char *field9; +} Orb; + +static const StructField orb_fields[] = { + {STRING, offsetof(Orb, field1)}, + {INT, offsetof(Orb, field2)}, + {INT, offsetof(Orb, field3)}, + {STRING, offsetof(Orb, field4)}, + {INT, offsetof(Orb, field5)}, + {INT, offsetof(Orb, id)}, + {INT, offsetof(Orb, size)}, + {FLOAT, offsetof(Orb, value)}, + {STRING, offsetof(Orb, field9)}, +}; diff --git a/analyzer/parser/find_parse_functions.h b/analyzer/parser/find_parse_functions.h index 1b6ce9e..e2c2acb 100644 --- a/analyzer/parser/find_parse_functions.h +++ b/analyzer/parser/find_parse_functions.h @@ -3,8 +3,4 @@ #include "parser/fight_results/fight_results.h" -#define RESULT_TYPE_COUNT 1 - -FindParseFunctions all_find_parse_functions[RESULT_TYPE_COUNT] = { - {.find_fn = find_fight_results, .parse_fn = parse_fight_results} -}; +FindParseFunctions all_find_parse_functions[] = {{find_fight_results, parse_fight_results}}; diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c index 3ef48b5..87ab4c0 100644 --- a/analyzer/parser/parser.c +++ b/analyzer/parser/parser.c @@ -3,6 +3,7 @@ #include #include "parser/find_parse_functions.h" +#include "utils/memory.h" // expand parsed data exponentially to fit el_count elements ParsedData *expand_parsed_data(ParsedData *parsed_data, size_t *capacity, size_t el_count) { @@ -14,7 +15,7 @@ ParsedData *expand_parsed_data(ParsedData *parsed_data, size_t *capacity, size_t // find earliest data to parse in stream byte_t *find_earliest_data(TCPStream *stream, int stream_offset, int *earliest_length, parse_function_t **earliest_parse_fun) { byte_t *earliest_data = NULL; - for (size_t i = 0; i < RESULT_TYPE_COUNT; i++) { + for (size_t i = 0; i < ARR_LEN(all_find_parse_functions); i++) { int length; byte_t *data = all_find_parse_functions[i].find_fn(stream, stream_offset, &length); // if it's the first found or earlier than earliest - save it diff --git a/analyzer/parser/parser_defs.h b/analyzer/parser/parser_defs.h index aa39a4c..75b0366 100644 --- a/analyzer/parser/parser_defs.h +++ b/analyzer/parser/parser_defs.h @@ -6,8 +6,6 @@ #ifndef PARSER_DEFS_H #define PARSER_DEFS_H -typedef enum { INT, FLOAT, DOUBLE, STRING } TypeEnum; - typedef enum { TYPE_FIGHT_RESULTS } ResultsEnum; // "generic" type for parsed data diff --git a/analyzer/parser/structs.c b/analyzer/parser/structs.c new file mode 100644 index 0000000..c59fa82 --- /dev/null +++ b/analyzer/parser/structs.c @@ -0,0 +1,49 @@ +#include "parser/structs.h" + +#include +#include +#include + +void fill_struct(void *obj, const StructField *fields, size_t n_fields, const char *data, size_t data_len, const char *sep) { + for (size_t i = 0; i < n_fields; i++) { + char *next_sep = memmem(data, data_len, sep, strlen(sep)); + size_t len = next_sep ? next_sep - data : data_len; + + char *str = malloc(len + 1); + memcpy(str, data, len); + str[len] = '\0'; + + // parse depending on variable type + void *p_field = (char *)obj + fields[i].offset; + switch (fields[i].type) { + case INT: + sscanf(str, "%d", (int *)p_field); + free(str); + break; + case FLOAT: + sscanf(str, "%f", (float *)p_field); + free(str); + break; + case DOUBLE: + sscanf(str, "%lf", (double *)p_field); + free(str); + break; + case STRING: + *(char **)p_field = str; + break; + } + + // update data pointer and length + if (next_sep) { + data_len -= next_sep - data + strlen(sep); + data = next_sep + strlen(sep); + } + } +} + +void cleanup_struct(void *obj, const StructField *fields, size_t n_fields) { + for (size_t i = 0; i < n_fields; i++) { + if (fields[i].type == STRING) + free(*(char **)((char *)obj + fields[i].offset)); + } +} diff --git a/analyzer/parser/structs.h b/analyzer/parser/structs.h new file mode 100644 index 0000000..023c19b --- /dev/null +++ b/analyzer/parser/structs.h @@ -0,0 +1,33 @@ +// parser/structs.h +// generic struct functionality for parsing + +#include + +#ifndef PARSER_STRUCTS_H +#define PARSER_STRUCTS_H + +typedef enum { INT, FLOAT, DOUBLE, STRING } TypeEnum; + +typedef struct { + TypeEnum type; + size_t offset; +} StructField; + +// fill generic struct with given data +// params: +// - obj - pointer to the struct to fill +// - fields - field types and offsets of the struct +// - n_fields - count of fields in the struct +// - data - data to fill the struct with +// - data_len - length of the data +// - sep - separator seperating the fields in data +extern void fill_struct(void *obj, const StructField *fields, size_t n_fields, const char *data, size_t data_len, const char *sep); + +// cleanup allocations inside generic struct +// params: +// - obj - pointer to the struct to cleanup +// - fields - field types and offsets of the struct +// - n_fields - count of fields in the struct +extern void cleanup_struct(void *obj, const StructField *fields, size_t n_fields); + +#endif diff --git a/analyzer/render.c b/analyzer/render.c index 0fdd334..5c4b2d0 100644 --- a/analyzer/render.c +++ b/analyzer/render.c @@ -63,14 +63,10 @@ void print_enemy_list(EnemyResults *results, size_t n_enemies) { } void print_column_names() { - char *names[TABLE_COLUMN_COUNT] = TABLE_COLUMN_NAMES; - int colors[TABLE_COLUMN_COUNT] = TABLE_COLUMN_COLORS; - size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; - printf(TABLE_LINE_START); - for (size_t i = 0; i < TABLE_COLUMN_COUNT; i++) { - print_center(names[i], colors[i], widths[i], ' '); - printf(i < TABLE_COLUMN_COUNT - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); + for (size_t i = 0; i < ARR_LEN(columns); i++) { + print_center(columns[i].name, columns[i].color, columns[i].width, ' '); + printf(i < ARR_LEN(columns) - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); } printf("\n"); } @@ -96,9 +92,7 @@ int item_type_to_color(ItemType type) { } void print_player_rewards(FriendlyResults results) { - char buffers[TABLE_COLUMN_COUNT][TABLE_LINE_WIDTH]; - size_t widths[TABLE_COLUMN_COUNT] = TABLE_COLUMN_WIDTHS; - int colors[TABLE_COLUMN_COUNT] = TABLE_COLUMN_COLORS; + char buffers[ARR_LEN(columns)][TABLE_LINE_WIDTH]; sprintf(buffers[0], "%s (%d)", results.name, results.level); sprintf(buffers[1], "%d", results.exp); @@ -120,11 +114,11 @@ void print_player_rewards(FriendlyResults results) { sprintf(buffers[5], "-"); printf(TABLE_LINE_START); - for (size_t i = 0; i < TABLE_COLUMN_COUNT; i++) { - char to_color[widths[i] + 1]; - sprintf(to_color, "%*s", (int)widths[i], buffers[i]); - print_color(to_color, colors[i]); - printf(i < TABLE_COLUMN_COUNT - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); + for (size_t i = 0; i < ARR_LEN(columns); i++) { + char to_color[columns[i].width + 1]; + sprintf(to_color, "%*s", (int)columns[i].width, buffers[i]); + print_color(to_color, columns[i].color); + printf(i < ARR_LEN(columns) - 1 ? TABLE_LINE_SEPARATOR : TABLE_LINE_END); } printf("\n"); diff --git a/analyzer/render.h b/analyzer/render.h index 1015eb9..3604bde 100644 --- a/analyzer/render.h +++ b/analyzer/render.h @@ -2,6 +2,7 @@ // functions for "rendering" data onto the terminal #include "parser/fight_results/fight_results.h" +#include "utils/memory.h" // ANSI escape sequence colors #define COLOR_GOLD 33 @@ -23,40 +24,32 @@ #define TABLE_LINE_END_WIDTH (sizeof(TABLE_LINE_END) - 1) #define TABLE_LINE_SEPARATOR_WIDTH (sizeof(TABLE_LINE_SEPARATOR) - 1) -#define TABLE_COLUMN_COUNT 6 - -#define TABLE_NAME_LVL_WIDTH 17 -#define TABLE_EXP_WIDTH 7 -#define TABLE_GOLD_WIDTH 7 -#define TABLE_PSYCHO_WIDTH 6 -#define TABLE_SATURATION_WIDTH 13 -#define TABLE_SPLINTERS_WIDTH 9 +// column info +typedef struct { + char *name; + size_t width; + int color; +} ColumnInfo; +static const ColumnInfo columns[] = { + {"Nick (LVL)", 17, COLOR_WHITE}, + {"EXP", 7, COLOR_WHITE}, + {"Gold", 7, COLOR_YELLOW}, + {"Psycho", 6, COLOR_AQUA}, + {"Saturation", 13, COLOR_GREEN}, + {"Splinters", 9, COLOR_BLUE}, +}; // clang-format off -#define TABLE_COLUMN_NAMES {"Nick (LVL)", "EXP", "Gold", "Psycho", "Saturation", "Splinters"} -#define TABLE_COLUMN_COLORS {COLOR_WHITE, COLOR_WHITE, COLOR_YELLOW, COLOR_AQUA, COLOR_GREEN, COLOR_BLUE} -#define TABLE_COLUMN_WIDTHS { \ - TABLE_NAME_LVL_WIDTH, \ - TABLE_EXP_WIDTH, \ - TABLE_GOLD_WIDTH, \ - TABLE_PSYCHO_WIDTH, \ - TABLE_SATURATION_WIDTH, \ - TABLE_SPLINTERS_WIDTH, \ -} - -#define TABLE_LINE_WIDTH ( \ - TABLE_LINE_START_WIDTH + \ - TABLE_NAME_LVL_WIDTH + \ - TABLE_EXP_WIDTH + \ - TABLE_GOLD_WIDTH + \ - TABLE_PSYCHO_WIDTH + \ - TABLE_SATURATION_WIDTH + \ - TABLE_SPLINTERS_WIDTH + \ - (TABLE_COLUMN_COUNT - 1) * TABLE_LINE_SEPARATOR_WIDTH + \ - TABLE_LINE_END_WIDTH \ -) +#define SUM_COL_WIDTHS ({ \ + size_t sum = 0; \ + for (size_t i = 0; i < ARR_LEN(columns); i++) \ + sum += columns[i].width; \ + sum; \ +}) // clang-format on +#define TABLE_LINE_WIDTH (TABLE_LINE_START_WIDTH + SUM_COL_WIDTHS + (ARR_LEN(columns) - 1) * TABLE_LINE_SEPARATOR_WIDTH + TABLE_LINE_END_WIDTH) + #define TABLE_ITEMS_WIDTH (TABLE_LINE_WIDTH - TABLE_LINE_START_WIDTH - TABLE_LINE_END_WIDTH) // render fight results onto the terminal diff --git a/analyzer/utils/memory.h b/analyzer/utils/memory.h new file mode 100644 index 0000000..1de58b9 --- /dev/null +++ b/analyzer/utils/memory.h @@ -0,0 +1,4 @@ +// utils/memory.h +// helper functions for dealing with memory management + +#define ARR_LEN(arr) (sizeof(arr) / sizeof(arr[0])) diff --git a/analyzer/utils/roman.c b/analyzer/utils/roman.c index 9ea2bbb..4fbe5c0 100644 --- a/analyzer/utils/roman.c +++ b/analyzer/utils/roman.c @@ -1,16 +1,15 @@ #include "utils/roman.h" -#include #include +#include "utils/memory.h" + void to_roman(char *buffer, int number) { buffer[0] = '\0'; - int nums[ROMAN_CHAR_COUNT] = ROMAN_NUMS; - char *chars[ROMAN_CHAR_COUNT] = ROMAN_CHARS; - for (size_t i = 0; i < ROMAN_CHAR_COUNT; i++) { - for (size_t j = 0; j < number / nums[i]; j++) { - strcat(buffer, chars[i]); + for (size_t i = 0; i < ARR_LEN(roman_symbols); i++) { + for (size_t j = 0; j < number / roman_symbols[i].num; j++) { + strcat(buffer, roman_symbols[i].symbol); } - number %= nums[i]; + number %= roman_symbols[i].num; } } diff --git a/analyzer/utils/roman.h b/analyzer/utils/roman.h index b4f5d3f..cebd16e 100644 --- a/analyzer/utils/roman.h +++ b/analyzer/utils/roman.h @@ -1,9 +1,26 @@ // utils/roman.h // functions for roman numbers -#define ROMAN_CHAR_COUNT 13 -#define ROMAN_NUMS {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1} -#define ROMAN_CHARS {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"} +typedef struct { + int num; + char *symbol; +} RomanSymbol; + +static const RomanSymbol roman_symbols[] = { + {1000, "M"}, + {900, "CM"}, + {500, "D"}, + {400, "CD"}, + {100, "C"}, + {90, "XC"}, + {50, "L"}, + {40, "XL"}, + {10, "X"}, + {9, "IX"}, + {5, "V"}, + {4, "IV"}, + {1, "I"}, +}; // convert a arabic number to roman number // params: -- cgit v1.2.3 From 8317c3b33b9b1c70e5ae4c87c7efe33d9b9ce09a Mon Sep 17 00:00:00 2001 From: Mosder Date: Thu, 23 Jul 2026 17:35:04 +0200 Subject: feat: added helper functions for dealing with memory management and refactored the codebase to use them --- Makefile | 1 + analyzer/parser/fight_results/fight_results.c | 109 +++++++++++--------------- analyzer/parser/fight_results/fight_results.h | 6 +- analyzer/parser/parser.c | 16 +--- analyzer/parser/structs.c | 4 +- analyzer/structures/hashmap.c | 12 +-- analyzer/structures/heap.c | 10 ++- analyzer/utils/memory.c | 62 +++++++++++++++ analyzer/utils/memory.h | 42 ++++++++++ analyzer/utils/tcp.c | 46 +++++------ 10 files changed, 195 insertions(+), 113 deletions(-) create mode 100644 analyzer/utils/memory.c (limited to 'analyzer/utils') diff --git a/Makefile b/Makefile index fa3a294..cc5cb87 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ utils: $(CC) $(CFLAGS) -c analyzer/utils/tcp.c $(CC) $(CFLAGS) -c analyzer/utils/tcp_seq_math.c $(CC) $(CFLAGS) -c analyzer/utils/roman.c + $(CC) $(CFLAGS) -c analyzer/utils/memory.c parser: $(CC) $(CFLAGS) -c analyzer/parser/parser.c diff --git a/analyzer/parser/fight_results/fight_results.c b/analyzer/parser/fight_results/fight_results.c index e7c7123..efc4b36 100644 --- a/analyzer/parser/fight_results/fight_results.c +++ b/analyzer/parser/fight_results/fight_results.c @@ -30,6 +30,8 @@ void cleanup_fight_results(void *data) { for (size_t i = 0; i < fight_results->num_enemy; i++) { free(fight_results->enemy_results[i].name); } + free(fight_results->friendly_results); + free(fight_results->enemy_results); } byte_t *find_fight_results(TCPStream *stream, int offset, int *length) { @@ -41,12 +43,6 @@ byte_t *find_fight_results(TCPStream *stream, int offset, int *length) { return start; } -Item *expand_items(Item *items, size_t *items_capacity, size_t to_fit) { - while (*items_capacity < to_fit) - *items_capacity <<= 1; - return realloc(items, *items_capacity * sizeof(Item)); -} - void get_stars(char *buffer, int incr_above_b1) { char star_types[] = {'B', 'S', 'G'}; sprintf(buffer, " (%c%d)", star_types[incr_above_b1 / 3], incr_above_b1 % 3 + 1); @@ -67,10 +63,11 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) if (gears_separator) gears_str = gears_separator + strlen(SEPARATOR_GEAR); - Item item; - + // get gear info for rank, name and stars / syng level GearInfo gear_info = {0}; fill_struct(&gear_info, gear_info_fields, ARR_LEN(gear_info_fields), gear.info, strlen(gear.info), SEPARATOR_GEAR_INFO_FIELD); + + // create the buffer of information about gear char buffer[SINGLE_ITEM_BUFFER_LEN]; char roman[5]; to_roman(roman, gear_info.rank); @@ -91,9 +88,9 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) break; } } - item.data = strdup(buffer); - cleanup_struct(&gear_info, gear_info_fields, ARR_LEN(gear_info_fields)); + // add gear with specific type + Item item = {.data = strdup(buffer)}; switch (gear.type) { case TYPE_SYNG_NORMAL: // atoi should be fine, since syng_tier starts from 1 for syngs @@ -109,26 +106,22 @@ void add_gear(FriendlyResults *results, size_t *items_capacity, char *gears_str) item.type = EPIC; break; } + results->items = array_append(results->items, sizeof(Item), &results->num_items, items_capacity, &item); - // expand if needed - if (results->num_items >= *items_capacity) - results->items = expand_items(results->items, items_capacity, results->num_items + 1); - results->items[results->num_items++] = item; + cleanup_struct(&gear_info, gear_info_fields, ARR_LEN(gear_info_fields)); // add orb to items if it exists if (strlen(gear.orb)) { - Item item; - char buffer[SINGLE_ITEM_BUFFER_LEN]; Orb orb = {0}; fill_struct(&orb, orb_fields, ARR_LEN(orb_fields), gear.orb, strlen(gear.orb), SEPARATOR_ORB_FIELD); + + char buffer[SINGLE_ITEM_BUFFER_LEN]; sprintf(buffer, "%sorb %s", get_artifact_size_from_id(orb.size), get_orb_name_from_id(orb.id)); - item.data = strdup(buffer); + + Item item = {.data = strdup(buffer), .type = ORB}; + results->items = array_append(results->items, sizeof(Item), &results->num_items, items_capacity, &item); + cleanup_struct(&orb, orb_fields, ARR_LEN(orb_fields)); - item.type = ORB; - // expand if needed - if (results->num_items >= *items_capacity) - results->items = expand_items(results->items, items_capacity, results->num_items + 1); - results->items[results->num_items++] = item; } cleanup_struct(&gear, gear_fields, ARR_LEN(gear_fields)); @@ -150,16 +143,10 @@ void add_drifs(FriendlyResults *results, size_t *items_capacity, char *drifs_str if (drifs_separator) drifs_str = drifs_separator + strlen(SEPARATOR_DRIF); - Item item; - item.data = strdup(drif.name); - item.type = DRIF; + Item item = {.data = strdup(drif.name), .type = DRIF}; + results->items = array_append(results->items, sizeof(Item), &results->num_items, items_capacity, &item); cleanup_struct(&drif, drif_fields, ARR_LEN(drif_fields)); - - // expand if needed - if (results->num_items >= *items_capacity) - results->items = expand_items(results->items, items_capacity, results->num_items + 1); - results->items[results->num_items++] = item; } while (drifs_separator); } @@ -180,37 +167,31 @@ void add_items(FriendlyResults *results, size_t *items_capacity, char *items_str if (items_separator) items_str = items_separator + strlen(SEPARATOR_ITEMS); - Item item; - item.data = strdup(buffer); - item.type = NORMAL; - - // expand if needed - if (results->num_items >= *items_capacity) - results->items = expand_items(results->items, items_capacity, results->num_items + 1); - results->items[results->num_items++] = item; + Item item = {.data = strdup(buffer), .type = NORMAL}; + results->items = array_append(results->items, sizeof(Item), &results->num_items, items_capacity, &item); } while (items_separator); } // parse EntityResults to FriendlyResults FriendlyResults parse_to_friendly(EntityResults entity_results) { - FriendlyResults results; - - results.name = strdup(entity_results.name); - results.exp = entity_results.exp; - results.gold = entity_results.gold; - results.level = entity_results.level; - results.psycho = entity_results.psycho; - results.splinters = entity_results.splinters; + FriendlyResults results = { + .name = strdup(entity_results.name), + .exp = entity_results.exp, + .gold = entity_results.gold, + .level = entity_results.level, + .psycho = entity_results.psycho, + .splinters = entity_results.splinters, + .num_items = 0, + .saturation = 0 + }; // create items array and add to it gear, drifs and items - results.num_items = 0; - results.items = malloc(INIT_ITEMS_CAPACITY * sizeof(Item)); + results.items = safe_malloc(INIT_ITEMS_CAPACITY * sizeof(Item)); size_t items_cap = INIT_ITEMS_CAPACITY; add_gear(&results, &items_cap, entity_results.gear); add_drifs(&results, &items_cap, entity_results.drifs); add_items(&results, &items_cap, entity_results.items); - results.saturation = 0; if (strlen(entity_results.saturation) > 0) { int saturation_id; sscanf(entity_results.saturation, "%d,%d", &saturation_id, &results.saturation); @@ -222,25 +203,26 @@ FriendlyResults parse_to_friendly(EntityResults entity_results) { // parse EntityResults to EnemyResults EnemyResults parse_to_enemy(EntityResults entity_results) { - EnemyResults results; - - results.name = strdup(entity_results.name); - results.level = entity_results.level; - + EnemyResults results = {.name = strdup(entity_results.name), .level = entity_results.level}; return results; } ParsedData parse_fight_results(byte_t *data, size_t length) { - ParsedData parsed_data; - parsed_data.data_type = TYPE_FIGHT_RESULTS; + ParsedData parsed_data = {.data_type = TYPE_FIGHT_RESULTS, .data_cleanup = cleanup_fight_results}; // ignore start and end indicators data += strlen(FIGHT_RESULTS_START); length -= strlen(FIGHT_RESULTS_START) + strlen(FIGHT_RESULTS_END); - FightResults *results = malloc(sizeof(FightResults)); + FightResults *results = safe_malloc(sizeof(FightResults)); + + results->friendly_results = safe_malloc(INIT_ENTITIES_PER_SIDE * sizeof(FriendlyResults)); results->num_friendly = 0; + size_t friendly_cap = INIT_ENTITIES_PER_SIDE; + + results->enemy_results = safe_malloc(INIT_ENTITIES_PER_SIDE * sizeof(EnemyResults)); results->num_enemy = 0; + size_t enemy_cap = INIT_ENTITIES_PER_SIDE; byte_t *entity_separator; do { @@ -252,12 +234,16 @@ ParsedData parse_fight_results(byte_t *data, size_t length) { // depending on team - parse it into friendly or enemy results switch (entity_results.team) { - case TEAM_FRIEND: - results->friendly_results[results->num_friendly++] = parse_to_friendly(entity_results); + case TEAM_FRIEND: { + FriendlyResults res = parse_to_friendly(entity_results); + results->friendly_results = array_append(results->friendly_results, sizeof(FriendlyResults), &results->num_friendly, &friendly_cap, &res); break; - case TEAM_ENEMY: - results->enemy_results[results->num_enemy++] = parse_to_enemy(entity_results); + } + case TEAM_ENEMY: { + EnemyResults res = parse_to_enemy(entity_results); + results->enemy_results = array_append(results->enemy_results, sizeof(EnemyResults), &results->num_enemy, &enemy_cap, &res); break; + } } cleanup_struct(&entity_results, entity_fields, ARR_LEN(entity_fields)); @@ -270,6 +256,5 @@ ParsedData parse_fight_results(byte_t *data, size_t length) { } while (entity_separator); parsed_data.data = results; - parsed_data.data_cleanup = cleanup_fight_results; return parsed_data; } diff --git a/analyzer/parser/fight_results/fight_results.h b/analyzer/parser/fight_results/fight_results.h index 04add96..ec90394 100644 --- a/analyzer/parser/fight_results/fight_results.h +++ b/analyzer/parser/fight_results/fight_results.h @@ -44,14 +44,14 @@ typedef struct { int level; } EnemyResults; -#define MAX_ENTITIES_PER_SIDE 8 +#define INIT_ENTITIES_PER_SIDE 6 // entire fight resutls typedef struct { size_t num_friendly; - FriendlyResults friendly_results[MAX_ENTITIES_PER_SIDE]; + FriendlyResults *friendly_results; size_t num_enemy; - EnemyResults enemy_results[MAX_ENTITIES_PER_SIDE]; + EnemyResults *enemy_results; } FightResults; extern find_function_t find_fight_results; diff --git a/analyzer/parser/parser.c b/analyzer/parser/parser.c index 87ab4c0..d00cd97 100644 --- a/analyzer/parser/parser.c +++ b/analyzer/parser/parser.c @@ -5,13 +5,6 @@ #include "parser/find_parse_functions.h" #include "utils/memory.h" -// expand parsed data exponentially to fit el_count elements -ParsedData *expand_parsed_data(ParsedData *parsed_data, size_t *capacity, size_t el_count) { - while (*capacity < el_count) - *capacity <<= 1; - return realloc(parsed_data, *capacity * sizeof(ParsedData)); -} - // find earliest data to parse in stream byte_t *find_earliest_data(TCPStream *stream, int stream_offset, int *earliest_length, parse_function_t **earliest_parse_fun) { byte_t *earliest_data = NULL; @@ -30,7 +23,7 @@ byte_t *find_earliest_data(TCPStream *stream, int stream_offset, int *earliest_l ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed_data) { size_t parsed_data_capacity = INIT_PARSED_DATA_CAPACITY; - ParsedData *parsed_data = malloc(parsed_data_capacity * sizeof(ParsedData)); + ParsedData *parsed_data = safe_malloc(parsed_data_capacity * sizeof(ParsedData)); *n_parsed_data = 0; int stream_offset = 0; @@ -50,11 +43,8 @@ ParsedData *parse_stream(TCPStream *stream, size_t *parsed_len, size_t *n_parsed // if data to parse was found, parse it and update the offset if (earliest_data) { - // if no capacity in parsed data array - expand it - if (*n_parsed_data >= parsed_data_capacity) - parsed_data = expand_parsed_data(parsed_data, &parsed_data_capacity, *n_parsed_data + 1); - - parsed_data[(*n_parsed_data)++] = earliest_parse_fun(earliest_data, earliest_length); + ParsedData new_parsed = earliest_parse_fun(earliest_data, earliest_length); + parsed_data = array_append(parsed_data, sizeof(ParsedData), n_parsed_data, &parsed_data_capacity, &new_parsed); stream_offset = earliest_data - stream->data + earliest_length; } } while (earliest_data); diff --git a/analyzer/parser/structs.c b/analyzer/parser/structs.c index c59fa82..dedf0d1 100644 --- a/analyzer/parser/structs.c +++ b/analyzer/parser/structs.c @@ -4,12 +4,14 @@ #include #include +#include "utils/memory.h" + void fill_struct(void *obj, const StructField *fields, size_t n_fields, const char *data, size_t data_len, const char *sep) { for (size_t i = 0; i < n_fields; i++) { char *next_sep = memmem(data, data_len, sep, strlen(sep)); size_t len = next_sep ? next_sep - data : data_len; - char *str = malloc(len + 1); + char *str = safe_malloc(len + 1); memcpy(str, data, len); str[len] = '\0'; diff --git a/analyzer/structures/hashmap.c b/analyzer/structures/hashmap.c index 84cc289..5eb27ae 100644 --- a/analyzer/structures/hashmap.c +++ b/analyzer/structures/hashmap.c @@ -3,6 +3,8 @@ #include #include +#include "utils/memory.h" + HashMap *hashmap_new( size_t key_size, size_t value_size, @@ -11,9 +13,9 @@ HashMap *hashmap_new( void (*key_cleanup)(void *key), void (*value_cleanup)(void *key) ) { - HashMap *map = malloc(sizeof(HashMap)); + HashMap *map = safe_malloc(sizeof(HashMap)); // initialize NULLs - memset(map->table, 0, sizeof(HashMapNode *) * HASHMAP_TABLE_SIZE); + memset(map->table, 0, HASHMAP_TABLE_SIZE * sizeof(HashMapNode *)); map->key_size = key_size; map->value_size = value_size; map->hash = hash; @@ -42,9 +44,9 @@ void hashmap_put(HashMap *map, void *key, void *value) { } // if it doesn't exist - create new node - HashMapNode *new_node = malloc(sizeof(HashMapNode)); - new_node->key = malloc(map->key_size); - new_node->value = malloc(map->value_size); + HashMapNode *new_node = safe_malloc(sizeof(HashMapNode)); + new_node->key = safe_malloc(map->key_size); + new_node->value = safe_malloc(map->value_size); memcpy(new_node->key, key, map->key_size); memcpy(new_node->value, value, map->value_size); diff --git a/analyzer/structures/heap.c b/analyzer/structures/heap.c index 6102940..1267668 100644 --- a/analyzer/structures/heap.c +++ b/analyzer/structures/heap.c @@ -3,9 +3,11 @@ #include #include +#include "utils/memory.h" + Heap *heap_new(size_t element_size, int (*compare)(void *element1, void *element2), void (*element_cleanup)(void *element)) { - Heap *heap = malloc(sizeof(Heap)); - heap->data = malloc(INIT_HEAP_CAPACITY * sizeof(void *)); + Heap *heap = safe_malloc(sizeof(Heap)); + heap->data = safe_malloc(INIT_HEAP_CAPACITY * sizeof(void *)); heap->n_elements = 0; heap->element_size = element_size; heap->capacity = INIT_HEAP_CAPACITY; @@ -40,7 +42,7 @@ void swap(void **p_el1, void **p_el2) { void expand_heap(Heap *heap, size_t el_count) { while (heap->capacity < el_count) heap->capacity = (heap->capacity << 1) | 1; - heap->data = realloc(heap->data, heap->capacity * sizeof(void *)); + heap->data = safe_realloc(heap->data, heap->capacity * sizeof(void *)); } void heap_insert(Heap *heap, void *element) { @@ -50,7 +52,7 @@ void heap_insert(Heap *heap, void *element) { // insert into the last position size_t i = heap->n_elements; - heap->data[i] = malloc(heap->element_size); + heap->data[i] = safe_malloc(heap->element_size); memcpy(heap->data[i], element, heap->element_size); heap->n_elements++; diff --git a/analyzer/utils/memory.c b/analyzer/utils/memory.c new file mode 100644 index 0000000..0a43bd3 --- /dev/null +++ b/analyzer/utils/memory.c @@ -0,0 +1,62 @@ +#include "memory.h" + +#include +#include +#include +#include + +void *safe_malloc(size_t size) { + for (size_t i = 0; i < MAX_ALLOC_ATTEMPTS; i++) { + void *p = malloc(size); + if (p) + return p; + if (i < MAX_ALLOC_ATTEMPTS - 1) { + printf("Failed malloc. Retrying in 1s...\n"); + sleep(1); + } + } + + printf("ERROR: Failed malloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS); + exit(EXIT_FAILURE); +} + +void *safe_realloc(void *ptr, size_t size) { + if (!ptr) + return safe_malloc(size); + + for (size_t i = 0; i < MAX_ALLOC_ATTEMPTS; i++) { + void *p = realloc(ptr, size); + if (p) + return p; + if (i < MAX_ALLOC_ATTEMPTS - 1) { + printf("Failed realloc. Retrying in 1s...\n"); + sleep(1); + } + } + + printf("ERROR: Failed realloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS); + exit(EXIT_FAILURE); +} + +// expand an array +// returns pointer to the expanded array +void *array_expand(void *arr, size_t el_size, size_t *cap, size_t to_fit) { + while (*cap < to_fit) + *cap *= 2; + return safe_realloc(arr, *cap * el_size); +} + +void *array_append(void *arr, size_t el_size, size_t *len, size_t *cap, void *val) { + if (*len >= *cap) + arr = array_expand(arr, el_size, cap, *len + 1); + memcpy((char *)arr + (*len)++ * el_size, val, el_size); + return arr; +} + +void *array_concat(void *arr1, size_t el_size, size_t *len1, size_t *cap, void *arr2, size_t len2) { + if (*len1 + len2 > *cap) + arr1 = array_expand(arr1, el_size, cap, *len1 + len2); + memcpy((char *)arr1 + *len1 * el_size, arr2, len2 * el_size); + *len1 += len2; + return arr1; +} diff --git a/analyzer/utils/memory.h b/analyzer/utils/memory.h index 1de58b9..0973623 100644 --- a/analyzer/utils/memory.h +++ b/analyzer/utils/memory.h @@ -1,4 +1,46 @@ // utils/memory.h // helper functions for dealing with memory management +#include + #define ARR_LEN(arr) (sizeof(arr) / sizeof(arr[0])) + +#define MAX_ALLOC_ATTEMPTS 3 + +// tries malloc MAX_ALLOC_ATTEMPTS times, if all fail - exits the program +// params: +// - size - count of bytes to allocate +// returns: +// pointer to the allocated memory +extern void *safe_malloc(size_t size); + +// tries realloc MAX_ALLOC_ATTEMPTS times, if all fail - exits the program +// params: +// - ptr - pointer to the memory to reallocate +// - size - new size of the memory +// returns: +// pointer to the reallocated memory +extern void *safe_realloc(void *ptr, size_t size); + +// append to array, expand if necessary +// params: +// - arr - array to append to +// - el_size - size of an array element +// - len - pointer to the length of the array +// - cap - pointer to the capacity of the array +// - val - pointer to the value to append to the array +// returns: +// new pointer to the array (different if it was realloced) +extern void *array_append(void *arr, size_t el_size, size_t *len, size_t *cap, void *val); + +// concatenate 2 arrays, expand if necessary +// params: +// - arr1 - first array (destination) +// - el_size - size of an array element +// - len1 - pointer to the length of arr1 +// - cap - pointer to the capacity of arr1 +// - arr2 - second array +// - len2 - length of arr2 +// returns: +// new pointer to the array (different if it was realloced) +extern void *array_concat(void *arr1, size_t el_size, size_t *len1, size_t *cap, void *arr2, size_t len2); diff --git a/analyzer/utils/tcp.c b/analyzer/utils/tcp.c index b925ecf..6309e64 100644 --- a/analyzer/utils/tcp.c +++ b/analyzer/utils/tcp.c @@ -6,15 +6,19 @@ #include #include +#include "utils/memory.h" #include "utils/tcp_seq_math.h" // initalize default TCP segment TCPSegment new_segment() { - TCPSegment segment; - segment.payload = NULL; - segment.len = 0; - segment.seq = 0; - segment.port = 0; + // clang-format off + TCPSegment segment = { + .payload = NULL, + .len = 0, + .seq = 0, + .port = 0 + }; + // clang-format on return segment; } @@ -61,19 +65,22 @@ TCPSegment get_segment_from_packet(const byte_t *packet, const struct pcap_pkthd // get payload int total_header_len = eth_header_len + ip_header_len + tcp_header_len; segment.len = packet_data->caplen - total_header_len; - segment.payload = malloc(segment.len); + segment.payload = safe_malloc(segment.len); memcpy(segment.payload, packet + total_header_len, segment.len); return segment; } TCPStream new_stream() { - TCPStream stream; - stream.data = malloc(INIT_STREAM_CAPACITY); - stream.len = 0; - stream.capacity = INIT_STREAM_CAPACITY; - stream.seq = 0; - stream.port = 0; - stream.pending = NULL; + // clang-format off + TCPStream stream = { + .data = safe_malloc(INIT_STREAM_CAPACITY), + .len = 0, + .capacity = INIT_STREAM_CAPACITY, + .seq = 0, + .port = 0, + .pending = NULL + }; + // clang-format on return stream; } @@ -107,20 +114,9 @@ int in_acceptable_range(TCPStream stream, TCPSegment segment) { return seq_in_range_ei(segment.seq, stream_expected, max_acceptable_seq); } -// expand stream capacity exponentially to fit new_len -void expand_stream_capacity(TCPStream *stream, size_t new_len) { - while (stream->capacity < new_len) - stream->capacity <<= 1; - stream->data = realloc(stream->data, stream->capacity); -} - // add segment directly to stream void add_segment(TCPStream *stream, TCPSegment segment, size_t data_start) { - size_t new_stream_len = stream->len + segment.len - data_start; - if (new_stream_len > stream->capacity) - expand_stream_capacity(stream, new_stream_len); - memcpy(stream->data + stream->len, segment.payload + data_start, segment.len - data_start); - stream->len = new_stream_len; + stream->data = array_concat(stream->data, 1, &stream->len, &stream->capacity, segment.payload + data_start, segment.len - data_start); } // add pending segments to the stream -- cgit v1.2.3 From 4add8c9c34c48f01de58b3b41e79a768063f7acf Mon Sep 17 00:00:00 2001 From: Mosder Date: Thu, 23 Jul 2026 20:04:32 +0200 Subject: feat: redirected runtime errors to stderr --- analyzer/analyzer.c | 2 +- analyzer/utils/memory.c | 8 ++++---- csv_table_gen.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'analyzer/utils') diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c index bd24613..aa4a645 100644 --- a/analyzer/analyzer.c +++ b/analyzer/analyzer.c @@ -59,7 +59,7 @@ void analyze_file(char *path) { // handle errors if (handle == NULL) { - printf("Couldn't open the specified file:\n%s\n", errbuf); + fprintf(stderr, "Couldn't open the specified file:\n%s\n", errbuf); return; } diff --git a/analyzer/utils/memory.c b/analyzer/utils/memory.c index 0a43bd3..91c5f18 100644 --- a/analyzer/utils/memory.c +++ b/analyzer/utils/memory.c @@ -11,12 +11,12 @@ void *safe_malloc(size_t size) { if (p) return p; if (i < MAX_ALLOC_ATTEMPTS - 1) { - printf("Failed malloc. Retrying in 1s...\n"); + fprintf(stderr, "Failed malloc. Retrying in 1s...\n"); sleep(1); } } - printf("ERROR: Failed malloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS); + fprintf(stderr, "FATAL: Failed malloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS); exit(EXIT_FAILURE); } @@ -29,12 +29,12 @@ void *safe_realloc(void *ptr, size_t size) { if (p) return p; if (i < MAX_ALLOC_ATTEMPTS - 1) { - printf("Failed realloc. Retrying in 1s...\n"); + fprintf(stderr, "Failed realloc. Retrying in 1s...\n"); sleep(1); } } - printf("ERROR: Failed realloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS); + fprintf(stderr, "FATAL: Failed realloc %d times. Exiting...", MAX_ALLOC_ATTEMPTS); exit(EXIT_FAILURE); } diff --git a/csv_table_gen.c b/csv_table_gen.c index 0bfd51a..ae80b78 100644 --- a/csv_table_gen.c +++ b/csv_table_gen.c @@ -80,7 +80,7 @@ void create_c(char *path, char *h_path, char *from, char *to, char from_to_array fprintf(fp, "const char *get_%s_from_%s(int %s) {\n", to, from, from); fprintf(fp, " if (%s >= 0 && %s < %s)\n", from, from, arr_len_name); fprintf(fp, " return %s[%s];\n", arr_name, from); - fprintf(fp, " printf(\"%s = %%d is outside of table range [0; %%d]\\n\", %s, %s - 1);\n", from, from, arr_len_name); + fprintf(fp, " fprintf(stderr, \"%s = %%d is outside of table range [0; %%d]\\n\", %s, %s - 1);\n", from, from, arr_len_name); fprintf(fp, " return \"null\";\n"); fprintf(fp, "}"); -- cgit v1.2.3