/* Orders.java
* Component: ProperJavaRDP
*
* Revision: $Revision: 1.1.1.1 $
* Author: $Author: suvarov $
* Date: $Date: 2007/03/08 00:26:31 $
*
* Copyright (c) 2005 Propero Limited
*
* Purpose: Encapsulates an RDP order
*/
package com.lixia.rdp;
import com.alssoftrd.events.EventWakeUp;
import com.alssoftrd.utils.ManageBitmaps;
import com.alssoftrd.utils.RDPConnection;
import java.awt.image.IndexColorModel;
import java.io.IOException;
import com.lixia.rdp.orders.*;
import com.lixia.rdp.Package.RdpPackage;
import com.lixia.rdp.rdp5.ARDP5;
public class OrdersJPanel {
// static Logger logger = Logger.getLogger(OrdersJPanel.class);
private OrderState os = null;
private Cache cache = null;
public static ManageBitmaps mb;
/* 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 OrdersJPanel() {
os = new OrderState();
}
public void resetOrderState() {
this.os.reset();
os.setOrderType(RDP_ORDER_PATBLT);
}
private int inPresent(RdpPackage data, int flags, int size) {
int present = 0;
int bits;
int i;
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(RdpPackage data, int next_packet, int n_orders) throws OrderException, RdesktopException {
int present;
// int n_orders = 0;
int order_flags, order_type = 0;
int size, 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());
}
mb.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) {
mb.resetClip();
// logger.debug("Reset clip");
}
}
processed++;
}
if (data.getPosition() != next_packet) {
throw new OrderException("End not reached!");
}
RDPConnection.sc.setImage(ARDP5.wi);
EventWakeUp.getInstance().setImage(RDPConnection.sc);
}
private int ROP2_S(int rop3) {
return (rop3 & 0x0f);
}
private int ROP2_P(int rop3) {
return ((rop3 & 0x3) | ((rop3 & 0x30) >> 2));
}
/**
* Register an RdesktopJPanel with this Orders object. This surface is where
* all drawing orders will be carried out.
*
*/
public void registerDrawingSurface() {
}
/**
* Handle secondary, or caching, orders
*
* @param data Packet containing secondary order
* @throws OrderException
* @throws RdesktopException
*/
private void processSecondaryOrders(RdpPackage data) throws OrderException, RdesktopException {
int length;
int type;
int flags;
int next_order;
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(RdpPackage 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);
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(RdpPackage data) throws RdesktopException {
byte[] palette;
byte[] red;
byte[] green;
byte[] blue;
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];
j += 4;
}
IndexColorModel cm = new IndexColorModel(8, n_colors, red, green, blue);
cache.put_colourmap(cache_id, cm);
}
/**
* Process a compressed bitmap and store in the bitmap cache
*
* @param data Packet containing compressed bitmap
* @throws RdesktopException
*/
private void processBitmapCache(RdpPackage data) throws RdesktopException {
int bufsize, pad2, row_size, final_size, size;
int pad1;
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();
if (RDPConnection.conf.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,
}
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(RdpPackage 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;
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;
}
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");
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) + data.getPosition(), width * Bpp);
}
bitmap = new Bitmap(Bitmap.convertImage(bmpdata, Bpp), width, height, 0, 0);
}
if (bitmap != null) {
cache.putBitmap(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");
}
}
/**
* 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(RdpPackage data) throws RdesktopException {
Glyph glyph;
int font, nglyphs;
int character, offset, baseline, width, height;
int datasize;
byte[] fontdata;
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(RdpPackage 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()));
}
mb.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(RdpPackage 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(RdpPackage 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);
mb.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(RdpPackage 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));
}
mb.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(RdpPackage 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 (line.getOpcode() < 0x01 || line.getOpcode() > 0x10) {
// logger.warn("bad ROP2 0x" + line.getOpcode());
return;
}
mb.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(RdpPackage 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);
mb.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(RdpPackage data, DeskSaveOrder desksave, int present, boolean delta) throws RdesktopException {
int width, height;
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 = mb.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);
mb.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(RdpPackage 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());
}
mb.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(RdpPackage 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());
}
mb.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(RdpPackage 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);
}
mb.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(RdpPackage 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);
}
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(RdpPackage data, BoundsOrder bounds) throws OrderException {
int present;
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(RdpPackage data, int coordinate, boolean delta) {
byte change;
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(RdpPackage data) {
int color;
int i;
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) {
this.cache = cache;
mb = new ManageBitmaps(cache);
mb.setVisible(true);
}
/**
* 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(RdpPackage 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;
Glyph glyph;
int offset;
int ptext = 0;
int length = text2.getLength();
int x = text2.getX();
int y = text2.getY();
if (boxcx > 1) {
mb.fillRectangle(text2.getBoxLeft(), text2.getBoxTop(), boxcx, boxcy, text2.getBackgroundColor());
} else if (text2.getMixmode() == MIX_OPAQUE) {
mb.fillRectangle(text2.getClipLeft(), text2.getClipTop(), clipcx, clipcy, text2.getBackgroundColor());
}
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;
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) {
mb.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) {
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) {
mb.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;
}
}
}
}