susi-qemu 0.0.3 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/bin/susi +9 -4
  3. data/lib/disk.rb +7 -5
  4. data/lib/novnc/core/base64.js +104 -0
  5. data/lib/novnc/core/crypto/aes.js +178 -0
  6. data/lib/novnc/core/crypto/bigint.js +34 -0
  7. data/lib/novnc/core/crypto/crypto.js +90 -0
  8. data/lib/novnc/core/crypto/des.js +330 -0
  9. data/lib/novnc/core/crypto/dh.js +55 -0
  10. data/lib/novnc/core/crypto/md5.js +82 -0
  11. data/lib/novnc/core/crypto/rsa.js +132 -0
  12. data/lib/novnc/core/decoders/copyrect.js +27 -0
  13. data/lib/novnc/core/decoders/h264.js +321 -0
  14. data/lib/novnc/core/decoders/hextile.js +181 -0
  15. data/lib/novnc/core/decoders/jpeg.js +146 -0
  16. data/lib/novnc/core/decoders/raw.js +59 -0
  17. data/lib/novnc/core/decoders/rre.js +44 -0
  18. data/lib/novnc/core/decoders/tight.js +393 -0
  19. data/lib/novnc/core/decoders/tightpng.js +27 -0
  20. data/lib/novnc/core/decoders/zlib.js +51 -0
  21. data/lib/novnc/core/decoders/zrle.js +185 -0
  22. data/lib/novnc/core/deflator.js +84 -0
  23. data/lib/novnc/core/display.js +575 -0
  24. data/lib/novnc/core/encodings.js +53 -0
  25. data/lib/novnc/core/inflator.js +65 -0
  26. data/lib/novnc/core/input/domkeytable.js +311 -0
  27. data/lib/novnc/core/input/fixedkeys.js +129 -0
  28. data/lib/novnc/core/input/gesturehandler.js +567 -0
  29. data/lib/novnc/core/input/keyboard.js +294 -0
  30. data/lib/novnc/core/input/keysym.js +616 -0
  31. data/lib/novnc/core/input/keysymdef.js +688 -0
  32. data/lib/novnc/core/input/util.js +191 -0
  33. data/lib/novnc/core/input/vkeys.js +116 -0
  34. data/lib/novnc/core/input/xtscancodes.js +173 -0
  35. data/lib/novnc/core/ra2.js +312 -0
  36. data/lib/novnc/core/rfb.js +3257 -0
  37. data/lib/novnc/core/util/browser.js +172 -0
  38. data/lib/novnc/core/util/cursor.js +249 -0
  39. data/lib/novnc/core/util/element.js +32 -0
  40. data/lib/novnc/core/util/events.js +138 -0
  41. data/lib/novnc/core/util/eventtarget.js +35 -0
  42. data/lib/novnc/core/util/int.js +15 -0
  43. data/lib/novnc/core/util/logging.js +56 -0
  44. data/lib/novnc/core/util/strings.js +28 -0
  45. data/lib/novnc/core/websock.js +365 -0
  46. data/lib/novnc/screen.html +21 -0
  47. data/lib/novnc/vendor/pako/lib/utils/common.js +45 -0
  48. data/lib/novnc/vendor/pako/lib/zlib/adler32.js +27 -0
  49. data/lib/novnc/vendor/pako/lib/zlib/constants.js +47 -0
  50. data/lib/novnc/vendor/pako/lib/zlib/crc32.js +36 -0
  51. data/lib/novnc/vendor/pako/lib/zlib/deflate.js +1846 -0
  52. data/lib/novnc/vendor/pako/lib/zlib/gzheader.js +35 -0
  53. data/lib/novnc/vendor/pako/lib/zlib/inffast.js +324 -0
  54. data/lib/novnc/vendor/pako/lib/zlib/inflate.js +1527 -0
  55. data/lib/novnc/vendor/pako/lib/zlib/inftrees.js +322 -0
  56. data/lib/novnc/vendor/pako/lib/zlib/messages.js +11 -0
  57. data/lib/novnc/vendor/pako/lib/zlib/trees.js +1195 -0
  58. data/lib/novnc/vendor/pako/lib/zlib/zstream.js +24 -0
  59. data/lib/output.rb +11 -0
  60. data/lib/qmp.rb +6 -0
  61. data/lib/ssh.rb +3 -1
  62. data/lib/susi.rb +7 -6
  63. data/lib/version.rb +1 -1
  64. data/lib/vm.rb +36 -13
  65. data/lib/vnc.rb +34 -30
  66. metadata +85 -1
@@ -0,0 +1,44 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2019 The noVNC Authors
4
+ * Licensed under MPL 2.0 (see LICENSE.txt)
5
+ *
6
+ * See README.md for usage and integration instructions.
7
+ *
8
+ */
9
+
10
+ export default class RREDecoder {
11
+ constructor() {
12
+ this._subrects = 0;
13
+ }
14
+
15
+ decodeRect(x, y, width, height, sock, display, depth) {
16
+ if (this._subrects === 0) {
17
+ if (sock.rQwait("RRE", 4 + 4)) {
18
+ return false;
19
+ }
20
+
21
+ this._subrects = sock.rQshift32();
22
+
23
+ let color = sock.rQshiftBytes(4); // Background
24
+ display.fillRect(x, y, width, height, color);
25
+ }
26
+
27
+ while (this._subrects > 0) {
28
+ if (sock.rQwait("RRE", 4 + 8)) {
29
+ return false;
30
+ }
31
+
32
+ let color = sock.rQshiftBytes(4);
33
+ let sx = sock.rQshift16();
34
+ let sy = sock.rQshift16();
35
+ let swidth = sock.rQshift16();
36
+ let sheight = sock.rQshift16();
37
+ display.fillRect(x + sx, y + sy, swidth, sheight, color);
38
+
39
+ this._subrects--;
40
+ }
41
+
42
+ return true;
43
+ }
44
+ }
@@ -0,0 +1,393 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2019 The noVNC Authors
4
+ * (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
5
+ * Licensed under MPL 2.0 (see LICENSE.txt)
6
+ *
7
+ * See README.md for usage and integration instructions.
8
+ *
9
+ */
10
+
11
+ import * as Log from '../util/logging.js';
12
+ import Inflator from "../inflator.js";
13
+
14
+ export default class TightDecoder {
15
+ constructor() {
16
+ this._ctl = null;
17
+ this._filter = null;
18
+ this._numColors = 0;
19
+ this._palette = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel)
20
+ this._len = 0;
21
+
22
+ this._zlibs = [];
23
+ for (let i = 0; i < 4; i++) {
24
+ this._zlibs[i] = new Inflator();
25
+ }
26
+ }
27
+
28
+ decodeRect(x, y, width, height, sock, display, depth) {
29
+ if (this._ctl === null) {
30
+ if (sock.rQwait("TIGHT compression-control", 1)) {
31
+ return false;
32
+ }
33
+
34
+ this._ctl = sock.rQshift8();
35
+
36
+ // Reset streams if the server requests it
37
+ for (let i = 0; i < 4; i++) {
38
+ if ((this._ctl >> i) & 1) {
39
+ this._zlibs[i].reset();
40
+ Log.Info("Reset zlib stream " + i);
41
+ }
42
+ }
43
+
44
+ // Figure out filter
45
+ this._ctl = this._ctl >> 4;
46
+ }
47
+
48
+ let ret;
49
+
50
+ if (this._ctl === 0x08) {
51
+ ret = this._fillRect(x, y, width, height,
52
+ sock, display, depth);
53
+ } else if (this._ctl === 0x09) {
54
+ ret = this._jpegRect(x, y, width, height,
55
+ sock, display, depth);
56
+ } else if (this._ctl === 0x0A) {
57
+ ret = this._pngRect(x, y, width, height,
58
+ sock, display, depth);
59
+ } else if ((this._ctl & 0x08) == 0) {
60
+ ret = this._basicRect(this._ctl, x, y, width, height,
61
+ sock, display, depth);
62
+ } else {
63
+ throw new Error("Illegal tight compression received (ctl: " +
64
+ this._ctl + ")");
65
+ }
66
+
67
+ if (ret) {
68
+ this._ctl = null;
69
+ }
70
+
71
+ return ret;
72
+ }
73
+
74
+ _fillRect(x, y, width, height, sock, display, depth) {
75
+ if (sock.rQwait("TIGHT", 3)) {
76
+ return false;
77
+ }
78
+
79
+ let pixel = sock.rQshiftBytes(3);
80
+ display.fillRect(x, y, width, height, pixel, false);
81
+
82
+ return true;
83
+ }
84
+
85
+ _jpegRect(x, y, width, height, sock, display, depth) {
86
+ let data = this._readData(sock);
87
+ if (data === null) {
88
+ return false;
89
+ }
90
+
91
+ display.imageRect(x, y, width, height, "image/jpeg", data);
92
+
93
+ return true;
94
+ }
95
+
96
+ _pngRect(x, y, width, height, sock, display, depth) {
97
+ throw new Error("PNG received in standard Tight rect");
98
+ }
99
+
100
+ _basicRect(ctl, x, y, width, height, sock, display, depth) {
101
+ if (this._filter === null) {
102
+ if (ctl & 0x4) {
103
+ if (sock.rQwait("TIGHT", 1)) {
104
+ return false;
105
+ }
106
+
107
+ this._filter = sock.rQshift8();
108
+ } else {
109
+ // Implicit CopyFilter
110
+ this._filter = 0;
111
+ }
112
+ }
113
+
114
+ let streamId = ctl & 0x3;
115
+
116
+ let ret;
117
+
118
+ switch (this._filter) {
119
+ case 0: // CopyFilter
120
+ ret = this._copyFilter(streamId, x, y, width, height,
121
+ sock, display, depth);
122
+ break;
123
+ case 1: // PaletteFilter
124
+ ret = this._paletteFilter(streamId, x, y, width, height,
125
+ sock, display, depth);
126
+ break;
127
+ case 2: // GradientFilter
128
+ ret = this._gradientFilter(streamId, x, y, width, height,
129
+ sock, display, depth);
130
+ break;
131
+ default:
132
+ throw new Error("Illegal tight filter received (ctl: " +
133
+ this._filter + ")");
134
+ }
135
+
136
+ if (ret) {
137
+ this._filter = null;
138
+ }
139
+
140
+ return ret;
141
+ }
142
+
143
+ _copyFilter(streamId, x, y, width, height, sock, display, depth) {
144
+ const uncompressedSize = width * height * 3;
145
+ let data;
146
+
147
+ if (uncompressedSize === 0) {
148
+ return true;
149
+ }
150
+
151
+ if (uncompressedSize < 12) {
152
+ if (sock.rQwait("TIGHT", uncompressedSize)) {
153
+ return false;
154
+ }
155
+
156
+ data = sock.rQshiftBytes(uncompressedSize);
157
+ } else {
158
+ data = this._readData(sock);
159
+ if (data === null) {
160
+ return false;
161
+ }
162
+
163
+ this._zlibs[streamId].setInput(data);
164
+ data = this._zlibs[streamId].inflate(uncompressedSize);
165
+ this._zlibs[streamId].setInput(null);
166
+ }
167
+
168
+ let rgbx = new Uint8Array(width * height * 4);
169
+ for (let i = 0, j = 0; i < width * height * 4; i += 4, j += 3) {
170
+ rgbx[i] = data[j];
171
+ rgbx[i + 1] = data[j + 1];
172
+ rgbx[i + 2] = data[j + 2];
173
+ rgbx[i + 3] = 255; // Alpha
174
+ }
175
+
176
+ display.blitImage(x, y, width, height, rgbx, 0, false);
177
+
178
+ return true;
179
+ }
180
+
181
+ _paletteFilter(streamId, x, y, width, height, sock, display, depth) {
182
+ if (this._numColors === 0) {
183
+ if (sock.rQwait("TIGHT palette", 1)) {
184
+ return false;
185
+ }
186
+
187
+ const numColors = sock.rQpeek8() + 1;
188
+ const paletteSize = numColors * 3;
189
+
190
+ if (sock.rQwait("TIGHT palette", 1 + paletteSize)) {
191
+ return false;
192
+ }
193
+
194
+ this._numColors = numColors;
195
+ sock.rQskipBytes(1);
196
+
197
+ sock.rQshiftTo(this._palette, paletteSize);
198
+ }
199
+
200
+ const bpp = (this._numColors <= 2) ? 1 : 8;
201
+ const rowSize = Math.floor((width * bpp + 7) / 8);
202
+ const uncompressedSize = rowSize * height;
203
+
204
+ let data;
205
+
206
+ if (uncompressedSize === 0) {
207
+ return true;
208
+ }
209
+
210
+ if (uncompressedSize < 12) {
211
+ if (sock.rQwait("TIGHT", uncompressedSize)) {
212
+ return false;
213
+ }
214
+
215
+ data = sock.rQshiftBytes(uncompressedSize);
216
+ } else {
217
+ data = this._readData(sock);
218
+ if (data === null) {
219
+ return false;
220
+ }
221
+
222
+ this._zlibs[streamId].setInput(data);
223
+ data = this._zlibs[streamId].inflate(uncompressedSize);
224
+ this._zlibs[streamId].setInput(null);
225
+ }
226
+
227
+ // Convert indexed (palette based) image data to RGB
228
+ if (this._numColors == 2) {
229
+ this._monoRect(x, y, width, height, data, this._palette, display);
230
+ } else {
231
+ this._paletteRect(x, y, width, height, data, this._palette, display);
232
+ }
233
+
234
+ this._numColors = 0;
235
+
236
+ return true;
237
+ }
238
+
239
+ _monoRect(x, y, width, height, data, palette, display) {
240
+ // Convert indexed (palette based) image data to RGB
241
+ // TODO: reduce number of calculations inside loop
242
+ const dest = this._getScratchBuffer(width * height * 4);
243
+ const w = Math.floor((width + 7) / 8);
244
+ const w1 = Math.floor(width / 8);
245
+
246
+ for (let y = 0; y < height; y++) {
247
+ let dp, sp, x;
248
+ for (x = 0; x < w1; x++) {
249
+ for (let b = 7; b >= 0; b--) {
250
+ dp = (y * width + x * 8 + 7 - b) * 4;
251
+ sp = (data[y * w + x] >> b & 1) * 3;
252
+ dest[dp] = palette[sp];
253
+ dest[dp + 1] = palette[sp + 1];
254
+ dest[dp + 2] = palette[sp + 2];
255
+ dest[dp + 3] = 255;
256
+ }
257
+ }
258
+
259
+ for (let b = 7; b >= 8 - width % 8; b--) {
260
+ dp = (y * width + x * 8 + 7 - b) * 4;
261
+ sp = (data[y * w + x] >> b & 1) * 3;
262
+ dest[dp] = palette[sp];
263
+ dest[dp + 1] = palette[sp + 1];
264
+ dest[dp + 2] = palette[sp + 2];
265
+ dest[dp + 3] = 255;
266
+ }
267
+ }
268
+
269
+ display.blitImage(x, y, width, height, dest, 0, false);
270
+ }
271
+
272
+ _paletteRect(x, y, width, height, data, palette, display) {
273
+ // Convert indexed (palette based) image data to RGB
274
+ const dest = this._getScratchBuffer(width * height * 4);
275
+ const total = width * height * 4;
276
+ for (let i = 0, j = 0; i < total; i += 4, j++) {
277
+ const sp = data[j] * 3;
278
+ dest[i] = palette[sp];
279
+ dest[i + 1] = palette[sp + 1];
280
+ dest[i + 2] = palette[sp + 2];
281
+ dest[i + 3] = 255;
282
+ }
283
+
284
+ display.blitImage(x, y, width, height, dest, 0, false);
285
+ }
286
+
287
+ _gradientFilter(streamId, x, y, width, height, sock, display, depth) {
288
+ // assume the TPIXEL is 3 bytes long
289
+ const uncompressedSize = width * height * 3;
290
+ let data;
291
+
292
+ if (uncompressedSize === 0) {
293
+ return true;
294
+ }
295
+
296
+ if (uncompressedSize < 12) {
297
+ if (sock.rQwait("TIGHT", uncompressedSize)) {
298
+ return false;
299
+ }
300
+
301
+ data = sock.rQshiftBytes(uncompressedSize);
302
+ } else {
303
+ data = this._readData(sock);
304
+ if (data === null) {
305
+ return false;
306
+ }
307
+
308
+ this._zlibs[streamId].setInput(data);
309
+ data = this._zlibs[streamId].inflate(uncompressedSize);
310
+ this._zlibs[streamId].setInput(null);
311
+ }
312
+
313
+ let rgbx = new Uint8Array(4 * width * height);
314
+
315
+ let rgbxIndex = 0, dataIndex = 0;
316
+ let left = new Uint8Array(3);
317
+ for (let x = 0; x < width; x++) {
318
+ for (let c = 0; c < 3; c++) {
319
+ const prediction = left[c];
320
+ const value = data[dataIndex++] + prediction;
321
+ rgbx[rgbxIndex++] = value;
322
+ left[c] = value;
323
+ }
324
+ rgbx[rgbxIndex++] = 255;
325
+ }
326
+
327
+ let upperIndex = 0;
328
+ let upper = new Uint8Array(3),
329
+ upperleft = new Uint8Array(3);
330
+ for (let y = 1; y < height; y++) {
331
+ left.fill(0);
332
+ upperleft.fill(0);
333
+ for (let x = 0; x < width; x++) {
334
+ for (let c = 0; c < 3; c++) {
335
+ upper[c] = rgbx[upperIndex++];
336
+ let prediction = left[c] + upper[c] - upperleft[c];
337
+ if (prediction < 0) {
338
+ prediction = 0;
339
+ } else if (prediction > 255) {
340
+ prediction = 255;
341
+ }
342
+ const value = data[dataIndex++] + prediction;
343
+ rgbx[rgbxIndex++] = value;
344
+ upperleft[c] = upper[c];
345
+ left[c] = value;
346
+ }
347
+ rgbx[rgbxIndex++] = 255;
348
+ upperIndex++;
349
+ }
350
+ }
351
+
352
+ display.blitImage(x, y, width, height, rgbx, 0, false);
353
+
354
+ return true;
355
+ }
356
+
357
+ _readData(sock) {
358
+ if (this._len === 0) {
359
+ if (sock.rQwait("TIGHT", 3)) {
360
+ return null;
361
+ }
362
+
363
+ let byte;
364
+
365
+ byte = sock.rQshift8();
366
+ this._len = byte & 0x7f;
367
+ if (byte & 0x80) {
368
+ byte = sock.rQshift8();
369
+ this._len |= (byte & 0x7f) << 7;
370
+ if (byte & 0x80) {
371
+ byte = sock.rQshift8();
372
+ this._len |= byte << 14;
373
+ }
374
+ }
375
+ }
376
+
377
+ if (sock.rQwait("TIGHT", this._len)) {
378
+ return null;
379
+ }
380
+
381
+ let data = sock.rQshiftBytes(this._len, false);
382
+ this._len = 0;
383
+
384
+ return data;
385
+ }
386
+
387
+ _getScratchBuffer(size) {
388
+ if (!this._scratchBuffer || (this._scratchBuffer.length < size)) {
389
+ this._scratchBuffer = new Uint8Array(size);
390
+ }
391
+ return this._scratchBuffer;
392
+ }
393
+ }
@@ -0,0 +1,27 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2019 The noVNC Authors
4
+ * Licensed under MPL 2.0 (see LICENSE.txt)
5
+ *
6
+ * See README.md for usage and integration instructions.
7
+ *
8
+ */
9
+
10
+ import TightDecoder from './tight.js';
11
+
12
+ export default class TightPNGDecoder extends TightDecoder {
13
+ _pngRect(x, y, width, height, sock, display, depth) {
14
+ let data = this._readData(sock);
15
+ if (data === null) {
16
+ return false;
17
+ }
18
+
19
+ display.imageRect(x, y, width, height, "image/png", data);
20
+
21
+ return true;
22
+ }
23
+
24
+ _basicRect(ctl, x, y, width, height, sock, display, depth) {
25
+ throw new Error("BasicCompression received in TightPNG rect");
26
+ }
27
+ }
@@ -0,0 +1,51 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2024 The noVNC Authors
4
+ * Licensed under MPL 2.0 (see LICENSE.txt)
5
+ *
6
+ * See README.md for usage and integration instructions.
7
+ *
8
+ */
9
+
10
+ import Inflator from "../inflator.js";
11
+
12
+ export default class ZlibDecoder {
13
+ constructor() {
14
+ this._zlib = new Inflator();
15
+ this._length = 0;
16
+ }
17
+
18
+ decodeRect(x, y, width, height, sock, display, depth) {
19
+ if ((width === 0) || (height === 0)) {
20
+ return true;
21
+ }
22
+
23
+ if (this._length === 0) {
24
+ if (sock.rQwait("ZLIB", 4)) {
25
+ return false;
26
+ }
27
+
28
+ this._length = sock.rQshift32();
29
+ }
30
+
31
+ if (sock.rQwait("ZLIB", this._length)) {
32
+ return false;
33
+ }
34
+
35
+ let data = new Uint8Array(sock.rQshiftBytes(this._length, false));
36
+ this._length = 0;
37
+
38
+ this._zlib.setInput(data);
39
+ data = this._zlib.inflate(width * height * 4);
40
+ this._zlib.setInput(null);
41
+
42
+ // Max sure the image is fully opaque
43
+ for (let i = 0; i < width * height; i++) {
44
+ data[i * 4 + 3] = 255;
45
+ }
46
+
47
+ display.blitImage(x, y, width, height, data, 0);
48
+
49
+ return true;
50
+ }
51
+ }
@@ -0,0 +1,185 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2021 The noVNC Authors
4
+ * Licensed under MPL 2.0 (see LICENSE.txt)
5
+ *
6
+ * See README.md for usage and integration instructions.
7
+ *
8
+ */
9
+
10
+ import Inflate from "../inflator.js";
11
+
12
+ const ZRLE_TILE_WIDTH = 64;
13
+ const ZRLE_TILE_HEIGHT = 64;
14
+
15
+ export default class ZRLEDecoder {
16
+ constructor() {
17
+ this._length = 0;
18
+ this._inflator = new Inflate();
19
+
20
+ this._pixelBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4);
21
+ this._tileBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4);
22
+ }
23
+
24
+ decodeRect(x, y, width, height, sock, display, depth) {
25
+ if (this._length === 0) {
26
+ if (sock.rQwait("ZLib data length", 4)) {
27
+ return false;
28
+ }
29
+ this._length = sock.rQshift32();
30
+ }
31
+ if (sock.rQwait("Zlib data", this._length)) {
32
+ return false;
33
+ }
34
+
35
+ const data = sock.rQshiftBytes(this._length, false);
36
+
37
+ this._inflator.setInput(data);
38
+
39
+ for (let ty = y; ty < y + height; ty += ZRLE_TILE_HEIGHT) {
40
+ let th = Math.min(ZRLE_TILE_HEIGHT, y + height - ty);
41
+
42
+ for (let tx = x; tx < x + width; tx += ZRLE_TILE_WIDTH) {
43
+ let tw = Math.min(ZRLE_TILE_WIDTH, x + width - tx);
44
+
45
+ const tileSize = tw * th;
46
+ const subencoding = this._inflator.inflate(1)[0];
47
+ if (subencoding === 0) {
48
+ // raw data
49
+ const data = this._readPixels(tileSize);
50
+ display.blitImage(tx, ty, tw, th, data, 0, false);
51
+ } else if (subencoding === 1) {
52
+ // solid
53
+ const background = this._readPixels(1);
54
+ display.fillRect(tx, ty, tw, th, [background[0], background[1], background[2]]);
55
+ } else if (subencoding >= 2 && subencoding <= 16) {
56
+ const data = this._decodePaletteTile(subencoding, tileSize, tw, th);
57
+ display.blitImage(tx, ty, tw, th, data, 0, false);
58
+ } else if (subencoding === 128) {
59
+ const data = this._decodeRLETile(tileSize);
60
+ display.blitImage(tx, ty, tw, th, data, 0, false);
61
+ } else if (subencoding >= 130 && subencoding <= 255) {
62
+ const data = this._decodeRLEPaletteTile(subencoding - 128, tileSize);
63
+ display.blitImage(tx, ty, tw, th, data, 0, false);
64
+ } else {
65
+ throw new Error('Unknown subencoding: ' + subencoding);
66
+ }
67
+ }
68
+ }
69
+ this._length = 0;
70
+ return true;
71
+ }
72
+
73
+ _getBitsPerPixelInPalette(paletteSize) {
74
+ if (paletteSize <= 2) {
75
+ return 1;
76
+ } else if (paletteSize <= 4) {
77
+ return 2;
78
+ } else if (paletteSize <= 16) {
79
+ return 4;
80
+ }
81
+ }
82
+
83
+ _readPixels(pixels) {
84
+ let data = this._pixelBuffer;
85
+ const buffer = this._inflator.inflate(3*pixels);
86
+ for (let i = 0, j = 0; i < pixels*4; i += 4, j += 3) {
87
+ data[i] = buffer[j];
88
+ data[i + 1] = buffer[j + 1];
89
+ data[i + 2] = buffer[j + 2];
90
+ data[i + 3] = 255; // Add the Alpha
91
+ }
92
+ return data;
93
+ }
94
+
95
+ _decodePaletteTile(paletteSize, tileSize, tilew, tileh) {
96
+ const data = this._tileBuffer;
97
+ const palette = this._readPixels(paletteSize);
98
+ const bitsPerPixel = this._getBitsPerPixelInPalette(paletteSize);
99
+ const mask = (1 << bitsPerPixel) - 1;
100
+
101
+ let offset = 0;
102
+ let encoded = this._inflator.inflate(1)[0];
103
+
104
+ for (let y=0; y<tileh; y++) {
105
+ let shift = 8-bitsPerPixel;
106
+ for (let x=0; x<tilew; x++) {
107
+ if (shift<0) {
108
+ shift=8-bitsPerPixel;
109
+ encoded = this._inflator.inflate(1)[0];
110
+ }
111
+ let indexInPalette = (encoded>>shift) & mask;
112
+
113
+ data[offset] = palette[indexInPalette * 4];
114
+ data[offset + 1] = palette[indexInPalette * 4 + 1];
115
+ data[offset + 2] = palette[indexInPalette * 4 + 2];
116
+ data[offset + 3] = palette[indexInPalette * 4 + 3];
117
+ offset += 4;
118
+ shift-=bitsPerPixel;
119
+ }
120
+ if (shift<8-bitsPerPixel && y<tileh-1) {
121
+ encoded = this._inflator.inflate(1)[0];
122
+ }
123
+ }
124
+ return data;
125
+ }
126
+
127
+ _decodeRLETile(tileSize) {
128
+ const data = this._tileBuffer;
129
+ let i = 0;
130
+ while (i < tileSize) {
131
+ const pixel = this._readPixels(1);
132
+ const length = this._readRLELength();
133
+ for (let j = 0; j < length; j++) {
134
+ data[i * 4] = pixel[0];
135
+ data[i * 4 + 1] = pixel[1];
136
+ data[i * 4 + 2] = pixel[2];
137
+ data[i * 4 + 3] = pixel[3];
138
+ i++;
139
+ }
140
+ }
141
+ return data;
142
+ }
143
+
144
+ _decodeRLEPaletteTile(paletteSize, tileSize) {
145
+ const data = this._tileBuffer;
146
+
147
+ // palette
148
+ const palette = this._readPixels(paletteSize);
149
+
150
+ let offset = 0;
151
+ while (offset < tileSize) {
152
+ let indexInPalette = this._inflator.inflate(1)[0];
153
+ let length = 1;
154
+ if (indexInPalette >= 128) {
155
+ indexInPalette -= 128;
156
+ length = this._readRLELength();
157
+ }
158
+ if (indexInPalette > paletteSize) {
159
+ throw new Error('Too big index in palette: ' + indexInPalette + ', palette size: ' + paletteSize);
160
+ }
161
+ if (offset + length > tileSize) {
162
+ throw new Error('Too big rle length in palette mode: ' + length + ', allowed length is: ' + (tileSize - offset));
163
+ }
164
+
165
+ for (let j = 0; j < length; j++) {
166
+ data[offset * 4] = palette[indexInPalette * 4];
167
+ data[offset * 4 + 1] = palette[indexInPalette * 4 + 1];
168
+ data[offset * 4 + 2] = palette[indexInPalette * 4 + 2];
169
+ data[offset * 4 + 3] = palette[indexInPalette * 4 + 3];
170
+ offset++;
171
+ }
172
+ }
173
+ return data;
174
+ }
175
+
176
+ _readRLELength() {
177
+ let length = 0;
178
+ let current = 0;
179
+ do {
180
+ current = this._inflator.inflate(1)[0];
181
+ length += current;
182
+ } while (current === 255);
183
+ return length + 1;
184
+ }
185
+ }