Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/libfreerdp/codec/xcrush.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * XCrush (RDP6.1) 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 <winpr/assert.h>
23
#include <winpr/cast.h>
24
25
#include <freerdp/config.h>
26
27
#include <winpr/crt.h>
28
#include <winpr/print.h>
29
#include <winpr/bitstream.h>
30
31
#include <freerdp/log.h>
32
#include "xcrush.h"
33
34
#pragma pack(push, 1)
35
36
typedef struct
37
{
38
  UINT32 MatchOffset;
39
  UINT32 ChunkOffset;
40
  UINT32 MatchLength;
41
} XCRUSH_MATCH_INFO;
42
43
typedef struct
44
{
45
  UINT32 offset;
46
  UINT32 next;
47
} XCRUSH_CHUNK;
48
49
typedef struct
50
{
51
  UINT16 seed;
52
  UINT16 size;
53
} XCRUSH_SIGNATURE;
54
55
typedef struct
56
{
57
  UINT16 MatchLength;
58
  UINT16 MatchOutputOffset;
59
  UINT32 MatchHistoryOffset;
60
} RDP61_MATCH_DETAILS;
61
62
typedef struct
63
{
64
  BYTE Level1ComprFlags;
65
  BYTE Level2ComprFlags;
66
  UINT16 MatchCount;
67
  RDP61_MATCH_DETAILS* MatchDetails;
68
  BYTE* Literals;
69
} RDP61_COMPRESSED_DATA;
70
71
#pragma pack(pop)
72
73
struct s_XCRUSH_CONTEXT
74
{
75
  ALIGN64 BOOL Compressor;
76
  ALIGN64 MPPC_CONTEXT* mppc;
77
  ALIGN64 BYTE* HistoryPtr;
78
  ALIGN64 UINT32 HistoryOffset;
79
  ALIGN64 UINT32 HistoryBufferSize;
80
  ALIGN64 BYTE HistoryBuffer[2000000];
81
  ALIGN64 BYTE BlockBuffer[16384];
82
  ALIGN64 UINT32 CompressionFlags;
83
  ALIGN64 UINT32 SignatureIndex;
84
  ALIGN64 UINT32 SignatureCount;
85
  ALIGN64 XCRUSH_SIGNATURE Signatures[1000];
86
  ALIGN64 UINT32 ChunkHead;
87
  ALIGN64 UINT32 ChunkTail;
88
  ALIGN64 XCRUSH_CHUNK Chunks[65534];
89
  ALIGN64 UINT16 NextChunks[65536];
90
  ALIGN64 UINT32 OriginalMatchCount;
91
  ALIGN64 UINT32 OptimizedMatchCount;
92
  ALIGN64 XCRUSH_MATCH_INFO OriginalMatches[1000];
93
  ALIGN64 XCRUSH_MATCH_INFO OptimizedMatches[1000];
94
};
95
96
//#define DEBUG_XCRUSH 1
97
#if defined(DEBUG_XCRUSH)
98
static const char* xcrush_get_level_2_compression_flags_string(UINT32 flags)
99
{
100
  flags &= 0xE0;
101
102
  if (flags == 0)
103
    return "PACKET_UNCOMPRESSED";
104
  else if (flags == PACKET_COMPRESSED)
105
    return "PACKET_COMPRESSED";
106
  else if (flags == PACKET_AT_FRONT)
107
    return "PACKET_AT_FRONT";
108
  else if (flags == PACKET_FLUSHED)
109
    return "PACKET_FLUSHED";
110
  else if (flags == (PACKET_COMPRESSED | PACKET_AT_FRONT))
111
    return "PACKET_COMPRESSED | PACKET_AT_FRONT";
112
  else if (flags == (PACKET_COMPRESSED | PACKET_FLUSHED))
113
    return "PACKET_COMPRESSED | PACKET_FLUSHED";
114
  else if (flags == (PACKET_AT_FRONT | PACKET_FLUSHED))
115
    return "PACKET_AT_FRONT | PACKET_FLUSHED";
116
  else if (flags == (PACKET_COMPRESSED | PACKET_AT_FRONT | PACKET_FLUSHED))
117
    return "PACKET_COMPRESSED | PACKET_AT_FRONT | PACKET_FLUSHED";
118
119
  return "PACKET_UNKNOWN";
120
}
121
122
static const char* xcrush_get_level_1_compression_flags_string(UINT32 flags)
123
{
124
  flags &= 0x17;
125
126
  if (flags == 0)
127
    return "L1_UNKNOWN";
128
  else if (flags == L1_PACKET_AT_FRONT)
129
    return "L1_PACKET_AT_FRONT";
130
  else if (flags == L1_NO_COMPRESSION)
131
    return "L1_NO_COMPRESSION";
132
  else if (flags == L1_COMPRESSED)
133
    return "L1_COMPRESSED";
134
  else if (flags == L1_INNER_COMPRESSION)
135
    return "L1_INNER_COMPRESSION";
136
  else if (flags == (L1_PACKET_AT_FRONT | L1_NO_COMPRESSION))
137
    return "L1_PACKET_AT_FRONT | L1_NO_COMPRESSION";
138
  else if (flags == (L1_PACKET_AT_FRONT | L1_COMPRESSED))
139
    return "L1_PACKET_AT_FRONT | L1_COMPRESSED";
140
  else if (flags == (L1_PACKET_AT_FRONT | L1_INNER_COMPRESSION))
141
    return "L1_PACKET_AT_FRONT | L1_INNER_COMPRESSION";
142
  else if (flags == (L1_NO_COMPRESSION | L1_COMPRESSED))
143
    return "L1_NO_COMPRESSION | L1_COMPRESSED";
144
  else if (flags == (L1_NO_COMPRESSION | L1_INNER_COMPRESSION))
145
    return "L1_NO_COMPRESSION | L1_INNER_COMPRESSION";
146
  else if (flags == (L1_COMPRESSED | L1_INNER_COMPRESSION))
147
    return "L1_COMPRESSED | L1_INNER_COMPRESSION";
148
  else if (flags == (L1_NO_COMPRESSION | L1_COMPRESSED | L1_INNER_COMPRESSION))
149
    return "L1_NO_COMPRESSION | L1_COMPRESSED | L1_INNER_COMPRESSION";
150
  else if (flags == (L1_PACKET_AT_FRONT | L1_COMPRESSED | L1_INNER_COMPRESSION))
151
    return "L1_PACKET_AT_FRONT | L1_COMPRESSED | L1_INNER_COMPRESSION";
152
  else if (flags == (L1_PACKET_AT_FRONT | L1_NO_COMPRESSION | L1_INNER_COMPRESSION))
153
    return "L1_PACKET_AT_FRONT | L1_NO_COMPRESSION | L1_INNER_COMPRESSION";
154
  else if (flags == (L1_PACKET_AT_FRONT | L1_NO_COMPRESSION | L1_COMPRESSED))
155
    return "L1_PACKET_AT_FRONT | L1_NO_COMPRESSION | L1_COMPRESSED";
156
  else if (flags ==
157
           (L1_PACKET_AT_FRONT | L1_NO_COMPRESSION | L1_COMPRESSED | L1_INNER_COMPRESSION))
158
    return "L1_PACKET_AT_FRONT | L1_NO_COMPRESSION | L1_COMPRESSED | L1_INNER_COMPRESSION";
159
160
  return "L1_UNKNOWN";
161
}
162
#endif
163
164
static UINT16 xcrush_update_hash(const BYTE* WINPR_RESTRICT data, UINT32 size)
165
0
{
166
0
  const BYTE* end = NULL;
167
0
  UINT16 seed = 5381; /* same value as in djb2 */
168
169
0
  WINPR_ASSERT(data);
170
0
  WINPR_ASSERT(size >= 4);
171
172
0
  if (size > 32)
173
0
  {
174
0
    size = 32;
175
0
    seed = 5413;
176
0
  }
177
178
0
  end = &data[size - 4];
179
180
0
  while (data < end)
181
0
  {
182
0
    seed += (data[3] ^ data[0]) + (data[1] << 8);
183
0
    data += 4;
184
0
  }
185
186
0
  return seed;
187
0
}
188
189
static int xcrush_append_chunk(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush,
190
                               const BYTE* WINPR_RESTRICT data, UINT32* WINPR_RESTRICT beg,
191
                               UINT32 end)
192
0
{
193
0
  UINT32 size = 0;
194
195
0
  WINPR_ASSERT(xcrush);
196
0
  WINPR_ASSERT(data);
197
0
  WINPR_ASSERT(beg);
198
199
0
  if (xcrush->SignatureIndex >= xcrush->SignatureCount)
200
0
    return 0;
201
202
0
  size = end - *beg;
203
204
0
  if (size > 65535)
205
0
    return 0;
206
207
0
  if (size >= 15)
208
0
  {
209
0
    const UINT16 seed = xcrush_update_hash(&data[*beg], WINPR_ASSERTING_INT_CAST(UINT16, size));
210
0
    xcrush->Signatures[xcrush->SignatureIndex].size = WINPR_ASSERTING_INT_CAST(UINT16, size);
211
0
    xcrush->Signatures[xcrush->SignatureIndex].seed = seed;
212
0
    xcrush->SignatureIndex++;
213
0
    *beg = end;
214
0
  }
215
216
0
  return 1;
217
0
}
218
219
static int xcrush_compute_chunks(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush,
220
                                 const BYTE* WINPR_RESTRICT data, UINT32 size,
221
                                 UINT32* WINPR_RESTRICT pIndex)
222
0
{
223
0
  UINT32 offset = 0;
224
0
  UINT32 rotation = 0;
225
0
  UINT32 accumulator = 0;
226
227
0
  WINPR_ASSERT(xcrush);
228
0
  WINPR_ASSERT(data);
229
0
  WINPR_ASSERT(pIndex);
230
231
0
  *pIndex = 0;
232
0
  xcrush->SignatureIndex = 0;
233
234
0
  if (size < 128)
235
0
    return 0;
236
237
0
  for (UINT32 i = 0; i < 32; i++)
238
0
  {
239
0
    rotation = _rotl(accumulator, 1);
240
0
    accumulator = data[i] ^ rotation;
241
0
  }
242
243
0
  for (UINT32 i = 0; i < size - 64; i++)
244
0
  {
245
0
    rotation = _rotl(accumulator, 1);
246
0
    accumulator = data[i + 32] ^ data[i] ^ rotation;
247
248
0
    if (!(accumulator & 0x7F))
249
0
    {
250
0
      if (!xcrush_append_chunk(xcrush, data, &offset, i + 32))
251
0
        return 0;
252
0
    }
253
254
0
    i++;
255
0
    rotation = _rotl(accumulator, 1);
256
0
    accumulator = data[i + 32] ^ data[i] ^ rotation;
257
258
0
    if (!(accumulator & 0x7F))
259
0
    {
260
0
      if (!xcrush_append_chunk(xcrush, data, &offset, i + 32))
261
0
        return 0;
262
0
    }
263
264
0
    i++;
265
0
    rotation = _rotl(accumulator, 1);
266
0
    accumulator = data[i + 32] ^ data[i] ^ rotation;
267
268
0
    if (!(accumulator & 0x7F))
269
0
    {
270
0
      if (!xcrush_append_chunk(xcrush, data, &offset, i + 32))
271
0
        return 0;
272
0
    }
273
274
0
    i++;
275
0
    rotation = _rotl(accumulator, 1);
276
0
    accumulator = data[i + 32] ^ data[i] ^ rotation;
277
278
0
    if (!(accumulator & 0x7F))
279
0
    {
280
0
      if (!xcrush_append_chunk(xcrush, data, &offset, i + 32))
281
0
        return 0;
282
0
    }
283
0
  }
284
285
0
  if ((size == offset) || xcrush_append_chunk(xcrush, data, &offset, size))
286
0
  {
287
0
    *pIndex = xcrush->SignatureIndex;
288
0
    return 1;
289
0
  }
290
291
0
  return 0;
292
0
}
293
294
static UINT32 xcrush_compute_signatures(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush,
295
                                        const BYTE* WINPR_RESTRICT data, UINT32 size)
296
0
{
297
0
  UINT32 index = 0;
298
299
0
  if (xcrush_compute_chunks(xcrush, data, size, &index))
300
0
    return index;
301
302
0
  return 0;
303
0
}
304
305
static void xcrush_clear_hash_table_range(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush, UINT32 beg,
306
                                          UINT32 end)
307
0
{
308
0
  WINPR_ASSERT(xcrush);
309
310
0
  for (UINT32 index = 0; index < 65536; index++)
311
0
  {
312
0
    if (xcrush->NextChunks[index] >= beg)
313
0
    {
314
0
      if (xcrush->NextChunks[index] <= end)
315
0
      {
316
0
        xcrush->NextChunks[index] = 0;
317
0
      }
318
0
    }
319
0
  }
320
321
0
  for (UINT32 index = 0; index < 65534; index++)
322
0
  {
323
0
    if (xcrush->Chunks[index].next >= beg)
324
0
    {
325
0
      if (xcrush->Chunks[index].next <= end)
326
0
      {
327
0
        xcrush->Chunks[index].next = 0;
328
0
      }
329
0
    }
330
0
  }
331
0
}
332
333
static int xcrush_find_next_matching_chunk(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush,
334
                                           XCRUSH_CHUNK* WINPR_RESTRICT chunk,
335
                                           XCRUSH_CHUNK** WINPR_RESTRICT pNextChunk)
336
0
{
337
0
  XCRUSH_CHUNK* next = NULL;
338
339
0
  WINPR_ASSERT(xcrush);
340
341
0
  if (!chunk)
342
0
    return -4001; /* error */
343
344
0
  if (chunk->next)
345
0
  {
346
    // NOLINTNEXTLINE(bugprone-sizeof-expression)
347
0
    const intptr_t diff = (chunk - xcrush->Chunks);
348
0
    if (diff < 0)
349
0
      return -4011;
350
351
0
    const size_t index = (size_t)diff / sizeof(XCRUSH_CHUNK);
352
0
    if (index >= 65534)
353
0
      return -4002; /* error */
354
355
0
    if ((index < xcrush->ChunkHead) || (chunk->next >= xcrush->ChunkHead))
356
0
    {
357
0
      if (chunk->next >= 65534)
358
0
        return -4003; /* error */
359
360
0
      next = &xcrush->Chunks[chunk->next];
361
0
    }
362
0
  }
363
364
0
  WINPR_ASSERT(pNextChunk);
365
0
  *pNextChunk = next;
366
0
  return 1;
367
0
}
368
369
static int xcrush_insert_chunk(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush,
370
                               XCRUSH_SIGNATURE* WINPR_RESTRICT signature, UINT32 offset,
371
                               XCRUSH_CHUNK** WINPR_RESTRICT pPrevChunk)
372
0
{
373
0
  WINPR_ASSERT(xcrush);
374
375
0
  if (xcrush->ChunkHead >= 65530)
376
0
  {
377
0
    xcrush->ChunkHead = 1;
378
0
    xcrush->ChunkTail = 1;
379
0
  }
380
381
0
  if (xcrush->ChunkHead >= xcrush->ChunkTail)
382
0
  {
383
0
    xcrush_clear_hash_table_range(xcrush, xcrush->ChunkTail, xcrush->ChunkTail + 10000);
384
0
    xcrush->ChunkTail += 10000;
385
0
  }
386
387
0
  const UINT32 index = xcrush->ChunkHead++;
388
389
0
  if (xcrush->ChunkHead >= 65534)
390
0
    return -3001; /* error */
391
392
0
  xcrush->Chunks[index].offset = offset;
393
0
  const UINT16 seed = signature->seed;
394
395
0
  if (xcrush->NextChunks[seed])
396
0
  {
397
0
    if (xcrush->NextChunks[seed] >= 65534)
398
0
      return -3003; /* error */
399
400
0
    WINPR_ASSERT(pPrevChunk);
401
0
    *pPrevChunk = &xcrush->Chunks[xcrush->NextChunks[seed]];
402
0
  }
403
404
0
  xcrush->Chunks[index].next = xcrush->NextChunks[seed] & 0xFFFF;
405
0
  xcrush->NextChunks[seed] = WINPR_ASSERTING_INT_CAST(UINT16, index);
406
0
  return 1;
407
0
}
408
409
static int xcrush_find_match_length(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush, UINT32 MatchOffset,
410
                                    UINT32 ChunkOffset, UINT32 HistoryOffset, UINT32 SrcSize,
411
                                    UINT32 MaxMatchLength,
412
                                    XCRUSH_MATCH_INFO* WINPR_RESTRICT MatchInfo)
413
0
{
414
0
  UINT32 MatchSymbol = 0;
415
0
  UINT32 ChunkSymbol = 0;
416
0
  BYTE* ChunkBuffer = NULL;
417
0
  BYTE* MatchBuffer = NULL;
418
0
  BYTE* MatchStartPtr = NULL;
419
0
  BYTE* ForwardChunkPtr = NULL;
420
0
  BYTE* ReverseChunkPtr = NULL;
421
0
  BYTE* ForwardMatchPtr = NULL;
422
0
  BYTE* ReverseMatchPtr = NULL;
423
0
  BYTE* HistoryBufferEnd = NULL;
424
0
  UINT32 ReverseMatchLength = 0;
425
0
  UINT32 ForwardMatchLength = 0;
426
0
  UINT32 TotalMatchLength = 0;
427
0
  BYTE* HistoryBuffer = NULL;
428
0
  UINT32 HistoryBufferSize = 0;
429
430
0
  WINPR_ASSERT(xcrush);
431
0
  WINPR_ASSERT(MatchInfo);
432
433
0
  HistoryBuffer = xcrush->HistoryBuffer;
434
0
  HistoryBufferSize = xcrush->HistoryBufferSize;
435
0
  HistoryBufferEnd = &HistoryBuffer[HistoryOffset + SrcSize];
436
437
0
  if (MatchOffset > HistoryBufferSize)
438
0
    return -2001; /* error */
439
440
0
  MatchBuffer = &HistoryBuffer[MatchOffset];
441
442
0
  if (ChunkOffset > HistoryBufferSize)
443
0
    return -2002; /* error */
444
445
0
  ChunkBuffer = &HistoryBuffer[ChunkOffset];
446
447
0
  if (MatchOffset == ChunkOffset)
448
0
    return -2003; /* error */
449
450
0
  if (MatchBuffer < HistoryBuffer)
451
0
    return -2004; /* error */
452
453
0
  if (ChunkBuffer < HistoryBuffer)
454
0
    return -2005; /* error */
455
456
0
  ForwardMatchPtr = &HistoryBuffer[MatchOffset];
457
0
  ForwardChunkPtr = &HistoryBuffer[ChunkOffset];
458
459
0
  if ((&MatchBuffer[MaxMatchLength + 1] < HistoryBufferEnd) &&
460
0
      (MatchBuffer[MaxMatchLength + 1] != ChunkBuffer[MaxMatchLength + 1]))
461
0
  {
462
0
    return 0;
463
0
  }
464
465
0
  while (1)
466
0
  {
467
0
    MatchSymbol = *ForwardMatchPtr++;
468
0
    ChunkSymbol = *ForwardChunkPtr++;
469
470
0
    if (MatchSymbol != ChunkSymbol)
471
0
      break;
472
473
0
    if (ForwardMatchPtr > HistoryBufferEnd)
474
0
      break;
475
476
0
    ForwardMatchLength++;
477
0
  }
478
479
0
  ReverseMatchPtr = MatchBuffer - 1;
480
0
  ReverseChunkPtr = ChunkBuffer - 1;
481
482
0
  while ((ReverseMatchPtr > &HistoryBuffer[HistoryOffset]) && (ReverseChunkPtr > HistoryBuffer) &&
483
0
         (*ReverseMatchPtr == *ReverseChunkPtr))
484
0
  {
485
0
    ReverseMatchLength++;
486
0
    ReverseMatchPtr--;
487
0
    ReverseChunkPtr--;
488
0
  }
489
490
0
  MatchStartPtr = MatchBuffer - ReverseMatchLength;
491
0
  TotalMatchLength = ReverseMatchLength + ForwardMatchLength;
492
493
0
  if (TotalMatchLength < 11)
494
0
    return 0;
495
496
0
  if (MatchStartPtr < HistoryBuffer)
497
0
    return -2006; /* error */
498
499
0
  const intptr_t diff = MatchStartPtr - HistoryBuffer;
500
0
  const intptr_t cdiff = ChunkBuffer - ReverseMatchLength - HistoryBuffer;
501
0
  if ((diff > UINT32_MAX) || (diff < 0) || (cdiff < 0) || (cdiff > UINT32_MAX))
502
0
    return -1;
503
0
  MatchInfo->MatchOffset = (UINT32)diff;
504
0
  MatchInfo->ChunkOffset = (UINT32)cdiff;
505
0
  MatchInfo->MatchLength = TotalMatchLength;
506
0
  return (int)TotalMatchLength;
507
0
}
508
509
static int xcrush_find_all_matches(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush, UINT32 SignatureIndex,
510
                                   UINT32 HistoryOffset, UINT32 SrcOffset, UINT32 SrcSize)
511
0
{
512
0
  UINT32 j = 0;
513
0
  int status = 0;
514
0
  UINT32 ChunkIndex = 0;
515
0
  UINT32 ChunkCount = 0;
516
0
  XCRUSH_CHUNK* chunk = NULL;
517
0
  UINT32 MatchLength = 0;
518
0
  UINT32 MaxMatchLength = 0;
519
0
  UINT32 PrevMatchEnd = 0;
520
0
  XCRUSH_SIGNATURE* Signatures = NULL;
521
0
  XCRUSH_MATCH_INFO MaxMatchInfo = { 0 };
522
523
0
  WINPR_ASSERT(xcrush);
524
525
0
  Signatures = xcrush->Signatures;
526
527
0
  for (UINT32 i = 0; i < SignatureIndex; i++)
528
0
  {
529
0
    XCRUSH_MATCH_INFO MatchInfo = { 0 };
530
0
    UINT32 offset = SrcOffset + HistoryOffset;
531
532
0
    if (!Signatures[i].size)
533
0
      return -1001; /* error */
534
535
0
    status = xcrush_insert_chunk(xcrush, &Signatures[i], offset, &chunk);
536
537
0
    if (status < 0)
538
0
      return status;
539
540
0
    if (chunk && (SrcOffset + HistoryOffset + Signatures[i].size >= PrevMatchEnd))
541
0
    {
542
0
      ChunkCount = 0;
543
0
      MaxMatchLength = 0;
544
545
0
      while (chunk)
546
0
      {
547
0
        if ((chunk->offset < HistoryOffset) || (chunk->offset < offset) ||
548
0
            (chunk->offset > SrcSize + HistoryOffset))
549
0
        {
550
0
          status = xcrush_find_match_length(xcrush, offset, chunk->offset, HistoryOffset,
551
0
                                            SrcSize, MaxMatchLength, &MatchInfo);
552
553
0
          if (status < 0)
554
0
            return status; /* error */
555
556
0
          MatchLength = (UINT32)status;
557
558
0
          if (MatchLength > MaxMatchLength)
559
0
          {
560
0
            MaxMatchLength = MatchLength;
561
0
            MaxMatchInfo.MatchOffset = MatchInfo.MatchOffset;
562
0
            MaxMatchInfo.ChunkOffset = MatchInfo.ChunkOffset;
563
0
            MaxMatchInfo.MatchLength = MatchInfo.MatchLength;
564
565
0
            if (MatchLength > 256)
566
0
              break;
567
0
          }
568
0
        }
569
570
0
        ChunkIndex = ChunkCount++;
571
572
0
        if (ChunkIndex > 4)
573
0
          break;
574
575
0
        status = xcrush_find_next_matching_chunk(xcrush, chunk, &chunk);
576
577
0
        if (status < 0)
578
0
          return status; /* error */
579
0
      }
580
581
0
      if (MaxMatchLength)
582
0
      {
583
0
        xcrush->OriginalMatches[j].MatchOffset = MaxMatchInfo.MatchOffset;
584
0
        xcrush->OriginalMatches[j].ChunkOffset = MaxMatchInfo.ChunkOffset;
585
0
        xcrush->OriginalMatches[j].MatchLength = MaxMatchInfo.MatchLength;
586
587
0
        if (xcrush->OriginalMatches[j].MatchOffset < HistoryOffset)
588
0
          return -1002; /* error */
589
590
0
        PrevMatchEnd =
591
0
            xcrush->OriginalMatches[j].MatchLength + xcrush->OriginalMatches[j].MatchOffset;
592
0
        j++;
593
594
0
        if (j >= 1000)
595
0
          return -1003; /* error */
596
0
      }
597
0
    }
598
599
0
    SrcOffset += Signatures[i].size;
600
601
0
    if (SrcOffset > SrcSize)
602
0
      return -1004; /* error */
603
0
  }
604
605
0
  if (SrcOffset > SrcSize)
606
0
    return -1005; /* error */
607
608
0
  return (int)j;
609
0
}
610
611
static int xcrush_optimize_matches(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush)
612
0
{
613
0
  UINT32 j = 0;
614
0
  UINT32 MatchDiff = 0;
615
0
  UINT32 PrevMatchEnd = 0;
616
0
  UINT32 TotalMatchLength = 0;
617
0
  UINT32 OriginalMatchCount = 0;
618
0
  UINT32 OptimizedMatchCount = 0;
619
0
  XCRUSH_MATCH_INFO* OriginalMatch = NULL;
620
0
  XCRUSH_MATCH_INFO* OptimizedMatch = NULL;
621
0
  XCRUSH_MATCH_INFO* OriginalMatches = NULL;
622
0
  XCRUSH_MATCH_INFO* OptimizedMatches = NULL;
623
624
0
  WINPR_ASSERT(xcrush);
625
626
0
  OriginalMatches = xcrush->OriginalMatches;
627
0
  OriginalMatchCount = xcrush->OriginalMatchCount;
628
0
  OptimizedMatches = xcrush->OptimizedMatches;
629
630
0
  for (UINT32 i = 0; i < OriginalMatchCount; i++)
631
0
  {
632
0
    if (OriginalMatches[i].MatchOffset <= PrevMatchEnd)
633
0
    {
634
0
      if ((OriginalMatches[i].MatchOffset < PrevMatchEnd) &&
635
0
          (OriginalMatches[i].MatchLength + OriginalMatches[i].MatchOffset >
636
0
           PrevMatchEnd + 6))
637
0
      {
638
0
        MatchDiff = PrevMatchEnd - OriginalMatches[i].MatchOffset;
639
0
        OriginalMatch = &OriginalMatches[i];
640
0
        OptimizedMatch = &OptimizedMatches[j];
641
0
        OptimizedMatch->MatchOffset = OriginalMatch->MatchOffset;
642
0
        OptimizedMatch->ChunkOffset = OriginalMatch->ChunkOffset;
643
0
        OptimizedMatch->MatchLength = OriginalMatch->MatchLength;
644
645
0
        if (OptimizedMatches[j].MatchLength <= MatchDiff)
646
0
          return -5001; /* error */
647
648
0
        if (MatchDiff >= 20000)
649
0
          return -5002; /* error */
650
651
0
        OptimizedMatches[j].MatchLength -= MatchDiff;
652
0
        OptimizedMatches[j].MatchOffset += MatchDiff;
653
0
        OptimizedMatches[j].ChunkOffset += MatchDiff;
654
0
        PrevMatchEnd = OptimizedMatches[j].MatchLength + OptimizedMatches[j].MatchOffset;
655
0
        TotalMatchLength += OptimizedMatches[j].MatchLength;
656
0
        j++;
657
0
      }
658
0
    }
659
0
    else
660
0
    {
661
0
      OriginalMatch = &OriginalMatches[i];
662
0
      OptimizedMatch = &OptimizedMatches[j];
663
0
      OptimizedMatch->MatchOffset = OriginalMatch->MatchOffset;
664
0
      OptimizedMatch->ChunkOffset = OriginalMatch->ChunkOffset;
665
0
      OptimizedMatch->MatchLength = OriginalMatch->MatchLength;
666
0
      PrevMatchEnd = OptimizedMatches[j].MatchLength + OptimizedMatches[j].MatchOffset;
667
0
      TotalMatchLength += OptimizedMatches[j].MatchLength;
668
0
      j++;
669
0
    }
670
0
  }
671
672
0
  OptimizedMatchCount = j;
673
0
  xcrush->OptimizedMatchCount = OptimizedMatchCount;
674
0
  return (int)TotalMatchLength;
675
0
}
676
677
static int xcrush_generate_output(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush,
678
                                  BYTE* WINPR_RESTRICT OutputBuffer, UINT32 OutputSize,
679
                                  UINT32 HistoryOffset, UINT32* WINPR_RESTRICT pDstSize)
680
0
{
681
0
  BYTE* Literals = NULL;
682
0
  BYTE* OutputEnd = NULL;
683
0
  UINT32 MatchIndex = 0;
684
0
  UINT32 MatchOffset = 0;
685
0
  UINT16 MatchLength = 0;
686
0
  UINT32 CurrentOffset = 0;
687
0
  UINT32 MatchOffsetDiff = 0;
688
0
  UINT32 HistoryOffsetDiff = 0;
689
0
  RDP61_MATCH_DETAILS* MatchDetails = NULL;
690
691
0
  WINPR_ASSERT(xcrush);
692
0
  WINPR_ASSERT(OutputBuffer);
693
0
  WINPR_ASSERT(OutputSize >= 2);
694
0
  WINPR_ASSERT(pDstSize);
695
696
0
  const UINT32 MatchCount = xcrush->OptimizedMatchCount;
697
0
  OutputEnd = &OutputBuffer[OutputSize];
698
699
0
  if (&OutputBuffer[2] >= &OutputBuffer[OutputSize])
700
0
    return -6001; /* error */
701
702
0
  winpr_Data_Write_UINT16(OutputBuffer, WINPR_ASSERTING_INT_CAST(UINT16, MatchCount));
703
0
  MatchDetails = (RDP61_MATCH_DETAILS*)&OutputBuffer[2];
704
0
  Literals = (BYTE*)&MatchDetails[MatchCount];
705
706
0
  if (Literals > OutputEnd)
707
0
    return -6002; /* error */
708
709
0
  for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++)
710
0
  {
711
0
    const UINT32 len = xcrush->OptimizedMatches[MatchIndex].MatchLength;
712
0
    winpr_Data_Write_UINT16(&MatchDetails[MatchIndex].MatchLength,
713
0
                            WINPR_ASSERTING_INT_CAST(UINT16, len));
714
715
0
    const UINT32 moff = xcrush->OptimizedMatches[MatchIndex].MatchOffset;
716
0
    WINPR_ASSERT(moff >= HistoryOffset);
717
718
0
    const UINT32 off = moff - HistoryOffset;
719
0
    winpr_Data_Write_UINT16(&MatchDetails[MatchIndex].MatchOutputOffset,
720
0
                            WINPR_ASSERTING_INT_CAST(UINT16, off));
721
0
    winpr_Data_Write_UINT32(&MatchDetails[MatchIndex].MatchHistoryOffset,
722
0
                            xcrush->OptimizedMatches[MatchIndex].ChunkOffset);
723
0
  }
724
725
0
  CurrentOffset = HistoryOffset;
726
727
0
  for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++)
728
0
  {
729
0
    MatchLength = (UINT16)(xcrush->OptimizedMatches[MatchIndex].MatchLength);
730
0
    MatchOffset = xcrush->OptimizedMatches[MatchIndex].MatchOffset;
731
732
0
    if (MatchOffset <= CurrentOffset)
733
0
    {
734
0
      if (MatchOffset != CurrentOffset)
735
0
        return -6003; /* error */
736
737
0
      CurrentOffset = MatchOffset + MatchLength;
738
0
    }
739
0
    else
740
0
    {
741
0
      MatchOffsetDiff = MatchOffset - CurrentOffset;
742
743
0
      if (Literals + MatchOffset - CurrentOffset >= OutputEnd)
744
0
        return -6004; /* error */
745
746
0
      CopyMemory(Literals, &xcrush->HistoryBuffer[CurrentOffset], MatchOffsetDiff);
747
748
0
      if (Literals >= OutputEnd)
749
0
        return -6005; /* error */
750
751
0
      Literals += MatchOffsetDiff;
752
0
      CurrentOffset = MatchOffset + MatchLength;
753
0
    }
754
0
  }
755
756
0
  HistoryOffsetDiff = xcrush->HistoryOffset - CurrentOffset;
757
758
0
  if (Literals + HistoryOffsetDiff >= OutputEnd)
759
0
    return -6006; /* error */
760
761
0
  CopyMemory(Literals, &xcrush->HistoryBuffer[CurrentOffset], HistoryOffsetDiff);
762
0
  const intptr_t diff = Literals + HistoryOffsetDiff - OutputBuffer;
763
0
  if ((diff < 0) || (diff > UINT32_MAX))
764
0
    return -1;
765
0
  *pDstSize = (UINT32)diff;
766
0
  return 1;
767
0
}
768
769
static INLINE size_t xcrush_copy_bytes_no_overlap(BYTE* WINPR_RESTRICT dst,
770
                                                  const BYTE* WINPR_RESTRICT src, size_t num)
771
0
{
772
  // src and dst overlaps
773
  // we should copy the area that doesn't overlap repeatedly
774
0
  const size_t diff = WINPR_ASSERTING_INT_CAST(size_t, (dst > src) ? dst - src : src - dst);
775
0
  const size_t rest = num % diff;
776
0
  const size_t end = num - rest;
777
778
0
  for (size_t a = 0; a < end; a += diff)
779
0
    memcpy(&dst[a], &src[a], diff);
780
781
0
  if (rest != 0)
782
0
    memcpy(&dst[end], &src[end], rest);
783
784
0
  return num;
785
0
}
786
787
static INLINE size_t xcrush_copy_bytes(BYTE* dst, const BYTE* src, size_t num)
788
0
{
789
0
  WINPR_ASSERT(dst);
790
0
  WINPR_ASSERT(src);
791
792
0
  if (src + num < dst || src > dst + num)
793
0
    memcpy(dst, src, num);
794
0
  else if (src != dst)
795
0
    return xcrush_copy_bytes_no_overlap(dst, src, num);
796
797
0
  return num;
798
0
}
799
800
static int xcrush_decompress_l1(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush,
801
                                const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize,
802
                                const BYTE** WINPR_RESTRICT ppDstData,
803
                                UINT32* WINPR_RESTRICT pDstSize, UINT32 flags)
804
0
{
805
0
  const BYTE* pSrcEnd = NULL;
806
0
  const BYTE* Literals = NULL;
807
0
  UINT16 MatchCount = 0;
808
0
  UINT16 MatchIndex = 0;
809
0
  BYTE* OutputPtr = NULL;
810
0
  size_t OutputLength = 0;
811
0
  size_t OutputOffset = 0;
812
0
  BYTE* HistoryPtr = NULL;
813
0
  BYTE* HistoryBuffer = NULL;
814
0
  BYTE* HistoryBufferEnd = NULL;
815
0
  UINT32 HistoryBufferSize = 0;
816
0
  UINT16 MatchLength = 0;
817
0
  UINT16 MatchOutputOffset = 0;
818
0
  UINT32 MatchHistoryOffset = 0;
819
0
  const RDP61_MATCH_DETAILS* MatchDetails = NULL;
820
821
0
  WINPR_ASSERT(xcrush);
822
823
0
  if (SrcSize < 1)
824
0
    return -1001;
825
826
0
  WINPR_ASSERT(pSrcData);
827
0
  WINPR_ASSERT(ppDstData);
828
0
  WINPR_ASSERT(pDstSize);
829
830
0
  if (flags & L1_PACKET_AT_FRONT)
831
0
    xcrush->HistoryOffset = 0;
832
833
0
  pSrcEnd = &pSrcData[SrcSize];
834
0
  HistoryBuffer = xcrush->HistoryBuffer;
835
0
  HistoryBufferSize = xcrush->HistoryBufferSize;
836
0
  HistoryBufferEnd = &(HistoryBuffer[HistoryBufferSize]);
837
0
  xcrush->HistoryPtr = HistoryPtr = &(HistoryBuffer[xcrush->HistoryOffset]);
838
839
0
  if (flags & L1_NO_COMPRESSION)
840
0
  {
841
0
    Literals = pSrcData;
842
0
  }
843
0
  else
844
0
  {
845
0
    if (!(flags & L1_COMPRESSED))
846
0
      return -1002;
847
848
0
    if ((pSrcData + 2) > pSrcEnd)
849
0
      return -1003;
850
851
0
    MatchCount = winpr_Data_Get_UINT16(pSrcData);
852
0
    MatchDetails = (const RDP61_MATCH_DETAILS*)&pSrcData[2];
853
0
    Literals = (const BYTE*)&MatchDetails[MatchCount];
854
0
    OutputOffset = 0;
855
856
0
    if (Literals > pSrcEnd)
857
0
      return -1004;
858
859
0
    for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++)
860
0
    {
861
0
      MatchLength = winpr_Data_Get_UINT16(&MatchDetails[MatchIndex].MatchLength);
862
0
      MatchOutputOffset = winpr_Data_Get_UINT16(&MatchDetails[MatchIndex].MatchOutputOffset);
863
0
      MatchHistoryOffset =
864
0
          winpr_Data_Get_UINT32(&MatchDetails[MatchIndex].MatchHistoryOffset);
865
866
0
      if (MatchOutputOffset < OutputOffset)
867
0
        return -1005;
868
869
0
      if (MatchLength > HistoryBufferSize)
870
0
        return -1006;
871
872
0
      if (MatchHistoryOffset > HistoryBufferSize)
873
0
        return -1007;
874
875
0
      OutputLength = MatchOutputOffset - OutputOffset;
876
877
0
      if ((MatchOutputOffset - OutputOffset) > HistoryBufferSize)
878
0
        return -1008;
879
880
0
      if (OutputLength > 0)
881
0
      {
882
0
        if ((&HistoryPtr[OutputLength] >= HistoryBufferEnd) || (Literals >= pSrcEnd) ||
883
0
            (&Literals[OutputLength] > pSrcEnd))
884
0
          return -1009;
885
886
0
        xcrush_copy_bytes(HistoryPtr, Literals, OutputLength);
887
0
        HistoryPtr += OutputLength;
888
0
        Literals += OutputLength;
889
0
        OutputOffset += OutputLength;
890
891
0
        if (Literals > pSrcEnd)
892
0
          return -1010;
893
0
      }
894
895
0
      OutputPtr = &xcrush->HistoryBuffer[MatchHistoryOffset];
896
897
0
      if ((&HistoryPtr[MatchLength] >= HistoryBufferEnd) ||
898
0
          (&OutputPtr[MatchLength] >= HistoryBufferEnd))
899
0
        return -1011;
900
901
0
      xcrush_copy_bytes(HistoryPtr, OutputPtr, MatchLength);
902
0
      OutputOffset += MatchLength;
903
0
      HistoryPtr += MatchLength;
904
0
    }
905
0
  }
906
907
0
  if (Literals < pSrcEnd)
908
0
  {
909
0
    OutputLength = WINPR_ASSERTING_INT_CAST(size_t, pSrcEnd - Literals);
910
911
0
    if ((&HistoryPtr[OutputLength] >= HistoryBufferEnd) || (&Literals[OutputLength] > pSrcEnd))
912
0
      return -1012;
913
914
0
    xcrush_copy_bytes(HistoryPtr, Literals, OutputLength);
915
0
    HistoryPtr += OutputLength;
916
0
  }
917
918
0
  const intptr_t diff = HistoryPtr - HistoryBuffer;
919
0
  if ((diff < 0) || (diff > UINT32_MAX))
920
0
    return -1;
921
0
  xcrush->HistoryOffset = (UINT32)diff;
922
923
0
  const intptr_t sizediff = HistoryPtr - xcrush->HistoryPtr;
924
0
  if ((sizediff < 0) || (sizediff > UINT32_MAX))
925
0
    return -1;
926
0
  *pDstSize = (UINT32)sizediff;
927
0
  *ppDstData = xcrush->HistoryPtr;
928
0
  return 1;
929
0
}
930
931
int xcrush_decompress(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush, const BYTE* WINPR_RESTRICT pSrcData,
932
                      UINT32 SrcSize, const BYTE** WINPR_RESTRICT ppDstData,
933
                      UINT32* WINPR_RESTRICT pDstSize, UINT32 flags)
934
0
{
935
0
  int status = 0;
936
0
  UINT32 DstSize = 0;
937
0
  const BYTE* pDstData = NULL;
938
0
  BYTE Level1ComprFlags = 0;
939
0
  BYTE Level2ComprFlags = 0;
940
941
0
  WINPR_ASSERT(xcrush);
942
943
0
  if (SrcSize < 2)
944
0
    return -1;
945
946
0
  WINPR_ASSERT(pSrcData);
947
0
  WINPR_ASSERT(ppDstData);
948
0
  WINPR_ASSERT(pDstSize);
949
950
0
  Level1ComprFlags = pSrcData[0];
951
0
  Level2ComprFlags = pSrcData[1];
952
0
  pSrcData += 2;
953
0
  SrcSize -= 2;
954
955
0
  if (flags & PACKET_FLUSHED)
956
0
  {
957
0
    ZeroMemory(xcrush->HistoryBuffer, xcrush->HistoryBufferSize);
958
0
    xcrush->HistoryOffset = 0;
959
0
  }
960
961
0
  if (!(Level2ComprFlags & PACKET_COMPRESSED))
962
0
  {
963
0
    status =
964
0
        xcrush_decompress_l1(xcrush, pSrcData, SrcSize, ppDstData, pDstSize, Level1ComprFlags);
965
0
    return status;
966
0
  }
967
968
0
  status =
969
0
      mppc_decompress(xcrush->mppc, pSrcData, SrcSize, &pDstData, &DstSize, Level2ComprFlags);
970
971
0
  if (status < 0)
972
0
    return status;
973
974
0
  status = xcrush_decompress_l1(xcrush, pDstData, DstSize, ppDstData, pDstSize, Level1ComprFlags);
975
0
  return status;
976
0
}
977
978
static int xcrush_compress_l1(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush,
979
                              const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize,
980
                              BYTE* WINPR_RESTRICT pDstData, UINT32* WINPR_RESTRICT pDstSize,
981
                              UINT32* WINPR_RESTRICT pFlags)
982
0
{
983
0
  int status = 0;
984
0
  UINT32 Flags = 0;
985
0
  UINT32 HistoryOffset = 0;
986
0
  BYTE* HistoryPtr = NULL;
987
0
  BYTE* HistoryBuffer = NULL;
988
0
  UINT32 SignatureIndex = 0;
989
990
0
  WINPR_ASSERT(xcrush);
991
0
  WINPR_ASSERT(pSrcData);
992
0
  WINPR_ASSERT(SrcSize > 0);
993
0
  WINPR_ASSERT(pDstData);
994
0
  WINPR_ASSERT(pDstSize);
995
0
  WINPR_ASSERT(pFlags);
996
997
0
  if (xcrush->HistoryOffset + SrcSize + 8 > xcrush->HistoryBufferSize)
998
0
  {
999
0
    xcrush->HistoryOffset = 0;
1000
0
    Flags |= L1_PACKET_AT_FRONT;
1001
0
  }
1002
1003
0
  HistoryOffset = xcrush->HistoryOffset;
1004
0
  HistoryBuffer = xcrush->HistoryBuffer;
1005
0
  HistoryPtr = &HistoryBuffer[HistoryOffset];
1006
0
  MoveMemory(HistoryPtr, pSrcData, SrcSize);
1007
0
  xcrush->HistoryOffset += SrcSize;
1008
1009
0
  if (SrcSize > 50)
1010
0
  {
1011
0
    SignatureIndex = xcrush_compute_signatures(xcrush, pSrcData, SrcSize);
1012
1013
0
    if (SignatureIndex)
1014
0
    {
1015
0
      status = xcrush_find_all_matches(xcrush, SignatureIndex, HistoryOffset, 0, SrcSize);
1016
1017
0
      if (status < 0)
1018
0
        return status;
1019
1020
0
      xcrush->OriginalMatchCount = (UINT32)status;
1021
0
      xcrush->OptimizedMatchCount = 0;
1022
1023
0
      if (xcrush->OriginalMatchCount)
1024
0
      {
1025
0
        status = xcrush_optimize_matches(xcrush);
1026
1027
0
        if (status < 0)
1028
0
          return status;
1029
0
      }
1030
1031
0
      if (xcrush->OptimizedMatchCount)
1032
0
      {
1033
0
        status = xcrush_generate_output(xcrush, pDstData, SrcSize, HistoryOffset, pDstSize);
1034
1035
0
        if (status < 0)
1036
0
          return status;
1037
1038
0
        Flags |= L1_COMPRESSED;
1039
0
      }
1040
0
    }
1041
0
  }
1042
1043
0
  if (!(Flags & L1_COMPRESSED))
1044
0
  {
1045
0
    Flags |= L1_NO_COMPRESSION;
1046
0
    *pDstSize = SrcSize;
1047
0
  }
1048
1049
0
  *pFlags = Flags;
1050
0
  return 1;
1051
0
}
1052
1053
int xcrush_compress(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush, const BYTE* WINPR_RESTRICT pSrcData,
1054
                    UINT32 SrcSize, BYTE* WINPR_RESTRICT pDstBuffer,
1055
                    const BYTE** WINPR_RESTRICT ppDstData, UINT32* WINPR_RESTRICT pDstSize,
1056
                    UINT32* WINPR_RESTRICT pFlags)
1057
0
{
1058
0
  int status = 0;
1059
0
  UINT32 DstSize = 0;
1060
0
  BYTE* pDstData = NULL;
1061
0
  const BYTE* CompressedData = NULL;
1062
0
  UINT32 CompressedDataSize = 0;
1063
0
  BYTE* OriginalData = NULL;
1064
0
  UINT32 OriginalDataSize = 0;
1065
0
  UINT32 Level1ComprFlags = 0;
1066
0
  UINT32 Level2ComprFlags = 0;
1067
0
  UINT32 CompressionLevel = 3;
1068
1069
0
  WINPR_ASSERT(xcrush);
1070
0
  WINPR_ASSERT(pSrcData);
1071
0
  WINPR_ASSERT(SrcSize > 0);
1072
0
  WINPR_ASSERT(ppDstData);
1073
0
  WINPR_ASSERT(pDstSize);
1074
0
  WINPR_ASSERT(pFlags);
1075
1076
0
  if (SrcSize > 16384)
1077
0
    return -1001;
1078
1079
0
  if ((SrcSize + 2) > *pDstSize)
1080
0
    return -1002;
1081
1082
0
  OriginalData = pDstBuffer;
1083
0
  *ppDstData = pDstBuffer;
1084
0
  OriginalDataSize = SrcSize;
1085
0
  pDstData = xcrush->BlockBuffer;
1086
0
  CompressedDataSize = SrcSize;
1087
0
  status = xcrush_compress_l1(xcrush, pSrcData, SrcSize, pDstData, &CompressedDataSize,
1088
0
                              &Level1ComprFlags);
1089
1090
0
  if (status < 0)
1091
0
    return status;
1092
1093
0
  if (Level1ComprFlags & L1_COMPRESSED)
1094
0
  {
1095
0
    CompressedData = pDstData;
1096
1097
0
    if (CompressedDataSize > SrcSize)
1098
0
      return -1003;
1099
0
  }
1100
0
  else
1101
0
  {
1102
0
    CompressedData = pSrcData;
1103
1104
0
    if (CompressedDataSize != SrcSize)
1105
0
      return -1004;
1106
0
  }
1107
1108
0
  status = 0;
1109
0
  pDstData = &OriginalData[2];
1110
0
  DstSize = OriginalDataSize - 2;
1111
1112
0
  if (CompressedDataSize > 50)
1113
0
  {
1114
0
    const BYTE* pUnusedDstData = NULL;
1115
0
    status = mppc_compress(xcrush->mppc, CompressedData, CompressedDataSize, pDstData,
1116
0
                           &pUnusedDstData, &DstSize, &Level2ComprFlags);
1117
0
  }
1118
1119
0
  if (status < 0)
1120
0
    return status;
1121
1122
0
  if (!status || (Level2ComprFlags & PACKET_FLUSHED))
1123
0
  {
1124
0
    if (CompressedDataSize > DstSize)
1125
0
    {
1126
0
      xcrush_context_reset(xcrush, TRUE);
1127
0
      *ppDstData = pSrcData;
1128
0
      *pDstSize = SrcSize;
1129
0
      *pFlags = 0;
1130
0
      return 1;
1131
0
    }
1132
1133
0
    DstSize = CompressedDataSize;
1134
0
    CopyMemory(&OriginalData[2], CompressedData, CompressedDataSize);
1135
0
  }
1136
1137
0
  if (Level2ComprFlags & PACKET_COMPRESSED)
1138
0
  {
1139
0
    Level2ComprFlags |= xcrush->CompressionFlags;
1140
0
    xcrush->CompressionFlags = 0;
1141
0
  }
1142
0
  else if (Level2ComprFlags & PACKET_FLUSHED)
1143
0
  {
1144
0
    xcrush->CompressionFlags = PACKET_FLUSHED;
1145
0
  }
1146
1147
0
  Level1ComprFlags |= L1_INNER_COMPRESSION;
1148
0
  OriginalData[0] = (BYTE)Level1ComprFlags;
1149
0
  OriginalData[1] = (BYTE)Level2ComprFlags;
1150
#if defined(DEBUG_XCRUSH)
1151
  WLog_DBG(TAG, "XCrushCompress: Level1ComprFlags: %s Level2ComprFlags: %s",
1152
           xcrush_get_level_1_compression_flags_string(Level1ComprFlags),
1153
           xcrush_get_level_2_compression_flags_string(Level2ComprFlags));
1154
#endif
1155
1156
0
  if (*pDstSize < (DstSize + 2))
1157
0
    return -1006;
1158
1159
0
  *pDstSize = DstSize + 2;
1160
0
  *pFlags = PACKET_COMPRESSED | CompressionLevel;
1161
0
  return 1;
1162
0
}
1163
1164
void xcrush_context_reset(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush, BOOL flush)
1165
0
{
1166
0
  WINPR_ASSERT(xcrush);
1167
1168
0
  xcrush->SignatureIndex = 0;
1169
0
  xcrush->SignatureCount = 1000;
1170
0
  ZeroMemory(&(xcrush->Signatures), sizeof(XCRUSH_SIGNATURE) * xcrush->SignatureCount);
1171
0
  xcrush->CompressionFlags = 0;
1172
0
  xcrush->ChunkHead = xcrush->ChunkTail = 1;
1173
0
  ZeroMemory(&(xcrush->Chunks), sizeof(xcrush->Chunks));
1174
0
  ZeroMemory(&(xcrush->NextChunks), sizeof(xcrush->NextChunks));
1175
0
  ZeroMemory(&(xcrush->OriginalMatches), sizeof(xcrush->OriginalMatches));
1176
0
  ZeroMemory(&(xcrush->OptimizedMatches), sizeof(xcrush->OptimizedMatches));
1177
1178
0
  if (flush)
1179
0
    xcrush->HistoryOffset = xcrush->HistoryBufferSize + 1;
1180
0
  else
1181
0
    xcrush->HistoryOffset = 0;
1182
1183
0
  mppc_context_reset(xcrush->mppc, flush);
1184
0
}
1185
1186
XCRUSH_CONTEXT* xcrush_context_new(BOOL Compressor)
1187
0
{
1188
0
  XCRUSH_CONTEXT* xcrush = (XCRUSH_CONTEXT*)calloc(1, sizeof(XCRUSH_CONTEXT));
1189
1190
0
  if (!xcrush)
1191
0
    goto fail;
1192
1193
0
  xcrush->Compressor = Compressor;
1194
0
  xcrush->mppc = mppc_context_new(1, Compressor);
1195
0
  if (!xcrush->mppc)
1196
0
    goto fail;
1197
0
  xcrush->HistoryBufferSize = 2000000;
1198
0
  xcrush_context_reset(xcrush, FALSE);
1199
1200
0
  return xcrush;
1201
0
fail:
1202
0
  xcrush_context_free(xcrush);
1203
1204
0
  return NULL;
1205
0
}
1206
1207
void xcrush_context_free(XCRUSH_CONTEXT* xcrush)
1208
0
{
1209
0
  if (xcrush)
1210
0
  {
1211
0
    mppc_context_free(xcrush->mppc);
1212
0
    free(xcrush);
1213
0
  }
1214
0
}