1 /*
2  * OpenBOR - http://www.chronocrash.com
3  * -----------------------------------------------------------------------
4  * All rights reserved, see LICENSE in OpenBOR root for details.
5  *
6  * Copyright (c) 2004 - 2014 OpenBOR Team
7  */
8 
9 /*
10 	Code to read files from packfiles.
11 
12 	============= Format description (a bit cryptical) ================
13 
14 	dword	4B434150 ("PACK")
15 	dword	version
16 	?????	filedata
17 	{
18 		dword	structsize
19 		dword	filestart
20 		dword	filesize
21 		bytes	name
22 	} rep
23 	dword	headerstart
24 */
25 #include <assert.h>
26 #ifndef SPK_SUPPORTED
27 
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include "utils.h"
32 #include "borendian.h"
33 #include "stristr.h"
34 #include "packfile.h"
35 #include "filecache.h"
36 #include "soundmix.h"
37 #include "savedata.h"
38 #include "List.h"
39 
40 #if WIN || LINUX
41 #include <dirent.h>
42 #endif
43 
44 #if _POSIX_SOURCE || SYMBIAN
45 #define	stricmp	strcasecmp
46 #endif
47 
48 #ifndef DC
49 #pragma pack (1)
50 #endif
51 
52 
53 /////////////////////////////////////////////////////////////////////////////
54 //
55 // Requirements for Compressed Packfiles.
56 //
57 #define	MAXPACKHANDLES	8
58 #define	PACKMAGIC		0x4B434150
59 #define	PACKVERSION		0x00000000
60 static const size_t USED_FLAG = (((size_t) 1) << ((sizeof(size_t) * 8) - 1));
61 
62 /////////////////////////////////////////////////////////////////////////////
63 //
64 // This defines are only used for Cached code
65 // CACHEBLOCKSIZE*CACHEBLOCKS is the size of the ever-present file cache
66 // cacheblocks must be 255 or less!
67 //
68 #define CACHEBLOCKSIZE (32768)
69 #ifndef OPENDINGUX
70 #define CACHEBLOCKS    (96)
71 #else
72 #define CACHEBLOCKS    (8)
73 #endif
74 
75 static int pak_initialized;
76 int printFileUsageStatistics = 0;
77 
78 /////////////////////////////////////////////////////////////////////////////
79 //
80 // This variables are only used for Non-Cached code
81 //
82 // Actual file handles.
83 static int packhandle[MAXPACKHANDLES] = { -1, -1, -1, -1, -1, -1, -1, -1 };
84 
85 // Own file pointers and sizes
86 static unsigned int packfilepointer[MAXPACKHANDLES];
87 static unsigned int packfilesize[MAXPACKHANDLES];
88 //char packfile[128] <- defined in sdl/sdlport.c... hmmm
89 List *filenamelist = NULL;
90 
91 /////////////////////////////////////////////////////////////////////////////
92 //
93 // This variables are only used for with Caching code
94 //
95 static int pakfd;
96 static int paksize;
97 static int pak_vfdexists[MAXPACKHANDLES];
98 static int pak_vfdstart[MAXPACKHANDLES];
99 static int pak_vfdsize[MAXPACKHANDLES];
100 static int pak_vfdpos[MAXPACKHANDLES];
101 static int pak_vfdreadahead[MAXPACKHANDLES];
102 static int pak_headerstart;
103 static int pak_headersize;
104 static unsigned char *pak_cdheader;
105 static unsigned char *pak_header;
106 
107 /////////////////////////////////////////////////////////////////////////////
108 /////////////////////////////////////////////////////////////////////////////
109 //
110 // Pointers to the Real Functions
111 //
112 typedef int (*OpenPackfile)(const char *, const char *);
113 typedef int (*ReadPackfile)(int, void *, int);
114 typedef int (*SeekPackfile)(int, int, int);
115 typedef int (*ClosePackfile)(int);
116 
117 int openPackfile(const char *, const char *);
118 int readPackfile(int, void *, int);
119 int seekPackfile(int, int, int);
120 int closePackfile(int);
121 int openPackfileCached(const char *, const char *);
122 int readPackfileCached(int, void *, int);
123 int seekPackfileCached(int, int, int);
124 int closePackfileCached(int);
125 
126 static OpenPackfile pOpenPackfile;
127 static ReadPackfile pReadPackfile;
128 static SeekPackfile pSeekPackfile;
129 static ClosePackfile pClosePackfile;
130 
131 /////////////////////////////////////////////////////////////////////////////
132 //
133 // Generic but useful functions
134 //
135 
tolowerOneChar(const char * c)136 char tolowerOneChar(const char *c)
137 {
138     static const char diff = 'a' - 'A';
139     switch(*c)
140     {
141     case 'A':
142     case 'B':
143     case 'C':
144     case 'D':
145     case 'E':
146     case 'F':
147     case 'G':
148     case 'H':
149     case 'I':
150     case 'J':
151     case 'K':
152     case 'L':
153     case 'M':
154     case 'N':
155     case 'O':
156     case 'P':
157     case 'Q':
158     case 'R':
159     case 'S':
160     case 'T':
161     case 'U':
162     case 'V':
163     case 'W':
164     case 'X':
165     case 'Y':
166     case 'Z':
167         return *c + diff;
168     case '\\':
169         return '/';
170     default:
171         return *c;
172     }
173     return '\0'; //should never be reached
174 }
175 
176 // file name lowercase in-place.
fnlc(char * buf)177 void fnlc(char *buf)
178 {
179     char *copy = buf;
180     while(copy && *copy)
181     {
182         *copy = tolowerOneChar(copy);
183         copy++;
184     }
185 }
186 
187 // we only return 0 on success, and non 0 on failure, to speed it up
myfilenamecmp(const char * a,size_t asize,const char * b,size_t bsize)188 int myfilenamecmp(const char *a, size_t asize, const char *b, size_t bsize)
189 {
190     char *ca;
191     char *cb;
192 
193     if (a == b)
194     {
195         return 0;
196     }
197     if(asize != bsize)
198     {
199         return 1;
200     }
201 
202     ca = (char *) a;
203     cb = (char *) b;
204 
205     for (;;)
206     {
207         if (!*ca)
208         {
209             if (*cb)
210             {
211                 return -1;
212             }
213             else
214             {
215                 return 0;    // default exit on match
216             }
217         }
218         if (!*cb)
219         {
220             return 1;
221         }
222         if (*ca == *cb)
223         {
224             goto cont;
225         }
226         if (tolowerOneChar(ca) != tolowerOneChar(cb))
227         {
228             return 1;
229         }
230 cont:
231         ca++;
232         cb++;
233     }
234     return -2; // should never be reached
235 }
236 
237 // Convert slashes (UNIX) to backslashes (DOS).
238 // Return a pointer to buffer with filename converted to DOS format.
slashback(const char * sz)239 static char *slashback(const char *sz)
240 {
241     int i = 0;
242     static char new[PACKFILE_PATH_MAX];
243     while(sz[i] && i < PACKFILE_PATH_MAX - 1)
244     {
245         new[i] = sz[i];
246         if(new[i] == '/')
247         {
248             new[i] = '\\';
249         }
250         ++i;
251     }
252     new[i] = 0;
253     return new;
254 }
255 
256 #ifndef WIN
257 // Convert backslashes (DOS) to forward slashes (everything else).
258 // Return a pointer to buffer with filename using forward slash as separator.
slashfwd(const char * sz)259 static char *slashfwd(const char *sz)
260 {
261     int i = 0;
262     static char new[PACKFILE_PATH_MAX];
263     while(sz[i] && i < PACKFILE_PATH_MAX - 1)
264     {
265         new[i] = sz[i];
266         if(new[i] == '\\')
267         {
268             new[i] = '/';
269         }
270         ++i;
271     }
272     new[i] = 0;
273     return new;
274 }
275 #endif
276 
277 #ifdef LINUX
casesearch(const char * dir,const char * filepath)278 char *casesearch(const char *dir, const char *filepath)
279 {
280     DIR *d;
281     struct dirent *entry;;
282     char filename[PACKFILE_PATH_MAX] = {""}, *rest_of_path;
283     static char fullpath[PACKFILE_PATH_MAX];
284     int i = 0;
285 #ifdef VERBOSE
286     printf("casesearch: %s, %s\n", dir, filepath);
287 #endif
288 
289     if ((d = opendir(dir)) == NULL)
290     {
291         return NULL;
292     }
293 
294     // are we searching for a file or a directory?
295     rest_of_path = strchr(filepath, '/');
296     if (rest_of_path != NULL) // directory
297     {
298         if(rest_of_path - filepath <= 0)
299         {
300             return NULL;
301         }
302         strncat(filename, filepath, rest_of_path - filepath);
303         rest_of_path++;
304     }
305     else
306     {
307         strcpy(filename, filepath);    // file
308     }
309 
310     while ((entry = readdir(d)) != NULL)
311     {
312         if (stricmp(entry->d_name, filename) == 0)
313         {
314             i = 1;
315             break;
316         }
317     }
318 
319     //if (entry != NULL && entry->d_name != NULL)
320     if (entry != NULL)
321     {
322         snprintf(fullpath, sizeof(fullpath), "%s/%s", dir, entry->d_name);
323     }
324 
325     if (closedir(d))
326     {
327         return NULL;
328     }
329     if (i == 0)
330     {
331         return NULL;
332     }
333 
334     return rest_of_path == NULL ? fullpath : casesearch(fullpath, rest_of_path);
335 }
336 
337 #endif
338 
339 /////////////////////////////////////////////////////////////////////////////
340 
getFreeHandle(void)341 int getFreeHandle(void)
342 {
343     int h;
344     for(h = 0; h < MAXPACKHANDLES && packhandle[h] > -1; h++); // Find free handle
345     if(h >= MAXPACKHANDLES)
346     {
347         printf ("no free handles\n"); // since this condition shuts down openbor, we can savely give more info.
348         return -1;			// No free handles
349     }
350     return h;
351 }
352 
353 
packfile_mode(int mode)354 void packfile_mode(int mode)
355 {
356     if(!mode)
357     {
358 #ifdef DC
359         fs_chdir("/cd");
360 #endif
361         pOpenPackfile = openPackfile;
362         pReadPackfile = readPackfile;
363         pSeekPackfile = seekPackfile;
364         pClosePackfile = closePackfile;
365         return;
366     }
367     pOpenPackfile = openPackfileCached;
368     pReadPackfile = readPackfileCached;
369     pSeekPackfile = seekPackfileCached;
370     pClosePackfile = closePackfileCached;
371 }
372 
373 
374 /////////////////////////////////////////////////////////////////////////////
375 
376 #if WIN || LINUX
isRawData()377 int isRawData()
378 {
379     DIR *d;
380     if ((d = opendir("data")) == NULL)
381     {
382         return 0;
383     }
384     closedir(d);
385     return 1;
386 }
387 #endif
388 
389 /////////////////////////////////////////////////////////////////////////////
390 
openpackfile(const char * filename,const char * packfilename)391 int openpackfile(const char *filename, const char *packfilename)
392 {
393 #ifdef VERBOSE
394     char *pointsto;
395 
396     if (pOpenPackfile == openPackfileCached)
397     {
398         pointsto = "oPackCached";
399     }
400     else if (pOpenPackfile == openPackfile)
401     {
402         pointsto = "openPackFile";
403     }
404     else
405     {
406         pointsto = "unknown destination";
407     }
408     printf ("openpackfile called: f: %s, p: %s, dest: %s\n", filename, packfilename, pointsto);
409 #endif
410     return pOpenPackfile(filename, packfilename);
411 }
412 
openPackfile(const char * filename,const char * packfilename)413 int openPackfile(const char *filename, const char *packfilename)
414 {
415     int h, handle;
416     unsigned int magic, version, headerstart, p;
417     pnamestruct pn;
418 #ifdef LINUX
419     char *fspath;
420 #endif
421 
422     h = getFreeHandle();
423     if (h == -1)
424     {
425         return -1;
426     }
427 
428 #ifdef WIN
429     // Convert slashes to backslashes
430     filename = slashback(filename);
431 #else
432     // Convert backslashes to slashes
433     filename = slashfwd(filename);
434 #endif
435 
436     packfilepointer[h] = 0;
437 	int per = 666;
438     // Separate file present?
439     if((handle = open(filename, O_RDONLY | O_BINARY, per)) != -1)
440     {
441         if((packfilesize[h] = lseek(handle, 0, SEEK_END)) == -1)
442         {
443 #ifdef VERBOSE
444             printf ("err handles 1\n");
445 #endif
446             close(handle);
447             return -1;
448         }
449         if(lseek(handle, 0, SEEK_SET) == -1)
450         {
451 #ifdef VERBOSE
452             printf ("err handles 2\n");
453 #endif
454             close(handle);
455             return -1;
456         }
457         packhandle[h] = handle;
458         return h;
459     }
460 
461 #ifdef LINUX
462     // Try a case-insensitive search for a separate file.
463     fspath = casesearch(".", filename);
464     if (fspath != NULL)
465     {
466         if((handle = open(fspath, O_RDONLY | O_BINARY, per)) != -1)
467         {
468             if((packfilesize[h] = lseek(handle, 0, SEEK_END)) == -1)
469             {
470 #ifdef VERBOSE
471                 printf ("err handles 3\n");
472 #endif
473                 close(handle);
474                 return -1;
475             }
476             if(lseek(handle, 0, SEEK_SET) == -1)
477             {
478 #ifdef VERBOSE
479                 printf ("err handles 4\n");
480 #endif
481                 close(handle);
482                 return -1;
483             }
484             packhandle[h] = handle;
485             return h;
486         }
487     }
488 #endif
489 
490 #ifndef WIN
491     // Convert slashes to backslashes
492     filename = slashback(filename);
493 #endif
494 
495     // Try to open packfile
496     if((handle = open(packfilename, O_RDONLY | O_BINARY, per)) == -1)
497     {
498 #ifdef VERBOSE
499         printf ("perm err\n");
500 #endif
501         return -1;
502     }
503 
504 
505     // Read magic dword ("PACK" identifier)
506     if(read(handle, &magic, 4) != 4 || magic != SwapLSB32(PACKMAGIC))
507     {
508 #ifdef VERBOSE
509         printf ("err magic\n");
510 #endif
511         close(handle);
512         return -1;
513     }
514     // Read version from packfile
515     if(read(handle, &version, 4) != 4 || version != SwapLSB32(PACKVERSION))
516     {
517 #ifdef VERBOSE
518         printf ("err version\n");
519 #endif
520 
521         close(handle);
522         return -1;
523     }
524 
525     // Seek to position of headerstart indicator
526     if(lseek(handle, -4, SEEK_END) == -1)
527     {
528 #ifdef VERBOSE
529         printf ("seek failed\n");
530 #endif
531         close(handle);
532         return -1;
533     }
534     // Read headerstart
535     if(read(handle, &headerstart, 4) != 4)
536     {
537 #ifdef VERBOSE
538         printf ("err header\n");
539 #endif
540         close(handle);
541         return -1;
542     }
543 
544     headerstart = SwapLSB32(headerstart);
545 
546     // Seek to headerstart
547     if(lseek(handle, headerstart, SEEK_SET) == -1)
548     {
549 #ifdef VERBOSE
550         printf ("err headerstart 1\n");
551 #endif
552         close(handle);
553         return -1;
554     }
555 
556     p = headerstart;
557 
558     // Search for filename
559     while(read(handle, &pn, sizeof(pn)) > 12)
560     {
561         pn.filesize = SwapLSB32(pn.filesize);
562         pn.filestart = SwapLSB32(pn.filestart);
563         pn.pns_len = SwapLSB32(pn.pns_len);
564 
565         if(stricmp(filename, pn.namebuf) == 0)
566         {
567             packhandle[h] = handle;
568             packfilesize[h] = pn.filesize;
569             lseek(handle, pn.filestart, SEEK_SET);
570             return h;
571         }
572         p += pn.pns_len;
573         if(lseek(handle, p, SEEK_SET) == -1)
574         {
575 #ifdef VERBOSE
576             printf ("err seek handles\n");
577 #endif
578             close(handle);
579             return -1;
580         }
581     }
582     // Filename not found
583 #ifdef VERBOSE
584     printf ("err filename not found\n");
585 #endif
586     close(handle);
587     return -1;
588 }
589 
update_filecache_vfd(int vfd)590 void update_filecache_vfd(int vfd)
591 {
592     if(pak_vfdexists[vfd])
593     {
594         filecache_setvfd(vfd, pak_vfdstart[vfd], (pak_vfdstart[vfd] + pak_vfdpos[vfd]) / CACHEBLOCKSIZE, (pak_vfdreadahead[vfd] + (CACHEBLOCKSIZE - 1)) / CACHEBLOCKSIZE);
595     }
596     else
597     {
598         filecache_setvfd(vfd, -1, -1, 0);
599     }
600 }
601 
makefilenamecache(void)602 void makefilenamecache(void)
603 {
604     ptrdiff_t hpos;
605     char target[PACKFILE_PATH_MAX];
606 
607     if(!filenamelist)
608     {
609         filenamelist = malloc(sizeof(List));
610     }
611     List_Init(filenamelist);
612 
613     // look for filename in the header
614 
615     hpos = 0;
616     for(;;)
617     {
618         if((hpos + 12) >= pak_headersize)
619         {
620             return;
621         }
622         strncpy(target, (char *)pak_header + hpos + 12, PACKFILE_PATH_MAX - 1);
623         fnlc(target);
624         List_InsertAfter(filenamelist, (void *) hpos, target);
625         hpos += readlsb32(pak_header + hpos);
626     }
627 }
628 
freefilenamecache(void)629 void freefilenamecache(void)
630 {
631     Node *n;
632     size_t count = 0;
633     size_t total = 0;
634     if(filenamelist)
635     {
636         if(printFileUsageStatistics)
637         {
638             printf("unused files in the pack:\n");
639             List_GotoFirst(filenamelist);
640             n = List_GetCurrentNode(filenamelist);
641             while(n)
642             {
643                 if(((size_t) n->value & USED_FLAG) != USED_FLAG)
644                 {
645                     count++;
646                     printf("%s\n", n->name);
647                 }
648                 if(List_GotoNext(filenamelist))
649                 {
650                     n = List_GetCurrentNode(filenamelist);
651                 }
652                 else
653                 {
654                     break;
655                 }
656                 total++;
657             }
658             printf("Summary: %d of %d files have been unused\n", (int) count, (int) total);
659             printf("WARNING\n");
660             printf("to be completely sure if a file is unused, you have to play the entire mod\n");
661             printf("in every possible branch, including every possible player, and so forth.\n");
662             printf("so only remove stuff from a foreign mod if you're completely sure that it is unused.\n");
663         }
664         List_Clear(filenamelist);
665         free(filenamelist);
666         filenamelist = NULL;
667     }
668 }
669 
openreadaheadpackfile(const char * filename,const char * packfilename,int readaheadsize,int prebuffersize)670 int openreadaheadpackfile(const char *filename, const char *packfilename, int readaheadsize, int prebuffersize)
671 {
672     size_t hpos;
673     int vfd;
674     size_t fnl;
675     size_t al;
676     char target[PACKFILE_PATH_MAX];
677     Node *n;
678 
679     if(packfilename != packfile)
680     {
681         fnl = strlen(packfile);
682         al = strlen(packfilename);
683         if(myfilenamecmp(packfilename, al, packfile, fnl))
684         {
685             printf("tried to open from unknown pack file (%s)\n", packfilename);
686             return -1;
687         }
688     }
689 
690     if(!filenamelist)
691     {
692         makefilenamecache();
693     }
694 
695     strncpy(target, filename, PACKFILE_PATH_MAX - 1);
696     fnlc(target);
697 
698     n = List_GetNodeByName(filenamelist, target);
699     if (!n)
700     {
701         return -1;
702     }
703 
704     hpos = (size_t) n->value & ~USED_FLAG;
705     n->value = (void *) (((size_t) n->value) | USED_FLAG);
706 
707     // find a free vfd
708     for(vfd = 0; vfd < MAXPACKHANDLES; vfd++) if(!pak_vfdexists[vfd])
709         {
710             break;
711         }
712     if(vfd >= MAXPACKHANDLES)
713     {
714         return -1;
715     }
716 
717     pak_vfdstart[vfd] = readlsb32(pak_header + hpos + 4);
718     pak_vfdsize[vfd] = readlsb32(pak_header + hpos + 8);
719 
720     pak_vfdpos[vfd] = 0;
721     pak_vfdexists[vfd] = 1;
722     pak_vfdreadahead[vfd] = readaheadsize;
723 
724     // notify filecache that we have a new vfd
725     update_filecache_vfd(vfd);
726 
727     // if we want prebuffering, wait for it
728     if(prebuffersize > 0)
729     {
730         filecache_wait_for_prebuffer(vfd, (prebuffersize + ((CACHEBLOCKSIZE) - 1)) / CACHEBLOCKSIZE);
731     }
732     return vfd;
733 }
734 
openPackfileCached(const char * filename,const char * packfilename)735 int openPackfileCached(const char *filename, const char *packfilename)
736 {
737     return openreadaheadpackfile(filename, packfilename, 0, 0);
738 }
739 
740 /////////////////////////////////////////////////////////////////////////////
741 
readpackfile(int handle,void * buf,int len)742 int readpackfile(int handle, void *buf, int len)
743 {
744     return pReadPackfile(handle, buf, len);
745 }
746 
readPackfile(int handle,void * buf,int len)747 int readPackfile(int handle, void *buf, int len)
748 {
749     int realhandle;
750     if(handle < 0 || handle >= MAXPACKHANDLES)
751     {
752         return -1;
753     }
754     if(len < 0)
755     {
756         return -1;
757     }
758     if(len == 0)
759     {
760         return 0;
761     }
762     realhandle = packhandle[handle];
763     if(realhandle == -1)
764     {
765         return -1;
766     }
767     if(len + packfilepointer[handle] > packfilesize[handle])
768     {
769         len = packfilesize[handle] - packfilepointer[handle];
770     }
771     if((len = read(realhandle, buf, len)) == -1)
772     {
773         return -1;
774     }
775     packfilepointer[handle] += len;
776     return len;
777 }
778 
pak_isvalidhandle(int handle)779 int pak_isvalidhandle(int handle)
780 {
781     if(handle < 0 || handle >= MAXPACKHANDLES)
782     {
783         return 0;
784     }
785     if(!pak_vfdexists[handle])
786     {
787         return 0;
788     }
789     return 1;
790 }
791 
pak_rawread(int fd,unsigned char * dest,int len,int blocking)792 static int pak_rawread(int fd, unsigned char *dest, int len, int blocking)
793 {
794     int end;
795     int r;
796     int total = 0;
797     int pos = pak_vfdstart[fd] + pak_vfdpos[fd];
798 
799     if(pos < 0)
800     {
801         return 0;
802     }
803     if(pos >= paksize)
804     {
805         return 0;
806     }
807     if((pos + len) > paksize)
808     {
809         len = paksize - pos;
810     }
811     end = pos + len;
812 
813     update_filecache_vfd(fd);
814 
815     while(pos < end)
816     {
817         int b = pos / CACHEBLOCKSIZE;
818         int startthisblock = pos % CACHEBLOCKSIZE;
819         int sizethisblock = CACHEBLOCKSIZE - startthisblock;
820         if(sizethisblock > (end - pos))
821         {
822             sizethisblock = (end - pos);
823         }
824         r = filecache_readpakblock(dest, b, startthisblock, sizethisblock, blocking);
825         if(r >= 0)
826         {
827             total += r;
828             pak_vfdpos[fd] += r;
829             update_filecache_vfd(fd);
830         }
831         if(r < sizethisblock)
832         {
833             break;
834         }
835 
836         dest += sizethisblock;
837         pos += sizethisblock;
838     }
839     return total;
840 }
841 
readpackfileblocking(int fd,void * buf,int len,int blocking)842 int readpackfileblocking(int fd, void *buf, int len, int blocking)
843 {
844     int n;
845     if(!pak_isvalidhandle(fd))
846     {
847         return -1;
848     }
849     if(pak_vfdpos[fd] < 0)
850     {
851         return 0;
852     }
853     if(pak_vfdpos[fd] > pak_vfdsize[fd])
854     {
855         pak_vfdpos[fd] = pak_vfdsize[fd];
856     }
857     if((len + pak_vfdpos[fd]) > pak_vfdsize[fd])
858     {
859         len = pak_vfdsize[fd] - pak_vfdpos[fd];
860     }
861     if(len < 1)
862     {
863         return 0;
864     }
865     update_filecache_vfd(fd);
866     n = pak_rawread(fd, buf, len, blocking);
867     if(n < 0)
868     {
869         n = 0;
870     }
871     if(pak_vfdpos[fd] > pak_vfdsize[fd])
872     {
873         pak_vfdpos[fd] = pak_vfdsize[fd];
874     }
875     update_filecache_vfd(fd);
876     return n;
877 }
878 
readpackfile_noblock(int handle,void * buf,int len)879 int readpackfile_noblock(int handle, void *buf, int len)
880 {
881     return readpackfileblocking(handle, buf, len, 0);
882 }
883 
readPackfileCached(int handle,void * buf,int len)884 int readPackfileCached(int handle, void *buf, int len)
885 {
886     return readpackfileblocking(handle, buf, len, 1);
887 }
888 
889 /////////////////////////////////////////////////////////////////////////////
890 
closepackfile(int handle)891 int closepackfile(int handle)
892 {
893 #ifdef VERBOSE
894     char *pointsto;
895 
896     if (pClosePackfile == closePackfileCached)
897     {
898         pointsto = "closePackCached";
899     }
900     else if (pClosePackfile == closePackfile)
901     {
902         pointsto = "closePackFile";
903     }
904     else
905     {
906         pointsto = "unknown destination";
907     }
908     printf ("closepackfile called: h: %d, dest: %s\n", handle, pointsto);
909 #endif
910     return pClosePackfile(handle);
911 }
912 
closePackfile(int handle)913 int closePackfile(int handle)
914 {
915 #ifdef VERBOSE
916     printf ("closePackfile called: h: %d\n", handle);
917 #endif
918 
919     if(handle < 0 || handle >= MAXPACKHANDLES)
920     {
921 #ifdef VERBOSE
922         printf("handle too small/big\n");
923 #endif
924         return -1;
925     }
926     if(packhandle[handle] == -1)
927     {
928 #ifdef VERBOSE
929         printf("packhandle -1\n");
930 #endif
931         return -1;
932     }
933     close(packhandle[handle]);
934     packhandle[handle] = -1;
935     return 0;
936 }
937 
closePackfileCached(int handle)938 int closePackfileCached(int handle)
939 {
940     if(!pak_isvalidhandle(handle))
941     {
942         return -1;
943     }
944     pak_vfdexists[handle] = 0;
945     update_filecache_vfd(handle);
946     return 0;
947 }
948 
949 
950 /////////////////////////////////////////////////////////////////////////////
951 
seekpackfile(int handle,int offset,int whence)952 int seekpackfile(int handle, int offset, int whence)
953 {
954     return pSeekPackfile(handle, offset, whence);
955 }
956 
seekPackfile(int handle,int offset,int whence)957 int seekPackfile(int handle, int offset, int whence)
958 {
959     int realhandle;
960     int desiredoffs;
961 
962     if(handle < 0 || handle >= MAXPACKHANDLES)
963     {
964         return -1;
965     }
966     realhandle = packhandle[handle];
967     if(realhandle == -1)
968     {
969         return -1;
970     }
971 
972     switch(whence)
973     {
974     case SEEK_SET:
975         desiredoffs = offset;
976         if(desiredoffs > packfilesize[handle])
977         {
978             desiredoffs = packfilesize[handle];
979         }
980         else if(desiredoffs < 0)
981         {
982             desiredoffs = 0;
983         }
984         break;
985 
986     case SEEK_CUR:
987         desiredoffs = packfilepointer[handle] + offset;
988         if(desiredoffs > packfilesize[handle])
989         {
990             desiredoffs = packfilesize[handle];
991         }
992         else if(desiredoffs < 0)
993         {
994             desiredoffs = 0;
995         }
996         break;
997 
998     case SEEK_END:
999         desiredoffs = packfilesize[handle] + offset;
1000         if(desiredoffs > packfilesize[handle])
1001         {
1002             desiredoffs = packfilesize[handle];
1003         }
1004         else if(desiredoffs < 0)
1005         {
1006             desiredoffs = 0;
1007         }
1008         break;
1009 
1010     default:
1011         return -1;
1012     }
1013     desiredoffs -= packfilepointer[handle];
1014     if((lseek(realhandle, desiredoffs, SEEK_CUR)) == -1)
1015     {
1016         return -1;
1017     }
1018     packfilepointer[handle] += desiredoffs;
1019     return packfilepointer[handle];
1020 }
1021 
seekPackfileCached(int handle,int offset,int whence)1022 int seekPackfileCached(int handle, int offset, int whence)
1023 {
1024     if(!pak_isvalidhandle(handle))
1025     {
1026         return -1;
1027     }
1028     switch(whence)
1029     {
1030     case 0:
1031         pak_vfdpos[handle]  = offset;
1032         break; // set
1033     case 1:
1034         pak_vfdpos[handle] += offset;
1035         break; // cur
1036     case 2:
1037         pak_vfdpos[handle]  = pak_vfdsize[handle] + offset;
1038         break; // end
1039     default:
1040         return -1;
1041     }
1042     // original code had this check too, so do it here
1043     if(pak_vfdpos[handle] < 0)
1044     {
1045         pak_vfdpos[handle] = 0;
1046     }
1047     if(pak_vfdpos[handle] > pak_vfdsize[handle])
1048     {
1049         pak_vfdpos[handle] = pak_vfdsize[handle];
1050     }
1051     update_filecache_vfd(handle);
1052     return pak_vfdpos[handle];
1053 }
1054 
1055 /////////////////////////////////////////////////////////////////////////////
1056 //
1057 // returns number of sectors read successfully
1058 //
pak_getsectors(void * dest,int lba,int n)1059 static int pak_getsectors(void *dest, int lba, int n)
1060 {
1061 #ifdef DC
1062     if((lba + n) > ((paksize + 0x7FF) / 0x800))
1063     {
1064         n = ((paksize + 0x7FF) / 0x800) - lba;
1065     }
1066     if(pakfd >= 0)
1067     {
1068         lseek(pakfd, lba << 11, SEEK_SET);
1069         read(pakfd, dest, n << 11);
1070     }
1071     else
1072     {
1073         gdrom_readsectors(dest, (-pakfd) + lba, n);
1074         while(gdrom_poll());
1075     }
1076 #else
1077     lseek(pakfd, lba << 11, SEEK_SET);
1078     read(pakfd, dest, n << 11);
1079 #endif
1080     return n;
1081 }
1082 
1083 #ifdef DC
1084 /////////////////////////////////////////////////////////////////////////////
1085 //
1086 // returns 0 if they match
1087 //
fncmp(const char * filename,const char * isofilename,int isolen)1088 static int fncmp(const char *filename, const char *isofilename, int isolen)
1089 {
1090     for(; isolen > 0; isolen--)
1091     {
1092         char cf = *filename++;
1093         char ci = *isofilename++;
1094         if(!cf)
1095         {
1096             // allowed to omit the version on filename
1097             if(ci == ';' || ci == 0)
1098             {
1099                 return 0;
1100             }
1101             return 1;
1102         }
1103         if(cf >= 'A' && cf <= 'Z')
1104         {
1105             cf += 'a' - 'A';
1106         }
1107         if(ci >= 'A' && ci <= 'Z')
1108         {
1109             ci += 'a' - 'A';
1110         }
1111         if(cf != ci)
1112         {
1113             return 1;
1114         }
1115     }
1116     // allowed to omit the version on isofilename too O_o
1117     if(*filename == ';' || *filename == 0)
1118     {
1119         return 0;
1120     }
1121     return 1;
1122 }
1123 
1124 /////////////////////////////////////////////////////////////////////////////
1125 //
1126 // input: starting lba of the track
1127 // returns starting lba of the file, or 0 on failure
1128 //
find_iso_file(const char * filename,int lba,int * bytes)1129 int find_iso_file(const char *filename, int lba, int *bytes)
1130 {
1131     int dirlen;
1132     unsigned char sector[4096];
1133     int secofs;
1134 
1135     // read the root descriptor
1136     gdrom_readsectors(sector, lba + 16, 1);
1137     while(gdrom_poll());
1138     // get the root directory extent and size
1139     lba    = 150 + readmsb32(sector + 156 + 6);
1140     dirlen =       readmsb32(sector + 156 + 14);
1141 
1142     // at this point, lba is the lba of the root dir
1143     secofs = 4096;
1144     while(dirlen > 0)
1145     {
1146         if(secofs >= 4096 || ((secofs + sector[secofs]) > 4096))
1147         {
1148             memcpy(sector, sector + 2048, 2048);
1149             gdrom_readsectors(sector + 2048, lba, 1);
1150             while(gdrom_poll());
1151             lba++;
1152             secofs -= 2048;
1153         }
1154         if(!sector[secofs])
1155         {
1156             break;
1157         }
1158         if(!fncmp(filename, sector + secofs + 33, sector[secofs + 32]))
1159         {
1160             lba = 150 + readmsb32(sector + secofs + 6);
1161             if(bytes)
1162             {
1163                 *bytes = readmsb32(sector + secofs + 14);
1164             }
1165             return lba;
1166         }
1167         secofs += sector[secofs];
1168         dirlen -= sector[secofs];
1169     }
1170     // didn't find the file
1171     return 0;
1172 }
1173 #endif
1174 
1175 /////////////////////////////////////////////////////////////////////////////
1176 
pak_term()1177 void pak_term()
1178 {
1179     int i;
1180     if(!pak_initialized)
1181     {
1182         return;
1183     }
1184     if(pak_cdheader != NULL)
1185     {
1186         free(pak_cdheader);
1187         pak_cdheader = NULL;
1188     }
1189     filecache_term();
1190     close(pakfd);
1191     pakfd           = -1;
1192     paksize         = -1;
1193     pak_headerstart = -1;
1194     pak_headersize  = -1;
1195     for(i = 0; i < MAXPACKHANDLES; i++)
1196     {
1197         pak_vfdexists[i]    = -1;
1198         pak_vfdstart[i]     = -1;
1199         pak_vfdsize[i]      = -1;
1200         pak_vfdpos[i]       = -1;
1201         pak_vfdreadahead[i] = -1;
1202     }
1203     pak_initialized = 0;
1204 }
1205 
1206 /////////////////////////////////////////////////////////////////////////////
1207 
pak_init()1208 int pak_init()
1209 {
1210     int i;
1211     unsigned char *sectors;
1212     unsigned int magic, version;
1213 
1214     if(pak_initialized)
1215     {
1216         printf("pak_init already initialized!");
1217         return 0;
1218     }
1219 
1220 #if WIN || LINUX
1221     if(isRawData())
1222     {
1223         pak_initialized = 1;
1224         return 0;
1225     }
1226 #endif
1227 
1228     pOpenPackfile = openPackfileCached;
1229     pReadPackfile = readPackfileCached;
1230     pSeekPackfile = seekPackfileCached;
1231     pClosePackfile = closePackfileCached;
1232 
1233 #if DC
1234     if(cd_lba)
1235     {
1236         paksize = 0;
1237         pakfd = find_iso_file(packfile, cd_lba, &paksize);
1238         if(pakfd <= 0)
1239         {
1240             printf("unable to find pak file on cd\n");
1241             return 0;
1242         }
1243         pakfd = -pakfd;
1244     }
1245     else
1246     {
1247 #endif
1248 	int per = 666;
1249         pakfd = open(packfile, O_RDONLY | O_BINARY, per);
1250 
1251         if(pakfd < 0)
1252         {
1253             printf("error opening %s (%d) - could not get a valid device descriptor.\n%s\n", packfile, pakfd, strerror(errno));
1254             return 0;
1255         }
1256 
1257         paksize = lseek(pakfd, 0, SEEK_END);
1258 
1259 #ifdef DC
1260     }
1261 #endif
1262 
1263     // Is it a valid Packfile
1264     close(pakfd);
1265     pakfd = open(packfile, O_RDONLY | O_BINARY, per);
1266 
1267     // Read magic dword ("PACK")
1268     if(read(pakfd, &magic, 4) != 4 || magic != SwapLSB32(PACKMAGIC))
1269     {
1270         close(pakfd);
1271         return -1;
1272     }
1273     // Read version from packfile
1274     if(read(pakfd, &version, 4) != 4 || version != SwapLSB32(PACKVERSION))
1275     {
1276         close(pakfd);
1277         return -1;
1278     }
1279 
1280     sectors = malloc(4096);
1281     if(!sectors)
1282     {
1283         printf("sector malloc failed\n");
1284         return 0;
1285     }
1286     {
1287         int getptrfrom = paksize - 4;
1288         if(pak_getsectors(sectors, getptrfrom >> 11, 2) < 1)
1289         {
1290             printf("unable to read pak header pointer\n");
1291             return 0;
1292         }
1293         pak_headerstart = readlsb32(sectors + (getptrfrom & 0x7FF));
1294     }
1295     free(sectors);
1296     if(pak_headerstart >= paksize || pak_headerstart < 0)
1297     {
1298         printf("invalid pak header pointer\n");
1299         return 0;
1300     }
1301     pak_headersize = paksize - pak_headerstart;
1302     {
1303         // let's cache it on CD sector boundaries
1304         int pak_cdheaderstart = pak_headerstart & (~0x7FF);
1305         int pak_cdheadersize = ((paksize - pak_cdheaderstart) + 0x7FF) & (~0x7FF);
1306         if(pak_cdheadersize > 524288)
1307         {
1308             // Original value was 262144, which has been doubled.
1309             // I can not find a reason why it was orginally set to
1310             // this size.  Hence, I have doubled it.  This could
1311             // pose a problem on optical media, but that is yet to be
1312             // determined.
1313             printf("Warning: pak header is too large: %d / 524288\n", pak_cdheadersize);
1314             //return 0;
1315         }
1316         pak_cdheader = malloc(pak_cdheadersize);
1317         if(!pak_cdheader)
1318         {
1319             printf("pak_cdheader malloc failed\n");
1320             return 0;
1321         }
1322         if(pak_getsectors(pak_cdheader, pak_cdheaderstart >> 11, pak_cdheadersize >> 11) != (pak_cdheadersize >> 11))
1323         {
1324             printf("unable to read pak header\n");
1325             return 0;
1326         }
1327         // ok, header is now cached
1328         pak_header = pak_cdheader + (pak_headerstart & 0x7FF);
1329     }
1330     // header does not include the last 4-byte stuff
1331     if(pak_headersize >= 4)
1332     {
1333         pak_headersize -= 4;
1334         // add a trailing null o/~
1335         pak_header[pak_headersize] = 0;
1336     }
1337     //printf("pak cached header (%d bytes)\n", pak_headersize);
1338     // initialize vfd table
1339     for(i = 0; i < MAXPACKHANDLES; i++)
1340     {
1341         pak_vfdexists[i] = 0;
1342     }
1343     // finally, initialize the filecache
1344     filecache_init(pakfd, (paksize + 0x7FF) / 0x800, CACHEBLOCKSIZE, CACHEBLOCKS, MAXPACKHANDLES);
1345     pak_initialized = 1;
1346     return (CACHEBLOCKSIZE * CACHEBLOCKS + 64);
1347 }
1348 
1349 /////////////////////////////////////////////////////////////////////////////
1350 
packfileeof(int handle)1351 int packfileeof(int handle)
1352 {
1353     if(!pak_isvalidhandle(handle))
1354     {
1355         return -1;
1356     }
1357     return (pak_vfdpos[handle] >= pak_vfdsize[handle]);
1358 }
1359 
1360 /////////////////////////////////////////////////////////////////////////////
1361 
packfile_supported(const char * filename)1362 int packfile_supported(const char *filename)
1363 {
1364     if(stricmp(filename, "menu.pak") != 0)
1365     {
1366         if (stristr(filename, ".pak"))
1367         {
1368             return 1;
1369         }
1370     }
1371     return 0;
1372 }
1373 
1374 /////////////////////////////////////////////////////////////////////////////
1375 
packfile_get_titlename(char In[MAX_FILENAME_LEN],char Out[MAX_FILENAME_LEN])1376 void packfile_get_titlename(char In[MAX_FILENAME_LEN], char Out[MAX_FILENAME_LEN])
1377 {
1378     int i, x = 0, y = 0;
1379     for(i = 0; i < (int)strlen(In); i++)
1380     {
1381         if((In[i] == '/') || (In[i] == '\\'))
1382         {
1383             x = i;
1384         }
1385     }
1386     for(i = 0; i < (int)strlen(In); i++)
1387     {
1388         if(i > x)
1389         {
1390             Out[y] = In[i];
1391             y++;
1392         }
1393     }
1394 }
1395 
packfile_music_read(fileliststruct * filelist,int dListTotal)1396 void packfile_music_read(fileliststruct *filelist, int dListTotal)
1397 {
1398     pnamestruct pn;
1399     FILE *fd;
1400     int len, i;
1401     unsigned int off;
1402     char pack[4], *p = NULL;
1403     for(i = 0; i < dListTotal; i++)
1404     {
1405         getBasePath(packfile, filelist[i].filename, 1);
1406         if(stristr(packfile, ".pak"))
1407         {
1408             memset(filelist[i].bgmTracks, 0, MAX_TRACKS * sizeof(unsigned int));
1409             filelist[i].nTracks = 0;
1410             fd = fopen(packfile, "rb");
1411             if(fd == NULL)
1412             {
1413                 continue;
1414             }
1415             if(!fread(pack, 4, 1, fd))
1416             {
1417                 goto closepak;
1418             }
1419             if(fseek(fd, -4, SEEK_END) < 0)
1420             {
1421                 goto closepak;
1422             }
1423             if(!fread(&off, 4, 1, fd))
1424             {
1425                 goto closepak;
1426             }
1427             if(fseek(fd, off, SEEK_SET) < 0)
1428             {
1429                 goto closepak;
1430             }
1431             while((len = fread(&pn, 1, sizeof(pn), fd)) > 12)
1432             {
1433                 p = strrchr(pn.namebuf, '.');
1434                 if((p && (!stricmp(p, ".bor") || !stricmp(p, ".ogg"))) || (stristr(pn.namebuf, "music")))
1435                 {
1436                     if(!stristr(pn.namebuf, ".bor") && !stristr(pn.namebuf, ".ogg"))
1437                     {
1438                         goto nextpak;
1439                     }
1440                     if(filelist[i].nTracks < MAX_TRACKS)
1441                     {
1442                         packfile_get_titlename(pn.namebuf, filelist[i].bgmFileName[filelist[i].nTracks]);
1443                         filelist[i].bgmTracks[filelist[i].nTracks] = off;
1444                         filelist[i].nTracks++;
1445                     }
1446                 }
1447 nextpak:
1448                 off += pn.pns_len;
1449                 if(fseek(fd, off, SEEK_SET) < 0)
1450                 {
1451                     goto closepak;
1452                 }
1453             }
1454 closepak:
1455             fclose(fd);
1456         }
1457     }
1458 }
1459 
1460 /////////////////////////////////////////////////////////////////////////////
1461 
packfile_music_play(struct fileliststruct * filelist,FILE * bgmFile,int bgmLoop,int curPos,int scrPos)1462 int packfile_music_play(struct fileliststruct *filelist, FILE *bgmFile, int bgmLoop, int curPos, int scrPos)
1463 {
1464     pnamestruct pn;
1465     int len;
1466     getBasePath(packfile, filelist[curPos + scrPos].filename, 1);
1467     if (bgmFile)
1468     {
1469         fclose(bgmFile);
1470         bgmFile = NULL;
1471     }
1472     bgmFile = fopen(packfile, "rb");
1473     if (!bgmFile)
1474     {
1475         return 0;
1476     }
1477     if (stristr(packfile, ".pak"))
1478     {
1479         if(fseek(bgmFile, filelist[curPos + scrPos].bgmTracks[filelist[curPos + scrPos].bgmTrack], SEEK_SET) < 0)
1480         {
1481             return 0;
1482         }
1483         if((len = fread(&pn, 1, sizeof(pn), bgmFile)) > 12)
1484         {
1485             sound_open_music(pn.namebuf, packfile, savedata.musicvol, bgmLoop, 0);
1486         }
1487     }
1488     return 1;
1489 }
1490 
1491 #endif
1492 
1493 
1494 
1495