package com.aionemu.packetsamurai.logwriters;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import com.aionemu.packetsamurai.PacketSamurai;
import com.aionemu.packetsamurai.protocol.protocoltree.PacketFamilly.packetDirection;
import com.aionemu.packetsamurai.session.DataPacket;
import com.aionemu.packetsamurai.session.Session;
/**
*
* @author Ulysses R. Ribeiro
* @author Gilles Duboscq
*
*/
public class PSLogWriter extends AbstractLogWriter
{
private static final byte LOG_FORMAT_VERSION = 0x07;
private static final long MAX_LOG_SIZE = Long.decode(PacketSamurai.getConfigProperty("MaxLogSize", ""+4*1024*1024));
private int _part = 0;
private boolean _wroteOnTheFly; // used to ensure only one method is used onthe fly or bulk writing (avoid the bulk method beeing called after having written everything on the fly)
private int _packetsWrote;
public PSLogWriter(Session session) throws IOException
{
super(session);
}
public PSLogWriter(Session session, boolean wroteOnTheFly) throws IOException
{
super(session);
_wroteOnTheFly= wroteOnTheFly;
}
public PSLogWriter(String filename, Session session) throws IOException
{
super(filename, session);
}
public PSLogWriter(String filename, Session session, boolean wroteOnTheFly) throws IOException
{
super(filename, session);
_wroteOnTheFly= wroteOnTheFly;
}
public PSLogWriter(String dir, String filename, Session session) throws IOException
{
super(dir, filename, session);
}
public PSLogWriter(String dir, String filename, Session session, boolean wroteOnTheFly) throws IOException
{
super(dir, filename, session);
_wroteOnTheFly= wroteOnTheFly;
}
@Override
protected void writeHeader() throws IOException
{
int bufSize = 1+4+1+2+2+4+4+2*_session.getProtocol().getName().length()+2+2+2+8+8+1;
if(this.getSession().getComments() != null)
bufSize += this.getSession().getComments().length()*2;
if(this.getSession().getServerType() != null)
bufSize += this.getSession().getServerType().length()*2;
System.out.println("Computed Header size : "+bufSize);
ByteBuffer buf = ByteBuffer.allocate(bufSize);
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.put(LOG_FORMAT_VERSION);
// write dummy packet count
buf.putInt(this.getSession().getPackets().size());
// write dummy has Continuation (split log)
buf.put((byte) 0x00);
// write dummy part
buf.putShort((short) 0x00);
buf.putShort((short) this.getSession().getProtocol().getPort());
if(this.getSession().getClientIp() == null)
buf.put(new byte[4]);
else
buf.put(this.getSession().getClientIp().getAddress());
if(this.getSession().getServerIp() == null)
buf.put(new byte[4]);
else
buf.put(this.getSession().getServerIp().getAddress());
writeS(this.getSession().getProtocol().getName(),buf);
writeS(this.getSession().getComments(), buf);
writeS(this.getSession().getServerType(), buf);
buf.putLong(this.getSession().getAnalyserBitSet());//bitset
buf.putLong(this.getSession().getSessionId());
buf.put((byte) (this.getSession().isDecrypted() ? 0x00 : 0x01));
this.getRandomAccessFile().write(buf.array());
}
@Override
protected void writePackets() throws IOException
{
if(_wroteOnTheFly)
return;
for (DataPacket packet : this.getSession().getPackets())
{
this.writePacket(packet, false);
}
}
@Override
public void writePacket(DataPacket packet) throws IOException
{
if(!_wroteOnTheFly)
throw new IllegalStateException("Write on the fly methods can not be called on a writer that was not iniated with this property");
this.writePacket(packet, true);
}
public void writePacket(DataPacket packet, boolean isWriteOnTheFly) throws IOException
{
if(!_wroteOnTheFly && isWriteOnTheFly)
throw new IllegalStateException("Write on the fly methods can not be called on a writer that was not iniated with this property");
int size = packet.getRawSize();
// uglyish
ByteBuffer buffer = ByteBuffer.allocate(3+8+size);
buffer.order(ByteOrder.LITTLE_ENDIAN);
// if packet came from server
if (packet.getDirection() == packetDirection.serverPacket)
buffer.put((byte) 0x01);
else
buffer.put((byte) 0x00);
buffer.putShort((short) (size + 2));
buffer.putLong(packet.getTimeStamp());
buffer.put(packet.getIdData());
buffer.put(packet.getData(),0,packet.getSize()); //must use the size of the data in the strcuture coz the data array might contain some useless bytes that are here because of the compact/flip that removes the id bytes
this.getRandomAccessFile().write(buffer.array());
if (isWriteOnTheFly)
{
long pos = this.getRandomAccessFile().getFilePointer();
this.getRandomAccessFile().seek(1);
// right... this is lame.. but that daln RandomAccess doesnt write with little endianess alone...
ByteBuffer buf = ByteBuffer.allocate(4);
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.putInt(this.getSession().getPackets().size() - _packetsWrote);
this.getRandomAccessFile().write(buf.array());
this.getRandomAccessFile().seek(pos);
}
if (this.getRandomAccessFile().getFilePointer() >= PSLogWriter.MAX_LOG_SIZE)
{
// mark log as continued
long pos = this.getRandomAccessFile().getFilePointer();
this.getRandomAccessFile().seek(5);
this.getRandomAccessFile().write(0x01);
// write short
this.getRandomAccessFile().write(_part & 0xFF);
this.getRandomAccessFile().write((_part >> 8) & 0xFF);
this.getRandomAccessFile().seek(pos);
// close current file
this.close();
_packetsWrote = this.getSession().getPackets().size();
_part++;
String file = this.getDirectory()+this.getFileName()+"-"+_part+"."+this.getFileExtension();
this.setRandomAccessFile(new RandomAccessFile(file,"rw"));
this.writeHeader();
}
}
@Override
protected String getFileExtension()
{
return "psl";
}
private void writeS(CharSequence text, ByteBuffer buf)
{
if (text == null)
{
buf.putChar('\000');
}
else
{
final int len = text.length();
for (int i=0; i < len; i++)
buf.putChar(text.charAt(i));
buf.putChar('\000');
}
}
}