source: golgotha/src/render/tex_cache.cc

Last change on this file was 80, checked in by Sam Hocevar, 11 years ago
  • Adding the Golgotha source code. Not sure what's going to be interesting in there, but since it's all public domain, there's certainly stuff to pick up.
File size: 19.7 KB
Line 
1/********************************************************************** <BR>
2  This file is part of Crack dot Com's free source code release of
3  Golgotha. <a href="http://www.crack.com/golgotha_release"> <BR> for
4  information about compiling & licensing issues visit this URL</a>
5  <PRE> If that doesn't help, contact Jonathan Clark at
6  golgotha_source@usa.net (Subject should have "GOLG" in it)
7***********************************************************************/
8
9//WARNING: this code is really nauseating.
10#include "tex_cache.hh"
11#include "mip_average.hh"
12#include "status/status.hh"
13#include "file/file.hh"
14#include "error/error.hh"
15#include "r1_res.hh"
16
17void do_single_file(i4_file_class *dst_file,
18                    w32 id,                   
19                    sw32 max_texture_dimention,
20                    w32 network_file_date,
21                    tex_cache_entry_t *t);
22
23struct file_status_sort_struct
24{
25  w32 id;
26  w32 file_status_index;
27};
28
29int file_status_struct_compare(const file_status_sort_struct *a, const file_status_sort_struct *b)
30{
31  if (a->id<b->id)
32    return -1;
33  else if (a->id>b->id)
34    return 1;
35  else return 0;
36}
37
38
39void tex_cache_entry_t::write(i4_file_class *f)
40{
41  if (!f)
42  {
43    i4_warning("tex_cache_entry_t::write() NULL file");
44    return;
45  }
46
47  f->write_32(id);
48  f->write_32(lowmipoffset);
49  f->write_32(average_color);
50  f->write_32(last_modified);
51   
52  f->write_16(base_width);
53  f->write_16(base_height);
54
55  f->write_8(flags);
56  f->write_8(num_mip_levels);   
57}
58
59void tex_cache_entry_t::read(i4_file_class *f)
60{
61  if (!f)
62  {
63    i4_warning("tex_cache_entry_t::read() NULL file");
64    return;
65  }
66
67  id             = f->read_32();
68  lowmipoffset   = f->read_32();
69  average_color  = f->read_32();
70  last_modified  = f->read_32();
71   
72  base_width     = f->read_16();
73  base_height    = f->read_16();
74
75  flags          = f->read_8();
76  num_mip_levels = f->read_8();
77}
78
79
80
81void tex_cache_header_t::write(i4_file_class *f)
82{
83  if (!f)
84  {
85    i4_warning("tex_cache_header_t::write() NULL file");
86    return;
87  }
88
89  regular_format.write(f);
90  chroma_format.write(f);
91  alpha_format.write(f);
92   
93  f->write_32(max_mip_dimention);
94  f->write_32(num_entries);       
95}
96 
97void tex_cache_header_t::read(i4_file_class *f)
98{
99  if (!f)
100  {
101    i4_warning("tex_cache_header_t::read() NULL file");
102    return;
103  }
104
105  if (f->size() < tex_cache_header_disk_size())
106  {
107    i4_warning("texture cache file corrupt");
108    regular_format.default_format();
109    chroma_format.default_format();
110    alpha_format.default_format();
111    max_mip_dimention = 0;
112    num_entries       = 0;
113    entries           = 0;
114    return;
115  }
116
117  //seek to the end and read the last dword
118   
119  f->seek(f->size()-4);
120  sw32 expected_size = f->read_32();
121
122  if (expected_size != f->size())
123  {     
124    i4_warning("texture cache file corrupt");
125    entries = 0;
126    num_entries = 0;
127    return;
128  }   
129   
130  //seek back to the beginning and start reading the file
131  f->seek(0);
132
133  regular_format.read(f);
134  chroma_format.read(f);
135  alpha_format.read(f);
136   
137  max_mip_dimention = f->read_32();
138  num_entries = f->read_32();
139         
140  w32 entry_lump_size = tex_cache_entry_disk_size() * num_entries;
141
142  //read in the entries
143
144  if (entry_lump_size)
145  {   
146    entries = (tex_cache_entry_t *)i4_malloc(sizeof(tex_cache_entry_t) * num_entries,"tex_cache_entries");
147    f->seek(f->size() - 4 - entry_lump_size);
148  }
149  else
150  {
151    num_entries = 0;
152    entries     = 0;
153  }
154
155  sw32 i;
156
157  for (i=0; i<num_entries; i++)
158  {
159    entries[i].read(f);
160  } 
161}
162
163
164i4_bool r1_texture_manager_class::update_cache_file(i4_array<w32> &update_ids,
165                                                    const i4_const_str &network_dir,
166                                                    const i4_const_str &local_dir)
167{
168  sw32 i,j,k;
169
170  i4_array<w32>               network_file_ids(128,128);
171  i4_array<tex_cache_entry_t> new_cache_entries(128,128);
172
173  i4_status_class *stat=i4_create_status(r1_gets("checking_times"), I4_STATUS_UNKNOWN_TOTAL);
174
175
176  i4_directory_struct ds;
177  i4_get_directory(network_dir, ds, i4_T, stat);
178
179  delete stat;
180
181  for (i=0; i<ds.tfiles; i++)
182    network_file_ids.add(r1_get_file_id(*ds.files[i]));   
183
184  i4_file_class *old_cache_file = i4_open(r1_get_cache_file(), I4_READ | I4_NO_BUFFER);
185
186  w32 csize = old_cache_file->size();
187
188  void *old_cache_file_data = i4_malloc(csize,"old cache file data");
189  old_cache_file->read(old_cache_file_data,csize);
190
191  delete old_cache_file;
192 
193  old_cache_file = new i4_ram_file_class(old_cache_file_data,csize);
194
195  tex_cache_header_t old_cache_header;
196
197  old_cache_header.read(old_cache_file); 
198
199  w32 total_cache_entries = old_cache_header.num_entries;
200  w32 old_cache_header_num_entries = old_cache_header.num_entries;
201
202  for (i=0; i < update_ids.size();)
203  {
204    tex_cache_entry_t *t = find_id_in_tex_cache(old_cache_header.entries,old_cache_header.num_entries,update_ids[i]);
205   
206    if (t)
207    {
208      update_ids.remove(i); //pull this out of the list
209      t->flags |= R1_MIP_EXTRA_FLAG;
210    }
211    else
212    {
213      //new textures going into the cache file
214      total_cache_entries++;
215      i++;
216    }
217  }
218 
219  i4_file_class *new_cache_file = i4_open(r1_get_cache_file(), I4_WRITE | I4_NO_BUFFER);
220
221  old_cache_header.num_entries = total_cache_entries;
222  old_cache_header.write(new_cache_file); 
223 
224  r1_setup_decompression(&regular_format,&chroma_format,&alpha_format,R1_CHROMA_COLOR,square_textures);
225
226  update_ids.sort(w32_compare); 
227 
228  j = 0;
229  i = 0; 
230
231  i4_bool i_done = i4_F;
232  i4_bool j_done = i4_F;
233
234  stat = i4_create_status(r1_gets("updating_texture_cache"));
235
236  while (1)
237  { 
238    if (j>=update_ids.size()) j_done = i4_T;
239   
240    if (i>=old_cache_header_num_entries) i_done = i4_T;
241
242    if (i_done && j_done) break;
243
244    w32 file_offs = new_cache_file->tell();
245
246    if ((j_done && !i_done) || (!i_done && old_cache_header.entries[i].id < update_ids[j]))
247    {
248      tex_cache_entry_t *t = &old_cache_header.entries[i];
249           
250      if (!(t->flags & R1_MIP_EXTRA_FLAG))
251      {
252        if (t->lowmipoffset != 0xFFFFFFFF)
253        {
254          old_cache_file->seek(t->lowmipoffset);
255
256          sw32 entry_size = sizeof(sw32) * 2 +
257                           t->base_width  / (1<<(t->num_mip_levels-1)) *
258                           t->base_height / (1<<(t->num_mip_levels-1)) * 2;
259   
260          t->lowmipoffset = file_offs;
261
262          w8 copy_buffer[2048];
263          while (entry_size)
264          {         
265            sw32 copy_size = (entry_size < 2048) ? (entry_size) : (2048);
266
267            old_cache_file->read(copy_buffer, copy_size);
268            new_cache_file->write(copy_buffer,copy_size);
269           
270            entry_size -= copy_size;
271          }
272        }
273      }
274      else
275      {
276        t->flags &= (~R1_MIP_EXTRA_FLAG);
277
278        w32 file_date=0xFFFFFFFF;
279
280        //have to update this texture   
281        for (k=0; k<network_file_ids.size(); k++)
282        {
283          if (t->id==network_file_ids[k])
284          {
285            file_date = ds.file_status[k].last_modified;
286            break;
287          }
288        }
289
290        //dont want files in the cache w/different mip levels.
291        do_single_file(new_cache_file,
292                       t->id,                       
293                       old_cache_header.max_mip_dimention,
294                       file_date,
295                       t);       
296      }
297
298      new_cache_entries.add(*t);
299
300      i++;
301    }   
302    else
303    if ((i_done && !j_done) || (!j_done && update_ids[j] < old_cache_header.entries[i].id))   
304    {
305      //have to add this id
306      w32 new_id = update_ids[j];
307
308      w32 file_date=0xFFFFFFFF;
309
310      //have to update this texture   
311      for (k=0; k<network_file_ids.size(); k++)
312      {
313        if (new_id==network_file_ids[k])
314        {
315          file_date = ds.file_status[k].last_modified;
316          break;
317        }
318      }
319
320      tex_cache_entry_t new_entry;
321
322      do_single_file(new_cache_file,
323                     new_id,                     
324                     old_cache_header.max_mip_dimention,
325                     file_date,
326                     &new_entry);
327
328      new_cache_entries.add(new_entry);
329      j++; 
330    }
331
332    if (stat)
333      stat->update((new_cache_entries.size()+1) / (float)total_cache_entries);
334  }
335   
336  if (stat)
337    delete stat;
338
339
340  if (old_cache_header.entries)
341    i4_free(old_cache_header.entries);
342
343  i4_free(old_cache_file_data);
344  delete old_cache_file;
345 
346  r1_end_decompression(); 
347
348  if (total_cache_entries != new_cache_entries.size())
349  {
350    i4_warning("error updating the texture cache file");
351  }
352
353  for (i=0;i<new_cache_entries.size();i++)
354  {
355    new_cache_entries[i].write(new_cache_file);   
356  }
357
358  sw32 cache_size = new_cache_file->tell() + 4;
359
360  new_cache_file->write_32(cache_size);
361
362  delete new_cache_file;
363                 
364  return i4_T;
365}
366
367i4_bool r1_texture_manager_class::build_cache_file(i4_array<w32> &texture_file_ids,
368                                                   const i4_const_str &network_dir,
369                                                   const i4_const_str &local_dir)
370{
371  //creates a tex_cache.dat "directory" w/lowest mip levels and mipheader_t info (tex_cache_entry_t, actually)
372  //decompresses others to local .gtx files
373
374  i4_array<w32> network_file_ids(128,128); 
375
376  sw32 i,j; 
377
378
379  r1_setup_decompression(&regular_format,&chroma_format,&alpha_format,R1_CHROMA_COLOR,square_textures);
380
381  i4_file_class *dst_file = i4_open(r1_get_cache_file(),I4_WRITE | I4_NO_BUFFER);
382
383  tex_cache_header_t tex_cache_header;
384
385  memcpy(&tex_cache_header.regular_format,&regular_format,sizeof(i4_pixel_format));
386  memcpy(&tex_cache_header.chroma_format ,&chroma_format, sizeof(i4_pixel_format));
387  memcpy(&tex_cache_header.alpha_format  ,&alpha_format,  sizeof(i4_pixel_format));
388
389  tex_cache_header.max_mip_dimention = max_texture_dimention;
390  tex_cache_header.num_entries       = texture_file_ids.size();
391
392  tex_cache_header.write(dst_file); 
393 
394  tex_cache_entry_t *tex_cache_entries = 0;
395
396  if (texture_file_ids.size())
397  {
398    tex_cache_entries = (tex_cache_entry_t *)
399      i4_malloc(sizeof(tex_cache_entry_t) * texture_file_ids.size(),"tex_cache_entries");
400  }
401
402  //the entries will go at the end of the file. to get to the start of the entry list,
403  //seek to:   filesize()-(num_entries*sizeof(tex_cache_entry_t))
404
405
406 
407
408  i4_status_class *stat=i4_create_status(r1_gets("checking_times"), I4_STATUS_UNKNOWN_TOTAL);
409
410  i4_directory_struct ds;
411  i4_get_directory(network_dir, ds, i4_T, stat);
412  delete stat;
413
414  for (i=0; i<ds.tfiles; i++)
415    network_file_ids.add(r1_get_file_id(*ds.files[i]));   
416
417  stat = i4_create_status(r1_gets("building_texture_cache"));
418 
419  if (network_file_ids.size() != ds.tfiles)
420    i4_error("something bad happened building the texture cache file");
421 
422  for (i=0; i<texture_file_ids.size(); i++)
423  {   
424    w32 id = texture_file_ids[i];   
425   
426    w32 file_date = 0xFFFFFFFF;
427   
428    for (j=0; j<network_file_ids.size(); j++)
429    {
430      if (id==network_file_ids[j])
431      {
432        file_date = ds.file_status[j].last_modified;
433        break;
434      }
435    }       
436
437    //we need to decompress (for now, just copy)
438    //the network (or cd image) texture to our hard drive
439    do_single_file(dst_file,
440                   id,                   
441                   max_texture_dimention,
442                   file_date,
443                   &tex_cache_entries[i]);
444       
445   
446    if (stat)
447      stat->update((float)(i+1) / (float)texture_file_ids.size());
448  }
449
450  if (stat)
451    delete stat;
452
453
454  if (tex_cache_entries)   
455  {
456    for (i=0; i<tex_cache_header.num_entries; i++)
457    {
458      tex_cache_entries[i].write(dst_file);
459    }   
460    i4_free(tex_cache_entries);
461  }
462
463  sw32 cache_size = dst_file->tell() + 4;
464
465  dst_file->write_32(cache_size);
466
467  delete dst_file; 
468
469  r1_end_decompression(); 
470 
471
472
473  return i4_T;
474}
475
476
477
478void do_single_file(i4_file_class *dst_file,
479                    w32 id,                   
480                    sw32 max_texture_dimention,
481                    w32 network_file_date,
482                    tex_cache_entry_t *t)
483{
484
485  i4_str *local_fname   = r1_texture_id_to_filename(id, r1_get_decompressed_dir());
486  i4_str *network_fname = r1_texture_id_to_filename(id, r1_get_compressed_dir());
487
488  char local_tex_file[256];
489  i4_os_string(*local_fname,local_tex_file,256);
490  delete local_fname;
491   
492  char network_tex_file[256];
493  i4_os_string(*network_fname,network_tex_file,256);   
494  delete network_fname;
495
496  i4_bool loaded = i4_F;
497     
498  w32 file_offs = dst_file->tell();
499
500  //open the mip file from the network to get information on the mip levels   
501  mipheader_t mipheader;
502
503  i4_file_class *src_file = i4_open(i4_const_str(network_tex_file),I4_READ | I4_NO_BUFFER);
504  if (!src_file)
505    i4_warning("Couldn't open compressed texture: %s.", network_tex_file);
506   
507  void *file_buf = 0;
508  w32   file_size;
509
510  if (src_file)
511  {
512    file_size = src_file->size();
513    file_buf  = i4_malloc(file_size,"file buffer");
514 
515    src_file->read(file_buf,file_size);
516    delete src_file;
517
518    src_file = new i4_ram_file_class(file_buf,file_size);
519  }   
520
521  if (src_file)
522  {     
523    mipheader.read(src_file);
524
525    loaded = r1_decompress_to_local_mip(src_file,
526                                        dst_file,
527                                        network_tex_file,
528                                        local_tex_file,
529                                        &mipheader,
530                                        max_texture_dimention);
531     
532    if (file_buf)       
533      i4_free(file_buf);
534
535    delete src_file;
536  }       
537
538  if (loaded)   
539  {     
540    t->id             = id;     
541    t->lowmipoffset   = file_offs;
542    t->last_modified  = network_file_date;
543    t->average_color  = mipheader.average_color;
544    t->base_width     = mipheader.base_width;
545    t->base_height    = mipheader.base_height;
546    t->flags          = mipheader.flags;
547    t->num_mip_levels = mipheader.num_mip_levels;
548  }
549  else
550  {
551    i4_warning("Failed to decompress %s. Try updating textures w/the maxtool.",network_tex_file);
552   
553    //be sure to set lowmipoffset to 0xFFFFFFFF and last_modified to 0 since the load failed
554    t->id             = id;     
555    t->lowmipoffset   = 0xFFFFFFFF;
556    t->last_modified  = 0;
557    t->average_color  = 0;
558    t->base_width     = 0;
559    t->base_height    = 0;
560    t->flags          = 0;
561    t->num_mip_levels = 0;
562  } 
563}
564
565
566void r1_texture_manager_class::keep_cache_current(i4_array<w32> *file_ids)
567{
568  tex_cache_header_t tex_cache_header;
569
570  i4_file_class *cache_file = i4_open(r1_get_cache_file(),I4_READ | I4_NO_BUFFER); 
571
572  //does the cache file exist?
573  if (!cache_file)
574  {
575    //need to build a cache file
576    if (file_ids && file_ids->size())
577      build_cache_file(*file_ids, r1_get_compressed_dir(), r1_get_decompressed_dir());
578  }
579  else
580  {
581    //cache file exists, make sure its in the proper format
582    //if anything doesnt match, gotta rebuild
583    tex_cache_header.read(cache_file);   
584   
585    if (
586        !palettes_are_same(&tex_cache_header.regular_format,&regular_format) ||       
587        !palettes_are_same(&tex_cache_header.chroma_format,&chroma_format)   ||
588        !palettes_are_same(&tex_cache_header.alpha_format,&alpha_format)     ||
589        (tex_cache_header.max_mip_dimention < max_texture_dimention)
590       )
591    {
592      //close the old cache file
593      if (tex_cache_header.entries)
594        i4_free(tex_cache_header.entries);
595
596      delete cache_file;
597      cache_file = 0;
598
599      //build a new one
600      if (file_ids && file_ids->size())
601        build_cache_file(*file_ids,
602                         r1_get_compressed_dir(),
603                         r1_get_decompressed_dir());
604
605    }
606    else
607    if (file_ids && file_ids->size())
608    {
609      //cache file was there (and in a compatible format) but we need to make sure it is up to date           
610 
611      //yes. get directory information from the source compressed texture directory
612      i4_directory_struct ds;
613      i4_get_directory(r1_get_compressed_dir(), ds, i4_T);
614
615      i4_array<file_status_sort_struct> sorted_status_indices(128,128);
616     
617      sw32 i;
618
619      for (i=0; i<ds.tfiles; i++)
620      {
621        file_status_sort_struct s;
622        s.id = r1_get_file_id(*(ds.files[i]));
623        s.file_status_index = i;
624       
625        sorted_status_indices.add(s);
626      }
627     
628      sorted_status_indices.sort(file_status_struct_compare);
629
630      i4_array<w32> ids_needing_update(128,128);
631
632      //determine which textures need to be updated,
633      //and also determine if there are any NEW ones
634      //that need to be added to the cache
635
636      for (i=0; i<file_ids->size(); i++)
637      {
638        w32 id = (*file_ids)[i];//r1_get_file_id(*ds.files[i]);
639
640        tex_cache_entry_t *t = find_id_in_tex_cache(tex_cache_header.entries,
641                                                    tex_cache_header.num_entries,id);
642       
643        if (!t || (t->lowmipoffset==0xFFFFFFFF))
644        {
645          if (t && t->lowmipoffset==0xFFFFFFFF)
646          {
647            //its not in the cache right now because the file didnt exist before
648            //if it exists now, add it to the update list. if not, well dont even try
649            //(this avoids creating an "update textures" wait everytime if a bunch of texture files
650            // just dont exist)
651            file_status_sort_struct s;
652            s.id = id;
653
654            sw32 location = sorted_status_indices.binary_search(&s,file_status_struct_compare);
655            if (location != -1)
656            {
657              //yeah, its in the directory, add it to the update list
658              ids_needing_update.add(id);
659            }
660          }
661          else
662          {
663            //its not in the cache. add it
664            ids_needing_update.add(id);
665          }
666        }
667        else
668        {
669          //it is in the cache. make sure its current. if the file does not exist, dont
670          //add it
671
672          file_status_sort_struct s;
673          s.id = id;
674
675          if (id==0x0f6dbf86)
676          {
677            int a;
678            a=0;
679          }
680
681          sw32 location = sorted_status_indices.binary_search(&s,file_status_struct_compare);
682
683          //it exists in the directory. if the directory file is more recent than ours, update it.
684          //if it doesnt exist, well then obviously dont update it.
685          if (location != -1)
686          {
687            if (ds.file_status[sorted_status_indices[location].file_status_index].last_modified > t->last_modified)
688            {
689              //must update this texture
690              ids_needing_update.add(id);
691            }
692          }
693        }
694      }
695   
696
697      if (tex_cache_header.entries)
698        i4_free(tex_cache_header.entries);
699
700      delete cache_file;
701        cache_file = 0;
702
703      //does anything need updating?
704      if (ids_needing_update.size())
705      {
706        //yes. delete this old data and re-read it after we've updated the cache file     
707        update_cache_file(ids_needing_update,
708                          r1_get_compressed_dir(),
709                          r1_get_decompressed_dir());
710      }
711    }
712    else
713    {
714      if (tex_cache_header.entries)
715        i4_free(tex_cache_header.entries);
716
717      delete cache_file;
718      cache_file = 0;
719    }
720  }
721
722  if (cache_file)
723  {
724    i4_warning("unhandled case: keep_cache_current");
725  }
726}
Note: See TracBrowser for help on using the repository browser.