Panda3D
|
00001 // Filename: datagramInputFile.cxx 00002 // Created by: drose (30Oct00) 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 "datagramInputFile.h" 00016 #include "temporaryFile.h" 00017 #include "numeric_types.h" 00018 #include "datagramIterator.h" 00019 #include "profileTimer.h" 00020 #include "config_util.h" 00021 #include "config_express.h" 00022 #include "virtualFileSystem.h" 00023 #include "streamReader.h" 00024 #include "thread.h" 00025 00026 //////////////////////////////////////////////////////////////////// 00027 // Function: DatagramInputFile::open 00028 // Access: Published 00029 // Description: Opens the indicated filename for reading. Returns 00030 // true on success, false on failure. 00031 //////////////////////////////////////////////////////////////////// 00032 bool DatagramInputFile:: 00033 open(const FileReference *file) { 00034 close(); 00035 00036 _file = file; 00037 _filename = _file->get_filename(); 00038 00039 // DatagramInputFiles are always binary. 00040 _filename.set_binary(); 00041 00042 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 00043 _vfile = vfs->get_file(_filename); 00044 if (_vfile == (VirtualFile *)NULL) { 00045 // No such file. 00046 return false; 00047 } 00048 _in = _vfile->open_read_file(true); 00049 _owns_in = (_in != (istream *)NULL); 00050 return _owns_in && !_in->fail(); 00051 } 00052 00053 //////////////////////////////////////////////////////////////////// 00054 // Function: DatagramInputFile::open 00055 // Access: Published 00056 // Description: Starts reading from the indicated stream. Returns 00057 // true on success, false on failure. The 00058 // DatagramInputFile does not take ownership of the 00059 // stream; you are responsible for closing or deleting 00060 // it when you are done. 00061 //////////////////////////////////////////////////////////////////// 00062 bool DatagramInputFile:: 00063 open(istream &in, const Filename &filename) { 00064 close(); 00065 00066 _in = ∈ 00067 _owns_in = false; 00068 _filename = filename; 00069 00070 if (!filename.empty()) { 00071 _file = new FileReference(filename); 00072 } 00073 00074 return !_in->fail(); 00075 } 00076 00077 //////////////////////////////////////////////////////////////////// 00078 // Function: DatagramInputFile::close 00079 // Access: Published 00080 // Description: Closes the file. This is also implicitly done when 00081 // the DatagramInputFile destructs. 00082 //////////////////////////////////////////////////////////////////// 00083 void DatagramInputFile:: 00084 close() { 00085 _vfile.clear(); 00086 if (_owns_in) { 00087 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 00088 vfs->close_read_file(_in); 00089 } 00090 _in = (istream *)NULL; 00091 _owns_in = false; 00092 00093 _file.clear(); 00094 _filename = Filename(); 00095 00096 _read_first_datagram = false; 00097 _error = false; 00098 } 00099 00100 //////////////////////////////////////////////////////////////////// 00101 // Function: DatagramInputFile::read_header 00102 // Access: Published 00103 // Description: Reads a sequence of bytes from the beginning of the 00104 // datagram file. This may be called any number of 00105 // times after the file has been opened and before the 00106 // first datagram is read. It may not be called once 00107 // the first datagram has been read. 00108 //////////////////////////////////////////////////////////////////// 00109 bool DatagramInputFile:: 00110 read_header(string &header, size_t num_bytes) { 00111 nassertr(!_read_first_datagram, false); 00112 nassertr(_in != (istream *)NULL, false); 00113 00114 char *buffer = (char *)alloca(num_bytes); 00115 nassertr(buffer != (char *)NULL, false); 00116 00117 _in->read(buffer, num_bytes); 00118 if (_in->fail() || _in->eof()) { 00119 return false; 00120 } 00121 00122 header = string(buffer, num_bytes); 00123 Thread::consider_yield(); 00124 return true; 00125 } 00126 00127 //////////////////////////////////////////////////////////////////// 00128 // Function: DatagramInputFile::get_datagram 00129 // Access: Published, Virtual 00130 // Description: Reads the next datagram from the file. Returns true 00131 // on success, false if there is an error or end of 00132 // file. 00133 //////////////////////////////////////////////////////////////////// 00134 bool DatagramInputFile:: 00135 get_datagram(Datagram &data) { 00136 nassertr(_in != (istream *)NULL, false); 00137 _read_first_datagram = true; 00138 00139 // First, get the size of the upcoming datagram. 00140 StreamReader reader(_in, false); 00141 size_t num_bytes = reader.get_uint32(); 00142 if (_in->fail() || _in->eof()) { 00143 return false; 00144 } 00145 00146 if (num_bytes == 0) { 00147 // A special case for a zero-length datagram: no need to try to 00148 // read any data. 00149 data.clear(); 00150 return true; 00151 } 00152 00153 // Now, read the datagram itself. 00154 00155 // If the number of bytes is large, we will need to allocate a 00156 // temporary buffer from the heap. Otherwise, we can get away with 00157 // allocating it on the stack, via alloca(). 00158 if (num_bytes > 65536) { 00159 char *buffer = (char *)PANDA_MALLOC_ARRAY(num_bytes); 00160 nassertr(buffer != (char *)NULL, false); 00161 00162 _in->read(buffer, num_bytes); 00163 if (_in->fail() || _in->eof()) { 00164 _error = true; 00165 PANDA_FREE_ARRAY(buffer); 00166 return false; 00167 } 00168 00169 data = Datagram(buffer, num_bytes); 00170 PANDA_FREE_ARRAY(buffer); 00171 00172 } else { 00173 char *buffer = (char *)alloca(num_bytes); 00174 nassertr(buffer != (char *)NULL, false); 00175 00176 _in->read(buffer, num_bytes); 00177 if (_in->fail() || _in->eof()) { 00178 _error = true; 00179 return false; 00180 } 00181 00182 data = Datagram(buffer, num_bytes); 00183 } 00184 Thread::consider_yield(); 00185 00186 return true; 00187 } 00188 00189 //////////////////////////////////////////////////////////////////// 00190 // Function: DatagramInputFile::save_datagram 00191 // Access: Published, Virtual 00192 // Description: Skips over the next datagram without extracting it, 00193 // but saves the relevant file information in the 00194 // SubfileInfo object so that its data may be read 00195 // later. For non-file-based datagram generators, this 00196 // may mean creating a temporary file and copying the 00197 // contents of the datagram to disk. 00198 // 00199 // Returns true on success, false on failure or if this 00200 // method is unimplemented. 00201 //////////////////////////////////////////////////////////////////// 00202 bool DatagramInputFile:: 00203 save_datagram(SubfileInfo &info) { 00204 nassertr(_in != (istream *)NULL, false); 00205 _read_first_datagram = true; 00206 00207 // First, get the size of the upcoming datagram. 00208 StreamReader reader(_in, false); 00209 size_t num_bytes = reader.get_uint32(); 00210 if (_in->fail() || _in->eof()) { 00211 return false; 00212 } 00213 00214 // If this stream is file-based, we can just point the SubfileInfo 00215 // directly into this file. 00216 if (_file != (FileReference *)NULL) { 00217 info = SubfileInfo(_file, _in->tellg(), num_bytes); 00218 _in->seekg(num_bytes, ios::cur); 00219 return true; 00220 } 00221 00222 // Otherwise, we have to dump the data into a temporary file. 00223 PT(TemporaryFile) tfile = new TemporaryFile(Filename::temporary("", "")); 00224 pofstream out; 00225 Filename filename = tfile->get_filename(); 00226 filename.set_binary(); 00227 if (!filename.open_write(out)) { 00228 util_cat.error() 00229 << "Couldn't write to " << tfile->get_filename() << "\n"; 00230 return false; 00231 } 00232 00233 if (util_cat.is_debug()) { 00234 util_cat.debug() 00235 << "Copying " << num_bytes << " bytes to " << tfile->get_filename() << "\n"; 00236 } 00237 00238 size_t num_remaining = num_bytes; 00239 static const size_t buffer_size = 4096; 00240 char buffer[buffer_size]; 00241 00242 _in->read(buffer, min(buffer_size, num_remaining)); 00243 size_t count = _in->gcount(); 00244 while (count != 0) { 00245 out.write(buffer, count); 00246 if (out.fail()) { 00247 util_cat.error() 00248 << "Couldn't write " << num_bytes << " bytes to " 00249 << tfile->get_filename() << "\n"; 00250 return false; 00251 } 00252 num_remaining -= count; 00253 if (num_remaining == 0) { 00254 break; 00255 } 00256 _in->read(buffer, min(buffer_size, num_remaining)); 00257 count = _in->gcount(); 00258 } 00259 00260 if (num_remaining != 0) { 00261 util_cat.error() 00262 << "Truncated data stream.\n"; 00263 return false; 00264 } 00265 00266 info = SubfileInfo(tfile, 0, num_bytes); 00267 return true; 00268 } 00269 00270 //////////////////////////////////////////////////////////////////// 00271 // Function: DatagramInputFile::is_eof 00272 // Access: Published, Virtual 00273 // Description: Returns true if the file has reached the end-of-file. 00274 // This test may only be made after a call to 00275 // read_header() or get_datagram() has failed. 00276 //////////////////////////////////////////////////////////////////// 00277 bool DatagramInputFile:: 00278 is_eof() { 00279 return _in != (istream *)NULL ? _in->eof() : true; 00280 } 00281 00282 //////////////////////////////////////////////////////////////////// 00283 // Function: DatagramInputFile::is_error 00284 // Access: Published, Virtual 00285 // Description: Returns true if the file has reached an error 00286 // condition. 00287 //////////////////////////////////////////////////////////////////// 00288 bool DatagramInputFile:: 00289 is_error() { 00290 if (_in == (istream *)NULL) { 00291 return true; 00292 } 00293 00294 if (_in->fail()) { 00295 _error = true; 00296 } 00297 return _error; 00298 } 00299 00300 //////////////////////////////////////////////////////////////////// 00301 // Function: DatagramInputFile::get_filename 00302 // Access: Published, Virtual 00303 // Description: Returns the filename that provides the source for 00304 // these datagrams, if any, or empty string if the 00305 // datagrams do not originate from a file on disk. 00306 //////////////////////////////////////////////////////////////////// 00307 const Filename &DatagramInputFile:: 00308 get_filename() { 00309 return _filename; 00310 } 00311 00312 //////////////////////////////////////////////////////////////////// 00313 // Function: DatagramInputFile::get_file 00314 // Access: Published, Virtual 00315 // Description: Returns the FileReference that provides the source for 00316 // these datagrams, if any, or NULL if the datagrams do 00317 // not originate from a file on disk. 00318 //////////////////////////////////////////////////////////////////// 00319 const FileReference *DatagramInputFile:: 00320 get_file() { 00321 return _file; 00322 } 00323 00324 //////////////////////////////////////////////////////////////////// 00325 // Function: DatagramInputFile::get_vfile 00326 // Access: Published, Virtual 00327 // Description: Returns the VirtualFile that provides the source for 00328 // these datagrams, if any, or NULL if the datagrams do 00329 // not originate from a VirtualFile. 00330 //////////////////////////////////////////////////////////////////// 00331 VirtualFile *DatagramInputFile:: 00332 get_vfile() { 00333 return _vfile; 00334 } 00335 00336 //////////////////////////////////////////////////////////////////// 00337 // Function: DatagramInputFile::get_file_pos 00338 // Access: Published, Virtual 00339 // Description: Returns the current file position within the data 00340 // stream, if any, or 0 if the file position is not 00341 // meaningful or cannot be determined. 00342 // 00343 // For DatagramInputFiles that return a meaningful file 00344 // position, this will be pointing to the first byte 00345 // following the datagram returned after a call to 00346 // get_datagram(). 00347 //////////////////////////////////////////////////////////////////// 00348 streampos DatagramInputFile:: 00349 get_file_pos() { 00350 if (_in == (istream *)NULL) { 00351 return 0; 00352 } 00353 return _in->tellg(); 00354 }