lcw.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Command & Conquer *
  23. * *
  24. * $Archive:: /G/wwlib/lcw.cpp $*
  25. * *
  26. * $Author:: Neal_k $*
  27. * *
  28. * $Modtime:: 10/04/99 10:25a $*
  29. * *
  30. * $Revision:: 4 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * LCW_Comp -- Performes LCW compression on a block of data. *
  35. * LCW_Uncomp -- Decompress an LCW encoded data block. *
  36. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  37. #include "always.h"
  38. #include "lcw.h"
  39. /***************************************************************************
  40. * LCW_Uncomp -- Decompress an LCW encoded data block. *
  41. * *
  42. * Uncompress data to the following codes in the format b = byte, w = word *
  43. * n = byte code pulled from compressed data. *
  44. * *
  45. * Command code, n |Description *
  46. * ------------------------------------------------------------------------*
  47. * n=0xxxyyyy,yyyyyyyy |short copy back y bytes and run x+3 from dest *
  48. * n=10xxxxxx,n1,n2,...,nx+1|med length copy the next x+1 bytes from source*
  49. * n=11xxxxxx,w1 |med copy from dest x+3 bytes from offset w1 *
  50. * n=11111111,w1,w2 |long copy from dest w1 bytes from offset w2 *
  51. * n=11111110,w1,b1 |long run of byte b1 for w1 bytes *
  52. * n=10000000 |end of data reached *
  53. * *
  54. * *
  55. * INPUT: *
  56. * void * source ptr *
  57. * void * destination ptr *
  58. * unsigned long length of uncompressed data *
  59. * *
  60. * *
  61. * OUTPUT: *
  62. * unsigned long # of destination bytes written *
  63. * *
  64. * WARNINGS: *
  65. * 3rd argument is dummy. It exists to provide cross-platform *
  66. * compatibility. Note therefore that this implementation does not *
  67. * check for corrupt source data by testing the uncompressed length. *
  68. * *
  69. * HISTORY: *
  70. * 03/20/1995 IML : Created. *
  71. *=========================================================================*/
  72. int LCW_Uncomp(void const * source, void * dest, unsigned long )
  73. {
  74. unsigned char * source_ptr, * dest_ptr, * copy_ptr;
  75. unsigned char op_code, data;
  76. unsigned count;
  77. unsigned * word_dest_ptr;
  78. unsigned word_data;
  79. /* Copy the source and destination ptrs. */
  80. source_ptr = (unsigned char*) source;
  81. dest_ptr = (unsigned char*) dest;
  82. for (;;) {
  83. /* Read in the operation code. */
  84. op_code = *source_ptr++;
  85. if (!(op_code & 0x80)) {
  86. /* Do a short copy from destination. */
  87. count = (op_code >> 4) + 3;
  88. copy_ptr = dest_ptr - ((unsigned) *source_ptr++ + (((unsigned) op_code & 0x0f) << 8));
  89. while (count--) *dest_ptr++ = *copy_ptr++;
  90. } else {
  91. if (!(op_code & 0x40)) {
  92. if (op_code == 0x80) {
  93. /* Return # of destination bytes written. */
  94. return ((unsigned long) (dest_ptr - (unsigned char*) dest));
  95. } else {
  96. /* Do a medium copy from source. */
  97. count = op_code & 0x3f;
  98. while (count--) *dest_ptr++ = *source_ptr++;
  99. }
  100. } else {
  101. if (op_code == 0xfe) {
  102. /* Do a long run. */
  103. count = *source_ptr + ((unsigned) *(source_ptr + 1) << 8);
  104. word_data = data = *(source_ptr + 2);
  105. word_data = (word_data << 24) + (word_data << 16) + (word_data << 8) + word_data;
  106. source_ptr += 3;
  107. copy_ptr = dest_ptr + 4 - ((unsigned) dest_ptr & 0x3);
  108. count -= (copy_ptr - dest_ptr);
  109. while (dest_ptr < copy_ptr) *dest_ptr++ = data;
  110. word_dest_ptr = (unsigned*) dest_ptr;
  111. dest_ptr += (count & 0xfffffffc);
  112. while (word_dest_ptr < (unsigned*) dest_ptr) {
  113. *word_dest_ptr = word_data;
  114. *(word_dest_ptr + 1) = word_data;
  115. word_dest_ptr += 2;
  116. }
  117. copy_ptr = dest_ptr + (count & 0x3);
  118. while (dest_ptr < copy_ptr) *dest_ptr++ = data;
  119. } else {
  120. if (op_code == 0xff) {
  121. /* Do a long copy from destination. */
  122. count = *source_ptr + ((unsigned) *(source_ptr + 1) << 8);
  123. copy_ptr = (unsigned char*) dest + *(source_ptr + 2) + ((unsigned) *(source_ptr + 3) << 8);
  124. source_ptr += 4;
  125. while (count--) *dest_ptr++ = *copy_ptr++;
  126. } else {
  127. /* Do a medium copy from destination. */
  128. count = (op_code & 0x3f) + 3;
  129. copy_ptr = (unsigned char*) dest + *source_ptr + ((unsigned) *(source_ptr + 1) << 8);
  130. source_ptr += 2;
  131. while (count--) *dest_ptr++ = *copy_ptr++;
  132. }
  133. }
  134. }
  135. }
  136. }
  137. }
  138. #if defined(_MSC_VER)
  139. /***********************************************************************************************
  140. * LCW_Comp -- Performes LCW compression on a block of data. *
  141. * *
  142. * This routine will compress a block of data using the LCW compression method. LCW has *
  143. * the primary characteristic of very fast uncompression at the expense of very slow *
  144. * compression times. *
  145. * *
  146. * INPUT: source -- Pointer to the source data to compress. *
  147. * *
  148. * dest -- Pointer to the destination location to store the compressed data *
  149. * to. *
  150. * *
  151. * datasize -- The size (in bytes) of the source data to compress. *
  152. * *
  153. * OUTPUT: Returns with the number of bytes of output data stored into the destination *
  154. * buffer. *
  155. * *
  156. * WARNINGS: Be sure that the destination buffer is big enough. The maximum size required *
  157. * for the destination buffer is (datasize + datasize/128). *
  158. * *
  159. * HISTORY: *
  160. * 05/20/1997 JLB : Created. *
  161. *=============================================================================================*/
  162. /*ARGSUSED*/
  163. int LCW_Comp(void const * source, void * dest, int datasize)
  164. {
  165. int retval = 0;
  166. #ifdef _WINDOWS
  167. long inlen = 0;
  168. long a1stdest = 0;
  169. long a1stsrc = 0;
  170. long lenoff = 0;
  171. long ndest = 0;
  172. long count = 0;
  173. long matchoff = 0;
  174. long end_of_data =0;
  175. #ifdef _DEBUG
  176. inlen = inlen;
  177. a1stdest = a1stdest;
  178. a1stsrc = a1stsrc;
  179. lenoff = lenoff;
  180. ndest = ndest;
  181. count = count;
  182. matchoff = matchoff;
  183. end_of_data = end_of_data;
  184. #endif
  185. __asm {
  186. cld // make sure all string commands are forward
  187. mov edi,[dest]
  188. mov esi,[source]
  189. mov edx,[datasize] // get length of data to compress
  190. // compress data to the following codes in the format b = byte, w = word
  191. // n = byte code pulled from compressed data
  192. // Bit field of n command description
  193. // n=0xxxyyyy,yyyyyyyy short run back y bytes and run x+3
  194. // n=10xxxxxx,n1,n2,...,nx+1 med length copy the next x+1 bytes
  195. // n=11xxxxxx,w1 med run run x+3 bytes from offset w1
  196. // n=11111111,w1,w2 long run run w1 bytes from offset w2
  197. // n=10000000 end end of data reached
  198. mov ebx,esi
  199. add ebx,edx
  200. mov [end_of_data],ebx
  201. mov [inlen],1 //; set the in-length flag
  202. mov [a1stdest],edi //; save original dest offset for size calc
  203. mov [a1stsrc],esi //; save offset of first byte of data
  204. mov [lenoff],edi //; save the offset of the legth of this len
  205. sub eax,eax
  206. mov al,081h //; the first byte is always a len
  207. stosb //; write out a len of 1
  208. lodsb //; get the byte
  209. stosb //; save it
  210. }
  211. loopstart:
  212. __asm {
  213. mov [ndest],edi //; save offset of compressed data
  214. mov edi,[a1stsrc] //; get the offset to the first byte of data
  215. mov [count],1 //; set the count of run to 0
  216. }
  217. searchloop:
  218. __asm {
  219. sub eax,eax
  220. mov al,[esi] //; get the current byte of data
  221. cmp al,[esi+64]
  222. jne short notrunlength
  223. mov ebx,edi
  224. mov edi,esi
  225. mov ecx,[end_of_data]
  226. sub ecx,edi
  227. repe scasb
  228. dec edi
  229. mov ecx,edi
  230. sub ecx,esi
  231. cmp ecx,65
  232. jb short notlongenough
  233. mov [inlen],0 //; clear the in-length flag
  234. // mov [DWORD PTR inlen],0 //; clear the in-length flag
  235. mov esi,edi
  236. mov edi,[ndest] //; get the offset of our compressed data
  237. mov ah,al
  238. mov al,0FEh
  239. stosb
  240. xchg ecx,eax
  241. stosw
  242. mov al,ch
  243. stosb
  244. mov [ndest],edi //; save offset of compressed data
  245. mov edi,ebx
  246. jmp searchloop
  247. }
  248. notlongenough:
  249. __asm {
  250. mov edi,ebx
  251. }
  252. notrunlength:
  253. oploop:
  254. __asm {
  255. mov ecx,esi //; get the address of the last byte +1
  256. sub ecx,edi //; get the total number of bytes left to comp
  257. jz short searchdone
  258. repne scasb //; look for a match
  259. jne short searchdone //; if we don't find one we're done
  260. mov ebx,[count]
  261. mov ah,[esi+ebx-1]
  262. cmp ah,[edi+ebx-2]
  263. jne oploop
  264. mov edx,esi //; save this spot for the next search
  265. mov ebx,edi //; save this spot for the length calc
  266. dec edi //; back up one for compare
  267. mov ecx,[end_of_data] //; get the end of data
  268. sub ecx,esi //; sub current source for max len
  269. repe cmpsb //; see how many bytes match
  270. jne short notend //; if found mismatch then di - bx = match count
  271. inc edi //; else cx = 0 and di + 1 - bx = match count
  272. }
  273. notend:
  274. __asm {
  275. mov esi,edx //; restore si
  276. mov eax,edi //; get the dest
  277. sub eax,ebx //; sub the start for total bytes that match
  278. mov edi,ebx //; restore dest
  279. cmp eax,[count] //; see if its better than before
  280. jb searchloop //; if not keep looking
  281. mov [count],eax //; if so keep the count
  282. dec ebx //; back it up for the actual match offset
  283. mov [matchoff],ebx //; save the offset for later
  284. jmp searchloop //; loop until we searched it all
  285. }
  286. searchdone:
  287. __asm {
  288. mov ecx,[count] //; get the count of the longest run
  289. mov edi,[ndest] //; get the offset of our compressed data
  290. cmp ecx,2 //; see if its not enough run to matter
  291. jbe short lenin //; if its 0,1, or 2 its too small
  292. cmp ecx,10 //; if not, see if it would fit in a short
  293. ja short medrun //; if not, see if its a medium run
  294. mov eax,esi //; if its short get the current address
  295. sub eax,[matchoff] //; sub the offset of the match
  296. cmp eax,0FFFh //; if its less than 12 bits its a short
  297. ja short medrun //; if its not, its a medium
  298. }
  299. //shortrun:
  300. __asm {
  301. sub ebx,ebx
  302. mov bl,cl //; get the length (3-10)
  303. sub bl,3 //; sub 3 for a 3 bit number 0-7
  304. shl bl,4 //; shift it left 4
  305. add ah,bl //; add in the length for the high nibble
  306. xchg ah,al //; reverse the bytes for a word store
  307. jmp short srunnxt //; do the run fixup code
  308. }
  309. medrun:
  310. __asm {
  311. cmp ecx,64 //; see if its a short run
  312. ja short longrun //; if not, oh well at least its long
  313. sub cl,3 //; back down 3 to keep it in 6 bits
  314. or cl,0C0h //; the highest bits are always on
  315. mov al,cl //; put it in al for the stosb
  316. stosb //; store it
  317. jmp short medrunnxt //; do the run fixup code
  318. }
  319. lenin:
  320. __asm {
  321. cmp [inlen],0 //; is it doing a length?
  322. // cmp [DWORD PTR inlen],0 //; is it doing a length?
  323. jnz short len //; if so, skip code
  324. }
  325. lenin1:
  326. __asm {
  327. mov [lenoff],edi //; save the length code offset
  328. mov al,80h //; set the length to 0
  329. stosb //; save it
  330. }
  331. len:
  332. __asm {
  333. mov ebx,[lenoff] //; get the offset of the length code
  334. cmp [ebx],0BFh //; see if its maxed out
  335. // cmp [BYTE PTR ebx],0BFh //; see if its maxed out
  336. je lenin1 //; if so put out a new len code
  337. }
  338. //stolen:
  339. __asm {
  340. inc [ebx] //; inc the count code
  341. // inc [BYTE PTR ebx] //; inc the count code
  342. lodsb //; get the byte
  343. stosb //; store it
  344. mov [inlen],1 //; we are now in a length so save it
  345. // mov [DWORD PTR inlen],1 //; we are now in a length so save it
  346. jmp short nxt //; do the next code
  347. }
  348. longrun:
  349. __asm {
  350. mov al,0ffh //; its a long so set a code of FF
  351. stosb //; store it
  352. mov eax,[count] //; send out the count
  353. stosw //; store it
  354. }
  355. medrunnxt:
  356. __asm {
  357. mov eax,[matchoff] //; get the offset
  358. sub eax,[a1stsrc] //; make it relative tot he start of data
  359. }
  360. srunnxt:
  361. __asm {
  362. stosw //; store it
  363. //; this code common to all runs
  364. add esi,[count] //; add in the length of the run to the source
  365. mov [inlen],0 //; set the in leght flag to false
  366. // mov [DWORD PTR inlen],0 //; set the in leght flag to false
  367. }
  368. nxt:
  369. __asm {
  370. cmp esi,[end_of_data] //; see if we did the whole pic
  371. jae short outofhere //; if so, cool! were done
  372. jmp loopstart
  373. }
  374. outofhere:
  375. __asm {
  376. mov ax,080h //; remember to send an end of data code
  377. stosb //; store it
  378. mov eax,edi //; get the last compressed address
  379. sub eax,[a1stdest] //; sub the first for the compressed size
  380. mov [retval],eax
  381. }
  382. #endif
  383. return(retval);
  384. }
  385. #endif