// // Copyright (c) 1993-2022 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 ////////////////////////////////////////////////////////////////////////////////////////// // // ON_3dmRevisionHistory // ON_3dmRevisionHistory::ON_3dmRevisionHistory() { memset( &m_create_time, 0, sizeof(m_create_time) ); memset( &m_last_edit_time, 0, sizeof(m_last_edit_time) ); } ON_3dmRevisionHistory ON_3dmRevisionHistory::FirstRevision() { ON_3dmRevisionHistory first_revision; first_revision.NewRevision(); return first_revision; } static int ON_CompareRevisionHistoryTime( const struct tm* time0, const struct tm* time1 ) { if ( 0 == time0 || 0 == time1 ) { if ( 0 != time0 ) return 1; if ( 0 != time1 ) return -1; return 0; } if (time0->tm_year < time1->tm_year) return -1; if (time0->tm_year > time1->tm_year) return 1; if (time0->tm_mon < time1->tm_mon) return -1; if (time0->tm_mon > time1->tm_mon) return 1; if (time0->tm_mday < time1->tm_mday) return -1; if (time0->tm_mday > time1->tm_mday) return 1; if (time0->tm_hour < time1->tm_hour) return -1; if (time0->tm_hour > time1->tm_hour) return 1; if (time0->tm_min < time1->tm_min) return -1; if (time0->tm_min > time1->tm_min) return 1; if (time0->tm_sec < time1->tm_sec) return -1; if (time0->tm_sec > time1->tm_sec) return 1; return 0; } bool ON_3dmRevisionHistory::CreateTimeIsSet() const { struct tm jan_1_1970; memset(&jan_1_1970,0,sizeof(jan_1_1970)); jan_1_1970.tm_mday = 1; /* day of the month - [1,31] */ jan_1_1970.tm_year = 70; /* years since 1900 */ return ( ON_CompareRevisionHistoryTime(&jan_1_1970,&m_create_time) <= 0 ); } /* Returns: true if m_last_edit_time is >= January 1, 1970 */ bool ON_3dmRevisionHistory::LastEditedTimeIsSet() const { struct tm jan_1_1970; memset(&jan_1_1970,0,sizeof(jan_1_1970)); jan_1_1970.tm_mday = 1; /* day of the month - [1,31] */ jan_1_1970.tm_year = 70; /* years since 1900 */ return ( ON_CompareRevisionHistoryTime(&jan_1_1970,&m_last_edit_time) <= 0 ); } bool ON_3dmRevisionHistory::IsEmpty() const { if ( 0 == m_revision_count && !CreateTimeIsSet() && !LastEditedTimeIsSet() && m_sCreatedBy.IsEmpty() && m_sLastEditedBy.IsEmpty() ) { return true; } return false; } bool ON_3dmRevisionHistory::IsValid() const { return ( LastEditedTimeIsSet() && ON_CompareRevisionHistoryTime(&m_create_time, &m_last_edit_time) <= 0 ); } int ON_3dmRevisionHistory::NewRevision() { if ( 0 == m_revision_count ) *this = ON_3dmRevisionHistory::Empty; struct tm current_time; memset(¤t_time,0,sizeof(current_time)); { time_t gmt = time(0); const struct tm* t = gmtime(&gmt); if ( t ) current_time = *t; } m_last_edit_time = current_time; #if defined(ON_RUNTIME_WIN) // use Windows ::GetUserNameW() to get current user name wchar_t current_user[512]; memset( current_user, 0, sizeof(current_user) ); ULONG len = 510; if( !::GetUserNameW(current_user,&len) ) current_user[0] = 0; m_sLastEditedBy = current_user; #endif #if defined(ON_RUNTIME_COCOA_AVAILABLE) m_sLastEditedBy = NSFullUserName().UTF8String; #endif if ( m_revision_count <= 0 ) { m_revision_count = 0; m_sCreatedBy = m_sLastEditedBy; m_create_time = current_time; }; m_revision_count++; return m_revision_count; } bool ON_3dmRevisionHistory::Write( ON_BinaryArchive& file ) const { bool rc = file.Write3dmChunkVersion( 1, 0 ); if (rc) rc = file.WriteString( m_sCreatedBy ); if (rc) rc = file.WriteTime( m_create_time ); if (rc) rc = file.WriteString( m_sLastEditedBy ); if (rc) rc = file.WriteTime(m_last_edit_time ); if (rc) rc = file.WriteInt( m_revision_count ); return rc; } bool ON_3dmRevisionHistory::Read( ON_BinaryArchive& file ) { *this = ON_3dmRevisionHistory::Empty; int major_version = 0; int minor_version = 0; bool rc = file.Read3dmChunkVersion( &major_version, &minor_version ); if ( rc && major_version == 1 ) { rc = file.ReadString( m_sCreatedBy ); if (rc) rc = file.ReadTime( m_create_time ); if (rc) rc = file.ReadString( m_sLastEditedBy ); if (rc) rc = file.ReadTime(m_last_edit_time ); if (rc) rc = file.ReadInt( &m_revision_count ); } return rc; } void ON_3dmRevisionHistory::Dump( ON_TextLog& dump ) const { if ( IsEmpty() ) { dump.Print("Empty revision history.\n"); } else { const wchar_t* ws = static_cast< const wchar_t* >(m_sCreatedBy); if ( !ws ) ws = L""; dump.Print("Created by: %ls\n", ws ); dump.Print("Created on: "); dump.PrintTime(m_create_time); dump.Print("\n"); ws = static_cast< const wchar_t* >(m_sLastEditedBy); if ( !ws ) ws = L""; dump.Print("Last edited by: %ls\n", ws ); dump.Print("Last edited on: "); dump.PrintTime(m_last_edit_time); dump.Print("\n"); dump.Print("Revision count: %d\n",m_revision_count); } } ////////////////////////////////////////////////////////////////////////////////////////// // // ON_3dmNotes // const ON_3dmNotes ON_3dmNotes::Empty; ON_3dmNotes::ON_3dmNotes() : m_bVisible(0) , m_bHTML(0) , m_window_left(0) , m_window_top(0) , m_window_right(0) , m_window_bottom(0) {} ON_3dmNotes::~ON_3dmNotes() { m_notes.Destroy(); } bool ON_3dmNotes::IsValid() const { return m_notes.IsEmpty() ? false : true; } bool ON_3dmNotes::IsEmpty() const { return m_notes.IsEmpty(); } bool ON_3dmNotes::Read( ON_BinaryArchive& file ) { *this = ON_3dmNotes::Empty; bool rc = false; for(;;) { int i; int major_version = 0; int minor_version = 0; if ( !file.Read3dmChunkVersion( &major_version, &minor_version ) ) break; if ( 1 != major_version == 1 ) break; i = m_bHTML; if ( !file.ReadInt( &i ) ) break; m_bHTML = i ? true : false; if (!file.ReadString( m_notes )) break; i = m_bVisible; if (!file.ReadInt( &i )) break; m_bVisible = i ? true : false; if (!file.ReadInt( &m_window_left )) break; if (!file.ReadInt( &m_window_top )) break; if (!file.ReadInt( &m_window_right )) break; if (!file.ReadInt( &m_window_bottom )) break; rc = true; break; } return rc; } bool ON_3dmNotes::Write( ON_BinaryArchive& file ) const { bool rc = file.Write3dmChunkVersion(1,0); if ( rc ) rc = file.WriteInt( m_bHTML ); if ( rc ) rc = file.WriteString( m_notes ); if ( rc ) rc = file.WriteInt( m_bVisible ); if ( rc ) rc = file.WriteInt( m_window_left ); if ( rc ) rc = file.WriteInt( m_window_top ); if ( rc ) rc = file.WriteInt( m_window_right ); if ( rc ) rc = file.WriteInt( m_window_bottom ); return rc; } void ON_3dmNotes::Dump(ON_TextLog& dump) const { const wchar_t* s = static_cast< const wchar_t* >(m_notes); if ( s ) dump.PrintWrappedText(s); dump.Print("\n"); } ////////////////////////////////////////////////////////////////////////////////////////// // // ON_3dmApplication // const ON_3dmApplication ON_3dmApplication::Empty; ON_3dmApplication::ON_3dmApplication() { } ON_3dmApplication::~ON_3dmApplication() { m_application_name.Empty(); m_application_URL.Empty(); m_application_details.Empty(); } void ON_3dmApplication::Dump( ON_TextLog& dump ) const { const wchar_t* s = static_cast< const wchar_t* >(m_application_name); if ( s ) dump.Print("Name: %ls\n",s); s = static_cast< const wchar_t* >(m_application_URL); if ( s ) dump.Print("URL: %ls\n",s); s = static_cast< const wchar_t* >(m_application_details); if ( s ) dump.Print("Details: %ls\n",s); } bool ON_3dmApplication::IsValid() const { return m_application_name.IsEmpty() ? false : true; } bool ON_3dmApplication::IsEmpty() const { return m_application_name.IsEmpty() && m_application_URL.IsEmpty() && m_application_details.IsEmpty(); } bool ON_3dmApplication::Read( ON_BinaryArchive& file ) { int major_version = 0; int minor_version = 0; bool rc = file.Read3dmChunkVersion( &major_version, &minor_version ); if (rc) rc = file.ReadString( m_application_name ); if (rc) rc = file.ReadString( m_application_URL ); if (rc) rc = file.ReadString( m_application_details ); return rc; } bool ON_3dmApplication::Write( ON_BinaryArchive& file ) const { bool rc = file.Write3dmChunkVersion( 1, 0 ); if (rc) rc = file.WriteString( m_application_name ); if (rc) rc = file.WriteString( m_application_URL ); if (rc) rc = file.WriteString( m_application_details ); return rc; } ////////////////////////////////////////////////////////////////////////////////////////// // // ON_3dmProperties // bool ON_3dmProperties::IsEmpty() const { return m_RevisionHistory.IsEmpty() && m_PreviewImage.IsEmpty() && m_Notes.IsEmpty() && m_Application.IsEmpty() && m_3dmArchiveFullPathName.IsEmpty(); } void ON_SetBinaryArchiveOpenNURBSVersion(ON_BinaryArchive& file, unsigned int value) { if ( ON_VersionNumberIsValid(value) ) { // can be yyyymmddn or a number from ON_VersionNumberConstruct(). file.m_3dm_opennurbs_version = value; } else if ( ON_VersionNumberIsYearMonthDateFormat(file.Archive3dmVersion(),value) ) { // can be yyyymmddn or a number from ON_VersionNumberConstruct(). if ( 9 == (value%10) && value <= 201712319 ) { // 9 marked debug versions. unsigned int n = file.Archive3dmVersion(); if ( n >= 50 && 0 == (n%10) ) n /= 10; if ( n > 0 && n < 9 ) value = ((value/10)*10) + n; } file.m_3dm_opennurbs_version = value; } else { ON_ERROR("ON_SetBinaryArchiveOpenNURBSVersion - invalid opennurbs version"); file.m_3dm_opennurbs_version = 0; } } bool ON_3dmProperties::Read(ON_BinaryArchive& file ) { *this = ON_3dmProperties::Empty; bool rc = true; unsigned int tcode; ON__INT64 value; for(;;) { rc = file.BeginRead3dmBigChunk( &tcode, &value ); if ( !rc ) break; switch(tcode) { case TCODE_PROPERTIES_OPENNURBS_VERSION: { unsigned int opennurbs_version = 0; if ( value > 0 && value <= 0xFFFFFFFFll) { opennurbs_version = (unsigned int)((ON__UINT64)value); } if ( !ON_VersionNumberIsValid(opennurbs_version) && !ON_VersionNumberIsYearMonthDateFormat(file.Archive3dmVersion(),opennurbs_version) ) { ON_ERROR("ON_3dmProperties::Read - TCODE_PROPERTIES_OPENNURBS_VERSION corrupt value"); rc = false; } ON_SetBinaryArchiveOpenNURBSVersion(file,opennurbs_version); } break; case TCODE_PROPERTIES_AS_FILE_NAME: rc = file.ReadString(m_3dmArchiveFullPathName); break; case TCODE_PROPERTIES_REVISIONHISTORY: // file creation/revision information rc = m_RevisionHistory.Read(file); break; case TCODE_PROPERTIES_NOTES: // file notes rc = m_Notes.Read(file); break; case TCODE_PROPERTIES_PREVIEWIMAGE: // uncompressed preview image rc = m_PreviewImage.ReadUncompressed(file); break; case TCODE_PROPERTIES_COMPRESSED_PREVIEWIMAGE: // compressed preview image rc = m_PreviewImage.ReadCompressed(file); break; case TCODE_PROPERTIES_APPLICATION: // application that created 3dm file rc = m_Application.Read(file); break; default: // information added in future will be skipped by file.EndRead3dmChunk() break; } if ( !file.EndRead3dmChunk() ) rc = false; if (!rc) break; if ( TCODE_ENDOFTABLE == tcode ) break; } return rc; } bool ON_3dmProperties::Write(ON_BinaryArchive& file) const { bool rc = true; // This short chunk identifies the version of OpenNURBS that was used to write this file. const unsigned int version_number_to_write = ON_BinaryArchive::ArchiveOpenNURBSVersionToWrite(file.Archive3dmVersion(), ON::Version()); rc = file.BeginWrite3dmChunk(TCODE_PROPERTIES_OPENNURBS_VERSION, version_number_to_write); if (rc) rc = file.EndWrite3dmChunk(); if (!rc) return false; // This chunk added November 5, 2015 const ON_wString archive_full_path = file.ArchiveFullPath().IsEmpty() ? m_3dmArchiveFullPathName : file.ArchiveFullPath(); if (rc && archive_full_path.IsNotEmpty()) { if (!file.BeginWrite3dmChunk(TCODE_PROPERTIES_AS_FILE_NAME, 0)) return false; rc = file.WriteString(file.ArchiveFullPath()); if (!file.EndWrite3dmChunk()) rc = false; if (!rc) return false; } // optional TCODE_PROPERTIES_REVISIONHISTORY chunk - file creation/revision information if ( rc && m_RevisionHistory.IsValid() ) { rc = file.BeginWrite3dmChunk(TCODE_PROPERTIES_REVISIONHISTORY,0); if ( rc ) { rc = m_RevisionHistory.Write(file); if ( !file.EndWrite3dmChunk() ) rc = false; } } // optional TCODE_PROPERTIES_NOTES chunk - file notes if ( rc && m_Notes.IsValid() ) { rc = file.BeginWrite3dmChunk(TCODE_PROPERTIES_NOTES,0); if ( rc ) { rc = m_Notes.Write(file); if ( !file.EndWrite3dmChunk() ) rc = false; } } //// When merging Mac code please note that the //// TCODE_PROPERTIES_PREVIEWIMAGE chunk is OBSOLETE. //// DO NOT WRITE THEM IN V6 FILES. If performance is an //// issue, we will address it some other way. // optional TCODE_PROPERTIES_COMPRESSED_PREVIEWIMAGE chunk - bitmap preview if ( rc && m_PreviewImage.IsValid() && file.Save3dmPreviewImage()) { rc = file.BeginWrite3dmChunk(TCODE_PROPERTIES_COMPRESSED_PREVIEWIMAGE,0); if ( rc ) { rc = m_PreviewImage.WriteCompressed(file); if ( !file.EndWrite3dmChunk() ) rc = false; } } // optional TCODE_PROPERTIES_APPLICATION chunk - application information if ( rc && m_Application.IsValid() ) { rc = file.BeginWrite3dmChunk(TCODE_PROPERTIES_APPLICATION,0); if ( rc ) { rc = m_Application.Write(file); if ( !file.EndWrite3dmChunk() ) rc = false; } } // required TCODE_ENDOFTABLE chunk - marks end of properties table if ( rc ) { rc = file.BeginWrite3dmChunk( TCODE_ENDOFTABLE, 0 ); if ( rc ) { if ( !file.EndWrite3dmChunk() ) rc = false; } } return rc; } void ON_3dmProperties::Dump( ON_TextLog& dump ) const { dump.Print("Revision history:\n"); dump.PushIndent(); m_RevisionHistory.Dump(dump); dump.PopIndent(); dump.Print("\n"); dump.Print("Notes:\n"); if ( m_Notes.m_notes.Length() > 0 ) { dump.PushIndent(); m_Notes.Dump(dump); dump.PopIndent(); } dump.Print("\n"); dump.Print("Application information:\n"); dump.PushIndent(); m_Application.Dump(dump); dump.PopIndent(); if ( m_PreviewImage.IsValid() ) { dump.Print("\n"); dump.Print("Preview image:\n"); dump.PushIndent(); m_PreviewImage.Dump(dump); dump.PopIndent(); } }