/*
   rdesktop: A Remote Desktop Protocol client.
   RDP order processing
   Copyright (C) Matthew Chapman 1999-2000

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "rdesktop.h"
#include "orders.h"

extern unsigned char *next_packet;
static RDP_ORDER_STATE order_state;

/* Read field indicating which parameters are present */
static void
rdp_in_present (STREAM s, uint32 * present, uint8 flags, int size)
{
  uint8 bits;
  int i;

  if (flags & RDP_ORDER_SMALL)
    {
      size--;
    }

  if (flags & RDP_ORDER_TINY)
    {
      if (size < 2)
	size = 0;
      else
	size -= 2;
    }

  *present = 0;
  for (i = 0; i < size; i++)
    {
      in_uint8 (s, bits);
      *present |= bits << (i * 8);
    }
}

/* Read a co-ordinate (16-bit, or 8-bit delta) */
static void
rdp_in_coord (STREAM s, uint16 * coord, Bool delta)
{
  signed char change;

  if (delta)
    {
      in_uint8 (s, change);
      *coord += (signed char) change;
    }
  else
    {
      in_uint16_le (s, *coord);
    }
}

/* Read a colour entry */
static void
rdp_in_colour (STREAM s, uint8 * colour)
{
  in_uint8 (s, *colour);
  s->p += 2;
}

/* Parse bounds information */
static Bool
rdp_parse_bounds (STREAM s, BOUNDS * bounds)
{
  uint8 present;

  in_uint8 (s, present);

  if (present & 1)
    rdp_in_coord (s, &bounds->left, False);
  else if (present & 16)
    rdp_in_coord (s, &bounds->left, True);

  if (present & 2)
    rdp_in_coord (s, &bounds->top, False);
  else if (present & 32)
    rdp_in_coord (s, &bounds->top, True);

  if (present & 4)
    rdp_in_coord (s, &bounds->right, False);
  else if (present & 64)
    rdp_in_coord (s, &bounds->right, True);

  if (present & 8)
    rdp_in_coord (s, &bounds->bottom, False);
  else if (present & 128)
    rdp_in_coord (s, &bounds->bottom, True);

  return s_check (s);
}

/* Parse a pen */
static Bool
rdp_parse_pen (STREAM s, PEN * pen, uint32 present)
{
  if (present & 1)
    in_uint8 (s, pen->style);

  if (present & 2)
    in_uint8 (s, pen->width);

  if (present & 4)
    rdp_in_colour (s, &pen->colour);

  return s_check (s);
}

/* Parse a brush */
static Bool
rdp_parse_brush (STREAM s, BRUSH * brush, uint32 present)
{
  if (present & 1)
    in_uint8 (s, brush->xorigin);

  if (present & 2)
    in_uint8 (s, brush->yorigin);

  if (present & 4)
    in_uint8 (s, brush->style);

  if (present & 8)
    in_uint8 (s, brush->pattern[0]);

  if (present & 16)
    in_uint8a (s, &brush->pattern[1], 7);

  return s_check (s);
}

/* Process a destination blt order */
static void
process_destblt (STREAM s, DESTBLT_ORDER * os, uint32 present, Bool delta)
{
  if (present & 0x01)
    rdp_in_coord (s, &os->x, delta);

  if (present & 0x02)
    rdp_in_coord (s, &os->y, delta);

  if (present & 0x04)
    rdp_in_coord (s, &os->cx, delta);

  if (present & 0x08)
    rdp_in_coord (s, &os->cy, delta);

  if (present & 0x10)
    in_uint8 (s, os->opcode);

  DEBUG ("DESTBLT(op=0x%x,x=%d,y=%d,cx=%d,cy=%d)\n",
	 os->opcode, os->x, os->y, os->cx, os->cy);

  ui_destblt (ROP2_S (os->opcode), os->x, os->y, os->cx, os->cy);
}

/* Process a pattern blt order */
static void
process_patblt (STREAM s, PATBLT_ORDER * os, uint32 present, Bool delta)
{
  if (present & 0x0001)
    rdp_in_coord (s, &os->x, delta);

  if (present & 0x0002)
    rdp_in_coord (s, &os->y, delta);

  if (present & 0x0004)
    rdp_in_coord (s, &os->cx, delta);

  if (present & 0x0008)
    rdp_in_coord (s, &os->cy, delta);

  if (present & 0x0010)
    in_uint8 (s, os->opcode);

  if (present & 0x0020)
    rdp_in_colour (s, &os->bgcolour);

  if (present & 0x0040)
    rdp_in_colour (s, &os->fgcolour);

  rdp_parse_brush (s, &os->brush, present >> 7);

  DEBUG ("PATBLT(op=0x%x,x=%d,y=%d,cx=%d,cy=%d,bs=%d,bg=0x%x,fg=0x%x)\n",
	 os->opcode, os->x, os->y, os->cx, os->cy,
	 os->brush.style, os->bgcolour, os->fgcolour);

  ui_patblt (ROP2_P (os->opcode), os->x, os->y, os->cx, os->cy,
	     &os->brush, os->bgcolour, os->fgcolour);
}

/* Process a screen blt order */
static void
process_screenblt (STREAM s, SCREENBLT_ORDER * os, uint32 present, Bool delta)
{
  if (present & 0x0001)
    rdp_in_coord (s, &os->x, delta);

  if (present & 0x0002)
    rdp_in_coord (s, &os->y, delta);

  if (present & 0x0004)
    rdp_in_coord (s, &os->cx, delta);

  if (present & 0x0008)
    rdp_in_coord (s, &os->cy, delta);

  if (present & 0x0010)
    in_uint8 (s, os->opcode);

  if (present & 0x0020)
    rdp_in_coord (s, &os->srcx, delta);

  if (present & 0x0040)
    rdp_in_coord (s, &os->srcy, delta);

  DEBUG ("SCREENBLT(op=0x%x,x=%d,y=%d,cx=%d,cy=%d,srcx=%d,srcy=%d)\n",
	 os->opcode, os->x, os->y, os->cx, os->cy, os->srcx, os->srcy);

  ui_screenblt (ROP2_S (os->opcode), os->x, os->y, os->cx, os->cy,
		os->srcx, os->srcy);
}

/* Process a line order */
static void
process_line (STREAM s, LINE_ORDER * os, uint32 present, Bool delta)
{
  if (present & 0x0001)
    in_uint16_le (s, os->mixmode);

  if (present & 0x0002)
    rdp_in_coord (s, &os->startx, delta);

  if (present & 0x0004)
    rdp_in_coord (s, &os->starty, delta);

  if (present & 0x0008)
    rdp_in_coord (s, &os->endx, delta);

  if (present & 0x0010)
    rdp_in_coord (s, &os->endy, delta);

  if (present & 0x0020)
    rdp_in_colour (s, &os->bgcolour);

  if (present & 0x0040)
    in_uint8 (s, os->opcode);

  rdp_parse_pen (s, &os->pen, present >> 7);

  DEBUG ("LINE(op=0x%x,sx=%d,sy=%d,dx=%d,dx=%d,fg=0x%x)\n",
	 os->opcode, os->startx, os->starty, os->endx, os->endy,
	 os->pen.colour);

  if (os->opcode < 0x01 || os->opcode > 0x10)
    {
      ERROR ("bad ROP2 0x%x\n", os->opcode);
      return;
    }

  ui_line (os->opcode - 1, os->startx, os->starty,
	   os->endx, os->endy, &os->pen);
}

/* Process an opaque rectangle order */
static void
process_rect (STREAM s, RECT_ORDER * os, uint32 present, Bool delta)
{
  if (present & 0x01)
    rdp_in_coord (s, &os->x, delta);

  if (present & 0x02)
    rdp_in_coord (s, &os->y, delta);

  if (present & 0x04)
    rdp_in_coord (s, &os->cx, delta);

  if (present & 0x08)
    rdp_in_coord (s, &os->cy, delta);

  if (present & 0x10)
    in_uint8 (s, os->colour);

  DEBUG ("RECT(x=%d,y=%d,cx=%d,cy=%d,fg=0x%x)\n",
	 os->x, os->y, os->cx, os->cy, os->colour);

  ui_rect (os->x, os->y, os->cx, os->cy, os->colour);
}

/* Process a desktop save order */
static void
process_desksave (STREAM s, DESKSAVE_ORDER * os, uint32 present, Bool delta)
{
  int width, height;

  if (present & 0x01)
    in_uint32_le (s, os->offset);

  if (present & 0x02)
    rdp_in_coord (s, &os->left, delta);

  if (present & 0x04)
    rdp_in_coord (s, &os->top, delta);

  if (present & 0x08)
    rdp_in_coord (s, &os->right, delta);

  if (present & 0x10)
    rdp_in_coord (s, &os->bottom, delta);

  if (present & 0x20)
    in_uint8 (s, os->action);

  DEBUG ("DESKSAVE(l=%d,t=%d,r=%d,b=%d,off=%d,op=%d)\n",
	 os->left, os->top, os->right, os->bottom, os->offset, os->action);

  width = os->right - os->left + 1;
  height = os->bottom - os->top + 1;

  if (os->action == 0)
    ui_desktop_save (os->offset, os->left, os->top, width, height);
  else
    ui_desktop_restore (os->offset, os->left, os->top, width, height);
}

/* Process a memory blt order */
static void
process_memblt (STREAM s, MEMBLT_ORDER * os, uint32 present, Bool delta)
{
  HBITMAP bitmap;

  if (present & 0x0001)
    {
      in_uint8 (s, os->cache_id);
      in_uint8 (s, os->colour_table);
    }

  if (present & 0x0002)
    rdp_in_coord (s, &os->x, delta);

  if (present & 0x0004)
    rdp_in_coord (s, &os->y, delta);

  if (present & 0x0008)
    rdp_in_coord (s, &os->cx, delta);

  if (present & 0x0010)
    rdp_in_coord (s, &os->cy, delta);

  if (present & 0x0020)
    in_uint8 (s, os->opcode);

  if (present & 0x0040)
    rdp_in_coord (s, &os->srcx, delta);

  if (present & 0x0080)
    rdp_in_coord (s, &os->srcy, delta);

  if (present & 0x0100)
    in_uint16_le (s, os->cache_idx);

  DEBUG ("MEMBLT(op=0x%x,x=%d,y=%d,cx=%d,cy=%d,id=%d,idx=%d)\n",
	 os->opcode, os->x, os->y, os->cx, os->cy, os->cache_id,
	 os->cache_idx);

  bitmap = cache_get_bitmap (os->cache_id, os->cache_idx);
  if (bitmap == NULL)
    return;

  ui_memblt (ROP2_S (os->opcode), os->x, os->y, os->cx, os->cy,
	     bitmap, os->srcx, os->srcy);
}

/* Process a 3-way blt order */
static void
process_triblt (STREAM s, TRIBLT_ORDER * os, uint32 present, Bool delta)
{
  HBITMAP bitmap;

  if (present & 0x000001)
    {
      in_uint8 (s, os->cache_id);
      in_uint8 (s, os->colour_table);
    }

  if (present & 0x000002)
    rdp_in_coord (s, &os->x, delta);

  if (present & 0x000004)
    rdp_in_coord (s, &os->y, delta);

  if (present & 0x000008)
    rdp_in_coord (s, &os->cx, delta);

  if (present & 0x000010)
    rdp_in_coord (s, &os->cy, delta);

  if (present & 0x000020)
    in_uint8 (s, os->opcode);

  if (present & 0x000040)
    rdp_in_coord (s, &os->srcx, delta);

  if (present & 0x000080)
    rdp_in_coord (s, &os->srcy, delta);

  if (present & 0x000100)
    rdp_in_colour (s, &os->bgcolour);

  if (present & 0x000200)
    rdp_in_colour (s, &os->fgcolour);

  rdp_parse_brush (s, &os->brush, present >> 10);

  if (present & 0x008000)
    in_uint16_le (s, os->cache_idx);

  if (present & 0x010000)
    in_uint16_le (s, os->unknown);

  DEBUG
    ("TRIBLT(op=0x%x,x=%d,y=%d,cx=%d,cy=%d,id=%d,idx=%d,bs=%d,bg=0x%x,fg=0x%x)\n",
     os->opcode, os->x, os->y, os->cx, os->cy, os->cache_id, os->cache_idx,
     os->brush.style, os->bgcolour, os->fgcolour);

  bitmap = cache_get_bitmap (os->cache_id, os->cache_idx);
  if (bitmap == NULL)
    return;

  ui_triblt (os->opcode, os->x, os->y, os->cx, os->cy,
	     bitmap, os->srcx, os->srcy,
	     &os->brush, os->bgcolour, os->fgcolour);
}

/* Parse a delta co-ordinate in polyline order form */
inline static int
parse_delta (uint8 * buffer, int *offset)
{
  int value = buffer[(*offset)++];
  int two_byte = value & 0x80;

  if (value & 0x40)		/* sign bit */
    value |= ~0x3f;
  else
    value &= 0x3f;

  if (two_byte)
    value = (value << 8) | buffer[(*offset)++];

  return value;
}

/* Process a polyline order */
static void
process_polyline (STREAM s, POLYLINE_ORDER * os, uint32 present, Bool delta)
{
  int index, line, data;
  int x, y, xfrom, yfrom;
  uint8 flags = 0;
  PEN pen;

  if (present & 0x01)
    rdp_in_coord (s, &os->x, delta);

  if (present & 0x02)
    rdp_in_coord (s, &os->y, delta);

  if (present & 0x04)
    in_uint8 (s, os->flags);

  if (present & 0x10)
    rdp_in_colour (s, &os->fgcolour);

  if (present & 0x20)
    in_uint8 (s, os->lines);

  if (present & 0x40)
    {
      in_uint8 (s, os->datasize);
      in_uint8a (s, os->data, os->datasize);
    }

  DEBUG ("POLYLINE(x=%d,y=%d,fl=0x%x,fg=0x%x,n=%d,sz=%d)\n",
	 os->x, os->y, os->flags, os->fgcolour, os->lines, os->datasize);

  DEBUG ("Data: ");

  for (index = 0; index < os->datasize; index++)
    DEBUG ("%02x ", os->data[index]);

  DEBUG ("\n");

  x = os->x;
  y = os->y;
  pen.style = pen.width = 0;
  pen.colour = os->fgcolour;

  index = 0;
  data = ((os->lines - 1) / 4) + 1;
  for (line = 0; (line < os->lines) && (data < os->datasize); line++)
    {
      xfrom = x;
      yfrom = y;

      if (line % 4 == 0)
	flags = os->data[index++];

      if ((flags & 0xc0) == 0)
	flags |= 0xc0;		/* none = both */

      if (flags & 0x40)
	x += parse_delta (os->data, &data);

      if (flags & 0x80)
	y += parse_delta (os->data, &data);

      /*ui_line (ROP2_COPY, xfrom, yfrom, x, y, &pen);*/
      ui_line (ROP2_NXOR, xfrom, yfrom, x, y, &pen);

      flags <<= 2;
    }
}

/* Process a text order */
static void
process_text2 (STREAM s, TEXT2_ORDER * os, uint32 present, Bool delta)
{
  int i;

  if (present & 0x000001)
    in_uint8 (s, os->font);

  if (present & 0x000002)
    in_uint8 (s, os->flags);

  if (present & 0x000004)
    in_uint8 (s, os->unknown);

  if (present & 0x000008)
    in_uint8 (s, os->mixmode);

  if (present & 0x000010)
    rdp_in_colour (s, &os->fgcolour);

  if (present & 0x000020)
    rdp_in_colour (s, &os->bgcolour);

  if (present & 0x000040)
    in_uint16_le (s, os->clipleft);

  if (present & 0x000080)
    in_uint16_le (s, os->cliptop);

  if (present & 0x000100)
    in_uint16_le (s, os->clipright);

  if (present & 0x000200)
    in_uint16_le (s, os->clipbottom);

  if (present & 0x000400)
    in_uint16_le (s, os->boxleft);

  if (present & 0x000800)
    in_uint16_le (s, os->boxtop);

  if (present & 0x001000)
    in_uint16_le (s, os->boxright);

  if (present & 0x002000)
    in_uint16_le (s, os->boxbottom);

  if (present & 0x080000)
    in_uint16_le (s, os->x);

  if (present & 0x100000)
    in_uint16_le (s, os->y);

  if (present & 0x200000)
    {
      in_uint8 (s, os->length);
      in_uint8a (s, os->text, os->length);
    }

  DEBUG
    ("TEXT2(x=%d,y=%d,cl=%d,ct=%d,cr=%d,cb=%d,bl=%d,bt=%d,bb=%d,br=%d,fg=0x%x,bg=0x%x,font=%d,fl=0x%x,mix=%d,unk=0x%x,n=%d)\n",
     os->x, os->y, os->clipleft, os->cliptop, os->clipright, os->clipbottom,
     os->boxleft, os->boxtop, os->boxright, os->boxbottom, os->fgcolour,
     os->bgcolour, os->font, os->flags, os->mixmode, os->unknown, os->length);

  DEBUG ("Text: ");

  for (i = 0; i < os->length; i++)
    DEBUG ("%02x ", os->text[i]);

  DEBUG ("\n");

  ui_draw_text (os->font, os->flags, os->mixmode, os->x, os->y,
		os->clipleft, os->cliptop,
		os->clipright - os->clipleft,
		os->clipbottom - os->cliptop,
		os->boxleft, os->boxtop,
		os->boxright - os->boxleft,
		os->boxbottom - os->boxtop,
		os->bgcolour, os->fgcolour, os->text, os->length);
}

/* Process a raw bitmap cache order */
static void
process_raw_bmpcache (STREAM s)
{
  HBITMAP bitmap;
  uint16 cache_idx, bufsize;
  uint8 cache_id, width, height, bpp;
  uint8 *data, *d2;
  int y;

  in_uint8 (s, cache_id);
  in_uint8s (s, 1);		/* pad */
  in_uint8 (s, width);
  in_uint8 (s, height);
  in_uint8 (s, bpp);
  in_uint16_le (s, bufsize);
  in_uint16_le (s, cache_idx);
  in_uint8p (s, data, bufsize);

  DEBUG ("RAW_BMPCACHE(cx=%d,cy=%d,id=%d,idx=%d)\n",
	 width, height, cache_id, cache_idx);
  d2 = xmalloc (width * height);
  for (y = 0; y < height; y++)
    {
      memcpy (&d2[(height - y - 1) * width], &data[y * width], width);
    }

  bitmap = ui_create_bitmap (width, height, d2);
  xfree (d2);
  cache_put_bitmap (cache_id, cache_idx, bitmap);
}

/* Process a bitmap cache order */
static void
process_bmpcache (STREAM s)
{
  HBITMAP bitmap;
  uint16 cache_idx, size;
  uint8 cache_id, width, height, bpp;
  uint8 *data, *bmpdata;

  in_uint8 (s, cache_id);
  in_uint8s (s, 1);		/* pad */
  in_uint8 (s, width);
  in_uint8 (s, height);
  in_uint8 (s, bpp);
  in_uint8s (s, 2);		/* bufsize */
  in_uint16_le (s, cache_idx);
  in_uint8s (s, 2);		/* pad */
  in_uint16_le (s, size);
  in_uint8s (s, 4);		/* row_size, final_size */
  in_uint8p (s, data, size);

  DEBUG ("BMPCACHE(cx=%d,cy=%d,id=%d,idx=%d)\n",
	 width, height, cache_id, cache_idx);

  bmpdata = xmalloc (width * height);

  if (bitmap_decompress (bmpdata, width, height, data, size))
    {
      bitmap = ui_create_bitmap (width, height, bmpdata);
      cache_put_bitmap (cache_id, cache_idx, bitmap);
    }

  xfree (bmpdata);
}

/* Process a colourmap cache order */
static void
process_colcache (STREAM s)
{
  COLOURENTRY *entry;
  COLOURMAP map;
  HCOLOURMAP hmap;
  uint8 cache_id;
  int i;

  in_uint8 (s, cache_id);
  in_uint16_le (s, map.ncolours);

  map.colours = xmalloc (3 * map.ncolours);

  for (i = 0; i < map.ncolours; i++)
    {
      entry = &map.colours[i];
      in_uint8 (s, entry->blue);
      in_uint8 (s, entry->green);
      in_uint8 (s, entry->red);
      in_uint8s (s, 1);		/* pad */
    }

  DEBUG ("COLCACHE(id=%d,n=%d)\n", cache_id, map.ncolours);

  hmap = ui_create_colourmap (&map);
  ui_set_colourmap (hmap);
  ui_destroy_colourmap (hmap);

  xfree (map.colours);
}


/* unroll defines, used to make the loops a bit more readable... */
#define unroll8Expr(uexp) uexp uexp uexp uexp uexp uexp uexp uexp
#define unroll8Lefts(uexp) case 7: uexp \
	case 6: uexp \
	case 5: uexp \
	case 4: uexp \
	case 3: uexp \
	case 2: uexp \
	case 1: uexp


inline static unsigned char rotate( unsigned char i ){
    i =  (i << 4)         |  (i >> 4);
    i = ((i << 2) & 0xCC) | ((i >> 2) & 0x33);
    i = ((i << 1) & 0xAA) | ((i >> 1) & 0x55);
    return(i);
}


/* Process a font cache order */
static void
process_fontcache (STREAM s)
{
  HGLYPH bitmap;
  uint8 font, nglyphs;
  uint16 character, offset, baseline, width, height;
  uint8 *data, *rev_data, in;/*, out;*/
  int i, j, m, datasize;

  in_uint8 (s, font);
  in_uint8 (s, nglyphs);

  DEBUG ("FONTCACHE(font=%d,n=%d)\n", font, nglyphs);

  for (i = 0; i < nglyphs; i++)
    {
      in_uint16_le (s, character);
      in_uint16_le (s, offset);
      in_uint16_le (s, baseline);
      in_uint16_le (s, width);
      in_uint16_le (s, height);

      datasize = (height * ((width + 7) / 8) + 3) & ~3;
      in_uint8p (s, data, datasize);

#ifdef L_ENDIAN
      j = ( datasize & ~0x7 );

      /* Need to reverse bit order */
      rev_data = xmalloc (datasize);

      /* XXX, investigate if it's possible to get rid of "m"..  */
      m = 0;

      while (j)
	{
         unroll8Expr(
//	        in = data[m];
                rev_data[m++] = rotate( data[m] );
/*                     ((in & 0x1) << 7) | ((in & 0x2) << 5) | ((in & 0x4) << 3) |
	             ((in & 0x8) << 1) | ((in & 0x10) >> 1) | ((in & 0x20) >> 3) |
	             ((in & 0x40) >> 5) | ((in & 0x80) >> 7);
*/
/*
                out = 0;
                if (in & 1) out |= 128;
                if (in & 2) out |= 64;
	        if (in & 4) out |= 32;
	        if (in & 8) out |= 16;
	        if (in & 16) out |= 8;
	        if (in & 32) out |= 4;
	        if (in & 64) out |= 2;
	        if (in & 128) out |= 1;
                rev_data[m++] = out;
*/
                j--;
         )
	}
        j = ( datasize & 0x7 );
        if( j )
        switch(j){
         unroll8Lefts(
//        	in = data[m];
                rev_data[m++] = rotate( data[m] );
/*                     ((in & 0x1) << 7) | ((in & 0x2) << 5) | ((in & 0x4) << 3) |
	             ((in & 0x8) << 1) | ((in & 0x10) >> 1) | ((in & 0x20) >> 3) |
	             ((in & 0x40) >> 5) | ((in & 0x80) >> 7);
*/
/*                out = 0;
                if (in & 1) out |= 128;
                if (in & 2) out |= 64;
	        if (in & 4) out |= 32;
	        if (in & 8) out |= 16;
	        if (in & 16) out |= 8;
	        if (in & 32) out |= 4;
	        if (in & 64) out |= 2;
	        if (in & 128) out |= 1;
                rev_data[m++] = out;
*/
                j--;
         )
        }

      bitmap = ui_create_glyph (width, height, rev_data);
      xfree (rev_data);

#else /* BIGENDIAN */
      bitmap = ui_create_glyph (width, height, data);
#endif
      cache_put_font (font, character, offset, baseline, width, height,
		      bitmap);
    }

}


/* Process a secondary order */
static void
process_secondary_order (STREAM s)
{
  uint16 length;
  uint8 type;
  uint8 *next_order;

  in_uint16_le (s, length);
  in_uint8s (s, 2);		/* flags */
  in_uint8 (s, type);

  next_order = s->p + length + 7;

  switch (type)
    {
    case RDP_ORDER_RAW_BMPCACHE:
      process_raw_bmpcache (s);
      break;

    case RDP_ORDER_COLCACHE:
      process_colcache (s);
      break;

    case RDP_ORDER_BMPCACHE:
      process_bmpcache (s);
      break;

    case RDP_ORDER_FONTCACHE:
      process_fontcache (s);
      break;

    default:
      NOTIMP ("secondary order %d\n", type);
    }

  s->p = next_order;
}

/* Process an order PDU */
void
process_orders (STREAM s)
{
  RDP_ORDER_STATE *os = &order_state;
  uint32 present;
  uint16 num_orders;
  uint8 order_flags;
  int size, processed = 0;
  Bool delta;

  in_uint8s (s, 2);		/* pad */
  in_uint16_le (s, num_orders);
  in_uint8s (s, 2);		/* pad */

  while (processed < num_orders)
    {
      in_uint8 (s, order_flags);

      if (!(order_flags & RDP_ORDER_STANDARD))
	{
	  ERROR ("order parsing failed\n");
	  break;
	}

      if (order_flags & RDP_ORDER_SECONDARY)
	{
	  process_secondary_order (s);
	}
      else
	{
	  if (order_flags & RDP_ORDER_CHANGE)
	    {
	      in_uint8 (s, os->order_type);
	    }

	  switch (os->order_type)
	    {
	    case RDP_ORDER_TRIBLT:
	    case RDP_ORDER_TEXT2:
	      size = 3;
	      break;

	    case RDP_ORDER_PATBLT:
	    case RDP_ORDER_MEMBLT:
	    case RDP_ORDER_LINE:
	      size = 2;
	      break;

	    default:
	      size = 1;
	    }

	  rdp_in_present (s, &present, order_flags, size);

	  if (order_flags & RDP_ORDER_BOUNDS)
	    {
	      if (!(order_flags & RDP_ORDER_LASTBOUNDS))
		rdp_parse_bounds (s, &os->bounds);

	      ui_set_clip (os->bounds.left,
			   os->bounds.top,
			   os->bounds.right - os->bounds.left + 1,
			   os->bounds.bottom - os->bounds.top + 1);
	    }

	  delta = order_flags & RDP_ORDER_DELTA;

	  switch (os->order_type)
	    {
	    case RDP_ORDER_DESTBLT:
	      process_destblt (s, &os->destblt, present, delta);
	      break;

	    case RDP_ORDER_PATBLT:
	      process_patblt (s, &os->patblt, present, delta);
	      break;

	    case RDP_ORDER_SCREENBLT:
	      process_screenblt (s, &os->screenblt, present, delta);
	      break;

	    case RDP_ORDER_LINE:
	      process_line (s, &os->line, present, delta);
	      break;

	    case RDP_ORDER_RECT:
	      process_rect (s, &os->rect, present, delta);
	      break;

	    case RDP_ORDER_DESKSAVE:
	      process_desksave (s, &os->desksave, present, delta);
	      break;

	    case RDP_ORDER_MEMBLT:
	      process_memblt (s, &os->memblt, present, delta);
	      break;

	    case RDP_ORDER_TRIBLT:
	      process_triblt (s, &os->triblt, present, delta);
	      break;

	    case RDP_ORDER_POLYLINE:
	      process_polyline (s, &os->polyline, present, delta);
	      break;

	    case RDP_ORDER_TEXT2:
	      process_text2 (s, &os->text2, present, delta);
	      break;

	    default:
	      NOTIMP ("order %d\n", os->order_type);
	      return;
	    }

	  if (order_flags & RDP_ORDER_BOUNDS)
	    ui_reset_clip ();
	}

      processed++;
    }

  if (s->p != next_packet)
    WARN ("%d bytes remaining\n", (int) (next_packet - s->p));
}

/* Reset order state */
void
reset_order_state ()
{
  memset (&order_state, 0, sizeof (order_state));
  /* Must set initial order (fix for coloured backgrounds on logon screen) */
  order_state.order_type=RDP_ORDER_PATBLT;
}



