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