public sealed class BoundImportDirectoryParser
{
    public IReadOnlyList<BoundImportInfo> Parse(byte[] rawBytes, NtHeaders nt, IReadOnlyList<SectionInfo> sections)
    {
        if (nt.OptionalHeader.DataDirectories.Count <= 11)
        {
            return Array.Empty<BoundImportInfo>();
        }

        var dir = nt.OptionalHeader.DataDirectories[11];
        if (dir.VirtualAddress == 0 || dir.Size == 0)
        {
            return Array.Empty<BoundImportInfo>();
        }

        var baseOffset = RvaMapper.RvaToFileOffset(dir.VirtualAddress, sections, nt.OptionalHeader.SizeOfHeaders);
        if (baseOffset is null)
        {
            return Array.Empty<BoundImportInfo>();
        }

        var items = new List<BoundImportInfo>();
        var pos = baseOffset.Value;
        var end = Math.Min(rawBytes.Length, baseOffset.Value + (int)dir.Size);

        while (pos + 8 <= end)
        {
            var tds = BitConverter.ToUInt32(rawBytes, pos);
            var offsetModuleName = BitConverter.ToUInt16(rawBytes, pos + 4);
            var forwardRefs = BitConverter.ToUInt16(rawBytes, pos + 6);

            if (tds == 0 && offsetModuleName == 0 && forwardRefs == 0)
            {
                break;
            }

            var nameOffset = baseOffset.Value + offsetModuleName;
            var name = nameOffset < rawBytes.Length
                ? BinaryReaderExtensions.ReadAsciiStringAt(rawBytes, nameOffset, 512)
                : string.Empty;

            items.Add(new BoundImportInfo(tds, offsetModuleName, forwardRefs, name));
            pos += 8;
        }

        return items;
    }
}