不通过Content直接创建XNB文件

时间:2021-12-08 18:16:41

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

这里有几点说明下:

1、代码用到不安全代码,要在项目属性中把“允许不安全代码”勾上。

2、Texture2DReader类型的全称是从已生成的XNB文件中复制过来的,我在对象浏览器中都没找到这个类,有人能告诉我为什么吗?

3、因为文件中第7个Byte开始的Uint类型的数表示文件大小,所以我先把整个文件写到List<byte>中,然后将数组长度加上4作为文件大小插入到该位置,然后在将整个List一起保存。(事实上开始的时候我把文件大小都设为0,游戏一样可以正常加载)

4、如果标记位指定文件为压缩的,那么文件大小后面还需指定解压后的文件大小,因为对压缩不甚了解,所以直接跳过了。

5、这里设置纹理的Surface format为Color,这种格式最占空间了,拿一张约900kb的jpg图像编译后达3M多。所以要将本程序实用化,可以研究下其他的格式。

结束,睡觉zzzzz~不通过Content直接创建XNB文件