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