1 /*
2  *  Abuse - dark 2D side-scrolling platform game
3  *  Copyright (c) 1995 Crack dot Com
4  *  Copyright (c) 2005-2011 Sam Hocevar <sam@hocevar.net>
5  *
6  *  This software was released into the Public Domain. As with most public
7  *  domain software, no warranty is made or implied by Crack dot Com, by
8  *  Jonathan Clark, or by Sam Hocevar.
9  */
10 
11 #if defined HAVE_CONFIG_H
12 #   include "config.h"
13 #endif
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stdint.h>
18 #include <ctype.h>
19 #include <fcntl.h>
20 #include <math.h>
21 #include <unistd.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 
25 #include "common.h"
26 
27 #include "image.h"
28 #include "palette.h"
29 #include "specs.h"
30 #include "dprint.h"
31 
32 char const *spec_types[] =
33 {
34     "Invalid type", // 0
35     "Color table",  // 1
36     "Palette",      // 2
37     "Invalid Type", // 3
38     "Image",        // 4
39     "Fore Tile",
40     "Back Tile",
41     "Character",
42     "8 Morph",
43     "16 Morph",
44     "Grue objs",
45     "Extern WAV",
46     "DMX MUS",
47     "Patched morph",
48     "Normal file",
49     "Compress1 file",
50     "Vector Image",
51     "Light list",
52     "Grue fgmap",
53     "Grue bgmap",
54     "Data array",
55     "Character2",
56     "Particle",
57     "Extern lcache"
58 };
59 
60 
61 int total_files_open=0;
62 char spec_main_file[100];
63 
64 static char *spec_prefix=NULL;
65 static char *save_spec_prefix=NULL;
66 
67 static jFILE spec_main_jfile((FILE*)0);
68 static int spec_main_fd = -1;
69 static long spec_main_offset = -1;
70 static spec_directory spec_main_sd;
71 
set_filename_prefix(char const * prefix)72 void set_filename_prefix(char const *prefix)
73 {
74     if( spec_prefix )
75     {
76         free( spec_prefix );
77     }
78 
79     if( prefix )
80     {
81         spec_prefix = strcpy( (char *)malloc( strlen( prefix ) + 2 ), prefix );
82         int len = strlen( prefix );
83         if( prefix[len - 1] != '\\' && prefix[len - 1] != '/')
84         {
85             spec_prefix[len] = '/';
86             spec_prefix[len + 1] = 0;
87         }
88     }
89     else
90     {
91         spec_prefix = NULL;
92     }
93 }
94 
get_filename_prefix()95 char *get_filename_prefix()
96 {
97     return spec_prefix;
98 }
99 
100 
set_save_filename_prefix(char const * save_prefix)101 void set_save_filename_prefix(char const *save_prefix)
102 {
103     if( save_spec_prefix )
104     {
105         free( save_spec_prefix );
106     }
107 
108     if( save_prefix )
109     {
110         int len = strlen( save_prefix );
111         save_spec_prefix = (char *)malloc( len + 1 );
112         strcpy( save_spec_prefix, save_prefix );
113 /* AK - Commented this out as it may cause problems
114         if( save_prefix[len - 1] != '\\' && save_prefix[len - 1] != '/' )
115         {
116             save_spec_prefix[len] = '/';
117             save_spec_prefix[len + 1] = '\0';
118         } */
119     }
120     else
121     {
122         save_spec_prefix = NULL;
123     }
124 }
125 
get_save_filename_prefix()126 char *get_save_filename_prefix()
127 {
128   return save_spec_prefix;
129 }
130 
131 int search_order=SPEC_SEARCH_OUTSIDE_INSIDE;
132 
133 static void (*no_space_handle_fun)()=NULL;
134 
set_no_space_handler(void (* handle_fun)())135 void set_no_space_handler(void (*handle_fun)())
136 {
137   no_space_handle_fun=handle_fun;
138 }
139 
140 
bFILE()141 bFILE::bFILE()
142 {
143   rbuf_size=8192;
144   rbuf=(unsigned char *)malloc(rbuf_size);
145   rbuf_start=rbuf_end=0;
146 
147   wbuf_size=8192;
148   wbuf=(unsigned char *)malloc(wbuf_size);
149   wbuf_end=0;
150 }
151 
~bFILE()152 bFILE::~bFILE()
153 {
154   if (rbuf) free(rbuf);
155   flush_writes();
156   if (wbuf) free(wbuf);
157 }
158 
flush_writes()159 int bFILE::flush_writes()
160 {
161   if (wbuf_end!=0)
162   {
163     unsigned long ret=unbuffered_write(wbuf,wbuf_end);
164     if (ret!=wbuf_end && no_space_handle_fun)
165       no_space_handle_fun();
166 
167     wbuf_end=0;
168     return ret;
169   }
170   return 0;
171 }
172 
read(void * buf,size_t count)173 int bFILE::read(void *buf, size_t count)       // returns number of bytes read, calls unbuffer_read
174 {
175   if (!allow_read_buffering())
176     return unbuffered_read(buf,count);
177 
178   int total_read=0,error=0;
179   if (!count) return 0;
180   while (count && !error)
181   {
182     if (rbuf_start<rbuf_end)
183     {
184       unsigned int avail_size=rbuf_end-rbuf_start;
185       int copy_size=avail_size>count ? count : avail_size;
186       memcpy(buf,rbuf+rbuf_start,copy_size);
187       buf=(void *)(((unsigned char *)buf)+copy_size);
188       rbuf_start+=copy_size;
189       if (rbuf_start>=rbuf_end)
190       {
191                 if (rbuf_end!=rbuf_size)  // buffer wasn't full before so there is no way we can complete read
192                   error=1;
193                 rbuf_start=rbuf_end=0;
194       }
195       total_read+=copy_size;
196       count-=copy_size;
197     } else
198     {
199       rbuf_end=unbuffered_read(rbuf,rbuf_size);
200       if (rbuf_end==0) error=1;
201       rbuf_start=0;
202     }
203   }
204   return total_read;
205 }
206 
207 
write(void const * buf,size_t count)208 int bFILE::write(void const *buf, size_t count)      // returns number of bytes written
209 {
210   if (allow_write_buffering())
211   {
212     int total_written=0;
213     while (count)
214     {
215       int copy_size=wbuf_end+count<=wbuf_size ? count :  wbuf_size-wbuf_end;
216       memcpy(wbuf+wbuf_end,buf,copy_size);
217       wbuf_end+=copy_size;
218       count-=copy_size;
219       buf=(void *)(((char *)buf)+copy_size);
220       if (wbuf_end==wbuf_size)
221         if ((unsigned int)flush_writes()!=wbuf_size)
222       return total_written;
223 
224       total_written+=copy_size;
225     }
226     return total_written;
227   } else
228   {
229     unsigned long ret=unbuffered_write(buf,count);
230     if (ret!=count && no_space_handle_fun)
231       no_space_handle_fun();
232   }
233   return 0;
234 }
235 
seek(long offset,int whence)236 int bFILE::seek(long offset, int whence) // whence=SEEK_SET, SEEK_CUR, SEEK_END, ret=0=success
237 {
238 //    rbuf_start=rbuf_end=0;
239 //    unbuffered_seek(offset,SEEK_SET);
240 
241   long realpos=unbuffered_tell();
242   long curpos=realpos-rbuf_end+rbuf_start;
243   if (whence==SEEK_CUR) offset+=curpos;
244   else if (whence==SEEK_END) offset=file_size()-offset;
245 
246   if (offset<realpos-(long)rbuf_end || offset>=realpos)
247   {
248     rbuf_start=rbuf_end=0;
249     unbuffered_seek(offset,SEEK_SET);
250   } else
251     rbuf_start=rbuf_end-(realpos-offset);
252   return 1;
253 }
254 
tell()255 int bFILE::tell()
256 {
257   return unbuffered_tell()-rbuf_end+rbuf_start+
258          wbuf_end;    // if this a write file, add on how much we've written
259 }
260 
allow_read_buffering()261 int bFILE::allow_read_buffering() { return 1; }
allow_write_buffering()262 int bFILE::allow_write_buffering() { return 1; }
263 
set_spec_main_file(char const * filename,int Search_order)264 void set_spec_main_file(char const *filename, int Search_order)
265 {
266   dprintf("Specs : main file set to %s\n",filename);
267   strcpy(spec_main_file,filename);
268   search_order=Search_order;
269 
270 #if (defined(__APPLE__) && !defined(__MACH__))
271   spec_main_jfile.open_external(filename,"rb",O_BINARY|O_RDONLY);
272 #else
273   spec_main_jfile.open_external(filename,"rb",O_RDONLY);
274 #endif
275   spec_main_fd = spec_main_jfile.get_fd();
276   if (spec_main_fd==-1)
277     return;
278   spec_main_sd.startup(&spec_main_jfile);
279 }
280 
jFILE(FILE * file_pointer)281 jFILE::jFILE(FILE *file_pointer)                       // assumes fp is at begining of file
282 {
283   access=0;
284   fd=-1;
285   file_length=0;
286   start_offset=0;
287   flags=JFILE_CLONED;
288 }
289 
open_external(char const * filename,char const * mode,int flags)290 void jFILE::open_external(char const *filename, char const *mode, int flags)
291 {
292   int skip_size=0;
293   char tmp_name[200];
294   if (spec_prefix && filename[0] != '/')
295     sprintf(tmp_name,"%s%s",spec_prefix,filename);
296   else strcpy(tmp_name,filename);
297 
298 //  int old_mask=umask(S_IRWXU | S_IRWXG | S_IRWXO);
299   if (flags&O_WRONLY)
300   {
301     if ((flags&O_APPEND)==0)
302     {
303       skip_size=1;
304       //int errval = unlink(tmp_name);
305     }
306 
307     flags-=O_WRONLY;
308     flags|=O_CREAT|O_RDWR;
309 
310     fd=open(tmp_name,flags,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
311   } else
312     fd=open(tmp_name,flags);
313 
314 //  umask(old_mask);
315   if (fd>=0 && !skip_size)
316   {
317     file_length=lseek(fd,0,SEEK_END);
318     if ((flags&O_APPEND)==0)
319       lseek(fd,0,SEEK_SET);
320     else
321         current_offset = file_length;
322     start_offset=0;
323   } else
324   {
325     file_length=0;
326     start_offset=0;
327   }
328 }
329 
330 
331 class null_file : public bFILE     // this file type will use virtual opens inside of a spe
332 {
333   public :
open_failure()334   virtual int open_failure() { return 1; }
unbuffered_read(void * buf,size_t count)335   virtual int unbuffered_read(void *buf, size_t count)   { return 0; }
unbuffered_write(void const * buf,size_t count)336   virtual int unbuffered_write(void const *buf, size_t count)  { return 0; }
unbuffered_seek(long offset,int whence)337   virtual int unbuffered_seek(long offset, int whence)   { return 0; }
338 
unbuffered_tell()339   virtual int unbuffered_tell() { return 0; }
file_size()340   virtual int file_size() { return 0; }
~null_file()341   virtual ~null_file() { ; }
342 } ;
343 
344 
345 static bFILE *(*open_file_fun)(char const *,char const *)=NULL;
346 int (*verify_file_fun)(char const *,char const *)=NULL;
347 
set_file_opener(bFILE * (* open_fun)(char const *,char const *))348 void set_file_opener(bFILE *(*open_fun)(char const *, char const *))
349 {
350   open_file_fun=open_fun;
351 }
352 
open_file(char const * filename,char const * mode)353 bFILE *open_file(char const *filename, char const *mode)
354 {
355   if (!verify_file_fun || verify_file_fun(filename,mode))
356   {
357     if (open_file_fun)
358       return open_file_fun(filename,mode);
359     else return new jFILE(filename,mode);
360   } else return new null_file;
361 }
362 
open_internal(char const * filename,char const * mode,int flags)363 void jFILE::open_internal(char const *filename, char const *mode, int flags)
364 {
365   int wr=0;
366   for (char const *s=mode; *s; s++)
367     if (toupper(*s)=='A' || toupper(*s)=='W')
368       wr=1;
369 
370   if (wr)
371     fd=-1;                 // only allow extern file openings for writing
372   else
373   {
374        fd = spec_main_fd;
375     if (fd>=0)                    // if we were able to open the main file, see if it's in there
376     {
377       start_offset=0;
378       spec_entry *se=spec_main_sd.find(filename);
379       if (se)
380       {
381     start_offset=se->offset;
382     current_offset = 0;
383     file_length=se->size;
384     rbuf_start=rbuf_end=0;
385       } else
386       {
387     close(fd);
388     fd=-1;
389       }
390     }
391   }
392 }
393 
jFILE(char const * filename,char const * access_string)394 jFILE::jFILE(char const *filename, char const *access_string)      // same as fopen parameters
395 {
396  flags=access=0;
397  char const *s=access_string;
398   for (; *s; s++)
399     if (toupper(*s)=='R') access=O_RDONLY;
400 
401   for (s=access_string; *s; s++)
402     if (toupper(*s)=='W')
403     {
404       if (access)
405         access=O_RDWR;
406       else access=O_WRONLY;
407     }
408 
409   for (s=access_string; *s; s++)
410     if (toupper(*s)=='A')
411       access|=O_APPEND|O_WRONLY;
412 
413   file_length=start_offset=-1;
414   current_offset = 0;
415 
416   fd=-1;
417   if (search_order==SPEC_SEARCH_OUTSIDE_INSIDE)
418     open_external(filename,access_string,access);
419 
420   if (fd<0)
421     open_internal(filename,access_string,access);
422 
423   if (fd<0 && search_order==SPEC_SEARCH_INSIDE_OUTSIDE)
424     open_external(filename,access_string,access);
425 
426   total_files_open++;
427 }
428 
~jFILE()429 jFILE::~jFILE()
430 {
431   flush_writes();
432   if (fd>=0 && !(flags&JFILE_CLONED))
433   {
434     total_files_open--;
435     if (fd != spec_main_fd)
436         close(fd);
437   }
438 }
439 
unbuffered_tell()440 int jFILE::unbuffered_tell()
441 {
442 //    int ret = ::lseek(fd,0,SEEK_CUR) - start_offset;
443 //    if (ret != current_offset)
444 //        fprintf(stderr,"Bad tell %d\n",current_offset);
445     return current_offset;
446 }
447 
unbuffered_read(void * buf,size_t count)448 int jFILE::unbuffered_read(void *buf, size_t count)
449 {
450     unsigned long len;
451 
452     if (fd == spec_main_fd)
453     {
454         if (current_offset+start_offset != spec_main_offset)
455             spec_main_offset = lseek(fd, start_offset+current_offset, SEEK_SET);
456 
457         len = ::read(fd,(char*)buf,count);
458         spec_main_offset += len;
459     }
460     else
461     {
462       len = ::read(fd,(char*)buf,count);
463     }
464     current_offset += len;
465     return len;
466 }
467 
unbuffered_write(void const * buf,size_t count)468 int jFILE::unbuffered_write(void const *buf, size_t count)
469 {
470   long ret = ::write(fd,(char*)buf,count);
471     current_offset += ret;
472     return ret;
473 }
474 
unbuffered_seek(long offset,int whence)475 int jFILE::unbuffered_seek(long offset, int whence) // whence=SEEK_SET, SEEK_CUR, SEEK_END, ret=0=success
476 {
477   long ret;
478 
479   switch (whence)
480   {
481     case SEEK_SET :
482     { ret = lseek(fd,start_offset+offset,SEEK_SET); } break;
483     case SEEK_END :
484     { ret = lseek(fd,start_offset+file_length-offset,SEEK_SET); } break;
485     case SEEK_CUR :
486     { ret = lseek(fd,offset,SEEK_CUR); } break;
487     default:
488         ret = -1;
489         break;
490   }
491   if (ret>=0)
492   {
493     current_offset = ret - start_offset;
494     if (spec_main_fd == fd)
495       spec_main_offset = ret;
496     return ret;
497   }
498   else
499     return -1;  // if a bad whence, then failure
500 }
501 
502 
read_uint8()503 uint8_t bFILE::read_uint8()
504 { uint8_t x;
505   read(&x,1);
506   return x;
507 }
508 
read_uint16()509 uint16_t bFILE::read_uint16()
510 {
511   uint16_t x;
512   read(&x,2);
513   return lstl(x);
514 }
515 
516 
read_uint32()517 uint32_t bFILE::read_uint32()
518 {
519   uint32_t x;
520   read(&x,4);
521   return lltl(x);
522 }
523 
write_uint8(uint8_t x)524 void bFILE::write_uint8(uint8_t x)
525 {
526   write(&x,1);
527 }
528 
write_uint16(uint16_t x)529 void bFILE::write_uint16(uint16_t x)
530 {
531   x=lstl(x);
532   write(&x,2);
533 }
534 
535 
write_uint32(uint32_t x)536 void bFILE::write_uint32(uint32_t x)
537 {
538   x=lltl(x);
539   write(&x,4);
540 }
541 
write_double(double x)542 void bFILE::write_double(double x)
543 {
544   double a;
545   write_uint32((long)(modf(x,&a)*(double)(1<<31)));
546   write_uint32((long)a);
547 }
548 
read_double()549 double bFILE::read_double()
550 {
551   long a,b;
552   a=read_uint32();
553   b=read_uint32();
554   return (double)b+a/(double)(1<<31);
555 }
556 
~spec_directory()557 spec_directory::~spec_directory()
558 {
559 
560   if (total)
561   {
562     free(data);
563     free(entries);
564   }
565 }
566 
FullyLoad(bFILE * fp)567 void spec_directory::FullyLoad(bFILE *fp)
568 {
569     for (int i = 0; i < total; i++)
570     {
571         spec_entry *se = entries[i];
572         free(se->data);
573         se->data = malloc(se->size);
574         fp->seek(se->offset, SEEK_SET);
575         fp->read(se->data, se->size);
576     }
577 }
578 
spec_entry(uint8_t spec_type,char const * object_name,char const * link_filename,unsigned long data_size,unsigned long data_offset)579 spec_entry::spec_entry(uint8_t spec_type, char const *object_name,
580                        char const *link_filename,
581                        unsigned long data_size, unsigned long data_offset)
582 {
583     type = spec_type;
584     name = strdup(object_name);
585     data = NULL;
586     size = data_size;
587     offset = data_offset;
588 }
589 
~spec_entry()590 spec_entry::~spec_entry()
591 {
592     free(name);
593     free(data);
594 }
595 
Print()596 void spec_entry::Print()
597 {
598     printf("%15s%25s%8ld%8ld\n", spec_types[type], name, size, offset);
599 }
600 
calc_offsets()601 void spec_directory::calc_offsets()
602 {
603     size_t o = SPEC_SIG_SIZE + 2;
604 
605     // calculate the size of directory info
606     for (int i = 0; i < total; i++)
607         o += 1 + 1 + strlen(entries[i]->name) + 1 + 1 + 8;
608 
609     // calculate offset for each entry
610     for (int i = 0; i < total; i++)
611     {
612         entries[i]->offset = o;
613         o += entries[i]->size;
614     }
615 }
616 
find(char const * name,int type)617 spec_entry *spec_directory::find(char const *name, int type)
618 {
619   int i;
620   spec_entry **e;
621   for (i=0,e=entries; i<total; i++,e++)
622     if (!strcmp((*e)->name,name) && (*e)->type==type)
623       return (*e);
624   return NULL;
625 }
626 
find(char const * name)627 spec_entry *spec_directory::find(char const *name)
628 {
629   int i;
630   spec_entry **e;
631   for (i=0,e=entries; i<total; i++,e++)
632     if (!strcmp((*e)->name,name))
633       return (*e);
634   return NULL;
635 }
636 
find_number(char const * name)637 long spec_directory::find_number(char const *name)
638 {
639   int i;
640   spec_entry **e;
641   for (i=0,e=entries; i<total; i++,e++)
642     if (!strcmp((*e)->name,name))
643       return i;
644   return -1;
645 }
646 
find(int type)647 spec_entry *spec_directory::find(int type)
648 {
649   int i;
650   spec_entry **e;
651   for (i=0,e=entries; i<total; i++,e++)
652     if ((*e)->type==type)
653       return (*e);
654   return NULL;
655 }
656 
type_total(int type)657 long spec_directory::type_total(int type)
658 {
659   int i,x=0;
660   spec_entry **e;
661   for (i=0,e=entries; i<total; i++,e++)
662     if ((*e)->type==type) x++;
663   return x;
664 }
665 
find_number(int type)666 long spec_directory::find_number(int type)
667 {
668   int i;
669   spec_entry **e;
670   for (i=0,e=entries; i<total; i++,e++)
671     if ((*e)->type==type)
672       return i;
673   return -1;
674 }
675 
print()676 void spec_directory::print()
677 {
678   spec_entry **se;
679   int i;
680   printf("[   Entry type   ][   Entry name   ][  Size  ][ Offset ]\n");
681   for (i=0,se=entries; i<total; i++,se++)
682     (*se)->Print();
683 }
684 
685 
startup(bFILE * fp)686 void spec_directory::startup(bFILE *fp)
687 {
688   char buf[256];
689   memset(buf,0,256);
690   fp->read(buf,8);
691   buf[9]=0;
692   size=0;
693   if (!strcmp(buf,SPEC_SIGNATURE))
694   {
695     total=fp->read_uint16();
696     entries=(spec_entry **)malloc(sizeof(spec_entry *)*total);
697     long start=fp->tell();
698 
699     int i;
700     for (i=0; i<total; i++)
701     {
702       fp->read(buf,2);
703       long entry_size=sizeof(spec_entry)+(unsigned char)buf[1];
704       entry_size=(entry_size+3)&(~3);
705       fp->read(buf,(unsigned char)buf[1]);
706       fp->read(buf,9);
707 
708       size+=entry_size;
709     }
710     data=malloc(size);
711     char *dp=(char *)data;
712     fp->seek(start,SEEK_SET);
713     for (i=0; i<total; i++)
714     {
715       spec_entry *se=(spec_entry *)dp;
716       entries[i]=se;
717 
718       unsigned char len,flags,type;
719       fp->read(&type,1);
720       fp->read(&len,1);
721       se->type=type;
722       se->data = NULL;
723       se->name=dp+sizeof(spec_entry);
724       fp->read(se->name,len);
725       fp->read(&flags,1);
726 
727       se->size=fp->read_uint32();
728       se->offset=fp->read_uint32();
729       dp+=((sizeof(spec_entry)+len)+3)&(~3);
730     }
731   }
732   else
733   {
734     total=0;
735     data=NULL;
736     entries=NULL;
737   }
738 }
739 
740 
spec_directory(bFILE * fp)741 spec_directory::spec_directory(bFILE *fp)
742 { startup(fp); }
743 
spec_directory(FILE * fp)744 spec_directory::spec_directory(FILE *fp)
745 {
746   jFILE jfp(fp);
747   startup(&jfp);
748 }
749 
spec_directory()750 spec_directory::spec_directory()
751 {
752   size=0;
753   total=0;
754   data=NULL;
755   entries=NULL;
756 }
757 
758 /*
759 spec_directory::spec_directory(char *filename)
760 {
761   jFILE *fp;
762   if (filename)
763   {
764     fp=new jFILE(filename,"rb");
765     if (!fp->open_failure())
766       startup(fp);
767     else
768     {
769       total=0;
770       entries=NULL;
771     }
772     delete fp;
773   } else printf("NULL filename to spec_directory::spec_directory\n");
774 }*/
775 
write_string(bFILE * fp,char const * st)776 int write_string(bFILE *fp, char const *st)
777 {
778   unsigned char length=strlen(st)+1;
779   if (fp->write(&length,1)!=1) return 0;
780   if (fp->write(st,length)!=length) return 0;
781   return 1;
782 }
783 
data_start_offset()784 long spec_directory::data_start_offset()
785 {
786     /* FIXME: no need for a for loop here! */
787     long i;
788     for(i = 0; i < total; i++)
789         return entries[i]->offset;
790 
791     // If no entries, then no data, but return where it would start anyway
792     return SPEC_SIG_SIZE + 2;
793 }
794 
data_end_offset()795 long spec_directory::data_end_offset()
796 {
797     /* FIXME: no need for a for loop here! */
798   spec_entry **e;
799   long i;
800   for (i=total-1,e=entries; i>=0; i--,e++)
801     return (*e)->offset+(*e)->size;
802 
803   return SPEC_SIG_SIZE+2;
804 }
805 
write(bFILE * fp)806 int spec_directory::write(bFILE *fp)
807 {
808 
809   char sig[SPEC_SIG_SIZE];
810   unsigned char flags=0;
811   unsigned long offset,data_size;
812   spec_entry **e;
813   strcpy(sig,SPEC_SIGNATURE);
814 
815   if (fp->write(sig,sizeof(sig))!=sizeof(sig))    return 0;
816   fp->write_uint16(total);
817 
818 
819   int i;
820   for (i=0,e=entries; i<total; i++,e++)
821   {
822     if (fp->write(&(*e)->type,1)!=1)                 return 0;
823     if (!write_string(fp,(*e)->name))                return 0;
824     flags=0;
825     if (fp->write(&flags,1)!=1)                     return 0;
826 
827     data_size=lltl((*e)->size);
828     if (fp->write((char *)&data_size,4)!=4)              return 0;
829     offset=lltl((*e)->offset);
830     if (fp->write((char *)&offset,4)!=4)                  return 0;
831 
832   }
833   return 1;
834 }
835 
write(char const * filename)836 jFILE *spec_directory::write(char const *filename)
837 {
838   jFILE *fp;
839   fp=new jFILE(filename,"wb");
840   if (fp->open_failure()) { delete fp; return NULL; }
841   if (!write(fp))
842   {
843     delete fp;
844     return NULL;
845   } else return fp;
846 
847 }
848 
read_uint16(FILE * fp)849 uint16_t read_uint16(FILE *fp)
850 {
851   uint16_t x;
852   fread(&x,1,2,fp);
853   return lstl(x);
854 }
855 
read_uint32(FILE * fp)856 uint32_t read_uint32(FILE *fp)
857 {
858   uint32_t x;
859   fread(&x,1,4,fp);
860   return lltl(x);
861 }
write_uint16(FILE * fp,uint16_t x)862 void write_uint16(FILE *fp, uint16_t x)
863 {
864   x=lstl(x);
865   fwrite(&x,1,2,fp);
866 }
867 
write_uint32(FILE * fp,uint32_t x)868 void write_uint32(FILE *fp, uint32_t x)
869 {
870   x=lltl(x);
871   fwrite(&x,1,4,fp);
872 }
873 
read_uint8(FILE * fp)874 uint8_t read_uint8(FILE *fp) { return fgetc(fp)&0xff; }
write_uint8(FILE * fp,uint8_t x)875 void write_uint8(FILE *fp, uint8_t x) { fputc((unsigned char)x,fp); }
876 
remove(spec_entry * e)877 void spec_directory::remove(spec_entry *e)
878 {
879   int i;
880   for (i=0; i<total && entries[i]!=e; i++);            // find the entry in the array first
881 
882   if (entries[i]==e)                                 // make sre it was found
883   {
884     delete e;
885     total--;
886     for (; i<total; i++)                               // compact the pointer array
887       entries[i]=entries[i+1];
888     entries=(spec_entry **)realloc(entries,sizeof(spec_entry *)*total);
889   }
890   else
891     printf("Spec_directory::remove bad entry pointer\n");
892 }
893 
894 
895 
add_by_hand(spec_entry * e)896 void spec_directory::add_by_hand(spec_entry *e)
897 {
898   total++;
899   entries=(spec_entry **)realloc(entries,sizeof(spec_entry *)*total);
900   entries[total-1]=e;
901 }
902 
delete_entries()903 void spec_directory::delete_entries()   // if the directory was created by hand instead of by file
904 {
905   int i;
906   for (i=0; i<total; i++)
907     delete entries[i];
908 
909   if (total)
910     free(entries);
911 }
912 
note_open_fd(int fd,char const * str)913 void note_open_fd(int fd, char const *str)
914 {
915     total_files_open++;
916 }
917 
note_close_fd(int fd)918 void note_close_fd(int fd)
919 {
920     total_files_open--;
921 }
922 
list_open_fds()923 void list_open_fds()
924 {
925     printf("Total open file descriptors: %d\n", total_files_open);
926 }
927 
928