use memcpy to avoid alignment problem
[btb/d2x.git] / main / piggy.c
1 /* $Id: piggy.c,v 1.20 2003-01-21 22:28:08 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include <conf.h>
17 #endif
18
19 #ifdef RCS
20 static char rcsid[] = "$Id: piggy.c,v 1.20 2003-01-21 22:28:08 btb Exp $";
21 #endif
22
23
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "pstypes.h"
28 #include "strutil.h"
29 #include "inferno.h"
30 #include "gr.h"
31 #include "u_mem.h"
32 #include "iff.h"
33 #include "mono.h"
34 #include "error.h"
35 #include "sounds.h"
36 #include "songs.h"
37 #include "bm.h"
38 #include "bmread.h"
39 #include "hash.h"
40 #include "args.h"
41 #include "palette.h"
42 #include "gamefont.h"
43 #include "rle.h"
44 #include "screens.h"
45 #include "piggy.h"
46 #include "texmerge.h"
47 #include "paging.h"
48 #include "game.h"
49 #include "text.h"
50 #include "cfile.h"
51 #include "newmenu.h"
52 #include "byteswap.h"
53 #include "findfile.h"
54 #include "makesig.h"
55
56 #ifndef MACINTOSH
57 //      #include "unarj.h"
58 #else
59         #include <Strings.h>            // MacOS Toolbox header
60         #include <Files.h>
61         #include <unistd.h>
62 #endif
63
64 //#define NO_DUMP_SOUNDS        1   //if set, dump bitmaps but not sounds
65
66 #define DEFAULT_PIGFILE_REGISTERED      "groupa.pig"
67 #define DEFAULT_PIGFILE_SHAREWARE       "d2demo.pig"
68 #define DEFAULT_HAMFILE_REGISTERED      "descent2.ham"
69 #define DEFAULT_HAMFILE_SHAREWARE       "d2demo.ham"
70
71 #define DEFAULT_PIGFILE (cfexist(DEFAULT_PIGFILE_REGISTERED)?DEFAULT_PIGFILE_REGISTERED:DEFAULT_PIGFILE_SHAREWARE)
72 #define DEFAULT_HAMFILE (cfexist(DEFAULT_HAMFILE_REGISTERED)?DEFAULT_HAMFILE_REGISTERED:DEFAULT_HAMFILE_SHAREWARE)
73 #define DEFAULT_SNDFILE ((Piggy_hamfile_version < 3)?DEFAULT_HAMFILE_SHAREWARE:(digi_sample_rate==SAMPLE_RATE_22K)?"descent2.s22":"descent2.s11")
74
75 #define MAC_D2DEMO_PIG_SIZE 4929684
76
77 ubyte *BitmapBits = NULL;
78 ubyte *SoundBits = NULL;
79
80 typedef struct BitmapFile {
81         char    name[15];
82 } BitmapFile;
83
84 typedef struct SoundFile {
85         char    name[15];
86 } SoundFile;
87
88 hashtable AllBitmapsNames;
89 hashtable AllDigiSndNames;
90
91 int Num_bitmap_files = 0;
92 int Num_sound_files = 0;
93
94 digi_sound GameSounds[MAX_SOUND_FILES];
95 int SoundOffset[MAX_SOUND_FILES];
96 grs_bitmap GameBitmaps[MAX_BITMAP_FILES];
97
98 alias alias_list[MAX_ALIASES];
99 int Num_aliases=0;
100
101 int Must_write_hamfile = 0;
102 int Num_bitmap_files_new = 0;
103 int Num_sound_files_new = 0;
104 BitmapFile AllBitmaps[ MAX_BITMAP_FILES ];
105 static SoundFile AllSounds[ MAX_SOUND_FILES ];
106
107 int Piggy_hamfile_version = 0;
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 #endif
152
153 void swap_0_255(grs_bitmap *bmp)
154 {
155         int i;
156
157         for (i = 0; i < bmp->bm_h * bmp->bm_w; i++) {
158                 if(bmp->bm_data[i] == 0)
159                         bmp->bm_data[i] = 255;
160                 else if (bmp->bm_data[i] == 255)
161                         bmp->bm_data[i] = 0;
162         }
163 }
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         //rename pigfile for shareware
529         if (stricmp(DEFAULT_PIGFILE, DEFAULT_PIGFILE_SHAREWARE) == 0 && !cfexist(filename))
530                 filename = DEFAULT_PIGFILE_SHAREWARE;
531
532         #ifndef MACINTOSH
533                 Piggy_fp = cfopen( filename, "rb" );
534         #else
535                 sprintf(name, ":Data:%s", filename);
536                 Piggy_fp = cfopen( name, "rb" );
537
538                 #ifdef SHAREWARE        // if we are in the shareware version, we must have the pig by now.
539                         if (Piggy_fp == NULL)
540                         {
541                                 Error("Cannot load required file <%s>",name);
542                         }
543                 #endif  // end of if def shareware
544
545         #endif
546
547         if (!Piggy_fp) {
548                 #ifdef EDITOR
549                         return;         //if editor, ok to not have pig, because we'll build one
550                 #else
551                         Piggy_fp = copy_pigfile_from_cd(filename);
552                 #endif
553         }
554
555         if (Piggy_fp) {                         //make sure pig is valid type file & is up-to-date
556                 int pig_id,pig_version;
557
558                 pig_id = cfile_read_int(Piggy_fp);
559                 pig_version = cfile_read_int(Piggy_fp);
560                 if (pig_id != PIGFILE_ID || pig_version != PIGFILE_VERSION) {
561                         cfclose(Piggy_fp);              //out of date pig
562                         Piggy_fp = NULL;                        //..so pretend it's not here
563                 }
564         }
565
566         if (!Piggy_fp) {
567
568                 #ifdef EDITOR
569                         return;         //if editor, ok to not have pig, because we'll build one
570                 #else
571                         Error("Cannot load required file <%s>",filename);
572                 #endif
573         }
574
575         strncpy(Current_pigfile,filename,sizeof(Current_pigfile));
576
577         N_bitmaps = cfile_read_int(Piggy_fp);
578
579         header_size = N_bitmaps*DISKBITMAPHEADER_SIZE;
580
581         data_start = header_size + cftell(Piggy_fp);
582
583         data_size = cfilelength(Piggy_fp) - data_start;
584
585         Num_bitmap_files = 1;
586
587         for (i=0; i<N_bitmaps; i++ )    {
588                 DiskBitmapHeader_read(&bmh, Piggy_fp);
589                 memcpy( temp_name_read, bmh.name, 8 );
590                 temp_name_read[8] = 0;
591                 if ( bmh.dflags & DBM_FLAG_ABM )        
592                         sprintf( temp_name, "%s#%d", temp_name_read, bmh.dflags & 63 );
593                 else
594                         strcpy( temp_name, temp_name_read );
595                 memset( &temp_bitmap, 0, sizeof(grs_bitmap) );
596                 temp_bitmap.bm_w = temp_bitmap.bm_rowsize = bmh.width + ((short) (bmh.wh_extra&0x0f)<<8);
597                 temp_bitmap.bm_h = bmh.height + ((short) (bmh.wh_extra&0xf0)<<4);
598                 temp_bitmap.bm_flags = BM_FLAG_PAGED_OUT;
599                 temp_bitmap.avg_color = bmh.avg_color;
600                 temp_bitmap.bm_data = Piggy_bitmap_cache_data;
601
602                 GameBitmapFlags[i+1] = 0;
603                 if ( bmh.flags & BM_FLAG_TRANSPARENT ) GameBitmapFlags[i+1] |= BM_FLAG_TRANSPARENT;
604                 if ( bmh.flags & BM_FLAG_SUPER_TRANSPARENT ) GameBitmapFlags[i+1] |= BM_FLAG_SUPER_TRANSPARENT;
605                 if ( bmh.flags & BM_FLAG_NO_LIGHTING ) GameBitmapFlags[i+1] |= BM_FLAG_NO_LIGHTING;
606                 if ( bmh.flags & BM_FLAG_RLE ) GameBitmapFlags[i+1] |= BM_FLAG_RLE;
607                 if ( bmh.flags & BM_FLAG_RLE_BIG ) GameBitmapFlags[i+1] |= BM_FLAG_RLE_BIG;
608
609                 GameBitmapOffset[i+1] = bmh.offset + data_start;
610                 Assert( (i+1) == Num_bitmap_files );
611                 piggy_register_bitmap( &temp_bitmap, temp_name, 1 );
612         }
613
614 #ifdef EDITOR
615         Piggy_bitmap_cache_size = data_size + (data_size/10);   //extra mem for new bitmaps
616         Assert( Piggy_bitmap_cache_size > 0 );
617 #else
618         Piggy_bitmap_cache_size = PIGGY_BUFFER_SIZE;
619         #ifdef MACINTOSH
620         if (piggy_low_memory)
621                 Piggy_bitmap_cache_size = PIGGY_SMALL_BUFFER_SIZE;
622         #endif
623 #endif
624         BitmapBits = d_malloc( Piggy_bitmap_cache_size );
625         if ( BitmapBits == NULL )
626                 Error( "Not enough memory to load bitmaps\n" );
627         Piggy_bitmap_cache_data = BitmapBits;
628         Piggy_bitmap_cache_next = 0;
629
630         #if defined(MACINTOSH) && defined(SHAREWARE)
631 //      load_exit_models();
632         #endif
633
634         Pigfile_initialized=1;
635 }
636
637 #define FILENAME_LEN 13
638 #define MAX_BITMAPS_PER_BRUSH 30
639
640 extern int compute_average_pixel(grs_bitmap *new);
641
642 //reads in a new pigfile (for new palette)
643 //returns the size of all the bitmap data
644 void piggy_new_pigfile(char *pigname)
645 {
646         int i;
647         char temp_name[16];
648         char temp_name_read[16];
649         grs_bitmap temp_bitmap;
650         DiskBitmapHeader bmh;
651         int header_size, N_bitmaps, data_size, data_start;
652         int must_rewrite_pig = 0;
653         #ifdef MACINTOSH
654         char name[255];
655         #endif
656
657         strlwr(pigname);
658
659         //rename pigfile for shareware
660         if (stricmp(DEFAULT_PIGFILE, DEFAULT_PIGFILE_SHAREWARE) == 0 && !cfexist(pigname))
661                 pigname = DEFAULT_PIGFILE_SHAREWARE;
662
663         if (strnicmp(Current_pigfile,pigname,sizeof(Current_pigfile))==0)
664                 return;         //already have correct pig
665
666         if (!Pigfile_initialized) {                     //have we ever opened a pigfile?
667                 piggy_init_pigfile(pigname);            //..no, so do initialization stuff
668                 return;
669         }
670         else
671                 piggy_close_file();             //close old pig if still open
672
673         Piggy_bitmap_cache_next = 0;            //free up cache
674
675         strncpy(Current_pigfile,pigname,sizeof(Current_pigfile));
676
677         #ifndef MACINTOSH
678                 Piggy_fp = cfopen( pigname, "rb" );
679         #else
680                 sprintf(name, ":Data:%s", pigname);
681                 Piggy_fp = cfopen( name, "rb" );
682
683                 #ifdef SHAREWARE        // if we are in the shareware version, we must have the pig by now.
684                         if (Piggy_fp == NULL)
685                         {
686                                 Error("Cannot load required file <%s>",name);
687                         }
688                 #endif  // end of if def shareware
689         #endif
690
691         #ifndef EDITOR
692         if (!Piggy_fp)
693                 Piggy_fp = copy_pigfile_from_cd(pigname);
694         #endif
695
696         if (Piggy_fp) {                         //make sure pig is valid type file & is up-to-date
697                 int pig_id,pig_version;
698
699                 pig_id = cfile_read_int(Piggy_fp);
700                 pig_version = cfile_read_int(Piggy_fp);
701                 if (pig_id != PIGFILE_ID || pig_version != PIGFILE_VERSION) {
702                         cfclose(Piggy_fp);              //out of date pig
703                         Piggy_fp = NULL;                        //..so pretend it's not here
704                 }
705         }
706
707         #ifndef EDITOR
708         if (!Piggy_fp) Error ("Piggy_fp not defined in piggy_new_pigfile.");
709         #endif
710
711         if (Piggy_fp) {
712
713                 N_bitmaps = cfile_read_int(Piggy_fp);
714         
715                 header_size = N_bitmaps*DISKBITMAPHEADER_SIZE;
716         
717                 data_start = header_size + cftell(Piggy_fp);
718
719                 data_size = cfilelength(Piggy_fp) - data_start;
720         
721                 for (i=1; i<=N_bitmaps; i++ )   {
722                         DiskBitmapHeader_read(&bmh, Piggy_fp);
723                         memcpy( temp_name_read, bmh.name, 8 );
724                         temp_name_read[8] = 0;
725         
726                         if ( bmh.dflags & DBM_FLAG_ABM )        
727                                 sprintf( temp_name, "%s#%d", temp_name_read, bmh.dflags & 63 );
728                         else
729                                 strcpy( temp_name, temp_name_read );
730         
731                         //Make sure name matches
732                         if (strcmp(temp_name,AllBitmaps[i].name)) {
733                                 //Int3();       //this pig is out of date.  Delete it
734                                 must_rewrite_pig=1;
735                         }
736         
737                         strcpy(AllBitmaps[i].name,temp_name);
738
739                         memset( &temp_bitmap, 0, sizeof(grs_bitmap) );
740         
741                         temp_bitmap.bm_w = temp_bitmap.bm_rowsize = bmh.width + ((short) (bmh.wh_extra&0x0f)<<8);
742                         temp_bitmap.bm_h = bmh.height + ((short) (bmh.wh_extra&0xf0)<<4);
743                         temp_bitmap.bm_flags = BM_FLAG_PAGED_OUT;
744                         temp_bitmap.avg_color = bmh.avg_color;
745                         temp_bitmap.bm_data = Piggy_bitmap_cache_data;
746
747                         GameBitmapFlags[i] = 0;
748
749                         if ( bmh.flags & BM_FLAG_TRANSPARENT ) GameBitmapFlags[i] |= BM_FLAG_TRANSPARENT;
750                         if ( bmh.flags & BM_FLAG_SUPER_TRANSPARENT ) GameBitmapFlags[i] |= BM_FLAG_SUPER_TRANSPARENT;
751                         if ( bmh.flags & BM_FLAG_NO_LIGHTING ) GameBitmapFlags[i] |= BM_FLAG_NO_LIGHTING;
752                         if ( bmh.flags & BM_FLAG_RLE ) GameBitmapFlags[i] |= BM_FLAG_RLE;
753                         if ( bmh.flags & BM_FLAG_RLE_BIG ) GameBitmapFlags[i] |= BM_FLAG_RLE_BIG;
754         
755                         GameBitmapOffset[i] = bmh.offset + data_start;
756         
757                         GameBitmaps[i] = temp_bitmap;
758                 }
759         }
760         else
761                 N_bitmaps = 0;          //no pigfile, so no bitmaps
762
763         #ifndef EDITOR
764
765         Assert(N_bitmaps == Num_bitmap_files-1);
766
767         #else
768
769         if (must_rewrite_pig || (N_bitmaps < Num_bitmap_files-1)) {
770                 int size;
771
772                 //re-read the bitmaps that aren't in this pig
773
774                 for (i=N_bitmaps+1;i<Num_bitmap_files;i++) {
775                         char *p;
776
777                         p = strchr(AllBitmaps[i].name,'#');
778
779                         if (p) {                //this is an ABM
780                                 char abmname[FILENAME_LEN];
781                                 int fnum;
782                                 grs_bitmap * bm[MAX_BITMAPS_PER_BRUSH];
783                                 int iff_error;          //reference parm to avoid warning message
784                                 ubyte newpal[768];
785                                 char basename[FILENAME_LEN];
786                                 int nframes;
787                         
788                                 strcpy(basename,AllBitmaps[i].name);
789                                 basename[p-AllBitmaps[i].name] = 0;             //cut off "#nn" part
790                                 
791                                 sprintf( abmname, "%s.abm", basename );
792
793                                 iff_error = iff_read_animbrush(abmname,bm,MAX_BITMAPS_PER_BRUSH,&nframes,newpal);
794
795                                 if (iff_error != IFF_NO_ERROR)  {
796                                         mprintf((1,"File %s - IFF error: %s",abmname,iff_errormsg(iff_error)));
797                                         Error("File %s - IFF error: %s",abmname,iff_errormsg(iff_error));
798                                 }
799                         
800                                 for (fnum=0;fnum<nframes; fnum++)       {
801                                         char tempname[20];
802                                         int SuperX;
803
804                                         sprintf( tempname, "%s#%d", basename, fnum );
805
806                                         //SuperX = (GameBitmaps[i+fnum].bm_flags&BM_FLAG_SUPER_TRANSPARENT)?254:-1;
807                                         SuperX = (GameBitmapFlags[i+fnum]&BM_FLAG_SUPER_TRANSPARENT)?254:-1;
808                                         //above makes assumption that supertransparent color is 254
809
810                                         if ( iff_has_transparency )
811                                                 gr_remap_bitmap_good( bm[fnum], newpal, iff_transparent_color, SuperX );
812                                         else
813                                                 gr_remap_bitmap_good( bm[fnum], newpal, -1, SuperX );
814
815                                         bm[fnum]->avg_color = compute_average_pixel(bm[fnum]);
816
817 #ifdef EDITOR
818                                         if ( FindArg("-macdata") )
819                                                 swap_0_255( bm[fnum] );
820 #endif
821                                         if ( !BigPig ) gr_bitmap_rle_compress( bm[fnum] );
822
823                                         if (bm[fnum]->bm_flags & BM_FLAG_RLE)
824                                                 size = *((int *) bm[fnum]->bm_data);
825                                         else
826                                                 size = bm[fnum]->bm_w * bm[fnum]->bm_h;
827
828                                         memcpy( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next],bm[fnum]->bm_data,size);
829                                         d_free(bm[fnum]->bm_data);
830                                         bm[fnum]->bm_data = &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next];
831                                         Piggy_bitmap_cache_next += size;
832
833                                         GameBitmaps[i+fnum] = *bm[fnum];
834
835                                         // -- mprintf( (0, "U" ));
836                                         d_free( bm[fnum] );
837                                 }
838
839                                 i += nframes-1;         //filled in multiple bitmaps
840                         }
841                         else {          //this is a BBM
842
843                                 grs_bitmap * new;
844                                 ubyte newpal[256*3];
845                                 int iff_error;
846                                 char bbmname[FILENAME_LEN];
847                                 int SuperX;
848
849                                 MALLOC( new, grs_bitmap, 1 );
850
851                                 sprintf( bbmname, "%s.bbm", AllBitmaps[i].name );
852                                 iff_error = iff_read_bitmap(bbmname,new,BM_LINEAR,newpal);
853
854                                 new->bm_handle=0;
855                                 if (iff_error != IFF_NO_ERROR)          {
856                                         mprintf((1, "File %s - IFF error: %s",bbmname,iff_errormsg(iff_error)));
857                                         Error("File %s - IFF error: %s",bbmname,iff_errormsg(iff_error));
858                                 }
859
860                                 SuperX = (GameBitmapFlags[i]&BM_FLAG_SUPER_TRANSPARENT)?254:-1;
861                                 //above makes assumption that supertransparent color is 254
862
863                                 if ( iff_has_transparency )
864                                         gr_remap_bitmap_good( new, newpal, iff_transparent_color, SuperX );
865                                 else
866                                         gr_remap_bitmap_good( new, newpal, -1, SuperX );
867
868                                 new->avg_color = compute_average_pixel(new);
869
870 #ifdef EDITOR
871                                 if ( FindArg("-macdata") )
872                                         swap_0_255( new );
873 #endif
874                                 if ( !BigPig )  gr_bitmap_rle_compress( new );
875
876                                 if (new->bm_flags & BM_FLAG_RLE)
877                                         size = *((int *) new->bm_data);
878                                 else
879                                         size = new->bm_w * new->bm_h;
880
881                                 memcpy( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next],new->bm_data,size);
882                                 d_free(new->bm_data);
883                                 new->bm_data = &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next];
884                                 Piggy_bitmap_cache_next += size;
885
886                                 GameBitmaps[i] = *new;
887         
888                                 d_free( new );
889
890                                 // -- mprintf( (0, "U" ));
891                         }
892                 }
893
894                 //@@Dont' do these things which are done when writing
895                 //@@for (i=0; i < Num_bitmap_files; i++ )       {
896                 //@@    bitmap_index bi;
897                 //@@    bi.index = i;
898                 //@@    PIGGY_PAGE_IN( bi );
899                 //@@}
900                 //@@
901                 //@@piggy_close_file();
902
903                 piggy_write_pigfile(pigname);
904
905                 Current_pigfile[0] = 0;                 //say no pig, to force reload
906
907                 piggy_new_pigfile(pigname);             //read in just-generated pig
908
909
910         }
911         #endif  //ifdef EDITOR
912
913 }
914
915 ubyte bogus_data[64*64];
916 grs_bitmap bogus_bitmap;
917 ubyte bogus_bitmap_initialized=0;
918 digi_sound bogus_sound;
919
920 #define HAMFILE_ID              MAKE_SIG('!','M','A','H') //HAM!
921 #define HAMFILE_VERSION 3
922 //version 1 -> 2:  save marker_model_num
923 //version 2 -> 3:  removed sound files
924
925 #define SNDFILE_ID              MAKE_SIG('D','N','S','D') //DSND
926 #define SNDFILE_VERSION 1
927
928 int read_hamfile()
929 {
930         CFILE * ham_fp = NULL;
931         int ham_id;
932         int sound_offset = 0;
933         #ifdef MACINTOSH
934         char name[255];
935         #endif
936
937         #ifndef MACINTOSH
938         ham_fp = cfopen( DEFAULT_HAMFILE, "rb" );
939         #else
940         sprintf(name, ":Data:%s", DEFAULT_HAMFILE );
941         ham_fp = cfopen( name, "rb" );
942         #endif
943
944         if (ham_fp == NULL) {
945                 Must_write_hamfile = 1;
946                 return 0;
947         }
948
949         //make sure ham is valid type file & is up-to-date
950         ham_id = cfile_read_int(ham_fp);
951         Piggy_hamfile_version = cfile_read_int(ham_fp);
952         if (ham_id != HAMFILE_ID)
953                 Error("Cannot open ham file %s\n", DEFAULT_HAMFILE);
954 #if 0
955         if (ham_id != HAMFILE_ID || Piggy_hamfile_version != HAMFILE_VERSION) {
956                 Must_write_hamfile = 1;
957                 cfclose(ham_fp);                                                //out of date ham
958                 return 0;
959         }
960 #endif
961
962         if (Piggy_hamfile_version < 3) // hamfile contains sound info
963                 sound_offset = cfile_read_int(ham_fp);
964
965         #ifndef EDITOR
966         {
967                 //int i;
968
969                 bm_read_all(ham_fp);
970                 cfread( GameBitmapXlat, sizeof(ushort)*MAX_BITMAP_FILES, 1, ham_fp );
971                 // no swap here?
972                 //for (i = 0; i < MAX_BITMAP_FILES; i++) {
973                         //GameBitmapXlat[i] = INTEL_SHORT(GameBitmapXlat[i]);
974                         //printf("GameBitmapXlat[%d] = %d\n", i, GameBitmapXlat[i]);
975                 //}
976         }
977         #endif
978
979         if (Piggy_hamfile_version < 3) {
980                 int N_sounds;
981                 int sound_start;
982                 int header_size;
983                 int i;
984                 DiskSoundHeader sndh;
985                 digi_sound temp_sound;
986                 char temp_name_read[16];
987                 int sbytes = 0;
988
989                 cfseek(ham_fp, sound_offset, SEEK_SET);
990                 N_sounds = cfile_read_int(ham_fp);
991
992                 sound_start = cftell(ham_fp);
993
994                 header_size = N_sounds * DISKSOUNDHEADER_SIZE;
995
996                 //Read sounds
997
998                 for (i=0; i<N_sounds; i++ ) {
999                         DiskSoundHeader_read(&sndh, ham_fp);
1000                         temp_sound.length = sndh.length;
1001                         temp_sound.data = (ubyte *)(sndh.offset + header_size + sound_start);
1002                         SoundOffset[Num_sound_files] = sndh.offset + header_size + sound_start;
1003                         memcpy( temp_name_read, sndh.name, 8 );
1004                         temp_name_read[8] = 0;
1005                         piggy_register_sound( &temp_sound, temp_name_read, 1 );
1006 #ifdef MACINTOSH
1007                         if (piggy_is_needed(i))
1008 #endif          // note link to if.
1009                                 sbytes += sndh.length;
1010                         //mprintf(( 0, "%d bytes of sound\n", sbytes ));
1011                 }
1012
1013                 SoundBits = d_malloc( sbytes + 16 );
1014                 if ( SoundBits == NULL )
1015                         Error( "Not enough memory to load sounds\n" );
1016
1017                 mprintf(( 0, "\nBitmaps: %d KB   Sounds: %d KB\n", Piggy_bitmap_cache_size/1024, sbytes/1024 ));
1018
1019                 //      piggy_read_sounds(ham_fp);
1020
1021         }
1022
1023         cfclose(ham_fp);
1024
1025         return 1;
1026
1027 }
1028
1029 int read_sndfile()
1030 {
1031         CFILE * snd_fp = NULL;
1032         int snd_id,snd_version;
1033         int N_sounds;
1034         int sound_start;
1035         int header_size;
1036         int i,size, length;
1037         DiskSoundHeader sndh;
1038         digi_sound temp_sound;
1039         char temp_name_read[16];
1040         int sbytes = 0;
1041         #ifdef MACINTOSH
1042         char name[255];
1043         #endif
1044
1045         #ifndef MACINTOSH
1046         snd_fp = cfopen( DEFAULT_SNDFILE, "rb" );
1047         #else
1048         sprintf( name, ":Data:%s", DEFAULT_SNDFILE );
1049         snd_fp = cfopen( name, "rb");
1050         #endif
1051         
1052         if (snd_fp == NULL)
1053                 return 0;
1054
1055         //make sure soundfile is valid type file & is up-to-date
1056         snd_id = cfile_read_int(snd_fp);
1057         snd_version = cfile_read_int(snd_fp);
1058         if (snd_id != SNDFILE_ID || snd_version != SNDFILE_VERSION) {
1059                 cfclose(snd_fp);                                                //out of date sound file
1060                 return 0;
1061         }
1062
1063         N_sounds = cfile_read_int(snd_fp);
1064
1065         sound_start = cftell(snd_fp);
1066         size = cfilelength(snd_fp) - sound_start;
1067         length = size;
1068         mprintf( (0, "\nReading data (%d KB) ", size/1024 ));
1069
1070         header_size = N_sounds*sizeof(DiskSoundHeader);
1071
1072         //Read sounds
1073
1074         for (i=0; i<N_sounds; i++ ) {
1075                 DiskSoundHeader_read(&sndh, snd_fp);
1076                 //size -= sizeof(DiskSoundHeader);
1077                 temp_sound.length = sndh.length;
1078                 temp_sound.data = (ubyte *)(sndh.offset + header_size + sound_start);
1079                 SoundOffset[Num_sound_files] = sndh.offset + header_size + sound_start;
1080                 memcpy( temp_name_read, sndh.name, 8 );
1081                 temp_name_read[8] = 0;
1082                 piggy_register_sound( &temp_sound, temp_name_read, 1 );
1083                 #ifdef MACINTOSH
1084                 if (piggy_is_needed(i))
1085                 #endif          // note link to if.
1086                 sbytes += sndh.length;
1087                 //mprintf(( 0, "%d bytes of sound\n", sbytes ));
1088         }
1089
1090         SoundBits = d_malloc( sbytes + 16 );
1091         if ( SoundBits == NULL )
1092                 Error( "Not enough memory to load sounds\n" );
1093
1094         mprintf(( 0, "\nBitmaps: %d KB   Sounds: %d KB\n", Piggy_bitmap_cache_size/1024, sbytes/1024 ));
1095
1096 //      piggy_read_sounds(snd_fp);
1097
1098         cfclose(snd_fp);
1099
1100         return 1;
1101 }
1102
1103 int piggy_init(void)
1104 {
1105         int ham_ok=0,snd_ok=0;
1106         int i;
1107
1108         hashtable_init( &AllBitmapsNames, MAX_BITMAP_FILES );
1109         hashtable_init( &AllDigiSndNames, MAX_SOUND_FILES );
1110
1111         for (i=0; i<MAX_SOUND_FILES; i++ )      {
1112                 GameSounds[i].length = 0;
1113                 GameSounds[i].data = NULL;
1114                 SoundOffset[i] = 0;
1115         }
1116
1117         for (i=0; i<MAX_BITMAP_FILES; i++ )     
1118                 GameBitmapXlat[i] = i;
1119
1120         if ( !bogus_bitmap_initialized )        {
1121                 int i;
1122                 ubyte c;
1123                 bogus_bitmap_initialized = 1;
1124                 memset( &bogus_bitmap, 0, sizeof(grs_bitmap) );
1125                 bogus_bitmap.bm_w = bogus_bitmap.bm_h = bogus_bitmap.bm_rowsize = 64;
1126                 bogus_bitmap.bm_data = bogus_data;
1127                 c = gr_find_closest_color( 0, 0, 63 );
1128                 for (i=0; i<4096; i++ ) bogus_data[i] = c;
1129                 c = gr_find_closest_color( 63, 0, 0 );
1130                 // Make a big red X !
1131                 for (i=0; i<64; i++ )   {
1132                         bogus_data[i*64+i] = c;
1133                         bogus_data[i*64+(63-i)] = c;
1134                 }
1135                 piggy_register_bitmap( &bogus_bitmap, "bogus", 1 );
1136                 bogus_sound.length = 64*64;
1137                 bogus_sound.data = bogus_data;
1138                 GameBitmapOffset[0] = 0;
1139         }
1140
1141         if ( FindArg( "-bigpig" ))
1142                 BigPig = 1;
1143
1144         if ( FindArg( "-lowmem" ))
1145                 piggy_low_memory = 1;
1146
1147         if ( FindArg( "-nolowmem" ))
1148                 piggy_low_memory = 0;
1149
1150         if (piggy_low_memory)
1151                 digi_lomem = 1;
1152
1153         WIN(DDGRLOCK(dd_grd_curcanv));
1154                 gr_set_curfont( SMALL_FONT );
1155                 gr_set_fontcolor(gr_find_closest_color_current( 20, 20, 20 ),-1 );
1156                 gr_printf( 0x8000, grd_curcanv->cv_h-20, "%s...", TXT_LOADING_DATA );
1157         WIN(DDGRUNLOCK(dd_grd_curcanv));
1158
1159         #ifdef EDITOR
1160         piggy_init_pigfile(DEFAULT_PIGFILE);
1161         #endif
1162
1163         snd_ok = ham_ok = read_hamfile();
1164
1165         if (Piggy_hamfile_version >= 3)
1166                 snd_ok = read_sndfile();
1167
1168         atexit(piggy_close);
1169
1170         mprintf ((0,"HamOk=%d SndOk=%d\n",ham_ok,snd_ok));
1171         return (ham_ok && snd_ok);               //read ok
1172 }
1173
1174 int piggy_is_needed(int soundnum)
1175 {
1176         int i;
1177
1178         if ( !digi_lomem ) return 1;
1179
1180         for (i=0; i<MAX_SOUNDS; i++ )   {
1181                 if ( (AltSounds[i] < 255) && (Sounds[AltSounds[i]] == soundnum) )
1182                         return 1;
1183         }
1184         return 0;
1185 }
1186
1187
1188 void piggy_read_sounds(void)
1189 {
1190         CFILE * fp = NULL;
1191         ubyte * ptr;
1192         int i, sbytes;
1193         #ifdef MACINTOSH
1194         char name[255];
1195         #endif
1196
1197         ptr = SoundBits;
1198         sbytes = 0;
1199
1200         #ifndef MACINTOSH
1201         fp = cfopen( DEFAULT_SNDFILE, "rb" );
1202         #else
1203         sprintf( name, ":Data:%s", DEFAULT_SNDFILE );
1204         fp = cfopen( name, "rb");
1205         #endif
1206
1207         if (fp == NULL)
1208                 return;
1209
1210         for (i=0; i<Num_sound_files; i++ )      {
1211                 digi_sound *snd = &GameSounds[i];
1212
1213                 if ( SoundOffset[i] > 0 )       {
1214                         if ( piggy_is_needed(i) )       {
1215                                 cfseek( fp, SoundOffset[i], SEEK_SET );
1216
1217                                 // Read in the sound data!!!
1218                                 snd->data = ptr;
1219                                 ptr += snd->length;
1220                                 sbytes += snd->length;
1221                                 cfread( snd->data, snd->length, 1, fp );
1222                         }
1223                         else
1224                                 snd->data = (ubyte *) -1;
1225                 }
1226         }
1227
1228         cfclose(fp);
1229
1230         mprintf(( 0, "\nActual Sound usage: %d KB\n", sbytes/1024 ));
1231
1232 }
1233
1234
1235 extern int descent_critical_error;
1236 extern unsigned descent_critical_deverror;
1237 extern unsigned descent_critical_errcode;
1238
1239 char * crit_errors[13] = { "Write Protected", "Unknown Unit", "Drive Not Ready", "Unknown Command", "CRC Error", \
1240 "Bad struct length", "Seek Error", "Unknown media type", "Sector not found", "Printer out of paper", "Write Fault", \
1241 "Read fault", "General Failure" };
1242
1243 void piggy_critical_error()
1244 {
1245         grs_canvas * save_canv;
1246         grs_font * save_font;
1247         int i;
1248         save_canv = grd_curcanv;
1249         save_font = grd_curcanv->cv_font;
1250         gr_palette_load( gr_palette );
1251         i = nm_messagebox( "Disk Error", 2, "Retry", "Exit", "%s\non drive %c:", crit_errors[descent_critical_errcode&0xf], (descent_critical_deverror&0xf)+'A'  );
1252         if ( i == 1 )
1253                 exit(1);
1254         gr_set_current_canvas(save_canv);
1255         grd_curcanv->cv_font = save_font;
1256 }
1257
1258 void piggy_bitmap_page_in( bitmap_index bitmap )
1259 {
1260         grs_bitmap * bmp;
1261         int i,org_i,temp;
1262
1263         org_i = 0;
1264
1265         i = bitmap.index;
1266         Assert( i >= 0 );
1267         Assert( i < MAX_BITMAP_FILES );
1268         Assert( i < Num_bitmap_files );
1269         Assert( Piggy_bitmap_cache_size > 0 );
1270
1271         if ( i < 1 ) return;
1272         if ( i >= MAX_BITMAP_FILES ) return;
1273         if ( i >= Num_bitmap_files ) return;
1274
1275         if ( GameBitmapOffset[i] == 0 ) return;         // A read-from-disk bitmap!!!
1276
1277         if ( piggy_low_memory ) {
1278                 org_i = i;
1279                 i = GameBitmapXlat[i];          // Xlat for low-memory settings!
1280         }
1281
1282         bmp = &GameBitmaps[i];
1283
1284         if ( bmp->bm_flags & BM_FLAG_PAGED_OUT )        {
1285                 stop_time();
1286
1287         ReDoIt:
1288                 descent_critical_error = 0;
1289                 cfseek( Piggy_fp, GameBitmapOffset[i], SEEK_SET );
1290                 if ( descent_critical_error )   {
1291                         piggy_critical_error();
1292                         goto ReDoIt;
1293                 }
1294
1295                 bmp->bm_data = &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next];
1296                 bmp->bm_flags = GameBitmapFlags[i];
1297
1298                 if ( bmp->bm_flags & BM_FLAG_RLE )      {
1299                         int zsize = 0;
1300                         descent_critical_error = 0;
1301                         zsize = cfile_read_int(Piggy_fp);
1302                         if ( descent_critical_error )   {
1303                                 piggy_critical_error();
1304                                 goto ReDoIt;
1305                         }
1306
1307                         // GET JOHN NOW IF YOU GET THIS ASSERT!!!
1308                         //Assert( Piggy_bitmap_cache_next+zsize < Piggy_bitmap_cache_size );
1309                         if ( Piggy_bitmap_cache_next+zsize >= Piggy_bitmap_cache_size ) {
1310                                 Int3();
1311                                 piggy_bitmap_page_out_all();
1312                                 goto ReDoIt;
1313                         }
1314                         descent_critical_error = 0;
1315                         temp = cfread( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next+4], 1, zsize-4, Piggy_fp );
1316                         if ( descent_critical_error )   {
1317                                 piggy_critical_error();
1318                                 goto ReDoIt;
1319                         }
1320
1321 #ifndef MACDATA
1322                         if (FindArg("-macdata") || cfilelength(Piggy_fp) == MAC_D2DEMO_PIG_SIZE) {
1323                                 rle_swap_0_255( bmp );
1324                                 memcpy(&zsize, bmp->bm_data, 4);
1325                         }
1326 #endif
1327
1328                         memcpy( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], &zsize, sizeof(int) );
1329                         Piggy_bitmap_cache_next += zsize;
1330                         if ( Piggy_bitmap_cache_next+zsize >= Piggy_bitmap_cache_size ) {
1331                                 Int3();
1332                                 piggy_bitmap_page_out_all();
1333                                 goto ReDoIt;
1334                         }
1335
1336                 } else {
1337                         // GET JOHN NOW IF YOU GET THIS ASSERT!!!
1338                         Assert( Piggy_bitmap_cache_next+(bmp->bm_h*bmp->bm_w) < Piggy_bitmap_cache_size );
1339                         if ( Piggy_bitmap_cache_next+(bmp->bm_h*bmp->bm_w) >= Piggy_bitmap_cache_size ) {
1340                                 piggy_bitmap_page_out_all();
1341                                 goto ReDoIt;
1342                         }
1343                         descent_critical_error = 0;
1344                         temp = cfread( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], 1, bmp->bm_h*bmp->bm_w, Piggy_fp );
1345                         if ( descent_critical_error )   {
1346                                 piggy_critical_error();
1347                                 goto ReDoIt;
1348                         }
1349                         Piggy_bitmap_cache_next+=bmp->bm_h*bmp->bm_w;
1350
1351 #ifndef MACDATA
1352                         if (FindArg("-macdata") || cfilelength(Piggy_fp) == MAC_D2DEMO_PIG_SIZE)
1353                                 swap_0_255( bmp );
1354 #endif
1355                 }
1356
1357
1358
1359                 //@@if ( bmp->bm_selector ) {
1360                 //@@#if !defined(WINDOWS) && !defined(MACINTOSH)
1361                 //@@    if (!dpmi_modify_selector_base( bmp->bm_selector, bmp->bm_data ))
1362                 //@@            Error( "Error modifying selector base in piggy.c\n" );
1363                 //@@#endif
1364                 //@@}
1365
1366                 start_time();
1367         }
1368
1369         if ( piggy_low_memory ) {
1370                 if ( org_i != i )
1371                         GameBitmaps[org_i] = GameBitmaps[i];
1372         }
1373
1374 //@@Removed from John's code:
1375 //@@#ifndef WINDOWS
1376 //@@    if ( bmp->bm_selector ) {
1377 //@@            if (!dpmi_modify_selector_base( bmp->bm_selector, bmp->bm_data ))
1378 //@@                    Error( "Error modifying selector base in piggy.c\n" );
1379 //@@    }
1380 //@@#endif
1381
1382 }
1383
1384 void piggy_bitmap_page_out_all()
1385 {
1386         int i;
1387         
1388         Piggy_bitmap_cache_next = 0;
1389
1390         piggy_page_flushed++;
1391
1392         texmerge_flush();
1393         rle_cache_flush();
1394
1395         for (i=0; i<Num_bitmap_files; i++ )             {
1396                 if ( GameBitmapOffset[i] > 0 )  {       // Don't page out bitmaps read from disk!!!
1397                         GameBitmaps[i].bm_flags = BM_FLAG_PAGED_OUT;
1398                         GameBitmaps[i].bm_data = Piggy_bitmap_cache_data;
1399                 }
1400         }
1401
1402         mprintf(( 0, "Flushing piggy bitmap cache\n" ));
1403 }
1404
1405 void piggy_load_level_data()
1406 {
1407         piggy_bitmap_page_out_all();
1408         paging_touch_all();
1409 }
1410
1411 #ifdef EDITOR
1412
1413 void change_filename_ext( char *dest, char *src, char *ext );
1414
1415 void piggy_write_pigfile(char *filename)
1416 {
1417         FILE *pig_fp;
1418         int bitmap_data_start,data_offset;
1419         DiskBitmapHeader bmh;
1420         int org_offset;
1421         char subst_name[32];
1422         int i;
1423         FILE *fp1,*fp2;
1424         char tname[FILENAME_LEN];
1425
1426         // -- mprintf( (0, "Paging in all piggy bitmaps..." ));
1427         for (i=0; i < Num_bitmap_files; i++ )   {
1428                 bitmap_index bi;
1429                 bi.index = i;
1430                 PIGGY_PAGE_IN( bi );
1431         }
1432         // -- mprintf( (0, "\n" ));
1433
1434         piggy_close_file();
1435
1436         // -- mprintf( (0, "Creating %s...",filename ));
1437
1438         pig_fp = fopen( filename, "wb" );       //open PIG file
1439         Assert( pig_fp!=NULL );
1440
1441         write_int(PIGFILE_ID,pig_fp);
1442         write_int(PIGFILE_VERSION,pig_fp);
1443
1444         Num_bitmap_files--;
1445         fwrite( &Num_bitmap_files, sizeof(int), 1, pig_fp );
1446         Num_bitmap_files++;
1447
1448         bitmap_data_start = ftell(pig_fp);
1449         bitmap_data_start += (Num_bitmap_files-1)*DISKBITMAPHEADER_SIZE; 
1450         data_offset = bitmap_data_start;
1451
1452         change_filename_ext(tname,filename,"lst");
1453         fp1 = fopen( tname, "wt" );
1454         change_filename_ext(tname,filename,"all");
1455         fp2 = fopen( tname, "wt" );
1456
1457         for (i=1; i < Num_bitmap_files; i++ )   {
1458                 int *size;
1459                 grs_bitmap *bmp;
1460
1461                 {               
1462                         char * p, *p1;
1463                         p = strchr(AllBitmaps[i].name,'#');
1464                         if (p)  {
1465                                 int n;
1466                                 p1 = p; p1++; 
1467                                 n = atoi(p1);
1468                                 *p = 0;
1469                                 if (fp2 && n==0)
1470                                         fprintf( fp2, "%s.abm\n", AllBitmaps[i].name );
1471                                 memcpy( bmh.name, AllBitmaps[i].name, 8 );
1472                                 Assert( n <= 63 );
1473                                 bmh.dflags = DBM_FLAG_ABM + n;
1474                                 *p = '#';
1475                         }else {
1476                                 if (fp2)
1477                                         fprintf( fp2, "%s.bbm\n", AllBitmaps[i].name );
1478                                 memcpy( bmh.name, AllBitmaps[i].name, 8 );
1479                                 bmh.dflags = 0;
1480                         }
1481                 }
1482                 bmp = &GameBitmaps[i];
1483
1484                 Assert( !(bmp->bm_flags&BM_FLAG_PAGED_OUT) );
1485
1486                 if (fp1)
1487                         fprintf( fp1, "BMP: %s, size %d bytes", AllBitmaps[i].name, bmp->bm_rowsize * bmp->bm_h );
1488                 org_offset = ftell(pig_fp);
1489                 bmh.offset = data_offset - bitmap_data_start;
1490                 fseek( pig_fp, data_offset, SEEK_SET );
1491
1492                 if ( bmp->bm_flags & BM_FLAG_RLE )      {
1493                         size = (int *)bmp->bm_data;
1494                         fwrite( bmp->bm_data, sizeof(ubyte), *size, pig_fp );
1495                         data_offset += *size;
1496                         if (fp1)
1497                                 fprintf( fp1, ", and is already compressed to %d bytes.\n", *size );
1498                 } else {
1499                         fwrite( bmp->bm_data, sizeof(ubyte), bmp->bm_rowsize * bmp->bm_h, pig_fp );
1500                         data_offset += bmp->bm_rowsize * bmp->bm_h;
1501                         if (fp1)
1502                                 fprintf( fp1, ".\n" );
1503                 }
1504                 fseek( pig_fp, org_offset, SEEK_SET );
1505                 Assert( GameBitmaps[i].bm_w < 4096 );
1506                 bmh.width = (GameBitmaps[i].bm_w & 0xff);
1507                 bmh.wh_extra = ((GameBitmaps[i].bm_w >> 8) & 0x0f);
1508                 Assert( GameBitmaps[i].bm_h < 4096 );
1509                 bmh.height = GameBitmaps[i].bm_h;
1510                 bmh.wh_extra |= ((GameBitmaps[i].bm_h >> 4) & 0xf0);
1511                 bmh.flags = GameBitmaps[i].bm_flags;
1512                 if (piggy_is_substitutable_bitmap( AllBitmaps[i].name, subst_name ))    {
1513                         bitmap_index other_bitmap;
1514                         other_bitmap = piggy_find_bitmap( subst_name );
1515                         GameBitmapXlat[i] = other_bitmap.index;
1516                         bmh.flags |= BM_FLAG_PAGED_OUT;
1517                         //mprintf(( 0, "Skipping bitmap %d\n", i ));
1518                         //mprintf(( 0, "Marking '%s' as substitutible\n", AllBitmaps[i].name ));
1519                 } else  {
1520                         bmh.flags &= ~BM_FLAG_PAGED_OUT;
1521                 }
1522                 bmh.avg_color=GameBitmaps[i].avg_color;
1523                 fwrite( &bmh, DISKBITMAPHEADER_SIZE, 1, pig_fp );                    // Mark as a bitmap
1524         }
1525
1526         fclose(pig_fp);
1527
1528         mprintf( (0, " Dumped %d assorted bitmaps.\n", Num_bitmap_files ));
1529         fprintf( fp1, " Dumped %d assorted bitmaps.\n", Num_bitmap_files );
1530
1531         fclose(fp1);
1532         fclose(fp2);
1533
1534 }
1535
1536 static void write_int(int i,FILE *file)
1537 {
1538         if (fwrite( &i, sizeof(i), 1, file) != 1)
1539                 Error( "Error reading int in gamesave.c" );
1540
1541 }
1542
1543 void piggy_dump_all()
1544 {
1545         int i, xlat_offset;
1546         FILE * ham_fp;
1547         int org_offset,data_offset=0;
1548         DiskSoundHeader sndh;
1549         int sound_data_start=0;
1550         FILE *fp1,*fp2;
1551
1552         #ifdef NO_DUMP_SOUNDS
1553         Num_sound_files = 0;
1554         Num_sound_files_new = 0;
1555         #endif
1556
1557         if (!Must_write_hamfile && (Num_bitmap_files_new == 0) && (Num_sound_files_new == 0) )
1558                 return;
1559
1560         fp1 = fopen( "ham.lst", "wt" );
1561         fp2 = fopen( "ham.all", "wt" );
1562
1563         if (Must_write_hamfile || Num_bitmap_files_new) {
1564
1565                 mprintf( (0, "Creating %s...",DEFAULT_HAMFILE));
1566         
1567                 ham_fp = fopen( DEFAULT_HAMFILE, "wb" );                       //open HAM file
1568                 Assert( ham_fp!=NULL );
1569         
1570                 write_int(HAMFILE_ID,ham_fp);
1571                 write_int(HAMFILE_VERSION,ham_fp);
1572         
1573                 bm_write_all(ham_fp);
1574                 xlat_offset = ftell(ham_fp);
1575                 fwrite( GameBitmapXlat, sizeof(ushort)*MAX_BITMAP_FILES, 1, ham_fp );
1576                 //Dump bitmaps
1577         
1578                 if (Num_bitmap_files_new)
1579                         piggy_write_pigfile(DEFAULT_PIGFILE);
1580         
1581                 //free up memeory used by new bitmaps
1582                 for (i=Num_bitmap_files-Num_bitmap_files_new;i<Num_bitmap_files;i++)
1583                         d_free(GameBitmaps[i].bm_data);
1584         
1585                 //next thing must be done after pig written
1586                 fseek( ham_fp, xlat_offset, SEEK_SET );
1587                 fwrite( GameBitmapXlat, sizeof(ushort)*MAX_BITMAP_FILES, 1, ham_fp );
1588         
1589                 fclose(ham_fp);
1590                 mprintf( (0, "\n" ));
1591         }
1592         
1593         if (Num_sound_files_new) {
1594
1595                 mprintf( (0, "Creating %s...",DEFAULT_HAMFILE));
1596                 // Now dump sound file
1597                 ham_fp = fopen( DEFAULT_SNDFILE, "wb" );
1598                 Assert( ham_fp!=NULL );
1599         
1600                 write_int(SNDFILE_ID,ham_fp);
1601                 write_int(SNDFILE_VERSION,ham_fp);
1602
1603                 fwrite( &Num_sound_files, sizeof(int), 1, ham_fp );
1604         
1605                 mprintf( (0, "\nDumping sounds..." ));
1606         
1607                 sound_data_start = ftell(ham_fp);
1608                 sound_data_start += Num_sound_files*sizeof(DiskSoundHeader);
1609                 data_offset = sound_data_start;
1610         
1611                 for (i=0; i < Num_sound_files; i++ )    {
1612                         digi_sound *snd;
1613         
1614                         snd = &GameSounds[i];
1615                         strcpy( sndh.name, AllSounds[i].name );
1616                         sndh.length = GameSounds[i].length;
1617                         sndh.offset = data_offset - sound_data_start;
1618         
1619                         org_offset = ftell(ham_fp);
1620                         fseek( ham_fp, data_offset, SEEK_SET );
1621         
1622                         sndh.data_length = GameSounds[i].length;
1623                         fwrite( snd->data, sizeof(ubyte), snd->length, ham_fp );
1624                         data_offset += snd->length;
1625                         fseek( ham_fp, org_offset, SEEK_SET );
1626                         fwrite( &sndh, sizeof(DiskSoundHeader), 1, ham_fp );                    // Mark as a bitmap
1627         
1628                         fprintf( fp1, "SND: %s, size %d bytes\n", AllSounds[i].name, snd->length );
1629                         fprintf( fp2, "%s.raw\n", AllSounds[i].name );
1630                 }
1631
1632                 fclose(ham_fp);
1633                 mprintf( (0, "\n" ));
1634         }
1635
1636         fprintf( fp1, "Total sound size: %d bytes\n", data_offset-sound_data_start);
1637         mprintf( (0, " Dumped %d assorted sounds.\n", Num_sound_files ));
1638         fprintf( fp1, " Dumped %d assorted sounds.\n", Num_sound_files );
1639
1640         fclose(fp1);
1641         fclose(fp2);
1642
1643         // Never allow the game to run after building ham.
1644         exit(0);
1645 }
1646
1647 #endif
1648
1649 void piggy_close()
1650 {
1651         piggy_close_file();
1652
1653         if (BitmapBits)
1654                 d_free(BitmapBits);
1655
1656         if ( SoundBits )
1657                 d_free( SoundBits );
1658
1659         hashtable_free( &AllBitmapsNames );
1660         hashtable_free( &AllDigiSndNames );
1661
1662 }
1663
1664 int piggy_does_bitmap_exist_slow( char * name )
1665 {
1666         int i;
1667
1668         for (i=0; i<Num_bitmap_files; i++ )     {
1669                 if ( !strcmp( AllBitmaps[i].name, name) )
1670                         return 1;
1671         }
1672         return 0;
1673 }
1674
1675
1676 #define NUM_GAUGE_BITMAPS 23
1677 char * gauge_bitmap_names[NUM_GAUGE_BITMAPS] = {
1678         "gauge01", "gauge01b",
1679         "gauge02", "gauge02b",
1680         "gauge06", "gauge06b",
1681         "targ01", "targ01b",
1682         "targ02", "targ02b", 
1683         "targ03", "targ03b",
1684         "targ04", "targ04b",
1685         "targ05", "targ05b",
1686         "targ06", "targ06b",
1687         "gauge18", "gauge18b",
1688         "gauss1", "helix1",
1689         "phoenix1"
1690 };
1691
1692
1693 int piggy_is_gauge_bitmap( char * base_name )
1694 {
1695         int i;
1696         for (i=0; i<NUM_GAUGE_BITMAPS; i++ )    {
1697                 if ( !stricmp( base_name, gauge_bitmap_names[i] ))      
1698                         return 1;
1699         }
1700
1701         return 0;       
1702 }
1703
1704 int piggy_is_substitutable_bitmap( char * name, char * subst_name )
1705 {
1706         int frame;
1707         char * p;
1708         char base_name[ 16 ];
1709         
1710         strcpy( subst_name, name );
1711         p = strchr( subst_name, '#' );
1712         if ( p )        {
1713                 frame = atoi( &p[1] );
1714                 *p = 0;
1715                 strcpy( base_name, subst_name );
1716                 if ( !piggy_is_gauge_bitmap( base_name ))       {
1717                         sprintf( subst_name, "%s#%d", base_name, frame+1 );
1718                         if ( piggy_does_bitmap_exist_slow( subst_name )  )      {
1719                                 if ( frame & 1 ) {
1720                                         sprintf( subst_name, "%s#%d", base_name, frame-1 );
1721                                         return 1;
1722                                 }
1723                         }
1724                 }
1725         }
1726         strcpy( subst_name, name );
1727         return 0;
1728 }
1729
1730
1731
1732 #ifdef WINDOWS
1733 //      New Windows stuff
1734
1735 //      windows bitmap page in
1736 //              Page in a bitmap, if ddraw, then page it into a ddsurface in 
1737 //              'video' memory.  if that fails, page it in normally.
1738
1739 void piggy_bitmap_page_in_w( bitmap_index bitmap, int ddraw )
1740 {
1741 }
1742
1743
1744 //      Essential when switching video modes!
1745
1746 void piggy_bitmap_page_out_all_w()
1747 {
1748 }
1749
1750 #endif
1751
1752 #ifndef FAST_FILE_IO
1753 /*
1754  * reads a bitmap_index structure from a CFILE
1755  */
1756 void bitmap_index_read(bitmap_index *bi, CFILE *fp)
1757 {
1758         bi->index = cfile_read_short(fp);
1759 }
1760
1761 /*
1762  * reads n bitmap_index structs from a CFILE
1763  */
1764 int bitmap_index_read_n(bitmap_index *bi, int n, CFILE *fp)
1765 {
1766         int i;
1767
1768         for (i = 0; i < n; i++)
1769                 bi[i].index = cfile_read_short(fp);
1770         return i;
1771 }
1772
1773 /*
1774  * reads a DiskBitmapHeader structure from a CFILE
1775  */
1776 void DiskBitmapHeader_read(DiskBitmapHeader *dbh, CFILE *fp)
1777 {
1778         cfread(dbh->name, 8, 1, fp);
1779         dbh->dflags = cfile_read_byte(fp);
1780         dbh->width = cfile_read_byte(fp);
1781         dbh->height = cfile_read_byte(fp);
1782         dbh->wh_extra = cfile_read_byte(fp);
1783         dbh->flags = cfile_read_byte(fp);
1784         dbh->avg_color = cfile_read_byte(fp);
1785         dbh->offset = cfile_read_int(fp);
1786 }
1787
1788 /*
1789  * reads a DiskSoundHeader structure from a CFILE
1790  */
1791 void DiskSoundHeader_read(DiskSoundHeader *dsh, CFILE *fp)
1792 {
1793         cfread(dsh->name, 8, 1, fp);
1794         dsh->length = cfile_read_int(fp);
1795         dsh->data_length = cfile_read_int(fp);
1796         dsh->offset = cfile_read_int(fp);
1797 }
1798 #endif