Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/putil/bamWriter.cxx

Go to the documentation of this file.
00001 // Filename: bamWriter.cxx
00002 // Created by:  jason (08Jun00)
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 "pandabase.h"
00020 #include "notify.h"
00021 
00022 #include "typedWritable.h"
00023 #include "config_util.h"
00024 #include "bam.h"
00025 #include "bamWriter.h"
00026 #include "bamReader.h"
00027 
00028 ////////////////////////////////////////////////////////////////////
00029 //     Function: BamWriter::Constructor
00030 //       Access: Public
00031 //  Description:
00032 ////////////////////////////////////////////////////////////////////
00033 BamWriter::
00034 BamWriter(DatagramSink *sink) :
00035   _target(sink)
00036 {
00037 }
00038 
00039 ////////////////////////////////////////////////////////////////////
00040 //     Function: BamWriter::Destructor
00041 //       Access: Public
00042 //  Description:
00043 ////////////////////////////////////////////////////////////////////
00044 BamWriter::
00045 ~BamWriter() {
00046 }
00047 
00048 ////////////////////////////////////////////////////////////////////
00049 //     Function: BamWriter::init
00050 //       Access: Public
00051 //  Description: Initializes the BamWriter prior to writing any
00052 //               objects to its output stream.  This includes writing
00053 //               out the Bam header.
00054 //
00055 //               This returns true if the BamWriter successfully
00056 //               initialized, false otherwise.
00057 ////////////////////////////////////////////////////////////////////
00058 bool BamWriter::
00059 init() {
00060   // Initialize the next object and PTA ID's.  These start counting at
00061   // 1, since 0 is reserved for NULL.
00062   _next_object_id = 1;
00063   _next_pta_id = 1;
00064 
00065   // Write out the current major and minor BAM file version numbers.
00066   Datagram header;
00067 
00068   header.add_uint16(_bam_major_ver);
00069   header.add_uint16(_bam_minor_ver);
00070 
00071   if (!_target->put_datagram(header)) {
00072     util_cat.error()
00073       << "Unable to write Bam header.\n";
00074     return false;
00075   }
00076   return true;
00077 }
00078 
00079 ////////////////////////////////////////////////////////////////////
00080 //     Function: BamWriter::write_object
00081 //       Access: Public
00082 //  Description: Writes a single object to the Bam file, so that the
00083 //               BamReader::read_object() can later correctly restore
00084 //               the object and all its pointers.
00085 //
00086 //               This implicitly also writes any additional objects
00087 //               this object references (if they haven't already been
00088 //               written), so that pointers may be fully resolved.
00089 //
00090 //               This may be called repeatedly to write a sequence of
00091 //               objects to the Bam file, but typically (especially
00092 //               for scene graph files, indicated with the .bam
00093 //               extension), only one object is written directly from
00094 //               the Bam file: the root of the scene graph.  The
00095 //               remaining objects will all be written recursively by
00096 //               the first object.
00097 //
00098 //               Returns true if the object is successfully written,
00099 //               false otherwise.
00100 ////////////////////////////////////////////////////////////////////
00101 bool BamWriter::
00102 write_object(const TypedWritable *object) {
00103   nassertr(_object_queue.empty(), false);
00104 
00105   int object_id = enqueue_object(object);
00106   nassertr(object_id != 0, false);
00107 
00108   // Now we write out all the objects in the queue, in order.  The
00109   // first one on the queue will, of course, be this object we just
00110   // queued up, but each object we write may append more to the queue.
00111   while (!_object_queue.empty()) {
00112     object = _object_queue.front();
00113     _object_queue.pop_front();
00114 
00115     // Look up the object in the map.  It had better be there!
00116     StateMap::iterator si = _state_map.find(object);
00117     nassertr(si != _state_map.end(), false);
00118 
00119     int object_id = (*si).second._object_id;
00120     bool already_written = (*si).second._written;
00121 
00122     Datagram dg;
00123 
00124     if (!already_written) {
00125       // The first time we write a particular object, we do so by
00126       // writing its TypeHandle (which had better not be
00127       // TypeHandle::none(), since that's our code for a
00128       // previously-written object), followed by the object ID number,
00129       // followed by the object definition.
00130 
00131       TypeHandle type = object->get_type();
00132       nassertr(type != TypeHandle::none(), false);
00133 
00134       // Determine what the nearest kind of type is that the reader
00135       // will be able to handle, and write that instead.
00136       TypeHandle registered_type = 
00137         BamReader::get_factory()->find_registered_type(type);
00138       if (registered_type == TypeHandle::none()) {
00139         // We won't be able to read this type again.
00140         util_cat.warning()
00141           << "Objects of type " << type << " cannot be read; bam file is invalid.\n";
00142       } else if (registered_type != type) {
00143         util_cat.info()
00144           << "Writing " << registered_type << " instead of " << type << "\n";
00145         type = registered_type;
00146 
00147       } else if (util_cat.is_debug()) {
00148         util_cat.debug()
00149           << "Writing " << type << " object id " << object_id
00150           << " to bam file\n";
00151       }
00152 
00153       write_handle(dg, type);
00154       dg.add_uint16(object_id);
00155 
00156       // We cast the const pointer to non-const so that we may call
00157       // write_datagram() on it.  Really, write_datagram() should be a
00158       // const method anyway, but there may be times when a class
00159       // object wants to update some transparent cache value during
00160       // writing or something like that, so it's more convenient to
00161       // cheat and define it as a non-const method.
00162       ((TypedWritable *)object)->write_datagram(this, dg);
00163 
00164       (*si).second._written = true;
00165 
00166     } else {
00167       // On subsequent times when we write a particular object, we
00168       // write simply TypeHandle::none(), followed by the object ID.
00169       // The occurrence of TypeHandle::none() is an indicator to the
00170       // BamReader that this is a previously-written object.
00171 
00172       write_handle(dg, TypeHandle::none());
00173       dg.add_uint16(object_id);
00174     }
00175 
00176     if (!_target->put_datagram(dg)) {
00177       util_cat.error()
00178         << "Unable to write datagram to file.\n";
00179       return false;
00180     }
00181   }
00182 
00183   return true;
00184 }
00185 
00186 ////////////////////////////////////////////////////////////////////
00187 //     Function: BamWriter::has_object
00188 //       Access: Public
00189 //  Description: Returns true if the object has previously been
00190 //               written (or at least requested to be written) to the
00191 //               bam file, or false if we've never heard of it before.
00192 ////////////////////////////////////////////////////////////////////
00193 bool BamWriter::
00194 has_object(const TypedWritable *object) const {
00195   StateMap::const_iterator si = _state_map.find(object);
00196   return (si != _state_map.end());
00197 }
00198 
00199 ////////////////////////////////////////////////////////////////////
00200 //     Function: BamWriter::write_pointer
00201 //       Access: Public
00202 //  Description: The interface for writing a pointer to another object
00203 //               to a Bam file.  This is intended to be called by the
00204 //               various objects that write themselves to the Bam
00205 //               file, within the write_datagram() method.
00206 //
00207 //               This writes the pointer out in such a way that the
00208 //               BamReader will be able to restore the pointer later.
00209 //               If the pointer is to an object that has not yet
00210 //               itself been written to the Bam file, that object will
00211 //               automatically be written.
00212 ////////////////////////////////////////////////////////////////////
00213 void BamWriter::
00214 write_pointer(Datagram &packet, const TypedWritable *object) {
00215   // If the pointer is NULL, we always simply write a zero for an
00216   // object ID and leave it at that.
00217   if (object == (const TypedWritable *)NULL) {
00218     packet.add_uint16(0);
00219 
00220   } else {
00221     StateMap::iterator si = _state_map.find(object);
00222     if (si == _state_map.end()) {
00223       // We have not written this pointer out yet.  This means we must
00224       // queue the object definition up for later.
00225       int object_id = enqueue_object(object);
00226       packet.add_uint16(object_id);
00227 
00228     } else {
00229       // We have already assigned this pointer an ID; thus, we can
00230       // simply write out the ID.
00231       packet.add_uint16((*si).second._object_id);
00232     }
00233   }
00234 }
00235 
00236 ////////////////////////////////////////////////////////////////////
00237 //     Function: BamWriter::write_cdata
00238 //       Access: Public
00239 //  Description: Writes out the indicated CycleData object.  This
00240 //               should be used by classes that store some or all of
00241 //               their data within a CycleData subclass, in support of
00242 //               pipelining.  This will call the virtual
00243 //               CycleData::write_datagram() method to do the actual
00244 //               writing.
00245 ////////////////////////////////////////////////////////////////////
00246 void BamWriter::
00247 write_cdata(Datagram &packet, const PipelineCyclerBase &cycler) {
00248   const CycleData *cdata = cycler.read();
00249   cdata->write_datagram(this, packet);
00250   cycler.release_read(cdata);
00251 }
00252 
00253 ////////////////////////////////////////////////////////////////////
00254 //     Function: BamWriter::register_pta
00255 //       Access: Public
00256 //  Description: Prepares to write a PointerToArray to the Bam file,
00257 //               unifying references to the same pointer across the
00258 //               Bam file.
00259 //
00260 //               The writing object should call this prior to writing
00261 //               out a PointerToArray.  It will return true if the
00262 //               same pointer has been written previously, in which
00263 //               case the writing object need do nothing further; or
00264 //               it will return false if this particular pointer has
00265 //               not yet been written, in which case the writing
00266 //               object must then write out the contents of the array.
00267 //
00268 //               Also see the WRITE_PTA() macro, which consolidates
00269 //               the work that must be done to write a PTA.
00270 ////////////////////////////////////////////////////////////////////
00271 bool BamWriter::
00272 register_pta(Datagram &packet, const void *ptr) {
00273   if (ptr == (const void *)NULL) {
00274     // A zero for the PTA ID indicates a NULL pointer.  This is a
00275     // special case.
00276     packet.add_uint16(0);
00277 
00278     // We return false to indicate the user must now write out the
00279     // "definition" of the NULL pointer.  This is necessary because of
00280     // a quirk in the BamReader's design, which forces callers to read
00281     // the definition of every NULL pointer.  Presumably, the caller
00282     // will be able to write the definition in a concise way that will
00283     // clearly indicate a NULL pointer; in the case of a
00284     // PointerToArray, this will generally be simply a zero element
00285     // count.
00286     return false;
00287   }
00288 
00289   PTAMap::iterator pi = _pta_map.find(ptr);
00290   if (pi == _pta_map.end()) {
00291     // We have not encountered this pointer before.
00292     int pta_id = _next_pta_id;
00293     _next_pta_id++;
00294 
00295     // Make sure our PTA ID will fit within the PN_uint16 we have
00296     // allocated for it.
00297     nassertr(pta_id <= 65535, 0);
00298 
00299     bool inserted = _pta_map.insert(PTAMap::value_type(ptr, pta_id)).second;
00300     nassertr(inserted, false);
00301 
00302     packet.add_uint16(pta_id);
00303 
00304     // Return false to indicate the caller must now write out the
00305     // array definition.
00306     return false;
00307 
00308   } else {
00309     // We have encountered this pointer before.
00310     int pta_id = (*pi).second;
00311     packet.add_uint16(pta_id);
00312 
00313     // Return true to indicate the caller need do nothing further.
00314     return true;
00315   }
00316 }
00317 
00318 ////////////////////////////////////////////////////////////////////
00319 //     Function: BamWriter::write_handle
00320 //       Access: Public
00321 //  Description: Writes a TypeHandle to the file in such a way that
00322 //               the BamReader can read the same TypeHandle later via
00323 //               read_handle().
00324 ////////////////////////////////////////////////////////////////////
00325 void BamWriter::
00326 write_handle(Datagram &packet, TypeHandle type) {
00327   // We encode TypeHandles within the Bam file by writing a unique
00328   // index number for each one to the file.  When we write a
00329   // particular TypeHandle for the first time, we assign it a new
00330   // index number and then immediately follow it by its definition;
00331   // when we write the same TypeHandle on subsequent times we only
00332   // write the index number.
00333 
00334   // The unique number we choose is actually the internal index number
00335   // of the TypeHandle.  Why not?
00336   int index = type.get_index();
00337 
00338   // Also make sure the index number fits within a PN_uint16.
00339   nassertv(index <= 65535);
00340 
00341   packet.add_uint16(index);
00342 
00343   if (index != 0) {
00344     bool inserted = _types_written.insert(index).second;
00345 
00346     if (inserted) {
00347       // This is the first time this TypeHandle has been written, so
00348       // also write out its definition.
00349       packet.add_string(type.get_name());
00350 
00351       // We also need to write the derivation of the TypeHandle, in case
00352       // the program reading this file later has never heard of this
00353       // type before.
00354       int num_parent_classes = type.get_num_parent_classes();
00355       nassertv(num_parent_classes <= 255);  // Good grief!
00356       packet.add_uint8(num_parent_classes);
00357       for (int i = 0; i < num_parent_classes; i++) {
00358         write_handle(packet, type.get_parent_class(i));
00359       }
00360     }
00361   }
00362 }
00363 
00364 ////////////////////////////////////////////////////////////////////
00365 //     Function: BamWriter::enqueue_object
00366 //       Access: Private
00367 //  Description: Assigns an object ID to the object and queues it up
00368 //               for later writing to the Bam file.
00369 //
00370 //               The return value is the object ID, or 0 if there is
00371 //               an error.
00372 ////////////////////////////////////////////////////////////////////
00373 int BamWriter::
00374 enqueue_object(const TypedWritable *object) {
00375   Datagram dg;
00376 
00377   nassertr(object != TypedWritable::Null, 0);
00378 
00379   // No object should ever be written out that is not registered as a
00380   // child of TypedWritable.  The only way this can happen is if
00381   // someone failed to initialize their type correctly in init_type().
00382 #ifndef NDEBUG
00383   if (!object->is_of_type(TypedWritable::get_class_type())) {
00384     util_cat.error()
00385       << "Type " << object->get_type() 
00386       << " does not indicate inheritance from TypedWritable.\n"
00387       << "(this is almost certainly an oversight in " << object->get_type()
00388       << "::init_type().)\n";
00389   }
00390 #endif
00391 
00392   // We need to assign a unique index number to every object we write
00393   // out.  Has this object been assigned a number yet?
00394   int object_id;
00395   //  bool already_written;
00396 
00397   StateMap::iterator si = _state_map.find(object);
00398   if (si == _state_map.end()) {
00399     // No, it hasn't, so assign it the next number in sequence
00400     // arbitrarily.
00401     object_id = _next_object_id;
00402     //    already_written = false;
00403 
00404     // Make sure our object ID will fit within the PN_uint16 we have
00405     // allocated for it.
00406     nassertr(object_id <= 65535, 0);
00407 
00408     bool inserted =
00409       _state_map.insert(StateMap::value_type(object, StoreState(_next_object_id))).second;
00410     nassertr(inserted, false);
00411     _next_object_id++;
00412 
00413   } else {
00414     // Yes, it has; get the object ID.
00415     object_id = (*si).second._object_id;
00416     //    already_written = (*si).second._written;
00417   }
00418 
00419   _object_queue.push_back(object);
00420   return object_id;
00421 }

Generated on Fri May 2 00:43:30 2003 for Panda by doxygen1.3