/src/FreeRDP/libfreerdp/codec/zgfx.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * FreeRDP: A Remote Desktop Protocol Implementation |
3 | | * ZGFX (RDP8) Bulk Data Compression |
4 | | * |
5 | | * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com> |
6 | | * Copyright 2017 Armin Novak <armin.novak@thincast.com> |
7 | | * Copyright 2017 Thincast Technologies GmbH |
8 | | * |
9 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
10 | | * you may not use this file except in compliance with the License. |
11 | | * You may obtain a copy of the License at |
12 | | * |
13 | | * http://www.apache.org/licenses/LICENSE-2.0 |
14 | | * |
15 | | * Unless required by applicable law or agreed to in writing, software |
16 | | * distributed under the License is distributed on an "AS IS" BASIS, |
17 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
18 | | * See the License for the specific language governing permissions and |
19 | | * limitations under the License. |
20 | | */ |
21 | | |
22 | | #include <freerdp/config.h> |
23 | | |
24 | | #include <winpr/crt.h> |
25 | | #include <winpr/print.h> |
26 | | #include <winpr/bitstream.h> |
27 | | |
28 | | #include <freerdp/log.h> |
29 | | #include <freerdp/codec/zgfx.h> |
30 | | |
31 | | #define TAG FREERDP_TAG("codec") |
32 | | |
33 | | /** |
34 | | * RDP8 Compressor Limits: |
35 | | * |
36 | | * Maximum number of uncompressed bytes in a single segment: 65535 |
37 | | * Maximum match distance / minimum history size: 2500000 bytes. |
38 | | * Maximum number of segments: 65535 |
39 | | * Maximum expansion of a segment (when compressed size exceeds uncompressed): 1000 bytes |
40 | | * Minimum match length: 3 bytes |
41 | | */ |
42 | | |
43 | | typedef struct |
44 | | { |
45 | | UINT32 prefixLength; |
46 | | UINT32 prefixCode; |
47 | | UINT32 valueBits; |
48 | | UINT32 tokenType; |
49 | | UINT32 valueBase; |
50 | | } ZGFX_TOKEN; |
51 | | |
52 | | struct S_ZGFX_CONTEXT |
53 | | { |
54 | | BOOL Compressor; |
55 | | |
56 | | const BYTE* pbInputCurrent; |
57 | | const BYTE* pbInputEnd; |
58 | | |
59 | | UINT32 bits; |
60 | | UINT32 cBitsRemaining; |
61 | | UINT32 BitsCurrent; |
62 | | UINT32 cBitsCurrent; |
63 | | |
64 | | BYTE OutputBuffer[65536]; |
65 | | UINT32 OutputCount; |
66 | | |
67 | | BYTE HistoryBuffer[2500000]; |
68 | | UINT32 HistoryIndex; |
69 | | UINT32 HistoryBufferSize; |
70 | | }; |
71 | | |
72 | | static const ZGFX_TOKEN ZGFX_TOKEN_TABLE[] = { |
73 | | // len code vbits type vbase |
74 | | { 1, 0, 8, 0, 0 }, // 0 |
75 | | { 5, 17, 5, 1, 0 }, // 10001 |
76 | | { 5, 18, 7, 1, 32 }, // 10010 |
77 | | { 5, 19, 9, 1, 160 }, // 10011 |
78 | | { 5, 20, 10, 1, 672 }, // 10100 |
79 | | { 5, 21, 12, 1, 1696 }, // 10101 |
80 | | { 5, 24, 0, 0, 0x00 }, // 11000 |
81 | | { 5, 25, 0, 0, 0x01 }, // 11001 |
82 | | { 6, 44, 14, 1, 5792 }, // 101100 |
83 | | { 6, 45, 15, 1, 22176 }, // 101101 |
84 | | { 6, 52, 0, 0, 0x02 }, // 110100 |
85 | | { 6, 53, 0, 0, 0x03 }, // 110101 |
86 | | { 6, 54, 0, 0, 0xFF }, // 110110 |
87 | | { 7, 92, 18, 1, 54944 }, // 1011100 |
88 | | { 7, 93, 20, 1, 317088 }, // 1011101 |
89 | | { 7, 110, 0, 0, 0x04 }, // 1101110 |
90 | | { 7, 111, 0, 0, 0x05 }, // 1101111 |
91 | | { 7, 112, 0, 0, 0x06 }, // 1110000 |
92 | | { 7, 113, 0, 0, 0x07 }, // 1110001 |
93 | | { 7, 114, 0, 0, 0x08 }, // 1110010 |
94 | | { 7, 115, 0, 0, 0x09 }, // 1110011 |
95 | | { 7, 116, 0, 0, 0x0A }, // 1110100 |
96 | | { 7, 117, 0, 0, 0x0B }, // 1110101 |
97 | | { 7, 118, 0, 0, 0x3A }, // 1110110 |
98 | | { 7, 119, 0, 0, 0x3B }, // 1110111 |
99 | | { 7, 120, 0, 0, 0x3C }, // 1111000 |
100 | | { 7, 121, 0, 0, 0x3D }, // 1111001 |
101 | | { 7, 122, 0, 0, 0x3E }, // 1111010 |
102 | | { 7, 123, 0, 0, 0x3F }, // 1111011 |
103 | | { 7, 124, 0, 0, 0x40 }, // 1111100 |
104 | | { 7, 125, 0, 0, 0x80 }, // 1111101 |
105 | | { 8, 188, 20, 1, 1365664 }, // 10111100 |
106 | | { 8, 189, 21, 1, 2414240 }, // 10111101 |
107 | | { 8, 252, 0, 0, 0x0C }, // 11111100 |
108 | | { 8, 253, 0, 0, 0x38 }, // 11111101 |
109 | | { 8, 254, 0, 0, 0x39 }, // 11111110 |
110 | | { 8, 255, 0, 0, 0x66 }, // 11111111 |
111 | | { 9, 380, 22, 1, 4511392 }, // 101111100 |
112 | | { 9, 381, 23, 1, 8705696 }, // 101111101 |
113 | | { 9, 382, 24, 1, 17094304 }, // 101111110 |
114 | | { 0 } |
115 | | }; |
116 | | |
117 | | static INLINE BOOL zgfx_GetBits(ZGFX_CONTEXT* WINPR_RESTRICT zgfx, UINT32 nbits) |
118 | 48.5M | { |
119 | 48.5M | if (!zgfx) |
120 | 0 | return FALSE; |
121 | | |
122 | 74.8M | while (zgfx->cBitsCurrent < nbits) |
123 | 26.3M | { |
124 | 26.3M | zgfx->BitsCurrent <<= 8; |
125 | | |
126 | 26.3M | if (zgfx->pbInputCurrent < zgfx->pbInputEnd) |
127 | 1.92M | zgfx->BitsCurrent += *(zgfx->pbInputCurrent)++; |
128 | | |
129 | 26.3M | zgfx->cBitsCurrent += 8; |
130 | 26.3M | } |
131 | | |
132 | 48.5M | zgfx->cBitsRemaining -= nbits; |
133 | 48.5M | zgfx->cBitsCurrent -= nbits; |
134 | 48.5M | zgfx->bits = zgfx->BitsCurrent >> zgfx->cBitsCurrent; |
135 | 48.5M | zgfx->BitsCurrent &= ((1 << zgfx->cBitsCurrent) - 1); |
136 | 48.5M | return TRUE; |
137 | 48.5M | } |
138 | | |
139 | | static INLINE void zgfx_history_buffer_ring_write(ZGFX_CONTEXT* WINPR_RESTRICT zgfx, |
140 | | const BYTE* WINPR_RESTRICT src, size_t count) |
141 | 42.5k | { |
142 | 42.5k | UINT32 front = 0; |
143 | | |
144 | 42.5k | if (count <= 0) |
145 | 1.03k | return; |
146 | | |
147 | 41.5k | if (count > zgfx->HistoryBufferSize) |
148 | 0 | { |
149 | 0 | const size_t residue = count - zgfx->HistoryBufferSize; |
150 | 0 | count = zgfx->HistoryBufferSize; |
151 | 0 | src += residue; |
152 | 0 | zgfx->HistoryIndex = (zgfx->HistoryIndex + residue) % zgfx->HistoryBufferSize; |
153 | 0 | } |
154 | | |
155 | 41.5k | if (zgfx->HistoryIndex + count <= zgfx->HistoryBufferSize) |
156 | 41.5k | { |
157 | 41.5k | CopyMemory(&(zgfx->HistoryBuffer[zgfx->HistoryIndex]), src, count); |
158 | | |
159 | 41.5k | if ((zgfx->HistoryIndex += count) == zgfx->HistoryBufferSize) |
160 | 0 | zgfx->HistoryIndex = 0; |
161 | 41.5k | } |
162 | 0 | else |
163 | 0 | { |
164 | 0 | front = zgfx->HistoryBufferSize - zgfx->HistoryIndex; |
165 | 0 | CopyMemory(&(zgfx->HistoryBuffer[zgfx->HistoryIndex]), src, front); |
166 | 0 | CopyMemory(zgfx->HistoryBuffer, &src[front], count - front); |
167 | 0 | zgfx->HistoryIndex = count - front; |
168 | 0 | } |
169 | 41.5k | } |
170 | | |
171 | | static INLINE void zgfx_history_buffer_ring_read(ZGFX_CONTEXT* WINPR_RESTRICT zgfx, int offset, |
172 | | BYTE* WINPR_RESTRICT dst, UINT32 count) |
173 | 41.0k | { |
174 | 41.0k | UINT32 front = 0; |
175 | 41.0k | UINT32 index = 0; |
176 | 41.0k | INT32 bytes = 0; |
177 | 41.0k | UINT32 valid = 0; |
178 | 41.0k | INT32 bytesLeft = 0; |
179 | 41.0k | BYTE* dptr = dst; |
180 | 41.0k | BYTE* origDst = dst; |
181 | | |
182 | 41.0k | if ((count <= 0) || (count > INT32_MAX)) |
183 | 425 | return; |
184 | | |
185 | 40.6k | bytesLeft = (INT32)count; |
186 | 40.6k | index = (zgfx->HistoryIndex + zgfx->HistoryBufferSize - offset) % zgfx->HistoryBufferSize; |
187 | 40.6k | bytes = MIN(bytesLeft, offset); |
188 | | |
189 | 40.6k | if ((index + bytes) <= zgfx->HistoryBufferSize) |
190 | 40.5k | { |
191 | 40.5k | CopyMemory(dptr, &(zgfx->HistoryBuffer[index]), bytes); |
192 | 40.5k | } |
193 | 111 | else |
194 | 111 | { |
195 | 111 | front = zgfx->HistoryBufferSize - index; |
196 | 111 | CopyMemory(dptr, &(zgfx->HistoryBuffer[index]), front); |
197 | 111 | CopyMemory(&dptr[front], zgfx->HistoryBuffer, bytes - front); |
198 | 111 | } |
199 | | |
200 | 40.6k | if ((bytesLeft -= bytes) == 0) |
201 | 39.2k | return; |
202 | | |
203 | 1.36k | dptr += bytes; |
204 | 1.36k | valid = bytes; |
205 | | |
206 | 1.36k | do |
207 | 2.87k | { |
208 | 2.87k | bytes = valid; |
209 | | |
210 | 2.87k | if (bytes > bytesLeft) |
211 | 1.34k | bytes = bytesLeft; |
212 | | |
213 | 2.87k | CopyMemory(dptr, origDst, bytes); |
214 | 2.87k | dptr += bytes; |
215 | 2.87k | valid <<= 1; |
216 | 2.87k | } while ((bytesLeft -= bytes) > 0); |
217 | 1.36k | } |
218 | | |
219 | | static INLINE BOOL zgfx_decompress_segment(ZGFX_CONTEXT* WINPR_RESTRICT zgfx, |
220 | | wStream* WINPR_RESTRICT stream, size_t segmentSize) |
221 | 3.00k | { |
222 | 3.00k | BYTE c = 0; |
223 | 3.00k | BYTE flags = 0; |
224 | 3.00k | UINT32 extra = 0; |
225 | 3.00k | int opIndex = 0; |
226 | 3.00k | UINT32 haveBits = 0; |
227 | 3.00k | UINT32 inPrefix = 0; |
228 | 3.00k | UINT32 count = 0; |
229 | 3.00k | UINT32 distance = 0; |
230 | 3.00k | BYTE* pbSegment = NULL; |
231 | 3.00k | size_t cbSegment = 0; |
232 | | |
233 | 3.00k | WINPR_ASSERT(zgfx); |
234 | 3.00k | WINPR_ASSERT(stream); |
235 | | |
236 | 3.00k | if (segmentSize < 2) |
237 | 11 | return FALSE; |
238 | | |
239 | 2.99k | cbSegment = segmentSize - 1; |
240 | | |
241 | 2.99k | if (!Stream_CheckAndLogRequiredLength(TAG, stream, segmentSize) || (segmentSize > UINT32_MAX)) |
242 | 146 | return FALSE; |
243 | | |
244 | 2.84k | Stream_Read_UINT8(stream, flags); /* header (1 byte) */ |
245 | 2.84k | zgfx->OutputCount = 0; |
246 | 2.84k | pbSegment = Stream_Pointer(stream); |
247 | 2.84k | if (!Stream_SafeSeek(stream, cbSegment)) |
248 | 0 | return FALSE; |
249 | | |
250 | 2.84k | if (!(flags & PACKET_COMPRESSED)) |
251 | 457 | { |
252 | 457 | zgfx_history_buffer_ring_write(zgfx, pbSegment, cbSegment); |
253 | | |
254 | 457 | if (cbSegment > sizeof(zgfx->OutputBuffer)) |
255 | 8 | return FALSE; |
256 | | |
257 | 449 | CopyMemory(zgfx->OutputBuffer, pbSegment, cbSegment); |
258 | 449 | zgfx->OutputCount = cbSegment; |
259 | 449 | return TRUE; |
260 | 457 | } |
261 | | |
262 | 2.39k | zgfx->pbInputCurrent = pbSegment; |
263 | 2.39k | zgfx->pbInputEnd = &pbSegment[cbSegment - 1]; |
264 | | /* NumberOfBitsToDecode = ((NumberOfBytesToDecode - 1) * 8) - ValueOfLastByte */ |
265 | 2.39k | const UINT32 bits = 8u * (cbSegment - 1u); |
266 | 2.39k | if (bits < *zgfx->pbInputEnd) |
267 | 32 | return FALSE; |
268 | | |
269 | 2.35k | zgfx->cBitsRemaining = bits - *zgfx->pbInputEnd; |
270 | 2.35k | zgfx->cBitsCurrent = 0; |
271 | 2.35k | zgfx->BitsCurrent = 0; |
272 | | |
273 | 23.3M | while (zgfx->cBitsRemaining) |
274 | 23.3M | { |
275 | 23.3M | haveBits = 0; |
276 | 23.3M | inPrefix = 0; |
277 | | |
278 | 30.0M | for (opIndex = 0; ZGFX_TOKEN_TABLE[opIndex].prefixLength != 0; opIndex++) |
279 | 30.0M | { |
280 | 55.0M | while (haveBits < ZGFX_TOKEN_TABLE[opIndex].prefixLength) |
281 | 25.0M | { |
282 | 25.0M | zgfx_GetBits(zgfx, 1); |
283 | 25.0M | inPrefix = (inPrefix << 1) + zgfx->bits; |
284 | 25.0M | haveBits++; |
285 | 25.0M | } |
286 | | |
287 | 30.0M | if (inPrefix == ZGFX_TOKEN_TABLE[opIndex].prefixCode) |
288 | 23.3M | { |
289 | 23.3M | if (ZGFX_TOKEN_TABLE[opIndex].tokenType == 0) |
290 | 23.3M | { |
291 | | /* Literal */ |
292 | 23.3M | zgfx_GetBits(zgfx, ZGFX_TOKEN_TABLE[opIndex].valueBits); |
293 | 23.3M | c = (BYTE)(ZGFX_TOKEN_TABLE[opIndex].valueBase + zgfx->bits); |
294 | 23.3M | zgfx->HistoryBuffer[zgfx->HistoryIndex] = c; |
295 | | |
296 | 23.3M | if (++zgfx->HistoryIndex == zgfx->HistoryBufferSize) |
297 | 0 | zgfx->HistoryIndex = 0; |
298 | | |
299 | 23.3M | if (zgfx->OutputCount >= sizeof(zgfx->OutputBuffer)) |
300 | 383 | return FALSE; |
301 | | |
302 | 23.3M | zgfx->OutputBuffer[zgfx->OutputCount++] = c; |
303 | 23.3M | } |
304 | 42.2k | else |
305 | 42.2k | { |
306 | 42.2k | zgfx_GetBits(zgfx, ZGFX_TOKEN_TABLE[opIndex].valueBits); |
307 | 42.2k | distance = ZGFX_TOKEN_TABLE[opIndex].valueBase + zgfx->bits; |
308 | | |
309 | 42.2k | if (distance != 0) |
310 | 41.1k | { |
311 | | /* Match */ |
312 | 41.1k | zgfx_GetBits(zgfx, 1); |
313 | | |
314 | 41.1k | if (zgfx->bits == 0) |
315 | 27.6k | { |
316 | 27.6k | count = 3; |
317 | 27.6k | } |
318 | 13.4k | else |
319 | 13.4k | { |
320 | 13.4k | count = 4; |
321 | 13.4k | extra = 2; |
322 | 13.4k | zgfx_GetBits(zgfx, 1); |
323 | | |
324 | 61.4k | while (zgfx->bits == 1) |
325 | 47.9k | { |
326 | 47.9k | count *= 2; |
327 | 47.9k | extra++; |
328 | 47.9k | zgfx_GetBits(zgfx, 1); |
329 | 47.9k | } |
330 | | |
331 | 13.4k | zgfx_GetBits(zgfx, extra); |
332 | 13.4k | count += zgfx->bits; |
333 | 13.4k | } |
334 | | |
335 | 41.1k | if (count > sizeof(zgfx->OutputBuffer) - zgfx->OutputCount) |
336 | 62 | return FALSE; |
337 | | |
338 | 41.0k | zgfx_history_buffer_ring_read( |
339 | 41.0k | zgfx, distance, &(zgfx->OutputBuffer[zgfx->OutputCount]), count); |
340 | 41.0k | zgfx_history_buffer_ring_write( |
341 | 41.0k | zgfx, &(zgfx->OutputBuffer[zgfx->OutputCount]), count); |
342 | 41.0k | zgfx->OutputCount += count; |
343 | 41.0k | } |
344 | 1.08k | else |
345 | 1.08k | { |
346 | | /* Unencoded */ |
347 | 1.08k | zgfx_GetBits(zgfx, 15); |
348 | 1.08k | count = zgfx->bits; |
349 | 1.08k | zgfx->cBitsRemaining -= zgfx->cBitsCurrent; |
350 | 1.08k | zgfx->cBitsCurrent = 0; |
351 | 1.08k | zgfx->BitsCurrent = 0; |
352 | | |
353 | 1.08k | if (count > sizeof(zgfx->OutputBuffer) - zgfx->OutputCount) |
354 | 1 | return FALSE; |
355 | 1.08k | else if (count > zgfx->cBitsRemaining / 8) |
356 | 25 | return FALSE; |
357 | 1.05k | else if (zgfx->pbInputCurrent + count > zgfx->pbInputEnd) |
358 | 20 | return FALSE; |
359 | | |
360 | 1.03k | CopyMemory(&(zgfx->OutputBuffer[zgfx->OutputCount]), zgfx->pbInputCurrent, |
361 | 1.03k | count); |
362 | 1.03k | zgfx_history_buffer_ring_write(zgfx, zgfx->pbInputCurrent, count); |
363 | 1.03k | zgfx->pbInputCurrent += count; |
364 | 1.03k | zgfx->cBitsRemaining -= (8 * count); |
365 | 1.03k | zgfx->OutputCount += count; |
366 | 1.03k | } |
367 | 42.2k | } |
368 | | |
369 | 23.3M | break; |
370 | 23.3M | } |
371 | 30.0M | } |
372 | 23.3M | } |
373 | | |
374 | 1.86k | return TRUE; |
375 | 2.35k | } |
376 | | |
377 | | /* Allocate the buffers a bit larger. |
378 | | * |
379 | | * Due to optimizations some h264 decoders will read data beyond |
380 | | * the actual available data, so ensure that it will never be a |
381 | | * out of bounds read. |
382 | | */ |
383 | | static INLINE BYTE* aligned_zgfx_malloc(size_t size) |
384 | 0 | { |
385 | 0 | return malloc(size + 64); |
386 | 0 | } |
387 | | |
388 | | static INLINE BOOL zgfx_append(ZGFX_CONTEXT* WINPR_RESTRICT zgfx, |
389 | | BYTE** WINPR_RESTRICT ppConcatenated, size_t uncompressedSize, |
390 | | size_t* WINPR_RESTRICT pUsed) |
391 | 2.31k | { |
392 | 2.31k | WINPR_ASSERT(zgfx); |
393 | 2.31k | WINPR_ASSERT(ppConcatenated); |
394 | 2.31k | WINPR_ASSERT(pUsed); |
395 | | |
396 | 2.31k | const size_t used = *pUsed; |
397 | 2.31k | if (zgfx->OutputCount > UINT32_MAX - used) |
398 | 0 | return FALSE; |
399 | | |
400 | 2.31k | if (used + zgfx->OutputCount > uncompressedSize) |
401 | 26 | return FALSE; |
402 | | |
403 | 2.28k | BYTE* tmp = realloc(*ppConcatenated, used + zgfx->OutputCount + 64ull); |
404 | 2.28k | if (!tmp) |
405 | 0 | return FALSE; |
406 | 2.28k | *ppConcatenated = tmp; |
407 | 2.28k | CopyMemory(&tmp[used], zgfx->OutputBuffer, zgfx->OutputCount); |
408 | 2.28k | *pUsed = used + zgfx->OutputCount; |
409 | 2.28k | return TRUE; |
410 | 2.28k | } |
411 | | |
412 | | int zgfx_decompress(ZGFX_CONTEXT* WINPR_RESTRICT zgfx, const BYTE* WINPR_RESTRICT pSrcData, |
413 | | UINT32 SrcSize, BYTE** WINPR_RESTRICT ppDstData, |
414 | | UINT32* WINPR_RESTRICT pDstSize, UINT32 flags) |
415 | 5.40k | { |
416 | 5.40k | int status = -1; |
417 | 5.40k | BYTE descriptor = 0; |
418 | 5.40k | wStream sbuffer = { 0 }; |
419 | 5.40k | size_t used = 0; |
420 | 5.40k | BYTE* pConcatenated = NULL; |
421 | 5.40k | wStream* stream = Stream_StaticConstInit(&sbuffer, pSrcData, SrcSize); |
422 | | |
423 | 5.40k | WINPR_ASSERT(zgfx); |
424 | 5.40k | WINPR_ASSERT(stream); |
425 | 5.40k | WINPR_ASSERT(ppDstData); |
426 | 5.40k | WINPR_ASSERT(pDstSize); |
427 | | |
428 | 5.40k | *ppDstData = NULL; |
429 | 5.40k | *pDstSize = 0; |
430 | | |
431 | 5.40k | if (!Stream_CheckAndLogRequiredLength(TAG, stream, 1)) |
432 | 0 | goto fail; |
433 | | |
434 | 5.40k | Stream_Read_UINT8(stream, descriptor); /* descriptor (1 byte) */ |
435 | | |
436 | 5.40k | if (descriptor == ZGFX_SEGMENTED_SINGLE) |
437 | 638 | { |
438 | 638 | if (!zgfx_decompress_segment(zgfx, stream, Stream_GetRemainingLength(stream))) |
439 | 511 | goto fail; |
440 | | |
441 | 127 | if (zgfx->OutputCount > 0) |
442 | 123 | { |
443 | 123 | if (!zgfx_append(zgfx, &pConcatenated, zgfx->OutputCount, &used)) |
444 | 0 | goto fail; |
445 | 123 | if (used != zgfx->OutputCount) |
446 | 0 | goto fail; |
447 | 123 | *ppDstData = pConcatenated; |
448 | 123 | *pDstSize = zgfx->OutputCount; |
449 | 123 | } |
450 | 127 | } |
451 | 4.76k | else if (descriptor == ZGFX_SEGMENTED_MULTIPART) |
452 | 384 | { |
453 | 384 | UINT32 segmentSize = 0; |
454 | 384 | UINT16 segmentNumber = 0; |
455 | 384 | UINT16 segmentCount = 0; |
456 | 384 | UINT32 uncompressedSize = 0; |
457 | | |
458 | 384 | if (!Stream_CheckAndLogRequiredLength(TAG, stream, 6)) |
459 | 39 | goto fail; |
460 | | |
461 | 345 | Stream_Read_UINT16(stream, segmentCount); /* segmentCount (2 bytes) */ |
462 | 345 | Stream_Read_UINT32(stream, uncompressedSize); /* uncompressedSize (4 bytes) */ |
463 | | |
464 | 2.50k | for (segmentNumber = 0; segmentNumber < segmentCount; segmentNumber++) |
465 | 2.47k | { |
466 | 2.47k | if (!Stream_CheckAndLogRequiredLength(TAG, stream, sizeof(UINT32))) |
467 | 105 | goto fail; |
468 | | |
469 | 2.36k | Stream_Read_UINT32(stream, segmentSize); /* segmentSize (4 bytes) */ |
470 | | |
471 | 2.36k | if (!zgfx_decompress_segment(zgfx, stream, segmentSize)) |
472 | 177 | goto fail; |
473 | | |
474 | 2.19k | if (!zgfx_append(zgfx, &pConcatenated, uncompressedSize, &used)) |
475 | 26 | goto fail; |
476 | 2.19k | } |
477 | | |
478 | 37 | if (used != uncompressedSize) |
479 | 35 | goto fail; |
480 | | |
481 | 2 | *ppDstData = pConcatenated; |
482 | 2 | *pDstSize = uncompressedSize; |
483 | 2 | } |
484 | 4.38k | else |
485 | 4.38k | { |
486 | 4.38k | goto fail; |
487 | 4.38k | } |
488 | | |
489 | 129 | status = 1; |
490 | 5.40k | fail: |
491 | 5.40k | if (status < 0) |
492 | 5.27k | free(pConcatenated); |
493 | 5.40k | return status; |
494 | 129 | } |
495 | | |
496 | | static BOOL zgfx_compress_segment(ZGFX_CONTEXT* WINPR_RESTRICT zgfx, wStream* WINPR_RESTRICT s, |
497 | | const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize, |
498 | | UINT32* WINPR_RESTRICT pFlags) |
499 | 0 | { |
500 | | /* FIXME: Currently compression not implemented. Just copy the raw source */ |
501 | 0 | if (!Stream_EnsureRemainingCapacity(s, SrcSize + 1)) |
502 | 0 | { |
503 | 0 | WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); |
504 | 0 | return FALSE; |
505 | 0 | } |
506 | | |
507 | 0 | (*pFlags) |= ZGFX_PACKET_COMPR_TYPE_RDP8; /* RDP 8.0 compression format */ |
508 | 0 | Stream_Write_UINT8(s, (*pFlags)); /* header (1 byte) */ |
509 | 0 | Stream_Write(s, pSrcData, SrcSize); |
510 | 0 | return TRUE; |
511 | 0 | } |
512 | | |
513 | | int zgfx_compress_to_stream(ZGFX_CONTEXT* WINPR_RESTRICT zgfx, wStream* WINPR_RESTRICT sDst, |
514 | | const BYTE* WINPR_RESTRICT pUncompressed, UINT32 uncompressedSize, |
515 | | UINT32* WINPR_RESTRICT pFlags) |
516 | 0 | { |
517 | 0 | int fragment = 0; |
518 | 0 | UINT16 maxLength = 0; |
519 | 0 | UINT32 totalLength = 0; |
520 | 0 | size_t posSegmentCount = 0; |
521 | 0 | const BYTE* pSrcData = NULL; |
522 | 0 | int status = 0; |
523 | 0 | maxLength = ZGFX_SEGMENTED_MAXSIZE; |
524 | 0 | totalLength = uncompressedSize; |
525 | 0 | pSrcData = pUncompressed; |
526 | |
|
527 | 0 | for (; (totalLength > 0) || (fragment == 0); fragment++) |
528 | 0 | { |
529 | 0 | UINT32 SrcSize = 0; |
530 | 0 | size_t posDstSize = 0; |
531 | 0 | size_t posDataStart = 0; |
532 | 0 | UINT32 DstSize = 0; |
533 | 0 | SrcSize = (totalLength > maxLength) ? maxLength : totalLength; |
534 | 0 | posDstSize = 0; |
535 | 0 | totalLength -= SrcSize; |
536 | | |
537 | | /* Ensure we have enough space for headers */ |
538 | 0 | if (!Stream_EnsureRemainingCapacity(sDst, 12)) |
539 | 0 | { |
540 | 0 | WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); |
541 | 0 | return -1; |
542 | 0 | } |
543 | | |
544 | 0 | if (fragment == 0) |
545 | 0 | { |
546 | | /* First fragment */ |
547 | | /* descriptor (1 byte) */ |
548 | 0 | Stream_Write_UINT8(sDst, (totalLength == 0) ? ZGFX_SEGMENTED_SINGLE |
549 | 0 | : ZGFX_SEGMENTED_MULTIPART); |
550 | |
|
551 | 0 | if (totalLength > 0) |
552 | 0 | { |
553 | 0 | posSegmentCount = Stream_GetPosition(sDst); /* segmentCount (2 bytes) */ |
554 | 0 | Stream_Seek(sDst, 2); |
555 | 0 | Stream_Write_UINT32(sDst, uncompressedSize); /* uncompressedSize (4 bytes) */ |
556 | 0 | } |
557 | 0 | } |
558 | |
|
559 | 0 | if (fragment > 0 || totalLength > 0) |
560 | 0 | { |
561 | | /* Multipart */ |
562 | 0 | posDstSize = Stream_GetPosition(sDst); /* size (4 bytes) */ |
563 | 0 | Stream_Seek(sDst, 4); |
564 | 0 | } |
565 | |
|
566 | 0 | posDataStart = Stream_GetPosition(sDst); |
567 | |
|
568 | 0 | if (!zgfx_compress_segment(zgfx, sDst, pSrcData, SrcSize, pFlags)) |
569 | 0 | return -1; |
570 | | |
571 | 0 | if (posDstSize) |
572 | 0 | { |
573 | | /* Fill segment data size */ |
574 | 0 | DstSize = Stream_GetPosition(sDst) - posDataStart; |
575 | 0 | Stream_SetPosition(sDst, posDstSize); |
576 | 0 | Stream_Write_UINT32(sDst, DstSize); |
577 | 0 | Stream_SetPosition(sDst, posDataStart + DstSize); |
578 | 0 | } |
579 | |
|
580 | 0 | pSrcData += SrcSize; |
581 | 0 | } |
582 | | |
583 | 0 | Stream_SealLength(sDst); |
584 | | |
585 | | /* fill back segmentCount */ |
586 | 0 | if (posSegmentCount) |
587 | 0 | { |
588 | 0 | Stream_SetPosition(sDst, posSegmentCount); |
589 | 0 | Stream_Write_UINT16(sDst, fragment); |
590 | 0 | Stream_SetPosition(sDst, Stream_Length(sDst)); |
591 | 0 | } |
592 | |
|
593 | 0 | return status; |
594 | 0 | } |
595 | | |
596 | | int zgfx_compress(ZGFX_CONTEXT* WINPR_RESTRICT zgfx, const BYTE* WINPR_RESTRICT pSrcData, |
597 | | UINT32 SrcSize, BYTE** WINPR_RESTRICT ppDstData, UINT32* WINPR_RESTRICT pDstSize, |
598 | | UINT32* WINPR_RESTRICT pFlags) |
599 | 0 | { |
600 | 0 | int status = 0; |
601 | 0 | wStream* s = Stream_New(NULL, SrcSize); |
602 | 0 | status = zgfx_compress_to_stream(zgfx, s, pSrcData, SrcSize, pFlags); |
603 | 0 | (*ppDstData) = Stream_Buffer(s); |
604 | 0 | (*pDstSize) = Stream_GetPosition(s); |
605 | 0 | Stream_Free(s, FALSE); |
606 | 0 | return status; |
607 | 0 | } |
608 | | |
609 | | void zgfx_context_reset(ZGFX_CONTEXT* WINPR_RESTRICT zgfx, BOOL flush) |
610 | 5.40k | { |
611 | 5.40k | zgfx->HistoryIndex = 0; |
612 | 5.40k | } |
613 | | |
614 | | ZGFX_CONTEXT* zgfx_context_new(BOOL Compressor) |
615 | 5.40k | { |
616 | 5.40k | ZGFX_CONTEXT* zgfx = NULL; |
617 | 5.40k | zgfx = (ZGFX_CONTEXT*)calloc(1, sizeof(ZGFX_CONTEXT)); |
618 | | |
619 | 5.40k | if (zgfx) |
620 | 5.40k | { |
621 | 5.40k | zgfx->Compressor = Compressor; |
622 | 5.40k | zgfx->HistoryBufferSize = sizeof(zgfx->HistoryBuffer); |
623 | 5.40k | zgfx_context_reset(zgfx, FALSE); |
624 | 5.40k | } |
625 | | |
626 | 5.40k | return zgfx; |
627 | 5.40k | } |
628 | | |
629 | | void zgfx_context_free(ZGFX_CONTEXT* zgfx) |
630 | 5.40k | { |
631 | 5.40k | free(zgfx); |
632 | 5.40k | } |