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