Background: I have nothing to do recently, and at the request of a member of the forum, I wanted to create a site for downloading QQ emoticons. Originally, things were very simple, just write a small CRUD, but since Hahahaha is a .Net programmer, of course he has to use .Net to implement it. Today we will use .Net to implement the packaging function of CFC (custom face cab?) expression format.
To achieve this function, we must first understand this format, first Google it. We found this article from Tsinghua University: Detailed explanation of FC file format.
From this article we learned that the CFC file format is roughly as follows:
A block has 15 fields, as follows:
md5 string length, 4-byte shortcut key length, 4-byte emoticon name length, 4-byte emoticon file name length, 4-byte emoticon file length, 4-byte thumbnail File name length, 4-byte miniature file length, 4-byte emoticon file frame number, 4-byte image md5 string form shortcut key emoticon name emoticon file name thumbnail file name emoticon file content thumbnail content Just know the format Done, let’s define a structure (struct) step by step
1 Struct#region Struct
2 public struct FaceBlock
3 {
4 public uint MD5Length; //32
5 public uint uintcutLength; //4
6 public uint FaceNameLength; //4
7 public uint FaceFileNameLength; //36 md5 + extension
8 public uint FileLength;
9 public uint ThumbnailFileNameLength; //41 md5 + fixed.bmp
10 public uint ThumbnailFileLength;
11 public uint FrameLength;
12 public string MD5;
13 public string uintcuts;
14 public string FaceName;
15 public string FaceFileName;
16 public string ThumbnailFileName;
17 public byte[] FaceData;
18 public byte[] ThumbnailData;
19
20 public static FaceBlock FromImage(string file)
twenty one {
22 return FaceHelper.GetFaceBlockFromImage(file);
twenty three }
twenty four
25 byte[] GetBytes(uint value)
26 {
27 byte[] bt = BitConverter.GetBytes(value);
28 List<byte> bytes = new List<byte>();
29 bytes.AddRange(bt);
30 if (bytes.Count < 4)
31 {
32 int l = 4 - bytes.Count;
33 for (int i = 0; i < l; i++)
34 bytes.Add((byte)0);
35}
36 return bytes.ToArray();
37 }
38
39 public byte[]ToBytes()
40 {
41 List<byte> bytes = new List<byte>();
42 Encoding e = Encoding.ASCII;
43 bytes.AddRange(GetBytes(MD5Length));
44 bytes.AddRange(GetBytes(uintcutLength));
45 bytes.AddRange(GetBytes(FaceNameLength));
46 bytes.AddRange(GetBytes(FaceFileNameLength));
47 bytes.AddRange(GetBytes(FileLength));
48 bytes.AddRange(GetBytes(ThumbnailFileNameLength));
49 bytes.AddRange(GetBytes(ThumbnailFileLength));
50 bytes.AddRange(GetBytes(FrameLength));
51
52 bytes.AddRange(e.GetBytes(MD5));
53 bytes.AddRange(e.GetBytes(uintcuts));
54 bytes.AddRange(e.GetBytes(FaceName));
55 bytes.AddRange(e.GetBytes(FaceFileName));
56 bytes.AddRange(e.GetBytes(ThumbnailFileName));
57
58 bytes.AddRange(FaceData);
59 bytes.AddRange(ThumbnailData);
60
61 return bytes.ToArray();
62 }
63}
64 #endregion contains two methods, one is a static method to obtain this structure from the file, and the other is to convert this structure into a byte array.
Let's create another class and name it: FaceHelper
The code is as follows:
public class FaceHelper
{
internal static FaceBlock GetFaceBlockFromImage(string file)
{
FaceBlock fb = new FaceBlock();
//Open file stream
FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);
//Get the image
Image img = Image.FromStream(fs);
//Get a 20*20 thumbnail
Image thumbnail = img.GetThumbnailImage(20, 20, null, IntPtr.Zero);
MemoryStream ms = new MemoryStream();
//Convert the thumbnail image into a byte array
thumbnail.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
byte[] thumbnailData = ms.ToArray();
ms.Close();
ms.Dispose();
thumbnail.Dispose();
//Get a unique MD5 string
string md5 = GetMD5(fs);
//File name, the format is: md5 + extension
string fileName = string.Format("{0}{1}", md5, Path.GetExtension(file));
//Thumbnail file name, format: md5 + fixed.bmp
string thumbnailName = string.Format("{0}fixed.bmp", md5);
//Set a random shortcut key
string uintcuts = "qq.5inet.net_" + RandomNum(6);
fs.Close();
fs.Dispose();
//Get the total number of frames
System.Drawing.Imaging.FrameDimension fd = System.Drawing.Imaging.FrameDimension.Resolution;
int frameCount = img.FrameDimensionsList.Length;
img.Dispose();
fb.MD5 = md5;
fb.MD5Length = (uint)md5.Length;
fb.uintcuts = uintcuts;
fb.uintcutLength = (uint)uintcuts.Length;
fb.FaceName = uintcuts;
fb.FaceNameLength = (uint)uintcuts.Length;
fb.FaceFileName = fileName;
fb.FaceFileNameLength = (uint)fileName.Length;
fb.ThumbnailFileName = thumbnailName;
fb.ThumbnailFileNameLength = (uint)thumbnailName.Length;
fb.FaceData = File.ReadAllBytes(file);
fb.FileLength = (uint)fb.FaceData.Length;
fb.ThumbnailData = thumbnailData;
fb.ThumbnailFileLength = (uint)thumbnailData.Length;
fb.FrameLength = (uint)frameCount;
return fb;
}
Helper#region Helper
//random method
internal static string RandomNum(int n) //
{
string strchar = "0,1,2,3,4,5,6,7,8,9";
string[] VcArray = strchar.Split(',');
string VNum = "";//Since the string is very short, F77pclw,cnetworkG|?,ye,e'b does not need StringBuilder
int temp = -1; //Record the last random value and try to avoid generating several identical random numbers.
//Use a simple algorithm to ensure the difference in generated random numbers
Random rand = new Random();
for (int i = 1; i < n + 1; i++)
{
if (temp != -1)
{
rand = new Random(i * temp * unchecked((int)
DateTime.Now.Ticks));
}
//int t = rand.Next(35);
int t = rand.Next(10);
if (temp != -1 && temp == t)
{
return RandomNum(n);
}
temp = t;
VNum += VcArray[t];
}
return VNum;//Return the generated random number
}
//Get MD5 from file name
internal static string GetMD5(FileStream fs)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
byte[] md5byte = md5.ComputeHash(fs);
string str = string.Empty;
int i, j;
foreach (byte b in md5byte)
{
i = Convert.ToInt32(b);
j = i >> 4;
str += (Convert.ToString(j, 16));
j = ((i << 4) & 0x00ff) >> 4;
str += (Convert.ToString(j, 16));
}
return str.ToUpper();
}
#endregion
//Generate a CFC file collection from a directory
public static void
BuildCFCFileFromDirectory(string directory)
{
List<byte> bytes = new List<byte>();
foreach (string file in Directory.GetFiles(directory))
{
if (!IsImageFile(file))
continue;
bytes.AddRange(FaceBlock.FromImage(file).ToBytes());
}
string fName = Path.Combine(directory, Path.GetDirectoryName(directory) + ".cfc");
FileStream fs = File.Create(fName);
fs.Write(bytes.ToArray(), 0, bytes.Count);
fs.Close();
}
//The method to determine whether it is an image file is relatively simple.
private static bool IsImageFile(string file)
{
List<string> validExt = new List<string>(new string[]{
".bmp",
".jpg",
".jpeg",
".gif",
".png",
});
return validExt.Contains(Path.GetExtension(file).ToLower());
}
}
Okay, with the above method, we can call it.
Calling the method is really simple.
FaceHelper.BuildCFCFileFromDirectory(Server.MapPath("~/img/"));
That's OK. Now go to the root directory of your website and see if there is an img.cfc file? Double-click again. Have all the files in the img directory been imported into QQ emoticons? Enjoy coding!
This article was originally published by: Wuyuan IT Teaching Network
http://www.cnblogs.com/skyover/archive/2006/10/03/520581.html