public sealed class LoadConfigDirectoryParser
{
    public LoadConfigInfo? Parse(byte[] rawBytes, NtHeaders nt, IReadOnlyList<SectionInfo> sections)
    {
        if (nt.OptionalHeader.DataDirectories.Count <= 10)
        {
            return null;
        }

        var dir = nt.OptionalHeader.DataDirectories[10];
        if (dir.VirtualAddress == 0 || dir.Size == 0)
        {
            return null;
        }

        var offset = RvaMapper.RvaToFileOffset(dir.VirtualAddress, sections, nt.OptionalHeader.SizeOfHeaders);
        if (offset is null)
        {
            return null;
        }

        var pos = offset.Value;
        BoundsChecker.EnsureRange(pos, 16, rawBytes.Length, "load config directory");

        uint size = BitConverter.ToUInt32(rawBytes, pos);
        uint timeDateStamp = pos + 8 <= rawBytes.Length ? BitConverter.ToUInt32(rawBytes, pos + 4) : 0;
        ushort major = pos + 10 <= rawBytes.Length ? BitConverter.ToUInt16(rawBytes, pos + 8) : (ushort)0;
        ushort minor = pos + 12 <= rawBytes.Length ? BitConverter.ToUInt16(rawBytes, pos + 10) : (ushort)0;

        ulong securityCookie = 0;
        if (!nt.OptionalHeader.IsPe32Plus)
        {
            if (pos + 64 <= rawBytes.Length)
            {
                BoundsChecker.EnsureRange(pos + 60, 4, rawBytes.Length, "load config security cookie");
                securityCookie = BitConverter.ToUInt32(rawBytes, pos + 60);
            }
        }
        else
        {
            if (pos + 96 <= rawBytes.Length)
            {
                BoundsChecker.EnsureRange(pos + 88, 8, rawBytes.Length, "load config security cookie");
                securityCookie = BitConverter.ToUInt64(rawBytes, pos + 88);
            }
        }

        return new LoadConfigInfo(size, timeDateStamp, major, minor, securityCookie);
    }
}