好久沒關注Xna了,剛剛上了Xna游戲世界 得知AppHub發布了新示例,其中有關於XNB文件結構解析的示例,於是第一時間去瀏覽了下:Compiled (XNB) Content Format 。有興趣的朋友可以下載示例研究一下(是C++代碼),另外里面有份關於XNB文件結構的文檔比較好。
參照文檔,我用C#(4.0)寫了個簡單的紋理XNB文件的生成工具。其實就是個命令行工具,把一堆文件拖上去,會自動將圖像文件編譯到相同目錄下。編譯后的文件放到游戲的Content目錄中,然后Content.Load<Texture2D>就能加載到Texture2D變量中用於繪制。
2 {
3 foreach ( string fileName in args)
4 {
5 if (File.Exists(fileName))
6 ImageToXnb(fileName);
7 }
8 Console.ReadLine();
9 }
10
11 static void ImageToXnb( string fileName)
12 {
13 try
14 {
15 Bitmap image = Bitmap.FromFile(fileName) as Bitmap;
16 if (image != null )
17 {
18 // 獲取圖像的數組。
19 int w = image.Width;
20 int h = image.Height;
21 int s = 4 * w * h;
22 BitmapData bmpData = image.LockBits( new Rectangle( 0 , 0 , w, h), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
23 byte [] bmpBytes = new byte [s];
24 unsafe
25 {
26 byte * data = ( byte * )(bmpData.Scan0.ToPointer());
27 for ( int i = 0 ; i < w * h; i ++ )
28 {
29 bmpBytes[ 4 * i] = data[ 4 * i + 2 ];
30 bmpBytes[ 4 * i + 1 ] = data[ 4 * i + 1 ];
31 bmpBytes[ 4 * i + 2 ] = data[ 4 * i];
32 bmpBytes[ 4 * i + 3 ] = data[ 4 * i + 3 ];
33 }
34 }
35 image.UnlockBits(bmpData);
36 // 開始寫入xnb數據。
37 List < byte > bytes;
38 string xnbFile = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName) + " .xnb " );
39 FileStream stream = new FileStream(xnbFile, FileMode.Create, FileAccess.Write);
40 bytes = new List < byte > ();
41 bytes.AddRange(Encoding.Default.GetBytes( " XNB " )); // 文件頭標識"XNB"
42 bytes.AddRange(Encoding.Default.GetBytes( " w " )); // 平台標識:w - Window
43 bytes.Add(( byte ) 5 ); // 5 - Xna4.0
44 // 寫入當前xnb文件需要的Type Reader。Texture2D對應的是Texture2DReader。
45 bytes.Add(( byte ) 1 ); // 標記位:0x01 - 是否HiDef模式;0x80 - 是否壓縮。
46 bytes.Add(( byte ) 1 ); // Type Reader的數量。
47 // 寫入Type Reader的全稱。
48 string reader = " Microsoft.Xna.Framework.Content.Texture2DReader, Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553 " ;
49 bytes.Add(( byte )reader.Length);
50 bytes.Add(( byte ) 1 );
51 bytes.AddRange(Encoding.Default.GetBytes(reader));
52 bytes.Add(( byte ) 0 );
53 bytes.AddRange(BitConverter.GetBytes( 0 )); // Type Reader 的版本。
54 // 寫入xnb文件的內容。
55 bytes.Add(( byte ) 1 ); // 內容的數量。
56 // 寫入內容,此處為Texture2D。
57 bytes.AddRange(BitConverter.GetBytes( 0 )); // Surface format-此處為Color。
58 bytes.AddRange(BitConverter.GetBytes(( uint )w)); // 寬和高。
59 bytes.AddRange(BitConverter.GetBytes(( uint )h));
60 bytes.AddRange(BitConverter.GetBytes(( uint ) 1 )); // Mip 數量。
61 bytes.AddRange(BitConverter.GetBytes(( uint )(s))); // 數據大小。
62 bytes.AddRange(bmpBytes);
63 // 計算文件大小,插入指定位置。實際上在那個標志位后面緊跟着就是 uint 類型的文件大小。
64 int size = bytes.Count + 4 ;
65 bytes.InsertRange( 6 , BitConverter.GetBytes(size));
66 // 寫入文件。
67 stream.Write(bytes.ToArray(), 0 , bytes.Count);
68 stream.Close();
69 Console.WriteLine( " 文件 {0} 成功編譯成 xnb 文件! " , fileName);
70 }
71 }
72 catch
73 {
74 Console.WriteLine( " 文件 {0} 不是有效的圖像文件,編譯失敗! " , fileName);
75 }
76 }
這里有幾點說明下:
1、代碼用到不安全代碼,要在項目屬性中把“允許不安全代碼”勾上。
2、Texture2DReader類型的全稱是從已生成的XNB文件中復制過來的,我在對象瀏覽器中都沒找到這個類,有人能告訴我為什么嗎?
3、因為文件中第7個Byte開始的Uint類型的數表示文件大小,所以我先把整個文件寫到List<byte>中,然后將數組長度加上4作為文件大小插入到該位置,然后在將整個List一起保存。(事實上開始的時候我把文件大小都設為0,游戲一樣可以正常加載)
4、如果標記位指定文件為壓縮的,那么文件大小后面還需指定解壓后的文件大小,因為對壓縮不甚了解,所以直接跳過了。
5、這里設置紋理的Surface format為Color,這種格式最占空間了,拿一張約900kb的jpg圖像編譯后達3M多。所以要將本程序實用化,可以研究下其他的格式。
結束,睡覺zzzzz~
轉自:http://www.cnblogs.com/huobilie/archive/2011/07/20/2112244.html