--- /dev/null
+#include "libft.h"
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <limits.h>
+
+typedef struct s_database_entry
+{
+ int level;
+ time_t kill_time;
+ char *name;
+ char *biome;
+} t_entry;
+
+typedef enum e_basic_command
+{
+ basic_kill,
+ basic_add,
+ basic_list,
+ basic_remove,
+} t_basic;
+
+typedef struct s_command
+{
+ t_basic basic;
+ t_entry entry;
+} t_command;
+
+// I know this is not an efficient database, the bad things about it are:
+// the whole database is loaded into memory
+// the whole database is rewritten with every call to this program
+int write_entry(int fd, t_entry *entry)
+{
+ ft_dprintf(fd, "%s\037\t%i\037\t%s\037\t%i\036\n",
+ entry->name,
+ entry->level,
+ entry->biome,
+ entry->kill_time);
+ return (0);
+}
+
+int write_database(int fd, t_vec *database)
+{
+ size_t i;
+
+ i = 0;
+ while (i < database->size)
+ {
+ if (write_entry(fd, ft_vec_access(database, i++)))
+ {
+ close(fd);
+ return (1);
+ }
+ }
+ return (0);
+}
+
+int save_database(const char *filename, t_vec *database)
+{
+ int res;
+ int fd;
+
+ fd = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0)
+ return (1);
+ res = write_database(fd, database);
+ close(fd);
+ return (res);
+}
+
+time_t get_time_sec(void)
+{
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+ return (now.tv_sec);
+}
+
+void print_entry(t_entry *entry)
+{
+ int countdown;
+
+ countdown = 3600 - (get_time_sec() - entry->kill_time);
+ countdown = ft_max(0, countdown);
+ ft_printf(" %-20s%5i %-20s%2i:%02i\n",
+ entry->name,
+ entry->level,
+ entry->biome,
+ countdown / 60,
+ countdown % 60);
+ return ;
+}
+
+void print_database(t_vec *database)
+{
+ size_t i;
+
+ i = 0;
+ while (i < database->size)
+ print_entry(ft_vec_access(database, i++));
+ return ;
+}
+
+int advance_line(char **line)
+{
+ while (**line && **line != '\037' && **line != '\036')
+ ++*line;
+ if (**line == '\0')
+ return (1);
+ **line = '\0';
+ *line += 2;
+ return (0);
+}
+
+int parse_line(char *line, t_vec *database)
+{
+ t_entry new_entry;
+
+ new_entry.name = line;
+ if (advance_line(&line))
+ return (1);
+ new_entry.level = ft_atoi(line);
+ if (advance_line(&line))
+ return (1);
+ new_entry.biome = line;
+ if (advance_line(&line))
+ return (1);
+ new_entry.kill_time = ft_atoi(line);
+ if (advance_line(&line))
+ return (1);
+ if (*line != '\0')
+ return (1);
+ *line = '\0';
+ ft_vec_append(database, &new_entry);
+ return (0);
+}
+
+// Instead of freeing the line the database entry points
+// to the line and is freed at the free of the database
+// This is done as a malloc saving step (not needed, I just wanted to do it)
+int parse_database(const char *filename, t_vec *database)
+{
+ int fd;
+ char *line;
+
+ fd = open(filename, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0)
+ return (1);
+ line = get_next_line(fd);
+ while (line)
+ {
+ if (parse_line(line, database))
+ {
+ get_next_line(-1);
+ close(fd);
+ return (1);
+ }
+ line = get_next_line(fd);
+ }
+ close(fd);
+ return (0);
+}
+
+// This function does not free entry->biome as it is a part
+// of the block that starts with entry->name
+void free_entry(void *entry)
+{
+ free(((t_entry *)entry)->name);
+ return ;
+}
+
+int construct_new_entry(t_entry *entry, char **argv)
+{
+ char *datastr;
+ const size_t namelen = ft_strlen(argv[2]);
+ const size_t biomelen = ft_strlen(argv[4]);
+
+ datastr = malloc(namelen + biomelen + 2);
+ if (!datastr)
+ return (1);
+ ft_strlcpy(datastr, argv[2], namelen + biomelen + 2);
+ ft_strlcpy(datastr + namelen + 1, argv[4], biomelen + 1);
+ entry->name = datastr;
+ entry->level = ft_atoi(argv[3]);
+ entry->biome = datastr + namelen + 1;
+ entry->kill_time = 0;
+ return (0);
+}
+
+int parse_input(t_command *command, int argc, char **argv)
+{
+ command->basic = basic_list;
+ command->entry.name = NULL;
+ if (argc == 1)
+ return (0);
+ else if (argc == 2 && !ft_strcmp(argv[1], "-l"))
+ return (0);
+ else if (argc == 3)
+ {
+ if (ft_strcmp(argv[1], "-l") && ft_strcmp(argv[1], "-k") && ft_strcmp(argv[1], "-r"))
+ return (1);
+ command->entry.name = argv[2];
+ if (argv[1][1] == 'k')
+ command->basic = basic_kill;
+ else if (argv[1][1] == 'r')
+ command->basic = basic_remove;
+ return (0);
+ }
+ else if (argc == 5 && !ft_strcmp(argv[1], "-a"))
+ {
+ command->basic = basic_add;
+ return (construct_new_entry(&command->entry, argv));
+ }
+ return (1);
+}
+
+void kill_entry(t_vec *database, size_t ind)
+{
+ t_entry entry;
+
+ entry = *(t_entry *)ft_vec_access(database, ind);
+ entry.kill_time = get_time_sec();
+ ft_vec_forget(database, ind);
+ ft_vec_append(database, &entry);
+ return ;
+}
+
+int find_name_ind(t_vec *database, const char *name, size_t *ind)
+{
+ size_t i;
+ t_entry *entry;
+
+ i = 0;
+ while (i < database->size)
+ {
+ entry = (t_entry *)ft_vec_access(database, i);
+ if (!ft_strcmp(entry->name, name))
+ {
+ *ind = i;
+ return (0);
+ }
+ ++i;
+ }
+ return (1);
+}
+
+void carry_out_command(t_command *com, t_vec *database)
+{
+ size_t ind;
+
+ if (com->entry.name != NULL && find_name_ind(database, com->entry.name, &ind) && com->basic != basic_add)
+ {
+ ft_printf("Problem with the name!\n");
+ return ;
+ }
+ if (com->basic == basic_kill)
+ kill_entry(database, ind);
+ else if (com->basic == basic_remove)
+ ft_vec_erase(database, ind, free_entry);
+ else if (com->basic == basic_add)
+ ft_vec_append(database, &com->entry);
+ else if (com->basic == basic_list)
+ {
+ if (com->entry.name == NULL)
+ print_database(database);
+ else
+ print_entry(ft_vec_access(database, ind));
+ }
+ return ;
+}
+
+void print_help(void)
+{
+ static const char *helpstr =
+ "Palboss is a simple tracker of cooldown of bosses in Palworld\n"
+ "Usage:\tpalboss [option option_arguments]\n"
+ "\tWithout options it lists all the bosses\n"
+ "Options:\n"
+ "\t-l\n"
+ "\t\tPrint the list in a loop. End by using Q key.\n"
+ "\t-k boss\n"
+ "\t\tMark the boss as killed, setting the counter to 60 minutes.\n"
+ "\t-a boss level location\n"
+ "\t\tAdd a boss to the database. Long strings may break formatting.\n"
+ "\t-r boss\n"
+ "\t\tRemove a boss from the database.\n";
+
+ ft_printf("%s", helpstr);
+ return ;
+}
+
+int main(int argc, char **argv)
+{
+ t_vec database;
+ t_command command;
+
+ ft_vec_init(&database, sizeof(t_entry));
+ if (parse_input(&command, argc, argv))
+ {
+ print_help();
+ return (1);
+ }
+ if (parse_database("database.dat", &database))
+ return (2);
+ carry_out_command(&command, &database);
+ save_database("database.dat", &database);
+ ft_vec_free(&database, free_entry);
+ return (0);
+}