/* ** ClanLib SDK ** Copyright (c) 1997-2005 The ClanLib Team ** ** This software is provided 'as-is', without any express or implied ** warranty. In no event will the authors be held liable for any damages ** arising from the use of this software. ** ** Permission is granted to anyone to use this software for any purpose, ** including commercial applications, and to alter it and redistribute it ** freely, subject to the following restrictions: ** ** 1. The origin of this software must not be misrepresented; you must not ** claim that you wrote the original software. If you use this software ** in a product, an acknowledgment in the product documentation would be ** appreciated but is not required. ** 2. Altered source versions must be plainly marked as such, and must not be ** misrepresented as being the original software. ** 3. This notice may not be removed or altered from any source distribution. ** ** Note: Some of the libraries ClanLib may link to may have additional ** requirements or restrictions. ** ** File Author(s): ** ** Magnus Norddahl ** (if your name is missing here, please add it) */ #include "Core/precomp.h" #include "datafile_inputprovider.h" #include "API/Core/System/cl_assert.h" #ifndef WIN32 #include #else #include #endif #include #ifdef WIN32 #define OPENFLAGS O_RDONLY|O_BINARY #else #define OPENFLAGS O_RDONLY #endif char datafile_id[]="ClanSoft datafile version 4.0"; CL_InputSourceProvider *CL_InputSourceProvider::create_datafile_provider(const std::string &filename) { return new CL_InputSourceProvider_Datafile(filename); } CL_InputSourceProvider_Datafile::CL_InputSourceProvider_Datafile(const std::string &_datafile) : resource_cache(NULL), datafile_handle(-1) { datafile = _datafile; open(); load_resource_ids(); } CL_InputSourceProvider_Datafile::CL_InputSourceProvider_Datafile(CL_InputSourceProvider_Datafile *other) { datafile = other->datafile; resource_cache = other->resource_cache; resource_cache->addref(); datafile_handle = dup(other->datafile_handle); } CL_InputSourceProvider_Datafile::~CL_InputSourceProvider_Datafile() { if (resource_cache != NULL) resource_cache->release(); if (datafile_handle != -1) ::close(datafile_handle); } CL_InputSource *CL_InputSourceProvider_Datafile::open_source(const std::string &filename) { if (datafile_handle == -1) open(); return new CL_InputSource_Datafile(filename, this); } CL_InputSourceProvider *CL_InputSourceProvider_Datafile::clone() { return new CL_InputSourceProvider_Datafile(this); } void CL_InputSourceProvider_Datafile::open() { datafile_handle = ::open(datafile.c_str(), OPENFLAGS); if (datafile_handle == -1) { std::string err = std::string("Could not open datafile ") + datafile; throw CL_Error(err); } int id_len = strlen(datafile_id); char *temp = new char[id_len+1]; ::read(datafile_handle, temp, id_len); temp[id_len] = 0; if (strcmp(temp, datafile_id) != 0) { ::close(datafile_handle); datafile_handle = -1; delete[] temp; throw CL_Error("Invalid datafile format"); } delete[] temp; } void CL_InputSourceProvider_Datafile::load_resource_ids() { resource_cache = new IndexLocationCache; lseek(datafile_handle, strlen(datafile_id), SEEK_SET); // Skip file ID int index_pos; ::read(datafile_handle, &index_pos, sizeof(int)); lseek(datafile_handle, index_pos, SEEK_SET); int num_indexes = 0; ::read(datafile_handle, &num_indexes, sizeof(int)); for (int i=0; iinsert(objname, objpos, objsize); delete[] objname; } } std::string CL_InputSourceProvider_Datafile::get_pathname(const std::string &filename) { return filename; } CL_InputSourceProvider *CL_InputSourceProvider_Datafile::create_relative(const std::string &path) { return clone(); } /************************** CL_InputSource_Datafile **************************/ CL_InputSource_Datafile::CL_InputSource_Datafile( const std::string &filename, CL_InputSourceProvider_Datafile *provider) : filename(filename), provider(provider), index_open(0) { open(); } CL_InputSource_Datafile::CL_InputSource_Datafile(const CL_InputSource_Datafile *source) { filename = source->filename; provider = source->provider; index_open = 0; open(); } CL_InputSource_Datafile::~CL_InputSource_Datafile() { close(); } int CL_InputSource_Datafile::read(void *addr, int size) { if (compressed) { int svar = gzread(gzfile, addr, size); seek_pos += svar; return svar; } else { if (seek_pos + size > objsize) size = objsize - seek_pos; int svar = ::read(datafile_handle, addr, size); seek_pos += svar; return svar; } } void CL_InputSource_Datafile::open() { if (index_open) close(); int pos, size; if (provider->lookup_resource(filename, pos, size)) { datafile_handle = dup(provider->get_handle()); lseek(datafile_handle, pos, SEEK_SET); size--; ::read(datafile_handle, &compressed, sizeof(char)); if (compressed) gzfile = gzdopen(datafile_handle, "rb"); index_open = 1; seek_pos = 0; objsize = size; index_pos = pos+1; } else { std::string err("ClanLib: Couldn't find datafile index: "); err += std::string(filename); throw CL_Error(err); } } void CL_InputSource_Datafile::close() { if (index_open == 0) return; index_open=0; if (compressed) gzclose(gzfile); else ::close(datafile_handle); } CL_InputSource *CL_InputSource_Datafile::clone() const { return new CL_InputSource_Datafile(this); } int CL_InputSource_Datafile::tell() const { return seek_pos; } void CL_InputSource_Datafile::seek(int pos, SeekEnum seek_type) { if (compressed) { if (seek_type == seek_cur) { if (pos > 0) { char *temp = new char[pos]; read(temp, pos); delete temp; } else if (pos < 0) { seek(tell()+pos, seek_set); } return; } else if (seek_type == seek_set) { int cur_pos = tell(); if (pos >= cur_pos) { seek(pos - cur_pos, seek_cur); return; } else if (pos>=0) { /* * OK, now we have to go backwards. Since there's no * easy way to do it (think about compressed datafiles) * we close and then open the datafile. This way we are * back to position 0, and can proceed to a standard * forward seek. OK this is a performance killer * method, but it's better than nothing... */ close(); open(); seek(pos, seek_cur); return; } } throw CL_Error("ClanLib: seek() in datafiles called with an invalid position"); } else { if (seek_type == seek_set) { lseek(datafile_handle, index_pos+pos, SEEK_SET); } else if (seek_type == seek_cur) { lseek(datafile_handle, pos, SEEK_CUR); } else if (seek_type == seek_end) { lseek(datafile_handle, index_pos+objsize+pos, SEEK_SET); } } } int CL_InputSource_Datafile::size() const { return objsize; } void CL_InputSource_Datafile::push_position() { CL_Zipped_Position indexpos; indexpos.gzfile = gzfile; // indexpos.datafile_pos = lseek(datafile_handle, 0, SEEK_CUR); indexpos.seek_pos = seek_pos; index_stack.push(indexpos); index_open=0; } void CL_InputSource_Datafile::pop_position() { // if (index_open != 0) close_index(); // index_open = 1; CL_Zipped_Position pushed_index = index_stack.top(); index_stack.pop(); gzfile = pushed_index.gzfile; // lseek(datafile_handle, pushed_index.datafile_pos, SEEK_SET); seek_pos = pushed_index.seek_pos; if (!compressed) seek(seek_pos, seek_set); }