Coverage Report

Created: 2025-07-11 06:43

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