Чтение байтов в структуру в C # - PullRequest
6 голосов
/ 14 июля 2010

время для еще одного закулисного вопроса. Я пишу загрузчик MD2 для моего небольшого 3D-движка. На моем старом языке (C) я мог определить структуру и затем прочитать () из открытого файла непосредственно в структуру. У меня есть структура для хранения информации заголовка из файла MD2, следующим образом:

[StructLayout(LayoutKind.Sequential)]
public struct MD2_Header
{
    public int FourCC;
    public int Version;
    public int TextureWidth;
    public int TextureHeight;
    public int FrameSizeInBytes;
    public int NbrTextures;
    public int NbrVertices;
    public int NbrTextureCoords;
    public int NbrTriangles;
    public int NbrOpenGLCmds;
    public int NbrFrames;
    public int TextureOffset;
    public int TexCoordOffset;
    public int TriangleOffset;
    public int FrameOffset;
    public int OpenGLCmdOffset;
    public int EndOffset;
}

В своем коде читателя я хотел бы сделать что-то вроде:

// Suck the MD2 header into a structure, it is 68 bytes long.
Classic.Util.MD2_Header md2hdr = new Classic.Util.MD2_Header();
md2hdr = reader.ReadBytes(sizeof(Classic.Util.MD2_Header));

Я понимаю, что это не правильно, поскольку это несколько странно нарушает безопасность типов, но вы понимаете, чего я хочу достичь. Я мог бы сделать это с отдельными вызовами reader.ReadInt32 (), но мне любопытно, есть ли способ заставить это работать так, как я хочу, используя обычные библиотечные вызовы.

Я немного посмотрел на метод Marshal.Copy (), но, похоже, он идет между управляемой и неуправляемой памятью, что на самом деле не то, что я здесь делаю.

Есть предложения?

Ответы [ 7 ]

5 голосов
/ 14 июля 2010

Считайте поток байтов в байтовый массив, назовите его packet и попробуйте следующее:

GCHandle pinned = GCHandle.Alloc(packet, GCHandleType.Pinned);
MD2_Header h = (MD2_Header)Marshal.PtrToStructure(pinned.AddrOfPinnedObject(), typeof(MD2_Header));
pinned.Free();
3 голосов
/ 14 июля 2010

Структура в C и структура в C # - это две совершенно разные вещи.Структура в C используется как для типов значений, так и для ссылочных типов, в то время как структура в C # используется только для типов значений.

Тип значения должен представлять одно значение, но у вас есть множество значений,поэтому вы должны использовать класс вместо этого.Рекомендуемый максимальный размер для структуры в .NET составляет 16 байтов, и у вас есть в четыре раза больше данных.

Класс со свойствами и конструктор, который принимает байтовый массив, будет выглядеть так:

public class MD2_Header {

  public int FourCC { get; set; }
  public int Version { get; set; };
  public int TextureWidth { get; set; };
  public int TextureHeight { get; set; };
  public int FrameSizeInBytes { get; set; };
  public int NbrTextures { get; set; };
  public int NbrVertices { get; set; };
  public int NbrTextureCoords { get; set; };
  public int NbrTriangles { get; set; };
  public int NbrOpenGLCmds { get; set; };
  public int NbrFrames { get; set; };
  public int TextureOffset { get; set; };
  public int TexCoordOffset { get; set; };
  public int TriangleOffset { get; set; };
  public int FrameOffset { get; set; };
  public int OpenGLCmdOffset { get; set; };
  public int EndOffset { get; set; };

  public MD2_Header(byte[] values) {
    FourCC = BitConverter.ToInt32(values, 0);
    Version = BitConverter.ToInt32(values, 4);
    TextureWidth = BitConverter.ToInt32(values, 8);
    TextureHeight = BitConverter.ToInt32(values, 12);
    FrameSizeInBytes = BitConverter.ToInt32(values, 16);
    NbrTextures = BitConverter.ToInt32(values, 20);
    NbrVertices = BitConverter.ToInt32(values, 24);
    NbrTextureCoords = BitConverter.ToInt32(values, 28);
    NbrTriangels = BitConverter.ToInt32(values, 32);
    NbrOpenGLCmds = BitConverter.ToInt32(values, 36);
    NbrFrames = BitConverter.ToInt32(values, 40);
    TextureOffset = BitConverter.ToInt32(values, 44);
    TexCoordOffset = BitConverter.ToInt32(values, 48);
    TriangleOffset = BitConverter.ToInt32(values, 52);
    FrameOffset = BitConverter.ToInt32(values, 56);
    OpenGLCmdOffset = BitConverter.ToInt32(values, 60);
    EndOffset = BitConverter.ToInt32(values, 64);
  }

}
3 голосов
/ 14 июля 2010

Вы можете использовать Marshal.PtrToStructure , чтобы скопировать указатель прямо в вашу структуру за один выстрел.

byte[] data = reader.ReadBytes(...);
fixed (byte* bytes = data)
{
     Classic.Util.MD2_Header md2hdr = 
          (Classic.Util.MD2_Header)Marshal.PtrToStructure(
               Marshal.UnsafeAddrOfPinnedArrayElement(data, 0),
               typeof(Classic.Util.MD2_Header)
          );
}
1 голос
/ 14 июля 2010

Я знаю, что у вас уже есть ответ, и это хороший ответ.

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

Структура из двоичных данных

И соответствующий пост для реверса

Двоичные данные из структуры

1 голос
/ 14 июля 2010

Вы можете использовать маршалинг для копирования. Не нужно писать код для его обработки.

//create object
Classic.Util.MD2_Header md2hdr = new Classic.Util.MD2_Header();
Classic.Util.MD2_Header another = new Classic.Util.MD2_Header();
byte[] mem = new byte[Marshal.SizeOf(typeof(MD2_Header))];

//allocate unmanaged memory
IntPtr hmem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Classic.Util.MD2_Header)));

//copy structure to unmanaged memory
Marshal.StructureToPtr(md2hdr, hmem, false);

//copy to managed memory
Marshal.Copy(hmem, mem, 0, mem.Length);

//copy unmanaged memory to structure
another = (Classic.Util.MD2_Header)Marshal.PtrToStructure(hmem, typeof(Classic.Util.MD2_Header));

//free unmanaged memory
Marshal.FreeHGlobal(hmem);
1 голос
/ 14 июля 2010

Что вы можете сделать, это прочитать байты в буфер подходящего размера, использовать fixed (int* = &md2hdr.FourCC), чтобы получить указатель на начало вашей структуры, привести указатель к вашей структуре в byte* и скопировать байты вручную,

0 голосов
/ 14 июля 2010

Это может быть немного сложно в C #, но вы могли бы настроить, где вы можете скопировать байты из массива байтов в структуру.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...