2010-07-14 17 views
6

hora para otra pregunta fuera de la pared. Estoy escribiendo un cargador MD2 para mi pequeño proyecto de motor 3D. En mi idioma antiguo (C), podría definir una estructura y luego leer() desde un archivo abierto directamente en la estructura. Tengo una estructura para mantener la información del encabezado del archivo MD2, de la siguiente manera:Lectura de bytes en una estructura en C#

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

En mi código lector, me gustaría hacer algo como:

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

Sé que esto no es correcto , ya que rompe la seguridad de tipo algo extrañamente, pero se obtiene la idea de lo que quiero lograr. Podría hacer esto con llamadas separadas a reader.ReadInt32(), pero tengo curiosidad de saber si hay alguna manera de hacer que esto funcione de la manera que quiero utilizando las llamadas normales de la biblioteca.

He analizado un poco el método Marshal.Copy(), pero parece ser para pasar de la memoria administrada a la no administrada, que no es lo que estoy haciendo aquí.

¿Alguna sugerencia?

+0

Parece que alguien puede vencer a la misma: http://gpwiki.org/index.php/C_sharp:MD2_loader_in_CSharp – Jess

Respuesta

3

Una estructura en C y una estructura en C# son dos cosas completamente diferentes. Una estructura en C se usa tanto para tipos de valores como para tipos de referencia, mientras que una estructura en C# solo se usa para tipos de valores.

Un tipo de valor debe representar un solo valor, pero lo que tiene son muchos valores, por lo que debe usar una clase en su lugar. El tamaño máximo recomendado para una estructura en .NET es de 16 bytes, y usted tiene más de cuatro veces la cantidad de datos.

Una clase con propiedades y un constructor que toma una matriz de bytes se vería así:

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

} 
+0

Seguí adelante y usé este método, estoy completamente de acuerdo con su distinción entre el valor y los tipos de referencia . Su solución es un mejor diseño, sin duda, tenía curiosidad por saber si había un método simple para emular mi viejo estilo de hacer las cosas. :-) –

+0

-1 ... ignorando por completo las respuestas correctas que indican que EXPLICITAMENTE es compatible por razones de interoperabilidad. – TomTom

+0

@Chris: Es posible, pero no tan simple. Debería usar atributos que especifiquen exactamente cómo se organizan los miembros en la estructura para asegurarse de que no haya relleno y que estén en el orden correcto. Luego, debe usar el recolector de elementos no utilizados para anclar los datos en la memoria y organizar el código o inseguro para copiar los datos en la estructura. – Guffa

0

Puede ser un poco complejo en C#, pero podría configurar las cosas donde puede memcopy los bytes de una matriz de bytes a la estructura.

1

Lo que puede hacer es leer los bytes en una memoria intermedia del tamaño approprate, utilice fixed (int* = &md2hdr.FourCC) para obtener un puntero al comienzo de su estructura, echaron el puntero a la estructura de byte*, y copiar los bytes manualmente.

5

Leer el flujo de bytes para el conjunto de bytes, el nombre packet, y tratar lo siguiente:

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

Este elude el GC de una manera bastante sustancial, algo que quiero evitar. –

3

Puede utilizar Marshal.PtrToStructure copiar de un puntero directamente en su estructura en una sola toma. Por

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

Esto es un poco más limpio que la solución de Yossarian, pero todavía implica un montón de confusión con el GC que preferiría evitar. Además, una sección "fija" requiere compilar con el parámetro/inseguro. –

+0

@Chris D .: Casi siempre que estás jugando con bytes y tratando de copiar memoria directamente, debes ~ estar ~ inseguro. Dicho esto, puede hacer esto copiando de otra manera, pero esta es probablemente la respuesta más directa a su pregunta, IMO ... –

1

Usted podría utilizar el cálculo de referencias para manejar la copia. No es necesario escribir el código para manejarlo.

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

El formato de color es automático si JS lib SO usa reconoce su código. Parece que está poniendo un texto en negrita aquí, que podría estar tirando ... –

+0

Acutally, miré el código de su comentario y lo está haciendo mal. Para fuente, en SO, todo lo que tiene que hacer es tener saltos de línea que preceden al código y sangría las líneas de su código en cuatro espacios. El resto es automático. Sin embargo, no puedes hacer ciertas líneas en negrita. –

+0

Originalmente dejé fuera el negrita pero lo puse allí más tarde cuando no pude obtener el color correcto para resaltar las partes clave. Trataré de arreglarlo para obtener el color ahora. Gracias. :) –

1

Sé que ya tiene la respuesta y es una buena respuesta.

Pensé que podría obtener algún valor de una publicación de blog que hice sobre algunas de las opciones disponibles en .NET para lograr esto.

Structure from binary data

y un puesto correspondiente a la inversa

Binary data from a structure

+1

Gracias por estos enlaces, son buenas lecturas. –

Cuestiones relacionadas