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