aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMosder <xmosder12@gmail.com>2026-07-28 19:37:29 +0200
committerMosder <xmosder12@gmail.com>2026-07-28 19:37:29 +0200
commita30d6feab718a887edc7eb5b0470faa77677a8c3 (patch)
tree87c97ea0fe71f7d9adbdbe399dea39ae24e53fd6
parent9e874301350a70a1ba40b5508878d559f68f8f4a (diff)
feat: added a very-very basic total rewards thingy, still needs to do detection through the special packet sent by client and there's a lot of placeholder things and not the best code there. Also - normal items don't stack and they definitely should - that's gonna be a bit of refactoring :)))))))))))
-rw-r--r--analyzer/analyzer.c132
-rw-r--r--analyzer/analyzer.h25
-rw-r--r--analyzer/parser/fight_results/fight_results.c1
-rw-r--r--analyzer/parser/fight_results/fight_results.h2
-rw-r--r--analyzer/render.c55
-rw-r--r--analyzer/render.h6
-rw-r--r--analyzer/structures/hashmap.c26
-rw-r--r--analyzer/structures/hashmap.h25
8 files changed, 248 insertions, 24 deletions
diff --git a/analyzer/analyzer.c b/analyzer/analyzer.c
index 7b80328..b3468d6 100644
--- a/analyzer/analyzer.c
+++ b/analyzer/analyzer.c
@@ -1,11 +1,119 @@
#include "analyzer.h"
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include "parser/parser.h"
#include "render.h"
#include "utils/tcp.h"
+// cleanup TotalRewards to be used with HashMap
+void cleanup_total_rewards(void *rewards) {
+ TotalRewards *r = rewards;
+ hashmap_free(r->saturations);
+ for (size_t i = 0; i < MAX_ITEM_TYPE; i++)
+ array_destroy(r->items[i]);
+}
+
+// cleanup char** to be used in DynArray
+void cleanup_str_pointer(void *str_pointer) {
+ char **p_str = str_pointer;
+ free(*p_str);
+}
+
+// initalize new total rewards
+TotalRewards new_total_rewards() {
+ HashMap *saturations = hashmap_new(sizeof(const char *), sizeof(int), hash_str, compare_strs, NULL, NULL);
+ // clang-format off
+ TotalRewards rewards = {
+ .exp = 0,
+ .gold = 0,
+ .psycho = 0,
+ .saturations = saturations
+ };
+ // clang-format on
+ for (size_t i = 0; i < MAX_ITEM_TYPE; i++)
+ rewards.items[i] = array_new(sizeof(char **), 16, cleanup_str_pointer);
+ return rewards;
+}
+
+// initalize file analysis state
+FileAnalState init_file_anal_state() {
+ HashMap *characters = hashmap_new(sizeof(char *), sizeof(TotalRewards), hash_str, compare_strs, NULL, cleanup_total_rewards);
+ FileAnalState state = {.fight_count = 0, .curr_character = {0}, .characters = characters};
+ return state;
+}
+
+// destroy FileAnalState
+void destroy_file_anal_state(FileAnalState state) {
+ hashmap_free(state.characters);
+}
+
+// add data from FriendlyResults into TotalRewards
+void add_to_total_rewards(TotalRewards *rewards, FriendlyResults *results) {
+ rewards->exp += results->exp;
+ rewards->gold += results->gold;
+ rewards->psycho += results->psycho;
+
+ Item *it = NULL;
+ while ((it = array_next(results->items, it))) {
+ char *data = strdup(it->data);
+ array_append(&rewards->items[it->type], &data);
+ }
+
+ if (results->saturation > 0) {
+ int *old_sat = hashmap_get(rewards->saturations, (char *)results->saturation_type);
+ int new_sat = results->saturation + (old_sat ? *old_sat : 0);
+ hashmap_put(rewards->saturations, (char *)results->saturation_type, &new_sat);
+ }
+}
+
+// file analyzer parsed data handler
+void file_parsed_data_handler(void *state, DynArray parsed_data) {
+ FileAnalState *st = state;
+ ParsedData *data = NULL;
+ while ((data = array_next(parsed_data, data))) {
+ switch (data->data_type) {
+ case TYPE_FIGHT_RESULTS: {
+ FightResults *res = data->data;
+
+ // if only one friendly - it's the player
+ if (res->friendly_results.count == 1) {
+ FriendlyResults *player = array_get(res->friendly_results, 0);
+ strcpy(st->curr_character, player->name);
+ }
+
+ // find the current player
+ FriendlyResults *friendly = NULL;
+ int found = 0;
+ while (!found && (friendly = array_next(res->friendly_results, friendly))) {
+ if (!strcmp(st->curr_character, friendly->name))
+ found = 1;
+ }
+
+ // if found - save the rewards
+ if (found) {
+ TotalRewards *r = hashmap_get(st->characters, st->curr_character);
+ if (r) {
+ add_to_total_rewards(r, friendly);
+ }
+ else {
+ TotalRewards rewards = new_total_rewards();
+ add_to_total_rewards(&rewards, friendly);
+ hashmap_put(st->characters, st->curr_character, &rewards);
+ }
+ }
+
+ // print fight results
+ printf("\nFIGHT %zu:\n", ++st->fight_count);
+ render_fight_results(*(FightResults *)data->data);
+ break;
+ }
+ }
+ }
+}
+
// loop through packets received from pcap_handle and analyze the TCP streams
void analyzer_loop(
TCPStream *server_stream, TCPStream *client_stream, pcap_t *pcap_handle, void *state, void (*parsed_data_handler)(void *state, DynArray parsed_data)
@@ -41,20 +149,6 @@ void analyzer_loop(
}
}
-// file analyzer parsed data handler
-void file_parsed_data_handler(void *state, DynArray parsed_data) {
- int *fights_count = state;
- ParsedData *data = NULL;
- while ((data = array_next(parsed_data, data))) {
- switch (data->data_type) {
- case TYPE_FIGHT_RESULTS:
- printf("\nFIGHT %d:\n", ++*fights_count);
- render_fight_results(*(FightResults *)data->data);
- break;
- }
- }
-}
-
void analyze_file(char *path) {
// open the file
char errbuf[PCAP_ERRBUF_SIZE];
@@ -69,9 +163,17 @@ void analyze_file(char *path) {
// analyze the file
TCPStream server_stream = new_stream(SERVER);
TCPStream client_stream = new_stream(CLIENT);
- int state = 0;
+ FileAnalState state = init_file_anal_state();
analyzer_loop(&server_stream, &client_stream, handle, &state, file_parsed_data_handler);
+
+ // TODO: replace this
+ TotalRewards *r = hashmap_get(state.characters, "Denem");
+ if (r)
+ render_total_rewards(*r);
+
+ // free everything
destroy_stream(server_stream);
destroy_stream(client_stream);
+ destroy_file_anal_state(state);
pcap_close(handle);
}
diff --git a/analyzer/analyzer.h b/analyzer/analyzer.h
index 1b8d2f9..15bddf2 100644
--- a/analyzer/analyzer.h
+++ b/analyzer/analyzer.h
@@ -1,7 +1,32 @@
// analyzer.h
// functions for analyzing the packets received from the game server
+#include "structures/hashmap.h"
+#include "structures/array.h"
+#include "parser/fight_results/fight_results.h"
+
+#ifndef ANALYZER_H
+#define ANALYZER_H
+
+#define MAX_NAME_LENGTH 11
+
+typedef struct {
+ int exp;
+ int gold;
+ DynArray items[MAX_ITEM_TYPE]; // item type -> dynamic array of items
+ int psycho;
+ HashMap *saturations; // saturation type -> total gained
+} TotalRewards;
+
+typedef struct {
+ size_t fight_count;
+ char curr_character[MAX_NAME_LENGTH+1];
+ HashMap *characters; // nick -> TotalRewards
+} FileAnalState;
+
// analyze file
// params:
// - path - path to the file to analyze
extern void analyze_file(char *path);
+
+#endif
diff --git a/analyzer/parser/fight_results/fight_results.c b/analyzer/parser/fight_results/fight_results.c
index 942d9b4..f79d113 100644
--- a/analyzer/parser/fight_results/fight_results.c
+++ b/analyzer/parser/fight_results/fight_results.c
@@ -189,6 +189,7 @@ FriendlyResults parse_to_friendly(EntityResults entity_results) {
.level = entity_results.level,
.psycho = entity_results.psycho,
.splinters = entity_results.splinters,
+ .saturation_type = NULL,
.saturation = 0
};
diff --git a/analyzer/parser/fight_results/fight_results.h b/analyzer/parser/fight_results/fight_results.h
index 03b3e7b..b719ca9 100644
--- a/analyzer/parser/fight_results/fight_results.h
+++ b/analyzer/parser/fight_results/fight_results.h
@@ -18,7 +18,7 @@
#define SEPARATOR_ITEMS "[-]"
// Item struct - for keeping item string and type
-typedef enum { NORMAL, DRIF, ORB, SYNG, SET, RARE, EPIC } ItemType;
+typedef enum { NORMAL, DRIF, ORB, SYNG, SET, RARE, EPIC, MAX_ITEM_TYPE } ItemType;
typedef struct {
char *data;
ItemType type;
diff --git a/analyzer/render.c b/analyzer/render.c
index fbaa72e..cd4835d 100644
--- a/analyzer/render.c
+++ b/analyzer/render.c
@@ -87,8 +87,9 @@ int item_type_to_color(ItemType type) {
return COLOR_AQUA;
case EPIC:
return COLOR_PURPLE;
+ default:
+ return COLOR_WHITE;
}
- return COLOR_WHITE;
}
void print_player_rewards(FriendlyResults results) {
@@ -162,3 +163,55 @@ void render_fight_results(FightResults results) {
print_row_separator();
}
}
+
+// TODO: improve this ugly shit
+void render_total_rewards(TotalRewards rewards) {
+ printf("\n");
+ print_center("TOTAL REWARDS", COLOR_GREEN, TABLE_LINE_WIDTH, TABLE_ROW_SEPARATOR_CHAR);
+ printf("\n");
+
+ print_column_names();
+ print_row_separator();
+
+ char buffers[ARR_LEN(columns)][TABLE_LINE_WIDTH];
+ sprintf(buffers[0], "---");
+ sprintf(buffers[1], "%d", rewards.exp);
+ sprintf(buffers[2], "%d", rewards.gold);
+ sprintf(buffers[3], "%d", rewards.psycho);
+ int *sat = hashmap_get(rewards.saturations, "Yantrei");
+ sprintf(buffers[4], "%d Yan", sat ? *sat : 0);
+ sprintf(buffers[5], "---");
+
+ printf(TABLE_LINE_START);
+ 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");
+
+ for (size_t i = 0; i < MAX_ITEM_TYPE; i++) {
+ if (rewards.items[i].count > 0) {
+ printf(TABLE_LINE_START);
+ print_center("ITEMS", COLOR_WHITE, TABLE_ITEMS_WIDTH, ' ');
+ printf(TABLE_LINE_END "\n" TABLE_LINE_START);
+
+ size_t line_len = 0;
+ char **item = NULL;
+ while ((item = array_next(rewards.items[i], item))) {
+ char *sep = array_next(rewards.items[i], item) ? ", " : "";
+ size_t item_len = utf8_strlen(*item) + strlen(sep);
+ if (line_len + item_len > TABLE_ITEMS_WIDTH) {
+ printf("%*s" TABLE_LINE_END "\n" TABLE_LINE_START, (int)(TABLE_ITEMS_WIDTH - line_len), "");
+ line_len = 0;
+ }
+ print_color(*item, item_type_to_color(i));
+ printf("%s", sep);
+ line_len += item_len;
+ }
+ printf("%*s" TABLE_LINE_END "\n", (int)(TABLE_ITEMS_WIDTH - line_len), "");
+ }
+ }
+ print_row_separator();
+}
diff --git a/analyzer/render.h b/analyzer/render.h
index c28f3e3..7ad2e92 100644
--- a/analyzer/render.h
+++ b/analyzer/render.h
@@ -3,6 +3,7 @@
#include "parser/fight_results/fight_results.h"
#include "utils/memory.h"
+#include "analyzer.h"
// ANSI escape sequence colors
#define COLOR_GOLD 33
@@ -53,3 +54,8 @@ inline static size_t sum_col_widths() {
// params:
// - results - fight results to render
extern void render_fight_results(FightResults results);
+
+// render total rewards onto the terminal
+// params:
+// - rewards - total rewards to render
+extern void render_total_rewards(TotalRewards rewards);
diff --git a/analyzer/structures/hashmap.c b/analyzer/structures/hashmap.c
index 53fd5fc..0ef2ade 100644
--- a/analyzer/structures/hashmap.c
+++ b/analyzer/structures/hashmap.c
@@ -8,7 +8,7 @@
HashMap *hashmap_new(
size_t key_size,
size_t val_size,
- size_t (*hash)(void *key),
+ uint32_t (*hash)(void *key),
int (*compare)(void *key1, void *key2),
void (*key_cleanup)(void *key),
void (*val_cleanup)(void *val)
@@ -26,7 +26,7 @@ HashMap *hashmap_new(
}
void hashmap_put(HashMap *map, void *key, void *val) {
- size_t key_hash = map->hash(key);
+ size_t key_hash = map->hash(key) % HASHMAP_TABLE_SIZE;
HashMapNode *node = map->table[key_hash];
// check if it exists
@@ -56,7 +56,7 @@ void hashmap_put(HashMap *map, void *key, void *val) {
}
void *hashmap_get(HashMap *map, void *key) {
- size_t key_hash = map->hash(key);
+ size_t key_hash = map->hash(key) % HASHMAP_TABLE_SIZE;
HashMapNode *node = map->table[key_hash];
// check if it exists
@@ -85,7 +85,7 @@ void free_node_without_next(HashMap *map, HashMapNode *node) {
}
void hashmap_delete(HashMap *map, void *key) {
- size_t key_hash = map->hash(key);
+ size_t key_hash = map->hash(key) % HASHMAP_TABLE_SIZE;
HashMapNode **p_node = &map->table[key_hash];
// check if it exists
@@ -114,3 +114,21 @@ void hashmap_free(HashMap *map) {
}
free(map);
}
+
+// FNV hash
+uint32_t hash_str(void *str) {
+ char *s = str;
+ uint32_t hash = 2166136261;
+ while (*s) {
+ hash ^= (unsigned char)*s;
+ hash *= 16777619;
+ s++;
+ }
+ return hash;
+}
+
+int compare_strs(void *str1, void *str2) {
+ char *s1 = str1;
+ char *s2 = str2;
+ return !strcmp(s1, s2);
+}
diff --git a/analyzer/structures/hashmap.h b/analyzer/structures/hashmap.h
index 10637f1..7559010 100644
--- a/analyzer/structures/hashmap.h
+++ b/analyzer/structures/hashmap.h
@@ -2,6 +2,10 @@
// a simple implementation of hashmap using linked list
#include <stddef.h>
+#include <stdint.h>
+
+#ifndef HASHMAP_H
+#define HASHMAP_H
#define HASHMAP_TABLE_SIZE 128
@@ -15,7 +19,7 @@ typedef struct {
HashMapNode *table[HASHMAP_TABLE_SIZE];
size_t key_size;
size_t val_size;
- size_t (*hash)(void *key);
+ uint32_t (*hash)(void *key);
int (*compare)(void *key1, void *key2);
void (*key_cleanup)(void *key);
void (*val_cleanup)(void *val);
@@ -29,7 +33,7 @@ typedef struct {
// params:
// - key - pointer to the key to hash
// returns:
-// hash of the key (size_t in [0; HASHMAP_TABLE_SIZE))
+// hash of the key
// - compare - function for comparing keys
// params:
// - key1 - pointer to the first of the keys to compare
@@ -49,7 +53,7 @@ typedef struct {
extern HashMap *hashmap_new(
size_t key_size,
size_t val_size,
- size_t (*hash)(void *key),
+ uint32_t (*hash)(void *key),
int (*compare)(void *key1, void *key2),
void (*key_cleanup)(void *key),
void (*val_cleanup)(void *val)
@@ -79,3 +83,18 @@ extern void hashmap_delete(HashMap *map, void *key);
// free the hash map
// - map - hash map to free
extern void hashmap_free(HashMap *map);
+
+// hash a string
+// - str - string to hash
+// returns:
+// hash of the string
+extern uint32_t hash_str(void *str);
+
+// compare 2 strings
+// - str1 - first string to compare
+// - str2 - second string to compare
+// returns:
+// 1 if strings are identical, 0 otherwise
+extern int compare_strs(void *str1, void *str2);
+
+#endif