diff --git a/.gitignore b/.gitignore index c6127b3..f36f0f7 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ *.i*86 *.x86_64 *.hex +/cub3D # Debug files *.dSYM/ diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..4f3a506 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,17 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/usr/bin/clang", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-clang-x64", + "configurationProvider": "ms-vscode.makefile-tools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6cb9523 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "util.h": "c" + } +} diff --git a/Makefile b/Makefile index 6f950dd..255c719 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ NAME = cub3D -CPP = gcc -Wall -Wextra -Werror $(FLAGS) -C_FILES = $(wildcard *.c) +CPP = gcc -Wall -Wextra -Werror -I include $(FLAGS) +C_FILES = $(wildcard **/*.c) O_FILES = $(patsubst %.c,%.o,$(C_FILES)) ECHO = echoo(){ \ @@ -33,14 +33,13 @@ $(NAME) : $(O_FILES) @$(ECHO) "\t\t \e[0;92m\(^o^)/ \e[0;102;30;1m $(NAME) made! \e[0;92m \(^o^)/\e[0m" @$(ECHO) -%.o : %.cpp +%.o : %.c @$(ECHO) @$(ECHO) "\e[30;47;1m $(NAME): making '$@'... \e[0m" $(CPP) $(FLAGS_1) -c $< -o $@ check_headers : @ERROR=0; \ - echo $(wildcard **/*.h); \ for HEADER in $(wildcard **/*.h); \ do \ echo "check header $$HEADER..."; \ @@ -51,13 +50,12 @@ check_headers : if [ $$? -ne 0 ]; \ then \ ERROR=1; \ - echo error \ - break; \ + echo " error \e[31m:(\e[0m"; \ fi; \ - echo "good :)"; \ + echo " good \e[32m:)\e[0m"; \ 2> /dev/null rm -- "__tmp_check_header.out" "__tmp_check_header.c"; \ done; \ 2> /dev/null rm __tmp_check_header.out __tmp_check_header.c; \ if [ $$ERROR -eq 0 ]; then true; else false; fi; -.PHONY : all clean fclean re check_headers \ No newline at end of file +.PHONY : all clean fclean re check_headers diff --git a/dev/cub.vim b/dev/cub.vim new file mode 100644 index 0000000..72ed989 --- /dev/null +++ b/dev/cub.vim @@ -0,0 +1,21 @@ +function! CubSyntax() +if expand('%:e') == 'cub' + +highlight cubParameter ctermfg=blue +syntax match cubParameter /^(NO|SO|WE|EA|F|C) .$/ + +highlight cubDefault ctermfg=white +syntax match cubDefault /./ + +highlight cub0 ctermfg=grey +syntax match cub0 /0/ + +highlight cub1 cterm=bold ctermfg=white +syntax match cub1 /1/ + +highlight cubP cterm=bold ctermfg=blue +syntax match cubP /[NSWE]/ + +endif +endfunction +autocmd BufReadPost * call CubSyntax() diff --git a/include/color.h b/include/color.h new file mode 100644 index 0000000..98c01fa --- /dev/null +++ b/include/color.h @@ -0,0 +1,30 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* color.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/01 16:25:50 by mcolonna #+# #+# */ +/* Updated: 2024/10/02 15:41:43 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef COLOR_H +# define COLOR_H + +# include + +// TODO Must the transparency be 0 or 255? +/// @brief Represents an TRGB color in 0xTTRRGGBB format. +typedef __u32 t_color; + +/// @brief Convert a color from each color value to a t_color. +/// +/// @param red Level of red from 0 to 255. +/// @param green Level of green from 0 to 255. +/// @param blue Level of blue from 0 to 255. +/// @return The result. +t_color color_from_rgb(int red, int green, int blue); + +#endif diff --git a/include/map.h b/include/map.h index 67e379e..2224a3c 100644 --- a/include/map.h +++ b/include/map.h @@ -6,13 +6,16 @@ /* By: mcolonna +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/10/01 13:59:04 by mcolonna #+# #+# */ -/* Updated: 2024/10/01 14:00:40 by mcolonna ### ########.fr */ +/* Updated: 2024/10/04 13:13:04 by mcolonna ### ########.fr */ /* */ /* ************************************************************************** */ #ifndef MAP_H # define MAP_H +# include +# include "color.h" + /// @brief The type of a case. typedef enum e_map_wall { @@ -26,14 +29,20 @@ typedef enum e_map_wall typedef struct s_object { /// @brief Function called when creating the object. + /// /// @param data Address of the s_object.data pointer. void (*init)(void **data); - /// @brief Function called when destroying the object (to free everything). + + /// @brief Function called when destroying the object (to avoid leaks). + /// /// @param data Address of the s_object.data pointer. void (*destroy)(void **data); + /// @brief Function called each tick. + /// /// @param data Address of the s_object.data pointer. void (*tick)(void **data); + /// @brief Pointer the object can use to save data. void *data; } t_object; @@ -43,6 +52,7 @@ typedef struct s_map_case { /// @brief Is the case empty or a wall? t_map_wall wall; + /// @brief The object that appears on this case. If there is none, /// every pointer of the struct will be NULL. t_object object; @@ -51,14 +61,46 @@ typedef struct s_map_case /// @brief Represents a map. typedef struct s_map { + /// @brief Color of the floor. + t_color color_floor; + + /// @brief Color of the ceiling. + t_color color_ceiling; + + /// @brief Path to the image file for the wall face north. + const char *texture_north; + + /// @brief Path to the image file for the wall face south. + const char *texture_south; + + /// @brief Path to the image file for the wall face west. + const char *texture_west; + + /// @brief Path to the image file for the wall face east. + const char *texture_east; + /// @brief Width of the map. - int width; + unsigned int width; + /// @brief Height of the map. - int height; + unsigned int height; + /// @brief An 2D array of all the cases. - /// Syntax to get a case: cases[y][x] + /// + /// Syntax to get a case: cases[y * width + x] /// x is left to right, y is top to bottom. - t_map_case **cases; + t_map_case *cases; } t_map; -#endif \ No newline at end of file +/// @brief Create a t_map from the content of a .cub file. +/// +/// @param dest Pointer to the t_map to set. +/// @param file .cub file to use to create the t_map. +/// It must be destroyed with mapDestroy to avoid leaks. +/// @return false if an error occured, otherwise true. +bool map_from_file(t_map *dest, const char *file); + +/// @brief Destroy the map to avoid leaks. +void map_destroy(t_map *map); + +#endif diff --git a/include/map_mapping.h b/include/map_mapping.h new file mode 100644 index 0000000..5a3b2c9 --- /dev/null +++ b/include/map_mapping.h @@ -0,0 +1,31 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* map_mapping.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/04 12:00:07 by mcolonna #+# #+# */ +/* Updated: 2024/10/04 15:24:14 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef MAP_MAPPING_H +# define MAP_MAPPING_H + +# include "map.h" + +/// @brief Each element of g_map_mapping. A case and its associated char. +typedef struct s_map_mapping_element +{ + /// @brief char representing the case. + char name; + /// @brief associated case. + t_map_case value; +} t_map_mapping_element; + +/// @brief List of each char and its according case. +/// Ended by an element with the name '\0'. +extern const t_map_mapping_element g_map_mapping[]; + +#endif diff --git a/include/read_all_text.h b/include/read_all_text.h new file mode 100644 index 0000000..f86eec9 --- /dev/null +++ b/include/read_all_text.h @@ -0,0 +1,29 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* read_all_text.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/02 15:39:02 by mcolonna #+# #+# */ +/* Updated: 2024/10/02 17:04:33 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef READ_ALL_TEXT_H +# define READ_ALL_TEXT_H + +/// @brief Size of buffer used in read_all_text(). +# define BUFFER_SIZE 1000 + +/// @brief Read all the text of the given file. +/// +/// If the last line isn't ended by a '\\n', add one. +/// +/// If error, returns NULL and set errno accordingly. +/// +/// @param fd File descriptor of the file. +/// @return Alloc'd string of the file content. NULL if error. +char *read_all_text(int fd); + +#endif diff --git a/include/stream.h b/include/stream.h new file mode 100644 index 0000000..12232ac --- /dev/null +++ b/include/stream.h @@ -0,0 +1,54 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* stream.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/02 12:33:25 by mcolonna #+# #+# */ +/* Updated: 2024/10/02 16:14:23 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef STREAM_H +# define STREAM_H + +# include + +/// @brief Represents a string and an associated cursor. +typedef struct s_stream +{ + /// @brief Index of the cursor. + int i; + + /// @brief Pointer to the string. + const char *str; +} t_stream; + +/// @brief Read a specific string, error if it isn't the expected string. +/// +/// @param str Expected string. +/// @param stream Stream to use. +/// @param err Set to true if an error occured. +/// If already true, the function won't do anything. +void read_expected_string(const char *str, t_stream *stream, bool *err); + +/// @brief Read an unsigned int (which fits the pattern /[0-9]+/). +/// +/// @param dest Will be set to the value of the unsigned integer. +/// @param stream Stream to use. +/// @param err Set to true if an error occured. +/// If already true, the function won't do anything. +void read_unsigned(unsigned int *dest, t_stream *stream, bool *err); + +/// @brief Read a string until limit char or \0. +/// +/// @param c Limit char. +/// @param dest Will be set to an alloc'd pointer to the read string. +/// NULL if alloc error. +/// @param stream Stream to use. +/// @param err Set to true if an error occured. +/// If already true, the function won't do anything. +void read_until(char c, char **dest, t_stream *stream, bool *err); + +#endif diff --git a/include/util.h b/include/util.h new file mode 100644 index 0000000..bab8592 --- /dev/null +++ b/include/util.h @@ -0,0 +1,24 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* util.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/03 14:19:03 by mc #+# #+# */ +/* Updated: 2024/10/04 15:27:07 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef UTIL_H +# define UTIL_H + +/// @brief Get the length of a string. +int getlen(const char *str); + +/// @brief Write an error message on stderr. +/// +/// @param str... All the strings to write. The last parameter MUST BE NULL. +void write_err(const char *str, ...); + +#endif diff --git a/src/color.c b/src/color.c new file mode 100644 index 0000000..c0af271 --- /dev/null +++ b/src/color.c @@ -0,0 +1,18 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* color.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/01 17:04:56 by mcolonna #+# #+# */ +/* Updated: 2024/10/02 14:27:53 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "color.h" + +t_color color_from_rgb(int red, int green, int blue) +{ + return (red << 16 | green << 8 | blue); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..115ee5a --- /dev/null +++ b/src/main.c @@ -0,0 +1,34 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* main.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/02 15:45:49 by mcolonna #+# #+# */ +/* Updated: 2024/10/04 15:10:50 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "map.h" +#include + +int main(int argc, char *argv[]) +{ + t_map map; + bool success; + + if (argc != 2) + { + printf(":(\n"); + return (1); + } + success = map_from_file(&map, argv[1]); + printf("--> Success: %i <--\n", success); + if (success) + printf("C 0x%08x\nF 0x%08x\nEA %s\nWE %s\nNO %s\nSO %s\n", + map.color_ceiling, map.color_floor, + map.texture_east, map.texture_west, + map.texture_north, map.texture_south); + map_destroy(&map); +} diff --git a/src/map.c b/src/map.c new file mode 100644 index 0000000..475b024 --- /dev/null +++ b/src/map.c @@ -0,0 +1,55 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* map.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/01 17:12:58 by mcolonna #+# #+# */ +/* Updated: 2024/10/04 15:25:32 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "map.h" +#include "map_utils.h" +#include "stream.h" +#include "read_all_text.h" +#include +#include +#include +#include +#include + +bool map_from_file(t_map *dest, const char *file) +{ + int fd; + t_stream stream; + bool err; + + fd = open(file, O_RDONLY); + if (fd < 0) + { + write_err("Can't open '", file, "': ", strerror(errno), "\n", + NULL); + return (false); + } + stream.str = read_all_text(fd); + if (!stream.str) + { + write_err("Can't read file.\n", NULL); + return (false); + } + stream.i = 0; + err = read_map(dest, &stream); + free((void *)stream.str); + return (err); +} + +void map_destroy(t_map *map) +{ + free((void *)map->texture_east); + free((void *)map->texture_west); + free((void *)map->texture_north); + free((void *)map->texture_south); + free(map->cases); +} diff --git a/src/map_mapping.c b/src/map_mapping.c new file mode 100644 index 0000000..e4b575f --- /dev/null +++ b/src/map_mapping.c @@ -0,0 +1,24 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* map_mapping.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/04 12:05:21 by mcolonna #+# #+# */ +/* Updated: 2024/10/04 15:23:28 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "map_mapping.h" +#include + +const t_map_mapping_element g_map_mapping[] = { +{' ', {EMPTY, {NULL, NULL, NULL, NULL}}}, +{'0', {EMPTY, {NULL, NULL, NULL, NULL}}}, +{'1', {WALL, {NULL, NULL, NULL, NULL}}}, +{'N', {EMPTY, {NULL, NULL, NULL, NULL}}}, // TODO add player object +{'S', {EMPTY, {NULL, NULL, NULL, NULL}}}, // -- +{'E', {EMPTY, {NULL, NULL, NULL, NULL}}}, // -- +{'W', {EMPTY, {NULL, NULL, NULL, NULL}}}, // -- +{'\0', {EMPTY, {NULL, NULL, NULL, NULL}}}}; diff --git a/src/map_utils.h b/src/map_utils.h new file mode 100644 index 0000000..6c0a693 --- /dev/null +++ b/src/map_utils.h @@ -0,0 +1,80 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* map_utils.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/03 15:05:13 by mc #+# #+# */ +/* Updated: 2024/10/04 15:30:03 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef MAP_UTILS_H +# define MAP_UTILS_H + +# include "color.h" +# include "stream.h" +# include "map.h" +# include +# include + +/// @brief Read a parameter of the map which has a color as an argument. +/// Color format: [0-255],[0-255],[0-255] +/// @param name Name of the parameter. +/// @param dest Will be set to the value of the parameter. +/// Unchanged if error. +/// If success but *dest is not 0xFF000000, throws an error. +/// @param stream Stream to use. +/// Unchanged if error. +/// @param redefined Set to true if the parameter was already defined. +/// @return true if success or redefined, false if non-fatal error. +bool read_color_parameter(const char *name, t_color *dest, + t_stream *stream, bool *redefined); + +/// @brief Read a parameter of the map which has a string as an argument. +/// @param name Name of the parameter. +/// @param dest Will be set to an alloc'd pointer to the value of the parameter. +/// Unchanged if error. +/// If success but *dest is not null, throws an error. +/// @param stream Stream to use. +/// Unchanged if error. +/// @param redefined Set to true if the parameter was already defined. +/// @return true if success or redefined, false if non-fatal error. +bool read_string_parameter(const char *name, const char **dest, + t_stream *stream, bool *redefined); + +/// @brief Set the map to initial values. Every texture becomes NULL +/// and every color becomes 0xFF000000. +/// @param dest Map to reset. +void reset_map(t_map *dest); + +/// @brief Read a map-formatted string. +/// If an error occurs, write an error message on stderr. +/// @param dest Will be set to the map. +/// @param stream Stream to use. +/// @return true if success, false if error. +bool read_map(t_map *dest, t_stream *stream); + +/// @brief Get the case associated with the char. +/// +/// @param dest Will be set to the char. +/// @param name Name of the case. +/// @return true if success, false if error. +bool get_case(t_map_case *dest, char name); + +/// @brief Fill the pointer with 'size' null bytes. +/// +/// @param dest The pointer to set. +/// @param size The number of bytes. +void fill_zeros(void *dest, size_t size); + +/// @brief Read the map description part of the map. +/// Read until the end of the file. +/// +/// @param map The .width, .height and .cases members will be set. +/// @param stream Stream to use. +/// @return true if success, false if error. +bool read_map_description(t_map *map, t_stream *stream); + +#endif diff --git a/src/map_utils1.c b/src/map_utils1.c new file mode 100644 index 0000000..6377db0 --- /dev/null +++ b/src/map_utils1.c @@ -0,0 +1,116 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* map_utils1.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/03 15:02:09 by mc #+# #+# */ +/* Updated: 2024/10/04 15:28:06 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "map_utils.h" +#include "util.h" +#include "map_mapping.h" +#include + +bool read_color_parameter(const char *name, t_color *dest, + t_stream *stream, bool *redefined) +{ + unsigned int rgb[3]; + bool err; + t_stream old; + + old = *stream; + err = false; + read_expected_string(name, stream, &err); + read_expected_string(" ", stream, &err); + read_unsigned(&rgb[0], stream, &err); + read_expected_string(",", stream, &err); + read_unsigned(&rgb[1], stream, &err); + read_expected_string(",", stream, &err); + read_unsigned(&rgb[2], stream, &err); + read_expected_string("\n", stream, &err); + if (!err && *dest != 0xFF000000) + { + *redefined = true; + write_err("Parameter '", name, "' was defined several times\n", + NULL); + } + else if (!err) + *dest = color_from_rgb(rgb[0], rgb[1], rgb[2]); + else + *stream = old; + return (!err); +} + +bool read_string_parameter(const char *name, const char **dest, + t_stream *stream, bool *redefined) +{ + char *r; + bool err; + t_stream old; + + old = *stream; + err = false; + r = NULL; + read_expected_string(name, stream, &err); + read_expected_string(" ", stream, &err); + read_until('\n', &r, stream, &err); + read_expected_string("\n", stream, &err); + if (!err && *dest) + { + *redefined = true; + write_err("Parameter '", name, "' was defined several times\n", + NULL); + } + if (err || (!err && *dest)) + { + *stream = old; + free(r); + } + else + *dest = r; + return (!err); +} + +void reset_map(t_map *dest) +{ + dest->cases = NULL; + dest->color_ceiling = 0xFF000000; + dest->color_floor = 0xFF000000; + dest->texture_east = NULL; + dest->texture_west = NULL; + dest->texture_north = NULL; + dest->texture_south = NULL; +} + +bool read_map(t_map *dest, t_stream *stream) +{ + bool err; + bool rdf; + + reset_map(dest); + err = false; + rdf = false; + while (!rdf && !err && stream->str[stream->i]) + { + if (true + && (read_expected_string("\n", stream, &err), err) + && !read_string_parameter("NO", &dest->texture_north, stream, &rdf) + && !read_string_parameter("SO", &dest->texture_south, stream, &rdf) + && !read_string_parameter("WE", &dest->texture_west, stream, &rdf) + && !read_string_parameter("EA", &dest->texture_east, stream, &rdf) + && !read_color_parameter("F", &dest->color_floor, stream, &rdf) + && !read_color_parameter("C", &dest->color_ceiling, stream, &rdf)) + { + err = !read_map_description(dest, stream); + return (!err); + } + err = false; + } + if (!rdf && !dest->cases) + write_err("Map description missing\n", NULL); + return (!err && !rdf && dest->cases); +} diff --git a/src/map_utils2.c b/src/map_utils2.c new file mode 100644 index 0000000..7c762c2 --- /dev/null +++ b/src/map_utils2.c @@ -0,0 +1,105 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* map_utils2.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/04 15:12:08 by mcolonna #+# #+# */ +/* Updated: 2024/10/04 15:25:52 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "map_utils.h" +#include "map_mapping.h" +#include "util.h" +#include + +/// @brief Read the map description to get the dimensions. +/// The map must be at least 1x1. +/// +/// @param map The .width and .height members will be set. +/// @param stream Stream to use. +/// @return true if success, false if error. +static bool get_map_dimensions(t_map *map, t_stream *stream) +{ + unsigned int x; + unsigned int y; + + map->width = 0; + map->height = 0; + y = 0; + while (stream->str[stream->i]) + { + x = 0; + while (stream->str[stream->i] && stream->str[stream->i] != '\n') + { + stream->i++; + x++; + } + if (x > map->width) + map->width = x; + if (stream->str[stream->i] == '\n') + stream->i++; + y++; + } + map->height = y; + return (map->width > 0 && map->height > 0); +} + +bool get_case(t_map_case *dest, char name) +{ + int i; + char tmp[2]; + + i = -1; + while (g_map_mapping[++i].name) + { + if (name == g_map_mapping[i].name) + { + *dest = g_map_mapping[i].value; + return (true); + } + } + tmp[0] = name; + tmp[1] = '\0'; + write_err("Character '", tmp, "' unexpected\n", NULL); + return (false); +} + +void fill_zeros(void *dest, size_t size) +{ + size_t i; + + i = -1; + while (++i < size) + ((char *)dest)[i] = '\0'; +} + +bool read_map_description(t_map *map, t_stream *stream) +{ + t_stream stream2; + unsigned int x; + unsigned int y; + + stream2 = *stream; + if (!get_map_dimensions(map, &stream2)) + return (false); + map->cases = malloc((map->width * map->height) * sizeof(t_map_case)); + y = -1; + fill_zeros(map->cases, (map->width * map->height) * sizeof(t_map_case)); + while (++y < map->height) + { + x = 0; + while (stream->str[stream->i] && stream->str[stream->i] != '\n') + { + if (!get_case(&map->cases[y * map->width + x], + stream->str[stream->i])) + return (false); + stream->i++; + x++; + } + stream->i++; + } + return (true); +} diff --git a/src/read_all_text.c b/src/read_all_text.c new file mode 100644 index 0000000..490efc3 --- /dev/null +++ b/src/read_all_text.c @@ -0,0 +1,94 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* read_all_text.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mc +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/02 16:18:35 by mcolonna #+# #+# */ +/* Updated: 2024/10/03 14:20:37 by mc ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "read_all_text.h" +#include "util.h" +#include +#include +#include + +/// @brief Concatenate *dest and src and put the result in *dest. +/// +/// *dest is alloc'd to contain the result value. +/// +/// the old *dest is freed only if the function succeds. +/// +/// src doesn't have to be nul-terminated, instead n is the size of src. +/// +/// @return true if success, false if error. +static bool strconcat(char **dest, char *src, int n) +{ + const int len_dest = getlen(*dest); + char *old_dest; + int i; + + old_dest = *dest; + *dest = malloc((len_dest + n + 1) * sizeof(char)); + if (!*dest) + return (false); + i = -1; + while (old_dest[++i]) + (*dest)[i] = old_dest[i]; + while (i < len_dest + n) + { + (*dest)[i] = src[i - len_dest]; + i++; + } + (*dest)[i] = '\0'; + free(old_dest); + return (true); +} + +/// @brief If the string isn't empty and isn't ended by a '\\n', add one and +/// return the result. +/// +/// str is either returned directly, or another freeable pointer is returned +/// and str is freed. +/// +/// @return Return the result or NULL if error. +static char *add_endline_if_necessary(char *str) +{ + char *endline; + + if (str[0] == '\0' || str[getlen(str) - 1] == '\n') + return (str); + endline = malloc(1 * sizeof(char)); + if (!endline) + return (free(str), NULL); + endline[0] = '\n'; + if (!strconcat(&str, endline, 1)) + return (free(str), NULL); + return (str); +} + +char *read_all_text(int fd) +{ + char buf[BUFFER_SIZE]; + int n; + char *r; + + r = malloc(sizeof(char)); + if (!r) + return (NULL); + r[0] = '\0'; + n = 1; + while (n) + { + n = read(fd, buf, BUFFER_SIZE); + if (n < 0 || (n && !strconcat(&r, buf, n))) + { + free(r); + return (NULL); + } + } + return (add_endline_if_necessary(r)); +} diff --git a/src/stream.c b/src/stream.c new file mode 100644 index 0000000..107c464 --- /dev/null +++ b/src/stream.c @@ -0,0 +1,89 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* stream.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/02 14:08:10 by mcolonna #+# #+# */ +/* Updated: 2024/10/02 17:31:34 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "stream.h" +#include + +void read_expected_string(const char *str, t_stream *stream, bool *err) +{ + int i; + + if (*err) + return ; + i = 0; + while (str[i]) + { + if (str[i] != stream->str[stream->i]) + { + *err = true; + return ; + } + i++; + stream->i++; + } + return ; +} + +/// @brief Check if a character is a digit. +/// @param c Character to check. +/// @return true if the character is a digit, false if not. +/// false if the character is '\0'. +static bool is_digit(char c) +{ + return (c >= '0' && c <= '9'); +} + +void read_unsigned(unsigned int *dest, t_stream *stream, bool *err) +{ + int r; + + if (*err) + return ; + if (!is_digit(stream->str[stream->i])) + { + *err = true; + return ; + } + r = 0; + while (is_digit(stream->str[stream->i])) + { + r = r * 10 + stream->str[stream->i] - '0'; + stream->i++; + } + *dest = r; +} + +void read_until(char c, char **dest, t_stream *stream, bool *err) +{ + int len; + int i; + + if (*err) + return ; + len = 0; + while (stream->str[stream->i + len] && stream->str[stream->i + len] != c) + len++; + *dest = malloc((len + 1) * sizeof(char)); + if (!*dest) + { + *err = true; + return ; + } + i = 0; + while (stream->str[stream->i] && stream->str[stream->i] != c) + { + (*dest)[i] = stream->str[stream->i]; + i++; + stream->i++; + } + (*dest)[i] = '\0'; +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..a18b2c5 --- /dev/null +++ b/src/util.c @@ -0,0 +1,38 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* util.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mcolonna +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/10/03 14:19:38 by mc #+# #+# */ +/* Updated: 2024/10/04 15:26:32 by mcolonna ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "util.h" +#include +#include + +int getlen(const char *str) +{ + int i; + + i = 0; + while (str[i]) + i++; + return (i); +} + +void write_err(const char *str, ...) +{ + va_list args; + + va_start(args, str); + write(2, "Error\n", 6); + while (str) + { + write(2, str, getlen(str)); + str = va_arg(args, const char *); + } +} diff --git a/testmaps/hdhd.cub b/testmaps/hdhd.cub new file mode 100644 index 0000000..df272c6 --- /dev/null +++ b/testmaps/hdhd.cub @@ -0,0 +1,19 @@ + + +F 255,127,0 + +EA eastimage + + + +NO theimageforthenorthwall +C 0,2,67 + +SO SOUTH!!!!!!1 +SO jde: + +WE weeeee + + + +SO south2 diff --git a/testmaps/idid.cub b/testmaps/idid.cub new file mode 100644 index 0000000..0eb3904 --- /dev/null +++ b/testmaps/idid.cub @@ -0,0 +1,15 @@ +F 255,127,0 + +EA eastimage + +NO theimageforthenorthwall +C 0,2,67 + +SO SOUTH!!!!!!1 + +WE weeeee + + + + + diff --git a/testmaps/jdjd.cub b/testmaps/jdjd.cub new file mode 100644 index 0000000..cf473b2 --- /dev/null +++ b/testmaps/jdjd.cub @@ -0,0 +1,20 @@ +F 255,127,0 + +EA eastimage + +NO theimageforthenorthwall +C 0,2,67 + +SO SOUTH!!!!!!1 + +WE weeeee + + + + + +1111111 +1000001 +10W0001 +1000001 +1111111