Implement palboss program
authorLukáš Jiřiště <gymnazium.jiriste@gmail.com>
Thu, 7 Mar 2024 09:32:41 +0000 (10:32 +0100)
committerLukáš Jiřiště <gymnazium.jiriste@gmail.com>
Thu, 7 Mar 2024 09:32:41 +0000 (10:32 +0100)
.gitignore [new file with mode: 0644]
.gitmodules [new file with mode: 0644]
Libft [new submodule]
Makefile [new file with mode: 0644]
README.txt [new file with mode: 0644]
TODO [new file with mode: 0644]
src/main.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..0401c62
--- /dev/null
@@ -0,0 +1,3 @@
+*.[ao]
+palboss
+database.dat
diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..626d139
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "Libft"]
+       path = Libft
+       url = git://ljiriste.work/Libft
diff --git a/Libft b/Libft
new file mode 160000 (submodule)
index 0000000..db179cd
--- /dev/null
+++ b/Libft
@@ -0,0 +1 @@
+Subproject commit db179cd200163de7d1cd25cc02b868edd0766cb2
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..f9be1eb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,50 @@
+CC := gcc
+CFLAGS = -std=c99 -Wall -Wextra -Werror -Wpedantic
+
+RM := rm -f
+
+SUBPROJECTS := Libft
+
+INCDIR := inc
+INCDIR += $(addsuffix /inc, $(SUBPROJECTS));
+INCLUDE := $(addprefix -I, $(INCDIR))
+
+SRCDIR := src
+
+SOURCES :=     main.c                  \
+
+SOURCES := $(addprefix $(SRCDIR)/, $(SOURCES))
+
+OBJECTS := $(SOURCES:.c=.o)
+
+NAME := palboss
+
+all : $(NAME)
+
+debug : CFLAGS += -g
+debug : $(NAME)
+
+$(NAME) : $(OBJECTS) Libft/libft.a
+       $(CC) $(CFLAGS) -o $@ $^
+
+Libft/libft.a : | Libft/Makefile
+ifneq (,$(findstring debug, $(MAKECMDGOALS)))
+       $(MAKE) -C Libft debug
+else
+       $(MAKE) -C Libft
+endif
+
+%.o : %.c | Libft/Makefile
+       $(CC) $(CFLAGS) -o $@ -c $< $(INCLUDE)
+
+%/Makefile :
+       git submodule update --init $($@%/Makefile=%)
+
+clean :
+       $(RM) $(OBJECTS)
+
+fclean : clean
+       $(RM) $(NAME)
+
+re : fclean
+       $(MAKE) all
diff --git a/README.txt b/README.txt
new file mode 100644 (file)
index 0000000..35b463e
--- /dev/null
@@ -0,0 +1,7 @@
+This is my a small project that keeps track (you have to make it keep track)
+of the bosses kill times in Palworld.
+The utility lies in that the program shows you bosses, that are ready to be
+fought so that you don't have to search the map for available bosses.
+
+Simply make the project to use.
+Databse of bosses not included (yet?)
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..ab7ef36
--- /dev/null
+++ b/TODO
@@ -0,0 +1,5 @@
+TODO
+give option -l the functionality described in help
+implement -h flag?
+colored output
+sort available bosses by level?
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..ff61748
--- /dev/null
@@ -0,0 +1,312 @@
+#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);
+}