Подтвердить что ты не робот

С# - Бинарный считыватель в Big Endian?

Я пытаюсь улучшить свое понимание формата файла STFS, используя программу для чтения всех разных бит информации. Используя веб-сайт с ссылкой, какие смещения содержат какую информацию, я написал код, который имеет двоичный считыватель, просматривает файл и помещает значения в правильные переменные.

Проблема в том, что все данные SUPPOSED являются Big Endian, и все, что читает бинарный считыватель, - Little Endian. Итак, что лучший способ сделать это?

Могу ли я создать мимический класс двоичного считывателя, который возвращает обратный массив байтов? Есть ли что-то, что я могу изменить в экземпляре класса, который заставит его читать в большом endian, поэтому мне не нужно переписывать все?

Любая помощь приветствуется.

edit: Я попытался добавить параметр Encoding.BigEndianUnicode в качестве параметра, но он по-прежнему читает мало endian.

4b9b3361

Ответ 1

Я обычно не отвечаю на свои вопросы, но я достиг именно того, чего хотел, с помощью простого кода:

class BinaryReader2 : BinaryReader { 
    public BinaryReader2(System.IO.Stream stream)  : base(stream) { }

    public override int ReadInt32()
    {
        var data = base.ReadBytes(4);
        Array.Reverse(data);
        return BitConverter.ToInt32(data, 0);
    }

    public Int16 ReadInt16()
    {
        var data = base.ReadBytes(2);
        Array.Reverse(data);
        return BitConverter.ToInt16(data, 0);
    }

    public Int64 ReadInt64()
    {
        var data = base.ReadBytes(8);
        Array.Reverse(data);
        return BitConverter.ToInt64(data, 0);
    }

    public UInt32 ReadUInt32()
    {
        var data = base.ReadBytes(4);
        Array.Reverse(data);
        return BitConverter.ToUInt32(data, 0);
    }

}

Я знал, что хотел, но не знал, как это написать. Я нашел эту страницу, и она помогла: http://www.codekeep.net/snippets/870c4ab3-419b-4dd2-a950-6d45beaf1295.aspx

Ответ 2

IMHO немного лучше ответить, поскольку он не требует появления нового класса, делает вызовы большого конца очевидными и позволяет смешивать вызовы big- и little-endian в потоке.

public static class Helpers
{
  // Note this MODIFIES THE GIVEN ARRAY then returns a reference to the modified array.
  public static byte[] Reverse(this byte[] b)
  {
    Array.Reverse(b);
    return b;
  }

  public static UInt16 ReadUInt16BE(this BinaryReader binRdr)
  {
    return BitConverter.ToUInt16(binRdr.ReadBytesRequired(sizeof(UInt16)).Reverse(), 0);
  }

  public static Int16 ReadInt16BE(this BinaryReader binRdr)
  {
    return BitConverter.ToInt16(binRdr.ReadBytesRequired(sizeof(Int16)).Reverse(), 0);
  }

  public static UInt32 ReadUInt32BE(this BinaryReader binRdr)
  {
    return BitConverter.ToUInt32(binRdr.ReadBytesRequired(sizeof(UInt32)).Reverse(), 0);
  }

  public static Int32 ReadInt32BE(this BinaryReader binRdr)
  {
    return BitConverter.ToInt32(binRdr.ReadBytesRequired(sizeof(Int32)).Reverse(), 0);
  }

  public static byte[] ReadBytesRequired(this BinaryReader binRdr, int byteCount)
  {
    var result = binRdr.ReadBytes(byteCount);

    if (result.Length != byteCount)
      throw new EndOfStreamException(string.Format("{0} bytes required from stream, but only {1} returned.", byteCount, result.Length));

    return result;
  }
}

Ответ 3

Я не знаком с STFS, но изменение endianess относительно просто. "Сетевой порядок" является большим аргументом, поэтому все, что вам нужно сделать, это перевести из сети в хостинг.

Это легко, потому что уже есть код, который делает это. Посмотрите IPAddress.NetworkToHostOrder, как описано здесь: ntohs() и ntohl() эквивалент?

Ответ 4

На мой взгляд, вы хотите быть осторожным, делая это. Причина, по которой нужно преобразовать из BigEndian в LittleEndian, - это чтение байтов в BigEndian, а операционная система, вычисляющая их, работает в LittleEndian.

С# больше не является окном. С такими портами, как Mono, а также другими платформами Microsoft, такими как Windows Phone 7/8, Xbox 360/Xbox One, Windwos CE, Windows 8 Mobile, Linux с MONO, Apple с MONO и т.д. Возможно, операционная платформа может быть в BigEndian, и в этом случае вы бы прикрутили себя, если бы вы преобразовали код без каких-либо проверок.

У BitConverter уже есть поле на нем, называемое "IsLittleEndian", которое вы можете использовать, чтобы определить, находится ли рабочая среда в LittleEndian или нет. Тогда вы можете сделать реверсирование условно.

Таким образом, я просто написал несколько байтовых [] расширений вместо создания большого класса:

    /// <summary>
    /// Get a byte array from a point in a source byte array and reverses the bytes. Note, if the current platform is not in LittleEndian the input array is assumed to be BigEndian and the bytes are not returned in reverse order
    /// </summary>
    /// <param name="byteArray">The source array to get reversed bytes for</param>
    /// <param name="startIndex">The index in the source array at which to begin the reverse</param>
    /// <param name="count">The number of bytes to reverse</param>
    /// <returns>A new array containing the reversed bytes, or a sub set of the array not reversed.</returns>
    public static byte[] ReverseForBigEndian(this byte[] byteArray, int startIndex, int count)
    {
        if (BitConverter.IsLittleEndian)
            return byteArray.Reverse(startIndex, count);
        else
            return byteArray.SubArray(startIndex, count);

    }

    public static byte[] Reverse(this byte[] byteArray, int startIndex, int count)
    {
        byte[] ret = new byte[count];
        for (int i = startIndex + (count - 1); i >= startIndex; --i)
        {
            byte b = byteArray[i];
            ret[(startIndex + (count - 1)) - i] = b;
        }
        return ret;
    }

    public static byte[] SubArray(this byte[] byteArray, int startIndex, int count)
    {
        byte[] ret = new byte[count];
        for (int i = 0; i < count; ++i)            
            ret[0] = byteArray[i + startIndex];
        return ret;
    }

Итак, представьте этот пример кода:

byte[] fontBytes = byte[240000]; //some data loaded in here, E.G. a TTF TrueTypeCollection font file. (which is in BigEndian)

int _ttcVersionMajor = BitConverter.ToUint16(fontBytes.ReverseForBigEndian(4, 2), 0);

//output
_ttcVersionMajor = 1 //TCCHeader is version 1

Ответ 5

В основном полная (для моих целей) замена для BinaryReader, которая корректно обрабатывает порядок байтов, в отличие от большинства этих ответов. По умолчанию он работает точно так же, как BinaryReader, но может быть сконструирован для чтения с требуемым порядком байтов. Кроме того, методы Read<Primitive> перегружены, чтобы вы могли указать порядок байтов для чтения определенного значения, что полезно в (маловероятном) сценарии, когда вы работаете с потоком смешанных данных LE/BE.

public class EndiannessAwareBinaryReader : BinaryReader
{
    public enum Endianness
    {
        Little,
        Big,
    }

    private readonly Endianness _endianness = Endianness.Little;

    public EndiannessAwareBinaryReader(Stream input) : base(input)
    {
    }

    public EndiannessAwareBinaryReader(Stream input, Encoding encoding) : base(input, encoding)
    {
    }

    public EndiannessAwareBinaryReader(Stream input, Encoding encoding, bool leaveOpen) : base(input, encoding, leaveOpen)
    {
    }

    public EndiannessAwareBinaryReader(Stream input, Endianness endianness) : base(input)
    {
        _endianness = endianness;
    }

    public EndiannessAwareBinaryReader(Stream input, Encoding encoding, Endianness endianness) : base(input, encoding)
    {
        _endianness = endianness;
    }

    public EndiannessAwareBinaryReader(Stream input, Encoding encoding, bool leaveOpen, Endianness endianness) : base(input, encoding, leaveOpen)
    {
        _endianness = endianness;
    }

    public override short ReadInt16() => ReadInt16(_endianness);

    public override int ReadInt32() => ReadInt32(_endianness);

    public override long ReadInt64() => ReadInt64(_endianness);

    public override ushort ReadUInt16() => ReadUInt16(_endianness);

    public override uint ReadUInt32() => ReadUInt32(_endianness);

    public override ulong ReadUInt64() => ReadUInt64(_endianness);

    public short ReadInt16(Endianness endianness) => BitConverter.ToInt16(ReadForEndianness(sizeof(short), endianness));

    public int ReadInt32(Endianness endianness) => BitConverter.ToInt32(ReadForEndianness(sizeof(int), endianness));

    public long ReadInt64(Endianness endianness) => BitConverter.ToInt64(ReadForEndianness(sizeof(long), endianness));

    public ushort ReadUInt16(Endianness endianness) => BitConverter.ToUInt16(ReadForEndianness(sizeof(ushort), endianness));

    public uint ReadUInt32(Endianness endianness) => BitConverter.ToUInt32(ReadForEndianness(sizeof(uint), endianness));

    public ulong ReadUInt64(Endianness endianness) => BitConverter.ToUInt64(ReadForEndianness(sizeof(ulong), endianness));

    private byte[] ReadForEndianness(int bytesToRead, Endianness endianness)
    {
        var bytesRead = ReadBytes(bytesToRead);

        switch (endianness)
        {
            case Endianness.Little:
                if (!BitConverter.IsLittleEndian)
                {
                    Array.Reverse(bytesRead);
                }
                break;

            case Endianness.Big:
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(bytesRead);
                }
                break;
        }

        return bytesRead;
    }
}