/* THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ /* * * Functions for managing the pig files. * */ #include #include #include #include #include "pstypes.h" #include "inferno.h" #include "gr.h" #include "u_mem.h" #include "error.h" #include "sounds.h" #include "bm.h" #include "hash.h" #include "args.h" #include "palette.h" #include "gamefont.h" #include "rle.h" #include "screens.h" #ifdef BITMAP_SELECTOR #include "u_dpmi.h" #endif #include "snddecom.h" #include "console.h" #include "piggy.h" #include "texmerge.h" #include "paging.h" #include "game.h" #include "text.h" #include "cfile.h" #include "newmenu.h" #include "custom.h" int piggy_is_substitutable_bitmap( char * name, char * subst_name ); //#define NO_DUMP_SOUNDS 1 //if set, dump bitmaps but not sounds ubyte *BitmapBits = NULL; ubyte *SoundBits = NULL; typedef struct BitmapFile { char name[15]; } BitmapFile; typedef struct SoundFile { char name[15]; } SoundFile; hashtable AllBitmapsNames; hashtable AllDigiSndNames; int Num_bitmap_files = 0; int Num_sound_files = 0; digi_sound GameSounds[MAX_SOUND_FILES]; int SoundOffset[MAX_SOUND_FILES]; grs_bitmap GameBitmaps[MAX_BITMAP_FILES]; int Num_bitmap_files_new = 0; int Num_sound_files_new = 0; static BitmapFile AllBitmaps[ MAX_BITMAP_FILES ]; static SoundFile AllSounds[ MAX_SOUND_FILES ]; #define DBM_FLAG_LARGE 128 // Flags added onto the flags struct in b #define DBM_FLAG_ABM 64 int Piggy_bitmap_cache_size = 0; int Piggy_bitmap_cache_next = 0; ubyte * Piggy_bitmap_cache_data = NULL; /*static*/ int GameBitmapOffset[MAX_BITMAP_FILES]; /*static*/ ubyte GameBitmapFlags[MAX_BITMAP_FILES]; ushort GameBitmapXlat[MAX_BITMAP_FILES]; #define DEFAULT_PIGFILE_REGISTERED "descent.pig" #define PIGGY_BUFFER_SIZE (2048*1024) #define PIGGY_SMALL_BUFFER_SIZE (1400*1024) // size of buffer when GameArg.SysLowMem is set int piggy_page_flushed = 0; typedef struct DiskBitmapHeader { char name[8]; ubyte dflags; ubyte width; ubyte height; ubyte flags; ubyte avg_color; int offset; } __pack__ DiskBitmapHeader; typedef struct DiskSoundHeader { char name[8]; int length; int data_length; int offset; } __pack__ DiskSoundHeader; /* * reads a DiskBitmapHeader structure from a CFILE */ void DiskBitmapHeader_read(DiskBitmapHeader *dbh, CFILE *fp) { cfread(dbh->name, 8, 1, fp); dbh->dflags = cfile_read_byte(fp); dbh->width = cfile_read_byte(fp); dbh->height = cfile_read_byte(fp); dbh->flags = cfile_read_byte(fp); dbh->avg_color = cfile_read_byte(fp); dbh->offset = cfile_read_int(fp); } /* * reads a DiskSoundHeader structure from a CFILE */ void DiskSoundHeader_read(DiskSoundHeader *dsh, CFILE *fp) { cfread(dsh->name, 8, 1, fp); dsh->length = cfile_read_int(fp); dsh->data_length = cfile_read_int(fp); dsh->offset = cfile_read_int(fp); } static int SoundCompressed[ MAX_SOUND_FILES ]; void swap_0_255(grs_bitmap *bmp) { int i; for (i = 0; i < bmp->bm_h * bmp->bm_w; i++) { if(bmp->bm_data[i] == 0) bmp->bm_data[i] = 255; else if (bmp->bm_data[i] == 255) bmp->bm_data[i] = 0; } } void piggy_get_bitmap_name( int i, char * name ) { strncpy( name, AllBitmaps[i].name, 12 ); name[12] = 0; } bitmap_index piggy_register_bitmap( grs_bitmap * bmp, char * name, int in_file ) { bitmap_index temp; Assert( Num_bitmap_files < MAX_BITMAP_FILES ); #ifdef D1XD3D Assert (bmp->iMagic == BM_MAGIC_NUMBER); #endif temp.index = Num_bitmap_files; if (!in_file) { if ( !GameArg.DbgBigPig ) gr_bitmap_rle_compress( bmp ); Num_bitmap_files_new++; } strncpy( AllBitmaps[Num_bitmap_files].name, name, 12 ); hashtable_insert( &AllBitmapsNames, AllBitmaps[Num_bitmap_files].name, Num_bitmap_files ); GameBitmaps[Num_bitmap_files] = *bmp; if ( !in_file ) { GameBitmapOffset[Num_bitmap_files] = 0; GameBitmapFlags[Num_bitmap_files] = bmp->bm_flags; } Num_bitmap_files++; return temp; } int piggy_register_sound( digi_sound * snd, char * name, int in_file ) { int i; Assert( Num_sound_files < MAX_SOUND_FILES ); strncpy( AllSounds[Num_sound_files].name, name, 12 ); hashtable_insert( &AllDigiSndNames, AllSounds[Num_sound_files].name, Num_sound_files ); GameSounds[Num_sound_files] = *snd; //added/moved on 11/13/99 by Victor Rachels to ready for changing freq //#ifdef ALLEGRO GameSounds[Num_sound_files].bits = snd->bits; GameSounds[Num_sound_files].freq = snd->freq; #ifdef ALLEGRO //end this section move - VR GameSounds[Num_sound_files].priority = 128; GameSounds[Num_sound_files].loop_start = 0; GameSounds[Num_sound_files].loop_end = GameSounds[Num_sound_files].len; GameSounds[Num_sound_files].param = -1; #endif if ( !in_file ) { SoundOffset[Num_sound_files] = 0; } i = Num_sound_files; if (!in_file) Num_sound_files_new++; Num_sound_files++; return i; } bitmap_index piggy_find_bitmap( char * name ) { bitmap_index bmp; int i; bmp.index = 0; i = hashtable_search( &AllBitmapsNames, name ); Assert( i != 0 ); if ( i < 0 ) return bmp; bmp.index = i; return bmp; } int piggy_find_sound( char * name ) { int i; i = hashtable_search( &AllDigiSndNames, name ); if ( i < 0 ) return 255; return i; } CFILE * Piggy_fp = NULL; void piggy_close_file() { if ( Piggy_fp ) { cfclose( Piggy_fp ); Piggy_fp = NULL; } } ubyte bogus_data[64*64]; grs_bitmap bogus_bitmap; ubyte bogus_bitmap_initialized=0; digi_sound bogus_sound; int MacPig = 0; // using the Macintosh pigfile? extern void properties_read_cmp(CFILE * fp); #ifdef EDITOR extern void bm_write_all(FILE * fp); #endif int properties_init() { int pcshare = 0; int sbytes = 0; char temp_name_read[16]; char temp_name[16]; grs_bitmap temp_bitmap; digi_sound temp_sound; DiskBitmapHeader bmh; DiskSoundHeader sndh; int header_size, N_bitmaps, N_sounds; int i,size, length; int read_sounds = 1; int Pigdata_start; int pigsize; int retval; hashtable_init( &AllBitmapsNames, MAX_BITMAP_FILES ); hashtable_init( &AllDigiSndNames, MAX_SOUND_FILES ); if (GameArg.SndNoSound) { read_sounds = 0; } for (i=0; i 0 ); #else Piggy_bitmap_cache_size = PIGGY_BUFFER_SIZE; if (GameArg.SysLowMem) Piggy_bitmap_cache_size = PIGGY_SMALL_BUFFER_SIZE; #endif BitmapBits = d_malloc( Piggy_bitmap_cache_size ); if ( BitmapBits == NULL ) Error( "Not enough memory to load DESCENT.PIG bitmaps\n" ); Piggy_bitmap_cache_data = BitmapBits; Piggy_bitmap_cache_next = 0; return retval; } int piggy_is_needed(int soundnum) { int i; if ( !GameArg.SysLowMem ) return 1; for (i=0; i 0 ) { if ( piggy_is_needed(i) ) { cfseek( Piggy_fp, SoundOffset[i], SEEK_SET ); // Read in the sound data!!! snd->data = ptr; #ifdef ALLEGRO ptr += snd->len; sbytes += snd->len; #else ptr += snd->length; sbytes += snd->length; #endif //Arne's decompress for shareware on all soundcards - Tim@Rikers.org if (pc_shareware) { if (lastsize < SoundCompressed[i]) { if (lastbuf) d_free(lastbuf); lastbuf = d_malloc(SoundCompressed[i]); } cfread( lastbuf, SoundCompressed[i], 1, Piggy_fp ); sound_decompress( lastbuf, SoundCompressed[i], snd->data ); } else #ifdef ALLEGRO cfread( snd->data, snd->len, 1, Piggy_fp ); #else cfread( snd->data, snd->length, 1, Piggy_fp ); #endif } } } if (lastbuf) d_free(lastbuf); } extern int descent_critical_error; extern unsigned descent_critical_deverror; extern unsigned descent_critical_errcode; char * crit_errors[13] = { "Write Protected", "Unknown Unit", "Drive Not Ready", "Unknown Command", "CRC Error", "Bad struct length", "Seek Error", "Unknown media type", "Sector not found", "Printer out of paper", "Write Fault", "Read fault", "General Failure" }; void piggy_critical_error() { grs_canvas * save_canv; grs_font * save_font; int i; save_canv = grd_curcanv; save_font = grd_curcanv->cv_font; gr_palette_load( gr_palette ); i = nm_messagebox( "Disk Error", 2, "Retry", "Exit", "%s\non drive %c:", crit_errors[descent_critical_errcode&0xf], (descent_critical_deverror&0xf)+'A' ); if ( i == 1 ) exit(1); gr_set_current_canvas(save_canv); grd_curcanv->cv_font = save_font; } void piggy_bitmap_page_in( bitmap_index bitmap ) { grs_bitmap * bmp; int i,org_i,temp; org_i = 0; i = bitmap.index; Assert( i >= 0 ); Assert( i < MAX_BITMAP_FILES ); Assert( i < Num_bitmap_files ); Assert( Piggy_bitmap_cache_size > 0 ); if ( i < 1 ) return; if ( i >= MAX_BITMAP_FILES ) return; if ( i >= Num_bitmap_files ) return; if ( GameBitmapOffset[i] == 0 ) return; // A read-from-disk bitmap!!! if ( GameArg.SysLowMem ) { org_i = i; i = GameBitmapXlat[i]; // Xlat for low-memory settings! } bmp = &GameBitmaps[i]; if ( bmp->bm_flags & BM_FLAG_PAGED_OUT ) { stop_time(); ReDoIt: descent_critical_error = 0; cfseek( Piggy_fp, GameBitmapOffset[i], SEEK_SET ); if ( descent_critical_error ) { piggy_critical_error(); goto ReDoIt; } gr_set_bitmap_flags (bmp, GameBitmapFlags[i]); gr_set_bitmap_data (bmp, &Piggy_bitmap_cache_data [Piggy_bitmap_cache_next]); if ( bmp->bm_flags & BM_FLAG_RLE ) { int zsize = 0; descent_critical_error = 0; zsize = cfile_read_int(Piggy_fp); if ( descent_critical_error ) { piggy_critical_error(); goto ReDoIt; } // GET JOHN NOW IF YOU GET THIS ASSERT!!! Assert( Piggy_bitmap_cache_next+zsize < Piggy_bitmap_cache_size ); if ( Piggy_bitmap_cache_next+zsize >= Piggy_bitmap_cache_size ) { piggy_bitmap_page_out_all(); goto ReDoIt; } memcpy( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], &zsize, sizeof(int) ); Piggy_bitmap_cache_next += sizeof(int); descent_critical_error = 0; temp = cfread( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], 1, zsize-4, Piggy_fp ); if ( descent_critical_error ) { piggy_critical_error(); goto ReDoIt; } if (MacPig) { rle_swap_0_255(bmp); memcpy(&zsize, bmp->bm_data, 4); } Piggy_bitmap_cache_next += zsize-4; } else { // GET JOHN NOW IF YOU GET THIS ASSERT!!! Assert( Piggy_bitmap_cache_next+(bmp->bm_h*bmp->bm_w) < Piggy_bitmap_cache_size ); if ( Piggy_bitmap_cache_next+(bmp->bm_h*bmp->bm_w) >= Piggy_bitmap_cache_size ) { piggy_bitmap_page_out_all(); goto ReDoIt; } descent_critical_error = 0; temp = cfread( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], 1, bmp->bm_h*bmp->bm_w, Piggy_fp ); if ( descent_critical_error ) { piggy_critical_error(); goto ReDoIt; } if (MacPig) swap_0_255(bmp); Piggy_bitmap_cache_next+=bmp->bm_h*bmp->bm_w; } #ifdef BITMAP_SELECTOR if ( bmp->bm_selector ) { if (!dpmi_modify_selector_base( bmp->bm_selector, bmp->bm_data )) Error( "Error modifying selector base in piggy.c\n" ); } #endif start_time(); } if ( GameArg.SysLowMem ) { if ( org_i != i ) GameBitmaps[org_i] = GameBitmaps[i]; } } void piggy_bitmap_page_out_all() { int i; Piggy_bitmap_cache_next = 0; piggy_page_flushed++; texmerge_flush(); rle_cache_flush(); for (i=0; i 0 ) { // Don't page out bitmaps read from disk!!! GameBitmaps[i].bm_flags = BM_FLAG_PAGED_OUT; gr_set_bitmap_data (&GameBitmaps[i], Piggy_bitmap_cache_data); } } } void piggy_load_level_data() { piggy_bitmap_page_out_all(); paging_touch_all(); } #ifdef EDITOR void piggy_dump_all() { int i, xlat_offset; FILE * fp; #ifndef RELEASE FILE * fp1; FILE * fp2; #endif char * filename; int data_offset; int org_offset; DiskBitmapHeader bmh; DiskSoundHeader sndh; int header_offset; char subst_name[32]; #ifdef NO_DUMP_SOUNDS Num_sound_files = 0; Num_sound_files_new = 0; #endif // { // bitmap_index bi; // bi.index = 614; // PIGGY_PAGE_IN( bi ); // count_colors( bi.index, &GameBitmaps[bi.index] ); // key_getch(); // } // { // bitmap_index bi; // bi.index = 478; // PIGGY_PAGE_IN( bi ); // Int3(); // count_colors( bi.index, &GameBitmaps[bi.index] ); // key_getch(); // } // { // bitmap_index bi; // bi.index = 1398; // PIGGY_PAGE_IN( bi ); // count_colors( bi.index, &GameBitmaps[bi.index] ); // key_getch(); // } // { // bitmap_index bi; // bi.index = 642; // PIGGY_PAGE_IN( bi ); // count_colors( bi.index, &GameBitmaps[bi.index] ); // key_getch(); // } // { // bitmap_index bi; // bi.index = 529; // PIGGY_PAGE_IN( bi ); // count_colors( bi.index, &GameBitmaps[bi.index] ); // key_getch(); // } // exit(0); // if ((Num_bitmap_files_new == 0) && (Num_sound_files_new == 0) ) return; for (i=0; i < Num_bitmap_files; i++ ) { bitmap_index bi; bi.index = i; PIGGY_PAGE_IN( bi ); } piggy_close_file(); filename = SHAREPATH "descent.pig"; fp = fopen( filename, "wb" ); Assert( fp!=NULL ); #ifndef RELEASE fp1 = fopen( "piggy.lst", "wt" ); fp2 = fopen( "piggy.all", "wt" ); #endif i = 0; fwrite( &i, sizeof(int), 1, fp ); bm_write_all(fp); xlat_offset = ftell(fp); fwrite( GameBitmapXlat, sizeof(ushort)*MAX_BITMAP_FILES, 1, fp ); i = ftell(fp); fseek( fp, 0, SEEK_SET ); fwrite( &i, sizeof(int), 1, fp ); fseek( fp, i, SEEK_SET ); Num_bitmap_files--; fwrite( &Num_bitmap_files, sizeof(int), 1, fp ); Num_bitmap_files++; fwrite( &Num_sound_files, sizeof(int), 1, fp ); header_offset = ftell(fp); header_offset += ((Num_bitmap_files-1)*sizeof(DiskBitmapHeader)) + (Num_sound_files*sizeof(DiskSoundHeader)); data_offset = header_offset; for (i=1; i < Num_bitmap_files; i++ ) { int *size; grs_bitmap *bmp; { char * p, *p1; p = strchr(AllBitmaps[i].name,'#'); if (p) { int n; p1 = p; p1++; n = atoi(p1); *p = 0; #ifndef RELEASE if (n==0) { fprintf( fp2, "%s.abm\n", AllBitmaps[i].name ); } #endif memcpy( bmh.name, AllBitmaps[i].name, 8 ); Assert( n <= 63 ); bmh.dflags = DBM_FLAG_ABM + n; *p = '#'; }else { #ifndef RELEASE fprintf( fp2, "%s.bbm\n", AllBitmaps[i].name ); #endif memcpy( bmh.name, AllBitmaps[i].name, 8 ); bmh.dflags = 0; } } bmp = &GameBitmaps[i]; Assert( !(bmp->bm_flags&BM_FLAG_PAGED_OUT) ); #ifndef RELEASE fprintf( fp1, "BMP: %s, size %d bytes", AllBitmaps[i].name, bmp->bm_rowsize * bmp->bm_h ); #endif org_offset = ftell(fp); bmh.offset = data_offset - header_offset; fseek( fp, data_offset, SEEK_SET ); if ( bmp->bm_flags & BM_FLAG_RLE ) { size = (int *)bmp->bm_data; fwrite( bmp->bm_data, sizeof(ubyte), *size, fp ); data_offset += *size; //bmh.data_length = *size; #ifndef RELEASE fprintf( fp1, ", and is already compressed to %d bytes.\n", *size ); #endif } else { fwrite( bmp->bm_data, sizeof(ubyte), bmp->bm_rowsize * bmp->bm_h, fp ); data_offset += bmp->bm_rowsize * bmp->bm_h; //bmh.data_length = bmp->bm_rowsize * bmp->bm_h; #ifndef RELEASE fprintf( fp1, ".\n" ); #endif } fseek( fp, org_offset, SEEK_SET ); if ( GameBitmaps[i].bm_w > 255 ) { Assert( GameBitmaps[i].bm_w < 512 ); bmh.width = GameBitmaps[i].bm_w - 256; bmh.dflags |= DBM_FLAG_LARGE; } else { bmh.width = GameBitmaps[i].bm_w; } Assert( GameBitmaps[i].bm_h < 256 ); bmh.height = GameBitmaps[i].bm_h; bmh.flags = GameBitmaps[i].bm_flags; if (piggy_is_substitutable_bitmap( AllBitmaps[i].name, subst_name )) { bitmap_index other_bitmap; other_bitmap = piggy_find_bitmap( subst_name ); GameBitmapXlat[i] = other_bitmap.index; bmh.flags |= BM_FLAG_PAGED_OUT; } else { #ifdef BUILD_PSX_DATA count_colors( i, &GameBitmaps[i] ); #endif bmh.flags &= ~BM_FLAG_PAGED_OUT; } bmh.avg_color=GameBitmaps[i].avg_color; fwrite( &bmh, sizeof(DiskBitmapHeader), 1, fp ); // Mark as a bitmap } for (i=0; i < Num_sound_files; i++ ) { digi_sound *snd; snd = &GameSounds[i]; strcpy( sndh.name, AllSounds[i].name ); #ifdef ALLEGRO sndh.length = GameSounds[i].len; #else sndh.length = GameSounds[i].length; #endif sndh.offset = data_offset - header_offset; org_offset = ftell(fp); fseek( fp, data_offset, SEEK_SET ); sndh.data_length = sndh.length; fwrite( snd->data, sizeof(ubyte), sndh.length, fp ); data_offset += sndh.length; fseek( fp, org_offset, SEEK_SET ); fwrite( &sndh, sizeof(DiskSoundHeader), 1, fp ); // Mark as a bitmap #ifndef RELEASE fprintf( fp1, "SND: %s, size %d bytes\n", AllSounds[i].name, sndh.length ); fprintf( fp2, "%s.raw\n", AllSounds[i].name ); #endif } fseek( fp, xlat_offset, SEEK_SET ); fwrite( GameBitmapXlat, sizeof(ushort)*MAX_BITMAP_FILES, 1, fp ); fclose(fp); #ifndef RELEASE fprintf( fp1, " Dumped %d assorted bitmaps.\n", Num_bitmap_files ); fprintf( fp1, " Dumped %d assorted sounds.\n", Num_sound_files ); fclose(fp1); fclose(fp2); #endif #ifdef BUILD_PSX_DATA fp = fopen( "psx/descent.dat", "wb" ); fwrite( &i, sizeof(int), 1, fp ); bm_write_all(fp); fwrite( GameBitmapXlat, sizeof(ushort)*MAX_BITMAP_FILES, 1, fp ); fclose(fp); #endif // Never allow the game to run after building pig. exit(0); } #endif void piggy_close() { int i; custom_close(); piggy_close_file(); //added ifndef on 10/04/98 by Matt Mueller to fix crash on exit bug -- killed 2000/02/06 since they don't seem to cause crash anymore. heh. //#ifndef __LINUX__ if (BitmapBits) d_free(BitmapBits); if ( SoundBits ) d_free( SoundBits ); for (i = 0; i < Num_sound_files; i++) if (SoundOffset[i] == 0) d_free(GameSounds[i].data); //#endif //end addition -MM hashtable_free( &AllBitmapsNames ); hashtable_free( &AllDigiSndNames ); } int piggy_does_bitmap_exist_slow( char * name ) { int i; for (i=0; iindex = cfile_read_short(fp); } /* * reads n bitmap_index structs from a CFILE */ int bitmap_index_read_n(bitmap_index *bi, int n, CFILE *fp) { int i; for (i = 0; i < n; i++) bi[i].index = cfile_read_short(fp); return i; }