some more utilities for librnc
[awish.git] / src / resfile.c
blobb7606f50ec11378cde5d36c7529444b39a564a90
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 #endif
66 inited = 1;
68 return myDir;
72 #ifndef _WIN32
73 static const char *getHomeDir (void) {
74 static char homeDir[8192];
75 static int inited = 0;
77 if (!inited) {
78 #ifndef _WIN32
79 const char *h = getenv("HOME");
81 strcpy(homeDir, (h && h[0]) ? h : ".");
82 strcat(homeDir, "/.local/awish/data");
83 #else
84 // fuck windoze
85 strcpy(homeDir, ".");
86 #endif
87 inited = 1;
89 return homeDir;
91 #endif
94 int initResFile (ResFile *resfile, const char *fname) {
95 uint8_t fcnt;
96 uint8_t sign[3];
97 FILE *fl = fopen(fname, "rb");
98 int rr = -1, sz;
100 if (!fl) return -1;
101 if (!resfile) goto quit;
102 memset(resfile, 0, sizeof(resfile));
104 if (fread(sign, 3, 1, fl) != 1) goto quit;
105 if (memcmp(sign, "RES", 3) != 0) goto quit;
106 if (fread(&fcnt, 1, 1, fl) != 1) goto quit;
107 if (fcnt != 93) goto quit;
109 resfile->fl = fl;
110 resfile->count = fcnt;
112 if ((resfile->offsets = malloc(4*resfile->count)) == NULL) goto quit;
113 if ((resfile->sizes = malloc(4*resfile->count)) == NULL) goto quit;
115 if (fread(resfile->offsets, 4*resfile->count, 1, fl) != 1) goto quit;
116 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
117 for (int f = 0; f < resfile->count; ++f) resfile->offsets[f] = SDL_SwapLE32(resfile->offsets[f]);
118 #endif
119 if (fseek(fl, 0, SEEK_END) != 0) goto quit;
120 sz = ftell(fl);
121 for (int f = 0; f < resfile->count-1; ++f) resfile->sizes[f] = resfile->offsets[f+1]-resfile->offsets[f];
122 resfile->sizes[resfile->count-1] = sz-resfile->offsets[resfile->count-1];
124 fl = NULL;
125 rr = 0;
126 quit:
127 if (fl != NULL) {
128 fclose(fl);
129 if (resfile->sizes) free(resfile->sizes);
130 if (resfile->offsets) free(resfile->offsets);
131 memset(resfile, 0, sizeof(resfile));
133 return rr;
137 void deinitResFile (ResFile *resfile) {
138 if (resfile) {
139 if (resfile->fl) fclose(resfile->fl);
140 if (resfile->sizes) free(resfile->sizes);
141 if (resfile->offsets) free(resfile->offsets);
142 memset(resfile, 0, sizeof(resfile));
147 typedef int (*UnpackCB) (unsigned char *dest, const unsigned char *data, int size);
150 static int unpackFNTFont (unsigned char *dest, const unsigned char *data, int size) {
151 Uint8 fc, lc;
152 int pos;
154 memset(dest, 0, 256*8);
155 if (size < 6) goto quit;
156 if (memcmp(data, "FNT0", 4) != 0) goto quit;
157 fc = data[4];
158 lc = data[5];
159 if (fc > lc) goto quit;
160 pos = 6;
161 for (int f = fc; f <= lc; ++f) {
162 if (pos+8 > size) goto quit;
163 memcpy(dest+f*8, data+pos, 8);
164 pos += 8;
166 return 0;
167 quit:
168 memset(dest, 0, 256*8);
169 return -1;
173 static int unpackBINFont (unsigned char *dest, const unsigned char *data, int size) {
174 memset(dest, 0, 256*8);
175 if (size < 90*8) return -1;
176 memcpy(dest+33*8, data, 90*8);
177 return 0;
181 static int unpackVPI (unsigned char *dest, const unsigned char *data, int size) {
182 if (size >= 320*200) {
183 memcpy(dest, data, 320*200);
184 } else {
185 int pos = 0; // in packed
186 int opos = 0; // in unpacked
187 int cpos = 0; // meaningless here (position of colormap)
189 while (opos < 320*200 && pos+2 <= size) {
190 int csz = ((uint32_t)(data[pos]))+256*((uint32_t)(data[pos+1])); // picture chunk size
192 cpos = (pos += 2); // colormap
193 pos += 512; // skip colormap
194 while (opos < 320*200 && pos < size && csz > 0) {
195 int idx = data[pos++];
197 dest[opos++] = data[cpos+idx*2+0];
198 dest[opos++] = data[cpos+idx*2+1];
199 --csz;
203 return 0;
207 uint8_t *tryDiskFile (const char *fname, int *rsz) {
208 FILE *fl = fopen(fname, "rb");
209 uint8_t *res = NULL;
210 long size;
212 if (rsz) *rsz = 0;
213 if (fl == NULL) return NULL;
214 if (goobers) fprintf(stderr, "trying disk resource: '%s'\n", fname);
215 if (fseek(fl, 0, SEEK_END) != 0) goto quit;
216 size = ftell(fl);
217 rewind(fl);
218 if (size < 0) goto quit;
220 res = calloc(1, size+16);
221 if (res == NULL) goto quit;
222 if (fread(res, size, 1, fl) != 1) goto quit;
224 if (size >= 18 && rnc_sign(res)) {
225 // unpack RNC file
226 int ulen, uplen;
227 uint8_t *udata;
229 ulen = rnc_ulen(res);
230 if (ulen < 1) goto quit;
231 udata = malloc(ulen+16);
232 if (udata == NULL) goto quit;
233 uplen = rnc_unpack(res, udata, NULL);
234 free(res);
235 res = udata;
236 if (goobers) fprintf(stderr, "RNC: %d -> %d (%d)\n", (int)size, ulen, uplen);
237 if (uplen != ulen) goto quit;
238 size = ulen;
240 fclose(fl);
241 fl = NULL;
243 quit:
244 if (fl != NULL) {
245 fclose(fl);
246 if (res != NULL) { free(res); res = NULL; }
247 } else {
248 if (res != NULL && rsz) *rsz = size;
250 return res;
254 static uint8_t *tryDiskFileEx (const char *fname, int *rsz, UnpackCB unp, int destsize) {
255 uint8_t *res, *up;
256 int sz;
258 if ((res = tryDiskFile(fname, &sz)) == NULL) return NULL;
260 if (!unp) {
261 if (destsize >= 0 && sz < destsize) { free(res); return NULL; }
262 } else {
264 if (destsize < 0) { free(res); return NULL; }
265 if ((up = calloc(1, destsize+16)) == NULL) { free(res); return NULL; }
266 if (unp(up, res, sz) != 0) { free(up); free(res); return NULL; }
267 free(res);
268 res = up;
269 sz = destsize;
271 if (rsz) *rsz = sz;
272 return res;
276 #define TRY_FILE(upk,usz,fmt,srcdir,...) do { \
277 sprintf(fname, (fmt), (srcdir), __VA_ARGS__); \
278 if ((res = tryDiskFileEx(fname, rsz, (upk), (usz))) != NULL) return res; \
279 } while (0)
282 #ifndef _WIN32
284 #define TRY_FILES(upk,usz,fmt,...) do { \
285 TRY_FILE((upk), (usz), "%s/" fmt, getHomeDir(), __VA_ARGS__); \
286 TRY_FILE((upk), (usz), "%s/" fmt, getMyDir(), __VA_ARGS__); \
287 TRY_FILE((upk), (usz), "%s/" fmt, DATA_DIR, __VA_ARGS__); \
288 } while (0)
290 #else
292 #define TRY_FILES(upk,usz,fmt,...) do { \
293 TRY_FILE((upk), (usz), "%s/" fmt, getMyDir(), __VA_ARGS__); \
294 } while (0)
296 #endif
299 static uint8_t *tryLocalCodeFile (int *rsz) {
300 static char fname[512];
301 char *t;
303 strcpy(fname, getMyDir());
304 t = strrchr(fname, '/');
305 if (t) *t = 0;
306 strcat(fname, "/awish.vmd");
307 return tryDiskFileEx(fname, rsz, NULL, 8);
311 static uint8_t *loadDiskFile (int idx, int *rsz) {
312 static char fname[512];
313 uint8_t *res;
315 if (rsz) *rsz = 0;
316 if (idx < 0) return NULL;
318 if (idx >= 0 && idx <= 6) {
319 TRY_FILES(unpackVPI, 64000, "pics/back%02d.vpi", idx+1);
320 return NULL;
323 if (idx == 7) {
324 TRY_FILES(unpackVPI, 64000, "pics/title.vpi%s", "");
325 return NULL;
328 if (idx == 8) {
329 TRY_FILES(unpackVPI, 64000, "pics/interback.vpi%s", "");
330 return NULL;
333 if (idx >= 9 && idx <= 82) {
334 TRY_FILES(NULL, 3700, "maps/level%02d.lvl", idx-8);
335 return NULL;
338 if (idx == 83) {
339 TRY_FILES(NULL, 768, "pics/palette.vga%s", "");
340 return NULL;
343 if (idx == 84) {
344 TRY_FILES(NULL, 50568, "sprites/professor.spr%s", "");
345 return NULL;
348 if (idx == 85) {
349 TRY_FILES(NULL, 3468, "sprites/fgtiles.spr%s", "");
350 return NULL;
353 if (idx == 86) {
354 TRY_FILES(NULL, 2652, "sprites/bgtiles.spr%s", "");
355 return NULL;
358 if (idx == 87) {
359 TRY_FILES(NULL, 588, "sprites/maptiles.spr%s", "");
360 return NULL;
363 if (idx == 88) {
364 TRY_FILES(NULL, 19796, "sprites/items.spr%s", "");
365 return NULL;
368 if (idx == 89) {
369 TRY_FILES(NULL, 620, "sprites/mapitems.spr%s", "");
370 return NULL;
373 if (idx == 90) {
374 TRY_FILES(NULL, 44940, "sprites/professorim.spr%s", "");
375 return NULL;
378 if (idx == 91) {
379 TRY_FILES(NULL, 9441, "sprites/himenu.spr%s", "");
380 return NULL;
383 if (idx == 92) {
384 TRY_FILES(unpackFNTFont, 256*8, "fonts/namco.fnt%s", "");
385 TRY_FILES(unpackFNTFont, 256*8, "fonts/font.fnt%s", "");
386 TRY_FILES(unpackBINFont, 256*8, "fonts/font.bin%s", "");
387 return NULL;
390 if (idx == 666 || idx == 93) {
391 if (goobers) {
392 if ((res = tryLocalCodeFile(rsz)) != NULL) return res;
395 TRY_FILES(NULL, 8, "code/awish.vmd%s", "");
397 if (!goobers) {
398 if ((res = tryLocalCodeFile(rsz)) != NULL) return res;
400 return NULL;
402 return NULL;
406 static uint8_t *tryResFile (ResFile *resfile, int idx, int *rsz, UnpackCB unp, int destsize) {
407 uint8_t *res = NULL, *up;
408 int size;
410 if (rsz) *rsz = 0;
411 if (!resfile || !resfile->fl || idx < 0 || idx >= resfile->count) return NULL;
413 if (goobers) fprintf(stderr, "trying RES resource: %d\n", idx);
414 //fprintf(stderr, "idx=%d; ofs=%u; size=%u\n", idx, resfile->offsets[idx], resfile->sizes[idx]);
415 size = resfile->sizes[idx];
416 if (fseek(resfile->fl, resfile->offsets[idx], SEEK_SET) != 0) return NULL;
417 if ((res = (uint8_t *)calloc(1, size+16)) == NULL) return NULL;
418 if (size > 0) {
419 if (fread(res, size, 1, resfile->fl) != 1) { free(res); return NULL; }
422 if (size >= 18 && rnc_sign(res)) {
423 // unpack RNC file
424 int ulen, uplen;
425 uint8_t *udata;
427 ulen = rnc_ulen(res);
428 if (ulen < 1) { free(res); return NULL; }
429 udata = malloc(ulen+16);
430 if (udata == NULL) { free(res); return NULL; }
431 uplen = rnc_unpack(res, udata, NULL);
432 free(res);
433 res = udata;
434 if (uplen != ulen) { free(res); return NULL; }
435 size = ulen;
438 if (!unp) {
439 if (destsize >= 0 && size < destsize) { free(res); return NULL; }
440 } else {
441 if (destsize < 0) { free(res); return NULL; }
442 if ((up = calloc(1, destsize+16)) == NULL) { free(res); return NULL; }
443 if (unp(up, res, size) != 0) { free(up); free(res); return NULL; }
444 free(res);
445 res = up;
446 size = destsize;
449 if (rsz) *rsz = size;
450 return res;
454 uint8_t *loadResFile (ResFile *resfile, int idx, int *rsz) {
455 uint8_t *res = NULL;
457 if (!datfirst) res = loadDiskFile(idx, rsz);
459 if (res != NULL || resfile == NULL) return res;
461 if (idx >= 0 && idx <= 8) {
462 return tryResFile(resfile, idx, rsz, (resfile->sizes[idx] < 320*200) ? unpackVPI : NULL, 320*200);
465 if (idx >= 9 && idx <= 82) {
466 return tryResFile(resfile, idx, rsz, NULL, 3700);
469 if (idx == 83) {
470 return tryResFile(resfile, idx, rsz, NULL, 768);
473 if (idx == 84) {
474 return tryResFile(resfile, idx, rsz, NULL, 50568);
477 if (idx == 85) {
478 return tryResFile(resfile, idx, rsz, NULL, 3468);
481 if (idx == 86) {
482 return tryResFile(resfile, idx, rsz, NULL, 2652);
485 if (idx == 87) {
486 return tryResFile(resfile, idx, rsz, NULL, 588);
489 if (idx == 88) {
490 return tryResFile(resfile, idx, rsz, NULL, 19796);
493 if (idx == 89) {
494 return tryResFile(resfile, idx, rsz, NULL, 620);
497 if (idx == 90) {
498 return tryResFile(resfile, idx, rsz, NULL, 44940);
501 if (idx == 91) {
502 return tryResFile(resfile, idx, rsz, NULL, 9441);
505 if (idx == 92) {
506 return tryResFile(resfile, idx, rsz, unpackBINFont, 256*8);
509 if (datfirst) res = loadDiskFile(idx, rsz);
511 return res;