1 /***************************************************************************
2 RlePack.cpp - description
3 -------------------
4 begin : Mon Sep 24 2001
5 copyright : (C) 2001 by upi
6 email : upi@apocalypse.rulez.org
7 ***************************************************************************/
8
9 #include "RlePack.h"
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include "SDL.h"
16 #include "gfx.h"
17 #include "common.h"
18
19
20 /// Sanity: This is the maximal number of entries in a .DAT file.
21 #define MAXDATACOUNT 65530
22
23
ChangeEndian32(Uint32 & a_riArg)24 inline void ChangeEndian32( Uint32& a_riArg )
25 {
26 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
27 char* pcArg = (char*)&a_riArg;
28 char cTemp;
29 cTemp = pcArg[0]; pcArg[0] = pcArg[3]; pcArg[3] = cTemp;
30 cTemp = pcArg[1]; pcArg[1] = pcArg[2]; pcArg[2] = cTemp;
31 #endif
32 }
33
34
ChangeEndian16(Uint16 & a_riArg)35 inline void ChangeEndian16( Uint16& a_riArg )
36 {
37 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
38 char* pcArg = (char*)&a_riArg;
39 char cTemp;
40 cTemp = pcArg[0]; pcArg[0] = pcArg[1]; pcArg[1] = cTemp;
41 #endif
42 }
43
44
ConvertEndian32(Uint32 a_iArg)45 inline Uint32 ConvertEndian32( Uint32 a_iArg )
46 {
47 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
48 char* pcArg = (char*)&a_iArg;
49 char cTemp;
50 cTemp = pcArg[0]; pcArg[0] = pcArg[3]; pcArg[3] = cTemp;
51 cTemp = pcArg[1]; pcArg[1] = pcArg[2]; pcArg[2] = cTemp;
52 #endif
53 return a_iArg;
54 }
55
56
ConvertEndian16(Uint16 a_iArg)57 inline Uint16 ConvertEndian16( Uint16 a_iArg )
58 {
59 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
60 char* pcArg = (char*)&a_iArg;
61 char cTemp;
62 cTemp = pcArg[0]; pcArg[0] = pcArg[1]; pcArg[1] = cTemp;
63 #endif
64 return a_iArg;
65 }
66
67
68
69 typedef struct RLE_SPRITE /* a RLE compressed sprite */
70 {
71 Uint16 dummy; // For better alignment... NASTY NASTY HACK!!
72 Uint16 color_depth; /* color depth of the image */
73 Uint16 w, h; /* width and height in pixels */
74 Uint32 size;
75 signed char dat[0];
76 } RLE_SPRITE;
77
78
79 struct RlePack_P
80 {
81 SDL_Color m_aoPalette[256];
82 SDL_Color m_aoTintedPalette[256];
83 TintEnum m_enTint;
84 int m_iCount;
85 int m_iArraysize;
86 RLE_SPRITE** m_pSprites;
87 void* m_pData;
88
89 int m_iColorCount;
90 int m_iColorOffset;
91 Uint32 m_aiRGBPalette[256];
92
93 void draw_rle_sprite8( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
94 void draw_rle_sprite_v_flip8( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
95 void draw_rle_sprite16( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
96 void draw_rle_sprite_v_flip16( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
97 void draw_rle_sprite24( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
98 void draw_rle_sprite_v_flip24( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
99 void draw_rle_sprite32( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
100 void draw_rle_sprite_v_flip32( RLE_SPRITE* a_poSprite, int a_dx, int a_dy );
101 };
102
103
104
RlePack(const char * a_pcFilename,int a_iNumColors)105 RlePack::RlePack( const char* a_pcFilename, int a_iNumColors )
106 {
107 p = new RlePack_P;
108 p->m_enTint = NO_TINT;
109 p->m_iCount = 0;
110 p->m_iArraysize = 0;
111 p->m_pSprites = NULL;
112 p->m_pData = NULL;
113
114 p->m_iColorCount = 0;
115 p->m_iColorOffset = 0;
116
117 // Load file and stuff
118
119 LoadFile( a_pcFilename, a_iNumColors );
120 }
121
122
~RlePack()123 RlePack::~RlePack()
124 {
125 if (!p)
126 return;
127
128 if (p->m_pSprites)
129 {
130 delete[] p->m_pSprites;
131 p->m_pSprites = NULL;
132 }
133
134 free( p->m_pData );
135 delete( p );
136 p = NULL;
137 }
138
139
Clear()140 void RlePack::Clear()
141 {
142 if ( p && p->m_pSprites )
143 {
144 delete[] p->m_pSprites;
145 p->m_pSprites = NULL;
146 }
147 }
148
149
LoadFile(const char * a_pcFilename,int a_iNumColors)150 int RlePack::LoadFile( const char* a_pcFilename, int a_iNumColors )
151 {
152 FILE* f;
153
154 f = fopen( a_pcFilename, "rb" );
155 if (f==NULL)
156 {
157 debug( "Can't open file '%s'.\n", a_pcFilename );
158 return -1;
159 }
160
161 fseek( f, 0, SEEK_END );
162 long iFileSize = ftell ( f );
163 p->m_pData = malloc( iFileSize );
164 if ( NULL == p->m_pData )
165 {
166 fclose( f );
167 return -1;
168 }
169
170 fseek( f, 0, SEEK_SET );
171 int iRead = fread( p->m_pData, 1, iFileSize, f );
172 fclose( f );
173
174 p->m_iColorCount = a_iNumColors;
175
176 if ( iFileSize != iRead )
177 {
178 debug( "Warning RlePack(): iFileSize=%d, iRead=%d\n", iFileSize, iRead );
179 }
180
181 struct SHeader
182 {
183 char acDummy[8];
184 Uint32 iDatacount;
185 } *poHeader = (SHeader*) p->m_pData;
186
187 ChangeEndian32( poHeader->iDatacount );
188 debug( "File '%s' contains %d entries.\n", a_pcFilename, poHeader->iDatacount );
189
190 if (poHeader->iDatacount>MAXDATACOUNT) poHeader->iDatacount = MAXDATACOUNT; // Sanity
191
192 p->m_iArraysize = poHeader->iDatacount;
193 p->m_pSprites = new RLE_SPRITE*[ poHeader->iDatacount ];
194
195 char* pcNext = ((char*)p->m_pData) + sizeof(SHeader);
196 char* pcEnd = ((char*)p->m_pData) + iFileSize;
197
198 while ( pcNext < pcEnd - 4 )
199 {
200 if ( 0 == strncmp( pcNext, "prop", 4 ) )
201 {
202 struct SProperty
203 {
204 char acName[4];
205 Uint32 iSize;
206 } *poProperty = (SProperty*) (pcNext+4);
207 ChangeEndian32( poProperty->iSize );
208
209 pcNext += 4 + sizeof(SProperty) + poProperty->iSize;
210 }
211 else if ( 0 == strncmp( pcNext, "RLE ", 4 ) )
212 {
213 struct SRLE
214 {
215 RLE_SPRITE oSprite;
216 } *poRle = (SRLE*) (pcNext+10);
217 poRle->oSprite.color_depth = ConvertEndian16(poRle->oSprite.color_depth);
218 poRle->oSprite.w = ConvertEndian16(poRle->oSprite.w);
219 poRle->oSprite.h = ConvertEndian16(poRle->oSprite.h);
220 poRle->oSprite.size = ConvertEndian32(poRle->oSprite.size);
221
222 p->m_pSprites[p->m_iCount] = &(poRle->oSprite);
223 p->m_iCount++;
224 pcNext += 10 + sizeof( SRLE ) + poRle->oSprite.size;
225 }
226 else if ( 0 == strncmp( pcNext, "PAL ", 4 ) )
227 {
228 struct SPAL
229 {
230 Uint32 iLength1;
231 Uint32 iLength;
232 SDL_Color aoColors[256];
233 } *poPal = (SPAL*) (pcNext+4);
234 ChangeEndian32( poPal->iLength );
235
236 int iNumColors = poPal->iLength>1024 ? 1024 : poPal->iLength;
237 iNumColors /= 4;
238
239 for (int i=0; i< iNumColors; i++)
240 {
241 p->m_aoPalette[i].r = poPal->aoColors[i].r*4;
242 p->m_aoPalette[i].g = poPal->aoColors[i].g*4;
243 p->m_aoPalette[i].b = poPal->aoColors[i].b*4;
244 p->m_aoPalette[i].unused = 0;
245 p->m_aoTintedPalette[i] = p->m_aoPalette[i];
246 }
247
248 pcNext += 4 + 8 + poPal->iLength;
249 }
250 else
251 {
252 struct SUnknown
253 {
254 Uint32 iSize;
255 } *poUnknown = (SUnknown*) (pcNext+4);
256 ChangeEndian32( poUnknown->iSize );
257
258 debug( "Unknown: '%4s', size: %d\n", pcNext, poUnknown->iSize );
259
260 pcNext += 4 + sizeof(SUnknown) + poUnknown->iSize;
261 }
262 }
263
264 return p->m_iCount;
265
266 #if 0
267
268 int datacount;
269
270 #define READDW(I) { \
271 unsigned char data[4]; \
272 fread( data, 4, 1, f ); \
273 (I) = (data[0]<<24) + (data[1]<<16) + (data[2]<<8) + data[3]; }
274 #define READW(I) { \
275 unsigned char data[2]; \
276 fread( data, 2, 1, f ); \
277 (I) = (data[0]<<8) + data[1]; }
278 #define READCH(S,C) { \
279 fread( S, C, 1, f ); S[C] = 0; }
280
281 fseek( f, 8, SEEK_SET ); // Skip header
282 READDW( datacount );
283
284 debug( "File '%s' contains %d entries.\n", filename, datacount );
285 if (datacount>500) datacount = 500; // Sanity
286
287 p->arraysize = datacount;
288 p->sprites = new RLE_SPRITE*[ datacount ];
289
290 while( (!feof(f)) && (!ferror(f)) && (datacount>0) )
291 {
292 char s[10];
293 READCH( s, 4 );
294 if ( !strcmp( s, "prop" )) // Found a property
295 {
296 fseek( f, 4, SEEK_CUR );
297 unsigned int propsize;
298 READDW( propsize );
299 fseek( f, propsize, SEEK_CUR );
300 }
301 else if (!strcmp( s, "RLE " )) // Found an RLE_SPRITE
302 {
303 datacount--;
304
305 unsigned int length, bpp, width, height, size;
306
307 READDW( length );
308 READDW( length );
309 READW( bpp );
310 READW( width );
311 READW( height );
312 READDW( size );
313
314 RLE_SPRITE* sprite = (RLE_SPRITE*) malloc( sizeof(RLE_SPRITE) + size );
315 p->sprites[ p->count ] = sprite;
316 (p->count)++;
317 sprite->w = width;
318 sprite->h = height;
319 sprite->color_depth = bpp;
320 sprite->size = size;
321 fread( sprite->dat, 1, size, f );
322 }
323 else if (!strcmp( s, "PAL ")) // Found a palette
324 {
325 datacount--;
326
327 unsigned int length, pallength;
328 READDW( length );
329 READDW( length );
330 pallength = length>1024 ? 1024 : length;
331 pallength /= 4;
332
333 for (unsigned int i=0; i< pallength; i++)
334 {
335 char c[4];
336 fread( c, 4, 1, f );
337 p->palette[i].r = c[0]*4;
338 p->palette[i].g = c[1]*4;
339 p->palette[i].b = c[2]*4;
340 p->palette[i].unused = 0;
341 }
342
343 fseek( f, length - pallength*4, SEEK_CUR );
344 }
345 else // Found something else
346 {
347 debug( "Unknown: %s.", s );
348 datacount--;
349
350 unsigned int length;
351 READDW( length );
352 READDW( length );
353 fseek( f, length, SEEK_CUR );
354 }
355 }
356
357 fclose( f );
358 #endif
359 }
360
361
362
363
Count()364 int RlePack::Count()
365 {
366 return p->m_iCount;
367 }
368
369
370 /** Worker method of RlePack::OffsetSprites() .*/
371
OffsetRLESprite(RLE_SPRITE * spr,int offset)372 void OffsetRLESprite( RLE_SPRITE* spr, int offset ) // Static method
373 {
374 if (!spr || !offset) return;
375
376 signed char *s = spr->dat;
377 signed char c;
378 int y;
379
380 for (y=0; y<spr->h; y++)
381 {
382 c = *s++;
383
384 while (c)
385 {
386 // For positive c: solid pixels.
387 for ( ; c>0; c-- )
388 {
389 *s = (*s) + offset;
390 s++;
391 }
392 c = *s++;
393 }
394 }
395 }
396
397
398 /** Offsets the sprites "logical" palette values by the given offset.
399 This is only relevant in 8BPP mode; in other color depths this is a
400 no-op.
401
402 Explanation: RlePacks have an internal palette which contains up to 256
403 colors. These colors are always indexed from 0 up. However, if you load
404 two RlePacks with different palettes, the palettes will collide, and one
405 RlePack will be displayed with an incorrect palette.
406
407 To work around this, you can offset one of the sprites palette. For example,
408 you might load an RlePack with 16 colors, and another with 64 colors. You
409 can offset the second RlePack by 16 colors; the total effect is that the
410 two RlePacks now use 80 colors of the available 256 colors, the first using
411 colors 0-15, the second using colors 16-79.
412 */
413
OffsetSprites(int a_iOffset)414 void RlePack::OffsetSprites( int a_iOffset )
415 {
416 if ( (a_iOffset<=0) || (a_iOffset>255) || (8!=gamescreen->format->BitsPerPixel) )
417 return;
418
419 p->m_iColorOffset = a_iOffset;
420
421 int i;
422
423 // Offset every RLE_SPRITE
424
425 for ( i=0; i<p->m_iCount; ++i )
426 {
427 OffsetRLESprite( p->m_pSprites[i], a_iOffset );
428 }
429 }
430
431
SetTint(TintEnum a_enTint)432 void RlePack::SetTint( TintEnum a_enTint )
433 {
434 int i;
435
436 switch( a_enTint )
437 {
438 case ZOMBIE_TINT:
439 {
440 for ( i=0; i<p->m_iColorCount; ++i )
441 {
442 p->m_aoTintedPalette[i].r = 0;
443 p->m_aoTintedPalette[i].g = p->m_aoPalette[i].g;
444 p->m_aoTintedPalette[i].b = 0;
445 }
446 break;
447 }
448
449 case GRAY_TINT:
450 {
451 int j;
452 for ( i=0; i<p->m_iColorCount; ++i )
453 {
454 j = (p->m_aoPalette[i].r + p->m_aoPalette[i].g + p->m_aoPalette[i].b)/4;
455 p->m_aoTintedPalette[i].r = j;
456 p->m_aoTintedPalette[i].g = j;
457 p->m_aoTintedPalette[i].b = j;
458 }
459 break;
460 }
461
462 case DARK_TINT:
463 {
464 for ( i=0; i<p->m_iColorCount; ++i )
465 {
466 p->m_aoTintedPalette[i].r = int(p->m_aoPalette[i].r) * 2 / 3;
467 p->m_aoTintedPalette[i].g = int(p->m_aoPalette[i].g) * 2 / 3;
468 p->m_aoTintedPalette[i].b = int(p->m_aoPalette[i].b) * 2 / 3;
469 }
470 break;
471 }
472
473 case INVERTED_TINT:
474 {
475 for ( i=0; i<p->m_iColorCount; ++i )
476 {
477 p->m_aoTintedPalette[i].r = 255 - p->m_aoPalette[i].r;
478 p->m_aoTintedPalette[i].g = 255 - p->m_aoPalette[i].g;
479 p->m_aoTintedPalette[i].b = 255 - p->m_aoPalette[i].b;
480 }
481 break;
482 }
483
484 case NO_TINT:
485 default:
486 {
487 for ( i=0; i<p->m_iColorCount; ++i )
488 {
489 p->m_aoTintedPalette[i] = p->m_aoPalette[i];
490 }
491 break;
492 }
493
494 } // end of switch( a_enTint )
495
496 }
497
498
499 /** Loads the palette of the RlePack to the gamescreen.
500 This only works in 8BPP mode; in other modes the palette is always considered
501 to be loaded, and this is a no-operation.
502 */
503
ApplyPalette()504 void RlePack::ApplyPalette()
505 {
506 if ( 8 == gamescreen->format->BitsPerPixel )
507 {
508 SDL_SetColors( gamescreen, p->m_aoTintedPalette, p->m_iColorOffset, p->m_iColorCount );
509 }
510 else
511 {
512 // Now is the time to compile m_aiRGBPalette
513 for ( int i=0; i<p->m_iColorCount; ++i )
514 {
515 SDL_Color& roColor = p->m_aoTintedPalette[i];
516 p->m_aiRGBPalette[i] = SDL_MapRGB( gamescreen->format, roColor.r, roColor.g, roColor.b );
517 }
518 }
519 }
520
521
522 /** Returns the width of a given sprite in pixels.
523
524 \param a_iIndex The index of the sprite, 0 <= a_iIndex < Count()
525 */
526
GetWidth(int a_iIndex)527 int RlePack::GetWidth( int a_iIndex )
528 {
529 if ( (a_iIndex<0) || (a_iIndex>=p->m_iCount) )
530 return -1;
531
532 RLE_SPRITE* poSprite = p->m_pSprites[a_iIndex];
533 if (!poSprite)
534 return -1;
535
536 return poSprite->w;
537 }
538
539
540 /** Returns the height of a given sprite in pixels.
541
542 \param a_iIndex The index of the sprite, 0 <= a_iIndex < Count()
543 */
544
GetHeight(int a_iIndex)545 int RlePack::GetHeight( int a_iIndex )
546 {
547 if ( (a_iIndex<0) || (a_iIndex>=p->m_iCount) )
548 return -1;
549
550 RLE_SPRITE* poSprite = p->m_pSprites[a_iIndex];
551 if (!poSprite)
552 return -1;
553
554 return poSprite->h;
555 }
556
557
558
559 #define METHODNAME RlePack_P::draw_rle_sprite8
560 #define METHODNAME_FLIP RlePack_P::draw_rle_sprite_v_flip8
561 #define PIXEL_PTR unsigned char*
562 #define PUT_PIXEL(p,c) (*((unsigned char *)(p)) = (c))
563 #define PITCH (dst->pitch)
564 #include "DrawRle.h"
565 #undef METHODNAME
566 #undef METHODNAME_FLIP
567 #undef PIXEL_PTR
568 #undef PUT_PIXEL
569 #undef PITCH
570
571 #define METHODNAME RlePack_P::draw_rle_sprite16
572 #define METHODNAME_FLIP RlePack_P::draw_rle_sprite_v_flip16
573 #define PIXEL_PTR Uint16*
574 #define PUT_PIXEL(p,c) (*((PIXEL_PTR )(p)) = (m_aiRGBPalette[c]))
575 #define PITCH (dst->pitch / 2)
576 #include "DrawRle.h"
577
578 #undef METHODNAME
579 #undef METHODNAME_FLIP
580 #undef PIXEL_PTR
581 #undef PUT_PIXEL
582 #undef PITCH
583
584 #define METHODNAME RlePack_P::draw_rle_sprite32
585 #define METHODNAME_FLIP RlePack_P::draw_rle_sprite_v_flip32
586 #define PIXEL_PTR Uint32*
587 #define PUT_PIXEL(p,c) (*((PIXEL_PTR )(p)) = (m_aiRGBPalette[c]))
588 #define PITCH (dst->pitch / 4)
589 #include "DrawRle.h"
590
591
592
593
Draw(int a_iIndex,int a_iX,int a_iY,bool a_bFlipped)594 void RlePack::Draw( int a_iIndex, int a_iX, int a_iY, bool a_bFlipped )
595 {
596 if ( (a_iIndex<0) || (a_iIndex>=p->m_iCount) )
597 return;
598
599 RLE_SPRITE* poSprite = p->m_pSprites[a_iIndex];
600 if (!poSprite)
601 return;
602
603 CSurfaceLocker oLock;
604
605 if ( a_bFlipped )
606 {
607 switch (gamescreen->format->BitsPerPixel)
608 {
609 case 8:
610 p->draw_rle_sprite_v_flip8( poSprite, a_iX, a_iY ); break;
611 case 15:
612 case 16:
613 p->draw_rle_sprite_v_flip16( poSprite, a_iX, a_iY ); break;
614 case 32:
615 p->draw_rle_sprite_v_flip32( poSprite, a_iX, a_iY ); break;
616 }
617 }
618 else
619 {
620 switch (gamescreen->format->BitsPerPixel)
621 {
622 case 8:
623 p->draw_rle_sprite8( poSprite, a_iX, a_iY ); break;
624 case 15:
625 case 16:
626 p->draw_rle_sprite16( poSprite, a_iX, a_iY ); break;
627 case 32:
628 p->draw_rle_sprite32( poSprite, a_iX, a_iY ); break;
629 }
630 }
631 }
632
633
CreateSurface(int a_iIndex,bool a_bFlipped)634 SDL_Surface* RlePack::CreateSurface( int a_iIndex, bool a_bFlipped )
635 {
636 if ( (a_iIndex<0) || (a_iIndex>=p->m_iCount) )
637 return NULL;
638
639 RLE_SPRITE* poSprite = p->m_pSprites[a_iIndex];
640 if (!poSprite)
641 return NULL;
642
643 SDL_Surface* poSurface = SDL_CreateRGBSurface( SDL_SWSURFACE, poSprite->w, poSprite->h, gamescreen->format->BitsPerPixel,
644 gamescreen->format->Rmask, gamescreen->format->Gmask, gamescreen->format->Bmask, gamescreen->format->Amask );
645
646 if ( NULL == poSurface )
647 {
648 return NULL;
649 }
650
651 if ( gamescreen->format->BitsPerPixel <= 8 )
652 {
653 SDL_SetColors( poSurface, gamescreen->format->palette->colors, 0, gamescreen->format->palette->ncolors );
654 }
655
656 SDL_FillRect( poSurface, NULL, 0 );// C_LIGHTGREEN );
657 SDL_SetColorKey( poSurface, SDL_SRCCOLORKEY, 0 ); //C_LIGHTGREEN );
658
659 SDL_Surface* poTemp = gamescreen;
660 gamescreen = poSurface;
661 Draw( a_iIndex, 0, 0, a_bFlipped );
662 gamescreen = poTemp;
663
664 return poSurface;
665 }
666