<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="text" media-type="text/plain" encoding="utf-8"/>

<xsl:template match="/"><![CDATA[
# This is an automatically generated file. 
# This is an automatically generated file. 
# This is an automatically generated file. 
# This is an automatically generated file. 
# This is an automatically generated file. 

# See doc/protocol.xml and doc/doc2messages_pm.xsl (and doc/Makefile)

package KGS::Messages;

use strict;

our %type;

our %dec_client; # decode messages send to server
our %enc_client; # encode messages send to server
our %dec_server; # decode messages received from server
our %enc_server; # encode messages received from server

{

use Gtk2::GoBoard::Constants; # for MARK_xyz
use Math::BigInt ();

my $data; # stores currently processed decoding/encoding packet

sub _set_data($) { $data = shift } # for debugging or special apps only
sub _get_data() { $data } # for debugging or special apps only

# primitive enc/decoders

#############################################################################

sub dec_U8 {
   (my ($r), $data) = unpack "C a*", $data; $r;
}

sub dec_U16 {
   (my ($r), $data) = unpack "v a*", $data; $r;
}

sub dec_U32 {
   (my ($r), $data) = unpack "V a*", $data; $r;
}

sub dec_U64 {
   # do NOT use Math::BigInt here.
   my ($lo, $hi) = (dec_U32, dec_U32);
   $hi * 2**32 + $lo;
}

sub dec_I8 {
   (my ($r), $data) = unpack "c a*", $data;
   $r;
}

sub dec_I16 {
   (my ($r), $data) = unpack "v a*", $data;
   unpack "s", pack "S", $r;
}

sub dec_I32 {
   (my ($r), $data) = unpack "V a*", $data;
   unpack "i", pack "I", $r;
}

sub dec_DATA {
   (my ($r), $data) = ($data, ""); $r;
}

sub dec_ZSTRING {
   $data =~ s/^((?:..)*?)(?:\x00\x00|\Z)//s;
   # use Encode...
   join "", map chr, unpack "v*", $1;
}

BEGIN { *dec_STRING = \&dec_ZSTRING };

sub dec_CONSTANT {
   $_[0];
}

sub dec_password {
   dec_U64;
}

sub dec_HEX { # for debugging
   "HEX: " . unpack "H*", $data;#d#
}

#############################################################################

sub enc_U8 {
   $data .= pack "C", $_[0];
}

sub enc_U16 {
   $data .= pack "v", $_[0];
}

sub enc_U32 {
   $data .= pack "V", $_[0];
}

sub enc_U64 {
   my $i = new Math::BigInt $_[0];

   enc_U32 $i & 0xffffffff;
   enc_U32 $i >> 32;
}

sub enc_I8 {
   $data .= pack "c", $_[0];
}

sub enc_I16 {
   enc_U16 unpack "S", pack "s", $_[0];
}

sub enc_I32 {
   enc_U32 unpack "I", pack "i", $_[0];
}

sub enc_DATA {
   # a dream!
   $data .= $_[0];
}

sub enc_ZSTRING {
   # should use encode for speed and clarity ;)
   $data .= pack "v*", (map ord, split //, $_[0]), 0;
}

sub enc_STRING {
   # should use encode for speed and clarity ;)
   $data .= pack "v*", map ord, split //, $_[0];
}

sub enc_CONSTANT {
   # nop
}

sub enc_password {
   # $hash must be 64 bit
   my $hash = new Math::BigInt;
   $hash = $hash * 1055 + ord for split //, $_[0];
   enc_U64 $hash & new Math::BigInt "0xffffffffffffffff";
}

sub enc_HEX {
   die "enc_HEX not defined for good";
}

]]>

#############################################################################
# types
<xsl:apply-templates select="descendant::type"/>

#############################################################################
# structures
<xsl:apply-templates select="descendant::struct"/>

#############################################################################
# "less" primitive types<![CDATA[

# this was the most horrible thing to decode. still not everything is decoded correctly(?)
sub dec_TREE {
   my @r;
   my $old_data = $data;#d#
   while (length $data) {
      my $type = dec_U8;
      my $add = $type < 128;

      my $ofs = (length $old_data) - (length $data);#d#

      $type &= 127;

      if ($type == 127) {
         dec_U8; # unused?? *sigh*
         push @r, [add_node => dec_I32];

      } elsif ($type == 126) {
         push @r, [set_node => dec_I32];

      } elsif ($type == 125) {
         push @r, [set_current => dec_I32];

      } elsif ($type == 34) {
         push @r, [score => dec_U8, dec_score32_1000];

      } elsif ($type == 29) {
         push @r, [type_29 => dec_ZSTRING];
         warn "UNKNOWN TREE TYPE 29 $r[-1][1]\007 PLEASE REPORT";#d#
         die;

      } elsif ($type == 28) {
         # move number, only in variations it seems. oh my.
         push @r, [movenum => dec_ZSTRING];

      } elsif ($type == 26) {
         push @r, [type_26 => dec_U8]; # sets a flag (?)
         warn "unknown tree node 26, please ignore\n";
         # possibly marks moves done while editing, as opposed to game-moves(?)

      } elsif ($type == 25) {
         push @r, [result => dec_result];

      } elsif ($type == 23) {
         push @r, [mark => $add, MARK_GRAYED, dec_U8, dec_U8];

      } elsif ($type == 22) {
         push @r, [mark => $add, dec_U8() ? MARK_SMALL_W : MARK_SMALL_B, dec_U8, dec_U8];

      } elsif ($type == 21) {
         push @r, [mark => $add, MARK_SQUARE, dec_U8, dec_U8];

      } elsif ($type == 20) {
         push @r, [mark => $add, MARK_TRIANGLE, dec_U8, dec_U8];

      } elsif ($type == 19) {
         push @r, [mark => $add, MARK_LABEL, dec_U8, dec_U8, dec_ZSTRING];
         #push @r, [unknown_18 => dec_U8, dec_U32, dec_U32, dec_U8, dec_U32, dec_U32, dec_U32];
         #push @r, [set_timer => (dec_U8, dec_U32, dec_time)[0,2,1]];

      } elsif ($type == 18) {
         push @r, [set_timer => (dec_U8, dec_U32, dec_time)[0,2,1]];

      } elsif ($type == 17) {
         push @r, [set_stone => dec_U8, dec_U8, dec_U8];#d#?

#      } elsif ($type == 16) {
#         push @r, [set_stone => dec_U8, dec_U8, dec_U8];#o#

      } elsif ($type == 15) {
         push @r, [mark => $add, MARK_CIRCLE, dec_U8, dec_U8];#d#?

      } elsif ($type == 14) {
         push @r, [move => dec_U8, dec_U8, dec_U8];

      } elsif (($type >= 4 && $type <= 9)
               || ($type >= 11 && $type <= 13)
               || $type == 24) {

         push @r, [({
               4 => "date",
               5 => "unknown_comment5",
               6 => "game_id", #?#
               7 => "unknown_comment7",
               8 => "unknown_comment8",
               9 => "copyright", #?
              11 => "unknown_comment11",
              12 => "unknown_comment12",
              13 => "unknown_comment13",
              24 => "comment",
               })->{$type} => dec_ZSTRING];

      } elsif ($type == 3) {
         push @r, [rank => dec_U8, dec_U32];

      } elsif ($type == 2) {
         push @r, [player => dec_U8, dec_ZSTRING];

      } elsif ($type == 1) {
         push @r, [sgf_name => dec_ZSTRING];

      } elsif ($type == 0) {
         # as usual, wms finds yet another way to duplicate code... oh well, what a mess.
         # (no wonder he is so keen on keeping it a secret...)

         push @r, [rules => dec_rules];

      # OLD

      } else {
         require KGS::Listener::Debug; # hack
         print STDERR KGS::Listener::Debug::dumpval(\@r);
         printf "offset: 0x%04x\n", $ofs;
         open XTYPE, "|xtype"; print XTYPE $old_data; close XTYPE;
         warn "unknown tree type $type, PLEASE REPORT and include the game you wanted to watch. thx.";

      }

      #push @{$r[-1]}, offset => sprintf "0x%x", $ofs;#d#
      
   }
#         print STDERR KGS::Listener::Debug::dumpval(\@r);#d#
#            return [];#d#
   \@r;
}

sub enc_TREE {
   for (@{$_[0]}) {
      my ($type, @arg) = @$_;

      if ($type eq "add_node") {
         enc_U8 127;
         enc_U8 0; # unused?
         enc_I32 $arg[0];

      } elsif ($type eq "set_node") {
         enc_U8 126;
         enc_I32 $arg[0];

      } elsif ($type eq "set_current") {
         enc_U8 125;
         enc_I32 $arg[0];

      } elsif ($type eq "movenum") {
         enc_U8 28;
         enc_ZSTRING $arg[0];

      } elsif ($type eq "set_stone") {
         enc_U8 16;
         enc_U8 $arg[0];
         enc_U8 $arg[1];
         enc_U8 $arg[2];

      } elsif ($type eq "move") {
         enc_U8 14;
         enc_U8 $arg[0];
         enc_U8 $arg[1];
         enc_U8 $arg[2];

      } elsif ($type eq "comment") {
         enc_U8 24;
         enc_ZSTRING $arg[0];

      } elsif ($type eq "mark") {
         my $op = ({
                  &MARK_GRAYED   => 23,
                  &MARK_SMALL_B  => 22,
                  &MARK_SMALL_W  => 22,
                  &MARK_SQUARE   => 21,
                  &MARK_TRIANGLE => 20,
                  &MARK_LABEL    => 19,
                  &MARK_CIRCLE   => 15,
                })->{$arg[1]};

         enc_U8 $op + ($arg[0] ? 0 : 128);
         enc_U8 $arg[1] == MARK_SMALL_W if $op == 22;
         enc_U8 $arg[2];
         enc_U8 $arg[3];

         enc_ZSTRING $arg[4] if $op == 18;

      # unknown types
      } elsif ($type eq "type_29") {
         enc_U8 29;
         enc_ZSTRING $arg[0];
      } elsif ($type eq "type_26") {
         enc_U8 26;
         enc_U8 $arg[0];

      } else {
         warn "unable to encode tree node type $type\n";
      }
   }
};

]]>

#############################################################################
# messages
<xsl:apply-templates select="descendant::message"/>
}

1;
</xsl:template>

<xsl:template match="type[@type = 'S']">
sub dec_<xsl:value-of select="@name"/> {
   my $res = "";
   my @r = unpack "v<xsl:value-of select="@length"/> a*", $data;
   $data = pop @r;
   for (@r) {
      last unless $_;
      $res .= chr $_;
   }
   # dump extra data to file for later analysis
   #my $x = pack "v*", @r; $x =~ s/^(..)*?\x00\x00//s; open DUMP, ">>/root/kgs-dump"; print DUMP $x; close DUMP;#d#
   $res;
}

sub enc_<xsl:value-of select="@name"/> {
   $data .= pack "v<xsl:value-of select="@length"/>", map ord, split //, $_[0];
}
</xsl:template>

<xsl:template match="type[@type = 'A']">
sub dec_<xsl:value-of select="@name"/> {
   (my ($r), $data) = unpack "Z<xsl:value-of select="@length"/> a*", $data; $r;
}

sub enc_<xsl:value-of select="@name"/> {
   $data .= pack "a<xsl:value-of select="@length"/>", $_[0];
}
</xsl:template>

<xsl:template match="type[@multiplier]">
sub dec_<xsl:value-of select="@name"/> {
   (1 / <xsl:value-of select="@multiplier"/>) * dec_<xsl:value-of select="@type"/>;
}

sub enc_<xsl:value-of select="@name"/> {
   enc_<xsl:value-of select="@type"/> $_[0] * <xsl:value-of select="@multiplier"/>;
}
</xsl:template>

<xsl:template match="member[@array = 'yes']" mode="dec">
   $r->{<xsl:value-of select="@name"/>} = (my $array = []);
   while (length $data) {
      push @$array, dec_<xsl:value-of select="@type"/>
                        <xsl:text> </xsl:text>;
   }
</xsl:template>

<xsl:template match="member" mode="dec">
   $r->{<xsl:value-of select="@name"/>} = dec_<xsl:value-of select="@type"/>
                                              <xsl:text> </xsl:text>
                                              <xsl:value-of select="concat('q|',@value,'|')"/>
   <xsl:if test="@guard-cond">
      if ($r->{<xsl:value-of select="@guard-member"/>} <xsl:value-of select="@guard-cond"/>)</xsl:if>
   <xsl:text>;</xsl:text>
</xsl:template>

<xsl:template match="member" mode="enc">
   enc_<xsl:value-of select="@type"/> defined $_[0]{<xsl:value-of select="@name"/>} ? $_[0]{<xsl:value-of select="@name"/>
   <xsl:text>} : (</xsl:text>
   <xsl:value-of select="concat('q|',@value,'|')"/>
   <xsl:text>);</xsl:text>
</xsl:template>

<xsl:template match="struct">
sub dec_<xsl:value-of select="@name"/> {
   my $r = {};
   <xsl:apply-templates select="member" mode="dec"/>
   <xsl:if test="@class">
   bless $r, <xsl:value-of select="@class"/>::;
   </xsl:if>
   $r;
}

sub enc_<xsl:value-of select="@name"/> {
   <xsl:apply-templates select="member" mode="enc"/>
}
</xsl:template>

<xsl:template match="message">
# <xsl:value-of select="@name"/>
$dec_<xsl:value-of select="@src"/>{0x<xsl:value-of select="@type"/>} = sub {
   $data = $_[0];
   my $r = { DATA => $data };
   $r->{type} = "<xsl:value-of select="@name"/>";
   <xsl:apply-templates select="member" mode="dec"/>
   $r->{TRAILING_DATA} = $data if length $data;
   $r;
};
$enc_<xsl:value-of select="@src"/>{<xsl:value-of select="@name"/>} = sub {
   $data = "";
   enc_U16 0x<xsl:value-of select="@type"/>;
   <xsl:apply-templates select="member" mode="enc"/>
   $data;
};
</xsl:template>

<xsl:template match="text()">
</xsl:template>

</xsl:stylesheet>