How do you deal with pointers in this case, as obviously they will have different values each time you run the engine? I suppose you could store some other data in the pointer field, and then fix the pointers at runtime -- e.g. maybe a hash of a filename. Or do you do something else?
First up, if you want to research this, the technique of loading your runtime data structures straight out of a file with no (or little) on-load processing is usually called "load-in-place" or "in-place memory", or something similar. There's a gamasutra article here.
For pointers within a particular asset (e.g. a pointer from a model header to an array of state-group pointers, to a state-group) I use the Offset (relative address) and Address (absolute address) classes in this header -- i.e. I don't use actual pointers.
For things that need to be pointers at runtime, but can't be known in advance (e.g. a pointer to an actual D3D vertex buffer), then they have to undergo "pointer patching" on-load.
For example, say a model asset has n vertex-buffers, and also has some state-groups that need to contain pointers to those vertex buffers. The state groups are saved with an integer from 0..n, in place of the VB pointer, which indicates the index of the VB that it should point to. On-load, the model's VBs are created by D3D and we now know the real pointer values. We can then iterate through the state-groups, reading these index integers and using them to look up the appropriate VB pointer, and writing the pointer over the top of the integer.
For references to other assets, I use filename hashes, yep. As above, these hashes can be converted to real pointers on-load, if required.
but if not, the next step is to check each state individually?
Yeah, my states are variable size, and the group-header doesn't contain the actual offset of each state. Therefore to iterate through the group, you have to inspect each state in a linear order, and determine the current state's size to know how far to jump ahead to find the next state. The bitfield does allow you to halt this iteration early if you know that you've already inspected all of the 'interesting' states in the group though.
As an alternative, you could allocate an array of size numStates in/after the header and write the offset of each state into this array. If you then ordered the states by their ID value, you'd be able to quickly jump to a particular state that you're interested in without iterating through each one.