Panda3D
 All Classes Functions Variables Enumerations
extractor.cxx
00001 // Filename: extractor.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 "extractor.h"
00016 #include "config_downloader.h"
00017 #include "trueClock.h"
00018 #include "filename.h"
00019 #include "error_utils.h"
00020 
00021 
00022 ////////////////////////////////////////////////////////////////////
00023 //     Function: Extractor::Constructor
00024 //       Access: Published
00025 //  Description:
00026 ////////////////////////////////////////////////////////////////////
00027 Extractor::
00028 Extractor() {
00029   _initiated = false;
00030   _multifile = new Multifile;
00031 }
00032 
00033 ////////////////////////////////////////////////////////////////////
00034 //     Function: Extractor::Destructor
00035 //       Access: Published
00036 //  Description:
00037 ////////////////////////////////////////////////////////////////////
00038 Extractor::
00039 ~Extractor() {
00040   reset();
00041 }
00042 
00043 ////////////////////////////////////////////////////////////////////
00044 //     Function: Extractor::set_multifile
00045 //       Access: Published
00046 //  Description: Specifies the filename of the Multifile that the
00047 //               Extractor will read.  Returns true on success, false
00048 //               if the mulifile name is invalid.
00049 ////////////////////////////////////////////////////////////////////
00050 bool Extractor::
00051 set_multifile(const Filename &multifile_name) {
00052   reset();
00053   _multifile_name = multifile_name;
00054   return _multifile->open_read(multifile_name);
00055 }
00056 
00057 ////////////////////////////////////////////////////////////////////
00058 //     Function: Extractor::set_extract_dir
00059 //       Access: Published
00060 //  Description: Specifies the directory into which all extracted
00061 //               subfiles will be written.  Relative paths of subfiles
00062 //               within the Multifile will be written as relative
00063 //               paths to this directory.
00064 ////////////////////////////////////////////////////////////////////
00065 void Extractor::
00066 set_extract_dir(const Filename &extract_dir) {
00067   _extract_dir = extract_dir;
00068 }
00069 
00070 ////////////////////////////////////////////////////////////////////
00071 //     Function: Extractor::reset
00072 //       Access: Published
00073 //  Description: Interrupts the Extractor in the middle of its
00074 //               business and makes it ready to accept a new list of
00075 //               subfiles to extract.
00076 ////////////////////////////////////////////////////////////////////
00077 void Extractor::
00078 reset() {
00079   if (_initiated) {
00080     if (_read != (istream *)NULL) {
00081       Multifile::close_read_subfile(_read);
00082       _read = (istream *)NULL;
00083     }
00084     _write.close();
00085     _initiated = false;
00086   }
00087 
00088   _requests.clear();
00089   _requests_total_length = 0;
00090 }
00091 
00092 ////////////////////////////////////////////////////////////////////
00093 //     Function: Extractor::request_subfile
00094 //       Access: Published
00095 //  Description: Requests a particular subfile to be extracted when
00096 //               step() or run() is called.  Returns true if the
00097 //               subfile exists, false otherwise.
00098 ////////////////////////////////////////////////////////////////////
00099 bool Extractor::
00100 request_subfile(const Filename &subfile_name) {
00101   int index = _multifile->find_subfile(subfile_name);
00102   if (index < 0) {
00103     return false;
00104   }
00105   _requests.push_back(index);
00106   _requests_total_length += _multifile->get_subfile_length(index);
00107   return true;
00108 }
00109 
00110 ////////////////////////////////////////////////////////////////////
00111 //     Function: Extractor::request_all_subfiles
00112 //       Access: Published
00113 //  Description: Requests all subfiles in the Multifile to be
00114 //               extracted.  Returns the number requested.
00115 ////////////////////////////////////////////////////////////////////
00116 int Extractor::
00117 request_all_subfiles() {
00118   _requests.clear();
00119   _requests_total_length = 0;
00120   int num_subfiles = _multifile->get_num_subfiles();
00121   for (int i = 0; i < num_subfiles; i++) {
00122     _requests.push_back(i);
00123     _requests_total_length += _multifile->get_subfile_length(i);
00124   }
00125   return num_subfiles;
00126 }
00127 
00128 ////////////////////////////////////////////////////////////////////
00129 //     Function: Extractor::step
00130 //       Access: Published
00131 //  Description: After all of the requests have been made via
00132 //               request_file() or request_all_subfiles(), call step()
00133 //               repeatedly until it stops returning EU_ok.
00134 //
00135 //               step() extracts the next small unit of data from the
00136 //               Multifile.  Returns EU_ok if progress is continuing,
00137 //               EU_error_abort if there is a problem, or EU_success
00138 //               when the last piece has been extracted.
00139 //
00140 //               Also see run().
00141 ////////////////////////////////////////////////////////////////////
00142 int Extractor::
00143 step() {
00144   if (!_initiated) {
00145     _request_index = 0;
00146     _subfile_index = 0;
00147     _subfile_pos = 0;
00148     _subfile_length = 0;
00149     _total_bytes_extracted = 0;
00150     _read = (istream *)NULL;
00151     _initiated = true;
00152   }
00153 
00154   TrueClock *clock = TrueClock::get_global_ptr();
00155   double now = clock->get_short_time();
00156   double finish = now + extractor_step_time;
00157 
00158   do {
00159     if (_read == (istream *)NULL) {
00160       // Time to open the next subfile.
00161       if (_request_index >= (int)_requests.size()) {
00162         // All done!
00163         if (downloader_cat.is_debug()) {
00164           downloader_cat.debug()
00165             << "Finished extracting.\n";
00166         }
00167         reset();
00168         return EU_success;
00169       }
00170       
00171       _subfile_index = _requests[_request_index];
00172       _subfile_filename = Filename(_extract_dir, 
00173                                    _multifile->get_subfile_name(_subfile_index));
00174 
00175       if (downloader_cat.is_debug()) {
00176         downloader_cat.debug()
00177           << "Extracting " << _subfile_filename << ".\n";
00178       }
00179 
00180       _subfile_filename.set_binary();
00181       _subfile_filename.make_dir();
00182       if (!_subfile_filename.open_write(_write, true)) {
00183         downloader_cat.error()
00184           << "Unable to write to " << _subfile_filename << ".\n";
00185         reset();
00186         return EU_error_abort;
00187       }
00188       
00189       _subfile_length = _multifile->get_subfile_length(_subfile_index);
00190       _subfile_pos = 0;
00191       _read = _multifile->open_read_subfile(_subfile_index);
00192       if (_read == (istream *)NULL) {
00193         downloader_cat.error()
00194           << "Unable to read subfile "
00195           << _multifile->get_subfile_name(_subfile_index) << ".\n";
00196         reset();
00197         return EU_error_abort;
00198       }
00199       
00200     } else if (_subfile_pos >= _subfile_length) {
00201       // Time to close this subfile.
00202 
00203       if (downloader_cat.is_debug()) {
00204         downloader_cat.debug()
00205           << "Finished current subfile.\n";
00206       }
00207       Multifile::close_read_subfile(_read);
00208       _read = (istream *)NULL;
00209       _write.close();
00210       _request_index++;
00211       
00212     } else {
00213       // Read a number of bytes from the subfile and write them to the
00214       // output.
00215       static const size_t buffer_size = 1024;
00216       char buffer[buffer_size];
00217       
00218       size_t max_bytes = min(buffer_size, _subfile_length - _subfile_pos);
00219       _read->read(buffer, max_bytes);
00220       size_t count = _read->gcount();
00221       while (count != 0) {
00222         if (downloader_cat.is_spam()) {
00223           downloader_cat.spam()
00224             << " . . . read " << count << " bytes.\n";
00225         }
00226         _write.write(buffer, count);
00227         if (!_write) {
00228           downloader_cat.error()
00229             << "Error writing to " << _subfile_filename << ".\n";
00230           reset();
00231           return EU_error_abort;
00232         }
00233         
00234         _subfile_pos += count;
00235         _total_bytes_extracted += count;
00236         
00237         now = clock->get_short_time();
00238         if (now >= finish) {
00239           // That's enough for now.
00240           return EU_ok;
00241         }
00242         
00243         max_bytes = min(buffer_size, _subfile_length - _subfile_pos);
00244         _read->read(buffer, max_bytes);
00245         count = _read->gcount();
00246       }
00247       
00248       if (max_bytes != 0) {
00249         downloader_cat.error()
00250           << "Unexpected EOF on multifile " << _multifile_name << ".\n";
00251         reset();
00252         return EU_error_abort;
00253       }
00254     }
00255   
00256     now = clock->get_short_time();
00257   } while (now < finish);
00258 
00259   // That's enough for now.
00260   return EU_ok;
00261 }
00262 
00263 ////////////////////////////////////////////////////////////////////
00264 //     Function: Extractor::get_progress
00265 //       Access: Public
00266 //  Description: Returns the fraction of the Multifile extracted so
00267 //               far.
00268 ////////////////////////////////////////////////////////////////////
00269 PN_stdfloat Extractor::
00270 get_progress() const {
00271   if (!_initiated) {
00272     return 0.0f;
00273   }
00274   if (_requests_total_length == 0) {
00275     return 1.0f;
00276   }
00277 
00278   return (PN_stdfloat)_total_bytes_extracted / (PN_stdfloat)_requests_total_length;
00279 }
00280 
00281 ////////////////////////////////////////////////////////////////////
00282 //     Function: Extractor::run
00283 //       Access: Published
00284 //  Description: A convenience function to extract the Multifile all
00285 //               at once, when you don't care about doing it in the
00286 //               background.
00287 //
00288 //               First, call request_file() or request_all_files() to
00289 //               specify the files you would like to extract, then
00290 //               call run() to do the extraction.  Also see step() for
00291 //               when you would like the extraction to happen as a
00292 //               background task.
00293 ////////////////////////////////////////////////////////////////////
00294 bool Extractor::
00295 run() {
00296   while (true) {
00297     int ret = step();
00298     if (ret == EU_success) {
00299       return true;
00300     }
00301     if (ret < 0) {
00302       return false;
00303     }
00304   }
00305 }
 All Classes Functions Variables Enumerations