00001 // Filename: bamWriter.cxx 00002 // Created by: jason (08Jun00) 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 #include "pnotify.h" 00017 00018 #include "typedWritable.h" 00019 #include "config_util.h" 00020 #include "bam.h" 00021 #include "bamWriter.h" 00022 #include "bamReader.h" 00023 #include "lightMutexHolder.h" 00024 00025 #include <algorithm> 00026 00027 //////////////////////////////////////////////////////////////////// 00028 // Function: BamWriter::Constructor 00029 // Access: Published 00030 // Description: 00031 //////////////////////////////////////////////////////////////////// 00032 BamWriter:: 00033 BamWriter(DatagramSink *target) : 00034 _target(target) 00035 { 00036 ++_writing_seq; 00037 _next_boc = BOC_adjunct; 00038 _needs_init = true; 00039 00040 // Initialize the next object and PTA ID's. These start counting at 00041 // 1, since 0 is reserved for NULL. 00042 _next_object_id = 1; 00043 _long_object_id = false; 00044 _next_pta_id = 1; 00045 _long_pta_id = false; 00046 00047 _file_endian = bam_endian; 00048 _file_stdfloat_double = bam_stdfloat_double; 00049 _file_texture_mode = bam_texture_mode; 00050 } 00051 00052 //////////////////////////////////////////////////////////////////// 00053 // Function: BamWriter::Destructor 00054 // Access: Published 00055 // Description: 00056 //////////////////////////////////////////////////////////////////// 00057 BamWriter:: 00058 ~BamWriter() { 00059 // Tell all the TypedWritables whose pointer we are still keeping to 00060 // forget about us. 00061 StateMap::iterator si; 00062 for (si = _state_map.begin(); si != _state_map.end(); ++si) { 00063 TypedWritable *object = (TypedWritable *)(*si).first; 00064 LightMutexHolder holder(TypedWritable::_bam_writers_lock); 00065 nassertv(object->_bam_writers != (TypedWritable::BamWriters *)NULL); 00066 TypedWritable::BamWriters::iterator wi = 00067 find(object->_bam_writers->begin(), object->_bam_writers->end(), this); 00068 nassertv(wi != object->_bam_writers->end()); 00069 object->_bam_writers->erase(wi); 00070 } 00071 } 00072 00073 //////////////////////////////////////////////////////////////////// 00074 // Function: BamWriter::set_target 00075 // Access: Published 00076 // Description: Changes the destination of future datagrams written 00077 // by the BamWriter. This also implicitly calls init() 00078 // if it has not already been called. 00079 //////////////////////////////////////////////////////////////////// 00080 void BamWriter:: 00081 set_target(DatagramSink *target) { 00082 if (_target != NULL) { 00083 _target->flush(); 00084 } 00085 _target = target; 00086 00087 if (_needs_init && _target != NULL) { 00088 init(); 00089 } 00090 } 00091 00092 //////////////////////////////////////////////////////////////////// 00093 // Function: BamWriter::init 00094 // Access: Published 00095 // Description: Initializes the BamWriter prior to writing any 00096 // objects to its output stream. This includes writing 00097 // out the Bam header. 00098 // 00099 // This returns true if the BamWriter successfully 00100 // initialized, false otherwise. 00101 //////////////////////////////////////////////////////////////////// 00102 bool BamWriter:: 00103 init() { 00104 nassertr(_target != NULL, false); 00105 nassertr(_needs_init, false); 00106 _needs_init = false; 00107 00108 // Initialize the next object and PTA ID's. These start counting at 00109 // 1, since 0 is reserved for NULL. 00110 _next_object_id = 1; 00111 _long_object_id = false; 00112 _next_pta_id = 1; 00113 _long_pta_id = false; 00114 00115 _file_endian = bam_endian; 00116 _file_texture_mode = bam_texture_mode; 00117 00118 // Write out the current major and minor BAM file version numbers. 00119 Datagram header; 00120 00121 header.add_uint16(_bam_major_ver); 00122 header.add_uint16(_bam_minor_ver); 00123 header.add_uint8(_file_endian); 00124 header.add_bool(_file_stdfloat_double); 00125 00126 if (!_target->put_datagram(header)) { 00127 util_cat.error() 00128 << "Unable to write Bam header.\n"; 00129 return false; 00130 } 00131 return true; 00132 } 00133 00134 //////////////////////////////////////////////////////////////////// 00135 // Function: BamWriter::write_object 00136 // Access: Published 00137 // Description: Writes a single object to the Bam file, so that the 00138 // BamReader::read_object() can later correctly restore 00139 // the object and all its pointers. 00140 // 00141 // This implicitly also writes any additional objects 00142 // this object references (if they haven't already been 00143 // written), so that pointers may be fully resolved. 00144 // 00145 // This may be called repeatedly to write a sequence of 00146 // objects to the Bam file, but typically (especially 00147 // for scene graph files, indicated with the .bam 00148 // extension), only one object is written directly from 00149 // the Bam file: the root of the scene graph. The 00150 // remaining objects will all be written recursively by 00151 // the first object. 00152 // 00153 // Returns true if the object is successfully written, 00154 // false otherwise. 00155 //////////////////////////////////////////////////////////////////// 00156 bool BamWriter:: 00157 write_object(const TypedWritable *object) { 00158 nassertr(_target != NULL, false); 00159 00160 // Increment the _writing_seq, so we can check for newly stale 00161 // objects during this operation. 00162 ++_writing_seq; 00163 00164 // If there are any freed objects to indicate, write them out now. 00165 if (!_freed_object_ids.empty()) { 00166 Datagram dg; 00167 dg.add_uint8(BOC_remove); 00168 00169 FreedObjectIds::iterator fi; 00170 for (fi = _freed_object_ids.begin(); fi != _freed_object_ids.end(); ++fi) { 00171 write_object_id(dg, (*fi)); 00172 } 00173 _freed_object_ids.clear(); 00174 00175 if (!_target->put_datagram(dg)) { 00176 util_cat.error() 00177 << "Unable to write data to output.\n"; 00178 return false; 00179 } 00180 } 00181 00182 nassertr(_object_queue.empty(), false); 00183 _next_boc = BOC_push; 00184 00185 int object_id = enqueue_object(object); 00186 nassertr(object_id != 0, false); 00187 if (!flush_queue()) { 00188 return false; 00189 } 00190 00191 // Finally, write the closing pop. 00192 if (_next_boc != BOC_push) { 00193 Datagram dg; 00194 dg.add_uint8(BOC_pop); 00195 if (!_target->put_datagram(dg)) { 00196 util_cat.error() 00197 << "Unable to write data to output.\n"; 00198 return false; 00199 } 00200 } 00201 00202 return true; 00203 } 00204 00205 //////////////////////////////////////////////////////////////////// 00206 // Function: BamWriter::has_object 00207 // Access: Published 00208 // Description: Returns true if the object has previously been 00209 // written (or at least requested to be written) to the 00210 // bam file, or false if we've never heard of it before. 00211 //////////////////////////////////////////////////////////////////// 00212 bool BamWriter:: 00213 has_object(const TypedWritable *object) const { 00214 StateMap::const_iterator si = _state_map.find(object); 00215 return (si != _state_map.end()); 00216 } 00217 00218 //////////////////////////////////////////////////////////////////// 00219 // Function: BamWriter::flush 00220 // Access: Published 00221 // Description: Ensures that all data written thus far is manifested 00222 // on the output stream. 00223 //////////////////////////////////////////////////////////////////// 00224 void BamWriter:: 00225 flush() { 00226 nassertv(_target != NULL); 00227 _target->flush(); 00228 } 00229 00230 //////////////////////////////////////////////////////////////////// 00231 // Function: BamWriter::consider_update 00232 // Access: Public 00233 // Description: Should be called from 00234 // TypedWritable::update_bam_nested() to recursively 00235 // check the entire hiererachy of writable objects for 00236 // needed updates. This tests the indicated 00237 // TypedWritable object and writes it to the bam stream 00238 // if it has recently been modified, then recurses 00239 // through update_bam_nested. 00240 //////////////////////////////////////////////////////////////////// 00241 void BamWriter:: 00242 consider_update(const TypedWritable *object) { 00243 StateMap::iterator si = _state_map.find(object); 00244 if (si == _state_map.end()) { 00245 // This object has never even been seen before. 00246 enqueue_object(object); 00247 00248 } else if ((*si).second._written_seq.is_initial()) { 00249 // This object has not been written yet. 00250 enqueue_object(object); 00251 00252 } else if ((*si).second._written_seq == _writing_seq) { 00253 // We have already visited this object this pass, so no need to 00254 // look closer. 00255 00256 } else if ((*si).second._modified != object->get_bam_modified()) { 00257 // This object has been recently modified and needs to be rewritten. 00258 enqueue_object(object); 00259 00260 } else { 00261 // Mark that we have now visited this object and pronounced it clean. 00262 (*si).second._written_seq = _writing_seq; 00263 00264 // Recurse to child objects. 00265 ((TypedWritable *)object)->update_bam_nested(this); 00266 } 00267 } 00268 00269 //////////////////////////////////////////////////////////////////// 00270 // Function: BamWriter::write_pointer 00271 // Access: Public 00272 // Description: The interface for writing a pointer to another object 00273 // to a Bam file. This is intended to be called by the 00274 // various objects that write themselves to the Bam 00275 // file, within the write_datagram() method. 00276 // 00277 // This writes the pointer out in such a way that the 00278 // BamReader will be able to restore the pointer later. 00279 // If the pointer is to an object that has not yet 00280 // itself been written to the Bam file, that object will 00281 // automatically be written. 00282 //////////////////////////////////////////////////////////////////// 00283 void BamWriter:: 00284 write_pointer(Datagram &packet, const TypedWritable *object) { 00285 // If the pointer is NULL, we always simply write a zero for an 00286 // object ID and leave it at that. 00287 if (object == (const TypedWritable *)NULL) { 00288 write_object_id(packet, 0); 00289 00290 } else { 00291 StateMap::iterator si = _state_map.find(object); 00292 if (si == _state_map.end()) { 00293 // We have not written this pointer out yet. This means we must 00294 // queue the object definition up for later. 00295 int object_id = enqueue_object(object); 00296 write_object_id(packet, object_id); 00297 00298 } else { 00299 // We have already assigned this pointer an ID, so it has 00300 // previously been written; but we might still need to rewrite 00301 // it if it is stale. 00302 int object_id = (*si).second._object_id; 00303 bool already_written = !(*si).second._written_seq.is_initial(); 00304 if ((*si).second._written_seq != _writing_seq && 00305 (*si).second._modified != object->get_bam_modified()) { 00306 // This object was previously written, but it has since been 00307 // modified, so we should write it again. 00308 already_written = false; 00309 } 00310 00311 write_object_id(packet, object_id); 00312 00313 if (!already_written) { 00314 // It's stale, so queue the object for rewriting too. 00315 enqueue_object(object); 00316 } else { 00317 // Not stale, but maybe its child object is. 00318 ((TypedWritable *)object)->update_bam_nested(this); 00319 } 00320 } 00321 } 00322 } 00323 00324 //////////////////////////////////////////////////////////////////// 00325 // Function: BamWriter::write_file_data 00326 // Access: Public 00327 // Description: Writes a block of auxiliary file data from the 00328 // indicated file (within the vfs). This can be a block 00329 // of arbitrary size, and it is assumed it may be quite 00330 // large. This must be balanced by a matching call to 00331 // read_file_data() on restore. 00332 //////////////////////////////////////////////////////////////////// 00333 void BamWriter:: 00334 write_file_data(SubfileInfo &result, const Filename &filename) { 00335 // We write file data by preceding with a singleton datagram that 00336 // contains only the BOC_file_data token. 00337 Datagram dg; 00338 dg.add_uint8(BOC_file_data); 00339 if (!_target->put_datagram(dg)) { 00340 util_cat.error() 00341 << "Unable to write data to output.\n"; 00342 return; 00343 } 00344 00345 // Then we can write the file data itself, as its own (possibly 00346 // quite large) followup datagram. 00347 if (!_target->copy_datagram(result, filename)) { 00348 util_cat.error() 00349 << "Unable to write file data to output.\n"; 00350 return; 00351 } 00352 00353 // Both of those get written to the bam stream prior to the datagram 00354 // that represents this particular object, but they'll get pulled 00355 // out in the same order and queued up in the BamReader. 00356 } 00357 00358 //////////////////////////////////////////////////////////////////// 00359 // Function: BamWriter::write_file_data 00360 // Access: Public 00361 // Description: Writes a block of auxiliary file data from the 00362 // indicated file (outside of the vfs). This can be a 00363 // block of arbitrary size, and it is assumed it may be 00364 // quite large. This must be balanced by a matching 00365 // call to read_file_data() on restore. 00366 //////////////////////////////////////////////////////////////////// 00367 void BamWriter:: 00368 write_file_data(SubfileInfo &result, const SubfileInfo &source) { 00369 // We write file data by preceding with a singleton datagram that 00370 // contains only the BOC_file_data token. 00371 Datagram dg; 00372 dg.add_uint8(BOC_file_data); 00373 if (!_target->put_datagram(dg)) { 00374 util_cat.error() 00375 << "Unable to write data to output.\n"; 00376 return; 00377 } 00378 00379 // Then we can write the file data itself, as its own (possibly 00380 // quite large) followup datagram. 00381 if (!_target->copy_datagram(result, source)) { 00382 util_cat.error() 00383 << "Unable to write file data to output.\n"; 00384 return; 00385 } 00386 00387 // Both of those get written to the bam stream prior to the datagram 00388 // that represents this particular object, but they'll get pulled 00389 // out in the same order and queued up in the BamReader. 00390 } 00391 00392 //////////////////////////////////////////////////////////////////// 00393 // Function: BamWriter::write_cdata 00394 // Access: Public 00395 // Description: Writes out the indicated CycleData object. This 00396 // should be used by classes that store some or all of 00397 // their data within a CycleData subclass, in support of 00398 // pipelining. This will call the virtual 00399 // CycleData::write_datagram() method to do the actual 00400 // writing. 00401 //////////////////////////////////////////////////////////////////// 00402 void BamWriter:: 00403 write_cdata(Datagram &packet, const PipelineCyclerBase &cycler) { 00404 const CycleData *cdata = cycler.read(Thread::get_current_thread()); 00405 cdata->write_datagram(this, packet); 00406 cycler.release_read(cdata); 00407 } 00408 00409 //////////////////////////////////////////////////////////////////// 00410 // Function: BamWriter::write_cdata 00411 // Access: Public 00412 // Description: This version of write_cdata allows passing an 00413 // additional parameter to cdata->write_datagram(). 00414 //////////////////////////////////////////////////////////////////// 00415 void BamWriter:: 00416 write_cdata(Datagram &packet, const PipelineCyclerBase &cycler, 00417 void *extra_data) { 00418 const CycleData *cdata = cycler.read(Thread::get_current_thread()); 00419 cdata->write_datagram(this, packet, extra_data); 00420 cycler.release_read(cdata); 00421 } 00422 00423 //////////////////////////////////////////////////////////////////// 00424 // Function: BamWriter::register_pta 00425 // Access: Public 00426 // Description: Prepares to write a PointerToArray to the Bam file, 00427 // unifying references to the same pointer across the 00428 // Bam file. 00429 // 00430 // The writing object should call this prior to writing 00431 // out a PointerToArray. It will return true if the 00432 // same pointer has been written previously, in which 00433 // case the writing object need do nothing further; or 00434 // it will return false if this particular pointer has 00435 // not yet been written, in which case the writing 00436 // object must then write out the contents of the array. 00437 // 00438 // Also see the WRITE_PTA() macro, which consolidates 00439 // the work that must be done to write a PTA. 00440 //////////////////////////////////////////////////////////////////// 00441 bool BamWriter:: 00442 register_pta(Datagram &packet, const void *ptr) { 00443 if (ptr == (const void *)NULL) { 00444 // A zero for the PTA ID indicates a NULL pointer. This is a 00445 // special case. 00446 write_pta_id(packet, 0); 00447 00448 // We return false to indicate the user must now write out the 00449 // "definition" of the NULL pointer. This is necessary because of 00450 // a quirk in the BamReader's design, which forces callers to read 00451 // the definition of every NULL pointer. Presumably, the caller 00452 // will be able to write the definition in a concise way that will 00453 // clearly indicate a NULL pointer; in the case of a 00454 // PointerToArray, this will generally be simply a zero element 00455 // count. 00456 return false; 00457 } 00458 00459 PTAMap::iterator pi = _pta_map.find(ptr); 00460 if (pi == _pta_map.end()) { 00461 // We have not encountered this pointer before. 00462 int pta_id = _next_pta_id; 00463 _next_pta_id++; 00464 00465 bool inserted = _pta_map.insert(PTAMap::value_type(ptr, pta_id)).second; 00466 nassertr(inserted, false); 00467 00468 write_pta_id(packet, pta_id); 00469 00470 // Return false to indicate the caller must now write out the 00471 // array definition. 00472 return false; 00473 00474 } else { 00475 // We have encountered this pointer before. 00476 int pta_id = (*pi).second; 00477 write_pta_id(packet, pta_id); 00478 00479 // Return true to indicate the caller need do nothing further. 00480 return true; 00481 } 00482 } 00483 00484 //////////////////////////////////////////////////////////////////// 00485 // Function: BamWriter::write_handle 00486 // Access: Public 00487 // Description: Writes a TypeHandle to the file in such a way that 00488 // the BamReader can read the same TypeHandle later via 00489 // read_handle(). 00490 //////////////////////////////////////////////////////////////////// 00491 void BamWriter:: 00492 write_handle(Datagram &packet, TypeHandle type) { 00493 // We encode TypeHandles within the Bam file by writing a unique 00494 // index number for each one to the file. When we write a 00495 // particular TypeHandle for the first time, we assign it a new 00496 // index number and then immediately follow it by its definition; 00497 // when we write the same TypeHandle on subsequent times we only 00498 // write the index number. 00499 00500 // The unique number we choose is actually the internal index number 00501 // of the TypeHandle. Why not? 00502 int index = type.get_index(); 00503 00504 // Also make sure the index number fits within a PN_uint16. 00505 nassertv(index <= 0xffff); 00506 00507 packet.add_uint16(index); 00508 00509 if (index != 0) { 00510 bool inserted = _types_written.insert(index).second; 00511 00512 if (inserted) { 00513 // This is the first time this TypeHandle has been written, so 00514 // also write out its definition. 00515 packet.add_string(type.get_name()); 00516 00517 // We also need to write the derivation of the TypeHandle, in case 00518 // the program reading this file later has never heard of this 00519 // type before. 00520 int num_parent_classes = type.get_num_parent_classes(); 00521 nassertv(num_parent_classes <= 255); // Good grief! 00522 packet.add_uint8(num_parent_classes); 00523 for (int i = 0; i < num_parent_classes; i++) { 00524 write_handle(packet, type.get_parent_class(i)); 00525 } 00526 } 00527 } 00528 } 00529 00530 00531 //////////////////////////////////////////////////////////////////// 00532 // Function: BamWriter::object_destructs 00533 // Access: Private 00534 // Description: This is called by the TypedWritable destructor. It 00535 // should remove the pointer from any structures that 00536 // keep a reference to it, and also write a flag to the 00537 // bam file (if it is open) so that a reader will know 00538 // the object id will no longer be used. 00539 //////////////////////////////////////////////////////////////////// 00540 void BamWriter:: 00541 object_destructs(TypedWritable *object) { 00542 StateMap::iterator si = _state_map.find(object); 00543 if (si != _state_map.end()) { 00544 // We ought to have written out the object by the time it 00545 // destructs, or we're in trouble when we do write it out. 00546 nassertv(!(*si).second._written_seq.is_initial()); 00547 00548 int object_id = (*si).second._object_id; 00549 _freed_object_ids.push_back(object_id); 00550 00551 _state_map.erase(si); 00552 } 00553 } 00554 00555 //////////////////////////////////////////////////////////////////// 00556 // Function: BamWriter::write_object_id 00557 // Access: Private 00558 // Description: Writes the indicated object id to the datagram. 00559 //////////////////////////////////////////////////////////////////// 00560 void BamWriter:: 00561 write_object_id(Datagram &dg, int object_id) { 00562 if (_long_object_id) { 00563 dg.add_uint32(object_id); 00564 00565 } else { 00566 dg.add_uint16(object_id); 00567 // Once we fill up our uint16, we write all object id's 00568 // thereafter with a uint32. 00569 if (object_id == 0xffff) { 00570 _long_object_id = true; 00571 } 00572 } 00573 } 00574 00575 //////////////////////////////////////////////////////////////////// 00576 // Function: BamWriter::write_pta_id 00577 // Access: Private 00578 // Description: Writes the indicated pta id to the datagram. 00579 //////////////////////////////////////////////////////////////////// 00580 void BamWriter:: 00581 write_pta_id(Datagram &dg, int pta_id) { 00582 if (_long_pta_id) { 00583 dg.add_uint32(pta_id); 00584 00585 } else { 00586 dg.add_uint16(pta_id); 00587 // Once we fill up our uint16, we write all pta id's 00588 // thereafter with a uint32. 00589 if (pta_id == 0xffff) { 00590 _long_pta_id = true; 00591 } 00592 } 00593 } 00594 00595 //////////////////////////////////////////////////////////////////// 00596 // Function: BamWriter::enqueue_object 00597 // Access: Private 00598 // Description: Assigns an object ID to the object and queues it up 00599 // for later writing to the Bam file. 00600 // 00601 // The return value is the object ID, or 0 if there is 00602 // an error. 00603 //////////////////////////////////////////////////////////////////// 00604 int BamWriter:: 00605 enqueue_object(const TypedWritable *object) { 00606 Datagram dg; 00607 00608 nassertr(object != TypedWritable::Null, 0); 00609 00610 // No object should ever be written out that is not registered as a 00611 // child of TypedWritable. The only way this can happen is if 00612 // someone failed to initialize their type correctly in init_type(). 00613 #ifndef NDEBUG 00614 if (!object->is_of_type(TypedWritable::get_class_type())) { 00615 util_cat.error() 00616 << "Type " << object->get_type() 00617 << " does not indicate inheritance from TypedWritable.\n" 00618 << "(this is almost certainly an oversight in " << object->get_type() 00619 << "::init_type().)\n"; 00620 } 00621 #endif 00622 00623 // We need to assign a unique index number to every object we write 00624 // out. Has this object been assigned a number yet? 00625 int object_id; 00626 00627 StateMap::iterator si = _state_map.find(object); 00628 if (si == _state_map.end()) { 00629 // No, it hasn't, so assign it the next number in sequence 00630 // arbitrarily. 00631 object_id = _next_object_id; 00632 00633 bool inserted = 00634 _state_map.insert(StateMap::value_type(object, StoreState(_next_object_id))).second; 00635 nassertr(inserted, false); 00636 { 00637 LightMutexHolder holder(TypedWritable::_bam_writers_lock); 00638 if (object->_bam_writers == ((TypedWritable::BamWriters *)NULL)) { 00639 ((TypedWritable *)object)->_bam_writers = new TypedWritable::BamWriters; 00640 } 00641 object->_bam_writers->push_back(this); 00642 } 00643 _next_object_id++; 00644 00645 } else { 00646 // Yes, it has; get the object ID. 00647 object_id = (*si).second._object_id; 00648 } 00649 00650 _object_queue.push_back(object); 00651 return object_id; 00652 } 00653 00654 //////////////////////////////////////////////////////////////////// 00655 // Function: BamWriter::flush_queue 00656 // Access: Private 00657 // Description: Writes all of the objects on the _object_queue to the 00658 // bam stream, until the queue is empty. 00659 // 00660 // Returns true on success, false on failure. 00661 //////////////////////////////////////////////////////////////////// 00662 bool BamWriter:: 00663 flush_queue() { 00664 nassertr(_target != NULL, false); 00665 // Each object we write may append more to the queue. 00666 while (!_object_queue.empty()) { 00667 const TypedWritable *object = _object_queue.front(); 00668 _object_queue.pop_front(); 00669 00670 // Look up the object in the map. It had better be there! 00671 StateMap::iterator si = _state_map.find(object); 00672 nassertr(si != _state_map.end(), false); 00673 00674 if ((*si).second._written_seq == _writing_seq) { 00675 // We have already visited this object; no need to consider it again. 00676 continue; 00677 } 00678 00679 int object_id = (*si).second._object_id; 00680 bool already_written = !(*si).second._written_seq.is_initial(); 00681 if ((*si).second._modified != object->get_bam_modified()) { 00682 // This object was previously written, but it has since been 00683 // modified, so we should write it again. 00684 already_written = false; 00685 } 00686 00687 Datagram dg; 00688 dg.set_stdfloat_double(_file_stdfloat_double); 00689 dg.add_uint8(_next_boc); 00690 _next_boc = BOC_adjunct; 00691 00692 if (!already_written) { 00693 // The first time we write a particular object, or when we 00694 // update the same object later, we do so by writing its 00695 // TypeHandle (which had better not be TypeHandle::none(), since 00696 // that's our code for a previously-written object), followed by 00697 // the object ID number, followed by the object definition. 00698 00699 TypeHandle type = object->get_type(); 00700 nassertr(type != TypeHandle::none(), false); 00701 00702 // Determine what the nearest kind of type is that the reader 00703 // will be able to handle, and write that instead. 00704 TypeHandle registered_type = 00705 BamReader::get_factory()->find_registered_type(type); 00706 if (registered_type == TypeHandle::none()) { 00707 // We won't be able to read this type again. 00708 util_cat.warning() 00709 << "Objects of type " << type << " cannot be read; bam file is invalid.\n"; 00710 } else if (registered_type != type) { 00711 util_cat.info() 00712 << "Writing " << registered_type << " instead of " << type << "\n"; 00713 type = registered_type; 00714 00715 } else if (util_cat.is_debug()) { 00716 util_cat.debug() 00717 << "Writing " << type << " object id " << object_id 00718 << " to bam file\n"; 00719 } 00720 00721 write_handle(dg, type); 00722 write_object_id(dg, object_id); 00723 00724 // We cast the const pointer to non-const so that we may call 00725 // write_datagram() on it. Really, write_datagram() should be a 00726 // const method anyway, but there may be times when a class 00727 // object wants to update some transparent cache value during 00728 // writing or something like that, so it's more convenient to 00729 // cheat and define it as a non-const method. 00730 ((TypedWritable *)object)->write_datagram(this, dg); 00731 00732 (*si).second._written_seq = _writing_seq; 00733 (*si).second._modified = object->get_bam_modified(); 00734 00735 } else { 00736 // On subsequent times when we write a particular object, we 00737 // write simply TypeHandle::none(), followed by the object ID. 00738 // The occurrence of TypeHandle::none() is an indicator to the 00739 // BamReader that this is a previously-written object. 00740 00741 write_handle(dg, TypeHandle::none()); 00742 write_object_id(dg, object_id); 00743 00744 // The object has not been modified, but maybe one of its child 00745 // objects has. 00746 ((TypedWritable *)object)->update_bam_nested(this); 00747 } 00748 00749 if (!_target->put_datagram(dg)) { 00750 util_cat.error() 00751 << "Unable to write data to output.\n"; 00752 return false; 00753 } 00754 } 00755 00756 return true; 00757 }