Panda3D
|
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 }