public static M ModParse <M, G>(
            M record,
            MutagenFrame frame,
            G importMask,
            RecordTypeConverter?recordTypeConverter,
            RecordStructFill <M> fillStructs,
            ModRecordTypeFill <M, G> fillTyped)
        {
            var modHeader = frame.Reader.GetMod();

            fillTyped(
                record: record,
                frame: frame,
                importMask: importMask,
                nextRecordType: modHeader.RecordType,
                contentLength: checked ((int)modHeader.ContentLength),
                recordTypeConverter: recordTypeConverter);
            while (!frame.Complete)
            {
                var groupHeader = frame.GetGroup();
                if (!groupHeader.IsGroup)
                {
                    throw new ArgumentException("Did not see GRUP header as expected.");
                }
                var len      = checked ((int)groupHeader.ContentLength);
                var finalPos = frame.Position + groupHeader.TotalLength;
                if (len == 0)
                {
                    frame.Position = finalPos;
                    continue;
                }
                var parsed = fillTyped(
                    record: record,
                    frame: frame,
                    importMask: importMask,
                    nextRecordType: groupHeader.ContainedRecordType,
                    contentLength: len,
                    recordTypeConverter: recordTypeConverter);
                if (parsed.Failed)
                {
                    break;
                }
                if (frame.Position < finalPos)
                {
                    frame.Position = finalPos;
                }
            }
            frame.SetToFinalPosition();
            return(record);
        }
        public static M MajorRecordParse <M>(
            M record,
            MutagenFrame frame,
            RecordTypeConverter?recordTypeConverter,
            RecordStructFill <M> fillStructs,
            RecordTypeFill <M> fillTyped)
            where M : IMajorRecordCommonGetter
        {
            frame = frame.SpawnWithFinalPosition(HeaderTranslation.ParseRecord(frame.Reader));
            fillStructs(
                record: record,
                frame: frame);
            if (fillTyped == null)
            {
                return(record);
            }
            MutagenFrame targetFrame = frame;

            if (record.IsCompressed)
            {
                targetFrame = frame.Decompress();
            }
            while (!targetFrame.Complete)
            {
                var subMeta  = targetFrame.GetSubrecord();
                var finalPos = targetFrame.Position + subMeta.TotalLength;
                var parsed   = fillTyped(
                    record: record,
                    frame: targetFrame,
                    nextRecordType: subMeta.RecordType,
                    contentLength: subMeta.ContentLength,
                    recordTypeConverter: recordTypeConverter);
                if (parsed.Failed)
                {
                    break;
                }
                if (targetFrame.Position < finalPos)
                {
                    targetFrame.Position = finalPos;
                }
            }
            frame.SetToFinalPosition();
            return(record);
        }
        public static G GroupParse <G>(
            G record,
            MutagenFrame frame,
            RecordTypeConverter?recordTypeConverter,
            RecordStructFill <G> fillStructs,
            RecordTypeFill <G> fillTyped)
        {
            var groupMeta = frame.GetGroup();

            if (!groupMeta.IsGroup)
            {
                throw new ArgumentException($"Expected GRUP header was not read in: {frame.Position}");
            }
            frame.Position += groupMeta.TypeAndLengthLength;
            frame           = frame.ReadAndReframe(checked ((int)(groupMeta.TotalLength - groupMeta.TypeAndLengthLength)));

            fillStructs?.Invoke(
                record: record,
                frame: frame);
            while (!frame.Complete)
            {
                var nextRecordType = HeaderTranslation.GetNextSubrecordType(
                    reader: frame.Reader,
                    contentLength: out var contentLength);
                var finalPos = frame.Position + contentLength;
                var parsed   = fillTyped(
                    record: record,
                    frame: frame,
                    nextRecordType: nextRecordType,
                    contentLength: contentLength,
                    recordTypeConverter: recordTypeConverter);
                if (parsed.Failed)
                {
                    break;
                }
                if (frame.Position < finalPos)
                {
                    frame.Position = finalPos;
                }
            }
            frame.SetToFinalPosition();
            return(record);
        }