不通过Content直接创建XNB文件

好久没关注Xna了,刚刚上了Xna游戏世界得知AppHub发布了新示例,其中有关于XNB文件结构解析的示例,于是第一时间去浏览了下:Compiled (XNB) Content Format。有兴趣的朋友可以下载示例研究一下(是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(00, 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~

posted @ 2011-07-20 23:44  火必烈  阅读(2468)  评论(2编辑  收藏  举报