/**********************************************************************
This file is part of Crack dot Com's free source code release of
Golgotha.
for
information about compiling & licensing issues visit this URL
If that doesn't help, contact Jonathan Clark at
golgotha_source@usa.net (Subject should have "GOLG" in it)
***********************************************************************/
//WARNING: this code is really nauseating.
#include "tex_cache.hh"
#include "mip_average.hh"
#include "status/status.hh"
#include "file/file.hh"
#include "error/error.hh"
#include "r1_res.hh"
void do_single_file(i4_file_class *dst_file,
w32 id,
sw32 max_texture_dimention,
w32 network_file_date,
tex_cache_entry_t *t);
struct file_status_sort_struct
{
w32 id;
w32 file_status_index;
};
int file_status_struct_compare(const file_status_sort_struct *a, const file_status_sort_struct *b)
{
if (a->idid)
return -1;
else if (a->id>b->id)
return 1;
else return 0;
}
void tex_cache_entry_t::write(i4_file_class *f)
{
if (!f)
{
i4_warning("tex_cache_entry_t::write() NULL file");
return;
}
f->write_32(id);
f->write_32(lowmipoffset);
f->write_32(average_color);
f->write_32(last_modified);
f->write_16(base_width);
f->write_16(base_height);
f->write_8(flags);
f->write_8(num_mip_levels);
}
void tex_cache_entry_t::read(i4_file_class *f)
{
if (!f)
{
i4_warning("tex_cache_entry_t::read() NULL file");
return;
}
id = f->read_32();
lowmipoffset = f->read_32();
average_color = f->read_32();
last_modified = f->read_32();
base_width = f->read_16();
base_height = f->read_16();
flags = f->read_8();
num_mip_levels = f->read_8();
}
void tex_cache_header_t::write(i4_file_class *f)
{
if (!f)
{
i4_warning("tex_cache_header_t::write() NULL file");
return;
}
regular_format.write(f);
chroma_format.write(f);
alpha_format.write(f);
f->write_32(max_mip_dimention);
f->write_32(num_entries);
}
void tex_cache_header_t::read(i4_file_class *f)
{
if (!f)
{
i4_warning("tex_cache_header_t::read() NULL file");
return;
}
if (f->size() < tex_cache_header_disk_size())
{
i4_warning("texture cache file corrupt");
regular_format.default_format();
chroma_format.default_format();
alpha_format.default_format();
max_mip_dimention = 0;
num_entries = 0;
entries = 0;
return;
}
//seek to the end and read the last dword
f->seek(f->size()-4);
sw32 expected_size = f->read_32();
if (expected_size != f->size())
{
i4_warning("texture cache file corrupt");
entries = 0;
num_entries = 0;
return;
}
//seek back to the beginning and start reading the file
f->seek(0);
regular_format.read(f);
chroma_format.read(f);
alpha_format.read(f);
max_mip_dimention = f->read_32();
num_entries = f->read_32();
w32 entry_lump_size = tex_cache_entry_disk_size() * num_entries;
//read in the entries
if (entry_lump_size)
{
entries = (tex_cache_entry_t *)i4_malloc(sizeof(tex_cache_entry_t) * num_entries,"tex_cache_entries");
f->seek(f->size() - 4 - entry_lump_size);
}
else
{
num_entries = 0;
entries = 0;
}
sw32 i;
for (i=0; i &update_ids,
const i4_const_str &network_dir,
const i4_const_str &local_dir)
{
sw32 i,j,k;
i4_array network_file_ids(128,128);
i4_array new_cache_entries(128,128);
i4_status_class *stat=i4_create_status(r1_gets("checking_times"), I4_STATUS_UNKNOWN_TOTAL);
i4_directory_struct ds;
i4_get_directory(network_dir, ds, i4_T, stat);
delete stat;
for (i=0; isize();
void *old_cache_file_data = i4_malloc(csize,"old cache file data");
old_cache_file->read(old_cache_file_data,csize);
delete old_cache_file;
old_cache_file = new i4_ram_file_class(old_cache_file_data,csize);
tex_cache_header_t old_cache_header;
old_cache_header.read(old_cache_file);
w32 total_cache_entries = old_cache_header.num_entries;
w32 old_cache_header_num_entries = old_cache_header.num_entries;
for (i=0; i < update_ids.size();)
{
tex_cache_entry_t *t = find_id_in_tex_cache(old_cache_header.entries,old_cache_header.num_entries,update_ids[i]);
if (t)
{
update_ids.remove(i); //pull this out of the list
t->flags |= R1_MIP_EXTRA_FLAG;
}
else
{
//new textures going into the cache file
total_cache_entries++;
i++;
}
}
i4_file_class *new_cache_file = i4_open(r1_get_cache_file(), I4_WRITE | I4_NO_BUFFER);
old_cache_header.num_entries = total_cache_entries;
old_cache_header.write(new_cache_file);
r1_setup_decompression(®ular_format,&chroma_format,&alpha_format,R1_CHROMA_COLOR,square_textures);
update_ids.sort(w32_compare);
j = 0;
i = 0;
i4_bool i_done = i4_F;
i4_bool j_done = i4_F;
stat = i4_create_status(r1_gets("updating_texture_cache"));
while (1)
{
if (j>=update_ids.size()) j_done = i4_T;
if (i>=old_cache_header_num_entries) i_done = i4_T;
if (i_done && j_done) break;
w32 file_offs = new_cache_file->tell();
if ((j_done && !i_done) || (!i_done && old_cache_header.entries[i].id < update_ids[j]))
{
tex_cache_entry_t *t = &old_cache_header.entries[i];
if (!(t->flags & R1_MIP_EXTRA_FLAG))
{
if (t->lowmipoffset != 0xFFFFFFFF)
{
old_cache_file->seek(t->lowmipoffset);
sw32 entry_size = sizeof(sw32) * 2 +
t->base_width / (1<<(t->num_mip_levels-1)) *
t->base_height / (1<<(t->num_mip_levels-1)) * 2;
t->lowmipoffset = file_offs;
w8 copy_buffer[2048];
while (entry_size)
{
sw32 copy_size = (entry_size < 2048) ? (entry_size) : (2048);
old_cache_file->read(copy_buffer, copy_size);
new_cache_file->write(copy_buffer,copy_size);
entry_size -= copy_size;
}
}
}
else
{
t->flags &= (~R1_MIP_EXTRA_FLAG);
w32 file_date=0xFFFFFFFF;
//have to update this texture
for (k=0; kid==network_file_ids[k])
{
file_date = ds.file_status[k].last_modified;
break;
}
}
//dont want files in the cache w/different mip levels.
do_single_file(new_cache_file,
t->id,
old_cache_header.max_mip_dimention,
file_date,
t);
}
new_cache_entries.add(*t);
i++;
}
else
if ((i_done && !j_done) || (!j_done && update_ids[j] < old_cache_header.entries[i].id))
{
//have to add this id
w32 new_id = update_ids[j];
w32 file_date=0xFFFFFFFF;
//have to update this texture
for (k=0; kupdate((new_cache_entries.size()+1) / (float)total_cache_entries);
}
if (stat)
delete stat;
if (old_cache_header.entries)
i4_free(old_cache_header.entries);
i4_free(old_cache_file_data);
delete old_cache_file;
r1_end_decompression();
if (total_cache_entries != new_cache_entries.size())
{
i4_warning("error updating the texture cache file");
}
for (i=0;itell() + 4;
new_cache_file->write_32(cache_size);
delete new_cache_file;
return i4_T;
}
i4_bool r1_texture_manager_class::build_cache_file(i4_array &texture_file_ids,
const i4_const_str &network_dir,
const i4_const_str &local_dir)
{
//creates a tex_cache.dat "directory" w/lowest mip levels and mipheader_t info (tex_cache_entry_t, actually)
//decompresses others to local .gtx files
i4_array network_file_ids(128,128);
sw32 i,j;
r1_setup_decompression(®ular_format,&chroma_format,&alpha_format,R1_CHROMA_COLOR,square_textures);
i4_file_class *dst_file = i4_open(r1_get_cache_file(),I4_WRITE | I4_NO_BUFFER);
tex_cache_header_t tex_cache_header;
memcpy(&tex_cache_header.regular_format,®ular_format,sizeof(i4_pixel_format));
memcpy(&tex_cache_header.chroma_format ,&chroma_format, sizeof(i4_pixel_format));
memcpy(&tex_cache_header.alpha_format ,&alpha_format, sizeof(i4_pixel_format));
tex_cache_header.max_mip_dimention = max_texture_dimention;
tex_cache_header.num_entries = texture_file_ids.size();
tex_cache_header.write(dst_file);
tex_cache_entry_t *tex_cache_entries = 0;
if (texture_file_ids.size())
{
tex_cache_entries = (tex_cache_entry_t *)
i4_malloc(sizeof(tex_cache_entry_t) * texture_file_ids.size(),"tex_cache_entries");
}
//the entries will go at the end of the file. to get to the start of the entry list,
//seek to: filesize()-(num_entries*sizeof(tex_cache_entry_t))
i4_status_class *stat=i4_create_status(r1_gets("checking_times"), I4_STATUS_UNKNOWN_TOTAL);
i4_directory_struct ds;
i4_get_directory(network_dir, ds, i4_T, stat);
delete stat;
for (i=0; iupdate((float)(i+1) / (float)texture_file_ids.size());
}
if (stat)
delete stat;
if (tex_cache_entries)
{
for (i=0; itell() + 4;
dst_file->write_32(cache_size);
delete dst_file;
r1_end_decompression();
return i4_T;
}
void do_single_file(i4_file_class *dst_file,
w32 id,
sw32 max_texture_dimention,
w32 network_file_date,
tex_cache_entry_t *t)
{
i4_str *local_fname = r1_texture_id_to_filename(id, r1_get_decompressed_dir());
i4_str *network_fname = r1_texture_id_to_filename(id, r1_get_compressed_dir());
char local_tex_file[256];
i4_os_string(*local_fname,local_tex_file,256);
delete local_fname;
char network_tex_file[256];
i4_os_string(*network_fname,network_tex_file,256);
delete network_fname;
i4_bool loaded = i4_F;
w32 file_offs = dst_file->tell();
//open the mip file from the network to get information on the mip levels
mipheader_t mipheader;
i4_file_class *src_file = i4_open(i4_const_str(network_tex_file),I4_READ | I4_NO_BUFFER);
if (!src_file)
i4_warning("Couldn't open compressed texture: %s.", network_tex_file);
void *file_buf = 0;
w32 file_size;
if (src_file)
{
file_size = src_file->size();
file_buf = i4_malloc(file_size,"file buffer");
src_file->read(file_buf,file_size);
delete src_file;
src_file = new i4_ram_file_class(file_buf,file_size);
}
if (src_file)
{
mipheader.read(src_file);
loaded = r1_decompress_to_local_mip(src_file,
dst_file,
network_tex_file,
local_tex_file,
&mipheader,
max_texture_dimention);
if (file_buf)
i4_free(file_buf);
delete src_file;
}
if (loaded)
{
t->id = id;
t->lowmipoffset = file_offs;
t->last_modified = network_file_date;
t->average_color = mipheader.average_color;
t->base_width = mipheader.base_width;
t->base_height = mipheader.base_height;
t->flags = mipheader.flags;
t->num_mip_levels = mipheader.num_mip_levels;
}
else
{
i4_warning("Failed to decompress %s. Try updating textures w/the maxtool.",network_tex_file);
//be sure to set lowmipoffset to 0xFFFFFFFF and last_modified to 0 since the load failed
t->id = id;
t->lowmipoffset = 0xFFFFFFFF;
t->last_modified = 0;
t->average_color = 0;
t->base_width = 0;
t->base_height = 0;
t->flags = 0;
t->num_mip_levels = 0;
}
}
void r1_texture_manager_class::keep_cache_current(i4_array *file_ids)
{
tex_cache_header_t tex_cache_header;
i4_file_class *cache_file = i4_open(r1_get_cache_file(),I4_READ | I4_NO_BUFFER);
//does the cache file exist?
if (!cache_file)
{
//need to build a cache file
if (file_ids && file_ids->size())
build_cache_file(*file_ids, r1_get_compressed_dir(), r1_get_decompressed_dir());
}
else
{
//cache file exists, make sure its in the proper format
//if anything doesnt match, gotta rebuild
tex_cache_header.read(cache_file);
if (
!palettes_are_same(&tex_cache_header.regular_format,®ular_format) ||
!palettes_are_same(&tex_cache_header.chroma_format,&chroma_format) ||
!palettes_are_same(&tex_cache_header.alpha_format,&alpha_format) ||
(tex_cache_header.max_mip_dimention < max_texture_dimention)
)
{
//close the old cache file
if (tex_cache_header.entries)
i4_free(tex_cache_header.entries);
delete cache_file;
cache_file = 0;
//build a new one
if (file_ids && file_ids->size())
build_cache_file(*file_ids,
r1_get_compressed_dir(),
r1_get_decompressed_dir());
}
else
if (file_ids && file_ids->size())
{
//cache file was there (and in a compatible format) but we need to make sure it is up to date
//yes. get directory information from the source compressed texture directory
i4_directory_struct ds;
i4_get_directory(r1_get_compressed_dir(), ds, i4_T);
i4_array sorted_status_indices(128,128);
sw32 i;
for (i=0; i ids_needing_update(128,128);
//determine which textures need to be updated,
//and also determine if there are any NEW ones
//that need to be added to the cache
for (i=0; isize(); i++)
{
w32 id = (*file_ids)[i];//r1_get_file_id(*ds.files[i]);
tex_cache_entry_t *t = find_id_in_tex_cache(tex_cache_header.entries,
tex_cache_header.num_entries,id);
if (!t || (t->lowmipoffset==0xFFFFFFFF))
{
if (t && t->lowmipoffset==0xFFFFFFFF)
{
//its not in the cache right now because the file didnt exist before
//if it exists now, add it to the update list. if not, well dont even try
//(this avoids creating an "update textures" wait everytime if a bunch of texture files
// just dont exist)
file_status_sort_struct s;
s.id = id;
sw32 location = sorted_status_indices.binary_search(&s,file_status_struct_compare);
if (location != -1)
{
//yeah, its in the directory, add it to the update list
ids_needing_update.add(id);
}
}
else
{
//its not in the cache. add it
ids_needing_update.add(id);
}
}
else
{
//it is in the cache. make sure its current. if the file does not exist, dont
//add it
file_status_sort_struct s;
s.id = id;
if (id==0x0f6dbf86)
{
int a;
a=0;
}
sw32 location = sorted_status_indices.binary_search(&s,file_status_struct_compare);
//it exists in the directory. if the directory file is more recent than ours, update it.
//if it doesnt exist, well then obviously dont update it.
if (location != -1)
{
if (ds.file_status[sorted_status_indices[location].file_status_index].last_modified > t->last_modified)
{
//must update this texture
ids_needing_update.add(id);
}
}
}
}
if (tex_cache_header.entries)
i4_free(tex_cache_header.entries);
delete cache_file;
cache_file = 0;
//does anything need updating?
if (ids_needing_update.size())
{
//yes. delete this old data and re-read it after we've updated the cache file
update_cache_file(ids_needing_update,
r1_get_compressed_dir(),
r1_get_decompressed_dir());
}
}
else
{
if (tex_cache_header.entries)
i4_free(tex_cache_header.entries);
delete cache_file;
cache_file = 0;
}
}
if (cache_file)
{
i4_warning("unhandled case: keep_cache_current");
}
}