Cub3D uses .cub files that contain:
Texture paths for each wall direction (NO, SO, WE, EA)
Floor and ceiling colors as RGB values (F, C)
Map grid with walls (‘1’), empty spaces (‘0’), and player start (‘N’, ‘S’, ‘E’, ‘W’)
Example Map File
NO ./textures/north_wall.xpm
SO ./textures/south_wall.xpm
WE ./textures/west_wall.xpm
EA ./textures/east_wall.xpm
F 220,100,0
C 225,230,255
111111
100001
101N01
100001
111111
Empty lines are ignored. Attribute order doesn’t matter, but all six attributes (NO, SO, WE, EA, F, C) must be present before the map grid.
Parsing Pipeline
The parsing process follows this sequence:
Phase 1: Filename Validation
ismap() - File Extension Check
int ismap ( char * str , t_map * map )
{
int i;
int ii;
char * check;
i = ft_strlen (str);
ii = checkmap_name_folders (str);
if (ii > 0 )
ii ++ ;
if (i - ii < 5 )
return ( msg ( 2 , FORMAT), exit ( 1 ), 0 );
check = & str [i - 4 ];
check_printable_map_name (str, i, ii);
if ( check [ 0 ] == '.' && check [ 1 ] == 'c' && check [ 2 ] == 'u'
&& check [ 3 ] == 'b' && check [ 4 ] == ' \0 ' )
{
map -> name = ft_substr (str, ii, i - 4 - ii);
return ( 1 );
}
return ( 0 );
}
Check Minimum Length
Filename must be at least 5 characters (e.g., a.cub)
Extract Path Components
checkmap_name_folders() finds the last / to separate directory from filename
Validate Characters
check_printable_map_name() ensures all characters in the filename are printable
Check Extension
Last 4 characters must be exactly .cub
Extract Map Name
The filename without path or extension is stored as map->name (used for window title)
The validation exits the program on error rather than returning an error code. This is inconsistent with the rest of the parsing pipeline.
Phase 2: Attribute Parsing
check3atributtes() - Main Orchestrator
int check3atributtes (t_map * map )
{
int fd;
int i;
fd = open ( map -> path , O_RDONLY);
if (fd > 0 )
{
i = 1 ;
parsefloorceilingdirections (map, i, fd);
i = 0 ;
i = 0 ;
mapcpy (map, fd);
getfinalmap (map);
if ( map -> final_map == NULL )
return ( msg ( 2 , MAPERROR), freeme ( map -> final_map ), 0 );
flood_staff (map);
}
else
return ( msg ( 2 , MAPERROR2), 0 );
return ( close (fd), 1 );
}
This function:
Opens the .cub file
Parses texture/color attributes
Parses the map grid
Validates the map structure
Returns 1 on success, 0 on failure
parsefloorceilingdirections() - Attribute Loop
parselineutils.c (lines 64-82)
void parsefloorceilingdirections (t_map * map , int i , int fd )
{
char * line;
while (i)
{
line = get_next_line (fd);
if (i == 1 && line == NULL )
return ( msg ( 2 , MAPERROR3), freetextures (map), exit ( 1 ));
if (line && ft_strcmp (line, " \n " ))
checkatributes (map, line);
if ( all_atributes_asigned (map))
return ( ft_memdel (line));
if (line == NULL )
break ;
ft_memdel (line);
i ++ ;
}
}
Read Line
Uses get_next_line() to read one line at a time
Skip Empty Lines
Ignores lines that contain only \n
Parse Attribute
Calls checkatributes() for non-empty lines
Check Completion
Exits loop once all 6 attributes (NO, SO, WE, EA, F, C) are assigned
Handle EOF
Exits with error if file ends before all attributes are found
checkatributes() - Line Parser
parselineutils.c (lines 34-53)
static void checkatributes (t_map * map , char * line )
{
char ** arr;
int i;
i = 0 ;
arr = ft_split (line, ' ' );
if ( ! arr)
return ( msg ( 2 , MAPERROR9), ft_memdel (line), freeall (map), exit ( 1 ));
while ( arr [i])
i ++ ;
if (i > 2 && ft_strcmp ( arr [ 2 ], " \n " ))
return ( msg ( 2 , MAPERROR9), printf ( " %s " , arr [ 2 ]), ft_memdel (line),
freeall (map), freeme (arr), exit ( 1 ));
if ( ! ft_strcmp ( arr [ 0 ], " \n " ))
return ( freeme (arr));
if ( check_array_for_atributes (map, arr))
return ( freeme (arr), ft_memdel (line), freeall (map), exit ( 1 ));
freeme (arr);
}
Validates line format:
Splits on spaces
Ensures at most 2 tokens (identifier + value)
Calls check_array_for_atributes() to process
check_array_for_atributes() - Attribute Dispatcher
parselineutils.c (lines 15-32)
int check_array_for_atributes (t_map * map , char ** arr )
{
if ( ft_strcmp ( arr [ 0 ], "NO" ) && ft_strcmp ( arr [ 0 ], "SO" )
&& ft_strcmp ( arr [ 0 ], "WE" ) && ft_strcmp ( arr [ 0 ], "EA" )
&& ft_strcmp ( arr [ 0 ], "F" ) && ft_strcmp ( arr [ 0 ], "C" ))
return ( msg ( 2 , arr [ 0 ]), msg ( 2 , MAPERROR10), 1 );
if ( ! ft_strcmp ( arr [ 0 ], "F" ) || ! ft_strcmp ( arr [ 0 ], "C" ))
{
if ( asign_colors (map, arr))
return ( 1 );
}
else
{
if ( asign_directions (map, arr))
return ( 1 );
}
return ( 0 );
}
Routes to appropriate handler:
F or C → asign_colors()
NO, SO, WE, EA → asign_directions()
Anything else → Error
The function uses ft_strcmp() which returns 0 for equal strings. The logic may appear inverted due to this convention.
Phase 3: Texture Path Assignment
asign_directions() - Texture Path Validation
asignatributes.c (lines 37-56)
int asign_directions (t_map * map , char ** arr )
{
char * noendline;
int fd;
if ( arr [ 1 ])
noendline = noendl_dup ( arr [ 1 ]);
else
return ( msg ( 2 , NOPATH), 1 );
if (noendline)
if ( fill_directions (map, noendline, arr [ 0 ]))
return ( ft_memdel (noendline), 1 );
fd = open (noendline, O_RDONLY);
ft_memdel (noendline);
if (fd > 0 )
close (fd);
else
return ( msg ( 2 , IMG), 1 );
return ( 0 );
}
Strip Newline
noendl_dup() creates a copy without the trailing \n
Assign to Structure
fill_directions() stores the path in the appropriate field (northtexture, southtexture, etc.)
Verify File Exists
Attempts to open the file to ensure it exists and is readable
Check for Duplicates
fill_directions() returns error if the attribute was already set
fill_directions() - Field Assignment
asignatributes.c (lines 24-35)
static int fill_directions (t_map * map , char * noendline , char * arr0 )
{
if ( ! ft_strcmp (arr0, "NO" ) && ! map -> northtexture )
return ( map -> northtexture = ft_strdup (noendline), 0 );
if ( ! ft_strcmp (arr0, "SO" ) && ! map -> southtexture )
return ( map -> southtexture = ft_strdup (noendline), 0 );
if ( ! ft_strcmp (arr0, "WE" ) && ! map -> westtexture )
return ( map -> westtexture = ft_strdup (noendline), 0 );
if ( ! ft_strcmp (arr0, "EA" ) && ! map -> easttexture )
return ( map -> easttexture = ft_strdup (noendline), 0 );
return ( msg ( 2 , MAP_ARG_DUP), msg ( 2 , arr0), msg ( 2 , " \n " ), 1 );
}
Ensures each attribute is set exactly once.
Phase 4: Color Parsing
asign_colors() - RGB Validation
asignatributes.c (lines 58-78)
int asign_colors (t_map * map , char ** arr )
{
char * noendline;
char ** arr2;
if ( arr [ 1 ])
noendline = noendl_dup ( arr [ 1 ]);
else
return ( msg ( 2 , NOPATH), 1 );
if (noendline)
if ( fill_colors (map, noendline, arr [ 0 ]))
return ( ft_memdel (noendline), 1 );
arr2 = ft_split (noendline, ',' );
if ( ! arr2)
return ( ft_memdel (noendline), 1 );
ft_memdel (noendline);
if ( isvalidnum (arr2))
return ( freeme (arr2), 1 );
freeme (arr2);
return ( 0 );
}
Strip Newline
Creates a clean copy of the RGB string
Store String
fill_colors() saves the string to floorcolor or ceilingcolor
Split on Comma
Separates R, G, B components
Validate Numbers
isvalidnum() checks format and range
isvalidnum() - RGB Range Validation
int isvalidnum ( char ** arr2 )
{
int i;
int ii;
i = 0 ;
while ( arr2 [i])
i ++ ;
if (i != 3 )
return ( msg ( 2 , RGBERROR), 1 );
i = 0 ;
while ( arr2 [i])
{
ii = 0 ;
while ( arr2 [i][ii])
{
if ( ! ft_isdigit ( arr2 [i][ii]))
return ( msg ( 2 , FORMAT3), 1 );
ii ++ ;
}
i ++ ;
}
if ( isrgbable (arr2))
return ( 1 );
return ( 0 );
}
Validates:
Exactly 3 values (R, G, B)
All characters are digits
All values in range [0, 255]
isrgbable() - Range Check
int isrgbable ( char ** arr2 )
{
int i;
i = 0 ;
while ( arr2 [i])
{
if ( ft_atoi ( arr2 [i]) < 0 || ft_atoi ( arr2 [i]) > 255 )
return ( msg ( 2 , RGBERROR2), 1 );
i ++ ;
}
return ( 0 );
}
parse_color() - String to Integer Conversion
Colors are converted to integer format during initialization:
int parse_color ( const char * line )
{
int r;
int g;
int b;
while ( * line && ( * line == 'F' || * line == ' ' ))
line ++ ;
r = ft_atoi (line);
line = ft_strchr (line, ',' ) + 1 ;
g = ft_atoi (line);
line = ft_strchr (line, ',' ) + 1 ;
b = ft_atoi (line);
return ((r << 16 ) | (g << 8 ) | b);
}
Packs RGB into format: 0x00RRGGBB
The function assumes ‘F’ prefix for floor but is also used for ceiling colors. The prefix check is not robust.
Phase 5: Map Grid Parsing
mapcpy() - Copy Map Lines
void mapcpy (t_map * map , int fd )
{
int ii;
char * line;
int x;
x = 0 ;
while ( 1 )
{
line = get_next_line (fd);
if (line == NULL )
break ;
if ( ft_strcmp (line, " \n " ))
{
ii = 0 ;
while ( line [ii])
{
map -> map [x][ii] = line [ii];
ii ++ ;
}
map -> map [x][ii] = ' \0 ' ;
(x) ++ ;
}
ft_memdel (line);
}
map -> row = x;
}
Reads remaining lines into map->map[500][500], skipping empty lines.
getfinalmap() - Allocate Dynamic Array
void getfinalmap (t_map * map )
{
int i;
i = 0 ;
map -> final_map = ft_calloc ( map -> row + 1 , sizeof ( char * ));
if ( ! map -> final_map )
return ( msg ( 2 , MEMERROR), exit ( 1 ));
while (i < map -> row )
{
map -> final_map [i] = ft_strdup ( map -> map [i]);
i ++ ;
}
}
Copies static buffer to heap-allocated array for gameplay.
Phase 6: Map Validation
flood_staff() - Orchestrate Validation
void flood_staff (t_map * map )
{
int i;
i = 0 ;
map4flood (map);
if ( map -> toflood_map == NULL )
return ( msg ( 2 , "Error \n " ), freeall (map), exit ( 1 ));
if ( checkns (map, 0 , 0 , 0 ) != 1 )
return ( msg ( 2 , SPOTERROR), freeall (map), exit ( 1 ));
flood_fill (map, map -> toflood_map , map -> x , map -> y );
}
Create Flood Map Copy
map4flood() duplicates final_map to toflood_map
Find Player Position
checkns() finds exactly one N/S/E/W character and stores position
Flood Fill Validation
flood_fill() recursively marks all reachable spaces, exits with error if map edge is reached
checkns() - Find Player
int checkns (t_map * map , int count , int i , int ii )
{
while ( map -> toflood_map [i])
{
ii = 0 ;
while ( map -> toflood_map [i][ii])
{
if ( ft_is_nswe ( map -> toflood_map [i][ii]) != 1 )
return ( 0 );
if ( map -> toflood_map [i][ii] == 'N' || map -> toflood_map [i][ii] == 'S'
|| map -> toflood_map [i][ii] == 'E'
|| map -> toflood_map [i][ii] == 'W' )
{
if (count == 0 )
add_values (map, i, ii);
know_starting_angle (map, i, ii);
count ++ ;
}
ii ++ ;
}
i ++ ;
}
return (count);
}
Returns the count of player characters found (must be exactly 1).
ft_is_nswe() - Valid Character Check
int ft_is_nswe ( char c )
{
if (c == 'N' || c == 'S' || c == 'W' || c == 'E' || c == '1'
|| c == '0' || c == ' ' || c == ' ' || c == ' \n ' )
return ( 1 );
return ( 0 );
}
Allowed characters:
N, S, E, W : Player starting positions
1 : Wall
0 : Empty space
Space, tab, newline : Whitespace
know_starting_angle() - Set Initial Camera Direction
void know_starting_angle (t_map * map , int i , int ii )
{
if ( map -> toflood_map [i][ii] == 'S' )
map -> starting_angle = PI / 2 ;
else if ( map -> toflood_map [i][ii] == 'N' )
map -> starting_angle = 3 * PI / 2 ;
else if ( map -> toflood_map [i][ii] == 'E' )
map -> starting_angle = 2 * PI;
else if ( map -> toflood_map [i][ii] == 'W' )
map -> starting_angle = PI;
}
Character Angle Direction N 3π/2 North (up) S π/2 South (down) E 2π (0) East (right) W π West (left)
flood_fill() - Recursive Map Closure Check
void flood_fill (t_map * mapa , char ** map , int x , int y )
{
if (x < 0 )
return ( msg ( 2 , MAPERROR4), freeall (mapa), exit ( 1 ));
if (x == mapa -> row )
return ( msg ( 2 , MAPERROR4), freeall (mapa), exit ( 1 ));
if ( map [x][y] == ' ' || map [x][y] == ' \n ' || map [x][y] == ' '
|| map [x][y] == ' \0 ' )
return ( msg ( 2 , MAPERROR4), freeall (mapa), exit ( 1 ));
if ( ! map [x][y])
return ( msg ( 2 , MAPERROR4), freeall (mapa), exit ( 1 ));
if ( map [x][y] && map [x][y] != '1' )
{
if ( map [x][y] != 1 )
map [x][y] = '1' ;
flood_fill (mapa, map, x + 1 , y);
flood_fill (mapa, map, x - 1 , y);
flood_fill (mapa, map, x, y + 1 );
flood_fill (mapa, map, x, y - 1 );
}
}
Boundary Check
If coordinates are out of bounds (x < 0 or x == row), the map is not closed → Error
Whitespace Check
If a space/tab/newline is reached, the player can “escape” → Error
Mark Cell
Change the cell to ‘1’ to mark it as visited
Recurse
Flood fill in all 4 directions (up, down, left, right)
Stop at Walls
If cell is already ‘1’, stop recursion (wall or already visited)
The flood fill algorithm exits the program immediately on error rather than returning an error code. This makes it impossible to recover or provide detailed error locations.
Error Handling Summary
Fatal Errors (exit)
Invalid filename format
Cannot open file
Empty file
Missing attributes
Duplicate attributes
Texture file not found
Invalid RGB values
Multiple or no player positions
Map not closed
Invalid map characters
Memory allocation failure
Recoverable Errors (return 0)
None - all errors are fatal
All error messages are printed to stderr (fd 2) using the msg() function.
Common Validation Failures
Problem : Player can reach a space, tab, or map edge111111
100001 ← Open at top
1N0001
111111
Detection : Flood fill reaches whitespace or boundary
Problem : More than one N/S/E/W in the map111111
1N0E01 ← Two players
111111
Detection : checkns() returns count > 1
Problem : Map contains characters other than 0, 1, N, S, E, W, space, tab, newline111111
1N0#01 ← Invalid '#'
111111
Detection : ft_is_nswe() returns 0
Problem : Texture file path is invalid or file doesn’t existNO ./textures/missing.xpm ← File not found
Detection : open() returns -1 in asign_directions()
Problem : Color values out of range or wrong formatF 255,100 ← Only 2 values
C 255,256,0 ← 256 > 255
F 255,100,abc ← Non-numeric
Detection : isvalidnum() and isrgbable() checks
Memory Management
Parsing allocates memory that must be freed:
Allocation Freed By northtexture, southtexture, easttexture, westtexturefreetextures()floorcolor, ceilingcolorfreetextures()pathfreeall()namefreeall()final_mapfreeme() called by freeall()toflood_mapfreeme() called by freeall()spaceless_mapfreeme() called by freeall() (if allocated)
The freeall() function (not shown in provided code) is responsible for complete cleanup.
The static map[500][500] buffer does not need explicit freeing as it’s stack-allocated within the t_map structure.