#include "cache.hpp" #include "lisp.hpp" #include "video.hpp" #include "dprint.hpp" #include "exitproc.hpp" #include "lcache.hpp" #include "status.hpp" #include "game.hpp" #include "lisp_gc.hpp" #include "level.hpp" #include "status.hpp" #include "crc.hpp" #include "specache.hpp" #include "ramfile.hpp" #ifndef __MAC__ #include #endif #include char lfname[100]=""; // name of compiled lisp code cache file #define touch(x) { (x)->last_access=last_access++; \ if ((x)->last_access<0) { normalize(); (x)->last_access=1; } } extern char *symbol_str(char *name); crc_manager crc_man; int past_startup=0; int crc_man_write_crc_file(char *filename) { return crc_man.write_crc_file(filename); } int crc_manager::write_crc_file(char *filename) // return 0 on failure { get_filenumber("light.tbl"); get_filenumber("art/keys.spe"); get_filenumber("art/loading.spe"); get_filenumber("art/status.spe"); get_filenumber("art/mon_cfg.spe"); char msg[100]; sprintf(msg,symbol_str("calc_crc")); // this may take some time, show the user a status indicator if (stat_man) stat_man->push(msg,NULL); int i,total=0; for (i=0;iopen_failure()) { set_crc(i,crc_file(fp)); total++; } delete fp; } else total++; if (stat_man) stat_man->update(i*100/total_files); } if (stat_man) stat_man->pop(); jFILE *fp=new jFILE("#net_crc","wb"); if (fp->open_failure()) { delete fp; return 0; } fp->write_short(total); total=0; for (i=0;iwrite_long(crc); uchar len=strlen(get_filename(i))+1; fp->write_byte(len); fp->write(get_filename(i),len); total++; } } delete fp; return 1; } int crc_manager::load_crc_file(char *filename) { bFILE *fp=open_file(filename,"rb"); if (fp->open_failure()) { delete fp; return 0; } else { short total=fp->read_short(); int i; for (i=0;iread_long(); uchar len=fp->read_byte(); fp->read(name,len); set_crc(get_filenumber(name),crc); } delete fp; } return 1; } void crc_manager::clean_up() { for (int i=0;ifilename)) return i; total_files++; files=(crced_file **)jrealloc(files,total_files*sizeof(crced_file *),"crc_file_list"); files[total_files-1]=new crced_file(filename); return total_files-1; } char *crc_manager::get_filename(long filenumber) { CHECK(filenumber>=0 && filenumberfilename; } ulong crc_manager::get_crc(long filenumber, int &failed) { CHECK(filenumber>=0 && filenumbercrc_calculated) { failed=0; return files[filenumber]->crc; } failed=1; return 0; } void crc_manager::set_crc(long filenumber, ulong crc) { CHECK(filenumber>=0 && filenumbercrc_calculated=1; files[filenumber]->crc=crc; } void cache_list::unmalloc(cache_item *i) { switch (i->type) { case SPEC_CHARACTER2 : case SPEC_CHARACTER : delete ((figure *)i->data); break; case SPEC_FORETILE : delete ((foretile *)i->data); break; case SPEC_BACKTILE : delete ((backtile *)i->data); break; case SPEC_IMAGE : delete ((image *)i->data); break; case SPEC_EXTERN_SFX : delete ((sound_effect *)i->data); break; case SPEC_EXTERNAL_LCACHE : if (i->data) jfree(i->data); break; case SPEC_PALETTE : delete ((char_tint *)i->data); break; default : dprintf("Trying to unmalloc unknown type\n"); } i->data=NULL; i->last_access=-1; } void cache_list::prof_init() { if (prof_data) jfree(prof_data); prof_data=(int *)jmalloc(sizeof(int)*total,"cache profile"); memset(prof_data,0,sizeof(int)*total); } static int c_sorter(const void *a, const void *b) { return cash.compare(*(int *)a,*(int *)b); } int cache_list::compare(int a, int b) { if (prof_data[a]prof_data[b]) return -1; else return 0; } int cache_list::prof_size() { int size=0; // count up the size for a spec enrty size+=2; // total filenames int i; for (i=0;i0) // don't save unaccessed counts size+=2+4+1; // filenumber & offset & type return size; } void cache_list::prof_write(bFILE *fp) { if (prof_data) { int *ordered_ids=(int *)jmalloc(sizeof(int)*total,"profile order"); int i; for (i=0;iwrite_short(crc_man.total_filenames()); for (i=0;iwrite_byte(l); fp->write(crc_man.get_filename(i),l); } int tsaved=0; for (i=0;i0) tsaved++; fp->write_long(tsaved); for (i=0;i0) // don't save unaccessed counts { fp->write_byte(list[id].type); // save type, if type changed on reload // don't cache in-> its a different refrence fp->write_short(list[id].file_number); fp->write_long(list[id].offset); } } } jfree(ordered_ids); } else dprintf("Cache profiling was not initialized\n"); } void cache_list::prof_uninit() { if (prof_data) { jfree(prof_data); prof_data=NULL; } } int *sorted_id_list; static int s_offset_compare(const void *a, const void *b) { return cash.offset_compare(*(int *)a,*(int *)b); } int cache_list::offset_compare(int a, int b) { if (list[a].offsetlist[b].offset) return 1; else if (list[a].file_numberlist[b].file_number) return 1; else return 0; } int cache_list::search(int *sarray, ushort filenum, long offset) { int x1=0,x2=total-1; int split; do { split=(x1+x2)/2; cache_item *e=list+sarray[split]; int comp; if (e->offsetoffset>offset) x2=split-1; else if (e->file_numberfile_number>filenum) x2=split-1; else return sarray[split]; } while (x1<=x2); return -1; } static int load_chars() // returns 0 if cache filled { int i; for (i=0;iget_cflag(CFLAG_NEED_CACHE_IN)) { figures[i]->set_cflag(CFLAG_CACHED_IN,0); figures[i]->cache_in(); figures[i]->set_cflag(CFLAG_NEED_CACHE_IN,0); } } return 1; } void cache_list::note_need(int id) { if (list[id].last_access<0) list[id].last_access=-2; else list[id].last_access=2; } void cache_list::preload_cache_object(int type) { if (type<0xffff) { if (!figures[type]->get_cflag(CFLAG_NEED_CACHE_IN)) // see if it's already marked { figures[type]->set_cflag(CFLAG_NEED_CACHE_IN,1); void *cache_fun=figures[type]->get_fun(OFUN_GET_CACHE_LIST); if (cache_fun) { int sp=current_space; current_space=PERM_SPACE; void *call_with=NULL; push_onto_list(new_lisp_number(type),call_with); void *cache_list=eval_function((lisp_symbol *)cache_fun,call_with); p_ref r1(cache_list); if (cache_list && lcar(cache_list)) { void *obj_list=lcar(cache_list); while (obj_list) { int t=lnumber_value(CAR(obj_list)); if (t<0 || t>=total_objects) lbreak("Get cache list returned a bad object number %d\n",t); else preload_cache_object(t); obj_list=CDR(obj_list); } } if (cache_list && lcdr(cache_list)) { void *id_list=lcar(lcdr(cache_list)); while (id_list) { int id=lnumber_value(CAR(id_list)); if (id<0 || id>=total) lbreak("Get cache list returned a bad id number %d\n",id); else if (list[id].last_access<0) list[id].last_access=-2; else list[id].last_access=2; id_list=CDR(id_list); } } current_space=sp; } } } } void cache_list::preload_cache(level *lev) { game_object *f; int i; for (i=0;iset_cflag(CFLAG_NEED_CACHE_IN,0); for (f=lev->first_object();f;f=f->next) // go through each object and get requested items to cache in preload_cache_object(f->otype); int j; ushort *fg_line; for (j=0;jforeground_height();j++) { fg_line=lev->get_fgline(j); for (i=0;iforeground_width();i++,fg_line++) { int id=foretiles[fgvalue(*fg_line)]; if (id>=0 && idbackground_height();j++) { bg_line=lev->get_bgline(j); for (i=0;ibackground_width();i++,bg_line++) { int id=backtiles[bgvalue(*bg_line)]; if (id>=0 && idtotal;j++) if (list[j].last_access>=0) // reset all loaded cache items to 0, all non-load to -1 list[j].last_access=0; preload_cache(lev); // preliminary guesses at stuff to load int load_fail=1; bFILE *fp=open_file(filename,"rb"); if (!fp->open_failure()) { spec_directory sd(fp); spec_entry *se=sd.find("cache profile info"); // see if the cache profile info is in the file if (se) { fp->seek(se->offset,0); char name[255]; int tnames=0; int *fnum_remap; // remaps old filenumbers into current ones tnames=fp->read_short(); if (tnames) /// make sure there isn't bad info in the file { fnum_remap=(int *)jmalloc(sizeof(int)*tnames,"pfname remap"); int i; for (i=0;iread(name,fp->read_byte()); fnum_remap[i]=-1; // initialize the map to no-map int j; for (j=0;jread_long(); int *priority=(int *)jmalloc(tsaved*sizeof(int),"priorities"); memset(priority,0xff,tsaved*sizeof(int)); // initialize to -1 int tmatches=0; sorted_id_list=(int *)jmalloc(sizeof(int)*total,"sorted ids"); for (j=0;jread_byte(); short file_num=fp->read_short(); if (file_num>=tnames) // bad data? file_num=-1; else file_num=fnum_remap[file_num]; ulong offset=fp->read_long(); // search for a match j=search(sorted_id_list,file_num,offset); if (j!=-1) { if (list[j].last_access<0) // if not loaded list[j].last_access=-2; // mark as needing loading else list[j].last_access=2; // mark as loaded and needing to stay that way priority[i]=j; tmatches++; } } jfree(sorted_id_list); // was used for searching, no longer needed for (j=0;jupdate(j*70/total+25); if (list[j].file_number>=0 && list[j].last_access==-2) { list[j].last_access=-1; if (!ful) { switch (list[j].type) { case SPEC_BACKTILE : backt(j); break; case SPEC_FORETILE : foret(j); break; case SPEC_CHARACTER : case SPEC_CHARACTER2 : fig(j); break; case SPEC_IMAGE : img(j); break; case SPEC_EXTERN_SFX : sfx(j); break; case SPEC_EXTERNAL_LCACHE : lblock(j); break; case SPEC_PALETTE : ctint(j); break; } tcached++; } } } load_fail=0; // if (full()) // dprintf("Cache filled while loading\n"); if (tsaved>tmatches) tmatches=tsaved+1; last_access=tmatches+1; for (i=0;iupdate(j*75/total+25); if (list[j].file_number>=0 && list[j].last_access==-2) { list[j].last_access=-1; if (!ful) { switch (list[j].type) { case SPEC_BACKTILE : backt(j); break; case SPEC_FORETILE : foret(j); break; case SPEC_CHARACTER : case SPEC_CHARACTER2 : fig(j); break; case SPEC_IMAGE : img(j); break; case SPEC_EXTERN_SFX : sfx(j); break; case SPEC_EXTERNAL_LCACHE : lblock(j); break; case SPEC_PALETTE : ctint(j); break; } } } } if (full()) dprintf("Cache filled while loading\n"); } stat_man->update(100); delete fp; } void cache_list::prof_poll_start() { poll_start_access=last_access; } void cache_list::prof_poll_end() { if (prof_data) { int i=0; for (;i=poll_start_access) prof_data[i]++; } } } void cache_list::expire(int id) { if (list[id].file_number && list[id].data) unmalloc(list+id); } void cache_list::unreg(int id) { if (list[id].file_number && list[id].data) unmalloc(&list[id]); else dprintf("Error : trying to unregister free object\n"); list[id].file_number=-1; } void cache_cleanup2() { unlink(lfname); } void cache_cleanup(int ret, void *arg) { unlink(lfname); } FILE *open_FILE(char *filename, char *mode); extern char *macify_name(char *s); void cache_list::create_lcache() { #ifdef __WATCOMC__ char *prefix="c:\\"; #else char *prefix="/tmp/"; // for UNIX store lisp cache in tmp dir int flags=O_CREAT | O_RDWR; #endif int cfail=1,num=0; do { sprintf(lfname,"%slcache%02d.tmp",prefix,num); #if defined( __WATCOMC__ ) || defined( __MAC__ ) unlink(lfname); #ifdef __MAC__ macify_name(lfname); #endif FILE *fp=fopen(lfname,"wb"); if (fp) { fclose(fp); cfail=0; } #else int fd=open(lfname,flags,S_IRWXU | S_IRWXG | S_IRWXO); // can we get exclusive rights to this file? if (fd<0) close(fd); else cfail=0; #endif if (cfail) num++; } while (cfail && num<15); if (cfail) { dprintf("Error : Unable to open cache file for compiled code.\n" " Please delete all files named lcacheXX.tmp\n" " and make sure you have write permission to\n" " directory (%s)\n",prefix); exit(0); } else { exit_proc(cache_cleanup,cache_cleanup2); // make sure this file gets deleted on exit.. } lcache_number=-1; } cache_list::cache_list() { // start out with a decent sized cache buffer because it's going to get allocated anyway. total=0; list=NULL; last_registered=-1; cache_mfile=0; fp=NULL; last_access=1; used=ful=0; last_dir=NULL; last_file=-1; prof_data=NULL; create_lcache(); } cache_list::~cache_list() { } void cache_list::empty() { for (int i=0;i=0 && list[i].last_access!=-1) unmalloc(&list[i]); } jfree(list); if (fp) delete fp; if (last_dir) delete last_dir; if (cache_mfile) { delete cache_mfile; cache_mfile=NULL; } if (prof_data) { delete prof_data; prof_data=NULL; } total=0; // reinitalize list=NULL; last_registered=-1; cache_mfile=0; fp=NULL; last_access=1; used=ful=0; last_dir=NULL; last_file=-1; prof_data=NULL; } void cache_list::locate(cache_item *i, int local_only) { // dprintf("cache in %s, type %d, offset %d\n",crc_man.get_filename(i->file_number),i->type,i->offset); if (i->file_number!=last_file) { if (fp) delete fp; if (last_dir) delete last_dir; if (local_only) fp=new jFILE(crc_man.get_filename(i->file_number),"rb"); else fp=open_file(crc_man.get_filename(i->file_number),"rb"); if (fp->open_failure()) { dprintf("Ooch. Could not open file %s\n",crc_man.get_filename(i->file_number)); delete fp; exit(0); } last_offset=-1; last_dir=new spec_directory(fp); last_file=i->file_number; } if (i->offset!=last_offset) { fp->seek(i->offset,SEEK_SET); last_offset=i->offset; } used=1; } long cache_list::alloc_id() { int id; if (prof_data) { the_game->show_help("new id allocated, cache profiling turned off\n"); prof_uninit(); } // see if we previously allocated an id, if so check the next spot in the array // otherwise we will have to scan the whole list for a free id and possible // grow the list. if (last_registered+1file_number<0) id=i; } if (id<0) // if no free id's then make list bigger { int add_size=20; list=(cache_item *)jrealloc(list,(sizeof(cache_item)*(total+add_size)),"Cache list"); for (i=0;ifile_number=fn; ci->last_access=-1; ci->type=SPEC_EXTERNAL_LCACHE; if (!can_cache_lisp()) { ci->data=(void *)block; // we can't cache it out so it must be in memory return id; } ci->data=NULL; // assume that it is in tmp memory, need to cache in on access ci->offset=cache_mfile->wp; s=block_size(block); cache_mfile->write_long(s); write_level(cache_mfile,block); return id; } long cache_list::reg_object(char *filename, void *object, int type, int rm_dups) { char *name; if (item_type(object)==L_CONS_CELL) // see if we got a object with a filename included { filename=lstring_value(lcar(object)); name=lstring_value(lcdr(object)); } else name=lstring_value(object); // otherwise should be a string return reg(filename,name,type,rm_dups); } extern int total_files_open; long cache_list::reg(char *filename, char *name, int type, int rm_dups) { int id=alloc_id(),i,fn=crc_man.get_filenumber(filename); cache_item *ci=list+id; CHECK(idopen_failure()) { delete check; dprintf("Unable to open file '%s' for reading\n",filename); exit(0); } char buf[4]; check->read(buf,4); delete check; if (memcmp(buf,"RIFF",4)) { dprintf("File %s is not a WAV file\n",filename); exit(0); } ci->file_number=fn; ci->last_access=-1; ci->data=NULL; ci->offset=0; ci->type=type; return id; } spec_directory *sd=sd_cache.get_spec_directory(filename); if (!sd) { dprintf("Unable to open filename %s for requested item %s\n",filename,name); exit(0); } spec_entry *se; if (type!=-1) { se=sd->find(name,type); if (!se) se=sd->find(name); } else se=sd->find(name); if (!se) { dprintf("No such item %s in file %s\n",name,filename); exit(0); } else if (type>=0 && (type!=se->type && ((type!=SPEC_CHARACTER2 && type!=SPEC_CHARACTER) || (se->type!=SPEC_CHARACTER && se->type!=SPEC_CHARACTER2)))) { dprintf("Item %s of file %s should be type %s\n",name,filename,spec_types[type]); exit(0); } if (rm_dups) { for (i=0;ioffset) return i; } ci->file_number=fn; ci->last_access=-1; ci->data=NULL; ci->offset=se->offset; ci->type=se->type; return id; } void cache_list::normalize() { int j; cache_item *ci=list; last_access=-1; for (j=0;jlast_access>=0) ci->last_access=ci->last_access>>16; // shift everything over by 16 if (ci->last_access>last_access) // and find new largest timestamp last_access=ci->last_access; } last_access++; } backtile *cache_list::backt(int id) { cache_item *me=list+id; CONDITION(id=0 && me->file_number>=0,"Bad id"); if (me->last_access>=0) { touch(me); return (backtile *)me->data; } else { touch(me); locate(me); int sp=alloc_space; alloc_space=ALLOC_SPACE_CACHE; me->data=(void *)new backtile(fp); alloc_space=sp; last_offset=fp->tell(); return (backtile *)me->data; } } foretile *cache_list::foret(int id) { cache_item *me=list+id; CONDITION(id=0 && me->file_number>=0,"Bad id"); if (me->last_access>=0) { touch(me); return (foretile *)me->data; } else { touch(me); locate(me); int sp=alloc_space; alloc_space=ALLOC_SPACE_CACHE; me->data=(void *)new foretile(fp); alloc_space=sp; last_offset=fp->tell(); return (foretile *)me->data; } } figure *cache_list::fig(int id) { cache_item *me=list+id; // CONDITION(id=0 && me->file_number>=0,"Bad id"); if (me->last_access>=0) { touch(me); return (figure *)me->data; } else { touch(me); locate(me); int sp=alloc_space; alloc_space=ALLOC_SPACE_CACHE; me->data=(void *)new figure(fp,me->type); alloc_space=sp; last_offset=fp->tell(); return (figure *)me->data; } } image *cache_list::img(int id) { cache_item *me=list+id; CONDITION(id=0 && me->file_number>=0,"Bad id"); if (me->last_access>=0) { touch(me); return (image *)me->data; } else { touch(me); // hold me, feel me, be me! locate(me); int sp=alloc_space; alloc_space=ALLOC_SPACE_CACHE; image *im=new image(fp); alloc_space=sp; me->data=(void *)im; last_offset=fp->tell(); return (image *)me->data; } } sound_effect *cache_list::sfx(int id) { cache_item *me=list+id; CONDITION(id=0 && me->file_number>=0,"Bad id"); if (me->last_access>=0) { touch(me); // hold me, feel me, be me! return (sound_effect *)me->data; } else { touch(me); // hold me, feel me, be me! char *fn=crc_man.get_filename(me->file_number); int sp=alloc_space; alloc_space=ALLOC_SPACE_CACHE; me->data=(void *)new sound_effect(fn); alloc_space=sp; return (sound_effect *)me->data; } } #if can_cache_lisp() void *cache_list::lblock(int id) { cache_item *me=list+id; if (me->last_access>=0) { touch(me); return (void *)me->data; } else { touch(me); cache_mfile->rp=me->offset; int sp=alloc_space; alloc_space=ALLOC_SPACE_CACHE; long size=cache_mfile->read_long(); void *space; if (size) space=jmalloc(size,"cached lisp block"); else space=NULL; int cs=current_space; use_user_space(space,size); load_block(cache_mfile); current_space=cs; alloc_space=sp; if (size) me->data=(void *)space; else me->data=0; return (void *)me->data; } } #endif cache_list cash; void free_up_memory() { cash.free_oldest(); } void cache_list::free_oldest() { long i,old_time=last_access; cache_item *ci=list,*oldest=NULL; ful=1; for (i=0;idata && ci->last_accesslast_access; } } if (oldest) { dprintf("mem_maker : freeing %s\n",spec_types[oldest->type]); unmalloc(oldest); } else { close_graphics(); dprintf("Out of memory, please remove any TSR's device drivers you can\n"); mem_report("out_of_mem"); exit(0); } } void cache_list::show_accessed() { int old=last_access,new_old_accessed; cache_item *ci,*new_old; do { new_old_accessed=-1; new_old=NULL; ci=list; for (int i=0;ilast_accesslast_access>0 && ci->last_access>new_old_accessed) { new_old_accessed=ci->last_access; new_old=ci; } } if (new_old) { ci=new_old; old=ci->last_access; dprintf("type=(%20s) file=(%20s) access=(%6d)\n",spec_types[ci->type], crc_man.get_filename(ci->file_number), ci->last_access); } } while (new_old); } int cache_list::loaded(int id) { cache_item *me=list+id; CONDITION(id=0 && me->file_number>=0,"Bad id"); if (me->last_access>=0) return 1; else return 0; } char_tint *cache_list::ctint(int id) { cache_item *me=list+id; CONDITION(id=0 && me->file_number>=0,"Bad id" && me->type==SPEC_PALETTE); if (me->last_access>=0) { touch(me); return (char_tint *)me->data; } else { touch(me); locate(me); int sp=alloc_space; alloc_space=ALLOC_SPACE_CACHE; me->data=(void *)new char_tint(fp); alloc_space=sp; last_offset=fp->tell(); return (char_tint *)me->data; } }