/* Orders.java * Component: ProperJavaRDP * * Revision: $Revision: 1.7 $ * Author: $Author: telliott $ * Date: $Date: 2005/09/27 14:15:39 $ * * Copyright (c) 2005 Propero Limited * * Purpose: Encapsulates an RDP order * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * (See gpl.txt for details of the GNU General Public License.) * */ package org.jopenray.rdp; import org.apache.log4j.Logger; import org.jopenray.rdp.orders.*; import java.awt.image.IndexColorModel; import java.io.IOException; public class Orders { static Logger logger = Logger.getLogger(Orders.class); private OrderState os = null; private RDPCanvas surface = null; public static Cache cache = null; /* RDP_BMPCACHE2_ORDER */ private static final int ID_MASK = 0x0007; private static final int MODE_MASK = 0x0038; private static final int SQUARE = 0x0080; private static final int PERSIST = 0x0100; private static final int FLAG_51_UNKNOWN = 0x0800; private static final int MODE_SHIFT = 3; private static final int LONG_FORMAT = 0x80; private static final int BUFSIZE_MASK = 0x3FFF; /* or 0x1FFF? */ private static final int RDP_ORDER_STANDARD = 0x01; private static final int RDP_ORDER_SECONDARY = 0x02; private static final int RDP_ORDER_BOUNDS = 0x04; private static final int RDP_ORDER_CHANGE = 0x08; private static final int RDP_ORDER_DELTA = 0x10; private static final int RDP_ORDER_LASTBOUNDS = 0x20; private static final int RDP_ORDER_SMALL = 0x40; private static final int RDP_ORDER_TINY = 0x80; /* standard order types */ private static final int RDP_ORDER_DESTBLT = 0; private static final int RDP_ORDER_PATBLT = 1; private static final int RDP_ORDER_SCREENBLT = 2; private static final int RDP_ORDER_LINE = 9; private static final int RDP_ORDER_RECT = 10; private static final int RDP_ORDER_DESKSAVE = 11; private static final int RDP_ORDER_MEMBLT = 13; private static final int RDP_ORDER_TRIBLT = 14; private static final int RDP_ORDER_POLYLINE = 22; private static final int RDP_ORDER_TEXT2 = 27; private int rect_colour; /* secondary order types */ private static final int RDP_ORDER_RAW_BMPCACHE = 0; private static final int RDP_ORDER_COLCACHE = 1; private static final int RDP_ORDER_BMPCACHE = 2; private static final int RDP_ORDER_FONTCACHE = 3; private static final int RDP_ORDER_RAW_BMPCACHE2 = 4; private static final int RDP_ORDER_BMPCACHE2 = 5; private static final int MIX_TRANSPARENT = 0; private static final int MIX_OPAQUE = 1; private static final int TEXT2_VERTICAL = 0x04; private static final int TEXT2_IMPLICIT_X = 0x20; public Orders() { os = new OrderState(); } public void resetOrderState() { this.os.reset(); os.setOrderType(RDP_ORDER_PATBLT); return; } private int inPresent(RdpPacket_Localised data, int flags, int size) { int present = 0; int bits = 0; int i = 0; if ((flags & RDP_ORDER_SMALL) != 0) { size--; } if ((flags & RDP_ORDER_TINY) != 0) { if (size < 2) { size = 0; } else { size -= 2; } } for (i = 0; i < size; i++) { bits = data.get8(); present |= (bits << (i * 8)); } return present; } /** * Process a set of orders sent by the server * @param data Packet packet containing orders * @param next_packet Offset of end of this packet (start of next) * @param n_orders Number of orders sent in this packet * @throws OrderException * @throws RdesktopException */ public void processOrders(RdpPacket_Localised data, int next_packet, int n_orders) throws OrderException, RdesktopException { int present = 0; // int n_orders = 0; int order_flags = 0, order_type = 0; int size = 0, processed = 0; boolean delta; while (processed < n_orders) { order_flags = data.get8(); if ((order_flags & RDP_ORDER_STANDARD) == 0) { throw new OrderException("Order parsing failed!"); } if ((order_flags & RDP_ORDER_SECONDARY) != 0) { this.processSecondaryOrders(data); } else { if ((order_flags & RDP_ORDER_CHANGE) != 0) { os.setOrderType(data.get8()); } switch (os.getOrderType()) { 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; } present = this.inPresent(data, order_flags, size); if ((order_flags & RDP_ORDER_BOUNDS) != 0) { if ((order_flags & RDP_ORDER_LASTBOUNDS) == 0) { this.parseBounds(data, os.getBounds()); } surface.setClip(os.getBounds()); } delta = ((order_flags & RDP_ORDER_DELTA) != 0); switch (os.getOrderType()) { case RDP_ORDER_DESTBLT: logger.debug("DestBlt Order"); this.processDestBlt(data, os.getDestBlt(), present, delta); break; case RDP_ORDER_PATBLT: logger.debug("PatBlt Order"); this.processPatBlt(data, os.getPatBlt(), present, delta); break; case RDP_ORDER_SCREENBLT: logger.debug("ScreenBlt Order"); this.processScreenBlt(data, os.getScreenBlt(), present, delta); break; case RDP_ORDER_LINE: logger.debug("Line Order"); this.processLine(data, os.getLine(), present, delta); break; case RDP_ORDER_RECT: logger.debug("Rectangle Order"); this.processRectangle(data, os.getRectangle(), present, delta); break; case RDP_ORDER_DESKSAVE: logger.debug("Desksave!"); this .processDeskSave(data, os.getDeskSave(), present, delta); break; case RDP_ORDER_MEMBLT: logger.debug("MemBlt Order"); this.processMemBlt(data, os.getMemBlt(), present, delta); break; case RDP_ORDER_TRIBLT: logger.debug("TriBlt Order"); this.processTriBlt(data, os.getTriBlt(), present, delta); break; case RDP_ORDER_POLYLINE: logger.debug("Polyline Order"); this .processPolyLine(data, os.getPolyLine(), present, delta); break; case RDP_ORDER_TEXT2: logger.debug("Text2 Order"); this.processText2(data, os.getText2(), present, delta); break; default: logger.warn("Unimplemented Order type " + order_type); return; } if ((order_flags & RDP_ORDER_BOUNDS) != 0) { surface.resetClip(); logger.debug("Reset clip"); } } processed++; } if (data.getPosition() != next_packet) { throw new OrderException("End not reached!"); } } private int ROP2_S(int rop3) { return (rop3 & 0x0f); } private int ROP2_P(int rop3) { return ((rop3 & 0x3) | ((rop3 & 0x30) >> 2)); } /** * Register an RdesktopCanvas with this Orders object. * This surface is where all drawing orders will be carried * out. * @param surface Surface to register */ public void registerDrawingSurface(RDPCanvas surface) { this.surface = surface; surface.registerCache(cache); } /** * Handle secondary, or caching, orders * @param data Packet containing secondary order * @throws OrderException * @throws RdesktopException */ private void processSecondaryOrders(RdpPacket_Localised data) throws OrderException, RdesktopException { int length = 0; int type = 0; int flags = 0; int next_order = 0; length = data.getLittleEndian16(); flags = data.getLittleEndian16(); type = data.get8(); next_order = data.getPosition() + length + 7; switch (type) { case RDP_ORDER_RAW_BMPCACHE: logger.debug("Raw BitmapCache Order"); this.processRawBitmapCache(data); break; case RDP_ORDER_COLCACHE: logger.debug("Colorcache Order"); this.processColorCache(data); break; case RDP_ORDER_BMPCACHE: logger.debug("Bitmapcache Order"); this.processBitmapCache(data); break; case RDP_ORDER_FONTCACHE: logger.debug("Fontcache Order"); this.processFontCache(data); break; case RDP_ORDER_RAW_BMPCACHE2: try { this.process_bmpcache2(data, flags, false); } catch (IOException e) { throw new RdesktopException(e.getMessage()); } /* uncompressed */ break; case RDP_ORDER_BMPCACHE2: try { this.process_bmpcache2(data, flags, true); } catch (IOException e) { throw new RdesktopException(e.getMessage()); } /* compressed */ break; default: logger.warn("Unimplemented 2ry Order type " + type); } data.setPosition(next_order); } /** * Process a raw bitmap and store it in the bitmap cache * @param data Packet containing raw bitmap data * @throws RdesktopException */ private void processRawBitmapCache(RdpPacket_Localised data) throws RdesktopException { int cache_id = data.get8(); data.incrementPosition(1); // pad int width = data.get8(); int height = data.get8(); int bpp = data.get8(); int Bpp = (bpp + 7) / 8; int bufsize = data.getLittleEndian16(); int cache_idx = data.getLittleEndian16(); int pdata = data.getPosition(); data.incrementPosition(bufsize); byte[] inverted = new byte[width * height * Bpp]; int pinverted = (height - 1) * (width * Bpp); for (int y = 0; y < height; y++) { data.copyToByteArray(inverted, pinverted, pdata, width * Bpp); // data.copyToByteArray(inverted, (height - y - 1) * (width * Bpp), // y * (width * Bpp), width*Bpp); pinverted -= width * Bpp; pdata += width * Bpp; } cache.putBitmap(cache_id, cache_idx, new Bitmap(Bitmap.convertImage( inverted, Bpp), width, height, 0, 0), 0); } /** * Process and store details of a colour cache * @param data Packet containing cache information * @throws RdesktopException */ private void processColorCache(RdpPacket_Localised data) throws RdesktopException { byte[] palette = null; byte[] red = null; byte[] green = null; byte[] blue = null; int j = 0; int cache_id = data.get8(); int n_colors = data.getLittleEndian16(); // Number of Colors in // Palette palette = new byte[n_colors * 4]; red = new byte[n_colors]; green = new byte[n_colors]; blue = new byte[n_colors]; data.copyToByteArray(palette, 0, data.getPosition(), palette.length); data.incrementPosition(palette.length); for (int i = 0; i < n_colors; i++) { blue[i] = palette[j]; green[i] = palette[j + 1]; red[i] = palette[j + 2]; // palette[j+3] is pad j += 4; } IndexColorModel cm = new IndexColorModel(8, n_colors, red, green, blue); cache.put_colourmap(cache_id, cm); // surface.registerPalette(cm); } /** * Process a compressed bitmap and store in the bitmap cache * @param data Packet containing compressed bitmap * @throws RdesktopException */ private void processBitmapCache(RdpPacket_Localised data) throws RdesktopException { int bufsize, pad2, row_size, final_size, size; int pad1; bufsize = pad2 = row_size = final_size = size = 0; int cache_id = data.get8(); pad1 = data.get8(); // pad int width = data.get8(); int height = data.get8(); int bpp = data.get8(); int Bpp = (bpp + 7) / 8; bufsize = data.getLittleEndian16(); // bufsize int cache_idx = data.getLittleEndian16(); /* * data.incrementPosition(2); // pad int size = * data.getLittleEndian16(); data.incrementPosition(4); // row_size, * final_size */ if (Options.use_rdp5) { /* Begin compressedBitmapData */ pad2 = data.getLittleEndian16(); // in_uint16_le(s, pad2); /* pad // */ size = data.getLittleEndian16(); // in_uint16_le(s, size); row_size = data.getLittleEndian16(); // in_uint16_le(s, // row_size); final_size = data.getLittleEndian16(); // in_uint16_le(s, // final_size); } else { data.incrementPosition(2); // pad size = data.getLittleEndian16(); row_size = data.getLittleEndian16(); // in_uint16_le(s, // row_size); final_size = data.getLittleEndian16(); // in_uint16_le(s, // final_size); // this is what's in rdesktop, but doesn't seem to work // size = bufsize; } // logger.info("BMPCACHE(cx=" + width + ",cy=" + height + ",id=" + // cache_id + ",idx=" + cache_idx + ",bpp=" + bpp + ",size=" + size + // ",pad1=" + pad1 + ",bufsize=" + bufsize + ",pad2=" + pad2 + ",rs=" + // row_size + ",fs=" + final_size + ")"); if (Bpp == 1) { byte[] pixel = Bitmap.decompress(width, height, size, data, Bpp); if (pixel != null) cache.putBitmap(cache_id, cache_idx, new Bitmap(Bitmap .convertImage(pixel, Bpp), width, height, 0, 0), 0); else logger.warn("Failed to decompress bitmap"); } else { int[] pixel = Bitmap.decompressInt(width, height, size, data, Bpp); if (pixel != null) cache.putBitmap(cache_id, cache_idx, new Bitmap(pixel, width, height, 0, 0), 0); else logger.warn("Failed to decompress bitmap"); } } /* Process a bitmap cache v2 order */ /** * Process a bitmap cache v2 order, storing a bitmap in the main cache, and * the persistant cache if so required * @param data Packet containing order and bitmap data * @param flags Set of flags defining mode of order * @param compressed True if bitmap data is compressed * @throws RdesktopException * @throws IOException */ private void process_bmpcache2(RdpPacket_Localised data, int flags, boolean compressed) throws RdesktopException, IOException { Bitmap bitmap; int y; int cache_id, cache_idx_low, width, height, Bpp; int cache_idx, bufsize; byte[] bmpdata, bitmap_id; bitmap_id = new byte[8]; /* prevent compiler warning */ cache_id = flags & ID_MASK; Bpp = ((flags & MODE_MASK) >> MODE_SHIFT) - 2; Bpp = Options.Bpp; if ((flags & PERSIST) != 0) { bitmap_id = new byte[8]; data.copyToByteArray(bitmap_id, 0, data.getPosition(), 8); } if ((flags & SQUARE) != 0) { width = data.get8(); // in_uint8(s, width); height = width; } else { width = data.get8(); // in_uint8(s, width); height = data.get8(); // in_uint8(s, height); } bufsize = data.getBigEndian16(); // in_uint16_be(s, bufsize); bufsize &= BUFSIZE_MASK; cache_idx = data.get8(); // in_uint8(s, cache_idx); if ((cache_idx & LONG_FORMAT) != 0) { cache_idx_low = data.get8(); // in_uint8(s, cache_idx_low); cache_idx = ((cache_idx ^ LONG_FORMAT) << 8) + cache_idx_low; } // in_uint8p(s, data, bufsize); logger.info("BMPCACHE2(compr=" + compressed + ",flags=" + flags + ",cx=" + width + ",cy=" + height + ",id=" + cache_id + ",idx=" + cache_idx + ",Bpp=" + Bpp + ",bs=" + bufsize + ")"); bmpdata = new byte[width * height * Bpp]; int[] bmpdataInt = new int[width * height]; if (compressed) { if (Bpp == 1) bmpdataInt = Bitmap.convertImage(Bitmap.decompress(width, height, bufsize, data, Bpp), Bpp); else bmpdataInt = Bitmap.decompressInt(width, height, bufsize, data, Bpp); if (bmpdataInt == null) { logger.debug("Failed to decompress bitmap data"); // xfree(bmpdata); return; } bitmap = new Bitmap(bmpdataInt, width, height, 0, 0); } else { for (y = 0; y < height; y++) data.copyToByteArray(bmpdata, y * (width * Bpp), (height - y - 1) * (width * Bpp), width * Bpp); // memcpy(&bmpdata[(height // - y - // 1) * // (width // * // Bpp)], // &data[y // * // (width // * // Bpp)], // width // * // Bpp); bitmap = new Bitmap(Bitmap.convertImage(bmpdata, Bpp), width, height, 0, 0); } // bitmap = ui_create_bitmap(width, height, bmpdata); if (bitmap != null) { cache.putBitmap(cache_id, cache_idx, bitmap, 0); // cache_put_bitmap(cache_id, cache_idx, bitmap, 0); if ((flags & PERSIST) != 0) PstCache.pstcache_put_bitmap(cache_id, cache_idx, bitmap_id, width, height, width * height * Bpp, bmpdata); } else { logger.debug("process_bmpcache2: ui_create_bitmap failed"); } // xfree(bmpdata); } /** * Process a font caching order, and store font in the cache * @param data Packet containing font cache order, with data for a series of glyphs representing a font * @throws RdesktopException */ private void processFontCache(RdpPacket_Localised data) throws RdesktopException { Glyph glyph = null; int font = 0, nglyphs = 0; int character = 0, offset = 0, baseline = 0, width = 0, height = 0; int datasize = 0; byte[] fontdata = null; font = data.get8(); nglyphs = data.get8(); for (int i = 0; i < nglyphs; i++) { character = data.getLittleEndian16(); offset = data.getLittleEndian16(); baseline = data.getLittleEndian16(); width = data.getLittleEndian16(); height = data.getLittleEndian16(); datasize = (height * ((width + 7) / 8) + 3) & ~3; fontdata = new byte[datasize]; data.copyToByteArray(fontdata, 0, data.getPosition(), datasize); data.incrementPosition(datasize); glyph = new Glyph(font, character, offset, baseline, width, height, fontdata); cache.putFont(glyph); } } /** * Process a dest blit order, and perform blit on drawing surface * @param data Packet containing description of the order * @param destblt DestBltOrder object in which to store the blit description * @param present Flags defining the information available in the packet * @param delta True if the coordinates of the blit destination are described as relative to the source */ private void processDestBlt(RdpPacket_Localised data, DestBltOrder destblt, int present, boolean delta) { if ((present & 0x01) != 0) destblt.setX(setCoordinate(data, destblt.getX(), delta)); if ((present & 0x02) != 0) destblt.setY(setCoordinate(data, destblt.getY(), delta)); if ((present & 0x04) != 0) destblt.setCX(setCoordinate(data, destblt.getCX(), delta)); if ((present & 0x08) != 0) destblt.setCY(setCoordinate(data, destblt.getCY(), delta)); if ((present & 0x10) != 0) destblt.setOpcode(ROP2_S(data.get8())); // if(logger.isInfoEnabled()) // logger.info("opcode="+destblt.getOpcode()); surface.drawDestBltOrder(destblt); } /** * Parse data defining a brush and store brush information * @param data Packet containing brush data * @param brush Brush object in which to store the brush description * @param present Flags defining the information available within the packet */ private void parseBrush(RdpPacket_Localised data, Brush brush, int present) { if ((present & 0x01) != 0) brush.setXOrigin(data.get8()); if ((present & 0x02) != 0) brush.setXOrigin(data.get8()); if ((present & 0x04) != 0) brush.setStyle(data.get8()); byte[] pat = brush.getPattern(); if ((present & 0x08) != 0) pat[0] = (byte) data.get8(); if ((present & 0x10) != 0) for (int i = 1; i < 8; i++) pat[i] = (byte) data.get8(); brush.setPattern(pat); } /** * Parse data describing a pattern blit, and perform blit on drawing surface * @param data Packet containing blit data * @param patblt PatBltOrder object in which to store the blit description * @param present Flags defining the information available within the packet * @param delta True if the coordinates of the blit destination are described as relative to the source */ private void processPatBlt(RdpPacket_Localised data, PatBltOrder patblt, int present, boolean delta) { if ((present & 0x01) != 0) patblt.setX(setCoordinate(data, patblt.getX(), delta)); if ((present & 0x02) != 0) patblt.setY(setCoordinate(data, patblt.getY(), delta)); if ((present & 0x04) != 0) patblt.setCX(setCoordinate(data, patblt.getCX(), delta)); if ((present & 0x08) != 0) patblt.setCY(setCoordinate(data, patblt.getCY(), delta)); if ((present & 0x10) != 0) patblt.setOpcode(ROP2_P(data.get8())); if ((present & 0x20) != 0) patblt.setBackgroundColor(setColor(data)); if ((present & 0x40) != 0) patblt.setForegroundColor(setColor(data)); parseBrush(data, patblt.getBrush(), present >> 7); // if(logger.isInfoEnabled()) logger.info("opcode="+patblt.getOpcode()); surface.drawPatBltOrder(patblt); } /** * Parse data describing a screen blit, and perform blit on drawing surface * @param data Packet containing blit data * @param screenblt ScreenBltOrder object in which to store blit description * @param present Flags defining the information available within the packet * @param delta True if the coordinates of the blit destination are described as relative to the source */ private void processScreenBlt(RdpPacket_Localised data, ScreenBltOrder screenblt, int present, boolean delta) { if ((present & 0x01) != 0) screenblt.setX(setCoordinate(data, screenblt.getX(), delta)); if ((present & 0x02) != 0) screenblt.setY(setCoordinate(data, screenblt.getY(), delta)); if ((present & 0x04) != 0) screenblt.setCX(setCoordinate(data, screenblt.getCX(), delta)); if ((present & 0x08) != 0) screenblt.setCY(setCoordinate(data, screenblt.getCY(), delta)); if ((present & 0x10) != 0) screenblt.setOpcode(ROP2_S(data.get8())); if ((present & 0x20) != 0) screenblt.setSrcX(setCoordinate(data, screenblt.getSrcX(), delta)); if ((present & 0x40) != 0) screenblt.setSrcY(setCoordinate(data, screenblt.getSrcY(), delta)); // if(logger.isInfoEnabled()) // logger.info("opcode="+screenblt.getOpcode()); surface.drawScreenBltOrder(screenblt); } /** * Parse data describing a line order, and draw line on drawing surface * @param data Packet containing line order data * @param line LineOrder object describing the line drawing operation * @param present Flags defining the information available within the packet * @param delta True if the coordinates of the end of the line are defined as relative to the start */ private void processLine(RdpPacket_Localised data, LineOrder line, int present, boolean delta) { if ((present & 0x01) != 0) line.setMixmode(data.getLittleEndian16()); if ((present & 0x02) != 0) line.setStartX(setCoordinate(data, line.getStartX(), delta)); if ((present & 0x04) != 0) line.setStartY(setCoordinate(data, line.getStartY(), delta)); if ((present & 0x08) != 0) line.setEndX(setCoordinate(data, line.getEndX(), delta)); if ((present & 0x10) != 0) line.setEndY(setCoordinate(data, line.getEndY(), delta)); if ((present & 0x20) != 0) line.setBackgroundColor(setColor(data)); if ((present & 0x40) != 0) line.setOpcode(data.get8()); parsePen(data, line.getPen(), present >> 7); // if(logger.isInfoEnabled()) logger.info("Line from // ("+line.getStartX()+","+line.getStartY()+") to // ("+line.getEndX()+","+line.getEndY()+")"); if (line.getOpcode() < 0x01 || line.getOpcode() > 0x10) { logger.warn("bad ROP2 0x" + line.getOpcode()); return; } // now draw the line surface.drawLineOrder(line); } /** * Parse data describing a rectangle order, and draw the rectangle to the drawing surface * @param data Packet containing rectangle order * @param rect RectangleOrder object in which to store order description * @param present Flags defining information available in packet * @param delta True if the rectangle is described as (x,y,width,height), as opposed to (x1,y1,x2,y2) */ private void processRectangle(RdpPacket_Localised data, RectangleOrder rect, int present, boolean delta) { if ((present & 0x01) != 0) rect.setX(setCoordinate(data, rect.getX(), delta)); if ((present & 0x02) != 0) rect.setY(setCoordinate(data, rect.getY(), delta)); if ((present & 0x04) != 0) rect.setCX(setCoordinate(data, rect.getCX(), delta)); if ((present & 0x08) != 0) rect.setCY(setCoordinate(data, rect.getCY(), delta)); if ((present & 0x10) != 0) this.rect_colour = (this.rect_colour & 0xffffff00) | data.get8(); // rect.setColor(setColor(data)); if ((present & 0x20) != 0) this.rect_colour = (this.rect_colour & 0xffff00ff) | (data.get8() << 8); // rect.setColor(setColor(data)); if ((present & 0x40) != 0) this.rect_colour = (this.rect_colour & 0xff00ffff) | (data.get8() << 16); rect.setColor(this.rect_colour); surface.drawRectangleOrder(rect); } /** * Parse data describing a desktop save order, either saving the desktop to cache, or drawing a section to screen * @param data Packet containing desktop save order * @param desksave DeskSaveOrder object in which to store order description * @param present Flags defining information available within the packet * @param delta True if destination coordinates are described as relative to the source * @throws RdesktopException */ private void processDeskSave(RdpPacket_Localised data, DeskSaveOrder desksave, int present, boolean delta) throws RdesktopException { int width = 0, height = 0; if ((present & 0x01) != 0) { desksave.setOffset(data.getLittleEndian32()); } if ((present & 0x02) != 0) { desksave.setLeft(setCoordinate(data, desksave.getLeft(), delta)); } if ((present & 0x04) != 0) { desksave.setTop(setCoordinate(data, desksave.getTop(), delta)); } if ((present & 0x08) != 0) { desksave.setRight(setCoordinate(data, desksave.getRight(), delta)); } if ((present & 0x10) != 0) { desksave .setBottom(setCoordinate(data, desksave.getBottom(), delta)); } if ((present & 0x20) != 0) { desksave.setAction(data.get8()); } width = desksave.getRight() - desksave.getLeft() + 1; height = desksave.getBottom() - desksave.getTop() + 1; if (desksave.getAction() == 0) { int[] pixel = surface.getImage(desksave.getLeft(), desksave .getTop(), width, height); cache.putDesktop((int) desksave.getOffset(), width, height, pixel); } else { int[] pixel = cache.getDesktopInt((int) desksave.getOffset(), width, height); surface.putImage(desksave.getLeft(), desksave.getTop(), width, height, pixel); } } /** * Process data describing a memory blit, and perform blit on drawing surface * @param data Packet containing mem blit order * @param memblt MemBltOrder object in which to store description of blit * @param present Flags defining information available in packet * @param delta True if destination coordinates are described as relative to the source */ private void processMemBlt(RdpPacket_Localised data, MemBltOrder memblt, int present, boolean delta) { if ((present & 0x01) != 0) { memblt.setCacheID(data.get8()); memblt.setColorTable(data.get8()); } if ((present & 0x02) != 0) memblt.setX(setCoordinate(data, memblt.getX(), delta)); if ((present & 0x04) != 0) memblt.setY(setCoordinate(data, memblt.getY(), delta)); if ((present & 0x08) != 0) memblt.setCX(setCoordinate(data, memblt.getCX(), delta)); if ((present & 0x10) != 0) memblt.setCY(setCoordinate(data, memblt.getCY(), delta)); if ((present & 0x20) != 0) memblt.setOpcode(ROP2_S(data.get8())); if ((present & 0x40) != 0) memblt.setSrcX(setCoordinate(data, memblt.getSrcX(), delta)); if ((present & 0x80) != 0) memblt.setSrcY(setCoordinate(data, memblt.getSrcY(), delta)); if ((present & 0x0100) != 0) memblt.setCacheIDX(data.getLittleEndian16()); // if(logger.isInfoEnabled()) logger.info("Memblt // opcode="+memblt.getOpcode()); surface.drawMemBltOrder(memblt); } /** * Parse data describing a tri blit order, and perform blit on drawing surface * @param data Packet containing tri blit order * @param triblt TriBltOrder object in which to store blit description * @param present Flags defining information available in packet * @param delta True if destination coordinates are described as relative to the source */ private void processTriBlt(RdpPacket_Localised data, TriBltOrder triblt, int present, boolean delta) { if ((present & 0x01) != 0) { triblt.setCacheID(data.get8()); triblt.setColorTable(data.get8()); } if ((present & 0x02) != 0) triblt.setX(setCoordinate(data, triblt.getX(), delta)); if ((present & 0x04) != 0) triblt.setY(setCoordinate(data, triblt.getY(), delta)); if ((present & 0x08) != 0) triblt.setCX(setCoordinate(data, triblt.getCX(), delta)); if ((present & 0x10) != 0) triblt.setCY(setCoordinate(data, triblt.getCY(), delta)); if ((present & 0x20) != 0) triblt.setOpcode(ROP2_S(data.get8())); if ((present & 0x40) != 0) triblt.setSrcX(setCoordinate(data, triblt.getSrcX(), delta)); if ((present & 0x80) != 0) triblt.setSrcY(setCoordinate(data, triblt.getSrcY(), delta)); if ((present & 0x0100) != 0) triblt.setBackgroundColor(setColor(data)); if ((present & 0x0200) != 0) triblt.setForegroundColor(setColor(data)); parseBrush(data, triblt.getBrush(), present >> 10); if ((present & 0x8000) != 0) triblt.setCacheIDX(data.getLittleEndian16()); if ((present & 0x10000) != 0) triblt.setUnknown(data.getLittleEndian16()); surface.drawTriBltOrder(triblt); } /** * Parse data describing a multi-line order, and draw to registered surface * @param data Packet containing polyline order * @param polyline PolyLineOrder object in which to store order description * @param present Flags defining information available in packet * @param delta True if each set of coordinates is described relative to previous set */ private void processPolyLine(RdpPacket_Localised data, PolyLineOrder polyline, int present, boolean delta) { if ((present & 0x01) != 0) polyline.setX(setCoordinate(data, polyline.getX(), delta)); if ((present & 0x02) != 0) polyline.setY(setCoordinate(data, polyline.getY(), delta)); if ((present & 0x04) != 0) polyline.setOpcode(data.get8()); if ((present & 0x10) != 0) polyline.setForegroundColor(setColor(data)); if ((present & 0x20) != 0) polyline.setLines(data.get8()); if ((present & 0x40) != 0) { int datasize = data.get8(); polyline.setDataSize(datasize); byte[] databytes = new byte[datasize]; for (int i = 0; i < datasize; i++) databytes[i] = (byte) data.get8(); polyline.setData(databytes); } // logger.info("polyline delta="+delta); // if(logger.isInfoEnabled()) logger.info("Line from // ("+line.getStartX()+","+line.getStartY()+") to // ("+line.getEndX()+","+line.getEndY()+")"); // now draw the line surface.drawPolyLineOrder(polyline); } /** * Process a text2 order and output to drawing surface * @param data Packet containing text2 order * @param text2 Text2Order object in which to store order description * @param present Flags defining information available in packet * @param delta Unused * @throws RdesktopException */ private void processText2(RdpPacket_Localised data, Text2Order text2, int present, boolean delta) throws RdesktopException { if ((present & 0x000001) != 0) { text2.setFont(data.get8()); } if ((present & 0x000002) != 0) { text2.setFlags(data.get8()); } if ((present & 0x000004) != 0) { text2.setOpcode(data.get8()); // setUnknown(data.get8()); } if ((present & 0x000008) != 0) { text2.setMixmode(data.get8()); } if ((present & 0x000010) != 0) { text2.setForegroundColor(setColor(data)); } if ((present & 0x000020) != 0) { text2.setBackgroundColor(setColor(data)); } if ((present & 0x000040) != 0) { text2.setClipLeft(data.getLittleEndian16()); } if ((present & 0x000080) != 0) { text2.setClipTop(data.getLittleEndian16()); } if ((present & 0x000100) != 0) { text2.setClipRight(data.getLittleEndian16()); } if ((present & 0x000200) != 0) { text2.setClipBottom(data.getLittleEndian16()); } if ((present & 0x000400) != 0) { text2.setBoxLeft(data.getLittleEndian16()); } if ((present & 0x000800) != 0) { text2.setBoxTop(data.getLittleEndian16()); } if ((present & 0x001000) != 0) { text2.setBoxRight(data.getLittleEndian16()); } if ((present & 0x002000) != 0) { text2.setBoxBottom(data.getLittleEndian16()); } /* * Unknown members, seen when connecting to a session that was * disconnected with mstsc and with wintach's spreadsheet test. */ if ((present & 0x004000) != 0) data.incrementPosition(1); if ((present & 0x008000) != 0) data.incrementPosition(1); if ((present & 0x010000) != 0) { data.incrementPosition(1); /* guessing the length here */ logger .warn("Unknown order state member (0x010000) in text2 order.\n"); } if ((present & 0x020000) != 0) data.incrementPosition(4); if ((present & 0x040000) != 0) data.incrementPosition(4); if ((present & 0x080000) != 0) { text2.setX(data.getLittleEndian16()); } if ((present & 0x100000) != 0) { text2.setY(data.getLittleEndian16()); } if ((present & 0x200000) != 0) { text2.setLength(data.get8()); byte[] text = new byte[text2.getLength()]; data.copyToByteArray(text, 0, data.getPosition(), text.length); data.incrementPosition(text.length); text2.setText(text); /* * if(logger.isInfoEnabled()) logger.info("X: " + text2.getX() + " * Y: " + text2.getY() + " Left Clip: " + text2.getClipLeft() + " * Top Clip: " + text2.getClipTop() + " Right Clip: " + * text2.getClipRight() + " Bottom Clip: " + text2.getClipBottom() + " * Left Box: " + text2.getBoxLeft() + " Top Box: " + * text2.getBoxTop() + " Right Box: " + text2.getBoxRight() + " * Bottom Box: " + text2.getBoxBottom() + " Foreground Color: " + * text2.getForegroundColor() + " Background Color: " + * text2.getBackgroundColor() + " Font: " + text2.getFont() + " * Flags: " + text2.getFlags() + " Mixmode: " + text2.getMixmode() + " * Unknown: " + text2.getUnknown() + " Length: " + * text2.getLength()); */ } this.drawText(text2, text2.getClipRight() - text2.getClipLeft(), text2 .getClipBottom() - text2.getClipTop(), text2.getBoxRight() - text2.getBoxLeft(), text2.getBoxBottom() - text2.getBoxTop()); } /** * Parse a description for a bounding box * @param data Packet containing order defining bounding box * @param bounds BoundsOrder object in which to store description of bounds * @throws OrderException */ private void parseBounds(RdpPacket_Localised data, BoundsOrder bounds) throws OrderException { int present = 0; present = data.get8(); if ((present & 1) != 0) { bounds.setLeft(setCoordinate(data, bounds.getLeft(), false)); } else if ((present & 16) != 0) { bounds.setLeft(setCoordinate(data, bounds.getLeft(), true)); } if ((present & 2) != 0) { bounds.setTop(setCoordinate(data, bounds.getTop(), false)); } else if ((present & 32) != 0) { bounds.setTop(setCoordinate(data, bounds.getTop(), true)); } if ((present & 4) != 0) { bounds.setRight(setCoordinate(data, bounds.getRight(), false)); } else if ((present & 64) != 0) { bounds.setRight(setCoordinate(data, bounds.getRight(), true)); } if ((present & 8) != 0) { bounds.setBottom(setCoordinate(data, bounds.getBottom(), false)); } else if ((present & 128) != 0) { bounds.setBottom(setCoordinate(data, bounds.getBottom(), true)); } if (data.getPosition() > data.getEnd()) { throw new OrderException("Too far!"); } } /** * Retrieve a coordinate from a packet and return as an absolute integer coordinate * @param data Packet containing coordinate at current read position * @param coordinate Offset coordinate * @param delta True if coordinate being read should be taken as relative to offset coordinate, false if absolute * @return Integer value of coordinate stored in packet, in absolute form */ private static int setCoordinate(RdpPacket_Localised data, int coordinate, boolean delta) { byte change = 0; if (delta) { change = (byte) data.get8(); coordinate += (int) change; return coordinate; } else { coordinate = data.getLittleEndian16(); return coordinate; } } /** * Read a colour value from a packet * @param data Packet containing colour value at current read position * @return Integer colour value read from packet */ private static int setColor(RdpPacket_Localised data) { int color = 0; int i = 0; i = data.get8(); // in_uint8(s, i); color = i; // *colour = i; i = data.get8(); // in_uint8(s, i); color |= i << 8; // *colour |= i << 8; i = data.get8(); // in_uint8(s, i); color |= i << 16; // *colour |= i << 16; // color = data.get8(); // data.incrementPosition(2); return color; } /** * Set current cache * @param cache Cache object to set as current global cache */ public void registerCache(Cache cache) { Orders.cache = cache; } /** * Parse a pen definition * @param data Packet containing pen description at current read position * @param pen Pen object in which to store pen description * @param present Flags defining information available within packet * @return True if successful */ private static boolean parsePen(RdpPacket_Localised data, Pen pen, int present) { if ((present & 0x01) != 0) pen.setStyle(data.get8()); if ((present & 0x02) != 0) pen.setWidth(data.get8()); if ((present & 0x04) != 0) pen.setColor(setColor(data)); return true; // return s_check(s); } /** * Interpret an integer as a 16-bit two's complement number, based on its binary representation * @param val Integer interpretation of binary number * @return 16-bit two's complement value of input */ private int twosComplement16(int val) { return ((val & 0x8000) != 0) ? -((~val & 0xFFFF)+1) : val; } /** * Draw a text2 order to the drawing surface * @param text2 Text2Order describing text to be drawn * @param clipcx Width of clipping area * @param clipcy Height of clipping area * @param boxcx Width of bounding box (to draw if > 1) * @param boxcy Height of bounding box (to draw if boxcx > 1) * @throws RdesktopException */ private void drawText(Text2Order text2, int clipcx, int clipcy, int boxcx, int boxcy) throws RdesktopException { byte[] text = text2.getText(); DataBlob entry = null; Glyph glyph = null; int offset = 0; int ptext = 0; int length = text2.getLength(); int x = text2.getX(); int y = text2.getY(); if(boxcx > 1) { surface.fillRectangle(text2.getBoxLeft(), text2.getBoxTop(), boxcx, boxcy, text2.getBackgroundColor()); } else if (text2.getMixmode() == MIX_OPAQUE) { surface.fillRectangle(text2.getClipLeft(), text2.getClipTop(), clipcx, clipcy, text2.getBackgroundColor()); } /* * logger.debug("X: " + text2.getX() + " Y: " + text2.getY() + " Left Clip: " + * text2.getClipLeft() + " Top Clip: " + text2.getClipTop() + " Right Clip: " + * text2.getClipRight() + " Bottom Clip: " + text2.getClipBottom() + " Left * Box: " + text2.getBoxLeft() + " Top Box: " + text2.getBoxTop() + " Right * Box: " + text2.getBoxRight() + " Bottom Box: " + text2.getBoxBottom() + " * Foreground Color: " + text2.getForegroundColor() + " Background Color: " + * text2.getBackgroundColor() + " Font: " + text2.getFont() + " Flags: " + * text2.getFlags() + " Mixmode: " + text2.getMixmode() + " Unknown: " + * text2.getUnknown() + " Length: " + text2.getLength()); */ for(int i = 0; i < length;) { switch(text[ptext + i]&0x000000ff) { case (0xff): if(i + 2 < length) { byte[] data = new byte[text[ptext + i + 2]&0x000000ff]; System.arraycopy(text ,ptext , data, 0, text[ptext + i + 2]&0x000000ff); DataBlob db = new DataBlob(text[ptext + i + 2]&0x000000ff, data); cache.putText(text[ptext + i + 1]&0x000000ff, db); } else { throw new RdesktopException(); } length -= i + 3; ptext = i + 3; i = 0; break; case (0xfe): entry = cache.getText(text[ptext + i + 1]&0x000000ff); if(entry != null){ if((entry.getData()[1] == 0) && ((text2.getFlags() & TEXT2_IMPLICIT_X) == 0)) { if((text2.getFlags() & 0x04) != 0) { y += text[ptext + i + 2]&0x000000ff; } else { x += text[ptext + i + 2]&0x000000ff; } } } if(i + 2 < length) { i += 3; } else { i += 2; } length -= i; ptext = i; i = 0; // break; byte[] data = entry.getData(); for(int j = 0; j < entry.getSize(); j++) { glyph = cache.getFont(text2.getFont(), data[j]&0x000000ff); if((text2.getFlags() & TEXT2_IMPLICIT_X) == 0) { offset = data[++j]&0x000000ff; if((offset & 0x80) !=0) { if((text2.getFlags() & TEXT2_VERTICAL) != 0) { int var = this.twosComplement16((data[j+1]&0xff) | ((data[j+2]&0xff) << 8)); y += var; j += 2; } else { int var = this.twosComplement16((data[j+1]&0xff) | ((data[j+2]&0xff) << 8)); x += var; j += 2; } } else { if((text2.getFlags() & TEXT2_VERTICAL) != 0) { y += offset; } else { x += offset; } } } if(glyph != null) { //if((text2.getFlags() & TEXT2_VERTICAL) != 0) logger.info("Drawing glyph: (" + (x + (short)glyph.getOffset()) + ", " + (y + (short)glyph.getBaseLine()) + ")" ); surface.drawGlyph(text2.getMixmode(), x + (short)glyph.getOffset(), y + (short)glyph.getBaseLine(), glyph.getWidth(), glyph.getHeight(), glyph.getFontData(), text2.getBackgroundColor(), text2.getForegroundColor()); if((text2.getFlags() & TEXT2_IMPLICIT_X) != 0) { x += glyph.getWidth(); } } } break; default: glyph = cache.getFont(text2.getFont(), text[ptext + i]&0x000000ff); if((text2.getFlags() & TEXT2_IMPLICIT_X) == 0) { offset = text[ptext + (++i)]&0x000000ff; if((offset & 0x80) !=0) { if((text2.getFlags() & TEXT2_VERTICAL) != 0) { //logger.info("y +=" + (text[ptext + (i+1)]&0x000000ff) + " | " + ((text[ptext + (i+2)]&0x000000ff) << 8)); int var = this.twosComplement16((text[ptext + i+1]&0x000000ff) | ((text[ptext + i+2]&0x000000ff) << 8)); y += var; i += 2; } else { int var = this.twosComplement16((text[ptext + i+1]&0x000000ff) | ((text[ptext + i+2]&0x000000ff) << 8)); x += var; i += 2; } } else { if((text2.getFlags() & TEXT2_VERTICAL) != 0) { y += offset; } else { x += offset; } } } if(glyph != null) { surface.drawGlyph(text2.getMixmode(), x + (short)glyph.getOffset(), y + (short)glyph.getBaseLine(), glyph.getWidth(), glyph.getHeight(), glyph.getFontData(), text2.getBackgroundColor(), text2.getForegroundColor()); if((text2.getFlags() & TEXT2_IMPLICIT_X) != 0) x += glyph.getWidth(); } i++; break; } } }}