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/>.
22 #include <sys/types.h>
24 #include "librnc/librnc.h"
25 #include "libwdx/libwdx.h"
30 # define DATA_DIR "/usr/local/share/awish"
38 static const char *getMyDir (void) {
39 static char myDir
[8192];
40 static int inited
= 0;
47 sprintf(buf
, "/proc/%u/exe", (unsigned int)pid
);
48 if (readlink(buf
, myDir
, sizeof(myDir
)-1) < 0) {
51 char *p
= (char *)strrchr(myDir
, '/');
53 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
55 strcat(myDir
, "/data");
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
= '/';
74 static const char *getHomeDir (void) {
75 static char homeDir
[8192];
76 static int inited
= 0;
80 const char *h
= getenv("HOME");
82 strcpy(homeDir
, (h
&& h
[0]) ? h
: ".");
83 strcat(homeDir
, "/.local/awish/data");
87 for (char *p
= myDir
; *p
; ++p
) if (*p
== '\\') *p
= '/';
96 static uint32_t getUInt (const uint8_t *buf
) {
99 for (int f
= 3; f
>= 0; --f
) res
= (res
<<8)|buf
[f
];
104 static int tryInitResFile (ResFile
*resfile
, const char *fname
) {
108 FILE *fl
= fopen(fname
, "rb");
111 //fprintf(stderr, "RES: [%s]\n", fname);
113 if (!resfile
) goto quit
;
114 memset(resfile
, 0, sizeof(resfile
));
116 if (fread(sign
, 3, 1, fl
) != 1) goto quit
;
117 if (memcmp(sign
, "RES", 3) != 0 && memcmp(sign
, "SND", 3) != 0 && memcmp(sign
, "MUS", 3) != 0) goto quit
;
118 if (fread(&fcnt
, 1, 1, fl
) != 1) goto quit
;
119 if (fcnt
< 1/*93*/) goto quit
;
122 resfile
->count
= fcnt
;
124 if ((resfile
->offsets
= malloc(4*resfile
->count
)) == NULL
) goto quit
;
125 if ((resfile
->sizes
= malloc(4*resfile
->count
)) == NULL
) goto quit
;
127 if (fread(resfile
->offsets
, 4*resfile
->count
, 1, fl
) != 1) goto quit
;
128 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
129 for (int f
= 0; f
< resfile
->count
; ++f
) resfile
->offsets
[f
] = SDL_SwapLE32(resfile
->offsets
[f
]);
131 if (fseek(fl
, 0, SEEK_END
) != 0) goto quit
;
133 for (int f
= 0; f
< resfile
->count
-1; ++f
) resfile
->sizes
[f
] = resfile
->offsets
[f
+1]-resfile
->offsets
[f
];
134 resfile
->sizes
[resfile
->count
-1] = sz
-resfile
->offsets
[resfile
->count
-1];
138 //fprintf(stderr, "RES: [%s]: OK\n", fname);
142 if (resfile
->sizes
) free(resfile
->sizes
);
143 if (resfile
->offsets
) free(resfile
->offsets
);
144 memset(resfile
, 0, sizeof(resfile
));
145 //fprintf(stderr, "RES: [%s]: FAIL\n", fname);
151 int initResFile (ResFile
*resfile
, const char *rfname
) {
152 static char fname
[8192];
156 sprintf(fname
, "%s/%s", getHomeDir(), rfname
);
157 res
= tryInitResFile(resfile
, fname
);
160 sprintf(fname
, "%s/%s", getMyDir(), rfname
);
161 res
= tryInitResFile(resfile
, fname
);
165 sprintf(fname
, "%s/%s", DATA_DIR
, rfname
);
166 res
= tryInitResFile(resfile
, fname
);
172 strcpy(fname
, getMyDir());
173 t
= strrchr(fname
, '/');
175 strcat(fname
, rfname
);
176 res
= tryInitResFile(resfile
, fname
);
180 if (res
== 0) fprintf(stderr
, "using resource file '%s'\n", fname
);
181 else fprintf(stderr
, "can't find resource file '%s'\n", rfname
);
187 void deinitResFile (ResFile
*resfile
) {
189 if (resfile
->fl
) fclose(resfile
->fl
);
190 if (resfile
->sizes
) free(resfile
->sizes
);
191 if (resfile
->offsets
) free(resfile
->offsets
);
192 memset(resfile
, 0, sizeof(resfile
));
197 typedef int (*UnpackCB
) (unsigned char *dest
, const unsigned char *data
, int size
);
200 static int unpackFNTFont (unsigned char *dest
, const unsigned char *data
, int size
) {
204 memset(dest
, 0, 256*8);
205 if (size
< 6) goto quit
;
206 if (memcmp(data
, "FNT0", 4) != 0) goto quit
;
209 if (fc
> lc
) goto quit
;
211 for (int f
= fc
; f
<= lc
; ++f
) {
212 if (pos
+8 > size
) goto quit
;
213 memcpy(dest
+f
*8, data
+pos
, 8);
218 memset(dest
, 0, 256*8);
223 static int unpackBINFont (unsigned char *dest
, const unsigned char *data
, int size
) {
224 memset(dest
, 0, 256*8);
225 if (size
< 90*8) return -1;
226 memcpy(dest
+33*8, data
, 90*8);
231 static int unpackVPI (unsigned char *dest
, const unsigned char *data
, int size
) {
232 if (size
>= 320*200) {
233 memcpy(dest
, data
, 320*200);
235 int pos
= 0; // in packed
236 int opos
= 0; // in unpacked
237 int cpos
= 0; // meaningless here (position of colormap)
239 while (opos
< 320*200 && pos
+2 <= size
) {
240 int csz
= ((uint32_t)(data
[pos
]))+256*((uint32_t)(data
[pos
+1])); // picture chunk size
242 cpos
= (pos
+= 2); // colormap
243 pos
+= 512; // skip colormap
244 while (opos
< 320*200 && pos
< size
&& csz
> 0) {
245 int idx
= data
[pos
++];
247 dest
[opos
++] = data
[cpos
+idx
*2+0];
248 dest
[opos
++] = data
[cpos
+idx
*2+1];
257 uint8_t *tryDiskFile (const char *fname
, int *rsz
) {
258 FILE *fl
= fopen(fname
, "rb");
263 if (fl
== NULL
) return NULL
;
264 if (goobers
) fprintf(stderr
, "trying disk resource: '%s'\n", fname
);
265 if (fseek(fl
, 0, SEEK_END
) != 0) goto quit
;
268 if (size
< 0) goto quit
;
270 res
= calloc(1, size
+16);
271 if (res
== NULL
) goto quit
;
272 if (fread(res
, size
, 1, fl
) != 1) goto quit
;
274 if (size
>= 18 && rnc_sign(res
)) {
279 ulen
= rnc_ulen(res
);
280 if (ulen
< 1) goto quit
;
281 udata
= malloc(ulen
+16);
282 if (udata
== NULL
) goto quit
;
283 uplen
= rnc_unpack(res
, udata
, NULL
);
286 //if (goobers) fprintf(stderr, "RNC: %d -> %d (%d)\n", (int)size, ulen, uplen);
287 if (uplen
!= ulen
) goto quit
;
289 } else if (size
>= 20 && memcmp(res
, "WDX0", 4) == 0) {
291 const uint8_t *ibuf
= (const uint8_t *)res
;
295 ibuf
+= 4; /* skip signature */
296 osz
= getUInt(ibuf
); ibuf
+= 4; /* unpacked size */
297 crc
= getUInt(ibuf
); ibuf
+= 4; /* crc */
298 psz
= getUInt(ibuf
); ibuf
+= 4; /* packed size */
299 if (psz
+4*4 > size
) goto quit
;
301 if (obuf
== NULL
) goto quit
;
302 xsz
= wdxUnpack(obuf
, osz
, ibuf
, psz
);
303 if (xsz
< 0 || xsz
!= osz
) goto quit
;
306 ncrc
= wdxCRC32(obuf
, xsz
);
307 if (ncrc
!= crc
) goto quit
;
316 if (res
!= NULL
) { free(res
); res
= NULL
; }
318 if (res
!= NULL
&& rsz
) *rsz
= size
;
324 static uint8_t *tryDiskFileEx (const char *fname
, int *rsz
, UnpackCB unp
, int destsize
) {
328 if ((res
= tryDiskFile(fname
, &sz
)) == NULL
) return NULL
;
331 if (destsize
>= 0 && sz
< destsize
) { free(res
); return NULL
; }
334 if (destsize
< 0) { free(res
); return NULL
; }
335 if ((up
= calloc(1, destsize
+16)) == NULL
) { free(res
); return NULL
; }
336 if (unp(up
, res
, sz
) != 0) { free(up
); free(res
); return NULL
; }
346 #define TRY_FILE(upk,usz,fmt,srcdir,...) do { \
347 sprintf(fname, (fmt), (srcdir), __VA_ARGS__); \
348 if ((res = tryDiskFileEx(fname, rsz, (upk), (usz))) != NULL) return res; \
354 #define TRY_FILES(upk,usz,fmt,...) do { \
355 TRY_FILE((upk), (usz), "%s/" fmt, getHomeDir(), __VA_ARGS__); \
356 TRY_FILE((upk), (usz), "%s/" fmt, getMyDir(), __VA_ARGS__); \
357 TRY_FILE((upk), (usz), "%s/" fmt, DATA_DIR, __VA_ARGS__); \
362 #define TRY_FILES(upk,usz,fmt,...) do { \
363 TRY_FILE((upk), (usz), "%s/" fmt, getMyDir(), __VA_ARGS__); \
369 static uint8_t *tryLocalCodeFile (int *rsz
) {
370 static char fname
[512];
373 strcpy(fname
, getMyDir());
374 t
= strrchr(fname
, '/');
376 strcat(fname
, "/awish.vmd");
377 return tryDiskFileEx(fname
, rsz
, NULL
, 8);
381 static uint8_t *loadDiskFile (int idx
, int *rsz
) {
382 static char fname
[512];
386 if (idx
< 0) return NULL
;
388 if (idx
>= 0 && idx
<= 6) {
389 TRY_FILES(unpackVPI
, 64000, "pics/back%02d.vpi", idx
+1);
394 TRY_FILES(unpackVPI
, 64000, "pics/title.vpi%s", "");
399 TRY_FILES(unpackVPI
, 64000, "pics/interback.vpi%s", "");
403 if (idx
>= 9 && idx
<= 82) {
404 TRY_FILES(NULL
, 3700, "maps/level%02d.lvl", idx
-8);
409 TRY_FILES(NULL
, 768, "pics/palette.vga%s", "");
414 TRY_FILES(NULL
, 50568, "sprites/professor.spr%s", "");
419 TRY_FILES(NULL
, 3468, "sprites/fgtiles.spr%s", "");
424 TRY_FILES(NULL
, 2652, "sprites/bgtiles.spr%s", "");
429 TRY_FILES(NULL
, 588, "sprites/maptiles.spr%s", "");
434 TRY_FILES(NULL
, 19796, "sprites/items.spr%s", "");
439 TRY_FILES(NULL
, 620, "sprites/mapitems.spr%s", "");
444 TRY_FILES(NULL
, 44940, "sprites/professorim.spr%s", "");
449 TRY_FILES(NULL
, 9441, "sprites/himenu.spr%s", "");
454 TRY_FILES(unpackFNTFont
, 256*8, "fonts/namco.fnt%s", "");
455 TRY_FILES(unpackFNTFont
, 256*8, "fonts/font.fnt%s", "");
456 TRY_FILES(unpackBINFont
, 256*8, "fonts/font.bin%s", "");
460 if (idx
== 666 || idx
== 93) {
462 if ((res
= tryLocalCodeFile(rsz
)) != NULL
) return res
;
465 TRY_FILES(NULL
, 8, "code/awish.vmd%s", "");
468 if ((res
= tryLocalCodeFile(rsz
)) != NULL
) return res
;
473 if (idx
>= 94 && idx
<= 94+73) {
474 TRY_FILES(NULL
, -1, "maps/level%02d.vmd", idx
-93);
480 static uint8_t *tryResFile (ResFile
*resfile
, int idx
, int *rsz
, UnpackCB unp
, int destsize
) {
481 uint8_t *res
= NULL
, *up
;
485 if (!resfile
|| !resfile
->fl
|| idx
< 0 || idx
>= resfile
->count
) return NULL
;
487 if (goobers
) fprintf(stderr
, "trying RES resource: %d\n", idx
);
488 //fprintf(stderr, "idx=%d; ofs=%u; size=%u\n", idx, resfile->offsets[idx], resfile->sizes[idx]);
489 size
= resfile
->sizes
[idx
];
490 if (fseek(resfile
->fl
, resfile
->offsets
[idx
], SEEK_SET
) != 0) return NULL
;
491 if ((res
= (uint8_t *)calloc(1, size
+16)) == NULL
) return NULL
;
493 if (fread(res
, size
, 1, resfile
->fl
) != 1) { free(res
); return NULL
; }
496 if (size
>= 18 && rnc_sign(res
)) {
501 ulen
= rnc_ulen(res
);
502 if (ulen
< 1) { free(res
); return NULL
; }
503 udata
= malloc(ulen
+16);
504 if (udata
== NULL
) { free(res
); return NULL
; }
505 uplen
= rnc_unpack(res
, udata
, NULL
);
508 if (uplen
!= ulen
) { free(res
); return NULL
; }
510 } else if (size
>= 20 && memcmp(res
, "WDX0", 4) == 0) {
512 const uint8_t *ibuf
= (const uint8_t *)res
;
516 ibuf
+= 4; /* skip signature */
517 osz
= getUInt(ibuf
); ibuf
+= 4; /* unpacked size */
518 crc
= getUInt(ibuf
); ibuf
+= 4; /* crc */
519 psz
= getUInt(ibuf
); ibuf
+= 4; /* packed size */
520 if (psz
+4*4 > size
) return NULL
;
522 if (obuf
== NULL
) return NULL
;
523 xsz
= wdxUnpack(obuf
, osz
, ibuf
, psz
);
524 if (xsz
< 0 || xsz
!= osz
) { free(obuf
); return NULL
; }
527 ncrc
= wdxCRC32(obuf
, xsz
);
528 if (ncrc
!= crc
) { free(res
); return NULL
; }
533 if (destsize
>= 0 && size
< destsize
) { free(res
); return NULL
; }
535 if (destsize
< 0) { free(res
); return NULL
; }
536 if ((up
= calloc(1, destsize
+16)) == NULL
) { free(res
); return NULL
; }
537 if (unp(up
, res
, size
) != 0) { free(up
); free(res
); return NULL
; }
543 if (rsz
) *rsz
= size
;
548 uint8_t *loadResFile (ResFile
*resfile
, int idx
, int *rsz
) {
551 if (!datfirst
) res
= loadDiskFile(idx
, rsz
);
553 if (res
!= NULL
|| resfile
== NULL
) return res
;
555 if (idx
>= 0 && idx
< resfile
->count
) {
558 if (resfile
->sizes
[idx
] < 320*200) {
559 res
= tryResFile(resfile
, idx
, rsz
, unpackVPI
, 320*200);
561 res
= tryResFile(resfile
, idx
, rsz
, NULL
, 320*200);
565 res
= tryResFile(resfile
, idx
, rsz
, NULL
, 3700);
568 res
= tryResFile(resfile
, idx
, rsz
, NULL
, 768);
571 res
= tryResFile(resfile
, idx
, rsz
, NULL
, 50568);
574 res
= tryResFile(resfile
, idx
, rsz
, NULL
, 3468);
577 res
= tryResFile(resfile
, idx
, rsz
, NULL
, 2652);
580 res
= tryResFile(resfile
, idx
, rsz
, NULL
, 588);
583 res
= tryResFile(resfile
, idx
, rsz
, NULL
, 19796);
586 res
= tryResFile(resfile
, idx
, rsz
, NULL
, 620);
589 res
= tryResFile(resfile
, idx
, rsz
, NULL
, 44940);
592 res
= tryResFile(resfile
, idx
, rsz
, NULL
, 9441);
595 res
= tryResFile(resfile
, idx
, rsz
, unpackBINFont
, 256*8);
598 res
= tryResFile(resfile
, idx
, rsz
, NULL
, -1);
603 if (!res
&& datfirst
) res
= loadDiskFile(idx
, rsz
);
609 #ifndef AWISH_NO_SOUND
610 static Mix_Chunk
*loadADS (const void *adata
, int len
) {
611 const unsigned char *src
= (const unsigned char *)adata
;
614 int size
= src
[1]|(src
[2]<<8)|(src
[3]<<16);
621 int audio_rate
, audio_channels
;
624 int newlen
, sdatalen
;
627 Mix_QuerySpec(&audio_rate
, &audio_format
, &audio_channels
);
631 if (size
< 2 || size
> len
) return NULL
;
632 rate
= 1000000/(256-src
[0]); // 'real' sample rate
636 // very simple resampler
637 sdatalen
= (long long)size
*audio_rate
/rate
;
638 newlen
= 2*4/*RIFF*/+4/*WAVE*/+2*4+16/*fmt*/+4*2+sdatalen
/*data*/;
639 if ((wav
= calloc(1, newlen
)) == NULL
) return NULL
;
640 memcpy(wav
+0, "RIFF", 4);
641 newlen
-= 4*2; // w/o RIFF header
642 wav
[4] = newlen
&0xff;
643 wav
[5] = (newlen
>>8)&0xff;
644 wav
[6] = (newlen
>>16)&0xff;
645 wav
[7] = (newlen
>>24)&0xff;
646 newlen
+= 4*2; // with RIFF header
647 memcpy(wav
+8, "WAVE", 4);
648 memcpy(wav
+12, "fmt ", 4);
649 wav
[16] = 16; // chunk size
650 wav
[20+0] = 1; // PCM
651 wav
[20+2] = 1; // mono
652 wav
[20+4] = audio_rate
&0xff;
653 wav
[20+5] = (audio_rate
>>8)&0xff;
654 wav
[20+6] = (audio_rate
>>16)&0xff;
655 wav
[20+7] = (audio_rate
>>24)&0xff;
656 memcpy(wav
+20+8, wav
+20+4, 4); // SampleRate * NumChannels * BitsPerSample/8
657 wav
[20+12] = 1; // block align
658 wav
[20+14] = 8; // bits per sample
659 memcpy(wav
+36, "data", 4);
660 wav
[40] = sdatalen
&0xff;
661 wav
[41] = (sdatalen
>>8)&0xff;
662 wav
[42] = (sdatalen
>>16)&0xff;
663 wav
[43] = (sdatalen
>>24)&0xff;
664 //memcpy(wav+44, src, size);
666 delta
= (double)size
/sdatalen
;
667 //printf("size=%d; sdatalen=%d; delta=%f\n", size, sdatalen, delta);
668 for (int f
= 0; f
< sdatalen
; ++f
) {
669 double dpos
= delta
*f
;
670 int ipos
= (int)dpos
;
672 dpos
-= ipos
; // fractional part
673 //fprintf(stderr, "f=%d; ipos=%d; dpos=%f\n", f, ipos, dpos);
674 if (f
== 0) wav
[44] = src
[0];
675 else if (ipos
+1 < size
) {
676 int v
= (((double)src
[ipos
])-128)*(1.0-dpos
)+(((double)src
[ipos
+1])-128)*dpos
;
678 if (v
< -128) v
= -128; else if (v
> 127) v
= 127;
680 } else wav
[44+f
] = 0x7f;
682 wav
[44+sdatalen
-1] = src
[size
-1];
685 FILE *fo = fopen("z.wav", "wb");
686 fwrite(wav, newlen, 1, fo);
690 rw
= SDL_RWFromMem(wav
, newlen
);
691 res
= Mix_LoadWAV_RW(rw
, 0); // autofree
706 Mix_Chunk
*loadDiskSoundFile (const char *fname
) {
710 data
= tryDiskFile(fname
, &rsz
);
712 Mix_Chunk
*res
= loadADS(data
, rsz
);
714 //if (goobers) fprintf(stderr, "sound [%s]: %s\n", fname, res!=NULL?"OK":"BAD");
718 //if (goobers) fprintf(stderr, "sound [%s]: MISSING\n", fname);
723 static Mix_Chunk
*tryDiskSoundFile (int idx
) {
724 static const char *extlist
[] = {
731 if (idx
< 0 || idx
> 999) return NULL
;
733 for (int extnum
= 0; extlist
[extnum
] != NULL
; ++extnum
) {
734 static char fname
[8192];
738 sprintf(fname
, "%s/sound%03d.%s", getHomeDir(), idx
, extlist
[extnum
]);
739 if ((res
= loadDiskSoundFile(fname
)) != NULL
) return res
;
741 sprintf(fname
, "%s/sound%03d.%s", getMyDir(), idx
, extlist
[extnum
]);
742 if ((res
= loadDiskSoundFile(fname
)) != NULL
) return res
;
744 sprintf(fname
, "%s/sound%03d.%s", DATA_DIR
, idx
, extlist
[extnum
]);
745 if ((res
= loadDiskSoundFile(fname
)) != NULL
) return res
;
752 Mix_Chunk
*loadSoundFile (ResFile
*resfile
, int idx
) {
753 Mix_Chunk
*res
= NULL
;
755 //fprintf(stderr, ":::%p:%d:%d\n", resfile, idx, resfile->count);
756 if (!datfirst
) res
= tryDiskSoundFile(idx
);
757 if (res
== NULL
&& resfile
!= NULL
&& idx
>= 0 && idx
< resfile
->count
) {
761 data
= tryResFile(resfile
, idx
, &rsz
, NULL
, -1);
763 res
= loadADS(data
, rsz
);
764 //if (goobers) fprintf(stderr, "sound [%d]: %s\n", idx, res!=NULL?"OK":"BAD");
767 //if (goobers) fprintf(stderr, "sound [%d]: MISSING\n", idx);
770 if (res
== NULL
&& datfirst
) res
= tryDiskSoundFile(idx
);