source: abuse/trunk/src/imlib/specs.cpp @ 539

Last change on this file since 539 was 539, checked in by Sam Hocevar, 8 years ago

imlib: implement a small spec file dumper. "abuse -export foo.spe" will
extract the contents of the file and create PCX files from the images.

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