From: Lukáš Jiřiště Date: Thu, 7 Mar 2024 09:32:41 +0000 (+0100) Subject: Implement palboss program X-Git-Url: https://git.ljiriste.work/?a=commitdiff_plain;h=0580505fabae20a5332e6ae133138bc5265d7e64;p=palboss.git Implement palboss program --- 0580505fabae20a5332e6ae133138bc5265d7e64 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0401c62 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.[ao] +palboss +database.dat diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..626d139 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Libft"] + path = Libft + url = git://ljiriste.work/Libft diff --git a/Libft b/Libft new file mode 160000 index 0000000..db179cd --- /dev/null +++ b/Libft @@ -0,0 +1 @@ +Subproject commit db179cd200163de7d1cd25cc02b868edd0766cb2 diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..35b463e --- /dev/null +++ b/README.txt @@ -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 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 index 0000000..ff61748 --- /dev/null +++ b/src/main.c @@ -0,0 +1,312 @@ +#include "libft.h" +#include +#include +#include +#include +#include +#include +#include + +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); +}