好久没关注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~
转自:http://www.cnblogs.com/huobilie/archive/2011/07/20/2112244.html