1 /***************************************************************************
2 xpmladen.cpp - description
3 -------------------
4 begin : Fri Apr 20 2001
5 copyright : (C) 2001 by Immi
6 email : cuyo@karimmi.de
7
8 Modified 2002,2003,2006,2008-2011,2014 by the cuyo developers
9
10 ***************************************************************************/
11
12 /***************************************************************************
13 * *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
18 * *
19 ***************************************************************************/
20
21 #include <cstdlib>
22 #include <cstdio>
23
24 #include "sdltools.h"
25
26 #include "cuyointl.h"
27 #include "fehler.h"
28 #include "xpmladen.h"
29 #include "leveldaten.h"
30 #include "global.h"
31 #include "inkompatibel.h"
32
33 #if HAVE_LIBZ
34 #include <zlib.h>
35 #endif
36
37
38 /* Die nachfolgenden Variablen existieren nur innerhalb von xpmladen.cpp */
39 static char * gDatAnfang;
40 static char * gDatBei;
41 static char * gDatEnde;
42
43 /* True, wenn eine .xpm.gz-Datei gefunden wurde. Diese Var. ist nur
44 n�tig, damit wenn eine gezippte Datei gefunden wurde, die aber nicht
45 von meinem Algorithmus lesbar ist, nicht ein "Datei nicht gefunden"
46 ausgegeben wird. */
47 static bool gDateiGezippt;
48
49
50 /* Damit in ladeDateiLow nicht ein Haufen #ifdefs stehen m�ssen, hier
51 ein paar Dummy-Definitionen f�r wenn's die zlib nicht gibt. */
52
53
54 #if !HAVE_LIBZ
55 typedef int gzFile;
gzopen(char *,char *)56 int gzopen(char *, char *) {return 0;}
gzeof(int)57 bool gzeof(int) {return 0;}
gzread(int,char *,int)58 int gzread(int, char *, int) {return 0;}
gzclose(int)59 void gzclose(int) {}
60 #endif
61
62
63
64 /** Wird von ladeDatei benutzt, um entweder eine normale oder eine
65 gz-Datei zu laden... */
ladeDateiLow(Str na,bool gz)66 bool ladeDateiLow(Str na, bool gz) {
67 FILE * f = 0; // Das "= 0" ist nur um keine Warnungen zu bekommen
68 gzFile gzf = 0;
69
70 /* Datei �ffnen */
71 if (gz) {
72 /* Das "b" bedeutet binary-Datei. */
73 gzf = gzopen(na.data(), "rb");
74 if (!gzf) return false;
75 } else {
76 f = fopen(na.data(), "r");
77 if (!f) return false;
78 }
79
80 int bei = 0;
81
82 while ( gz ? !gzeof(gzf) : !feof(f) ) {
83 /* So viel wollen wir diesmal auf einmal lesen: So viel wie wir
84 schon haben, plus 4096 */
85 int neu = bei + 4096;
86
87 /* Speicher f�r neu zu ladendes alloziieren. (Wenn noch nix
88 geladen war, ist gDatAnfang = 0 und realloc �quivalent zu malloc) */
89 gDatAnfang = (char *) realloc(gDatAnfang, bei + neu);
90
91 if (gz) {
92 bei += neu = gzread(gzf, gDatAnfang + bei, neu);
93
94 if (neu < 0) {
95 gzclose(gzf);
96 /* gzerror() could improve the error message... */
97 throw Fehler("%s","Read error");
98 }
99 } else {
100 bei += fread(gDatAnfang + bei, 1, neu, f);
101 if (ferror(f)) {
102 fclose(f);
103 throw Fehler("%s","Read error");
104 }
105 }
106 }
107
108 /* OK, jetzt ist wahrscheinlich zu viel Speicher alloziiert.
109 Korrigieren wir das mal noch ein bisschen. Das "+1" ist, weil
110 wir am Ende noch eine "0" anh�ngen wollen. */
111 gDatAnfang = (char *) realloc(gDatAnfang, bei + 1);
112
113 gDatEnde = gDatAnfang + bei;
114 *gDatEnde = 0;
115
116 if (gz)
117 gzclose(gzf);
118 else
119 fclose(f);
120
121 return true;
122 }
123
124
125
126 /** L�d die angegebene Datei komplett. Danach zeigt gDatAnfang auf den
127 Anfang und gDatEnde auf das Ende. F�gt au�erdem noch eine 0 ans Ende
128 an.
129 Liefert false, wenn die Datei nicht gefunden wird.
130 Sucht auch nach der Datei na.gz. */
ladeDatei(Str na)131 bool ladeDatei(Str na) {
132
133 /* Erst mal versuchen, eine ungezippte Datei zu laden. */
134 if (ladeDateiLow(na, false)) return true;
135
136 /* Und dann eine gezippte. Aber nur mit zlib */
137 #if HAVE_LIBZ
138 if (ladeDateiLow(na + ".gz", true)) {
139 gDateiGezippt = true;
140 return true;
141 }
142 #endif
143
144 return false;
145 }
146
147
148
leerWeg()149 void leerWeg() {
150 while (*gDatBei == ' ' || *gDatBei == '\t' || *gDatBei == '\n' || *gDatBei == '\r')
151 gDatBei++;
152 }
153
leerUndKommentarWeg()154 void leerUndKommentarWeg() {
155 while (1) {
156 leerWeg();
157 /* Kein Kommentar-Anfang? Dann fertig */
158 if (gDatBei[0] != '/' || gDatBei[1] != '*')
159 return;
160 /* Kommentar weglesen */
161 gDatBei += 2;
162 while (gDatBei[0] != '*' || gDatBei[1] != '/') {
163 if (!gDatBei[0])
164 throw Fehler("%s","Endless comment.");
165 gDatBei++;
166 }
167 gDatBei += 2;
168 }
169 }
170
171 /** Erwartet, dass ein s kommt (davor ist whitespace erlaubt) */
erwarte(const char * s)172 void erwarte(const char * s) {
173 const char *t = s;
174 while (*s) {
175 if (*gDatBei != *s)
176 throw Fehler("\"%s\" expected", t);
177 gDatBei++;
178 s++;
179 }
180 }
181
182
183 /** Das gleiche mit char */
erwarte(char c)184 void erwarte(char c) {
185 if (*gDatBei != c)
186 throw Fehler("'%c' expected; found: '%c' (filepos: %d)", c, *gDatBei,
187 (int) (gDatBei - gDatAnfang));
188 gDatBei++;
189 }
190
191
192
193 /** Liest so lange, bis ein c auftaucht. */
liesBis(char c)194 void liesBis(char c) {
195 while (*gDatBei != c) {
196 if (*gDatBei == 0)
197 throw Fehler("'%c' expected", c);
198 gDatBei++;
199 }
200 gDatBei++;
201 }
202
203
204 /** Pr�ft, ob der String s jetzt kommt. Wenn ja, wird
205 er weggelesen */
kommtString(const char * s)206 bool kommtString(const char * s) {
207 char * merk = gDatBei;
208 while (*s) {
209 if (*gDatBei != *s) {
210 gDatBei = merk;
211 return false;
212 }
213 gDatBei++;
214 s++;
215 }
216 return true;
217 }
218
219
220
getInt()221 int getInt() {
222 int ret = 0;
223 bool geht = false;
224 leerWeg();
225 while (*gDatBei >= '0' && *gDatBei <= '9') {
226 ret = ret * 10 + *gDatBei - '0';
227 gDatBei++;
228 geht = true;
229 }
230 if (!geht)
231 throw Fehler("%s","Number expected");
232 return ret;
233 }
234
235
236
decodeHex1(char a)237 int decodeHex1(char a) {
238 if (a >= '0' && a <= '9')
239 return a - '0';
240 if (a >= 'A' && a <= 'F')
241 return a - 'A' + 10;
242 if (a >= 'a' && a <= 'f')
243 return a - 'a' + 10;
244 throw Fehler("%s","Hex number expected");
245 }
246
getHex()247 int getHex() {
248 int ret = decodeHex1(*gDatBei++) * 16;
249 return ret + decodeHex1(*gDatBei++);
250 }
251
252
253
254
255 struct info {
256 Uint32 farbcode;
257 bool hintergrund;
258 };
259
260 union suchbaum {
261 info einziges;
262 info blatt[256];
263 suchbaum * weiter[256];
264
suchbaum(int tiefe)265 suchbaum(int tiefe) {
266 if (tiefe>1)
267 for (int i=0; i<256; i++) weiter[i]=NULL;
268 }
269
rein(int tiefe)270 inline info* rein(int tiefe) {
271 if (tiefe==0)
272 return &einziges;
273 suchbaum* such = this;
274 for (;tiefe-->1;) {
275 if (!such->weiter[(unsigned int)*gDatBei])
276 such->weiter[(unsigned int)*gDatBei] = new suchbaum(tiefe);
277 such = such->weiter[(unsigned int)*gDatBei++];
278 }
279 return (such->blatt) + (*gDatBei++);
280 }
281
raus(int tiefe)282 inline info raus(int tiefe) {
283 if (tiefe==0)
284 return einziges;
285 suchbaum* such = this;
286 for (;tiefe-->1;) {
287 if (!such->weiter[(unsigned int)*gDatBei])
288 throw Fehler("%s","Undefined pixel name");
289 such = such->weiter[(unsigned int)*gDatBei++];
290 }
291 return such->blatt[(unsigned int)*gDatBei++];
292 }
293
loesch(int tiefe)294 void loesch(int tiefe) {
295 if (tiefe-->1)
296 for (int i=0; i<256; i++)
297 if (weiter[i]) {
298 weiter[i]->loesch(tiefe);
299 delete weiter[i];
300 }
301 }
302 };
303
304
305
306
307
308
309 /* Versucht die Datei na zu laden.
310 Versucht au�erdem, die Datei na.gz zu laden.
311 Liefert 0, wenn keine der Dateien existiert.
312 Throwt, wenn's beim Laden einen Fehler gibt.
313 (Falls die SDL-Lad-Routine verwendet wird, kann nicht versucht werden,
314 die .gz-Datei zu laden.) */
ladXPM(Str na,RohMaske & maske)315 SDL_Surface * ladXPM(Str na, RohMaske & maske) {
316 SDL_Surface * s;
317
318 gDatAnfang = 0;
319
320 gDateiGezippt = false;
321
322 /* Das nachfolgende try-catch ist 1. um evtl Speicher freizugeben
323 und zweitens, weil wir es dann nochmal mit der SDL-Laderoutine
324 versuchen wollen. */
325 try {
326
327 /* Datei laden. Dabei werden gDatAnfang und gDatEnde gesetzt. */
328 if (!ladeDatei(na)) return 0;
329
330
331 gDatBei = gDatAnfang;
332
333 /* XPM-Kommentar-Zeile lesen */
334 leerWeg();
335 erwarte("/*");
336 leerWeg();
337 erwarte("XPM");
338 leerWeg();
339 erwarte("*/");
340 /* Alles bis zur ersten { entfernen */
341 liesBis('{');
342
343 /* OK, jetzt sind wir im interessanten Bereich. */
344 /* "groesse_x groesse_y farbzahl charpp" parsen. */
345 leerUndKommentarWeg();
346 erwarte('"');
347
348 int groesse_x, groesse_y, farb, tiefe;
349 groesse_x = getInt();
350 groesse_y = getInt();
351 farb = getInt();
352 tiefe = getInt();
353 bool monochrom = tiefe==0;
354 leerWeg();
355 erwarte('"');
356
357 s = SDLTools::createSurface32(groesse_x, groesse_y);
358 SDL_PixelFormat * pf = s->format;
359
360 maske.init(groesse_x,groesse_y);
361
362 /* Farben parsen */
363
364 suchbaum farben(tiefe);
365
366 for (int i = 0; i < farb; i++) {
367 leerUndKommentarWeg();
368 erwarte(',');
369 leerUndKommentarWeg();
370 erwarte('"');
371
372 info* index = farben.rein(tiefe);
373 leerWeg();
374 char typ = *gDatBei++;
375 CASSERT(typ == 'c');
376 leerWeg();
377
378 index->hintergrund=false;
379 if (kommtString("None")) {
380 // durchsichtig
381 index->farbcode = SDL_MapRGBA(pf, 0, 0, 0, 0);
382 } else if (kommtString("black")) {
383 index->farbcode = SDL_MapRGBA(pf, 0, 0, 0, 255);
384 } else if (kommtString("white")) {
385 index->farbcode = SDL_MapRGBA(pf, 255, 255, 255, 255);
386 } else if (kommtString("Background")) {
387 // Farbe vom Level-Hintergrund (f�r Explosion)
388 index->farbcode = ld->mHintergrundFarbe.getPixel(pf);
389 index->hintergrund = true;
390 // } else if (kommtString("FontDark")) {
391 // // Farbe der Schrift in dem Level (f�r Punkt-Ziffern)
392 // *index = ld->mSchriftFarbe[schrift_dunkel].getPixel(pf);
393 // } else if (kommtString("Font")) {
394 // // Farbe der Schrift in dem Level (f�r Punkt-Ziffern)
395 // *index = ld->mSchriftFarbe[schrift_normal].getPixel(pf);
396 // } else if (kommtString("FontLight")) {
397 // // Farbe der Schrift in dem Level (f�r Punkt-Ziffern)
398 // *index = ld->mSchriftFarbe[schrift_hell].getPixel(pf);
399 } else {
400 erwarte('#');
401 int f_r = getHex();
402 int f_g = getHex();
403 int f_b = getHex();
404 index->farbcode = SDL_MapRGBA(pf, f_r, f_g, f_b, 255);
405 }
406 liesBis('"');
407 }
408
409 SDL_LockSurface(s); /* Damit wir Pixel direkt bearbeiten duerfen */
410
411 if (monochrom) {
412 if (farb) {
413 info farbe = farben.raus(tiefe);
414 for (int y=0; y<groesse_y; y++)
415 for (int x=0; x<groesse_x; x++)
416 SDLTools::getPixel32(s,x,y) = farbe.farbcode;
417 maske.fill(farbe.hintergrund);
418 };
419 } else
420 for (int y = 0; y < groesse_y; y++) {
421 leerUndKommentarWeg();
422 erwarte(',');
423 leerUndKommentarWeg();
424 erwarte('"');
425 for (int x = 0; x < groesse_x; x++) {
426 info farbe = farben.raus(tiefe);
427 SDLTools::getPixel32(s, x, y) = farbe.farbcode;
428 maske.set_pixel(x,y,farbe.hintergrund);
429 };
430 erwarte('"');
431 }
432
433 SDL_UnlockSurface(s);
434
435 farben.loesch(tiefe);
436
437 /* Angeblich d�rfen jetzt noch Extensionen von xpm kommen. Da k�mmern wir
438 uns nicht weiter drum. */
439
440 free(gDatAnfang);
441
442 } catch (Fehler f) {
443 if (gDatAnfang) free(gDatAnfang);
444
445 if (gDebug)
446 print_to_stderr(_sprintf("Fast xpm loading did not work for \"%s\":\n%s\n",
447 na.data(), f.getText().data()));
448
449 /* Ich habe im Nachfolgenden IMG_Load-Verwendung auskommentiert:
450 IMG_Load scheint nicht zu funkionieren (segmentation fault).
451 Oder vielleicht funktioniert es auch, aber es reagiert mit segfault
452 auf Dateien, die es nicht lesen kann. Deshalb: Wenn wir die Datei
453 nicht selbst lesen koennen, dann lieber den Fehler ausgeben als
454 IMG_Load aufrufen. */
455 // if (gDateiGezippt) {
456 /* Bei einer gezippten Datei geht fallBackLaden nicht... */
457 // TRANSLATORS: This is to prepend an error message
458 throw Fehler("File \"%s.gz\": %s\n", na.data(), f.getText().data());
459 // } else {
460 /* Die sdl-Laderoutine verwenden. Waere eh zu pruefen ob die nicht besser ist als
461 unsere eigene */
462 // return IMG_Load(na.data());
463 // }
464 }
465
466
467 return s;
468 }
469