好久没关注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(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 }
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