📄 normalim.java
字号:
/*
* LumaQQ - Java QQ Client
*
* Copyright (C) 2004 luma <stubma@163.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.tsinghua.lumaqq.qq.beans;
import java.nio.ByteBuffer;
/**
* <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'表示。如果是已经出现过的,
* 用一个大写字母表示,第一个新表情代号是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>
*
* @author luma
*/
public class NormalIM {
public boolean hasFontAttribute; // 是一个字节,为了方便,我弄成boolean表示
public int totalFragments;
public int fragmentSequence;
public int messageId;
public byte replyType;
// 字体属性
public FontStyle fontStyle;
// 消息内容,在解析的时候只用byte[],正式要显示到界面上时才会转为String,上层程序
// 要负责这个事,这个类只负责把内容读入byte[]
public byte[] messageBytes;
public String message;
/**
* 给定一个输入流,解析NormalIM结构
* @param buf
*/
public void readBean(ByteBuffer buf) {
fontStyle = new FontStyle();
// 是否有字体属性
hasFontAttribute = buf.getInt() != 0;
// 分片数
totalFragments = buf.get() & 0xFF;
// 分片序号
fragmentSequence = buf.get() & 0xFF;
// 消息id
messageId = buf.getChar();
// 消息类型,这里的类型表示是正常回复还是自动回复之类的信息
replyType = buf.get();
// 消息正文,长度=剩余字节数 - 包尾字体属性长度
int remain = buf.remaining();
int fontAttributeLength = hasFontAttribute ? (buf.get(buf.position() + remain - 1) & 0xFF) : 0;
messageBytes = new byte[remain - fontAttributeLength];
buf.get(messageBytes);
// 这后面都是字体属性,这个和SendIMPacket里面的是一样的
if(hasFontAttribute) {
if(buf.hasRemaining())
fontStyle.readBean(buf);
else
hasFontAttribute = false;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -