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