eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q'
  if 0;

#!perl -w
#line 6
#
# opp_msgc: supports message subclassing: compiles NED files with message
# declarations into C++ code.
#

#
# If no args, print usage
#
$Usage = 'opp_msgc - part of OMNeT++. (c) 2002-2003 Andras Varga
Message subclassing compiler (experimental prototype, written in Perl)

Usage: opp_msgc [-s <cc-file-suffix>] [-t <h-file-suffix>]
                [-I <dir> -I ...] [-h] <nedfile>
  -I <dir>    add directory to include path
  -s <suffix> output C++ file suffix (defaults to: _m.cc)
  -t <suffix> output C++ header file suffix (defaults to: _m.h)
  -h          output in current directory
';

if ($#ARGV == -1)
{
    print $Usage;
    exit(1);
}

#
#  Parse the command line for options and files.
#
$filename = '';
$ccsuffix = '_m.cc';
$hsuffix = '_m.h';
while (@ARGV)
{
    $arg = shift @ARGV;

    if ($arg eq "-s")
    {
        $ccsuffix = shift @ARGV;
    }
    elsif ($arg eq "-t")
    {
        $hsuffix = shift @ARGV;
    }
    elsif ($arg eq "-I")
    {
        # import not supported: just ignore -I <dir> for now
        shift @ARGV;
    }
    elsif ($arg =~ /^-I/)
    {
        # import not supported: just ignore -I<dir> for now
    }
    elsif ($arg eq "-h")
    {
        $here = "true";
    }
    elsif ($filename eq '')
    {
        $filename = $arg;
    }
    else
    {
        print $Usage;
        exit(1);
    }
}

#
# parse file
#
$filename =~ /./ || die "*** Error: no file name given\n";
$filename =~ /\.[^\\\/]*$/ || die "*** Error: file name must contain a dot\n";
$filename =~ s|\\|/|g;
$hfile = $filename;
$hfile =~ s|\.[^.]*$|$hsuffix|;
if ($here eq "true")
{
    $hfile =~ s|^.*/||;
}
$hdef = $hfile;
$hdef =~ s|^.*/||;
$hdef =~ s|\.[^.]*$|_H_|;
$hdef = '_'.$hdef;
$hdef =~ tr/[a-z]/[A-Z]/;
$hdef =~ s/[^a-zA-Z0-9]/_/g;
$ccfile = $filename;
$ccfile =~ s|\.[^.]*$|$ccsuffix|;
if ($here eq "true")
{
    $ccfile =~ s|^.*/||;
}

$ret = 0;

$msg = "";
open(IN,$filename) || die "*** Error: cannot open input file $filename";
while (<IN>)
{
    s|\r||;
    s|//.*$||;
    $msg .= $_;
}
close(IN);

open(H,">$hfile") || die "*** Error: cannot open output file $hfile";
open(CC,">$ccfile") || die "*** Error: cannot open output file $ccfile";

print H "//\n// Generated file, do not edit! Created by opp_msgc from $filename.\n//\n\n";
print H "#ifndef $hdef\n";
print H "#define $hdef\n\n";
print H "#include <omnetpp.h>\n";
print CC "//\n// Generated file, do not edit! Created by opp_msgc.\n//\n\n";

print CC "// Disable warnings about unused variables, empty switch stmts, etc:\n";
print CC "#ifdef _MSC_VER\n";
print CC "#  pragma warning(disable:4101)\n";
print CC "#  pragma warning(disable:4065)\n";
print CC "#endif\n\n";

print CC "#include <iostream>\n";
print CC "#include <sstream>\n";
print CC "#include \"$hfile\"\n\n";

print CC "// Template rule which fires if a struct or class doesn't have operator<<\n";
print CC "template<typename T>\n";
print CC "std::ostream& operator<<(std::ostream& out,const T&) {return out;}\n";
print CC "\n";
print CC "// Another default rule (prevents compiler from choosing base class' doPacking())\n";
print CC "template<typename T>\n";
print CC "void doPacking(cCommBuffer *, T& t) {\n";
print CC "    throw new cException(\"Parsim error: no doPacking() function for type %s or its base class (check .msg and _m.cc/h files!)\",opp_typename(typeid(t)));\n";
print CC "}\n";
print CC "template<typename T>\n";
print CC "void doUnpacking(cCommBuffer *, T& t) {\n";
print CC "    throw new cException(\"Parsim error: no doUnpacking() function for type %s or its base class (check .msg and _m.cc/h files!)\",opp_typename(typeid(t)));\n";
print CC "}\n";
print CC "\n";
print CC "// Automatically supply array (un)packing functions\n";
print CC "template<typename T>\n";
print CC "void doPacking(cCommBuffer *b, T *t, int n) {\n";
print CC "    for (int i=0; i<n; i++)\n";
print CC "        doPacking(b,t[i]);\n";
print CC "}\n";
print CC "template<typename T>\n";
print CC "void doUnpacking(cCommBuffer *b, T *t, int n) {\n";
print CC "    for (int i=0; i<n; i++)\n";
print CC "        doUnpacking(b,t[i]);\n";
print CC "}\n";
print CC "inline void doPacking(cCommBuffer *, cPolymorphic&) {}\n";
print CC "inline void doUnpacking(cCommBuffer *, cPolymorphic&) {}\n";
print CC "\n";
print CC "#define DOPACKING(T,R) \\\n";
print CC "    inline void doPacking(cCommBuffer *b, T R a) {b->pack(a);}  \\\n";
print CC "    inline void doPacking(cCommBuffer *b, T *a, int n) {b->pack(a,n);}  \\\n";
print CC "    inline void doUnpacking(cCommBuffer *b, T& a) {b->unpack(a);}  \\\n";
print CC "    inline void doUnpacking(cCommBuffer *b, T *a, int n) {b->unpack(a,n);}\n";
print CC "#define _\n";
print CC "DOPACKING(char,_)\n";
print CC "DOPACKING(unsigned char,_)\n";
print CC "DOPACKING(bool,_)\n";
print CC "DOPACKING(short,_)\n";
print CC "DOPACKING(unsigned short,_)\n";
print CC "DOPACKING(int,_)\n";
print CC "DOPACKING(unsigned int,_)\n";
print CC "DOPACKING(long,_)\n";
print CC "DOPACKING(unsigned long,_)\n";
print CC "DOPACKING(float,_)\n";
print CC "DOPACKING(double,_)\n";
print CC "DOPACKING(long double,_)\n";
print CC "DOPACKING(char *,_)\n";
print CC "DOPACKING(const char *,_)\n";
print CC "DOPACKING(opp_string,&)\n";
print CC "//DOPACKING(std::string,&)\n"; #FIXME add it after cCommBuffer support is done
print CC "#undef _\n";
print CC "#undef DOPACKING\n";
print CC "\n\n";

# pre-register some OMNeT++ classes so that one doesn't need to announce them
@classes = ('cPolymorphic', 'cObject', 'cMessage', 'cPacket');
$classtype{'cPolymorphic'} = 'noncobject';
$classtype{'cObject'} = 'cobject';
$classtype{'cMessage'} = 'cobject';
$classtype{'cPacket'} = 'cobject';

@enums = ();

# parse cppincludes
while ($msg =~ s/cppinclude\s+(["\<].*?["\>])//s)
{
    print STDERR "*** Warning: cppinclude is deprecated, use cplusplus {{ ... }} syntax instead\n";
    print H "#include $1\n";
}
while ($msg =~ s/cplusplus\s+{{(.*?)}};?//s)
{
    print H "// cplusplus {{ ... }} section:\n";
    print H "$1";
    print H "// end cplusplus\n\n";
}
print H "\n";

# parse imports
while ($msg =~ s/import\s+(".*?");//s)
{
    print STDERR "*** Error: imports are not supported (yet)\n"; $ret=1;
}

# parse type announcements in ned text
while ($msg =~ s/(struct|cobject|noncobject|class|class\s+noncobject|message)\s+([A-Za-z_][A-Za-z0-9_]*)\s*;//s)
{
    $class = $2;
    $type0 = $1;

    if ($type0 eq 'struct') {
        $type = 'struct';
    } elsif ($type0 eq 'cobject') {
        print STDERR "*** Warning: 'cobject' keyword is deprecated, write 'class' instead\n";
        $type = 'cobject';
    } elsif ($type0 eq 'noncobject') {
        print STDERR "*** Warning: 'noncobject' is deprecated, write 'class noncobject' instead\n";
        $type = 'noncobject';
    } elsif ($type0 eq 'class' or $type0 eq 'message') {
        $type = 'cobject';
    } elsif ($type0 =~ /class\s+noncobject/) {
        $type = 'noncobject';
    } else {
        die 'invalid type';
    }

    if (grep(/^\Q$class\E$/,@classes)) {
        if ($classtype{$class} ne $type) {
            print STDERR "*** Error: different declarations for '$class' are inconsistent\n"; $ret=1;
        }
    } else {
        $classtype{$class} = $type;
        push(@classes, $class);
    }
}

# parse enum announcements in ned text
while ($msg =~ s/enum\s+([A-Za-z_][A-Za-z0-9_]*)\s*;//s)
{
    $enumname = $1;

    if (grep(/^\Q$enumname\E$/,@classes)) {
        print STDERR "*** Error: inconsistent declarations for '$enum'\n"; $ret=1;
    } else {
        push(@enums, $enumname);
    }
}


# parse enums in ned text
while ($msg =~ s/enum\s+([A-Za-z0-9_ \t]+?)\s*{(.*?)};?//s)
{
    $enumhdr = $1;
    $fields = $2;

    if ($enumhdr =~ /^([^ \t\n]+?)\s*extends\s*([^ \t\n]+?)$/s)
    {
        $enumname = $1;
        $baseenum = $2;
    }
    elsif ($enumhdr =~ /^([^ \t\n]+?)$/s)
    {
        $enumname = $enumhdr;
        $baseenum = '';
    }
    else
    {
        $enumhdr =~ s/\s+/ /sg;
        print STDERR "*** Error: invalid enum declaration syntax '$enumhdr'\n"; $ret=1;
        $enumname = "???";
        $baseenum = '';
    }

    @fieldlist = ();
    undef %fval;

    #
    # parse enum { ... } syntax
    #
    $scrap = '';
    while ($fields =~ s/^(.*?);//s)
    {
        $field = $1;

        # value
        if ($field =~ s/=\s*(.*?)\s*$//s) {
            $fieldvalue = $1;
        } else {
            $fieldvalue = '';
        }

        # identifier
        if ($field =~ /^\s*([A-Za-z0-9_]+)\s*$/s) {
            $fieldname = $1;
        } else {
            $scrap .= $field;
            print STDERR "*** Error: missing identifier name in enum $enumname\n"; $ret=1;
        }

        # store field
        push(@fieldlist,$fieldname);
        $fval{$fieldname}=$fieldvalue;

    }
    $scrap .= $fields;
    if ($scrap =~ /[^ \t\n]/s) {
        $scrap =~ s/\n\n+/\n\n/sg;
        $scrap =~ s/^\n//s;
        $scrap =~ s/\n$//s;
        print STDERR "*** Error: some parts not understood in enum $enumname:\n"; $ret=1;
        print STDERR "'$scrap'\n";
    }

    #
    # generate code
    #
    if (grep(/^\Q$enumname\E$/,@enums)) {
        print STDERR "*** Error: enum '$enumname' already defined\n"; $ret=1;
    }
    push(@enums, $enumname);

    print H "/**\n";
    print H " * Enum generated from $filename by opp_msgc.\n";
    print H " */\n";
    print H "enum $enumname {\n";
    foreach $fieldname (@fieldlist)
    {
        print H "    $fieldname = $fval{$fieldname},\n";
    }
    print H "};\n\n";

    print CC "EXECUTE_ON_STARTUP($enumname,\n";
    print CC "    cEnum *e = findEnum(\"$enumname\");\n";
    print CC "    if (!e) enums.instance()->add(e = new cEnum(\"$enumname\"));\n";
    foreach $fieldname (@fieldlist)
    {
        print CC "    e->insert($fieldname, \"$fieldname\");\n";
    }
    print CC ");\n\n";

    if ($baseenum ne '')
    {
        # add to base enum, too
        print CC "EXECUTE_ON_STARTUP(_${baseenum}_${enumname},\n";
        print CC "    cEnum *e = findEnum(\"$baseenum\");\n";
        print CC "    if (!e) enums.instance()->add(e = new cEnum(\"$baseenum\"));\n";
        foreach $fieldname (@fieldlist)
        {
            print CC "    e->insert($fieldname, \"$fieldname\");\n";
        }
        print CC ");\n\n";
    }
}

# parse message/class/struct definitions
while ($msg =~ s/(message|class|struct)\s+(.+?)\s*{(.*?)};?//s)
{
    #
    # parse message { ... } syntax
    #
    $keyword = $1;  # 'message' or 'class' or 'struct'
    $msghdr = $2;   # must be "<name>" or "<name> extends <name>"
    $body = $3;

    # reset
    @fieldlist = ();
    @proplist = ();
    undef %pval;

    if ($msghdr =~ /^([^ \t\n]+?)\s*extends\s*([^ \t\n]+?)$/s)
    {
        $msgname = $1;
        $msgbase = $2;
    }
    elsif ($msghdr =~ /^([^ \t\n]+?)$/s)
    {
        $msgname = $msghdr;
        $msgbase = '';
    }
    else
    {
        $msghdr =~ s/\s+/ /sg;
        print STDERR "*** Error: invalid declaration syntax for '$msghdr'\n"; $ret=1;
        $msgname = "???";
        $msgbase = '';
    }

    #
    # process "properties:"
    #
    $scrap = '';
    if ($body =~ /properties:(.*)$/s)
    {
        $properties = $1;
        $properties =~ s/fields:.*$//s;  # cut off fields section
        while ($properties =~ s/^(.*?);//s)
        {
            $prop = $1;
            if ($prop =~ /^\s*(.*?)\s*=\s*(.*?)\s*$/s)
            {
                $propname = $1;
                $propvalue = $2;
                push(@proplist,$propname);
                $pval{$propname} = $propvalue;
            }
            else {$scrap.=$prop;}
        }
        $scrap.=$properties;
        if ($scrap =~ /[^ \t\n]/s)
        {
            $scrap =~ s/\n\n+/\n\n/sg;
            $scrap =~ s/^\n//s;
            $scrap =~ s/\n$//s;
            print STDERR "*** Error: some parts not understood in 'properties' section of '$msgname':\n"; $ret=1;
            print STDERR "'$scrap'\n";
        }
    }

    #
    # process "fields:"
    #
    $scrap = '';
    if ($body =~ /fields:(.*)$/s)
    {
        $fields = $1;
        while ($fields =~ s/^(.*?);//s)
        {
            $field = $1;
            # abstract
            if ($field =~ s/^\s*abstract\s+//s) {
                $isabstract = 1;
                if ($keyword eq 'struct') {
                    print STDERR "*** Error: a struct cannot have abstract fields in '$msgname'\n"; $ret=1;
                }
            } else {
                $isabstract = 0;
            }

            # enum
            if ($field =~ s/enum\s*\((.*?)\)\s*//s) {
                $fieldenum = $1;
            } else {
                $fieldenum = '';
            }

            # default value
            if ($field =~ s/=\s*(.*?)\s*$//s) {
                $fieldvalue = $1;
            } else {
                $fieldvalue = '';
            }

            # array
            if ($field =~ s/\[\s*(.*?)\s*\]\s*$//s) {
                $isarray = 1;
                $arraysize = $1;
                if ($arraysize !~ /^[0-9]*$/ && $arraysize !~ /^[A-Za-z_][A-Za-z0-9_]*$/) {
                    print STDERR "*** Error: array size must be numeric or symbolic constant (not '$arraysize') in '$msgname'\n"; $ret=1;
                }
                if ($arraysize eq '' && $keyword eq 'struct') {
                    print STDERR "*** Error: a struct cannot have dynamic array fields in '$msgname'\n"; $ret=1;
                }
            } else {
                $isarray = 0;
                $arraysize = '';
            }

            # type fieldname
            if ($field =~ /^\s*(.+?)\s*([A-Za-z0-9_]+)\s*$/s)
            {
                $fieldtype = $1;
                $fieldname = $2;
                $fieldtype =~ s/\s+/ /sg;

                push(@fieldlist,$fieldname);
                $ftype{$fieldname} = $fieldtype;
                $fval{$fieldname} = $fieldvalue;
                $fisabstract{$fieldname} = $isabstract;
                $fisarray{$fieldname} = $isarray;
                $farraysize{$fieldname} = $arraysize;
                $fenumname{$fieldname} = $fieldenum;
#                print "$fieldname: $ftype{$fieldname} =$fval{$fieldname}  []:$fisarray{$fieldname}  [$farraysize{$fieldname}]\n";
            }
            else {$scrap.=$field;}
        }
        $scrap.=$fields;
        if ($scrap =~ /[^ \t\n]/s)
        {
            $scrap =~ s/\n\n+/\n\n/sg;
            $scrap =~ s/^\n//s;
            $scrap =~ s/\n$//s;
            print STDERR "*** Error: some parts not understood in 'fields' section of '$msgname':\n"; $ret=1;
            print STDERR "'$scrap'\n";
        }
    }
    else
    {
        print STDERR "*** Error: no 'fields' section in '$msgname'\n"; $ret=1;
        next;
    }

    # now generate code
    prepareForCodeGeneration();
    if ($classtype eq 'struct') {
        generateStruct();
    } else {
        generateClass();
    }
    generateDescriptorClass();
}

$scrap = $msg;
if ($scrap =~ /[^ \t\n]/s)
{
    $scrap =~ s/\n\n+/\n\n/sg;
    $scrap =~ s/^\n//s;
    $scrap =~ s/\n$//s;
    print STDERR "*** Error: following parts of input file were not understood:\n"; $ret=1;
    print STDERR "'$scrap'\n";
}

print H "#endif // $hdef\n";


close(H);
close(CC);

if ($ret)
{
    unlink($hfile);
    unlink($ccfile);
}
exit $ret;


#
# prepare for code generation
#
# in variables:
#
#  $keyword
#  $classtype
#  $gap
#  $omitgetverb
#  $msgclass
#  $realmsgclass
#  $msgbaseclass
#
#  $msgdescclass
#
#  $fieldcount
#  @fieldlist
#
#  %ftype{fieldname}
#  %fval{fieldname}
#  %fisabstract{fieldname}
#  %fisarray{fieldname}
#  %farraysize{fieldname}
#  %fenumname{fieldname}
#
#  %fkind{fieldname}
#  %datatype{fieldname}
#  %argtype{fieldname}
#  %rettype{fieldname}
#  %var{fieldname}
#  %varsize{fieldname}
#  %getter{fieldname}
#  %setter{fieldname}
#  %alloc{fieldname}
#  %getsize{fieldname}
#  %tostring{fieldname}
#  %fromstring{fieldname}
#  %maybe_c_str{fieldname}
#

sub prepareForCodeGeneration
{

    # check base class and determine type of object
    if ($msgbase eq '') {
        if ($keyword eq 'message') {
            $classtype = 'cobject';
        } elsif ($keyword eq 'class') {
            $classtype = 'noncobject';
        } elsif ($keyword eq 'struct') {
            $classtype = 'struct';
        } else {
            die 'internal error';
        }
    } else {
        if (!grep(/^\Q$msgbase\E$/,@classes)) {
            print STDERR "*** Error: unknown base class '$msgbase'\n"; $ret=1;
        }
        $classtype = $classtype{$msgbase};
    }

    # check earlier declarations and register this class
    if (grep(/^\Q$msgname\E$/,@classes)) {
        if (0) { #FIXME what is the condition?
            print STDERR "*** Error: attempt to redefine '$msgname'\n"; $ret=1;
        } elsif ($classtype{$msgname} ne $classtype) {
            print STDERR "*** Error: definition of '$msgname' inconsistent with earlier declaration(s)\n"; $ret=1;
        }
    } else {
        push(@classes, $msgname);
        $classtype{$msgname} = $classtype;
    }

    #
    # produce all sorts of derived names
    #
    if ($pval{"customize"} eq "true") {
        $gap = 1;
        $msgclass = $msgname."_Base";
        $realmsgclass = $msgname;
        $msgdescclass = $realmsgclass."Descriptor";
    } else {
        $gap = 0;
        $msgclass = $msgname;
        $realmsgclass = $msgclass;
        $msgdescclass = $msgclass."Descriptor";
    }
    if ($msgbase eq '') {
        if ($keyword eq 'message') {
            $msgbaseclass = 'cMessage';
        } elsif ($keyword eq 'class') {
            $msgbaseclass = 'cPolymorphic';
        } elsif ($keyword eq 'struct') {
            $msgbaseclass = '';
        } else {
            die 'internal error';
        }
    } else {
        $msgbaseclass = $msgbase;
    }

    if ($pval{"omitGetVerb"} eq "true") {
        $omitgetverb = 1;
    } else {
        $omitgetverb = 0;
    }

    foreach $fieldname (@fieldlist)
    {
        if ($fisabstract{$fieldname} && !$gap) {
            print STDERR "*** Error: abstract fields need 'customize=true' property in '$msgname'\n"; $ret=1;
        }
        if ($fenumname{$fieldname} ne '' && !grep(/^\Q$fenumname{$fieldname}\E$/,@enums)) {
            print STDERR "*** Error: undeclared enum '$fenumname{$fieldname}' used in '$msgname'\n"; $ret=1;
        }
        if ($classtype{$ftype{$fieldname}} eq 'cobject' && ($classtype eq 'noncobject' || $classtype eq 'struct')) {
            print STDERR "*** Error: cannot use cObject field '$ftype{$fieldname} $fieldname' in struct or non-cObject class '$msgname'\n"; $ret=1;
        }

        # variable name
        if ($classtype eq 'struct') {
            $var{$fieldname} = $fieldname;
        } else {
            $var{$fieldname} = $fieldname."_var";
        }
        $varsize{$fieldname} = $fieldname."_arraysize";

        # method names
        if ($classtype ne 'struct') {
            $capfieldname = $fieldname;
            $capfieldname =~ s/(.)(.*)/uc($1).$2/e;
            $setter{$fieldname} = "set".$capfieldname;
            $alloc{$fieldname} = "set".$capfieldname."ArraySize";
            if ($omitgetverb) {
                $getter{$fieldname} = $fieldname;
                $getsize{$fieldname} = $fieldname."ArraySize";
            } else {
                $getter{$fieldname} = "get".$capfieldname;
                $getsize{$fieldname} = "get".$capfieldname."ArraySize";
            }
        }

        # pointer
        $ftype = $ftype{$fieldname};
        if ($ftype =~ /^(.*?)\s*\*$/) {
            $ftype = $1;
            $fpointer = 1;
            print STDERR "*** Error: pointers not supported yet in '$msgname'\n"; $ret=1;
        } else {
            $fpointer = 0;
        }

        # data type, argument type, conversion to/from string...
        if (grep(/^\Q$ftype\E$/,@classes)) {
            $fkind{$fieldname} = 'struct';
        } else {
            $fkind{$fieldname} = 'basic';
        }
        $maybe_c_str{$fieldname} = "";
        if ($fkind{$fieldname} eq 'struct') {
            $datatype{$fieldname} = $ftype;
            $argtype{$fieldname} = "const $ftype&";
            $rettype{$fieldname} = "$ftype&";
            $tostring{$fieldname} = "";
            $fromstring{$fieldname} = "";
            #$fval{$fieldname} = '' unless ($fval{$fieldname} ne '');
        } elsif ($fkind{$fieldname} eq 'basic') {
            if ($ftype eq "bool") {
                $datatype{$fieldname} = "bool";
                $argtype{$fieldname} = "bool";
                $rettype{$fieldname} = "bool";
                $tostring{$fieldname} = "bool2string";
                $fromstring{$fieldname} = "string2bool";
                $fval{$fieldname} = 'false' unless ($fval{$fieldname} ne '');
            } elsif ($ftype eq "char") {
                $datatype{$fieldname} = "char";
                $argtype{$fieldname} = "char";
                $rettype{$fieldname} = "char";
                $tostring{$fieldname} = "long2string";
                $fromstring{$fieldname} = "string2long";
                $fval{$fieldname} = '0' unless ($fval{$fieldname} ne '');
            } elsif ($ftype eq "unsigned char") {
                $datatype{$fieldname} = "unsigned char";
                $argtype{$fieldname} = "unsigned char";
                $rettype{$fieldname} = "unsigned char";
                $tostring{$fieldname} = "long2string";
                $fromstring{$fieldname} = "string2long";
                $fval{$fieldname} = '0' unless ($fval{$fieldname} ne '');
            } elsif ($ftype eq "short") {
                $datatype{$fieldname} = "short";
                $argtype{$fieldname} = "short";
                $rettype{$fieldname} = "short";
                $tostring{$fieldname} = "long2string";
                $fromstring{$fieldname} = "string2long";
                $fval{$fieldname} = '0' unless ($fval{$fieldname} ne '');
            } elsif ($ftype eq "unsigned short") {
                $datatype{$fieldname} = "unsigned short";
                $argtype{$fieldname} = "unsigned short";
                $rettype{$fieldname} = "unsigned short";
                $tostring{$fieldname} = "long2string";
                $fromstring{$fieldname} = "string2long";
                $fval{$fieldname} = '0' unless ($fval{$fieldname} ne '');
            } elsif ($ftype eq "int") {
                $datatype{$fieldname} = "int";
                $argtype{$fieldname} = "int";
                $rettype{$fieldname} = "int";
                $tostring{$fieldname} = "long2string";
                $fromstring{$fieldname} = "string2long";
                $fval{$fieldname} = '0' unless ($fval{$fieldname} ne '');
            } elsif ($ftype eq "unsigned int") {
                $datatype{$fieldname} = "unsigned int";
                $argtype{$fieldname} = "unsigned int";
                $rettype{$fieldname} = "unsigned int";
                $tostring{$fieldname} = "long2string";
                $fromstring{$fieldname} = "string2long";
                $fval{$fieldname} = '0' unless ($fval{$fieldname} ne '');
            } elsif ($ftype eq "long") {
                $datatype{$fieldname} = "long";
                $argtype{$fieldname} = "long";
                $rettype{$fieldname} = "long";
                $tostring{$fieldname} = "long2string";
                $fromstring{$fieldname} = "string2long";
                $fval{$fieldname} = '0' unless ($fval{$fieldname} ne '');
            } elsif ($ftype eq "unsigned long") {
                $datatype{$fieldname} = "unsigned long";
                $argtype{$fieldname} = "unsigned long";
                $rettype{$fieldname} = "unsigned long";
                $tostring{$fieldname} = "long2string";
                $fromstring{$fieldname} = "string2long";
                $fval{$fieldname} = '0' unless ($fval{$fieldname} ne '');
            } elsif ($ftype eq "double") {
                $datatype{$fieldname} = "double";
                $argtype{$fieldname} = "double";
                $rettype{$fieldname} = "double";
                $tostring{$fieldname} = "double2string";
                $fromstring{$fieldname} = "string2double";
                $fval{$fieldname} = '0' unless ($fval{$fieldname} ne '');
            } elsif ($ftype eq "simtime_t") {
                $datatype{$fieldname} = "simtime_t";
                $argtype{$fieldname} = "simtime_t";
                $rettype{$fieldname} = "simtime_t";
                $tostring{$fieldname} = "double2string";
                $fromstring{$fieldname} = "string2double";
                $fval{$fieldname} = '0' unless ($fval{$fieldname} ne '');
            } elsif ($ftype eq "string") {
                $datatype{$fieldname} = "opp_string";
                $argtype{$fieldname} = "const char *";
                $rettype{$fieldname} = "const char *";
                $tostring{$fieldname} = "oppstring2string";
                $fromstring{$fieldname} = "";
                $fval{$fieldname} = '""' unless ($fval{$fieldname} ne '');
                $maybe_c_str{$fieldname} = ".c_str()";
            } else {
                print STDERR "*** Error: unknown data type '$ftype' (is it struct?)\n"; $ret=1;
                $datatype{$fieldname} = $ftype;
                $argtype{$fieldname} = $ftype;
                $rettype{$fieldname} = $ftype;
                $tostring{$fieldname} = "";  # means op<<
                $fromstring{$fieldname} = "";
                $fval{$fieldname} = '0' unless ($fval{$fieldname} ne '');
            }
        } elsif ($fkind{$fieldname} eq 'special') {
            # FIXME TBD
        } else {
            die 'internal error';
        }
    }
}


#
# print class
#
sub generateClass
{
    print H "/**\n";
    print H " * Class generated from $filename by opp_msgc.\n";
    if ($gap)
    {
        print H " *\n";
        print H " * $msgclass is only useful if it gets subclassed, and $realmsgclass is derived from it.\n";
        print H " * The minimum code to be written for $realmsgclass is the following:\n";
        print H " *\n";
        print H " * <pre>\n";
        print H " * class $realmsgclass : public $msgclass\n";
        print H " * {\n";
        print H " *   public:\n";
        if ($classtype eq "cobject") {
            if ($keyword eq "message") {
                print H " *     $realmsgclass(const char *name=NULL, int kind=0) : $msgclass(name,kind) {}\n";
            } else {
                print H " *     $realmsgclass(const char *name=NULL) : $msgclass(name) {}\n";
            }
            print H " *     $realmsgclass(const $realmsgclass& other) : $msgclass(other.name()) {operator=(other);}\n";
        } else {
            print H " *     $realmsgclass() : $msgclass() {}\n";
            print H " *     $realmsgclass(const $realmsgclass& other) : $msgclass() {operator=(other);}\n";
        }
        print H " *     $realmsgclass& operator=(const $realmsgclass& other) {$msgclass\:\:operator=(other); return *this;}\n";
        if ($classtype eq "cobject") {
            print H " *     virtual cObject *dup() const {return new $realmsgclass(*this);}\n";
        }
        print H " *     // ADD CODE HERE to redefine and implement pure virtual functions from $msgclass\n";
        print H " * };\n";
        print H " * </pre>\n";
        if ($classtype eq "cobject") {
            print H " *\n";
            print H " * The following should go into a .cc (.cpp) file:\n";
            print H " *\n";
            print H " * <pre>\n";
            print H " * Register_Class($realmsgclass);\n";
            print H " * </pre>\n";
        }
    }
    print H " */\n";
    if ($msgbaseclass eq "") {
        print H "class $msgclass\n";
    } else {
        print H "class $msgclass : public $msgbaseclass\n";
    }
    print H "{\n";
    print H "  protected:\n";
    foreach $fieldname (@fieldlist)
    {
        if (!$fisabstract{$fieldname}) {
            if ($fisarray{$fieldname} && $farraysize{$fieldname} ne '') {
                print H "    $datatype{$fieldname} $var{$fieldname}\[$farraysize{$fieldname}\];\n";
            } elsif ($fisarray{$fieldname} && $farraysize{$fieldname} eq '') {
                print H "    $datatype{$fieldname} *$var{$fieldname}; // array ptr\n";
                print H "    unsigned int $varsize{$fieldname};\n";
            } else {
                print H "    $datatype{$fieldname} $var{$fieldname};\n";
            }
        }
    }
    if ($gap) {
        print H "    // make constructors protected to avoid instantiation\n";
    } else {
        print H "  public:\n";
    }
    if ($classtype eq "cobject") {
        if ($keyword eq "message") {
            print H "    $msgclass(const char *name=NULL, int kind=0);\n";
        } else {
            print H "    $msgclass(const char *name=NULL);\n";
        }
    } else {
        print H "    $msgclass();\n";
    }
    print H "    $msgclass(const $msgclass& other);\n";
    if ($gap) {
        print H "    // make assignment operator protected to force the user override it\n";
        print H "    $msgclass& operator=(const $msgclass& other);\n";
        print H "  public:\n";
    }
    print H "    virtual ~$msgclass();\n";
    if (!$gap) {
        print H "    $msgclass& operator=(const $msgclass& other);\n";
    }
    if ($classtype eq "cobject") {
        if ($gap) {
            print H "    virtual cObject *dup() const {throw new cException(\"You forgot to manually add a dup() function to class $realmsgclass\");}\n";
        } else {
            print H "    virtual cObject *dup() const {return new $msgclass(*this);}\n";
        }
    }
    print H "    virtual void netPack(cCommBuffer *b);\n";
    print H "    virtual void netUnpack(cCommBuffer *b);\n";
    print H "\n";
    print H "    // field getter/setter methods\n";
    foreach $fieldname (@fieldlist)
    {
        if ($fisabstract{$fieldname}) {
            $pure = ' = 0';
        } else {
            $pure = '';
        }
        if ($fkind{$fieldname} eq 'struct') {
            $getterconst = '';
        } else {
            $getterconst = ' const';
        }
        if ($fisarray{$fieldname} && $farraysize{$fieldname} ne '') {
            print H "    virtual unsigned int $getsize{$fieldname}() const$pure;\n";
            print H "    virtual $rettype{$fieldname} $getter{$fieldname}(unsigned int k)$getterconst$pure;\n";
            print H "    virtual void $setter{$fieldname}(unsigned int k, $argtype{$fieldname} $var{$fieldname})$pure;\n";
        } elsif ($fisarray{$fieldname} && $farraysize{$fieldname} eq '') {
            print H "    virtual void $alloc{$fieldname}(unsigned int size)$pure;\n";
            print H "    virtual unsigned int $getsize{$fieldname}() const$pure;\n";
            print H "    virtual $rettype{$fieldname} $getter{$fieldname}(unsigned int k)$getterconst$pure;\n";
            print H "    virtual void $setter{$fieldname}(unsigned int k, $argtype{$fieldname} $var{$fieldname})$pure;\n";
        } else {
            print H "    virtual $rettype{$fieldname} $getter{$fieldname}()$getterconst$pure;\n";
            print H "    virtual void $setter{$fieldname}($argtype{$fieldname} $var{$fieldname})$pure;\n";
        }
    }
    print H "};\n\n";

    if ($classtype eq "cobject" && !$gap) {
        # we only register cObject subclasses. If something is at least cPolymorphic,
        # it could be registed in theory too, but we'll leave this to the user.
        print CC "Register_Class($msgclass);\n\n";
    }

    if (!$gap) {
        print H "inline void doPacking(cCommBuffer *b, $realmsgclass& obj) {obj.netPack(b);}\n";
        print H "inline void doUnpacking(cCommBuffer *b, $realmsgclass& obj) {obj.netUnpack(b);}\n\n";
    }

    if ($classtype eq "cobject") {
        if ($keyword eq "message") {
            # CAREFUL when assigning values to existing members gets implemented!
            # The msg kind passed to the ctor should take priority!!!
            print CC "$msgclass\:\:$msgclass(const char *name, int kind) : $msgbaseclass(name,kind)\n";
        } else {
            if ($msgbaseclass eq "") {
                print CC "$msgclass\:\:$msgclass(const char *name)\n";
            } else {
                print CC "$msgclass\:\:$msgclass(const char *name) : $msgbaseclass(name)\n";
            }
        }
    } else {
        if ($msgbaseclass eq "") {
            print CC "$msgclass\:\:$msgclass()\n";
        } else {
            print CC "$msgclass\:\:$msgclass() : $msgbaseclass()\n";
        }
    }
    print CC "{\n";
    print CC "    unsigned int i;\n";
    foreach $fieldname (@fieldlist)
    {
        if (!$fisabstract{$fieldname}) {
            if ($fisarray{$fieldname} && $farraysize{$fieldname} ne '') {
                if ($fkind{$fieldname} eq 'basic') {
                    print CC "    for (i=0; i<$farraysize{$fieldname}; i++)\n";
                    print CC "        this->$var{$fieldname}\[i\] = $fval{$fieldname};\n";
                }
                if ($classtype{$ftype{$fieldname}} eq 'cobject') {
                  print CC "    for (i=0; i<$farraysize{$fieldname}; i++)\n";
                  print CC "        take(&(this->$var{$fieldname}\[i\]));\n";
                }
            } elsif ($fisarray{$fieldname} && $farraysize{$fieldname} eq '') {
                print CC "    $varsize{$fieldname} = 0;\n";
                print CC "    this->$var{$fieldname} = 0;\n";
            } else {
                if ($fval{$fieldname} ne '') {
                  print CC "    this->$var{$fieldname} = $fval{$fieldname};\n";
                }
                if ($classtype{$ftype{$fieldname}} eq 'cobject') {
                  print CC "    take(&(this->$var{$fieldname}));\n";
                }
            }
        }
    }
    print CC "}\n\n";
    if ($msgbaseclass eq "") {
        print CC "$msgclass\:\:$msgclass(const $msgclass& other)\n";
    } else {
        print CC "$msgclass\:\:$msgclass(const $msgclass& other) : $msgbaseclass()\n";
    }
    print CC "{\n";
    print CC "    unsigned int i;\n";
    if ($classtype eq "cobject") {
        print CC "    setName(other.name());\n";
    }
    foreach $fieldname (@fieldlist)
    {
      if (!$fisabstract{$fieldname}) {
        if ($fisarray{$fieldname} && $farraysize{$fieldname} ne '') {
          if ($classtype{$ftype{$fieldname}} eq 'cobject') {
            print CC "    for (i=0; i<$farraysize{$fieldname}; i++)\n";
            print CC "        take(&(this->$var{$fieldname}\[i\]));\n";
          }
        } elsif ($fisarray{$fieldname} && $farraysize{$fieldname} eq '') {
          print CC "    $varsize{$fieldname} = 0;\n";
          print CC "    this->$var{$fieldname} = 0;\n";
        } elsif (!$fisarray{$fieldname} && $classtype{$ftype{$fieldname}} eq 'cobject') {
          print CC "    take(&(this->$var{$fieldname}));\n";
        }
      }
    }
    print CC "    operator=(other);\n";
    print CC "}\n\n";
    print CC "$msgclass\:\:~$msgclass()\n";
    print CC "{\n";
    print CC "    unsigned int i;\n";
    foreach $fieldname (@fieldlist)
    {
        if (!$fisabstract{$fieldname}) {
            if ($classtype{$ftype{$fieldname}} eq 'cobject') {
                if (!$fisarray{$fieldname}) {
                    print CC "    drop(&(this->$var{$fieldname}));\n";
                } elsif ($farraysize{$fieldname} ne '') {
                    print CC "    for (i=0; i<$farraysize{$fieldname}; i++)\n";
                    print CC "        drop(&(this->$var{$fieldname}\[i\]));\n";
                } else {
                    print CC "    for (i=0; i<$varsize{$fieldname}; i++)\n";
                    print CC "        drop(&(this->$var{$fieldname}\[i\]));\n";
                }
            }
            if ($fisarray{$fieldname} && $farraysize{$fieldname} eq '') {
                print CC "    delete [] $var{$fieldname};\n";
            }
        }
    }
    print CC "}\n\n";
    print CC "$msgclass& $msgclass\:\:operator=(const $msgclass& other)\n";
    print CC "{\n";
    print CC "    if (this==&other) return *this;\n";
    print CC "    unsigned int i;\n";
    if ($msgbaseclass ne "") {
        print CC "    $msgbaseclass\:\:operator=(other);\n";
    }
    foreach $fieldname (@fieldlist)
    {
        if (!$fisabstract{$fieldname}) {
            if ($fisarray{$fieldname} && $farraysize{$fieldname} ne '') {
                print CC "    for (i=0; i<$farraysize{$fieldname}; i++)\n";
                print CC "        this->$var{$fieldname}\[i\] = other.$var{$fieldname}\[i\];\n";
                if ($classtype{$ftype{$fieldname}} eq 'cobject') {
                    print CC "    for (i=0; i<$farraysize{$fieldname}; i++)\n";
                    print CC "        this->$var{$fieldname}\[i\].setName(other.$var{$fieldname}\[i\].name());\n";
                }
            } elsif ($fisarray{$fieldname} && $farraysize{$fieldname} eq '') {
                print CC "    delete [] this->$var{$fieldname};\n";
                print CC "    this->$var{$fieldname} = (other.$varsize{$fieldname}==0) ? NULL : new $datatype{$fieldname}\[other.$varsize{$fieldname}\];\n";
                print CC "    $varsize{$fieldname} = other.$varsize{$fieldname};\n";
                print CC "    for (i=0; i<$varsize{$fieldname}; i++)\n";
                if ($classtype{$ftype{$fieldname}} eq 'cobject') {
                    print CC "    {\n";
                    print CC "        take(&(this->$var{$fieldname}\[i\]));\n";
                    print CC "        this->$var{$fieldname}\[i\] = other.$var{$fieldname}\[i\];\n";
                    print CC "        this->$var{$fieldname}\[i\].setName(other.$var{$fieldname}\[i\].name());\n";
                    print CC "    }\n";
                } else {
                    print CC "        this->$var{$fieldname}\[i\] = other.$var{$fieldname}\[i\];\n";
                }
            } else {
                print CC "    this->$var{$fieldname} = other.$var{$fieldname};\n";
                if (!$fisarray{$fieldname} && $classtype{$ftype{$fieldname}} eq 'cobject') {
                    print CC "    this->$var{$fieldname}.setName(other.$var{$fieldname}.name());\n";
                }
            }
        }
    }
    print CC "    return *this;\n";
    print CC "}\n\n";

    #
    # Note: This class may not be derived from cObject, and then this netPack()/
    # netUnpack() is NOT that of cObject. However it's still needed because a
    # "friend" doPacking() function could not access protected members otherwise.
    #
    print CC "void $msgclass\:\:netPack(cCommBuffer *b)\n";
    print CC "{\n";
    if ($msgbaseclass ne "") {
        if ($classtype eq "cobject") {
            print CC "    $msgbaseclass\:\:netPack(b);\n";
        } else {
            print CC "    doPacking(b,($msgbaseclass&)*this);\n"; # this would do for cObject too, but the other is nicer
        }
    }
    foreach $fieldname (@fieldlist)
    {
        if ($fisabstract{$fieldname}) {
            print CC "    // field $fieldname is abstract -- please do packing in customized class\n";
        } else {
            if ($fisarray{$fieldname} && $farraysize{$fieldname} ne '') {
                print CC "    doPacking(b,this->$var{$fieldname},$farraysize{$fieldname});\n";
            } elsif ($fisarray{$fieldname} && $farraysize{$fieldname} eq '') {
                print CC "    b->pack($varsize{$fieldname});\n";
                print CC "    doPacking(b,this->$var{$fieldname},$varsize{$fieldname});\n";
            } else {
                print CC "    doPacking(b,this->$var{$fieldname});\n";
            }
        }
    }
    print CC "}\n\n";

    print CC "void $msgclass\:\:netUnpack(cCommBuffer *b)\n";
    print CC "{\n";
    if ($msgbaseclass ne "") {
        if ($classtype eq "cobject") {
            print CC "    $msgbaseclass\:\:netUnpack(b);\n";
        } else {
            print CC "    doUnpacking(b,($msgbaseclass&)*this);\n"; # this would do for cObject too, but the other is nicer
        }
    }
    foreach $fieldname (@fieldlist)
    {
        if ($fisabstract{$fieldname}) {
            print CC "    // field $fieldname is abstract -- please do unpacking in customized class\n";
        } else {
            if ($fisarray{$fieldname} && $farraysize{$fieldname} ne '') {
                print CC "    doUnpacking(b,this->$var{$fieldname},$farraysize{$fieldname});\n";
            } elsif ($fisarray{$fieldname} && $farraysize{$fieldname} eq '') {
                print CC "    delete [] this->$var{$fieldname};\n";
                print CC "    b->unpack($varsize{$fieldname});\n";
                print CC "    if ($varsize{$fieldname}==0) {\n";
                print CC "        this->$var{$fieldname} = 0;\n";
                print CC "    } else {\n";
                print CC "        this->$var{$fieldname} = new $datatype{$fieldname}\[$varsize{$fieldname}\];\n";
                print CC "        doUnpacking(b,this->$var{$fieldname},$varsize{$fieldname});\n";
                print CC "    }\n";
            } else {
                print CC "    doUnpacking(b,this->$var{$fieldname});\n";
            }
        }
    }
    print CC "}\n\n";

    foreach $fieldname (@fieldlist)
    {
        if (!$fisabstract{$fieldname}) {
            if ($fkind{$fieldname} eq 'struct') {
                $getterconst = '';
            } else {
                $getterconst = ' const';
            }
            if ($fisarray{$fieldname} && $farraysize{$fieldname} ne '') {
                print CC "unsigned int $msgclass\:\:$getsize{$fieldname}() const\n";
                print CC "{\n";
                print CC "    return $farraysize{$fieldname};\n";
                print CC "}\n\n";
                print CC "$rettype{$fieldname} $msgclass\:\:$getter{$fieldname}(unsigned int k)$getterconst\n";
                print CC "{\n";
                print CC "    if (k>=$farraysize{$fieldname}) throw new cException(\"Array of size $farraysize{$fieldname} indexed by \%d\", k);\n";
                print CC "    return $var{$fieldname}\[k\]$maybe_c_str{$fieldname};\n";
                print CC "}\n\n";
                print CC "void $msgclass\:\:$setter{$fieldname}(unsigned int k, $argtype{$fieldname} $var{$fieldname})\n";
                print CC "{\n";
                print CC "    if (k>=$farraysize{$fieldname}) throw new cException(\"Array of size $farraysize{$fieldname} indexed by \%d\", k);\n";
                print CC "    this->$var{$fieldname}\[k\] = $var{$fieldname};\n";
                print CC "}\n\n";
            } elsif ($fisarray{$fieldname} && $farraysize{$fieldname} eq '') {
                print CC "void $msgclass\:\:$alloc{$fieldname}(unsigned int size)\n";
                print CC "{\n";
                print CC "    $datatype{$fieldname} *$var{$fieldname}2 = (size==0) ? NULL : new $datatype{$fieldname}\[size\];\n";
                print CC "    unsigned int sz = $varsize{$fieldname} < size ? $varsize{$fieldname} : size;\n";
                print CC "    unsigned int i;\n";
                print CC "    for (i=0; i<sz; i++)\n";
                print CC "        $var{$fieldname}2\[i\] = this->$var{$fieldname}\[i\];\n";
                if ($fkind{$fieldname} eq 'basic') {
                    print CC "    for (i=sz; i<size; i++)\n";
                    print CC "        $var{$fieldname}2\[i\] = 0;\n";
                }
                if ($classtype{$ftype{$fieldname}} eq 'cobject') {
                    print CC "    for (i=sz; i<size; i++)\n";
                    print CC "        take(&($var{$fieldname}2\[i\]));\n";
                }
                print CC "    $varsize{$fieldname} = size;\n";
                print CC "    delete [] this->$var{$fieldname};\n";
                print CC "    this->$var{$fieldname} = $var{$fieldname}2;\n";
                print CC "}\n\n";
                print CC "unsigned int $msgclass\:\:$getsize{$fieldname}() const\n";
                print CC "{\n";
                print CC "    return $varsize{$fieldname};\n";
                print CC "}\n\n";
                print CC "$rettype{$fieldname} $msgclass\:\:$getter{$fieldname}(unsigned int k)$getterconst\n";
                print CC "{\n";
                print CC "    if (k>=$varsize{$fieldname}) throw new cException(\"Array of size $varsize{$fieldname} indexed by \%d\", k);\n";
                print CC "    return $var{$fieldname}\[k\]$maybe_c_str{$fieldname};\n";
                print CC "}\n\n";
                print CC "void $msgclass\:\:$setter{$fieldname}(unsigned int k, $argtype{$fieldname} $var{$fieldname})\n";
                print CC "{\n";
                print CC "    if (k>=$varsize{$fieldname}) throw new cException(\"Array of size $varsize{$fieldname} indexed by \%d\", k);\n";
                print CC "    this->$var{$fieldname}\[k\]=$var{$fieldname};\n";
                print CC "}\n\n";
            } else {
                print CC "$rettype{$fieldname} $msgclass\:\:$getter{$fieldname}()$getterconst\n";
                print CC "{\n";
                print CC "    return $var{$fieldname}$maybe_c_str{$fieldname};\n";
                print CC "}\n\n";
                print CC "void $msgclass\:\:$setter{$fieldname}($argtype{$fieldname} $var{$fieldname})\n";
                print CC "{\n";
                print CC "    this->$var{$fieldname} = $var{$fieldname};\n";
                print CC "}\n\n";
            }
        }
    }
}


#
# print struct
#
sub generateStruct
{
    print H "/**\n";
    print H " * Struct generated from $filename by opp_msgc.\n";
    print H " */\n";
    if ($msgbaseclass eq "") {
        print H "struct $msgclass\n";
    } else {
        print H "struct $msgclass : public $msgbaseclass\n";
    }
    print H "{\n";
    print H "    $msgclass();\n";
    foreach $fieldname (@fieldlist)
    {
        if ($fisarray{$fieldname}) {
            print H "    $datatype{$fieldname} $var{$fieldname}\[$farraysize{$fieldname}\];\n";
        } else {
            print H "    $datatype{$fieldname} $var{$fieldname};\n";
        }
    }
    print H "};\n\n";

    print H "void doPacking(cCommBuffer *b, $msgclass& a);\n";
    print H "void doUnpacking(cCommBuffer *b, $msgclass& a);\n\n";

    print CC "$msgclass\:\:$msgclass()\n";
    print CC "{\n";
    my $hasarray=0;
    foreach $fieldname (@fieldlist) {$hasarray=1 if ($fisarray{$fieldname});}
    print CC "    unsigned int i;\n" if ($hasarray);
    foreach $fieldname (@fieldlist)
    {
        die "abstract field not possible in struct" if ($fisabstract{$fieldname});
        die "cObject field not possible in struct" if ($classtype{$ftype{$fieldname}} eq 'cobject');
        die "dynamic array not possible in struct" if ($fisarray{$fieldname} && $farraysize{$fieldname} eq '');
        if ($fisarray{$fieldname} && $farraysize{$fieldname} ne '') {
            if ($fkind{$fieldname} eq 'basic') {
                print CC "    for (i=0; i<$farraysize{$fieldname}; i++)\n";
                print CC "        $var{$fieldname}\[i\] = $fval{$fieldname};\n";
            }
        } else {
            if ($fval{$fieldname} ne '') {
                print CC "    $var{$fieldname} = $fval{$fieldname};\n";
            }
        }
    }
    print CC "}\n\n";
    print CC "void doPacking(cCommBuffer *b, $msgclass& a)\n";
    print CC "{\n";
    if ($msgbaseclass ne "") {
        print CC "    doPacking(b,($msgbaseclass&)a);\n";
    }
    foreach $fieldname (@fieldlist)
    {
        if ($fisarray{$fieldname}) {
            print CC "    doPacking(b,a.$var{$fieldname},$farraysize{$fieldname});\n";
        } else {
            print CC "    doPacking(b,a.$var{$fieldname});\n";
        }
    }
    print CC "}\n\n";

    print CC "void doUnpacking(cCommBuffer *b, $msgclass& a)\n";
    print CC "{\n";
    if ($msgbaseclass ne "") {
        print CC "    doUnpacking(b,($msgbaseclass&)a);\n";
    }
    foreach $fieldname (@fieldlist)
    {
        if ($fisarray{$fieldname}) {
            print CC "    doUnpacking(b,a.$var{$fieldname},$farraysize{$fieldname});\n";
        } else {
            print CC "    doUnpacking(b,a.$var{$fieldname});\n";
        }
    }
    print CC "}\n\n";
}


#
# print descriptor class
#
sub generateDescriptorClass
{
    print CC "class $msgdescclass : public cStructDescriptor\n";
    print CC "{\n";
    print CC "  public:\n";
    print CC "    $msgdescclass();\n";
    print CC "    virtual ~$msgdescclass();\n";
    print CC "    $msgdescclass& operator=(const $msgdescclass& other);\n";
    print CC "    virtual cObject *dup() const {return new $msgdescclass(*this);}\n";


    print CC "\n";
    print CC "    virtual int getFieldCount();\n";
    print CC "    virtual const char *getFieldName(int field);\n";
    print CC "    virtual int getFieldType(int field);\n";
    print CC "    virtual const char *getFieldTypeString(int field);\n";
    print CC "    virtual const char *getFieldEnumName(int field);\n";
    print CC "    virtual int getArraySize(int field);\n";
    print CC "\n";
    print CC "    virtual bool getFieldAsString(int field, int i, char *resultbuf, int bufsize);\n";
    print CC "    virtual bool setFieldAsString(int field, int i, const char *value);\n";
    print CC "\n";
    print CC "    virtual const char *getFieldStructName(int field);\n";
    print CC "    virtual void *getFieldStructPointer(int field, int i);\n";
    print CC "    virtual sFieldWrapper *getFieldWrapper(int field, int i);\n";
    print CC "};\n\n";

    # register class
    print CC "Register_Class($msgdescclass);\n\n";

    # ctor, dtor
    $fieldcount = $#fieldlist+1;
    print CC "$msgdescclass\:\:$msgdescclass() : cStructDescriptor(\"$msgbaseclass\")\n";
    print CC "{\n";
    print CC "}\n";
    print CC "\n";

    print CC "$msgdescclass\:\:~$msgdescclass()\n";
    print CC "{\n";
    print CC "}\n";
    print CC "\n";

    # getFieldCount()
    print CC "int $msgdescclass\:\:getFieldCount()\n";
    print CC "{\n";
    print CC "    return baseclassdesc ? $fieldcount+baseclassdesc->getFieldCount() : $fieldcount;\n";
    print CC "}\n";
    print CC "\n";

    # getFieldType()
    print CC "int $msgdescclass\:\:getFieldType(int field)\n";
    print CC "{\n";
    print CC "    if (baseclassdesc) {\n";
    print CC "        if (field < baseclassdesc->getFieldCount())\n";
    print CC "            return baseclassdesc->getFieldType(field);\n";
    print CC "        field -= baseclassdesc->getFieldCount();\n";
    print CC "    }\n";
    print CC "    switch (field) {\n";
    for ($i=0; $i<$fieldcount; $i++)
    {
        if ($fisarray{$fieldlist[$i]}) {
            $arr = '_ARRAY';
        } else {
            $arr = '';
        }
        if ($fkind{$fieldlist[$i]} eq 'basic') {
            print CC "        case $i: return FT_BASIC${arr};\n";
        } elsif ($fkind{$fieldlist[$i]} eq 'struct') {
            print CC "        case $i: return FT_STRUCT${arr};\n";
        } elsif ($fkind{$fieldlist[$i]} eq 'special') {
            print CC "        case $i: return FT_SPECIAL${arr};\n";
        } else {
            die 'internal error';
        }
    }
    print CC "        default: return FT_INVALID;\n";
    print CC "    }\n";
    print CC "}\n";
    print CC "\n";

    # getFieldName()
    print CC "const char *$msgdescclass\:\:getFieldName(int field)\n";
    print CC "{\n";
    print CC "    if (baseclassdesc) {\n";
    print CC "        if (field < baseclassdesc->getFieldCount())\n";
    print CC "            return baseclassdesc->getFieldName(field);\n";
    print CC "        field -= baseclassdesc->getFieldCount();\n";
    print CC "    }\n";
    print CC "    switch (field) {\n";
    for ($i=0; $i<$fieldcount; $i++)
    {
        print CC "        case $i: return \"$fieldlist[$i]\";\n";
    }
    print CC "        default: return NULL;\n";
    print CC "    }\n";
    print CC "}\n";
    print CC "\n";

    # getFieldTypeString()
    print CC "const char *$msgdescclass\:\:getFieldTypeString(int field)\n";
    print CC "{\n";
    print CC "    if (baseclassdesc) {\n";
    print CC "        if (field < baseclassdesc->getFieldCount())\n";
    print CC "            return baseclassdesc->getFieldTypeString(field);\n";
    print CC "        field -= baseclassdesc->getFieldCount();\n";
    print CC "    }\n";
    print CC "    switch (field) {\n";
    for ($i=0; $i<$fieldcount; $i++)
    {
        print CC "        case $i: return \"$ftype{$fieldlist[$i]}\";\n";
    }
    print CC "        default: return NULL;\n";
    print CC "    }\n";
    print CC "}\n";
    print CC "\n";

    # getFieldEnumName()
    print CC "const char *$msgdescclass\:\:getFieldEnumName(int field)\n";
    print CC "{\n";
    print CC "    if (baseclassdesc) {\n";
    print CC "        if (field < baseclassdesc->getFieldCount())\n";
    print CC "            return baseclassdesc->getFieldEnumName(field);\n";
    print CC "        field -= baseclassdesc->getFieldCount();\n";
    print CC "    }\n";
    print CC "    switch (field) {\n";
    for ($i=0; $i<$fieldcount; $i++)
    {
        if ($fenumname{$fieldlist[$i]} ne '') {
            print CC "        case $i: return \"$fenumname{$fieldlist[$i]}\";\n";
        }
    }
    print CC "        default: return NULL;\n";
    print CC "    }\n";
    print CC "}\n";
    print CC "\n";

    # getArraySize()
    print CC "int $msgdescclass\:\:getArraySize(int field)\n";
    print CC "{\n";
    print CC "    if (baseclassdesc) {\n";
    print CC "        if (field < baseclassdesc->getFieldCount())\n";
    print CC "            return baseclassdesc->getArraySize(field);\n";
    print CC "        field -= baseclassdesc->getFieldCount();\n";
    print CC "    }\n";
    print CC "    $msgclass *pp = ($msgclass *)p;\n";
    print CC "    switch (field) {\n";
    for ($i=0; $i<$fieldcount; $i++) {
        if ($fisarray{$fieldlist[$i]}) {
            if ($farraysize{$fieldlist[$i]} ne '') {
                print CC "        case $i: return $farraysize{$fieldlist[$i]};\n";
            } elsif ($classtype eq 'struct') {
                print CC "        case $i: return pp->$varsize{$fieldlist[$i]};\n";
            } else {
                print CC "        case $i: return pp->$getsize{$fieldlist[$i]}();\n";
            }
        }
    }
    print CC "        default: return 0;\n";
    print CC "    }\n";
    print CC "}\n";
    print CC "\n";

    # getFieldAsString()
    print CC "bool $msgdescclass\:\:getFieldAsString(int field, int i, char *resultbuf, int bufsize)\n";
    print CC "{\n";
    print CC "    if (baseclassdesc) {\n";
    print CC "        if (field < baseclassdesc->getFieldCount())\n";
    print CC "            return baseclassdesc->getFieldAsString(field,i,resultbuf,bufsize);\n";
    print CC "        field -= baseclassdesc->getFieldCount();\n";
    print CC "    }\n";
    print CC "    $msgclass *pp = ($msgclass *)p;\n";
    print CC "    switch (field) {\n";
    for ($i=0; $i<$fieldcount; $i++)
    {
        if ($fkind{$fieldlist[$i]} eq 'basic') {
            if ($classtype eq 'struct') {
                if ($fisarray{$fieldlist[$i]}) {
                    if ($farraysize{$fieldlist[$i]} ne '') {
                        print CC "        case $i: if (i>=$farraysize{$fieldlist[$i]}) return false;\n";
                    } else {
                        print CC "        case $i: if (i>=pp->$varsize{$fieldlist[$i]}) return false;\n";
                    }
                    print CC "                $tostring{$fieldlist[$i]}(pp->$var{$fieldlist[$i]}\[i\],resultbuf,bufsize); return true;\n";
                } else {
                    print CC "        case $i: $tostring{$fieldlist[$i]}(pp->$var{$fieldlist[$i]},resultbuf,bufsize); return true;\n";
                }
            } else {
                if ($fisarray{$fieldlist[$i]}) {
                    print CC "        case $i: $tostring{$fieldlist[$i]}(pp->$getter{$fieldlist[$i]}(i),resultbuf,bufsize); return true;\n";
                } else {
                    print CC "        case $i: $tostring{$fieldlist[$i]}(pp->$getter{$fieldlist[$i]}(),resultbuf,bufsize); return true;\n";
                }
            }
        } elsif ($fkind{$fieldlist[$i]} eq 'struct') {
            if ($classtype eq 'struct') {
                if ($fisarray{$fieldlist[$i]}) {
                    print CC "        case $i: {std::stringstream out; out << pp->$var{$fieldlist[$i]}\[i\]; opp_strprettytrunc(resultbuf,out.str().c_str(),bufsize-1); return true;}\n";
                } else {
                    print CC "        case $i: {std::stringstream out; out << pp->$var{$fieldlist[$i]}; opp_strprettytrunc(resultbuf,out.str().c_str(),bufsize-1); return true;}\n";
                }
            } else {
                if ($fisarray{$fieldlist[$i]}) {
                    print CC "        case $i: {std::stringstream out; out << pp->$getter{$fieldlist[$i]}(i); opp_strprettytrunc(resultbuf,out.str().c_str(),bufsize-1); return true;}\n";
                } else {
                    print CC "        case $i: {std::stringstream out; out << pp->$getter{$fieldlist[$i]}(); opp_strprettytrunc(resultbuf,out.str().c_str(),bufsize-1); return true;}\n";
                }
            }
        } elsif ($fkind{$fieldlist[$i]} eq 'special') {
            print CC "        case $i: return false; // TBD\n";
        } else {
            die 'internal error';
        }
    }
    print CC "        default: return false;\n";
    print CC "    }\n";
    print CC "}\n";
    print CC "\n";

    # setFieldAsString()
    print CC "bool $msgdescclass\:\:setFieldAsString(int field, int i, const char *value)\n";
    print CC "{\n";
    print CC "    if (baseclassdesc) {\n";
    print CC "        if (field < baseclassdesc->getFieldCount())\n";
    print CC "            return baseclassdesc->setFieldAsString(field,i,value);\n";
    print CC "        field -= baseclassdesc->getFieldCount();\n";
    print CC "    }\n";
    print CC "    $msgclass *pp = ($msgclass *)p;\n";
    print CC "    switch (field) {\n";
    for ($i=0; $i<$fieldcount; $i++)
    {
        if ($fkind{$fieldlist[$i]} eq 'basic') {
            if ($classtype eq 'struct') {
                if ($fisarray{$fieldlist[$i]}) {
                    if ($farraysize{$fieldlist[$i]} ne '') {
                        print CC "        case $i: if (i>=$farraysize{$fieldlist[$i]}) return false;\n";
                    } else {
                        print CC "        case $i: if (i>=pp->$varsize{$fieldlist[$i]}) return false;\n";
                    }
                    print CC "                pp->$var{$fieldlist[$i]}\[i\] = $fromstring{$fieldlist[$i]}(value); return true;\n";
                } else {
                    print CC "        case $i: pp->$var{$fieldlist[$i]} = $fromstring{$fieldlist[$i]}(value); return true;\n";
                }
            } else {
                if ($fisarray{$fieldlist[$i]}) {
                    print CC "        case $i: pp->$setter{$fieldlist[$i]}(i,$fromstring{$fieldlist[$i]}(value)); return true;\n";
                } else {
                    print CC "        case $i: pp->$setter{$fieldlist[$i]}($fromstring{$fieldlist[$i]}(value)); return true;\n";
                }
            }
        } elsif ($fkind{$fieldlist[$i]} eq 'struct') {
            print CC "        case $i: return false;\n";
        } elsif ($fkind{$fieldlist[$i]} eq 'special') {
            print CC "        case $i: return false; //TBD!!!\n";
        } else {
            die 'internal error';
        }
    }
    print CC "        default: return false;\n";
    print CC "    }\n";
    print CC "}\n";
    print CC "\n";

    # getFieldStructName()
    print CC "const char *$msgdescclass\:\:getFieldStructName(int field)\n";
    print CC "{\n";
    print CC "    if (baseclassdesc) {\n";
    print CC "        if (field < baseclassdesc->getFieldCount())\n";
    print CC "            return baseclassdesc->getFieldStructName(field);\n";
    print CC "        field -= baseclassdesc->getFieldCount();\n";
    print CC "    }\n";
    print CC "    switch (field) {\n";
    for ($i=0; $i<$fieldcount; $i++)
    {
        if ($fkind{$fieldlist[$i]} eq 'struct') {
            print CC "        case $i: return \"$ftype{$fieldlist[$i]}\"; break;\n";
        }
    }
    print CC "        default: return NULL;\n";
    print CC "    }\n";
    print CC "}\n";
    print CC "\n";

    # getFieldStructPointer()
    print CC "void *$msgdescclass\:\:getFieldStructPointer(int field, int i)\n";
    print CC "{\n";
    print CC "    if (baseclassdesc) {\n";
    print CC "        if (field < baseclassdesc->getFieldCount())\n";
    print CC "            return baseclassdesc->getFieldStructPointer(field, i);\n";
    print CC "        field -= baseclassdesc->getFieldCount();\n";
    print CC "    }\n";
    print CC "    $msgclass *pp = ($msgclass *)p;\n";
    print CC "    switch (field) {\n";
    for ($i=0; $i<$fieldcount; $i++)
    {
        if ($fkind{$fieldlist[$i]} eq 'struct') {
            if ($classtype eq 'struct') {
                if ($fisarray{$fieldlist[$i]}) {
                    print CC "        case $i: return (void *)&pp->$var{$fieldlist[$i]}\[i\]; break;\n";
                } else {
                    print CC "        case $i: return (void *)&pp->$var{$fieldlist[$i]}; break;\n";
                }
            } else {
                if ($fisarray{$fieldlist[$i]}) {
                    print CC "        case $i: return (void *)&pp->$getter{$fieldlist[$i]}(i); break;\n";
                } else {
                    print CC "        case $i: return (void *)&pp->$getter{$fieldlist[$i]}(); break;\n";
                }
            }
        }
    }
    print CC "        default: return NULL;\n";
    print CC "    }\n";
    print CC "}\n";
    print CC "\n";

    # getFieldWrapper()
    print CC "sFieldWrapper *$msgdescclass\:\:getFieldWrapper(int field, int i)\n";
    print CC "{\n";
    print CC "    return NULL;\n";
    print CC "}\n\n";
}


