Coverage Report

Created: 2025-07-01 06:46

/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
}