opennurbs_archive.cpp
Go to the documentation of this file.
00001 /* $NoKeywords: $ */
00002 /*
00003 //
00004 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
00005 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
00006 // McNeel & Associates.
00007 //
00008 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
00009 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
00010 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
00011 //                              
00012 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
00013 //
00015 */
00016 
00017 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
00018 
00019 FILE* ON_FileStream::Open( const wchar_t* filename, const wchar_t* mode )
00020 {
00021   FILE* fp = 0;
00022 
00023   if ( 0 == filename || 0 == filename[0] || 0 == mode || 0 == mode[0] )
00024     return fp;
00025 
00026 #if defined(ON_OS_WINDOWS)
00027   errno_t e = _wfopen_s(&fp,filename,mode); 
00028   if ( 0 != e && 0 == fp )
00029     fp = 0; // reference e to keep lint quiet.
00030 #else
00031   // I can't find an wfopen() or _wfopen() in
00032   // gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)
00033   ON_String fnameUTF8(filename);
00034   ON_String modeUTF8(mode);
00035   fp = fopen(fnameUTF8,modeUTF8);
00036 #endif
00037 
00038   return fp;
00039 }
00040 
00041 FILE* ON_FileStream::Open( const char* filename, const char* mode )
00042 {
00043   FILE* fp = 0;
00044 
00045   if ( 0 == filename || 0 == filename[0] || 0 == mode || 0 == mode[0] )
00046     return fp;
00047 
00048 #if defined(ON_OS_WINDOWS)
00049   errno_t e = fopen_s(&fp,filename,mode); 
00050   if ( 0 != e && 0 == fp )
00051     fp = 0; // reference e to keep lint quiet.
00052 #else
00053   fp = fopen(filename,mode);
00054 #endif
00055 
00056   return fp;
00057 }
00058 
00059 int ON_FileStream::Close( FILE* fp )
00060 {
00061   return ( ( 0 != fp ) ? fclose(fp) : -1 );
00062 }
00063 
00064 ON__INT64 ON_FileStream::CurrentPosition( FILE* fp )
00065 {
00066   if ( 0 == fp )
00067     return -1;
00068 #if defined(ON_OS_WINDOWS)
00069   return _ftelli64(fp);
00070 #else
00071   return ftell(fp);
00072 #endif
00073 }
00074 
00075 bool ON_FileStream::SeekFromCurrentPosition( FILE* fp, ON__INT64 offset )
00076 {
00077   return ON_FileStream::Seek(fp,offset,SEEK_CUR);
00078 }
00079 
00080 bool ON_FileStream::SeekFromStart( FILE* fp, ON__INT64 offset )
00081 {
00082   return ON_FileStream::Seek(fp,offset,SEEK_SET);
00083 }
00084 
00085 bool ON_FileStream::SeekFromEnd( FILE* fp, ON__INT64 offset )
00086 {
00087   return ON_FileStream::Seek(fp,offset,SEEK_END);
00088 }
00089 
00090 bool ON_FileStream::Seek( FILE* fp, ON__INT64 offset, int origin )
00091 {
00092   if ( 0 == fp )
00093     return false;
00094 
00095   if ( origin < 0 || origin > 2 )
00096     return false;
00097 
00098   if ( 0 == offset )
00099     return true;
00100   
00101 #if defined(ON_OS_WINDOWS)
00102 
00103   if ( 0 != _fseeki64(fp,offset,origin) )
00104     return false;
00105 
00106 #else
00107 
00108   const int i = 2147483646;
00109   const ON__INT64 i64 = i;
00110   while ( offset > i64 )
00111   {
00112     if ( 0 != fseek( fp, i, origin ) )
00113       return false;
00114     offset -= i64;
00115   }
00116   while ( offset < -i64 )
00117   {
00118     if ( 0 != fseek( fp, -i, origin ) )
00119       return false;
00120     offset += i64;
00121   }
00122   if ( 0 != offset )
00123   {
00124     int ioffset = (int)offset;
00125     if ( 0 != fseek( fp, ioffset, origin ) )
00126       return false;
00127   }
00128 
00129 #endif
00130 
00131   return true;
00132 }
00133 
00134 ON__UINT64 ON_FileStream::Read( FILE* fp, ON__UINT64 count, void* buffer )
00135 {
00136   ON__UINT64 rc = 0;
00137   if ( 0 == fp || count <= 0 || 0 == buffer )
00138     return rc;
00139 
00140   if ( count <= ON_MAX_SIZE_T )
00141   {
00142     rc = (ON__UINT64)fread(buffer,1,(size_t)count,fp);
00143   }
00144   else
00145   {
00146     size_t sz, szread;
00147     while ( count > 0 )
00148     {
00149       sz = ( count > ON_MAX_SIZE_T ) ? ON_MAX_SIZE_T : ((size_t)count);
00150       szread = fread(buffer,1,sz,fp);
00151       rc += szread;
00152       if ( szread != sz )
00153         break;
00154       count -= sz;
00155       buffer = ((unsigned char*)buffer) + sz;
00156     }
00157   }
00158 
00159  return rc;
00160 }
00161 
00162 ON__UINT64 ON_FileStream::Write( FILE* fp, ON__UINT64 count, const void* buffer )
00163 {
00164   ON__UINT64 rc = 0;
00165   if ( 0 == fp || count <= 0 || 0 == buffer )
00166     return rc;
00167 
00168   if ( count <= ON_MAX_SIZE_T )
00169   {
00170     rc = fwrite(buffer,1,(size_t)count,fp);
00171   }
00172   else
00173   {
00174     size_t sz, szwrite;
00175     while ( count > 0 )
00176     {
00177       sz = ( count > ON_MAX_SIZE_T ) ? ON_MAX_SIZE_T : ((size_t)count);
00178       szwrite = fwrite(buffer,1,sz,fp);
00179       rc += szwrite;
00180       if ( szwrite != sz )
00181         break;
00182       count -= sz;
00183       buffer = ((unsigned char*)buffer) + sz;
00184     }
00185   }
00186 
00187  return rc;
00188 }
00189 
00190 bool ON_FileStream::Flush( FILE* fp )
00191 {
00192   if ( 0 == fp )
00193     return false;
00194   if ( 0 != fflush(fp) )
00195     return false;
00196   return true;
00197 }
00198 
00199 
00200 bool ON_FileStream::GetFileInformation( 
00201     FILE* fp,
00202     ON__UINT64* file_size,
00203     ON__UINT64* file_create_time,
00204     ON__UINT64* file_last_modified_time
00205     )
00206 {
00207   bool rc = false;
00208 
00209   if (file_size)
00210     *file_size = 0;
00211   if (file_create_time)
00212     *file_create_time = 0;
00213   if (file_last_modified_time)
00214     *file_last_modified_time = 0;
00215 
00216   if ( fp )
00217   {
00218 
00219 #if defined(ON_COMPILER_MSC)
00220 
00221     // Microsoft compilers
00222     int fd = _fileno(fp);    
00223 #if (_MSC_VER >= 1400)
00224     // VC 8 (2005) 
00225     // works for file sizes > 4GB 
00226     // when size_t is a 64 bit integer
00227     struct _stat64 sb;
00228     memset(&sb,0,sizeof(sb));
00229     int fstat_rc = _fstat64(fd, &sb);
00230 #else
00231     // VC6 compiler
00232     // works on most compilers
00233     struct _stat sb;
00234     memset(&sb,0,sizeof(sb));
00235     int fstat_rc = _fstat(fd, &sb);
00236 #endif
00237 
00238 #else
00239     // works on most compilers
00240     int fd = fileno(fp);
00241     struct stat sb;
00242     memset(&sb,0,sizeof(sb));
00243     int fstat_rc = fstat(fd, &sb);
00244 #endif
00245 
00246 
00247     if (0 == fstat_rc)
00248     {
00249       if (file_size)
00250         *file_size = (ON__UINT64)sb.st_size;
00251       if (file_create_time)
00252         *file_create_time = (ON__UINT64)sb.st_ctime;
00253       if (file_last_modified_time)
00254         *file_last_modified_time = (ON__UINT64)sb.st_mtime;
00255       rc = true;
00256     }
00257   }
00258 
00259   return rc;
00260 }
00261 
00262 
00263 class ON_ReadChunkHelper
00264 {
00265 public:
00266   ON_ReadChunkHelper(ON_BinaryArchive&);
00267   ~ON_ReadChunkHelper();
00268 
00269   ON_BinaryArchive& m_binary_archive;
00270   bool m_bReadSuccess;
00271   ON__UINT32 m_chunk_tcode;
00272   ON__INT64 m_chunk_value;
00273 
00274 private:
00275   bool m_bCallEndRead3dmChunk;
00276   // prohibit use - no implementation
00277   ON_ReadChunkHelper();
00278   ON_ReadChunkHelper(const ON_ReadChunkHelper&);
00279   ON_ReadChunkHelper& operator=(const ON_ReadChunkHelper&);
00280 };
00281 
00282 ON_ReadChunkHelper::ON_ReadChunkHelper(ON_BinaryArchive& binary_archive)
00283 : m_binary_archive(binary_archive)
00284 , m_bReadSuccess(0)
00285 , m_chunk_tcode(0)
00286 , m_chunk_value(0)
00287 , m_bCallEndRead3dmChunk(0)
00288 {
00289   m_bReadSuccess = m_binary_archive.BeginRead3dmBigChunk(&m_chunk_tcode,&m_chunk_value);
00290   if ( m_bReadSuccess )
00291     m_bCallEndRead3dmChunk = true;
00292 }
00293 
00294 ON_ReadChunkHelper::~ON_ReadChunkHelper()
00295 {
00296   if ( m_bReadSuccess && !m_binary_archive.EndRead3dmChunk() )
00297     m_bReadSuccess = false;
00298 }
00299 
00300 bool ON_IsUnsignedChunkTypecode( ON__UINT32 typecode )
00301 {
00302   // returns tru if the chunk value should be treated as an unsigned int.
00303   return ( 0 == (TCODE_SHORT & typecode)
00304            || TCODE_RGB == typecode 
00305            || TCODE_RGBDISPLAY == typecode
00306            || TCODE_PROPERTIES_OPENNURBS_VERSION == typecode
00307            || TCODE_OBJECT_RECORD_TYPE == typecode
00308          );
00309 }
00310 
00311 bool ON_IsLongChunkTypecode(ON__UINT32 typecode)
00312 {
00313   // NOTE: RenderXXXX plug-in used zero as a typecode in material userdata, sigh ...
00314   //return (0 != typecode && 0 == (TCODE_SHORT & typecode));
00315   return (0 == (TCODE_SHORT & typecode));
00316 }
00317 
00318 bool ON_IsShorChunkTypecode(ON__UINT32 typecode)
00319 {
00320   return (0 != (TCODE_SHORT & typecode));
00321 }
00322 
00323 static
00324 bool DownSizeINT( ON__INT64 i64, ON__INT32* i32 )
00325 {
00326   const static ON__INT64 i32max = 2147483647;
00327   if ( i64 <= i32max && i64 >= (-i32max - 1) )
00328   {
00329     *i32 = (ON__INT32)i64;
00330     return true;
00331   }
00332 
00333   ON_ERROR("i64 too big to convert to 4 byte signed int");
00334   *i32 = 0;
00335   return false;
00336 }
00337 
00338 static
00339 bool DownSizeUINT( ON__UINT64 u64, ON__UINT32* u32 )
00340 {
00341   if ( u64 <= 0xFFFFFFFF )
00342   {
00343     *u32 = (ON__UINT32)u64;
00344     return true;
00345   }
00346 
00347   ON_ERROR("u64 too big to convert to 4 byte unsigned int");
00348   *u32 = 0;
00349   return false;
00350 }
00351 
00352 static
00353 bool DownSizeChunkValue( ON__UINT32 typecode, ON__INT64 v64, ON__INT32* v32 )
00354 {
00355   if ( 0 == v32 )
00356     return true;
00357   return ( ON_IsLongChunkTypecode(typecode) )
00358          ? DownSizeUINT( (ON__UINT64)v64, (ON__UINT32*)v32 )
00359          : DownSizeINT( v64, v32 );
00360 }
00361 
00362 
00363 
00364 struct ON__3dmV1LayerIndex
00365 {
00366   int m_layer_index;
00367   int m_layer_name_length;
00368   char* m_layer_name;
00369   struct ON__3dmV1LayerIndex* m_next;
00370 };
00371 
00372 ON_BinaryArchive::ON_BinaryArchive( ON::archive_mode mode ) 
00373                  : m_3dm_version(0), 
00374                    m_3dm_v1_layer_index(0), m_3dm_v1_material_index(0),
00375                    m_error_message_mask(0),
00376                    m_3dm_opennurbs_version(0),
00377                    m_3dm_start_section_offset(0),
00378                    m_active_table(ON_BinaryArchive::no_active_table),
00379                    m_bDoChunkCRC(0), m_bad_CRC_count(0),
00380                    m_endian(ON::Endian()), 
00381                    m_mode(mode)
00382 {
00383   // Sparc, MIPS, ... CPUs have big endian byte order
00384   // ON_BinaryArchives use little endian byte order
00385 
00386   m_bSaveUserData = true; // true to save user data (increases file size)
00387   m_bSavePreviewImage    = false; // true to save 200x200 preview bitmap (increases file size)
00388   m_bEmbedTextureBitmaps = false; // true to embed texture, bump, trace, and wallpaper bitmaps (increases file size)
00389   m_bSaveRenderMeshes    = false; // true to save meshes used to render B-rep objects (increases file size)
00390   m_bSaveAnalysisMeshes  = false; // true to save meshes used in surface analysis (increases file size)
00391 
00392   m_zlib.mode = ON::unknown_archive_mode;
00393   memset( &m_zlib.strm, 0, sizeof(m_zlib.strm) );
00394 
00395   m_V1_layer_list = 0;
00396 }
00397 
00398 ON_BinaryArchive::~ON_BinaryArchive()
00399 {
00400   if ( 0 != m_V1_layer_list )
00401   {
00402     struct ON__3dmV1LayerIndex* next = m_V1_layer_list;
00403     m_V1_layer_list = 0;
00404     for ( int i = 0; 0 != next && i < 1000; i++ )
00405     {
00406       struct ON__3dmV1LayerIndex* p = next;
00407       next = p->m_next;
00408       onfree(p);
00409     }
00410   }
00411 
00412   CompressionEnd();
00413 }
00414 
00415 
00416 bool ON_BinaryArchive::ToggleByteOrder(
00417   int count,          // number of elements
00418   int sizeof_element, // size of element (2,4, or 8)
00419   const void* src,    // source buffer
00420   void* dst           // destination buffer (can be same as source buffer)
00421   )
00422 {
00423   unsigned char c[32];
00424   const unsigned char* a = (const unsigned char*)src;
00425   unsigned char* b = (unsigned char*)dst;
00426 
00427   bool rc = (count==0 || (count>0&&src&&dst));
00428   if ( rc ) 
00429   {
00430     // loops are unrolled and a switch is used
00431     // to speed things up a bit.
00432     switch(sizeof_element) 
00433     {
00434     case 2:
00435       while(count--) 
00436       {
00437         c[0] = *a++;
00438         c[1] = *a++;
00439         *b++ = c[1];
00440         *b++ = c[0];
00441       }
00442       break;
00443 
00444     case 4:
00445       while(count--) 
00446       {
00447         c[0] = *a++;
00448         c[1] = *a++;
00449         c[2] = *a++;
00450         c[3] = *a++;
00451         *b++ = c[3];
00452         *b++ = c[2];
00453         *b++ = c[1];
00454         *b++ = c[0];
00455       }
00456       break;
00457 
00458     case 8:
00459       while(count--) 
00460       {
00461         c[0] = *a++;
00462         c[1] = *a++;
00463         c[2] = *a++;
00464         c[3] = *a++;
00465         c[4] = *a++;
00466         c[5] = *a++;
00467         c[6] = *a++;
00468         c[7] = *a++;
00469         *b++ = c[7];
00470         *b++ = c[6];
00471         *b++ = c[5];
00472         *b++ = c[4];
00473         *b++ = c[3];
00474         *b++ = c[2];
00475         *b++ = c[1];
00476         *b++ = c[0];
00477       }
00478       break;
00479 
00480     default:
00481       if ( sizeof_element > 0 && sizeof_element < 32 )
00482       {
00483         // As of 2 May 2003, this case is never used
00484         // by core opennurbs objects.
00485         //
00486         // This is here so that future code will work
00487         // if and when 128 bit "ints"/"doubles" become common
00488         // enough that they can be stored in 3dm files.
00489         // It may also happen that third party applications
00490         // on specialized CPUs need to toggle byte order
00491         // for 128 bit ints/doubles stored as user data.
00492         int i;
00493         while(count--)
00494         {
00495           for (i = 0; i < sizeof_element; i++)
00496             c[i] = *a++;
00497           while(i--)
00498             *b++ = c[i];
00499         }
00500       }
00501       else
00502       {
00503         rc = false;
00504       }
00505       break;
00506     }
00507   }
00508   return rc;
00509 }
00510 
00511 bool ON_BinaryArchive::BigSeekFromStart( ON__UINT64 offset )
00512 {
00513   // SeekFromStart() is a virutal function that
00514   // any developer can implement.  Some implementations
00515   // may use signed 4 byte int in critical places.
00516   // BigSeekFromStart() will work correctly in
00517   // this worst case situation.
00518   return ( offset > 2147483632 )
00519     ? ( SeekFromStart(2147483632) && BigSeekForward(offset - 2147483632) )
00520     : SeekFromStart((size_t)offset);
00521 }
00522 
00523 
00524 
00525 bool ON_BinaryArchive::BigSeekForward( ON__UINT64 offset )
00526 {
00527   // SeekFromCurrentPosition() is a virutal function that
00528   // uses a signed 4 byte int in critical places.
00529   // BigSeekForward() will work correctly when
00530   // offset is larger than the maximum value of a signed
00531   // 4 byte int.
00532   while ( offset > 2147483632 )
00533   {
00534     if ( !SeekFromCurrentPosition(2147483632) )
00535       return false;
00536     offset -= 2147483632;
00537   }
00538   if ( offset > 0 )
00539   {
00540     int ioffset32 = (int)((ON__INT64)offset);
00541     return SeekFromCurrentPosition(ioffset32);
00542   }
00543   return true;
00544 }
00545 
00546 
00547 bool ON_BinaryArchive::BigSeekBackward( ON__UINT64 offset )
00548 {
00549   // SeekFromCurrentPosition() is a virutal function that
00550   // uses a signed 4 byte int in critical places.
00551   // BigSeekBackward() will work correctly when
00552   // offset is larger than the maximum value of a signed
00553   // 4 byte int.
00554   while ( offset > 2147483632 )
00555   {
00556     if ( !SeekFromCurrentPosition(-2147483632) )
00557       return false;
00558     offset -= 2147483632;
00559   }
00560   if ( offset > 0 )
00561   {
00562     int ioffset32 = (int)((ON__INT64)offset);
00563     return SeekFromCurrentPosition(-ioffset32);
00564   }
00565   return true;
00566 }
00567 
00568 
00569 bool ON_BinaryArchive::BigSeekFromCurrentPosition( ON__INT64 offset )
00570 {
00571   // SeekFromCurrentPosition() is a virutal function that
00572   // uses a signed 4 byte int in critical places.
00573   // BigSeekFromCurrentPosition() will work correctly when
00574   // offset is smaller than the minimum value of a signed
00575   // 4 byte int or the maximum value of a signed 4 byte int.
00576   return ( offset >= 0 ) 
00577          ? BigSeekForward((ON__UINT64)offset)
00578          : BigSeekBackward((ON__UINT64)(-offset));
00579 }
00580 
00581 bool
00582 ON_BinaryArchive::ReadMode() const
00583 {
00584   return (m_mode & ON::read) ? true : false;
00585 }
00586 
00587 bool
00588 ON_BinaryArchive::WriteMode() const
00589 {
00590   return (m_mode & ON::write) ? true : false;
00591 }
00592 
00593 bool
00594 ON_BinaryArchive::ReadChar(    // Read an array of 8 bit chars
00595                 size_t count,       // number of chars to read
00596                 char*  p  
00597                 )
00598 {
00599   return ReadByte( count, p );
00600 }
00601 
00602 bool
00603 ON_BinaryArchive::ReadChar(    // Read an array of 8 bit unsigned chars
00604                 size_t count,       // number of unsigned chars to read
00605                 unsigned char* p   
00606                 )
00607 {
00608   return ReadByte( count, p );
00609 }
00610 
00611 bool
00612 ON_BinaryArchive::ReadChar(    // Read a single 8 bit char
00613                 char* p
00614                 )
00615 {
00616   return ReadByte( 1, p );
00617 }
00618 
00619 bool
00620 ON_BinaryArchive::ReadChar(    // Read a single 8 bit unsigned char
00621                 unsigned char* p
00622                 )
00623 {
00624   return ReadByte( 1, p );
00625 }
00626 
00627 bool
00628 ON_BinaryArchive::ReadInt16( // Read an array of 16 bit integers
00629                 size_t count,            // number of unsigned integers to read
00630                 ON__INT16* p
00631                 )
00632 {
00633   bool rc = ReadByte( count<<1, p );
00634   if ( rc && m_endian == ON::big_endian )
00635   {
00636     // reverse byte order
00637                 unsigned char* b= (unsigned char*) (p);
00638                 unsigned char  c;
00639                 while(count--) {
00640                         c = b[0]; b[0] = b[1]; b[1] = c;
00641                         b += 2;
00642                 }
00643   }
00644   return rc;
00645 }
00646 
00647 bool
00648 ON_BinaryArchive::ReadShort(   // Read an array of 16 bit shorts
00649                 size_t count,       // number of unsigned chars to read
00650                 short* p
00651                 )
00652 {
00653 #if defined(ON_COMPILER_MSC)
00654 #pragma warning( push )
00655 // Disable the MSC /W4 "conditional expression is constant" warning
00656 // about 2 == sizeof(*p).  Since this code has to run on machines
00657 // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary.
00658 #pragma warning( disable : 4127 )
00659 #endif
00660 
00661   bool rc = true;
00662 
00663   if ( 2 == sizeof(*p) )
00664   {
00665     rc = ReadInt16( count, (ON__INT16*)p );
00666   }
00667   else
00668   {
00669     size_t j;
00670     ON__INT16 i16;
00671     for ( j = 0; j < count && rc; j++ )
00672     {
00673       rc = ReadInt16( 1, &i16 );
00674       *p++ = (short)i16;
00675     }
00676   }
00677   return rc;
00678 
00679 #if defined(ON_COMPILER_MSC)
00680 #pragma warning( pop )
00681 #endif
00682 }
00683 
00684 bool
00685 ON_BinaryArchive::ReadShort(   // Read an array of 16 bit unsigned shorts
00686                 size_t count,       // number of unsigned chars to read
00687                 unsigned short* p
00688                 )
00689 {
00690   return ReadShort( count, (short*)p );
00691 }
00692 
00693 bool
00694 ON_BinaryArchive::ReadShort(   // Read a single 16 bit short
00695                 short* p
00696                 )
00697 {
00698   return ReadShort( 1, p );
00699 }
00700 
00701 bool
00702 ON_BinaryArchive::ReadShort(   // Read a single 16 bit unsigned short
00703                 unsigned short* p
00704                 )
00705 {
00706   return ReadShort( 1, p );
00707 }
00708 
00709 bool
00710 ON_BinaryArchive::ReadInt32( // Read an array of 32 bit integers
00711                 size_t count,            // number of 32 bit integers to read
00712                 ON__INT32* p
00713                 )
00714 {
00715   bool rc = ReadByte( count<<2, p );
00716   if ( rc && m_endian == ON::big_endian ) 
00717   {
00718                 unsigned char* b= (unsigned char*)p;
00719                 unsigned char  c;
00720                 while(count--) {
00721                         c = b[0]; b[0] = b[3]; b[3] = c;
00722                         c = b[1]; b[1] = b[2]; b[2] = c;
00723                         b += 4;
00724                 }
00725   }
00726   return rc;
00727 }
00728 
00729 bool
00730 ON_BinaryArchive::ReadInt( // Read an array of integers
00731                 size_t count,          // number of unsigned chars to read
00732                 int* p
00733                 )
00734 {
00735 #if defined(ON_COMPILER_MSC)
00736 #pragma warning( push )
00737 // Disable the MSC /W4 "conditional expression is constant" warning
00738 // about 4 == sizeof(*p).  Since this code has to run on machines
00739 // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary.
00740 #pragma warning( disable : 4127 )
00741 #endif
00742 
00743   bool rc;
00744   if ( 4 == sizeof(*p) )
00745   {
00746     rc = ReadInt32( count, (ON__INT32*)p );
00747   }
00748   else
00749   {
00750     rc = true;
00751     ON__INT32 i32;
00752     size_t j;
00753     for ( j = 0; j < count && rc; j++ )
00754     {
00755       rc = ReadInt32(1,&i32);
00756       if (rc)
00757         *p++ = (int)i32;
00758     }
00759   }
00760   return rc;
00761 
00762 #if defined(ON_COMPILER_MSC)
00763 #pragma warning( pop )
00764 #endif
00765 }
00766 
00767 bool
00768 ON_BinaryArchive::ReadInt( // Read an array of 32 bit integers
00769                 size_t count,       // number of unsigned chars to read
00770                 unsigned int* p
00771                 )
00772 {
00773   return ReadInt( count, (int*)p );
00774 }
00775 
00776 bool
00777 ON_BinaryArchive::ReadInt( // Read a single 32 bit integer
00778                 int* p
00779                 )
00780 {
00781   return ReadInt( 1, p );
00782 }
00783 
00784 bool
00785 ON_BinaryArchive::ReadInt( // Read a single 32 bit unsigned integer
00786                 unsigned int* p
00787                 )
00788 {
00789   return ReadInt( 1, p );
00790 }
00791 
00792 bool ON_BinaryArchive::ReadBigInt( // Read an array of 64 bit integers
00793                 size_t count,
00794                 ON__INT64* p 
00795                 )
00796 {
00797   return ReadInt64(1,p);
00798 }
00799 
00800 bool ON_BinaryArchive::ReadBigInt( // Read an array of 64 bit integers
00801                 size_t count,
00802                 ON__UINT64* p
00803                 )
00804 {
00805   return ReadInt64(1,(ON__INT64*)p);
00806 }
00807 
00808 bool ON_BinaryArchive::ReadBigInt( // Read a single 64 bit integer
00809                 ON__INT64* p
00810                 )
00811 {
00812   return ReadInt64(1,p);
00813 }
00814 
00815 bool ON_BinaryArchive::ReadBigInt( // Read a single 64 bit unsigned integer
00816                 ON__UINT64* p
00817                 )
00818 {
00819   return ReadInt64(1,(ON__INT64*)p);
00820 }
00821 
00822 
00823 
00824 bool
00825 ON_BinaryArchive::ReadLong( // Read an array of 32 bit integers
00826                 size_t count,       // number of unsigned chars to read
00827                 long* p
00828                 )
00829 {
00830 #if defined(ON_COMPILER_MSC)
00831 #pragma warning( push )
00832 // Disable the MSC /W4 "conditional expression is constant" warning
00833 // about 4 == sizeof(*p).  Since this code has to run on machines
00834 // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary.
00835 #pragma warning( disable : 4127 )
00836 #endif
00837 
00838   bool rc;
00839   if ( 4 == sizeof(*p) )
00840   {
00841     rc = ReadInt32( count, (ON__INT32*)p );
00842   }
00843   else
00844   {
00845     rc = true;
00846     ON__INT32 i32;
00847     size_t j;
00848     for ( j = 0; j < count && rc; j++ )
00849     {
00850       rc = ReadInt32(1,&i32);
00851       if (rc)
00852         *p++ = (long)i32;
00853     }
00854   }
00855   return rc;
00856 
00857 #if defined(ON_COMPILER_MSC)
00858 #pragma warning( pop )
00859 #endif
00860 }
00861 
00862 bool
00863 ON_BinaryArchive::ReadLong( // Read an array of 32 bit integers
00864                 size_t count,       // number of unsigned chars to read
00865                 unsigned long* p
00866                 )
00867 {
00868   return ReadLong( count, (long*)p );
00869 }
00870 
00871 bool
00872 ON_BinaryArchive::ReadLong( // Read a single 32 bit integer
00873                 long* p
00874                 )
00875 {
00876   return ReadLong( 1, (long*)p );
00877 }
00878 
00879 bool
00880 ON_BinaryArchive::ReadLong( // Read a single 32 bit unsigned integer
00881                 unsigned long* p
00882                 )
00883 {
00884   return ReadLong( 1, (long*)p );
00885 }
00886 
00887 bool
00888 ON_BinaryArchive::ReadFloat(   // Read an array of floats
00889                 size_t count,       // number of unsigned chars to read
00890                 float* p
00891                 )
00892 {
00893   // 32 bit floats and 32 bit integers have same size and endian issues
00894   return ReadInt32( count, (ON__INT32*)p );
00895 }
00896 
00897 bool
00898 ON_BinaryArchive::ReadFloat(   // Read a single float
00899                 float* p
00900                 )
00901 {
00902   return ReadFloat( 1, p );
00903 }
00904 
00905 bool
00906 ON_BinaryArchive::ReadDouble(  // Read an array of IEEE 64 bit doubles
00907                 size_t count,       // number of unsigned chars to read
00908                 double* p
00909                 )
00910 {
00911   bool rc = ReadByte( count<<3, p );
00912   if ( rc && m_endian == ON::big_endian ) 
00913   {
00914                 unsigned char* b=(unsigned char*)p;
00915                 unsigned char  c;
00916                 while(count--) {
00917                         c = b[0]; b[0] = b[7]; b[7] = c;
00918                         c = b[1]; b[1] = b[6]; b[6] = c;
00919                         c = b[2]; b[2] = b[5]; b[5] = c;
00920                         c = b[3]; b[3] = b[4]; b[4] = c;
00921                         b += 8;
00922                 }
00923   }
00924   return rc;
00925 }
00926 
00927 bool
00928 ON_BinaryArchive::ReadDouble(  // Read a single double
00929                 double* p
00930                 )
00931 {
00932   return ReadDouble( 1, p );
00933 }
00934 
00935 bool
00936 ON_BinaryArchive::ReadColor( ON_Color& color )
00937 {
00938   unsigned int colorref = 0;
00939   bool rc = ReadInt( &colorref );
00940   color = colorref;
00941   return rc;
00942 }
00943 
00944 bool
00945 ON_BinaryArchive::ReadPoint (
00946   ON_2dPoint& p
00947   )
00948 {
00949   return ReadDouble( 2, &p.x );
00950 }
00951 
00952 bool
00953 ON_BinaryArchive::ReadPoint (
00954   ON_3dPoint& p
00955   )
00956 {
00957   return ReadDouble( 3, &p.x );
00958 }
00959 
00960 bool
00961 ON_BinaryArchive::ReadPoint (
00962   ON_4dPoint& p
00963   )
00964 {
00965   return ReadDouble( 4, &p.x );
00966 }
00967 
00968 bool
00969 ON_BinaryArchive::ReadVector (
00970   ON_2dVector& v
00971   )
00972 {
00973   return ReadDouble( 2, &v.x );
00974 }
00975 
00976 bool
00977 ON_BinaryArchive::ReadVector (
00978   ON_3dVector& v
00979   )
00980 {
00981   return ReadDouble( 3, &v.x );
00982 }
00983 
00984 bool ON_BinaryArchive::WriteBoundingBox(const ON_BoundingBox& bbox)
00985 {
00986   bool rc = WritePoint( bbox.m_min );
00987   if (rc) rc = WritePoint( bbox.m_max );
00988   return rc;
00989 }
00990 
00991 bool ON_BinaryArchive::ReadBoundingBox(ON_BoundingBox& bbox)
00992 {
00993   bool rc = ReadPoint( bbox.m_min );
00994   if (rc) rc = ReadPoint( bbox.m_max );
00995   return rc;
00996 }
00997 
00998 bool 
00999 ON_BinaryArchive::WriteXform( const ON_Xform& x )
01000 {
01001   return WriteDouble( 16, &x.m_xform[0][0] );
01002 }
01003 
01004 bool 
01005 ON_BinaryArchive::ReadXform( ON_Xform& x )
01006 {
01007   return ReadDouble( 16, &x.m_xform[0][0] );
01008 }
01009 bool 
01010 ON_BinaryArchive::WritePlaneEquation( const ON_PlaneEquation& plane_equation )
01011 {
01012   bool rc = WriteDouble( 4, &plane_equation.x );
01013   return rc;
01014 }
01015 
01016 bool 
01017 ON_BinaryArchive::ReadPlaneEquation( ON_PlaneEquation& plane_equation )
01018 {
01019   bool rc = ReadDouble( 4, &plane_equation.x );
01020   return rc;
01021 }
01022 
01023 bool 
01024 ON_BinaryArchive::WritePlane( const ON_Plane& plane )
01025 {
01026   bool rc = WritePoint( plane.origin );
01027   if (rc) rc = WriteVector( plane.xaxis );
01028   if (rc) rc = WriteVector( plane.yaxis );
01029   if (rc) rc = WriteVector( plane.zaxis );
01030   if (rc) rc = WriteDouble( 4, &plane.plane_equation.x );
01031   return rc;
01032 }
01033 
01034 bool 
01035 ON_BinaryArchive::ReadPlane( ON_Plane& plane )
01036 {
01037   bool rc = ReadPoint( plane.origin );
01038   if (rc) rc = ReadVector( plane.xaxis );
01039   if (rc) rc = ReadVector( plane.yaxis );
01040   if (rc) rc = ReadVector( plane.zaxis );
01041   if (rc) rc = ReadDouble( 4, &plane.plane_equation.x );
01042   return rc;
01043 }
01044 
01045 bool 
01046 ON_BinaryArchive::WriteLine( const ON_Line& line )
01047 {
01048   bool rc = WritePoint( line.from );
01049   if (rc) rc = WritePoint( line.to );
01050   return rc;
01051 }
01052 
01053 bool 
01054 ON_BinaryArchive::ReadLine( ON_Line& line )
01055 {
01056   bool rc = ReadPoint( line.from );
01057   if (rc) rc = ReadPoint( line.to );
01058   return rc;
01059 }
01060 
01061 bool 
01062 ON_BinaryArchive::WriteArc(const ON_Arc& arc )
01063 {
01064   bool rc = WriteCircle(arc);
01065   if (rc)
01066     rc = WriteInterval(arc.m_angle);
01067   return rc;
01068 }
01069 
01070 bool 
01071 ON_BinaryArchive::ReadArc( ON_Arc& arc )
01072 {
01073   bool rc = ReadCircle(arc);
01074   if (rc)
01075     rc = ReadInterval(arc.m_angle);
01076   return rc;
01077 }
01078 
01079 bool 
01080 ON_BinaryArchive::WriteCircle(const ON_Circle& circle)
01081 {
01082   bool rc = WritePlane( circle.plane );
01083   if (rc)
01084     rc = WriteDouble( circle.radius );
01085   // m_point[] removed 2001, November, 7
01086   if (rc)
01087     rc = WritePoint( circle.PointAt(0.0) );
01088   if (rc)
01089     rc = WritePoint( circle.PointAt(0.5*ON_PI) );
01090   if (rc)
01091     rc = WritePoint( circle.PointAt(ON_PI) );
01092   /*
01093   if (rc)
01094     rc = WritePoint( circle.m_point[0] );
01095   if (rc)
01096     rc = WritePoint( circle.m_point[1] );
01097   if (rc)
01098     rc = WritePoint( circle.m_point[2] );
01099   */
01100   return rc;
01101 }
01102 
01103 bool 
01104 ON_BinaryArchive::ReadCircle(ON_Circle& circle)
01105 {
01106   ON_3dPoint scratch;
01107   bool rc = ReadPlane( circle.plane );
01108   if (rc)
01109     rc = ReadDouble( &circle.radius );
01110   // m_point[] removed 2001, November, 7
01111   if (rc)
01112     rc = ReadPoint( scratch );
01113   if (rc)
01114     rc = ReadPoint( scratch );
01115   if (rc)
01116     rc = ReadPoint( scratch );
01117   /*
01118   if (rc)
01119     rc = ReadPoint( circle.m_point[0] );
01120   if (rc)
01121     rc = ReadPoint( circle.m_point[1] );
01122   if (rc)
01123     rc = ReadPoint( circle.m_point[2] );
01124   */
01125   return rc;
01126 }
01127 
01128 
01129 bool 
01130 ON_BinaryArchive::WriteInterval( const ON_Interval& t )
01131 {
01132   return WriteDouble( 2, t.m_t );
01133 }
01134 
01135 bool 
01136 ON_BinaryArchive::ReadInterval( ON_Interval& t )
01137 {
01138   return ReadDouble( 2, t.m_t );
01139 }
01140 
01141 bool
01142 ON_BinaryArchive::ReadUuid( ON_UUID& uuid )
01143 {
01144   bool    rc = ReadInt32( 1, (ON__INT32*)(&uuid.Data1) );
01145   if (rc) rc = ReadInt16( 1, (ON__INT16*)(&uuid.Data2) );
01146   if (rc) rc = ReadInt16( 1, (ON__INT16*)(&uuid.Data3) );
01147   if (rc) rc = ReadByte( 8, uuid.Data4 );
01148   return rc;
01149 }
01150 
01151 bool ON_BinaryArchive::ReadDisplayMaterialRef( ON_DisplayMaterialRef& dmr )
01152 {
01153   bool rc = ReadUuid( dmr.m_viewport_id );
01154   if (rc) 
01155     rc = ReadUuid( dmr.m_display_material_id );
01156   return rc;
01157 }
01158 
01159 bool
01160 ON_BinaryArchive::ReadTime( struct tm& utc )
01161 {
01162   // utc = coordinated universal time ( a.k.a GMT, UTC )
01163   // (From ANSI C time() and gmtime().)
01164   bool rc = ReadInt( &utc.tm_sec );
01165   if ( rc )
01166     rc = ReadInt( &utc.tm_min );
01167   if ( rc )
01168     rc = ReadInt( &utc.tm_hour );
01169   if ( rc )
01170     rc = ReadInt( &utc.tm_mday );
01171   if ( rc )
01172     rc = ReadInt( &utc.tm_mon );
01173   if ( rc )
01174     rc = ReadInt( &utc.tm_year );
01175   if ( rc )
01176     rc = ReadInt( &utc.tm_wday );
01177   if ( rc )
01178     rc = ReadInt( &utc.tm_yday );
01179   if ( rc ) {
01180     if ( utc.tm_sec < 0 || utc.tm_sec > 60 )
01181       rc = false;
01182     if ( utc.tm_min < 0 || utc.tm_min > 60 )
01183       rc = false;
01184     if ( utc.tm_hour < 0 || utc.tm_hour > 24 )
01185       rc = false;
01186     if ( utc.tm_mday < 0 || utc.tm_mday > 31 )
01187       rc = false;
01188     if ( utc.tm_mon < 0 || utc.tm_mon > 12 )
01189       rc = false;
01190     // no year restrictions because dates are used in archeological userdata
01191     if ( utc.tm_wday < 0 || utc.tm_wday > 7 )
01192       rc = false;
01193     if ( utc.tm_yday < 0 || utc.tm_yday > 366 )
01194       rc = false;
01195     if ( !rc ) {
01196       ON_ERROR("ON_BinaryArchive::ReadTime() - bad time in archive");
01197     }
01198   }
01199   return rc;
01200 }
01201 
01202 bool
01203 ON_BinaryArchive::ReadStringSize( // Read size of NULL terminated string
01204     size_t* sizeof_string          // (returned size includes NULL terminator)
01205     )
01206 {
01207   ON__UINT32 ui32 = 0;
01208   bool rc = ReadInt32(1,(ON__INT32*)&ui32);
01209   // Note that ui32 = number of elements in the string array, including
01210   // the null terminator.  So ui32 should either be 0 or be >= 2.
01211   // The string array elements can be chars or unsigned shorts;
01212   // therefore the number of bytes in the string cannot be determined
01213   // at this point because we don't know what type of string is
01214   // being read.
01215   if (rc)
01216   {
01217     // 8 October 2004 Dale Lear
01218     //    Added the sanity checks on string size to avoid attempts
01219     //    to allocate huge amounts of memory when the value
01220     //    comes from a damaged file.
01221     if ( 0 != (0xF000000 & ui32) )
01222     {
01223       // 268 million chars oughta be plenty
01224       ON_ERROR("string element count is impossibly large");
01225       rc = false;
01226     }
01227     else if ( ui32 > 0 )
01228     {
01229       // make sure this is possible
01230       const ON_3DM_BIG_CHUNK* curchunk = m_chunk.Last();
01231       if ( 0 != curchunk && 0 == (TCODE_SHORT & curchunk->m_typecode) )
01232       {
01233         if (    curchunk->m_big_value < 0 
01234              || ((ON__INT64)ui32) > curchunk->m_big_value 
01235            )
01236         {
01237           ON_ERROR("string element count exceeds current chunk size");
01238           rc = false;
01239         }
01240       }
01241     }
01242 
01243     if (rc)
01244     {
01245       *sizeof_string = (size_t)ui32;
01246     }
01247   }
01248   return rc;
01249 }
01250 
01251 
01252 bool
01253 ON_BinaryArchive::ReadStringUTF8ElementCount(
01254     size_t* string_utf8_element_count
01255     )
01256 {
01257   ON__UINT32 ui32 = 0;
01258   bool rc = ReadInt32(1,(ON__INT32*)&ui32);
01259   // Note that ui32 = number of elements in the string array, including
01260   // the null terminator.  So ui32 should either be 0 or be >= 2.
01261   // The string array elements can be chars or unsigned shorts;
01262   // therefore the number of bytes in the string cannot be determined
01263   // at this point because we don't know what type of string is
01264   // being read.
01265   if (rc)
01266   {
01267     // 8 October 2004 Dale Lear
01268     //    Added the sanity checks on string size to avoid attempts
01269     //    to allocate huge amounts of memory when the value
01270     //    comes from a damaged file.
01271     if ( 0 != (0xF000000 & ui32) )
01272     {
01273       // 268 million chars oughta be plenty
01274       ON_ERROR("string element count is impossibly large");
01275       rc = false;
01276     }
01277     else if ( ui32 > 0 )
01278     {
01279       // make sure this is possible
01280       const ON_3DM_BIG_CHUNK* curchunk = m_chunk.Last();
01281       if ( 0 != curchunk && 0 == (TCODE_SHORT & curchunk->m_typecode) )
01282       {
01283         if (    curchunk->m_big_value < 0 
01284              || ((ON__INT64)ui32) > curchunk->m_big_value
01285            )
01286         {
01287           ON_ERROR("string byte count exceeds current chunk size");
01288           rc = false;
01289         }
01290       }
01291     }
01292   }
01293   if (!rc)
01294     ui32 = 0;
01295   if ( string_utf8_element_count )
01296     *string_utf8_element_count = (size_t)ui32;
01297   return rc;
01298 }
01299 
01300 
01301 bool
01302 ON_BinaryArchive::ReadStringUTF16ElementCount(
01303     size_t* string_utf16_element_count
01304     )
01305 {
01306   ON__UINT32 ui32 = 0;
01307   bool rc = ReadInt32(1,(ON__INT32*)&ui32);
01308   // Note that ui32 = number of elements in the string array, including
01309   // the null terminator.  So ui32 should either be 0 or be >= 2.
01310   // The string array elements can be chars or unsigned shorts;
01311   // therefore the number of bytes in the string cannot be determined
01312   // at this point because we don't know what type of string is
01313   // being read.
01314   if (rc)
01315   {
01316     if ( 0 != (0xF000000 & ui32) )
01317     {
01318       // 268 million chars oughta be plenty
01319       ON_ERROR("string element count is impossibly large");
01320       rc = false;
01321     }
01322     else if ( ui32 > 0 )
01323     {
01324       // make sure this is possible
01325       const ON_3DM_BIG_CHUNK* curchunk = m_chunk.Last();
01326       if ( 0 != curchunk && 0 == (TCODE_SHORT & curchunk->m_typecode) )
01327       {
01328         // 2*ui32 is used because opennurbs writes all wide character
01329         // strings as UTF-16 encoded strings.
01330         if (    curchunk->m_big_value < 0 
01331              || ((ON__INT64)2*ui32) > curchunk->m_big_value
01332            )
01333         {
01334           ON_ERROR("string byte count exceeds current chunk size");
01335           rc = false;
01336         }
01337       }
01338     }
01339   }
01340 
01341   if (!rc)
01342     ui32 = 0;
01343   if ( string_utf16_element_count )
01344     *string_utf16_element_count = (size_t)ui32;
01345   return rc;
01346 }
01347 
01348 bool
01349 ON_BinaryArchive::ReadString(         // Read NULL terminated string
01350     size_t string_utf8_element_count, // = value from ReadStringUTF8ElementCount()
01351     char* p                           // array[string_utf8_element_count]
01352     )
01353 {
01354   return ReadByte( string_utf8_element_count, p );
01355 }
01356 
01357 bool
01358 ON_BinaryArchive::ReadString(         // Read NULL terminated string
01359     size_t string_utf8_element_count, // = value from ReadStringUTF8ElementCount()
01360     unsigned char* p                  // array[string_utf8_element_count]
01361     )
01362 {
01363   return ReadByte( string_utf8_element_count, p );
01364 }
01365 
01366 bool
01367 ON_BinaryArchive::ReadString(          // Read NULL terminated unicode string
01368     size_t string_utf16_element_count, // length = value ReadStringUTF16ElementCount ReadStringSize()
01369     unsigned short* p                  // array[string_utf16_element_count]
01370     )
01371 {
01372   return ReadShort( string_utf16_element_count, p );
01373 }
01374 
01375 bool
01376 ON_BinaryArchive::ReadString( ON_String& s )
01377 {
01378   s.Destroy();
01379   size_t string_utf8_element_count = 0;
01380   bool rc = ReadStringUTF8ElementCount( &string_utf8_element_count );
01381   if ( rc && string_utf8_element_count > 0 ) 
01382   {
01383     const int istring_utf8_element_count = (int)string_utf8_element_count; // (int) converts 64 bits size_t
01384     s.ReserveArray(istring_utf8_element_count);
01385     ReadString( string_utf8_element_count, s.Array() );
01386     s.SetLength( istring_utf8_element_count-1 );
01387   }
01388   return rc;
01389 }
01390 
01391 bool
01392 ON_BinaryArchive::ReadString( ON_wString& s )
01393 {
01394 #if defined(ON_COMPILER_MSC)
01395 #pragma warning( push )
01396 // Disable the MSC /W4 "conditional expression is constant" warning
01397 // about 2 == sizeof(wchar_t).  Since this code has to run on machines
01398 // where sizeof(wchar_t) can be 2, 4, or 8 bytes, the test is necessary.
01399 #pragma warning( disable : 4127 )
01400 #endif
01401 
01402   s.Destroy();
01403   size_t string_utf16_element_count = 0;
01404   bool rc = ReadStringUTF16ElementCount( &string_utf16_element_count );
01405   if ( rc && string_utf16_element_count > 0 ) 
01406   {
01407     // string_utf16_element_count = number of ON__INT16 elements in
01408     // the string.  This is almost always the same as the
01409     // number of unicode code points. However, if one of
01410     // the code points happens to require two ON__INT16 
01411     // values to encode, then string_utf16_element_count will be 
01412     // larger than the number of unicode code points in 
01413     // the array.
01414     const int istring_utf16_element_count = (int)string_utf16_element_count;
01415     if ( 2 == sizeof(wchar_t) ) 
01416     {
01417       // When sizeof(wchar_t) is 2 bytes, assume wchar_t strings are 
01418       // UTF-16 encoded unicode strings.
01419       s.ReserveArray( istring_utf16_element_count );
01420       rc = ReadInt16( string_utf16_element_count, (ON__INT16*)s.Array() );
01421       if (rc)
01422         s.SetLength( istring_utf16_element_count-1 );
01423     }
01424     else if ( 4 == sizeof(wchar_t)  )
01425     {
01426       // When sizeof(wchar_t) is 4 bytes, assume wchar_t strings are 
01427       // UTF-32 encoded unicode strings. (some gcc implementations do this.)
01428 
01429       // Read the UTF-16 encode string from the file into
01430       // utf16_buffer[].
01431       const int istring_utf16_element_count = (int)string_utf16_element_count;
01432       ON_SimpleArray<ON__UINT16> utf16_buffer(istring_utf16_element_count);
01433       rc = ReadInt16(string_utf16_element_count,(ON__INT16*)utf16_buffer.Array());
01434       if(rc)
01435       {
01436         // convert to a UTF-32 encoded unicode string.
01437         utf16_buffer.SetCount(istring_utf16_element_count);
01438         utf16_buffer[istring_utf16_element_count-1] = 0;
01439         rc = false;
01440         const ON__UINT16* sUTF16 = utf16_buffer.Array();
01441         const int bTestByteOrder = false;
01442         const int sUTF16_count = istring_utf16_element_count-1;
01443         const ON__UINT32 error_code_point = 0xFFFD;
01444         const unsigned int error_mask = 0xFFFFFFFF;
01445         unsigned int error_status = 0;
01446 
01447         const int utf32_array_count = ON_ConvertUTF16ToUTF32(
01448             bTestByteOrder,
01449             sUTF16,
01450             sUTF16_count,
01451             0, // unsigned int* sUTF32
01452             0, // int sUTF32_count
01453             &error_status,
01454             error_mask,
01455             error_code_point,
01456             0 // const ON__UINT16** sNextUTF16
01457             );
01458 
01459         if ( 0 == utf32_array_count )
01460         {
01461           rc = true;
01462         }
01463         else if ( utf32_array_count > 0 )
01464         {
01465           error_status = 0;
01466           s.ReserveArray(utf32_array_count+1);
01467           const int utf32_array_count1 = ON_ConvertUTF16ToUTF32(
01468               bTestByteOrder,
01469               sUTF16,
01470               sUTF16_count,
01471               (unsigned int*)s.Array(), // unsigned int* sUTF32
01472               utf32_array_count, // sUTF32_count
01473               &error_status,
01474               error_mask,
01475               error_code_point,
01476               0 // const ON__UINT16** sNextUTF16
01477               );
01478           if ( utf32_array_count1 == utf32_array_count )
01479           {
01480             s.SetLength( utf32_array_count );
01481             rc = true;
01482           }
01483         }
01484       }
01485     }
01486     if (!rc)
01487       s.Destroy();
01488   }
01489   return rc;
01490 
01491 #if defined(ON_COMPILER_MSC)
01492 #pragma warning( pop )
01493 #endif
01494 }
01495 
01496 
01497 bool ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_MappingChannel>& a)
01498 {
01499   int i, count = a.Count();
01500   if ( count < 0 )
01501     count = 0;
01502   bool rc = WriteInt( count );
01503   for  ( i = 0; i < count && rc; i++ )
01504   {
01505     // ON_MappingChannel::Write() puts the element in a chunk
01506     rc = a[i].Write(*this);
01507   }
01508   return rc;
01509 }
01510 
01511 bool ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_MaterialRef>& a)
01512 {
01513   int i, count = a.Count();
01514   if ( count < 0 )
01515     count = 0;
01516   bool rc = WriteInt( count );
01517   for  ( i = 0; i < count && rc; i++ )
01518   {
01519     // ON_MaterialRef::Write() puts the element in a chunk
01520     rc = a[i].Write(*this);
01521   }
01522   return rc;
01523 }
01524 
01525 
01526 bool ON_BinaryArchive::WriteArray( int count, const ON_Layer* a)
01527 {
01528   int i;
01529   if ( count < 0 || 0 == a )
01530     count = 0;
01531   bool rc = WriteInt( count );
01532   for  ( i = 0; i < count && rc; i++ )
01533   {
01534     rc = WriteObject(a[i]);
01535   }
01536   return rc;
01537 }
01538 
01539 bool ON_BinaryArchive::WriteArray( int count, const ON_Layer*const* a)
01540 {
01541   int i;
01542   if ( count < 0 || 0 == a )
01543     count = 0;
01544   bool rc = WriteInt( count );
01545   for  ( i = 0; i < count && rc; i++ )
01546   {
01547     rc = WriteObject(a[i]);
01548   }
01549   return rc;
01550 }
01551 
01552 bool ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_MappingRef>& a )
01553 {
01554   int i, count = a.Count();
01555   if ( count < 0 )
01556     count = 0;
01557   bool rc = WriteInt( count );
01558   for  ( i = 0; i < count && rc; i++ )
01559   {
01560     // ON_MappingRef::Write() puts the element in a chunk
01561     rc = a[i].Write(*this);
01562   }
01563   return rc;
01564 }
01565 
01566 
01567 bool ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_ObjRef>& a)
01568 {
01569   int i, count = a.Count();
01570   if ( count < 0 )
01571     count = 0;
01572   bool rc = WriteInt( count );
01573   for  ( i = 0; i < count && rc; i++ )
01574   {
01575     // ON_ObjRef::Write() puts the element in a chunk
01576     rc = a[i].Write(*this);
01577   }
01578   return rc;
01579 }
01580 
01581 bool ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_ObjRef_IRefID>& a)
01582 {
01583   int i, count = a.Count();
01584   if ( count < 0 )
01585     count = 0;
01586   bool rc = WriteInt( count );
01587   for  ( i = 0; i < count && rc; i++ )
01588   {
01589     // ON_ObjRef_IRefID::Write() puts the element in a chunk
01590     rc = a[i].Write(*this);
01591   }
01592   return rc;
01593 }
01594 
01595 bool ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_MappingChannel>& a )
01596 {
01597   a.Empty();
01598   int i, count;
01599   bool rc = ReadInt( &count );
01600   if (rc)
01601   {
01602     a.SetCapacity(count);
01603     for  ( i = 0; i < count && rc; i++ )
01604     {
01605       rc = a.AppendNew().Read(*this);
01606     }
01607   }
01608   return rc;
01609 }
01610 
01611 bool ON_BinaryArchive::ReadArray( ON_ClassArray<ON_MaterialRef>& a)
01612 {
01613   a.Empty();
01614   int i, count;
01615   bool rc = ReadInt( &count );
01616   if (rc)
01617   {
01618     a.SetCapacity(count);
01619     for  ( i = 0; i < count && rc; i++ )
01620     {
01621       rc = a.AppendNew().Read(*this);
01622     }
01623   }
01624   return rc;
01625 }
01626 
01627 bool ON_BinaryArchive::ReadArray( ON_ObjectArray<class ON_Layer>& a)
01628 {
01629   a.Empty();
01630   int i, count;
01631   bool rc = ReadInt( &count );
01632   if (rc)
01633   {
01634     a.SetCapacity(count);
01635     for  ( i = 0; i < count && rc; i++ )
01636     {
01637       rc = (1 == ReadObject(a.AppendNew()));
01638       if (!rc)
01639       {
01640         a.Remove();
01641         break;
01642       }
01643     }
01644   }
01645   return rc;
01646 }
01647 
01648 
01649 bool ON_BinaryArchive::ReadArray( ON_SimpleArray<class ON_Layer*>& a)
01650 {
01651   a.Empty();
01652   ON_Layer* layer;
01653   int i, count;
01654   bool rc = ReadInt( &count );
01655   if (rc)
01656   {
01657     a.SetCapacity(count);
01658     for  ( i = 0; i < count && rc; i++ )
01659     {
01660       layer = 0;
01661       ON_Object* p = 0;
01662       rc = (1==ReadObject(&p));
01663       if (rc)
01664       {
01665         layer = ON_Layer::Cast(p);
01666       }
01667       if (!rc || 0 == layer)
01668       {
01669         if ( p )
01670           delete p;
01671         rc = false;
01672         break;
01673       }
01674       a.Append(layer);
01675     }
01676   }
01677   return rc;
01678 }
01679 
01680 bool ON_BinaryArchive::ReadArray( ON_ClassArray<ON_MappingRef>& a)
01681 {
01682   a.Empty();
01683   int i, count;
01684   bool rc = ReadInt( &count );
01685   if (rc)
01686   {
01687     a.SetCapacity(count);
01688     for  ( i = 0; i < count && rc; i++ )
01689     {
01690       rc = a.AppendNew().Read(*this);
01691     }
01692   }
01693   return rc;
01694 }
01695 
01696 bool ON_BinaryArchive::ReadArray( ON_ClassArray<ON_ObjRef>& a)
01697 {
01698   a.Empty();
01699   int i, count;
01700   bool rc = ReadInt( &count );
01701   if (rc)
01702   {
01703     a.SetCapacity(count);
01704     for  ( i = 0; i < count && rc; i++ )
01705     {
01706       rc = a.AppendNew().Read(*this);
01707     }
01708   }
01709   return rc;
01710 }
01711 
01712 bool ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_ObjRef_IRefID>& a)
01713 {
01714   a.Empty();
01715   int i, count;
01716   bool rc = ReadInt( &count );
01717   if (rc)
01718   {
01719     a.SetCapacity(count);
01720     for  ( i = 0; i < count && rc; i++ )
01721     {
01722       rc = a.AppendNew().Read(*this);
01723     }
01724   }
01725   return rc;
01726 }
01727 
01728 
01729 bool ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_DisplayMaterialRef>& a )
01730 {
01731   a.Empty();
01732   int count = 0;
01733   bool rc = ReadInt( &count );
01734   if ( rc && count > 0 ) 
01735   {
01736     a.SetCapacity( count );
01737     int i;
01738     for ( i = 0; i < count && rc; i++ )
01739     {
01740       rc = ReadDisplayMaterialRef(a.AppendNew());
01741     }
01742   }
01743   return rc;
01744 }
01745 
01746 bool 
01747 ON_BinaryArchive::ReadArray( ON_ClassArray<ON_String>& a)
01748 {
01749   a.Empty();
01750   int count = 0;
01751   bool rc = ReadInt( &count );
01752   if ( rc && count > 0 ) 
01753   {
01754     a.SetCapacity( count );
01755     int i;
01756     for ( i = 0; i < count && rc; i++ )
01757     {
01758       rc = ReadString( a.AppendNew() );
01759     }
01760   }
01761   return rc;
01762 }
01763 
01764 bool 
01765 ON_BinaryArchive::ReadArray( ON_ClassArray<ON_wString>& a)
01766 {
01767   a.Empty();
01768   int count = 0;
01769   bool rc = ReadInt( &count );
01770   if ( rc && count > 0 )
01771   {
01772     a.SetCapacity( count );
01773     int i;
01774     for ( i = 0; i < count && rc; i++ )
01775     {
01776       rc = ReadString( a.AppendNew() );
01777     }
01778   }
01779   return rc;
01780 }
01781 
01782 bool
01783 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_DisplayMaterialRef>& a )
01784 {
01785   int i, count = a.Count();
01786   if ( count < 0 )
01787     count = 0;
01788   bool rc = WriteInt( count );
01789   for  ( i = 0; i < count && rc; i++ )
01790   {
01791     rc = WriteDisplayMaterialRef( a[i] );
01792   }
01793   return rc;
01794 }
01795 
01796 bool 
01797 ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_String>& a )
01798 {
01799   int i, count = a.Count();
01800   if ( count < 0 )
01801     count = 0;
01802   bool rc = WriteInt( count );
01803   for  ( i = 0; i < count && rc; i++ )
01804   {
01805     rc = WriteString( a[i] );
01806   }
01807   return rc;
01808 }
01809 
01810 bool 
01811 ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_wString>& a )
01812 {
01813   int i, count = a.Count();
01814   if ( count < 0 )
01815     count = 0;
01816   bool rc = WriteInt( count );
01817   for  ( i = 0; i < count && rc; i++ )
01818   {
01819     rc = WriteString( a[i] );
01820   }
01821   return rc;
01822 }
01823 
01824 bool 
01825 ON_BinaryArchive::ReadArray( ON_SimpleArray<bool>& a )
01826 {
01827 #if defined(ON_COMPILER_MSC)
01828 // Disable the MSC /W4 "conditional expression is constant" warning
01829 // about sizeof(*c) == sizeof(*b).  Since this code has to run on machines
01830 // where sizeof(bool) can be 1, 2, 4, or 8 bytes, the test is necessary.
01831 #pragma warning( push )
01832 #pragma warning( disable : 4127 )
01833 #endif
01834 
01835   a.Empty();
01836   int count = 0;
01837   bool rc = ReadInt( &count );
01838   if ( rc && count > 0 ) 
01839   {
01840     a.SetCapacity( count );
01841     char* c = 0;
01842     bool* b = a.Array();
01843     if ( sizeof(*c) == sizeof(*b) )
01844     {
01845       // 8 bit "bool" on this compiler
01846       c = (char*)b;
01847     }
01848     else if ( b )
01849     {
01850       // bigger "bool" on this compiler
01851       c = (char*)onmalloc(count*sizeof(*c));
01852     }
01853     rc = ReadChar( count, c );
01854     if ( rc )
01855     {
01856       if ( c == (char*)b )
01857       {
01858         a.SetCount(count);
01859       }
01860       else if ( c )
01861       {
01862         int i;
01863         for ( i = 0; i < count; i++ )
01864         {
01865           a.Append(c[i]?true:false);
01866         }
01867         onfree(c);
01868       }
01869     }
01870   }
01871   return rc;
01872 
01873 #if defined(ON_COMPILER_MSC)
01874 #pragma warning( pop )
01875 #endif
01876 
01877 }
01878 
01879 bool
01880 ON_BinaryArchive::ReadArray( ON_SimpleArray<char>& a )
01881 {
01882   a.Empty();
01883   int count = 0;
01884   bool rc = ReadInt( &count );
01885   if ( rc && count > 0 ) {
01886     a.SetCapacity( count );
01887     rc = ReadChar( count, a.Array() );
01888     if ( rc )
01889       a.SetCount(count);
01890   }
01891   return rc;
01892 }
01893 
01894 bool
01895 ON_BinaryArchive::ReadArray( ON_SimpleArray<short>& a )
01896 {
01897   a.Empty();
01898   int count = 0;
01899   bool rc = ReadInt( &count );
01900   if ( rc && count > 0 ) {
01901     a.SetCapacity( count );
01902     rc = ReadShort( count, a.Array() );
01903     if ( rc )
01904       a.SetCount(count);
01905   }
01906   return rc;
01907 }
01908 
01909 bool
01910 ON_BinaryArchive::ReadArray( ON_SimpleArray<int>& a )
01911 {
01912   a.Empty();
01913   int count = 0;
01914   bool rc = ReadInt( &count );
01915   if ( rc && count > 0 ) {
01916     a.SetCapacity( count );
01917     rc = ReadInt( count, a.Array() );
01918     if ( rc )
01919       a.SetCount(count);
01920   }
01921   return rc;
01922 }
01923 
01924 bool
01925 ON_BinaryArchive::ReadArray( ON_SimpleArray<float>& a )
01926 {
01927   a.Empty();
01928   int count = 0;
01929   bool rc = ReadInt( &count );
01930   if ( rc && count > 0 ) {
01931     a.SetCapacity( count );
01932     rc = ReadFloat( count, a.Array() );
01933     if ( rc )
01934       a.SetCount(count);
01935   }
01936   return rc;
01937 }
01938 
01939 bool
01940 ON_BinaryArchive::ReadArray( ON_SimpleArray<double>& a )
01941 {
01942   a.Empty();
01943   int count = 0;
01944   bool rc = ReadInt( &count );
01945   if ( rc && count > 0 ) {
01946     a.SetCapacity( count );
01947     rc = ReadDouble( count, a.Array() );
01948     if ( rc )
01949       a.SetCount(count);
01950   }
01951   return rc;
01952 }
01953 
01954 bool
01955 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_Color>& a )
01956 {
01957   a.Empty();
01958   int count = 0;
01959   bool rc = ReadInt( &count );
01960   if ( rc && count > 0 ) {
01961     a.SetCapacity( count );
01962     rc = ReadInt( count, (int*)a.Array() );
01963     if ( rc )
01964       a.SetCount(count);
01965   }
01966   return rc;
01967 }
01968 
01969 
01970 bool
01971 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_2dPoint>& a )
01972 {
01973   a.Empty();
01974   int count = 0;
01975   bool rc = ReadInt( &count );
01976   if ( rc && count > 0 ) {
01977     a.SetCapacity( count );
01978     rc = ReadDouble( 2*count, &a.Array()->x );
01979     if ( rc )
01980       a.SetCount(count);
01981   }
01982   return rc;
01983 }
01984 
01985 bool
01986 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_3dPoint>& a )
01987 {
01988   a.Empty();
01989   int count = 0;
01990   bool rc = ReadInt( &count );
01991   if ( rc && count > 0 ) {
01992     a.SetCapacity( count );
01993     rc = ReadDouble( 3*count, &a.Array()->x );
01994     if ( rc )
01995       a.SetCount(count);
01996   }
01997   return rc;
01998 }
01999 
02000 bool
02001 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_4dPoint>& a )
02002 {
02003   a.Empty();
02004   int count = 0;
02005   bool rc = ReadInt( &count );
02006   if ( rc && count > 0 ) {
02007     a.SetCapacity( count );
02008     rc = ReadDouble( 4*count, &a.Array()->x );
02009     if ( rc )
02010       a.SetCount(count);
02011   }
02012   return rc;
02013 }
02014 
02015 bool
02016 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_2dVector>& a )
02017 {
02018   a.Empty();
02019   int count = 0;
02020   bool rc = ReadInt( &count );
02021   if ( rc && count > 0 ) {
02022     a.SetCapacity( count );
02023     rc = ReadDouble( 2*count, &a.Array()->x );
02024     if ( rc )
02025       a.SetCount(count);
02026   }
02027   return rc;
02028 }
02029 
02030 bool
02031 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_3dVector>& a )
02032 {
02033   a.Empty();
02034   int count = 0;
02035   bool rc = ReadInt( &count );
02036   if ( rc && count > 0 ) {
02037     a.SetCapacity( count );
02038     rc = ReadDouble( 3*count, &a.Array()->x );
02039     if ( rc )
02040       a.SetCount(count);
02041   }
02042   return rc;
02043 }
02044 
02045 bool
02046 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_Xform>& a )
02047 {
02048   a.Empty();
02049   int count = 0;
02050   bool rc = ReadInt( &count );
02051   if ( rc && count > 0 ) 
02052   {
02053     a.SetCapacity( count );
02054     int i;
02055     for ( i = 0; i < count && rc; i++ )
02056     {
02057       rc = ReadXform(a.AppendNew());
02058     }
02059   }
02060   return rc;
02061 }
02062 
02063 bool
02064 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_2fPoint>& a )
02065 {
02066   a.Empty();
02067   int count = 0;
02068   bool rc = ReadInt( &count );
02069   if ( rc && count > 0 ) {
02070     a.SetCapacity( count );
02071     rc = ReadFloat( 2*count, &a.Array()->x );
02072     if ( rc )
02073       a.SetCount(count);
02074   }
02075   return rc;
02076 }
02077 
02078 bool
02079 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_3fPoint>& a )
02080 {
02081   a.Empty();
02082   int count = 0;
02083   bool rc = ReadInt( &count );
02084   if ( rc && count > 0 ) {
02085     a.SetCapacity( count );
02086     rc = ReadFloat( 3*count, &a.Array()->x );
02087     if ( rc )
02088       a.SetCount(count);
02089   }
02090   return rc;
02091 }
02092 
02093 bool
02094 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_4fPoint>& a )
02095 {
02096   a.Empty();
02097   int count = 0;
02098   bool rc = ReadInt( &count );
02099   if ( rc && count > 0 ) {
02100     a.SetCapacity( count );
02101     rc = ReadFloat( 4*count, &a.Array()->x );
02102     if ( rc )
02103       a.SetCount(count);
02104   }
02105   return rc;
02106 }
02107 
02108 bool
02109 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_2fVector>& a )
02110 {
02111   a.Empty();
02112   int count = 0;
02113   bool rc = ReadInt( &count );
02114   if ( rc && count > 0 ) {
02115     a.SetCapacity( count );
02116     rc = ReadFloat( 2*count, &a.Array()->x );
02117     if ( rc )
02118       a.SetCount(count);
02119   }
02120   return rc;
02121 }
02122 
02123 bool
02124 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_3fVector>& a )
02125 {
02126   a.Empty();
02127   int count = 0;
02128   bool rc = ReadInt( &count );
02129   if ( rc && count > 0 ) {
02130     a.SetCapacity( count );
02131     rc = ReadFloat( 3*count, &a.Array()->x );
02132     if ( rc )
02133       a.SetCount(count);
02134   }
02135   return rc;
02136 }
02137 
02138 bool
02139 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_UUID>& a )
02140 {
02141   a.Empty();
02142   ON_UUID uuid;
02143   int i, count = 0;
02144   bool rc = ReadInt( &count );
02145   if ( rc && count > 0 ) 
02146   {
02147     a.SetCapacity( count );
02148     for ( i = 0; i < count && rc; i++ )
02149     {
02150       rc = ReadUuid( uuid );
02151       if ( rc )
02152         a.Append(uuid);
02153     }
02154   }
02155   return rc;
02156 }
02157 
02158 
02159 
02160 
02161 bool
02162 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_UuidIndex>& a )
02163 {
02164   a.Empty();
02165   ON_UuidIndex idi;
02166   int i, count = 0;
02167   bool rc = ReadInt( &count );
02168   if ( rc && count > 0 ) 
02169   {
02170     a.SetCapacity( count );
02171     for ( i = 0; i < count && rc; i++ )
02172     {
02173       rc = ReadUuid( idi.m_id );
02174       if ( rc )
02175       {
02176         rc = ReadInt(&idi.m_i);
02177         if(rc)
02178           a.Append(idi);
02179       }
02180     }
02181   }
02182   return rc;
02183 }
02184 
02185 
02186 bool
02187 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_UUID>& a )
02188 {
02189   int i, count = a.Count();
02190   if ( count < 0 )
02191     count = 0;
02192   bool rc = WriteInt( count );
02193   for  ( i = 0; i < count && rc; i++ )
02194   {
02195     rc = WriteUuid( a[i] );
02196   }
02197   return rc;
02198 }
02199 
02200 
02201 bool
02202 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_UuidIndex>& a )
02203 {
02204   int i, count = a.Count();
02205   if ( count < 0 )
02206     count = 0;
02207   bool rc = WriteInt( count );
02208   for  ( i = 0; i < count && rc; i++ )
02209   {
02210     rc = WriteUuid( a[i].m_id );
02211     if (rc)
02212       rc = WriteInt( a[i].m_i );
02213   }
02214   return rc;
02215 }
02216 
02217 
02218 bool
02219 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_LinetypeSegment>& a )
02220 {
02221   a.Empty();
02222   ON_LinetypeSegment seg;
02223   int i, count = 0;
02224   bool rc = ReadInt( &count );
02225   if ( rc && count > 0 ) 
02226   {
02227     a.SetCapacity( count );
02228     for ( i = 0; i < count && rc; i++ )
02229     {
02230       rc = ReadLinetypeSegment( seg );
02231       if ( rc )
02232         a.Append(seg);
02233     }
02234   }
02235   return rc;
02236 }
02237 
02238 bool
02239 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_LinetypeSegment>& a )
02240 {
02241   int i, count = a.Count();
02242   if ( count < 0 )
02243     count = 0;
02244   bool rc = WriteInt( count );
02245   for  ( i = 0; i < count && rc; i++ )
02246   {
02247     rc = WriteLinetypeSegment( a[i] );
02248   }
02249   return rc;
02250 }
02251 
02252 bool ON_BinaryArchive::ReadLinetypeSegment(ON_LinetypeSegment& seg)
02253 {
02254   seg.m_length = 1.0;
02255   seg.m_seg_type = ON_LinetypeSegment::stLine;
02256   unsigned int i;
02257   bool rc = ReadDouble(&seg.m_length);
02258   if (rc)
02259   {
02260     rc = ReadInt(&i);
02261     if( ON_LinetypeSegment::stLine == i )
02262       seg.m_seg_type = ON_LinetypeSegment::stLine;
02263     else if ( ON_LinetypeSegment::stSpace == i )
02264       seg.m_seg_type = ON_LinetypeSegment::stSpace;
02265   }
02266   return rc;
02267 }
02268 
02269 
02270 bool ON_BinaryArchive::WriteLinetypeSegment( const ON_LinetypeSegment& seg)
02271 {
02272   // do not add chunk info here
02273   unsigned int i = seg.m_seg_type;
02274   bool rc = WriteDouble(seg.m_length);
02275   if (rc)
02276     rc = WriteInt(i);
02277   return rc;
02278 }
02279 
02280 
02281 bool
02282 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_SurfaceCurvature>& a )
02283 {
02284   a.Empty();
02285   int count = 0;
02286   bool rc = ReadInt( &count );
02287   if ( rc && count > 0 ) {
02288     a.SetCapacity( count );
02289     rc = ReadDouble( 2*count, &a.Array()->k1 );
02290     if ( rc )
02291       a.SetCount(count);
02292   }
02293   return rc;
02294 }
02295 
02296 
02297 bool
02298 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_ClippingPlaneInfo>& a )
02299 {
02300   a.Empty();
02301   int count = 0;
02302   bool rc = ReadInt( &count );
02303   if ( rc && count > 0 ) 
02304   {
02305     a.SetCapacity(count);
02306     for ( int i = 0; i < count && rc ; i++ )
02307     {
02308       rc = a.AppendNew().Read(*this);
02309       if (!rc)
02310         a.Remove();
02311     }
02312   }
02313   return rc;
02314 }
02315 
02316 
02317 bool ON_BinaryArchive::WriteBool( bool b )
02318 {
02319   unsigned char c = (b?1:0);
02320   return WriteChar(c);
02321 }
02322 
02323 bool ON_BinaryArchive::ReadBool( bool *b )
02324 {
02325   unsigned char c;
02326   bool rc = ReadChar(&c);
02327   if (rc && b)
02328   {
02329     if ( c != 0 && c != 1 )
02330     {
02331       // WriteBool always writes a 0 or 1.  So either your code
02332       // has a bug, the file is corrupt, the the file pointer
02333       // is where it should be.
02334       ON_ERROR("ON_BinaryArchive::ReadBool - bool value != 0 and != 1");
02335       rc = false;
02336     }
02337     *b = c?true:false;
02338   }
02339   return rc;
02340 }
02341 
02342 bool
02343 ON_BinaryArchive::WriteChar(    // Write an array of 8 bit chars
02344                 size_t count,       // number of chars to write
02345                 const char* p   
02346                 )
02347 {
02348   return WriteByte( count, p );
02349 }
02350 
02351 bool
02352 ON_BinaryArchive::WriteChar(    // Write an array of 8 bit unsigned chars
02353                 size_t count,       // number of unsigned chars to write
02354                 const unsigned char* p
02355                 )
02356 {
02357   return WriteByte( count, p );
02358 }
02359 
02360 bool
02361 ON_BinaryArchive::WriteChar(    // Write a single 8 bit char
02362                 char c
02363                 )
02364 {
02365   return WriteByte( 1, &c );
02366 }
02367 
02368 bool
02369 ON_BinaryArchive::WriteChar(    // Write a single 8 bit unsigned char
02370                 unsigned char c
02371                 )
02372 {
02373   return WriteByte( 1, &c );
02374 }
02375 
02376 bool
02377 ON_BinaryArchive::WriteInt16(   // Write an array of 16 bit shorts
02378                 size_t count,               // number of shorts to write
02379                 const ON__INT16* p
02380                 )
02381 {
02382   bool rc = true;
02383   if ( m_endian == ON::big_endian )
02384   {
02385     if ( count > 0 ) 
02386     {
02387       const char* b = (const char*)p;
02388       while ( rc && count-- ) 
02389       {
02390         rc = WriteByte( 1, b+1 );
02391         if (rc)
02392           rc = WriteByte( 1, b );
02393         b++;
02394         b++;
02395       }
02396     }
02397   }
02398   else 
02399   {
02400     rc = WriteByte( count<<1, p );
02401   }
02402   return rc;
02403 }
02404 
02405 bool
02406 ON_BinaryArchive::WriteShort(   // Write an array of 16 bit shorts
02407                 size_t count,       // number of shorts to write
02408                 const short* p
02409                 )
02410 {
02411 #if defined(ON_COMPILER_MSC)
02412 #pragma warning( push )
02413 // Disable the MSC /W4 "conditional expression is constant" warning
02414 // about 2 == sizeof(*p).  Since this code has to run on machines
02415 // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary.
02416 #pragma warning( disable : 4127 )
02417 #endif
02418 
02419   bool rc;
02420   if ( 2 == sizeof(*p) )
02421   {
02422     rc = WriteInt16( count, (const ON__INT16*)p );
02423   }
02424   else
02425   {
02426     rc = true;
02427     ON__INT16 i16;
02428     size_t j;
02429     for ( j = 0; j < count; j++ )
02430     {
02431       i16 = (ON__INT16)(*p++);
02432       rc = WriteInt16( 1, &i16);
02433     }
02434   }
02435   return rc;
02436 
02437 #if defined(ON_COMPILER_MSC)
02438 #pragma warning( pop )
02439 #endif
02440 }
02441 
02442 bool
02443 ON_BinaryArchive::WriteShort(   // Write an array of 16 bit unsigned shorts
02444                 size_t count,       // number of shorts to write
02445                 const unsigned short* p
02446                 )
02447 {
02448   return WriteShort( count, (const short*)p );
02449 }
02450 
02451 bool
02452 ON_BinaryArchive::WriteShort(   // Write a single 16 bit short
02453                 short s
02454                 )
02455 {
02456   return WriteShort( 1, &s );
02457 }
02458 
02459 bool
02460 ON_BinaryArchive::WriteShort(   // Write a single 16 bit unsigned short
02461                 unsigned short s
02462                 )
02463 {
02464   return WriteShort( 1, &s );
02465 }
02466 
02467 bool
02468 ON_BinaryArchive::WriteInt32( // Write an array of 32 bit integers
02469                 size_t count,               // number of ints to write
02470                 const ON__INT32* p    
02471                 )
02472 {
02473   bool rc = true;
02474   if ( m_endian == ON::big_endian ) 
02475   {
02476     if ( count > 0 ) 
02477     {
02478       const char* b = (const char*)p;
02479       while ( rc && count-- ) 
02480       {
02481         rc = WriteByte( 1, b+3 );
02482         if (rc) rc = WriteByte( 1, b+2 );
02483         if (rc) rc = WriteByte( 1, b+1 );
02484         if (rc) rc = WriteByte( 1, b );
02485         b += 4;
02486       }
02487     }
02488   }
02489   else 
02490   {
02491     rc = WriteByte( count<<2, p );
02492   }
02493   return rc;
02494 }
02495 
02496 bool
02497 ON_BinaryArchive::ReadInt64( // Read an array of 64 bit integers
02498                 size_t count,            // number of 64 bit integers to read
02499                 ON__INT64* p
02500                 )
02501 {
02502   bool rc = ReadByte( count<<3, p );
02503   if ( rc && m_endian == ON::big_endian ) 
02504   {
02505                 unsigned char* b=(unsigned char*)p;
02506                 unsigned char  c;
02507                 while(count--) {
02508                         c = b[0]; b[0] = b[7]; b[7] = c;
02509                         c = b[1]; b[1] = b[6]; b[6] = c;
02510                         c = b[2]; b[2] = b[5]; b[5] = c;
02511                         c = b[3]; b[3] = b[4]; b[4] = c;
02512                         b += 8;
02513                 }
02514   }
02515   return rc;
02516 }
02517 
02518 bool
02519 ON_BinaryArchive::WriteInt64( // Write an array of 64 bit integers
02520                 size_t count,               // number of ints to write
02521                 const ON__INT64* p    
02522                 )
02523 {
02524   bool rc = true;
02525   if ( m_endian == ON::big_endian ) 
02526   {
02527     if ( count > 0 ) 
02528     {
02529       const char* b = (const char*)p;
02530       while ( rc && count-- ) 
02531       {
02532         rc = WriteByte( 1, b+7 );
02533         if (rc) rc = WriteByte( 1, b+6 );
02534         if (rc) rc = WriteByte( 1, b+5 );
02535         if (rc) rc = WriteByte( 1, b+4 );
02536         if (rc) rc = WriteByte( 1, b+3 );
02537         if (rc) rc = WriteByte( 1, b+2 );
02538         if (rc) rc = WriteByte( 1, b+1 );
02539         if (rc) rc = WriteByte( 1, b );
02540         b += 8;
02541       }
02542     }
02543   }
02544   else 
02545   {
02546     rc = WriteByte( count<<3, p );
02547   }
02548   return rc;
02549 }
02550 
02551 bool
02552 ON_BinaryArchive::WriteInt( // Write an array of integers
02553                 size_t count,             // number of ints to write
02554                 const int* p    
02555                 )
02556 {
02557 #if defined(ON_COMPILER_MSC)
02558 #pragma warning( push )
02559 // Disable the MSC /W4 "conditional expression is constant" warning
02560 // about 4 == sizeof(*p).  Since this code has to run on machines
02561 // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary.
02562 #pragma warning( disable : 4127 )
02563 #endif
02564 
02565   bool rc;
02566   if ( 4 == sizeof(*p) )
02567   {
02568     rc = WriteInt32( count, (const ON__INT32*)p );
02569   }
02570   else
02571   {
02572     ON__INT32 i32;
02573     size_t j;
02574     rc = true;
02575     for ( j = 0; j < count && rc; j++ )
02576     {
02577       i32 = (ON__INT32)(*p++);
02578       rc = WriteInt32( 1, &i32 );
02579     }
02580   }
02581   return rc;
02582 
02583 #if defined(ON_COMPILER_MSC)
02584 #pragma warning( pop )
02585 #endif
02586 }
02587 
02588 bool
02589 ON_BinaryArchive::WriteSize(size_t sz)
02590 {
02591   unsigned int u = (unsigned int)sz;
02592   return WriteInt(u);
02593 }
02594 
02595 bool
02596 ON_BinaryArchive::ReadSize(size_t* sz)
02597 {
02598   unsigned int u = 0;
02599   bool rc = ReadInt(&u);
02600   if (rc)
02601     *sz = u;
02602   return rc;
02603 }
02604 
02605 bool ON_BinaryArchive::WriteBigSize(size_t sz)
02606 {
02607   ON__UINT64 u = (ON__UINT64)sz;
02608   return WriteInt64(1,(ON__INT64*)&u);;
02609 }
02610 
02611 bool ON_BinaryArchive::ReadBigSize( size_t* sz )
02612 {
02613   ON__UINT64 u;
02614   bool rc = ReadInt64(1,(ON__INT64*)&u);
02615   if (rc)
02616     *sz = (size_t)u;
02617   return rc;
02618 }
02619 
02620 bool ON_BinaryArchive::WriteBigTime(time_t t)
02621 {
02622   ON__UINT64 u = (ON__UINT64)t;
02623   return WriteInt64(1,(ON__INT64*)&u);
02624 }
02625 
02626 bool ON_BinaryArchive::ReadBigTime( time_t* t )
02627 {
02628   ON__UINT64 u;
02629   bool rc = ReadInt64(1,(ON__INT64*)&u);
02630   if (rc)
02631     *t = (time_t)u;
02632   return rc;
02633 }
02634 
02635 
02636 bool
02637 ON_BinaryArchive::WriteInt( // Write an array of 32 bit integers
02638                 size_t count,         // number of ints to write
02639                 const unsigned int* p
02640                 )
02641 {
02642   return WriteInt( count, (const int*)p );
02643 }
02644 
02645 bool
02646 ON_BinaryArchive::WriteInt( // Write a single 32 bit integer
02647                 int i
02648                 )
02649 {
02650   return WriteInt( 1, &i );
02651 }
02652 
02653 bool
02654 ON_BinaryArchive::WriteInt( // Write a single 32 bit integer
02655                 unsigned int i
02656                 )
02657 {
02658   return WriteInt( 1, &i );
02659 }
02660 
02661 bool ON_BinaryArchive::WriteBigInt( // Write an array of 64 bit integers
02662                 size_t count,
02663                 const ON__INT64* p      
02664                 )
02665 {
02666   return WriteInt64(count,p);
02667 }
02668 
02669 bool ON_BinaryArchive::WriteBigInt( // Write an array of 64 bit integers
02670                 size_t count,
02671                 const ON__UINT64* p     
02672                 )
02673 {
02674   return WriteInt64(count,(const ON__INT64*)p);
02675 }
02676 
02677 bool ON_BinaryArchive:: WriteBigInt( // Write a single 64 bit integer
02678                 ON__INT64 i
02679                 )
02680 {
02681   return WriteInt64(1,&i);
02682 }
02683 
02684 bool ON_BinaryArchive::WriteBigInt( // Write a single 64 bit unsigned integer
02685                 ON__UINT64 u
02686                 )
02687 {
02688   return WriteInt64(1,(const ON__INT64*)&u);
02689 }
02690 
02691 
02692 
02693 bool
02694 ON_BinaryArchive::WriteLong( // Write an array of longs
02695                 size_t count,         // number of longs to write
02696                 const long* p    
02697                 )
02698 {
02699 #if defined(ON_COMPILER_MSC)
02700 #pragma warning( push )
02701 // Disable the MSC /W4 "conditional expression is constant" warning
02702 // about 4 == sizeof(*p).  Since this code has to run on machines
02703 // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary.
02704 #pragma warning( disable : 4127 )
02705 #endif
02706 
02707   bool rc;
02708   if ( 4 == sizeof(*p) )
02709   {
02710     rc = WriteInt32( count, (const ON__INT32*)p );
02711   }
02712   else
02713   {
02714     ON__INT32 i32;
02715     size_t j;
02716     rc = true;
02717     for ( j = 0; j < count && rc; j++ )
02718     {
02719       i32 = (ON__INT32)(*p++);
02720       rc = WriteInt32( 1, &i32 );
02721     }
02722   }
02723   return rc;
02724 
02725 #if defined(ON_COMPILER_MSC)
02726 #pragma warning( pop )
02727 #endif
02728 }
02729 
02730 bool
02731 ON_BinaryArchive::WriteLong( // Write an array of longs
02732                 size_t count,         // number of longs to write
02733                 const unsigned long* p
02734                 )
02735 {
02736   return WriteLong( count, (const long*)p );
02737 }
02738 
02739 bool
02740 ON_BinaryArchive::WriteLong( // Write a single long
02741                 long i
02742                 )
02743 {
02744   return WriteLong( 1, &i );
02745 }
02746 
02747 bool
02748 ON_BinaryArchive::WriteLong( // Write a single unsigned long
02749                 unsigned long i
02750                 )
02751 {
02752   return WriteLong( 1, &i );
02753 }
02754 
02755 
02756 bool
02757 ON_BinaryArchive::WriteFloat(   // Write a number of IEEE floats
02758                 size_t count,       // number of doubles
02759                 const float* p
02760                 )
02761 {
02762   // floats and integers have same size and endian issues
02763   return WriteInt( count, (const int*)p );
02764 }
02765 
02766 bool
02767 ON_BinaryArchive::WriteFloat(   // Write a single float
02768                 float f
02769                 )
02770 {
02771   return WriteFloat( 1, &f );
02772 }
02773 
02774 bool
02775 ON_BinaryArchive::WriteDouble(  // Write a single double
02776                 size_t count,       // number of doubles
02777                 const double* p
02778                 )
02779 {
02780   bool rc = true;
02781   if ( m_endian == ON::big_endian ) {
02782     if ( count > 0 ) {
02783       const char* b = (const char*)p;
02784       while ( rc && count-- ) {
02785         rc = WriteByte( 1, b+7 );
02786         if (rc) rc = WriteByte( 1, b+6 );
02787         if (rc) rc = WriteByte( 1, b+5 );
02788         if (rc) rc = WriteByte( 1, b+4 );
02789         if (rc) rc = WriteByte( 1, b+3 );
02790         if (rc) rc = WriteByte( 1, b+2 );
02791         if (rc) rc = WriteByte( 1, b+1 );
02792         if (rc) rc = WriteByte( 1, b );
02793         b += 8;
02794       }
02795     }
02796   }
02797   else {
02798     rc = WriteByte( count<<3, p );
02799   }
02800   return rc;
02801 }
02802 
02803 bool
02804 ON_BinaryArchive::WriteComponentIndex(
02805                 const ON_COMPONENT_INDEX& ci
02806                 )
02807 {
02808   bool rc = WriteInt( ci.m_type );
02809   if (rc)
02810     rc = WriteInt( ci.m_index );
02811   // do not add additional writes - you will break old file IO
02812   return rc;
02813 }
02814 
02815 bool
02816 ON_BinaryArchive::ReadComponentIndex(
02817                 ON_COMPONENT_INDEX& ci
02818                 )
02819 {
02820   int t;
02821   ci.m_type = ON_COMPONENT_INDEX::invalid_type;
02822   ci.m_index = 0;
02823   bool rc = ReadInt( &t );
02824   if (rc)
02825   {
02826     rc = ReadInt( &ci.m_index );
02827     if (rc)
02828     {
02829       ci.m_type = ON_COMPONENT_INDEX::Type(t);
02830     }
02831     // do not add additional read - you will break old file IO
02832   }
02833   return rc;
02834 }
02835 
02836 bool
02837 ON_BinaryArchive::WriteDouble(  // Write a single double
02838                 const double x
02839                 )
02840 {
02841   return WriteDouble( 1, &x );
02842 }
02843 
02844 bool
02845 ON_BinaryArchive::WriteColor( const ON_Color& color )
02846 {
02847   unsigned int colorref = color;
02848   return WriteInt( colorref );
02849 }
02850 
02851 bool
02852 ON_BinaryArchive::WritePoint (
02853   const ON_2dPoint& p
02854   )
02855 {
02856   return WriteDouble( 2, &p.x );
02857 }
02858 
02859 bool
02860 ON_BinaryArchive::WritePoint (
02861   const ON_3dPoint& p
02862   )
02863 {
02864   return WriteDouble( 3, &p.x );
02865 }
02866 
02867 bool
02868 ON_BinaryArchive::WritePoint (
02869   const ON_4dPoint& p
02870   )
02871 {
02872   return WriteDouble( 4, &p.x );
02873 }
02874 
02875 bool
02876 ON_BinaryArchive::WriteVector (
02877   const ON_2dVector& v
02878   )
02879 {
02880   return WriteDouble( 2, &v.x );
02881 }
02882 
02883 bool
02884 ON_BinaryArchive::WriteVector (
02885   const ON_3dVector& v
02886   )
02887 {
02888   return WriteDouble( 3, &v.x );
02889 }
02890 
02891 bool ON_BinaryArchive::WriteDisplayMaterialRef( const ON_DisplayMaterialRef& dmr )
02892 {
02893   bool rc = WriteUuid( dmr.m_viewport_id );
02894   if (rc) rc = WriteUuid( dmr.m_display_material_id );
02895   return rc;
02896 }
02897 
02898 bool
02899 ON_BinaryArchive::WriteUuid( const ON_UUID& uuid )
02900 {
02901   bool    rc = WriteInt32( 1, (const ON__INT32*)(&uuid.Data1) );
02902   if (rc) rc = WriteInt16( 1, (const ON__INT16*)(&uuid.Data2) );
02903   if (rc) rc = WriteInt16( 1, (const ON__INT16*)(&uuid.Data3) );
02904   if (rc) rc = WriteByte( 8, uuid.Data4 );
02905   return rc;
02906 }
02907 
02908 bool
02909 ON_BinaryArchive::WriteTime( const struct tm& utc )
02910 {
02911   // utc = coordinated universal time ( a.k.a GMT, UTC )
02912   // (From ANSI C time() and gmtime().)
02913   // The checks are here so we can insure files don't contain
02914   // garbage dates and ReadTime() can treat out of bounds
02915   // values as file corruption errors.
02916   int i;
02917   i = (int)utc.tm_sec;  if ( i < 0 || i > 60 ) i = 0;
02918   bool rc = WriteInt( i );
02919   i = (int)utc.tm_min;  if ( i < 0 || i > 60 ) i = 0;
02920   if ( rc )
02921     rc = WriteInt( i );
02922   i = (int)utc.tm_hour;  if ( i < 0 || i > 24 ) i = 0;
02923   if ( rc )
02924     rc = WriteInt( i );
02925   i = (int)utc.tm_mday;  if ( i < 0 || i > 31 ) i = 0;
02926   if ( rc )
02927     rc = WriteInt( i );
02928   i = (int)utc.tm_mon;  if ( i < 0 || i > 12 ) i = 0;
02929   if ( rc )
02930     rc = WriteInt( i );
02931 
02932   // no year restrictions because dates are used in archeological userdata
02933   i = (int)utc.tm_year;
02934   if ( rc )
02935     rc = WriteInt( i );
02936 
02937   i = (int)utc.tm_wday;  if ( i < 0 || i > 7 ) i = 0;
02938   if ( rc )
02939     rc = WriteInt( i );
02940   i = (int)utc.tm_yday;  if ( i < 0 || i > 366 ) i = 0;
02941   if ( rc )
02942     rc = WriteInt( i );
02943   return rc;
02944 }
02945 
02946 bool
02947 ON_BinaryArchive::WriteString( // Write NULL terminated UTF-8 encoded unicode string
02948     const char* sUTF8
02949     )
02950 {
02951   size_t string_utf8_element_count = 0;
02952   if ( sUTF8 )
02953   {
02954     while(sUTF8[string_utf8_element_count])
02955       string_utf8_element_count++;
02956     if ( string_utf8_element_count )
02957       string_utf8_element_count++;
02958   }
02959   ON__UINT32 ui32 = (ON__UINT32)string_utf8_element_count;
02960   bool rc = WriteInt32(1,(ON__INT32*)&ui32);
02961   if ( rc && string_utf8_element_count > 0 )
02962     rc = WriteByte( string_utf8_element_count, sUTF8 );
02963   return rc;  
02964 }
02965 
02966 bool
02967 ON_BinaryArchive::WriteString( // Write NULL terminated UTF-8 encoded unicode string
02968     const unsigned char* sUTF8
02969     )
02970 {
02971   return WriteString( (const char*)sUTF8 );
02972 }
02973 
02974 bool
02975 ON_BinaryArchive::WriteString(  // Write NULL terminated UTF-16 encoded unicode string
02976     const unsigned short* sUTF16
02977     )
02978 {
02979   size_t string_utf16_element_count = 0;
02980   if ( sUTF16 )
02981   {
02982     while(sUTF16[string_utf16_element_count])
02983       string_utf16_element_count++;
02984     if ( string_utf16_element_count )
02985       string_utf16_element_count++;
02986   }
02987   ON__UINT32 ui32 = (ON__UINT32)string_utf16_element_count;
02988   bool rc = WriteInt32(1,(ON__INT32*)&ui32);
02989   if ( rc && string_utf16_element_count > 0 )
02990   {
02991     rc = WriteShort( string_utf16_element_count, sUTF16 );
02992   }
02993   return rc;  
02994 }
02995 
02996 bool
02997 ON_BinaryArchive::WriteString( const ON_String& sUTF8 )
02998 {
02999   size_t string_utf8_element_count = sUTF8.Length();
03000   if ( string_utf8_element_count )
03001     string_utf8_element_count++;
03002   ON__UINT32 ui32 = (ON__UINT32)string_utf8_element_count;
03003   bool rc = WriteInt32(1,(ON__INT32*)&ui32);
03004   if ( rc && string_utf8_element_count > 0 )
03005     rc = WriteByte( string_utf8_element_count, sUTF8.Array() );
03006   return rc;  
03007 }
03008 
03009 bool
03010 ON_BinaryArchive::WriteString( const ON_wString& s )
03011 {
03012 #if defined(ON_COMPILER_MSC)
03013 #pragma warning( push )
03014 // Disable the MSC /W4 "conditional expression is constant" warning
03015 // about 2 == sizeof(wchar_t).  Since this code has to run on machines
03016 // where sizeof(wchar_t) can be 2, 4, or ... bytes, the test is necessary.
03017 #pragma warning( disable : 4127 )
03018 #endif
03019 
03020   size_t string_element_count = s.Length();
03021   if ( string_element_count > 0)
03022     string_element_count++;
03023   bool rc = false;
03024   if ( string_element_count <= 1 )
03025   {
03026     ON__UINT32 ui32 = 0;
03027     rc = WriteInt32(1,(ON__INT32*)&ui32);
03028   }
03029   else if ( 2 == sizeof(wchar_t) && string_element_count > 0 ) 
03030   {
03031     ON__UINT32 ui32 = (ON__UINT32)string_element_count;
03032     rc = WriteInt32(1,(ON__INT32*)&ui32);
03033     if (rc)
03034       rc = WriteInt16( string_element_count, (const ON__INT16*)s.Array() );
03035   }
03036   else if ( 4 == sizeof(wchar_t) && string_element_count > 0 )
03037   {
03038     // Assume the string is UTF-32 encoded (this is the case for some gcc implementations).
03039     const int bTestByteOrder = false;
03040     const ON__UINT32* sUTF32 = (const ON__UINT32*)s.Array();
03041     const int sUTF32_count = (int)(string_element_count-1);
03042     const unsigned int error_mask = 0xFFFFFFFF;
03043     const ON__UINT32 error_code_point = 0xFFFD; 
03044     unsigned int error_status = 0;
03045 
03046     const int sUTF16_count = ON_ConvertUTF32ToUTF16(
03047       bTestByteOrder,
03048       sUTF32,
03049       sUTF32_count,
03050       0, // ON__UINT16* sUTF16,
03051       0, // int sUTF16_count,
03052       &error_status,
03053       error_mask,
03054       error_code_point,
03055       0 // const ON__UINT32** sNextUTF32
03056       );
03057 
03058     if ( sUTF16_count > 0 )
03059     {
03060       error_status = 0;
03061       ON_SimpleArray<ON__UINT16> utf16_buffer(sUTF16_count+1);
03062       utf16_buffer.SetCount(sUTF16_count+1);
03063       const int sUTF16_count1 = ON_ConvertUTF32ToUTF16(
03064         bTestByteOrder,
03065         sUTF32,
03066         sUTF32_count,
03067         utf16_buffer.Array(),
03068         utf16_buffer.Count(),
03069         &error_status,
03070         error_mask,
03071         error_code_point,
03072         0 // const ON__UINT32** sNextUTF32
03073         );
03074       if ( sUTF16_count1 == sUTF16_count )
03075       {
03076         utf16_buffer[sUTF16_count] = 0;
03077         const ON__UINT32 ui32 = (ON__UINT32)(sUTF16_count+1);
03078         rc = WriteInt32(1,(const ON__INT32*)&ui32);
03079         if ( rc && ui32 > 0 )
03080           rc = WriteInt16( ui32, (const ON__INT16*)utf16_buffer.Array() );
03081       }
03082     }
03083   }
03084   return rc;  
03085 
03086 #if defined(ON_COMPILER_MSC)
03087 #pragma warning( pop )
03088 #endif
03089 }
03090 
03091 bool ON_BinaryArchive::WriteArray( const ON_SimpleArray<bool>& a )
03092 {
03093 #if defined(ON_COMPILER_MSC)
03094 #pragma warning( push )
03095 // Disable the MSC /W4 "conditional expression is constant" warning
03096 // about sizeof(*c) == sizeof(*b).  Since this code has to run on machines
03097 // where sizeof(bool) can be 1, 2, 4, or 8 bytes, the test is necessary.
03098 #pragma warning( disable : 4127 )
03099 #endif
03100 
03101   int count = a.Count();
03102   if ( count < 0 )
03103     count = 0;
03104   bool rc = WriteInt( count );
03105 
03106   if ( rc && count > 0 ) 
03107   {
03108     char* p = 0;
03109     const char* c = 0;
03110     const bool* b = a.Array();
03111     if ( sizeof(*c) == sizeof(*b) )
03112     {
03113       // 8 bit "bool" on this compiler
03114       c = (char*)(b);
03115     }
03116     else if ( b )
03117     {
03118       // bigger "bool" on this compiler
03119       p = (char*)onmalloc(count*sizeof(*p));
03120       int i;
03121       for ( i = 0; i < count; i++ )
03122         p[i] = (b[i]?1:0);
03123       c = p;
03124     }
03125     rc = WriteChar( count, c );
03126     if ( p )
03127       onfree(p);
03128   }
03129 
03130   return rc;
03131 
03132 #if defined(ON_COMPILER_MSC)
03133 #pragma warning( pop )
03134 #endif
03135 }
03136 
03137 bool
03138 ON_BinaryArchive::WriteArray( const ON_SimpleArray<char>& a )
03139 {
03140   int count = a.Count();
03141   if ( count < 0 )
03142     count = 0;
03143   bool rc = WriteInt( count );
03144   if ( rc && count > 0 ) {
03145     rc = WriteChar( count, a.Array() );
03146   }
03147   return rc;
03148 }
03149 
03150 bool
03151 ON_BinaryArchive::WriteArray( const ON_SimpleArray<short>& a )
03152 {
03153   int count = a.Count();
03154   if ( count < 0 )
03155     count = 0;
03156   bool rc = WriteInt( count );
03157   if ( rc && count > 0 ) {
03158     rc = WriteShort( count, a.Array() );
03159   }
03160   return rc;
03161 }
03162 
03163 bool
03164 ON_BinaryArchive::WriteArray( const ON_SimpleArray<int>& a )
03165 {
03166   int count = a.Count();
03167   if ( count < 0 )
03168     count = 0;
03169   bool rc = WriteInt( count );
03170   if ( rc && count > 0 ) {
03171     rc = WriteInt( count, a.Array() );
03172   }
03173   return rc;
03174 }
03175 
03176 bool
03177 ON_BinaryArchive::WriteArray( const ON_SimpleArray<float>& a )
03178 {
03179   int count = a.Count();
03180   if ( count < 0 )
03181     count = 0;
03182   bool rc = WriteInt( count );
03183   if ( rc && count > 0 ) {
03184     rc = WriteFloat( count, a.Array() );
03185   }
03186   return rc;
03187 }
03188 
03189 bool
03190 ON_BinaryArchive::WriteArray( const ON_SimpleArray<double>& a )
03191 {
03192   int count = a.Count();
03193   if ( count < 0 )
03194     count = 0;
03195   bool rc = WriteInt( count );
03196   if ( rc && count > 0 ) {
03197     rc = WriteDouble( count, a.Array() );
03198   }
03199   return rc;
03200 }
03201 
03202 bool
03203 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_Color>& a )
03204 {
03205   int count = a.Count();
03206   if ( count < 0 )
03207     count = 0;
03208   bool rc = WriteInt( count );
03209   if ( rc && count > 0 ) {
03210     rc = WriteInt( count, (int*)a.Array() );
03211   }
03212   return rc;
03213 }
03214 
03215 bool
03216 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_2dPoint>& a )
03217 {
03218   int count = a.Count();
03219   if ( count < 0 )
03220     count = 0;
03221   bool rc = WriteInt( count );
03222   if ( rc && count > 0 ) {
03223     rc = WriteDouble( count*2, &a.Array()->x );
03224   }
03225   return rc;
03226 }
03227 
03228 bool
03229 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_3dPoint>& a )
03230 {
03231   int count = a.Count();
03232   if ( count < 0 )
03233     count = 0;
03234   bool rc = WriteInt( count );
03235   if ( rc && count > 0 ) {
03236     rc = WriteDouble( count*3, &a.Array()->x );
03237   }
03238   return rc;
03239 }
03240 
03241 bool
03242 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_4dPoint>& a )
03243 {
03244   int count = a.Count();
03245   if ( count < 0 )
03246     count = 0;
03247   bool rc = WriteInt( count );
03248   if ( rc && count > 0 ) {
03249     rc = WriteDouble( count*4, &a.Array()->x );
03250   }
03251   return rc;
03252 }
03253 
03254 bool
03255 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_2dVector>& a )
03256 {
03257   int count = a.Count();
03258   if ( count < 0 )
03259     count = 0;
03260   bool rc = WriteInt( count );
03261   if ( rc && count > 0 ) {
03262     rc = WriteDouble( count*2, &a.Array()->x );
03263   }
03264   return rc;
03265 }
03266 
03267 
03268 bool
03269 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_3dVector>& a )
03270 {
03271   int count = a.Count();
03272   if ( count < 0 )
03273     count = 0;
03274   bool rc = WriteInt( count );
03275   if ( rc && count > 0 ) {
03276     rc = WriteDouble( count*3, &a.Array()->x );
03277   }
03278   return rc;
03279 }
03280 
03281 
03282 bool
03283 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_Xform>& a )
03284 {
03285   int count = a.Count();
03286   if ( count < 0 )
03287     count = 0;
03288   bool rc = WriteInt( count );
03289   if ( rc && count > 0 ) 
03290   {
03291     int i;
03292     for ( i = 0; i < count && rc; i++ )
03293       rc = WriteXform(a[i]);
03294   }
03295   return rc;
03296 }
03297 
03298 bool
03299 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_2fPoint>& a )
03300 {
03301   int count = a.Count();
03302   if ( count < 0 )
03303     count = 0;
03304   bool rc = WriteInt( count );
03305   if ( rc && count > 0 ) {
03306     rc = WriteFloat( count*2, &a.Array()->x );
03307   }
03308   return rc;
03309 }
03310 
03311 bool
03312 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_3fPoint>& a )
03313 {
03314   int count = a.Count();
03315   if ( count < 0 )
03316     count = 0;
03317   bool rc = WriteInt( count );
03318   if ( rc && count > 0 ) {
03319     rc = WriteFloat( count*3, &a.Array()->x );
03320   }
03321   return rc;
03322 }
03323 
03324 bool
03325 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_4fPoint>& a )
03326 {
03327   int count = a.Count();
03328   if ( count < 0 )
03329     count = 0;
03330   bool rc = WriteInt( count );
03331   if ( rc && count > 0 ) {
03332     rc = WriteFloat( count*4, &a.Array()->x );
03333   }
03334   return rc;
03335 }
03336 
03337 bool
03338 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_2fVector>& a )
03339 {
03340   int count = a.Count();
03341   if ( count < 0 )
03342     count = 0;
03343   bool rc = WriteInt( count );
03344   if ( rc && count > 0 ) {
03345     rc = WriteFloat( count*2, &a.Array()->x );
03346   }
03347   return rc;
03348 }
03349 
03350 bool
03351 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_3fVector>& a )
03352 {
03353   int count = a.Count();
03354   if ( count < 0 )
03355     count = 0;
03356   bool rc = WriteInt( count );
03357   if ( rc && count > 0 ) {
03358     rc = WriteFloat( count*3, &a.Array()->x );
03359   }
03360   return rc;
03361 }
03362 
03363 
03364 bool
03365 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_SurfaceCurvature>& a )
03366 {
03367   int count = a.Count();
03368   if ( count < 0 )
03369     count = 0;
03370   bool rc = WriteInt( count );
03371   if ( rc && count > 0 ) {
03372     rc = WriteDouble( count*2, &a.Array()->k1 );
03373   }
03374   return rc;
03375 }
03376 
03377 bool
03378 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_ClippingPlaneInfo>& a )
03379 {
03380   int count = a.Count();
03381   if ( count < 0 )
03382     count = 0;
03383   bool rc = WriteInt( count );
03384   for ( int i = 0; i < count && rc ; i++ )
03385   {
03386     rc = a[i].Write(*this);
03387   }
03388   return rc;
03389 }
03390 
03391 
03395 
03396 bool
03397 ON_BinaryArchive::WriteObject( const ON_Object* o )
03398 {
03399   bool rc = false;
03400   if ( o )
03401     rc = WriteObject(*o);
03402   else {
03403     // NULL object has nil UUID and no date
03404     rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS, 0 );
03405     if (rc) {
03406       rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_UUID, 0 );
03407       if ( rc ) {
03408         rc = WriteUuid( ON_nil_uuid );
03409         if ( !EndWrite3dmChunk() ) // end of TCODE_OPENNURBS_CLASS_UUID chunk
03410           rc = false;
03411       }
03412       if ( !EndWrite3dmChunk() )
03413         rc = false;
03414     }
03415   }
03416   return rc;
03417 }
03418 
03419 static
03420 bool IsCriticalUserData( const ON_BinaryArchive& ar, const ON_UserData* ud )
03421 {
03422   // {31F55AA3-71FB-49f5-A975-757584D937FF}
03423   static const ON_UUID ON_MeshNgonUserData_ID = 
03424   { 0x31F55AA3, 0x71FB, 0x49f5, { 0xA9, 0x75, 0x75, 0x75, 0x84, 0xD9, 0x37, 0xFF } };
03425 
03426   // Userdata that must be saved even when userdata saving is "off".
03427   // Please discuss any changes with Dale Lear.  In particular,
03428   // "critical" is used in a narrow sense and modifying this 
03429   // function to save in form of plug-in's user data saved is
03430   // not appropriate.  The definition of the user data class must
03431   // be in the opennurbs library and its purpose must be to extend
03432   // a core opennurbs data structure, usually because it was the
03433   // only way to add information to core opennurbs data structure
03434   // and not break the public C++ SDK.
03435   if ( 0 == ud )
03436     return false;
03437 
03438   switch( ar.Archive3dmVersion() )
03439   {
03440   case 50:
03441     if ( ON_opennurbs5_id == ud->m_application_uuid )
03442     {
03443       // As of 30 August 2012, all the core opennurbs user data
03444       // classes with an application id of ON_opennurbs5_id are
03445       // deemed "critical".
03446       return true;
03447     }
03448 
03449     if ( ON_opennurbs4_id == ud->m_application_uuid )
03450     {
03451       // ON_MeshNgonUserData is the only "critical" core 
03452       // opennurbs user data with an application id of 
03453       // ON_opennurbs4_id.
03454       if ( ON_MeshNgonUserData_ID == ud->m_userdata_uuid )
03455         return true;
03456     }
03457     break;
03458 
03459   case 4:
03460     if ( ON_opennurbs4_id == ud->m_application_uuid )
03461     {
03462       // ON_MeshNgonUserData is the only "critical" core 
03463       // opennurbs user data with an application id of 
03464       // ON_opennurbs4_id.
03465       if ( ON_MeshNgonUserData_ID == ud->m_userdata_uuid )
03466         return true;
03467     }
03468     break;
03469   }
03470 
03471   return false;
03472 }
03473 
03474 static
03475 bool HasCriticalUserData( const ON_BinaryArchive& ar, const ON_Object* obj )
03476 {
03477   if ( 0 == obj )
03478     return false;
03479 
03480   for ( const ON_UserData* ud = obj->FirstUserData(); 0 != ud; ud = ud->Next() )
03481   {
03482     if ( IsCriticalUserData(ar,ud) )
03483       return true;
03484   }
03485 
03486   return false;
03487 }
03488 
03489 static
03490 bool IsCoreUserData( const ON_UserData* ud )
03491 {
03492   // Userdata with IO code we trust.
03493   if ( 0 == ud )
03494     return false;
03495   if (    ud->m_application_uuid == ON_rhino4_id
03496        || ud->m_application_uuid == ON_rhino5_id
03497        || ud->m_application_uuid == ON_rhino_id
03498        || ud->m_application_uuid == ON_opennurbs4_id
03499        || ud->m_application_uuid == ON_opennurbs5_id
03500        || ud->m_application_uuid == ON_opennurbs_id
03501       )
03502   {
03503     return true;
03504   }
03505   return false;
03506 }
03507 
03508 bool
03509 ON_BinaryArchive::WriteObject( const ON_Object& o )
03510 {
03511   // writes polymorphic object derived from ON_Object in a way that
03512   // it can be recreated from ON_BinaryArchive::ReadObject
03513   ON_UUID uuid;
03514   bool rc = false;
03515   const ON_ClassId* pID = o.ClassId();
03516   if ( !pID ) {
03517     ON_ERROR("ON_BinaryArchive::WriteObject() o.ClassId() returned NULL.");
03518     return false;
03519   }
03520   uuid = pID->Uuid();
03521 
03522   if ( m_3dm_version <= 2 )
03523   {
03524     if ( ON_Curve::Cast(&o) && !ON_NurbsCurve::Cast(&o) )
03525     {
03526       ON_NurbsCurve nc;
03527       const ON_Curve* curve = static_cast<const ON_Curve*>(&o);
03528       if ( curve->GetNurbForm(nc) )
03529         return WriteObject( nc );
03530     }
03531     else if ( ON_Surface::Cast(&o) && !ON_NurbsSurface::Cast(&o) )
03532     {
03533       ON_NurbsSurface ns;
03534       const ON_Surface* surface = static_cast<const ON_Surface*>(&o);
03535       if ( surface->GetNurbForm(ns) )
03536         return WriteObject( ns );
03537     }
03538     else if( ON_Annotation2::Cast(&o))
03539     {
03540       // 6-23-03 lw added for writing annotation to v2 files
03541       ON_Annotation2* pA = (ON_Annotation2*)&o;
03542       switch( pA->Type())
03543       {
03544       case ON::dtNothing:
03545         break;
03546       case ON::dtDimLinear:
03547       case ON::dtDimAligned:
03548         {
03549           ON_LinearDimension dim;
03550           ((ON_LinearDimension2*)pA)->GetV2Form( dim);
03551           return WriteObject( dim);
03552         }
03553       case ON::dtDimAngular:
03554         {
03555           ON_AngularDimension dim;
03556           ((ON_AngularDimension2*)pA)->GetV2Form( dim);
03557           return WriteObject( dim);
03558         }
03559       case ON::dtDimDiameter:
03560       case ON::dtDimRadius:
03561         {
03562           ON_RadialDimension dim;
03563           ((ON_RadialDimension2*)pA)->GetV2Form( dim);
03564           return WriteObject( dim);
03565         }
03566       case ON::dtLeader:
03567         {
03568           ON_Leader leader;
03569           ((ON_Leader2*)pA)->GetV2Form( leader);
03570           return WriteObject( leader);
03571         }
03572       case ON::dtTextBlock:
03573         {
03574           ON_TextEntity text;
03575           ((ON_TextEntity2*)pA)->GetV2Form( text);
03576           return WriteObject( text);
03577         }
03578       case ON::dtDimOrdinate:
03579         // no V2 form of ordinate dimensions
03580         //  Fall through and save it in v4 format.
03581         //  It will be skipped by the v2 and v3 readers
03582         //  but users won't loose the ordinate dimensions
03583         //  when they encounter the situation described in
03584         //  the next comment.
03585         break;
03586       }
03587 
03588       // 7 August 2003 Dale Lear
03589       //    I commented out the "return false;".
03590       //    It is imporant to fall through to the working
03591       //    code because people overwrite their "good" files
03592       //    with a V2 version and loose everything when the
03593       //    writing code returns false.  By falling through,
03594       //    V2 will still read what it can from the file and
03595       //    V3 will read the entire file.
03596       //return false;
03597     }
03598   }
03599 
03600   if ( m_3dm_version <= 4 && ON::extrusion_object == o.ObjectType() )
03601   {
03602     // 29 September 2010 Dale Lear
03603     //   ON_Extrusion was added in V5.  It must be translated
03604     //   to a brep or surface to save it in a V4 file.
03605     const ON_Extrusion* extrusion = ON_Extrusion::Cast(&o);
03606     if ( 0 != extrusion )
03607     {
03608       ON_Object* v4object = 0;
03609       if ( extrusion->IsCapped() || extrusion->ProfileCount() >= 2 )
03610         v4object = extrusion->BrepForm(0);
03611       if ( 0 == v4object )
03612         v4object = extrusion->SumSurfaceForm(0);
03613       if ( 0 == v4object )
03614         v4object = extrusion->NurbsSurface(0);
03615       if ( 0 != v4object 
03616            && ON::extrusion_object != v4object->ObjectType() // no infinte recursion!
03617          )
03618       {
03619         // Some plug-in userdata code is not robust enough to check 
03620         // archive version numbers and correctly handle save as v4 
03621         // changes.  The upshot is that, extrusion user data
03622         // has to be lost when saving as V4.
03623         //v4object->MoveUserData(const_cast<ON_Object&>(o));
03624         rc = WriteObject( v4object );
03625         //const_cast<ON_Object&>(o).MoveUserData(*v4object);
03626         delete v4object;
03627         return rc;
03628       }
03629       if ( 0 != v4object )
03630         delete v4object;
03631     }
03632   }
03633 
03634   rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS, 0 );
03635   if (rc) {
03636 
03637     // TCODE_OPENNURBS_CLASS_UUID chunk contains class's UUID 
03638     rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_UUID, 0 );
03639     if ( rc ) {
03640       rc = WriteUuid( uuid );
03641       if ( !EndWrite3dmChunk() ) // end of TCODE_OPENNURBS_CLASS_UUID chunk
03642         rc = false;
03643     }
03644 
03645     // TCODE_OPENNURBS_CLASS_DATA chunk contains definition of class
03646     if ( rc ) {
03647       rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_DATA, 0 );
03648       if (rc)
03649       {
03650         rc = o.Write( *this )?true:false;
03651         if ( !rc ) {
03652           ON_ERROR("ON_BinaryArchive::WriteObject() o.Write() failed.");
03653         }
03654         if ( !EndWrite3dmChunk() ) // end of TCODE_OPENNURBS_CLASS_DATA chunk
03655           rc = false;
03656       }
03657     }
03658 
03659     if (rc && (m_bSaveUserData || HasCriticalUserData(*this,&o) ) ) 
03660     {
03661       // write user data.  Each piece of user data is in a 
03662       // TCODE_OPENNURBS_CLASS_USERDATA chunk.
03663       rc = WriteObjectUserData(o);
03664     }
03665 
03666     // TCODE_OPENNURBS_CLASS_END chunk marks end of class record
03667     if ( BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_END, 0 ) ) {
03668       if ( !EndWrite3dmChunk() )
03669         rc = false;
03670     }
03671     else
03672       rc = false;
03673 
03674     if ( !EndWrite3dmChunk() ) // end of TCODE_OPENNURBS_CLASS chunk
03675       rc = false;
03676   }
03677 
03678   return rc;
03679 }
03680 
03681 bool ON_BinaryArchive::WriteObjectUserData( const ON_Object& object )
03682 {
03683   if ( m_3dm_version < 3 )
03684   {
03685     // no user data is saved in V1 and V2 files.
03686     return true;
03687   }
03688 
03689   // writes user data attached to object.
03690   bool rc = true;
03691   const ON_UserData* ud;
03692   ON_UUID userdata_classid;
03693   
03694   for (ud = object.m_userdata_list; ud && rc; ud = ud->m_userdata_next) 
03695   {
03696     if ( !ud->Archive() )
03697     {
03698       continue;
03699     }
03700 
03701     if ( false == m_bSaveUserData )
03702     {
03703       // user data is not being saved unless it is functioning
03704       // as a critcial extension of a core opennurbs data
03705       // structure.
03706       if ( false == IsCriticalUserData(*this,ud) )
03707         continue;
03708     }
03709 
03710 
03711     // LOTS of tests to weed out bogus user data
03712     if ( 0 == ON_UuidCompare( ud->m_userdata_uuid, ON_nil_uuid ) )
03713       continue;
03714     if ( &object != ud->m_userdata_owner )
03715       continue;
03716     const ON_ClassId* cid = ud->ClassId();
03717     if ( 0 == cid )
03718       continue;
03719     if ( cid == &ON_UserData::m_ON_UserData_class_id )
03720       continue;
03721     if ( cid == &ON_UserData::m_ON_Object_class_id )
03722       continue;
03723 
03724     // The UserDataClassUuid() function is used instead of 
03725     // calling cid->Uuid() so we get the value of the 
03726     // plug-in's class id when the plug-in is not loaded
03727     // and ud is ON_UnknownUserData.
03728     userdata_classid = ud->UserDataClassUuid();
03729     if ( 0 == ON_UuidCompare( userdata_classid, ON_nil_uuid ) )
03730       continue;
03731     if ( 0 == ON_UuidCompare( userdata_classid, ON_UserData::m_ON_UserData_class_id.Uuid() ) )
03732       continue;
03733     if ( 0 == ON_UuidCompare( userdata_classid, ON_Object::m_ON_Object_class_id.Uuid() ) )
03734       continue;
03735     if ( 0 == ON_UuidCompare( userdata_classid, ON_UnknownUserData::m_ON_Object_class_id.Uuid() ) )
03736       continue;
03737 
03738     if ( 3 == m_3dm_version )
03739     {
03740       // When saving a V3 archive and the user data is not
03741       // native V3 data, make sure the plug-in supports
03742       // writing V3 user data.
03743       if ( m_V3_plugin_id_list.BinarySearch( &ud->m_application_uuid, ON_UuidCompare ) < 0 )
03744         continue;
03745     }
03746 
03747     if ( ON_UuidIsNil( ud->m_application_uuid ) )
03748     {
03749       // As of version 200909190 - a non-nil application_uuid is 
03750       // required in order for user data to be saved in a 
03751       // 3dm archive.
03752       ON_Error(__FILE__,__LINE__,"Not saving %s userdata - m_application_uuid is nil.",cid->ClassName());
03753       continue; 
03754     }
03755 
03756     // See if we have unknown user data (goo) and make sure
03757     // IsUnknownUserData() agrees with ON_UnknownUserData::Cast().
03758     const ON_UnknownUserData* unknown_ud = ON_UnknownUserData::Cast(ud);
03759     if ( 0 == unknown_ud )
03760     {
03761       if ( ud->IsUnknownUserData() )
03762       {
03763         ON_ERROR("ON_UnknownUserData::Cast(ud) is null and ud->IsUnknownUserData() is true.");
03764         continue; // something's wrong
03765       }
03766     }
03767     else
03768     {
03769       if ( !ud->IsUnknownUserData() )
03770       {
03771         ON_ERROR("ON_UnknownUserData::Cast(ud) is not null and ud->IsUnknownUserData() is false.");
03772         continue; // something's wrong
03773       }
03774     }
03775 
03776     if ( 0 != unknown_ud )
03777     {
03778       if ( false == m_bSaveUserData )
03779         continue; // "unknown" user data cannot be "critical"
03780 
03781       if ( unknown_ud->m_3dm_version <= 3 )
03782         continue; // Unknown will not be resaved in V3 archives
03783 
03784       if ( unknown_ud->m_3dm_version > 5 && unknown_ud->m_3dm_version < 50 )
03785         continue;
03786 
03787       if ( unknown_ud->m_3dm_opennurbs_version < 200701010 )
03788         continue;
03789 
03790       if ( unknown_ud->m_3dm_version >= 50 && m_3dm_version < 50 )
03791       {
03792         // Unknown userdata with 8 byte chunk lengths cannot be
03793         // saved into a V4 file with 4 byte chunk lengths because
03794         // the resulting chunk will be unreadable in V4.
03795         // This is not an error condition.  It is a consequence
03796         // of V4 IO code not being robust enough to handle
03797         // 8 bytes chunk lengths.
03798         continue;
03799       }
03800     }
03801 
03802     // Each piece of user data is inside of 
03803     // a TCODE_OPENNURBS_CLASS_USERDATA chunk.
03804     rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_USERDATA, 0 );
03805     if (rc) {
03806       rc = Write3dmChunkVersion(2,2);
03807       // wrap user data header info in an TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk
03808       rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_USERDATA_HEADER, 0 );
03809       if (rc) 
03810       {
03811         if ( rc ) rc = WriteUuid( userdata_classid );
03812         if ( rc ) rc = WriteUuid( ud->m_userdata_uuid );
03813         if ( rc ) rc = WriteInt( ud->m_userdata_copycount );
03814         if ( rc ) rc = WriteXform( ud->m_userdata_xform );
03815 
03816         // added for version 2.1
03817         if ( rc ) rc = WriteUuid( ud->m_application_uuid );
03818 
03819         // added for version 2.2 - 14, October 2009
03820         if ( rc )
03821         {
03822           rc = WriteBool( unknown_ud ? true : false );
03823           int ver = unknown_ud ? unknown_ud->m_3dm_version : m_3dm_version;
03824           rc = WriteInt( ver );
03825           ver = unknown_ud ? unknown_ud->m_3dm_opennurbs_version : m_3dm_opennurbs_version;
03826           if (rc) rc = WriteInt( ver );
03827         }
03828 
03829         if ( !EndWrite3dmChunk() )
03830           rc = false;
03831       }
03832       if (rc) 
03833       {
03834         // wrap user data in an anonymous chunk
03835         rc = BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 );
03836         if ( rc ) 
03837         {
03838           if ( 0 != unknown_ud )
03839           {
03840             // 22 January 2004 Dale Lear
03841             //   Disable crc checking when writing the
03842             //   unknow user data block.
03843             //   This has to be done so we don't get an extra
03844             //   32 bit CRC calculated on the block that
03845             //   ON_UnknownUserData::Write() writes.  The
03846             //   original 32 bit crc is at the end of this
03847             //   block and will be checked when the class
03848             //   that wrote this user data is present.
03849             //   The EndWrite3dmChunk() will reset the
03850             //   CRC checking flags to the appropriate
03851             //   values.
03852             m_chunk.Last()->m_do_crc16 = 0;
03853             m_chunk.Last()->m_do_crc32 = 0;
03854             m_bDoChunkCRC = false;
03855           }
03856           rc = ud->Write(*this)?true:false;
03857           if ( !EndWrite3dmChunk() )
03858             rc = false;
03859         }
03860       }
03861       if ( !EndWrite3dmChunk() )
03862         rc = false;
03863     }
03864   }
03865   return rc;
03866 }
03867 
03868 int
03869 ON_BinaryArchive::LoadUserDataApplication( ON_UUID application_id )
03870 {
03871   // This is a virtual function.
03872   // Rhino overrides this function to load plug-ins.
03873   return 0;
03874 }
03875 
03876 int ON_BinaryArchive::ReadObject( ON_Object** ppObject )
03877 {
03878   if ( !ppObject ) 
03879   {
03880     ON_ERROR("ON_BinaryArchive::ReadObject() called with NULL ppObject.");
03881     return 0;
03882   }
03883   *ppObject = 0;
03884   return ReadObjectHelper(ppObject);
03885 }
03886 
03887 int ON_BinaryArchive::ReadObject( ON_Object& object )
03888 {
03889   ON_Object* pObject = &object;
03890   return ReadObjectHelper(&pObject);
03891 }
03892 
03893 int ON_BinaryArchive::ReadObjectHelper( ON_Object** ppObject )
03894 {
03895   // returns 0: failure - unable to read object because of file IO problems
03896   //         1: success
03897   //         3: unable to read object because it's UUID is not registered
03898   //            this could happen in cases where old code is attempting to read
03899   //            new objects.
03900   ON__UINT32 tcode;
03901   ON__INT64 length_TCODE_OPENNURBS_CLASS = 0;
03902   ON__INT64 length_TCODE_OPENNURBS_CLASS_UUID = 0;
03903   ON__INT64 length_TCODE_OPENNURBS_CLASS_DATA = 0;
03904   ON_UUID uuid;
03905   const ON_ClassId* pID = 0;
03906   ON_Object* pObject = *ppObject; // If not null, use input
03907   int rc = 0;
03908   const ON__INT64 sizeof_chunk_header = (ON__INT64)(4 + SizeofChunkLength());
03909   const ON__INT64 expected_length_TCODE_OPENNURBS_CLASS_UUID = 20;
03910 
03911   //bool bBogusUserData = false;
03912 
03913 
03914   // all ON_Objects written by WriteObject are in a TCODE_OPENNURBS_CLASS chunk
03915   rc = BeginRead3dmBigChunk( &tcode, &length_TCODE_OPENNURBS_CLASS );
03916   if ( !rc )
03917     return 0;
03918 
03919   // When a NULL ON_Object is written, the file has
03920   // 
03921   //  TCODE_OPENNURBS_CLASS, length = 20 + sizeof_chunk_header
03922   //  TCODE_OPENNURBS_CLASS_UUID, length = 20
03923   //    16 byte nil uuid
03924   //    4 byte TCODE_OPENNURBS_CLASS_UUID crc
03925   //
03926   // When a non-NULL ON_Object is written, the file has
03927   // 
03928   //  TCODE_OPENNURBS_CLASS, length = 20 + 3*sizeof_chunk_header + length_DATA_chunk + length_USER_DATA_chunk(s)
03929   //
03930   //    TCODE_OPENNURBS_CLASS_UUID, length = 20
03931   //      16 byte nil uuid
03932   //      4 byte TCODE_OPENNURBS_CLASS_UUID crc
03933   //
03934   //    TCODE_OPENNURBS_CLASS_DATA, length_DATA >= 4
03935   //      ...
03936   //      4 byte TCODE_OPENNURBS_CLASS_DATA crc
03937   //
03938   //    Optional TCODE_OPENNURBS_CLASS_USERDATA chunks
03939   //
03940   //    TCODE_OPENNURBS_CLASS_END, chunk value = 0
03941 
03942   if ( tcode != TCODE_OPENNURBS_CLASS ) 
03943   {
03944     ON_ERROR("ON_BinaryArchive::ReadObject() didn't find TCODE_OPENNURBS_CLASS block.");
03945     rc = 0;
03946   }
03947   else if ( length_TCODE_OPENNURBS_CLASS < expected_length_TCODE_OPENNURBS_CLASS_UUID + sizeof_chunk_header) 
03948   {
03949     ON_ERROR("ON_BinaryArchive::ReadObject() TCODE_OPENNURBS_CLASS chunk length too small.");
03950     rc = 0;
03951   }
03952   else 
03953   {
03954     // we break out of this loop if something bad happens
03955     for (;;) 
03956     {
03957       // read class's UUID ///////////////////////////////////////////////////////////
03958       rc = BeginRead3dmBigChunk( &tcode, &length_TCODE_OPENNURBS_CLASS_UUID );
03959       if ( !rc )
03960         break;
03961       if ( tcode != TCODE_OPENNURBS_CLASS_UUID )
03962       {
03963         ON_ERROR("ON_BinaryArchive::ReadObject() didn't find TCODE_OPENNURBS_CLASS_UUID block");
03964         rc = 0;
03965       }
03966       else if ( expected_length_TCODE_OPENNURBS_CLASS_UUID != length_TCODE_OPENNURBS_CLASS_UUID )
03967       {
03968         ON_ERROR("ON_BinaryArchive::ReadObject() TCODE_OPENNURBS_CLASS_UUID has invalid length");
03969         rc = 0;
03970       }
03971       else if ( !ReadUuid( uuid ) )
03972       {
03973         rc = 0;
03974       }
03975       if ( !EndRead3dmChunk() ) 
03976       {
03977         rc = 0;
03978       }
03979       if ( !rc ) {
03980         break;
03981       }
03983 
03984       if ( !ON_UuidCompare( &uuid, &ON_nil_uuid ) ) {
03985         // nil UUID written if NULL pointer is passed to WriteObject();
03986         rc = 1;
03987         break;
03988       }
03989 
03990       // Use UUID to get ON_ClassId for this class //////////////////////////////////
03991       if ( pObject )
03992       {
03993         pID = pObject->ClassId();
03994         if ( uuid != pID->Uuid() )
03995         {
03996           ON_ERROR("ON_BinaryArchive::ReadObject() - uuid does not match intput pObject's class id.");
03997           pID = 0;
03998           rc = 2;
03999           break;
04000         }
04001       }
04002       else
04003       {
04004         pID = ON_ClassId::ClassId( uuid );
04005       }
04006       if ( !pID ) 
04007       {
04008         // If you get here and you are not calling ON::Begin() at some point in your
04009         // executable, then call ON::Begin() to force all class definition to be linked.
04010         // If you are callig ON::Begin(), then either the uuid is garbage or you are 
04011         // attempting to read an object with old code.
04012         // Visit http://www.opennurbs.org to get the latest OpenNURBS code.
04013         ON_WARNING("ON_BinaryArchive::ReadObject() ON_ClassId::ClassId(uuid) returned NULL.");
04014         rc = 3;
04015         break;
04016       }
04018 
04019       // read class's definitions   /////////////////////////////////////////////////
04020       rc = BeginRead3dmBigChunk( &tcode, &length_TCODE_OPENNURBS_CLASS_DATA );
04021       if ( !rc )
04022         break;
04023       if ( tcode != TCODE_OPENNURBS_CLASS_DATA )
04024       {
04025         ON_ERROR("ON_BinaryArchive::ReadObject() didn't find TCODE_OPENNURBS_CLASS_DATA block");
04026         rc = 0;
04027       }
04028       else if ( length_TCODE_OPENNURBS_CLASS_DATA <= 0  )
04029       {
04030         ON_ERROR("ON_BinaryArchive::ReadObject() TCODE_OPENNURBS_CLASS_DATA chunk length too small");
04031         rc = 0;
04032       }
04033       else 
04034       {
04035         if ( !pObject )
04036         {
04037           pObject = pID->Create();
04038         }
04039 
04040         if ( !pObject ) 
04041         {
04042           ON_ERROR("ON_BinaryArchive::ReadObject() pID->Create() returned NULL.");
04043           rc = 0;
04044         }
04045         else 
04046         {
04047           rc = pObject->Read(*this);
04048           if ( !rc ) 
04049           {
04050             ON_ERROR("ON_BinaryArchive::ReadObject() pObject->Read() failed.");
04051             delete pObject;
04052             // don't break here - we still need to call end chunk.
04053           }
04054           else 
04055           {
04056             *ppObject = pObject;
04057           }
04058         }
04059       }
04060       if ( !EndRead3dmChunk() )
04061       {
04062         rc = 0;
04063       }
04064 
04065       if ( rc && 0 != pObject )
04066       {
04067         // read user data  /////////////////////////////////////////////////
04068         //   If TCODE_OPENNURBS_CLASS_USERDATA chunks exist, this reads them.
04069         //   ReadObjectUserData() stops when it reads a TCODE_OPENNURBS_CLASS_END chunk.
04070         if (!ReadObjectUserData(*pObject))
04071           rc = 0;
04072       }
04073 
04074       break; 
04075     }
04076   }
04077   if ( !EndRead3dmChunk() ) // TCODE_OPENNURBS_CLASS
04078     rc = 0;
04079 
04080   return rc;
04081 }
04082 
04083 bool ON_BinaryArchive::ReadObjectUserDataAnonymousChunk( 
04084           const ON__UINT64 length_TCODE_ANONYMOUS_CHUNK,
04085           const int archive_3dm_version,
04086           const int archive_opennurbs_version,
04087           ON_UserData* ud )
04088 {
04089   // Reads the portion of the file containing the userdata into a buffer
04090   // and lets the plug-in try to read from that.  If the plug-in fails,
04091   // we press on because we cannot trust plug-ins to get IO code right.
04092   bool rc = false;
04093 
04094   if ( 0 == ud )
04095     return false;
04096 
04097   if ( ud->IsUnknownUserData()
04098        || (archive_3dm_version == Archive3dmVersion() 
04099            && archive_opennurbs_version == ArchiveOpenNURBSVersion() 
04100            && IsCoreUserData(ud))
04101      )
04102   {
04103     // assume this userdata's read function is robust.
04104     ON_ReadChunkHelper ch(*this);
04105     if (    !ch.m_bReadSuccess
04106          || TCODE_ANONYMOUS_CHUNK != ch.m_chunk_tcode
04107          || length_TCODE_ANONYMOUS_CHUNK != (ON__UINT64)ch.m_chunk_value
04108        )
04109     {
04110       return false;
04111     }
04112     if ( ud->IsUnknownUserData() )
04113     {
04114       // 22 January 2004 Dale Lear:
04115       //   Disable CRC checking while reading this chunk.  
04116       //   (If the user data has nested chunks, the crc we get
04117       //   by reading the thing as one large chunk will be wrong.)
04118       m_chunk.Last()->m_do_crc16 = false;
04119       m_chunk.Last()->m_do_crc32 = false;
04120       m_bDoChunkCRC = false;
04121     }
04122     rc = ud->Read(*this) ? true : false;
04123   }
04124   else
04125   {
04126     // Untrusted plug-in userdata.
04127     // Insulate file reading from possible bugs plug-in IO code by reading
04128     // entire anonymous chunk into memory and letting the plug-in use
04129     // the memory buffer archive.
04130     unsigned char stack_buffer[2048];
04131     const size_t sizeof_buffer = (size_t)(length_TCODE_ANONYMOUS_CHUNK + 4 + SizeofChunkLength());
04132     void* freeme = 0;
04133     void* buffer = (sizeof_buffer <= sizeof(stack_buffer))
04134                  ? &stack_buffer[0]
04135                  : (freeme = onmalloc(sizeof_buffer)); // generally, object userdata is small we almost never use heap
04136     if ( 0 != buffer 
04137          && sizeof_buffer == ReadBuffer(sizeof_buffer,buffer)
04138        ) 
04139     {
04140       ON_Read3dmBufferArchive memory_archive(
04141         sizeof_buffer,
04142         buffer,
04143         false,
04144         archive_3dm_version,
04145         archive_opennurbs_version
04146         );
04147 
04148       // The TCODE_ANONYMOUS_CHUNK wrapper has chunk lengths set 
04149       // by whatever version wrote this file.  The information
04150       // in the chunk has chunk lengths set by the plug-in that
04151       // originally wrote the user data.  If the plug-in used
04152       // worte to a version <= 5 archive and the user data has
04153       // was read as goo and saved as goo in a version 50+
04154       // archive, then we need to tweak the archive version
04155       // when reading the chunk length of the TCODE_ANONYMOUS_CHUNK wrapper.
04156       bool bTweakArchiveVersion = (memory_archive.SizeofChunkLength() != SizeofChunkLength());
04157       if ( bTweakArchiveVersion )
04158         memory_archive.SetArchive3dmVersion(Archive3dmVersion());
04159       ON_ReadChunkHelper ch(memory_archive);
04160       if ( bTweakArchiveVersion )
04161         memory_archive.SetArchive3dmVersion(archive_3dm_version);
04162 
04163       if (    !ch.m_bReadSuccess
04164            || TCODE_ANONYMOUS_CHUNK != ch.m_chunk_tcode
04165            || length_TCODE_ANONYMOUS_CHUNK != (ON__UINT64)ch.m_chunk_value
04166          )
04167         rc = false;
04168       else
04169         rc = ud->Read(memory_archive) ? true : false;
04170     }
04171     if ( freeme )
04172       onfree(freeme);
04173   }
04174   return rc;
04175 }
04176 
04177 
04178 class CUserDataHeaderInfo
04179 {
04180 public:
04181   CUserDataHeaderInfo();
04182 
04183   void Initialize();
04184 
04185   ON_UUID m_classid;
04186   ON_UUID m_itemid;
04187   ON_UUID m_appid;
04188   int m_3dm_version;
04189   int m_3dm_opennurbs_version;
04190   int m_copycount;
04191   bool m_bLastSavedAsGoo;
04192   ON_Xform m_xform;
04193 };
04194 
04195 CUserDataHeaderInfo::CUserDataHeaderInfo()
04196 {
04197   Initialize();
04198 }
04199 
04200 void CUserDataHeaderInfo::Initialize()
04201 {
04202   memset(this,0,sizeof(*this));
04203 }
04204 
04205 static
04206 bool ReadObjectUserDataHeaderHelper( 
04207           ON_BinaryArchive& binary_archive,
04208           const int major_userdata_version,
04209           const int minor_userdata_version,
04210           CUserDataHeaderInfo& ud_header
04211           )
04212 {
04213   bool rc = true;
04214   ON__UINT32 t = 0;
04215   ON__INT64 length_TCODE_OPENNURBS_CLASS_USERDATA_HEADER = 0;
04216 
04217   ud_header.Initialize();
04218 
04219   if ( major_userdata_version == 2 )
04220   {
04221     // major_userdata_version 2 started wrapping the userdata header info 
04222     // in a TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk
04223     rc = binary_archive.BeginRead3dmBigChunk( &t, &length_TCODE_OPENNURBS_CLASS_USERDATA_HEADER );
04224     if (!rc)
04225       return false;
04226     if ( t != TCODE_OPENNURBS_CLASS_USERDATA_HEADER )
04227     {
04228       ON_ERROR("version 2.0 TCODE_OPENNURBS_CLASS_USERDATA chunk is missing TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk.");
04229       binary_archive.EndRead3dmChunk(); // end of mystery chunk
04230       return false;
04231     }
04232   }
04233   
04234   if (rc) rc = binary_archive.ReadUuid( ud_header.m_classid );
04235   if (rc) rc = binary_archive.ReadUuid( ud_header.m_itemid );
04236   if (rc) rc = binary_archive.ReadInt( &ud_header.m_copycount );
04237   if (rc) rc = binary_archive.ReadXform( ud_header.m_xform );
04238   if ( major_userdata_version == 2 ) 
04239   {
04240     if ( minor_userdata_version >= 1 )
04241     {
04242       if (rc) rc = binary_archive.ReadUuid( ud_header.m_appid );
04243       if ( minor_userdata_version >= 2 )
04244       {
04245         // bLastSavedAsGoo is true if the user data was saved
04246         // into the file by ON_UnknownUserData.
04247         if (rc) rc = binary_archive.ReadBool( &ud_header.m_bLastSavedAsGoo ); 
04248         if (rc) rc = binary_archive.ReadInt( &ud_header.m_3dm_version );
04249         if (rc) rc = binary_archive.ReadInt( &ud_header.m_3dm_opennurbs_version );
04250       }
04251     }
04252     if ( !binary_archive.EndRead3dmChunk() ) // end of TCODE_OPENNURBS_CLASS_USERDATA_HEADER
04253       rc = 0;
04254   }
04255 
04256   if (!rc)
04257   {
04258     ON_ERROR("Unable to read user data header information.");
04259     return false;
04260   }
04261 
04262   if ( 0 == ud_header.m_3dm_version || 0 == ud_header.m_3dm_opennurbs_version )
04263   {
04264     // The userdata was saved in in an archive before
04265     // the 3dm_version and 3dm_opennurbs_version were saved in
04266     // userdata headers.  This means it has to be userdata
04267     // with 4 byte chunk lengths.  If the archive we are
04268     // reading was written with a newer version of opennurbs
04269     // that does save the 3dm version info, then this unknown
04270     // userdata was that has persisted through multiple read/write 
04271     // cycles and we cannot tell it's original version. So we
04272     // will default to a maximum of 5 and 200910180 - the 
04273     // 3dm versions just before we started saving 3dm 
04274     // version info n userdata headers.
04275     if ( binary_archive.Archive3dmVersion() < 50 )
04276     {
04277      ud_header.m_3dm_version = binary_archive.Archive3dmVersion();
04278     }
04279     else
04280     {
04281       // All Archive3dmVersion() >= 50 have userdata_3dm_version info,
04282       // so this userdata had to be saved as goo from a version 5 or
04283       // earlier archive.
04284       ud_header.m_bLastSavedAsGoo = true;
04285       ud_header.m_3dm_version = 5;
04286     }
04287     ud_header.m_3dm_opennurbs_version = binary_archive.ArchiveOpenNURBSVersion();
04288     if ( ud_header.m_3dm_opennurbs_version >= 200910190 )
04289     {
04290       ud_header.m_3dm_opennurbs_version = 200910180;
04291       ud_header.m_bLastSavedAsGoo = true;
04292     }
04293   }
04294 
04295   return rc;
04296 }
04297 
04298 bool ON_BinaryArchive::ReadObjectUserData( ON_Object& object )
04299 {
04300   bool rc = true;
04301   while(rc) 
04302   {
04303     ON_ReadChunkHelper ch(*this);
04304     if ( !ch.m_bReadSuccess )
04305     {
04306       rc = false;
04307       break;
04308     }
04309 
04310     if ( TCODE_OPENNURBS_CLASS_END == ch.m_chunk_tcode )
04311     {
04312       // A short TCODE_OPENNURBS_CLASS_END chunk marks the end of the opennurbs class
04313       break; // done
04314     }
04315 
04316     if ( TCODE_OPENNURBS_CLASS_USERDATA != ch.m_chunk_tcode )
04317     {
04318       // skip new chunk type added by later version
04319       continue;
04320     }
04321 
04322     if ( ch.m_chunk_value < (ON__INT64)(8 + 4 * SizeofChunkLength()) )
04323     {
04324       ON_ERROR("TCODE_OPENNURBS_CLASS_USERDATA chunk is too short");
04325       continue;
04326     }
04327 
04328     // Read userdata header information
04329     int major_userdata_version = 0;
04330     int minor_userdata_version = 0;
04331     rc = Read3dmChunkVersion( &major_userdata_version, &minor_userdata_version );
04332     if ( !rc )
04333     {
04334       ON_ERROR("Unable to read TCODE_OPENNURBS_CLASS_USERDATA chunk version numbers");
04335       break;
04336     }
04337 
04338     if ( major_userdata_version < 1 || major_userdata_version > 2 )
04339     {
04340       // unsupported version - too old or added in new version
04341       continue;
04342     }
04343 
04344     CUserDataHeaderInfo ud_header;
04345     rc = ReadObjectUserDataHeaderHelper(*this,major_userdata_version,minor_userdata_version,ud_header);
04346     if (!rc)
04347     {
04348       ON_ERROR("Unable to read user data header information.");
04349       break;
04350     }
04351 
04352     // we should be ready to read a TCODE_ANONYMOUS_CHUNK containing userdata
04353     ON__INT64 length_TCODE_ANONYMOUS_CHUNK = 0;
04354     for(;;)
04355     {
04356       ON__UINT32 t = 0;
04357       rc = PeekAt3dmBigChunkType( &t, &length_TCODE_ANONYMOUS_CHUNK );
04358       if (!rc)
04359         break;
04360       if ( t != TCODE_ANONYMOUS_CHUNK )
04361       {
04362         ON_ERROR("Reading object user data - unable to find TCODE_ANONYMOUS_CHUNK");
04363         rc = false;
04364         break;
04365       }
04366       if ( length_TCODE_ANONYMOUS_CHUNK < 4 )
04367       {
04368         ON_ERROR("Reading object user data - length of TCODE_ANONYMOUS_CHUNK < 4");
04369         rc = false;
04370         break;
04371       }
04372       break;
04373     }
04374     if (!rc)
04375     {
04376       break;
04377     }
04378 
04379     // attempt to get an instance of the userdata class that saved this information
04380     ON_UserData* ud = 0;
04381     for(;;)
04382     {
04383       const ON_ClassId* udId = ON_ClassId::ClassId( ud_header.m_classid );
04384       if ( 0 == udId ) 
04385       {
04386         // The application that created this userdata is not active
04387         if ( !ON_UuidIsNil(ud_header.m_appid) )
04388         {
04389           // see if we can load the application
04390           if ( 1 == LoadUserDataApplication(ud_header.m_appid) )
04391           {
04392             // try again
04393             udId = ON_ClassId::ClassId( ud_header.m_classid );
04394           }
04395         }
04396 
04397         if ( 0 == udId )
04398         {
04399           // The application that created this user data is
04400           // not available.  This information will be stored
04401           // in an ON_UnknownUserData class so that it can
04402           // persist.
04403           udId = &ON_UnknownUserData::m_ON_UnknownUserData_class_id;
04404         }
04405       }
04406 
04407       ON_Object* tmp = udId->Create();
04408       ud = ON_UserData::Cast(tmp);
04409       if ( 0 == ud )
04410       {
04411         ON_ERROR("Reading object user data - unable to create userdata class");
04412         if ( tmp )
04413           delete tmp;
04414         tmp = 0;
04415         break;
04416       }
04417       tmp = 0;
04418 
04419       break;
04420     }
04421 
04422     if ( 0 == ud )
04423     {
04424       // no luck on this one 
04425       // One reason can be that the plug-in userdata class has something wrong with 
04426       // its ON_OBJECT_DECLARE/ON_OBJECT_IMPLEMENT stuff.
04427       ON_ERROR("Unable to create object user data class. Flawed class id information.");
04428       continue; // keep trying
04429     }
04430 
04431     if ( ON_UuidIsNil(ud->m_application_uuid) )
04432     {
04433       if ( ON_UuidIsNil(ud_header.m_appid) )
04434       {
04435         switch( Archive3dmVersion())
04436         {
04437         case 2:
04438           // V2 archives do not contain app ids.
04439           // This id flags the userdata as being read from a V3 archive.
04440           ud_header.m_appid = ON_v2_userdata_id;
04441           break;
04442         case 3:
04443           // V3 archives do not contain app ids.
04444           // This id flags the userdata as being
04445           // read from a V3 archive.
04446           ud_header.m_appid = ON_v3_userdata_id;
04447           break;
04448         case 4:
04449           if ( ArchiveOpenNURBSVersion() < 200909190 )
04450           {
04451             // V4 archives before version 200909190
04452             // did not require user data application ids.
04453             ud_header.m_appid = ON_v4_userdata_id;
04454           }
04455           break;
04456         }
04457       }
04458       ud->m_application_uuid = ud_header.m_appid;
04459     }
04460     ud->m_userdata_uuid = ud_header.m_itemid;
04461     ud->m_userdata_copycount = ud_header.m_copycount;
04462     ud->m_userdata_xform = ud_header.m_xform;
04463     if ( ud->IsUnknownUserData() ) 
04464     {
04465       ON_UnknownUserData* uud = ON_UnknownUserData::Cast(ud);
04466       if ( uud ) 
04467       {
04468         uud->m_sizeof_buffer = (int)length_TCODE_ANONYMOUS_CHUNK;
04469         uud->m_unknownclass_uuid = ud_header.m_classid;
04470         uud->m_3dm_version = ud_header.m_3dm_version;
04471         uud->m_3dm_opennurbs_version = ud_header.m_3dm_opennurbs_version;
04472       }
04473     }
04474     ud->m_userdata_owner = &object; // so reading code can look at owner
04475     bool bReadUserData = ReadObjectUserDataAnonymousChunk(
04476               length_TCODE_ANONYMOUS_CHUNK,
04477               ud_header.m_3dm_version,
04478               ud_header.m_3dm_opennurbs_version,
04479               ud
04480               );
04481     ud->m_userdata_owner = 0;
04482     if (bReadUserData) 
04483     {
04484       if ( !object.AttachUserData( ud ) )
04485         delete ud;
04486     }
04487     else
04488     {
04489       delete ud;
04490     }    
04491   }
04492 
04493   return rc;
04494 }
04495 
04496 bool ON_BinaryArchive::Write3dmChunkVersion(
04497   int major_version, // major // 0 to 15
04498   int minor_version // minor // 0 to 16
04499   )
04500 {
04501   const unsigned char v = (unsigned char)(major_version*16+minor_version);
04502   return WriteChar( v );
04503 }
04504 
04505 bool ON_BinaryArchive::Read3dmChunkVersion(
04506   int* major_version, // major // 0 to 15
04507   int* minor_version // minor // 0 to 16
04508   )
04509 {
04510   unsigned char v = 0;
04511   bool rc = ReadChar( &v );
04512   if ( minor_version) *minor_version = v%16;
04513   // The bit shift happens on the fly in the following
04514   // if statement.  It was happening twice which always
04515   // set the major version to 0
04516   //v >>= 4;
04517   if ( major_version) *major_version = (v>>4);
04518   return rc;
04519 }
04520 
04521 int ON_BinaryArchive::Archive3dmVersion() const
04522 {
04523   return m_3dm_version;
04524 }
04525 
04526 int ON_BinaryArchive::ArchiveOpenNURBSVersion() const
04527 {
04528   return m_3dm_opennurbs_version;
04529 }
04530 
04531 size_t ON_BinaryArchive::ArchiveStartOffset() const
04532 {
04533   return m_3dm_start_section_offset;
04534 }
04535 
04539 
04540 bool 
04541 ON_BinaryArchive::BeginWrite3dmChunk( unsigned int typecode, int value )
04542 {
04543   ON__INT64 value64 = 0;
04544   if ( 0 != value )
04545   {
04546     if ( ON_IsUnsignedChunkTypecode(typecode) )
04547     {
04548       // treat value parameter as an unsigned int
04549       ON__UINT32 u32 = (ON__UINT32)value;
04550       ON__UINT64 u64 = u32;
04551       value64 = (ON__INT64)u64;
04552     }
04553     else
04554     {
04555       // treat value paramter is a signed int
04556       value64 = value;
04557     }
04558   }
04559   return BeginWrite3dmBigChunk(typecode,value);
04560 }
04561 
04562 bool 
04563 ON_BinaryArchive::BeginWrite3dmBigChunk( ON__UINT32 typecode, ON__INT64 value64 )
04564 {
04565   m_bDoChunkCRC = false; // no CRC on chunks because length is written twice.
04566   bool rc = WriteInt32( 1, (ON__INT32*)&typecode );
04567   if (rc)
04568     rc = WriteChunkValue( typecode, value64 );
04569   if (rc)
04570     rc = PushBigChunk( typecode, value64 );
04571   return rc;
04572 }
04573 
04574 bool 
04575 ON_BinaryArchive::BeginWrite3dmChunk(
04576           unsigned int tcode,
04577           int major_version,
04578           int minor_version
04579           )
04580 {
04581   bool rc = false;
04582   if ( 0 == tcode )
04583   {
04584     ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input tcode = 0");
04585   }
04586   else if ( 0 != (tcode & TCODE_SHORT) )
04587   {
04588     ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input tcode has short flag set.");
04589   }
04590   else if ( major_version <= 0 )
04591   {
04592     ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input major_version <= 0.");
04593   }
04594   else if ( minor_version < 0 )
04595   {
04596     ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input minor_version < 0.");
04597   }
04598   else
04599   {
04600     rc = BeginWrite3dmChunk(tcode,0);
04601     if (rc)
04602     {
04603       rc = WriteInt(major_version);
04604       if (rc)
04605         rc = WriteInt(minor_version);
04606       if ( !rc)
04607         EndWrite3dmChunk();
04608     }
04609   }
04610   return rc;
04611 }
04612 
04613 bool 
04614 ON_BinaryArchive::EndWrite3dmChunk()
04615 {
04616   bool rc = false;
04617   ON_3DM_BIG_CHUNK* c = m_chunk.Last();
04618   if ( c ) {
04619     if ( c->m_bLongChunk ) 
04620     {
04621       if ( c->m_do_crc16 ) 
04622       {
04623         // write 16 bit CRC
04624         unsigned char two_zero_bytes[2] = {0,0};
04625         ON__UINT16 crc = ON_CRC16( c->m_crc16, 2, two_zero_bytes );
04626         rc = WriteInt16( 1, (ON__INT16*)&crc );
04627         if (c->m_crc16) 
04628         {
04629           // should never happen unless ON_CRC16() code is damaged
04630           m_bad_CRC_count++;
04631           ON_ERROR("ON_BinaryArchive::EndWrite3dmChunk: CRC16 computation error.");
04632         }
04633       }
04634       else if ( c->m_do_crc32 ) 
04635       {
04636         // write 32 bit CRC
04637         const ON__UINT32 crc0 = c->m_crc32; 
04638         rc = WriteInt32( 1, (ON__INT32*)&crc0 );
04639       }
04640       else {
04641         rc = true;
04642       }
04643 
04644       // write length
04645       m_bDoChunkCRC = 0;
04646       const size_t offset = CurrentPosition();
04647       if ( offset < c->m_big_offset ) 
04648       {
04649         ON_ERROR("ON_BinaryArchive::EndWrite3dmChunk() - chunk length < 0");
04650         rc = false;
04651       }
04652       else 
04653       {
04654         ON__UINT64 length = (offset - c->m_big_offset);
04655         if ( !BigSeekBackward( length + SizeofChunkLength() ) ) 
04656         {
04657           rc = false;
04658         }
04659         else 
04660         {
04661           if ( !WriteChunkLength( length ) )
04662           {
04663             rc = false;
04664           }
04665           if ( !BigSeekForward( length ) ) 
04666           {
04667             rc = false;
04668           }
04669         }
04670         if ( CurrentPosition() != offset ) 
04671         {
04672           ON_ERROR("ON_BinaryArchive::EndWrite3dmChunk() - CurrentPosition() != offset");
04673           rc = false;
04674         }
04675       }
04676     }
04677     else 
04678     {
04679       // "short" chunks are completely written by call to BeginWrite3dmChunk().
04680       rc = true;
04681     }
04682 
04683     m_chunk.Remove();
04684     c = m_chunk.Last();
04685     if ( !c )
04686     {
04687       Flush();
04688     }
04689     m_bDoChunkCRC = c && (c->m_do_crc16 || c->m_do_crc32);
04690   }
04691   return rc;
04692 }
04693 
04694 bool ON_BinaryArchive::Write3dmGoo( const ON_3dmGoo& goo )
04695 {
04696   bool rc = false;
04697 
04698   if ( goo.m_typecode ) {
04699     const bool savedDoCRC = m_bDoChunkCRC;
04700     m_bDoChunkCRC = false;
04701     if ( 0 != (goo.m_typecode & TCODE_SHORT) ) {
04702       if ( goo.m_value == 0 || (goo.m_value > 0 && goo.m_goo) ) {
04703         // write long chunk - do not use Begin/EndWrite3dmChunk() because
04704         //                    goo may contain subchunks and CRC would be
04705         //                    incorrectly computed.
04706         rc = WriteInt( goo.m_typecode );
04707         if (rc) rc = WriteInt( goo.m_value );
04708         if (rc && goo.m_value>0) rc = WriteByte( goo.m_value, goo.m_goo );
04709       }
04710     }
04711     else {
04712       // write short chunk
04713       rc = WriteInt( goo.m_typecode );
04714       if (rc) rc = WriteInt( goo.m_value );
04715     }
04716     m_bDoChunkCRC = savedDoCRC;
04717   }
04718   
04719   return rc;
04720 }
04721 
04722 bool ON_BinaryArchive::PeekAt3dmChunkType( unsigned int* typecode, int* value )
04723 {
04724   // does not change file position
04725   bool rc;
04726   unsigned int tc = 0;
04727   ON__INT64 i64 = 0;
04728   rc = PeekAt3dmBigChunkType(&tc,&i64);
04729   if ( rc )
04730   {
04731     if ( 0 != typecode )
04732       *typecode = tc;
04733     if ( 0 != value )
04734     {
04735       ON__INT32 i32 = 0;
04736       if ( ON_IsUnsignedChunkTypecode(tc) )
04737         rc = DownSizeUINT((ON__UINT64)i64,(ON__UINT32*)&i32);
04738       else
04739         rc = DownSizeINT(i64,&i32);
04740       *value = i32;
04741     }
04742   }
04743   return rc;
04744 }
04745 
04746 bool ON_BinaryArchive::PeekAt3dmBigChunkType( 
04747       unsigned int* typecode,
04748       ON__INT64* big_value
04749       )
04750 {
04751   // does not change file position
04752 
04753   // 10 January 2005 Dale Lear
04754   //     Do not let the "peeking" affect the CRC.
04755   bool bDoChunkCRC = m_bDoChunkCRC;
04756   m_bDoChunkCRC = false;
04757 
04758   const ON__UINT64 pos0 = CurrentPosition();
04759   ON__UINT32 t = 0;
04760   ON__INT64 v = 0;
04761   bool rc = ReadChunkTypecode( &t );
04762   if (rc) 
04763   {
04764     rc = ReadChunkValue( t, &v );
04765   }
04766   const ON__UINT64 pos1 = CurrentPosition();
04767   if ( pos1 > pos0 && !BigSeekBackward( pos1-pos0 ) )
04768   {
04769     rc = false;
04770   }
04771 
04772   m_bDoChunkCRC = bDoChunkCRC;
04773 
04774   if ( typecode )
04775     *typecode = t;
04776   if ( big_value )
04777     *big_value = v;
04778 
04779   return rc;
04780 }
04781 
04782 
04783 bool ON_BinaryArchive::Seek3dmChunkFromStart( 
04784       // beginning at the start of the active chunk, search portion of
04785       // archive included in active chunk for the start of a subchunk 
04786       // with the specified type.
04787       // if true is returned, then the position is set so the next call to
04788       // BeginRead3dmChunk() will read a chunk with the specified typecode
04789       unsigned int typecode   // typecode from opennurbs_3dm.h
04790       )
04791 {
04792   bool rc = false;
04793   if ( ReadMode() ) 
04794   {
04795     const ON__UINT64 pos0 = CurrentPosition();
04796     const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
04797     if ( c ) 
04798     {
04799       // set archive position to the beginning of this chunk
04800       if ( !ON_IsLongChunkTypecode(c->m_typecode) ) 
04801       {
04802         ON_ERROR("ON_BinaryArchive::Seek3dmChunkFromStart() - current chunk is not a long chunk");
04803         return false;
04804       }
04805       if ( c->m_big_value < 0 ) 
04806       {
04807         ON_ERROR("ON_BinaryArchive::Seek3dmChunkFromStart() called with an active chunk that has m_value < 0");
04808         return false;
04809       }
04810       if ( pos0 < c->m_big_offset || pos0 > c->m_big_offset + c->Length() ) 
04811       {
04812         ON_ERROR("ON_BinaryArchive::Seek3dmChunkFromStart() called with out of bounds current position");
04813         return false;
04814       }
04815       rc = BigSeekBackward( pos0 - c->m_big_offset ); // pos0 >= c->m_offset, so this size_t subtraction is ok
04816     }
04817     else 
04818     {
04819       // set archive position to the beginning of archive chunks by skipping
04820       // 32 byte version info and any start section padding.
04821       size_t start_offset = ((m_3dm_start_section_offset > 0) ? m_3dm_start_section_offset : 0);
04822       rc = SeekFromStart( start_offset );
04823       if (!rc && start_offset > 0)
04824       {
04825         start_offset = 0;
04826         rc = SeekFromStart(start_offset);
04827       }
04828       char s3d[33];
04829       memset(s3d,0,sizeof(s3d));
04830       if (rc) 
04831         rc = ReadByte(32,s3d);
04832 
04833       if (rc)
04834       {
04835         rc = (0 == strncmp( s3d, "3D Geometry File Format ", 24));
04836         if ( !rc && start_offset > 0 )
04837         {
04838           start_offset = 0;
04839           rc = SeekFromStart(start_offset);
04840           if (rc) rc = ReadByte(32,s3d);
04841           rc = (0 == strncmp( s3d, "3D Geometry File Format ", 24));
04842         }
04843       }
04844 
04845       if (rc)
04846       {
04847         if ( start_offset != m_3dm_start_section_offset )
04848           m_3dm_start_section_offset = start_offset;
04849         unsigned int t=0;
04850         ON__INT64 v=-1;
04851         rc = PeekAt3dmBigChunkType(&t,&v);
04852         if (rc && (t != 1 || v < 0) )
04853           rc = false;
04854       }
04855     }
04856 
04857     if (rc) 
04858     {
04859       rc = Seek3dmChunkFromCurrentPosition( typecode );
04860     }
04861 
04862     if (!rc) 
04863     {
04864       BigSeekFromStart(pos0);
04865     }
04866   }
04867   return rc;
04868 }
04869 
04870 ON__UINT64 ON_3DM_BIG_CHUNK::Length() const
04871 {
04872   return (ON_IsLongChunkTypecode(m_typecode) && m_big_value >= 0 )
04873     ? ((ON__UINT64)m_big_value)
04874     : 0;
04875 }
04876 
04877 bool ON_BinaryArchive::Seek3dmChunkFromCurrentPosition( 
04878       // beginning at the current position, search portion of archive
04879       // included in active chunk for the start of a subchunk with the
04880       // specified type.
04881       // if true is returned, then the position is set so the next call to
04882       // BeginRead3dmChunk() will read a chunk with the specified typecode
04883       unsigned int typecode // typecode from opennurbs_3dm.h
04884       )
04885 {
04886   bool rc = false;
04887   if ( ReadMode() ) 
04888   {
04889     const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
04890     const ON__UINT64 pos1 = c ? c->m_big_offset + c->Length() : 0;
04891     const size_t pos_start = CurrentPosition();
04892     size_t pos_prev = 0;
04893     size_t pos = 0;
04894     unsigned int t;
04895     ON__INT64 v64;
04896     bool bFirstTime = true;
04897     while(pos > pos_prev || bFirstTime) 
04898     {
04899       bFirstTime = false;
04900       pos_prev = pos;
04901       pos = CurrentPosition();
04902       if ( pos1 && pos > pos1 )
04903         break;
04904       t = !typecode;
04905       if ( !PeekAt3dmBigChunkType( &t, 0 ) )
04906         break;
04907       if ( t == typecode ) 
04908       {
04909         rc = true;
04910         break;
04911       }
04912       if ( 0 == t )
04913       {
04914         // zero is not a valid typecode - file is corrupt at or before this position
04915         break;
04916       }
04917       if ( !BeginRead3dmBigChunk( &t, &v64 ) )
04918         break;
04919       if ( !EndRead3dmChunk() )
04920         break;
04921       if ( TCODE_ENDOFTABLE == t && 0 != v64 )
04922       {
04923         // TCODE_ENDOFTABLE chunks always have value = 0 - file is corrupt at or before this position
04924         break;
04925       }
04926     }
04927     if ( !rc ) 
04928     {
04929       SeekFromStart( pos_start );
04930     }
04931   }
04932   return rc;
04933 }
04934 
04935 bool ON_BinaryArchive::BeginRead3dmChunk( unsigned int* typecode, int* value )
04936 {
04937   ON__UINT32 tc = 0;
04938   ON__INT64 v64 = 0;
04939   bool rc = BeginRead3dmBigChunk(&tc,&v64);
04940   if (rc)
04941   {
04942     if ( 0 != typecode )
04943       *typecode = tc;
04944     rc = DownSizeChunkValue(tc,v64,value);
04945   }
04946   return rc;
04947 }
04948 
04949 bool ON_BinaryArchive::BeginRead3dmBigChunk( unsigned int* typecode, ON__INT64* value )
04950 {
04951   ON__UINT32 t = 0;
04952   ON__INT64 v = 0;
04953   m_bDoChunkCRC = false; // no CRC on chunk headers because length is written twice
04954   const unsigned int saved_error_message_mask = m_error_message_mask;
04955   m_error_message_mask |= 0x0001; // disable ReadByte() error message at EOF
04956   bool rc = ReadChunkTypecode( &t );
04957   m_error_message_mask = saved_error_message_mask;
04958   if (rc) 
04959   {
04960     if ( t == TCODE_ENDOFFILE ) 
04961     {
04962       // Either this chunk is a bona fide end of file mark, or it's "goo" 
04963       // that Rhino 1.0 or the pre-February 2000 Rhino 1.1 saved and wrote.
04964       ON__UINT64 sizeof_file = 0;
04965       if ( rc )
04966         rc = ReadChunkValue( t, &v );
04967       if ( rc && v >= 0 && ((ON__UINT64)v) >= SizeofChunkLength() ) 
04968       {
04969         ON__UINT64 EOF_chunk_length = (ON__UINT64)v;
04970         ON__UINT64 pos0 = CurrentPosition();
04971         rc = ReadEOFSizeOfFile( &sizeof_file );
04972         ON__UINT64 pos1 = CurrentPosition();
04973         if ( pos0 > 0 && pos1 > pos0 )
04974         {
04975           if ( !BigSeekBackward(pos1-pos0) )
04976             rc = false;
04977         }
04978         if ( rc ) 
04979         {
04980           if ( BigSeekForward( EOF_chunk_length ) )
04981           {
04982             ON__UINT64 pos2 = CurrentPosition(); 
04983             if ( m_3dm_version <= 1 )
04984             {
04985               if ( !AtEnd() ) 
04986               {
04987                 // Rhino v1 reads chunks with unknown typecodes as a block of "goo"
04988                 // and then saves them back into file.  When this happens to an
04989                 // eof marker, we get TCODE_ENDOFFILE chunks in the middle of a file.
04990                 // This can only happen in m_3dm_version = 1 files.
04991                 t = TCODE_ENDOFFILE_GOO;
04992               }
04993             }
04994             else
04995             {
04996               // check that current position matches saved file size
04997               if ( pos2 != sizeof_file ) {
04998                 ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk() - Rogue eof marker in v2 file.\n");
04999               }
05000             }
05001             rc = BigSeekBackward( EOF_chunk_length );
05002           }
05003         }
05004         if ( rc )
05005           rc = PushBigChunk( t, v );
05006       }
05007       else 
05008       {
05009         ON_ERROR( "ON_BinaryArchive::BeginRead3dmChunk() - file is damaged." );
05010         rc = false;
05011         t = 0; // ?? file is trashed ??
05012       }
05013     }
05014     else 
05015     {
05016       if ( rc )
05017         rc = ReadChunkValue( t, &v );
05018       if ( rc )
05019         rc = PushBigChunk( t, v );
05020     }
05021   }
05022   if ( typecode )
05023     *typecode = t;
05024   if ( value )
05025     *value = v;
05026   return rc;
05027 }
05028 
05029 bool 
05030 ON_BinaryArchive::BeginRead3dmChunk(
05031           unsigned int expected_tcode,
05032           int* major_version,
05033           int* minor_version
05034           )
05035 {
05036   bool rc = false;
05037   if ( 0 == expected_tcode )
05038   {
05039     ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input expected_tcode = 0");
05040   }
05041   else if ( 0 != (expected_tcode & TCODE_SHORT) )
05042   {
05043     ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input expected_tcode has short flag set.");
05044   }
05045   else if ( 0 == major_version )
05046   {
05047     ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input major_version NULL");
05048   }
05049   else if ( 0 == minor_version )
05050   {
05051     ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input minor_version NULL");
05052   }
05053   else
05054   {
05055     *major_version = 0;
05056     *minor_version = 0;
05057     unsigned int tcode = 0;
05058     ON__INT64 value = 0;
05059     rc = PeekAt3dmBigChunkType(&tcode,&value);
05060     if ( expected_tcode != tcode )
05061     {
05062       ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - unexpected tcode");
05063       rc = false;
05064     }
05065     else if ( value < 8 )
05066     {
05067       ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - unexpected chunk length");
05068       rc = false;
05069     }
05070     else
05071     {
05072       tcode = 0;
05073       value = 0;
05074       rc = BeginRead3dmBigChunk(&tcode,&value);
05075       if (rc)
05076       {
05077         if ( expected_tcode != tcode || value < 8 )
05078         {
05079           // can happen when seek fails
05080           ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - unexpected tcode or chunk length - archive driver or device may be bad");
05081           rc = false;
05082         }
05083         else
05084         {
05085           rc = ReadInt(major_version);
05086           if ( rc && *major_version < 1 )
05087           {
05088             ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - major_version < 1");
05089             rc = false;
05090           }
05091           if (rc)
05092           {
05093             rc = ReadInt(minor_version);
05094             if ( rc && *minor_version < 0 )
05095             {
05096               ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - minor_version < 0");
05097               rc = false;
05098             }
05099           }
05100         }
05101 
05102         if ( !rc )
05103         {
05104           // this is required to keep chunk accounting in synch
05105           EndRead3dmChunk();
05106         }
05107       }
05108     }
05109   }
05110   return rc;
05111 }
05112 bool ON_BinaryArchive::EndRead3dmChunk()
05113 {
05114   return EndRead3dmChunk(false);
05115 }
05116 
05117 bool ON_BinaryArchive::EndRead3dmChunk(bool bSupressPartiallyReadChunkWarning)  
05118 {
05119   //int length = 0;
05120   bool rc = false;
05121   ON_3DM_BIG_CHUNK* c = m_chunk.Last();
05122   if ( c ) 
05123   {
05124     ON__UINT64 file_offset = CurrentPosition();
05125     ON__UINT64 end_offset = c->m_big_offset;
05126     if ( c->m_bLongChunk )
05127     {
05128       if ( c->m_big_value < 0 )
05129       {
05130         ON_ERROR("ON_BinaryArchive::EndRead3dmChunk - negative chunk length");
05131       }
05132       else
05133       {
05134         end_offset += ((ON__UINT64)c->m_big_value);
05135       }
05136     }
05137 
05138     if ( c->m_bLongChunk ) 
05139     {
05140       if ( c->m_do_crc16 ) 
05141       {
05142         if ( file_offset+2 == end_offset )
05143         {
05144           // read 16 bit CRC
05145           unsigned char two_crc_bytes[2] = {0,0};
05146           rc = ReadByte( 2, two_crc_bytes );
05147           if (rc) 
05148           {
05149             file_offset+=2;
05150             if (c->m_crc16) 
05151             {
05152               m_bad_CRC_count++;
05153               ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: CRC16 error.");
05154             }
05155           }
05156         }
05157         else
05158         {
05159           // partially read chunk - crc check not possible.
05160           rc = true;
05161         }
05162       }
05163       else if ( c->m_do_crc32 ) 
05164       {
05165         if ( file_offset+4 == end_offset )
05166         {
05167           // read 32 bit CRC
05168           ON__UINT32 crc1 = c->m_crc32;
05169           ON__UINT32 crc0;
05170           rc = ReadInt32( 1, (ON__INT32*)&crc0 );
05171           if (rc) 
05172           {
05173             file_offset+=4;
05174             if (crc0 != crc1) 
05175             {
05176               m_bad_CRC_count++;
05177               ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: CRC32 error.");
05178             }
05179           }
05180         }
05181         else
05182         {
05183           // partially read chunk - crc check not possible.
05184           rc = true;
05185         }
05186       }
05187       else 
05188       {
05189         // no crc in this chunk
05190         rc = true;
05191       }
05192     }
05193     else 
05194     {
05195       rc = true;
05196     }
05197 
05198     // check length and seek to end of chunk if things are amiss
05199     if ( file_offset < c->m_big_offset ) 
05200     {
05201       ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: current position before start of current chunk.");
05202       if ( !BigSeekFromStart( end_offset ) )
05203         rc = false;
05204     }
05205     else if ( file_offset > end_offset ) 
05206     {
05207       ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: current position after end of current chunk.");
05208       if ( !BigSeekFromStart( end_offset ) )
05209         rc = false;
05210     }
05211     else if ( file_offset != end_offset ) 
05212     {
05213       // partially read chunk - happens when chunks are skipped or old code
05214       // reads a new minor version of a chunk whnich has added information.
05215       if ( file_offset != c->m_big_offset ) 
05216       {
05217         if ( m_3dm_version != 1 || (m_error_message_mask&0x02) == 0 ) 
05218         {
05219           // when reading v1 files, there are some situations where
05220           // it is reasonable to attempt to read 4 bytes at the end
05221           // of a file.  The above test prevents making a call
05222           // to ON_WARNING() in these situations.
05223 
05224           const int app_opennurbs_version = ON::Version();
05225           const int filev_date = m_3dm_opennurbs_version/10;
05226           const int appv_date  = app_opennurbs_version/10;
05227           int file_v = m_3dm_opennurbs_version%10;
05228           int app_v  = app_opennurbs_version%10;
05229           if ( 9 == file_v || 9 == app_v )
05230           {
05231             // 9 means DEBUG opennurbs was used to write the file.
05232             file_v = app_v = 0;
05233           }
05234           if ( file_v <= app_v && filev_date <= appv_date )
05235           {
05236             // We are reading a file written by this version or an
05237             // earlier version of opennurbs.  
05238             // There should not be any partially read chunks.
05239             if (!bSupressPartiallyReadChunkWarning)
05240             {
05241             ON_WARNING("ON_BinaryArchive::EndRead3dmChunk: partially read chunk - skipping bytes at end of current chunk.");
05242           }
05243         }
05244       }
05245       }
05246       //int delta =  (end_offset >= file_offset) 
05247       //          ?  ((int)(end_offset-file_offset)) 
05248       //          : -((int)(file_offset-end_offset));
05249       //if ( !SeekFromCurrentPosition( delta ) )
05250       //  rc = false;
05251       if ( end_offset > file_offset )
05252       {
05253         if ( !BigSeekForward(end_offset - file_offset) )
05254           rc = false;
05255       }
05256       else if ( end_offset < file_offset )
05257       {
05258         if ( !BigSeekBackward(file_offset - end_offset) )
05259           rc = false;
05260       }
05261     }
05262 
05263     m_chunk.Remove();
05264     c = m_chunk.Last();
05265     m_bDoChunkCRC = (c && (c->m_do_crc16 || c->m_do_crc32));
05266   }
05267   return rc;
05268 }
05269 
05270 bool ON_BinaryArchive::BeginWriteDictionary(
05271         ON_UUID dictionary_id,
05272         unsigned int version,
05273         const wchar_t* dictionary_name
05274         )
05275 {
05276 #if defined(ON_COMPILER_MSC)
05277 // Disable the MSC /W4 "conditional expression is constant" warning
05278 // about sizeof(unsigned short) == sizeof(*dictionary_name).  
05279 // Since this code has to run on machines where sizeof(wchar_t) 
05280 // can be 2, 4, or 8 bytes, the test is necessary.
05281 #pragma warning( push )
05282 #pragma warning( disable : 4127 )
05283 #endif
05284 
05285   bool rc = BeginWrite3dmChunk(TCODE_DICTIONARY,1,0);
05286   if ( !rc )
05287     return rc;
05288 
05289   // Write dictionary id chunk
05290   rc = BeginWrite3dmChunk(TCODE_DICTIONARY_ID,1,0);
05291   if ( rc )
05292   {
05293     for(;;)
05294     {
05295       rc = WriteUuid(dictionary_id);
05296       if (!rc) break;
05297       rc = WriteInt(version);
05298       if (!rc) break;
05299       if ( sizeof(unsigned short) == sizeof(*dictionary_name) )
05300       {
05301         rc = WriteString((const unsigned short*)dictionary_name);
05302       }
05303       else
05304       {
05305         ON_wString s(dictionary_name);
05306         rc = WriteString(s);
05307       }
05308       if (!rc) break;
05309       break;
05310     }
05311     if ( !EndWrite3dmChunk() ) // TCODE_DICTIONARY_ID end
05312       rc = false;
05313   }
05314 
05315   if ( !rc )
05316     EndWrite3dmChunk(); // TCODE_DICTIONARY end
05317   return rc;
05318 
05319 #if defined(ON_COMPILER_MSC)
05320 #pragma warning( pop )
05321 #endif
05322 }
05323 
05324 bool ON_BinaryArchive::EndWriteDictionary()
05325 {
05326   int chunk_count = m_chunk.Count();
05327   bool rc = ( chunk_count > 0 && TCODE_DICTIONARY == m_chunk[chunk_count-1].m_typecode );
05328   if (rc)
05329   {
05330     rc = BeginWrite3dmChunk(TCODE_DICTIONARY_END,0);
05331     if (rc)
05332       rc = EndWrite3dmChunk(); // TCODE_DICTIONARY_END
05333 
05334     if ( !EndWrite3dmChunk() ) // TCODE_DICTIONARY
05335       rc = false;
05336   }
05337   return rc;
05338 }
05339 
05340 bool ON_BinaryArchive::BeginWriteDictionaryEntry(
05341         int de_type, 
05342         const wchar_t* entry_name
05343         )
05344 {
05345 #if defined(ON_COMPILER_MSC)
05346 // Disable the MSC /W4 "conditional expression is constant" warning
05347 // about sizeof(unsigned short) == sizeof(*entry_name).  
05348 // Since this code has to run on machines where sizeof(wchar_t) 
05349 // can be 2, 4, or 8 bytes, the test is necessary.
05350 #pragma warning( push )
05351 #pragma warning( disable : 4127 )
05352 #endif
05353 
05354   bool rc = BeginWrite3dmChunk(TCODE_DICTIONARY_ENTRY,0);
05355   if ( rc )
05356   {
05357     for(;;)
05358     {
05359       rc = WriteInt(de_type);
05360       if (!rc) break;
05361       if ( sizeof(unsigned short) == sizeof(*entry_name) )
05362       {
05363         rc = WriteString((const unsigned short*)entry_name);
05364       }
05365       else
05366       {
05367         ON_wString s(entry_name);
05368         rc = WriteString(s);
05369       }
05370       if (!rc) break;
05371       break;
05372     }
05373     if ( !rc )
05374       EndWrite3dmChunk(); // TCODE_DICTIONARY_ENTRY
05375   }
05376   return rc;
05377 
05378 #if defined(ON_COMPILER_MSC)
05379 #pragma warning( pop )
05380 #endif
05381 }
05382 
05383 bool ON_BinaryArchive::EndWriteDictionaryEntry()
05384 {
05385   int chunk_count = m_chunk.Count();
05386   bool rc = ( chunk_count > 0 && TCODE_DICTIONARY_ENTRY == m_chunk[chunk_count-1].m_typecode )
05387           ? EndWrite3dmChunk()
05388           : false;
05389   return rc;
05390 }
05391 
05392 bool ON_BinaryArchive::BeginReadDictionary(
05393         ON_UUID* dictionary_id,
05394         unsigned int* version,
05395         ON_wString& dictionary_name
05396         )
05397 {
05398   int major_version = 0;
05399   int minor_version = 0;
05400   bool rc = BeginRead3dmChunk(TCODE_DICTIONARY,&major_version,&minor_version);
05401   if ( rc )
05402   {
05403     for(;;)
05404     {
05405       rc = (1 == major_version);
05406       if (!rc) break;
05407 
05408       // Read dictionary id chunk
05409       rc = BeginRead3dmChunk(TCODE_DICTIONARY_ID,&major_version,&minor_version);
05410       if ( !rc ) break;
05411       for(;;)
05412       {
05413         rc = (1==major_version);
05414         if (!rc) break;
05415         ON_UUID id;
05416         rc = ReadUuid(id);
05417         if (!rc) break;
05418         if ( dictionary_id )
05419           *dictionary_id = id;
05420         rc = ReadInt(version);
05421         if (!rc) break;
05422         rc = ReadString(dictionary_name);
05423         if (!rc) break;
05424         break;
05425       }
05426       if ( !EndRead3dmChunk() ) // TCODE_DICTIONARY_ID end
05427         rc = false;
05428       break;
05429     }
05430 
05431     if ( !rc )
05432       EndRead3dmChunk(); // TCODE_DICTIONARY end
05433   }
05434   return rc;
05435 }
05436 
05437 bool ON_BinaryArchive::EndReadDictionary()
05438 {
05439   int chunk_count = m_chunk.Count();
05440   bool rc = ( chunk_count > 0 && TCODE_DICTIONARY == m_chunk[chunk_count-1].m_typecode )
05441           ? EndRead3dmChunk()
05442           : false;
05443   return rc;
05444 }
05445 
05446 int ON_BinaryArchive::BeginReadDictionaryEntry(
05447         int* de_type, 
05448         ON_wString& entry_name
05449         )
05450 {
05451   unsigned int tcode = 0;
05452   ON__INT64 chunk_length = 0;
05453   int chunk_count = m_chunk.Count();
05454   int rc = ( chunk_count > 0 && TCODE_DICTIONARY == m_chunk[chunk_count-1].m_typecode )
05455           ? (BeginRead3dmBigChunk(&tcode,&chunk_length)?1:0)
05456           : 0;
05457   if ( de_type )
05458     *de_type = 0;
05459   if ( rc )
05460   {
05461     if ( TCODE_DICTIONARY_ENTRY == tcode )
05462     {
05463       for(;;)
05464       {
05465         rc = 0;
05466         if ( !ReadInt(de_type) )
05467         {
05468           entry_name.Empty();
05469           break;
05470         }
05471         if ( !ReadString(entry_name) )
05472         {
05473           entry_name.Empty();
05474           break;
05475         }
05476         rc = 1;
05477         break;
05478       }
05479     }
05480     else
05481     {
05482       rc = (TCODE_DICTIONARY_END == tcode) ? 2 : 0;
05483     }
05484     if ( 1 != rc )
05485     {
05486       if ( !EndRead3dmChunk() )
05487         rc = 0;
05488     }
05489   }
05490   return rc;
05491 }
05492 
05493 bool ON_BinaryArchive::EndReadDictionaryEntry()
05494 {
05495   int chunk_count = m_chunk.Count();
05496   bool rc = ( chunk_count > 0 && TCODE_DICTIONARY_ENTRY == m_chunk[chunk_count-1].m_typecode )
05497           ? EndRead3dmChunk()
05498           : false;
05499   return rc;
05500 }
05501 
05502 
05503 
05504 bool ON_BinaryArchive::Read3dmGoo( ON_3dmGoo& goo )
05505 {
05506   // goo is an entire "chunk" that is not short.
05507   // A call to EndRead3dmChunk() must immediately follow 
05508   // the call to Read3dmGoo().
05509   bool rc = false;
05510   if (goo.m_goo) 
05511   {
05512     onfree(goo.m_goo);
05513     goo.m_goo = 0;
05514   }
05515   goo.m_typecode = 0;
05516   goo.m_value = 0;
05517   ON_3DM_BIG_CHUNK* c = m_chunk.Last();
05518   if (c) 
05519   {
05520     goo.m_typecode = c->m_typecode;
05521     if ( c->m_bLongChunk )
05522       rc = DownSizeUINT(c->Length(),(ON__UINT32*)&goo.m_value);
05523     else
05524       rc = DownSizeINT(c->m_big_value,&goo.m_value);
05525     if ( rc && c->m_bLongChunk && c->m_big_value > 0 ) 
05526     {
05527       if ( CurrentPosition() == c->m_big_offset )
05528       {
05529         // read the rest of this chunk into the goo.m_goo buffer.
05530         // 23 January 2004 Dale Lear:
05531         //   Have to turn of CRC checking because the goo may contiain
05532         //   subchunks.  If a CRC exixts, it is at the end of the
05533         //   goo and will persist until the application that
05534         //   wrote this chunk is available to parse the chunk.
05535         c->m_do_crc16 = 0;
05536         c->m_do_crc32 = 0;
05537         m_bDoChunkCRC = false;
05538         size_t sizeof_goo = (size_t)c->Length();
05539         goo.m_goo = (unsigned char*)onmalloc( sizeof_goo );
05540         rc = ReadByte( sizeof_goo, goo.m_goo );
05541       }
05542     }
05543   }
05544   return rc;
05545 }
05546 
05547 bool ON_BinaryArchive::WriteChunkTypecode( ON__UINT32 typecode )
05548 {
05549   return WriteInt32(1,(ON__INT32*)&typecode);
05550 }
05551 
05552 bool ON_BinaryArchive::ReadChunkTypecode( ON__UINT32* typecode )
05553 {
05554   ON__UINT32 tc = 0;
05555   bool rc = ReadInt32(1,(ON__INT32*)&tc);
05556   if (rc && typecode )
05557     *typecode = tc;
05558   return rc;
05559 }
05560 
05561 bool ON_BinaryArchive::WriteChunkValue( ON__UINT32 typecode, ON__INT64 big_value )
05562 {
05563   bool rc;
05564   if ( 8 == SizeofChunkLength() )
05565   {
05566     rc = WriteInt64(1,&big_value);
05567   }
05568   else if ( ON_IsUnsignedChunkTypecode(typecode) )
05569   {
05570     // treat big_value as an unsigned int
05571     ON__UINT32 u32 = 0;
05572     rc = DownSizeUINT((ON__UINT64)big_value,&u32);
05573     if ( !WriteInt32(1,(ON__INT32*)&u32) )
05574       rc = false;
05575   }
05576   else
05577   {
05578     // treat big_value as a signed int
05579     ON__INT32 v32 = 0;
05580     rc = DownSizeINT(big_value,&v32);
05581     if ( !WriteInt32(1,&v32) )
05582       rc = false;
05583   }
05584   return rc;
05585 }
05586 
05587 
05588 bool ON_BinaryArchive::WriteChunkLength( ON__UINT64 length )
05589 {
05590   bool rc;
05591   if ( 8 == SizeofChunkLength() )
05592   {
05593     rc = WriteInt64(1,(ON__INT64*)&length);
05594   }
05595   else
05596   {
05597     ON__UINT32 u32 = 0;
05598     rc = DownSizeUINT(length,&u32);
05599     if ( !WriteInt32(1,(ON__INT32*)&u32) )
05600       rc = false;
05601   }
05602   return rc;
05603 }
05604 
05605 bool ON_BinaryArchive::ReadEOFSizeOfFile( ON__UINT64* sizeof_file )
05606 {
05607   bool rc;
05608   ON__INT64 u64 = 0;
05609   if ( 8 == SizeofChunkLength() )
05610   {
05611     // file has a 8 byte file size
05612     rc = ReadInt64(1,(ON__INT64*)&u64);
05613   }
05614   else
05615   {
05616     // file has a 4 byte file size
05617     ON__UINT32 u32 = 0;
05618     rc = ReadInt32(1,(ON__INT32*)&u32);
05619     if ( rc )
05620       u64 = u32;
05621   }
05622   if ( rc && 0 != sizeof_file )
05623     *sizeof_file = u64;
05624   return rc;
05625 }
05626 
05627 bool ON_BinaryArchive::WriteEOFSizeOfFile( ON__UINT64 sizeof_file )
05628 {
05629   bool rc;
05630   if ( 8 == SizeofChunkLength() )
05631   {
05632     // file has a 8 byte file size
05633     rc = WriteInt64(1,(ON__INT64*)&sizeof_file);
05634   }
05635   else
05636   {
05637     // file has a 4 byte file size
05638     ON__UINT32 u32=0;
05639     DownSizeUINT(sizeof_file,&u32);
05640     rc = WriteInt32(1,(ON__INT32*)&u32);
05641   }
05642   return rc;
05643 }
05644 
05645 bool ON_BinaryArchive::ReadChunkValue( ON__UINT32 typecode, ON__INT64* value64 )
05646 {
05647   bool rc;
05648   ON__INT64 i64 = 0;
05649   if ( 8 == SizeofChunkLength() )
05650   {
05651     // file has a 8 byte chunk value
05652     rc = ReadInt64(1,&i64);
05653   }
05654   else
05655   {
05656     // file has a 4 byte chunk value
05657     if ( ON_IsUnsignedChunkTypecode(typecode) )
05658     {
05659       // This Mickey Mouse is here to convince all compilers 
05660       // that when we read a 4 byte value with the high bit set, 
05661       // the resulting i64 value is positive. I.e.,
05662       // 0xFFFFFFFF is converted to 0x00000000FFFFFFFF
05663       ON__UINT32 u32 = 0;
05664       ON__UINT64 u64 = 0;
05665       rc = ReadInt32(1,(ON__INT32*)&u32);
05666       if ( rc )
05667         u64 = u32;
05668       i64 = (ON__INT64)u64;
05669     }
05670     else
05671     {
05672       // If we read a 4 byte value with the high bit set,
05673       // the resulting i64 value is negative. I.e.,
05674       // -1 is converted to -1 (0xFFFFFFFF to 0xFFFFFFFFFFFFFFFF)
05675       ON__INT32 i32 = 0;
05676       rc = ReadInt32(1,&i32);
05677       i64 = i32;
05678     }
05679   }
05680   if ( rc && 0 != value64 )
05681     *value64 = i64;
05682   return rc;
05683 }
05684 
05685 size_t ON_BinaryArchive::SizeofChunkLength() const
05686 {
05687   // Version 1 - 4 and early version 5 files had
05688   // 4 byte chunk lengths.  In October of 2009,
05689   // 8 byte chunk lengths were phased in for V5
05690   // files.
05691   return (m_3dm_version < 50) ? 4 : 8;
05692 }
05693 
05694 bool ON_BinaryArchive::PushBigChunk( ON__UINT32 typecode, ON__INT64 big_value )
05695 {
05696   ON_3DM_BIG_CHUNK c;
05697   memset(&c,0,sizeof(c));
05698   c.m_typecode  = typecode;
05699   c.m_big_value = big_value;
05700 
05701   // | and & are BITOPS - do NOT change to || and &&
05702   //
05703   // Some v1 files have a short chunk with typecode = 0.
05704   if ( 0 == ( TCODE_SHORT & typecode ) && (0 != typecode  || 1 != Archive3dmVersion()) ) 
05705   {
05706     if ( m_3dm_version == 1 && 0 != (TCODE_LEGACY_GEOMETRY & typecode) )
05707     {
05708       // these legacy typecodes have 16 bit CRCs
05709       c.m_do_crc16 = 1;
05710       c.m_crc16 = 1;
05711     }
05712     else 
05713     {
05714       // some other legacy typecodes that have 16 bit CRCs
05715       switch(typecode) 
05716       {
05717 
05718       case TCODE_SUMMARY:
05719         if ( m_3dm_version == 1 ) 
05720         {
05721           c.m_do_crc16 = 1;
05722           c.m_crc16 = 1;
05723         }
05724         break;
05725 
05726       case TCODE_OPENNURBS_OBJECT | TCODE_CRC | 0x7FFD:
05727         if ( m_3dm_version == 1 ) 
05728         {
05729           // 1.1 uuid has a 16 bit crc
05730           c.m_do_crc16 = 1;
05731           c.m_crc16 = 1;
05732         }
05733         else 
05734         {
05735           // 2.0 uuid has a 32 bit crc
05736           c.m_do_crc32 = 1;
05737           c.m_crc32 = 0;
05738         }
05739         break;
05740 
05741       default:
05742         if ( m_3dm_version != 1 && 0 != (TCODE_CRC & typecode) ) 
05743         {
05744           // 32 bit CRC
05745           c.m_do_crc32 = 1;
05746           c.m_crc32 = 0;
05747         }
05748         break;
05749 
05750       }
05751     }
05752     c.m_bLongChunk = 1;
05753   }
05754   c.m_big_offset = CurrentPosition();
05755   m_bDoChunkCRC = c.m_do_crc16 || c.m_do_crc32;
05756   
05757   if ( m_chunk.Capacity() == 0 )
05758     m_chunk.Reserve(128);
05759   m_chunk.Append( c );
05760 
05761   return true;
05762 }
05763 
05764 bool ON_BinaryArchive::EnableSave3dmRenderMeshes( ON_BOOL32  b /* default = true */ )
05765 {
05766   bool oldb = m_bSaveRenderMeshes;
05767   m_bSaveRenderMeshes = b?true:false;
05768   return oldb;  
05769 }
05770 
05771 bool ON_BinaryArchive::Save3dmRenderMeshes() const
05772 {
05773   return m_bSaveRenderMeshes;
05774 }
05775 
05776 bool ON_BinaryArchive::EnableSave3dmAnalysisMeshes( ON_BOOL32  b /* default = true */ )
05777 {
05778   bool oldb = m_bSaveAnalysisMeshes;
05779   m_bSaveAnalysisMeshes = b?true:false;
05780   return oldb;  
05781 }
05782 
05783 bool ON_BinaryArchive::Save3dmAnalysisMeshes() const
05784 {
05785   return m_bSaveAnalysisMeshes;
05786 }
05787 
05788 bool ON_BinaryArchive::EnableSaveUserData( ON_BOOL32 b )
05789 {
05790   bool b0 = m_bSaveUserData;
05791   m_bSaveUserData = b?true:false;
05792   return b0;
05793 }
05794 
05795 bool ON_BinaryArchive::SaveUserData() const
05796 {
05797   return m_bSaveUserData;
05798 }
05799 
05800 int ON_BinaryArchive::CurrentArchiveVersion()
05801 {
05802   // Latest version of opennurbs binary archives supported by
05803   // this version of opennurbs.
05804   return 50; // Rhino 5.0 files with 8 byte chunk lengths.
05805 }
05806 
05807 bool ON_BinaryArchive::Write3dmStartSection( int version, const char* sInformation )
05808 {
05809   if ( 0 == version )
05810     version = ON_BinaryArchive::CurrentArchiveVersion();
05811 
05812   // 2009 November 6 
05813   // Default 3dm files now have 8 byte chunk lengths.
05814   // 3dm archive version numbers >= 50 indicate the file has 8 byte chunk lengths.
05815   // Rather than change the hundreds of places in Rhino that use 5, 6, 7 ...
05816   // the "version *= 10" line handles it.  
05817   if ( version >= 5 && version < 50 )
05818     version *= 10;
05819 
05820   if ( version > ON_BinaryArchive::CurrentArchiveVersion() )
05821   {
05822     ON_ERROR("3dm archive version must be <= ON_BinaryArchive::CurrentArchiveVersion() ");
05823     return false;
05824   }
05825 
05826   if (    version < 2 
05827        || (version >= 5 && version < 50) 
05828        || (version >= 50 && 0 != (version % 10)) 
05829        )
05830   {
05831     ON_ERROR("3dm archive version must be 2, 3, 4 or 50");
05832     return false;
05833   }
05834 
05835   m_bad_CRC_count = 0;
05836   m_3dm_version = 0;
05837   m_3dm_opennurbs_version = ON::Version();
05838   m_3dm_version = version;
05839 
05840   char sVersion[64];
05841   memset( sVersion, 0, sizeof(sVersion) );
05842   if ( version < 1 )
05843     version = ON_BinaryArchive::CurrentArchiveVersion();
05844   sprintf(sVersion,"3D Geometry File Format %8d",version);
05845   bool rc = WriteByte( 32, sVersion );
05846   if ( rc )
05847     rc = BeginWrite3dmBigChunk( TCODE_COMMENTBLOCK, 0 );
05848   if ( rc ) {
05849     if ( sInformation && sInformation[0] ) {
05850       rc = WriteByte( strlen(sInformation), sInformation );
05851     }
05852     if ( rc ) {
05853       // write information that helps determine what code wrote the 3dm file
05854       char s[2048];
05855       size_t s_len = 0;
05856       memset( s, 0, sizeof(s) );
05857       sprintf(s," 3DM I/O processor: OpenNURBS toolkit version %d",ON::Version());
05858       strcat(s," (compiled on ");
05859       strcat(s,__DATE__);
05860       strcat(s,")\n");
05861       s_len = strlen(s);
05862       s[s_len++] = 26; // ^Z
05863       s[s_len++] = 0;
05864       rc = WriteByte( s_len, s );
05865     }
05866     if ( !EndWrite3dmChunk() ) // need to call EndWrite3dmChunk() even if a WriteByte() has failed
05867       rc = false;
05868   }
05869   return rc;
05870 }
05871 
05872 bool ON_BinaryArchive::Read3dmStartSection( int* version, ON_String& s )
05873 {
05874   // The first 24 bytes of a 3dm file must be "3D Geometry File Format "
05875   // The next 8 bytes must be a right justified ASCII integer >= 1.  For
05876   // example, prior to March 2000, all 3DM files were version 1 files and
05877   // they began with "3D Geometry File Format        1".  At the time of
05878   // this writing (May 2011) there are version 1,2,3,4,5 and 50 files.
05879   //
05880   // The next section must contain a long chunk with typecode 1.
05881   m_bad_CRC_count = 0;
05882   m_3dm_version = 0;
05883 
05884   // In Read3dmProperties, m_3dm_opennurbs_version will be set to 
05885   // the version of OpenNURBS that was used to write this archive.
05886   m_3dm_opennurbs_version = 0;
05887 
05888   unsigned int typecode = 0;
05889   ON__INT64 length = -1;
05890   int ver = m_3dm_version;
05891   if ( version )
05892     *version = m_3dm_version;
05893   s.Destroy();
05894   char s3d[33];
05895   memset( s3d, 0, sizeof(s3d) );
05896   bool rc = ReadByte( 32, s3d );
05897   if ( rc ) 
05898   {
05899 
05900     if ( strncmp( s3d, "3D Geometry File Format ", 24) ) 
05901     {
05902       // it's not a "pure" .3DM file 
05903       // - see if we have a .3DM file with MS OLE-goo at the start
05904       // (generally, there is around 6kb of goo.  I keep looking
05905       // for up to 32mb just in case.)
05906       rc =  false;
05907       unsigned int n;
05908       int j;
05909       for ( n = 0; n < 33554432 && !rc; n++ ) 
05910       {
05911         for ( j = 0; j < 31; j++ )
05912           s3d[j] = s3d[j+1];
05913         if ( !ReadByte( 1, &s3d[31]) ) 
05914           break;
05915         if ( !strncmp( s3d, "3D Geometry File Format ", 24) ) 
05916         {
05917           m_3dm_start_section_offset = n+1;
05918           rc = true;
05919           break;
05920         }
05921       }
05922     }
05923 
05924     if (rc) {
05925       // get version
05926       //char* sVersion = s3d+24;
05927       // skip leading spaces
05928       int i = 24;
05929       while (i < 32 && s3d[i] == ' ')
05930         i++;
05931       while (i < 32 && rc) {
05932         // TEMPORARY 2 = X
05933         if ( i == 31 && s3d[i] == 'X' ) {
05934           s3d[i] = '2';
05935         }
05936 
05937         if ( s3d[i] < '0' || s3d[i] > '9' ) {
05938           rc = false; // it's not a valid .3DM file version
05939           break;
05940         }
05941         ver = ver*10 + ((int)(s3d[i]-'0'));
05942         i++;
05943       }
05944       if ( rc ) {
05945         m_3dm_version = ver;
05946         if ( version )
05947           *version = ver;
05948       }
05949     }
05950     if ( rc ) {
05951       rc = BeginRead3dmBigChunk( &typecode, &length );
05952       if ( rc ) {
05953         if ( typecode != 1 ) {
05954           rc = false; // it's not a .3DM file
05955         }
05956         else if ( length > 0 ) 
05957         {
05958           if ( length > 0x00FFFFFF )
05959           {
05960             ON_ERROR("ON_BinaryArchive::Read3dmStartSection - start section string is unreasonably long.");
05961             rc = false;
05962           }
05963           else 
05964           {
05965             int slen = (int)length;
05966             s.ReserveArray( slen+1 );
05967             s.SetLength( slen );
05968             s[slen] = 0;
05969             ReadByte( slen, s.Array() );
05970             while ( slen > 0 && (0 == s[slen-1] || 26 == s[slen-1]) )
05971             {
05972               s[slen-1] = 0;
05973               slen--;
05974             }
05975             s.SetLength(slen);
05976           }
05977         }
05978       }
05979       if ( !EndRead3dmChunk() )
05980         rc = false;
05981       if ( rc && m_3dm_version == 1 ) {
05982         // In March 2001, we got reports of files with V1 headers and
05983         // a V2 bodies.  We haven't been able to track down the application
05984         // that is creating these damaged files, but we can detect them
05985         // and read them because they all have a TCODE_PROPERTIES_TABLE
05986         // chunk right after the start comments chunk and no valid V1
05987         // file has a chunk with a TCODE_PROPERTIES_TABLE tcode.
05988         //
05989         // Rhino 1.1 version 31-May-2000 reads 2.0 files as goo.  If a user
05990         // saves this file for some reason (no instances have been reported)
05991         // the resulting file has a V1 header, V1 fluff, and a V2 body.
05992         // This code will cause opennurbs to read the V2 body.
05993         // a file that is different from those describe This code
05994         // detects these files.
05995         {
05996 
05997           const ON__UINT64 pos1 = CurrentPosition();
05998           //int v1_fluff_chunk_count = 0;
05999           bool bCheckChunks = true;
06000 
06002           while(bCheckChunks) {
06003             if ( !PeekAt3dmBigChunkType(&typecode,&length) )
06004               break;
06005             switch(typecode)
06006             {
06007               case TCODE_SUMMARY:
06008               case TCODE_BITMAPPREVIEW:
06009               case TCODE_UNIT_AND_TOLERANCES:
06010               case TCODE_VIEWPORT:
06011               case TCODE_LAYER:
06012               case TCODE_RENDERMESHPARAMS:
06013               case TCODE_CURRENTLAYER:
06014               case TCODE_ANNOTATION_SETTINGS:
06015               case TCODE_NOTES:
06016               case TCODE_NAMED_CPLANE:
06017               case TCODE_NAMED_VIEW:
06018                 // skip potential v1 fluff
06019                 bCheckChunks = BeginRead3dmBigChunk( &typecode, &length );
06020                 if ( bCheckChunks )
06021                   bCheckChunks = EndRead3dmChunk();
06022                 break;
06023 
06024               //case TCODE_PROPERTIES_TABLE:
06025               //case TCODE_SETTINGS_TABLE:
06026               //case TCODE_OBJECT_TABLE:
06027               //case TCODE_BITMAP_TABLE:
06028               //case TCODE_LAYER_TABLE:
06029               //case TCODE_GROUP_TABLE:
06030               //case TCODE_LIGHT_TABLE:
06031               //case TCODE_MATERIAL_TABLE:
06032               //case TCODE_USER_TABLE:
06033               default:
06034                 if ( TCODE_TABLE == (typecode & 0xFFFF0000) ) {
06035                   // Found a V2 table which has to be V1 goo
06036                   ON_WARNING("ON_BinaryArchive::Read3dmStartSection(): Archive has V1 header and V2 body. Continuing to read V2 body.");
06037                   m_3dm_version = 2;
06038                   if ( version )
06039                     *version = 2;
06040                 }
06041                 bCheckChunks = false;
06042                 break;
06043             }
06044           }
06045 
06046           if ( m_3dm_version == 1 ) {
06047             // move archive pointer back to 
06048             ON__UINT64 pos2 = CurrentPosition();
06049             if ( pos2 > pos1 ) 
06050             {
06051               BigSeekBackward(pos2 - pos1);
06052             }
06053           }
06054         }
06055       }
06056     }
06057   }
06058   return rc;
06059 }
06060 
06061 bool ON_BinaryArchive::Write3dmProperties(
06062       const ON_3dmProperties& prop
06063       )
06064 {
06065   bool rc = false;
06066   if ( m_3dm_version == 1 ) 
06067   {
06068     ON_String s;
06069 
06070     rc = true;
06071 
06072     if ( rc && prop.m_RevisionHistory.IsValid() ) 
06073     {
06074       rc = BeginWrite3dmChunk(TCODE_SUMMARY,0);
06075       if (rc)
06076       {
06077         // version 1 revision history chunk
06078         s = prop.m_RevisionHistory.m_sCreatedBy;
06079         if (rc) rc = WriteString(s);
06080         if (rc) rc = WriteTime( prop.m_RevisionHistory.m_create_time );
06081         if (rc) rc = WriteInt(0);
06082         s = prop.m_RevisionHistory.m_sLastEditedBy;
06083         if (rc) rc = WriteString(s);
06084         if (rc) rc = WriteTime( prop.m_RevisionHistory.m_last_edit_time );
06085         if (rc) rc = WriteInt(0);
06086         if (rc) rc = WriteInt( prop.m_RevisionHistory.m_revision_count );
06087         if ( !EndWrite3dmChunk() ) // writes 16 bit crc
06088           rc = false;
06089       }
06090     }
06091 
06092     if ( rc && prop.m_Notes.IsValid() ) 
06093     {
06094       rc = BeginWrite3dmChunk(TCODE_NOTES,0);
06095       // version 1 notes chunk
06096       if ( rc )
06097       {
06098         if ( rc ) rc = WriteInt( prop.m_Notes.m_bVisible );
06099         if ( rc ) rc = WriteInt( prop.m_Notes.m_window_left );
06100         if ( rc ) rc = WriteInt( prop.m_Notes.m_window_top );
06101         if ( rc ) rc = WriteInt( prop.m_Notes.m_window_right );
06102         if ( rc ) rc = WriteInt( prop.m_Notes.m_window_bottom );
06103         s = prop.m_Notes.m_notes;
06104         if ( rc ) rc = WriteString( s );
06105         if ( !EndWrite3dmChunk() )
06106           rc = false;
06107       }
06108     }
06109 
06110     if ( rc && prop.m_PreviewImage.IsValid() )
06111     {
06112       rc = BeginWrite3dmChunk(TCODE_BITMAPPREVIEW,0);
06113       if (rc)
06114       {
06115         // version 1 preview image chunk
06116         prop.m_PreviewImage.Write(*this);
06117         if ( !EndWrite3dmChunk() )
06118           rc = false;
06119       }
06120     }
06121   }
06122   else 
06123   {
06124     // version 2+ file properties chunk
06125     rc = BeginWrite3dmChunk(TCODE_PROPERTIES_TABLE,0);
06126     if ( rc ) {
06127       rc = prop.Write( *this )?true:false;
06128       if ( !EndWrite3dmChunk() )
06129         rc = false;
06130     }
06131   }
06132   return rc;
06133 }
06134 
06135 int on_strnicmp(const char * s1, const char * s2, int n)
06136 {
06137 #if defined(ON_OS_WINDOWS)
06138   //return stricmp(s1,s2,n);
06139   return _strnicmp(s1,s2,n);
06140 #else
06141   return strncasecmp(s1,s2,n);
06142 #endif
06143 }
06144 
06145 bool ON_BinaryArchive::Read3dmProperties( ON_3dmProperties& prop )
06146 {
06147   // In ON_3dmProperties::Read(), m_3dm_opennurbs_version will be 
06148   // set to the version of OpenNURBS that was used to write this archive.
06149   // If the file was written with by a pre 200012210 version of OpenNURBS,
06150   // then m_3dm_opennurbs_version will be zero.
06151   m_3dm_opennurbs_version = 0;
06152 
06153   prop.Default();
06154 
06155   bool rc = true;
06156 
06157   // we need these when reading version 1 files
06158   const size_t pos0 = CurrentPosition();
06159   bool bHaveRevisionHistory = false;
06160   bool bHaveNotes = false;
06161   bool bHavePreviewImage = false;
06162   bool bDone = false;
06163   bool bRewindFilePointer = false;
06164 
06165   ON__UINT32 tcode;
06166   ON__INT64 big_value;
06167   int version = 0;
06168 
06169   if ( m_3dm_version != 1 ) {
06170     for(;;) 
06171     {
06172       tcode = 0;
06173       big_value = 0;
06174       rc = BeginRead3dmBigChunk( &tcode, &big_value );
06175       if ( !rc ) {
06176         bRewindFilePointer = true;
06177         break;
06178       }
06179 
06180       if ( tcode == TCODE_PROPERTIES_TABLE ) {
06181         rc = prop.Read(*this)?true:false;
06182       }
06183       else {
06184         bRewindFilePointer = true;
06185       }
06186 
06187       if ( !EndRead3dmChunk() ) {
06188         rc = false;
06189         bRewindFilePointer = true;
06190       }
06191       if ( tcode == TCODE_PROPERTIES_TABLE || !rc )
06192         break;
06193     }
06194   }
06195   else {
06196     // version 1 file
06197     rc = SeekFromStart(32)?true:false;
06198     bRewindFilePointer = true;
06199     for(;;) 
06200     {
06201       tcode = 0;
06202       big_value = 0;
06203       rc = BeginRead3dmBigChunk( &tcode, &big_value );
06204       if ( !rc ) {
06205         rc = true; // assume we are at the end of the file
06206         bRewindFilePointer = true;
06207         break;
06208       }
06209 
06210       switch ( tcode ) {
06211 
06212       case 1: // comments section has application name
06213         if ( big_value > 1000000 )
06214         {
06215           ON_ERROR("Comment length > 1000000");
06216         }
06217         else if ( big_value > 1 )
06218         {
06219           int slen = (int)big_value;
06220           int i;
06221           char* name = 0;
06222           ON_String s;
06223           s.ReserveArray( slen+1 );
06224           s.SetLength( slen );
06225           s[slen] = 0;
06226           ReadByte( slen, s.Array() );
06227           while ( slen > 0 && (0 == s[slen-1] || 26 == s[slen-1]) ) 
06228           {
06229             s[slen-1] = 0;
06230             slen--;
06231           }
06232           s.SetLength(slen);
06233           name = s.Array();
06234           if ( name ) {
06235             while(*name) {
06236               if ( !on_strnicmp(name,"Interface:",10) ) {
06237                 name += 10;
06238                 break;
06239               }
06240               name++;
06241             }
06242             while(*name && *name <= 32 )
06243               name++;
06244             for ( i = 0; name[i] ; i++ ) {
06245               if ( name[i] == '(' ) {
06246                 name[i] = 0;
06247                 while ( i > 0 && (name[i] <= 32 || name[i] == '-') ) {
06248                   name[i] = 0;
06249                   i--;
06250                 }
06251                 break;
06252               }
06253             }
06254             if ( *name )
06255             {
06256               char* details = 0;
06257               if ( !on_strnicmp(name,"Rhinoceros",10) ) {
06258                 prop.m_Application.m_application_URL = "http://www.rhino3d.com";
06259                 details = name+10;
06260                 while ( *details && *details <= 32 )
06261                   details++;
06262                 while ( (*details >= '0' && *details <= '9') || *details == '.' )
06263                   details++;
06264                 if ( *details && *details <= 32 ) {
06265                   *details = 0;
06266                   details++;
06267                   while ( *details && (*details <= 32 ||*details == '-')) {
06268                     details++;
06269                   }
06270                 }
06271               }
06272               if (*name)
06273                 prop.m_Application.m_application_name = name;
06274               if (details && *details)
06275                 prop.m_Application.m_application_details = details;
06276             }
06277           }
06278         }
06279         break;
06280 
06281       case TCODE_SUMMARY: 
06282         // version 1 revision history chunk (has 16 bit CRC)
06283         version = 1;
06284         bHaveRevisionHistory = true;
06285         {
06286           int slength = 0;
06287           char* s = 0;
06288           if (rc) rc = ReadInt(&slength);
06289           if (rc && slength > 0 ) {
06290             s = (char*)onmalloc((slength+1)*sizeof(*s));
06291             memset( s, 0, (slength+1)*sizeof(*s) );
06292             if (rc) rc = ReadChar( slength, s );
06293             if ( rc )
06294               prop.m_RevisionHistory.m_sCreatedBy = s;
06295             onfree(s);
06296             slength = 0;
06297             s = 0;
06298           }
06299           if (rc) rc = ReadTime( prop.m_RevisionHistory.m_create_time );
06300           int i32 = 0;
06301           if (rc) rc = ReadInt(&i32); // 0 in 1.x files
06302           if (rc) rc = ReadInt(&slength);
06303           if ( rc && slength > 0 ) 
06304           {
06305             s = (char*)onmalloc((slength+1)*sizeof(*s));
06306             memset( s, 0, (slength+1)*sizeof(*s) );
06307             if (rc) rc = ReadChar( slength, s );
06308             if ( rc )
06309               prop.m_RevisionHistory.m_sLastEditedBy = s;
06310             onfree(s);
06311             slength = 0;
06312             s = 0;
06313           }
06314           if (rc) rc = ReadTime( prop.m_RevisionHistory.m_last_edit_time );
06315           if (rc) rc = ReadInt(&i32); // 0 in 1.x files
06316           if (rc) rc = ReadInt( &prop.m_RevisionHistory.m_revision_count );
06317         }
06318         break;
06319 
06320       case TCODE_NOTES: 
06321         // version 1 notes chunk
06322         version = 1;
06323         bHaveNotes = true;
06324         for(;;)
06325         {
06326           int slength;
06327           char* s = 0;
06328           rc = ReadInt( &prop.m_Notes.m_bVisible );
06329           if(!rc) break;
06330           rc = ReadInt( &prop.m_Notes.m_window_left );
06331           if(!rc) break;
06332           rc = ReadInt( &prop.m_Notes.m_window_top );
06333           if(!rc) break;
06334           rc = ReadInt( &prop.m_Notes.m_window_right );
06335           if(!rc) break;
06336           rc = ReadInt( &prop.m_Notes.m_window_bottom );
06337           if(!rc) break;
06338           rc = ReadInt( &slength );
06339           if(!rc) break;
06340           if ( slength > 0 ) 
06341           {
06342             s = (char*)onmalloc( (slength+1)*sizeof(*s) );
06343             memset( s, 0, (slength+1)*sizeof(*s) );
06344             if ( rc ) rc = ReadChar( slength, s );
06345             if ( rc ) 
06346             {
06347               prop.m_Notes.m_notes = s;
06348             }
06349             onfree(s);
06350             slength = 0;
06351             s = 0;
06352           }
06353           break;
06354         }
06355         break;
06356 
06357       case TCODE_BITMAPPREVIEW: 
06358         // version 1 preview image chunk
06359         version = 1;
06360         rc = prop.m_PreviewImage.Read(*this)?true:false;
06361         bHavePreviewImage = rc;
06362         break;
06363 
06364       case TCODE_CURRENTLAYER:
06365       case TCODE_LAYER:
06366         // version 1 layer and current layer chunks always came after notes/bitmap/summary
06367         bDone = true;
06368         bRewindFilePointer = true;
06369         break;
06370 
06371       default:
06372         // the call to EndRead3dmChunk() will skip over this chunk
06373         bRewindFilePointer = true;
06374         break;
06375       }
06376 
06377       // this call to EndRead3dmChunk() skips any unread portions of the chunk
06378       if ( !EndRead3dmChunk() ) {
06379         rc = false;
06380         bRewindFilePointer = true;
06381       }
06382 
06383       if ( bHaveRevisionHistory && bHaveNotes && bHavePreviewImage )
06384         bDone = true;
06385 
06386       if ( bDone || !rc )
06387         break;
06388     }
06389   }
06390 
06391   if ( bRewindFilePointer ) {
06392     // reposition file pointer to pos0
06393     const ON__UINT64 pos1 = CurrentPosition();
06394     if ( pos0 != pos1 ) 
06395     {
06396       if (pos1 > pos0)
06397         BigSeekBackward(pos1-pos0);
06398       else if ( pos1 < pos0 )
06399         BigSeekForward(pos0-pos1);
06400     }
06401   }
06402 
06403   return rc;
06404 }
06405 
06406 bool ON_BinaryArchive::Write3dmSettings(
06407       const ON_3dmSettings& settings
06408       )
06409 {
06410   bool rc = false;
06411   if ( m_3dm_version == 1 ) {
06412     // legacy v1 settings info is a bunch of unreleated top level chunks
06413     rc = settings.Write(*this)?true:false;
06414   }
06415   else 
06416   {
06417     // version 2+ file settings chunk
06418     rc = BeginWrite3dmChunk(TCODE_SETTINGS_TABLE,0);
06419     if ( rc ) {
06420       rc = settings.Write( *this );
06421       if ( !EndWrite3dmChunk() )
06422         rc = false;
06423     }
06424 
06425     if ( rc && 3 == Archive3dmVersion() )
06426     {
06427       // Build a list of ids of plug-ins that support saving
06428       // V3 user data.  If a plug-in id is not in this list,
06429       // the user data will not be saved in the V3 archive.
06430       int i, count = settings.m_plugin_list.Count();
06431       m_V3_plugin_id_list.SetCount(0);
06432       m_V3_plugin_id_list.SetCapacity( count+5 );
06433       for ( i = 0; i < count; i++ )
06434       {
06435         const ON_UUID& pid = settings.m_plugin_list[i].m_plugin_id;
06436         if ( !ON_UuidIsNil(pid) )
06437           m_V3_plugin_id_list.Append(pid);
06438       }
06439 
06440       // These ids insure V3, V4 and V5 core user data will round trip
06441       // through SaveAs V3 and SaveAs V4
06442       m_V3_plugin_id_list.Append( ON_v3_userdata_id );
06443       m_V3_plugin_id_list.Append( ON_v4_userdata_id );
06444       m_V3_plugin_id_list.Append( ON_opennurbs4_id );
06445       m_V3_plugin_id_list.Append( ON_opennurbs5_id );
06446       m_V3_plugin_id_list.Append( ON_rhino3_id );
06447       m_V3_plugin_id_list.Append( ON_rhino4_id );
06448       m_V3_plugin_id_list.Append( ON_rhino5_id );
06449       m_V3_plugin_id_list.QuickSort( ON_UuidCompare );
06450     }
06451   }
06452   return rc;
06453 }
06454 
06455 bool ON_BinaryArchive::Read3dmSettings( ON_3dmSettings& settings )
06456 {
06457   bool rc = false;
06458   ON__UINT32 tcode;
06459   ON__INT64 big_value;
06460 
06461   if ( m_3dm_version == 1 ) {
06462     // read legacy v 1 info that is scattered around the file
06463     rc = settings.Read(*this);
06464   }
06465   else {
06466     rc = true;
06467     while(rc) 
06468     {
06469       tcode = 0;
06470       big_value = 0;
06471       rc = BeginRead3dmBigChunk( &tcode, &big_value );
06472       if ( !rc )
06473         break;
06474       if ( tcode == TCODE_SETTINGS_TABLE ) {
06475         // version 2 model properties
06476         rc = settings.Read(*this);
06477       }
06478       if ( !EndRead3dmChunk() ) {
06479         rc = false;
06480         break;
06481       }
06482       if ( TCODE_SETTINGS_TABLE == tcode )
06483         break;
06484     }
06485   }
06486 
06487   return rc;
06488 }
06489 
06490 ON_BinaryArchive::table_type ON_BinaryArchive::TableTypeFromTypecode( unsigned int typecode )
06491 {
06492   table_type tt = no_active_table;
06493   switch(typecode) 
06494   {
06495   case TCODE_PROPERTIES_TABLE: tt = properties_table; break;
06496   case TCODE_SETTINGS_TABLE:   tt = settings_table; break;
06497   case TCODE_BITMAP_TABLE:     tt = bitmap_table; break;
06498   case TCODE_TEXTURE_MAPPING_TABLE: tt = texture_mapping_table; break;
06499   case TCODE_MATERIAL_TABLE:   tt = material_table; break;
06500   case TCODE_LINETYPE_TABLE:   tt = linetype_table; break;
06501   case TCODE_LAYER_TABLE:      tt = layer_table; break;
06502   case TCODE_LIGHT_TABLE:      tt = light_table; break;
06503   case TCODE_OBJECT_TABLE:     tt = object_table; break;
06504   case TCODE_GROUP_TABLE:      tt = group_table; break;
06505   case TCODE_FONT_TABLE:       tt = font_table; break;
06506   case TCODE_DIMSTYLE_TABLE:   tt = dimstyle_table; break;
06507   case TCODE_HATCHPATTERN_TABLE: tt = hatchpattern_table; break;
06508   case TCODE_INSTANCE_DEFINITION_TABLE: tt = instance_definition_table; break;
06509   case TCODE_HISTORYRECORD_TABLE: tt = historyrecord_table; break;
06510   case TCODE_USER_TABLE:       tt = user_table; break;
06511   }
06512   return tt;
06513 }
06514 
06515 bool ON_BinaryArchive::BeginWrite3dmTable( unsigned int typecode )
06516 {
06517   const table_type tt = TableTypeFromTypecode(typecode);
06518   if (tt == no_active_table) {
06519     ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() bad typecode");
06520     return false;
06521   }
06522   if ( m_active_table != no_active_table ) {
06523     ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() m_active_table != no_active_table");
06524     return false;
06525   }
06526   if ( m_chunk.Count() ) {
06527     ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() m_chunk.Count() > 0");
06528     return false;
06529   }
06530   bool rc = BeginWrite3dmChunk(typecode,0);
06531   if (rc)
06532     m_active_table = tt;
06533   return rc;
06534 }
06535 
06536 bool ON_BinaryArchive::EndWrite3dmTable( unsigned int typecode )
06537 {
06538   const table_type tt = TableTypeFromTypecode(typecode);
06539   if (tt == no_active_table) {
06540     ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() bad typecode");
06541     return false;
06542   }
06543   if ( m_active_table != tt ) {
06544     ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() m_active_table != t");
06545     return false;
06546   }
06547   if ( m_chunk.Count() != 1 ) {
06548     ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() m_chunk.Count() != 1");
06549     return false;
06550   }
06551   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
06552   if ( 0 == c || c->m_typecode != typecode ) {
06553     ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() m_chunk.Last()->typecode != typecode");
06554     return false;
06555   }
06556   bool rc = BeginWrite3dmChunk( TCODE_ENDOFTABLE, 0 );
06557   if (rc) {
06558     if (!EndWrite3dmChunk())
06559       rc = false;
06560   }
06561   if (!EndWrite3dmChunk())
06562     rc = false;
06563   Flush();
06564   m_active_table = no_active_table;
06565   return rc;
06566 }
06567 
06568 bool ON_BinaryArchive::BeginRead3dmTable( unsigned int typecode )
06569 {
06570   bool rc = false;
06571   const table_type tt = TableTypeFromTypecode(typecode);
06572   if (tt == no_active_table) {
06573     ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() bad typecode");
06574     return false;
06575   }
06576   if ( m_active_table != no_active_table ) {
06577     ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() m_active_table != no_active_table");
06578     return false;
06579   }
06580   if ( m_chunk.Count() ) {
06581     ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() m_chunk.Count() > 0");
06582     return false;
06583   }
06584 
06585   if ( m_3dm_version <= 1 ) 
06586   {
06587     if ( TCODE_USER_TABLE == typecode )
06588       return false; // no user tables in V1 files
06589 
06590     // version 1 files had can have chunks in any order.  To read a "table",
06591     // you have to go through the entire list of chunks looking for those
06592     // that belong to a particular table.
06593     rc = SeekFromStart(32)?true:false;
06594     m_active_table = tt;
06595   }
06596   else 
06597   {
06598     if ( TCODE_USER_TABLE == typecode && m_3dm_version <= 2 )
06599       return false; // no user tables in V2 files
06600 
06601     bool bReadTable = true;
06602     ON__UINT32 tcode = !typecode;
06603     ON__INT64 big_value = 0;
06604     rc = PeekAt3dmBigChunkType( &tcode, &big_value );
06605     if ( rc ) {
06606       if ( tcode != typecode ) 
06607       {
06608         if ( typecode == TCODE_USER_TABLE ) 
06609         {
06610           // it's ok to not have user tables
06611           rc = false;
06612           bReadTable = false;
06613         }
06614         else if ( typecode == TCODE_GROUP_TABLE && m_3dm_opennurbs_version < 200012210 ) 
06615         {
06616           // 3DM archives written before version 200012210 and before do not have group tables
06617           rc = true;
06618           m_active_table = tt;
06619           bReadTable = false;
06620         }
06621         else if ( typecode == TCODE_FONT_TABLE && m_3dm_opennurbs_version < 200109180 )
06622         {
06623           // 3DM archives written before version 200109180 and before do not have font tables
06624           rc = true;
06625           m_active_table = tt;
06626           bReadTable = false;
06627         }
06628         else if ( typecode == TCODE_DIMSTYLE_TABLE && m_3dm_opennurbs_version < 200109260 ) 
06629         {
06630           // 3DM archives written before version 200109260 and before do not have dimstyle tables
06631           rc = true;
06632           m_active_table = tt;
06633           bReadTable = false;
06634         }
06635         else if ( typecode == TCODE_INSTANCE_DEFINITION_TABLE && m_3dm_opennurbs_version < 200205110 )
06636         {
06637           // 3DM archives written before version 200205110 and before do not have instance definition tables
06638           rc = true;
06639           m_active_table = tt;
06640           bReadTable = false;
06641         }
06642         else if ( typecode == TCODE_HATCHPATTERN_TABLE && m_3dm_opennurbs_version < 200405030 )
06643         {
06644           // 3DM archives written before version 200405030 and before do not have hatch pattern tables
06645           rc = true;
06646           m_active_table = tt;
06647           bReadTable = false;
06648         }
06649         else if ( typecode == TCODE_LINETYPE_TABLE && m_3dm_opennurbs_version < 200503170 )
06650         {
06651           // 3DM archives written before version 200503170 and before do not have linetype tables
06652           rc = true;
06653           m_active_table = tt;
06654           bReadTable = false;
06655         }
06656         else if ( typecode == TCODE_TEXTURE_MAPPING_TABLE && m_3dm_opennurbs_version < 200511110 )
06657         {
06658           // 3DM archives written before version 200511110 and before do not have texture mapping tables
06659           rc = true;
06660           m_active_table = tt;
06661           bReadTable = false;
06662         }
06663         else if ( typecode == TCODE_HISTORYRECORD_TABLE && m_3dm_opennurbs_version < 200601180 )
06664         {
06665           // 3DM archives written before version 200601180 and before do not have history record tables
06666           rc = true;
06667           m_active_table = tt;
06668           bReadTable = false;
06669         }
06670         else 
06671         {
06672           // A required table is not at the current position in the archive
06673           // see if we can find it someplace else in the archive.  This can
06674           // happen when old code encounters a table that was added later.
06675 
06676           bool bSeekFromStart = true;
06677 
06678           if (   TCODE_HATCHPATTERN_TABLE == tcode
06679               && TCODE_INSTANCE_DEFINITION_TABLE == typecode
06680               && 3 == m_3dm_version 
06681               && 200405190 <= m_3dm_opennurbs_version )
06682           {
06683             // Dale Lear
06684             //   V3 files from 19 may 2004 on contained bogus hatch pattern tables
06685             //   where the instance definition table was supposed to be.
06686             //
06687             // Do not set rc in this code.  The goal of this code is to
06688             // avoid seeking from the start of the file and posting
06689             // an ON_ERROR alert about something we can work around
06690             // and has been fixed in V4.
06691             tcode = 0;
06692             big_value = 0;
06693             if ( BeginRead3dmBigChunk( &tcode, &big_value ) )
06694             {
06695               if ( TCODE_HATCHPATTERN_TABLE == tcode )
06696               {
06697                 bSeekFromStart = false;
06698               }
06699 
06700               if ( !EndRead3dmChunk() )
06701               {
06702                 bSeekFromStart = true;
06703               }
06704               else if ( !bSeekFromStart )
06705               {
06706                 tcode = 0;
06707                 big_value = 0;
06708                 PeekAt3dmBigChunkType( &tcode, &big_value );
06709                 if ( tcode != typecode )
06710                   bSeekFromStart = true;
06711               }
06712             }
06713           }
06714 
06715           if ( bSeekFromStart )
06716           {
06717             ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() - current file position not at start of table - searching");
06718             rc = Seek3dmChunkFromStart( typecode );
06719           }
06720         }
06721       }
06722       if ( rc && bReadTable ) {
06723         tcode = !typecode;
06724         big_value = 0;
06725         rc = BeginRead3dmBigChunk( &tcode, &big_value );
06726         if ( rc && tcode != typecode ) {
06727           ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() - corrupt table - skipping");
06728           rc = false;
06729           EndRead3dmChunk();
06730         }
06731         else if (rc) {
06732           m_active_table = tt;
06733         }
06734       }
06735     }
06736   }
06737 
06738   return rc;
06739 }
06740 
06741 
06742 int ON_BinaryArchive::GetCurrentChunk(ON_3DM_CHUNK& chunk) const
06743 {
06744   ON_3DM_BIG_CHUNK big_chunk;
06745   memset(&chunk,0,sizeof(ON_3DM_CHUNK));
06746   memset(&big_chunk,0,sizeof(big_chunk));
06747   int rc = GetCurrentChunk(big_chunk);
06748   if ( rc > 0 )
06749   {
06750     chunk.m_offset = (size_t)big_chunk.m_big_offset;
06751     chunk.m_typecode = big_chunk.m_typecode;
06752 
06753     ON__INT32 i32 = 0;
06754     if ( ON_IsLongChunkTypecode( big_chunk.m_typecode ) )
06755       DownSizeUINT( (ON__UINT64)big_chunk.m_big_value, (ON__UINT32*)&i32 );
06756     else
06757       DownSizeINT( big_chunk.m_big_value, &i32 );
06758     chunk.m_value = i32;
06759 
06760     chunk.m_do_length = big_chunk.m_bLongChunk ? 1 : 0;
06761     chunk.m_do_crc16 = big_chunk.m_do_crc16 ? 1 : 0;
06762     chunk.m_do_crc32 = big_chunk.m_do_crc32 ? 1 : 0;
06763     chunk.m_crc16 = big_chunk.m_crc16;
06764     chunk.m_crc32 = big_chunk.m_crc32;
06765   }
06766   return rc;
06767 }
06768 
06769 
06770 int ON_BinaryArchive::GetCurrentChunk(ON_3DM_BIG_CHUNK& chunk) const
06771 {
06772   int rc = m_chunk.Count();
06773   if ( rc > 0 )
06774   {
06775     chunk = m_chunk[rc-1];
06776   }
06777   else
06778   {
06779     memset(&chunk,0,sizeof(ON_3DM_BIG_CHUNK));
06780   }
06781   return rc;
06782 }
06783 
06784 static
06785 const unsigned char*  BufferToUINT16(
06786           bool bReverseByteOrder, 
06787           const unsigned char* buffer,
06788           const unsigned char* buffer_max,
06789           ON__UINT16* u16 )
06790 {
06791   if ( buffer >= buffer_max || buffer_max - buffer < 2 )
06792     return 0;
06793   if ( u16 )
06794   {
06795     unsigned char* dst = (unsigned char*)u16;
06796     if ( bReverseByteOrder )
06797     {
06798       dst[0] = buffer[1];
06799       dst[1] = buffer[0];
06800     }
06801     else
06802     {
06803       dst[0] = buffer[0];
06804       dst[1] = buffer[1];
06805     }
06806   }
06807   return buffer+2;
06808 }
06809 
06810 static
06811 const unsigned char* BufferToUINT32(
06812           bool bReverseByteOrder,
06813           const unsigned char* buffer, 
06814           const unsigned char* buffer_end,
06815           ON__UINT32* u32 )
06816 {
06817   if ( buffer >= buffer_end || buffer_end - buffer < 4 )
06818     return 0;
06819   if ( u32 )
06820   {
06821     unsigned char* dst = (unsigned char*)u32;
06822     if ( bReverseByteOrder )
06823     {
06824       dst[0] = buffer[3];
06825       dst[1] = buffer[2];
06826       dst[2] = buffer[1];
06827       dst[3] = buffer[0];
06828     }
06829     else
06830     {
06831       dst[0] = buffer[0];
06832       dst[1] = buffer[1];
06833       dst[2] = buffer[2];
06834       dst[3] = buffer[3];
06835     }
06836   }
06837   return buffer+4;
06838 }
06839 
06840 static
06841 const unsigned char* BufferToINT64(
06842           bool bReverseByteOrder, 
06843           const unsigned char* buffer, 
06844           const unsigned char* buffer_end,
06845           ON__INT64* i64 )
06846 {
06847   if ( buffer >= buffer_end || buffer_end - buffer < 8 )
06848     return 0;
06849   if ( i64 )
06850   {
06851     unsigned char* dst = (unsigned char*)i64;
06852     if ( bReverseByteOrder )
06853     {
06854       dst[0] = buffer[7];
06855       dst[1] = buffer[6];
06856       dst[2] = buffer[5];
06857       dst[3] = buffer[4];
06858       dst[4] = buffer[3];
06859       dst[5] = buffer[2];
06860       dst[6] = buffer[1];
06861       dst[7] = buffer[0];
06862     }
06863     else
06864     {
06865       dst[0] = buffer[0];
06866       dst[1] = buffer[1];
06867       dst[2] = buffer[2];
06868       dst[3] = buffer[3];
06869       dst[4] = buffer[4];
06870       dst[5] = buffer[5];
06871       dst[6] = buffer[6];
06872       dst[7] = buffer[7];
06873     }
06874   }
06875   return buffer+8;
06876 }
06877 
06878 static
06879 const unsigned char* BufferValidateTcode( 
06880           bool bReverseByteOrder,
06881           const unsigned char* buffer, 
06882           const unsigned char* buffer_end,
06883           ON__UINT32 expected_tcode )
06884 {
06885   ON__UINT32 tc = expected_tcode ? 0 : 1;
06886   buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&tc);
06887   return ( 0 != buffer && tc == expected_tcode ) ? buffer : 0;
06888 }
06889 
06890 static 
06891 const unsigned char* BufferToChunkValue( 
06892           bool bReverseByteOrder,
06893           size_t sizeof_chunk_value, 
06894           const unsigned char* buffer, 
06895           const unsigned char* buffer_end,
06896           ON__INT64* chunk_value )
06897 {
06898   if ( 8 == sizeof_chunk_value )
06899   {
06900     buffer = BufferToINT64(bReverseByteOrder,buffer,buffer_end,chunk_value);
06901   }
06902   else
06903   {
06904     ON__UINT32 u32;
06905     ON__UINT64 u64;
06906     buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&u32);
06907     if ( buffer && chunk_value )
06908     {
06909       // this u64 = u32 is here so 4 byte unsigned ints with the high
06910       // bit set are converted to positive 8 bytes ints.
06911       u64 = u32; 
06912       *chunk_value = (ON__INT64)u64;
06913     }
06914   }
06915   return buffer;
06916 }
06917 
06918 static 
06919 const unsigned char* BufferToUuid(
06920           bool bReverseByteOrder, 
06921           const unsigned char* buffer,
06922           const unsigned char* buffer_end,
06923           ON_UUID& uuid )
06924 {
06925   ON__UINT32 data1=0;
06926   ON__UINT16 data2=0, data3=0;
06927   if ( buffer >= buffer_end || buffer_end - buffer < 16 )
06928     return 0;
06929   buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&data1);
06930   if (buffer)
06931     buffer = BufferToUINT16(bReverseByteOrder,buffer,buffer_end,&data2);
06932   if (buffer)
06933     buffer = BufferToUINT16(bReverseByteOrder,buffer,buffer_end,&data3);
06934   if (buffer)
06935   {
06936     if ( buffer >= buffer_end || buffer_end - buffer < 8 )
06937       buffer = 0;
06938     else
06939     {
06940       uuid.Data1 = data1;
06941       uuid.Data2 = data2;
06942       uuid.Data3 = data3;
06943       memcpy(&uuid.Data4,buffer,8);
06944       buffer += 8;
06945     }
06946   }
06947   return buffer;
06948 }
06949 
06950 static
06951 const unsigned char* EmergencyFindTable_UuidHelper( 
06952           bool bReverseByteOrder,
06953           size_t sizeof_chunk_value, 
06954           const unsigned char* buffer,
06955           const unsigned char* buffer_end,
06956           const ON__UINT32 expected_tcode,
06957           const ON_UUID* expected_uuid
06958           )
06959 {
06960   ON__INT64 chunk_value;
06961   ON__UINT32 c, cc;
06962   ON_UUID uuid;
06963 
06964   // make sure the value at the start of the buffer = expected_tcode.
06965   buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,expected_tcode);
06966   if ( 0 == buffer )
06967     return 0;
06968 
06969   // get length of this chunk containing the uuid
06970   chunk_value = -1;
06971   buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&chunk_value );
06972   if ( 0 == buffer || chunk_value < 0 )
06973     return 0;
06974 
06975   // determine how long the chunk is supposed to be and validate the length.
06976   //
06977   // The "16+4+sizeof_chunk_value+21+4+4" in the bLookForUserTableRecordHeader
06978   // breaks down as:
06979   //  16                       sizeof(uuid)
06980   //  +4                     + sizeof(TCODE_USER_TABLE_RECORD_HEADER chunk typecode)
06981   //  +sizeof_chunk_value    + sizeof(TCODE_USER_TABLE_RECORD_HEADER chunk length)
06982   //  +21                    + major ver, minor ver, bool, archive ver, 3dm ver
06983   //  +4                     + sizeof(TCODE_USER_TABLE_RECORD_HEADER chunk crc)
06984   //  +4                     + sizeof(TCODE_USER_TABLE_UUID chunk crc)
06985   const bool bLookForUserTableRecordHeader = (TCODE_USER_TABLE_UUID == expected_tcode
06986                                               && ((ON__UINT64)chunk_value) >= (16+4+sizeof_chunk_value+21+4+4)
06987                                              );
06988   if ( !bLookForUserTableRecordHeader && 20 != chunk_value )
06989     return 0;
06990   buffer = BufferToUuid(bReverseByteOrder,buffer,buffer_end,uuid);
06991   if ( 0 == buffer )
06992     return 0;
06993   if( 0 != expected_uuid && uuid != *expected_uuid )
06994     return 0;
06995   
06996   if ( bLookForUserTableRecordHeader )
06997   {
06998     // make sure there is a TCODE_USER_TABLE_RECORD_HEADER chunk and skip over it.
06999     buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_USER_TABLE_RECORD_HEADER);
07000     if ( 0 == buffer )
07001       return 0;
07002     // get length of the TCODE_USER_TABLE_RECORD_HEADER chunk
07003     ON__INT64 header_length = -1;
07004     buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&header_length );
07005     if ( 0 == buffer )
07006       return 0;
07007     if ( header_length < 25 )
07008       return 0;
07009     if ( buffer >= buffer_end || buffer_end - buffer < header_length )
07010       return 0;
07011     buffer += header_length;
07012   }
07013 
07014   buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&c);
07015   if ( 0 == buffer )
07016     return 0;
07017   cc = ON_CRC32(0,4,&uuid.Data1);
07018   cc = ON_CRC32(cc,2,&uuid.Data2);
07019   cc = ON_CRC32(cc,2,&uuid.Data3);
07020   cc = ON_CRC32(cc,8,&uuid.Data4[0]);
07021   if ( c != cc )
07022     return 0;
07023 
07024   return buffer;
07025 }
07026 
07027 
07028 bool ON_BinaryArchive::FindMisplacedTable( 
07029         ON__UINT64 filelength,
07030         const ON__UINT32 table_tcode,
07031         const ON__UINT32 table_record_tcode,
07032         const ON_UUID class_uuid,
07033         const ON__UINT64 min_length_data
07034         )
07035 {
07036   bool rc = false;
07037   unsigned char buffer2048[2048];
07038   const ON__UINT64 pos0 = CurrentPosition();
07039   if ( filelength > 0 && pos0 >= filelength )
07040     return false;
07041 
07042   ON__UINT32 tcode;
07043   ON__INT64 i64;
07044 
07045   const bool bReverseByteOrder = (ON::big_endian == Endian());
07046   const size_t sizeof_chunk_typecode = 4;
07047   const size_t sizeof_chunk_value = SizeofChunkLength();
07048   const size_t sizeof_chunk_header = sizeof_chunk_typecode + sizeof_chunk_value;
07049   size_t length_of_user_uuid_and_header = 0;
07050   const bool bFindObjectTable    = (  TCODE_OBJECT_TABLE == table_tcode
07051                                    && TCODE_OBJECT_RECORD == table_record_tcode );
07052   const bool bFindUserTable    = (    TCODE_USER_TABLE == table_tcode
07053                                    && TCODE_USER_RECORD == table_record_tcode );
07054 
07055   if ( TCODE_USER_TABLE == table_tcode && !bFindUserTable )
07056     return false;
07057   if ( TCODE_OBJECT_TABLE == table_tcode && !bFindObjectTable )
07058     return false;
07059   if ( bFindUserTable && ON_UuidIsNil(class_uuid) )
07060   {
07061     // must provide plug-in id when searching for user tables
07062     ON_ERROR("ON_BinaryArchive::FindMisplacedTable - must provide plug-in id when searching for user tables");
07063     return false;
07064   }
07065 
07066   if ( !SeekFromStart(0) )
07067     return false;
07068 
07069   ON__UINT64 pos1 = CurrentPosition();
07070   ON__UINT64 pos;
07071   ON__UINT64 empty_table_pos = 0;    // position of first empty table candidate
07072   int        empty_table_status = 0; // 1 = found a candidate for an empty table
07073                                      // 2 = found 2 or more candidates
07074 
07075   const size_t sizeof_buffer2048 = sizeof(buffer2048);
07076   bool bAtEOF = false;
07077 
07078   while(!bAtEOF)
07079   {
07080     pos = CurrentPosition();
07081     if ( pos < pos1 )
07082     {
07083       break;
07084     }
07085     else if ( pos > pos1 )
07086     {
07087       if ( !BigSeekBackward(pos - pos1) )
07088         break;
07089       if ( pos1 != CurrentPosition() )
07090         break;
07091     }
07092 
07093     memset(buffer2048,0,sizeof_buffer2048);
07094     // Depending on the table and the version of the file, less than 
07095     // sizeof_buffer128 bytes may be read.  Setting bit 0x04 of
07096     // m_error_message_mask disables calls to ON_Error when we
07097     // attempt to read beyond the end of file.
07098     const unsigned int saved_error_message_mask = m_error_message_mask;
07099     m_error_message_mask |= 0x04;
07100     const size_t sizeof_read = Read(sizeof_buffer2048,buffer2048);
07101     m_error_message_mask = saved_error_message_mask;
07102     if ( sizeof_read < sizeof_buffer2048 )
07103     {
07104       // we need to parse what was read, but there's nothing after this.
07105       bAtEOF = true;
07106     }
07107     if ( sizeof_read < 2*sizeof_chunk_header || sizeof_read > sizeof_buffer2048 )
07108       break;
07109     const unsigned char* buffer_end = (&buffer2048[0]) + sizeof_read;
07110     const unsigned char* buffer = buffer2048;
07111 
07112     pos1++;
07113 
07114     // "read" 4 byte tcode
07115     tcode = !table_tcode;
07116     buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&tcode);
07117     if ( 0 == buffer )
07118       break;
07119 
07120     if ( table_tcode != tcode )
07121     {
07122       // This for loop looks through the buffer we just
07123       // read to reduce the amount of times we seek backwards
07124       // and re-read.
07125       for ( size_t i = 1; i <= sizeof_read - sizeof_chunk_typecode; i++ )
07126       {
07127         tcode = !table_tcode;
07128         buffer = BufferToUINT32(bReverseByteOrder,&buffer2048[i],buffer_end,&tcode);
07129         if ( 0 == buffer || table_tcode == tcode )
07130         {
07131           if ( bAtEOF && sizeof_read > 0 && 0 != buffer && table_tcode == tcode )
07132           {
07133             // this table starts within sizeof_buffer2048 bytes of the eof.
07134             bAtEOF = false;
07135           }
07136           break;
07137         }
07138         pos1++;
07139       }
07140       continue; // start again with archive positioned at the tcode we want
07141     }
07142 
07143     // "read" 4 or 8 byte chunk value
07144     i64 = -1;
07145     buffer = BufferToChunkValue(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64);
07146     if ( 0 == buffer || i64 <= 0 )
07147       continue;
07148     const ON__UINT64 length_of_table = (ON__UINT64)i64;
07149 
07150     if ( length_of_table < 2*sizeof_chunk_header + 4 + min_length_data )
07151     {
07152       if ( sizeof_chunk_header == length_of_table && 2 != empty_table_status )
07153       {
07154         // see if we are at a TCODE_ENDOFTABLE chunk
07155         buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_ENDOFTABLE);
07156         if ( 0 != buffer )
07157         {
07158           i64 = -1;
07159           buffer = BufferToChunkValue(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64);
07160           if ( 0 == i64 )
07161           {
07162             if ( 0 == empty_table_status )
07163             {
07164               empty_table_pos = pos1-1;
07165               empty_table_status = 1;
07166             }
07167             else
07168             {
07169               // found 2 or more candidates for the end of table chunk
07170               empty_table_status = 2;
07171             }
07172           }
07173         }
07174       }
07175       continue;
07176     }
07177 
07178     if ( bFindUserTable )
07179     {
07180       // We found TCODE_USER_TABLE + chunk length.  If it is a user table,
07181       // there should be a TCODE_USER_TABLE_UUID chunk with a crc.
07182       const unsigned char* buffer0 = buffer;
07183       buffer = EmergencyFindTable_UuidHelper(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,TCODE_USER_TABLE_UUID,&class_uuid);
07184       if ( 0 == buffer || buffer <= buffer0 )
07185         continue;
07186 
07187       length_of_user_uuid_and_header = buffer - buffer0;
07188       // At this point we should be postioned at the table_record_tcode = TCODE_USER_RECORD chunk
07189     }
07190 
07191     // see if the start of the buffer contains the 4 byte typecode value = table_record_tcode.
07192     buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,table_record_tcode);
07193     if ( 0 == buffer )
07194       continue;
07195     i64 = -1;
07196     buffer = BufferToChunkValue( bReverseByteOrder, sizeof_chunk_value,buffer,buffer_end,&i64);
07197     if ( 0 == buffer || i64 <= 0 )
07198       continue;
07199     const ON__UINT64 length_of_record = (ON__UINT64)i64;
07200 
07201 
07202     if ( bFindUserTable )
07203     {
07204       ON__UINT64 expected_length_of_table = length_of_user_uuid_and_header 
07205                                           + sizeof_chunk_header
07206                                           + length_of_record;
07207       if ( expected_length_of_table != length_of_table )
07208         continue;
07209     }
07210     else
07211     {
07212       if ( length_of_record < 4*sizeof_chunk_header + 20 + min_length_data )
07213         continue;
07214       if ( length_of_record + 2*sizeof_chunk_header > length_of_table)
07215         continue;
07216 
07217       if (bFindObjectTable)
07218       {
07219         buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_OBJECT_RECORD_TYPE);
07220         if ( 0 == buffer )
07221           continue;
07222         // The TCODE_OBJECT_RECORD_TYPE is a shot chunk whose value is a bitfield 
07223         // used to filter reading of objects.  Checking the value will not help
07224         // validate the record, but we need to skip over it.
07225         buffer = BufferToChunkValue(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,0);
07226         if ( 0 == buffer )
07227           continue;
07228       }
07229 
07230       buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_OPENNURBS_CLASS);
07231       if ( 0 == buffer )
07232         continue;
07233 
07234       i64 = -1;
07235       buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64);
07236       if ( 0 == buffer || i64 <= 0 )
07237         continue;
07238       const ON__UINT64 length_of_on_class = (ON__UINT64)i64;
07239 
07240       if ( length_of_on_class < 3*sizeof_chunk_header + 20 + min_length_data )
07241         continue;
07242 
07243       if ( length_of_on_class + sizeof_chunk_header + 4 > length_of_record)
07244         continue;
07245 
07246       const unsigned char* buffer0 = buffer;
07247       buffer = EmergencyFindTable_UuidHelper(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,TCODE_OPENNURBS_CLASS_UUID,(ON_UuidIsNil(class_uuid) ? NULL : &class_uuid));
07248       if ( 0 == buffer || buffer <= buffer0)
07249         continue;
07250       const size_t length_of_uuid_chunk = buffer-buffer0;
07251 
07252       buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_OPENNURBS_CLASS_DATA);
07253       if ( 0 == buffer )
07254         continue;
07255 
07256       i64 = -1;
07257       buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64);
07258       if ( 0 == buffer || i64 < 0 )
07259         continue;
07260       const ON__UINT64 length_of_data = (ON__UINT64)i64;
07261 
07262       if ( length_of_data < min_length_data )
07263         continue;
07264       if ( length_of_data + length_of_uuid_chunk + 2*sizeof_chunk_header > length_of_on_class)
07265         continue;
07266     }
07267 
07268     // position archive at point where the table tcode was read
07269     if ( !BigSeekBackward(sizeof_read) )
07270       break;
07271     pos = CurrentPosition();
07272     if ( pos+1 == pos1)
07273       rc = true;
07274     break;
07275   }
07276 
07277   if ( !rc )
07278   {
07279     // we didn't fing a table containing anything
07280     if ( 1 == empty_table_status )
07281     {
07282       // we found one candidate for an empty table.
07283       // This is reasonable for materials, bitmaps, and the like.
07284       rc = BigSeekFromStart(empty_table_pos);
07285     }
07286     else
07287     {
07288       // nothing in this file.
07289       BigSeekFromStart(pos0);
07290     }
07291   }
07292   return rc;
07293 }
07294 
07295 bool ON_BinaryArchive::FindTableInDamagedArchive(
07296                 const unsigned int tcode_table,
07297                 const unsigned int tcode_record,
07298                 const ON_UUID class_uuid,
07299                 const int min_length_data
07300                 )
07301 {
07302   bool rc = FindMisplacedTable(
07303                   0,
07304                   tcode_table,
07305                   tcode_record,
07306                   class_uuid,
07307                   min_length_data
07308                   );
07309   return rc;
07310 }
07311 
07312 /*
07313 static
07314 bool FindMaterialTable( ON_BinaryArchive& binary_archive, size_t filelength )
07315 {
07316   bool rc = EmergencyFindTable( 
07317                 binary_archive, filelength,
07318                 TCODE_MATERIAL_TABLE, TCODE_MATERIAL_RECORD,
07319                 ON_Material::m_ON_Material_class_id.Uuid(),
07320                 114
07321                 );
07322   return rc;
07323 }
07324 */
07325 
07326 bool ON_BinaryArchive::EndRead3dmTable( unsigned int typecode )
07327 {
07328   bool rc = false;
07329   const table_type tt = TableTypeFromTypecode(typecode);
07330   if (tt == no_active_table) {
07331     ON_ERROR("ON_BinaryArchive::EndRead3dmTable() bad typecode");
07332     return false;
07333   }
07334   if ( m_active_table != tt ) {
07335     ON_ERROR("ON_BinaryArchive::EndRead3dmTable() m_active_table != t");
07336     return false;
07337   }
07338   if ( m_3dm_version == 1 ) {
07339     if ( m_chunk.Count() != 0 ) {
07340       ON_ERROR("ON_BinaryArchive::EndRead3dmTable() v1 file m_chunk.Count() != 0");
07341       return false;
07342     }
07343     rc = true;
07344   }
07345   else {
07346     if ( m_active_table == group_table && m_3dm_opennurbs_version < 200012210 ) 
07347     {
07348       // 3DM archives written before version 200012210 and before do not have group tables
07349       rc = true;
07350     }
07351     else if ( m_active_table == font_table && m_3dm_opennurbs_version < 200109180 ) 
07352     {
07353       // 3DM archives written before version 200109180 and before do not have font tables
07354       rc = true;
07355     }
07356     else if ( m_active_table == dimstyle_table && m_3dm_opennurbs_version < 200109260 ) 
07357     {
07358       // 3DM archives written before version 200109260 and before do not have dimstyle tables
07359       rc = true;
07360     }
07361     else if ( m_active_table == instance_definition_table && m_3dm_opennurbs_version < 200205110 ) 
07362     {
07363       // 3DM archives written before version 200205110 and before do not have instance definition tables
07364       rc = true;
07365     }
07366     else if ( m_active_table == hatchpattern_table && m_3dm_opennurbs_version < 200405030 ) 
07367     {
07368       // 3DM archives written before version 200405030 and before do not have hatch pattern tables
07369       rc = true;
07370     }
07371     else if ( m_active_table == linetype_table && m_3dm_opennurbs_version < 200503170 ) 
07372     {
07373       // 3DM archives written before version 200503170 and before do not have linetype tables
07374       rc = true;
07375     }
07376     else if ( m_active_table == texture_mapping_table && m_3dm_opennurbs_version < 200511110 )
07377     {
07378       // 3DM archives written before version 200511110 and before do not have texture mapping tables
07379       rc = true;
07380     }
07381     else if ( m_active_table == historyrecord_table && m_3dm_opennurbs_version < 200601180 )
07382     {
07383       // 3DM archives written before version 200601180 and before do not have history record tables
07384       rc = true;
07385     }
07386     else 
07387     {
07388       if ( m_chunk.Count() != 1 ) 
07389       {
07390         ON_ERROR("ON_BinaryArchive::EndRead3dmTable() v2 file m_chunk.Count() != 1");
07391         return false;
07392       }
07393       const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
07394       if ( 0 == c || c->m_typecode != typecode ) 
07395       {
07396         ON_ERROR("ON_BinaryArchive::EndRead3dmTable() m_chunk.Last()->typecode != typecode");
07397         return false;
07398       }
07399       rc = EndRead3dmChunk();
07400     }
07401   }
07402   m_active_table = no_active_table;
07403   return rc;
07404 }
07405 
07406 bool ON_BinaryArchive::BeginWrite3dmBitmapTable()
07407 {
07408   return BeginWrite3dmTable( TCODE_BITMAP_TABLE );
07409 }
07410 
07411 bool ON_BinaryArchive::EndWrite3dmBitmapTable()
07412 {
07413   return EndWrite3dmTable( TCODE_BITMAP_TABLE );
07414 }
07415 
07416 bool ON_BinaryArchive::Write3dmBitmap( const ON_Bitmap& bitmap )
07417 {
07418   bool rc = false;
07419   if ( m_3dm_version != 1 ) 
07420   {
07421     const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
07422     if ( c && c->m_typecode == TCODE_BITMAP_TABLE ) 
07423     {
07424       rc = BeginWrite3dmChunk( TCODE_BITMAP_RECORD, 0 );
07425       if ( rc ) 
07426       {
07427         rc = WriteObject( bitmap );
07428         if ( !EndWrite3dmChunk() )
07429           rc = false;
07430       }
07431     }
07432     else 
07433     {
07434       ON_ERROR("ON_BinaryArchive::Write3dmBitmap() must be called in BeginWrite3dmBitmapTable() block");
07435       rc = false;
07436     }
07437   }
07438   return rc;
07439 }
07440 
07441 bool ON_BinaryArchive::BeginRead3dmBitmapTable()
07442 {
07443   bool rc =  BeginRead3dmTable( TCODE_BITMAP_TABLE );
07444   if ( !rc )
07445   {
07446     // 1 November 2005 Dale Lear
07447     //    This fall back is slow but it has been finding
07448     //    layer and object tables in damaged files.  I'm
07449     //    adding it to the other BeginRead3dm...Table()
07450     //    functions when it makes sense.
07451     rc = FindMisplacedTable( 
07452                 0,
07453                 TCODE_BITMAP_TABLE, TCODE_BITMAP_RECORD,
07454                 ON_nil_uuid, // multiple types of opennurbs objects in bitmap tables
07455                 40 
07456                 );
07457     if ( rc )
07458     {
07459       rc = BeginRead3dmTable( TCODE_BITMAP_TABLE );
07460     }
07461   }
07462   return rc;
07463 }
07464 
07465 bool ON_BinaryArchive::EndRead3dmBitmapTable()
07466 {
07467   return EndRead3dmTable( TCODE_BITMAP_TABLE );
07468 }
07469 
07470 int ON_BinaryArchive::Read3dmBitmap(  // returns 0 at end of bitmap table
07471                                       //         1 bitmap successfully read
07472           ON_Bitmap** ppBitmap // bitmap returned here
07473           )
07474 {
07475   if ( ppBitmap )
07476     *ppBitmap = 0;
07477   ON_Bitmap* bitmap = 0;
07478   int rc = 0;
07479   if ( m_3dm_version != 1 ) {
07480     ON__UINT32 tcode = 0;
07481     ON__INT64 big_value = 0;
07482     if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) 
07483     {
07484       if ( tcode == TCODE_BITMAP_RECORD ) 
07485       {
07486         ON_Object* p = 0;
07487         if ( ReadObject( &p ) ) 
07488         {
07489           bitmap = ON_Bitmap::Cast(p);
07490           if ( !bitmap )
07491             delete p;
07492           else
07493             rc = 1;
07494         }
07495         if (!bitmap) 
07496         {
07497           ON_ERROR("ON_BinaryArchive::Read3dmBitmap() - corrupt bitmap table");
07498         }
07499         if ( ppBitmap )
07500           *ppBitmap = bitmap;
07501         else if ( bitmap )
07502           delete bitmap;          
07503       }
07504       else if ( tcode != TCODE_ENDOFTABLE )
07505       {
07506         ON_ERROR("ON_BinaryArchive::Read3dmBitmap() - corrupt bitmap table");
07507       }
07508       EndRead3dmChunk();
07509     }
07510   }
07511 
07512   return rc;
07513 }
07514 
07515 
07516 bool ON_BinaryArchive::BeginWrite3dmLayerTable()
07517 {
07518   bool rc = false;
07519   if ( m_3dm_version != 1 ) {
07520     rc = BeginWrite3dmTable( TCODE_LAYER_TABLE );
07521   }
07522   else {
07523     if ( m_chunk.Count() ) {
07524       ON_ERROR("ON_BinaryArchive::BeginWrite3dmLayerTable() - chunk stack should be empty");
07525       return false;
07526     }
07527     if ( m_active_table != no_active_table ) {
07528       ON_ERROR("ON_BinaryArchive::BeginWrite3dmLayerTable() - m_active_table != no_active_table");
07529     }
07530     m_active_table = layer_table;
07531     rc = true;
07532   }
07533 
07534   return rc;
07535 }
07536 
07537 bool ON_BinaryArchive::Write3dmLayer( const ON_Layer&  layer )
07538 {
07539   bool rc = false;
07540   if ( m_active_table != layer_table ) {
07541     ON_ERROR("ON_BinaryArchive::Write3dmLayer() - m_active_table != layer_table");
07542   }
07543 
07544   if ( m_3dm_version == 1 ) {
07545     // legacy version 1 layer information is in a top level TCODE_LAYER chunk
07546     if ( m_chunk.Count() ) {
07547       ON_ERROR("ON_BinaryArchive::Write3dmLayer() - version 1 - chunk stack should be empty");
07548       return false;
07549     }
07550     ON_String s = layer.LayerName();
07551     if ( !s.IsEmpty() ) {
07552       rc = BeginWrite3dmChunk( TCODE_LAYER, 0 );
07553 
07554       // layer name
07555       if (rc) {
07556         rc = BeginWrite3dmChunk( TCODE_LAYERNAME, 0 );
07557         if(rc) rc = WriteString(s);
07558         if (!EndWrite3dmChunk())
07559           rc = false;
07560       }
07561 
07562       // layer color
07563       if (rc) {
07564         rc = BeginWrite3dmChunk( TCODE_RGB, layer.Color() );
07565         if (!EndWrite3dmChunk())
07566           rc = false;
07567       }
07568 
07569       // layer mode normal=0/hidden=1/locked=2
07570       if (rc) 
07571       {
07572         int mode;
07573         if ( layer.IsLocked() )
07574           mode = 2; // "locked"
07575         else if ( layer.IsVisible() )
07576           mode = 0; // "normal"
07577         else
07578           mode = 1; // "hidden"
07579         rc = BeginWrite3dmChunk( TCODE_LAYERSTATE, mode );
07580         if (!EndWrite3dmChunk())
07581           rc = false;
07582       }
07583 
07584       if ( !BeginWrite3dmChunk( TCODE_ENDOFTABLE, 0 ) )
07585         rc = false;
07586       if ( !EndWrite3dmChunk() )
07587         rc = false;
07588 
07589       if (!EndWrite3dmChunk()) // end of TCODE_LAYER chunk
07590         rc = false;
07591     }
07592   }
07593   else {
07594     // version 2+
07595     const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
07596     if ( c && c->m_typecode == TCODE_LAYER_TABLE ) {
07597       rc = BeginWrite3dmChunk( TCODE_LAYER_RECORD, 0 );
07598       if ( rc ) {
07599         rc = WriteObject( layer );
07600         if ( !EndWrite3dmChunk() )
07601           rc = false;
07602       }
07603     }
07604     else {
07605       ON_ERROR("ON_BinaryArchive::Write3dmLayer() must be called in BeginWrite3dmLayerTable(2) block");
07606       rc = false;
07607     }
07608   }
07609 
07610   return rc;
07611 }
07612 
07613 bool ON_BinaryArchive::EndWrite3dmLayerTable()
07614 {
07615   bool rc = false;
07616   if ( m_3dm_version == 1 ) {
07617     if ( m_active_table != layer_table ) {
07618       ON_ERROR("ON_BinaryArchive::EndWrite3dmLayerTable() - m_active_table != layer_table");
07619     }
07620     rc = true;
07621     m_active_table = no_active_table;
07622   }
07623   else {
07624     rc = EndWrite3dmTable( TCODE_LAYER_TABLE );
07625   }
07626   return rc;
07627 }
07628 
07629 bool ON_BinaryArchive::BeginRead3dmLayerTable()
07630 {
07631   bool rc = false;
07632   m_3dm_v1_layer_index = 0;
07633   rc = BeginRead3dmTable( TCODE_LAYER_TABLE );
07634   if ( !rc )
07635   {
07636     // 8 October 2004 Dale Lear
07637     //    This fall back is slow but it will find
07638     //    layer tables in files that have been damaged.
07639     rc = FindMisplacedTable( 
07640                 0,
07641                 TCODE_LAYER_TABLE, TCODE_LAYER_RECORD,
07642                 ON_Layer::m_ON_Layer_class_id.Uuid(),
07643                 30
07644                 );
07645     if ( rc )
07646     {
07647       rc = BeginRead3dmTable( TCODE_LAYER_TABLE );
07648     }
07649 
07650   }
07651   else if ( rc && m_3dm_version == 1 ) {
07652     rc = Seek3dmChunkFromStart( TCODE_LAYER );
07653     rc = true; // there are 1.0 files written by the old IO toolkit that have no layers
07654   }
07655   return rc;
07656 }
07657 
07658 int ON_BinaryArchive::Read3dmV1LayerIndex(const char* sV1LayerName) const
07659 {
07660   // returns V1 layer index
07661 
07662   int layer_index = -1;
07663 
07664   if (    ON::read3dm == m_mode
07665        && 0 == m_3dm_opennurbs_version
07666        && 1 == m_3dm_version
07667        && 0 != m_V1_layer_list
07668        && 0 != sV1LayerName 
07669        && 0 != sV1LayerName[0] 
07670      )
07671   {
07672     struct ON__3dmV1LayerIndex* p = m_V1_layer_list;
07673     int i;
07674     for ( i = 0; 0 != p && i < 1000; i++ )
07675     {
07676       if ( p->m_layer_index < 0 )
07677         break;
07678       if ( p->m_layer_name_length < 1 || p->m_layer_name_length>256)
07679         break;
07680       if ( 0 == p->m_layer_name )
07681         break;
07682       if ( 0 == p->m_layer_name[0] )
07683         break;
07684       if ( 0 != p->m_layer_name[p->m_layer_name_length] )
07685         break;
07686       if ( !on_stricmp(p->m_layer_name,sV1LayerName) )
07687       {
07688         layer_index = p->m_layer_index;
07689         break;
07690       }
07691       p = p->m_next;
07692     }
07693   }
07694 
07695   return layer_index;
07696 }
07697 
07698 bool ON_BinaryArchive::Read3dmV1Layer( ON_Layer*& layer )
07699 {
07700   ON_String s;
07701   bool rc = 0;
07702   ON__UINT32 tcode;
07703   ON__INT64 big_value;
07704   for (;;) 
07705   {
07706     tcode = 0;
07707     big_value = 0;
07708     if (!BeginRead3dmBigChunk(&tcode,&big_value))
07709       break; // assume we are at the end of the file
07710     if ( tcode == TCODE_LAYER ) {
07711       layer = new ON_Layer();
07712       layer->SetLayerIndex(m_3dm_v1_layer_index++);
07713       rc = 1;
07714       break;
07715     }
07716     if (!EndRead3dmChunk())
07717        break;
07718   }
07719   if ( layer ) {
07720     rc = false;
07721     for (;;) 
07722     {
07723       tcode = 0;
07724       big_value = 0;
07725       if (!BeginRead3dmBigChunk(&tcode,&big_value))
07726         break;
07727       switch(tcode) 
07728       {
07729       case TCODE_LAYERNAME:
07730         {
07731           int slen = 0;
07732           ReadInt(&slen);
07733           if ( slen < 0 || slen > 10000 )
07734           {
07735             ON_ERROR("ON_BinaryArchive::Read3dmV1Layer() - invalid layer name length");
07736           }
07737           else
07738           {
07739             s.SetLength(slen);
07740             if ( ReadByte( s.Length(), s.Array() ) ) 
07741             {
07742               layer->SetLayerName(s);
07743             }
07744           }
07745         }
07746         break;
07747       case TCODE_RGB:
07748         {
07749           ON__UINT64 rgb64 = (ON__UINT64)big_value;
07750           ON__UINT32 rgb32 = (ON__UINT32)rgb64;
07751           layer->SetColor( ON_Color((ON__UINT32)rgb32) );
07752         }
07753         break;
07754       case TCODE_LAYERSTATE:
07755         switch (big_value) 
07756         {
07757         case 1: // hidden
07758           layer->SetVisible(false);
07759           layer->SetLocked(false);
07760           break;
07761         case 2: // locked
07762           layer->SetVisible(true);
07763           layer->SetLocked(true);
07764           break;
07765         default: // normal
07766           layer->SetVisible(true);
07767           layer->SetLocked(false);
07768           break;
07769         }
07770         break;
07771       }
07772       if (!EndRead3dmChunk())
07773          break;
07774       if ( TCODE_ENDOFTABLE == tcode ) {
07775         rc = true;
07776         break;
07777       }
07778     }
07779     if ( !EndRead3dmChunk() ) // end of TCODE_LAYER chunk
07780       rc = false;
07781   }
07782   if ( !rc && layer ) 
07783   {
07784     delete layer;
07785     layer = 0;
07786   }
07787   else if (rc && layer)
07788   {
07789     if (    ON::read3dm == m_mode
07790          && 0 == m_3dm_opennurbs_version
07791          && 1 == m_3dm_version
07792          )
07793     {
07794       // save layer index and name in a linked list.
07795       int s_length = s.Length();
07796       const char* s_name = s.Array();
07797       if (    layer->LayerIndex() >= 0 
07798            && s_length > 0 
07799            && s_length < 256 
07800            && 0 != s_name 
07801            && 0 != s_name[0] 
07802          )
07803       {
07804         struct ON__3dmV1LayerIndex* p = (struct ON__3dmV1LayerIndex*)oncalloc(1, sizeof(*p) + (s_length+1)*sizeof(*p->m_layer_name) );
07805         p->m_layer_name = (char*)(p+1);
07806         p->m_layer_index = layer->LayerIndex();
07807         p->m_layer_name_length = s_length;
07808         memcpy(p->m_layer_name,s_name,s_length*sizeof(*p->m_layer_name));
07809         p->m_layer_name[s_length] = 0;
07810         p->m_next = m_V1_layer_list;
07811         m_V1_layer_list = p;
07812       }
07813     }
07814   }
07815   return rc;
07816 }
07817 
07818 int ON_BinaryArchive::Read3dmLayer( ON_Layer** ppLayer )
07819 {
07820   if ( !ppLayer )
07821     return 0;
07822   *ppLayer = 0;
07823   if ( m_active_table != layer_table ) {
07824     ON_ERROR("ON_BinaryArchive::BeginRead3dmLayerTable() - m_active_table != no_active_table");
07825   }
07826   ON__UINT32 tcode;
07827   ON__INT64 big_value;
07828   ON_Layer* layer = NULL;
07829   // returns 0 at end of layer table
07830   if ( m_3dm_version == 1 ) {
07831     Read3dmV1Layer(layer);
07832   }
07833   else {
07834     // version 2+
07835     tcode = 0;
07836     big_value = 0;
07837     if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) {
07838       if ( tcode == TCODE_LAYER_RECORD ) {
07839         ON_Object* p = 0;
07840         if ( ReadObject( &p ) ) {
07841           layer = ON_Layer::Cast(p);
07842           if ( !layer )
07843             delete p;
07844         }
07845         if (!layer) {
07846           ON_ERROR("ON_BinaryArchive::Read3dmLayer() - corrupt layer table");
07847         }
07848       }
07849       else if ( tcode != TCODE_ENDOFTABLE ) {
07850         ON_ERROR("ON_BinaryArchive::Read3dmLayer() - corrupt layer table");
07851       }
07852       EndRead3dmChunk();
07853     }
07854   }
07855   if ( layer )
07856     layer->HasPerViewportSettings(ON_nil_uuid); // this call sets ON_Layer::m__runtime_flags
07857   *ppLayer = layer;
07858   return (layer) ? 1 : 0;
07859 }
07860 
07861 bool ON_BinaryArchive::EndRead3dmLayerTable()
07862 {
07863   bool rc = false;
07864   if ( m_3dm_version == 1 ) {
07865     if ( m_active_table != layer_table ) {
07866       ON_ERROR("ON_BinaryArchive::EndRead3dmLayerTable() - m_active_table != no_active_table");
07867       rc = false;
07868     }
07869     else if ( m_chunk.Count() ) {
07870       ON_ERROR("ON_BinaryArchive::EndRead3dmLayerTable() - m_chunk.Count() > 0");
07871       rc = false;
07872     }
07873     else {
07874       // rewind to start of chunks
07875       rc = SeekFromStart(32)?true:false;
07876     }
07877     m_active_table = no_active_table;
07878   }
07879   else {
07880     rc = EndRead3dmTable( TCODE_LAYER_TABLE );
07881   }
07882   return rc;
07883 }
07884 
07885 
07890 
07891 bool ON_BinaryArchive::BeginWrite3dmGroupTable()
07892 {
07893   bool rc = false;
07894   rc = BeginWrite3dmTable( TCODE_GROUP_TABLE );
07895   return rc;
07896 }
07897 
07898 bool ON_BinaryArchive::Write3dmGroup( const ON_Group&  group )
07899 {
07900   bool rc = false;
07901   if ( m_active_table != group_table ) {
07902     ON_ERROR("ON_BinaryArchive::Write3dmGroup() - m_active_table != group_table");
07903   }
07904 
07905   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
07906   if ( c && c->m_typecode == TCODE_GROUP_TABLE ) {
07907     rc = BeginWrite3dmChunk( TCODE_GROUP_RECORD, 0 );
07908     if ( rc ) {
07909       rc = WriteObject( group );
07910       if ( !EndWrite3dmChunk() )
07911         rc = false;
07912     }
07913   }
07914   else {
07915     ON_ERROR("ON_BinaryArchive::Write3dmGroup() must be called in BeginWrite3dmGroupTable() block");
07916     rc = false;
07917   }
07918 
07919   return rc;
07920 }
07921 
07922 bool ON_BinaryArchive::EndWrite3dmGroupTable()
07923 {
07924   bool rc = false;
07925   rc = EndWrite3dmTable( TCODE_GROUP_TABLE );
07926   return rc;
07927 }
07928 
07929 bool ON_BinaryArchive::BeginRead3dmGroupTable()
07930 {
07931   if ( m_3dm_version == 1 ) {
07932     return true;
07933   }
07934   bool rc = false;
07935   rc = BeginRead3dmTable( TCODE_GROUP_TABLE );
07936 
07937   if ( !rc )
07938   {
07939     // 1 November 2005 Dale Lear
07940     //    This fall back is slow but it has been finding
07941     //    layer and object tables in damaged files.  I'm
07942     //    adding it to the other BeginRead3dm...Table()
07943     //    functions when it makes sense.
07944     rc = FindMisplacedTable( 
07945                 0,
07946                 TCODE_GROUP_TABLE, TCODE_GROUP_RECORD,
07947                 ON_Group::m_ON_Group_class_id.Uuid(),
07948                 20 
07949                 );
07950     if ( rc )
07951     {
07952       rc = BeginRead3dmTable( TCODE_GROUP_TABLE );
07953     }
07954   }
07955 
07956   return rc;
07957 }
07958 
07959 int ON_BinaryArchive::Read3dmGroup( ON_Group** ppGroup )
07960 {
07961   if ( !ppGroup )
07962     return 0;
07963   *ppGroup = 0;
07964   if ( m_3dm_version == 1 ) {
07965     return 0;
07966   }
07967   if ( m_active_table != group_table ) {
07968     ON_ERROR("ON_BinaryArchive::BeginRead3dmGroupTable() - m_active_table != no_active_table");
07969   }
07970   if ( m_3dm_opennurbs_version < 200012210 ) {
07971     // 3DM archives written before version 200012210 and before do not have group tables
07972     return 0;
07973   }
07974 
07975   ON__UINT32 tcode = 0;
07976   ON__INT64 big_value = 0;
07977   ON_Group* group = NULL;
07978   if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) 
07979   {
07980     if ( tcode == TCODE_GROUP_RECORD ) {
07981       ON_Object* p = 0;
07982       if ( ReadObject( &p ) ) {
07983         group = ON_Group::Cast(p);
07984         if ( !group )
07985           delete p;
07986       }
07987       if (!group) {
07988         ON_ERROR("ON_BinaryArchive::Read3dmGroup() - corrupt group table");
07989       }
07990     }
07991     else if ( tcode != TCODE_ENDOFTABLE ) {
07992       ON_ERROR("ON_BinaryArchive::Read3dmGroup() - corrupt group table");
07993     }
07994     EndRead3dmChunk();
07995   }
07996   *ppGroup = group;
07997   return (group) ? 1 : 0;
07998 }
07999 
08000 bool ON_BinaryArchive::EndRead3dmGroupTable()
08001 {
08002   bool rc = false;
08003   if ( m_3dm_version == 1 ) {
08004     return true;
08005   }
08006   else {
08007     rc = EndRead3dmTable( TCODE_GROUP_TABLE );
08008   }
08009   return rc;
08010 }
08011 
08016 
08017 bool ON_BinaryArchive::BeginWrite3dmFontTable()
08018 {
08019   bool rc = false;
08020   rc = BeginWrite3dmTable( TCODE_FONT_TABLE );
08021   return rc;
08022 }
08023 
08024 bool ON_BinaryArchive::Write3dmFont( const ON_Font&  font )
08025 {
08026   bool rc = false;
08027   if ( m_active_table != font_table ) {
08028     ON_ERROR("ON_BinaryArchive::Write3dmFont() - m_active_table != font_table");
08029   }
08030 
08031   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
08032   if ( c && c->m_typecode == TCODE_FONT_TABLE ) {
08033     rc = BeginWrite3dmChunk( TCODE_FONT_RECORD, 0 );
08034     if ( rc ) {
08035       rc = WriteObject( font );
08036       if ( !EndWrite3dmChunk() )
08037         rc = false;
08038     }
08039   }
08040   else {
08041     ON_ERROR("ON_BinaryArchive::Write3dmFont() must be called in BeginWrite3dmFontTable() block");
08042     rc = false;
08043   }
08044 
08045   return rc;
08046 }
08047 
08048 bool ON_BinaryArchive::EndWrite3dmFontTable()
08049 {
08050   bool rc = false;
08051   rc = EndWrite3dmTable( TCODE_FONT_TABLE );
08052   return rc;
08053 }
08054 
08055 bool ON_BinaryArchive::BeginRead3dmFontTable()
08056 {
08057   if ( m_3dm_version <= 2 ) {
08058     return true;
08059   }
08060   bool rc = false;
08061   rc = BeginRead3dmTable( TCODE_FONT_TABLE );
08062 
08063   if ( !rc )
08064   {
08065     // 1 November 2005 Dale Lear
08066     //    This fall back is slow but it has been finding
08067     //    layer and object tables in damaged files.  I'm
08068     //    adding it to the other BeginRead3dm...Table()
08069     //    functions FindMisplacedTable it makes sense.
08070     rc = FindMisplacedTable( 
08071                 0,
08072                 TCODE_FONT_TABLE, TCODE_FONT_RECORD,
08073                 ON_Font::m_ON_Font_class_id.Uuid(),
08074                 30 
08075                 );
08076     if ( rc )
08077     {
08078       rc = BeginRead3dmTable( TCODE_FONT_TABLE );
08079     }
08080   }
08081 
08082   return rc;
08083 }
08084 
08085 int ON_BinaryArchive::Read3dmFont( ON_Font** ppFont )
08086 {
08087   if ( !ppFont )
08088     return 0;
08089   *ppFont = 0;
08090   if ( m_3dm_version <= 2 ) {
08091     return 0;
08092   }
08093   if ( m_active_table != font_table ) {
08094     ON_ERROR("ON_BinaryArchive::BeginRead3dmFontTable() - m_active_table != no_active_table");
08095   }
08096   if ( m_3dm_opennurbs_version < 200109180 ) {
08097     // 3DM archives written before version 200109180 and before do not have font tables
08098     return 0;
08099   }
08100 
08101   ON__UINT32 tcode = 0;
08102   ON__INT64 big_value = 0;
08103   ON_Font* font = NULL;
08104   if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) 
08105   {
08106     if ( tcode == TCODE_FONT_RECORD ) {
08107       ON_Object* p = 0;
08108       if ( ReadObject( &p ) ) {
08109         font = ON_Font::Cast(p);
08110         if ( !font )
08111           delete p;
08112       }
08113       if (!font) {
08114         ON_ERROR("ON_BinaryArchive::Read3dmFont() - corrupt font table");
08115       }
08116     }
08117     else if ( tcode != TCODE_ENDOFTABLE ) {
08118       ON_ERROR("ON_BinaryArchive::Read3dmFont() - corrupt font table");
08119     }
08120     EndRead3dmChunk();
08121   }
08122   *ppFont = font;
08123   return (font) ? 1 : 0;
08124 }
08125 
08126 bool ON_BinaryArchive::EndRead3dmFontTable()
08127 {
08128   bool rc = false;
08129   if ( m_3dm_version <= 2 ) {
08130     return true;
08131   }
08132   else {
08133     rc = EndRead3dmTable( TCODE_FONT_TABLE );
08134   }
08135   return rc;
08136 }
08137 
08142 
08143 bool ON_BinaryArchive::BeginWrite3dmDimStyleTable()
08144 {
08145   bool rc = false;
08146   rc = BeginWrite3dmTable( TCODE_DIMSTYLE_TABLE );
08147   return rc;
08148 }
08149 
08150 bool ON_BinaryArchive::Write3dmDimStyle( const ON_DimStyle&  dimstyle )
08151 {
08152   bool rc = false;
08153   if ( m_active_table != dimstyle_table ) {
08154     ON_ERROR("ON_BinaryArchive::Write3dmDimStyle() - m_active_table != dimstyle_table");
08155   }
08156 
08157   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
08158   if ( c && c->m_typecode == TCODE_DIMSTYLE_TABLE ) {
08159     rc = BeginWrite3dmChunk( TCODE_DIMSTYLE_RECORD, 0 );
08160     if ( rc ) {
08161       rc = WriteObject( dimstyle );
08162       if ( !EndWrite3dmChunk() )
08163         rc = false;
08164     }
08165   }
08166   else {
08167     ON_ERROR("ON_BinaryArchive::Write3dmDimStyle() must be called in BeginWrite3dmDimStyleTable() block");
08168     rc = false;
08169   }
08170 
08171   return rc;
08172 }
08173 
08174 bool ON_BinaryArchive::EndWrite3dmDimStyleTable()
08175 {
08176   bool rc = false;
08177   rc = EndWrite3dmTable( TCODE_DIMSTYLE_TABLE );
08178   return rc;
08179 }
08180 
08181 bool ON_BinaryArchive::BeginRead3dmDimStyleTable()
08182 {
08183   if ( m_3dm_version <= 2 ) {
08184     return true;
08185   }
08186   bool rc = false;
08187   rc = BeginRead3dmTable( TCODE_DIMSTYLE_TABLE );
08188 
08189   if ( !rc )
08190   {
08191     // 1 November 2005 Dale Lear
08192     //    This fall back is slow but it has been finding
08193     //    layer and object tables in damaged files.  I'm
08194     //    adding it to the other BeginRead3dm...Table()
08195     //    functions when it makes sense.
08196     rc = FindMisplacedTable( 
08197                 0,
08198                 TCODE_DIMSTYLE_TABLE, TCODE_DIMSTYLE_RECORD,
08199                 ON_DimStyle::m_ON_DimStyle_class_id.Uuid(),
08200                 30 
08201                 );
08202     if ( rc )
08203     {
08204       rc = BeginRead3dmTable( TCODE_DIMSTYLE_TABLE );
08205     }
08206   }
08207 
08208   return rc;
08209 }
08210 
08211 int ON_BinaryArchive::Read3dmDimStyle( ON_DimStyle** ppDimStyle )
08212 {
08213   if ( !ppDimStyle )
08214     return 0;
08215   *ppDimStyle = 0;
08216   if ( m_3dm_version <= 2 ) {
08217     return 0;
08218   }
08219   if ( m_active_table != dimstyle_table ) {
08220     ON_ERROR("ON_BinaryArchive::BeginRead3dmDimStyleTable() - m_active_table != no_active_table");
08221   }
08222   if ( m_3dm_opennurbs_version < 200109260 ) {
08223     // 3DM archives written before version 200109260 and before do not have dimstyle tables
08224     return 0;
08225   }
08226 
08227   ON__UINT32 tcode = 0;
08228   ON__INT64 big_value = 0;
08229   ON_DimStyle* dimstyle = NULL;
08230   tcode = 0;
08231   if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) 
08232   {
08233     if ( tcode == TCODE_DIMSTYLE_RECORD ) {
08234       ON_Object* p = 0;
08235       if ( ReadObject( &p ) ) {
08236         dimstyle = ON_DimStyle::Cast(p);
08237         if ( !dimstyle )
08238           delete p;
08239       }
08240       if (!dimstyle) {
08241         ON_ERROR("ON_BinaryArchive::Read3dmDimStyle() - corrupt dimstyle table");
08242       }
08243     }
08244     else if ( tcode != TCODE_ENDOFTABLE ) {
08245       ON_ERROR("ON_BinaryArchive::Read3dmDimStyle() - corrupt dimstyle table");
08246     }
08247     EndRead3dmChunk();
08248   }
08249   *ppDimStyle = dimstyle;
08250   return (dimstyle) ? 1 : 0;
08251 }
08252 
08253 bool ON_BinaryArchive::EndRead3dmDimStyleTable()
08254 {
08255   bool rc = false;
08256   if ( m_3dm_version <= 2 ) {
08257     return true;
08258   }
08259   else {
08260     rc = EndRead3dmTable( TCODE_DIMSTYLE_TABLE );
08261   }
08262   return rc;
08263 }
08264 
08269 
08270 bool ON_BinaryArchive::BeginWrite3dmHatchPatternTable()
08271 {
08272   bool rc = false;
08273   rc = BeginWrite3dmTable( TCODE_HATCHPATTERN_TABLE );
08274   return rc;
08275 }
08276 
08277 bool ON_BinaryArchive::Write3dmHatchPattern( const ON_HatchPattern&  pattern )
08278 {
08279   bool rc = false;
08280   if ( m_active_table != hatchpattern_table ) {
08281     ON_ERROR("ON_BinaryArchive::Write3dmHatchPattern() - m_active_table != hatchpattern_table");
08282   }
08283 
08284   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
08285   if ( c && c->m_typecode == TCODE_HATCHPATTERN_TABLE ) 
08286   {
08287     rc = BeginWrite3dmChunk( TCODE_HATCHPATTERN_RECORD, 0 );
08288     if (rc)
08289     {
08290       rc = WriteObject( pattern );
08291       if ( !EndWrite3dmChunk() )
08292         rc = false;
08293     }
08294 
08295     // 1 Nov 2005 Dale Lear:
08296     //
08297     //   This code was used before version 200511010.  The reader can
08298     //   still read the old files, but old versions of Rhino cannot read
08299     //   files written with version 200511010 or later.  This happened in
08300     //   the early beta cycile of V4.  V3 did not have hatch patterns.
08301     //   Please leave this comment here until Nov 2006 so I can remember
08302     //   what happened if I have to debug file IO.  By May 2006, all
08303     //   the betas that could write the bogus hatch tables should have
08304     //   expired.
08305     //
08306     //if ( rc ) {
08307     //  rc = pattern.Write( *this)?true:false;
08308     //  if ( !EndWrite3dmChunk())
08309     //    rc = false;
08310     //}
08311   }
08312   else 
08313   {
08314     ON_ERROR("ON_BinaryArchive::Write3dmHatchPattern() must be called in BeginWrite3dmHatchPatternTable() block");
08315     rc = false;
08316   }
08317 
08318   return rc;
08319 }
08320 
08321 bool ON_BinaryArchive::EndWrite3dmHatchPatternTable()
08322 {
08323   bool rc = false;
08324   rc = EndWrite3dmTable( TCODE_HATCHPATTERN_TABLE );
08325   return rc;
08326 }
08327 
08328 bool ON_BinaryArchive::BeginRead3dmHatchPatternTable()
08329 {
08330   if ( m_3dm_version <= 3)
08331   {
08332     return true;
08333   }
08334   bool rc = BeginRead3dmTable( TCODE_HATCHPATTERN_TABLE );
08335 
08336   if ( !rc && m_3dm_opennurbs_version >= 200511010 )
08337   {
08338     // 1 November 2005 Dale Lear
08339     //    This fall back is slow but it has been finding
08340     //    layer and object tables in damaged files.  I'm
08341     //    adding it to the other BeginRead3dm...Table()
08342     //    functions when it makes sense.
08343     //    It only works on files with ver
08344     rc = FindMisplacedTable( 
08345                 0,
08346                 TCODE_HATCHPATTERN_TABLE, TCODE_HATCHPATTERN_RECORD,
08347                 ON_HatchPattern::m_ON_HatchPattern_class_id.Uuid(),
08348                 30 
08349                 );
08350     if ( rc )
08351     {
08352       rc = BeginRead3dmTable( TCODE_HATCHPATTERN_TABLE );
08353     }
08354   }
08355 
08356   return rc;
08357 }
08358 
08359 int ON_BinaryArchive::Read3dmHatchPattern( ON_HatchPattern** ppPattern )
08360 {
08361   if( !ppPattern )
08362     return 0;
08363   
08364   *ppPattern = 0;
08365   if( m_3dm_version <= 3) // 1 Nov 2005 Dale lear: change < to <= 
08366     return 0;             // because v3 files don't have hatch patterns.
08367 
08368   if ( m_active_table != hatchpattern_table ) 
08369   {
08370     ON_ERROR("ON_BinaryArchive::BeginRead3dmHatchPatternTable() - m_active_table != hatchpattern_table");
08371   }
08372   if ( m_3dm_opennurbs_version < 200405030 ) 
08373   {
08374     // 3DM archives written before version 200405030 do not have hatchpattern tables
08375     return 0;
08376   }
08377 
08378   ON__UINT32 tcode = 0;
08379   ON__INT64 big_value = 0;
08380   ON_HatchPattern* pPat = NULL;
08381   if( BeginRead3dmBigChunk( &tcode, &big_value)) 
08382   {
08383     if ( tcode == TCODE_HATCHPATTERN_RECORD ) 
08384     {
08385       if ( m_3dm_opennurbs_version < 200511010 )
08386       {
08387         // There was a bug in Write3dmHatchPattern and files written 
08388         // before version 200511010 didn't use ON_Object IO.
08389         pPat = new ON_HatchPattern;
08390         if( !pPat->Read( *this))
08391         {
08392           delete pPat;
08393           pPat = NULL;
08394           ON_ERROR("ON_BinaryArchive::Read3dmHatchPattern() - corrupt hatch pattern table");
08395         }
08396       }
08397       else
08398       {
08399         ON_Object* p = 0;
08400         if ( ReadObject( &p ) ) 
08401         {
08402           pPat = ON_HatchPattern::Cast(p);
08403           if ( !pPat )
08404             delete p;
08405         }
08406         if (!pPat) 
08407         {
08408           ON_ERROR("ON_BinaryArchive::Read3dmLayer() - corrupt layer table");
08409         }
08410       }
08411     }
08412     else if ( tcode != TCODE_ENDOFTABLE ) 
08413     {
08414       ON_ERROR("ON_BinaryArchive::Read3dmHatchPattern() - corrupt hatch pattern table");
08415     }
08416 
08417     EndRead3dmChunk();
08418   }
08419   *ppPattern = pPat;
08420   return( pPat) ? 1 : 0;
08421 }
08422 
08423 bool ON_BinaryArchive::EndRead3dmHatchPatternTable()
08424 {
08425   bool rc = false;
08426   if( m_3dm_version <= 3)
08427   {
08428     return true;
08429   }
08430   else 
08431   {
08432     rc = EndRead3dmTable( TCODE_HATCHPATTERN_TABLE);
08433   }
08434   return rc;
08435 }
08436 
08437 
08438 
08443 
08444 bool ON_BinaryArchive::BeginWrite3dmLinetypeTable()
08445 {
08446   bool rc = BeginWrite3dmTable( TCODE_LINETYPE_TABLE );
08447   return rc;
08448 }
08449 
08450 bool ON_BinaryArchive::Write3dmLinetype( const ON_Linetype&  linetype )
08451 {
08452   bool rc = false;
08453 
08454   if( m_active_table != linetype_table ) 
08455   {
08456     ON_ERROR("ON_BinaryArchive::Write3dmLinetype() - m_active_table != linetype_table");
08457   }
08458 
08459   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
08460   if ( c && c->m_typecode == TCODE_LINETYPE_TABLE ) 
08461   {
08462     rc = BeginWrite3dmChunk( TCODE_LINETYPE_RECORD, 0 );
08463     if ( rc ) 
08464     {
08465       rc = WriteObject( linetype );
08466       if ( !EndWrite3dmChunk())
08467         rc = false;
08468     }
08469   }
08470   else 
08471   {
08472     ON_ERROR("ON_BinaryArchive::Write3dmLinetype() must be called in BeginWrite3dmLinetypeTable() block");
08473     rc = false;
08474   }
08475 
08476   return rc;
08477 }
08478 
08479 bool ON_BinaryArchive::EndWrite3dmLinetypeTable()
08480 {
08481   bool rc = EndWrite3dmTable( TCODE_LINETYPE_TABLE );
08482   return rc;
08483 }
08484 
08485 bool ON_BinaryArchive::BeginRead3dmLinetypeTable()
08486 {
08487   bool rc = false;
08488 
08489   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200503170 )
08490   {
08491     rc = true;
08492   }
08493   else
08494   {
08495     rc = BeginRead3dmTable( TCODE_LINETYPE_TABLE );
08496     if ( !rc )
08497     {
08498       // 1 November 2005 Dale Lear
08499       //    This fall back is slow but it has been finding
08500       //    layer and object tables in damaged files.  I'm
08501       //    adding it to the other BeginRead3dm...Table()
08502       //    functions when it makes sense.
08503       rc = FindMisplacedTable( 
08504                   0,
08505                   TCODE_LINETYPE_TABLE, TCODE_LINETYPE_RECORD,
08506                   ON_Linetype::m_ON_Linetype_class_id.Uuid(),
08507                   20 
08508                   );
08509       if ( rc )
08510       {
08511         rc = BeginRead3dmTable( TCODE_LINETYPE_TABLE );
08512       }
08513     }
08514   }
08515 
08516   return rc;
08517 }
08518 
08519 int ON_BinaryArchive::Read3dmLinetype( ON_Linetype** ppLinetype )
08520 {
08521   if( !ppLinetype)
08522     return 0;
08523   
08524   *ppLinetype = 0;
08525 
08526   if( m_3dm_version < 4 || m_3dm_opennurbs_version < 200503170) 
08527     return 0;
08528 
08529   if ( m_active_table != linetype_table ) 
08530   {
08531     ON_ERROR("ON_BinaryArchive::BeginRead3dmLinetypeTable() - m_active_table != linetype_table");
08532   }
08533 
08534   ON__UINT32 tcode = 0;
08535   ON__INT64 big_value = 0;
08536   ON_Linetype* linetype = NULL;
08537   int rc = -1;
08538   if( BeginRead3dmBigChunk( &tcode, &big_value)) 
08539   {
08540     if ( tcode == TCODE_LINETYPE_RECORD ) 
08541     {
08542       ON_Object* p = 0;
08543       if ( ReadObject( &p ) )
08544       {
08545         linetype = ON_Linetype::Cast(p);
08546         if (!linetype )
08547           delete p;
08548         else
08549         {
08550           if (ppLinetype)
08551             *ppLinetype = linetype;
08552           rc = 1;
08553         }
08554       }
08555       if (!linetype)
08556       {
08557         ON_ERROR("ON_BinaryArchive::Read3dmLinetype() - corrupt linetype table");
08558       }
08559     }
08560     else if ( tcode == TCODE_ENDOFTABLE ) 
08561     {
08562      // end of linetype table
08563       rc = 0;
08564     }
08565     else
08566     {
08567       ON_ERROR("ON_BinaryArchive::Read3dmLinetype() - corrupt linetype table");
08568     }
08569     if (!EndRead3dmChunk())
08570       rc = -1;
08571   }
08572 
08573   return rc;
08574 }
08575 
08576 bool ON_BinaryArchive::EndRead3dmLinetypeTable()
08577 {
08578   bool rc = false;
08579   if( m_3dm_version < 4 || m_3dm_opennurbs_version < 200503170) 
08580   {
08581     rc = true;
08582   }
08583   else
08584   {
08585     rc = EndRead3dmTable( TCODE_LINETYPE_TABLE);
08586   }
08587   return rc;
08588 }
08589 
08590 
08596 
08597 bool ON_BinaryArchive::BeginWrite3dmInstanceDefinitionTable()
08598 {
08599   bool rc = false;
08600   rc = BeginWrite3dmTable( TCODE_INSTANCE_DEFINITION_TABLE );
08601   return rc;
08602 }
08603 
08604 bool ON_BinaryArchive::Write3dmInstanceDefinition( const ON_InstanceDefinition&  idef )
08605 {
08606   bool rc = false;
08607   if ( m_active_table != instance_definition_table ) {
08608     ON_ERROR("ON_BinaryArchive::Write3dmInstanceDefinition() - m_active_table != instance_definition_table");
08609   }
08610 
08611   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
08612   if ( c && c->m_typecode == TCODE_INSTANCE_DEFINITION_TABLE ) {
08613     rc = BeginWrite3dmChunk( TCODE_INSTANCE_DEFINITION_RECORD, 0 );
08614     if ( rc ) {
08615       rc = WriteObject( idef );
08616       if ( !EndWrite3dmChunk() )
08617         rc = false;
08618     }
08619   }
08620   else {
08621     ON_ERROR("ON_BinaryArchive::Write3dmInstanceDefinition() must be called in BeginWrite3dmInstanceDefinitionTable() block");
08622     rc = false;
08623   }
08624 
08625   return rc;
08626 }
08627 
08628 bool ON_BinaryArchive::EndWrite3dmInstanceDefinitionTable()
08629 {
08630   bool rc = false;
08631   rc = EndWrite3dmTable( TCODE_INSTANCE_DEFINITION_TABLE );
08632   return rc;
08633 }
08634 
08635 bool ON_BinaryArchive::BeginRead3dmInstanceDefinitionTable()
08636 {
08637   if ( m_3dm_version <= 2 ) {
08638     return true;
08639   }
08640   bool rc = false;
08641   rc = BeginRead3dmTable( TCODE_INSTANCE_DEFINITION_TABLE );
08642 
08643   if ( !rc )
08644   {
08645     // 1 November 2005 Dale Lear
08646     //    This fall back is slow but it has been finding
08647     //    layer and object tables in damaged files.  I'm
08648     //    adding it to the other BeginRead3dm...Table()
08649     //    functions when it makes sense.
08650     rc = FindMisplacedTable( 
08651                 0,
08652                 TCODE_INSTANCE_DEFINITION_TABLE, TCODE_INSTANCE_DEFINITION_RECORD,
08653                 ON_InstanceDefinition::m_ON_InstanceDefinition_class_id.Uuid(),
08654                 30 
08655                 );
08656     if ( rc )
08657     {
08658       rc = BeginRead3dmTable( TCODE_INSTANCE_DEFINITION_TABLE );
08659     }
08660   }
08661 
08662   return rc;
08663 }
08664 
08665 int ON_BinaryArchive::Read3dmInstanceDefinition( ON_InstanceDefinition** ppInstanceDefinition )
08666 {
08667   if ( !ppInstanceDefinition )
08668     return 0;
08669   *ppInstanceDefinition = 0;
08670   if ( m_3dm_version <= 2 ) {
08671     return 0;
08672   }
08673   if ( m_active_table != instance_definition_table )
08674   {
08675     ON_ERROR("ON_BinaryArchive::BeginRead3dmInstanceDefinitionTable() - m_active_table != no_active_table");
08676   }
08677   if ( m_3dm_opennurbs_version < 200205110 ) 
08678   {
08679     // 3DM archives written before version 200205110 and before do not have instance definition tables
08680     return 0;
08681   }
08682 
08683   ON__UINT32 tcode = 0;
08684   ON__INT64 big_value = 0;
08685   ON_InstanceDefinition* idef = NULL;
08686   if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) 
08687   {
08688     if ( tcode == TCODE_INSTANCE_DEFINITION_RECORD ) {
08689       ON_Object* p = 0;
08690       if ( ReadObject( &p ) ) {
08691         idef = ON_InstanceDefinition::Cast(p);
08692         if ( !idef )
08693           delete p;
08694       }
08695       if (!idef) {
08696         ON_ERROR("ON_BinaryArchive::Read3dmInstanceDefinition() - corrupt instance definition table");
08697       }
08698     }
08699     else if ( tcode != TCODE_ENDOFTABLE ) {
08700       ON_ERROR("ON_BinaryArchive::Read3dmInstanceDefinition() - corrupt instance definition table");
08701     }
08702     EndRead3dmChunk();
08703   }
08704   *ppInstanceDefinition = idef;
08705   return (idef) ? 1 : 0;
08706 }
08707 
08708 bool ON_BinaryArchive::EndRead3dmInstanceDefinitionTable()
08709 {
08710   bool rc = false;
08711   if ( m_3dm_version <= 2 ) {
08712     return true;
08713   }
08714   else {
08715     rc = EndRead3dmTable( TCODE_INSTANCE_DEFINITION_TABLE );
08716   }
08717   return rc;
08718 }
08719 
08725 
08726 bool ON_BinaryArchive::BeginWrite3dmTextureMappingTable()
08727 {
08728   return BeginWrite3dmTable( TCODE_TEXTURE_MAPPING_TABLE );
08729 }
08730 
08731 bool ON_BinaryArchive::Write3dmTextureMapping( const ON_TextureMapping& texture_mapping )
08732 {
08733   bool rc = false;
08734 
08735   if ( m_active_table != texture_mapping_table ) 
08736   {
08737     ON_ERROR("ON_BinaryArchive::Write3dmTextureMapping() - m_active_table != texture_mapping_table");
08738   }
08739 
08740   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
08741   if ( !c || c->m_typecode != TCODE_TEXTURE_MAPPING_TABLE )
08742   {
08743     ON_ERROR("ON_BinaryArchive::Write3dmTextureMapping() - active chunk typecode != TCODE_TEXTURE_MAPPING_TABLE");
08744   }
08745   else 
08746   {
08747     rc = BeginWrite3dmChunk( TCODE_TEXTURE_MAPPING_RECORD, 0 );
08748     if (rc) 
08749     {
08750       rc = WriteObject( texture_mapping );
08751       if ( !EndWrite3dmChunk() )
08752         rc = false;
08753     }
08754   }
08755   return rc;
08756 }
08757 
08758 bool ON_BinaryArchive::EndWrite3dmTextureMappingTable()
08759 {
08760   return EndWrite3dmTable( TCODE_TEXTURE_MAPPING_TABLE );
08761 }
08762 
08763 bool ON_BinaryArchive::BeginRead3dmTextureMappingTable()
08764 {
08765   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200511110 )
08766   {
08767     return true;
08768   }
08769 
08770   bool rc = BeginRead3dmTable( TCODE_TEXTURE_MAPPING_TABLE );
08771   if ( !rc )
08772   {
08773     // 31 October 2005 Dale Lear
08774     //    This fall back is slow but it will find
08775     //    texture_mapping tables in files that have been damaged.
08776     //
08777     //    This approach has been tested with layer tables
08778     //    for over a year and has successfully made files
08779     //    with garbled starts read correctly after the
08780     //    call to EmergencyFindTable was able to detect 
08781     //    the start of the layer table.  I'm adding it
08782     //    to texture_mapping tables now because I have a good
08783     //    test file.
08784     rc = FindMisplacedTable( 
08785                 0,
08786                 TCODE_TEXTURE_MAPPING_TABLE, TCODE_TEXTURE_MAPPING_RECORD,
08787                 ON_TextureMapping::m_ON_TextureMapping_class_id.Uuid(),
08788                 sizeof(ON_TextureMapping)
08789                 );
08790     if ( rc )
08791     {
08792       rc = BeginRead3dmTable( TCODE_TEXTURE_MAPPING_TABLE );
08793     }
08794   }
08795   return rc;
08796 }
08797 
08798 int ON_BinaryArchive::Read3dmTextureMapping( ON_TextureMapping** ppTextureMapping )
08799 {
08800   int rc = 0;
08801   if ( !ppTextureMapping )
08802     return 0;
08803   *ppTextureMapping = 0;
08804   ON_TextureMapping* texture_mapping = NULL;
08805   ON__UINT32 tcode = 0;
08806   ON__INT64 big_value = 0;
08807   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200511110 ) 
08808   {
08809     // no texture mapping table until version 200511110 of v4 files
08810     return 0;
08811   }
08812 
08813   rc = -1;
08814   if ( BeginRead3dmBigChunk( &tcode, &big_value ) )
08815   {
08816     if ( tcode == TCODE_TEXTURE_MAPPING_RECORD ) 
08817     {
08818       ON_Object* p = 0;
08819       if ( ReadObject( &p ) ) 
08820       {
08821         texture_mapping = ON_TextureMapping::Cast(p);
08822         if ( !texture_mapping )
08823           delete p;
08824         else 
08825         {
08826           if ( ppTextureMapping )
08827             *ppTextureMapping = texture_mapping;
08828           rc = 1;
08829         }
08830       }
08831       if (!texture_mapping)
08832       {
08833         ON_ERROR("ON_BinaryArchive::Read3dmTextureMapping() - corrupt texture_mapping table");
08834       }
08835     }
08836     else if ( tcode == TCODE_ENDOFTABLE ) 
08837     {
08838       // end of texture_mapping table
08839       rc = 0;
08840     }
08841     else 
08842     {
08843       ON_ERROR("ON_BinaryArchive::Read3dmTextureMapping() - corrupt texture_mapping table");
08844     }
08845     if ( !EndRead3dmChunk() )
08846       rc = -1;
08847   }
08848 
08849   return rc;
08850 }
08851 
08852 bool ON_BinaryArchive::EndRead3dmTextureMappingTable()
08853 {
08854   bool rc = false;
08855   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200511110 )
08856   {
08857     rc = true;
08858   }
08859   else
08860   {
08861     rc = EndRead3dmTable( TCODE_TEXTURE_MAPPING_TABLE );
08862   }
08863   return rc;
08864 }
08865 
08871 
08872 bool ON_BinaryArchive::BeginWrite3dmHistoryRecordTable()
08873 {
08874   return BeginWrite3dmTable( TCODE_HISTORYRECORD_TABLE );
08875 }
08876 
08877 bool ON_BinaryArchive::Write3dmHistoryRecord( const ON_HistoryRecord& history_record )
08878 {
08879   bool rc = false;
08880 
08881   if ( m_active_table != historyrecord_table ) 
08882   {
08883     ON_ERROR("ON_BinaryArchive::Write3dmHistoryRecord() - m_active_table != history_record_table");
08884   }
08885 
08886   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
08887   if ( !c || c->m_typecode != TCODE_HISTORYRECORD_TABLE )
08888   {
08889     ON_ERROR("ON_BinaryArchive::Write3dmHistoryRecord() - active chunk typecode != TCODE_HISTORYRECORD_TABLE");
08890   }
08891   else 
08892   {
08893     rc = BeginWrite3dmChunk( TCODE_HISTORYRECORD_RECORD, 0 );
08894     if (rc) 
08895     {
08896       rc = WriteObject( history_record );
08897       if ( !EndWrite3dmChunk() )
08898         rc = false;
08899     }
08900   }
08901   return rc;
08902 }
08903 
08904 bool ON_BinaryArchive::EndWrite3dmHistoryRecordTable()
08905 {
08906   return EndWrite3dmTable( TCODE_HISTORYRECORD_TABLE );
08907 }
08908 
08909 bool ON_BinaryArchive::BeginRead3dmHistoryRecordTable()
08910 {
08911   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200601180 )
08912   {
08913     return true;
08914   }
08915 
08916   bool rc = BeginRead3dmTable( TCODE_HISTORYRECORD_TABLE );
08917   if ( !rc )
08918   {
08919     // 31 October 2005 Dale Lear
08920     //    This fall back is slow but it will find
08921     //    history_record tables in files that have been damaged.
08922     //
08923     //    This approach has been tested with layer tables
08924     //    for over a year and has successfully made files
08925     //    with garbled starts read correctly after the
08926     //    call to EmergencyFindTable was able to detect 
08927     //    the start of the layer table.  I'm adding it
08928     //    to history_record tables now because I have a good
08929     //    test file.
08930     rc = FindMisplacedTable( 
08931                 0,
08932                 TCODE_HISTORYRECORD_TABLE, TCODE_HISTORYRECORD_RECORD,
08933                 ON_HistoryRecord::m_ON_HistoryRecord_class_id.Uuid(),
08934                 sizeof(ON_HistoryRecord)
08935                 );
08936     if ( rc )
08937     {
08938       rc = BeginRead3dmTable( TCODE_HISTORYRECORD_TABLE );
08939     }
08940   }
08941   return rc;
08942 }
08943 
08944 int ON_BinaryArchive::Read3dmHistoryRecord( ON_HistoryRecord*& history_record )
08945 {
08946   int rc = 0;
08947   history_record = 0;
08948   ON__UINT32 tcode = 0;
08949   ON__INT64 big_value = 0;
08950   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200601180 ) 
08951   {
08952     // no history record table until version 200601180 of v4 files
08953     return 0;
08954   }
08955 
08956   rc = -1;
08957   if ( BeginRead3dmBigChunk( &tcode, &big_value ) )
08958   {
08959     if ( tcode == TCODE_HISTORYRECORD_RECORD ) 
08960     {
08961       ON_Object* p = 0;
08962       if ( ReadObject( &p ) ) 
08963       {
08964         history_record = ON_HistoryRecord::Cast(p);
08965         if ( !history_record )
08966         {
08967           delete p;
08968         }
08969         else 
08970         {
08971           rc = 1;
08972         }
08973       }
08974       if (!history_record)
08975       {
08976         ON_ERROR("ON_BinaryArchive::Read3dmHistoryRecord() - corrupt history_record table");
08977       }
08978     }
08979     else if ( tcode == TCODE_ENDOFTABLE ) 
08980     {
08981       // end of history_record table
08982       rc = 0;
08983     }
08984     else 
08985     {
08986       ON_ERROR("ON_BinaryArchive::Read3dmHistoryRecord() - corrupt history_record table");
08987     }
08988     if ( !EndRead3dmChunk() )
08989       rc = -1;
08990   }
08991 
08992   return rc;
08993 }
08994 
08995 bool ON_BinaryArchive::EndRead3dmHistoryRecordTable()
08996 {
08997   bool rc = false;
08998   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200601180 )
08999   {
09000     rc = true;
09001   }
09002   else
09003   {
09004     rc = EndRead3dmTable( TCODE_HISTORYRECORD_TABLE );
09005   }
09006   return rc;
09007 }
09008 
09014 
09015 bool ON_BinaryArchive::BeginWrite3dmMaterialTable()
09016 {
09017   return BeginWrite3dmTable( TCODE_MATERIAL_TABLE );
09018 }
09019 
09020 bool ON_BinaryArchive::Write3dmMaterial( const ON_Material& material )
09021 {
09022   bool rc = false;
09023 
09024   if ( m_active_table != material_table ) 
09025   {
09026     ON_ERROR("ON_BinaryArchive::Write3dmMaterial() - m_active_table != material_table");
09027   }
09028 
09029   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
09030   if ( !c || c->m_typecode != TCODE_MATERIAL_TABLE )
09031   {
09032     ON_ERROR("ON_BinaryArchive::Write3dmMaterial() - active chunk typecode != TCODE_MATERIAL_TABLE");
09033   }
09034   else 
09035   {
09036     rc = BeginWrite3dmChunk( TCODE_MATERIAL_RECORD, 0 );
09037     if (rc) 
09038     {
09039       rc = WriteObject( material );
09040       if ( !EndWrite3dmChunk() )
09041         rc = false;
09042     }
09043   }
09044   return rc;
09045 }
09046 
09047 bool ON_BinaryArchive::EndWrite3dmMaterialTable()
09048 {
09049   return EndWrite3dmTable( TCODE_MATERIAL_TABLE );
09050 }
09051 
09052 bool ON_BinaryArchive::BeginRead3dmMaterialTable()
09053 {
09054   m_3dm_v1_material_index = 0;
09055   bool rc = BeginRead3dmTable( TCODE_MATERIAL_TABLE );
09056   if ( !rc )
09057   {
09058     // 31 October 2005 Dale Lear
09059     //    This fall back is slow but it will find
09060     //    material tables in files that have been damaged.
09061     //
09062     //    This approach has been tested with layer tables
09063     //    for over a year and has successfully made files
09064     //    with garbled starts read correctly after the
09065     //    call to EmergencyFindTable was able to detect 
09066     //    the start of the layer table.  I'm adding it
09067     //    to material tables now because I have a good
09068     //    test file.
09069     rc = FindMisplacedTable( 
09070                 0,
09071                 TCODE_MATERIAL_TABLE, TCODE_MATERIAL_RECORD,
09072                 ON_Material::m_ON_Material_class_id.Uuid(),
09073                 114
09074                 );
09075     if ( rc )
09076     {
09077       rc = BeginRead3dmTable( TCODE_MATERIAL_TABLE );
09078     }
09079   }
09080   return rc;
09081 }
09082 
09083 bool ON_BinaryArchive::Read3dmV1String( ON_String& s )
09084 {
09085   int string_length = 0;
09086   s.Empty();
09087   bool rc = ReadInt( &string_length );
09088   if (rc) {
09089     s.ReserveArray(string_length+1);
09090     rc = ReadChar( string_length, s.Array() );
09091     if (rc)
09092       s.SetLength(string_length);
09093   }
09094   return rc;
09095 }
09096 
09097 
09098 class ON__3dmV1_XDATA
09099 {
09100   // helper class to get V1 "xdata" out of attributes block.
09101 public:
09102   enum
09103   {
09104     unknown_xdata = 0,
09105     hidden_object_layer_name, // m_string = actual layer name
09106     locked_object_layer_name, // m_string = actual layer name
09107     arrow_direction,          // m_vector = arrow head location
09108     dot_text                  // m_string = dot text
09109   }
09110   m_type;
09111   ON_String m_string; 
09112   ON_3dVector m_vector;
09113 };
09114 
09115 bool ON_BinaryArchive::Read3dmV1AttributesOrMaterial( 
09116                          ON_3dmObjectAttributes* attributes,
09117                          ON_Material* material,
09118                          ON_BOOL32& bHaveMat,
09119                          unsigned int end_mark_tcode,
09120                          ON__3dmV1_XDATA* xdata
09121                          )
09122 {
09123   // Check ReadV1Material() if you fix any bugs in the mateial related items
09124 
09125   if ( 0 != xdata )
09126   {
09127     xdata->m_type = ON__3dmV1_XDATA::unknown_xdata;
09128   }
09129 
09130   bool rc = false;
09131   unsigned int u;
09132   ON__UINT32 tcode = 0;
09133   ON__INT64 big_value = 0;
09134   ON_Color c;
09135   bHaveMat = false;
09136   bool bEndRead3dmChunk_rc;
09137 
09138   const unsigned int saved_error_message_mask = m_error_message_mask;
09139 
09140   int xdata_layer_index = -1;
09141 
09142   if ( attributes )
09143   {
09144     attributes->Default();
09145   }
09146 
09147   if ( material ) 
09148   {
09149     material->Default();
09150     material->m_diffuse.SetRGB(255,255,255);
09151     material->m_specular.SetRGB(255,255,255);
09152     material->m_ambient.SetRGB(0,0,0);
09153   }
09154 
09155   for (;;) 
09156   {
09157     m_error_message_mask = saved_error_message_mask;
09158 
09159     if ( end_mark_tcode != TCODE_ENDOFTABLE ) {
09160       tcode = 0;
09161       big_value = 0;
09162       if ( !PeekAt3dmBigChunkType(&tcode,&big_value) ) {
09163         break; // should not happen
09164       }
09165       if ( tcode == end_mark_tcode ) {
09166         rc = true;
09167         break; // done reading attributes
09168       }
09169     }
09170     tcode = 0;
09171     big_value = 0;
09172     if ( !BeginRead3dmBigChunk(&tcode,&big_value) )
09173       break;
09174     if ( tcode == end_mark_tcode ) {
09175       rc = EndRead3dmChunk();
09176       break;
09177     }
09178     
09179     switch( tcode ) 
09180     {      
09181     case (TCODE_OPENNURBS_OBJECT | TCODE_CRC | 0x7FFD):
09182       // 1.1 object 16 byte UUID + 2 byte crc
09183       if ( attributes )
09184         ReadUuid( attributes->m_uuid );
09185       break;
09186       
09187     case TCODE_LAYERREF:
09188       if (    attributes 
09189            && (-1 == xdata_layer_index || attributes->m_layer_index != xdata_layer_index) 
09190            && (big_value >= 0 && big_value < 0x7FFFFFFF)
09191          )
09192       {
09193         attributes->m_layer_index = (int)big_value;
09194       }
09195       break;
09196       
09197     case TCODE_RGB:
09198       if ( big_value != 0xFFFFFF ) 
09199       {
09200         if ( material ) 
09201         {
09202           ON__UINT64 rgb64 = (ON__UINT64)big_value;
09203           ON__UINT32 rgb32 = (ON__UINT32)rgb64;
09204           u = rgb32;
09205           c.SetRGB( u%256,(u>>8)%256,(u>>16)%256 );
09206           material->SetDiffuse(c);
09207           material->SetShine((u >> 24)/100.0*ON_Material::MaxShine());
09208         }
09209         bHaveMat = true;
09210       }
09211       break;            
09212       
09213     case TCODE_RGBDISPLAY:
09214       if ( attributes ) 
09215       {
09216           ON__UINT64 rgb64 = (ON__UINT64)big_value;
09217           ON__UINT32 rgb32 = (ON__UINT32)rgb64;
09218           u = rgb32;
09219         attributes->m_color.SetRGB( u%256,(u>>8)%256,(u>>16)%256 );
09220       }
09221       break;
09222       
09223     case TCODE_TRANSPARENCY:
09224       if ( big_value > 0 && big_value <= 255 ) 
09225       {
09226         if ( material )
09227           material->SetTransparency(big_value/255.0);
09228         bHaveMat = true;
09229       }
09230       break;                            
09231       
09232     case TCODE_NAME:
09233       if ( attributes ) {
09234         ON_String s;
09235         Read3dmV1String(s);
09236         if( s.Length() > 0 )
09237           attributes->m_name = s;
09238       }
09239       break;
09240    
09241     case TCODE_TEXTUREMAP:
09242       {
09243         ON_String s;
09244         Read3dmV1String(s);
09245         if ( s.Length() > 0 ) 
09246         {
09247           if ( material )
09248           {
09249             ON_Texture& tx = material->m_textures.AppendNew();
09250             tx.m_filename = s;
09251             tx.m_type = ON_Texture::bitmap_texture;
09252           }
09253           bHaveMat = true;
09254         }
09255       }
09256       break;
09257 
09258     case TCODE_BUMPMAP:
09259       if ( material ) {
09260         ON_String s;
09261         Read3dmV1String(s);
09262         if ( s.Length() ) 
09263         {
09264           if ( material )
09265           {
09266             ON_Texture& tx = material->m_textures.AppendNew();
09267             tx.m_filename = s;
09268             tx.m_type = ON_Texture::bump_texture;
09269           }
09270           bHaveMat = true;
09271         }
09272       }
09273       break;
09274       
09275     case TCODE_XDATA:
09276       // v1 "xdata"
09277       if ( attributes ) 
09278       {
09279         ON_String layer_name;
09280         ON_String xid;
09281         int sizeof_xid = 0;
09282         int sizeof_data = 0;
09283         ReadInt(&sizeof_xid);
09284         ReadInt(&sizeof_data);
09285         xid.SetLength(sizeof_xid);
09286         ReadByte(sizeof_xid,xid.Array());
09287         if ( !on_stricmp("RhHidePrevLayer",xid) )
09288         {
09289           // v1 object is hidden - real layer name is in xdata
09290           char* buffer = (char*)alloca((sizeof_data+1)*sizeof(buffer[0]));
09291           buffer[0] = 0;
09292           buffer[sizeof_data] = 0;
09293           if ( ReadByte(sizeof_data,buffer) )
09294           {
09295             if ( -1 == xdata_layer_index )
09296             {
09297               xdata_layer_index = Read3dmV1LayerIndex(buffer);
09298               if ( xdata_layer_index >= 0 )
09299               {
09300                 attributes->m_layer_index = xdata_layer_index;
09301                 attributes->SetVisible(false);
09302               }
09303             }
09304             else
09305             {
09306               xdata_layer_index = -2;
09307             }
09308             //if ( 0 != xdata )
09309             //{
09310             //  xdata->m_type = ON__3dmV1_XDATA::hidden_object_layer_name;
09311             //  xdata->m_string = buffer;               
09312             //}
09313           }
09314         }
09315         else if ( !on_stricmp("RhFreezePrevLayer",xid) )
09316         {
09317           // v1 object is locked - real layer name is in xdata
09318           char* buffer = (char*)alloca((sizeof_data+1)*sizeof(buffer[0]));
09319           buffer[0] = 0;
09320           buffer[sizeof_data] = 0;
09321           if ( ReadByte(sizeof_data,buffer)  )
09322           {
09323             if ( -1 == xdata_layer_index )
09324             {
09325               xdata_layer_index = Read3dmV1LayerIndex(buffer);
09326               if ( xdata_layer_index >= 0 )
09327               {
09328                 attributes->m_layer_index = xdata_layer_index;
09329                 attributes->SetMode(ON::locked_object);
09330               }
09331             }
09332             else
09333             {
09334               xdata_layer_index = -2;
09335             }
09336             //if ( 0 != xdata )
09337             //{
09338             //  xdata->m_type = ON__3dmV1_XDATA::locked_object_layer_name;
09339             //  xdata->m_string = buffer;            
09340             //}
09341           }
09342         }
09343         else if ( !on_stricmp("RhAnnotateArrow",xid) && 24 == sizeof_data )
09344         {
09345           // v1 annotation arrow objects were saved
09346           // as TCODE_RH_POINT objects with the
09347           // arrow tail location = point location and the
09348           // arrow head location saved in 24 bytes of "xdata".
09349           ON_3dVector arrow_direction;
09350           if ( ReadVector( arrow_direction ) && 0 != xdata )
09351           {
09352             xdata->m_type = ON__3dmV1_XDATA::arrow_direction;
09353             xdata->m_vector = arrow_direction;
09354           }
09355         }
09356         else if ( !on_stricmp("RhAnnotateDot",xid) )
09357         {
09358           // v1 annotation dot objects were saved
09359           // as TCODE_RH_POINT objects with the
09360           // dot text saved in "xdata".
09361           char* buffer = (char*)alloca((sizeof_data+1)*sizeof(buffer[0]));
09362           buffer[0] = 0;
09363           buffer[sizeof_data] = 0;
09364           if ( ReadByte(sizeof_data,buffer) && 0 != xdata )
09365           {
09366             xdata->m_type = ON__3dmV1_XDATA::dot_text;
09367             xdata->m_string = buffer;            
09368           }
09369         }
09370         else 
09371         {
09372           m_error_message_mask |= 0x0002; // disable v1 EndRead3dmChunk() partially read chunk warning
09373         }
09374         // call to EndRead3dmChunk() will skip unread junk
09375       }
09376       break;
09377       
09378     case TCODE_DISP_CPLINES:
09379       if ( big_value > 0 && big_value <= 0x7FFFFFFF && attributes )
09380         attributes->m_wire_density = (int)big_value;
09381       break;
09382 
09383     case TCODE_RENDER_MATERIAL_ID:
09384       {
09385         int flag;
09386         ON_String s;
09387         ReadInt(&flag);
09388         if ( flag == 1 ) {
09389           Read3dmV1String(s);
09390           if ( s.Length() > 0 ) 
09391           {
09392             if ( material )
09393               material->m_material_name = s;
09394             bHaveMat = true;
09395           }
09396         }
09397       }
09398       break;
09399       
09400     default:
09401       // obsolete attributes from v1
09402       m_error_message_mask |= 0x02; // disable v1 EndRead3dmChunk() partially read chunk warning
09403       break;
09404     }
09405 
09406     bEndRead3dmChunk_rc = EndRead3dmChunk();
09407     if ( !bEndRead3dmChunk_rc )
09408       break;
09409   }
09410 
09411   m_error_message_mask = saved_error_message_mask;
09412 
09413   if ( bHaveMat ) {
09414     if ( attributes )
09415       attributes->m_material_index = m_3dm_v1_material_index;
09416     if ( material )
09417       material->SetMaterialIndex(m_3dm_v1_material_index);
09418     m_3dm_v1_material_index++;
09419   }
09420 
09421   return rc;
09422 }
09423 
09424 
09425 
09426 int ON_BinaryArchive::Read3dmV1Material( ON_Material** ppMaterial )
09427 {
09428   int rc = 0;
09429   // returns -1: failure
09430   //          0: end of material table
09431   //          1: success
09432 
09433   ON_Material material;
09434   ON__UINT32 tcode = 0;
09435   ON__INT64 big_value = 0;
09436   ON_BOOL32 bHaveMat;
09437   bool bEndReadChunk_rc;
09438   // reads NURBS, point, and mesh objects
09439 
09440   while( 0 == rc )
09441   {
09442     bHaveMat = false;
09443     rc = 0;
09444     tcode = 0;
09445     big_value = 0;
09446     if ( !BeginRead3dmBigChunk(&tcode,&big_value) ) 
09447     {
09448       // assume we are at the end of the file
09449       break;
09450     }
09451 
09452     switch(tcode)
09453     {
09454     case TCODE_RH_POINT:
09455       // v1 3d point
09456       {
09457         ON_3DM_BIG_CHUNK* point_chunk = m_chunk.Last();
09458         ON__UINT64 pos0 = 0;
09459         if (    0 != point_chunk 
09460              && TCODE_RH_POINT == point_chunk->m_typecode 
09461              && 0 == point_chunk->m_big_value )
09462         {
09463           // Some V1 files have TCODE_RH_POINT chunks with length=0.
09464           // (It appears that points with arrow xdata have this problem.)
09465           // For these chunks we need to set the chunk length so EndRead3dmChunk()
09466           // will keep going.
09467           pos0 = CurrentPosition();
09468         }
09469         else
09470           point_chunk = 0;
09471 
09472         ON_3dPoint pt; // need to read point to get to material defn
09473         bool bOK = ReadPoint( pt );
09474         
09475         if ( bOK )
09476           bOK = Read3dmV1AttributesOrMaterial( NULL, &material, bHaveMat, TCODE_ENDOFTABLE );
09477         
09478         if ( !bOK )
09479           rc = -1;
09480         // else if appropriate, rc will get set to +1 below.
09481 
09482         if ( bOK
09483              && 0 != point_chunk 
09484              && point_chunk == m_chunk.Last()
09485              && TCODE_RH_POINT == point_chunk->m_typecode 
09486              && 0 == point_chunk->m_big_value )
09487         {
09488           // set the chunk length so that reading can continue.
09489           ON__UINT64 pos1 = CurrentPosition();
09490           ON__UINT64 chunk_length = (pos1 > pos0) ? (pos1 - pos0) : 0;
09491           if ( chunk_length >= 32 && chunk_length < 0x0FFFFFFF )
09492             point_chunk->m_big_value = (ON__INT64)chunk_length;
09493         }
09494       }
09495       break;
09496 
09497     case TCODE_MESH_OBJECT:
09498       // v1 mesh
09499       {
09500         ON__UINT32 tc = 0;
09501         ON__INT64 i64 = 0;
09502         if ( !PeekAt3dmBigChunkType( &tc, &i64 ) )
09503           break;
09504         if ( tc != TCODE_COMPRESSED_MESH_GEOMETRY )
09505           break;
09506         // skip over the TCODE_COMPRESSED_MESH_GEOMETRY chunk
09507         if ( !BeginRead3dmBigChunk(&tc,&i64) )