SHAREWARE fixes
[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.12 2002-07-30 04:52:45 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                 "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 extern void bm_read_all(CFILE * fp);
923
924 #define HAMFILE_ID              MAKE_SIG('!','M','A','H') //HAM!
925 #ifdef SHAREWARE
926 #define HAMFILE_VERSION 2
927 #else
928 #define HAMFILE_VERSION 3
929 #endif
930 //version 1 -> 2:  save marker_model_num
931 //version 2 -> 3:  removed sound files
932
933 #define SNDFILE_ID              MAKE_SIG('D','N','S','D') //DSND
934 #define SNDFILE_VERSION 1
935
936 int read_hamfile()
937 {
938         CFILE * ham_fp = NULL;
939         int ham_id,ham_version;
940         int sound_offset = 0;
941         #ifdef MACINTOSH
942         char name[255];
943         #endif
944         
945         #ifndef MACINTOSH
946         ham_fp = cfopen( DEFAULT_HAMFILE, "rb" );
947         #else
948         sprintf(name, ":Data:%s", DEFAULT_HAMFILE );
949         ham_fp = cfopen( name, "rb" );
950         #endif
951         
952         if (ham_fp == NULL) {
953                 Must_write_hamfile = 1;
954                 return 0;
955         }
956
957         //make sure ham is valid type file & is up-to-date
958         ham_id = cfile_read_int(ham_fp);
959         ham_version = cfile_read_int(ham_fp);
960         if (ham_id != HAMFILE_ID || ham_version != HAMFILE_VERSION) {
961                 Must_write_hamfile = 1;
962                 cfclose(ham_fp);                                                //out of date ham
963                 return 0;
964         }
965
966         if (ham_version < 3) // hamfile contains sound info
967                 sound_offset = cfile_read_int(ham_fp);
968         
969         #ifndef EDITOR
970         {
971                 //int i;
972
973                 bm_read_all( ham_fp );  // Note connection to above if!!!
974                 printf("position: %d\n", cftell(ham_fp));
975                 cfread( GameBitmapXlat, sizeof(ushort)*MAX_BITMAP_FILES, 1, ham_fp );
976                 // no swap here?
977                 //for (i = 0; i < MAX_BITMAP_FILES; i++) {
978                         //GameBitmapXlat[i] = INTEL_SHORT(GameBitmapXlat[i]);
979                         //printf("GameBitmapXlat[%d] = %d\n", i, GameBitmapXlat[i]);
980                 //}
981         }
982         #endif
983
984         if (ham_version < 3) {
985                 int N_sounds;
986                 int sound_start;
987                 int header_size;
988                 int i;
989                 DiskSoundHeader sndh;
990                 digi_sound temp_sound;
991                 char temp_name_read[16];
992                 int sbytes = 0;
993
994                 cfseek(ham_fp, sound_offset, SEEK_SET);
995                 N_sounds = cfile_read_int(ham_fp);
996
997                 sound_start = cftell(ham_fp);
998
999                 header_size = N_sounds * DISKSOUNDHEADER_SIZE;
1000
1001                 //Read sounds
1002
1003                 for (i=0; i<N_sounds; i++ )     {
1004                         DiskSoundHeader_read(&sndh, ham_fp);
1005                         temp_sound.length = sndh.length;
1006                         temp_sound.data = (ubyte *)(sndh.offset + header_size + sound_start);
1007                         SoundOffset[Num_sound_files] = sndh.offset + header_size + sound_start;
1008                         memcpy( temp_name_read, sndh.name, 8 );
1009                         temp_name_read[8] = 0;
1010                         piggy_register_sound( &temp_sound, temp_name_read, 1 );
1011 #ifdef MACINTOSH
1012                         if (piggy_is_needed(i))
1013 #endif          // note link to if.
1014                                 sbytes += sndh.length;
1015                         //mprintf(( 0, "%d bytes of sound\n", sbytes ));
1016                 }
1017
1018                 SoundBits = d_malloc( sbytes + 16 );
1019                 if ( SoundBits == NULL )
1020                         Error( "Not enough memory to load sounds\n" );
1021
1022                 mprintf(( 0, "\nBitmaps: %d KB   Sounds: %d KB\n", Piggy_bitmap_cache_size/1024, sbytes/1024 ));
1023
1024                 //      piggy_read_sounds(ham_fp);
1025
1026         }
1027
1028         cfclose(ham_fp);
1029
1030         return 1;
1031
1032 }
1033
1034 int read_sndfile()
1035 {
1036         CFILE * snd_fp = NULL;
1037         int snd_id,snd_version;
1038         int N_sounds;
1039         int sound_start;
1040         int header_size;
1041         int i,size, length;
1042         DiskSoundHeader sndh;
1043         digi_sound temp_sound;
1044         char temp_name_read[16];
1045         int sbytes = 0;
1046         #ifdef MACINTOSH
1047         char name[255];
1048         #endif
1049
1050         #ifndef MACINTOSH
1051         snd_fp = cfopen( DEFAULT_SNDFILE, "rb" );
1052         #else
1053         sprintf( name, ":Data:%s", DEFAULT_SNDFILE );
1054         snd_fp = cfopen( name, "rb");
1055         #endif
1056         
1057         if (snd_fp == NULL)
1058                 return 0;
1059
1060         //make sure soundfile is valid type file & is up-to-date
1061         snd_id = cfile_read_int(snd_fp);
1062         snd_version = cfile_read_int(snd_fp);
1063         if (snd_id != SNDFILE_ID || snd_version != SNDFILE_VERSION) {
1064                 cfclose(snd_fp);                                                //out of date sound file
1065                 return 0;
1066         }
1067
1068         N_sounds = cfile_read_int(snd_fp);
1069
1070         sound_start = cftell(snd_fp);
1071         size = cfilelength(snd_fp) - sound_start;
1072         length = size;
1073         mprintf( (0, "\nReading data (%d KB) ", size/1024 ));
1074
1075         header_size = N_sounds*sizeof(DiskSoundHeader);
1076
1077         //Read sounds
1078
1079         for (i=0; i<N_sounds; i++ )     {
1080                 DiskSoundHeader_read(&sndh, snd_fp);
1081                 //size -= sizeof(DiskSoundHeader);
1082                 temp_sound.length = sndh.length;
1083                 temp_sound.data = (ubyte *)(sndh.offset + header_size + sound_start);
1084                 SoundOffset[Num_sound_files] = sndh.offset + header_size + sound_start;
1085                 memcpy( temp_name_read, sndh.name, 8 );
1086                 temp_name_read[8] = 0;
1087                 piggy_register_sound( &temp_sound, temp_name_read, 1 );
1088                 #ifdef MACINTOSH
1089                 if (piggy_is_needed(i))
1090                 #endif          // note link to if.
1091                 sbytes += sndh.length;
1092                 //mprintf(( 0, "%d bytes of sound\n", sbytes ));
1093         }
1094
1095         SoundBits = d_malloc( sbytes + 16 );
1096         if ( SoundBits == NULL )
1097                 Error( "Not enough memory to load sounds\n" );
1098
1099         mprintf(( 0, "\nBitmaps: %d KB   Sounds: %d KB\n", Piggy_bitmap_cache_size/1024, sbytes/1024 ));
1100
1101 //      piggy_read_sounds(snd_fp);
1102
1103         cfclose(snd_fp);
1104
1105         return 1;
1106 }
1107
1108 int piggy_init(void)
1109 {
1110         int ham_ok=0,snd_ok=0;
1111         int i;
1112
1113         hashtable_init( &AllBitmapsNames, MAX_BITMAP_FILES );
1114         hashtable_init( &AllDigiSndNames, MAX_SOUND_FILES );
1115
1116         for (i=0; i<MAX_SOUND_FILES; i++ )      {
1117                 GameSounds[i].length = 0;
1118                 GameSounds[i].data = NULL;
1119                 SoundOffset[i] = 0;
1120         }
1121
1122         for (i=0; i<MAX_BITMAP_FILES; i++ )     
1123                 GameBitmapXlat[i] = i;
1124
1125         if ( !bogus_bitmap_initialized )        {
1126                 int i;
1127                 ubyte c;
1128                 bogus_bitmap_initialized = 1;
1129                 memset( &bogus_bitmap, 0, sizeof(grs_bitmap) );
1130                 bogus_bitmap.bm_w = bogus_bitmap.bm_h = bogus_bitmap.bm_rowsize = 64;
1131                 bogus_bitmap.bm_data = bogus_data;
1132                 c = gr_find_closest_color( 0, 0, 63 );
1133                 for (i=0; i<4096; i++ ) bogus_data[i] = c;
1134                 c = gr_find_closest_color( 63, 0, 0 );
1135                 // Make a big red X !
1136                 for (i=0; i<64; i++ )   {
1137                         bogus_data[i*64+i] = c;
1138                         bogus_data[i*64+(63-i)] = c;
1139                 }
1140                 piggy_register_bitmap( &bogus_bitmap, "bogus", 1 );
1141                 bogus_sound.length = 64*64;
1142                 bogus_sound.data = bogus_data;
1143                 GameBitmapOffset[0] = 0;
1144         }
1145
1146         if ( FindArg( "-bigpig" ))
1147                 BigPig = 1;
1148
1149         if ( FindArg( "-lowmem" ))
1150                 piggy_low_memory = 1;
1151
1152         if ( FindArg( "-nolowmem" ))
1153                 piggy_low_memory = 0;
1154
1155         if (piggy_low_memory)
1156                 digi_lomem = 1;
1157
1158         WIN(DDGRLOCK(dd_grd_curcanv));
1159                 gr_set_curfont( SMALL_FONT );
1160                 gr_set_fontcolor(gr_find_closest_color_current( 20, 20, 20 ),-1 );
1161                 gr_printf( 0x8000, grd_curcanv->cv_h-20, "%s...", TXT_LOADING_DATA );
1162         WIN(DDGRUNLOCK(dd_grd_curcanv));
1163                 
1164         #ifdef EDITOR
1165         piggy_init_pigfile(DEFAULT_PIGFILE);
1166         #endif
1167
1168 #ifndef SHAREWARE
1169         ham_ok = read_hamfile();
1170         snd_ok = read_sndfile();
1171 #else
1172         snd_ok = ham_ok = read_hamfile();
1173 #endif
1174
1175         atexit(piggy_close);
1176
1177         mprintf ((0,"HamOk=%d SndOk=%d\n",ham_ok,snd_ok));
1178         return (ham_ok && snd_ok);               //read ok
1179 }
1180
1181 int piggy_is_needed(int soundnum)
1182 {
1183         int i;
1184
1185         if ( !digi_lomem ) return 1;
1186
1187         for (i=0; i<MAX_SOUNDS; i++ )   {
1188                 if ( (AltSounds[i] < 255) && (Sounds[AltSounds[i]] == soundnum) )
1189                         return 1;
1190         }
1191         return 0;
1192 }
1193
1194
1195 void piggy_read_sounds(void)
1196 {
1197         CFILE * fp = NULL;
1198         ubyte * ptr;
1199         int i, sbytes;
1200         #ifdef MACINTOSH
1201         char name[255];
1202         #endif
1203
1204         ptr = SoundBits;
1205         sbytes = 0;
1206
1207         #ifndef MACINTOSH
1208         fp = cfopen( DEFAULT_SNDFILE, "rb" );
1209         #else
1210         sprintf( name, ":Data:%s", DEFAULT_SNDFILE );
1211         fp = cfopen( name, "rb");
1212         #endif
1213
1214         if (fp == NULL)
1215                 return;
1216
1217         for (i=0; i<Num_sound_files; i++ )      {
1218                 digi_sound *snd = &GameSounds[i];
1219
1220                 if ( SoundOffset[i] > 0 )       {
1221                         if ( piggy_is_needed(i) )       {
1222                                 cfseek( fp, SoundOffset[i], SEEK_SET );
1223         
1224                                 // Read in the sound data!!!
1225                                 snd->data = ptr;
1226                                 ptr += snd->length;
1227                                 sbytes += snd->length;
1228                                 cfread( snd->data, snd->length, 1, fp );
1229                         }
1230                         else
1231                                 snd->data = (ubyte *) -1;
1232                 }
1233         }
1234
1235         cfclose(fp);
1236
1237         mprintf(( 0, "\nActual Sound usage: %d KB\n", sbytes/1024 ));
1238
1239 }
1240
1241
1242 extern int descent_critical_error;
1243 extern unsigned descent_critical_deverror;
1244 extern unsigned descent_critical_errcode;
1245
1246 char * crit_errors[13] = { "Write Protected", "Unknown Unit", "Drive Not Ready", "Unknown Command", "CRC Error", \
1247 "Bad struct length", "Seek Error", "Unknown media type", "Sector not found", "Printer out of paper", "Write Fault", \
1248 "Read fault", "General Failure" };
1249
1250 void piggy_critical_error()
1251 {
1252         grs_canvas * save_canv;
1253         grs_font * save_font;
1254         int i;
1255         save_canv = grd_curcanv;
1256         save_font = grd_curcanv->cv_font;
1257         gr_palette_load( gr_palette );
1258         i = nm_messagebox( "Disk Error", 2, "Retry", "Exit", "%s\non drive %c:", crit_errors[descent_critical_errcode&0xf], (descent_critical_deverror&0xf)+'A'  );
1259         if ( i == 1 )
1260                 exit(1);
1261         gr_set_current_canvas(save_canv);
1262         grd_curcanv->cv_font = save_font;
1263 }
1264
1265 void piggy_bitmap_page_in( bitmap_index bitmap )
1266 {
1267         grs_bitmap * bmp;
1268         int i,org_i,temp;
1269
1270         org_i = 0;
1271                         
1272         i = bitmap.index;
1273         Assert( i >= 0 );
1274         Assert( i < MAX_BITMAP_FILES );
1275         Assert( i < Num_bitmap_files );
1276         Assert( Piggy_bitmap_cache_size > 0 );
1277
1278         if ( i < 1 ) return;
1279         if ( i >= MAX_BITMAP_FILES ) return;
1280         if ( i >= Num_bitmap_files ) return;
1281         
1282         if ( GameBitmapOffset[i] == 0 ) return;         // A read-from-disk bitmap!!!
1283
1284         if ( piggy_low_memory ) {
1285                 org_i = i;
1286                 i = GameBitmapXlat[i];          // Xlat for low-memory settings!
1287         }
1288         
1289         bmp = &GameBitmaps[i];
1290
1291         if ( bmp->bm_flags & BM_FLAG_PAGED_OUT )        {
1292                 stop_time();
1293
1294         ReDoIt:
1295                 descent_critical_error = 0;
1296                 cfseek( Piggy_fp, GameBitmapOffset[i], SEEK_SET );
1297                 if ( descent_critical_error )   {
1298                         piggy_critical_error();
1299                         goto ReDoIt;
1300                 }
1301                 
1302                 bmp->bm_data = &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next];
1303                 bmp->bm_flags = GameBitmapFlags[i];
1304         
1305                 if ( bmp->bm_flags & BM_FLAG_RLE )      {
1306                         int zsize = 0;
1307                         descent_critical_error = 0;
1308                         zsize = cfile_read_int(Piggy_fp);
1309                         if ( descent_critical_error )   {
1310                                 piggy_critical_error();
1311                                 goto ReDoIt;
1312                         }
1313         
1314                         // GET JOHN NOW IF YOU GET THIS ASSERT!!!
1315                         //Assert( Piggy_bitmap_cache_next+zsize < Piggy_bitmap_cache_size );      
1316                         if ( Piggy_bitmap_cache_next+zsize >= Piggy_bitmap_cache_size ) {
1317                                 Int3();
1318                                 piggy_bitmap_page_out_all();
1319                                 goto ReDoIt;
1320                         }
1321                         memcpy( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], &zsize, sizeof(int) );
1322                         Piggy_bitmap_cache_next += sizeof(int);
1323                         descent_critical_error = 0;
1324                         temp = cfread( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], 1, zsize-4, Piggy_fp );
1325                         if ( descent_critical_error )   {
1326                                 piggy_critical_error();
1327                                 goto ReDoIt;
1328                         }
1329                         Piggy_bitmap_cache_next += zsize-4;
1330                 } else {
1331                         // GET JOHN NOW IF YOU GET THIS ASSERT!!!
1332                         Assert( Piggy_bitmap_cache_next+(bmp->bm_h*bmp->bm_w) < Piggy_bitmap_cache_size );      
1333                         if ( Piggy_bitmap_cache_next+(bmp->bm_h*bmp->bm_w) >= Piggy_bitmap_cache_size ) {
1334                                 piggy_bitmap_page_out_all();
1335                                 goto ReDoIt;
1336                         }
1337                         descent_critical_error = 0;
1338                         temp = cfread( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], 1, bmp->bm_h*bmp->bm_w, Piggy_fp );
1339                         if ( descent_critical_error )   {
1340                                 piggy_critical_error();
1341                                 goto ReDoIt;
1342                         }
1343                         Piggy_bitmap_cache_next+=bmp->bm_h*bmp->bm_w;
1344                 }
1345         
1346                 //@@if ( bmp->bm_selector ) {
1347                 //@@#if !defined(WINDOWS) && !defined(MACINTOSH)
1348                 //@@    if (!dpmi_modify_selector_base( bmp->bm_selector, bmp->bm_data ))
1349                 //@@            Error( "Error modifying selector base in piggy.c\n" );
1350                 //@@#endif
1351                 //@@}
1352
1353                 start_time();
1354         }
1355
1356         if ( piggy_low_memory ) {
1357                 if ( org_i != i )
1358                         GameBitmaps[org_i] = GameBitmaps[i];
1359         }
1360
1361 //@@Removed from John's code:
1362 //@@#ifndef WINDOWS
1363 //@@    if ( bmp->bm_selector ) {
1364 //@@            if (!dpmi_modify_selector_base( bmp->bm_selector, bmp->bm_data ))
1365 //@@                    Error( "Error modifying selector base in piggy.c\n" );
1366 //@@    }
1367 //@@#endif
1368
1369 }
1370
1371 void piggy_bitmap_page_out_all()
1372 {
1373         int i;
1374         
1375         Piggy_bitmap_cache_next = 0;
1376
1377         piggy_page_flushed++;
1378
1379         texmerge_flush();
1380         rle_cache_flush();
1381
1382         for (i=0; i<Num_bitmap_files; i++ )             {
1383                 if ( GameBitmapOffset[i] > 0 )  {       // Don't page out bitmaps read from disk!!!
1384                         GameBitmaps[i].bm_flags = BM_FLAG_PAGED_OUT;
1385                         GameBitmaps[i].bm_data = Piggy_bitmap_cache_data;
1386                 }
1387         }
1388
1389         mprintf(( 0, "Flushing piggy bitmap cache\n" ));
1390 }
1391
1392 void piggy_load_level_data()
1393 {
1394         piggy_bitmap_page_out_all();
1395         paging_touch_all();
1396 }
1397
1398 #ifdef EDITOR
1399
1400 void change_filename_ext( char *dest, char *src, char *ext );
1401
1402 void piggy_write_pigfile(char *filename)
1403 {
1404         FILE *pig_fp;
1405         int bitmap_data_start,data_offset;
1406         DiskBitmapHeader bmh;
1407         int org_offset;
1408         char subst_name[32];
1409         int i;
1410         FILE *fp1,*fp2;
1411         char tname[FILENAME_LEN];
1412
1413         // -- mprintf( (0, "Paging in all piggy bitmaps..." ));
1414         for (i=0; i < Num_bitmap_files; i++ )   {
1415                 bitmap_index bi;
1416                 bi.index = i;
1417                 PIGGY_PAGE_IN( bi );
1418         }
1419         // -- mprintf( (0, "\n" ));
1420
1421         piggy_close_file();
1422
1423         // -- mprintf( (0, "Creating %s...",filename ));
1424
1425         pig_fp = fopen( filename, "wb" );       //open PIG file
1426         Assert( pig_fp!=NULL );
1427
1428         write_int(PIGFILE_ID,pig_fp);
1429         write_int(PIGFILE_VERSION,pig_fp);
1430
1431         Num_bitmap_files--;
1432         fwrite( &Num_bitmap_files, sizeof(int), 1, pig_fp );
1433         Num_bitmap_files++;
1434
1435         bitmap_data_start = ftell(pig_fp);
1436         bitmap_data_start += (Num_bitmap_files-1)*DISKBITMAPHEADER_SIZE; 
1437         data_offset = bitmap_data_start;
1438
1439         change_filename_ext(tname,filename,"lst");
1440         fp1 = fopen( tname, "wt" );
1441         change_filename_ext(tname,filename,"all");
1442         fp2 = fopen( tname, "wt" );
1443
1444         for (i=1; i < Num_bitmap_files; i++ )   {
1445                 int *size;
1446                 grs_bitmap *bmp;
1447
1448                 {               
1449                         char * p, *p1;
1450                         p = strchr(AllBitmaps[i].name,'#');
1451                         if (p)  {
1452                                 int n;
1453                                 p1 = p; p1++; 
1454                                 n = atoi(p1);
1455                                 *p = 0;
1456                                 if (fp2 && n==0)
1457                                         fprintf( fp2, "%s.abm\n", AllBitmaps[i].name );
1458                                 memcpy( bmh.name, AllBitmaps[i].name, 8 );
1459                                 Assert( n <= 63 );
1460                                 bmh.dflags = DBM_FLAG_ABM + n;
1461                                 *p = '#';
1462                         }else {
1463                                 if (fp2)
1464                                         fprintf( fp2, "%s.bbm\n", AllBitmaps[i].name );
1465                                 memcpy( bmh.name, AllBitmaps[i].name, 8 );
1466                                 bmh.dflags = 0;
1467                         }
1468                 }
1469                 bmp = &GameBitmaps[i];
1470
1471                 Assert( !(bmp->bm_flags&BM_FLAG_PAGED_OUT) );
1472
1473                 if (fp1)
1474                         fprintf( fp1, "BMP: %s, size %d bytes", AllBitmaps[i].name, bmp->bm_rowsize * bmp->bm_h );
1475                 org_offset = ftell(pig_fp);
1476                 bmh.offset = data_offset - bitmap_data_start;
1477                 fseek( pig_fp, data_offset, SEEK_SET );
1478
1479                 if ( bmp->bm_flags & BM_FLAG_RLE )      {
1480                         size = (int *)bmp->bm_data;
1481                         fwrite( bmp->bm_data, sizeof(ubyte), *size, pig_fp );
1482                         data_offset += *size;
1483                         if (fp1)
1484                                 fprintf( fp1, ", and is already compressed to %d bytes.\n", *size );
1485                 } else {
1486                         fwrite( bmp->bm_data, sizeof(ubyte), bmp->bm_rowsize * bmp->bm_h, pig_fp );
1487                         data_offset += bmp->bm_rowsize * bmp->bm_h;
1488                         if (fp1)
1489                                 fprintf( fp1, ".\n" );
1490                 }
1491                 fseek( pig_fp, org_offset, SEEK_SET );
1492                 Assert( GameBitmaps[i].bm_w < 4096 );
1493                 bmh.width = (GameBitmaps[i].bm_w & 0xff);
1494                 bmh.wh_extra = ((GameBitmaps[i].bm_w >> 8) & 0x0f);
1495                 Assert( GameBitmaps[i].bm_h < 4096 );
1496                 bmh.height = GameBitmaps[i].bm_h;
1497                 bmh.wh_extra |= ((GameBitmaps[i].bm_h >> 4) & 0xf0);
1498                 bmh.flags = GameBitmaps[i].bm_flags;
1499                 if (piggy_is_substitutable_bitmap( AllBitmaps[i].name, subst_name ))    {
1500                         bitmap_index other_bitmap;
1501                         other_bitmap = piggy_find_bitmap( subst_name );
1502                         GameBitmapXlat[i] = other_bitmap.index;
1503                         bmh.flags |= BM_FLAG_PAGED_OUT;
1504                         //mprintf(( 0, "Skipping bitmap %d\n", i ));
1505                         //mprintf(( 0, "Marking '%s' as substitutible\n", AllBitmaps[i].name ));
1506                 } else  {
1507                         bmh.flags &= ~BM_FLAG_PAGED_OUT;
1508                 }
1509                 bmh.avg_color=GameBitmaps[i].avg_color;
1510                 fwrite( &bmh, DISKBITMAPHEADER_SIZE, 1, pig_fp );                    // Mark as a bitmap
1511         }
1512
1513         fclose(pig_fp);
1514
1515         mprintf( (0, " Dumped %d assorted bitmaps.\n", Num_bitmap_files ));
1516         fprintf( fp1, " Dumped %d assorted bitmaps.\n", Num_bitmap_files );
1517
1518         fclose(fp1);
1519         fclose(fp2);
1520
1521 }
1522
1523 static void write_int(int i,FILE *file)
1524 {
1525         if (fwrite( &i, sizeof(i), 1, file) != 1)
1526                 Error( "Error reading int in gamesave.c" );
1527
1528 }
1529
1530 void piggy_dump_all()
1531 {
1532         int i, xlat_offset;
1533         FILE * ham_fp;
1534         int org_offset,data_offset=0;
1535         DiskSoundHeader sndh;
1536         int sound_data_start=0;
1537         FILE *fp1,*fp2;
1538
1539         #ifdef NO_DUMP_SOUNDS
1540         Num_sound_files = 0;
1541         Num_sound_files_new = 0;
1542         #endif
1543
1544         if (!Must_write_hamfile && (Num_bitmap_files_new == 0) && (Num_sound_files_new == 0) )
1545                 return;
1546
1547         fp1 = fopen( "ham.lst", "wt" );
1548         fp2 = fopen( "ham.all", "wt" );
1549
1550         if (Must_write_hamfile || Num_bitmap_files_new) {
1551
1552                 mprintf( (0, "Creating %s...",DEFAULT_HAMFILE));
1553         
1554                 ham_fp = fopen( DEFAULT_HAMFILE, "wb" );                       //open HAM file
1555                 Assert( ham_fp!=NULL );
1556         
1557                 write_int(HAMFILE_ID,ham_fp);
1558                 write_int(HAMFILE_VERSION,ham_fp);
1559         
1560                 bm_write_all(ham_fp);
1561                 xlat_offset = ftell(ham_fp);
1562                 fwrite( GameBitmapXlat, sizeof(ushort)*MAX_BITMAP_FILES, 1, ham_fp );
1563                 //Dump bitmaps
1564         
1565                 if (Num_bitmap_files_new)
1566                         piggy_write_pigfile(DEFAULT_PIGFILE);
1567         
1568                 //free up memeory used by new bitmaps
1569                 for (i=Num_bitmap_files-Num_bitmap_files_new;i<Num_bitmap_files;i++)
1570                         d_free(GameBitmaps[i].bm_data);
1571         
1572                 //next thing must be done after pig written
1573                 fseek( ham_fp, xlat_offset, SEEK_SET );
1574                 fwrite( GameBitmapXlat, sizeof(ushort)*MAX_BITMAP_FILES, 1, ham_fp );
1575         
1576                 fclose(ham_fp);
1577                 mprintf( (0, "\n" ));
1578         }
1579         
1580         if (Num_sound_files_new) {
1581
1582                 mprintf( (0, "Creating %s...",DEFAULT_HAMFILE));
1583                 // Now dump sound file
1584                 ham_fp = fopen( DEFAULT_SNDFILE, "wb" );
1585                 Assert( ham_fp!=NULL );
1586         
1587                 write_int(SNDFILE_ID,ham_fp);
1588                 write_int(SNDFILE_VERSION,ham_fp);
1589
1590                 fwrite( &Num_sound_files, sizeof(int), 1, ham_fp );
1591         
1592                 mprintf( (0, "\nDumping sounds..." ));
1593         
1594                 sound_data_start = ftell(ham_fp);
1595                 sound_data_start += Num_sound_files*sizeof(DiskSoundHeader);
1596                 data_offset = sound_data_start;
1597         
1598                 for (i=0; i < Num_sound_files; i++ )    {
1599                         digi_sound *snd;
1600         
1601                         snd = &GameSounds[i];
1602                         strcpy( sndh.name, AllSounds[i].name );
1603                         sndh.length = GameSounds[i].length;
1604                         sndh.offset = data_offset - sound_data_start;
1605         
1606                         org_offset = ftell(ham_fp);
1607                         fseek( ham_fp, data_offset, SEEK_SET );
1608         
1609                         sndh.data_length = GameSounds[i].length;
1610                         fwrite( snd->data, sizeof(ubyte), snd->length, ham_fp );
1611                         data_offset += snd->length;
1612                         fseek( ham_fp, org_offset, SEEK_SET );
1613                         fwrite( &sndh, sizeof(DiskSoundHeader), 1, ham_fp );                    // Mark as a bitmap
1614         
1615                         fprintf( fp1, "SND: %s, size %d bytes\n", AllSounds[i].name, snd->length );
1616                         fprintf( fp2, "%s.raw\n", AllSounds[i].name );
1617                 }
1618
1619                 fclose(ham_fp);
1620                 mprintf( (0, "\n" ));
1621         }
1622
1623         fprintf( fp1, "Total sound size: %d bytes\n", data_offset-sound_data_start);
1624         mprintf( (0, " Dumped %d assorted sounds.\n", Num_sound_files ));
1625         fprintf( fp1, " Dumped %d assorted sounds.\n", Num_sound_files );
1626
1627         fclose(fp1);
1628         fclose(fp2);
1629
1630         // Never allow the game to run after building ham.
1631         exit(0);
1632 }
1633
1634 #endif
1635
1636 void piggy_close()
1637 {
1638         piggy_close_file();
1639
1640         if (BitmapBits)
1641                 d_free(BitmapBits);
1642
1643         if ( SoundBits )
1644                 d_free( SoundBits );
1645
1646         hashtable_free( &AllBitmapsNames );
1647         hashtable_free( &AllDigiSndNames );
1648
1649 }
1650
1651 int piggy_does_bitmap_exist_slow( char * name )
1652 {
1653         int i;
1654
1655         for (i=0; i<Num_bitmap_files; i++ )     {
1656                 if ( !strcmp( AllBitmaps[i].name, name) )
1657                         return 1;
1658         }
1659         return 0;
1660 }
1661
1662
1663 #define NUM_GAUGE_BITMAPS 23
1664 char * gauge_bitmap_names[NUM_GAUGE_BITMAPS] = {
1665         "gauge01", "gauge01b",
1666         "gauge02", "gauge02b",
1667         "gauge06", "gauge06b",
1668         "targ01", "targ01b",
1669         "targ02", "targ02b", 
1670         "targ03", "targ03b",
1671         "targ04", "targ04b",
1672         "targ05", "targ05b",
1673         "targ06", "targ06b",
1674         "gauge18", "gauge18b",
1675         "gauss1", "helix1",
1676         "phoenix1"
1677 };
1678
1679
1680 int piggy_is_gauge_bitmap( char * base_name )
1681 {
1682         int i;
1683         for (i=0; i<NUM_GAUGE_BITMAPS; i++ )    {
1684                 if ( !stricmp( base_name, gauge_bitmap_names[i] ))      
1685                         return 1;
1686         }
1687
1688         return 0;       
1689 }
1690
1691 int piggy_is_substitutable_bitmap( char * name, char * subst_name )
1692 {
1693         int frame;
1694         char * p;
1695         char base_name[ 16 ];
1696         
1697         strcpy( subst_name, name );
1698         p = strchr( subst_name, '#' );
1699         if ( p )        {
1700                 frame = atoi( &p[1] );
1701                 *p = 0;
1702                 strcpy( base_name, subst_name );
1703                 if ( !piggy_is_gauge_bitmap( base_name ))       {
1704                         sprintf( subst_name, "%s#%d", base_name, frame+1 );
1705                         if ( piggy_does_bitmap_exist_slow( subst_name )  )      {
1706                                 if ( frame & 1 ) {
1707                                         sprintf( subst_name, "%s#%d", base_name, frame-1 );
1708                                         return 1;
1709                                 }
1710                         }
1711                 }
1712         }
1713         strcpy( subst_name, name );
1714         return 0;
1715 }
1716
1717
1718
1719 #ifdef WINDOWS
1720 //      New Windows stuff
1721
1722 //      windows bitmap page in
1723 //              Page in a bitmap, if ddraw, then page it into a ddsurface in 
1724 //              'video' memory.  if that fails, page it in normally.
1725
1726 void piggy_bitmap_page_in_w( bitmap_index bitmap, int ddraw )
1727 {
1728 }
1729
1730
1731 //      Essential when switching video modes!
1732
1733 void piggy_bitmap_page_out_all_w()
1734 {
1735 }
1736
1737 #endif
1738
1739 /*
1740  * reads a bitmap_index structure from a CFILE
1741  */
1742 void bitmap_index_read(bitmap_index *bi, CFILE *fp)
1743 {
1744         bi->index = cfile_read_short(fp);
1745 }
1746
1747 /*
1748  * reads a DiskBitmapHeader structure from a CFILE
1749  */
1750 void DiskBitmapHeader_read(DiskBitmapHeader *dbh, CFILE *fp)
1751 {
1752         cfread(dbh->name, 8, 1, fp);
1753         dbh->dflags = cfile_read_byte(fp);
1754         dbh->width = cfile_read_byte(fp);
1755         dbh->height = cfile_read_byte(fp);
1756         dbh->wh_extra = cfile_read_byte(fp);
1757         dbh->flags = cfile_read_byte(fp);
1758         dbh->avg_color = cfile_read_byte(fp);
1759         dbh->offset = cfile_read_int(fp);
1760 }
1761
1762 /*
1763  * reads a DiskSoundHeader structure from a CFILE
1764  */
1765 void DiskSoundHeader_read(DiskSoundHeader *dsh, CFILE *fp)
1766 {
1767         cfread(dsh->name, 8, 1, fp);
1768         dsh->length = cfile_read_int(fp);
1769         dsh->data_length = cfile_read_int(fp);
1770         dsh->offset = cfile_read_int(fp);
1771 }