Coverage Report

Created: 2024-09-08 06:16

/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 <freerdp/config.h>
24
25
#include <winpr/crt.h>
26
#include <winpr/print.h>
27
#include <winpr/bitstream.h>
28
29
#include <freerdp/log.h>
30
#include "xcrush.h"
31
32
#define TAG FREERDP_TAG("codec")
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 UINT32 xcrush_update_hash(const BYTE* WINPR_RESTRICT data, UINT32 size)
165
0
{
166
0
  const BYTE* end = NULL;
167
0
  UINT32 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 (UINT16)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
    UINT32 seed = xcrush_update_hash(&data[*beg], (UINT16)size);
210
0
    xcrush->Signatures[xcrush->SignatureIndex].size = 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
0
    UINT32 index = (chunk - xcrush->Chunks) / sizeof(XCRUSH_CHUNK);
347
348
0
    if (index >= 65534)
349
0
      return -4002; /* error */
350
351
0
    if ((index < xcrush->ChunkHead) || (chunk->next >= xcrush->ChunkHead))
352
0
    {
353
0
      if (chunk->next >= 65534)
354
0
        return -4003; /* error */
355
356
0
      next = &xcrush->Chunks[chunk->next];
357
0
    }
358
0
  }
359
360
0
  WINPR_ASSERT(pNextChunk);
361
0
  *pNextChunk = next;
362
0
  return 1;
363
0
}
364
365
static int xcrush_insert_chunk(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush,
366
                               XCRUSH_SIGNATURE* WINPR_RESTRICT signature, UINT32 offset,
367
                               XCRUSH_CHUNK** WINPR_RESTRICT pPrevChunk)
368
0
{
369
0
  UINT32 seed = 0;
370
0
  UINT32 index = 0;
371
372
0
  WINPR_ASSERT(xcrush);
373
374
0
  if (xcrush->ChunkHead >= 65530)
375
0
  {
376
0
    xcrush->ChunkHead = 1;
377
0
    xcrush->ChunkTail = 1;
378
0
  }
379
380
0
  if (xcrush->ChunkHead >= xcrush->ChunkTail)
381
0
  {
382
0
    xcrush_clear_hash_table_range(xcrush, xcrush->ChunkTail, xcrush->ChunkTail + 10000);
383
0
    xcrush->ChunkTail += 10000;
384
0
  }
385
386
0
  index = xcrush->ChunkHead++;
387
388
0
  if (xcrush->ChunkHead >= 65534)
389
0
    return -3001; /* error */
390
391
0
  xcrush->Chunks[index].offset = offset;
392
0
  seed = signature->seed;
393
394
0
  if (seed >= 65536)
395
0
    return -3002; /* error */
396
397
0
  if (xcrush->NextChunks[seed])
398
0
  {
399
0
    if (xcrush->NextChunks[seed] >= 65534)
400
0
      return -3003; /* error */
401
402
0
    WINPR_ASSERT(pPrevChunk);
403
0
    *pPrevChunk = &xcrush->Chunks[xcrush->NextChunks[seed]];
404
0
  }
405
406
0
  xcrush->Chunks[index].next = xcrush->NextChunks[seed] & 0xFFFF;
407
0
  xcrush->NextChunks[seed] = index;
408
0
  return 1;
409
0
}
410
411
static int xcrush_find_match_length(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush, UINT32 MatchOffset,
412
                                    UINT32 ChunkOffset, UINT32 HistoryOffset, UINT32 SrcSize,
413
                                    UINT32 MaxMatchLength,
414
                                    XCRUSH_MATCH_INFO* WINPR_RESTRICT MatchInfo)
415
0
{
416
0
  UINT32 MatchSymbol = 0;
417
0
  UINT32 ChunkSymbol = 0;
418
0
  BYTE* ChunkBuffer = NULL;
419
0
  BYTE* MatchBuffer = NULL;
420
0
  BYTE* MatchStartPtr = NULL;
421
0
  BYTE* ForwardChunkPtr = NULL;
422
0
  BYTE* ReverseChunkPtr = NULL;
423
0
  BYTE* ForwardMatchPtr = NULL;
424
0
  BYTE* ReverseMatchPtr = NULL;
425
0
  BYTE* HistoryBufferEnd = NULL;
426
0
  UINT32 ReverseMatchLength = 0;
427
0
  UINT32 ForwardMatchLength = 0;
428
0
  UINT32 TotalMatchLength = 0;
429
0
  BYTE* HistoryBuffer = NULL;
430
0
  UINT32 HistoryBufferSize = 0;
431
432
0
  WINPR_ASSERT(xcrush);
433
0
  WINPR_ASSERT(MatchInfo);
434
435
0
  HistoryBuffer = xcrush->HistoryBuffer;
436
0
  HistoryBufferSize = xcrush->HistoryBufferSize;
437
0
  HistoryBufferEnd = &HistoryBuffer[HistoryOffset + SrcSize];
438
439
0
  if (MatchOffset > HistoryBufferSize)
440
0
    return -2001; /* error */
441
442
0
  MatchBuffer = &HistoryBuffer[MatchOffset];
443
444
0
  if (ChunkOffset > HistoryBufferSize)
445
0
    return -2002; /* error */
446
447
0
  ChunkBuffer = &HistoryBuffer[ChunkOffset];
448
449
0
  if (MatchOffset == ChunkOffset)
450
0
    return -2003; /* error */
451
452
0
  if (MatchBuffer < HistoryBuffer)
453
0
    return -2004; /* error */
454
455
0
  if (ChunkBuffer < HistoryBuffer)
456
0
    return -2005; /* error */
457
458
0
  ForwardMatchPtr = &HistoryBuffer[MatchOffset];
459
0
  ForwardChunkPtr = &HistoryBuffer[ChunkOffset];
460
461
0
  if ((&MatchBuffer[MaxMatchLength + 1] < HistoryBufferEnd) &&
462
0
      (MatchBuffer[MaxMatchLength + 1] != ChunkBuffer[MaxMatchLength + 1]))
463
0
  {
464
0
    return 0;
465
0
  }
466
467
0
  while (1)
468
0
  {
469
0
    MatchSymbol = *ForwardMatchPtr++;
470
0
    ChunkSymbol = *ForwardChunkPtr++;
471
472
0
    if (MatchSymbol != ChunkSymbol)
473
0
      break;
474
475
0
    if (ForwardMatchPtr > HistoryBufferEnd)
476
0
      break;
477
478
0
    ForwardMatchLength++;
479
0
  }
480
481
0
  ReverseMatchPtr = MatchBuffer - 1;
482
0
  ReverseChunkPtr = ChunkBuffer - 1;
483
484
0
  while ((ReverseMatchPtr > &HistoryBuffer[HistoryOffset]) && (ReverseChunkPtr > HistoryBuffer) &&
485
0
         (*ReverseMatchPtr == *ReverseChunkPtr))
486
0
  {
487
0
    ReverseMatchLength++;
488
0
    ReverseMatchPtr--;
489
0
    ReverseChunkPtr--;
490
0
  }
491
492
0
  MatchStartPtr = MatchBuffer - ReverseMatchLength;
493
0
  TotalMatchLength = ReverseMatchLength + ForwardMatchLength;
494
495
0
  if (TotalMatchLength < 11)
496
0
    return 0;
497
498
0
  if (MatchStartPtr < HistoryBuffer)
499
0
    return -2006; /* error */
500
501
0
  MatchInfo->MatchOffset = MatchStartPtr - HistoryBuffer;
502
0
  MatchInfo->ChunkOffset = ChunkBuffer - ReverseMatchLength - HistoryBuffer;
503
0
  MatchInfo->MatchLength = TotalMatchLength;
504
0
  return (int)TotalMatchLength;
505
0
}
506
507
static int xcrush_find_all_matches(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush, UINT32 SignatureIndex,
508
                                   UINT32 HistoryOffset, UINT32 SrcOffset, UINT32 SrcSize)
509
0
{
510
0
  UINT32 j = 0;
511
0
  int status = 0;
512
0
  UINT32 ChunkIndex = 0;
513
0
  UINT32 ChunkCount = 0;
514
0
  XCRUSH_CHUNK* chunk = NULL;
515
0
  UINT32 MatchLength = 0;
516
0
  UINT32 MaxMatchLength = 0;
517
0
  UINT32 PrevMatchEnd = 0;
518
0
  XCRUSH_SIGNATURE* Signatures = NULL;
519
0
  XCRUSH_MATCH_INFO MaxMatchInfo = { 0 };
520
521
0
  WINPR_ASSERT(xcrush);
522
523
0
  Signatures = xcrush->Signatures;
524
525
0
  for (UINT32 i = 0; i < SignatureIndex; i++)
526
0
  {
527
0
    XCRUSH_MATCH_INFO MatchInfo = { 0 };
528
0
    UINT32 offset = SrcOffset + HistoryOffset;
529
530
0
    if (!Signatures[i].size)
531
0
      return -1001; /* error */
532
533
0
    status = xcrush_insert_chunk(xcrush, &Signatures[i], offset, &chunk);
534
535
0
    if (status < 0)
536
0
      return status;
537
538
0
    if (chunk && (SrcOffset + HistoryOffset + Signatures[i].size >= PrevMatchEnd))
539
0
    {
540
0
      ChunkCount = 0;
541
0
      MaxMatchLength = 0;
542
543
0
      while (chunk)
544
0
      {
545
0
        if ((chunk->offset < HistoryOffset) || (chunk->offset < offset) ||
546
0
            (chunk->offset > SrcSize + HistoryOffset))
547
0
        {
548
0
          status = xcrush_find_match_length(xcrush, offset, chunk->offset, HistoryOffset,
549
0
                                            SrcSize, MaxMatchLength, &MatchInfo);
550
551
0
          if (status < 0)
552
0
            return status; /* error */
553
554
0
          MatchLength = (UINT32)status;
555
556
0
          if (MatchLength > MaxMatchLength)
557
0
          {
558
0
            MaxMatchLength = MatchLength;
559
0
            MaxMatchInfo.MatchOffset = MatchInfo.MatchOffset;
560
0
            MaxMatchInfo.ChunkOffset = MatchInfo.ChunkOffset;
561
0
            MaxMatchInfo.MatchLength = MatchInfo.MatchLength;
562
563
0
            if (MatchLength > 256)
564
0
              break;
565
0
          }
566
0
        }
567
568
0
        ChunkIndex = ChunkCount++;
569
570
0
        if (ChunkIndex > 4)
571
0
          break;
572
573
0
        status = xcrush_find_next_matching_chunk(xcrush, chunk, &chunk);
574
575
0
        if (status < 0)
576
0
          return status; /* error */
577
0
      }
578
579
0
      if (MaxMatchLength)
580
0
      {
581
0
        xcrush->OriginalMatches[j].MatchOffset = MaxMatchInfo.MatchOffset;
582
0
        xcrush->OriginalMatches[j].ChunkOffset = MaxMatchInfo.ChunkOffset;
583
0
        xcrush->OriginalMatches[j].MatchLength = MaxMatchInfo.MatchLength;
584
585
0
        if (xcrush->OriginalMatches[j].MatchOffset < HistoryOffset)
586
0
          return -1002; /* error */
587
588
0
        PrevMatchEnd =
589
0
            xcrush->OriginalMatches[j].MatchLength + xcrush->OriginalMatches[j].MatchOffset;
590
0
        j++;
591
592
0
        if (j >= 1000)
593
0
          return -1003; /* error */
594
0
      }
595
0
    }
596
597
0
    SrcOffset += Signatures[i].size;
598
599
0
    if (SrcOffset > SrcSize)
600
0
      return -1004; /* error */
601
0
  }
602
603
0
  if (SrcOffset > SrcSize)
604
0
    return -1005; /* error */
605
606
0
  return (int)j;
607
0
}
608
609
static int xcrush_optimize_matches(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush)
610
0
{
611
0
  UINT32 j = 0;
612
0
  UINT32 MatchDiff = 0;
613
0
  UINT32 PrevMatchEnd = 0;
614
0
  UINT32 TotalMatchLength = 0;
615
0
  UINT32 OriginalMatchCount = 0;
616
0
  UINT32 OptimizedMatchCount = 0;
617
0
  XCRUSH_MATCH_INFO* OriginalMatch = NULL;
618
0
  XCRUSH_MATCH_INFO* OptimizedMatch = NULL;
619
0
  XCRUSH_MATCH_INFO* OriginalMatches = NULL;
620
0
  XCRUSH_MATCH_INFO* OptimizedMatches = NULL;
621
622
0
  WINPR_ASSERT(xcrush);
623
624
0
  OriginalMatches = xcrush->OriginalMatches;
625
0
  OriginalMatchCount = xcrush->OriginalMatchCount;
626
0
  OptimizedMatches = xcrush->OptimizedMatches;
627
628
0
  for (UINT32 i = 0; i < OriginalMatchCount; i++)
629
0
  {
630
0
    if (OriginalMatches[i].MatchOffset <= PrevMatchEnd)
631
0
    {
632
0
      if ((OriginalMatches[i].MatchOffset < PrevMatchEnd) &&
633
0
          (OriginalMatches[i].MatchLength + OriginalMatches[i].MatchOffset >
634
0
           PrevMatchEnd + 6))
635
0
      {
636
0
        MatchDiff = PrevMatchEnd - OriginalMatches[i].MatchOffset;
637
0
        OriginalMatch = &OriginalMatches[i];
638
0
        OptimizedMatch = &OptimizedMatches[j];
639
0
        OptimizedMatch->MatchOffset = OriginalMatch->MatchOffset;
640
0
        OptimizedMatch->ChunkOffset = OriginalMatch->ChunkOffset;
641
0
        OptimizedMatch->MatchLength = OriginalMatch->MatchLength;
642
643
0
        if (OptimizedMatches[j].MatchLength <= MatchDiff)
644
0
          return -5001; /* error */
645
646
0
        if (MatchDiff >= 20000)
647
0
          return -5002; /* error */
648
649
0
        OptimizedMatches[j].MatchLength -= MatchDiff;
650
0
        OptimizedMatches[j].MatchOffset += MatchDiff;
651
0
        OptimizedMatches[j].ChunkOffset += MatchDiff;
652
0
        PrevMatchEnd = OptimizedMatches[j].MatchLength + OptimizedMatches[j].MatchOffset;
653
0
        TotalMatchLength += OptimizedMatches[j].MatchLength;
654
0
        j++;
655
0
      }
656
0
    }
657
0
    else
658
0
    {
659
0
      OriginalMatch = &OriginalMatches[i];
660
0
      OptimizedMatch = &OptimizedMatches[j];
661
0
      OptimizedMatch->MatchOffset = OriginalMatch->MatchOffset;
662
0
      OptimizedMatch->ChunkOffset = OriginalMatch->ChunkOffset;
663
0
      OptimizedMatch->MatchLength = OriginalMatch->MatchLength;
664
0
      PrevMatchEnd = OptimizedMatches[j].MatchLength + OptimizedMatches[j].MatchOffset;
665
0
      TotalMatchLength += OptimizedMatches[j].MatchLength;
666
0
      j++;
667
0
    }
668
0
  }
669
670
0
  OptimizedMatchCount = j;
671
0
  xcrush->OptimizedMatchCount = OptimizedMatchCount;
672
0
  return (int)TotalMatchLength;
673
0
}
674
675
static int xcrush_generate_output(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush,
676
                                  BYTE* WINPR_RESTRICT OutputBuffer, UINT32 OutputSize,
677
                                  UINT32 HistoryOffset, UINT32* WINPR_RESTRICT pDstSize)
678
0
{
679
0
  BYTE* Literals = NULL;
680
0
  BYTE* OutputEnd = NULL;
681
0
  UINT32 MatchIndex = 0;
682
0
  UINT32 MatchOffset = 0;
683
0
  UINT16 MatchLength = 0;
684
0
  UINT32 MatchCount = 0;
685
0
  UINT32 CurrentOffset = 0;
686
0
  UINT32 MatchOffsetDiff = 0;
687
0
  UINT32 HistoryOffsetDiff = 0;
688
0
  RDP61_MATCH_DETAILS* MatchDetails = NULL;
689
690
0
  WINPR_ASSERT(xcrush);
691
0
  WINPR_ASSERT(OutputBuffer);
692
0
  WINPR_ASSERT(OutputSize >= 2);
693
0
  WINPR_ASSERT(pDstSize);
694
695
0
  MatchCount = xcrush->OptimizedMatchCount;
696
0
  OutputEnd = &OutputBuffer[OutputSize];
697
698
0
  if (&OutputBuffer[2] >= &OutputBuffer[OutputSize])
699
0
    return -6001; /* error */
700
701
0
  Data_Write_UINT16(OutputBuffer, MatchCount);
702
0
  MatchDetails = (RDP61_MATCH_DETAILS*)&OutputBuffer[2];
703
0
  Literals = (BYTE*)&MatchDetails[MatchCount];
704
705
0
  if (Literals > OutputEnd)
706
0
    return -6002; /* error */
707
708
0
  for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++)
709
0
  {
710
0
    Data_Write_UINT16(&MatchDetails[MatchIndex].MatchLength,
711
0
                      xcrush->OptimizedMatches[MatchIndex].MatchLength);
712
0
    Data_Write_UINT16(&MatchDetails[MatchIndex].MatchOutputOffset,
713
0
                      xcrush->OptimizedMatches[MatchIndex].MatchOffset - HistoryOffset);
714
0
    Data_Write_UINT32(&MatchDetails[MatchIndex].MatchHistoryOffset,
715
0
                      xcrush->OptimizedMatches[MatchIndex].ChunkOffset);
716
0
  }
717
718
0
  CurrentOffset = HistoryOffset;
719
720
0
  for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++)
721
0
  {
722
0
    MatchLength = (UINT16)(xcrush->OptimizedMatches[MatchIndex].MatchLength);
723
0
    MatchOffset = xcrush->OptimizedMatches[MatchIndex].MatchOffset;
724
725
0
    if (MatchOffset <= CurrentOffset)
726
0
    {
727
0
      if (MatchOffset != CurrentOffset)
728
0
        return -6003; /* error */
729
730
0
      CurrentOffset = MatchOffset + MatchLength;
731
0
    }
732
0
    else
733
0
    {
734
0
      MatchOffsetDiff = MatchOffset - CurrentOffset;
735
736
0
      if (Literals + MatchOffset - CurrentOffset >= OutputEnd)
737
0
        return -6004; /* error */
738
739
0
      CopyMemory(Literals, &xcrush->HistoryBuffer[CurrentOffset], MatchOffsetDiff);
740
741
0
      if (Literals >= OutputEnd)
742
0
        return -6005; /* error */
743
744
0
      Literals += MatchOffsetDiff;
745
0
      CurrentOffset = MatchOffset + MatchLength;
746
0
    }
747
0
  }
748
749
0
  HistoryOffsetDiff = xcrush->HistoryOffset - CurrentOffset;
750
751
0
  if (Literals + HistoryOffsetDiff >= OutputEnd)
752
0
    return -6006; /* error */
753
754
0
  CopyMemory(Literals, &xcrush->HistoryBuffer[CurrentOffset], HistoryOffsetDiff);
755
0
  *pDstSize = Literals + HistoryOffsetDiff - OutputBuffer;
756
0
  return 1;
757
0
}
758
759
static INLINE size_t xcrush_copy_bytes_no_overlap(BYTE* WINPR_RESTRICT dst,
760
                                                  const BYTE* WINPR_RESTRICT src, size_t num)
761
0
{
762
  // src and dst overlaps
763
  // we should copy the area that doesn't overlap repeatly
764
0
  const size_t diff = (dst > src) ? dst - src : src - dst;
765
0
  const size_t rest = num % diff;
766
0
  const size_t end = num - rest;
767
768
0
  for (size_t a = 0; a < end; a += diff)
769
0
    memcpy(&dst[a], &src[a], diff);
770
771
0
  if (rest != 0)
772
0
    memcpy(&dst[end], &src[end], rest);
773
774
0
  return num;
775
0
}
776
777
static INLINE size_t xcrush_copy_bytes(BYTE* dst, const BYTE* src, size_t num)
778
0
{
779
0
  WINPR_ASSERT(dst);
780
0
  WINPR_ASSERT(src);
781
782
0
  if (src + num < dst || src > dst + num)
783
0
    memcpy(dst, src, num);
784
0
  else if (src != dst)
785
0
    return xcrush_copy_bytes_no_overlap(dst, src, num);
786
787
0
  return num;
788
0
}
789
790
static int xcrush_decompress_l1(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush,
791
                                const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize,
792
                                const BYTE** WINPR_RESTRICT ppDstData,
793
                                UINT32* WINPR_RESTRICT pDstSize, UINT32 flags)
794
0
{
795
0
  const BYTE* pSrcEnd = NULL;
796
0
  const BYTE* Literals = NULL;
797
0
  UINT16 MatchCount = 0;
798
0
  UINT16 MatchIndex = 0;
799
0
  BYTE* OutputPtr = NULL;
800
0
  size_t OutputLength = 0;
801
0
  UINT32 OutputOffset = 0;
802
0
  BYTE* HistoryPtr = NULL;
803
0
  BYTE* HistoryBuffer = NULL;
804
0
  BYTE* HistoryBufferEnd = NULL;
805
0
  UINT32 HistoryBufferSize = 0;
806
0
  UINT16 MatchLength = 0;
807
0
  UINT16 MatchOutputOffset = 0;
808
0
  UINT32 MatchHistoryOffset = 0;
809
0
  const RDP61_MATCH_DETAILS* MatchDetails = NULL;
810
811
0
  WINPR_ASSERT(xcrush);
812
813
0
  if (SrcSize < 1)
814
0
    return -1001;
815
816
0
  WINPR_ASSERT(pSrcData);
817
0
  WINPR_ASSERT(ppDstData);
818
0
  WINPR_ASSERT(pDstSize);
819
820
0
  if (flags & L1_PACKET_AT_FRONT)
821
0
    xcrush->HistoryOffset = 0;
822
823
0
  pSrcEnd = &pSrcData[SrcSize];
824
0
  HistoryBuffer = xcrush->HistoryBuffer;
825
0
  HistoryBufferSize = xcrush->HistoryBufferSize;
826
0
  HistoryBufferEnd = &(HistoryBuffer[HistoryBufferSize]);
827
0
  xcrush->HistoryPtr = HistoryPtr = &(HistoryBuffer[xcrush->HistoryOffset]);
828
829
0
  if (flags & L1_NO_COMPRESSION)
830
0
  {
831
0
    Literals = pSrcData;
832
0
  }
833
0
  else
834
0
  {
835
0
    if (!(flags & L1_COMPRESSED))
836
0
      return -1002;
837
838
0
    if ((pSrcData + 2) > pSrcEnd)
839
0
      return -1003;
840
841
0
    Data_Read_UINT16(pSrcData, MatchCount);
842
0
    MatchDetails = (const RDP61_MATCH_DETAILS*)&pSrcData[2];
843
0
    Literals = (const BYTE*)&MatchDetails[MatchCount];
844
0
    OutputOffset = 0;
845
846
0
    if (Literals > pSrcEnd)
847
0
      return -1004;
848
849
0
    for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++)
850
0
    {
851
0
      Data_Read_UINT16(&MatchDetails[MatchIndex].MatchLength, MatchLength);
852
0
      Data_Read_UINT16(&MatchDetails[MatchIndex].MatchOutputOffset, MatchOutputOffset);
853
0
      Data_Read_UINT32(&MatchDetails[MatchIndex].MatchHistoryOffset, MatchHistoryOffset);
854
855
0
      if (MatchOutputOffset < OutputOffset)
856
0
        return -1005;
857
858
0
      if (MatchLength > HistoryBufferSize)
859
0
        return -1006;
860
861
0
      if (MatchHistoryOffset > HistoryBufferSize)
862
0
        return -1007;
863
864
0
      OutputLength = MatchOutputOffset - OutputOffset;
865
866
0
      if ((MatchOutputOffset - OutputOffset) > HistoryBufferSize)
867
0
        return -1008;
868
869
0
      if (OutputLength > 0)
870
0
      {
871
0
        if ((&HistoryPtr[OutputLength] >= HistoryBufferEnd) || (Literals >= pSrcEnd) ||
872
0
            (&Literals[OutputLength] > pSrcEnd))
873
0
          return -1009;
874
875
0
        xcrush_copy_bytes(HistoryPtr, Literals, OutputLength);
876
0
        HistoryPtr += OutputLength;
877
0
        Literals += OutputLength;
878
0
        OutputOffset += OutputLength;
879
880
0
        if (Literals > pSrcEnd)
881
0
          return -1010;
882
0
      }
883
884
0
      OutputPtr = &xcrush->HistoryBuffer[MatchHistoryOffset];
885
886
0
      if ((&HistoryPtr[MatchLength] >= HistoryBufferEnd) ||
887
0
          (&OutputPtr[MatchLength] >= HistoryBufferEnd))
888
0
        return -1011;
889
890
0
      xcrush_copy_bytes(HistoryPtr, OutputPtr, MatchLength);
891
0
      OutputOffset += MatchLength;
892
0
      HistoryPtr += MatchLength;
893
0
    }
894
0
  }
895
896
0
  if (Literals < pSrcEnd)
897
0
  {
898
0
    OutputLength = pSrcEnd - Literals;
899
900
0
    if ((&HistoryPtr[OutputLength] >= HistoryBufferEnd) || (&Literals[OutputLength] > pSrcEnd))
901
0
      return -1012;
902
903
0
    xcrush_copy_bytes(HistoryPtr, Literals, OutputLength);
904
0
    HistoryPtr += OutputLength;
905
0
  }
906
907
0
  xcrush->HistoryOffset = HistoryPtr - HistoryBuffer;
908
0
  *pDstSize = HistoryPtr - xcrush->HistoryPtr;
909
0
  *ppDstData = xcrush->HistoryPtr;
910
0
  return 1;
911
0
}
912
913
int xcrush_decompress(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush, const BYTE* WINPR_RESTRICT pSrcData,
914
                      UINT32 SrcSize, const BYTE** WINPR_RESTRICT ppDstData,
915
                      UINT32* WINPR_RESTRICT pDstSize, UINT32 flags)
916
0
{
917
0
  int status = 0;
918
0
  UINT32 DstSize = 0;
919
0
  const BYTE* pDstData = NULL;
920
0
  BYTE Level1ComprFlags = 0;
921
0
  BYTE Level2ComprFlags = 0;
922
923
0
  WINPR_ASSERT(xcrush);
924
925
0
  if (SrcSize < 2)
926
0
    return -1;
927
928
0
  WINPR_ASSERT(pSrcData);
929
0
  WINPR_ASSERT(ppDstData);
930
0
  WINPR_ASSERT(pDstSize);
931
932
0
  Level1ComprFlags = pSrcData[0];
933
0
  Level2ComprFlags = pSrcData[1];
934
0
  pSrcData += 2;
935
0
  SrcSize -= 2;
936
937
0
  if (flags & PACKET_FLUSHED)
938
0
  {
939
0
    ZeroMemory(xcrush->HistoryBuffer, xcrush->HistoryBufferSize);
940
0
    xcrush->HistoryOffset = 0;
941
0
  }
942
943
0
  if (!(Level2ComprFlags & PACKET_COMPRESSED))
944
0
  {
945
0
    status =
946
0
        xcrush_decompress_l1(xcrush, pSrcData, SrcSize, ppDstData, pDstSize, Level1ComprFlags);
947
0
    return status;
948
0
  }
949
950
0
  status =
951
0
      mppc_decompress(xcrush->mppc, pSrcData, SrcSize, &pDstData, &DstSize, Level2ComprFlags);
952
953
0
  if (status < 0)
954
0
    return status;
955
956
0
  status = xcrush_decompress_l1(xcrush, pDstData, DstSize, ppDstData, pDstSize, Level1ComprFlags);
957
0
  return status;
958
0
}
959
960
static int xcrush_compress_l1(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush,
961
                              const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize,
962
                              BYTE* WINPR_RESTRICT pDstData, UINT32* WINPR_RESTRICT pDstSize,
963
                              UINT32* WINPR_RESTRICT pFlags)
964
0
{
965
0
  int status = 0;
966
0
  UINT32 Flags = 0;
967
0
  UINT32 HistoryOffset = 0;
968
0
  BYTE* HistoryPtr = NULL;
969
0
  BYTE* HistoryBuffer = NULL;
970
0
  UINT32 SignatureIndex = 0;
971
972
0
  WINPR_ASSERT(xcrush);
973
0
  WINPR_ASSERT(pSrcData);
974
0
  WINPR_ASSERT(SrcSize > 0);
975
0
  WINPR_ASSERT(pDstData);
976
0
  WINPR_ASSERT(pDstSize);
977
0
  WINPR_ASSERT(pFlags);
978
979
0
  if (xcrush->HistoryOffset + SrcSize + 8 > xcrush->HistoryBufferSize)
980
0
  {
981
0
    xcrush->HistoryOffset = 0;
982
0
    Flags |= L1_PACKET_AT_FRONT;
983
0
  }
984
985
0
  HistoryOffset = xcrush->HistoryOffset;
986
0
  HistoryBuffer = xcrush->HistoryBuffer;
987
0
  HistoryPtr = &HistoryBuffer[HistoryOffset];
988
0
  MoveMemory(HistoryPtr, pSrcData, SrcSize);
989
0
  xcrush->HistoryOffset += SrcSize;
990
991
0
  if (SrcSize > 50)
992
0
  {
993
0
    SignatureIndex = xcrush_compute_signatures(xcrush, pSrcData, SrcSize);
994
995
0
    if (SignatureIndex)
996
0
    {
997
0
      status = xcrush_find_all_matches(xcrush, SignatureIndex, HistoryOffset, 0, SrcSize);
998
999
0
      if (status < 0)
1000
0
        return status;
1001
1002
0
      xcrush->OriginalMatchCount = (UINT32)status;
1003
0
      xcrush->OptimizedMatchCount = 0;
1004
1005
0
      if (xcrush->OriginalMatchCount)
1006
0
      {
1007
0
        status = xcrush_optimize_matches(xcrush);
1008
1009
0
        if (status < 0)
1010
0
          return status;
1011
0
      }
1012
1013
0
      if (xcrush->OptimizedMatchCount)
1014
0
      {
1015
0
        status = xcrush_generate_output(xcrush, pDstData, SrcSize, HistoryOffset, pDstSize);
1016
1017
0
        if (status < 0)
1018
0
          return status;
1019
1020
0
        Flags |= L1_COMPRESSED;
1021
0
      }
1022
0
    }
1023
0
  }
1024
1025
0
  if (!(Flags & L1_COMPRESSED))
1026
0
  {
1027
0
    Flags |= L1_NO_COMPRESSION;
1028
0
    *pDstSize = SrcSize;
1029
0
  }
1030
1031
0
  *pFlags = Flags;
1032
0
  return 1;
1033
0
}
1034
1035
int xcrush_compress(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush, const BYTE* WINPR_RESTRICT pSrcData,
1036
                    UINT32 SrcSize, BYTE* WINPR_RESTRICT pDstBuffer,
1037
                    const BYTE** WINPR_RESTRICT ppDstData, UINT32* WINPR_RESTRICT pDstSize,
1038
                    UINT32* WINPR_RESTRICT pFlags)
1039
0
{
1040
0
  int status = 0;
1041
0
  UINT32 DstSize = 0;
1042
0
  BYTE* pDstData = NULL;
1043
0
  const BYTE* CompressedData = NULL;
1044
0
  UINT32 CompressedDataSize = 0;
1045
0
  BYTE* OriginalData = NULL;
1046
0
  UINT32 OriginalDataSize = 0;
1047
0
  UINT32 Level1ComprFlags = 0;
1048
0
  UINT32 Level2ComprFlags = 0;
1049
0
  UINT32 CompressionLevel = 3;
1050
1051
0
  WINPR_ASSERT(xcrush);
1052
0
  WINPR_ASSERT(pSrcData);
1053
0
  WINPR_ASSERT(SrcSize > 0);
1054
0
  WINPR_ASSERT(ppDstData);
1055
0
  WINPR_ASSERT(pDstSize);
1056
0
  WINPR_ASSERT(pFlags);
1057
1058
0
  if (SrcSize > 16384)
1059
0
    return -1001;
1060
1061
0
  if ((SrcSize + 2) > *pDstSize)
1062
0
    return -1002;
1063
1064
0
  OriginalData = pDstBuffer;
1065
0
  *ppDstData = pDstBuffer;
1066
0
  OriginalDataSize = SrcSize;
1067
0
  pDstData = xcrush->BlockBuffer;
1068
0
  CompressedDataSize = SrcSize;
1069
0
  status = xcrush_compress_l1(xcrush, pSrcData, SrcSize, pDstData, &CompressedDataSize,
1070
0
                              &Level1ComprFlags);
1071
1072
0
  if (status < 0)
1073
0
    return status;
1074
1075
0
  if (Level1ComprFlags & L1_COMPRESSED)
1076
0
  {
1077
0
    CompressedData = pDstData;
1078
1079
0
    if (CompressedDataSize > SrcSize)
1080
0
      return -1003;
1081
0
  }
1082
0
  else
1083
0
  {
1084
0
    CompressedData = pSrcData;
1085
1086
0
    if (CompressedDataSize != SrcSize)
1087
0
      return -1004;
1088
0
  }
1089
1090
0
  status = 0;
1091
0
  pDstData = &OriginalData[2];
1092
0
  DstSize = OriginalDataSize - 2;
1093
1094
0
  if (CompressedDataSize > 50)
1095
0
  {
1096
0
    const BYTE* pUnusedDstData = NULL;
1097
0
    status = mppc_compress(xcrush->mppc, CompressedData, CompressedDataSize, pDstData,
1098
0
                           &pUnusedDstData, &DstSize, &Level2ComprFlags);
1099
0
  }
1100
1101
0
  if (status < 0)
1102
0
    return status;
1103
1104
0
  if (!status || (Level2ComprFlags & PACKET_FLUSHED))
1105
0
  {
1106
0
    if (CompressedDataSize > DstSize)
1107
0
    {
1108
0
      xcrush_context_reset(xcrush, TRUE);
1109
0
      *ppDstData = pSrcData;
1110
0
      *pDstSize = SrcSize;
1111
0
      *pFlags = 0;
1112
0
      return 1;
1113
0
    }
1114
1115
0
    DstSize = CompressedDataSize;
1116
0
    CopyMemory(&OriginalData[2], CompressedData, CompressedDataSize);
1117
0
  }
1118
1119
0
  if (Level2ComprFlags & PACKET_COMPRESSED)
1120
0
  {
1121
0
    Level2ComprFlags |= xcrush->CompressionFlags;
1122
0
    xcrush->CompressionFlags = 0;
1123
0
  }
1124
0
  else if (Level2ComprFlags & PACKET_FLUSHED)
1125
0
  {
1126
0
    xcrush->CompressionFlags = PACKET_FLUSHED;
1127
0
  }
1128
1129
0
  Level1ComprFlags |= L1_INNER_COMPRESSION;
1130
0
  OriginalData[0] = (BYTE)Level1ComprFlags;
1131
0
  OriginalData[1] = (BYTE)Level2ComprFlags;
1132
#if defined(DEBUG_XCRUSH)
1133
  WLog_DBG(TAG, "XCrushCompress: Level1ComprFlags: %s Level2ComprFlags: %s",
1134
           xcrush_get_level_1_compression_flags_string(Level1ComprFlags),
1135
           xcrush_get_level_2_compression_flags_string(Level2ComprFlags));
1136
#endif
1137
1138
0
  if (*pDstSize < (DstSize + 2))
1139
0
    return -1006;
1140
1141
0
  *pDstSize = DstSize + 2;
1142
0
  *pFlags = PACKET_COMPRESSED | CompressionLevel;
1143
0
  return 1;
1144
0
}
1145
1146
void xcrush_context_reset(XCRUSH_CONTEXT* WINPR_RESTRICT xcrush, BOOL flush)
1147
0
{
1148
0
  WINPR_ASSERT(xcrush);
1149
1150
0
  xcrush->SignatureIndex = 0;
1151
0
  xcrush->SignatureCount = 1000;
1152
0
  ZeroMemory(&(xcrush->Signatures), sizeof(XCRUSH_SIGNATURE) * xcrush->SignatureCount);
1153
0
  xcrush->CompressionFlags = 0;
1154
0
  xcrush->ChunkHead = xcrush->ChunkTail = 1;
1155
0
  ZeroMemory(&(xcrush->Chunks), sizeof(xcrush->Chunks));
1156
0
  ZeroMemory(&(xcrush->NextChunks), sizeof(xcrush->NextChunks));
1157
0
  ZeroMemory(&(xcrush->OriginalMatches), sizeof(xcrush->OriginalMatches));
1158
0
  ZeroMemory(&(xcrush->OptimizedMatches), sizeof(xcrush->OptimizedMatches));
1159
1160
0
  if (flush)
1161
0
    xcrush->HistoryOffset = xcrush->HistoryBufferSize + 1;
1162
0
  else
1163
0
    xcrush->HistoryOffset = 0;
1164
1165
0
  mppc_context_reset(xcrush->mppc, flush);
1166
0
}
1167
1168
XCRUSH_CONTEXT* xcrush_context_new(BOOL Compressor)
1169
0
{
1170
0
  XCRUSH_CONTEXT* xcrush = (XCRUSH_CONTEXT*)calloc(1, sizeof(XCRUSH_CONTEXT));
1171
1172
0
  if (!xcrush)
1173
0
    goto fail;
1174
1175
0
  xcrush->Compressor = Compressor;
1176
0
  xcrush->mppc = mppc_context_new(1, Compressor);
1177
0
  if (!xcrush->mppc)
1178
0
    goto fail;
1179
0
  xcrush->HistoryBufferSize = 2000000;
1180
0
  xcrush_context_reset(xcrush, FALSE);
1181
1182
0
  return xcrush;
1183
0
fail:
1184
0
  xcrush_context_free(xcrush);
1185
1186
0
  return NULL;
1187
0
}
1188
1189
void xcrush_context_free(XCRUSH_CONTEXT* xcrush)
1190
0
{
1191
0
  if (xcrush)
1192
0
  {
1193
0
    mppc_context_free(xcrush->mppc);
1194
0
    free(xcrush);
1195
0
  }
1196
0
}