Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading bytes into a structure in C#

Tags:

c#

file-io

struct

time for another off the wall question. I am writing an MD2 loader for my small 3D engine project. In my old language (C) I could define a structure and then read() from an open file directly into the structure. I have a structure to hold the header information from the MD2 file, as follows:

[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;
}

In my reader code, I would like to do something like:

// 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));

I realize this is not correct, as it breaks type safety somewhat oddly, but you get the idea of what I want to accomplish. I could do this with separate calls to reader.ReadInt32(), but I am curious if there is anyway to get this to work the way I am wanting using normal library calls.

I have looked a little into the Marshal.Copy() method, but it seems to be for going between managed and unmanaged memory, which is not really what I am doing here.

Any suggestions?

like image 747
Chris D. Avatar asked Jul 14 '10 18:07

Chris D.


2 Answers

Read the byte stream to byte array, name it packet, and try following:

GCHandle pinned = GCHandle.Alloc(packet, GCHandleType.Pinned);
MD2_Header h = (MD2_Header)Marshal.PtrToStructure(pinned.AddrOfPinnedObject(), typeof(MD2_Header));
pinned.Free();
like image 83
nothrow Avatar answered Oct 31 '22 04:10

nothrow


A structure in C and a structure in C# are two completely different things. A structure in C is used both for value types and reference types, while a structure in C# is only used for value types.

A value type should represent a single value, but what you have is plenty of values, so you should use a class instead. The recommended maximum size for a structure in .NET is 16 bytes, and you have more than four times as much data.

A class with properties and a constructor that takes a byte array would look like this:

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);
  }

}
like image 43
Guffa Avatar answered Oct 31 '22 05:10

Guffa