/**
 * xrdp: A Remote Desktop Protocol server.
 *
 * Copyright (C) Jay Sorg 2004-2013
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ibrdp orders
 */

#include "rdp.h"

#ifndef NULL
#define NULL 0
#endif

/*****************************************************************************/
struct rdp_orders *APP_CC
rdp_orders_create(struct rdp_rdp *owner)
{
    struct rdp_orders *self = (struct rdp_orders *)NULL;

    self = (struct rdp_orders *)g_malloc(sizeof(struct rdp_orders), 1);
    self->rdp_layer = owner;
    return self;
}

/*****************************************************************************/
void APP_CC
rdp_orders_delete(struct rdp_orders *self)
{
    int i = 0;
    int j = 0;

    if (self == 0)
    {
        return;
    }

    /* free the colormap cache */
    for (i = 0; i < 6; i++)
    {
        g_free(self->cache_colormap[i]);
    }

    /* free the bitmap cache */
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 600; j++)
        {
            if (self->cache_bitmap[i][j] != 0)
            {
                g_free(self->cache_bitmap[i][j]->data);
            }

            g_free(self->cache_bitmap[i][j]);
        }
    }

    g_free(self);
}

/*****************************************************************************/
void APP_CC
rdp_orders_reset_state(struct rdp_orders *self)
{
    g_memset(&self->state, 0, sizeof(self->state));
}

/*****************************************************************************/
/* Read field indicating which parameters are present */
static void APP_CC
rdp_orders_in_present(struct stream *s, int *present,
                      int flags, int size)
{
    int bits = 0;
    int i = 0;

    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 APP_CC
rdp_orders_in_coord(struct stream *s, int *coord, int delta)
{
    int change = 0;

    if (delta)
    {
        in_sint8(s, change);
        *coord += change;
    }
    else
    {
        in_sint16_le(s, *coord);
    }
}

/*****************************************************************************/
/* Parse bounds information */
static void APP_CC
rdp_orders_parse_bounds(struct rdp_orders *self, struct stream *s)
{
    int present = 0;

    in_uint8(s, present);

    if (present & 1)
    {
        rdp_orders_in_coord(s, &self->state.clip_left, 0);
    }
    else if (present & 16)
    {
        rdp_orders_in_coord(s, &self->state.clip_left, 1);
    }

    if (present & 2)
    {
        rdp_orders_in_coord(s, &self->state.clip_top, 0);
    }
    else if (present & 32)
    {
        rdp_orders_in_coord(s, &self->state.clip_top, 1);
    }

    if (present & 4)
    {
        rdp_orders_in_coord(s, &self->state.clip_right, 0);
    }
    else if (present & 64)
    {
        rdp_orders_in_coord(s, &self->state.clip_right, 1);
    }

    if (present & 8)
    {
        rdp_orders_in_coord(s, &self->state.clip_bottom, 0);
    }
    else if (present & 128)
    {
        rdp_orders_in_coord(s, &self->state.clip_bottom, 1);
    }
}

/*****************************************************************************/
/* Process a colormap cache order */
static void APP_CC
rdp_orders_process_colcache(struct rdp_orders *self, struct stream *s,
                            int flags)
{
    struct rdp_colormap *colormap = (struct rdp_colormap *)NULL;
    struct stream *rec_s = (struct stream *)NULL;
    int cache_id = 0;
    int i = 0;

    colormap = (struct rdp_colormap *)g_malloc(sizeof(struct rdp_colormap), 1);
    in_uint8(s, cache_id);
    in_uint16_le(s, colormap->ncolors);

    for (i = 0; i < colormap->ncolors; i++)
    {
        in_uint32_le(s, colormap->colors[i]);
    }

    g_free(self->cache_colormap[cache_id]);
    self->cache_colormap[cache_id] = colormap;

    if (self->rdp_layer->rec_mode)
    {
        rdp_rec_check_file(self->rdp_layer);
        make_stream(rec_s);
        init_stream(rec_s, 4096);
        s_push_layer(rec_s, iso_hdr, 4);
        out_uint8(rec_s, 10);
        out_uint8(rec_s, cache_id);

        for (i = 0; i < 256; i++)
        {
            out_uint32_le(rec_s, colormap->colors[i]);
        }

        rdp_rec_write_item(self->rdp_layer, rec_s);
        free_stream(rec_s);
    }
}

/*****************************************************************************/
/* Process a raw bitmap cache order */
static void APP_CC
rdp_orders_process_raw_bmpcache(struct rdp_orders *self, struct stream *s,
                                int flags)
{
    int cache_idx = 0;
    int cache_id = 0;
    int width = 0;
    int height = 0;
    int bpp = 0;
    int Bpp = 0;
    int x = 0;
    int y = 0;
    char *inverted = (char *)NULL;
    char *dst = (char *)NULL;
    struct rdp_bitmap *bitmap = (struct rdp_bitmap *)NULL;
    struct stream *rec_s = (struct stream *)NULL;

    in_uint8(s, cache_id);
    in_uint8s(s, 1);
    in_uint8(s, width);
    in_uint8(s, height);
    in_uint8(s, bpp);
    Bpp = (bpp + 7) / 8;
    in_uint8s(s, 2); /* bufsize */
    in_uint16_le(s, cache_idx);
    inverted = (char *)g_malloc(width * height * Bpp, 0);

    for (y = 0; y < height; y++)
    {
        dst = inverted + (((height - y) - 1) * (width * Bpp));

        if (Bpp == 1)
        {
            for (x = 0; x < width; x++)
            {
                in_uint8(s, dst[x]);
            }
        }
        else if (Bpp == 2)
        {
            for (x = 0; x < width; x++)
            {
                in_uint16_le(s, ((tui16 *)dst)[x]);
            }
        }
        else if (Bpp == 3)
        {
            for (x = 0; x < width; x++)
            {
                in_uint8(s, dst[x * 3 + 0]);
                in_uint8(s, dst[x * 3 + 1]);
                in_uint8(s, dst[x * 3 + 2]);
            }
        }
    }

    bitmap = (struct rdp_bitmap *)g_malloc(sizeof(struct rdp_bitmap), 0);
    bitmap->width = width;
    bitmap->height = height;
    bitmap->bpp = bpp;
    bitmap->data = inverted;

    if (self->cache_bitmap[cache_id][cache_idx] != 0)
    {
        g_free(self->cache_bitmap[cache_id][cache_idx]->data);
    }

    g_free(self->cache_bitmap[cache_id][cache_idx]);
    self->cache_bitmap[cache_id][cache_idx] = bitmap;

    if (self->rdp_layer->rec_mode)
    {
        y = width * height * Bpp;
        rdp_rec_check_file(self->rdp_layer);
        make_stream(rec_s);
        init_stream(rec_s, y + 256);
        s_push_layer(rec_s, iso_hdr, 4);
        out_uint8(rec_s, 8);
        out_uint8(rec_s, cache_id);
        out_uint16_le(rec_s, cache_idx);
        out_uint16_le(rec_s, width);
        out_uint16_le(rec_s, height);
        out_uint16_le(rec_s, y);
        out_uint8a(rec_s, inverted, y);
        rdp_rec_write_item(self->rdp_layer, rec_s);
        free_stream(rec_s);
    }
}

/*****************************************************************************/
/* Process a bitmap cache order */
static void APP_CC
rdp_orders_process_bmpcache(struct rdp_orders *self, struct stream *s,
                            int flags)
{
    char *data = (char *)NULL;
    char *bmpdata = (char *)NULL;
    int cache_idx = 0;
    int size = 0;
    int cache_id = 0;
    int width = 0;
    int height = 0;
    int bpp = 0;
    int Bpp = 0;
    int bufsize = 0;
    struct rdp_bitmap *bitmap = (struct rdp_bitmap *)NULL;
    struct stream *rec_s = (struct stream *)NULL;

    in_uint8(s, cache_id);
    in_uint8s(s, 1); /* pad */
    in_uint8(s, width);
    in_uint8(s, height);
    in_uint8(s, bpp);
    Bpp = (bpp + 7) / 8;
    in_uint16_le(s, bufsize);
    in_uint16_le(s, cache_idx);

    if (flags & 1024)
    {
        size = bufsize;
    }
    else
    {
        in_uint8s(s, 2); /* pad */
        in_uint16_le(s, size);
        in_uint8s(s, 2); /* row_size */
        in_uint8s(s, 2); /* final_size */
    }

    in_uint8p(s, data, size);
    bmpdata = (char *)g_malloc(width * height * Bpp, 0);

    if (rdp_bitmap_decompress(bmpdata, width, height, data, size, Bpp))
    {
    }
    else
    {
        /* error */
    }

    bitmap = (struct rdp_bitmap *)g_malloc(sizeof(struct rdp_bitmap), 0);
    bitmap->width = width;
    bitmap->height = height;
    bitmap->bpp = bpp;
    bitmap->data = bmpdata;

    if (self->cache_bitmap[cache_id][cache_idx] != 0)
    {
        g_free(self->cache_bitmap[cache_id][cache_idx]->data);
    }

    g_free(self->cache_bitmap[cache_id][cache_idx]);
    self->cache_bitmap[cache_id][cache_idx] = bitmap;

    if (self->rdp_layer->rec_mode)
    {
        size = width * height * Bpp;
        rdp_rec_check_file(self->rdp_layer);
        make_stream(rec_s);
        init_stream(rec_s, size + 256);
        s_push_layer(rec_s, iso_hdr, 4);
        out_uint8(rec_s, 8);
        out_uint8(rec_s, cache_id);
        out_uint16_le(rec_s, cache_idx);
        out_uint16_le(rec_s, width);
        out_uint16_le(rec_s, height);
        out_uint16_le(rec_s, size);
        out_uint8a(rec_s, bmpdata, size);
        rdp_rec_write_item(self->rdp_layer, rec_s);
        free_stream(rec_s);
    }
}

/*****************************************************************************/
/* Process a font cache order */
static void APP_CC
rdp_orders_process_fontcache(struct rdp_orders *self, struct stream *s,
                             int flags)
{
    struct stream *rec_s = (struct stream *)NULL;
    int font = 0;
    int nglyphs = 0;
    int character = 0;
    int offset = 0;
    int baseline = 0;
    int width = 0;
    int height = 0;
    int i = 0;
    int datasize = 0;
    char *data = (char *)NULL;

    in_uint8(s, font);
    in_uint8(s, 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);
        self->rdp_layer->mod->server_add_char(self->rdp_layer->mod, font,
                                              character, offset, baseline,
                                              width, height, data);

        if (self->rdp_layer->rec_mode)
        {
            rdp_rec_check_file(self->rdp_layer);
            make_stream(rec_s);
            init_stream(rec_s, datasize + 256);
            s_push_layer(rec_s, iso_hdr, 4);
            out_uint8(rec_s, 9);
            out_uint8(rec_s, font);
            out_uint16_le(rec_s, character);
            out_uint16_le(rec_s, offset);
            out_uint16_le(rec_s, baseline);
            out_uint16_le(rec_s, width);
            out_uint16_le(rec_s, height);
            out_uint16_le(rec_s, datasize);
            out_uint8a(rec_s, data, datasize);
            rdp_rec_write_item(self->rdp_layer, rec_s);
            free_stream(rec_s);
        }
    }
}

/*****************************************************************************/
/* Process a secondary order */
static int APP_CC
rdp_orders_process_secondary_order(struct rdp_orders *self, struct stream *s)
{
    short length = 0;
    int flags = 0;
    int type = 0;
    char *next_order = (char *)NULL;

    in_uint16_le(s, length);
    in_uint16_le(s, flags);
    in_uint8(s, type);
    next_order = s->p + length + 7;

    switch (type)
    {
        case RDP_ORDER_COLCACHE:
            rdp_orders_process_colcache(self, s, flags);
            break;
        case RDP_ORDER_RAW_BMPCACHE:
            rdp_orders_process_raw_bmpcache(self, s, flags);
            break;
        case RDP_ORDER_BMPCACHE:
            rdp_orders_process_bmpcache(self, s, flags);
            break;
        case RDP_ORDER_FONTCACHE:
            rdp_orders_process_fontcache(self, s, flags);
            break;
        default:
            /* error, unknown order */
            break;
    }

    s->p = next_order;
    return 0;
}

/*****************************************************************************/
/* Read a color entry */
static void APP_CC
rdp_orders_in_color(struct stream *s, int *color)
{
    int i = 0;

    in_uint8(s, i);
    *color = i;
    in_uint8(s, i);
    *color |= i << 8;
    in_uint8(s, i);
    *color |= i << 16;
}

/*****************************************************************************/
/* Parse a brush */
static void APP_CC
rdp_orders_parse_brush(struct stream *s, struct rdp_brush *brush, int 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);
    }
}

/*****************************************************************************/
/* Parse a pen */
static void APP_CC
rdp_orders_parse_pen(struct stream *s, struct rdp_pen *pen, int present)
{
    if (present & 1)
    {
        in_uint8(s, pen->style);
    }

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

    if (present & 4)
    {
        rdp_orders_in_color(s, &pen->color);
    }
}

/*****************************************************************************/
/* Process a text order */
static void APP_CC
rdp_orders_process_text2(struct rdp_orders *self, struct stream *s,
                         int present, int delta)
{
    int fgcolor = 0;
    int bgcolor = 0;
    struct stream *rec_s = (struct stream *)NULL;

    if (present & 0x000001)
    {
        in_uint8(s, self->state.text_font);
    }

    if (present & 0x000002)
    {
        in_uint8(s, self->state.text_flags);
    }

    if (present & 0x000004)
    {
        in_uint8(s, self->state.text_opcode);
    }

    if (present & 0x000008)
    {
        in_uint8(s, self->state.text_mixmode);
    }

    if (present & 0x000010)
    {
        rdp_orders_in_color(s, &self->state.text_fgcolor);
    }

    if (present & 0x000020)
    {
        rdp_orders_in_color(s, &self->state.text_bgcolor);
    }

    if (present & 0x000040)
    {
        in_sint16_le(s, self->state.text_clipleft);
    }

    if (present & 0x000080)
    {
        in_sint16_le(s, self->state.text_cliptop);
    }

    if (present & 0x000100)
    {
        in_sint16_le(s, self->state.text_clipright);
    }

    if (present & 0x000200)
    {
        in_sint16_le(s, self->state.text_clipbottom);
    }

    if (present & 0x000400)
    {
        in_sint16_le(s, self->state.text_boxleft);
    }

    if (present & 0x000800)
    {
        in_sint16_le(s, self->state.text_boxtop);
    }

    if (present & 0x001000)
    {
        in_sint16_le(s, self->state.text_boxright);
    }

    if (present & 0x002000)
    {
        in_sint16_le(s, self->state.text_boxbottom);
    }

    rdp_orders_parse_brush(s, &self->state.text_brush, present >> 14);

    if (present & 0x080000)
    {
        in_sint16_le(s, self->state.text_x);
    }

    if (present & 0x100000)
    {
        in_sint16_le(s, self->state.text_y);
    }

    if (present & 0x200000)
    {
        in_uint8(s, self->state.text_length);
        in_uint8a(s, self->state.text_text, self->state.text_length);
    }

    self->rdp_layer->mod->server_set_opcode(self->rdp_layer->mod,
                                            self->state.text_opcode);
    fgcolor = rdp_orders_convert_color(self->rdp_layer->mod->rdp_bpp,
                                       self->rdp_layer->mod->xrdp_bpp,
                                       self->state.text_fgcolor,
                                       self->rdp_layer->colormap.colors);
    self->rdp_layer->mod->server_set_fgcolor(self->rdp_layer->mod, fgcolor);
    bgcolor = rdp_orders_convert_color(self->rdp_layer->mod->rdp_bpp,
                                       self->rdp_layer->mod->xrdp_bpp,
                                       self->state.text_bgcolor,
                                       self->rdp_layer->colormap.colors);
    self->rdp_layer->mod->server_set_bgcolor(self->rdp_layer->mod, bgcolor);
    self->rdp_layer->mod->server_draw_text(self->rdp_layer->mod,
                                           self->state.text_font,
                                           self->state.text_flags,
                                           self->state.text_mixmode,
                                           self->state.text_clipleft,
                                           self->state.text_cliptop,
                                           self->state.text_clipright,
                                           self->state.text_clipbottom,
                                           self->state.text_boxleft,
                                           self->state.text_boxtop,
                                           self->state.text_boxright,
                                           self->state.text_boxbottom,
                                           self->state.text_x,
                                           self->state.text_y,
                                           self->state.text_text,
                                           self->state.text_length);
    self->rdp_layer->mod->server_set_opcode(self->rdp_layer->mod, 0xcc);

    if (self->rdp_layer->rec_mode)
    {
        rdp_rec_check_file(self->rdp_layer);
        make_stream(rec_s);
        init_stream(rec_s, 512);
        s_push_layer(rec_s, iso_hdr, 4);
        out_uint8(rec_s, 7);
        out_uint8(rec_s, self->state.text_font);
        out_uint8(rec_s, self->state.text_flags);
        out_uint8(rec_s, self->state.text_opcode);
        out_uint8(rec_s, self->state.text_mixmode);
        out_uint32_le(rec_s, self->state.text_fgcolor);
        out_uint32_le(rec_s, self->state.text_bgcolor);
        out_uint16_le(rec_s, self->state.text_clipleft);
        out_uint16_le(rec_s, self->state.text_cliptop);
        out_uint16_le(rec_s, self->state.text_clipright);
        out_uint16_le(rec_s, self->state.text_clipbottom);
        out_uint16_le(rec_s, self->state.text_boxleft);
        out_uint16_le(rec_s, self->state.text_boxtop);
        out_uint16_le(rec_s, self->state.text_boxright);
        out_uint16_le(rec_s, self->state.text_boxbottom);
        out_uint16_le(rec_s, self->state.text_x);
        out_uint16_le(rec_s, self->state.text_y);
        out_uint16_le(rec_s, self->state.text_length);
        out_uint8a(rec_s, self->state.text_text, self->state.text_length);
        rdp_rec_write_item(self->rdp_layer, rec_s);
        free_stream(rec_s);
    }
}

/*****************************************************************************/
/* Process a destination blt order */
static void APP_CC
rdp_orders_process_destblt(struct rdp_orders *self, struct stream *s,
                           int present, int delta)
{
    struct stream *rec_s = (struct stream *)NULL;

    if (present & 0x01)
    {
        rdp_orders_in_coord(s, &self->state.dest_x, delta);
    }

    if (present & 0x02)
    {
        rdp_orders_in_coord(s, &self->state.dest_y, delta);
    }

    if (present & 0x04)
    {
        rdp_orders_in_coord(s, &self->state.dest_cx, delta);
    }

    if (present & 0x08)
    {
        rdp_orders_in_coord(s, &self->state.dest_cy, delta);
    }

    if (present & 0x10)
    {
        in_uint8(s, self->state.dest_opcode);
    }

    self->rdp_layer->mod->server_set_opcode(self->rdp_layer->mod,
                                            self->state.dest_opcode);
    self->rdp_layer->mod->server_fill_rect(self->rdp_layer->mod,
                                           self->state.dest_x,
                                           self->state.dest_y,
                                           self->state.dest_cx,
                                           self->state.dest_cy);
    self->rdp_layer->mod->server_set_opcode(self->rdp_layer->mod, 0xcc);

    if (self->rdp_layer->rec_mode)
    {
        rdp_rec_check_file(self->rdp_layer);
        make_stream(rec_s);
        init_stream(rec_s, 512);
        s_push_layer(rec_s, iso_hdr, 4);
        out_uint8(rec_s, 6);
        out_uint16_le(rec_s, self->state.dest_x);
        out_uint16_le(rec_s, self->state.dest_y);
        out_uint16_le(rec_s, self->state.dest_cx);
        out_uint16_le(rec_s, self->state.dest_cy);
        out_uint8(rec_s, self->state.dest_opcode);
        rdp_rec_write_item(self->rdp_layer, rec_s);
        free_stream(rec_s);
    }
}

/*****************************************************************************/
/* Process a pattern blt order */
static void APP_CC
rdp_orders_process_patblt(struct rdp_orders *self, struct stream *s,
                          int present, int delta)
{
    int fgcolor = 0;
    int bgcolor = 0;
    struct stream *rec_s = (struct stream *)NULL;

    if (present & 0x0001)
    {
        rdp_orders_in_coord(s, &self->state.pat_x, delta);
    }

    if (present & 0x0002)
    {
        rdp_orders_in_coord(s, &self->state.pat_y, delta);
    }

    if (present & 0x0004)
    {
        rdp_orders_in_coord(s, &self->state.pat_cx, delta);
    }

    if (present & 0x0008)
    {
        rdp_orders_in_coord(s, &self->state.pat_cy, delta);
    }

    if (present & 0x0010)
    {
        in_uint8(s, self->state.pat_opcode);
    }

    if (present & 0x0020)
    {
        rdp_orders_in_color(s, &self->state.pat_bgcolor);
    }

    if (present & 0x0040)
    {
        rdp_orders_in_color(s, &self->state.pat_fgcolor);
    }

    rdp_orders_parse_brush(s, &self->state.pat_brush, present >> 7);
    self->rdp_layer->mod->server_set_opcode(self->rdp_layer->mod,
                                            self->state.pat_opcode);
    self->rdp_layer->mod->server_set_mixmode(self->rdp_layer->mod, 1);
    fgcolor = rdp_orders_convert_color(self->rdp_layer->mod->rdp_bpp,
                                       self->rdp_layer->mod->xrdp_bpp,
                                       self->state.pat_fgcolor,
                                       self->rdp_layer->colormap.colors);
    self->rdp_layer->mod->server_set_fgcolor(self->rdp_layer->mod, fgcolor);
    bgcolor = rdp_orders_convert_color(self->rdp_layer->mod->rdp_bpp,
                                       self->rdp_layer->mod->xrdp_bpp,
                                       self->state.pat_bgcolor,
                                       self->rdp_layer->colormap.colors);
    self->rdp_layer->mod->server_set_bgcolor(self->rdp_layer->mod, bgcolor);
    self->rdp_layer->mod->server_set_brush(self->rdp_layer->mod,
                                           self->state.pat_brush.xorigin,
                                           self->state.pat_brush.yorigin,
                                           self->state.pat_brush.style,
                                           self->state.pat_brush.pattern);
    self->rdp_layer->mod->server_fill_rect(self->rdp_layer->mod,
                                           self->state.pat_x,
                                           self->state.pat_y,
                                           self->state.pat_cx,
                                           self->state.pat_cy);
    self->rdp_layer->mod->server_set_opcode(self->rdp_layer->mod, 0xcc);
    self->rdp_layer->mod->server_set_mixmode(self->rdp_layer->mod, 0);

    if (self->rdp_layer->rec_mode)
    {
        rdp_rec_check_file(self->rdp_layer);
        make_stream(rec_s);
        init_stream(rec_s, 512);
        s_push_layer(rec_s, iso_hdr, 4);
        out_uint8(rec_s, 5);
        out_uint16_le(rec_s, self->state.pat_x);
        out_uint16_le(rec_s, self->state.pat_y);
        out_uint16_le(rec_s, self->state.pat_cx);
        out_uint16_le(rec_s, self->state.pat_cy);
        out_uint8(rec_s, self->state.pat_opcode);
        out_uint32_le(rec_s, self->state.pat_fgcolor);
        out_uint32_le(rec_s, self->state.pat_bgcolor);
        out_uint8(rec_s, self->state.pat_brush.xorigin);
        out_uint8(rec_s, self->state.pat_brush.yorigin);
        out_uint8(rec_s, self->state.pat_brush.style);
        out_uint8a(rec_s, self->state.pat_brush.pattern, 8);
        rdp_rec_write_item(self->rdp_layer, rec_s);
        free_stream(rec_s);
    }
}

/*****************************************************************************/
/* Process a screen blt order */
static void APP_CC
rdp_orders_process_screenblt(struct rdp_orders *self, struct stream *s,
                             int present, int delta)
{
    struct stream *rec_s;

    if (present & 0x0001)
    {
        rdp_orders_in_coord(s, &self->state.screenblt_x, delta);
    }

    if (present & 0x0002)
    {
        rdp_orders_in_coord(s, &self->state.screenblt_y, delta);
    }

    if (present & 0x0004)
    {
        rdp_orders_in_coord(s, &self->state.screenblt_cx, delta);
    }

    if (present & 0x0008)
    {
        rdp_orders_in_coord(s, &self->state.screenblt_cy, delta);
    }

    if (present & 0x0010)
    {
        in_uint8(s, self->state.screenblt_opcode);
    }

    if (present & 0x0020)
    {
        rdp_orders_in_coord(s, &self->state.screenblt_srcx, delta);
    }

    if (present & 0x0040)
    {
        rdp_orders_in_coord(s, &self->state.screenblt_srcy, delta);
    }

    self->rdp_layer->mod->server_set_opcode(self->rdp_layer->mod,
                                            self->state.screenblt_opcode);
    self->rdp_layer->mod->server_screen_blt(self->rdp_layer->mod,
                                            self->state.screenblt_x,
                                            self->state.screenblt_y,
                                            self->state.screenblt_cx,
                                            self->state.screenblt_cy,
                                            self->state.screenblt_srcx,
                                            self->state.screenblt_srcy);
    self->rdp_layer->mod->server_set_opcode(self->rdp_layer->mod, 0xcc);

    if (self->rdp_layer->rec_mode)
    {
        rdp_rec_check_file(self->rdp_layer);
        make_stream(rec_s);
        init_stream(rec_s, 512);
        s_push_layer(rec_s, iso_hdr, 4);
        out_uint8(rec_s, 4);
        out_uint16_le(rec_s, self->state.screenblt_x);
        out_uint16_le(rec_s, self->state.screenblt_y);
        out_uint16_le(rec_s, self->state.screenblt_cx);
        out_uint16_le(rec_s, self->state.screenblt_cy);
        out_uint16_le(rec_s, self->state.screenblt_srcx);
        out_uint16_le(rec_s, self->state.screenblt_srcy);
        out_uint8(rec_s, self->state.screenblt_opcode);
        rdp_rec_write_item(self->rdp_layer, rec_s);
        free_stream(rec_s);
    }
}

/*****************************************************************************/
/* Process a line order */
static void APP_CC
rdp_orders_process_line(struct rdp_orders *self, struct stream *s,
                        int present, int delta)
{
    int bgcolor = 0;
    int fgcolor = 0;
    struct stream *rec_s = (struct stream *)NULL;

    if (present & 0x0001)
    {
        in_uint16_le(s, self->state.line_mixmode);
    }

    if (present & 0x0002)
    {
        rdp_orders_in_coord(s, &self->state.line_startx, delta);
    }

    if (present & 0x0004)
    {
        rdp_orders_in_coord(s, &self->state.line_starty, delta);
    }

    if (present & 0x0008)
    {
        rdp_orders_in_coord(s, &self->state.line_endx, delta);
    }

    if (present & 0x0010)
    {
        rdp_orders_in_coord(s, &self->state.line_endy, delta);
    }

    if (present & 0x0020)
    {
        rdp_orders_in_color(s, &self->state.line_bgcolor);
    }

    if (present & 0x0040)
    {
        in_uint8(s, self->state.line_opcode);
    }

    rdp_orders_parse_pen(s, &self->state.line_pen, present >> 7);
    self->rdp_layer->mod->server_set_opcode(self->rdp_layer->mod,
                                            self->state.line_opcode);
    bgcolor = rdp_orders_convert_color(self->rdp_layer->mod->rdp_bpp,
                                       self->rdp_layer->mod->xrdp_bpp,
                                       self->state.line_bgcolor,
                                       self->rdp_layer->colormap.colors);
    fgcolor = rdp_orders_convert_color(self->rdp_layer->mod->rdp_bpp,
                                       self->rdp_layer->mod->xrdp_bpp,
                                       self->state.line_pen.color,
                                       self->rdp_layer->colormap.colors);
    self->rdp_layer->mod->server_set_fgcolor(self->rdp_layer->mod, fgcolor);
    self->rdp_layer->mod->server_set_bgcolor(self->rdp_layer->mod, bgcolor);
    self->rdp_layer->mod->server_set_pen(self->rdp_layer->mod,
                                         self->state.line_pen.style,
                                         self->state.line_pen.width);
    self->rdp_layer->mod->server_draw_line(self->rdp_layer->mod,
                                           self->state.line_startx,
                                           self->state.line_starty,
                                           self->state.line_endx,
                                           self->state.line_endy);
    self->rdp_layer->mod->server_set_opcode(self->rdp_layer->mod, 0xcc);

    if (self->rdp_layer->rec_mode)
    {
        rdp_rec_check_file(self->rdp_layer);
        make_stream(rec_s);
        init_stream(rec_s, 512);
        s_push_layer(rec_s, iso_hdr, 4);
        out_uint8(rec_s, 3);
        out_uint16_le(rec_s, self->state.line_mixmode);
        out_uint16_le(rec_s, self->state.line_startx);
        out_uint16_le(rec_s, self->state.line_starty);
        out_uint16_le(rec_s, self->state.line_endx);
        out_uint16_le(rec_s, self->state.line_endy);
        out_uint32_le(rec_s, self->state.line_bgcolor);
        out_uint8(rec_s, self->state.line_opcode);
        out_uint8(rec_s, self->state.line_pen.style);
        out_uint8(rec_s, self->state.line_pen.width);
        out_uint32_le(rec_s, self->state.line_pen.color);
        rdp_rec_write_item(self->rdp_layer, rec_s);
        free_stream(rec_s);
    }
}

/*****************************************************************************/
/* Process an opaque rectangle order */
static void APP_CC
rdp_orders_process_rect(struct rdp_orders *self, struct stream *s,
                        int present, int delta)
{
    int i = 0;
    int fgcolor = 0;
    struct stream *rec_s = (struct stream *)NULL;

    if (present & 0x01)
    {
        rdp_orders_in_coord(s, &self->state.rect_x, delta);
    }

    if (present & 0x02)
    {
        rdp_orders_in_coord(s, &self->state.rect_y, delta);
    }

    if (present & 0x04)
    {
        rdp_orders_in_coord(s, &self->state.rect_cx, delta);
    }

    if (present & 0x08)
    {
        rdp_orders_in_coord(s, &self->state.rect_cy, delta);
    }

    if (present & 0x10)
    {
        in_uint8(s, i);
        self->state.rect_color = (self->state.rect_color & 0xffffff00) | i;
    }

    if (present & 0x20)
    {
        in_uint8(s, i);
        self->state.rect_color = (self->state.rect_color & 0xffff00ff) | (i << 8);
    }

    if (present & 0x40)
    {
        in_uint8(s, i);
        self->state.rect_color = (self->state.rect_color & 0xff00ffff) | (i << 16);
    }

    fgcolor = rdp_orders_convert_color(self->rdp_layer->mod->rdp_bpp,
                                       self->rdp_layer->mod->xrdp_bpp,
                                       self->state.rect_color,
                                       self->rdp_layer->colormap.colors);
    self->rdp_layer->mod->server_set_fgcolor(self->rdp_layer->mod, fgcolor);
    self->rdp_layer->mod->server_fill_rect(self->rdp_layer->mod,
                                           self->state.rect_x,
                                           self->state.rect_y,
                                           self->state.rect_cx,
                                           self->state.rect_cy);

    if (self->rdp_layer->rec_mode)
    {
        rdp_rec_check_file(self->rdp_layer);
        make_stream(rec_s);
        init_stream(rec_s, 512);
        s_push_layer(rec_s, iso_hdr, 4);
        out_uint8(rec_s, 1);
        out_uint16_le(rec_s, self->state.rect_x);
        out_uint16_le(rec_s, self->state.rect_y);
        out_uint16_le(rec_s, self->state.rect_cx);
        out_uint16_le(rec_s, self->state.rect_cy);
        out_uint32_le(rec_s, self->state.rect_color);
        rdp_rec_write_item(self->rdp_layer, rec_s);
        free_stream(rec_s);
    }
}

/*****************************************************************************/
/* Process a desktop save order */
static void APP_CC
rdp_orders_process_desksave(struct rdp_orders *self, struct stream *s,
                            int present, int delta)
{
    //int width = 0;
    //int height = 0;

    if (present & 0x01)
    {
        in_uint32_le(s, self->state.desksave_offset);
    }

    if (present & 0x02)
    {
        rdp_orders_in_coord(s, &self->state.desksave_left, delta);
    }

    if (present & 0x04)
    {
        rdp_orders_in_coord(s, &self->state.desksave_top, delta);
    }

    if (present & 0x08)
    {
        rdp_orders_in_coord(s, &self->state.desksave_right, delta);
    }

    if (present & 0x10)
    {
        rdp_orders_in_coord(s, &self->state.desksave_bottom, delta);
    }

    if (present & 0x20)
    {
        in_uint8(s, self->state.desksave_action);
    }

    //  width = (self->state.desksave_right - self->state.desksave_left) + 1;
    //  height = (self->state.desksave_bottom - self->state.desksave_top) + 1;
    if (self->state.desksave_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 APP_CC
rdp_orders_process_memblt(struct rdp_orders *self, struct stream *s,
                          int present, int delta)
{
    struct rdp_bitmap *bitmap = (struct rdp_bitmap *)NULL;
    struct stream *rec_s = (struct stream *)NULL;
    char *bmpdata = (char *)NULL;

    if (present & 0x0001)
    {
        in_uint8(s, self->state.memblt_cache_id);
        in_uint8(s, self->state.memblt_color_table);
    }

    if (present & 0x0002)
    {
        rdp_orders_in_coord(s, &self->state.memblt_x, delta);
    }

    if (present & 0x0004)
    {
        rdp_orders_in_coord(s, &self->state.memblt_y, delta);
    }

    if (present & 0x0008)
    {
        rdp_orders_in_coord(s, &self->state.memblt_cx, delta);
    }

    if (present & 0x0010)
    {
        rdp_orders_in_coord(s, &self->state.memblt_cy, delta);
    }

    if (present & 0x0020)
    {
        in_uint8(s, self->state.memblt_opcode);
    }

    if (present & 0x0040)
    {
        rdp_orders_in_coord(s, &self->state.memblt_srcx, delta);
    }

    if (present & 0x0080)
    {
        rdp_orders_in_coord(s, &self->state.memblt_srcy, delta);
    }

    if (present & 0x0100)
    {
        in_uint16_le(s, self->state.memblt_cache_idx);
    }

    bitmap = self->cache_bitmap[self->state.memblt_cache_id]
             [self->state.memblt_cache_idx];

    if (bitmap != 0)
    {
        self->rdp_layer->mod->server_set_opcode(self->rdp_layer->mod,
                                                self->state.memblt_opcode);
        bmpdata = rdp_orders_convert_bitmap(self->rdp_layer->mod->rdp_bpp,
                                            self->rdp_layer->mod->xrdp_bpp,
                                            bitmap->data, bitmap->width,
                                            bitmap->height,
                                            self->cache_colormap
                                            [self->state.memblt_color_table]->colors);
        self->rdp_layer->mod->server_paint_rect(self->rdp_layer->mod,
                                                self->state.memblt_x,
                                                self->state.memblt_y,
                                                self->state.memblt_cx,
                                                self->state.memblt_cy,
                                                bmpdata,
                                                bitmap->width,
                                                bitmap->height,
                                                self->state.memblt_srcx,
                                                self->state.memblt_srcy);
        self->rdp_layer->mod->server_set_opcode(self->rdp_layer->mod, 0xcc);

        if (self->rdp_layer->rec_mode)
        {
            rdp_rec_check_file(self->rdp_layer);
            make_stream(rec_s);
            init_stream(rec_s, 512);
            s_push_layer(rec_s, iso_hdr, 4);
            out_uint8(rec_s, 2);
            out_uint8(rec_s, self->state.memblt_opcode);
            out_uint16_le(rec_s, self->state.memblt_x);
            out_uint16_le(rec_s, self->state.memblt_y);
            out_uint16_le(rec_s, self->state.memblt_cx);
            out_uint16_le(rec_s, self->state.memblt_cy);
            out_uint16_le(rec_s, self->state.memblt_cache_id);
            out_uint16_le(rec_s, self->state.memblt_cache_idx);
            out_uint16_le(rec_s, self->state.memblt_srcx);
            out_uint16_le(rec_s, self->state.memblt_srcy);
            rdp_rec_write_item(self->rdp_layer, rec_s);
            free_stream(rec_s);
        }

        if (bmpdata != bitmap->data)
        {
            g_free(bmpdata);
        }
    }
}

/*****************************************************************************/
/* Process a 3-way blt order */
static void APP_CC
rdp_orders_process_triblt(struct rdp_orders *self, struct stream *s,
                          int present, int delta)
{
    /* not used */
}

/*****************************************************************************/
/* Process a polyline order */
static void APP_CC
rdp_orders_process_polyline(struct rdp_orders *self, struct stream *s,
                            int present, int delta)
{
    if (present & 0x01)
    {
        rdp_orders_in_coord(s, &self->state.polyline_x, delta);
    }

    if (present & 0x02)
    {
        rdp_orders_in_coord(s, &self->state.polyline_y, delta);
    }

    if (present & 0x04)
    {
        in_uint8(s, self->state.polyline_opcode);
    }

    if (present & 0x10)
    {
        rdp_orders_in_color(s, &self->state.polyline_fgcolor);
    }

    if (present & 0x20)
    {
        in_uint8(s, self->state.polyline_lines);
    }

    if (present & 0x40)
    {
        in_uint8(s, self->state.polyline_datasize);
        in_uint8a(s, self->state.polyline_data, self->state.polyline_datasize);
    }

    /* todo */
}

/*****************************************************************************/
int APP_CC
rdp_orders_process_orders(struct rdp_orders *self, struct stream *s,
                          int num_orders)
{
    int processed = 0;
    int order_flags = 0;
    int size = 0;
    int present = 0;
    int delta = 0;

    processed = 0;

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

        if (!(order_flags & RDP_ORDER_STANDARD))
        {
            /* error, this should always be set */
            break;
        }

        if (order_flags & RDP_ORDER_SECONDARY)
        {
            rdp_orders_process_secondary_order(self, s);
        }
        else
        {
            if (order_flags & RDP_ORDER_CHANGE)
            {
                in_uint8(s, self->state.order_type);
            }

            switch (self->state.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;
                    break;
            }

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

            if (order_flags & RDP_ORDER_BOUNDS)
            {
                if (!(order_flags & RDP_ORDER_LASTBOUNDS))
                {
                    rdp_orders_parse_bounds(self, s);
                }

                self->rdp_layer->mod->server_set_clip(self->rdp_layer->mod,
                                                      self->state.clip_left,
                                                      self->state.clip_top,
                                                      (self->state.clip_right - self->state.clip_left) + 1,
                                                      (self->state.clip_bottom - self->state.clip_top) + 1);
            }

            delta = order_flags & RDP_ORDER_DELTA;

            switch (self->state.order_type)
            {
                case RDP_ORDER_TEXT2:
                    rdp_orders_process_text2(self, s, present, delta);
                    break;
                case RDP_ORDER_DESTBLT:
                    rdp_orders_process_destblt(self, s, present, delta);
                    break;
                case RDP_ORDER_PATBLT:
                    rdp_orders_process_patblt(self, s, present, delta);
                    break;
                case RDP_ORDER_SCREENBLT:
                    rdp_orders_process_screenblt(self, s, present, delta);
                    break;
                case RDP_ORDER_LINE:
                    rdp_orders_process_line(self, s, present, delta);
                    break;
                case RDP_ORDER_RECT:
                    rdp_orders_process_rect(self, s, present, delta);
                    break;
                case RDP_ORDER_DESKSAVE:
                    rdp_orders_process_desksave(self, s, present, delta);
                    break;
                case RDP_ORDER_MEMBLT:
                    rdp_orders_process_memblt(self, s, present, delta);
                    break;
                case RDP_ORDER_TRIBLT:
                    rdp_orders_process_triblt(self, s, present, delta);
                    break;
                case RDP_ORDER_POLYLINE:
                    rdp_orders_process_polyline(self, s, present, delta);
                    break;
                default:
                    /* error unknown order */
                    break;
            }

            if (order_flags & RDP_ORDER_BOUNDS)
            {
                self->rdp_layer->mod->server_reset_clip(self->rdp_layer->mod);
            }
        }

        processed++;
    }

    return 0;
}

/*****************************************************************************/
/* returns pointer, it might return bmpdata if the data doesn't need to
   be converted, else it mallocs it.  The calling function must free
   it if needed */
char *APP_CC
rdp_orders_convert_bitmap(int in_bpp, int out_bpp, char *bmpdata,
                          int width, int height, int *palette)
{
    char *out = (char *)NULL;
    char *src = (char *)NULL;
    char *dst = (char *)NULL;
    int i = 0;
    int j = 0;
    int red = 0;
    int green = 0;
    int blue = 0;
    int pixel = 0;

    if ((in_bpp == 8) && (out_bpp == 8))
    {
        out = (char *)g_malloc(width * height, 0);
        src = bmpdata;
        dst = out;

        for (i = 0; i < height; i++)
        {
            for (j = 0; j < width; j++)
            {
                pixel = *((tui8 *)src);
                pixel = palette[pixel];
                SPLITCOLOR32(red, green, blue, pixel);
                pixel = COLOR8(red, green, blue);
                *dst = pixel;
                src++;
                dst++;
            }
        }

        return out;
    }

    if ((in_bpp == 8) && (out_bpp == 16))
    {
        out = (char *)g_malloc(width * height * 2, 0);
        src = bmpdata;
        dst = out;

        for (i = 0; i < height; i++)
        {
            for (j = 0; j < width; j++)
            {
                pixel = *((tui8 *)src);
                pixel = palette[pixel];
                SPLITCOLOR32(red, green, blue, pixel);
                pixel = COLOR16(red, green, blue);
                *((tui16 *)dst) = pixel;
                src++;
                dst += 2;
            }
        }

        return out;
    }

    if ((in_bpp == 8) && (out_bpp == 24))
    {
        out = (char *)g_malloc(width * height * 4, 0);
        src = bmpdata;
        dst = out;

        for (i = 0; i < height; i++)
        {
            for (j = 0; j < width; j++)
            {
                pixel = *((tui8 *)src);
                pixel = palette[pixel];
                SPLITCOLOR32(red, green, blue, pixel);
                pixel = COLOR24RGB(red, green, blue);
                *((tui32 *)dst) = pixel;
                src++;
                dst += 4;
            }
        }

        return out;
    }

    if ((in_bpp == 15) && (out_bpp == 16))
    {
        out = (char *)g_malloc(width * height * 2, 0);
        src = bmpdata;
        dst = out;

        for (i = 0; i < height; i++)
        {
            for (j = 0; j < width; j++)
            {
                pixel = *((tui16 *)src);
                SPLITCOLOR15(red, green, blue, pixel);
                pixel = COLOR16(red, green, blue);
                *((tui16 *)dst) = pixel;
                src += 2;
                dst += 2;
            }
        }

        return out;
    }

    if ((in_bpp == 15) && (out_bpp == 24))
    {
        out = (char *)g_malloc(width * height * 4, 0);
        src = bmpdata;
        dst = out;

        for (i = 0; i < height; i++)
        {
            for (j = 0; j < width; j++)
            {
                pixel = *((tui16 *)src);
                SPLITCOLOR15(red, green, blue, pixel);
                pixel = COLOR24RGB(red, green, blue);
                *((tui32 *)dst) = pixel;
                src += 2;
                dst += 4;
            }
        }

        return out;
    }

    if ((in_bpp == 16) && (out_bpp == 16))
    {
        return bmpdata;
    }

    if ((in_bpp == 16) && (out_bpp == 24))
    {
        out = (char *)g_malloc(width * height * 4, 0);
        src = bmpdata;
        dst = out;

        for (i = 0; i < height; i++)
        {
            for (j = 0; j < width; j++)
            {
                pixel = *((tui16 *)src);
                SPLITCOLOR16(red, green, blue, pixel);
                pixel = COLOR24RGB(red, green, blue);
                *((tui32 *)dst) = pixel;
                src += 2;
                dst += 4;
            }
        }

        return out;
    }

    if ((in_bpp == 24) && (out_bpp == 24))
    {
        out = (char *)g_malloc(width * height * 4, 0);
        src = bmpdata;
        dst = out;

        for (i = 0; i < height; i++)
        {
            for (j = 0; j < width; j++)
            {
                blue = *((tui8 *)src);
                src++;
                green = *((tui8 *)src);
                src++;
                red = *((tui8 *)src);
                src++;
                pixel = COLOR24RGB(red, green, blue);
                *((tui32 *)dst) = pixel;
                dst += 4;
            }
        }

        return out;
    }

    return 0;
}

/*****************************************************************************/
/* returns color or 0 */
int APP_CC
rdp_orders_convert_color(int in_bpp, int out_bpp, int in_color, int *palette)
{
    int pixel = 0;
    int red = 0;
    int green = 0;
    int blue = 0;

    if ((in_bpp == 8) && (out_bpp == 8))
    {
        pixel = palette[in_color];
        SPLITCOLOR32(red, green, blue, pixel);
        pixel = COLOR8(red, green, blue);
        return pixel;
    }

    if ((in_bpp == 8) && (out_bpp == 16))
    {
        pixel = palette[in_color];
        SPLITCOLOR32(red, green, blue, pixel);
        pixel = COLOR16(red, green, blue);
        return pixel;
    }

    if ((in_bpp == 8) && (out_bpp == 24))
    {
        pixel = palette[in_color];
        SPLITCOLOR32(red, green, blue, pixel);
        pixel = COLOR24BGR(red, green, blue);
        return pixel;
    }

    if ((in_bpp == 15) && (out_bpp == 16))
    {
        pixel = in_color;
        SPLITCOLOR15(red, green, blue, pixel);
        pixel = COLOR16(red, green, blue);
        return pixel;
    }

    if ((in_bpp == 15) && (out_bpp == 24))
    {
        pixel = in_color;
        SPLITCOLOR15(red, green, blue, pixel);
        pixel = COLOR24BGR(red, green, blue);
        return pixel;
    }

    if ((in_bpp == 16) && (out_bpp == 16))
    {
        return in_color;
    }

    if ((in_bpp == 16) && (out_bpp == 24))
    {
        pixel = in_color;
        SPLITCOLOR16(red, green, blue, pixel);
        pixel = COLOR24BGR(red, green, blue);
        return pixel;
    }

    if ((in_bpp == 24) && (out_bpp == 24))
    {
        return in_color;
    }

    return 0;
}