Apart from using the actual quickfixengine it's easy to parse fix message when you know it contains specific tags.
It contains 0x1 separated pairs of 'key=value' strings. One complication are groups because you have to figure out that the tag is the first in a group (group header) and then figure out when the group ends (when it hits another tag not in the group).
Another problematic field is the RawData which can contain anything including the field separator 0x1, but it is preceded by RawDataLength so you have to read that first and then read RawDataLength number of bytes after the RawData tag to get to the next field.
I believe quickfixengine uses the dictionary of tags where it can figure out that the tag is first of a group then keep adding until hitting tag that is not in a group.
When I need to do custom parsing of FIX messages I mostly know exactly what messages and what data we expect so I can tweak it for those messages.