zoukankan      html  css  js  c++  java
  • 关于转换QQ消息中系统表情,自定义表情和截图的函数

        QQ的系统表情,自定表情和截图都是直接混合在文字信息中直接发送过来的,如果在接收到时不加转换,直接显示,会造成乱码。因此我们要把这些信息转化下再显示。

        关于这些表情,自定义表情和截图的说明如下:来源LumaQQ的代码注解

    * <pre>
    * 普通消息的本体,其在NormalIMHeader之后
    *
    * 普通消息中可能内嵌一些图片信息,除了普通的文本之外,图片的信息格式为:
    * 一. 缺省表情,缺省表情的前导字节是0x14,0x14之后的一个字节表示缺省表情的索引值
    * 二. 自定义表情,自定义表情的前导字节是0x15,0x15之后的格式为:
    *       1. 存在性字节,如果这个表情第一次出现,则为0x33,如果已经出现过,则为0x34,当为0x33时,后面的内容是
    *          i.   扩展名长度,1字节,以'0'为基准,'2'则表示长度为3
    *       ii.  表情图片的文件名,其文件名由md5的字符串形式和扩展名构成,因此这个长度应该是32 + 1 + 3(一般是GIF)
    *       iii. 表情的shortcut长度,以'A'为基准,如果长度是2,则这个字节是'C'
    *       iv.  表情的shortcut
    *    2. 如果为0x34时,则后面的内容为:
    *          i.   1字节索引值,假如这个自定义表情出现在第一个位置,则这个字节为'A'  
    *    3. 如果为0x36时,群内自定义表情
    *          i. 自定义表情协议块的长度的10进制字符串形式,3字节,不足者前部填为空格,比如为了表示这个自定义表情用了
    *          88个字节,那么这个字段就是" 88",呵呵,晕吧,注意这个长度是从0x15开始算起,一直到结束。注意这个长度
    *          是字节长度
    *       ii. 表情标识,1字节,标识这个表情是新的,还是已经出现过的,如果是新的自定义表情,用'e'表示;如果是截图,用'k'表示。如果是已经出现过的,
    *           用一个大写字母表示,第一个新表情代号是A,第二个是B,以此类推
    *       iii. 表情的快捷键字节长度,1字节,用一个大写字母表示,比如A表示长度为0,依次类推
    *       iv. 后面的内容开始一直到agent key之前的内容的长度,2字节,用16进制的字符串表示
    *       v. session id的16进制字符串形式,8字节,不足者前面是空格
    *       vi. 中转服务器IP的16进制字符串形式,注意是little-endian,那么ipv4的话自然就是8个字节了
    *       vii. 中转服务器端口号的16进制字符串形式,8个字节
    *       viii. file agent key,16字节
    *       ix. 图片的文件名,文件名的形式是MD5的字符串形式加上点加上后缀名而成,所以一般是36个字节,但是
    *           我想最好还是根据前面的长度减去其他字段的长度来判断好些
    *       x.  快捷键,长度前面已经说了
    *       xi. 一个字节,'A',可能是用来分界用的
    *    4. 如果为0x37时,群内自定义表情
    *       0x37表示这个表情已经在前面出现过,参见0x36时的格式,0x37缺少0x36的iv, v, vii, viii, ix部分,
    *       其他部分均相同
    * </pre>

    代码如下:

       1: private string AnalyCustomFace(byte[] IMBytes)
       2: {
       3:     List<byte> al = new List<byte>();
       4:     List<string> Faces = new List<string>();
       5:     byte[] tempBytes;
       6:     int bytesSize = 0;
       7:     int shortcutSize = 0;
       8:     bool FaceOrPic = true;//true是自定义表情,false是截屏
       9:     string FaceName = "";
      10:     byte[] FaceNameBytes;
      11:     byte[] facebytes;
      12:     al.AddRange(IMBytes);
      13:  
      14:     for (int i = 0; i < al.Count - 1; i++)
      15:     {
      16:  
      17:         if ((FaceType)al[i] == FaceType.DEFAULT && (byte)al[i + 1] >= 0x40 && (byte)al[i + 1] <= 0xC7)//QQ的表情符号是0x14开头的,下一字节表示表情索引号。0x15开头的是自定义表情。这里处理系统表情,以免乱码。
      18:         {
      19:             string face = string.Format("<img src=\"face/{0}.gif\" />", al[i + 1].ToString());
      20:             facebytes = Encoding.GetEncoding("GBK").GetBytes(face);
      21:             al.RemoveRange(i, 2);
      22:             al.InsertRange(i, facebytes);
      23:             i += facebytes.Length - 1;
      24:             continue;
      25:         }
      26:  
      27:         if ((FaceType)al[i] == FaceType.CUSTOM && (FaceType)al[i + 1] == FaceType.NEW_CUSTOM)
      28:         {
      29:             int extSize = (int)(al[i + 2] - 0x30 + 1);//扩展名长度
      30:             shortcutSize = (int)(al[i + 2 + 32 + 1 + extSize + 1] - 0x41);//快捷键长度
      31:             bytesSize = 3 + 32 + 1 + extSize + 1 + shortcutSize;
      32:             tempBytes = new byte[bytesSize];
      33:             al.CopyTo(i, tempBytes, 0, bytesSize);
      34:             FaceNameBytes = new byte[36];
      35:             Array.Copy(tempBytes, 3, FaceNameBytes, 0, 36);
      36:             FaceName = Encoding.GetEncoding("GBK").GetString(FaceNameBytes);
      37:             string face = string.Format("[CustomFace={0}]", FaceName);
      38:             facebytes = Encoding.GetEncoding("GBK").GetBytes(face);
      39:             al.RemoveRange(i, bytesSize);//删除原数据
      40:             al.InsertRange(i, facebytes);//插入自己转换后的数据
      41:             Faces.Add(FaceName);//加入队列,因为和群自定义表情不会同时在同一条消息里出现,因此不会有问题
      42:             i += facebytes.Length - 1;
      43:         }
      44:         else if ((FaceType)al[i] == FaceType.CUSTOM && (FaceType)al[i + 1] == FaceType.EXISTING_CUSTOM)
      45:         {
      46:             FaceName = Faces[al[i + 2] - 0x41];
      47:             string face = string.Format("[CustomFace={0}]", FaceName);
      48:             facebytes = Encoding.GetEncoding("GBK").GetBytes(face);
      49:             al.RemoveRange(i, 3);//删除原数据
      50:             al.InsertRange(i, facebytes);//插入自己转换后的数据
      51:             i += facebytes.Length - 1;
      52:  
      53:         }
      54:         else if ((FaceType)al[i] == FaceType.CUSTOM && (FaceType)al[i + 1] == FaceType.NEW_SERVER_SIDE_CUSTOM)//这里转换自定义表情和贴图 FaceType.CUSTOM表示是自定义表情或贴图, FaceType.NEW_SERVER_SIDE_CUSTOM表示是在这条消息里第一次出现的自定义表情或贴图
      55:         {
      56:             tempBytes = new byte[3];//这里获取表情数据的长度
      57:             al.CopyTo(i + 2, tempBytes, 0, 3);
      58:             bytesSize = Convert.ToInt32(Encoding.GetEncoding("GBK").GetString(tempBytes));
      59:             tempBytes = new byte[bytesSize];
      60:             al.CopyTo(i, tempBytes, 0, bytesSize);
      61:  
      62:             if (tempBytes[5] == 0x65)//如果是'e'表示是自定义表情
      63:             {
      64:                 FaceOrPic = true;
      65:             }
      66:             else if (tempBytes[5] == 0x6B)//如果是'k'表示是贴图 自定义表情和贴图的区别在于文件名不同,自定义表情的文件名是MD5字串加扩展名,贴图是{GUID}加扩展名
      67:             {
      68:                 FaceOrPic = false;
      69:             }
      70:  
      71:             shortcutSize = (int)(tempBytes[6] - 0x41);//快捷键长度
      72:  
      73:             if (FaceOrPic)
      74:             {
      75:                 FaceNameBytes = new byte[36];//MD5+".gif"一共36个字节
      76:                 Array.Copy(tempBytes, 0x31, FaceNameBytes, 0, 36);
      77:                 FaceName = Encoding.GetEncoding("GBK").GetString(FaceNameBytes);
      78:                 string face = string.Format("[CustomFace={0}]", FaceName);
      79:                 facebytes = Encoding.GetEncoding("GBK").GetBytes(face);
      80:                 al.RemoveRange(i, bytesSize);//删除原数据
      81:                 al.InsertRange(i, facebytes);//插入自己转换后的数据
      82:             }
      83:             else
      84:             {
      85:                 FaceNameBytes = new byte[42];//{GUID}+".gif"一共42个字节
      86:                 Array.Copy(tempBytes, 0x31, FaceNameBytes, 0, 42);
      87:                 FaceName = Encoding.GetEncoding("GBK").GetString(FaceNameBytes).Replace("{", "").Replace("}", "");
      88:                 string face = string.Format("[CustomFace={0}]", FaceName);
      89:                 facebytes = Encoding.GetEncoding("GBK").GetBytes(face);
      90:                 al.RemoveRange(i, bytesSize);
      91:                 al.InsertRange(i, facebytes);
      92:             }
      93:  
      94:             Faces.Add(FaceName);//加入队列
      95:             i += facebytes.Length - 1;
      96:  
      97:         }
      98:         else if ((FaceType)al[i] == FaceType.CUSTOM && (FaceType)al[i + 1] == FaceType.EXISTING_SERVER_SIDE_CUSTOM_SIDE)//如果是本消息中已经出现过的表情或截图
      99:         {
     100:             tempBytes = new byte[3];
     101:             al.CopyTo(i + 2, tempBytes, 0, 3);
     102:             bytesSize = Convert.ToInt32(Encoding.GetEncoding("GBK").GetString(tempBytes));
     103:  
     104:             tempBytes = new byte[bytesSize];
     105:  
     106:             al.CopyTo(i, tempBytes, 0, bytesSize);
     107:  
     108:             FaceName = Faces[tempBytes[5] - 0x41];//从队列中取出
     109:             string face = string.Format("[CustomFace={0}]", FaceName);
     110:             facebytes = Encoding.GetEncoding("GBK").GetBytes(face);
     111:             al.RemoveRange(i, bytesSize);
     112:             al.InsertRange(i, facebytes);
     113:  
     114:             i += facebytes.Length - 1;
     115:         }
     116:     }
     117:  
     118:     tempBytes = new byte[al.Count];
     119:     al.CopyTo(0, tempBytes, 0, tempBytes.Length);//ArrayList转byte[]
     120:     return Encoding.GetEncoding("GBK").GetString(tempBytes);//byte[]转String
     121: }

    系统表情图片包:face.rar

    PS.本函数只是把系统表情,自定义表情,截图转换为可阅读的形式,并不能获取自定义表情和截图的图片数据

    PS2.不好意思阿不,之前这个函数只处理了群消息的自定义表情和截图,普通消息的忘了处理了……现在已经加上了,TM和QQ都验证过了没问题

  • 相关阅读:
    leetcode231
    leetcode326
    leetcode202
    leetcode121
    leetcode405
    leetcode415
    2019-9-2-win10-uwp-应用转后台清理内存
    2019-9-2-win10-uwp-应用转后台清理内存
    ACM学习心得
    ACM学习心得
  • 原文地址:https://www.cnblogs.com/lersh/p/1163565.html
Copyright © 2011-2022 走看看