WWCompression.cs 30 KB

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