@@ -169,41 +169,23 @@ typedef struct {
169169 uint32_t line , column ;
170170} Token ;
171171
172- static void initLexer (Lexer * _lexer , char * _buf ) {
173- _lexer -> buf = _buf ;
174- _lexer -> line = _lexer -> column = 1 ;
172+ static void initLexer (Lexer * _lexer ) {
173+ _lexer -> buf = NULL ;
174+ _lexer -> column = 1 ;
175+ _lexer -> line = 0 ;
175176}
176177
177178static bool isEol (const Lexer * _lexer ) {
178- return (_lexer -> buf [0 ] == '\n' || (_lexer -> buf [0 ] == '\r' && _lexer -> buf [1 ] != '\n' ));
179- }
180-
181- static bool isEof (const Lexer * _lexer ) {
182179 return (_lexer -> buf [0 ] == 0 );
183180}
184181
185182static bool isWhitespace (const Lexer * _lexer ) {
186183 return (_lexer -> buf [0 ] == ' ' || _lexer -> buf [0 ] == '\t' || _lexer -> buf [0 ] == '\r' );
187184}
188185
189- static void skipLine (Lexer * _lexer ) {
190- for (;;) {
191- if (isEof (_lexer ))
192- break ;
193- if (isEol (_lexer )) {
194- _lexer -> column = 1 ;
195- _lexer -> line ++ ;
196- _lexer -> buf ++ ;
197- break ;
198- }
199- _lexer -> buf ++ ;
200- _lexer -> column ++ ;
201- }
202- }
203-
204186static void skipWhitespace (Lexer * _lexer ) {
205187 for (;;) {
206- if (isEof (_lexer ))
188+ if (isEol (_lexer ))
207189 break ;
208190 if (!isWhitespace (_lexer ))
209191 break ;
@@ -212,13 +194,19 @@ static void skipWhitespace(Lexer *_lexer) {
212194 }
213195}
214196
197+ static void lexerSetLine (Lexer * _lexer , char * _buf ) {
198+ _lexer -> column = 1 ;
199+ _lexer -> line ++ ;
200+ _lexer -> buf = _buf ;
201+ }
202+
215203static void tokenize (Lexer * _lexer , Token * _token , bool includeWhitespace ) {
216204 uint32_t i = 0 ;
217205 skipWhitespace (_lexer );
218206 _token -> line = _lexer -> line ;
219207 _token -> column = _lexer -> column ;
220208 for (;;) {
221- if (isEof ( _lexer ) || isEol (_lexer ) || (!includeWhitespace && isWhitespace (_lexer )))
209+ if (isEol (_lexer ) || (!includeWhitespace && isWhitespace (_lexer )))
222210 break ;
223211 _token -> text [i ++ ] = _lexer -> buf [0 ];
224212 _lexer -> buf ++ ;
@@ -251,29 +239,60 @@ static bool parseInt(Lexer *_lexer, int32_t *_result) {
251239 return true;
252240}
253241
254- static char * readFile (const char * _filename , size_t * _length ) {
255- FILE * f ;
256- OBJZ_FOPEN (f , _filename , "rb" );
257- if (!f )
258- return NULL ;
259- fseek (f , 0 , SEEK_END );
260- const size_t length = (size_t )ftell (f );
261- if (_length )
262- * _length = length ;
263- fseek (f , 0 , SEEK_SET );
264- if (length <= 0 ) {
265- fclose (f );
266- return NULL ;
242+ typedef struct {
243+ char * buffer ;
244+ size_t length ;
245+ size_t pos ;
246+ } File ;
247+
248+ bool fileOpen (File * _file , const char * _filename ) {
249+ FILE * handle ;
250+ OBJZ_FOPEN (handle , _filename , "rb" );
251+ if (!handle )
252+ return false;
253+ fseek (handle , 0 , SEEK_END );
254+ _file -> length = (size_t )ftell (handle );
255+ fseek (handle , 0 , SEEK_SET );
256+ if (_file -> length == 0 ) {
257+ fclose (handle );
258+ return false;
267259 }
268- char * buffer = objz_malloc (length + 1 );
269- const size_t bytesRead = fread (buffer , 1 , length , f );
270- fclose (f );
271- if (bytesRead < length ) {
272- objz_free (buffer );
273- return NULL ;
260+ _file -> pos = 0 ;
261+ _file -> buffer = objz_malloc (_file -> length + 1 );
262+ const size_t bytesRead = fread (_file -> buffer , 1 , _file -> length , handle );
263+ fclose (handle );
264+ if (bytesRead < _file -> length ) {
265+ objz_free (_file -> buffer );
266+ return false;
267+ }
268+ _file -> buffer [_file -> length ] = 0 ;
269+ return true;
270+ }
271+
272+ void fileClose (File * _file ) {
273+ objz_free (_file -> buffer );
274+ }
275+
276+ char * fileReadLine (File * _file ) {
277+ if (_file -> buffer [_file -> pos ] == 0 )
278+ return NULL ; // eof
279+ char * start = & _file -> buffer [_file -> pos ];
280+ // Find eol. Newline is replaced with a null terminator. Position is set to the start of the next line.
281+ for (;;) {
282+ char * c = & _file -> buffer [_file -> pos ];
283+ if (* c == 0 ) {
284+ break ;
285+ }
286+ if (* c == '\r' )
287+ * c = 0 ; // Null terminate here, but keep reading until newline.
288+ else if (* c == '\n' ) {
289+ * c = 0 ;
290+ _file -> pos ++ ;
291+ break ;
292+ }
293+ _file -> pos ++ ;
274294 }
275- buffer [length ] = 0 ;
276- return buffer ;
295+ return start ;
277296}
278297
279298#define OBJZ_MAT_TOKEN_STRING 0
@@ -320,20 +339,21 @@ static bool loadMaterialFile(const char *_objFilename, const char *_materialName
320339 } else
321340 OBJZ_STRNCPY (filename , sizeof (filename ), _materialName );
322341 bool result = false;
323- char * buffer = readFile ( filename , NULL ) ;
324- if (!buffer )
342+ File file ;
343+ if (!fileOpen ( & file , filename ) )
325344 return result ;
326345 Lexer lexer ;
327- initLexer (& lexer , buffer );
346+ initLexer (& lexer );
328347 Token token ;
329348 objzMaterial mat ;
330349 materialInit (& mat );
331350 for (;;) {
351+ char * line = fileReadLine (& file );
352+ if (!line )
353+ break ;
354+ lexerSetLine (& lexer , line );
332355 tokenize (& lexer , & token , false);
333- if (token .text [0 ] == 0 ) {
334- if (isEof (& lexer ))
335- break ;
336- } else if (OBJZ_STRICMP (token .text , "newmtl" ) == 0 ) {
356+ if (OBJZ_STRICMP (token .text , "newmtl" ) == 0 ) {
337357 tokenize (& lexer , & token , false);
338358 if (token .text [0 ] == 0 ) {
339359 snprintf (s_error , OBJZ_MAX_ERROR_LENGTH , "(%u:%u) Expected name after 'newmtl'" , token .line , token .column );
@@ -366,13 +386,12 @@ static bool loadMaterialFile(const char *_objFilename, const char *_materialName
366386 }
367387 }
368388 }
369- skipLine (& lexer );
370389 }
371390 if (mat .name [0 ] != 0 )
372391 arrayAppend (_materials , & mat );
373392 result = true;
374393cleanup :
375- objz_free ( buffer );
394+ fileClose ( & file );
376395 return result ;
377396}
378397
@@ -710,35 +729,34 @@ void objz_setVertexFormat(size_t _stride, size_t _positionOffset, size_t _texcoo
710729}
711730
712731objzModel * objz_load (const char * _filename ) {
713- objzModel * model = NULL ;
714- size_t fileLength ;
715- char * buffer = readFile (_filename , & fileLength );
716- if (!buffer ) {
732+ File file ;
733+ if (!fileOpen (& file , _filename )) {
717734 snprintf (s_error , OBJZ_MAX_ERROR_LENGTH , "Failed to read file '%s'" , _filename );
718735 return NULL ;
719736 }
720737 // Parse the obj file and any material files.
721738 Array materials , positions , texcoords , normals , tempObjects , tempFaces ;
722739 Array faceIndices , tempFaceIndices ; // Re-used per face.
723740 arrayInit (& materials , sizeof (objzMaterial ), 16 );
724- arrayInit (& positions , sizeof (float ) * 3 , guessArrayInitialSize (fileLength , UINT16_MAX , 1 <<21 ));
725- arrayInit (& texcoords , sizeof (float ) * 2 , guessArrayInitialSize (fileLength , UINT16_MAX , UINT16_MAX ));
726- arrayInit (& normals , sizeof (float ) * 3 , guessArrayInitialSize (fileLength , 1 <<14 , 1 <<14 ));
727- arrayInit (& tempObjects , sizeof (TempObject ), guessArrayInitialSize (fileLength , 64 , 64 ));
728- arrayInit (& tempFaces , sizeof (TempFace ), guessArrayInitialSize (fileLength , 1 <<17 , 1 <<23 ));
741+ arrayInit (& positions , sizeof (float ) * 3 , guessArrayInitialSize (file . length , UINT16_MAX , 1 <<21 ));
742+ arrayInit (& texcoords , sizeof (float ) * 2 , guessArrayInitialSize (file . length , UINT16_MAX , UINT16_MAX ));
743+ arrayInit (& normals , sizeof (float ) * 3 , guessArrayInitialSize (file . length , 1 <<14 , 1 <<14 ));
744+ arrayInit (& tempObjects , sizeof (TempObject ), guessArrayInitialSize (file . length , 64 , 64 ));
745+ arrayInit (& tempFaces , sizeof (TempFace ), guessArrayInitialSize (file . length , 1 <<17 , 1 <<23 ));
729746 arrayInit (& faceIndices , sizeof (IndexTriplet ), 8 );
730747 arrayInit (& tempFaceIndices , sizeof (IndexTriplet ), 8 );
731748 int32_t currentMaterialIndex = -1 ;
732749 uint32_t flags = 0 ;
733750 Lexer lexer ;
734- initLexer (& lexer , buffer );
751+ initLexer (& lexer );
735752 Token token ;
736753 for (;;) {
754+ char * line = fileReadLine (& file );
755+ if (!line )
756+ break ;
757+ lexerSetLine (& lexer , line );
737758 tokenize (& lexer , & token , false);
738- if (token .text [0 ] == 0 ) {
739- if (isEof (& lexer ))
740- break ;
741- } else if (OBJZ_STRICMP (token .text , "f" ) == 0 ) {
759+ if (OBJZ_STRICMP (token .text , "f" ) == 0 ) {
742760 // Get current object.
743761 if (tempObjects .length == 0 ) {
744762 // No objects specifed, but there's a face, so create one.
@@ -840,7 +858,6 @@ objzModel *objz_load(const char *_filename) {
840858 arrayAppend (& texcoords , texcoord );
841859 flags |= OBJZ_FLAG_TEXCOORDS ;
842860 }
843- skipLine (& lexer );
844861 }
845862 // Do some post-processing of parsed data:
846863 // * find unique vertices from separately index vertex attributes (pos, texcoord, normal).
@@ -904,7 +921,7 @@ objzModel *objz_load(const char *_filename) {
904921 arrayAppend (& objects , & object );
905922 }
906923 // Build output data structure.
907- model = objz_malloc (sizeof (objzModel ));
924+ objzModel * model = objz_malloc (sizeof (objzModel ));
908925 model -> flags = flags ;
909926 if (s_indexFormat == OBJZ_INDEX_FORMAT_U32 || (flags & OBJZ_FLAG_INDEX32 ))
910927 model -> indices = indices .data ;
@@ -951,16 +968,15 @@ objzModel *objz_load(const char *_filename) {
951968 objz_free (tempFaceIndices .data );
952969 objz_free (vertexHashMap .indices .data );
953970 vertexHashMapDestroy (& vertexHashMap );
954- objz_free (buffer );
955971 return model ;
956972error :
973+ fileClose (& file );
957974 objz_free (materials .data );
958975 objz_free (positions .data );
959976 objz_free (texcoords .data );
960977 objz_free (normals .data );
961978 objz_free (faceIndices .data );
962979 objz_free (tempFaceIndices .data );
963- objz_free (buffer );
964980 return NULL ;
965981}
966982
0 commit comments