C# - двоичный считыватель в Big Endian?

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

проблема в том, что все данные должны быть с обратным порядком байтов, и все бинарные читателя читать с обратным порядком байтов. Итак, каков наилучший способ исправить это?

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

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

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

4 ответов


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

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

IMHO немного лучший ответ, поскольку он не требует нового класса, делает вызовы big-endian очевидными и позволяет смешивать вызовы 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;
  }
}

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

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


на мой взгляд, вы хотите быть осторожными в этом. Почему можно было бы хотеть преобразовать сохранены в LittleEndian если байты читаются в сохранены и ОС расчет против них действует в LittleEndian.

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

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

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

    /// <summary>
    /// Get's 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