/* $NoKeywords: $ */ /* // // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert // McNeel & Associates. // // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF // MERCHANTABILITY ARE HEREBY DISCLAIMED. // // For complete openNURBS copyright information see . // //////////////////////////////////////////////////////////////// */ #include "opennurbs.h" #if !defined(ON_COMPILING_OPENNURBS) // This check is included in all opennurbs source .c and .cpp files to insure // ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. // When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined // and the opennurbs .h files alter what is declared and how it is declared. #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif // obsolete V5 dimension style #include "opennurbs_internal_V5_dimstyle.h" // obsolete V2 and V5 annotation objects #include "opennurbs_internal_V2_annotation.h" #include "opennurbs_internal_V5_annotation.h" const ON_String Internal_RuntimeEnvironmentToString( ON::RuntimeEnvironment runtime_environment ) { switch (runtime_environment) { ON_ENUM_TO_STRING_CASE(ON::RuntimeEnvironment::Unset); ON_ENUM_TO_STRING_CASE(ON::RuntimeEnvironment::None); ON_ENUM_TO_STRING_CASE(ON::RuntimeEnvironment::Windows); ON_ENUM_TO_STRING_CASE(ON::RuntimeEnvironment::Apple); ON_ENUM_TO_STRING_CASE(ON::RuntimeEnvironment::Android); ON_ENUM_TO_STRING_CASE(ON::RuntimeEnvironment::Linux); } ON_ERROR("Invalid runtime_environment parameter value."); return ON_String::EmptyString; } const ON_wString Internal_RuntimeEnvironmentToWideString( ON::RuntimeEnvironment runtime_environment ) { const ON_String s = Internal_RuntimeEnvironmentToString(runtime_environment); return ON_wString(s); } unsigned int ON_BinaryArchive::ArchiveOpenNURBSVersionToWrite( unsigned int archive_3dm_version, unsigned int opennurbs_version ) { // When writing V5 files, any version number in the new V6 format needs to be // converted to the old V5 format unsigned int opennurbs_version_to_write = opennurbs_version; if ( ((archive_3dm_version >= 2 && archive_3dm_version <= 4) || (50 == archive_3dm_version)) && false == ON_VersionNumberIsYearMonthDateFormat(archive_3dm_version, opennurbs_version) ) { unsigned int yyyy = 0; unsigned int mm = 0; unsigned int dd = 0; unsigned int major_version_number = 0; if (ON_VersionNumberParse(opennurbs_version, &major_version_number, 0, &yyyy, &mm, &dd, 0)) { unsigned int n = major_version_number < 10 ? major_version_number : 9; opennurbs_version_to_write = ((yyyy * 100 + mm) * 100 + dd) * 10 + n; } } return opennurbs_version_to_write; } ON_UserDataItemFilter::ON_UserDataItemFilter() : m_application_id(ON_nil_uuid) , m_item_id(ON_nil_uuid) , m_precedence(0) , m_bSerialize(false) {} ON_UserDataItemFilter::ON_UserDataItemFilter( ON_UUID application_id, bool bSerialize ) : m_application_id(application_id) , m_item_id(ON_nil_uuid) , m_precedence(0) , m_bSerialize(bSerialize ? true : false) {} ON_UserDataItemFilter::ON_UserDataItemFilter( ON_UUID application_id, ON_UUID item_id, bool bSerialize ) : m_application_id(application_id) , m_item_id(item_id) , m_precedence(0) , m_bSerialize(bSerialize ? true : false) {} int ON_UserDataItemFilter::Compare( const class ON_UserDataItemFilter* a, const class ON_UserDataItemFilter* b ) { // null pointer handling if (a == b) return 0; if (0 == b) return -1; // non-null < null if (0 == a) return 1; int rc = ON_UuidCompare(a->m_application_id, b->m_application_id); if (0 != rc) return rc; rc = ON_UuidCompare(a->m_item_id, b->m_item_id); if (0 != rc) return rc; if (a->m_precedence < b->m_precedence) return -1; if (b->m_precedence < a->m_precedence) return 1; rc = ((int)(a->m_bSerialize ? 1 : 0)) - ((int)(a->m_bSerialize ? 1 : 0)); return rc; } class ON_ReadChunkHelper { public: /* Parameters: archive - [in] bReadSuccess - [out] The value of *bReadSuccess is set to false if an error occurs. Otherwise the value of *bReadSuccess is not changed. */ ON_ReadChunkHelper( ON_BinaryArchive&, bool& bReadSuccess ); ~ON_ReadChunkHelper(); ON_BinaryArchive& m_binary_archive; ON__UINT32 m_chunk_tcode = 0; bool m_bSupressPartiallyReadChunkWarning = false; ON__INT64 m_chunk_value = 0; bool& m_bReadSuccess; private: bool m_bCallEndRead3dmChunk = false; private: // prohibit use - no implementation ON_ReadChunkHelper() = delete; ON_ReadChunkHelper(const ON_ReadChunkHelper&) = delete; ON_ReadChunkHelper& operator=(const ON_ReadChunkHelper&) = delete; }; ON_ReadChunkHelper::ON_ReadChunkHelper( ON_BinaryArchive& binary_archive, bool &bReadSuccess ) : m_binary_archive(binary_archive) , m_bReadSuccess(bReadSuccess) { m_bCallEndRead3dmChunk = m_binary_archive.BeginRead3dmBigChunk(&m_chunk_tcode,&m_chunk_value); if ( false == m_bCallEndRead3dmChunk || 0 == m_chunk_tcode ) { // RH-22447 - valid chunk typecodes are never zero m_bReadSuccess = false; } } ON_ReadChunkHelper::~ON_ReadChunkHelper() { if (m_bCallEndRead3dmChunk) { if ( !m_binary_archive.EndRead3dmChunk(m_bSupressPartiallyReadChunkWarning) ) { m_bReadSuccess = false; } } } bool ON_IsUnsignedChunkTypecode( ON__UINT32 typecode ) { // returns tru if the chunk value should be treated as an unsigned int. return ( 0 == (TCODE_SHORT & typecode) || TCODE_RGB == typecode || TCODE_RGBDISPLAY == typecode || TCODE_PROPERTIES_OPENNURBS_VERSION == typecode || TCODE_OBJECT_RECORD_TYPE == typecode ); } bool ON_IsLongChunkTypecode(ON__UINT32 typecode) { // NOTE: RenderXXXX plug-in used zero as a typecode in material userdata, sigh ... //return (0 != typecode && 0 == (TCODE_SHORT & typecode)); return (0 == (TCODE_SHORT & typecode)); } bool ON_IsShortChunkTypecode(ON__UINT32 typecode) { return (0 != (TCODE_SHORT & typecode)); } static bool DownSizeINT( ON__INT64 i64, ON__INT32* i32 ) { const static ON__INT64 i32max = 2147483647; if ( i64 <= i32max && i64 >= (-i32max - 1) ) { *i32 = (ON__INT32)i64; return true; } ON_ERROR("i64 too big to convert to 4 byte signed int"); *i32 = 0; return false; } static bool DownSizeUINT( ON__UINT64 u64, ON__UINT32* u32 ) { if ( u64 <= 0xFFFFFFFF ) { *u32 = (ON__UINT32)u64; return true; } ON_ERROR("u64 too big to convert to 4 byte unsigned int"); *u32 = 0; return false; } struct ON__3dmV1LayerIndex { int m_layer_index; int m_layer_name_length; char* m_layer_name; struct ON__3dmV1LayerIndex* m_next; }; ON_BinaryArchive::ON_BinaryArchive( ON::archive_mode mode ) : m_mode(mode) { if (ON::archive_mode::read3dm == mode || ON::archive_mode::write3dm == mode) m_bChunkBoundaryCheck = true; m_annotation_context.SetReferencedBinaryArchive(this); } class ON_3dmTableStatusLink { public: ON_3dmTableStatusLink() = default; ~ON_3dmTableStatusLink() = default; ON_3dmTableStatusLink(const ON_3dmTableStatusLink&) = default; ON_3dmTableStatusLink& operator=(const ON_3dmTableStatusLink&) = default; ON_3dmTableStatusLink* m_next = nullptr; ON_3dmArchiveTableStatus m_table_status; }; ON_BinaryArchive::~ON_BinaryArchive() { if ( 0 != m_V1_layer_list ) { struct ON__3dmV1LayerIndex* next = m_V1_layer_list; m_V1_layer_list = 0; for ( int i = 0; 0 != next && i < 1000; i++ ) { struct ON__3dmV1LayerIndex* p = next; next = p->m_next; onfree(p); } } if (nullptr != m_compressor) { CompressionEnd(); onfree(m_compressor); } ON_3dmTableStatusLink* next = m_3dm_table_status_list; m_3dm_table_status_list = nullptr; while (nullptr != next) { ON_3dmTableStatusLink* p = next; next = const_cast(next->m_next); delete p; } m_annotation_context.SetReferencedDimStyle(nullptr,nullptr,ON_UNSET_INT_INDEX); if (nullptr != m_archive_3dm_properties) { delete m_archive_3dm_properties; m_archive_3dm_properties = nullptr; } if (nullptr != m_archive_3dm_settings) { delete m_archive_3dm_settings; m_archive_3dm_settings = nullptr; } for (int i = 0; i < m_archive_text_style_table.Count(); i++) { if (nullptr != m_archive_text_style_table[i]) delete m_archive_text_style_table[i]; } m_archive_text_style_table.Destroy(); for (int i = 0; i < m_archive_dim_style_table.Count(); i++) { if (nullptr != m_archive_dim_style_table[i]) delete m_archive_dim_style_table[i]; } m_archive_dim_style_table.Destroy(); } bool ON_BinaryArchive::ArchiveFileMoved() const { return m_b3dmArchiveMoved; } const ON_wString& ON_BinaryArchive::ArchiveFileName() const { return m_archive_file_name; } const ON_wString& ON_BinaryArchive::ArchiveDirectoryName() const { return m_archive_directory_name; } const ON_wString& ON_BinaryArchive::ArchiveFullPath() const { return m_archive_full_path; } const ON_wString& ON_BinaryArchive::ArchiveSavedAsFullPath() const { return m_archive_saved_as_full_path; } const wchar_t* ON_BinaryArchive::ArchiveFileNameAsPointer() const { return static_cast(m_archive_file_name); } const wchar_t* ON_BinaryArchive::ArchiveDirectoryNameAsPointer() const { return static_cast(m_archive_directory_name); } const wchar_t* ON_BinaryArchive::ArchiveFullPathAsPointer() const { return static_cast(m_archive_full_path); } const wchar_t* ON_BinaryArchive::ArchiveSavedAsFullPathPointer() const { return static_cast(m_archive_saved_as_full_path); } void ON_BinaryArchive::SetArchiveFullPath( const wchar_t* archive_full_path ) { if (m_archive_full_path.IsNotEmpty()) { // The first attempt wins! if (false == m_archive_full_path.EqualOrdinal(archive_full_path, false)) { // You need to get this right on the first try. // If you are hitting this error, figure out why you are attempting to change // this value and/or and make sure ArchiveFullPath().IsEmpty() // is true before you attempt to set it. // This is here because the value gets set correctly and then changed // to the wrong value when people use temp files and rename after writing // and don't pay attention to what they are doing. ON_ERROR("Attempt to change archive path."); } return; } ON_wString local_full_path(archive_full_path); archive_full_path = local_full_path; ON_wString archive_file_name; ON_wString archive_directory_name; if (nullptr != archive_full_path && 0 != archive_full_path[0]) { const wchar_t* dr = 0; const wchar_t* d = 0; const wchar_t* f = 0; const wchar_t* e = 0; on_wsplitpath(archive_full_path, &dr, &d, &f, &e); if (archive_full_path == f || (nullptr != d && f > archive_full_path && ON_FileSystemPath::IsRelativePath(archive_full_path) ) ) { const ON_wString current_directory = ON_FileSystemPath::CurrentDirectory(true); if (current_directory.IsNotEmpty()) { local_full_path = ON_FileSystemPath::CombinePaths(static_cast(current_directory), false, archive_full_path, true, false); archive_full_path = local_full_path; on_wsplitpath(archive_full_path, &dr, &d, &f, &e); } } if (nullptr != f && 0 != f[0]) { archive_file_name = f; if (nullptr == dr) dr = d; if (nullptr != dr && 0 != dr[0] && dr < f) { archive_directory_name = dr; archive_directory_name.SetLength(f-dr); } } } SetArchiveFullPath( static_cast(archive_directory_name), static_cast(archive_file_name) ); m_archive_full_path = archive_full_path; switch (m_mode) { case ON::archive_mode::write3dm: case ON::archive_mode::write: m_archive_saved_as_full_path = m_archive_full_path; break; } } void ON_BinaryArchive::SetArchiveFullPath( const wchar_t* archive_directory_name, const wchar_t* archive_file_name ) { ON_wString s(archive_directory_name); s.TrimRight(L"/\\"); if (s.IsEmpty() || (2 == s.Length() && ':' == s[1])) s = archive_directory_name; const ON_wString local_directory_name(s); if (nullptr != archive_file_name) { switch (archive_file_name[0]) { case '/': case '\\': case ':': ON_ERROR("archive_file_name is not valid."); archive_file_name = nullptr; } } const ON_wString local_file_name(archive_file_name); if (m_archive_directory_name.IsNotEmpty() || m_archive_full_path.IsNotEmpty()) { // The first attempt wins! if (false == m_archive_directory_name.EqualOrdinal(local_directory_name, false)) { // You need to get this right on the first try. // If you are hitting this error, figure out why you are attempting to change // this value and/or and make sure ArchiveFullPath().IsEmpty() // is true before you attempt to set it. // This is here because the value gets set correctly and then changed // to the wrong value when people use temp files and rename after writing // and don't pay attention to what they are doing. ON_ERROR("Attempt to change archive path."); } return; } if (m_archive_file_name.IsNotEmpty() || m_archive_full_path.IsNotEmpty()) { // The first attempt wins! if (false == m_archive_file_name.EqualOrdinal(local_file_name, false)) { // You need to get this right on the first try. // If you are hitting this error, figure out why you are attempting to change // this value and/or and make sure ArchiveFullPath().IsEmpty() // is true before you attempt to set it. // This is here because the value gets set correctly and then changed // to the wrong value when people use temp files and rename after writing // and don't pay attention to what they are doing. ON_ERROR("Attempt to change archive path."); } return; } m_archive_directory_name = local_directory_name; m_archive_file_name = local_file_name; if (m_archive_directory_name.IsNotEmpty() && m_archive_file_name.IsNotEmpty()) { m_archive_full_path = ON_wString::EmptyString; m_archive_full_path += ON_FileSystemPath::DirectorySeparator; m_archive_full_path += m_archive_file_name; } else m_archive_full_path = ON_wString::EmptyString; switch (m_mode) { case ON::archive_mode::write3dm: case ON::archive_mode::write: m_archive_saved_as_full_path = m_archive_full_path; break; } } ON_BinaryArchive::eStorageDeviceError ON_BinaryArchive::StorageDeviceErrorFromUnsigned( unsigned int storage_device_error_as_unsigned ) { switch (storage_device_error_as_unsigned) { ON_ENUM_FROM_UNSIGNED_CASE(ON_BinaryArchive::eStorageDeviceError::None); ON_ENUM_FROM_UNSIGNED_CASE(ON_BinaryArchive::eStorageDeviceError::WriteFailed); ON_ENUM_FROM_UNSIGNED_CASE(ON_BinaryArchive::eStorageDeviceError::SeekFailedDuringWriting); ON_ENUM_FROM_UNSIGNED_CASE(ON_BinaryArchive::eStorageDeviceError::ReadFailed); ON_ENUM_FROM_UNSIGNED_CASE(ON_BinaryArchive::eStorageDeviceError::SeekFailedDuringReading); ON_ENUM_FROM_UNSIGNED_CASE(ON_BinaryArchive::eStorageDeviceError::UnknownDeviceError); } ON_ERROR("Invalid storage_device_error_as_unsigned parmeter."); return ON_BinaryArchive::eStorageDeviceError::UnknownDeviceError; } unsigned int ON_BinaryArchive::StorageDeviceError() const { return m_storage_device_error; } void ON_BinaryArchive::SetStorageDeviceError( ON_BinaryArchive::eStorageDeviceError storage_device_error ) { const unsigned int u = static_cast(storage_device_error); SetStorageDeviceError(u); } void ON_BinaryArchive::SetStorageDeviceError( unsigned int storage_device_error ) { if (0 != storage_device_error) { Internal_ReportCriticalError(); // A critical error terminates the read/write. // The first one sets the code and subsequent attempts to modify // the code fail. if (0 == m_storage_device_error) { ON_ERROR("Damaged file and / or buggy code. Please investigate."); m_storage_device_error = storage_device_error; } } } bool ON_BinaryArchive::ToggleByteOrder( size_t count, // number of elements size_t sizeof_element, // size of element (2,4, or 8) const void* src, // source buffer void* dst // destination buffer (can be same as source buffer) ) { bool rc = (0 == count || (count > 0 && sizeof_element > 0 && nullptr != src && nullptr != dst)); if ( rc && count > 0 ) { unsigned char c[32]; const unsigned char* a = (const unsigned char*)src; unsigned char* b = (unsigned char*)dst; const unsigned char* b1 = b + (count*sizeof_element); // loops are unrolled and a switch is used // to speed things up a bit. switch(sizeof_element) { case 2: while(b < b1) { c[0] = *a++; c[1] = *a++; *b++ = c[1]; *b++ = c[0]; } break; case 4: while(b < b1) { c[0] = *a++; c[1] = *a++; c[2] = *a++; c[3] = *a++; *b++ = c[3]; *b++ = c[2]; *b++ = c[1]; *b++ = c[0]; } break; case 8: while(b < b1) { c[0] = *a++; c[1] = *a++; c[2] = *a++; c[3] = *a++; c[4] = *a++; c[5] = *a++; c[6] = *a++; c[7] = *a++; *b++ = c[7]; *b++ = c[6]; *b++ = c[5]; *b++ = c[4]; *b++ = c[3]; *b++ = c[2]; *b++ = c[1]; *b++ = c[0]; } break; default: if ( sizeof_element < 32 ) { // As of 2 May 2003, this case is never used // by core opennurbs objects. // // This is here so that future code will work // if and when 128 bit "ints"/"doubles" become common // enough that they can be stored in 3dm files. // It may also happen that third party applications // on specialized CPUs need to toggle byte order // for 128 bit ints/doubles stored as user data. size_t i; while(b < b1) { for (i = 0; i < sizeof_element; i++) c[i] = *a++; while(i--) *b++ = c[i]; } } else { rc = false; } break; } } return rc; } ON__UINT64 ON_BinaryArchive::CurrentPosition() const { return m_current_positionX; } bool ON_BinaryArchive::Internal_IncrementCurrentPosition( ON__UINT64 delta ) { // TODO: Add error detection. const ON__UINT64 new_pos = m_current_positionX + delta; m_current_positionX = new_pos; return true; } bool ON_BinaryArchive::Internal_DecrementCurrentPosition( ON__UINT64 delta ) { if (m_current_positionX >= delta) { const ON__UINT64 new_pos = m_current_positionX - delta; m_current_positionX = new_pos; return true; } ON_ERROR("Attempt to set current position before start of archive."); return false; } bool ON_BinaryArchive::SeekFromStart( ON__UINT64 bytes_from_start ) { if (UnsetMode()) { ON_ERROR("Invalid archive Mode()."); return false; } if (m_bChunkBoundaryCheck) { const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if (nullptr != c) { ON_ERROR("Attempt to seek before beginning of current chunk."); return false; } } if (0 != CurrentPosition() ) { // Internal_SeekToStart() is a pure virutal function that must overridden. if (!Internal_SeekToStartOverride()) { ON_ERROR("Internal_SeekToStartOverride() failed."); if (ReadMode()) SetStorageDeviceError(ON_BinaryArchive::eStorageDeviceError::SeekFailedDuringReading); if (WriteMode()) SetStorageDeviceError(ON_BinaryArchive::eStorageDeviceError::SeekFailedDuringWriting); return false; } m_current_positionX = 0; } return (bytes_from_start > 0) ? SeekForward(bytes_from_start) : true; } bool ON_BinaryArchive::SeekForward( ON__UINT64 bytes_forward ) { return Internal_SeekCur(true, bytes_forward); } bool ON_BinaryArchive::SeekBackward( ON__UINT64 bytes_backward ) { return Internal_SeekCur(false, bytes_backward); } bool ON_BinaryArchive::Internal_SeekCur( bool bForward, ON__UINT64 offset ) { // Internal_SeekFromCurrentPosition() is a pure virutal function that must overridden. // Some implementations may use signed 4 byte int in critical places. // SeekForward() will work correctly in this worst case situation. if (UnsetMode()) { ON_ERROR("Invalid archive Mode()."); return false; } const ON__UINT64 current_pos = CurrentPosition(); if (false == bForward && offset > current_pos) { ON_ERROR("Attempt to seek before archive beginning."); return false; } const ON__UINT64 new_pos = bForward ? (current_pos + offset) : (current_pos - offset); if (m_bChunkBoundaryCheck) { const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if (nullptr != c && c->m_start_offset <= current_pos && current_pos <= c->m_end_offset ) { if (new_pos > c->m_end_offset) { // The code that seeks 1 byte past the end of a level 1 chunk disables chunk boundary checking for that seek.) ON_ERROR("Attempt to seek beyond end of current chunk."); return false; } if (new_pos < c->m_start_offset) { // seeking before beginning of chunk's data // (The code that seeks back to write a chunk length disables chunk boundary checking for that write.) ON_ERROR("Attempt to seek before beginning of current chunk."); return false; } } } const int dir = bForward ? 1 : -1; int ioffset; const ON__UINT64 max_internal_seek = 2147483632; // < maximum signed 4 bytes int = 2147483647 while ( offset > 0 ) { const ON__UINT64 delta = (offset > max_internal_seek) ? max_internal_seek : offset; ioffset = dir*((int)delta); if (false == Internal_SeekFromCurrentPositionOverride(ioffset)) { ON_ERROR("Internal_SeekFromCurrentPositionOverride(ioffset) failed."); if (ReadMode()) SetStorageDeviceError(ON_BinaryArchive::eStorageDeviceError::SeekFailedDuringReading); if (WriteMode()) SetStorageDeviceError(ON_BinaryArchive::eStorageDeviceError::SeekFailedDuringWriting); return false; } if (bForward) Internal_IncrementCurrentPosition(delta); else Internal_DecrementCurrentPosition(delta); offset -= delta; } return true; } bool ON_BinaryArchive::ReadChar( // Read an array of 8 bit chars size_t count, // number of chars to read char* p ) { return ReadByte( count, p ); } bool ON_BinaryArchive::ReadChar( // Read an array of 8 bit unsigned chars size_t count, // number of unsigned chars to read unsigned char* p ) { return ReadByte( count, p ); } bool ON_BinaryArchive::ReadChar( // Read a single 8 bit char char* p ) { return ReadByte( 1, p ); } bool ON_BinaryArchive::ReadChar( // Read a single 8 bit unsigned char unsigned char* p ) { return ReadByte( 1, p ); } bool ON_BinaryArchive::ReadInt16( // Read an array of 16 bit integers size_t count, // number of unsigned integers to read ON__INT16* p ) { bool rc = ReadByte( count<<1, p ); if (rc && m_endian == ON::endian::big_endian) { // reverse byte order unsigned char* b= (unsigned char*) (p); unsigned char c; while(count--) { c = b[0]; b[0] = b[1]; b[1] = c; b += 2; } } return rc; } bool ON_BinaryArchive::ReadShort( // Read an array of 16 bit shorts size_t count, // number of unsigned chars to read short* p ) { #pragma ON_PRAGMA_WARNING_PUSH // Disable the MSC /W4 "conditional expression is constant" warning // about 2 == sizeof(*p). Since this code has to run on machines // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary. #pragma ON_PRAGMA_WARNING_DISABLE_MSC(4127) bool rc = true; if ( 2 == sizeof(*p) ) { rc = ReadInt16( count, (ON__INT16*)p ); } else { size_t j; ON__INT16 i16; for ( j = 0; j < count && rc; j++ ) { rc = ReadInt16( 1, &i16 ); *p++ = (short)i16; } } return rc; #pragma ON_PRAGMA_WARNING_POP } bool ON_BinaryArchive::ReadShort( // Read an array of 16 bit unsigned shorts size_t count, // number of unsigned chars to read unsigned short* p ) { return ReadShort( count, (short*)p ); } bool ON_BinaryArchive::ReadShort( // Read a single 16 bit short short* p ) { return ReadShort( 1, p ); } bool ON_BinaryArchive::ReadShort( // Read a single 16 bit unsigned short unsigned short* p ) { return ReadShort( 1, p ); } bool ON_BinaryArchive::ReadInt32( // Read an array of 32 bit integers size_t count, // number of 32 bit integers to read ON__INT32* p ) { bool rc = ReadByte( count<<2, p ); if (rc && m_endian == ON::endian::big_endian) { unsigned char* b= (unsigned char*)p; unsigned char c; while(count--) { c = b[0]; b[0] = b[3]; b[3] = c; c = b[1]; b[1] = b[2]; b[2] = c; b += 4; } } return rc; } bool ON_BinaryArchive::ReadInt( // Read an array of integers size_t count, // number of unsigned chars to read int* p ) { #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_PUSH // Disable the MSC /W4 "conditional expression is constant" warning // about 4 == sizeof(*p). Since this code has to run on machines // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary. #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) #endif bool rc; if ( 4 == sizeof(*p) ) { rc = ReadInt32( count, (ON__INT32*)p ); } else { rc = true; ON__INT32 i32; size_t j; for ( j = 0; j < count && rc; j++ ) { rc = ReadInt32(1,&i32); if (rc) *p++ = (int)i32; } } return rc; #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_POP #endif } bool ON_BinaryArchive::ReadInt( // Read an array of 32 bit integers size_t count, // number of unsigned chars to read unsigned int* p ) { return ReadInt( count, (int*)p ); } bool ON_BinaryArchive::ReadInt( // Read a single 32 bit integer int* p ) { return ReadInt( 1, p ); } bool ON_BinaryArchive::ReadInt( // Read a single 32 bit unsigned integer unsigned int* p ) { return ReadInt( 1, p ); } bool ON_BinaryArchive::ReadBigInt( // Read an array of 64 bit integers size_t count, ON__INT64* p ) { return ReadInt64(1,p); } bool ON_BinaryArchive::ReadBigInt( // Read an array of 64 bit integers size_t count, ON__UINT64* p ) { return ReadInt64(1,(ON__INT64*)p); } bool ON_BinaryArchive::ReadBigInt( // Read a single 64 bit integer ON__INT64* p ) { return ReadInt64(1,p); } bool ON_BinaryArchive::ReadBigInt( // Read a single 64 bit unsigned integer ON__UINT64* p ) { return ReadInt64(1,(ON__INT64*)p); } bool ON_BinaryArchive::ReadLong( // Read an array of 32 bit integers size_t count, // number of unsigned chars to read long* p ) { #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_PUSH // Disable the MSC /W4 "conditional expression is constant" warning // about 4 == sizeof(*p). Since this code has to run on machines // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary. #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) #endif bool rc; if ( 4 == sizeof(*p) ) { rc = ReadInt32( count, (ON__INT32*)p ); } else { rc = true; ON__INT32 i32; size_t j; for ( j = 0; j < count && rc; j++ ) { rc = ReadInt32(1,&i32); if (rc) *p++ = (long)i32; } } return rc; #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_POP #endif } bool ON_BinaryArchive::ReadLong( // Read an array of 32 bit integers size_t count, // number of unsigned chars to read unsigned long* p ) { return ReadLong( count, (long*)p ); } bool ON_BinaryArchive::ReadLong( // Read a single 32 bit integer long* p ) { return ReadLong( 1, (long*)p ); } bool ON_BinaryArchive::ReadLong( // Read a single 32 bit unsigned integer unsigned long* p ) { return ReadLong( 1, (long*)p ); } bool ON_BinaryArchive::ReadFloat( // Read an array of floats size_t count, // number of unsigned chars to read float* p ) { // 32 bit floats and 32 bit integers have same size and endian issues return ReadInt32( count, (ON__INT32*)p ); } bool ON_BinaryArchive::ReadFloat( // Read a single float float* p ) { return ReadFloat( 1, p ); } bool ON_BinaryArchive::ReadDouble( // Read an array of IEEE 64 bit doubles size_t count, // number of unsigned chars to read double* p ) { bool rc = ReadByte( count<<3, p ); if (rc && m_endian == ON::endian::big_endian) { unsigned char* b=(unsigned char*)p; unsigned char c; while(count--) { c = b[0]; b[0] = b[7]; b[7] = c; c = b[1]; b[1] = b[6]; b[6] = c; c = b[2]; b[2] = b[5]; b[5] = c; c = b[3]; b[3] = b[4]; b[4] = c; b += 8; } } return rc; } bool ON_BinaryArchive::ReadDouble( // Read a single double double* p ) { return ReadDouble( 1, p ); } bool ON_BinaryArchive::ReadColor( ON_Color& color ) { unsigned int colorref = 0; bool rc = ReadByte( 4, (unsigned char*)&colorref ); // ReadByte prevents big endian swaps color = colorref; return rc; } bool ON_BinaryArchive::ReadColor(ON_4fColor& color) { float f = 0.f; bool rc = ReadFloat(&f); if (rc) { color.SetRed(f); rc = ReadFloat(&f); } if (rc) { color.SetGreen(f); rc = ReadFloat(&f); } if (rc) { color.SetBlue(f); rc = ReadFloat(&f); } if (rc) { color.SetAlpha(f); } return rc; } bool ON_BinaryArchive::ReadPoint ( ON_2dPoint& p ) { return ReadDouble( 2, &p.x ); } bool ON_BinaryArchive::ReadPoint ( ON_3dPoint& p ) { return ReadDouble( 3, &p.x ); } bool ON_BinaryArchive::ReadPoint ( ON_4dPoint& p ) { return ReadDouble( 4, &p.x ); } bool ON_BinaryArchive::ReadVector ( ON_2dVector& v ) { return ReadDouble( 2, &v.x ); } bool ON_BinaryArchive::ReadVector ( ON_3dVector& v ) { return ReadDouble( 3, &v.x ); } bool ON_BinaryArchive::WriteBoundingBox(const ON_BoundingBox& bbox) { bool rc = WritePoint( bbox.m_min ); if (rc) rc = WritePoint( bbox.m_max ); return rc; } bool ON_BinaryArchive::ReadBoundingBox(ON_BoundingBox& bbox) { bool rc = ReadPoint( bbox.m_min ); if (rc) rc = ReadPoint( bbox.m_max ); return rc; } bool ON_BinaryArchive::WriteXform( const ON_Xform& x ) { return WriteDouble( 16, &x.m_xform[0][0] ); } bool ON_BinaryArchive::ReadXform( ON_Xform& x ) { return ReadDouble( 16, &x.m_xform[0][0] ); } bool ON_BinaryArchive::WritePlaneEquation( const ON_PlaneEquation& plane_equation ) { bool rc = WriteDouble( 4, &plane_equation.x ); return rc; } bool ON_BinaryArchive::ReadPlaneEquation( ON_PlaneEquation& plane_equation ) { bool rc = ReadDouble( 4, &plane_equation.x ); return rc; } bool ON_BinaryArchive::WritePlane( const ON_Plane& plane ) { bool rc = WritePoint( plane.origin ); if (rc) rc = WriteVector( plane.xaxis ); if (rc) rc = WriteVector( plane.yaxis ); if (rc) rc = WriteVector( plane.zaxis ); if (rc) rc = WriteDouble( 4, &plane.plane_equation.x ); return rc; } bool ON_BinaryArchive::ReadPlane( ON_Plane& plane ) { bool rc = ReadPoint( plane.origin ); if (rc) rc = ReadVector( plane.xaxis ); if (rc) rc = ReadVector( plane.yaxis ); if (rc) rc = ReadVector( plane.zaxis ); if (rc) rc = ReadDouble( 4, &plane.plane_equation.x ); return rc; } bool ON_BinaryArchive::WriteLine( const ON_Line& line ) { bool rc = WritePoint( line.from ); if (rc) rc = WritePoint( line.to ); return rc; } bool ON_BinaryArchive::ReadLine( ON_Line& line ) { bool rc = ReadPoint( line.from ); if (rc) rc = ReadPoint( line.to ); return rc; } bool ON_BinaryArchive::WriteArc(const ON_Arc& arc ) { bool rc = WriteCircle(arc); if (rc) rc = WriteInterval(arc.m_angle); return rc; } bool ON_BinaryArchive::ReadArc( ON_Arc& arc ) { bool rc = ReadCircle(arc); if (rc) rc = ReadInterval(arc.m_angle); return rc; } bool ON_BinaryArchive::WriteCircle(const ON_Circle& circle) { bool rc = WritePlane( circle.plane ); if (rc) rc = WriteDouble( circle.radius ); // m_point[] removed 2001, November, 7 if (rc) rc = WritePoint( circle.PointAt(0.0) ); if (rc) rc = WritePoint( circle.PointAt(0.5*ON_PI) ); if (rc) rc = WritePoint( circle.PointAt(ON_PI) ); /* if (rc) rc = WritePoint( circle.m_point[0] ); if (rc) rc = WritePoint( circle.m_point[1] ); if (rc) rc = WritePoint( circle.m_point[2] ); */ return rc; } bool ON_BinaryArchive::ReadCircle(ON_Circle& circle) { ON_3dPoint scratch; bool rc = ReadPlane( circle.plane ); if (rc) rc = ReadDouble( &circle.radius ); // m_point[] removed 2001, November, 7 if (rc) rc = ReadPoint( scratch ); if (rc) rc = ReadPoint( scratch ); if (rc) rc = ReadPoint( scratch ); /* if (rc) rc = ReadPoint( circle.m_point[0] ); if (rc) rc = ReadPoint( circle.m_point[1] ); if (rc) rc = ReadPoint( circle.m_point[2] ); */ return rc; } bool ON_BinaryArchive::WriteInterval( const ON_Interval& t ) { return WriteDouble( 2, t.m_t ); } bool ON_BinaryArchive::ReadInterval( ON_Interval& t ) { return ReadDouble( 2, t.m_t ); } bool ON_BinaryArchive::ReadUuid( ON_UUID& uuid ) { bool rc = ReadInt32( 1, (ON__INT32*)(&uuid.Data1) ); if (rc) rc = ReadInt16( 1, (ON__INT16*)(&uuid.Data2) ); if (rc) rc = ReadInt16( 1, (ON__INT16*)(&uuid.Data3) ); if (rc) rc = ReadByte( 8, uuid.Data4 ); return rc; } bool ON_BinaryArchive::ReadDisplayMaterialRef( ON_DisplayMaterialRef& dmr ) { bool rc = ReadUuid( dmr.m_viewport_id ); if (rc) rc = ReadUuid( dmr.m_display_material_id ); return rc; } bool ON_BinaryArchive::ReadTime( struct tm& utc ) { // utc = coordinated universal time ( a.k.a GMT, UTC ) // (From ANSI C time() and gmtime().) bool rc = ReadInt( &utc.tm_sec ); if ( rc ) rc = ReadInt( &utc.tm_min ); if ( rc ) rc = ReadInt( &utc.tm_hour ); if ( rc ) rc = ReadInt( &utc.tm_mday ); if ( rc ) rc = ReadInt( &utc.tm_mon ); if ( rc ) rc = ReadInt( &utc.tm_year ); if ( rc ) rc = ReadInt( &utc.tm_wday ); if ( rc ) rc = ReadInt( &utc.tm_yday ); if ( rc ) { if ( utc.tm_sec < 0 || utc.tm_sec > 60 ) rc = false; if ( utc.tm_min < 0 || utc.tm_min > 60 ) rc = false; if ( utc.tm_hour < 0 || utc.tm_hour > 24 ) rc = false; if ( utc.tm_mday < 0 || utc.tm_mday > 31 ) rc = false; if ( utc.tm_mon < 0 || utc.tm_mon > 12 ) rc = false; // no year restrictions because dates are used in archeological userdata if ( utc.tm_wday < 0 || utc.tm_wday > 7 ) rc = false; if ( utc.tm_yday < 0 || utc.tm_yday > 366 ) rc = false; if ( !rc ) { ON_ERROR("ON_BinaryArchive::ReadTime() - bad time in archive"); } } return rc; } bool ON_BinaryArchive::ReadStringSize( // Read size of nullptr terminated string size_t* sizeof_string // (returned size includes nullptr terminator) ) { ON__UINT32 ui32 = 0; bool rc = ReadInt32(1,(ON__INT32*)&ui32); // Note that ui32 = number of elements in the string array, including // the null terminator. So ui32 should either be 0 or be >= 2. // The string array elements can be chars or unsigned shorts; // therefore the number of bytes in the string cannot be determined // at this point because we don't know what type of string is // being read. if (rc) { // 8 October 2004 Dale Lear // Added the sanity checks on string size to avoid attempts // to allocate huge amounts of memory when the value // comes from a damaged file. if ( 0 != (0xF000000 & ui32) ) { // 268 million chars oughta be plenty ON_ERROR("string element count is impossibly large"); rc = false; } else if ( ui32 > 0 ) { // make sure this is possible const ON_3DM_BIG_CHUNK* curchunk = m_chunk.Last(); if ( 0 != curchunk && 0 == (TCODE_SHORT & curchunk->m_typecode) ) { if ( curchunk->m_big_value < 0 || ((ON__INT64)ui32) > curchunk->m_big_value ) { ON_ERROR("string element count exceeds current chunk size"); rc = false; } } } if (rc) { *sizeof_string = (size_t)ui32; } } return rc; } bool ON_BinaryArchive::ReadStringUTF8ElementCount( size_t* string_utf8_element_count ) { ON__UINT32 ui32 = 0; bool rc = ReadInt32(1,(ON__INT32*)&ui32); // Note that ui32 = number of elements in the string array, including // the null terminator. So ui32 should either be 0 or be >= 2. // The string array elements can be chars or unsigned shorts; // therefore the number of bytes in the string cannot be determined // at this point because we don't know what type of string is // being read. if (rc) { // 8 October 2004 Dale Lear // Added the sanity checks on string size to avoid attempts // to allocate huge amounts of memory when the value // comes from a damaged file. if ( 0 != (0xF000000 & ui32) ) { // 268 million chars oughta be plenty ON_ERROR("string element count is impossibly large"); rc = false; } else if ( ui32 > 0 ) { // make sure this is possible const ON_3DM_BIG_CHUNK* curchunk = m_chunk.Last(); if ( 0 != curchunk && 0 == (TCODE_SHORT & curchunk->m_typecode) ) { if ( curchunk->m_big_value < 0 || ((ON__INT64)ui32) > curchunk->m_big_value ) { ON_ERROR("string byte count exceeds current chunk size"); rc = false; } } } } if (!rc) ui32 = 0; if ( string_utf8_element_count ) *string_utf8_element_count = (size_t)ui32; return rc; } bool ON_BinaryArchive::ReadStringUTF16ElementCount( size_t* string_utf16_element_count ) { ON__UINT32 ui32 = 0; bool rc = ReadInt32(1,(ON__INT32*)&ui32); // Note that ui32 = number of ON__UINT16 elements in the string array, // including the null terminator. So ui32 should either be 0 or >= 2. if (rc) { if ( 0 != (0xF000000 & ui32) ) { // 268 million chars oughta be plenty ON_ERROR("string element count is impossibly large"); rc = false; } else if ( ui32 > 0 ) { // make sure this is possible const ON_3DM_BIG_CHUNK* curchunk = m_chunk.Last(); if ( 0 != curchunk && 0 == (TCODE_SHORT & curchunk->m_typecode) ) { const ON__UINT64 unread_byte_count = curchunk->LengthRemaining(CurrentPosition()); const ON__UINT64 string_byte_count = (2 * ui32); // 2 = sizeof(ON__UINT16) if (unread_byte_count < string_byte_count ) { ON_ERROR("string byte count exceeds current chunk size"); rc = false; } } } } if (!rc) ui32 = 0; if ( string_utf16_element_count ) *string_utf16_element_count = (size_t)ui32; return rc; } bool ON_BinaryArchive::ReadString( // Read nullptr terminated string size_t string_utf8_element_count, // = value from ReadStringUTF8ElementCount() char* p // array[string_utf8_element_count] ) { return ReadByte( string_utf8_element_count, p ); } bool ON_BinaryArchive::ReadString( // Read nullptr terminated string size_t string_utf8_element_count, // = value from ReadStringUTF8ElementCount() unsigned char* p // array[string_utf8_element_count] ) { return ReadByte( string_utf8_element_count, p ); } bool ON_BinaryArchive::ReadString( // Read nullptr terminated unicode string size_t string_utf16_element_count, // length = value from ReadStringUTF16ElementCount() unsigned short* p // array[string_utf16_element_count] ) { return ReadShort( string_utf16_element_count, p ); } bool ON_BinaryArchive::ReadString( ON_String& s ) { s.Destroy(); size_t string_utf8_element_count = 0; bool rc = ReadStringUTF8ElementCount( &string_utf8_element_count ); if ( rc && string_utf8_element_count > 0 ) { const int istring_utf8_element_count = (int)string_utf8_element_count; // (int) converts 64 bits size_t s.ReserveArray(istring_utf8_element_count); ReadString( string_utf8_element_count, s.Array() ); s.SetLength( istring_utf8_element_count-1 ); } return rc; } bool ON_BinaryArchive::ReadString( ON_wString& s ) { #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_PUSH // Disable the MSC /W4 "conditional expression is constant" warning // about 2 == sizeof(wchar_t). Since this code has to run on machines // where sizeof(wchar_t) can be 2, 4, or 8 bytes, the test is necessary. #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) #endif s.Destroy(); size_t string_utf16_element_count = 0; bool rc = ReadStringUTF16ElementCount( &string_utf16_element_count ); if ( rc && string_utf16_element_count > 0 ) { // string_utf16_element_count = number of ON__INT16 elements in // the string. This is almost always the same as the // number of unicode code points. However, if one of // the code points happens to require two ON__INT16 // values to encode, then string_utf16_element_count will be // larger than the number of unicode code points in // the array. const int istring_utf16_element_count = (int)string_utf16_element_count; if ( 2 == sizeof(wchar_t) ) { // When sizeof(wchar_t) is 2 bytes, assume wchar_t strings are // UTF-16 encoded unicode strings. s.ReserveArray( istring_utf16_element_count ); rc = ReadInt16( string_utf16_element_count, (ON__INT16*)s.Array() ); if (rc) s.SetLength( istring_utf16_element_count-1 ); } else if ( 4 == sizeof(wchar_t) ) { // When sizeof(wchar_t) is 4 bytes, assume wchar_t strings are // UTF-32 encoded unicode strings. (some Apple CLang and GNU gcc implementations do this.) // Read the UTF-16 encode string from the file into // utf16_buffer[]. ON_SimpleArray utf16_buffer(istring_utf16_element_count); rc = ReadInt16(string_utf16_element_count,(ON__INT16*)utf16_buffer.Array()); if(rc) { // convert to a UTF-32 encoded unicode string. utf16_buffer.SetCount(istring_utf16_element_count); utf16_buffer[istring_utf16_element_count-1] = 0; rc = false; const ON__UINT16* sUTF16 = utf16_buffer.Array(); const int bTestByteOrder = false; const int sUTF16_count = istring_utf16_element_count-1; const ON__UINT32 error_code_point = 0xFFFD; const unsigned int error_mask = 0xFFFFFFFF; unsigned int error_status = 0; const int utf32_array_count = ON_ConvertUTF16ToUTF32( bTestByteOrder, sUTF16, sUTF16_count, 0, // unsigned int* sUTF32 0, // int sUTF32_count &error_status, error_mask, error_code_point, 0 // const ON__UINT16** sNextUTF16 ); if ( 0 == utf32_array_count ) { rc = true; } else if ( utf32_array_count > 0 ) { error_status = 0; s.ReserveArray(utf32_array_count+1); const int utf32_array_count1 = ON_ConvertUTF16ToUTF32( bTestByteOrder, sUTF16, sUTF16_count, (unsigned int*)s.Array(), // unsigned int* sUTF32 utf32_array_count, // sUTF32_count &error_status, error_mask, error_code_point, 0 // const ON__UINT16** sNextUTF16 ); if ( utf32_array_count1 == utf32_array_count ) { s.SetLength( utf32_array_count ); rc = true; } } } } if (!rc) s.Destroy(); } return rc; #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_POP #endif } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a) { int i, count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { // ON_MappingChannel::Write() puts the element in a chunk rc = a[i].Write(*this); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_ClassArray& a) { int i, count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { // ON_MaterialRef::Write() puts the element in a chunk rc = a[i].Write(*this); } return rc; } bool ON_BinaryArchive::WriteArray( int count, const ON_Layer* a) { int i; if ( count < 0 || 0 == a ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { rc = WriteObject(a[i]); } return rc; } bool ON_BinaryArchive::WriteArray( int count, const ON_Layer*const* a) { int i; if ( count < 0 || 0 == a ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { rc = WriteObject(a[i]); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_ClassArray& a ) { int i, count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { // ON_MappingRef::Write() puts the element in a chunk rc = a[i].Write(*this); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_ClassArray& a) { int i, count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { // ON_ObjRef::Write() puts the element in a chunk rc = a[i].Write(*this); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a) { int i, count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { // ON_ObjRef_IRefID::Write() puts the element in a chunk rc = a[i].Write(*this); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int i, count; bool rc = ReadInt( &count ); if (rc) { a.SetCapacity(count); for ( i = 0; i < count && rc; i++ ) { rc = a.AppendNew().Read(*this); } } return rc; } bool ON_BinaryArchive::ReadArray( ON_ClassArray& a) { a.Empty(); int i, count; bool rc = ReadInt( &count ); if (rc) { a.SetCapacity(count); for ( i = 0; i < count && rc; i++ ) { rc = a.AppendNew().Read(*this); } } return rc; } bool ON_BinaryArchive::ReadArray( ON_ObjectArray& a) { a.Empty(); int i, count; bool rc = ReadInt( &count ); if (rc) { a.SetCapacity(count); for ( i = 0; i < count && rc; i++ ) { rc = (1 == ReadObject(a.AppendNew())); if (!rc) { a.Remove(); break; } } } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a) { a.Empty(); ON_Layer* layer; int i, count; bool rc = ReadInt( &count ); if (rc) { a.SetCapacity(count); for ( i = 0; i < count && rc; i++ ) { layer = 0; ON_Object* p = 0; rc = (1==ReadObject(&p)); if (rc) { layer = ON_Layer::Cast(p); } if (!rc || 0 == layer) { if ( p ) delete p; rc = false; break; } a.Append(layer); } } return rc; } bool ON_BinaryArchive::ReadArray( ON_ClassArray& a) { a.Empty(); int i, count; bool rc = ReadInt( &count ); if (rc) { a.SetCapacity(count); for ( i = 0; i < count && rc; i++ ) { rc = a.AppendNew().Read(*this); } } return rc; } bool ON_BinaryArchive::ReadArray( ON_ClassArray& a) { a.Empty(); int i, count; bool rc = ReadInt( &count ); if (rc) { a.SetCapacity(count); for ( i = 0; i < count && rc; i++ ) { rc = a.AppendNew().Read(*this); } } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a) { a.Empty(); int i, count; bool rc = ReadInt( &count ); if (rc) { a.SetCapacity(count); for ( i = 0; i < count && rc; i++ ) { rc = a.AppendNew().Read(*this); } } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); int i; for ( i = 0; i < count && rc; i++ ) { rc = ReadDisplayMaterialRef(a.AppendNew()); } } return rc; } bool ON_BinaryArchive::ReadArray( ON_ClassArray& a) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); int i; for ( i = 0; i < count && rc; i++ ) { rc = ReadString( a.AppendNew() ); } } return rc; } bool ON_BinaryArchive::ReadArray( ON_ClassArray& a) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); int i; for ( i = 0; i < count && rc; i++ ) { rc = ReadString( a.AppendNew() ); } } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int i, count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { rc = WriteDisplayMaterialRef( a[i] ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_ClassArray& a ) { int i, count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { rc = WriteString( a[i] ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_ClassArray& a ) { int i, count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { rc = WriteString( a[i] ); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { #if defined(ON_COMPILER_MSC) // Disable the MSC /W4 "conditional expression is constant" warning // about sizeof(*c) == sizeof(*b). Since this code has to run on machines // where sizeof(bool) can be 1, 2, 4, or 8 bytes, the test is necessary. #pragma ON_PRAGMA_WARNING_PUSH #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) #endif a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); char* c = 0; bool* b = a.Array(); if ( sizeof(*c) == sizeof(*b) ) { // 8 bit "bool" on this compiler c = (char*)b; } else if ( b ) { // bigger "bool" on this compiler c = (char*)onmalloc(count*sizeof(*c)); } rc = ReadChar( count, c ); if ( rc ) { if ( c == (char*)b ) { a.SetCount(count); } else if ( c ) { int i; for ( i = 0; i < count; i++ ) { a.Append(c[i]?true:false); } onfree(c); } } } return rc; #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_POP #endif } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadChar( count, a.Array() ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadShort( count, a.Array() ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadInt( count, a.Array() ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadFloat( count, a.Array() ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadDouble( count, a.Array() ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadByte( 4*count, (unsigned char*)a.Array() ); // ReadByte prevents big endian swaps if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadDouble( 2*count, &a.Array()->x ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadDouble( 3*count, &a.Array()->x ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadDouble( 4*count, &a.Array()->x ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadDouble( 2*count, &a.Array()->x ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadDouble( 3*count, &a.Array()->x ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); int i; for ( i = 0; i < count && rc; i++ ) { rc = ReadXform(a.AppendNew()); } } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadFloat( 2*count, &a.Array()->x ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadFloat( 3*count, &a.Array()->x ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadFloat( 4*count, &a.Array()->x ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadFloat( 2*count, &a.Array()->x ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadFloat( 3*count, &a.Array()->x ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); ON_UUID uuid; int i, count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); for ( i = 0; i < count && rc; i++ ) { rc = ReadUuid( uuid ); if ( rc ) a.Append(uuid); } } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); ON_UuidIndex idi; int i, count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); for ( i = 0; i < count && rc; i++ ) { rc = ReadUuid( idi.m_id ); if ( rc ) { rc = ReadInt(&idi.m_i); if(rc) a.Append(idi); } } } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); ON_UuidPtr idi; int i, count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); for ( i = 0; i < count && rc; i++ ) { rc = ReadUuid( idi.m_id ); if ( rc ) { ON__UINT64 ptr = 0; // 64 bits on all platforms rc = ReadBigInt(&ptr); if (rc) { idi.m_ptr = (ON__UINT_PTR)ptr; a.Append(idi); } } } } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int i, count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { rc = WriteUuid( a[i] ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int i, count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { rc = WriteUuid( a[i].m_id ); if (rc) rc = WriteInt( a[i].m_i ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int i, count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { rc = WriteUuid( a[i].m_id ); if (rc) { ON__UINT64 ptr = (ON__UINT64)a[i].m_ptr; rc = WriteBigInt(ptr); // 64 bits on all platforms } } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); ON_LinetypeSegment seg; int i, count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); for ( i = 0; i < count && rc; i++ ) { rc = ReadLinetypeSegment( seg ); if ( rc ) a.Append(seg); } } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int i, count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); for ( i = 0; i < count && rc; i++ ) { rc = WriteLinetypeSegment( a[i] ); } return rc; } bool ON_BinaryArchive::ReadLinetypeSegment(ON_LinetypeSegment& seg) { seg = ON_LinetypeSegment::OneMillimeterLine; bool rc = ReadDouble(&seg.m_length); if (rc) { // ON_LinetypeSegment::eSegType::Unset was not added initialy, so juggling values is required for // Unset, stLine and stSpace. Any future values with work without // juggling. unsigned int i = 0; rc = ReadInt(&i); switch (i) { case 0: i = static_cast(ON_LinetypeSegment::eSegType::stLine); break; case 1: i = static_cast(ON_LinetypeSegment::eSegType::stSpace); break; case ON_UNSET_UINT_INDEX: i = static_cast(ON_LinetypeSegment::eSegType::Unset); break; } seg.m_seg_type = ON_LinetypeSegment::SegmentTypeFromUnsigned(i); } return rc; } bool ON_BinaryArchive::WriteLinetypeSegment( const ON_LinetypeSegment& seg) { // do not add chunk info here bool rc = WriteDouble(seg.m_length); if (rc) { // ON_LinetypeSegment::eSegType::Unset was not added initialy, so juggling values is required for // Unset, stLine and stSpace. Any future values with work without // juggling. unsigned int i; switch (seg.m_seg_type) { case ON_LinetypeSegment::eSegType::Unset: i = ON_UNSET_UINT_INDEX; break; case ON_LinetypeSegment::eSegType::stLine: i = 0; break; case ON_LinetypeSegment::eSegType::stSpace: i = 1; break; default: i = static_cast(seg.m_seg_type); break; } rc = WriteInt(i); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity( count ); rc = ReadDouble( 2*count, &a.Array()->k1 ); if ( rc ) a.SetCount(count); } return rc; } bool ON_BinaryArchive::ReadArray( ON_SimpleArray& a ) { a.Empty(); int count = 0; bool rc = ReadInt( &count ); if ( rc && count > 0 ) { a.SetCapacity(count); for ( int i = 0; i < count && rc ; i++ ) { rc = a.AppendNew().Read(*this); if (!rc) a.Remove(); } } return rc; } bool ON_BinaryArchive::WriteBool( bool b ) { // Dale Lear - August 2017 RH-40908 // Some Release builds using Visual Studio 2017 were optimizing all this code away // and writing the value of b as a byte. This was allowing values besides 0 or 1 // to be saved in the file. // I added WriteBoolTrue() and WriteBoolFalse(). //// Code that was writing values besides 0 or 1 in Release builds. ////unsigned char c = (b?1:0); ////return WriteChar(c); // New code to avoid error described above. return (b) ? WriteBoolTrue() : WriteBoolFalse(); } bool ON_BinaryArchive::WriteBoolTrue() { // true is saved a a singel byte with value = 1. const unsigned char c = 1; return WriteChar(c); } bool ON_BinaryArchive::WriteBoolFalse() { // false is saved a a single byte with value = 0. const unsigned char c = 0; return WriteChar(c); } bool ON_BinaryArchive::ReadBool( bool *b ) { unsigned char c; bool rc = ReadChar(&c); if (rc && b) { if ( c != 0 && c != 1 ) { const unsigned int version_6_0_August_24_2017 = ON_VersionNumberConstruct(6, 0, 2017, 8, 24, 0); if ( ArchiveOpenNURBSVersion() < version_6_0_August_24_2017 ) { // See RH-40941 and RH-40941 for examples when c is not 0 and not 1 but // file writing code was correctly written by the developer. c = 1; } else { // WriteBool always writes a 0 or 1. So either your code // has a bug, the file is corrupt, the the file pointer // is where it should be. ON_ERROR("ON_BinaryArchive::ReadBool - bool value != 0 and != 1"); rc = false; } } *b = c?true:false; } return rc; } bool ON_BinaryArchive::WriteChar( // Write an array of 8 bit chars size_t count, // number of chars to write const char* p ) { return WriteByte( count, p ); } bool ON_BinaryArchive::WriteChar( // Write an array of 8 bit unsigned chars size_t count, // number of unsigned chars to write const unsigned char* p ) { return WriteByte( count, p ); } bool ON_BinaryArchive::WriteChar( // Write a single 8 bit char char c ) { return WriteByte( 1, &c ); } bool ON_BinaryArchive::WriteChar( // Write a single 8 bit unsigned char unsigned char c ) { return WriteByte( 1, &c ); } bool ON_BinaryArchive::WriteInt16( // Write an array of 16 bit shorts size_t count, // number of shorts to write const ON__INT16* p ) { bool rc = true; if (m_endian == ON::endian::big_endian) { if ( count > 0 ) { const char* b = (const char*)p; while ( rc && count-- ) { rc = WriteByte( 1, b+1 ); if (rc) rc = WriteByte( 1, b ); b++; b++; } } } else { rc = WriteByte( count<<1, p ); } return rc; } bool ON_BinaryArchive::WriteShort( // Write an array of 16 bit shorts size_t count, // number of shorts to write const short* p ) { #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_PUSH // Disable the MSC /W4 "conditional expression is constant" warning // about 2 == sizeof(*p). Since this code has to run on machines // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary. #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) #endif bool rc; if ( 2 == sizeof(*p) ) { rc = WriteInt16( count, (const ON__INT16*)p ); } else { rc = true; ON__INT16 i16; size_t j; for ( j = 0; j < count; j++ ) { i16 = (ON__INT16)(*p++); rc = WriteInt16( 1, &i16); } } return rc; #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_POP #endif } bool ON_BinaryArchive::WriteShort( // Write an array of 16 bit unsigned shorts size_t count, // number of shorts to write const unsigned short* p ) { return WriteShort( count, (const short*)p ); } bool ON_BinaryArchive::WriteShort( // Write a single 16 bit short short s ) { return WriteShort( 1, &s ); } bool ON_BinaryArchive::WriteShort( // Write a single 16 bit unsigned short unsigned short s ) { return WriteShort( 1, &s ); } bool ON_BinaryArchive::WriteInt32( // Write an array of 32 bit integers size_t count, // number of ints to write const ON__INT32* p ) { bool rc = true; if (m_endian == ON::endian::big_endian) { if ( count > 0 ) { const char* b = (const char*)p; while ( rc && count-- ) { rc = WriteByte( 1, b+3 ); if (rc) rc = WriteByte( 1, b+2 ); if (rc) rc = WriteByte( 1, b+1 ); if (rc) rc = WriteByte( 1, b ); b += 4; } } } else { rc = WriteByte( count<<2, p ); } return rc; } bool ON_BinaryArchive::ReadInt64( // Read an array of 64 bit integers size_t count, // number of 64 bit integers to read ON__INT64* p ) { bool rc = ReadByte( count<<3, p ); if (rc && m_endian == ON::endian::big_endian) { unsigned char* b=(unsigned char*)p; unsigned char c; while(count--) { c = b[0]; b[0] = b[7]; b[7] = c; c = b[1]; b[1] = b[6]; b[6] = c; c = b[2]; b[2] = b[5]; b[5] = c; c = b[3]; b[3] = b[4]; b[4] = c; b += 8; } } return rc; } bool ON_BinaryArchive::WriteInt64( // Write an array of 64 bit integers size_t count, // number of ints to write const ON__INT64* p ) { bool rc = true; if (m_endian == ON::endian::big_endian) { if ( count > 0 ) { const char* b = (const char*)p; while ( rc && count-- ) { rc = WriteByte( 1, b+7 ); if (rc) rc = WriteByte( 1, b+6 ); if (rc) rc = WriteByte( 1, b+5 ); if (rc) rc = WriteByte( 1, b+4 ); if (rc) rc = WriteByte( 1, b+3 ); if (rc) rc = WriteByte( 1, b+2 ); if (rc) rc = WriteByte( 1, b+1 ); if (rc) rc = WriteByte( 1, b ); b += 8; } } } else { rc = WriteByte( count<<3, p ); } return rc; } bool ON_BinaryArchive::WriteInt( // Write an array of integers size_t count, // number of ints to write const int* p ) { #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_PUSH // Disable the MSC /W4 "conditional expression is constant" warning // about 4 == sizeof(*p). Since this code has to run on machines // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary. #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) #endif bool rc; if ( 4 == sizeof(*p) ) { rc = WriteInt32( count, (const ON__INT32*)p ); } else { ON__INT32 i32; size_t j; rc = true; for ( j = 0; j < count && rc; j++ ) { i32 = (ON__INT32)(*p++); rc = WriteInt32( 1, &i32 ); } } return rc; #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_POP #endif } bool ON_BinaryArchive::WriteSize(size_t sz) { unsigned int u = (unsigned int)sz; return WriteInt(u); } bool ON_BinaryArchive::ReadSize(size_t* sz) { unsigned int u = 0; bool rc = ReadInt(&u); if (rc) *sz = u; return rc; } bool ON_BinaryArchive::WriteBigSize(size_t sz) { ON__UINT64 u = (ON__UINT64)sz; return WriteInt64(1,(ON__INT64*)&u);; } bool ON_BinaryArchive::ReadBigSize( size_t* sz ) { ON__UINT64 u; bool rc = ReadInt64(1,(ON__INT64*)&u); if (rc) *sz = (size_t)u; return rc; } bool ON_BinaryArchive::WriteBigTime(time_t t) { ON__UINT64 u = (ON__UINT64)t; return WriteInt64(1,(ON__INT64*)&u); } bool ON_BinaryArchive::ReadBigTime( time_t* t ) { ON__UINT64 u; bool rc = ReadInt64(1,(ON__INT64*)&u); if (rc) *t = (time_t)u; return rc; } bool ON_BinaryArchive::WriteInt( // Write an array of 32 bit integers size_t count, // number of ints to write const unsigned int* p ) { return WriteInt( count, (const int*)p ); } bool ON_BinaryArchive::WriteInt( // Write a single 32 bit integer int i ) { return WriteInt( 1, &i ); } bool ON_BinaryArchive::WriteInt( // Write a single 32 bit integer unsigned int i ) { return WriteInt( 1, &i ); } bool ON_BinaryArchive::WriteBigInt( // Write an array of 64 bit integers size_t count, const ON__INT64* p ) { return WriteInt64(count,p); } bool ON_BinaryArchive::WriteBigInt( // Write an array of 64 bit integers size_t count, const ON__UINT64* p ) { return WriteInt64(count,(const ON__INT64*)p); } bool ON_BinaryArchive:: WriteBigInt( // Write a single 64 bit integer ON__INT64 i ) { return WriteInt64(1,&i); } bool ON_BinaryArchive::WriteBigInt( // Write a single 64 bit unsigned integer ON__UINT64 u ) { return WriteInt64(1,(const ON__INT64*)&u); } bool ON_BinaryArchive::WriteLong( // Write an array of longs size_t count, // number of longs to write const long* p ) { #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_PUSH // Disable the MSC /W4 "conditional expression is constant" warning // about 4 == sizeof(*p). Since this code has to run on machines // where sizeof(*p) can be 2, 4, or 8 bytes, the test is necessary. #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) #endif bool rc; if ( 4 == sizeof(*p) ) { rc = WriteInt32( count, (const ON__INT32*)p ); } else { ON__INT32 i32; size_t j; rc = true; for ( j = 0; j < count && rc; j++ ) { i32 = (ON__INT32)(*p++); rc = WriteInt32( 1, &i32 ); } } return rc; #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_POP #endif } bool ON_BinaryArchive::WriteLong( // Write an array of longs size_t count, // number of longs to write const unsigned long* p ) { return WriteLong( count, (const long*)p ); } bool ON_BinaryArchive::WriteLong( // Write a single long long i ) { return WriteLong( 1, &i ); } bool ON_BinaryArchive::WriteLong( // Write a single unsigned long unsigned long i ) { return WriteLong( 1, &i ); } bool ON_BinaryArchive::WriteFloat( // Write a number of IEEE floats size_t count, // number of doubles const float* p ) { // floats and integers have same size and endian issues return WriteInt( count, (const int*)p ); } bool ON_BinaryArchive::WriteFloat( // Write a single float float f ) { return WriteFloat( 1, &f ); } bool ON_BinaryArchive::WriteDouble( // Write a single double size_t count, // number of doubles const double* p ) { bool rc = true; if (m_endian == ON::endian::big_endian) { if ( count > 0 ) { const char* b = (const char*)p; while ( rc && count-- ) { rc = WriteByte( 1, b+7 ); if (rc) rc = WriteByte( 1, b+6 ); if (rc) rc = WriteByte( 1, b+5 ); if (rc) rc = WriteByte( 1, b+4 ); if (rc) rc = WriteByte( 1, b+3 ); if (rc) rc = WriteByte( 1, b+2 ); if (rc) rc = WriteByte( 1, b+1 ); if (rc) rc = WriteByte( 1, b ); b += 8; } } } else { rc = WriteByte( count<<3, p ); } return rc; } bool ON_BinaryArchive::WriteComponentIndex( const ON_COMPONENT_INDEX& ci ) { bool rc = WriteInt( ci.m_type ); if (rc) rc = WriteInt( ci.m_index ); // do not add additional writes - you will break old file IO return rc; } bool ON_BinaryArchive::ReadComponentIndex( ON_COMPONENT_INDEX& ci ) { int t; ci.m_type = ON_COMPONENT_INDEX::invalid_type; ci.m_index = 0; bool rc = ReadInt( &t ); if (rc) { rc = ReadInt( &ci.m_index ); if (rc) { ci.m_type = ON_COMPONENT_INDEX::Type(t); } // do not add additional read - you will break old file IO } return rc; } bool ON_BinaryArchive::WriteDouble( // Write a single double const double x ) { return WriteDouble( 1, &x ); } bool ON_BinaryArchive::WriteColor( const ON_Color& color ) { unsigned int colorref = color; return WriteByte( 4, (const unsigned char*)&colorref ); // WriteByte prevents big endian swaps } bool ON_BinaryArchive::WriteColor(const ON_4fColor& color) { if (!WriteFloat(color.Red())) return false; if (!WriteFloat(color.Green())) return false; if (!WriteFloat(color.Blue())) return false; if (!WriteFloat(color.Alpha())) return false; return true; } bool ON_BinaryArchive::WritePoint ( const ON_2dPoint& p ) { return WriteDouble( 2, &p.x ); } bool ON_BinaryArchive::WritePoint ( const ON_3dPoint& p ) { return WriteDouble( 3, &p.x ); } bool ON_BinaryArchive::WritePoint ( const ON_4dPoint& p ) { return WriteDouble( 4, &p.x ); } bool ON_BinaryArchive::WriteVector ( const ON_2dVector& v ) { return WriteDouble( 2, &v.x ); } bool ON_BinaryArchive::WriteVector ( const ON_3dVector& v ) { return WriteDouble( 3, &v.x ); } bool ON_BinaryArchive::WriteDisplayMaterialRef( const ON_DisplayMaterialRef& dmr ) { bool rc = WriteUuid( dmr.m_viewport_id ); if (rc) rc = WriteUuid( dmr.m_display_material_id ); return rc; } bool ON_BinaryArchive::WriteUuid( const ON_UUID& uuid ) { bool rc = WriteInt32( 1, (const ON__INT32*)(&uuid.Data1) ); if (rc) rc = WriteInt16( 1, (const ON__INT16*)(&uuid.Data2) ); if (rc) rc = WriteInt16( 1, (const ON__INT16*)(&uuid.Data3) ); if (rc) rc = WriteByte( 8, uuid.Data4 ); return rc; } bool ON_BinaryArchive::WriteTime( const struct tm& utc ) { // utc = coordinated universal time ( a.k.a GMT, UTC ) // (From ANSI C time() and gmtime().) // The checks are here so we can insure files don't contain // garbage dates and ReadTime() can treat out of bounds // values as file corruption errors. int i; i = (int)utc.tm_sec; if ( i < 0 || i > 60 ) i = 0; bool rc = WriteInt( i ); i = (int)utc.tm_min; if ( i < 0 || i > 60 ) i = 0; if ( rc ) rc = WriteInt( i ); i = (int)utc.tm_hour; if ( i < 0 || i > 24 ) i = 0; if ( rc ) rc = WriteInt( i ); i = (int)utc.tm_mday; if ( i < 0 || i > 31 ) i = 0; if ( rc ) rc = WriteInt( i ); i = (int)utc.tm_mon; if ( i < 0 || i > 12 ) i = 0; if ( rc ) rc = WriteInt( i ); // no year restrictions because dates are used in archeological userdata i = (int)utc.tm_year; if ( rc ) rc = WriteInt( i ); i = (int)utc.tm_wday; if ( i < 0 || i > 7 ) i = 0; if ( rc ) rc = WriteInt( i ); i = (int)utc.tm_yday; if ( i < 0 || i > 366 ) i = 0; if ( rc ) rc = WriteInt( i ); return rc; } bool ON_BinaryArchive::WriteString( // Write nullptr terminated UTF-8 encoded unicode string const char* sUTF8 ) { size_t string_utf8_element_count = 0; if ( sUTF8 ) { while(sUTF8[string_utf8_element_count]) string_utf8_element_count++; if ( string_utf8_element_count ) string_utf8_element_count++; } ON__UINT32 ui32 = (ON__UINT32)string_utf8_element_count; bool rc = WriteInt32(1,(ON__INT32*)&ui32); if ( rc && string_utf8_element_count > 0 ) rc = WriteByte( string_utf8_element_count, sUTF8 ); return rc; } bool ON_BinaryArchive::WriteString( // Write nullptr terminated UTF-8 encoded unicode string const unsigned char* sUTF8 ) { return WriteString( (const char*)sUTF8 ); } bool ON_BinaryArchive::WriteUTF16String( const unsigned short* sUTF16 ) { // Write nullptr terminated UTF-16 encoded unicode string size_t string_utf16_element_count = 0; if ( sUTF16 ) { while(sUTF16[string_utf16_element_count]) string_utf16_element_count++; if ( string_utf16_element_count ) string_utf16_element_count++; } ON__UINT32 ui32 = (ON__UINT32)string_utf16_element_count; bool rc = WriteInt32(1,(ON__INT32*)&ui32); if ( rc && string_utf16_element_count > 0 ) { rc = WriteShort( string_utf16_element_count, sUTF16 ); } return rc; } bool ON_BinaryArchive::WriteString( const ON_String& sUTF8 ) { // The ON_String::IsValid call prevents corrupt strings from breaking file IO // The parameter MUST be false here. sUTF8.IsValid(false); size_t string_utf8_element_count = sUTF8.Length(); if ( string_utf8_element_count ) string_utf8_element_count++; ON__UINT32 ui32 = (ON__UINT32)string_utf8_element_count; bool rc = WriteInt32(1,(ON__INT32*)&ui32); if ( rc && string_utf8_element_count > 0 ) rc = WriteByte( string_utf8_element_count, sUTF8.Array() ); return rc; } bool ON_BinaryArchive::WriteWideString( const ON_wString& wide_string ) { return WriteWideString(static_cast(wide_string), wide_string.Length()); } bool ON_BinaryArchive::WriteWideString( const wchar_t* sWideChar, int sWideChar_count ) { char sUTF8_buffer[256]; if (!BeginWrite3dmBigChunk(TCODE_UTF8_STRING_CHUNK,0)) return false; bool rc = false; for (;;) { const unsigned char format = (nullptr != sWideChar && sWideChar_count > 0 && sWideChar_count < 2147483647) ? 1 // 1 = uncompressed : 0; // 0 = empty if (!WriteChar(format)) break; if (0 == format) { rc = true; break; } #if 1 == ON_SIZEOF_WCHAR_T // wchar_t = char rc = WriteChar(sWideChar_count, (const char*)sWideChar); #else // Convert to UTF8 const wchar_t* sLastWideChar = sWideChar+sWideChar_count; int bTestByteOrder = true; const wchar_t* sNextWideChar; const int sUTF8_capacity = (int)(sizeof(sUTF8_buffer) / sizeof(sUTF8_buffer[0])); unsigned int error_status; const unsigned int error_mask = 0xFFFFFFFC; ON__UINT32 error_code_point = 0xFFFD; for (;;) { error_status = 0; sNextWideChar = nullptr; int sUTF8_count = ON_ConvertWideCharToUTF8( bTestByteOrder, sWideChar, sWideChar_count, sUTF8_buffer, sUTF8_capacity, &error_status, error_mask, error_code_point, &sNextWideChar ); if (sUTF8_count <= 0 || sUTF8_count > sUTF8_capacity) { ON_ERROR("Invalid wide char string - incomplete write."); // Not serious enough to terminate writing - true will be returned. break; } if (false == WriteChar(sUTF8_count, sUTF8_buffer)) { rc = false; break; } if (0 == (error_status & 0x03) && sNextWideChar == sWideChar + sWideChar_count) { break; // finished writing UTF8 string. } if (2 == (error_status & 0x03) && sWideChar < sNextWideChar && sNextWideChar < sLastWideChar) { int c = (int)(sLastWideChar - sNextWideChar); if (c < sWideChar_count) { sWideChar = sNextWideChar; sWideChar_count = c; bTestByteOrder = 0; // subsequent conversion should never test byte order continue; } } ON_ERROR("Invalid wide char string - incomplete write."); // Not serious enough to terminate writing - true will be returned. #endif break; } rc = true; break; } if (!EndWrite3dmChunk()) rc = false; return rc; } bool ON_BinaryArchive::ReadWideString( ON_wString& wide_string ) { wide_string = ON_wString::EmptyString; ON__INT64 big_value = 0; for (int pass = 0; pass < 2; pass++) { unsigned int typecode = 0; big_value = 0; if (0 == pass) { if (false == PeekAt3dmBigChunkType(&typecode, &big_value)) return false; } else { if (false == BeginRead3dmBigChunk(&typecode, &big_value)) return false; } if (TCODE_UTF8_STRING_CHUNK != typecode || big_value < 5) { if ( 0 != pass ) EndRead3dmChunk(); return false; } } wchar_t sWideChar_buffer[512]; char char_buffer[2][sizeof(sWideChar_buffer)/sizeof(sWideChar_buffer[0])]; const int buffer_capacity = (int)(sizeof(sWideChar_buffer)/sizeof(sWideChar_buffer[0])); int char_buffer_count[2] = { 0 }; int chunk_byte_count = (int)(big_value-5); bool rc = false; for (;;) { unsigned char format = 0; if (!ReadChar(&format)) break; if (0 == format) { if (0 == chunk_byte_count) rc = true; break; } if ( 1 != format ) break; #if 1 == ON_SIZEOF_WCHAR_T wchar_t* sWideChar = wide_string.ReserveArray(chunk_byte_count); rc = ReadChar(chunk_byte_count, (char*)sWideChar); if (rc) wide_string.SetLength(chunk_byte_count); #else const int bTestByteOrder = false; const char* sNextUTF8; unsigned int error_status; const unsigned int error_mask = 0xFFFFFFFC; const ON__UINT32 error_code_point = 0xFFFD; for (int char_buffer_index=0; /*true*/; char_buffer_index = 1-char_buffer_index) { // Read UTF8 and convert to wide_char char_buffer_count[1-char_buffer_index] = 0; int char_count = buffer_capacity - char_buffer_count[char_buffer_index]; if (char_count > chunk_byte_count) char_count = chunk_byte_count; if (char_count > 0) { if (false == ReadChar(char_count, char_buffer[char_buffer_index] + char_buffer_count[char_buffer_index])) break; chunk_byte_count -= char_count; char_buffer_count[char_buffer_index] += char_count; } else if (chunk_byte_count > 0) { // This should never happend and is probably a bug. // If it occurs due to invalid input, describe the situation // in this comment. ON_ERROR("char buffer reading stalled."); break; } if (chunk_byte_count > 0 && buffer_capacity == char_buffer_count[char_buffer_index]) { // There are more bytes to read from the archive. char c = char_buffer[char_buffer_index][buffer_capacity - 1]; if (c < 0 || c > 127) { // c is the second or later byte in a UTF-8 muli-byte encoding. // // There are more bytes to read from the archive and it is possible there are more // trailing bytes to be read from the archive. Back up to the beginning of the // this multi-byte encoded code point, move the bytes to char_buffer[1-char_buffer_index], // and decode them after the next read. for (int i = buffer_capacity - 2; i > 0; i--) { c = char_buffer[char_buffer_index][i]; if (c >= 0 && c <= 127) { // c is the beginning of a UTF-8 encoding sequence. char_buffer_count[1 - char_buffer_index] = buffer_capacity - i; char_buffer_count[char_buffer_index] = i; for (i = 0; i < char_buffer_count[1 - char_buffer_index]; i++) char_buffer[1 - char_buffer_index][i] = char_buffer[char_buffer_index][char_buffer_count[char_buffer_index] + i]; break; } } if (c < 0 || c > 127) { // 512 bytes of with the hight bit set is not what was written and it cannot be an "overly long" UTF-8 encoding. // The arcive contents have been damaged. ON_ERROR("archive contents damaged."); break; } } } error_status = 0; sNextUTF8 = nullptr; int WideChar_count = ON_ConvertUTF8ToWideChar( bTestByteOrder, char_buffer[char_buffer_index], char_buffer_count[char_buffer_index], sWideChar_buffer, buffer_capacity, &error_status, error_mask, error_code_point, &sNextUTF8 ); if (WideChar_count <= 0 || WideChar_count > buffer_capacity) { ON_ERROR("Invalid UTF-8 encoding - incomplete string read."); // Not serious enough to terminate reading. rc = true; break; } wide_string.Append(sWideChar_buffer,WideChar_count); if ( chunk_byte_count > 0 ) continue; rc = true; break; } #endif break; } if (!EndRead3dmChunk()) rc = false; return rc; } bool ON_BinaryArchive::WriteString( const ON_wString& s ) { // No matter what size wchar_t is, this function writes a UTF-16 encoded string // to the archive. #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_PUSH // Disable the MSC /W4 "conditional expression is constant" warning // about 2 == sizeof(wchar_t). Since this code has to run on machines // where sizeof(wchar_t) can be 2, 4, or ... bytes, the test is necessary. #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) #endif // The ON_wString::IsValid call prevents corrupt strings from breaking file IO // The parameter MUST be false here. s.IsValid(false); // NOTE WELL: // In some cases there are embedded nulls in strings. size_t string_element_count = s.Length(); if ( string_element_count > 0) string_element_count++; bool rc = false; if ( string_element_count <= 1 ) { ON__UINT32 ui32 = 0; rc = WriteInt32(1,(ON__INT32*)&ui32); } else if ( 2 == sizeof(wchar_t) && string_element_count > 0 ) { ON__UINT32 ui32 = (ON__UINT32)string_element_count; rc = WriteInt32(1,(ON__INT32*)&ui32); if (rc) rc = WriteInt16( string_element_count, (const ON__INT16*)s.Array() ); } else if ( 4 == sizeof(wchar_t) && string_element_count > 0 ) { // Assume the string is UTF-32 encoded (this is the case for some gcc implementations). const int bTestByteOrder = false; const ON__UINT32* sUTF32 = (const ON__UINT32*)s.Array(); const int sUTF32_count = (int)(string_element_count-1); const unsigned int error_mask = 0xFFFFFFFF; const ON__UINT32 error_code_point = 0xFFFD; unsigned int error_status = 0; const int sUTF16_count = ON_ConvertUTF32ToUTF16( bTestByteOrder, sUTF32, sUTF32_count, 0, // ON__UINT16* sUTF16, 0, // int sUTF16_count, &error_status, error_mask, error_code_point, 0 // const ON__UINT32** sNextUTF32 ); if ( sUTF16_count > 0 ) { error_status = 0; ON_SimpleArray utf16_buffer(sUTF16_count+1); utf16_buffer.SetCount(sUTF16_count+1); const int sUTF16_count1 = ON_ConvertUTF32ToUTF16( bTestByteOrder, sUTF32, sUTF32_count, utf16_buffer.Array(), utf16_buffer.Count(), &error_status, error_mask, error_code_point, 0 // const ON__UINT32** sNextUTF32 ); if ( sUTF16_count1 == sUTF16_count ) { utf16_buffer[sUTF16_count] = 0; const ON__UINT32 ui32 = (ON__UINT32)(sUTF16_count+1); rc = WriteInt32(1,(const ON__INT32*)&ui32); if ( rc && ui32 > 0 ) rc = WriteInt16( ui32, (const ON__INT16*)utf16_buffer.Array() ); } } } return rc; #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_POP #endif } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_PUSH // Disable the MSC /W4 "conditional expression is constant" warning // about sizeof(*c) == sizeof(*b). Since this code has to run on machines // where sizeof(bool) can be 1, 2, 4, or 8 bytes, the test is necessary. #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) #endif int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { char* p = 0; const char* c = 0; const bool* b = a.Array(); if ( sizeof(*c) == sizeof(*b) ) { // 8 bit "bool" on this compiler c = (char*)(b); } else if ( b ) { // bigger "bool" on this compiler p = (char*)onmalloc(count*sizeof(*p)); int i; for ( i = 0; i < count; i++ ) p[i] = (b[i]?1:0); c = p; } rc = WriteChar( count, c ); if ( p ) onfree(p); } return rc; #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_POP #endif } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteChar( count, a.Array() ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteShort( count, a.Array() ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteInt( count, a.Array() ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteFloat( count, a.Array() ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteDouble( count, a.Array() ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteByte( 4*count, (const unsigned char*)a.Array() ); // WriteByte prevents big endian swaps } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteDouble( count*2, &a.Array()->x ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteDouble( count*3, &a.Array()->x ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteDouble( count*4, &a.Array()->x ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteDouble( count*2, &a.Array()->x ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteDouble( count*3, &a.Array()->x ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { int i; for ( i = 0; i < count && rc; i++ ) rc = WriteXform(a[i]); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteFloat( count*2, &a.Array()->x ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteFloat( count*3, &a.Array()->x ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteFloat( count*4, &a.Array()->x ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteFloat( count*2, &a.Array()->x ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteFloat( count*3, &a.Array()->x ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); if ( rc && count > 0 ) { rc = WriteDouble( count*2, &a.Array()->k1 ); } return rc; } bool ON_BinaryArchive::WriteArray( const ON_SimpleArray& a ) { int count = a.Count(); if ( count < 0 ) count = 0; bool rc = WriteInt( count ); for ( int i = 0; i < count && rc ; i++ ) { rc = a[i].Write(*this); } return rc; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// bool ON_BinaryArchive::WriteObject( const ON_Object* o ) { bool rc = false; if ( o ) rc = WriteObject(*o); else { // nullptr object has nil UUID and no date rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS, 0 ); if (rc) { rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_UUID, 0 ); if ( rc ) { rc = WriteUuid( ON_nil_uuid ); if ( !EndWrite3dmChunk() ) // end of TCODE_OPENNURBS_CLASS_UUID chunk rc = false; } if ( !EndWrite3dmChunk() ) rc = false; } } return rc; } bool ON_BinaryArchive::ShouldSerializeUserDataItem( ON_UUID application_id, ON_UUID item_id ) const { if (application_id == ON_nil_uuid) return false; unsigned int i = m_user_data_filter.UnsignedCount(); if (i <= 0) return true; // {31F55AA3-71FB-49f5-A975-757584D937FF} static const ON_UUID ON_V4V5_MeshNgonUserData_ID = { 0x31F55AA3, 0x71FB, 0x49f5, { 0xA9, 0x75, 0x75, 0x75, 0x84, 0xD9, 0x37, 0xFF } }; // Userdata that must be saved even when userdata saving is "off". // Please discuss any changes with Dale Lear. In particular, // "critical" is used in a narrow sense and modifying this // function to save in form of plug-in's user data saved is // not appropriate. The definition of the user data class must // be in the opennurbs library and its purpose must be to extend // a core opennurbs data structure, usually because it was the // only way to add information to core opennurbs data structure // and not break the public C++ SDK. unsigned int opennurbs_userdata_application_version = ON_IsOpennurbsApplicationId(application_id); if (opennurbs_userdata_application_version >= 4) { // The user data is opennurbs "core" user data unsigned int opennurbs_file_application_version = Archive3dmVersion(); if (opennurbs_file_application_version > 10 && 0 == opennurbs_file_application_version % 10) opennurbs_file_application_version /= 10; if ( opennurbs_userdata_application_version >= 5 && opennurbs_file_application_version >= 5 && opennurbs_userdata_application_version <= opennurbs_file_application_version ) { // As of 30 August 2012, all the core opennurbs user data // classes with an application id of ON_opennurbs5_id are // deemed "critical" for V5 files. return true; } if (4 == opennurbs_userdata_application_version && opennurbs_file_application_version <= 5 && opennurbs_file_application_version >= 4 && ON_V4V5_MeshNgonUserData_ID == item_id ) { // ON_V4V5_MeshNgonUserData is the only "critical" core // opennurbs user data with an application id of // ON_opennurbs4_id and is saved only in V4 and V5 files. return true; } } const ON_UserDataItemFilter *f = m_user_data_filter.Array(); if (m_3dm_version > 0) { // After SortUserDataFilter(), m_user_data_filter items go from // lowest precedence to highest precedence. for (i--; i > 0; i--) { if (application_id == f[i].m_application_id) { for (;;) { if (ON_nil_uuid == f[i].m_item_id || item_id == f[i].m_item_id) return f[i].m_bSerialize; if (0 == --i) break; if (!(application_id == f[i].m_application_id)) break; } } } return f[0].m_bSerialize; } // called before reading/writing has started and items are not sorted. bool rc = f[0].m_bSerialize; bool bFoundNilItemId = false; for (i--; i > 0; i--) { if (application_id == f[i].m_application_id) { if (item_id == f[i].m_item_id) return f[i].m_bSerialize; if (false == bFoundNilItemId && ON_nil_uuid == f[i].m_item_id) { bFoundNilItemId = true; rc = f[i].m_bSerialize; } } } return rc; } bool ON_BinaryArchive::ShouldSerializeUserDataDefault() const { // Default setting is always the first element in the m_user_data_filter[] array. return (m_user_data_filter.UnsignedCount() > 0) ? m_user_data_filter[0].m_bSerialize : true; } bool ON_BinaryArchive::SetShouldSerializeUserDataDefault( bool bSerialize ) { if (0 != this->m_3dm_version) return false; // once reading/writing begins, the settings cannot be changed if (0 == m_user_data_filter.UnsignedCount()) { m_user_data_filter.AppendNew().m_bSerialize = bSerialize; } else { m_user_data_filter[0].m_bSerialize = bSerialize; } return true; } bool ON_BinaryArchive::SetShouldSerializeUserDataItem( ON_UUID application_id, ON_UUID item_id, bool bSerializeUserDataItem ) { if (0 != this->m_3dm_version) return false; // once reading/writing begins, the settings cannot be changed if (ON_nil_uuid == application_id) return false; if (0 == m_user_data_filter.UnsignedCount()) { m_user_data_filter.AppendNew().m_bSerialize = true; } if (ON_nil_uuid == application_id && ON_nil_uuid == item_id) { m_user_data_filter[0].m_bSerialize = bSerializeUserDataItem ? true : false; return true; } ON_UserDataItemFilter& f = m_user_data_filter.AppendNew(); f.m_application_id = application_id; f.m_item_id = item_id; f.m_bSerialize = bSerializeUserDataItem ? true : false; f.m_precedence = m_user_data_filter.UnsignedCount() - 1; return true; } bool ON_BinaryArchive::ShouldWriteUserDataItem( const class ON_Object* object, const class ON_UserData* ud ) const { if (nullptr == object) return false; if (nullptr == ud) return false; if (object != ud->Owner()) return false; if (false == ShouldSerializeUserDataItem( ud->m_application_uuid, ud->m_userdata_uuid)) return false; return ud->WriteToArchive(*this, object); } bool ON_BinaryArchive::ShouldSerializeNoUserData() const { const unsigned int count = m_user_data_filter.UnsignedCount(); if (count <= 0) return false; // All user data is serialized const ON_UserDataItemFilter *f = m_user_data_filter.Array(); for (unsigned int i = 0; i < count; i++) { if (false != f[i].m_bSerialize) return false;// some type of user data is serialized } return true; } bool ON_BinaryArchive::ShouldSerializeAllUserData() const { const unsigned int count = m_user_data_filter.UnsignedCount(); if (count <= 0) return true; // All user data is serialized const ON_UserDataItemFilter *f = m_user_data_filter.Array(); for (unsigned int i = 0; i < count; i++) { if (false == f[i].m_bSerialize) return false;// some type of user data is not serialized } return true; } bool ON_BinaryArchive::ShouldSerializeSomeUserData() const { const unsigned int count = m_user_data_filter.UnsignedCount(); if (count <= 0) return false; // All user data is serialized const ON_UserDataItemFilter *f = m_user_data_filter.Array(); for (unsigned int i = 0; i < count; i++) { if (false != f[i].m_bSerialize) return true; // some type of user data is serialized } return false; // No user data is serialized } bool ON_BinaryArchive::ObjectHasUserDataToWrite(const ON_Object* object) const { if (nullptr == object) return false; for (const ON_UserData* ud = object->FirstUserData(); 0 != ud; ud = ud->Next()) { if (ShouldWriteUserDataItem(object, ud)) return true; } return false; } static bool IsCoreUserData( const ON_UserData* ud ) { // Userdata with IO code we trust. if ( 0 == ud ) return false; if (ON_IsRhinoApplicationId(ud->m_application_uuid) > 0) return true; if (ON_IsOpennurbsApplicationId(ud->m_application_uuid) > 0) return true; return false; } bool ON_BinaryArchive::WriteObject( const ON_Object& model_object ) { // writes polymorphic object derived from ON_Object in a way that // it can be recreated from ON_BinaryArchive::ReadObject if ( m_3dm_version >= 1 && m_3dm_version <= 50 ) { const ON::object_type model_object_type = model_object.ObjectType(); switch (model_object_type) { case ON::object_type::curve_object: { if (m_3dm_version > 2) break; const ON_Curve* curve = static_cast(&model_object); if (nullptr == curve) break; if (nullptr != ON_NurbsCurve::Cast(curve)) break; ON_NurbsCurve nurbs_curve; if (curve->GetNurbForm(nurbs_curve) && nurbs_curve.Order() >= 2 && nurbs_curve.CVCount() >= nurbs_curve.Order() && nurbs_curve.Dimension() >= 1 ) { return Internal_WriteObject(nurbs_curve); } } break; case ON::object_type::surface_object: { if (m_3dm_version > 2) break; const ON_Surface* surface = static_cast(&model_object); if (nullptr == surface) break; if (nullptr != ON_NurbsSurface::Cast(surface)) break; ON_NurbsSurface nurbs_surface; if (surface->GetNurbForm(nurbs_surface) && nurbs_surface.Order(0) >= 2 && nurbs_surface.Order(1) >= 2 && nurbs_surface.CVCount(0) >= nurbs_surface.Order(0) && nurbs_surface.CVCount(1) >= nurbs_surface.Order(1) && nurbs_surface.Dimension() >= 1 ) { return Internal_WriteObject(nurbs_surface); } } break; case ON::extrusion_object: { if (m_3dm_version > 4) break; // 29 September 2010 Dale Lear // ON_Extrusion was added in V5. It must be translated // to a brep or surface to save it in a V4 file. const ON_Extrusion* extrusion = ON_Extrusion::Cast(&model_object); if (nullptr == extrusion) break; ON_Object* V4_object = nullptr; if (extrusion->IsCapped() || extrusion->ProfileCount() >= 2) V4_object = extrusion->BrepForm(0); if (nullptr == V4_object) { if (m_3dm_version >= 4) V4_object = extrusion->SumSurfaceForm(0); if (nullptr == V4_object) V4_object = extrusion->NurbsSurface(0); if (nullptr == V4_object) break; } const bool rc = Internal_WriteObject(*V4_object); delete V4_object; return rc; } break; case ON::annotation_object: { const ON_Annotation* V6_annotation = ON_Annotation::Cast(&model_object); if (nullptr != V6_annotation) return Internal_WriteV5AnnotationObject(*V6_annotation, nullptr); if (m_3dm_version > 2) break; const ON_OBSOLETE_V5_Annotation* V5_annotation = ON_OBSOLETE_V5_Annotation::Cast(&model_object); if (nullptr != V5_annotation) return Internal_WriteV2AnnotationObject(*V5_annotation, nullptr); } break; case ON::object_type::text_dot: { if (m_3dm_version > 2) break; const ON_TextDot* text_dot = ON_TextDot::Cast(&model_object); if (nullptr == text_dot) break; ON_OBSOLETE_V2_TextDot V2_text_dot; V2_text_dot.point = text_dot->CenterPoint(); V2_text_dot.m_text = text_dot->PrimaryText(); return Internal_WriteObject(V2_text_dot); } break; } } return Internal_WriteObject(model_object); } bool ON_BinaryArchive::Internal_WriteV5AnnotationObject( const ON_Annotation& V6_annotation, const ON_3dmAnnotationContext* annotation_context ) { if (m_3dm_version < 1 || m_3dm_version > 50) { ON_ERROR("m_3dm_version must be bewtween 1 and 5"); return false; } // TODO: // If V6_annotation has overrides, figure out how to find the archive index. // Right now, dim style override information is lost when saving a V5 3dm archive. const ON_DimStyle* dim_style = nullptr; const ON_UUID model_dim_style_id = V6_annotation.DimensionStyleId(); int V5_3dm_archive_dim_style_index = ON_UNSET_INT_INDEX; if (ON_nil_uuid != model_dim_style_id) { for (unsigned int i = 0; i < m_archive_dim_style_table.UnsignedCount(); i++) { const ON_DimStyle* model_dim_style = m_archive_dim_style_table[i]; if (nullptr != model_dim_style && model_dim_style_id == model_dim_style->Id()) { dim_style = model_dim_style; V5_3dm_archive_dim_style_index = i; break; } } if (nullptr == dim_style) { const ON_DimStyle& system_dim_style = ON_DimStyle::SystemDimstyleFromId(model_dim_style_id); if (model_dim_style_id == system_dim_style.Id()) { dim_style = &system_dim_style; V5_3dm_archive_dim_style_index = system_dim_style.Index(); } } } const ON_DimStyle* override_style = nullptr; if (nullptr == dim_style) { dim_style = &this->ArchiveCurrentDimStyle(); V5_3dm_archive_dim_style_index = dim_style->Index(); } else if (V6_annotation.HasDimensionStyleOverrides() && dim_style->IdIsNotNil() && dim_style->Id() == V6_annotation.DimensionStyleId()) { const ON_DimStyle& v6_override_style = V6_annotation.DimensionStyle(*dim_style); if (v6_override_style.ParentId() == dim_style->Id() && v6_override_style.HasOverrides()) { const ON_SHA1_Hash override_content_hash = v6_override_style.ContentHash(); for (unsigned int i = 0; i < m_archive_dim_style_table.UnsignedCount(); i++) { const ON_DimStyle* model_dim_style = m_archive_dim_style_table[i]; if (dim_style->Id() != model_dim_style->ParentId()) continue; if (override_content_hash != model_dim_style->ContentHash()) continue; override_style = model_dim_style; V5_3dm_archive_dim_style_index = i; break; } } } m_annotation_context.SetReferencedDimStyle( dim_style, override_style, V5_3dm_archive_dim_style_index ); if (nullptr == annotation_context) annotation_context = &m_annotation_context; ON_OBSOLETE_V5_Annotation* V5_annotation = ON_OBSOLETE_V5_Annotation::CreateFromV6Annotation( V6_annotation, annotation_context ); bool rc; if (nullptr != V5_annotation) { if (m_3dm_version <= 2) rc = Internal_WriteV2AnnotationObject(*V5_annotation,annotation_context); else rc = Internal_WriteObject(*V5_annotation); delete V5_annotation; } else { rc = Internal_WriteObject(V6_annotation); } return rc; } bool ON_BinaryArchive::Internal_WriteObject( const ON_Object& model_object ) { const ON_ClassId* pID = model_object.ClassId(); if ( nullptr == pID ) { ON_ERROR("archive_object->ClassId() is nullptr."); return false; } if (false == BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS, 0)) return false; bool rc = false; for (;;) { // TCODE_OPENNURBS_CLASS_UUID chunk contains class's UUID if (false == BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_UUID, 0)) break; const ON_UUID object_class_id = pID->Uuid(); bool bChunkIdOk = WriteUuid(object_class_id); if (false == EndWrite3dmChunk()) // end of TCODE_OPENNURBS_CLASS_UUID chunk bChunkIdOk = false; if (false == bChunkIdOk) break; // TCODE_OPENNURBS_CLASS_DATA chunk contains definition of class if (false == BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_DATA, 0)) break; bool bChunkDataOk = model_object.Write(*this) ? true : false; if (false == bChunkDataOk) { ON_ERROR("archive_object->Write() failed."); } if (false == EndWrite3dmChunk()) // end of TCODE_OPENNURBS_CLASS_DATA chunk bChunkDataOk = false; if (false == bChunkDataOk) break; if ( ObjectHasUserDataToWrite(&model_object) ) { // write user data. Each piece of user data is in a // TCODE_OPENNURBS_CLASS_USERDATA chunk. if (false == WriteObjectUserData(model_object)) break; } // TCODE_OPENNURBS_CLASS_END chunk marks end of class record if (false == BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_END, 0)) break; if (false == EndWrite3dmChunk()) break; rc = true; break; } if (false == EndWrite3dmChunk()) // end of TCODE_OPENNURBS_CLASS chunk rc = false; return rc; } bool ON_BinaryArchive::WriteObjectUserData( const ON_Object& object ) { if ( m_3dm_version < 3 ) { // no user data is saved in V1 and V2 files. return true; } const bool bSaveAllUserData = ShouldSerializeAllUserData(); const bool bSaveSelectedUserData = bSaveAllUserData ? false : ShouldSerializeSomeUserData(); const bool bHasWritableUserData = (bSaveAllUserData || ObjectHasUserDataToWrite(&object)); // writes user data attached to object. bool rc = true; const ON_UserData* ud; ON_UUID userdata_classid; ON_UserData* delete_this_ud = 0; for (ud = object.FirstUserData(); ud && rc; ud = ud->m_userdata_next) { if (0 != delete_this_ud) { if (delete_this_ud->m_userdata_next == ud) { if (const_cast(object).DetachUserData(delete_this_ud)) delete delete_this_ud; } delete_this_ud = 0; } if (ud->DeleteAfterWrite(*this,&object)) delete_this_ud = const_cast< ON_UserData* >(ud); if (false == bHasWritableUserData) continue; if (!ud->WriteToArchive(*this,&object)) continue; // LOTS of tests to weed out bogus user data if ( 0 == ON_UuidCompare( ud->m_userdata_uuid, ON_nil_uuid ) ) continue; if ( &object != ud->m_userdata_owner ) continue; const ON_ClassId* cid = ud->ClassId(); if ( 0 == cid ) continue; if ( cid == &ON_CLASS_RTTI(ON_UserData) ) continue; if ( cid == &ON_CLASS_RTTI(ON_Object) ) continue; // The UserDataClassUuid() function is used instead of // calling cid->Uuid() so we get the value of the // plug-in's class id when the plug-in is not loaded // and ud is ON_UnknownUserData. userdata_classid = ud->UserDataClassUuid(); if ( 0 == ON_UuidCompare( userdata_classid, ON_nil_uuid ) ) continue; if ( 0 == ON_UuidCompare( userdata_classid, ON_CLASS_ID(ON_UserData) ) ) continue; if ( 0 == ON_UuidCompare( userdata_classid, ON_CLASS_ID(ON_Object) ) ) continue; if (0 == ON_UuidCompare(userdata_classid, ON_CLASS_ID(ON_UnknownUserData))) continue; if (0 == ON_UuidCompare(userdata_classid, ON_CLASS_ID(ON_ObsoleteUserData))) continue; if ( 3 == m_3dm_version ) { // When saving a V3 archive and the user data is not // native V3 data, make sure the plug-in supports // writing V3 user data. if ( m_V3_plugin_id_list.BinarySearch( &ud->m_application_uuid, ON_UuidCompare ) < 0 ) continue; } if (false == bSaveAllUserData && false == ShouldSerializeUserDataItem(ud->m_application_uuid, ud->m_userdata_uuid) ) continue; if ( ON_UuidIsNil( ud->m_application_uuid ) ) { // As of version 200909190 - a non-nil application_uuid is // required in order for user data to be saved in a // 3dm archive. ON_Error(__FILE__,__LINE__,"Not saving %s userdata - m_application_uuid is nil.",cid->ClassName()); continue; } // See if we have unknown user data (goo) and make sure // IsUnknownUserData() agrees with ON_UnknownUserData::Cast(). const ON_UnknownUserData* unknown_ud = ON_UnknownUserData::Cast(ud); if ( 0 == unknown_ud ) { if ( ud->IsUnknownUserData() ) { ON_ERROR("ON_UnknownUserData::Cast(ud) is null and ud->IsUnknownUserData() is true."); continue; // something's wrong } } else { if ( !ud->IsUnknownUserData() ) { ON_ERROR("ON_UnknownUserData::Cast(ud) is not null and ud->IsUnknownUserData() is false."); continue; // something's wrong } } if ( 0 != unknown_ud ) { if (false == bSaveAllUserData && false == bSaveSelectedUserData) continue; // unknown user data cannot be critical if ( unknown_ud->m_3dm_version <= 3 ) continue; // Unknown will not be resaved in V3 archives if ( unknown_ud->m_3dm_version > 5 && unknown_ud->m_3dm_version < 50 ) continue; if ( unknown_ud->m_3dm_opennurbs_version_number < 200701010 ) continue; if ( unknown_ud->m_3dm_version >= 50 && m_3dm_version < 50 ) { // Unknown userdata with 8 byte chunk lengths cannot be // saved into a V4 file with 4 byte chunk lengths because // the resulting chunk will be unreadable in V4. // This is not an error condition. It is a consequence // of V4 IO code not being robust enough to handle // 8 bytes chunk lengths. continue; } } // Each piece of user data is inside of // a TCODE_OPENNURBS_CLASS_USERDATA chunk. rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_USERDATA, 0 ); if (rc) { rc = Write3dmChunkVersion(2,2); // wrap user data header info in an TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk rc = BeginWrite3dmChunk( TCODE_OPENNURBS_CLASS_USERDATA_HEADER, 0 ); if (rc) { if ( rc ) rc = WriteUuid( userdata_classid ); if ( rc ) rc = WriteUuid( ud->m_userdata_uuid ); if ( rc ) rc = WriteInt( ud->m_userdata_copycount ); if ( rc ) rc = WriteXform( ud->m_userdata_xform ); // added for version 2.1 if ( rc ) rc = WriteUuid( ud->m_application_uuid ); // added for version 2.2 - 14, October 2009 if ( rc ) { rc = WriteBool( unknown_ud ? true : false ); // ver = 2,3,4,50,60,... const int file_format_ver = unknown_ud ? unknown_ud->m_3dm_version : m_3dm_version; rc = WriteInt(file_format_ver); // ver = opennurbs version number unsigned int app_ver = unknown_ud ? unknown_ud->m_3dm_opennurbs_version_number : m_3dm_opennurbs_version; unsigned int app_ver_to_write = ON_BinaryArchive::ArchiveOpenNURBSVersionToWrite(file_format_ver, app_ver); if (rc) rc = WriteInt(app_ver_to_write); } if ( !EndWrite3dmChunk() ) rc = false; } if (rc) { // wrap user data in an anonymous chunk rc = BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 ); if ( rc ) { if ( 0 != unknown_ud ) { // 22 January 2004 Dale Lear // Disable crc checking when writing the // unknow user data block. // This has to be done so we don't get an extra // 32 bit CRC calculated on the block that // ON_UnknownUserData::Write() writes. The // original 32 bit crc is at the end of this // block and will be checked when the class // that wrote this user data is present. // The EndWrite3dmChunk() will reset the // CRC checking flags to the appropriate // values. m_chunk.Last()->m_do_crc16 = 0; m_chunk.Last()->m_do_crc32 = 0; m_bDoChunkCRC = false; } if (m_user_data_depth < 0) { ON_ERROR("m_user_data_depth < 0"); m_user_data_depth = 0; } m_user_data_depth++; rc = ud->Write(*this)?true:false; m_user_data_depth--; if ( !EndWrite3dmChunk() ) rc = false; } } if ( !EndWrite3dmChunk() ) rc = false; } } if (0 != delete_this_ud) { if (0 == delete_this_ud->m_userdata_next) { if (const_cast(object).DetachUserData(delete_this_ud)) delete delete_this_ud; } delete_this_ud = 0; } return rc; } int ON_BinaryArchive::LoadUserDataApplication( ON_UUID application_id ) { // This is a virtual function. // Rhino overrides this function to load plug-ins. return 0; } int ON_BinaryArchive::ReadObject( ON_Object** ppObject ) { if ( nullptr == ppObject ) { ON_ERROR("ON_BinaryArchive::ReadObject() called with nullptr ppObject."); return 0; } *ppObject = nullptr; return ReadObjectHelper(ppObject); } int ON_BinaryArchive::ReadObject( ON_Object& object ) { ON_Object* pObject = &object; return ReadObjectHelper(&pObject); } int ON_BinaryArchive::ReadObjectHelper( ON_Object** ppObject ) { // returns 0: failure - unable to read object because of file IO problems // 1: success // 3: unable to read object because it's UUID is not registered // this could happen in cases where old code is attempting to read // new objects. ON__UINT32 tcode; ON__INT64 length_TCODE_OPENNURBS_CLASS = 0; ON__INT64 length_TCODE_OPENNURBS_CLASS_UUID = 0; ON__INT64 length_TCODE_OPENNURBS_CLASS_DATA = 0; ON_UUID uuid; const ON_ClassId* pID = 0; ON_Object* pObject = *ppObject; // If not null, use input const bool bDestinationIsManaged = (nullptr == pObject); const ON__INT64 sizeof_chunk_header = (ON__INT64)(4 + SizeofChunkLength()); const ON__INT64 expected_length_TCODE_OPENNURBS_CLASS_UUID = 20; //bool bBogusUserData = false; // all ON_Objects written by WriteObject are in a TCODE_OPENNURBS_CLASS chunk if ( false == BeginRead3dmBigChunk( &tcode, &length_TCODE_OPENNURBS_CLASS ) ) return 0; // When a nullptr ON_Object is written, the file has // // TCODE_OPENNURBS_CLASS, length = 20 + sizeof_chunk_header // TCODE_OPENNURBS_CLASS_UUID, length = 20 // 16 byte nil uuid // 4 byte TCODE_OPENNURBS_CLASS_UUID crc // // When a non-nullptr ON_Object is written, the file has // // TCODE_OPENNURBS_CLASS, length = 20 + 3*sizeof_chunk_header + length_DATA_chunk + length_USER_DATA_chunk(s) // // TCODE_OPENNURBS_CLASS_UUID, length = 20 // 16 byte nil uuid // 4 byte TCODE_OPENNURBS_CLASS_UUID crc // // TCODE_OPENNURBS_CLASS_DATA, length_DATA >= 4 // ... // 4 byte TCODE_OPENNURBS_CLASS_DATA crc // // Optional TCODE_OPENNURBS_CLASS_USERDATA chunks // // TCODE_OPENNURBS_CLASS_END, chunk value = 0 int rc = 0; // return 0 indicates error if ( tcode != TCODE_OPENNURBS_CLASS ) { ON_ERROR("ON_BinaryArchive::ReadObject() didn't find TCODE_OPENNURBS_CLASS block."); } else if ( length_TCODE_OPENNURBS_CLASS < expected_length_TCODE_OPENNURBS_CLASS_UUID + sizeof_chunk_header) { ON_ERROR("ON_BinaryArchive::ReadObject() TCODE_OPENNURBS_CLASS chunk length too small."); } else { // we break out of this loop if something bad happens for (;;) { // read class's UUID /////////////////////////////////////////////////////////// bool bClassIdRead = false; if (false == BeginRead3dmBigChunk(&tcode, &length_TCODE_OPENNURBS_CLASS_UUID)) { break; } for (;;) { if (tcode != TCODE_OPENNURBS_CLASS_UUID) { ON_ERROR("ON_BinaryArchive::ReadObject() didn't find TCODE_OPENNURBS_CLASS_UUID block"); break; } if (expected_length_TCODE_OPENNURBS_CLASS_UUID != length_TCODE_OPENNURBS_CLASS_UUID) { ON_ERROR("ON_BinaryArchive::ReadObject() TCODE_OPENNURBS_CLASS_UUID has invalid length"); break; } if (false == ReadUuid(uuid)) { break; } bClassIdRead = true; break; } if (false == EndRead3dmChunk()) { break; } if (false == bClassIdRead) break; /////////////////////////////////////////////////////////////////////////////// rc = 1; if ( !ON_UuidCompare( &uuid, &ON_nil_uuid ) ) { // nil UUID written if nullptr pointer is passed to WriteObject(); break; } // Use UUID to get ON_ClassId for this class ////////////////////////////////// if ( nullptr != pObject ) { pID = pObject->ClassId(); if ( nullptr == pID ) { // pObject is not properly defined. This is a C++ source code error // or the data segment is corrupt. ON_WARNING(" pObject->ClassId() returned nullptr."); rc = 3; break; } if ( nullptr != pID && uuid != pID->Uuid() ) { ON_ERROR("ON_BinaryArchive::ReadObject() - uuid does not match intput pObject's class id."); pID = 0; rc = 2; break; } } else { pID = ON_ClassId::ClassId( uuid ); } if ( nullptr == pID ) { // If you get here and you are not calling ON::Begin() at some point in your // executable, then call ON::Begin() to force all class definition to be linked. // If you are callig ON::Begin(), then either the uuid is garbage or you are // attempting to read an object with old code. // Visit http://www.opennurbs.org to get the latest OpenNURBS code. ON_WARNING("ON_BinaryArchive::ReadObject() ON_ClassId::ClassId(uuid) returned nullptr."); rc = 3; break; } /////////////////////////////////////////////////////////////////////////////// // read class's definitions ///////////////////////////////////////////////// if (false == BeginRead3dmBigChunk(&tcode, &length_TCODE_OPENNURBS_CLASS_DATA)) { rc = 0; break; } bool bSuppressPartiallyReadChunkWarning = false; if ( tcode != TCODE_OPENNURBS_CLASS_DATA ) { ON_ERROR("ON_BinaryArchive::ReadObject() didn't find TCODE_OPENNURBS_CLASS_DATA block"); rc = 0; } else if ( length_TCODE_OPENNURBS_CLASS_DATA <= 0 ) { ON_ERROR("ON_BinaryArchive::ReadObject() TCODE_OPENNURBS_CLASS_DATA chunk length too small"); rc = 0; } else { if ( nullptr == pObject ) { pObject = pID->Create(); } if ( nullptr == pObject ) { ON_ERROR("ON_BinaryArchive::ReadObject() pID->Create() returned nullptr."); rc = 0; } else { if ( false == (pObject->Read(*this) ? true : false) ) { rc = 0; ON_ERROR("ON_BinaryArchive::ReadObject() pObject->Read() failed."); if ( bDestinationIsManaged ) delete pObject; // don't break here - we still need to call end chunk. } else { *ppObject = pObject; if ( nullptr != ON_InstanceDefinition::Cast(pObject) && 60 == Archive3dmVersion() && ArchiveOpenNURBSVersion() <= 2348834153 ) { // April 2016 // The ON_InstanceDefinition Read()/Write() code for V5 and early V6 WIPs was // written more than 10 years ago and was not wrapped in an internal chunk. // Attempts to use it for V6 IO failed. // Setting bSuppressPartiallyReadChunkWarning = true here suppresses a // false warning when files saved by early V6 WIPs are read. bSuppressPartiallyReadChunkWarning = true; } } } } if ( false == EndRead3dmChunk(bSuppressPartiallyReadChunkWarning) ) { rc = 0; } if ( 0 != rc && nullptr != pObject ) { // read user data ///////////////////////////////////////////////// // If TCODE_OPENNURBS_CLASS_USERDATA chunks exist, this reads them. // ReadObjectUserData() stops when it reads a TCODE_OPENNURBS_CLASS_END chunk. if ( false == ReadObjectUserData(*pObject) ) rc = 0; } break; } } if ( false == EndRead3dmChunk() ) // TCODE_OPENNURBS_CLASS rc = 0; return rc; } bool ON_BinaryArchive::ReadObjectUserDataAnonymousChunk( const ON__UINT64 length_TCODE_ANONYMOUS_CHUNK, const int archive_3dm_version, const unsigned int archive_opennurbs_version, ON_UserData* ud ) { // Reads the portion of the file containing the userdata into a buffer // and lets the plug-in try to read from that. If the plug-in fails, // we press on because we cannot trust plug-ins to get IO code right. bool rc = false; bool bChunkReadSuccess = true; if ( 0 == ud ) return false; if ( ud->IsUnknownUserData() || (archive_3dm_version == Archive3dmVersion() && archive_opennurbs_version == ArchiveOpenNURBSVersion() && IsCoreUserData(ud)) ) { // assume this userdata's read function is robust. ON_ReadChunkHelper ch(*this,bChunkReadSuccess); if ( !bChunkReadSuccess || TCODE_ANONYMOUS_CHUNK != ch.m_chunk_tcode || length_TCODE_ANONYMOUS_CHUNK != (ON__UINT64)ch.m_chunk_value ) { return false; } if ( ud->IsUnknownUserData() ) { // 22 January 2004 Dale Lear: // Disable CRC checking while reading this chunk. // (If the user data has nested chunks, the crc we get // by reading the thing as one large chunk will be wrong.) ON_3DM_BIG_CHUNK* c = m_chunk.Last(); const ON__UINT64 sizeof_CRC = c->SizeofCRC(); m_chunk.Last()->m_do_crc16 = false; m_chunk.Last()->m_do_crc32 = false; m_bDoChunkCRC = false; if (c->m_bLongChunk && sizeof_CRC > c->SizeofCRC() && c->m_start_offset + c->Length() == c->m_end_offset + sizeof_CRC ) { c->m_end_offset += sizeof_CRC; } } if (m_user_data_depth < 0) { ON_ERROR("m_user_data_depth < 0"); m_user_data_depth = 0; } m_user_data_depth++; rc = ud->Read(*this) ? true : false; m_user_data_depth--; } else { // Untrusted plug-in userdata. // Insulate file reading from possible bugs plug-in IO code by reading // entire anonymous chunk into memory and letting the plug-in use // the memory buffer archive. unsigned char stack_buffer[2048]; const size_t sizeof_buffer = (size_t)(length_TCODE_ANONYMOUS_CHUNK + 4 + SizeofChunkLength()); void* freeme = 0; void* buffer = (sizeof_buffer <= sizeof(stack_buffer)) ? &stack_buffer[0] : (freeme = onmalloc(sizeof_buffer)); // generally, object userdata is small we almost never use heap if ( 0 != buffer && sizeof_buffer == ReadBuffer(sizeof_buffer,buffer) ) { ON_Read3dmBufferArchive memory_archive( sizeof_buffer, buffer, false, archive_3dm_version, archive_opennurbs_version ); // The TCODE_ANONYMOUS_CHUNK wrapper has chunk lengths set // by whatever version wrote this file. The information // in the chunk has chunk lengths set by the plug-in that // originally wrote the user data. If the plug-in used // worte to a version <= 5 archive and the user data has // was read as goo and saved as goo in a version 50+ // archive, then we need to tweak the archive version // when reading the chunk length of the TCODE_ANONYMOUS_CHUNK wrapper. bool bTweakArchiveVersion = (memory_archive.SizeofChunkLength() != SizeofChunkLength()); if ( bTweakArchiveVersion ) memory_archive.SetArchive3dmVersion(Archive3dmVersion()); ON_ReadChunkHelper ch(memory_archive,bChunkReadSuccess); if ( bTweakArchiveVersion ) memory_archive.SetArchive3dmVersion(archive_3dm_version); if ( !bChunkReadSuccess || TCODE_ANONYMOUS_CHUNK != ch.m_chunk_tcode || length_TCODE_ANONYMOUS_CHUNK != (ON__UINT64)ch.m_chunk_value ) rc = false; else { if (m_user_data_depth < 0) { ON_ERROR("m_user_data_depth < 0"); m_user_data_depth = 0; } m_user_data_depth++; rc = ud->Read(memory_archive) ? true : false; m_user_data_depth--; } } if ( freeme ) onfree(freeme); } if ( !bChunkReadSuccess ) rc = false; return rc; } class CUserDataHeaderInfo { public: CUserDataHeaderInfo(); void Initialize(); ON_UUID m_classid; ON_UUID m_itemid; ON_UUID m_appid; int m_3dm_version; int m_3dm_opennurbs_version_number; int m_copycount; bool m_bLastSavedAsGoo; ON_Xform m_xform; }; CUserDataHeaderInfo::CUserDataHeaderInfo() { Initialize(); } void CUserDataHeaderInfo::Initialize() { memset(this,0,sizeof(*this)); } static bool ReadObjectUserDataHeaderHelper( ON_BinaryArchive& binary_archive, const int major_userdata_version, const int minor_userdata_version, CUserDataHeaderInfo& ud_header ) { bool rc = true; ON__UINT32 t = 0; ON__INT64 length_TCODE_OPENNURBS_CLASS_USERDATA_HEADER = 0; ud_header.Initialize(); if ( major_userdata_version == 2 ) { // major_userdata_version 2 started wrapping the userdata header info // in a TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk rc = binary_archive.BeginRead3dmBigChunk( &t, &length_TCODE_OPENNURBS_CLASS_USERDATA_HEADER ); if (!rc) return false; if ( t != TCODE_OPENNURBS_CLASS_USERDATA_HEADER ) { ON_ERROR("version 2.0 TCODE_OPENNURBS_CLASS_USERDATA chunk is missing TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk."); binary_archive.EndRead3dmChunk(); // end of mystery chunk return false; } } if (rc) rc = binary_archive.ReadUuid( ud_header.m_classid ); if (rc) rc = binary_archive.ReadUuid( ud_header.m_itemid ); if (rc) rc = binary_archive.ReadInt( &ud_header.m_copycount ); if (rc) rc = binary_archive.ReadXform( ud_header.m_xform ); if ( major_userdata_version == 2 ) { if ( minor_userdata_version >= 1 ) { if (rc) rc = binary_archive.ReadUuid( ud_header.m_appid ); if ( minor_userdata_version >= 2 ) { // bLastSavedAsGoo is true if the user data was saved // into the file by ON_UnknownUserData. if (rc) rc = binary_archive.ReadBool( &ud_header.m_bLastSavedAsGoo ); if (rc) rc = binary_archive.ReadInt( &ud_header.m_3dm_version ); if (rc) rc = binary_archive.ReadInt(&ud_header.m_3dm_opennurbs_version_number); } } if ( !binary_archive.EndRead3dmChunk() ) // end of TCODE_OPENNURBS_CLASS_USERDATA_HEADER rc = 0; } if (!rc) { ON_ERROR("Unable to read user data header information."); return false; } if (0 == ud_header.m_3dm_version || 0 == ud_header.m_3dm_opennurbs_version_number) { // The userdata was saved in in an archive before // the 3dm_version and 3dm_opennurbs_version were saved in // userdata headers. This means it has to be userdata // with 4 byte chunk lengths. If the archive we are // reading was written with a newer version of opennurbs // that does save the 3dm version info, then this unknown // userdata was that has persisted through multiple read/write // cycles and we cannot tell it's original version. So we // will default to a maximum of 5 and 200910180 - the // 3dm versions just before we started saving 3dm // version info n userdata headers. if ( binary_archive.Archive3dmVersion() < 50 ) { ud_header.m_3dm_version = binary_archive.Archive3dmVersion(); } else { // All Archive3dmVersion() >= 50 have userdata_3dm_version info, // so this userdata had to be saved as goo from a version 5 or // earlier archive. ud_header.m_bLastSavedAsGoo = true; ud_header.m_3dm_version = 5; } ud_header.m_3dm_opennurbs_version_number = binary_archive.ArchiveOpenNURBSVersion(); if (ud_header.m_3dm_opennurbs_version_number >= 200910190) { ud_header.m_3dm_opennurbs_version_number = 200910180; ud_header.m_bLastSavedAsGoo = true; } } return rc; } bool ON_BinaryArchive::ReadObjectUserData( ON_Object& object ) { bool rc = true; bool bChunkReadSuccess = true; while(rc && bChunkReadSuccess) { // Note: // The destructor ~ON_ReadChunkHelper() may set bChunkReadSuccess // An example of this case occures when reading the corrupt file // attached to bug report RH-22547. ON_ReadChunkHelper ch(*this,bChunkReadSuccess); if ( !bChunkReadSuccess ) { rc = false; break; } if ( TCODE_OPENNURBS_CLASS_END == ch.m_chunk_tcode ) { // A short TCODE_OPENNURBS_CLASS_END chunk marks the end of the opennurbs class break; // done } if ( TCODE_OPENNURBS_CLASS_USERDATA != ch.m_chunk_tcode ) { if ( 0 == ch.m_chunk_tcode ) { // A short TCODE_OPENNURBS_CLASS_END chunk marks the end of the opennurbs class // Checking for zero here fixes RH-22547 which was a report of Rescue3dm taking // hours to read a damaged file that was filled with large blocks of zeros // caused by some form of storage media or tranmission corruption. rc = false; // there should never be a typecode of zero. break; } // skip new chunk type added by later version continue; } if ( ch.m_chunk_value < (ON__INT64)(8 + 4 * SizeofChunkLength()) ) { ON_ERROR("TCODE_OPENNURBS_CLASS_USERDATA chunk is too short"); continue; } // Read userdata header information int major_userdata_version = 0; int minor_userdata_version = 0; rc = Read3dmChunkVersion( &major_userdata_version, &minor_userdata_version ); if ( !rc ) { ON_ERROR("Unable to read TCODE_OPENNURBS_CLASS_USERDATA chunk version numbers"); break; } if ( major_userdata_version < 1 || major_userdata_version > 2 ) { // unsupported version - too old or added in new version continue; } CUserDataHeaderInfo ud_header; rc = ReadObjectUserDataHeaderHelper(*this,major_userdata_version,minor_userdata_version,ud_header); if (!rc) { ON_ERROR("Unable to read user data header information."); break; } // we should be ready to read a TCODE_ANONYMOUS_CHUNK containing userdata ON__INT64 length_TCODE_ANONYMOUS_CHUNK = 0; for(;;) { ON__UINT32 t = 0; rc = PeekAt3dmBigChunkType( &t, &length_TCODE_ANONYMOUS_CHUNK ); if (!rc) break; if ( t != TCODE_ANONYMOUS_CHUNK ) { ON_ERROR("Reading object user data - unable to find TCODE_ANONYMOUS_CHUNK"); rc = false; break; } if ( length_TCODE_ANONYMOUS_CHUNK < 4 ) { ON_ERROR("Reading object user data - length of TCODE_ANONYMOUS_CHUNK < 4"); rc = false; break; } break; } if (!rc) { break; } if (false == ShouldSerializeUserDataItem(ud_header.m_appid, ud_header.m_itemid)) { ch.m_bSupressPartiallyReadChunkWarning = true; continue; } // attempt to get an instance of the userdata class that saved this information ON_UserData* ud = 0; for(;;) { const ON_ClassId* udId = ON_ClassId::ClassId( ud_header.m_classid ); if ( 0 == udId ) { // The application that created this userdata is not active if ( !ON_UuidIsNil(ud_header.m_appid) ) { // see if we can load the application if ( 1 == LoadUserDataApplication(ud_header.m_appid) ) { // try again udId = ON_ClassId::ClassId( ud_header.m_classid ); } } if ( 0 == udId ) { // The application that created this user data is // not available. This information will be stored // in an ON_UnknownUserData class so that it can // persist. udId = &ON_CLASS_RTTI(ON_UnknownUserData); } } ON_Object* tmp = udId->Create(); ud = ON_UserData::Cast(tmp); if ( 0 == ud ) { ON_ERROR("Reading object user data - unable to create userdata class"); if ( tmp ) delete tmp; tmp = 0; break; } tmp = 0; break; } if ( 0 == ud ) { // no luck on this one // One reason can be that the plug-in userdata class has something wrong with // its ON_OBJECT_DECLARE/ON_OBJECT_IMPLEMENT stuff. ON_ERROR("Unable to create object user data class. Flawed class id information."); continue; // keep trying } if ( ON_UuidIsNil(ud->m_application_uuid) ) { if ( ON_UuidIsNil(ud_header.m_appid) ) { switch( Archive3dmVersion()) { case 2: // V2 archives do not contain app ids. // This id flags the userdata as being read from a V3 archive. ud_header.m_appid = ON_v2_userdata_id; break; case 3: // V3 archives do not contain app ids. // This id flags the userdata as being // read from a V3 archive. ud_header.m_appid = ON_v3_userdata_id; break; case 4: if ( ArchiveOpenNURBSVersion() < 200909190 ) { // V4 archives before version 200909190 // did not require user data application ids. ud_header.m_appid = ON_v4_userdata_id; } break; } } ud->m_application_uuid = ud_header.m_appid; } ud->m_userdata_uuid = ud_header.m_itemid; ud->m_userdata_copycount = ud_header.m_copycount; ud->m_userdata_xform = ud_header.m_xform; if ( ud->IsUnknownUserData() ) { ON_UnknownUserData* uud = ON_UnknownUserData::Cast(ud); if ( uud ) { uud->m_sizeof_buffer = (int)length_TCODE_ANONYMOUS_CHUNK; uud->m_unknownclass_uuid = ud_header.m_classid; uud->m_3dm_version = ud_header.m_3dm_version; uud->m_3dm_opennurbs_version_number = ud_header.m_3dm_opennurbs_version_number; } } ud->m_userdata_owner = &object; // so reading code can look at owner bool bReadUserData = ReadObjectUserDataAnonymousChunk( length_TCODE_ANONYMOUS_CHUNK, ud_header.m_3dm_version, ud_header.m_3dm_opennurbs_version_number, ud ); ud->m_userdata_owner = 0; if (bReadUserData) { if (ud->DeleteAfterRead(*this,&object)) { // obsolete user data delete ud; } else if (!object.AttachUserData(ud)) { // attach failed delete ud; } } else { delete ud; } } if ( !bChunkReadSuccess ) rc = false; return rc; } bool ON_BinaryArchive::Write3dmChunkVersion( int major_version, // major // 0 to 15 int minor_version // minor // 0 to 16 ) { const unsigned char v = (unsigned char)(major_version*16+minor_version); return WriteChar( v ); } bool ON_BinaryArchive::Read3dmChunkVersion( int* major_version, // major // 0 to 15 int* minor_version // minor // 0 to 16 ) { unsigned char v = 0; bool rc = ReadChar( &v ); if ( minor_version) *minor_version = v%16; // The bit shift happens on the fly in the following // if statement. It was happening twice which always // set the major version to 0 //v >>= 4; if ( major_version) *major_version = (v>>4); return rc; } int ON_BinaryArchive::Archive3dmVersion() const { // 1,2,3,4,5,50,60,... return m_3dm_version; } unsigned int ON_BinaryArchive::ArchiveOpenNURBSVersion() const { return m_3dm_opennurbs_version; } ON::RuntimeEnvironment ON_BinaryArchive::ArchiveRuntimeEnvironment() const { return m_archive_runtime_environment; } size_t ON_BinaryArchive::ArchiveStartOffset() const { return m_3dm_start_section_offset; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// bool ON_BinaryArchive::BeginWrite3dmChunk( unsigned int typecode, int value ) { ON__INT64 value64 = 0; if ( 0 != value ) { if ( ON_IsUnsignedChunkTypecode(typecode) ) { // treat value parameter as an unsigned int ON__UINT32 u32 = (ON__UINT32)value; ON__UINT64 u64 = u32; value64 = (ON__INT64)u64; } else { // treat value paramter is a signed int value64 = value; } } return BeginWrite3dmBigChunk(typecode,value64); } bool ON_BinaryArchive::BeginWrite3dmBigChunk( ON__UINT32 typecode, ON__INT64 value64 ) { if (false == WriteMode()) { ON_ERROR("WriteMode() = false."); return false; } m_bDoChunkCRC = false; // no CRC on chunks because length is written twice. bool rc = WriteInt32( 1, (ON__INT32*)&typecode ); if (rc) rc = WriteChunkValue( typecode, value64 ); if (rc) rc = PushBigChunk( typecode, value64 ); return rc; } bool ON_BinaryArchive::BeginWrite3dmAnonymousChunk( int version ) { const int major_version = 1; if (version < 0) { ON_ERROR("Incorrect version value."); return false; } return BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, major_version, version); } bool ON_BinaryArchive::BeginRead3dmAnonymousChunk( int* version ) { int major_version = 0; int minor_version = 0; bool rc = BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version); if (rc && (1 != major_version || minor_version < 0)) { ON_ERROR("Incorrect major_version value."); // BeginRead3dmChunk() was successful, so EndRead3dmChunk() must be called. // The archive is corrupt or this function is being used incorrectly. EndRead3dmChunk(); rc = false; } if (nullptr != version) { *version = rc ? minor_version : -1; } return rc; } bool ON_BinaryArchive::BeginWrite3dmChunk( unsigned int tcode, int major_version, int minor_version ) { bool rc = false; if (false == WriteMode()) { ON_ERROR("WriteMode() = false."); } else if ( 0 == tcode ) { ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input tcode = 0"); } else if ( 0 != (tcode & TCODE_SHORT) ) { ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input tcode has short flag set."); } else if ( major_version <= 0 ) { ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input major_version <= 0."); } else if ( minor_version < 0 ) { ON_ERROR("ON_BinaryArchive::BeginWrite3dmChunk - input minor_version < 0."); } else { rc = BeginWrite3dmChunk(tcode,0); if (rc) { rc = WriteInt(major_version); if (rc) rc = WriteInt(minor_version); if ( !rc) EndWrite3dmChunk(); } } return rc; } bool ON_BinaryArchive::EndWrite3dmChunk() { if (false == WriteMode()) { ON_ERROR("WriteMode() = false."); return false; } bool rc = false; ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c ) { if ( c->m_bLongChunk ) { if ( c->m_do_crc16 ) { // write 16 bit CRC unsigned char two_zero_bytes[2] = {0,0}; ON__UINT16 crc = ON_CRC16( c->m_crc16, 2, two_zero_bytes ); rc = WriteInt16( 1, (ON__INT16*)&crc ); if (c->m_crc16) { // should never happen unless ON_CRC16() code is damaged Internal_ReportCRCError(); ON_ERROR("ON_BinaryArchive::EndWrite3dmChunk: CRC16 computation error."); } } else if ( c->m_do_crc32 ) { // write 32 bit CRC const ON__UINT32 crc0 = c->m_crc32; rc = WriteInt32( 1, (ON__INT32*)&crc0 ); } else { rc = true; } // write length m_bDoChunkCRC = 0; const ON__UINT64 offset = CurrentPosition(); if ( offset < c->m_start_offset ) { ON_ERROR("ON_BinaryArchive::EndWrite3dmChunk() - chunk length < 0"); rc = false; } else { const bool bChunkBoundaryCheck = m_bChunkBoundaryCheck; // SeekBackward seeks to SizeofChunkLength() bytes before // the chunk data boundary to write the chunk length // The SeekForward() seeks 1 byte after the end of the chunk boundary. m_bChunkBoundaryCheck = false; ON__UINT64 length = (offset - c->m_start_offset); if ( !SeekBackward( length + SizeofChunkLength() ) ) { rc = false; } else { if ( !WriteChunkLength( length ) ) { rc = false; } if ( !SeekForward( length ) ) { rc = false; } } m_bChunkBoundaryCheck = bChunkBoundaryCheck; if ( CurrentPosition() != offset ) { ON_ERROR("ON_BinaryArchive::EndWrite3dmChunk() - CurrentPosition() != offset"); rc = false; } } } else { // "short" chunks are completely written by call to BeginWrite3dmChunk(). rc = true; } m_chunk.Remove(); c = m_chunk.Last(); if ( nullptr == c ) { Flush(); m_bDoChunkCRC = false; } else { if ( c->m_bLongChunk ) { ON__UINT64 current_pos = CurrentPosition(); if (current_pos > c->m_end_offset) c->m_end_offset = current_pos; } m_bDoChunkCRC = (c->m_do_crc16 || c->m_do_crc32); } } return rc; } bool ON_BinaryArchive::Write3dmGoo( const ON_3dmGoo& goo ) { bool rc = false; if ( goo.m_typecode ) { const bool savedDoCRC = m_bDoChunkCRC; m_bDoChunkCRC = false; if ( 0 != (goo.m_typecode & TCODE_SHORT) ) { if ( goo.m_value == 0 || (goo.m_value > 0 && goo.m_goo) ) { // write long chunk - do not use Begin/EndWrite3dmChunk() because // goo may contain subchunks and CRC would be // incorrectly computed. rc = WriteInt( goo.m_typecode ); if (rc) rc = WriteInt( goo.m_value ); if (rc && goo.m_value>0) rc = WriteByte( goo.m_value, goo.m_goo ); } } else { // write short chunk rc = WriteInt( goo.m_typecode ); if (rc) rc = WriteInt( goo.m_value ); } m_bDoChunkCRC = savedDoCRC; } return rc; } bool ON_BinaryArchive::PeekAt3dmChunkType( unsigned int* typecode, int* value ) { // does not change file position bool rc; unsigned int tc = 0; ON__INT64 i64 = 0; rc = PeekAt3dmBigChunkType(&tc,&i64); if ( rc ) { if ( 0 != typecode ) *typecode = tc; if ( 0 != value ) { ON__INT32 i32 = 0; if ( ON_IsUnsignedChunkTypecode(tc) ) rc = DownSizeUINT((ON__UINT64)i64,(ON__UINT32*)&i32); else rc = DownSizeINT(i64,&i32); *value = i32; } } return rc; } bool ON_BinaryArchive::PeekAt3dmBigChunkType( ON__UINT32* typecode, ON__INT64* big_value ) { // does not change file position // 10 January 2005 Dale Lear // Do not let the "peeking" affect the CRC. const bool bDoChunkCRC = m_bDoChunkCRC; m_bDoChunkCRC = false; const ON__UINT64 pos0 = CurrentPosition(); ON__UINT32 t = 0; ON__INT64 v = 0; const unsigned int saved_error_message_mask = m_error_message_mask; m_error_message_mask |= 0x01; bool rc = ReadChunkTypecode( &t ); m_error_message_mask = saved_error_message_mask; if (false == rc) t = 0; if (rc) { rc = ReadChunkValue( t, &v ); } const ON__UINT64 pos1 = CurrentPosition(); if ( pos1 > pos0 && !SeekBackward( pos1-pos0 ) ) { rc = false; } m_bDoChunkCRC = bDoChunkCRC; if ( typecode ) *typecode = t; if ( big_value ) *big_value = v; return rc; } bool ON_BinaryArchive::Seek3dmChunkFromStart( // beginning at the start of the active chunk, search portion of // archive included in active chunk for the start of a subchunk // with the specified type. // if true is returned, then the position is set so the next call to // BeginRead3dmChunk() will read a chunk with the specified typecode unsigned int typecode // typecode from opennurbs_3dm.h ) { bool rc = false; if ( ReadMode() ) { const ON__UINT64 pos0 = CurrentPosition(); const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c ) { // set archive position to the beginning of this chunk if ( !ON_IsLongChunkTypecode(c->m_typecode) ) { ON_ERROR("ON_BinaryArchive::Seek3dmChunkFromStart() - current chunk is not a long chunk"); return false; } if ( c->m_big_value < 0 ) { ON_ERROR("ON_BinaryArchive::Seek3dmChunkFromStart() called with an active chunk that has m_value < 0"); return false; } if ( pos0 < c->m_start_offset || pos0 > c->m_end_offset ) { ON_ERROR("ON_BinaryArchive::Seek3dmChunkFromStart() called with out of bounds current position"); return false; } rc = SeekBackward( pos0 - c->m_start_offset ); // pos0 >= c->m_offset, so this size_t subtraction is ok } else { // set archive position to the beginning of archive chunks by skipping // 32 byte version info and any start section padding. size_t start_offset = ((m_3dm_start_section_offset > 0) ? m_3dm_start_section_offset : 0); rc = SeekFromStart( start_offset ); if (!rc && start_offset > 0) { start_offset = 0; rc = SeekFromStart(start_offset); } char s3d[33]; memset(s3d,0,sizeof(s3d)); if (rc) rc = ReadByte(32,s3d); if (rc) { rc = (0 == strncmp( s3d, "3D Geometry File Format ", 24)); if ( !rc && start_offset > 0 ) { start_offset = 0; rc = SeekFromStart(start_offset); if (rc) rc = ReadByte(32,s3d); rc = (0 == strncmp( s3d, "3D Geometry File Format ", 24)); } } if (rc) { if ( start_offset != m_3dm_start_section_offset ) m_3dm_start_section_offset = start_offset; unsigned int t=0; ON__INT64 v=-1; rc = PeekAt3dmBigChunkType(&t,&v); if (rc && (t != 1 || v < 0) ) rc = false; } } if (rc) { rc = Seek3dmChunkFromCurrentPosition( typecode ); } if (!rc) { SeekFromStart(pos0); } } return rc; } ON__UINT64 ON_3DM_BIG_CHUNK::Length() const { return (ON_IsLongChunkTypecode(m_typecode) && m_big_value >= 0 ) ? ((ON__UINT64)m_big_value) : 0; } ON__UINT64 ON_3DM_BIG_CHUNK::SizeofCRC() const { const ON__UINT64 sizeof_crc = m_do_crc32 ? 4 : (m_do_crc16 ? 2 : 0); return sizeof_crc; } ON__UINT64 ON_3DM_BIG_CHUNK::LengthRemaining( ON__UINT64 current_position ) const { if (0 == (TCODE_SHORT & m_typecode) && m_start_offset <= current_position && current_position <= m_end_offset) { return (m_end_offset - current_position); } return 0; } bool ON_BinaryArchive::Seek3dmChunkFromCurrentPosition( // beginning at the current position, search portion of archive // included in active chunk for the start of a subchunk with the // specified type. // if true is returned, then the position is set so the next call to // BeginRead3dmChunk() will read a chunk with the specified typecode unsigned int typecode // typecode from opennurbs_3dm.h ) { bool rc = false; if ( ReadMode() ) { const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); const ON__UINT64 pos1 = c ? c->m_start_offset + c->Length() : 0; const ON__UINT64 pos_start = CurrentPosition(); ON__UINT64 pos_prev = 0; ON__UINT64 pos = 0; unsigned int t; ON__INT64 v64; bool bFirstTime = true; while(pos > pos_prev || bFirstTime) { bFirstTime = false; pos_prev = pos; pos = CurrentPosition(); if ( pos1 && pos > pos1 ) break; t = !typecode; if ( !PeekAt3dmBigChunkType( &t, 0 ) ) break; if ( t == typecode ) { rc = true; break; } if ( 0 == t ) { // zero is not a valid typecode - file is corrupt at or before this position break; } if ( !BeginRead3dmBigChunk( &t, &v64 ) ) break; if ( !EndRead3dmChunk() ) break; if ( TCODE_ENDOFTABLE == t && 0 != v64 ) { // TCODE_ENDOFTABLE chunks always have value = 0 - file is corrupt at or before this position break; } } if ( !rc ) { SeekFromStart( pos_start ); } } return rc; } bool ON_BinaryArchive::BeginRead3dmBigChunk( unsigned int* typecode, ON__INT64* value ) { if (false == ReadMode()) { ON_ERROR("ReadMode() = false."); if (typecode) *typecode = 0; if (value) *value = 0; return false; } ON__UINT32 t = 0; ON__INT64 v = 0; m_bDoChunkCRC = false; // no CRC on chunk headers because length is written twice const unsigned int saved_error_message_mask = m_error_message_mask; m_error_message_mask |= 0x0001; // disable ReadByte() error message at EOF bool rc = ReadChunkTypecode( &t ); m_error_message_mask = saved_error_message_mask; if (rc) { if ( t == TCODE_ENDOFFILE ) { // Either this chunk is a bona fide end of file mark, or it's "goo" // that Rhino 1.0 or the pre-February 2000 Rhino 1.1 saved and wrote. ON__UINT64 sizeof_file = 0; if ( rc ) rc = ReadChunkValue( t, &v ); if ( rc && v >= 0 && ((ON__UINT64)v) >= SizeofChunkLength() ) { ON__UINT64 EOF_chunk_length = (ON__UINT64)v; ON__UINT64 pos0 = CurrentPosition(); rc = ReadEOFSizeOfFile( &sizeof_file ); ON__UINT64 pos1 = CurrentPosition(); if ( pos0 > 0 && pos1 > pos0 ) { if ( !SeekBackward(pos1-pos0) ) rc = false; } if ( rc ) { if ( SeekForward( EOF_chunk_length ) ) { ON__UINT64 pos2 = CurrentPosition(); if ( m_3dm_version <= 1 ) { if ( !AtEnd() ) { // Rhino v1 reads chunks with unknown typecodes as a block of "goo" // and then saves them back into file. When this happens to an // eof marker, we get TCODE_ENDOFFILE chunks in the middle of a file. // This can only happen in m_3dm_version = 1 files. t = TCODE_ENDOFFILE_GOO; } } else { // check that current position matches saved file size if ( pos2 != sizeof_file ) { ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk() - Rogue eof marker in v2 file.\n"); } } rc = SeekBackward( EOF_chunk_length ); } } if ( rc ) rc = PushBigChunk( t, v ); } else { ON_ERROR( "ON_BinaryArchive::BeginRead3dmChunk() - file is damaged." ); rc = false; t = 0; // ?? file is trashed ?? } } else { if ( rc ) rc = ReadChunkValue( t, &v ); if ( rc ) rc = PushBigChunk( t, v ); } } if ( typecode ) *typecode = t; if ( value ) *value = v; return rc; } bool ON_BinaryArchive::BeginRead3dmChunk( unsigned int expected_tcode, int* major_version, int* minor_version ) { bool rc = false; if (false == ReadMode()) { ON_ERROR("ReadMode() = false."); } else if ( 0 == expected_tcode ) { ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input expected_tcode = 0"); } else if ( 0 != (expected_tcode & TCODE_SHORT) ) { ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input expected_tcode has short flag set."); } else if ( 0 == major_version ) { ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input major_version nullptr"); } else if ( 0 == minor_version ) { ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - input minor_version nullptr"); } else { *major_version = 0; *minor_version = 0; unsigned int tcode = 0; ON__INT64 value = 0; rc = PeekAt3dmBigChunkType(&tcode,&value); if ( expected_tcode != tcode ) { ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - unexpected tcode"); rc = false; } else if ( value < 8 ) { ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - unexpected chunk length"); rc = false; } else { tcode = 0; value = 0; rc = BeginRead3dmBigChunk(&tcode,&value); if (rc) { if ( expected_tcode != tcode || value < 8 ) { // can happen when seek fails ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - unexpected tcode or chunk length - archive driver or device may be bad"); rc = false; } else { rc = ReadInt(major_version); if ( rc && *major_version < 1 ) { ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - major_version < 1"); rc = false; } if (rc) { rc = ReadInt(minor_version); if ( rc && *minor_version < 0 ) { ON_ERROR("ON_BinaryArchive::BeginRead3dmChunk - minor_version < 0"); rc = false; } } } if ( !rc ) { // this is required to keep chunk accounting in synch EndRead3dmChunk(); } } } } return rc; } bool ON_BinaryArchive::EndRead3dmChunk() { return EndRead3dmChunk(false); } bool ON_BinaryArchive::EndRead3dmChunk(bool bSupressPartiallyReadChunkWarning) { if (false == ReadMode()) { ON_ERROR("ReadMode() = false."); return false; } //int length = 0; bool rc = false; ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c ) { ON__UINT64 file_offset = CurrentPosition(); ON__UINT64 end_offset = c->m_start_offset; if ( c->m_bLongChunk ) { if ( c->m_big_value < 0 ) { Internal_ReportCriticalError(); ON_ERROR("ON_BinaryArchive::EndRead3dmChunk - negative chunk length"); } else { end_offset += ((ON__UINT64)c->m_big_value); } } if ( c->m_bLongChunk ) { const bool bChunkBoundaryCheck = m_bChunkBoundaryCheck; if ( c->m_do_crc16 ) { if ( file_offset+2 == end_offset ) { // read 16 bit CRC unsigned char two_crc_bytes[2] = {0,0}; m_bChunkBoundaryCheck = false; rc = ReadByte( 2, two_crc_bytes ); m_bChunkBoundaryCheck = bChunkBoundaryCheck; if (rc) { file_offset+=2; if (c->m_crc16) { Internal_ReportCRCError(); ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: CRC16 error."); } } } else { // partially read chunk - crc check not possible. rc = true; } } else if ( c->m_do_crc32 ) { if ( file_offset+4 == end_offset ) { // read 32 bit CRC ON__UINT32 crc1 = c->m_crc32; ON__UINT32 crc0; m_bChunkBoundaryCheck = false; rc = ReadInt32( 1, (ON__INT32*)&crc0 ); m_bChunkBoundaryCheck = bChunkBoundaryCheck; if (rc) { file_offset+=4; if (crc0 != crc1) { Internal_ReportCRCError(); ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: CRC32 error."); } } } else { // partially read chunk - crc check not possible. rc = true; } } else { // no crc in this chunk rc = true; } } else { rc = true; } // check length and seek to end of chunk if things are amiss if ( file_offset < c->m_start_offset ) { Internal_ReportCriticalError(); ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: current position before start of current chunk."); if ( !SeekFromStart( end_offset ) ) rc = false; } else if ( file_offset > end_offset ) { Internal_ReportCriticalError(); ON_ERROR("ON_BinaryArchive::EndRead3dmChunk: current position after end of current chunk."); if ( !SeekFromStart( end_offset ) ) rc = false; } else if ( file_offset != end_offset ) { // partially read chunk - happens when chunks are skipped or old code // reads a new minor version of a chunk whnich has added information. if ( file_offset != c->m_start_offset ) { if ( m_3dm_version != 1 || (m_error_message_mask&0x02) == 0 ) { // when reading v1 files, there are some situations where // it is reasonable to attempt to read 4 bytes at the end // of a file. The above test prevents making a call // to ON_WARNING() in these situations. unsigned int file_year = 0; unsigned int file_month = 0; unsigned int file_date = 0; unsigned int file_major_version = 0; const bool bHaveFileDate = ON_VersionNumberParse( m_3dm_opennurbs_version, &file_major_version, 0, &file_year, &file_month, &file_date, 0 ); const unsigned int file_ymd = bHaveFileDate ? ((file_year * 100 + file_month) * 100 + file_date) : 0; unsigned int app_year = 0; unsigned int app_month = 0; unsigned int app_date = 0; unsigned int app_major_version = 0; const bool bHaveAppDate = ON_VersionNumberParse( ON::Version(), &app_major_version, 0, &app_year, &app_month, &app_date, 0 ); const unsigned int app_ymd = bHaveAppDate ? ((app_year * 100 + app_month) * 100 + app_date) : 0; if (file_major_version <= app_major_version && file_ymd <= app_ymd ) { // We are reading a file written by this version or an // earlier version of opennurbs. // There should not be any partially read chunks. if (!bSupressPartiallyReadChunkWarning) { ON_WARNING("ON_BinaryArchive::EndRead3dmChunk: partially read chunk - skipping bytes at end of current chunk."); } } } } const bool bChunkBoundaryCheck = m_bChunkBoundaryCheck; m_bChunkBoundaryCheck = false; if ( end_offset > file_offset ) { if ( !SeekForward(end_offset - file_offset) ) rc = false; } else if ( end_offset < file_offset ) { if ( !SeekBackward(file_offset - end_offset) ) rc = false; } m_bChunkBoundaryCheck = bChunkBoundaryCheck; } m_chunk.Remove(); c = m_chunk.Last(); m_bDoChunkCRC = (c && (c->m_do_crc16 || c->m_do_crc32)); } return rc; } bool ON_BinaryArchive::BeginWriteDictionary( ON_UUID dictionary_id, unsigned int version, const wchar_t* dictionary_name ) { #if defined(ON_COMPILER_MSC) // Disable the MSC /W4 "conditional expression is constant" warning // about sizeof(unsigned short) == sizeof(*dictionary_name). // Since this code has to run on machines where sizeof(wchar_t) // can be 2, 4, or 8 bytes, the test is necessary. #pragma ON_PRAGMA_WARNING_PUSH #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) #endif bool rc = BeginWrite3dmChunk(TCODE_DICTIONARY,1,0); if ( !rc ) return rc; // Write dictionary id chunk rc = BeginWrite3dmChunk(TCODE_DICTIONARY_ID,1,0); if ( rc ) { for(;;) { rc = WriteUuid(dictionary_id); if (!rc) break; rc = WriteInt(version); if (!rc) break; if ( 2 == sizeof(*dictionary_name) ) { rc = WriteUTF16String((const unsigned short*)dictionary_name); } else { ON_wString s(dictionary_name); rc = WriteString(s); } if (!rc) break; break; } if ( !EndWrite3dmChunk() ) // TCODE_DICTIONARY_ID end rc = false; } if ( !rc ) EndWrite3dmChunk(); // TCODE_DICTIONARY end return rc; #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_POP #endif } bool ON_BinaryArchive::EndWriteDictionary() { int chunk_count = m_chunk.Count(); bool rc = ( chunk_count > 0 && TCODE_DICTIONARY == m_chunk[chunk_count-1].m_typecode ); if (rc) { rc = BeginWrite3dmChunk(TCODE_DICTIONARY_END,0); if (rc) rc = EndWrite3dmChunk(); // TCODE_DICTIONARY_END if ( !EndWrite3dmChunk() ) // TCODE_DICTIONARY rc = false; } return rc; } bool ON_BinaryArchive::BeginWriteDictionaryEntry( int de_type, const wchar_t* entry_name ) { #if defined(ON_COMPILER_MSC) // Disable the MSC /W4 "conditional expression is constant" warning // about sizeof(unsigned short) == sizeof(*entry_name). // Since this code has to run on machines where sizeof(wchar_t) // can be 2, 4, or 8 bytes, the test is necessary. #pragma ON_PRAGMA_WARNING_PUSH #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) #endif bool rc = BeginWrite3dmChunk(TCODE_DICTIONARY_ENTRY,0); if ( rc ) { for(;;) { rc = WriteInt(de_type); if (!rc) break; if ( sizeof(unsigned short) == sizeof(*entry_name) ) { rc = WriteUTF16String((const unsigned short*)entry_name); } else { ON_wString s(entry_name); rc = WriteString(s); } if (!rc) break; break; } if ( !rc ) EndWrite3dmChunk(); // TCODE_DICTIONARY_ENTRY } return rc; #if defined(ON_COMPILER_MSC) #pragma ON_PRAGMA_WARNING_POP #endif } bool ON_BinaryArchive::EndWriteDictionaryEntry() { int chunk_count = m_chunk.Count(); bool rc = ( chunk_count > 0 && TCODE_DICTIONARY_ENTRY == m_chunk[chunk_count-1].m_typecode ) ? EndWrite3dmChunk() : false; return rc; } bool ON_BinaryArchive::BeginReadDictionary( ON_UUID* dictionary_id, unsigned int* version, ON_wString& dictionary_name ) { int major_version = 0; int minor_version = 0; bool rc = BeginRead3dmChunk(TCODE_DICTIONARY,&major_version,&minor_version); if ( rc ) { for(;;) { rc = (1 == major_version); if (!rc) break; // Read dictionary id chunk rc = BeginRead3dmChunk(TCODE_DICTIONARY_ID,&major_version,&minor_version); if ( !rc ) break; for(;;) { rc = (1==major_version); if (!rc) break; ON_UUID id; rc = ReadUuid(id); if (!rc) break; if ( dictionary_id ) *dictionary_id = id; rc = ReadInt(version); if (!rc) break; rc = ReadString(dictionary_name); if (!rc) break; break; } if ( !EndRead3dmChunk() ) // TCODE_DICTIONARY_ID end rc = false; break; } if ( !rc ) EndRead3dmChunk(); // TCODE_DICTIONARY end } return rc; } bool ON_BinaryArchive::EndReadDictionary() { int chunk_count = m_chunk.Count(); bool rc = ( chunk_count > 0 && TCODE_DICTIONARY == m_chunk[chunk_count-1].m_typecode ) ? EndRead3dmChunk() : false; return rc; } int ON_BinaryArchive::BeginReadDictionaryEntry( int* de_type, ON_wString& entry_name ) { unsigned int tcode = 0; ON__INT64 chunk_length = 0; int chunk_count = m_chunk.Count(); int rc = ( chunk_count > 0 && TCODE_DICTIONARY == m_chunk[chunk_count-1].m_typecode ) ? (BeginRead3dmBigChunk(&tcode,&chunk_length)?1:0) : 0; if ( de_type ) *de_type = 0; if ( rc ) { if ( TCODE_DICTIONARY_ENTRY == tcode ) { for(;;) { rc = 0; if ( !ReadInt(de_type) ) { entry_name.Empty(); break; } if ( !ReadString(entry_name) ) { entry_name.Empty(); break; } rc = 1; break; } } else { rc = (TCODE_DICTIONARY_END == tcode) ? 2 : 0; } if ( 1 != rc ) { if ( !EndRead3dmChunk() ) rc = 0; } } return rc; } bool ON_BinaryArchive::EndReadDictionaryEntry() { int chunk_count = m_chunk.Count(); bool rc = ( chunk_count > 0 && TCODE_DICTIONARY_ENTRY == m_chunk[chunk_count-1].m_typecode ) ? EndRead3dmChunk() : false; return rc; } bool ON_BinaryArchive::Read3dmGoo( ON_3dmGoo& goo ) { // goo is an entire "chunk" that is not short. // A call to EndRead3dmChunk() must immediately follow // the call to Read3dmGoo(). bool rc = false; if (goo.m_goo) { onfree(goo.m_goo); goo.m_goo = 0; } goo.m_typecode = 0; goo.m_value = 0; ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if (c) { goo.m_typecode = c->m_typecode; if ( c->m_bLongChunk ) rc = DownSizeUINT(c->Length(),(ON__UINT32*)&goo.m_value); else rc = DownSizeINT(c->m_big_value,&goo.m_value); if ( rc && c->m_bLongChunk && c->m_big_value > 0 ) { if ( CurrentPosition() == c->m_start_offset ) { // read the rest of this chunk into the goo.m_goo buffer. // 23 January 2004 Dale Lear: // Have to turn of CRC checking because the goo may contiain // subchunks. If a CRC exixts, it is at the end of the // goo and will persist until the application that // wrote this chunk is available to parse the chunk. c->m_do_crc16 = 0; c->m_do_crc32 = 0; m_bDoChunkCRC = false; size_t sizeof_goo = (size_t)c->Length(); goo.m_goo = (unsigned char*)onmalloc( sizeof_goo ); rc = ReadByte( sizeof_goo, goo.m_goo ); } } } return rc; } bool ON_BinaryArchive::WriteChunkTypecode( ON__UINT32 typecode ) { return WriteInt32(1,(ON__INT32*)&typecode); } bool ON_BinaryArchive::ReadChunkTypecode( ON__UINT32* typecode ) { ON__UINT32 tc = 0; bool rc = ReadInt32(1,(ON__INT32*)&tc); if (rc && typecode ) *typecode = tc; return rc; } bool ON_BinaryArchive::WriteChunkValue( ON__UINT32 typecode, ON__INT64 big_value ) { bool rc; if ( 8 == SizeofChunkLength() ) { rc = WriteInt64(1,&big_value); } else if ( ON_IsUnsignedChunkTypecode(typecode) ) { // treat big_value as an unsigned int ON__UINT32 u32 = 0; rc = DownSizeUINT((ON__UINT64)big_value,&u32); if ( !WriteInt32(1,(ON__INT32*)&u32) ) rc = false; } else { // treat big_value as a signed int ON__INT32 v32 = 0; rc = DownSizeINT(big_value,&v32); if ( !WriteInt32(1,&v32) ) rc = false; } return rc; } bool ON_BinaryArchive::WriteChunkLength( ON__UINT64 length ) { bool rc; if ( 8 == SizeofChunkLength() ) { rc = WriteInt64(1,(ON__INT64*)&length); } else { ON__UINT32 u32 = 0; rc = DownSizeUINT(length,&u32); if ( !WriteInt32(1,(ON__INT32*)&u32) ) rc = false; } return rc; } bool ON_BinaryArchive::ReadEOFSizeOfFile( ON__UINT64* sizeof_file ) { bool rc; ON__INT64 u64 = 0; if ( 8 == SizeofChunkLength() ) { // file has a 8 byte file size rc = ReadInt64(1,(ON__INT64*)&u64); } else { // file has a 4 byte file size ON__UINT32 u32 = 0; rc = ReadInt32(1,(ON__INT32*)&u32); if ( rc ) u64 = u32; } if ( rc && 0 != sizeof_file ) *sizeof_file = u64; return rc; } bool ON_BinaryArchive::WriteEOFSizeOfFile( ON__UINT64 sizeof_file ) { bool rc; if ( 8 == SizeofChunkLength() ) { // file has a 8 byte file size rc = WriteInt64(1,(ON__INT64*)&sizeof_file); } else { // file has a 4 byte file size ON__UINT32 u32=0; DownSizeUINT(sizeof_file,&u32); rc = WriteInt32(1,(ON__INT32*)&u32); } return rc; } bool ON_BinaryArchive::ReadChunkValue( ON__UINT32 typecode, ON__INT64* value64 ) { bool rc; ON__INT64 i64 = 0; if ( 8 == SizeofChunkLength() ) { // file has a 8 byte chunk value rc = ReadInt64(1,&i64); } else { // file has a 4 byte chunk value if ( ON_IsUnsignedChunkTypecode(typecode) ) { // This Mickey Mouse is here to convince all compilers // that when we read a 4 byte value with the high bit set, // the resulting i64 value is positive. I.e., // 0xFFFFFFFF is converted to 0x00000000FFFFFFFF ON__UINT32 u32 = 0; ON__UINT64 u64 = 0; rc = ReadInt32(1,(ON__INT32*)&u32); if ( rc ) u64 = u32; i64 = (ON__INT64)u64; } else { // If we read a 4 byte value with the high bit set, // the resulting i64 value is negative. I.e., // -1 is converted to -1 (0xFFFFFFFF to 0xFFFFFFFFFFFFFFFF) ON__INT32 i32 = 0; rc = ReadInt32(1,&i32); i64 = i32; } } if ( rc && 0 != value64 ) *value64 = i64; return rc; } bool ON_BinaryArchive::ChunkBoundaryCheck() const { return m_bChunkBoundaryCheck; } void ON_BinaryArchive::SetChunkBoundaryCheck( bool bChunkBoundaryCheck ) { m_bChunkBoundaryCheck = bChunkBoundaryCheck ? true : false; } size_t ON_BinaryArchive::SizeofChunkLength() const { // Version 1 - 4 and early version 5 files had // 4 byte chunk lengths. In October of 2009, // 8 byte chunk lengths were phased in for V5 // files. return (m_3dm_version < 50) ? 4 : 8; } bool ON_BinaryArchive::PushBigChunk( ON__UINT32 typecode, ON__INT64 big_value ) { ON_3DM_BIG_CHUNK c; memset(&c,0,sizeof(c)); c.m_typecode = typecode; c.m_big_value = big_value; ON__UINT64 length = 0; ON__UINT64 sizeof_crc = 0; // | and & are BITOPS - do NOT change to || and && // // Some v1 files have a short chunk with typecode = 0. // // NOTE: RenderXXXX plug-in used zero as a typecode in material userdata, sigh ... // so ... const bool bLongChunk = (0 != typecode && 0 == (TCODE_SHORT & typecode)); doesn't work. // const bool bLongChunk = (0 == (TCODE_SHORT & typecode) && (0 != typecode || 1 != Archive3dmVersion()) && big_value >= 0 ); if ( bLongChunk ) { length = (ON__UINT64)big_value; if ( m_3dm_version == 1 && 0 != (TCODE_LEGACY_GEOMETRY & typecode) ) { // these legacy typecodes have 16 bit CRCs c.m_do_crc16 = 1; c.m_crc16 = 1; } else { // some other legacy typecodes that have 16 bit CRCs switch(typecode) { case TCODE_SUMMARY: if ( m_3dm_version == 1 ) { c.m_do_crc16 = 1; c.m_crc16 = 1; } break; case TCODE_OPENNURBS_OBJECT | TCODE_CRC | 0x7FFD: if ( m_3dm_version == 1 ) { // 1.1 uuid has a 16 bit crc c.m_do_crc16 = 1; c.m_crc16 = 1; } else { // 2.0 uuid has a 32 bit crc c.m_do_crc32 = 1; c.m_crc32 = 0; } break; default: if ( m_3dm_version != 1 && 0 != (TCODE_CRC & typecode) ) { // 32 bit CRC c.m_do_crc32 = 1; c.m_crc32 = 0; } break; } } c.m_bLongChunk = 1; sizeof_crc = c.m_do_crc32 ? 4 : (c.m_do_crc16 ? 2 : 0); } c.m_start_offset = CurrentPosition(); c.m_end_offset = c.m_start_offset; if (ReadMode() && length > sizeof_crc) c.m_end_offset += (length - sizeof_crc); m_bDoChunkCRC = c.m_do_crc16 || c.m_do_crc32; if ( m_chunk.Capacity() == 0 ) m_chunk.Reserve(128); m_chunk.Append( c ); return true; } void ON_BinaryArchive::EnableSave3dmRenderMeshes( unsigned int object_type_flags, bool bSave3dmRenderMeshes ) { if (bSave3dmRenderMeshes) { // set object type bits m_save_3dm_render_mesh_flags |= object_type_flags; } else { // clear object type bits unsigned int mask = ~object_type_flags; m_save_3dm_render_mesh_flags &= mask; } } unsigned int ON_BinaryArchive::Save3dmRenderMeshObjectTypeFlags() const { return m_save_3dm_render_mesh_flags; } bool ON_BinaryArchive::Save3dmRenderMesh( ON::object_type object_type ) const { return (0 != (object_type & m_save_3dm_render_mesh_flags)); } void ON_BinaryArchive::EnableSave3dmAnalysisMeshes( unsigned int object_type_flags, bool bSave3dmAnalysisMeshes ) { if (bSave3dmAnalysisMeshes) { // set object type bits m_save_3dm_analysis_mesh_flags |= object_type_flags; } else { // clear object type bits unsigned int mask = ~object_type_flags; m_save_3dm_analysis_mesh_flags &= mask; } } unsigned int ON_BinaryArchive::Save3dmAnalysisMeshObjectTypeFlags() const { return m_save_3dm_analysis_mesh_flags; } bool ON_BinaryArchive::Save3dmAnalysisMesh( ON::object_type object_type ) const { return (0 != (object_type & m_save_3dm_analysis_mesh_flags)); } void ON_BinaryArchive::SetUseBufferCompression( bool bUseBufferCompression ) { m_bUseBufferCompression = bUseBufferCompression ? true : false; } bool ON_BinaryArchive::UseBufferCompression() const { return m_bUseBufferCompression; } void ON_BinaryArchive::SetSave3dmPreviewImage( bool bSave3dmPreviewImage ) { m_bSave3dmPreviewImage = bSave3dmPreviewImage ? true : false; } bool ON_BinaryArchive::Save3dmPreviewImage() const { return m_bSave3dmPreviewImage; } void ON_BinaryArchive::SetModelSerialNumber( unsigned int model_serial_number, unsigned int reference_model_serial_number, unsigned int instance_definition_model_serial_number ) { m_SetModelComponentSerialNumbers = true; m_model_serial_number = model_serial_number; m_reference_model_serial_number = reference_model_serial_number; m_instance_definition_model_serial_number = instance_definition_model_serial_number; } void ON_BinaryArchive::ClearModelSerialNumber() { m_SetModelComponentSerialNumbers = false; m_model_serial_number = 0; m_reference_model_serial_number = 0; m_instance_definition_model_serial_number = 0; } unsigned int ON_BinaryArchive::ModelSerialNumber() const { return m_model_serial_number; } unsigned int ON_BinaryArchive::ReferenceModelSerialNumber() const { return m_reference_model_serial_number; } unsigned int ON_BinaryArchive::InstanceDefinitionModelSerialNumber() const { return m_instance_definition_model_serial_number; } void ON_BinaryArchive::SortUserDataFilter() { unsigned int count0 = m_user_data_filter.UnsignedCount(); if (count0 > 0) { if (count0 > 1) { m_user_data_filter.QuickSort(ON_UserDataItemFilter::Compare); // remove duplicates ON_UserDataItemFilter* f = m_user_data_filter.Array(); unsigned int i0 = 1; f[0].m_precedence = 0; f[1].m_precedence = 1; for (unsigned i = 2; i < count0; i++) { if (f[i0].m_application_id == f[i].m_application_id && f[i0].m_item_id == f[i].m_item_id) continue; i0++; if (i0 < i) f[i0] = f[i]; f[i0].m_precedence = i0; } if (i0 + 1 < count0) m_user_data_filter.SetCount(i0 + 1); } // simplify if ( ShouldSerializeAllUserData() ) { // all user data should be serialized. m_user_data_filter.Destroy(); } else if ( ShouldSerializeNoUserData() ) { // no user data should be serialized. m_user_data_filter.SetCount(1); m_user_data_filter.SetCapacity(1); m_user_data_filter[0].m_bSerialize = false; } } } static void GetFirst32BytesOf3dmFile( int version, char sVersion[33] ) { unsigned int i; const char* s = "3D Geometry File Format "; for ( i = 0; i < 32; i++) sVersion[i] = s[i]; sVersion[32] = 0; if ( version <= 0 ) sVersion[31] = '0'; else { i = 31; while(version > 0 && i > 23) { sVersion[i] = ('0' + ((char)(version %10))); i--; version /= 10; } } return; } bool ON_BinaryArchive::Begin3dmTable( ON::archive_mode expected_mode, ON_3dmArchiveTableType table ) { if (ON_3dmArchiveTableType::Unset != Active3dmTable()) { ON_ERROR("Attempt to begin reading or writing a 3dm archive table while another table is active."); // call End3dmTable() but do not modify m_3dm_active_table or m_3dm_previous_table values. const ON_3dmArchiveTableType saved_3dm_active_table = Active3dmTable(); const ON_3dmArchiveTableType saved_3dm_previous_table = Previous3dmTable(); End3dmTable(table,false); m_3dm_active_table = saved_3dm_active_table; m_3dm_previous_table = saved_3dm_previous_table; Internal_ReportCriticalError(); return false; } if (ON::archive_mode::read3dm != expected_mode && ON::archive_mode::write3dm != expected_mode) { ON_ERROR("Invalid expected_mode parameter value."); return End3dmTable(table,false); } if (expected_mode != Mode()) { ON_ERROR("Archive read/write mode is opposited expected_mode."); return End3dmTable(table,false); } if ( table == ON_3dmArchiveTableType::start_section ) { // m_3dm_version is set during reading of the start section. if (0 != m_3dm_version && ON::archive_mode::read3dm == Mode()) { ON_ERROR("Archive m_3dm_version is set during start section reading."); return End3dmTable(table,false); } } else { if (m_3dm_version <= 0) { ON_ERROR("Archive m_3dm_version <= 0."); return End3dmTable(table,false); } } if (1 == m_3dm_version && ON::archive_mode::write3dm == expected_mode) { ON_ERROR("Current opennurbs does not write version 1 files."); return End3dmTable(table,false); } if (ON_3dmArchiveTableType::Unset == table) { ON_ERROR("Invalid table parameter value."); return End3dmTable(table,false); } const unsigned int previous_index = static_cast(Previous3dmTable()); const unsigned int table_index = static_cast(table); if (table_index <= previous_index) { if (ON_3dmArchiveTableType::user_table != table || ON_3dmArchiveTableType::user_table != Previous3dmTable() ) { ON_ERROR("Multiple attempt to begin reading or writing a 3dm archive section."); return End3dmTable(table, false); } } for (ON_3dmTableStatusLink* table_link = m_3dm_table_status_list; nullptr != table_link; table_link = table_link->m_next ) { if (table == table_link->m_table_status.m_table_type) { if (ON_3dmArchiveTableType::user_table != table || ON_3dmArchiveTableType::user_table != Previous3dmTable() ) { ON_ERROR("Multiple attempt to begin reading or writing a 3dm archive section."); return End3dmTable(table, false); } } } const unsigned int settings_index = static_cast(ON_3dmArchiveTableType::settings_table); while (previous_index < settings_index) { const unsigned int start_index = static_cast(ON_3dmArchiveTableType::start_section); const unsigned int properties_index = static_cast(ON_3dmArchiveTableType::properties_table); if (table_index == start_index) break; if (previous_index == start_index && table_index == properties_index) break; if (previous_index == properties_index && table_index == settings_index) break; ON_ERROR("Must read/write 3dm archive start, properties and settings sections first."); // call End3dmTable() but do not modify m_3dm_previous_table value. const ON_3dmArchiveTableType saved_3dm_previous_table = Previous3dmTable(); End3dmTable(table, false); m_3dm_previous_table = saved_3dm_previous_table; return false; } // Begin reading the table m_3dm_active_table = table; ON_3dmTableStatusLink* table_link = new ON_3dmTableStatusLink(); table_link->m_table_status.m_table_type = table; table_link->m_table_status.m_state = ON_3dmArchiveTableStatus::TableState::Started; table_link->m_next = m_3dm_table_status_list; m_3dm_table_status_list = table_link; return true; } bool ON_BinaryArchive::Internal_Begin3dmTableRecord( ON_3dmArchiveTableType table ) { if (ON_3dmArchiveTableType::Unset == table) { Internal_ReportCriticalError(); ON_ERROR("Attempt to read/write a table record outside the scope of BeginRead/Write3dm...Table() / EndRead/Write3dm...Table()."); return false; } if (Active3dmTable() != table) { Internal_ReportCriticalError(); ON_ERROR("Attempt to read/write a table record of the wrong type."); return false; } else { if ( nullptr != m_3dm_table_status_list && table == m_3dm_table_status_list->m_table_status.m_table_type && ON_3dmArchiveTableStatus::TableState::Started == m_3dm_table_status_list->m_table_status.m_state ) m_3dm_table_status_list->m_table_status.m_state = ON_3dmArchiveTableStatus::TableState::InProgress; } if (false == ArchiveContains3dmTable(table)) { // Reading a 3dm archive that does not contain this table // Not an error condition. return false; } // Continue with attempt to write record. return true; } bool ON_BinaryArchive::Read3dmTableRecord( ON_3dmArchiveTableType table, void** ptr ) { if ( nullptr != ptr ) *ptr = 0; if (false == Internal_Begin3dmTableRecord(table)) return false; if (false == ArchiveContains3dmTable(table)) { // Reading a 3dm archive that does not contain this table // Not an error condition. return false; } if (nullptr == ptr) { // Caller doesn't want the actual elements. // Not an error condition. return false; } // Continue with attempt to read record. return true; } void ON_BinaryArchive::Internal_Increment3dmTableItemCount() { if (nullptr != m_3dm_table_status_list && Active3dmTable() == m_3dm_table_status_list->m_table_status.m_table_type && ON_3dmArchiveTableStatus::TableState::InProgress == m_3dm_table_status_list->m_table_status.m_state ) { m_3dm_table_status_list->m_table_status.m_item_count++; } else { ON_ERROR("Table item reading/writing not in progress."); } } bool ON_BinaryArchive::End3dmTable( ON_3dmArchiveTableType table, bool bSuccess ) { bool rc = bSuccess; bool bReportError = true; if (false == bSuccess) { Internal_ReportCriticalError(); bReportError = false; } if (0 == m_chunk.Count()) { if (table != Active3dmTable()) { ON_ERROR("End3dmTable() table does not match the active table setting."); rc = false; } if (static_cast(table) > static_cast(Previous3dmTable())) m_3dm_previous_table = table; else { if ( ON_3dmArchiveTableType::user_table != table || ON_3dmArchiveTableType::user_table != Previous3dmTable() ) { ON_ERROR("3dm archive tables read/written in incorrect order."); rc = false; } } if (false == rc && bReportError) { Internal_ReportCriticalError(); bReportError = false; } if ( nullptr != m_3dm_table_status_list && m_3dm_active_table == m_3dm_table_status_list->m_table_status.m_table_type ) m_3dm_table_status_list->m_table_status.m_state = ON_3dmArchiveTableStatus::TableState::Finished; m_3dm_active_table = ON_3dmArchiveTableType::Unset; } else { if (bReportError) { Internal_ReportCriticalError(); } ON_ERROR("End3dmTable() called while chunks are open."); rc = false; } if (false == rc && ON_3dmArchiveTableType::Unset == m_3dm_first_failed_table) { ON_ERROR("Reading or writing 3dm archive first failure."); m_3dm_first_failed_table = table; } return rc; } bool ON_BinaryArchive::WriteModelComponentName(const ON_ModelComponent & model_component) { ON_wString valid_name; if ( 0 == m_user_data_depth && ON_BinaryArchive::TableComponentType(this->m_3dm_active_table) == model_component.ComponentType() ) { bool bPermitReferencePrefix = false; if (false == ON_ModelComponent::IsValidComponentName(m_manifest, model_component, bPermitReferencePrefix, valid_name)) { ON_ERROR("Invalid component name."); } } else { // user data, list of layers on an idef, or ... valid_name = model_component.Name(); } return WriteString(valid_name); } bool ON_BinaryArchive::Write3dmStartSection( int version, const char* sInformation ) { if (!Begin3dmTable(ON::archive_mode::write3dm,ON_3dmArchiveTableType::start_section)) return false; m_archive_runtime_environment = ON::CurrentRuntimeEnvironment(); m_archive_3dm_start_section_comment = sInformation; if ( 0 == version ) version = ON_BinaryArchive::CurrentArchiveVersion(); // 2009 November 6 // Default 3dm files now have 8 byte chunk lengths. // 3dm archive version numbers >= 50 indicate the file has 8 byte chunk lengths. // Rather than change the hundreds of places in Rhino that use 5, 6, 7 ... // the "version *= 10" line handles it. if ( version >= 5 && version < 50 ) version *= 10; if ( version > ON_BinaryArchive::CurrentArchiveVersion() ) { ON_ERROR("3dm archive version must be <= ON_BinaryArchive::CurrentArchiveVersion() "); return End3dmTable(ON_3dmArchiveTableType::start_section,false); } if ( version < 2 || (version >= 5 && version < 50) || (version >= 50 && 0 != (version % 10)) ) { ON_ERROR("3dm archive version must be 2, 3, 4, 50 or 60"); return End3dmTable(ON_3dmArchiveTableType::start_section,false); } m_crc_error_count = 0; m_critical_error_count = 0; m_3dm_version = version; m_3dm_opennurbs_version = ON::Version(); SortUserDataFilter(); char sVersion[64]; memset( sVersion, 0, sizeof(sVersion) ); GetFirst32BytesOf3dmFile(version,sVersion); if (!WriteByte( 32, sVersion )) return false; if (!BeginWrite3dmBigChunk( TCODE_COMMENTBLOCK, 0 )) return false; bool rc = false; for (;;) { if ( sInformation && sInformation[0] ) { if (!WriteByte( strlen(sInformation), sInformation ) ) break; } // write information that helps determine what code wrote the 3dm file const ON_String runtime(Internal_RuntimeEnvironmentToString(ON::CurrentRuntimeEnvironment())); char s[2048]; const size_t s_capacity = sizeof(s)/sizeof(s[0]); int s_len = ON_String::FormatIntoBuffer( s,s_capacity, " Runtime: %s 3DM I/O processor: OpenNURBS toolkit version %u (compiled on " __DATE__ ")\n", static_cast(runtime), ON::Version() ); if ( s_len < 0 || s_len + 2 >= s_capacity) s_len = 0; s[s_len++] = 26; // ^Z s[s_len++] = 0; if (!WriteByte( s_len, s )) break; rc = true; break; } if ( !EndWrite3dmChunk() ) // need to call EndWrite3dmChunk() even if a WriteByte() has failed rc = false; return End3dmTable(ON_3dmArchiveTableType::start_section,rc); } static bool Internal_EndOfRuntimeName(char c) { return (c >= 0 && c <= 32); } static bool Internal_StartOfRuntimeName(char prev_c, char c) { return ( Internal_EndOfRuntimeName(prev_c) && ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) ); } static ON::RuntimeEnvironment Internal_RuntimeEnvironmentFromString( const ON_String& str ) { if (str.IsEmpty()) return ON::RuntimeEnvironment::Unset; // Look at the start section comment to see if the platform is mentioned. const ON_String runtime_windows(Internal_RuntimeEnvironmentToString(ON::RuntimeEnvironment::Windows)); const ON_String runtime_android(Internal_RuntimeEnvironmentToString(ON::RuntimeEnvironment::Android)); const ON_String runtime_apple(Internal_RuntimeEnvironmentToString(ON::RuntimeEnvironment::Apple)); const char* sRuntimeWindows[] = { static_cast(runtime_windows), "windows", nullptr }; const char* sRuntimeAndroid[] = { static_cast(runtime_android), "android", nullptr }; const char* sRuntimeApple[] = { static_cast(runtime_apple), "apple", "mac rhinoceros", nullptr }; const char capA = (char)'A'; const char capZ = (char)'Z'; const char toLowerAZ = (char)('a' - 'A'); for ( int i0 = 0; i0 < 2; i0++) { for (int pass = 0; pass < 3; pass++) { const char** tokens = nullptr; ON::RuntimeEnvironment r = ON::RuntimeEnvironment::Unset; switch (pass) { case 0: tokens = sRuntimeWindows; r = ON::RuntimeEnvironment::Windows; break; case 1: tokens = sRuntimeAndroid; r = ON::RuntimeEnvironment::Android; break; case 2: tokens = sRuntimeApple; r = ON::RuntimeEnvironment::Apple; break; } for (int i = i0; nullptr != tokens[i]; i++) { char prev_c = 0; for (const char* s = static_cast(str); 0 != *s; prev_c = *s++) { if (!Internal_StartOfRuntimeName(prev_c, *s)) continue; const char* t = tokens[i]; const char* s1 = s; while (0 != *t && 0 != *s1) { char c = *s1++; if (0 != i0) { if (c >= capA && c <= capZ) c += toLowerAZ; } if (c != *t++) break; } if (0 == *t && Internal_EndOfRuntimeName(*s1)) { return r; } } if (0 == i0) break; } } } return ON::RuntimeEnvironment::Unset; } static ON::RuntimeEnvironment Internal_RuntimeEnvironmentFromWideString( const ON_wString& wide_string ) { const ON_String str(wide_string); return Internal_RuntimeEnvironmentFromString(str); } bool ON_BinaryArchive::Read3dmStartSection( int* version, ON_String& s ) { // The first 24 bytes of a 3dm file must be "3D Geometry File Format " // The next 8 bytes must be a right justified ASCII integer >= 1. For // example, prior to March 2000, all 3DM files were version 1 files and // they began with "3D Geometry File Format 1". At the time of // this writing (May 2011) there are version 1,2,3,4,5 and 50 files. // // The next section must contain a long chunk with typecode 1. if (!Begin3dmTable(ON::archive_mode::read3dm,ON_3dmArchiveTableType::start_section)) return false; m_archive_3dm_start_section_comment = ON_String::EmptyString; SortUserDataFilter(); unsigned int typecode = 0; ON__INT64 length = -1; if ( version ) *version = m_3dm_version; s.Destroy(); char s3d[33]; memset( s3d, 0, sizeof(s3d) ); if (!ReadByte( 32, s3d )) return false; if ( 0 != strncmp( s3d, "3D Geometry File Format ", 24) ) { // it's not a "pure" .3DM file // - see if we have a .3DM file with MS OLE-goo at the start // (generally, there is around 6kb of goo. I keep looking // for up to 32mb just in case.) unsigned int offset = 0; for ( unsigned int n = 0; n < 33554432; n++ ) { for ( int j = 0; j < 31; j++ ) s3d[j] = s3d[j+1]; if ( !ReadByte( 1, &s3d[31]) ) break; if ( 0 == strncmp( s3d, "3D Geometry File Format ", 24) ) { offset = n+1; break; } } if (0 == offset) { ON_ERROR("3dm start section header not found."); return End3dmTable(ON_3dmArchiveTableType::start_section,false); } m_3dm_start_section_offset = offset; } // get version //char* sVersion = s3d+24; // skip leading spaces int ver = 0; int i = 24; while (i < 32 && s3d[i] == ' ') i++; while (i < 32) { // TEMPORARY 2 = X if ( i == 31 && s3d[i] == 'X' ) { s3d[i] = '2'; } if ( s3d[i] < '0' || s3d[i] > '9' ) { // it's not a valid .3DM file version ON_ERROR("3dm start section header is not valid."); return End3dmTable(ON_3dmArchiveTableType::start_section,false); } ver = ver*10 + ((int)(s3d[i]-'0')); i++; } if (ver <= 0) { ON_ERROR("3dm start section version is not valid."); return End3dmTable(ON_3dmArchiveTableType::start_section,false); } m_3dm_version = ver; if ( version ) *version = ver; if ( !BeginRead3dmBigChunk( &typecode, &length ) ) return false; bool rc = false; for (;;) { if ( TCODE_COMMENTBLOCK != typecode ) { // it's not a .3DM file // Fix https://mcneel.myjetbrains.com/youtrack/issue/RH-36190 // If the first chunk is damaged, don't start seeking. // In the bug refereced above, the chunk length was 10^63-ish // because the user had edited a .3dm file with a text editor. m_chunk.Remove(); return false; } if ( length > 0 ) { if ( length > 0x00FFFFFF ) { ON_ERROR("ON_BinaryArchive::Read3dmStartSection - start section string is unreasonably long."); rc = false; } else { int slen = (int)length; s.ReserveArray( slen+1 ); s.SetLength( slen ); s[slen] = 0; rc = ReadByte( slen, s.Array() ); if (rc) { while (slen > 0 && (0 == s[slen - 1] || 26 == s[slen - 1])) { s[slen - 1] = 0; slen--; } s.SetLength(slen); } } } rc = true; break; } if ( !EndRead3dmChunk() ) rc = false; m_archive_3dm_start_section_comment = s; if (ON::RuntimeEnvironment::Unset == m_archive_runtime_environment) { m_archive_runtime_environment = Internal_RuntimeEnvironmentFromString(m_archive_3dm_start_section_comment); } if ( !rc ) return false; if ( 1 == m_3dm_version ) { // In March 2001, we got reports of files with V1 headers and // a V2 bodies. We haven't been able to track down the application // that is creating these damaged files, but we can detect them // and read them because they all have a TCODE_PROPERTIES_TABLE // chunk right after the start comments chunk and no valid V1 // file has a chunk with a TCODE_PROPERTIES_TABLE tcode. // // Rhino 1.1 version 31-May-2000 reads 2.0 files as goo. If a user // saves this file for some reason (no instances have been reported) // the resulting file has a V1 header, V1 fluff, and a V2 body. // This code will cause opennurbs to read the V2 body. // a file that is different from those describe This code // detects these files. { const ON__UINT64 pos1 = CurrentPosition(); //int v1_fluff_chunk_count = 0; bool bCheckChunks = true; ////////// while(bCheckChunks) { if ( !PeekAt3dmBigChunkType(&typecode,&length) ) break; switch(typecode) { case TCODE_SUMMARY: case TCODE_BITMAPPREVIEW: case TCODE_UNIT_AND_TOLERANCES: case TCODE_VIEWPORT: case TCODE_LAYER: case TCODE_RENDERMESHPARAMS: case TCODE_CURRENTLAYER: case TCODE_ANNOTATION_SETTINGS: case TCODE_NOTES: case TCODE_NAMED_CPLANE: case TCODE_NAMED_VIEW: // skip potential v1 fluff bCheckChunks = BeginRead3dmBigChunk( &typecode, &length ); if ( bCheckChunks ) bCheckChunks = EndRead3dmChunk(); break; //case TCODE_PROPERTIES_TABLE: //case TCODE_SETTINGS_TABLE: //case TCODE_OBJECT_TABLE: //case TCODE_BITMAP_TABLE: //case TCODE_LAYER_TABLE: //case TCODE_GROUP_TABLE: //case TCODE_LIGHT_TABLE: //case TCODE_MATERIAL_TABLE: //case TCODE_USER_TABLE: default: if ( TCODE_TABLE == (typecode & 0xFFFF0000) ) { // Found a V2 table which has to be V1 goo ON_WARNING("ON_BinaryArchive::Read3dmStartSection(): Archive has V1 header and V2 body. Continuing to read V2 body."); m_3dm_version = 2; if ( version ) *version = 2; } bCheckChunks = false; break; } } if ( m_3dm_version == 1 ) { // move archive pointer back to ON__UINT64 pos2 = CurrentPosition(); if ( pos2 > pos1 ) { SeekBackward(pos2 - pos1); } } } } if (0 == m_3dm_version) { ON_ERROR("3dm archive start section is damaged."); m_3dm_version = 10*ON::VersionMajor(); rc = false; } return End3dmTable(ON_3dmArchiveTableType::start_section,rc); } const ON_String& ON_BinaryArchive::Archive3dmStartSectionComment() const { return m_archive_3dm_start_section_comment; } const ON_3dmProperties& ON_BinaryArchive::Archive3dmProperties() const { if (nullptr != m_archive_3dm_properties) return *m_archive_3dm_properties; return ON_3dmProperties::Empty; } const ON_3dmSettings& ON_BinaryArchive::Archive3dmSettings() const { if (nullptr != m_archive_3dm_settings) return *m_archive_3dm_settings; return ON_3dmSettings::Default; } bool ON_BinaryArchive::Write3dmProperties( const ON_3dmProperties& prop ) { if (!Begin3dmTable(ON::archive_mode::write3dm,ON_3dmArchiveTableType::properties_table)) return false; if (nullptr != m_archive_3dm_properties) { delete m_archive_3dm_properties; m_archive_3dm_properties = nullptr; } // version 2+ file properties chunk bool rc = BeginWrite3dmChunk(TCODE_PROPERTIES_TABLE,0); if ( rc ) { rc = prop.Write( *this )?true:false; if ( !EndWrite3dmChunk() ) rc = false; } const bool final_rc = End3dmTable(ON_3dmArchiveTableType::properties_table,rc); if (final_rc) { m_archive_3dm_properties = new ON_3dmProperties(prop); } return final_rc; } int on_strnicmp(const char * s1, const char * s2, int n) { #if defined(ON_RUNTIME_WIN) //return stricmp(s1,s2,n); return _strnicmp(s1,s2,n); #else return strncasecmp(s1,s2,n); #endif } bool ON_BinaryArchive::Read3dmProperties( ON_3dmProperties& prop ) { prop = ON_3dmProperties::Empty; if (!Begin3dmTable(ON::archive_mode::read3dm,ON_3dmArchiveTableType::properties_table)) return false; if (nullptr != m_archive_3dm_properties) { delete m_archive_3dm_properties; m_archive_3dm_properties = nullptr; } // In ON_3dmProperties::Read(), m_3dm_opennurbs_version will be // set to the version of OpenNURBS that was used to write this archive. // If the file was written with by a pre 200012210 version of OpenNURBS, // then m_3dm_opennurbs_version will be zero. m_3dm_opennurbs_version = 0; bool rc = true; // we need these when reading version 1 files const ON__UINT64 pos0 = CurrentPosition(); bool bHaveRevisionHistory = false; bool bHaveNotes = false; bool bHavePreviewImage = false; bool bDone = false; bool bRewindFilePointer = false; ON__UINT32 tcode; ON__INT64 big_value; int version = 0; if ( m_3dm_version != 1 ) { for(;;) { tcode = 0; big_value = 0; rc = BeginRead3dmBigChunk( &tcode, &big_value ); if ( !rc ) { bRewindFilePointer = true; break; } if ( tcode == TCODE_PROPERTIES_TABLE ) { rc = prop.Read(*this)?true:false; if (rc) { // m_archive_saved_as_full_path // = path where file was originally saved. // When m_archive_saved_as_full_path != m_archive_full_path, // then the file has been moved since it was saved and // finding referenced files can get more difficult // if they were moved as well. That's when the relative // path information in ON_FileReference classes // is used in conjunction with m_archive_saved_as_full_path. m_archive_saved_as_full_path = prop.m_3dmArchiveFullPathName; if (m_archive_full_path.IsNotEmpty() && m_archive_saved_as_full_path.IsNotEmpty() && m_archive_full_path.ComparePath(m_archive_saved_as_full_path) ) { m_b3dmArchiveMoved = true; } } } else { bRewindFilePointer = true; } if ( !EndRead3dmChunk() ) { rc = false; bRewindFilePointer = true; } if ( tcode == TCODE_PROPERTIES_TABLE || !rc ) break; } } else { // version 1 file rc = SeekFromStart(32)?true:false; bRewindFilePointer = true; for(;;) { tcode = 0; big_value = 0; rc = BeginRead3dmBigChunk( &tcode, &big_value ); if ( !rc ) { rc = true; // assume we are at the end of the file bRewindFilePointer = true; break; } switch ( tcode ) { case 1: // comments section has application name if ( big_value > 1000000 ) { ON_ERROR("Comment length > 1000000"); } else if ( big_value > 1 ) { int slen = (int)big_value; int i; char* name = 0; ON_String s; s.ReserveArray( slen+1 ); s.SetLength( slen ); s[slen] = 0; ReadByte( slen, s.Array() ); while ( slen > 0 && (0 == s[slen-1] || 26 == s[slen-1]) ) { s[slen-1] = 0; slen--; } s.SetLength(slen); name = s.Array(); if ( name ) { while(*name) { if ( !on_strnicmp(name,"Interface:",10) ) { name += 10; break; } name++; } while(*name && *name <= 32 ) name++; for ( i = 0; name[i] ; i++ ) { if ( name[i] == '(' ) { name[i] = 0; while ( i > 0 && (name[i] <= 32 || name[i] == '-') ) { name[i] = 0; i--; } break; } } if ( *name ) { char* details = 0; if ( !on_strnicmp(name,"Rhinoceros",10) ) { prop.m_Application.m_application_URL = "http://www.rhino3d.com"; details = name+10; while ( *details && *details <= 32 ) details++; while ( (*details >= '0' && *details <= '9') || *details == '.' ) details++; if ( *details && *details <= 32 ) { *details = 0; details++; while ( *details && (*details <= 32 ||*details == '-')) { details++; } } } if (*name) prop.m_Application.m_application_name = name; if (details && *details) prop.m_Application.m_application_details = details; } } } break; case TCODE_SUMMARY: // version 1 revision history chunk (has 16 bit CRC) version = 1; bHaveRevisionHistory = true; { int slength = 0; char* s = 0; if (rc) rc = ReadInt(&slength); if (rc && slength > 0 ) { s = (char*)onmalloc((slength+1)*sizeof(*s)); memset( s, 0, (slength+1)*sizeof(*s) ); if (rc) rc = ReadChar( slength, s ); if ( rc ) prop.m_RevisionHistory.m_sCreatedBy = s; onfree(s); slength = 0; s = 0; } if (rc) rc = ReadTime( prop.m_RevisionHistory.m_create_time ); int i32 = 0; if (rc) rc = ReadInt(&i32); // 0 in 1.x files if (rc) rc = ReadInt(&slength); if ( rc && slength > 0 ) { s = (char*)onmalloc((slength+1)*sizeof(*s)); memset( s, 0, (slength+1)*sizeof(*s) ); if (rc) rc = ReadChar( slength, s ); if ( rc ) prop.m_RevisionHistory.m_sLastEditedBy = s; onfree(s); slength = 0; s = 0; } if (rc) rc = ReadTime( prop.m_RevisionHistory.m_last_edit_time ); if (rc) rc = ReadInt(&i32); // 0 in 1.x files if (rc) rc = ReadInt( &prop.m_RevisionHistory.m_revision_count ); } break; case TCODE_NOTES: // version 1 notes chunk version = 1; bHaveNotes = true; for(;;) { int slength; char* s = 0; int i = prop.m_Notes.m_bVisible; rc = ReadInt( &i ); if(!rc) break; prop.m_Notes.m_bVisible = i ? true : false; rc = ReadInt( &prop.m_Notes.m_window_left ); if(!rc) break; rc = ReadInt( &prop.m_Notes.m_window_top ); if(!rc) break; rc = ReadInt( &prop.m_Notes.m_window_right ); if(!rc) break; rc = ReadInt( &prop.m_Notes.m_window_bottom ); if(!rc) break; rc = ReadInt( &slength ); if(!rc) break; if ( slength > 0 ) { s = (char*)onmalloc( (slength+1)*sizeof(*s) ); memset( s, 0, (slength+1)*sizeof(*s) ); if ( rc ) rc = ReadChar( slength, s ); if ( rc ) { prop.m_Notes.m_notes = s; } onfree(s); slength = 0; s = 0; } break; } break; case TCODE_BITMAPPREVIEW: // version 1 preview image chunk version = 1; rc = prop.m_PreviewImage.Read(*this)?true:false; bHavePreviewImage = rc; break; case TCODE_CURRENTLAYER: case TCODE_LAYER: // version 1 layer and current layer chunks always came after notes/bitmap/summary bDone = true; bRewindFilePointer = true; break; default: // the call to EndRead3dmChunk() will skip over this chunk bRewindFilePointer = true; break; } // this call to EndRead3dmChunk() skips any unread portions of the chunk if ( !EndRead3dmChunk() ) { rc = false; bRewindFilePointer = true; } if ( bHaveRevisionHistory && bHaveNotes && bHavePreviewImage ) bDone = true; if ( bDone || !rc ) break; } } if ( bRewindFilePointer ) { // reposition file pointer to pos0 const ON__UINT64 pos1 = CurrentPosition(); if ( pos0 != pos1 ) { if (pos1 > pos0) SeekBackward(pos1-pos0); else if ( pos1 < pos0 ) SeekForward(pos0-pos1); } } if (0 == m_3dm_opennurbs_version) { if (m_3dm_version <= 2) { // 200012210 is the earliest known opennurbs version number // Earlier versions are marked as 0. m_3dm_opennurbs_version = 200012210; } else { ON_ERROR("The 3dm archive properties section is damaged."); m_3dm_opennurbs_version = ON::Version(); } } const bool final_rc = End3dmTable(ON_3dmArchiveTableType::properties_table,rc); if (ON::RuntimeEnvironment::Unset == m_archive_runtime_environment) m_archive_runtime_environment = Internal_RuntimeEnvironmentFromWideString(prop.m_Application.m_application_name); if (ON::RuntimeEnvironment::Unset == m_archive_runtime_environment) m_archive_runtime_environment = Internal_RuntimeEnvironmentFromWideString(prop.m_Application.m_application_details); if (final_rc) { m_archive_3dm_properties = new ON_3dmProperties(prop); } return final_rc; } bool ON_BinaryArchive::Write3dmSettings( const ON_3dmSettings& settings ) { if (!Begin3dmTable(ON::archive_mode::write3dm,ON_3dmArchiveTableType::settings_table)) return false; if (nullptr != m_archive_3dm_settings) { delete m_archive_3dm_settings; m_archive_3dm_settings = nullptr; } // version 2+ file settings chunk bool rc = BeginWrite3dmChunk(TCODE_SETTINGS_TABLE,0); if ( rc ) { rc = settings.Write( *this ); if ( !EndWrite3dmChunk() ) rc = false; } if ( rc && 3 == Archive3dmVersion() ) { // Build a list of ids of plug-ins that support saving // V3 user data. If a plug-in id is not in this list, // the user data will not be saved in the V3 archive. int i, count = settings.m_plugin_list.Count(); m_V3_plugin_id_list.SetCount(0); m_V3_plugin_id_list.SetCapacity( count+5 ); for ( i = 0; i < count; i++ ) { const ON_UUID& pid = settings.m_plugin_list[i].m_plugin_id; if ( !ON_UuidIsNil(pid) ) m_V3_plugin_id_list.Append(pid); } // These ids insure V3, V4 and V5 core user data will round trip // through SaveAs V3 and SaveAs V4 m_V3_plugin_id_list.Append( ON_v3_userdata_id ); m_V3_plugin_id_list.Append( ON_v4_userdata_id ); m_V3_plugin_id_list.Append( ON_opennurbs4_id ); m_V3_plugin_id_list.Append( ON_opennurbs5_id ); m_V3_plugin_id_list.Append( ON_opennurbs6_id ); m_V3_plugin_id_list.Append( ON_rhino3_id ); m_V3_plugin_id_list.Append( ON_rhino4_id ); m_V3_plugin_id_list.Append( ON_rhino5_id ); m_V3_plugin_id_list.Append( ON_rhino6_id ); m_V3_plugin_id_list.QuickSort( ON_UuidCompare ); } bool final_rc = End3dmTable(ON_3dmArchiveTableType::settings_table,rc); if (final_rc) { m_archive_3dm_settings = new ON_3dmSettings(settings); m_annotation_context.SetReferencedAnnotationSettings(&m_archive_3dm_settings->m_AnnotationSettings); m_annotation_context.SetModelLengthUnitSystem(m_archive_3dm_settings->m_ModelUnitsAndTolerances.m_unit_system.UnitSystem()); m_annotation_context.SetPageLengthUnitSystem(m_archive_3dm_settings->m_PageUnitsAndTolerances.m_unit_system.UnitSystem()); } return final_rc; } bool ON_BinaryArchive::Read3dmSettings( ON_3dmSettings& settings ) { if (!Begin3dmTable(ON::archive_mode::read3dm,ON_3dmArchiveTableType::settings_table)) return false; if (nullptr != m_archive_3dm_settings) { delete m_archive_3dm_settings; m_archive_3dm_settings = nullptr; } bool rc = false; ON__UINT32 tcode; ON__INT64 big_value; if ( m_3dm_version == 1 ) { // read legacy v 1 info that is scattered around the file rc = settings.Read(*this); } else { rc = true; while(rc) { tcode = 0; big_value = 0; rc = BeginRead3dmBigChunk( &tcode, &big_value ); if ( !rc ) break; if ( tcode == TCODE_SETTINGS_TABLE ) { // version 2 model settings rc = settings.Read(*this); } if ( !EndRead3dmChunk() ) { rc = false; break; } if ( TCODE_SETTINGS_TABLE == tcode ) break; } } bool final_rc = End3dmTable(ON_3dmArchiveTableType::settings_table,rc); if (final_rc) { m_archive_3dm_settings = new ON_3dmSettings(settings); m_annotation_context.SetReferencedAnnotationSettings(&m_archive_3dm_settings->m_AnnotationSettings); } return final_rc; } bool ON_BinaryArchive::ArchiveContains3dmTable( ON_3dmArchiveTableType table, unsigned int archive_3dm_version, unsigned int opennurbs_library_version ) { if ( archive_3dm_version <= 0 ) return false; if (archive_3dm_version > 5) { if (archive_3dm_version < 50) return false; if (0 != archive_3dm_version % 10) return false; } if ( archive_3dm_version >= 3 && opennurbs_library_version <= 0 ) return false; bool rc; switch (table) { case ON_3dmArchiveTableType::Unset: rc = false; break; case ON_3dmArchiveTableType::start_section: rc = true; break; case ON_3dmArchiveTableType::properties_table: rc = true; break; case ON_3dmArchiveTableType::settings_table: rc = true; break; case ON_3dmArchiveTableType::bitmap_table: rc = (archive_3dm_version >= 2); break; case ON_3dmArchiveTableType::texture_mapping_table: rc = (archive_3dm_version >= 4 && opennurbs_library_version >= 200511110); break; case ON_3dmArchiveTableType::material_table: rc = true; break; case ON_3dmArchiveTableType::linetype_table: rc = (archive_3dm_version >= 4 && opennurbs_library_version >= 200503170 ); break; case ON_3dmArchiveTableType::layer_table: rc = true; break; case ON_3dmArchiveTableType::group_table: rc = (archive_3dm_version >= 2 && opennurbs_library_version >= 200012210); break; case ON_3dmArchiveTableType::text_style_table: rc = (archive_3dm_version >= 3 && opennurbs_library_version >= 200109180); break; case ON_3dmArchiveTableType::dimension_style_table: rc = (archive_3dm_version >= 3 && opennurbs_library_version >= 200109260); break; case ON_3dmArchiveTableType::light_table: rc = true; break; case ON_3dmArchiveTableType::hatchpattern_table: rc = (archive_3dm_version >= 4 && opennurbs_library_version >= 200405030); break; case ON_3dmArchiveTableType::instance_definition_table: rc = (archive_3dm_version >= 3 && opennurbs_library_version >= 200205110); break; case ON_3dmArchiveTableType::object_table: rc = true; break; case ON_3dmArchiveTableType::historyrecord_table: rc = ( archive_3dm_version >= 4 && opennurbs_library_version >= 200601180 ); break; case ON_3dmArchiveTableType::user_table: rc = ( archive_3dm_version >= 4); break; case ON_3dmArchiveTableType::end_mark: rc = true; break; default: rc = false; break; } return rc; } bool ON_BinaryArchive::ArchiveContains3dmTable( ON_3dmArchiveTableType table ) const { unsigned int opennurbs_library_version = 0; if (ON::archive_mode::read3dm == Mode()) opennurbs_library_version = m_3dm_opennurbs_version; else if (ON::archive_mode::write3dm == Mode()) opennurbs_library_version = ON::Version(); if ( 0 == opennurbs_library_version && m_3dm_version <= 3) opennurbs_library_version = 200012210; return ON_BinaryArchive::ArchiveContains3dmTable( table, m_3dm_version, opennurbs_library_version ); } ON_3dmArchiveTableType ON_BinaryArchive::TableTypeFromTypecode( unsigned int typecode ) { ON_3dmArchiveTableType tt; switch(typecode) { case 0: tt = m_3dm_active_table; break; case TCODE_PROPERTIES_TABLE: tt = ON_3dmArchiveTableType::properties_table; break; case TCODE_SETTINGS_TABLE: tt = ON_3dmArchiveTableType::settings_table; break; case TCODE_BITMAP_TABLE: tt = ON_3dmArchiveTableType::bitmap_table; break; case TCODE_TEXTURE_MAPPING_TABLE: tt = ON_3dmArchiveTableType::texture_mapping_table; break; case TCODE_MATERIAL_TABLE: tt = ON_3dmArchiveTableType::material_table; break; case TCODE_LINETYPE_TABLE: tt = ON_3dmArchiveTableType::linetype_table; break; case TCODE_LAYER_TABLE: tt = ON_3dmArchiveTableType::layer_table; break; case TCODE_LIGHT_TABLE: tt = ON_3dmArchiveTableType::light_table; break; case TCODE_OBJECT_TABLE: tt = ON_3dmArchiveTableType::object_table; break; case TCODE_GROUP_TABLE: tt = ON_3dmArchiveTableType::group_table; break; case TCODE_FONT_TABLE: tt = ON_3dmArchiveTableType::text_style_table; break; case TCODE_DIMSTYLE_TABLE: tt = ON_3dmArchiveTableType::dimension_style_table; break; case TCODE_HATCHPATTERN_TABLE: tt = ON_3dmArchiveTableType::hatchpattern_table; break; case TCODE_INSTANCE_DEFINITION_TABLE: tt = ON_3dmArchiveTableType::instance_definition_table; break; case TCODE_HISTORYRECORD_TABLE: tt = ON_3dmArchiveTableType::historyrecord_table; break; case TCODE_USER_TABLE: tt = ON_3dmArchiveTableType::user_table; break; default: ON_ERROR("invalid typecode value"); tt = ON_3dmArchiveTableType::Unset; break; } return tt; } bool ON_BinaryArchive::BeginWrite3dmTable( unsigned int typecode ) { if (0 == typecode) { ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() bad typecode"); return false; } const ON_3dmArchiveTableType tt = TableTypeFromTypecode(typecode); if (tt == ON_3dmArchiveTableType::Unset) { ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() bad typecode"); return false; } if ( Active3dmTable() != ON_3dmArchiveTableType::Unset ) { ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() m_active_table != no_active_table"); return false; } if ( 0 != m_chunk.Count() ) { ON_ERROR("ON_BinaryArchive::BeginWrite3dmTable() m_chunk.Count() != 0"); return false; } if (false == Begin3dmTable(ON::archive_mode::write3dm,tt)) return false; bool rc; if (ArchiveContains3dmTable(tt)) { rc = BeginWrite3dmChunk(typecode, 0); if (false == rc) End3dmTable(tt, false); } else rc = true; return rc; } bool ON_BinaryArchive::EndWrite3dmTable( unsigned int typecode ) { const ON_3dmArchiveTableType tt = TableTypeFromTypecode(typecode); if (tt == ON_3dmArchiveTableType::Unset) { ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() bad typecode"); return false; } if ( Active3dmTable() != tt ) { ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() m_active_table != t"); return false; } if ( m_chunk.Count() != 1 ) { ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() m_chunk.Count() != 1"); return false; } const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( 0 == c || c->m_typecode != typecode ) { ON_ERROR("ON_BinaryArchive::EndWrite3dmTable() m_chunk.Last()->typecode != typecode"); return false; } bool rc = BeginWrite3dmChunk( TCODE_ENDOFTABLE, 0 ); if (rc) { if (!EndWrite3dmChunk()) rc = false; } if (!EndWrite3dmChunk()) rc = false; Flush(); return End3dmTable(tt,rc); } bool ON_BinaryArchive::BeginRead3dmTable( unsigned int typecode ) { bool rc = false; if (ON::archive_mode::read3dm != Mode()) { ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() ON::archive_mode::read3dm != Mode()"); return false; } if (0 == typecode) { ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() bad typecode"); return false; } const ON_3dmArchiveTableType tt = TableTypeFromTypecode(typecode); if (tt == ON_3dmArchiveTableType::Unset) { ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() bad typecode"); return false; } const bool bUserTable = ( TCODE_USER_TABLE == typecode || ON_3dmArchiveTableType::user_table == tt ); if (static_cast(tt) <= static_cast(ON_3dmArchiveTableType::settings_table)) { ON_ERROR("ON_BinaryArchive::BeginRead3dmTable cannot be used for start, properties, or settings table."); return false; } if (false == ArchiveContains3dmTable(tt)) { // table type was added after the code that wrote the // file was compiled or after this code was compiled. if ( bUserTable ) return false; // false for user tables // true for the other tabelss if things are as they should be return Begin3dmTable(ON::archive_mode::read3dm,tt); } if ( m_3dm_version <= 1 ) { // version 1 files had can have chunks in any order. To read a "table", // you have to go through the entire list of chunks looking for those // that belong to a particular table. rc = SeekFromStart(32)?true:false; if (Begin3dmTable(ON::archive_mode::read3dm, tt)) { if (false == rc) End3dmTable(tt, false); } return rc; } ON__UINT32 tcode = 0; ON__INT64 big_value = 0; rc = PeekAt3dmBigChunkType( &tcode, &big_value ); if ( rc ) { if ( tcode != typecode ) { if ( bUserTable ) { // it's ok to not have user tables return false; } // A required table is not at the current position in the archive // see if we can find it someplace else in the archive. This can // happen when old code encounters a table that was added later. bool bSeekFromStart = true; if ( TCODE_HATCHPATTERN_TABLE == tcode && TCODE_INSTANCE_DEFINITION_TABLE == typecode && 3 == m_3dm_version && 200405190 <= m_3dm_opennurbs_version ) { // Dale Lear // V3 files from 19 may 2004 on contained bogus hatch pattern tables // where the instance definition table was supposed to be. // // Do not set rc in this code. The goal of this code is to // avoid seeking from the start of the file and posting // an ON_ERROR alert about something we can work around // and has been fixed in V4. bSeekFromStart = false; tcode = 0; big_value = 0; if (BeginRead3dmBigChunk(&tcode, &big_value)) { if (EndRead3dmChunk()) { tcode = 0; big_value = 0; if (TCODE_HATCHPATTERN_TABLE == tcode) { // We've skipped the incorrectly positioned hatch pattern table. // If we're lucking we are at the start of the correct table. PeekAt3dmBigChunkType(&tcode, &big_value); if (tcode != typecode) { // We're not lucky. bSeekFromStart = true; } } } else tcode = 0; } } if ( bSeekFromStart ) { ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() - current file position not at start of table - searching"); rc = Seek3dmChunkFromStart( typecode ); } } if ( rc ) { rc = Begin3dmTable(ON::archive_mode::read3dm,tt); if (rc) { tcode = 0; big_value = 0; rc = BeginRead3dmBigChunk(&tcode, &big_value); if (rc && tcode != typecode) { ON_ERROR("ON_BinaryArchive::BeginRead3dmTable() - corrupt table - skipping"); rc = false; if (EndRead3dmChunk()) { // 1 November 2005 Dale Lear // This fall back is slow but it has been finding // layer and object tables in damaged files. I'm // adding it to the other BeginRead3dm...Table() // functions when it makes sense. ON__UINT64 filelength = 0; ON__UINT32 table_record_record = 0; ON_UUID class_uuid = ON_nil_uuid; ON__UINT64 min_length_data = 0; switch (typecode) { case TCODE_BITMAP_TABLE: table_record_record = TCODE_BITMAP_RECORD; class_uuid = ON_nil_uuid; // multiple types of opennurbs objects in bitmap tables min_length_data = 40; break; case TCODE_TEXTURE_MAPPING_TABLE: table_record_record = TCODE_TEXTURE_MAPPING_RECORD; class_uuid = ON_CLASS_ID(ON_TextureMapping); min_length_data = sizeof(ON_TextureMapping); break; case TCODE_MATERIAL_TABLE: table_record_record = TCODE_MATERIAL_RECORD; class_uuid = ON_CLASS_ID(ON_Material); min_length_data = 114; break; case TCODE_LINETYPE_TABLE: table_record_record = TCODE_LINETYPE_RECORD; class_uuid = ON_CLASS_ID(ON_Linetype); min_length_data = 20; break; case TCODE_LAYER_TABLE: table_record_record = TCODE_LAYER_RECORD; class_uuid = ON_CLASS_ID(ON_Layer); min_length_data = 30; break; case TCODE_GROUP_TABLE: table_record_record = TCODE_GROUP_RECORD; class_uuid = ON_CLASS_ID(ON_Group); min_length_data = 20; break; case TCODE_FONT_TABLE: table_record_record = TCODE_FONT_RECORD; class_uuid = ON_CLASS_ID(ON_TextStyle); min_length_data = 30; break; case TCODE_DIMSTYLE_TABLE: table_record_record = TCODE_DIMSTYLE_RECORD; class_uuid = (m_3dm_version >= 60) ? ON_CLASS_ID(ON_DimStyle) : ON_CLASS_ID(ON_V5x_DimStyle); min_length_data = 30; break; case TCODE_LIGHT_TABLE: table_record_record = TCODE_LIGHT_RECORD; class_uuid = ON_CLASS_ID(ON_Light); min_length_data = 30; break; case TCODE_HATCHPATTERN_TABLE: table_record_record = TCODE_HATCHPATTERN_RECORD; class_uuid = ON_CLASS_ID(ON_HatchPattern); min_length_data = 30; break; case TCODE_INSTANCE_DEFINITION_TABLE: table_record_record = TCODE_INSTANCE_DEFINITION_RECORD; class_uuid = ON_CLASS_ID(ON_InstanceDefinition); min_length_data = 30; break; case TCODE_OBJECT_TABLE: table_record_record = TCODE_OBJECT_RECORD; class_uuid = ON_nil_uuid; // multiple object types in this table min_length_data = 26; // ON_Point data is 3 doubles + 2 byte version number break; case TCODE_HISTORYRECORD_TABLE: table_record_record = TCODE_HISTORYRECORD_RECORD; class_uuid = ON_CLASS_ID(ON_HistoryRecord); min_length_data = sizeof(ON_HistoryRecord); break; default: break; } if ( 0 != table_record_record && min_length_data > 0 ) { if ( FindMisplacedTable(filelength, typecode, table_record_record, class_uuid, min_length_data) ) { tcode = 0; big_value = 0; if (PeekAt3dmBigChunkType(&tcode, &big_value)) { if (tcode == typecode) { // try one last time End3dmTable(tt,true); m_3dm_previous_table = ON_3dmArchiveTableType::settings_table; rc = Begin3dmTable(ON::archive_mode::read3dm, tt); if (rc) { tcode = 0; big_value = 0; rc = BeginRead3dmBigChunk(&tcode, &big_value); if (rc) { if (tcode != typecode) { EndRead3dmChunk(); rc = false; End3dmTable(tt, false); } } } } } } } } } if ( false == rc ) End3dmTable(tt,false); } } } return rc; } int ON_BinaryArchive::GetCurrentChunk(ON_3DM_CHUNK& chunk) const { ON_3DM_BIG_CHUNK big_chunk; memset(&chunk,0,sizeof(ON_3DM_CHUNK)); memset(&big_chunk,0,sizeof(big_chunk)); int rc = GetCurrentChunk(big_chunk); if ( rc > 0 ) { chunk.m_offset = (size_t)big_chunk.m_start_offset; chunk.m_typecode = big_chunk.m_typecode; ON__INT32 i32 = 0; if ( ON_IsLongChunkTypecode( big_chunk.m_typecode ) ) DownSizeUINT( (ON__UINT64)big_chunk.m_big_value, (ON__UINT32*)&i32 ); else DownSizeINT( big_chunk.m_big_value, &i32 ); chunk.m_value = i32; chunk.m_do_length = big_chunk.m_bLongChunk ? 1 : 0; chunk.m_do_crc16 = big_chunk.m_do_crc16 ? 1 : 0; chunk.m_do_crc32 = big_chunk.m_do_crc32 ? 1 : 0; chunk.m_crc16 = big_chunk.m_crc16; chunk.m_crc32 = big_chunk.m_crc32; } return rc; } int ON_BinaryArchive::GetCurrentChunk(ON_3DM_BIG_CHUNK& chunk) const { int rc = m_chunk.Count(); if ( rc > 0 ) { chunk = m_chunk[rc-1]; } else { memset(&chunk,0,sizeof(ON_3DM_BIG_CHUNK)); } return rc; } static const unsigned char* BufferToUINT16( bool bReverseByteOrder, const unsigned char* buffer, const unsigned char* buffer_max, ON__UINT16* u16 ) { if ( buffer >= buffer_max || buffer_max - buffer < 2 ) return 0; if ( u16 ) { unsigned char* dst = (unsigned char*)u16; if ( bReverseByteOrder ) { dst[0] = buffer[1]; dst[1] = buffer[0]; } else { dst[0] = buffer[0]; dst[1] = buffer[1]; } } return buffer+2; } static const unsigned char* BufferToUINT32( bool bReverseByteOrder, const unsigned char* buffer, const unsigned char* buffer_end, ON__UINT32* u32 ) { if ( buffer >= buffer_end || buffer_end - buffer < 4 ) return 0; if ( u32 ) { unsigned char* dst = (unsigned char*)u32; if ( bReverseByteOrder ) { dst[0] = buffer[3]; dst[1] = buffer[2]; dst[2] = buffer[1]; dst[3] = buffer[0]; } else { dst[0] = buffer[0]; dst[1] = buffer[1]; dst[2] = buffer[2]; dst[3] = buffer[3]; } } return buffer+4; } static const unsigned char* BufferToINT64( bool bReverseByteOrder, const unsigned char* buffer, const unsigned char* buffer_end, ON__INT64* i64 ) { if ( buffer >= buffer_end || buffer_end - buffer < 8 ) return 0; if ( i64 ) { unsigned char* dst = (unsigned char*)i64; if ( bReverseByteOrder ) { dst[0] = buffer[7]; dst[1] = buffer[6]; dst[2] = buffer[5]; dst[3] = buffer[4]; dst[4] = buffer[3]; dst[5] = buffer[2]; dst[6] = buffer[1]; dst[7] = buffer[0]; } else { dst[0] = buffer[0]; dst[1] = buffer[1]; dst[2] = buffer[2]; dst[3] = buffer[3]; dst[4] = buffer[4]; dst[5] = buffer[5]; dst[6] = buffer[6]; dst[7] = buffer[7]; } } return buffer+8; } static const unsigned char* BufferValidateTcode( bool bReverseByteOrder, const unsigned char* buffer, const unsigned char* buffer_end, ON__UINT32 expected_tcode ) { ON__UINT32 tc = expected_tcode ? 0 : 1; buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&tc); return ( 0 != buffer && tc == expected_tcode ) ? buffer : 0; } static const unsigned char* BufferToChunkValue( bool bReverseByteOrder, size_t sizeof_chunk_value, const unsigned char* buffer, const unsigned char* buffer_end, ON__INT64* chunk_value ) { if ( 8 == sizeof_chunk_value ) { buffer = BufferToINT64(bReverseByteOrder,buffer,buffer_end,chunk_value); } else { ON__UINT32 u32; ON__UINT64 u64; buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&u32); if ( buffer && chunk_value ) { // this u64 = u32 is here so 4 byte unsigned ints with the high // bit set are converted to positive 8 bytes ints. u64 = u32; *chunk_value = (ON__INT64)u64; } } return buffer; } static const unsigned char* BufferToUuid( bool bReverseByteOrder, const unsigned char* buffer, const unsigned char* buffer_end, ON_UUID& uuid ) { ON__UINT32 data1=0; ON__UINT16 data2=0, data3=0; if ( buffer >= buffer_end || buffer_end - buffer < 16 ) return 0; buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&data1); if (buffer) buffer = BufferToUINT16(bReverseByteOrder,buffer,buffer_end,&data2); if (buffer) buffer = BufferToUINT16(bReverseByteOrder,buffer,buffer_end,&data3); if (buffer) { if ( buffer >= buffer_end || buffer_end - buffer < 8 ) buffer = 0; else { uuid.Data1 = data1; uuid.Data2 = data2; uuid.Data3 = data3; memcpy(&uuid.Data4,buffer,8); buffer += 8; } } return buffer; } static const unsigned char* 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 ) { ON__INT64 chunk_value; ON__UINT32 c, cc; ON_UUID uuid; // make sure the value at the start of the buffer = expected_tcode. buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,expected_tcode); if ( 0 == buffer ) return 0; // get length of this chunk containing the uuid chunk_value = -1; buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&chunk_value ); if ( 0 == buffer || chunk_value < 0 ) return 0; // determine how long the chunk is supposed to be and validate the length. // // The "16+4+sizeof_chunk_value+21+4+4" in the bLookForUserTableRecordHeader // breaks down as: // 16 sizeof(uuid) // +4 + sizeof(TCODE_USER_TABLE_RECORD_HEADER chunk typecode) // +sizeof_chunk_value + sizeof(TCODE_USER_TABLE_RECORD_HEADER chunk length) // +21 + major ver, minor ver, bool, archive ver, 3dm ver // +4 + sizeof(TCODE_USER_TABLE_RECORD_HEADER chunk crc) // +4 + sizeof(TCODE_USER_TABLE_UUID chunk crc) const bool bLookForUserTableRecordHeader = (TCODE_USER_TABLE_UUID == expected_tcode && ((ON__UINT64)chunk_value) >= (16+4+sizeof_chunk_value+21+4+4) ); if ( !bLookForUserTableRecordHeader && 20 != chunk_value ) return 0; buffer = BufferToUuid(bReverseByteOrder,buffer,buffer_end,uuid); if ( 0 == buffer ) return 0; if( 0 != expected_uuid && uuid != *expected_uuid ) return 0; if ( bLookForUserTableRecordHeader ) { // make sure there is a TCODE_USER_TABLE_RECORD_HEADER chunk and skip over it. buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_USER_TABLE_RECORD_HEADER); if ( 0 == buffer ) return 0; // get length of the TCODE_USER_TABLE_RECORD_HEADER chunk ON__INT64 header_length = -1; buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&header_length ); if ( 0 == buffer ) return 0; if ( header_length < 25 ) return 0; if ( buffer >= buffer_end || buffer_end - buffer < header_length ) return 0; buffer += header_length; } buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&c); if ( 0 == buffer ) return 0; cc = ON_CRC32(0,4,&uuid.Data1); cc = ON_CRC32(cc,2,&uuid.Data2); cc = ON_CRC32(cc,2,&uuid.Data3); cc = ON_CRC32(cc,8,&uuid.Data4[0]); if ( c != cc ) return 0; return buffer; } bool ON_BinaryArchive::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 ) { bool rc = false; unsigned char buffer2048[2048]; const ON__UINT64 pos0 = CurrentPosition(); if ( filelength > 0 && pos0 >= filelength ) return false; ON__UINT32 tcode; ON__INT64 i64; const bool bReverseByteOrder = (ON::endian::big_endian == Endian()); const size_t sizeof_chunk_typecode = 4; const size_t sizeof_chunk_value = SizeofChunkLength(); const size_t sizeof_chunk_header = sizeof_chunk_typecode + sizeof_chunk_value; size_t length_of_user_uuid_and_header = 0; const bool bFindObjectTable = ( TCODE_OBJECT_TABLE == table_tcode && TCODE_OBJECT_RECORD == table_record_tcode ); const bool bFindUserTable = ( TCODE_USER_TABLE == table_tcode && TCODE_USER_RECORD == table_record_tcode ); if ( TCODE_USER_TABLE == table_tcode && !bFindUserTable ) return false; if ( TCODE_OBJECT_TABLE == table_tcode && !bFindObjectTable ) return false; if ( bFindUserTable && ON_UuidIsNil(class_uuid) ) { // must provide plug-in id when searching for user tables ON_ERROR("ON_BinaryArchive::FindMisplacedTable - must provide plug-in id when searching for user tables"); return false; } if ( !SeekFromStart(0) ) return false; ON__UINT64 pos1 = CurrentPosition(); ON__UINT64 pos; ON__UINT64 empty_table_pos = 0; // position of first empty table candidate int empty_table_status = 0; // 1 = found a candidate for an empty table // 2 = found 2 or more candidates const size_t sizeof_buffer2048 = sizeof(buffer2048); bool bAtEOF = false; while(!bAtEOF) { pos = CurrentPosition(); if ( pos < pos1 ) { break; } else if ( pos > pos1 ) { if ( !SeekBackward(pos - pos1) ) break; if ( pos1 != CurrentPosition() ) break; } memset(buffer2048,0,sizeof_buffer2048); // Depending on the table and the version of the file, less than // sizeof_buffer128 bytes may be read. Setting bit 0x04 of // m_error_message_mask disables calls to ON_Error when we // attempt to read beyond the end of file. const unsigned int saved_error_message_mask = m_error_message_mask; m_error_message_mask |= 0x04; const size_t sizeof_read = Read(sizeof_buffer2048,buffer2048); m_error_message_mask = saved_error_message_mask; if ( sizeof_read < sizeof_buffer2048 ) { // we need to parse what was read, but there's nothing after this. bAtEOF = true; } if ( sizeof_read < 2*sizeof_chunk_header || sizeof_read > sizeof_buffer2048 ) break; const unsigned char* buffer_end = (&buffer2048[0]) + sizeof_read; const unsigned char* buffer = buffer2048; pos1++; // "read" 4 byte tcode tcode = !table_tcode; buffer = BufferToUINT32(bReverseByteOrder,buffer,buffer_end,&tcode); if ( 0 == buffer ) break; if ( table_tcode != tcode ) { // This for loop looks through the buffer we just // read to reduce the amount of times we seek backwards // and re-read. for ( size_t i = 1; i <= sizeof_read - sizeof_chunk_typecode; i++ ) { tcode = !table_tcode; buffer = BufferToUINT32(bReverseByteOrder,&buffer2048[i],buffer_end,&tcode); if ( 0 == buffer || table_tcode == tcode ) { if ( bAtEOF && sizeof_read > 0 && 0 != buffer && table_tcode == tcode ) { // this table starts within sizeof_buffer2048 bytes of the eof. bAtEOF = false; } break; } pos1++; } continue; // start again with archive positioned at the tcode we want } // "read" 4 or 8 byte chunk value i64 = -1; buffer = BufferToChunkValue(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64); if ( 0 == buffer || i64 <= 0 ) continue; const ON__UINT64 length_of_table = (ON__UINT64)i64; if ( length_of_table < 2*sizeof_chunk_header + 4 + min_length_data ) { if ( sizeof_chunk_header == length_of_table && 2 != empty_table_status ) { // see if we are at a TCODE_ENDOFTABLE chunk buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_ENDOFTABLE); if ( 0 != buffer ) { i64 = -1; buffer = BufferToChunkValue(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64); if ( 0 == i64 ) { if ( 0 == empty_table_status ) { empty_table_pos = pos1-1; empty_table_status = 1; } else { // found 2 or more candidates for the end of table chunk empty_table_status = 2; } } } } continue; } if ( bFindUserTable ) { // We found TCODE_USER_TABLE + chunk length. If it is a user table, // there should be a TCODE_USER_TABLE_UUID chunk with a crc. const unsigned char* buffer0 = buffer; buffer = EmergencyFindTable_UuidHelper(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,TCODE_USER_TABLE_UUID,&class_uuid); if ( 0 == buffer || buffer <= buffer0 ) continue; length_of_user_uuid_and_header = buffer - buffer0; // At this point we should be postioned at the table_record_tcode = TCODE_USER_RECORD chunk } // see if the start of the buffer contains the 4 byte typecode value = table_record_tcode. buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,table_record_tcode); if ( 0 == buffer ) continue; i64 = -1; buffer = BufferToChunkValue( bReverseByteOrder, sizeof_chunk_value,buffer,buffer_end,&i64); if ( 0 == buffer || i64 <= 0 ) continue; const ON__UINT64 length_of_record = (ON__UINT64)i64; if ( bFindUserTable ) { ON__UINT64 expected_length_of_table = length_of_user_uuid_and_header + sizeof_chunk_header + length_of_record; if ( expected_length_of_table != length_of_table ) continue; } else { if ( length_of_record < 4*sizeof_chunk_header + 20 + min_length_data ) continue; if ( length_of_record + 2*sizeof_chunk_header > length_of_table) continue; if (bFindObjectTable) { buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_OBJECT_RECORD_TYPE); if ( 0 == buffer ) continue; // The TCODE_OBJECT_RECORD_TYPE is a shot chunk whose value is a bitfield // used to filter reading of objects. Checking the value will not help // validate the record, but we need to skip over it. buffer = BufferToChunkValue(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,0); if ( 0 == buffer ) continue; } buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_OPENNURBS_CLASS); if ( 0 == buffer ) continue; i64 = -1; buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64); if ( 0 == buffer || i64 <= 0 ) continue; const ON__UINT64 length_of_on_class = (ON__UINT64)i64; if ( length_of_on_class < 3*sizeof_chunk_header + 20 + min_length_data ) continue; if ( length_of_on_class + sizeof_chunk_header + 4 > length_of_record) continue; const unsigned char* buffer0 = buffer; buffer = EmergencyFindTable_UuidHelper(bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,TCODE_OPENNURBS_CLASS_UUID,(ON_UuidIsNil(class_uuid) ? nullptr : &class_uuid)); if ( 0 == buffer || buffer <= buffer0) continue; const size_t length_of_uuid_chunk = buffer-buffer0; buffer = BufferValidateTcode(bReverseByteOrder,buffer,buffer_end,TCODE_OPENNURBS_CLASS_DATA); if ( 0 == buffer ) continue; i64 = -1; buffer = BufferToChunkValue( bReverseByteOrder,sizeof_chunk_value,buffer,buffer_end,&i64); if ( 0 == buffer || i64 < 0 ) continue; const ON__UINT64 length_of_data = (ON__UINT64)i64; if ( length_of_data < min_length_data ) continue; if ( length_of_data + length_of_uuid_chunk + 2*sizeof_chunk_header > length_of_on_class) continue; } // position archive at point where the table tcode was read if ( !SeekBackward(sizeof_read) ) break; pos = CurrentPosition(); if ( pos+1 == pos1) rc = true; break; } if ( !rc ) { // we didn't fing a table containing anything if ( 1 == empty_table_status ) { // we found one candidate for an empty table. // This is reasonable for materials, bitmaps, and the like. rc = SeekFromStart(empty_table_pos); } else { // nothing in this file. SeekFromStart(pos0); } } if (rc) { ON_3dmArchiveTableType tt = TableTypeFromTypecode(table_tcode); if (ON_3dmArchiveTableType::Unset != tt) { // permit a second try if (static_cast(Previous3dmTable()) > static_cast(ON_3dmArchiveTableType::properties_table) ) m_3dm_previous_table = ON_3dmArchiveTableType::properties_table; } } return rc; } bool ON_BinaryArchive::FindTableInDamagedArchive( const unsigned int tcode_table, const unsigned int tcode_record, const ON_UUID class_uuid, const int min_length_data ) { bool rc = FindMisplacedTable( 0, tcode_table, tcode_record, class_uuid, min_length_data ); return rc; } bool ON_BinaryArchive::EndRead3dmTable( unsigned int typecode ) { bool rc = false; if (0 == typecode) { ON_ERROR("ON_BinaryArchive::EndRead3dmTable() bad typecode"); return false; } const ON_3dmArchiveTableType tt = TableTypeFromTypecode(typecode); if (tt == ON_3dmArchiveTableType::Unset) { ON_ERROR("ON_BinaryArchive::EndRead3dmTable() bad typecode"); return false; } if ( Active3dmTable() != tt ) { ON_ERROR("ON_BinaryArchive::EndRead3dmTable() m_active_table != t"); return false; } if (false == ArchiveContains3dmTable(tt)) { // This archive was written by code that was compiled before the table was added. return End3dmTable(tt,true); } if ( m_3dm_version == 1 ) { if ( m_chunk.Count() != 0 ) { ON_ERROR("ON_BinaryArchive::EndRead3dmTable() v1 file m_chunk.Count() != 0"); return false; } // rewind for next table rc = SeekFromStart(32)?true:false; } else { { if ( m_chunk.Count() != 1 ) { ON_ERROR("ON_BinaryArchive::EndRead3dmTable() v2 file m_chunk.Count() != 1"); return false; } const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( 0 == c || c->m_typecode != typecode ) { ON_ERROR("ON_BinaryArchive::EndRead3dmTable() m_chunk.Last()->typecode != typecode"); return false; } rc = EndRead3dmChunk(); } } return End3dmTable(m_3dm_active_table,rc); } ON_ModelComponent::Type ON_BinaryArchive::TableComponentType( ON_3dmArchiveTableType table_type ) { ON_ModelComponent::Type model_component_type; switch (table_type) { case ON_3dmArchiveTableType::Unset: model_component_type = ON_ModelComponent::Type::Unset; break; case ON_3dmArchiveTableType::start_section: model_component_type = ON_ModelComponent::Type::Unset; break; case ON_3dmArchiveTableType::properties_table: model_component_type = ON_ModelComponent::Type::Unset; break; case ON_3dmArchiveTableType::settings_table: model_component_type = ON_ModelComponent::Type::Unset; break; case ON_3dmArchiveTableType::bitmap_table: model_component_type = ON_ModelComponent::Type::Image; break; case ON_3dmArchiveTableType::texture_mapping_table: model_component_type = ON_ModelComponent::Type::TextureMapping; break; case ON_3dmArchiveTableType::material_table: model_component_type = ON_ModelComponent::Type::RenderMaterial; break; case ON_3dmArchiveTableType::linetype_table: model_component_type = ON_ModelComponent::Type::LinePattern; break; case ON_3dmArchiveTableType::layer_table: model_component_type = ON_ModelComponent::Type::Layer; break; case ON_3dmArchiveTableType::group_table: model_component_type = ON_ModelComponent::Type::Group; break; case ON_3dmArchiveTableType::text_style_table: model_component_type = ON_ModelComponent::Type::TextStyle; break; case ON_3dmArchiveTableType::leader_style_table: model_component_type = ON_ModelComponent::Type::Unset; break; case ON_3dmArchiveTableType::dimension_style_table: model_component_type = ON_ModelComponent::Type::DimStyle; break; case ON_3dmArchiveTableType::light_table: model_component_type = ON_ModelComponent::Type::RenderLight; break; case ON_3dmArchiveTableType::hatchpattern_table: model_component_type = ON_ModelComponent::Type::HatchPattern; break; case ON_3dmArchiveTableType::instance_definition_table: model_component_type = ON_ModelComponent::Type::InstanceDefinition; break; case ON_3dmArchiveTableType::object_table: model_component_type = ON_ModelComponent::Type::ModelGeometry; break; case ON_3dmArchiveTableType::historyrecord_table: model_component_type = ON_ModelComponent::Type::HistoryRecord; break; case ON_3dmArchiveTableType::user_table: model_component_type = ON_ModelComponent::Type::Unset; break; case ON_3dmArchiveTableType::end_mark: model_component_type = ON_ModelComponent::Type::Unset; break; default: model_component_type = ON_ModelComponent::Type::Unset; break; } return model_component_type; } ON_3dmArchiveTableType ON_BinaryArchive::Active3dmTable() const { return m_3dm_active_table; } ON_3dmArchiveTableType ON_BinaryArchive::Previous3dmTable() const { return m_3dm_previous_table; } ON_3dmArchiveTableType ON_BinaryArchive::FirstFailed3dmTable() const { return m_3dm_first_failed_table; } bool ON_BinaryArchive::BeginWrite3dmBitmapTable() { return BeginWrite3dmTable( TCODE_BITMAP_TABLE ); } bool ON_BinaryArchive::EndWrite3dmBitmapTable() { return EndWrite3dmTable( TCODE_BITMAP_TABLE ); } bool ON_BinaryArchive::Write3dmImageComponent( const ON_ModelComponentReference& model_component_reference ) { return Write3dmImageComponent(model_component_reference.ModelComponent()); } bool ON_BinaryArchive::Write3dmImageComponent( const ON_ModelComponent* model_component ) { bool rc = false; for (;;) { const ON_Bitmap* image = ON_Bitmap::Cast(model_component); if (nullptr == image) { ON_ERROR("model_component parameter is not an image component."); break; } rc = Write3dmBitmap(*image); break; } return rc; } bool ON_BinaryArchive::Write3dmBitmap( const ON_Bitmap& bitmap ) { if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::bitmap_table)) return true; if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::bitmap_table) ) return false; bool rc = false; if ( m_3dm_version > 1 ) { Internal_Increment3dmTableItemCount(); const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c && c->m_typecode == TCODE_BITMAP_TABLE ) { rc = BeginWrite3dmChunk( TCODE_BITMAP_RECORD, 0 ); if ( rc ) { Internal_Write3dmUpdateManifest(bitmap); rc = WriteObject( bitmap ); if ( !EndWrite3dmChunk() ) rc = false; } } else { Internal_ReportCriticalError(); ON_ERROR("ON_BinaryArchive::Write3dmBitmap() must be called in BeginWrite3dmBitmapTable() block"); rc = false; } } return rc; } bool ON_BinaryArchive::BeginRead3dmBitmapTable() { return BeginRead3dmTable( TCODE_BITMAP_TABLE ); } bool ON_BinaryArchive::EndRead3dmBitmapTable() { return EndRead3dmTable( TCODE_BITMAP_TABLE ); } int ON_BinaryArchive::Read3dmBitmap( // returns 0 at end of bitmap table // 1 bitmap successfully read ON_Bitmap** ppBitmap // bitmap returned here ) { if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::bitmap_table, (void**)ppBitmap)) return 0; ON_Bitmap* bitmap = 0; int rc = 0; if ( m_3dm_version != 1 ) { ON__UINT32 tcode = 0; ON__INT64 big_value = 0; if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) { if ( tcode == TCODE_BITMAP_RECORD ) { Internal_Increment3dmTableItemCount(); ON_Object* p = 0; if ( ReadObject( &p ) ) { bitmap = ON_Bitmap::Cast(p); if (nullptr == bitmap) { ON_ERROR("ON_BinaryArchive::Read3dmBitmap() - corrupt bitmap table"); delete p; } else { Internal_Read3dmUpdateManifest(*bitmap); if ( ppBitmap ) *ppBitmap = bitmap; rc = 1; } } } else if ( tcode != TCODE_ENDOFTABLE ) { ON_ERROR("ON_BinaryArchive::Read3dmBitmap() - corrupt bitmap table"); Internal_ReportCriticalError(); } EndRead3dmChunk(); } } return rc; } bool ON_BinaryArchive::BeginWrite3dmLayerTable() { return BeginWrite3dmTable( TCODE_LAYER_TABLE ); } bool ON_BinaryArchive::Write3dmLayerComponent( const ON_ModelComponentReference& model_component_reference ) { return Write3dmLayerComponent(model_component_reference.ModelComponent()); } bool ON_BinaryArchive::Write3dmLayerComponent( const ON_ModelComponent* model_component ) { bool rc = false; for (;;) { const ON_Layer* layer = ON_Layer::Cast(model_component); if (nullptr == layer) { ON_ERROR("model_component parameter is not a layer component."); break; } rc = Write3dmLayer(*layer); break; } return rc; } bool ON_BinaryArchive::Write3dmLayer( const ON_Layer& layer ) { if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::layer_table)) return true; if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::layer_table) ) return false; bool rc = false; // version 2+ const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c && c->m_typecode == TCODE_LAYER_TABLE ) { Internal_Increment3dmTableItemCount(); rc = BeginWrite3dmChunk( TCODE_LAYER_RECORD, 0 ); if ( rc ) { Internal_Write3dmUpdateManifest(layer); rc = WriteObject( layer ); if ( !EndWrite3dmChunk() ) rc = false; } } else { Internal_ReportCriticalError(); ON_ERROR("ON_BinaryArchive::Write3dmLayer() must be called in BeginWrite3dmLayerTable(2) block"); rc = false; } return rc; } bool ON_BinaryArchive::EndWrite3dmLayerTable() { return EndWrite3dmTable( TCODE_LAYER_TABLE ); } bool ON_BinaryArchive::BeginRead3dmLayerTable() { m_3dm_v1_layer_index = 0; bool rc = BeginRead3dmTable( TCODE_LAYER_TABLE ); if ( rc && m_3dm_version == 1 ) { rc = Seek3dmChunkFromStart( TCODE_LAYER ); rc = true; // there are 1.0 files written by the old IO toolkit that have no layers } return rc; } int ON_BinaryArchive::Read3dmV1LayerIndex(const char* sV1LayerName) const { // returns V1 layer index int layer_index = -1; if ( ON::archive_mode::read3dm == m_mode && 0 == m_3dm_opennurbs_version && 1 == m_3dm_version && 0 != m_V1_layer_list && 0 != sV1LayerName && 0 != sV1LayerName[0] ) { struct ON__3dmV1LayerIndex* p = m_V1_layer_list; int i; for ( i = 0; 0 != p && i < 1000; i++ ) { if ( p->m_layer_index < 0 ) break; if ( p->m_layer_name_length < 1 || p->m_layer_name_length>256) break; if ( 0 == p->m_layer_name ) break; if ( 0 == p->m_layer_name[0] ) break; if ( 0 != p->m_layer_name[p->m_layer_name_length] ) break; if ( !on_stricmp(p->m_layer_name,sV1LayerName) ) { layer_index = p->m_layer_index; break; } p = p->m_next; } } return layer_index; } bool ON_BinaryArchive::Read3dmV1Layer( ON_Layer*& layer ) { ON_String s; bool rc = 0; ON__UINT32 tcode; ON__INT64 big_value; for (;;) { tcode = 0; big_value = 0; if (!BeginRead3dmBigChunk(&tcode,&big_value)) break; // assume we are at the end of the file if ( tcode == TCODE_LAYER ) { layer = new ON_Layer(); layer->SetIndex(m_3dm_v1_layer_index++); rc = 1; break; } if (!EndRead3dmChunk()) break; } if ( layer ) { rc = false; for (;;) { tcode = 0; big_value = 0; if (!BeginRead3dmBigChunk(&tcode,&big_value)) break; switch(tcode) { case TCODE_LAYERNAME: { int slen = 0; ReadInt(&slen); if ( slen < 0 || slen > 10000 ) { ON_ERROR("ON_BinaryArchive::Read3dmV1Layer() - invalid layer name length"); } else { s.SetLength(slen); if ( ReadByte( s.Length(), s.Array() ) ) { layer->SetName(ON_wString(s)); } } } break; case TCODE_RGB: { ON__UINT64 rgb64 = (ON__UINT64)big_value; ON__UINT32 rgb32 = (ON__UINT32)rgb64; layer->SetColor( ON_Color((ON__UINT32)rgb32) ); } break; case TCODE_LAYERSTATE: switch (big_value) { case 1: // hidden layer->SetVisible(false); layer->SetLocked(false); break; case 2: // locked layer->SetVisible(true); layer->SetLocked(true); break; default: // normal layer->SetVisible(true); layer->SetLocked(false); break; } break; } if (!EndRead3dmChunk()) break; if ( TCODE_ENDOFTABLE == tcode ) { rc = true; break; } } if ( !EndRead3dmChunk() ) // end of TCODE_LAYER chunk rc = false; } if ( !rc && layer ) { delete layer; layer = 0; } else if (rc && layer) { // V1 files did not have layer ids. layer->SetId(); if ( ON::archive_mode::read3dm == m_mode && 0 == m_3dm_opennurbs_version && 1 == m_3dm_version ) { // save layer index and name in a linked list. int s_length = s.Length(); const char* s_name = s.Array(); if ( layer->Index() >= 0 && s_length > 0 && s_length < 256 && 0 != s_name && 0 != s_name[0] ) { struct ON__3dmV1LayerIndex* p = (struct ON__3dmV1LayerIndex*)oncalloc(1, sizeof(*p) + (s_length+1)*sizeof(*p->m_layer_name) ); p->m_layer_name = (char*)(p+1); p->m_layer_index = layer->Index(); p->m_layer_name_length = s_length; memcpy(p->m_layer_name,s_name,s_length*sizeof(*p->m_layer_name)); p->m_layer_name[s_length] = 0; p->m_next = m_V1_layer_list; m_V1_layer_list = p; } } } return rc; } int ON_BinaryArchive::Read3dmLayer( ON_Layer** ppLayer ) { if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::layer_table, (void**)ppLayer)) return 0; ON__UINT32 tcode; ON__INT64 big_value; ON_Layer* layer = nullptr; // returns 0 at end of layer table if ( m_3dm_version == 1 ) { if (Read3dmV1Layer(layer) && nullptr != layer) Internal_Read3dmUpdateManifest(*layer); } else { // version 2+ tcode = 0; big_value = 0; if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) { if ( tcode == TCODE_LAYER_RECORD ) { Internal_Increment3dmTableItemCount(); ON_Object* p = 0; if ( ReadObject( &p ) ) { layer = ON_Layer::Cast(p); if ( nullptr == layer ) delete p; else { Internal_Read3dmUpdateManifest(*layer); } } if (nullptr == layer) { ON_ERROR("ON_BinaryArchive::Read3dmLayer() - corrupt layer table"); } } else if ( tcode != TCODE_ENDOFTABLE ) { ON_ERROR("ON_BinaryArchive::Read3dmLayer() - corrupt layer table"); } EndRead3dmChunk(); } } if ( layer ) layer->HasPerViewportSettings(ON_nil_uuid); // this call sets ON_Layer::m__runtime_flags *ppLayer = layer; return (layer) ? 1 : 0; } bool ON_BinaryArchive::EndRead3dmLayerTable() { return EndRead3dmTable( TCODE_LAYER_TABLE ); } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// bool ON_BinaryArchive::BeginWrite3dmGroupTable() { return BeginWrite3dmTable( TCODE_GROUP_TABLE ); } bool ON_BinaryArchive::Write3dmGroupComponent( const ON_ModelComponentReference& model_component_reference ) { return Write3dmGroupComponent(model_component_reference.ModelComponent()); } bool ON_BinaryArchive::Write3dmGroupComponent( const ON_ModelComponent* model_component ) { bool rc = false; for (;;) { const ON_Group* group = ON_Group::Cast(model_component); if (nullptr == group) { ON_ERROR("model_component parameter is not a group component."); break; } rc = Write3dmGroup(*group); break; } return rc; } bool ON_BinaryArchive::Write3dmGroup( const ON_Group& group ) { if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::group_table)) return true; if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::group_table) ) return false; bool rc = false; const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c && c->m_typecode == TCODE_GROUP_TABLE ) { Internal_Increment3dmTableItemCount(); rc = BeginWrite3dmChunk( TCODE_GROUP_RECORD, 0 ); if ( rc ) { Internal_Write3dmUpdateManifest(group); rc = WriteObject( group ); if ( !EndWrite3dmChunk() ) rc = false; } } else { ON_ERROR("ON_BinaryArchive::Write3dmGroup() must be called in BeginWrite3dmGroupTable() block"); rc = false; } return rc; } bool ON_BinaryArchive::EndWrite3dmGroupTable() { return EndWrite3dmTable( TCODE_GROUP_TABLE ); } bool ON_BinaryArchive::BeginRead3dmGroupTable() { return BeginRead3dmTable( TCODE_GROUP_TABLE ); } int ON_BinaryArchive::Read3dmGroup( ON_Group** ppGroup ) { if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::group_table, (void**)ppGroup)) return 0; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; ON_Group* group = nullptr; if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) { if ( tcode == TCODE_GROUP_RECORD ) { Internal_Increment3dmTableItemCount(); ON_Object* p = 0; if ( ReadObject( &p ) ) { group = ON_Group::Cast(p); if (!group) delete p; else Internal_Read3dmUpdateManifest(*group); } if (!group) { ON_ERROR("ON_BinaryArchive::Read3dmGroup() - corrupt group table"); } } else if ( tcode != TCODE_ENDOFTABLE ) { ON_ERROR("ON_BinaryArchive::Read3dmGroup() - corrupt group table"); } EndRead3dmChunk(); } *ppGroup = group; return (group) ? 1 : 0; } bool ON_BinaryArchive::EndRead3dmGroupTable() { return EndRead3dmTable( TCODE_GROUP_TABLE ); } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// const ON_TextStyle* ON_BinaryArchive::ArchiveTextStyleFromArchiveTextStyleIndex( int archive_text_style_index ) const { if (archive_text_style_index >= 0 && archive_text_style_index < m_archive_text_style_table.Count()) return m_archive_text_style_table[archive_text_style_index]; return nullptr; } ////bool ON_BinaryArchive::WriteArchiveTextStyleIndex( //// int dim_style_model_index ////) ////{ //// // In version 6, August 2016, text style information was moved to ON_DimStyle. //// // //// // When version 5 and earlier files are written, a text style is saved for each //// // dimstyle and in the same order. This means the V5 text_style index = V5 dim_style index //// // in the v5 archive. //// // //// // When version 6 files are saved a single text style is saved so versions of Rhino WIP //// // delivered before August 2016 have a text style. //// int v6_text_style_index = 0; //// bool rc = //// ( Archive3dmVersion() < 60 ) //// ? Write3dmReferencedComponentIndex(ON_ModelComponent::Type::DimStyle, dim_style_model_index) //// : WriteInt(v6_text_style_index); //// return rc; ////} ////bool ON_BinaryArchive::BeginWrite3dmTextStyleTable() ////{ //// // Does nothing. Text style information is on ON_DimStyle. //// ON_ERROR("do not call ON_BinaryArchive::BeginWrite3dmTextStyleTable."); //// return true; ////} //// ////bool ON_BinaryArchive::Write3dmTextStyle( //// const class ON_TextStyle& text_style ////) ////{ //// // Does nothing. Text style information is on ON_DimStyle. //// ON_ERROR("do not call ON_BinaryArchive::Write3dmTextStyle."); //// return true; ////} //// ////bool ON_BinaryArchive::EndWrite3dmTextStyleTable() ////{ //// // Does nothing. Text style information is on ON_DimStyle. //// ON_ERROR("do not call ON_BinaryArchive::EndWrite3dmTextStyleTable."); //// return true; ////} ////bool ON_BinaryArchive::Internal_BeginWrite3dmTextStyleTable() ////{ //// return BeginWrite3dmTable( TCODE_FONT_TABLE ); ////} //bool ON_BinaryArchive::Write3dmTextStyleComponent( // const ON_ModelComponentReference& model_component_reference // ) //{ // return Write3dmTextStyleComponent(model_component_reference.ModelComponent()); //} // //bool ON_BinaryArchive::Write3dmTextStyleComponent( // const ON_ModelComponent* model_component // ) //{ // bool rc = false; // for (;;) // { // const ON_TextStyle* text_style = ON_TextStyle::Cast(model_component); // if (nullptr == text_style) // { // ON_ERROR("model_component parameter is not a text style component."); // break; // } // rc = Write3dmTextStyle(*text_style); // break; // } // return rc; //} bool ON_BinaryArchive::Internal_Write3dmTextStyle( const class ON_TextStyle& text_style ) { if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::text_style_table)) return true; if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::text_style_table) ) return false; Internal_Increment3dmTableItemCount(); bool rc = false; const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c && c->m_typecode == TCODE_FONT_TABLE ) { rc = BeginWrite3dmChunk( TCODE_FONT_RECORD, 0 ); if ( rc ) { Internal_Write3dmUpdateManifest(text_style); rc = WriteObject( text_style ); if ( !EndWrite3dmChunk() ) rc = false; } } else { ON_ERROR("ON_BinaryArchive::Write3dmTextStyle() must be called in BeginWrite3dmTextStyleTable() block"); rc = false; } return rc; } ////bool ON_BinaryArchive::Internal_EndWrite3dmTextStyleTable() ////{ //// return EndWrite3dmTable( TCODE_FONT_TABLE ); ////} //// ////bool ON_BinaryArchive::BeginRead3dmTextStyleTable() ////{ //// ON_ERROR("Do not call BeginRead3dmTextStyleTable()"); //// return true; ////} //// //// ////int ON_BinaryArchive::Read3dmTextStyle(ON_TextStyle** ppTextStyle) ////{ //// ON_ERROR("Do not call Read3dmTextStyle()"); //// return 0; ////} //// ////bool ON_BinaryArchive::EndRead3dmTextStyleTable() ////{ //// ON_ERROR("Do not call EndRead3dmTextStyleTable()"); //// return true; ////} int ON_BinaryArchive::Internal_Read3dmTextStyle( ON_TextStyle** ppTextStyle ) { if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::text_style_table, (void**)ppTextStyle)) return 0; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; ON_TextStyle* text_style = nullptr; if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) { if ( tcode == TCODE_FONT_RECORD ) { Internal_Increment3dmTableItemCount(); ON_Object* p = 0; if ( ReadObject( &p ) ) { text_style = ON_TextStyle::Cast(p); if ( !text_style ) delete p; else { Internal_Read3dmUpdateManifest(*text_style); } } if (!text_style) { ON_ERROR("ON_BinaryArchive::Read3dmTextStyle() - corrupt text_style table"); } } else if ( tcode != TCODE_ENDOFTABLE ) { ON_ERROR("ON_BinaryArchive::Read3dmTextStyle() - corrupt text_style table"); } EndRead3dmChunk(); } *ppTextStyle = text_style; return (text_style) ? 1 : 0; } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// bool ON_BinaryArchive::Internal_Write3dmDimStyle( const ON_DimStyle& dimstyle, bool bUpdateManifest ) { if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::dimension_style_table)) return true; if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::dimension_style_table) ) return false; Internal_Increment3dmTableItemCount(); bool rc = false; const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c && c->m_typecode == TCODE_DIMSTYLE_TABLE ) { rc = BeginWrite3dmChunk( TCODE_DIMSTYLE_RECORD, 0 ); if ( rc ) { if ( bUpdateManifest ) Internal_Write3dmUpdateManifest(dimstyle); if (this->Archive3dmVersion() < 60) { const ON_V5x_DimStyle V5_dimstyle( this->Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(), dimstyle ); rc = WriteObject(V5_dimstyle); } else { rc = WriteObject(dimstyle); } if ( !EndWrite3dmChunk() ) rc = false; } } else { ON_ERROR("ON_BinaryArchive::Write3dmDimStyle() must be called in BeginWrite3dmDimStyleTable() block"); rc = false; } return rc; } bool ON_BinaryArchive::BeginWrite3dmDimStyleTable() { // August 2016. text style information is now on ON_DimStyle // A text style table must still be saved in archives so earlier versions of Rhino // can read the archives written by new versions. // if (0 != m_archive_dim_style_table_status) { ON_ERROR("BeginWrite3dmDimStyleTable() called at the wrong time."); return false; } const unsigned int previous_table_index = static_cast(Previous3dmTable()); const unsigned int text_style_table_index = static_cast(ON_3dmArchiveTableType::text_style_table); if (previous_table_index >= text_style_table_index) { ON_ERROR("archive contains text style information. This is incorrect."); return false; } m_archive_dim_style_table_status = 1; return true; } bool ON_BinaryArchive::Write3dmDimStyleComponent( const ON_ModelComponentReference& model_component_reference ) { return Write3dmDimStyleComponent(model_component_reference.ModelComponent()); } bool ON_BinaryArchive::Write3dmDimStyleComponent( const ON_ModelComponent* model_component ) { bool rc = false; for (;;) { const ON_DimStyle* dimension_style = ON_DimStyle::Cast(model_component); if (nullptr == dimension_style) { ON_ERROR("model_component parameter is not a text style component."); break; } rc = Write3dmDimStyle(*dimension_style); break; } return rc; } bool ON_BinaryArchive::Write3dmDimStyle( const ON_DimStyle& dimstyle ) { if ( dimstyle.ParentIdIsNotNil() || dimstyle.HasOverrides()) { ON_ERROR("Override dimstyles are not stored in the dimstyle table in V6 files."); return true; // don't abort writing the file. } if (1 == m_archive_dim_style_table_status) { if (0 != m_archive_dim_style_table.UnsignedCount()) { ON_ERROR("Write3dmDimStyle() called at the incorrect time"); return false; } m_archive_dim_style_table_status = 2; } else if (2 == m_archive_dim_style_table_status) { if (m_archive_dim_style_table.UnsignedCount() <= 0 ) { ON_ERROR("Write3dmDimStyle() called at the incorrect time"); return false; } } ON_DimStyle* copy_of_model_dimstyle = new ON_DimStyle(dimstyle); if (nullptr != copy_of_model_dimstyle) { Internal_Write3dmUpdateManifest(*copy_of_model_dimstyle); m_archive_dim_style_table.Append(copy_of_model_dimstyle); if (nullptr == m_archive_current_dim_style) { if (nullptr != m_archive_3dm_settings) { if (dimstyle.IdIsNotNil() && dimstyle.Id() == m_archive_3dm_settings->CurrentDimensionStyleId()) m_archive_current_dim_style = copy_of_model_dimstyle; } } } return true; } bool ON_BinaryArchive::EndWrite3dmDimStyleTable() { if (m_archive_dim_style_table_status < 1 || m_archive_dim_style_table_status > 2) { ON_ERROR("EndWrite3dmDimStyleTable() called at the incorrect time"); return false; } // August 2016. text style information is now on ON_DimStyle // A text style table must still be saved in archives so earlier versions of Rhino // can read the archives written by new versions. // For V6 files this is empty as of December 15 2016. We've given the WIP system // 4 months to transition from a pre Aut 2016 Rhino V6 WIP to a post Aug 2016 Rhino V6 WIP. bool rc = true; { // Write one text style for every dimension style rc = BeginWrite3dmTable(TCODE_FONT_TABLE); if (false == rc) return false; // Archive3dmVersion() < 60 added December 15, 2016. if (Archive3dmVersion() < 60) { for (int j = 0; j < m_archive_dim_style_table.Count() && rc; j++) { const ON_DimStyle* dim_style = m_archive_dim_style_table[j]; if (nullptr == dim_style) continue; ON_TextStyle archive_text_style; archive_text_style.SetFont(dim_style->FontCharacteristics()); archive_text_style.SetId(); // we need a new and unique id. archive_text_style.SetIndex(dim_style->Index()); archive_text_style.SetName(dim_style->Name()); rc = Internal_Write3dmTextStyle(archive_text_style); } } if (!EndWrite3dmTable(TCODE_FONT_TABLE)) rc = false; if (false == rc) return false; } rc = BeginWrite3dmTable(TCODE_DIMSTYLE_TABLE); if (false == rc) return false; for (int j = 0; j < m_archive_dim_style_table.Count() && rc; j++) { const ON_DimStyle* dim_style = m_archive_dim_style_table[j]; if (nullptr == dim_style) continue; rc = Internal_Write3dmDimStyle(*dim_style, false); } if (false == EndWrite3dmTable(TCODE_DIMSTYLE_TABLE)) rc = false; m_annotation_context.SetReferencedDimStyle(&ArchiveCurrentDimStyle(),nullptr,ArchiveCurrentDimStyleIndex()); return rc; } const ON_DimStyle* ON_BinaryArchive::Internal_ArchiveCurrentDimStyle() { ON::LengthUnitSystem model_unit_system = ON::LengthUnitSystem::Unset; if (nullptr != m_archive_3dm_settings) { model_unit_system = m_archive_3dm_settings->m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(); const ON_UUID id = m_archive_3dm_settings->CurrentDimensionStyleId(); if (ON_nil_uuid != id) { for (unsigned int i = 0; i < m_archive_dim_style_table.UnsignedCount(); i++) { const ON_DimStyle* archive_dim_style = m_archive_dim_style_table[i]; if (archive_dim_style->ParentIdIsNotNil()) continue; if ( id == archive_dim_style->Id()) { return archive_dim_style; break; } } } const int index = m_archive_3dm_settings->CurrentDimensionStyleIndex(); if (index >= 0 && index < m_archive_dim_style_table.Count() && nullptr != m_archive_dim_style_table[index] && m_archive_dim_style_table[index]->ParentIdIsNotNil() ) return m_archive_dim_style_table[index]; if (ON::IsTerrestrialLengthUnit(model_unit_system)) { double unit_scale = 1e300; const ON_DimStyle* inch_mm_ds = nullptr; const ON_DimStyle* unit_scale_ds = nullptr; for (unsigned int i = 0; i < m_archive_dim_style_table.UnsignedCount(); i++) { const ON_DimStyle* archive_dim_style = m_archive_dim_style_table[i]; if (archive_dim_style->ParentIdIsNotNil()) continue; if (false == archive_dim_style->UnitSystemIsSet()) continue; const ON::LengthUnitSystem ds_unit_system = archive_dim_style->UnitSystem(); if (ds_unit_system == model_unit_system) return archive_dim_style; if (nullptr == m_archive_current_dim_style) { if (ON::IsUnitedStatesCustomaryLengthUnit(model_unit_system) && ON::LengthUnitSystem::Inches == ds_unit_system) { inch_mm_ds = archive_dim_style; } else if (ON::IsMetricLengthUnit(model_unit_system) && ON::LengthUnitSystem::Millimeters == ds_unit_system) { inch_mm_ds = archive_dim_style; } } double s = ON::UnitScale(model_unit_system, ds_unit_system); if (s < 1.0) s = ON::UnitScale(ds_unit_system, model_unit_system); if (s > 1.0 && s < unit_scale) { unit_scale_ds = archive_dim_style; unit_scale = s; } } if (nullptr != inch_mm_ds) return inch_mm_ds; if (nullptr != unit_scale_ds) return unit_scale_ds; } } for (unsigned int i = 0; i < m_archive_dim_style_table.UnsignedCount(); i++) { const ON_DimStyle* archive_dim_style = m_archive_dim_style_table[i]; if (archive_dim_style->ParentIdIsNotNil()) continue; if (false == archive_dim_style->UnitSystemIsSet()) continue; return archive_dim_style; } const ON_DimStyle* system_ds; if (ON::IsUnitedStatesCustomaryLengthUnit(model_unit_system)) system_ds = &ON_DimStyle::DefaultInchDecimal; else if (ON::IsMetricLengthUnit(model_unit_system)) system_ds = &ON_DimStyle::DefaultMillimeterSmall; else system_ds = &ON_DimStyle::Default; // invent a current dimstyle. Happens when reading files written by Rhino 1.1. ON_DimStyle* current_ds = new ON_DimStyle(*system_ds); current_ds->SetId(); current_ds->SetIndex(m_archive_dim_style_table.Count()); if ( ON::archive_mode::read3dm == Mode()) Internal_Read3dmUpdateManifest(*current_ds); else m_manifest.AddComponentToManifest(*current_ds, true, nullptr); m_archive_dim_style_table.Append(current_ds); return current_ds; } double ON_BinaryArchive::Internal_ArchiveModelSpaceTextScale() const { const double default_model_space_text_scale = 1.0; const double model_space_text_scale = (nullptr != m_archive_3dm_settings && m_archive_3dm_settings->m_AnnotationSettings.Is_V5_AnnotationScalingEnabled()) ? m_archive_3dm_settings->m_AnnotationSettings.WorldViewTextScale() : default_model_space_text_scale; return (model_space_text_scale > 0.0 && ON_IsValid(model_space_text_scale)) ? model_space_text_scale : default_model_space_text_scale; } void ON_BinaryArchive::Internal_ConvertTextStylesToDimStyles() { m_text_style_to_dim_style_archive_index_map.SetCount(0); const int archive_text_style_count = m_archive_text_style_table.Count(); if (archive_text_style_count <= 0) return; m_text_style_to_dim_style_archive_index_map.Reserve(archive_text_style_count); const int current_ds_index = (nullptr == m_archive_current_dim_style) ? -1 : m_archive_current_dim_style->Index(); for (int i = 0; i < m_archive_text_style_table.Count(); i++) { ON_2dex& ts_to_ds = m_text_style_to_dim_style_archive_index_map.AppendNew(); ts_to_ds.i = i; ts_to_ds.j = (nullptr == m_archive_text_style_table[i] ) ? current_ds_index : ON_UNSET_INT_INDEX; } const int archive_dim_style_count = m_archive_dim_style_table.Count(); for (;;) { // see if we need to map text styles to dimstyles const unsigned int archive_opennurbs_version = ArchiveOpenNURBSVersion(); unsigned int version_major = 0; unsigned int version_minor = 0; unsigned int version_year = 0; unsigned int version_month = 0; unsigned int version_day_of_month = 0; unsigned int version_branch = 0; bool bParsedVersionNumber = ON_VersionNumberParse( archive_opennurbs_version, &version_major, &version_minor, &version_year, &version_month, &version_day_of_month, &version_branch ); if (false == bParsedVersionNumber) break; if (version_major < 6) break; if (version_major == 6) { // version 6 files written before aug 15 2016 have text styles. if ((version_year * 10000 + version_month * 100 + version_day_of_month) < (2016 * 10000 + 8 * 100 + 15)) break; } // 3dm archive created with new code that has no text style index references. // Ignore "fake" text style table in written by new code so files // could be read by older code. for (int i = 0; i < m_archive_text_style_table.Count(); i++) m_text_style_to_dim_style_archive_index_map[i].j = current_ds_index; return; } // Map text styles to dimstyles, creating new dimstyles as required. const double model_view_text_scale = Internal_ArchiveModelSpaceTextScale(); for (int i = 0; i < archive_text_style_count; i++) { ON_2dex& ts_to_ds = m_text_style_to_dim_style_archive_index_map[i]; if (ON_UNSET_INT_INDEX != ts_to_ds.j) continue; // this text style is already mapped. const ON_TextStyle* text_style = m_archive_text_style_table[i]; if (nullptr == text_style) { ts_to_ds.j = current_ds_index; continue; } const ON_Font& font = text_style->Font(); const unsigned int font_sn = font.ManagedFontSerialNumber(); double delta_scale = 1e300; for (int j = 0; j < archive_dim_style_count; j++) { const ON_DimStyle* archive_dim_style = m_archive_dim_style_table[j]; if (nullptr == archive_dim_style) continue; if (archive_dim_style->ParentIdIsNotNil()) continue; if (font_sn != archive_dim_style->Font().ManagedFontSerialNumber()) continue; // Do not require exact model_view_text_scale = DimScale(), // We will simply override this property // on a per object basis when that is required. double d = fabs(model_view_text_scale - archive_dim_style->DimScale()); if (ON_UNSET_INT_INDEX == ts_to_ds.j || d < delta_scale) { ts_to_ds.j = j; delta_scale = d; if (0.0 == delta_scale) break; } } if (ON_UNSET_INT_INDEX == ts_to_ds.j) { // need to make a new dim style for this font. ON_DimStyle* dim_style = ON_DimStyle::CreateFromFont(&font, model_view_text_scale, nullptr, &m_manifest, nullptr); ts_to_ds.j = m_archive_dim_style_table.Count(); dim_style->ClearIndex(); dim_style->SetId(); const ON::LengthUnitSystem dim_style_unit_system = ON::IsUnitedStatesCustomaryLengthUnit(Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem()) ? ON::LengthUnitSystem::Inches : ON::LengthUnitSystem::Millimeters; dim_style->SetUnitSystem( dim_style_unit_system ); m_archive_dim_style_table.Append(dim_style); Internal_Read3dmUpdateManifest(*dim_style); } // If other text styles use the same font, map them as well for (int k = i + 1; k < archive_text_style_count; k++) { text_style = m_archive_text_style_table[k]; if (nullptr == text_style) continue; if (font_sn != text_style->Font().ManagedFontSerialNumber()) continue; m_text_style_to_dim_style_archive_index_map[k].j = ts_to_ds.j; } } } bool ON_BinaryArchive::BeginRead3dmDimStyleTable() { // August 2016. text style information is now on ON_DimStyle // A text style table must still be saved in archives so earlier versions of Rhino // can read the archives written by new versions. if (0 != m_archive_dim_style_table_status || ON_UNSET_UINT_INDEX != m_archive_dim_style_table_read_index) { ON_ERROR("BeginRead3dmDimStyleTable() has already been called."); return false; } m_archive_dim_style_table_read_index = 0; // read legacy text style table if (!BeginRead3dmTable(TCODE_FONT_TABLE)) return false; for (;;) { ON_TextStyle* text_style = nullptr; Internal_Read3dmTextStyle(&text_style); if (nullptr == text_style) break; m_archive_text_style_table.Append(text_style); } const int archive_text_style_count = m_archive_text_style_table.Count(); m_text_style_to_dim_style_archive_index_map.SetCount(0); m_text_style_to_dim_style_archive_index_map.Reserve(archive_text_style_count); for (int i = 0; i < archive_text_style_count; i++) { ON_2dex& ts_to_ds = m_text_style_to_dim_style_archive_index_map.AppendNew(); ts_to_ds.i = i; ts_to_ds.j = ON_UNSET_INT_INDEX; } if (!EndRead3dmTable(TCODE_FONT_TABLE)) return false; if (!BeginRead3dmTable(TCODE_DIMSTYLE_TABLE)) return false; m_archive_dim_style_table_status = 1; // read all the dimstyles in the 3dm archive for (;;) { ON_DimStyle* archive_dim_style = nullptr; int rc = Internal_Read3dmDimStyle(&archive_dim_style); const ON::LengthUnitSystem archive_unit_system = Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(); if (nullptr != archive_dim_style) { if (archive_dim_style->ParentId() == archive_dim_style->Id()) { ON_ERROR("Invalid dimstyle parent id in archive."); archive_dim_style->ClearParentId(); } if ( archive_dim_style->ParentIdIsNil() ) archive_dim_style->ClearAllFieldOverrides(); else m_bLegacyOverrideDimStylesInArchive = true; archive_dim_style->SetUnitSystemFromContext(true, archive_unit_system, ON::LengthUnitSystem::None); m_archive_dim_style_table.Append(archive_dim_style); } if (rc <= 0) break; } if (m_bLegacyOverrideDimStylesInArchive) { m_bLegacyOverrideDimStylesInArchive = false; for (unsigned int j = 0; j < m_archive_dim_style_table.UnsignedCount(); j++) { ON_DimStyle* archive_dim_style = m_archive_dim_style_table[j]; if (archive_dim_style->ParentIdIsNil()) continue; const ON_UUID parent_id = archive_dim_style->ParentId(); ON_DimStyle* archive_parent_dim_style = nullptr; for (unsigned int i = 0; i < m_archive_dim_style_table.UnsignedCount(); i++) { ON_DimStyle* ds = m_archive_dim_style_table[i]; if (ds->ParentIdIsNotNil()) continue; if (parent_id == ds->Id()) { archive_parent_dim_style = ds; break; } } if (nullptr == archive_parent_dim_style) { ON_ERROR("Invalid dimstyle parent id in archive."); archive_dim_style->ClearAllFieldOverrides(); archive_dim_style->ClearParentId(); continue; } } } for (unsigned int j = 0; j < m_archive_dim_style_table.UnsignedCount(); j++) { ON_DimStyle* archive_dim_style = m_archive_dim_style_table[j]; archive_dim_style->ClearIndex(); if (archive_dim_style->ParentIdIsNotNil()) { m_bLegacyOverrideDimStylesInArchive = true; continue; } Internal_Read3dmUpdateManifest(*archive_dim_style); } // Set a current dimstyle. m_archive_current_dim_style = Internal_ArchiveCurrentDimStyle(); // Set the current annotation context int V5_archive_dim_style_index = ON_UNSET_INT_INDEX; if (nullptr != m_archive_current_dim_style) { if (m_archive_current_dim_style->IsSystemComponent()) { V5_archive_dim_style_index = m_archive_current_dim_style->Index(); } else { for (unsigned int j = 0; j < m_archive_dim_style_table.UnsignedCount(); j++) { if (m_archive_current_dim_style == m_archive_dim_style_table[j]) { V5_archive_dim_style_index = j; break; } } } } m_annotation_context.SetReferencedDimStyle(m_archive_current_dim_style,nullptr,V5_archive_dim_style_index); // convert text styles to dimstyles Internal_ConvertTextStylesToDimStyles(); return true; // must return true because calling code must call EndRead3dmDimStyleTable(). } int ON_BinaryArchive::Read3dmDimStyle( class ON_DimStyle** ppDimStyle ) { if (nullptr != ppDimStyle) *ppDimStyle = nullptr; if (1 != m_archive_dim_style_table_status || m_archive_dim_style_table_read_index >= ON_UNSET_UINT_INDEX - 1) { ON_ERROR("All calls to Read3dmDimStyle() must be after a single call to BeginRead3dmDimStyleTable() and before a single call to EndRead3dmDimStyleTable()."); return 0; } ON_DimStyle* dim_style = nullptr; while (m_archive_dim_style_table_read_index < m_archive_dim_style_table.UnsignedCount()) { const unsigned int i = m_archive_dim_style_table_read_index++; ON_DimStyle* archive_dim_style = m_archive_dim_style_table[i]; if (nullptr == archive_dim_style) continue; if (archive_dim_style->ParentIdIsNotNil()) continue; ON_DimStyle* managed_dim_style = new ON_DimStyle(*archive_dim_style); m_archive_dim_style_table[i] = managed_dim_style; m_annotation_context.UpdateReferencedDimStyle(archive_dim_style, managed_dim_style); if (archive_dim_style == m_archive_current_dim_style) m_archive_current_dim_style = managed_dim_style; dim_style = archive_dim_style; break; } if (nullptr == dim_style) { m_archive_dim_style_table_read_index = ON_UNSET_UINT_INDEX - 1; return 0; } if (nullptr != ppDimStyle) *ppDimStyle = dim_style; return 1; } int ON_BinaryArchive::Internal_Read3dmDimStyle( class ON_DimStyle**ppDimStyle ) { if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::dimension_style_table, (void**)ppDimStyle)) return 0; int rc = -1; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) { if ( tcode == TCODE_DIMSTYLE_RECORD ) { Internal_Increment3dmTableItemCount(); ON_Object* p = nullptr; if ( ReadObject( &p ) ) { ON_DimStyle* V6_dimstyle = ON_DimStyle::Cast(p); int legacy_text_style_index = ON_UNSET_INT_INDEX; if (nullptr == V6_dimstyle) { ON_V5x_DimStyle* V5_dimstyle = ON_V5x_DimStyle::Cast(p); if (nullptr != V5_dimstyle) { legacy_text_style_index = V5_dimstyle->V5TextStyle().Index(); V6_dimstyle = new ON_DimStyle( this->Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(), *V5_dimstyle ); // This was the behavior for leader text alignment in v5 and didn't have a field in v5 dimstyles. // The default so far in V6 is Middle which only looks like v5 with single line leader text V6_dimstyle->SetLeaderTextVerticalAlignment(ON::TextVerticalAlignment::MiddleOfTop); // old files do not contain unit system information // Keep in mind this V5 file may have be written as a "SaveAs V5" from V6 or later. // That's why we are checking for a matching system dimstyle before using the unit // system from the file. V6_dimstyle->SetUnitSystem(ON::LengthUnitSystem::None); V6_dimstyle->SetUnitSystemFromContext(true,Archive3dmSettings().m_ModelUnitsAndTolerances.m_unit_system.UnitSystem(),ON::LengthUnitSystem::None); delete V5_dimstyle; } else { delete p; p = nullptr; } } if (nullptr == V6_dimstyle) { ON_ERROR("ON_BinaryArchive::Read3dmDimStyle() - corrupt annotation style table"); } else { rc = 1; *ppDimStyle = V6_dimstyle; } } } else if (TCODE_ENDOFTABLE == tcode) { // end of dimension style table rc = 0; } else { ON_ERROR("ON_BinaryArchive::Read3dmDimStyle() - corrupt annotation style table"); } EndRead3dmChunk(); } return rc; } bool ON_BinaryArchive::EndRead3dmDimStyleTable() { return EndRead3dmTable( TCODE_DIMSTYLE_TABLE ); } const ON_DimStyle& ON_BinaryArchive::ArchiveCurrentDimStyle() const { return (nullptr == m_archive_current_dim_style) ? ON_DimStyle::Default : *m_archive_current_dim_style; } const int ON_BinaryArchive::ArchiveCurrentDimStyleIndex() const { const ON_DimStyle& dim_style = ArchiveCurrentDimStyle(); if (dim_style.IsSystemComponent()) return dim_style.Index(); const ON_ComponentManifestItem& item = m_manifest.ItemFromId(ON_ModelComponent::Type::DimStyle,ArchiveCurrentDimStyle().Id()); if (item.IsValid()) return item.Index(); return -1; } const ON_UUID ON_BinaryArchive::ArchiveCurrentDimStyleId() const { return ArchiveCurrentDimStyle().Id(); } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// bool ON_BinaryArchive::BeginWrite3dmHatchPatternTable() { return BeginWrite3dmTable( TCODE_HATCHPATTERN_TABLE ); } bool ON_BinaryArchive::Write3dmHatchPatternComponent( const ON_ModelComponentReference& model_component_reference ) { return Write3dmHatchPatternComponent(model_component_reference.ModelComponent()); } bool ON_BinaryArchive::Write3dmHatchPatternComponent( const ON_ModelComponent* model_component ) { bool rc = false; for (;;) { const ON_HatchPattern* hatch_pattern = ON_HatchPattern::Cast(model_component); if (nullptr == hatch_pattern) { ON_ERROR("model_component parameter is not a hatch pattern component."); break; } rc = Write3dmHatchPattern(*hatch_pattern); break; } return rc; } bool ON_BinaryArchive::Write3dmHatchPattern( const ON_HatchPattern& pattern ) { if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::hatchpattern_table)) return true; if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::hatchpattern_table) ) return false; Internal_Increment3dmTableItemCount(); bool rc = false; const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c && c->m_typecode == TCODE_HATCHPATTERN_TABLE ) { rc = BeginWrite3dmChunk( TCODE_HATCHPATTERN_RECORD, 0 ); if (rc) { Internal_Write3dmUpdateManifest(pattern); rc = WriteObject( pattern ); if ( !EndWrite3dmChunk() ) rc = false; } // 1 Nov 2005 Dale Lear: // // This code was used before version 200511010. The reader can // still read the old files, but old versions of Rhino cannot read // files written with version 200511010 or later. This happened in // the early beta cycile of V4. V3 did not have hatch patterns. // Please leave this comment here until Nov 2006 so I can remember // what happened if I have to debug file IO. By May 2006, all // the betas that could write the bogus hatch tables should have // expired. // //if ( rc ) { // rc = pattern.Write( *this)?true:false; // if ( !EndWrite3dmChunk()) // rc = false; //} } else { ON_ERROR("ON_BinaryArchive::Write3dmHatchPattern() must be called in BeginWrite3dmHatchPatternTable() block"); rc = false; } return rc; } bool ON_BinaryArchive::EndWrite3dmHatchPatternTable() { return EndWrite3dmTable( TCODE_HATCHPATTERN_TABLE ); } bool ON_BinaryArchive::BeginRead3dmHatchPatternTable() { return BeginRead3dmTable( TCODE_HATCHPATTERN_TABLE ); } int ON_BinaryArchive::Read3dmHatchPattern( ON_HatchPattern** ppPattern ) { if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::hatchpattern_table, (void**)ppPattern)) return 0; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; ON_HatchPattern* hatch_pattern = nullptr; if( BeginRead3dmBigChunk( &tcode, &big_value)) { if ( tcode == TCODE_HATCHPATTERN_RECORD ) { Internal_Increment3dmTableItemCount(); if ( m_3dm_opennurbs_version < 200511010 ) { // There was a bug in Write3dmHatchPattern and files written // before version 200511010 didn't use ON_Object IO. hatch_pattern = new ON_HatchPattern; if( !hatch_pattern->Read( *this)) { delete hatch_pattern; hatch_pattern = nullptr; ON_ERROR("ON_BinaryArchive::Read3dmHatchPattern() - corrupt hatch pattern table"); } } else { ON_Object* p = 0; if ( ReadObject( &p ) ) { hatch_pattern = ON_HatchPattern::Cast(p); if ( nullptr == hatch_pattern ) delete p; } if (nullptr == hatch_pattern) { ON_ERROR("ON_BinaryArchive::Read3dmHatchPattern() - corrupt hatch pattern table"); } } } else if ( tcode != TCODE_ENDOFTABLE ) { ON_ERROR("ON_BinaryArchive::Read3dmHatchPattern() - corrupt hatch pattern table"); } EndRead3dmChunk(); } if (nullptr != hatch_pattern) Internal_Read3dmUpdateManifest(*hatch_pattern); *ppPattern = hatch_pattern; return( hatch_pattern) ? 1 : 0; } bool ON_BinaryArchive::EndRead3dmHatchPatternTable() { return EndRead3dmTable( TCODE_HATCHPATTERN_TABLE); } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// bool ON_BinaryArchive::BeginWrite3dmLinetypeTable() { bool rc = BeginWrite3dmTable( TCODE_LINETYPE_TABLE ); return rc; } bool ON_BinaryArchive:: Write3dmLinePatternComponent( const ON_ModelComponentReference& model_component_reference ) { return Write3dmLinePatternComponent(model_component_reference.ModelComponent()); } bool ON_BinaryArchive::Write3dmLinePatternComponent( const ON_ModelComponent* model_component ) { bool rc = false; for (;;) { const ON_Linetype* line_pattern = ON_Linetype::Cast(model_component); if (nullptr == line_pattern) { ON_ERROR("model_component parameter is not a line pattern component."); break; } rc = Write3dmLinetype(*line_pattern); break; } return rc; } bool ON_BinaryArchive::Write3dmLinetype( const ON_Linetype& line_pattern ) { if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::linetype_table)) return true; if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::linetype_table) ) return false; Internal_Increment3dmTableItemCount(); bool rc = false; if( Active3dmTable() != ON_3dmArchiveTableType::linetype_table ) { Internal_ReportCriticalError(); ON_ERROR("ON_BinaryArchive::Write3dmLinetype() - m_active_table != linetype_table"); } const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c && c->m_typecode == TCODE_LINETYPE_TABLE ) { rc = BeginWrite3dmChunk( TCODE_LINETYPE_RECORD, 0 ); if ( rc ) { Internal_Write3dmUpdateManifest(line_pattern); rc = WriteObject( line_pattern ); if ( !EndWrite3dmChunk()) rc = false; } } else { Internal_ReportCriticalError(); ON_ERROR("ON_BinaryArchive::Write3dmLinetype() must be called in BeginWrite3dmLinetypeTable() block"); rc = false; } return rc; } bool ON_BinaryArchive::EndWrite3dmLinetypeTable() { return EndWrite3dmTable( TCODE_LINETYPE_TABLE ); } bool ON_BinaryArchive::BeginRead3dmLinetypeTable() { return BeginRead3dmTable( TCODE_LINETYPE_TABLE ); } int ON_BinaryArchive::Read3dmLinetype( ON_Linetype** ppLinetype ) { if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::linetype_table, (void**)ppLinetype)) return 0; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; ON_Linetype* linetype = nullptr; int rc = -1; if( BeginRead3dmBigChunk( &tcode, &big_value)) { if ( tcode == TCODE_LINETYPE_RECORD ) { Internal_Increment3dmTableItemCount(); ON_Object* p = 0; if ( ReadObject( &p ) ) { linetype = ON_Linetype::Cast(p); if (!linetype ) delete p; else { if (ppLinetype) *ppLinetype = linetype; Internal_Read3dmUpdateManifest(*linetype); rc = 1; } } if (!linetype) { Internal_ReportCriticalError(); ON_ERROR("ON_BinaryArchive::Read3dmLinetype() - corrupt linetype table"); } } else if ( tcode == TCODE_ENDOFTABLE ) { // end of linetype table rc = 0; } else { Internal_ReportCriticalError(); ON_ERROR("ON_BinaryArchive::Read3dmLinetype() - corrupt linetype table"); } if (!EndRead3dmChunk()) rc = -1; } return rc; } bool ON_BinaryArchive::EndRead3dmLinetypeTable() { return EndRead3dmTable( TCODE_LINETYPE_TABLE); } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// bool ON_BinaryArchive::BeginWrite3dmInstanceDefinitionTable() { return BeginWrite3dmTable( TCODE_INSTANCE_DEFINITION_TABLE ); } bool ON_BinaryArchive::Write3dmInstanceDefinitionComponent( const class ON_ModelComponentReference& model_component_reference ) { return Write3dmInstanceDefinitionComponent(model_component_reference.ModelComponent()); } bool ON_BinaryArchive::Write3dmInstanceDefinitionComponent( const class ON_ModelComponent* model_component ) { bool rc = false; for (;;) { const ON_InstanceDefinition* instance_definition = ON_InstanceDefinition::Cast(model_component); if (nullptr == instance_definition) { ON_ERROR("model_component parameter is not an instance definition component."); break; } rc = Write3dmInstanceDefinition(*instance_definition); break; } return rc; } bool ON_BinaryArchive::Write3dmInstanceDefinition( const ON_InstanceDefinition& idef ) { if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::instance_definition_table)) return true; if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::instance_definition_table) ) return false; Internal_Increment3dmTableItemCount(); bool rc = false; const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c && c->m_typecode == TCODE_INSTANCE_DEFINITION_TABLE ) { rc = BeginWrite3dmChunk( TCODE_INSTANCE_DEFINITION_RECORD, 0 ); if ( rc ) { Internal_Write3dmUpdateManifest(idef); rc = WriteObject( idef ); if ( !EndWrite3dmChunk() ) rc = false; } } else { ON_ERROR("ON_BinaryArchive::Write3dmInstanceDefinition() must be called in BeginWrite3dmInstanceDefinitionTable() block"); rc = false; } return rc; } bool ON_BinaryArchive::EndWrite3dmInstanceDefinitionTable() { return EndWrite3dmTable( TCODE_INSTANCE_DEFINITION_TABLE ); } bool ON_BinaryArchive::BeginRead3dmInstanceDefinitionTable() { return BeginRead3dmTable( TCODE_INSTANCE_DEFINITION_TABLE ); } int ON_BinaryArchive::Read3dmInstanceDefinition( ON_InstanceDefinition** ppInstanceDefinition ) { if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::instance_definition_table, (void**)ppInstanceDefinition)) return 0; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; ON_InstanceDefinition* idef = nullptr; if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) { if ( tcode == TCODE_INSTANCE_DEFINITION_RECORD ) { Internal_Increment3dmTableItemCount(); ON_Object* p = 0; if ( ReadObject( &p ) ) { idef = ON_InstanceDefinition::Cast(p); if ( !idef ) delete p; } if (!idef) { ON_ERROR("ON_BinaryArchive::Read3dmInstanceDefinition() - corrupt instance definition table"); } } else if ( tcode != TCODE_ENDOFTABLE ) { ON_ERROR("ON_BinaryArchive::Read3dmInstanceDefinition() - corrupt instance definition table"); } EndRead3dmChunk(); } if (idef) { Internal_Read3dmUpdateManifest(*idef); } *ppInstanceDefinition = idef; return (idef) ? 1 : 0; } bool ON_BinaryArchive::EndRead3dmInstanceDefinitionTable() { return EndRead3dmTable( TCODE_INSTANCE_DEFINITION_TABLE ); } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// bool ON_BinaryArchive::BeginWrite3dmTextureMappingTable() { return BeginWrite3dmTable( TCODE_TEXTURE_MAPPING_TABLE ); } bool ON_BinaryArchive::Write3dmTextureMappingComponent( const ON_ModelComponentReference& model_component_reference ) { return Write3dmTextureMappingComponent(model_component_reference.ModelComponent()); } bool ON_BinaryArchive::Write3dmTextureMappingComponent( const ON_ModelComponent* model_component ) { bool rc = false; for (;;) { const ON_TextureMapping* texture_mapping = ON_TextureMapping::Cast(model_component); if (nullptr == texture_mapping) { ON_ERROR("model_component parameter is not a texture mapping component."); break; } rc = Write3dmTextureMapping(*texture_mapping); break; } return rc; } bool ON_BinaryArchive::Write3dmTextureMapping( const ON_TextureMapping& texture_mapping ) { if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::texture_mapping_table)) return true; if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::texture_mapping_table) ) return false; Internal_Increment3dmTableItemCount(); bool rc = false; if ( m_3dm_active_table != ON_3dmArchiveTableType::texture_mapping_table ) { ON_ERROR("ON_BinaryArchive::Write3dmTextureMapping() - m_active_table != texture_mapping_table"); } const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( !c || c->m_typecode != TCODE_TEXTURE_MAPPING_TABLE ) { ON_ERROR("ON_BinaryArchive::Write3dmTextureMapping() - active chunk typecode != TCODE_TEXTURE_MAPPING_TABLE"); } else { rc = BeginWrite3dmChunk( TCODE_TEXTURE_MAPPING_RECORD, 0 ); if (rc) { Internal_Write3dmUpdateManifest(texture_mapping); rc = WriteObject( texture_mapping ); if ( !EndWrite3dmChunk() ) rc = false; } } return rc; } bool ON_BinaryArchive::EndWrite3dmTextureMappingTable() { return EndWrite3dmTable( TCODE_TEXTURE_MAPPING_TABLE ); } bool ON_BinaryArchive::BeginRead3dmTextureMappingTable() { return BeginRead3dmTable( TCODE_TEXTURE_MAPPING_TABLE ); } int ON_BinaryArchive::Read3dmTextureMapping( ON_TextureMapping** ppTextureMapping ) { if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::texture_mapping_table, (void**)ppTextureMapping)) return 0; ON_TextureMapping* texture_mapping = nullptr; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; int rc = -1; if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) { if ( tcode == TCODE_TEXTURE_MAPPING_RECORD ) { Internal_Increment3dmTableItemCount(); ON_Object* p = 0; if ( ReadObject( &p ) ) { texture_mapping = ON_TextureMapping::Cast(p); if ( !texture_mapping ) delete p; else { if ( ppTextureMapping ) *ppTextureMapping = texture_mapping; rc = 1; Internal_Read3dmUpdateManifest(*texture_mapping); } } if (!texture_mapping) { Internal_ReportCriticalError(); ON_ERROR("ON_BinaryArchive::Read3dmTextureMapping() - corrupt texture_mapping table"); } } else if ( tcode == TCODE_ENDOFTABLE ) { // end of texture_mapping table rc = 0; } else { Internal_ReportCriticalError(); ON_ERROR("ON_BinaryArchive::Read3dmTextureMapping() - corrupt texture_mapping table"); } if ( !EndRead3dmChunk() ) rc = -1; } return rc; } bool ON_BinaryArchive::EndRead3dmTextureMappingTable() { return EndRead3dmTable( TCODE_TEXTURE_MAPPING_TABLE ); } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// bool ON_BinaryArchive::BeginWrite3dmHistoryRecordTable() { return BeginWrite3dmTable( TCODE_HISTORYRECORD_TABLE ); } bool ON_BinaryArchive::Write3dmHistoryRecordComponent( const class ON_ModelComponentReference& model_component_reference ) { return Write3dmHistoryRecordComponent(model_component_reference.ModelComponent()); } bool ON_BinaryArchive::Write3dmHistoryRecordComponent( const class ON_ModelComponent* model_component ) { bool rc = false; for (;;) { const ON_HistoryRecord* history_record = ON_HistoryRecord::Cast(model_component); if (nullptr == history_record) { ON_ERROR("model_component parameter is not a history record component."); break; } rc = Write3dmHistoryRecord(*history_record); break; } return rc; } bool ON_BinaryArchive::Write3dmHistoryRecord( const ON_HistoryRecord& history_record ) { if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::historyrecord_table)) return true; if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::historyrecord_table) ) return false; Internal_Increment3dmTableItemCount(); bool rc = false; const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( !c || c->m_typecode != TCODE_HISTORYRECORD_TABLE ) { ON_ERROR("ON_BinaryArchive::Write3dmHistoryRecord() - active chunk typecode != TCODE_HISTORYRECORD_TABLE"); } else { rc = BeginWrite3dmChunk( TCODE_HISTORYRECORD_RECORD, 0 ); if (rc) { Internal_Write3dmUpdateManifest(history_record); rc = WriteObject( history_record ); if ( !EndWrite3dmChunk() ) rc = false; } } return rc; } bool ON_BinaryArchive::EndWrite3dmHistoryRecordTable() { return EndWrite3dmTable( TCODE_HISTORYRECORD_TABLE ); } bool ON_BinaryArchive::BeginRead3dmHistoryRecordTable() { return BeginRead3dmTable( TCODE_HISTORYRECORD_TABLE ); } int ON_BinaryArchive::Read3dmHistoryRecord( ON_HistoryRecord*& history_record ) { history_record = nullptr; if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::historyrecord_table, (void**)&history_record)) return 0; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; int rc = -1; if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) { if ( tcode == TCODE_HISTORYRECORD_RECORD ) { Internal_Increment3dmTableItemCount(); ON_Object* p = 0; if ( ReadObject( &p ) ) { history_record = ON_HistoryRecord::Cast(p); if ( nullptr == history_record ) { delete p; } else { rc = 1; Internal_Read3dmUpdateManifest(*history_record); } } if (nullptr == history_record) { ON_ERROR("ON_BinaryArchive::Read3dmHistoryRecord() - corrupt history_record table"); } } else if ( tcode == TCODE_ENDOFTABLE ) { // end of history_record table rc = 0; } else { ON_ERROR("ON_BinaryArchive::Read3dmHistoryRecord() - corrupt history_record table"); } if ( !EndRead3dmChunk() ) rc = -1; } return rc; } bool ON_BinaryArchive::EndRead3dmHistoryRecordTable() { return EndRead3dmTable( TCODE_HISTORYRECORD_TABLE ); } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// bool ON_BinaryArchive::BeginWrite3dmMaterialTable() { return BeginWrite3dmTable( TCODE_MATERIAL_TABLE ); } bool ON_BinaryArchive::Write3dmMaterialComponent( const ON_ModelComponentReference& model_component_reference ) { return Write3dmMaterialComponent(model_component_reference.ModelComponent()); } bool ON_BinaryArchive::Write3dmMaterialComponent( const ON_ModelComponent* model_component ) { bool rc = false; for (;;) { const ON_Material* render_material = ON_Material::Cast(model_component); if (nullptr == render_material) { ON_ERROR("model_component parameter is not a render material component."); break; } rc = Write3dmMaterial(*render_material); break; } return rc; } bool ON_BinaryArchive::Write3dmMaterial( const ON_Material& material ) { if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::material_table)) return true; if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::material_table) ) return false; Internal_Increment3dmTableItemCount(); bool rc = false; const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( !c || c->m_typecode != TCODE_MATERIAL_TABLE ) { Internal_ReportCriticalError(); ON_ERROR("ON_BinaryArchive::Write3dmMaterial() - active chunk typecode != TCODE_MATERIAL_TABLE"); } else { rc = BeginWrite3dmChunk( TCODE_MATERIAL_RECORD, 0 ); if (rc) { Internal_Write3dmUpdateManifest(material); rc = WriteObject( material ); if ( !EndWrite3dmChunk() ) rc = false; } } return rc; } bool ON_BinaryArchive::EndWrite3dmMaterialTable() { return EndWrite3dmTable( TCODE_MATERIAL_TABLE ); } bool ON_BinaryArchive::BeginRead3dmMaterialTable() { m_3dm_v1_material_index = 0; return BeginRead3dmTable( TCODE_MATERIAL_TABLE ); } bool ON_BinaryArchive::Read3dmV1String( ON_String& s ) { int string_length = 0; s.Empty(); bool rc = ReadInt( &string_length ); if (rc) { s.ReserveArray(string_length+1); rc = ReadChar( string_length, s.Array() ); if (rc) s.SetLength(string_length); } return rc; } class ON__3dmV1_XDATA { // helper class to get V1 "xdata" out of attributes block. public: enum { unknown_xdata = 0, hidden_object_layer_name, // m_string = actual layer name locked_object_layer_name, // m_string = actual layer name arrow_direction, // m_vector = arrow head location dot_text // m_string = dot text } m_type; ON_String m_string; ON_3dVector m_vector; }; bool ON_BinaryArchive::Read3dmV1AttributesOrMaterial( ON_3dmObjectAttributes* attributes, ON_Material* material, bool& bHaveMat, unsigned int end_mark_tcode, ON__3dmV1_XDATA* xdata ) { // Check ReadV1Material() if you fix any bugs in the mateial related items if ( 0 != xdata ) { xdata->m_type = ON__3dmV1_XDATA::unknown_xdata; } bool rc = false; unsigned int u; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; ON_Color c; bHaveMat = false; bool bEndRead3dmChunk_rc; const unsigned int saved_error_message_mask = m_error_message_mask; int xdata_layer_index = -1; if ( attributes ) { attributes->Default(); } if ( material ) { *material = ON_Material::Unset; material->m_diffuse.SetRGB(255,255,255); // default is (128,128,128) } for (;;) { m_error_message_mask = saved_error_message_mask; if ( end_mark_tcode != TCODE_ENDOFTABLE ) { tcode = 0; big_value = 0; if ( !PeekAt3dmBigChunkType(&tcode,&big_value) ) { break; // should not happen } if ( tcode == end_mark_tcode ) { rc = true; break; // done reading attributes } } tcode = 0; big_value = 0; if ( !BeginRead3dmBigChunk(&tcode,&big_value) ) break; if ( tcode == end_mark_tcode ) { rc = EndRead3dmChunk(); break; } switch( tcode ) { case (TCODE_OPENNURBS_OBJECT | TCODE_CRC | 0x7FFD): // 1.1 object 16 byte UUID + 2 byte crc if ( attributes ) ReadUuid( attributes->m_uuid ); break; case TCODE_LAYERREF: if ( attributes && (-1 == xdata_layer_index || attributes->m_layer_index != xdata_layer_index) && (big_value >= 0 && big_value < 0x7FFFFFFF) ) { const int file_layer_index = (int)big_value; const ON_ManifestMapItem& map_item = m_manifest_map.MapItemFromSourceIndex(ON_ModelComponent::Type::Layer, file_layer_index); attributes->m_layer_index = map_item.SourceAndDestinationAreSet() ? map_item.DestinationIndex() : file_layer_index; } break; case TCODE_RGB: if ( big_value != 0xFFFFFF ) { if ( material ) { ON__UINT64 rgb64 = (ON__UINT64)big_value; ON__UINT32 rgb32 = (ON__UINT32)rgb64; u = rgb32; c.SetRGB( u%256,(u>>8)%256,(u>>16)%256 ); material->SetDiffuse(c); material->SetShine((u >> 24)/100.0*ON_Material::MaxShine); } bHaveMat = true; } break; case TCODE_RGBDISPLAY: if ( attributes ) { ON__UINT64 rgb64 = (ON__UINT64)big_value; ON__UINT32 rgb32 = (ON__UINT32)rgb64; u = rgb32; attributes->m_color.SetRGB( u%256,(u>>8)%256,(u>>16)%256 ); } break; case TCODE_TRANSPARENCY: if ( big_value > 0 && big_value <= 255 ) { if ( material ) material->SetTransparency(big_value/255.0); bHaveMat = true; } break; case TCODE_NAME: if ( attributes ) { ON_String s; Read3dmV1String(s); if( s.Length() > 0 ) attributes->m_name = s; } break; case TCODE_TEXTUREMAP: { ON_String s; Read3dmV1String(s); if ( s.Length() > 0 ) { if ( material ) { ON_Texture& tx = material->m_textures.AppendNew(); tx.m_image_file_reference.SetFullPath(s,false); tx.m_type = ON_Texture::TYPE::bitmap_texture; } bHaveMat = true; } } break; case TCODE_BUMPMAP: if ( material ) { ON_String s; Read3dmV1String(s); if ( s.Length() ) { if ( material ) { ON_Texture& tx = material->m_textures.AppendNew(); tx.m_image_file_reference.SetFullPath(s,false); tx.m_type = ON_Texture::TYPE::bump_texture; } bHaveMat = true; } } break; case TCODE_XDATA: // v1 "xdata" if ( attributes ) { ON_String layer_name; ON_String xid; int sizeof_xid = 0; int sizeof_data = 0; ReadInt(&sizeof_xid); ReadInt(&sizeof_data); xid.SetLength(sizeof_xid); ReadByte(sizeof_xid,xid.Array()); if ( !on_stricmp("RhHidePrevLayer",static_cast< const char* >(xid)) ) { if ( sizeof_data > 0 ) { // v1 object is hidden - real layer name is in xdata char* buffer = (char*)onmalloc((sizeof_data+1)*sizeof(buffer[0])); buffer[0] = 0; buffer[sizeof_data] = 0; if ( ReadByte(sizeof_data,buffer) ) { if ( -1 == xdata_layer_index ) { xdata_layer_index = Read3dmV1LayerIndex(buffer); if ( xdata_layer_index >= 0 ) { attributes->m_layer_index = xdata_layer_index; attributes->SetVisible(false); } } else { xdata_layer_index = -2; } //if ( 0 != xdata ) //{ // xdata->m_type = ON__3dmV1_XDATA::hidden_object_layer_name; // xdata->m_string = buffer; //} } onfree(buffer); } } else if ( !on_stricmp("RhFreezePrevLayer",static_cast< const char* >(xid)) ) { // v1 object is locked - real layer name is in xdata if ( sizeof_data > 0 ) { char* buffer = (char*)onmalloc((sizeof_data+1)*sizeof(buffer[0])); buffer[0] = 0; buffer[sizeof_data] = 0; if ( ReadByte(sizeof_data,buffer) ) { if ( -1 == xdata_layer_index ) { xdata_layer_index = Read3dmV1LayerIndex(buffer); if ( xdata_layer_index >= 0 ) { attributes->m_layer_index = xdata_layer_index; attributes->SetMode(ON::locked_object); } } else { xdata_layer_index = -2; } //if ( 0 != xdata ) //{ // xdata->m_type = ON__3dmV1_XDATA::locked_object_layer_name; // xdata->m_string = buffer; //} } onfree(buffer); } } else if ( !on_stricmp("RhAnnotateArrow",static_cast< const char* >(xid)) && 24 == sizeof_data ) { // v1 annotation arrow objects were saved // as TCODE_RH_POINT objects with the // arrow tail location = point location and the // arrow head location saved in 24 bytes of "xdata". ON_3dVector arrow_direction; if ( ReadVector( arrow_direction ) && 0 != xdata ) { xdata->m_type = ON__3dmV1_XDATA::arrow_direction; xdata->m_vector = arrow_direction; } } else if ( !on_stricmp("RhAnnotateDot",static_cast< const char* >(xid)) ) { if ( sizeof_data > 0 ) { // v1 annotation dot objects were saved // as TCODE_RH_POINT objects with the // dot text saved in "xdata". char* buffer = (char*)onmalloc((sizeof_data+1)*sizeof(buffer[0])); buffer[0] = 0; buffer[sizeof_data] = 0; if ( ReadByte(sizeof_data,buffer) && 0 != xdata ) { xdata->m_type = ON__3dmV1_XDATA::dot_text; xdata->m_string = buffer; } onfree(buffer); } } else { m_error_message_mask |= 0x0002; // disable v1 EndRead3dmChunk() partially read chunk warning } // call to EndRead3dmChunk() will skip unread junk } break; case TCODE_DISP_CPLINES: if ( big_value > 0 && big_value <= 0x7FFFFFFF && attributes ) attributes->m_wire_density = (int)big_value; break; case TCODE_RENDER_MATERIAL_ID: { int flag; ON_String s; ReadInt(&flag); if ( flag == 1 ) { Read3dmV1String(s); if ( s.Length() > 0 ) { if (material) { ON_wString wide_name(s); material->SetName(wide_name); } bHaveMat = true; } } } break; default: // obsolete attributes from v1 m_error_message_mask |= 0x02; // disable v1 EndRead3dmChunk() partially read chunk warning break; } bEndRead3dmChunk_rc = EndRead3dmChunk(); if ( !bEndRead3dmChunk_rc ) break; } m_error_message_mask = saved_error_message_mask; if ( bHaveMat ) { if ( attributes ) attributes->m_material_index = m_3dm_v1_material_index; if ( material ) material->SetIndex(m_3dm_v1_material_index); m_3dm_v1_material_index++; } return rc; } int ON_BinaryArchive::Read3dmV1Material( ON_Material** ppMaterial ) { int rc = 0; // returns -1: failure // 0: end of material table // 1: success ON_Material material; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; bool bHaveMat; bool bEndReadChunk_rc; // reads NURBS, point, and mesh objects while( 0 == rc ) { bHaveMat = false; rc = 0; tcode = 0; big_value = 0; if ( !BeginRead3dmBigChunk(&tcode,&big_value) ) { // assume we are at the end of the file break; } switch(tcode) { case TCODE_RH_POINT: // v1 3d point { ON_3DM_BIG_CHUNK* point_chunk = m_chunk.Last(); ON__UINT64 pos0 = 0; if ( 0 != point_chunk && TCODE_RH_POINT == point_chunk->m_typecode && 0 == point_chunk->m_big_value ) { // Some V1 files have TCODE_RH_POINT chunks with length=0. // (It appears that points with arrow xdata have this problem.) // For these chunks we need to set the chunk length so EndRead3dmChunk() // will keep going. pos0 = CurrentPosition(); } else point_chunk = 0; ON_3dPoint pt; // need to read point to get to material defn bool bOK = ReadPoint( pt ); if ( bOK ) bOK = Read3dmV1AttributesOrMaterial( nullptr, &material, bHaveMat, TCODE_ENDOFTABLE ); if ( !bOK ) rc = -1; // else if appropriate, rc will get set to +1 below. if ( bOK && 0 != point_chunk && point_chunk == m_chunk.Last() && TCODE_RH_POINT == point_chunk->m_typecode && 0 == point_chunk->m_big_value ) { // set the chunk length so that reading can continue. ON__UINT64 pos1 = CurrentPosition(); ON__UINT64 chunk_length = (pos1 > pos0) ? (pos1 - pos0) : 0; if ( chunk_length >= 32 && chunk_length < 0x0FFFFFFF ) point_chunk->m_big_value = (ON__INT64)chunk_length; } } break; case TCODE_MESH_OBJECT: // v1 mesh { ON__UINT32 tc = 0; ON__INT64 i64 = 0; if ( !PeekAt3dmBigChunkType( &tc, &i64 ) ) break; if ( tc != TCODE_COMPRESSED_MESH_GEOMETRY ) break; // skip over the TCODE_COMPRESSED_MESH_GEOMETRY chunk if ( !BeginRead3dmBigChunk(&tc,&i64) ) break; if ( !EndRead3dmChunk() ) break; // attributes and material informtion follow the TCODE_COMPRESSED_MESH_GEOMETRY chunk if ( !Read3dmV1AttributesOrMaterial( nullptr, &material, bHaveMat, TCODE_ENDOFTABLE ) ) rc = -1; // if appropriate, rc will get set to +1 below } break; case TCODE_LEGACY_SHL: // v1 polysurface if ( !Read3dmV1AttributesOrMaterial( nullptr, &material, bHaveMat, TCODE_LEGACY_SHLSTUFF ) ) rc = -1; // if appropriate, rc will get set to +1 below break; case TCODE_LEGACY_FAC: // v1 trimmed surface if ( !Read3dmV1AttributesOrMaterial( nullptr, &material, bHaveMat, TCODE_LEGACY_FACSTUFF ) ) rc = -1; // if appropriate, rc will get set to +1 below break; case TCODE_LEGACY_CRV: // v1 curve if ( !Read3dmV1AttributesOrMaterial( nullptr, &material, bHaveMat, TCODE_LEGACY_CRVSTUFF ) ) rc = -1; // if appropriate, rc will get set to +1 below break; case TCODE_RHINOIO_OBJECT_NURBS_CURVE: case TCODE_RHINOIO_OBJECT_NURBS_SURFACE: case TCODE_RHINOIO_OBJECT_BREP: // old Rhino I/O toolkit nurbs curve, surface, and breps { ON__UINT32 tc = 0; ON__INT64 i64 = 0; if ( !PeekAt3dmBigChunkType( &tc, &i64 ) ) break; if ( tc != TCODE_RHINOIO_OBJECT_DATA ) break; // skip over the TCODE_RHINOIO_OBJECT_DATA chunk if ( !BeginRead3dmBigChunk(&tc,&i64) ) break; if ( !EndRead3dmChunk() ) break; if ( !Read3dmV1AttributesOrMaterial( nullptr, &material, bHaveMat, TCODE_RHINOIO_OBJECT_END ) ) rc = -1; // if appropriate, rc will get set to +1 below } break; } const unsigned int saved_error_message_mask = m_error_message_mask; m_error_message_mask |= 0x02; // disable v1 EndRead3dmChunk() partially read chunk warning bEndReadChunk_rc = EndRead3dmChunk(); m_error_message_mask = saved_error_message_mask; // enable v1 EndRead3dmChunk() partially read chunk warning if (!bEndReadChunk_rc ) { rc = -1; break; } if ( bHaveMat && ppMaterial) { Internal_Increment3dmTableItemCount(); // found a valid non-default material *ppMaterial = new ON_Material(material); (*ppMaterial)->SetId(); // V1 materials did not have ids rc = 1; break; } } return rc; } int ON_BinaryArchive::Read3dmMaterial( ON_Material** ppMaterial ) { if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::material_table, (void**)ppMaterial)) return 0; int rc; ON_Material* material = nullptr; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; if ( m_3dm_version == 1 ) { ON_Material* V1_material = nullptr; rc = ON_BinaryArchive::Read3dmV1Material( &V1_material ); if (nullptr != V1_material) { if ( V1_material->IdIsNil() ) V1_material->SetId(); Internal_Read3dmUpdateManifest(*V1_material); if (ppMaterial) *ppMaterial = V1_material; else delete V1_material; } } else { // version 2+ rc = -1; if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) { if ( tcode == TCODE_MATERIAL_RECORD ) { Internal_Increment3dmTableItemCount(); ON_Object* p = 0; if ( ReadObject( &p ) ) { material = ON_Material::Cast(p); if ( !material ) delete p; else { if ( material->IdIsNil() ) material->SetId(); Internal_Read3dmUpdateManifest(*material); if ( ppMaterial ) *ppMaterial = material; rc = 1; } } if (!material) { ON_ERROR("ON_BinaryArchive::Read3dmMaterial() - corrupt material table"); } } else if ( tcode == TCODE_ENDOFTABLE ) { // end of material table rc = 0; } else { ON_ERROR("ON_BinaryArchive::Read3dmMaterial() - corrupt material table"); } if ( !EndRead3dmChunk() ) rc = -1; } } return rc; } bool ON_BinaryArchive::EndRead3dmMaterialTable() { return EndRead3dmTable( TCODE_MATERIAL_TABLE ); } bool ON_BinaryArchive::BeginWrite3dmLightTable() { return BeginWrite3dmTable( TCODE_LIGHT_TABLE ); } bool ON_BinaryArchive::Write3dmModelLightComponent( const ON_ModelComponentReference& model_component_reference ) { return Write3dmModelLightComponent(ON_ModelGeometryComponent::Cast(model_component_reference.ModelComponent())); } bool ON_BinaryArchive::Write3dmModelLightComponent( const ON_ModelGeometryComponent* model_light ) { bool rc = false; for (;;) { if (nullptr == model_light) { ON_ERROR("model_light parameter is nullptr."); break; } const ON_Light* light = ON_Light::Cast(model_light->Geometry(nullptr)); if (nullptr == light) { ON_ERROR("model_light parameter is empty."); break; } rc = Write3dmLight(*light, model_light->Attributes(nullptr) ); break; } return rc; } bool ON_BinaryArchive::Write3dmLight( const ON_Light& light, const ON_3dmObjectAttributes* attributes ) { if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::light_table)) return true; if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::light_table) ) return false; Internal_Increment3dmTableItemCount(); bool rc = false; const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c && c->m_typecode == TCODE_LIGHT_TABLE ) { rc = BeginWrite3dmChunk( TCODE_LIGHT_RECORD, 0 ); if (rc) { // Because render lights have an index (unused), the manifest item has to be set before writing the light. Internal_Write3dmLightOrGeometryUpdateManifest(ON_ModelComponent::Type::RenderLight, light.m_light_id, light.m_light_index, light.m_light_name); rc = WriteObject( light ); if (rc) // optional TCODE_LIGHT_RECORD_ATTRIBUTES chunk if ( rc && attributes ) { rc = BeginWrite3dmChunk( TCODE_LIGHT_RECORD_ATTRIBUTES, 0 ); if (rc) { rc = attributes->Write( *this )?true:false; if (!EndWrite3dmChunk()) rc = false; if( rc && Archive3dmVersion() >= 4 && ObjectHasUserDataToWrite(attributes) ) { // 14 May 2008 Dale Lear // Added support for saving light attribute userdata rc = BeginWrite3dmChunk( TCODE_LIGHT_RECORD_ATTRIBUTES_USERDATA, 0 ); if (rc) { // write user data rc = WriteObjectUserData(*attributes); if (rc) { // Because I'm not using Write3dmObject() to write // the attributes, the user data must be immediately // followed by a short TCODE_OPENNURBS_CLASS_END chunk // in order for ReadObjectUserData() to work correctly. // // The reason that this is hacked in is that V3 files did // not support attribute user data and doing it this way // means that V3 can still read V4 files. rc = BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_END,0); if (rc) { if (!EndWrite3dmChunk()) rc = false; } } if (!EndWrite3dmChunk()) rc = false; } } } } // TCODE_LIGHT_RECORD_END chunk marks end of light record if ( BeginWrite3dmChunk( TCODE_LIGHT_RECORD_END, 0 ) ) { if (!EndWrite3dmChunk()) rc = false; } else { rc = false; } if ( !EndWrite3dmChunk() ) // end of TCODE_LIGHT_RECORD rc = false; } } else { ON_ERROR("ON_BinaryArchive::Write3dmMaterial() - active chunk typecode != TCODE_LIGHT_TABLE"); } return rc; } bool ON_BinaryArchive::EndWrite3dmLightTable() { return EndWrite3dmTable( TCODE_LIGHT_TABLE ); } bool ON_BinaryArchive::BeginRead3dmLightTable() { return BeginRead3dmTable( TCODE_LIGHT_TABLE ); } int ON_BinaryArchive::Read3dmV1Light( // returns 0 at end of light table // 1 light successfully read // -1 if file is corrupt ON_Light** ppLight, // light returned here ON_3dmObjectAttributes* pAttributes// optional - if NOT nullptr, object attributes are // returned here ) { bool bHaveMat; ON_Material material; // TODO - read v1 lights if ( m_chunk.Count() != 0 ) { ON_ERROR("ON_BinaryArchive::Read3dmV1Light() m_chunk.Count() != 0"); return false; } bool rc = false; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; // find TCODE_RH_SPOTLIGHT chunk for(;;) { if ( !BeginRead3dmBigChunk(&tcode,&big_value) ) break; // assume we are at the end of the file if ( tcode == TCODE_RH_SPOTLIGHT ) { rc = 1; break; } if ( !EndRead3dmChunk() ) break; } if (rc) { ON_3dPoint origin; ON_3dVector xaxis, yaxis; double radius; double height; double hotspot; for(;;) { rc = ReadPoint( origin ); if (!rc) break; rc = ReadVector( xaxis ); if (!rc) break; rc = ReadVector( yaxis ); if (!rc) break; rc = ReadDouble( &radius ); if (!rc) break; rc = ReadDouble( &height ); if (!rc) break; rc = ReadDouble( &hotspot ); if (!rc) break; if (ppLight ) { ON_3dVector Z = ON_CrossProduct( xaxis, yaxis ); ON_3dPoint location = height*Z + origin; ON_3dVector direction = -Z; if( height > 0.0) direction *= height; ON_Light* light = new ON_Light; light->SetStyle( ON::world_spot_light ); light->SetLocation(location); light->SetDirection(direction); light->SetSpotExponent( 64.0); if( radius > 0.0 && height > 0.0 ) light->SetSpotAngleRadians( atan( radius/height)); *ppLight = light; } break; } if (rc && ppLight && *ppLight) { Internal_Increment3dmTableItemCount(); bHaveMat = false; Read3dmV1AttributesOrMaterial(pAttributes,&material,bHaveMat,TCODE_ENDOFTABLE); if ( pAttributes ) pAttributes->m_material_index = -1; if (bHaveMat) (*ppLight)->SetDiffuse(material.Diffuse()); } if ( !EndRead3dmChunk() ) // end of TCODE_RH_SPOTLIGHT chunk rc = false; } return rc; } int ON_BinaryArchive::Read3dmModelLight( class ON_ModelGeometryComponent** model_light ) { if ( nullptr != model_light ) *model_light = nullptr; ON_Light* light = nullptr; ON_3dmObjectAttributes* attributes = new ON_3dmObjectAttributes(); int rc = Read3dmLight(&light, attributes); if (1 == rc && nullptr != light) { attributes->m_uuid = light->m_light_id; attributes->m_name = light->m_light_name; ON_ModelGeometryComponent* p = ON_ModelGeometryComponent::CreateManaged(light,attributes,nullptr); if (nullptr != p) { p->SetIndex(light->m_light_index); p->SetId(light->m_light_id); p->SetName(light->m_light_name); *model_light = p; } } else { delete light; delete attributes; } return rc; } int ON_BinaryArchive::Read3dmLight( ON_Light** ppLight, ON_3dmObjectAttributes* attributes ) { if ( attributes ) attributes->Default(); if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::light_table, (void**)ppLight)) return 0; int rc = -1; if ( m_3dm_version == 1 ) { rc = Read3dmV1Light( ppLight, attributes ); } else { ON_Light* light = nullptr; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; if ( BeginRead3dmBigChunk( &tcode, &big_value ) ) { if ( tcode == TCODE_LIGHT_RECORD ) { Internal_Increment3dmTableItemCount(); ON_Object* p = 0; if ( ReadObject( &p ) ) { light = ON_Light::Cast(p); if ( !light ) delete p; } if (!light) { ON_ERROR("ON_BinaryArchive::Read3dmLight() - corrupt light table"); } else { *ppLight = light; rc = 1; } } else if ( tcode != TCODE_ENDOFTABLE ) { ON_ERROR("ON_BinaryArchive::Read3dmLight() - corrupt light table"); } else rc = 0; while(rc==1) { tcode = 0; big_value = 0; if (!BeginRead3dmBigChunk( &tcode, &big_value )) { rc = -1; break; } if ( tcode == TCODE_LIGHT_RECORD_ATTRIBUTES && attributes ) { if ( !attributes->Read( *this ) ) rc = -1; } else if ( tcode == TCODE_LIGHT_RECORD_ATTRIBUTES_USERDATA ) { if ( 0 != attributes ) { // 14 May 2008 // Added support for reading user data on light attributes if ( !ReadObjectUserData(*attributes)) rc = -1; } } if ( !EndRead3dmChunk() ) { rc = -1; break; } if ( tcode == TCODE_LIGHT_RECORD_END ) break; } EndRead3dmChunk(); } } if ( nullptr != ppLight && nullptr != (*ppLight) ) { // If file is an older version with no id, then create one. if (ON_nil_uuid == (*ppLight)->m_light_id) { if (nullptr != attributes) { if (ON_nil_uuid == attributes->m_uuid) { attributes->m_uuid = ON_CreateId(); } (*ppLight)->m_light_id = attributes->m_uuid; } else (*ppLight)->m_light_id = ON_CreateId(); } Internal_Read3dmLightOrGeometryUpdateManifest( ON_ModelComponent::Type::RenderLight, (*ppLight)->m_light_id, (*ppLight)->m_light_index, (*ppLight)->m_light_name ); } return rc; } bool ON_BinaryArchive::EndRead3dmLightTable() { return EndRead3dmTable( TCODE_LIGHT_TABLE ); } bool ON_BinaryArchive::BeginWrite3dmObjectTable() { return BeginWrite3dmTable( TCODE_OBJECT_TABLE ); } bool ON_BinaryArchive::Write3dmModelGeometryComponent( const class ON_ModelComponentReference& model_component_reference ) { return Write3dmModelGeometryComponent(ON_ModelGeometryComponent::Cast(model_component_reference.ModelComponent())); } bool ON_BinaryArchive::Write3dmModelGeometryComponent( const class ON_ModelGeometryComponent* model_geometry ) { bool rc = false; for (;;) { if (nullptr == model_geometry) { ON_ERROR("model_geometry parameter is nullptr."); break; } const ON_Geometry* geometry = model_geometry->Geometry(nullptr); if (nullptr == geometry) { ON_ERROR("model_light parameter is empty."); break; } rc = Write3dmObject(*geometry, model_geometry->Attributes(nullptr) ); break; } return rc; } bool ON_BinaryArchive::Write3dmObject( const ON_Object& object, const ON_3dmObjectAttributes* attributes ) { if ( false == Internal_Begin3dmTableRecord(ON_3dmArchiveTableType::object_table) ) return false; Internal_Increment3dmTableItemCount(); bool rc = false; if ( Archive3dmVersion() <= 2 && object.ObjectType() == ON::pointset_object ) { // There were no point clouds in V1 and V2 files and we cannot handle // this problem inside of ON_PointCloud::Write() because we have to // write multiple point objects. (c.f. ON_Brep::Write()). const ON_PointCloud* pc = ON_PointCloud::Cast(&object); if ( 0 != pc ) { int i, count = pc->PointCount(); rc = true; for ( i = 0; i < count && rc ; i++ ) { ON_Point pt( pc->m_P[i] ); rc = Write3dmObject( pt, attributes ); } return rc; } } m_annotation_context.SetViewContext( (nullptr != attributes) ? attributes->m_space : ON_3dmAnnotationContext::Default.ViewContext() ); const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c && c->m_typecode == TCODE_OBJECT_TABLE ) { Flush(); rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD, 0 ); if (rc) { // TCODE_OBJECT_RECORD_TYPE chunk integer value that can be used // for skipping unwanted types of objects rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD_TYPE, object.ObjectType() ); if (rc) { if (!EndWrite3dmChunk()) rc = false; } // WriteObject writes TCODE_OPENNURBS_CLASS chunk that contains object definition rc = WriteObject( object ); // optional TCODE_OBJECT_RECORD_ATTRIBUTES chunk if ( rc && nullptr != attributes ) { rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD_ATTRIBUTES, 0 ); if (rc) { rc = attributes->Write( *this )?true:false; if (rc && ON_nil_uuid != attributes->m_uuid) Internal_Write3dmLightOrGeometryUpdateManifest(ON_ModelComponent::Type::ModelGeometry, attributes->m_uuid, ON_UNSET_INT_INDEX, ON_wString::EmptyString); if (!EndWrite3dmChunk()) rc = false; if( rc && Archive3dmVersion() >= 4 && 0 != attributes->FirstUserData() && ObjectHasUserDataToWrite(attributes) ) { // 19 October 2004 // Added support for saving user data on object attributes rc = BeginWrite3dmChunk( TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA, 0 ); if (rc) { // write user data rc = WriteObjectUserData(*attributes); if (rc) { // Because I'm not using Write3dmObject() to write // the attributes, the user data must be immediately // followed by a short TCODE_OPENNURBS_CLASS_END chunk // in order for ReadObjectUserData() to work correctly. // // The reason that this is hacked in is that V3 files did // not support attribute user data and doing it this way // means that V3 can still read V4 files. rc = BeginWrite3dmChunk(TCODE_OPENNURBS_CLASS_END,0); if (rc) { if (!EndWrite3dmChunk()) rc = false; } } if (!EndWrite3dmChunk()) rc = false; } } } } // TCODE_OBJECT_RECORD_END chunk marks end of object record if ( BeginWrite3dmChunk( TCODE_OBJECT_RECORD_END, 0 ) ) { if (!EndWrite3dmChunk()) rc = false; } else { rc = false; } if (!EndWrite3dmChunk()) // end of TCODE_OBJECT_RECORD { rc = false; } if (!Flush()) rc = false; } else { ON_ERROR("ON_BinaryArchive::Write3dmObject() - active chunk typecode != TCODE_OBJECT_TABLE"); } } m_annotation_context.SetViewContext( ON_3dmAnnotationContext::Default.ViewContext() ); return rc; } bool ON_BinaryArchive::EndWrite3dmObjectTable() { return EndWrite3dmTable( TCODE_OBJECT_TABLE ); } bool ON_BinaryArchive::BeginRead3dmObjectTable() { m_3dm_v1_material_index = 0; return BeginRead3dmTable( TCODE_OBJECT_TABLE ); } bool ON_BinaryArchive::ReadV1_TCODE_RH_POINT( ON_Object** ppObject, ON_3dmObjectAttributes* pAttributes ) { ON__UINT64 pos0 = 0; ON_3DM_BIG_CHUNK* point_chunk = m_chunk.Last(); if ( 0 != point_chunk && TCODE_RH_POINT == point_chunk->m_typecode && 0 == point_chunk->m_big_value ) { // Some early V1 files have TCODE_RH_POINT chunks with arrow xdata // attached have a length set to zero. // For these chunks we need to set the chunk length so EndRead3dmChunk() // will keep going. pos0 = CurrentPosition(); } else point_chunk = 0; // read v1 point bool rc = false; bool bHaveMat = false; ON_3dPoint pt; ON__3dmV1_XDATA xdata; rc = ReadPoint(pt); if (rc) { rc = Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_ENDOFTABLE,&xdata); // do switch even if Read3dmV1AttributesOrMaterial() fails switch ( xdata.m_type ) { case ON__3dmV1_XDATA::arrow_direction: if ( xdata.m_vector.Length() > ON_ZERO_TOLERANCE ) { ON_OBSOLETE_V2_AnnotationArrow* arrow = new ON_OBSOLETE_V2_AnnotationArrow(); arrow->m_tail = pt; arrow->m_head = pt + xdata.m_vector; *ppObject = arrow; } else { *ppObject = new ON_Point(pt); } break; case ON__3dmV1_XDATA::dot_text: { ON_OBSOLETE_V2_TextDot* dot = new ON_OBSOLETE_V2_TextDot(); dot->point = pt; dot->m_text = xdata.m_string; if ( dot->m_text.IsEmpty() ) dot->m_text = " "; // a single blank *ppObject = dot; } break; default: *ppObject = new ON_Point(pt); break; } } // carefully test for the V1 zero length chunk bug if ( rc && pos0 > 0 && 0 != point_chunk && point_chunk == m_chunk.Last() ) { if ( TCODE_RH_POINT == point_chunk->m_typecode && 0 == point_chunk->m_big_value ) { // This TCODE_RH_POINT chunk has the zero length chunk bug // that was in some V1 files. // Fill in the correct chunk length so that reading can continue. ON__UINT64 pos1 = CurrentPosition(); ON__UINT64 chunk_length = (pos1 > pos0) ? (pos1 - pos0) : 0; if ( chunk_length >= 32 && chunk_length < 0x0FFFFFFF ) point_chunk->m_big_value = (ON__INT64)chunk_length; } } return rc; } static void TweakAnnotationPlane( ON_Plane& plane ) { // 10 November 2003 Dale Lear // Fixed lots of bugs in annotation plane tweaking. // Before the fix this block of code was cut-n-pasted // in three places. The fabs() calls were wrong. In addition // and the // .x values where tested and then the .y/.z values were set. // if( fabs( plane.origin.x > 1e10 )) // plane.origin.x = 0.0; // if( fabs( plane.origin.y > 1e10 )) // plane.origin.y = 0.0; // if( fabs( plane.origin.z > 1e10 )) // plane.origin.z = 0.0; // // if( fabs( plane.xaxis.x > 1e10 )) // plane.xaxis.x = 1.0; // if( fabs( plane.xaxis.x > 1e10 )) // plane.xaxis.y = 0.0; // if( fabs( plane.xaxis.x > 1e10 )) // plane.xaxis.z = 0.0; // // if( fabs( plane.yaxis.x > 1e10 )) // plane.yaxis.x = 0.0; // if( fabs( plane.yaxis.x > 1e10 )) // plane.yaxis.y = 1.0; // if( fabs( plane.yaxis.x > 1e10 )) // plane.yaxis.z = 0.0; // Lowell has decided that annotation plane // coordinates bigger than "too_big" should be // set to zero. const double too_big = 1.0e10; if( fabs( plane.origin.x ) > too_big ) plane.origin.x = 0.0; if( fabs( plane.origin.y ) > too_big ) plane.origin.y = 0.0; if( fabs( plane.origin.z ) > too_big ) plane.origin.z = 0.0; if( fabs( plane.xaxis.x ) > too_big ) plane.xaxis.x = 1.0; if( fabs( plane.xaxis.y ) > too_big ) plane.xaxis.y = 0.0; if( fabs( plane.xaxis.z ) > too_big ) plane.xaxis.z = 0.0; if( fabs( plane.yaxis.x ) > too_big ) plane.yaxis.x = 0.0; if( fabs( plane.yaxis.y ) > too_big ) plane.yaxis.y = 1.0; if( fabs( plane.yaxis.z ) > too_big ) plane.yaxis.z = 0.0; plane.xaxis.Unitize(); plane.yaxis.Unitize(); plane.zaxis = ON_CrossProduct(plane.xaxis,plane.yaxis); plane.zaxis.Unitize(); plane.UpdateEquation(); } #define RHINO_ANNOTATION_SETTINGS_VERSION_1 1 #define RHINO_LINEAR_DIMENSION_VERSION_1 1 #define RHINO_RADIAL_DIMENSION_VERSION_1 1 #define RHINO_ANGULAR_DIMENSION_VERSION_1 1 #define RHINO_TEXT_BLOCK_VERSION_1 1 #define RHINO_TEXT_BLOCK_VERSION_2 2 #define RHINO_ANNOTATION_LEADER_VERSION_1 1 #define BUFLEN 128 static bool ReadV1_TCODE_ANNOTATION_Helper( ON_BinaryArchive& archive, char* buffer, ON_wString& tc ) { char* cp = 0; int j = 0; if( !archive.ReadInt( &j)) return false; size_t sz = (j+1)*sizeof(cp[0]); if( j > BUFLEN - 1 || !buffer ) { cp = (char*)onmalloc( sz ); if( !cp) return false; } else { cp = buffer; } memset( cp, 0, sz ); if( !archive.ReadChar( j, cp)) { if ( cp != buffer ) onfree(cp); return false; } cp[j] = 0; tc = cp; if ( cp != buffer ) onfree( cp ); return true; } bool ON_BinaryArchive::ReadV1_TCODE_ANNOTATION( unsigned int tcode, ON_Object** ppObject, ON_3dmObjectAttributes* pAttributes ) { enum RhAnnotationType { Nothing = 0, TextBlock = 1, DimHorizontal = 2, DimVertical = 3, DimAligned = 4, DimRotated = 5, DimAngular = 6, DimDiameter = 7 , DimRadius = 8, Leader = 9, DimLinear = 10, }; bool rc = false; *ppObject = nullptr; ON_wString tc; char buffer[BUFLEN]; int i, j, k, byobject, version; //char* cp; double d, d4[4]; //ON_3dPoint pt; switch( tcode) { case TCODE_TEXT_BLOCK: { // read the version id rc = ReadInt( &version); if ( rc && (version == RHINO_TEXT_BLOCK_VERSION_1 || version == RHINO_TEXT_BLOCK_VERSION_2) ) { //this is a version we can read.... // this is a type flag that we throw away rc = ReadInt( &i); if( !rc) return rc; ON_OBSOLETE_V2_TextObject* text = new ON_OBSOLETE_V2_TextObject; text->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtTextBlock); ON_Plane plane; // the entity plane if( !ReadDouble( 3, &plane.origin.x)) return false; if( !ReadDouble( 3, &plane.xaxis.x)) return false; if( !ReadDouble( 3, &plane.yaxis.x)) return false; // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane() TweakAnnotationPlane( plane ); text->SetPlane( plane); // read string to display if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) return false; text->SetUserText( tc.Array()); // flags ( future ) if( !ReadInt( 1, &j)) return false; // settings byobject flag if( !ReadInt( 1, &byobject)) return false; // depending on the value of byobject, more fields might be read here // facename if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) return false; text->SetFaceName(tc); // face weight if( !ReadInt( 1, &j)) return false; text->SetFontWeight( j); if( !ReadDouble( 1, &d)) return false; text->SetHeight( d); // 2 extra doubles were written into the file by mistake in version 1 if( version == RHINO_TEXT_BLOCK_VERSION_1 ) { if( !ReadDouble( 1, &d)) return false; if( !ReadDouble( 1, &d)) return false; } if( text->UserText().Length() < 1 ) { *ppObject = 0; return true; } *ppObject = text; rc = true; } } break; case TCODE_ANNOTATION_LEADER: { // read the version id if( !ReadInt( &i)) return false; if( i == RHINO_ANNOTATION_LEADER_VERSION_1) { // redundant type code to throw away if( !ReadInt( &i)) return false; ON_OBSOLETE_V2_Leader* ldr = new ON_OBSOLETE_V2_Leader; ldr->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtLeader); ON_Plane plane; // the entity plane if( !ReadDouble( 3, &plane.origin.x)) return false; if( !ReadDouble( 3, &plane.xaxis.x)) return false; if( !ReadDouble( 3, &plane.yaxis.x)) return false; // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane() TweakAnnotationPlane( plane ); ldr->SetPlane( plane); // flags ( future ) if( !ReadInt( 1, &j)) return false; // settings byobject flag if( !ReadInt( 1, &byobject)) return false; // number of points to read if( !ReadInt( &k)) return false; if( k < 2) return true; ON_SimpleArray points; for( j = 0; j < k; j++ ) { double pt[3]; if( !ReadDouble( 3, pt)) return false; points.Append( ON_2dPoint( pt)); } ldr->SetPoints( points); *ppObject = ldr; rc = true; break; } } break; case TCODE_LINEAR_DIMENSION: { // read the version id if( !ReadInt( &i)) return false; if( i == RHINO_LINEAR_DIMENSION_VERSION_1) { if( !ReadInt( &i)) return false; ON_OBSOLETE_V2_DimLinear* dim = new ON_OBSOLETE_V2_DimLinear; switch( i ) { case DimHorizontal: case DimVertical: case DimRotated: case DimLinear: dim->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimLinear); break; default: dim->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAligned); break; } ON_Plane plane; // the entity plane if( !ReadDouble( 3, &plane.origin.x)) return false; if( !ReadDouble( 3, &plane.xaxis.x)) return false; if( !ReadDouble( 3, &plane.yaxis.x)) return false; // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane() TweakAnnotationPlane( plane ); dim->SetPlane( plane); // definition points in coordinates of entity plane ON_SimpleArray points; for( j = 0; j < 11; j++ ) { double pt[3]; if( !ReadDouble( 3, pt)) return false; points.Append( ON_2dPoint( pt)); } dim->SetPoints( points); // read user text string if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) return false; dim->SetUserText( tc.Array()); // Set the symbols used in dimension strings to the selected options // SetStringSymbols(); // read string to display if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) return false; dim->SetDefaultText( tc.Array()); // user positioned text flag if( !ReadInt( &j)) return false; dim->SetUserPositionedText( j); // flags ( future ) if( !ReadInt( 1, &j)) return false; // settings byobject flag if( !ReadInt( 1, &byobject)) return false; *ppObject = dim; rc = true; break; } } break; case TCODE_ANGULAR_DIMENSION: { // read the version id if( !ReadInt( &i)) return false; if( i == RHINO_ANGULAR_DIMENSION_VERSION_1) { if( !ReadInt( &i)) return false; ON_OBSOLETE_V2_DimAngular* dim = new ON_OBSOLETE_V2_DimAngular; dim->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimAngular); ON_Plane plane; // the entity plane if( !ReadDouble( 3, &plane.origin.x)) return false; if( !ReadDouble( 3, &plane.xaxis.x)) return false; if( !ReadDouble( 3, &plane.yaxis.x)) return false; // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane() TweakAnnotationPlane( plane ); dim->SetPlane( plane); if( !ReadDouble( &d)) return false; dim->SetAngle( d); if( !ReadDouble( &d)) return false; dim->SetRadius( d); // distances from apes to start and end of dimensioned lines - not used if( !ReadDouble( 4, d4)) return false; // definition points in coordinates of entity plane ON_SimpleArray points; for( j = 0; j < 5; j++ ) { double pt[3]; if( !ReadDouble( 3, pt)) return false; points.Append( ON_2dPoint( pt)); } dim->SetPoints( points); // read user text string if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) return false; dim->SetUserText( tc.Array()); // Set the symbols used in dimension strings to the selected options // SetStringSymbols(); // read string to display if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) return false; dim->SetDefaultText( tc.Array()); // user positioned text flag if( !ReadInt( &j)) return false; dim->SetUserPositionedText( j); // flags ( future ) if( !ReadInt( 1, &j)) return false; // settings byobject flag if( !ReadInt( 1, &byobject)) return false; *ppObject = dim; rc = true; break; } } break; case TCODE_RADIAL_DIMENSION: { // read the version id if( !ReadInt( &i)) return false; if( i == RHINO_RADIAL_DIMENSION_VERSION_1) { if( !ReadInt( &i)) return false; ON_OBSOLETE_V2_DimRadial* dim = new ON_OBSOLETE_V2_DimRadial; switch( i) { case DimDiameter: dim->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimDiameter); break; case DimRadius: dim->SetType( ON_INTERNAL_OBSOLETE::V5_eAnnotationType::dtDimRadius); break; } ON_Plane plane; // the entity plane if( !ReadDouble( 3, &plane.origin.x)) return false; if( !ReadDouble( 3, &plane.xaxis.x)) return false; if( !ReadDouble( 3, &plane.yaxis.x)) return false; // 11 November 2003 Dale Lear - see comments in TweakAnnotationPlane() TweakAnnotationPlane( plane ); dim->SetPlane( plane); // definition points in coordinates of entity plane ON_SimpleArray points; for( j = 0; j < 5; j++ ) { double pt[3]; if( !ReadDouble( 3, pt)) return false; points.Append( ON_2dPoint( pt)); } dim->SetPoints( points); // read user text string if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) return false; dim->SetUserText( tc.Array()); // Set the symbols used in dimension strings to the selected options // SetStringSymbols(); // read string to display if ( !ReadV1_TCODE_ANNOTATION_Helper( *this, buffer, tc ) ) return false; dim->SetDefaultText( tc.Array()); // user positioned text flag if( !ReadInt( &j)) return false; dim->SetUserPositionedText( j); // flags ( future ) if( !ReadInt( 1, &j)) return false; // settings byobject flag if( !ReadInt( 1, &byobject)) return false; *ppObject = dim; rc = true; break; } } break; default: // some unknown type to skip over return true; } //switch if( rc) { bool bHaveMat = false; Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_ENDOFTABLE); } return rc; // TODO: fill in this function // input tcode returned *ppObject points to // TCODE_TEXT_BLOCK: ON_OBSOLETE_V2_TextObject // TCODE_ANNOTATION_LEADER: ON_OBSOLETE_V2_Leader // TCODE_LINEAR_DIMENSION: ON_OBSOLETE_V2_DimLinear // TCODE_ANGULAR_DIMENSION: ON_OBSOLETE_V2_DimAngular // TCODE_RADIAL_DIMENSION: ON_OBSOLETE_V2_DimRadial //return true if successful and false for failure. } bool ON_BinaryArchive::ReadV1_TCODE_MESH_OBJECT( ON_Object** ppObject, ON_3dmObjectAttributes* pAttributes ) { ON_Mesh* mesh = 0; bool rc = false; // read v1 mesh ON__UINT32 tcode = 0; ON__INT64 big_value = 0; int i; if ( !BeginRead3dmBigChunk(&tcode,&big_value) ) return false; if ( tcode == TCODE_COMPRESSED_MESH_GEOMETRY ) for(;;) { int point_count = 0; int face_count = 0; int boolHasVertexNormalsAsInt = false; int boolHasTexCoordsAsInt = false; ON_BoundingBox bbox; if (!ReadInt(&point_count) ) break; if ( point_count <= 0 ) break; if (!ReadInt(&face_count) ) break; if ( face_count <= 0 ) break; if (!ReadInt(&boolHasVertexNormalsAsInt) ) break; if (!ReadInt(&boolHasTexCoordsAsInt) ) break; if ( !ReadPoint(bbox.m_min) ) break; if ( !ReadPoint(bbox.m_max) ) break; const bool bHasVertexNormals = boolHasVertexNormalsAsInt ? true : false; const bool bHasTexCoords = boolHasTexCoordsAsInt ? true : false; mesh = new ON_Mesh(face_count, point_count, bHasVertexNormals, bHasTexCoords ); // read 3d vertex locations { ON_3dVector d = bbox.Diagonal(); double dx = d.x / 65535.0; double dy = d.y / 65535.0; double dz = d.z / 65535.0; unsigned short xyz[3]; ON_3fPoint pt; for ( i = 0; i < point_count; i++ ) { if ( !ReadShort(3,xyz) ) break; pt.x = (float)(dx*xyz[0] + bbox.m_min.x); pt.y = (float)(dy*xyz[1] + bbox.m_min.y); pt.z = (float)(dz*xyz[2] + bbox.m_min.z); mesh->m_V.Append(pt); } } if ( mesh->m_V.Count() != point_count ) break; // read triangle/quadrangle faces if ( point_count < 65535 ) { unsigned short abcd[4]; for ( i = 0; i < face_count; i++ ) { if ( !ReadShort(4,abcd) ) break; ON_MeshFace& f = mesh->m_F.AppendNew(); f.vi[0] = abcd[0]; f.vi[1] = abcd[1]; f.vi[2] = abcd[2]; f.vi[3] = abcd[3]; } } else { int abcd[4]; for ( i = 0; i < face_count; i++ ) { if ( !ReadInt(4,abcd) ) break; ON_MeshFace& f = mesh->m_F.AppendNew(); f.vi[0] = abcd[0]; f.vi[1] = abcd[1]; f.vi[2] = abcd[2]; f.vi[3] = abcd[3]; } } if ( mesh->m_F.Count() != face_count ) break; if ( bHasVertexNormals ) { char xyz[3]; ON_3fVector normal; for ( i = 0; i < point_count; i++ ) { if ( !ReadChar(3,xyz) ) break; normal.x = (float)(((signed char)xyz[0])/127.0); normal.y = (float)(((signed char)xyz[1])/127.0); normal.z = (float)(((signed char)xyz[2])/127.0); mesh->m_N.Append(normal); } if ( mesh->m_N.Count() != mesh->m_V.Count() ) break; } if ( bHasTexCoords ) { unsigned short uv[2]; ON_2fPoint t; for ( i = 0; i < point_count; i++ ) { if ( !ReadShort(2,uv) ) break; t.x = (float)(uv[0]/65535.0); t.y = (float)(uv[1]/65535.0); mesh->m_T.Append(t); } if ( mesh->m_T.Count() != mesh->m_V.Count() ) break; } rc = true; break; } if ( !EndRead3dmChunk() ) rc = false; if ( rc && mesh ) { *ppObject = mesh; } else { if ( mesh ) delete mesh; rc = false; } if ( rc && mesh ) { // attributes and material information follows the TCODE_COMPRESSED_MESH_GEOMETRY chunk; bool bHaveMat = false; Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_ENDOFTABLE); } return rc; } static bool BeginRead3dmLEGACYSTUFF( ON_BinaryArchive& file, unsigned int stuff_tcode ) { // begins reading stuff chunk bool rc = false; ON__UINT32 tcode = !stuff_tcode; ON__INT64 big_value = 0; for (;;) { if ( !file.BeginRead3dmBigChunk(&tcode,&big_value) ) break; if ( tcode == stuff_tcode ) { rc = true; break; } if ( !file.EndRead3dmChunk() ) break; } return rc; } static ON_NurbsCurve* ReadV1_TCODE_LEGACY_SPLSTUFF( ON_BinaryArchive& file ) { // reads contents of a v1 TCODE_LEGACY_SPLSTUFF chunk ON_NurbsCurve* pNurbsCurve = 0; int i, dim, is_rat, order, cv_count, is_closed, form; ON_BoundingBox bbox; char c; // read v1 agspline chunk if ( !file.ReadChar(1,&c) ) return nullptr; if ( c != 2 && c != 3 ) return nullptr; dim = c; if ( !file.ReadChar(1,&c) ) return nullptr; if ( c != 0 && c != 1 && c != 2 ) return nullptr; is_rat = c; // 0 = no, 1 = euclidean, 2 = homogeneous if ( !file.ReadChar(1,&c) ) return nullptr; if ( c < 2 ) return nullptr; order = c; { // 5 February 2003 - An single case of a V1 file // with a spline that had cv_count = 54467 (>32767) // exists. Changing from a signed short to // an unsigned short fixed the problem. // The ui casting stuff is here to keep all // the various compilers/lints happy and to // make sure the short with the high bit set // gets properly converted to a positive cv_count. unsigned short s; if ( !file.ReadShort(1,&s) ) return nullptr; unsigned int ui = s; cv_count = (int)ui; if ( cv_count < order ) return nullptr; } // The "is_closed" and "form" flags are here to recording // the values of legacy data found in the Rhino file. These // values are not used in the toolkit code. if ( !file.ReadByte(1,&c) ) return nullptr; if (c != 0 && c != 1 && c != 2) return nullptr; is_closed = c; // 0 = open, 1 = closed, 2 = periodic if ( !file.ReadByte(1,&c) ) return nullptr; form = c; // read bounding box if ( !file.ReadDouble( dim, bbox.m_min ) ) return nullptr; if ( !file.ReadDouble( dim, bbox.m_max ) ) return nullptr; pNurbsCurve = new ON_NurbsCurve( dim, is_rat?true:false, order, cv_count ); bool rc = false; for(;;) { // read legacy v1 knot vector const int knot_count = order+cv_count-2; int knot_index = 0; double knot; // clamped_end_knot_flag: 0 = none, 1 = left, 2 = right, 3 = both char clamped_end_knot_flag = 0; if ( order > 2 ) file.ReadChar(1,&clamped_end_knot_flag); // first knot(s) if ( !file.ReadDouble(&knot) ) break; pNurbsCurve->m_knot[knot_index++] = knot; if (clamped_end_knot_flag % 2) { // clamped_start_knot while ( knot_index <= order-2 ) pNurbsCurve->m_knot[knot_index++] = knot; } // middle knots while ( knot_index <= cv_count-1 ) { if ( !file.ReadDouble(&knot) ) break; pNurbsCurve->m_knot[knot_index++] = knot; } if ( knot_index <= cv_count-1 ) break; // end knot(s) if ( clamped_end_knot_flag >= 2 ) { while ( knot_index < knot_count ) pNurbsCurve->m_knot[knot_index++] = knot; } else { while ( knot_index < knot_count ) { if ( !file.ReadDouble(&knot) ) break; pNurbsCurve->m_knot[knot_index++] = knot; } if ( knot_index < knot_count ) break; } // read legacy v1 control points const int cvdim = ( is_rat ) ? dim+1 : dim; for ( i = 0; i < cv_count; i++ ) { if ( !file.ReadDouble( cvdim, pNurbsCurve->CV(i) ) ) break; } if ( i < cv_count ) break; if ( is_rat ) { // is_rat == 1 check fails because invalid is_rat flags in old files // convert rational CVs from euclidean to homogeneous double w, *cv; int cv_index; for ( cv_index = 0; cv_index < cv_count; cv_index++ ) { cv = pNurbsCurve->CV(cv_index); w = cv[dim]; for ( i = 0; i < dim; i++ ) cv[i] *= w; } } if ( order == 2 && cv_count == 2 && pNurbsCurve->m_knot[0] > pNurbsCurve->m_knot[1] ) { // a few isolated old v1 3DM file created by Rhino 1.0 files have lines with reversed knots. pNurbsCurve->m_knot[0] = -pNurbsCurve->m_knot[0]; pNurbsCurve->m_knot[1] = -pNurbsCurve->m_knot[1]; } rc = true; break; } if ( !rc && pNurbsCurve ) { delete pNurbsCurve; pNurbsCurve = 0; } return pNurbsCurve; } static bool ReadV1_TCODE_LEGACY_SPL( ON_BinaryArchive& file, ON_NurbsCurve*& pNurbsCurve ) { // reads contents of TCODE_LEGACY_SPL chunk pNurbsCurve = 0; bool rc = BeginRead3dmLEGACYSTUFF(file, TCODE_LEGACY_SPLSTUFF ); if ( !rc ) return false; pNurbsCurve = ReadV1_TCODE_LEGACY_SPLSTUFF(file); if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_SPLSTUFF chunk rc = false; if ( !pNurbsCurve ) rc = false; return rc; } static ON_NurbsSurface* ReadV1_TCODE_LEGACY_SRFSTUFF( ON_BinaryArchive& file ) { // reads contents of TCODE_LEGACY_SRFSTUFF chunk ON_NurbsSurface* pNurbsSurface = 0; int i, j, dim=0, is_rat=0, order[2], cv_count[2], is_closed[2], is_singular[2], form; ON_BoundingBox bbox; char c; // read contents of v1 TCODE_LEGACY_SRFSTUFF chunk if ( !file.ReadChar(1,&c) ) return nullptr; if ( c != 2 && c != 3 ) return nullptr; dim = c; if ( !file.ReadByte(1,&c) ) return nullptr; form = c; if ( !file.ReadChar(1,&c) ) return nullptr; if ( c < 1 ) return nullptr; order[0] = c+1; if ( !file.ReadChar(1,&c) ) return nullptr; if ( c < 1 ) return nullptr; order[1] = c+1; { // 5 February 2003 - An single case of a V1 files // See the comment above in ReadV1_TCODE_LEGACY_SPLSTUFF // concerning the spline with cv_count >= 0x8000. // The analogous fix is here for the surface case. unsigned short s; if ( !file.ReadShort(1,&s) ) return nullptr; if ( s < 1 ) return nullptr; unsigned int ui = s; cv_count[0] = order[0]-1+((int)ui); if ( !file.ReadShort(1,&s) ) return nullptr; if ( s < 1 ) return nullptr; ui = s; cv_count[1] = order[1]-1+((int)ui); } // "ratu" 0 = no, 1 = euclidean, 2 = homogeneous if ( !file.ReadChar(1,&c) ) return nullptr; if ( c == 1 ) is_rat = 1; else if ( c == 2 ) is_rat = 2; // "ratv" 0 = no, 1 = euclidean, 2 = homogeneous if ( !file.ReadChar(1,&c) ) return nullptr; if ( c == 1 ) is_rat = 1; else if ( c == 2 ) is_rat = 2; // The "is_closed" and "is_singular" flags are here to recording // the values of legacy data found in the Rhino file. These // values are not used in the toolkit code. if ( !file.ReadByte(1,&c) ) return nullptr; if (c != 0 && c != 1 && c != 2) return nullptr; is_closed[0] = c; // 0 = open, 1 = closed, 2 = periodic if ( !file.ReadByte(1,&c) ) return nullptr; if (c != 0 && c != 1 && c != 2) return nullptr; is_closed[1] = c; // 0 = open, 1 = closed, 2 = periodic if ( !file.ReadByte(1,&c) ) return nullptr; if (c != 0 && c != 1 && c != 2 && c != 3) return nullptr; is_singular[0] = c; if ( !file.ReadByte(1,&c) ) return nullptr; if (c != 0 && c != 1 && c != 2 && c != 3) return nullptr; is_singular[1] = c; // read bounding box if ( !file.ReadDouble( dim, bbox.m_min ) ) return nullptr; if ( !file.ReadDouble( dim, bbox.m_max ) ) return nullptr; pNurbsSurface = new ON_NurbsSurface( dim, is_rat?true:false, order[0], order[1], cv_count[0], cv_count[1] ); bool rc = false; for (;;) { // read legacy v1 knot vectors if ( !file.ReadDouble( order[0]+cv_count[0]-2, pNurbsSurface->m_knot[0] ) ) break; if ( !file.ReadDouble( order[1]+cv_count[1]-2, pNurbsSurface->m_knot[1] ) ) break; // read legacy v1 control points const int cvdim = ( is_rat ) ? dim+1 : dim; for ( i = 0; i < cv_count[0]; i++ ) { for ( j = 0; j < cv_count[1]; j++ ) { if ( !file.ReadDouble( cvdim, pNurbsSurface->CV(i,j) ) ) break; } if ( j < cv_count[1] ) break; } if ( i < cv_count[0] ) break; if ( is_rat == 1 ) { double w, *cv; int k; for ( i = 0; i < cv_count[0]; i++ ) for ( j = 0; j < cv_count[1]; j++ ) { cv = pNurbsSurface->CV(i,j); w = cv[dim]; for ( k = 0; k < dim; k++ ) cv[k] *= w; } } rc = true; break; } if ( !rc ) { delete pNurbsSurface; pNurbsSurface = 0; } return pNurbsSurface; } static bool ReadV1_TCODE_LEGACY_SRF( ON_BinaryArchive& file, ON_NurbsSurface*& pNurbsSurface ) { pNurbsSurface = 0; bool rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_SRF ); if ( rc ) { rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_SRFSTUFF ); if ( rc ) { pNurbsSurface = ReadV1_TCODE_LEGACY_SRFSTUFF( file ); if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_SRFSTUFF chunk rc = false; } if ( !file.EndRead3dmChunk() ) rc = false; // end of TCODE_LEGACY_SRF chunk } if ( !rc && pNurbsSurface ) { delete pNurbsSurface; pNurbsSurface = 0; } return rc; } ON_Curve* ReadV1_TCODE_LEGACY_CRVSTUFF( ON_BinaryArchive& file ) { // reads contents of a v1 TCODE_LEGACY_CRVSTUFF chunk ON_Curve* curve = 0; ON_PolyCurve* polycurve = 0; ON_NurbsCurve* segment = 0; bool rc = false; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; int i; bool bIsPolyline = false; ON_BoundingBox bbox; for (;;) { char c; short s; int segment_count = 0; file.ReadChar(1,&c); // fix RH-40959 // Microsoft Visual C 15.3.3 release build fails to set dim (works in debug builds) //if ( c != 2 && c != 3 ) // break; //int dim = c; // this works int dim; if (2 == c) dim = 2; else if (3 == c) dim = 3; else break; file.ReadChar(1,&c); if ( c != -1 && c != 0 && c != 1 && c != 2 ) break; //int is_closed = (c) ? 1 : 0; file.ReadShort(&s); if ( s < 1 ) break; file.ReadDouble( dim, bbox.m_min); file.ReadDouble( dim, bbox.m_max); segment_count = s; for ( i = 0; i < segment_count; i++ ) { segment = 0; tcode = 0; big_value = 0; if ( !file.BeginRead3dmBigChunk( &tcode, &big_value ) ) break; if ( tcode == TCODE_LEGACY_SPL && big_value > 0 ) { ReadV1_TCODE_LEGACY_SPL(file,segment); } if ( !file.EndRead3dmChunk() ) { if ( segment ) { delete segment; segment = 0; } break; } if ( !segment ) break; if ( i == 0 ) polycurve = new ON_PolyCurve(segment_count); if ( segment->CVCount() > 2 || segment->Order() != 2 || segment->IsRational() ) { if ( segment->Order() != 2 || segment->IsRational() ) bIsPolyline = false; polycurve->Append(segment); } else { ON_LineCurve* line = new ON_LineCurve(); line->m_t.Set( segment->m_knot[0], segment->m_knot[1] ); segment->GetCV( 0, line->m_line.from ); segment->GetCV( 1, line->m_line.to ); line->m_dim = segment->m_dim; delete segment; segment = 0; polycurve->Append(line); } } // 5 February 2003 // The check for a nullptr polycurve was added to avoid // crashes in files when the first NURBS curve in the // polycurve could not be read. if ( 0 == polycurve ) break; if ( polycurve->Count() != segment_count ) break; rc = true; break; } if ( rc && polycurve ) { if ( polycurve->Count() == 1 ) { curve = polycurve->HarvestSegment(0); delete polycurve; } else if ( bIsPolyline ) { ON_PolylineCurve* pline = new ON_PolylineCurve(); pline->m_dim = polycurve->Dimension(); pline->m_t.Reserve(polycurve->Count()+1); pline->m_t.SetCount(polycurve->Count()+1); polycurve->GetSpanVector( pline->m_t.Array() ); pline->m_pline.Reserve(polycurve->Count()+1); for ( i = 0; i < polycurve->Count(); i++ ) { pline->m_pline.Append(polycurve->SegmentCurve(i)->PointAtStart()); } pline->m_pline.Append(polycurve->SegmentCurve(polycurve->Count()-1)->PointAtEnd()); curve = pline; delete polycurve; } else { curve = polycurve; } } else { if ( polycurve ) delete polycurve; rc = false; } return curve; } bool ON_Brep::ReadV1_LegacyTrimStuff( ON_BinaryArchive& file, ON_BrepFace&, // face - formal parameter intentionally ignored ON_BrepLoop& loop ) { // read contents of TCODE_LEGACY_TRMSTUFF chunk bool rc = false; int revedge, gcon, mono; int curve2d_index = -1, curve3d_index = -1, trim_index = -1; double tol_3d, tol_2d; ON_Curve* curve2d = nullptr; ON_Curve* curve3d = nullptr; char c; file.ReadChar( &c ); bool bHasEdge = (c % 2 ); // bit 0 = 1 if "tedge" has non nullptr "->edge" bool bHasMate = (c & 6 ); // bit 1 or 2 = 1 if "tedge" has non nullptr "->twin" bool bIsSeam = (c & 2 ); // bit 1 = 1 if "tedge->twin" belongs to same face if ( !file.ReadInt(&revedge) ) return false; if ( !file.ReadInt(&gcon) ) return false; if ( !file.ReadInt(&mono) ) return false; if ( !file.ReadDouble( &tol_3d ) ) return false; if ( !file.ReadDouble( &tol_2d ) ) return false; // 2d trim curve if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRV ) ) return false; if ( BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRVSTUFF ) ) { curve2d = ReadV1_TCODE_LEGACY_CRVSTUFF(file); if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRVSTUFF chunk rc = false; } if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRV chunk rc = false; if ( !curve2d ) return false; curve2d_index = AddTrimCurve(curve2d); if ( curve2d_index < 0 ) { delete curve2d; return false; } // 3d curve if ( bHasEdge ) { if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRV ) ) return false; if ( BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_CRVSTUFF ) ) { curve3d = ReadV1_TCODE_LEGACY_CRVSTUFF(file); if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRVSTUFF chunk rc = false; } if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_CRV chunk rc = false; if ( !curve3d ) return false; curve3d_index = AddEdgeCurve(curve3d); if ( curve3d_index < 0 ) { delete curve3d; return false; } ON_BrepEdge& edge = NewEdge(curve3d_index); ON_BrepTrim& trim = NewTrim( edge, revedge ? true : false, loop, curve2d_index ); trim_index = trim.m_trim_index; } else { ON_BrepTrim& trim = NewTrim( revedge ? true : false, loop, curve2d_index ); trim_index = trim.m_trim_index; } if ( trim_index >= 0 ) { ON_BrepTrim& trim = m_T[trim_index]; trim.m__legacy_2d_tol = tol_2d; trim.m__legacy_3d_tol = tol_3d; trim.m__legacy_flags_Set(gcon,mono); if ( bIsSeam ) { trim.m_type = ON_BrepTrim::seam; } else if ( bHasMate ) { trim.m_type = ON_BrepTrim::mated; } else if ( bHasEdge ) { trim.m_type = ON_BrepTrim::boundary; } else { trim.m_type = ON_BrepTrim::singular; } } return (trim_index>=0) ? true : false; } bool ON_Brep::ReadV1_LegacyTrim( ON_BinaryArchive& file, ON_BrepFace& face, ON_BrepLoop& loop ) { bool rc = false; if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_TRM ) ) return false; rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_TRMSTUFF ); if ( rc ) { rc = ReadV1_LegacyTrimStuff( file, face, loop ); if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_TRMSTUFF rc = false; } if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_TRM chunk rc = false; return rc; } bool ON_Brep::ReadV1_LegacyLoopStuff( ON_BinaryArchive& file, ON_BrepFace& face ) { // reads contents of TCODE_LEGACY_BNDSTUFF chunk // read boundary ON_BrepLoop::TYPE loop_type = ON_BrepLoop::unknown; int tedge_count, btype, lti; double pspace_box[2][2]; // parameter space bounding box if ( !file.ReadInt( &tedge_count ) ) return false; if ( tedge_count < 1 ) { return false; } if ( !file.ReadInt( &btype ) ) return false; if ( btype < -1 || btype > 1 ) { return false; } if ( !file.ReadDouble( 4, &pspace_box[0][0] ) ) return false; switch( btype ) { case -1: loop_type = ON_BrepLoop::slit; break; case 0: loop_type = ON_BrepLoop::outer; break; case 1: loop_type = ON_BrepLoop::inner; break; } ON_BrepLoop& loop = NewLoop( loop_type, face ); for ( lti = 0; lti < tedge_count; lti++ ) { if ( !ReadV1_LegacyTrim( file, face, loop ) ) return false; } return true; } bool ON_Brep::ReadV1_LegacyLoop( ON_BinaryArchive& file, ON_BrepFace& face ) { bool rc = false; if ( !BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_BND ) ) return false; rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_BNDSTUFF ); if ( rc ) { rc = ReadV1_LegacyLoopStuff( file, face ); if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_BNDSTUFF rc = false; } if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_BND chunk rc = false; return rc; } bool ON_Brep::ReadV1_LegacyFaceStuff( ON_BinaryArchive& file ) { // reads contents of TCODE_LEGACY_FACSTUFF chunk ON_NurbsSurface* surface = 0; ON_Workspace ws; int flipnorm = 0; int ftype = 0; int bndcnt = 0; int twincnt = 0; bool bHasOuter = false; ON_BoundingBox face_bbox; int ti0 = m_T.Count(); bool rc = false; // read flags if ( !file.ReadInt(&flipnorm) ) return false; if ( flipnorm < 0 || flipnorm > 1 ) return false; if ( !file.ReadInt(&ftype) ) return false; if ( !file.ReadInt(&bndcnt) ) return false; bHasOuter = (bndcnt%2); // always true in v1 files bndcnt /= 2; // read bounding box if ( !file.ReadDouble( 3, face_bbox.m_min ) ) return false; if ( !file.ReadDouble( 3, face_bbox.m_max ) ) return false; // B-rep edge gluing info if ( !file.ReadInt(&twincnt) ) return false; short* glue = (twincnt > 0 ) ? (short*)ws.GetMemory(twincnt*sizeof(*glue)) : nullptr; if (twincnt > 0) { if ( !file.ReadShort(twincnt,glue) ) return false; } // read surface if ( !ReadV1_TCODE_LEGACY_SRF( file, surface ) ) return false; if ( !surface ) return false; const int srf_index = AddSurface(surface); // create face ON_BrepFace& face = NewFace(srf_index); face.m_bRev = (flipnorm) ? true : false; face.m_li.Reserve(bndcnt); // read boundary loops int loop_index = -1; if ( !bHasOuter ) { // TODO: cook up outer boundary loop (never happes with v1 files) face.m_li.Append(loop_index); } int bi; rc = true; for ( bi = 0; rc && bi < bndcnt; bi++ ) { rc = ReadV1_LegacyLoop( file, face ); } if ( twincnt > 0 ) { // twincnt = number of seams edges in face // glue[] = order 2 permutation of {0,....,twincnt-1} // set seam_i[] = m_T[] indices of seam trims int si, ti; const int ti1 = m_T.Count(); int* seam_i = (int*)ws.GetMemory(twincnt*sizeof(*seam_i)); for ( ti = ti0, si = 0; ti < ti1 && si < twincnt; ti++ ) { if (m_T[ti].m_type != ON_BrepTrim::seam ) continue; seam_i[si++] = ti; } if ( si == twincnt ) { // glue seams for ( si = 0; si < twincnt; si++ ) { if ( glue[si] >= 0 && glue[si] < twincnt ) { const int i0 = seam_i[si]; const int i1 = seam_i[glue[si]]; // m_T[i0] and m_T[i1] use the same edge; const int ei0 = m_T[i0].m_ei; const int ei1 = m_T[i1].m_ei; if ( ei0 == -1 && ei1 >= 0 ) { m_T[i0].m_ei = ei1; m_E[ei1].m_ti.Append(i0); } else if ( ei1 == -1 && ei0 >= 0 ) { m_T[i1].m_ei = ei0; m_E[ei0].m_ti.Append(i1); } } } } } return rc; } bool ON_Brep::ReadV1_LegacyShellStuff( ON_BinaryArchive& file ) { // read contents of TCODE_LEGACY_SHLSTUFF chunk ON_Workspace ws; int outer = 0; int facecnt = 0; int twincnt = 0; ON_BoundingBox shell_bbox; const int ti0 = m_T.Count(); /* read flags */ file.ReadInt(&outer); file.ReadInt(&facecnt); // read bounding box file.ReadPoint( shell_bbox.m_min ); file.ReadPoint( shell_bbox.m_max ); /* B-rep edge gluing info */ file.ReadInt(&twincnt); short* glue = (twincnt > 0 ) ? (short*)ws.GetMemory(twincnt*sizeof(*glue)) : nullptr; if (twincnt > 0) file.ReadShort(twincnt,glue); bool rc = true; int fi; for ( fi = 0; rc && fi < facecnt; fi++ ) { rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_FAC ); if ( rc ) { rc = BeginRead3dmLEGACYSTUFF( file, TCODE_LEGACY_FACSTUFF ); if ( rc ) { rc = ReadV1_LegacyFaceStuff( file ); if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_FACSTUFF chunk rc = false; } if ( !file.EndRead3dmChunk() ) // end of TCODE_LEGACY_FAC chunk rc = false; } } if ( twincnt > 0 ) { // twincnt = number of shared (inter-face) edges // glue[] = order 2 permutation of {0,....,twincnt-1} // set share_i[] = m_T[] indices of shared trims int si, ti; const int ti1 = m_T.Count(); int* share_i = (int*)ws.GetMemory(twincnt*sizeof(*share_i)); for ( ti = ti0, si = 0; ti < ti1 && si < twincnt; ti++ ) { if (m_T[ti].m_type != ON_BrepTrim::mated ) continue; share_i[si++] = ti; } if ( si == twincnt ) { // glue seams for ( si = 0; si < twincnt; si++ ) { if ( glue[si] >= 0 && glue[si] < twincnt ) { const int i0 = share_i[si]; const int i1 = share_i[glue[si]]; // m_T[i0] and m_T[i1] use the same edge; const int ei0 = m_T[i0].m_ei; const int ei1 = m_T[i1].m_ei; if ( ei0 == -1 && ei1 >= 0 ) { m_T[i0].m_ei = ei1; m_E[ei1].m_ti.Append(i0); } else if ( ei1 == -1 && ei0 >= 0 ) { m_T[i1].m_ei = ei0; m_E[ei0].m_ti.Append(i1); } } } } } return rc; } bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_CRV( ON_Object** ppObject, ON_3dmObjectAttributes* pAttributes ) { ON_Curve* curve = nullptr; bool rc = false; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; bool bHaveMat = false; Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_LEGACY_CRVSTUFF); if ( !BeginRead3dmBigChunk( &tcode, &big_value ) ) return false; if ( tcode == TCODE_LEGACY_CRVSTUFF ) curve = ReadV1_TCODE_LEGACY_CRVSTUFF(*this); rc = EndRead3dmChunk(); if ( !curve ) rc = false; else *ppObject = curve; return rc; } bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_FAC( ON_Object** ppObject, ON_3dmObjectAttributes* pAttributes ) { // read V1 TCODE_LEGACY_FAC chunk bool bHaveMat = false; if ( !Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_LEGACY_FACSTUFF) ) return false; if ( !BeginRead3dmLEGACYSTUFF( *this, TCODE_LEGACY_FACSTUFF ) ) return false; ON_Brep* brep = new ON_Brep(); bool rc = brep->ReadV1_LegacyFaceStuff( *this ); if ( !EndRead3dmChunk() ) // end of TCODE_LEGACY_FACSTUFF chunk rc = false; if ( !rc ) { delete brep; } else { brep->SetVertices(); brep->SetTrimIsoFlags(); brep->SetTolsFromLegacyValues(); *ppObject = brep; } return rc; } bool ON_BinaryArchive::ReadV1_TCODE_LEGACY_SHL( ON_Object** ppObject, ON_3dmObjectAttributes* pAttributes ) { // read v1 TCODE_LEGACY_SHL chunk bool bHaveMat = false; if ( !Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_LEGACY_SHLSTUFF) ) return false; if ( !BeginRead3dmLEGACYSTUFF( *this, TCODE_LEGACY_SHLSTUFF ) ) return false; ON_Brep* brep = new ON_Brep(); bool rc = brep->ReadV1_LegacyShellStuff( *this ); if ( !EndRead3dmChunk() ) // end of TCODE_LEGACY_SHLSTUFF chunk rc = false; if ( !rc ) { delete brep; } else { brep->SetVertices(); brep->SetTrimIsoFlags(); brep->SetTolsFromLegacyValues(); *ppObject = brep; } return rc; } static ON_NurbsCurve* ReadV1_RHINOIO_NURBS_CURVE_OBJECT_DATA( ON_BinaryArchive& file ) { // read TCODE_RHINOIO_OBJECT_DATA chunk that is contained in a // TCODE_RHINOIO_OBJECT_NURBS_CURVE chunk. The TCODE_RHINOIO_OBJECT_DATA // chunk contains the definition of NURBS curves written by the // old RhinoIO toolkit. ON_NurbsCurve* curve = 0; bool rc = false; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; int version, dim, is_rat, order, cv_count, flag, i; if ( !file.BeginRead3dmBigChunk( &tcode, &big_value ) ) return nullptr; if ( tcode == TCODE_RHINOIO_OBJECT_DATA ) for (;;) { if ( !file.ReadInt(&version) ) break; // int bReverse = version & 0x100; version &= 0xFFFFFEFF; if ( version != 100 && version != 101 ) break; file.ReadInt(&dim); if ( dim < 1 ) break; file.ReadInt(&is_rat); if ( is_rat < 0 || is_rat > 1 ) break; file.ReadInt(&order); if ( order < 2 ) break; file.ReadInt(&cv_count); if ( cv_count < order ) break; file.ReadInt(&flag); if ( flag != 0 ) break; curve = new ON_NurbsCurve(dim,is_rat,order,cv_count); if ( !file.ReadDouble( order+cv_count-2, curve->m_knot ) ) break; int cvdim = is_rat ? dim+1 : dim; for ( i = 0; i < cv_count; i++ ) { if ( !file.ReadDouble( cvdim, curve->CV(i) ) ) break; } if ( i < cv_count ) break; rc = true; break; } if ( !file.EndRead3dmChunk() ) // end of TCODE_RHINOIO_OBJECT_DATA chunk rc = false; if ( !rc && curve ) { delete curve; curve = 0; } return curve; } static ON_NurbsSurface* ReadV1_RHINOIO_NURBS_SURFACE_OBJECT_DATA( ON_BinaryArchive& file ) { // read TCODE_RHINOIO_OBJECT_DATA chunk that is contained in a // TCODE_RHINOIO_OBJECT_NURBS_SURFACE chunk. The TCODE_RHINOIO_OBJECT_DATA // chunk contains the definition of NURBS surfaces written by the // old RhinoIO toolkit. bool rc = false; ON_NurbsSurface* surface = 0; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; int version, dim, is_rat, order[2], cv_count[2], flag, i, j; if ( !file.BeginRead3dmBigChunk( &tcode, &big_value ) ) return nullptr; if ( tcode == TCODE_RHINOIO_OBJECT_DATA ) for (;;) { if ( !file.ReadInt(&version) ) break; // int bReverse = version & 0x100; version &= 0xFFFFFEFF; if ( version != 100 && version != 101 ) break; file.ReadInt(&dim); if ( dim < 1 ) break; file.ReadInt(&is_rat); if ( is_rat < 0 || is_rat > 1 ) break; file.ReadInt(&order[0]); if ( order[0] < 2 ) break; file.ReadInt(&order[1]); if ( order[1] < 2 ) break; file.ReadInt(&cv_count[0]); if ( cv_count[0] < order[0] ) break; file.ReadInt(&cv_count[1]); if ( cv_count[1] < order[1] ) break; file.ReadInt(&flag); if ( flag != 0 ) break; surface = new ON_NurbsSurface(dim,is_rat,order[0],order[1],cv_count[0],cv_count[1]); if ( !file.ReadDouble( order[0]+cv_count[0]-2, surface->m_knot[0] ) ) break; if ( !file.ReadDouble( order[1]+cv_count[1]-2, surface->m_knot[1] ) ) break; int cvdim = is_rat ? dim+1 : dim; for ( i = 0; i < cv_count[0]; i++ ) { for ( j = 0; j < cv_count[1]; j++ ) { if ( !file.ReadDouble( cvdim, surface->CV(i,j) ) ) break; } if ( j < cv_count[1] ) break; } if ( i < cv_count[0] ) break; rc = true; break; } if ( !file.EndRead3dmChunk() ) // end of TCODE_RHINOIO_OBJECT_DATA rc = false; if ( !rc && surface ) { delete surface; surface = 0; } return surface; } bool ON_BinaryArchive::ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE( ON_Object** ppObject, ON_3dmObjectAttributes* pAttributes ) { // read contents of ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE chunk // written by v1 RhinoIO toolkit ON_NurbsCurve* curve = 0; bool rc = false; bool bHaveMat = false; // reads TCODE_RHINOIO_OBJECT_DATA header and nurbs curve definition curve = ReadV1_RHINOIO_NURBS_CURVE_OBJECT_DATA(*this); if ( curve ) { *ppObject = curve; rc = true; Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_RHINOIO_OBJECT_END); } return rc; } bool ON_BinaryArchive::ReadV1_TCODE_RHINOIO_OBJECT_NURBS_SURFACE( ON_Object** ppObject, ON_3dmObjectAttributes* pAttributes ) { // read contents of TCODE_RHINOIO_OBJECT_NURBS_SURFACE chunk // written by v1 RhinoIO toolkit bool bHaveMat = false; bool rc = false; ON_NurbsSurface* surface = 0; surface = ReadV1_RHINOIO_NURBS_SURFACE_OBJECT_DATA( *this ); if ( surface ) { *ppObject = surface; rc = true; Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_RHINOIO_OBJECT_END); } return rc; } static ON_Curve* ReadV1_RHINOIO_BREP_CURVE( ON_BinaryArchive& file ) { ON_Curve* curve = nullptr; ON_PolyCurve* pcurve = nullptr; ON_NurbsCurve* nurbs_curve = nullptr; int segment_index, segment_count = 0; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; if ( !file.ReadInt(&segment_count) ) return nullptr; if ( segment_count < 1 ) return nullptr; for ( segment_index = 0; segment_index < segment_count; segment_index++ ) { if ( !file.BeginRead3dmBigChunk(&tcode,&big_value) ) break; if ( tcode == TCODE_RHINOIO_OBJECT_NURBS_CURVE ) { nurbs_curve = ReadV1_RHINOIO_NURBS_CURVE_OBJECT_DATA( file ); } if ( !file.EndRead3dmChunk() ) break; if ( !nurbs_curve ) break; if ( segment_index == 0 ) { curve = nurbs_curve; nurbs_curve = 0; } else { if ( segment_index == 1 ) { pcurve = new ON_PolyCurve(); pcurve->Append(curve); curve = pcurve; } pcurve->Append(nurbs_curve); nurbs_curve = nullptr; } } if ( segment_index < segment_count ) { if ( nurbs_curve ) { delete nurbs_curve; nurbs_curve = 0; } if ( curve ) { delete curve; curve = 0; } } return curve; } bool ON_BinaryArchive::ReadV1_TCODE_RHINOIO_OBJECT_BREP( ON_Object** ppObject, ON_3dmObjectAttributes* pAttributes ) { ON_3dPoint m_oldTrim_mP[2]; bool bHaveMat = false; bool rc = false; ON_Brep* brep = 0; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; if ( !BeginRead3dmBigChunk( &tcode, &big_value ) ) return false; if ( tcode == TCODE_RHINOIO_OBJECT_DATA ) for (;;) { int version = -1; int sz, i, j; double tol2d, tol3d; if ( !ReadInt( &version ) ) break; // serialization version // version = 100 means the b-rep was written by the RhinoIO toolkit // version = 101 means the b-rep was written by Rhino 1.0 if ( version != 100 && version != 101 ) { return false; } brep = new ON_Brep(); // 2d trimming curves if ( !ReadInt( &sz ) ) break; if ( sz < 1 ) { break; } brep->m_C2.Reserve(sz); for ( i = 0; i < sz; i++ ) { ON_Curve* curve = ReadV1_RHINOIO_BREP_CURVE( *this ); if ( !curve ) break; brep->m_C2.Append(curve); } if ( i < sz ) break; // 3d trimming curves if ( !ReadInt( &sz ) ) break; if ( sz < 1 ) { break; } brep->m_C3.Reserve(sz); for ( i = 0; i < sz; i++ ) { ON_Curve* curve = ReadV1_RHINOIO_BREP_CURVE( *this ); if ( !curve ) break; brep->m_C3.Append(curve); } if ( i < sz ) break; // 3d untrimmed surfaces if ( !ReadInt( &sz ) ) break; if ( sz < 1 ) { break; } brep->m_S.Reserve(sz); for ( i = 0; i < sz; i++ ) { ON_NurbsSurface* surface = 0; tcode = 0; big_value = 0; if ( !BeginRead3dmBigChunk(&tcode,&big_value) ) break; if ( tcode == TCODE_RHINOIO_OBJECT_NURBS_SURFACE ) { surface = ReadV1_RHINOIO_NURBS_SURFACE_OBJECT_DATA( *this ); } if ( !EndRead3dmChunk() ) break; if ( !surface ) break; brep->m_S.Append(surface); } if ( i < sz ) break; // vertices ReadInt( &sz ); brep->m_V.Reserve(sz); for ( i = 0; i < sz; i++ ) { ON_BrepVertex& vertex = brep->NewVertex(); if ( !ReadInt( &vertex.m_vertex_index ) ) break; if ( !ReadPoint( vertex.point ) ) break; if ( !ReadArray( vertex.m_ei ) ) break; if ( !ReadDouble( &vertex.m_tolerance ) ) break; } if ( i < sz ) break; // edges ReadInt( &sz ); brep->m_E.Reserve(sz); for ( i = 0; i < sz; i++ ) { ON_Interval proxy_domain; ON_BrepEdge& edge = brep->NewEdge(); if ( !ReadInt( &edge.m_edge_index ) ) break; if ( !ReadInt( &edge.m_c3i ) ) break; if ( !ReadInterval( proxy_domain ) ) break; edge.SetProxyCurveDomain(proxy_domain); if ( !ReadInt( 2, edge.m_vi ) ) break; if ( !ReadArray( edge.m_ti ) ) break; if ( !ReadDouble( &edge.m_tolerance ) ) break; } if ( i < sz ) break; // trims ReadInt( &sz ); brep->m_T.Reserve(sz); for ( i = 0; i < sz; i++ ) { ON_BrepTrim& trim = brep->NewTrim(); if ( !ReadInt( &trim.m_trim_index ) ) break; if ( !ReadInt( &trim.m_c2i ) ) break; ON_Interval d; if ( !ReadInterval( d ) ) break; trim.SetProxyCurve(nullptr,d); if ( !ReadInt( &trim.m_ei ) ) break; if ( !ReadInt( 2, trim.m_vi ) ) break; j = trim.m_bRev3d; if ( !ReadInt( &j ) ) break; trim.m_bRev3d = (j!=0); if ( !ReadInt( &j ) ) break; switch(j) { case 1: trim.m_type = ON_BrepTrim::boundary; break; case 2: trim.m_type = ON_BrepTrim::mated; break; case 3: trim.m_type = ON_BrepTrim::seam; break; case 4: trim.m_type = ON_BrepTrim::singular; break; } if ( !ReadInt( &j ) ) break; // legacy iso flag - ignore and recaluate if ( !ReadInt( &trim.m_li ) ) break; if ( !ReadDouble( 2, trim.m_tolerance ) ) break; if ( !ReadPoint( m_oldTrim_mP[0] ) ) break; if ( !ReadPoint( m_oldTrim_mP[1] ) ) break; if ( !ReadDouble( &tol2d ) ) break; if ( !ReadDouble( &tol3d ) ) break; } if ( i < sz ) break; // loops ReadInt( &sz ); brep->m_L.Reserve(sz); for ( i = 0; i < sz; i++ ) { ON_BrepLoop& loop = brep->NewLoop(ON_BrepLoop::unknown); if ( !ReadInt( &loop.m_loop_index ) ) break; if ( !ReadArray( loop.m_ti ) ) break; if ( !ReadInt( &j ) ) break; switch (j) { case 1: loop.m_type = ON_BrepLoop::outer; break; case 2: loop.m_type = ON_BrepLoop::inner; break; case 3: loop.m_type = ON_BrepLoop::slit; break; } if ( !ReadInt( &loop.m_fi ) ) break; } if ( i < sz ) break; // faces ReadInt( &sz ); brep->m_F.Reserve(sz); for ( i = 0; i < sz; i++ ) { ON_BrepFace& face = brep->NewFace(); if ( !ReadInt( &face.m_face_index ) ) break; if ( !ReadArray( face.m_li ) ) break; if ( !ReadInt( &face.m_si ) ) break; int k = face.m_bRev; if ( !ReadInt( &k ) ) break; face.m_bRev = (k!=0); } if ( i < sz ) break; // bounding box { ON_BoundingBox bbox; if ( !ReadPoint( bbox.m_min ) ) break; if ( !ReadPoint( bbox.m_max ) ) break; } rc = true; break; } if ( !EndRead3dmChunk() ) rc = false; if ( rc && brep ) { brep->SetTrimIsoFlags(); *ppObject = brep; } else { if ( brep ) delete brep; rc = false; } if ( rc && brep ) { Read3dmV1AttributesOrMaterial(pAttributes,nullptr,bHaveMat,TCODE_RHINOIO_OBJECT_END); } return rc; } int ON_BinaryArchive::Read3dmV1Object( ON_Object** ppObject, // object is returned here ON_3dmObjectAttributes* pAttributes, // optional - object attributes unsigned int object_filter // optional filter made by or-ing object_type bits ) { int rc = 0; // returns -1: failure // 0: end of geometry table // 1: success // 2: skipped filtered objects ON__UINT32 tcode = 0; ON__INT64 big_value = 0; // reads NURBS, point, and mesh objects for (;;) { tcode = 0; big_value = 0; if (!BeginRead3dmBigChunk(&tcode, &big_value)) { rc = 0; // at the end of the file break; } switch (tcode) { case TCODE_TEXT_BLOCK: case TCODE_ANNOTATION_LEADER: case TCODE_LINEAR_DIMENSION: case TCODE_ANGULAR_DIMENSION: case TCODE_RADIAL_DIMENSION: if (0 != (ON::annotation_object & object_filter)) { if (ReadV1_TCODE_ANNOTATION(tcode, ppObject, pAttributes)) rc = 1; else rc = -1; } else { rc = 2; } break; case TCODE_RH_POINT: // v1 3d point if (0 != (ON::point_object & object_filter)) { if (ReadV1_TCODE_RH_POINT(ppObject, pAttributes)) rc = 1; else rc = -1; } else { rc = 2; } break; case TCODE_MESH_OBJECT: // v1 mesh if (0 != (ON::mesh_object & object_filter)) { if (ReadV1_TCODE_MESH_OBJECT(ppObject, pAttributes)) rc = 1; else rc = -1; } else { rc = 2; } break; case TCODE_LEGACY_SHL: // v1 polysurface if (0 != (ON::mesh_object & object_filter)) { if (ReadV1_TCODE_LEGACY_SHL(ppObject, pAttributes)) rc = 1; else rc = -1; } else { rc = 2; } break; case TCODE_LEGACY_FAC: // v1 trimmed surface if (0 != (ON::mesh_object & object_filter)) { if (ReadV1_TCODE_LEGACY_FAC(ppObject, pAttributes)) rc = 1; else rc = -1; } else { rc = 2; } break; case TCODE_LEGACY_CRV: // v1 curve if (0 != (ON::mesh_object & object_filter)) { if (ReadV1_TCODE_LEGACY_CRV(ppObject, pAttributes)) rc = 1; else rc = -1; } else { rc = 2; } break; case TCODE_RHINOIO_OBJECT_NURBS_CURVE: // old Rhino I/O toolkit nurbs curve if (0 != (ON::mesh_object & object_filter)) { if (ReadV1_TCODE_RHINOIO_OBJECT_NURBS_CURVE(ppObject, pAttributes)) rc = 1; else rc = -1; } else { rc = 2; } break; case TCODE_RHINOIO_OBJECT_NURBS_SURFACE: // old Rhino I/O toolkit nurbs surface if (0 != (ON::mesh_object & object_filter)) { if (ReadV1_TCODE_RHINOIO_OBJECT_NURBS_SURFACE(ppObject, pAttributes)) rc = 1; else rc = -1; } else { rc = 2; } break; case TCODE_RHINOIO_OBJECT_BREP: // old Rhino I/O toolkit nurbs brep if (0 != (ON::mesh_object & object_filter)) { if (ReadV1_TCODE_RHINOIO_OBJECT_BREP(ppObject, pAttributes)) rc = 1; else rc = -1; } else { rc = 2; } break; } if (!EndRead3dmChunk()) break; if (1 == rc && nullptr != pAttributes && ON_nil_uuid == pAttributes->m_uuid) pAttributes->m_uuid = ON_CreateId(); // v1 files did not have ids. if (rc == 1 || rc == -1) break; } return rc; } #if 1 class ON_OBSOLETE_CCustomMeshUserData : public ON_UserData { ON_OBJECT_DECLARE(ON_OBSOLETE_CCustomMeshUserData); public: ON_OBSOLETE_CCustomMeshUserData(); ~ON_OBSOLETE_CCustomMeshUserData(); bool GetDescription( ON_wString& ) override; bool Read(ON_BinaryArchive& binary_archive) override; bool m_bInUse; ON_MeshParameters m_mp; }; ON_OBJECT_IMPLEMENT(ON_OBSOLETE_CCustomMeshUserData, ON_UserData, "69F27695-3011-4FBA-82C1-E529F25B5FD9"); ON_OBSOLETE_CCustomMeshUserData::ON_OBSOLETE_CCustomMeshUserData() { m_userdata_copycount = 0; m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_CCustomMeshUserData); m_application_uuid = ON_nil_uuid; m_bInUse = false; } ON_OBSOLETE_CCustomMeshUserData::~ON_OBSOLETE_CCustomMeshUserData() { } bool ON_OBSOLETE_CCustomMeshUserData::Read(ON_BinaryArchive& ba) { int i = 0; if ( !ba.ReadInt( &i ) ) return false; if( !ba.ReadBool( &m_bInUse ) ) return false; return m_mp.Read( ba ); } bool ON_OBSOLETE_CCustomMeshUserData::GetDescription( ON_wString& s ) { s = "OBSOLETE CustomMeshUserData"; return true; } #endif int ON_BinaryArchive::Read3dmModelGeometry( class ON_ModelGeometryComponent** model_geometry, unsigned int object_filter ) { bool bManageGeometry = false; bool bManageAttributes = false; return Read3dmModelGeometryForExperts( bManageGeometry, bManageAttributes, model_geometry, object_filter ); } int ON_BinaryArchive::Read3dmModelGeometryForExperts( bool bManageGeometry, bool bManageAttributes, class ON_ModelGeometryComponent** model_geometry, unsigned int object_filter ) { if ( nullptr != model_geometry ) *model_geometry = nullptr; ON_Object* p = nullptr; ON_3dmObjectAttributes* attributes = new ON_3dmObjectAttributes(); int rc = Read3dmObject(&p, attributes, object_filter); ON_Geometry* geometry = ON_Geometry::Cast(p); if (1 == rc && nullptr != geometry) { *model_geometry = ON_ModelGeometryComponent::CreateForExperts(bManageGeometry,geometry,bManageAttributes,attributes,nullptr); } else { delete p; delete attributes; } return rc; } int ON_BinaryArchive::Read3dmObject( ON_Object** ppObject, // object is returned here ON_3dmObjectAttributes* pAttributes, // optional - object attributes unsigned int object_filter // optional filter made by or-ing object_type bits ) { if ( pAttributes ) pAttributes->Default(); if ( false == Read3dmTableRecord(ON_3dmArchiveTableType::object_table, (void**)ppObject)) return 0; if ( 0 == object_filter ) // default filter (0) reads every object object_filter = 0xFFFFFFFF; // returns -1: failure // 0: end of geometry table // 1: success // 2: skipped filtered objects // 3: skipped new object (object's class UUID wasn't found in class list) int rc = -1; if ( m_3dm_version == 1 ) { rc = Read3dmV1Object(ppObject,pAttributes,object_filter); } else { ON__UINT32 tcode = 0; ON__INT64 length_TCODE_OBJECT_RECORD = 0; ON__INT64 value_TCODE_OBJECT_RECORD_TYPE = 0; ON__INT64 length_TCODE_OBJECT_RECORD_ATTRIBUTES = 0; if ( BeginRead3dmBigChunk( &tcode, &length_TCODE_OBJECT_RECORD ) ) { if ( tcode == TCODE_OBJECT_RECORD ) { Internal_Increment3dmTableItemCount(); if (BeginRead3dmBigChunk( &tcode, &value_TCODE_OBJECT_RECORD_TYPE )) { if ( tcode != TCODE_OBJECT_RECORD_TYPE ) { rc = -1; ON_ERROR("ON_BinaryArchive::Read3dmObject() - missing TCODE_OBJECT_RECORD_TYPE chunk."); } else if ( 0 != value_TCODE_OBJECT_RECORD_TYPE && 0 == (value_TCODE_OBJECT_RECORD_TYPE & object_filter) ) rc = 2; // skip reading this object else rc = 1; // need to read this object if ( !EndRead3dmChunk() ) rc = -1; if ( 1 == rc ) { switch(ReadObject(ppObject)) { case 1: rc = 1; // successfully read this object break; case 3: rc = 3; // skipped object - assume it's just a newer object than this code reads break; default: rc = -1; // serious failure break; } } } else rc = -1; } else if ( tcode != TCODE_ENDOFTABLE ) { ON_ERROR("ON_BinaryArchive::Read3dmObject() - corrupt object table"); rc = -1; } else rc = 0; while(rc==1) { tcode = 0; if (!BeginRead3dmBigChunk( &tcode, &length_TCODE_OBJECT_RECORD_ATTRIBUTES )) { rc = -1; break; } if ( tcode == TCODE_OBJECT_RECORD_ATTRIBUTES ) { if ( 0 != pAttributes ) { if ( !pAttributes->Read( *this ) ) rc = -1; } } else if ( tcode == TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA ) { if ( 0 != pAttributes ) { // 19 October 2004 // Added support for saving user data on object attributes if ( !ReadObjectUserData(*pAttributes)) rc = -1; else { #if 1 // 3 March 2011 - convert obsolete user data ON_OBSOLETE_CCustomMeshUserData* ud = ON_OBSOLETE_CCustomMeshUserData::Cast(pAttributes->GetUserData(ON_CLASS_ID(ON_OBSOLETE_CCustomMeshUserData))); if ( ud ) { ud->m_mp.SetCustomSettingsEnabled(ud->m_bInUse); pAttributes->SetCustomRenderMeshParameters(ud->m_mp); delete ud; } #endif } } } if ( !EndRead3dmChunk() ) { rc = -1; } if ( tcode == TCODE_OBJECT_RECORD_END ) break; } if ( !EndRead3dmChunk() ) rc = -1; } } if ( 1 == rc && nullptr != ppObject && nullptr != *ppObject && nullptr != pAttributes ) { if (ON_nil_uuid == pAttributes->m_uuid) { // some older files are missing ids // Modern times require unique object ids. pAttributes->m_uuid = ON_CreateId(); } else if ( false == Manifest().IdIsAvailable(pAttributes->m_uuid) ) { // And some files contain objects with duplicate ids. ON_ERROR("pAttributes->m_uuid is in use. Assigning new id."); pAttributes->m_uuid = ON_CreateId(); } // In rare cases one object must be converted into another. // Examples include reading obsolete objects and converting them into their // current counterpart, converting WIP objects into a proxy for a commercial build, // and converting a proxy object into a WIP object for a WIP build. ON_Object* updated_object = Internal_ConvertObject(*ppObject, pAttributes); if (nullptr != updated_object && updated_object != *ppObject) { delete *ppObject; *ppObject = updated_object; } Internal_Read3dmLightOrGeometryUpdateManifest( ON_ModelComponent::Type::ModelGeometry, pAttributes->m_uuid, ON_UNSET_INT_INDEX, pAttributes->m_name ); } return rc; } bool ON_BinaryArchive::EndRead3dmObjectTable() { bool rc = EndRead3dmTable( TCODE_OBJECT_TABLE ); if ( 0 != m_V1_layer_list ) { struct ON__3dmV1LayerIndex* next = m_V1_layer_list; m_V1_layer_list = 0; for ( int i = 0; 0 != next && i < 1000; i++ ) { struct ON__3dmV1LayerIndex* p = next; next = p->m_next; onfree(p); } } return rc; } bool ON_BinaryArchive::BeginWrite3dmUserTable( const ON_UUID& usertable_uuid ) { return BeginWrite3dmUserTable(usertable_uuid, false, 0, 0 ); } bool ON_BinaryArchive::BeginWrite3dmUserTable( ON_UUID plugin_id, bool bSavingGoo, int goo_3dm_version, unsigned int goo_opennurbs_version ) { if ( m_3dm_active_table != ON_3dmArchiveTableType::Unset ) { ON_ERROR("ON_BinaryArchive::BeginWrite3dmUserTable() - m_active_table != no_active_table"); } if ( !ON_UuidCompare( &ON_nil_uuid, &plugin_id ) ) { ON_ERROR("ON_BinaryArchive::BeginWrite3dmUserTable() - nil usertable_uuid not permitted."); return false; } if (false == ArchiveContains3dmTable(ON_3dmArchiveTableType::user_table)) return false; if (false == ShouldSerializeUserDataItem(plugin_id, plugin_id)) return false; if ( bSavingGoo ) { if ( goo_3dm_version <= 3 ) return false; if ( goo_opennurbs_version < 200601010 ) return false; if ( goo_3dm_version >= 50 && Archive3dmVersion() < 50 ) { // goo with 8 byte chunk lengths cannot be saved // in files expecting 4 byte chunk lengths. return false; } } else { goo_3dm_version = Archive3dmVersion(); goo_opennurbs_version = ArchiveOpenNURBSVersion(); } bool rc = BeginWrite3dmTable( TCODE_USER_TABLE); if (rc) { rc = BeginWrite3dmChunk( TCODE_USER_TABLE_UUID, 0 ); if (rc) { rc = WriteUuid( plugin_id ); if (rc) { // The TCODE_USER_TABLE_RECORD_HEADER chunk was added in // version 200910190 to contain the archive and opennurbs // version the plug-in used when writing the file. // This information is needed so "goo" can be correctly // read. rc = BeginWrite3dmChunk( TCODE_USER_TABLE_RECORD_HEADER, 1, 0 ); if ( rc ) { if (rc) rc = WriteBool(bSavingGoo); if (rc) rc = WriteInt(goo_3dm_version); unsigned int opennurbs_version_to_write = ON_BinaryArchive::ArchiveOpenNURBSVersionToWrite(goo_3dm_version, goo_opennurbs_version); if (rc) rc = WriteInt(opennurbs_version_to_write); if ( !EndWrite3dmChunk() ) rc = false; } } if ( !EndWrite3dmChunk() ) rc = false; } if (rc) { rc = BeginWrite3dmChunk( TCODE_USER_RECORD, 0 ); } if ( !rc ) { EndWrite3dmTable( TCODE_USER_TABLE); } } return rc; } bool ON_BinaryArchive::Write3dmAnonymousUserTableRecord( ON_UUID plugin_id, int goo_3dm_version, unsigned int goo_opennurbs_version, const ON_3dmGoo& goo ) { if ( ON_UuidIsNil(plugin_id) ) return false; if (false == ShouldSerializeUserDataItem(plugin_id, plugin_id)) return false; if ( goo_3dm_version <= 3 ) return false; if ( !ON_VersionNumberIsValid(goo_opennurbs_version) && !ON_VersionNumberIsYearMonthDateFormat(goo_3dm_version,goo_opennurbs_version) ) { return false; } if ( goo.m_typecode != TCODE_USER_RECORD ) return false; if ( 0 == goo.m_value ) return false; if ( 0 == goo.m_goo ) return false; bool bSavingGoo = true; if ( !BeginWrite3dmUserTable( plugin_id, bSavingGoo, goo_3dm_version, goo_opennurbs_version ) ) return false; bool rc = WriteByte( goo.m_value, goo.m_goo ); if ( !EndWrite3dmUserTable() ) rc = false; return rc; } bool ON_BinaryArchive::Write3dmAnonymousUserTable( const ON_3dmGoo& goo ) { bool rc = false; const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( !c || c->m_typecode != TCODE_USER_RECORD ) { ON_ERROR("ON_BinaryArchive::Write3dmAnonymousUserTable() - active chunk not a TCODE_USER_RECORD."); rc = false; } else if ( goo.m_typecode != TCODE_USER_RECORD ) { ON_ERROR("ON_BinaryArchive::Write3dmAnonymousUserTable() - goo chunk not a TCODE_USER_RECORD."); rc = false; } else { rc = ( goo.m_value > 0 ) ? WriteByte( goo.m_value, goo.m_goo ) : true; } return rc; } bool ON_BinaryArchive::EndWrite3dmUserTable() { bool rc = false; const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( c && c->m_typecode == TCODE_USER_RECORD ) { rc = EndWrite3dmChunk(); } else { ON_ERROR("ON_BinaryArchive::EndWrite3dmUserTable() - not in a TCODE_USER_RECORD chunk."); rc = false; } if ( !EndWrite3dmTable(TCODE_USER_TABLE) ) rc = false; return rc; } bool ON_BinaryArchive::BeginRead3dmUserTable( ON_UUID& plugin_id, bool* bLastSavedAsGoo, int* archive_3dm_version, unsigned int* archive_opennurbs_version ) { bool bReadArchiveInfo = false; if ( bLastSavedAsGoo ) *bLastSavedAsGoo = false; if ( archive_3dm_version ) *archive_3dm_version = 0; if ( archive_opennurbs_version ) *archive_opennurbs_version = 0; if ( m_3dm_version == 1 ) return false; bool rc = BeginRead3dmTable( TCODE_USER_TABLE ); // Do not add calls to EmergencyFindTable() here. // BeginRead3dmTable( TCODE_USER_TABLE ) returns // false when there are no user tables and that // is a common situation. if ( rc ) { // read table id ON__UINT32 tcode = 0; ON__INT64 big_value = 0; if (rc) rc = BeginRead3dmBigChunk( &tcode, &big_value ); if (rc) { if ( tcode != TCODE_USER_TABLE_UUID ) { ON_ERROR("ON_BinaryArchive::BeginRead3dmUserTable() - missing user table UUID"); rc = false; } else { rc = ReadUuid( plugin_id ); // Version 200910190 of OpenNURBS began writing a TCODE_USER_TABLE_RECORD_HEADER // section immediately after the uuid. This was possible because the uuid // was wrapped in a TCODE_USER_TABLE_UUID chunk. The TCODE_USER_TABLE_RECORD_HEADER // contains information that let's us determine what version of Rhino and // opennurbs wrote the user table. We need to know this because "goo" // can have chunks with 4 byte lengths embedded in a file with 8 byte // chunk lengths. If this information is missing, then we know the "goo" // must have 4 byte chunk lengths and we assume it is from a V4 file. // // 37 + SizeofChunkLength() = // 16 bytes of uuid // + 4 bytes of TCODE_USER_TABLE_RECORD_HEADER typecode // + SizeofChunkLength() TCODE_USER_TABLE_RECORD_HEADER chunk length // + 1 byte of bSlastSavedAsGoo bool // + 4 bytes of archive_3dm_version // + 4 bytes of archive_opennurbs_version // + 4 bytes of TCODE_USER_TABLE_RECORD_HEADER chunk crc // + 4 bytes of TCODE_USER_TABLE_UUID chunk crc const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( rc && ArchiveOpenNURBSVersion() >= 200910190 && 0 != c && TCODE_USER_TABLE_UUID == c->m_typecode && c->Length() >= 45 + SizeofChunkLength() ) { int major_chunk_version = 0; int minor_chunk_version = 0; rc = BeginRead3dmChunk(TCODE_USER_TABLE_RECORD_HEADER,&major_chunk_version,&minor_chunk_version); if (rc) { bReadArchiveInfo = true; bool b = true; int arch_ver = 0; unsigned int on_ver = 0; rc = ReadBool(&b); if ( rc && bLastSavedAsGoo ) *bLastSavedAsGoo = b; if (rc) rc = ReadInt(&arch_ver); if (rc && archive_3dm_version) *archive_3dm_version = arch_ver; if (rc) rc = ReadInt(&on_ver); if (rc && archive_opennurbs_version) *archive_opennurbs_version = on_ver; if ( !EndRead3dmChunk() ) rc = false; } } } if ( !EndRead3dmChunk() ) rc = false; } tcode = 0; big_value = 0; if (rc) rc = BeginRead3dmBigChunk( &tcode, &big_value ); if (rc) { if ( tcode != TCODE_USER_RECORD ) { ON_ERROR("ON_BinaryArchive::BeginRead3dmUserTable() - missing user table TCODE_USER_RECORD chunk."); EndRead3dmChunk(); rc = false; } } if (!rc) EndRead3dmTable(TCODE_USER_TABLE); if ( rc && !bReadArchiveInfo ) { // If the file we are reading is V4 or an early V5 file, then use the // version numbers from the file. Otherwise, assume the goo is from // an early V5 file. All we know for sure is that the chunk lengths // in the user table are 4 bytes. if ( Archive3dmVersion() < 50 ) { if (archive_3dm_version) *archive_3dm_version = Archive3dmVersion(); if (archive_opennurbs_version) *archive_opennurbs_version = ArchiveOpenNURBSVersion(); } else { // Assume the goo is from an early V5 file. // All we know for sure is that the chunk lengths // in the user table are 4 bytes. if (archive_3dm_version) *archive_3dm_version = 5; if (archive_opennurbs_version) *archive_opennurbs_version = 200910180; } } } return rc; } bool ON_BinaryArchive::Read3dmAnonymousUserTable( int archive_3dm_version, unsigned int archive_opennurbs_version, ON_3dmGoo& goo ) { if ( 0 == archive_3dm_version ) { if ( Archive3dmVersion() < 50 ) { archive_3dm_version = Archive3dmVersion(); archive_opennurbs_version = ArchiveOpenNURBSVersion(); } else { // recent version with 4 byte chunk lengths. archive_3dm_version = 5; archive_opennurbs_version = 200910190; } } bool rc = Read3dmGoo( goo ); if (rc && goo.m_typecode != TCODE_USER_RECORD ) { ON_ERROR("ON_BinaryArchive::Read3dmAnonymousUserTable() do not read a TCODE_USER_RECORD chunk."); rc = false; } return rc; } bool ON_BinaryArchive::EndRead3dmUserTable() { if ( m_chunk.Count() != 2 ) { ON_ERROR("ON_BinaryArchive::EndRead3dmUserTable() m_chunk.Count() != 2"); return false; } const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if ( 0 == c || c->m_typecode != TCODE_USER_RECORD ) { ON_ERROR("ON_BinaryArchive::EndRead3dmTable() m_chunk.Last()->typecode != TCODE_USER_RECORD"); return false; } // end of TCODE_USER_RECORD chunk // Suppress the partially read chunk warning because plug-in IO // is too upredictable for this warning to be helpful. bool rc = EndRead3dmChunk(true); if (rc) { // end of table chunk unsigned int tcode = 0; ON__INT64 big_value = -1; rc = BeginRead3dmBigChunk( &tcode, &big_value ); if ( rc ) { if ( tcode != TCODE_ENDOFTABLE ) { ON_ERROR("ON_BinaryArchive::EndRead3dmTable() missing TCODE_ENDOFTABLE marker."); } if ( !EndRead3dmChunk() ) rc = false; } } if ( !EndRead3dmTable(TCODE_USER_TABLE) ) rc = false; return rc; } bool ON_BinaryArchive::Write3dmEndMark() { Flush(); if ( m_chunk.Count() != 0 ) { ON_ERROR( "ON_BinaryArchive::WriteEndMark() called with unfinished chunks.\n" ); return false; } if (false == Begin3dmTable(ON::archive_mode::write3dm,ON_3dmArchiveTableType::end_mark)) return false; ON__UINT64 length = CurrentPosition(); // since no chunks are unfinished, everything // has been committed to disk in either // write mode. bool rc = BeginWrite3dmChunk( TCODE_ENDOFFILE, 0 ); if ( rc ) { size_t sizeof_chunk_length = SizeofChunkLength(); size_t sizeoffile_length = (8==SizeofChunkLength()) ? 8 : 4; length += (4 + sizeof_chunk_length + sizeoffile_length ); rc = WriteEOFSizeOfFile(length); if ( !EndWrite3dmChunk() ) rc = false; } Flush(); return End3dmTable(ON_3dmArchiveTableType::end_mark,rc); } bool ON_BinaryArchive::Read3dmEndMark( size_t* file_length ) { if (false == Begin3dmTable(ON::archive_mode::read3dm,ON_3dmArchiveTableType::end_mark)) return false; unsigned int tcode=0; ON__INT64 value=0; if ( file_length ) *file_length = 0; const unsigned int saved_error_message_mask = m_error_message_mask; m_error_message_mask |= 0x0001; // disable v1 ReadByte() error message at EOF bool rc = PeekAt3dmBigChunkType(&tcode,&value); m_error_message_mask = saved_error_message_mask; if (rc) { if ( tcode == TCODE_ENDOFFILE ) { rc = BeginRead3dmBigChunk(&tcode,&value); if ( rc && value > 0 && ((ON__UINT64)value) >= SizeofChunkLength() ) { ON__UINT64 u64 = 0; rc = ReadEOFSizeOfFile( &u64 ); if ( rc && file_length ) *file_length = (size_t)u64; m_3dm_end_mark_length = u64; if ( !EndRead3dmChunk() ) rc = false; } } } else if (1 == m_3dm_version) { // 2015-Nov-13 Dale Lear // The end chunk is missing in some V1 files. It's 20 years later and this error is not helpful. rc = true; } return End3dmTable(ON_3dmArchiveTableType::end_mark,rc); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// ON::endian ON_BinaryArchive::Endian() const // endian-ness of cpu { return m_endian; } ON::archive_mode ON_BinaryArchive::Mode() const { return m_mode; } bool ON_BinaryArchive::ReadMode() const { bool bIsReading = false; switch (m_mode) { case ON::archive_mode::unset_archive_mode: break; case ON::archive_mode::read: bIsReading = true; break; case ON::archive_mode::write: break; case ON::archive_mode::readwrite: bIsReading = true; break; case ON::archive_mode::read3dm: bIsReading = true; break; case ON::archive_mode::write3dm: break; default: ON_ERROR("Invalid m_mode."); break; } return bIsReading; } bool ON_BinaryArchive::WriteMode() const { bool bIsWriting = false; switch (m_mode) { case ON::archive_mode::unset_archive_mode: break; case ON::archive_mode::read: break; case ON::archive_mode::write: bIsWriting = true; break; case ON::archive_mode::readwrite: bIsWriting = true; break; case ON::archive_mode::read3dm: break; case ON::archive_mode::write3dm: bIsWriting = true; break; default: ON_ERROR("Invalid m_mode."); break; } return bIsWriting; } bool ON_BinaryArchive::UnsetMode() const { return (false == ReadMode() && false == WriteMode()); } void ON_BinaryArchive::UpdateCRC( size_t count, const void* p ) { if ( m_bDoChunkCRC ) { ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if (c) { if ( c->m_do_crc16 ) c->m_crc16 = ON_CRC16( c->m_crc16, count, p ); // version 1 files had 16 bit CRC if ( c->m_do_crc32 ) c->m_crc32 = ON_CRC32( c->m_crc32, count, p ); // version 2 files have 32 bit CRC } } } void ON_BinaryArchive::Internal_ReportCRCError() { m_crc_error_count++; if (nullptr != m_3dm_table_status_list && m_3dm_active_table == m_3dm_table_status_list->m_table_status.m_table_type && ON_3dmArchiveTableStatus::TableState::Finished != m_3dm_table_status_list->m_table_status.m_state ) { m_3dm_table_status_list->m_table_status.m_crc_error_count++; } return; } unsigned int ON_BinaryArchive::BadCRCCount() const { return m_crc_error_count; } /* Returns: Number of critical errors */ unsigned int ON_BinaryArchive::CriticalErrorCount() const { return m_critical_error_count; } void ON_BinaryArchive::Internal_ReportCriticalError() { m_critical_error_count++; if (nullptr != m_3dm_table_status_list && m_3dm_active_table == m_3dm_table_status_list->m_table_status.m_table_type) { m_3dm_table_status_list->m_table_status.m_critical_error_count++; } return; } const ON_3dmArchiveTableStatus ON_BinaryArchive::Archive3dmTableStatus( ON_3dmArchiveTableType table_type ) { for (const ON_3dmTableStatusLink* link = m_3dm_table_status_list; nullptr != link; link = link->m_next) { if ( table_type == link->m_table_status.m_table_type ) return link->m_table_status; } return ON_3dmArchiveTableStatus::Unset; } unsigned int ON_BinaryArchive::ErrorMessageMask() const { return m_error_message_mask; } bool ON_BinaryArchive::MaskReadError( ON__UINT64 sizeof_request, ON__UINT64 sizeof_read ) const { if ( 0 == (static_cast(m_mode) % 2 ) ) return false; // something is seriously wrong if ( sizeof_request == sizeof_read ) return true; // no error if ( sizeof_read > sizeof_request ) return false; // something is seriously wrong if ( 0 != (0x04 & m_error_message_mask) ) return true; if (0 != (0x01 & m_error_message_mask) && 4 == sizeof_request && 0 == sizeof_read) { // when reading v1 files, there are some situations where // it is reasonable to attempt to read 4 bytes at the end // of a file. return true; } if (0 == m_3dm_version && 0 == m_3dm_opennurbs_version && 0 == m_3dm_start_section_offset && ON_3dmArchiveTableType::Unset == m_3dm_previous_table && ON_3dmArchiveTableType::Unset == m_3dm_active_table && ON_3dmArchiveTableType::Unset == m_3dm_first_failed_table && 0 == m_chunk && ON::archive_mode::read3dm == m_mode ) { // In Read3dmStartSection(), we search for the string // "3D Geometry File Format ...". When a non-.3dm file // is searched, we eventually reach the end of the file. // This error condition is reported by the returning // false from ON_BinaryArchive::Read3dmStartSection(). // ON_ERROR is not called to prevent annoying everyone // when the open file dialog is digging around looking // for files. return true; } return false; // parial read not permitted at this time. } ON__UINT64 ON_BinaryArchive::ReadBuffer( ON__UINT64 sizeof_buffer, void* buffer ) { // Expert user function to load a buffer with up to sizeof_buffer // bytes but tolerate encountering the end of the file. if ( 0 == buffer ) return 0; unsigned int saved_error_mask = m_error_message_mask; m_error_message_mask |= 0x04; // tell Read to tolerate hitting the end of the file ON__UINT64 sizeof_read = Read((size_t)sizeof_buffer,buffer); m_error_message_mask = saved_error_mask; return sizeof_read; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// ON_BinaryFile::ON_BinaryFile( ON::archive_mode archive_mode ) : ON_BinaryArchive( archive_mode ) {} ON_BinaryFile::ON_BinaryFile( ON::archive_mode archive_mode, FILE* fp ) : ON_BinaryArchive( archive_mode ) , m_fp(fp) {} ON_BinaryFile::ON_BinaryFile( ON::archive_mode archive_mode, const char* file_system_path ) : ON_BinaryArchive( archive_mode ) { switch (archive_mode) { case ON::archive_mode::unset_archive_mode: break; case ON::archive_mode::read: case ON::archive_mode::read3dm: m_fp = ON::OpenFile(file_system_path,"rb"); break; case ON::archive_mode::write: case ON::archive_mode::write3dm: m_fp = ON::OpenFile(file_system_path,"wb"); break; default: break; } if ( nullptr != m_fp ) m_bCloseFileInDestructor = true; else { ON_ERROR("Invalid parameters"); } } ON_BinaryFile::ON_BinaryFile( ON::archive_mode archive_mode, const wchar_t* file_system_path ) : ON_BinaryArchive( archive_mode ) { switch (archive_mode) { case ON::archive_mode::unset_archive_mode: break; case ON::archive_mode::read: case ON::archive_mode::read3dm: m_fp = ON::OpenFile(file_system_path,L"rb"); break; case ON::archive_mode::write: case ON::archive_mode::write3dm: m_fp = ON::OpenFile(file_system_path,L"wb"); break; default: break; } if ( nullptr != m_fp ) m_bCloseFileInDestructor = true; else { ON_ERROR("Invalid parameters"); } } ON_BinaryFile::~ON_BinaryFile() { if ( m_bCloseFileInDestructor ) CloseFile(); EnableMemoryBuffer(0); } bool ON_BinaryFile::FileIsOpen() const { return (nullptr != m_fp); } void ON_BinaryFile::CloseFile() { FILE* fp = m_fp; if (nullptr != fp) { m_fp = nullptr; ON::CloseFile(fp); } m_bCloseFileInDestructor = false; } size_t ON_BinaryArchive::Read( size_t count, void* p ) { size_t readcount = 0; if ( !ReadMode() ) { Internal_ReportCriticalError(); ON_ERROR("ReadMode() is false."); } else if ( count > 0 ) { if ( nullptr == p ) { Internal_ReportCriticalError(); ON_ERROR("buffer parameter is nullptr."); } else { if (m_bChunkBoundaryCheck) { const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if (nullptr != c) { const ON__UINT64 current_pos = CurrentPosition(); const ON__UINT64 new_pos = current_pos + ((ON__UINT64)count); if (current_pos < c->m_start_offset) { ON_ERROR("Attempt to read before the start of current chunk."); count = 0; } else if ( new_pos > c->m_end_offset ) { ON_ERROR("Attempt to read beyond end of current chunk."); count = 0; } } } if (count > 0) { readcount = Internal_ReadOverride(count, p); if (readcount == count) { UpdateCRC(count, p); } else if ( false == MaskReadError(count,readcount) ) { // error occured SetStorageDeviceError(ON_BinaryArchive::eStorageDeviceError::ReadFailed); ON_ERROR("Internal_ReadOverride(count, p) failed."); } if (readcount > 0) Internal_IncrementCurrentPosition((ON__UINT64)readcount); } } } return readcount; } bool ON_BinaryArchive::ReadByte(size_t count, void* p) { return (count == Read(count, p)); } size_t ON_BinaryArchive::Write( size_t count, const void* p ) { size_t writecount = 0; if ( !WriteMode() ) { Internal_ReportCriticalError(); ON_ERROR("WriteMode() is false."); } else if ( count > 0 ) { if ( nullptr == p ) { Internal_ReportCriticalError(); ON_ERROR("buffer parameter is nullptr."); } else { const ON__UINT64 current_pos = CurrentPosition(); if (m_bChunkBoundaryCheck) { const ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if (nullptr != c ) { // March 2017 - Do not permit writing outside chunk boundaries. // (m_bChunkBoundaryCheck = false when WriteChunkLength() is called). if (current_pos < c->m_start_offset ) { ON_ERROR("Attempt to write before current chunk boundary."); count = 0; } if ( current_pos > c->m_end_offset ) { ON_ERROR("Attempt to write after current chunk boundary."); count = 0; } } } if (count > 0) { writecount = (size_t)Internal_WriteOverride(count, p); if (writecount == count) { UpdateCRC(count, p); } else { SetStorageDeviceError(ON_BinaryArchive::eStorageDeviceError::WriteFailed); ON_ERROR("Internal_WriteOverride(count, p) failed."); } if (writecount > 0) { ON_3DM_BIG_CHUNK* c = m_chunk.Last(); if (nullptr != c && current_pos >= c->m_start_offset) { const ON__UINT64 new_pos = current_pos + (ON__UINT64)writecount; if (new_pos > c->m_end_offset) c->m_end_offset = new_pos; } Internal_IncrementCurrentPosition((ON__UINT64)writecount); } } } } return writecount; } bool ON_BinaryArchive::WriteByte( size_t count, const void* p ) { return (count == Write(count, p)); } bool ON_BinaryArchive::EnableCRCCalculation( bool bEnable ) { bool rc = m_bDoChunkCRC; m_bDoChunkCRC = bEnable ? true : false; return rc; } bool ON_BinaryArchive::SetArchive3dmVersion(int v) { bool rc = false; // valid versions are 1,2,3,4,5,50,60,70,... // 1 - 4 correspond to Rhino V1 - V4. // 5 was used for early V5 betas with 4 byte chunk lengths // 50 is used for V5 files with 8 bytes chunk lengths. // 60, 70, ... will be used for Rhino V6, V7, etc. if ( (v >= 1 && v <= 5) || ( v >= 50 && 0 == (v % 10) && v <= ON_BinaryArchive::CurrentArchiveVersion() ) ) { m_3dm_version = v; rc = true; } else { m_3dm_version = 0; ON_ERROR("ON_BinaryArchive::SetArchive3dmVersion - invalid version"); } return rc; } void ON_BinaryFile::EnableMemoryBuffer( int buffer_capacity ) { if ( buffer_capacity > 0 && !m_memory_buffer) { m_memory_buffer = (unsigned char*)onmalloc(buffer_capacity); if ( m_memory_buffer ) { m_memory_buffer_capacity = buffer_capacity; m_memory_buffer_size = 0; m_memory_buffer_ptr = 0; } } else { if ( buffer_capacity == 0 && m_memory_buffer ) { Flush(); onfree(m_memory_buffer); } m_memory_buffer = 0; m_memory_buffer_capacity = 0; m_memory_buffer_size = 0; m_memory_buffer_ptr = 0; } } size_t ON_BinaryFile::Internal_ReadOverride( size_t count, void* p ) { const size_t rc = (m_fp) ? fread( p, 1, count, m_fp ) : 0; if (rc != count && nullptr != m_fp) { if (false == MaskReadError(count, rc)) { ON_ERROR("fread() failed."); } } return rc; } size_t ON_BinaryFile::Internal_WriteOverride( size_t count, const void* p ) { size_t rc = 0; if ( m_fp ) { if ( m_memory_buffer ) { if ( count+m_memory_buffer_ptr >= m_memory_buffer_capacity ) { if ( !Flush() ) // flush existing memory buffer to disk return 0; rc = fwrite( p, 1, count, m_fp ); // write directly to disk if (rc != count) { ON_ERROR("fwrite() failed - situation A."); } } else { // copy to the memory buffer memcpy( m_memory_buffer+m_memory_buffer_ptr, p, count ); m_memory_buffer_ptr += count; if ( m_memory_buffer_ptr > m_memory_buffer_size ) m_memory_buffer_size = m_memory_buffer_ptr; rc = count; } } else { rc = fwrite( p, 1, count, m_fp ); if (rc != count) { ON_ERROR("fwrite() failed - situation B."); } } } return rc; } bool ON_BinaryFile::Flush() { bool rc = true; if ( m_fp ) { if ( m_memory_buffer && m_memory_buffer_size > 0 ) { // Put all of m_memory_buffer on "disk". rc = ( m_memory_buffer_size == fwrite( m_memory_buffer, 1, m_memory_buffer_size, m_fp )); if (false == rc) { ON_ERROR("fwrite( m_memory_buffer, 1, m_memory_buffer_size, m_fp ) failed."); } if ( rc && m_memory_buffer_ptr != m_memory_buffer_size ) { // The current position is not at the end of what we just wrote. // fseek back to m_fp points at the position corresponding to m_memory_buffer_ptr. ON__INT64 delta = (m_memory_buffer_ptr >= m_memory_buffer_size) ? ((ON__INT64)(m_memory_buffer_ptr - m_memory_buffer_size)) : -((ON__INT64)(m_memory_buffer_size - m_memory_buffer_ptr)); if ( false == ON_FileStream::SeekFromCurrentPosition(m_fp,delta) ) { ON_ERROR("ON_FileStream::SeekFromCurrentPosition(m_fp,delta) failed."); rc = false; } } m_memory_buffer_size = 0; m_memory_buffer_ptr = 0; } } return rc; } ON__UINT64 ON_BinaryFile::Internal_CurrentPositionOverride() const { ON__UINT64 offset = 0; if ( 0 != m_fp ) { #if defined(ON_COMPILER_MSC) // use an ftell() that supports file offsets > 2GB ON__INT64 offset64 = _ftelli64(m_fp); if ( offset64 < 0 ) { ON_ERROR(" _ftelli64() failed"); } else { offset = (ON__UINT64)offset64; } #else offset = (ON__UINT64)ftell(m_fp); #endif if ( m_memory_buffer && m_memory_buffer_size > 0 && m_memory_buffer_ptr > 0 ) { // add bytes in memory buffer offset += (ON__INT64)m_memory_buffer_ptr; } } else { ON_ERROR("m_fp is nullptr."); } return offset; } bool ON_BinaryFile::AtEnd() const { bool rc = true; if ( m_fp ) { rc = false; if ( ReadMode() && m_memory_buffer_ptr >= m_memory_buffer_size ) { if (feof(m_fp)) { rc = true; } else { // Attempt to read a single byte. int buffer; size_t fread_count = fread(&buffer, 1, 1, m_fp); if (feof(m_fp)) { rc = true; } if (1 == fread_count) { // undo the 1 byte freed() ON_FileStream::SeekFromCurrentPosition(m_fp, -1); } } } } return rc; } bool ON_BinaryFile::Internal_SeekFromCurrentPositionOverride( int offset ) { // it appears that the calls to SeekFromCurrentPosition(), // and consequent call to fseek(), in ON_BinaryArchive::EndWrite3DMChunk() // really slow down 3DM file writing on some networks. There are // reports of a 100x difference in local vs network saves. // I could not reproduce these 100x slow saves in testing on McNeel's // network but we have a good quality network and a server that's // properly tuned. My guess is that the slow saves are happening // on servers that do a poor job of cacheing because they are starved // for memory and/or heavily used at the time of the save. // // To speed up network saves, ON_BinaryFile can optionally use // it's own buffer for buffered I/O instead of relying on fwrite() // and the OS to handle this. bool rc = false; if ( m_fp ) { if ( nullptr != m_memory_buffer && ((ON__INT_PTR)m_memory_buffer_ptr)+((ON__INT_PTR)offset) >= 0 && m_memory_buffer_ptr+offset <= m_memory_buffer_size ) { m_memory_buffer_ptr += offset; rc = true; } else { // don't deal with memory buffer I/O if seek lands outside of current memory buffer. Flush(); if ( ON_FileStream::SeekFromCurrentPosition(m_fp,offset) ) { rc = true; } else { ON_ERROR("ON_FileStream::SeekFromCurrentPosition(m_fp,offset) failed."); } } } return rc; } bool ON_BinaryFile::Internal_SeekToStartOverride() { bool rc = false; if ( m_fp ) { Flush(); // don't deal with memory buffer I/O in rare seek from start if ( ON_FileStream::SeekFromStart(m_fp,0) ) { rc = true; } else { ON_ERROR("ON_FileStream::SeekFromStart(m_fp,0) failed."); } } return rc; } ON_3dmGoo::ON_3dmGoo() : m_typecode(0), m_value(0), m_goo(0), m_next_goo(0), m_prev_goo(0) {} ON_3dmGoo::~ON_3dmGoo() { if ( m_prev_goo ) m_prev_goo->m_next_goo = m_next_goo; if ( m_next_goo ) m_next_goo->m_prev_goo = m_prev_goo; if ( m_goo ) { onfree(m_goo); m_goo = 0; } } ON_3dmGoo::ON_3dmGoo( const ON_3dmGoo& src ) : m_typecode(0), m_value(0), m_goo(0), m_next_goo(0), m_prev_goo(0) { *this = src; } ON_3dmGoo& ON_3dmGoo::operator=( const ON_3dmGoo& src ) { if ( this != &src ) { if ( m_goo ) { onfree(m_goo); } m_typecode = src.m_typecode; m_value = src.m_value; m_goo = (m_value > 0 && src.m_goo) ? (unsigned char*)onmemdup( src.m_goo, m_value ) : 0; } return *this; } void ON_3dmGoo::Dump(ON_TextLog& dump) const { dump.Print("typecode = %08x value = %d\n",m_typecode,m_value); } bool ON_WriteOneObjectArchive( ON_BinaryArchive& archive, int version, const ON_Object& object ) { const ON_Object* object_array = &object; return ON_WriteMultipleObjectArchive( archive, version, 1, &object_array ); } bool ON_WriteOneObjectArchive( const wchar_t* filename, const ON_Object& object ) { const ON_Object* object_array = &object; return ON_WriteMultipleObjectArchive( filename, 0, 1, &object_array ); } bool ON_WriteMultipleObjectArchive( ON_BinaryArchive& archive, int version, const ON_SimpleArray& object_list ) { return ON_WriteMultipleObjectArchive( archive, version, object_list.Count(), object_list.Array() ); } bool ON_WriteMultipleObjectArchive( const wchar_t* filename, int version, size_t object_list_count, const ON_Object* const* object_list ) { FILE* fp = ON::OpenFile( filename, L"wb" ); if (nullptr == fp) return false; ON_BinaryFile archive( ON::archive_mode::write3dm, fp ); archive.SetArchiveFullPath(filename); bool ok = ON_WriteMultipleObjectArchive( archive, version, object_list_count, object_list ); ON::CloseFile( fp ); return ok; } bool ON_WriteMultipleObjectArchive( ON_BinaryArchive& archive, int version, size_t object_list_count, const ON_Object* const* object_list ) { if (object_list_count <= 0 || nullptr == object_list) return false; ONX_Model model; const bool bResolveIdAndNameConflicts = true; model.m_properties.m_RevisionHistory.NewRevision(); // 1 Feb 2012 Dale Lear // http://dev.mcneel.com/bugtrack/?q=98543 // These simple object archives have no unit system so they // can be read into a file with no scaling. Prior to // today the unit system was always millimeters. model.m_settings.m_ModelUnitsAndTolerances.m_unit_system.SetUnitSystem( ON::LengthUnitSystem::None ); ON_Layer* layer = new ON_Layer(ON_Layer::Default); layer->SetId(); layer->SetIndex(0); model.AddManagedModelComponent(layer, bResolveIdAndNameConflicts); for (size_t object_index = 0; object_index < object_list_count; object_index++) { const ON_Geometry* geometry = ON_Geometry::Cast(object_list[object_index]); if ( ON_BrepEdge::Cast(geometry) ) { // write parent brep geometry = static_cast(geometry)->Brep(); } else if ( ON_BrepTrim::Cast(geometry) ) { geometry = nullptr; } else if ( ON_BrepLoop::Cast(geometry) ) { geometry = static_cast(geometry)->Brep(); } else if ( ON_BrepFace::Cast(geometry) ) { // write parent brep geometry = static_cast(geometry)->Brep(); } else if ( ON_CurveProxy::Cast(geometry) ) { // write actual curve geometry = static_cast(geometry)->ProxyCurve(); } else if ( ON_SurfaceProxy::Cast(geometry) ) { // write actual surface geometry = static_cast(geometry)->ProxySurface(); } if (nullptr == geometry) continue; // bManageGeometry = false means ~ON_ModelGeometryComponent will not delete geometry const bool bManageGeometry = false; const bool bManageAttributes = true; ON_3dmObjectAttributes* attributes = new ON_3dmObjectAttributes(); attributes->m_layer_index = layer->Index(); attributes->m_uuid = ON_CreateId(); ON_ModelGeometryComponent* mg = ON_ModelGeometryComponent::CreateForExperts( bManageGeometry, const_cast(geometry), // safe - see bManageGeometry = false comment above. bManageAttributes, attributes, nullptr ); if (nullptr == mg) { delete attributes; continue; } model.AddManagedModelComponent( mg, bResolveIdAndNameConflicts ); } if ( (0 != version % 10) || version < ON_BinaryArchive::CurrentArchiveVersion() - 10 || version > ON_BinaryArchive::CurrentArchiveVersion() ) { version = ON_BinaryArchive::CurrentArchiveVersion(); } model.m_sStartSectionComments = "Archive created by ON_WriteMultipleObjectArchive"; bool rc = model.Write(archive, version ); return rc; } static void Dump3dmChunk_ErrorReportHelper( ON__UINT64 offset, const char* msg, ON_TextLog& dump ) { int ioffset = (int)offset; dump.Print("** ERROR near offset %d ** %s\n",ioffset,msg); } static bool DumpChunk_PrintHeaderInfo( ON__UINT64 offset0, ON__UINT32 typecode, ON__INT64 big_value, const char* typecode_name, ON_TextLog& dump) { bool bShortChunk = (0 != (typecode & TCODE_SHORT)); if ( 0 == typecode_name ) typecode_name = ON_BinaryArchive::TypecodeName(typecode); if ( 0 == typecode_name ) typecode_name = "unknown tcode"; if ( bShortChunk ) { dump.Print("%6d: %08X %s: value = %lld (%016llX)\n", offset0, typecode, typecode_name, big_value, big_value ); } else { // long chunk value = length of chunk data if ( big_value < 0 ) { Dump3dmChunk_ErrorReportHelper(offset0,"BeginRead3dmChunk() returned length < 0.",dump); return false; } dump.Print("%6d: %08X %s: length = %lld bytes\n", offset0, typecode, typecode_name, big_value ); } return true; } #define CASEtcode2string(tc) case tc: s = #tc ; break const char* ON_BinaryArchive::TypecodeName( unsigned int tcode ) { const char* s; switch( tcode ) { CASEtcode2string(TCODE_FONT_TABLE); CASEtcode2string(TCODE_FONT_RECORD); CASEtcode2string(TCODE_DIMSTYLE_TABLE); CASEtcode2string(TCODE_DIMSTYLE_RECORD); CASEtcode2string(TCODE_INSTANCE_DEFINITION_RECORD); CASEtcode2string(TCODE_COMMENTBLOCK); CASEtcode2string(TCODE_ENDOFFILE); CASEtcode2string(TCODE_ENDOFFILE_GOO); CASEtcode2string(TCODE_LEGACY_GEOMETRY); CASEtcode2string(TCODE_OPENNURBS_OBJECT); CASEtcode2string(TCODE_GEOMETRY); CASEtcode2string(TCODE_ANNOTATION); CASEtcode2string(TCODE_DISPLAY); CASEtcode2string(TCODE_RENDER); CASEtcode2string(TCODE_INTERFACE); CASEtcode2string(TCODE_TOLERANCE); CASEtcode2string(TCODE_TABLE); CASEtcode2string(TCODE_TABLEREC); CASEtcode2string(TCODE_USER); CASEtcode2string(TCODE_SHORT); CASEtcode2string(TCODE_CRC); CASEtcode2string(TCODE_ANONYMOUS_CHUNK); CASEtcode2string(TCODE_MATERIAL_TABLE); CASEtcode2string(TCODE_LAYER_TABLE); CASEtcode2string(TCODE_LIGHT_TABLE); CASEtcode2string(TCODE_OBJECT_TABLE); CASEtcode2string(TCODE_PROPERTIES_TABLE); CASEtcode2string(TCODE_SETTINGS_TABLE); CASEtcode2string(TCODE_BITMAP_TABLE); CASEtcode2string(TCODE_USER_TABLE); CASEtcode2string(TCODE_INSTANCE_DEFINITION_TABLE); CASEtcode2string(TCODE_HATCHPATTERN_TABLE); CASEtcode2string(TCODE_HATCHPATTERN_RECORD); CASEtcode2string(TCODE_LINETYPE_TABLE); CASEtcode2string(TCODE_LINETYPE_RECORD); CASEtcode2string(TCODE_OBSOLETE_LAYERSET_TABLE); CASEtcode2string(TCODE_OBSOLETE_LAYERSET_RECORD); CASEtcode2string(TCODE_TEXTURE_MAPPING_TABLE); CASEtcode2string(TCODE_TEXTURE_MAPPING_RECORD); CASEtcode2string(TCODE_HISTORYRECORD_TABLE); CASEtcode2string(TCODE_HISTORYRECORD_RECORD); CASEtcode2string(TCODE_ENDOFTABLE); CASEtcode2string(TCODE_PROPERTIES_REVISIONHISTORY); CASEtcode2string(TCODE_PROPERTIES_NOTES); CASEtcode2string(TCODE_PROPERTIES_PREVIEWIMAGE); CASEtcode2string(TCODE_PROPERTIES_COMPRESSED_PREVIEWIMAGE); CASEtcode2string(TCODE_PROPERTIES_APPLICATION); CASEtcode2string(TCODE_PROPERTIES_OPENNURBS_VERSION); CASEtcode2string(TCODE_SETTINGS_PLUGINLIST); CASEtcode2string(TCODE_SETTINGS_UNITSANDTOLS); CASEtcode2string(TCODE_SETTINGS_RENDERMESH); CASEtcode2string(TCODE_SETTINGS_ANALYSISMESH); CASEtcode2string(TCODE_SETTINGS_ANNOTATION); CASEtcode2string(TCODE_SETTINGS_NAMED_CPLANE_LIST); CASEtcode2string(TCODE_SETTINGS_NAMED_VIEW_LIST); CASEtcode2string(TCODE_SETTINGS_VIEW_LIST); CASEtcode2string(TCODE_SETTINGS_CURRENT_LAYER_INDEX); CASEtcode2string(TCODE_SETTINGS_CURRENT_MATERIAL_INDEX); CASEtcode2string(TCODE_SETTINGS_CURRENT_COLOR); CASEtcode2string(TCODE_SETTINGS__NEVER__USE__THIS); CASEtcode2string(TCODE_SETTINGS_CURRENT_WIRE_DENSITY); CASEtcode2string(TCODE_SETTINGS_RENDER); CASEtcode2string(TCODE_SETTINGS_GRID_DEFAULTS); CASEtcode2string(TCODE_SETTINGS_MODEL_URL); CASEtcode2string(TCODE_SETTINGS_CURRENT_FONT_INDEX); CASEtcode2string(TCODE_SETTINGS_CURRENT_DIMSTYLE_INDEX); CASEtcode2string(TCODE_SETTINGS_ATTRIBUTES); CASEtcode2string(TCODE_SETTINGS_RENDER_USERDATA); CASEtcode2string(TCODE_VIEW_RECORD); CASEtcode2string(TCODE_VIEW_CPLANE); CASEtcode2string(TCODE_VIEW_VIEWPORT); CASEtcode2string(TCODE_VIEW_VIEWPORT_USERDATA); CASEtcode2string(TCODE_VIEW_SHOWCONGRID); CASEtcode2string(TCODE_VIEW_SHOWCONAXES); CASEtcode2string(TCODE_VIEW_SHOWWORLDAXES); CASEtcode2string(TCODE_VIEW_TRACEIMAGE); CASEtcode2string(TCODE_VIEW_WALLPAPER); CASEtcode2string(TCODE_VIEW_WALLPAPER_V3); CASEtcode2string(TCODE_VIEW_TARGET); CASEtcode2string(TCODE_VIEW_V3_DISPLAYMODE); CASEtcode2string(TCODE_VIEW_NAME); CASEtcode2string(TCODE_VIEW_POSITION); CASEtcode2string(TCODE_VIEW_ATTRIBUTES); CASEtcode2string(TCODE_BITMAP_RECORD); CASEtcode2string(TCODE_MATERIAL_RECORD); CASEtcode2string(TCODE_LAYER_RECORD); CASEtcode2string(TCODE_LIGHT_RECORD); CASEtcode2string(TCODE_LIGHT_RECORD_ATTRIBUTES); CASEtcode2string(TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA); CASEtcode2string(TCODE_OBJECT_RECORD_HISTORY); CASEtcode2string(TCODE_OBJECT_RECORD_HISTORY_HEADER); CASEtcode2string(TCODE_OBJECT_RECORD_HISTORY_DATA); CASEtcode2string(TCODE_LIGHT_RECORD_END); CASEtcode2string(TCODE_USER_TABLE_UUID); CASEtcode2string(TCODE_USER_TABLE_RECORD_HEADER); CASEtcode2string(TCODE_USER_RECORD); CASEtcode2string(TCODE_GROUP_TABLE); CASEtcode2string(TCODE_GROUP_RECORD); CASEtcode2string(TCODE_OBJECT_RECORD); CASEtcode2string(TCODE_OBJECT_RECORD_TYPE); CASEtcode2string(TCODE_OBJECT_RECORD_ATTRIBUTES); CASEtcode2string(TCODE_OBJECT_RECORD_END); CASEtcode2string(TCODE_OPENNURBS_CLASS); CASEtcode2string(TCODE_OPENNURBS_CLASS_UUID); CASEtcode2string(TCODE_OPENNURBS_CLASS_DATA); CASEtcode2string(TCODE_OPENNURBS_CLASS_USERDATA); CASEtcode2string(TCODE_OPENNURBS_CLASS_USERDATA_HEADER); CASEtcode2string(TCODE_OPENNURBS_CLASS_END); CASEtcode2string(TCODE_OPENNURBS_BUFFER); CASEtcode2string(TCODE_ANNOTATION_SETTINGS); CASEtcode2string(TCODE_TEXT_BLOCK); CASEtcode2string(TCODE_ANNOTATION_LEADER); CASEtcode2string(TCODE_LINEAR_DIMENSION); CASEtcode2string(TCODE_ANGULAR_DIMENSION); CASEtcode2string(TCODE_RADIAL_DIMENSION); CASEtcode2string(TCODE_RHINOIO_OBJECT_NURBS_CURVE); CASEtcode2string(TCODE_RHINOIO_OBJECT_NURBS_SURFACE); CASEtcode2string(TCODE_RHINOIO_OBJECT_BREP); CASEtcode2string(TCODE_RHINOIO_OBJECT_DATA); CASEtcode2string(TCODE_RHINOIO_OBJECT_END); CASEtcode2string(TCODE_LEGACY_ASM); CASEtcode2string(TCODE_LEGACY_PRT); CASEtcode2string(TCODE_LEGACY_SHL); CASEtcode2string(TCODE_LEGACY_FAC); CASEtcode2string(TCODE_LEGACY_BND); CASEtcode2string(TCODE_LEGACY_TRM); CASEtcode2string(TCODE_LEGACY_SRF); CASEtcode2string(TCODE_LEGACY_CRV); CASEtcode2string(TCODE_LEGACY_SPL); CASEtcode2string(TCODE_LEGACY_PNT); CASEtcode2string(TCODE_STUFF); CASEtcode2string(TCODE_LEGACY_ASMSTUFF); CASEtcode2string(TCODE_LEGACY_PRTSTUFF); CASEtcode2string(TCODE_LEGACY_SHLSTUFF); CASEtcode2string(TCODE_LEGACY_FACSTUFF); CASEtcode2string(TCODE_LEGACY_BNDSTUFF); CASEtcode2string(TCODE_LEGACY_TRMSTUFF); CASEtcode2string(TCODE_LEGACY_SRFSTUFF); CASEtcode2string(TCODE_LEGACY_CRVSTUFF); CASEtcode2string(TCODE_LEGACY_SPLSTUFF); CASEtcode2string(TCODE_LEGACY_PNTSTUFF); CASEtcode2string(TCODE_RH_POINT); CASEtcode2string(TCODE_RH_SPOTLIGHT); CASEtcode2string(TCODE_OLD_RH_TRIMESH); CASEtcode2string(TCODE_OLD_MESH_VERTEX_NORMALS); CASEtcode2string(TCODE_OLD_MESH_UV); CASEtcode2string(TCODE_OLD_FULLMESH); CASEtcode2string(TCODE_MESH_OBJECT); CASEtcode2string(TCODE_COMPRESSED_MESH_GEOMETRY); CASEtcode2string(TCODE_ANALYSIS_MESH); CASEtcode2string(TCODE_NAME); CASEtcode2string(TCODE_VIEW); CASEtcode2string(TCODE_CPLANE); CASEtcode2string(TCODE_NAMED_CPLANE); CASEtcode2string(TCODE_NAMED_VIEW); CASEtcode2string(TCODE_VIEWPORT); CASEtcode2string(TCODE_SHOWGRID); CASEtcode2string(TCODE_SHOWGRIDAXES); CASEtcode2string(TCODE_SHOWWORLDAXES); CASEtcode2string(TCODE_VIEWPORT_POSITION); CASEtcode2string(TCODE_VIEWPORT_TRACEINFO); CASEtcode2string(TCODE_SNAPSIZE); CASEtcode2string(TCODE_NEAR_CLIP_PLANE); CASEtcode2string(TCODE_HIDE_TRACE); CASEtcode2string(TCODE_NOTES); CASEtcode2string(TCODE_UNIT_AND_TOLERANCES); CASEtcode2string(TCODE_MAXIMIZED_VIEWPORT); CASEtcode2string(TCODE_VIEWPORT_WALLPAPER); CASEtcode2string(TCODE_SUMMARY); CASEtcode2string(TCODE_BITMAPPREVIEW); CASEtcode2string(TCODE_VIEWPORT_V1_DISPLAYMODE); CASEtcode2string(TCODE_LAYERTABLE); CASEtcode2string(TCODE_LAYERREF); CASEtcode2string(TCODE_XDATA); CASEtcode2string(TCODE_RGB); CASEtcode2string(TCODE_TEXTUREMAP); CASEtcode2string(TCODE_BUMPMAP); CASEtcode2string(TCODE_TRANSPARENCY); CASEtcode2string(TCODE_DISP_AM_RESOLUTION); CASEtcode2string(TCODE_RGBDISPLAY); CASEtcode2string(TCODE_RENDER_MATERIAL_ID); CASEtcode2string(TCODE_LAYER); CASEtcode2string(TCODE_LAYER_OBSELETE_1); CASEtcode2string(TCODE_LAYER_OBSELETE_2); CASEtcode2string(TCODE_LAYER_OBSELETE_3); CASEtcode2string(TCODE_LAYERON); CASEtcode2string(TCODE_LAYERTHAWED); CASEtcode2string(TCODE_LAYERLOCKED); CASEtcode2string(TCODE_LAYERVISIBLE); CASEtcode2string(TCODE_LAYERPICKABLE); CASEtcode2string(TCODE_LAYERSNAPABLE); CASEtcode2string(TCODE_LAYERRENDERABLE); CASEtcode2string(TCODE_LAYERSTATE); CASEtcode2string(TCODE_LAYERINDEX); CASEtcode2string(TCODE_LAYERMATERIALINDEX); CASEtcode2string(TCODE_RENDERMESHPARAMS); CASEtcode2string(TCODE_DISP_CPLINES); CASEtcode2string(TCODE_DISP_MAXLENGTH); CASEtcode2string(TCODE_CURRENTLAYER); CASEtcode2string(TCODE_LAYERNAME); CASEtcode2string(TCODE_LEGACY_TOL_FIT); CASEtcode2string(TCODE_LEGACY_TOL_ANGLE); CASEtcode2string(TCODE_DICTIONARY); CASEtcode2string(TCODE_DICTIONARY_ID); CASEtcode2string(TCODE_DICTIONARY_ENTRY); CASEtcode2string(TCODE_DICTIONARY_END); default: // unknown typecode. s = 0; break; } return s; } #undef CASEtcode2string char* ON_BinaryArchive::ON_TypecodeParse( unsigned int tcode, char* typecode_name, size_t max_length ) { char* s; const char* sub_name; const char* h = "0123456789ABCDEF"; char c, c0; size_t slen; if ( !typecode_name || max_length <= 0 ) return 0; memset(typecode_name,0,max_length*sizeof(typecode_name[0])); slen = max_length-1; // the -1 insures the there is a null terminator if ( slen <= 0 ) return 0; sub_name = ON_BinaryArchive::TypecodeName(tcode); if ( 0 != sub_name && 0 != sub_name[0] ) { c0 = *sub_name++; s = typecode_name+1; slen--; while ( *sub_name ) { if ( slen <= 0 ) return 0; *s++ = *sub_name++; slen--; } typecode_name[0] = c0; return typecode_name; } sub_name = ON_BinaryArchive::TypecodeName( tcode & 0x7FFF0000 ); if ( !sub_name || 0 == sub_name[0] ) return 0; c0 = *sub_name++; s = typecode_name+1; slen--; while ( *sub_name ) { if ( slen <= 0 ) return 0; *s++ = *sub_name++; slen--; } sub_name = ON_BinaryArchive::TypecodeName( tcode & TCODE_SHORT ); if ( sub_name ) { if ( slen <= 0 ) return 0; *s++ = ' '; slen--; if ( slen <= 0 ) return 0; *s++ = '|'; slen--; if ( slen <= 0 ) return 0; *s++ = ' '; slen--; while ( *sub_name ) { if ( slen <= 0 ) return 0; *s++ = *sub_name++; slen--; } } sub_name = ON_BinaryArchive::TypecodeName( tcode & TCODE_CRC ); if ( sub_name ) { if ( slen <= 0 ) return 0; *s++ = ' '; slen--; if ( slen <= 0 ) return 0; *s++ = '|'; slen--; if ( slen <= 0 ) return 0; *s++ = ' '; slen--; while ( *sub_name ) { if ( slen <= 0 ) return 0; *s++ = *sub_name++; slen--; } } sub_name = ON_BinaryArchive::TypecodeName( tcode & 0x7FFF ); if ( sub_name ) { if ( slen <= 0 ) return 0; *s++ = ' '; slen--; if ( slen <= 0 ) return 0; *s++ = '|'; slen--; if ( slen <= 0 ) return 0; *s++ = ' '; slen--; while ( *sub_name ) { if ( slen <= 0 ) return 0; *s++ = *sub_name++; slen--; } } else { if ( slen <= 0 ) return 0; *s++ = ' '; slen--; if ( slen <= 0 ) return 0; *s++ = '|'; slen--; if ( slen <= 0 ) return 0; *s++ = ' '; slen--; if ( slen <= 0 ) return 0; *s++ = '0'; slen--; if ( slen <= 0 ) return 0; *s++ = 'x'; slen--; c = h[((tcode & 0x7000) / 0x1000) & 0xF]; if ( slen > 0 ) {*s++ = c; slen--;} c = h[((tcode & 0xF00) / 0x100) & 0xF]; if ( slen > 0 ) {*s++ = c; slen--;} c = h[((tcode & 0xF0) / 0x10) & 0xF]; if ( slen > 0 ) {*s++ = c; slen--;} c = h[tcode & 0xF]; if ( slen > 0 ) {*s++ = c; slen--;} } *typecode_name = c0; return typecode_name; } static bool Dump3dmChunk_EndReadChunkHelper( ON_BinaryArchive& file, ON__UINT64 offset0, ON__UINT32 tcode, ON__INT64 big_value, ON_TextLog& dump ) { const bool bShortChunk = (0 != (tcode & TCODE_SHORT)); const ON__UINT64 offset1 = file.CurrentPosition(); bool rc = file.EndRead3dmChunk(); if ( !rc ) { Dump3dmChunk_ErrorReportHelper(offset1,"EndRead3dmChunk() failed.",dump); } else if (!bShortChunk) { // The crc is read or skipped by the EndRead3dmChunk() call. // "extra" is the number of bytes we did not parse in the dump. ON__INT64 sizeof_crc = (0 != (TCODE_CRC & tcode)) ? 4 : 0; ON__INT64 sizeof_chunk_header = 4+file.SizeofChunkLength(); ON__INT64 delta = (offset1 > offset0) ? ((ON__INT64)(offset1 - offset0)) : -((ON__INT64)(offset0 - offset1)); const ON__INT64 extra = big_value - (delta+sizeof_crc-sizeof_chunk_header); if ( extra < 0 ) { Dump3dmChunk_ErrorReportHelper(offset0,"Read beyond end of chunk.",dump); rc = false; } } return rc; } static bool Dump3dmChunk_UserDataHeaderHelper( ON__UINT64 offset, ON_BinaryArchive& file, int major_userdata_version, int minor_userdata_version, ON_TextLog& dump ) { // TCODE_OPENNURBS_CLASS_USERDATA chunks have 2 uuids // the first identifies the type of ON_Object class // the second identifies that kind of user data ON_UUID userdata_classid = ON_nil_uuid; ON_UUID userdata_itemid = ON_nil_uuid; ON_UUID userdata_appid = ON_nil_uuid; // id of plug-in that owns the user data int userdata_copycount = -1; ON_Xform userdata_xform; bool bLastSavedAsGoo = false; int ud_archive_3dm_version = 0; int ud_archive_opennurbs_version = 0; bool rc = false; bool bCallEndRead3dmChunk = false; ON__UINT32 tcode = 0; ON__INT64 big_value = 0; const ON__UINT64 offset0 = file.CurrentPosition(); for(;;) { if ( 2 == major_userdata_version ) { rc = file.PeekAt3dmBigChunkType(&tcode,&big_value); if ( !rc ) { Dump3dmChunk_ErrorReportHelper(offset,"Unable to find the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump); break; } if ( TCODE_OPENNURBS_CLASS_USERDATA_HEADER != tcode ) { Dump3dmChunk_ErrorReportHelper(offset,"Unable to find the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump); rc = false; break; } rc = file.BeginRead3dmBigChunk(&tcode,&big_value); if ( !rc ) { Dump3dmChunk_ErrorReportHelper(offset,"Unable to read the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump); break; } if ( TCODE_OPENNURBS_CLASS_USERDATA_HEADER != tcode ) { Dump3dmChunk_ErrorReportHelper(offset,"Missing TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk header in a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump); Dump3dmChunk_EndReadChunkHelper(file,offset0,tcode,big_value,dump); rc = false; break; } bCallEndRead3dmChunk = true; } rc = file.ReadUuid( userdata_classid ); if ( !rc ) { Dump3dmChunk_ErrorReportHelper(offset,"ReadUuid() failed to read the user data class id.",dump); break; } dump.Print("UserData class id = "); dump.Print( userdata_classid ); const ON_ClassId* pUserDataClassId = ON_ClassId::ClassId(userdata_classid); if ( pUserDataClassId ) { const char* sClassName = pUserDataClassId->ClassName(); if ( sClassName ) { dump.Print(" (%s)",sClassName); } } dump.Print("\n"); rc = file.ReadUuid( userdata_itemid ); if ( !rc ) { Dump3dmChunk_ErrorReportHelper(offset,"ReadUuid() failed to read the user data item id.",dump); break; } dump.Print("UserData item id = "); dump.Print( userdata_itemid ); dump.Print("\n"); rc = file.ReadInt( &userdata_copycount ); if ( !rc ) { Dump3dmChunk_ErrorReportHelper(offset,"ReadInt() failed to read the user data copy count.",dump); break; } dump.Print("UserData copy count = %d\n",userdata_copycount); rc = file.ReadXform( userdata_xform ); if ( !rc ) { Dump3dmChunk_ErrorReportHelper(offset,"ReadXform() failed to read the user data xform.",dump); break; } if ( 2 != major_userdata_version ) break; if ( minor_userdata_version < 1 ) break; rc = file.ReadUuid( userdata_appid ); if ( !rc) { Dump3dmChunk_ErrorReportHelper(offset,"ReadUuid() failed to read the user data app plug-in id.",dump); break; } dump.Print("UserData app plug-in id = "); dump.Print( userdata_appid ); dump.Print("\n"); if ( minor_userdata_version < 2 ) break; rc = file.ReadBool(&bLastSavedAsGoo); if (!rc) { Dump3dmChunk_ErrorReportHelper(offset,"ReadBool() failed to read the user data header bSavedAsGoo value.",dump); break; } rc = file.ReadInt( &ud_archive_3dm_version ); if (!rc) { Dump3dmChunk_ErrorReportHelper(offset,"ReadBool() failed to read the user data header bSavedAsGoo value.",dump); break; } rc = file.ReadInt( &ud_archive_opennurbs_version ); if (!rc) { Dump3dmChunk_ErrorReportHelper(offset,"ReadBool() failed to read the user data header bSavedAsGoo value.",dump); break; } if ( bLastSavedAsGoo ) 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); else dump.Print("Userdata written by opennurbs %d in 3dm version %d.\n",ud_archive_opennurbs_version,ud_archive_3dm_version); break; } if ( bCallEndRead3dmChunk ) { if (!Dump3dmChunk_EndReadChunkHelper(file,offset0,tcode,big_value,dump)) { if (rc) { Dump3dmChunk_ErrorReportHelper(offset,"EndRead3dmChunk() failed to close the TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk.",dump); } rc = false; } } return rc; } unsigned int ON_BinaryArchive::Dump3dmChunk( ON_TextLog& dump, int recursion_depth ) { //ON_BinaryArchive& file = *this; const char* typecode_name = 0; bool bShortChunk = false; const ON__UINT64 offset0 = CurrentPosition(); unsigned int typecode = 0; ON__INT64 big_value; bool rc = BeginRead3dmBigChunk( &typecode, &big_value ); if (!rc) { Dump3dmChunk_ErrorReportHelper(offset0,"BeginRead3dmChunk() failed.",dump); } else { if ( 0 == typecode ) { Dump3dmChunk_ErrorReportHelper(offset0,"BeginRead3dmChunk() returned typecode = 0.",dump); EndRead3dmChunk(); return 0; } else { if ( 0 == recursion_depth ) { dump.Print("\n"); } ////bShortChunk = (0 != (typecode & TCODE_SHORT)); typecode_name = ON_BinaryArchive::TypecodeName(typecode); bShortChunk = (0 != (typecode & TCODE_SHORT)); if ( !DumpChunk_PrintHeaderInfo(offset0,typecode,big_value,typecode_name,dump) ) { EndRead3dmChunk(); return 0; } int major_userdata_version = -1; int minor_userdata_version = -1; switch( typecode ) { case TCODE_PROPERTIES_TABLE: case TCODE_SETTINGS_TABLE: case TCODE_BITMAP_TABLE: case TCODE_MATERIAL_TABLE: case TCODE_LAYER_TABLE: case TCODE_GROUP_TABLE: case TCODE_LIGHT_TABLE: case TCODE_FONT_TABLE: case TCODE_DIMSTYLE_TABLE: case TCODE_HATCHPATTERN_TABLE: case TCODE_LINETYPE_TABLE: case TCODE_TEXTURE_MAPPING_TABLE: case TCODE_HISTORYRECORD_TABLE: case TCODE_USER_TABLE: case TCODE_INSTANCE_DEFINITION_TABLE: case TCODE_OBJECT_TABLE: // start of a table { dump.PushIndent(); unsigned int record_typecode = 0; for (;;) { record_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( !record_typecode ) { break; } if ( TCODE_ENDOFTABLE == record_typecode ) { break; } } dump.PopIndent(); } break; case TCODE_PROPERTIES_OPENNURBS_VERSION: { dump.PushIndent(); dump.Print("Version of opennurbs that wrote this file: %lld\n",big_value); dump.PopIndent(); if (0 == m_3dm_opennurbs_version && big_value > 0 && ((ON__UINT64)big_value) < ON_UNSET_UINT_INDEX) ON_SetBinaryArchiveOpenNURBSVersion(*this,(ON__UINT32)big_value); } break; case TCODE_BITMAP_RECORD: { dump.PushIndent(); unsigned int bitmap_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( 0 == typecode ) typecode = bitmap_chunk_typecode; dump.PopIndent(); } break; case TCODE_MATERIAL_RECORD: { dump.PushIndent(); unsigned int material_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( 0 == typecode ) typecode = material_chunk_typecode; dump.PopIndent(); } break; case TCODE_LAYER_RECORD: { dump.PushIndent(); unsigned int material_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( 0 == typecode ) typecode = material_chunk_typecode; dump.PopIndent(); } break; case TCODE_GROUP_RECORD: { dump.PushIndent(); unsigned int group_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( 0 == typecode ) typecode = group_chunk_typecode; dump.PopIndent(); } break; case TCODE_FONT_RECORD: { dump.PushIndent(); unsigned int font_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( 0 == typecode ) typecode = font_chunk_typecode; dump.PopIndent(); } break; case TCODE_DIMSTYLE_RECORD: { dump.PushIndent(); unsigned int dimstyle_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( 0 == typecode ) typecode = dimstyle_chunk_typecode; dump.PopIndent(); } break; case TCODE_LIGHT_RECORD: { dump.PushIndent(); unsigned int light_chunk_typecode = 0; for (;;) { light_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( !light_chunk_typecode ) { break; } if ( TCODE_LIGHT_RECORD_END == light_chunk_typecode ) { break; } switch( light_chunk_typecode ) { //case TCODE_OBJECT_RECORD_TYPE: case TCODE_LIGHT_RECORD_ATTRIBUTES: case TCODE_LIGHT_RECORD_ATTRIBUTES_USERDATA: case TCODE_OPENNURBS_CLASS: break; default: { Dump3dmChunk_ErrorReportHelper(offset0,"Rogue chunk in light record.",dump); } } } dump.PopIndent(); } break; case TCODE_TEXTURE_MAPPING_RECORD: { dump.PushIndent(); unsigned int mapping_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( !typecode ) typecode = mapping_chunk_typecode; dump.PopIndent(); } break; case TCODE_HISTORYRECORD_RECORD: { dump.PushIndent(); unsigned int history_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( !typecode ) typecode = history_chunk_typecode; dump.PopIndent(); } break; case TCODE_HATCHPATTERN_RECORD: { dump.PushIndent(); unsigned int hatch_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( !typecode ) typecode = hatch_chunk_typecode; dump.PopIndent(); } break; case TCODE_INSTANCE_DEFINITION_RECORD: { dump.PushIndent(); unsigned int idef_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( 0 == typecode ) typecode = idef_chunk_typecode; dump.PopIndent(); } break; case TCODE_OBJECT_RECORD: { dump.PushIndent(); unsigned int object_chunk_typecode = 0; for (;;) { object_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( !object_chunk_typecode ) { break; } if ( TCODE_OBJECT_RECORD_END == object_chunk_typecode ) { break; } switch( object_chunk_typecode ) { case TCODE_OBJECT_RECORD_TYPE: case TCODE_OBJECT_RECORD_ATTRIBUTES: case TCODE_OBJECT_RECORD_ATTRIBUTES_USERDATA: case TCODE_OPENNURBS_CLASS: break; default: { Dump3dmChunk_ErrorReportHelper(offset0,"Rogue chunk in object record.",dump); } } } dump.PopIndent(); } break; case TCODE_OBJECT_RECORD_ATTRIBUTES: { dump.PushIndent(); if ( big_value < 14 ) { Dump3dmChunk_ErrorReportHelper(offset0,"Length of chunk is too small. Should be >= 14.",dump); } else { ON_UUID uuid = ON_nil_uuid; int layer_index = -99; int mj = -1; int mn = -1; if ( !Read3dmChunkVersion(&mj,&mn)) { Dump3dmChunk_ErrorReportHelper(offset0,"Read3dmChunkVersion() failed.",dump); } else if (!ReadUuid(uuid)) { Dump3dmChunk_ErrorReportHelper(offset0,"ReadUuid() failed.",dump); } else if ( !ReadInt(&layer_index) ) { Dump3dmChunk_ErrorReportHelper(offset0,"ReadInt() failed to read layer index.",dump); } else { dump.Print("Rhino object uuid: "); dump.Print(uuid); dump.Print("\n"); dump.Print("layer index: %d\n",layer_index); } } dump.PopIndent(); } break; case TCODE_OPENNURBS_CLASS: { dump.PushIndent(); unsigned int opennurbs_object_chunk_typecode = 0; for (;;) { opennurbs_object_chunk_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( !opennurbs_object_chunk_typecode ) { break; } if ( TCODE_OPENNURBS_CLASS_END == opennurbs_object_chunk_typecode ) { break; } switch( opennurbs_object_chunk_typecode ) { case TCODE_OPENNURBS_CLASS_UUID: break; case TCODE_OPENNURBS_CLASS_DATA: break; case TCODE_OPENNURBS_CLASS_USERDATA: break; default: { Dump3dmChunk_ErrorReportHelper(offset0,"Rogue chunk in OpenNURBS class record.",dump); } } } dump.PopIndent(); } break; case TCODE_OPENNURBS_CLASS_USERDATA: { if ( !Read3dmChunkVersion(&major_userdata_version, &minor_userdata_version ) ) { Dump3dmChunk_ErrorReportHelper(offset0,"Read3dmChunkVersion() failed to read TCODE_OPENNURBS_CLASS_USERDATA chunk version.",dump); } else { dump.PushIndent(); dump.Print("UserData chunk version: %d.%d\n", major_userdata_version, minor_userdata_version ); if ( 1 == major_userdata_version || 2 == major_userdata_version ) { const ON__UINT64 userdata_header_offset = CurrentPosition(); switch ( major_userdata_version ) { case 1: case 2: { // version 1 user data header information was not wrapped // in a chunk. // // version 2 user data header information is wrapped // in a TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk. if ( Dump3dmChunk_UserDataHeaderHelper( userdata_header_offset, *this, major_userdata_version, minor_userdata_version, dump ) ) { // a TCODE_ANONYMOUS_CHUNK contains user data goo int anon_typecode = Dump3dmChunk( dump, recursion_depth+1 ); if ( TCODE_ANONYMOUS_CHUNK != anon_typecode ) { Dump3dmChunk_ErrorReportHelper( offset0,"Userdata Expected a TCODE_ANONYMOUS_CHUNK chunk.",dump); } } } break; default: if ( major_userdata_version < 3 ) { } else { dump.Print("New user data format created after this diagnostic tool was written.\n"); } break; } } dump.PopIndent(); } } break; case TCODE_OPENNURBS_CLASS_UUID: case TCODE_USER_TABLE_UUID: { dump.PushIndent(); ON_UUID uuid = ON_nil_uuid; const ON_ClassId* pClassId = 0; if ( !ReadUuid( uuid ) ) { Dump3dmChunk_ErrorReportHelper(offset0,"ReadUuid() failed.",dump); } else { if ( typecode == TCODE_OPENNURBS_CLASS_UUID ) { dump.Print("OpenNURBS class id = "); pClassId = ON_ClassId::ClassId(uuid); } else if ( typecode == TCODE_USER_TABLE_UUID ) { dump.Print("User table id = "); } else { dump.Print("UUID = "); } dump.Print( uuid ); if ( pClassId ) { const char* sClassName = pClassId->ClassName(); if ( sClassName ) { dump.Print(" (%s)",sClassName); } } dump.Print("\n"); } dump.PopIndent(); } break; case TCODE_OPENNURBS_CLASS_USERDATA_HEADER: { // we should never get here because this chunk is parsed in the TCODE_OPENNURBS_CLASS_USERDATA case above. Dump3dmChunk_ErrorReportHelper(offset0,"Encountered TCODE_OPENNURBS_CLASS_USERDATA_HEADER chunk outside a TCODE_OPENNURBS_CLASS_USERDATA chunk.",dump); } break; case TCODE_ENDOFFILE: case TCODE_ENDOFFILE_GOO: { dump.PushIndent(); if ( big_value < 4 ) { Dump3dmChunk_ErrorReportHelper(offset0,"TCODE_ENDOFFILE chunk withlength < 4.",dump); } else { ON__UINT64 sizeof_file = 0; ReadEOFSizeOfFile(&sizeof_file); dump.Print("current position = %d stored size = %llu\n", CurrentPosition(), sizeof_file ); } dump.PopIndent(); } break; } } const ON__UINT64 offset1 = CurrentPosition(); if ( !EndRead3dmChunk(true) ) { Dump3dmChunk_ErrorReportHelper(offset1,"EndRead3dmChunk() failed.",dump); rc = false; } else if (!bShortChunk) { ON__INT64 delta = (offset1 > offset0) ? ((ON__INT64)(offset1 - offset0)) : -((ON__INT64)(offset0 - offset1)); const ON__INT64 extra = big_value - (delta-4-SizeofChunkLength()); if ( extra < 0 ) { Dump3dmChunk_ErrorReportHelper(offset0,"Read beyond end of chunk.",dump); } } } return typecode; } ON_Read3dmBufferArchive::ON_Read3dmBufferArchive( size_t sizeof_buffer, const void* buffer, bool bCopyBuffer, int archive_3dm_version, unsigned int archive_opennurbs_version ) : ON_BinaryArchive(ON::archive_mode::read3dm) , m_p(0) , m_buffer(0) , m_sizeof_buffer(0) , m_buffer_position(0) , m_reserved1(0) , m_reserved2(0) , m_reserved3(0) , m_reserved4(0) { if ( sizeof_buffer > 0 && 0 != buffer ) { if ( bCopyBuffer ) { m_p = onmalloc(sizeof_buffer); if ( 0 != m_p ) memcpy(m_p,buffer,sizeof_buffer); m_buffer = (const unsigned char*)m_p; } else { m_buffer = (const unsigned char*)buffer; } if ( m_buffer ) { m_sizeof_buffer = sizeof_buffer; SetArchive3dmVersion(archive_3dm_version); ON_SetBinaryArchiveOpenNURBSVersion(*this,archive_opennurbs_version); } } } ON_Read3dmBufferArchive::~ON_Read3dmBufferArchive() { if ( m_p ) onfree(m_p); } // ON_BinaryArchive overrides ON__UINT64 ON_Read3dmBufferArchive::Internal_CurrentPositionOverride() const { return (ON__UINT64)m_buffer_position; } bool ON_Read3dmBufferArchive::Internal_SeekFromCurrentPositionOverride( int offset ) { bool rc = false; if ( m_buffer ) { if (offset >= 0 ) { m_buffer_position += offset; rc = true; } else if ( size_t(-offset) <= m_buffer_position ) { m_buffer_position -= (size_t(-offset)); rc = true; } } return rc; } bool ON_Read3dmBufferArchive::Internal_SeekToStartOverride() { bool rc = false; if ( m_buffer ) { m_buffer_position = 0; rc = true; } return rc; } bool ON_Read3dmBufferArchive::AtEnd() const { return (m_buffer_position >= m_sizeof_buffer) ? true : false; } size_t ON_Read3dmBufferArchive::Internal_ReadOverride( size_t count, void* buffer ) { if ( count <= 0 || 0 == buffer ) return 0; size_t maxcount = ( m_sizeof_buffer > m_buffer_position ) ? (m_sizeof_buffer - m_buffer_position) : 0; if ( count > maxcount ) count = maxcount; if ( count > 0 ) { memcpy( buffer, m_buffer+m_buffer_position, count ); m_buffer_position += count; } return count; } size_t ON_Read3dmBufferArchive::Internal_WriteOverride( size_t, const void* ) { // ON_Read3dmBufferArchive does not support Write() and Flush() return 0; } bool ON_Read3dmBufferArchive::Flush() { // ON_Read3dmBufferArchive does not support Write() and Flush() return false; } size_t ON_Read3dmBufferArchive::SizeOfBuffer() const { return m_sizeof_buffer; } const void* ON_Read3dmBufferArchive::Buffer() const { return (const void*)m_buffer; } ON_Write3dmBufferArchive::ON_Write3dmBufferArchive( size_t initial_sizeof_buffer, size_t max_sizeof_buffer, int archive_3dm_version, unsigned int archive_opennurbs_version ) : ON_BinaryArchive(ON::archive_mode::write3dm) , m_p(0) , m_buffer(0) , m_sizeof_buffer(0) , m_max_sizeof_buffer(max_sizeof_buffer) , m_sizeof_archive(0) , m_buffer_position(0) , m_reserved1(0) , m_reserved2(0) , m_reserved3(0) , m_reserved4(0) { if ( initial_sizeof_buffer > 0 ) AllocBuffer(initial_sizeof_buffer); if ( archive_3dm_version < 2 ) { archive_3dm_version = ON_BinaryArchive::CurrentArchiveVersion(); archive_opennurbs_version = ON::Version(); } SetArchive3dmVersion(archive_3dm_version); ON_SetBinaryArchiveOpenNURBSVersion(*this,archive_opennurbs_version); } ON_Write3dmBufferArchive::~ON_Write3dmBufferArchive() { if ( m_p ) onfree(m_p); } void ON_Write3dmBufferArchive::AllocBuffer( size_t sz ) { if ( sz > m_sizeof_buffer && (m_max_sizeof_buffer <= 0 || sz <= m_max_sizeof_buffer) ) { if (sz < 2 * m_sizeof_buffer || 0 == m_sizeof_buffer) { sz = 2*m_sizeof_buffer; if (sz < 512) sz = 512; if (sz > m_max_sizeof_buffer && m_max_sizeof_buffer > 0) sz = m_max_sizeof_buffer; } m_p = onrealloc(m_p,sz); m_buffer = (unsigned char*)m_p; if ( 0 != m_buffer ) { memset( m_buffer + m_sizeof_buffer, 0, sz - m_sizeof_buffer ); m_sizeof_buffer = sz; } else { m_sizeof_buffer = 0; } } } // ON_BinaryArchive overrides ON__UINT64 ON_Write3dmBufferArchive::Internal_CurrentPositionOverride() const { return (ON__UINT64)m_buffer_position; } bool ON_Write3dmBufferArchive::Internal_SeekFromCurrentPositionOverride( int offset ) { bool rc = false; if ( m_buffer ) { if (offset >= 0 ) { m_buffer_position += offset; rc = true; } else if ( size_t(-offset) <= m_buffer_position ) { m_buffer_position -= (size_t(-offset)); rc = true; } } return rc; } bool ON_Write3dmBufferArchive::Internal_SeekToStartOverride() { m_buffer_position = 0; return true; } bool ON_Write3dmBufferArchive::AtEnd() const { return (m_buffer_position >= m_sizeof_buffer) ? true : false; } size_t ON_Write3dmBufferArchive::Internal_ReadOverride( size_t count, void* buffer ) { if ( count <= 0 || 0 == buffer ) return 0; size_t maxcount = ( m_sizeof_buffer > m_buffer_position ) ? (m_sizeof_buffer - m_buffer_position) : 0; if ( count > maxcount ) count = maxcount; if ( count > 0 ) { memcpy( buffer, m_buffer+m_buffer_position, count ); m_buffer_position += count; } return count; } size_t ON_Write3dmBufferArchive::Internal_WriteOverride( size_t sz, const void* buffer ) { if ( sz <= 0 || 0 == buffer ) return 0; if ( m_buffer_position + sz > m_sizeof_buffer ) { AllocBuffer(m_buffer_position + sz); } if ( m_buffer_position + sz > m_sizeof_buffer ) return 0; memcpy( m_buffer + m_buffer_position, buffer, sz ); m_buffer_position += sz; if ( m_buffer_position > m_sizeof_archive ) m_sizeof_archive = m_buffer_position; return sz; } bool ON_Write3dmBufferArchive::Flush() { // Nothing to flush return true; } size_t ON_Write3dmBufferArchive::SizeOfBuffer() const { return m_sizeof_buffer; } const void* ON_Write3dmBufferArchive::Buffer() const { return (const void*)m_buffer; } void* ON_Write3dmBufferArchive::HarvestBuffer() { void* buffer = m_buffer; m_p = 0; m_buffer = 0; m_sizeof_buffer = 0; m_sizeof_archive = 0; m_buffer_position = 0; return buffer; } size_t ON_Write3dmBufferArchive::SizeOfArchive() const { return m_sizeof_archive; } ON_BinaryArchiveBuffer::ON_BinaryArchiveBuffer( ON::archive_mode mode, ON_Buffer* buffer ) : ON_BinaryArchive(mode) , m_buffer(buffer) { if (nullptr != m_buffer) m_buffer->SeekFromStart(0); } ON_BinaryArchiveBuffer::~ON_BinaryArchiveBuffer() { m_buffer = 0; } bool ON_BinaryArchiveBuffer::SetBuffer( ON_Buffer* buffer ) { if ( 0 == m_buffer ) { m_buffer = buffer; if (nullptr != m_buffer) m_buffer->SeekFromStart(0); return true; } return false; } ON_Buffer* ON_BinaryArchiveBuffer::Buffer() const { return m_buffer; } ON__UINT64 ON_BinaryArchiveBuffer::Internal_CurrentPositionOverride() const { if ( 0 != m_buffer ) return m_buffer->CurrentPosition(); return 0; } bool ON_BinaryArchiveBuffer::Internal_SeekFromCurrentPositionOverride(int offset) { if ( 0 != m_buffer ) return m_buffer->SeekFromCurrentPosition(offset); return false; } bool ON_BinaryArchiveBuffer::Internal_SeekToStartOverride() { if ( 0 != m_buffer ) return m_buffer->SeekFromStart(0); return false; } bool ON_BinaryArchiveBuffer::AtEnd() const { if ( 0 != m_buffer ) return m_buffer->AtEnd(); return false; } size_t ON_BinaryArchiveBuffer::Internal_ReadOverride( size_t count, void* a ) { if ( 0 != m_buffer ) return (size_t)m_buffer->Read(count,a); return 0; } size_t ON_BinaryArchiveBuffer::Internal_WriteOverride( size_t count, const void* a ) { if ( 0 != m_buffer ) return (size_t)m_buffer->Write(count,a); return 0; } bool ON_BinaryArchiveBuffer::Flush() { if ( 0 != m_buffer ) return true; return false; } ON_3dmAnnotationContext::~ON_3dmAnnotationContext() { Internal_Destroy(); } ON_3dmAnnotationContext::ON_3dmAnnotationContext(const ON_3dmAnnotationContext& src) { Internal_CopyFrom(src); } ON_3dmAnnotationContext& ON_3dmAnnotationContext::operator=(const ON_3dmAnnotationContext& src) { if (this != &src) { Internal_Destroy(); Internal_CopyFrom(src); } return *this; } void ON_3dmAnnotationContext::Internal_CopyFrom(const ON_3dmAnnotationContext& src) { if (nullptr != src.m_managed_parent_dim_style) { m_managed_parent_dim_style = new ON_DimStyle(*src.m_managed_parent_dim_style); m_parent_dim_style = m_managed_parent_dim_style; } else { m_parent_dim_style = src.m_parent_dim_style; } if (nullptr != src.m_managed_override_dim_style) { m_managed_override_dim_style = new ON_DimStyle(*src.m_managed_override_dim_style); m_override_dim_style = m_managed_override_dim_style; } else { m_override_dim_style = src.m_override_dim_style; } if (nullptr != src.m_annotation_settings) { m_managed_annotation_settings = new ON_3dmAnnotationSettings(*src.m_managed_annotation_settings); m_annotation_settings = m_managed_annotation_settings; } else { m_annotation_settings = src.m_annotation_settings; } m_view_context = src.m_view_context; m_model_length_unit_system = src.m_model_length_unit_system; m_page_length_unit_system = src.m_page_length_unit_system; m_binary_archive = src.m_binary_archive; m_V5_3dm_archive_dim_style_index = src.m_V5_3dm_archive_dim_style_index; } void ON_3dmAnnotationContext::Internal_Destroy() { SetReferencedAnnotationSettings(nullptr); SetReferencedDimStyle(nullptr,nullptr,ON_UNSET_INT_INDEX); m_view_context = ON_3dmAnnotationContext::Default.m_view_context; m_model_length_unit_system = ON_3dmAnnotationContext::Default.m_model_length_unit_system; m_page_length_unit_system = ON_3dmAnnotationContext::Default.m_page_length_unit_system; m_binary_archive = ON_3dmAnnotationContext::Default.m_binary_archive; }; const ON_3dmAnnotationSettings& ON_3dmAnnotationContext::AnnotationSettings() const { return (nullptr != m_annotation_settings) ? *m_annotation_settings : ON_3dmSettings::Default.m_AnnotationSettings; } void ON_3dmAnnotationContext::SetReferencedAnnotationSettings( const ON_3dmAnnotationSettings* annotation_settings ) { if (nullptr != m_managed_annotation_settings) { delete m_managed_annotation_settings; m_managed_annotation_settings = nullptr; } m_annotation_settings = annotation_settings; } void ON_3dmAnnotationContext::SetManagedAnnotationSettings( const ON_3dmAnnotationSettings& annotation_settings ) { SetReferencedAnnotationSettings(nullptr); m_managed_annotation_settings = new ON_3dmAnnotationSettings(annotation_settings); m_annotation_settings = m_managed_annotation_settings; } bool ON_3dmAnnotationContext::AnnotationSettingsAreSet() const { return (nullptr != m_annotation_settings); } ON::active_space ON_3dmAnnotationContext::ViewContext() const { return m_view_context; } void ON_3dmAnnotationContext::SetViewContext( ON::active_space view_context ) { m_view_context = view_context; } ON::LengthUnitSystem ON_3dmAnnotationContext::ModelLengthUnitSystem() const { return m_model_length_unit_system; } void ON_3dmAnnotationContext::SetModelLengthUnitSystem( ON::LengthUnitSystem model_length_unit_system ) { m_model_length_unit_system = model_length_unit_system; } ON::LengthUnitSystem ON_3dmAnnotationContext::PageLengthUnitSystem() const { return m_page_length_unit_system; } void ON_3dmAnnotationContext::SetPageLengthUnitSystem( ON::LengthUnitSystem page_length_unit_system ) { m_page_length_unit_system = page_length_unit_system; } const ON_DimStyle& ON_3dmAnnotationContext::DimStyle() const { return (nullptr != m_override_dim_style) ? *m_override_dim_style : ParentDimStyle(); } const ON_DimStyle& ON_3dmAnnotationContext::ParentDimStyle() const { return (nullptr != m_parent_dim_style) ? *m_parent_dim_style : ON_DimStyle::Default; } void ON_3dmAnnotationContext::UpdateReferencedDimStyle( const class ON_DimStyle* old_pointer, const class ON_DimStyle* new_pointer ) { // this function is called to update referenced dimstyle pointers. if (nullptr == old_pointer || nullptr == new_pointer) return; if (nullptr == old_pointer || old_pointer == m_managed_parent_dim_style || old_pointer == m_managed_override_dim_style) { ON_ERROR("Invalid old_pointer value."); return; } if (m_parent_dim_style == old_pointer) m_parent_dim_style = new_pointer; if (m_override_dim_style == old_pointer) m_override_dim_style = new_pointer; } void ON_3dmAnnotationContext::SetReferencedDimStyle( const ON_DimStyle* parent_dim_style, const ON_DimStyle* override_dim_style, int V5_3dm_archive_index ) { if (nullptr != m_managed_parent_dim_style) { delete m_managed_parent_dim_style; m_managed_parent_dim_style = nullptr; } if (nullptr != m_managed_override_dim_style) { delete m_managed_override_dim_style; m_managed_override_dim_style = nullptr; } m_parent_dim_style = parent_dim_style; m_override_dim_style = (nullptr != parent_dim_style && nullptr != override_dim_style && parent_dim_style->IdIsNotNil() && parent_dim_style->Id() == override_dim_style->ParentId() && override_dim_style->HasOverrides() ) ? override_dim_style : nullptr; m_V5_3dm_archive_dim_style_index = V5_3dm_archive_index; } void ON_3dmAnnotationContext::SetManagedDimStyle( const ON_DimStyle& parent_dim_style, const ON_DimStyle* override_dim_style, int V5_3dm_archive_index ) { if (nullptr != override_dim_style) { if (parent_dim_style.IdIsNil() || override_dim_style->ParentId() != parent_dim_style.Id() || false == override_dim_style->HasOverrides()) override_dim_style = nullptr; } ON_DimStyle* managed_parent = new ON_DimStyle(parent_dim_style); ON_DimStyle* managed_override = (nullptr == override_dim_style) ? nullptr : new ON_DimStyle(*override_dim_style); SetReferencedDimStyle(nullptr,nullptr,ON_UNSET_INT_INDEX); m_managed_parent_dim_style = managed_parent; m_managed_override_dim_style = managed_override; } bool ON_3dmAnnotationContext::DimStyleIsSet() const { return (nullptr != m_parent_dim_style); } /* Returns: If the dimstyle is not set or it has a nil parent id, then DimStyleId() is returned. Otherwise the parent id is returned. */ ON_UUID ON_3dmAnnotationContext::ParentDimStyleId() const { return (nullptr != m_parent_dim_style) ? m_parent_dim_style->Id() : ON_nil_uuid; } int ON_3dmAnnotationContext::V5_ArchiveDimStyleIndex() const { return m_V5_3dm_archive_dim_style_index; } bool ON_3dmAnnotationContext::IsOverrideDimStyle() const { return (nullptr != m_override_dim_style); } const class ON_BinaryArchive* ON_3dmAnnotationContext::BinaryArchive() const { return m_binary_archive; } void ON_3dmAnnotationContext::SetReferencedBinaryArchive( const class ON_BinaryArchive* binary_archive ) { m_binary_archive = binary_archive; } bool ON_3dmAnnotationContext::BinaryArchiveIsSet() const { return (nullptr != m_binary_archive); }