Cornell Box The MDL I/O Library
Cornell University Program of Computer Graphics
Cornell Seal

The I/O Library

We have written a C++ library for reading and writing mdl files. The definitive documentation is the comments in mdl.H, but this page should serve as a general introduction to how the library should be used.

The principal objects in the library are mdlInput and mdlOutput, which control reading and writing mdl files. They provide transparent access to binary and text mdl files, and they can do type checking on all standard chunks. If you have your own special chunk types, you can provide type specifications for them as well.

Reading Mdl Files

Here is the essence of the interface to mdlInput:
class mdlInput {
 public:

   mdlInput(FILE *inputFile);
   ~mdlInput();

   mdlKey BeginChunk();
   void EndChunk();

   long ReadInt();
   float ReadFloat();
   char *ReadString();

   int ReadInts(long *dest, int n);
   int ReadFloats(float *dest, int n);

   int NumRemain();
   };

The constructor takes a file pointer that has been opened for reading with fopen. It will figure out for itself whether it's a binary or text file.

Recall that mdl files contain ints, floats, and strings arranged into nested chunks. Each chunk has a keyword telling what type of chunk it is and a sequence of data items, each of which can be an int, a float, a string, or a chunk. To read a chunk, you first open it by calling BeginChunk, which returns the chunk's keyword, then read its items. You read data items with the ReadThing functions, and you read chunks by following this same procedure recursively. Here is an example.

The functions called ReadThing are fairly self-explanatory. They each read exactly one data item. ReadString returns a pointer to a string that is valid only until the next time you call ReadString, so you'll have to make a copy of it if you want to use it for something.

The functions called ReadThings are functionally equivalent to calling ReadThing many times, but they are much more efficient, especially on binary files. You are responsible for making sure dest points to an array of the right type with space for n items. These functions return the number of items actually read, which could be different from n if you tried to read more items than were present in the chunk.

The NumRemain function returns a number n that is equal to zero if you have read everything in the current chunk and greater than zero if you have not. As an added bonus, if there are only ints and floats remaining in the chunk, n is the number of data items remaining. If there are chunks or strings remaining, this is not true. If there are no chunks open, NumRemain tells you whether you are at the end of the file of not. Here is an example.

Type Checking in Mdl

With every chunk definition comes a specification of the sequence of item types allowed that kind of chunk. Type checking is a way for the I/O code to enforce compilance with this specification, so that the program that reads a data item and the program that wrote it will always agree about what type it is. It also enforces correspondence between the types in a text file and the types that are read from it.

Many optional arguments to the contructors of mdlInput and mdlOutput are concerned with type checking. Here are the full declarations:

mdlInput(FILE *, int checkTypes = TRUE, const mdlChunkSpecList *specs = NULL);
mdlOutput(FILE *, int binary = FALSE, int typeCheck = TRUE,
	  mdlByteOrder = mdlSame, mdlChunkSpecList *specs = NULL);

Type checking is turned on by default in both mdlInput and mdlOutput, so you don't have to do anything to have your types checked for standard chunks. If you use non-standard chunks, the library will warn you that it does not have a type specification and will not perform type checking on those chunks. If you want type checking turned off for some reason (for instance, if you want to read old files that don't comply with the new standard type specifications), you can pass FALSE for checkTypes.

In order to get the benefits of type checking for non-standard chunk types, you have to provide your own type strings. You do this by creating a mdlChunkSpecList and passing it to the constructor of mdlInput or mdlOutput as the specs argument. The easiest way to create a mdlChunkSpecList is to make a NULL-terminated array of mdlChunkSpecs, each of which gives a keyword and a type string, and pass it to the constructor of mdlChunkSpecList.

Type strings use exactly the same format as the type specifications of the syntax description, but the repeating part is separated from the fixed part by a comma, and there are no spaces or parentheses or asterisks. For example:

  • msh: sC (C)* becomes ``sC,C''
  • vrtxPstn: (fff)* becomes ``,fff''
  • cmr: s fff fff fff f ff ff f (C)* becomes ``sfffffffffffffff,C''
  • ``'' specifies an empty chunk
Here is an example.


Last updated 10/07/04 PCG www Home