2010-07-14 46 views
6

另一個關閉牆問題的時間。我正在爲我的小型3D引擎項目編寫MD2加載程序。在我的舊語言(C)中,我可以定義一個結構,然後從打開的文件中直接讀取()到結構中。我一定要挺住從MD2文件頭信息的結構如下:將字節讀入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; 
} 

在我的讀者的代碼,我想這樣做:

// 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()方法,但它似乎用於託管和非託管內存之間,這不是我在這裏做的。

有什麼建議嗎?

+0

看樣子有人打你吧:http://gpwiki.org/index.php/C_sharp:MD2_loader_in_CSharp – Jess 2010-07-14 18:29:02

回答

3

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

} 
+0

我繼續前進並使用這種方法,我完全同意你在值和引用類型之間的區別。你的解決方案是更好的設計,當然,我只是好奇,如果有一個簡單的方法來模仿我的舊做法。 :-) – 2010-07-14 18:46:35

+0

-1 ...完全忽略了正確的答案,指出由於互操作原因而被明確支持。 – TomTom 2010-07-14 19:02:18

+0

@Chris:這是可能的,但並不那麼簡單。您需要使用屬性指定結構中成員的組織方式,以確保沒有填充並且它們的順序是正確的。然後,您需要使用垃圾回收器將數據固定在內存中,然後使用編組或不安全的代碼將數據複製到結構中。 – Guffa 2010-07-14 19:04:06

0

它可以在C#中得到一點複雜,但你可能設置的東西,你可以memcopy字節從字節數組結構。

1

你可以做的是讀取的字節到approprate大小的緩衝區,使用fixed (int* = &md2hdr.FourCC)獲得的一個指針結構的開始,轉換指針到您的結構byte*,並手動複製字節。

5

所讀取的字節流字節數組,將其命名packet,並嘗試以下操作:

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

這就避免GC以相當實質的方式,我想避免的東西。 – 2010-07-14 18:47:48

3

您可以一次性使用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) 
     ); 
} 
+0

這比Yossarian的解決方案稍微清潔一些,但仍然涉及到我很想避免的GC混淆。此外,「固定」部分需要使用/ unsafe參數進行編譯。 – 2010-07-14 18:49:35

+0

@Chris D .:幾乎任何時候你搞亂字節並試圖直接複製內存,你都應該是/不安全的。這就是說,你可以通過大量複製來做到這一點,否則,這可能是對你的問題最直接的答案,IMO ... – 2010-07-14 18:55:29

1

你可以使用封送處理複製。無需編寫代碼來處理它。

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

如果JS庫SO使用的顏色格式是自動識別您的代碼。看起來你在這裏設置了一些粗體文本,這可能會把它扔掉...... – 2010-07-14 19:32:49

+0

Acalally,我看着你的評論的src,你做錯了。對於源代碼,在SO上,您只需在代碼前面換行並將代碼行縮進四個空格即可。其餘的是自動的。不過,你不能使某些行粗體。 – 2010-07-14 19:35:09

+0

最初我忽略了粗體,但稍後將它放在那裏時,我無法着色突出關鍵部分。我會嘗試修復它以獲取現在的着色。謝謝。 :) – 2010-07-14 20:17:47

1

我知道你已經有了答案,這是一個很好的答案。

我以爲你可能會從一篇博客文章中獲得一些價值,我可以在一些可用的.NET選項中做到這一點。

Structure from binary data

和相應的職位反向

Binary data from a structure

+1

感謝這些鏈接,他們是好讀。 – 2010-07-14 19:33:23

相關問題