Coverage Report

Created: 2025-07-04 06:53

/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
49.6M
{
121
49.6M
  if (!zgfx)
122
0
    return FALSE;
123
124
76.9M
  while (zgfx->cBitsCurrent < nbits)
125
27.3M
  {
126
27.3M
    zgfx->BitsCurrent <<= 8;
127
128
27.3M
    if (zgfx->pbInputCurrent < zgfx->pbInputEnd)
129
1.88M
      zgfx->BitsCurrent += *(zgfx->pbInputCurrent)++;
130
131
27.3M
    zgfx->cBitsCurrent += 8;
132
27.3M
  }
133
134
49.6M
  zgfx->cBitsRemaining -= nbits;
135
49.6M
  zgfx->cBitsCurrent -= nbits;
136
49.6M
  zgfx->bits = zgfx->BitsCurrent >> zgfx->cBitsCurrent;
137
49.6M
  zgfx->BitsCurrent &= ((1 << zgfx->cBitsCurrent) - 1);
138
49.6M
  return TRUE;
139
49.6M
}
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
23.3k
{
144
23.3k
  if (count <= 0)
145
965
    return;
146
147
22.4k
  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
22.4k
  if (zgfx->HistoryIndex + count <= zgfx->HistoryBufferSize)
156
22.4k
  {
157
22.4k
    CopyMemory(&(zgfx->HistoryBuffer[zgfx->HistoryIndex]), src, count);
158
159
44.8k
    zgfx->HistoryIndex += WINPR_ASSERTING_INT_CAST(uint32_t, count);
160
22.4k
    if (zgfx->HistoryIndex == zgfx->HistoryBufferSize)
161
0
      zgfx->HistoryIndex = 0;
162
44.8k
  }
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
22.4k
}
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
21.8k
{
175
21.8k
  UINT32 front = 0;
176
21.8k
  UINT32 index = 0;
177
21.8k
  INT32 bytes = 0;
178
21.8k
  UINT32 valid = 0;
179
21.8k
  INT32 bytesLeft = 0;
180
21.8k
  BYTE* dptr = dst;
181
21.8k
  BYTE* origDst = dst;
182
183
21.8k
  if ((count <= 0) || (count > INT32_MAX))
184
164
    return;
185
186
21.6k
  bytesLeft = (INT32)count;
187
21.6k
  index = (zgfx->HistoryIndex + zgfx->HistoryBufferSize -
188
43.3k
           WINPR_ASSERTING_INT_CAST(uint32_t, offset)) %
189
0
          zgfx->HistoryBufferSize;
190
21.6k
  bytes = MIN(bytesLeft, offset);
191
192
43.3k
  if ((index + WINPR_ASSERTING_INT_CAST(uint32_t, bytes)) <= zgfx->HistoryBufferSize)
193
21.5k
  {
194
43.1k
    CopyMemory(dptr, &(zgfx->HistoryBuffer[index]), WINPR_ASSERTING_INT_CAST(size_t, bytes));
195
43.1k
  }
196
107
  else
197
107
  {
198
107
    front = zgfx->HistoryBufferSize - index;
199
107
    CopyMemory(dptr, &(zgfx->HistoryBuffer[index]), front);
200
214
    CopyMemory(&dptr[front], zgfx->HistoryBuffer,
201
214
               WINPR_ASSERTING_INT_CAST(uint32_t, bytes) - front);
202
214
  }
203
204
21.6k
  if ((bytesLeft -= bytes) == 0)
205
19.6k
    return;
206
207
1.98k
  dptr += bytes;
208
3.96k
  valid = WINPR_ASSERTING_INT_CAST(uint32_t, bytes);
209
210
0
  do
211
4.16k
  {
212
8.33k
    bytes = WINPR_ASSERTING_INT_CAST(int32_t, valid);
213
214
4.16k
    if (bytes > bytesLeft)
215
1.96k
      bytes = bytesLeft;
216
217
8.33k
    CopyMemory(dptr, origDst, WINPR_ASSERTING_INT_CAST(size_t, bytes));
218
0
    dptr += bytes;
219
8.33k
    valid <<= 1;
220
8.33k
  } while ((bytesLeft -= bytes) > 0);
221
3.96k
}
222
223
static INLINE BOOL zgfx_decompress_segment(ZGFX_CONTEXT* WINPR_RESTRICT zgfx,
224
                                           wStream* WINPR_RESTRICT stream, size_t segmentSize)
225
1.52k
{
226
1.52k
  BYTE c = 0;
227
1.52k
  BYTE flags = 0;
228
1.52k
  UINT32 extra = 0;
229
1.52k
  int opIndex = 0;
230
1.52k
  UINT32 haveBits = 0;
231
1.52k
  UINT32 inPrefix = 0;
232
1.52k
  UINT32 count = 0;
233
1.52k
  UINT32 distance = 0;
234
1.52k
  BYTE* pbSegment = NULL;
235
236
1.52k
  WINPR_ASSERT(zgfx);
237
1.52k
  WINPR_ASSERT(stream);
238
239
1.52k
  if (segmentSize < 2)
240
8
    return FALSE;
241
242
1.51k
  const size_t cbSegment = segmentSize - 1;
243
244
1.51k
  if (!Stream_CheckAndLogRequiredLength(TAG, stream, segmentSize) || (segmentSize > UINT32_MAX))
245
123
    return FALSE;
246
247
1.39k
  Stream_Read_UINT8(stream, flags); /* header (1 byte) */
248
1.39k
  zgfx->OutputCount = 0;
249
1.39k
  pbSegment = Stream_Pointer(stream);
250
1.39k
  if (!Stream_SafeSeek(stream, cbSegment))
251
0
    return FALSE;
252
253
1.39k
  if (!(flags & PACKET_COMPRESSED))
254
313
  {
255
313
    zgfx_history_buffer_ring_write(zgfx, pbSegment, cbSegment);
256
257
313
    if (cbSegment > sizeof(zgfx->OutputBuffer))
258
10
      return FALSE;
259
260
303
    CopyMemory(zgfx->OutputBuffer, pbSegment, cbSegment);
261
303
    zgfx->OutputCount = (UINT32)cbSegment;
262
303
    return TRUE;
263
313
  }
264
265
1.08k
  zgfx->pbInputCurrent = pbSegment;
266
1.08k
  zgfx->pbInputEnd = &pbSegment[cbSegment - 1];
267
  /* NumberOfBitsToDecode = ((NumberOfBytesToDecode - 1) * 8) - ValueOfLastByte */
268
1.08k
  const size_t bits = 8u * (cbSegment - 1u);
269
1.08k
  if (bits > UINT32_MAX)
270
0
    return FALSE;
271
1.08k
  if (bits < *zgfx->pbInputEnd)
272
25
    return FALSE;
273
274
1.05k
  zgfx->cBitsRemaining = (UINT32)(bits - *zgfx->pbInputEnd);
275
1.05k
  zgfx->cBitsCurrent = 0;
276
1.05k
  zgfx->BitsCurrent = 0;
277
278
24.3M
  while (zgfx->cBitsRemaining)
279
24.2M
  {
280
24.2M
    haveBits = 0;
281
24.2M
    inPrefix = 0;
282
283
28.3M
    for (opIndex = 0; ZGFX_TOKEN_TABLE[opIndex].prefixLength != 0; opIndex++)
284
28.3M
    {
285
53.6M
      while (haveBits < ZGFX_TOKEN_TABLE[opIndex].prefixLength)
286
25.2M
      {
287
25.2M
        zgfx_GetBits(zgfx, 1);
288
25.2M
        inPrefix = (inPrefix << 1) + zgfx->bits;
289
25.2M
        haveBits++;
290
25.2M
      }
291
292
28.3M
      if (inPrefix == ZGFX_TOKEN_TABLE[opIndex].prefixCode)
293
24.2M
      {
294
24.2M
        if (ZGFX_TOKEN_TABLE[opIndex].tokenType == 0)
295
24.2M
        {
296
          /* Literal */
297
24.2M
          zgfx_GetBits(zgfx, ZGFX_TOKEN_TABLE[opIndex].valueBits);
298
24.2M
          c = (BYTE)(ZGFX_TOKEN_TABLE[opIndex].valueBase + zgfx->bits);
299
24.2M
          zgfx->HistoryBuffer[zgfx->HistoryIndex] = c;
300
301
24.2M
          if (++zgfx->HistoryIndex == zgfx->HistoryBufferSize)
302
0
            zgfx->HistoryIndex = 0;
303
304
24.2M
          if (zgfx->OutputCount >= sizeof(zgfx->OutputBuffer))
305
410
            return FALSE;
306
307
24.2M
          zgfx->OutputBuffer[zgfx->OutputCount++] = c;
308
24.2M
        }
309
23.1k
        else
310
23.1k
        {
311
23.1k
          zgfx_GetBits(zgfx, ZGFX_TOKEN_TABLE[opIndex].valueBits);
312
23.1k
          distance = ZGFX_TOKEN_TABLE[opIndex].valueBase + zgfx->bits;
313
314
23.1k
          if (distance != 0)
315
21.8k
          {
316
            /* Match */
317
21.8k
            zgfx_GetBits(zgfx, 1);
318
319
21.8k
            if (zgfx->bits == 0)
320
14.6k
            {
321
14.6k
              count = 3;
322
14.6k
            }
323
7.26k
            else
324
7.26k
            {
325
7.26k
              count = 4;
326
7.26k
              extra = 2;
327
7.26k
              zgfx_GetBits(zgfx, 1);
328
329
46.2k
              while (zgfx->bits == 1)
330
38.9k
              {
331
38.9k
                count *= 2;
332
38.9k
                extra++;
333
38.9k
                zgfx_GetBits(zgfx, 1);
334
38.9k
              }
335
336
7.26k
              zgfx_GetBits(zgfx, extra);
337
7.26k
              count += zgfx->bits;
338
7.26k
            }
339
340
21.8k
            if (count > sizeof(zgfx->OutputBuffer) - zgfx->OutputCount)
341
55
              return FALSE;
342
343
43.6k
            zgfx_history_buffer_ring_read(zgfx, WINPR_ASSERTING_INT_CAST(int, distance),
344
0
                                          &(zgfx->OutputBuffer[zgfx->OutputCount]),
345
43.6k
                                          count);
346
0
            zgfx_history_buffer_ring_write(
347
43.6k
                zgfx, &(zgfx->OutputBuffer[zgfx->OutputCount]), count);
348
43.6k
            zgfx->OutputCount += count;
349
43.6k
          }
350
1.29k
          else
351
1.29k
          {
352
            /* Unencoded */
353
1.29k
            zgfx_GetBits(zgfx, 15);
354
1.29k
            count = zgfx->bits;
355
1.29k
            zgfx->cBitsRemaining -= zgfx->cBitsCurrent;
356
1.29k
            zgfx->cBitsCurrent = 0;
357
1.29k
            zgfx->BitsCurrent = 0;
358
359
1.29k
            if (count > sizeof(zgfx->OutputBuffer) - zgfx->OutputCount)
360
2
              return FALSE;
361
1.28k
            else if (count > zgfx->cBitsRemaining / 8)
362
33
              return FALSE;
363
1.25k
            else if (zgfx->pbInputCurrent + count > zgfx->pbInputEnd)
364
23
              return FALSE;
365
366
1.23k
            CopyMemory(&(zgfx->OutputBuffer[zgfx->OutputCount]), zgfx->pbInputCurrent,
367
1.23k
                       count);
368
1.23k
            zgfx_history_buffer_ring_write(zgfx, zgfx->pbInputCurrent, count);
369
1.23k
            zgfx->pbInputCurrent += count;
370
1.23k
            zgfx->cBitsRemaining -= (8 * count);
371
1.23k
            zgfx->OutputCount += count;
372
1.23k
          }
373
23.1k
        }
374
375
24.2M
        break;
376
24.2M
      }
377
28.3M
    }
378
24.2M
  }
379
380
533
  return TRUE;
381
1.05k
}
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
833
{
387
833
  WINPR_ASSERT(zgfx);
388
833
  WINPR_ASSERT(ppConcatenated);
389
833
  WINPR_ASSERT(pUsed);
390
391
833
  const size_t used = *pUsed;
392
833
  if (zgfx->OutputCount > UINT32_MAX - used)
393
0
    return FALSE;
394
395
833
  if (used + zgfx->OutputCount > uncompressedSize)
396
42
    return FALSE;
397
398
791
  BYTE* tmp = realloc(*ppConcatenated, used + zgfx->OutputCount + 64ull);
399
791
  if (!tmp)
400
0
    return FALSE;
401
791
  *ppConcatenated = tmp;
402
791
  CopyMemory(&tmp[used], zgfx->OutputBuffer, zgfx->OutputCount);
403
791
  *pUsed = used + zgfx->OutputCount;
404
791
  return TRUE;
405
791
}
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
5.98k
{
411
5.98k
  int status = -1;
412
5.98k
  BYTE descriptor = 0;
413
5.98k
  wStream sbuffer = { 0 };
414
5.98k
  size_t used = 0;
415
5.98k
  BYTE* pConcatenated = NULL;
416
5.98k
  wStream* stream = Stream_StaticConstInit(&sbuffer, pSrcData, SrcSize);
417
418
5.98k
  WINPR_ASSERT(zgfx);
419
5.98k
  WINPR_ASSERT(stream);
420
5.98k
  WINPR_ASSERT(ppDstData);
421
5.98k
  WINPR_ASSERT(pDstSize);
422
423
5.98k
  *ppDstData = NULL;
424
5.98k
  *pDstSize = 0;
425
426
5.98k
  if (!Stream_CheckAndLogRequiredLength(TAG, stream, 1))
427
0
    goto fail;
428
429
5.98k
  Stream_Read_UINT8(stream, descriptor); /* descriptor (1 byte) */
430
431
5.98k
  if (descriptor == ZGFX_SEGMENTED_SINGLE)
432
684
  {
433
684
    if (!zgfx_decompress_segment(zgfx, stream, Stream_GetRemainingLength(stream)))
434
554
      goto fail;
435
436
130
    if (zgfx->OutputCount > 0)
437
127
    {
438
127
      if (!zgfx_append(zgfx, &pConcatenated, zgfx->OutputCount, &used))
439
0
        goto fail;
440
127
      if (used != zgfx->OutputCount)
441
0
        goto fail;
442
127
      *ppDstData = pConcatenated;
443
127
      *pDstSize = zgfx->OutputCount;
444
127
    }
445
130
  }
446
5.30k
  else if (descriptor == ZGFX_SEGMENTED_MULTIPART)
447
336
  {
448
336
    UINT32 segmentSize = 0;
449
336
    UINT16 segmentNumber = 0;
450
336
    UINT16 segmentCount = 0;
451
336
    UINT32 uncompressedSize = 0;
452
453
336
    if (!Stream_CheckAndLogRequiredLength(TAG, stream, 6))
454
25
      goto fail;
455
456
311
    Stream_Read_UINT16(stream, segmentCount);     /* segmentCount (2 bytes) */
457
311
    Stream_Read_UINT32(stream, uncompressedSize); /* uncompressedSize (4 bytes) */
458
459
975
    for (segmentNumber = 0; segmentNumber < segmentCount; segmentNumber++)
460
917
    {
461
917
      if (!Stream_CheckAndLogRequiredLength(TAG, stream, sizeof(UINT32)))
462
76
        goto fail;
463
464
841
      Stream_Read_UINT32(stream, segmentSize); /* segmentSize (4 bytes) */
465
466
841
      if (!zgfx_decompress_segment(zgfx, stream, segmentSize))
467
135
        goto fail;
468
469
706
      if (!zgfx_append(zgfx, &pConcatenated, uncompressedSize, &used))
470
42
        goto fail;
471
706
    }
472
473
58
    if (used != uncompressedSize)
474
52
      goto fail;
475
476
6
    *ppDstData = pConcatenated;
477
6
    *pDstSize = uncompressedSize;
478
6
  }
479
4.96k
  else
480
4.96k
  {
481
4.96k
    goto fail;
482
4.96k
  }
483
484
136
  status = 1;
485
5.98k
fail:
486
5.98k
  if (status < 0)
487
5.85k
    free(pConcatenated);
488
5.98k
  return status;
489
136
}
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
5.98k
{
613
5.98k
  zgfx->HistoryIndex = 0;
614
5.98k
}
615
616
ZGFX_CONTEXT* zgfx_context_new(BOOL Compressor)
617
5.98k
{
618
5.98k
  ZGFX_CONTEXT* zgfx = NULL;
619
5.98k
  zgfx = (ZGFX_CONTEXT*)calloc(1, sizeof(ZGFX_CONTEXT));
620
621
5.98k
  if (zgfx)
622
5.98k
  {
623
5.98k
    zgfx->Compressor = Compressor;
624
5.98k
    zgfx->HistoryBufferSize = sizeof(zgfx->HistoryBuffer);
625
5.98k
    zgfx_context_reset(zgfx, FALSE);
626
5.98k
  }
627
628
5.98k
  return zgfx;
629
5.98k
}
630
631
void zgfx_context_free(ZGFX_CONTEXT* zgfx)
632
5.98k
{
633
5.98k
  free(zgfx);
634
5.98k
}