CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

WWCompression.cs (30518B)


      1 //
      2 // Copyright 2020 Electronic Arts Inc.
      3 //
      4 // The Command & Conquer Map Editor and corresponding source code is free 
      5 // software: you can redistribute it and/or modify it under the terms of 
      6 // the GNU General Public License as published by the Free Software Foundation, 
      7 // either version 3 of the License, or (at your option) any later version.
      8 
      9 // The Command & Conquer Map Editor and corresponding source code is distributed 
     10 // in the hope that it will be useful, but with permitted additional restrictions 
     11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
     12 // distributed with this program. You should have received a copy of the 
     13 // GNU General Public License along with permitted additional restrictions 
     14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
     15 using System;
     16 
     17 namespace MobiusEditor.Utility
     18 {
     19     /// <summary>
     20     /// This class contains encoders and decoders for the Westwood XOR Delta and LCW compression schemes.
     21     /// </summary>
     22     public static class WWCompression
     23     {
     24         ////////////////////////////////////////////////////////////////////////////////
     25         //  Notes
     26         ////////////////////////////////////////////////////////////////////////////////
     27         //
     28         // LCW streams should always start and end with the fill command (& 0x80) though
     29         // the decompressor doesn't strictly require that it start with one the ability
     30         // to use the offset commands in place of the RLE command early in the stream
     31         // relies on it. Streams larger than 64k that need the relative versions of the
     32         // 3 and 5 byte commands should start with a null byte before the first 0x80
     33         // command to flag that they are relative compressed.
     34         //
     35         // LCW uses the following rules to decide which command to use:
     36         // 1. Runs of the same colour should only use 4 byte RLE command if longer than
     37         //    64 bytes. 2 and 3 byte offset commands are more efficient otherwise.
     38         // 2. Runs of less than 3 should just be stored as is with the one byte fill
     39         //    command.
     40         // 3. Runs greater than 10 or if the relative offset is greater than
     41         //    4095 use an absolute copy. Less than 64 bytes uses 3 byte command, else it
     42         //    uses the 5 byte command.
     43         // 4. If Absolute rule isn't met then copy from a relative offset with 2 byte
     44         //    command.
     45         //
     46         // Absolute LCW can efficiently compress data that is 64k in size, much greater
     47         // and relative offsets for the 3 and 5 byte commands are needed.
     48         //
     49         // The XOR delta generator code works to the following assumptions
     50         //
     51         // 1. Any skip command is preferable if source and base are same
     52         // 2. Fill is preferable to XOR if 4 or larger, XOR takes same data plus at
     53         //    least 1 byte
     54         //
     55         ////////////////////////////////////////////////////////////////////////////////
     56 
     57         ////////////////////////////////////////////////////////////////////////////////
     58         //  Some defines used by the encoders
     59         ////////////////////////////////////////////////////////////////////////////////
     60         public const Byte XOR_SMALL = 0x7F;
     61         public const Byte XOR_MED = 0xFF;
     62         public const Int32 XOR_LARGE = 0x3FFF;
     63         public const Int32 XOR_MAX = 0x7FFF;
     64 
     65         ////////////////////////////////////////////////////////////////////////////////
     66         //  Some utility functions to get worst case sizes for buffer allocation
     67         ////////////////////////////////////////////////////////////////////////////////
     68 
     69         public static Int32 LCWWorstCase(Int32 datasize)
     70         {
     71             return datasize + (datasize / 63) + 1;
     72         }
     73 
     74         public static Int32 XORWorstCase(Int32 datasize)
     75         {
     76             return datasize + ((datasize / 63) * 3) + 4;
     77         }
     78         
     79         /// <summary>
     80         ///    Compresses data to the proprietary LCW format used in
     81         ///    many games developed by Westwood Studios. Compression is better
     82         ///    than that achieved by popular community tools. This is a new
     83         ///    implementation based on understanding of the compression gained from
     84         ///    the reference code.
     85         /// </summary>
     86         /// <param name="input">Array of the data to compress.</param>
     87         /// <returns>The compressed data.</returns>
     88         /// <remarks>Commonly known in the community as "format80".</remarks>
     89         public static Byte[] LcwCompress(Byte[] input)
     90         {
     91             if (input == null || input.Length == 0)
     92                 return new Byte[0];
     93 
     94             //Decide if we are going to do relative offsets for 3 and 5 byte commands
     95             Boolean relative = input.Length > UInt16.MaxValue;
     96 
     97             // Nyer's C# conversion: replacements for write and read for pointers.
     98             Int32 getp = 0;
     99             Int32 putp = 0;
    100             // Input length. Used commonly enough to warrant getting it out in advance I guess.
    101             Int32 getend = input.Length;
    102             // "Worst case length" code by OmniBlade. We'll just use a buffer of
    103             // that max length and cut it down to the actual used size at the end.
    104             // Not using it- it's not big enough in case of some small images.
    105             //LCWWorstCase(getend)
    106             Int32 worstcase = Math.Max(10000, getend * 2);
    107             Byte[] output = new Byte[worstcase];
    108             // relative LCW starts with 0 as flag to decoder.
    109             // this is only used by later games for decoding hi-color vqa files.
    110             if (relative)
    111                 output[putp++] = 0;
    112 
    113             //Implementations that properly conform to the WestWood encoder should
    114             //write a starting cmd1. It's important for using the offset copy commands
    115             //to do more efficient RLE in some cases than the cmd4.
    116 
    117             //we also set bool to flag that we have an on going cmd1.
    118             Int32 cmd_onep = putp;
    119             output[putp++] = 0x81;
    120             output[putp++] = input[getp++];
    121             Boolean cmd_one = true;
    122 
    123             //Compress data until we reach end of input buffer.
    124             while (getp < getend)
    125             {
    126                 //Is RLE encode (4bytes) worth evaluating?
    127                 if (getend - getp > 64 && input[getp] == input[getp + 64])
    128                 {
    129                     //RLE run length is encoded as a short so max is UINT16_MAX
    130                     Int32 rlemax = (getend - getp) < UInt16.MaxValue ? getend : getp + UInt16.MaxValue;
    131                     Int32 rlep = getp + 1;
    132                     while (rlep < rlemax && input[rlep] == input[getp])
    133                         rlep++;
    134 
    135                     UInt16 run_length = (UInt16)(rlep - getp);
    136 
    137                     //If run length is long enough, write the command and start loop again
    138                     if (run_length >= 0x41)
    139                     {
    140                         //write 4byte command 0b11111110
    141                         cmd_one = false;
    142                         output[putp++] = 0xFE;
    143                         output[putp++] = (Byte)(run_length & 0xFF);
    144                         output[putp++] = (Byte)((run_length >> 8) & 0xFF);
    145                         output[putp++] = input[getp];
    146                         getp = rlep;
    147                         continue;
    148                     }
    149                 }
    150 
    151                 //current block size for an offset copy
    152                 UInt16 block_size = 0;
    153                 //Set where we start looking for matching runs.
    154                 Int32 offstart = relative ? getp < UInt16.MaxValue ? 0 : getp - UInt16.MaxValue : 0;
    155 
    156                 //Look for matching runs
    157                 Int32 offchk = offstart;
    158                 Int32 offsetp = getp;
    159                 while (offchk < getp)
    160                 {
    161                     //Move offchk to next matching position
    162                     while (offchk < getp && input[offchk] != input[getp])
    163                         offchk++;
    164 
    165                     //If the checking pointer has reached current pos, break
    166                     if (offchk >= getp)
    167                         break;
    168 
    169                     //find out how long the run of matches goes for
    170                     Int32 i;
    171                     for (i = 1; getp + i < getend; ++i)
    172                         if (input[offchk + i] != input[getp + i])
    173                             break;
    174                     if (i >= block_size)
    175                     {
    176                         block_size = (UInt16)i;
    177                         offsetp = offchk;
    178                     }
    179                     offchk++;
    180                 }
    181 
    182                 //decide what encoding to use for current run
    183                 //If it's less than 2 bytes long, we store as is with cmd1
    184                 if (block_size <= 2)
    185                 {
    186                     //short copy 0b10??????
    187                     //check we have an existing 1 byte command and if its value is still
    188                     //small enough to handle additional bytes
    189                     //start a new command if current one doesn't have space or we don't
    190                     //have one to continue
    191                     if (cmd_one && output[cmd_onep] < 0xBF)
    192                     {
    193                         //increment command value
    194                         output[cmd_onep]++;
    195                         output[putp++] = input[getp++];
    196                     }
    197                     else
    198                     {
    199                         cmd_onep = putp;
    200                         output[putp++] = 0x81;
    201                         output[putp++] = input[getp++];
    202                         cmd_one = true;
    203                     }
    204                     //Otherwise we need to decide what relative copy command is most efficient
    205                 }
    206                 else
    207                 {
    208                     Int32 offset;
    209                     Int32 rel_offset = getp - offsetp;
    210                     if (block_size > 0xA || ((rel_offset) > 0xFFF))
    211                     {
    212                         //write 5 byte command 0b11111111
    213                         if (block_size > 0x40)
    214                         {
    215                             output[putp++] = 0xFF;
    216                             output[putp++] = (Byte)(block_size & 0xFF);
    217                             output[putp++] = (Byte)((block_size >> 8) & 0xFF);
    218                             //write 3 byte command 0b11??????
    219                         }
    220                         else
    221                         {
    222                             output[putp++] = (Byte)((block_size - 3) | 0xC0);
    223                         }
    224 
    225                         offset = relative ? rel_offset : offsetp;
    226                         //write 2 byte command? 0b0???????
    227                     }
    228                     else
    229                     {
    230                         offset = rel_offset << 8 | (16 * (block_size - 3) + (rel_offset >> 8));
    231                     }
    232                     output[putp++] = (Byte)(offset & 0xFF);
    233                     output[putp++] = (Byte)((offset >> 8) & 0xFF);
    234                     getp += block_size;
    235                     cmd_one = false;
    236                 }
    237             }
    238 
    239             //write final 0x80, basically an empty cmd1 to signal the end of the stream.
    240             output[putp++] = 0x80;
    241 
    242             Byte[] finalOutput = new Byte[putp];
    243             Array.Copy(output, 0, finalOutput, 0, putp);
    244             // Return the final compressed data.
    245             return finalOutput;
    246         }
    247 
    248         /// <summary>
    249         ///     Decompresses data in the proprietary LCW format used in many games
    250         ///     developed by Westwood Studios.
    251         /// </summary>
    252         /// <param name="input">The data to decompress.</param>
    253         /// <param name="readOffset">Location to start at in the input array.</param>
    254         /// <param name="output">The buffer to store the decompressed data. This is assumed to be initialized to the correct size.</param>
    255         /// <param name="readEnd">End offset for reading. Use 0 to take the end of the given data array.</param>
    256         /// <returns>Length of the decompressed data in bytes.</returns>
    257         public static Int32 LcwDecompress(Byte[] input, ref Int32 readOffset, Byte[] output, Int32 readEnd)
    258         {
    259             if (input == null || input.Length == 0 || output == null || output.Length == 0)
    260                 return 0;
    261 	        Boolean relative = false;
    262             // Nyer's C# conversion: replacements for write and read for pointers.
    263 	        Int32 writeOffset = 0;
    264             // Output length should be part of the information given in the file format using LCW.
    265             // Techncically it can just be cropped at the end, though this value is used to
    266             // automatically cut off repeat-commands that go too far.
    267             Int32 writeEnd = output.Length;
    268             if (readEnd <= 0)
    269                 readEnd = input.Length;
    270 
    271             //Decide if the stream uses relative 3 and 5 byte commands
    272 	        //Extension allows effective compression of data > 64k
    273 	        //https://github.com/madmoose/scummvm/blob/bladerunner/engines/bladerunner/decompress_lcw.cpp
    274             // this is only used by later games for decoding hi-color vqa files.
    275             // For other stuff (like shp), just check in advance to decide if the data is too big.
    276             if (readOffset >= readEnd)
    277                 return writeOffset;
    278 	        if (input[readOffset] == 0)
    279 	        {
    280 		        relative = true;
    281 		        readOffset++;
    282 	        }
    283 	        //DEBUG_SAY("LCW Decompression... \n");
    284 	        while (writeOffset < writeEnd)
    285 	        {
    286                 if (readOffset >= readEnd)
    287 	                return writeOffset;
    288 		        Byte flag = input[readOffset++];
    289 		        UInt16 cpysize;
    290                 UInt16 offset;
    291 
    292 		        if ((flag & 0x80) != 0)
    293 		        {
    294 			        if ((flag & 0x40) != 0)
    295 			        {
    296                         cpysize = (UInt16)((flag & 0x3F) + 3);
    297 				        //long set 0b11111110
    298 				        if (flag == 0xFE)
    299 				        {
    300                             if (readOffset >= readEnd)
    301                                 return writeOffset;
    302                             cpysize = input[readOffset++];
    303                             if (readOffset >= readEnd)
    304                                 return writeOffset;
    305                             cpysize += (UInt16)((input[readOffset++]) << 8);
    306 					        if (cpysize > writeEnd - writeOffset)
    307                                 cpysize = (UInt16)(writeEnd - writeOffset);
    308                             if (readOffset >= readEnd)
    309                                 return writeOffset;
    310 					        //DEBUG_SAY("0b11111110 Source Pos %ld, Dest Pos %ld, Count %d\n", source - sstart - 3, dest - start, cpysize);
    311                             for (; cpysize > 0; --cpysize)
    312                             {
    313                                 if (writeOffset >= writeEnd)
    314                                     return writeOffset;
    315                                 output[writeOffset++] = input[readOffset];
    316                             }
    317 				            readOffset++;
    318 				        }
    319 				        else
    320 				        {
    321 					        Int32 s;
    322 					        //long move, abs 0b11111111
    323 					        if (flag == 0xFF)
    324 					        {
    325                                 if (readOffset >= readEnd)
    326                                     return writeOffset;
    327                                 cpysize = input[readOffset++];
    328                                 if (readOffset >= readEnd)
    329                                     return writeOffset;
    330 						        cpysize += (UInt16)((input[readOffset++]) << 8);
    331 						        if (cpysize > writeEnd - writeOffset)
    332 							        cpysize = (UInt16)(writeEnd - writeOffset);
    333                                 if (readOffset >= readEnd)
    334                                     return writeOffset;
    335                                 offset = input[readOffset++];
    336                                 if (readOffset >= readEnd)
    337                                     return writeOffset;
    338                                 offset += (UInt16)((input[readOffset++]) << 8);
    339                                 //extended format for VQA32
    340 						        if (relative)
    341 							        s = writeOffset - offset;
    342 						        else
    343 							        s = offset;
    344 						        //DEBUG_SAY("0b11111111 Source Pos %ld, Dest Pos %ld, Count %d, Offset %d\n", source - sstart - 5, dest - start, cpysize, offset);
    345 					            for (; cpysize > 0; --cpysize)
    346 					            {
    347                                     if (writeOffset >= writeEnd)
    348                                         return writeOffset;
    349 					                output[writeOffset++] = output[s++];
    350 					            }
    351 					            //short move abs 0b11??????
    352 					        }
    353 					        else
    354 					        {
    355 						        if (cpysize > writeEnd - writeOffset)
    356 							        cpysize = (UInt16)(writeEnd - writeOffset);
    357                                 if (readOffset >= readEnd)
    358                                     return writeOffset;
    359                                 offset = input[readOffset++];
    360                                 if (readOffset >= readEnd)
    361                                     return writeOffset;
    362                                 offset += (UInt16)((input[readOffset++]) << 8);
    363 						        //extended format for VQA32
    364 						        if (relative)
    365 							        s = writeOffset - offset;
    366 						        else
    367 							        s = offset;
    368 						        //DEBUG_SAY("0b11?????? Source Pos %ld, Dest Pos %ld, Count %d, Offset %d\n", source - sstart - 3, dest - start, cpysize, offset);
    369 					            for (; cpysize > 0; --cpysize)
    370                                 {
    371                                     if (writeOffset >= writeEnd)
    372                                         return writeOffset;
    373                                     output[writeOffset++] = output[s++];
    374 					            }
    375 					        }
    376 				        }
    377 			        //short copy 0b10??????
    378 			        }
    379 			        else
    380 			        {
    381 				        if (flag == 0x80)
    382 				        {
    383 					        //DEBUG_SAY("0b10?????? Source Pos %ld, Dest Pos %ld, Count %d\n", source - sstart - 1, dest - start, 0);
    384 					        return writeOffset;
    385 				        }
    386 				        cpysize = (UInt16)(flag & 0x3F);
    387 				        if (cpysize > writeEnd - writeOffset)
    388 					        cpysize = (UInt16)(writeEnd - writeOffset);
    389 				        //DEBUG_SAY("0b10?????? Source Pos %ld, Dest Pos %ld, Count %d\n", source - sstart - 1, dest - start, cpysize);
    390 			            for (; cpysize > 0; --cpysize)
    391 			            {
    392                             if (readOffset >= readEnd || writeOffset >= writeEnd)
    393                                 return writeOffset;
    394 			                output[writeOffset++] = input[readOffset++];
    395 			            }
    396 			        }
    397 		        //short move rel 0b0???????
    398 		        }
    399 		        else
    400 		        {
    401 			        cpysize = (UInt16)((flag >> 4) + 3);
    402 			        if (cpysize > writeEnd - writeOffset)
    403 				        cpysize = (UInt16)(writeEnd - writeOffset);
    404                     if (readOffset >= readEnd)
    405                         return writeOffset;
    406 			        offset = (UInt16)(((flag & 0xF) << 8) + input[readOffset++]);
    407 			        //DEBUG_SAY("0b0??????? Source Pos %ld, Dest Pos %ld, Count %d, Offset %d\n", source - sstart - 2, dest - start, cpysize, offset);
    408 			        for (; cpysize > 0; --cpysize)
    409                     {
    410                         if (writeOffset >= writeEnd || writeOffset < offset)
    411                             return writeOffset;
    412 				        output[writeOffset] = output[writeOffset - offset];
    413 				        writeOffset++;
    414 			        }
    415 		        }
    416 	        }
    417             // If buffer is full, make sure to skip end command!
    418             if (writeOffset == writeEnd && readOffset < input.Length && input[readOffset] == 0x80)
    419                 readOffset++;
    420 	        return writeOffset;
    421         }
    422 
    423         /// <summary>
    424         /// Generates a binary delta between two buffers. Mainly used for image data.
    425         /// </summary>
    426         /// <param name="source">Buffer containing data to generate the delta for.</param>
    427         /// <param name="base">Buffer containing data that is the base for the delta.</param>
    428         /// <returns>The generated delta as bytes array.</returns>
    429         /// <remarks>Commonly known in the community as "format40".</remarks>
    430         public static Byte[] GenerateXorDelta(Byte[] source, Byte[] @base)
    431         {
    432             // Nyer's C# conversion: replacements for write and read for pointers.
    433             // -for our delta (output)
    434             Int32 putp = 0;
    435             // -for the image we go to
    436             Int32 getsp = 0;
    437             // -for the image we come from
    438             Int32 getbp = 0;
    439             //Length to process
    440             Int32 getsendp = Math.Min(source.Length, @base.Length);
    441             Byte[] dest = new Byte[XORWorstCase(getsendp)];
    442 
    443             //Only check getsp to save a redundant check.
    444             //Both source and base should be same size and both pointers should be
    445             //incremented at the same time.
    446             while (getsp < getsendp)
    447             {
    448                 UInt32 fillcount = 0;
    449                 UInt32 xorcount = 0;
    450                 UInt32 skipcount = 0;
    451                 Byte lastxor = (Byte)(source[getsp] ^ @base[getbp]);
    452                 Int32 testsp = getsp;
    453                 Int32 testbp = getbp;
    454 
    455                 //Only evaluate other options if we don't have a matched pair
    456                 while (testsp < getsendp && source[testsp] != @base[testbp])
    457                 {
    458                     if ((source[testsp] ^  @base[testbp]) == lastxor)
    459                     {
    460                         ++fillcount;
    461                         ++xorcount;
    462                     }
    463                     else
    464                     {
    465                         if (fillcount > 3)
    466                             break;
    467                         lastxor = (Byte)(source[testsp] ^ @base[testbp]);
    468                         fillcount = 1;
    469                         ++xorcount;
    470                     }
    471                     testsp++;
    472                     testbp++;
    473                 }
    474 
    475                 //fillcount should always be lower than xorcount and should be greater
    476                 //than 3 to warrant using the fill commands.
    477                 fillcount = fillcount > 3 ? fillcount : 0;
    478 
    479                 //Okay, lets see if we have any xor bytes we need to handle
    480                 xorcount -= fillcount;
    481                 while (xorcount != 0)
    482                 {
    483                     UInt16 count;
    484                     //It's cheaper to do the small cmd twice than do the large cmd once
    485                     //for data that can be handled by two small cmds.
    486                     //cmd 0???????
    487                     if (xorcount < XOR_MED)
    488                     {
    489                         count = (UInt16)(xorcount <= XOR_SMALL ? xorcount : XOR_SMALL);
    490                         dest[putp++] = (Byte)count;
    491                         //cmd 10000000 10?????? ??????
    492                     }
    493                     else
    494                     {
    495                         count = (UInt16)(xorcount <= XOR_LARGE ? xorcount : XOR_LARGE);
    496                         dest[putp++] = 0x80;
    497                         dest[putp++] = (Byte)(count & 0xFF);
    498                         dest[putp++] = (Byte)(((count >> 8) & 0xFF) | 0x80);
    499                     }
    500 
    501                     while (count != 0)
    502                     {
    503                         dest[putp++] = (Byte)(source[getsp++] ^ @base[getbp++]);
    504                         count--;
    505                         xorcount--;
    506                     }
    507                 }
    508 
    509                 //lets handle the bytes that are best done as xorfill
    510                 while (fillcount != 0)
    511                 {
    512                     UInt16 count;
    513                     //cmd 00000000 ????????
    514                     if (fillcount <= XOR_MED)
    515                     {
    516                         count = (UInt16)fillcount;
    517                         dest[putp++] = 0;
    518                         dest[putp++] = (Byte)(count & 0xFF);
    519                         //cmd 10000000 11?????? ??????
    520                     }
    521                     else
    522                     {
    523                         count = (UInt16)(fillcount <= XOR_LARGE ? fillcount : XOR_LARGE);
    524                         dest[putp++] = 0x80;
    525                         dest[putp++] = (Byte)(count & 0xFF);
    526                         dest[putp++] = (Byte)(((count >> 8) & 0xFF) | 0xC0);
    527                     }
    528                     dest[putp++] = (Byte)(source[getsp] ^ @base[getbp]);
    529                     fillcount -= count;
    530                     getsp += count;
    531                     getbp += count;
    532                 }
    533 
    534                 //Handle regions that match exactly
    535                 while (testsp < getsendp && source[testsp] == @base[testbp])
    536                 {
    537                     skipcount++;
    538                     testsp++;
    539                     testbp++;
    540                 }
    541 
    542                 while (skipcount != 0)
    543                 {
    544                     UInt16 count;
    545                     //Again it's cheaper to do the small cmd twice than do the large cmd
    546                     //once for data that can be handled by two small cmds.
    547                     //cmd 1???????
    548                     if (skipcount < XOR_MED)
    549                     {
    550                         count = (Byte)(skipcount <= XOR_SMALL ? skipcount : XOR_SMALL);
    551                         dest[putp++] = (Byte)(count | 0x80);
    552                         //cmd 10000000 0??????? ????????
    553                     }
    554                     else
    555                     {
    556                         count = (UInt16)(skipcount <= XOR_MAX ? skipcount : XOR_MAX);
    557                         dest[putp++] = 0x80;
    558                         dest[putp++] = (Byte)(count & 0xFF);
    559                         dest[putp++] = (Byte)((count >> 8) & 0xFF);
    560                     }
    561                     skipcount -= count;
    562                     getsp += count;
    563                     getbp += count;
    564                 }
    565             }
    566 
    567             //final skip command of 0 to signal end of stream.
    568             dest[putp++] = 0x80;
    569             dest[putp++] = 0;
    570             dest[putp++] = 0;
    571 
    572             Byte[] finalOutput = new Byte[putp];
    573             Array.Copy(dest, 0, finalOutput, 0, putp);
    574             // Return the final data
    575             return finalOutput;
    576         }
    577 
    578         /// <summary>
    579         /// Applies a binary delta to a buffer.
    580         /// </summary>
    581         /// <param name="data">The data to apply the xor to.</param>
    582         /// <param name="xorSource">The the delta data to apply.</param>
    583         /// <param name="xorStart">Start offset in the data.</param>
    584         /// <param name="xorEnd">End offset in the data. Use 0 to take the end of the whole array.</param>
    585         public static void ApplyXorDelta(Byte[] data, Byte[] xorSource, ref Int32 xorStart, Int32 xorEnd)
    586         {
    587             // Nyer's C# conversion: replacements for write and read for pointers.
    588             Int32 putp = 0;
    589             Byte value = 0;
    590             Int32 dataEnd = data.Length;
    591             if (xorEnd <= 0)
    592                 xorEnd = xorSource.Length;
    593             while (putp < dataEnd && xorStart < xorEnd)
    594             {
    595                 //DEBUG_SAY("XOR_Delta Put pos: %u, Get pos: %u.... ", putp - scast<sint8*>(dest), getp - scast<sint8*>(source));
    596                 Byte cmd = xorSource[xorStart++];
    597                 UInt16 count = cmd;
    598                 Boolean xorval = false;
    599 
    600                 if ((cmd & 0x80) == 0)
    601                 {
    602                     //0b00000000
    603                     if (cmd == 0)
    604                     {
    605                         if (xorStart >= xorEnd)
    606                             return;
    607                         count = (UInt16)(xorSource[xorStart++] & 0xFF);
    608                         if (xorStart >= xorEnd)
    609                             return;
    610                         value = xorSource[xorStart++];
    611                         xorval = true;
    612                         //DEBUG_SAY("0b00000000 Val Count %d ", count);
    613                         //0b0???????
    614                     }
    615                 }
    616                 else
    617                 {
    618                     //0b1??????? remove most significant bit
    619                     count &= 0x7F;
    620                     if (count != 0)
    621                     {
    622                         putp += count;
    623                         //DEBUG_SAY("0b1??????? Skip Count %d\n", count);
    624                         continue;
    625                     }
    626                     if (xorStart >= xorEnd)
    627                         return;
    628                     count = (UInt16) (xorSource[xorStart++] & 0xFF);
    629                     if (xorStart >= xorEnd)
    630                         return;
    631                     count += (UInt16) (xorSource[xorStart++] << 8);
    632 
    633                     //0b10000000 0 0
    634                     if (count == 0)
    635                     {
    636                         //DEBUG_SAY("0b10000000 Count %d to end delta\n", count);
    637                         return;
    638                     }
    639 
    640                     //0b100000000 0?
    641                     if ((count & 0x8000) == 0)
    642                     {
    643                         putp += count;
    644                         //DEBUG_SAY("0b100000000 0? Skip Count %d\n", count);
    645                         continue;
    646                     }
    647                     //0b10000000 11
    648                     if ((count & 0x4000) != 0)
    649                     {
    650                         count &= 0x3FFF;
    651                         if (xorStart >= xorEnd)
    652                             return;
    653                         value = xorSource[xorStart++];
    654                         //DEBUG_SAY("0b10000000 11 Val Count %d ", count);
    655                         xorval = true;
    656                         //0b10000000 10
    657                     }
    658                     else
    659                     {
    660                         count &= 0x3FFF;
    661                         //DEBUG_SAY("0b10000000 10 XOR Count %d ", count);
    662                     }
    663                 }
    664 
    665                 if (xorval)
    666                 {
    667                     //DEBUG_SAY("XOR Val %d\n", value);
    668                     for (; count > 0; --count)
    669                     {
    670                         if (putp >= dataEnd)
    671                             return; 
    672                         data[putp++] ^= value;
    673                     }
    674                 }
    675                 else
    676                 {
    677                     //DEBUG_SAY("XOR Source to Dest\n");
    678                     for (; count > 0; --count)
    679                     {
    680                         if (putp >= dataEnd || xorStart >= xorEnd)
    681                             return;
    682                         data[putp++] ^= xorSource[xorStart++];
    683                     }
    684                 }
    685             }
    686         }
    687 
    688     }
    689 }