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