]> icculus.org git repositories - btb/d2x.git/blob - main/piggy.c
fast i/o macros
[btb/d2x.git] / main / piggy.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 #ifdef HAVE_CONFIG_H
15 #include <conf.h>
16 #endif
17
18 #ifdef RCS
19 static char rcsid[] = "$Id: piggy.c,v 1.14 2002-08-02 04:57:19 btb Exp $";
20 #endif
21
22
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "pstypes.h"
27 #include "strutil.h"
28 #include "inferno.h"
29 #include "gr.h"
30 #include "u_mem.h"
31 #include "iff.h"
32 #include "mono.h"
33 #include "error.h"
34 #include "sounds.h"
35 #include "songs.h"
36 #include "bm.h"
37 #include "bmread.h"
38 #include "hash.h"
39 #include "args.h"
40 #include "palette.h"
41 #include "gamefont.h"
42 #include "rle.h"
43 #include "screens.h"
44 #include "piggy.h"
45 #include "texmerge.h"
46 #include "paging.h"
47 #include "game.h"
48 #include "text.h"
49 #include "cfile.h"
50 #include "newmenu.h"
51 #include "byteswap.h"
52 #include "findfile.h"
53 #include "makesig.h"
54
55 #ifndef MACINTOSH
56 //      #include "unarj.h"
57 #else
58         #include <Strings.h>            // MacOS Toolbox header
59         #include <Files.h>
60         #include <unistd.h>
61 #endif
62
63 //#define NO_DUMP_SOUNDS        1               //if set, dump bitmaps but not sounds
64
65 #define DEFAULT_PIGFILE_REGISTERED      "groupa.pig"
66 #define DEFAULT_PIGFILE_SHAREWARE       "d2demo.pig"
67
68
69 #ifdef SHAREWARE
70         #define DEFAULT_HAMFILE         "d2demo.ham"
71         #define DEFAULT_PIGFILE         DEFAULT_PIGFILE_SHAREWARE
72         #define DEFAULT_SNDFILE                 DEFAULT_HAMFILE //"descent2.s11"
73 #else
74         #define DEFAULT_HAMFILE         "descent2.ham"
75         #define DEFAULT_PIGFILE         DEFAULT_PIGFILE_REGISTERED
76         #define DEFAULT_SNDFILE                 ((digi_sample_rate==SAMPLE_RATE_22K)?"descent2.s22":"descent2.s11")
77 #endif  // end of ifdef SHAREWARE
78
79 ubyte *BitmapBits = NULL;
80 ubyte *SoundBits = NULL;
81
82 typedef struct BitmapFile       {
83         char                    name[15];
84 } BitmapFile;
85
86 typedef struct SoundFile        {
87         char                    name[15];
88 } SoundFile;
89
90 hashtable AllBitmapsNames;
91 hashtable AllDigiSndNames;
92
93 int Num_bitmap_files = 0;
94 int Num_sound_files = 0;
95
96 digi_sound GameSounds[MAX_SOUND_FILES];
97 int SoundOffset[MAX_SOUND_FILES];
98 grs_bitmap GameBitmaps[MAX_BITMAP_FILES];
99
100 alias alias_list[MAX_ALIASES];
101 int Num_aliases=0;
102
103 int Must_write_hamfile = 0;
104 int Num_bitmap_files_new = 0;
105 int Num_sound_files_new = 0;
106 BitmapFile AllBitmaps[ MAX_BITMAP_FILES ];
107 static SoundFile AllSounds[ MAX_SOUND_FILES ];
108
109 int piggy_low_memory = 0;
110
111 int Piggy_bitmap_cache_size = 0;
112 int Piggy_bitmap_cache_next = 0;
113 ubyte * Piggy_bitmap_cache_data = NULL;
114 static int GameBitmapOffset[MAX_BITMAP_FILES];
115 static ubyte GameBitmapFlags[MAX_BITMAP_FILES];
116 ushort GameBitmapXlat[MAX_BITMAP_FILES];
117
118 #define PIGGY_BUFFER_SIZE (2400*1024)
119
120 #ifdef MACINTOSH
121 #define PIGGY_SMALL_BUFFER_SIZE (1400*1024)             // size of buffer when piggy_low_memory is set
122
123 #ifdef SHAREWARE
124 #undef PIGGY_BUFFER_SIZE
125 #undef PIGGY_SMALL_BUFFER_SIZE
126
127 #define PIGGY_BUFFER_SIZE (2000*1024)
128 #define PIGGY_SMALL_BUFFER_SIZE (1100 * 1024)
129 #endif          // SHAREWARE
130
131 #endif
132
133 int piggy_page_flushed = 0;
134
135 #define DBM_FLAG_ABM            64
136
137 ubyte BigPig = 0;
138
139 #ifdef MACINTOSH
140         extern short    cd_VRefNum;
141         extern void             ConcatPStr(StringPtr dst, StringPtr src);
142         extern int              ConvertPToCStr(StringPtr inPStr, char* outCStrBuf);
143         extern int              ConvertCToPStr(char* inCStr, StringPtr outPStrBuf);
144 #endif
145
146 int piggy_is_substitutable_bitmap( char * name, char * subst_name );
147
148 #ifdef EDITOR
149 void piggy_write_pigfile(char *filename);
150 static void write_int(int i,FILE *file);
151
152 void swap_0_255(grs_bitmap *bmp)
153 {
154         int i;
155
156         for (i = 0; i < bmp->bm_h * bmp->bm_w; i++) {
157                 if(bmp->bm_data[i] == 0)
158                         bmp->bm_data[i] = 255;
159                 else if (bmp->bm_data[i] == 255)
160                         bmp->bm_data[i] = 0;
161         }
162 }
163 #endif
164
165 bitmap_index piggy_register_bitmap( grs_bitmap * bmp, char * name, int in_file )
166 {
167         bitmap_index temp;
168         Assert( Num_bitmap_files < MAX_BITMAP_FILES );
169
170         temp.index = Num_bitmap_files;
171
172         if (!in_file)   {
173 #ifdef EDITOR
174                 if ( FindArg("-macdata") )
175                         swap_0_255( bmp );
176 #endif
177                 if ( !BigPig )  gr_bitmap_rle_compress( bmp );
178                 Num_bitmap_files_new++;
179         }
180
181         strncpy( AllBitmaps[Num_bitmap_files].name, name, 12 );
182         hashtable_insert( &AllBitmapsNames, AllBitmaps[Num_bitmap_files].name, Num_bitmap_files );
183         GameBitmaps[Num_bitmap_files] = *bmp;
184         if ( !in_file ) {
185                 GameBitmapOffset[Num_bitmap_files] = 0;
186                 GameBitmapFlags[Num_bitmap_files] = bmp->bm_flags;
187         }
188         Num_bitmap_files++;
189
190         return temp;
191 }
192
193 int piggy_register_sound( digi_sound * snd, char * name, int in_file )
194 {
195         int i;
196
197         Assert( Num_sound_files < MAX_SOUND_FILES );
198
199         strncpy( AllSounds[Num_sound_files].name, name, 12 );
200         hashtable_insert( &AllDigiSndNames, AllSounds[Num_sound_files].name, Num_sound_files );
201         GameSounds[Num_sound_files] = *snd;
202         if ( !in_file ) {
203                 SoundOffset[Num_sound_files] = 0;       
204         }
205
206         i = Num_sound_files;
207    
208         if (!in_file)
209                 Num_sound_files_new++;
210
211         Num_sound_files++;
212         return i;
213 }
214
215 bitmap_index piggy_find_bitmap( char * name )   
216 {
217         bitmap_index bmp;
218         int i;
219         char *t;
220
221         bmp.index = 0;
222
223         if ((t=strchr(name,'#'))!=NULL)
224                 *t=0;
225
226         for (i=0;i<Num_aliases;i++)
227                 if (stricmp(name,alias_list[i].alias_name)==0) {
228                         if (t) {                //extra stuff for ABMs
229                                 static char temp[FILENAME_LEN];
230                                 _splitpath(alias_list[i].file_name, NULL, NULL, temp, NULL );
231                                 name = temp;
232                                 strcat(name,"#");
233                                 strcat(name,t+1);
234                         }
235                         else
236                                 name=alias_list[i].file_name; 
237                         break;
238                 }
239
240         if (t)
241                 *t = '#';
242
243         i = hashtable_search( &AllBitmapsNames, name );
244         Assert( i != 0 );
245         if ( i < 0 )
246                 return bmp;
247
248         bmp.index = i;
249         return bmp;
250 }
251
252 int piggy_find_sound( char * name )     
253 {
254         int i;
255
256         i = hashtable_search( &AllDigiSndNames, name );
257
258         if ( i < 0 )
259                 return 255;
260
261         return i;
262 }
263
264 CFILE * Piggy_fp = NULL;
265
266 #define FILENAME_LEN 13
267
268 char Current_pigfile[FILENAME_LEN] = "";
269
270 void piggy_close_file()
271 {
272         if ( Piggy_fp ) {
273                 cfclose( Piggy_fp );
274                 Piggy_fp        = NULL;
275                 Current_pigfile[0] = 0;
276         }
277 }
278
279 int Pigfile_initialized=0;
280
281 #define PIGFILE_ID              MAKE_SIG('G','I','P','P') //PPIG
282 #define PIGFILE_VERSION         2
283
284 extern char CDROM_dir[];
285
286 int request_cd(void);
287
288
289 #ifdef MACINTOSH
290
291 //copies a pigfile from the CD to the current dir
292 //retuns file handle of new pig
293 CFILE *copy_pigfile_from_cd(char *filename)             // MACINTOSH VERSION
294 {
295         // C Stuff
296         char                    sourcePathAndFileCStr[255] = "";
297         char                    destPathAndFileCStr[255]        = "";
298         FILEFINDSTRUCT  find;
299         FILE*                   sourceFile      = NULL;
300         FILE*                   destFile        = NULL;
301         const int               BUF_SIZE = 4096;
302         ubyte                   buf[BUF_SIZE];
303
304         // Mac Stuff
305         Str255                  sourcePathAndFilePStr = "\p";
306         Str255                  destPathAndFilePStr = "\p";
307         Str255                  pigfileNamePStr = "\p";
308         HParamBlockRec  theSourcePigHFSParams;
309         HParamBlockRec  theDestPigHFSParams;
310         OSErr                   theErr = noErr;
311         char                    oldDirCStr[255] = "";
312
313         getcwd(oldDirCStr, 255);
314         
315         show_boxed_message("Copying bitmap data from CD...");
316         gr_palette_load(gr_palette);    //I don't think this line is really needed
317
318         chdir(":Data");
319         //First, delete all PIG files currently in the directory
320         if( !FileFindFirst( "*.pig", &find ) )
321         {
322                 do
323                 {
324                         remove(find.name);
325                 } while( !FileFindNext( &find ) );
326                 
327                 FileFindClose();
328         }
329         chdir(oldDirCStr);
330
331         //Now, copy over new pig
332         songs_stop_redbook();           //so we can read off the cd
333
334         // make the source path "<cd volume>:Data:filename.pig"
335 //MWA   ConvertCToPStr(filename, pigfileNamePStr);
336
337 //MWA   ConcatPStr(sourcePathAndFilePStr, "\pDescent II:Data:");        // volume ID is cd_VRefNum
338 //MWA   ConcatPStr(sourcePathAndFilePStr, pigfileNamePStr);
339
340         strupr(filename);
341         strcpy(sourcePathAndFileCStr, "Descent II:Data:");
342         strcat(sourcePathAndFileCStr, filename);
343         
344         // make the destination path "<default directory>:Data:filename.pig"
345 //MWA   ConcatPStr(destPathAndFilePStr, "\p:Data:");
346 //MWA   ConcatPStr(destPathAndFilePStr, pigfileNamePStr);
347 //MWA   ConvertPToCStr(sourcePathAndFilePStr, sourcePathAndFileCStr);
348 //MWA   ConvertPToCStr(destPathAndFilePStr, destPathAndFileCStr);
349
350         strcpy(destPathAndFileCStr, ":Data:");
351         strcat(destPathAndFileCStr, filename);
352
353         strcpy(sourcePathAndFilePStr, sourcePathAndFileCStr);
354         strcpy(destPathAndFilePStr, destPathAndFileCStr);
355         c2pstr(sourcePathAndFilePStr);
356         c2pstr(destPathAndFilePStr);
357         
358         do {
359                 // Open the source file
360                 sourceFile = fopen(sourcePathAndFileCStr,"rb");
361
362                 if (!sourceFile) {
363
364                         if (request_cd() == -1)
365                                 Error("Cannot load file <%s> from CD",filename);
366                 }
367
368         } while (!sourceFile);
369
370
371         // Get the time stamp from the source file
372         theSourcePigHFSParams.fileParam.ioCompletion    = nil;
373         theSourcePigHFSParams.fileParam.ioNamePtr               = sourcePathAndFilePStr;
374         theSourcePigHFSParams.fileParam.ioVRefNum               = cd_VRefNum;
375         theSourcePigHFSParams.fileParam.ioFDirIndex     = 0;
376         theSourcePigHFSParams.fileParam.ioDirID         = 0;
377         
378         theErr = PBHGetFInfo(&theSourcePigHFSParams, false);
379         if (theErr != noErr)
380         {
381                 // Error getting file time stamp!! Why? JTS
382                 Error("Can't get old file time stamp: <%s>\n", sourcePathAndFileCStr);
383         }
384         
385         // Copy the file over
386         // C Stuff......
387         
388         // Open the destination file
389         destFile = fopen(destPathAndFileCStr,"wb");
390         if (!destFile)
391         {
392                 Error("Cannot create file: <%s>\n", destPathAndFileCStr);
393         }
394         
395         // Copy bytes until the end of the source file
396         while (!feof(sourceFile))
397         {
398                 int bytes_read;
399                 int x;
400
401                 bytes_read = fread(buf,1,BUF_SIZE,sourceFile);
402                 if (ferror(sourceFile))
403                         Error("Cannot read from file <%s>: %s", sourcePathAndFileCStr, strerror(errno));
404
405 // Assert is bogus              Assert(bytes_read == BUF_SIZE || feof(sourceFile));
406
407                 fwrite(buf,1,bytes_read,destFile);
408                 if (ferror(destFile))
409                         Error("Cannot write to file <%s>: %s",destPathAndFileCStr, strerror(errno));
410         }
411
412         // close the source/dest files
413         if (fclose(sourceFile))
414                 Error("Error closing file <%s>: %s", sourcePathAndFileCStr, strerror(errno));
415         if (fclose(destFile))
416                 Error("Error closing file <%s>: %s", destPathAndFileCStr, strerror(errno));
417
418         // Get the current hfs data for the new file
419         theDestPigHFSParams.fileParam.ioCompletion      = nil;
420         theDestPigHFSParams.fileParam.ioNamePtr         = destPathAndFilePStr;
421         theDestPigHFSParams.fileParam.ioVRefNum         = 0;
422         theDestPigHFSParams.fileParam.ioFDirIndex       = 0;
423         theDestPigHFSParams.fileParam.ioDirID           = 0;
424         theErr = PBHGetFInfo(&theDestPigHFSParams, false);
425         if ((theErr != noErr) || (theDestPigHFSParams.fileParam.ioResult != noErr))
426         {
427                 // Error getting file time stamp!! Why? JTS
428                 Error("Can't get destination pig file information: <%s>\n", destPathAndFileCStr);
429         }
430
431         // Reset this data !!!!! or else the relative pathname won't work, could use just filename instead but, oh well.
432         theDestPigHFSParams.fileParam.ioNamePtr         = destPathAndFilePStr;
433         theDestPigHFSParams.fileParam.ioVRefNum         = 0;
434         theDestPigHFSParams.fileParam.ioFDirIndex       = 0;
435         theDestPigHFSParams.fileParam.ioDirID           = 0;
436
437         // Copy the time stamp from the source file info
438         theDestPigHFSParams.fileParam.ioFlCrDat = theSourcePigHFSParams.fileParam.ioFlCrDat;
439         theDestPigHFSParams.fileParam.ioFlMdDat = theSourcePigHFSParams.fileParam.ioFlMdDat;
440         theDestPigHFSParams.fileParam.ioFlFndrInfo.fdType = 'PGGY';
441         theDestPigHFSParams.fileParam.ioFlFndrInfo.fdCreator = 'DCT2';
442         
443         // Set the dest file's time stamp to the source file's time stamp values
444         theErr = PBHSetFInfo(&theDestPigHFSParams, false);
445
446         if ((theErr != noErr) || (theDestPigHFSParams.fileParam.ioResult != noErr))
447         {
448                 Error("Can't set destination pig file time stamp: <%s>\n", destPathAndFileCStr);
449         }
450
451         theErr = PBHGetFInfo(&theDestPigHFSParams, false);
452
453         return cfopen(destPathAndFileCStr, "rb");
454 }
455
456 #else   //PC Version of copy_pigfile_from_cd is below
457
458 //copies a pigfile from the CD to the current dir
459 //retuns file handle of new pig
460 CFILE *copy_pigfile_from_cd(char *filename)
461 {
462         char name[80];
463         FILEFINDSTRUCT find;
464         int ret;
465
466         return cfopen(filename, "rb");
467         show_boxed_message("Copying bitmap data from CD...");
468         gr_palette_load(gr_palette);    //I don't think this line is really needed
469
470         //First, delete all PIG files currently in the directory
471
472         if( !FileFindFirst( "*.pig", &find ) ) {
473                 do      {
474                         remove(find.name);
475                 } while( !FileFindNext( &find ) );
476                 FileFindClose();
477         }
478
479         //Now, copy over new pig
480
481         songs_stop_redbook();           //so we can read off the cd
482
483         //new code to unarj file
484         strcpy(name,CDROM_dir);
485         strcat(name,"descent2.sow");
486
487         do {
488 //              ret = unarj_specific_file(name,filename,filename);
489 // DPH:FIXME
490
491                 ret = !EXIT_SUCCESS;
492
493                 if (ret != EXIT_SUCCESS) {
494
495                         //delete file, so we don't leave partial file
496                         remove(filename);
497
498                         #ifndef MACINTOSH
499                         if (request_cd() == -1)
500                         #endif
501                                 //NOTE LINK TO ABOVE IF
502                                 Error("Cannot load file <%s> from CD",filename);
503                 }
504
505         } while (ret != EXIT_SUCCESS);
506
507         return cfopen(filename, "rb");
508 }
509
510 #endif // end of ifdef MAC around copy_pigfile_from_cd
511
512 //initialize a pigfile, reading headers
513 //returns the size of all the bitmap data
514 void piggy_init_pigfile(char *filename)
515 {
516         int i;
517         char temp_name[16];
518         char temp_name_read[16];
519         grs_bitmap temp_bitmap;
520         DiskBitmapHeader bmh;
521         int header_size, N_bitmaps, data_size, data_start;
522         #ifdef MACINTOSH
523         char name[255];         // filename + path for the mac
524         #endif
525
526         piggy_close_file();             //close old pig if still open
527
528         #ifdef SHAREWARE                //rename pigfile for shareware
529         if (stricmp(filename,DEFAULT_PIGFILE_REGISTERED)==0)
530                 filename = DEFAULT_PIGFILE_SHAREWARE;
531         #endif
532
533         #ifndef MACINTOSH
534                 Piggy_fp = cfopen( filename, "rb" );
535         #else
536                 sprintf(name, ":Data:%s", filename);
537                 Piggy_fp = cfopen( name, "rb" );
538         
539                 #ifdef SHAREWARE        // if we are in the shareware version, we must have the pig by now.
540                         if (Piggy_fp == NULL)
541                         {
542                                 Error("Cannot load required file <%s>",name);
543                         }
544                 #endif  // end of if def shareware
545         
546         #endif
547
548         if (!Piggy_fp) {
549                 #ifdef EDITOR
550                         return;         //if editor, ok to not have pig, because we'll build one
551                 #else
552                         Piggy_fp = copy_pigfile_from_cd(filename);
553                 #endif
554         }
555
556         if (Piggy_fp) {                         //make sure pig is valid type file & is up-to-date
557                 int pig_id,pig_version;
558
559                 pig_id = cfile_read_int(Piggy_fp);
560                 pig_version = cfile_read_int(Piggy_fp);
561                 if (pig_id != PIGFILE_ID || pig_version != PIGFILE_VERSION) {
562                         cfclose(Piggy_fp);              //out of date pig
563                         Piggy_fp = NULL;                        //..so pretend it's not here
564                 }
565         }
566
567         if (!Piggy_fp) {
568
569                 #ifdef EDITOR
570                         return;         //if editor, ok to not have pig, because we'll build one
571                 #else
572                         Error("Cannot load required file <%s>",filename);
573                 #endif
574         }
575
576         strncpy(Current_pigfile,filename,sizeof(Current_pigfile));
577
578         N_bitmaps = cfile_read_int(Piggy_fp);
579
580         header_size = N_bitmaps*DISKBITMAPHEADER_SIZE;
581
582         data_start = header_size + cftell(Piggy_fp);
583
584         data_size = cfilelength(Piggy_fp) - data_start;
585
586         Num_bitmap_files = 1;
587
588         for (i=0; i<N_bitmaps; i++ )    {
589                 DiskBitmapHeader_read(&bmh, Piggy_fp);
590                 memcpy( temp_name_read, bmh.name, 8 );
591                 temp_name_read[8] = 0;
592                 if ( bmh.dflags & DBM_FLAG_ABM )        
593                         sprintf( temp_name, "%s#%d", temp_name_read, bmh.dflags & 63 );
594                 else
595                         strcpy( temp_name, temp_name_read );
596                 memset( &temp_bitmap, 0, sizeof(grs_bitmap) );
597                 temp_bitmap.bm_w = temp_bitmap.bm_rowsize = bmh.width + ((short) (bmh.wh_extra&0x0f)<<8);
598                 temp_bitmap.bm_h = bmh.height + ((short) (bmh.wh_extra&0xf0)<<4);
599                 temp_bitmap.bm_flags = BM_FLAG_PAGED_OUT;
600                 temp_bitmap.avg_color = bmh.avg_color;
601                 temp_bitmap.bm_data = Piggy_bitmap_cache_data;
602
603                 GameBitmapFlags[i+1] = 0;
604                 if ( bmh.flags & BM_FLAG_TRANSPARENT ) GameBitmapFlags[i+1] |= BM_FLAG_TRANSPARENT;
605                 if ( bmh.flags & BM_FLAG_SUPER_TRANSPARENT ) GameBitmapFlags[i+1] |= BM_FLAG_SUPER_TRANSPARENT;
606                 if ( bmh.flags & BM_FLAG_NO_LIGHTING ) GameBitmapFlags[i+1] |= BM_FLAG_NO_LIGHTING;
607                 if ( bmh.flags & BM_FLAG_RLE ) GameBitmapFlags[i+1] |= BM_FLAG_RLE;
608                 if ( bmh.flags & BM_FLAG_RLE_BIG ) GameBitmapFlags[i+1] |= BM_FLAG_RLE_BIG;
609
610                 GameBitmapOffset[i+1] = bmh.offset + data_start;
611                 Assert( (i+1) == Num_bitmap_files );
612                 piggy_register_bitmap( &temp_bitmap, temp_name, 1 );
613         }
614
615 #ifdef EDITOR
616         Piggy_bitmap_cache_size = data_size + (data_size/10);   //extra mem for new bitmaps
617         Assert( Piggy_bitmap_cache_size > 0 );
618 #else
619         Piggy_bitmap_cache_size = PIGGY_BUFFER_SIZE;
620         #ifdef MACINTOSH
621         if (piggy_low_memory)
622                 Piggy_bitmap_cache_size = PIGGY_SMALL_BUFFER_SIZE;
623         #endif
624 #endif
625         BitmapBits = d_malloc( Piggy_bitmap_cache_size );
626         if ( BitmapBits == NULL )
627                 Error( "Not enough memory to load bitmaps\n" );
628         Piggy_bitmap_cache_data = BitmapBits;   
629         Piggy_bitmap_cache_next = 0;
630         
631         #if defined(MACINTOSH) && defined(SHAREWARE)
632 //      load_exit_models();
633         #endif
634
635         Pigfile_initialized=1;  
636 }
637
638 #define FILENAME_LEN 13
639 #define MAX_BITMAPS_PER_BRUSH 30
640
641 extern int compute_average_pixel(grs_bitmap *new);
642
643 //reads in a new pigfile (for new palette)
644 //returns the size of all the bitmap data
645 void piggy_new_pigfile(char *pigname)
646 {
647         int i;
648         char temp_name[16];
649         char temp_name_read[16];
650         grs_bitmap temp_bitmap;
651         DiskBitmapHeader bmh;
652         int header_size, N_bitmaps, data_size, data_start;
653         int must_rewrite_pig = 0;
654         #ifdef MACINTOSH
655         char name[255];
656         #endif
657
658         strlwr(pigname);
659
660         #ifdef SHAREWARE                //rename pigfile for shareware
661         if (stricmp(pigname,DEFAULT_PIGFILE_REGISTERED)==0)
662                 pigname = DEFAULT_PIGFILE_SHAREWARE;
663         #endif
664
665         if (strnicmp(Current_pigfile,pigname,sizeof(Current_pigfile))==0)
666                 return;         //already have correct pig
667
668         if (!Pigfile_initialized) {                     //have we ever opened a pigfile?
669                 piggy_init_pigfile(pigname);            //..no, so do initialization stuff
670                 return;
671         }
672         else
673                 piggy_close_file();             //close old pig if still open
674
675         Piggy_bitmap_cache_next = 0;            //free up cache
676
677         strncpy(Current_pigfile,pigname,sizeof(Current_pigfile));
678
679         #ifndef MACINTOSH
680                 Piggy_fp = cfopen( pigname, "rb" );
681         #else
682                 sprintf(name, ":Data:%s", pigname);
683                 Piggy_fp = cfopen( name, "rb" );
684
685                 #ifdef SHAREWARE        // if we are in the shareware version, we must have the pig by now.
686                         if (Piggy_fp == NULL)
687                         {
688                                 Error("Cannot load required file <%s>",name);
689                         }
690                 #endif  // end of if def shareware
691         #endif
692
693         #ifndef EDITOR
694         if (!Piggy_fp)
695                 Piggy_fp = copy_pigfile_from_cd(pigname);
696         #endif
697
698         if (Piggy_fp) {                         //make sure pig is valid type file & is up-to-date
699                 int pig_id,pig_version;
700
701                 pig_id = cfile_read_int(Piggy_fp);
702                 pig_version = cfile_read_int(Piggy_fp);
703                 if (pig_id != PIGFILE_ID || pig_version != PIGFILE_VERSION) {
704                         cfclose(Piggy_fp);              //out of date pig
705                         Piggy_fp = NULL;                        //..so pretend it's not here
706                 }
707         }
708
709         #ifndef EDITOR
710         if (!Piggy_fp) Error ("Piggy_fp not defined in piggy_new_pigfile.");
711         #endif
712
713         if (Piggy_fp) {
714
715                 N_bitmaps = cfile_read_int(Piggy_fp);
716         
717                 header_size = N_bitmaps*DISKBITMAPHEADER_SIZE;
718         
719                 data_start = header_size + cftell(Piggy_fp);
720
721                 data_size = cfilelength(Piggy_fp) - data_start;
722         
723                 for (i=1; i<=N_bitmaps; i++ )   {
724                         DiskBitmapHeader_read(&bmh, Piggy_fp);
725                         memcpy( temp_name_read, bmh.name, 8 );
726                         temp_name_read[8] = 0;
727         
728                         if ( bmh.dflags & DBM_FLAG_ABM )        
729                                 sprintf( temp_name, "%s#%d", temp_name_read, bmh.dflags & 63 );
730                         else
731                                 strcpy( temp_name, temp_name_read );
732         
733                         //Make sure name matches
734                         if (strcmp(temp_name,AllBitmaps[i].name)) {
735                                 //Int3();       //this pig is out of date.  Delete it
736                                 must_rewrite_pig=1;
737                         }
738         
739                         strcpy(AllBitmaps[i].name,temp_name);
740
741                         memset( &temp_bitmap, 0, sizeof(grs_bitmap) );
742         
743                         temp_bitmap.bm_w = temp_bitmap.bm_rowsize = bmh.width + ((short) (bmh.wh_extra&0x0f)<<8);
744                         temp_bitmap.bm_h = bmh.height + ((short) (bmh.wh_extra&0xf0)<<4);
745                         temp_bitmap.bm_flags = BM_FLAG_PAGED_OUT;
746                         temp_bitmap.avg_color = bmh.avg_color;
747                         temp_bitmap.bm_data = Piggy_bitmap_cache_data;
748         
749                         GameBitmapFlags[i] = 0;
750         
751                         if ( bmh.flags & BM_FLAG_TRANSPARENT ) GameBitmapFlags[i] |= BM_FLAG_TRANSPARENT;
752                         if ( bmh.flags & BM_FLAG_SUPER_TRANSPARENT ) GameBitmapFlags[i] |= BM_FLAG_SUPER_TRANSPARENT;
753                         if ( bmh.flags & BM_FLAG_NO_LIGHTING ) GameBitmapFlags[i] |= BM_FLAG_NO_LIGHTING;
754                         if ( bmh.flags & BM_FLAG_RLE ) GameBitmapFlags[i] |= BM_FLAG_RLE;
755                         if ( bmh.flags & BM_FLAG_RLE_BIG ) GameBitmapFlags[i] |= BM_FLAG_RLE_BIG;
756         
757                         GameBitmapOffset[i] = bmh.offset + data_start;
758         
759                         GameBitmaps[i] = temp_bitmap;
760                 }
761         }
762         else
763                 N_bitmaps = 0;          //no pigfile, so no bitmaps
764
765         #ifndef EDITOR
766
767         Assert(N_bitmaps == Num_bitmap_files-1);
768
769         #else
770
771         if (must_rewrite_pig || (N_bitmaps < Num_bitmap_files-1)) {
772                 int size;
773
774                 //re-read the bitmaps that aren't in this pig
775
776                 for (i=N_bitmaps+1;i<Num_bitmap_files;i++) {
777                         char *p;
778
779                         p = strchr(AllBitmaps[i].name,'#');
780
781                         if (p) {                //this is an ABM
782                                 char abmname[FILENAME_LEN];
783                                 int fnum;
784                                 grs_bitmap * bm[MAX_BITMAPS_PER_BRUSH];
785                                 int iff_error;          //reference parm to avoid warning message
786                                 ubyte newpal[768];
787                                 char basename[FILENAME_LEN];
788                                 int nframes;
789                         
790                                 strcpy(basename,AllBitmaps[i].name);
791                                 basename[p-AllBitmaps[i].name] = 0;             //cut off "#nn" part
792                                 
793                                 sprintf( abmname, "%s.abm", basename );
794
795                       iff_error = iff_read_animbrush(abmname,bm,MAX_BITMAPS_PER_BRUSH,&nframes,newpal);
796
797                                 if (iff_error != IFF_NO_ERROR)  {
798                                         mprintf((1,"File %s - IFF error: %s",abmname,iff_errormsg(iff_error)));
799                                         Error("File %s - IFF error: %s",abmname,iff_errormsg(iff_error));
800                                 }
801                         
802                                 for (fnum=0;fnum<nframes; fnum++)       {
803                                         char tempname[20];
804                                         int SuperX;
805
806                                         sprintf( tempname, "%s#%d", basename, fnum );
807
808                                         //SuperX = (GameBitmaps[i+fnum].bm_flags&BM_FLAG_SUPER_TRANSPARENT)?254:-1;
809                                         SuperX = (GameBitmapFlags[i+fnum]&BM_FLAG_SUPER_TRANSPARENT)?254:-1;
810                                         //above makes assumption that supertransparent color is 254
811
812                                         if ( iff_has_transparency )
813                                                 gr_remap_bitmap_good( bm[fnum], newpal, iff_transparent_color, SuperX );
814                                         else
815                                                 gr_remap_bitmap_good( bm[fnum], newpal, -1, SuperX );
816                         
817                                         bm[fnum]->avg_color = compute_average_pixel(bm[fnum]);
818
819 #ifdef EDITOR
820                                         if ( FindArg("-macdata") )
821                                                 swap_0_255( bm[fnum] );
822 #endif
823                                         if ( !BigPig ) gr_bitmap_rle_compress( bm[fnum] );
824
825                                         if (bm[fnum]->bm_flags & BM_FLAG_RLE)
826                                                 size = *((int *) bm[fnum]->bm_data);
827                                         else
828                                                 size = bm[fnum]->bm_w * bm[fnum]->bm_h;
829
830                                         memcpy( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next],bm[fnum]->bm_data,size);
831                                         d_free(bm[fnum]->bm_data);
832                                         bm[fnum]->bm_data = &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next];
833                                         Piggy_bitmap_cache_next += size;
834
835                                         GameBitmaps[i+fnum] = *bm[fnum];
836
837                                         // -- mprintf( (0, "U" ));
838                                         d_free( bm[fnum] );
839                                 }
840
841                                 i += nframes-1;         //filled in multiple bitmaps
842                         }
843                         else {          //this is a BBM
844
845                                 grs_bitmap * new;
846                                 ubyte newpal[256*3];
847                                 int iff_error;
848                                 char bbmname[FILENAME_LEN];
849                                 int SuperX;
850
851                                 MALLOC( new, grs_bitmap, 1 );
852
853                                 sprintf( bbmname, "%s.bbm", AllBitmaps[i].name );
854                                 iff_error = iff_read_bitmap(bbmname,new,BM_LINEAR,newpal);
855
856                                 new->bm_handle=0;
857                                 if (iff_error != IFF_NO_ERROR)          {
858                                         mprintf((1, "File %s - IFF error: %s",bbmname,iff_errormsg(iff_error)));
859                                         Error("File %s - IFF error: %s",bbmname,iff_errormsg(iff_error));
860                                 }
861                         
862                                 SuperX = (GameBitmapFlags[i]&BM_FLAG_SUPER_TRANSPARENT)?254:-1;
863                                 //above makes assumption that supertransparent color is 254
864
865                                 if ( iff_has_transparency )
866                                         gr_remap_bitmap_good( new, newpal, iff_transparent_color, SuperX );
867                                 else
868                                         gr_remap_bitmap_good( new, newpal, -1, SuperX );
869                         
870                                 new->avg_color = compute_average_pixel(new);
871                         
872 #ifdef EDITOR
873                                         if ( FindArg("-macdata") )
874                                                 swap_0_255( new );
875 #endif
876                                 if ( !BigPig )  gr_bitmap_rle_compress( new );
877
878                                 if (new->bm_flags & BM_FLAG_RLE)
879                                         size = *((int *) new->bm_data);
880                                 else
881                                         size = new->bm_w * new->bm_h;
882
883                                 memcpy( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next],new->bm_data,size);
884                                 d_free(new->bm_data);
885                                 new->bm_data = &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next];
886                                 Piggy_bitmap_cache_next += size;
887
888                                 GameBitmaps[i] = *new;
889         
890                                 d_free( new );
891
892                                 // -- mprintf( (0, "U" ));
893                         }
894                 }
895
896                 //@@Dont' do these things which are done when writing
897                 //@@for (i=0; i < Num_bitmap_files; i++ )       {
898                 //@@    bitmap_index bi;
899                 //@@    bi.index = i;
900                 //@@    PIGGY_PAGE_IN( bi );
901                 //@@}
902                 //@@
903                 //@@piggy_close_file();
904
905                 piggy_write_pigfile(pigname);
906
907                 Current_pigfile[0] = 0;                 //say no pig, to force reload
908
909                 piggy_new_pigfile(pigname);             //read in just-generated pig
910
911
912         }
913         #endif  //ifdef EDITOR
914
915 }
916
917 ubyte bogus_data[64*64];
918 grs_bitmap bogus_bitmap;
919 ubyte bogus_bitmap_initialized=0;
920 digi_sound bogus_sound;
921
922 #define HAMFILE_ID              MAKE_SIG('!','M','A','H') //HAM!
923 #ifdef SHAREWARE
924 #define HAMFILE_VERSION 2
925 #else
926 #define HAMFILE_VERSION 3
927 #endif
928 //version 1 -> 2:  save marker_model_num
929 //version 2 -> 3:  removed sound files
930
931 #define SNDFILE_ID              MAKE_SIG('D','N','S','D') //DSND
932 #define SNDFILE_VERSION 1
933
934 int read_hamfile()
935 {
936         CFILE * ham_fp = NULL;
937         int ham_id,ham_version;
938         int sound_offset = 0;
939         #ifdef MACINTOSH
940         char name[255];
941         #endif
942
943         #ifndef MACINTOSH
944         ham_fp = cfopen( DEFAULT_HAMFILE, "rb" );
945         #else
946         sprintf(name, ":Data:%s", DEFAULT_HAMFILE );
947         ham_fp = cfopen( name, "rb" );
948         #endif
949
950         if (ham_fp == NULL) {
951                 Must_write_hamfile = 1;
952                 return 0;
953         }
954
955         //make sure ham is valid type file & is up-to-date
956         ham_id = cfile_read_int(ham_fp);
957         ham_version = cfile_read_int(ham_fp);
958         if (ham_id != HAMFILE_ID || ham_version != HAMFILE_VERSION) {
959                 Must_write_hamfile = 1;
960                 cfclose(ham_fp);                                                //out of date ham
961                 return 0;
962         }
963
964         if (ham_version < 3) // hamfile contains sound info
965                 sound_offset = cfile_read_int(ham_fp);
966
967         #ifndef EDITOR
968         {
969                 //int i;
970
971                 bm_read_all(ham_fp, ham_version);
972                 cfread( GameBitmapXlat, sizeof(ushort)*MAX_BITMAP_FILES, 1, ham_fp );
973                 // no swap here?
974                 //for (i = 0; i < MAX_BITMAP_FILES; i++) {
975                         //GameBitmapXlat[i] = INTEL_SHORT(GameBitmapXlat[i]);
976                         //printf("GameBitmapXlat[%d] = %d\n", i, GameBitmapXlat[i]);
977                 //}
978         }
979         #endif
980
981         if (ham_version < 3) {
982                 int N_sounds;
983                 int sound_start;
984                 int header_size;
985                 int i;
986                 DiskSoundHeader sndh;
987                 digi_sound temp_sound;
988                 char temp_name_read[16];
989                 int sbytes = 0;
990
991                 cfseek(ham_fp, sound_offset, SEEK_SET);
992                 N_sounds = cfile_read_int(ham_fp);
993
994                 sound_start = cftell(ham_fp);
995
996                 header_size = N_sounds * DISKSOUNDHEADER_SIZE;
997
998                 //Read sounds
999
1000                 for (i=0; i<N_sounds; i++ ) {
1001                         DiskSoundHeader_read(&sndh, ham_fp);
1002                         temp_sound.length = sndh.length;
1003                         temp_sound.data = (ubyte *)(sndh.offset + header_size + sound_start);
1004                         SoundOffset[Num_sound_files] = sndh.offset + header_size + sound_start;
1005                         memcpy( temp_name_read, sndh.name, 8 );
1006                         temp_name_read[8] = 0;
1007                         piggy_register_sound( &temp_sound, temp_name_read, 1 );
1008 #ifdef MACINTOSH
1009                         if (piggy_is_needed(i))
1010 #endif          // note link to if.
1011                                 sbytes += sndh.length;
1012                         //mprintf(( 0, "%d bytes of sound\n", sbytes ));
1013                 }
1014
1015                 SoundBits = d_malloc( sbytes + 16 );
1016                 if ( SoundBits == NULL )
1017                         Error( "Not enough memory to load sounds\n" );
1018
1019                 mprintf(( 0, "\nBitmaps: %d KB   Sounds: %d KB\n", Piggy_bitmap_cache_size/1024, sbytes/1024 ));
1020
1021                 //      piggy_read_sounds(ham_fp);
1022
1023         }
1024
1025         cfclose(ham_fp);
1026
1027         return 1;
1028
1029 }
1030
1031 int read_sndfile()
1032 {
1033         CFILE * snd_fp = NULL;
1034         int snd_id,snd_version;
1035         int N_sounds;
1036         int sound_start;
1037         int header_size;
1038         int i,size, length;
1039         DiskSoundHeader sndh;
1040         digi_sound temp_sound;
1041         char temp_name_read[16];
1042         int sbytes = 0;
1043         #ifdef MACINTOSH
1044         char name[255];
1045         #endif
1046
1047         #ifndef MACINTOSH
1048         snd_fp = cfopen( DEFAULT_SNDFILE, "rb" );
1049         #else
1050         sprintf( name, ":Data:%s", DEFAULT_SNDFILE );
1051         snd_fp = cfopen( name, "rb");
1052         #endif
1053         
1054         if (snd_fp == NULL)
1055                 return 0;
1056
1057         //make sure soundfile is valid type file & is up-to-date
1058         snd_id = cfile_read_int(snd_fp);
1059         snd_version = cfile_read_int(snd_fp);
1060         if (snd_id != SNDFILE_ID || snd_version != SNDFILE_VERSION) {
1061                 cfclose(snd_fp);                                                //out of date sound file
1062                 return 0;
1063         }
1064
1065         N_sounds = cfile_read_int(snd_fp);
1066
1067         sound_start = cftell(snd_fp);
1068         size = cfilelength(snd_fp) - sound_start;
1069         length = size;
1070         mprintf( (0, "\nReading data (%d KB) ", size/1024 ));
1071
1072         header_size = N_sounds*sizeof(DiskSoundHeader);
1073
1074         //Read sounds
1075
1076         for (i=0; i<N_sounds; i++ ) {
1077                 DiskSoundHeader_read(&sndh, snd_fp);
1078                 //size -= sizeof(DiskSoundHeader);
1079                 temp_sound.length = sndh.length;
1080                 temp_sound.data = (ubyte *)(sndh.offset + header_size + sound_start);
1081                 SoundOffset[Num_sound_files] = sndh.offset + header_size + sound_start;
1082                 memcpy( temp_name_read, sndh.name, 8 );
1083                 temp_name_read[8] = 0;
1084                 piggy_register_sound( &temp_sound, temp_name_read, 1 );
1085                 #ifdef MACINTOSH
1086                 if (piggy_is_needed(i))
1087                 #endif          // note link to if.
1088                 sbytes += sndh.length;
1089                 //mprintf(( 0, "%d bytes of sound\n", sbytes ));
1090         }
1091
1092         SoundBits = d_malloc( sbytes + 16 );
1093         if ( SoundBits == NULL )
1094                 Error( "Not enough memory to load sounds\n" );
1095
1096         mprintf(( 0, "\nBitmaps: %d KB   Sounds: %d KB\n", Piggy_bitmap_cache_size/1024, sbytes/1024 ));
1097
1098 //      piggy_read_sounds(snd_fp);
1099
1100         cfclose(snd_fp);
1101
1102         return 1;
1103 }
1104
1105 int piggy_init(void)
1106 {
1107         int ham_ok=0,snd_ok=0;
1108         int i;
1109
1110         hashtable_init( &AllBitmapsNames, MAX_BITMAP_FILES );
1111         hashtable_init( &AllDigiSndNames, MAX_SOUND_FILES );
1112
1113         for (i=0; i<MAX_SOUND_FILES; i++ )      {
1114                 GameSounds[i].length = 0;
1115                 GameSounds[i].data = NULL;
1116                 SoundOffset[i] = 0;
1117         }
1118
1119         for (i=0; i<MAX_BITMAP_FILES; i++ )     
1120                 GameBitmapXlat[i] = i;
1121
1122         if ( !bogus_bitmap_initialized )        {
1123                 int i;
1124                 ubyte c;
1125                 bogus_bitmap_initialized = 1;
1126                 memset( &bogus_bitmap, 0, sizeof(grs_bitmap) );
1127                 bogus_bitmap.bm_w = bogus_bitmap.bm_h = bogus_bitmap.bm_rowsize = 64;
1128                 bogus_bitmap.bm_data = bogus_data;
1129                 c = gr_find_closest_color( 0, 0, 63 );
1130                 for (i=0; i<4096; i++ ) bogus_data[i] = c;
1131                 c = gr_find_closest_color( 63, 0, 0 );
1132                 // Make a big red X !
1133                 for (i=0; i<64; i++ )   {
1134                         bogus_data[i*64+i] = c;
1135                         bogus_data[i*64+(63-i)] = c;
1136                 }
1137                 piggy_register_bitmap( &bogus_bitmap, "bogus", 1 );
1138                 bogus_sound.length = 64*64;
1139                 bogus_sound.data = bogus_data;
1140                 GameBitmapOffset[0] = 0;
1141         }
1142
1143         if ( FindArg( "-bigpig" ))
1144                 BigPig = 1;
1145
1146         if ( FindArg( "-lowmem" ))
1147                 piggy_low_memory = 1;
1148
1149         if ( FindArg( "-nolowmem" ))
1150                 piggy_low_memory = 0;
1151
1152         if (piggy_low_memory)
1153                 digi_lomem = 1;
1154
1155         WIN(DDGRLOCK(dd_grd_curcanv));
1156                 gr_set_curfont( SMALL_FONT );
1157                 gr_set_fontcolor(gr_find_closest_color_current( 20, 20, 20 ),-1 );
1158                 gr_printf( 0x8000, grd_curcanv->cv_h-20, "%s...", TXT_LOADING_DATA );
1159         WIN(DDGRUNLOCK(dd_grd_curcanv));
1160
1161         #ifdef EDITOR
1162         piggy_init_pigfile(DEFAULT_PIGFILE);
1163         #endif
1164
1165 #ifndef SHAREWARE
1166         ham_ok = read_hamfile();
1167         snd_ok = read_sndfile();
1168 #else
1169         snd_ok = ham_ok = read_hamfile();
1170 #endif
1171
1172         atexit(piggy_close);
1173
1174         mprintf ((0,"HamOk=%d SndOk=%d\n",ham_ok,snd_ok));
1175         return (ham_ok && snd_ok);               //read ok
1176 }
1177
1178 int piggy_is_needed(int soundnum)
1179 {
1180         int i;
1181
1182         if ( !digi_lomem ) return 1;
1183
1184         for (i=0; i<MAX_SOUNDS; i++ )   {
1185                 if ( (AltSounds[i] < 255) && (Sounds[AltSounds[i]] == soundnum) )
1186                         return 1;
1187         }
1188         return 0;
1189 }
1190
1191
1192 void piggy_read_sounds(void)
1193 {
1194         CFILE * fp = NULL;
1195         ubyte * ptr;
1196         int i, sbytes;
1197         #ifdef MACINTOSH
1198         char name[255];
1199         #endif
1200
1201         ptr = SoundBits;
1202         sbytes = 0;
1203
1204         #ifndef MACINTOSH
1205         fp = cfopen( DEFAULT_SNDFILE, "rb" );
1206         #else
1207         sprintf( name, ":Data:%s", DEFAULT_SNDFILE );
1208         fp = cfopen( name, "rb");
1209         #endif
1210
1211         if (fp == NULL)
1212                 return;
1213
1214         for (i=0; i<Num_sound_files; i++ )      {
1215                 digi_sound *snd = &GameSounds[i];
1216
1217                 if ( SoundOffset[i] > 0 )       {
1218                         if ( piggy_is_needed(i) )       {
1219                                 cfseek( fp, SoundOffset[i], SEEK_SET );
1220         
1221                                 // Read in the sound data!!!
1222                                 snd->data = ptr;
1223                                 ptr += snd->length;
1224                                 sbytes += snd->length;
1225                                 cfread( snd->data, snd->length, 1, fp );
1226                         }
1227                         else
1228                                 snd->data = (ubyte *) -1;
1229                 }
1230         }
1231
1232         cfclose(fp);
1233
1234         mprintf(( 0, "\nActual Sound usage: %d KB\n", sbytes/1024 ));
1235
1236 }
1237
1238
1239 extern int descent_critical_error;
1240 extern unsigned descent_critical_deverror;
1241 extern unsigned descent_critical_errcode;
1242
1243 char * crit_errors[13] = { "Write Protected", "Unknown Unit", "Drive Not Ready", "Unknown Command", "CRC Error", \
1244 "Bad struct length", "Seek Error", "Unknown media type", "Sector not found", "Printer out of paper", "Write Fault", \
1245 "Read fault", "General Failure" };
1246
1247 void piggy_critical_error()
1248 {
1249         grs_canvas * save_canv;
1250         grs_font * save_font;
1251         int i;
1252         save_canv = grd_curcanv;
1253         save_font = grd_curcanv->cv_font;
1254         gr_palette_load( gr_palette );
1255         i = nm_messagebox( "Disk Error", 2, "Retry", "Exit", "%s\non drive %c:", crit_errors[descent_critical_errcode&0xf], (descent_critical_deverror&0xf)+'A'  );
1256         if ( i == 1 )
1257                 exit(1);
1258         gr_set_current_canvas(save_canv);
1259         grd_curcanv->cv_font = save_font;
1260 }
1261
1262 void piggy_bitmap_page_in( bitmap_index bitmap )
1263 {
1264         grs_bitmap * bmp;
1265         int i,org_i,temp;
1266
1267         org_i = 0;
1268                         
1269         i = bitmap.index;
1270         Assert( i >= 0 );
1271         Assert( i < MAX_BITMAP_FILES );
1272         Assert( i < Num_bitmap_files );
1273         Assert( Piggy_bitmap_cache_size > 0 );
1274
1275         if ( i < 1 ) return;
1276         if ( i >= MAX_BITMAP_FILES ) return;
1277         if ( i >= Num_bitmap_files ) return;
1278         
1279         if ( GameBitmapOffset[i] == 0 ) return;         // A read-from-disk bitmap!!!
1280
1281         if ( piggy_low_memory ) {
1282                 org_i = i;
1283                 i = GameBitmapXlat[i];          // Xlat for low-memory settings!
1284         }
1285         
1286         bmp = &GameBitmaps[i];
1287
1288         if ( bmp->bm_flags & BM_FLAG_PAGED_OUT )        {
1289                 stop_time();
1290
1291         ReDoIt:
1292                 descent_critical_error = 0;
1293                 cfseek( Piggy_fp, GameBitmapOffset[i], SEEK_SET );
1294                 if ( descent_critical_error )   {
1295                         piggy_critical_error();
1296                         goto ReDoIt;
1297                 }
1298                 
1299                 bmp->bm_data = &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next];
1300                 bmp->bm_flags = GameBitmapFlags[i];
1301         
1302                 if ( bmp->bm_flags & BM_FLAG_RLE )      {
1303                         int zsize = 0;
1304                         descent_critical_error = 0;
1305                         zsize = cfile_read_int(Piggy_fp);
1306                         if ( descent_critical_error )   {
1307                                 piggy_critical_error();
1308                                 goto ReDoIt;
1309                         }
1310         
1311                         // GET JOHN NOW IF YOU GET THIS ASSERT!!!
1312                         //Assert( Piggy_bitmap_cache_next+zsize < Piggy_bitmap_cache_size );      
1313                         if ( Piggy_bitmap_cache_next+zsize >= Piggy_bitmap_cache_size ) {
1314                                 Int3();
1315                                 piggy_bitmap_page_out_all();
1316                                 goto ReDoIt;
1317                         }
1318                         memcpy( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], &zsize, sizeof(int) );
1319                         Piggy_bitmap_cache_next += sizeof(int);
1320                         descent_critical_error = 0;
1321                         temp = cfread( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], 1, zsize-4, Piggy_fp );
1322                         if ( descent_critical_error )   {
1323                                 piggy_critical_error();
1324                                 goto ReDoIt;
1325                         }
1326                         Piggy_bitmap_cache_next += zsize-4;
1327                 } else {
1328                         // GET JOHN NOW IF YOU GET THIS ASSERT!!!
1329                         Assert( Piggy_bitmap_cache_next+(bmp->bm_h*bmp->bm_w) < Piggy_bitmap_cache_size );      
1330                         if ( Piggy_bitmap_cache_next+(bmp->bm_h*bmp->bm_w) >= Piggy_bitmap_cache_size ) {
1331                                 piggy_bitmap_page_out_all();
1332                                 goto ReDoIt;
1333                         }
1334                         descent_critical_error = 0;
1335                         temp = cfread( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], 1, bmp->bm_h*bmp->bm_w, Piggy_fp );
1336                         if ( descent_critical_error )   {
1337                                 piggy_critical_error();
1338                                 goto ReDoIt;
1339                         }
1340                         Piggy_bitmap_cache_next+=bmp->bm_h*bmp->bm_w;
1341                 }
1342         
1343                 //@@if ( bmp->bm_selector ) {
1344                 //@@#if !defined(WINDOWS) && !defined(MACINTOSH)
1345                 //@@    if (!dpmi_modify_selector_base( bmp->bm_selector, bmp->bm_data ))
1346                 //@@            Error( "Error modifying selector base in piggy.c\n" );
1347                 //@@#endif
1348                 //@@}
1349
1350                 start_time();
1351         }
1352
1353         if ( piggy_low_memory ) {
1354                 if ( org_i != i )
1355                         GameBitmaps[org_i] = GameBitmaps[i];
1356         }
1357
1358 //@@Removed from John's code:
1359 //@@#ifndef WINDOWS
1360 //@@    if ( bmp->bm_selector ) {
1361 //@@            if (!dpmi_modify_selector_base( bmp->bm_selector, bmp->bm_data ))
1362 //@@                    Error( "Error modifying selector base in piggy.c\n" );
1363 //@@    }
1364 //@@#endif
1365
1366 }
1367
1368 void piggy_bitmap_page_out_all()
1369 {
1370         int i;
1371         
1372         Piggy_bitmap_cache_next = 0;
1373
1374         piggy_page_flushed++;
1375
1376         texmerge_flush();
1377         rle_cache_flush();
1378
1379         for (i=0; i<Num_bitmap_files; i++ )             {
1380                 if ( GameBitmapOffset[i] > 0 )  {       // Don't page out bitmaps read from disk!!!
1381                         GameBitmaps[i].bm_flags = BM_FLAG_PAGED_OUT;
1382                         GameBitmaps[i].bm_data = Piggy_bitmap_cache_data;
1383                 }
1384         }
1385
1386         mprintf(( 0, "Flushing piggy bitmap cache\n" ));
1387 }
1388
1389 void piggy_load_level_data()
1390 {
1391         piggy_bitmap_page_out_all();
1392         paging_touch_all();
1393 }
1394
1395 #ifdef EDITOR
1396
1397 void change_filename_ext( char *dest, char *src, char *ext );
1398
1399 void piggy_write_pigfile(char *filename)
1400 {
1401         FILE *pig_fp;
1402         int bitmap_data_start,data_offset;
1403         DiskBitmapHeader bmh;
1404         int org_offset;
1405         char subst_name[32];
1406         int i;
1407         FILE *fp1,*fp2;
1408         char tname[FILENAME_LEN];
1409
1410         // -- mprintf( (0, "Paging in all piggy bitmaps..." ));
1411         for (i=0; i < Num_bitmap_files; i++ )   {
1412                 bitmap_index bi;
1413                 bi.index = i;
1414                 PIGGY_PAGE_IN( bi );
1415         }
1416         // -- mprintf( (0, "\n" ));
1417
1418         piggy_close_file();
1419
1420         // -- mprintf( (0, "Creating %s...",filename ));
1421
1422         pig_fp = fopen( filename, "wb" );       //open PIG file
1423         Assert( pig_fp!=NULL );
1424
1425         write_int(PIGFILE_ID,pig_fp);
1426         write_int(PIGFILE_VERSION,pig_fp);
1427
1428         Num_bitmap_files--;
1429         fwrite( &Num_bitmap_files, sizeof(int), 1, pig_fp );
1430         Num_bitmap_files++;
1431
1432         bitmap_data_start = ftell(pig_fp);
1433         bitmap_data_start += (Num_bitmap_files-1)*DISKBITMAPHEADER_SIZE; 
1434         data_offset = bitmap_data_start;
1435
1436         change_filename_ext(tname,filename,"lst");
1437         fp1 = fopen( tname, "wt" );
1438         change_filename_ext(tname,filename,"all");
1439         fp2 = fopen( tname, "wt" );
1440
1441         for (i=1; i < Num_bitmap_files; i++ )   {
1442                 int *size;
1443                 grs_bitmap *bmp;
1444
1445                 {               
1446                         char * p, *p1;
1447                         p = strchr(AllBitmaps[i].name,'#');
1448                         if (p)  {
1449                                 int n;
1450                                 p1 = p; p1++; 
1451                                 n = atoi(p1);
1452                                 *p = 0;
1453                                 if (fp2 && n==0)
1454                                         fprintf( fp2, "%s.abm\n", AllBitmaps[i].name );
1455                                 memcpy( bmh.name, AllBitmaps[i].name, 8 );
1456                                 Assert( n <= 63 );
1457                                 bmh.dflags = DBM_FLAG_ABM + n;
1458                                 *p = '#';
1459                         }else {
1460                                 if (fp2)
1461                                         fprintf( fp2, "%s.bbm\n", AllBitmaps[i].name );
1462                                 memcpy( bmh.name, AllBitmaps[i].name, 8 );
1463                                 bmh.dflags = 0;
1464                         }
1465                 }
1466                 bmp = &GameBitmaps[i];
1467
1468                 Assert( !(bmp->bm_flags&BM_FLAG_PAGED_OUT) );
1469
1470                 if (fp1)
1471                         fprintf( fp1, "BMP: %s, size %d bytes", AllBitmaps[i].name, bmp->bm_rowsize * bmp->bm_h );
1472                 org_offset = ftell(pig_fp);
1473                 bmh.offset = data_offset - bitmap_data_start;
1474                 fseek( pig_fp, data_offset, SEEK_SET );
1475
1476                 if ( bmp->bm_flags & BM_FLAG_RLE )      {
1477                         size = (int *)bmp->bm_data;
1478                         fwrite( bmp->bm_data, sizeof(ubyte), *size, pig_fp );
1479                         data_offset += *size;
1480                         if (fp1)
1481                                 fprintf( fp1, ", and is already compressed to %d bytes.\n", *size );
1482                 } else {
1483                         fwrite( bmp->bm_data, sizeof(ubyte), bmp->bm_rowsize * bmp->bm_h, pig_fp );
1484                         data_offset += bmp->bm_rowsize * bmp->bm_h;
1485                         if (fp1)
1486                                 fprintf( fp1, ".\n" );
1487                 }
1488                 fseek( pig_fp, org_offset, SEEK_SET );
1489                 Assert( GameBitmaps[i].bm_w < 4096 );
1490                 bmh.width = (GameBitmaps[i].bm_w & 0xff);
1491                 bmh.wh_extra = ((GameBitmaps[i].bm_w >> 8) & 0x0f);
1492                 Assert( GameBitmaps[i].bm_h < 4096 );
1493                 bmh.height = GameBitmaps[i].bm_h;
1494                 bmh.wh_extra |= ((GameBitmaps[i].bm_h >> 4) & 0xf0);
1495                 bmh.flags = GameBitmaps[i].bm_flags;
1496                 if (piggy_is_substitutable_bitmap( AllBitmaps[i].name, subst_name ))    {
1497                         bitmap_index other_bitmap;
1498                         other_bitmap = piggy_find_bitmap( subst_name );
1499                         GameBitmapXlat[i] = other_bitmap.index;
1500                         bmh.flags |= BM_FLAG_PAGED_OUT;
1501                         //mprintf(( 0, "Skipping bitmap %d\n", i ));
1502                         //mprintf(( 0, "Marking '%s' as substitutible\n", AllBitmaps[i].name ));
1503                 } else  {
1504                         bmh.flags &= ~BM_FLAG_PAGED_OUT;
1505                 }
1506                 bmh.avg_color=GameBitmaps[i].avg_color;
1507                 fwrite( &bmh, DISKBITMAPHEADER_SIZE, 1, pig_fp );                    // Mark as a bitmap
1508         }
1509
1510         fclose(pig_fp);
1511
1512         mprintf( (0, " Dumped %d assorted bitmaps.\n", Num_bitmap_files ));
1513         fprintf( fp1, " Dumped %d assorted bitmaps.\n", Num_bitmap_files );
1514
1515         fclose(fp1);
1516         fclose(fp2);
1517
1518 }
1519
1520 static void write_int(int i,FILE *file)
1521 {
1522         if (fwrite( &i, sizeof(i), 1, file) != 1)
1523                 Error( "Error reading int in gamesave.c" );
1524
1525 }
1526
1527 void piggy_dump_all()
1528 {
1529         int i, xlat_offset;
1530         FILE * ham_fp;
1531         int org_offset,data_offset=0;
1532         DiskSoundHeader sndh;
1533         int sound_data_start=0;
1534         FILE *fp1,*fp2;
1535
1536         #ifdef NO_DUMP_SOUNDS
1537         Num_sound_files = 0;
1538         Num_sound_files_new = 0;
1539         #endif
1540
1541         if (!Must_write_hamfile && (Num_bitmap_files_new == 0) && (Num_sound_files_new == 0) )
1542                 return;
1543
1544         fp1 = fopen( "ham.lst", "wt" );
1545         fp2 = fopen( "ham.all", "wt" );
1546
1547         if (Must_write_hamfile || Num_bitmap_files_new) {
1548
1549                 mprintf( (0, "Creating %s...",DEFAULT_HAMFILE));
1550         
1551                 ham_fp = fopen( DEFAULT_HAMFILE, "wb" );                       //open HAM file
1552                 Assert( ham_fp!=NULL );
1553         
1554                 write_int(HAMFILE_ID,ham_fp);
1555                 write_int(HAMFILE_VERSION,ham_fp);
1556         
1557                 bm_write_all(ham_fp);
1558                 xlat_offset = ftell(ham_fp);
1559                 fwrite( GameBitmapXlat, sizeof(ushort)*MAX_BITMAP_FILES, 1, ham_fp );
1560                 //Dump bitmaps
1561         
1562                 if (Num_bitmap_files_new)
1563                         piggy_write_pigfile(DEFAULT_PIGFILE);
1564         
1565                 //free up memeory used by new bitmaps
1566                 for (i=Num_bitmap_files-Num_bitmap_files_new;i<Num_bitmap_files;i++)
1567                         d_free(GameBitmaps[i].bm_data);
1568         
1569                 //next thing must be done after pig written
1570                 fseek( ham_fp, xlat_offset, SEEK_SET );
1571                 fwrite( GameBitmapXlat, sizeof(ushort)*MAX_BITMAP_FILES, 1, ham_fp );
1572         
1573                 fclose(ham_fp);
1574                 mprintf( (0, "\n" ));
1575         }
1576         
1577         if (Num_sound_files_new) {
1578
1579                 mprintf( (0, "Creating %s...",DEFAULT_HAMFILE));
1580                 // Now dump sound file
1581                 ham_fp = fopen( DEFAULT_SNDFILE, "wb" );
1582                 Assert( ham_fp!=NULL );
1583         
1584                 write_int(SNDFILE_ID,ham_fp);
1585                 write_int(SNDFILE_VERSION,ham_fp);
1586
1587                 fwrite( &Num_sound_files, sizeof(int), 1, ham_fp );
1588         
1589                 mprintf( (0, "\nDumping sounds..." ));
1590         
1591                 sound_data_start = ftell(ham_fp);
1592                 sound_data_start += Num_sound_files*sizeof(DiskSoundHeader);
1593                 data_offset = sound_data_start;
1594         
1595                 for (i=0; i < Num_sound_files; i++ )    {
1596                         digi_sound *snd;
1597         
1598                         snd = &GameSounds[i];
1599                         strcpy( sndh.name, AllSounds[i].name );
1600                         sndh.length = GameSounds[i].length;
1601                         sndh.offset = data_offset - sound_data_start;
1602         
1603                         org_offset = ftell(ham_fp);
1604                         fseek( ham_fp, data_offset, SEEK_SET );
1605         
1606                         sndh.data_length = GameSounds[i].length;
1607                         fwrite( snd->data, sizeof(ubyte), snd->length, ham_fp );
1608                         data_offset += snd->length;
1609                         fseek( ham_fp, org_offset, SEEK_SET );
1610                         fwrite( &sndh, sizeof(DiskSoundHeader), 1, ham_fp );                    // Mark as a bitmap
1611         
1612                         fprintf( fp1, "SND: %s, size %d bytes\n", AllSounds[i].name, snd->length );
1613                         fprintf( fp2, "%s.raw\n", AllSounds[i].name );
1614                 }
1615
1616                 fclose(ham_fp);
1617                 mprintf( (0, "\n" ));
1618         }
1619
1620         fprintf( fp1, "Total sound size: %d bytes\n", data_offset-sound_data_start);
1621         mprintf( (0, " Dumped %d assorted sounds.\n", Num_sound_files ));
1622         fprintf( fp1, " Dumped %d assorted sounds.\n", Num_sound_files );
1623
1624         fclose(fp1);
1625         fclose(fp2);
1626
1627         // Never allow the game to run after building ham.
1628         exit(0);
1629 }
1630
1631 #endif
1632
1633 void piggy_close()
1634 {
1635         piggy_close_file();
1636
1637         if (BitmapBits)
1638                 d_free(BitmapBits);
1639
1640         if ( SoundBits )
1641                 d_free( SoundBits );
1642
1643         hashtable_free( &AllBitmapsNames );
1644         hashtable_free( &AllDigiSndNames );
1645
1646 }
1647
1648 int piggy_does_bitmap_exist_slow( char * name )
1649 {
1650         int i;
1651
1652         for (i=0; i<Num_bitmap_files; i++ )     {
1653                 if ( !strcmp( AllBitmaps[i].name, name) )
1654                         return 1;
1655         }
1656         return 0;
1657 }
1658
1659
1660 #define NUM_GAUGE_BITMAPS 23
1661 char * gauge_bitmap_names[NUM_GAUGE_BITMAPS] = {
1662         "gauge01", "gauge01b",
1663         "gauge02", "gauge02b",
1664         "gauge06", "gauge06b",
1665         "targ01", "targ01b",
1666         "targ02", "targ02b", 
1667         "targ03", "targ03b",
1668         "targ04", "targ04b",
1669         "targ05", "targ05b",
1670         "targ06", "targ06b",
1671         "gauge18", "gauge18b",
1672         "gauss1", "helix1",
1673         "phoenix1"
1674 };
1675
1676
1677 int piggy_is_gauge_bitmap( char * base_name )
1678 {
1679         int i;
1680         for (i=0; i<NUM_GAUGE_BITMAPS; i++ )    {
1681                 if ( !stricmp( base_name, gauge_bitmap_names[i] ))      
1682                         return 1;
1683         }
1684
1685         return 0;       
1686 }
1687
1688 int piggy_is_substitutable_bitmap( char * name, char * subst_name )
1689 {
1690         int frame;
1691         char * p;
1692         char base_name[ 16 ];
1693         
1694         strcpy( subst_name, name );
1695         p = strchr( subst_name, '#' );
1696         if ( p )        {
1697                 frame = atoi( &p[1] );
1698                 *p = 0;
1699                 strcpy( base_name, subst_name );
1700                 if ( !piggy_is_gauge_bitmap( base_name ))       {
1701                         sprintf( subst_name, "%s#%d", base_name, frame+1 );
1702                         if ( piggy_does_bitmap_exist_slow( subst_name )  )      {
1703                                 if ( frame & 1 ) {
1704                                         sprintf( subst_name, "%s#%d", base_name, frame-1 );
1705                                         return 1;
1706                                 }
1707                         }
1708                 }
1709         }
1710         strcpy( subst_name, name );
1711         return 0;
1712 }
1713
1714
1715
1716 #ifdef WINDOWS
1717 //      New Windows stuff
1718
1719 //      windows bitmap page in
1720 //              Page in a bitmap, if ddraw, then page it into a ddsurface in 
1721 //              'video' memory.  if that fails, page it in normally.
1722
1723 void piggy_bitmap_page_in_w( bitmap_index bitmap, int ddraw )
1724 {
1725 }
1726
1727
1728 //      Essential when switching video modes!
1729
1730 void piggy_bitmap_page_out_all_w()
1731 {
1732 }
1733
1734 #endif
1735
1736 #ifndef FAST_FILE_IO
1737 /*
1738  * reads a bitmap_index structure from a CFILE
1739  */
1740 void bitmap_index_read(bitmap_index *bi, CFILE *fp)
1741 {
1742         bi->index = cfile_read_short(fp);
1743 }
1744
1745 /*
1746  * reads n bitmap_index structs from a CFILE
1747  */
1748 int bitmap_index_read_n(bitmap_index *bi, int n, CFILE *fp)
1749 {
1750         int i;
1751
1752         for (i = 0; i < n; i++)
1753                 bi[i].index = cfile_read_short(fp);
1754         return i;
1755 }
1756
1757 /*
1758  * reads a DiskBitmapHeader structure from a CFILE
1759  */
1760 void DiskBitmapHeader_read(DiskBitmapHeader *dbh, CFILE *fp)
1761 {
1762         cfread(dbh->name, 8, 1, fp);
1763         dbh->dflags = cfile_read_byte(fp);
1764         dbh->width = cfile_read_byte(fp);
1765         dbh->height = cfile_read_byte(fp);
1766         dbh->wh_extra = cfile_read_byte(fp);
1767         dbh->flags = cfile_read_byte(fp);
1768         dbh->avg_color = cfile_read_byte(fp);
1769         dbh->offset = cfile_read_int(fp);
1770 }
1771
1772 /*
1773  * reads a DiskSoundHeader structure from a CFILE
1774  */
1775 void DiskSoundHeader_read(DiskSoundHeader *dsh, CFILE *fp)
1776 {
1777         cfread(dsh->name, 8, 1, fp);
1778         dsh->length = cfile_read_int(fp);
1779         dsh->data_length = cfile_read_int(fp);
1780         dsh->offset = cfile_read_int(fp);
1781 }
1782 #endif