DAmn/SampleParser

From Botdom Wiki

Jump to: navigation, search

Here are some examples of dAmn packet parsers written in different programming languages.

Contents

C#

This one was written by plaguethenet, parses packets the exact same way the official client does it.

    public class dAmnArgs
    {
        private string _body;
        private Dictionary<string, string> _args;
 
        public Dictionary<string, string> args
        {
            get { return _args; }
            set { _args = value; }
        }
        public string body
        {
            get { return _body; }
            set { _body = value; }
        }
        public static dAmnArgs getArgsNData(string data)
        {
            dAmnArgs p = new dAmnArgs();
            p.args = new Dictionary<string, string>();
            p.body = null;
            while (true)
            {
                if (data.Length == 0 || data[0] == '\n')
                    break;
                int i = data.IndexOf('\n');
                int j = data.IndexOf('=');
                if (j > i)
                    break;
                p.args[data.Substring(0, j)] = data.Substring(j + 1, i - (j + 1));
                data = data.Substring(i + 1);
            }
            if (data != null && data.Length > 0)
                p.body = data.Substring(1);
            else
                p.body = "";
            return p;
        }
    }
    public class dAmnPacket
    {
        private string _cmd = "";
        private string _param = "";
        //private string _body = "";
        //private Dictionary<string, string> _args = new Dictionary<string,string>();
        dAmnArgs argsAndBody;
        public string cmd
        {
            get { return _cmd; }
            set { _cmd = value; }
        }
 
        public string param
        {
            get { return _param; }
            set { _param = value; }
        }
 
        public string body
        {
            get { return argsAndBody.body; }
            set { argsAndBody.body = value; }
        }
 
        public Dictionary<string, string> args
        {
            get { return argsAndBody.args; }
            set { argsAndBody.args = value; }
        }
 
        public static dAmnPacket parse(string data)
        {
            if (data == null)
                throw new ArgumentNullException("data");
 
            string orig_data = data;
            try
            {
                dAmnPacket p = new dAmnPacket();
                int i = data.IndexOf('\n');
                if (i < 0)
                    throw new Exception("Parser error, No line break.");
                string tmp = data.Substring(0, i);
                p.cmd = tmp.Split(' ')[0];
                int j = tmp.IndexOf(' ');
                if (j > 0)
                    p.param = tmp.Substring(j + 1);
                p.argsAndBody = dAmnArgs.getArgsNData(data.Substring(i + 1));
                return p;
            }
            catch (Exception e)
            {
                throw e;
            }
            //return null; //wtf? how did we get here? D:
        }
    }

It is very easy to use this parser:

dAmnPacket p = dAmnPacket.parse(data);

For the recv packet you can just parse the body again to extract the sub packet.


PHP

This is dbpc and xbot's dAmn packet parser. I added this here because ManjyomeThunder complained that all the other ones on this page were too complicated/slow/long.

You'll need this constant defined:

define('LF', chr(10));

Parser:

// converts a dAmn raw string to a dAmn packet data structure
function str2dpkt($raw) {
	list($head, $body) = args($raw, 2, LF.LF);
	list($hair, $face) = args($head, 2, LF);
	$pkt = args($hair, 2);
	$pkt[2] = arghasht($face);
	$pkt[3] = $body;
	return $pkt;
}

It uses two customs function, args() and arghasht(), included with the clients, but if you have no need of their extra functionality, these simpler ones will work fine.

function args($data, $r=0, $sep=' ') {
	$args = explode($sep,$data,$r);
	if (count($args)<$r) { $args = array_pad($args,$r,''); }
	return $args;
}
function arghasht($data, $sep='=', $linesep=LF) {
	if (empty($data)) { return array(); } // most of the time this will be the case
	$lines = explode($linesep, $data);
	foreach ($lines as $line) {
		if (empty($line)) { continue; }
		list($k, $v) = args($line, 2, $sep);
		$hasht[$k] = $v;
	}
	return $hasht;
}

And here's the inverse function:

// converts a dAmn packet data structure to a dAmn raw string
function dpkt2str($pkt) {
	$str = empty($pkt[1])? $pkt[0].LF: $pkt[0].' '.$pkt[1].LF;
	if (isset($pkt[2])) {
		foreach ($pkt[2] as $k=>$v) {
			$str .= $k.'='.$v.LF;
		}
	}
	if (!empty($pkt[3])) { $str .= LF.$pkt[3]; }
	return $str;
}

This one was written by photofroggy, official parser was not referred to when making it. Uses a much more simple concept. ManjyomeThunder helped design this version.

function parse_dAmn_packet($data, $sep = '=') {
 
	$packet = array(
		'cmd' => Null,
		'param' => Null,
		'args' => array(),
		'body' => Null,
		'raw' => $data
	);
	if(stristr($data, "\n\n")) {
		$packet['body'] = trim(stristr($data, "\n\n"));
		$data = substr($data, 0, strpos($data, "\n\n"));
	}
	$data = explode("\n", $data);
	foreach($data as $id => $str) {
		if(strpos($str, $sep) != 0) {
			$packet['args'][substr($str, 0, strpos($str, $sep))] = substr($str, strpos($str, $sep)+1);
		} elseif(strlen($str) >=1) {
			if($id == 0) {
				if(!stristr($str, ' ')) { $packet['cmd'] = $str; } else {
					$packet['cmd'] = substr($str, 0, strpos($str, ' '));
					$packet['param'] = trim(stristr($str, ' '));
				}
			} else { $packet['args'][] = $str; }
		}
	}
	return $packet;
 
}

Using this parser is also easy:

$packet = parse_dAmn_packet($data);
# Example of usage:
 
$packet = parse_dAmn_packet("command parameter\narg=value\narg2=value2\n\nBody");
 
# This would result in the following:
#      $packet['cmd']   = 'command';
#      $packet['param'] = 'parameter';
#      $packet['args'] = array(
#            'arg' => 'value',
#            'arg2'=> 'value2'
#       );
#      $packet['body'] = 'Body';
#      $packet['raw']  = 'command parameter\narg=value\narg2=value2\n\nBody';

For the recv packet you can just parse the body again to extract the sub packet.

Python

Ported from plaguethenet's code by SubjectX52873M

import UserDict 
 
class Packet(UserDict.DictMixin):
	
	def __init__(self,data=None,subPacket=False):
		"""if a string is passed the string is parsed as a 
		dAmnserver packet otherwise creates an empty packet
		object"""
		self.cmd=""
		self.param=False
		self.body=False
		self.__args={}
		if not data==None:
			data=str(data)
			try:
				i = data.find("\n")
				if i==-1:
					raise DamnParserException,"Parser error, No line break."
				tmp = data[0:i].split(' ',1)
				self.cmd = tmp[0]
				if len(tmp)==2:
					self.param=tmp[1]
				data=data[(i+1):]
				while(true):
					if len(data) == 0 or data[0] == "\n": 
						break;
					i = data.find("\n")
					j = data.find('=')
					if i==-1 and not j==-1:
						r[data[0:j]]=data[(j+1):]
					elif j > i:
						break;
					else:
						self.__args[data[0:j]]=data[(j+1):i]
						data=data[i:]
					#EndWhile
				if data[0:1]=="\n\n" and not subPacket and not self.cmd=="property":
					#Property packets can get nasty. 
					self.body=Packet(data,True)
				elif not data==None and len(data) > 0:
					self.body = data
			except e:
				raise DamnParserException,"Unknown error: %s" % e
	
	def __str__(self):
		with self.__lock:
			"""Returns a dAmn formated packet"""
			str=""
			str+=self.cmd
			if self.param:
				str+= " %s" % self.param
			str+="\n"
			if not self.__args == 0:
				for key,value in self.__args.iteritems():
					str+= "%(k)s=%(v)s\n" % {'k':key,'v':value}
			if self.body:
				str+="\n"
				str+=str(self.body)
			return str
 
	def getArgs(self,data):
		"""Returns a two truple with (dict,string/bool:False)"""
		#Useful for property packets, eventually add parsing for those types here.
		data= str(data) #enfore type
		r={}
		body=False
		while(true):
			if len(data) == 0 or data[0] == "\n": 
				break;
			i = data.find("\n")
			j = data.find('=')
			if i==-1 and not j==-1:
				r[data[0:j]]=data[(j+1):]
			elif j > i:
				break;
			else:
				r[data[0:j]]=data[(j+1):i]
				data=data[i:]
			#EndWhile
		if not data==None and len(data) > 0:
			body = data
		return (r,body)
 
	#Implemented for DictMixin
	def __getitem__(self,key): #Thread safe
		return self.__args[key]
	def __setitem__(self,key,value):
		self.__args[key]=value
	def __delitem__(self,key):
		del self.__args[key]
	def __contains__(self,key):
		self.__args.__contains__(key)
	def __iter__():
		return self.__args.__iter__()
	def __len__():
		return len(self.__args)
	def iteritems():
		return self.__args.iteritems()
	def keys():
		return self.__args.keys()

Implemented as a dictionary.

  • Parse a Message packet
recv chat:chatroom

msg main
from=username

message
p = Packet("recv chat:chatroom\n\nmsg main\nfrom=username\n\nmessage\0")
    • Access a argument
>>>print p["from"]
username
    • Access the command name
>>>print p.cmd
recv
    • Access the command parameter
>>>print p.param
chat:chatroom
    • Access the message
>>>print p.body.body
message
    • Access the type of message (action vs. msg)
>>>print p.body.cmd
msg


  • Create a send packet
p = Packet()
p.cmd="send"
p.param="chat:chatroom"
p.body=Packet()
p.body.cmd="msg"
p.body.param="main"
p.body.body="message"
  • Convert for sending to the server
str(p)

This is a much simpler parser written in Python by photofroggy. It has less functionality but parses packets easily.

class Packet:
	
	'''
		The Packet class is a simple
		parser which breaks the received
		dAmn packets into a more usable
		form. This could be easily achieved
		as a single function and would be
		virtually unchanged.
	'''
	
	cmd = None
	param = None
	args = {}
	body = None
	raw = None
 
	def __init__(self, data = None, sep = '='):
	
		self.raw = data
		
		if not data == None:
			try:
				if not data.find("\n\n") == -1:
					self.body = data[data.find("\n\n")+2:]
					data = data[0:data.find("\n\n")]
				for st in data.split("\n"):
					if not st.find(sep) == -1: self.args[st[0:st.find(sep)]] = st[st.find(sep)+len(sep):]
					elif self.cmd == None:
						if st.find(' ') == -1: self.cmd = st
						else:
							self.cmd = st[0:st.find(' ')]
							self.param = st[st.find(' ')+1:]
			except: pass
		else: pass

The parser could be reduced to a single function and the changes would be minimal. Below is an example of how to use this parser.

# Create a packet object with some random data.
pack = Packet("command parameter\narg=foo\narg2=bar\n\nbody")
'''
	This leaves you with the following:
		pack.cmd = 'command'
		pack.param = 'parameter'
		pack.args = {
			'arg' : 'foo',
			'arg2': 'bar'
		}
		pack.body = 'body'
		pack.raw = 'command param\narg=foo\narg2=bar\n\nbody'
'''

Ruby

Written by ManjyomeThunder. Released under a GNU General Public License (v3 or above).

class Packet
	attr_reader :cmd, :param, :sub, :dAmn, :body, :args
	def initialize(data, dAmn = nil, separator = '=')
		@args, @body, @dAmn = {}, '', dAmn
		if data.include?("\n\n")
			@body = data[data.index("\n\n") + 2 .. -1]
			data = data[0 ... data.index("\n\n")]
			@sub = Packet.new(@body) unless @body.empty?
		end
		data.each_line { |line|
			if line.include?(separator)
				@args[line[0 ... line.index(separator)].to_sym] = line[line.index(separator) + separator.length .. -1]
			else
				@cmd = (line.include?(' ') ? line[0 ... line.index(' ')] : line).to_sym
				@param = line[line.index(' ') + 1 .. -1] if line.include?(' ')
			end
		}
	end
	def [](key)
		@args[key.to_sym]
	end
	def to_s
		packet = @cmd.to_s
		packet << ' ' + @param unless @param.nil?
		packet << "\n" unless packet.empty?
		@args.each { |key, val| packet << "#{key}=#{val}\n" }
		packet << "\n#{body}" unless @body.nil?
	end
end
 
# Create a packet object.
packet = Packet.new("recv chat:Botdom\n\nmsg main\nf=ManjyomeThunder\n\nTest message packet", some_dAmn_object)
 
# Access the command and param
puts packet.cmd # prints "recv"
puts packet.param # prints "chat:Botdom"
 
# Access packet arguments
puts packet[:f] # prints "ManjyomeThunder"
 
# Access sub-packet and body
puts packet.sub.cmd # prints "msg"
puts packet.body # prints "msg main\nf=ManjyomeThunder\n\nTest message packet"
 
# Access dAmn
packet.dAmn.write_packet(packet) # writes the packet back to the server.

Java

Not to be confused with JavaScript, which is an unrelated programming language.

This one was written by photofroggy, and ported to Java by Solitude12 (I realize this isn't that amazing, especially since it is not written cleanly. It is a more messy version. I will have to clean it up soon - Solitude12)

import java.util.HashMap;
import java.util.ArrayList;
 
public class Packet {
	public String cmd, param, body = null;
	public HashMap<String, String> args = new HashMap<String, String>();
	public String raw;
 
	public Packet(String packet){
		this(packet, "=");
	}
	
	public Packet(String packet, String splitter){
		this.raw = packet;
		ArrayList<String> splitted = new ArrayList<String>();
		ArrayList<String> splitteddos = new ArrayList<String>();
		for (String splits : packet.split("\n\n", -1)){
			splitted.add(splits);
			splitteddos.add(splits);
		}
		splitted.remove(0);
		for(int i = 0; i<splitted.size(); i++){
			String bit = splitted.get(i);
			if(bit.length() > 0) {
				if (this.body==null)
					this.body = bit;
				else
					this.body = this.body + "\n\n" + bit;
			}
		}
		ArrayList<String> head = new ArrayList<String>();
		for (String splits : splitteddos.get(0).split("\n", -1))
			head.add(splits);
		ArrayList<String> splittedtres = new ArrayList<String>();
		for (String splits : head.get(0).split(" ", -1))
			splittedtres.add(splits);
		if(splittedtres.size()>0) {
			this.cmd   = splittedtres.get(0);
			this.param = splittedtres.get(1);
			head.remove(0);
		}
		for(int i = 0; i<head.size(); i++){
			String string = head.get(i);
			int pos = string.indexOf(splitter);
			if(pos < string.length()-1 && pos > 0) {
				String[] args2 = string.split(splitter, -1);
				this.args.put(args2[0], string.substring(pos+1, string.length()));
			} else if(string.length()>=1)
				if(i == 0 && this.cmd==null)
					this.cmd = string;
		}
	}
}

Using this parser is also easy:

// Example of usage:
Packet test = new Packet("command parameter\narg=value\narg2=value2\n\nBody");
/*
test.cmd -> command
test.param -> parameter
test.args.get("arg") -> value
test.args.get("arg2") -> value2
test.body -> Body
*/

For the recv packet you can just parse the body again to extract the sub packet.

JavaScript

Not to be confused with Java, which is an unrelated programming language.

Experiemental: This is a packet parser developed by miksago, heavily based on Contra's php one:

    function parse_pkt(data){
        var packet = {
            cmd: null,
            param: null,
            args: {},
            body: null,
            raw: data
        };
        
        data = data.split("\n\n");
 
        packet['body'] = data[1] || null;
        
        data = data[0].split("\n");
        
        for(var i=0;i<data.length;i++){
            if(i==0){
                packet['cmd'] = data[i].split(' ')[0];
                packet['param'] = data[i].split(' ')[1] || null;
            } else if(data[i].indexOf("=") != -1){
                packet['args'][data[i].substr(0, data[i].indexOf("="))] = data[i].substr(data[i].indexOf("=")+1);
            }
        }
        return packet;
    }

If in Doubt, look and use the webchats packet parser:

      function parseArgs(data){
        var args = new Object();
        var body;
    
        while(1){
          if( !data.length || data.charAt(0) == '\n')
            break;
          
          var res = dAmn_packetArgs_re.exec( data );
          
          args[res[1]] = res[2].replace(/\s*$/,'');
          
          data = data.substr(res[0].length);
        }
        
        // strip initial \n
        if( data && data.length )
          body = data.substr(1);
        else
          body = '';
        
        return {'args':args,'body':body};
      }
      function parse(data){
        if( !data )
          alert("packet data is null");
        
        var orig_data = data;
        try{
          var cmd;
          var param;
          
          var i = data.search('\n');
          
          if( 0 == i )
            return null;
            
          var tmp = data.substr(0,i).replace(/\s*$/,'');
          
          cmd	= tmp.match(/\S+/)[0];
          var j	= tmp.search(' ');
          if( j )
            param	= tmp.substr(j+1).match(/\S.*/)[0].replace(/\s*$/,'');
            
          var ad = parseArgs(data.substr(i+1));
          
          return {'cmd':cmd,'param':param,'args':ad.args,'body':ad.body};
          
        } catch(e){
          alert('parse packet exception: \n' + orig_data + '\n\n----\n' + e );
          return null;
        }
      }
Personal tools