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) )
09508           break;
09509         if ( !EndRead3dmChunk() )
09510           break;
09511         // attributes and material informtion follow the TCODE_COMPRESSED_MESH_GEOMETRY chunk
09512         if ( !Read3dmV1AttributesOrMaterial( NULL, &material, bHaveMat, TCODE_ENDOFTABLE ) )
09513           rc = -1;
09514         // if appropriate, rc will get set to +1 below
09515       }
09516       break;
09517 
09518     case TCODE_LEGACY_SHL:
09519       // v1 polysurface
09520       if ( !Read3dmV1AttributesOrMaterial( NULL, &material, bHaveMat, TCODE_LEGACY_SHLSTUFF ) )
09521         rc = -1;
09522         // if appropriate, rc will get set to +1 below
09523       break;
09524     case TCODE_LEGACY_FAC:
09525       // v1 trimmed surface
09526       if ( !Read3dmV1AttributesOrMaterial( NULL, &material, bHaveMat, TCODE_LEGACY_FACSTUFF ) )
09527         rc = -1;
09528         // if appropriate, rc will get set to +1 below
09529       break;
09530     case TCODE_LEGACY_CRV:
09531       // v1 curve
09532       if ( !Read3dmV1AttributesOrMaterial( NULL, &material, bHaveMat, TCODE_LEGACY_CRVSTUFF ) )
09533         rc = -1;
09534         // if appropriate, rc will get set to +1 below
09535       break;
09536 
09537     case TCODE_RHINOIO_OBJECT_NURBS_CURVE:
09538     case TCODE_RHINOIO_OBJECT_NURBS_SURFACE:
09539     case TCODE_RHINOIO_OBJECT_BREP:
09540       // old Rhino I/O toolkit nurbs curve, surface, and breps
09541       {
09542         ON__UINT32 tc = 0;
09543         ON__INT64 i64 = 0;
09544         if ( !PeekAt3dmBigChunkType( &tc, &i64 ) )
09545           break;
09546         if ( tc != TCODE_RHINOIO_OBJECT_DATA )
09547           break;
09548         // skip over the TCODE_RHINOIO_OBJECT_DATA chunk
09549         if ( !BeginRead3dmBigChunk(&tc,&i64) )
09550           break;
09551         if ( !EndRead3dmChunk() )
09552           break;
09553         if ( !Read3dmV1AttributesOrMaterial( NULL, &material, bHaveMat, TCODE_RHINOIO_OBJECT_END ) )
09554           rc = -1;
09555         // if appropriate, rc will get set to +1 below
09556       }
09557       break;
09558     }
09559 
09560     const unsigned int saved_error_message_mask = m_error_message_mask;
09561     m_error_message_mask |= 0x02; // disable v1 EndRead3dmChunk() partially read chunk warning
09562     bEndReadChunk_rc = EndRead3dmChunk();
09563     m_error_message_mask = saved_error_message_mask; // enable v1 EndRead3dmChunk() partially read chunk warning
09564     if (!bEndReadChunk_rc ) 
09565     {
09566       rc = -1;
09567       break;
09568     }
09569     if ( bHaveMat && ppMaterial) 
09570     {
09571       // found a valid non-default material
09572       *ppMaterial = new ON_Material(material);
09573       rc = 1;
09574       break;
09575     }
09576   }
09577 
09578   return rc;
09579 }
09580 
09581 
09582 int ON_BinaryArchive::Read3dmMaterial( ON_Material** ppMaterial )
09583 {
09584   int rc = 0;
09585   if ( !ppMaterial )
09586     return 0;
09587   *ppMaterial = 0;
09588   ON_Material* material = NULL;
09589   ON__UINT32 tcode = 0;
09590   ON__INT64 big_value = 0;
09591   if ( m_3dm_version == 1 ) 
09592   {
09593     rc = ON_BinaryArchive::Read3dmV1Material( ppMaterial );
09594   }
09595   else 
09596   {
09597     // version 2+
09598     rc = -1;
09599     if ( BeginRead3dmBigChunk( &tcode, &big_value ) )
09600     {
09601       if ( tcode == TCODE_MATERIAL_RECORD ) 
09602       {
09603         ON_Object* p = 0;
09604         if ( ReadObject( &p ) ) 
09605         {
09606           material = ON_Material::Cast(p);
09607           if ( !material )
09608             delete p;
09609           else 
09610           {
09611             if ( ppMaterial )
09612               *ppMaterial = material;
09613             rc = 1;
09614           }
09615         }
09616         if (!material)
09617         {
09618           ON_ERROR("ON_BinaryArchive::Read3dmMaterial() - corrupt material table");
09619         }
09620       }
09621       else if ( tcode == TCODE_ENDOFTABLE ) 
09622       {
09623         // end of material table
09624         rc = 0;
09625       }
09626       else 
09627       {
09628         ON_ERROR("ON_BinaryArchive::Read3dmMaterial() - corrupt material table");
09629       }
09630       if ( !EndRead3dmChunk() )
09631         rc = -1;
09632     }
09633   }
09634   return rc;
09635 }
09636 
09637 bool ON_BinaryArchive::EndRead3dmMaterialTable()
09638 {
09639   return EndRead3dmTable( TCODE_MATERIAL_TABLE );
09640 }
09641 
09642 
09643 bool ON_BinaryArchive::BeginWrite3dmLightTable()
09644 {
09645   return BeginWrite3dmTable( TCODE_LIGHT_TABLE );
09646 }
09647 
09648 bool ON_BinaryArchive::Write3dmLight( const ON_Light& light,  const ON_3dmObjectAttributes* attributes )
09649 {
09650   bool rc = false;
09651   if ( m_active_table != light_table ) {
09652     ON_ERROR("ON_BinaryArchive::Write3dmLight() - m_active_table != light_table");
09653   }
09654   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
09655   if ( c && c->m_typecode == TCODE_LIGHT_TABLE ) {
09656     rc = BeginWrite3dmChunk( TCODE_LIGHT_RECORD, 0 );
09657     if (rc) {
09658       // WriteObject writes TCODE_OPENNURBS_CLASS chunk that contains light definition
09659       rc = WriteObject( light );
09660 
09661       // optional TCODE_LIGHT_RECORD_ATTRIBUTES chunk
09662       if ( rc && attributes ) 
09663       {
09664         rc = BeginWrite3dmChunk( TCODE_LIGHT_RECORD_ATTRIBUTES, 0 );
09665         if (rc) 
09666         {
09667           rc = attributes->Write( *this )?true:false;
09668           if (!EndWrite3dmChunk())
09669             rc = false;
09670           if( rc 
09671               && (m_bSaveUserData || HasCriticalUserData(*this,attributes))
09672               && Archive3dmVersion() >= 4 
09673               && 0 != attributes->FirstUserData() 
09674             )
09675           {
09676             // 14 May 2008 Dale Lear
09677             //    Added support for saving light attribute userdata
09678             rc = BeginWrite3dmChunk( TCODE_LIGHT_RECORD_ATTRIBUTES_USERDATA, 0 );
09679             if (rc)
09680             {
09681               // write user data
09682               rc = WriteObjectUserData(*attributes);
09683               if (rc)
09684               {
09685                 // Because I'm not using Write3dmObject() to write
09686                 // the attributes, the user data must be immediately 
09687                 // followed by a short TCODE_OPENNURBS_CLASS_END chunk 
09688                 // in order for ReadObjectUserData() to work correctly.
09689                 //
09690                 // The reason that this is hacked in is that V3 files did
09691                 // not support attribute user data and doing it this way
09692                 // means that V3 can still read V4 files.
09693                 rc = BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_END,0);
09694                 if (rc)
09695                 {
09696                   if (!EndWrite3dmChunk())
09697                     rc = false;
09698                 }
09699               }
09700               if (!EndWrite3dmChunk())
09701                 rc = false;
09702             }
09703           }
09704         }
09705       }
09706 
09707       // TCODE_LIGHT_RECORD_END chunk marks end of light record
09708       if ( BeginWrite3dmChunk( TCODE_LIGHT_RECORD_END, 0 ) ) {
09709         if (!EndWrite3dmChunk())
09710           rc = false;
09711       }
09712       else {
09713         rc = false;
09714       }
09715 
09716       if ( !EndWrite3dmChunk() ) // end of TCODE_LIGHT_RECORD
09717         rc = false;
09718     }
09719   }
09720   else {
09721     ON_ERROR("ON_BinaryArchive::Write3dmMaterial() - active chunk typecode != TCODE_LIGHT_TABLE");
09722   }
09723   return rc;
09724 }
09725 
09726 bool ON_BinaryArchive::EndWrite3dmLightTable()
09727 {
09728   return EndWrite3dmTable( TCODE_LIGHT_TABLE );
09729 }
09730 
09731 bool ON_BinaryArchive::BeginRead3dmLightTable()
09732 {
09733   bool rc = BeginRead3dmTable( TCODE_LIGHT_TABLE );
09734 
09735   if ( !rc )
09736   {
09737     // 1 November 2005 Dale Lear
09738     //    This fall back is slow but it has been finding
09739     //    layer and object tables in damaged files.  I'm
09740     //    adding it to the other BeginRead3dm...Table()
09741     //    functions when it makes sense.
09742     rc = FindMisplacedTable( 
09743                 0,
09744                 TCODE_LIGHT_TABLE, TCODE_LIGHT_RECORD,
09745                 ON_Light::m_ON_Light_class_id.Uuid(),
09746                 30 
09747                 );
09748     if ( rc )
09749     {
09750       rc = BeginRead3dmTable( TCODE_LIGHT_TABLE );
09751     }
09752   }
09753 
09754   return rc;
09755 }
09756 
09757 int ON_BinaryArchive::Read3dmV1Light(  // returns 0 at end of light table
09758                     //         1 light successfully read
09759                     //        -1 if file is corrupt
09760           ON_Light** ppLight, // light returned here
09761           ON_3dmObjectAttributes* pAttributes// optional - if NOT NULL, object attributes are
09762                                   //            returned here
09763           )
09764 {
09765   ON_BOOL32 bHaveMat;
09766   ON_Material material;
09767   // TODO - read v1 lights
09768   if ( m_chunk.Count() != 0 ) {
09769     ON_ERROR("ON_BinaryArchive::Read3dmV1Light() m_chunk.Count() != 0");
09770     return false;
09771   }
09772   ON_BOOL32 rc = false;
09773   ON__UINT32 tcode = 0;
09774   ON__INT64 big_value = 0;
09775 
09776   // find TCODE_RH_SPOTLIGHT chunk
09777   for(;;)
09778   {
09779     if ( !BeginRead3dmBigChunk(&tcode,&big_value) )
09780       break; // assume we are at the end of the file
09781     if ( tcode == TCODE_RH_SPOTLIGHT ) {
09782       rc = 1;
09783       break;
09784     }
09785     if ( !EndRead3dmChunk() )
09786       break;
09787   }
09788   if (rc) {
09789     ON_3dPoint origin;
09790     ON_3dVector xaxis, yaxis;
09791     double radius;
09792     double height;
09793     double hotspot;
09794 
09795     for(;;)
09796     {
09797       rc = ReadPoint( origin );
09798       if (!rc) break;
09799       rc = ReadVector( xaxis );
09800       if (!rc) break;
09801       rc = ReadVector( yaxis );
09802       if (!rc) break;
09803       rc = ReadDouble( &radius );
09804       if (!rc) break;
09805       rc = ReadDouble( &height );
09806       if (!rc) break;
09807       rc = ReadDouble( &hotspot );
09808       if (!rc) break;
09809       if (ppLight ) 
09810       {
09811         ON_3dVector Z = ON_CrossProduct( xaxis, yaxis );
09812         ON_3dPoint  location = height*Z + origin;
09813         ON_3dVector direction = -Z;
09814 
09815         if( height > 0.0)
09816           direction *= height;
09817         ON_Light* light = new ON_Light;
09818         light->SetStyle( ON::world_spot_light );
09819         light->SetLocation(location);
09820         light->SetDirection(direction);
09821         light->SetSpotExponent( 64.0);
09822         if( radius > 0.0 && height > 0.0 )
09823           light->SetSpotAngleRadians( atan( radius/height));
09824         *ppLight = light;
09825       }
09826       break;
09827     }
09828 
09829     if (rc && ppLight && *ppLight) {
09830       bHaveMat = false;
09831       Read3dmV1AttributesOrMaterial(pAttributes,&material,bHaveMat,TCODE_ENDOFTABLE);
09832       if ( pAttributes )
09833         pAttributes->m_material_index = -1;
09834       if (bHaveMat)
09835         (*ppLight)->SetDiffuse(material.Diffuse());
09836     }
09837 
09838     if ( !EndRead3dmChunk() ) // end of TCODE_RH_SPOTLIGHT chunk
09839       rc = false;
09840   }
09841 
09842   return rc;
09843 }
09844 
09845 int ON_BinaryArchive::Read3dmLight( ON_Light** ppLight, ON_3dmObjectAttributes* attributes )
09846 {
09847   if ( attributes )
09848     attributes->Default();
09849   int rc = -1;
09850   if ( !ppLight )
09851     return 0;
09852   *ppLight = 0;
09853   if ( m_active_table != light_table ) {
09854     ON_ERROR("ON_BinaryArchive::Read3dmLight() - m_active_table != light_table");
09855   }
09856   else if ( m_3dm_version == 1 ) {
09857     rc = Read3dmV1Light( ppLight, attributes );
09858   }
09859   else {
09860     ON_Light* light = NULL;
09861     ON__UINT32 tcode = 0;
09862     ON__INT64 big_value = 0;
09863     if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) {
09864       if ( tcode == TCODE_LIGHT_RECORD ) {
09865         ON_Object* p = 0;
09866         if ( ReadObject( &p ) ) {
09867           light = ON_Light::Cast(p);
09868           if ( !light )
09869             delete p;
09870         }
09871         if (!light) {
09872           ON_ERROR("ON_BinaryArchive::Read3dmLight() - corrupt light table");
09873         }
09874         else {
09875           *ppLight = light;
09876           rc = 1;
09877         }
09878       }
09879       else if ( tcode != TCODE_ENDOFTABLE ) 
09880       {
09881         ON_ERROR("ON_BinaryArchive::Read3dmLight() - corrupt light table");
09882       }
09883       else
09884         rc = 0;
09885 
09886       while(rc==1) 
09887       {
09888         tcode = 0;
09889         big_value = 0;
09890         if (!BeginRead3dmBigChunk( &tcode, &big_value )) 
09891         {
09892           rc = -1;
09893           break;
09894         }
09895         if ( tcode == TCODE_LIGHT_RECORD_ATTRIBUTES && attributes ) 
09896         {
09897           if ( !attributes->Read( *this ) )
09898             rc = -1;
09899         }
09900         else if ( tcode == TCODE_LIGHT_RECORD_ATTRIBUTES_USERDATA )
09901         {
09902           if ( 0 != attributes )
09903           {
09904             // 14 May 2008
09905             //   Added support for reading user data on light attributes
09906             if ( !ReadObjectUserData(*attributes))
09907               rc = -1;
09908           }
09909         }
09910         if ( !EndRead3dmChunk() )
09911         {
09912           rc = -1;
09913           break;
09914         }
09915         if ( tcode == TCODE_LIGHT_RECORD_END )
09916           break;
09917       }
09918 
09919       EndRead3dmChunk();
09920     }
09921   }
09922   return rc;
09923 }
09924 
09925 bool ON_BinaryArchive::EndRead3dmLightTable()
09926 {
09927   return EndRead3dmTable( TCODE_LIGHT_TABLE );
09928 }
09929 
09930 bool ON_BinaryArchive::BeginWrite3dmObjectTable()
09931 {
09932   return BeginWrite3dmTable( TCODE_OBJECT_TABLE );
09933 }
09934 
09935 bool ON_BinaryArchive::Write3dmObject( 
09936           const ON_Object& object, 
09937           const ON_3dmObjectAttributes* attributes 
09938           )
09939 {
09940   bool rc = false;
09941   if ( m_active_table != object_table ) {
09942     ON_ERROR("ON_BinaryArchive::Write3dmObject() - m_active_table != object_table");
09943   }
09944 
09945   if ( Archive3dmVersion() <= 2 && object.ObjectType() == ON::pointset_object )
09946   {
09947     // There were no point clouds in V1 and V2 files and we cannot handle
09948     // this problem inside of ON_PointCloud::Write() because we have to
09949     // write multiple point objects.  (c.f. ON_Brep::Write()).
09950     const ON_PointCloud* pc = ON_PointCloud::Cast(&object);
09951     if ( 0 != pc )
09952     {
09953       int i, count = pc->PointCount();
09954       rc = true;
09955       for ( i = 0; i < count && rc ; i++ )
09956       {
09957         ON_Point pt( pc->m_P[i] );
09958         rc = Write3dmObject( pt, attributes );
09959       }
09960       return rc;
09961     }
09962   }
09963 
09964   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
09965   if ( c && c->m_typecode == TCODE_OBJECT_TABLE ) 
09966   {
09967     Flush();
09968     rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD, 0 );
09969     if (rc) {
09970       // TCODE_OBJECT_RECORD_TYPE chunk integer value that can be used
09971       // for skipping unwanted types of objects
09972       rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD_TYPE, object.ObjectType() );
09973       if (rc) {
09974         if (!EndWrite3dmChunk())
09975           rc = false;
09976       }
09977 
09978       // WriteObject writes TCODE_OPENNURBS_CLASS chunk that contains object definition
09979       rc = WriteObject( object );
09980 
09981       // optional TCODE_OBJECT_RECORD_ATTRIBUTES chunk
09982       if ( rc && attributes ) {
09983         rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD_ATTRIBUTES, 0 );
09984         if (rc) {
09985           rc = attributes->Write( *this )?true:false;
09986           if (!EndWrite3dmChunk())
09987             rc = false;
09988 
09989           if( rc 
09990               && (m_bSaveUserData || HasCriticalUserData(*this,attributes))
09991               && Archive3dmVersion() >= 4 
09992               && 0 != attributes->FirstUserData() 
09993               )
09994           {
09995             // 19 October 2004
09996             //   Added support for saving user data on object attributes
09997             rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA, 0 );
09998             if (rc)
09999             {
10000               // write user data
10001               rc = WriteObjectUserData(*attributes);
10002               if (rc)
10003               {
10004                 // Because I'm not using Write3dmObject() to write
10005                 // the attributes, the user data must be immediately 
10006                 // followed by a short TCODE_OPENNURBS_CLASS_END chunk 
10007                 // in order for ReadObjectUserData() to work correctly.
10008                 //
10009                 // The reason that this is hacked in is that V3 files did
10010                 // not support attribute user data and doing it this way
10011                 // means that V3 can still read V4 files.
10012                 rc = BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_END,0);
10013                 if (rc)
10014                 {
10015                   if (!EndWrite3dmChunk())
10016                     rc = false;
10017                 }
10018               }
10019               if (!EndWrite3dmChunk())
10020                 rc = false;
10021             }
10022           }
10023         }
10024       }
10025 
10026       // TCODE_OBJECT_RECORD_END chunk marks end of object record
10027       if ( BeginWrite3dmChunk( TCODE_OBJECT_RECORD_END, 0 ) ) {
10028         if (!EndWrite3dmChunk())
10029           rc = false;
10030       }
10031       else {
10032         rc = false;
10033       }
10034 
10035       if (!EndWrite3dmChunk()) // end of TCODE_OBJECT_RECORD
10036       {
10037         rc = false;
10038       }
10039       if (!Flush())
10040         rc = false;
10041     }
10042     else {
10043       ON_ERROR("ON_BinaryArchive::Write3dmObject() - active chunk typecode != TCODE_OBJECT_TABLE");
10044     }
10045   }
10046   return rc;
10047 }
10048 
10049 bool ON_BinaryArchive::EndWrite3dmObjectTable()
10050 {
10051   return EndWrite3dmTable( TCODE_OBJECT_TABLE );
10052 }
10053 
10054 bool ON_BinaryArchive::BeginRead3dmObjectTable()
10055 {
10056   m_3dm_v1_material_index = 0;
10057   bool rc = BeginRead3dmTable( TCODE_OBJECT_TABLE );
10058   if ( !rc )
10059   {
10060     // 8 October 2004 Dale Lear
10061     //    This fall back is slow but it will find
10062     //    object tables in files that have been damaged.
10063     rc = FindMisplacedTable( 
10064                 0,
10065                 TCODE_OBJECT_TABLE, TCODE_OBJECT_RECORD,
10066                 ON_nil_uuid,
10067                 26 // ON_Point data is 3 doubles + 2 byte version number
10068                 );
10069     if ( rc )
10070     {
10071       rc = BeginRead3dmTable( TCODE_OBJECT_TABLE );
10072     }
10073 
10074   }
10075   return rc;
10076 }
10077 
10078 bool ON_BinaryArchive::ReadV1_TCODE_RH_POINT(
10079   ON_Object** ppObject,
10080   ON_3dmObjectAttributes* pAttributes
10081   )
10082 {
10083   ON__UINT64 pos0 = 0;
10084   ON_3DM_BIG_CHUNK* point_chunk = m_chunk.Last();
10085 
10086   if (    0 != point_chunk 
10087        && TCODE_RH_POINT == point_chunk->m_typecode 
10088        && 0 == point_chunk->m_big_value )
10089   {
10090     // Some early V1 files have TCODE_RH_POINT chunks with arrow xdata
10091     // attached have a length set to zero.
10092     // For these chunks we need to set the chunk length so EndRead3dmChunk()
10093     // will keep going.
10094     pos0 = CurrentPosition();
10095   }
10096   else
10097     point_chunk = 0;
10098 
10099   // read v1 point
10100   bool rc = false;
10101   ON_BOOL32 bHaveMat = false;
10102   ON_3dPoint pt;
10103   ON__3dmV1_XDATA xdata;
10104   rc = ReadPoint(pt);
10105   if (rc) 
10106   {
10107     rc = Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_ENDOFTABLE,&xdata);
10108     // do switch even if Read3dmV1AttributesOrMaterial() fails
10109     switch ( xdata.m_type )
10110     {
10111     case ON__3dmV1_XDATA::arrow_direction:
10112       if ( xdata.m_vector.Length() > ON_ZERO_TOLERANCE )
10113       {
10114         ON_AnnotationArrow* arrow = new ON_AnnotationArrow();
10115         arrow->m_tail = pt;
10116         arrow->m_head = pt + xdata.m_vector;
10117         *ppObject = arrow;
10118       }
10119       else
10120       {
10121         *ppObject = new ON_Point(pt);
10122       }
10123       break;
10124 
10125     case ON__3dmV1_XDATA::dot_text:
10126       {
10127         ON_AnnotationTextDot* dot = new ON_AnnotationTextDot();
10128         dot->point = pt;
10129         dot->m_text = xdata.m_string;
10130         if ( dot->m_text.IsEmpty() )
10131           dot->m_text = " "; // a single blank
10132         *ppObject = dot;
10133       }
10134       break;
10135 
10136     default:
10137       *ppObject = new ON_Point(pt);
10138       break;
10139     }
10140   }
10141 
10142   // carefully test for the V1 zero length chunk bug
10143   if ( rc && pos0 > 0 && 0 != point_chunk && point_chunk == m_chunk.Last() )
10144   {
10145     if ( TCODE_RH_POINT == point_chunk->m_typecode && 0 == point_chunk->m_big_value )
10146     {
10147       // This TCODE_RH_POINT chunk has the zero length chunk bug
10148       // that was in some V1 files. 
10149       // Fill in the correct chunk length so that reading can continue.
10150       ON__UINT64 pos1 = CurrentPosition();
10151       ON__UINT64 chunk_length = (pos1 > pos0) ? (pos1 - pos0) : 0;
10152       if ( chunk_length >= 32 && chunk_length < 0x0FFFFFFF )
10153         point_chunk->m_big_value = (ON__INT64)chunk_length;
10154     }
10155   }
10156 
10157   return rc;
10158 }
10159 
10160 static
10161 void TweakAnnotationPlane( ON_Plane& plane )
10162 {
10163   // 10 November 2003 Dale Lear
10164   //   Fixed lots of bugs in annotation plane tweaking.
10165   //   Before the fix this block of code was cut-n-pasted 
10166   //   in three places.  The fabs() calls were wrong.  In addition
10167   //   and the
10168   //   .x values where tested and then the .y/.z values were set.
10169 
10170   //    if( fabs( plane.origin.x > 1e10 ))
10171   //      plane.origin.x = 0.0;
10172   //    if( fabs( plane.origin.y > 1e10 ))
10173   //      plane.origin.y = 0.0;
10174   //    if( fabs( plane.origin.z > 1e10 ))
10175   //      plane.origin.z = 0.0;
10176   //    
10177   //    if( fabs( plane.xaxis.x > 1e10 ))
10178   //      plane.xaxis.x = 1.0;
10179   //    if( fabs( plane.xaxis.x > 1e10 ))
10180   //      plane.xaxis.y = 0.0;
10181   //    if( fabs( plane.xaxis.x > 1e10 ))
10182   //      plane.xaxis.z = 0.0;
10183   //    
10184   //    if( fabs( plane.yaxis.x > 1e10 ))
10185   //      plane.yaxis.x = 0.0;
10186   //    if( fabs( plane.yaxis.x > 1e10 ))
10187   //      plane.yaxis.y = 1.0;
10188   //    if( fabs( plane.yaxis.x > 1e10 ))
10189   //      plane.yaxis.z = 0.0;
10190 
10191   // Lowell has decided that annotation plane
10192   // coordinates bigger than "too_big" should be
10193   // set to zero.
10194   const double too_big = 1.0e10;
10195 
10196   if( fabs( plane.origin.x ) > too_big )
10197     plane.origin.x = 0.0;
10198   if( fabs( plane.origin.y ) > too_big )
10199     plane.origin.y = 0.0;
10200   if( fabs( plane.origin.z ) > too_big )
10201     plane.origin.z = 0.0;
10202   
10203   if( fabs( plane.xaxis.x ) > too_big )
10204     plane.xaxis.x = 1.0;
10205   if( fabs( plane.xaxis.y ) > too_big )
10206     plane.xaxis.y = 0.0;
10207   if( fabs( plane.xaxis.z ) > too_big )
10208     plane.xaxis.z = 0.0;
10209   
10210   if( fabs( plane.yaxis.x ) > too_big )
10211     plane.yaxis.x = 0.0;
10212   if( fabs( plane.yaxis.y ) > too_big )
10213     plane.yaxis.y = 1.0;
10214   if( fabs( plane.yaxis.z ) > too_big )
10215     plane.yaxis.z = 0.0;
10216 
10217   plane.xaxis.Unitize();
10218   plane.yaxis.Unitize();
10219   plane.zaxis = ON_CrossProduct(plane.xaxis,plane.yaxis);
10220   plane.zaxis.Unitize();
10221   plane.UpdateEquation();
10222 }
10223 
10224 
10225 #define RHINO_ANNOTATION_SETTINGS_VERSION_1  1
10226 #define RHINO_LINEAR_DIMENSION_VERSION_1     1
10227 #define RHINO_RADIAL_DIMENSION_VERSION_1     1
10228 #define RHINO_ANGULAR_DIMENSION_VERSION_1    1
10229 #define RHINO_TEXT_BLOCK_VERSION_1           1
10230 #define RHINO_TEXT_BLOCK_VERSION_2           2
10231 #define RHINO_ANNOTATION_LEADER_VERSION_1    1
10232 
10233 #define BUFLEN 128
10234 
10235 static bool ReadV1_TCODE_ANNOTATION_Helper( ON_BinaryArchive& archive, char* buffer, ON_wString& tc )
10236 {
10237   char* cp = 0;
10238   int j = 0;
10239   if( !archive.ReadInt( &j))
10240     return false;
10241   size_t sz = (j+1)*sizeof(cp[0]);
10242   if( j > BUFLEN - 1 || !buffer )
10243   {
10244     cp = (char*)onmalloc( sz );
10245     if( !cp)
10246       return false;
10247   }
10248   else
10249   {
10250     cp = buffer;
10251   }
10252 
10253   memset( cp, 0, sz );
10254   if( !archive.ReadChar( j,  cp))
10255   {
10256     if ( cp != buffer )
10257       onfree(cp);
10258     return false;
10259   }
10260 
10261   cp[j] = 0;
10262   tc = cp;
10263   if ( cp != buffer )
10264     onfree( cp );
10265   return true;
10266 }
10267 
10268 bool ON_BinaryArchive::ReadV1_TCODE_ANNOTATION(
10269   unsigned int tcode,
10270   ON_Object** ppObject,
10271   ON_3dmObjectAttributes* pAttributes
10272   )
10273 {
10274   enum RhAnnotationType
10275   {
10276     Nothing = 0,
10277     TextBlock = 1,
10278     DimHorizontal = 2,
10279     DimVertical = 3,
10280     DimAligned = 4,
10281     DimRotated = 5,
10282     DimAngular = 6,
10283     DimDiameter = 7 ,
10284     DimRadius = 8,
10285     Leader = 9,
10286     DimLinear = 10,
10287   };
10288   
10289   bool rc = false;
10290   *ppObject = NULL;
10291   ON_wString tc;
10292   char buffer[BUFLEN];
10293   int i, j, k, byobject, version;
10294   //char* cp;
10295   double d, d4[4];
10296   //ON_3dPoint pt;
10297 
10298   switch( tcode)
10299   {
10300   case TCODE_TEXT_BLOCK:
10301     {
10302       // read the version id
10303       rc = ReadInt( &version);
10304       if ( rc &&
10305            (version == RHINO_TEXT_BLOCK_VERSION_1 ||
10306             version == RHINO_TEXT_BLOCK_VERSION_2) 
10307          )
10308       {
10309         //this is a version we can read....
10310         // this is a type flag that we throw away
10311         rc = ReadInt( &i);
10312         if( !rc)
10313           return rc;
10314  
10315         ON_TextEntity* text = new ON_TextEntity;
10316         text->SetType( ON::dtTextBlock);
10317 
10318         ON_Plane plane;
10319 
10320         // the entity plane 
10321         if( !ReadDouble( 3, &plane.origin.x))
10322           return false;
10323         if( !ReadDouble( 3, &plane.xaxis.x))
10324           return false;
10325         if( !ReadDouble( 3, &plane.yaxis.x))
10326           return false;
10327 
10328         // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane()
10329         TweakAnnotationPlane( plane );
10330 
10331         text->SetPlane( plane);
10332 
10333         // read string to display
10334         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10335           return false;
10336         text->SetUserText( tc.Array());
10337 
10338         // flags ( future )
10339         if( !ReadInt( 1, &j))
10340           return false;
10341 
10342         // settings byobject flag
10343         if( !ReadInt( 1, &byobject))
10344           return false;
10345 
10346         // depending on the value of byobject, more fields might be read here
10347 
10348         // facename
10349         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10350           return false;
10351         text->SetFaceName(tc);
10352 
10353         // face weight
10354         if( !ReadInt( 1, &j))
10355           return false;
10356         text->SetFontWeight( j);
10357 
10358         if( !ReadDouble( 1, &d))
10359           return false;
10360         text->SetHeight( d);
10361 
10362         // 2 extra doubles were written into the file by mistake in version 1
10363         if( version == RHINO_TEXT_BLOCK_VERSION_1 )
10364         {
10365           if( !ReadDouble( 1, &d))
10366             return false;
10367           if( !ReadDouble( 1, &d))
10368             return false;
10369         }
10370     
10371         if( text->UserText().Length() < 1 )
10372         {
10373           *ppObject = 0;
10374           return true;
10375         }
10376         *ppObject = text;
10377         rc = true;
10378       }
10379     }
10380     break;
10381  
10382   case TCODE_ANNOTATION_LEADER:
10383     {
10384       // read the version id
10385       if( !ReadInt( &i))
10386         return false;
10387       
10388       if( i == RHINO_ANNOTATION_LEADER_VERSION_1)
10389       {
10390         // redundant type code to throw away
10391         if( !ReadInt( &i))
10392           return false;
10393 
10394         ON_Leader* ldr = new ON_Leader;
10395         ldr->SetType( ON::dtLeader);
10396 
10397         ON_Plane plane;
10398 
10399         // the entity plane 
10400         if( !ReadDouble( 3, &plane.origin.x))
10401           return false;
10402         if( !ReadDouble( 3, &plane.xaxis.x))
10403           return false;
10404         if( !ReadDouble( 3, &plane.yaxis.x))
10405           return false;
10406         
10407         // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane()
10408         TweakAnnotationPlane( plane );
10409 
10410         ldr->SetPlane( plane);
10411         
10412         // flags ( future )
10413         if( !ReadInt( 1, &j))
10414           return false;
10415         
10416         // settings byobject flag
10417         if( !ReadInt( 1, &byobject))
10418           return false;
10419         
10420         // number of points to read
10421         if( !ReadInt( &k))
10422           return false;
10423         
10424         if( k < 2)
10425           return true;
10426         
10427         ON_SimpleArray<ON_2dPoint> points;
10428         for( j = 0; j < k; j++ )
10429         {
10430           double pt[3];
10431           if( !ReadDouble( 3, pt))
10432             return false;
10433           points.Append( ON_2dPoint( pt));
10434         }
10435         ldr->SetPoints( points);
10436         
10437         *ppObject = ldr;
10438         rc = true;
10439         break;
10440       }
10441     }
10442     break;
10443   case TCODE_LINEAR_DIMENSION:
10444     {
10445       // read the version id
10446       if( !ReadInt( &i))
10447         return false;
10448       
10449       if( i == RHINO_LINEAR_DIMENSION_VERSION_1)
10450       {
10451         if( !ReadInt( &i))
10452           return false;
10453 
10454         ON_LinearDimension* dim = new ON_LinearDimension;
10455         switch( i )
10456         {
10457         case DimHorizontal:
10458         case DimVertical:
10459         case DimRotated:
10460         case DimLinear:
10461           dim->SetType( ON::dtDimLinear);
10462           break;
10463         default:
10464           dim->SetType( ON::dtDimAligned);
10465           break;
10466         }
10467 
10468         ON_Plane plane;
10469 
10470         // the entity plane 
10471         if( !ReadDouble( 3, &plane.origin.x))
10472           return false;
10473         if( !ReadDouble( 3, &plane.xaxis.x))
10474           return false;
10475         if( !ReadDouble( 3, &plane.yaxis.x))
10476           return false;
10477 
10478         // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane()
10479         TweakAnnotationPlane( plane );
10480 
10481         dim->SetPlane( plane);
10482         
10483         // definition points in coordinates of entity plane
10484         ON_SimpleArray<ON_2dPoint> points;
10485         for( j = 0; j < 11; j++ )
10486         {
10487           double pt[3];
10488           if( !ReadDouble( 3, pt))
10489             return false;
10490           points.Append( ON_2dPoint( pt));
10491         }
10492         dim->SetPoints( points);
10493         
10494         // read user text string 
10495         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10496           return false;
10497         dim->SetUserText( tc.Array());
10498         
10499         // Set the symbols used in dimension strings to the selected options
10500         //        SetStringSymbols();
10501         
10502         // read string to display
10503         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10504           return false;
10505         dim->SetDefaultText( tc.Array());
10506         
10507         // user positioned text flag
10508         if( !ReadInt( &j))
10509           return false;
10510         dim->SetUserPositionedText( j);
10511         
10512         // flags ( future )
10513         if( !ReadInt( 1, &j))
10514           return false;
10515         
10516         // settings byobject flag
10517         if( !ReadInt( 1, &byobject))
10518           return false;
10519         
10520         *ppObject = dim;
10521         rc = true;
10522         break;
10523       }
10524     }
10525     break;
10526 
10527   case TCODE_ANGULAR_DIMENSION:
10528     {
10529       // read the version id
10530       if( !ReadInt( &i))
10531         return false;
10532       
10533       if( i == RHINO_ANGULAR_DIMENSION_VERSION_1)
10534       {
10535         if( !ReadInt( &i))
10536           return false;
10537 
10538         ON_AngularDimension* dim = new ON_AngularDimension;
10539         dim->SetType( ON::dtDimAngular);
10540         
10541         ON_Plane plane;
10542 
10543         // the entity plane 
10544         if( !ReadDouble( 3, &plane.origin.x))
10545           return false;
10546         if( !ReadDouble( 3, &plane.xaxis.x))
10547           return false;
10548         if( !ReadDouble( 3, &plane.yaxis.x))
10549           return false;
10550 
10551         // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane()
10552         TweakAnnotationPlane( plane );
10553 
10554         dim->SetPlane( plane);
10555         
10556         if( !ReadDouble( &d))
10557           return false;
10558         dim->SetAngle( d);
10559 
10560         if( !ReadDouble( &d))
10561           return false;
10562         dim->SetRadius( d);
10563 
10564         // distances from apes to start and end of dimensioned lines - not used
10565         if( !ReadDouble( 4, d4))
10566           return false;
10567 
10568         // definition points in coordinates of entity plane
10569         ON_SimpleArray<ON_2dPoint> points;
10570         for( j = 0; j < 5; j++ )
10571         {
10572           double pt[3];
10573           if( !ReadDouble( 3, pt))
10574             return false;
10575           points.Append( ON_2dPoint( pt));
10576         }
10577         dim->SetPoints( points);
10578         
10579         // read user text string 
10580         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10581           return false;
10582         dim->SetUserText( tc.Array());
10583         
10584         // Set the symbols used in dimension strings to the selected options
10585         //        SetStringSymbols();
10586         
10587         // read string to display
10588         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10589           return false;
10590         dim->SetDefaultText( tc.Array());
10591         
10592         // user positioned text flag
10593         if( !ReadInt( &j))
10594           return false;
10595         dim->SetUserPositionedText( j);
10596         
10597         
10598         // flags ( future )
10599         if( !ReadInt( 1, &j))
10600           return false;
10601         
10602         // settings byobject flag
10603         if( !ReadInt( 1, &byobject))
10604           return false;
10605         
10606         
10607         *ppObject = dim;
10608         rc = true;
10609         break;
10610       }
10611     }
10612     break;
10613 
10614   case TCODE_RADIAL_DIMENSION:
10615     {
10616       // read the version id
10617       if( !ReadInt( &i))
10618         return false;
10619       
10620       if( i == RHINO_RADIAL_DIMENSION_VERSION_1)
10621       {
10622         if( !ReadInt( &i))
10623           return false;
10624 
10625         ON_RadialDimension* dim = new ON_RadialDimension;
10626 
10627         switch( i)
10628         {
10629         case DimDiameter:
10630           dim->SetType( ON::dtDimDiameter);
10631           break;
10632         case DimRadius:
10633           dim->SetType( ON::dtDimRadius);
10634           break;
10635         }
10636 
10637         ON_Plane plane;
10638 
10639         // the entity plane 
10640         if( !ReadDouble( 3, &plane.origin.x))
10641           return false;
10642         if( !ReadDouble( 3, &plane.xaxis.x))
10643           return false;
10644         if( !ReadDouble( 3, &plane.yaxis.x))
10645           return false;
10646 
10647         // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane()
10648         TweakAnnotationPlane( plane );
10649 
10650         dim->SetPlane( plane);
10651         
10652         // definition points in coordinates of entity plane
10653         ON_SimpleArray<ON_2dPoint> points;
10654         for( j = 0; j < 5; j++ )
10655         {
10656           double pt[3];
10657           if( !ReadDouble( 3, pt))
10658             return false;
10659           points.Append( ON_2dPoint( pt));
10660         }
10661         dim->SetPoints( points);
10662         
10663         // read user text string 
10664         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10665           return false;
10666         dim->SetUserText( tc.Array());
10667         
10668         // Set the symbols used in dimension strings to the selected options
10669         //        SetStringSymbols();
10670         
10671         // read string to display
10672         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10673           return false;
10674         dim->SetDefaultText( tc.Array());
10675         
10676         // user positioned text flag
10677         if( !ReadInt( &j))
10678           return false;
10679         dim->SetUserPositionedText( j);
10680         
10681         // flags ( future )
10682         if( !ReadInt( 1, &j))
10683           return false;
10684         
10685         // settings byobject flag
10686         if( !ReadInt( 1, &byobject))
10687           return false;
10688         
10689         
10690         *ppObject = dim;
10691         rc = true;
10692         break;
10693       }
10694     
10695     }
10696     break;
10697 
10698   default:  // some unknown type to skip over
10699     return true;
10700   } //switch
10701   
10702   if( rc)
10703   {
10704     ON_BOOL32 bHaveMat = false;
10705     Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_ENDOFTABLE);
10706   }
10707   
10708   return rc;
10709   
10710   // TODO: fill in this function
10711   
10712   // input tcode               returned *ppObject points to
10713   // TCODE_TEXT_BLOCK:         ON_TextEntity
10714   // TCODE_ANNOTATION_LEADER:  ON_Leader
10715   // TCODE_LINEAR_DIMENSION:   ON_LinearDimension
10716   // TCODE_ANGULAR_DIMENSION:  ON_AngularDimension
10717   // TCODE_RADIAL_DIMENSION:   ON_RadialDimension
10718   //return true if successful and false for failure.
10719 }
10720 
10721 
10722 bool ON_BinaryArchive::ReadV1_TCODE_MESH_OBJECT(
10723                                                 ON_Object** ppObject,
10724                                                 ON_3dmObjectAttributes* pAttributes
10725                                                 )
10726 {
10727   ON_Mesh* mesh = 0;
10728   bool rc = false;
10729   // read v1 mesh
10730   ON__UINT32 tcode = 0;
10731   ON__INT64 big_value = 0;
10732   int i;
10733   if ( !BeginRead3dmBigChunk(&tcode,&big_value) )
10734     return false;
10735   
10736   if ( tcode == TCODE_COMPRESSED_MESH_GEOMETRY ) for(;;) 
10737   {
10738     
10739     int point_count = 0;
10740     int face_count = 0;
10741     ON_BOOL32 bHasVertexNormals = false;
10742     ON_BOOL32 bHasTexCoords = false;
10743     ON_BoundingBox bbox;
10744     
10745     if (!ReadInt(&point_count) )
10746       break;
10747     if ( point_count <= 0 )
10748       break;
10749     if (!ReadInt(&face_count) )
10750       break;
10751     if ( face_count <= 0 )
10752       break;
10753     if (!ReadInt(&bHasVertexNormals) )
10754       break;
10755     if (!ReadInt(&bHasTexCoords) )
10756       break;
10757     if ( !ReadPoint(bbox.m_min) )
10758       break;
10759     if ( !ReadPoint(bbox.m_max) )
10760       break;
10761 
10762     mesh = new ON_Mesh(face_count,
10763                        point_count,
10764                        bHasVertexNormals?true:false,
10765                        bHasTexCoords?true:false
10766                        );
10767 
10768     // read 3d vertex locations
10769     {
10770       ON_3dVector d = bbox.Diagonal();
10771       double dx = d.x / 65535.0;
10772       double dy = d.y / 65535.0;
10773       double dz = d.z / 65535.0;
10774       unsigned short xyz[3];
10775       ON_3fPoint pt;
10776       for ( i = 0; i < point_count; i++ ) {
10777         if ( !ReadShort(3,xyz) )
10778           break;
10779         pt.x = (float)(dx*xyz[0] + bbox.m_min.x);
10780         pt.y = (float)(dy*xyz[1] + bbox.m_min.y);
10781         pt.z = (float)(dz*xyz[2] + bbox.m_min.z);
10782         mesh->m_V.Append(pt);
10783       }
10784     }
10785     if ( mesh->m_V.Count() != point_count )
10786       break;
10787 
10788     // read triangle/quadrangle faces
10789     if ( point_count < 65535 ) {
10790       unsigned short abcd[4];
10791       for ( i = 0; i < face_count; i++ ) {
10792         if ( !ReadShort(4,abcd) )
10793           break;
10794         ON_MeshFace& f = mesh->m_F.AppendNew();
10795         f.vi[0] = abcd[0];
10796         f.vi[1] = abcd[1];
10797         f.vi[2] = abcd[2];
10798         f.vi[3] = abcd[3];
10799       }
10800     }
10801     else {
10802       int abcd[4];
10803       for ( i = 0; i < face_count; i++ ) {
10804         if ( !ReadInt(4,abcd) )
10805           break;
10806         ON_MeshFace& f = mesh->m_F.AppendNew();
10807         f.vi[0] = abcd[0];
10808         f.vi[1] = abcd[1];
10809         f.vi[2] = abcd[2];
10810         f.vi[3] = abcd[3];
10811       }
10812     }
10813     if ( mesh->m_F.Count() != face_count )
10814       break;
10815 
10816     if ( bHasVertexNormals ) {
10817       char xyz[3];
10818       ON_3fVector normal;
10819       for ( i = 0; i < point_count; i++ ) {
10820         if ( !ReadChar(3,xyz) )
10821           break;
10822         normal.x = (float)(((signed char)xyz[0])/127.0);
10823         normal.y = (float)(((signed char)xyz[1])/127.0);
10824         normal.z = (float)(((signed char)xyz[2])/127.0);
10825         mesh->m_N.Append(normal);
10826       }
10827       if ( mesh->m_N.Count() != mesh->m_V.Count() )
10828         break;
10829     }
10830 
10831     if ( bHasTexCoords ) {
10832       unsigned short uv[2];
10833       ON_2fPoint t;
10834       for ( i = 0; i < point_count; i++ ) {
10835         if ( !ReadShort(2,uv) )
10836           break;
10837         t.x = (float)(uv[0]/65535.0);
10838         t.y = (float)(uv[1]/65535.0);
10839         mesh->m_T.Append(t);
10840       }
10841       if ( mesh->m_T.Count() != mesh->m_V.Count() )
10842         break;
10843     }
10844 
10845     rc = true;
10846 
10847     break;
10848   }
10849   
10850   if ( !EndRead3dmChunk() )
10851     rc = false;
10852 
10853   if ( rc && mesh ) {
10854     *ppObject = mesh;
10855   }
10856   else {
10857     if ( mesh )
10858       delete mesh;
10859     rc = false;
10860   }
10861 
10862   if ( rc && mesh ) {
10863     // attributes and material information follows the TCODE_COMPRESSED_MESH_GEOMETRY chunk;
10864     ON_BOOL32 bHaveMat = false;
10865     Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_ENDOFTABLE);
10866   }
10867 
10868   return rc;
10869 }
10870 
10871 static bool BeginRead3dmLEGACYSTUFF( ON_BinaryArchive& file, unsigned int stuff_tcode )
10872 {
10873   // begins reading stuff chunk
10874   bool rc = false;
10875   ON__UINT32 tcode = !stuff_tcode;
10876   ON__INT64 big_value = 0;
10877   for (;;) 
10878   {
10879     if ( !file.BeginRead3dmBigChunk(&tcode,&big_value) )
10880       break;
10881     if ( tcode == stuff_tcode ) {
10882       rc = true;
10883       break;
10884     }
10885     if ( !file.EndRead3dmChunk() )
10886       break;
10887   }
10888   return rc;
10889 }
10890 
10891 static ON_NurbsCurve* ReadV1_TCODE_LEGACY_SPLSTUFF( ON_BinaryArchive& file )
10892 {
10893   // reads contents of a v1 TCODE_LEGACY_SPLSTUFF chunk
10894   ON_NurbsCurve* pNurbsCurve = 0;
10895   int i, dim, is_rat, order, cv_count, is_closed, form;
10896   ON_BoundingBox bbox;
10897   char c;
10898 
10899   // read v1 agspline chunk
10900   if ( !file.ReadChar(1,&c) )
10901     return NULL;
10902   if ( c != 2 && c != 3 )
10903     return NULL;
10904   dim = c;
10905   if ( !file.ReadChar(1,&c) )
10906     return NULL;
10907   if ( c != 0 && c != 1 && c != 2 )
10908     return NULL;
10909   is_rat = c; // 0 = no, 1 = euclidean, 2 = homogeneous
10910   if ( !file.ReadChar(1,&c) )
10911     return NULL;
10912   if ( c < 2 )
10913     return NULL;
10914   order = c;   
10915   
10916   {
10917     // 5 February 2003 - An single case of a V1 file
10918     //     with a spline that had cv_count = 54467 (>32767)
10919     //     exists.  Changing from a signed short to
10920     //     an unsigned short fixed the problem.
10921     //     The ui casting stuff is here to keep all
10922     //     the various compilers/lints happy and to
10923     //     make sure the short with the high bit set
10924     //     gets properly converted to a positive cv_count.
10925     unsigned short s;
10926     if ( !file.ReadShort(1,&s) )
10927       return NULL;
10928     unsigned int ui = s;
10929     cv_count = (int)ui;
10930     if ( cv_count < order )
10931       return NULL;
10932   }
10933 
10934   // The "is_closed" and "form" flags are here to recording
10935   // the values of legacy data found in the Rhino file.  These
10936   // values are not used in the toolkit code.
10937   if ( !file.ReadByte(1,&c) )
10938     return NULL;  
10939   if (c != 0 && c != 1 && c != 2)
10940     return NULL;
10941   is_closed = c; // 0 = open, 1 = closed, 2 = periodic
10942   if ( !file.ReadByte(1,&c) )
10943     return NULL;
10944   form = c;
10945 
10946   // read bounding box
10947   if ( !file.ReadDouble( dim, bbox.m_min ) )
10948     return NULL;
10949   if ( !file.ReadDouble( dim, bbox.m_max ) )
10950     return NULL;
10951 
10952   pNurbsCurve = new ON_NurbsCurve( dim, is_rat?true:false, order, cv_count );
10953 
10954   ON_BOOL32 rc = false;
10955   for(;;) {
10956 
10957     // read legacy v1 knot vector 
10958     const int knot_count = order+cv_count-2;
10959     int       knot_index = 0;
10960     double    knot;
10961 
10962     // clamped_end_knot_flag: 0 = none, 1 = left, 2 = right, 3 = both
10963     char clamped_end_knot_flag = 0;
10964     if ( order > 2 ) 
10965       file.ReadChar(1,&clamped_end_knot_flag);
10966 
10967     // first knot(s)
10968     if ( !file.ReadDouble(&knot) )
10969       break;
10970     pNurbsCurve->m_knot[knot_index++] = knot;
10971     if (clamped_end_knot_flag % 2) {
10972       // clamped_start_knot
10973       while ( knot_index <= order-2 ) 
10974         pNurbsCurve->m_knot[knot_index++] = knot;
10975     }
10976 
10977     // middle knots
10978     while ( knot_index <= cv_count-1 ) {
10979       if ( !file.ReadDouble(&knot) )
10980         break;
10981       pNurbsCurve->m_knot[knot_index++] = knot;
10982     }
10983     if ( knot_index <= cv_count-1 )
10984       break;
10985 
10986     // end knot(s)
10987     if ( clamped_end_knot_flag >= 2 ) {
10988       while ( knot_index < knot_count )
10989         pNurbsCurve->m_knot[knot_index++] = knot;
10990     }
10991     else {
10992       while ( knot_index < knot_count ) {
10993         if ( !file.ReadDouble(&knot) )
10994           break;
10995         pNurbsCurve->m_knot[knot_index++] = knot;
10996       }
10997       if ( knot_index < knot_count )
10998         break;
10999     }
11000 
11001     // read legacy v1 control points
11002     const int cvdim = ( is_rat ) ? dim+1 : dim;
11003     for ( i = 0; i < cv_count; i++ ) {
11004       if ( !file.ReadDouble( cvdim, pNurbsCurve->CV(i) ) )
11005         break;
11006     }
11007     if ( i < cv_count )
11008       break;
11009     if ( is_rat ) {
11010       // is_rat == 1 check fails because invalid is_rat flags in old files
11011       // convert rational CVs from euclidean to homogeneous
11012       double w, *cv;
11013       int cv_index;
11014       for ( cv_index = 0; cv_index < cv_count; cv_index++ ) {
11015         cv = pNurbsCurve->CV(cv_index);
11016         w = cv[dim];
11017         for ( i = 0; i < dim; i++ )
11018           cv[i] *= w;
11019       }
11020     }
11021     if ( order == 2 && cv_count == 2 && pNurbsCurve->m_knot[0] > pNurbsCurve->m_knot[1] ) {
11022       // a few isolated old v1 3DM file created by Rhino 1.0 files have lines with reversed knots.
11023       pNurbsCurve->m_knot[0] = -pNurbsCurve->m_knot[0];
11024       pNurbsCurve->m_knot[1] = -pNurbsCurve->m_knot[1];
11025     }
11026     rc = true;
11027 
11028     break;
11029   }
11030   if ( !rc && pNurbsCurve ) {
11031     delete pNurbsCurve;
11032     pNurbsCurve = 0;
11033   }
11034   return pNurbsCurve;
11035 }
11036 
11037 static ON_BOOL32 ReadV1_TCODE_LEGACY_SPL( ON_BinaryArchive& file,
11038   ON_NurbsCurve*& pNurbsCurve
11039   )
11040 {
11041   // reads contents of TCODE_LEGACY_SPL chunk
11042   pNurbsCurve = 0;
11043   ON_BOOL32 rc = BeginRead3dmLEGACYSTUFF(file, TCODE_LEGACY_SPLSTUFF );
11044   if ( !rc )
11045     return false;
11046   pNurbsCurve = ReadV1_TCODE_LEGACY_SPLSTUFF(file);
11047   if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_SPLSTUFF chunk
11048     rc = false;
11049   if ( !pNurbsCurve )
11050     rc = false;
11051   return rc;
11052 }
11053 
11054 static ON_NurbsSurface* ReadV1_TCODE_LEGACY_SRFSTUFF( ON_BinaryArchive& file )
11055 {
11056   // reads contents of TCODE_LEGACY_SRFSTUFF chunk
11057   ON_NurbsSurface* pNurbsSurface = 0;
11058   int i, j, dim=0, is_rat=0, order[2], cv_count[2], is_closed[2], is_singular[2], form;
11059   ON_BoundingBox bbox;
11060   char c;
11061 
11062   // read contents of v1 TCODE_LEGACY_SRFSTUFF chunk
11063   if ( !file.ReadChar(1,&c) )
11064     return NULL;
11065   if ( c != 2 && c != 3 )
11066     return NULL;
11067   dim = c;
11068   if ( !file.ReadByte(1,&c) )
11069     return NULL;
11070   form = c;
11071   if ( !file.ReadChar(1,&c) )
11072     return NULL;
11073   if ( c < 1 )
11074     return NULL;
11075   order[0] = c+1;    
11076   if ( !file.ReadChar(1,&c) )
11077     return NULL;
11078   if ( c < 1 )
11079     return NULL;
11080   order[1] = c+1;    
11081   
11082   {
11083     // 5 February 2003 - An single case of a V1 files
11084     //     See the comment above in ReadV1_TCODE_LEGACY_SPLSTUFF
11085     //     concerning the spline with cv_count >= 0x8000.
11086     //     The analogous fix is here for the surface case.
11087     unsigned short s;
11088     if ( !file.ReadShort(1,&s) )
11089       return NULL;
11090     if ( s < 1 )
11091       return NULL;
11092     unsigned int ui = s;
11093     cv_count[0] = order[0]-1+((int)ui);    
11094     if ( !file.ReadShort(1,&s) )
11095       return NULL;
11096     if ( s < 1 )
11097       return NULL;
11098     ui = s;
11099     cv_count[1] = order[1]-1+((int)ui);    
11100   }
11101   
11102   // "ratu" 0 = no, 1 = euclidean, 2 = homogeneous
11103   if ( !file.ReadChar(1,&c) )
11104     return NULL;
11105   if ( c == 1 ) is_rat = 1; else if ( c == 2 ) is_rat = 2;
11106   
11107   // "ratv" 0 = no, 1 = euclidean, 2 = homogeneous
11108   if ( !file.ReadChar(1,&c) )
11109     return NULL;
11110   if ( c == 1 ) is_rat = 1; else if ( c == 2 ) is_rat = 2;
11111 
11112   // The "is_closed" and "is_singular" flags are here to recording
11113   // the values of legacy data found in the Rhino file.  These
11114   // values are not used in the toolkit code.
11115   if ( !file.ReadByte(1,&c) )
11116     return NULL;  
11117   if (c != 0 && c != 1 && c != 2)
11118     return NULL;
11119   is_closed[0] = c; // 0 = open, 1 = closed, 2 = periodic
11120   if ( !file.ReadByte(1,&c) )
11121     return NULL;  
11122   if (c != 0 && c != 1 && c != 2)
11123     return NULL;
11124   is_closed[1] = c; // 0 = open, 1 = closed, 2 = periodic
11125 
11126   if ( !file.ReadByte(1,&c) )
11127     return NULL;  
11128   if (c != 0 && c != 1 && c != 2 && c != 3)
11129     return NULL;
11130   is_singular[0] = c;
11131   if ( !file.ReadByte(1,&c) )
11132     return NULL;  
11133   if (c != 0 && c != 1 && c != 2 && c != 3)
11134     return NULL;
11135   is_singular[1] = c;
11136 
11137   // read bounding box
11138   if ( !file.ReadDouble( dim, bbox.m_min ) )
11139     return NULL;
11140   if ( !file.ReadDouble( dim, bbox.m_max ) )
11141     return NULL;
11142 
11143   pNurbsSurface = new ON_NurbsSurface( dim, is_rat?true:false, 
11144                                        order[0], order[1], 
11145                                        cv_count[0], cv_count[1] );
11146 
11147   ON_BOOL32 rc = false;
11148   for (;;) {
11149 
11150     // read legacy v1 knot vectors 
11151     if ( !file.ReadDouble( order[0]+cv_count[0]-2, pNurbsSurface->m_knot[0] ) )
11152       break;
11153     if ( !file.ReadDouble( order[1]+cv_count[1]-2, pNurbsSurface->m_knot[1] ) )
11154       break;
11155 
11156     // read legacy v1 control points
11157     const int cvdim = ( is_rat ) ? dim+1 : dim;
11158     for ( i = 0; i < cv_count[0]; i++ ) {
11159       for ( j = 0; j < cv_count[1]; j++ ) {
11160         if ( !file.ReadDouble( cvdim, pNurbsSurface->CV(i,j) ) )
11161           break;
11162       }
11163       if ( j < cv_count[1] )
11164         break;
11165     }
11166     if ( i < cv_count[0] )
11167       break;
11168     if ( is_rat == 1 ) {
11169       double w, *cv;
11170       int k;
11171       for ( i = 0; i < cv_count[0]; i++ ) for ( j = 0; j < cv_count[1]; j++ ) {
11172         cv = pNurbsSurface->CV(i,j);
11173         w = cv[dim];
11174         for ( k = 0; k < dim; k++ )
11175           cv[k] *= w;
11176       }
11177     }
11178     rc = true;
11179 
11180     break;
11181   }
11182   if ( !rc ) {
11183     delete pNurbsSurface;
11184     pNurbsSurface = 0;
11185   }
11186 
11187   return pNurbsSurface;
11188 }
11189 
11190 static ON_BOOL32 ReadV1_TCODE_LEGACY_SRF( ON_BinaryArchive& file,
11191   ON_NurbsSurface*& pNurbsSurface
11192   )
11193 {
11194   pNurbsSurface = 0;
11195   ON_BOOL32 rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_SRF );
11196   if ( rc ) {
11197     rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_SRFSTUFF );
11198     if ( rc ) {
11199       pNurbsSurface = ReadV1_TCODE_LEGACY_SRFSTUFF( file );
11200       if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_SRFSTUFF chunk
11201         rc = false;
11202     }
11203     if ( !file.EndRead3dmChunk() )
11204       rc = false; // end of TCODE_LEGACY_SRF chunk
11205   }
11206   if ( !rc && pNurbsSurface ) {
11207     delete pNurbsSurface;
11208     pNurbsSurface = 0;
11209   }
11210   return rc;
11211 }
11212 
11213 ON_Curve* ReadV1_TCODE_LEGACY_CRVSTUFF( ON_BinaryArchive& file )
11214 {
11215   // reads contents of a v1 TCODE_LEGACY_CRVSTUFF chunk
11216   ON_Curve* curve = 0;
11217   ON_PolyCurve* polycurve = 0;
11218   ON_NurbsCurve* segment = 0;
11219   ON_BOOL32 rc = false;
11220   ON__UINT32 tcode = 0;
11221   ON__INT64 big_value = 0;
11222   int i;
11223   ON_BOOL32 bIsPolyline = false;
11224   ON_BoundingBox bbox;
11225 
11226   for (;;) {
11227     char c;
11228     short s;
11229     int segment_count = 0;
11230     file.ReadChar(1,&c);  
11231     if ( c != 2 && c != 3 )
11232       break;
11233     int dim = c;
11234     file.ReadChar(1,&c);
11235     if ( c != -1 && c != 0 && c != 1 && c != 2 )
11236       break;
11237     //int is_closed = (c) ? 1 : 0;
11238     file.ReadShort(&s);
11239     if ( s < 1 )
11240       break;
11241     file.ReadDouble( dim, bbox.m_min);
11242     file.ReadDouble( dim, bbox.m_max);
11243     segment_count = s;
11244     for ( i = 0; i < segment_count; i++ ) {
11245       segment = 0;
11246       tcode = 0;
11247       big_value = 0;
11248       if ( !file.BeginRead3dmBigChunk( &tcode, &big_value ) )
11249         break;
11250       if ( tcode == TCODE_LEGACY_SPL && big_value > 0 ) {
11251         ReadV1_TCODE_LEGACY_SPL(file,segment);
11252       }
11253       if ( !file.EndRead3dmChunk() ) {
11254         if ( segment ) {
11255           delete segment;
11256           segment = 0;
11257         }
11258         break;
11259       }
11260       if ( !segment )
11261         break;
11262       if ( i == 0 )
11263         polycurve = new ON_PolyCurve(segment_count);
11264       if ( segment->CVCount() > 2 || segment->Order() != 2 || segment->IsRational() ) 
11265       {
11266         if ( segment->Order() != 2 || segment->IsRational() )
11267           bIsPolyline = false;
11268         polycurve->Append(segment);
11269       }
11270       else 
11271       {
11272         ON_LineCurve* line = new ON_LineCurve();
11273         line->m_t.Set( segment->m_knot[0], segment->m_knot[1] );
11274         segment->GetCV( 0, line->m_line.from );
11275         segment->GetCV( 1, line->m_line.to );
11276         line->m_dim = segment->m_dim;
11277         delete segment;
11278         segment = 0;
11279         polycurve->Append(line);
11280       }
11281     }
11282 
11283     // 5 February 2003
11284     //   The check for a NULL polycurve was added to avoid
11285     //   crashes in files when the first NURBS curve in the
11286     //   polycurve could not be read.
11287     if ( 0 == polycurve )
11288       break;
11289     if ( polycurve->Count() != segment_count )
11290       break;
11291     rc = true;
11292     break;
11293   }
11294 
11295   if ( rc && polycurve ) 
11296   {
11297     if ( polycurve->Count() == 1 )
11298     {
11299       curve = polycurve->HarvestSegment(0);
11300       delete polycurve;
11301     }
11302     else if ( bIsPolyline ) 
11303     {
11304       ON_PolylineCurve* pline = new ON_PolylineCurve();
11305       pline->m_dim = polycurve->Dimension();
11306       pline->m_t.Reserve(polycurve->Count()+1);
11307       pline->m_t.SetCount(polycurve->Count()+1);
11308       polycurve->GetSpanVector( pline->m_t.Array() );
11309       pline->m_pline.Reserve(polycurve->Count()+1);
11310       for ( i = 0; i < polycurve->Count(); i++ ) {
11311         pline->m_pline.Append(polycurve->SegmentCurve(i)->PointAtStart());
11312       }
11313       pline->m_pline.Append(polycurve->SegmentCurve(polycurve->Count()-1)->PointAtEnd());
11314       curve = pline;
11315       delete polycurve;
11316     }
11317     else 
11318     {
11319       curve = polycurve;
11320     }
11321   }
11322   else 
11323   {
11324     if ( polycurve )
11325       delete polycurve;
11326     rc = false;
11327   }
11328 
11329   return curve;
11330 }
11331 
11332 bool ON_Brep::ReadV1_LegacyTrimStuff( ON_BinaryArchive& file, 
11333         ON_BrepFace&, // face - formal parameter intentionally ignored
11334         ON_BrepLoop& loop )
11335 {
11336   // read contents of TCODE_LEGACY_TRMSTUFF chunk
11337   bool rc = false;
11338   int revedge, gcon, mono;
11339   int curve2d_index = -1, curve3d_index = -1, trim_index = -1;
11340   double tol_3d, tol_2d;
11341   ON_Curve* curve2d = NULL;
11342   ON_Curve* curve3d = NULL;
11343 
11344   char c;
11345   file.ReadChar( &c );
11346 
11347   ON_BOOL32 bHasEdge = (c % 2 ); // bit 0 = 1 if "tedge" has non NULL "->edge"
11348   ON_BOOL32 bHasMate = (c & 6 ); // bit 1 or 2 = 1 if "tedge" has non NULL "->twin"
11349   ON_BOOL32 bIsSeam  = (c & 2 ); // bit 1 = 1 if "tedge->twin" belongs to same face
11350 
11351   if ( !file.ReadInt(&revedge) )
11352     return false;
11353   if ( !file.ReadInt(&gcon) )
11354     return false;
11355   if ( !file.ReadInt(&mono) )
11356     return false;
11357   if ( !file.ReadDouble( &tol_3d ) )
11358     return false;
11359   if ( !file.ReadDouble( &tol_2d ) )
11360     return false;
11361 
11362   // 2d trim curve
11363   if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRV ) )
11364     return false;
11365   if ( BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRVSTUFF ) ) {
11366     curve2d = ReadV1_TCODE_LEGACY_CRVSTUFF(file);
11367     if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRVSTUFF chunk
11368       rc = false;
11369   }
11370   if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRV chunk
11371     rc = false;
11372   if ( !curve2d )
11373     return false;
11374   curve2d_index = AddTrimCurve(curve2d);
11375   if ( curve2d_index < 0 ) {
11376     delete curve2d;
11377     return false;
11378   }
11379 
11380   // 3d curve
11381   if ( bHasEdge ) {
11382     if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRV ) )
11383       return false;
11384     if ( BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRVSTUFF ) ) {
11385       curve3d = ReadV1_TCODE_LEGACY_CRVSTUFF(file);
11386       if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRVSTUFF chunk
11387         rc = false;
11388     }
11389     if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRV chunk
11390       rc = false;
11391     if ( !curve3d )
11392       return false;
11393     curve3d_index = AddEdgeCurve(curve3d);
11394     if ( curve3d_index < 0 ) {
11395       delete curve3d;
11396       return false;
11397     }
11398     ON_BrepEdge& edge = NewEdge(curve3d_index);
11399     ON_BrepTrim& trim = NewTrim( edge, 
11400                                     revedge ? true : false,
11401                                     loop,
11402                                     curve2d_index
11403                                     );
11404     trim_index = trim.m_trim_index;
11405   }
11406   else {
11407     ON_BrepTrim& trim = NewTrim( revedge ? true : false,
11408                                     loop,
11409                                     curve2d_index
11410                                     );
11411     trim_index = trim.m_trim_index;
11412   }
11413   if ( trim_index >= 0 ) {
11414     ON_BrepTrim& trim = m_T[trim_index];
11415     trim.m__legacy_2d_tol = tol_2d;
11416     trim.m__legacy_3d_tol = tol_3d;
11417     trim.m__legacy_flags_Set(gcon,mono);
11418     if ( bIsSeam ) {
11419       trim.m_type = ON_BrepTrim::seam;
11420     }
11421     else if ( bHasMate ) {
11422       trim.m_type = ON_BrepTrim::mated;
11423     }
11424     else if ( bHasEdge ) {
11425       trim.m_type = ON_BrepTrim::boundary;
11426     }
11427     else {
11428       trim.m_type = ON_BrepTrim::singular;
11429     }
11430   }
11431 
11432   return (trim_index>=0) ? true : false;
11433 }
11434 
11435 bool ON_Brep::ReadV1_LegacyTrim( ON_BinaryArchive& file, 
11436                             ON_BrepFace& face,
11437                             ON_BrepLoop& loop )
11438 {
11439   bool rc = false;
11440   if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_TRM ) )
11441     return false;
11442   rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_TRMSTUFF );
11443   if ( rc ) {
11444     rc = ReadV1_LegacyTrimStuff( file, face, loop );
11445     if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_TRMSTUFF
11446       rc = false;
11447   }
11448   if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_TRM chunk
11449     rc = false;
11450   return rc;
11451 }
11452 
11453 
11454 bool ON_Brep::ReadV1_LegacyLoopStuff( ON_BinaryArchive& file, 
11455                                ON_BrepFace& face )
11456 {
11457   // reads contents of TCODE_LEGACY_BNDSTUFF chunk
11458   // read boundary
11459   ON_BrepLoop::TYPE loop_type = ON_BrepLoop::unknown;
11460   int tedge_count, btype, lti;
11461   double pspace_box[2][2]; // parameter space bounding box
11462 
11463   if ( !file.ReadInt( &tedge_count ) )
11464     return false;
11465   if ( tedge_count < 1 ) {
11466     return false;
11467   }
11468   if ( !file.ReadInt( &btype ) )
11469     return false;
11470   if ( btype < -1 || btype > 1 ) {
11471     return false;
11472   }
11473   if ( !file.ReadDouble( 4, &pspace_box[0][0] ) )
11474     return false;
11475   switch( btype ) {
11476   case -1:
11477     loop_type = ON_BrepLoop::slit;
11478     break;
11479   case  0:
11480     loop_type = ON_BrepLoop::outer;
11481     break;
11482   case  1:
11483     loop_type = ON_BrepLoop::inner;
11484     break;
11485   }
11486   ON_BrepLoop& loop = NewLoop( loop_type, face );
11487 
11488   for ( lti = 0; lti < tedge_count; lti++ ) {
11489     if ( !ReadV1_LegacyTrim( file, face, loop ) )
11490       return false;
11491   }
11492 
11493   return true;
11494 }
11495 
11496 bool ON_Brep::ReadV1_LegacyLoop( ON_BinaryArchive& file, 
11497                                         ON_BrepFace& face )
11498 {
11499   bool rc = false;
11500   if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_BND ) )
11501     return false;
11502   rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_BNDSTUFF );
11503   if ( rc ) {
11504     rc = ReadV1_LegacyLoopStuff( file, face );
11505     if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_BNDSTUFF
11506       rc = false;
11507   }
11508   if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_BND chunk
11509     rc = false;
11510   return rc;
11511 }
11512 
11513 bool ON_Brep::ReadV1_LegacyFaceStuff( ON_BinaryArchive& file )
11514 {
11515   // reads contents of TCODE_LEGACY_FACSTUFF chunk
11516   ON_NurbsSurface* surface = 0;
11517   ON_Workspace ws;
11518   int flipnorm = 0;
11519   int ftype = 0;
11520   int bndcnt = 0;
11521   int twincnt = 0;
11522   ON_BOOL32 bHasOuter = false;
11523   ON_BoundingBox face_bbox;
11524 
11525   int ti0 = m_T.Count();
11526 
11527   bool rc = false;
11528 
11529   // read flags
11530   if ( !file.ReadInt(&flipnorm) )
11531     return false;
11532   if ( flipnorm < 0 || flipnorm > 1 )
11533     return false;
11534   if ( !file.ReadInt(&ftype) )
11535     return false;
11536   if ( !file.ReadInt(&bndcnt) )
11537     return false;
11538   bHasOuter = (bndcnt%2); // always true in v1 files
11539   bndcnt /= 2;
11540 
11541   // read bounding box
11542   if ( !file.ReadDouble( 3, face_bbox.m_min ) )
11543     return false;
11544   if ( !file.ReadDouble( 3, face_bbox.m_max ) )
11545     return false;
11546 
11547   // B-rep edge gluing info 
11548   if ( !file.ReadInt(&twincnt) )
11549     return false;
11550   short* glue = (twincnt > 0 ) ? (short*)ws.GetMemory(twincnt*sizeof(*glue)) : NULL;
11551   if (twincnt > 0) {
11552     if ( !file.ReadShort(twincnt,glue) )
11553       return false;
11554   }
11555 
11556   // read surface
11557   if ( !ReadV1_TCODE_LEGACY_SRF( file, surface ) )
11558     return false;
11559   if ( !surface )
11560     return false;
11561   const int srf_index = AddSurface(surface);
11562 
11563   // create face
11564   ON_BrepFace& face = NewFace(srf_index);
11565   face.m_bRev = (flipnorm) ? true : false;
11566   face.m_li.Reserve(bndcnt);
11567 
11568   // read boundary loops
11569   int loop_index = -1;
11570   if ( !bHasOuter ) {
11571     // TODO: cook up outer boundary loop (never happes with v1 files)
11572     face.m_li.Append(loop_index);
11573   }
11574   int bi;
11575   rc = true;
11576   for ( bi = 0; rc && bi < bndcnt; bi++ ) {
11577     rc = ReadV1_LegacyLoop( file, face );
11578   }
11579 
11580   if ( twincnt > 0 ) {
11581     // twincnt = number of seams edges in face
11582     // glue[] = order 2 permutation of {0,....,twincnt-1}
11583 
11584     // set seam_i[] = m_T[] indices of seam trims
11585     int si, ti;
11586     const int ti1 = m_T.Count();
11587     int* seam_i = (int*)ws.GetMemory(twincnt*sizeof(*seam_i));
11588     for ( ti = ti0, si = 0; ti < ti1 && si < twincnt; ti++ ) {
11589       if (m_T[ti].m_type != ON_BrepTrim::seam )
11590         continue;
11591       seam_i[si++] = ti;
11592     }
11593 
11594     if ( si == twincnt ) {
11595       // glue seams
11596       for ( si = 0; si < twincnt; si++ ) {
11597         if ( glue[si] >= 0 && glue[si] < twincnt ) {
11598           const int i0 = seam_i[si];
11599           const int i1 = seam_i[glue[si]];
11600           // m_T[i0] and m_T[i1] use the same edge;
11601           const int ei0 = m_T[i0].m_ei;
11602           const int ei1 = m_T[i1].m_ei;
11603           if ( ei0 == -1 && ei1 >= 0 ) {
11604             m_T[i0].m_ei = ei1;
11605             m_E[ei1].m_ti.Append(i0);
11606           }
11607           else if ( ei1 == -1 && ei0 >= 0 ) {
11608             m_T[i1].m_ei = ei0;
11609             m_E[ei0].m_ti.Append(i1);
11610           }
11611         }
11612       }
11613     }
11614   }
11615 
11616   return rc;
11617 }
11618 
11619 bool ON_Brep::ReadV1_LegacyShellStuff( ON_BinaryArchive& file )
11620 {
11621   // read contents of TCODE_LEGACY_SHLSTUFF chunk
11622   ON_Workspace ws;
11623   int outer = 0;
11624   int facecnt = 0;
11625   int twincnt = 0;
11626   ON_BoundingBox shell_bbox;
11627   const int ti0 = m_T.Count();
11628 
11629   /* read flags */
11630   file.ReadInt(&outer);  
11631   file.ReadInt(&facecnt);  
11632 
11633   // read bounding box
11634   file.ReadPoint( shell_bbox.m_min );
11635   file.ReadPoint( shell_bbox.m_max );
11636 
11637   /* B-rep edge gluing info */
11638   file.ReadInt(&twincnt);
11639   short* glue = (twincnt > 0 ) ? (short*)ws.GetMemory(twincnt*sizeof(*glue)) : NULL;
11640   if (twincnt > 0)
11641     file.ReadShort(twincnt,glue);
11642 
11643   bool rc = true;
11644   int fi;
11645   for ( fi = 0; rc && fi < facecnt; fi++ ) {
11646     rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_FAC );
11647     if ( rc ) {
11648       rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_FACSTUFF );
11649       if ( rc ) {
11650         rc = ReadV1_LegacyFaceStuff( file );
11651         if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_FACSTUFF chunk
11652           rc = false;
11653       }
11654       if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_FAC chunk
11655         rc = false;
11656     }
11657   }
11658 
11659   if ( twincnt > 0 ) {
11660     // twincnt = number of shared (inter-face) edges
11661     // glue[] = order 2 permutation of {0,....,twincnt-1}
11662 
11663     // set share_i[] = m_T[] indices of shared trims
11664     int si, ti;
11665     const int ti1 = m_T.Count();
11666     int* share_i = (int*)ws.GetMemory(twincnt*sizeof(*share_i));
11667     for ( ti = ti0, si = 0; ti < ti1 && si < twincnt; ti++ ) {
11668       if (m_T[ti].m_type != ON_BrepTrim::mated )
11669         continue;
11670       share_i[si++] = ti;
11671     }
11672 
11673     if ( si == twincnt ) {
11674       // glue seams
11675       for ( si = 0; si < twincnt; si++ ) {
11676         if ( glue[si] >= 0 && glue[si] < twincnt ) {
11677           const int i0 = share_i[si];
11678           const int i1 = share_i[glue[si]];
11679           // m_T[i0] and m_T[i1] use the same edge;
11680           const int ei0 = m_T[i0].m_ei;
11681           const int ei1 = m_T[i1].m_ei;
11682           if ( ei0 == -1 && ei1 >= 0 ) {
11683             m_T[i0].m_ei = ei1;
11684             m_E[ei1].m_ti.Append(i0);
11685           }
11686           else if ( ei1 == -1 && ei0 >= 0 ) {
11687             m_T[i1].m_ei = ei0;
11688             m_E[ei0].m_ti.Append(i1);
11689           }
11690         }
11691       }
11692     }
11693   }
11694 
11695   return rc;
11696 }
11697 
11698 bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_CRV(
11699   ON_Object** ppObject,
11700   ON_3dmObjectAttributes* pAttributes
11701   )
11702 {
11703   ON_Curve* curve = NULL;
11704   bool rc = false;
11705   ON__UINT32 tcode = 0;
11706   ON__INT64 big_value = 0;
11707   ON_BOOL32 bHaveMat = false;
11708 
11709   Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_LEGACY_CRVSTUFF);
11710 
11711   if ( !BeginRead3dmBigChunk( &tcode, &big_value ) )
11712     return false;
11713   if ( tcode == TCODE_LEGACY_CRVSTUFF )
11714     curve = ReadV1_TCODE_LEGACY_CRVSTUFF(*this);
11715   rc = EndRead3dmChunk();
11716   if ( !curve )
11717     rc = false;
11718   else
11719     *ppObject = curve;
11720   return rc;
11721 }
11722 
11723 
11724 bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_FAC(
11725   ON_Object** ppObject,
11726   ON_3dmObjectAttributes* pAttributes
11727   )
11728 {
11729   // read V1 TCODE_LEGACY_FAC chunk
11730   ON_BOOL32 bHaveMat = false;
11731   if ( !Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_LEGACY_FACSTUFF) )
11732     return false;
11733   if ( !BeginRead3dmLEGACYSTUFF( *this, TCODE_LEGACY_FACSTUFF ) )
11734     return false;
11735   ON_Brep* brep = new ON_Brep();
11736   bool rc = brep->ReadV1_LegacyFaceStuff( *this );
11737   if ( !EndRead3dmChunk() ) // end of TCODE_LEGACY_FACSTUFF chunk
11738     rc = false;
11739 
11740   if ( !rc ) {
11741     delete brep;
11742   }
11743   else {
11744     brep->SetVertices();
11745     brep->SetTrimIsoFlags();
11746     brep->SetTolsFromLegacyValues();
11747     *ppObject = brep;
11748   }
11749 
11750   return rc;
11751 }
11752 
11753 bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_SHL(
11754   ON_Object** ppObject,
11755   ON_3dmObjectAttributes* pAttributes
11756   )
11757 {
11758   // read v1 TCODE_LEGACY_SHL chunk
11759   ON_BOOL32 bHaveMat = false;
11760   if ( !Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_LEGACY_SHLSTUFF) )
11761     return false;
11762   if ( !BeginRead3dmLEGACYSTUFF( *this, TCODE_LEGACY_SHLSTUFF ) )
11763     return false;
11764   ON_Brep* brep = new ON_Brep();
11765   bool rc = brep->ReadV1_LegacyShellStuff( *this );
11766   if ( !EndRead3dmChunk() ) // end of TCODE_LEGACY_SHLSTUFF chunk
11767     rc = false;
11768 
11769   if ( !rc ) {
11770     delete brep;
11771   }
11772   else {
11773     brep->SetVertices();
11774     brep->SetTrimIsoFlags();
11775     brep->SetTolsFromLegacyValues();
11776     *ppObject = brep;
11777   }
11778 
11779   return rc;
11780 }
11781 
11782 
11783 static
11784 ON_NurbsCurve* ReadV1_RHINOIO_NURBS_CURVE_OBJECT_DATA( ON_BinaryArchive& file )
11785 {
11786   // read TCODE_RHINOIO_OBJECT_DATA chunk that is contained in a
11787   // TCODE_RHINOIO_OBJECT_NURBS_CURVE chunk.  The TCODE_RHINOIO_OBJECT_DATA
11788   // chunk contains the definition of NURBS curves written by the 
11789   // old RhinoIO toolkit.
11790   ON_NurbsCurve* curve = 0;
11791   ON_BOOL32 rc = false;
11792   ON__UINT32 tcode = 0;
11793   ON__INT64 big_value = 0;
11794   int version, dim, is_rat, order, cv_count, flag, i;
11795   if ( !file.BeginRead3dmBigChunk( &tcode, &big_value ) )
11796     return NULL;
11797   if ( tcode == TCODE_RHINOIO_OBJECT_DATA ) for (;;) {
11798     if ( !file.ReadInt(&version) )
11799       break;
11800     // int bReverse = version & 0x100;
11801     version &= 0xFFFFFEFF;
11802     if ( version != 100 && version != 101 )
11803       break;
11804     file.ReadInt(&dim);
11805     if ( dim < 1 )
11806       break;
11807     file.ReadInt(&is_rat);
11808     if ( is_rat < 0 || is_rat > 1 )
11809       break;
11810     file.ReadInt(&order);
11811     if ( order < 2 )
11812       break;
11813     file.ReadInt(&cv_count);
11814     if ( cv_count < order )
11815       break;
11816     file.ReadInt(&flag);
11817     if ( flag != 0 )
11818       break;
11819 
11820     curve = new ON_NurbsCurve(dim,is_rat,order,cv_count);
11821     if ( !file.ReadDouble( order+cv_count-2, curve->m_knot ) )
11822       break;
11823     int cvdim = is_rat ? dim+1 : dim;
11824     for ( i = 0; i < cv_count; i++ ) {
11825       if ( !file.ReadDouble( cvdim, curve->CV(i) ) )
11826         break;
11827     }
11828     if ( i < cv_count )
11829       break;
11830     rc = true;
11831     break;
11832   }
11833   if ( !file.EndRead3dmChunk() ) // end of TCODE_RHINOIO_OBJECT_DATA chunk
11834     rc = false;
11835   if ( !rc && curve ) {
11836     delete curve;
11837     curve = 0;
11838   }
11839 
11840   return curve;
11841 }
11842 
11843 static
11844 ON_NurbsSurface* ReadV1_RHINOIO_NURBS_SURFACE_OBJECT_DATA( ON_BinaryArchive& file )
11845 {
11846   // read TCODE_RHINOIO_OBJECT_DATA chunk that is contained in a
11847   // TCODE_RHINOIO_OBJECT_NURBS_SURFACE chunk.  The TCODE_RHINOIO_OBJECT_DATA
11848   // chunk contains the definition of NURBS surfaces written by the 
11849   // old RhinoIO toolkit.
11850   bool rc = false;
11851   ON_NurbsSurface* surface = 0;
11852   ON__UINT32 tcode = 0;
11853   ON__INT64 big_value = 0;
11854   int version, dim, is_rat, order[2], cv_count[2], flag, i, j;
11855 
11856   if ( !file.BeginRead3dmBigChunk( &tcode, &big_value ) )
11857     return NULL;
11858   if ( tcode == TCODE_RHINOIO_OBJECT_DATA ) for (;;) {
11859     if ( !file.ReadInt(&version) )
11860       break;
11861     // int bReverse = version & 0x100;
11862     version &= 0xFFFFFEFF;
11863     if ( version != 100 && version != 101 )
11864       break;
11865     file.ReadInt(&dim);
11866     if ( dim < 1 )
11867       break;
11868     file.ReadInt(&is_rat);
11869     if ( is_rat < 0 || is_rat > 1 )
11870       break;
11871     file.ReadInt(&order[0]);
11872     if ( order[0] < 2 )
11873       break;
11874     file.ReadInt(&order[1]);
11875     if ( order[1] < 2 )
11876       break;
11877     file.ReadInt(&cv_count[0]);
11878     if ( cv_count[0] < order[0] )
11879       break;
11880     file.ReadInt(&cv_count[1]);
11881     if ( cv_count[1] < order[1] )
11882       break;
11883     file.ReadInt(&flag);
11884     if ( flag != 0 )
11885       break;
11886 
11887     surface = new ON_NurbsSurface(dim,is_rat,order[0],order[1],cv_count[0],cv_count[1]);
11888     if ( !file.ReadDouble( order[0]+cv_count[0]-2, surface->m_knot[0] ) )
11889       break;
11890     if ( !file.ReadDouble( order[1]+cv_count[1]-2, surface->m_knot[1] ) )
11891       break;
11892     int cvdim = is_rat ? dim+1 : dim;
11893     for ( i = 0; i < cv_count[0]; i++ ) {
11894       for ( j = 0; j < cv_count[1]; j++ ) {
11895         if ( !file.ReadDouble( cvdim, surface->CV(i,j) ) )
11896           break;
11897       }
11898       if ( j < cv_count[1] )
11899         break;
11900     }
11901     if ( i < cv_count[0] )
11902       break;
11903     rc = true;
11904     break;
11905   }
11906   if ( !file.EndRead3dmChunk() ) // end of TCODE_RHINOIO_OBJECT_DATA
11907     rc = false;
11908   if ( !rc && surface ) {
11909     delete surface;
11910     surface = 0;
11911   }
11912   return surface;
11913 }
11914 
11915 bool ON_BinaryArchive::ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE(
11916   ON_Object** ppObject,
11917   ON_3dmObjectAttributes* pAttributes
11918   )
11919 {
11920   // read contents of ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE chunk
11921   // written by v1 RhinoIO toolkit
11922   ON_NurbsCurve* curve = 0;
11923   bool rc = false;
11924   ON_BOOL32 bHaveMat = false;
11925   
11926   // reads TCODE_RHINOIO_OBJECT_DATA header and nurbs curve definition
11927   curve = ReadV1_RHINOIO_NURBS_CURVE_OBJECT_DATA(*this);
11928 
11929   if ( curve ) {
11930     *ppObject = curve;
11931     rc = true;
11932     Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_RHINOIO_OBJECT_END);
11933   }
11934 
11935   return rc;
11936 }
11937 
11938 bool ON_BinaryArchive::ReadV1_TCODE_RHINOIO_OBJECT_NURBS_SURFACE(
11939   ON_Object** ppObject,
11940   ON_3dmObjectAttributes* pAttributes
11941   )
11942 {
11943   // read contents of TCODE_RHINOIO_OBJECT_NURBS_SURFACE chunk
11944   // written by v1 RhinoIO toolkit
11945   ON_BOOL32 bHaveMat = false;
11946   bool rc = false;
11947   ON_NurbsSurface* surface = 0;
11948 
11949   surface = ReadV1_RHINOIO_NURBS_SURFACE_OBJECT_DATA( *this );
11950 
11951   if ( surface ) {
11952     *ppObject = surface;
11953     rc = true;
11954     Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_RHINOIO_OBJECT_END);
11955   }
11956 
11957   return rc;
11958 }
11959 
11960 static
11961 ON_Curve* ReadV1_RHINOIO_BREP_CURVE( ON_BinaryArchive& file )
11962 {
11963   ON_Curve* curve = NULL;
11964   ON_PolyCurve* pcurve = NULL;
11965   ON_NurbsCurve* nurbs_curve = NULL;
11966   int segment_index, segment_count = 0;
11967   ON__UINT32 tcode = 0;
11968   ON__INT64 big_value = 0;
11969   
11970   if ( !file.ReadInt(&segment_count) )
11971     return NULL;
11972   if ( segment_count < 1 )
11973     return NULL;
11974 
11975   for ( segment_index = 0; segment_index < segment_count; segment_index++ ) {
11976     if ( !file.BeginRead3dmBigChunk(&tcode,&big_value) )
11977       break;
11978     if ( tcode == TCODE_RHINOIO_OBJECT_NURBS_CURVE ) {
11979       nurbs_curve = ReadV1_RHINOIO_NURBS_CURVE_OBJECT_DATA( file );
11980     }
11981     if ( !file.EndRead3dmChunk() )
11982       break;
11983     if ( !nurbs_curve )
11984       break;
11985     if ( segment_index == 0 ) {
11986       curve = nurbs_curve;
11987       nurbs_curve = 0;
11988     }
11989     else {
11990       if ( segment_index == 1 ) {
11991         pcurve = new ON_PolyCurve();
11992         pcurve->Append(curve);
11993         curve = pcurve;
11994       }
11995       pcurve->Append(nurbs_curve);
11996       nurbs_curve = NULL;
11997     }
11998   }
11999 
12000   if ( segment_index < segment_count ) {
12001     if ( nurbs_curve ) {
12002       delete nurbs_curve;
12003       nurbs_curve = 0;
12004     }
12005     if ( curve ) {
12006       delete curve;
12007       curve = 0;
12008     }
12009   }
12010   return curve;
12011 }
12012 
12013 bool ON_BinaryArchive::ReadV1_TCODE_RHINOIO_OBJECT_BREP(
12014   ON_Object** ppObject,
12015   ON_3dmObjectAttributes* pAttributes
12016   )
12017 {
12018   ON_3dPoint m_oldTrim_mP[2];
12019   ON_BOOL32 bHaveMat = false;
12020   bool rc = false;
12021   ON_Brep* brep = 0;
12022   ON__UINT32 tcode = 0;
12023   ON__INT64 big_value = 0;
12024   if ( !BeginRead3dmBigChunk( &tcode, &big_value ) )
12025     return false;
12026   if ( tcode == TCODE_RHINOIO_OBJECT_DATA ) for (;;) {
12027     int version = -1;
12028     int sz, i, j;
12029     double tol2d, tol3d;
12030     if ( !ReadInt( &version ) ) 
12031       break; // serialization version
12032     // version = 100 means the b-rep was written by the RhinoIO toolkit
12033     // version = 101 means the b-rep was written by Rhino 1.0
12034     if ( version != 100 && version != 101 ) {
12035       return false;
12036     }
12037 
12038     brep = new ON_Brep();
12039 
12040     // 2d trimming curves
12041     if ( !ReadInt( &sz ) )
12042       break;
12043     if ( sz < 1 ) {
12044       break;
12045     }
12046     brep->m_C2.Reserve(sz);
12047     for ( i = 0; i < sz; i++ ) {
12048       ON_Curve* curve = ReadV1_RHINOIO_BREP_CURVE( *this );
12049       if ( !curve )
12050         break;
12051       brep->m_C2.Append(curve);
12052     }
12053     if ( i < sz )
12054       break;
12055 
12056     // 3d trimming curves
12057     if ( !ReadInt( &sz ) )
12058       break;
12059     if ( sz < 1 ) {
12060       break;
12061     }
12062     brep->m_C3.Reserve(sz);
12063     for ( i = 0; i < sz; i++ ) {
12064       ON_Curve* curve = ReadV1_RHINOIO_BREP_CURVE( *this );
12065       if ( !curve )
12066         break;
12067       brep->m_C3.Append(curve);
12068     }
12069     if ( i < sz )
12070       break;
12071 
12072     // 3d untrimmed surfaces
12073     if ( !ReadInt( &sz ) )
12074       break;
12075     if ( sz < 1 ) {
12076       break;
12077     }
12078     brep->m_S.Reserve(sz);
12079     for ( i = 0; i < sz; i++ ) {
12080       ON_NurbsSurface* surface = 0;
12081       tcode = 0;
12082       big_value = 0;
12083       if ( !BeginRead3dmBigChunk(&tcode,&big_value) )
12084         break;
12085       if ( tcode == TCODE_RHINOIO_OBJECT_NURBS_SURFACE ) {
12086         surface = ReadV1_RHINOIO_NURBS_SURFACE_OBJECT_DATA( *this );
12087       }
12088       if ( !EndRead3dmChunk() )
12089         break;
12090       if ( !surface )
12091         break;
12092       brep->m_S.Append(surface);
12093     }
12094     if ( i < sz )
12095       break;
12096 
12097     // vertices
12098     ReadInt( &sz );
12099     brep->m_V.Reserve(sz);
12100     for ( i = 0; i < sz; i++ ) {
12101       ON_BrepVertex& vertex = brep->NewVertex();
12102       if ( !ReadInt( &vertex.m_vertex_index ) ) break;
12103       if ( !ReadPoint( vertex.point ) ) break;
12104       if ( !ReadArray( vertex.m_ei ) ) break;
12105       if ( !ReadDouble( &vertex.m_tolerance ) ) break;
12106     }
12107     if ( i < sz )
12108       break;
12109 
12110     // edges
12111     ReadInt( &sz );
12112     brep->m_E.Reserve(sz);
12113     for ( i = 0; i < sz; i++ ) 
12114     {
12115       ON_Interval proxy_domain;
12116       ON_BrepEdge& edge = brep->NewEdge();
12117       if ( !ReadInt( &edge.m_edge_index ) ) break;
12118       if ( !ReadInt( &edge.m_c3i ) ) break;
12119       if ( !ReadInterval( proxy_domain ) ) break;
12120       edge.SetProxyCurveDomain(proxy_domain);
12121       if ( !ReadInt( 2, edge.m_vi ) ) break;
12122       if ( !ReadArray( edge.m_ti ) ) break;
12123       if ( !ReadDouble( &edge.m_tolerance ) ) break;
12124     }
12125     if ( i < sz )
12126       break;
12127 
12128     // trims
12129     ReadInt( &sz );
12130     brep->m_T.Reserve(sz);
12131     for ( i = 0; i < sz; i++ ) {
12132       ON_BrepTrim& trim = brep->NewTrim();
12133       if ( !ReadInt( &trim.m_trim_index ) ) break;
12134       if ( !ReadInt( &trim.m_c2i ) ) break;
12135       ON_Interval d;
12136       if ( !ReadInterval( d ) ) 
12137         break;
12138       trim.SetProxyCurve(NULL,d);
12139       if ( !ReadInt( &trim.m_ei ) ) break;
12140       if ( !ReadInt( 2, trim.m_vi ) ) break;
12141       j = trim.m_bRev3d;
12142       if ( !ReadInt( &j ) ) break;
12143       trim.m_bRev3d = (j!=0);
12144       if ( !ReadInt( &j ) ) break;
12145       switch(j) {
12146       case 1: trim.m_type = ON_BrepTrim::boundary; break;
12147       case 2: trim.m_type = ON_BrepTrim::mated; break;
12148       case 3: trim.m_type = ON_BrepTrim::seam; break;
12149       case 4: trim.m_type = ON_BrepTrim::singular; break;
12150       }
12151       if ( !ReadInt( &j ) ) break; // legacy iso flag - ignore and recaluate
12152       if ( !ReadInt( &trim.m_li ) ) break;
12153       if ( !ReadDouble( 2, trim.m_tolerance ) ) break;
12154       if ( !ReadPoint( m_oldTrim_mP[0] ) ) break;
12155       if ( !ReadPoint( m_oldTrim_mP[1] ) ) break;
12156       if ( !ReadDouble( &tol2d ) ) break;
12157       if ( !ReadDouble( &tol3d ) ) break;
12158     }
12159     if ( i < sz )
12160       break;
12161 
12162     // loops
12163     ReadInt( &sz );
12164     brep->m_L.Reserve(sz);
12165     for ( i = 0; i < sz; i++ ) {
12166       ON_BrepLoop& loop = brep->NewLoop(ON_BrepLoop::unknown);
12167       if ( !ReadInt( &loop.m_loop_index ) ) break;
12168       if ( !ReadArray( loop.m_ti ) ) break;
12169       if ( !ReadInt( &j ) ) break;
12170       switch (j) {
12171       case 1: loop.m_type = ON_BrepLoop::outer; break;
12172       case 2: loop.m_type = ON_BrepLoop::inner; break;
12173       case 3: loop.m_type = ON_BrepLoop::slit; break;
12174       }
12175       if ( !ReadInt( &loop.m_fi ) ) break;
12176     }
12177     if ( i < sz )
12178       break;
12179 
12180     // faces
12181     ReadInt( &sz );
12182     brep->m_F.Reserve(sz);
12183     for ( i = 0; i < sz; i++ ) {
12184       ON_BrepFace& face = brep->NewFace();
12185       if ( !ReadInt( &face.m_face_index ) ) break;
12186       if ( !ReadArray( face.m_li ) ) break;
12187       if ( !ReadInt( &face.m_si ) ) break;
12188       int k = face.m_bRev;
12189       if ( !ReadInt( &k ) ) break;
12190       face.m_bRev = (k!=0);
12191     }
12192     if ( i < sz )
12193       break;
12194 
12195     // bounding box
12196     {
12197       ON_BoundingBox bbox;
12198       if ( !ReadPoint( bbox.m_min ) ) break;
12199       if ( !ReadPoint( bbox.m_max ) ) break;
12200     }
12201 
12202     rc = true;
12203     break;
12204   }
12205   if ( !EndRead3dmChunk() )
12206     rc = false;
12207   if ( rc && brep ) {
12208     brep->SetTrimIsoFlags();
12209     *ppObject = brep;
12210   }
12211   else {
12212     if ( brep )
12213       delete brep;
12214     rc = false;
12215   }
12216 
12217   if ( rc && brep ) {
12218     Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_RHINOIO_OBJECT_END);
12219   }
12220 
12221   return rc;
12222 }
12223 
12224 int
12225 ON_BinaryArchive::Read3dmV1Object( 
12226   ON_Object** ppObject,                // object is returned here
12227   ON_3dmObjectAttributes* pAttributes, // optional - object attributes 
12228   unsigned int object_filter           // optional filter made by or-ing object_type bits
12229   )
12230 {
12231   int rc = 0;
12232   // returns -1: failure
12233   //          0: end of geometry table
12234   //          1: success
12235   //          2: skipped filtered objects
12236 
12237   ON__UINT32 tcode = 0;
12238   ON__INT64 big_value = 0;
12239   // reads NURBS, point, and mesh objects
12240   for(;;)
12241   {
12242     tcode = 0;
12243     big_value = 0;
12244     if ( !BeginRead3dmBigChunk(&tcode,&big_value) ) {
12245       rc = 0; // at the end of the file
12246       break;
12247     }
12248     switch(tcode)
12249     {
12250 
12251     case  TCODE_TEXT_BLOCK:
12252     case  TCODE_ANNOTATION_LEADER:
12253     case  TCODE_LINEAR_DIMENSION:
12254     case  TCODE_ANGULAR_DIMENSION:
12255     case  TCODE_RADIAL_DIMENSION:
12256       if ( 0 != (ON::annotation_object & object_filter) )
12257       {
12258         if ( ReadV1_TCODE_ANNOTATION( tcode, ppObject, pAttributes ) )
12259           rc = 1;
12260         else
12261           rc = -1;
12262       }
12263       else
12264       {
12265         rc = 2;
12266       }
12267       break;
12268 
12269     case TCODE_RH_POINT:
12270       // v1 3d point
12271       if ( 0 != (ON::point_object & object_filter) ) {
12272         if (ReadV1_TCODE_RH_POINT( ppObject, pAttributes ))
12273           rc = 1;
12274         else
12275           rc = -1;
12276       }
12277       else {
12278         rc = 2;
12279       }
12280       break;
12281 
12282     case TCODE_MESH_OBJECT:
12283       // v1 mesh
12284       if ( 0 != (ON::mesh_object & object_filter) ) {
12285         if ( ReadV1_TCODE_MESH_OBJECT( ppObject, pAttributes ) )
12286           rc = 1;
12287         else
12288           rc = -1;
12289       }
12290       else {
12291         rc = 2;
12292       }
12293       break;
12294 
12295     case TCODE_LEGACY_SHL:
12296       // v1 polysurface
12297       if ( 0 != (ON::mesh_object & object_filter) ) {
12298         if ( ReadV1_TCODE_LEGACY_SHL( ppObject, pAttributes ) )
12299           rc = 1;
12300         else
12301           rc = -1;
12302       }
12303       else {
12304         rc = 2;
12305       }
12306       break;
12307 
12308     case TCODE_LEGACY_FAC:
12309       // v1 trimmed surface
12310       if ( 0 != (ON::mesh_object & object_filter) ) {
12311         if ( ReadV1_TCODE_LEGACY_FAC( ppObject, pAttributes ) )
12312           rc = 1;
12313         else
12314           rc = -1;
12315       }
12316       else {
12317         rc = 2;
12318       }
12319       break;
12320 
12321     case TCODE_LEGACY_CRV:
12322       // v1 curve
12323       if ( 0 != (ON::mesh_object & object_filter) ) {
12324         if ( ReadV1_TCODE_LEGACY_CRV( ppObject, pAttributes ) )
12325           rc = 1;
12326         else
12327           rc = -1;
12328       }
12329       else {
12330         rc = 2;
12331       }
12332       break;
12333 
12334     case TCODE_RHINOIO_OBJECT_NURBS_CURVE:
12335       // old Rhino I/O toolkit nurbs curve
12336       if ( 0 != (ON::mesh_object & object_filter) ) {
12337         if ( ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE( ppObject, pAttributes ) )
12338           rc = 1;
12339         else
12340           rc = -1;
12341       }
12342       else {
12343         rc = 2;
12344       }
12345       break;
12346 
12347     case TCODE_RHINOIO_OBJECT_NURBS_SURFACE:
12348       // old Rhino I/O toolkit nurbs surface
12349       if ( 0 != (ON::mesh_object & object_filter) ) {
12350         if ( ReadV1_TCODE_RHINOIO_OBJECT_NURBS_SURFACE( ppObject, pAttributes ) )
12351           rc = 1;
12352         else
12353           rc = -1;
12354       }
12355       else {
12356         rc = 2;
12357       }
12358       break;
12359 
12360     case TCODE_RHINOIO_OBJECT_BREP:
12361       // old Rhino I/O toolkit nurbs brep
12362       if ( 0 != (ON::mesh_object & object_filter) ) {
12363         if ( ReadV1_TCODE_RHINOIO_OBJECT_BREP( ppObject, pAttributes ) )
12364           rc = 1;
12365         else
12366           rc = -1;
12367       }
12368       else {
12369         rc = 2;
12370       }
12371       break;
12372     }
12373 
12374     if (!EndRead3dmChunk() )
12375       break;
12376     if ( rc == 1 || rc == -1 )
12377       break;
12378   }
12379 
12380   return rc;
12381 }
12382 
12383 #if 1
12384 class ON_OBSOLETE_CCustomMeshUserData : public ON_UserData
12385 {
12386 public:
12387         ON_OBJECT_DECLARE(ON_OBSOLETE_CCustomMeshUserData);
12388   ON_OBSOLETE_CCustomMeshUserData();
12389         ~ON_OBSOLETE_CCustomMeshUserData();
12390         ON_BOOL32 GetDescription( ON_wString& );
12391   ON_BOOL32 Read(ON_BinaryArchive& binary_archive);
12392   bool m_bInUse;
12393   ON_MeshParameters m_mp;
12394 };
12395 
12396 ON_OBJECT_IMPLEMENT(ON_OBSOLETE_CCustomMeshUserData, ON_UserData, "69F27695-3011-4FBA-82C1-E529F25B5FD9");
12397 
12398 ON_OBSOLETE_CCustomMeshUserData::ON_OBSOLETE_CCustomMeshUserData()
12399 {
12400         m_userdata_copycount = 0;
12401         m_userdata_uuid = ON_OBSOLETE_CCustomMeshUserData::m_ON_OBSOLETE_CCustomMeshUserData_class_id.Uuid();
12402   m_application_uuid = ON_nil_uuid;
12403   m_bInUse = false;
12404 }
12405 
12406 ON_OBSOLETE_CCustomMeshUserData::~ON_OBSOLETE_CCustomMeshUserData()
12407 {
12408 }
12409 
12410 ON_BOOL32 ON_OBSOLETE_CCustomMeshUserData::Read(ON_BinaryArchive& ba)
12411 {
12412   int i = 0;
12413   if ( !ba.ReadInt( &i ) )
12414     return false;    
12415   if( !ba.ReadBool( &m_bInUse ) )
12416     return false;    
12417   return m_mp.Read( ba );
12418 }
12419 
12420 ON_BOOL32 ON_OBSOLETE_CCustomMeshUserData::GetDescription( ON_wString& s )
12421 {
12422         s = "OBSOLETE CustomMeshUserData";
12423         return true;
12424 }
12425 #endif
12426 
12427 
12428 int
12429 ON_BinaryArchive::Read3dmObject( 
12430   ON_Object** ppObject,                // object is returned here
12431   ON_3dmObjectAttributes* pAttributes, // optional - object attributes 
12432   unsigned int object_filter           // optional filter made by or-ing object_type bits
12433   )
12434 {
12435   // returns -1: failure
12436   //          0: end of geometry table
12437   //          1: success
12438   //          2: skipped filtered objects
12439   //          3: skipped new object (object's class UUID wasn't found in class list)
12440   ON_BOOL32 rc = -1;
12441   if ( pAttributes )
12442     pAttributes->Default();
12443   if ( !ppObject )
12444     return 0;
12445   *ppObject = 0;
12446   if ( !object_filter ) // default filter (0) reads every object
12447     object_filter = 0xFFFFFFFF;
12448 
12449   if ( m_3dm_version == 1 ) {
12450     rc = Read3dmV1Object(ppObject,pAttributes,object_filter);
12451   }
12452   else 
12453   {
12454     ON__UINT32 tcode = 0;
12455     ON__INT64 length_TCODE_OBJECT_RECORD = 0;
12456     ON__INT64 value_TCODE_OBJECT_RECORD_TYPE = 0;
12457     ON__INT64 length_TCODE_OBJECT_RECORD_ATTRIBUTES = 0;
12458     if ( BeginRead3dmBigChunk( &tcode, &length_TCODE_OBJECT_RECORD ) ) 
12459     {
12460       if ( tcode == TCODE_OBJECT_RECORD ) 
12461       {
12462         if (BeginRead3dmBigChunk( &tcode, &value_TCODE_OBJECT_RECORD_TYPE )) 
12463         {
12464           if ( tcode != TCODE_OBJECT_RECORD_TYPE ) {
12465             rc = -1;
12466             ON_ERROR("ON_BinaryArchive::Read3dmObject() - missing TCODE_OBJECT_RECORD_TYPE chunk.");
12467           }
12468           else if ( 0 != value_TCODE_OBJECT_RECORD_TYPE && 0 == (value_TCODE_OBJECT_RECORD_TYPE | object_filter) )
12469             rc = 2; // skip reading this object
12470           else
12471             rc = 1; // need to read this object
12472 
12473           if ( !EndRead3dmChunk() )
12474             rc = -1;
12475 
12476           switch(ReadObject(ppObject))
12477           {
12478           case 1:
12479             rc = 1; // successfully read this object
12480             break;
12481           case 3:
12482             rc = 3; // skipped object - assume it's just a newer object than this code reads
12483             break;
12484           default:
12485             rc = -1; // serious failure
12486             break;
12487           }
12488         }
12489         else
12490           rc = -1;
12491       }
12492       else if ( tcode != TCODE_ENDOFTABLE ) {
12493         ON_ERROR("ON_BinaryArchive::Read3dmObject() - corrupt object table");
12494         rc = -1;
12495       }
12496       else
12497         rc = 0;
12498 
12499       while(rc==1)
12500       {
12501         tcode = 0;
12502         if (!BeginRead3dmBigChunk( &tcode, &length_TCODE_OBJECT_RECORD_ATTRIBUTES )) {
12503           rc = -1;
12504           break;
12505         }
12506         if ( tcode == TCODE_OBJECT_RECORD_ATTRIBUTES ) 
12507         {
12508           if ( 0 != pAttributes )
12509           {
12510             if ( !pAttributes->Read( *this ) )
12511               rc = -1;
12512           }
12513         }
12514         else if ( tcode == TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA )
12515         {
12516           if ( 0 != pAttributes )
12517           {
12518             // 19 October 2004
12519             //   Added support for saving user data on object attributes
12520             if ( !ReadObjectUserData(*pAttributes))
12521               rc = -1;
12522             else
12523             {
12524 #if 1
12525               // 3 March 2011 - convert obsolete user data
12526               ON_OBSOLETE_CCustomMeshUserData* ud = ON_OBSOLETE_CCustomMeshUserData::Cast(pAttributes->GetUserData(ON_OBSOLETE_CCustomMeshUserData::m_ON_OBSOLETE_CCustomMeshUserData_class_id.Uuid()));
12527               if ( ud )
12528               {
12529                 ud->m_mp.m_bCustomSettingsEnabled = ud->m_bInUse ? true : false;
12530                 pAttributes->SetCustomRenderMeshParameters(ud->m_mp);
12531                 delete ud;
12532               }
12533 #endif
12534             }
12535           }
12536         }
12537 
12538         if ( !EndRead3dmChunk() ) 
12539         {
12540           rc = -1;
12541         }
12542         if ( tcode == TCODE_OBJECT_RECORD_END )
12543           break;
12544       }
12545 
12546       if ( !EndRead3dmChunk() )
12547         rc = -1;
12548     }
12549   }
12550 
12551   return rc;
12552 }
12553 
12554 bool ON_BinaryArchive::EndRead3dmObjectTable()
12555 {
12556   bool rc = EndRead3dmTable( TCODE_OBJECT_TABLE );
12557 
12558   if ( 0 != m_V1_layer_list )
12559   {
12560     struct ON__3dmV1LayerIndex* next = m_V1_layer_list;
12561     m_V1_layer_list = 0;
12562     for ( int i = 0; 0 != next && i < 1000; i++ )
12563     {
12564       struct ON__3dmV1LayerIndex* p = next;
12565       next = p->m_next;
12566       onfree(p);
12567     }
12568   }
12569 
12570   return rc;
12571 }
12572 
12573 bool ON_BinaryArchive::BeginWrite3dmUserTable( const ON_UUID& usertable_uuid )
12574 {
12575   return BeginWrite3dmUserTable(usertable_uuid, false, 0, 0 );
12576 }
12577 
12578 bool ON_BinaryArchive::BeginWrite3dmUserTable(
12579     const ON_UUID& plugin_id,
12580     bool bSavingGoo,
12581     int goo_3dm_version,
12582     int goo_opennurbs_version
12583     )
12584 {
12585   if ( m_active_table != no_active_table ) {
12586     ON_ERROR("ON_BinaryArchive::BeginWrite3dmUserTable() - m_active_table != no_active_table");
12587   }
12588   if ( !ON_UuidCompare( &ON_nil_uuid, &plugin_id ) ) {
12589     ON_ERROR("ON_BinaryArchive::BeginWrite3dmUserTable() - nil usertable_uuid not permitted.");
12590     return false;
12591   }
12592   if ( bSavingGoo )
12593   {
12594     if ( goo_3dm_version <= 3 )
12595       return false;
12596     if ( goo_opennurbs_version < 200601010 )
12597       return false;
12598     if ( goo_3dm_version >= 50 && Archive3dmVersion() < 50 )
12599     {
12600       // goo with 8 byte chunk lengths cannot be saved 
12601       // in files expecting 4 byte chunk lengths.
12602       return false;
12603     }
12604   }
12605   else
12606   {
12607     goo_3dm_version = Archive3dmVersion();
12608     goo_opennurbs_version = ArchiveOpenNURBSVersion();
12609   }
12610 
12611   bool rc = BeginWrite3dmTable( TCODE_USER_TABLE);
12612   if (rc) {
12613     rc = BeginWrite3dmChunk( TCODE_USER_TABLE_UUID, 0 );
12614     if (rc)
12615     {
12616       rc = WriteUuid( plugin_id );
12617       if (rc)
12618       {
12619         // The TCODE_USER_TABLE_RECORD_HEADER chunk was added in 
12620         // version 200910190 to contain the archive and opennurbs
12621         // version the plug-in used when writing the file.
12622         // This information is needed so "goo" can be correctly
12623         // read.
12624         rc = BeginWrite3dmChunk( TCODE_USER_TABLE_RECORD_HEADER, 1, 0 );
12625         if ( rc )
12626         {
12627           if (rc) rc = WriteBool(bSavingGoo);
12628           if (rc) rc = WriteInt(goo_3dm_version);
12629           if (rc) rc = WriteInt(goo_opennurbs_version);
12630           if ( !EndWrite3dmChunk() )
12631             rc = false;
12632         }
12633       }
12634       if ( !EndWrite3dmChunk() )
12635         rc = false;
12636     }
12637     if (rc) {
12638       rc = BeginWrite3dmChunk( TCODE_USER_RECORD, 0 );
12639     }
12640     if ( !rc ) {
12641       EndWrite3dmTable( TCODE_USER_TABLE);
12642     }
12643   }
12644   return rc;
12645 }
12646 
12647 bool ON_BinaryArchive::Write3dmAnonymousUserTableRecord( 
12648     const ON_UUID& plugin_id,
12649     int goo_3dm_version,
12650     int goo_opennurbs_version,
12651     const ON_3dmGoo& goo
12652     )
12653 {
12654   if ( ON_UuidIsNil(plugin_id) )
12655     return false;
12656   if ( goo_3dm_version <= 3 )
12657     return false;
12658   if (goo_opennurbs_version < 200000000 )
12659     return false;
12660   if ( goo.m_typecode != TCODE_USER_RECORD )
12661     return false;
12662   if ( 0 == goo.m_value )
12663     return false;
12664   if ( 0 == goo.m_goo )
12665     return false;
12666   bool bSavingGoo = true;
12667   if ( !BeginWrite3dmUserTable( plugin_id, bSavingGoo, goo_3dm_version, goo_opennurbs_version ) )
12668     return false;
12669   bool rc = WriteByte( goo.m_value, goo.m_goo );
12670   if ( !EndWrite3dmUserTable() )
12671     rc = false;
12672   return rc;
12673 }
12674 
12675 
12676 bool ON_BinaryArchive::Write3dmAnonymousUserTable( const ON_3dmGoo& goo )
12677 {
12678   bool rc = false;
12679   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
12680   if ( !c || c->m_typecode != TCODE_USER_RECORD ) {
12681     ON_ERROR("ON_BinaryArchive::Write3dmAnonymousUserTable() - active chunk not a TCODE_USER_RECORD.");
12682     rc = false;
12683   }
12684   else if ( goo.m_typecode != TCODE_USER_RECORD ) {
12685     ON_ERROR("ON_BinaryArchive::Write3dmAnonymousUserTable() - goo chunk not a TCODE_USER_RECORD.");
12686     rc = false;
12687   }
12688   else {
12689     rc = ( goo.m_value > 0 ) ? WriteByte( goo.m_value, goo.m_goo ) : true;
12690   }
12691   return rc;
12692 }
12693 
12694 
12695 bool ON_BinaryArchive::EndWrite3dmUserTable()
12696 {
12697   bool rc = false;
12698   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
12699   if ( c && c->m_typecode == TCODE_USER_RECORD ) {
12700     rc = EndWrite3dmChunk();
12701   }
12702   else {
12703     ON_ERROR("ON_BinaryArchive::EndWrite3dmUserTable() - not in a TCODE_USER_RECORD chunk.");
12704     rc = false;
12705   }
12706   if ( !EndWrite3dmTable(TCODE_USER_TABLE) )
12707     rc = false;
12708   return rc;
12709 }
12710 
12711 bool ON_BinaryArchive::BeginRead3dmUserTable( ON_UUID& usertable_uuid )
12712 {
12713   // OBSOLETE - use the  BeginRead3dmUserTable() with more arguments
12714   bool bGoo = false;
12715   int archive_3dm_version = 0;
12716   int archive_opennurbs_version = 0;
12717   bool rc = BeginRead3dmUserTable( usertable_uuid, &bGoo, &archive_3dm_version, &archive_opennurbs_version );
12718   return rc;
12719 }
12720 
12721 bool ON_BinaryArchive::BeginRead3dmUserTable(
12722     ON_UUID& plugin_id,
12723     bool* bLastSavedAsGoo,
12724     int* archive_3dm_version,
12725     int* archive_opennurbs_version
12726     )
12727 {
12728   bool bReadArchiveInfo = false;
12729   if ( bLastSavedAsGoo )
12730     *bLastSavedAsGoo = false;
12731   if ( archive_3dm_version )
12732     *archive_3dm_version = 0;
12733   if ( archive_opennurbs_version )
12734     *archive_opennurbs_version = 0;
12735 
12736   if ( m_3dm_version == 1 )
12737     return false;
12738 
12739   bool rc = BeginRead3dmTable( TCODE_USER_TABLE );
12740 
12741   // Do not add calls to EmergencyFindTable() here.
12742   // BeginRead3dmTable( TCODE_USER_TABLE ) returns
12743   // false when there are no user tables and that
12744   // is a common situation.
12745 
12746   if ( rc ) 
12747   {
12748     // read table id
12749     ON__UINT32 tcode = 0;
12750     ON__INT64 big_value = 0;
12751     if (rc) rc = BeginRead3dmBigChunk( &tcode, &big_value );
12752     if (rc)
12753     {
12754       if ( tcode != TCODE_USER_TABLE_UUID ) 
12755       {
12756         ON_ERROR("ON_BinaryArchive::BeginRead3dmUserTable() - missing user table UUID");
12757         rc = false;
12758       }
12759       else 
12760       {
12761         rc = ReadUuid( plugin_id );
12762 
12763         // Version 200910190 of OpenNURBS began writing a TCODE_USER_TABLE_RECORD_HEADER
12764         // section immediately after the uuid.  This was possible because the uuid
12765         // was wrapped in a TCODE_USER_TABLE_UUID chunk.  The TCODE_USER_TABLE_RECORD_HEADER
12766         // contains information that let's us determine what version of Rhino and
12767         // opennurbs wrote the user table.  We need to know this because "goo"
12768         // can have chunks with 4 byte lengths embedded in a file with 8 byte
12769         // chunk lengths.  If this information is missing, then we know the "goo"
12770         // must have 4 byte chunk lengths and we assume it is from a V4 file.
12771         //
12772         // 37 + SizeofChunkLength() = 
12773         //      16 bytes of uuid
12774         //    + 4 bytes of TCODE_USER_TABLE_RECORD_HEADER typecode
12775         //    + SizeofChunkLength() TCODE_USER_TABLE_RECORD_HEADER chunk length
12776         //    + 1 byte of bSlastSavedAsGoo bool
12777         //    + 4 bytes of archive_3dm_version
12778         //    + 4 bytes of archive_opennurbs_version
12779         //    + 4 bytes of TCODE_USER_TABLE_RECORD_HEADER chunk crc
12780         //    + 4 bytes of TCODE_USER_TABLE_UUID chunk crc
12781         const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
12782         if ( rc 
12783              && ArchiveOpenNURBSVersion() >= 200910190 
12784              && 0 != c
12785              && TCODE_USER_TABLE_UUID == c->m_typecode
12786              && c->Length() >= 45 + SizeofChunkLength()
12787            )
12788         {
12789           int major_chunk_version = 0;
12790           int minor_chunk_version = 0;
12791           rc = BeginRead3dmChunk(TCODE_USER_TABLE_RECORD_HEADER,&major_chunk_version,&minor_chunk_version);
12792           if (rc)
12793           {
12794             bReadArchiveInfo = true;
12795             bool b = true;
12796             int arch_ver = 0;
12797             int on_ver = 0;
12798             rc = ReadBool(&b);
12799             if ( rc && bLastSavedAsGoo )
12800               *bLastSavedAsGoo = b;
12801             if (rc)
12802               rc = ReadInt(&arch_ver);
12803             if (rc && archive_3dm_version)
12804               *archive_3dm_version = arch_ver;
12805             if (rc)
12806               rc = ReadInt(&on_ver);
12807             if (rc && archive_opennurbs_version)
12808               *archive_opennurbs_version = on_ver;
12809             if ( !EndRead3dmChunk() )
12810               rc = false;
12811           }
12812         }
12813       }
12814       if ( !EndRead3dmChunk() )
12815         rc = false;
12816     }
12817 
12818     tcode = 0;
12819     big_value = 0;
12820     if (rc) rc = BeginRead3dmBigChunk( &tcode, &big_value );
12821     if (rc) {
12822       if ( tcode != TCODE_USER_RECORD ) {
12823         ON_ERROR("ON_BinaryArchive::BeginRead3dmUserTable() - missing user table TCODE_USER_RECORD chunk.");
12824         EndRead3dmChunk();
12825         rc = false;
12826       }
12827     }
12828 
12829     if (!rc)
12830       EndRead3dmTable(TCODE_USER_TABLE);
12831 
12832     if ( rc && !bReadArchiveInfo )
12833     {
12834       // If the file we are reading is V4 or an early V5 file, then use the 
12835       // version numbers from the file.  Otherwise, assume the goo is from
12836       // an early V5 file.  All we know for sure is that the chunk lengths
12837       // in the user table are 4 bytes.
12838       if ( Archive3dmVersion() < 50 )
12839       {
12840         if (archive_3dm_version)
12841           *archive_3dm_version = Archive3dmVersion();
12842         if (archive_opennurbs_version)
12843           *archive_opennurbs_version = ArchiveOpenNURBSVersion();
12844       }
12845       else
12846       {
12847         if (archive_3dm_version) 
12848           *archive_3dm_version = 5;
12849         if (archive_opennurbs_version) 
12850           *archive_opennurbs_version = 200910180;
12851       }
12852     }
12853   }
12854 
12855 
12856   return rc;
12857 }
12858 
12859 bool ON_BinaryArchive::Read3dmAnonymousUserTable( ON_3dmGoo& goo )
12860 {
12861   return Read3dmAnonymousUserTable(0,0,goo);
12862 }
12863 
12864 bool ON_BinaryArchive::Read3dmAnonymousUserTable( 
12865     int archive_3dm_version,
12866     int archive_opennurbs_version,
12867     ON_3dmGoo& goo
12868     )
12869 {
12870   if ( 0 == archive_3dm_version )
12871   {
12872     if ( Archive3dmVersion() < 50 )
12873     {
12874       archive_3dm_version = Archive3dmVersion();
12875       archive_opennurbs_version = ArchiveOpenNURBSVersion();
12876     }
12877     else
12878     {
12879       // recent version with 4 byte chunk lengths.
12880       archive_3dm_version = 5;
12881       archive_opennurbs_version = 200910190;
12882     }
12883   }
12884   bool rc = Read3dmGoo( goo );
12885   if (rc && goo.m_typecode != TCODE_USER_RECORD ) {
12886     ON_ERROR("ON_BinaryArchive::Read3dmAnonymousUserTable() do not read a TCODE_USER_RECORD chunk.");
12887     rc = false;
12888   }
12889   return rc;
12890 }
12891 
12892 bool ON_BinaryArchive::EndRead3dmUserTable()
12893 {
12894   if ( m_chunk.Count() != 2 ) {
12895     ON_ERROR("ON_BinaryArchive::EndRead3dmUserTable() m_chunk.Count() != 2");
12896     return false;
12897   }
12898   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
12899   if ( 0 == c || c->m_typecode != TCODE_USER_RECORD ) {
12900     ON_ERROR("ON_BinaryArchive::EndRead3dmTable() m_chunk.Last()->typecode != TCODE_USER_RECORD");
12901     return false;
12902   }
12903 
12904 
12905   // end of TCODE_USER_RECORD chunk
12906   // Suppress the partially read chunk warning because plug-in IO
12907   // is too upredictable for this warning to be helpful.
12908   bool rc = EndRead3dmChunk(true); 
12909 
12910   if (rc) {
12911     // end of table chunk
12912     unsigned int tcode = 0;
12913     ON__INT64 big_value = -1;
12914     rc = BeginRead3dmBigChunk( &tcode, &big_value );
12915     if ( rc ) {
12916       if ( tcode != TCODE_ENDOFTABLE ) {
12917         ON_ERROR("ON_BinaryArchive::EndRead3dmTable() missing TCODE_ENDOFTABLE marker.");
12918       }
12919       if ( !EndRead3dmChunk() )
12920         rc = false;
12921     }
12922   }
12923 
12924   if ( !EndRead3dmTable(TCODE_USER_TABLE) )
12925     rc = false;
12926   return rc;
12927 }
12928 
12929 bool ON_BinaryArchive::Write3dmEndMark()
12930 {
12931   Flush();
12932   if ( m_chunk.Count() != 0 ) {
12933     ON_ERROR( "ON_BinaryArchive::WriteEndMark() called with unfinished chunks.\n" );
12934     return false;
12935   }
12936   ON__UINT64 length = CurrentPosition(); // since no chunks are unfinished, everything
12937                                   // has been committed to disk in either
12938                                   // write mode.
12939   bool rc = BeginWrite3dmChunk( TCODE_ENDOFFILE, 0 );
12940   if ( rc ) 
12941   {    
12942     size_t sizeof_chunk_length = SizeofChunkLength();
12943     size_t sizeoffile_length = (8==SizeofChunkLength()) ? 8 : 4;
12944     length += (4 + sizeof_chunk_length + sizeoffile_length );
12945     rc = WriteEOFSizeOfFile(length);
12946     if ( !EndWrite3dmChunk() )
12947       rc = false;
12948   }
12949   Flush();
12950 
12951   return rc;
12952 }
12953 
12954 bool ON_BinaryArchive::Read3dmEndMark( size_t* file_length )
12955 {
12956   unsigned int tcode=0;
12957   ON__INT64 value=0;
12958   if ( file_length )
12959     *file_length = 0;
12960 
12961   const unsigned int saved_error_message_mask = m_error_message_mask;
12962   m_error_message_mask |= 0x0001; // disable v1 ReadByte() error message at EOF
12963   bool rc = PeekAt3dmBigChunkType(&tcode,&value);
12964   m_error_message_mask = saved_error_message_mask;
12965 
12966   if (rc)
12967   {
12968     if ( tcode == TCODE_ENDOFFILE ) 
12969     {
12970       rc = BeginRead3dmBigChunk(&tcode,&value);
12971       if ( rc && value > 0 && ((ON__UINT64)value) >= SizeofChunkLength() )
12972       {
12973         ON__UINT64 u64 = 0;
12974         rc = ReadEOFSizeOfFile( &u64 );
12975         if ( rc && file_length )
12976           *file_length = (size_t)u64;
12977         if ( !EndRead3dmChunk() )
12978           rc = false;
12979       }
12980     }
12981   }
12982   return rc;
12983 }
12984 
12988 
12989 ON::endian
12990 ON_BinaryArchive::Endian() const // endian-ness of cpu
12991 {
12992   return m_endian;
12993 }
12994 
12995 ON::archive_mode
12996 ON_BinaryArchive::Mode() const
12997 {
12998   return m_mode;
12999 }
13000 
13001 void ON_BinaryArchive::UpdateCRC( size_t count, const void* p )
13002 {
13003   if ( m_bDoChunkCRC ) {
13004     ON_3DM_BIG_CHUNK* c = m_chunk.Last();
13005     if (c) {
13006       if ( c->m_do_crc16 )
13007         c->m_crc16 = ON_CRC16( c->m_crc16, count, p ); // version 1 files had 16 bit CRC
13008       if ( c->m_do_crc32 )
13009         c->m_crc32 = ON_CRC32( c->m_crc32, count, p ); // version 2 files have 32 bit CRC
13010     }
13011   }
13012 }
13013 
13014 int ON_BinaryArchive::BadCRCCount() const
13015 {
13016   return m_bad_CRC_count;
13017 }
13018 
13019 unsigned int ON_BinaryArchive::ErrorMessageMask() const
13020 {
13021   return m_error_message_mask;
13022 }
13023 
13024 bool ON_BinaryArchive::MaskReadError( ON__UINT64 sizeof_request, ON__UINT64 sizeof_read ) const
13025 {
13026   if ( sizeof_request == sizeof_read )
13027     return true; // no error
13028   if ( sizeof_request > sizeof_read )
13029     return false; // something is seriously wrong
13030   if ( 0 != (0x04 & m_error_message_mask) )
13031     return true;
13032   if ( 0 != (0x01 & m_error_message_mask) && 4 == sizeof_request && 0 == sizeof_read )
13033     return true;
13034   return false; // parial read not permitted at this time.
13035 }
13036 
13037 ON__UINT64 ON_BinaryArchive::ReadBuffer( ON__UINT64 sizeof_buffer, void* buffer )
13038 {
13039   // Expert user function to load a buffer with up to sizeof_buffer
13040   // bytes but tolerate encountering the end of the file.
13041   if ( 0 == buffer )
13042     return 0;
13043   unsigned int saved_error_mask = m_error_message_mask;
13044   m_error_message_mask |= 0x04; // tell Read to tolerate hitting the end of the file
13045   ON__UINT64 sizeof_read = Read((size_t)sizeof_buffer,buffer);
13046   m_error_message_mask = saved_error_mask;
13047   return sizeof_read;
13048 }
13049 
13050 
13054 
13055 ON_BinaryFile::ON_BinaryFile( ON::archive_mode mode ) 
13056               : ON_BinaryArchive( mode ), 
13057                 m_fp(0),
13058                 m_memory_buffer_capacity(0),
13059                 m_memory_buffer_size(0),
13060                 m_memory_buffer_ptr(0),
13061                 m_memory_buffer(0)
13062 {}
13063 
13064 ON_BinaryFile::ON_BinaryFile( ON::archive_mode mode, FILE* fp ) 
13065               : ON_BinaryArchive( mode ), 
13066                 m_fp(fp),
13067                 m_memory_buffer_capacity(0),
13068                 m_memory_buffer_size(0),
13069                 m_memory_buffer_ptr(0),
13070                 m_memory_buffer(0)
13071 {}
13072 
13073 ON_BinaryFile::~ON_BinaryFile()
13074 {
13075   EnableMemoryBuffer(0);
13076 }
13077 
13078 bool
13079 ON_BinaryArchive::ReadByte( size_t count, void* p )
13080 {
13081   bool rc = false;
13082   if ( count > 0 ) {
13083     if ( !ReadMode() ) {
13084       ON_ERROR("ON_BinaryArchive::ReadByte() ReadMode() is false.");
13085     }
13086     else if ( p ) 
13087     {
13088 
13089 #if defined(ON_DEBUG)
13090       {
13091         // this is slow (becuase of the call to CurrentPosition(), but it really helps find bugs in IO code
13092         const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
13093         if ( c )
13094         {
13095           ON__UINT64 file_offset = CurrentPosition();
13096           if ( file_offset < c->m_big_offset )
13097           {
13098             ON_ERROR("ON_BinaryArchive::ReadByte will read before the start of the chunk.");
13099           }
13100           else if (c->m_big_offset + c->Length() < file_offset + count )
13101           {
13102             ON_ERROR("ON_BinaryArchive::ReadByte will read past end of the chunk");
13103           }
13104         }
13105       }
13106 #endif
13107 
13108       size_t readcount = Read( count, p );
13109       if ( readcount == count ) 
13110       {
13111         UpdateCRC( count, p );
13112         rc = true;  
13113       }
13114       else 
13115       {
13116         // see if this is an error condition
13117         for(;;)
13118         {
13119           if ( 0 != (m_error_message_mask&0x01)
13120                && 0 == readcount && 4 == count 
13121              )
13122           {
13123             // when reading v1 files, there are some situations where
13124             // it is reasonable to attempt to read 4 bytes at the end
13125             // of a file.
13126             break;
13127           }
13128           if (    0 == m_3dm_version
13129                && 0 == m_3dm_opennurbs_version
13130                && 0 == m_3dm_start_section_offset
13131                && ON_BinaryArchive::no_active_table == m_active_table
13132                && 0 == m_chunk
13133                && ON::read3dm == m_mode
13134                )
13135           {
13136             // In Read3dmStartSection(), we search for the string 
13137             // "3D Geometry File Format ...".  When a non-.3dm file
13138             // is searched, we eventually reach the end of the file.
13139             // This error condition is reported by the returning
13140             // false from ON_BinaryArchive::Read3dmStartSection().
13141             // ON_ERROR is not called to prevent annoying everyone
13142             // when the open file dialog is digging around looking
13143             // for files.
13144             break;
13145           }
13146           ON_ERROR("ON_BinaryArchive::ReadByte() Read() failed.");
13147           break;
13148         }
13149       }
13150     }
13151     else 
13152     {
13153       ON_ERROR("ON_BinaryArchive::ReadByte() NULL file or buffer.");
13154     }
13155   }
13156   else 
13157   {
13158     rc = true;
13159   }
13160   return rc;
13161 }
13162 
13163 bool
13164 ON_BinaryArchive::WriteByte( size_t count, const void* p )
13165 {
13166   bool rc = false;
13167   if ( count > 0 ) {
13168     if ( !WriteMode() ) {
13169       ON_ERROR("ON_BinaryArchive::WriteByte() WriteMode() is false.");
13170     }
13171     else if ( p ) {
13172       size_t writecount = (size_t)Write( count, p );
13173       if ( writecount == count ) {
13174         UpdateCRC( count, p );
13175         rc = true;      
13176       }
13177       else {
13178         ON_ERROR("ON_BinaryArchive::WriteByte() fwrite() failed.");
13179       }
13180     }
13181     else {
13182       ON_ERROR("ON_BinaryArchive::WriteByte() NULL file or buffer.");
13183     }
13184   }
13185   else {
13186     rc = true;
13187   }
13188   return rc;
13189 }
13190 
13191 bool ON_BinaryArchive::EnableCRCCalculation( bool bEnable )
13192 {
13193   bool rc = m_bDoChunkCRC;
13194   m_bDoChunkCRC = bEnable ? true : false;
13195   return rc;
13196 }
13197 
13198 bool
13199 ON_BinaryArchive::SetArchive3dmVersion(int v)
13200 {
13201   bool rc = false;
13202   // valid versions are 1,2,3,4,5,50,60,70,...
13203   // 1 - 4 correspond to Rhino V1 - V4.
13204   // 5 was used for early V5 betas with 4 byte chunk lengths
13205   // 50 is used for V5 files with 8 bytes chunk lengths.
13206   // 60, 70, ... will be used for Rhino V6, V7, etc.
13207   if ( (v >= 1 && v <= 5) || ( v >= 50 && 0 == (v % 10) ) )
13208   {
13209     m_3dm_version = v;
13210     rc = true;
13211   }
13212   else 
13213   {
13214     m_3dm_version = 0;
13215     ON_ERROR("ON_BinaryArchive::SetArchive3dmVersion - invalid version");
13216   }
13217   return rc;
13218 }
13219 
13220 void 
13221 ON_BinaryFile::EnableMemoryBuffer(
13222        int buffer_capacity
13223        )
13224 {
13225   if ( buffer_capacity > 0 && !m_memory_buffer) {
13226     m_memory_buffer = (unsigned char*)onmalloc(buffer_capacity);
13227     if ( m_memory_buffer ) {
13228       m_memory_buffer_capacity = buffer_capacity;
13229       m_memory_buffer_size = 0;
13230       m_memory_buffer_ptr = 0;
13231     }
13232   }
13233   else {
13234     if ( buffer_capacity == 0 && m_memory_buffer ) {
13235       Flush();
13236       onfree(m_memory_buffer);
13237     }
13238     m_memory_buffer = 0;
13239     m_memory_buffer_capacity = 0;
13240     m_memory_buffer_size = 0;
13241     m_memory_buffer_ptr = 0;
13242   }
13243 }
13244 
13245 
13246 size_t ON_BinaryFile::Read( size_t count, void* p )
13247 {
13248   return (m_fp) ? fread( p, 1, count, m_fp ) : 0;
13249 }
13250 
13251 size_t ON_BinaryFile::Write( size_t count, const void* p )
13252 {
13253   size_t rc = 0;
13254   if ( m_fp ) 
13255   {
13256     if ( m_memory_buffer ) 
13257     {
13258       if ( count+m_memory_buffer_ptr >= m_memory_buffer_capacity ) {
13259         if ( !Flush() ) // flush existing memory buffer to disk
13260           return 0;
13261         rc = fwrite( p, 1, count, m_fp ); // write directly to disk
13262       }
13263       else 
13264       {
13265         // copy to the memory buffer
13266         memcpy( m_memory_buffer+m_memory_buffer_ptr, p, count );
13267         m_memory_buffer_ptr += count;
13268         if ( m_memory_buffer_ptr > m_memory_buffer_size )
13269           m_memory_buffer_size = m_memory_buffer_ptr;
13270         rc = count;
13271       }      
13272     }
13273     else
13274     {
13275       rc = fwrite( p, 1, count, m_fp );
13276     }
13277   }
13278   return rc;
13279 }
13280 
13281 bool ON_BinaryFile::Flush()
13282 {
13283   bool rc = true;
13284   if ( m_fp ) 
13285   {
13286     if ( m_memory_buffer && m_memory_buffer_size > 0 ) 
13287     {
13288       rc = ( m_memory_buffer_size == fwrite( m_memory_buffer, 1, m_memory_buffer_size, m_fp ));   
13289       if ( rc && m_memory_buffer_ptr != m_memory_buffer_size ) 
13290       {
13291         //if ( !fseek( m_fp, m_memory_buffer_size-m_memory_buffer_ptr, SEEK_CUR ) )
13292         int delta =  (m_memory_buffer_ptr >= m_memory_buffer_size) 
13293                   ?  ((int)(m_memory_buffer_ptr - m_memory_buffer_size))
13294                   : -((int)(m_memory_buffer_size - m_memory_buffer_ptr));
13295         if ( !fseek( m_fp, delta, SEEK_CUR ) )          
13296         {
13297           rc = false;
13298         }
13299       }
13300       m_memory_buffer_size = 0;
13301       m_memory_buffer_ptr = 0;
13302     }
13303   }
13304   return rc;
13305 }
13306 
13307 size_t ON_BinaryFile::CurrentPosition() const
13308 {
13309   size_t offset = 0;
13310 
13311   if ( 0 != m_fp ) 
13312   {
13313 
13314 #if defined(ON_COMPILER_MSC)
13315     // use an ftell() that supports file offsets > 2GB
13316     ON__INT64 offset64 = _ftelli64(m_fp);
13317     if ( offset64 < 0 )
13318     {
13319       ON_ERROR("ON_BinaryFile::CurrentPosition() - _ftelli64() failed");
13320     }
13321     else
13322     {
13323       offset = (size_t)((ON__UINT64)offset64);
13324     }
13325 #else
13326     offset = ftell(m_fp);
13327 #endif
13328 
13329     if ( m_memory_buffer && m_memory_buffer_size > 0 ) 
13330     {
13331       offset += m_memory_buffer_ptr;
13332     }
13333   }
13334   else 
13335   {
13336     ON_ERROR("ON_BinaryFile::CurrentPosition() NULL file.");
13337   }
13338 
13339   return offset;
13340 }
13341 
13342 bool ON_BinaryFile::AtEnd() const
13343 {
13344   bool rc = true;
13345   if ( m_fp ) {
13346     rc = false;
13347     if ( ReadMode() ) {
13348       if ( feof( m_fp ) ) {
13349         rc = true;
13350       }
13351       else 
13352       {
13353         int buffer;
13354         fread( &buffer, 1, 1, m_fp );
13355         if ( feof( m_fp ) ) 
13356         {
13357           rc = true;
13358         }
13359         else 
13360         {
13361           // back up to the byte we just read
13362           fseek( m_fp, -1, SEEK_CUR );
13363         }
13364       }
13365     }
13366   }
13367   return rc;
13368 }
13369 
13370 bool ON_BinaryFile::SeekFromCurrentPosition( int offset )
13371 {
13372   // it appears that the calls to SeekFromCurrentPosition(),
13373   // and consequent call to fseek(), in ON_BinaryArchive::EndWrite3DMChunk()
13374   // really slow down 3DM file writing on some networks.  There are
13375   // reports of a 100x difference in local vs network saves.
13376   // I could not reproduce these 100x slow saves in testing on McNeel's
13377   // network but we have a good quality network and a server that's
13378   // properly tuned.  My guess is that the slow saves are happening
13379   // on servers that do a poor job of cacheing because they are starved
13380   // for memory and/or heavily used at the time of the save.
13381   //
13382   // To speed up network saves, ON_BinaryFile can optionally use
13383   // it's own buffer for buffered I/O instead of relying on fwrite()
13384   // and the OS to handle this.
13385   bool rc = false;
13386   if ( m_fp ) 
13387   {
13388     if ( m_memory_buffer && 
13389          ((ON__INT_PTR)m_memory_buffer_ptr)+((ON__INT_PTR)offset) >= 0 &&
13390          m_memory_buffer_ptr+offset <= m_memory_buffer_size ) {
13391       m_memory_buffer_ptr += offset;
13392       rc = true;
13393     }
13394     else
13395     {
13396       // don't deal with memory buffer I/O if seek lands outside of
13397       // our current memory buffer.
13398       Flush();
13399       if ( !fseek(m_fp,offset,SEEK_CUR) )
13400       {
13401         rc = true;
13402       }
13403       else 
13404       {
13405         ON_ERROR("ON_BinaryFile::Seek() fseek(,SEEK_CUR) failed.");
13406       }
13407     }
13408   }
13409   return rc;
13410 }
13411 
13412 bool ON_BinaryFile::SeekFromEnd( int offset )
13413 {
13414   bool rc = false;
13415   if ( m_fp ) 
13416   {
13417     Flush(); // don't deal with memory buffer I/O in rare seek from end
13418     if ( !fseek(m_fp,offset,SEEK_END) )
13419     {
13420       rc = true;
13421     }
13422     else 
13423     {
13424       ON_ERROR("ON_BinaryFile::SeekFromEnd() fseek(,SEEK_END) failed.");
13425     }
13426   }
13427   return rc;
13428 }
13429 
13430 bool ON_BinaryFile::SeekFromStart( size_t offset )
13431 {
13432   bool rc = false;
13433   if ( m_fp ) 
13434   {
13435     Flush(); // don't deal with memory buffer I/O in rare seek from start
13436     long loffset = (long)offset;
13437     if ( !fseek(m_fp,loffset,SEEK_SET) )
13438     {
13439       rc = true;
13440     }
13441     else 
13442     {
13443       ON_ERROR("ON_BinaryFile::SeekFromStart() fseek(,SEEK_SET) failed.");
13444     }
13445   }
13446   return rc;
13447 }
13448 
13449 ON_3dmGoo::ON_3dmGoo()
13450         : m_typecode(0),
13451           m_value(0),
13452           m_goo(0),
13453           m_next_goo(0),
13454           m_prev_goo(0)
13455 {}
13456 
13457 ON_3dmGoo::~ON_3dmGoo()
13458 {
13459   if ( m_prev_goo )
13460     m_prev_goo->m_next_goo = m_next_goo;
13461   if ( m_next_goo )
13462     m_next_goo->m_prev_goo = m_prev_goo;
13463   if ( m_goo ) {
13464     onfree(m_goo);
13465     m_goo = 0;
13466   }
13467 }
13468 
13469 ON_3dmGoo::ON_3dmGoo( const ON_3dmGoo& src )
13470         : m_typecode(0),
13471           m_value(0),
13472           m_goo(0),
13473           m_next_goo(0),
13474           m_prev_goo(0)
13475 {
13476   *this = src;
13477 }
13478 
13479 ON_3dmGoo& ON_3dmGoo::operator=( const ON_3dmGoo& src )
13480 {
13481   if ( this != &src ) {
13482     if ( m_goo ) {
13483       onfree(m_goo);
13484     }
13485     m_typecode = src.m_typecode;
13486     m_value = src.m_value;
13487     m_goo = (m_value > 0 && src.m_goo) ? (unsigned char*)onmemdup( src.m_goo, m_value ) : 0;
13488   }
13489   return *this;
13490 }
13491 
13492 void ON_3dmGoo::Dump(ON_TextLog& dump) const
13493 {
13494   dump.Print("typecode = %08x value = %d\n",m_typecode,m_value);
13495 }
13496 
13497 bool ON_WriteOneObjectArchive( 
13498           ON_BinaryArchive& archive,
13499           int version,
13500           const ON_Object& object
13501           )
13502 {
13503   bool rc = false;
13504 
13505   const ON_Object* pObject = &object;
13506   {
13507     if ( ON_BrepEdge::Cast(pObject) )
13508     {
13509       // write parent brep
13510       pObject = static_cast<const ON_BrepEdge*>(pObject)->Brep();
13511     }
13512     else if ( ON_BrepTrim::Cast(pObject) )
13513     {
13514       pObject = NULL;
13515     }
13516     else if ( ON_BrepLoop::Cast(pObject) )
13517     {
13518       pObject = static_cast<const ON_BrepLoop*>(pObject)->Brep();
13519     }
13520     else if ( ON_BrepFace::Cast(pObject) )
13521     {
13522       // write parent brep
13523       pObject = static_cast<const ON_BrepFace*>(pObject)->Brep();
13524     }
13525     else if ( ON_CurveProxy::Cast(pObject) )
13526     {
13527       // write actual curve
13528       pObject = static_cast<const ON_CurveProxy*>(pObject)->ProxyCurve();
13529     }
13530     else if ( ON_SurfaceProxy::Cast(pObject) )
13531     {
13532       // write actual surface
13533       pObject = static_cast<const ON_SurfaceProxy*>(pObject)->ProxySurface();
13534     }
13535   }
13536   
13537   ON_3dmProperties props;
13538   props.m_RevisionHistory.NewRevision();
13539   
13540   ON_3dmSettings settings;
13541   // 1 Feb 2012 Dale Lear 
13542   //   http://dev.mcneel.com/bugtrack/?q=98543
13543   //   Single object archives have no unit system so they
13544   //   can be read into a file with no scaling.  Prior to
13545   //   today the unit system was always millimeters.
13546   settings.m_ModelUnitsAndTolerances.m_unit_system.m_unit_system = ON::no_unit_system;
13547 
13548   ON_Layer layer;
13549   ON_3dmObjectAttributes attributes;
13550 
13551   // layer table will have one layer
13552   layer.SetLayerIndex(0);
13553   layer.SetLayerName(L"Default");
13554   
13555   // object attributes
13556   attributes.m_layer_index = 0;
13557 
13558   while(pObject)
13559   {
13560     rc = archive.Write3dmStartSection( version, "Archive created by ON_WriteOneObjectArchive "__DATE__" "__TIME__ );
13561     if ( !rc )
13562       break;
13563 
13564     version = archive.Archive3dmVersion();
13565 
13566     rc = archive.Write3dmProperties( props );
13567     if ( !rc )
13568       break;
13569 
13570     rc = archive.Write3dmSettings( settings );
13571     if ( !rc )
13572       break;
13573 
13574     rc = archive.BeginWrite3dmBitmapTable();
13575     if ( !rc )
13576       break;
13577     rc = archive.EndWrite3dmBitmapTable();
13578     if ( !rc )
13579       break;
13580 
13581     if ( version >= 4 )
13582     {
13583       rc = archive.BeginWrite3dmTextureMappingTable();
13584       if ( !rc )
13585         break;
13586       rc = archive.EndWrite3dmTextureMappingTable();
13587       if ( !rc )
13588         break;
13589     }
13590 
13591     rc = archive.BeginWrite3dmMaterialTable();
13592     if ( !rc )
13593       break;
13594     rc = archive.EndWrite3dmMaterialTable();
13595     if ( !rc )
13596       break;
13597 
13598     if ( version >= 4 )
13599     {
13600       rc = archive.BeginWrite3dmLinetypeTable();
13601       if ( !rc )
13602         break;
13603       rc = archive.EndWrite3dmLinetypeTable();
13604       if ( !rc )
13605         break;
13606     }
13607 
13608     rc = archive.BeginWrite3dmLayerTable();
13609     if ( !rc )
13610       break;
13611     {
13612       rc = archive.Write3dmLayer(layer);
13613     }
13614     if (!archive.EndWrite3dmLayerTable())
13615       rc = false;
13616     if ( !rc )
13617       break;
13618 
13619     rc = archive.BeginWrite3dmGroupTable();
13620     if ( !rc )
13621       break;
13622     rc = archive.EndWrite3dmGroupTable();
13623     if ( !rc )
13624       break;
13625 
13626     if ( version >= 3 )
13627     {
13628       rc = archive.BeginWrite3dmFontTable();
13629       if ( !rc )
13630         break;
13631       rc = archive.EndWrite3dmFontTable();
13632       if ( !rc )
13633         break;
13634     }
13635 
13636     if ( version >= 3 )
13637     {
13638       rc = archive.BeginWrite3dmDimStyleTable();
13639       if ( !rc )
13640         break;
13641       rc = archive.EndWrite3dmDimStyleTable();
13642       if ( !rc )
13643         break;
13644     }
13645 
13646     rc = archive.BeginWrite3dmLightTable();
13647     if ( !rc )
13648       break;
13649     rc = archive.EndWrite3dmLightTable();
13650     if ( !rc )
13651       break;
13652 
13653     if ( version >= 4 )
13654     {
13655       rc = archive.BeginWrite3dmHatchPatternTable();
13656       if ( !rc )
13657         break;
13658       rc = archive.EndWrite3dmHatchPatternTable();
13659       if ( !rc )
13660         break;
13661     }
13662 
13663     if ( version >= 3 )
13664     {
13665       rc = archive.BeginWrite3dmInstanceDefinitionTable();
13666       if ( !rc )
13667         break;
13668       rc = archive.EndWrite3dmInstanceDefinitionTable();
13669       if ( !rc )
13670         break;
13671     }
13672 
13673     rc = archive.BeginWrite3dmObjectTable();
13674     if ( !rc )
13675       break;
13676     {
13677       rc = archive.Write3dmObject( *pObject, &attributes );
13678     }
13679     if ( !archive.EndWrite3dmObjectTable() )
13680       rc = false;
13681     if ( !rc )
13682       break;
13683 
13684     if ( version >= 4 )
13685     {
13686       rc = archive.BeginWrite3dmHistoryRecordTable();
13687       if ( !rc )
13688         break;
13689       rc = archive.EndWrite3dmHistoryRecordTable();
13690       if ( !rc )
13691         break;
13692     }
13693 
13694     rc = archive.Write3dmEndMark();
13695 
13696     break;
13697   }
13698 
13699   return rc;
13700 }
13701 
13702 static 
13703 void Dump3dmChunk_ErrorReportHelper( size_t offset, const char* msg, ON_TextLog& dump )
13704 {
13705   int ioffset = (int)offset;
13706   dump.Print("** ERROR near offset %d ** %s\n",ioffset,msg);
13707 }
13708 static
13709 bool DumpChunk_PrintHeaderInfo( size_t offset0, ON__UINT32 typecode, ON__INT64 big_value, const char* typecode_name, ON_TextLog& dump)
13710 {
13711   bool bShortChunk = (0 != (typecode & TCODE_SHORT));
13712   if ( 0 == typecode_name )
13713     typecode_name = ON_BinaryArchive::TypecodeName(typecode);
13714   if ( 0 == typecode_name )
13715     typecode_name = "unknown tcode";
13716   if ( bShortChunk )
13717   {
13718     dump.Print("%6d: %08X %s: value = %lld (%016llX)\n", offset0, typecode, typecode_name, big_value, big_value );
13719   }
13720   else 
13721   {
13722     // long chunk value = length of chunk data
13723     if ( big_value < 0 ) 
13724     {
13725       Dump3dmChunk_ErrorReportHelper(offset0,"BeginRead3dmChunk() returned length < 0.",dump);
13726       return false;
13727     }
13728     dump.Print("%6d: %08X %s: length = %lld bytes\n", offset0, typecode, typecode_name, big_value );
13729   }
13730   return true;
13731 }
13732 
13733 #define CASEtcode2string(tc) case tc: s = #tc ; break
13734 
13735 const char* ON_BinaryArchive::TypecodeName( unsigned int tcode )
13736 {
13737 
13738   const char* s;
13739   switch( tcode ) 
13740   {
13741   CASEtcode2string(TCODE_FONT_TABLE);
13742   CASEtcode2string(TCODE_FONT_RECORD);
13743   CASEtcode2string(TCODE_DIMSTYLE_TABLE);
13744   CASEtcode2string(TCODE_DIMSTYLE_RECORD);
13745   CASEtcode2string(TCODE_INSTANCE_DEFINITION_RECORD);
13746   CASEtcode2string(TCODE_COMMENTBLOCK);
13747   CASEtcode2string(TCODE_ENDOFFILE);
13748   CASEtcode2string(TCODE_ENDOFFILE_GOO);
13749   CASEtcode2string(TCODE_LEGACY_GEOMETRY);
13750   CASEtcode2string(TCODE_OPENNURBS_OBJECT);
13751   CASEtcode2string(TCODE_GEOMETRY);
13752   CASEtcode2string(TCODE_ANNOTATION);
13753   CASEtcode2string(TCODE_DISPLAY);
13754   CASEtcode2string(TCODE_RENDER);
13755   CASEtcode2string(TCODE_INTERFACE);
13756   CASEtcode2string(TCODE_TOLERANCE);
13757   CASEtcode2string(TCODE_TABLE);
13758   CASEtcode2string(TCODE_TABLEREC);
13759   CASEtcode2string(TCODE_USER);
13760   CASEtcode2string(TCODE_SHORT);
13761   CASEtcode2string(TCODE_CRC);
13762   CASEtcode2string(TCODE_ANONYMOUS_CHUNK);
13763   CASEtcode2string(TCODE_MATERIAL_TABLE);
13764   CASEtcode2string(TCODE_LAYER_TABLE);
13765   CASEtcode2string(TCODE_LIGHT_TABLE);
13766   CASEtcode2string(TCODE_OBJECT_TABLE);
13767   CASEtcode2string(TCODE_PROPERTIES_TABLE);
13768   CASEtcode2string(TCODE_SETTINGS_TABLE);
13769   CASEtcode2string(TCODE_BITMAP_TABLE);
13770   CASEtcode2string(TCODE_USER_TABLE);
13771   CASEtcode2string(TCODE_INSTANCE_DEFINITION_TABLE);
13772   CASEtcode2string(TCODE_HATCHPATTERN_TABLE);
13773   CASEtcode2string(TCODE_HATCHPATTERN_RECORD);
13774   CASEtcode2string(TCODE_LINETYPE_TABLE);
13775   CASEtcode2string(TCODE_LINETYPE_RECORD);
13776   CASEtcode2string(TCODE_OBSOLETE_LAYERSET_TABLE);
13777   CASEtcode2string(TCODE_OBSOLETE_LAYERSET_RECORD);
13778   CASEtcode2string(TCODE_TEXTURE_MAPPING_TABLE);
13779   CASEtcode2string(TCODE_TEXTURE_MAPPING_RECORD);
13780   CASEtcode2string(TCODE_HISTORYRECORD_TABLE);  
13781   CASEtcode2string(TCODE_HISTORYRECORD_RECORD);
13782   CASEtcode2string(TCODE_ENDOFTABLE);
13783   CASEtcode2string(TCODE_PROPERTIES_REVISIONHISTORY);
13784   CASEtcode2string(TCODE_PROPERTIES_NOTES);
13785   CASEtcode2string(TCODE_PROPERTIES_PREVIEWIMAGE);
13786   CASEtcode2string(TCODE_PROPERTIES_COMPRESSED_PREVIEWIMAGE);
13787   CASEtcode2string(TCODE_PROPERTIES_APPLICATION);
13788   CASEtcode2string(TCODE_PROPERTIES_OPENNURBS_VERSION);
13789   CASEtcode2string(TCODE_SETTINGS_PLUGINLIST);
13790   CASEtcode2string(TCODE_SETTINGS_UNITSANDTOLS);
13791   CASEtcode2string(TCODE_SETTINGS_RENDERMESH);
13792   CASEtcode2string(TCODE_SETTINGS_ANALYSISMESH);
13793   CASEtcode2string(TCODE_SETTINGS_ANNOTATION);
13794   CASEtcode2string(TCODE_SETTINGS_NAMED_CPLANE_LIST);
13795   CASEtcode2string(TCODE_SETTINGS_NAMED_VIEW_LIST);
13796   CASEtcode2string(TCODE_SETTINGS_VIEW_LIST);
13797   CASEtcode2string(TCODE_SETTINGS_CURRENT_LAYER_INDEX);
13798   CASEtcode2string(TCODE_SETTINGS_CURRENT_MATERIAL_INDEX);
13799   CASEtcode2string(TCODE_SETTINGS_CURRENT_COLOR);
13800   CASEtcode2string(TCODE_SETTINGS__NEVER__USE__THIS);
13801   CASEtcode2string(TCODE_SETTINGS_CURRENT_WIRE_DENSITY);
13802   CASEtcode2string(TCODE_SETTINGS_RENDER);
13803   CASEtcode2string(TCODE_SETTINGS_GRID_DEFAULTS);
13804   CASEtcode2string(TCODE_SETTINGS_MODEL_URL);
13805   CASEtcode2string(TCODE_SETTINGS_CURRENT_FONT_INDEX);
13806   CASEtcode2string(TCODE_SETTINGS_CURRENT_DIMSTYLE_INDEX);
13807   CASEtcode2string(TCODE_SETTINGS_ATTRIBUTES);
13808   CASEtcode2string(TCODE_VIEW_RECORD);
13809   CASEtcode2string(TCODE_VIEW_CPLANE);
13810   CASEtcode2string(TCODE_VIEW_VIEWPORT);
13811   CASEtcode2string(TCODE_VIEW_VIEWPORT_USERDATA);
13812   CASEtcode2string(TCODE_VIEW_SHOWCONGRID);
13813   CASEtcode2string(TCODE_VIEW_SHOWCONAXES);
13814   CASEtcode2string(TCODE_VIEW_SHOWWORLDAXES);
13815   CASEtcode2string(TCODE_VIEW_TRACEIMAGE);
13816   CASEtcode2string(TCODE_VIEW_WALLPAPER);
13817   CASEtcode2string(TCODE_VIEW_WALLPAPER_V3);
13818   CASEtcode2string(TCODE_VIEW_TARGET);
13819   CASEtcode2string(TCODE_VIEW_DISPLAYMODE);
13820   CASEtcode2string(TCODE_VIEW_NAME);
13821   CASEtcode2string(TCODE_VIEW_POSITION);
13822   CASEtcode2string(TCODE_VIEW_ATTRIBUTES);
13823   CASEtcode2string(TCODE_BITMAP_RECORD);
13824   CASEtcode2string(TCODE_MATERIAL_RECORD);
13825   CASEtcode2string(TCODE_LAYER_RECORD);
13826   CASEtcode2string(TCODE_LIGHT_RECORD);
13827   CASEtcode2string(TCODE_LIGHT_RECORD_ATTRIBUTES);
13828   CASEtcode2string(TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA);
13829   CASEtcode2string(TCODE_OBJECT_RECORD_HISTORY);
13830   CASEtcode2string(TCODE_OBJECT_RECORD_HISTORY_HEADER);
13831   CASEtcode2string(TCODE_OBJECT_RECORD_HISTORY_DATA);
13832   CASEtcode2string(TCODE_LIGHT_RECORD_END);
13833   CASEtcode2string(TCODE_USER_TABLE_UUID);
13834   CASEtcode2string(TCODE_USER_TABLE_RECORD_HEADER);
13835   CASEtcode2string(TCODE_USER_RECORD);
13836   CASEtcode2string(TCODE_GROUP_TABLE);
13837   CASEtcode2string(TCODE_GROUP_RECORD);
13838   CASEtcode2string(TCODE_OBJECT_RECORD);
13839   CASEtcode2string(TCODE_OBJECT_RECORD_TYPE);
13840   CASEtcode2string(TCODE_OBJECT_RECORD_ATTRIBUTES);
13841   CASEtcode2string(TCODE_OBJECT_RECORD_END);
13842   CASEtcode2string(TCODE_OPENNURBS_CLASS);
13843   CASEtcode2string(TCODE_OPENNURBS_CLASS_UUID);
13844   CASEtcode2string(TCODE_OPENNURBS_CLASS_DATA);
13845   CASEtcode2string(TCODE_OPENNURBS_CLASS_USERDATA);
13846   CASEtcode2string(TCODE_OPENNURBS_CLASS_USERDATA_HEADER);
13847   CASEtcode2string(TCODE_OPENNURBS_CLASS_END);
13848   CASEtcode2string(TCODE_OPENNURBS_BUFFER);
13849   CASEtcode2string(TCODE_ANNOTATION_SETTINGS);
13850   CASEtcode2string(TCODE_TEXT_BLOCK);
13851   CASEtcode2string(TCODE_ANNOTATION_LEADER);
13852   CASEtcode2string(TCODE_LINEAR_DIMENSION);
13853   CASEtcode2string(TCODE_ANGULAR_DIMENSION);
13854   CASEtcode2string(TCODE_RADIAL_DIMENSION);
13855   CASEtcode2string(TCODE_RHINOIO_OBJECT_NURBS_CURVE);
13856   CASEtcode2string(TCODE_RHINOIO_OBJECT_NURBS_SURFACE);
13857   CASEtcode2string(TCODE_RHINOIO_OBJECT_BREP);
13858   CASEtcode2string(TCODE_RHINOIO_OBJECT_DATA);
13859   CASEtcode2string(TCODE_RHINOIO_OBJECT_END);
13860   CASEtcode2string(TCODE_LEGACY_ASM);
13861   CASEtcode2string(TCODE_LEGACY_PRT);
13862   CASEtcode2string(TCODE_LEGACY_SHL);
13863   CASEtcode2string(TCODE_LEGACY_FAC);
13864   CASEtcode2string(TCODE_LEGACY_BND);
13865   CASEtcode2string(TCODE_LEGACY_TRM);
13866   CASEtcode2string(TCODE_LEGACY_SRF);
13867   CASEtcode2string(TCODE_LEGACY_CRV);
13868   CASEtcode2string(TCODE_LEGACY_SPL);
13869   CASEtcode2string(TCODE_LEGACY_PNT);
13870   CASEtcode2string(TCODE_STUFF);
13871   CASEtcode2string(TCODE_LEGACY_ASMSTUFF);
13872   CASEtcode2string(TCODE_LEGACY_PRTSTUFF);
13873   CASEtcode2string(TCODE_LEGACY_SHLSTUFF);
13874   CASEtcode2string(TCODE_LEGACY_FACSTUFF);
13875   CASEtcode2string(TCODE_LEGACY_BNDSTUFF);
13876   CASEtcode2string(TCODE_LEGACY_TRMSTUFF);
13877   CASEtcode2string(TCODE_LEGACY_SRFSTUFF);
13878   CASEtcode2string(TCODE_LEGACY_CRVSTUFF);
13879   CASEtcode2string(TCODE_LEGACY_SPLSTUFF);
13880   CASEtcode2string(TCODE_LEGACY_PNTSTUFF);
13881   CASEtcode2string(TCODE_RH_POINT);
13882   CASEtcode2string(TCODE_RH_SPOTLIGHT);
13883   CASEtcode2string(TCODE_OLD_RH_TRIMESH);
13884   CASEtcode2string(TCODE_OLD_MESH_VERTEX_NORMALS);
13885   CASEtcode2string(TCODE_OLD_MESH_UV);
13886   CASEtcode2string(TCODE_OLD_FULLMESH);
13887   CASEtcode2string(TCODE_MESH_OBJECT);
13888   CASEtcode2string(TCODE_COMPRESSED_MESH_GEOMETRY);
13889   CASEtcode2string(TCODE_ANALYSIS_MESH);
13890   CASEtcode2string(TCODE_NAME);
13891   CASEtcode2string(TCODE_VIEW);
13892   CASEtcode2string(TCODE_CPLANE);
13893   CASEtcode2string(TCODE_NAMED_CPLANE);
13894   CASEtcode2string(TCODE_NAMED_VIEW);
13895   CASEtcode2string(TCODE_VIEWPORT);
13896   CASEtcode2string(TCODE_SHOWGRID);
13897   CASEtcode2string(TCODE_SHOWGRIDAXES);
13898   CASEtcode2string(TCODE_SHOWWORLDAXES);
13899   CASEtcode2string(TCODE_VIEWPORT_POSITION);
13900   CASEtcode2string(TCODE_VIEWPORT_TRACEINFO);
13901   CASEtcode2string(TCODE_SNAPSIZE);
13902   CASEtcode2string(TCODE_NEAR_CLIP_PLANE);
13903   CASEtcode2string(TCODE_HIDE_TRACE);
13904   CASEtcode2string(TCODE_NOTES);
13905   CASEtcode2string(TCODE_UNIT_AND_TOLERANCES);
13906   CASEtcode2string(TCODE_MAXIMIZED_VIEWPORT);
13907   CASEtcode2string(TCODE_VIEWPORT_WALLPAPER);
13908   CASEtcode2string(TCODE_SUMMARY);
13909   CASEtcode2string(TCODE_BITMAPPREVIEW);
13910   CASEtcode2string(TCODE_VIEWPORT_DISPLAY_MODE);
13911   CASEtcode2string(TCODE_LAYERTABLE);
13912   CASEtcode2string(TCODE_LAYERREF);
13913   CASEtcode2string(TCODE_XDATA);
13914   CASEtcode2string(TCODE_RGB);
13915   CASEtcode2string(TCODE_TEXTUREMAP);
13916   CASEtcode2string(TCODE_BUMPMAP);
13917   CASEtcode2string(TCODE_TRANSPARENCY);
13918   CASEtcode2string(TCODE_DISP_AM_RESOLUTION);
13919   CASEtcode2string(TCODE_RGBDISPLAY);
13920   CASEtcode2string(TCODE_RENDER_MATERIAL_ID);
13921   CASEtcode2string(TCODE_LAYER);
13922   CASEtcode2string(TCODE_LAYER_OBSELETE_1);
13923   CASEtcode2string(TCODE_LAYER_OBSELETE_2);
13924   CASEtcode2string(TCODE_LAYER_OBSELETE_3);
13925   CASEtcode2string(TCODE_LAYERON);
13926   CASEtcode2string(TCODE_LAYERTHAWED);
13927   CASEtcode2string(TCODE_LAYERLOCKED);
13928   CASEtcode2string(TCODE_LAYERVISIBLE);
13929   CASEtcode2string(TCODE_LAYERPICKABLE);
13930   CASEtcode2string(TCODE_LAYERSNAPABLE);
13931   CASEtcode2string(TCODE_LAYERRENDERABLE);
13932   CASEtcode2string(TCODE_LAYERSTATE);
13933   CASEtcode2string(TCODE_LAYERINDEX);
13934   CASEtcode2string(TCODE_LAYERMATERIALINDEX);
13935   CASEtcode2string(TCODE_RENDERMESHPARAMS);
13936   CASEtcode2string(TCODE_DISP_CPLINES);
13937   CASEtcode2string(TCODE_DISP_MAXLENGTH);
13938   CASEtcode2string(TCODE_CURRENTLAYER);
13939   CASEtcode2string(TCODE_LAYERNAME);
13940   CASEtcode2string(TCODE_LEGACY_TOL_FIT);
13941   CASEtcode2string(TCODE_LEGACY_TOL_ANGLE);
13942   CASEtcode2string(TCODE_DICTIONARY);
13943   CASEtcode2string(TCODE_DICTIONARY_ID);
13944   CASEtcode2string(TCODE_DICTIONARY_ENTRY);
13945   CASEtcode2string(TCODE_DICTIONARY_END);
13946   default:
13947     // unknown typecode.
13948     s = 0; 
13949     break;
13950   }
13951   return s;
13952 }
13953 
13954 #undef CASEtcode2string
13955 
13956 char* ON_BinaryArchive::ON_TypecodeParse( unsigned int tcode, char* typecode_name, size_t max_length )
13957 {
13958   char* s;
13959   const char* sub_name;
13960   const char* h = "0123456789ABCDEF";
13961   char c, c0;
13962   size_t slen;
13963 
13964   if ( !typecode_name || max_length <= 0 )
13965     return 0;
13966   memset(typecode_name,0,max_length*sizeof(typecode_name[0]));
13967   slen = max_length-1; // the -1 insures the there is a null terminator
13968   if ( slen <= 0 )
13969     return 0;
13970 
13971   sub_name = ON_BinaryArchive::TypecodeName(tcode);
13972   if ( 0 != sub_name && 0 != sub_name[0] )
13973   {
13974     c0 = *sub_name++;
13975     s = typecode_name+1;
13976     slen--;
13977     while ( *sub_name )
13978     {
13979       if ( slen <= 0 )
13980         return 0;
13981       *s++ = *sub_name++;
13982       slen--;
13983     }
13984     typecode_name[0] = c0;
13985     return typecode_name;    
13986   }
13987 
13988   sub_name = ON_BinaryArchive::TypecodeName( tcode & 0x7FFF0000 );
13989   if ( !sub_name || 0 == sub_name[0] )
13990     return 0;
13991 
13992   c0 = *sub_name++;
13993   s = typecode_name+1;
13994   slen--;
13995 
13996   while ( *sub_name )
13997   {
13998     if ( slen <= 0 )
13999       return 0;
14000     *s++ = *sub_name++;
14001     slen--;
14002   }
14003 
14004   sub_name = ON_BinaryArchive::TypecodeName( tcode & TCODE_SHORT );
14005   if ( sub_name )
14006   {
14007     if ( slen <= 0 ) return 0; *s++ = ' '; slen--;
14008     if ( slen <= 0 ) return 0; *s++ = '|'; slen--;
14009     if ( slen <= 0 ) return 0; *s++ = ' '; slen--;
14010     while ( *sub_name )
14011     {
14012       if ( slen <= 0 )
14013         return 0;
14014       *s++ = *sub_name++;
14015       slen--;
14016     }
14017   }
14018 
14019   sub_name = ON_BinaryArchive::TypecodeName( tcode & TCODE_CRC );
14020   if ( sub_name )
14021   {
14022     if ( slen <= 0 ) return 0; *s++ = ' '; slen--;
14023     if ( slen <= 0 ) return 0; *s++ = '|'; slen--;
14024     if ( slen <= 0 ) return 0; *s++ = ' '; slen--;
14025     while ( *sub_name )
14026     {
14027       if ( slen <= 0 )
14028         return 0;
14029       *s++ = *sub_name++;
14030       slen--;
14031     }
14032   }
14033 
14034   sub_name = ON_BinaryArchive::TypecodeName( tcode & 0x7FFF );
14035   if ( sub_name )
14036   {
14037     if ( slen <= 0 ) return 0; *s++ = ' '; slen--;
14038     if ( slen <= 0 ) return 0; *s++ = '|'; slen--;
14039     if ( slen <= 0 ) return 0; *s++ = ' '; slen--;
14040     while ( *sub_name )
14041     {
14042       if ( slen <= 0 )
14043         return 0;
14044       *s++ = *sub_name++;
14045       slen--;
14046     }
14047   }
14048   else 
14049   {
14050     if ( slen <= 0 ) return 0; *s++ = ' '; slen--;
14051     if ( slen <= 0 ) return 0; *s++ = '|'; slen--;
14052     if ( slen <= 0 ) return 0; *s++ = ' '; slen--;
14053     if ( slen <= 0 ) return 0; *s++ = '0'; slen--;
14054     if ( slen <= 0 ) return 0; *s++ = 'x'; slen--;
14055     c = h[((tcode & 0x7000) / 0x1000) & 0xF];
14056     if ( slen > 0 ) {*s++ = c; slen--;}
14057     c = h[((tcode & 0xF00) / 0x100) & 0xF];
14058     if ( slen > 0 ) {*s++ = c; slen--;}
14059     c = h[((tcode & 0xF0) / 0x10) & 0xF];
14060     if ( slen > 0 ) {*s++ = c; slen--;}
14061     c = h[tcode & 0xF];
14062     if ( slen > 0 ) {*s++ = c; slen--;}
14063   }
14064 
14065   *typecode_name = c0;
14066 
14067   return typecode_name;
14068 }
14069 
14070 static
14071 bool Dump3dmChunk_EndReadChunkHelper( ON_BinaryArchive& file, size_t offset0, ON__UINT32 tcode, ON__INT64 big_value, ON_TextLog& dump )
14072 {
14073   const bool bShortChunk = (0 != (tcode & TCODE_SHORT));
14074   const size_t offset1 = file.CurrentPosition();
14075   bool rc = file.EndRead3dmChunk();
14076   if ( !rc ) 
14077   {
14078     Dump3dmChunk_ErrorReportHelper(offset1,"EndRead3dmChunk() failed.",dump);
14079   }
14080   else if (!bShortChunk) 
14081   {
14082     // The crc is read or skipped by the EndRead3dmChunk() call.
14083     // "extra" is the number of bytes we did not parse in the dump.
14084     ON__INT64 sizeof_crc = (0 != (TCODE_CRC & tcode)) ? 4 : 0;
14085     ON__INT64 sizeof_chunk_header = 4+file.SizeofChunkLength();
14086     ON__INT64 delta =  (offset1 > offset0)
14087               ?  ((ON__INT64)(offset1 - offset0))
14088               : -((ON__INT64)(offset0 - offset1));
14089     const ON__INT64 extra = big_value - (delta+sizeof_crc-sizeof_chunk_header);
14090     if ( extra < 0 ) 
14091     {
14092       Dump3dmChunk_ErrorReportHelper(offset0,"Read beyond end of chunk.",dump);
14093       rc = false;
14094     }
14095   }
14096   return rc;
14097 }
14098 
14099 static 
14100 bool Dump3dmChunk_UserDataHeaderHelper( size_t offset, ON_BinaryArchive& file, 
14101                                  int major_userdata_version, int minor_userdata_version, 
14102                                  ON_TextLog& dump )
14103 {
14104   // TCODE_OPENNURBS_CLASS_USERDATA chunks have 2 uuids
14105   // the first identifies the type of ON_Object class
14106   // the second identifies that kind of user data
14107   ON_UUID userdata_classid = ON_nil_uuid;
14108   ON_UUID userdata_itemid = ON_nil_uuid;
14109   ON_UUID userdata_appid = ON_nil_uuid;  // id of plug-in that owns the user data
14110   int userdata_copycount = -1;
14111   ON_Xform userdata_xform;
14112   bool bLastSavedAsGoo = false;
14113   int ud_archive_3dm_version = 0;
14114   int ud_archive_opennurbs_version = 0;
14115 
14116   bool rc = false;
14117   bool bCallEndRead3dmChunk = false;
14118 
14119   ON__UINT32 tcode = 0;
14120   ON__INT64 big_value = 0;
14121   const size_t offset0 = file.CurrentPosition();
14122 
14123   for(;;)
14124   {
14125     if ( 2 == major_userdata_version )
14126     {
14127       rc = file.PeekAt3dmBigChunkType(&tcode,&big_value);
14128       if ( !rc )
14129       {
14130         Dump3dmChunk_ErrorReportHelper(offset,"Unable to find the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump);
14131         break;
14132       }
14133       if ( TCODE_OPENNURBS_CLASS_USERDATA_HEADER != tcode )
14134       {
14135         Dump3dmChunk_ErrorReportHelper(offset,"Unable to find the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump);
14136         rc = false;
14137         break;
14138       }
14139       rc = file.BeginRead3dmBigChunk(&tcode,&big_value); 
14140       if ( !rc )
14141       {
14142         Dump3dmChunk_ErrorReportHelper(offset,"Unable to read the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump);
14143         break;
14144       }
14145       if ( TCODE_OPENNURBS_CLASS_USERDATA_HEADER != tcode )
14146       {
14147         Dump3dmChunk_ErrorReportHelper(offset,"Missing TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump);
14148         Dump3dmChunk_EndReadChunkHelper(file,offset0,tcode,big_value,dump);
14149         rc = false;
14150         break;
14151       }
14152       bCallEndRead3dmChunk = true;
14153     }
14154 
14155     rc = file.ReadUuid( userdata_classid );
14156     if ( !rc ) 
14157     {
14158       Dump3dmChunk_ErrorReportHelper(offset,"ReadUuid() failed to read the user data class id.",dump);
14159       break;
14160     }
14161 
14162     dump.Print("UserData class id = ");
14163     dump.Print( userdata_classid );
14164     const ON_ClassId* pUserDataClassId = ON_ClassId::ClassId(userdata_classid);
14165     if ( pUserDataClassId )
14166     {
14167       const char* sClassName = pUserDataClassId->ClassName();
14168       if ( sClassName )
14169       {
14170         dump.Print(" (%s)",sClassName);
14171       }
14172     }
14173     dump.Print("\n");
14174 
14175     rc = file.ReadUuid( userdata_itemid );
14176     if ( !rc ) 
14177     {
14178        Dump3dmChunk_ErrorReportHelper(offset,"ReadUuid() failed to read the user data item id.",dump);
14179        break;
14180     }
14181     dump.Print("UserData item id = ");
14182     dump.Print( userdata_itemid );
14183     dump.Print("\n");
14184 
14185     rc = file.ReadInt( &userdata_copycount );
14186     if ( !rc )
14187     {
14188       Dump3dmChunk_ErrorReportHelper(offset,"ReadInt() failed to read the user data copy count.",dump);
14189       break;
14190     }
14191     dump.Print("UserData copy count = %d\n",userdata_copycount);
14192 
14193     rc = file.ReadXform( userdata_xform );
14194     if ( !rc )
14195     {
14196       Dump3dmChunk_ErrorReportHelper(offset,"ReadXform() failed to read the user data xform.",dump);
14197       break;
14198     }
14199 
14200     if ( 2 != major_userdata_version )
14201       break;
14202     if ( minor_userdata_version < 1 )
14203       break;
14204     rc = file.ReadUuid( userdata_appid );
14205     if ( !rc)
14206     {
14207       Dump3dmChunk_ErrorReportHelper(offset,"ReadUuid() failed to read the user data app plug-in id.",dump);
14208       break;
14209     }
14210     dump.Print("UserData app plug-in id = ");
14211     dump.Print( userdata_appid );
14212     dump.Print("\n");
14213     if ( minor_userdata_version < 2 )
14214       break;
14215     rc = file.ReadBool(&bLastSavedAsGoo);
14216     if (!rc)
14217     {
14218       Dump3dmChunk_ErrorReportHelper(offset,"ReadBool() failed to read the user data header bSavedAsGoo value.",dump);
14219       break;
14220     }
14221     rc = file.ReadInt( &ud_archive_3dm_version );
14222     if (!rc)
14223     {
14224       Dump3dmChunk_ErrorReportHelper(offset,"ReadBool() failed to read the user data header bSavedAsGoo value.",dump);
14225       break;
14226     }
14227     rc = file.ReadInt( &ud_archive_opennurbs_version );
14228     if (!rc)
14229     {
14230       Dump3dmChunk_ErrorReportHelper(offset,"ReadBool() failed to read the user data header bSavedAsGoo value.",dump);
14231       break;
14232     }
14233     if ( bLastSavedAsGoo )
14234       dump.Print("Userdata originally written by opennurbs %d in 3dm version %d and saved as goo in this file.\n",ud_archive_opennurbs_version,ud_archive_3dm_version);
14235     else
14236       dump.Print("Userdata written by opennurbs %d in 3dm version %d.\n",ud_archive_opennurbs_version,ud_archive_3dm_version);
14237 
14238     break;
14239   }
14240 
14241   if ( bCallEndRead3dmChunk )
14242   {
14243     if (!Dump3dmChunk_EndReadChunkHelper(file,offset0,tcode,big_value,dump))
14244     {
14245       if (rc)
14246       {
14247         Dump3dmChunk_ErrorReportHelper(offset,"EndRead3dmChunk() failed to close the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk.",dump);
14248       }
14249       rc = false;
14250     }
14251   }
14252 
14253   return rc;
14254 }
14255 
14256 
14257 unsigned int 
14258 ON_BinaryArchive::Dump3dmChunk( ON_TextLog& dump, int recursion_depth )
14259 {
14260   //ON_BinaryArchive& file = *this;
14261   const char* typecode_name = 0;
14262   bool bShortChunk = false;
14263   const size_t offset0 = CurrentPosition();
14264   unsigned int typecode = 0;
14265   ON__INT64 big_value;
14266   bool rc = BeginRead3dmBigChunk( &typecode, &big_value );
14267   if (!rc) 
14268   {
14269     Dump3dmChunk_ErrorReportHelper(offset0,"BeginRead3dmChunk() failed.",dump);
14270   }
14271   else 
14272   {
14273     if ( 0 == typecode ) 
14274     {
14275       Dump3dmChunk_ErrorReportHelper(offset0,"BeginRead3dmChunk() returned typecode = 0.",dump);
14276       EndRead3dmChunk();
14277       return 0;
14278     }
14279     else {
14280       if ( 0 == recursion_depth )
14281       {
14282         dump.Print("\n");
14283       }
14284 
14286       typecode_name = ON_BinaryArchive::TypecodeName(typecode);
14287       bShortChunk = (0 != (typecode & TCODE_SHORT));
14288       if ( !DumpChunk_PrintHeaderInfo(offset0,typecode,big_value,typecode_name,dump) )
14289       {
14290         EndRead3dmChunk();
14291         return 0;
14292       }
14293 
14294       int major_userdata_version = -1;
14295       int minor_userdata_version = -1;
14296 
14297       switch( typecode ) 
14298       {
14299       case TCODE_PROPERTIES_TABLE:
14300       case TCODE_SETTINGS_TABLE:
14301       case TCODE_BITMAP_TABLE:
14302       case TCODE_MATERIAL_TABLE:
14303       case TCODE_LAYER_TABLE:
14304       case TCODE_GROUP_TABLE:
14305       case TCODE_LIGHT_TABLE:
14306       case TCODE_FONT_TABLE:
14307       case TCODE_DIMSTYLE_TABLE:
14308       case TCODE_HATCHPATTERN_TABLE:
14309       case TCODE_LINETYPE_TABLE:
14310       case TCODE_TEXTURE_MAPPING_TABLE:
14311       case TCODE_HISTORYRECORD_TABLE:
14312       case TCODE_USER_TABLE:
14313       case TCODE_INSTANCE_DEFINITION_TABLE:
14314       case TCODE_OBJECT_TABLE:
14315         // start of a table
14316         {
14317           dump.PushIndent();
14318           unsigned int record_typecode = 0;
14319           for (;;) {
14320             record_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14321             if ( !record_typecode ) {
14322               break;
14323             }
14324             if ( TCODE_ENDOFTABLE == record_typecode ) {
14325               break;
14326             }
14327           }
14328           dump.PopIndent();
14329         }
14330         break;
14331 
14332       case TCODE_PROPERTIES_OPENNURBS_VERSION:
14333         {
14334           dump.PushIndent();
14335           dump.Print("Version of opennurbs that wrote this file: %lld\n",big_value);
14336           dump.PopIndent();           
14337           if ( 0 == m_3dm_opennurbs_version && big_value > 0 && big_value <= 299999999 )
14338             ON_SetBinaryArchiveOpenNURBSVersion(*this,(ON__INT32)big_value);
14339         }
14340         break;
14341 
14342       case TCODE_BITMAP_RECORD:
14343         {
14344           dump.PushIndent();
14345           unsigned int bitmap_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14346           if ( 0 == typecode )
14347             typecode = bitmap_chunk_typecode;
14348           dump.PopIndent();
14349         }
14350         break;
14351 
14352       case TCODE_MATERIAL_RECORD:
14353         {
14354           dump.PushIndent();
14355           unsigned int material_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14356           if ( 0 == typecode )
14357             typecode = material_chunk_typecode;
14358           dump.PopIndent();
14359         }
14360         break;
14361 
14362       case TCODE_LAYER_RECORD:
14363         {
14364           dump.PushIndent();
14365           unsigned int material_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14366           if ( 0 == typecode )
14367             typecode = material_chunk_typecode;
14368           dump.PopIndent();
14369         }
14370         break;
14371 
14372       case TCODE_GROUP_RECORD:
14373         {
14374           dump.PushIndent();
14375           unsigned int group_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14376           if ( 0 == typecode )
14377             typecode = group_chunk_typecode;
14378           dump.PopIndent();
14379         }
14380         break;
14381 
14382       case TCODE_FONT_RECORD:
14383         {
14384           dump.PushIndent();
14385           unsigned int font_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14386           if ( 0 == typecode )
14387             typecode = font_chunk_typecode;
14388           dump.PopIndent();
14389         }
14390         break;
14391 
14392       case TCODE_DIMSTYLE_RECORD:
14393         {
14394           dump.PushIndent();
14395           unsigned int dimstyle_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14396           if ( 0 == typecode )
14397             typecode = dimstyle_chunk_typecode;
14398           dump.PopIndent();
14399         }
14400         break;
14401 
14402       case TCODE_LIGHT_RECORD:
14403         {
14404           dump.PushIndent();
14405           unsigned int light_chunk_typecode = 0;
14406           for (;;) {
14407             light_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14408             if ( !light_chunk_typecode ) {
14409               break;
14410             }
14411             if ( TCODE_LIGHT_RECORD_END == light_chunk_typecode ) {
14412               break;
14413             }
14414             switch( light_chunk_typecode ) {
14415             //case TCODE_OBJECT_RECORD_TYPE:
14416             case TCODE_LIGHT_RECORD_ATTRIBUTES:
14417             case TCODE_LIGHT_RECORD_ATTRIBUTES_USERDATA:
14418             case TCODE_OPENNURBS_CLASS:
14419               break;
14420             default:
14421               {
14422                 Dump3dmChunk_ErrorReportHelper(offset0,"Rogue chunk in light record.",dump);
14423               }
14424             }
14425           }
14426           dump.PopIndent();
14427         }
14428         break;
14429 
14430       case TCODE_TEXTURE_MAPPING_RECORD:
14431         {
14432           dump.PushIndent();
14433           unsigned int mapping_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14434           if ( !typecode )
14435             typecode = mapping_chunk_typecode;
14436           dump.PopIndent();
14437         }
14438         break;
14439 
14440       case TCODE_HISTORYRECORD_RECORD:
14441         {
14442           dump.PushIndent();
14443           unsigned int history_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14444           if ( !typecode )
14445             typecode = history_chunk_typecode;
14446           dump.PopIndent();
14447         }
14448         break;
14449 
14450       case TCODE_HATCHPATTERN_RECORD:
14451         {
14452           dump.PushIndent();
14453           unsigned int hatch_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14454           if ( !typecode )
14455             typecode = hatch_chunk_typecode;
14456           dump.PopIndent();
14457         }
14458         break;
14459 
14460       case TCODE_INSTANCE_DEFINITION_RECORD:
14461         {
14462           dump.PushIndent();
14463           unsigned int idef_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14464           if ( 0 == typecode )
14465             typecode = idef_chunk_typecode;
14466           dump.PopIndent();
14467         }
14468         break;
14469 
14470       case TCODE_OBJECT_RECORD:
14471         {
14472           dump.PushIndent();
14473           unsigned int object_chunk_typecode = 0;
14474           for (;;) {
14475             object_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14476             if ( !object_chunk_typecode ) {
14477               break;
14478             }
14479             if ( TCODE_OBJECT_RECORD_END == object_chunk_typecode ) {
14480               break;
14481             }
14482             switch( object_chunk_typecode ) {
14483             case TCODE_OBJECT_RECORD_TYPE:
14484             case TCODE_OBJECT_RECORD_ATTRIBUTES:
14485             case TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA:
14486             case TCODE_OPENNURBS_CLASS:
14487               break;
14488             default:
14489               {
14490                 Dump3dmChunk_ErrorReportHelper(offset0,"Rogue chunk in object record.",dump);
14491               }
14492             }
14493           }
14494           dump.PopIndent();
14495         }
14496         break;
14497 
14498       case TCODE_OBJECT_RECORD_ATTRIBUTES:
14499         {
14500           dump.PushIndent();
14501           if ( big_value < 14 )
14502           {
14503             Dump3dmChunk_ErrorReportHelper(offset0,"Length of chunk is too small.  Should be >= 14.",dump);
14504           }
14505           else
14506           {
14507             ON_UUID uuid = ON_nil_uuid;
14508             int layer_index = -99;
14509             int mj = -1;
14510             int mn = -1;
14511             if ( !Read3dmChunkVersion(&mj,&mn))
14512             {
14513               Dump3dmChunk_ErrorReportHelper(offset0,"Read3dmChunkVersion() failed.",dump);
14514             }
14515             else if (!ReadUuid(uuid))
14516             {
14517               Dump3dmChunk_ErrorReportHelper(offset0,"ReadUuid() failed.",dump);
14518             }
14519             else if ( !ReadInt(&layer_index) )
14520             {
14521               Dump3dmChunk_ErrorReportHelper(offset0,"ReadInt() failed to read layer index.",dump);
14522             }
14523             else
14524             {
14525               dump.Print("Rhino object uuid: ");
14526               dump.Print(uuid);
14527               dump.Print("\n");
14528               dump.Print("layer index: %d\n",layer_index);
14529             }
14530           }
14531           dump.PopIndent();
14532         }
14533         break;
14534 
14535       case TCODE_OPENNURBS_CLASS:
14536         {
14537           dump.PushIndent();
14538           unsigned int opennurbs_object_chunk_typecode = 0;
14539           for (;;) {
14540             opennurbs_object_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1  );
14541             if ( !opennurbs_object_chunk_typecode ) {
14542               break;
14543             }
14544             if ( TCODE_OPENNURBS_CLASS_END == opennurbs_object_chunk_typecode ) {
14545               break;
14546             }
14547             switch( opennurbs_object_chunk_typecode ) 
14548             {
14549             case TCODE_OPENNURBS_CLASS_UUID:
14550               break;
14551             case TCODE_OPENNURBS_CLASS_DATA:
14552               break;
14553             case TCODE_OPENNURBS_CLASS_USERDATA:
14554               break;
14555             default:
14556               {
14557                 Dump3dmChunk_ErrorReportHelper(offset0,"Rogue chunk in OpenNURBS class record.",dump);
14558               }
14559             }
14560           }
14561           dump.PopIndent();
14562         }
14563         break;
14564 
14565       case TCODE_OPENNURBS_CLASS_USERDATA:
14566         {
14567           if ( !Read3dmChunkVersion(&major_userdata_version, &minor_userdata_version ) )
14568           {
14569             Dump3dmChunk_ErrorReportHelper(offset0,"Read3dmChunkVersion() failed to read TCODE_OPENNURBS_CLASS_USERDATA chunk version.",dump);
14570           }
14571           else
14572           {
14573             dump.PushIndent();
14574             dump.Print("UserData chunk version: %d.%d\n",
14575                        major_userdata_version,
14576                        minor_userdata_version
14577                        );
14578             if ( 1 == major_userdata_version || 2 == major_userdata_version )
14579             {
14580               const size_t userdata_header_offset = CurrentPosition();
14581               switch ( major_userdata_version )
14582               {
14583               case 1:
14584               case 2:
14585                 {
14586                   // version 1 user data header information was not wrapped
14587                   // in a chunk.
14588                   //
14589                   // version 2 user data header information is wrapped
14590                   // in a TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk.
14591                   if ( Dump3dmChunk_UserDataHeaderHelper( 
14592                                   userdata_header_offset, *this, 
14593                                   major_userdata_version, minor_userdata_version,
14594                                   dump ) 
14595                      )
14596                   {
14597                     // a TCODE_ANONYMOUS_CHUNK contains user data goo
14598                     int anon_typecode =  Dump3dmChunk( dump, recursion_depth+1 );
14599                     if ( TCODE_ANONYMOUS_CHUNK != anon_typecode )
14600                     {
14601                       Dump3dmChunk_ErrorReportHelper( offset0,"Userdata Expected a TCODE_ANONYMOUS_CHUNK chunk.",dump);
14602                     }
14603                   }
14604                 }
14605                 break;
14606               default:
14607                 if ( major_userdata_version < 3 )
14608                 {
14609                 }
14610                 else
14611                 {
14612                   dump.Print("New user data format created after this diagnostic tool was written.\n");
14613                 }
14614                 break;
14615               }
14616             }
14617 
14618             dump.PopIndent();
14619           }
14620         }
14621         break;
14622 
14623       case TCODE_OPENNURBS_CLASS_UUID:
14624       case TCODE_USER_TABLE_UUID:
14625         {
14626           dump.PushIndent();
14627           ON_UUID uuid = ON_nil_uuid;
14628           const ON_ClassId* pClassId = 0;
14629           if ( !ReadUuid( uuid ) ) {
14630              Dump3dmChunk_ErrorReportHelper(offset0,"ReadUuid() failed.",dump);
14631           }
14632           else 
14633           {
14634             if ( typecode == TCODE_OPENNURBS_CLASS_UUID ) 
14635             {
14636               dump.Print("OpenNURBS class id = ");
14637               pClassId = ON_ClassId::ClassId(uuid);
14638             }
14639             else if ( typecode == TCODE_USER_TABLE_UUID ) 
14640             {
14641               dump.Print("User table id = ");
14642             }
14643             else {
14644               dump.Print("UUID = ");
14645             }
14646             dump.Print( uuid );
14647             if ( pClassId )
14648             {
14649               const char* sClassName = pClassId->ClassName();
14650               if ( sClassName )
14651               {
14652                 dump.Print(" (%s)",sClassName);
14653               }
14654             }
14655             dump.Print("\n");
14656           }
14657 
14658           dump.PopIndent();
14659         }
14660         break;
14661 
14662       case TCODE_OPENNURBS_CLASS_USERDATA_HEADER:
14663         {
14664           // we should never get here because this chunk is parsed in the TCODE_OPENNURBS_CLASS_USERDATA case above.
14665           Dump3dmChunk_ErrorReportHelper(offset0,"Encountered TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk outside a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump);
14666         }
14667         break;
14668 
14669       case TCODE_ENDOFFILE:
14670       case TCODE_ENDOFFILE_GOO:
14671         {
14672           dump.PushIndent();
14673           if ( big_value < 4 ) {
14674             Dump3dmChunk_ErrorReportHelper(offset0,"TCODE_ENDOFFILE chunk withlength < 4.",dump);
14675           }
14676           else {
14677             ON__UINT64 sizeof_file = 0;
14678             ReadEOFSizeOfFile(&sizeof_file);
14679             dump.Print("current position = %d  stored size = %llu\n",
14680                        CurrentPosition(), 
14681                        sizeof_file
14682                        );
14683           }
14684           dump.PopIndent();
14685         }
14686         break;
14687 
14688       }
14689     }
14690 
14691     const size_t offset1 = CurrentPosition();
14692     if ( !EndRead3dmChunk(true) ) 
14693     {
14694       Dump3dmChunk_ErrorReportHelper(offset1,"EndRead3dmChunk() failed.",dump);
14695       rc = false;
14696     }
14697     else if (!bShortChunk) 
14698     {
14699       ON__INT64 delta =  (offset1 > offset0)
14700                 ?  ((ON__INT64)(offset1 - offset0))
14701                 : -((ON__INT64)(offset0 - offset1));
14702       const ON__INT64 extra = big_value - (delta-4-SizeofChunkLength());
14703       if ( extra < 0 ) 
14704       {
14705         Dump3dmChunk_ErrorReportHelper(offset0,"Read beyond end of chunk.",dump);
14706       }
14707     }
14708   }
14709   return typecode;
14710 }
14711 
14712 
14713 
14714 ON_Read3dmBufferArchive::ON_Read3dmBufferArchive( 
14715           size_t sizeof_buffer, 
14716           const void* buffer,
14717           bool bCopyBuffer,
14718           int archive_3dm_version,
14719           int archive_opennurbs_version
14720           )
14721 : ON_BinaryArchive(ON::read3dm)
14722 , m_p(0)
14723 , m_buffer(0)
14724 , m_sizeof_buffer(0)
14725 , m_buffer_position(0)
14726 , m_reserved1(0)
14727 , m_reserved2(0)
14728 , m_reserved3(0)
14729 , m_reserved4(0)
14730 {
14731   if ( sizeof_buffer > 0 && 0 != buffer )
14732   {
14733     if ( bCopyBuffer )
14734     {
14735       m_p = onmalloc(sizeof_buffer);
14736       if ( 0 != m_p )
14737         memcpy(m_p,buffer,sizeof_buffer);
14738       m_buffer = (const unsigned char*)m_p;
14739     }
14740     else
14741     {
14742       m_buffer = (const unsigned char*)buffer;
14743     }
14744     if ( m_buffer )
14745     {
14746       m_sizeof_buffer = sizeof_buffer;
14747       SetArchive3dmVersion(archive_3dm_version);
14748       ON_SetBinaryArchiveOpenNURBSVersion(*this,archive_opennurbs_version);
14749     }
14750   }
14751 }
14752 
14753 ON_Read3dmBufferArchive::~ON_Read3dmBufferArchive()
14754 {
14755   if ( m_p )
14756     onfree(m_p);
14757 }
14758 
14759 // ON_BinaryArchive overrides
14760 size_t ON_Read3dmBufferArchive::CurrentPosition() const
14761 {
14762   return m_buffer_position;
14763 }
14764 
14765 bool ON_Read3dmBufferArchive::SeekFromCurrentPosition( int offset )
14766 {
14767   bool rc = false;
14768   if ( m_buffer )
14769   {
14770     if (offset >= 0 )
14771     {
14772       m_buffer_position += offset;
14773       rc = true;
14774     }
14775     else if ( size_t(-offset) <= m_buffer_position )
14776     {
14777       m_buffer_position -= (size_t(-offset));
14778       rc = true;
14779     }
14780   }
14781   return rc;
14782 }
14783 
14784 bool ON_Read3dmBufferArchive::SeekFromStart( size_t offset )
14785 {
14786   bool rc = false;
14787   if ( m_buffer ) 
14788   {
14789     if ( offset > 0 )
14790       m_buffer_position = offset;
14791     else
14792       m_buffer_position = 0;
14793     rc = true;
14794   }
14795   return rc;
14796 }
14797 
14798 bool ON_Read3dmBufferArchive::AtEnd() const
14799 {
14800   return (m_buffer_position >= m_sizeof_buffer) ? true : false;
14801 }
14802 
14803 size_t ON_Read3dmBufferArchive::Read( size_t count, void* buffer )
14804 {
14805   if ( count <= 0 || 0 == buffer )
14806     return 0;
14807 
14808   size_t maxcount = ( m_sizeof_buffer > m_buffer_position ) 
14809                   ? (m_sizeof_buffer - m_buffer_position)
14810                   : 0;
14811   if ( count > maxcount )
14812     count = maxcount;
14813 
14814   if ( count > 0 ) 
14815   {
14816     memcpy( buffer, m_buffer+m_buffer_position, count );
14817     m_buffer_position += count;
14818   }
14819 
14820   return count;
14821 }
14822 
14823 size_t ON_Read3dmBufferArchive::Write( size_t, const void* )
14824 {
14825   // ON_Read3dmBufferArchive does not support Write() and Flush()
14826   return 0;
14827 }
14828 
14829 bool ON_Read3dmBufferArchive::Flush()
14830 {
14831   // ON_Read3dmBufferArchive does not support Write() and Flush()
14832   return false;
14833 }
14834 
14835 
14836 size_t ON_Read3dmBufferArchive::SizeOfBuffer() const
14837 {
14838   return m_sizeof_buffer;
14839 }
14840 
14841 const void* ON_Read3dmBufferArchive::Buffer() const
14842 {
14843   return (const void*)m_buffer;
14844 }
14845 
14846 
14847 
14848 
14849 
14850 ON_Write3dmBufferArchive::ON_Write3dmBufferArchive( 
14851           size_t initial_sizeof_buffer, 
14852           size_t max_sizeof_buffer, 
14853           int archive_3dm_version,
14854           int archive_opennurbs_version
14855           )
14856 : ON_BinaryArchive(ON::write3dm)
14857 , m_p(0)
14858 , m_buffer(0)
14859 , m_sizeof_buffer(0)
14860 , m_max_sizeof_buffer(max_sizeof_buffer)
14861 , m_sizeof_archive(0)
14862 , m_buffer_position(0)
14863 , m_reserved1(0)
14864 , m_reserved2(0)
14865 , m_reserved3(0)
14866 , m_reserved4(0)
14867 {
14868   if ( initial_sizeof_buffer > 0 )
14869     AllocBuffer(initial_sizeof_buffer);
14870   if ( archive_3dm_version < 2 )
14871     archive_3dm_version = ON_BinaryArchive::CurrentArchiveVersion();
14872   SetArchive3dmVersion(archive_3dm_version);
14873   ON_SetBinaryArchiveOpenNURBSVersion(*this,archive_opennurbs_version);
14874 }
14875 
14876 ON_Write3dmBufferArchive::~ON_Write3dmBufferArchive()
14877 {
14878   if ( m_p )
14879     onfree(m_p);
14880 }
14881 
14882 void ON_Write3dmBufferArchive::AllocBuffer( size_t sz )
14883 {
14884   if ( sz > m_sizeof_buffer 
14885        && (m_max_sizeof_buffer <= 0 || sz <= m_max_sizeof_buffer) 
14886      )
14887   {
14888     if ( sz < 2*m_sizeof_buffer )
14889     {
14890       sz = 2*m_sizeof_buffer;
14891       if ( sz > m_max_sizeof_buffer )
14892         sz = m_max_sizeof_buffer;
14893     }
14894 
14895     m_p = onrealloc(m_p,sz);
14896     m_buffer = (unsigned char*)m_p;
14897 
14898     if ( 0 != m_buffer )
14899     {
14900       memset( m_buffer + m_sizeof_buffer, 0, sz - m_sizeof_buffer );
14901       m_sizeof_buffer = sz;
14902     }
14903     else
14904     {
14905       m_sizeof_buffer = 0;
14906     }
14907 
14908   }
14909 }
14910 
14911 // ON_BinaryArchive overrides
14912 size_t ON_Write3dmBufferArchive::CurrentPosition() const
14913 {
14914   return m_buffer_position;
14915 }
14916 
14917 bool ON_Write3dmBufferArchive::SeekFromCurrentPosition( int offset )
14918 {
14919   bool rc = false;
14920   if ( m_buffer )
14921   {
14922     if (offset >= 0 )
14923     {
14924       m_buffer_position += offset;
14925       rc = true;
14926     }
14927     else if ( size_t(-offset) <= m_buffer_position )
14928     {
14929       m_buffer_position -= (size_t(-offset));
14930       rc = true;
14931     }
14932   }
14933   return rc;
14934 }
14935 
14936 bool ON_Write3dmBufferArchive::SeekFromStart( size_t offset )
14937 {
14938   bool rc = false;
14939   if ( m_buffer ) 
14940   {
14941     if ( offset > 0 )
14942       m_buffer_position = offset;
14943     else
14944       m_buffer_position = 0;
14945     rc = true;
14946   }
14947   return rc;
14948 }
14949 
14950 bool ON_Write3dmBufferArchive::AtEnd() const
14951 {
14952   return (m_buffer_position >= m_sizeof_buffer) ? true : false;
14953 }
14954 
14955 size_t ON_Write3dmBufferArchive::Read( size_t count, void* buffer )
14956 {
14957   if ( count <= 0 || 0 == buffer )
14958     return 0;
14959 
14960   size_t maxcount = ( m_sizeof_buffer > m_buffer_position ) 
14961                   ? (m_sizeof_buffer - m_buffer_position)
14962                   : 0;
14963   if ( count > maxcount )
14964     count = maxcount;
14965 
14966   if ( count > 0 ) 
14967   {
14968     memcpy( buffer, m_buffer+m_buffer_position, count );
14969     m_buffer_position += count;
14970   }
14971 
14972   return count;
14973 }
14974 
14975 size_t ON_Write3dmBufferArchive::Write( size_t sz, const void* buffer )
14976 {
14977   if ( sz <= 0 || 0 == buffer )
14978     return 0;
14979 
14980   if ( m_buffer_position + sz > m_sizeof_buffer )
14981   {
14982     AllocBuffer(m_buffer_position + sz);
14983   }
14984 
14985   if ( m_buffer_position + sz > m_sizeof_buffer )
14986     return 0;
14987 
14988   memcpy( m_buffer + m_buffer_position, buffer, sz );
14989   m_buffer_position += sz;
14990   if ( m_buffer_position > m_sizeof_archive )
14991     m_sizeof_archive = m_buffer_position;
14992 
14993   return sz;
14994 }
14995 
14996 bool ON_Write3dmBufferArchive::Flush()
14997 {
14998   // Nothing to flush
14999   return true;
15000 }
15001 
15002 
15003 size_t ON_Write3dmBufferArchive::SizeOfBuffer() const
15004 {
15005   return m_sizeof_buffer;
15006 }
15007 
15008 const void* ON_Write3dmBufferArchive::Buffer() const
15009 {
15010   return (const void*)m_buffer;
15011 }
15012 
15013 void* ON_Write3dmBufferArchive::HarvestBuffer()
15014 {
15015   void* buffer = m_buffer;
15016 
15017   m_p = 0;
15018   m_buffer = 0;
15019   m_sizeof_buffer = 0;
15020   m_sizeof_archive = 0;
15021   m_buffer_position = 0;
15022 
15023   return buffer;
15024 }
15025 
15026 size_t ON_Write3dmBufferArchive::SizeOfArchive() const
15027 {
15028   return m_sizeof_archive;
15029 }
15030 
15031 
15032 
15033 
15034 ON_BinaryArchiveBuffer::ON_BinaryArchiveBuffer( ON::archive_mode mode, ON_Buffer* buffer )
15035 : ON_BinaryArchive(mode)
15036 , m_buffer(buffer)
15037 {
15038 }
15039 
15040 ON_BinaryArchiveBuffer::~ON_BinaryArchiveBuffer()
15041 {
15042   m_buffer = 0;
15043 }
15044 
15045 bool ON_BinaryArchiveBuffer::SetBuffer( ON_Buffer* buffer )
15046 {
15047   if ( 0 == m_buffer )
15048   {
15049     m_buffer = buffer;
15050     return true;
15051   }
15052 
15053   return false;
15054 }
15055 
15056 ON_Buffer* ON_BinaryArchiveBuffer::Buffer() const
15057 {
15058   return m_buffer;
15059 }
15060 
15061 size_t ON_BinaryArchiveBuffer::CurrentPosition() const
15062 {
15063   if ( 0 != m_buffer )
15064     return (size_t)m_buffer->CurrentPosition();
15065 
15066   return 0;
15067 }
15068 
15069 bool ON_BinaryArchiveBuffer::SeekFromCurrentPosition(int offset)
15070 {
15071   if ( 0 != m_buffer )
15072     return m_buffer->SeekFromCurrentPosition(offset);
15073 
15074   return false;
15075 }
15076 
15077 bool ON_BinaryArchiveBuffer::SeekFromStart(size_t offset)
15078 {
15079   if ( 0 != m_buffer )
15080     return m_buffer->SeekFromStart((ON__INT64)offset);
15081 
15082   return false;
15083 }
15084 
15085 bool ON_BinaryArchiveBuffer::AtEnd() const
15086 {
15087   if ( 0 != m_buffer )
15088     return m_buffer->AtEnd();
15089 
15090   return false;
15091 }
15092 
15093 bool ON_BinaryArchiveBuffer::SeekFromEnd( ON__INT64 offset )
15094 {
15095   if ( 0 != m_buffer )
15096     return m_buffer->SeekFromEnd(offset);
15097 
15098   return false;
15099 }
15100 
15101 size_t ON_BinaryArchiveBuffer::Read( size_t count, void* a )
15102 {
15103   if ( 0 != m_buffer )
15104     return (size_t)m_buffer->Read(count,a);
15105 
15106   return 0;
15107 }
15108 
15109 size_t ON_BinaryArchiveBuffer::Write( size_t count, const void* a )
15110 {
15111   if ( 0 != m_buffer )
15112     return (size_t)m_buffer->Write(count,a);
15113 
15114   return 0;
15115 }
15116 
15117 bool ON_BinaryArchiveBuffer::Flush()
15118 {
15119   if ( 0 != m_buffer )
15120     return true;
15121 
15122   return false;
15123 }
15124 
15125 ON_FileIterator::ON_FileIterator()
15126 : m_count(0)
15127 #if defined(ON_COMPILER_MSC)
15128 , m_file_attributes_mask(0)
15129 , m_h(0)
15130 #else
15131 , m_dir(0)
15132 , m_current_file_attributes(0)
15133 , m_current_file_size(0)
15134 , m_current_file_create_time(0)
15135 , m_current_last_modified_time(0)
15136 , m_current_last_access_time(0)
15137 #endif
15138 {
15139   Destroy();
15140 #if defined(ON_COMPILER_MSC)
15141   memset(&m_fd,0,sizeof(m_fd));
15142 #else
15143   memset(&m_dirent,0,sizeof(m_dirent));
15144   m_dirent.d_name[0] = 0;
15145   memset(&m_dirent_name_buffer[0],0,sizeof(m_dirent_name_buffer));
15146   memset(&m_current_name[0],0,sizeof(m_current_name));
15147 #endif
15148 }
15149 
15150 ON_FileIterator::~ON_FileIterator()
15151 {
15152   Destroy();
15153 }
15154 
15155 void ON_FileIterator::Destroy()
15156 {
15157 #if defined(ON_COMPILER_MSC)
15158   if ( 0 != m_h )
15159   {
15160     ::FindClose(m_h);
15161     m_h = 0;
15162   }
15163   m_file_attributes_mask = 0;
15164   memset(&m_fd,0,sizeof(m_fd));
15165 #else
15166   if ( 0 != m_dir )
15167   {
15168     closedir(m_dir);
15169     m_dir = 0;
15170   }
15171   memset(&m_dirent,0,sizeof(m_dirent));
15172   m_dirent.d_name[0] = 0;
15173   memset(&m_dirent_name_buffer[0],0,sizeof(m_dirent_name_buffer));
15174   m_ws_file_name_filter.Destroy();
15175   m_utf8_file_name_filter.Destroy();
15176   memset(&m_current_name[0],0,sizeof(m_current_name));
15177   m_current_file_attributes = 0;
15178   m_current_file_size = 0;
15179   m_current_file_create_time = 0;
15180   m_current_last_modified_time = 0;
15181   m_current_last_access_time = 0;
15182 #endif
15183   m_count = 0;
15184   m_directory.Empty();
15185 }
15186 
15187 ON__UINT64 ON_FileIterator::Count() const
15188 {
15189   return m_count;
15190 }
15191 
15192 #if defined(ON_COMPILER_MSC)
15193 static bool IsDotOrDotDotDir( const wchar_t* s )
15194 #else
15195 static bool IsDotOrDotDotDir( const char* s )
15196 #endif
15197 {
15198   bool rc = false;
15199   for (;;)
15200   {
15201     if ( 0 == s )
15202       break;
15203     if ( '.' != s[0] )
15204       break;
15205     if ( 0 != s[1] )
15206     {
15207       if ( '.' != s[1] )
15208         break;
15209       if ( 0 != s[2] )
15210         break;
15211     }
15212     rc = true; // s = "." or s = ".."
15213     break;
15214   }
15215   return rc;
15216 }
15217 
15218 const wchar_t* ON_FileIterator::FirstFile(
15219     const char* directory_name, 
15220     const char* file_name_filter 
15221     )
15222 {
15223   // assume directory_name and file_name_filter are UTF-8 encoded
15224   // strings, convert them to wchar_t strings and call the
15225   // wchar_t version of this function.
15226   ON_wString ws_directory_name = directory_name;
15227   ON_wString ws_file_name_filter = file_name_filter;
15228   const wchar_t* wchar_directory_name = ws_directory_name;
15229   const wchar_t* wchar_file_name_filter = ws_file_name_filter;
15230   return FirstFile(wchar_directory_name,wchar_file_name_filter);
15231 }
15232 
15233 const wchar_t* ON_FileIterator::FirstFile(
15234     const wchar_t* directory_name, 
15235     const wchar_t* file_name_filter 
15236     )
15237 {
15238   ON_wString buffer(directory_name);
15239   {
15240     const wchar_t* dir_seps = L"/\\";
15241     buffer.TrimRight(dir_seps);
15242     if ( buffer.Length() <= 0 || buffer.IsEmpty() )
15243       buffer = directory_name;
15244     else
15245       directory_name = buffer;
15246   }
15247 
15248 #if defined(ON_COMPILER_MSC)
15249   const ON__UINT32 saved_mask = m_file_attributes_mask;
15250   Destroy();
15251   m_file_attributes_mask = saved_mask;
15252 
15253   ON_wString s(directory_name);
15254 
15255   
15256   if ( 0 == file_name_filter )
15257   {
15258     // A null file file_name_filter means iterate 
15259     // through all items in the directory.  To do
15260     // this using Windows' ::FindFirstFile, set the
15261     // filter to "*.*", even though some items will
15262     // not contain a "dot".
15263     file_name_filter = L"*.*";  
15264   }
15265   
15266   if ( 0 != file_name_filter[0] )
15267   {
15268     s += L"\\";
15269     s += file_name_filter;
15270   }
15271 
15272   m_h = ::FindFirstFile(s, &m_fd);
15273   if ( 0 == m_h || INVALID_HANDLE_VALUE == m_h || 0 == m_fd.cFileName[0] )
15274   {
15275     // Happens on "fake" directories like "My Music" and "My Videos"
15276     m_h = 0;
15277     Destroy();
15278     m_file_attributes_mask = saved_mask;
15279     return 0;
15280   }
15281 
15282   m_directory = directory_name;
15283 
15284   if ( IsDotOrDotDotDir(m_fd.cFileName) || 0 != (m_file_attributes_mask & m_fd.dwFileAttributes) )
15285   {
15286     return NextFile();
15287   }
15288   
15289   m_count++;
15290   m_fd.cFileName[(sizeof(m_fd.cFileName)/sizeof(m_fd.cFileName[0]))-1] = 0;
15291   return m_fd.cFileName;
15292 
15293 #else
15294 
15295   // gcc code
15296   Destroy();
15297   m_directory = directory_name;
15298   m_ws_file_name_filter = file_name_filter;
15299   m_utf8_file_name_filter = file_name_filter;
15300   const ON_String utf8_str(m_directory); // convert wchar_t to utf8 string
15301   const char* s = utf8_str;
15302   m_dir = (0 != s && 0 != s[0]) ? opendir(s) : 0;
15303   if ( 0 != m_dir )
15304     return NextFile();
15305   Destroy();
15306   return 0;
15307 
15308 #endif
15309 }
15310 
15311 const wchar_t* ON_FileIterator::NextFile()
15312 {
15313 #if defined(ON_COMPILER_MSC)
15314   const ON__UINT32 saved_mask = m_file_attributes_mask;
15315   if ( 0 == m_h || INVALID_HANDLE_VALUE == m_h || 0 == m_fd.cFileName[0] )
15316   {
15317     Destroy();
15318     m_file_attributes_mask = saved_mask;
15319     return 0;
15320   }
15321 
15322   for (;;)
15323   {
15324     if ( !::FindNextFile( m_h, &m_fd) || 0 == m_fd.cFileName[0] )
15325     {
15326       Destroy();
15327       m_file_attributes_mask = saved_mask;
15328       return 0;
15329     }
15330 
15331     if ( IsDotOrDotDotDir(m_fd.cFileName) || 0 != (m_file_attributes_mask & m_fd.dwFileAttributes) )
15332     {
15333       continue;
15334     }
15335 
15336     break;
15337   }
15338 
15339   m_count++;
15340   m_fd.cFileName[(sizeof(m_fd.cFileName)/sizeof(m_fd.cFileName[0]))-1] = 0;
15341   return m_fd.cFileName;
15342 #else
15343 
15344   // gcc code
15345   ON__UINT64 current_file_attributes = 0;
15346   wchar_t current_name[ sizeof(m_current_name)/sizeof(m_current_name[0]) ];
15347   for(;;)
15348   {
15349     current_file_attributes = 0;
15350     struct dirent* dp = 0;
15351     int readdir_errno = readdir_r(m_dir, &m_dirent, &dp);
15352     if ( 0 !=  readdir_errno )
15353       break;
15354     if ( 0 == dp )
15355       break;
15356     if ( 0 == m_dirent.d_name[0] )
15357       break;
15358 
15359     if ( IsDotOrDotDotDir(m_dirent.d_name) )
15360       continue;
15361 
15362     memset( current_name, 0, sizeof(current_name) );
15363     ON_ConvertUTF8ToWideChar(
15364       &m_dirent.d_name[0],-1, // null terminated utf8 string
15365       &current_name[0], ((int)(sizeof(current_name)/sizeof(current_name[0]))) - 1, // output wchar_t string
15366       0, // null output error status
15367       (4|8|16), // mask common conversion errors
15368       0, // error_code_point = null terminator inserted at point of conversion error
15369       0  // null ouput end-of-string pointer
15370       );
15371     // TODO
15372     //   Test m_dirent.d_name to make sure it passes m_ws/utf8_file_name_filter
15373 
15374     ON_wString wpath = m_directory;
15375     wpath += '/';
15376     wpath += current_name;
15377 
15378     // get a utf8 version of the full path to pass to stat
15379     const ON_String utf8buffer(wpath);
15380     const char* utf8path = utf8buffer;
15381     if ( 0 == utf8path )
15382       continue;
15383 
15384     struct stat buf;
15385     memset(&buf,0,sizeof(buf));
15386     int stat_errno = stat( utf8path, &buf);
15387     if ( 0 != stat_errno )
15388       continue;
15389 
15390     if ( S_ISDIR(buf.st_mode) )
15391     {
15392       current_file_attributes = 2;
15393     }
15394     else if ( S_ISREG(buf.st_mode) )
15395     {
15396       // Only *.ext filters work at this time for non-windows
15397       const wchar_t* file_name_filter = m_ws_file_name_filter;
15398       if (   0 != file_name_filter
15399             && '*' == file_name_filter[0] 
15400             && '.' == file_name_filter[1]
15401             && 0 != file_name_filter[2]
15402             && '*' != file_name_filter[2] )
15403       {
15404         // assume this is a *.extension filter
15405         const wchar_t* current_name_ext = 0;
15406         on_wsplitpath(current_name,0,0,0,&current_name_ext);
15407         if (   0 == current_name_ext 
15408             || 0 != wcscmp(file_name_filter+1,current_name_ext) 
15409            )
15410         {
15411           // current_name does pass match file_name_filter
15412           continue;
15413         }
15414       }
15415       current_file_attributes = 1;
15416     }
15417     else
15418       continue;
15419 
15420     // save current item information
15421     memcpy( m_current_name, current_name, sizeof(m_current_name) );
15422     m_current_file_attributes = current_file_attributes;
15423     m_current_file_size = buf.st_size;
15424     m_current_file_create_time = buf.st_mtime; // create time is not available on struct stat
15425     m_current_last_modified_time = buf.st_mtime;
15426     m_current_last_access_time = buf.st_atime;
15427 
15428     return m_current_name;
15429   }
15430 
15431   Destroy();
15432   return 0;
15433 #endif
15434 }
15435 
15436 
15437 const wchar_t* ON_FileIterator::CurrentFileName() const
15438 {
15439 #if defined(ON_COMPILER_MSC)
15440   return ( 0 != m_h && 0 != m_fd.cFileName[0] ) ? m_fd.cFileName : 0;
15441 #else
15442   return ( 0 != m_current_name[0] ) ? m_current_name : 0;
15443 #endif
15444 }
15445 
15446 ON__UINT64 ON_FileIterator::CurrentFileSize() const
15447 {
15448   ON__UINT64 file_size = 0;
15449 
15450 #if defined(ON_COMPILER_MSC)
15451   if ( 0 != CurrentFileName() ) 
15452   {
15453     file_size  = m_fd.nFileSizeHigh;
15454     file_size *= ((ON__UINT64)0xFFFFFFFF);
15455     file_size += m_fd.nFileSizeLow;
15456   }
15457 #else
15458   file_size = m_current_file_size;
15459 #endif
15460 
15461   return file_size;
15462 }
15463 
15464 bool ON_FileIterator::CurrentFileIsDirectory() const
15465 {
15466   bool rc = false;
15467   const wchar_t* current_file_name = CurrentFileName();
15468   if ( 0 != current_file_name && 0 != current_file_name[0] )
15469   {
15470 #if defined(ON_COMPILER_MSC)
15471     if ( 0 != (FILE_ATTRIBUTE_DIRECTORY & m_fd.dwFileAttributes) )
15472     {
15473       rc = true;
15474     }
15475 #else
15476     if ( 2 == m_current_file_attributes)
15477     {
15478       rc = true;
15479     }
15480 #endif
15481   }
15482   return rc;
15483 }
15484 
15485 bool ON_FileIterator::CurrentFileIsHidden() const
15486 {
15487   bool rc = false;
15488   const wchar_t* current_file_name = CurrentFileName();
15489   if ( 0 != current_file_name && 0 != current_file_name[0] )
15490   {
15491     if ( '.' == current_file_name[0] )
15492     {
15493       rc = true;
15494     }
15495 #if defined(ON_COMPILER_MSC)
15496     else if ( 0 != (FILE_ATTRIBUTE_HIDDEN & m_fd.dwFileAttributes) )
15497     {
15498       rc = true;
15499     }
15500 #endif
15501   }
15502   return rc;
15503 }
15504 
15505 
15506 bool ON_FileIterator::GetCurrentFullPathFileName( ON_wString& filename ) const
15507 {
15508   bool rc = false;
15509 
15510 #if defined(ON_COMPILER_MSC)
15511   if ( 0 == m_h || 0 == m_fd.cFileName )
15512   {
15513     filename.Empty();
15514   }
15515   else
15516   {
15517     filename = m_directory;
15518     filename += L"\\";
15519     filename += m_fd.cFileName;
15520     rc = true;
15521   }
15522 #else
15523 
15524   // gcc implementation
15525   if ( 0 == m_current_name[0] )
15526   {
15527     filename.Empty();
15528   }
15529   else
15530   {
15531     filename = m_directory;
15532     filename += L"/";
15533     filename += m_current_name;
15534     rc = true;
15535   }
15536 
15537 #endif
15538 
15539   return rc;
15540 }
15541 
15542 
15543 #if defined(ON_COMPILER_MSC)
15544 static ON__UINT64 SecondsSinceJanOne1970( FILETIME ft )
15545 {
15546   // The FILETIME is in 100-nanosecond intervals since January 1, 1601 UCT.
15547   //
15548   // Between midnight January 1, 1601 and midnight January 1, 1970 there 
15549   // were 134774 days = 11644473600 seconds. Each second has 10^7 intervals
15550   // that are one hundred nanoseconds long.  So, if N = number of one hundred
15551   // nanosecond intervals since midnight January 1, 1601, then
15552   // (N / 10000000) - 11644473600 = number of seconds since midnight
15553   // January 1, 1970.
15554   //
15555   // January 1, 1601 was the start of a Gregorian calendary 400 year cycle
15556   // and "the internet" sometimes cites that as the reason that date is 
15557   // the "beginning of time" for Windows' FILETIME values.  This convention
15558   // would slightly simplify the formulae used to account for leap years, 
15559   // so it is plausable this might might even be true.
15560 
15561   ON__UINT64 ft_since_jan_1_1601 = ft.dwHighDateTime;
15562   ft_since_jan_1_1601 *= 0xFFFFFFFF;
15563   ft_since_jan_1_1601 += ft.dwLowDateTime;
15564 
15565   ON__UINT64 hundrednanoseconds_per_second = 10000000;
15566 
15567   ON__UINT64 seconds_since_jan_1_1601 = ft_since_jan_1_1601 / hundrednanoseconds_per_second;
15568                                                    
15569   ON__UINT64 seconds_since_jan_1_1970 = seconds_since_jan_1_1601 - 11644473600;
15570 
15571   return seconds_since_jan_1_1970;
15572 }
15573 #endif
15574 
15575 ON__UINT64 ON_FileIterator::CurrentFileCreateTime() const
15576 {
15577 #if defined(ON_COMPILER_MSC)
15578   return SecondsSinceJanOne1970(m_fd.ftCreationTime);
15579 #else
15580   return m_current_file_create_time;
15581 #endif
15582 }
15583 
15584 ON__UINT64 ON_FileIterator::CurrentFileLastModifiedTime() const
15585 {
15586 #if defined(ON_COMPILER_MSC)
15587   return SecondsSinceJanOne1970(m_fd.ftLastWriteTime);
15588 #else
15589   return m_current_last_modified_time;
15590 #endif
15591 }
15592 
15593 ON__UINT64 ON_FileIterator::CurrentFileLastAccessTime() const
15594 {
15595 #if defined(ON_COMPILER_MSC)
15596   return SecondsSinceJanOne1970(m_fd.ftLastAccessTime);
15597 #else
15598   return m_current_last_access_time;
15599 #endif
15600 }
15601 


pcl
Author(s): Open Perception
autogenerated on Wed Aug 26 2015 15:26:59