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