Coverage Report

Created: 2024-09-08 06:18

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