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 }