Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions core/decoders/raw.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default class RawDecoder {
this._lines = 0;
}

decodeRect(x, y, width, height, sock, display, depth) {
decodeRect(x, y, width, height, sock, display, depth, pixelFormat) {
if ((width === 0) || (height === 0)) {
return true;
}
Expand All @@ -21,7 +21,7 @@ export default class RawDecoder {
this._lines = height;
}

const pixelSize = depth == 8 ? 1 : 4;
const pixelSize = depth == 8 ? 1 : (depth == 16 ? 2 : 4); // Modifications
const bytesPerLine = width * pixelSize;

while (this._lines > 0) {
Expand All @@ -43,6 +43,36 @@ export default class RawDecoder {
newdata[i * 4 + 3] = 255;
}
data = newdata;
} else if (depth == 16) { // Modifications: decode 16bpp raw
const fmt = pixelFormat || {};
const redMax = fmt.redMax !== undefined ? fmt.redMax : 31;
const greenMax = fmt.greenMax !== undefined ? fmt.greenMax : 63;
const blueMax = fmt.blueMax !== undefined ? fmt.blueMax : 31;
const redShift = fmt.redShift !== undefined ? fmt.redShift : 11;
const greenShift = fmt.greenShift !== undefined ? fmt.greenShift : 5;
const blueShift = fmt.blueShift !== undefined ? fmt.blueShift : 0;
const bigEndian = !!fmt.bigEndian;

const newdata = new Uint8Array(width * 4);
for (let i = 0; i < width; i++) {
const idx = i * 2;
let pixel;
if (bigEndian) {
pixel = (data[idx] << 8) | data[idx + 1];
} else {
pixel = data[idx] | (data[idx + 1] << 8);
}

const r = (pixel >> redShift) & redMax;
const g = (pixel >> greenShift) & greenMax;
const b = (pixel >> blueShift) & blueMax;

newdata[i * 4 + 0] = redMax ? (r * 255 / redMax) : 0;
newdata[i * 4 + 1] = greenMax ? (g * 255 / greenMax) : 0;
newdata[i * 4 + 2] = blueMax ? (b * 255 / blueMax) : 0;
newdata[i * 4 + 3] = 255;
}
data = newdata;
}

// Max sure the image is fully opaque
Expand Down
33 changes: 32 additions & 1 deletion core/rfb.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,15 @@ export default class RFB extends EventTargetMixin {
// Server capabilities
this._rfbVersion = 0;
this._rfbMaxVersion = 3.8;
this._rfbServerVersion = null;
this._rfbTightVNC = false;
this._rfbVeNCryptState = 0;
this._rfbXvpVer = 0;

this._fbWidth = 0;
this._fbHeight = 0;
this._fbPixelFormat = null;
this._forceRawEncoding = false; // Modifications: 003.005 compat path

this._fbName = "";

Expand Down Expand Up @@ -1506,6 +1509,7 @@ export default class RFB extends EventTargetMixin {
}

const sversion = this._sock.rQshiftStr(12).substr(4, 7);
this._rfbServerVersion = sversion;
Log.Info("Server ProtocolVersion: " + sversion);
let isRepeater = 0;
switch (sversion) {
Expand All @@ -1516,6 +1520,11 @@ export default class RFB extends EventTargetMixin {
case "003.006": // UltraVNC
this._rfbVersion = 3.3;
break;
case "003.005": // non-standard (seen in impact.pcapng)
Log.Warn("Non-standard server version " + sversion + ", treating as RFB 3.3");
this._rfbVersion = 3.3;
this._forceRawEncoding = true; // Modifications: force raw/16bpp
break;
case "003.007":
this._rfbVersion = 3.7;
break;
Expand Down Expand Up @@ -2164,6 +2173,18 @@ export default class RFB extends EventTargetMixin {
const greenShift = this._sock.rQshift8();
const blueShift = this._sock.rQshift8();
this._sock.rQskipBytes(3); // padding
this._fbPixelFormat = { // Modifications: store server pixel format
bpp: bpp,
depth: depth,
bigEndian: bigEndian,
trueColor: trueColor,
redMax: redMax,
greenMax: greenMax,
blueMax: blueMax,
redShift: redShift,
greenShift: greenShift,
blueShift: blueShift,
};

// NB(directxman12): we don't want to call any callbacks or print messages until
// *after* we're past the point where we could backtrack
Expand Down Expand Up @@ -2226,6 +2247,11 @@ export default class RFB extends EventTargetMixin {
Log.Warn("Intel AMT KVM only supports 8/16 bit depths. Using low color mode.");
this._fbDepth = 8;
}
if (this._forceRawEncoding && this._fbPixelFormat &&
this._fbPixelFormat.depth === 16 && this._fbPixelFormat.trueColor) {
Log.Warn("Forcing 16bpp pixel format due to non-standard RFB version");
this._fbDepth = 16;
}

RFB.messages.pixelFormat(this._sock, this._fbDepth, true);
this._sendEncodings();
Expand All @@ -2240,6 +2266,11 @@ export default class RFB extends EventTargetMixin {

// In preference order
encs.push(encodings.encodingCopyRect);
if (this._forceRawEncoding) { // Modifications: raw-only encodings
encs.push(encodings.encodingRaw);
RFB.messages.clientEncodings(this._sock, encs);
return;
}
// Only supported with full depth support
if (this._fbDepth == 24) {
if (supportsWebCodecsH264Decode) {
Expand Down Expand Up @@ -3000,7 +3031,7 @@ export default class RFB extends EventTargetMixin {
return decoder.decodeRect(this._FBU.x, this._FBU.y,
this._FBU.width, this._FBU.height,
this._sock, this._display,
this._fbDepth);
this._fbDepth, this._fbPixelFormat); // Modifications
} catch (err) {
this._fail("Error decoding rect: " + err);
return false;
Expand Down