1 /*
2  *  MapLoader.cpp
3  *  OpenLieroX
4  *
5  *  Created by Albert Zeyer on 03.05.09.
6  *  code under LGPL
7  *
8  */
9 
10 #include "MapLoader.h"
11 #include "PreInitVar.h"
12 #include "Cache.h"
13 #include "CMap.h"
14 #include "EndianSwap.h"
15 #ifndef DEDICATED_ONLY
16 #include <gd.h>
17 #endif
18 #include <zlib.h>
19 #include "FindFile.h"
20 #include "FileUtils.h"
21 #include "SafeVector.h"
22 #include "ConfigHandler.h"
23 
24 
25 class ML_OrigLiero : public MapLoader {
26 public:
27 	static const long Width = 504, Height = 350;
28 	PIVar(bool,false) Powerlevel;
29 
format()30 	std::string format() { return "Original Liero"; }
31 
parseHeader(bool printErrors)32 	bool parseHeader(bool printErrors) {
33 		// Validate the liero level
34 		fseek(fp,0,SEEK_END);
35 		size_t length = ftell(fp);
36 
37 		// check for powerlevel
38 		if(length != 176400 && length != 176402) {
39 			if(length == 177178)
40 				Powerlevel = true;
41 			else {
42 				// bad file
43 				if(printErrors) errors << "OrigLiero loader: bad file: " << filename << endl;
44 				return false;
45 			}
46 		}
47 
48 		head.name = GetBaseFilename(filename);
49 		head.width = Width;
50 		head.height = Height;
51 
52 		return true;
53 	}
54 
parseData(CMap * m)55 	bool parseData(CMap* m) {
56 		fseek(fp,0,SEEK_SET);
57 
58 		// Default is a dirt theme for the background & dirtballs
59 		if( !m->New(504,350,"dirt") ) {
60 			return false;
61 		}
62 
63 		// Image type of map
64 		m->Type = MPT_IMAGE;
65 
66 		uchar *palette = new uchar[768];
67 		if( palette == NULL) {
68 			errors << "CMap::LoadOriginal: ERROR: not enough memory for palette" << endl;
69 			return false;
70 		}
71 
72 		// Load the palette
73 		if(!Powerlevel) {
74 			FILE *fpal = OpenGameFile("data/lieropal.act","rb");
75 			if(!fpal) {
76 				return false;
77 			}
78 
79 			if(fread(palette,sizeof(uchar),768,fpal) < 768) {
80 				return false;
81 			}
82 			fclose(fpal);
83 		}
84 
85 		// Load the image map
86 	imageMapCreate:
87 		uchar *bytearr = new uchar[Width*Height];
88 		if(bytearr == NULL) {
89 			errors << "CMap::LoadOriginal: ERROR: not enough memory for bytearr" << endl;
90 			if(cCache.GetEntryCount() > 0) {
91 				hints << "current cache size is " << cCache.GetCacheSize() << ", we are clearing it now" << endl;
92 				cCache.Clear();
93 				goto imageMapCreate;
94 			}
95 			delete[] palette;
96 			return false;
97 		}
98 
99 		if(fread(bytearr,sizeof(uchar),Width*Height,fp) < Width*Height) {
100 			errors << "CMap::LoadOriginal: cannot read file" << endl;
101 			return false;
102 		}
103 
104 		// Load the palette from the same file if it's a powerlevel
105 		if(Powerlevel) {
106 			std::string id;
107 			// Load id
108 			fread_fixedwidthstr<10>(id,fp);
109 			if(!stringcaseequal(id,"POWERLEVEL")) {
110 				delete[] palette;
111 				delete[] bytearr;
112 				return false;
113 			}
114 
115 			// Load the palette
116 			if(fread(palette,sizeof(uchar),768,fp) < 768) {
117 				return false;
118 			}
119 
120 			// Convert the 6bit colours to 8bit colours
121 			for(short n=0;n<768;n++) {
122 				float f = (float)palette[n] / 63.0f * 255.0f;
123 				palette[n] = (int)f;
124 			}
125 		}
126 
127 		// Set the image
128 		LOCK_OR_FAIL(m->bmpBackImage);
129 		LOCK_OR_FAIL(m->bmpImage);
130 		m->lockFlags();
131 		uint n=0;
132 		for(long y=0;y<Height;y++) {
133 			for(long x=0;x<Width;x++) {
134 				uchar p = bytearr[n];
135 				uchar type = PX_EMPTY;
136 				//if(p >= 0 && p <= 255) {
137 
138 				// Dirt
139 				if( (p >= 12 && p <= 18) ||
140 				   (p >= 55 && p <= 58) ||
141 				   (p >= 82 && p <= 84) ||
142 				   (p >= 94 && p <= 103) ||
143 				   (p >= 120 && p <= 123) ||
144 				   (p >= 176 && p <= 180))
145 					type = PX_DIRT;
146 
147 				// Rock
148 				else if( (p >= 19 && p <= 29) ||
149 						(p >= 59 && p <= 61) ||
150 						(p >= 85 && p <= 87) ||
151 						(p >= 91 && p <= 93) ||
152 						(p >= 123 && p <= 125) ||
153 						p==104)
154 					type = PX_ROCK;
155 
156 				PutPixel(m->bmpImage.get(),x,y, MakeColour(palette[p*3], palette[p*3+1], palette[p*3+2]));
157 				if(type == PX_EMPTY)
158 					PutPixel(m->bmpBackImage.get(),x,y, MakeColour(palette[p*3], palette[p*3+1], palette[p*3+2]));
159 				m->SetPixelFlag(x,y,type);
160 				//}
161 				n++;
162 			}
163 		}
164 		m->unlockFlags();
165 		UnlockSurface(m->bmpImage);
166 		UnlockSurface(m->bmpBackImage);
167 
168 		delete[] palette;
169 		delete[] bytearr;
170 
171 		return true;
172 	}
173 
174 };
175 
176 
177 class ML_LieroX : public MapLoader {
178 
179 	std::string id;
180 	PIVar(int,0) Type;
181 	std::string Theme_Name;
182 	PIVar(int,0) numobj;
183 	PIVar(bool,false) ctf;
format()184 	std::string format() { return id; }
185 
parseHeader(bool printErrors)186 	bool parseHeader(bool printErrors) {
187 		// Header
188 		id = freadfixedcstr(fp, 32);
189 		int	version = 0;
190 		fread_endian<int>(fp, version);
191 
192 		// Check to make sure it's a valid level file
193 		if((id != "LieroX Level" && id != "LieroX CTF Level") || version != MAP_VERSION) {
194 			if(printErrors) errors << "CMap::Load: " << filename << " is not a valid level file (" << id << ") or wrong version (" << version << ")" << endl;
195 			return false;
196 		}
197 
198 		// CTF map?
199 		ctf = (id == "LieroX CTF Level"); // TODO: there's no CTF maps around, and it was a hack, remove it
200 
201 		head.name = freadfixedcstr(fp, 64);
202 		fread_endian<int>(fp, head.width);
203 		fread_endian<int>(fp, head.height);
204 		fread_endian<int>(fp, (int&)Type);
205 		Theme_Name = freadfixedcstr(fp, 32);
206 		fread_endian<int>(fp, (int&)numobj);
207 
208 		return true;
209 	}
210 
211 
212 	///////////////////
213 	// Load the image format
LoadImageFormat(CMap * m)214 	bool LoadImageFormat(CMap* m)
215 	{
216 		// Load the details
217 		Uint32 size = 0, destsize = 0;
218 
219 		fread_compat(size, sizeof(Uint32), 1, fp);
220 		EndianSwap(size);
221 		fread_compat(destsize, sizeof(Uint32), 1, fp);
222 		EndianSwap(destsize);
223 
224 		// Allocate the memory
225 		uchar *pSource = new uchar[size];
226 		uchar *pDest = new uchar[destsize];
227 
228 		if(!pSource || !pDest) {
229 			errors << "CMap::LoadImageFormat: not enough memory" << endl;
230 			return false;
231 		}
232 
233 		if(fread(pSource, sizeof(uchar), size, fp) < size) {
234 			errors << "CMap::LoadImageFormat: cannot read data" << endl;
235 			return false;
236 		}
237 
238 		ulong lng_dsize = destsize;
239 		if( uncompress( pDest, &lng_dsize, pSource, size ) != Z_OK ) {
240 			errors("Failed decompression\n");
241 			delete[] pSource;
242 			delete[] pDest;
243 			return false;
244 		}
245 		destsize = lng_dsize;
246 		if( destsize < Uint32(head.width * head.height * 3 * 2) )
247 		{
248 			errors("CMap::LoadImageFormat(): image too small for Width*Height");
249 			delete[] pSource;
250 			delete[] pDest;
251 			return false;
252 		}
253 
254 		delete[] pSource;  // not needed anymore
255 
256 		//
257 		// Translate the data
258 		//
259 
260 		// Lock surfaces
261 		LOCK_OR_FAIL(m->bmpBackImage);
262 		LOCK_OR_FAIL(m->bmpImage);
263 
264 		Uint64 p=0;
265 		Uint32 curcolor=0;
266 		Uint8* curpixel = (Uint8*)m->bmpBackImage.get()->pixels;
267 		Uint8* PixelRow = curpixel;
268 
269 		Uint8 bpp = m->bmpImage.get()->format->BytesPerPixel;
270 		// Load the back image
271 		for (Sint64 y = 0; y < head.height; y++, PixelRow += m->bmpBackImage.get()->pitch)  {
272 			curpixel = PixelRow;
273 			for (Sint64 x = 0; x < head.width; x++, curpixel += bpp)  {
274 				curcolor = MakeColour(pDest[p], pDest[p+1], pDest[p+2]);
275 				p += 3;
276 				PutPixelToAddr(curpixel, curcolor, bpp);
277 			}
278 		}
279 
280 		// Load the front image
281 		curpixel = (Uint8 *)m->bmpImage.get()->pixels;
282 		PixelRow = curpixel;
283 		for (Sint64 y = 0; y < head.height; y++, PixelRow += m->bmpImage.get()->pitch)  {
284 			curpixel = PixelRow;
285 			for (Sint64 x = 0;x < head.width; x++, curpixel += bpp)  {
286 				curcolor = MakeColour(pDest[p], pDest[p+1], pDest[p+2]);
287 				p += 3;
288 				PutPixelToAddr(curpixel, curcolor, bpp);
289 			}
290 		}
291 
292 
293 		// Load the pixel flags and calculate dirt count
294 		Uint64 n=0;
295 		m->nTotalDirtCount = 0;
296 
297 		curpixel = (Uint8 *)m->bmpImage.get()->pixels;
298 		PixelRow = curpixel;
299 		Uint8 *backpixel = (Uint8 *)m->bmpBackImage.get()->pixels;
300 		Uint8 *BackPixelRow = backpixel;
301 
302 		m->lockFlags();
303 
304 		for(Sint64 y=0; y< head.height; y++, PixelRow+=m->bmpImage.get()->pitch, BackPixelRow+=m->bmpBackImage.get()->pitch) {
305 			curpixel = PixelRow;
306 			backpixel = BackPixelRow;
307 			for(Sint64 x=0; x<head.width; x++, curpixel+=bpp, backpixel+=bpp) {
308 				m->PixelFlags[n] = pDest[p++];
309 				if(m->PixelFlags[n] & PX_EMPTY)
310 					memcpy(curpixel, backpixel, bpp);
311 				if(m->PixelFlags[n] & PX_DIRT)
312 					m->nTotalDirtCount++;
313 				n++;
314 			}
315 		}
316 		m->unlockFlags();
317 
318 		// Unlock the surfaces
319 		UnlockSurface(m->bmpBackImage);
320 		UnlockSurface(m->bmpImage);
321 
322 		// Load the CTF gametype variables
323 		if (ctf)  {
324 			warnings << "CMap::LoadImageFormat(): trying to load old-format CTF map, we do not support this anymore" << endl;
325 			short dummy;
326 			fread_endian<short>(fp, dummy);
327 			fread_endian<short>(fp, dummy);
328 			fread_endian<short>(fp, dummy);
329 			fread_endian<short>(fp, dummy);
330 			fread_endian<short>(fp, dummy);
331 			fread_endian<short>(fp, dummy);
332 		}
333 
334 		//SDL_SaveBMP(pxf, "mat.bmp");
335 		//SDL_SaveBMP(bmpImage, "front.bmp");
336 		//SDL_SaveBMP(bmpBackImage, "back.bmp");
337 
338 		// Delete the data
339 		delete[] pDest;
340 
341 		// Try to load additional data (like hi-res images)
342 		LoadAdditionalLevelData(m);
343 
344 		return true;
345 	}
346 
347 	///////////////////
348 	// Load the high-resolution images
LoadAdditionalLevelData(CMap * m)349 	void LoadAdditionalLevelData(CMap* m)
350 	{
351 		// Check that we are not at the end of the file
352 		// HINT: this needs to be done because until we actually read the EOF, feof returns false
353 		int c = fgetc(fp);
354 		if (c == EOF)
355 			return;
356 		ungetc(c, fp);
357 
358 		while( !feof(fp) && !ferror(fp) )
359 		{
360 			std::string chunkName;
361 			if(!fread_fixedwidthstr<16>(chunkName, fp)) {
362 				errors << "CMap::LoadAdditionalLevelData: error while reading" << endl;
363 				break;
364 			}
365 			Uint32 size = 0;
366 			if(fread_endian<Uint32>(fp, size) == 0) {
367 				errors << "CMap::LoadAdditionalLevelData: error while reading (attribute " << chunkName << ")" << endl;
368 				break;
369 			}
370 			uchar *pSource = new uchar[size];
371 			if(pSource == NULL) {
372 				errors << "CMap::LoadAdditionalLevelData: not enough memory" << endl;
373 				break;
374 			}
375 			if(fread(pSource, sizeof(uchar), size, fp) < size) {
376 				delete[] pSource;
377 				errors << "CMap::LoadAdditionalLevelData: error while reading" << endl;
378 				break;
379 			}
380 
381 			if( stringcaseequal( chunkName, "OLX hi-res data") )
382 				LoadLevelImageHiRes( m, pSource, size );
383 			else
384 				if( stringcaseequal( chunkName, "OLX level config") )
385 					LoadLevelConfig( m, pSource, size );
386 				else
387 					warnings << "Unknown additional data found in level file: " << chunkName << ", size " << size << endl;
388 
389 			delete [] pSource;
390 		}
391 
392 	}
393 
394 
395 	///////////////////
396 	// Load level config, such as CTF base spawnpoints
LoadLevelConfig(CMap * m,uchar * pSource,Uint32 size)397 	void LoadLevelConfig(CMap* m, uchar *pSource, Uint32 size)
398 	{
399 		warnings << "CMap::LoadLevelConfig(): level config is not used yet in this version of OLX" << endl;
400 		return;
401 
402 		// TODO: test if this code works
403 
404 		Uint32 destsize = *(Uint32 *)(pSource);
405 		EndianSwap(destsize);
406 
407 		// Allocate the memory
408 		uchar *pDest = new uchar[destsize];
409 
410 		ulong lng_dsize = destsize;
411 		int ret = uncompress( pDest, &lng_dsize, pSource + sizeof(Uint32), size - sizeof(Uint32) );
412 		if( ret != Z_OK )
413 		{
414 			warnings << "CMap::LoadLevelConfig(): failed to load hi-res image, using low-res image" << endl;
415 			lng_dsize = 0;
416 		}
417 		destsize = lng_dsize;
418 
419 		// Fill up additional data
420 		m->AdditionalData.clear();
421 		Uint32 pos = 0;
422 		Uint32 AdditionalDataSize = *(Uint32 *)(pDest + pos);
423 		EndianSwap(AdditionalDataSize);
424 		pos += 4;
425 		if( AdditionalDataSize + 4 > destsize )
426 		{
427 			warnings << "CMap::LoadLevelConfig(): wrong additional data size " << AdditionalDataSize << endl;
428 		}
429 		else
430 		{
431 			bool nameChunk = true;
432 			std::string nameChunkData;
433 			while( AdditionalDataSize + 4 > pos ) // 4 bytes of whole data size
434 			{
435 				Uint32 chunkSize = *(Uint32 *)(pDest + pos);
436 				EndianSwap(chunkSize);
437 				if( chunkSize + pos > AdditionalDataSize )
438 				{
439 					warnings << "CMap::LoadLevelConfig(): wrong additional data chunk size " << chunkSize << endl;
440 					break;
441 				}
442 				pos += 4;
443 
444 				if( nameChunk )
445 					nameChunkData = std::string( (char *)(pDest + pos), chunkSize );
446 				else
447 					m->AdditionalData[nameChunkData] = std::string( (char *)(pDest + pos), chunkSize );
448 
449 				nameChunk = !nameChunk;
450 				pos += chunkSize;
451 			}
452 		}
453 		delete[] pDest;
454 	}
455 
456 	///////////////////
457 	// Load the high-resolution images
LoadLevelImageHiRes(CMap * m,uchar * pSource,Uint32 size)458 	void LoadLevelImageHiRes(CMap* m, uchar *pSource, Uint32 size)
459 	{
460 #ifndef DEDICATED_ONLY // Hi-res images are not needed for dedicated server, it uses only material image which is in low-res data
461 
462 		if(bDedicated) return;
463 
464 		gdImagePtr gdImage = gdImageCreateFromPngPtr( size, pSource );
465 
466 		if( !gdImage || gdImageSX(gdImage) != (int)head.width * 2 || gdImageSY(gdImage) != (int)head.height * 4 )
467 		{
468 			warnings << "CMap: hi-res image loading failed" << endl;
469 			if( gdImage )
470 				gdImageDestroy(gdImage);
471 			return;
472 		}
473 
474 		m->bmpBackImageHiRes = gfxCreateSurface((int)head.width*2, (int)head.height*2);
475 		if(m->bmpBackImageHiRes.get() == NULL)
476 		{
477 			warnings << "CMap::LoadImageFormatHiRes(): bmpBackImageHiRes creation failed, using low-res image" << endl;
478 			gdImageDestroy(gdImage);
479 			return;
480 		}
481 
482 		hints << "CMap: Loading high-res level images" << endl;
483 		// Lock surfaces
484 		LOCK_OR_QUIT(m->bmpDrawImage);
485 		LOCK_OR_QUIT(m->bmpBackImageHiRes);
486 
487 		Uint32 curcolor=0;
488 		Uint8* curpixel = (Uint8*)m->bmpDrawImage.get()->pixels;
489 		Uint8* PixelRow = curpixel;
490 		Uint8 bpp = m->bmpDrawImage.get()->format->BytesPerPixel;
491 
492 		// Load the front image
493 		for (Sint64 y = 0; y < head.height*2; y++, PixelRow += m->bmpDrawImage.get()->pitch)  {
494 			curpixel = PixelRow;
495 			for (Sint64 x = 0; x < head.width*2; x++, curpixel += bpp)  {
496 				curcolor = gdImageGetTrueColorPixel( gdImage, (int)x, (int)y ); // Maybe we can make direct memory access, but PNG may be palette-based, and I'm too lazy
497 				curcolor = MakeColour(gdTrueColorGetRed(curcolor), gdTrueColorGetGreen(curcolor), gdTrueColorGetBlue(curcolor));
498 				PutPixelToAddr(curpixel, curcolor, bpp);
499 			}
500 		}
501 
502 		curpixel = (Uint8*)m->bmpBackImageHiRes.get()->pixels;
503 		PixelRow = curpixel;
504 		Sint64 HeightX2 = head.height*2;
505 		// Load the back image
506 		for (Sint64 y = 0; y < head.height*2; y++, PixelRow += m->bmpBackImageHiRes.get()->pitch)  {
507 			curpixel = PixelRow;
508 			for (Sint64 x = 0; x < head.width*2; x++, curpixel += bpp)  {
509 				curcolor = gdImageGetTrueColorPixel( gdImage, (int)x, (int)y + (int)HeightX2 ); // Maybe we can make direct memory access, but PNG may be palette-based, and I'm too lazy
510 				curcolor = MakeColour(gdTrueColorGetRed(curcolor), gdTrueColorGetGreen(curcolor), gdTrueColorGetBlue(curcolor));
511 				PutPixelToAddr(curpixel, curcolor, bpp);
512 			}
513 		}
514 
515 		// Update image according to the pixel flags
516 		Uint64 n=0;
517 
518 		curpixel = (Uint8 *)m->bmpDrawImage.get()->pixels;
519 		PixelRow = curpixel;
520 		Uint8 *backpixel = (Uint8 *)m->bmpBackImageHiRes.get()->pixels;
521 		Uint8 *BackPixelRow = backpixel;
522 
523 		m->lockFlags();
524 		Uint8 bppX2 = bpp*2;
525 		int pitch = m->bmpDrawImage.get()->pitch;
526 		for(Sint64 y=0; y<head.height; y++, PixelRow+=pitch*2, BackPixelRow+=pitch*2 )
527 		{
528 			curpixel = PixelRow;
529 			backpixel = BackPixelRow;
530 			for(Sint64 x=0; x<head.width; x++, curpixel+=bppX2, backpixel+=bppX2)
531 			{
532 				if(m->PixelFlags[n] & PX_EMPTY)
533 				{
534 					memcpy(curpixel, backpixel, bppX2);
535 					memcpy(curpixel+pitch, backpixel+pitch, bppX2);
536 				}
537 				n++;
538 			}
539 		}
540 		m->unlockFlags();
541 		UnlockSurface(m->bmpBackImageHiRes);
542 		UnlockSurface(m->bmpDrawImage);
543 
544 		gdImageDestroy(gdImage);
545 
546 #endif // DEDICATED_ONLY
547 
548 	}
549 
550 
parseData(CMap * m)551 	bool parseData(CMap* m) {
552 		m->Name = head.name;
553 		m->Width = (int)head.width;
554 		m->Height = (int)head.height;
555 		m->Type = Type;
556 		const int Width = (int)head.width, Height = (int)head.height;
557 
558 
559 		/*
560 		 notes("Level info:\n");
561 		 notes("  id = %s\n", id);
562 		 notes("  version = %i\n", version);
563 		 notes("  Name = %s\n", Name);
564 		 notes("  Width = %i\n", Width);
565 		 notes("  Height = %i\n", Height);
566 		 notes("  Type = %i\n", Type);
567 		 notes("  Theme_Name = %s\n", Theme_Name);
568 		 notes("  numobj = %i\n", numobj);
569 		 */
570 
571 		// Load the images if in an image format
572 		if(Type == MPT_IMAGE)
573 		{
574 			// Allocate the map
575 		createMap:
576 			if(!m->Create(Width, Height, Theme_Name, m->MinimapWidth, m->MinimapHeight)) {
577 				errors << "CMap::Load (" << filename << "): cannot allocate map" << endl;
578 #ifdef MEMSTATS
579 				printMemStats();
580 #endif
581 				if(cCache.GetEntryCount() > 0) {
582 					hints << "current cache size is " << cCache.GetCacheSize() << ", we are clearing it now" << endl;
583 					cCache.Clear();
584 					goto createMap;
585 				}
586 				return false;
587 			}
588 
589 			// Load the image format
590 			notes << "CMap::Load: level " << filename << " is in image format" << endl;
591 			return LoadImageFormat(m);
592 		} else if (ctf)  {
593 			errors("pixmap format is not supported for CTF levels\n");
594 			return false;
595 		}
596 
597 
598 
599 		// Create a blank map
600 		if(!m->New(Width, Height, Theme_Name, m->MinimapWidth, m->MinimapHeight)) {
601 			errors << "CMap::Load (" << filename << "): cannot create map" << endl;
602 			return false;
603 		}
604 
605 		// Lock the surfaces
606 		LOCK_OR_FAIL(m->bmpImage);
607 		LOCK_OR_FAIL(m->bmpBackImage);
608 
609 		// Dirt map
610 		Uint8 *p1 = (Uint8 *)m->bmpImage.get()->pixels;
611 		Uint8 *p2 = (Uint8 *)m->bmpBackImage.get()->pixels;
612 		Uint8 *dstrow = p1;
613 		Uint8 *srcrow = p2;
614 
615 		// Load the bitmask, 1 bit == 1 pixel with a yes/no dirt flag
616 		uint size = Width*Height/8;
617 		uchar *bitmask = new uchar[size];
618 		if (!bitmask)  {
619 			errors << "CMap::Load: Could not create bit mask" << endl;
620 			return false;
621 		}
622 		if(fread(bitmask,sizeof(uchar),size,fp) < size) {
623 			errors << "CMap::Load: could not read bitmask" << endl;
624 			return false;
625 		}
626 
627 		static const unsigned char mask[] = {1,2,4,8,16,32,64,128};
628 
629 		m->nTotalDirtCount = Width*Height;  // Calculate the dirt count
630 
631 		m->lockFlags();
632 
633 		for(size_t n = 0, i = 0, x = 0; i < size; i++, x += 8) {
634 			if (x >= (size_t)Width)  {
635 				srcrow += m->bmpBackImage.get()->pitch;
636 				dstrow += m->bmpImage.get()->pitch;
637 				p1 = dstrow;
638 				p2 = srcrow;
639 				x = 0;
640 			}
641 
642 			// 1 bit == 1 pixel with a yes/no dirt flag
643 			for(size_t j = 0; j < 8;
644 				j++,
645 				n++,
646 				p1 += m->bmpImage.get()->format->BytesPerPixel,
647 				p2 += m->bmpBackImage.get()->format->BytesPerPixel) {
648 
649 				if(bitmask[i] & mask[j])  {
650 					m->PixelFlags[n] = PX_EMPTY;
651 					m->nTotalDirtCount--;
652 					memcpy(p1, p2, m->bmpImage.get()->format->BytesPerPixel);
653 				}
654 			}
655 		}
656 
657 		m->unlockFlags();
658 
659 		delete[] bitmask;
660 
661 		// Unlock the surfaces
662 		UnlockSurface(m->bmpImage);
663 		UnlockSurface(m->bmpBackImage);
664 
665 		// Objects
666 		object_t o;
667 		m->NumObjects = 0;
668 		for(int i = 0; i < numobj; i++) {
669 			fread_compat(o.Type,	sizeof(int),	1,	fp);
670 			EndianSwap(o.Type);
671 			fread_compat(o.Size,	sizeof(int),	1,	fp);
672 			EndianSwap(o.Size);
673 			fread_compat(o.X,	    sizeof(int),	1,	fp);
674 			EndianSwap(o.X);
675 			fread_compat(o.Y,	    sizeof(int),	1,	fp);
676 			EndianSwap(o.Y);
677 
678 			// Place the object
679 			if(o.Type == OBJ_STONE)
680 				m->PlaceStone(o.Size, CVec((float)o.X, (float)o.Y));
681 			else if(o.Type == OBJ_MISC)
682 				m->PlaceMisc(o.Size, CVec((float)o.X, (float)o.Y));
683 		}
684 
685 		return true;
686 	}
687 };
688 
689 
690 
691 
692 class ML_CommanderKeen123 : public MapLoader {
693 public:
format()694 	std::string format() { return "Commander Keen (1-3) level"; }
695 
696 	enum {
697 		MAX_TILES  =  700,
698 		MAP_MAXWIDTH	=	256,
699 		MAP_MAXHEIGHT	=	256,
700 		NUM_OPTIONS     = 21,
701 		HD_PLANES_START	=		16,
702 		TILE_W		=	16,
703 		TILE_H		=	16,
704 		TILE_S		=	4,
705 	};
706 
707 	struct stMap
708 	{
709 		Uint16 xsize, ysize;            // size of the map
710 		bool isworldmap;             // this is the world map
711 		bool ismenumap;				// score+menu map
712 		unsigned int mapdata[MAP_MAXWIDTH][MAP_MAXHEIGHT];       // the map data
713 		// in-game, contains monsters and special object tags like for switches
714 		// on world map contains level numbers and flags for things like teleporters
715 		unsigned int objectlayer[MAP_MAXWIDTH][MAP_MAXHEIGHT];
716 		// player start pos
717 		int startx, starty;
718 		// if 1, there is a time limit to finish the level
719 		bool hastimelimit;
720 		int time_m, time_s;		// how much time they have
721 		// play Tantalus Ray cinematic on time out (ep2)
722 		// or Game Over on time out (all other episodes)
723 		bool GameOverOnTimeOut;
724 		// map forced options (for usermaps)
725 		int forced_options[NUM_OPTIONS];
726 	};
727 	stMap map;
728 
729 	struct stTile
730 	{
731 		int solidfall;       // if =1, things can not fall through
732 		int solidl;          // if =1, things can not walk through left->right
733 		int solidr;          // if =1, things can not walk through right->left
734 		int solidceil;       // if =1, things can not go up through
735 		int goodie;          // if =1, is reported to get_goodie on touch
736 		int standgoodie;     // if =1, is reported to get_goodie when standing on it
737 		int lethal;          // if =1 and goodie=1, is deadly to the touch
738 		int pickupable;      // if =1, will be erased from map when touched
739 		int points;		  // how many points you get for picking it up
740 		int priority;        // if =1, will appear in front of objects
741 		int ice;             // if =1, it's very slippery!
742 		int semiice;         // if =1, player has no friction but can walk normally
743 		int masktile;        // if nonzero, specifies a mask for this tile
744 		int bonklethal;      // if you hit your head on it you die (hanging moss)
745 		int chgtile;         // tile to change to when level completed (for wm)
746 		// or tile to change to when picked up (in-level)
747 		// stuff for animated tiles
748 		unsigned char isAnimated;  // if =1, tile is animated
749 		unsigned int animOffset;   // starting offset from the base frame
750 		unsigned int animlength;   // animation length
751 	};
752 	stTile tiles[MAX_TILES];
753 
754 	// graphics
755 	unsigned char tiledata[MAX_TILES][16][16];
756 
roundup(int index,int nearest)757 	static int roundup(int index, int nearest)
758 	{
759 		if (index % nearest)
760 		{
761 			index /= nearest;
762 			index *= nearest;
763 			index += nearest;
764 		}
765 		return index;
766 	}
767 
768 	// decompress up to maxlen bytes of data from level file FP,
769 	// storing it in the buffer 'data'. returns nonzero on error.
rle_decompress(FILE * fp,SafeVector<Uint16> & data,size_t maxlen,bool debug=false,bool printErrors=false)770 	static bool rle_decompress(FILE *fp, SafeVector<Uint16>& data, size_t maxlen, bool debug = false, bool printErrors = false) {
771 		if(debug) notes << "map_rle_decompress: decompressing " << maxlen << " words." << endl;
772 
773 		size_t index = 0;
774 		size_t runs = 0;
775 		while(!feof(fp) && index < maxlen)
776 		{
777 			Uint16 ch = 0;
778 			fread_endian<Uint16>(fp, ch);
779 
780 			if (ch==0xFEFE)
781 			{
782 				Sint32 count = 0; fread_endian<Uint16>(fp, count);
783 				Uint16 what = 0; fread_endian<Uint16>(fp, what);
784 				while(count-- && index < maxlen) {
785 					Uint16* p = data[index]; index++;
786 					if(p) *p = what;
787 					else errors << "map_rle_decompress: data-index out of range" << endl;
788 				}
789 				runs++;
790 			}
791 			else
792 			{
793 				Uint16* p = data[index]; index++;
794 				if(p) *p = ch;
795 				else errors << "map_rle_decompress: data-index out of range" << endl;
796 			}
797 		}
798 
799 		if (index < maxlen)
800 		{
801 			if(printErrors) errors << "map_rle_decompress (at " << index << "): uh-oh, less data exists than spec'd in header." << endl;
802 			return false;
803 		}
804 
805 		if(debug) notes << "map_rle_decompress: decompressed " << index << " words in " << runs << " runs." << endl;
806 		return true;
807 	}
808 
809 private:
810 	struct LatchReader {
811 
812 		ML_CommanderKeen123* parent;
813 
814 		struct EgaHead {
815 			long LatchPlaneSize;                //Size of one plane of latch data
816 			long SpritePlaneSize;               //Size of one plane of sprite data
817 			long OffBitmapTable;                //Offset in EGAHEAD to bitmap table
818 			long OffSpriteTable;                //Offset in EGAHEAD to sprite table
819 			short Num8Tiles;                    //Number of 8x8 tiles
820 			long Off8Tiles;                     //Offset of 8x8 tiles (relative to plane data)
821 			short Num32Tiles;                   //Number of 32x32 tiles (always 0)
822 			long Off32Tiles;                    //Offset of 32x32 tiles (relative to plane data)
823 			short Num16Tiles;                   //Number of 16x16 tiles
824 			long Off16Tiles;                    //Offset of 16x16 tiles (relative to plane data)
825 			short NumBitmaps;                   //Number of bitmaps in table
826 			long OffBitmaps;                    //Offset of bitmaps (relative to plane data)
827 			short NumSprites;                   //Number of sprites
828 			long OffSprites;                    //Offset of sprites (relative to plane data)
829 			short Compressed;                   //(Keen 1 only) Nonzero: LZ compressed data
830 		};
831 
832 		EgaHead LatchHeader;
833 		size_t getbit_bytepos[5];
834 		uchar getbit_bitmask[5];
835 		char* RawData;
836 
LatchReaderML_CommanderKeen123::LatchReader837 		LatchReader(ML_CommanderKeen123* p) : parent(p), RawData(NULL) {}
~LatchReaderML_CommanderKeen123::LatchReader838 		~LatchReader() {
839 			if(RawData) delete[] RawData;
840 			RawData = NULL;
841 		}
842 
843 		// initilizes the positions getbit will retrieve data from
setplanepositionsML_CommanderKeen123::LatchReader844 		void setplanepositions(unsigned long p1, unsigned long p2, unsigned long p3,
845 							   unsigned long p4, unsigned long p5)
846 		{
847 			int i;
848 			getbit_bytepos[0] = p1;
849 			getbit_bytepos[1] = p2;
850 			getbit_bytepos[2] = p3;
851 			getbit_bytepos[3] = p4;
852 			getbit_bytepos[4] = p5;
853 
854 			for(i=0;i<=4;i++)
855 			{
856 				getbit_bitmask[i] = 128;
857 			}
858 		}
859 
860 		// retrieves a bit from plane "plane". the positions of the planes
861 		// should have been previously initilized with setplanepositions()
getbitML_CommanderKeen123::LatchReader862 		unsigned char getbit(char *buf, unsigned char plane)
863 		{
864 			if(plane >= 5) {
865 				errors << "getbit with plane=" << (int)plane << endl;
866 				return 0;
867 			}
868 			if (!getbit_bitmask[plane])
869 			{
870 				getbit_bitmask[plane] = 128;
871 				getbit_bytepos[plane]++;
872 			}
873 
874 			int byt = buf[getbit_bytepos[plane]];
875 
876 			int retval = 0;
877 			if (byt & getbit_bitmask[plane])
878 				retval = 1;
879 
880 			getbit_bitmask[plane] >>= 1;
881 
882 			return retval;
883 		}
884 
fgetlML_CommanderKeen123::LatchReader885 		static Uint32 fgetl(FILE* fp) {
886 			Uint32 res = 0;
887 			fread_endian<Uint32>(fp, res);
888 			return res;
889 		}
890 
fgetiML_CommanderKeen123::LatchReader891 		static Uint16 fgeti(FILE* fp) {
892 			Uint16 res = 0;
893 			fread_endian<Uint16>(fp, res);
894 			return res;
895 		}
896 
897 		// load the EGAHEAD file
latch_loadheaderML_CommanderKeen123::LatchReader898 		char latch_loadheader(const std::string& dir, bool abs_filename, int episode)
899 		{
900 			//unsigned long SpriteTableRAMSize;
901 			//unsigned long BitmapTableRAMSize;
902 			//char buf[12];
903 			//int i,j,k;
904 
905 			std::string fname = dir + "/EGAHEAD.CK" + itoa(episode);
906 			FILE* headfile = abs_filename ? OpenAbsFile(fname, "rb") : OpenGameFile(fname, "rb");
907 			if (!headfile)
908 			{
909 				errors << "latch_loadheader(): unable to open " << fname << endl;
910 				return 1;
911 			}
912 
913 			notes << "latch_loadheader(): reading main header from " << fname << endl;
914 
915 			// read the main header data from EGAHEAD
916 			LatchHeader.LatchPlaneSize = fgetl(headfile);
917 			LatchHeader.SpritePlaneSize = fgetl(headfile);
918 			LatchHeader.OffBitmapTable = fgetl(headfile);
919 			LatchHeader.OffSpriteTable = fgetl(headfile);
920 			LatchHeader.Num8Tiles = fgeti(headfile);
921 			LatchHeader.Off8Tiles = fgetl(headfile);
922 			LatchHeader.Num32Tiles = fgeti(headfile);
923 			LatchHeader.Off32Tiles = fgetl(headfile);
924 			LatchHeader.Num16Tiles = fgeti(headfile);
925 			LatchHeader.Off16Tiles = fgetl(headfile);
926 			LatchHeader.NumBitmaps = fgeti(headfile);
927 			LatchHeader.OffBitmaps = fgetl(headfile);
928 			LatchHeader.NumSprites = fgeti(headfile);
929 			LatchHeader.OffSprites = fgetl(headfile);
930 			LatchHeader.Compressed = fgeti(headfile);
931 
932 			notes << "   LatchPlaneSize = " << LatchHeader.LatchPlaneSize << endl;
933 			notes << "   SpritePlaneSize = " << LatchHeader.SpritePlaneSize << endl;
934 			notes << "   OffBitmapTable = " << LatchHeader.OffBitmapTable << endl;
935 			notes << "   OffSpriteTable = " << LatchHeader.OffSpriteTable << endl;
936 			notes << "   Num8Tiles = " << LatchHeader.Num8Tiles << endl;
937 			notes << "   Off8Tiles = " << LatchHeader.Off8Tiles << endl;
938 			notes << "   Num32Tiles = " << LatchHeader.Num32Tiles << endl;
939 			notes << "   Off32Tiles = " << LatchHeader.Off32Tiles << endl;
940 			notes << "   Num16Tiles = " << LatchHeader.Num16Tiles << endl;
941 			notes << "   Off16Tiles = " << LatchHeader.Off16Tiles << endl;
942 			notes << "   NumBitmaps = " << LatchHeader.NumBitmaps << endl;
943 			notes << "   OffBitmaps = " << LatchHeader.OffBitmaps << endl;
944 			notes << "   NumSprites = " << LatchHeader.NumSprites << endl;
945 			notes << "   OffSprites = " << LatchHeader.OffSprites << endl;
946 			notes << "   Compressed = " << LatchHeader.Compressed << endl;
947 
948 			/** read in the sprite table **/
949 			/*
950 			// allocate memory for the sprite table
951 			SpriteTableRAMSize = sizeof(SpriteHead) * (LatchHeader.NumSprites + 1);
952 			lprintf("latch_loadheader(): Allocating %d bytes for sprite table.\n", SpriteTableRAMSize);
953 
954 			SpriteTable = malloc(SpriteTableRAMSize);
955 			if (!SpriteTable)
956 			{
957 				lprintf("latch_loadheader(): Can't allocate sprite table!\n");
958 				return 1;
959 			}
960 
961 			lprintf("latch_loadheader(): Reading sprite table from '%s'...\n", fname);
962 
963 			fseek(headfile, LatchHeader.OffSpriteTable, SEEK_SET);
964 			for(i=0;i<LatchHeader.NumSprites;i++)
965 			{
966 				SpriteTable[i].Width = fgeti(headfile) * 8;
967 				SpriteTable[i].Height = fgeti(headfile);
968 				SpriteTable[i].OffsetDelta = fgeti(headfile);
969 				SpriteTable[i].OffsetParas = fgeti(headfile);
970 				SpriteTable[i].Rx1 = (fgeti(headfile) >> 8);
971 				SpriteTable[i].Ry1 = (fgeti(headfile) >> 8);
972 				SpriteTable[i].Rx2 = (fgeti(headfile) >> 8);
973 				SpriteTable[i].Ry2 = (fgeti(headfile) >> 8);
974 				for(j=0;j<16;j++) SpriteTable[i].Name[j] = fgetc(headfile);
975 				// for some reason each sprite occurs 4 times in the table.
976 				// we're only interested in the first occurance.
977 				for(j=0;j<3;j++)
978 				{
979 					for(k=0;k<sizeof(SpriteHead);k++) fgetc(headfile);
980 				}
981 
982 			}
983 			*/
984 			/** read in the bitmap table **/
985 			/*
986 			// allocate memory for the bitmap table
987 			BitmapTableRAMSize = sizeof(BitmapHead) * (LatchHeader.NumBitmaps + 1);
988 			lprintf("latch_loadheader(): Allocating %d bytes for bitmap table.\n", BitmapTableRAMSize);
989 
990 			BitmapTable = malloc(BitmapTableRAMSize);
991 			if (!BitmapTable)
992 			{
993 				lprintf("latch_loadheader(): Can't allocate bitmap table!\n");
994 				return 1;
995 			}
996 
997 			lprintf("latch_loadheader(): reading bitmap table from '%s'...\n", fname);
998 
999 			fseek(headfile, LatchHeader.OffBitmapTable, SEEK_SET);
1000 
1001 			BitmapBufferRAMSize = 0;
1002 			for(i=0;i<LatchHeader.NumBitmaps;i++)
1003 			{
1004 				BitmapTable[i].Width = fgeti(headfile) * 8;
1005 				BitmapTable[i].Height = fgeti(headfile);
1006 				BitmapTable[i].Offset = fgetl(headfile);
1007 				for(j=0;j<8;j++) BitmapTable[i].Name[j] = fgetc(headfile);
1008 
1009 				// keep a tally of the bitmap sizes so we'll know how much RAM we have
1010 				// to allocate for all of the bitmaps once they're decoded
1011 				BitmapBufferRAMSize += (BitmapTable[i].Width * BitmapTable[i].Height);
1012 
1013 				// print the bitmap info to the console for debug
1014 				for(j=0;j<8;j++) buf[j] = BitmapTable[i].Name[j];
1015 				buf[j] = 0;
1016 				lprintf("   Bitmap '%s': %dx%d at offset %04x. RAMAllocSize=0x%04x\n", buf,BitmapTable[i].Width,BitmapTable[i].Height,BitmapTable[i].Offset,BitmapBufferRAMSize);
1017 			}
1018 			BitmapBufferRAMSize++;
1019 			*/
1020 
1021 			fclose(headfile);
1022 			return 0;
1023 		}
1024 
latch_loadlatchML_CommanderKeen123::LatchReader1025 		char latch_loadlatch(const std::string& dir, bool abs_filename, int episode) {
1026 
1027 			unsigned long plane1, plane2, plane3, plane4;
1028 			int x,y,t,c=0,p;
1029 			// int b;
1030 			//char *bmdataptr;
1031 
1032 			std::string fname = dir + "/EGALATCH.CK" + itoa(episode);
1033 
1034 			notes << "latch_loadlatch(): Opening file " << fname << endl;
1035 
1036 			FILE* latchfile = abs_filename ? OpenAbsFile(fname, "rb") : OpenGameFile(fname, "rb");
1037 			if (!latchfile)
1038 			{
1039 				errors << "latch_loadlatch(): Unable to open " << fname << endl;
1040 				return 1;
1041 			}
1042 
1043 			// figure out how much RAM we'll need to read all 4 planes of
1044 			// latch data into memory.
1045 			size_t RawDataSize = (LatchHeader.LatchPlaneSize * 4);
1046 			if(RawData) delete[] RawData;
1047 			RawData = new char[RawDataSize];
1048 			if (!RawData)
1049 			{
1050 				errors << "latch_loadlatch(): Unable to allocate RawData buffer!" << endl;
1051 				fclose(latchfile);
1052 				return 1;
1053 			}
1054 
1055 			// get the data out of the file into memory, decompressing if necessary.
1056 			if (LatchHeader.Compressed)
1057 			{
1058 				notes << "latch_loadlatch(): Decompressing..." << endl;
1059 				fseek(latchfile, 6, SEEK_SET);
1060 				int ok = lz_decompress(latchfile, (uchar*)RawData, (uchar*)RawData + RawDataSize);
1061 				if (ok) {
1062 					errors << "lzd returns " << ok << endl;
1063 					fclose(latchfile);
1064 					return 1;
1065 				}
1066 			}
1067 			else
1068 			{
1069 				notes << "latch_loadlatch(): Reading " << RawDataSize << " bytes..." << endl;
1070 				fread(RawData, RawDataSize, 1, latchfile);
1071 			}
1072 			fclose(latchfile);
1073 
1074 			// these are the offsets of the different video planes as
1075 			// relative to each other--that is if a pixel in plane1
1076 			// is at N, the byte for that same pixel in plane3 will be
1077 			// at (N + plane3).
1078 			plane1 = 0;
1079 			plane2 = (LatchHeader.LatchPlaneSize * 1);
1080 			plane3 = (LatchHeader.LatchPlaneSize * 2);
1081 			plane4 = (LatchHeader.LatchPlaneSize * 3);
1082 
1083 			// ** read the 8x8 tiles **
1084 			notes << "latch_loadlatch(): Decoding 8x8 tiles..." << endl;
1085 
1086 			// set up the getbit() function
1087 			setplanepositions(plane1 + LatchHeader.Off8Tiles,
1088 							  plane2 + LatchHeader.Off8Tiles,
1089 							  plane3 + LatchHeader.Off8Tiles,
1090 							  plane4 + LatchHeader.Off8Tiles,
1091 							  0);
1092 
1093 			for(p=0;p<4;p++)
1094 			{
1095 				for(t=0;t<LatchHeader.Num8Tiles;t++)
1096 				{
1097 					for(y=0;y<8;y++)
1098 					{
1099 						for(x=0;x<8;x++)
1100 						{
1101 							// if we're on the first plane start with black,
1102 							// else merge with the previously accumulated data
1103 							if (p==0)
1104 							{
1105 								c = 0;
1106 							}
1107 							else
1108 							{
1109 								//c = font[t][y][x];
1110 							}
1111 
1112 							// read a bit out of the current plane, shift it into the
1113 							// correct position and merge it
1114 							c |= (getbit(RawData, p) << p);
1115 							if (p==3 && !c) c=16;
1116 							//font[t][y][x] = c;
1117 						}
1118 					}
1119 				}
1120 			}
1121 			//Make_Font_Clear();
1122 
1123 			// ** read the 16x16 tiles **
1124 			notes << "latch_loadlatch(): Decoding 16x16 tiles..." << endl;
1125 
1126 			// set up the getbit() function
1127 			setplanepositions(plane1 + LatchHeader.Off16Tiles,
1128 							  plane2 + LatchHeader.Off16Tiles,
1129 							  plane3 + LatchHeader.Off16Tiles,
1130 							  plane4 + LatchHeader.Off16Tiles,
1131 							  0);
1132 
1133 			for(p=0;p<4;p++)
1134 			{
1135 				for(t=0;t<LatchHeader.Num16Tiles;t++)
1136 				{
1137 					for(y=0;y<16;y++)
1138 					{
1139 						for(x=0;x<16;x++)
1140 						{
1141 							if (p==0)
1142 							{
1143 								c = 0;
1144 							}
1145 							else
1146 							{
1147 								c = parent->tiledata[t][y][x];
1148 							}
1149 							c |= (getbit(RawData, p) << p);
1150 							parent->tiledata[t][y][x] = c;
1151 						}
1152 					}
1153 				}
1154 			}
1155 
1156 			// clear all unused tiles
1157 			for(t=LatchHeader.Num16Tiles;t<MAX_TILES;t++)
1158 			{
1159 				for(y=0;y<TILE_H;y++)
1160 					for(x=0;x<TILE_W;x++)
1161 					{
1162 						parent->tiledata[t][y][x] = ((x&1) ^ (y&1)) ? 8:0;
1163 					}
1164 			}
1165 
1166 			// ** read the bitmaps **
1167 			/*lprintf("latch_loadlatch(): Allocating %d bytes for bitmap data...\n", BitmapBufferRAMSize);
1168 			BitmapData = malloc(BitmapBufferRAMSize);
1169 			if (!BitmapData)
1170 			{
1171 				lprintf("Cannot allocate memory for bitmaps.\n");
1172 				return 1;
1173 			}
1174 
1175 			lprintf("latch_loadlatch(): Decoding bitmaps...\n", fname);
1176 
1177 			// set up the getbit() function
1178 			setplanepositions(plane1 + LatchHeader.OffBitmaps, \
1179 							  plane2 + LatchHeader.OffBitmaps, \
1180 							  plane3 + LatchHeader.OffBitmaps, \
1181 							  plane4 + LatchHeader.OffBitmaps, \
1182 							  0);
1183 
1184 			// decode bitmaps into the BitmapData structure. The bitmaps are
1185 			// loaded into one continous stream of image data, with the bitmaps[]
1186 			// array giving pointers to where each bitmap starts within the stream.
1187 
1188 			for(p=0;p<4;p++)
1189 			{
1190 				// this points to the location that we're currently
1191 				// decoding bitmap data to
1192 				bmdataptr = &BitmapData[0];
1193 
1194 				for(b=0;b<LatchHeader.NumBitmaps;b++)
1195 				{
1196 					bitmaps[b].xsize = BitmapTable[b].Width;
1197 					bitmaps[b].ysize = BitmapTable[b].Height;
1198 					bitmaps[b].bmptr = bmdataptr;
1199 					memcpy(&bitmaps[b].name[0], &BitmapTable[b].Name[0], 8);
1200 					bitmaps[b].name[8] = 0;  //ensure null-terminated
1201 
1202 					for(y=0;y<bitmaps[b].ysize;y++)
1203 					{
1204 						for(x=0;x<bitmaps[b].xsize;x++)
1205 						{
1206 							if (p==0)
1207 							{
1208 								c = 0;
1209 							}
1210 							else
1211 							{
1212 								c = *bmdataptr;
1213 							}
1214 							c |= (getbit(RawData, p) << p);
1215 							*bmdataptr = c;
1216 							bmdataptr++;
1217 						}
1218 					}
1219 				}
1220 			}*/
1221 
1222 			delete[] RawData; RawData = NULL;
1223 			return 0;
1224 		}
1225 
1226 
1227 
1228 		enum {
1229 			LZ_STARTBITS    =    9,
1230 			LZ_MAXBITS     =     12,
1231 			LZ_ERRORCODE   =     256,
1232 			LZ_EOFCODE     =     257,
1233 			LZ_DICTSTARTCODE  =  258,
1234 			LZ_MAXDICTSIZE  =    ((1<<LZ_MAXBITS)+1),
1235 			LZ_MAXSTRINGSIZE =   72
1236 		};
1237 
1238 		// only temporarly pointing to the current position in buffer when decompressing
1239 		unsigned char *lz_outbuffer;
1240 		unsigned char *lz_outbuffer_end;
1241 
1242 		struct stLZDictionaryEntry
1243 		{
1244 			int stringlen;
1245 			unsigned char string[LZ_MAXSTRINGSIZE];
1246 		};
1247 
1248 		stLZDictionaryEntry *lzdict[LZ_MAXDICTSIZE];
1249 
1250 		// reads a word of length numbits from file lzfile.
lz_readbitsML_CommanderKeen123::LatchReader1251 		unsigned int lz_readbits(FILE *lzfile, unsigned char numbits, unsigned char reset)
1252 		{
1253 			static int mask, byte;
1254 			unsigned char bitsread;
1255 			unsigned int dat;
1256 
1257 			if (reset)
1258 			{
1259 				mask = 0;
1260 				byte = 0;
1261 				return 0;
1262 			}
1263 
1264 			bitsread = 0;
1265 			dat = 0;
1266 			do
1267 			{
1268 				if (!mask)
1269 				{
1270 					byte = fgetc(lzfile);
1271 					mask = 0x80;
1272 				}
1273 
1274 				if (byte & mask)
1275 				{
1276 					dat |= 1 << ((numbits - bitsread) - 1);
1277 				}
1278 
1279 				mask >>= 1;
1280 				bitsread++;
1281 			} while(bitsread<numbits);
1282 
1283 			return dat;
1284 		}
1285 
1286 		// writes dictionary entry 'entry' to the output buffer
lz_outputdictML_CommanderKeen123::LatchReader1287 		void lz_outputdict(int entry)
1288 		{
1289 			int i;
1290 
1291 			for(i=0;i<lzdict[entry]->stringlen;i++)
1292 			{
1293 				if(lz_outbuffer >= lz_outbuffer_end) {
1294 					errors << "lz_outputdict: out of buffer range" << endl;
1295 					return;
1296 				}
1297 				*lz_outbuffer = lzdict[entry]->string[i];
1298 				lz_outbuffer++;
1299 			}
1300 		}
1301 
1302 		// decompresses LZ data from open file lzfile into buffer outbuffer
1303 		// returns nonzero if an error occurs
lz_decompressML_CommanderKeen123::LatchReader1304 		char lz_decompress(FILE *lzfile, uchar *outbuffer, uchar* bufend)
1305 		{
1306 			int i;
1307 			int numbits;
1308 			unsigned int dictindex, maxdictindex;
1309 			unsigned int lzcode,lzcode_save,lastcode;
1310 			char addtodict;
1311 
1312 			/* allocate memory for the LZ dictionary */
1313 			for(i=0;i<LZ_MAXDICTSIZE;i++)
1314 			{
1315 				lzdict[i] = new stLZDictionaryEntry;
1316 				if (!lzdict[i])
1317 				{
1318 					errors << "lz_decompress(): unable to allocate memory for dictionary!" << endl;
1319 					for(i--;i>=0;i--) delete lzdict[i];
1320 
1321 					return 1;
1322 				}
1323 			}
1324 
1325 			/* initilize the dictionary */
1326 
1327 			// entries 0-255 start with a single character corresponding
1328 			// to their entry number
1329 			for(i=0;i<256;i++)
1330 			{
1331 				lzdict[i]->stringlen = 1;
1332 				lzdict[i]->string[0] = i;
1333 			}
1334 			// 256+ start undefined
1335 			for(i=256;i<LZ_MAXDICTSIZE;i++)
1336 			{
1337 				lzdict[i]->stringlen = 0;
1338 			}
1339 
1340 			// reset readbits
1341 			lz_readbits(NULL, 0, 1);
1342 
1343 			// set starting # of bits-per-code
1344 			numbits = LZ_STARTBITS;
1345 			maxdictindex = (1 << numbits) - 1;
1346 
1347 			// point the global pointer to the buffer we were passed
1348 			lz_outbuffer = outbuffer;
1349 			lz_outbuffer_end = bufend;
1350 
1351 			// setup where to start adding strings to the dictionary
1352 			dictindex = LZ_DICTSTARTCODE;
1353 			addtodict = 1;                    // enable adding to dictionary
1354 
1355 			// read first code
1356 			lastcode = lz_readbits(lzfile, numbits, 0);
1357 			lz_outputdict(lastcode);
1358 			do
1359 			{
1360 				// read the next code from the compressed data stream
1361 				lzcode = lz_readbits(lzfile, numbits, 0);
1362 				lzcode_save = lzcode;
1363 
1364 				if (lzcode==LZ_ERRORCODE || lzcode==LZ_EOFCODE)
1365 					break;
1366 
1367 				// if the code is present in the dictionary,
1368 				// lookup and write the string for that code, then add the
1369 				// last string + the first char of the just-looked-up string
1370 				// to the dictionary at dictindex
1371 
1372 				// if not in dict, add the last string + the first char of the
1373 				// last string to the dictionary at dictindex (which will be equal
1374 				// to lzcode), then lookup and write string lzcode.
1375 
1376 				if (lzdict[lzcode]->stringlen==0)
1377 				{  // code is not present in dictionary
1378 					lzcode = lastcode;
1379 				}
1380 
1381 				if (addtodict)     // room to add more entries to the dictionary?
1382 				{
1383 					// copies string lastcode to string dictindex, then
1384 					// concatenates the first character of string lzcode.
1385 					for(i=0;i<lzdict[lastcode]->stringlen;i++)
1386 					{
1387 						lzdict[dictindex]->string[i] = lzdict[lastcode]->string[i];
1388 					}
1389 					lzdict[dictindex]->string[i] = lzdict[lzcode]->string[0];
1390 					lzdict[dictindex]->stringlen = (lzdict[lastcode]->stringlen + 1);
1391 
1392 					// ensure we haven't overflowed the buffer
1393 					if (lzdict[dictindex]->stringlen >= (LZ_MAXSTRINGSIZE-1))
1394 					{
1395 						errors << "lz_decompress(): lzdict[" << dictindex << "]->stringlen is too long...max length is " << (int)LZ_MAXSTRINGSIZE << endl;
1396 						for(i=0;i<LZ_MAXDICTSIZE;i++) delete lzdict[i];
1397 						return 1;
1398 					}
1399 
1400 					if (++dictindex >= maxdictindex)
1401 					{ // no more entries can be specified with current code bit-width
1402 						if (numbits < LZ_MAXBITS)
1403 						{  // increase width of codes
1404 							numbits++;
1405 							maxdictindex = (1 << numbits) - 1;
1406 						}
1407 						else
1408 						{
1409 							// reached maximum bit width, can't increase.
1410 							// use the final entry (4095) before we shut off
1411 							// adding items to the dictionary.
1412 							if (dictindex>=(LZ_MAXDICTSIZE-1)) addtodict = 0;
1413 						}
1414 					}
1415 				}
1416 
1417 				// write the string associated with the original code read.
1418 				// if the code wasn't present, it now should have been added.
1419 				lz_outputdict(lzcode_save);
1420 
1421 				lastcode = lzcode_save;
1422 			} while(1);
1423 
1424 			/* free the memory used by the LZ dictionary */
1425 			for(i=0;i<LZ_MAXDICTSIZE;i++)
1426 				delete lzdict[i];
1427 
1428 			return 0;
1429 		}
1430 
1431 	};
1432 
loadtileattributes(int episode)1433 	bool loadtileattributes(int episode)
1434 	{
1435 		int t,a,b,c,intendedep,intendedver;
1436 
1437 		std::string fname = "data/commanderkeen123/ep" + itoa(episode) + "attr.dat";
1438 
1439 		//  notes << "Loading tile attributes from '" << fname << "'..." << endl;
1440 
1441 		FILE* fp = OpenGameFile(fname, "rb");
1442 		if (!fp)
1443 		{
1444 			errors << "loadtileattributes(): Cannot open tile attribute file " << fname << endl;
1445 			return false;
1446 		}
1447 
1448 		/* check the header */
1449 		// header format: 'A', 'T', 'R', episode, version
1450 		a = fgetc(fp);
1451 		b = fgetc(fp);
1452 		c = fgetc(fp);
1453 		if (a != 'A' || b != 'T' || c != 'R')
1454 		{
1455 			errors << "loadtileattributes(): Attribute file corrupt! ('ATR' marker not found)" << endl;
1456 			fclose(fp);
1457 			return false;
1458 		}
1459 
1460 		intendedep = fgetc(fp);
1461 		if (intendedep != episode)
1462 		{
1463 			errors << "loadtileattributes(): file is intended for episode " << intendedep << " but you're trying to use it with episode " << episode << endl;
1464 			fclose(fp);
1465 			return false;
1466 		}
1467 
1468 		intendedver = fgetc(fp);
1469 		static const int ATTRFILEVERSION = 2;
1470 		if (intendedver != ATTRFILEVERSION)
1471 		{
1472 			errors << "attr file version " << intendedver << ", I need version " << ATTRFILEVERSION << endl;
1473 			fclose(fp);
1474 			return false;
1475 		}
1476 
1477 		/* load in the tile attributes */
1478 
1479 		for(t=0;t<MAX_TILES-1;t++)
1480 		{
1481 
1482 			tiles[t].solidl = fgetc(fp);
1483 			if (tiles[t].solidl==-1)
1484 			{
1485 				errors << "loadtileattributes(): " << fname << " corrupt! (unexpected EOF)" << endl;
1486 				fclose(fp);
1487 				return false;
1488 			}
1489 
1490 			tiles[t].solidr = fgetc(fp);
1491 			tiles[t].solidfall = fgetc(fp);
1492 			tiles[t].solidceil = fgetc(fp);
1493 			tiles[t].ice = fgetc(fp);
1494 			tiles[t].semiice = fgetc(fp);
1495 			tiles[t].priority = fgetc(fp);
1496 			if (fgetc(fp)) tiles[t].masktile=t+1; else tiles[t].masktile = 0;
1497 			tiles[t].goodie = fgetc(fp);
1498 			tiles[t].standgoodie = fgetc(fp);
1499 			tiles[t].pickupable = fgetc(fp);
1500 			fread_endian<Uint16>(fp, tiles[t].points);
1501 			tiles[t].lethal = fgetc(fp);
1502 			tiles[t].bonklethal = fgetc(fp);
1503 			fread_endian<Uint16>(fp, tiles[t].chgtile);
1504 			tiles[t].isAnimated = fgetc(fp);
1505 			tiles[t].animOffset = fgetc(fp);
1506 			tiles[t].animlength = fgetc(fp);
1507 
1508 		}
1509 
1510 		fclose(fp);
1511 		return true;
1512 	}
1513 
1514 
parseTiles(int episode)1515 	bool parseTiles(int episode) {
1516 		if(!loadtileattributes(episode)) return false;
1517 		LatchReader latch(this);
1518 		if(latch.latch_loadheader(GetDirName(filename), abs_filename, episode)) return false;
1519 		if(latch.latch_loadlatch(GetDirName(filename), abs_filename, episode)) return false;
1520 		return true;
1521 	}
1522 
1523 public:
1524 	static const bool withKeenBorders = false;
1525 
parseHeader(bool printErrors)1526 	bool parseHeader(bool printErrors) {
1527 		head.name = GetBaseFilename(filename);
1528 
1529 		fseek(fp,0,SEEK_SET);
1530 
1531 		Uint32 maplen = 0;
1532 		fread_endian<Uint32>(fp, maplen);
1533 		if(maplen <= 4) {
1534 			if(printErrors) errors << "CK loader: file size invalid" << endl;
1535 			return false;
1536 		}
1537 
1538 		SafeVector<Uint16> data(2);
1539 		if(!rle_decompress(fp, data, data.size(), false, printErrors)) {
1540 			if(printErrors) errors << "CK loader: invalid RLE encoding" << endl;
1541 			return false;
1542 		}
1543 
1544 		Sint32 w = *data[0], h = *data[1];
1545 		if (w >= MAP_MAXWIDTH || h >= MAP_MAXHEIGHT) {
1546 			if(printErrors) errors << "CK loader: map too big" << endl;
1547 			return false;
1548 		}
1549 
1550 		if(!withKeenBorders) {
1551 			w -= 4; h -= 4;
1552 			if(w < 0 || h < 0) {
1553 				if(printErrors) errors << "CK loader: map too small" << endl;
1554 				return false;
1555 			}
1556 		}
1557 
1558 		head.width = w * TILE_W;
1559 		head.height = h * TILE_H;
1560 
1561 		return true;
1562 	}
1563 
1564 
parseData(CMap * m)1565 	bool parseData(CMap* m) {
1566 		int episode = 0;
1567 		if(filename != "") {
1568 			episode = (int)filename[filename.size() - 1] - (int)'0';
1569 			if(episode < 1 || episode > 3) episode = 0;
1570 		}
1571 
1572 		if(!parseTiles(episode)) return false;
1573 		if(!loadPalette(episode)) return false;
1574 
1575 		fseek(fp,0,SEEK_SET);
1576 
1577 		map.isworldmap = strStartsWith( stringtolower(GetBaseFilename(filename)), "level80.ck" );
1578 		map.ismenumap = strStartsWith( stringtolower(GetBaseFilename(filename)), "level90.ck" );
1579 
1580 		Uint32 maplen = 0;
1581 		fread_endian<Uint32>(fp, maplen);
1582 		notes << "Map uncompressed length: " << maplen << " bytes." << endl;
1583 		if (!maplen) {
1584 			errors << "loadmap: This map says it's 0 bytes long... it was probably created by a broken map editor" << endl;
1585 			return false;
1586 		}
1587 		maplen /= 2;
1588 
1589 		SafeVector<Uint16> data(maplen + 1);
1590 		if (data.size() == 0) {
1591 			errors << "loadmap: unable to allocate " << maplen*2 << " bytes." << endl;
1592 			return false;
1593 		}
1594 
1595 		if (!rle_decompress(fp, data, maplen, true, true)) {
1596 			errors << "loadmap: RLE decompression error" << endl;
1597 			return false;
1598 		}
1599 
1600 		map.xsize = *data[0];
1601 		map.ysize = *data[1];
1602 		if (*data[2] != 2) {
1603 			errors << "loadmap(): incorrect number of planes (loader only supports 2)" << endl;
1604 			return false;
1605 		}
1606 
1607 		notes << "loadmap(): " << filename << " map dimensions " << map.xsize << "x" << map.ysize << endl;
1608 		if (map.xsize >= MAP_MAXWIDTH || map.ysize >= MAP_MAXHEIGHT) {
1609 			errors << "loadmap(): level " << filename << " is too big (max size " << (int)MAP_MAXWIDTH << "x" << (int)MAP_MAXHEIGHT << ")" << endl;
1610 			return false;
1611 		}
1612 
1613 		Uint16 plane_size = *data[7];
1614 		notes << "plane size " << plane_size << " bytes" << endl;
1615 		if (plane_size & 1) {
1616 			errors << "loadmap(): plane size is not even!" << endl;
1617 			return false;
1618 		}
1619 
1620 		// copy the tile layer into the map
1621 		size_t index = HD_PLANES_START;
1622 		for(Uint16 y=0;y<map.ysize;y++)
1623 		{
1624 			for(Uint16 x=0;x<map.xsize;x++)
1625 			{
1626 				map.mapdata[x][y] = *data[index++];
1627 			}
1628 		}
1629 
1630 		// copy the object layer into the map
1631 		// get index of plane 2, rounding up to the nearest 16 worde boundary (8 words)
1632 		index = roundup((HD_PLANES_START + (plane_size / 2)), 8);
1633 
1634 		for(Uint16 y=0;y<map.ysize;y++)
1635 		{
1636 			for(Uint16 x=0;x<map.xsize;x++)
1637 			{
1638 				Uint16 t = *data[index++];
1639 				if (t==255)
1640 				{
1641 					//map_setstartpos(x, y);
1642 				}
1643 				else
1644 				{
1645 					map.objectlayer[x][y] = t;
1646 					if (t)
1647 					{
1648 						if (!map.isworldmap)
1649 						{
1650 							// spawn enemies as appropriate
1651 							//AddEnemy(x, y);
1652 						}
1653 						/* else
1654 						{
1655 							// spawn Nessie at first occurance of her path
1656 							if ( episode==3 && t==NESSIE_PATH)
1657 							{
1658 								if (!NessieObjectHandle)
1659 								{
1660 									NessieObjectHandle = spawn_object(x<<TILE_S<<CSF, y<<TILE_S<<CSF, OBJ_NESSIE);
1661 									objects[NessieObjectHandle].hasbeenonscreen = 1;
1662 								}
1663 							}
1664 							else
1665 							{
1666 								// make completed levels into "done" tiles
1667 								t &= 0x7fff;
1668 								if (t < MAX_LEVELS && levelcontrol.levels_completed[t])
1669 								{
1670 									map.objectlayer[x][y] = 0;
1671 									map.mapdata[x][y] = tiles[map.mapdata[x][y]].chgtile;
1672 								}
1673 							}
1674 						} */
1675 					}
1676 				}
1677 			}
1678 		}
1679 
1680 		// check if the mapfile has a clonekeen-specific special region
1681 		/* if (data[HD_HAS_SPECIAL_1]==SPECIAL_VALUE_1 && \
1682 			data[HD_HAS_SPECIAL_2]==SPECIAL_VALUE_2)
1683 		{
1684 			lprintf("> level created in CloneKeen Editor!\n");
1685 			unsigned long special_offs;
1686 			special_offs = data[HD_SPECIAL_OFFS_MSB]; special_offs <<= 16;
1687 			special_offs |= data[HD_SPECIAL_OFFS_LSB];
1688 
1689 			lprintf("Parsing CloneKeen-specific data at %08x\n", special_offs);
1690 			if (parse_special_region(&data[special_offs]))
1691 			{
1692 				lprintf("loadmap(): error parsing special region\n");
1693 				return 1;
1694 			}
1695 		}
1696 		else
1697 		{
1698 			lprintf("> level NOT created by CloneKeen.\n");
1699 		} */
1700 
1701 		map_coat_border(episode);
1702 		//map_calc_max_scroll();
1703 
1704 		notes << "loadmap(): success!" << endl;
1705 
1706 		return constructMap(m);
1707 	}
1708 
1709 private:
1710 
map_coat_border(int episode)1711 	void map_coat_border(int episode)
1712 	{
1713 		enum {
1714 			TILE_FELLOFFMAP		=	582,
1715 			TILE_FELLOFFMAP_EP3	=	0,
1716 			TILE_FUEL			=	245,
1717 			BG_GREY				=	143,
1718 		};
1719 
1720 		Uint16 border= (episode==3) ? TILE_FUEL : 144;
1721 		for(Uint32 x=0;x<map.xsize;x++)
1722 		{
1723 			map.mapdata[x][0] = border;
1724 			map.mapdata[x][1] = border;
1725 			map.mapdata[x][map.ysize-1] = border;
1726 			map.mapdata[x][map.ysize-2] = border;
1727 		}
1728 		for(Uint32 y=0;y<map.ysize;y++)
1729 		{
1730 			map.mapdata[0][y] = border;
1731 			map.mapdata[1][y] = border;
1732 			map.mapdata[map.xsize-1][y] = border;
1733 			map.mapdata[map.xsize-2][y] = border;
1734 		}
1735 
1736 		if (episode == 3)
1737 		{
1738 			// coat the top of the map ("oh no!" border) with a non-solid tile
1739 			// so keen can jump partially off the top of the screen
1740 			for(int x=2;x<map.xsize-2;x++)
1741 			{
1742 				map.mapdata[x][1] = BG_GREY;
1743 			}
1744 
1745 			// make it lethal to fall off the bottom of the map.
1746 			for(int x=2;x<map.xsize-2;x++)
1747 			{
1748 				map.mapdata[x][map.ysize-1] = TILE_FELLOFFMAP_EP3;
1749 			}
1750 		}
1751 		else
1752 		{
1753 			// coat the bottom of the map below the border.
1754 			// since the border has solidceil=1 this provides
1755 			// a platform to catch enemies that fall off the map
1756 			for(int x=2;x<map.xsize-2;x++)
1757 			{
1758 				map.mapdata[x][map.ysize-1] = TILE_FELLOFFMAP;
1759 			}
1760 		}
1761 	}
1762 
1763 
constructMap(CMap * m)1764 	bool constructMap(CMap* m) {
1765 		m->Name = head.name;
1766 		m->Width = (int)head.width;
1767 		m->Height = (int)head.height;
1768 		m->Type = MPT_IMAGE;
1769 
1770 		// Allocate the map
1771 	createMap:
1772 		if(!m->New(m->Width, m->Height, "dirt")) {
1773 			errors << "CMap::Load (" << filename << "): cannot allocate map" << endl;
1774 			if(cCache.GetEntryCount() > 0) {
1775 				hints << "current cache size is " << cCache.GetCacheSize() << ", we are clearing it now" << endl;
1776 				cCache.Clear();
1777 				goto createMap;
1778 			}
1779 			return false;
1780 		}
1781 
1782 		LOCK_OR_FAIL(m->bmpBackImage);
1783 		LOCK_OR_FAIL(m->bmpImage);
1784 		m->lockFlags();
1785 		for(Uint32 x = 0; x < map.xsize; x++)
1786 			for(Uint32 y = 0; y < map.ysize; y++) {
1787 				if(!withKeenBorders) {
1788 					if(x <= 1 || x+2 >= map.xsize) continue;
1789 					if(y <= 1 || y+2 >= map.ysize) continue;
1790 				}
1791 				Uint16 tile = map.mapdata[x][y];
1792 				Uint16 backtile = tile;
1793 				char pixelflag = getPixelFlag(tile);
1794 				if(pixelflag == PX_DIRT) {
1795 					if(tiles[tile].chgtile > 0)
1796 						backtile = tiles[tile].chgtile;
1797 					else
1798 						backtile = searchNextFreeCell(x, y);
1799 				}
1800 				Uint32 realx = x * TILE_W, realy = y * TILE_H;
1801 				if(!withKeenBorders) { realx -= 2*TILE_W; realy -= 2*TILE_H; }
1802 				fillpixelflags(m->PixelFlags, m->Width, realx, realy, pixelflag);
1803 				sb_drawtile(m->bmpImage.get(), realx, realy, tile);
1804 				sb_drawtile(m->bmpBackImage.get(), realx, realy, backtile);
1805 			}
1806 		m->unlockFlags();
1807 		UnlockSurface(m->bmpImage);
1808 		UnlockSurface(m->bmpBackImage);
1809 
1810 		return true;
1811 	}
1812 
searchNextFreeCell(int x,int y)1813 	Uint16 searchNextFreeCell(int x, int y) {
1814 #define CHECKNRET(_x,_y) { if(getPixelFlag(map.mapdata[_x][_y]) == PX_EMPTY) return map.mapdata[_x][_y]; }
1815 		int d = 1;
1816 		while(y+1 < map.ysize) {
1817 			y++;
1818 			CHECKNRET(x,y);
1819 			for(int dx = 1; dx < d; dx++) {
1820 				if(x+dx < map.xsize) CHECKNRET(x+dx,y);
1821 				if(x>=dx) CHECKNRET(x-dx,y);
1822 			}
1823 			for(int dy = 0; dy <= d; dy++) {
1824 				if(x+d < map.xsize) CHECKNRET(x+d,y-dy);
1825 				if(x>d) CHECKNRET(x-d,y-dy);
1826 			}
1827 			d++;
1828 		}
1829 
1830 		return map.mapdata[x][y];
1831 #undef CHECKNRET
1832 	}
1833 
1834 	SafeVector<Color> palette;
1835 
loadPalette(int episode)1836 	bool loadPalette(int episode) {
1837 		static const std::string palettefn = "data/commanderkeen123/palette.ini";
1838 		int palette_ncolors = 17;
1839 		//ReadInteger(palettefn, "", "NCOLORS", &palette_ncolors, 0);
1840 		if(palette_ncolors <= 0) {
1841 			errors << palettefn << " does not contain any colors" << endl;
1842 			return false;
1843 		}
1844 		palette.resize(palette_ncolors);
1845 		for(int i=0;i<palette_ncolors;i++) {
1846 			std::string key = "EP" + itoa(episode) + "_COLOR" + itoa(i);
1847 			std::string col;
1848 			if(ReadString(palettefn, "", key, col, "")) {
1849 				*palette[i] = StrToCol("#" + col);
1850 			}
1851 		}
1852 		return true;
1853 	}
1854 
getPaletteColor(int c)1855 	Color getPaletteColor(int c)
1856 	{
1857 		Color* col = palette[c];
1858 		if(col) return *col;
1859 		return Color();
1860 	}
1861 
sb_drawtile(SDL_Surface * dest,Uint32 x,Uint32 y,unsigned int t)1862 	void sb_drawtile(SDL_Surface* dest, Uint32 x, Uint32 y, unsigned int t)
1863 	{
1864 		for(Uint8 dy = 0; dy < TILE_H; dy++)
1865 			for(Uint8 dx = 0; dx < TILE_W; dx++) {
1866 				PutPixel(dest, x + dx, y + dy, getPaletteColor(tiledata[t][dy][dx]).get(dest->format));
1867 			}
1868 	}
1869 
getPixelFlag(Uint32 t)1870 	char getPixelFlag(Uint32 t) {
1871 		char flag = tiles[t].solidceil ? PX_ROCK : (tiles[t].solidfall ? PX_DIRT : PX_EMPTY);
1872 		if(flag == PX_ROCK && (map.isworldmap || map.ismenumap))
1873 			flag = PX_DIRT; // otherwise we would have a lot of not-accessible areas
1874 		return flag;
1875 	}
1876 
fillpixelflags(uchar * PixelFlags,Uint32 mapwidth,Uint32 x,Uint32 y,char flag)1877 	void fillpixelflags(uchar* PixelFlags, Uint32 mapwidth, Uint32 x, Uint32 y, char flag) {
1878 		for(Uint8 h = 0; h < TILE_H; h++) {
1879 			memset((char*)&PixelFlags[(y + h) * mapwidth + x], flag, TILE_W);
1880 		}
1881 	}
1882 
1883 };
1884 
1885 
open(const std::string & filename,bool abs_filename,bool printErrors)1886 MapLoader* MapLoader::open(const std::string& filename, bool abs_filename, bool printErrors) {
1887 	FILE* fp = abs_filename ? OpenAbsFile(filename, "rb") : OpenGameFile(filename, "rb");
1888 	if(fp == NULL) {
1889 		if(printErrors) errors << "level " << filename << " does not exist" << endl;
1890 		return NULL;
1891 	}
1892 
1893 	std::string fileext = GetFileExtension(filename); stringlwr(fileext);
1894 	if( fileext == "lxl" )
1895 		return (new ML_LieroX()) -> Set(filename, abs_filename, fp) -> parseHeaderAndCheck(printErrors);;
1896 
1897 	if( fileext == "lev" )
1898 		return (new ML_OrigLiero()) -> Set(filename, abs_filename, fp) -> parseHeaderAndCheck(printErrors);
1899 
1900 	if( fileext == "ck1" || fileext == "ck2" || fileext == "ck3" )
1901 		return (new ML_CommanderKeen123()) -> Set(filename, abs_filename, fp) -> parseHeaderAndCheck(printErrors);
1902 
1903 	// HINT: Other level formats could be added here
1904 
1905 	if(printErrors) errors << "level format of file " << filename << " unknown" << endl;
1906 	return NULL;
1907 }
1908 
1909