00001 // Filename: decompressor.cxx 00002 // Created by: mike (09Jan97) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved 00008 // 00009 // All use of this software is subject to the terms of the Panda 3d 00010 // Software license. You should have received a copy of this license 00011 // along with this source code; you will also find a current copy of 00012 // the license at http://www.panda3d.org/license.txt . 00013 // 00014 // To contact the maintainers of this program write to 00015 // panda3d@yahoogroups.com . 00016 // 00017 //////////////////////////////////////////////////////////////////// 00018 00019 // This file is compiled only if we have zlib installed. 00020 00021 #include "config_downloader.h" 00022 00023 #include "error_utils.h" 00024 #include "filename.h" 00025 #include "buffer.h" 00026 #include "zStream.h" 00027 #include "config_express.h" 00028 00029 #include "decompressor.h" 00030 00031 #include <stdio.h> 00032 #include <errno.h> 00033 00034 //////////////////////////////////////////////////////////////////// 00035 // Function: Decompressor::Constructor 00036 // Access: Public 00037 // Description: 00038 //////////////////////////////////////////////////////////////////// 00039 Decompressor:: 00040 Decompressor() { 00041 _source = NULL; 00042 _decompress = NULL; 00043 _dest = NULL; 00044 } 00045 00046 //////////////////////////////////////////////////////////////////// 00047 // Function: Decompressor::Destructor 00048 // Access: Public 00049 // Description: 00050 //////////////////////////////////////////////////////////////////// 00051 Decompressor:: 00052 ~Decompressor() { 00053 cleanup(); 00054 } 00055 00056 //////////////////////////////////////////////////////////////////// 00057 // Function: Decompressor::initiate 00058 // Access: Public 00059 // Description: Begins a background decompression of the named file 00060 // (whose filename must end in ".pz") to a new file 00061 // without the .pz extension. The source file is 00062 // removed after successful completion. 00063 //////////////////////////////////////////////////////////////////// 00064 int Decompressor:: 00065 initiate(const Filename &source_file) { 00066 string extension = source_file.get_extension(); 00067 if (extension == "pz") { 00068 Filename dest_file = source_file; 00069 dest_file = source_file.get_fullpath_wo_extension(); 00070 return initiate(source_file, dest_file); 00071 } 00072 00073 if (downloader_cat.is_debug()) { 00074 downloader_cat.debug() 00075 << "Unknown file extension for decompressor: ." 00076 << extension << endl; 00077 } 00078 return EU_error_abort; 00079 } 00080 00081 //////////////////////////////////////////////////////////////////// 00082 // Function: Decompressor::initiate 00083 // Access: Public 00084 // Description: Begins a background decompression from the named 00085 // source file to the named destination file. The 00086 // source file is removed after successful completion. 00087 //////////////////////////////////////////////////////////////////// 00088 int Decompressor:: 00089 initiate(const Filename &source_file, const Filename &dest_file) { 00090 cleanup(); 00091 00092 // Open source file 00093 _source_filename = Filename(source_file); 00094 _source_filename.set_binary(); 00095 00096 ifstream *source_fstream = new ifstream; 00097 _source = source_fstream; 00098 if (!_source_filename.open_read(*source_fstream)) { 00099 downloader_cat.error() 00100 << "Unable to read " << _source_filename << "\n"; 00101 return get_write_error(); 00102 } 00103 00104 // Determine source file length 00105 source_fstream->seekg(0, ios::end); 00106 _source_length = source_fstream->tellg(); 00107 if (_source_length == 0) { 00108 downloader_cat.warning() 00109 << "Zero length file: " << source_file << "\n"; 00110 return EU_error_file_empty; 00111 } 00112 source_fstream->seekg(0, ios::beg); 00113 00114 // Open destination file 00115 Filename dest_filename(dest_file); 00116 dest_filename.set_binary(); 00117 00118 ofstream *dest_fstream = new ofstream; 00119 _dest = dest_fstream; 00120 if (dest_filename.exists()) { 00121 downloader_cat.info() 00122 << dest_filename << " already exists, removing.\n"; 00123 if (!dest_filename.unlink()) { 00124 downloader_cat.error() 00125 << "Unable to remove old " << dest_filename << "\n"; 00126 return get_write_error(); 00127 } 00128 } else { 00129 if (downloader_cat.is_debug()) { 00130 downloader_cat.debug() 00131 << dest_filename << " does not already exist.\n"; 00132 } 00133 } 00134 if (!dest_filename.open_write(*dest_fstream, true)) { 00135 downloader_cat.error() 00136 << "Unable to write to " << dest_filename << "\n"; 00137 return get_write_error(); 00138 } 00139 00140 // Now create the decompressor stream. 00141 _decompress = new IDecompressStream(_source, false); 00142 return EU_success; 00143 } 00144 00145 //////////////////////////////////////////////////////////////////// 00146 // Function: Decompressor::run 00147 // Access: Public 00148 // Description: Called each frame to do the next bit of work in the 00149 // background task. Returns EU_ok if a chunk is 00150 // completed but there is more to go, or EU_success when 00151 // we're all done. Any other return value indicates an 00152 // error. 00153 //////////////////////////////////////////////////////////////////// 00154 int Decompressor:: 00155 run() { 00156 if (_decompress == (istream *)NULL) { 00157 // Hmm, we were already done. 00158 return EU_success; 00159 } 00160 00161 // Read a bunch of characters from the decompress stream, but no 00162 // more than decompressor_buffer_size. 00163 int count = 0; 00164 int ch = _decompress->get(); 00165 while (!_decompress->eof() && !_decompress->fail()) { 00166 _dest->put(ch); 00167 if (++count >= decompressor_buffer_size) { 00168 // That's enough for now. 00169 return EU_ok; 00170 } 00171 00172 ch = _decompress->get(); 00173 } 00174 00175 // All done! 00176 cleanup(); 00177 if (!keep_temporary_files) { 00178 _source_filename.unlink(); 00179 } 00180 return EU_success; 00181 } 00182 00183 //////////////////////////////////////////////////////////////////// 00184 // Function: Decompressor::decompress 00185 // Access: Public 00186 // Description: Performs a foreground decompression of the named 00187 // file; does not return until the decompression is 00188 // complete. 00189 //////////////////////////////////////////////////////////////////// 00190 bool Decompressor:: 00191 decompress(const Filename &source_file) { 00192 int ret = initiate(source_file); 00193 if (ret < 0) 00194 return false; 00195 00196 int ch = _decompress->get(); 00197 while (!_decompress->eof() && !_decompress->fail()) { 00198 _dest->put(ch); 00199 ch = _decompress->get(); 00200 } 00201 00202 cleanup(); 00203 if (!keep_temporary_files) { 00204 _source_filename.unlink(); 00205 } 00206 return true; 00207 } 00208 00209 //////////////////////////////////////////////////////////////////// 00210 // Function: Decompressor::decompress 00211 // Access: Public 00212 // Description: Does an in-memory decompression of the indicated 00213 // Ramfile. The decompressed contents are written back 00214 // into the same Ramfile on completion. 00215 //////////////////////////////////////////////////////////////////// 00216 bool Decompressor:: 00217 decompress(Ramfile &source_and_dest_file) { 00218 istringstream source(source_and_dest_file._data); 00219 ostringstream dest; 00220 00221 IDecompressStream decompress(&source, false); 00222 00223 int ch = decompress.get(); 00224 while (!decompress.eof() && !decompress.fail()) { 00225 dest.put(ch); 00226 ch = decompress.get(); 00227 } 00228 00229 source_and_dest_file._pos = 0; 00230 source_and_dest_file._data = dest.str(); 00231 return true; 00232 } 00233 00234 //////////////////////////////////////////////////////////////////// 00235 // Function: Decompressor::get_progress 00236 // Access: Public 00237 // Description: Returns the ratio through the decompression step 00238 // in the background. 00239 //////////////////////////////////////////////////////////////////// 00240 float Decompressor:: 00241 get_progress() const { 00242 if (_decompress == (istream *)NULL) { 00243 // Hmm, we were already done. 00244 return 1.0f; 00245 } 00246 00247 nassertr(_source_length > 0, 0.0); 00248 size_t source_pos = _source->tellg(); 00249 00250 // We stop the scale at 0.99 because there may be a little bit more 00251 // to do even after the decompressor has read all of the source. 00252 return (0.99f * (float)source_pos / (float)_source_length); 00253 } 00254 00255 //////////////////////////////////////////////////////////////////// 00256 // Function: Decompressor::cleanup 00257 // Access: Private 00258 // Description: Called to reset a previous decompressor state and 00259 // clean up properly. 00260 //////////////////////////////////////////////////////////////////// 00261 void Decompressor:: 00262 cleanup() { 00263 if (_source != (istream *)NULL) { 00264 delete _source; 00265 _source = NULL; 00266 } 00267 if (_dest != (ostream *)NULL) { 00268 delete _dest; 00269 _dest = NULL; 00270 } 00271 if (_decompress != (istream *)NULL) { 00272 delete _decompress; 00273 _decompress = NULL; 00274 } 00275 }