1 /* $NoKeywords: $ */
2 /*
3 //
4 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
5 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
6 // McNeel & Associates.
7 //
8 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
9 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
10 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
11 //
12 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
13 //
14 ////////////////////////////////////////////////////////////////
15 */
16 
17 #include "opennurbs.h"
18 
Open(const wchar_t * filename,const wchar_t * mode)19 FILE* ON_FileStream::Open( const wchar_t* filename, const wchar_t* mode )
20 {
21   FILE* fp = 0;
22 
23   if ( 0 == filename || 0 == filename[0] || 0 == mode || 0 == mode[0] )
24     return fp;
25 
26 #if defined(ON_OS_WINDOWS)
27   errno_t e = _wfopen_s(&fp,filename,mode);
28   if ( 0 != e && 0 == fp )
29     fp = 0; // reference e to keep lint quiet.
30 #else
31   // I can't find an wfopen() or _wfopen() in
32   // gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)
33   ON_String fnameUTF8(filename);
34   ON_String modeUTF8(mode);
35   fp = fopen(fnameUTF8,modeUTF8);
36 #endif
37 
38   return fp;
39 }
40 
Open(const char * filename,const char * mode)41 FILE* ON_FileStream::Open( const char* filename, const char* mode )
42 {
43   FILE* fp = 0;
44 
45   if ( 0 == filename || 0 == filename[0] || 0 == mode || 0 == mode[0] )
46     return fp;
47 
48 #if defined(ON_OS_WINDOWS)
49   errno_t e = fopen_s(&fp,filename,mode);
50   if ( 0 != e && 0 == fp )
51     fp = 0; // reference e to keep lint quiet.
52 #else
53   fp = fopen(filename,mode);
54 #endif
55 
56   return fp;
57 }
58 
Close(FILE * fp)59 int ON_FileStream::Close( FILE* fp )
60 {
61   return ( ( 0 != fp ) ? fclose(fp) : -1 );
62 }
63 
CurrentPosition(FILE * fp)64 ON__INT64 ON_FileStream::CurrentPosition( FILE* fp )
65 {
66   if ( 0 == fp )
67     return -1;
68 #if defined(ON_OS_WINDOWS)
69   return _ftelli64(fp);
70 #else
71   return ftell(fp);
72 #endif
73 }
74 
SeekFromCurrentPosition(FILE * fp,ON__INT64 offset)75 bool ON_FileStream::SeekFromCurrentPosition( FILE* fp, ON__INT64 offset )
76 {
77   return ON_FileStream::Seek(fp,offset,SEEK_CUR);
78 }
79 
SeekFromStart(FILE * fp,ON__INT64 offset)80 bool ON_FileStream::SeekFromStart( FILE* fp, ON__INT64 offset )
81 {
82   return ON_FileStream::Seek(fp,offset,SEEK_SET);
83 }
84 
SeekFromEnd(FILE * fp,ON__INT64 offset)85 bool ON_FileStream::SeekFromEnd( FILE* fp, ON__INT64 offset )
86 {
87   return ON_FileStream::Seek(fp,offset,SEEK_END);
88 }
89 
Seek(FILE * fp,ON__INT64 offset,int origin)90 bool ON_FileStream::Seek( FILE* fp, ON__INT64 offset, int origin )
91 {
92   if ( 0 == fp )
93     return false;
94 
95   if ( origin < 0 || origin > 2 )
96     return false;
97 
98   if ( 0 == offset )
99     return true;
100 
101 #if defined(ON_OS_WINDOWS)
102 
103   if ( 0 != _fseeki64(fp,offset,origin) )
104     return false;
105 
106 #else
107 
108   const int i = 2147483646;
109   const ON__INT64 i64 = i;
110   while ( offset > i64 )
111   {
112     if ( 0 != fseek( fp, i, origin ) )
113       return false;
114     offset -= i64;
115   }
116   while ( offset < -i64 )
117   {
118     if ( 0 != fseek( fp, -i, origin ) )
119       return false;
120     offset += i64;
121   }
122   if ( 0 != offset )
123   {
124     int ioffset = (int)offset;
125     if ( 0 != fseek( fp, ioffset, origin ) )
126       return false;
127   }
128 
129 #endif
130 
131   return true;
132 }
133 
Read(FILE * fp,ON__UINT64 count,void * buffer)134 ON__UINT64 ON_FileStream::Read( FILE* fp, ON__UINT64 count, void* buffer )
135 {
136   ON__UINT64 rc = 0;
137   if ( 0 == fp || count <= 0 || 0 == buffer )
138     return rc;
139 
140   if ( count <= ON_MAX_SIZE_T )
141   {
142     rc = (ON__UINT64)fread(buffer,1,(size_t)count,fp);
143   }
144   else
145   {
146     size_t sz, szread;
147     while ( count > 0 )
148     {
149       sz = ( count > ON_MAX_SIZE_T ) ? ON_MAX_SIZE_T : ((size_t)count);
150       szread = fread(buffer,1,sz,fp);
151       rc += szread;
152       if ( szread != sz )
153         break;
154       count -= sz;
155       buffer = ((unsigned char*)buffer) + sz;
156     }
157   }
158 
159  return rc;
160 }
161 
Write(FILE * fp,ON__UINT64 count,const void * buffer)162 ON__UINT64 ON_FileStream::Write( FILE* fp, ON__UINT64 count, const void* buffer )
163 {
164   ON__UINT64 rc = 0;
165   if ( 0 == fp || count <= 0 || 0 == buffer )
166     return rc;
167 
168   if ( count <= ON_MAX_SIZE_T )
169   {
170     rc = fwrite(buffer,1,(size_t)count,fp);
171   }
172   else
173   {
174     size_t sz, szwrite;
175     while ( count > 0 )
176     {
177       sz = ( count > ON_MAX_SIZE_T ) ? ON_MAX_SIZE_T : ((size_t)count);
178       szwrite = fwrite(buffer,1,sz,fp);
179       rc += szwrite;
180       if ( szwrite != sz )
181         break;
182       count -= sz;
183       buffer = ((unsigned char*)buffer) + sz;
184     }
185   }
186 
187  return rc;
188 }
189 
Flush(FILE * fp)190 bool ON_FileStream::Flush( FILE* fp )
191 {
192   if ( 0 == fp )
193     return false;
194   if ( 0 != fflush(fp) )
195     return false;
196   return true;
197 }
198 
199 
GetFileInformation(FILE * fp,ON__UINT64 * file_size,ON__UINT64 * file_create_time,ON__UINT64 * file_last_modified_time)200 bool ON_FileStream::GetFileInformation(
201     FILE* fp,
202     ON__UINT64* file_size,
203     ON__UINT64* file_create_time,
204     ON__UINT64* file_last_modified_time
205     )
206 {
207   bool rc = false;
208 
209   if (file_size)
210     *file_size = 0;
211   if (file_create_time)
212     *file_create_time = 0;
213   if (file_last_modified_time)
214     *file_last_modified_time = 0;
215 
216   if ( fp )
217   {
218 
219 #if defined(ON_COMPILER_MSC)
220 
221     // Microsoft compilers
222     int fd = _fileno(fp);
223 #if (_MSC_VER >= 1400)
224     // VC 8 (2005)
225     // works for file sizes > 4GB
226     // when size_t is a 64 bit integer
227     struct _stat64 sb;
228     memset(&sb,0,sizeof(sb));
229     int fstat_rc = _fstat64(fd, &sb);
230 #else
231     // VC6 compiler
232     // works on most compilers
233     struct _stat sb;
234     memset(&sb,0,sizeof(sb));
235     int fstat_rc = _fstat(fd, &sb);
236 #endif
237 
238 #else
239     // works on most compilers
240     int fd = fileno(fp);
241     struct stat sb;
242     memset(&sb,0,sizeof(sb));
243     int fstat_rc = fstat(fd, &sb);
244 #endif
245 
246 
247     if (0 == fstat_rc)
248     {
249       if (file_size)
250         *file_size = (ON__UINT64)sb.st_size;
251       if (file_create_time)
252         *file_create_time = (ON__UINT64)sb.st_ctime;
253       if (file_last_modified_time)
254         *file_last_modified_time = (ON__UINT64)sb.st_mtime;
255       rc = true;
256     }
257   }
258 
259   return rc;
260 }
261 
262 
263 class ON_ReadChunkHelper
264 {
265 public:
266   ON_ReadChunkHelper(ON_BinaryArchive&);
267   ~ON_ReadChunkHelper();
268 
269   ON_BinaryArchive& m_binary_archive;
270   bool m_bReadSuccess;
271   ON__UINT32 m_chunk_tcode;
272   ON__INT64 m_chunk_value;
273 
274 private:
275   bool m_bCallEndRead3dmChunk;
276   // prohibit use - no implementation
277   ON_ReadChunkHelper();
278   ON_ReadChunkHelper(const ON_ReadChunkHelper&);
279   ON_ReadChunkHelper& operator=(const ON_ReadChunkHelper&);
280 };
281 
ON_ReadChunkHelper(ON_BinaryArchive & binary_archive)282 ON_ReadChunkHelper::ON_ReadChunkHelper(ON_BinaryArchive& binary_archive)
283 : m_binary_archive(binary_archive)
284 , m_bReadSuccess(0)
285 , m_chunk_tcode(0)
286 , m_chunk_value(0)
287 , m_bCallEndRead3dmChunk(0)
288 {
289   m_bReadSuccess = m_binary_archive.BeginRead3dmBigChunk(&m_chunk_tcode,&m_chunk_value);
290   if ( m_bReadSuccess )
291     m_bCallEndRead3dmChunk = true;
292 }
293 
~ON_ReadChunkHelper()294 ON_ReadChunkHelper::~ON_ReadChunkHelper()
295 {
296   if ( m_bReadSuccess && !m_binary_archive.EndRead3dmChunk() )
297     m_bReadSuccess = false;
298 }
299 
ON_IsUnsignedChunkTypecode(ON__UINT32 typecode)300 bool ON_IsUnsignedChunkTypecode( ON__UINT32 typecode )
301 {
302   // returns tru if the chunk value should be treated as an unsigned int.
303   return ( 0 == (TCODE_SHORT & typecode)
304            || TCODE_RGB == typecode
305            || TCODE_RGBDISPLAY == typecode
306            || TCODE_PROPERTIES_OPENNURBS_VERSION == typecode
307            || TCODE_OBJECT_RECORD_TYPE == typecode
308          );
309 }
310 
ON_IsLongChunkTypecode(ON__UINT32 typecode)311 bool ON_IsLongChunkTypecode(ON__UINT32 typecode)
312 {
313   // NOTE: RenderXXXX plug-in used zero as a typecode in material userdata, sigh ...
314   //return (0 != typecode && 0 == (TCODE_SHORT & typecode));
315   return (0 == (TCODE_SHORT & typecode));
316 }
317 
ON_IsShorChunkTypecode(ON__UINT32 typecode)318 bool ON_IsShorChunkTypecode(ON__UINT32 typecode)
319 {
320   return (0 != (TCODE_SHORT & typecode));
321 }
322 
323 static
DownSizeINT(ON__INT64 i64,ON__INT32 * i32)324 bool DownSizeINT( ON__INT64 i64, ON__INT32* i32 )
325 {
326   const static ON__INT64 i32max = 2147483647;
327   if ( i64 <= i32max && i64 >= (-i32max - 1) )
328   {
329     *i32 = (ON__INT32)i64;
330     return true;
331   }
332 
333   ON_ERROR("i64 too big to convert to 4 byte signed int");
334   *i32 = 0;
335   return false;
336 }
337 
338 static
DownSizeUINT(ON__UINT64 u64,ON__UINT32 * u32)339 bool DownSizeUINT( ON__UINT64 u64, ON__UINT32* u32 )
340 {
341   if ( u64 <= 0xFFFFFFFF )
342   {
343     *u32 = (ON__UINT32)u64;
344     return true;
345   }
346 
347   ON_ERROR("u64 too big to convert to 4 byte unsigned int");
348   *u32 = 0;
349   return false;
350 }
351 
352 static
DownSizeChunkValue(ON__UINT32 typecode,ON__INT64 v64,ON__INT32 * v32)353 bool DownSizeChunkValue( ON__UINT32 typecode, ON__INT64 v64, ON__INT32* v32 )
354 {
355   if ( 0 == v32 )
356     return true;
357   return ( ON_IsLongChunkTypecode(typecode) )
358          ? DownSizeUINT( (ON__UINT64)v64, (ON__UINT32*)v32 )
359          : DownSizeINT( v64, v32 );
360 }
361 
362 
363 
364 struct ON__3dmV1LayerIndex
365 {
366   int m_layer_index;
367   int m_layer_name_length;
368   char* m_layer_name;
369   struct ON__3dmV1LayerIndex* m_next;
370 };
371 
ON_BinaryArchive(ON::archive_mode mode)372 ON_BinaryArchive::ON_BinaryArchive( ON::archive_mode mode )
373                  : m_3dm_version(0),
374                    m_3dm_v1_layer_index(0), m_3dm_v1_material_index(0),
375                    m_error_message_mask(0),
376                    m_3dm_opennurbs_version(0),
377                    m_3dm_start_section_offset(0),
378                    m_active_table(ON_BinaryArchive::no_active_table),
379                    m_bDoChunkCRC(0), m_bad_CRC_count(0),
380                    m_endian(ON::Endian()),
381                    m_mode(mode)
382 {
383   // Sparc, MIPS, ... CPUs have big endian byte order
384   // ON_BinaryArchives use little endian byte order
385 
386   m_bSaveUserData = true; // true to save user data (increases file size)
387   m_bSavePreviewImage    = false; // true to save 200x200 preview bitmap (increases file size)
388   m_bEmbedTextureBitmaps = false; // true to embed texture, bump, trace, and wallpaper bitmaps (increases file size)
389   m_bSaveRenderMeshes    = false; // true to save meshes used to render B-rep objects (increases file size)
390   m_bSaveAnalysisMeshes  = false; // true to save meshes used in surface analysis (increases file size)
391 
392   m_zlib.mode = ON::unknown_archive_mode;
393   memset( &m_zlib.strm, 0, sizeof(m_zlib.strm) );
394 
395   m_V1_layer_list = 0;
396 }
397 
~ON_BinaryArchive()398 ON_BinaryArchive::~ON_BinaryArchive()
399 {
400   if ( 0 != m_V1_layer_list )
401   {
402     struct ON__3dmV1LayerIndex* next = m_V1_layer_list;
403     m_V1_layer_list = 0;
404     for ( int i = 0; 0 != next && i < 1000; i++ )
405     {
406       struct ON__3dmV1LayerIndex* p = next;
407       next = p->m_next;
408       onfree(p);
409     }
410   }
411 
412   CompressionEnd();
413 }
414 
415 
ToggleByteOrder(int count,int sizeof_element,const void * src,void * dst)416 bool ON_BinaryArchive::ToggleByteOrder(
417   int count,          // number of elements
418   int sizeof_element, // size of element (2,4, or 8)
419   const void* src,    // source buffer
420   void* dst           // destination buffer (can be same as source buffer)
421   )
422 {
423   unsigned char c[32];
424   const unsigned char* a = (const unsigned char*)src;
425   unsigned char* b = (unsigned char*)dst;
426 
427   bool rc = (count==0 || (count>0&&src&&dst));
428   if ( rc )
429   {
430     // loops are unrolled and a switch is used
431     // to speed things up a bit.
432     switch(sizeof_element)
433     {
434     case 2:
435       while(count--)
436       {
437         c[0] = *a++;
438         c[1] = *a++;
439         *b++ = c[1];
440         *b++ = c[0];
441       }
442       break;
443 
444     case 4:
445       while(count--)
446       {
447         c[0] = *a++;
448         c[1] = *a++;
449         c[2] = *a++;
450         c[3] = *a++;
451         *b++ = c[3];
452         *b++ = c[2];
453         *b++ = c[1];
454         *b++ = c[0];
455       }
456       break;
457 
458     case 8:
459       while(count--)
460       {
461         c[0] = *a++;
462         c[1] = *a++;
463         c[2] = *a++;
464         c[3] = *a++;
465         c[4] = *a++;
466         c[5] = *a++;
467         c[6] = *a++;
468         c[7] = *a++;
469         *b++ = c[7];
470         *b++ = c[6];
471         *b++ = c[5];
472         *b++ = c[4];
473         *b++ = c[3];
474         *b++ = c[2];
475         *b++ = c[1];
476         *b++ = c[0];
477       }
478       break;
479 
480     default:
481       if ( sizeof_element > 0 && sizeof_element < 32 )
482       {
483         // As of 2 May 2003, this case is never used
484         // by core opennurbs objects.
485         //
486         // This is here so that future code will work
487         // if and when 128 bit "ints"/"doubles" become common
488         // enough that they can be stored in 3dm files.
489         // It may also happen that third party applications
490         // on specialized CPUs need to toggle byte order
491         // for 128 bit ints/doubles stored as user data.
492         int i;
493         while(count--)
494         {
495           for (i = 0; i < sizeof_element; i++)
496             c[i] = *a++;
497           while(i--)
498             *b++ = c[i];
499         }
500       }
501       else
502       {
503         rc = false;
504       }
505       break;
506     }
507   }
508   return rc;
509 }
510 
BigSeekFromStart(ON__UINT64 offset)511 bool ON_BinaryArchive::BigSeekFromStart( ON__UINT64 offset )
512 {
513   // SeekFromStart() is a virutal function that
514   // any developer can implement.  Some implementations
515   // may use signed 4 byte int in critical places.
516   // BigSeekFromStart() will work correctly in
517   // this worst case situation.
518   return ( offset > 2147483632 )
519     ? ( SeekFromStart(2147483632) && BigSeekForward(offset - 2147483632) )
520     : SeekFromStart((size_t)offset);
521 }
522 
523 
524 
BigSeekForward(ON__UINT64 offset)525 bool ON_BinaryArchive::BigSeekForward( ON__UINT64 offset )
526 {
527   // SeekFromCurrentPosition() is a virutal function that
528   // uses a signed 4 byte int in critical places.
529   // BigSeekForward() will work correctly when
530   // offset is larger than the maximum value of a signed
531   // 4 byte int.
532   while ( offset > 2147483632 )
533   {
534     if ( !SeekFromCurrentPosition(2147483632) )
535       return false;
536     offset -= 2147483632;
537   }
538   if ( offset > 0 )
539   {
540     int ioffset32 = (int)((ON__INT64)offset);
541     return SeekFromCurrentPosition(ioffset32);
542   }
543   return true;
544 }
545 
546 
BigSeekBackward(ON__UINT64 offset)547 bool ON_BinaryArchive::BigSeekBackward( ON__UINT64 offset )
548 {
549   // SeekFromCurrentPosition() is a virutal function that
550   // uses a signed 4 byte int in critical places.
551   // BigSeekBackward() will work correctly when
552   // offset is larger than the maximum value of a signed
553   // 4 byte int.
554   while ( offset > 2147483632 )
555   {
556     if ( !SeekFromCurrentPosition(-2147483632) )
557       return false;
558     offset -= 2147483632;
559   }
560   if ( offset > 0 )
561   {
562     int ioffset32 = (int)((ON__INT64)offset);
563     return SeekFromCurrentPosition(-ioffset32);
564   }
565   return true;
566 }
567 
568 
BigSeekFromCurrentPosition(ON__INT64 offset)569 bool ON_BinaryArchive::BigSeekFromCurrentPosition( ON__INT64 offset )
570 {
571   // SeekFromCurrentPosition() is a virutal function that
572   // uses a signed 4 byte int in critical places.
573   // BigSeekFromCurrentPosition() will work correctly when
574   // offset is smaller than the minimum value of a signed
575   // 4 byte int or the maximum value of a signed 4 byte int.
576   return ( offset >= 0 )
577          ? BigSeekForward((ON__UINT64)offset)
578          : BigSeekBackward((ON__UINT64)(-offset));
579 }
580 
581 bool
ReadMode() const582 ON_BinaryArchive::ReadMode() const
583 {
584   return (m_mode & ON::read) ? true : false;
585 }
586 
587 bool
WriteMode() const588 ON_BinaryArchive::WriteMode() const
589 {
590   return (m_mode & ON::write) ? true : false;
591 }
592 
593 bool
ReadChar(size_t count,char * p)594 ON_BinaryArchive::ReadChar(    // Read an array of 8 bit chars
595 		size_t count,       // number of chars to read
596 		char*  p
597 		)
598 {
599   return ReadByte( count, p );
600 }
601 
602 bool
ReadChar(size_t count,unsigned char * p)603 ON_BinaryArchive::ReadChar(    // Read an array of 8 bit unsigned chars
604 		size_t count,       // number of unsigned chars to read
605 		unsigned char* p
606 		)
607 {
608   return ReadByte( count, p );
609 }
610 
611 bool
ReadChar(char * p)612 ON_BinaryArchive::ReadChar(    // Read a single 8 bit char
613 		char* p
614 		)
615 {
616   return ReadByte( 1, p );
617 }
618 
619 bool
ReadChar(unsigned char * p)620 ON_BinaryArchive::ReadChar(    // Read a single 8 bit unsigned char
621 		unsigned char* p
622 		)
623 {
624   return ReadByte( 1, p );
625 }
626 
627 bool
ReadInt16(size_t count,ON__INT16 * p)628 ON_BinaryArchive::ReadInt16( // Read an array of 16 bit integers
629 		size_t count,            // number of unsigned integers to read
630 		ON__INT16* p
631 		)
632 {
633   bool rc = ReadByte( count<<1, p );
634   if ( rc && m_endian == ON::big_endian )
635   {
636     // reverse byte order
637 		unsigned char* b= (unsigned char*) (p);
638 		unsigned char  c;
639 		while(count--) {
640 			c = b[0]; b[0] = b[1]; b[1] = c;
641 			b += 2;
642 		}
643   }
644   return rc;
645 }
646 
647 bool
ReadShort(size_t count,short * p)648 ON_BinaryArchive::ReadShort(   // Read an array of 16 bit shorts
649 		size_t count,       // number of unsigned chars to read
650 		short* p
651 		)
652 {
653 #if defined(ON_COMPILER_MSC)
654 #pragma warning( push )
655 // Disable the MSC /W4 "conditional expression is constant" warning
656 // about 2 == sizeof(*p).  Since this code has to run on machines
657 // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary.
658 #pragma warning( disable : 4127 )
659 #endif
660 
661   bool rc = true;
662 
663   if ( 2 == sizeof(*p) )
664   {
665     rc = ReadInt16( count, (ON__INT16*)p );
666   }
667   else
668   {
669     size_t j;
670     ON__INT16 i16;
671     for ( j = 0; j < count && rc; j++ )
672     {
673       rc = ReadInt16( 1, &i16 );
674       *p++ = (short)i16;
675     }
676   }
677   return rc;
678 
679 #if defined(ON_COMPILER_MSC)
680 #pragma warning( pop )
681 #endif
682 }
683 
684 bool
ReadShort(size_t count,unsigned short * p)685 ON_BinaryArchive::ReadShort(   // Read an array of 16 bit unsigned shorts
686 		size_t count,       // number of unsigned chars to read
687 		unsigned short* p
688 		)
689 {
690   return ReadShort( count, (short*)p );
691 }
692 
693 bool
ReadShort(short * p)694 ON_BinaryArchive::ReadShort(   // Read a single 16 bit short
695 		short* p
696 		)
697 {
698   return ReadShort( 1, p );
699 }
700 
701 bool
ReadShort(unsigned short * p)702 ON_BinaryArchive::ReadShort(   // Read a single 16 bit unsigned short
703 		unsigned short* p
704 		)
705 {
706   return ReadShort( 1, p );
707 }
708 
709 bool
ReadInt32(size_t count,ON__INT32 * p)710 ON_BinaryArchive::ReadInt32( // Read an array of 32 bit integers
711 		size_t count,            // number of 32 bit integers to read
712 		ON__INT32* p
713 		)
714 {
715   bool rc = ReadByte( count<<2, p );
716   if ( rc && m_endian == ON::big_endian )
717   {
718 		unsigned char* b= (unsigned char*)p;
719 		unsigned char  c;
720 		while(count--) {
721 			c = b[0]; b[0] = b[3]; b[3] = c;
722 			c = b[1]; b[1] = b[2]; b[2] = c;
723 			b += 4;
724 		}
725   }
726   return rc;
727 }
728 
729 bool
ReadInt(size_t count,int * p)730 ON_BinaryArchive::ReadInt( // Read an array of integers
731 		size_t count,          // number of unsigned chars to read
732 		int* p
733 		)
734 {
735 #if defined(ON_COMPILER_MSC)
736 #pragma warning( push )
737 // Disable the MSC /W4 "conditional expression is constant" warning
738 // about 4 == sizeof(*p).  Since this code has to run on machines
739 // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary.
740 #pragma warning( disable : 4127 )
741 #endif
742 
743   bool rc;
744   if ( 4 == sizeof(*p) )
745   {
746     rc = ReadInt32( count, (ON__INT32*)p );
747   }
748   else
749   {
750     rc = true;
751     ON__INT32 i32;
752     size_t j;
753     for ( j = 0; j < count && rc; j++ )
754     {
755       rc = ReadInt32(1,&i32);
756       if (rc)
757         *p++ = (int)i32;
758     }
759   }
760   return rc;
761 
762 #if defined(ON_COMPILER_MSC)
763 #pragma warning( pop )
764 #endif
765 }
766 
767 bool
ReadInt(size_t count,unsigned int * p)768 ON_BinaryArchive::ReadInt( // Read an array of 32 bit integers
769 		size_t count,       // number of unsigned chars to read
770 		unsigned int* p
771 		)
772 {
773   return ReadInt( count, (int*)p );
774 }
775 
776 bool
ReadInt(int * p)777 ON_BinaryArchive::ReadInt( // Read a single 32 bit integer
778 		int* p
779 		)
780 {
781   return ReadInt( 1, p );
782 }
783 
784 bool
ReadInt(unsigned int * p)785 ON_BinaryArchive::ReadInt( // Read a single 32 bit unsigned integer
786 		unsigned int* p
787 		)
788 {
789   return ReadInt( 1, p );
790 }
791 
ReadBigInt(size_t count,ON__INT64 * p)792 bool ON_BinaryArchive::ReadBigInt( // Read an array of 64 bit integers
793 		size_t count,
794 		ON__INT64* p
795 		)
796 {
797   return ReadInt64(1,p);
798 }
799 
ReadBigInt(size_t count,ON__UINT64 * p)800 bool ON_BinaryArchive::ReadBigInt( // Read an array of 64 bit integers
801 		size_t count,
802 		ON__UINT64* p
803 		)
804 {
805   return ReadInt64(1,(ON__INT64*)p);
806 }
807 
ReadBigInt(ON__INT64 * p)808 bool ON_BinaryArchive::ReadBigInt( // Read a single 64 bit integer
809 		ON__INT64* p
810 		)
811 {
812   return ReadInt64(1,p);
813 }
814 
ReadBigInt(ON__UINT64 * p)815 bool ON_BinaryArchive::ReadBigInt( // Read a single 64 bit unsigned integer
816 		ON__UINT64* p
817 		)
818 {
819   return ReadInt64(1,(ON__INT64*)p);
820 }
821 
822 
823 
824 bool
ReadLong(size_t count,long * p)825 ON_BinaryArchive::ReadLong( // Read an array of 32 bit integers
826 		size_t count,       // number of unsigned chars to read
827 		long* p
828 		)
829 {
830 #if defined(ON_COMPILER_MSC)
831 #pragma warning( push )
832 // Disable the MSC /W4 "conditional expression is constant" warning
833 // about 4 == sizeof(*p).  Since this code has to run on machines
834 // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary.
835 #pragma warning( disable : 4127 )
836 #endif
837 
838   bool rc;
839   if ( 4 == sizeof(*p) )
840   {
841     rc = ReadInt32( count, (ON__INT32*)p );
842   }
843   else
844   {
845     rc = true;
846     ON__INT32 i32;
847     size_t j;
848     for ( j = 0; j < count && rc; j++ )
849     {
850       rc = ReadInt32(1,&i32);
851       if (rc)
852         *p++ = (long)i32;
853     }
854   }
855   return rc;
856 
857 #if defined(ON_COMPILER_MSC)
858 #pragma warning( pop )
859 #endif
860 }
861 
862 bool
ReadLong(size_t count,unsigned long * p)863 ON_BinaryArchive::ReadLong( // Read an array of 32 bit integers
864 		size_t count,       // number of unsigned chars to read
865 		unsigned long* p
866 		)
867 {
868   return ReadLong( count, (long*)p );
869 }
870 
871 bool
ReadLong(long * p)872 ON_BinaryArchive::ReadLong( // Read a single 32 bit integer
873 		long* p
874 		)
875 {
876   return ReadLong( 1, (long*)p );
877 }
878 
879 bool
ReadLong(unsigned long * p)880 ON_BinaryArchive::ReadLong( // Read a single 32 bit unsigned integer
881 		unsigned long* p
882 		)
883 {
884   return ReadLong( 1, (long*)p );
885 }
886 
887 bool
ReadFloat(size_t count,float * p)888 ON_BinaryArchive::ReadFloat(   // Read an array of floats
889 		size_t count,       // number of unsigned chars to read
890 		float* p
891 		)
892 {
893   // 32 bit floats and 32 bit integers have same size and endian issues
894   return ReadInt32( count, (ON__INT32*)p );
895 }
896 
897 bool
ReadFloat(float * p)898 ON_BinaryArchive::ReadFloat(   // Read a single float
899 		float* p
900 		)
901 {
902   return ReadFloat( 1, p );
903 }
904 
905 bool
ReadDouble(size_t count,double * p)906 ON_BinaryArchive::ReadDouble(  // Read an array of IEEE 64 bit doubles
907 		size_t count,       // number of unsigned chars to read
908 		double* p
909 		)
910 {
911   bool rc = ReadByte( count<<3, p );
912   if ( rc && m_endian == ON::big_endian )
913   {
914 		unsigned char* b=(unsigned char*)p;
915 		unsigned char  c;
916 		while(count--) {
917 			c = b[0]; b[0] = b[7]; b[7] = c;
918 			c = b[1]; b[1] = b[6]; b[6] = c;
919 			c = b[2]; b[2] = b[5]; b[5] = c;
920 			c = b[3]; b[3] = b[4]; b[4] = c;
921 			b += 8;
922 		}
923   }
924   return rc;
925 }
926 
927 bool
ReadDouble(double * p)928 ON_BinaryArchive::ReadDouble(  // Read a single double
929 		double* p
930 		)
931 {
932   return ReadDouble( 1, p );
933 }
934 
935 bool
ReadColor(ON_Color & color)936 ON_BinaryArchive::ReadColor( ON_Color& color )
937 {
938   unsigned int colorref = 0;
939   bool rc = ReadInt( &colorref );
940   color = colorref;
941   return rc;
942 }
943 
944 bool
ReadPoint(ON_2dPoint & p)945 ON_BinaryArchive::ReadPoint (
946   ON_2dPoint& p
947   )
948 {
949   return ReadDouble( 2, &p.x );
950 }
951 
952 bool
ReadPoint(ON_3dPoint & p)953 ON_BinaryArchive::ReadPoint (
954   ON_3dPoint& p
955   )
956 {
957   return ReadDouble( 3, &p.x );
958 }
959 
960 bool
ReadPoint(ON_4dPoint & p)961 ON_BinaryArchive::ReadPoint (
962   ON_4dPoint& p
963   )
964 {
965   return ReadDouble( 4, &p.x );
966 }
967 
968 bool
ReadVector(ON_2dVector & v)969 ON_BinaryArchive::ReadVector (
970   ON_2dVector& v
971   )
972 {
973   return ReadDouble( 2, &v.x );
974 }
975 
976 bool
ReadVector(ON_3dVector & v)977 ON_BinaryArchive::ReadVector (
978   ON_3dVector& v
979   )
980 {
981   return ReadDouble( 3, &v.x );
982 }
983 
WriteBoundingBox(const ON_BoundingBox & bbox)984 bool ON_BinaryArchive::WriteBoundingBox(const ON_BoundingBox& bbox)
985 {
986   bool rc = WritePoint( bbox.m_min );
987   if (rc) rc = WritePoint( bbox.m_max );
988   return rc;
989 }
990 
ReadBoundingBox(ON_BoundingBox & bbox)991 bool ON_BinaryArchive::ReadBoundingBox(ON_BoundingBox& bbox)
992 {
993   bool rc = ReadPoint( bbox.m_min );
994   if (rc) rc = ReadPoint( bbox.m_max );
995   return rc;
996 }
997 
998 bool
WriteXform(const ON_Xform & x)999 ON_BinaryArchive::WriteXform( const ON_Xform& x )
1000 {
1001   return WriteDouble( 16, &x.m_xform[0][0] );
1002 }
1003 
1004 bool
ReadXform(ON_Xform & x)1005 ON_BinaryArchive::ReadXform( ON_Xform& x )
1006 {
1007   return ReadDouble( 16, &x.m_xform[0][0] );
1008 }
1009 bool
WritePlaneEquation(const ON_PlaneEquation & plane_equation)1010 ON_BinaryArchive::WritePlaneEquation( const ON_PlaneEquation& plane_equation )
1011 {
1012   bool rc = WriteDouble( 4, &plane_equation.x );
1013   return rc;
1014 }
1015 
1016 bool
ReadPlaneEquation(ON_PlaneEquation & plane_equation)1017 ON_BinaryArchive::ReadPlaneEquation( ON_PlaneEquation& plane_equation )
1018 {
1019   bool rc = ReadDouble( 4, &plane_equation.x );
1020   return rc;
1021 }
1022 
1023 bool
WritePlane(const ON_Plane & plane)1024 ON_BinaryArchive::WritePlane( const ON_Plane& plane )
1025 {
1026   bool rc = WritePoint( plane.origin );
1027   if (rc) rc = WriteVector( plane.xaxis );
1028   if (rc) rc = WriteVector( plane.yaxis );
1029   if (rc) rc = WriteVector( plane.zaxis );
1030   if (rc) rc = WriteDouble( 4, &plane.plane_equation.x );
1031   return rc;
1032 }
1033 
1034 bool
ReadPlane(ON_Plane & plane)1035 ON_BinaryArchive::ReadPlane( ON_Plane& plane )
1036 {
1037   bool rc = ReadPoint( plane.origin );
1038   if (rc) rc = ReadVector( plane.xaxis );
1039   if (rc) rc = ReadVector( plane.yaxis );
1040   if (rc) rc = ReadVector( plane.zaxis );
1041   if (rc) rc = ReadDouble( 4, &plane.plane_equation.x );
1042   return rc;
1043 }
1044 
1045 bool
WriteLine(const ON_Line & line)1046 ON_BinaryArchive::WriteLine( const ON_Line& line )
1047 {
1048   bool rc = WritePoint( line.from );
1049   if (rc) rc = WritePoint( line.to );
1050   return rc;
1051 }
1052 
1053 bool
ReadLine(ON_Line & line)1054 ON_BinaryArchive::ReadLine( ON_Line& line )
1055 {
1056   bool rc = ReadPoint( line.from );
1057   if (rc) rc = ReadPoint( line.to );
1058   return rc;
1059 }
1060 
1061 bool
WriteArc(const ON_Arc & arc)1062 ON_BinaryArchive::WriteArc(const ON_Arc& arc )
1063 {
1064   bool rc = WriteCircle(arc);
1065   if (rc)
1066     rc = WriteInterval(arc.m_angle);
1067   return rc;
1068 }
1069 
1070 bool
ReadArc(ON_Arc & arc)1071 ON_BinaryArchive::ReadArc( ON_Arc& arc )
1072 {
1073   bool rc = ReadCircle(arc);
1074   if (rc)
1075     rc = ReadInterval(arc.m_angle);
1076   return rc;
1077 }
1078 
1079 bool
WriteCircle(const ON_Circle & circle)1080 ON_BinaryArchive::WriteCircle(const ON_Circle& circle)
1081 {
1082   bool rc = WritePlane( circle.plane );
1083   if (rc)
1084     rc = WriteDouble( circle.radius );
1085   // m_point[] removed 2001, November, 7
1086   if (rc)
1087     rc = WritePoint( circle.PointAt(0.0) );
1088   if (rc)
1089     rc = WritePoint( circle.PointAt(0.5*ON_PI) );
1090   if (rc)
1091     rc = WritePoint( circle.PointAt(ON_PI) );
1092   /*
1093   if (rc)
1094     rc = WritePoint( circle.m_point[0] );
1095   if (rc)
1096     rc = WritePoint( circle.m_point[1] );
1097   if (rc)
1098     rc = WritePoint( circle.m_point[2] );
1099   */
1100   return rc;
1101 }
1102 
1103 bool
ReadCircle(ON_Circle & circle)1104 ON_BinaryArchive::ReadCircle(ON_Circle& circle)
1105 {
1106   ON_3dPoint scratch;
1107   bool rc = ReadPlane( circle.plane );
1108   if (rc)
1109     rc = ReadDouble( &circle.radius );
1110   // m_point[] removed 2001, November, 7
1111   if (rc)
1112     rc = ReadPoint( scratch );
1113   if (rc)
1114     rc = ReadPoint( scratch );
1115   if (rc)
1116     rc = ReadPoint( scratch );
1117   /*
1118   if (rc)
1119     rc = ReadPoint( circle.m_point[0] );
1120   if (rc)
1121     rc = ReadPoint( circle.m_point[1] );
1122   if (rc)
1123     rc = ReadPoint( circle.m_point[2] );
1124   */
1125   return rc;
1126 }
1127 
1128 
1129 bool
WriteInterval(const ON_Interval & t)1130 ON_BinaryArchive::WriteInterval( const ON_Interval& t )
1131 {
1132   return WriteDouble( 2, t.m_t );
1133 }
1134 
1135 bool
ReadInterval(ON_Interval & t)1136 ON_BinaryArchive::ReadInterval( ON_Interval& t )
1137 {
1138   return ReadDouble( 2, t.m_t );
1139 }
1140 
1141 bool
ReadUuid(ON_UUID & uuid)1142 ON_BinaryArchive::ReadUuid( ON_UUID& uuid )
1143 {
1144   bool    rc = ReadInt32( 1, (ON__INT32*)(&uuid.Data1) );
1145   if (rc) rc = ReadInt16( 1, (ON__INT16*)(&uuid.Data2) );
1146   if (rc) rc = ReadInt16( 1, (ON__INT16*)(&uuid.Data3) );
1147   if (rc) rc = ReadByte( 8, uuid.Data4 );
1148   return rc;
1149 }
1150 
ReadDisplayMaterialRef(ON_DisplayMaterialRef & dmr)1151 bool ON_BinaryArchive::ReadDisplayMaterialRef( ON_DisplayMaterialRef& dmr )
1152 {
1153   bool rc = ReadUuid( dmr.m_viewport_id );
1154   if (rc)
1155     rc = ReadUuid( dmr.m_display_material_id );
1156   return rc;
1157 }
1158 
1159 bool
ReadTime(struct tm & utc)1160 ON_BinaryArchive::ReadTime( struct tm& utc )
1161 {
1162   // utc = coordinated universal time ( a.k.a GMT, UTC )
1163   // (From ANSI C time() and gmtime().)
1164   bool rc = ReadInt( &utc.tm_sec );
1165   if ( rc )
1166     rc = ReadInt( &utc.tm_min );
1167   if ( rc )
1168     rc = ReadInt( &utc.tm_hour );
1169   if ( rc )
1170     rc = ReadInt( &utc.tm_mday );
1171   if ( rc )
1172     rc = ReadInt( &utc.tm_mon );
1173   if ( rc )
1174     rc = ReadInt( &utc.tm_year );
1175   if ( rc )
1176     rc = ReadInt( &utc.tm_wday );
1177   if ( rc )
1178     rc = ReadInt( &utc.tm_yday );
1179   if ( rc ) {
1180     if ( utc.tm_sec < 0 || utc.tm_sec > 60 )
1181       rc = false;
1182     if ( utc.tm_min < 0 || utc.tm_min > 60 )
1183       rc = false;
1184     if ( utc.tm_hour < 0 || utc.tm_hour > 24 )
1185       rc = false;
1186     if ( utc.tm_mday < 0 || utc.tm_mday > 31 )
1187       rc = false;
1188     if ( utc.tm_mon < 0 || utc.tm_mon > 12 )
1189       rc = false;
1190     // no year restrictions because dates are used in archeological userdata
1191     if ( utc.tm_wday < 0 || utc.tm_wday > 7 )
1192       rc = false;
1193     if ( utc.tm_yday < 0 || utc.tm_yday > 366 )
1194       rc = false;
1195     if ( !rc ) {
1196       ON_ERROR("ON_BinaryArchive::ReadTime() - bad time in archive");
1197     }
1198   }
1199   return rc;
1200 }
1201 
1202 bool
ReadStringSize(size_t * sizeof_string)1203 ON_BinaryArchive::ReadStringSize( // Read size of NULL terminated string
1204     size_t* sizeof_string          // (returned size includes NULL terminator)
1205     )
1206 {
1207   ON__UINT32 ui32 = 0;
1208   bool rc = ReadInt32(1,(ON__INT32*)&ui32);
1209   // Note that ui32 = number of elements in the string array, including
1210   // the null terminator.  So ui32 should either be 0 or be >= 2.
1211   // The string array elements can be chars or unsigned shorts;
1212   // therefore the number of bytes in the string cannot be determined
1213   // at this point because we don't know what type of string is
1214   // being read.
1215   if (rc)
1216   {
1217     // 8 October 2004 Dale Lear
1218     //    Added the sanity checks on string size to avoid attempts
1219     //    to allocate huge amounts of memory when the value
1220     //    comes from a damaged file.
1221     if ( 0 != (0xF000000 & ui32) )
1222     {
1223       // 268 million chars oughta be plenty
1224       ON_ERROR("string element count is impossibly large");
1225       rc = false;
1226     }
1227     else if ( ui32 > 0 )
1228     {
1229       // make sure this is possible
1230       const ON_3DM_BIG_CHUNK* curchunk = m_chunk.Last();
1231       if ( 0 != curchunk && 0 == (TCODE_SHORT & curchunk->m_typecode) )
1232       {
1233         if (    curchunk->m_big_value < 0
1234              || ((ON__INT64)ui32) > curchunk->m_big_value
1235            )
1236         {
1237           ON_ERROR("string element count exceeds current chunk size");
1238           rc = false;
1239         }
1240       }
1241     }
1242 
1243     if (rc)
1244     {
1245       *sizeof_string = (size_t)ui32;
1246     }
1247   }
1248   return rc;
1249 }
1250 
1251 
1252 bool
ReadStringUTF8ElementCount(size_t * string_utf8_element_count)1253 ON_BinaryArchive::ReadStringUTF8ElementCount(
1254     size_t* string_utf8_element_count
1255     )
1256 {
1257   ON__UINT32 ui32 = 0;
1258   bool rc = ReadInt32(1,(ON__INT32*)&ui32);
1259   // Note that ui32 = number of elements in the string array, including
1260   // the null terminator.  So ui32 should either be 0 or be >= 2.
1261   // The string array elements can be chars or unsigned shorts;
1262   // therefore the number of bytes in the string cannot be determined
1263   // at this point because we don't know what type of string is
1264   // being read.
1265   if (rc)
1266   {
1267     // 8 October 2004 Dale Lear
1268     //    Added the sanity checks on string size to avoid attempts
1269     //    to allocate huge amounts of memory when the value
1270     //    comes from a damaged file.
1271     if ( 0 != (0xF000000 & ui32) )
1272     {
1273       // 268 million chars oughta be plenty
1274       ON_ERROR("string element count is impossibly large");
1275       rc = false;
1276     }
1277     else if ( ui32 > 0 )
1278     {
1279       // make sure this is possible
1280       const ON_3DM_BIG_CHUNK* curchunk = m_chunk.Last();
1281       if ( 0 != curchunk && 0 == (TCODE_SHORT & curchunk->m_typecode) )
1282       {
1283         if (    curchunk->m_big_value < 0
1284              || ((ON__INT64)ui32) > curchunk->m_big_value
1285            )
1286         {
1287           ON_ERROR("string byte count exceeds current chunk size");
1288           rc = false;
1289         }
1290       }
1291     }
1292   }
1293   if (!rc)
1294     ui32 = 0;
1295   if ( string_utf8_element_count )
1296     *string_utf8_element_count = (size_t)ui32;
1297   return rc;
1298 }
1299 
1300 
1301 bool
ReadStringUTF16ElementCount(size_t * string_utf16_element_count)1302 ON_BinaryArchive::ReadStringUTF16ElementCount(
1303     size_t* string_utf16_element_count
1304     )
1305 {
1306   ON__UINT32 ui32 = 0;
1307   bool rc = ReadInt32(1,(ON__INT32*)&ui32);
1308   // Note that ui32 = number of elements in the string array, including
1309   // the null terminator.  So ui32 should either be 0 or be >= 2.
1310   // The string array elements can be chars or unsigned shorts;
1311   // therefore the number of bytes in the string cannot be determined
1312   // at this point because we don't know what type of string is
1313   // being read.
1314   if (rc)
1315   {
1316     if ( 0 != (0xF000000 & ui32) )
1317     {
1318       // 268 million chars oughta be plenty
1319       ON_ERROR("string element count is impossibly large");
1320       rc = false;
1321     }
1322     else if ( ui32 > 0 )
1323     {
1324       // make sure this is possible
1325       const ON_3DM_BIG_CHUNK* curchunk = m_chunk.Last();
1326       if ( 0 != curchunk && 0 == (TCODE_SHORT & curchunk->m_typecode) )
1327       {
1328         // 2*ui32 is used because opennurbs writes all wide character
1329         // strings as UTF-16 encoded strings.
1330         if (    curchunk->m_big_value < 0
1331              || ((ON__INT64)2*ui32) > curchunk->m_big_value
1332            )
1333         {
1334           ON_ERROR("string byte count exceeds current chunk size");
1335           rc = false;
1336         }
1337       }
1338     }
1339   }
1340 
1341   if (!rc)
1342     ui32 = 0;
1343   if ( string_utf16_element_count )
1344     *string_utf16_element_count = (size_t)ui32;
1345   return rc;
1346 }
1347 
1348 bool
ReadString(size_t string_utf8_element_count,char * p)1349 ON_BinaryArchive::ReadString(         // Read NULL terminated string
1350     size_t string_utf8_element_count, // = value from ReadStringUTF8ElementCount()
1351     char* p                           // array[string_utf8_element_count]
1352     )
1353 {
1354   return ReadByte( string_utf8_element_count, p );
1355 }
1356 
1357 bool
ReadString(size_t string_utf8_element_count,unsigned char * p)1358 ON_BinaryArchive::ReadString(         // Read NULL terminated string
1359     size_t string_utf8_element_count, // = value from ReadStringUTF8ElementCount()
1360     unsigned char* p                  // array[string_utf8_element_count]
1361     )
1362 {
1363   return ReadByte( string_utf8_element_count, p );
1364 }
1365 
1366 bool
ReadString(size_t string_utf16_element_count,unsigned short * p)1367 ON_BinaryArchive::ReadString(          // Read NULL terminated unicode string
1368     size_t string_utf16_element_count, // length = value ReadStringUTF16ElementCount ReadStringSize()
1369     unsigned short* p                  // array[string_utf16_element_count]
1370     )
1371 {
1372   return ReadShort( string_utf16_element_count, p );
1373 }
1374 
1375 bool
ReadString(ON_String & s)1376 ON_BinaryArchive::ReadString( ON_String& s )
1377 {
1378   s.Destroy();
1379   size_t string_utf8_element_count = 0;
1380   bool rc = ReadStringUTF8ElementCount( &string_utf8_element_count );
1381   if ( rc && string_utf8_element_count > 0 )
1382   {
1383     const int istring_utf8_element_count = (int)string_utf8_element_count; // (int) converts 64 bits size_t
1384     s.ReserveArray(istring_utf8_element_count);
1385     ReadString( string_utf8_element_count, s.Array() );
1386     s.SetLength( istring_utf8_element_count-1 );
1387   }
1388   return rc;
1389 }
1390 
1391 bool
ReadString(ON_wString & s)1392 ON_BinaryArchive::ReadString( ON_wString& s )
1393 {
1394 #if defined(ON_COMPILER_MSC)
1395 #pragma warning( push )
1396 // Disable the MSC /W4 "conditional expression is constant" warning
1397 // about 2 == sizeof(wchar_t).  Since this code has to run on machines
1398 // where sizeof(wchar_t) can be 2, 4, or 8 bytes, the test is necessary.
1399 #pragma warning( disable : 4127 )
1400 #endif
1401 
1402   s.Destroy();
1403   size_t string_utf16_element_count = 0;
1404   bool rc = ReadStringUTF16ElementCount( &string_utf16_element_count );
1405   if ( rc && string_utf16_element_count > 0 )
1406   {
1407     // string_utf16_element_count = number of ON__INT16 elements in
1408     // the string.  This is almost always the same as the
1409     // number of unicode code points. However, if one of
1410     // the code points happens to require two ON__INT16
1411     // values to encode, then string_utf16_element_count will be
1412     // larger than the number of unicode code points in
1413     // the array.
1414     const int istring_utf16_element_count = (int)string_utf16_element_count;
1415     if ( 2 == sizeof(wchar_t) )
1416     {
1417       // When sizeof(wchar_t) is 2 bytes, assume wchar_t strings are
1418       // UTF-16 encoded unicode strings.
1419       s.ReserveArray( istring_utf16_element_count );
1420       rc = ReadInt16( string_utf16_element_count, (ON__INT16*)s.Array() );
1421       if (rc)
1422         s.SetLength( istring_utf16_element_count-1 );
1423     }
1424     else if ( 4 == sizeof(wchar_t)  )
1425     {
1426       // When sizeof(wchar_t) is 4 bytes, assume wchar_t strings are
1427       // UTF-32 encoded unicode strings. (some gcc implementations do this.)
1428 
1429       // Read the UTF-16 encode string from the file into
1430       // utf16_buffer[].
1431       const int istring_utf16_element_count = (int)string_utf16_element_count;
1432       ON_SimpleArray<ON__UINT16> utf16_buffer(istring_utf16_element_count);
1433       rc = ReadInt16(string_utf16_element_count,(ON__INT16*)utf16_buffer.Array());
1434       if(rc)
1435       {
1436         // convert to a UTF-32 encoded unicode string.
1437         utf16_buffer.SetCount(istring_utf16_element_count);
1438         utf16_buffer[istring_utf16_element_count-1] = 0;
1439         rc = false;
1440         const ON__UINT16* sUTF16 = utf16_buffer.Array();
1441         const int bTestByteOrder = false;
1442         const int sUTF16_count = istring_utf16_element_count-1;
1443         const ON__UINT32 error_code_point = 0xFFFD;
1444         const unsigned int error_mask = 0xFFFFFFFF;
1445         unsigned int error_status = 0;
1446 
1447         const int utf32_array_count = ON_ConvertUTF16ToUTF32(
1448             bTestByteOrder,
1449             sUTF16,
1450             sUTF16_count,
1451             0, // unsigned int* sUTF32
1452             0, // int sUTF32_count
1453             &error_status,
1454             error_mask,
1455             error_code_point,
1456             0 // const ON__UINT16** sNextUTF16
1457             );
1458 
1459         if ( 0 == utf32_array_count )
1460         {
1461           rc = true;
1462         }
1463         else if ( utf32_array_count > 0 )
1464         {
1465           error_status = 0;
1466           s.ReserveArray(utf32_array_count+1);
1467           const int utf32_array_count1 = ON_ConvertUTF16ToUTF32(
1468               bTestByteOrder,
1469               sUTF16,
1470               sUTF16_count,
1471               (unsigned int*)s.Array(), // unsigned int* sUTF32
1472               utf32_array_count, // sUTF32_count
1473               &error_status,
1474               error_mask,
1475               error_code_point,
1476               0 // const ON__UINT16** sNextUTF16
1477               );
1478           if ( utf32_array_count1 == utf32_array_count )
1479           {
1480             s.SetLength( utf32_array_count );
1481             rc = true;
1482           }
1483         }
1484       }
1485     }
1486     // G+Smo
1487     //if (!rc)
1488       s.Destroy();
1489   }
1490   return rc;
1491 
1492 #if defined(ON_COMPILER_MSC)
1493 #pragma warning( pop )
1494 #endif
1495 }
1496 
1497 
WriteArray(const ON_SimpleArray<ON_MappingChannel> & a)1498 bool ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_MappingChannel>& a)
1499 {
1500   int i, count = a.Count();
1501   if ( count < 0 )
1502     count = 0;
1503   bool rc = WriteInt( count );
1504   for  ( i = 0; i < count && rc; i++ )
1505   {
1506     // ON_MappingChannel::Write() puts the element in a chunk
1507     rc = a[i].Write(*this);
1508   }
1509   return rc;
1510 }
1511 
WriteArray(const ON_ClassArray<ON_MaterialRef> & a)1512 bool ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_MaterialRef>& a)
1513 {
1514   int i, count = a.Count();
1515   if ( count < 0 )
1516     count = 0;
1517   bool rc = WriteInt( count );
1518   for  ( i = 0; i < count && rc; i++ )
1519   {
1520     // ON_MaterialRef::Write() puts the element in a chunk
1521     rc = a[i].Write(*this);
1522   }
1523   return rc;
1524 }
1525 
1526 
WriteArray(int count,const ON_Layer * a)1527 bool ON_BinaryArchive::WriteArray( int count, const ON_Layer* a)
1528 {
1529   int i;
1530   if ( count < 0 || 0 == a )
1531     count = 0;
1532   bool rc = WriteInt( count );
1533   for  ( i = 0; i < count && rc; i++ )
1534   {
1535     rc = WriteObject(a[i]);
1536   }
1537   return rc;
1538 }
1539 
WriteArray(int count,const ON_Layer * const * a)1540 bool ON_BinaryArchive::WriteArray( int count, const ON_Layer*const* a)
1541 {
1542   int i;
1543   if ( count < 0 || 0 == a )
1544     count = 0;
1545   bool rc = WriteInt( count );
1546   for  ( i = 0; i < count && rc; i++ )
1547   {
1548     rc = WriteObject(a[i]);
1549   }
1550   return rc;
1551 }
1552 
WriteArray(const ON_ClassArray<ON_MappingRef> & a)1553 bool ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_MappingRef>& a )
1554 {
1555   int i, count = a.Count();
1556   if ( count < 0 )
1557     count = 0;
1558   bool rc = WriteInt( count );
1559   for  ( i = 0; i < count && rc; i++ )
1560   {
1561     // ON_MappingRef::Write() puts the element in a chunk
1562     rc = a[i].Write(*this);
1563   }
1564   return rc;
1565 }
1566 
1567 
WriteArray(const ON_ClassArray<ON_ObjRef> & a)1568 bool ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_ObjRef>& a)
1569 {
1570   int i, count = a.Count();
1571   if ( count < 0 )
1572     count = 0;
1573   bool rc = WriteInt( count );
1574   for  ( i = 0; i < count && rc; i++ )
1575   {
1576     // ON_ObjRef::Write() puts the element in a chunk
1577     rc = a[i].Write(*this);
1578   }
1579   return rc;
1580 }
1581 
WriteArray(const ON_SimpleArray<ON_ObjRef_IRefID> & a)1582 bool ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_ObjRef_IRefID>& a)
1583 {
1584   int i, count = a.Count();
1585   if ( count < 0 )
1586     count = 0;
1587   bool rc = WriteInt( count );
1588   for  ( i = 0; i < count && rc; i++ )
1589   {
1590     // ON_ObjRef_IRefID::Write() puts the element in a chunk
1591     rc = a[i].Write(*this);
1592   }
1593   return rc;
1594 }
1595 
ReadArray(ON_SimpleArray<ON_MappingChannel> & a)1596 bool ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_MappingChannel>& a )
1597 {
1598   a.Empty();
1599   int i, count;
1600   bool rc = ReadInt( &count );
1601   if (rc)
1602   {
1603     a.SetCapacity(count);
1604     for  ( i = 0; i < count && rc; i++ )
1605     {
1606       rc = a.AppendNew().Read(*this);
1607     }
1608   }
1609   return rc;
1610 }
1611 
ReadArray(ON_ClassArray<ON_MaterialRef> & a)1612 bool ON_BinaryArchive::ReadArray( ON_ClassArray<ON_MaterialRef>& a)
1613 {
1614   a.Empty();
1615   int i, count;
1616   bool rc = ReadInt( &count );
1617   if (rc)
1618   {
1619     a.SetCapacity(count);
1620     for  ( i = 0; i < count && rc; i++ )
1621     {
1622       rc = a.AppendNew().Read(*this);
1623     }
1624   }
1625   return rc;
1626 }
1627 
ReadArray(ON_ObjectArray<class ON_Layer> & a)1628 bool ON_BinaryArchive::ReadArray( ON_ObjectArray<class ON_Layer>& a)
1629 {
1630   a.Empty();
1631   int i, count;
1632   bool rc = ReadInt( &count );
1633   if (rc)
1634   {
1635     a.SetCapacity(count);
1636     for  ( i = 0; i < count && rc; i++ )
1637     {
1638       rc = (1 == ReadObject(a.AppendNew()));
1639       if (!rc)
1640       {
1641         a.Remove();
1642         break;
1643       }
1644     }
1645   }
1646   return rc;
1647 }
1648 
1649 
ReadArray(ON_SimpleArray<class ON_Layer * > & a)1650 bool ON_BinaryArchive::ReadArray( ON_SimpleArray<class ON_Layer*>& a)
1651 {
1652   a.Empty();
1653   ON_Layer* layer;
1654   int i, count;
1655   bool rc = ReadInt( &count );
1656   if (rc)
1657   {
1658     a.SetCapacity(count);
1659     for  ( i = 0; i < count && rc; i++ )
1660     {
1661       layer = 0;
1662       ON_Object* p = 0;
1663       rc = (1==ReadObject(&p));
1664       if (rc)
1665       {
1666         layer = ON_Layer::Cast(p);
1667       }
1668       if (!rc || 0 == layer)
1669       {
1670         if ( p )
1671           delete p;
1672         rc = false;
1673         break;
1674       }
1675       a.Append(layer);
1676     }
1677   }
1678   return rc;
1679 }
1680 
ReadArray(ON_ClassArray<ON_MappingRef> & a)1681 bool ON_BinaryArchive::ReadArray( ON_ClassArray<ON_MappingRef>& a)
1682 {
1683   a.Empty();
1684   int i, count;
1685   bool rc = ReadInt( &count );
1686   if (rc)
1687   {
1688     a.SetCapacity(count);
1689     for  ( i = 0; i < count && rc; i++ )
1690     {
1691       rc = a.AppendNew().Read(*this);
1692     }
1693   }
1694   return rc;
1695 }
1696 
ReadArray(ON_ClassArray<ON_ObjRef> & a)1697 bool ON_BinaryArchive::ReadArray( ON_ClassArray<ON_ObjRef>& a)
1698 {
1699   a.Empty();
1700   int i, count;
1701   bool rc = ReadInt( &count );
1702   if (rc)
1703   {
1704     a.SetCapacity(count);
1705     for  ( i = 0; i < count && rc; i++ )
1706     {
1707       rc = a.AppendNew().Read(*this);
1708     }
1709   }
1710   return rc;
1711 }
1712 
ReadArray(ON_SimpleArray<ON_ObjRef_IRefID> & a)1713 bool ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_ObjRef_IRefID>& a)
1714 {
1715   a.Empty();
1716   int i, count;
1717   bool rc = ReadInt( &count );
1718   if (rc)
1719   {
1720     a.SetCapacity(count);
1721     for  ( i = 0; i < count && rc; i++ )
1722     {
1723       rc = a.AppendNew().Read(*this);
1724     }
1725   }
1726   return rc;
1727 }
1728 
1729 
ReadArray(ON_SimpleArray<ON_DisplayMaterialRef> & a)1730 bool ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_DisplayMaterialRef>& a )
1731 {
1732   a.Empty();
1733   int count = 0;
1734   bool rc = ReadInt( &count );
1735   if ( rc && count > 0 )
1736   {
1737     a.SetCapacity( count );
1738     int i;
1739     for ( i = 0; i < count && rc; i++ )
1740     {
1741       rc = ReadDisplayMaterialRef(a.AppendNew());
1742     }
1743   }
1744   return rc;
1745 }
1746 
1747 bool
ReadArray(ON_ClassArray<ON_String> & a)1748 ON_BinaryArchive::ReadArray( ON_ClassArray<ON_String>& a)
1749 {
1750   a.Empty();
1751   int count = 0;
1752   bool rc = ReadInt( &count );
1753   if ( rc && count > 0 )
1754   {
1755     a.SetCapacity( count );
1756     int i;
1757     for ( i = 0; i < count && rc; i++ )
1758     {
1759       rc = ReadString( a.AppendNew() );
1760     }
1761   }
1762   return rc;
1763 }
1764 
1765 bool
ReadArray(ON_ClassArray<ON_wString> & a)1766 ON_BinaryArchive::ReadArray( ON_ClassArray<ON_wString>& a)
1767 {
1768   a.Empty();
1769   int count = 0;
1770   bool rc = ReadInt( &count );
1771   if ( rc && count > 0 )
1772   {
1773     a.SetCapacity( count );
1774     int i;
1775     for ( i = 0; i < count && rc; i++ )
1776     {
1777       rc = ReadString( a.AppendNew() );
1778     }
1779   }
1780   return rc;
1781 }
1782 
1783 bool
WriteArray(const ON_SimpleArray<ON_DisplayMaterialRef> & a)1784 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_DisplayMaterialRef>& a )
1785 {
1786   int i, count = a.Count();
1787   if ( count < 0 )
1788     count = 0;
1789   bool rc = WriteInt( count );
1790   for  ( i = 0; i < count && rc; i++ )
1791   {
1792     rc = WriteDisplayMaterialRef( a[i] );
1793   }
1794   return rc;
1795 }
1796 
1797 bool
WriteArray(const ON_ClassArray<ON_String> & a)1798 ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_String>& a )
1799 {
1800   int i, count = a.Count();
1801   if ( count < 0 )
1802     count = 0;
1803   bool rc = WriteInt( count );
1804   for  ( i = 0; i < count && rc; i++ )
1805   {
1806     rc = WriteString( a[i] );
1807   }
1808   return rc;
1809 }
1810 
1811 bool
WriteArray(const ON_ClassArray<ON_wString> & a)1812 ON_BinaryArchive::WriteArray( const ON_ClassArray<ON_wString>& a )
1813 {
1814   int i, count = a.Count();
1815   if ( count < 0 )
1816     count = 0;
1817   bool rc = WriteInt( count );
1818   for  ( i = 0; i < count && rc; i++ )
1819   {
1820     rc = WriteString( a[i] );
1821   }
1822   return rc;
1823 }
1824 
1825 bool
ReadArray(ON_SimpleArray<bool> & a)1826 ON_BinaryArchive::ReadArray( ON_SimpleArray<bool>& a )
1827 {
1828 #if defined(ON_COMPILER_MSC)
1829 // Disable the MSC /W4 "conditional expression is constant" warning
1830 // about sizeof(*c) == sizeof(*b).  Since this code has to run on machines
1831 // where sizeof(bool) can be 1, 2, 4, or 8 bytes, the test is necessary.
1832 #pragma warning( push )
1833 #pragma warning( disable : 4127 )
1834 #endif
1835 
1836   a.Empty();
1837   int count = 0;
1838   bool rc = ReadInt( &count );
1839   if ( rc && count > 0 )
1840   {
1841     a.SetCapacity( count );
1842     char* c = 0;
1843     bool* b = a.Array();
1844     if ( sizeof(*c) == sizeof(*b) )
1845     {
1846       // 8 bit "bool" on this compiler
1847       c = (char*)b;
1848     }
1849     else if ( b )
1850     {
1851       // bigger "bool" on this compiler
1852       c = (char*)onmalloc(count*sizeof(*c));
1853     }
1854     rc = ReadChar( count, c );
1855     if ( rc )
1856     {
1857       if ( c == (char*)b )
1858       {
1859         a.SetCount(count);
1860       }
1861       else if ( c )
1862       {
1863         int i;
1864         for ( i = 0; i < count; i++ )
1865         {
1866           a.Append(c[i]?true:false);
1867         }
1868         onfree(c);
1869       }
1870     }
1871   }
1872   return rc;
1873 
1874 #if defined(ON_COMPILER_MSC)
1875 #pragma warning( pop )
1876 #endif
1877 
1878 }
1879 
1880 bool
ReadArray(ON_SimpleArray<char> & a)1881 ON_BinaryArchive::ReadArray( ON_SimpleArray<char>& a )
1882 {
1883   a.Empty();
1884   int count = 0;
1885   bool rc = ReadInt( &count );
1886   if ( rc && count > 0 ) {
1887     a.SetCapacity( count );
1888     rc = ReadChar( count, a.Array() );
1889     if ( rc )
1890       a.SetCount(count);
1891   }
1892   return rc;
1893 }
1894 
1895 bool
ReadArray(ON_SimpleArray<short> & a)1896 ON_BinaryArchive::ReadArray( ON_SimpleArray<short>& a )
1897 {
1898   a.Empty();
1899   int count = 0;
1900   bool rc = ReadInt( &count );
1901   if ( rc && count > 0 ) {
1902     a.SetCapacity( count );
1903     rc = ReadShort( count, a.Array() );
1904     if ( rc )
1905       a.SetCount(count);
1906   }
1907   return rc;
1908 }
1909 
1910 bool
ReadArray(ON_SimpleArray<int> & a)1911 ON_BinaryArchive::ReadArray( ON_SimpleArray<int>& a )
1912 {
1913   a.Empty();
1914   int count = 0;
1915   bool rc = ReadInt( &count );
1916   if ( rc && count > 0 ) {
1917     a.SetCapacity( count );
1918     rc = ReadInt( count, a.Array() );
1919     if ( rc )
1920       a.SetCount(count);
1921   }
1922   return rc;
1923 }
1924 
1925 bool
ReadArray(ON_SimpleArray<float> & a)1926 ON_BinaryArchive::ReadArray( ON_SimpleArray<float>& a )
1927 {
1928   a.Empty();
1929   int count = 0;
1930   bool rc = ReadInt( &count );
1931   if ( rc && count > 0 ) {
1932     a.SetCapacity( count );
1933     rc = ReadFloat( count, a.Array() );
1934     if ( rc )
1935       a.SetCount(count);
1936   }
1937   return rc;
1938 }
1939 
1940 bool
ReadArray(ON_SimpleArray<double> & a)1941 ON_BinaryArchive::ReadArray( ON_SimpleArray<double>& a )
1942 {
1943   a.Empty();
1944   int count = 0;
1945   bool rc = ReadInt( &count );
1946   if ( rc && count > 0 ) {
1947     a.SetCapacity( count );
1948     rc = ReadDouble( count, a.Array() );
1949     if ( rc )
1950       a.SetCount(count);
1951   }
1952   return rc;
1953 }
1954 
1955 bool
ReadArray(ON_SimpleArray<ON_Color> & a)1956 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_Color>& a )
1957 {
1958   a.Empty();
1959   int count = 0;
1960   bool rc = ReadInt( &count );
1961   if ( rc && count > 0 ) {
1962     a.SetCapacity( count );
1963     rc = ReadInt( count, (int*)a.Array() );
1964     if ( rc )
1965       a.SetCount(count);
1966   }
1967   return rc;
1968 }
1969 
1970 
1971 bool
ReadArray(ON_SimpleArray<ON_2dPoint> & a)1972 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_2dPoint>& a )
1973 {
1974   a.Empty();
1975   int count = 0;
1976   bool rc = ReadInt( &count );
1977   if ( rc && count > 0 ) {
1978     a.SetCapacity( count );
1979     rc = ReadDouble( 2*count, &a.Array()->x );
1980     if ( rc )
1981       a.SetCount(count);
1982   }
1983   return rc;
1984 }
1985 
1986 bool
ReadArray(ON_SimpleArray<ON_3dPoint> & a)1987 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_3dPoint>& a )
1988 {
1989   a.Empty();
1990   int count = 0;
1991   bool rc = ReadInt( &count );
1992   if ( rc && count > 0 ) {
1993     a.SetCapacity( count );
1994     rc = ReadDouble( 3*count, &a.Array()->x );
1995     if ( rc )
1996       a.SetCount(count);
1997   }
1998   return rc;
1999 }
2000 
2001 bool
ReadArray(ON_SimpleArray<ON_4dPoint> & a)2002 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_4dPoint>& a )
2003 {
2004   a.Empty();
2005   int count = 0;
2006   bool rc = ReadInt( &count );
2007   if ( rc && count > 0 ) {
2008     a.SetCapacity( count );
2009     rc = ReadDouble( 4*count, &a.Array()->x );
2010     if ( rc )
2011       a.SetCount(count);
2012   }
2013   return rc;
2014 }
2015 
2016 bool
ReadArray(ON_SimpleArray<ON_2dVector> & a)2017 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_2dVector>& a )
2018 {
2019   a.Empty();
2020   int count = 0;
2021   bool rc = ReadInt( &count );
2022   if ( rc && count > 0 ) {
2023     a.SetCapacity( count );
2024     rc = ReadDouble( 2*count, &a.Array()->x );
2025     if ( rc )
2026       a.SetCount(count);
2027   }
2028   return rc;
2029 }
2030 
2031 bool
ReadArray(ON_SimpleArray<ON_3dVector> & a)2032 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_3dVector>& a )
2033 {
2034   a.Empty();
2035   int count = 0;
2036   bool rc = ReadInt( &count );
2037   if ( rc && count > 0 ) {
2038     a.SetCapacity( count );
2039     rc = ReadDouble( 3*count, &a.Array()->x );
2040     if ( rc )
2041       a.SetCount(count);
2042   }
2043   return rc;
2044 }
2045 
2046 bool
ReadArray(ON_SimpleArray<ON_Xform> & a)2047 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_Xform>& a )
2048 {
2049   a.Empty();
2050   int count = 0;
2051   bool rc = ReadInt( &count );
2052   if ( rc && count > 0 )
2053   {
2054     a.SetCapacity( count );
2055     int i;
2056     for ( i = 0; i < count && rc; i++ )
2057     {
2058       rc = ReadXform(a.AppendNew());
2059     }
2060   }
2061   return rc;
2062 }
2063 
2064 bool
ReadArray(ON_SimpleArray<ON_2fPoint> & a)2065 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_2fPoint>& a )
2066 {
2067   a.Empty();
2068   int count = 0;
2069   bool rc = ReadInt( &count );
2070   if ( rc && count > 0 ) {
2071     a.SetCapacity( count );
2072     rc = ReadFloat( 2*count, &a.Array()->x );
2073     if ( rc )
2074       a.SetCount(count);
2075   }
2076   return rc;
2077 }
2078 
2079 bool
ReadArray(ON_SimpleArray<ON_3fPoint> & a)2080 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_3fPoint>& a )
2081 {
2082   a.Empty();
2083   int count = 0;
2084   bool rc = ReadInt( &count );
2085   if ( rc && count > 0 ) {
2086     a.SetCapacity( count );
2087     rc = ReadFloat( 3*count, &a.Array()->x );
2088     if ( rc )
2089       a.SetCount(count);
2090   }
2091   return rc;
2092 }
2093 
2094 bool
ReadArray(ON_SimpleArray<ON_4fPoint> & a)2095 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_4fPoint>& a )
2096 {
2097   a.Empty();
2098   int count = 0;
2099   bool rc = ReadInt( &count );
2100   if ( rc && count > 0 ) {
2101     a.SetCapacity( count );
2102     rc = ReadFloat( 4*count, &a.Array()->x );
2103     if ( rc )
2104       a.SetCount(count);
2105   }
2106   return rc;
2107 }
2108 
2109 bool
ReadArray(ON_SimpleArray<ON_2fVector> & a)2110 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_2fVector>& a )
2111 {
2112   a.Empty();
2113   int count = 0;
2114   bool rc = ReadInt( &count );
2115   if ( rc && count > 0 ) {
2116     a.SetCapacity( count );
2117     rc = ReadFloat( 2*count, &a.Array()->x );
2118     if ( rc )
2119       a.SetCount(count);
2120   }
2121   return rc;
2122 }
2123 
2124 bool
ReadArray(ON_SimpleArray<ON_3fVector> & a)2125 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_3fVector>& a )
2126 {
2127   a.Empty();
2128   int count = 0;
2129   bool rc = ReadInt( &count );
2130   if ( rc && count > 0 ) {
2131     a.SetCapacity( count );
2132     rc = ReadFloat( 3*count, &a.Array()->x );
2133     if ( rc )
2134       a.SetCount(count);
2135   }
2136   return rc;
2137 }
2138 
2139 bool
ReadArray(ON_SimpleArray<ON_UUID> & a)2140 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_UUID>& a )
2141 {
2142   a.Empty();
2143   ON_UUID uuid;
2144   int i, count = 0;
2145   bool rc = ReadInt( &count );
2146   if ( rc && count > 0 )
2147   {
2148     a.SetCapacity( count );
2149     for ( i = 0; i < count && rc; i++ )
2150     {
2151       rc = ReadUuid( uuid );
2152       if ( rc )
2153         a.Append(uuid);
2154     }
2155   }
2156   return rc;
2157 }
2158 
2159 
2160 
2161 
2162 bool
ReadArray(ON_SimpleArray<ON_UuidIndex> & a)2163 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_UuidIndex>& a )
2164 {
2165   a.Empty();
2166   ON_UuidIndex idi;
2167   int i, count = 0;
2168   bool rc = ReadInt( &count );
2169   if ( rc && count > 0 )
2170   {
2171     a.SetCapacity( count );
2172     for ( i = 0; i < count && rc; i++ )
2173     {
2174       rc = ReadUuid( idi.m_id );
2175       if ( rc )
2176       {
2177         rc = ReadInt(&idi.m_i);
2178         if(rc)
2179           a.Append(idi);
2180       }
2181     }
2182   }
2183   return rc;
2184 }
2185 
2186 
2187 bool
WriteArray(const ON_SimpleArray<ON_UUID> & a)2188 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_UUID>& a )
2189 {
2190   int i, count = a.Count();
2191   if ( count < 0 )
2192     count = 0;
2193   bool rc = WriteInt( count );
2194   for  ( i = 0; i < count && rc; i++ )
2195   {
2196     rc = WriteUuid( a[i] );
2197   }
2198   return rc;
2199 }
2200 
2201 
2202 bool
WriteArray(const ON_SimpleArray<ON_UuidIndex> & a)2203 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_UuidIndex>& a )
2204 {
2205   int i, count = a.Count();
2206   if ( count < 0 )
2207     count = 0;
2208   bool rc = WriteInt( count );
2209   for  ( i = 0; i < count && rc; i++ )
2210   {
2211     rc = WriteUuid( a[i].m_id );
2212     if (rc)
2213       rc = WriteInt( a[i].m_i );
2214   }
2215   return rc;
2216 }
2217 
2218 
2219 bool
ReadArray(ON_SimpleArray<ON_LinetypeSegment> & a)2220 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_LinetypeSegment>& a )
2221 {
2222   a.Empty();
2223   ON_LinetypeSegment seg;
2224   int i, count = 0;
2225   bool rc = ReadInt( &count );
2226   if ( rc && count > 0 )
2227   {
2228     a.SetCapacity( count );
2229     for ( i = 0; i < count && rc; i++ )
2230     {
2231       rc = ReadLinetypeSegment( seg );
2232       if ( rc )
2233         a.Append(seg);
2234     }
2235   }
2236   return rc;
2237 }
2238 
2239 bool
WriteArray(const ON_SimpleArray<ON_LinetypeSegment> & a)2240 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_LinetypeSegment>& a )
2241 {
2242   int i, count = a.Count();
2243   if ( count < 0 )
2244     count = 0;
2245   bool rc = WriteInt( count );
2246   for  ( i = 0; i < count && rc; i++ )
2247   {
2248     rc = WriteLinetypeSegment( a[i] );
2249   }
2250   return rc;
2251 }
2252 
ReadLinetypeSegment(ON_LinetypeSegment & seg)2253 bool ON_BinaryArchive::ReadLinetypeSegment(ON_LinetypeSegment& seg)
2254 {
2255   seg.m_length = 1.0;
2256   seg.m_seg_type = ON_LinetypeSegment::stLine;
2257   unsigned int i;
2258   bool rc = ReadDouble(&seg.m_length);
2259   if (rc)
2260   {
2261     rc = ReadInt(&i);
2262     if( ON_LinetypeSegment::stLine == i )
2263       seg.m_seg_type = ON_LinetypeSegment::stLine;
2264     else if ( ON_LinetypeSegment::stSpace == i )
2265       seg.m_seg_type = ON_LinetypeSegment::stSpace;
2266   }
2267   return rc;
2268 }
2269 
2270 
WriteLinetypeSegment(const ON_LinetypeSegment & seg)2271 bool ON_BinaryArchive::WriteLinetypeSegment( const ON_LinetypeSegment& seg)
2272 {
2273   // do not add chunk info here
2274   unsigned int i = seg.m_seg_type;
2275   bool rc = WriteDouble(seg.m_length);
2276   if (rc)
2277     rc = WriteInt(i);
2278   return rc;
2279 }
2280 
2281 
2282 bool
ReadArray(ON_SimpleArray<ON_SurfaceCurvature> & a)2283 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_SurfaceCurvature>& a )
2284 {
2285   a.Empty();
2286   int count = 0;
2287   bool rc = ReadInt( &count );
2288   if ( rc && count > 0 ) {
2289     a.SetCapacity( count );
2290     rc = ReadDouble( 2*count, &a.Array()->k1 );
2291     if ( rc )
2292       a.SetCount(count);
2293   }
2294   return rc;
2295 }
2296 
2297 
2298 bool
ReadArray(ON_SimpleArray<ON_ClippingPlaneInfo> & a)2299 ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_ClippingPlaneInfo>& a )
2300 {
2301   a.Empty();
2302   int count = 0;
2303   bool rc = ReadInt( &count );
2304   if ( rc && count > 0 )
2305   {
2306     a.SetCapacity(count);
2307     for ( int i = 0; i < count && rc ; i++ )
2308     {
2309       rc = a.AppendNew().Read(*this);
2310       if (!rc)
2311         a.Remove();
2312     }
2313   }
2314   return rc;
2315 }
2316 
2317 
WriteBool(bool b)2318 bool ON_BinaryArchive::WriteBool( bool b )
2319 {
2320   unsigned char c = (b?1:0);
2321   return WriteChar(c);
2322 }
2323 
ReadBool(bool * b)2324 bool ON_BinaryArchive::ReadBool( bool *b )
2325 {
2326   unsigned char c;
2327   bool rc = ReadChar(&c);
2328   if (rc && b)
2329   {
2330     if ( c != 0 && c != 1 )
2331     {
2332       // WriteBool always writes a 0 or 1.  So either your code
2333       // has a bug, the file is corrupt, the the file pointer
2334       // is where it should be.
2335       ON_ERROR("ON_BinaryArchive::ReadBool - bool value != 0 and != 1");
2336       rc = false;
2337     }
2338     *b = c?true:false;
2339   }
2340   return rc;
2341 }
2342 
2343 bool
WriteChar(size_t count,const char * p)2344 ON_BinaryArchive::WriteChar(    // Write an array of 8 bit chars
2345 		size_t count,       // number of chars to write
2346 		const char* p
2347 		)
2348 {
2349   return WriteByte( count, p );
2350 }
2351 
2352 bool
WriteChar(size_t count,const unsigned char * p)2353 ON_BinaryArchive::WriteChar(    // Write an array of 8 bit unsigned chars
2354 		size_t count,       // number of unsigned chars to write
2355 		const unsigned char* p
2356 		)
2357 {
2358   return WriteByte( count, p );
2359 }
2360 
2361 bool
WriteChar(char c)2362 ON_BinaryArchive::WriteChar(    // Write a single 8 bit char
2363 		char c
2364 		)
2365 {
2366   return WriteByte( 1, &c );
2367 }
2368 
2369 bool
WriteChar(unsigned char c)2370 ON_BinaryArchive::WriteChar(    // Write a single 8 bit unsigned char
2371 		unsigned char c
2372 		)
2373 {
2374   return WriteByte( 1, &c );
2375 }
2376 
2377 bool
WriteInt16(size_t count,const ON__INT16 * p)2378 ON_BinaryArchive::WriteInt16(   // Write an array of 16 bit shorts
2379 		size_t count,               // number of shorts to write
2380 		const ON__INT16* p
2381 		)
2382 {
2383   bool rc = true;
2384   if ( m_endian == ON::big_endian )
2385   {
2386     if ( count > 0 )
2387     {
2388       const char* b = (const char*)p;
2389       while ( rc && count-- )
2390       {
2391         rc = WriteByte( 1, b+1 );
2392         if (rc)
2393           rc = WriteByte( 1, b );
2394         b++;
2395         b++;
2396       }
2397     }
2398   }
2399   else
2400   {
2401     rc = WriteByte( count<<1, p );
2402   }
2403   return rc;
2404 }
2405 
2406 bool
WriteShort(size_t count,const short * p)2407 ON_BinaryArchive::WriteShort(   // Write an array of 16 bit shorts
2408 		size_t count,       // number of shorts to write
2409 		const short* p
2410 		)
2411 {
2412 #if defined(ON_COMPILER_MSC)
2413 #pragma warning( push )
2414 // Disable the MSC /W4 "conditional expression is constant" warning
2415 // about 2 == sizeof(*p).  Since this code has to run on machines
2416 // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary.
2417 #pragma warning( disable : 4127 )
2418 #endif
2419 
2420   bool rc;
2421   if ( 2 == sizeof(*p) )
2422   {
2423     rc = WriteInt16( count, (const ON__INT16*)p );
2424   }
2425   else
2426   {
2427     rc = true;
2428     ON__INT16 i16;
2429     size_t j;
2430     for ( j = 0; j < count; j++ )
2431     {
2432       i16 = (ON__INT16)(*p++);
2433       rc = WriteInt16( 1, &i16);
2434     }
2435   }
2436   return rc;
2437 
2438 #if defined(ON_COMPILER_MSC)
2439 #pragma warning( pop )
2440 #endif
2441 }
2442 
2443 bool
WriteShort(size_t count,const unsigned short * p)2444 ON_BinaryArchive::WriteShort(   // Write an array of 16 bit unsigned shorts
2445 		size_t count,       // number of shorts to write
2446 		const unsigned short* p
2447 		)
2448 {
2449   return WriteShort( count, (const short*)p );
2450 }
2451 
2452 bool
WriteShort(short s)2453 ON_BinaryArchive::WriteShort(   // Write a single 16 bit short
2454 		short s
2455 		)
2456 {
2457   return WriteShort( 1, &s );
2458 }
2459 
2460 bool
WriteShort(unsigned short s)2461 ON_BinaryArchive::WriteShort(   // Write a single 16 bit unsigned short
2462 		unsigned short s
2463 		)
2464 {
2465   return WriteShort( 1, &s );
2466 }
2467 
2468 bool
WriteInt32(size_t count,const ON__INT32 * p)2469 ON_BinaryArchive::WriteInt32( // Write an array of 32 bit integers
2470 		size_t count,	            // number of ints to write
2471 		const ON__INT32* p
2472 		)
2473 {
2474   bool rc = true;
2475   if ( m_endian == ON::big_endian )
2476   {
2477     if ( count > 0 )
2478     {
2479       const char* b = (const char*)p;
2480       while ( rc && count-- )
2481       {
2482         rc = WriteByte( 1, b+3 );
2483         if (rc) rc = WriteByte( 1, b+2 );
2484         if (rc) rc = WriteByte( 1, b+1 );
2485         if (rc) rc = WriteByte( 1, b );
2486         b += 4;
2487       }
2488     }
2489   }
2490   else
2491   {
2492     rc = WriteByte( count<<2, p );
2493   }
2494   return rc;
2495 }
2496 
2497 bool
ReadInt64(size_t count,ON__INT64 * p)2498 ON_BinaryArchive::ReadInt64( // Read an array of 64 bit integers
2499 		size_t count,            // number of 64 bit integers to read
2500 		ON__INT64* p
2501 		)
2502 {
2503   bool rc = ReadByte( count<<3, p );
2504   if ( rc && m_endian == ON::big_endian )
2505   {
2506 		unsigned char* b=(unsigned char*)p;
2507 		unsigned char  c;
2508 		while(count--) {
2509 			c = b[0]; b[0] = b[7]; b[7] = c;
2510 			c = b[1]; b[1] = b[6]; b[6] = c;
2511 			c = b[2]; b[2] = b[5]; b[5] = c;
2512 			c = b[3]; b[3] = b[4]; b[4] = c;
2513 			b += 8;
2514 		}
2515   }
2516   return rc;
2517 }
2518 
2519 bool
WriteInt64(size_t count,const ON__INT64 * p)2520 ON_BinaryArchive::WriteInt64( // Write an array of 64 bit integers
2521 		size_t count,	            // number of ints to write
2522 		const ON__INT64* p
2523 		)
2524 {
2525   bool rc = true;
2526   if ( m_endian == ON::big_endian )
2527   {
2528     if ( count > 0 )
2529     {
2530       const char* b = (const char*)p;
2531       while ( rc && count-- )
2532       {
2533         rc = WriteByte( 1, b+7 );
2534         if (rc) rc = WriteByte( 1, b+6 );
2535         if (rc) rc = WriteByte( 1, b+5 );
2536         if (rc) rc = WriteByte( 1, b+4 );
2537         if (rc) rc = WriteByte( 1, b+3 );
2538         if (rc) rc = WriteByte( 1, b+2 );
2539         if (rc) rc = WriteByte( 1, b+1 );
2540         if (rc) rc = WriteByte( 1, b );
2541         b += 8;
2542       }
2543     }
2544   }
2545   else
2546   {
2547     rc = WriteByte( count<<3, p );
2548   }
2549   return rc;
2550 }
2551 
2552 bool
WriteInt(size_t count,const int * p)2553 ON_BinaryArchive::WriteInt( // Write an array of integers
2554 		size_t count,	          // number of ints to write
2555 		const int* p
2556 		)
2557 {
2558 #if defined(ON_COMPILER_MSC)
2559 #pragma warning( push )
2560 // Disable the MSC /W4 "conditional expression is constant" warning
2561 // about 4 == sizeof(*p).  Since this code has to run on machines
2562 // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary.
2563 #pragma warning( disable : 4127 )
2564 #endif
2565 
2566   bool rc;
2567   if ( 4 == sizeof(*p) )
2568   {
2569     rc = WriteInt32( count, (const ON__INT32*)p );
2570   }
2571   else
2572   {
2573     ON__INT32 i32;
2574     size_t j;
2575     rc = true;
2576     for ( j = 0; j < count && rc; j++ )
2577     {
2578       i32 = (ON__INT32)(*p++);
2579       rc = WriteInt32( 1, &i32 );
2580     }
2581   }
2582   return rc;
2583 
2584 #if defined(ON_COMPILER_MSC)
2585 #pragma warning( pop )
2586 #endif
2587 }
2588 
2589 bool
WriteSize(size_t sz)2590 ON_BinaryArchive::WriteSize(size_t sz)
2591 {
2592   unsigned int u = (unsigned int)sz;
2593   return WriteInt(u);
2594 }
2595 
2596 bool
ReadSize(size_t * sz)2597 ON_BinaryArchive::ReadSize(size_t* sz)
2598 {
2599   unsigned int u = 0;
2600   bool rc = ReadInt(&u);
2601   if (rc)
2602     *sz = u;
2603   return rc;
2604 }
2605 
WriteBigSize(size_t sz)2606 bool ON_BinaryArchive::WriteBigSize(size_t sz)
2607 {
2608   ON__UINT64 u = (ON__UINT64)sz;
2609   return WriteInt64(1,(ON__INT64*)&u);;
2610 }
2611 
ReadBigSize(size_t * sz)2612 bool ON_BinaryArchive::ReadBigSize( size_t* sz )
2613 {
2614   ON__UINT64 u;
2615   bool rc = ReadInt64(1,(ON__INT64*)&u);
2616   if (rc)
2617     *sz = (size_t)u;
2618   return rc;
2619 }
2620 
WriteBigTime(time_t t)2621 bool ON_BinaryArchive::WriteBigTime(time_t t)
2622 {
2623   ON__UINT64 u = (ON__UINT64)t;
2624   return WriteInt64(1,(ON__INT64*)&u);
2625 }
2626 
ReadBigTime(time_t * t)2627 bool ON_BinaryArchive::ReadBigTime( time_t* t )
2628 {
2629   ON__UINT64 u;
2630   bool rc = ReadInt64(1,(ON__INT64*)&u);
2631   if (rc)
2632     *t = (time_t)u;
2633   return rc;
2634 }
2635 
2636 
2637 bool
WriteInt(size_t count,const unsigned int * p)2638 ON_BinaryArchive::WriteInt( // Write an array of 32 bit integers
2639 		size_t count,	      // number of ints to write
2640 		const unsigned int* p
2641 		)
2642 {
2643   return WriteInt( count, (const int*)p );
2644 }
2645 
2646 bool
WriteInt(int i)2647 ON_BinaryArchive::WriteInt( // Write a single 32 bit integer
2648 		int i
2649 		)
2650 {
2651   return WriteInt( 1, &i );
2652 }
2653 
2654 bool
WriteInt(unsigned int i)2655 ON_BinaryArchive::WriteInt( // Write a single 32 bit integer
2656 		unsigned int i
2657 		)
2658 {
2659   return WriteInt( 1, &i );
2660 }
2661 
WriteBigInt(size_t count,const ON__INT64 * p)2662 bool ON_BinaryArchive::WriteBigInt( // Write an array of 64 bit integers
2663 		size_t count,
2664 		const ON__INT64* p
2665 		)
2666 {
2667   return WriteInt64(count,p);
2668 }
2669 
WriteBigInt(size_t count,const ON__UINT64 * p)2670 bool ON_BinaryArchive::WriteBigInt( // Write an array of 64 bit integers
2671 		size_t count,
2672 		const ON__UINT64* p
2673 		)
2674 {
2675   return WriteInt64(count,(const ON__INT64*)p);
2676 }
2677 
WriteBigInt(ON__INT64 i)2678 bool ON_BinaryArchive:: WriteBigInt( // Write a single 64 bit integer
2679 		ON__INT64 i
2680 		)
2681 {
2682   return WriteInt64(1,&i);
2683 }
2684 
WriteBigInt(ON__UINT64 u)2685 bool ON_BinaryArchive::WriteBigInt( // Write a single 64 bit unsigned integer
2686 		ON__UINT64 u
2687 		)
2688 {
2689   return WriteInt64(1,(const ON__INT64*)&u);
2690 }
2691 
2692 
2693 
2694 bool
WriteLong(size_t count,const long * p)2695 ON_BinaryArchive::WriteLong( // Write an array of longs
2696 		size_t count,	      // number of longs to write
2697 		const long* p
2698 		)
2699 {
2700 #if defined(ON_COMPILER_MSC)
2701 #pragma warning( push )
2702 // Disable the MSC /W4 "conditional expression is constant" warning
2703 // about 4 == sizeof(*p).  Since this code has to run on machines
2704 // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary.
2705 #pragma warning( disable : 4127 )
2706 #endif
2707 
2708   bool rc;
2709   if ( 4 == sizeof(*p) )
2710   {
2711     rc = WriteInt32( count, (const ON__INT32*)p );
2712   }
2713   else
2714   {
2715     ON__INT32 i32;
2716     size_t j;
2717     rc = true;
2718     for ( j = 0; j < count && rc; j++ )
2719     {
2720       i32 = (ON__INT32)(*p++);
2721       rc = WriteInt32( 1, &i32 );
2722     }
2723   }
2724   return rc;
2725 
2726 #if defined(ON_COMPILER_MSC)
2727 #pragma warning( pop )
2728 #endif
2729 }
2730 
2731 bool
WriteLong(size_t count,const unsigned long * p)2732 ON_BinaryArchive::WriteLong( // Write an array of longs
2733 		size_t count,	      // number of longs to write
2734 		const unsigned long* p
2735 		)
2736 {
2737   return WriteLong( count, (const long*)p );
2738 }
2739 
2740 bool
WriteLong(long i)2741 ON_BinaryArchive::WriteLong( // Write a single long
2742 		long i
2743 		)
2744 {
2745   return WriteLong( 1, &i );
2746 }
2747 
2748 bool
WriteLong(unsigned long i)2749 ON_BinaryArchive::WriteLong( // Write a single unsigned long
2750 		unsigned long i
2751 		)
2752 {
2753   return WriteLong( 1, &i );
2754 }
2755 
2756 
2757 bool
WriteFloat(size_t count,const float * p)2758 ON_BinaryArchive::WriteFloat(   // Write a number of IEEE floats
2759 		size_t count,       // number of doubles
2760 		const float* p
2761 		)
2762 {
2763   // floats and integers have same size and endian issues
2764   return WriteInt( count, (const int*)p );
2765 }
2766 
2767 bool
WriteFloat(float f)2768 ON_BinaryArchive::WriteFloat(   // Write a single float
2769 		float f
2770 		)
2771 {
2772   return WriteFloat( 1, &f );
2773 }
2774 
2775 bool
WriteDouble(size_t count,const double * p)2776 ON_BinaryArchive::WriteDouble(  // Write a single double
2777 		size_t count,       // number of doubles
2778 		const double* p
2779 		)
2780 {
2781   bool rc = true;
2782   if ( m_endian == ON::big_endian ) {
2783     if ( count > 0 ) {
2784       const char* b = (const char*)p;
2785       while ( rc && count-- ) {
2786         rc = WriteByte( 1, b+7 );
2787         if (rc) rc = WriteByte( 1, b+6 );
2788         if (rc) rc = WriteByte( 1, b+5 );
2789         if (rc) rc = WriteByte( 1, b+4 );
2790         if (rc) rc = WriteByte( 1, b+3 );
2791         if (rc) rc = WriteByte( 1, b+2 );
2792         if (rc) rc = WriteByte( 1, b+1 );
2793         if (rc) rc = WriteByte( 1, b );
2794         b += 8;
2795       }
2796     }
2797   }
2798   else {
2799     rc = WriteByte( count<<3, p );
2800   }
2801   return rc;
2802 }
2803 
2804 bool
WriteComponentIndex(const ON_COMPONENT_INDEX & ci)2805 ON_BinaryArchive::WriteComponentIndex(
2806 		const ON_COMPONENT_INDEX& ci
2807 		)
2808 {
2809   bool rc = WriteInt( ci.m_type );
2810   if (rc)
2811     rc = WriteInt( ci.m_index );
2812   // do not add additional writes - you will break old file IO
2813   return rc;
2814 }
2815 
2816 bool
ReadComponentIndex(ON_COMPONENT_INDEX & ci)2817 ON_BinaryArchive::ReadComponentIndex(
2818 		ON_COMPONENT_INDEX& ci
2819 		)
2820 {
2821   int t;
2822   ci.m_type = ON_COMPONENT_INDEX::invalid_type;
2823   ci.m_index = 0;
2824   bool rc = ReadInt( &t );
2825   if (rc)
2826   {
2827     rc = ReadInt( &ci.m_index );
2828     if (rc)
2829     {
2830       ci.m_type = ON_COMPONENT_INDEX::Type(t);
2831     }
2832     // do not add additional read - you will break old file IO
2833   }
2834   return rc;
2835 }
2836 
2837 bool
WriteDouble(const double x)2838 ON_BinaryArchive::WriteDouble(  // Write a single double
2839 		const double x
2840 		)
2841 {
2842   return WriteDouble( 1, &x );
2843 }
2844 
2845 bool
WriteColor(const ON_Color & color)2846 ON_BinaryArchive::WriteColor( const ON_Color& color )
2847 {
2848   unsigned int colorref = color;
2849   return WriteInt( colorref );
2850 }
2851 
2852 bool
WritePoint(const ON_2dPoint & p)2853 ON_BinaryArchive::WritePoint (
2854   const ON_2dPoint& p
2855   )
2856 {
2857   return WriteDouble( 2, &p.x );
2858 }
2859 
2860 bool
WritePoint(const ON_3dPoint & p)2861 ON_BinaryArchive::WritePoint (
2862   const ON_3dPoint& p
2863   )
2864 {
2865   return WriteDouble( 3, &p.x );
2866 }
2867 
2868 bool
WritePoint(const ON_4dPoint & p)2869 ON_BinaryArchive::WritePoint (
2870   const ON_4dPoint& p
2871   )
2872 {
2873   return WriteDouble( 4, &p.x );
2874 }
2875 
2876 bool
WriteVector(const ON_2dVector & v)2877 ON_BinaryArchive::WriteVector (
2878   const ON_2dVector& v
2879   )
2880 {
2881   return WriteDouble( 2, &v.x );
2882 }
2883 
2884 bool
WriteVector(const ON_3dVector & v)2885 ON_BinaryArchive::WriteVector (
2886   const ON_3dVector& v
2887   )
2888 {
2889   return WriteDouble( 3, &v.x );
2890 }
2891 
WriteDisplayMaterialRef(const ON_DisplayMaterialRef & dmr)2892 bool ON_BinaryArchive::WriteDisplayMaterialRef( const ON_DisplayMaterialRef& dmr )
2893 {
2894   bool rc = WriteUuid( dmr.m_viewport_id );
2895   if (rc) rc = WriteUuid( dmr.m_display_material_id );
2896   return rc;
2897 }
2898 
2899 bool
WriteUuid(const ON_UUID & uuid)2900 ON_BinaryArchive::WriteUuid( const ON_UUID& uuid )
2901 {
2902   bool    rc = WriteInt32( 1, (const ON__INT32*)(&uuid.Data1) );
2903   if (rc) rc = WriteInt16( 1, (const ON__INT16*)(&uuid.Data2) );
2904   if (rc) rc = WriteInt16( 1, (const ON__INT16*)(&uuid.Data3) );
2905   if (rc) rc = WriteByte( 8, uuid.Data4 );
2906   return rc;
2907 }
2908 
2909 bool
WriteTime(const struct tm & utc)2910 ON_BinaryArchive::WriteTime( const struct tm& utc )
2911 {
2912   // utc = coordinated universal time ( a.k.a GMT, UTC )
2913   // (From ANSI C time() and gmtime().)
2914   // The checks are here so we can insure files don't contain
2915   // garbage dates and ReadTime() can treat out of bounds
2916   // values as file corruption errors.
2917   int i;
2918   i = (int)utc.tm_sec;  if ( i < 0 || i > 60 ) i = 0;
2919   bool rc = WriteInt( i );
2920   i = (int)utc.tm_min;  if ( i < 0 || i > 60 ) i = 0;
2921   if ( rc )
2922     rc = WriteInt( i );
2923   i = (int)utc.tm_hour;  if ( i < 0 || i > 24 ) i = 0;
2924   if ( rc )
2925     rc = WriteInt( i );
2926   i = (int)utc.tm_mday;  if ( i < 0 || i > 31 ) i = 0;
2927   if ( rc )
2928     rc = WriteInt( i );
2929   i = (int)utc.tm_mon;  if ( i < 0 || i > 12 ) i = 0;
2930   if ( rc )
2931     rc = WriteInt( i );
2932 
2933   // no year restrictions because dates are used in archeological userdata
2934   i = (int)utc.tm_year;
2935   if ( rc )
2936     rc = WriteInt( i );
2937 
2938   i = (int)utc.tm_wday;  if ( i < 0 || i > 7 ) i = 0;
2939   if ( rc )
2940     rc = WriteInt( i );
2941   i = (int)utc.tm_yday;  if ( i < 0 || i > 366 ) i = 0;
2942   if ( rc )
2943     rc = WriteInt( i );
2944   return rc;
2945 }
2946 
2947 bool
WriteString(const char * sUTF8)2948 ON_BinaryArchive::WriteString( // Write NULL terminated UTF-8 encoded unicode string
2949     const char* sUTF8
2950     )
2951 {
2952   size_t string_utf8_element_count = 0;
2953   if ( sUTF8 )
2954   {
2955     while(sUTF8[string_utf8_element_count])
2956       string_utf8_element_count++;
2957     if ( string_utf8_element_count )
2958       string_utf8_element_count++;
2959   }
2960   ON__UINT32 ui32 = (ON__UINT32)string_utf8_element_count;
2961   bool rc = WriteInt32(1,(ON__INT32*)&ui32);
2962   if ( rc && string_utf8_element_count > 0 )
2963     rc = WriteByte( string_utf8_element_count, sUTF8 );
2964   return rc;
2965 }
2966 
2967 bool
WriteString(const unsigned char * sUTF8)2968 ON_BinaryArchive::WriteString( // Write NULL terminated UTF-8 encoded unicode string
2969     const unsigned char* sUTF8
2970     )
2971 {
2972   return WriteString( (const char*)sUTF8 );
2973 }
2974 
2975 bool
WriteString(const unsigned short * sUTF16)2976 ON_BinaryArchive::WriteString(  // Write NULL terminated UTF-16 encoded unicode string
2977     const unsigned short* sUTF16
2978     )
2979 {
2980   size_t string_utf16_element_count = 0;
2981   if ( sUTF16 )
2982   {
2983     while(sUTF16[string_utf16_element_count])
2984       string_utf16_element_count++;
2985     if ( string_utf16_element_count )
2986       string_utf16_element_count++;
2987   }
2988   ON__UINT32 ui32 = (ON__UINT32)string_utf16_element_count;
2989   bool rc = WriteInt32(1,(ON__INT32*)&ui32);
2990   if ( rc && string_utf16_element_count > 0 )
2991   {
2992     rc = WriteShort( string_utf16_element_count, sUTF16 );
2993   }
2994   return rc;
2995 }
2996 
2997 bool
WriteString(const ON_String & sUTF8)2998 ON_BinaryArchive::WriteString( const ON_String& sUTF8 )
2999 {
3000   size_t string_utf8_element_count = sUTF8.Length();
3001   if ( string_utf8_element_count )
3002     string_utf8_element_count++;
3003   ON__UINT32 ui32 = (ON__UINT32)string_utf8_element_count;
3004   bool rc = WriteInt32(1,(ON__INT32*)&ui32);
3005   if ( rc && string_utf8_element_count > 0 )
3006     rc = WriteByte( string_utf8_element_count, sUTF8.Array() );
3007   return rc;
3008 }
3009 
3010 bool
WriteString(const ON_wString & s)3011 ON_BinaryArchive::WriteString( const ON_wString& s )
3012 {
3013 #if defined(ON_COMPILER_MSC)
3014 #pragma warning( push )
3015 // Disable the MSC /W4 "conditional expression is constant" warning
3016 // about 2 == sizeof(wchar_t).  Since this code has to run on machines
3017 // where sizeof(wchar_t) can be 2, 4, or ... bytes, the test is necessary.
3018 #pragma warning( disable : 4127 )
3019 #endif
3020 
3021   size_t string_element_count = s.Length();
3022   if ( string_element_count > 0)
3023     string_element_count++;
3024   bool rc = false;
3025   if ( string_element_count <= 1 )
3026   {
3027     ON__UINT32 ui32 = 0;
3028     rc = WriteInt32(1,(ON__INT32*)&ui32);
3029   }
3030   else if ( 2 == sizeof(wchar_t) && string_element_count > 0 )
3031   {
3032     ON__UINT32 ui32 = (ON__UINT32)string_element_count;
3033     rc = WriteInt32(1,(ON__INT32*)&ui32);
3034     if (rc)
3035       rc = WriteInt16( string_element_count, (const ON__INT16*)s.Array() );
3036   }
3037   else if ( 4 == sizeof(wchar_t) && string_element_count > 0 )
3038   {
3039     // Assume the string is UTF-32 encoded (this is the case for some gcc implementations).
3040     const int bTestByteOrder = false;
3041     const ON__UINT32* sUTF32 = (const ON__UINT32*)s.Array();
3042     const int sUTF32_count = (int)(string_element_count-1);
3043     const unsigned int error_mask = 0xFFFFFFFF;
3044     const ON__UINT32 error_code_point = 0xFFFD;
3045     unsigned int error_status = 0;
3046 
3047     const int sUTF16_count = ON_ConvertUTF32ToUTF16(
3048       bTestByteOrder,
3049       sUTF32,
3050       sUTF32_count,
3051       0, // ON__UINT16* sUTF16,
3052       0, // int sUTF16_count,
3053       &error_status,
3054       error_mask,
3055       error_code_point,
3056       0 // const ON__UINT32** sNextUTF32
3057       );
3058 
3059     if ( sUTF16_count > 0 )
3060     {
3061       error_status = 0;
3062       ON_SimpleArray<ON__UINT16> utf16_buffer(sUTF16_count+1);
3063       utf16_buffer.SetCount(sUTF16_count+1);
3064       const int sUTF16_count1 = ON_ConvertUTF32ToUTF16(
3065         bTestByteOrder,
3066         sUTF32,
3067         sUTF32_count,
3068         utf16_buffer.Array(),
3069         utf16_buffer.Count(),
3070         &error_status,
3071         error_mask,
3072         error_code_point,
3073         0 // const ON__UINT32** sNextUTF32
3074         );
3075       if ( sUTF16_count1 == sUTF16_count )
3076       {
3077         utf16_buffer[sUTF16_count] = 0;
3078         const ON__UINT32 ui32 = (ON__UINT32)(sUTF16_count+1);
3079         rc = WriteInt32(1,(const ON__INT32*)&ui32);
3080         if ( rc && ui32 > 0 )
3081           rc = WriteInt16( ui32, (const ON__INT16*)utf16_buffer.Array() );
3082       }
3083     }
3084   }
3085   return rc;
3086 
3087 #if defined(ON_COMPILER_MSC)
3088 #pragma warning( pop )
3089 #endif
3090 }
3091 
WriteArray(const ON_SimpleArray<bool> & a)3092 bool ON_BinaryArchive::WriteArray( const ON_SimpleArray<bool>& a )
3093 {
3094 #if defined(ON_COMPILER_MSC)
3095 #pragma warning( push )
3096 // Disable the MSC /W4 "conditional expression is constant" warning
3097 // about sizeof(*c) == sizeof(*b).  Since this code has to run on machines
3098 // where sizeof(bool) can be 1, 2, 4, or 8 bytes, the test is necessary.
3099 #pragma warning( disable : 4127 )
3100 #endif
3101 
3102   int count = a.Count();
3103   if ( count < 0 )
3104     count = 0;
3105   bool rc = WriteInt( count );
3106 
3107   if ( rc && count > 0 )
3108   {
3109     char* p = 0;
3110     const char* c = 0;
3111     const bool* b = a.Array();
3112     if ( sizeof(*c) == sizeof(*b) )
3113     {
3114       // 8 bit "bool" on this compiler
3115       c = (char*)(b);
3116     }
3117     else if ( b )
3118     {
3119       // bigger "bool" on this compiler
3120       p = (char*)onmalloc(count*sizeof(*p));
3121       int i;
3122       for ( i = 0; i < count; i++ )
3123         p[i] = (b[i]?1:0);
3124       c = p;
3125     }
3126     rc = WriteChar( count, c );
3127     if ( p )
3128       onfree(p);
3129   }
3130 
3131   return rc;
3132 
3133 #if defined(ON_COMPILER_MSC)
3134 #pragma warning( pop )
3135 #endif
3136 }
3137 
3138 bool
WriteArray(const ON_SimpleArray<char> & a)3139 ON_BinaryArchive::WriteArray( const ON_SimpleArray<char>& a )
3140 {
3141   int count = a.Count();
3142   if ( count < 0 )
3143     count = 0;
3144   bool rc = WriteInt( count );
3145   if ( rc && count > 0 ) {
3146     rc = WriteChar( count, a.Array() );
3147   }
3148   return rc;
3149 }
3150 
3151 bool
WriteArray(const ON_SimpleArray<short> & a)3152 ON_BinaryArchive::WriteArray( const ON_SimpleArray<short>& a )
3153 {
3154   int count = a.Count();
3155   if ( count < 0 )
3156     count = 0;
3157   bool rc = WriteInt( count );
3158   if ( rc && count > 0 ) {
3159     rc = WriteShort( count, a.Array() );
3160   }
3161   return rc;
3162 }
3163 
3164 bool
WriteArray(const ON_SimpleArray<int> & a)3165 ON_BinaryArchive::WriteArray( const ON_SimpleArray<int>& a )
3166 {
3167   int count = a.Count();
3168   if ( count < 0 )
3169     count = 0;
3170   bool rc = WriteInt( count );
3171   if ( rc && count > 0 ) {
3172     rc = WriteInt( count, a.Array() );
3173   }
3174   return rc;
3175 }
3176 
3177 bool
WriteArray(const ON_SimpleArray<float> & a)3178 ON_BinaryArchive::WriteArray( const ON_SimpleArray<float>& a )
3179 {
3180   int count = a.Count();
3181   if ( count < 0 )
3182     count = 0;
3183   bool rc = WriteInt( count );
3184   if ( rc && count > 0 ) {
3185     rc = WriteFloat( count, a.Array() );
3186   }
3187   return rc;
3188 }
3189 
3190 bool
WriteArray(const ON_SimpleArray<double> & a)3191 ON_BinaryArchive::WriteArray( const ON_SimpleArray<double>& a )
3192 {
3193   int count = a.Count();
3194   if ( count < 0 )
3195     count = 0;
3196   bool rc = WriteInt( count );
3197   if ( rc && count > 0 ) {
3198     rc = WriteDouble( count, a.Array() );
3199   }
3200   return rc;
3201 }
3202 
3203 bool
WriteArray(const ON_SimpleArray<ON_Color> & a)3204 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_Color>& a )
3205 {
3206   int count = a.Count();
3207   if ( count < 0 )
3208     count = 0;
3209   bool rc = WriteInt( count );
3210   if ( rc && count > 0 ) {
3211     rc = WriteInt( count, (int*)a.Array() );
3212   }
3213   return rc;
3214 }
3215 
3216 bool
WriteArray(const ON_SimpleArray<ON_2dPoint> & a)3217 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_2dPoint>& a )
3218 {
3219   int count = a.Count();
3220   if ( count < 0 )
3221     count = 0;
3222   bool rc = WriteInt( count );
3223   if ( rc && count > 0 ) {
3224     rc = WriteDouble( count*2, &a.Array()->x );
3225   }
3226   return rc;
3227 }
3228 
3229 bool
WriteArray(const ON_SimpleArray<ON_3dPoint> & a)3230 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_3dPoint>& a )
3231 {
3232   int count = a.Count();
3233   if ( count < 0 )
3234     count = 0;
3235   bool rc = WriteInt( count );
3236   if ( rc && count > 0 ) {
3237     rc = WriteDouble( count*3, &a.Array()->x );
3238   }
3239   return rc;
3240 }
3241 
3242 bool
WriteArray(const ON_SimpleArray<ON_4dPoint> & a)3243 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_4dPoint>& a )
3244 {
3245   int count = a.Count();
3246   if ( count < 0 )
3247     count = 0;
3248   bool rc = WriteInt( count );
3249   if ( rc && count > 0 ) {
3250     rc = WriteDouble( count*4, &a.Array()->x );
3251   }
3252   return rc;
3253 }
3254 
3255 bool
WriteArray(const ON_SimpleArray<ON_2dVector> & a)3256 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_2dVector>& a )
3257 {
3258   int count = a.Count();
3259   if ( count < 0 )
3260     count = 0;
3261   bool rc = WriteInt( count );
3262   if ( rc && count > 0 ) {
3263     rc = WriteDouble( count*2, &a.Array()->x );
3264   }
3265   return rc;
3266 }
3267 
3268 
3269 bool
WriteArray(const ON_SimpleArray<ON_3dVector> & a)3270 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_3dVector>& a )
3271 {
3272   int count = a.Count();
3273   if ( count < 0 )
3274     count = 0;
3275   bool rc = WriteInt( count );
3276   if ( rc && count > 0 ) {
3277     rc = WriteDouble( count*3, &a.Array()->x );
3278   }
3279   return rc;
3280 }
3281 
3282 
3283 bool
WriteArray(const ON_SimpleArray<ON_Xform> & a)3284 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_Xform>& a )
3285 {
3286   int count = a.Count();
3287   if ( count < 0 )
3288     count = 0;
3289   bool rc = WriteInt( count );
3290   if ( rc && count > 0 )
3291   {
3292     int i;
3293     for ( i = 0; i < count && rc; i++ )
3294       rc = WriteXform(a[i]);
3295   }
3296   return rc;
3297 }
3298 
3299 bool
WriteArray(const ON_SimpleArray<ON_2fPoint> & a)3300 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_2fPoint>& a )
3301 {
3302   int count = a.Count();
3303   if ( count < 0 )
3304     count = 0;
3305   bool rc = WriteInt( count );
3306   if ( rc && count > 0 ) {
3307     rc = WriteFloat( count*2, &a.Array()->x );
3308   }
3309   return rc;
3310 }
3311 
3312 bool
WriteArray(const ON_SimpleArray<ON_3fPoint> & a)3313 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_3fPoint>& a )
3314 {
3315   int count = a.Count();
3316   if ( count < 0 )
3317     count = 0;
3318   bool rc = WriteInt( count );
3319   if ( rc && count > 0 ) {
3320     rc = WriteFloat( count*3, &a.Array()->x );
3321   }
3322   return rc;
3323 }
3324 
3325 bool
WriteArray(const ON_SimpleArray<ON_4fPoint> & a)3326 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_4fPoint>& a )
3327 {
3328   int count = a.Count();
3329   if ( count < 0 )
3330     count = 0;
3331   bool rc = WriteInt( count );
3332   if ( rc && count > 0 ) {
3333     rc = WriteFloat( count*4, &a.Array()->x );
3334   }
3335   return rc;
3336 }
3337 
3338 bool
WriteArray(const ON_SimpleArray<ON_2fVector> & a)3339 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_2fVector>& a )
3340 {
3341   int count = a.Count();
3342   if ( count < 0 )
3343     count = 0;
3344   bool rc = WriteInt( count );
3345   if ( rc && count > 0 ) {
3346     rc = WriteFloat( count*2, &a.Array()->x );
3347   }
3348   return rc;
3349 }
3350 
3351 bool
WriteArray(const ON_SimpleArray<ON_3fVector> & a)3352 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_3fVector>& a )
3353 {
3354   int count = a.Count();
3355   if ( count < 0 )
3356     count = 0;
3357   bool rc = WriteInt( count );
3358   if ( rc && count > 0 ) {
3359     rc = WriteFloat( count*3, &a.Array()->x );
3360   }
3361   return rc;
3362 }
3363 
3364 
3365 bool
WriteArray(const ON_SimpleArray<ON_SurfaceCurvature> & a)3366 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_SurfaceCurvature>& a )
3367 {
3368   int count = a.Count();
3369   if ( count < 0 )
3370     count = 0;
3371   bool rc = WriteInt( count );
3372   if ( rc && count > 0 ) {
3373     rc = WriteDouble( count*2, &a.Array()->k1 );
3374   }
3375   return rc;
3376 }
3377 
3378 bool
WriteArray(const ON_SimpleArray<ON_ClippingPlaneInfo> & a)3379 ON_BinaryArchive::WriteArray( const ON_SimpleArray<ON_ClippingPlaneInfo>& a )
3380 {
3381   int count = a.Count();
3382   if ( count < 0 )
3383     count = 0;
3384   bool rc = WriteInt( count );
3385   for ( int i = 0; i < count && rc ; i++ )
3386   {
3387     rc = a[i].Write(*this);
3388   }
3389   return rc;
3390 }
3391 
3392 
3393 /////////////////////////////////////////////////////////////////////////
3394 /////////////////////////////////////////////////////////////////////////
3395 /////////////////////////////////////////////////////////////////////////
3396 
3397 bool
WriteObject(const ON_Object * o)3398 ON_BinaryArchive::WriteObject( const ON_Object* o )
3399 {
3400   bool rc = false;
3401   if ( o )
3402     rc = WriteObject(*o);
3403   else {
3404     // NULL object has nil UUID and no date
3405     rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS, 0 );
3406     if (rc) {
3407       rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_UUID, 0 );
3408       if ( rc ) {
3409         rc = WriteUuid( ON_nil_uuid );
3410         if ( !EndWrite3dmChunk() ) // end of TCODE_OPENNURBS_CLASS_UUID chunk
3411           rc = false;
3412       }
3413       if ( !EndWrite3dmChunk() )
3414         rc = false;
3415     }
3416   }
3417   return rc;
3418 }
3419 
3420 static
IsCriticalUserData(const ON_BinaryArchive & ar,const ON_UserData * ud)3421 bool IsCriticalUserData( const ON_BinaryArchive& ar, const ON_UserData* ud )
3422 {
3423   // {31F55AA3-71FB-49f5-A975-757584D937FF}
3424   static const ON_UUID ON_MeshNgonUserData_ID =
3425   { 0x31F55AA3, 0x71FB, 0x49f5, { 0xA9, 0x75, 0x75, 0x75, 0x84, 0xD9, 0x37, 0xFF } };
3426 
3427   // Userdata that must be saved even when userdata saving is "off".
3428   // Please discuss any changes with Dale Lear.  In particular,
3429   // "critical" is used in a narrow sense and modifying this
3430   // function to save in form of plug-in's user data saved is
3431   // not appropriate.  The definition of the user data class must
3432   // be in the opennurbs library and its purpose must be to extend
3433   // a core opennurbs data structure, usually because it was the
3434   // only way to add information to core opennurbs data structure
3435   // and not break the public C++ SDK.
3436   if ( 0 == ud )
3437     return false;
3438 
3439   switch( ar.Archive3dmVersion() )
3440   {
3441   case 50:
3442     if ( ON_opennurbs5_id == ud->m_application_uuid )
3443     {
3444       // As of 30 August 2012, all the core opennurbs user data
3445       // classes with an application id of ON_opennurbs5_id are
3446       // deemed "critical".
3447       return true;
3448     }
3449 
3450     if ( ON_opennurbs4_id == ud->m_application_uuid )
3451     {
3452       // ON_MeshNgonUserData is the only "critical" core
3453       // opennurbs user data with an application id of
3454       // ON_opennurbs4_id.
3455       if ( ON_MeshNgonUserData_ID == ud->m_userdata_uuid )
3456         return true;
3457     }
3458     break;
3459 
3460   case 4:
3461     if ( ON_opennurbs4_id == ud->m_application_uuid )
3462     {
3463       // ON_MeshNgonUserData is the only "critical" core
3464       // opennurbs user data with an application id of
3465       // ON_opennurbs4_id.
3466       if ( ON_MeshNgonUserData_ID == ud->m_userdata_uuid )
3467         return true;
3468     }
3469     break;
3470   }
3471 
3472   return false;
3473 }
3474 
3475 static
HasCriticalUserData(const ON_BinaryArchive & ar,const ON_Object * obj)3476 bool HasCriticalUserData( const ON_BinaryArchive& ar, const ON_Object* obj )
3477 {
3478   if ( 0 == obj )
3479     return false;
3480 
3481   for ( const ON_UserData* ud = obj->FirstUserData(); 0 != ud; ud = ud->Next() )
3482   {
3483     if ( IsCriticalUserData(ar,ud) )
3484       return true;
3485   }
3486 
3487   return false;
3488 }
3489 
3490 static
IsCoreUserData(const ON_UserData * ud)3491 bool IsCoreUserData( const ON_UserData* ud )
3492 {
3493   // Userdata with IO code we trust.
3494   if ( 0 == ud )
3495     return false;
3496   if (    ud->m_application_uuid == ON_rhino4_id
3497        || ud->m_application_uuid == ON_rhino5_id
3498        || ud->m_application_uuid == ON_rhino_id
3499        || ud->m_application_uuid == ON_opennurbs4_id
3500        || ud->m_application_uuid == ON_opennurbs5_id
3501        || ud->m_application_uuid == ON_opennurbs_id
3502       )
3503   {
3504     return true;
3505   }
3506   return false;
3507 }
3508 
3509 bool
WriteObject(const ON_Object & o)3510 ON_BinaryArchive::WriteObject( const ON_Object& o )
3511 {
3512   // writes polymorphic object derived from ON_Object in a way that
3513   // it can be recreated from ON_BinaryArchive::ReadObject
3514   ON_UUID uuid;
3515   bool rc = false;
3516   const ON_ClassId* pID = o.ClassId();
3517   if ( !pID ) {
3518     ON_ERROR("ON_BinaryArchive::WriteObject() o.ClassId() returned NULL.");
3519     return false;
3520   }
3521   uuid = pID->Uuid();
3522 
3523   if ( m_3dm_version <= 2 )
3524   {
3525     if ( ON_Curve::Cast(&o) && !ON_NurbsCurve::Cast(&o) )
3526     {
3527       ON_NurbsCurve nc;
3528       const ON_Curve* curve = static_cast<const ON_Curve*>(&o);
3529       if ( curve->GetNurbForm(nc) )
3530         return WriteObject( nc );
3531     }
3532     else if ( ON_Surface::Cast(&o) && !ON_NurbsSurface::Cast(&o) )
3533     {
3534       ON_NurbsSurface ns;
3535       const ON_Surface* surface = static_cast<const ON_Surface*>(&o);
3536       if ( surface->GetNurbForm(ns) )
3537         return WriteObject( ns );
3538     }
3539     else if( ON_Annotation2::Cast(&o))
3540     {
3541       // 6-23-03 lw added for writing annotation to v2 files
3542       ON_Annotation2* pA = (ON_Annotation2*)&o;
3543       switch( pA->Type())
3544       {
3545       case ON::dtNothing:
3546         break;
3547       case ON::dtDimLinear:
3548       case ON::dtDimAligned:
3549         {
3550           ON_LinearDimension dim;
3551           ((ON_LinearDimension2*)pA)->GetV2Form( dim);
3552           return WriteObject( dim);
3553         }
3554       case ON::dtDimAngular:
3555         {
3556           ON_AngularDimension dim;
3557           ((ON_AngularDimension2*)pA)->GetV2Form( dim);
3558           return WriteObject( dim);
3559         }
3560       case ON::dtDimDiameter:
3561       case ON::dtDimRadius:
3562         {
3563           ON_RadialDimension dim;
3564           ((ON_RadialDimension2*)pA)->GetV2Form( dim);
3565           return WriteObject( dim);
3566         }
3567       case ON::dtLeader:
3568         {
3569           ON_Leader leader;
3570           ((ON_Leader2*)pA)->GetV2Form( leader);
3571           return WriteObject( leader);
3572         }
3573       case ON::dtTextBlock:
3574         {
3575           ON_TextEntity text;
3576           ((ON_TextEntity2*)pA)->GetV2Form( text);
3577           return WriteObject( text);
3578         }
3579       case ON::dtDimOrdinate:
3580         // no V2 form of ordinate dimensions
3581         //  Fall through and save it in v4 format.
3582         //  It will be skipped by the v2 and v3 readers
3583         //  but users won't loose the ordinate dimensions
3584         //  when they encounter the situation described in
3585         //  the next comment.
3586         break;
3587       }
3588 
3589       // 7 August 2003 Dale Lear
3590       //    I commented out the "return false;".
3591       //    It is imporant to fall through to the working
3592       //    code because people overwrite their "good" files
3593       //    with a V2 version and loose everything when the
3594       //    writing code returns false.  By falling through,
3595       //    V2 will still read what it can from the file and
3596       //    V3 will read the entire file.
3597       //return false;
3598     }
3599   }
3600 
3601   if ( m_3dm_version <= 4 && ON::extrusion_object == o.ObjectType() )
3602   {
3603     // 29 September 2010 Dale Lear
3604     //   ON_Extrusion was added in V5.  It must be translated
3605     //   to a brep or surface to save it in a V4 file.
3606     const ON_Extrusion* extrusion = ON_Extrusion::Cast(&o);
3607     if ( 0 != extrusion )
3608     {
3609       ON_Object* v4object = 0;
3610       if ( extrusion->IsCapped() || extrusion->ProfileCount() >= 2 )
3611         v4object = extrusion->BrepForm(0);
3612       if ( 0 == v4object )
3613         v4object = extrusion->SumSurfaceForm(0);
3614       if ( 0 == v4object )
3615         v4object = extrusion->NurbsSurface(0);
3616       if ( 0 != v4object
3617            && ON::extrusion_object != v4object->ObjectType() // no infinte recursion!
3618          )
3619       {
3620         // Some plug-in userdata code is not robust enough to check
3621         // archive version numbers and correctly handle save as v4
3622         // changes.  The upshot is that, extrusion user data
3623         // has to be lost when saving as V4.
3624         //v4object->MoveUserData(const_cast<ON_Object&>(o));
3625         rc = WriteObject( v4object );
3626         //const_cast<ON_Object&>(o).MoveUserData(*v4object);
3627         delete v4object;
3628         return rc;
3629       }
3630       if ( 0 != v4object )
3631         delete v4object;
3632     }
3633   }
3634 
3635   rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS, 0 );
3636   if (rc) {
3637 
3638     // TCODE_OPENNURBS_CLASS_UUID chunk contains class's UUID
3639     rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_UUID, 0 );
3640     if ( rc ) {
3641       rc = WriteUuid( uuid );
3642       if ( !EndWrite3dmChunk() ) // end of TCODE_OPENNURBS_CLASS_UUID chunk
3643         rc = false;
3644     }
3645 
3646     // TCODE_OPENNURBS_CLASS_DATA chunk contains definition of class
3647     if ( rc ) {
3648       rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_DATA, 0 );
3649       if (rc)
3650       {
3651         rc = o.Write( *this )?true:false;
3652         if ( !rc ) {
3653           ON_ERROR("ON_BinaryArchive::WriteObject() o.Write() failed.");
3654         }
3655         if ( !EndWrite3dmChunk() ) // end of TCODE_OPENNURBS_CLASS_DATA chunk
3656           rc = false;
3657       }
3658     }
3659 
3660     if (rc && (m_bSaveUserData || HasCriticalUserData(*this,&o) ) )
3661     {
3662       // write user data.  Each piece of user data is in a
3663       // TCODE_OPENNURBS_CLASS_USERDATA chunk.
3664       rc = WriteObjectUserData(o);
3665     }
3666 
3667     // TCODE_OPENNURBS_CLASS_END chunk marks end of class record
3668     if ( BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_END, 0 ) ) {
3669       if ( !EndWrite3dmChunk() )
3670         rc = false;
3671     }
3672     else
3673       rc = false;
3674 
3675     if ( !EndWrite3dmChunk() ) // end of TCODE_OPENNURBS_CLASS chunk
3676       rc = false;
3677   }
3678 
3679   return rc;
3680 }
3681 
WriteObjectUserData(const ON_Object & object)3682 bool ON_BinaryArchive::WriteObjectUserData( const ON_Object& object )
3683 {
3684   if ( m_3dm_version < 3 )
3685   {
3686     // no user data is saved in V1 and V2 files.
3687     return true;
3688   }
3689 
3690   // writes user data attached to object.
3691   bool rc = true;
3692   const ON_UserData* ud;
3693   ON_UUID userdata_classid;
3694 
3695   for (ud = object.m_userdata_list; ud && rc; ud = ud->m_userdata_next)
3696   {
3697     if ( !ud->Archive() )
3698     {
3699       continue;
3700     }
3701 
3702     if ( false == m_bSaveUserData )
3703     {
3704       // user data is not being saved unless it is functioning
3705       // as a critcial extension of a core opennurbs data
3706       // structure.
3707       if ( false == IsCriticalUserData(*this,ud) )
3708         continue;
3709     }
3710 
3711 
3712     // LOTS of tests to weed out bogus user data
3713     if ( 0 == ON_UuidCompare( ud->m_userdata_uuid, ON_nil_uuid ) )
3714       continue;
3715     if ( &object != ud->m_userdata_owner )
3716       continue;
3717     const ON_ClassId* cid = ud->ClassId();
3718     if ( 0 == cid )
3719       continue;
3720     if ( cid == &ON_UserData::m_ON_UserData_class_id )
3721       continue;
3722     if ( cid == &ON_UserData::m_ON_Object_class_id )
3723       continue;
3724 
3725     // The UserDataClassUuid() function is used instead of
3726     // calling cid->Uuid() so we get the value of the
3727     // plug-in's class id when the plug-in is not loaded
3728     // and ud is ON_UnknownUserData.
3729     userdata_classid = ud->UserDataClassUuid();
3730     if ( 0 == ON_UuidCompare( userdata_classid, ON_nil_uuid ) )
3731       continue;
3732     if ( 0 == ON_UuidCompare( userdata_classid, ON_UserData::m_ON_UserData_class_id.Uuid() ) )
3733       continue;
3734     if ( 0 == ON_UuidCompare( userdata_classid, ON_Object::m_ON_Object_class_id.Uuid() ) )
3735       continue;
3736     if ( 0 == ON_UuidCompare( userdata_classid, ON_UnknownUserData::m_ON_Object_class_id.Uuid() ) )
3737       continue;
3738 
3739     if ( 3 == m_3dm_version )
3740     {
3741       // When saving a V3 archive and the user data is not
3742       // native V3 data, make sure the plug-in supports
3743       // writing V3 user data.
3744       if ( m_V3_plugin_id_list.BinarySearch( &ud->m_application_uuid, ON_UuidCompare ) < 0 )
3745         continue;
3746     }
3747 
3748     if ( ON_UuidIsNil( ud->m_application_uuid ) )
3749     {
3750       // As of version 200909190 - a non-nil application_uuid is
3751       // required in order for user data to be saved in a
3752       // 3dm archive.
3753       ON_Error(__FILE__,__LINE__,"Not saving %s userdata - m_application_uuid is nil.",cid->ClassName());
3754       continue;
3755     }
3756 
3757     // See if we have unknown user data (goo) and make sure
3758     // IsUnknownUserData() agrees with ON_UnknownUserData::Cast().
3759     const ON_UnknownUserData* unknown_ud = ON_UnknownUserData::Cast(ud);
3760     if ( 0 == unknown_ud )
3761     {
3762       if ( ud->IsUnknownUserData() )
3763       {
3764         ON_ERROR("ON_UnknownUserData::Cast(ud) is null and ud->IsUnknownUserData() is true.");
3765         continue; // something's wrong
3766       }
3767     }
3768     else
3769     {
3770       if ( !ud->IsUnknownUserData() )
3771       {
3772         ON_ERROR("ON_UnknownUserData::Cast(ud) is not null and ud->IsUnknownUserData() is false.");
3773         continue; // something's wrong
3774       }
3775     }
3776 
3777     if ( 0 != unknown_ud )
3778     {
3779       if ( false == m_bSaveUserData )
3780         continue; // "unknown" user data cannot be "critical"
3781 
3782       if ( unknown_ud->m_3dm_version <= 3 )
3783         continue; // Unknown will not be resaved in V3 archives
3784 
3785       if ( unknown_ud->m_3dm_version > 5 && unknown_ud->m_3dm_version < 50 )
3786         continue;
3787 
3788       if ( unknown_ud->m_3dm_opennurbs_version < 200701010 )
3789         continue;
3790 
3791       if ( unknown_ud->m_3dm_version >= 50 && m_3dm_version < 50 )
3792       {
3793         // Unknown userdata with 8 byte chunk lengths cannot be
3794         // saved into a V4 file with 4 byte chunk lengths because
3795         // the resulting chunk will be unreadable in V4.
3796         // This is not an error condition.  It is a consequence
3797         // of V4 IO code not being robust enough to handle
3798         // 8 bytes chunk lengths.
3799         continue;
3800       }
3801     }
3802 
3803     // Each piece of user data is inside of
3804     // a TCODE_OPENNURBS_CLASS_USERDATA chunk.
3805     rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_USERDATA, 0 );
3806     if (rc) {
3807       rc = Write3dmChunkVersion(2,2);
3808       // wrap user data header info in an TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk
3809       rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_USERDATA_HEADER, 0 );
3810       if (rc)
3811       {
3812         if ( rc ) rc = WriteUuid( userdata_classid );
3813         if ( rc ) rc = WriteUuid( ud->m_userdata_uuid );
3814         if ( rc ) rc = WriteInt( ud->m_userdata_copycount );
3815         if ( rc ) rc = WriteXform( ud->m_userdata_xform );
3816 
3817         // added for version 2.1
3818         if ( rc ) rc = WriteUuid( ud->m_application_uuid );
3819 
3820         // added for version 2.2 - 14, October 2009
3821         if ( rc )
3822         {
3823           rc = WriteBool( unknown_ud ? true : false );
3824           int ver = unknown_ud ? unknown_ud->m_3dm_version : m_3dm_version;
3825           rc = WriteInt( ver );
3826           ver = unknown_ud ? unknown_ud->m_3dm_opennurbs_version : m_3dm_opennurbs_version;
3827           if (rc) rc = WriteInt( ver );
3828         }
3829 
3830         if ( !EndWrite3dmChunk() )
3831           rc = false;
3832       }
3833       if (rc)
3834       {
3835         // wrap user data in an anonymous chunk
3836         rc = BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 );
3837         if ( rc )
3838         {
3839           if ( 0 != unknown_ud )
3840           {
3841             // 22 January 2004 Dale Lear
3842             //   Disable crc checking when writing the
3843             //   unknow user data block.
3844             //   This has to be done so we don't get an extra
3845             //   32 bit CRC calculated on the block that
3846             //   ON_UnknownUserData::Write() writes.  The
3847             //   original 32 bit crc is at the end of this
3848             //   block and will be checked when the class
3849             //   that wrote this user data is present.
3850             //   The EndWrite3dmChunk() will reset the
3851             //   CRC checking flags to the appropriate
3852             //   values.
3853             m_chunk.Last()->m_do_crc16 = 0;
3854             m_chunk.Last()->m_do_crc32 = 0;
3855             m_bDoChunkCRC = false;
3856           }
3857           rc = ud->Write(*this)?true:false;
3858           if ( !EndWrite3dmChunk() )
3859             rc = false;
3860         }
3861       }
3862       if ( !EndWrite3dmChunk() )
3863         rc = false;
3864     }
3865   }
3866   return rc;
3867 }
3868 
3869 int
LoadUserDataApplication(ON_UUID application_id)3870 ON_BinaryArchive::LoadUserDataApplication( ON_UUID application_id )
3871 {
3872   // This is a virtual function.
3873   // Rhino overrides this function to load plug-ins.
3874   return 0;
3875 }
3876 
ReadObject(ON_Object ** ppObject)3877 int ON_BinaryArchive::ReadObject( ON_Object** ppObject )
3878 {
3879   if ( !ppObject )
3880   {
3881     ON_ERROR("ON_BinaryArchive::ReadObject() called with NULL ppObject.");
3882     return 0;
3883   }
3884   *ppObject = 0;
3885   return ReadObjectHelper(ppObject);
3886 }
3887 
ReadObject(ON_Object & object)3888 int ON_BinaryArchive::ReadObject( ON_Object& object )
3889 {
3890   ON_Object* pObject = &object;
3891   return ReadObjectHelper(&pObject);
3892 }
3893 
ReadObjectHelper(ON_Object ** ppObject)3894 int ON_BinaryArchive::ReadObjectHelper( ON_Object** ppObject )
3895 {
3896   // returns 0: failure - unable to read object because of file IO problems
3897   //         1: success
3898   //         3: unable to read object because it's UUID is not registered
3899   //            this could happen in cases where old code is attempting to read
3900   //            new objects.
3901   ON__UINT32 tcode;
3902   ON__INT64 length_TCODE_OPENNURBS_CLASS = 0;
3903   ON__INT64 length_TCODE_OPENNURBS_CLASS_UUID = 0;
3904   ON__INT64 length_TCODE_OPENNURBS_CLASS_DATA = 0;
3905   ON_UUID uuid;
3906   const ON_ClassId* pID = 0;
3907   ON_Object* pObject = *ppObject; // If not null, use input
3908   int rc = 0;
3909   const ON__INT64 sizeof_chunk_header = (ON__INT64)(4 + SizeofChunkLength());
3910   const ON__INT64 expected_length_TCODE_OPENNURBS_CLASS_UUID = 20;
3911 
3912   //bool bBogusUserData = false;
3913 
3914 
3915   // all ON_Objects written by WriteObject are in a TCODE_OPENNURBS_CLASS chunk
3916   rc = BeginRead3dmBigChunk( &tcode, &length_TCODE_OPENNURBS_CLASS );
3917   if ( !rc )
3918     return 0;
3919 
3920   // When a NULL ON_Object is written, the file has
3921   //
3922   //  TCODE_OPENNURBS_CLASS, length = 20 + sizeof_chunk_header
3923   //  TCODE_OPENNURBS_CLASS_UUID, length = 20
3924   //    16 byte nil uuid
3925   //    4 byte TCODE_OPENNURBS_CLASS_UUID crc
3926   //
3927   // When a non-NULL ON_Object is written, the file has
3928   //
3929   //  TCODE_OPENNURBS_CLASS, length = 20 + 3*sizeof_chunk_header + length_DATA_chunk + length_USER_DATA_chunk(s)
3930   //
3931   //    TCODE_OPENNURBS_CLASS_UUID, length = 20
3932   //      16 byte nil uuid
3933   //      4 byte TCODE_OPENNURBS_CLASS_UUID crc
3934   //
3935   //    TCODE_OPENNURBS_CLASS_DATA, length_DATA >= 4
3936   //      ...
3937   //      4 byte TCODE_OPENNURBS_CLASS_DATA crc
3938   //
3939   //    Optional TCODE_OPENNURBS_CLASS_USERDATA chunks
3940   //
3941   //    TCODE_OPENNURBS_CLASS_END, chunk value = 0
3942 
3943   if ( tcode != TCODE_OPENNURBS_CLASS )
3944   {
3945     ON_ERROR("ON_BinaryArchive::ReadObject() didn't find TCODE_OPENNURBS_CLASS block.");
3946     rc = 0;
3947   }
3948   else if ( length_TCODE_OPENNURBS_CLASS < expected_length_TCODE_OPENNURBS_CLASS_UUID + sizeof_chunk_header)
3949   {
3950     ON_ERROR("ON_BinaryArchive::ReadObject() TCODE_OPENNURBS_CLASS chunk length too small.");
3951     rc = 0;
3952   }
3953   else
3954   {
3955     // we break out of this loop if something bad happens
3956     for (;;)
3957     {
3958       // read class's UUID ///////////////////////////////////////////////////////////
3959       rc = BeginRead3dmBigChunk( &tcode, &length_TCODE_OPENNURBS_CLASS_UUID );
3960       if ( !rc )
3961         break;
3962       if ( tcode != TCODE_OPENNURBS_CLASS_UUID )
3963       {
3964         ON_ERROR("ON_BinaryArchive::ReadObject() didn't find TCODE_OPENNURBS_CLASS_UUID block");
3965         rc = 0;
3966       }
3967       else if ( expected_length_TCODE_OPENNURBS_CLASS_UUID != length_TCODE_OPENNURBS_CLASS_UUID )
3968       {
3969         ON_ERROR("ON_BinaryArchive::ReadObject() TCODE_OPENNURBS_CLASS_UUID has invalid length");
3970         rc = 0;
3971       }
3972       else if ( !ReadUuid( uuid ) )
3973       {
3974         rc = 0;
3975       }
3976       if ( !EndRead3dmChunk() )
3977       {
3978         rc = 0;
3979       }
3980       if ( !rc ) {
3981         break;
3982       }
3983       ///////////////////////////////////////////////////////////////////////////////
3984 
3985       if ( !ON_UuidCompare( &uuid, &ON_nil_uuid ) ) {
3986         // nil UUID written if NULL pointer is passed to WriteObject();
3987         rc = 1;
3988         break;
3989       }
3990 
3991       // Use UUID to get ON_ClassId for this class //////////////////////////////////
3992       if ( pObject )
3993       {
3994         pID = pObject->ClassId();
3995         if ( uuid != pID->Uuid() )
3996         {
3997           ON_ERROR("ON_BinaryArchive::ReadObject() - uuid does not match intput pObject's class id.");
3998           pID = 0;
3999           rc = 2;
4000           break;
4001         }
4002       }
4003       else
4004       {
4005         pID = ON_ClassId::ClassId( uuid );
4006       }
4007       if ( !pID )
4008       {
4009         // If you get here and you are not calling ON::Begin() at some point in your
4010         // executable, then call ON::Begin() to force all class definition to be linked.
4011         // If you are callig ON::Begin(), then either the uuid is garbage or you are
4012         // attempting to read an object with old code.
4013         // Visit http://www.opennurbs.org to get the latest OpenNURBS code.
4014         ON_WARNING("ON_BinaryArchive::ReadObject() ON_ClassId::ClassId(uuid) returned NULL.");
4015         rc = 3;
4016         break;
4017       }
4018       ///////////////////////////////////////////////////////////////////////////////
4019 
4020       // read class's definitions   /////////////////////////////////////////////////
4021       rc = BeginRead3dmBigChunk( &tcode, &length_TCODE_OPENNURBS_CLASS_DATA );
4022       if ( !rc )
4023         break;
4024       if ( tcode != TCODE_OPENNURBS_CLASS_DATA )
4025       {
4026         ON_ERROR("ON_BinaryArchive::ReadObject() didn't find TCODE_OPENNURBS_CLASS_DATA block");
4027         rc = 0;
4028       }
4029       else if ( length_TCODE_OPENNURBS_CLASS_DATA <= 0  )
4030       {
4031         ON_ERROR("ON_BinaryArchive::ReadObject() TCODE_OPENNURBS_CLASS_DATA chunk length too small");
4032         rc = 0;
4033       }
4034       else
4035       {
4036         if ( !pObject )
4037         {
4038           pObject = pID->Create();
4039         }
4040 
4041         if ( !pObject )
4042         {
4043           ON_ERROR("ON_BinaryArchive::ReadObject() pID->Create() returned NULL.");
4044           rc = 0;
4045         }
4046         else
4047         {
4048           rc = pObject->Read(*this);
4049           if ( !rc )
4050           {
4051             ON_ERROR("ON_BinaryArchive::ReadObject() pObject->Read() failed.");
4052             delete pObject;
4053             // don't break here - we still need to call end chunk.
4054           }
4055           else
4056           {
4057             *ppObject = pObject;
4058           }
4059         }
4060       }
4061       if ( !EndRead3dmChunk() )
4062       {
4063         rc = 0;
4064       }
4065 
4066       if ( rc && 0 != pObject )
4067       {
4068         // read user data  /////////////////////////////////////////////////
4069         //   If TCODE_OPENNURBS_CLASS_USERDATA chunks exist, this reads them.
4070         //   ReadObjectUserData() stops when it reads a TCODE_OPENNURBS_CLASS_END chunk.
4071         if (!ReadObjectUserData(*pObject))
4072           rc = 0;
4073       }
4074 
4075       break;
4076     }
4077   }
4078   if ( !EndRead3dmChunk() ) // TCODE_OPENNURBS_CLASS
4079     rc = 0;
4080 
4081   return rc;
4082 }
4083 
ReadObjectUserDataAnonymousChunk(const ON__UINT64 length_TCODE_ANONYMOUS_CHUNK,const int archive_3dm_version,const int archive_opennurbs_version,ON_UserData * ud)4084 bool ON_BinaryArchive::ReadObjectUserDataAnonymousChunk(
4085           const ON__UINT64 length_TCODE_ANONYMOUS_CHUNK,
4086           const int archive_3dm_version,
4087           const int archive_opennurbs_version,
4088           ON_UserData* ud )
4089 {
4090   // Reads the portion of the file containing the userdata into a buffer
4091   // and lets the plug-in try to read from that.  If the plug-in fails,
4092   // we press on because we cannot trust plug-ins to get IO code right.
4093   bool rc = false;
4094 
4095   if ( 0 == ud )
4096     return false;
4097 
4098   if ( ud->IsUnknownUserData()
4099        || (archive_3dm_version == Archive3dmVersion()
4100            && archive_opennurbs_version == ArchiveOpenNURBSVersion()
4101            && IsCoreUserData(ud))
4102      )
4103   {
4104     // assume this userdata's read function is robust.
4105     ON_ReadChunkHelper ch(*this);
4106     if (    !ch.m_bReadSuccess
4107          || TCODE_ANONYMOUS_CHUNK != ch.m_chunk_tcode
4108          || length_TCODE_ANONYMOUS_CHUNK != (ON__UINT64)ch.m_chunk_value
4109        )
4110     {
4111       return false;
4112     }
4113     if ( ud->IsUnknownUserData() )
4114     {
4115       // 22 January 2004 Dale Lear:
4116       //   Disable CRC checking while reading this chunk.
4117       //   (If the user data has nested chunks, the crc we get
4118       //   by reading the thing as one large chunk will be wrong.)
4119       m_chunk.Last()->m_do_crc16 = false;
4120       m_chunk.Last()->m_do_crc32 = false;
4121       m_bDoChunkCRC = false;
4122     }
4123     rc = ud->Read(*this) ? true : false;
4124   }
4125   else
4126   {
4127     // Untrusted plug-in userdata.
4128     // Insulate file reading from possible bugs plug-in IO code by reading
4129     // entire anonymous chunk into memory and letting the plug-in use
4130     // the memory buffer archive.
4131     unsigned char stack_buffer[2048];
4132     const size_t sizeof_buffer = (size_t)(length_TCODE_ANONYMOUS_CHUNK + 4 + SizeofChunkLength());
4133     void* freeme = 0;
4134     void* buffer = (sizeof_buffer <= sizeof(stack_buffer))
4135                  ? &stack_buffer[0]
4136                  : (freeme = onmalloc(sizeof_buffer)); // generally, object userdata is small we almost never use heap
4137     if ( 0 != buffer
4138          && sizeof_buffer == ReadBuffer(sizeof_buffer,buffer)
4139        )
4140     {
4141       ON_Read3dmBufferArchive memory_archive(
4142         sizeof_buffer,
4143         buffer,
4144         false,
4145         archive_3dm_version,
4146         archive_opennurbs_version
4147         );
4148 
4149       // The TCODE_ANONYMOUS_CHUNK wrapper has chunk lengths set
4150       // by whatever version wrote this file.  The information
4151       // in the chunk has chunk lengths set by the plug-in that
4152       // originally wrote the user data.  If the plug-in used
4153       // worte to a version <= 5 archive and the user data has
4154       // was read as goo and saved as goo in a version 50+
4155       // archive, then we need to tweak the archive version
4156       // when reading the chunk length of the TCODE_ANONYMOUS_CHUNK wrapper.
4157       bool bTweakArchiveVersion = (memory_archive.SizeofChunkLength() != SizeofChunkLength());
4158       if ( bTweakArchiveVersion )
4159         memory_archive.SetArchive3dmVersion(Archive3dmVersion());
4160       ON_ReadChunkHelper ch(memory_archive);
4161       if ( bTweakArchiveVersion )
4162         memory_archive.SetArchive3dmVersion(archive_3dm_version);
4163 
4164       if (    !ch.m_bReadSuccess
4165            || TCODE_ANONYMOUS_CHUNK != ch.m_chunk_tcode
4166            || length_TCODE_ANONYMOUS_CHUNK != (ON__UINT64)ch.m_chunk_value
4167          )
4168         rc = false;
4169       else
4170         rc = ud->Read(memory_archive) ? true : false;
4171     }
4172     if ( freeme )
4173       onfree(freeme);
4174   }
4175   return rc;
4176 }
4177 
4178 
4179 class CUserDataHeaderInfo
4180 {
4181 public:
4182   CUserDataHeaderInfo();
4183 
4184   void Initialize();
4185 
4186   ON_UUID m_classid;
4187   ON_UUID m_itemid;
4188   ON_UUID m_appid;
4189   int m_3dm_version;
4190   int m_3dm_opennurbs_version;
4191   int m_copycount;
4192   bool m_bLastSavedAsGoo;
4193   ON_Xform m_xform;
4194 };
4195 
CUserDataHeaderInfo()4196 CUserDataHeaderInfo::CUserDataHeaderInfo()
4197 {
4198   Initialize();
4199 }
4200 
Initialize()4201 void CUserDataHeaderInfo::Initialize()
4202 {
4203   memset(this,0,sizeof(*this));
4204 }
4205 
4206 static
ReadObjectUserDataHeaderHelper(ON_BinaryArchive & binary_archive,const int major_userdata_version,const int minor_userdata_version,CUserDataHeaderInfo & ud_header)4207 bool ReadObjectUserDataHeaderHelper(
4208           ON_BinaryArchive& binary_archive,
4209           const int major_userdata_version,
4210           const int minor_userdata_version,
4211           CUserDataHeaderInfo& ud_header
4212           )
4213 {
4214   bool rc = true;
4215   ON__UINT32 t = 0;
4216   ON__INT64 length_TCODE_OPENNURBS_CLASS_USERDATA_HEADER = 0;
4217 
4218   ud_header.Initialize();
4219 
4220   if ( major_userdata_version == 2 )
4221   {
4222     // major_userdata_version 2 started wrapping the userdata header info
4223     // in a TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk
4224     rc = binary_archive.BeginRead3dmBigChunk( &t, &length_TCODE_OPENNURBS_CLASS_USERDATA_HEADER );
4225     if (!rc)
4226       return false;
4227     if ( t != TCODE_OPENNURBS_CLASS_USERDATA_HEADER )
4228     {
4229       ON_ERROR("version 2.0 TCODE_OPENNURBS_CLASS_USERDATA chunk is missing TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk.");
4230       binary_archive.EndRead3dmChunk(); // end of mystery chunk
4231       return false;
4232     }
4233   }
4234 
4235   if (rc) rc = binary_archive.ReadUuid( ud_header.m_classid );
4236   if (rc) rc = binary_archive.ReadUuid( ud_header.m_itemid );
4237   if (rc) rc = binary_archive.ReadInt( &ud_header.m_copycount );
4238   if (rc) rc = binary_archive.ReadXform( ud_header.m_xform );
4239   if ( major_userdata_version == 2 )
4240   {
4241     if ( minor_userdata_version >= 1 )
4242     {
4243       if (rc) rc = binary_archive.ReadUuid( ud_header.m_appid );
4244       if ( minor_userdata_version >= 2 )
4245       {
4246         // bLastSavedAsGoo is true if the user data was saved
4247         // into the file by ON_UnknownUserData.
4248         if (rc) rc = binary_archive.ReadBool( &ud_header.m_bLastSavedAsGoo );
4249         if (rc) rc = binary_archive.ReadInt( &ud_header.m_3dm_version );
4250         if (rc) rc = binary_archive.ReadInt( &ud_header.m_3dm_opennurbs_version );
4251       }
4252     }
4253     if ( !binary_archive.EndRead3dmChunk() ) // end of TCODE_OPENNURBS_CLASS_USERDATA_HEADER
4254       rc = 0;
4255   }
4256 
4257   if (!rc)
4258   {
4259     ON_ERROR("Unable to read user data header information.");
4260     return false;
4261   }
4262 
4263   if ( 0 == ud_header.m_3dm_version || 0 == ud_header.m_3dm_opennurbs_version )
4264   {
4265     // The userdata was saved in in an archive before
4266     // the 3dm_version and 3dm_opennurbs_version were saved in
4267     // userdata headers.  This means it has to be userdata
4268     // with 4 byte chunk lengths.  If the archive we are
4269     // reading was written with a newer version of opennurbs
4270     // that does save the 3dm version info, then this unknown
4271     // userdata was that has persisted through multiple read/write
4272     // cycles and we cannot tell it's original version. So we
4273     // will default to a maximum of 5 and 200910180 - the
4274     // 3dm versions just before we started saving 3dm
4275     // version info n userdata headers.
4276     if ( binary_archive.Archive3dmVersion() < 50 )
4277     {
4278      ud_header.m_3dm_version = binary_archive.Archive3dmVersion();
4279     }
4280     else
4281     {
4282       // All Archive3dmVersion() >= 50 have userdata_3dm_version info,
4283       // so this userdata had to be saved as goo from a version 5 or
4284       // earlier archive.
4285       ud_header.m_bLastSavedAsGoo = true;
4286       ud_header.m_3dm_version = 5;
4287     }
4288     ud_header.m_3dm_opennurbs_version = binary_archive.ArchiveOpenNURBSVersion();
4289     if ( ud_header.m_3dm_opennurbs_version >= 200910190 )
4290     {
4291       ud_header.m_3dm_opennurbs_version = 200910180;
4292       ud_header.m_bLastSavedAsGoo = true;
4293     }
4294   }
4295 
4296   return rc;
4297 }
4298 
ReadObjectUserData(ON_Object & object)4299 bool ON_BinaryArchive::ReadObjectUserData( ON_Object& object )
4300 {
4301   bool rc = true;
4302   while(rc)
4303   {
4304     ON_ReadChunkHelper ch(*this);
4305     if ( !ch.m_bReadSuccess )
4306     {
4307       rc = false;
4308       break;
4309     }
4310 
4311     if ( TCODE_OPENNURBS_CLASS_END == ch.m_chunk_tcode )
4312     {
4313       // A short TCODE_OPENNURBS_CLASS_END chunk marks the end of the opennurbs class
4314       break; // done
4315     }
4316 
4317     if ( TCODE_OPENNURBS_CLASS_USERDATA != ch.m_chunk_tcode )
4318     {
4319       // skip new chunk type added by later version
4320       continue;
4321     }
4322 
4323     if ( ch.m_chunk_value < (ON__INT64)(8 + 4 * SizeofChunkLength()) )
4324     {
4325       ON_ERROR("TCODE_OPENNURBS_CLASS_USERDATA chunk is too short");
4326       continue;
4327     }
4328 
4329     // Read userdata header information
4330     int major_userdata_version = 0;
4331     int minor_userdata_version = 0;
4332     rc = Read3dmChunkVersion( &major_userdata_version, &minor_userdata_version );
4333     if ( !rc )
4334     {
4335       ON_ERROR("Unable to read TCODE_OPENNURBS_CLASS_USERDATA chunk version numbers");
4336       break;
4337     }
4338 
4339     if ( major_userdata_version < 1 || major_userdata_version > 2 )
4340     {
4341       // unsupported version - too old or added in new version
4342       continue;
4343     }
4344 
4345     CUserDataHeaderInfo ud_header;
4346     rc = ReadObjectUserDataHeaderHelper(*this,major_userdata_version,minor_userdata_version,ud_header);
4347     if (!rc)
4348     {
4349       ON_ERROR("Unable to read user data header information.");
4350       break;
4351     }
4352 
4353     // we should be ready to read a TCODE_ANONYMOUS_CHUNK containing userdata
4354     ON__INT64 length_TCODE_ANONYMOUS_CHUNK = 0;
4355     for(;;)
4356     {
4357       ON__UINT32 t = 0;
4358       rc = PeekAt3dmBigChunkType( &t, &length_TCODE_ANONYMOUS_CHUNK );
4359       if (!rc)
4360         break;
4361       if ( t != TCODE_ANONYMOUS_CHUNK )
4362       {
4363         ON_ERROR("Reading object user data - unable to find TCODE_ANONYMOUS_CHUNK");
4364         rc = false;
4365         break;
4366       }
4367       if ( length_TCODE_ANONYMOUS_CHUNK < 4 )
4368       {
4369         ON_ERROR("Reading object user data - length of TCODE_ANONYMOUS_CHUNK < 4");
4370         rc = false;
4371         break;
4372       }
4373       break;
4374     }
4375     if (!rc)
4376     {
4377       break;
4378     }
4379 
4380     // attempt to get an instance of the userdata class that saved this information
4381     ON_UserData* ud = 0;
4382     for(;;)
4383     {
4384       const ON_ClassId* udId = ON_ClassId::ClassId( ud_header.m_classid );
4385       if ( 0 == udId )
4386       {
4387         // The application that created this userdata is not active
4388         if ( !ON_UuidIsNil(ud_header.m_appid) )
4389         {
4390           // see if we can load the application
4391           if ( 1 == LoadUserDataApplication(ud_header.m_appid) )
4392           {
4393             // try again
4394             udId = ON_ClassId::ClassId( ud_header.m_classid );
4395           }
4396         }
4397 
4398         if ( 0 == udId )
4399         {
4400           // The application that created this user data is
4401           // not available.  This information will be stored
4402           // in an ON_UnknownUserData class so that it can
4403           // persist.
4404           udId = &ON_UnknownUserData::m_ON_UnknownUserData_class_id;
4405         }
4406       }
4407 
4408       ON_Object* tmp = udId->Create();
4409       ud = ON_UserData::Cast(tmp);
4410       if ( 0 == ud )
4411       {
4412         ON_ERROR("Reading object user data - unable to create userdata class");
4413         if ( tmp )
4414           delete tmp;
4415         tmp = 0;
4416         break;
4417       }
4418       tmp = 0;
4419 
4420       break;
4421     }
4422 
4423     if ( 0 == ud )
4424     {
4425       // no luck on this one
4426       // One reason can be that the plug-in userdata class has something wrong with
4427       // its ON_OBJECT_DECLARE/ON_OBJECT_IMPLEMENT stuff.
4428       ON_ERROR("Unable to create object user data class. Flawed class id information.");
4429       continue; // keep trying
4430     }
4431 
4432     if ( ON_UuidIsNil(ud->m_application_uuid) )
4433     {
4434       if ( ON_UuidIsNil(ud_header.m_appid) )
4435       {
4436         switch( Archive3dmVersion())
4437         {
4438         case 2:
4439           // V2 archives do not contain app ids.
4440           // This id flags the userdata as being read from a V3 archive.
4441           ud_header.m_appid = ON_v2_userdata_id;
4442           break;
4443         case 3:
4444           // V3 archives do not contain app ids.
4445           // This id flags the userdata as being
4446           // read from a V3 archive.
4447           ud_header.m_appid = ON_v3_userdata_id;
4448           break;
4449         case 4:
4450           if ( ArchiveOpenNURBSVersion() < 200909190 )
4451           {
4452             // V4 archives before version 200909190
4453             // did not require user data application ids.
4454             ud_header.m_appid = ON_v4_userdata_id;
4455           }
4456           break;
4457         }
4458       }
4459       ud->m_application_uuid = ud_header.m_appid;
4460     }
4461     ud->m_userdata_uuid = ud_header.m_itemid;
4462     ud->m_userdata_copycount = ud_header.m_copycount;
4463     ud->m_userdata_xform = ud_header.m_xform;
4464     if ( ud->IsUnknownUserData() )
4465     {
4466       ON_UnknownUserData* uud = ON_UnknownUserData::Cast(ud);
4467       if ( uud )
4468       {
4469         uud->m_sizeof_buffer = (int)length_TCODE_ANONYMOUS_CHUNK;
4470         uud->m_unknownclass_uuid = ud_header.m_classid;
4471         uud->m_3dm_version = ud_header.m_3dm_version;
4472         uud->m_3dm_opennurbs_version = ud_header.m_3dm_opennurbs_version;
4473       }
4474     }
4475     ud->m_userdata_owner = &object; // so reading code can look at owner
4476     bool bReadUserData = ReadObjectUserDataAnonymousChunk(
4477               length_TCODE_ANONYMOUS_CHUNK,
4478               ud_header.m_3dm_version,
4479               ud_header.m_3dm_opennurbs_version,
4480               ud
4481               );
4482     ud->m_userdata_owner = 0;
4483     if (bReadUserData)
4484     {
4485       if ( !object.AttachUserData( ud ) )
4486         delete ud;
4487     }
4488     else
4489     {
4490       delete ud;
4491     }
4492   }
4493 
4494   return rc;
4495 }
4496 
Write3dmChunkVersion(int major_version,int minor_version)4497 bool ON_BinaryArchive::Write3dmChunkVersion(
4498   int major_version, // major // 0 to 15
4499   int minor_version // minor // 0 to 16
4500   )
4501 {
4502   const unsigned char v = (unsigned char)(major_version*16+minor_version);
4503   return WriteChar( v );
4504 }
4505 
Read3dmChunkVersion(int * major_version,int * minor_version)4506 bool ON_BinaryArchive::Read3dmChunkVersion(
4507   int* major_version, // major // 0 to 15
4508   int* minor_version // minor // 0 to 16
4509   )
4510 {
4511   unsigned char v = 0;
4512   bool rc = ReadChar( &v );
4513   if ( minor_version) *minor_version = v%16;
4514   // The bit shift happens on the fly in the following
4515   // if statement.  It was happening twice which always
4516   // set the major version to 0
4517   //v >>= 4;
4518   if ( major_version) *major_version = (v>>4);
4519   return rc;
4520 }
4521 
Archive3dmVersion() const4522 int ON_BinaryArchive::Archive3dmVersion() const
4523 {
4524   return m_3dm_version;
4525 }
4526 
ArchiveOpenNURBSVersion() const4527 int ON_BinaryArchive::ArchiveOpenNURBSVersion() const
4528 {
4529   return m_3dm_opennurbs_version;
4530 }
4531 
ArchiveStartOffset() const4532 size_t ON_BinaryArchive::ArchiveStartOffset() const
4533 {
4534   return m_3dm_start_section_offset;
4535 }
4536 
4537 /////////////////////////////////////////////////////////////////////////
4538 /////////////////////////////////////////////////////////////////////////
4539 /////////////////////////////////////////////////////////////////////////
4540 
4541 bool
BeginWrite3dmChunk(unsigned int typecode,int value)4542 ON_BinaryArchive::BeginWrite3dmChunk( unsigned int typecode, int value )
4543 {
4544 /*// G+Smo
4545   ON__INT64 value64 = 0;
4546   if ( 0 != value )
4547   {
4548     if ( ON_IsUnsignedChunkTypecode(typecode) )
4549     {
4550       // treat value parameter as an unsigned int
4551       ON__UINT32 u32 = (ON__UINT32)value;
4552       ON__UINT64 u64 = u32;
4553       value64 = (ON__INT64)u64;
4554     }
4555     else
4556     {
4557       // treat value paramter is a signed int
4558       value64 = value;
4559     }
4560   }
4561 */
4562   return BeginWrite3dmBigChunk(typecode,value);
4563 }
4564 
4565 bool
BeginWrite3dmBigChunk(ON__UINT32 typecode,ON__INT64 value64)4566 ON_BinaryArchive::BeginWrite3dmBigChunk( ON__UINT32 typecode, ON__INT64 value64 )
4567 {
4568   m_bDoChunkCRC = false; // no CRC on chunks because length is written twice.
4569   bool rc = WriteInt32( 1, (ON__INT32*)&typecode );
4570   if (rc)
4571     rc = WriteChunkValue( typecode, value64 );
4572   if (rc)
4573     rc = PushBigChunk( typecode, value64 );
4574   return rc;
4575 }
4576 
4577 bool
BeginWrite3dmChunk(unsigned int tcode,int major_version,int minor_version)4578 ON_BinaryArchive::BeginWrite3dmChunk(
4579           unsigned int tcode,
4580           int major_version,
4581           int minor_version
4582           )
4583 {
4584   bool rc = false;
4585   if ( 0 == tcode )
4586   {
4587     ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input tcode = 0");
4588   }
4589   else if ( 0 != (tcode & TCODE_SHORT) )
4590   {
4591     ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input tcode has short flag set.");
4592   }
4593   else if ( major_version <= 0 )
4594   {
4595     ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input major_version <= 0.");
4596   }
4597   else if ( minor_version < 0 )
4598   {
4599     ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input minor_version < 0.");
4600   }
4601   else
4602   {
4603     rc = BeginWrite3dmChunk(tcode,0);
4604     if (rc)
4605     {
4606       rc = WriteInt(major_version);
4607       if (rc)
4608         rc = WriteInt(minor_version);
4609       if ( !rc)
4610         EndWrite3dmChunk();
4611     }
4612   }
4613   return rc;
4614 }
4615 
4616 bool
EndWrite3dmChunk()4617 ON_BinaryArchive::EndWrite3dmChunk()
4618 {
4619   bool rc = false;
4620   ON_3DM_BIG_CHUNK* c = m_chunk.Last();
4621   if ( c ) {
4622     if ( c->m_bLongChunk )
4623     {
4624       if ( c->m_do_crc16 )
4625       {
4626         // write 16 bit CRC
4627         unsigned char two_zero_bytes[2] = {0,0};
4628         ON__UINT16 crc = ON_CRC16( c->m_crc16, 2, two_zero_bytes );
4629         rc = WriteInt16( 1, (ON__INT16*)&crc );
4630         if (c->m_crc16)
4631         {
4632           // should never happen unless ON_CRC16() code is damaged
4633           m_bad_CRC_count++;
4634           ON_ERROR("ON_BinaryArchive::EndWrite3dmChunk: CRC16 computation error.");
4635         }
4636       }
4637       else if ( c->m_do_crc32 )
4638       {
4639         // write 32 bit CRC
4640         const ON__UINT32 crc0 = c->m_crc32;
4641         rc = WriteInt32( 1, (ON__INT32*)&crc0 );
4642       }
4643       else {
4644         rc = true;
4645       }
4646 
4647       // write length
4648       m_bDoChunkCRC = 0;
4649       const size_t offset = CurrentPosition();
4650       if ( offset < c->m_big_offset )
4651       {
4652         ON_ERROR("ON_BinaryArchive::EndWrite3dmChunk() - chunk length < 0");
4653         rc = false;
4654       }
4655       else
4656       {
4657         ON__UINT64 length = (offset - c->m_big_offset);
4658         if ( !BigSeekBackward( length + SizeofChunkLength() ) )
4659         {
4660           rc = false;
4661         }
4662         else
4663         {
4664           if ( !WriteChunkLength( length ) )
4665           {
4666             rc = false;
4667           }
4668           if ( !BigSeekForward( length ) )
4669           {
4670             rc = false;
4671           }
4672         }
4673         if ( CurrentPosition() != offset )
4674         {
4675           ON_ERROR("ON_BinaryArchive::EndWrite3dmChunk() - CurrentPosition() != offset");
4676           rc = false;
4677         }
4678       }
4679     }
4680     else
4681     {
4682       // "short" chunks are completely written by call to BeginWrite3dmChunk().
4683       rc = true;
4684     }
4685 
4686     m_chunk.Remove();
4687     c = m_chunk.Last();
4688     if ( !c )
4689     {
4690       Flush();
4691     }
4692     m_bDoChunkCRC = c && (c->m_do_crc16 || c->m_do_crc32);
4693   }
4694   return rc;
4695 }
4696 
Write3dmGoo(const ON_3dmGoo & goo)4697 bool ON_BinaryArchive::Write3dmGoo( const ON_3dmGoo& goo )
4698 {
4699   bool rc = false;
4700 
4701   if ( goo.m_typecode ) {
4702     const bool savedDoCRC = m_bDoChunkCRC;
4703     m_bDoChunkCRC = false;
4704     if ( 0 != (goo.m_typecode & TCODE_SHORT) ) {
4705       if ( goo.m_value == 0 || (goo.m_value > 0 && goo.m_goo) ) {
4706         // write long chunk - do not use Begin/EndWrite3dmChunk() because
4707         //                    goo may contain subchunks and CRC would be
4708         //                    incorrectly computed.
4709         rc = WriteInt( goo.m_typecode );
4710         if (rc) rc = WriteInt( goo.m_value );
4711         if (rc && goo.m_value>0) rc = WriteByte( goo.m_value, goo.m_goo );
4712       }
4713     }
4714     else {
4715       // write short chunk
4716       rc = WriteInt( goo.m_typecode );
4717       if (rc) rc = WriteInt( goo.m_value );
4718     }
4719     m_bDoChunkCRC = savedDoCRC;
4720   }
4721 
4722   return rc;
4723 }
4724 
PeekAt3dmChunkType(unsigned int * typecode,int * value)4725 bool ON_BinaryArchive::PeekAt3dmChunkType( unsigned int* typecode, int* value )
4726 {
4727   // does not change file position
4728   bool rc;
4729   unsigned int tc = 0;
4730   ON__INT64 i64 = 0;
4731   rc = PeekAt3dmBigChunkType(&tc,&i64);
4732   if ( rc )
4733   {
4734     if ( 0 != typecode )
4735       *typecode = tc;
4736     if ( 0 != value )
4737     {
4738       ON__INT32 i32 = 0;
4739       if ( ON_IsUnsignedChunkTypecode(tc) )
4740         rc = DownSizeUINT((ON__UINT64)i64,(ON__UINT32*)&i32);
4741       else
4742         rc = DownSizeINT(i64,&i32);
4743       *value = i32;
4744     }
4745   }
4746   return rc;
4747 }
4748 
PeekAt3dmBigChunkType(unsigned int * typecode,ON__INT64 * big_value)4749 bool ON_BinaryArchive::PeekAt3dmBigChunkType(
4750       unsigned int* typecode,
4751       ON__INT64* big_value
4752       )
4753 {
4754   // does not change file position
4755 
4756   // 10 January 2005 Dale Lear
4757   //     Do not let the "peeking" affect the CRC.
4758   bool bDoChunkCRC = m_bDoChunkCRC;
4759   m_bDoChunkCRC = false;
4760 
4761   const ON__UINT64 pos0 = CurrentPosition();
4762   ON__UINT32 t = 0;
4763   ON__INT64 v = 0;
4764   bool rc = ReadChunkTypecode( &t );
4765   if (rc)
4766   {
4767     rc = ReadChunkValue( t, &v );
4768   }
4769   const ON__UINT64 pos1 = CurrentPosition();
4770   if ( pos1 > pos0 && !BigSeekBackward( pos1-pos0 ) )
4771   {
4772     rc = false;
4773   }
4774 
4775   m_bDoChunkCRC = bDoChunkCRC;
4776 
4777   if ( typecode )
4778     *typecode = t;
4779   if ( big_value )
4780     *big_value = v;
4781 
4782   return rc;
4783 }
4784 
4785 
Seek3dmChunkFromStart(unsigned int typecode)4786 bool ON_BinaryArchive::Seek3dmChunkFromStart(
4787       // beginning at the start of the active chunk, search portion of
4788       // archive included in active chunk for the start of a subchunk
4789       // with the specified type.
4790       // if true is returned, then the position is set so the next call to
4791       // BeginRead3dmChunk() will read a chunk with the specified typecode
4792       unsigned int typecode   // typecode from opennurbs_3dm.h
4793       )
4794 {
4795   bool rc = false;
4796   if ( ReadMode() )
4797   {
4798     const ON__UINT64 pos0 = CurrentPosition();
4799     const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
4800     if ( c )
4801     {
4802       // set archive position to the beginning of this chunk
4803       if ( !ON_IsLongChunkTypecode(c->m_typecode) )
4804       {
4805         ON_ERROR("ON_BinaryArchive::Seek3dmChunkFromStart() - current chunk is not a long chunk");
4806         return false;
4807       }
4808       if ( c->m_big_value < 0 )
4809       {
4810         ON_ERROR("ON_BinaryArchive::Seek3dmChunkFromStart() called with an active chunk that has m_value < 0");
4811         return false;
4812       }
4813       if ( pos0 < c->m_big_offset || pos0 > c->m_big_offset + c->Length() )
4814       {
4815         ON_ERROR("ON_BinaryArchive::Seek3dmChunkFromStart() called with out of bounds current position");
4816         return false;
4817       }
4818       rc = BigSeekBackward( pos0 - c->m_big_offset ); // pos0 >= c->m_offset, so this size_t subtraction is ok
4819     }
4820     else
4821     {
4822       // set archive position to the beginning of archive chunks by skipping
4823       // 32 byte version info and any start section padding.
4824       size_t start_offset = ((m_3dm_start_section_offset > 0) ? m_3dm_start_section_offset : 0);
4825       rc = SeekFromStart( start_offset );
4826       if (!rc && start_offset > 0)
4827       {
4828         start_offset = 0;
4829         rc = SeekFromStart(start_offset);
4830       }
4831       char s3d[33];
4832       memset(s3d,0,sizeof(s3d));
4833       if (rc)
4834         rc = ReadByte(32,s3d);
4835 
4836       if (rc)
4837       {
4838         rc = (0 == strncmp( s3d, "3D Geometry File Format ", 24));
4839         if ( !rc && start_offset > 0 )
4840         {
4841           start_offset = 0;
4842           rc = SeekFromStart(start_offset);
4843           if (rc) rc = ReadByte(32,s3d);
4844           rc = (0 == strncmp( s3d, "3D Geometry File Format ", 24));
4845         }
4846       }
4847 
4848       if (rc)
4849       {
4850         if ( start_offset != m_3dm_start_section_offset )
4851           m_3dm_start_section_offset = start_offset;
4852         unsigned int t=0;
4853         ON__INT64 v=-1;
4854         rc = PeekAt3dmBigChunkType(&t,&v);
4855         if (rc && (t != 1 || v < 0) )
4856           rc = false;
4857       }
4858     }
4859 
4860     if (rc)
4861     {
4862       rc = Seek3dmChunkFromCurrentPosition( typecode );
4863     }
4864 
4865     if (!rc)
4866     {
4867       BigSeekFromStart(pos0);
4868     }
4869   }
4870   return rc;
4871 }
4872 
Length() const4873 ON__UINT64 ON_3DM_BIG_CHUNK::Length() const
4874 {
4875   return (ON_IsLongChunkTypecode(m_typecode) && m_big_value >= 0 )
4876     ? ((ON__UINT64)m_big_value)
4877     : 0;
4878 }
4879 
Seek3dmChunkFromCurrentPosition(unsigned int typecode)4880 bool ON_BinaryArchive::Seek3dmChunkFromCurrentPosition(
4881       // beginning at the current position, search portion of archive
4882       // included in active chunk for the start of a subchunk with the
4883       // specified type.
4884       // if true is returned, then the position is set so the next call to
4885       // BeginRead3dmChunk() will read a chunk with the specified typecode
4886       unsigned int typecode // typecode from opennurbs_3dm.h
4887       )
4888 {
4889   bool rc = false;
4890   if ( ReadMode() )
4891   {
4892     const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
4893     const ON__UINT64 pos1 = c ? c->m_big_offset + c->Length() : 0;
4894     const size_t pos_start = CurrentPosition();
4895     size_t pos_prev = 0;
4896     size_t pos = 0;
4897     unsigned int t;
4898     ON__INT64 v64;
4899     bool bFirstTime = true;
4900     while(pos > pos_prev || bFirstTime)
4901     {
4902       bFirstTime = false;
4903       pos_prev = pos;
4904       pos = CurrentPosition();
4905       if ( pos1 && pos > pos1 )
4906         break;
4907       t = !typecode;
4908       if ( !PeekAt3dmBigChunkType( &t, 0 ) )
4909         break;
4910       if ( t == typecode )
4911       {
4912         rc = true;
4913         break;
4914       }
4915       if ( 0 == t )
4916       {
4917         // zero is not a valid typecode - file is corrupt at or before this position
4918         break;
4919       }
4920       if ( !BeginRead3dmBigChunk( &t, &v64 ) )
4921         break;
4922       if ( !EndRead3dmChunk() )
4923         break;
4924       if ( TCODE_ENDOFTABLE == t && 0 != v64 )
4925       {
4926         // TCODE_ENDOFTABLE chunks always have value = 0 - file is corrupt at or before this position
4927         break;
4928       }
4929     }
4930     if ( !rc )
4931     {
4932       SeekFromStart( pos_start );
4933     }
4934   }
4935   return rc;
4936 }
4937 
BeginRead3dmChunk(unsigned int * typecode,int * value)4938 bool ON_BinaryArchive::BeginRead3dmChunk( unsigned int* typecode, int* value )
4939 {
4940   ON__UINT32 tc = 0;
4941   ON__INT64 v64 = 0;
4942   bool rc = BeginRead3dmBigChunk(&tc,&v64);
4943   if (rc)
4944   {
4945     if ( 0 != typecode )
4946       *typecode = tc;
4947     rc = DownSizeChunkValue(tc,v64,value);
4948   }
4949   return rc;
4950 }
4951 
BeginRead3dmBigChunk(unsigned int * typecode,ON__INT64 * value)4952 bool ON_BinaryArchive::BeginRead3dmBigChunk( unsigned int* typecode, ON__INT64* value )
4953 {
4954   ON__UINT32 t = 0;
4955   ON__INT64 v = 0;
4956   m_bDoChunkCRC = false; // no CRC on chunk headers because length is written twice
4957   const unsigned int saved_error_message_mask = m_error_message_mask;
4958   m_error_message_mask |= 0x0001; // disable ReadByte() error message at EOF
4959   bool rc = ReadChunkTypecode( &t );
4960   m_error_message_mask = saved_error_message_mask;
4961   if (rc)
4962   {
4963     if ( t == TCODE_ENDOFFILE )
4964     {
4965       // Either this chunk is a bona fide end of file mark, or it's "goo"
4966       // that Rhino 1.0 or the pre-February 2000 Rhino 1.1 saved and wrote.
4967       ON__UINT64 sizeof_file = 0;
4968       if ( rc )
4969         rc = ReadChunkValue( t, &v );
4970       if ( rc && v >= 0 && ((ON__UINT64)v) >= SizeofChunkLength() )
4971       {
4972         ON__UINT64 EOF_chunk_length = (ON__UINT64)v;
4973         ON__UINT64 pos0 = CurrentPosition();
4974         rc = ReadEOFSizeOfFile( &sizeof_file );
4975         ON__UINT64 pos1 = CurrentPosition();
4976         if ( pos0 > 0 && pos1 > pos0 )
4977         {
4978           if ( !BigSeekBackward(pos1-pos0) )
4979             rc = false;
4980         }
4981         if ( rc )
4982         {
4983           if ( BigSeekForward( EOF_chunk_length ) )
4984           {
4985             ON__UINT64 pos2 = CurrentPosition();
4986             if ( m_3dm_version <= 1 )
4987             {
4988               if ( !AtEnd() )
4989               {
4990                 // Rhino v1 reads chunks with unknown typecodes as a block of "goo"
4991                 // and then saves them back into file.  When this happens to an
4992                 // eof marker, we get TCODE_ENDOFFILE chunks in the middle of a file.
4993                 // This can only happen in m_3dm_version = 1 files.
4994                 t = TCODE_ENDOFFILE_GOO;
4995               }
4996             }
4997             else
4998             {
4999               // check that current position matches saved file size
5000               if ( pos2 != sizeof_file ) {
5001                 ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk() - Rogue eof marker in v2 file.\n");
5002               }
5003             }
5004             rc = BigSeekBackward( EOF_chunk_length );
5005           }
5006         }
5007         if ( rc )
5008           rc = PushBigChunk( t, v );
5009       }
5010       else
5011       {
5012         ON_ERROR( "ON_BinaryArchive::BeginRead3dmChunk() - file is damaged." );
5013         rc = false;
5014         t = 0; // ?? file is trashed ??
5015       }
5016     }
5017     else
5018     {
5019       if ( rc )
5020         rc = ReadChunkValue( t, &v );
5021       if ( rc )
5022         rc = PushBigChunk( t, v );
5023     }
5024   }
5025   if ( typecode )
5026     *typecode = t;
5027   if ( value )
5028     *value = v;
5029   return rc;
5030 }
5031 
5032 bool
BeginRead3dmChunk(unsigned int expected_tcode,int * major_version,int * minor_version)5033 ON_BinaryArchive::BeginRead3dmChunk(
5034           unsigned int expected_tcode,
5035           int* major_version,
5036           int* minor_version
5037           )
5038 {
5039   bool rc = false;
5040   if ( 0 == expected_tcode )
5041   {
5042     ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input expected_tcode = 0");
5043   }
5044   else if ( 0 != (expected_tcode & TCODE_SHORT) )
5045   {
5046     ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input expected_tcode has short flag set.");
5047   }
5048   else if ( 0 == major_version )
5049   {
5050     ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input major_version NULL");
5051   }
5052   else if ( 0 == minor_version )
5053   {
5054     ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input minor_version NULL");
5055   }
5056   else
5057   {
5058     *major_version = 0;
5059     *minor_version = 0;
5060     unsigned int tcode = 0;
5061     ON__INT64 value = 0;
5062     rc = PeekAt3dmBigChunkType(&tcode,&value);
5063     if ( expected_tcode != tcode )
5064     {
5065       ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - unexpected tcode");
5066       rc = false;
5067     }
5068     else if ( value < 8 )
5069     {
5070       ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - unexpected chunk length");
5071       rc = false;
5072     }
5073     else
5074     {
5075       tcode = 0;
5076       value = 0;
5077       rc = BeginRead3dmBigChunk(&tcode,&value);
5078       if (rc)
5079       {
5080         if ( expected_tcode != tcode || value < 8 )
5081         {
5082           // can happen when seek fails
5083           ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - unexpected tcode or chunk length - archive driver or device may be bad");
5084           rc = false;
5085         }
5086         else
5087         {
5088           rc = ReadInt(major_version);
5089           if ( rc && *major_version < 1 )
5090           {
5091             ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - major_version < 1");
5092             rc = false;
5093           }
5094           if (rc)
5095           {
5096             rc = ReadInt(minor_version);
5097             if ( rc && *minor_version < 0 )
5098             {
5099               ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - minor_version < 0");
5100               rc = false;
5101             }
5102           }
5103         }
5104 
5105         if ( !rc )
5106         {
5107           // this is required to keep chunk accounting in synch
5108           EndRead3dmChunk();
5109         }
5110       }
5111     }
5112   }
5113   return rc;
5114 }
EndRead3dmChunk()5115 bool ON_BinaryArchive::EndRead3dmChunk()
5116 {
5117   return EndRead3dmChunk(false);
5118 }
5119 
EndRead3dmChunk(bool bSupressPartiallyReadChunkWarning)5120 bool ON_BinaryArchive::EndRead3dmChunk(bool bSupressPartiallyReadChunkWarning)
5121 {
5122   //int length = 0;
5123   bool rc = false;
5124   ON_3DM_BIG_CHUNK* c = m_chunk.Last();
5125   if ( c )
5126   {
5127     ON__UINT64 file_offset = CurrentPosition();
5128     ON__UINT64 end_offset = c->m_big_offset;
5129     if ( c->m_bLongChunk )
5130     {
5131       if ( c->m_big_value < 0 )
5132       {
5133         ON_ERROR("ON_BinaryArchive::EndRead3dmChunk - negative chunk length");
5134       }
5135       else
5136       {
5137         end_offset += ((ON__UINT64)c->m_big_value);
5138       }
5139     }
5140 
5141     if ( c->m_bLongChunk )
5142     {
5143       if ( c->m_do_crc16 )
5144       {
5145         if ( file_offset+2 == end_offset )
5146         {
5147           // read 16 bit CRC
5148           unsigned char two_crc_bytes[2] = {0,0};
5149           rc = ReadByte( 2, two_crc_bytes );
5150           if (rc)
5151           {
5152             file_offset+=2;
5153             if (c->m_crc16)
5154             {
5155               m_bad_CRC_count++;
5156               ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: CRC16 error.");
5157             }
5158           }
5159         }
5160         else
5161         {
5162           // partially read chunk - crc check not possible.
5163           rc = true;
5164         }
5165       }
5166       else if ( c->m_do_crc32 )
5167       {
5168         if ( file_offset+4 == end_offset )
5169         {
5170           // read 32 bit CRC
5171           ON__UINT32 crc1 = c->m_crc32;
5172           ON__UINT32 crc0;
5173           rc = ReadInt32( 1, (ON__INT32*)&crc0 );
5174           if (rc)
5175           {
5176             file_offset+=4;
5177             if (crc0 != crc1)
5178             {
5179               m_bad_CRC_count++;
5180               ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: CRC32 error.");
5181             }
5182           }
5183         }
5184         else
5185         {
5186           // partially read chunk - crc check not possible.
5187           rc = true;
5188         }
5189       }
5190       else
5191       {
5192         // no crc in this chunk
5193         rc = true;
5194       }
5195     }
5196     else
5197     {
5198       rc = true;
5199     }
5200 
5201     // check length and seek to end of chunk if things are amiss
5202     if ( file_offset < c->m_big_offset )
5203     {
5204       ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: current position before start of current chunk.");
5205       if ( !BigSeekFromStart( end_offset ) )
5206         rc = false;
5207     }
5208     else if ( file_offset > end_offset )
5209     {
5210       ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: current position after end of current chunk.");
5211       if ( !BigSeekFromStart( end_offset ) )
5212         rc = false;
5213     }
5214     else if ( file_offset != end_offset )
5215     {
5216       // partially read chunk - happens when chunks are skipped or old code
5217       // reads a new minor version of a chunk whnich has added information.
5218       if ( file_offset != c->m_big_offset )
5219       {
5220         if ( m_3dm_version != 1 || (m_error_message_mask&0x02) == 0 )
5221         {
5222           // when reading v1 files, there are some situations where
5223           // it is reasonable to attempt to read 4 bytes at the end
5224           // of a file.  The above test prevents making a call
5225           // to ON_WARNING() in these situations.
5226 
5227           const int app_opennurbs_version = ON::Version();
5228           const int filev_date = m_3dm_opennurbs_version/10;
5229           const int appv_date  = app_opennurbs_version/10;
5230           int file_v = m_3dm_opennurbs_version%10;
5231           int app_v  = app_opennurbs_version%10;
5232           if ( 9 == file_v || 9 == app_v )
5233           {
5234             // 9 means DEBUG opennurbs was used to write the file.
5235             file_v = app_v = 0;
5236           }
5237           if ( file_v <= app_v && filev_date <= appv_date )
5238           {
5239             // We are reading a file written by this version or an
5240             // earlier version of opennurbs.
5241             // There should not be any partially read chunks.
5242             if (!bSupressPartiallyReadChunkWarning)
5243             {
5244             ON_WARNING("ON_BinaryArchive::EndRead3dmChunk: partially read chunk - skipping bytes at end of current chunk.");
5245           }
5246         }
5247       }
5248       }
5249       //int delta =  (end_offset >= file_offset)
5250       //          ?  ((int)(end_offset-file_offset))
5251       //          : -((int)(file_offset-end_offset));
5252       //if ( !SeekFromCurrentPosition( delta ) )
5253       //  rc = false;
5254       if ( end_offset > file_offset )
5255       {
5256         if ( !BigSeekForward(end_offset - file_offset) )
5257           rc = false;
5258       }
5259       else if ( end_offset < file_offset )
5260       {
5261         if ( !BigSeekBackward(file_offset - end_offset) )
5262           rc = false;
5263       }
5264     }
5265 
5266     m_chunk.Remove();
5267     c = m_chunk.Last();
5268     m_bDoChunkCRC = (c && (c->m_do_crc16 || c->m_do_crc32));
5269   }
5270   return rc;
5271 }
5272 
BeginWriteDictionary(ON_UUID dictionary_id,unsigned int version,const wchar_t * dictionary_name)5273 bool ON_BinaryArchive::BeginWriteDictionary(
5274         ON_UUID dictionary_id,
5275         unsigned int version,
5276         const wchar_t* dictionary_name
5277         )
5278 {
5279 #if defined(ON_COMPILER_MSC)
5280 // Disable the MSC /W4 "conditional expression is constant" warning
5281 // about sizeof(unsigned short) == sizeof(*dictionary_name).
5282 // Since this code has to run on machines where sizeof(wchar_t)
5283 // can be 2, 4, or 8 bytes, the test is necessary.
5284 #pragma warning( push )
5285 #pragma warning( disable : 4127 )
5286 #endif
5287 
5288   bool rc = BeginWrite3dmChunk(TCODE_DICTIONARY,1,0);
5289   if ( !rc )
5290     return rc;
5291 
5292   // Write dictionary id chunk
5293   rc = BeginWrite3dmChunk(TCODE_DICTIONARY_ID,1,0);
5294   if ( rc )
5295   {
5296     for(;;)
5297     {
5298       rc = WriteUuid(dictionary_id);
5299       if (!rc) break;
5300       rc = WriteInt(version);
5301       if (!rc) break;
5302       if ( sizeof(unsigned short) == sizeof(*dictionary_name) )
5303       {
5304         rc = WriteString((const unsigned short*)dictionary_name);
5305       }
5306       else
5307       {
5308         ON_wString s(dictionary_name);
5309         rc = WriteString(s);
5310       }
5311       if (!rc) break;
5312       break;
5313     }
5314     if ( !EndWrite3dmChunk() ) // TCODE_DICTIONARY_ID end
5315       rc = false;
5316   }
5317 
5318   if ( !rc )
5319     EndWrite3dmChunk(); // TCODE_DICTIONARY end
5320   return rc;
5321 
5322 #if defined(ON_COMPILER_MSC)
5323 #pragma warning( pop )
5324 #endif
5325 }
5326 
EndWriteDictionary()5327 bool ON_BinaryArchive::EndWriteDictionary()
5328 {
5329   int chunk_count = m_chunk.Count();
5330   bool rc = ( chunk_count > 0 && TCODE_DICTIONARY == m_chunk[chunk_count-1].m_typecode );
5331   if (rc)
5332   {
5333     rc = BeginWrite3dmChunk(TCODE_DICTIONARY_END,0);
5334     if (rc)
5335       rc = EndWrite3dmChunk(); // TCODE_DICTIONARY_END
5336 
5337     if ( !EndWrite3dmChunk() ) // TCODE_DICTIONARY
5338       rc = false;
5339   }
5340   return rc;
5341 }
5342 
BeginWriteDictionaryEntry(int de_type,const wchar_t * entry_name)5343 bool ON_BinaryArchive::BeginWriteDictionaryEntry(
5344         int de_type,
5345         const wchar_t* entry_name
5346         )
5347 {
5348 #if defined(ON_COMPILER_MSC)
5349 // Disable the MSC /W4 "conditional expression is constant" warning
5350 // about sizeof(unsigned short) == sizeof(*entry_name).
5351 // Since this code has to run on machines where sizeof(wchar_t)
5352 // can be 2, 4, or 8 bytes, the test is necessary.
5353 #pragma warning( push )
5354 #pragma warning( disable : 4127 )
5355 #endif
5356 
5357   bool rc = BeginWrite3dmChunk(TCODE_DICTIONARY_ENTRY,0);
5358   if ( rc )
5359   {
5360     for(;;)
5361     {
5362       rc = WriteInt(de_type);
5363       if (!rc) break;
5364       if ( sizeof(unsigned short) == sizeof(*entry_name) )
5365       {
5366         rc = WriteString((const unsigned short*)entry_name);
5367       }
5368       else
5369       {
5370         ON_wString s(entry_name);
5371         rc = WriteString(s);
5372       }
5373       if (!rc) break;
5374       break;
5375     }
5376     if ( !rc )
5377       EndWrite3dmChunk(); // TCODE_DICTIONARY_ENTRY
5378   }
5379   return rc;
5380 
5381 #if defined(ON_COMPILER_MSC)
5382 #pragma warning( pop )
5383 #endif
5384 }
5385 
EndWriteDictionaryEntry()5386 bool ON_BinaryArchive::EndWriteDictionaryEntry()
5387 {
5388   int chunk_count = m_chunk.Count();
5389   bool rc = ( chunk_count > 0 && TCODE_DICTIONARY_ENTRY == m_chunk[chunk_count-1].m_typecode )
5390           ? EndWrite3dmChunk()
5391           : false;
5392   return rc;
5393 }
5394 
BeginReadDictionary(ON_UUID * dictionary_id,unsigned int * version,ON_wString & dictionary_name)5395 bool ON_BinaryArchive::BeginReadDictionary(
5396         ON_UUID* dictionary_id,
5397         unsigned int* version,
5398         ON_wString& dictionary_name
5399         )
5400 {
5401   int major_version = 0;
5402   int minor_version = 0;
5403   bool rc = BeginRead3dmChunk(TCODE_DICTIONARY,&major_version,&minor_version);
5404   if ( rc )
5405   {
5406     for(;;)
5407     {
5408       rc = (1 == major_version);
5409       if (!rc) break;
5410 
5411       // Read dictionary id chunk
5412       rc = BeginRead3dmChunk(TCODE_DICTIONARY_ID,&major_version,&minor_version);
5413       if ( !rc ) break;
5414       for(;;)
5415       {
5416         rc = (1==major_version);
5417         if (!rc) break;
5418         ON_UUID id;
5419         rc = ReadUuid(id);
5420         if (!rc) break;
5421         if ( dictionary_id )
5422           *dictionary_id = id;
5423         rc = ReadInt(version);
5424         if (!rc) break;
5425         rc = ReadString(dictionary_name);
5426         if (!rc) break;
5427         break;
5428       }
5429       if ( !EndRead3dmChunk() ) // TCODE_DICTIONARY_ID end
5430         rc = false;
5431       break;
5432     }
5433 
5434     if ( !rc )
5435       EndRead3dmChunk(); // TCODE_DICTIONARY end
5436   }
5437   return rc;
5438 }
5439 
EndReadDictionary()5440 bool ON_BinaryArchive::EndReadDictionary()
5441 {
5442   int chunk_count = m_chunk.Count();
5443   bool rc = ( chunk_count > 0 && TCODE_DICTIONARY == m_chunk[chunk_count-1].m_typecode )
5444           ? EndRead3dmChunk()
5445           : false;
5446   return rc;
5447 }
5448 
BeginReadDictionaryEntry(int * de_type,ON_wString & entry_name)5449 int ON_BinaryArchive::BeginReadDictionaryEntry(
5450         int* de_type,
5451         ON_wString& entry_name
5452         )
5453 {
5454   unsigned int tcode = 0;
5455   ON__INT64 chunk_length = 0;
5456   int chunk_count = m_chunk.Count();
5457   int rc = ( chunk_count > 0 && TCODE_DICTIONARY == m_chunk[chunk_count-1].m_typecode )
5458           ? (BeginRead3dmBigChunk(&tcode,&chunk_length)?1:0)
5459           : 0;
5460   if ( de_type )
5461     *de_type = 0;
5462   if ( rc )
5463   {
5464     if ( TCODE_DICTIONARY_ENTRY == tcode )
5465     {
5466       for(;;)
5467       {
5468         rc = 0;
5469         if ( !ReadInt(de_type) )
5470         {
5471           entry_name.Empty();
5472           break;
5473         }
5474         if ( !ReadString(entry_name) )
5475         {
5476           entry_name.Empty();
5477           break;
5478         }
5479         rc = 1;
5480         break;
5481       }
5482     }
5483     else
5484     {
5485       rc = (TCODE_DICTIONARY_END == tcode) ? 2 : 0;
5486     }
5487     if ( 1 != rc )
5488     {
5489       if ( !EndRead3dmChunk() )
5490         rc = 0;
5491     }
5492   }
5493   return rc;
5494 }
5495 
EndReadDictionaryEntry()5496 bool ON_BinaryArchive::EndReadDictionaryEntry()
5497 {
5498   int chunk_count = m_chunk.Count();
5499   bool rc = ( chunk_count > 0 && TCODE_DICTIONARY_ENTRY == m_chunk[chunk_count-1].m_typecode )
5500           ? EndRead3dmChunk()
5501           : false;
5502   return rc;
5503 }
5504 
5505 
5506 
Read3dmGoo(ON_3dmGoo & goo)5507 bool ON_BinaryArchive::Read3dmGoo( ON_3dmGoo& goo )
5508 {
5509   // goo is an entire "chunk" that is not short.
5510   // A call to EndRead3dmChunk() must immediately follow
5511   // the call to Read3dmGoo().
5512   bool rc = false;
5513   if (goo.m_goo)
5514   {
5515     onfree(goo.m_goo);
5516     goo.m_goo = 0;
5517   }
5518   goo.m_typecode = 0;
5519   goo.m_value = 0;
5520   ON_3DM_BIG_CHUNK* c = m_chunk.Last();
5521   if (c)
5522   {
5523     goo.m_typecode = c->m_typecode;
5524     if ( c->m_bLongChunk )
5525       rc = DownSizeUINT(c->Length(),(ON__UINT32*)&goo.m_value);
5526     else
5527       rc = DownSizeINT(c->m_big_value,&goo.m_value);
5528     if ( rc && c->m_bLongChunk && c->m_big_value > 0 )
5529     {
5530       if ( CurrentPosition() == c->m_big_offset )
5531       {
5532         // read the rest of this chunk into the goo.m_goo buffer.
5533         // 23 January 2004 Dale Lear:
5534         //   Have to turn of CRC checking because the goo may contiain
5535         //   subchunks.  If a CRC exixts, it is at the end of the
5536         //   goo and will persist until the application that
5537         //   wrote this chunk is available to parse the chunk.
5538         c->m_do_crc16 = 0;
5539         c->m_do_crc32 = 0;
5540         m_bDoChunkCRC = false;
5541         size_t sizeof_goo = (size_t)c->Length();
5542         goo.m_goo = (unsigned char*)onmalloc( sizeof_goo );
5543         rc = ReadByte( sizeof_goo, goo.m_goo );
5544       }
5545     }
5546   }
5547   return rc;
5548 }
5549 
WriteChunkTypecode(ON__UINT32 typecode)5550 bool ON_BinaryArchive::WriteChunkTypecode( ON__UINT32 typecode )
5551 {
5552   return WriteInt32(1,(ON__INT32*)&typecode);
5553 }
5554 
ReadChunkTypecode(ON__UINT32 * typecode)5555 bool ON_BinaryArchive::ReadChunkTypecode( ON__UINT32* typecode )
5556 {
5557   ON__UINT32 tc = 0;
5558   bool rc = ReadInt32(1,(ON__INT32*)&tc);
5559   if (rc && typecode )
5560     *typecode = tc;
5561   return rc;
5562 }
5563 
WriteChunkValue(ON__UINT32 typecode,ON__INT64 big_value)5564 bool ON_BinaryArchive::WriteChunkValue( ON__UINT32 typecode, ON__INT64 big_value )
5565 {
5566   bool rc;
5567   if ( 8 == SizeofChunkLength() )
5568   {
5569     rc = WriteInt64(1,&big_value);
5570   }
5571   else if ( ON_IsUnsignedChunkTypecode(typecode) )
5572   {
5573     // treat big_value as an unsigned int
5574     ON__UINT32 u32 = 0;
5575     rc = DownSizeUINT((ON__UINT64)big_value,&u32);
5576     if ( !WriteInt32(1,(ON__INT32*)&u32) )
5577       rc = false;
5578   }
5579   else
5580   {
5581     // treat big_value as a signed int
5582     ON__INT32 v32 = 0;
5583     rc = DownSizeINT(big_value,&v32);
5584     if ( !WriteInt32(1,&v32) )
5585       rc = false;
5586   }
5587   return rc;
5588 }
5589 
5590 
WriteChunkLength(ON__UINT64 length)5591 bool ON_BinaryArchive::WriteChunkLength( ON__UINT64 length )
5592 {
5593   bool rc;
5594   if ( 8 == SizeofChunkLength() )
5595   {
5596     rc = WriteInt64(1,(ON__INT64*)&length);
5597   }
5598   else
5599   {
5600     ON__UINT32 u32 = 0;
5601     rc = DownSizeUINT(length,&u32);
5602     if ( !WriteInt32(1,(ON__INT32*)&u32) )
5603       rc = false;
5604   }
5605   return rc;
5606 }
5607 
ReadEOFSizeOfFile(ON__UINT64 * sizeof_file)5608 bool ON_BinaryArchive::ReadEOFSizeOfFile( ON__UINT64* sizeof_file )
5609 {
5610   bool rc;
5611   ON__INT64 u64 = 0;
5612   if ( 8 == SizeofChunkLength() )
5613   {
5614     // file has a 8 byte file size
5615     rc = ReadInt64(1,(ON__INT64*)&u64);
5616   }
5617   else
5618   {
5619     // file has a 4 byte file size
5620     ON__UINT32 u32 = 0;
5621     rc = ReadInt32(1,(ON__INT32*)&u32);
5622     if ( rc )
5623       u64 = u32;
5624   }
5625   if ( rc && 0 != sizeof_file )
5626     *sizeof_file = u64;
5627   return rc;
5628 }
5629 
WriteEOFSizeOfFile(ON__UINT64 sizeof_file)5630 bool ON_BinaryArchive::WriteEOFSizeOfFile( ON__UINT64 sizeof_file )
5631 {
5632   bool rc;
5633   if ( 8 == SizeofChunkLength() )
5634   {
5635     // file has a 8 byte file size
5636     rc = WriteInt64(1,(ON__INT64*)&sizeof_file);
5637   }
5638   else
5639   {
5640     // file has a 4 byte file size
5641     ON__UINT32 u32=0;
5642     DownSizeUINT(sizeof_file,&u32);
5643     rc = WriteInt32(1,(ON__INT32*)&u32);
5644   }
5645   return rc;
5646 }
5647 
ReadChunkValue(ON__UINT32 typecode,ON__INT64 * value64)5648 bool ON_BinaryArchive::ReadChunkValue( ON__UINT32 typecode, ON__INT64* value64 )
5649 {
5650   bool rc;
5651   ON__INT64 i64 = 0;
5652   if ( 8 == SizeofChunkLength() )
5653   {
5654     // file has a 8 byte chunk value
5655     rc = ReadInt64(1,&i64);
5656   }
5657   else
5658   {
5659     // file has a 4 byte chunk value
5660     if ( ON_IsUnsignedChunkTypecode(typecode) )
5661     {
5662       // This Mickey Mouse is here to convince all compilers
5663       // that when we read a 4 byte value with the high bit set,
5664       // the resulting i64 value is positive. I.e.,
5665       // 0xFFFFFFFF is converted to 0x00000000FFFFFFFF
5666       ON__UINT32 u32 = 0;
5667       ON__UINT64 u64 = 0;
5668       rc = ReadInt32(1,(ON__INT32*)&u32);
5669       if ( rc )
5670         u64 = u32;
5671       i64 = (ON__INT64)u64;
5672     }
5673     else
5674     {
5675       // If we read a 4 byte value with the high bit set,
5676       // the resulting i64 value is negative. I.e.,
5677       // -1 is converted to -1 (0xFFFFFFFF to 0xFFFFFFFFFFFFFFFF)
5678       ON__INT32 i32 = 0;
5679       rc = ReadInt32(1,&i32);
5680       i64 = i32;
5681     }
5682   }
5683   if ( rc && 0 != value64 )
5684     *value64 = i64;
5685   return rc;
5686 }
5687 
SizeofChunkLength() const5688 size_t ON_BinaryArchive::SizeofChunkLength() const
5689 {
5690   // Version 1 - 4 and early version 5 files had
5691   // 4 byte chunk lengths.  In October of 2009,
5692   // 8 byte chunk lengths were phased in for V5
5693   // files.
5694   return (m_3dm_version < 50) ? 4 : 8;
5695 }
5696 
PushBigChunk(ON__UINT32 typecode,ON__INT64 big_value)5697 bool ON_BinaryArchive::PushBigChunk( ON__UINT32 typecode, ON__INT64 big_value )
5698 {
5699   ON_3DM_BIG_CHUNK c;
5700   memset(&c,0,sizeof(c));
5701   c.m_typecode  = typecode;
5702   c.m_big_value = big_value;
5703 
5704   // | and & are BITOPS - do NOT change to || and &&
5705   //
5706   // Some v1 files have a short chunk with typecode = 0.
5707   if ( 0 == ( TCODE_SHORT & typecode ) && (0 != typecode  || 1 != Archive3dmVersion()) )
5708   {
5709     if ( m_3dm_version == 1 && 0 != (TCODE_LEGACY_GEOMETRY & typecode) )
5710     {
5711       // these legacy typecodes have 16 bit CRCs
5712       c.m_do_crc16 = 1;
5713       c.m_crc16 = 1;
5714     }
5715     else
5716     {
5717       // some other legacy typecodes that have 16 bit CRCs
5718       switch(typecode)
5719       {
5720 
5721       case TCODE_SUMMARY:
5722         if ( m_3dm_version == 1 )
5723         {
5724           c.m_do_crc16 = 1;
5725           c.m_crc16 = 1;
5726         }
5727         break;
5728 
5729       case TCODE_OPENNURBS_OBJECT | TCODE_CRC | 0x7FFD:
5730         if ( m_3dm_version == 1 )
5731         {
5732           // 1.1 uuid has a 16 bit crc
5733           c.m_do_crc16 = 1;
5734           c.m_crc16 = 1;
5735         }
5736         else
5737         {
5738           // 2.0 uuid has a 32 bit crc
5739           c.m_do_crc32 = 1;
5740           c.m_crc32 = 0;
5741         }
5742         break;
5743 
5744       default:
5745         if ( m_3dm_version != 1 && 0 != (TCODE_CRC & typecode) )
5746         {
5747           // 32 bit CRC
5748           c.m_do_crc32 = 1;
5749           c.m_crc32 = 0;
5750         }
5751         break;
5752 
5753       }
5754     }
5755     c.m_bLongChunk = 1;
5756   }
5757   c.m_big_offset = CurrentPosition();
5758   m_bDoChunkCRC = c.m_do_crc16 || c.m_do_crc32;
5759 
5760   if ( m_chunk.Capacity() == 0 )
5761     m_chunk.Reserve(128);
5762   m_chunk.Append( c );
5763 
5764   return true;
5765 }
5766 
EnableSave3dmRenderMeshes(ON_BOOL32 b)5767 bool ON_BinaryArchive::EnableSave3dmRenderMeshes( ON_BOOL32  b /* default = true */ )
5768 {
5769   bool oldb = m_bSaveRenderMeshes;
5770   m_bSaveRenderMeshes = b?true:false;
5771   return oldb;
5772 }
5773 
Save3dmRenderMeshes() const5774 bool ON_BinaryArchive::Save3dmRenderMeshes() const
5775 {
5776   return m_bSaveRenderMeshes;
5777 }
5778 
EnableSave3dmAnalysisMeshes(ON_BOOL32 b)5779 bool ON_BinaryArchive::EnableSave3dmAnalysisMeshes( ON_BOOL32  b /* default = true */ )
5780 {
5781   bool oldb = m_bSaveAnalysisMeshes;
5782   m_bSaveAnalysisMeshes = b?true:false;
5783   return oldb;
5784 }
5785 
Save3dmAnalysisMeshes() const5786 bool ON_BinaryArchive::Save3dmAnalysisMeshes() const
5787 {
5788   return m_bSaveAnalysisMeshes;
5789 }
5790 
EnableSaveUserData(ON_BOOL32 b)5791 bool ON_BinaryArchive::EnableSaveUserData( ON_BOOL32 b )
5792 {
5793   bool b0 = m_bSaveUserData;
5794   m_bSaveUserData = b?true:false;
5795   return b0;
5796 }
5797 
SaveUserData() const5798 bool ON_BinaryArchive::SaveUserData() const
5799 {
5800   return m_bSaveUserData;
5801 }
5802 
CurrentArchiveVersion()5803 int ON_BinaryArchive::CurrentArchiveVersion()
5804 {
5805   // Latest version of opennurbs binary archives supported by
5806   // this version of opennurbs.
5807   return 50; // Rhino 5.0 files with 8 byte chunk lengths.
5808 }
5809 
Write3dmStartSection(int version,const char * sInformation)5810 bool ON_BinaryArchive::Write3dmStartSection( int version, const char* sInformation )
5811 {
5812   if ( 0 == version )
5813     version = ON_BinaryArchive::CurrentArchiveVersion();
5814 
5815   // 2009 November 6
5816   // Default 3dm files now have 8 byte chunk lengths.
5817   // 3dm archive version numbers >= 50 indicate the file has 8 byte chunk lengths.
5818   // Rather than change the hundreds of places in Rhino that use 5, 6, 7 ...
5819   // the "version *= 10" line handles it.
5820   if ( version >= 5 && version < 50 )
5821     version *= 10;
5822 
5823   if ( version > ON_BinaryArchive::CurrentArchiveVersion() )
5824   {
5825     ON_ERROR("3dm archive version must be <= ON_BinaryArchive::CurrentArchiveVersion() ");
5826     return false;
5827   }
5828 
5829   if (    version < 2
5830        || (version >= 5 && version < 50)
5831        || (version >= 50 && 0 != (version % 10))
5832        )
5833   {
5834     ON_ERROR("3dm archive version must be 2, 3, 4 or 50");
5835     return false;
5836   }
5837 
5838   m_bad_CRC_count = 0;
5839   m_3dm_version = 0;
5840   m_3dm_opennurbs_version = ON::Version();
5841   m_3dm_version = version;
5842 
5843   char sVersion[64];
5844   memset( sVersion, 0, sizeof(sVersion) );
5845   if ( version < 1 )
5846     version = ON_BinaryArchive::CurrentArchiveVersion();
5847   sprintf(sVersion,"3D Geometry File Format %8d",version);
5848   bool rc = WriteByte( 32, sVersion );
5849   if ( rc )
5850     rc = BeginWrite3dmBigChunk( TCODE_COMMENTBLOCK, 0 );
5851   if ( rc ) {
5852     if ( sInformation && sInformation[0] ) {
5853       rc = WriteByte( strlen(sInformation), sInformation );
5854     }
5855     if ( rc ) {
5856       // write information that helps determine what code wrote the 3dm file
5857       char s[2048];
5858       size_t s_len = 0;
5859       memset( s, 0, sizeof(s) );
5860       sprintf(s," 3DM I/O processor: OpenNURBS toolkit version %d",ON::Version());
5861       strcat(s," (compiled on ");
5862       strcat(s,__DATE__);
5863       strcat(s,")\n");
5864       s_len = strlen(s);
5865       s[s_len++] = 26; // ^Z
5866       s[s_len++] = 0;
5867       rc = WriteByte( s_len, s );
5868     }
5869     if ( !EndWrite3dmChunk() ) // need to call EndWrite3dmChunk() even if a WriteByte() has failed
5870       rc = false;
5871   }
5872   return rc;
5873 }
5874 
Read3dmStartSection(int * version,ON_String & s)5875 bool ON_BinaryArchive::Read3dmStartSection( int* version, ON_String& s )
5876 {
5877   // The first 24 bytes of a 3dm file must be "3D Geometry File Format "
5878   // The next 8 bytes must be a right justified ASCII integer >= 1.  For
5879   // example, prior to March 2000, all 3DM files were version 1 files and
5880   // they began with "3D Geometry File Format        1".  At the time of
5881   // this writing (May 2011) there are version 1,2,3,4,5 and 50 files.
5882   //
5883   // The next section must contain a long chunk with typecode 1.
5884   m_bad_CRC_count = 0;
5885   m_3dm_version = 0;
5886 
5887   // In Read3dmProperties, m_3dm_opennurbs_version will be set to
5888   // the version of OpenNURBS that was used to write this archive.
5889   m_3dm_opennurbs_version = 0;
5890 
5891   unsigned int typecode = 0;
5892   ON__INT64 length = -1;
5893   int ver = m_3dm_version;
5894   if ( version )
5895     *version = m_3dm_version;
5896   s.Destroy();
5897   char s3d[33];
5898   memset( s3d, 0, sizeof(s3d) );
5899   bool rc = ReadByte( 32, s3d );
5900   if ( rc )
5901   {
5902 
5903     if ( strncmp( s3d, "3D Geometry File Format ", 24) )
5904     {
5905       // it's not a "pure" .3DM file
5906       // - see if we have a .3DM file with MS OLE-goo at the start
5907       // (generally, there is around 6kb of goo.  I keep looking
5908       // for up to 32mb just in case.)
5909       rc =  false;
5910       unsigned int n;
5911       int j;
5912       for ( n = 0; n < 33554432 && !rc; n++ )
5913       {
5914         for ( j = 0; j < 31; j++ )
5915           s3d[j] = s3d[j+1];
5916         if ( !ReadByte( 1, &s3d[31]) )
5917           break;
5918         if ( !strncmp( s3d, "3D Geometry File Format ", 24) )
5919         {
5920           m_3dm_start_section_offset = n+1;
5921           rc = true;
5922           break;
5923         }
5924       }
5925     }
5926 
5927     if (rc) {
5928       // get version
5929       //char* sVersion = s3d+24;
5930       // skip leading spaces
5931       int i = 24;
5932       while (i < 32 && s3d[i] == ' ')
5933         i++;
5934       while (i < 32 && rc) {
5935         // TEMPORARY 2 = X
5936         if ( i == 31 && s3d[i] == 'X' ) {
5937           s3d[i] = '2';
5938         }
5939 
5940         if ( s3d[i] < '0' || s3d[i] > '9' ) {
5941           rc = false; // it's not a valid .3DM file version
5942           break;
5943         }
5944         ver = ver*10 + ((int)(s3d[i]-'0'));
5945         i++;
5946       }
5947       if ( rc ) {
5948         m_3dm_version = ver;
5949         if ( version )
5950           *version = ver;
5951       }
5952     }
5953     if ( rc ) {
5954       rc = BeginRead3dmBigChunk( &typecode, &length );
5955       if ( rc ) {
5956         if ( typecode != 1 ) {
5957           rc = false; // it's not a .3DM file
5958         }
5959         else if ( length > 0 )
5960         {
5961           if ( length > 0x00FFFFFF )
5962           {
5963             ON_ERROR("ON_BinaryArchive::Read3dmStartSection - start section string is unreasonably long.");
5964             rc = false;
5965           }
5966           else
5967           {
5968             int slen = (int)length;
5969             s.ReserveArray( slen+1 );
5970             s.SetLength( slen );
5971             s[slen] = 0;
5972             ReadByte( slen, s.Array() );
5973             while ( slen > 0 && (0 == s[slen-1] || 26 == s[slen-1]) )
5974             {
5975               s[slen-1] = 0;
5976               slen--;
5977             }
5978             s.SetLength(slen);
5979           }
5980         }
5981       }
5982       if ( !EndRead3dmChunk() )
5983         rc = false;
5984       if ( rc && m_3dm_version == 1 ) {
5985         // In March 2001, we got reports of files with V1 headers and
5986         // a V2 bodies.  We haven't been able to track down the application
5987         // that is creating these damaged files, but we can detect them
5988         // and read them because they all have a TCODE_PROPERTIES_TABLE
5989         // chunk right after the start comments chunk and no valid V1
5990         // file has a chunk with a TCODE_PROPERTIES_TABLE tcode.
5991         //
5992         // Rhino 1.1 version 31-May-2000 reads 2.0 files as goo.  If a user
5993         // saves this file for some reason (no instances have been reported)
5994         // the resulting file has a V1 header, V1 fluff, and a V2 body.
5995         // This code will cause opennurbs to read the V2 body.
5996         // a file that is different from those describe This code
5997         // detects these files.
5998         {
5999 
6000           const ON__UINT64 pos1 = CurrentPosition();
6001           //int v1_fluff_chunk_count = 0;
6002           bool bCheckChunks = true;
6003 
6004           //////////
6005           while(bCheckChunks) {
6006             if ( !PeekAt3dmBigChunkType(&typecode,&length) )
6007               break;
6008             switch(typecode)
6009             {
6010               case TCODE_SUMMARY:
6011               case TCODE_BITMAPPREVIEW:
6012               case TCODE_UNIT_AND_TOLERANCES:
6013               case TCODE_VIEWPORT:
6014               case TCODE_LAYER:
6015               case TCODE_RENDERMESHPARAMS:
6016               case TCODE_CURRENTLAYER:
6017               case TCODE_ANNOTATION_SETTINGS:
6018               case TCODE_NOTES:
6019               case TCODE_NAMED_CPLANE:
6020               case TCODE_NAMED_VIEW:
6021                 // skip potential v1 fluff
6022                 bCheckChunks = BeginRead3dmBigChunk( &typecode, &length );
6023                 if ( bCheckChunks )
6024                   bCheckChunks = EndRead3dmChunk();
6025                 break;
6026 
6027               //case TCODE_PROPERTIES_TABLE:
6028               //case TCODE_SETTINGS_TABLE:
6029               //case TCODE_OBJECT_TABLE:
6030               //case TCODE_BITMAP_TABLE:
6031               //case TCODE_LAYER_TABLE:
6032               //case TCODE_GROUP_TABLE:
6033               //case TCODE_LIGHT_TABLE:
6034               //case TCODE_MATERIAL_TABLE:
6035               //case TCODE_USER_TABLE:
6036               default:
6037                 if ( TCODE_TABLE == (typecode & 0xFFFF0000) ) {
6038                   // Found a V2 table which has to be V1 goo
6039                   ON_WARNING("ON_BinaryArchive::Read3dmStartSection(): Archive has V1 header and V2 body. Continuing to read V2 body.");
6040                   m_3dm_version = 2;
6041                   if ( version )
6042                     *version = 2;
6043                 }
6044                 bCheckChunks = false;
6045                 break;
6046             }
6047           }
6048 
6049           if ( m_3dm_version == 1 ) {
6050             // move archive pointer back to
6051             ON__UINT64 pos2 = CurrentPosition();
6052             if ( pos2 > pos1 )
6053             {
6054               BigSeekBackward(pos2 - pos1);
6055             }
6056           }
6057         }
6058       }
6059     }
6060   }
6061   return rc;
6062 }
6063 
Write3dmProperties(const ON_3dmProperties & prop)6064 bool ON_BinaryArchive::Write3dmProperties(
6065       const ON_3dmProperties& prop
6066       )
6067 {
6068   bool rc = false;
6069   if ( m_3dm_version == 1 )
6070   {
6071     ON_String s;
6072 
6073     rc = true;
6074 
6075     if ( rc && prop.m_RevisionHistory.IsValid() )
6076     {
6077       rc = BeginWrite3dmChunk(TCODE_SUMMARY,0);
6078       if (rc)
6079       {
6080         // version 1 revision history chunk
6081         s = prop.m_RevisionHistory.m_sCreatedBy;
6082         if (rc) rc = WriteString(s);
6083         if (rc) rc = WriteTime( prop.m_RevisionHistory.m_create_time );
6084         if (rc) rc = WriteInt(0);
6085         s = prop.m_RevisionHistory.m_sLastEditedBy;
6086         if (rc) rc = WriteString(s);
6087         if (rc) rc = WriteTime( prop.m_RevisionHistory.m_last_edit_time );
6088         if (rc) rc = WriteInt(0);
6089         if (rc) rc = WriteInt( prop.m_RevisionHistory.m_revision_count );
6090         if ( !EndWrite3dmChunk() ) // writes 16 bit crc
6091           rc = false;
6092       }
6093     }
6094 
6095     if ( rc && prop.m_Notes.IsValid() )
6096     {
6097       rc = BeginWrite3dmChunk(TCODE_NOTES,0);
6098       // version 1 notes chunk
6099       if ( rc )
6100       {
6101         if ( rc ) rc = WriteInt( prop.m_Notes.m_bVisible );
6102         if ( rc ) rc = WriteInt( prop.m_Notes.m_window_left );
6103         if ( rc ) rc = WriteInt( prop.m_Notes.m_window_top );
6104         if ( rc ) rc = WriteInt( prop.m_Notes.m_window_right );
6105         if ( rc ) rc = WriteInt( prop.m_Notes.m_window_bottom );
6106         s = prop.m_Notes.m_notes;
6107         if ( rc ) rc = WriteString( s );
6108         if ( !EndWrite3dmChunk() )
6109           rc = false;
6110       }
6111     }
6112 
6113     if ( rc && prop.m_PreviewImage.IsValid() )
6114     {
6115       rc = BeginWrite3dmChunk(TCODE_BITMAPPREVIEW,0);
6116       if (rc)
6117       {
6118         // version 1 preview image chunk
6119         prop.m_PreviewImage.Write(*this);
6120         if ( !EndWrite3dmChunk() )
6121           rc = false;
6122       }
6123     }
6124   }
6125   else
6126   {
6127     // version 2+ file properties chunk
6128     rc = BeginWrite3dmChunk(TCODE_PROPERTIES_TABLE,0);
6129     if ( rc ) {
6130       rc = prop.Write( *this )?true:false;
6131       if ( !EndWrite3dmChunk() )
6132         rc = false;
6133     }
6134   }
6135   return rc;
6136 }
6137 
on_strnicmp(const char * s1,const char * s2,int n)6138 int on_strnicmp(const char * s1, const char * s2, int n)
6139 {
6140 #if defined(ON_OS_WINDOWS)
6141   //return stricmp(s1,s2,n);
6142   return _strnicmp(s1,s2,n);
6143 #else
6144   return strncasecmp(s1,s2,n);
6145 #endif
6146 }
6147 
Read3dmProperties(ON_3dmProperties & prop)6148 bool ON_BinaryArchive::Read3dmProperties( ON_3dmProperties& prop )
6149 {
6150   // In ON_3dmProperties::Read(), m_3dm_opennurbs_version will be
6151   // set to the version of OpenNURBS that was used to write this archive.
6152   // If the file was written with by a pre 200012210 version of OpenNURBS,
6153   // then m_3dm_opennurbs_version will be zero.
6154   m_3dm_opennurbs_version = 0;
6155 
6156   prop.Default();
6157 
6158   bool rc = true;
6159 
6160   // we need these when reading version 1 files
6161   const size_t pos0 = CurrentPosition();
6162   bool bHaveRevisionHistory = false;
6163   bool bHaveNotes = false;
6164   bool bHavePreviewImage = false;
6165   bool bDone = false;
6166   bool bRewindFilePointer = false;
6167 
6168   ON__UINT32 tcode;
6169   ON__INT64 big_value;
6170   //int version = 0;
6171 
6172   if ( m_3dm_version != 1 ) {
6173     for(;;)
6174     {
6175       tcode = 0;
6176       big_value = 0;
6177       rc = BeginRead3dmBigChunk( &tcode, &big_value );
6178       if ( !rc ) {
6179         bRewindFilePointer = true;
6180         break;
6181       }
6182 
6183       if ( tcode == TCODE_PROPERTIES_TABLE ) {
6184         rc = prop.Read(*this)?true:false;
6185       }
6186       else {
6187         bRewindFilePointer = true;
6188       }
6189 
6190       if ( !EndRead3dmChunk() ) {
6191         rc = false;
6192         bRewindFilePointer = true;
6193       }
6194       if ( tcode == TCODE_PROPERTIES_TABLE || !rc )
6195         break;
6196     }
6197   }
6198   else {
6199     // version 1 file
6200     rc = SeekFromStart(32)?true:false;
6201     bRewindFilePointer = true;
6202     for(;;)
6203     {
6204       tcode = 0;
6205       big_value = 0;
6206       rc = BeginRead3dmBigChunk( &tcode, &big_value );
6207       if ( !rc ) {
6208         rc = true; // assume we are at the end of the file
6209         bRewindFilePointer = true;
6210         break;
6211       }
6212 
6213       switch ( tcode ) {
6214 
6215       case 1: // comments section has application name
6216         if ( big_value > 1000000 )
6217         {
6218           ON_ERROR("Comment length > 1000000");
6219         }
6220         else if ( big_value > 1 )
6221         {
6222           int slen = (int)big_value;
6223           int i;
6224           char* name = 0;
6225           ON_String s;
6226           s.ReserveArray( slen+1 );
6227           s.SetLength( slen );
6228           s[slen] = 0;
6229           ReadByte( slen, s.Array() );
6230           while ( slen > 0 && (0 == s[slen-1] || 26 == s[slen-1]) )
6231           {
6232             s[slen-1] = 0;
6233             slen--;
6234           }
6235           s.SetLength(slen);
6236           name = s.Array();
6237           if ( name ) {
6238             while(*name) {
6239               if ( !on_strnicmp(name,"Interface:",10) ) {
6240                 name += 10;
6241                 break;
6242               }
6243               name++;
6244             }
6245             while(*name && *name <= 32 )
6246               name++;
6247             for ( i = 0; name[i] ; i++ ) {
6248               if ( name[i] == '(' ) {
6249                 name[i] = 0;
6250                 while ( i > 0 && (name[i] <= 32 || name[i] == '-') ) {
6251                   name[i] = 0;
6252                   i--;
6253                 }
6254                 break;
6255               }
6256             }
6257             if ( *name )
6258             {
6259               char* details = 0;
6260               if ( !on_strnicmp(name,"Rhinoceros",10) ) {
6261                 prop.m_Application.m_application_URL = "http://www.rhino3d.com";
6262                 details = name+10;
6263                 while ( *details && *details <= 32 )
6264                   details++;
6265                 while ( (*details >= '0' && *details <= '9') || *details == '.' )
6266                   details++;
6267                 if ( *details && *details <= 32 ) {
6268                   *details = 0;
6269                   details++;
6270                   while ( *details && (*details <= 32 ||*details == '-')) {
6271                     details++;
6272                   }
6273                 }
6274               }
6275               if (*name)
6276                 prop.m_Application.m_application_name = name;
6277               if (details && *details)
6278                 prop.m_Application.m_application_details = details;
6279             }
6280           }
6281         }
6282         break;
6283 
6284       case TCODE_SUMMARY:
6285         // version 1 revision history chunk (has 16 bit CRC)
6286         //version = 1;
6287         bHaveRevisionHistory = true;
6288         {
6289           int slength = 0;
6290           char* s = 0;
6291           if (rc) rc = ReadInt(&slength);
6292           if (rc && slength > 0 ) {
6293             s = (char*)onmalloc((slength+1)*sizeof(*s));
6294             memset( s, 0, (slength+1)*sizeof(*s) );
6295             if (rc) rc = ReadChar( slength, s );
6296             if ( rc )
6297               prop.m_RevisionHistory.m_sCreatedBy = s;
6298             onfree(s);
6299             slength = 0;
6300             s = 0;
6301           }
6302           if (rc) rc = ReadTime( prop.m_RevisionHistory.m_create_time );
6303           int i32 = 0;
6304           if (rc) rc = ReadInt(&i32); // 0 in 1.x files
6305           if (rc) rc = ReadInt(&slength);
6306           if ( rc && slength > 0 )
6307           {
6308             s = (char*)onmalloc((slength+1)*sizeof(*s));
6309             memset( s, 0, (slength+1)*sizeof(*s) );
6310             if (rc) rc = ReadChar( slength, s );
6311             if ( rc )
6312               prop.m_RevisionHistory.m_sLastEditedBy = s;
6313             onfree(s);
6314             slength = 0;
6315             s = 0;
6316           }
6317           if (rc) rc = ReadTime( prop.m_RevisionHistory.m_last_edit_time );
6318           if (rc) rc = ReadInt(&i32); // 0 in 1.x files
6319           if (rc) rc = ReadInt( &prop.m_RevisionHistory.m_revision_count );
6320         }
6321         break;
6322 
6323       case TCODE_NOTES:
6324         // version 1 notes chunk
6325         //version = 1;
6326         bHaveNotes = true;
6327         for(;;)
6328         {
6329           int slength;
6330           char* s = 0;
6331           rc = ReadInt( &prop.m_Notes.m_bVisible );
6332           if(!rc) break;
6333           rc = ReadInt( &prop.m_Notes.m_window_left );
6334           if(!rc) break;
6335           rc = ReadInt( &prop.m_Notes.m_window_top );
6336           if(!rc) break;
6337           rc = ReadInt( &prop.m_Notes.m_window_right );
6338           if(!rc) break;
6339           rc = ReadInt( &prop.m_Notes.m_window_bottom );
6340           if(!rc) break;
6341           rc = ReadInt( &slength );
6342           if(!rc) break;
6343           if ( slength > 0 )
6344           {
6345             s = (char*)onmalloc( (slength+1)*sizeof(*s) );
6346             memset( s, 0, (slength+1)*sizeof(*s) );
6347             if ( rc ) rc = ReadChar( slength, s );
6348             if ( rc )
6349             {
6350               prop.m_Notes.m_notes = s;
6351             }
6352             onfree(s);
6353             slength = 0;
6354             s = 0;
6355           }
6356           break;
6357         }
6358         break;
6359 
6360       case TCODE_BITMAPPREVIEW:
6361         // version 1 preview image chunk
6362         //version = 1;
6363         rc = prop.m_PreviewImage.Read(*this)?true:false;
6364         bHavePreviewImage = rc;
6365         break;
6366 
6367       case TCODE_CURRENTLAYER:
6368       case TCODE_LAYER:
6369         // version 1 layer and current layer chunks always came after notes/bitmap/summary
6370         bDone = true;
6371         bRewindFilePointer = true;
6372         break;
6373 
6374       default:
6375         // the call to EndRead3dmChunk() will skip over this chunk
6376         bRewindFilePointer = true;
6377         break;
6378       }
6379 
6380       // this call to EndRead3dmChunk() skips any unread portions of the chunk
6381       if ( !EndRead3dmChunk() ) {
6382         rc = false;
6383         bRewindFilePointer = true;
6384       }
6385 
6386       if ( bHaveRevisionHistory && bHaveNotes && bHavePreviewImage )
6387         bDone = true;
6388 
6389       if ( bDone || !rc )
6390         break;
6391     }
6392   }
6393 
6394   if ( bRewindFilePointer ) {
6395     // reposition file pointer to pos0
6396     const ON__UINT64 pos1 = CurrentPosition();
6397     if ( pos0 != pos1 )
6398     {
6399       if (pos1 > pos0)
6400         BigSeekBackward(pos1-pos0);
6401       else if ( pos1 < pos0 )
6402         BigSeekForward(pos0-pos1);
6403     }
6404   }
6405 
6406   return rc;
6407 }
6408 
Write3dmSettings(const ON_3dmSettings & settings)6409 bool ON_BinaryArchive::Write3dmSettings(
6410       const ON_3dmSettings& settings
6411       )
6412 {
6413   bool rc = false;
6414   if ( m_3dm_version == 1 ) {
6415     // legacy v1 settings info is a bunch of unreleated top level chunks
6416     rc = settings.Write(*this)?true:false;
6417   }
6418   else
6419   {
6420     // version 2+ file settings chunk
6421     rc = BeginWrite3dmChunk(TCODE_SETTINGS_TABLE,0);
6422     if ( rc ) {
6423       rc = settings.Write( *this );
6424       if ( !EndWrite3dmChunk() )
6425         rc = false;
6426     }
6427 
6428     if ( rc && 3 == Archive3dmVersion() )
6429     {
6430       // Build a list of ids of plug-ins that support saving
6431       // V3 user data.  If a plug-in id is not in this list,
6432       // the user data will not be saved in the V3 archive.
6433       int i, count = settings.m_plugin_list.Count();
6434       m_V3_plugin_id_list.SetCount(0);
6435       m_V3_plugin_id_list.SetCapacity( count+5 );
6436       for ( i = 0; i < count; i++ )
6437       {
6438         const ON_UUID& pid = settings.m_plugin_list[i].m_plugin_id;
6439         if ( !ON_UuidIsNil(pid) )
6440           m_V3_plugin_id_list.Append(pid);
6441       }
6442 
6443       // These ids insure V3, V4 and V5 core user data will round trip
6444       // through SaveAs V3 and SaveAs V4
6445       m_V3_plugin_id_list.Append( ON_v3_userdata_id );
6446       m_V3_plugin_id_list.Append( ON_v4_userdata_id );
6447       m_V3_plugin_id_list.Append( ON_opennurbs4_id );
6448       m_V3_plugin_id_list.Append( ON_opennurbs5_id );
6449       m_V3_plugin_id_list.Append( ON_rhino3_id );
6450       m_V3_plugin_id_list.Append( ON_rhino4_id );
6451       m_V3_plugin_id_list.Append( ON_rhino5_id );
6452       m_V3_plugin_id_list.QuickSort( ON_UuidCompare );
6453     }
6454   }
6455   return rc;
6456 }
6457 
Read3dmSettings(ON_3dmSettings & settings)6458 bool ON_BinaryArchive::Read3dmSettings( ON_3dmSettings& settings )
6459 {
6460   bool rc = false;
6461   ON__UINT32 tcode;
6462   ON__INT64 big_value;
6463 
6464   if ( m_3dm_version == 1 ) {
6465     // read legacy v 1 info that is scattered around the file
6466     rc = settings.Read(*this);
6467   }
6468   else {
6469     rc = true;
6470     while(rc)
6471     {
6472       tcode = 0;
6473       big_value = 0;
6474       rc = BeginRead3dmBigChunk( &tcode, &big_value );
6475       if ( !rc )
6476         break;
6477       if ( tcode == TCODE_SETTINGS_TABLE ) {
6478         // version 2 model properties
6479         rc = settings.Read(*this);
6480       }
6481       if ( !EndRead3dmChunk() ) {
6482         rc = false;
6483         break;
6484       }
6485       if ( TCODE_SETTINGS_TABLE == tcode )
6486         break;
6487     }
6488   }
6489 
6490   return rc;
6491 }
6492 
TableTypeFromTypecode(unsigned int typecode)6493 ON_BinaryArchive::table_type ON_BinaryArchive::TableTypeFromTypecode( unsigned int typecode )
6494 {
6495   table_type tt = no_active_table;
6496   switch(typecode)
6497   {
6498   case TCODE_PROPERTIES_TABLE: tt = properties_table; break;
6499   case TCODE_SETTINGS_TABLE:   tt = settings_table; break;
6500   case TCODE_BITMAP_TABLE:     tt = bitmap_table; break;
6501   case TCODE_TEXTURE_MAPPING_TABLE: tt = texture_mapping_table; break;
6502   case TCODE_MATERIAL_TABLE:   tt = material_table; break;
6503   case TCODE_LINETYPE_TABLE:   tt = linetype_table; break;
6504   case TCODE_LAYER_TABLE:      tt = layer_table; break;
6505   case TCODE_LIGHT_TABLE:      tt = light_table; break;
6506   case TCODE_OBJECT_TABLE:     tt = object_table; break;
6507   case TCODE_GROUP_TABLE:      tt = group_table; break;
6508   case TCODE_FONT_TABLE:       tt = font_table; break;
6509   case TCODE_DIMSTYLE_TABLE:   tt = dimstyle_table; break;
6510   case TCODE_HATCHPATTERN_TABLE: tt = hatchpattern_table; break;
6511   case TCODE_INSTANCE_DEFINITION_TABLE: tt = instance_definition_table; break;
6512   case TCODE_HISTORYRECORD_TABLE: tt = historyrecord_table; break;
6513   case TCODE_USER_TABLE:       tt = user_table; break;
6514   }
6515   return tt;
6516 }
6517 
BeginWrite3dmTable(unsigned int typecode)6518 bool ON_BinaryArchive::BeginWrite3dmTable( unsigned int typecode )
6519 {
6520   const table_type tt = TableTypeFromTypecode(typecode);
6521   if (tt == no_active_table) {
6522     ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() bad typecode");
6523     return false;
6524   }
6525   if ( m_active_table != no_active_table ) {
6526     ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() m_active_table != no_active_table");
6527     return false;
6528   }
6529   if ( m_chunk.Count() ) {
6530     ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() m_chunk.Count() > 0");
6531     return false;
6532   }
6533   bool rc = BeginWrite3dmChunk(typecode,0);
6534   if (rc)
6535     m_active_table = tt;
6536   return rc;
6537 }
6538 
EndWrite3dmTable(unsigned int typecode)6539 bool ON_BinaryArchive::EndWrite3dmTable( unsigned int typecode )
6540 {
6541   const table_type tt = TableTypeFromTypecode(typecode);
6542   if (tt == no_active_table) {
6543     ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() bad typecode");
6544     return false;
6545   }
6546   if ( m_active_table != tt ) {
6547     ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() m_active_table != t");
6548     return false;
6549   }
6550   if ( m_chunk.Count() != 1 ) {
6551     ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() m_chunk.Count() != 1");
6552     return false;
6553   }
6554   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
6555   if ( 0 == c || c->m_typecode != typecode ) {
6556     ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() m_chunk.Last()->typecode != typecode");
6557     return false;
6558   }
6559   bool rc = BeginWrite3dmChunk( TCODE_ENDOFTABLE, 0 );
6560   if (rc) {
6561     if (!EndWrite3dmChunk())
6562       rc = false;
6563   }
6564   if (!EndWrite3dmChunk())
6565     rc = false;
6566   Flush();
6567   m_active_table = no_active_table;
6568   return rc;
6569 }
6570 
BeginRead3dmTable(unsigned int typecode)6571 bool ON_BinaryArchive::BeginRead3dmTable( unsigned int typecode )
6572 {
6573   bool rc = false;
6574   const table_type tt = TableTypeFromTypecode(typecode);
6575   if (tt == no_active_table) {
6576     ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() bad typecode");
6577     return false;
6578   }
6579   if ( m_active_table != no_active_table ) {
6580     ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() m_active_table != no_active_table");
6581     return false;
6582   }
6583   if ( m_chunk.Count() ) {
6584     ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() m_chunk.Count() > 0");
6585     return false;
6586   }
6587 
6588   if ( m_3dm_version <= 1 )
6589   {
6590     if ( TCODE_USER_TABLE == typecode )
6591       return false; // no user tables in V1 files
6592 
6593     // version 1 files had can have chunks in any order.  To read a "table",
6594     // you have to go through the entire list of chunks looking for those
6595     // that belong to a particular table.
6596     rc = SeekFromStart(32)?true:false;
6597     m_active_table = tt;
6598   }
6599   else
6600   {
6601     if ( TCODE_USER_TABLE == typecode && m_3dm_version <= 2 )
6602       return false; // no user tables in V2 files
6603 
6604     bool bReadTable = true;
6605     ON__UINT32 tcode = !typecode;
6606     ON__INT64 big_value = 0;
6607     rc = PeekAt3dmBigChunkType( &tcode, &big_value );
6608     if ( rc ) {
6609       if ( tcode != typecode )
6610       {
6611         if ( typecode == TCODE_USER_TABLE )
6612         {
6613           // it's ok to not have user tables
6614           rc = false;
6615           bReadTable = false;
6616         }
6617         else if ( typecode == TCODE_GROUP_TABLE && m_3dm_opennurbs_version < 200012210 )
6618         {
6619           // 3DM archives written before version 200012210 and before do not have group tables
6620           rc = true;
6621           m_active_table = tt;
6622           bReadTable = false;
6623         }
6624         else if ( typecode == TCODE_FONT_TABLE && m_3dm_opennurbs_version < 200109180 )
6625         {
6626           // 3DM archives written before version 200109180 and before do not have font tables
6627           rc = true;
6628           m_active_table = tt;
6629           bReadTable = false;
6630         }
6631         else if ( typecode == TCODE_DIMSTYLE_TABLE && m_3dm_opennurbs_version < 200109260 )
6632         {
6633           // 3DM archives written before version 200109260 and before do not have dimstyle tables
6634           rc = true;
6635           m_active_table = tt;
6636           bReadTable = false;
6637         }
6638         else if ( typecode == TCODE_INSTANCE_DEFINITION_TABLE && m_3dm_opennurbs_version < 200205110 )
6639         {
6640           // 3DM archives written before version 200205110 and before do not have instance definition tables
6641           rc = true;
6642           m_active_table = tt;
6643           bReadTable = false;
6644         }
6645         else if ( typecode == TCODE_HATCHPATTERN_TABLE && m_3dm_opennurbs_version < 200405030 )
6646         {
6647           // 3DM archives written before version 200405030 and before do not have hatch pattern tables
6648           rc = true;
6649           m_active_table = tt;
6650           bReadTable = false;
6651         }
6652         else if ( typecode == TCODE_LINETYPE_TABLE && m_3dm_opennurbs_version < 200503170 )
6653         {
6654           // 3DM archives written before version 200503170 and before do not have linetype tables
6655           rc = true;
6656           m_active_table = tt;
6657           bReadTable = false;
6658         }
6659         else if ( typecode == TCODE_TEXTURE_MAPPING_TABLE && m_3dm_opennurbs_version < 200511110 )
6660         {
6661           // 3DM archives written before version 200511110 and before do not have texture mapping tables
6662           rc = true;
6663           m_active_table = tt;
6664           bReadTable = false;
6665         }
6666         else if ( typecode == TCODE_HISTORYRECORD_TABLE && m_3dm_opennurbs_version < 200601180 )
6667         {
6668           // 3DM archives written before version 200601180 and before do not have history record tables
6669           rc = true;
6670           m_active_table = tt;
6671           bReadTable = false;
6672         }
6673         else
6674         {
6675           // A required table is not at the current position in the archive
6676           // see if we can find it someplace else in the archive.  This can
6677           // happen when old code encounters a table that was added later.
6678 
6679           bool bSeekFromStart = true;
6680 
6681           if (   TCODE_HATCHPATTERN_TABLE == tcode
6682               && TCODE_INSTANCE_DEFINITION_TABLE == typecode
6683               && 3 == m_3dm_version
6684               && 200405190 <= m_3dm_opennurbs_version )
6685           {
6686             // Dale Lear
6687             //   V3 files from 19 may 2004 on contained bogus hatch pattern tables
6688             //   where the instance definition table was supposed to be.
6689             //
6690             // Do not set rc in this code.  The goal of this code is to
6691             // avoid seeking from the start of the file and posting
6692             // an ON_ERROR alert about something we can work around
6693             // and has been fixed in V4.
6694             tcode = 0;
6695             big_value = 0;
6696             if ( BeginRead3dmBigChunk( &tcode, &big_value ) )
6697             {
6698               if ( TCODE_HATCHPATTERN_TABLE == tcode )
6699               {
6700                 bSeekFromStart = false;
6701               }
6702 
6703               if ( !EndRead3dmChunk() )
6704               {
6705                 bSeekFromStart = true;
6706               }
6707               else if ( !bSeekFromStart )
6708               {
6709                 tcode = 0;
6710                 big_value = 0;
6711                 PeekAt3dmBigChunkType( &tcode, &big_value );
6712                 if ( tcode != typecode )
6713                   bSeekFromStart = true;
6714               }
6715             }
6716           }
6717 
6718           if ( bSeekFromStart )
6719           {
6720             ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() - current file position not at start of table - searching");
6721             rc = Seek3dmChunkFromStart( typecode );
6722           }
6723         }
6724       }
6725       if ( rc && bReadTable ) {
6726         tcode = !typecode;
6727         big_value = 0;
6728         rc = BeginRead3dmBigChunk( &tcode, &big_value );
6729         if ( rc && tcode != typecode ) {
6730           ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() - corrupt table - skipping");
6731           rc = false;
6732           EndRead3dmChunk();
6733         }
6734         else if (rc) {
6735           m_active_table = tt;
6736         }
6737       }
6738     }
6739   }
6740 
6741   return rc;
6742 }
6743 
6744 
GetCurrentChunk(ON_3DM_CHUNK & chunk) const6745 int ON_BinaryArchive::GetCurrentChunk(ON_3DM_CHUNK& chunk) const
6746 {
6747   ON_3DM_BIG_CHUNK big_chunk;
6748   memset(&chunk,0,sizeof(ON_3DM_CHUNK));
6749   memset(&big_chunk,0,sizeof(big_chunk));
6750   int rc = GetCurrentChunk(big_chunk);
6751   if ( rc > 0 )
6752   {
6753     chunk.m_offset = (size_t)big_chunk.m_big_offset;
6754     chunk.m_typecode = big_chunk.m_typecode;
6755 
6756     ON__INT32 i32 = 0;
6757     if ( ON_IsLongChunkTypecode( big_chunk.m_typecode ) )
6758       DownSizeUINT( (ON__UINT64)big_chunk.m_big_value, (ON__UINT32*)&i32 );
6759     else
6760       DownSizeINT( big_chunk.m_big_value, &i32 );
6761     chunk.m_value = i32;
6762 
6763     chunk.m_do_length = big_chunk.m_bLongChunk ? 1 : 0;
6764     chunk.m_do_crc16 = big_chunk.m_do_crc16 ? 1 : 0;
6765     chunk.m_do_crc32 = big_chunk.m_do_crc32 ? 1 : 0;
6766     chunk.m_crc16 = big_chunk.m_crc16;
6767     chunk.m_crc32 = big_chunk.m_crc32;
6768   }
6769   return rc;
6770 }
6771 
6772 
GetCurrentChunk(ON_3DM_BIG_CHUNK & chunk) const6773 int ON_BinaryArchive::GetCurrentChunk(ON_3DM_BIG_CHUNK& chunk) const
6774 {
6775   int rc = m_chunk.Count();
6776   if ( rc > 0 )
6777   {
6778     chunk = m_chunk[rc-1];
6779   }
6780   else
6781   {
6782     memset(&chunk,0,sizeof(ON_3DM_BIG_CHUNK));
6783   }
6784   return rc;
6785 }
6786 
6787 static
BufferToUINT16(bool bReverseByteOrder,const unsigned char * buffer,const unsigned char * buffer_max,ON__UINT16 * u16)6788 const unsigned char*  BufferToUINT16(
6789           bool bReverseByteOrder,
6790           const unsigned char* buffer,
6791           const unsigned char* buffer_max,
6792           ON__UINT16* u16 )
6793 {
6794   if ( buffer >= buffer_max || buffer_max - buffer < 2 )
6795     return 0;
6796   if ( u16 )
6797   {
6798     unsigned char* dst = (unsigned char*)u16;
6799     if ( bReverseByteOrder )
6800     {
6801       dst[0] = buffer[1];
6802       dst[1] = buffer[0];
6803     }
6804     else
6805     {
6806       dst[0] = buffer[0];
6807       dst[1] = buffer[1];
6808     }
6809   }
6810   return buffer+2;
6811 }
6812 
6813 static
BufferToUINT32(bool bReverseByteOrder,const unsigned char * buffer,const unsigned char * buffer_end,ON__UINT32 * u32)6814 const unsigned char* BufferToUINT32(
6815           bool bReverseByteOrder,
6816           const unsigned char* buffer,
6817           const unsigned char* buffer_end,
6818           ON__UINT32* u32 )
6819 {
6820   if ( buffer >= buffer_end || buffer_end - buffer < 4 )
6821     return 0;
6822   if ( u32 )
6823   {
6824     unsigned char* dst = (unsigned char*)u32;
6825     if ( bReverseByteOrder )
6826     {
6827       dst[0] = buffer[3];
6828       dst[1] = buffer[2];
6829       dst[2] = buffer[1];
6830       dst[3] = buffer[0];
6831     }
6832     else
6833     {
6834       dst[0] = buffer[0];
6835       dst[1] = buffer[1];
6836       dst[2] = buffer[2];
6837       dst[3] = buffer[3];
6838     }
6839   }
6840   return buffer+4;
6841 }
6842 
6843 static
BufferToINT64(bool bReverseByteOrder,const unsigned char * buffer,const unsigned char * buffer_end,ON__INT64 * i64)6844 const unsigned char* BufferToINT64(
6845           bool bReverseByteOrder,
6846           const unsigned char* buffer,
6847           const unsigned char* buffer_end,
6848           ON__INT64* i64 )
6849 {
6850   if ( buffer >= buffer_end || buffer_end - buffer < 8 )
6851     return 0;
6852   if ( i64 )
6853   {
6854     unsigned char* dst = (unsigned char*)i64;
6855     if ( bReverseByteOrder )
6856     {
6857       dst[0] = buffer[7];
6858       dst[1] = buffer[6];
6859       dst[2] = buffer[5];
6860       dst[3] = buffer[4];
6861       dst[4] = buffer[3];
6862       dst[5] = buffer[2];
6863       dst[6] = buffer[1];
6864       dst[7] = buffer[0];
6865     }
6866     else
6867     {
6868       dst[0] = buffer[0];
6869       dst[1] = buffer[1];
6870       dst[2] = buffer[2];
6871       dst[3] = buffer[3];
6872       dst[4] = buffer[4];
6873       dst[5] = buffer[5];
6874       dst[6] = buffer[6];
6875       dst[7] = buffer[7];
6876     }
6877   }
6878   return buffer+8;
6879 }
6880 
6881 static
BufferValidateTcode(bool bReverseByteOrder,const unsigned char * buffer,const unsigned char * buffer_end,ON__UINT32 expected_tcode)6882 const unsigned char* BufferValidateTcode(
6883           bool bReverseByteOrder,
6884           const unsigned char* buffer,
6885           const unsigned char* buffer_end,
6886           ON__UINT32 expected_tcode )
6887 {
6888   ON__UINT32 tc = expected_tcode ? 0 : 1;
6889   buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&tc);
6890   return ( 0 != buffer && tc == expected_tcode ) ? buffer : 0;
6891 }
6892 
6893 static
BufferToChunkValue(bool bReverseByteOrder,size_t sizeof_chunk_value,const unsigned char * buffer,const unsigned char * buffer_end,ON__INT64 * chunk_value)6894 const unsigned char* BufferToChunkValue(
6895           bool bReverseByteOrder,
6896           size_t sizeof_chunk_value,
6897           const unsigned char* buffer,
6898           const unsigned char* buffer_end,
6899           ON__INT64* chunk_value )
6900 {
6901   if ( 8 == sizeof_chunk_value )
6902   {
6903     buffer = BufferToINT64(bReverseByteOrder,buffer,buffer_end,chunk_value);
6904   }
6905   else
6906   {
6907     ON__UINT32 u32;
6908     ON__UINT64 u64;
6909     buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&u32);
6910     if ( buffer && chunk_value )
6911     {
6912       // this u64 = u32 is here so 4 byte unsigned ints with the high
6913       // bit set are converted to positive 8 bytes ints.
6914       u64 = u32;
6915       *chunk_value = (ON__INT64)u64;
6916     }
6917   }
6918   return buffer;
6919 }
6920 
6921 static
BufferToUuid(bool bReverseByteOrder,const unsigned char * buffer,const unsigned char * buffer_end,ON_UUID & uuid)6922 const unsigned char* BufferToUuid(
6923           bool bReverseByteOrder,
6924           const unsigned char* buffer,
6925           const unsigned char* buffer_end,
6926           ON_UUID& uuid )
6927 {
6928   ON__UINT32 data1=0;
6929   ON__UINT16 data2=0, data3=0;
6930   if ( buffer >= buffer_end || buffer_end - buffer < 16 )
6931     return 0;
6932   buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&data1);
6933   if (buffer)
6934     buffer = BufferToUINT16(bReverseByteOrder,buffer,buffer_end,&data2);
6935   if (buffer)
6936     buffer = BufferToUINT16(bReverseByteOrder,buffer,buffer_end,&data3);
6937   if (buffer)
6938   {
6939     if ( buffer >= buffer_end || buffer_end - buffer < 8 )
6940       buffer = 0;
6941     else
6942     {
6943       uuid.Data1 = data1;
6944       uuid.Data2 = data2;
6945       uuid.Data3 = data3;
6946       memcpy(&uuid.Data4,buffer,8);
6947       buffer += 8;
6948     }
6949   }
6950   return buffer;
6951 }
6952 
6953 static
EmergencyFindTable_UuidHelper(bool bReverseByteOrder,size_t sizeof_chunk_value,const unsigned char * buffer,const unsigned char * buffer_end,const ON__UINT32 expected_tcode,const ON_UUID * expected_uuid)6954 const unsigned char* EmergencyFindTable_UuidHelper(
6955           bool bReverseByteOrder,
6956           size_t sizeof_chunk_value,
6957           const unsigned char* buffer,
6958           const unsigned char* buffer_end,
6959           const ON__UINT32 expected_tcode,
6960           const ON_UUID* expected_uuid
6961           )
6962 {
6963   ON__INT64 chunk_value;
6964   ON__UINT32 c, cc;
6965   ON_UUID uuid;
6966 
6967   // make sure the value at the start of the buffer = expected_tcode.
6968   buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,expected_tcode);
6969   if ( 0 == buffer )
6970     return 0;
6971 
6972   // get length of this chunk containing the uuid
6973   chunk_value = -1;
6974   buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&chunk_value );
6975   if ( 0 == buffer || chunk_value < 0 )
6976     return 0;
6977 
6978   // determine how long the chunk is supposed to be and validate the length.
6979   //
6980   // The "16+4+sizeof_chunk_value+21+4+4" in the bLookForUserTableRecordHeader
6981   // breaks down as:
6982   //  16                       sizeof(uuid)
6983   //  +4                     + sizeof(TCODE_USER_TABLE_RECORD_HEADER chunk typecode)
6984   //  +sizeof_chunk_value    + sizeof(TCODE_USER_TABLE_RECORD_HEADER chunk length)
6985   //  +21                    + major ver, minor ver, bool, archive ver, 3dm ver
6986   //  +4                     + sizeof(TCODE_USER_TABLE_RECORD_HEADER chunk crc)
6987   //  +4                     + sizeof(TCODE_USER_TABLE_UUID chunk crc)
6988   const bool bLookForUserTableRecordHeader = (TCODE_USER_TABLE_UUID == expected_tcode
6989                                               && ((ON__UINT64)chunk_value) >= (16+4+sizeof_chunk_value+21+4+4)
6990                                              );
6991   if ( !bLookForUserTableRecordHeader && 20 != chunk_value )
6992     return 0;
6993   buffer = BufferToUuid(bReverseByteOrder,buffer,buffer_end,uuid);
6994   if ( 0 == buffer )
6995     return 0;
6996   if( 0 != expected_uuid && uuid != *expected_uuid )
6997     return 0;
6998 
6999   if ( bLookForUserTableRecordHeader )
7000   {
7001     // make sure there is a TCODE_USER_TABLE_RECORD_HEADER chunk and skip over it.
7002     buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_USER_TABLE_RECORD_HEADER);
7003     if ( 0 == buffer )
7004       return 0;
7005     // get length of the TCODE_USER_TABLE_RECORD_HEADER chunk
7006     ON__INT64 header_length = -1;
7007     buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&header_length );
7008     if ( 0 == buffer )
7009       return 0;
7010     if ( header_length < 25 )
7011       return 0;
7012     if ( buffer >= buffer_end || buffer_end - buffer < header_length )
7013       return 0;
7014     buffer += header_length;
7015   }
7016 
7017   buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&c);
7018   if ( 0 == buffer )
7019     return 0;
7020   cc = ON_CRC32(0,4,&uuid.Data1);
7021   cc = ON_CRC32(cc,2,&uuid.Data2);
7022   cc = ON_CRC32(cc,2,&uuid.Data3);
7023   cc = ON_CRC32(cc,8,&uuid.Data4[0]);
7024   if ( c != cc )
7025     return 0;
7026 
7027   return buffer;
7028 }
7029 
7030 
FindMisplacedTable(ON__UINT64 filelength,const ON__UINT32 table_tcode,const ON__UINT32 table_record_tcode,const ON_UUID class_uuid,const ON__UINT64 min_length_data)7031 bool ON_BinaryArchive::FindMisplacedTable(
7032         ON__UINT64 filelength,
7033         const ON__UINT32 table_tcode,
7034         const ON__UINT32 table_record_tcode,
7035         const ON_UUID class_uuid,
7036         const ON__UINT64 min_length_data
7037         )
7038 {
7039   bool rc = false;
7040   unsigned char buffer2048[2048];
7041   const ON__UINT64 pos0 = CurrentPosition();
7042   if ( filelength > 0 && pos0 >= filelength )
7043     return false;
7044 
7045   ON__UINT32 tcode;
7046   ON__INT64 i64;
7047 
7048   const bool bReverseByteOrder = (ON::big_endian == Endian());
7049   const size_t sizeof_chunk_typecode = 4;
7050   const size_t sizeof_chunk_value = SizeofChunkLength();
7051   const size_t sizeof_chunk_header = sizeof_chunk_typecode + sizeof_chunk_value;
7052   size_t length_of_user_uuid_and_header = 0;
7053   const bool bFindObjectTable    = (  TCODE_OBJECT_TABLE == table_tcode
7054                                    && TCODE_OBJECT_RECORD == table_record_tcode );
7055   const bool bFindUserTable    = (    TCODE_USER_TABLE == table_tcode
7056                                    && TCODE_USER_RECORD == table_record_tcode );
7057 
7058   if ( TCODE_USER_TABLE == table_tcode && !bFindUserTable )
7059     return false;
7060   if ( TCODE_OBJECT_TABLE == table_tcode && !bFindObjectTable )
7061     return false;
7062   if ( bFindUserTable && ON_UuidIsNil(class_uuid) )
7063   {
7064     // must provide plug-in id when searching for user tables
7065     ON_ERROR("ON_BinaryArchive::FindMisplacedTable - must provide plug-in id when searching for user tables");
7066     return false;
7067   }
7068 
7069   if ( !SeekFromStart(0) )
7070     return false;
7071 
7072   ON__UINT64 pos1 = CurrentPosition();
7073   ON__UINT64 pos;
7074   ON__UINT64 empty_table_pos = 0;    // position of first empty table candidate
7075   int        empty_table_status = 0; // 1 = found a candidate for an empty table
7076                                      // 2 = found 2 or more candidates
7077 
7078   const size_t sizeof_buffer2048 = sizeof(buffer2048);
7079   bool bAtEOF = false;
7080 
7081   while(!bAtEOF)
7082   {
7083     pos = CurrentPosition();
7084     if ( pos < pos1 )
7085     {
7086       break;
7087     }
7088     else if ( pos > pos1 )
7089     {
7090       if ( !BigSeekBackward(pos - pos1) )
7091         break;
7092       if ( pos1 != CurrentPosition() )
7093         break;
7094     }
7095 
7096     memset(buffer2048,0,sizeof_buffer2048);
7097     // Depending on the table and the version of the file, less than
7098     // sizeof_buffer128 bytes may be read.  Setting bit 0x04 of
7099     // m_error_message_mask disables calls to ON_Error when we
7100     // attempt to read beyond the end of file.
7101     const unsigned int saved_error_message_mask = m_error_message_mask;
7102     m_error_message_mask |= 0x04;
7103     const size_t sizeof_read = Read(sizeof_buffer2048,buffer2048);
7104     m_error_message_mask = saved_error_message_mask;
7105     if ( sizeof_read < sizeof_buffer2048 )
7106     {
7107       // we need to parse what was read, but there's nothing after this.
7108       bAtEOF = true;
7109     }
7110     if ( sizeof_read < 2*sizeof_chunk_header || sizeof_read > sizeof_buffer2048 )
7111       break;
7112     const unsigned char* buffer_end = (&buffer2048[0]) + sizeof_read;
7113     const unsigned char* buffer = buffer2048;
7114 
7115     pos1++;
7116 
7117     // "read" 4 byte tcode
7118     tcode = !table_tcode;
7119     buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&tcode);
7120     if ( 0 == buffer )
7121       break;
7122 
7123     if ( table_tcode != tcode )
7124     {
7125       // This for loop looks through the buffer we just
7126       // read to reduce the amount of times we seek backwards
7127       // and re-read.
7128       for ( size_t i = 1; i <= sizeof_read - sizeof_chunk_typecode; i++ )
7129       {
7130         tcode = !table_tcode;
7131         buffer = BufferToUINT32(bReverseByteOrder,&buffer2048[i],buffer_end,&tcode);
7132         if ( 0 == buffer || table_tcode == tcode )
7133         {
7134           if ( bAtEOF && sizeof_read > 0 && 0 != buffer && table_tcode == tcode )
7135           {
7136             // this table starts within sizeof_buffer2048 bytes of the eof.
7137             bAtEOF = false;
7138           }
7139           break;
7140         }
7141         pos1++;
7142       }
7143       continue; // start again with archive positioned at the tcode we want
7144     }
7145 
7146     // "read" 4 or 8 byte chunk value
7147     i64 = -1;
7148     buffer = BufferToChunkValue(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64);
7149     if ( 0 == buffer || i64 <= 0 )
7150       continue;
7151     const ON__UINT64 length_of_table = (ON__UINT64)i64;
7152 
7153     if ( length_of_table < 2*sizeof_chunk_header + 4 + min_length_data )
7154     {
7155       if ( sizeof_chunk_header == length_of_table && 2 != empty_table_status )
7156       {
7157         // see if we are at a TCODE_ENDOFTABLE chunk
7158         buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_ENDOFTABLE);
7159         if ( 0 != buffer )
7160         {
7161           i64 = -1;
7162           buffer = BufferToChunkValue(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64);
7163           if ( 0 == i64 )
7164           {
7165             if ( 0 == empty_table_status )
7166             {
7167               empty_table_pos = pos1-1;
7168               empty_table_status = 1;
7169             }
7170             else
7171             {
7172               // found 2 or more candidates for the end of table chunk
7173               empty_table_status = 2;
7174             }
7175           }
7176         }
7177       }
7178       continue;
7179     }
7180 
7181     if ( bFindUserTable )
7182     {
7183       // We found TCODE_USER_TABLE + chunk length.  If it is a user table,
7184       // there should be a TCODE_USER_TABLE_UUID chunk with a crc.
7185       const unsigned char* buffer0 = buffer;
7186       buffer = EmergencyFindTable_UuidHelper(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,TCODE_USER_TABLE_UUID,&class_uuid);
7187       if ( 0 == buffer || buffer <= buffer0 )
7188         continue;
7189 
7190       length_of_user_uuid_and_header = buffer - buffer0;
7191       // At this point we should be postioned at the table_record_tcode = TCODE_USER_RECORD chunk
7192     }
7193 
7194     // see if the start of the buffer contains the 4 byte typecode value = table_record_tcode.
7195     buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,table_record_tcode);
7196     if ( 0 == buffer )
7197       continue;
7198     i64 = -1;
7199     buffer = BufferToChunkValue( bReverseByteOrder, sizeof_chunk_value,buffer,buffer_end,&i64);
7200     if ( 0 == buffer || i64 <= 0 )
7201       continue;
7202     const ON__UINT64 length_of_record = (ON__UINT64)i64;
7203 
7204 
7205     if ( bFindUserTable )
7206     {
7207       ON__UINT64 expected_length_of_table = length_of_user_uuid_and_header
7208                                           + sizeof_chunk_header
7209                                           + length_of_record;
7210       if ( expected_length_of_table != length_of_table )
7211         continue;
7212     }
7213     else
7214     {
7215       if ( length_of_record < 4*sizeof_chunk_header + 20 + min_length_data )
7216         continue;
7217       if ( length_of_record + 2*sizeof_chunk_header > length_of_table)
7218         continue;
7219 
7220       if (bFindObjectTable)
7221       {
7222         buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_OBJECT_RECORD_TYPE);
7223         if ( 0 == buffer )
7224           continue;
7225         // The TCODE_OBJECT_RECORD_TYPE is a shot chunk whose value is a bitfield
7226         // used to filter reading of objects.  Checking the value will not help
7227         // validate the record, but we need to skip over it.
7228         buffer = BufferToChunkValue(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,0);
7229         if ( 0 == buffer )
7230           continue;
7231       }
7232 
7233       buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_OPENNURBS_CLASS);
7234       if ( 0 == buffer )
7235         continue;
7236 
7237       i64 = -1;
7238       buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64);
7239       if ( 0 == buffer || i64 <= 0 )
7240         continue;
7241       const ON__UINT64 length_of_on_class = (ON__UINT64)i64;
7242 
7243       if ( length_of_on_class < 3*sizeof_chunk_header + 20 + min_length_data )
7244         continue;
7245 
7246       if ( length_of_on_class + sizeof_chunk_header + 4 > length_of_record)
7247         continue;
7248 
7249       const unsigned char* buffer0 = buffer;
7250       buffer = EmergencyFindTable_UuidHelper(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,TCODE_OPENNURBS_CLASS_UUID,(ON_UuidIsNil(class_uuid) ? NULL : &class_uuid));
7251       if ( 0 == buffer || buffer <= buffer0)
7252         continue;
7253       const size_t length_of_uuid_chunk = buffer-buffer0;
7254 
7255       buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_OPENNURBS_CLASS_DATA);
7256       if ( 0 == buffer )
7257         continue;
7258 
7259       i64 = -1;
7260       buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64);
7261       if ( 0 == buffer || i64 < 0 )
7262         continue;
7263       const ON__UINT64 length_of_data = (ON__UINT64)i64;
7264 
7265       if ( length_of_data < min_length_data )
7266         continue;
7267       if ( length_of_data + length_of_uuid_chunk + 2*sizeof_chunk_header > length_of_on_class)
7268         continue;
7269     }
7270 
7271     // position archive at point where the table tcode was read
7272     if ( !BigSeekBackward(sizeof_read) )
7273       break;
7274     pos = CurrentPosition();
7275     if ( pos+1 == pos1)
7276       rc = true;
7277     break;
7278   }
7279 
7280   if ( !rc )
7281   {
7282     // we didn't fing a table containing anything
7283     if ( 1 == empty_table_status )
7284     {
7285       // we found one candidate for an empty table.
7286       // This is reasonable for materials, bitmaps, and the like.
7287       rc = BigSeekFromStart(empty_table_pos);
7288     }
7289     else
7290     {
7291       // nothing in this file.
7292       BigSeekFromStart(pos0);
7293     }
7294   }
7295   return rc;
7296 }
7297 
FindTableInDamagedArchive(const unsigned int tcode_table,const unsigned int tcode_record,const ON_UUID class_uuid,const int min_length_data)7298 bool ON_BinaryArchive::FindTableInDamagedArchive(
7299                 const unsigned int tcode_table,
7300                 const unsigned int tcode_record,
7301                 const ON_UUID class_uuid,
7302                 const int min_length_data
7303                 )
7304 {
7305   bool rc = FindMisplacedTable(
7306                   0,
7307                   tcode_table,
7308                   tcode_record,
7309                   class_uuid,
7310                   min_length_data
7311                   );
7312   return rc;
7313 }
7314 
7315 /*
7316 static
7317 bool FindMaterialTable( ON_BinaryArchive& binary_archive, size_t filelength )
7318 {
7319   bool rc = EmergencyFindTable(
7320                 binary_archive, filelength,
7321                 TCODE_MATERIAL_TABLE, TCODE_MATERIAL_RECORD,
7322                 ON_Material::m_ON_Material_class_id.Uuid(),
7323                 114
7324                 );
7325   return rc;
7326 }
7327 */
7328 
EndRead3dmTable(unsigned int typecode)7329 bool ON_BinaryArchive::EndRead3dmTable( unsigned int typecode )
7330 {
7331   bool rc = false;
7332   const table_type tt = TableTypeFromTypecode(typecode);
7333   if (tt == no_active_table) {
7334     ON_ERROR("ON_BinaryArchive::EndRead3dmTable() bad typecode");
7335     return false;
7336   }
7337   if ( m_active_table != tt ) {
7338     ON_ERROR("ON_BinaryArchive::EndRead3dmTable() m_active_table != t");
7339     return false;
7340   }
7341   if ( m_3dm_version == 1 ) {
7342     if ( m_chunk.Count() != 0 ) {
7343       ON_ERROR("ON_BinaryArchive::EndRead3dmTable() v1 file m_chunk.Count() != 0");
7344       return false;
7345     }
7346     rc = true;
7347   }
7348   else {
7349     if ( m_active_table == group_table && m_3dm_opennurbs_version < 200012210 )
7350     {
7351       // 3DM archives written before version 200012210 and before do not have group tables
7352       rc = true;
7353     }
7354     else if ( m_active_table == font_table && m_3dm_opennurbs_version < 200109180 )
7355     {
7356       // 3DM archives written before version 200109180 and before do not have font tables
7357       rc = true;
7358     }
7359     else if ( m_active_table == dimstyle_table && m_3dm_opennurbs_version < 200109260 )
7360     {
7361       // 3DM archives written before version 200109260 and before do not have dimstyle tables
7362       rc = true;
7363     }
7364     else if ( m_active_table == instance_definition_table && m_3dm_opennurbs_version < 200205110 )
7365     {
7366       // 3DM archives written before version 200205110 and before do not have instance definition tables
7367       rc = true;
7368     }
7369     else if ( m_active_table == hatchpattern_table && m_3dm_opennurbs_version < 200405030 )
7370     {
7371       // 3DM archives written before version 200405030 and before do not have hatch pattern tables
7372       rc = true;
7373     }
7374     else if ( m_active_table == linetype_table && m_3dm_opennurbs_version < 200503170 )
7375     {
7376       // 3DM archives written before version 200503170 and before do not have linetype tables
7377       rc = true;
7378     }
7379     else if ( m_active_table == texture_mapping_table && m_3dm_opennurbs_version < 200511110 )
7380     {
7381       // 3DM archives written before version 200511110 and before do not have texture mapping tables
7382       rc = true;
7383     }
7384     else if ( m_active_table == historyrecord_table && m_3dm_opennurbs_version < 200601180 )
7385     {
7386       // 3DM archives written before version 200601180 and before do not have history record tables
7387       rc = true;
7388     }
7389     else
7390     {
7391       if ( m_chunk.Count() != 1 )
7392       {
7393         ON_ERROR("ON_BinaryArchive::EndRead3dmTable() v2 file m_chunk.Count() != 1");
7394         return false;
7395       }
7396       const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
7397       if ( 0 == c || c->m_typecode != typecode )
7398       {
7399         ON_ERROR("ON_BinaryArchive::EndRead3dmTable() m_chunk.Last()->typecode != typecode");
7400         return false;
7401       }
7402       rc = EndRead3dmChunk();
7403     }
7404   }
7405   m_active_table = no_active_table;
7406   return rc;
7407 }
7408 
BeginWrite3dmBitmapTable()7409 bool ON_BinaryArchive::BeginWrite3dmBitmapTable()
7410 {
7411   return BeginWrite3dmTable( TCODE_BITMAP_TABLE );
7412 }
7413 
EndWrite3dmBitmapTable()7414 bool ON_BinaryArchive::EndWrite3dmBitmapTable()
7415 {
7416   return EndWrite3dmTable( TCODE_BITMAP_TABLE );
7417 }
7418 
Write3dmBitmap(const ON_Bitmap & bitmap)7419 bool ON_BinaryArchive::Write3dmBitmap( const ON_Bitmap& bitmap )
7420 {
7421   bool rc = false;
7422   if ( m_3dm_version != 1 )
7423   {
7424     const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
7425     if ( c && c->m_typecode == TCODE_BITMAP_TABLE )
7426     {
7427       rc = BeginWrite3dmChunk( TCODE_BITMAP_RECORD, 0 );
7428       if ( rc )
7429       {
7430         rc = WriteObject( bitmap );
7431         if ( !EndWrite3dmChunk() )
7432           rc = false;
7433       }
7434     }
7435     else
7436     {
7437       ON_ERROR("ON_BinaryArchive::Write3dmBitmap() must be called in BeginWrite3dmBitmapTable() block");
7438       rc = false;
7439     }
7440   }
7441   return rc;
7442 }
7443 
BeginRead3dmBitmapTable()7444 bool ON_BinaryArchive::BeginRead3dmBitmapTable()
7445 {
7446   bool rc =  BeginRead3dmTable( TCODE_BITMAP_TABLE );
7447   if ( !rc )
7448   {
7449     // 1 November 2005 Dale Lear
7450     //    This fall back is slow but it has been finding
7451     //    layer and object tables in damaged files.  I'm
7452     //    adding it to the other BeginRead3dm...Table()
7453     //    functions when it makes sense.
7454     rc = FindMisplacedTable(
7455                 0,
7456                 TCODE_BITMAP_TABLE, TCODE_BITMAP_RECORD,
7457                 ON_nil_uuid, // multiple types of opennurbs objects in bitmap tables
7458                 40
7459                 );
7460     if ( rc )
7461     {
7462       rc = BeginRead3dmTable( TCODE_BITMAP_TABLE );
7463     }
7464   }
7465   return rc;
7466 }
7467 
EndRead3dmBitmapTable()7468 bool ON_BinaryArchive::EndRead3dmBitmapTable()
7469 {
7470   return EndRead3dmTable( TCODE_BITMAP_TABLE );
7471 }
7472 
Read3dmBitmap(ON_Bitmap ** ppBitmap)7473 int ON_BinaryArchive::Read3dmBitmap(  // returns 0 at end of bitmap table
7474                                       //         1 bitmap successfully read
7475           ON_Bitmap** ppBitmap // bitmap returned here
7476           )
7477 {
7478   if ( ppBitmap )
7479     *ppBitmap = 0;
7480   ON_Bitmap* bitmap = 0;
7481   int rc = 0;
7482   if ( m_3dm_version != 1 ) {
7483     ON__UINT32 tcode = 0;
7484     ON__INT64 big_value = 0;
7485     if ( BeginRead3dmBigChunk( &tcode, &big_value ) )
7486     {
7487       if ( tcode == TCODE_BITMAP_RECORD )
7488       {
7489         ON_Object* p = 0;
7490         if ( ReadObject( &p ) )
7491         {
7492           bitmap = ON_Bitmap::Cast(p);
7493           if ( !bitmap )
7494             delete p;
7495           else
7496             rc = 1;
7497         }
7498         if (!bitmap)
7499         {
7500           ON_ERROR("ON_BinaryArchive::Read3dmBitmap() - corrupt bitmap table");
7501         }
7502         if ( ppBitmap )
7503           *ppBitmap = bitmap;
7504         else if ( bitmap )
7505           delete bitmap;
7506       }
7507       else if ( tcode != TCODE_ENDOFTABLE )
7508       {
7509         ON_ERROR("ON_BinaryArchive::Read3dmBitmap() - corrupt bitmap table");
7510       }
7511       EndRead3dmChunk();
7512     }
7513   }
7514 
7515   return rc;
7516 }
7517 
7518 
BeginWrite3dmLayerTable()7519 bool ON_BinaryArchive::BeginWrite3dmLayerTable()
7520 {
7521   bool rc = false;
7522   if ( m_3dm_version != 1 ) {
7523     rc = BeginWrite3dmTable( TCODE_LAYER_TABLE );
7524   }
7525   else {
7526     if ( m_chunk.Count() ) {
7527       ON_ERROR("ON_BinaryArchive::BeginWrite3dmLayerTable() - chunk stack should be empty");
7528       return false;
7529     }
7530     if ( m_active_table != no_active_table ) {
7531       ON_ERROR("ON_BinaryArchive::BeginWrite3dmLayerTable() - m_active_table != no_active_table");
7532     }
7533     m_active_table = layer_table;
7534     rc = true;
7535   }
7536 
7537   return rc;
7538 }
7539 
Write3dmLayer(const ON_Layer & layer)7540 bool ON_BinaryArchive::Write3dmLayer( const ON_Layer&  layer )
7541 {
7542   bool rc = false;
7543   if ( m_active_table != layer_table ) {
7544     ON_ERROR("ON_BinaryArchive::Write3dmLayer() - m_active_table != layer_table");
7545   }
7546 
7547   if ( m_3dm_version == 1 ) {
7548     // legacy version 1 layer information is in a top level TCODE_LAYER chunk
7549     if ( m_chunk.Count() ) {
7550       ON_ERROR("ON_BinaryArchive::Write3dmLayer() - version 1 - chunk stack should be empty");
7551       return false;
7552     }
7553     ON_String s = layer.LayerName();
7554     if ( !s.IsEmpty() ) {
7555       rc = BeginWrite3dmChunk( TCODE_LAYER, 0 );
7556 
7557       // layer name
7558       if (rc) {
7559         rc = BeginWrite3dmChunk( TCODE_LAYERNAME, 0 );
7560         if(rc) rc = WriteString(s);
7561         if (!EndWrite3dmChunk())
7562           rc = false;
7563       }
7564 
7565       // layer color
7566       if (rc) {
7567         rc = BeginWrite3dmChunk( TCODE_RGB, layer.Color() );
7568         if (!EndWrite3dmChunk())
7569           rc = false;
7570       }
7571 
7572       // layer mode normal=0/hidden=1/locked=2
7573       if (rc)
7574       {
7575         int mode;
7576         if ( layer.IsLocked() )
7577           mode = 2; // "locked"
7578         else if ( layer.IsVisible() )
7579           mode = 0; // "normal"
7580         else
7581           mode = 1; // "hidden"
7582         rc = BeginWrite3dmChunk( TCODE_LAYERSTATE, mode );
7583         if (!EndWrite3dmChunk())
7584           rc = false;
7585       }
7586 
7587       if ( !BeginWrite3dmChunk( TCODE_ENDOFTABLE, 0 ) )
7588         rc = false;
7589       if ( !EndWrite3dmChunk() )
7590         rc = false;
7591 
7592       if (!EndWrite3dmChunk()) // end of TCODE_LAYER chunk
7593         rc = false;
7594     }
7595   }
7596   else {
7597     // version 2+
7598     const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
7599     if ( c && c->m_typecode == TCODE_LAYER_TABLE ) {
7600       rc = BeginWrite3dmChunk( TCODE_LAYER_RECORD, 0 );
7601       if ( rc ) {
7602         rc = WriteObject( layer );
7603         if ( !EndWrite3dmChunk() )
7604           rc = false;
7605       }
7606     }
7607     else {
7608       ON_ERROR("ON_BinaryArchive::Write3dmLayer() must be called in BeginWrite3dmLayerTable(2) block");
7609       rc = false;
7610     }
7611   }
7612 
7613   return rc;
7614 }
7615 
EndWrite3dmLayerTable()7616 bool ON_BinaryArchive::EndWrite3dmLayerTable()
7617 {
7618   bool rc = false;
7619   if ( m_3dm_version == 1 ) {
7620     if ( m_active_table != layer_table ) {
7621       ON_ERROR("ON_BinaryArchive::EndWrite3dmLayerTable() - m_active_table != layer_table");
7622     }
7623     rc = true;
7624     m_active_table = no_active_table;
7625   }
7626   else {
7627     rc = EndWrite3dmTable( TCODE_LAYER_TABLE );
7628   }
7629   return rc;
7630 }
7631 
BeginRead3dmLayerTable()7632 bool ON_BinaryArchive::BeginRead3dmLayerTable()
7633 {
7634   bool rc = false;
7635   m_3dm_v1_layer_index = 0;
7636   rc = BeginRead3dmTable( TCODE_LAYER_TABLE );
7637   if ( !rc )
7638   {
7639     // 8 October 2004 Dale Lear
7640     //    This fall back is slow but it will find
7641     //    layer tables in files that have been damaged.
7642     rc = FindMisplacedTable(
7643                 0,
7644                 TCODE_LAYER_TABLE, TCODE_LAYER_RECORD,
7645                 ON_Layer::m_ON_Layer_class_id.Uuid(),
7646                 30
7647                 );
7648     if ( rc )
7649     {
7650       rc = BeginRead3dmTable( TCODE_LAYER_TABLE );
7651     }
7652 
7653   }
7654   else if ( rc && m_3dm_version == 1 ) {
7655     rc = Seek3dmChunkFromStart( TCODE_LAYER );
7656     rc = true; // there are 1.0 files written by the old IO toolkit that have no layers
7657   }
7658   return rc;
7659 }
7660 
Read3dmV1LayerIndex(const char * sV1LayerName) const7661 int ON_BinaryArchive::Read3dmV1LayerIndex(const char* sV1LayerName) const
7662 {
7663   // returns V1 layer index
7664 
7665   int layer_index = -1;
7666 
7667   if (    ON::read3dm == m_mode
7668        && 0 == m_3dm_opennurbs_version
7669        && 1 == m_3dm_version
7670        && 0 != m_V1_layer_list
7671        && 0 != sV1LayerName
7672        && 0 != sV1LayerName[0]
7673      )
7674   {
7675     struct ON__3dmV1LayerIndex* p = m_V1_layer_list;
7676     int i;
7677     for ( i = 0; 0 != p && i < 1000; i++ )
7678     {
7679       if ( p->m_layer_index < 0 )
7680         break;
7681       if ( p->m_layer_name_length < 1 || p->m_layer_name_length>256)
7682         break;
7683       if ( 0 == p->m_layer_name )
7684         break;
7685       if ( 0 == p->m_layer_name[0] )
7686         break;
7687       if ( 0 != p->m_layer_name[p->m_layer_name_length] )
7688         break;
7689       if ( !on_stricmp(p->m_layer_name,sV1LayerName) )
7690       {
7691         layer_index = p->m_layer_index;
7692         break;
7693       }
7694       p = p->m_next;
7695     }
7696   }
7697 
7698   return layer_index;
7699 }
7700 
Read3dmV1Layer(ON_Layer * & layer)7701 bool ON_BinaryArchive::Read3dmV1Layer( ON_Layer*& layer )
7702 {
7703   ON_String s;
7704   bool rc = 0;
7705   ON__UINT32 tcode;
7706   ON__INT64 big_value;
7707   for (;;)
7708   {
7709     tcode = 0;
7710     big_value = 0;
7711     if (!BeginRead3dmBigChunk(&tcode,&big_value))
7712       break; // assume we are at the end of the file
7713     if ( tcode == TCODE_LAYER ) {
7714       layer = new ON_Layer();
7715       layer->SetLayerIndex(m_3dm_v1_layer_index++);
7716       rc = 1;
7717       break;
7718     }
7719     if (!EndRead3dmChunk())
7720        break;
7721   }
7722   if ( layer ) {
7723     rc = false;
7724     for (;;)
7725     {
7726       tcode = 0;
7727       big_value = 0;
7728       if (!BeginRead3dmBigChunk(&tcode,&big_value))
7729         break;
7730       switch(tcode)
7731       {
7732       case TCODE_LAYERNAME:
7733         {
7734           int slen = 0;
7735           ReadInt(&slen);
7736           if ( slen < 0 || slen > 10000 )
7737           {
7738             ON_ERROR("ON_BinaryArchive::Read3dmV1Layer() - invalid layer name length");
7739           }
7740           else
7741           {
7742             s.SetLength(slen);
7743             if ( ReadByte( s.Length(), s.Array() ) )
7744             {
7745               layer->SetLayerName(s);
7746             }
7747           }
7748         }
7749         break;
7750       case TCODE_RGB:
7751         {
7752           ON__UINT64 rgb64 = (ON__UINT64)big_value;
7753           ON__UINT32 rgb32 = (ON__UINT32)rgb64;
7754           layer->SetColor( ON_Color((ON__UINT32)rgb32) );
7755         }
7756         break;
7757       case TCODE_LAYERSTATE:
7758         switch (big_value)
7759         {
7760         case 1: // hidden
7761           layer->SetVisible(false);
7762           layer->SetLocked(false);
7763           break;
7764         case 2: // locked
7765           layer->SetVisible(true);
7766           layer->SetLocked(true);
7767           break;
7768         default: // normal
7769           layer->SetVisible(true);
7770           layer->SetLocked(false);
7771           break;
7772         }
7773         break;
7774       }
7775       if (!EndRead3dmChunk())
7776          break;
7777       if ( TCODE_ENDOFTABLE == tcode ) {
7778         rc = true;
7779         break;
7780       }
7781     }
7782     if ( !EndRead3dmChunk() ) // end of TCODE_LAYER chunk
7783       rc = false;
7784   }
7785   if ( !rc && layer )
7786   {
7787     delete layer;
7788     layer = 0;
7789   }
7790   else if (rc && layer)
7791   {
7792     if (    ON::read3dm == m_mode
7793          && 0 == m_3dm_opennurbs_version
7794          && 1 == m_3dm_version
7795          )
7796     {
7797       // save layer index and name in a linked list.
7798       int s_length = s.Length();
7799       const char* s_name = s.Array();
7800       if (    layer->LayerIndex() >= 0
7801            && s_length > 0
7802            && s_length < 256
7803            && 0 != s_name
7804            && 0 != s_name[0]
7805          )
7806       {
7807         struct ON__3dmV1LayerIndex* p = (struct ON__3dmV1LayerIndex*)oncalloc(1, sizeof(*p) + (s_length+1)*sizeof(*p->m_layer_name) );
7808         p->m_layer_name = (char*)(p+1);
7809         p->m_layer_index = layer->LayerIndex();
7810         p->m_layer_name_length = s_length;
7811         memcpy(p->m_layer_name,s_name,s_length*sizeof(*p->m_layer_name));
7812         p->m_layer_name[s_length] = 0;
7813         p->m_next = m_V1_layer_list;
7814         m_V1_layer_list = p;
7815       }
7816     }
7817   }
7818   return rc;
7819 }
7820 
Read3dmLayer(ON_Layer ** ppLayer)7821 int ON_BinaryArchive::Read3dmLayer( ON_Layer** ppLayer )
7822 {
7823   if ( !ppLayer )
7824     return 0;
7825   *ppLayer = 0;
7826   if ( m_active_table != layer_table ) {
7827     ON_ERROR("ON_BinaryArchive::BeginRead3dmLayerTable() - m_active_table != no_active_table");
7828   }
7829   ON__UINT32 tcode;
7830   ON__INT64 big_value;
7831   ON_Layer* layer = NULL;
7832   // returns 0 at end of layer table
7833   if ( m_3dm_version == 1 ) {
7834     Read3dmV1Layer(layer);
7835   }
7836   else {
7837     // version 2+
7838     tcode = 0;
7839     big_value = 0;
7840     if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) {
7841       if ( tcode == TCODE_LAYER_RECORD ) {
7842         ON_Object* p = 0;
7843         if ( ReadObject( &p ) ) {
7844           layer = ON_Layer::Cast(p);
7845           if ( !layer )
7846             delete p;
7847         }
7848         if (!layer) {
7849           ON_ERROR("ON_BinaryArchive::Read3dmLayer() - corrupt layer table");
7850         }
7851       }
7852       else if ( tcode != TCODE_ENDOFTABLE ) {
7853         ON_ERROR("ON_BinaryArchive::Read3dmLayer() - corrupt layer table");
7854       }
7855       EndRead3dmChunk();
7856     }
7857   }
7858   if ( layer )
7859     layer->HasPerViewportSettings(ON_nil_uuid); // this call sets ON_Layer::m__runtime_flags
7860   *ppLayer = layer;
7861   return (layer) ? 1 : 0;
7862 }
7863 
EndRead3dmLayerTable()7864 bool ON_BinaryArchive::EndRead3dmLayerTable()
7865 {
7866   bool rc = false;
7867   if ( m_3dm_version == 1 ) {
7868     if ( m_active_table != layer_table ) {
7869       ON_ERROR("ON_BinaryArchive::EndRead3dmLayerTable() - m_active_table != no_active_table");
7870       rc = false;
7871     }
7872     else if ( m_chunk.Count() ) {
7873       ON_ERROR("ON_BinaryArchive::EndRead3dmLayerTable() - m_chunk.Count() > 0");
7874       rc = false;
7875     }
7876     else {
7877       // rewind to start of chunks
7878       rc = SeekFromStart(32)?true:false;
7879     }
7880     m_active_table = no_active_table;
7881   }
7882   else {
7883     rc = EndRead3dmTable( TCODE_LAYER_TABLE );
7884   }
7885   return rc;
7886 }
7887 
7888 
7889 ///////////////////////////////////////////////////////////
7890 ///////////////////////////////////////////////////////////
7891 ///////////////////////////////////////////////////////////
7892 ///////////////////////////////////////////////////////////
7893 
BeginWrite3dmGroupTable()7894 bool ON_BinaryArchive::BeginWrite3dmGroupTable()
7895 {
7896   bool rc = false;
7897   rc = BeginWrite3dmTable( TCODE_GROUP_TABLE );
7898   return rc;
7899 }
7900 
Write3dmGroup(const ON_Group & group)7901 bool ON_BinaryArchive::Write3dmGroup( const ON_Group&  group )
7902 {
7903   bool rc = false;
7904   if ( m_active_table != group_table ) {
7905     ON_ERROR("ON_BinaryArchive::Write3dmGroup() - m_active_table != group_table");
7906   }
7907 
7908   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
7909   if ( c && c->m_typecode == TCODE_GROUP_TABLE ) {
7910     rc = BeginWrite3dmChunk( TCODE_GROUP_RECORD, 0 );
7911     if ( rc ) {
7912       rc = WriteObject( group );
7913       if ( !EndWrite3dmChunk() )
7914         rc = false;
7915     }
7916   }
7917   else {
7918     ON_ERROR("ON_BinaryArchive::Write3dmGroup() must be called in BeginWrite3dmGroupTable() block");
7919     rc = false;
7920   }
7921 
7922   return rc;
7923 }
7924 
EndWrite3dmGroupTable()7925 bool ON_BinaryArchive::EndWrite3dmGroupTable()
7926 {
7927   bool rc = false;
7928   rc = EndWrite3dmTable( TCODE_GROUP_TABLE );
7929   return rc;
7930 }
7931 
BeginRead3dmGroupTable()7932 bool ON_BinaryArchive::BeginRead3dmGroupTable()
7933 {
7934   if ( m_3dm_version == 1 ) {
7935     return true;
7936   }
7937   bool rc = false;
7938   rc = BeginRead3dmTable( TCODE_GROUP_TABLE );
7939 
7940   if ( !rc )
7941   {
7942     // 1 November 2005 Dale Lear
7943     //    This fall back is slow but it has been finding
7944     //    layer and object tables in damaged files.  I'm
7945     //    adding it to the other BeginRead3dm...Table()
7946     //    functions when it makes sense.
7947     rc = FindMisplacedTable(
7948                 0,
7949                 TCODE_GROUP_TABLE, TCODE_GROUP_RECORD,
7950                 ON_Group::m_ON_Group_class_id.Uuid(),
7951                 20
7952                 );
7953     if ( rc )
7954     {
7955       rc = BeginRead3dmTable( TCODE_GROUP_TABLE );
7956     }
7957   }
7958 
7959   return rc;
7960 }
7961 
Read3dmGroup(ON_Group ** ppGroup)7962 int ON_BinaryArchive::Read3dmGroup( ON_Group** ppGroup )
7963 {
7964   if ( !ppGroup )
7965     return 0;
7966   *ppGroup = 0;
7967   if ( m_3dm_version == 1 ) {
7968     return 0;
7969   }
7970   if ( m_active_table != group_table ) {
7971     ON_ERROR("ON_BinaryArchive::BeginRead3dmGroupTable() - m_active_table != no_active_table");
7972   }
7973   if ( m_3dm_opennurbs_version < 200012210 ) {
7974     // 3DM archives written before version 200012210 and before do not have group tables
7975     return 0;
7976   }
7977 
7978   ON__UINT32 tcode = 0;
7979   ON__INT64 big_value = 0;
7980   ON_Group* group = NULL;
7981   if ( BeginRead3dmBigChunk( &tcode, &big_value ) )
7982   {
7983     if ( tcode == TCODE_GROUP_RECORD ) {
7984       ON_Object* p = 0;
7985       if ( ReadObject( &p ) ) {
7986         group = ON_Group::Cast(p);
7987         if ( !group )
7988           delete p;
7989       }
7990       if (!group) {
7991         ON_ERROR("ON_BinaryArchive::Read3dmGroup() - corrupt group table");
7992       }
7993     }
7994     else if ( tcode != TCODE_ENDOFTABLE ) {
7995       ON_ERROR("ON_BinaryArchive::Read3dmGroup() - corrupt group table");
7996     }
7997     EndRead3dmChunk();
7998   }
7999   *ppGroup = group;
8000   return (group) ? 1 : 0;
8001 }
8002 
EndRead3dmGroupTable()8003 bool ON_BinaryArchive::EndRead3dmGroupTable()
8004 {
8005   bool rc = false;
8006   if ( m_3dm_version == 1 ) {
8007     return true;
8008   }
8009   else {
8010     rc = EndRead3dmTable( TCODE_GROUP_TABLE );
8011   }
8012   return rc;
8013 }
8014 
8015 ///////////////////////////////////////////////////////////
8016 ///////////////////////////////////////////////////////////
8017 ///////////////////////////////////////////////////////////
8018 ///////////////////////////////////////////////////////////
8019 
BeginWrite3dmFontTable()8020 bool ON_BinaryArchive::BeginWrite3dmFontTable()
8021 {
8022   bool rc = false;
8023   rc = BeginWrite3dmTable( TCODE_FONT_TABLE );
8024   return rc;
8025 }
8026 
Write3dmFont(const ON_Font & font)8027 bool ON_BinaryArchive::Write3dmFont( const ON_Font&  font )
8028 {
8029   bool rc = false;
8030   if ( m_active_table != font_table ) {
8031     ON_ERROR("ON_BinaryArchive::Write3dmFont() - m_active_table != font_table");
8032   }
8033 
8034   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
8035   if ( c && c->m_typecode == TCODE_FONT_TABLE ) {
8036     rc = BeginWrite3dmChunk( TCODE_FONT_RECORD, 0 );
8037     if ( rc ) {
8038       rc = WriteObject( font );
8039       if ( !EndWrite3dmChunk() )
8040         rc = false;
8041     }
8042   }
8043   else {
8044     ON_ERROR("ON_BinaryArchive::Write3dmFont() must be called in BeginWrite3dmFontTable() block");
8045     rc = false;
8046   }
8047 
8048   return rc;
8049 }
8050 
EndWrite3dmFontTable()8051 bool ON_BinaryArchive::EndWrite3dmFontTable()
8052 {
8053   bool rc = false;
8054   rc = EndWrite3dmTable( TCODE_FONT_TABLE );
8055   return rc;
8056 }
8057 
BeginRead3dmFontTable()8058 bool ON_BinaryArchive::BeginRead3dmFontTable()
8059 {
8060   if ( m_3dm_version <= 2 ) {
8061     return true;
8062   }
8063   bool rc = false;
8064   rc = BeginRead3dmTable( TCODE_FONT_TABLE );
8065 
8066   if ( !rc )
8067   {
8068     // 1 November 2005 Dale Lear
8069     //    This fall back is slow but it has been finding
8070     //    layer and object tables in damaged files.  I'm
8071     //    adding it to the other BeginRead3dm...Table()
8072     //    functions FindMisplacedTable it makes sense.
8073     rc = FindMisplacedTable(
8074                 0,
8075                 TCODE_FONT_TABLE, TCODE_FONT_RECORD,
8076                 ON_Font::m_ON_Font_class_id.Uuid(),
8077                 30
8078                 );
8079     if ( rc )
8080     {
8081       rc = BeginRead3dmTable( TCODE_FONT_TABLE );
8082     }
8083   }
8084 
8085   return rc;
8086 }
8087 
Read3dmFont(ON_Font ** ppFont)8088 int ON_BinaryArchive::Read3dmFont( ON_Font** ppFont )
8089 {
8090   if ( !ppFont )
8091     return 0;
8092   *ppFont = 0;
8093   if ( m_3dm_version <= 2 ) {
8094     return 0;
8095   }
8096   if ( m_active_table != font_table ) {
8097     ON_ERROR("ON_BinaryArchive::BeginRead3dmFontTable() - m_active_table != no_active_table");
8098   }
8099   if ( m_3dm_opennurbs_version < 200109180 ) {
8100     // 3DM archives written before version 200109180 and before do not have font tables
8101     return 0;
8102   }
8103 
8104   ON__UINT32 tcode = 0;
8105   ON__INT64 big_value = 0;
8106   ON_Font* font = NULL;
8107   if ( BeginRead3dmBigChunk( &tcode, &big_value ) )
8108   {
8109     if ( tcode == TCODE_FONT_RECORD ) {
8110       ON_Object* p = 0;
8111       if ( ReadObject( &p ) ) {
8112         font = ON_Font::Cast(p);
8113         if ( !font )
8114           delete p;
8115       }
8116       if (!font) {
8117         ON_ERROR("ON_BinaryArchive::Read3dmFont() - corrupt font table");
8118       }
8119     }
8120     else if ( tcode != TCODE_ENDOFTABLE ) {
8121       ON_ERROR("ON_BinaryArchive::Read3dmFont() - corrupt font table");
8122     }
8123     EndRead3dmChunk();
8124   }
8125   *ppFont = font;
8126   return (font) ? 1 : 0;
8127 }
8128 
EndRead3dmFontTable()8129 bool ON_BinaryArchive::EndRead3dmFontTable()
8130 {
8131   bool rc = false;
8132   if ( m_3dm_version <= 2 ) {
8133     return true;
8134   }
8135   else {
8136     rc = EndRead3dmTable( TCODE_FONT_TABLE );
8137   }
8138   return rc;
8139 }
8140 
8141 ///////////////////////////////////////////////////////////
8142 ///////////////////////////////////////////////////////////
8143 ///////////////////////////////////////////////////////////
8144 ///////////////////////////////////////////////////////////
8145 
BeginWrite3dmDimStyleTable()8146 bool ON_BinaryArchive::BeginWrite3dmDimStyleTable()
8147 {
8148   bool rc = false;
8149   rc = BeginWrite3dmTable( TCODE_DIMSTYLE_TABLE );
8150   return rc;
8151 }
8152 
Write3dmDimStyle(const ON_DimStyle & dimstyle)8153 bool ON_BinaryArchive::Write3dmDimStyle( const ON_DimStyle&  dimstyle )
8154 {
8155   bool rc = false;
8156   if ( m_active_table != dimstyle_table ) {
8157     ON_ERROR("ON_BinaryArchive::Write3dmDimStyle() - m_active_table != dimstyle_table");
8158   }
8159 
8160   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
8161   if ( c && c->m_typecode == TCODE_DIMSTYLE_TABLE ) {
8162     rc = BeginWrite3dmChunk( TCODE_DIMSTYLE_RECORD, 0 );
8163     if ( rc ) {
8164       rc = WriteObject( dimstyle );
8165       if ( !EndWrite3dmChunk() )
8166         rc = false;
8167     }
8168   }
8169   else {
8170     ON_ERROR("ON_BinaryArchive::Write3dmDimStyle() must be called in BeginWrite3dmDimStyleTable() block");
8171     rc = false;
8172   }
8173 
8174   return rc;
8175 }
8176 
EndWrite3dmDimStyleTable()8177 bool ON_BinaryArchive::EndWrite3dmDimStyleTable()
8178 {
8179   bool rc = false;
8180   rc = EndWrite3dmTable( TCODE_DIMSTYLE_TABLE );
8181   return rc;
8182 }
8183 
BeginRead3dmDimStyleTable()8184 bool ON_BinaryArchive::BeginRead3dmDimStyleTable()
8185 {
8186   if ( m_3dm_version <= 2 ) {
8187     return true;
8188   }
8189   bool rc = false;
8190   rc = BeginRead3dmTable( TCODE_DIMSTYLE_TABLE );
8191 
8192   if ( !rc )
8193   {
8194     // 1 November 2005 Dale Lear
8195     //    This fall back is slow but it has been finding
8196     //    layer and object tables in damaged files.  I'm
8197     //    adding it to the other BeginRead3dm...Table()
8198     //    functions when it makes sense.
8199     rc = FindMisplacedTable(
8200                 0,
8201                 TCODE_DIMSTYLE_TABLE, TCODE_DIMSTYLE_RECORD,
8202                 ON_DimStyle::m_ON_DimStyle_class_id.Uuid(),
8203                 30
8204                 );
8205     if ( rc )
8206     {
8207       rc = BeginRead3dmTable( TCODE_DIMSTYLE_TABLE );
8208     }
8209   }
8210 
8211   return rc;
8212 }
8213 
Read3dmDimStyle(ON_DimStyle ** ppDimStyle)8214 int ON_BinaryArchive::Read3dmDimStyle( ON_DimStyle** ppDimStyle )
8215 {
8216   if ( !ppDimStyle )
8217     return 0;
8218   *ppDimStyle = 0;
8219   if ( m_3dm_version <= 2 ) {
8220     return 0;
8221   }
8222   if ( m_active_table != dimstyle_table ) {
8223     ON_ERROR("ON_BinaryArchive::BeginRead3dmDimStyleTable() - m_active_table != no_active_table");
8224   }
8225   if ( m_3dm_opennurbs_version < 200109260 ) {
8226     // 3DM archives written before version 200109260 and before do not have dimstyle tables
8227     return 0;
8228   }
8229 
8230   ON__UINT32 tcode = 0;
8231   ON__INT64 big_value = 0;
8232   ON_DimStyle* dimstyle = NULL;
8233   tcode = 0;
8234   if ( BeginRead3dmBigChunk( &tcode, &big_value ) )
8235   {
8236     if ( tcode == TCODE_DIMSTYLE_RECORD ) {
8237       ON_Object* p = 0;
8238       if ( ReadObject( &p ) ) {
8239         dimstyle = ON_DimStyle::Cast(p);
8240         if ( !dimstyle )
8241           delete p;
8242       }
8243       if (!dimstyle) {
8244         ON_ERROR("ON_BinaryArchive::Read3dmDimStyle() - corrupt dimstyle table");
8245       }
8246     }
8247     else if ( tcode != TCODE_ENDOFTABLE ) {
8248       ON_ERROR("ON_BinaryArchive::Read3dmDimStyle() - corrupt dimstyle table");
8249     }
8250     EndRead3dmChunk();
8251   }
8252   *ppDimStyle = dimstyle;
8253   return (dimstyle) ? 1 : 0;
8254 }
8255 
EndRead3dmDimStyleTable()8256 bool ON_BinaryArchive::EndRead3dmDimStyleTable()
8257 {
8258   bool rc = false;
8259   if ( m_3dm_version <= 2 ) {
8260     return true;
8261   }
8262   else {
8263     rc = EndRead3dmTable( TCODE_DIMSTYLE_TABLE );
8264   }
8265   return rc;
8266 }
8267 
8268 ///////////////////////////////////////////////////////////
8269 ///////////////////////////////////////////////////////////
8270 ///////////////////////////////////////////////////////////
8271 ///////////////////////////////////////////////////////////
8272 
BeginWrite3dmHatchPatternTable()8273 bool ON_BinaryArchive::BeginWrite3dmHatchPatternTable()
8274 {
8275   bool rc = false;
8276   rc = BeginWrite3dmTable( TCODE_HATCHPATTERN_TABLE );
8277   return rc;
8278 }
8279 
Write3dmHatchPattern(const ON_HatchPattern & pattern)8280 bool ON_BinaryArchive::Write3dmHatchPattern( const ON_HatchPattern&  pattern )
8281 {
8282   bool rc = false;
8283   if ( m_active_table != hatchpattern_table ) {
8284     ON_ERROR("ON_BinaryArchive::Write3dmHatchPattern() - m_active_table != hatchpattern_table");
8285   }
8286 
8287   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
8288   if ( c && c->m_typecode == TCODE_HATCHPATTERN_TABLE )
8289   {
8290     rc = BeginWrite3dmChunk( TCODE_HATCHPATTERN_RECORD, 0 );
8291     if (rc)
8292     {
8293       rc = WriteObject( pattern );
8294       if ( !EndWrite3dmChunk() )
8295         rc = false;
8296     }
8297 
8298     // 1 Nov 2005 Dale Lear:
8299     //
8300     //   This code was used before version 200511010.  The reader can
8301     //   still read the old files, but old versions of Rhino cannot read
8302     //   files written with version 200511010 or later.  This happened in
8303     //   the early beta cycile of V4.  V3 did not have hatch patterns.
8304     //   Please leave this comment here until Nov 2006 so I can remember
8305     //   what happened if I have to debug file IO.  By May 2006, all
8306     //   the betas that could write the bogus hatch tables should have
8307     //   expired.
8308     //
8309     //if ( rc ) {
8310     //  rc = pattern.Write( *this)?true:false;
8311     //  if ( !EndWrite3dmChunk())
8312     //    rc = false;
8313     //}
8314   }
8315   else
8316   {
8317     ON_ERROR("ON_BinaryArchive::Write3dmHatchPattern() must be called in BeginWrite3dmHatchPatternTable() block");
8318     rc = false;
8319   }
8320 
8321   return rc;
8322 }
8323 
EndWrite3dmHatchPatternTable()8324 bool ON_BinaryArchive::EndWrite3dmHatchPatternTable()
8325 {
8326   bool rc = false;
8327   rc = EndWrite3dmTable( TCODE_HATCHPATTERN_TABLE );
8328   return rc;
8329 }
8330 
BeginRead3dmHatchPatternTable()8331 bool ON_BinaryArchive::BeginRead3dmHatchPatternTable()
8332 {
8333   if ( m_3dm_version <= 3)
8334   {
8335     return true;
8336   }
8337   bool rc = BeginRead3dmTable( TCODE_HATCHPATTERN_TABLE );
8338 
8339   if ( !rc && m_3dm_opennurbs_version >= 200511010 )
8340   {
8341     // 1 November 2005 Dale Lear
8342     //    This fall back is slow but it has been finding
8343     //    layer and object tables in damaged files.  I'm
8344     //    adding it to the other BeginRead3dm...Table()
8345     //    functions when it makes sense.
8346     //    It only works on files with ver
8347     rc = FindMisplacedTable(
8348                 0,
8349                 TCODE_HATCHPATTERN_TABLE, TCODE_HATCHPATTERN_RECORD,
8350                 ON_HatchPattern::m_ON_HatchPattern_class_id.Uuid(),
8351                 30
8352                 );
8353     if ( rc )
8354     {
8355       rc = BeginRead3dmTable( TCODE_HATCHPATTERN_TABLE );
8356     }
8357   }
8358 
8359   return rc;
8360 }
8361 
Read3dmHatchPattern(ON_HatchPattern ** ppPattern)8362 int ON_BinaryArchive::Read3dmHatchPattern( ON_HatchPattern** ppPattern )
8363 {
8364   if( !ppPattern )
8365     return 0;
8366 
8367   *ppPattern = 0;
8368   if( m_3dm_version <= 3) // 1 Nov 2005 Dale lear: change < to <=
8369     return 0;             // because v3 files don't have hatch patterns.
8370 
8371   if ( m_active_table != hatchpattern_table )
8372   {
8373     ON_ERROR("ON_BinaryArchive::BeginRead3dmHatchPatternTable() - m_active_table != hatchpattern_table");
8374   }
8375   if ( m_3dm_opennurbs_version < 200405030 )
8376   {
8377     // 3DM archives written before version 200405030 do not have hatchpattern tables
8378     return 0;
8379   }
8380 
8381   ON__UINT32 tcode = 0;
8382   ON__INT64 big_value = 0;
8383   ON_HatchPattern* pPat = NULL;
8384   if( BeginRead3dmBigChunk( &tcode, &big_value))
8385   {
8386     if ( tcode == TCODE_HATCHPATTERN_RECORD )
8387     {
8388       if ( m_3dm_opennurbs_version < 200511010 )
8389       {
8390         // There was a bug in Write3dmHatchPattern and files written
8391         // before version 200511010 didn't use ON_Object IO.
8392         pPat = new ON_HatchPattern;
8393         if( !pPat->Read( *this))
8394         {
8395           delete pPat;
8396           pPat = NULL;
8397           ON_ERROR("ON_BinaryArchive::Read3dmHatchPattern() - corrupt hatch pattern table");
8398         }
8399       }
8400       else
8401       {
8402         ON_Object* p = 0;
8403         if ( ReadObject( &p ) )
8404         {
8405           pPat = ON_HatchPattern::Cast(p);
8406           if ( !pPat )
8407             delete p;
8408         }
8409         if (!pPat)
8410         {
8411           ON_ERROR("ON_BinaryArchive::Read3dmLayer() - corrupt layer table");
8412         }
8413       }
8414     }
8415     else if ( tcode != TCODE_ENDOFTABLE )
8416     {
8417       ON_ERROR("ON_BinaryArchive::Read3dmHatchPattern() - corrupt hatch pattern table");
8418     }
8419 
8420     EndRead3dmChunk();
8421   }
8422   *ppPattern = pPat;
8423   return( pPat) ? 1 : 0;
8424 }
8425 
EndRead3dmHatchPatternTable()8426 bool ON_BinaryArchive::EndRead3dmHatchPatternTable()
8427 {
8428   bool rc = false;
8429   if( m_3dm_version <= 3)
8430   {
8431     return true;
8432   }
8433   else
8434   {
8435     rc = EndRead3dmTable( TCODE_HATCHPATTERN_TABLE);
8436   }
8437   return rc;
8438 }
8439 
8440 
8441 
8442 ///////////////////////////////////////////////////////////
8443 ///////////////////////////////////////////////////////////
8444 ///////////////////////////////////////////////////////////
8445 ///////////////////////////////////////////////////////////
8446 
BeginWrite3dmLinetypeTable()8447 bool ON_BinaryArchive::BeginWrite3dmLinetypeTable()
8448 {
8449   bool rc = BeginWrite3dmTable( TCODE_LINETYPE_TABLE );
8450   return rc;
8451 }
8452 
Write3dmLinetype(const ON_Linetype & linetype)8453 bool ON_BinaryArchive::Write3dmLinetype( const ON_Linetype&  linetype )
8454 {
8455   bool rc = false;
8456 
8457   if( m_active_table != linetype_table )
8458   {
8459     ON_ERROR("ON_BinaryArchive::Write3dmLinetype() - m_active_table != linetype_table");
8460   }
8461 
8462   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
8463   if ( c && c->m_typecode == TCODE_LINETYPE_TABLE )
8464   {
8465     rc = BeginWrite3dmChunk( TCODE_LINETYPE_RECORD, 0 );
8466     if ( rc )
8467     {
8468       rc = WriteObject( linetype );
8469       if ( !EndWrite3dmChunk())
8470         rc = false;
8471     }
8472   }
8473   else
8474   {
8475     ON_ERROR("ON_BinaryArchive::Write3dmLinetype() must be called in BeginWrite3dmLinetypeTable() block");
8476     rc = false;
8477   }
8478 
8479   return rc;
8480 }
8481 
EndWrite3dmLinetypeTable()8482 bool ON_BinaryArchive::EndWrite3dmLinetypeTable()
8483 {
8484   bool rc = EndWrite3dmTable( TCODE_LINETYPE_TABLE );
8485   return rc;
8486 }
8487 
BeginRead3dmLinetypeTable()8488 bool ON_BinaryArchive::BeginRead3dmLinetypeTable()
8489 {
8490   bool rc = false;
8491 
8492   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200503170 )
8493   {
8494     rc = true;
8495   }
8496   else
8497   {
8498     rc = BeginRead3dmTable( TCODE_LINETYPE_TABLE );
8499     if ( !rc )
8500     {
8501       // 1 November 2005 Dale Lear
8502       //    This fall back is slow but it has been finding
8503       //    layer and object tables in damaged files.  I'm
8504       //    adding it to the other BeginRead3dm...Table()
8505       //    functions when it makes sense.
8506       rc = FindMisplacedTable(
8507                   0,
8508                   TCODE_LINETYPE_TABLE, TCODE_LINETYPE_RECORD,
8509                   ON_Linetype::m_ON_Linetype_class_id.Uuid(),
8510                   20
8511                   );
8512       if ( rc )
8513       {
8514         rc = BeginRead3dmTable( TCODE_LINETYPE_TABLE );
8515       }
8516     }
8517   }
8518 
8519   return rc;
8520 }
8521 
Read3dmLinetype(ON_Linetype ** ppLinetype)8522 int ON_BinaryArchive::Read3dmLinetype( ON_Linetype** ppLinetype )
8523 {
8524   if( !ppLinetype)
8525     return 0;
8526 
8527   *ppLinetype = 0;
8528 
8529   if( m_3dm_version < 4 || m_3dm_opennurbs_version < 200503170)
8530     return 0;
8531 
8532   if ( m_active_table != linetype_table )
8533   {
8534     ON_ERROR("ON_BinaryArchive::BeginRead3dmLinetypeTable() - m_active_table != linetype_table");
8535   }
8536 
8537   ON__UINT32 tcode = 0;
8538   ON__INT64 big_value = 0;
8539   ON_Linetype* linetype = NULL;
8540   int rc = -1;
8541   if( BeginRead3dmBigChunk( &tcode, &big_value))
8542   {
8543     if ( tcode == TCODE_LINETYPE_RECORD )
8544     {
8545       ON_Object* p = 0;
8546       if ( ReadObject( &p ) )
8547       {
8548         linetype = ON_Linetype::Cast(p);
8549         if (!linetype )
8550           delete p;
8551         else
8552         {
8553           if (ppLinetype)
8554             *ppLinetype = linetype;
8555           rc = 1;
8556         }
8557       }
8558       if (!linetype)
8559       {
8560         ON_ERROR("ON_BinaryArchive::Read3dmLinetype() - corrupt linetype table");
8561       }
8562     }
8563     else if ( tcode == TCODE_ENDOFTABLE )
8564     {
8565      // end of linetype table
8566       rc = 0;
8567     }
8568     else
8569     {
8570       ON_ERROR("ON_BinaryArchive::Read3dmLinetype() - corrupt linetype table");
8571     }
8572     if (!EndRead3dmChunk())
8573       rc = -1;
8574   }
8575 
8576   return rc;
8577 }
8578 
EndRead3dmLinetypeTable()8579 bool ON_BinaryArchive::EndRead3dmLinetypeTable()
8580 {
8581   bool rc = false;
8582   if( m_3dm_version < 4 || m_3dm_opennurbs_version < 200503170)
8583   {
8584     rc = true;
8585   }
8586   else
8587   {
8588     rc = EndRead3dmTable( TCODE_LINETYPE_TABLE);
8589   }
8590   return rc;
8591 }
8592 
8593 
8594 ///////////////////////////////////////////////////////////
8595 ///////////////////////////////////////////////////////////
8596 ///////////////////////////////////////////////////////////
8597 ///////////////////////////////////////////////////////////
8598 ///////////////////////////////////////////////////////////
8599 
BeginWrite3dmInstanceDefinitionTable()8600 bool ON_BinaryArchive::BeginWrite3dmInstanceDefinitionTable()
8601 {
8602   bool rc = false;
8603   rc = BeginWrite3dmTable( TCODE_INSTANCE_DEFINITION_TABLE );
8604   return rc;
8605 }
8606 
Write3dmInstanceDefinition(const ON_InstanceDefinition & idef)8607 bool ON_BinaryArchive::Write3dmInstanceDefinition( const ON_InstanceDefinition&  idef )
8608 {
8609   bool rc = false;
8610   if ( m_active_table != instance_definition_table ) {
8611     ON_ERROR("ON_BinaryArchive::Write3dmInstanceDefinition() - m_active_table != instance_definition_table");
8612   }
8613 
8614   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
8615   if ( c && c->m_typecode == TCODE_INSTANCE_DEFINITION_TABLE ) {
8616     rc = BeginWrite3dmChunk( TCODE_INSTANCE_DEFINITION_RECORD, 0 );
8617     if ( rc ) {
8618       rc = WriteObject( idef );
8619       if ( !EndWrite3dmChunk() )
8620         rc = false;
8621     }
8622   }
8623   else {
8624     ON_ERROR("ON_BinaryArchive::Write3dmInstanceDefinition() must be called in BeginWrite3dmInstanceDefinitionTable() block");
8625     rc = false;
8626   }
8627 
8628   return rc;
8629 }
8630 
EndWrite3dmInstanceDefinitionTable()8631 bool ON_BinaryArchive::EndWrite3dmInstanceDefinitionTable()
8632 {
8633   bool rc = false;
8634   rc = EndWrite3dmTable( TCODE_INSTANCE_DEFINITION_TABLE );
8635   return rc;
8636 }
8637 
BeginRead3dmInstanceDefinitionTable()8638 bool ON_BinaryArchive::BeginRead3dmInstanceDefinitionTable()
8639 {
8640   if ( m_3dm_version <= 2 ) {
8641     return true;
8642   }
8643   bool rc = false;
8644   rc = BeginRead3dmTable( TCODE_INSTANCE_DEFINITION_TABLE );
8645 
8646   if ( !rc )
8647   {
8648     // 1 November 2005 Dale Lear
8649     //    This fall back is slow but it has been finding
8650     //    layer and object tables in damaged files.  I'm
8651     //    adding it to the other BeginRead3dm...Table()
8652     //    functions when it makes sense.
8653     rc = FindMisplacedTable(
8654                 0,
8655                 TCODE_INSTANCE_DEFINITION_TABLE, TCODE_INSTANCE_DEFINITION_RECORD,
8656                 ON_InstanceDefinition::m_ON_InstanceDefinition_class_id.Uuid(),
8657                 30
8658                 );
8659     if ( rc )
8660     {
8661       rc = BeginRead3dmTable( TCODE_INSTANCE_DEFINITION_TABLE );
8662     }
8663   }
8664 
8665   return rc;
8666 }
8667 
Read3dmInstanceDefinition(ON_InstanceDefinition ** ppInstanceDefinition)8668 int ON_BinaryArchive::Read3dmInstanceDefinition( ON_InstanceDefinition** ppInstanceDefinition )
8669 {
8670   if ( !ppInstanceDefinition )
8671     return 0;
8672   *ppInstanceDefinition = 0;
8673   if ( m_3dm_version <= 2 ) {
8674     return 0;
8675   }
8676   if ( m_active_table != instance_definition_table )
8677   {
8678     ON_ERROR("ON_BinaryArchive::BeginRead3dmInstanceDefinitionTable() - m_active_table != no_active_table");
8679   }
8680   if ( m_3dm_opennurbs_version < 200205110 )
8681   {
8682     // 3DM archives written before version 200205110 and before do not have instance definition tables
8683     return 0;
8684   }
8685 
8686   ON__UINT32 tcode = 0;
8687   ON__INT64 big_value = 0;
8688   ON_InstanceDefinition* idef = NULL;
8689   if ( BeginRead3dmBigChunk( &tcode, &big_value ) )
8690   {
8691     if ( tcode == TCODE_INSTANCE_DEFINITION_RECORD ) {
8692       ON_Object* p = 0;
8693       if ( ReadObject( &p ) ) {
8694         idef = ON_InstanceDefinition::Cast(p);
8695         if ( !idef )
8696           delete p;
8697       }
8698       if (!idef) {
8699         ON_ERROR("ON_BinaryArchive::Read3dmInstanceDefinition() - corrupt instance definition table");
8700       }
8701     }
8702     else if ( tcode != TCODE_ENDOFTABLE ) {
8703       ON_ERROR("ON_BinaryArchive::Read3dmInstanceDefinition() - corrupt instance definition table");
8704     }
8705     EndRead3dmChunk();
8706   }
8707   *ppInstanceDefinition = idef;
8708   return (idef) ? 1 : 0;
8709 }
8710 
EndRead3dmInstanceDefinitionTable()8711 bool ON_BinaryArchive::EndRead3dmInstanceDefinitionTable()
8712 {
8713   bool rc = false;
8714   if ( m_3dm_version <= 2 ) {
8715     return true;
8716   }
8717   else {
8718     rc = EndRead3dmTable( TCODE_INSTANCE_DEFINITION_TABLE );
8719   }
8720   return rc;
8721 }
8722 
8723 ///////////////////////////////////////////////////////////
8724 ///////////////////////////////////////////////////////////
8725 ///////////////////////////////////////////////////////////
8726 ///////////////////////////////////////////////////////////
8727 ///////////////////////////////////////////////////////////
8728 
BeginWrite3dmTextureMappingTable()8729 bool ON_BinaryArchive::BeginWrite3dmTextureMappingTable()
8730 {
8731   return BeginWrite3dmTable( TCODE_TEXTURE_MAPPING_TABLE );
8732 }
8733 
Write3dmTextureMapping(const ON_TextureMapping & texture_mapping)8734 bool ON_BinaryArchive::Write3dmTextureMapping( const ON_TextureMapping& texture_mapping )
8735 {
8736   bool rc = false;
8737 
8738   if ( m_active_table != texture_mapping_table )
8739   {
8740     ON_ERROR("ON_BinaryArchive::Write3dmTextureMapping() - m_active_table != texture_mapping_table");
8741   }
8742 
8743   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
8744   if ( !c || c->m_typecode != TCODE_TEXTURE_MAPPING_TABLE )
8745   {
8746     ON_ERROR("ON_BinaryArchive::Write3dmTextureMapping() - active chunk typecode != TCODE_TEXTURE_MAPPING_TABLE");
8747   }
8748   else
8749   {
8750     rc = BeginWrite3dmChunk( TCODE_TEXTURE_MAPPING_RECORD, 0 );
8751     if (rc)
8752     {
8753       rc = WriteObject( texture_mapping );
8754       if ( !EndWrite3dmChunk() )
8755         rc = false;
8756     }
8757   }
8758   return rc;
8759 }
8760 
EndWrite3dmTextureMappingTable()8761 bool ON_BinaryArchive::EndWrite3dmTextureMappingTable()
8762 {
8763   return EndWrite3dmTable( TCODE_TEXTURE_MAPPING_TABLE );
8764 }
8765 
BeginRead3dmTextureMappingTable()8766 bool ON_BinaryArchive::BeginRead3dmTextureMappingTable()
8767 {
8768   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200511110 )
8769   {
8770     return true;
8771   }
8772 
8773   bool rc = BeginRead3dmTable( TCODE_TEXTURE_MAPPING_TABLE );
8774   if ( !rc )
8775   {
8776     // 31 October 2005 Dale Lear
8777     //    This fall back is slow but it will find
8778     //    texture_mapping tables in files that have been damaged.
8779     //
8780     //    This approach has been tested with layer tables
8781     //    for over a year and has successfully made files
8782     //    with garbled starts read correctly after the
8783     //    call to EmergencyFindTable was able to detect
8784     //    the start of the layer table.  I'm adding it
8785     //    to texture_mapping tables now because I have a good
8786     //    test file.
8787     rc = FindMisplacedTable(
8788                 0,
8789                 TCODE_TEXTURE_MAPPING_TABLE, TCODE_TEXTURE_MAPPING_RECORD,
8790                 ON_TextureMapping::m_ON_TextureMapping_class_id.Uuid(),
8791                 sizeof(ON_TextureMapping)
8792                 );
8793     if ( rc )
8794     {
8795       rc = BeginRead3dmTable( TCODE_TEXTURE_MAPPING_TABLE );
8796     }
8797   }
8798   return rc;
8799 }
8800 
Read3dmTextureMapping(ON_TextureMapping ** ppTextureMapping)8801 int ON_BinaryArchive::Read3dmTextureMapping( ON_TextureMapping** ppTextureMapping )
8802 {
8803   int rc = 0;
8804   if ( !ppTextureMapping )
8805     return 0;
8806   *ppTextureMapping = 0;
8807   ON_TextureMapping* texture_mapping = NULL;
8808   ON__UINT32 tcode = 0;
8809   ON__INT64 big_value = 0;
8810   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200511110 )
8811   {
8812     // no texture mapping table until version 200511110 of v4 files
8813     return 0;
8814   }
8815 
8816   rc = -1;
8817   if ( BeginRead3dmBigChunk( &tcode, &big_value ) )
8818   {
8819     if ( tcode == TCODE_TEXTURE_MAPPING_RECORD )
8820     {
8821       ON_Object* p = 0;
8822       if ( ReadObject( &p ) )
8823       {
8824         texture_mapping = ON_TextureMapping::Cast(p);
8825         if ( !texture_mapping )
8826           delete p;
8827         else
8828         {
8829           if ( ppTextureMapping )
8830             *ppTextureMapping = texture_mapping;
8831           rc = 1;
8832         }
8833       }
8834       if (!texture_mapping)
8835       {
8836         ON_ERROR("ON_BinaryArchive::Read3dmTextureMapping() - corrupt texture_mapping table");
8837       }
8838     }
8839     else if ( tcode == TCODE_ENDOFTABLE )
8840     {
8841       // end of texture_mapping table
8842       rc = 0;
8843     }
8844     else
8845     {
8846       ON_ERROR("ON_BinaryArchive::Read3dmTextureMapping() - corrupt texture_mapping table");
8847     }
8848     if ( !EndRead3dmChunk() )
8849       rc = -1;
8850   }
8851 
8852   return rc;
8853 }
8854 
EndRead3dmTextureMappingTable()8855 bool ON_BinaryArchive::EndRead3dmTextureMappingTable()
8856 {
8857   bool rc = false;
8858   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200511110 )
8859   {
8860     rc = true;
8861   }
8862   else
8863   {
8864     rc = EndRead3dmTable( TCODE_TEXTURE_MAPPING_TABLE );
8865   }
8866   return rc;
8867 }
8868 
8869 ///////////////////////////////////////////////////////////
8870 ///////////////////////////////////////////////////////////
8871 ///////////////////////////////////////////////////////////
8872 ///////////////////////////////////////////////////////////
8873 ///////////////////////////////////////////////////////////
8874 
BeginWrite3dmHistoryRecordTable()8875 bool ON_BinaryArchive::BeginWrite3dmHistoryRecordTable()
8876 {
8877   return BeginWrite3dmTable( TCODE_HISTORYRECORD_TABLE );
8878 }
8879 
Write3dmHistoryRecord(const ON_HistoryRecord & history_record)8880 bool ON_BinaryArchive::Write3dmHistoryRecord( const ON_HistoryRecord& history_record )
8881 {
8882   bool rc = false;
8883 
8884   if ( m_active_table != historyrecord_table )
8885   {
8886     ON_ERROR("ON_BinaryArchive::Write3dmHistoryRecord() - m_active_table != history_record_table");
8887   }
8888 
8889   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
8890   if ( !c || c->m_typecode != TCODE_HISTORYRECORD_TABLE )
8891   {
8892     ON_ERROR("ON_BinaryArchive::Write3dmHistoryRecord() - active chunk typecode != TCODE_HISTORYRECORD_TABLE");
8893   }
8894   else
8895   {
8896     rc = BeginWrite3dmChunk( TCODE_HISTORYRECORD_RECORD, 0 );
8897     if (rc)
8898     {
8899       rc = WriteObject( history_record );
8900       if ( !EndWrite3dmChunk() )
8901         rc = false;
8902     }
8903   }
8904   return rc;
8905 }
8906 
EndWrite3dmHistoryRecordTable()8907 bool ON_BinaryArchive::EndWrite3dmHistoryRecordTable()
8908 {
8909   return EndWrite3dmTable( TCODE_HISTORYRECORD_TABLE );
8910 }
8911 
BeginRead3dmHistoryRecordTable()8912 bool ON_BinaryArchive::BeginRead3dmHistoryRecordTable()
8913 {
8914   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200601180 )
8915   {
8916     return true;
8917   }
8918 
8919   bool rc = BeginRead3dmTable( TCODE_HISTORYRECORD_TABLE );
8920   if ( !rc )
8921   {
8922     // 31 October 2005 Dale Lear
8923     //    This fall back is slow but it will find
8924     //    history_record tables in files that have been damaged.
8925     //
8926     //    This approach has been tested with layer tables
8927     //    for over a year and has successfully made files
8928     //    with garbled starts read correctly after the
8929     //    call to EmergencyFindTable was able to detect
8930     //    the start of the layer table.  I'm adding it
8931     //    to history_record tables now because I have a good
8932     //    test file.
8933     rc = FindMisplacedTable(
8934                 0,
8935                 TCODE_HISTORYRECORD_TABLE, TCODE_HISTORYRECORD_RECORD,
8936                 ON_HistoryRecord::m_ON_HistoryRecord_class_id.Uuid(),
8937                 sizeof(ON_HistoryRecord)
8938                 );
8939     if ( rc )
8940     {
8941       rc = BeginRead3dmTable( TCODE_HISTORYRECORD_TABLE );
8942     }
8943   }
8944   return rc;
8945 }
8946 
Read3dmHistoryRecord(ON_HistoryRecord * & history_record)8947 int ON_BinaryArchive::Read3dmHistoryRecord( ON_HistoryRecord*& history_record )
8948 {
8949   int rc = 0;
8950   history_record = 0;
8951   ON__UINT32 tcode = 0;
8952   ON__INT64 big_value = 0;
8953   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200601180 )
8954   {
8955     // no history record table until version 200601180 of v4 files
8956     return 0;
8957   }
8958 
8959   rc = -1;
8960   if ( BeginRead3dmBigChunk( &tcode, &big_value ) )
8961   {
8962     if ( tcode == TCODE_HISTORYRECORD_RECORD )
8963     {
8964       ON_Object* p = 0;
8965       if ( ReadObject( &p ) )
8966       {
8967         history_record = ON_HistoryRecord::Cast(p);
8968         if ( !history_record )
8969         {
8970           delete p;
8971         }
8972         else
8973         {
8974           rc = 1;
8975         }
8976       }
8977       if (!history_record)
8978       {
8979         ON_ERROR("ON_BinaryArchive::Read3dmHistoryRecord() - corrupt history_record table");
8980       }
8981     }
8982     else if ( tcode == TCODE_ENDOFTABLE )
8983     {
8984       // end of history_record table
8985       rc = 0;
8986     }
8987     else
8988     {
8989       ON_ERROR("ON_BinaryArchive::Read3dmHistoryRecord() - corrupt history_record table");
8990     }
8991     if ( !EndRead3dmChunk() )
8992       rc = -1;
8993   }
8994 
8995   return rc;
8996 }
8997 
EndRead3dmHistoryRecordTable()8998 bool ON_BinaryArchive::EndRead3dmHistoryRecordTable()
8999 {
9000   bool rc = false;
9001   if ( m_3dm_version < 4 || m_3dm_opennurbs_version < 200601180 )
9002   {
9003     rc = true;
9004   }
9005   else
9006   {
9007     rc = EndRead3dmTable( TCODE_HISTORYRECORD_TABLE );
9008   }
9009   return rc;
9010 }
9011 
9012 ///////////////////////////////////////////////////////////
9013 ///////////////////////////////////////////////////////////
9014 ///////////////////////////////////////////////////////////
9015 ///////////////////////////////////////////////////////////
9016 ///////////////////////////////////////////////////////////
9017 
BeginWrite3dmMaterialTable()9018 bool ON_BinaryArchive::BeginWrite3dmMaterialTable()
9019 {
9020   return BeginWrite3dmTable( TCODE_MATERIAL_TABLE );
9021 }
9022 
Write3dmMaterial(const ON_Material & material)9023 bool ON_BinaryArchive::Write3dmMaterial( const ON_Material& material )
9024 {
9025   bool rc = false;
9026 
9027   if ( m_active_table != material_table )
9028   {
9029     ON_ERROR("ON_BinaryArchive::Write3dmMaterial() - m_active_table != material_table");
9030   }
9031 
9032   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
9033   if ( !c || c->m_typecode != TCODE_MATERIAL_TABLE )
9034   {
9035     ON_ERROR("ON_BinaryArchive::Write3dmMaterial() - active chunk typecode != TCODE_MATERIAL_TABLE");
9036   }
9037   else
9038   {
9039     rc = BeginWrite3dmChunk( TCODE_MATERIAL_RECORD, 0 );
9040     if (rc)
9041     {
9042       rc = WriteObject( material );
9043       if ( !EndWrite3dmChunk() )
9044         rc = false;
9045     }
9046   }
9047   return rc;
9048 }
9049 
EndWrite3dmMaterialTable()9050 bool ON_BinaryArchive::EndWrite3dmMaterialTable()
9051 {
9052   return EndWrite3dmTable( TCODE_MATERIAL_TABLE );
9053 }
9054 
BeginRead3dmMaterialTable()9055 bool ON_BinaryArchive::BeginRead3dmMaterialTable()
9056 {
9057   m_3dm_v1_material_index = 0;
9058   bool rc = BeginRead3dmTable( TCODE_MATERIAL_TABLE );
9059   if ( !rc )
9060   {
9061     // 31 October 2005 Dale Lear
9062     //    This fall back is slow but it will find
9063     //    material tables in files that have been damaged.
9064     //
9065     //    This approach has been tested with layer tables
9066     //    for over a year and has successfully made files
9067     //    with garbled starts read correctly after the
9068     //    call to EmergencyFindTable was able to detect
9069     //    the start of the layer table.  I'm adding it
9070     //    to material tables now because I have a good
9071     //    test file.
9072     rc = FindMisplacedTable(
9073                 0,
9074                 TCODE_MATERIAL_TABLE, TCODE_MATERIAL_RECORD,
9075                 ON_Material::m_ON_Material_class_id.Uuid(),
9076                 114
9077                 );
9078     if ( rc )
9079     {
9080       rc = BeginRead3dmTable( TCODE_MATERIAL_TABLE );
9081     }
9082   }
9083   return rc;
9084 }
9085 
Read3dmV1String(ON_String & s)9086 bool ON_BinaryArchive::Read3dmV1String( ON_String& s )
9087 {
9088   int string_length = 0;
9089   s.Empty();
9090   bool rc = ReadInt( &string_length );
9091   if (rc) {
9092     s.ReserveArray(string_length+1);
9093     rc = ReadChar( string_length, s.Array() );
9094     if (rc)
9095       s.SetLength(string_length);
9096   }
9097   return rc;
9098 }
9099 
9100 
9101 class ON__3dmV1_XDATA
9102 {
9103   // helper class to get V1 "xdata" out of attributes block.
9104 public:
9105   enum
9106   {
9107     unknown_xdata = 0,
9108     hidden_object_layer_name, // m_string = actual layer name
9109     locked_object_layer_name, // m_string = actual layer name
9110     arrow_direction,          // m_vector = arrow head location
9111     dot_text                  // m_string = dot text
9112   }
9113   m_type;
9114   ON_String m_string;
9115   ON_3dVector m_vector;
9116 };
9117 
Read3dmV1AttributesOrMaterial(ON_3dmObjectAttributes * attributes,ON_Material * material,ON_BOOL32 & bHaveMat,unsigned int end_mark_tcode,ON__3dmV1_XDATA * xdata)9118 bool ON_BinaryArchive::Read3dmV1AttributesOrMaterial(
9119                          ON_3dmObjectAttributes* attributes,
9120                          ON_Material* material,
9121                          ON_BOOL32& bHaveMat,
9122                          unsigned int end_mark_tcode,
9123                          ON__3dmV1_XDATA* xdata
9124                          )
9125 {
9126   // Check ReadV1Material() if you fix any bugs in the mateial related items
9127 
9128   if ( 0 != xdata )
9129   {
9130     xdata->m_type = ON__3dmV1_XDATA::unknown_xdata;
9131   }
9132 
9133   bool rc = false;
9134   unsigned int u;
9135   ON__UINT32 tcode = 0;
9136   ON__INT64 big_value = 0;
9137   ON_Color c;
9138   bHaveMat = false;
9139   bool bEndRead3dmChunk_rc;
9140 
9141   const unsigned int saved_error_message_mask = m_error_message_mask;
9142 
9143   int xdata_layer_index = -1;
9144 
9145   if ( attributes )
9146   {
9147     attributes->Default();
9148   }
9149 
9150   if ( material )
9151   {
9152     material->Default();
9153     material->m_diffuse.SetRGB(255,255,255);
9154     material->m_specular.SetRGB(255,255,255);
9155     material->m_ambient.SetRGB(0,0,0);
9156   }
9157 
9158   for (;;)
9159   {
9160     m_error_message_mask = saved_error_message_mask;
9161 
9162     if ( end_mark_tcode != TCODE_ENDOFTABLE ) {
9163       tcode = 0;
9164       big_value = 0;
9165       if ( !PeekAt3dmBigChunkType(&tcode,&big_value) ) {
9166         break; // should not happen
9167       }
9168       if ( tcode == end_mark_tcode ) {
9169         rc = true;
9170         break; // done reading attributes
9171       }
9172     }
9173     tcode = 0;
9174     big_value = 0;
9175     if ( !BeginRead3dmBigChunk(&tcode,&big_value) )
9176       break;
9177     if ( tcode == end_mark_tcode ) {
9178       rc = EndRead3dmChunk();
9179       break;
9180     }
9181 
9182     switch( tcode )
9183     {
9184     case (TCODE_OPENNURBS_OBJECT | TCODE_CRC | 0x7FFD):
9185       // 1.1 object 16 byte UUID + 2 byte crc
9186       if ( attributes )
9187         ReadUuid( attributes->m_uuid );
9188       break;
9189 
9190     case TCODE_LAYERREF:
9191       if (    attributes
9192            && (-1 == xdata_layer_index || attributes->m_layer_index != xdata_layer_index)
9193            && (big_value >= 0 && big_value < 0x7FFFFFFF)
9194          )
9195       {
9196         attributes->m_layer_index = (int)big_value;
9197       }
9198       break;
9199 
9200     case TCODE_RGB:
9201       if ( big_value != 0xFFFFFF )
9202       {
9203         if ( material )
9204         {
9205           ON__UINT64 rgb64 = (ON__UINT64)big_value;
9206           ON__UINT32 rgb32 = (ON__UINT32)rgb64;
9207           u = rgb32;
9208           c.SetRGB( u%256,(u>>8)%256,(u>>16)%256 );
9209           material->SetDiffuse(c);
9210           material->SetShine((u >> 24)/100.0*ON_Material::MaxShine());
9211         }
9212         bHaveMat = true;
9213       }
9214       break;
9215 
9216     case TCODE_RGBDISPLAY:
9217       if ( attributes )
9218       {
9219           ON__UINT64 rgb64 = (ON__UINT64)big_value;
9220           ON__UINT32 rgb32 = (ON__UINT32)rgb64;
9221           u = rgb32;
9222         attributes->m_color.SetRGB( u%256,(u>>8)%256,(u>>16)%256 );
9223       }
9224       break;
9225 
9226     case TCODE_TRANSPARENCY:
9227       if ( big_value > 0 && big_value <= 255 )
9228       {
9229         if ( material )
9230           material->SetTransparency(big_value/255.0);
9231         bHaveMat = true;
9232       }
9233       break;
9234 
9235     case TCODE_NAME:
9236       if ( attributes ) {
9237         ON_String s;
9238         Read3dmV1String(s);
9239         if( s.Length() > 0 )
9240           attributes->m_name = s;
9241       }
9242       break;
9243 
9244     case TCODE_TEXTUREMAP:
9245       {
9246         ON_String s;
9247         Read3dmV1String(s);
9248         if ( s.Length() > 0 )
9249         {
9250           if ( material )
9251           {
9252             ON_Texture& tx = material->m_textures.AppendNew();
9253             tx.m_filename = s;
9254             tx.m_type = ON_Texture::bitmap_texture;
9255           }
9256           bHaveMat = true;
9257         }
9258       }
9259       break;
9260 
9261     case TCODE_BUMPMAP:
9262       if ( material ) {
9263         ON_String s;
9264         Read3dmV1String(s);
9265         if ( s.Length() )
9266         {
9267           if ( material )
9268           {
9269             ON_Texture& tx = material->m_textures.AppendNew();
9270             tx.m_filename = s;
9271             tx.m_type = ON_Texture::bump_texture;
9272           }
9273           bHaveMat = true;
9274         }
9275       }
9276       break;
9277 
9278     case TCODE_XDATA:
9279       // v1 "xdata"
9280       if ( attributes )
9281       {
9282         ON_String layer_name;
9283         ON_String xid;
9284         int sizeof_xid = 0;
9285         int sizeof_data = 0;
9286         ReadInt(&sizeof_xid);
9287         ReadInt(&sizeof_data);
9288         xid.SetLength(sizeof_xid);
9289         ReadByte(sizeof_xid,xid.Array());
9290         if ( !on_stricmp("RhHidePrevLayer",xid) )
9291         {
9292           if ( sizeof_data > 0 )
9293           {
9294             // v1 object is hidden - real layer name is in xdata
9295             char* buffer = (char*)onmalloc((sizeof_data+1)*sizeof(buffer[0]));
9296             buffer[0] = 0;
9297             buffer[sizeof_data] = 0;
9298             if ( ReadByte(sizeof_data,buffer) )
9299             {
9300               if ( -1 == xdata_layer_index )
9301               {
9302                 xdata_layer_index = Read3dmV1LayerIndex(buffer);
9303                 if ( xdata_layer_index >= 0 )
9304                 {
9305                   attributes->m_layer_index = xdata_layer_index;
9306                   attributes->SetVisible(false);
9307                 }
9308               }
9309               else
9310               {
9311                 xdata_layer_index = -2;
9312               }
9313               //if ( 0 != xdata )
9314               //{
9315               //  xdata->m_type = ON__3dmV1_XDATA::hidden_object_layer_name;
9316               //  xdata->m_string = buffer;
9317               //}
9318             }
9319             onfree(buffer);
9320           }
9321         }
9322         else if ( !on_stricmp("RhFreezePrevLayer",xid) )
9323         {
9324           // v1 object is locked - real layer name is in xdata
9325           if ( sizeof_data > 0 )
9326           {
9327             char* buffer = (char*)onmalloc((sizeof_data+1)*sizeof(buffer[0]));
9328             buffer[0] = 0;
9329             buffer[sizeof_data] = 0;
9330             if ( ReadByte(sizeof_data,buffer)  )
9331             {
9332               if ( -1 == xdata_layer_index )
9333               {
9334                 xdata_layer_index = Read3dmV1LayerIndex(buffer);
9335                 if ( xdata_layer_index >= 0 )
9336                 {
9337                   attributes->m_layer_index = xdata_layer_index;
9338                   attributes->SetMode(ON::locked_object);
9339                 }
9340               }
9341               else
9342               {
9343                 xdata_layer_index = -2;
9344               }
9345               //if ( 0 != xdata )
9346               //{
9347               //  xdata->m_type = ON__3dmV1_XDATA::locked_object_layer_name;
9348               //  xdata->m_string = buffer;
9349               //}
9350             }
9351             onfree(buffer);
9352           }
9353         }
9354         else if ( !on_stricmp("RhAnnotateArrow",xid) && 24 == sizeof_data )
9355         {
9356           // v1 annotation arrow objects were saved
9357           // as TCODE_RH_POINT objects with the
9358           // arrow tail location = point location and the
9359           // arrow head location saved in 24 bytes of "xdata".
9360           ON_3dVector arrow_direction;
9361           if ( ReadVector( arrow_direction ) && 0 != xdata )
9362           {
9363             xdata->m_type = ON__3dmV1_XDATA::arrow_direction;
9364             xdata->m_vector = arrow_direction;
9365           }
9366         }
9367         else if ( !on_stricmp("RhAnnotateDot",xid) )
9368         {
9369           if ( sizeof_data > 0 )
9370           {
9371             // v1 annotation dot objects were saved
9372             // as TCODE_RH_POINT objects with the
9373             // dot text saved in "xdata".
9374             char* buffer = (char*)onmalloc((sizeof_data+1)*sizeof(buffer[0]));
9375             buffer[0] = 0;
9376             buffer[sizeof_data] = 0;
9377             if ( ReadByte(sizeof_data,buffer) && 0 != xdata )
9378             {
9379               xdata->m_type = ON__3dmV1_XDATA::dot_text;
9380               xdata->m_string = buffer;
9381             }
9382             onfree(buffer);
9383           }
9384         }
9385         else
9386         {
9387           m_error_message_mask |= 0x0002; // disable v1 EndRead3dmChunk() partially read chunk warning
9388         }
9389         // call to EndRead3dmChunk() will skip unread junk
9390       }
9391       break;
9392 
9393     case TCODE_DISP_CPLINES:
9394       if ( big_value > 0 && big_value <= 0x7FFFFFFF && attributes )
9395         attributes->m_wire_density = (int)big_value;
9396       break;
9397 
9398     case TCODE_RENDER_MATERIAL_ID:
9399       {
9400         int flag;
9401         ON_String s;
9402         ReadInt(&flag);
9403         if ( flag == 1 ) {
9404           Read3dmV1String(s);
9405           if ( s.Length() > 0 )
9406           {
9407             if ( material )
9408               material->m_material_name = s;
9409             bHaveMat = true;
9410           }
9411         }
9412       }
9413       break;
9414 
9415     default:
9416       // obsolete attributes from v1
9417       m_error_message_mask |= 0x02; // disable v1 EndRead3dmChunk() partially read chunk warning
9418       break;
9419     }
9420 
9421     bEndRead3dmChunk_rc = EndRead3dmChunk();
9422     if ( !bEndRead3dmChunk_rc )
9423       break;
9424   }
9425 
9426   m_error_message_mask = saved_error_message_mask;
9427 
9428   if ( bHaveMat ) {
9429     if ( attributes )
9430       attributes->m_material_index = m_3dm_v1_material_index;
9431     if ( material )
9432       material->SetMaterialIndex(m_3dm_v1_material_index);
9433     m_3dm_v1_material_index++;
9434   }
9435 
9436   return rc;
9437 }
9438 
9439 
9440 
Read3dmV1Material(ON_Material ** ppMaterial)9441 int ON_BinaryArchive::Read3dmV1Material( ON_Material** ppMaterial )
9442 {
9443   int rc = 0;
9444   // returns -1: failure
9445   //          0: end of material table
9446   //          1: success
9447 
9448   ON_Material material;
9449   ON__UINT32 tcode = 0;
9450   ON__INT64 big_value = 0;
9451   ON_BOOL32 bHaveMat;
9452   bool bEndReadChunk_rc;
9453   // reads NURBS, point, and mesh objects
9454 
9455   while( 0 == rc )
9456   {
9457     bHaveMat = false;
9458     rc = 0;
9459     tcode = 0;
9460     big_value = 0;
9461     if ( !BeginRead3dmBigChunk(&tcode,&big_value) )
9462     {
9463       // assume we are at the end of the file
9464       break;
9465     }
9466 
9467     switch(tcode)
9468     {
9469     case TCODE_RH_POINT:
9470       // v1 3d point
9471       {
9472         ON_3DM_BIG_CHUNK* point_chunk = m_chunk.Last();
9473         ON__UINT64 pos0 = 0;
9474         if (    0 != point_chunk
9475              && TCODE_RH_POINT == point_chunk->m_typecode
9476              && 0 == point_chunk->m_big_value )
9477         {
9478           // Some V1 files have TCODE_RH_POINT chunks with length=0.
9479           // (It appears that points with arrow xdata have this problem.)
9480           // For these chunks we need to set the chunk length so EndRead3dmChunk()
9481           // will keep going.
9482           pos0 = CurrentPosition();
9483         }
9484         else
9485           point_chunk = 0;
9486 
9487         ON_3dPoint pt; // need to read point to get to material defn
9488         bool bOK = ReadPoint( pt );
9489 
9490         if ( bOK )
9491           bOK = Read3dmV1AttributesOrMaterial( NULL, &material, bHaveMat, TCODE_ENDOFTABLE );
9492 
9493         if ( !bOK )
9494           rc = -1;
9495         // else if appropriate, rc will get set to +1 below.
9496 
9497         if ( bOK
9498              && 0 != point_chunk
9499              && point_chunk == m_chunk.Last()
9500              && TCODE_RH_POINT == point_chunk->m_typecode
9501              && 0 == point_chunk->m_big_value )
9502         {
9503           // set the chunk length so that reading can continue.
9504           ON__UINT64 pos1 = CurrentPosition();
9505           ON__UINT64 chunk_length = (pos1 > pos0) ? (pos1 - pos0) : 0;
9506           if ( chunk_length >= 32 && chunk_length < 0x0FFFFFFF )
9507             point_chunk->m_big_value = (ON__INT64)chunk_length;
9508         }
9509       }
9510       break;
9511 
9512     case TCODE_MESH_OBJECT:
9513       // v1 mesh
9514       {
9515         ON__UINT32 tc = 0;
9516         ON__INT64 i64 = 0;
9517         if ( !PeekAt3dmBigChunkType( &tc, &i64 ) )
9518           break;
9519         if ( tc != TCODE_COMPRESSED_MESH_GEOMETRY )
9520           break;
9521         // skip over the TCODE_COMPRESSED_MESH_GEOMETRY chunk
9522         if ( !BeginRead3dmBigChunk(&tc,&i64) )
9523           break;
9524         if ( !EndRead3dmChunk() )
9525           break;
9526         // attributes and material informtion follow the TCODE_COMPRESSED_MESH_GEOMETRY chunk
9527         if ( !Read3dmV1AttributesOrMaterial( NULL, &material, bHaveMat, TCODE_ENDOFTABLE ) )
9528           rc = -1;
9529         // if appropriate, rc will get set to +1 below
9530       }
9531       break;
9532 
9533     case TCODE_LEGACY_SHL:
9534       // v1 polysurface
9535       if ( !Read3dmV1AttributesOrMaterial( NULL, &material, bHaveMat, TCODE_LEGACY_SHLSTUFF ) )
9536         rc = -1;
9537         // if appropriate, rc will get set to +1 below
9538       break;
9539     case TCODE_LEGACY_FAC:
9540       // v1 trimmed surface
9541       if ( !Read3dmV1AttributesOrMaterial( NULL, &material, bHaveMat, TCODE_LEGACY_FACSTUFF ) )
9542         rc = -1;
9543         // if appropriate, rc will get set to +1 below
9544       break;
9545     case TCODE_LEGACY_CRV:
9546       // v1 curve
9547       if ( !Read3dmV1AttributesOrMaterial( NULL, &material, bHaveMat, TCODE_LEGACY_CRVSTUFF ) )
9548         rc = -1;
9549         // if appropriate, rc will get set to +1 below
9550       break;
9551 
9552     case TCODE_RHINOIO_OBJECT_NURBS_CURVE:
9553     case TCODE_RHINOIO_OBJECT_NURBS_SURFACE:
9554     case TCODE_RHINOIO_OBJECT_BREP:
9555       // old Rhino I/O toolkit nurbs curve, surface, and breps
9556       {
9557         ON__UINT32 tc = 0;
9558         ON__INT64 i64 = 0;
9559         if ( !PeekAt3dmBigChunkType( &tc, &i64 ) )
9560           break;
9561         if ( tc != TCODE_RHINOIO_OBJECT_DATA )
9562           break;
9563         // skip over the TCODE_RHINOIO_OBJECT_DATA chunk
9564         if ( !BeginRead3dmBigChunk(&tc,&i64) )
9565           break;
9566         if ( !EndRead3dmChunk() )
9567           break;
9568         if ( !Read3dmV1AttributesOrMaterial( NULL, &material, bHaveMat, TCODE_RHINOIO_OBJECT_END ) )
9569           rc = -1;
9570         // if appropriate, rc will get set to +1 below
9571       }
9572       break;
9573     }
9574 
9575     const unsigned int saved_error_message_mask = m_error_message_mask;
9576     m_error_message_mask |= 0x02; // disable v1 EndRead3dmChunk() partially read chunk warning
9577     bEndReadChunk_rc = EndRead3dmChunk();
9578     m_error_message_mask = saved_error_message_mask; // enable v1 EndRead3dmChunk() partially read chunk warning
9579     if (!bEndReadChunk_rc )
9580     {
9581       rc = -1;
9582       break;
9583     }
9584     if ( bHaveMat && ppMaterial)
9585     {
9586       // found a valid non-default material
9587       *ppMaterial = new ON_Material(material);
9588       rc = 1;
9589       break;
9590     }
9591   }
9592 
9593   return rc;
9594 }
9595 
9596 
Read3dmMaterial(ON_Material ** ppMaterial)9597 int ON_BinaryArchive::Read3dmMaterial( ON_Material** ppMaterial )
9598 {
9599   int rc = 0;
9600   if ( !ppMaterial )
9601     return 0;
9602   *ppMaterial = 0;
9603   ON_Material* material = NULL;
9604   ON__UINT32 tcode = 0;
9605   ON__INT64 big_value = 0;
9606   if ( m_3dm_version == 1 )
9607   {
9608     rc = ON_BinaryArchive::Read3dmV1Material( ppMaterial );
9609   }
9610   else
9611   {
9612     // version 2+
9613     rc = -1;
9614     if ( BeginRead3dmBigChunk( &tcode, &big_value ) )
9615     {
9616       if ( tcode == TCODE_MATERIAL_RECORD )
9617       {
9618         ON_Object* p = 0;
9619         if ( ReadObject( &p ) )
9620         {
9621           material = ON_Material::Cast(p);
9622           if ( !material )
9623             delete p;
9624           else
9625           {
9626             if ( ppMaterial )
9627               *ppMaterial = material;
9628             rc = 1;
9629           }
9630         }
9631         if (!material)
9632         {
9633           ON_ERROR("ON_BinaryArchive::Read3dmMaterial() - corrupt material table");
9634         }
9635       }
9636       else if ( tcode == TCODE_ENDOFTABLE )
9637       {
9638         // end of material table
9639         rc = 0;
9640       }
9641       else
9642       {
9643         ON_ERROR("ON_BinaryArchive::Read3dmMaterial() - corrupt material table");
9644       }
9645       if ( !EndRead3dmChunk() )
9646         rc = -1;
9647     }
9648   }
9649   return rc;
9650 }
9651 
EndRead3dmMaterialTable()9652 bool ON_BinaryArchive::EndRead3dmMaterialTable()
9653 {
9654   return EndRead3dmTable( TCODE_MATERIAL_TABLE );
9655 }
9656 
9657 
BeginWrite3dmLightTable()9658 bool ON_BinaryArchive::BeginWrite3dmLightTable()
9659 {
9660   return BeginWrite3dmTable( TCODE_LIGHT_TABLE );
9661 }
9662 
Write3dmLight(const ON_Light & light,const ON_3dmObjectAttributes * attributes)9663 bool ON_BinaryArchive::Write3dmLight( const ON_Light& light,  const ON_3dmObjectAttributes* attributes )
9664 {
9665   bool rc = false;
9666   if ( m_active_table != light_table ) {
9667     ON_ERROR("ON_BinaryArchive::Write3dmLight() - m_active_table != light_table");
9668   }
9669   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
9670   if ( c && c->m_typecode == TCODE_LIGHT_TABLE ) {
9671     rc = BeginWrite3dmChunk( TCODE_LIGHT_RECORD, 0 );
9672     if (rc) {
9673       // WriteObject writes TCODE_OPENNURBS_CLASS chunk that contains light definition
9674       rc = WriteObject( light );
9675 
9676       // optional TCODE_LIGHT_RECORD_ATTRIBUTES chunk
9677       if ( rc && attributes )
9678       {
9679         rc = BeginWrite3dmChunk( TCODE_LIGHT_RECORD_ATTRIBUTES, 0 );
9680         if (rc)
9681         {
9682           rc = attributes->Write( *this )?true:false;
9683           if (!EndWrite3dmChunk())
9684             rc = false;
9685           if( rc
9686               && (m_bSaveUserData || HasCriticalUserData(*this,attributes))
9687               && Archive3dmVersion() >= 4
9688               && 0 != attributes->FirstUserData()
9689             )
9690           {
9691             // 14 May 2008 Dale Lear
9692             //    Added support for saving light attribute userdata
9693             rc = BeginWrite3dmChunk( TCODE_LIGHT_RECORD_ATTRIBUTES_USERDATA, 0 );
9694             if (rc)
9695             {
9696               // write user data
9697               rc = WriteObjectUserData(*attributes);
9698               if (rc)
9699               {
9700                 // Because I'm not using Write3dmObject() to write
9701                 // the attributes, the user data must be immediately
9702                 // followed by a short TCODE_OPENNURBS_CLASS_END chunk
9703                 // in order for ReadObjectUserData() to work correctly.
9704                 //
9705                 // The reason that this is hacked in is that V3 files did
9706                 // not support attribute user data and doing it this way
9707                 // means that V3 can still read V4 files.
9708                 rc = BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_END,0);
9709                 if (rc)
9710                 {
9711                   if (!EndWrite3dmChunk())
9712                     rc = false;
9713                 }
9714               }
9715               if (!EndWrite3dmChunk())
9716                 rc = false;
9717             }
9718           }
9719         }
9720       }
9721 
9722       // TCODE_LIGHT_RECORD_END chunk marks end of light record
9723       if ( BeginWrite3dmChunk( TCODE_LIGHT_RECORD_END, 0 ) ) {
9724         if (!EndWrite3dmChunk())
9725           rc = false;
9726       }
9727       else {
9728         rc = false;
9729       }
9730 
9731       if ( !EndWrite3dmChunk() ) // end of TCODE_LIGHT_RECORD
9732         rc = false;
9733     }
9734   }
9735   else {
9736     ON_ERROR("ON_BinaryArchive::Write3dmMaterial() - active chunk typecode != TCODE_LIGHT_TABLE");
9737   }
9738   return rc;
9739 }
9740 
EndWrite3dmLightTable()9741 bool ON_BinaryArchive::EndWrite3dmLightTable()
9742 {
9743   return EndWrite3dmTable( TCODE_LIGHT_TABLE );
9744 }
9745 
BeginRead3dmLightTable()9746 bool ON_BinaryArchive::BeginRead3dmLightTable()
9747 {
9748   bool rc = BeginRead3dmTable( TCODE_LIGHT_TABLE );
9749 
9750   if ( !rc )
9751   {
9752     // 1 November 2005 Dale Lear
9753     //    This fall back is slow but it has been finding
9754     //    layer and object tables in damaged files.  I'm
9755     //    adding it to the other BeginRead3dm...Table()
9756     //    functions when it makes sense.
9757     rc = FindMisplacedTable(
9758                 0,
9759                 TCODE_LIGHT_TABLE, TCODE_LIGHT_RECORD,
9760                 ON_Light::m_ON_Light_class_id.Uuid(),
9761                 30
9762                 );
9763     if ( rc )
9764     {
9765       rc = BeginRead3dmTable( TCODE_LIGHT_TABLE );
9766     }
9767   }
9768 
9769   return rc;
9770 }
9771 
Read3dmV1Light(ON_Light ** ppLight,ON_3dmObjectAttributes * pAttributes)9772 int ON_BinaryArchive::Read3dmV1Light(  // returns 0 at end of light table
9773                     //         1 light successfully read
9774                     //        -1 if file is corrupt
9775           ON_Light** ppLight, // light returned here
9776           ON_3dmObjectAttributes* pAttributes// optional - if NOT NULL, object attributes are
9777                                   //            returned here
9778           )
9779 {
9780   ON_BOOL32 bHaveMat;
9781   ON_Material material;
9782   // TODO - read v1 lights
9783   if ( m_chunk.Count() != 0 ) {
9784     ON_ERROR("ON_BinaryArchive::Read3dmV1Light() m_chunk.Count() != 0");
9785     return false;
9786   }
9787   ON_BOOL32 rc = false;
9788   ON__UINT32 tcode = 0;
9789   ON__INT64 big_value = 0;
9790 
9791   // find TCODE_RH_SPOTLIGHT chunk
9792   for(;;)
9793   {
9794     if ( !BeginRead3dmBigChunk(&tcode,&big_value) )
9795       break; // assume we are at the end of the file
9796     if ( tcode == TCODE_RH_SPOTLIGHT ) {
9797       rc = 1;
9798       break;
9799     }
9800     if ( !EndRead3dmChunk() )
9801       break;
9802   }
9803   if (rc) {
9804     ON_3dPoint origin;
9805     ON_3dVector xaxis, yaxis;
9806     double radius;
9807     double height;
9808     double hotspot;
9809 
9810     for(;;)
9811     {
9812       rc = ReadPoint( origin );
9813       if (!rc) break;
9814       rc = ReadVector( xaxis );
9815       if (!rc) break;
9816       rc = ReadVector( yaxis );
9817       if (!rc) break;
9818       rc = ReadDouble( &radius );
9819       if (!rc) break;
9820       rc = ReadDouble( &height );
9821       if (!rc) break;
9822       rc = ReadDouble( &hotspot );
9823       if (!rc) break;
9824       if (ppLight )
9825       {
9826         ON_3dVector Z = ON_CrossProduct( xaxis, yaxis );
9827         ON_3dPoint  location = height*Z + origin;
9828         ON_3dVector direction = -Z;
9829 
9830         if( height > 0.0)
9831           direction *= height;
9832         ON_Light* light = new ON_Light;
9833         light->SetStyle( ON::world_spot_light );
9834         light->SetLocation(location);
9835         light->SetDirection(direction);
9836         light->SetSpotExponent( 64.0);
9837         if( radius > 0.0 && height > 0.0 )
9838           light->SetSpotAngleRadians( atan( radius/height));
9839         *ppLight = light;
9840       }
9841       break;
9842     }
9843 
9844     if (rc && ppLight && *ppLight) {
9845       bHaveMat = false;
9846       Read3dmV1AttributesOrMaterial(pAttributes,&material,bHaveMat,TCODE_ENDOFTABLE);
9847       if ( pAttributes )
9848         pAttributes->m_material_index = -1;
9849       if (bHaveMat)
9850         (*ppLight)->SetDiffuse(material.Diffuse());
9851     }
9852 
9853     if ( !EndRead3dmChunk() ) // end of TCODE_RH_SPOTLIGHT chunk
9854       rc = false;
9855   }
9856 
9857   return rc;
9858 }
9859 
Read3dmLight(ON_Light ** ppLight,ON_3dmObjectAttributes * attributes)9860 int ON_BinaryArchive::Read3dmLight( ON_Light** ppLight, ON_3dmObjectAttributes* attributes )
9861 {
9862   if ( attributes )
9863     attributes->Default();
9864   int rc = -1;
9865   if ( !ppLight )
9866     return 0;
9867   *ppLight = 0;
9868   if ( m_active_table != light_table ) {
9869     ON_ERROR("ON_BinaryArchive::Read3dmLight() - m_active_table != light_table");
9870   }
9871   else if ( m_3dm_version == 1 ) {
9872     rc = Read3dmV1Light( ppLight, attributes );
9873   }
9874   else {
9875     ON_Light* light = NULL;
9876     ON__UINT32 tcode = 0;
9877     ON__INT64 big_value = 0;
9878     if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) {
9879       if ( tcode == TCODE_LIGHT_RECORD ) {
9880         ON_Object* p = 0;
9881         if ( ReadObject( &p ) ) {
9882           light = ON_Light::Cast(p);
9883           if ( !light )
9884             delete p;
9885         }
9886         if (!light) {
9887           ON_ERROR("ON_BinaryArchive::Read3dmLight() - corrupt light table");
9888         }
9889         else {
9890           *ppLight = light;
9891           rc = 1;
9892         }
9893       }
9894       else if ( tcode != TCODE_ENDOFTABLE )
9895       {
9896         ON_ERROR("ON_BinaryArchive::Read3dmLight() - corrupt light table");
9897       }
9898       else
9899         rc = 0;
9900 
9901       while(rc==1)
9902       {
9903         tcode = 0;
9904         big_value = 0;
9905         if (!BeginRead3dmBigChunk( &tcode, &big_value ))
9906         {
9907           rc = -1;
9908           break;
9909         }
9910         if ( tcode == TCODE_LIGHT_RECORD_ATTRIBUTES && attributes )
9911         {
9912           if ( !attributes->Read( *this ) )
9913             rc = -1;
9914         }
9915         else if ( tcode == TCODE_LIGHT_RECORD_ATTRIBUTES_USERDATA )
9916         {
9917           if ( 0 != attributes )
9918           {
9919             // 14 May 2008
9920             //   Added support for reading user data on light attributes
9921             if ( !ReadObjectUserData(*attributes))
9922               rc = -1;
9923           }
9924         }
9925         if ( !EndRead3dmChunk() )
9926         {
9927           rc = -1;
9928           break;
9929         }
9930         if ( tcode == TCODE_LIGHT_RECORD_END )
9931           break;
9932       }
9933 
9934       EndRead3dmChunk();
9935     }
9936   }
9937   return rc;
9938 }
9939 
EndRead3dmLightTable()9940 bool ON_BinaryArchive::EndRead3dmLightTable()
9941 {
9942   return EndRead3dmTable( TCODE_LIGHT_TABLE );
9943 }
9944 
BeginWrite3dmObjectTable()9945 bool ON_BinaryArchive::BeginWrite3dmObjectTable()
9946 {
9947   return BeginWrite3dmTable( TCODE_OBJECT_TABLE );
9948 }
9949 
Write3dmObject(const ON_Object & object,const ON_3dmObjectAttributes * attributes)9950 bool ON_BinaryArchive::Write3dmObject(
9951           const ON_Object& object,
9952           const ON_3dmObjectAttributes* attributes
9953           )
9954 {
9955   bool rc = false;
9956   if ( m_active_table != object_table ) {
9957     ON_ERROR("ON_BinaryArchive::Write3dmObject() - m_active_table != object_table");
9958   }
9959 
9960   if ( Archive3dmVersion() <= 2 && object.ObjectType() == ON::pointset_object )
9961   {
9962     // There were no point clouds in V1 and V2 files and we cannot handle
9963     // this problem inside of ON_PointCloud::Write() because we have to
9964     // write multiple point objects.  (c.f. ON_Brep::Write()).
9965     const ON_PointCloud* pc = ON_PointCloud::Cast(&object);
9966     if ( 0 != pc )
9967     {
9968       int i, count = pc->PointCount();
9969       rc = true;
9970       for ( i = 0; i < count && rc ; i++ )
9971       {
9972         ON_Point pt( pc->m_P[i] );
9973         rc = Write3dmObject( pt, attributes );
9974       }
9975       return rc;
9976     }
9977   }
9978 
9979   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
9980   if ( c && c->m_typecode == TCODE_OBJECT_TABLE )
9981   {
9982     Flush();
9983     rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD, 0 );
9984     if (rc) {
9985       // TCODE_OBJECT_RECORD_TYPE chunk integer value that can be used
9986       // for skipping unwanted types of objects
9987       rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD_TYPE, object.ObjectType() );
9988       if (rc) {
9989         if (!EndWrite3dmChunk())
9990           rc = false;
9991       }
9992 
9993       // WriteObject writes TCODE_OPENNURBS_CLASS chunk that contains object definition
9994       rc = WriteObject( object );
9995 
9996       // optional TCODE_OBJECT_RECORD_ATTRIBUTES chunk
9997       if ( rc && attributes ) {
9998         rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD_ATTRIBUTES, 0 );
9999         if (rc) {
10000           rc = attributes->Write( *this )?true:false;
10001           if (!EndWrite3dmChunk())
10002             rc = false;
10003 
10004           if( rc
10005               && (m_bSaveUserData || HasCriticalUserData(*this,attributes))
10006               && Archive3dmVersion() >= 4
10007               && 0 != attributes->FirstUserData()
10008               )
10009           {
10010             // 19 October 2004
10011             //   Added support for saving user data on object attributes
10012             rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA, 0 );
10013             if (rc)
10014             {
10015               // write user data
10016               rc = WriteObjectUserData(*attributes);
10017               if (rc)
10018               {
10019                 // Because I'm not using Write3dmObject() to write
10020                 // the attributes, the user data must be immediately
10021                 // followed by a short TCODE_OPENNURBS_CLASS_END chunk
10022                 // in order for ReadObjectUserData() to work correctly.
10023                 //
10024                 // The reason that this is hacked in is that V3 files did
10025                 // not support attribute user data and doing it this way
10026                 // means that V3 can still read V4 files.
10027                 rc = BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_END,0);
10028                 if (rc)
10029                 {
10030                   if (!EndWrite3dmChunk())
10031                     rc = false;
10032                 }
10033               }
10034               if (!EndWrite3dmChunk())
10035                 rc = false;
10036             }
10037           }
10038         }
10039       }
10040 
10041       // TCODE_OBJECT_RECORD_END chunk marks end of object record
10042       if ( BeginWrite3dmChunk( TCODE_OBJECT_RECORD_END, 0 ) ) {
10043         if (!EndWrite3dmChunk())
10044           rc = false;
10045       }
10046       else {
10047         rc = false;
10048       }
10049 
10050       if (!EndWrite3dmChunk()) // end of TCODE_OBJECT_RECORD
10051       {
10052         rc = false;
10053       }
10054       if (!Flush())
10055         rc = false;
10056     }
10057     else {
10058       ON_ERROR("ON_BinaryArchive::Write3dmObject() - active chunk typecode != TCODE_OBJECT_TABLE");
10059     }
10060   }
10061   return rc;
10062 }
10063 
EndWrite3dmObjectTable()10064 bool ON_BinaryArchive::EndWrite3dmObjectTable()
10065 {
10066   return EndWrite3dmTable( TCODE_OBJECT_TABLE );
10067 }
10068 
BeginRead3dmObjectTable()10069 bool ON_BinaryArchive::BeginRead3dmObjectTable()
10070 {
10071   m_3dm_v1_material_index = 0;
10072   bool rc = BeginRead3dmTable( TCODE_OBJECT_TABLE );
10073   if ( !rc )
10074   {
10075     // 8 October 2004 Dale Lear
10076     //    This fall back is slow but it will find
10077     //    object tables in files that have been damaged.
10078     rc = FindMisplacedTable(
10079                 0,
10080                 TCODE_OBJECT_TABLE, TCODE_OBJECT_RECORD,
10081                 ON_nil_uuid,
10082                 26 // ON_Point data is 3 doubles + 2 byte version number
10083                 );
10084     if ( rc )
10085     {
10086       rc = BeginRead3dmTable( TCODE_OBJECT_TABLE );
10087     }
10088 
10089   }
10090   return rc;
10091 }
10092 
ReadV1_TCODE_RH_POINT(ON_Object ** ppObject,ON_3dmObjectAttributes * pAttributes)10093 bool ON_BinaryArchive::ReadV1_TCODE_RH_POINT(
10094   ON_Object** ppObject,
10095   ON_3dmObjectAttributes* pAttributes
10096   )
10097 {
10098   ON__UINT64 pos0 = 0;
10099   ON_3DM_BIG_CHUNK* point_chunk = m_chunk.Last();
10100 
10101   if (    0 != point_chunk
10102        && TCODE_RH_POINT == point_chunk->m_typecode
10103        && 0 == point_chunk->m_big_value )
10104   {
10105     // Some early V1 files have TCODE_RH_POINT chunks with arrow xdata
10106     // attached have a length set to zero.
10107     // For these chunks we need to set the chunk length so EndRead3dmChunk()
10108     // will keep going.
10109     pos0 = CurrentPosition();
10110   }
10111   else
10112     point_chunk = 0;
10113 
10114   // read v1 point
10115   bool rc = false;
10116   ON_BOOL32 bHaveMat = false;
10117   ON_3dPoint pt;
10118   ON__3dmV1_XDATA xdata;
10119   rc = ReadPoint(pt);
10120   if (rc)
10121   {
10122     rc = Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_ENDOFTABLE,&xdata);
10123     // do switch even if Read3dmV1AttributesOrMaterial() fails
10124     switch ( xdata.m_type )
10125     {
10126     case ON__3dmV1_XDATA::arrow_direction:
10127       if ( xdata.m_vector.Length() > ON_ZERO_TOLERANCE )
10128       {
10129         ON_AnnotationArrow* arrow = new ON_AnnotationArrow();
10130         arrow->m_tail = pt;
10131         arrow->m_head = pt + xdata.m_vector;
10132         *ppObject = arrow;
10133       }
10134       else
10135       {
10136         *ppObject = new ON_Point(pt);
10137       }
10138       break;
10139 
10140     case ON__3dmV1_XDATA::dot_text:
10141       {
10142         ON_AnnotationTextDot* dot = new ON_AnnotationTextDot();
10143         dot->point = pt;
10144         dot->m_text = xdata.m_string;
10145         if ( dot->m_text.IsEmpty() )
10146           dot->m_text = " "; // a single blank
10147         *ppObject = dot;
10148       }
10149       break;
10150 
10151     default:
10152       *ppObject = new ON_Point(pt);
10153       break;
10154     }
10155   }
10156 
10157   // carefully test for the V1 zero length chunk bug
10158   if ( rc && pos0 > 0 && 0 != point_chunk && point_chunk == m_chunk.Last() )
10159   {
10160     if ( TCODE_RH_POINT == point_chunk->m_typecode && 0 == point_chunk->m_big_value )
10161     {
10162       // This TCODE_RH_POINT chunk has the zero length chunk bug
10163       // that was in some V1 files.
10164       // Fill in the correct chunk length so that reading can continue.
10165       ON__UINT64 pos1 = CurrentPosition();
10166       ON__UINT64 chunk_length = (pos1 > pos0) ? (pos1 - pos0) : 0;
10167       if ( chunk_length >= 32 && chunk_length < 0x0FFFFFFF )
10168         point_chunk->m_big_value = (ON__INT64)chunk_length;
10169     }
10170   }
10171 
10172   return rc;
10173 }
10174 
10175 static
TweakAnnotationPlane(ON_Plane & plane)10176 void TweakAnnotationPlane( ON_Plane& plane )
10177 {
10178   // 10 November 2003 Dale Lear
10179   //   Fixed lots of bugs in annotation plane tweaking.
10180   //   Before the fix this block of code was cut-n-pasted
10181   //   in three places.  The fabs() calls were wrong.  In addition
10182   //   and the
10183   //   .x values where tested and then the .y/.z values were set.
10184 
10185   //    if( fabs( plane.origin.x > 1e10 ))
10186   //      plane.origin.x = 0.0;
10187   //    if( fabs( plane.origin.y > 1e10 ))
10188   //      plane.origin.y = 0.0;
10189   //    if( fabs( plane.origin.z > 1e10 ))
10190   //      plane.origin.z = 0.0;
10191   //
10192   //    if( fabs( plane.xaxis.x > 1e10 ))
10193   //      plane.xaxis.x = 1.0;
10194   //    if( fabs( plane.xaxis.x > 1e10 ))
10195   //      plane.xaxis.y = 0.0;
10196   //    if( fabs( plane.xaxis.x > 1e10 ))
10197   //      plane.xaxis.z = 0.0;
10198   //
10199   //    if( fabs( plane.yaxis.x > 1e10 ))
10200   //      plane.yaxis.x = 0.0;
10201   //    if( fabs( plane.yaxis.x > 1e10 ))
10202   //      plane.yaxis.y = 1.0;
10203   //    if( fabs( plane.yaxis.x > 1e10 ))
10204   //      plane.yaxis.z = 0.0;
10205 
10206   // Lowell has decided that annotation plane
10207   // coordinates bigger than "too_big" should be
10208   // set to zero.
10209   const double too_big = 1.0e10;
10210 
10211   if( fabs( plane.origin.x ) > too_big )
10212     plane.origin.x = 0.0;
10213   if( fabs( plane.origin.y ) > too_big )
10214     plane.origin.y = 0.0;
10215   if( fabs( plane.origin.z ) > too_big )
10216     plane.origin.z = 0.0;
10217 
10218   if( fabs( plane.xaxis.x ) > too_big )
10219     plane.xaxis.x = 1.0;
10220   if( fabs( plane.xaxis.y ) > too_big )
10221     plane.xaxis.y = 0.0;
10222   if( fabs( plane.xaxis.z ) > too_big )
10223     plane.xaxis.z = 0.0;
10224 
10225   if( fabs( plane.yaxis.x ) > too_big )
10226     plane.yaxis.x = 0.0;
10227   if( fabs( plane.yaxis.y ) > too_big )
10228     plane.yaxis.y = 1.0;
10229   if( fabs( plane.yaxis.z ) > too_big )
10230     plane.yaxis.z = 0.0;
10231 
10232   plane.xaxis.Unitize();
10233   plane.yaxis.Unitize();
10234   plane.zaxis = ON_CrossProduct(plane.xaxis,plane.yaxis);
10235   plane.zaxis.Unitize();
10236   plane.UpdateEquation();
10237 }
10238 
10239 
10240 #define RHINO_ANNOTATION_SETTINGS_VERSION_1  1
10241 #define RHINO_LINEAR_DIMENSION_VERSION_1     1
10242 #define RHINO_RADIAL_DIMENSION_VERSION_1     1
10243 #define RHINO_ANGULAR_DIMENSION_VERSION_1    1
10244 #define RHINO_TEXT_BLOCK_VERSION_1           1
10245 #define RHINO_TEXT_BLOCK_VERSION_2           2
10246 #define RHINO_ANNOTATION_LEADER_VERSION_1    1
10247 
10248 #define BUFLEN 128
10249 
ReadV1_TCODE_ANNOTATION_Helper(ON_BinaryArchive & archive,char * buffer,ON_wString & tc)10250 static bool ReadV1_TCODE_ANNOTATION_Helper( ON_BinaryArchive& archive, char* buffer, ON_wString& tc )
10251 {
10252   char* cp = 0;
10253   int j = 0;
10254   if( !archive.ReadInt( &j))
10255     return false;
10256   size_t sz = (j+1)*sizeof(cp[0]);
10257   if( j > BUFLEN - 1 || !buffer )
10258   {
10259     cp = (char*)onmalloc( sz );
10260     if( !cp)
10261       return false;
10262   }
10263   else
10264   {
10265     cp = buffer;
10266   }
10267 
10268   memset( cp, 0, sz );
10269   if( !archive.ReadChar( j,  cp))
10270   {
10271     if ( cp != buffer )
10272       onfree(cp);
10273     return false;
10274   }
10275 
10276   cp[j] = 0;
10277   tc = cp;
10278   if ( cp != buffer )
10279     onfree( cp );
10280   return true;
10281 }
10282 
ReadV1_TCODE_ANNOTATION(unsigned int tcode,ON_Object ** ppObject,ON_3dmObjectAttributes * pAttributes)10283 bool ON_BinaryArchive::ReadV1_TCODE_ANNOTATION(
10284   unsigned int tcode,
10285   ON_Object** ppObject,
10286   ON_3dmObjectAttributes* pAttributes
10287   )
10288 {
10289   enum RhAnnotationType
10290   {
10291     Nothing = 0,
10292     TextBlock = 1,
10293     DimHorizontal = 2,
10294     DimVertical = 3,
10295     DimAligned = 4,
10296     DimRotated = 5,
10297     DimAngular = 6,
10298     DimDiameter = 7 ,
10299     DimRadius = 8,
10300     Leader = 9,
10301     DimLinear = 10,
10302   };
10303 
10304   bool rc = false;
10305   *ppObject = NULL;
10306   ON_wString tc;
10307   char buffer[BUFLEN];
10308   int i, j, k, byobject, version;
10309   //char* cp;
10310   double d, d4[4];
10311   //ON_3dPoint pt;
10312 
10313   switch( tcode)
10314   {
10315   case TCODE_TEXT_BLOCK:
10316     {
10317       // read the version id
10318       rc = ReadInt( &version);
10319       if ( rc &&
10320            (version == RHINO_TEXT_BLOCK_VERSION_1 ||
10321             version == RHINO_TEXT_BLOCK_VERSION_2)
10322          )
10323       {
10324         //this is a version we can read....
10325         // this is a type flag that we throw away
10326         rc = ReadInt( &i);
10327         if( !rc)
10328           return rc;
10329 
10330         ON_TextEntity* text = new ON_TextEntity;
10331         text->SetType( ON::dtTextBlock);
10332 
10333         ON_Plane plane;
10334 
10335         // the entity plane
10336         if( !ReadDouble( 3, &plane.origin.x))
10337           return false;
10338         if( !ReadDouble( 3, &plane.xaxis.x))
10339           return false;
10340         if( !ReadDouble( 3, &plane.yaxis.x))
10341           return false;
10342 
10343         // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane()
10344         TweakAnnotationPlane( plane );
10345 
10346         text->SetPlane( plane);
10347 
10348         // read string to display
10349         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10350           return false;
10351         text->SetUserText( tc.Array());
10352 
10353         // flags ( future )
10354         if( !ReadInt( 1, &j))
10355           return false;
10356 
10357         // settings byobject flag
10358         if( !ReadInt( 1, &byobject))
10359           return false;
10360 
10361         // depending on the value of byobject, more fields might be read here
10362 
10363         // facename
10364         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10365           return false;
10366         text->SetFaceName(tc);
10367 
10368         // face weight
10369         if( !ReadInt( 1, &j))
10370           return false;
10371         text->SetFontWeight( j);
10372 
10373         if( !ReadDouble( 1, &d))
10374           return false;
10375         text->SetHeight( d);
10376 
10377         // 2 extra doubles were written into the file by mistake in version 1
10378         if( version == RHINO_TEXT_BLOCK_VERSION_1 )
10379         {
10380           if( !ReadDouble( 1, &d))
10381             return false;
10382           if( !ReadDouble( 1, &d))
10383             return false;
10384         }
10385 
10386         if( text->UserText().Length() < 1 )
10387         {
10388           *ppObject = 0;
10389           return true;
10390         }
10391         *ppObject = text;
10392         rc = true;
10393       }
10394     }
10395     break;
10396 
10397   case TCODE_ANNOTATION_LEADER:
10398     {
10399       // read the version id
10400       if( !ReadInt( &i))
10401         return false;
10402 
10403       if( i == RHINO_ANNOTATION_LEADER_VERSION_1)
10404       {
10405         // redundant type code to throw away
10406         if( !ReadInt( &i))
10407           return false;
10408 
10409         ON_Leader* ldr = new ON_Leader;
10410         ldr->SetType( ON::dtLeader);
10411 
10412         ON_Plane plane;
10413 
10414         // the entity plane
10415         if( !ReadDouble( 3, &plane.origin.x))
10416           return false;
10417         if( !ReadDouble( 3, &plane.xaxis.x))
10418           return false;
10419         if( !ReadDouble( 3, &plane.yaxis.x))
10420           return false;
10421 
10422         // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane()
10423         TweakAnnotationPlane( plane );
10424 
10425         ldr->SetPlane( plane);
10426 
10427         // flags ( future )
10428         if( !ReadInt( 1, &j))
10429           return false;
10430 
10431         // settings byobject flag
10432         if( !ReadInt( 1, &byobject))
10433           return false;
10434 
10435         // number of points to read
10436         if( !ReadInt( &k))
10437           return false;
10438 
10439         if( k < 2)
10440           return true;
10441 
10442         ON_SimpleArray<ON_2dPoint> points;
10443         for( j = 0; j < k; j++ )
10444         {
10445           double pt[3];
10446           if( !ReadDouble( 3, pt))
10447             return false;
10448           points.Append( ON_2dPoint( pt));
10449         }
10450         ldr->SetPoints( points);
10451 
10452         *ppObject = ldr;
10453         rc = true;
10454         break;
10455       }
10456     }
10457     break;
10458   case TCODE_LINEAR_DIMENSION:
10459     {
10460       // read the version id
10461       if( !ReadInt( &i))
10462         return false;
10463 
10464       if( i == RHINO_LINEAR_DIMENSION_VERSION_1)
10465       {
10466         if( !ReadInt( &i))
10467           return false;
10468 
10469         ON_LinearDimension* dim = new ON_LinearDimension;
10470         switch( i )
10471         {
10472         case DimHorizontal:
10473         case DimVertical:
10474         case DimRotated:
10475         case DimLinear:
10476           dim->SetType( ON::dtDimLinear);
10477           break;
10478         default:
10479           dim->SetType( ON::dtDimAligned);
10480           break;
10481         }
10482 
10483         ON_Plane plane;
10484 
10485         // the entity plane
10486         if( !ReadDouble( 3, &plane.origin.x))
10487           return false;
10488         if( !ReadDouble( 3, &plane.xaxis.x))
10489           return false;
10490         if( !ReadDouble( 3, &plane.yaxis.x))
10491           return false;
10492 
10493         // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane()
10494         TweakAnnotationPlane( plane );
10495 
10496         dim->SetPlane( plane);
10497 
10498         // definition points in coordinates of entity plane
10499         ON_SimpleArray<ON_2dPoint> points;
10500         for( j = 0; j < 11; j++ )
10501         {
10502           double pt[3];
10503           if( !ReadDouble( 3, pt))
10504             return false;
10505           points.Append( ON_2dPoint( pt));
10506         }
10507         dim->SetPoints( points);
10508 
10509         // read user text string
10510         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10511           return false;
10512         dim->SetUserText( tc.Array());
10513 
10514         // Set the symbols used in dimension strings to the selected options
10515         //        SetStringSymbols();
10516 
10517         // read string to display
10518         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10519           return false;
10520         dim->SetDefaultText( tc.Array());
10521 
10522         // user positioned text flag
10523         if( !ReadInt( &j))
10524           return false;
10525         dim->SetUserPositionedText( j);
10526 
10527         // flags ( future )
10528         if( !ReadInt( 1, &j))
10529           return false;
10530 
10531         // settings byobject flag
10532         if( !ReadInt( 1, &byobject))
10533           return false;
10534 
10535         *ppObject = dim;
10536         rc = true;
10537         break;
10538       }
10539     }
10540     break;
10541 
10542   case TCODE_ANGULAR_DIMENSION:
10543     {
10544       // read the version id
10545       if( !ReadInt( &i))
10546         return false;
10547 
10548       if( i == RHINO_ANGULAR_DIMENSION_VERSION_1)
10549       {
10550         if( !ReadInt( &i))
10551           return false;
10552 
10553         ON_AngularDimension* dim = new ON_AngularDimension;
10554         dim->SetType( ON::dtDimAngular);
10555 
10556         ON_Plane plane;
10557 
10558         // the entity plane
10559         if( !ReadDouble( 3, &plane.origin.x))
10560           return false;
10561         if( !ReadDouble( 3, &plane.xaxis.x))
10562           return false;
10563         if( !ReadDouble( 3, &plane.yaxis.x))
10564           return false;
10565 
10566         // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane()
10567         TweakAnnotationPlane( plane );
10568 
10569         dim->SetPlane( plane);
10570 
10571         if( !ReadDouble( &d))
10572           return false;
10573         dim->SetAngle( d);
10574 
10575         if( !ReadDouble( &d))
10576           return false;
10577         dim->SetRadius( d);
10578 
10579         // distances from apes to start and end of dimensioned lines - not used
10580         if( !ReadDouble( 4, d4))
10581           return false;
10582 
10583         // definition points in coordinates of entity plane
10584         ON_SimpleArray<ON_2dPoint> points;
10585         for( j = 0; j < 5; j++ )
10586         {
10587           double pt[3];
10588           if( !ReadDouble( 3, pt))
10589             return false;
10590           points.Append( ON_2dPoint( pt));
10591         }
10592         dim->SetPoints( points);
10593 
10594         // read user text string
10595         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10596           return false;
10597         dim->SetUserText( tc.Array());
10598 
10599         // Set the symbols used in dimension strings to the selected options
10600         //        SetStringSymbols();
10601 
10602         // read string to display
10603         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10604           return false;
10605         dim->SetDefaultText( tc.Array());
10606 
10607         // user positioned text flag
10608         if( !ReadInt( &j))
10609           return false;
10610         dim->SetUserPositionedText( j);
10611 
10612 
10613         // flags ( future )
10614         if( !ReadInt( 1, &j))
10615           return false;
10616 
10617         // settings byobject flag
10618         if( !ReadInt( 1, &byobject))
10619           return false;
10620 
10621 
10622         *ppObject = dim;
10623         rc = true;
10624         break;
10625       }
10626     }
10627     break;
10628 
10629   case TCODE_RADIAL_DIMENSION:
10630     {
10631       // read the version id
10632       if( !ReadInt( &i))
10633         return false;
10634 
10635       if( i == RHINO_RADIAL_DIMENSION_VERSION_1)
10636       {
10637         if( !ReadInt( &i))
10638           return false;
10639 
10640         ON_RadialDimension* dim = new ON_RadialDimension;
10641 
10642         switch( i)
10643         {
10644         case DimDiameter:
10645           dim->SetType( ON::dtDimDiameter);
10646           break;
10647         case DimRadius:
10648           dim->SetType( ON::dtDimRadius);
10649           break;
10650         }
10651 
10652         ON_Plane plane;
10653 
10654         // the entity plane
10655         if( !ReadDouble( 3, &plane.origin.x))
10656           return false;
10657         if( !ReadDouble( 3, &plane.xaxis.x))
10658           return false;
10659         if( !ReadDouble( 3, &plane.yaxis.x))
10660           return false;
10661 
10662         // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane()
10663         TweakAnnotationPlane( plane );
10664 
10665         dim->SetPlane( plane);
10666 
10667         // definition points in coordinates of entity plane
10668         ON_SimpleArray<ON_2dPoint> points;
10669         for( j = 0; j < 5; j++ )
10670         {
10671           double pt[3];
10672           if( !ReadDouble( 3, pt))
10673             return false;
10674           points.Append( ON_2dPoint( pt));
10675         }
10676         dim->SetPoints( points);
10677 
10678         // read user text string
10679         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10680           return false;
10681         dim->SetUserText( tc.Array());
10682 
10683         // Set the symbols used in dimension strings to the selected options
10684         //        SetStringSymbols();
10685 
10686         // read string to display
10687         if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) )
10688           return false;
10689         dim->SetDefaultText( tc.Array());
10690 
10691         // user positioned text flag
10692         if( !ReadInt( &j))
10693           return false;
10694         dim->SetUserPositionedText( j);
10695 
10696         // flags ( future )
10697         if( !ReadInt( 1, &j))
10698           return false;
10699 
10700         // settings byobject flag
10701         if( !ReadInt( 1, &byobject))
10702           return false;
10703 
10704 
10705         *ppObject = dim;
10706         rc = true;
10707         break;
10708       }
10709 
10710     }
10711     break;
10712 
10713   default:  // some unknown type to skip over
10714     return true;
10715   } //switch
10716 
10717   if( rc)
10718   {
10719     ON_BOOL32 bHaveMat = false;
10720     Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_ENDOFTABLE);
10721   }
10722 
10723   return rc;
10724 
10725   // TODO: fill in this function
10726 
10727   // input tcode               returned *ppObject points to
10728   // TCODE_TEXT_BLOCK:         ON_TextEntity
10729   // TCODE_ANNOTATION_LEADER:  ON_Leader
10730   // TCODE_LINEAR_DIMENSION:   ON_LinearDimension
10731   // TCODE_ANGULAR_DIMENSION:  ON_AngularDimension
10732   // TCODE_RADIAL_DIMENSION:   ON_RadialDimension
10733   //return true if successful and false for failure.
10734 }
10735 
10736 
ReadV1_TCODE_MESH_OBJECT(ON_Object ** ppObject,ON_3dmObjectAttributes * pAttributes)10737 bool ON_BinaryArchive::ReadV1_TCODE_MESH_OBJECT(
10738                                                 ON_Object** ppObject,
10739                                                 ON_3dmObjectAttributes* pAttributes
10740                                                 )
10741 {
10742   ON_Mesh* mesh = 0;
10743   bool rc = false;
10744   // read v1 mesh
10745   ON__UINT32 tcode = 0;
10746   ON__INT64 big_value = 0;
10747   int i;
10748   if ( !BeginRead3dmBigChunk(&tcode,&big_value) )
10749     return false;
10750 
10751   if ( tcode == TCODE_COMPRESSED_MESH_GEOMETRY ) for(;;)
10752   {
10753 
10754     int point_count = 0;
10755     int face_count = 0;
10756     ON_BOOL32 bHasVertexNormals = false;
10757     ON_BOOL32 bHasTexCoords = false;
10758     ON_BoundingBox bbox;
10759 
10760     if (!ReadInt(&point_count) )
10761       break;
10762     if ( point_count <= 0 )
10763       break;
10764     if (!ReadInt(&face_count) )
10765       break;
10766     if ( face_count <= 0 )
10767       break;
10768     if (!ReadInt(&bHasVertexNormals) )
10769       break;
10770     if (!ReadInt(&bHasTexCoords) )
10771       break;
10772     if ( !ReadPoint(bbox.m_min) )
10773       break;
10774     if ( !ReadPoint(bbox.m_max) )
10775       break;
10776 
10777     mesh = new ON_Mesh(face_count,
10778                        point_count,
10779                        bHasVertexNormals?true:false,
10780                        bHasTexCoords?true:false
10781                        );
10782 
10783     // read 3d vertex locations
10784     {
10785       ON_3dVector d = bbox.Diagonal();
10786       double dx = d.x / 65535.0;
10787       double dy = d.y / 65535.0;
10788       double dz = d.z / 65535.0;
10789       unsigned short xyz[3];
10790       ON_3fPoint pt;
10791       for ( i = 0; i < point_count; i++ ) {
10792         if ( !ReadShort(3,xyz) )
10793           break;
10794         pt.x = (float)(dx*xyz[0] + bbox.m_min.x);
10795         pt.y = (float)(dy*xyz[1] + bbox.m_min.y);
10796         pt.z = (float)(dz*xyz[2] + bbox.m_min.z);
10797         mesh->m_V.Append(pt);
10798       }
10799     }
10800     if ( mesh->m_V.Count() != point_count )
10801       break;
10802 
10803     // read triangle/quadrangle faces
10804     if ( point_count < 65535 ) {
10805       unsigned short abcd[4];
10806       for ( i = 0; i < face_count; i++ ) {
10807         if ( !ReadShort(4,abcd) )
10808           break;
10809         ON_MeshFace& f = mesh->m_F.AppendNew();
10810         f.vi[0] = abcd[0];
10811         f.vi[1] = abcd[1];
10812         f.vi[2] = abcd[2];
10813         f.vi[3] = abcd[3];
10814       }
10815     }
10816     else {
10817       int abcd[4];
10818       for ( i = 0; i < face_count; i++ ) {
10819         if ( !ReadInt(4,abcd) )
10820           break;
10821         ON_MeshFace& f = mesh->m_F.AppendNew();
10822         f.vi[0] = abcd[0];
10823         f.vi[1] = abcd[1];
10824         f.vi[2] = abcd[2];
10825         f.vi[3] = abcd[3];
10826       }
10827     }
10828     if ( mesh->m_F.Count() != face_count )
10829       break;
10830 
10831     if ( bHasVertexNormals ) {
10832       char xyz[3];
10833       ON_3fVector normal;
10834       for ( i = 0; i < point_count; i++ ) {
10835         if ( !ReadChar(3,xyz) )
10836           break;
10837         normal.x = (float)(((signed char)xyz[0])/127.0);
10838         normal.y = (float)(((signed char)xyz[1])/127.0);
10839         normal.z = (float)(((signed char)xyz[2])/127.0);
10840         mesh->m_N.Append(normal);
10841       }
10842       if ( mesh->m_N.Count() != mesh->m_V.Count() )
10843         break;
10844     }
10845 
10846     if ( bHasTexCoords ) {
10847       unsigned short uv[2];
10848       ON_2fPoint t;
10849       for ( i = 0; i < point_count; i++ ) {
10850         if ( !ReadShort(2,uv) )
10851           break;
10852         t.x = (float)(uv[0]/65535.0);
10853         t.y = (float)(uv[1]/65535.0);
10854         mesh->m_T.Append(t);
10855       }
10856       if ( mesh->m_T.Count() != mesh->m_V.Count() )
10857         break;
10858     }
10859 
10860     rc = true;
10861 
10862     break;
10863   }
10864 
10865   if ( !EndRead3dmChunk() )
10866     rc = false;
10867 
10868   if ( rc && mesh ) {
10869     *ppObject = mesh;
10870   }
10871   else {
10872     if ( mesh )
10873       delete mesh;
10874     rc = false;
10875   }
10876 
10877   if ( rc && mesh ) {
10878     // attributes and material information follows the TCODE_COMPRESSED_MESH_GEOMETRY chunk;
10879     ON_BOOL32 bHaveMat = false;
10880     Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_ENDOFTABLE);
10881   }
10882 
10883   return rc;
10884 }
10885 
BeginRead3dmLEGACYSTUFF(ON_BinaryArchive & file,unsigned int stuff_tcode)10886 static bool BeginRead3dmLEGACYSTUFF( ON_BinaryArchive& file, unsigned int stuff_tcode )
10887 {
10888   // begins reading stuff chunk
10889   bool rc = false;
10890   ON__UINT32 tcode = !stuff_tcode;
10891   ON__INT64 big_value = 0;
10892   for (;;)
10893   {
10894     if ( !file.BeginRead3dmBigChunk(&tcode,&big_value) )
10895       break;
10896     if ( tcode == stuff_tcode ) {
10897       rc = true;
10898       break;
10899     }
10900     if ( !file.EndRead3dmChunk() )
10901       break;
10902   }
10903   return rc;
10904 }
10905 
ReadV1_TCODE_LEGACY_SPLSTUFF(ON_BinaryArchive & file)10906 static ON_NurbsCurve* ReadV1_TCODE_LEGACY_SPLSTUFF( ON_BinaryArchive& file )
10907 {
10908   // reads contents of a v1 TCODE_LEGACY_SPLSTUFF chunk
10909   ON_NurbsCurve* pNurbsCurve = 0;
10910   int i, dim, is_rat, order, cv_count; // , is_closed, form;
10911   ON_BoundingBox bbox;
10912   char c;
10913 
10914   // read v1 agspline chunk
10915   if ( !file.ReadChar(1,&c) )
10916     return NULL;
10917   if ( c != 2 && c != 3 )
10918     return NULL;
10919   dim = c;
10920   if ( !file.ReadChar(1,&c) )
10921     return NULL;
10922   if ( c != 0 && c != 1 && c != 2 )
10923     return NULL;
10924   is_rat = c; // 0 = no, 1 = euclidean, 2 = homogeneous
10925   if ( !file.ReadChar(1,&c) )
10926     return NULL;
10927   if ( c < 2 )
10928     return NULL;
10929   order = c;
10930 
10931   {
10932     // 5 February 2003 - An single case of a V1 file
10933     //     with a spline that had cv_count = 54467 (>32767)
10934     //     exists.  Changing from a signed short to
10935     //     an unsigned short fixed the problem.
10936     //     The ui casting stuff is here to keep all
10937     //     the various compilers/lints happy and to
10938     //     make sure the short with the high bit set
10939     //     gets properly converted to a positive cv_count.
10940     unsigned short s;
10941     if ( !file.ReadShort(1,&s) )
10942       return NULL;
10943     unsigned int ui = s;
10944     cv_count = (int)ui;
10945     if ( cv_count < order )
10946       return NULL;
10947   }
10948 
10949   // The "is_closed" and "form" flags are here to recording
10950   // the values of legacy data found in the Rhino file.  These
10951   // values are not used in the toolkit code.
10952   if ( !file.ReadByte(1,&c) )
10953     return NULL;
10954   if (c != 0 && c != 1 && c != 2)
10955     return NULL;
10956 //  is_closed = c; // 0 = open, 1 = closed, 2 = periodic
10957   if ( !file.ReadByte(1,&c) )
10958     return NULL;
10959 //  form = c;
10960 
10961   // read bounding box
10962   if ( !file.ReadDouble( dim, bbox.m_min ) )
10963     return NULL;
10964   if ( !file.ReadDouble( dim, bbox.m_max ) )
10965     return NULL;
10966 
10967   pNurbsCurve = new ON_NurbsCurve( dim, is_rat?true:false, order, cv_count );
10968 
10969   ON_BOOL32 rc = false;
10970   for(;;) {
10971 
10972     // read legacy v1 knot vector
10973     const int knot_count = order+cv_count-2;
10974     int       knot_index = 0;
10975     double    knot;
10976 
10977     // clamped_end_knot_flag: 0 = none, 1 = left, 2 = right, 3 = both
10978     char clamped_end_knot_flag = 0;
10979     if ( order > 2 )
10980       file.ReadChar(1,&clamped_end_knot_flag);
10981 
10982     // first knot(s)
10983     if ( !file.ReadDouble(&knot) )
10984       break;
10985     pNurbsCurve->m_knot[knot_index++] = knot;
10986     if (clamped_end_knot_flag % 2) {
10987       // clamped_start_knot
10988       while ( knot_index <= order-2 )
10989         pNurbsCurve->m_knot[knot_index++] = knot;
10990     }
10991 
10992     // middle knots
10993     while ( knot_index <= cv_count-1 ) {
10994       if ( !file.ReadDouble(&knot) )
10995         break;
10996       pNurbsCurve->m_knot[knot_index++] = knot;
10997     }
10998     if ( knot_index <= cv_count-1 )
10999       break;
11000 
11001     // end knot(s)
11002     if ( clamped_end_knot_flag >= 2 ) {
11003       while ( knot_index < knot_count )
11004         pNurbsCurve->m_knot[knot_index++] = knot;
11005     }
11006     else {
11007       while ( knot_index < knot_count ) {
11008         if ( !file.ReadDouble(&knot) )
11009           break;
11010         pNurbsCurve->m_knot[knot_index++] = knot;
11011       }
11012       if ( knot_index < knot_count )
11013         break;
11014     }
11015 
11016     // read legacy v1 control points
11017     const int cvdim = ( is_rat ) ? dim+1 : dim;
11018     for ( i = 0; i < cv_count; i++ ) {
11019       if ( !file.ReadDouble( cvdim, pNurbsCurve->CV(i) ) )
11020         break;
11021     }
11022     if ( i < cv_count )
11023       break;
11024     if ( is_rat ) {
11025       // is_rat == 1 check fails because invalid is_rat flags in old files
11026       // convert rational CVs from euclidean to homogeneous
11027       double w, *cv;
11028       int cv_index;
11029       for ( cv_index = 0; cv_index < cv_count; cv_index++ ) {
11030         cv = pNurbsCurve->CV(cv_index);
11031         w = cv[dim];
11032         for ( i = 0; i < dim; i++ )
11033           cv[i] *= w;
11034       }
11035     }
11036     if ( order == 2 && cv_count == 2 && pNurbsCurve->m_knot[0] > pNurbsCurve->m_knot[1] ) {
11037       // a few isolated old v1 3DM file created by Rhino 1.0 files have lines with reversed knots.
11038       pNurbsCurve->m_knot[0] = -pNurbsCurve->m_knot[0];
11039       pNurbsCurve->m_knot[1] = -pNurbsCurve->m_knot[1];
11040     }
11041     rc = true;
11042 
11043     break;
11044   }
11045   if ( !rc && pNurbsCurve ) {
11046     delete pNurbsCurve;
11047     pNurbsCurve = 0;
11048   }
11049   return pNurbsCurve;
11050 }
11051 
ReadV1_TCODE_LEGACY_SPL(ON_BinaryArchive & file,ON_NurbsCurve * & pNurbsCurve)11052 static ON_BOOL32 ReadV1_TCODE_LEGACY_SPL( ON_BinaryArchive& file,
11053   ON_NurbsCurve*& pNurbsCurve
11054   )
11055 {
11056   // reads contents of TCODE_LEGACY_SPL chunk
11057   pNurbsCurve = 0;
11058   ON_BOOL32 rc = BeginRead3dmLEGACYSTUFF(file, TCODE_LEGACY_SPLSTUFF );
11059   if ( !rc )
11060     return false;
11061   pNurbsCurve = ReadV1_TCODE_LEGACY_SPLSTUFF(file);
11062   if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_SPLSTUFF chunk
11063     rc = false;
11064   if ( !pNurbsCurve )
11065     rc = false;
11066   return rc;
11067 }
11068 
ReadV1_TCODE_LEGACY_SRFSTUFF(ON_BinaryArchive & file)11069 static ON_NurbsSurface* ReadV1_TCODE_LEGACY_SRFSTUFF( ON_BinaryArchive& file )
11070 {
11071   // reads contents of TCODE_LEGACY_SRFSTUFF chunk
11072   ON_NurbsSurface* pNurbsSurface = 0;
11073   int i, j, dim=0, is_rat=0, order[2], cv_count[2]; //, is_closed[2], is_singular[2], form;
11074   ON_BoundingBox bbox;
11075   char c;
11076 
11077   // read contents of v1 TCODE_LEGACY_SRFSTUFF chunk
11078   if ( !file.ReadChar(1,&c) )
11079     return NULL;
11080   if ( c != 2 && c != 3 )
11081     return NULL;
11082   dim = c;
11083   if ( !file.ReadByte(1,&c) )
11084     return NULL;
11085 //  form = c;
11086   if ( !file.ReadChar(1,&c) )
11087     return NULL;
11088   if ( c < 1 )
11089     return NULL;
11090   order[0] = c+1;
11091   if ( !file.ReadChar(1,&c) )
11092     return NULL;
11093   if ( c < 1 )
11094     return NULL;
11095   order[1] = c+1;
11096 
11097   {
11098     // 5 February 2003 - An single case of a V1 files
11099     //     See the comment above in ReadV1_TCODE_LEGACY_SPLSTUFF
11100     //     concerning the spline with cv_count >= 0x8000.
11101     //     The analogous fix is here for the surface case.
11102     unsigned short s;
11103     if ( !file.ReadShort(1,&s) )
11104       return NULL;
11105     if ( s < 1 )
11106       return NULL;
11107     unsigned int ui = s;
11108     cv_count[0] = order[0]-1+((int)ui);
11109     if ( !file.ReadShort(1,&s) )
11110       return NULL;
11111     if ( s < 1 )
11112       return NULL;
11113     ui = s;
11114     cv_count[1] = order[1]-1+((int)ui);
11115   }
11116 
11117   // "ratu" 0 = no, 1 = euclidean, 2 = homogeneous
11118   if ( !file.ReadChar(1,&c) )
11119     return NULL;
11120   if ( c == 1 ) is_rat = 1; else if ( c == 2 ) is_rat = 2;
11121 
11122   // "ratv" 0 = no, 1 = euclidean, 2 = homogeneous
11123   if ( !file.ReadChar(1,&c) )
11124     return NULL;
11125   if ( c == 1 ) is_rat = 1; else if ( c == 2 ) is_rat = 2;
11126 
11127   // The "is_closed" and "is_singular" flags are here to recording
11128   // the values of legacy data found in the Rhino file.  These
11129   // values are not used in the toolkit code.
11130   if ( !file.ReadByte(1,&c) )
11131     return NULL;
11132   if (c != 0 && c != 1 && c != 2)
11133     return NULL;
11134 //  is_closed[0] = c; // 0 = open, 1 = closed, 2 = periodic
11135   if ( !file.ReadByte(1,&c) )
11136     return NULL;
11137   if (c != 0 && c != 1 && c != 2)
11138     return NULL;
11139 //  is_closed[1] = c; // 0 = open, 1 = closed, 2 = periodic
11140 
11141   if ( !file.ReadByte(1,&c) )
11142     return NULL;
11143   if (c != 0 && c != 1 && c != 2 && c != 3)
11144     return NULL;
11145 //  is_singular[0] = c;
11146   if ( !file.ReadByte(1,&c) )
11147     return NULL;
11148   if (c != 0 && c != 1 && c != 2 && c != 3)
11149     return NULL;
11150 //  is_singular[1] = c;
11151 
11152   // read bounding box
11153   if ( !file.ReadDouble( dim, bbox.m_min ) )
11154     return NULL;
11155   if ( !file.ReadDouble( dim, bbox.m_max ) )
11156     return NULL;
11157 
11158   pNurbsSurface = new ON_NurbsSurface( dim, is_rat?true:false,
11159                                        order[0], order[1],
11160                                        cv_count[0], cv_count[1] );
11161 
11162   ON_BOOL32 rc = false;
11163   for (;;) {
11164 
11165     // read legacy v1 knot vectors
11166     if ( !file.ReadDouble( order[0]+cv_count[0]-2, pNurbsSurface->m_knot[0] ) )
11167       break;
11168     if ( !file.ReadDouble( order[1]+cv_count[1]-2, pNurbsSurface->m_knot[1] ) )
11169       break;
11170 
11171     // read legacy v1 control points
11172     const int cvdim = ( is_rat ) ? dim+1 : dim;
11173     for ( i = 0; i < cv_count[0]; i++ ) {
11174       for ( j = 0; j < cv_count[1]; j++ ) {
11175         if ( !file.ReadDouble( cvdim, pNurbsSurface->CV(i,j) ) )
11176           break;
11177       }
11178       if ( j < cv_count[1] )
11179         break;
11180     }
11181     if ( i < cv_count[0] )
11182       break;
11183     if ( is_rat == 1 ) {
11184       double w, *cv;
11185       int k;
11186       for ( i = 0; i < cv_count[0]; i++ ) for ( j = 0; j < cv_count[1]; j++ ) {
11187         cv = pNurbsSurface->CV(i,j);
11188         w = cv[dim];
11189         for ( k = 0; k < dim; k++ )
11190           cv[k] *= w;
11191       }
11192     }
11193     rc = true;
11194 
11195     break;
11196   }
11197   if ( !rc ) {
11198     delete pNurbsSurface;
11199     pNurbsSurface = 0;
11200   }
11201 
11202   return pNurbsSurface;
11203 }
11204 
ReadV1_TCODE_LEGACY_SRF(ON_BinaryArchive & file,ON_NurbsSurface * & pNurbsSurface)11205 static ON_BOOL32 ReadV1_TCODE_LEGACY_SRF( ON_BinaryArchive& file,
11206   ON_NurbsSurface*& pNurbsSurface
11207   )
11208 {
11209   pNurbsSurface = 0;
11210   ON_BOOL32 rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_SRF );
11211   if ( rc ) {
11212     rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_SRFSTUFF );
11213     if ( rc ) {
11214       pNurbsSurface = ReadV1_TCODE_LEGACY_SRFSTUFF( file );
11215       if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_SRFSTUFF chunk
11216         rc = false;
11217     }
11218     if ( !file.EndRead3dmChunk() )
11219       rc = false; // end of TCODE_LEGACY_SRF chunk
11220   }
11221   if ( !rc && pNurbsSurface ) {
11222     delete pNurbsSurface;
11223     pNurbsSurface = 0;
11224   }
11225   return rc;
11226 }
11227 
ReadV1_TCODE_LEGACY_CRVSTUFF(ON_BinaryArchive & file)11228 ON_Curve* ReadV1_TCODE_LEGACY_CRVSTUFF( ON_BinaryArchive& file )
11229 {
11230   // reads contents of a v1 TCODE_LEGACY_CRVSTUFF chunk
11231   ON_Curve* curve = 0;
11232   ON_PolyCurve* polycurve = 0;
11233   ON_NurbsCurve* segment = 0;
11234   ON_BOOL32 rc = false;
11235   ON__UINT32 tcode = 0;
11236   ON__INT64 big_value = 0;
11237   int i;
11238   ON_BOOL32 bIsPolyline = false;
11239   ON_BoundingBox bbox;
11240 
11241   for (;;) {
11242     char c;
11243     short s;
11244     int segment_count = 0;
11245     file.ReadChar(1,&c);
11246     if ( c != 2 && c != 3 )
11247       break;
11248     int dim = c;
11249     file.ReadChar(1,&c);
11250     if ( c != -1 && c != 0 && c != 1 && c != 2 )
11251       break;
11252     //int is_closed = (c) ? 1 : 0;
11253     file.ReadShort(&s);
11254     if ( s < 1 )
11255       break;
11256     file.ReadDouble( dim, bbox.m_min);
11257     file.ReadDouble( dim, bbox.m_max);
11258     segment_count = s;
11259     for ( i = 0; i < segment_count; i++ ) {
11260       segment = 0;
11261       tcode = 0;
11262       big_value = 0;
11263       if ( !file.BeginRead3dmBigChunk( &tcode, &big_value ) )
11264         break;
11265       if ( tcode == TCODE_LEGACY_SPL && big_value > 0 ) {
11266         ReadV1_TCODE_LEGACY_SPL(file,segment);
11267       }
11268       if ( !file.EndRead3dmChunk() ) {
11269         if ( segment ) {
11270           delete segment;
11271           segment = 0;
11272         }
11273         break;
11274       }
11275       if ( !segment )
11276         break;
11277       if ( i == 0 )
11278         polycurve = new ON_PolyCurve(segment_count);
11279       if ( segment->CVCount() > 2 || segment->Order() != 2 || segment->IsRational() )
11280       {
11281         if ( segment->Order() != 2 || segment->IsRational() )
11282           bIsPolyline = false;
11283         polycurve->Append(segment);
11284       }
11285       else
11286       {
11287         ON_LineCurve* line = new ON_LineCurve();
11288         line->m_t.Set( segment->m_knot[0], segment->m_knot[1] );
11289         segment->GetCV( 0, line->m_line.from );
11290         segment->GetCV( 1, line->m_line.to );
11291         line->m_dim = segment->m_dim;
11292         delete segment;
11293         segment = 0;
11294         polycurve->Append(line);
11295       }
11296     }
11297 
11298     // 5 February 2003
11299     //   The check for a NULL polycurve was added to avoid
11300     //   crashes in files when the first NURBS curve in the
11301     //   polycurve could not be read.
11302     if ( 0 == polycurve )
11303       break;
11304     if ( polycurve->Count() != segment_count )
11305       break;
11306     rc = true;
11307     break;
11308   }
11309 
11310   if ( rc && polycurve )
11311   {
11312     if ( polycurve->Count() == 1 )
11313     {
11314       curve = polycurve->HarvestSegment(0);
11315       delete polycurve;
11316     }
11317     else if ( bIsPolyline )
11318     {
11319       ON_PolylineCurve* pline = new ON_PolylineCurve();
11320       pline->m_dim = polycurve->Dimension();
11321       pline->m_t.Reserve(polycurve->Count()+1);
11322       pline->m_t.SetCount(polycurve->Count()+1);
11323       polycurve->GetSpanVector( pline->m_t.Array() );
11324       pline->m_pline.Reserve(polycurve->Count()+1);
11325       for ( i = 0; i < polycurve->Count(); i++ ) {
11326         pline->m_pline.Append(polycurve->SegmentCurve(i)->PointAtStart());
11327       }
11328       pline->m_pline.Append(polycurve->SegmentCurve(polycurve->Count()-1)->PointAtEnd());
11329       curve = pline;
11330       delete polycurve;
11331     }
11332     else
11333     {
11334       curve = polycurve;
11335     }
11336   }
11337   else
11338   {
11339     if ( polycurve )
11340       delete polycurve;
11341     rc = false;
11342   }
11343 
11344   return curve;
11345 }
11346 
ReadV1_LegacyTrimStuff(ON_BinaryArchive & file,ON_BrepFace &,ON_BrepLoop & loop)11347 bool ON_Brep::ReadV1_LegacyTrimStuff( ON_BinaryArchive& file,
11348         ON_BrepFace&, // face - formal parameter intentionally ignored
11349         ON_BrepLoop& loop )
11350 {
11351   // read contents of TCODE_LEGACY_TRMSTUFF chunk
11352   bool rc = false;
11353   int revedge, gcon, mono;
11354   int curve2d_index = -1, curve3d_index = -1, trim_index = -1;
11355   double tol_3d, tol_2d;
11356   ON_Curve* curve2d = NULL;
11357   ON_Curve* curve3d = NULL;
11358 
11359   char c;
11360   file.ReadChar( &c );
11361 
11362   ON_BOOL32 bHasEdge = (c % 2 ); // bit 0 = 1 if "tedge" has non NULL "->edge"
11363   ON_BOOL32 bHasMate = (c & 6 ); // bit 1 or 2 = 1 if "tedge" has non NULL "->twin"
11364   ON_BOOL32 bIsSeam  = (c & 2 ); // bit 1 = 1 if "tedge->twin" belongs to same face
11365 
11366   if ( !file.ReadInt(&revedge) )
11367     return false;
11368   if ( !file.ReadInt(&gcon) )
11369     return false;
11370   if ( !file.ReadInt(&mono) )
11371     return false;
11372   if ( !file.ReadDouble( &tol_3d ) )
11373     return false;
11374   if ( !file.ReadDouble( &tol_2d ) )
11375     return false;
11376 
11377   // 2d trim curve
11378   if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRV ) )
11379     return false;
11380   if ( BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRVSTUFF ) ) {
11381     curve2d = ReadV1_TCODE_LEGACY_CRVSTUFF(file);
11382     if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRVSTUFF chunk
11383       rc = false;
11384   }
11385   if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRV chunk
11386     rc = false;
11387   if ( !curve2d )
11388     return false;
11389   curve2d_index = AddTrimCurve(curve2d);
11390   if ( curve2d_index < 0 ) {
11391     delete curve2d;
11392     return false;
11393   }
11394 
11395   // 3d curve
11396   if ( bHasEdge ) {
11397     if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRV ) )
11398       return false;
11399     if ( BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRVSTUFF ) ) {
11400       curve3d = ReadV1_TCODE_LEGACY_CRVSTUFF(file);
11401       if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRVSTUFF chunk
11402         rc = false;
11403     }
11404     if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRV chunk
11405       rc = false;
11406     if ( !curve3d )
11407       return false;
11408     curve3d_index = AddEdgeCurve(curve3d);
11409     if ( curve3d_index < 0 ) {
11410       delete curve3d;
11411       return false;
11412     }
11413     ON_BrepEdge& edge = NewEdge(curve3d_index);
11414     ON_BrepTrim& trim = NewTrim( edge,
11415                                     revedge ? true : false,
11416                                     loop,
11417                                     curve2d_index
11418                                     );
11419     trim_index = trim.m_trim_index;
11420   }
11421   else {
11422     ON_BrepTrim& trim = NewTrim( revedge ? true : false,
11423                                     loop,
11424                                     curve2d_index
11425                                     );
11426     trim_index = trim.m_trim_index;
11427   }
11428   if ( trim_index >= 0 ) {
11429     ON_BrepTrim& trim = m_T[trim_index];
11430     trim.m__legacy_2d_tol = tol_2d;
11431     trim.m__legacy_3d_tol = tol_3d;
11432     trim.m__legacy_flags_Set(gcon,mono);
11433     if ( bIsSeam ) {
11434       trim.m_type = ON_BrepTrim::seam;
11435     }
11436     else if ( bHasMate ) {
11437       trim.m_type = ON_BrepTrim::mated;
11438     }
11439     else if ( bHasEdge ) {
11440       trim.m_type = ON_BrepTrim::boundary;
11441     }
11442     else {
11443       trim.m_type = ON_BrepTrim::singular;
11444     }
11445   }
11446 
11447   (void)rc;//G+Smo
11448   return (trim_index>=0) ? true : false;
11449 }
11450 
ReadV1_LegacyTrim(ON_BinaryArchive & file,ON_BrepFace & face,ON_BrepLoop & loop)11451 bool ON_Brep::ReadV1_LegacyTrim( ON_BinaryArchive& file,
11452                             ON_BrepFace& face,
11453                             ON_BrepLoop& loop )
11454 {
11455   bool rc = false;
11456   if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_TRM ) )
11457     return false;
11458   rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_TRMSTUFF );
11459   if ( rc ) {
11460     rc = ReadV1_LegacyTrimStuff( file, face, loop );
11461     if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_TRMSTUFF
11462       rc = false;
11463   }
11464   if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_TRM chunk
11465     rc = false;
11466   return rc;
11467 }
11468 
11469 
ReadV1_LegacyLoopStuff(ON_BinaryArchive & file,ON_BrepFace & face)11470 bool ON_Brep::ReadV1_LegacyLoopStuff( ON_BinaryArchive& file,
11471                                ON_BrepFace& face )
11472 {
11473   // reads contents of TCODE_LEGACY_BNDSTUFF chunk
11474   // read boundary
11475   ON_BrepLoop::TYPE loop_type = ON_BrepLoop::unknown;
11476   int tedge_count, btype, lti;
11477   double pspace_box[2][2]; // parameter space bounding box
11478 
11479   if ( !file.ReadInt( &tedge_count ) )
11480     return false;
11481   if ( tedge_count < 1 ) {
11482     return false;
11483   }
11484   if ( !file.ReadInt( &btype ) )
11485     return false;
11486   if ( btype < -1 || btype > 1 ) {
11487     return false;
11488   }
11489   if ( !file.ReadDouble( 4, &pspace_box[0][0] ) )
11490     return false;
11491   switch( btype ) {
11492   case -1:
11493     loop_type = ON_BrepLoop::slit;
11494     break;
11495   case  0:
11496     loop_type = ON_BrepLoop::outer;
11497     break;
11498   case  1:
11499     loop_type = ON_BrepLoop::inner;
11500     break;
11501   }
11502   ON_BrepLoop& loop = NewLoop( loop_type, face );
11503 
11504   for ( lti = 0; lti < tedge_count; lti++ ) {
11505     if ( !ReadV1_LegacyTrim( file, face, loop ) )
11506       return false;
11507   }
11508 
11509   return true;
11510 }
11511 
ReadV1_LegacyLoop(ON_BinaryArchive & file,ON_BrepFace & face)11512 bool ON_Brep::ReadV1_LegacyLoop( ON_BinaryArchive& file,
11513                                         ON_BrepFace& face )
11514 {
11515   bool rc = false;
11516   if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_BND ) )
11517     return false;
11518   rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_BNDSTUFF );
11519   if ( rc ) {
11520     rc = ReadV1_LegacyLoopStuff( file, face );
11521     if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_BNDSTUFF
11522       rc = false;
11523   }
11524   if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_BND chunk
11525     rc = false;
11526   return rc;
11527 }
11528 
ReadV1_LegacyFaceStuff(ON_BinaryArchive & file)11529 bool ON_Brep::ReadV1_LegacyFaceStuff( ON_BinaryArchive& file )
11530 {
11531   // reads contents of TCODE_LEGACY_FACSTUFF chunk
11532   ON_NurbsSurface* surface = 0;
11533   ON_Workspace ws;
11534   int flipnorm = 0;
11535   int ftype = 0;
11536   int bndcnt = 0;
11537   int twincnt = 0;
11538   ON_BOOL32 bHasOuter = false;
11539   ON_BoundingBox face_bbox;
11540 
11541   int ti0 = m_T.Count();
11542 
11543   bool rc = false;
11544 
11545   // read flags
11546   if ( !file.ReadInt(&flipnorm) )
11547     return false;
11548   if ( flipnorm < 0 || flipnorm > 1 )
11549     return false;
11550   if ( !file.ReadInt(&ftype) )
11551     return false;
11552   if ( !file.ReadInt(&bndcnt) )
11553     return false;
11554   bHasOuter = (bndcnt%2); // always true in v1 files
11555   bndcnt /= 2;
11556 
11557   // read bounding box
11558   if ( !file.ReadDouble( 3, face_bbox.m_min ) )
11559     return false;
11560   if ( !file.ReadDouble( 3, face_bbox.m_max ) )
11561     return false;
11562 
11563   // B-rep edge gluing info
11564   if ( !file.ReadInt(&twincnt) )
11565     return false;
11566   short* glue = (twincnt > 0 ) ? (short*)ws.GetMemory(twincnt*sizeof(*glue)) : NULL;
11567   if (twincnt > 0) {
11568     if ( !file.ReadShort(twincnt,glue) )
11569       return false;
11570   }
11571 
11572   // read surface
11573   if ( !ReadV1_TCODE_LEGACY_SRF( file, surface ) )
11574     return false;
11575   if ( !surface )
11576     return false;
11577   const int srf_index = AddSurface(surface);
11578 
11579   // create face
11580   ON_BrepFace& face = NewFace(srf_index);
11581   face.m_bRev = (flipnorm) ? true : false;
11582   face.m_li.Reserve(bndcnt);
11583 
11584   // read boundary loops
11585   int loop_index = -1;
11586   if ( !bHasOuter ) {
11587     // TODO: cook up outer boundary loop (never happes with v1 files)
11588     face.m_li.Append(loop_index);
11589   }
11590   int bi;
11591   rc = true;
11592   for ( bi = 0; rc && bi < bndcnt; bi++ ) {
11593     rc = ReadV1_LegacyLoop( file, face );
11594   }
11595 
11596   if ( twincnt > 0 ) {
11597     // twincnt = number of seams edges in face
11598     // glue[] = order 2 permutation of {0,....,twincnt-1}
11599 
11600     // set seam_i[] = m_T[] indices of seam trims
11601     int si, ti;
11602     const int ti1 = m_T.Count();
11603     int* seam_i = (int*)ws.GetMemory(twincnt*sizeof(*seam_i));
11604     for ( ti = ti0, si = 0; ti < ti1 && si < twincnt; ti++ ) {
11605       if (m_T[ti].m_type != ON_BrepTrim::seam )
11606         continue;
11607       seam_i[si++] = ti;
11608     }
11609 
11610     if ( si == twincnt ) {
11611       // glue seams
11612       for ( si = 0; si < twincnt; si++ ) {
11613         if ( glue[si] >= 0 && glue[si] < twincnt ) {
11614           const int i0 = seam_i[si];
11615           const int i1 = seam_i[glue[si]];
11616           // m_T[i0] and m_T[i1] use the same edge;
11617           const int ei0 = m_T[i0].m_ei;
11618           const int ei1 = m_T[i1].m_ei;
11619           if ( ei0 == -1 && ei1 >= 0 ) {
11620             m_T[i0].m_ei = ei1;
11621             m_E[ei1].m_ti.Append(i0);
11622           }
11623           else if ( ei1 == -1 && ei0 >= 0 ) {
11624             m_T[i1].m_ei = ei0;
11625             m_E[ei0].m_ti.Append(i1);
11626           }
11627         }
11628       }
11629     }
11630   }
11631 
11632   return rc;
11633 }
11634 
ReadV1_LegacyShellStuff(ON_BinaryArchive & file)11635 bool ON_Brep::ReadV1_LegacyShellStuff( ON_BinaryArchive& file )
11636 {
11637   // read contents of TCODE_LEGACY_SHLSTUFF chunk
11638   ON_Workspace ws;
11639   int outer = 0;
11640   int facecnt = 0;
11641   int twincnt = 0;
11642   ON_BoundingBox shell_bbox;
11643   const int ti0 = m_T.Count();
11644 
11645   /* read flags */
11646   file.ReadInt(&outer);
11647   file.ReadInt(&facecnt);
11648 
11649   // read bounding box
11650   file.ReadPoint( shell_bbox.m_min );
11651   file.ReadPoint( shell_bbox.m_max );
11652 
11653   /* B-rep edge gluing info */
11654   file.ReadInt(&twincnt);
11655   short* glue = (twincnt > 0 ) ? (short*)ws.GetMemory(twincnt*sizeof(*glue)) : NULL;
11656   if (twincnt > 0)
11657     file.ReadShort(twincnt,glue);
11658 
11659   bool rc = true;
11660   int fi;
11661   for ( fi = 0; rc && fi < facecnt; fi++ ) {
11662     rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_FAC );
11663     if ( rc ) {
11664       rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_FACSTUFF );
11665       if ( rc ) {
11666         rc = ReadV1_LegacyFaceStuff( file );
11667         if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_FACSTUFF chunk
11668           rc = false;
11669       }
11670       if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_FAC chunk
11671         rc = false;
11672     }
11673   }
11674 
11675   if ( twincnt > 0 ) {
11676     // twincnt = number of shared (inter-face) edges
11677     // glue[] = order 2 permutation of {0,....,twincnt-1}
11678 
11679     // set share_i[] = m_T[] indices of shared trims
11680     int si, ti;
11681     const int ti1 = m_T.Count();
11682     int* share_i = (int*)ws.GetMemory(twincnt*sizeof(*share_i));
11683     for ( ti = ti0, si = 0; ti < ti1 && si < twincnt; ti++ ) {
11684       if (m_T[ti].m_type != ON_BrepTrim::mated )
11685         continue;
11686       share_i[si++] = ti;
11687     }
11688 
11689     if ( si == twincnt ) {
11690       // glue seams
11691       for ( si = 0; si < twincnt; si++ ) {
11692         if ( glue[si] >= 0 && glue[si] < twincnt ) {
11693           const int i0 = share_i[si];
11694           const int i1 = share_i[glue[si]];
11695           // m_T[i0] and m_T[i1] use the same edge;
11696           const int ei0 = m_T[i0].m_ei;
11697           const int ei1 = m_T[i1].m_ei;
11698           if ( ei0 == -1 && ei1 >= 0 ) {
11699             m_T[i0].m_ei = ei1;
11700             m_E[ei1].m_ti.Append(i0);
11701           }
11702           else if ( ei1 == -1 && ei0 >= 0 ) {
11703             m_T[i1].m_ei = ei0;
11704             m_E[ei0].m_ti.Append(i1);
11705           }
11706         }
11707       }
11708     }
11709   }
11710 
11711   return rc;
11712 }
11713 
ReadV1_TCODE_LEGACY_CRV(ON_Object ** ppObject,ON_3dmObjectAttributes * pAttributes)11714 bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_CRV(
11715   ON_Object** ppObject,
11716   ON_3dmObjectAttributes* pAttributes
11717   )
11718 {
11719   ON_Curve* curve = NULL;
11720   bool rc = false;
11721   ON__UINT32 tcode = 0;
11722   ON__INT64 big_value = 0;
11723   ON_BOOL32 bHaveMat = false;
11724 
11725   Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_LEGACY_CRVSTUFF);
11726 
11727   if ( !BeginRead3dmBigChunk( &tcode, &big_value ) )
11728     return false;
11729   if ( tcode == TCODE_LEGACY_CRVSTUFF )
11730     curve = ReadV1_TCODE_LEGACY_CRVSTUFF(*this);
11731   rc = EndRead3dmChunk();
11732   if ( !curve )
11733     rc = false;
11734   else
11735     *ppObject = curve;
11736   return rc;
11737 }
11738 
11739 
ReadV1_TCODE_LEGACY_FAC(ON_Object ** ppObject,ON_3dmObjectAttributes * pAttributes)11740 bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_FAC(
11741   ON_Object** ppObject,
11742   ON_3dmObjectAttributes* pAttributes
11743   )
11744 {
11745   // read V1 TCODE_LEGACY_FAC chunk
11746   ON_BOOL32 bHaveMat = false;
11747   if ( !Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_LEGACY_FACSTUFF) )
11748     return false;
11749   if ( !BeginRead3dmLEGACYSTUFF( *this, TCODE_LEGACY_FACSTUFF ) )
11750     return false;
11751   ON_Brep* brep = new ON_Brep();
11752   bool rc = brep->ReadV1_LegacyFaceStuff( *this );
11753   if ( !EndRead3dmChunk() ) // end of TCODE_LEGACY_FACSTUFF chunk
11754     rc = false;
11755 
11756   if ( !rc ) {
11757     delete brep;
11758   }
11759   else {
11760     brep->SetVertices();
11761     brep->SetTrimIsoFlags();
11762     brep->SetTolsFromLegacyValues();
11763     *ppObject = brep;
11764   }
11765 
11766   return rc;
11767 }
11768 
ReadV1_TCODE_LEGACY_SHL(ON_Object ** ppObject,ON_3dmObjectAttributes * pAttributes)11769 bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_SHL(
11770   ON_Object** ppObject,
11771   ON_3dmObjectAttributes* pAttributes
11772   )
11773 {
11774   // read v1 TCODE_LEGACY_SHL chunk
11775   ON_BOOL32 bHaveMat = false;
11776   if ( !Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_LEGACY_SHLSTUFF) )
11777     return false;
11778   if ( !BeginRead3dmLEGACYSTUFF( *this, TCODE_LEGACY_SHLSTUFF ) )
11779     return false;
11780   ON_Brep* brep = new ON_Brep();
11781   bool rc = brep->ReadV1_LegacyShellStuff( *this );
11782   if ( !EndRead3dmChunk() ) // end of TCODE_LEGACY_SHLSTUFF chunk
11783     rc = false;
11784 
11785   if ( !rc ) {
11786     delete brep;
11787   }
11788   else {
11789     brep->SetVertices();
11790     brep->SetTrimIsoFlags();
11791     brep->SetTolsFromLegacyValues();
11792     *ppObject = brep;
11793   }
11794 
11795   return rc;
11796 }
11797 
11798 
11799 static
ReadV1_RHINOIO_NURBS_CURVE_OBJECT_DATA(ON_BinaryArchive & file)11800 ON_NurbsCurve* ReadV1_RHINOIO_NURBS_CURVE_OBJECT_DATA( ON_BinaryArchive& file )
11801 {
11802   // read TCODE_RHINOIO_OBJECT_DATA chunk that is contained in a
11803   // TCODE_RHINOIO_OBJECT_NURBS_CURVE chunk.  The TCODE_RHINOIO_OBJECT_DATA
11804   // chunk contains the definition of NURBS curves written by the
11805   // old RhinoIO toolkit.
11806   ON_NurbsCurve* curve = 0;
11807   ON_BOOL32 rc = false;
11808   ON__UINT32 tcode = 0;
11809   ON__INT64 big_value = 0;
11810   int version, dim, is_rat, order, cv_count, flag, i;
11811   if ( !file.BeginRead3dmBigChunk( &tcode, &big_value ) )
11812     return NULL;
11813   if ( tcode == TCODE_RHINOIO_OBJECT_DATA ) for (;;) {
11814     if ( !file.ReadInt(&version) )
11815       break;
11816     // int bReverse = version & 0x100;
11817     version &= 0xFFFFFEFF;
11818     if ( version != 100 && version != 101 )
11819       break;
11820     file.ReadInt(&dim);
11821     if ( dim < 1 )
11822       break;
11823     file.ReadInt(&is_rat);
11824     if ( is_rat < 0 || is_rat > 1 )
11825       break;
11826     file.ReadInt(&order);
11827     if ( order < 2 )
11828       break;
11829     file.ReadInt(&cv_count);
11830     if ( cv_count < order )
11831       break;
11832     file.ReadInt(&flag);
11833     if ( flag != 0 )
11834       break;
11835 
11836     curve = new ON_NurbsCurve(dim,is_rat,order,cv_count);
11837     if ( !file.ReadDouble( order+cv_count-2, curve->m_knot ) )
11838       break;
11839     int cvdim = is_rat ? dim+1 : dim;
11840     for ( i = 0; i < cv_count; i++ ) {
11841       if ( !file.ReadDouble( cvdim, curve->CV(i) ) )
11842         break;
11843     }
11844     if ( i < cv_count )
11845       break;
11846     rc = true;
11847     break;
11848   }
11849   if ( !file.EndRead3dmChunk() ) // end of TCODE_RHINOIO_OBJECT_DATA chunk
11850     rc = false;
11851   if ( !rc && curve ) {
11852     delete curve;
11853     curve = 0;
11854   }
11855 
11856   return curve;
11857 }
11858 
11859 static
ReadV1_RHINOIO_NURBS_SURFACE_OBJECT_DATA(ON_BinaryArchive & file)11860 ON_NurbsSurface* ReadV1_RHINOIO_NURBS_SURFACE_OBJECT_DATA( ON_BinaryArchive& file )
11861 {
11862   // read TCODE_RHINOIO_OBJECT_DATA chunk that is contained in a
11863   // TCODE_RHINOIO_OBJECT_NURBS_SURFACE chunk.  The TCODE_RHINOIO_OBJECT_DATA
11864   // chunk contains the definition of NURBS surfaces written by the
11865   // old RhinoIO toolkit.
11866   bool rc = false;
11867   ON_NurbsSurface* surface = 0;
11868   ON__UINT32 tcode = 0;
11869   ON__INT64 big_value = 0;
11870   int version, dim, is_rat, order[2], cv_count[2], flag, i, j;
11871 
11872   if ( !file.BeginRead3dmBigChunk( &tcode, &big_value ) )
11873     return NULL;
11874   if ( tcode == TCODE_RHINOIO_OBJECT_DATA ) for (;;) {
11875     if ( !file.ReadInt(&version) )
11876       break;
11877     // int bReverse = version & 0x100;
11878     version &= 0xFFFFFEFF;
11879     if ( version != 100 && version != 101 )
11880       break;
11881     file.ReadInt(&dim);
11882     if ( dim < 1 )
11883       break;
11884     file.ReadInt(&is_rat);
11885     if ( is_rat < 0 || is_rat > 1 )
11886       break;
11887     file.ReadInt(&order[0]);
11888     if ( order[0] < 2 )
11889       break;
11890     file.ReadInt(&order[1]);
11891     if ( order[1] < 2 )
11892       break;
11893     file.ReadInt(&cv_count[0]);
11894     if ( cv_count[0] < order[0] )
11895       break;
11896     file.ReadInt(&cv_count[1]);
11897     if ( cv_count[1] < order[1] )
11898       break;
11899     file.ReadInt(&flag);
11900     if ( flag != 0 )
11901       break;
11902 
11903     surface = new ON_NurbsSurface(dim,is_rat,order[0],order[1],cv_count[0],cv_count[1]);
11904     if ( !file.ReadDouble( order[0]+cv_count[0]-2, surface->m_knot[0] ) )
11905       break;
11906     if ( !file.ReadDouble( order[1]+cv_count[1]-2, surface->m_knot[1] ) )
11907       break;
11908     int cvdim = is_rat ? dim+1 : dim;
11909     for ( i = 0; i < cv_count[0]; i++ ) {
11910       for ( j = 0; j < cv_count[1]; j++ ) {
11911         if ( !file.ReadDouble( cvdim, surface->CV(i,j) ) )
11912           break;
11913       }
11914       if ( j < cv_count[1] )
11915         break;
11916     }
11917     if ( i < cv_count[0] )
11918       break;
11919     rc = true;
11920     break;
11921   }
11922   if ( !file.EndRead3dmChunk() ) // end of TCODE_RHINOIO_OBJECT_DATA
11923     rc = false;
11924   if ( !rc && surface ) {
11925     delete surface;
11926     surface = 0;
11927   }
11928   return surface;
11929 }
11930 
ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE(ON_Object ** ppObject,ON_3dmObjectAttributes * pAttributes)11931 bool ON_BinaryArchive::ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE(
11932   ON_Object** ppObject,
11933   ON_3dmObjectAttributes* pAttributes
11934   )
11935 {
11936   // read contents of ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE chunk
11937   // written by v1 RhinoIO toolkit
11938   ON_NurbsCurve* curve = 0;
11939   bool rc = false;
11940   ON_BOOL32 bHaveMat = false;
11941 
11942   // reads TCODE_RHINOIO_OBJECT_DATA header and nurbs curve definition
11943   curve = ReadV1_RHINOIO_NURBS_CURVE_OBJECT_DATA(*this);
11944 
11945   if ( curve ) {
11946     *ppObject = curve;
11947     rc = true;
11948     Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_RHINOIO_OBJECT_END);
11949   }
11950 
11951   return rc;
11952 }
11953 
ReadV1_TCODE_RHINOIO_OBJECT_NURBS_SURFACE(ON_Object ** ppObject,ON_3dmObjectAttributes * pAttributes)11954 bool ON_BinaryArchive::ReadV1_TCODE_RHINOIO_OBJECT_NURBS_SURFACE(
11955   ON_Object** ppObject,
11956   ON_3dmObjectAttributes* pAttributes
11957   )
11958 {
11959   // read contents of TCODE_RHINOIO_OBJECT_NURBS_SURFACE chunk
11960   // written by v1 RhinoIO toolkit
11961   ON_BOOL32 bHaveMat = false;
11962   bool rc = false;
11963   ON_NurbsSurface* surface = 0;
11964 
11965   surface = ReadV1_RHINOIO_NURBS_SURFACE_OBJECT_DATA( *this );
11966 
11967   if ( surface ) {
11968     *ppObject = surface;
11969     rc = true;
11970     Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_RHINOIO_OBJECT_END);
11971   }
11972 
11973   return rc;
11974 }
11975 
11976 static
ReadV1_RHINOIO_BREP_CURVE(ON_BinaryArchive & file)11977 ON_Curve* ReadV1_RHINOIO_BREP_CURVE( ON_BinaryArchive& file )
11978 {
11979   ON_Curve* curve = NULL;
11980   ON_PolyCurve* pcurve = NULL;
11981   ON_NurbsCurve* nurbs_curve = NULL;
11982   int segment_index, segment_count = 0;
11983   ON__UINT32 tcode = 0;
11984   ON__INT64 big_value = 0;
11985 
11986   if ( !file.ReadInt(&segment_count) )
11987     return NULL;
11988   if ( segment_count < 1 )
11989     return NULL;
11990 
11991   for ( segment_index = 0; segment_index < segment_count; segment_index++ ) {
11992     if ( !file.BeginRead3dmBigChunk(&tcode,&big_value) )
11993       break;
11994     if ( tcode == TCODE_RHINOIO_OBJECT_NURBS_CURVE ) {
11995       nurbs_curve = ReadV1_RHINOIO_NURBS_CURVE_OBJECT_DATA( file );
11996     }
11997     if ( !file.EndRead3dmChunk() )
11998       break;
11999     if ( !nurbs_curve )
12000       break;
12001     if ( segment_index == 0 ) {
12002       curve = nurbs_curve;
12003       nurbs_curve = 0;
12004     }
12005     else {
12006       if ( segment_index == 1 ) {
12007         pcurve = new ON_PolyCurve();
12008         pcurve->Append(curve);
12009         curve = pcurve;
12010       }
12011       pcurve->Append(nurbs_curve);
12012       nurbs_curve = NULL;
12013     }
12014   }
12015 
12016   if ( segment_index < segment_count ) {
12017     if ( nurbs_curve ) {
12018       delete nurbs_curve;
12019       nurbs_curve = 0;
12020     }
12021     if ( curve ) {
12022       delete curve;
12023       curve = 0;
12024     }
12025   }
12026   return curve;
12027 }
12028 
ReadV1_TCODE_RHINOIO_OBJECT_BREP(ON_Object ** ppObject,ON_3dmObjectAttributes * pAttributes)12029 bool ON_BinaryArchive::ReadV1_TCODE_RHINOIO_OBJECT_BREP(
12030   ON_Object** ppObject,
12031   ON_3dmObjectAttributes* pAttributes
12032   )
12033 {
12034   ON_3dPoint m_oldTrim_mP[2];
12035   ON_BOOL32 bHaveMat = false;
12036   bool rc = false;
12037   ON_Brep* brep = 0;
12038   ON__UINT32 tcode = 0;
12039   ON__INT64 big_value = 0;
12040   if ( !BeginRead3dmBigChunk( &tcode, &big_value ) )
12041     return false;
12042   if ( tcode == TCODE_RHINOIO_OBJECT_DATA ) for (;;) {
12043     int version = -1;
12044     int sz, i, j;
12045     double tol2d, tol3d;
12046     if ( !ReadInt( &version ) )
12047       break; // serialization version
12048     // version = 100 means the b-rep was written by the RhinoIO toolkit
12049     // version = 101 means the b-rep was written by Rhino 1.0
12050     if ( version != 100 && version != 101 ) {
12051       return false;
12052     }
12053 
12054     brep = new ON_Brep();
12055 
12056     // 2d trimming curves
12057     if ( !ReadInt( &sz ) )
12058       break;
12059     if ( sz < 1 ) {
12060       break;
12061     }
12062     brep->m_C2.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_C2.Append(curve);
12068     }
12069     if ( i < sz )
12070       break;
12071 
12072     // 3d trimming curves
12073     if ( !ReadInt( &sz ) )
12074       break;
12075     if ( sz < 1 ) {
12076       break;
12077     }
12078     brep->m_C3.Reserve(sz);
12079     for ( i = 0; i < sz; i++ ) {
12080       ON_Curve* curve = ReadV1_RHINOIO_BREP_CURVE( *this );
12081       if ( !curve )
12082         break;
12083       brep->m_C3.Append(curve);
12084     }
12085     if ( i < sz )
12086       break;
12087 
12088     // 3d untrimmed surfaces
12089     if ( !ReadInt( &sz ) )
12090       break;
12091     if ( sz < 1 ) {
12092       break;
12093     }
12094     brep->m_S.Reserve(sz);
12095     for ( i = 0; i < sz; i++ ) {
12096       ON_NurbsSurface* surface = 0;
12097       tcode = 0;
12098       big_value = 0;
12099       if ( !BeginRead3dmBigChunk(&tcode,&big_value) )
12100         break;
12101       if ( tcode == TCODE_RHINOIO_OBJECT_NURBS_SURFACE ) {
12102         surface = ReadV1_RHINOIO_NURBS_SURFACE_OBJECT_DATA( *this );
12103       }
12104       if ( !EndRead3dmChunk() )
12105         break;
12106       if ( !surface )
12107         break;
12108       brep->m_S.Append(surface);
12109     }
12110     if ( i < sz )
12111       break;
12112 
12113     // vertices
12114     ReadInt( &sz );
12115     brep->m_V.Reserve(sz);
12116     for ( i = 0; i < sz; i++ ) {
12117       ON_BrepVertex& vertex = brep->NewVertex();
12118       if ( !ReadInt( &vertex.m_vertex_index ) ) break;
12119       if ( !ReadPoint( vertex.point ) ) break;
12120       if ( !ReadArray( vertex.m_ei ) ) break;
12121       if ( !ReadDouble( &vertex.m_tolerance ) ) break;
12122     }
12123     if ( i < sz )
12124       break;
12125 
12126     // edges
12127     ReadInt( &sz );
12128     brep->m_E.Reserve(sz);
12129     for ( i = 0; i < sz; i++ )
12130     {
12131       ON_Interval proxy_domain;
12132       ON_BrepEdge& edge = brep->NewEdge();
12133       if ( !ReadInt( &edge.m_edge_index ) ) break;
12134       if ( !ReadInt( &edge.m_c3i ) ) break;
12135       if ( !ReadInterval( proxy_domain ) ) break;
12136       edge.SetProxyCurveDomain(proxy_domain);
12137       if ( !ReadInt( 2, edge.m_vi ) ) break;
12138       if ( !ReadArray( edge.m_ti ) ) break;
12139       if ( !ReadDouble( &edge.m_tolerance ) ) break;
12140     }
12141     if ( i < sz )
12142       break;
12143 
12144     // trims
12145     ReadInt( &sz );
12146     brep->m_T.Reserve(sz);
12147     for ( i = 0; i < sz; i++ ) {
12148       ON_BrepTrim& trim = brep->NewTrim();
12149       if ( !ReadInt( &trim.m_trim_index ) ) break;
12150       if ( !ReadInt( &trim.m_c2i ) ) break;
12151       ON_Interval d;
12152       if ( !ReadInterval( d ) )
12153         break;
12154       trim.SetProxyCurve(NULL,d);
12155       if ( !ReadInt( &trim.m_ei ) ) break;
12156       if ( !ReadInt( 2, trim.m_vi ) ) break;
12157       j = trim.m_bRev3d;
12158       if ( !ReadInt( &j ) ) break;
12159       trim.m_bRev3d = (j!=0);
12160       if ( !ReadInt( &j ) ) break;
12161       switch(j) {
12162       case 1: trim.m_type = ON_BrepTrim::boundary; break;
12163       case 2: trim.m_type = ON_BrepTrim::mated; break;
12164       case 3: trim.m_type = ON_BrepTrim::seam; break;
12165       case 4: trim.m_type = ON_BrepTrim::singular; break;
12166       }
12167       if ( !ReadInt( &j ) ) break; // legacy iso flag - ignore and recaluate
12168       if ( !ReadInt( &trim.m_li ) ) break;
12169       if ( !ReadDouble( 2, trim.m_tolerance ) ) break;
12170       if ( !ReadPoint( m_oldTrim_mP[0] ) ) break;
12171       if ( !ReadPoint( m_oldTrim_mP[1] ) ) break;
12172       if ( !ReadDouble( &tol2d ) ) break;
12173       if ( !ReadDouble( &tol3d ) ) break;
12174     }
12175     if ( i < sz )
12176       break;
12177 
12178     // loops
12179     ReadInt( &sz );
12180     brep->m_L.Reserve(sz);
12181     for ( i = 0; i < sz; i++ ) {
12182       ON_BrepLoop& loop = brep->NewLoop(ON_BrepLoop::unknown);
12183       if ( !ReadInt( &loop.m_loop_index ) ) break;
12184       if ( !ReadArray( loop.m_ti ) ) break;
12185       if ( !ReadInt( &j ) ) break;
12186       switch (j) {
12187       case 1: loop.m_type = ON_BrepLoop::outer; break;
12188       case 2: loop.m_type = ON_BrepLoop::inner; break;
12189       case 3: loop.m_type = ON_BrepLoop::slit; break;
12190       }
12191       if ( !ReadInt( &loop.m_fi ) ) break;
12192     }
12193     if ( i < sz )
12194       break;
12195 
12196     // faces
12197     ReadInt( &sz );
12198     brep->m_F.Reserve(sz);
12199     for ( i = 0; i < sz; i++ ) {
12200       ON_BrepFace& face = brep->NewFace();
12201       if ( !ReadInt( &face.m_face_index ) ) break;
12202       if ( !ReadArray( face.m_li ) ) break;
12203       if ( !ReadInt( &face.m_si ) ) break;
12204       int k = face.m_bRev;
12205       if ( !ReadInt( &k ) ) break;
12206       face.m_bRev = (k!=0);
12207     }
12208     if ( i < sz )
12209       break;
12210 
12211     // bounding box
12212     {
12213       ON_BoundingBox bbox;
12214       if ( !ReadPoint( bbox.m_min ) ) break;
12215       if ( !ReadPoint( bbox.m_max ) ) break;
12216     }
12217 
12218     rc = true;
12219     break;
12220   }
12221   if ( !EndRead3dmChunk() )
12222     rc = false;
12223   if ( rc && brep ) {
12224     brep->SetTrimIsoFlags();
12225     *ppObject = brep;
12226   }
12227   else {
12228     if ( brep )
12229       delete brep;
12230     rc = false;
12231   }
12232 
12233   if ( rc && brep ) {
12234     Read3dmV1AttributesOrMaterial(pAttributes,NULL,bHaveMat,TCODE_RHINOIO_OBJECT_END);
12235   }
12236 
12237   return rc;
12238 }
12239 
12240 int
Read3dmV1Object(ON_Object ** ppObject,ON_3dmObjectAttributes * pAttributes,unsigned int object_filter)12241 ON_BinaryArchive::Read3dmV1Object(
12242   ON_Object** ppObject,                // object is returned here
12243   ON_3dmObjectAttributes* pAttributes, // optional - object attributes
12244   unsigned int object_filter           // optional filter made by or-ing object_type bits
12245   )
12246 {
12247   int rc = 0;
12248   // returns -1: failure
12249   //          0: end of geometry table
12250   //          1: success
12251   //          2: skipped filtered objects
12252 
12253   ON__UINT32 tcode = 0;
12254   ON__INT64 big_value = 0;
12255   // reads NURBS, point, and mesh objects
12256   for(;;)
12257   {
12258     tcode = 0;
12259     big_value = 0;
12260     if ( !BeginRead3dmBigChunk(&tcode,&big_value) ) {
12261       rc = 0; // at the end of the file
12262       break;
12263     }
12264     switch(tcode)
12265     {
12266 
12267     case  TCODE_TEXT_BLOCK:
12268     case  TCODE_ANNOTATION_LEADER:
12269     case  TCODE_LINEAR_DIMENSION:
12270     case  TCODE_ANGULAR_DIMENSION:
12271     case  TCODE_RADIAL_DIMENSION:
12272       if ( 0 != (ON::annotation_object & object_filter) )
12273       {
12274         if ( ReadV1_TCODE_ANNOTATION( tcode, ppObject, pAttributes ) )
12275           rc = 1;
12276         else
12277           rc = -1;
12278       }
12279       else
12280       {
12281         rc = 2;
12282       }
12283       break;
12284 
12285     case TCODE_RH_POINT:
12286       // v1 3d point
12287       if ( 0 != (ON::point_object & object_filter) ) {
12288         if (ReadV1_TCODE_RH_POINT( ppObject, pAttributes ))
12289           rc = 1;
12290         else
12291           rc = -1;
12292       }
12293       else {
12294         rc = 2;
12295       }
12296       break;
12297 
12298     case TCODE_MESH_OBJECT:
12299       // v1 mesh
12300       if ( 0 != (ON::mesh_object & object_filter) ) {
12301         if ( ReadV1_TCODE_MESH_OBJECT( ppObject, pAttributes ) )
12302           rc = 1;
12303         else
12304           rc = -1;
12305       }
12306       else {
12307         rc = 2;
12308       }
12309       break;
12310 
12311     case TCODE_LEGACY_SHL:
12312       // v1 polysurface
12313       if ( 0 != (ON::mesh_object & object_filter) ) {
12314         if ( ReadV1_TCODE_LEGACY_SHL( ppObject, pAttributes ) )
12315           rc = 1;
12316         else
12317           rc = -1;
12318       }
12319       else {
12320         rc = 2;
12321       }
12322       break;
12323 
12324     case TCODE_LEGACY_FAC:
12325       // v1 trimmed surface
12326       if ( 0 != (ON::mesh_object & object_filter) ) {
12327         if ( ReadV1_TCODE_LEGACY_FAC( ppObject, pAttributes ) )
12328           rc = 1;
12329         else
12330           rc = -1;
12331       }
12332       else {
12333         rc = 2;
12334       }
12335       break;
12336 
12337     case TCODE_LEGACY_CRV:
12338       // v1 curve
12339       if ( 0 != (ON::mesh_object & object_filter) ) {
12340         if ( ReadV1_TCODE_LEGACY_CRV( ppObject, pAttributes ) )
12341           rc = 1;
12342         else
12343           rc = -1;
12344       }
12345       else {
12346         rc = 2;
12347       }
12348       break;
12349 
12350     case TCODE_RHINOIO_OBJECT_NURBS_CURVE:
12351       // old Rhino I/O toolkit nurbs curve
12352       if ( 0 != (ON::mesh_object & object_filter) ) {
12353         if ( ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE( ppObject, pAttributes ) )
12354           rc = 1;
12355         else
12356           rc = -1;
12357       }
12358       else {
12359         rc = 2;
12360       }
12361       break;
12362 
12363     case TCODE_RHINOIO_OBJECT_NURBS_SURFACE:
12364       // old Rhino I/O toolkit nurbs surface
12365       if ( 0 != (ON::mesh_object & object_filter) ) {
12366         if ( ReadV1_TCODE_RHINOIO_OBJECT_NURBS_SURFACE( ppObject, pAttributes ) )
12367           rc = 1;
12368         else
12369           rc = -1;
12370       }
12371       else {
12372         rc = 2;
12373       }
12374       break;
12375 
12376     case TCODE_RHINOIO_OBJECT_BREP:
12377       // old Rhino I/O toolkit nurbs brep
12378       if ( 0 != (ON::mesh_object & object_filter) ) {
12379         if ( ReadV1_TCODE_RHINOIO_OBJECT_BREP( ppObject, pAttributes ) )
12380           rc = 1;
12381         else
12382           rc = -1;
12383       }
12384       else {
12385         rc = 2;
12386       }
12387       break;
12388     }
12389 
12390     if (!EndRead3dmChunk() )
12391       break;
12392     if ( rc == 1 || rc == -1 )
12393       break;
12394   }
12395 
12396   return rc;
12397 }
12398 
12399 #if 1
12400 class ON_OBSOLETE_CCustomMeshUserData : public ON_UserData
12401 {
12402 public:
12403 	ON_OBJECT_DECLARE(ON_OBSOLETE_CCustomMeshUserData);
12404   ON_OBSOLETE_CCustomMeshUserData();
12405 	~ON_OBSOLETE_CCustomMeshUserData();
12406 	ON_BOOL32 GetDescription( ON_wString& );
12407   ON_BOOL32 Read(ON_BinaryArchive& binary_archive);
12408   bool m_bInUse;
12409   ON_MeshParameters m_mp;
12410 };
12411 
12412 ON_OBJECT_IMPLEMENT(ON_OBSOLETE_CCustomMeshUserData, ON_UserData, "69F27695-3011-4FBA-82C1-E529F25B5FD9");
12413 
ON_OBSOLETE_CCustomMeshUserData()12414 ON_OBSOLETE_CCustomMeshUserData::ON_OBSOLETE_CCustomMeshUserData()
12415 {
12416 	m_userdata_copycount = 0;
12417 	m_userdata_uuid = ON_OBSOLETE_CCustomMeshUserData::m_ON_OBSOLETE_CCustomMeshUserData_class_id.Uuid();
12418   m_application_uuid = ON_nil_uuid;
12419   m_bInUse = false;
12420 }
12421 
~ON_OBSOLETE_CCustomMeshUserData()12422 ON_OBSOLETE_CCustomMeshUserData::~ON_OBSOLETE_CCustomMeshUserData()
12423 {
12424 }
12425 
Read(ON_BinaryArchive & ba)12426 ON_BOOL32 ON_OBSOLETE_CCustomMeshUserData::Read(ON_BinaryArchive& ba)
12427 {
12428   int i = 0;
12429   if ( !ba.ReadInt( &i ) )
12430     return false;
12431   if( !ba.ReadBool( &m_bInUse ) )
12432     return false;
12433   return m_mp.Read( ba );
12434 }
12435 
GetDescription(ON_wString & s)12436 ON_BOOL32 ON_OBSOLETE_CCustomMeshUserData::GetDescription( ON_wString& s )
12437 {
12438 	s = "OBSOLETE CustomMeshUserData";
12439 	return true;
12440 }
12441 #endif
12442 
12443 
12444 int
Read3dmObject(ON_Object ** ppObject,ON_3dmObjectAttributes * pAttributes,unsigned int object_filter)12445 ON_BinaryArchive::Read3dmObject(
12446   ON_Object** ppObject,                // object is returned here
12447   ON_3dmObjectAttributes* pAttributes, // optional - object attributes
12448   unsigned int object_filter           // optional filter made by or-ing object_type bits
12449   )
12450 {
12451   // returns -1: failure
12452   //          0: end of geometry table
12453   //          1: success
12454   //          2: skipped filtered objects
12455   //          3: skipped new object (object's class UUID wasn't found in class list)
12456   ON_BOOL32 rc = -1;
12457   if ( pAttributes )
12458     pAttributes->Default();
12459   if ( !ppObject )
12460     return 0;
12461   *ppObject = 0;
12462   if ( !object_filter ) // default filter (0) reads every object
12463     object_filter = 0xFFFFFFFF;
12464 
12465   if ( m_3dm_version == 1 ) {
12466     rc = Read3dmV1Object(ppObject,pAttributes,object_filter);
12467   }
12468   else
12469   {
12470     ON__UINT32 tcode = 0;
12471     ON__INT64 length_TCODE_OBJECT_RECORD = 0;
12472     ON__INT64 value_TCODE_OBJECT_RECORD_TYPE = 0;
12473     ON__INT64 length_TCODE_OBJECT_RECORD_ATTRIBUTES = 0;
12474     if ( BeginRead3dmBigChunk( &tcode, &length_TCODE_OBJECT_RECORD ) )
12475     {
12476       if ( tcode == TCODE_OBJECT_RECORD )
12477       {
12478         if (BeginRead3dmBigChunk( &tcode, &value_TCODE_OBJECT_RECORD_TYPE ))
12479         {
12480           if ( tcode != TCODE_OBJECT_RECORD_TYPE ) {
12481             rc = -1;
12482             ON_ERROR("ON_BinaryArchive::Read3dmObject() - missing TCODE_OBJECT_RECORD_TYPE chunk.");
12483           }
12484           else if ( 0 != value_TCODE_OBJECT_RECORD_TYPE && 0 == (value_TCODE_OBJECT_RECORD_TYPE | object_filter) )
12485             rc = 2; // skip reading this object
12486           else
12487             rc = 1; // need to read this object
12488 
12489           if ( !EndRead3dmChunk() )
12490             rc = -1;
12491 
12492           switch(ReadObject(ppObject))
12493           {
12494           case 1:
12495             rc = 1; // successfully read this object
12496             break;
12497           case 3:
12498             rc = 3; // skipped object - assume it's just a newer object than this code reads
12499             break;
12500           default:
12501             rc = -1; // serious failure
12502             break;
12503           }
12504         }
12505         else
12506           rc = -1;
12507       }
12508       else if ( tcode != TCODE_ENDOFTABLE ) {
12509         ON_ERROR("ON_BinaryArchive::Read3dmObject() - corrupt object table");
12510         rc = -1;
12511       }
12512       else
12513         rc = 0;
12514 
12515       while(rc==1)
12516       {
12517         tcode = 0;
12518         if (!BeginRead3dmBigChunk( &tcode, &length_TCODE_OBJECT_RECORD_ATTRIBUTES )) {
12519           rc = -1;
12520           break;
12521         }
12522         if ( tcode == TCODE_OBJECT_RECORD_ATTRIBUTES )
12523         {
12524           if ( 0 != pAttributes )
12525           {
12526             if ( !pAttributes->Read( *this ) )
12527               rc = -1;
12528           }
12529         }
12530         else if ( tcode == TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA )
12531         {
12532           if ( 0 != pAttributes )
12533           {
12534             // 19 October 2004
12535             //   Added support for saving user data on object attributes
12536             if ( !ReadObjectUserData(*pAttributes))
12537               rc = -1;
12538             else
12539             {
12540 #if 1
12541               // 3 March 2011 - convert obsolete user data
12542               ON_OBSOLETE_CCustomMeshUserData* ud = ON_OBSOLETE_CCustomMeshUserData::Cast(pAttributes->GetUserData(ON_OBSOLETE_CCustomMeshUserData::m_ON_OBSOLETE_CCustomMeshUserData_class_id.Uuid()));
12543               if ( ud )
12544               {
12545                 ud->m_mp.m_bCustomSettingsEnabled = ud->m_bInUse ? true : false;
12546                 pAttributes->SetCustomRenderMeshParameters(ud->m_mp);
12547                 delete ud;
12548               }
12549 #endif
12550             }
12551           }
12552         }
12553 
12554         if ( !EndRead3dmChunk() )
12555         {
12556           rc = -1;
12557         }
12558         if ( tcode == TCODE_OBJECT_RECORD_END )
12559           break;
12560       }
12561 
12562       if ( !EndRead3dmChunk() )
12563         rc = -1;
12564     }
12565   }
12566 
12567   return rc;
12568 }
12569 
EndRead3dmObjectTable()12570 bool ON_BinaryArchive::EndRead3dmObjectTable()
12571 {
12572   bool rc = EndRead3dmTable( TCODE_OBJECT_TABLE );
12573 
12574   if ( 0 != m_V1_layer_list )
12575   {
12576     struct ON__3dmV1LayerIndex* next = m_V1_layer_list;
12577     m_V1_layer_list = 0;
12578     for ( int i = 0; 0 != next && i < 1000; i++ )
12579     {
12580       struct ON__3dmV1LayerIndex* p = next;
12581       next = p->m_next;
12582       onfree(p);
12583     }
12584   }
12585 
12586   return rc;
12587 }
12588 
BeginWrite3dmUserTable(const ON_UUID & usertable_uuid)12589 bool ON_BinaryArchive::BeginWrite3dmUserTable( const ON_UUID& usertable_uuid )
12590 {
12591   return BeginWrite3dmUserTable(usertable_uuid, false, 0, 0 );
12592 }
12593 
BeginWrite3dmUserTable(const ON_UUID & plugin_id,bool bSavingGoo,int goo_3dm_version,int goo_opennurbs_version)12594 bool ON_BinaryArchive::BeginWrite3dmUserTable(
12595     const ON_UUID& plugin_id,
12596     bool bSavingGoo,
12597     int goo_3dm_version,
12598     int goo_opennurbs_version
12599     )
12600 {
12601   if ( m_active_table != no_active_table ) {
12602     ON_ERROR("ON_BinaryArchive::BeginWrite3dmUserTable() - m_active_table != no_active_table");
12603   }
12604   if ( !ON_UuidCompare( &ON_nil_uuid, &plugin_id ) ) {
12605     ON_ERROR("ON_BinaryArchive::BeginWrite3dmUserTable() - nil usertable_uuid not permitted.");
12606     return false;
12607   }
12608   if ( bSavingGoo )
12609   {
12610     if ( goo_3dm_version <= 3 )
12611       return false;
12612     if ( goo_opennurbs_version < 200601010 )
12613       return false;
12614     if ( goo_3dm_version >= 50 && Archive3dmVersion() < 50 )
12615     {
12616       // goo with 8 byte chunk lengths cannot be saved
12617       // in files expecting 4 byte chunk lengths.
12618       return false;
12619     }
12620   }
12621   else
12622   {
12623     goo_3dm_version = Archive3dmVersion();
12624     goo_opennurbs_version = ArchiveOpenNURBSVersion();
12625   }
12626 
12627   bool rc = BeginWrite3dmTable( TCODE_USER_TABLE);
12628   if (rc) {
12629     rc = BeginWrite3dmChunk( TCODE_USER_TABLE_UUID, 0 );
12630     if (rc)
12631     {
12632       rc = WriteUuid( plugin_id );
12633       if (rc)
12634       {
12635         // The TCODE_USER_TABLE_RECORD_HEADER chunk was added in
12636         // version 200910190 to contain the archive and opennurbs
12637         // version the plug-in used when writing the file.
12638         // This information is needed so "goo" can be correctly
12639         // read.
12640         rc = BeginWrite3dmChunk( TCODE_USER_TABLE_RECORD_HEADER, 1, 0 );
12641         if ( rc )
12642         {
12643           if (rc) rc = WriteBool(bSavingGoo);
12644           if (rc) rc = WriteInt(goo_3dm_version);
12645           if (rc) rc = WriteInt(goo_opennurbs_version);
12646           if ( !EndWrite3dmChunk() )
12647             rc = false;
12648         }
12649       }
12650       if ( !EndWrite3dmChunk() )
12651         rc = false;
12652     }
12653     if (rc) {
12654       rc = BeginWrite3dmChunk( TCODE_USER_RECORD, 0 );
12655     }
12656     if ( !rc ) {
12657       EndWrite3dmTable( TCODE_USER_TABLE);
12658     }
12659   }
12660   return rc;
12661 }
12662 
Write3dmAnonymousUserTableRecord(const ON_UUID & plugin_id,int goo_3dm_version,int goo_opennurbs_version,const ON_3dmGoo & goo)12663 bool ON_BinaryArchive::Write3dmAnonymousUserTableRecord(
12664     const ON_UUID& plugin_id,
12665     int goo_3dm_version,
12666     int goo_opennurbs_version,
12667     const ON_3dmGoo& goo
12668     )
12669 {
12670   if ( ON_UuidIsNil(plugin_id) )
12671     return false;
12672   if ( goo_3dm_version <= 3 )
12673     return false;
12674   if (goo_opennurbs_version < 200000000 )
12675     return false;
12676   if ( goo.m_typecode != TCODE_USER_RECORD )
12677     return false;
12678   if ( 0 == goo.m_value )
12679     return false;
12680   if ( 0 == goo.m_goo )
12681     return false;
12682   bool bSavingGoo = true;
12683   if ( !BeginWrite3dmUserTable( plugin_id, bSavingGoo, goo_3dm_version, goo_opennurbs_version ) )
12684     return false;
12685   bool rc = WriteByte( goo.m_value, goo.m_goo );
12686   if ( !EndWrite3dmUserTable() )
12687     rc = false;
12688   return rc;
12689 }
12690 
12691 
Write3dmAnonymousUserTable(const ON_3dmGoo & goo)12692 bool ON_BinaryArchive::Write3dmAnonymousUserTable( const ON_3dmGoo& goo )
12693 {
12694   bool rc = false;
12695   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
12696   if ( !c || c->m_typecode != TCODE_USER_RECORD ) {
12697     ON_ERROR("ON_BinaryArchive::Write3dmAnonymousUserTable() - active chunk not a TCODE_USER_RECORD.");
12698     rc = false;
12699   }
12700   else if ( goo.m_typecode != TCODE_USER_RECORD ) {
12701     ON_ERROR("ON_BinaryArchive::Write3dmAnonymousUserTable() - goo chunk not a TCODE_USER_RECORD.");
12702     rc = false;
12703   }
12704   else {
12705     rc = ( goo.m_value > 0 ) ? WriteByte( goo.m_value, goo.m_goo ) : true;
12706   }
12707   return rc;
12708 }
12709 
12710 
EndWrite3dmUserTable()12711 bool ON_BinaryArchive::EndWrite3dmUserTable()
12712 {
12713   bool rc = false;
12714   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
12715   if ( c && c->m_typecode == TCODE_USER_RECORD ) {
12716     rc = EndWrite3dmChunk();
12717   }
12718   else {
12719     ON_ERROR("ON_BinaryArchive::EndWrite3dmUserTable() - not in a TCODE_USER_RECORD chunk.");
12720     rc = false;
12721   }
12722   if ( !EndWrite3dmTable(TCODE_USER_TABLE) )
12723     rc = false;
12724   return rc;
12725 }
12726 
BeginRead3dmUserTable(ON_UUID & usertable_uuid)12727 bool ON_BinaryArchive::BeginRead3dmUserTable( ON_UUID& usertable_uuid )
12728 {
12729   // OBSOLETE - use the  BeginRead3dmUserTable() with more arguments
12730   bool bGoo = false;
12731   int archive_3dm_version = 0;
12732   int archive_opennurbs_version = 0;
12733   bool rc = BeginRead3dmUserTable( usertable_uuid, &bGoo, &archive_3dm_version, &archive_opennurbs_version );
12734   return rc;
12735 }
12736 
BeginRead3dmUserTable(ON_UUID & plugin_id,bool * bLastSavedAsGoo,int * archive_3dm_version,int * archive_opennurbs_version)12737 bool ON_BinaryArchive::BeginRead3dmUserTable(
12738     ON_UUID& plugin_id,
12739     bool* bLastSavedAsGoo,
12740     int* archive_3dm_version,
12741     int* archive_opennurbs_version
12742     )
12743 {
12744   bool bReadArchiveInfo = false;
12745   if ( bLastSavedAsGoo )
12746     *bLastSavedAsGoo = false;
12747   if ( archive_3dm_version )
12748     *archive_3dm_version = 0;
12749   if ( archive_opennurbs_version )
12750     *archive_opennurbs_version = 0;
12751 
12752   if ( m_3dm_version == 1 )
12753     return false;
12754 
12755   bool rc = BeginRead3dmTable( TCODE_USER_TABLE );
12756 
12757   // Do not add calls to EmergencyFindTable() here.
12758   // BeginRead3dmTable( TCODE_USER_TABLE ) returns
12759   // false when there are no user tables and that
12760   // is a common situation.
12761 
12762   if ( rc )
12763   {
12764     // read table id
12765     ON__UINT32 tcode = 0;
12766     ON__INT64 big_value = 0;
12767     if (rc) rc = BeginRead3dmBigChunk( &tcode, &big_value );
12768     if (rc)
12769     {
12770       if ( tcode != TCODE_USER_TABLE_UUID )
12771       {
12772         ON_ERROR("ON_BinaryArchive::BeginRead3dmUserTable() - missing user table UUID");
12773         rc = false;
12774       }
12775       else
12776       {
12777         rc = ReadUuid( plugin_id );
12778 
12779         // Version 200910190 of OpenNURBS began writing a TCODE_USER_TABLE_RECORD_HEADER
12780         // section immediately after the uuid.  This was possible because the uuid
12781         // was wrapped in a TCODE_USER_TABLE_UUID chunk.  The TCODE_USER_TABLE_RECORD_HEADER
12782         // contains information that let's us determine what version of Rhino and
12783         // opennurbs wrote the user table.  We need to know this because "goo"
12784         // can have chunks with 4 byte lengths embedded in a file with 8 byte
12785         // chunk lengths.  If this information is missing, then we know the "goo"
12786         // must have 4 byte chunk lengths and we assume it is from a V4 file.
12787         //
12788         // 37 + SizeofChunkLength() =
12789         //      16 bytes of uuid
12790         //    + 4 bytes of TCODE_USER_TABLE_RECORD_HEADER typecode
12791         //    + SizeofChunkLength() TCODE_USER_TABLE_RECORD_HEADER chunk length
12792         //    + 1 byte of bSlastSavedAsGoo bool
12793         //    + 4 bytes of archive_3dm_version
12794         //    + 4 bytes of archive_opennurbs_version
12795         //    + 4 bytes of TCODE_USER_TABLE_RECORD_HEADER chunk crc
12796         //    + 4 bytes of TCODE_USER_TABLE_UUID chunk crc
12797         const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
12798         if ( rc
12799              && ArchiveOpenNURBSVersion() >= 200910190
12800              && 0 != c
12801              && TCODE_USER_TABLE_UUID == c->m_typecode
12802              && c->Length() >= 45 + SizeofChunkLength()
12803            )
12804         {
12805           int major_chunk_version = 0;
12806           int minor_chunk_version = 0;
12807           rc = BeginRead3dmChunk(TCODE_USER_TABLE_RECORD_HEADER,&major_chunk_version,&minor_chunk_version);
12808           if (rc)
12809           {
12810             bReadArchiveInfo = true;
12811             bool b = true;
12812             int arch_ver = 0;
12813             int on_ver = 0;
12814             rc = ReadBool(&b);
12815             if ( rc && bLastSavedAsGoo )
12816               *bLastSavedAsGoo = b;
12817             if (rc)
12818               rc = ReadInt(&arch_ver);
12819             if (rc && archive_3dm_version)
12820               *archive_3dm_version = arch_ver;
12821             if (rc)
12822               rc = ReadInt(&on_ver);
12823             if (rc && archive_opennurbs_version)
12824               *archive_opennurbs_version = on_ver;
12825             if ( !EndRead3dmChunk() )
12826               rc = false;
12827           }
12828         }
12829       }
12830       if ( !EndRead3dmChunk() )
12831         rc = false;
12832     }
12833 
12834     tcode = 0;
12835     big_value = 0;
12836     if (rc) rc = BeginRead3dmBigChunk( &tcode, &big_value );
12837     if (rc) {
12838       if ( tcode != TCODE_USER_RECORD ) {
12839         ON_ERROR("ON_BinaryArchive::BeginRead3dmUserTable() - missing user table TCODE_USER_RECORD chunk.");
12840         EndRead3dmChunk();
12841         rc = false;
12842       }
12843     }
12844 
12845     if (!rc)
12846       EndRead3dmTable(TCODE_USER_TABLE);
12847 
12848     if ( rc && !bReadArchiveInfo )
12849     {
12850       // If the file we are reading is V4 or an early V5 file, then use the
12851       // version numbers from the file.  Otherwise, assume the goo is from
12852       // an early V5 file.  All we know for sure is that the chunk lengths
12853       // in the user table are 4 bytes.
12854       if ( Archive3dmVersion() < 50 )
12855       {
12856         if (archive_3dm_version)
12857           *archive_3dm_version = Archive3dmVersion();
12858         if (archive_opennurbs_version)
12859           *archive_opennurbs_version = ArchiveOpenNURBSVersion();
12860       }
12861       else
12862       {
12863         if (archive_3dm_version)
12864           *archive_3dm_version = 5;
12865         if (archive_opennurbs_version)
12866           *archive_opennurbs_version = 200910180;
12867       }
12868     }
12869   }
12870 
12871 
12872   return rc;
12873 }
12874 
Read3dmAnonymousUserTable(ON_3dmGoo & goo)12875 bool ON_BinaryArchive::Read3dmAnonymousUserTable( ON_3dmGoo& goo )
12876 {
12877   return Read3dmAnonymousUserTable(0,0,goo);
12878 }
12879 
Read3dmAnonymousUserTable(int archive_3dm_version,int archive_opennurbs_version,ON_3dmGoo & goo)12880 bool ON_BinaryArchive::Read3dmAnonymousUserTable(
12881     int archive_3dm_version,
12882     int archive_opennurbs_version,
12883     ON_3dmGoo& goo
12884     )
12885 {
12886   if ( 0 == archive_3dm_version )
12887   {
12888     if ( Archive3dmVersion() < 50 )
12889     {
12890       archive_3dm_version = Archive3dmVersion();
12891       archive_opennurbs_version = ArchiveOpenNURBSVersion();
12892     }
12893     else
12894     {
12895       // recent version with 4 byte chunk lengths.
12896       archive_3dm_version = 5;
12897       archive_opennurbs_version = 200910190;
12898     }
12899   }
12900   bool rc = Read3dmGoo( goo );
12901   if (rc && goo.m_typecode != TCODE_USER_RECORD ) {
12902     ON_ERROR("ON_BinaryArchive::Read3dmAnonymousUserTable() do not read a TCODE_USER_RECORD chunk.");
12903     rc = false;
12904   }
12905   return rc;
12906 }
12907 
EndRead3dmUserTable()12908 bool ON_BinaryArchive::EndRead3dmUserTable()
12909 {
12910   if ( m_chunk.Count() != 2 ) {
12911     ON_ERROR("ON_BinaryArchive::EndRead3dmUserTable() m_chunk.Count() != 2");
12912     return false;
12913   }
12914   const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
12915   if ( 0 == c || c->m_typecode != TCODE_USER_RECORD ) {
12916     ON_ERROR("ON_BinaryArchive::EndRead3dmTable() m_chunk.Last()->typecode != TCODE_USER_RECORD");
12917     return false;
12918   }
12919 
12920 
12921   // end of TCODE_USER_RECORD chunk
12922   // Suppress the partially read chunk warning because plug-in IO
12923   // is too upredictable for this warning to be helpful.
12924   bool rc = EndRead3dmChunk(true);
12925 
12926   if (rc) {
12927     // end of table chunk
12928     unsigned int tcode = 0;
12929     ON__INT64 big_value = -1;
12930     rc = BeginRead3dmBigChunk( &tcode, &big_value );
12931     if ( rc ) {
12932       if ( tcode != TCODE_ENDOFTABLE ) {
12933         ON_ERROR("ON_BinaryArchive::EndRead3dmTable() missing TCODE_ENDOFTABLE marker.");
12934       }
12935       if ( !EndRead3dmChunk() )
12936         rc = false;
12937     }
12938   }
12939 
12940   if ( !EndRead3dmTable(TCODE_USER_TABLE) )
12941     rc = false;
12942   return rc;
12943 }
12944 
Write3dmEndMark()12945 bool ON_BinaryArchive::Write3dmEndMark()
12946 {
12947   Flush();
12948   if ( m_chunk.Count() != 0 ) {
12949     ON_ERROR( "ON_BinaryArchive::WriteEndMark() called with unfinished chunks.\n" );
12950     return false;
12951   }
12952   ON__UINT64 length = CurrentPosition(); // since no chunks are unfinished, everything
12953                                   // has been committed to disk in either
12954                                   // write mode.
12955   bool rc = BeginWrite3dmChunk( TCODE_ENDOFFILE, 0 );
12956   if ( rc )
12957   {
12958     size_t sizeof_chunk_length = SizeofChunkLength();
12959     size_t sizeoffile_length = (8==SizeofChunkLength()) ? 8 : 4;
12960     length += (4 + sizeof_chunk_length + sizeoffile_length );
12961     rc = WriteEOFSizeOfFile(length);
12962     if ( !EndWrite3dmChunk() )
12963       rc = false;
12964   }
12965   Flush();
12966 
12967   return rc;
12968 }
12969 
Read3dmEndMark(size_t * file_length)12970 bool ON_BinaryArchive::Read3dmEndMark( size_t* file_length )
12971 {
12972   unsigned int tcode=0;
12973   ON__INT64 value=0;
12974   if ( file_length )
12975     *file_length = 0;
12976 
12977   const unsigned int saved_error_message_mask = m_error_message_mask;
12978   m_error_message_mask |= 0x0001; // disable v1 ReadByte() error message at EOF
12979   bool rc = PeekAt3dmBigChunkType(&tcode,&value);
12980   m_error_message_mask = saved_error_message_mask;
12981 
12982   if (rc)
12983   {
12984     if ( tcode == TCODE_ENDOFFILE )
12985     {
12986       rc = BeginRead3dmBigChunk(&tcode,&value);
12987       if ( rc && value > 0 && ((ON__UINT64)value) >= SizeofChunkLength() )
12988       {
12989         ON__UINT64 u64 = 0;
12990         rc = ReadEOFSizeOfFile( &u64 );
12991         if ( rc && file_length )
12992           *file_length = (size_t)u64;
12993         if ( !EndRead3dmChunk() )
12994           rc = false;
12995       }
12996     }
12997   }
12998   return rc;
12999 }
13000 
13001 ///////////////////////////////////////////////////////////////////////////////
13002 ///////////////////////////////////////////////////////////////////////////////
13003 ///////////////////////////////////////////////////////////////////////////////
13004 
13005 ON::endian
Endian() const13006 ON_BinaryArchive::Endian() const // endian-ness of cpu
13007 {
13008   return m_endian;
13009 }
13010 
13011 ON::archive_mode
Mode() const13012 ON_BinaryArchive::Mode() const
13013 {
13014   return m_mode;
13015 }
13016 
UpdateCRC(size_t count,const void * p)13017 void ON_BinaryArchive::UpdateCRC( size_t count, const void* p )
13018 {
13019   if ( m_bDoChunkCRC ) {
13020     ON_3DM_BIG_CHUNK* c = m_chunk.Last();
13021     if (c) {
13022       if ( c->m_do_crc16 )
13023         c->m_crc16 = ON_CRC16( c->m_crc16, count, p ); // version 1 files had 16 bit CRC
13024       if ( c->m_do_crc32 )
13025         c->m_crc32 = ON_CRC32( c->m_crc32, count, p ); // version 2 files have 32 bit CRC
13026     }
13027   }
13028 }
13029 
BadCRCCount() const13030 int ON_BinaryArchive::BadCRCCount() const
13031 {
13032   return m_bad_CRC_count;
13033 }
13034 
ErrorMessageMask() const13035 unsigned int ON_BinaryArchive::ErrorMessageMask() const
13036 {
13037   return m_error_message_mask;
13038 }
13039 
MaskReadError(ON__UINT64 sizeof_request,ON__UINT64 sizeof_read) const13040 bool ON_BinaryArchive::MaskReadError( ON__UINT64 sizeof_request, ON__UINT64 sizeof_read ) const
13041 {
13042   if ( sizeof_request == sizeof_read )
13043     return true; // no error
13044   if ( sizeof_request > sizeof_read )
13045     return false; // something is seriously wrong
13046   if ( 0 != (0x04 & m_error_message_mask) )
13047     return true;
13048   if ( 0 != (0x01 & m_error_message_mask) && 4 == sizeof_request && 0 == sizeof_read )
13049     return true;
13050   return false; // parial read not permitted at this time.
13051 }
13052 
ReadBuffer(ON__UINT64 sizeof_buffer,void * buffer)13053 ON__UINT64 ON_BinaryArchive::ReadBuffer( ON__UINT64 sizeof_buffer, void* buffer )
13054 {
13055   // Expert user function to load a buffer with up to sizeof_buffer
13056   // bytes but tolerate encountering the end of the file.
13057   if ( 0 == buffer )
13058     return 0;
13059   unsigned int saved_error_mask = m_error_message_mask;
13060   m_error_message_mask |= 0x04; // tell Read to tolerate hitting the end of the file
13061   ON__UINT64 sizeof_read = Read((size_t)sizeof_buffer,buffer);
13062   m_error_message_mask = saved_error_mask;
13063   return sizeof_read;
13064 }
13065 
13066 
13067 ///////////////////////////////////////////////////////////////////////////////
13068 ///////////////////////////////////////////////////////////////////////////////
13069 ///////////////////////////////////////////////////////////////////////////////
13070 
ON_BinaryFile(ON::archive_mode mode)13071 ON_BinaryFile::ON_BinaryFile( ON::archive_mode mode )
13072               : ON_BinaryArchive( mode ),
13073                 m_fp(0),
13074                 m_memory_buffer_capacity(0),
13075                 m_memory_buffer_size(0),
13076                 m_memory_buffer_ptr(0),
13077                 m_memory_buffer(0)
13078 {}
13079 
ON_BinaryFile(ON::archive_mode mode,FILE * fp)13080 ON_BinaryFile::ON_BinaryFile( ON::archive_mode mode, FILE* fp )
13081               : ON_BinaryArchive( mode ),
13082                 m_fp(fp),
13083                 m_memory_buffer_capacity(0),
13084                 m_memory_buffer_size(0),
13085                 m_memory_buffer_ptr(0),
13086                 m_memory_buffer(0)
13087 {}
13088 
~ON_BinaryFile()13089 ON_BinaryFile::~ON_BinaryFile()
13090 {
13091   EnableMemoryBuffer(0);
13092 }
13093 
13094 bool
ReadByte(size_t count,void * p)13095 ON_BinaryArchive::ReadByte( size_t count, void* p )
13096 {
13097   bool rc = false;
13098   if ( count > 0 ) {
13099     if ( !ReadMode() ) {
13100       ON_ERROR("ON_BinaryArchive::ReadByte() ReadMode() is false.");
13101     }
13102     else if ( p )
13103     {
13104 
13105 #if defined(ON_DEBUG)
13106       {
13107         // this is slow (becuase of the call to CurrentPosition(), but it really helps find bugs in IO code
13108         const ON_3DM_BIG_CHUNK* c = m_chunk.Last();
13109         if ( c )
13110         {
13111           ON__UINT64 file_offset = CurrentPosition();
13112           if ( file_offset < c->m_big_offset )
13113           {
13114             ON_ERROR("ON_BinaryArchive::ReadByte will read before the start of the chunk.");
13115           }
13116           else if (c->m_big_offset + c->Length() < file_offset + count )
13117           {
13118             ON_ERROR("ON_BinaryArchive::ReadByte will read past end of the chunk");
13119           }
13120         }
13121       }
13122 #endif
13123 
13124       size_t readcount = Read( count, p );
13125       if ( readcount == count )
13126       {
13127         UpdateCRC( count, p );
13128         rc = true;
13129       }
13130       else
13131       {
13132         // see if this is an error condition
13133         for(;;)
13134         {
13135           if ( 0 != (m_error_message_mask&0x01)
13136                && 0 == readcount && 4 == count
13137              )
13138           {
13139             // when reading v1 files, there are some situations where
13140             // it is reasonable to attempt to read 4 bytes at the end
13141             // of a file.
13142             break;
13143           }
13144           if (    0 == m_3dm_version
13145                && 0 == m_3dm_opennurbs_version
13146                && 0 == m_3dm_start_section_offset
13147                && ON_BinaryArchive::no_active_table == m_active_table
13148                && 0 == m_chunk
13149                && ON::read3dm == m_mode
13150                )
13151           {
13152             // In Read3dmStartSection(), we search for the string
13153             // "3D Geometry File Format ...".  When a non-.3dm file
13154             // is searched, we eventually reach the end of the file.
13155             // This error condition is reported by the returning
13156             // false from ON_BinaryArchive::Read3dmStartSection().
13157             // ON_ERROR is not called to prevent annoying everyone
13158             // when the open file dialog is digging around looking
13159             // for files.
13160             break;
13161           }
13162           ON_ERROR("ON_BinaryArchive::ReadByte() Read() failed.");
13163           break;
13164         }
13165       }
13166     }
13167     else
13168     {
13169       ON_ERROR("ON_BinaryArchive::ReadByte() NULL file or buffer.");
13170     }
13171   }
13172   else
13173   {
13174     rc = true;
13175   }
13176   return rc;
13177 }
13178 
13179 bool
WriteByte(size_t count,const void * p)13180 ON_BinaryArchive::WriteByte( size_t count, const void* p )
13181 {
13182   bool rc = false;
13183   if ( count > 0 ) {
13184     if ( !WriteMode() ) {
13185       ON_ERROR("ON_BinaryArchive::WriteByte() WriteMode() is false.");
13186     }
13187     else if ( p ) {
13188       size_t writecount = (size_t)Write( count, p );
13189       if ( writecount == count ) {
13190         UpdateCRC( count, p );
13191         rc = true;
13192       }
13193       else {
13194         ON_ERROR("ON_BinaryArchive::WriteByte() fwrite() failed.");
13195       }
13196     }
13197     else {
13198       ON_ERROR("ON_BinaryArchive::WriteByte() NULL file or buffer.");
13199     }
13200   }
13201   else {
13202     rc = true;
13203   }
13204   return rc;
13205 }
13206 
EnableCRCCalculation(bool bEnable)13207 bool ON_BinaryArchive::EnableCRCCalculation( bool bEnable )
13208 {
13209   bool rc = m_bDoChunkCRC;
13210   m_bDoChunkCRC = bEnable ? true : false;
13211   return rc;
13212 }
13213 
13214 bool
SetArchive3dmVersion(int v)13215 ON_BinaryArchive::SetArchive3dmVersion(int v)
13216 {
13217   bool rc = false;
13218   // valid versions are 1,2,3,4,5,50,60,70,...
13219   // 1 - 4 correspond to Rhino V1 - V4.
13220   // 5 was used for early V5 betas with 4 byte chunk lengths
13221   // 50 is used for V5 files with 8 bytes chunk lengths.
13222   // 60, 70, ... will be used for Rhino V6, V7, etc.
13223   if ( (v >= 1 && v <= 5) || ( v >= 50 && 0 == (v % 10) ) )
13224   {
13225     m_3dm_version = v;
13226     rc = true;
13227   }
13228   else
13229   {
13230     m_3dm_version = 0;
13231     ON_ERROR("ON_BinaryArchive::SetArchive3dmVersion - invalid version");
13232   }
13233   return rc;
13234 }
13235 
13236 void
EnableMemoryBuffer(int buffer_capacity)13237 ON_BinaryFile::EnableMemoryBuffer(
13238        int buffer_capacity
13239        )
13240 {
13241   if ( buffer_capacity > 0 && !m_memory_buffer) {
13242     m_memory_buffer = (unsigned char*)onmalloc(buffer_capacity);
13243     if ( m_memory_buffer ) {
13244       m_memory_buffer_capacity = buffer_capacity;
13245       m_memory_buffer_size = 0;
13246       m_memory_buffer_ptr = 0;
13247     }
13248   }
13249   else {
13250     if ( buffer_capacity == 0 && m_memory_buffer ) {
13251       Flush();
13252       onfree(m_memory_buffer);
13253     }
13254     m_memory_buffer = 0;
13255     m_memory_buffer_capacity = 0;
13256     m_memory_buffer_size = 0;
13257     m_memory_buffer_ptr = 0;
13258   }
13259 }
13260 
13261 
Read(size_t count,void * p)13262 size_t ON_BinaryFile::Read( size_t count, void* p )
13263 {
13264   return (m_fp) ? fread( p, 1, count, m_fp ) : 0;
13265 }
13266 
Write(size_t count,const void * p)13267 size_t ON_BinaryFile::Write( size_t count, const void* p )
13268 {
13269   size_t rc = 0;
13270   if ( m_fp )
13271   {
13272     if ( m_memory_buffer )
13273     {
13274       if ( count+m_memory_buffer_ptr >= m_memory_buffer_capacity ) {
13275         if ( !Flush() ) // flush existing memory buffer to disk
13276           return 0;
13277         rc = fwrite( p, 1, count, m_fp ); // write directly to disk
13278       }
13279       else
13280       {
13281         // copy to the memory buffer
13282         memcpy( m_memory_buffer+m_memory_buffer_ptr, p, count );
13283         m_memory_buffer_ptr += count;
13284         if ( m_memory_buffer_ptr > m_memory_buffer_size )
13285           m_memory_buffer_size = m_memory_buffer_ptr;
13286         rc = count;
13287       }
13288     }
13289     else
13290     {
13291       rc = fwrite( p, 1, count, m_fp );
13292     }
13293   }
13294   return rc;
13295 }
13296 
Flush()13297 bool ON_BinaryFile::Flush()
13298 {
13299   bool rc = true;
13300   if ( m_fp )
13301   {
13302     if ( m_memory_buffer && m_memory_buffer_size > 0 )
13303     {
13304       rc = ( m_memory_buffer_size == fwrite( m_memory_buffer, 1, m_memory_buffer_size, m_fp ));
13305       if ( rc && m_memory_buffer_ptr != m_memory_buffer_size )
13306       {
13307         //if ( !fseek( m_fp, m_memory_buffer_size-m_memory_buffer_ptr, SEEK_CUR ) )
13308         int delta =  (m_memory_buffer_ptr >= m_memory_buffer_size)
13309                   ?  ((int)(m_memory_buffer_ptr - m_memory_buffer_size))
13310                   : -((int)(m_memory_buffer_size - m_memory_buffer_ptr));
13311         if ( !fseek( m_fp, delta, SEEK_CUR ) )
13312         {
13313           rc = false;
13314         }
13315       }
13316       m_memory_buffer_size = 0;
13317       m_memory_buffer_ptr = 0;
13318     }
13319   }
13320   return rc;
13321 }
13322 
CurrentPosition() const13323 size_t ON_BinaryFile::CurrentPosition() const
13324 {
13325   size_t offset = 0;
13326 
13327   if ( 0 != m_fp )
13328   {
13329 
13330 #if defined(ON_COMPILER_MSC)
13331     // use an ftell() that supports file offsets > 2GB
13332     ON__INT64 offset64 = _ftelli64(m_fp);
13333     if ( offset64 < 0 )
13334     {
13335       ON_ERROR("ON_BinaryFile::CurrentPosition() - _ftelli64() failed");
13336     }
13337     else
13338     {
13339       offset = (size_t)((ON__UINT64)offset64);
13340     }
13341 #else
13342     offset = ftell(m_fp);
13343 #endif
13344 
13345     if ( m_memory_buffer && m_memory_buffer_size > 0 )
13346     {
13347       offset += m_memory_buffer_ptr;
13348     }
13349   }
13350   else
13351   {
13352     ON_ERROR("ON_BinaryFile::CurrentPosition() NULL file.");
13353   }
13354 
13355   return offset;
13356 }
13357 
13358 #if defined(__GNUC__) && (!defined( __INTEL_COMPILER)) //G+Smo silence warning
13359     #pragma GCC diagnostic ignored "-Wunused-result"
13360 #       endif
13361 
AtEnd() const13362 bool ON_BinaryFile::AtEnd() const
13363 {
13364   bool rc = true;
13365   if ( m_fp ) {
13366     rc = false;
13367     if ( ReadMode() ) {
13368       if ( feof( m_fp ) ) {
13369         rc = true;
13370       }
13371       else
13372       {
13373         int buffer;
13374         fread( &buffer, 1, 1, m_fp );
13375         if ( feof( m_fp ) )
13376         {
13377           rc = true;
13378         }
13379         else
13380         {
13381           // back up to the byte we just read
13382           fseek( m_fp, -1, SEEK_CUR );
13383         }
13384       }
13385     }
13386   }
13387   return rc;
13388 }
13389 
SeekFromCurrentPosition(int offset)13390 bool ON_BinaryFile::SeekFromCurrentPosition( int offset )
13391 {
13392   // it appears that the calls to SeekFromCurrentPosition(),
13393   // and consequent call to fseek(), in ON_BinaryArchive::EndWrite3DMChunk()
13394   // really slow down 3DM file writing on some networks.  There are
13395   // reports of a 100x difference in local vs network saves.
13396   // I could not reproduce these 100x slow saves in testing on McNeel's
13397   // network but we have a good quality network and a server that's
13398   // properly tuned.  My guess is that the slow saves are happening
13399   // on servers that do a poor job of cacheing because they are starved
13400   // for memory and/or heavily used at the time of the save.
13401   //
13402   // To speed up network saves, ON_BinaryFile can optionally use
13403   // it's own buffer for buffered I/O instead of relying on fwrite()
13404   // and the OS to handle this.
13405   bool rc = false;
13406   if ( m_fp )
13407   {
13408     if ( m_memory_buffer &&
13409          ((ON__INT_PTR)m_memory_buffer_ptr)+((ON__INT_PTR)offset) >= 0 &&
13410          m_memory_buffer_ptr+offset <= m_memory_buffer_size ) {
13411       m_memory_buffer_ptr += offset;
13412       rc = true;
13413     }
13414     else
13415     {
13416       // don't deal with memory buffer I/O if seek lands outside of
13417       // our current memory buffer.
13418       Flush();
13419       if ( !fseek(m_fp,offset,SEEK_CUR) )
13420       {
13421         rc = true;
13422       }
13423       else
13424       {
13425         ON_ERROR("ON_BinaryFile::Seek() fseek(,SEEK_CUR) failed.");
13426       }
13427     }
13428   }
13429   return rc;
13430 }
13431 
SeekFromEnd(int offset)13432 bool ON_BinaryFile::SeekFromEnd( int offset )
13433 {
13434   bool rc = false;
13435   if ( m_fp )
13436   {
13437     Flush(); // don't deal with memory buffer I/O in rare seek from end
13438     if ( !fseek(m_fp,offset,SEEK_END) )
13439     {
13440       rc = true;
13441     }
13442     else
13443     {
13444       ON_ERROR("ON_BinaryFile::SeekFromEnd() fseek(,SEEK_END) failed.");
13445     }
13446   }
13447   return rc;
13448 }
13449 
SeekFromStart(size_t offset)13450 bool ON_BinaryFile::SeekFromStart( size_t offset )
13451 {
13452   bool rc = false;
13453   if ( m_fp )
13454   {
13455     Flush(); // don't deal with memory buffer I/O in rare seek from start
13456     long loffset = (long)offset;
13457     if ( !fseek(m_fp,loffset,SEEK_SET) )
13458     {
13459       rc = true;
13460     }
13461     else
13462     {
13463       ON_ERROR("ON_BinaryFile::SeekFromStart() fseek(,SEEK_SET) failed.");
13464     }
13465   }
13466   return rc;
13467 }
13468 
ON_3dmGoo()13469 ON_3dmGoo::ON_3dmGoo()
13470         : m_typecode(0),
13471           m_value(0),
13472           m_goo(0),
13473           m_next_goo(0),
13474           m_prev_goo(0)
13475 {}
13476 
~ON_3dmGoo()13477 ON_3dmGoo::~ON_3dmGoo()
13478 {
13479   if ( m_prev_goo )
13480     m_prev_goo->m_next_goo = m_next_goo;
13481   if ( m_next_goo )
13482     m_next_goo->m_prev_goo = m_prev_goo;
13483   if ( m_goo ) {
13484     onfree(m_goo);
13485     m_goo = 0;
13486   }
13487 }
13488 
ON_3dmGoo(const ON_3dmGoo & src)13489 ON_3dmGoo::ON_3dmGoo( const ON_3dmGoo& src )
13490         : m_typecode(0),
13491           m_value(0),
13492           m_goo(0),
13493           m_next_goo(0),
13494           m_prev_goo(0)
13495 {
13496   *this = src;
13497 }
13498 
operator =(const ON_3dmGoo & src)13499 ON_3dmGoo& ON_3dmGoo::operator=( const ON_3dmGoo& src )
13500 {
13501   if ( this != &src ) {
13502     if ( m_goo ) {
13503       onfree(m_goo);
13504     }
13505     m_typecode = src.m_typecode;
13506     m_value = src.m_value;
13507     m_goo = (m_value > 0 && src.m_goo) ? (unsigned char*)onmemdup( src.m_goo, m_value ) : 0;
13508   }
13509   return *this;
13510 }
13511 
Dump(ON_TextLog & dump) const13512 void ON_3dmGoo::Dump(ON_TextLog& dump) const
13513 {
13514   dump.Print("typecode = %08x value = %d\n",m_typecode,m_value);
13515 }
13516 
ON_WriteOneObjectArchive(ON_BinaryArchive & archive,int version,const ON_Object & object)13517 bool ON_WriteOneObjectArchive(
13518           ON_BinaryArchive& archive,
13519           int version,
13520           const ON_Object& object
13521           )
13522 {
13523   bool rc = false;
13524 
13525   const ON_Object* pObject = &object;
13526   {
13527     if ( ON_BrepEdge::Cast(pObject) )
13528     {
13529       // write parent brep
13530       pObject = static_cast<const ON_BrepEdge*>(pObject)->Brep();
13531     }
13532     else if ( ON_BrepTrim::Cast(pObject) )
13533     {
13534       pObject = NULL;
13535     }
13536     else if ( ON_BrepLoop::Cast(pObject) )
13537     {
13538       pObject = static_cast<const ON_BrepLoop*>(pObject)->Brep();
13539     }
13540     else if ( ON_BrepFace::Cast(pObject) )
13541     {
13542       // write parent brep
13543       pObject = static_cast<const ON_BrepFace*>(pObject)->Brep();
13544     }
13545     else if ( ON_CurveProxy::Cast(pObject) )
13546     {
13547       // write actual curve
13548       pObject = static_cast<const ON_CurveProxy*>(pObject)->ProxyCurve();
13549     }
13550     else if ( ON_SurfaceProxy::Cast(pObject) )
13551     {
13552       // write actual surface
13553       pObject = static_cast<const ON_SurfaceProxy*>(pObject)->ProxySurface();
13554     }
13555   }
13556 
13557   ON_3dmProperties props;
13558   props.m_RevisionHistory.NewRevision();
13559 
13560   ON_3dmSettings settings;
13561   // 1 Feb 2012 Dale Lear
13562   //   http://dev.mcneel.com/bugtrack/?q=98543
13563   //   Single object archives have no unit system so they
13564   //   can be read into a file with no scaling.  Prior to
13565   //   today the unit system was always millimeters.
13566   settings.m_ModelUnitsAndTolerances.m_unit_system.m_unit_system = ON::no_unit_system;
13567 
13568   ON_Layer layer;
13569   ON_3dmObjectAttributes attributes;
13570 
13571   // layer table will have one layer
13572   layer.SetLayerIndex(0);
13573   layer.SetLayerName(L"Default");
13574   ON_CreateUuid(layer.m_layer_id);
13575 
13576   // object attributes
13577   attributes.m_layer_index = 0;
13578   ON_CreateUuid(attributes.m_uuid);
13579 
13580   while(pObject)
13581   {
13582     rc = archive.Write3dmStartSection( version, "Archive created by ON_WriteOneObjectArchive " __DATE__ " " __TIME__ );
13583     if ( !rc )
13584       break;
13585 
13586     version = archive.Archive3dmVersion();
13587 
13588     rc = archive.Write3dmProperties( props );
13589     if ( !rc )
13590       break;
13591 
13592     rc = archive.Write3dmSettings( settings );
13593     if ( !rc )
13594       break;
13595 
13596     rc = archive.BeginWrite3dmBitmapTable();
13597     if ( !rc )
13598       break;
13599     rc = archive.EndWrite3dmBitmapTable();
13600     if ( !rc )
13601       break;
13602 
13603     if ( version >= 4 )
13604     {
13605       rc = archive.BeginWrite3dmTextureMappingTable();
13606       if ( !rc )
13607         break;
13608       rc = archive.EndWrite3dmTextureMappingTable();
13609       if ( !rc )
13610         break;
13611     }
13612 
13613     rc = archive.BeginWrite3dmMaterialTable();
13614     if ( !rc )
13615       break;
13616     rc = archive.EndWrite3dmMaterialTable();
13617     if ( !rc )
13618       break;
13619 
13620     if ( version >= 4 )
13621     {
13622       rc = archive.BeginWrite3dmLinetypeTable();
13623       if ( !rc )
13624         break;
13625       rc = archive.EndWrite3dmLinetypeTable();
13626       if ( !rc )
13627         break;
13628     }
13629 
13630     rc = archive.BeginWrite3dmLayerTable();
13631     if ( !rc )
13632       break;
13633     {
13634       rc = archive.Write3dmLayer(layer);
13635     }
13636     if (!archive.EndWrite3dmLayerTable())
13637       rc = false;
13638     if ( !rc )
13639       break;
13640 
13641     rc = archive.BeginWrite3dmGroupTable();
13642     if ( !rc )
13643       break;
13644     rc = archive.EndWrite3dmGroupTable();
13645     if ( !rc )
13646       break;
13647 
13648     if ( version >= 3 )
13649     {
13650       rc = archive.BeginWrite3dmFontTable();
13651       if ( !rc )
13652         break;
13653       rc = archive.EndWrite3dmFontTable();
13654       if ( !rc )
13655         break;
13656     }
13657 
13658     if ( version >= 3 )
13659     {
13660       rc = archive.BeginWrite3dmDimStyleTable();
13661       if ( !rc )
13662         break;
13663       rc = archive.EndWrite3dmDimStyleTable();
13664       if ( !rc )
13665         break;
13666     }
13667 
13668     rc = archive.BeginWrite3dmLightTable();
13669     if ( !rc )
13670       break;
13671     rc = archive.EndWrite3dmLightTable();
13672     if ( !rc )
13673       break;
13674 
13675     if ( version >= 4 )
13676     {
13677       rc = archive.BeginWrite3dmHatchPatternTable();
13678       if ( !rc )
13679         break;
13680       rc = archive.EndWrite3dmHatchPatternTable();
13681       if ( !rc )
13682         break;
13683     }
13684 
13685     if ( version >= 3 )
13686     {
13687       rc = archive.BeginWrite3dmInstanceDefinitionTable();
13688       if ( !rc )
13689         break;
13690       rc = archive.EndWrite3dmInstanceDefinitionTable();
13691       if ( !rc )
13692         break;
13693     }
13694 
13695     rc = archive.BeginWrite3dmObjectTable();
13696     if ( !rc )
13697       break;
13698     {
13699       rc = archive.Write3dmObject( *pObject, &attributes );
13700     }
13701     if ( !archive.EndWrite3dmObjectTable() )
13702       rc = false;
13703     if ( !rc )
13704       break;
13705 
13706     if ( version >= 4 )
13707     {
13708       rc = archive.BeginWrite3dmHistoryRecordTable();
13709       if ( !rc )
13710         break;
13711       rc = archive.EndWrite3dmHistoryRecordTable();
13712       if ( !rc )
13713         break;
13714     }
13715 
13716     rc = archive.Write3dmEndMark();
13717 
13718     break;
13719   }
13720 
13721   return rc;
13722 }
13723 
13724 static
Dump3dmChunk_ErrorReportHelper(size_t offset,const char * msg,ON_TextLog & dump)13725 void Dump3dmChunk_ErrorReportHelper( size_t offset, const char* msg, ON_TextLog& dump )
13726 {
13727   int ioffset = (int)offset;
13728   dump.Print("** ERROR near offset %d ** %s\n",ioffset,msg);
13729 }
13730 static
DumpChunk_PrintHeaderInfo(size_t offset0,ON__UINT32 typecode,ON__INT64 big_value,const char * typecode_name,ON_TextLog & dump)13731 bool DumpChunk_PrintHeaderInfo( size_t offset0, ON__UINT32 typecode, ON__INT64 big_value, const char* typecode_name, ON_TextLog& dump)
13732 {
13733   bool bShortChunk = (0 != (typecode & TCODE_SHORT));
13734   if ( 0 == typecode_name )
13735     typecode_name = ON_BinaryArchive::TypecodeName(typecode);
13736   if ( 0 == typecode_name )
13737     typecode_name = "unknown tcode";
13738   if ( bShortChunk )
13739   {
13740     dump.Print("%6d: %08X %s: value = %lld (%016llX)\n", offset0, typecode, typecode_name, big_value, big_value );
13741   }
13742   else
13743   {
13744     // long chunk value = length of chunk data
13745     if ( big_value < 0 )
13746     {
13747       Dump3dmChunk_ErrorReportHelper(offset0,"BeginRead3dmChunk() returned length < 0.",dump);
13748       return false;
13749     }
13750     dump.Print("%6d: %08X %s: length = %lld bytes\n", offset0, typecode, typecode_name, big_value );
13751   }
13752   return true;
13753 }
13754 
13755 #define CASEtcode2string(tc) case tc: s = #tc ; break
13756 
TypecodeName(unsigned int tcode)13757 const char* ON_BinaryArchive::TypecodeName( unsigned int tcode )
13758 {
13759 
13760   const char* s;
13761   switch( tcode )
13762   {
13763   CASEtcode2string(TCODE_FONT_TABLE);
13764   CASEtcode2string(TCODE_FONT_RECORD);
13765   CASEtcode2string(TCODE_DIMSTYLE_TABLE);
13766   CASEtcode2string(TCODE_DIMSTYLE_RECORD);
13767   CASEtcode2string(TCODE_INSTANCE_DEFINITION_RECORD);
13768   CASEtcode2string(TCODE_COMMENTBLOCK);
13769   CASEtcode2string(TCODE_ENDOFFILE);
13770   CASEtcode2string(TCODE_ENDOFFILE_GOO);
13771   CASEtcode2string(TCODE_LEGACY_GEOMETRY);
13772   CASEtcode2string(TCODE_OPENNURBS_OBJECT);
13773   CASEtcode2string(TCODE_GEOMETRY);
13774   CASEtcode2string(TCODE_ANNOTATION);
13775   CASEtcode2string(TCODE_DISPLAY);
13776   CASEtcode2string(TCODE_RENDER);
13777   CASEtcode2string(TCODE_INTERFACE);
13778   CASEtcode2string(TCODE_TOLERANCE);
13779   CASEtcode2string(TCODE_TABLE);
13780   CASEtcode2string(TCODE_TABLEREC);
13781   CASEtcode2string(TCODE_USER);
13782   CASEtcode2string(TCODE_SHORT);
13783   CASEtcode2string(TCODE_CRC);
13784   CASEtcode2string(TCODE_ANONYMOUS_CHUNK);
13785   CASEtcode2string(TCODE_MATERIAL_TABLE);
13786   CASEtcode2string(TCODE_LAYER_TABLE);
13787   CASEtcode2string(TCODE_LIGHT_TABLE);
13788   CASEtcode2string(TCODE_OBJECT_TABLE);
13789   CASEtcode2string(TCODE_PROPERTIES_TABLE);
13790   CASEtcode2string(TCODE_SETTINGS_TABLE);
13791   CASEtcode2string(TCODE_BITMAP_TABLE);
13792   CASEtcode2string(TCODE_USER_TABLE);
13793   CASEtcode2string(TCODE_INSTANCE_DEFINITION_TABLE);
13794   CASEtcode2string(TCODE_HATCHPATTERN_TABLE);
13795   CASEtcode2string(TCODE_HATCHPATTERN_RECORD);
13796   CASEtcode2string(TCODE_LINETYPE_TABLE);
13797   CASEtcode2string(TCODE_LINETYPE_RECORD);
13798   CASEtcode2string(TCODE_OBSOLETE_LAYERSET_TABLE);
13799   CASEtcode2string(TCODE_OBSOLETE_LAYERSET_RECORD);
13800   CASEtcode2string(TCODE_TEXTURE_MAPPING_TABLE);
13801   CASEtcode2string(TCODE_TEXTURE_MAPPING_RECORD);
13802   CASEtcode2string(TCODE_HISTORYRECORD_TABLE);
13803   CASEtcode2string(TCODE_HISTORYRECORD_RECORD);
13804   CASEtcode2string(TCODE_ENDOFTABLE);
13805   CASEtcode2string(TCODE_PROPERTIES_REVISIONHISTORY);
13806   CASEtcode2string(TCODE_PROPERTIES_NOTES);
13807   CASEtcode2string(TCODE_PROPERTIES_PREVIEWIMAGE);
13808   CASEtcode2string(TCODE_PROPERTIES_COMPRESSED_PREVIEWIMAGE);
13809   CASEtcode2string(TCODE_PROPERTIES_APPLICATION);
13810   CASEtcode2string(TCODE_PROPERTIES_OPENNURBS_VERSION);
13811   CASEtcode2string(TCODE_SETTINGS_PLUGINLIST);
13812   CASEtcode2string(TCODE_SETTINGS_UNITSANDTOLS);
13813   CASEtcode2string(TCODE_SETTINGS_RENDERMESH);
13814   CASEtcode2string(TCODE_SETTINGS_ANALYSISMESH);
13815   CASEtcode2string(TCODE_SETTINGS_ANNOTATION);
13816   CASEtcode2string(TCODE_SETTINGS_NAMED_CPLANE_LIST);
13817   CASEtcode2string(TCODE_SETTINGS_NAMED_VIEW_LIST);
13818   CASEtcode2string(TCODE_SETTINGS_VIEW_LIST);
13819   CASEtcode2string(TCODE_SETTINGS_CURRENT_LAYER_INDEX);
13820   CASEtcode2string(TCODE_SETTINGS_CURRENT_MATERIAL_INDEX);
13821   CASEtcode2string(TCODE_SETTINGS_CURRENT_COLOR);
13822   CASEtcode2string(TCODE_SETTINGS__NEVER__USE__THIS);
13823   CASEtcode2string(TCODE_SETTINGS_CURRENT_WIRE_DENSITY);
13824   CASEtcode2string(TCODE_SETTINGS_RENDER);
13825   CASEtcode2string(TCODE_SETTINGS_GRID_DEFAULTS);
13826   CASEtcode2string(TCODE_SETTINGS_MODEL_URL);
13827   CASEtcode2string(TCODE_SETTINGS_CURRENT_FONT_INDEX);
13828   CASEtcode2string(TCODE_SETTINGS_CURRENT_DIMSTYLE_INDEX);
13829   CASEtcode2string(TCODE_SETTINGS_ATTRIBUTES);
13830   CASEtcode2string(TCODE_VIEW_RECORD);
13831   CASEtcode2string(TCODE_VIEW_CPLANE);
13832   CASEtcode2string(TCODE_VIEW_VIEWPORT);
13833   CASEtcode2string(TCODE_VIEW_VIEWPORT_USERDATA);
13834   CASEtcode2string(TCODE_VIEW_SHOWCONGRID);
13835   CASEtcode2string(TCODE_VIEW_SHOWCONAXES);
13836   CASEtcode2string(TCODE_VIEW_SHOWWORLDAXES);
13837   CASEtcode2string(TCODE_VIEW_TRACEIMAGE);
13838   CASEtcode2string(TCODE_VIEW_WALLPAPER);
13839   CASEtcode2string(TCODE_VIEW_WALLPAPER_V3);
13840   CASEtcode2string(TCODE_VIEW_TARGET);
13841   CASEtcode2string(TCODE_VIEW_DISPLAYMODE);
13842   CASEtcode2string(TCODE_VIEW_NAME);
13843   CASEtcode2string(TCODE_VIEW_POSITION);
13844   CASEtcode2string(TCODE_VIEW_ATTRIBUTES);
13845   CASEtcode2string(TCODE_BITMAP_RECORD);
13846   CASEtcode2string(TCODE_MATERIAL_RECORD);
13847   CASEtcode2string(TCODE_LAYER_RECORD);
13848   CASEtcode2string(TCODE_LIGHT_RECORD);
13849   CASEtcode2string(TCODE_LIGHT_RECORD_ATTRIBUTES);
13850   CASEtcode2string(TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA);
13851   CASEtcode2string(TCODE_OBJECT_RECORD_HISTORY);
13852   CASEtcode2string(TCODE_OBJECT_RECORD_HISTORY_HEADER);
13853   CASEtcode2string(TCODE_OBJECT_RECORD_HISTORY_DATA);
13854   CASEtcode2string(TCODE_LIGHT_RECORD_END);
13855   CASEtcode2string(TCODE_USER_TABLE_UUID);
13856   CASEtcode2string(TCODE_USER_TABLE_RECORD_HEADER);
13857   CASEtcode2string(TCODE_USER_RECORD);
13858   CASEtcode2string(TCODE_GROUP_TABLE);
13859   CASEtcode2string(TCODE_GROUP_RECORD);
13860   CASEtcode2string(TCODE_OBJECT_RECORD);
13861   CASEtcode2string(TCODE_OBJECT_RECORD_TYPE);
13862   CASEtcode2string(TCODE_OBJECT_RECORD_ATTRIBUTES);
13863   CASEtcode2string(TCODE_OBJECT_RECORD_END);
13864   CASEtcode2string(TCODE_OPENNURBS_CLASS);
13865   CASEtcode2string(TCODE_OPENNURBS_CLASS_UUID);
13866   CASEtcode2string(TCODE_OPENNURBS_CLASS_DATA);
13867   CASEtcode2string(TCODE_OPENNURBS_CLASS_USERDATA);
13868   CASEtcode2string(TCODE_OPENNURBS_CLASS_USERDATA_HEADER);
13869   CASEtcode2string(TCODE_OPENNURBS_CLASS_END);
13870   CASEtcode2string(TCODE_OPENNURBS_BUFFER);
13871   CASEtcode2string(TCODE_ANNOTATION_SETTINGS);
13872   CASEtcode2string(TCODE_TEXT_BLOCK);
13873   CASEtcode2string(TCODE_ANNOTATION_LEADER);
13874   CASEtcode2string(TCODE_LINEAR_DIMENSION);
13875   CASEtcode2string(TCODE_ANGULAR_DIMENSION);
13876   CASEtcode2string(TCODE_RADIAL_DIMENSION);
13877   CASEtcode2string(TCODE_RHINOIO_OBJECT_NURBS_CURVE);
13878   CASEtcode2string(TCODE_RHINOIO_OBJECT_NURBS_SURFACE);
13879   CASEtcode2string(TCODE_RHINOIO_OBJECT_BREP);
13880   CASEtcode2string(TCODE_RHINOIO_OBJECT_DATA);
13881   CASEtcode2string(TCODE_RHINOIO_OBJECT_END);
13882   CASEtcode2string(TCODE_LEGACY_ASM);
13883   CASEtcode2string(TCODE_LEGACY_PRT);
13884   CASEtcode2string(TCODE_LEGACY_SHL);
13885   CASEtcode2string(TCODE_LEGACY_FAC);
13886   CASEtcode2string(TCODE_LEGACY_BND);
13887   CASEtcode2string(TCODE_LEGACY_TRM);
13888   CASEtcode2string(TCODE_LEGACY_SRF);
13889   CASEtcode2string(TCODE_LEGACY_CRV);
13890   CASEtcode2string(TCODE_LEGACY_SPL);
13891   CASEtcode2string(TCODE_LEGACY_PNT);
13892   CASEtcode2string(TCODE_STUFF);
13893   CASEtcode2string(TCODE_LEGACY_ASMSTUFF);
13894   CASEtcode2string(TCODE_LEGACY_PRTSTUFF);
13895   CASEtcode2string(TCODE_LEGACY_SHLSTUFF);
13896   CASEtcode2string(TCODE_LEGACY_FACSTUFF);
13897   CASEtcode2string(TCODE_LEGACY_BNDSTUFF);
13898   CASEtcode2string(TCODE_LEGACY_TRMSTUFF);
13899   CASEtcode2string(TCODE_LEGACY_SRFSTUFF);
13900   CASEtcode2string(TCODE_LEGACY_CRVSTUFF);
13901   CASEtcode2string(TCODE_LEGACY_SPLSTUFF);
13902   CASEtcode2string(TCODE_LEGACY_PNTSTUFF);
13903   CASEtcode2string(TCODE_RH_POINT);
13904   CASEtcode2string(TCODE_RH_SPOTLIGHT);
13905   CASEtcode2string(TCODE_OLD_RH_TRIMESH);
13906   CASEtcode2string(TCODE_OLD_MESH_VERTEX_NORMALS);
13907   CASEtcode2string(TCODE_OLD_MESH_UV);
13908   CASEtcode2string(TCODE_OLD_FULLMESH);
13909   CASEtcode2string(TCODE_MESH_OBJECT);
13910   CASEtcode2string(TCODE_COMPRESSED_MESH_GEOMETRY);
13911   CASEtcode2string(TCODE_ANALYSIS_MESH);
13912   CASEtcode2string(TCODE_NAME);
13913   CASEtcode2string(TCODE_VIEW);
13914   CASEtcode2string(TCODE_CPLANE);
13915   CASEtcode2string(TCODE_NAMED_CPLANE);
13916   CASEtcode2string(TCODE_NAMED_VIEW);
13917   CASEtcode2string(TCODE_VIEWPORT);
13918   CASEtcode2string(TCODE_SHOWGRID);
13919   CASEtcode2string(TCODE_SHOWGRIDAXES);
13920   CASEtcode2string(TCODE_SHOWWORLDAXES);
13921   CASEtcode2string(TCODE_VIEWPORT_POSITION);
13922   CASEtcode2string(TCODE_VIEWPORT_TRACEINFO);
13923   CASEtcode2string(TCODE_SNAPSIZE);
13924   CASEtcode2string(TCODE_NEAR_CLIP_PLANE);
13925   CASEtcode2string(TCODE_HIDE_TRACE);
13926   CASEtcode2string(TCODE_NOTES);
13927   CASEtcode2string(TCODE_UNIT_AND_TOLERANCES);
13928   CASEtcode2string(TCODE_MAXIMIZED_VIEWPORT);
13929   CASEtcode2string(TCODE_VIEWPORT_WALLPAPER);
13930   CASEtcode2string(TCODE_SUMMARY);
13931   CASEtcode2string(TCODE_BITMAPPREVIEW);
13932   CASEtcode2string(TCODE_VIEWPORT_DISPLAY_MODE);
13933   CASEtcode2string(TCODE_LAYERTABLE);
13934   CASEtcode2string(TCODE_LAYERREF);
13935   CASEtcode2string(TCODE_XDATA);
13936   CASEtcode2string(TCODE_RGB);
13937   CASEtcode2string(TCODE_TEXTUREMAP);
13938   CASEtcode2string(TCODE_BUMPMAP);
13939   CASEtcode2string(TCODE_TRANSPARENCY);
13940   CASEtcode2string(TCODE_DISP_AM_RESOLUTION);
13941   CASEtcode2string(TCODE_RGBDISPLAY);
13942   CASEtcode2string(TCODE_RENDER_MATERIAL_ID);
13943   CASEtcode2string(TCODE_LAYER);
13944   CASEtcode2string(TCODE_LAYER_OBSELETE_1);
13945   CASEtcode2string(TCODE_LAYER_OBSELETE_2);
13946   CASEtcode2string(TCODE_LAYER_OBSELETE_3);
13947   CASEtcode2string(TCODE_LAYERON);
13948   CASEtcode2string(TCODE_LAYERTHAWED);
13949   CASEtcode2string(TCODE_LAYERLOCKED);
13950   CASEtcode2string(TCODE_LAYERVISIBLE);
13951   CASEtcode2string(TCODE_LAYERPICKABLE);
13952   CASEtcode2string(TCODE_LAYERSNAPABLE);
13953   CASEtcode2string(TCODE_LAYERRENDERABLE);
13954   CASEtcode2string(TCODE_LAYERSTATE);
13955   CASEtcode2string(TCODE_LAYERINDEX);
13956   CASEtcode2string(TCODE_LAYERMATERIALINDEX);
13957   CASEtcode2string(TCODE_RENDERMESHPARAMS);
13958   CASEtcode2string(TCODE_DISP_CPLINES);
13959   CASEtcode2string(TCODE_DISP_MAXLENGTH);
13960   CASEtcode2string(TCODE_CURRENTLAYER);
13961   CASEtcode2string(TCODE_LAYERNAME);
13962   CASEtcode2string(TCODE_LEGACY_TOL_FIT);
13963   CASEtcode2string(TCODE_LEGACY_TOL_ANGLE);
13964   CASEtcode2string(TCODE_DICTIONARY);
13965   CASEtcode2string(TCODE_DICTIONARY_ID);
13966   CASEtcode2string(TCODE_DICTIONARY_ENTRY);
13967   CASEtcode2string(TCODE_DICTIONARY_END);
13968   default:
13969     // unknown typecode.
13970     s = 0;
13971     break;
13972   }
13973   return s;
13974 }
13975 
13976 #undef CASEtcode2string
13977 
ON_TypecodeParse(unsigned int tcode,char * typecode_name,size_t max_length)13978 char* ON_BinaryArchive::ON_TypecodeParse( unsigned int tcode, char* typecode_name, size_t max_length )
13979 {
13980   char* s;
13981   const char* sub_name;
13982   const char* h = "0123456789ABCDEF";
13983   char c, c0;
13984   size_t slen;
13985 
13986   if ( !typecode_name || max_length <= 0 )
13987     return 0;
13988   memset(typecode_name,0,max_length*sizeof(typecode_name[0]));
13989   slen = max_length-1; // the -1 insures the there is a null terminator
13990   if ( slen <= 0 )
13991     return 0;
13992 
13993   sub_name = ON_BinaryArchive::TypecodeName(tcode);
13994   if ( 0 != sub_name && 0 != sub_name[0] )
13995   {
13996     c0 = *sub_name++;
13997     s = typecode_name+1;
13998     slen--;
13999     while ( *sub_name )
14000     {
14001       if ( slen <= 0 )
14002         return 0;
14003       *s++ = *sub_name++;
14004       slen--;
14005     }
14006     typecode_name[0] = c0;
14007     return typecode_name;
14008   }
14009 
14010   sub_name = ON_BinaryArchive::TypecodeName( tcode & 0x7FFF0000 );
14011   if ( !sub_name || 0 == sub_name[0] )
14012     return 0;
14013 
14014   c0 = *sub_name++;
14015   s = typecode_name+1;
14016   slen--;
14017 
14018   while ( *sub_name )
14019   {
14020     if ( slen <= 0 )
14021       return 0;
14022     *s++ = *sub_name++;
14023     slen--;
14024   }
14025 
14026   sub_name = ON_BinaryArchive::TypecodeName( tcode & TCODE_SHORT );
14027   if ( sub_name )
14028   {
14029     if ( slen <= 0 ) {return 0;} *s++ = ' '; slen--;
14030     if ( slen <= 0 ) {return 0;} *s++ = '|'; slen--;
14031     if ( slen <= 0 ) {return 0;} *s++ = ' '; slen--;
14032     while ( *sub_name )
14033     {
14034       if ( slen <= 0 )
14035         return 0;
14036       *s++ = *sub_name++;
14037       slen--;
14038     }
14039   }
14040 
14041   sub_name = ON_BinaryArchive::TypecodeName( tcode & TCODE_CRC );
14042   if ( sub_name )
14043   {
14044     if ( slen <= 0 ) {return 0;} *s++ = ' '; slen--;
14045     if ( slen <= 0 ) {return 0;} *s++ = '|'; slen--;
14046     if ( slen <= 0 ) {return 0;} *s++ = ' '; slen--;
14047     while ( *sub_name )
14048     {
14049       if ( slen <= 0 )
14050         return 0;
14051       *s++ = *sub_name++;
14052       slen--;
14053     }
14054   }
14055 
14056   sub_name = ON_BinaryArchive::TypecodeName( tcode & 0x7FFF );
14057   if ( sub_name )
14058   {
14059     if ( slen <= 0 ) {return 0;} *s++ = ' '; slen--;
14060     if ( slen <= 0 ) {return 0;} *s++ = '|'; slen--;
14061     if ( slen <= 0 ) {return 0;} *s++ = ' '; slen--;
14062     while ( *sub_name )
14063     {
14064       if ( slen <= 0 )
14065         return 0;
14066       *s++ = *sub_name++;
14067       slen--;
14068     }
14069   }
14070   else
14071   {
14072     if ( slen <= 0 ) {return 0;} *s++ = ' '; slen--;
14073     if ( slen <= 0 ) {return 0;} *s++ = '|'; slen--;
14074     if ( slen <= 0 ) {return 0;} *s++ = ' '; slen--;
14075     if ( slen <= 0 ) {return 0;} *s++ = '0'; slen--;
14076     if ( slen <= 0 ) {return 0;} *s++ = 'x'; slen--;
14077     c = h[((tcode & 0x7000) / 0x1000) & 0xF];
14078     if ( slen > 0 ) {*s++ = c; slen--;}
14079     c = h[((tcode & 0xF00) / 0x100) & 0xF];
14080     if ( slen > 0 ) {*s++ = c; slen--;}
14081     c = h[((tcode & 0xF0) / 0x10) & 0xF];
14082     if ( slen > 0 ) {*s++ = c; slen--;}
14083     c = h[tcode & 0xF];
14084     if ( slen > 0 ) {*s++ = c; slen--;}
14085   }
14086 
14087   *typecode_name = c0;
14088 
14089   return typecode_name;
14090 }
14091 
14092 static
Dump3dmChunk_EndReadChunkHelper(ON_BinaryArchive & file,size_t offset0,ON__UINT32 tcode,ON__INT64 big_value,ON_TextLog & dump)14093 bool Dump3dmChunk_EndReadChunkHelper( ON_BinaryArchive& file, size_t offset0, ON__UINT32 tcode, ON__INT64 big_value, ON_TextLog& dump )
14094 {
14095   const bool bShortChunk = (0 != (tcode & TCODE_SHORT));
14096   const size_t offset1 = file.CurrentPosition();
14097   bool rc = file.EndRead3dmChunk();
14098   if ( !rc )
14099   {
14100     Dump3dmChunk_ErrorReportHelper(offset1,"EndRead3dmChunk() failed.",dump);
14101   }
14102   else if (!bShortChunk)
14103   {
14104     // The crc is read or skipped by the EndRead3dmChunk() call.
14105     // "extra" is the number of bytes we did not parse in the dump.
14106     ON__INT64 sizeof_crc = (0 != (TCODE_CRC & tcode)) ? 4 : 0;
14107     ON__INT64 sizeof_chunk_header = 4+file.SizeofChunkLength();
14108     ON__INT64 delta =  (offset1 > offset0)
14109               ?  ((ON__INT64)(offset1 - offset0))
14110               : -((ON__INT64)(offset0 - offset1));
14111     const ON__INT64 extra = big_value - (delta+sizeof_crc-sizeof_chunk_header);
14112     if ( extra < 0 )
14113     {
14114       Dump3dmChunk_ErrorReportHelper(offset0,"Read beyond end of chunk.",dump);
14115       rc = false;
14116     }
14117   }
14118   return rc;
14119 }
14120 
14121 static
Dump3dmChunk_UserDataHeaderHelper(size_t offset,ON_BinaryArchive & file,int major_userdata_version,int minor_userdata_version,ON_TextLog & dump)14122 bool Dump3dmChunk_UserDataHeaderHelper( size_t offset, ON_BinaryArchive& file,
14123                                  int major_userdata_version, int minor_userdata_version,
14124                                  ON_TextLog& dump )
14125 {
14126   // TCODE_OPENNURBS_CLASS_USERDATA chunks have 2 uuids
14127   // the first identifies the type of ON_Object class
14128   // the second identifies that kind of user data
14129   ON_UUID userdata_classid = ON_nil_uuid;
14130   ON_UUID userdata_itemid = ON_nil_uuid;
14131   ON_UUID userdata_appid = ON_nil_uuid;  // id of plug-in that owns the user data
14132   int userdata_copycount = -1;
14133   ON_Xform userdata_xform;
14134   bool bLastSavedAsGoo = false;
14135   int ud_archive_3dm_version = 0;
14136   int ud_archive_opennurbs_version = 0;
14137 
14138   bool rc = false;
14139   bool bCallEndRead3dmChunk = false;
14140 
14141   ON__UINT32 tcode = 0;
14142   ON__INT64 big_value = 0;
14143   const size_t offset0 = file.CurrentPosition();
14144 
14145   for(;;)
14146   {
14147     if ( 2 == major_userdata_version )
14148     {
14149       rc = file.PeekAt3dmBigChunkType(&tcode,&big_value);
14150       if ( !rc )
14151       {
14152         Dump3dmChunk_ErrorReportHelper(offset,"Unable to find the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump);
14153         break;
14154       }
14155       if ( TCODE_OPENNURBS_CLASS_USERDATA_HEADER != tcode )
14156       {
14157         Dump3dmChunk_ErrorReportHelper(offset,"Unable to find the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump);
14158         rc = false;
14159         break;
14160       }
14161       rc = file.BeginRead3dmBigChunk(&tcode,&big_value);
14162       if ( !rc )
14163       {
14164         Dump3dmChunk_ErrorReportHelper(offset,"Unable to read the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump);
14165         break;
14166       }
14167       if ( TCODE_OPENNURBS_CLASS_USERDATA_HEADER != tcode )
14168       {
14169         Dump3dmChunk_ErrorReportHelper(offset,"Missing TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump);
14170         Dump3dmChunk_EndReadChunkHelper(file,offset0,tcode,big_value,dump);
14171         rc = false;
14172         break;
14173       }
14174       bCallEndRead3dmChunk = true;
14175     }
14176 
14177     rc = file.ReadUuid( userdata_classid );
14178     if ( !rc )
14179     {
14180       Dump3dmChunk_ErrorReportHelper(offset,"ReadUuid() failed to read the user data class id.",dump);
14181       break;
14182     }
14183 
14184     dump.Print("UserData class id = ");
14185     dump.Print( userdata_classid );
14186     const ON_ClassId* pUserDataClassId = ON_ClassId::ClassId(userdata_classid);
14187     if ( pUserDataClassId )
14188     {
14189       const char* sClassName = pUserDataClassId->ClassName();
14190       if ( sClassName )
14191       {
14192         dump.Print(" (%s)",sClassName);
14193       }
14194     }
14195     dump.Print("\n");
14196 
14197     rc = file.ReadUuid( userdata_itemid );
14198     if ( !rc )
14199     {
14200        Dump3dmChunk_ErrorReportHelper(offset,"ReadUuid() failed to read the user data item id.",dump);
14201        break;
14202     }
14203     dump.Print("UserData item id = ");
14204     dump.Print( userdata_itemid );
14205     dump.Print("\n");
14206 
14207     rc = file.ReadInt( &userdata_copycount );
14208     if ( !rc )
14209     {
14210       Dump3dmChunk_ErrorReportHelper(offset,"ReadInt() failed to read the user data copy count.",dump);
14211       break;
14212     }
14213     dump.Print("UserData copy count = %d\n",userdata_copycount);
14214 
14215     rc = file.ReadXform( userdata_xform );
14216     if ( !rc )
14217     {
14218       Dump3dmChunk_ErrorReportHelper(offset,"ReadXform() failed to read the user data xform.",dump);
14219       break;
14220     }
14221 
14222     if ( 2 != major_userdata_version )
14223       break;
14224     if ( minor_userdata_version < 1 )
14225       break;
14226     rc = file.ReadUuid( userdata_appid );
14227     if ( !rc)
14228     {
14229       Dump3dmChunk_ErrorReportHelper(offset,"ReadUuid() failed to read the user data app plug-in id.",dump);
14230       break;
14231     }
14232     dump.Print("UserData app plug-in id = ");
14233     dump.Print( userdata_appid );
14234     dump.Print("\n");
14235     if ( minor_userdata_version < 2 )
14236       break;
14237     rc = file.ReadBool(&bLastSavedAsGoo);
14238     if (!rc)
14239     {
14240       Dump3dmChunk_ErrorReportHelper(offset,"ReadBool() failed to read the user data header bSavedAsGoo value.",dump);
14241       break;
14242     }
14243     rc = file.ReadInt( &ud_archive_3dm_version );
14244     if (!rc)
14245     {
14246       Dump3dmChunk_ErrorReportHelper(offset,"ReadBool() failed to read the user data header bSavedAsGoo value.",dump);
14247       break;
14248     }
14249     rc = file.ReadInt( &ud_archive_opennurbs_version );
14250     if (!rc)
14251     {
14252       Dump3dmChunk_ErrorReportHelper(offset,"ReadBool() failed to read the user data header bSavedAsGoo value.",dump);
14253       break;
14254     }
14255     if ( bLastSavedAsGoo )
14256       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);
14257     else
14258       dump.Print("Userdata written by opennurbs %d in 3dm version %d.\n",ud_archive_opennurbs_version,ud_archive_3dm_version);
14259 
14260     break;
14261   }
14262 
14263   if ( bCallEndRead3dmChunk )
14264   {
14265     if (!Dump3dmChunk_EndReadChunkHelper(file,offset0,tcode,big_value,dump))
14266     {
14267       if (rc)
14268       {
14269         Dump3dmChunk_ErrorReportHelper(offset,"EndRead3dmChunk() failed to close the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk.",dump);
14270       }
14271       rc = false;
14272     }
14273   }
14274 
14275   return rc;
14276 }
14277 
14278 
14279 unsigned int
Dump3dmChunk(ON_TextLog & dump,int recursion_depth)14280 ON_BinaryArchive::Dump3dmChunk( ON_TextLog& dump, int recursion_depth )
14281 {
14282   //ON_BinaryArchive& file = *this;
14283   const char* typecode_name = 0;
14284   bool bShortChunk = false;
14285   const size_t offset0 = CurrentPosition();
14286   unsigned int typecode = 0;
14287   ON__INT64 big_value;
14288   bool rc = BeginRead3dmBigChunk( &typecode, &big_value );
14289   if (!rc)
14290   {
14291     Dump3dmChunk_ErrorReportHelper(offset0,"BeginRead3dmChunk() failed.",dump);
14292   }
14293   else
14294   {
14295     if ( 0 == typecode )
14296     {
14297       Dump3dmChunk_ErrorReportHelper(offset0,"BeginRead3dmChunk() returned typecode = 0.",dump);
14298       EndRead3dmChunk();
14299       return 0;
14300     }
14301     else {
14302       if ( 0 == recursion_depth )
14303       {
14304         dump.Print("\n");
14305       }
14306 
14307       ////bShortChunk = (0 != (typecode & TCODE_SHORT));
14308       typecode_name = ON_BinaryArchive::TypecodeName(typecode);
14309       bShortChunk = (0 != (typecode & TCODE_SHORT));
14310       if ( !DumpChunk_PrintHeaderInfo(offset0,typecode,big_value,typecode_name,dump) )
14311       {
14312         EndRead3dmChunk();
14313         return 0;
14314       }
14315 
14316       int major_userdata_version = -1;
14317       int minor_userdata_version = -1;
14318 
14319       switch( typecode )
14320       {
14321       case TCODE_PROPERTIES_TABLE:
14322       case TCODE_SETTINGS_TABLE:
14323       case TCODE_BITMAP_TABLE:
14324       case TCODE_MATERIAL_TABLE:
14325       case TCODE_LAYER_TABLE:
14326       case TCODE_GROUP_TABLE:
14327       case TCODE_LIGHT_TABLE:
14328       case TCODE_FONT_TABLE:
14329       case TCODE_DIMSTYLE_TABLE:
14330       case TCODE_HATCHPATTERN_TABLE:
14331       case TCODE_LINETYPE_TABLE:
14332       case TCODE_TEXTURE_MAPPING_TABLE:
14333       case TCODE_HISTORYRECORD_TABLE:
14334       case TCODE_USER_TABLE:
14335       case TCODE_INSTANCE_DEFINITION_TABLE:
14336       case TCODE_OBJECT_TABLE:
14337         // start of a table
14338         {
14339           dump.PushIndent();
14340           unsigned int record_typecode = 0;
14341           for (;;) {
14342             record_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14343             if ( !record_typecode ) {
14344               break;
14345             }
14346             if ( TCODE_ENDOFTABLE == record_typecode ) {
14347               break;
14348             }
14349           }
14350           dump.PopIndent();
14351         }
14352         break;
14353 
14354       case TCODE_PROPERTIES_OPENNURBS_VERSION:
14355         {
14356           dump.PushIndent();
14357           dump.Print("Version of opennurbs that wrote this file: %lld\n",big_value);
14358           dump.PopIndent();
14359           if ( 0 == m_3dm_opennurbs_version && big_value > 0 && big_value <= 299999999 )
14360             ON_SetBinaryArchiveOpenNURBSVersion(*this,(ON__INT32)big_value);
14361         }
14362         break;
14363 
14364       case TCODE_BITMAP_RECORD:
14365         {
14366           dump.PushIndent();
14367           unsigned int bitmap_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14368           if ( 0 == typecode )
14369             typecode = bitmap_chunk_typecode;
14370           dump.PopIndent();
14371         }
14372         break;
14373 
14374       case TCODE_MATERIAL_RECORD:
14375         {
14376           dump.PushIndent();
14377           unsigned int material_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14378           if ( 0 == typecode )
14379             typecode = material_chunk_typecode;
14380           dump.PopIndent();
14381         }
14382         break;
14383 
14384       case TCODE_LAYER_RECORD:
14385         {
14386           dump.PushIndent();
14387           unsigned int material_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14388           if ( 0 == typecode )
14389             typecode = material_chunk_typecode;
14390           dump.PopIndent();
14391         }
14392         break;
14393 
14394       case TCODE_GROUP_RECORD:
14395         {
14396           dump.PushIndent();
14397           unsigned int group_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14398           if ( 0 == typecode )
14399             typecode = group_chunk_typecode;
14400           dump.PopIndent();
14401         }
14402         break;
14403 
14404       case TCODE_FONT_RECORD:
14405         {
14406           dump.PushIndent();
14407           unsigned int font_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14408           if ( 0 == typecode )
14409             typecode = font_chunk_typecode;
14410           dump.PopIndent();
14411         }
14412         break;
14413 
14414       case TCODE_DIMSTYLE_RECORD:
14415         {
14416           dump.PushIndent();
14417           unsigned int dimstyle_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14418           if ( 0 == typecode )
14419             typecode = dimstyle_chunk_typecode;
14420           dump.PopIndent();
14421         }
14422         break;
14423 
14424       case TCODE_LIGHT_RECORD:
14425         {
14426           dump.PushIndent();
14427           unsigned int light_chunk_typecode = 0;
14428           for (;;) {
14429             light_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14430             if ( !light_chunk_typecode ) {
14431               break;
14432             }
14433             if ( TCODE_LIGHT_RECORD_END == light_chunk_typecode ) {
14434               break;
14435             }
14436             switch( light_chunk_typecode ) {
14437             //case TCODE_OBJECT_RECORD_TYPE:
14438             case TCODE_LIGHT_RECORD_ATTRIBUTES:
14439             case TCODE_LIGHT_RECORD_ATTRIBUTES_USERDATA:
14440             case TCODE_OPENNURBS_CLASS:
14441               break;
14442             default:
14443               {
14444                 Dump3dmChunk_ErrorReportHelper(offset0,"Rogue chunk in light record.",dump);
14445               }
14446             }
14447           }
14448           dump.PopIndent();
14449         }
14450         break;
14451 
14452       case TCODE_TEXTURE_MAPPING_RECORD:
14453         {
14454           dump.PushIndent();
14455           unsigned int mapping_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14456           if ( !typecode )
14457             typecode = mapping_chunk_typecode;
14458           dump.PopIndent();
14459         }
14460         break;
14461 
14462       case TCODE_HISTORYRECORD_RECORD:
14463         {
14464           dump.PushIndent();
14465           unsigned int history_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14466           if ( !typecode )
14467             typecode = history_chunk_typecode;
14468           dump.PopIndent();
14469         }
14470         break;
14471 
14472       case TCODE_HATCHPATTERN_RECORD:
14473         {
14474           dump.PushIndent();
14475           unsigned int hatch_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14476           if ( !typecode )
14477             typecode = hatch_chunk_typecode;
14478           dump.PopIndent();
14479         }
14480         break;
14481 
14482       case TCODE_INSTANCE_DEFINITION_RECORD:
14483         {
14484           dump.PushIndent();
14485           unsigned int idef_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14486           if ( 0 == typecode )
14487             typecode = idef_chunk_typecode;
14488           dump.PopIndent();
14489         }
14490         break;
14491 
14492       case TCODE_OBJECT_RECORD:
14493         {
14494           dump.PushIndent();
14495           unsigned int object_chunk_typecode = 0;
14496           for (;;) {
14497             object_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 );
14498             if ( !object_chunk_typecode ) {
14499               break;
14500             }
14501             if ( TCODE_OBJECT_RECORD_END == object_chunk_typecode ) {
14502               break;
14503             }
14504             switch( object_chunk_typecode ) {
14505             case TCODE_OBJECT_RECORD_TYPE:
14506             case TCODE_OBJECT_RECORD_ATTRIBUTES:
14507             case TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA:
14508             case TCODE_OPENNURBS_CLASS:
14509               break;
14510             default:
14511               {
14512                 Dump3dmChunk_ErrorReportHelper(offset0,"Rogue chunk in object record.",dump);
14513               }
14514             }
14515           }
14516           dump.PopIndent();
14517         }
14518         break;
14519 
14520       case TCODE_OBJECT_RECORD_ATTRIBUTES:
14521         {
14522           dump.PushIndent();
14523           if ( big_value < 14 )
14524           {
14525             Dump3dmChunk_ErrorReportHelper(offset0,"Length of chunk is too small.  Should be >= 14.",dump);
14526           }
14527           else
14528           {
14529             ON_UUID uuid = ON_nil_uuid;
14530             int layer_index = -99;
14531             int mj = -1;
14532             int mn = -1;
14533             if ( !Read3dmChunkVersion(&mj,&mn))
14534             {
14535               Dump3dmChunk_ErrorReportHelper(offset0,"Read3dmChunkVersion() failed.",dump);
14536             }
14537             else if (!ReadUuid(uuid))
14538             {
14539               Dump3dmChunk_ErrorReportHelper(offset0,"ReadUuid() failed.",dump);
14540             }
14541             else if ( !ReadInt(&layer_index) )
14542             {
14543               Dump3dmChunk_ErrorReportHelper(offset0,"ReadInt() failed to read layer index.",dump);
14544             }
14545             else
14546             {
14547               dump.Print("Rhino object uuid: ");
14548               dump.Print(uuid);
14549               dump.Print("\n");
14550               dump.Print("layer index: %d\n",layer_index);
14551             }
14552           }
14553           dump.PopIndent();
14554         }
14555         break;
14556 
14557       case TCODE_OPENNURBS_CLASS:
14558         {
14559           dump.PushIndent();
14560           unsigned int opennurbs_object_chunk_typecode = 0;
14561           for (;;) {
14562             opennurbs_object_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1  );
14563             if ( !opennurbs_object_chunk_typecode ) {
14564               break;
14565             }
14566             if ( TCODE_OPENNURBS_CLASS_END == opennurbs_object_chunk_typecode ) {
14567               break;
14568             }
14569             switch( opennurbs_object_chunk_typecode )
14570             {
14571             case TCODE_OPENNURBS_CLASS_UUID:
14572               break;
14573             case TCODE_OPENNURBS_CLASS_DATA:
14574               break;
14575             case TCODE_OPENNURBS_CLASS_USERDATA:
14576               break;
14577             default:
14578               {
14579                 Dump3dmChunk_ErrorReportHelper(offset0,"Rogue chunk in OpenNURBS class record.",dump);
14580               }
14581             }
14582           }
14583           dump.PopIndent();
14584         }
14585         break;
14586 
14587       case TCODE_OPENNURBS_CLASS_USERDATA:
14588         {
14589           if ( !Read3dmChunkVersion(&major_userdata_version, &minor_userdata_version ) )
14590           {
14591             Dump3dmChunk_ErrorReportHelper(offset0,"Read3dmChunkVersion() failed to read TCODE_OPENNURBS_CLASS_USERDATA chunk version.",dump);
14592           }
14593           else
14594           {
14595             dump.PushIndent();
14596             dump.Print("UserData chunk version: %d.%d\n",
14597                        major_userdata_version,
14598                        minor_userdata_version
14599                        );
14600             if ( 1 == major_userdata_version || 2 == major_userdata_version )
14601             {
14602               const size_t userdata_header_offset = CurrentPosition();
14603               switch ( major_userdata_version )
14604               {
14605               case 1:
14606               case 2:
14607                 {
14608                   // version 1 user data header information was not wrapped
14609                   // in a chunk.
14610                   //
14611                   // version 2 user data header information is wrapped
14612                   // in a TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk.
14613                   if ( Dump3dmChunk_UserDataHeaderHelper(
14614                                   userdata_header_offset, *this,
14615                                   major_userdata_version, minor_userdata_version,
14616                                   dump )
14617                      )
14618                   {
14619                     // a TCODE_ANONYMOUS_CHUNK contains user data goo
14620                     int anon_typecode =  Dump3dmChunk( dump, recursion_depth+1 );
14621                     if ( TCODE_ANONYMOUS_CHUNK != anon_typecode )
14622                     {
14623                       Dump3dmChunk_ErrorReportHelper( offset0,"Userdata Expected a TCODE_ANONYMOUS_CHUNK chunk.",dump);
14624                     }
14625                   }
14626                 }
14627                 break;
14628               default:
14629                 if ( major_userdata_version < 3 )
14630                 {
14631                 }
14632                 else
14633                 {
14634                   dump.Print("New user data format created after this diagnostic tool was written.\n");
14635                 }
14636                 break;
14637               }
14638             }
14639 
14640             dump.PopIndent();
14641           }
14642         }
14643         break;
14644 
14645       case TCODE_OPENNURBS_CLASS_UUID:
14646       case TCODE_USER_TABLE_UUID:
14647         {
14648           dump.PushIndent();
14649           ON_UUID uuid = ON_nil_uuid;
14650           const ON_ClassId* pClassId = 0;
14651           if ( !ReadUuid( uuid ) ) {
14652              Dump3dmChunk_ErrorReportHelper(offset0,"ReadUuid() failed.",dump);
14653           }
14654           else
14655           {
14656             if ( typecode == TCODE_OPENNURBS_CLASS_UUID )
14657             {
14658               dump.Print("OpenNURBS class id = ");
14659               pClassId = ON_ClassId::ClassId(uuid);
14660             }
14661             else if ( typecode == TCODE_USER_TABLE_UUID )
14662             {
14663               dump.Print("User table id = ");
14664             }
14665             else {
14666               dump.Print("UUID = ");
14667             }
14668             dump.Print( uuid );
14669             if ( pClassId )
14670             {
14671               const char* sClassName = pClassId->ClassName();
14672               if ( sClassName )
14673               {
14674                 dump.Print(" (%s)",sClassName);
14675               }
14676             }
14677             dump.Print("\n");
14678           }
14679 
14680           dump.PopIndent();
14681         }
14682         break;
14683 
14684       case TCODE_OPENNURBS_CLASS_USERDATA_HEADER:
14685         {
14686           // we should never get here because this chunk is parsed in the TCODE_OPENNURBS_CLASS_USERDATA case above.
14687           Dump3dmChunk_ErrorReportHelper(offset0,"Encountered TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk outside a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump);
14688         }
14689         break;
14690 
14691       case TCODE_ENDOFFILE:
14692       case TCODE_ENDOFFILE_GOO:
14693         {
14694           dump.PushIndent();
14695           if ( big_value < 4 ) {
14696             Dump3dmChunk_ErrorReportHelper(offset0,"TCODE_ENDOFFILE chunk withlength < 4.",dump);
14697           }
14698           else {
14699             ON__UINT64 sizeof_file = 0;
14700             ReadEOFSizeOfFile(&sizeof_file);
14701             dump.Print("current position = %d  stored size = %llu\n",
14702                        CurrentPosition(),
14703                        sizeof_file
14704                        );
14705           }
14706           dump.PopIndent();
14707         }
14708         break;
14709 
14710       }
14711     }
14712 
14713     const size_t offset1 = CurrentPosition();
14714     if ( !EndRead3dmChunk(true) )
14715     {
14716       Dump3dmChunk_ErrorReportHelper(offset1,"EndRead3dmChunk() failed.",dump);
14717       rc = false;
14718     }
14719     else if (!bShortChunk)
14720     {
14721       ON__INT64 delta =  (offset1 > offset0)
14722                 ?  ((ON__INT64)(offset1 - offset0))
14723                 : -((ON__INT64)(offset0 - offset1));
14724       const ON__INT64 extra = big_value - (delta-4-SizeofChunkLength());
14725       if ( extra < 0 )
14726       {
14727         Dump3dmChunk_ErrorReportHelper(offset0,"Read beyond end of chunk.",dump);
14728       }
14729     }
14730   }
14731   return typecode;
14732 }
14733 
14734 
14735 
ON_Read3dmBufferArchive(size_t sizeof_buffer,const void * buffer,bool bCopyBuffer,int archive_3dm_version,int archive_opennurbs_version)14736 ON_Read3dmBufferArchive::ON_Read3dmBufferArchive(
14737           size_t sizeof_buffer,
14738           const void* buffer,
14739           bool bCopyBuffer,
14740           int archive_3dm_version,
14741           int archive_opennurbs_version
14742           )
14743 : ON_BinaryArchive(ON::read3dm)
14744 , m_p(0)
14745 , m_buffer(0)
14746 , m_sizeof_buffer(0)
14747 , m_buffer_position(0)
14748 , m_reserved1(0)
14749 , m_reserved2(0)
14750 , m_reserved3(0)
14751 , m_reserved4(0)
14752 {
14753   if ( sizeof_buffer > 0 && 0 != buffer )
14754   {
14755     if ( bCopyBuffer )
14756     {
14757       m_p = onmalloc(sizeof_buffer);
14758       if ( 0 != m_p )
14759         memcpy(m_p,buffer,sizeof_buffer);
14760       m_buffer = (const unsigned char*)m_p;
14761     }
14762     else
14763     {
14764       m_buffer = (const unsigned char*)buffer;
14765     }
14766     if ( m_buffer )
14767     {
14768       m_sizeof_buffer = sizeof_buffer;
14769       SetArchive3dmVersion(archive_3dm_version);
14770       ON_SetBinaryArchiveOpenNURBSVersion(*this,archive_opennurbs_version);
14771     }
14772   }
14773 }
14774 
~ON_Read3dmBufferArchive()14775 ON_Read3dmBufferArchive::~ON_Read3dmBufferArchive()
14776 {
14777   if ( m_p )
14778     onfree(m_p);
14779 }
14780 
14781 // ON_BinaryArchive overrides
CurrentPosition() const14782 size_t ON_Read3dmBufferArchive::CurrentPosition() const
14783 {
14784   return m_buffer_position;
14785 }
14786 
SeekFromCurrentPosition(int offset)14787 bool ON_Read3dmBufferArchive::SeekFromCurrentPosition( int offset )
14788 {
14789   bool rc = false;
14790   if ( m_buffer )
14791   {
14792     if (offset >= 0 )
14793     {
14794       m_buffer_position += offset;
14795       rc = true;
14796     }
14797     else if ( size_t(-offset) <= m_buffer_position )
14798     {
14799       m_buffer_position -= (size_t(-offset));
14800       rc = true;
14801     }
14802   }
14803   return rc;
14804 }
14805 
SeekFromStart(size_t offset)14806 bool ON_Read3dmBufferArchive::SeekFromStart( size_t offset )
14807 {
14808   bool rc = false;
14809   if ( m_buffer )
14810   {
14811     if ( offset > 0 )
14812       m_buffer_position = offset;
14813     else
14814       m_buffer_position = 0;
14815     rc = true;
14816   }
14817   return rc;
14818 }
14819 
AtEnd() const14820 bool ON_Read3dmBufferArchive::AtEnd() const
14821 {
14822   return (m_buffer_position >= m_sizeof_buffer) ? true : false;
14823 }
14824 
Read(size_t count,void * buffer)14825 size_t ON_Read3dmBufferArchive::Read( size_t count, void* buffer )
14826 {
14827   if ( count <= 0 || 0 == buffer )
14828     return 0;
14829 
14830   size_t maxcount = ( m_sizeof_buffer > m_buffer_position )
14831                   ? (m_sizeof_buffer - m_buffer_position)
14832                   : 0;
14833   if ( count > maxcount )
14834     count = maxcount;
14835 
14836   if ( count > 0 )
14837   {
14838     memcpy( buffer, m_buffer+m_buffer_position, count );
14839     m_buffer_position += count;
14840   }
14841 
14842   return count;
14843 }
14844 
Write(size_t,const void *)14845 size_t ON_Read3dmBufferArchive::Write( size_t, const void* )
14846 {
14847   // ON_Read3dmBufferArchive does not support Write() and Flush()
14848   return 0;
14849 }
14850 
Flush()14851 bool ON_Read3dmBufferArchive::Flush()
14852 {
14853   // ON_Read3dmBufferArchive does not support Write() and Flush()
14854   return false;
14855 }
14856 
14857 
SizeOfBuffer() const14858 size_t ON_Read3dmBufferArchive::SizeOfBuffer() const
14859 {
14860   return m_sizeof_buffer;
14861 }
14862 
Buffer() const14863 const void* ON_Read3dmBufferArchive::Buffer() const
14864 {
14865   return (const void*)m_buffer;
14866 }
14867 
14868 
14869 
14870 
14871 
ON_Write3dmBufferArchive(size_t initial_sizeof_buffer,size_t max_sizeof_buffer,int archive_3dm_version,int archive_opennurbs_version)14872 ON_Write3dmBufferArchive::ON_Write3dmBufferArchive(
14873           size_t initial_sizeof_buffer,
14874           size_t max_sizeof_buffer,
14875           int archive_3dm_version,
14876           int archive_opennurbs_version
14877           )
14878 : ON_BinaryArchive(ON::write3dm)
14879 , m_p(0)
14880 , m_buffer(0)
14881 , m_sizeof_buffer(0)
14882 , m_max_sizeof_buffer(max_sizeof_buffer)
14883 , m_sizeof_archive(0)
14884 , m_buffer_position(0)
14885 , m_reserved1(0)
14886 , m_reserved2(0)
14887 , m_reserved3(0)
14888 , m_reserved4(0)
14889 {
14890   if ( initial_sizeof_buffer > 0 )
14891     AllocBuffer(initial_sizeof_buffer);
14892   if ( archive_3dm_version < 2 )
14893     archive_3dm_version = ON_BinaryArchive::CurrentArchiveVersion();
14894   SetArchive3dmVersion(archive_3dm_version);
14895   ON_SetBinaryArchiveOpenNURBSVersion(*this,archive_opennurbs_version);
14896 }
14897 
~ON_Write3dmBufferArchive()14898 ON_Write3dmBufferArchive::~ON_Write3dmBufferArchive()
14899 {
14900   if ( m_p )
14901     onfree(m_p);
14902 }
14903 
AllocBuffer(size_t sz)14904 void ON_Write3dmBufferArchive::AllocBuffer( size_t sz )
14905 {
14906   if ( sz > m_sizeof_buffer
14907        && (m_max_sizeof_buffer <= 0 || sz <= m_max_sizeof_buffer)
14908      )
14909   {
14910     if ( sz < 2*m_sizeof_buffer )
14911     {
14912       sz = 2*m_sizeof_buffer;
14913       if ( sz > m_max_sizeof_buffer )
14914         sz = m_max_sizeof_buffer;
14915     }
14916 
14917     m_p = onrealloc(m_p,sz);
14918     m_buffer = (unsigned char*)m_p;
14919 
14920     if ( 0 != m_buffer )
14921     {
14922       memset( m_buffer + m_sizeof_buffer, 0, sz - m_sizeof_buffer );
14923       m_sizeof_buffer = sz;
14924     }
14925     else
14926     {
14927       m_sizeof_buffer = 0;
14928     }
14929 
14930   }
14931 }
14932 
14933 // ON_BinaryArchive overrides
CurrentPosition() const14934 size_t ON_Write3dmBufferArchive::CurrentPosition() const
14935 {
14936   return m_buffer_position;
14937 }
14938 
SeekFromCurrentPosition(int offset)14939 bool ON_Write3dmBufferArchive::SeekFromCurrentPosition( int offset )
14940 {
14941   bool rc = false;
14942   if ( m_buffer )
14943   {
14944     if (offset >= 0 )
14945     {
14946       m_buffer_position += offset;
14947       rc = true;
14948     }
14949     else if ( size_t(-offset) <= m_buffer_position )
14950     {
14951       m_buffer_position -= (size_t(-offset));
14952       rc = true;
14953     }
14954   }
14955   return rc;
14956 }
14957 
SeekFromStart(size_t offset)14958 bool ON_Write3dmBufferArchive::SeekFromStart( size_t offset )
14959 {
14960   bool rc = false;
14961   if ( m_buffer )
14962   {
14963     if ( offset > 0 )
14964       m_buffer_position = offset;
14965     else
14966       m_buffer_position = 0;
14967     rc = true;
14968   }
14969   return rc;
14970 }
14971 
AtEnd() const14972 bool ON_Write3dmBufferArchive::AtEnd() const
14973 {
14974   return (m_buffer_position >= m_sizeof_buffer) ? true : false;
14975 }
14976 
Read(size_t count,void * buffer)14977 size_t ON_Write3dmBufferArchive::Read( size_t count, void* buffer )
14978 {
14979   if ( count <= 0 || 0 == buffer )
14980     return 0;
14981 
14982   size_t maxcount = ( m_sizeof_buffer > m_buffer_position )
14983                   ? (m_sizeof_buffer - m_buffer_position)
14984                   : 0;
14985   if ( count > maxcount )
14986     count = maxcount;
14987 
14988   if ( count > 0 )
14989   {
14990     memcpy( buffer, m_buffer+m_buffer_position, count );
14991     m_buffer_position += count;
14992   }
14993 
14994   return count;
14995 }
14996 
Write(size_t sz,const void * buffer)14997 size_t ON_Write3dmBufferArchive::Write( size_t sz, const void* buffer )
14998 {
14999   if ( sz <= 0 || 0 == buffer )
15000     return 0;
15001 
15002   if ( m_buffer_position + sz > m_sizeof_buffer )
15003   {
15004     AllocBuffer(m_buffer_position + sz);
15005   }
15006 
15007   if ( m_buffer_position + sz > m_sizeof_buffer )
15008     return 0;
15009 
15010   memcpy( m_buffer + m_buffer_position, buffer, sz );
15011   m_buffer_position += sz;
15012   if ( m_buffer_position > m_sizeof_archive )
15013     m_sizeof_archive = m_buffer_position;
15014 
15015   return sz;
15016 }
15017 
Flush()15018 bool ON_Write3dmBufferArchive::Flush()
15019 {
15020   // Nothing to flush
15021   return true;
15022 }
15023 
15024 
SizeOfBuffer() const15025 size_t ON_Write3dmBufferArchive::SizeOfBuffer() const
15026 {
15027   return m_sizeof_buffer;
15028 }
15029 
Buffer() const15030 const void* ON_Write3dmBufferArchive::Buffer() const
15031 {
15032   return (const void*)m_buffer;
15033 }
15034 
HarvestBuffer()15035 void* ON_Write3dmBufferArchive::HarvestBuffer()
15036 {
15037   void* buffer = m_buffer;
15038 
15039   m_p = 0;
15040   m_buffer = 0;
15041   m_sizeof_buffer = 0;
15042   m_sizeof_archive = 0;
15043   m_buffer_position = 0;
15044 
15045   return buffer;
15046 }
15047 
SizeOfArchive() const15048 size_t ON_Write3dmBufferArchive::SizeOfArchive() const
15049 {
15050   return m_sizeof_archive;
15051 }
15052 
15053 
15054 
15055 
ON_BinaryArchiveBuffer(ON::archive_mode mode,ON_Buffer * buffer)15056 ON_BinaryArchiveBuffer::ON_BinaryArchiveBuffer( ON::archive_mode mode, ON_Buffer* buffer )
15057 : ON_BinaryArchive(mode)
15058 , m_buffer(buffer)
15059 {
15060 }
15061 
~ON_BinaryArchiveBuffer()15062 ON_BinaryArchiveBuffer::~ON_BinaryArchiveBuffer()
15063 {
15064   m_buffer = 0;
15065 }
15066 
SetBuffer(ON_Buffer * buffer)15067 bool ON_BinaryArchiveBuffer::SetBuffer( ON_Buffer* buffer )
15068 {
15069   if ( 0 == m_buffer )
15070   {
15071     m_buffer = buffer;
15072     return true;
15073   }
15074 
15075   return false;
15076 }
15077 
Buffer() const15078 ON_Buffer* ON_BinaryArchiveBuffer::Buffer() const
15079 {
15080   return m_buffer;
15081 }
15082 
CurrentPosition() const15083 size_t ON_BinaryArchiveBuffer::CurrentPosition() const
15084 {
15085   if ( 0 != m_buffer )
15086     return (size_t)m_buffer->CurrentPosition();
15087 
15088   return 0;
15089 }
15090 
SeekFromCurrentPosition(int offset)15091 bool ON_BinaryArchiveBuffer::SeekFromCurrentPosition(int offset)
15092 {
15093   if ( 0 != m_buffer )
15094     return m_buffer->SeekFromCurrentPosition(offset);
15095 
15096   return false;
15097 }
15098 
SeekFromStart(size_t offset)15099 bool ON_BinaryArchiveBuffer::SeekFromStart(size_t offset)
15100 {
15101   if ( 0 != m_buffer )
15102     return m_buffer->SeekFromStart((ON__INT64)offset);
15103 
15104   return false;
15105 }
15106 
AtEnd() const15107 bool ON_BinaryArchiveBuffer::AtEnd() const
15108 {
15109   if ( 0 != m_buffer )
15110     return m_buffer->AtEnd();
15111 
15112   return false;
15113 }
15114 
SeekFromEnd(ON__INT64 offset)15115 bool ON_BinaryArchiveBuffer::SeekFromEnd( ON__INT64 offset )
15116 {
15117   if ( 0 != m_buffer )
15118     return m_buffer->SeekFromEnd(offset);
15119 
15120   return false;
15121 }
15122 
Read(size_t count,void * a)15123 size_t ON_BinaryArchiveBuffer::Read( size_t count, void* a )
15124 {
15125   if ( 0 != m_buffer )
15126     return (size_t)m_buffer->Read(count,a);
15127 
15128   return 0;
15129 }
15130 
Write(size_t count,const void * a)15131 size_t ON_BinaryArchiveBuffer::Write( size_t count, const void* a )
15132 {
15133   if ( 0 != m_buffer )
15134     return (size_t)m_buffer->Write(count,a);
15135 
15136   return 0;
15137 }
15138 
Flush()15139 bool ON_BinaryArchiveBuffer::Flush()
15140 {
15141   if ( 0 != m_buffer )
15142     return true;
15143 
15144   return false;
15145 }
15146 
ON_FileIterator()15147 ON_FileIterator::ON_FileIterator()
15148 : m_count(0)
15149 #if defined(ON_COMPILER_MSC)
15150 , m_file_attributes_mask(0)
15151 , m_h(0)
15152 #else
15153 , m_dir(0)
15154 , m_current_file_attributes(0)
15155 , m_current_file_size(0)
15156 , m_current_file_create_time(0)
15157 , m_current_last_modified_time(0)
15158 , m_current_last_access_time(0)
15159 #endif
15160 {
15161   Destroy();
15162 #if defined(ON_COMPILER_MSC)
15163   memset(&m_fd,0,sizeof(m_fd));
15164 #else
15165   memset(&m_dirent,0,sizeof(m_dirent));
15166   m_dirent.d_name[0] = 0;
15167   memset(&m_dirent_name_buffer[0],0,sizeof(m_dirent_name_buffer));
15168   memset(&m_current_name[0],0,sizeof(m_current_name));
15169 #endif
15170 }
15171 
~ON_FileIterator()15172 ON_FileIterator::~ON_FileIterator()
15173 {
15174   Destroy();
15175 }
15176 
Destroy()15177 void ON_FileIterator::Destroy()
15178 {
15179 #if defined(ON_COMPILER_MSC)
15180   if ( 0 != m_h )
15181   {
15182     ::FindClose(m_h);
15183     m_h = 0;
15184   }
15185   m_file_attributes_mask = 0;
15186   memset(&m_fd,0,sizeof(m_fd));
15187 #else
15188   if ( 0 != m_dir )
15189   {
15190     closedir(m_dir);
15191     m_dir = 0;
15192   }
15193   memset(&m_dirent,0,sizeof(m_dirent));
15194   m_dirent.d_name[0] = 0;
15195   memset(&m_dirent_name_buffer[0],0,sizeof(m_dirent_name_buffer));
15196   m_ws_file_name_filter.Destroy();
15197   m_utf8_file_name_filter.Destroy();
15198   memset(&m_current_name[0],0,sizeof(m_current_name));
15199   m_current_file_attributes = 0;
15200   m_current_file_size = 0;
15201   m_current_file_create_time = 0;
15202   m_current_last_modified_time = 0;
15203   m_current_last_access_time = 0;
15204 #endif
15205   m_count = 0;
15206   m_directory.Empty();
15207 }
15208 
Count() const15209 ON__UINT64 ON_FileIterator::Count() const
15210 {
15211   return m_count;
15212 }
15213 
15214 #if defined(ON_COMPILER_MSC)
IsDotOrDotDotDir(const wchar_t * s)15215 static bool IsDotOrDotDotDir( const wchar_t* s )
15216 #else
15217 static bool IsDotOrDotDotDir( const char* s )
15218 #endif
15219 {
15220   bool rc = false;
15221   for (;;)
15222   {
15223     if ( 0 == s )
15224       break;
15225     if ( '.' != s[0] )
15226       break;
15227     if ( 0 != s[1] )
15228     {
15229       if ( '.' != s[1] )
15230         break;
15231       if ( 0 != s[2] )
15232         break;
15233     }
15234     rc = true; // s = "." or s = ".."
15235     break;
15236   }
15237   return rc;
15238 }
15239 
FirstFile(const char * directory_name,const char * file_name_filter)15240 const wchar_t* ON_FileIterator::FirstFile(
15241     const char* directory_name,
15242     const char* file_name_filter
15243     )
15244 {
15245   // assume directory_name and file_name_filter are UTF-8 encoded
15246   // strings, convert them to wchar_t strings and call the
15247   // wchar_t version of this function.
15248   ON_wString ws_directory_name = directory_name;
15249   ON_wString ws_file_name_filter = file_name_filter;
15250   const wchar_t* wchar_directory_name = ws_directory_name;
15251   const wchar_t* wchar_file_name_filter = ws_file_name_filter;
15252   return FirstFile(wchar_directory_name,wchar_file_name_filter);
15253 }
15254 
FirstFile(const wchar_t * directory_name,const wchar_t * file_name_filter)15255 const wchar_t* ON_FileIterator::FirstFile(
15256     const wchar_t* directory_name,
15257     const wchar_t* file_name_filter
15258     )
15259 {
15260   ON_wString buffer(directory_name);
15261   {
15262     const wchar_t* dir_seps = L"/\\";
15263     buffer.TrimRight(dir_seps);
15264     if ( buffer.Length() <= 0 || buffer.IsEmpty() )
15265       buffer = directory_name;
15266     else
15267       directory_name = buffer;
15268   }
15269 
15270 #if defined(ON_COMPILER_MSC)
15271   const ON__UINT32 saved_mask = m_file_attributes_mask;
15272   Destroy();
15273   m_file_attributes_mask = saved_mask;
15274 
15275   ON_wString s(directory_name);
15276 
15277 
15278   if ( 0 == file_name_filter )
15279   {
15280     // A null file file_name_filter means iterate
15281     // through all items in the directory.  To do
15282     // this using Windows' ::FindFirstFile, set the
15283     // filter to "*.*", even though some items will
15284     // not contain a "dot".
15285     file_name_filter = L"*.*";
15286   }
15287 
15288   if ( 0 != file_name_filter[0] )
15289   {
15290     s += L"\\";
15291     s += file_name_filter;
15292   }
15293 
15294   m_h = ::FindFirstFile(s, &m_fd);
15295   if ( 0 == m_h || INVALID_HANDLE_VALUE == m_h || 0 == m_fd.cFileName[0] )
15296   {
15297     // Happens on "fake" directories like "My Music" and "My Videos"
15298     m_h = 0;
15299     Destroy();
15300     m_file_attributes_mask = saved_mask;
15301     return 0;
15302   }
15303 
15304   m_directory = directory_name;
15305 
15306   if ( IsDotOrDotDotDir(m_fd.cFileName) || 0 != (m_file_attributes_mask & m_fd.dwFileAttributes) )
15307   {
15308     return NextFile();
15309   }
15310 
15311   m_count++;
15312   m_fd.cFileName[(sizeof(m_fd.cFileName)/sizeof(m_fd.cFileName[0]))-1] = 0;
15313   return m_fd.cFileName;
15314 
15315 #else
15316 
15317   // gcc code
15318   Destroy();
15319   m_directory = directory_name;
15320   m_ws_file_name_filter = file_name_filter;
15321   m_utf8_file_name_filter = file_name_filter;
15322   const ON_String utf8_str(m_directory); // convert wchar_t to utf8 string
15323   const char* s = utf8_str;
15324   m_dir = (0 != s && 0 != s[0]) ? opendir(s) : 0;
15325   if ( 0 != m_dir )
15326     return NextFile();
15327   Destroy();
15328   return 0;
15329 
15330 #endif
15331 }
15332 
NextFile()15333 const wchar_t* ON_FileIterator::NextFile()
15334 {
15335 #if defined(ON_COMPILER_MSC)
15336   const ON__UINT32 saved_mask = m_file_attributes_mask;
15337   if ( 0 == m_h || INVALID_HANDLE_VALUE == m_h || 0 == m_fd.cFileName[0] )
15338   {
15339     Destroy();
15340     m_file_attributes_mask = saved_mask;
15341     return 0;
15342   }
15343 
15344   for (;;)
15345   {
15346     if ( !::FindNextFile( m_h, &m_fd) || 0 == m_fd.cFileName[0] )
15347     {
15348       Destroy();
15349       m_file_attributes_mask = saved_mask;
15350       return 0;
15351     }
15352 
15353     if ( IsDotOrDotDotDir(m_fd.cFileName) || 0 != (m_file_attributes_mask & m_fd.dwFileAttributes) )
15354     {
15355       continue;
15356     }
15357 
15358     break;
15359   }
15360 
15361   m_count++;
15362   m_fd.cFileName[(sizeof(m_fd.cFileName)/sizeof(m_fd.cFileName[0]))-1] = 0;
15363   return m_fd.cFileName;
15364 #else
15365 
15366   // gcc code
15367   ON__UINT64 current_file_attributes = 0;
15368   wchar_t current_name[ sizeof(m_current_name)/sizeof(m_current_name[0]) ];
15369   for(;;)
15370   {
15371     current_file_attributes = 0;
15372     struct dirent* dp = 0;
15373     //G+Smo
15374 #   ifndef __MINGW32__
15375     dp = readdir(m_dir);
15376     //int readdir_errno = readdir_r(m_dir, &m_dirent, &dp);
15377     //if ( 0 !=  readdir_errno )
15378     //  break;
15379 #   endif
15380     if ( 0 != dp )
15381         m_dirent = *dp;
15382     else
15383       break;
15384 
15385     if ( 0 == m_dirent.d_name[0] )
15386       break;
15387 
15388     if ( IsDotOrDotDotDir(m_dirent.d_name) )
15389       continue;
15390 
15391     memset( current_name, 0, sizeof(current_name) );
15392     ON_ConvertUTF8ToWideChar(
15393       false, // no BOM in input file name as utf8 string
15394       &m_dirent.d_name[0],
15395       -1, // null terminated utf8 string
15396       &current_name[0], ((int)(sizeof(current_name)/sizeof(current_name[0]))) - 1, // output wchar_t string
15397       0, // null output error status
15398       (4|8|16), // mask common conversion errors
15399       0, // error_code_point = null terminator inserted at point of conversion error
15400       0  // null ouput end-of-string pointer
15401       );
15402     // TODO
15403     //   Test m_dirent.d_name to make sure it passes m_ws/utf8_file_name_filter
15404 
15405     ON_wString wpath = m_directory;
15406     wpath += '/';
15407     wpath += current_name;
15408 
15409     // get a utf8 version of the full path to pass to stat
15410     const ON_String utf8buffer(wpath);
15411     const char* utf8path = utf8buffer;
15412     if ( 0 == utf8path )
15413       continue;
15414 
15415     struct stat buf;
15416     memset(&buf,0,sizeof(buf));
15417     int stat_errno = stat( utf8path, &buf);
15418     if ( 0 != stat_errno )
15419       continue;
15420 
15421     if ( S_ISDIR(buf.st_mode) )
15422     {
15423       current_file_attributes = 2;
15424     }
15425     else if ( S_ISREG(buf.st_mode) )
15426     {
15427       // Only *.ext filters work at this time for non-windows
15428       const wchar_t* file_name_filter = m_ws_file_name_filter;
15429       if (   0 != file_name_filter
15430             && '*' == file_name_filter[0]
15431             && '.' == file_name_filter[1]
15432             && 0 != file_name_filter[2]
15433             && '*' != file_name_filter[2] )
15434       {
15435         // assume this is a *.extension filter
15436         const wchar_t* current_name_ext = 0;
15437         on_wsplitpath(current_name,0,0,0,&current_name_ext);
15438         if (   0 == current_name_ext
15439             || 0 != wcscmp(file_name_filter+1,current_name_ext)
15440            )
15441         {
15442           // current_name does pass match file_name_filter
15443           continue;
15444         }
15445       }
15446       current_file_attributes = 1;
15447     }
15448     else
15449       continue;
15450 
15451     // save current item information
15452     memcpy( m_current_name, current_name, sizeof(m_current_name) );
15453     m_current_file_attributes = current_file_attributes;
15454     m_current_file_size = buf.st_size;
15455     m_current_file_create_time = buf.st_mtime; // create time is not available on struct stat
15456     m_current_last_modified_time = buf.st_mtime;
15457     m_current_last_access_time = buf.st_atime;
15458 
15459     return m_current_name;
15460   }
15461 
15462   Destroy();
15463   return 0;
15464 #endif
15465 }
15466 
15467 
CurrentFileName() const15468 const wchar_t* ON_FileIterator::CurrentFileName() const
15469 {
15470 #if defined(ON_COMPILER_MSC)
15471   return ( 0 != m_h && 0 != m_fd.cFileName[0] ) ? m_fd.cFileName : 0;
15472 #else
15473   return ( 0 != m_current_name[0] ) ? m_current_name : 0;
15474 #endif
15475 }
15476 
CurrentFileSize() const15477 ON__UINT64 ON_FileIterator::CurrentFileSize() const
15478 {
15479   ON__UINT64 file_size = 0;
15480 
15481 #if defined(ON_COMPILER_MSC)
15482   if ( 0 != CurrentFileName() )
15483   {
15484     file_size  = m_fd.nFileSizeHigh;
15485     file_size *= ((ON__UINT64)0xFFFFFFFF);
15486     file_size += m_fd.nFileSizeLow;
15487   }
15488 #else
15489   file_size = m_current_file_size;
15490 #endif
15491 
15492   return file_size;
15493 }
15494 
CurrentFileIsDirectory() const15495 bool ON_FileIterator::CurrentFileIsDirectory() const
15496 {
15497   bool rc = false;
15498   const wchar_t* current_file_name = CurrentFileName();
15499   if ( 0 != current_file_name && 0 != current_file_name[0] )
15500   {
15501 #if defined(ON_COMPILER_MSC)
15502     if ( 0 != (FILE_ATTRIBUTE_DIRECTORY & m_fd.dwFileAttributes) )
15503     {
15504       rc = true;
15505     }
15506 #else
15507     if ( 2 == m_current_file_attributes)
15508     {
15509       rc = true;
15510     }
15511 #endif
15512   }
15513   return rc;
15514 }
15515 
CurrentFileIsHidden() const15516 bool ON_FileIterator::CurrentFileIsHidden() const
15517 {
15518   bool rc = false;
15519   const wchar_t* current_file_name = CurrentFileName();
15520   if ( 0 != current_file_name && 0 != current_file_name[0] )
15521   {
15522     if ( '.' == current_file_name[0] )
15523     {
15524       rc = true;
15525     }
15526 #if defined(ON_COMPILER_MSC)
15527     else if ( 0 != (FILE_ATTRIBUTE_HIDDEN & m_fd.dwFileAttributes) )
15528     {
15529       rc = true;
15530     }
15531 #endif
15532   }
15533   return rc;
15534 }
15535 
15536 
GetCurrentFullPathFileName(ON_wString & filename) const15537 bool ON_FileIterator::GetCurrentFullPathFileName( ON_wString& filename ) const
15538 {
15539   bool rc = false;
15540 
15541 #if defined(ON_COMPILER_MSC)
15542   if ( 0 == m_h || 0 == m_fd.cFileName )
15543   {
15544     filename.Empty();
15545   }
15546   else
15547   {
15548     filename = m_directory;
15549     filename += L"\\";
15550     filename += m_fd.cFileName;
15551     rc = true;
15552   }
15553 #else
15554 
15555   // gcc implementation
15556   if ( 0 == m_current_name[0] )
15557   {
15558     filename.Empty();
15559   }
15560   else
15561   {
15562     filename = m_directory;
15563     filename += L"/";
15564     filename += m_current_name;
15565     rc = true;
15566   }
15567 
15568 #endif
15569 
15570   return rc;
15571 }
15572 
15573 
15574 #if defined(ON_COMPILER_MSC)
SecondsSinceJanOne1970(FILETIME ft)15575 static ON__UINT64 SecondsSinceJanOne1970( FILETIME ft )
15576 {
15577   // The FILETIME is in 100-nanosecond intervals since January 1, 1601 UCT.
15578   //
15579   // Between midnight January 1, 1601 and midnight January 1, 1970 there
15580   // were 134774 days = 11644473600 seconds. Each second has 10^7 intervals
15581   // that are one hundred nanoseconds long.  So, if N = number of one hundred
15582   // nanosecond intervals since midnight January 1, 1601, then
15583   // (N / 10000000) - 11644473600 = number of seconds since midnight
15584   // January 1, 1970.
15585   //
15586   // January 1, 1601 was the start of a Gregorian calendary 400 year cycle
15587   // and "the internet" sometimes cites that as the reason that date is
15588   // the "beginning of time" for Windows' FILETIME values.  This convention
15589   // would slightly simplify the formulae used to account for leap years,
15590   // so it is plausable this might might even be true.
15591 
15592   ON__UINT64 ft_since_jan_1_1601 = ft.dwHighDateTime;
15593   ft_since_jan_1_1601 *= 0xFFFFFFFF;
15594   ft_since_jan_1_1601 += ft.dwLowDateTime;
15595 
15596   ON__UINT64 hundrednanoseconds_per_second = 10000000;
15597 
15598   ON__UINT64 seconds_since_jan_1_1601 = ft_since_jan_1_1601 / hundrednanoseconds_per_second;
15599 
15600   ON__UINT64 seconds_since_jan_1_1970 = seconds_since_jan_1_1601 - 11644473600;
15601 
15602   return seconds_since_jan_1_1970;
15603 }
15604 #endif
15605 
CurrentFileCreateTime() const15606 ON__UINT64 ON_FileIterator::CurrentFileCreateTime() const
15607 {
15608 #if defined(ON_COMPILER_MSC)
15609   return SecondsSinceJanOne1970(m_fd.ftCreationTime);
15610 #else
15611   return m_current_file_create_time;
15612 #endif
15613 }
15614 
CurrentFileLastModifiedTime() const15615 ON__UINT64 ON_FileIterator::CurrentFileLastModifiedTime() const
15616 {
15617 #if defined(ON_COMPILER_MSC)
15618   return SecondsSinceJanOne1970(m_fd.ftLastWriteTime);
15619 #else
15620   return m_current_last_modified_time;
15621 #endif
15622 }
15623 
CurrentFileLastAccessTime() const15624 ON__UINT64 ON_FileIterator::CurrentFileLastAccessTime() const
15625 {
15626 #if defined(ON_COMPILER_MSC)
15627   return SecondsSinceJanOne1970(m_fd.ftLastAccessTime);
15628 #else
15629   return m_current_last_access_time;
15630 #endif
15631 }
15632 
15633