Panda3D
 All Classes Functions Variables Enumerations
decompressor.cxx
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
 All Classes Functions Variables Enumerations