Reading bytes into a structure in C #

time for another question about the wall. I am writing an MD2 downloader 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 for storing header information from an 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 understand that this is not true, since it spoils the type security a bit, but you understand what I want to achieve. I could do this with separate calls to reader.ReadInt32 (), but I'm curious if it is anyway to make it work the way I want using regular library calls.

I looked a bit at the Marshal.Copy () method, but it seems to go between managed and unmanaged memory, which is actually not what I'm doing here.

Any suggestions?

+6
c # struct file-io
source share
7 answers

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

The value type should represent one value, but you have many 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 accepts a byte array will 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); } } 
+3
source share

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

 GCHandle pinned = GCHandle.Alloc(packet, GCHandleType.Pinned); MD2_Header h = (MD2_Header)Marshal.PtrToStructure(pinned.AddrOfPinnedObject(), typeof(MD2_Header)); pinned.Free(); 
+5
source share

You can use Marshal.PtrToStructure to copy from a pointer directly to your structure in one shot. By

 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) ); } 
+3
source share

What you can do is read bytes into a buffer of the appropriate size, use fixed (int* = &md2hdr.FourCC) to get a pointer to the beginning of your structure, point to your structure in byte* and copy the bytes manually.

+1
source share

You can use marshaling to process copy. No need to write code to process it.

 //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
source share

I know that you already have an answer, and that is a good answer.

I thought you could get some value from the blog post I made on some of the options available in .NET to achieve this.

Binary data structure

And the corresponding post for the reverse

Binary data from structure

+1
source share

It may get a little complicated in C #, but you can configure everything where you can memcopy bytes from an array of bytes into a structure.

0
source share

All Articles