sound extension fixed
[awish.git] / src / resfile.c
blob00435d1d9037e9af78b8b118029bf6390de2f670
1 /*
2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation, either version 3 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 #ifdef _WIN32
16 # include <windows.h>
17 #endif
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <sys/types.h>
24 #include "librnc/librnc.h"
26 #include "resfile.h"
29 #ifndef DATA_DIR
30 # define DATA_DIR "/usr/local/share/awish"
31 #endif
34 extern int goobers;
35 extern int datfirst;
38 static const char *getMyDir (void) {
39 static char myDir[8192];
40 static int inited = 0;
42 if (!inited) {
43 #ifndef _WIN32
44 pid_t pid = getpid();
45 char buf[128];
47 sprintf(buf, "/proc/%u/exe", (unsigned int)pid);
48 if (readlink(buf, myDir, sizeof(myDir)-1) < 0) {
49 strcpy(myDir, ".");
50 } else {
51 char *p = (char *)strrchr(myDir, '/');
53 if (!p) strcpy(myDir, "."); else *p = '\0';
55 strcat(myDir, "/data");
56 #else
57 char *p;
59 memset(myDir, 0, sizeof(myDir));
60 GetModuleFileName(GetModuleHandle(NULL), myDir, sizeof(myDir)-1);
61 for (p = myDir; *p; ++p) if (*p == '/') *p = '\\';
62 p = strrchr(myDir, '\\');
63 if (!p) strcpy(myDir, "."); else *p = '\0';
64 strcat(myDir, "\\data");
65 for (p = myDir; *p; ++p) if (*p == '\\') *p = '/';
66 #endif
67 inited = 1;
69 return myDir;
73 #ifndef _WIN32
74 static const char *getHomeDir (void) {
75 static char homeDir[8192];
76 static int inited = 0;
78 if (!inited) {
79 #ifndef _WIN32
80 const char *h = getenv("HOME");
82 strcpy(homeDir, (h && h[0]) ? h : ".");
83 strcat(homeDir, "/.local/awish/data");
84 #else
85 // fuck windoze
86 strcpy(homeDir, ".");
87 for (char *p = myDir; *p; ++p) if (*p == '\\') *p = '/';
88 #endif
89 inited = 1;
91 return homeDir;
93 #endif
96 static int tryInitResFile (ResFile *resfile, const char *fname) {
97 uint8_t fcnt;
98 uint8_t sign[3];
100 FILE *fl = fopen(fname, "rb");
101 int rr = -1, sz;
103 if (!fl) return -1;
104 if (!resfile) goto quit;
105 memset(resfile, 0, sizeof(resfile));
107 if (fread(sign, 3, 1, fl) != 1) goto quit;
108 if (memcmp(sign, "RES", 3) != 0) goto quit;
109 if (fread(&fcnt, 1, 1, fl) != 1) goto quit;
110 if (fcnt < 93) goto quit;
112 resfile->fl = fl;
113 resfile->count = fcnt;
115 if ((resfile->offsets = malloc(4*resfile->count)) == NULL) goto quit;
116 if ((resfile->sizes = malloc(4*resfile->count)) == NULL) goto quit;
118 if (fread(resfile->offsets, 4*resfile->count, 1, fl) != 1) goto quit;
119 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
120 for (int f = 0; f < resfile->count; ++f) resfile->offsets[f] = SDL_SwapLE32(resfile->offsets[f]);
121 #endif
122 if (fseek(fl, 0, SEEK_END) != 0) goto quit;
123 sz = ftell(fl);
124 for (int f = 0; f < resfile->count-1; ++f) resfile->sizes[f] = resfile->offsets[f+1]-resfile->offsets[f];
125 resfile->sizes[resfile->count-1] = sz-resfile->offsets[resfile->count-1];
127 fl = NULL;
128 rr = 0;
129 quit:
130 if (fl != NULL) {
131 fclose(fl);
132 if (resfile->sizes) free(resfile->sizes);
133 if (resfile->offsets) free(resfile->offsets);
134 memset(resfile, 0, sizeof(resfile));
136 return rr;
140 int initResFile (ResFile *resfile, const char *rfname) {
141 static char fname[8192];
142 int res = -1;
144 #ifndef _WIN32
145 sprintf(fname, "%s/%s", getHomeDir(), rfname);
146 res = tryInitResFile(resfile, fname);
147 #endif
148 if (res != 0) {
149 sprintf(fname, "%s/%s", getMyDir(), rfname);
150 res = tryInitResFile(resfile, fname);
152 #ifndef _WIN32
153 if (res != 0) {
154 sprintf(fname, "%s/%s", DATA_DIR, rfname);
155 res = tryInitResFile(resfile, fname);
157 #endif
158 if (res != 0) {
159 char *t;
161 strcpy(fname, getMyDir());
162 t = strrchr(fname, '/');
163 if (t) t[1] = 0;
164 strcat(fname, rfname);
165 res = tryInitResFile(resfile, fname);
168 if (goobers) {
169 if (res == 0) fprintf(stderr, "using resource file '%s'\n", fname);
170 else fprintf(stderr, "can't find resource file '%s'\n", rfname);
172 return res;
176 void deinitResFile (ResFile *resfile) {
177 if (resfile) {
178 if (resfile->fl) fclose(resfile->fl);
179 if (resfile->sizes) free(resfile->sizes);
180 if (resfile->offsets) free(resfile->offsets);
181 memset(resfile, 0, sizeof(resfile));
186 typedef int (*UnpackCB) (unsigned char *dest, const unsigned char *data, int size);
189 static int unpackFNTFont (unsigned char *dest, const unsigned char *data, int size) {
190 Uint8 fc, lc;
191 int pos;
193 memset(dest, 0, 256*8);
194 if (size < 6) goto quit;
195 if (memcmp(data, "FNT0", 4) != 0) goto quit;
196 fc = data[4];
197 lc = data[5];
198 if (fc > lc) goto quit;
199 pos = 6;
200 for (int f = fc; f <= lc; ++f) {
201 if (pos+8 > size) goto quit;
202 memcpy(dest+f*8, data+pos, 8);
203 pos += 8;
205 return 0;
206 quit:
207 memset(dest, 0, 256*8);
208 return -1;
212 static int unpackBINFont (unsigned char *dest, const unsigned char *data, int size) {
213 memset(dest, 0, 256*8);
214 if (size < 90*8) return -1;
215 memcpy(dest+33*8, data, 90*8);
216 return 0;
220 static int unpackVPI (unsigned char *dest, const unsigned char *data, int size) {
221 if (size >= 320*200) {
222 memcpy(dest, data, 320*200);
223 } else {
224 int pos = 0; // in packed
225 int opos = 0; // in unpacked
226 int cpos = 0; // meaningless here (position of colormap)
228 while (opos < 320*200 && pos+2 <= size) {
229 int csz = ((uint32_t)(data[pos]))+256*((uint32_t)(data[pos+1])); // picture chunk size
231 cpos = (pos += 2); // colormap
232 pos += 512; // skip colormap
233 while (opos < 320*200 && pos < size && csz > 0) {
234 int idx = data[pos++];
236 dest[opos++] = data[cpos+idx*2+0];
237 dest[opos++] = data[cpos+idx*2+1];
238 --csz;
242 return 0;
246 uint8_t *tryDiskFile (const char *fname, int *rsz) {
247 FILE *fl = fopen(fname, "rb");
248 uint8_t *res = NULL;
249 long size;
251 if (rsz) *rsz = 0;
252 if (fl == NULL) return NULL;
253 if (goobers) fprintf(stderr, "trying disk resource: '%s'\n", fname);
254 if (fseek(fl, 0, SEEK_END) != 0) goto quit;
255 size = ftell(fl);
256 rewind(fl);
257 if (size < 0) goto quit;
259 res = calloc(1, size+16);
260 if (res == NULL) goto quit;
261 if (fread(res, size, 1, fl) != 1) goto quit;
263 if (size >= 18 && rnc_sign(res)) {
264 // unpack RNC file
265 int ulen, uplen;
266 uint8_t *udata;
268 ulen = rnc_ulen(res);
269 if (ulen < 1) goto quit;
270 udata = malloc(ulen+16);
271 if (udata == NULL) goto quit;
272 uplen = rnc_unpack(res, udata, NULL);
273 free(res);
274 res = udata;
275 //if (goobers) fprintf(stderr, "RNC: %d -> %d (%d)\n", (int)size, ulen, uplen);
276 if (uplen != ulen) goto quit;
277 size = ulen;
279 fclose(fl);
280 fl = NULL;
282 quit:
283 if (fl != NULL) {
284 fclose(fl);
285 if (res != NULL) { free(res); res = NULL; }
286 } else {
287 if (res != NULL && rsz) *rsz = size;
289 return res;
293 static uint8_t *tryDiskFileEx (const char *fname, int *rsz, UnpackCB unp, int destsize) {
294 uint8_t *res, *up;
295 int sz;
297 if ((res = tryDiskFile(fname, &sz)) == NULL) return NULL;
299 if (!unp) {
300 if (destsize >= 0 && sz < destsize) { free(res); return NULL; }
301 } else {
303 if (destsize < 0) { free(res); return NULL; }
304 if ((up = calloc(1, destsize+16)) == NULL) { free(res); return NULL; }
305 if (unp(up, res, sz) != 0) { free(up); free(res); return NULL; }
306 free(res);
307 res = up;
308 sz = destsize;
310 if (rsz) *rsz = sz;
311 return res;
315 #define TRY_FILE(upk,usz,fmt,srcdir,...) do { \
316 sprintf(fname, (fmt), (srcdir), __VA_ARGS__); \
317 if ((res = tryDiskFileEx(fname, rsz, (upk), (usz))) != NULL) return res; \
318 } while (0)
321 #ifndef _WIN32
323 #define TRY_FILES(upk,usz,fmt,...) do { \
324 TRY_FILE((upk), (usz), "%s/" fmt, getHomeDir(), __VA_ARGS__); \
325 TRY_FILE((upk), (usz), "%s/" fmt, getMyDir(), __VA_ARGS__); \
326 TRY_FILE((upk), (usz), "%s/" fmt, DATA_DIR, __VA_ARGS__); \
327 } while (0)
329 #else
331 #define TRY_FILES(upk,usz,fmt,...) do { \
332 TRY_FILE((upk), (usz), "%s/" fmt, getMyDir(), __VA_ARGS__); \
333 } while (0)
335 #endif
338 static uint8_t *tryLocalCodeFile (int *rsz) {
339 static char fname[512];
340 char *t;
342 strcpy(fname, getMyDir());
343 t = strrchr(fname, '/');
344 if (t) *t = 0;
345 strcat(fname, "/awish.vmd");
346 return tryDiskFileEx(fname, rsz, NULL, 8);
350 static uint8_t *loadDiskFile (int idx, int *rsz) {
351 static char fname[512];
352 uint8_t *res;
354 if (rsz) *rsz = 0;
355 if (idx < 0) return NULL;
357 if (idx >= 0 && idx <= 6) {
358 TRY_FILES(unpackVPI, 64000, "pics/back%02d.vpi", idx+1);
359 return NULL;
362 if (idx == 7) {
363 TRY_FILES(unpackVPI, 64000, "pics/title.vpi%s", "");
364 return NULL;
367 if (idx == 8) {
368 TRY_FILES(unpackVPI, 64000, "pics/interback.vpi%s", "");
369 return NULL;
372 if (idx >= 9 && idx <= 82) {
373 TRY_FILES(NULL, 3700, "maps/level%02d.lvl", idx-8);
374 return NULL;
377 if (idx == 83) {
378 TRY_FILES(NULL, 768, "pics/palette.vga%s", "");
379 return NULL;
382 if (idx == 84) {
383 TRY_FILES(NULL, 50568, "sprites/professor.spr%s", "");
384 return NULL;
387 if (idx == 85) {
388 TRY_FILES(NULL, 3468, "sprites/fgtiles.spr%s", "");
389 return NULL;
392 if (idx == 86) {
393 TRY_FILES(NULL, 2652, "sprites/bgtiles.spr%s", "");
394 return NULL;
397 if (idx == 87) {
398 TRY_FILES(NULL, 588, "sprites/maptiles.spr%s", "");
399 return NULL;
402 if (idx == 88) {
403 TRY_FILES(NULL, 19796, "sprites/items.spr%s", "");
404 return NULL;
407 if (idx == 89) {
408 TRY_FILES(NULL, 620, "sprites/mapitems.spr%s", "");
409 return NULL;
412 if (idx == 90) {
413 TRY_FILES(NULL, 44940, "sprites/professorim.spr%s", "");
414 return NULL;
417 if (idx == 91) {
418 TRY_FILES(NULL, 9441, "sprites/himenu.spr%s", "");
419 return NULL;
422 if (idx == 92) {
423 TRY_FILES(unpackFNTFont, 256*8, "fonts/namco.fnt%s", "");
424 TRY_FILES(unpackFNTFont, 256*8, "fonts/font.fnt%s", "");
425 TRY_FILES(unpackBINFont, 256*8, "fonts/font.bin%s", "");
426 return NULL;
429 if (idx == 666 || idx == 93) {
430 if (goobers) {
431 if ((res = tryLocalCodeFile(rsz)) != NULL) return res;
434 TRY_FILES(NULL, 8, "code/awish.vmd%s", "");
436 if (!goobers) {
437 if ((res = tryLocalCodeFile(rsz)) != NULL) return res;
439 return NULL;
441 return NULL;
445 static uint8_t *tryResFile (ResFile *resfile, int idx, int *rsz, UnpackCB unp, int destsize) {
446 uint8_t *res = NULL, *up;
447 int size;
449 if (rsz) *rsz = 0;
450 if (!resfile || !resfile->fl || idx < 0 || idx >= resfile->count) return NULL;
452 if (goobers) fprintf(stderr, "trying RES resource: %d\n", idx);
453 //fprintf(stderr, "idx=%d; ofs=%u; size=%u\n", idx, resfile->offsets[idx], resfile->sizes[idx]);
454 size = resfile->sizes[idx];
455 if (fseek(resfile->fl, resfile->offsets[idx], SEEK_SET) != 0) return NULL;
456 if ((res = (uint8_t *)calloc(1, size+16)) == NULL) return NULL;
457 if (size > 0) {
458 if (fread(res, size, 1, resfile->fl) != 1) { free(res); return NULL; }
461 if (size >= 18 && rnc_sign(res)) {
462 // unpack RNC file
463 int ulen, uplen;
464 uint8_t *udata;
466 ulen = rnc_ulen(res);
467 if (ulen < 1) { free(res); return NULL; }
468 udata = malloc(ulen+16);
469 if (udata == NULL) { free(res); return NULL; }
470 uplen = rnc_unpack(res, udata, NULL);
471 free(res);
472 res = udata;
473 if (uplen != ulen) { free(res); return NULL; }
474 size = ulen;
477 if (!unp) {
478 if (destsize >= 0 && size < destsize) { free(res); return NULL; }
479 } else {
480 if (destsize < 0) { free(res); return NULL; }
481 if ((up = calloc(1, destsize+16)) == NULL) { free(res); return NULL; }
482 if (unp(up, res, size) != 0) { free(up); free(res); return NULL; }
483 free(res);
484 res = up;
485 size = destsize;
488 if (rsz) *rsz = size;
489 return res;
493 uint8_t *loadResFile (ResFile *resfile, int idx, int *rsz) {
494 uint8_t *res = NULL;
496 if (!datfirst) res = loadDiskFile(idx, rsz);
498 if (res != NULL || resfile == NULL) return res;
500 if (idx >= 0 && idx < resfile->count) {
501 switch (idx) {
502 case 0 ... 8:
503 if (resfile->sizes[idx] < 320*200) {
504 res = tryResFile(resfile, idx, rsz, unpackVPI, 320*200);
505 } else {
506 res = tryResFile(resfile, idx, rsz, NULL, 320*200);
508 break;
509 case 9 ... 82:
510 res = tryResFile(resfile, idx, rsz, NULL, 3700);
511 break;
512 case 83:
513 res = tryResFile(resfile, idx, rsz, NULL, 768);
514 break;
515 case 84:
516 res = tryResFile(resfile, idx, rsz, NULL, 50568);
517 break;
518 case 85:
519 res = tryResFile(resfile, idx, rsz, NULL, 3468);
520 break;
521 case 86:
522 res = tryResFile(resfile, idx, rsz, NULL, 2652);
523 break;
524 case 87:
525 res = tryResFile(resfile, idx, rsz, NULL, 588);
526 break;
527 case 88:
528 res = tryResFile(resfile, idx, rsz, NULL, 19796);
529 break;
530 case 89:
531 res = tryResFile(resfile, idx, rsz, NULL, 620);
532 break;
533 case 90:
534 res = tryResFile(resfile, idx, rsz, NULL, 44940);
535 break;
536 case 91:
537 res = tryResFile(resfile, idx, rsz, NULL, 9441);
538 break;
539 case 92:
540 res = tryResFile(resfile, idx, rsz, unpackBINFont, 256*8);
541 break;
542 default:
543 res = tryResFile(resfile, idx, rsz, NULL, -1);
544 break;
548 if (!res && datfirst) res = loadDiskFile(idx, rsz);
550 return res;