好久没关注Xna了,刚刚上了 得知AppHub发布了新示例,其中有关于XNB文件结构解析的示例,于是第一时间去浏览了下: 。有兴趣的朋友可以下载示例研究一下(是C++代码),另外里面有份关于XNB文件结构的文档比较好。
参照文档,我用C#(4.0)写了个简单的纹理XNB文件的生成工具。其实就是个命令行工具,把一堆文件拖上去,会自动将图像文件编译到相同目录下。编译后的文件放到游戏的Content目录中,然后Content.Load<Texture2D>就能加载到Texture2D变量中用于绘制。
1 static void Main( string [] args) 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~
转自: