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 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 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 self.cmd!="property":
					#Property packets can get nasty. 
					self.body=Packet(data,True)
				elif 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 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 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 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):
		return key in self.__args
	def __iter__():
		return iter(self.__args)
	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:
    """ Use this class to parse dAmn packets.
        Data is stored in the attributes cmd, param,
        args, body and raw.
    """
 
    def __init__(self, data=None, sep='='):
        self.cmd, self.param, self.args, self.body, self.raw = None, None, {}, None, data
        if not bool(data): return
        if '\n\n' in data:
            self.body = data[data.find('\n\n')+2:]
            data = data[:data.find('\n\n')]
        breaks = data.split('\n')
        if not bool(breaks): return
        if len(breaks) >= 1 and sep not in breaks[0]:
            head = breaks.pop(0).split(' ')
            self.cmd = head[0] or None
            self.param = None if len(head) < 2 else head[1]
        for line in breaks:
            if sep not in line: continue
            self.args[line[:line.find(sep)]] = line[line.find(sep)+len(sep):]
        # And that's the end of that chapter.

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;
    }

Packet parser developed by philo23 for the dAmnAIR library, originally based of miksago's Javascript but then completely rewritten to handle subpackets and pass strict Javascript tests. Note that it's probably not the most efficient parser. Usage is simple PacketParser(raw packet string); No need to pass a depth as its used internally for subpacket parsing.

function PacketParser (rpkt, depth)
{
	depth = depth || 0;
	var i;
	var ppkt = {
		cmd: null,
		param: null,
		args: {},
		body: null,
		sub: [],
		depth: depth,
		raw: rpkt
	};
	
	var parts = rpkt.split("\n\n");
	var head = parts.shift().split('\n');
	var cmd  = head.shift().split(' ');
	ppkt.cmd = cmd.shift();
	ppkt.param = cmd.join(' ');
	
	for (i in head)
	{
	    if (head.hasOwnProperty(i))
	    {
			var val = head[i].split('=');
			ppkt.args[ val.shift() ] = val.join('=');
	    }
	}
	ppkt.body = parts.join('\n\n') || null;
	if (parts.length > 1)
	{
		i = parts.length - 1;
		if (i === 1) { ppkt.sub.push( arguments.callee(ppkt.body, depth + 1) ); }
		else
		{
			for (i; i >= 0; i--) { ppkt.sub.push( arguments.callee(parts[i], depth + 1) ); }
		}
	}
	
	return ppkt;
}

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