diff --git a/uirdesktop/bitmap.c b/uirdesktop/bitmap.c new file mode 100644 index 00000000..18e0cee7 --- /dev/null +++ b/uirdesktop/bitmap.c @@ -0,0 +1,1021 @@ +/* -*- c-basic-offset: 8 -*- + rdesktop: A Remote Desktop Protocol client. + Bitmap decompression routines + Copyright (C) Matthew Chapman 1999-2005 + + 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. +*/ + +/* three seperate function for speed when decompressing the bitmaps */ +/* when modifing one function make the change in the others */ +/* comment out #define BITMAP_SPEED_OVER_SIZE below for one slower function */ +/* j@american-data.com */ + +#define BITMAP_SPEED_OVER_SIZE + +/* indent is confused by this file */ +/* *INDENT-OFF* */ + +#include "rdesktop.h" + +#define CVAL(p) (*(p++)) +#ifdef NEED_ALIGN +#ifdef L_ENDIAN +#define CVAL2(p, v) { v = (*(p++)); v |= (*(p++)) << 8; } +#else +#define CVAL2(p, v) { v = (*(p++)) << 8; v |= (*(p++)); } +#endif /* L_ENDIAN */ +#else +#define CVAL2(p, v) { v = (*((uint16*)p)); p += 2; } +#endif /* NEED_ALIGN */ + +#define UNROLL8(exp) { exp exp exp exp exp exp exp exp } + +#define REPEAT(statement) \ +{ \ + while((count & ~0x7) && ((x+8) < width)) \ + UNROLL8( statement; count--; x++; ); \ + \ + while((count > 0) && (x < width)) \ + { \ + statement; \ + count--; \ + x++; \ + } \ +} + +#define MASK_UPDATE() \ +{ \ + mixmask <<= 1; \ + if (mixmask == 0) \ + { \ + mask = fom_mask ? fom_mask : CVAL(input); \ + mixmask = 1; \ + } \ +} + +#ifdef BITMAP_SPEED_OVER_SIZE + +/* 1 byte bitmap decompress */ +static BOOL +bitmap_decompress1(uint8 * output, int width, int height, uint8 * input, int size) +{ + uint8 *end = input + size; + uint8 *prevline = NULL, *line = NULL; + int opcode, count, offset, isfillormix, x = width; + int lastopcode = -1, insertmix = False, bicolour = False; + uint8 code; + uint8 colour1 = 0, colour2 = 0; + uint8 mixmask, mask = 0; + uint8 mix = 0xff; + int fom_mask = 0; + + while (input < end) + { + fom_mask = 0; + code = CVAL(input); + opcode = code >> 4; + /* Handle different opcode forms */ + switch (opcode) + { + case 0xc: + case 0xd: + case 0xe: + opcode -= 6; + count = code & 0xf; + offset = 16; + break; + case 0xf: + opcode = code & 0xf; + if (opcode < 9) + { + count = CVAL(input); + count |= CVAL(input) << 8; + } + else + { + count = (opcode < 0xb) ? 8 : 1; + } + offset = 0; + break; + default: + opcode >>= 1; + count = code & 0x1f; + offset = 32; + break; + } + /* Handle strange cases for counts */ + if (offset != 0) + { + isfillormix = ((opcode == 2) || (opcode == 7)); + if (count == 0) + { + if (isfillormix) + count = CVAL(input) + 1; + else + count = CVAL(input) + offset; + } + else if (isfillormix) + { + count <<= 3; + } + } + /* Read preliminary data */ + switch (opcode) + { + case 0: /* Fill */ + if ((lastopcode == opcode) && !((x == width) && (prevline == NULL))) + insertmix = True; + break; + case 8: /* Bicolour */ + colour1 = CVAL(input); + case 3: /* Colour */ + colour2 = CVAL(input); + break; + case 6: /* SetMix/Mix */ + case 7: /* SetMix/FillOrMix */ + mix = CVAL(input); + opcode -= 5; + break; + case 9: /* FillOrMix_1 */ + mask = 0x03; + opcode = 0x02; + fom_mask = 3; + break; + case 0x0a: /* FillOrMix_2 */ + mask = 0x05; + opcode = 0x02; + fom_mask = 5; + break; + } + lastopcode = opcode; + mixmask = 0; + /* Output body */ + while (count > 0) + { + if (x >= width) + { + if (height <= 0) + return False; + x = 0; + height--; + prevline = line; + line = output + height * width; + } + switch (opcode) + { + case 0: /* Fill */ + if (insertmix) + { + if (prevline == NULL) + line[x] = mix; + else + line[x] = prevline[x] ^ mix; + insertmix = False; + count--; + x++; + } + if (prevline == NULL) + { + REPEAT(line[x] = 0) + } + else + { + REPEAT(line[x] = prevline[x]) + } + break; + case 1: /* Mix */ + if (prevline == NULL) + { + REPEAT(line[x] = mix) + } + else + { + REPEAT(line[x] = prevline[x] ^ mix) + } + break; + case 2: /* Fill or Mix */ + if (prevline == NULL) + { + REPEAT + ( + MASK_UPDATE(); + if (mask & mixmask) + line[x] = mix; + else + line[x] = 0; + ) + } + else + { + REPEAT + ( + MASK_UPDATE(); + if (mask & mixmask) + line[x] = prevline[x] ^ mix; + else + line[x] = prevline[x]; + ) + } + break; + case 3: /* Colour */ + REPEAT(line[x] = colour2) + break; + case 4: /* Copy */ + REPEAT(line[x] = CVAL(input)) + break; + case 8: /* Bicolour */ + REPEAT + ( + if (bicolour) + { + line[x] = colour2; + bicolour = False; + } + else + { + line[x] = colour1; + bicolour = True; count++; + } + ) + break; + case 0xd: /* White */ + REPEAT(line[x] = 0xff) + break; + case 0xe: /* Black */ + REPEAT(line[x] = 0) + break; + default: + unimpl("bitmap opcode 0x%x\n", opcode); + return False; + } + } + } + return True; +} + +/* 2 byte bitmap decompress */ +static BOOL +bitmap_decompress2(uint8 * output, int width, int height, uint8 * input, int size) +{ + uint8 *end = input + size; + uint16 *prevline = NULL, *line = NULL; + int opcode, count, offset, isfillormix, x = width; + int lastopcode = -1, insertmix = False, bicolour = False; + uint8 code; + uint16 colour1 = 0, colour2 = 0; + uint8 mixmask, mask = 0; + uint16 mix = 0xffff; + int fom_mask = 0; + + while (input < end) + { + fom_mask = 0; + code = CVAL(input); + opcode = code >> 4; + /* Handle different opcode forms */ + switch (opcode) + { + case 0xc: + case 0xd: + case 0xe: + opcode -= 6; + count = code & 0xf; + offset = 16; + break; + case 0xf: + opcode = code & 0xf; + if (opcode < 9) + { + count = CVAL(input); + count |= CVAL(input) << 8; + } + else + { + count = (opcode < 0xb) ? 8 : 1; + } + offset = 0; + break; + default: + opcode >>= 1; + count = code & 0x1f; + offset = 32; + break; + } + /* Handle strange cases for counts */ + if (offset != 0) + { + isfillormix = ((opcode == 2) || (opcode == 7)); + if (count == 0) + { + if (isfillormix) + count = CVAL(input) + 1; + else + count = CVAL(input) + offset; + } + else if (isfillormix) + { + count <<= 3; + } + } + /* Read preliminary data */ + switch (opcode) + { + case 0: /* Fill */ + if ((lastopcode == opcode) && !((x == width) && (prevline == NULL))) + insertmix = True; + break; + case 8: /* Bicolour */ + CVAL2(input, colour1); + case 3: /* Colour */ + CVAL2(input, colour2); + break; + case 6: /* SetMix/Mix */ + case 7: /* SetMix/FillOrMix */ + CVAL2(input, mix); + opcode -= 5; + break; + case 9: /* FillOrMix_1 */ + mask = 0x03; + opcode = 0x02; + fom_mask = 3; + break; + case 0x0a: /* FillOrMix_2 */ + mask = 0x05; + opcode = 0x02; + fom_mask = 5; + break; + } + lastopcode = opcode; + mixmask = 0; + /* Output body */ + while (count > 0) + { + if (x >= width) + { + if (height <= 0) + return False; + x = 0; + height--; + prevline = line; + line = ((uint16 *) output) + height * width; + } + switch (opcode) + { + case 0: /* Fill */ + if (insertmix) + { + if (prevline == NULL) + line[x] = mix; + else + line[x] = prevline[x] ^ mix; + insertmix = False; + count--; + x++; + } + if (prevline == NULL) + { + REPEAT(line[x] = 0) + } + else + { + REPEAT(line[x] = prevline[x]) + } + break; + case 1: /* Mix */ + if (prevline == NULL) + { + REPEAT(line[x] = mix) + } + else + { + REPEAT(line[x] = prevline[x] ^ mix) + } + break; + case 2: /* Fill or Mix */ + if (prevline == NULL) + { + REPEAT + ( + MASK_UPDATE(); + if (mask & mixmask) + line[x] = mix; + else + line[x] = 0; + ) + } + else + { + REPEAT + ( + MASK_UPDATE(); + if (mask & mixmask) + line[x] = prevline[x] ^ mix; + else + line[x] = prevline[x]; + ) + } + break; + case 3: /* Colour */ + REPEAT(line[x] = colour2) + break; + case 4: /* Copy */ + REPEAT(CVAL2(input, line[x])) + break; + case 8: /* Bicolour */ + REPEAT + ( + if (bicolour) + { + line[x] = colour2; + bicolour = False; + } + else + { + line[x] = colour1; + bicolour = True; + count++; + } + ) + break; + case 0xd: /* White */ + REPEAT(line[x] = 0xffff) + break; + case 0xe: /* Black */ + REPEAT(line[x] = 0) + break; + default: + unimpl("bitmap opcode 0x%x\n", opcode); + return False; + } + } + } + return True; +} + +/* 3 byte bitmap decompress */ +static BOOL +bitmap_decompress3(uint8 * output, int width, int height, uint8 * input, int size) +{ + uint8 *end = input + size; + uint8 *prevline = NULL, *line = NULL; + int opcode, count, offset, isfillormix, x = width; + int lastopcode = -1, insertmix = False, bicolour = False; + uint8 code; + uint8 colour1[3] = {0, 0, 0}, colour2[3] = {0, 0, 0}; + uint8 mixmask, mask = 0; + uint8 mix[3] = {0xff, 0xff, 0xff}; + int fom_mask = 0; + + while (input < end) + { + fom_mask = 0; + code = CVAL(input); + opcode = code >> 4; + /* Handle different opcode forms */ + switch (opcode) + { + case 0xc: + case 0xd: + case 0xe: + opcode -= 6; + count = code & 0xf; + offset = 16; + break; + case 0xf: + opcode = code & 0xf; + if (opcode < 9) + { + count = CVAL(input); + count |= CVAL(input) << 8; + } + else + { + count = (opcode < + 0xb) ? 8 : 1; + } + offset = 0; + break; + default: + opcode >>= 1; + count = code & 0x1f; + offset = 32; + break; + } + /* Handle strange cases for counts */ + if (offset != 0) + { + isfillormix = ((opcode == 2) || (opcode == 7)); + if (count == 0) + { + if (isfillormix) + count = CVAL(input) + 1; + else + count = CVAL(input) + offset; + } + else if (isfillormix) + { + count <<= 3; + } + } + /* Read preliminary data */ + switch (opcode) + { + case 0: /* Fill */ + if ((lastopcode == opcode) && !((x == width) && (prevline == NULL))) + insertmix = True; + break; + case 8: /* Bicolour */ + colour1[0] = CVAL(input); + colour1[1] = CVAL(input); + colour1[2] = CVAL(input); + case 3: /* Colour */ + colour2[0] = CVAL(input); + colour2[1] = CVAL(input); + colour2[2] = CVAL(input); + break; + case 6: /* SetMix/Mix */ + case 7: /* SetMix/FillOrMix */ + mix[0] = CVAL(input); + mix[1] = CVAL(input); + mix[2] = CVAL(input); + opcode -= 5; + break; + case 9: /* FillOrMix_1 */ + mask = 0x03; + opcode = 0x02; + fom_mask = 3; + break; + case 0x0a: /* FillOrMix_2 */ + mask = 0x05; + opcode = 0x02; + fom_mask = 5; + break; + } + lastopcode = opcode; + mixmask = 0; + /* Output body */ + while (count > 0) + { + if (x >= width) + { + if (height <= 0) + return False; + x = 0; + height--; + prevline = line; + line = output + height * (width * 3); + } + switch (opcode) + { + case 0: /* Fill */ + if (insertmix) + { + if (prevline == NULL) + { + line[x * 3] = mix[0]; + line[x * 3 + 1] = mix[1]; + line[x * 3 + 2] = mix[2]; + } + else + { + line[x * 3] = + prevline[x * 3] ^ mix[0]; + line[x * 3 + 1] = + prevline[x * 3 + 1] ^ mix[1]; + line[x * 3 + 2] = + prevline[x * 3 + 2] ^ mix[2]; + } + insertmix = False; + count--; + x++; + } + if (prevline == NULL) + { + REPEAT + ( + line[x * 3] = 0; + line[x * 3 + 1] = 0; + line[x * 3 + 2] = 0; + ) + } + else + { + REPEAT + ( + line[x * 3] = prevline[x * 3]; + line[x * 3 + 1] = prevline[x * 3 + 1]; + line[x * 3 + 2] = prevline[x * 3 + 2]; + ) + } + break; + case 1: /* Mix */ + if (prevline == NULL) + { + REPEAT + ( + line[x * 3] = mix[0]; + line[x * 3 + 1] = mix[1]; + line[x * 3 + 2] = mix[2]; + ) + } + else + { + REPEAT + ( + line[x * 3] = + prevline[x * 3] ^ mix[0]; + line[x * 3 + 1] = + prevline[x * 3 + 1] ^ mix[1]; + line[x * 3 + 2] = + prevline[x * 3 + 2] ^ mix[2]; + ) + } + break; + case 2: /* Fill or Mix */ + if (prevline == NULL) + { + REPEAT + ( + MASK_UPDATE(); + if (mask & mixmask) + { + line[x * 3] = mix[0]; + line[x * 3 + 1] = mix[1]; + line[x * 3 + 2] = mix[2]; + } + else + { + line[x * 3] = 0; + line[x * 3 + 1] = 0; + line[x * 3 + 2] = 0; + } + ) + } + else + { + REPEAT + ( + MASK_UPDATE(); + if (mask & mixmask) + { + line[x * 3] = + prevline[x * 3] ^ mix [0]; + line[x * 3 + 1] = + prevline[x * 3 + 1] ^ mix [1]; + line[x * 3 + 2] = + prevline[x * 3 + 2] ^ mix [2]; + } + else + { + line[x * 3] = + prevline[x * 3]; + line[x * 3 + 1] = + prevline[x * 3 + 1]; + line[x * 3 + 2] = + prevline[x * 3 + 2]; + } + ) + } + break; + case 3: /* Colour */ + REPEAT + ( + line[x * 3] = colour2 [0]; + line[x * 3 + 1] = colour2 [1]; + line[x * 3 + 2] = colour2 [2]; + ) + break; + case 4: /* Copy */ + REPEAT + ( + line[x * 3] = CVAL(input); + line[x * 3 + 1] = CVAL(input); + line[x * 3 + 2] = CVAL(input); + ) + break; + case 8: /* Bicolour */ + REPEAT + ( + if (bicolour) + { + line[x * 3] = colour2[0]; + line[x * 3 + 1] = colour2[1]; + line[x * 3 + 2] = colour2[2]; + bicolour = False; + } + else + { + line[x * 3] = colour1[0]; + line[x * 3 + 1] = colour1[1]; + line[x * 3 + 2] = colour1[2]; + bicolour = True; + count++; + } + ) + break; + case 0xd: /* White */ + REPEAT + ( + line[x * 3] = 0xff; + line[x * 3 + 1] = 0xff; + line[x * 3 + 2] = 0xff; + ) + break; + case 0xe: /* Black */ + REPEAT + ( + line[x * 3] = 0; + line[x * 3 + 1] = 0; + line[x * 3 + 2] = 0; + ) + break; + default: + unimpl("bitmap opcode 0x%x\n", opcode); + return False; + } + } + } + return True; +} + +#else + +static uint32 +cvalx(uint8 **input, int Bpp) +{ + uint32 rv = 0; + memcpy(&rv, *input, Bpp); + *input += Bpp; + return rv; +} + +static void +setli(uint8 *input, int offset, uint32 value, int Bpp) +{ + input += offset * Bpp; + memcpy(input, &value, Bpp); +} + +static uint32 +getli(uint8 *input, int offset, int Bpp) +{ + uint32 rv = 0; + input += offset * Bpp; + memcpy(&rv, input, Bpp); + return rv; +} + +static BOOL +bitmap_decompressx(uint8 *output, int width, int height, uint8 *input, int size, int Bpp) +{ + uint8 *end = input + size; + uint8 *prevline = NULL, *line = NULL; + int opcode, count, offset, isfillormix, x = width; + int lastopcode = -1, insertmix = False, bicolour = False; + uint8 code; + uint32 colour1 = 0, colour2 = 0; + uint8 mixmask, mask = 0; + uint32 mix = 0xffffffff; + int fom_mask = 0; + + while (input < end) + { + fom_mask = 0; + code = CVAL(input); + opcode = code >> 4; + + /* Handle different opcode forms */ + switch (opcode) + { + case 0xc: + case 0xd: + case 0xe: + opcode -= 6; + count = code & 0xf; + offset = 16; + break; + + case 0xf: + opcode = code & 0xf; + if (opcode < 9) + { + count = CVAL(input); + count |= CVAL(input) << 8; + } + else + { + count = (opcode < 0xb) ? 8 : 1; + } + offset = 0; + break; + + default: + opcode >>= 1; + count = code & 0x1f; + offset = 32; + break; + } + + /* Handle strange cases for counts */ + if (offset != 0) + { + isfillormix = ((opcode == 2) || (opcode == 7)); + + if (count == 0) + { + if (isfillormix) + count = CVAL(input) + 1; + else + count = CVAL(input) + offset; + } + else if (isfillormix) + { + count <<= 3; + } + } + + /* Read preliminary data */ + switch (opcode) + { + case 0: /* Fill */ + if ((lastopcode == opcode) && !((x == width) && (prevline == NULL))) + insertmix = True; + break; + case 8: /* Bicolour */ + colour1 = cvalx(&input, Bpp); + case 3: /* Colour */ + colour2 = cvalx(&input, Bpp); + break; + case 6: /* SetMix/Mix */ + case 7: /* SetMix/FillOrMix */ + mix = cvalx(&input, Bpp); + opcode -= 5; + break; + case 9: /* FillOrMix_1 */ + mask = 0x03; + opcode = 0x02; + fom_mask = 3; + break; + case 0x0a: /* FillOrMix_2 */ + mask = 0x05; + opcode = 0x02; + fom_mask = 5; + break; + + } + + lastopcode = opcode; + mixmask = 0; + + /* Output body */ + while (count > 0) + { + if (x >= width) + { + if (height <= 0) + return False; + + x = 0; + height--; + + prevline = line; + line = output + height * width * Bpp; + } + + switch (opcode) + { + case 0: /* Fill */ + if (insertmix) + { + if (prevline == NULL) + setli(line, x, mix, Bpp); + else + setli(line, x, + getli(prevline, x, Bpp) ^ mix, Bpp); + + insertmix = False; + count--; + x++; + } + + if (prevline == NULL) + { + REPEAT(setli(line, x, 0, Bpp))} + else + { + REPEAT(setli + (line, x, getli(prevline, x, Bpp), Bpp)); + } + break; + + case 1: /* Mix */ + if (prevline == NULL) + { + REPEAT(setli(line, x, mix, Bpp)); + } + else + { + REPEAT(setli + (line, x, getli(prevline, x, Bpp) ^ mix, + Bpp)); + } + break; + + case 2: /* Fill or Mix */ + if (prevline == NULL) + { + REPEAT(MASK_UPDATE(); + if (mask & mixmask) setli(line, x, mix, Bpp); + else + setli(line, x, 0, Bpp);); + } + else + { + REPEAT(MASK_UPDATE(); + if (mask & mixmask) + setli(line, x, getli(prevline, x, Bpp) ^ mix, + Bpp); + else + setli(line, x, getli(prevline, x, Bpp), + Bpp);); + } + break; + + case 3: /* Colour */ + REPEAT(setli(line, x, colour2, Bpp)); + break; + + case 4: /* Copy */ + REPEAT(setli(line, x, cvalx(&input, Bpp), Bpp)); + break; + + case 8: /* Bicolour */ + REPEAT(if (bicolour) + { + setli(line, x, colour2, Bpp); bicolour = False;} + else + { + setli(line, x, colour1, Bpp); bicolour = True; + count++;} + ); + break; + + case 0xd: /* White */ + REPEAT(setli(line, x, 0xffffffff, Bpp)); + break; + + case 0xe: /* Black */ + REPEAT(setli(line, x, 0, Bpp)); + break; + + default: + unimpl("bitmap opcode 0x%x\n", opcode); + return False; + } + } + } + + return True; +} + +#endif + +/* main decompress function */ +BOOL +bitmap_decompress(uint8 * output, int width, int height, uint8 * input, int size, int Bpp) +{ +#ifdef BITMAP_SPEED_OVER_SIZE + BOOL rv = False; + switch (Bpp) + { + case 1: + rv = bitmap_decompress1(output, width, height, input, size); + break; + case 2: + rv = bitmap_decompress2(output, width, height, input, size); + break; + case 3: + rv = bitmap_decompress3(output, width, height, input, size); + break; + } +#else + BOOL rv; + rv = bitmap_decompressx(output, width, height, input, size, Bpp); +#endif + return rv; +} + +/* *INDENT-ON* */ diff --git a/uirdesktop/cache.c b/uirdesktop/cache.c new file mode 100644 index 00000000..70fcf2cf --- /dev/null +++ b/uirdesktop/cache.c @@ -0,0 +1,432 @@ +/* -*- c-basic-offset: 8 -*- + rdesktop: A Remote Desktop Protocol client. + Cache routines + Copyright (C) Matthew Chapman 1999-2005 + Copyright (C) Jeroen Meijer 2005 + + 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" + +/* BITMAP CACHE */ +extern int g_pstcache_fd[]; + +#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(array[0])) +#define IS_PERSISTENT(id) (g_pstcache_fd[id] > 0) +#define TO_TOP -1 +#define NOT_SET -1 +#define IS_SET(idx) (idx >= 0) + +/* + * TODO: Test for optimal value of BUMP_COUNT. TO_TOP gives lowest cpu utilisation but using + * a positive value will hopefully result in less frequently used bitmaps having a greater chance + * of being evicted from the cache, and therby reducing the need to load bitmaps from disk. + * (Jeroen) + */ +#define BUMP_COUNT 40 + +struct bmpcache_entry +{ + HBITMAP bitmap; + sint16 previous; + sint16 next; +}; + +static struct bmpcache_entry g_bmpcache[3][0xa00]; +static HBITMAP g_volatile_bc[3]; + +static int g_bmpcache_lru[3] = { NOT_SET, NOT_SET, NOT_SET }; +static int g_bmpcache_mru[3] = { NOT_SET, NOT_SET, NOT_SET }; +static int g_bmpcache_count[3]; + +/* Setup the bitmap cache lru/mru linked list */ +void +cache_rebuild_bmpcache_linked_list(uint8 id, sint16 * idx, int count) +{ + int n = count, c = 0; + sint16 n_idx; + + /* find top, skip evicted bitmaps */ + while (--n >= 0 && g_bmpcache[id][idx[n]].bitmap == NULL); + if (n < 0) + { + g_bmpcache_mru[id] = g_bmpcache_lru[id] = NOT_SET; + return; + } + + g_bmpcache_mru[id] = idx[n]; + g_bmpcache[id][idx[n]].next = NOT_SET; + n_idx = idx[n]; + c++; + + /* link list */ + while (n >= 0) + { + /* skip evicted bitmaps */ + while (--n >= 0 && g_bmpcache[id][idx[n]].bitmap == NULL); + + if (n < 0) + break; + + g_bmpcache[id][n_idx].previous = idx[n]; + g_bmpcache[id][idx[n]].next = n_idx; + n_idx = idx[n]; + c++; + } + + g_bmpcache[id][n_idx].previous = NOT_SET; + g_bmpcache_lru[id] = n_idx; + + if (c != g_bmpcache_count[id]) + { + error("Oops. %d in bitmap cache linked list, %d in ui cache...\n", c, + g_bmpcache_count[id]); + exit(1); + } +} + +/* Move a bitmap to a new position in the linked list. */ +void +cache_bump_bitmap(uint8 id, uint16 idx, int bump) +{ + int p_idx, n_idx, n; + + if (!IS_PERSISTENT(id)) + return; + + if (g_bmpcache_mru[id] == idx) + return; + + DEBUG_RDP5(("bump bitmap: id=%d, idx=%d, bump=%d\n", id, idx, bump)); + + n_idx = g_bmpcache[id][idx].next; + p_idx = g_bmpcache[id][idx].previous; + + if (IS_SET(n_idx)) + { + /* remove */ + --g_bmpcache_count[id]; + if (IS_SET(p_idx)) + g_bmpcache[id][p_idx].next = n_idx; + else + g_bmpcache_lru[id] = n_idx; + if (IS_SET(n_idx)) + g_bmpcache[id][n_idx].previous = p_idx; + else + g_bmpcache_mru[id] = p_idx; + } + else + { + p_idx = NOT_SET; + n_idx = g_bmpcache_lru[id]; + } + + if (bump >= 0) + { + for (n = 0; n < bump && IS_SET(n_idx); n++) + { + p_idx = n_idx; + n_idx = g_bmpcache[id][p_idx].next; + } + } + else + { + p_idx = g_bmpcache_mru[id]; + n_idx = NOT_SET; + } + + /* insert */ + ++g_bmpcache_count[id]; + g_bmpcache[id][idx].previous = p_idx; + g_bmpcache[id][idx].next = n_idx; + + if (p_idx >= 0) + g_bmpcache[id][p_idx].next = idx; + else + g_bmpcache_lru[id] = idx; + + if (n_idx >= 0) + g_bmpcache[id][n_idx].previous = idx; + else + g_bmpcache_mru[id] = idx; +} + +/* Evict the least-recently used bitmap from the cache */ +void +cache_evict_bitmap(uint8 id) +{ + uint16 idx; + int n_idx; + + if (!IS_PERSISTENT(id)) + return; + + idx = g_bmpcache_lru[id]; + n_idx = g_bmpcache[id][idx].next; + DEBUG_RDP5(("evict bitmap: id=%d idx=%d n_idx=%d bmp=0x%x\n", id, idx, n_idx, + g_bmpcache[id][idx].bitmap)); + + ui_destroy_bitmap(g_bmpcache[id][idx].bitmap); + --g_bmpcache_count[id]; + g_bmpcache[id][idx].bitmap = 0; + + g_bmpcache_lru[id] = n_idx; + g_bmpcache[id][n_idx].previous = NOT_SET; + + pstcache_touch_bitmap(id, idx, 0); +} + +/* Retrieve a bitmap from the cache */ +HBITMAP +cache_get_bitmap(uint8 id, uint16 idx) +{ + if ((id < NUM_ELEMENTS(g_bmpcache)) && (idx < NUM_ELEMENTS(g_bmpcache[0]))) + { + if (g_bmpcache[id][idx].bitmap || pstcache_load_bitmap(id, idx)) + { + if (IS_PERSISTENT(id)) + cache_bump_bitmap(id, idx, BUMP_COUNT); + + return g_bmpcache[id][idx].bitmap; + } + } + else if ((id < NUM_ELEMENTS(g_volatile_bc)) && (idx == 0x7fff)) + { + return g_volatile_bc[id]; + } + + error("get bitmap %d:%d\n", id, idx); + return NULL; +} + +/* Store a bitmap in the cache */ +void +cache_put_bitmap(uint8 id, uint16 idx, HBITMAP bitmap) +{ + HBITMAP old; + + if ((id < NUM_ELEMENTS(g_bmpcache)) && (idx < NUM_ELEMENTS(g_bmpcache[0]))) + { + old = g_bmpcache[id][idx].bitmap; + if (old != NULL) + ui_destroy_bitmap(old); + g_bmpcache[id][idx].bitmap = bitmap; + + if (IS_PERSISTENT(id)) + { + if (old == NULL) + g_bmpcache[id][idx].previous = g_bmpcache[id][idx].next = NOT_SET; + + cache_bump_bitmap(id, idx, TO_TOP); + if (g_bmpcache_count[id] > BMPCACHE2_C2_CELLS) + cache_evict_bitmap(id); + } + } + else if ((id < NUM_ELEMENTS(g_volatile_bc)) && (idx == 0x7fff)) + { + old = g_volatile_bc[id]; + if (old != NULL) + ui_destroy_bitmap(old); + g_volatile_bc[id] = bitmap; + } + else + { + error("put bitmap %d:%d\n", id, idx); + } +} + +/* Updates the persistent bitmap cache MRU information on exit */ +void +cache_save_state(void) +{ + uint32 id = 0, t = 0; + int idx; + + for (id = 0; id < NUM_ELEMENTS(g_bmpcache); id++) + if (IS_PERSISTENT(id)) + { + DEBUG_RDP5(("Saving cache state for bitmap cache %d...", id)); + idx = g_bmpcache_lru[id]; + while (idx >= 0) + { + pstcache_touch_bitmap(id, idx, ++t); + idx = g_bmpcache[id][idx].next; + } + DEBUG_RDP5((" %d stamps written.\n", t)); + } +} + + +/* FONT CACHE */ +static FONTGLYPH g_fontcache[12][256]; + +/* Retrieve a glyph from the font cache */ +FONTGLYPH * +cache_get_font(uint8 font, uint16 character) +{ + FONTGLYPH *glyph; + + if ((font < NUM_ELEMENTS(g_fontcache)) && (character < NUM_ELEMENTS(g_fontcache[0]))) + { + glyph = &g_fontcache[font][character]; + if (glyph->pixmap != NULL) + return glyph; + } + + error("get font %d:%d\n", font, character); + return NULL; +} + +/* Store a glyph in the font cache */ +void +cache_put_font(uint8 font, uint16 character, uint16 offset, + uint16 baseline, uint16 width, uint16 height, HGLYPH pixmap) +{ + FONTGLYPH *glyph; + + if ((font < NUM_ELEMENTS(g_fontcache)) && (character < NUM_ELEMENTS(g_fontcache[0]))) + { + glyph = &g_fontcache[font][character]; + if (glyph->pixmap != NULL) + ui_destroy_glyph(glyph->pixmap); + + glyph->offset = offset; + glyph->baseline = baseline; + glyph->width = width; + glyph->height = height; + glyph->pixmap = pixmap; + } + else + { + error("put font %d:%d\n", font, character); + } +} + + +/* TEXT CACHE */ +static DATABLOB g_textcache[256]; + +/* Retrieve a text item from the cache */ +DATABLOB * +cache_get_text(uint8 cache_id) +{ + DATABLOB *text; + + text = &g_textcache[cache_id]; + return text; +} + +/* Store a text item in the cache */ +void +cache_put_text(uint8 cache_id, void *data, int length) +{ + DATABLOB *text; + + text = &g_textcache[cache_id]; + if (text->data != NULL) + xfree(text->data); + text->data = xmalloc(length); + text->size = length; + memcpy(text->data, data, length); +} + + +/* DESKTOP CACHE */ +static uint8 g_deskcache[0x38400 * 4]; + +/* Retrieve desktop data from the cache */ +uint8 * +cache_get_desktop(uint32 offset, int cx, int cy, int bytes_per_pixel) +{ + int length = cx * cy * bytes_per_pixel; + + if (offset > sizeof(g_deskcache)) + offset = 0; + + if ((offset + length) <= sizeof(g_deskcache)) + { + return &g_deskcache[offset]; + } + + error("get desktop %d:%d\n", offset, length); + return NULL; +} + +/* Store desktop data in the cache */ +void +cache_put_desktop(uint32 offset, int cx, int cy, int scanline, int bytes_per_pixel, uint8 * data) +{ + int length = cx * cy * bytes_per_pixel; + + if (offset > sizeof(g_deskcache)) + offset = 0; + + if ((offset + length) <= sizeof(g_deskcache)) + { + cx *= bytes_per_pixel; + while (cy--) + { + memcpy(&g_deskcache[offset], data, cx); + data += scanline; + offset += cx; + } + } + else + { + error("put desktop %d:%d\n", offset, length); + } +} + + +/* CURSOR CACHE */ +static HCURSOR g_cursorcache[0x20]; + +/* Retrieve cursor from cache */ +HCURSOR +cache_get_cursor(uint16 cache_idx) +{ + HCURSOR cursor; + + if (cache_idx < NUM_ELEMENTS(g_cursorcache)) + { + cursor = g_cursorcache[cache_idx]; + if (cursor != NULL) + return cursor; + } + + error("get cursor %d\n", cache_idx); + return NULL; +} + +/* Store cursor in cache */ +void +cache_put_cursor(uint16 cache_idx, HCURSOR cursor) +{ + HCURSOR old; + + if (cache_idx < NUM_ELEMENTS(g_cursorcache)) + { + old = g_cursorcache[cache_idx]; + if (old != NULL) + ui_destroy_cursor(old); + + g_cursorcache[cache_idx] = cursor; + } + else + { + error("put cursor %d\n", cache_idx); + } +} diff --git a/uirdesktop/mppc.c b/uirdesktop/mppc.c new file mode 100644 index 00000000..cc126c15 --- /dev/null +++ b/uirdesktop/mppc.c @@ -0,0 +1,380 @@ +/* -*- c-basic-offset: 8 -*- + rdesktop: A Remote Desktop Protocol client. + Protocol services - RDP decompression + Copyright (C) Matthew Chapman 1999-2005 + + 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 +#include + +#include "rdesktop.h" + +/* mppc decompression */ +/* http://www.faqs.org/rfcs/rfc2118.html */ + +/* Contacts: */ + +/* hifn contact mentioned in the faq is */ +/* Robert Friend rfriend at hifn dot com */ + +/* if you have questions regarding MPPC */ +/* our contact is */ +/* Guus Dhaeze GDhaeze at hifn dot com */ + +/* Licensing: */ + +/* decompression is alright as long as we */ +/* don't compress data */ + +/* Algorithm: */ + +/* as the rfc states the algorithm seems to */ +/* be LZ77 with a sliding buffer */ +/* that is empty at init. */ + +/* the algorithm is called LZS and is */ +/* patented for another couple of years. */ + +/* more information is available in */ +/* http://www.ietf.org/ietf/IPR/hifn-ipr-draft-friend-tls-lzs-compression.txt */ + + +RDPCOMP g_mppc_dict; + +int +mppc_expand(uint8 * data, uint32 clen, uint8 ctype, uint32 * roff, uint32 * rlen) +{ + int k, walker_len = 0, walker; + uint32 i = 0; + int next_offset, match_off; + int match_len; + int old_offset, match_bits; + BOOL big = ctype & RDP_MPPC_BIG ? True : False; + + uint8 *dict = g_mppc_dict.hist; + + if ((ctype & RDP_MPPC_COMPRESSED) == 0) + { + *roff = 0; + *rlen = clen; + return 0; + } + + if ((ctype & RDP_MPPC_RESET) != 0) + { + g_mppc_dict.roff = 0; + } + + if ((ctype & RDP_MPPC_FLUSH) != 0) + { + memset(dict, 0, RDP_MPPC_DICT_SIZE); + g_mppc_dict.roff = 0; + } + + *roff = 0; + *rlen = 0; + + walker = g_mppc_dict.roff; + + next_offset = walker; + old_offset = next_offset; + *roff = old_offset; + if (clen == 0) + return 0; + clen += i; + + do + { + if (walker_len == 0) + { + if (i >= clen) + break; + walker = data[i++] << 24; + walker_len = 8; + } + if (walker >= 0) + { + if (walker_len < 8) + { + if (i >= clen) + { + if (walker != 0) + return -1; + break; + } + walker |= (data[i++] & 0xff) << (24 - walker_len); + walker_len += 8; + } + if (next_offset >= RDP_MPPC_DICT_SIZE) + return -1; + dict[next_offset++] = (((uint32) walker) >> ((uint32) 24)); + walker <<= 8; + walker_len -= 8; + continue; + } + walker <<= 1; + /* fetch next 8-bits */ + if (--walker_len == 0) + { + if (i >= clen) + return -1; + walker = data[i++] << 24; + walker_len = 8; + } + /* literal decoding */ + if (walker >= 0) + { + if (walker_len < 8) + { + if (i >= clen) + return -1; + walker |= (data[i++] & 0xff) << (24 - walker_len); + walker_len += 8; + } + if (next_offset >= RDP_MPPC_DICT_SIZE) + return -1; + dict[next_offset++] = (uint8) (walker >> 24 | 0x80); + walker <<= 8; + walker_len -= 8; + continue; + } + + /* decode offset */ + /* length pair */ + walker <<= 1; + if (--walker_len < (big ? 3 : 2)) + { + if (i >= clen) + return -1; + walker |= (data[i++] & 0xff) << (24 - walker_len); + walker_len += 8; + } + + if (big) + { + /* offset decoding where offset len is: + -63: 11111 followed by the lower 6 bits of the value + 64-319: 11110 followed by the lower 8 bits of the value ( value - 64 ) + 320-2367: 1110 followed by lower 11 bits of the value ( value - 320 ) + 2368-65535: 110 followed by lower 16 bits of the value ( value - 2368 ) + */ + switch (((uint32) walker) >> ((uint32) 29)) + { + case 7: /* - 63 */ + for (; walker_len < 9; walker_len += 8) + { + if (i >= clen) + return -1; + walker |= (data[i++] & 0xff) << (24 - walker_len); + } + walker <<= 3; + match_off = ((uint32) walker) >> ((uint32) 26); + walker <<= 6; + walker_len -= 9; + break; + + case 6: /* 64 - 319 */ + for (; walker_len < 11; walker_len += 8) + { + if (i >= clen) + return -1; + walker |= (data[i++] & 0xff) << (24 - walker_len); + } + + walker <<= 3; + match_off = (((uint32) walker) >> ((uint32) 24)) + 64; + walker <<= 8; + walker_len -= 11; + break; + + case 5: + case 4: /* 320 - 2367 */ + for (; walker_len < 13; walker_len += 8) + { + if (i >= clen) + return -1; + walker |= (data[i++] & 0xff) << (24 - walker_len); + } + + walker <<= 2; + match_off = (((uint32) walker) >> ((uint32) 21)) + 320; + walker <<= 11; + walker_len -= 13; + break; + + default: /* 2368 - 65535 */ + for (; walker_len < 17; walker_len += 8) + { + if (i >= clen) + return -1; + walker |= (data[i++] & 0xff) << (24 - walker_len); + } + + walker <<= 1; + match_off = (((uint32) walker) >> ((uint32) 16)) + 2368; + walker <<= 16; + walker_len -= 17; + break; + } + } + else + { + /* offset decoding where offset len is: + -63: 1111 followed by the lower 6 bits of the value + 64-319: 1110 followed by the lower 8 bits of the value ( value - 64 ) + 320-8191: 110 followed by the lower 13 bits of the value ( value - 320 ) + */ + switch (((uint32) walker) >> ((uint32) 30)) + { + case 3: /* - 63 */ + if (walker_len < 8) + { + if (i >= clen) + return -1; + walker |= (data[i++] & 0xff) << (24 - walker_len); + walker_len += 8; + } + walker <<= 2; + match_off = ((uint32) walker) >> ((uint32) 26); + walker <<= 6; + walker_len -= 8; + break; + + case 2: /* 64 - 319 */ + for (; walker_len < 10; walker_len += 8) + { + if (i >= clen) + return -1; + walker |= (data[i++] & 0xff) << (24 - walker_len); + } + + walker <<= 2; + match_off = (((uint32) walker) >> ((uint32) 24)) + 64; + walker <<= 8; + walker_len -= 10; + break; + + default: /* 320 - 8191 */ + for (; walker_len < 14; walker_len += 8) + { + if (i >= clen) + return -1; + walker |= (data[i++] & 0xff) << (24 - walker_len); + } + + match_off = (walker >> 18) + 320; + walker <<= 14; + walker_len -= 14; + break; + } + } + if (walker_len == 0) + { + if (i >= clen) + return -1; + walker = data[i++] << 24; + walker_len = 8; + } + + /* decode length of match */ + match_len = 0; + if (walker >= 0) + { /* special case - length of 3 is in bit 0 */ + match_len = 3; + walker <<= 1; + walker_len--; + } + else + { + /* this is how it works len of: + 4-7: 10 followed by 2 bits of the value + 8-15: 110 followed by 3 bits of the value + 16-31: 1110 followed by 4 bits of the value + 32-63: .... and so forth + 64-127: + 128-255: + 256-511: + 512-1023: + 1024-2047: + 2048-4095: + 4096-8191: + + i.e. 4097 is encoded as: 111111111110 000000000001 + meaning 4096 + 1... + */ + match_bits = big ? 14 : 11; /* 11 or 14 bits of value at most */ + do + { + walker <<= 1; + if (--walker_len == 0) + { + if (i >= clen) + return -1; + walker = data[i++] << 24; + walker_len = 8; + } + if (walker >= 0) + break; + if (--match_bits == 0) + { + return -1; + } + } + while (1); + match_len = (big ? 16 : 13) - match_bits; + walker <<= 1; + if (--walker_len < match_len) + { + for (; walker_len < match_len; walker_len += 8) + { + if (i >= clen) + { + return -1; + } + walker |= (data[i++] & 0xff) << (24 - walker_len); + } + } + + match_bits = match_len; + match_len = + ((walker >> (32 - match_bits)) & (~(-1 << match_bits))) | (1 << + match_bits); + walker <<= match_bits; + walker_len -= match_bits; + } + if (next_offset + match_len >= RDP_MPPC_DICT_SIZE) + { + return -1; + } + /* memory areas can overlap - meaning we can't use memXXX functions */ + k = (next_offset - match_off) & (big ? 65535 : 8191); + do + { + dict[next_offset++] = dict[k++]; + } + while (--match_len != 0); + } + while (1); + + /* store history offset */ + g_mppc_dict.roff = next_offset; + + *roff = old_offset; + *rlen = next_offset - old_offset; + + return 0; +} diff --git a/uirdesktop/orders.c b/uirdesktop/orders.c new file mode 100644 index 00000000..418988ba --- /dev/null +++ b/uirdesktop/orders.c @@ -0,0 +1,1307 @@ +/* -*- c-basic-offset: 8 -*- + rdesktop: A Remote Desktop Protocol client. + RDP order processing + Copyright (C) Matthew Chapman 1999-2005 + + 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 uint8 *g_next_packet; +static RDP_ORDER_STATE g_order_state; +extern BOOL g_use_rdp5; + +/* 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, sint16 * coord, BOOL delta) +{ + sint8 change; + + if (delta) + { + in_uint8(s, change); + *coord += change; + } + else + { + in_uint16_le(s, *coord); + } +} + +/* Parse a delta co-ordinate in polyline/polygon order form */ +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; +} + +/* Read a colour entry */ +static void +rdp_in_colour(STREAM s, uint32 * colour) +{ + uint32 i; + in_uint8(s, i); + *colour = i; + in_uint8(s, i); + *colour |= i << 8; + in_uint8(s, i); + *colour |= i << 16; +} + +/* 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,dy=%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) +{ + uint32 i; + 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, i); + os->colour = (os->colour & 0xffffff00) | i; + } + + if (present & 0x20) + { + in_uint8(s, i); + os->colour = (os->colour & 0xffff00ff) | (i << 8); + } + + if (present & 0x40) + { + in_uint8(s, i); + os->colour = (os->colour & 0xff00ffff) | (i << 16); + } + + 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); +} + +/* Process a polygon order */ +static void +process_polygon(STREAM s, POLYGON_ORDER * os, uint32 present, BOOL delta) +{ + int index, data, next; + uint8 flags = 0; + POINT *points; + + 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->opcode); + + if (present & 0x08) + in_uint8(s, os->fillmode); + + if (present & 0x10) + rdp_in_colour(s, &os->fgcolour); + + if (present & 0x20) + in_uint8(s, os->npoints); + + if (present & 0x40) + { + in_uint8(s, os->datasize); + in_uint8a(s, os->data, os->datasize); + } + + DEBUG(("POLYGON(x=%d,y=%d,op=0x%x,fm=%d,fg=0x%x,n=%d,sz=%d)\n", + os->x, os->y, os->opcode, os->fillmode, os->fgcolour, os->npoints, os->datasize)); + + DEBUG(("Data: ")); + + for (index = 0; index < os->datasize; index++) + DEBUG(("%02x ", os->data[index])); + + DEBUG(("\n")); + + if (os->opcode < 0x01 || os->opcode > 0x10) + { + error("bad ROP2 0x%x\n", os->opcode); + return; + } + + points = (POINT *) xmalloc((os->npoints + 1) * sizeof(POINT)); + memset(points, 0, (os->npoints + 1) * sizeof(POINT)); + + points[0].x = os->x; + points[0].y = os->y; + + index = 0; + data = ((os->npoints - 1) / 4) + 1; + for (next = 1; (next <= os->npoints) && (next < 256) && (data < os->datasize); next++) + { + if ((next - 1) % 4 == 0) + flags = os->data[index++]; + + if (~flags & 0x80) + points[next].x = parse_delta(os->data, &data); + + if (~flags & 0x40) + points[next].y = parse_delta(os->data, &data); + + flags <<= 2; + } + + if (next - 1 == os->npoints) + ui_polygon(os->opcode - 1, os->fillmode, points, os->npoints + 1, NULL, 0, + os->fgcolour); + else + error("polygon parse error\n"); + + xfree(points); +} + +/* Process a polygon2 order */ +static void +process_polygon2(STREAM s, POLYGON2_ORDER * os, uint32 present, BOOL delta) +{ + int index, data, next; + uint8 flags = 0; + POINT *points; + + if (present & 0x0001) + rdp_in_coord(s, &os->x, delta); + + if (present & 0x0002) + rdp_in_coord(s, &os->y, delta); + + if (present & 0x0004) + in_uint8(s, os->opcode); + + if (present & 0x0008) + in_uint8(s, os->fillmode); + + if (present & 0x0010) + rdp_in_colour(s, &os->bgcolour); + + if (present & 0x0020) + rdp_in_colour(s, &os->fgcolour); + + rdp_parse_brush(s, &os->brush, present >> 6); + + if (present & 0x0800) + in_uint8(s, os->npoints); + + if (present & 0x1000) + { + in_uint8(s, os->datasize); + in_uint8a(s, os->data, os->datasize); + } + + DEBUG(("POLYGON2(x=%d,y=%d,op=0x%x,fm=%d,bs=%d,bg=0x%x,fg=0x%x,n=%d,sz=%d)\n", + os->x, os->y, os->opcode, os->fillmode, os->brush.style, os->bgcolour, os->fgcolour, + os->npoints, os->datasize)); + + DEBUG(("Data: ")); + + for (index = 0; index < os->datasize; index++) + DEBUG(("%02x ", os->data[index])); + + DEBUG(("\n")); + + if (os->opcode < 0x01 || os->opcode > 0x10) + { + error("bad ROP2 0x%x\n", os->opcode); + return; + } + + points = (POINT *) xmalloc((os->npoints + 1) * sizeof(POINT)); + memset(points, 0, (os->npoints + 1) * sizeof(POINT)); + + points[0].x = os->x; + points[0].y = os->y; + + index = 0; + data = ((os->npoints - 1) / 4) + 1; + for (next = 1; (next <= os->npoints) && (next < 256) && (data < os->datasize); next++) + { + if ((next - 1) % 4 == 0) + flags = os->data[index++]; + + if (~flags & 0x80) + points[next].x = parse_delta(os->data, &data); + + if (~flags & 0x40) + points[next].y = parse_delta(os->data, &data); + + flags <<= 2; + } + + if (next - 1 == os->npoints) + ui_polygon(os->opcode - 1, os->fillmode, points, os->npoints + 1, + &os->brush, os->bgcolour, os->fgcolour); + else + error("polygon2 parse error\n"); + + xfree(points); +} + +/* Process a polyline order */ +static void +process_polyline(STREAM s, POLYLINE_ORDER * os, uint32 present, BOOL delta) +{ + int index, next, data; + uint8 flags = 0; + PEN pen; + POINT *points; + + 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->opcode); + + 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,op=0x%x,fg=0x%x,n=%d,sz=%d)\n", + os->x, os->y, os->opcode, os->fgcolour, os->lines, os->datasize)); + + DEBUG(("Data: ")); + + for (index = 0; index < os->datasize; index++) + DEBUG(("%02x ", os->data[index])); + + DEBUG(("\n")); + + if (os->opcode < 0x01 || os->opcode > 0x10) + { + error("bad ROP2 0x%x\n", os->opcode); + return; + } + + points = (POINT *) xmalloc((os->lines + 1) * sizeof(POINT)); + memset(points, 0, (os->lines + 1) * sizeof(POINT)); + + points[0].x = os->x; + points[0].y = os->y; + pen.style = pen.width = 0; + pen.colour = os->fgcolour; + + index = 0; + data = ((os->lines - 1) / 4) + 1; + for (next = 1; (next <= os->lines) && (data < os->datasize); next++) + { + if ((next - 1) % 4 == 0) + flags = os->data[index++]; + + if (~flags & 0x80) + points[next].x = parse_delta(os->data, &data); + + if (~flags & 0x40) + points[next].y = parse_delta(os->data, &data); + + flags <<= 2; + } + + if (next - 1 == os->lines) + ui_polyline(os->opcode - 1, points, os->lines + 1, &pen); + else + error("polyline parse error\n"); + + xfree(points); +} + +/* Process an ellipse order */ +static void +process_ellipse(STREAM s, ELLIPSE_ORDER * os, uint32 present, BOOL delta) +{ + if (present & 0x01) + rdp_in_coord(s, &os->left, delta); + + if (present & 0x02) + rdp_in_coord(s, &os->top, delta); + + if (present & 0x04) + rdp_in_coord(s, &os->right, delta); + + if (present & 0x08) + rdp_in_coord(s, &os->bottom, delta); + + if (present & 0x10) + in_uint8(s, os->opcode); + + if (present & 0x20) + in_uint8(s, os->fillmode); + + if (present & 0x40) + rdp_in_colour(s, &os->fgcolour); + + DEBUG(("ELLIPSE(l=%d,t=%d,r=%d,b=%d,op=0x%x,fm=%d,fg=0x%x)\n", os->left, os->top, + os->right, os->bottom, os->opcode, os->fillmode, os->fgcolour)); + + ui_ellipse(os->opcode - 1, os->fillmode, os->left, os->top, os->right - os->left, + os->bottom - os->top, NULL, 0, os->fgcolour); +} + +/* Process an ellipse2 order */ +static void +process_ellipse2(STREAM s, ELLIPSE2_ORDER * os, uint32 present, BOOL delta) +{ + if (present & 0x0001) + rdp_in_coord(s, &os->left, delta); + + if (present & 0x0002) + rdp_in_coord(s, &os->top, delta); + + if (present & 0x0004) + rdp_in_coord(s, &os->right, delta); + + if (present & 0x0008) + rdp_in_coord(s, &os->bottom, delta); + + if (present & 0x0010) + in_uint8(s, os->opcode); + + if (present & 0x0020) + in_uint8(s, os->fillmode); + + if (present & 0x0040) + rdp_in_colour(s, &os->bgcolour); + + if (present & 0x0080) + rdp_in_colour(s, &os->fgcolour); + + rdp_parse_brush(s, &os->brush, present >> 8); + + DEBUG(("ELLIPSE2(l=%d,t=%d,r=%d,b=%d,op=0x%x,fm=%d,bs=%d,bg=0x%x,fg=0x%x)\n", + os->left, os->top, os->right, os->bottom, os->opcode, os->fillmode, os->brush.style, + os->bgcolour, os->fgcolour)); + + ui_ellipse(os->opcode - 1, os->fillmode, os->left, os->top, os->right - os->left, + os->bottom - os->top, &os->brush, os->bgcolour, os->fgcolour); +} + +/* 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->opcode); + + 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); + + rdp_parse_brush(s, &os->brush, present >> 14); + + 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,br=%d,bb=%d,bs=%d,bg=0x%x,fg=0x%x,font=%d,fl=0x%x,op=0x%x,mix=%d,n=%d)\n", os->x, os->y, os->clipleft, os->cliptop, os->clipright, os->clipbottom, os->boxleft, os->boxtop, os->boxright, os->boxbottom, os->brush.style, os->bgcolour, os->fgcolour, os->font, os->flags, os->opcode, os->mixmode, 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->opcode - 1, 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->brush, 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, Bpp; + uint8 *data, *inverted; + int y; + + 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); + in_uint8p(s, data, bufsize); + + DEBUG(("RAW_BMPCACHE(cx=%d,cy=%d,id=%d,idx=%d)\n", width, height, cache_id, cache_idx)); + inverted = (uint8 *) xmalloc(width * height * Bpp); + for (y = 0; y < height; y++) + { + memcpy(&inverted[(height - y - 1) * (width * Bpp)], &data[y * (width * Bpp)], + width * Bpp); + } + + bitmap = ui_create_bitmap(width, height, inverted); + xfree(inverted); + 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, Bpp; + uint8 *data, *bmpdata; + uint16 bufsize, pad2, row_size, final_size; + uint8 pad1; + + pad2 = row_size = final_size = 0xffff; /* Shut the compiler up */ + + in_uint8(s, cache_id); + in_uint8(s, pad1); /* pad */ + in_uint8(s, width); + in_uint8(s, height); + in_uint8(s, bpp); + Bpp = (bpp + 7) / 8; + in_uint16_le(s, bufsize); /* bufsize */ + in_uint16_le(s, cache_idx); + + if (g_use_rdp5) + { + size = bufsize; + } + else + { + + /* Begin compressedBitmapData */ + in_uint16_le(s, pad2); /* pad */ + in_uint16_le(s, size); + /* in_uint8s(s, 4); *//* row_size, final_size */ + in_uint16_le(s, row_size); + in_uint16_le(s, final_size); + + } + in_uint8p(s, data, size); + + DEBUG(("BMPCACHE(cx=%d,cy=%d,id=%d,idx=%d,bpp=%d,size=%d,pad1=%d,bufsize=%d,pad2=%d,rs=%d,fs=%d)\n", width, height, cache_id, cache_idx, bpp, size, pad1, bufsize, pad2, row_size, final_size)); + + bmpdata = (uint8 *) xmalloc(width * height * Bpp); + + if (bitmap_decompress(bmpdata, width, height, data, size, Bpp)) + { + bitmap = ui_create_bitmap(width, height, bmpdata); + cache_put_bitmap(cache_id, cache_idx, bitmap); + } + else + { + DEBUG(("Failed to decompress bitmap data\n")); + } + + xfree(bmpdata); +} + +/* Process a bitmap cache v2 order */ +static void +process_bmpcache2(STREAM s, uint16 flags, BOOL compressed) +{ + HBITMAP bitmap; + int y; + uint8 cache_id, cache_idx_low, width, height, Bpp; + uint16 cache_idx, bufsize; + uint8 *data, *bmpdata, *bitmap_id; + + bitmap_id = NULL; /* prevent compiler warning */ + cache_id = flags & ID_MASK; + Bpp = ((flags & MODE_MASK) >> MODE_SHIFT) - 2; + + if (flags & PERSIST) + { + in_uint8p(s, bitmap_id, 8); + } + + if (flags & SQUARE) + { + in_uint8(s, width); + height = width; + } + else + { + in_uint8(s, width); + in_uint8(s, height); + } + + in_uint16_be(s, bufsize); + bufsize &= BUFSIZE_MASK; + in_uint8(s, cache_idx); + + if (cache_idx & LONG_FORMAT) + { + in_uint8(s, cache_idx_low); + cache_idx = ((cache_idx ^ LONG_FORMAT) << 8) + cache_idx_low; + } + + in_uint8p(s, data, bufsize); + + DEBUG(("BMPCACHE2(compr=%d,flags=%x,cx=%d,cy=%d,id=%d,idx=%d,Bpp=%d,bs=%d)\n", + compressed, flags, width, height, cache_id, cache_idx, Bpp, bufsize)); + + bmpdata = (uint8 *) xmalloc(width * height * Bpp); + + if (compressed) + { + if (!bitmap_decompress(bmpdata, width, height, data, bufsize, Bpp)) + { + DEBUG(("Failed to decompress bitmap data\n")); + xfree(bmpdata); + return; + } + } + else + { + for (y = 0; y < height; y++) + memcpy(&bmpdata[(height - y - 1) * (width * Bpp)], + &data[y * (width * Bpp)], width * Bpp); + } + + bitmap = ui_create_bitmap(width, height, bmpdata); + + if (bitmap) + { + cache_put_bitmap(cache_id, cache_idx, bitmap); + if (flags & PERSIST) + pstcache_save_bitmap(cache_id, cache_idx, bitmap_id, width, height, + width * height * Bpp, bmpdata); + } + else + { + DEBUG(("process_bmpcache2: ui_create_bitmap failed\n")); + } + + 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 = (COLOURENTRY *) xmalloc(sizeof(COLOURENTRY) * 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); + + if (cache_id) + ui_set_colourmap(hmap); + + xfree(map.colours); +} + +/* Process a font cache order */ +static void +process_fontcache(STREAM s) +{ + HGLYPH bitmap; + uint8 font, nglyphs; + uint16 character, offset, baseline, width, height; + int i, datasize; + uint8 *data; + + 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); + + bitmap = ui_create_glyph(width, height, data); + cache_put_font(font, character, offset, baseline, width, height, bitmap); + } +} + +/* Process a secondary order */ +static void +process_secondary_order(STREAM s) +{ + /* The length isn't calculated correctly by the server. + * For very compact orders the length becomes negative + * so a signed integer must be used. */ + uint16 length; + uint16 flags; + uint8 type; + uint8 *next_order; + + in_uint16_le(s, length); + in_uint16_le(s, flags); /* used by bmpcache2 */ + in_uint8(s, type); + + next_order = s->p + (sint16) 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; + + case RDP_ORDER_RAW_BMPCACHE2: + process_bmpcache2(s, flags, False); /* uncompressed */ + break; + + case RDP_ORDER_BMPCACHE2: + process_bmpcache2(s, flags, True); /* compressed */ + break; + + default: + unimpl("secondary order %d\n", type); + } + + s->p = next_order; +} + +/* Process an order PDU */ +void +process_orders(STREAM s, uint16 num_orders) +{ + RDP_ORDER_STATE *os = &g_order_state; + uint32 present; + uint8 order_flags; + int size, processed = 0; + BOOL delta; + + 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: + case RDP_ORDER_POLYGON2: + case RDP_ORDER_ELLIPSE2: + 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_POLYGON: + process_polygon(s, &os->polygon, present, delta); + break; + + case RDP_ORDER_POLYGON2: + process_polygon2(s, &os->polygon2, present, delta); + break; + + case RDP_ORDER_POLYLINE: + process_polyline(s, &os->polyline, present, delta); + break; + + case RDP_ORDER_ELLIPSE: + process_ellipse(s, &os->ellipse, present, delta); + break; + + case RDP_ORDER_ELLIPSE2: + process_ellipse2(s, &os->ellipse2, present, delta); + break; + + case RDP_ORDER_TEXT2: + process_text2(s, &os->text2, present, delta); + break; + + default: + unimpl("order %d\n", os->order_type); + return; + } + + if (order_flags & RDP_ORDER_BOUNDS) + ui_reset_clip(); + } + + processed++; + } +#if 0 + /* not true when RDP_COMPRESSION is set */ + if (s->p != g_next_packet) + error("%d bytes remaining\n", (int) (g_next_packet - s->p)); +#endif + +} + +/* Reset order state */ +void +reset_order_state(void) +{ + memset(&g_order_state, 0, sizeof(g_order_state)); + g_order_state.order_type = RDP_ORDER_PATBLT; +} diff --git a/uirdesktop/orders.h b/uirdesktop/orders.h new file mode 100644 index 00000000..b1272822 --- /dev/null +++ b/uirdesktop/orders.h @@ -0,0 +1,368 @@ +/* + rdesktop: A Remote Desktop Protocol client. + RDP order processing + Copyright (C) Matthew Chapman 1999-2005 + + 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. +*/ + +#define RDP_ORDER_STANDARD 0x01 +#define RDP_ORDER_SECONDARY 0x02 +#define RDP_ORDER_BOUNDS 0x04 +#define RDP_ORDER_CHANGE 0x08 +#define RDP_ORDER_DELTA 0x10 +#define RDP_ORDER_LASTBOUNDS 0x20 +#define RDP_ORDER_SMALL 0x40 +#define RDP_ORDER_TINY 0x80 + +enum RDP_ORDER_TYPE +{ + RDP_ORDER_DESTBLT = 0, + RDP_ORDER_PATBLT = 1, + RDP_ORDER_SCREENBLT = 2, + RDP_ORDER_LINE = 9, + RDP_ORDER_RECT = 10, + RDP_ORDER_DESKSAVE = 11, + RDP_ORDER_MEMBLT = 13, + RDP_ORDER_TRIBLT = 14, + RDP_ORDER_POLYGON = 20, + RDP_ORDER_POLYGON2 = 21, + RDP_ORDER_POLYLINE = 22, + RDP_ORDER_ELLIPSE = 25, + RDP_ORDER_ELLIPSE2 = 26, + RDP_ORDER_TEXT2 = 27 +}; + +enum RDP_SECONDARY_ORDER_TYPE +{ + RDP_ORDER_RAW_BMPCACHE = 0, + RDP_ORDER_COLCACHE = 1, + RDP_ORDER_BMPCACHE = 2, + RDP_ORDER_FONTCACHE = 3, + RDP_ORDER_RAW_BMPCACHE2 = 4, + RDP_ORDER_BMPCACHE2 = 5, + RDP_ORDER_BRUSHCACHE = 7 +}; + +typedef struct _DESTBLT_ORDER +{ + sint16 x; + sint16 y; + sint16 cx; + sint16 cy; + uint8 opcode; + +} +DESTBLT_ORDER; + +typedef struct _PATBLT_ORDER +{ + sint16 x; + sint16 y; + sint16 cx; + sint16 cy; + uint8 opcode; + uint32 bgcolour; + uint32 fgcolour; + BRUSH brush; + +} +PATBLT_ORDER; + +typedef struct _SCREENBLT_ORDER +{ + sint16 x; + sint16 y; + sint16 cx; + sint16 cy; + uint8 opcode; + sint16 srcx; + sint16 srcy; + +} +SCREENBLT_ORDER; + +typedef struct _LINE_ORDER +{ + uint16 mixmode; + sint16 startx; + sint16 starty; + sint16 endx; + sint16 endy; + uint32 bgcolour; + uint8 opcode; + PEN pen; + +} +LINE_ORDER; + +typedef struct _RECT_ORDER +{ + sint16 x; + sint16 y; + sint16 cx; + sint16 cy; + uint32 colour; + +} +RECT_ORDER; + +typedef struct _DESKSAVE_ORDER +{ + uint32 offset; + sint16 left; + sint16 top; + sint16 right; + sint16 bottom; + uint8 action; + +} +DESKSAVE_ORDER; + +typedef struct _TRIBLT_ORDER +{ + uint8 colour_table; + uint8 cache_id; + sint16 x; + sint16 y; + sint16 cx; + sint16 cy; + uint8 opcode; + sint16 srcx; + sint16 srcy; + uint32 bgcolour; + uint32 fgcolour; + BRUSH brush; + uint16 cache_idx; + uint16 unknown; + +} +TRIBLT_ORDER; + +typedef struct _MEMBLT_ORDER +{ + uint8 colour_table; + uint8 cache_id; + sint16 x; + sint16 y; + sint16 cx; + sint16 cy; + uint8 opcode; + sint16 srcx; + sint16 srcy; + uint16 cache_idx; + +} +MEMBLT_ORDER; + +#define MAX_DATA 256 + +typedef struct _POLYGON_ORDER +{ + sint16 x; + sint16 y; + uint8 opcode; + uint8 fillmode; + uint32 fgcolour; + uint8 npoints; + uint8 datasize; + uint8 data[MAX_DATA]; + +} +POLYGON_ORDER; + +typedef struct _POLYGON2_ORDER +{ + sint16 x; + sint16 y; + uint8 opcode; + uint8 fillmode; + uint32 bgcolour; + uint32 fgcolour; + BRUSH brush; + uint8 npoints; + uint8 datasize; + uint8 data[MAX_DATA]; + +} +POLYGON2_ORDER; + +typedef struct _POLYLINE_ORDER +{ + sint16 x; + sint16 y; + uint8 opcode; + uint32 fgcolour; + uint8 lines; + uint8 datasize; + uint8 data[MAX_DATA]; + +} +POLYLINE_ORDER; + +typedef struct _ELLIPSE_ORDER +{ + sint16 left; + sint16 top; + sint16 right; + sint16 bottom; + uint8 opcode; + uint8 fillmode; + uint32 fgcolour; + +} +ELLIPSE_ORDER; + +typedef struct _ELLIPSE2_ORDER +{ + sint16 left; + sint16 top; + sint16 right; + sint16 bottom; + uint8 opcode; + uint8 fillmode; + BRUSH brush; + uint32 bgcolour; + uint32 fgcolour; + +} +ELLIPSE2_ORDER; + +#define MAX_TEXT 256 + +typedef struct _TEXT2_ORDER +{ + uint8 font; + uint8 flags; + uint8 opcode; + uint8 mixmode; + uint32 bgcolour; + uint32 fgcolour; + sint16 clipleft; + sint16 cliptop; + sint16 clipright; + sint16 clipbottom; + sint16 boxleft; + sint16 boxtop; + sint16 boxright; + sint16 boxbottom; + BRUSH brush; + sint16 x; + sint16 y; + uint8 length; + uint8 text[MAX_TEXT]; + +} +TEXT2_ORDER; + +typedef struct _RDP_ORDER_STATE +{ + uint8 order_type; + BOUNDS bounds; + + DESTBLT_ORDER destblt; + PATBLT_ORDER patblt; + SCREENBLT_ORDER screenblt; + LINE_ORDER line; + RECT_ORDER rect; + DESKSAVE_ORDER desksave; + MEMBLT_ORDER memblt; + TRIBLT_ORDER triblt; + POLYGON_ORDER polygon; + POLYGON2_ORDER polygon2; + POLYLINE_ORDER polyline; + ELLIPSE_ORDER ellipse; + ELLIPSE2_ORDER ellipse2; + TEXT2_ORDER text2; + +} +RDP_ORDER_STATE; + +typedef struct _RDP_RAW_BMPCACHE_ORDER +{ + uint8 cache_id; + uint8 pad1; + uint8 width; + uint8 height; + uint8 bpp; + uint16 bufsize; + uint16 cache_idx; + uint8 *data; + +} +RDP_RAW_BMPCACHE_ORDER; + +typedef struct _RDP_BMPCACHE_ORDER +{ + uint8 cache_id; + uint8 pad1; + uint8 width; + uint8 height; + uint8 bpp; + uint16 bufsize; + uint16 cache_idx; + uint16 pad2; + uint16 size; + uint16 row_size; + uint16 final_size; + uint8 *data; + +} +RDP_BMPCACHE_ORDER; + +/* RDP_BMPCACHE2_ORDER */ +#define ID_MASK 0x0007 +#define MODE_MASK 0x0038 +#define SQUARE 0x0080 +#define PERSIST 0x0100 +#define FLAG_51_UNKNOWN 0x0800 + +#define MODE_SHIFT 3 + +#define LONG_FORMAT 0x80 +#define BUFSIZE_MASK 0x3FFF /* or 0x1FFF? */ + +#define MAX_GLYPH 32 + +typedef struct _RDP_FONT_GLYPH +{ + uint16 character; + uint16 unknown; + uint16 baseline; + uint16 width; + uint16 height; + uint8 data[MAX_GLYPH]; + +} +RDP_FONT_GLYPH; + +#define MAX_GLYPHS 256 + +typedef struct _RDP_FONTCACHE_ORDER +{ + uint8 font; + uint8 nglyphs; + RDP_FONT_GLYPH glyphs[MAX_GLYPHS]; + +} +RDP_FONTCACHE_ORDER; + +typedef struct _RDP_COLCACHE_ORDER +{ + uint8 cache_id; + COLOURMAP map; + +} +RDP_COLCACHE_ORDER; diff --git a/uirdesktop/pstcache.c b/uirdesktop/pstcache.c new file mode 100644 index 00000000..9e6432bd --- /dev/null +++ b/uirdesktop/pstcache.c @@ -0,0 +1,200 @@ +/* -*- c-basic-offset: 8 -*- + rdesktop: A Remote Desktop Protocol client. + Persistent Bitmap Cache routines + Copyright (C) Jeroen Meijer 2004-2005 + + 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" + +#define MAX_CELL_SIZE 0x1000 /* pixels */ + +#define IS_PERSISTENT(id) (id < 8 && g_pstcache_fd[id] > 0) + +extern int g_server_depth; +extern BOOL g_bitmap_cache; +extern BOOL g_bitmap_cache_persist_enable; +extern BOOL g_bitmap_cache_precache; + +int g_pstcache_fd[8]; +int g_pstcache_Bpp; +BOOL g_pstcache_enumerated = False; +uint8 zero_key[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + +/* Update mru stamp/index for a bitmap */ +void +pstcache_touch_bitmap(uint8 cache_id, uint16 cache_idx, uint32 stamp) +{ + int fd; + + if (!IS_PERSISTENT(cache_id) || cache_idx >= BMPCACHE2_NUM_PSTCELLS) + return; + + fd = g_pstcache_fd[cache_id]; + rd_lseek_file(fd, 12 + cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER))); + rd_write_file(fd, &stamp, sizeof(stamp)); +} + +/* Load a bitmap from the persistent cache */ +BOOL +pstcache_load_bitmap(uint8 cache_id, uint16 cache_idx) +{ + uint8 *celldata; + int fd; + CELLHEADER cellhdr; + HBITMAP bitmap; + + if (!g_bitmap_cache_persist_enable) + return False; + + if (!IS_PERSISTENT(cache_id) || cache_idx >= BMPCACHE2_NUM_PSTCELLS) + return False; + + fd = g_pstcache_fd[cache_id]; + rd_lseek_file(fd, cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER))); + rd_read_file(fd, &cellhdr, sizeof(CELLHEADER)); + celldata = (uint8 *) xmalloc(cellhdr.length); + rd_read_file(fd, celldata, cellhdr.length); + + bitmap = ui_create_bitmap(cellhdr.width, cellhdr.height, celldata); + DEBUG(("Load bitmap from disk: id=%d, idx=%d, bmp=0x%x)\n", cache_id, cache_idx, bitmap)); + cache_put_bitmap(cache_id, cache_idx, bitmap); + + xfree(celldata); + return True; +} + +/* Store a bitmap in the persistent cache */ +BOOL +pstcache_save_bitmap(uint8 cache_id, uint16 cache_idx, uint8 * key, + uint8 width, uint8 height, uint16 length, uint8 * data) +{ + int fd; + CELLHEADER cellhdr; + + if (!IS_PERSISTENT(cache_id) || cache_idx >= BMPCACHE2_NUM_PSTCELLS) + return False; + + memcpy(cellhdr.key, key, sizeof(HASH_KEY)); + cellhdr.width = width; + cellhdr.height = height; + cellhdr.length = length; + cellhdr.stamp = 0; + + fd = g_pstcache_fd[cache_id]; + rd_lseek_file(fd, cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER))); + rd_write_file(fd, &cellhdr, sizeof(CELLHEADER)); + rd_write_file(fd, data, length); + + return True; +} + +/* List the bitmap keys from the persistent cache file */ +int +pstcache_enumerate(uint8 id, HASH_KEY * keylist) +{ + int fd, n; + uint16 idx; + sint16 mru_idx[0xa00]; + uint32 mru_stamp[0xa00]; + CELLHEADER cellhdr; + + if (!(g_bitmap_cache && g_bitmap_cache_persist_enable && IS_PERSISTENT(id))) + return 0; + + /* The server disconnects if the bitmap cache content is sent more than once */ + if (g_pstcache_enumerated) + return 0; + + DEBUG_RDP5(("Persistent bitmap cache enumeration... ")); + for (idx = 0; idx < BMPCACHE2_NUM_PSTCELLS; idx++) + { + fd = g_pstcache_fd[id]; + rd_lseek_file(fd, idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER))); + if (rd_read_file(fd, &cellhdr, sizeof(CELLHEADER)) <= 0) + break; + + if (memcmp(cellhdr.key, zero_key, sizeof(HASH_KEY)) != 0) + { + memcpy(keylist[idx], cellhdr.key, sizeof(HASH_KEY)); + + /* Pre-cache (not possible for 8 bit colour depth cause it needs a colourmap) */ + if (g_bitmap_cache_precache && cellhdr.stamp && g_server_depth > 8) + pstcache_load_bitmap(id, idx); + + /* Sort by stamp */ + for (n = idx; n > 0 && cellhdr.stamp < mru_stamp[n - 1]; n--) + { + mru_idx[n] = mru_idx[n - 1]; + mru_stamp[n] = mru_stamp[n - 1]; + } + + mru_idx[n] = idx; + mru_stamp[n] = cellhdr.stamp; + } + else + { + break; + } + } + + DEBUG_RDP5(("%d cached bitmaps.\n", idx)); + + cache_rebuild_bmpcache_linked_list(id, mru_idx, idx); + g_pstcache_enumerated = True; + return idx; +} + +/* initialise the persistent bitmap cache */ +BOOL +pstcache_init(uint8 cache_id) +{ + int fd; + char filename[256]; + + if (g_pstcache_enumerated) + return True; + + g_pstcache_fd[cache_id] = 0; + + if (!(g_bitmap_cache && g_bitmap_cache_persist_enable)) + return False; + + if (!rd_pstcache_mkdir()) + { + DEBUG(("failed to get/make cache directory!\n")); + return False; + } + + g_pstcache_Bpp = (g_server_depth + 7) / 8; + sprintf(filename, "cache/pstcache_%d_%d", cache_id, g_pstcache_Bpp); + DEBUG(("persistent bitmap cache file: %s\n", filename)); + + fd = rd_open_file(filename); + if (fd == -1) + return False; + + if (!rd_lock_file(fd, 0, 0)) + { + warning("Persistent bitmap caching is disabled. (The file is already in use)\n"); + rd_close_file(fd); + return False; + } + + g_pstcache_fd[cache_id] = fd; + return True; +}