Files
opennurbs/opennurbs_archive.cpp
2025-08-12 12:33:07 -07:00

18970 lines
504 KiB
C++

//
// 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 <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
#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_ENUM_TO_STRING_CASE(ON::RuntimeEnvironment::WebAssembly);
}
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<ON_3dmTableStatusLink*>(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<const wchar_t*>(m_archive_file_name);
}
const wchar_t* ON_BinaryArchive::ArchiveDirectoryNameAsPointer() const
{
return static_cast<const wchar_t*>(m_archive_directory_name);
}
const wchar_t* ON_BinaryArchive::ArchiveFullPathAsPointer() const
{
return static_cast<const wchar_t*>(m_archive_full_path);
}
const wchar_t* ON_BinaryArchive::ArchiveSavedAsFullPathPointer() const
{
return static_cast<const wchar_t*>(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<const wchar_t*>(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<const wchar_t*>(archive_directory_name),
static_cast<const wchar_t*>(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;
default:
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;
default:
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<unsigned int>(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 a single 8 bit char
char* p
)
{
return ReadByte(1, p);
}
bool
ON_BinaryArchive::ReadChar( // Read an array of 8 bit signed chars
size_t count, // number of chars to read
ON__INT8* 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
ON__UINT8* p
)
{
return ReadByte( count, p );
}
bool
ON_BinaryArchive::ReadChar( // Read a single 8 bit signed char
ON__INT8* p
)
{
return ReadByte( 1, p );
}
bool
ON_BinaryArchive::ReadChar( // Read a single 8 bit unsigned char
ON__UINT8* 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 signed chars to read
ON__INT16* 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
ON__UINT16* p
)
{
return ReadShort( count, (short*)p );
}
bool
ON_BinaryArchive::ReadShort( // Read a single 16 bit signed short
ON__INT16* p
)
{
return ReadShort( 1, p );
}
bool
ON_BinaryArchive::ReadShort( // Read a single 16 bit unsigned short
ON__UINT16* p
)
{
return ReadShort( 1, p );
}
bool
ON_BinaryArchive::ReadInt32( // Read an array of 32 bit signed integers
size_t count, // number of 32 bit signed 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 signed integers
size_t count, // number of signed chars to read
ON__INT32* 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 unsigned integers
size_t count, // number of unsigned chars to read
ON__UINT32* p
)
{
return ReadInt( count, (int*)p );
}
bool
ON_BinaryArchive::ReadInt( // Read a single 32 bit signed integer
ON__INT32* p
)
{
return ReadInt( 1, p );
}
bool
ON_BinaryArchive::ReadInt( // Read a single 32 bit unsigned integer
ON__UINT32* p
)
{
return ReadInt( 1, p );
}
bool ON_BinaryArchive::ReadBigInt( // Read an array of 64 bit signed integers
size_t count,
ON__INT64* p
)
{
return ReadInt64(1,p);
}
bool ON_BinaryArchive::ReadBigInt( // Read an array of 64 bit unsigned integers
size_t count,
ON__UINT64* p
)
{
return ReadInt64(1,(ON__INT64*)p);
}
bool ON_BinaryArchive::ReadBigInt( // Read a single 64 bit signed 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 signed integers
size_t count, // number of signed integers 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
)
{
#pragma ON_PRAGMA_WARNING_PUSH
#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4996)
#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
return ReadLong( count, (long*)p );
#pragma ON_PRAGMA_WARNING_POP
}
bool
ON_BinaryArchive::ReadLong( // Read a single 32 bit signed integer
long* p
)
{
#pragma ON_PRAGMA_WARNING_PUSH
#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4996)
#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
return ReadLong( 1, (long*)p );
#pragma ON_PRAGMA_WARNING_POP
}
bool
ON_BinaryArchive::ReadLong( // Read a single 32 bit unsigned integer
unsigned long* p
)
{
#pragma ON_PRAGMA_WARNING_PUSH
#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4996)
#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
return ReadLong( 1, (long*)p );
#pragma ON_PRAGMA_WARNING_POP
}
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<ON__UINT16> 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<ON_MappingChannel>& 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<ON_MaterialRef>& 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<ON_MappingRef>& 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<ON_ObjRef>& 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<ON_ObjRef_IRefID>& 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<ON_MappingChannel>& 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<ON_MaterialRef>& 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<class ON_Layer>& 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<class ON_Layer*>& 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<ON_MappingRef>& 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<ON_ObjRef>& 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<ON_ObjRef_IRefID>& 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<ON_DisplayMaterialRef>& 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<ON_String>& 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<ON_wString>& 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<ON_DisplayMaterialRef>& 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<ON_String>& 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<ON_wString>& 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<bool>& 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<char>& 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<ON__INT8>& 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<ON__INT16>& 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<ON__INT32>& 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<ON__UINT8>& 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<ON__UINT16>& 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<ON__UINT32>& 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<float>& 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<double>& 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<ON_Color>& 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<ON_2dPoint>& 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<ON_3dPoint>& 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<ON_4dPoint>& 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<ON_2dVector>& 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<ON_3dVector>& 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<ON_Xform>& 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<ON_2fPoint>& 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<ON_3fPoint>& 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<ON_4fPoint>& 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<ON_2fVector>& 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<ON_3fVector>& 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<ON_UUID>& 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<ON_UuidIndex>& 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<ON_UuidPtr>& 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<ON_UUID>& 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<ON_UuidIndex>& 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<ON_UuidPtr>& 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<ON_LinetypeSegment>& 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<ON_LinetypeSegment>& 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<unsigned int>(ON_LinetypeSegment::eSegType::stLine);
break;
case 1:
i = static_cast<unsigned int>(ON_LinetypeSegment::eSegType::stSpace);
break;
case ON_UNSET_UINT_INDEX:
i = static_cast<unsigned int>(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<unsigned int>(seg.m_seg_type);
break;
}
rc = WriteInt(i);
}
return rc;
}
bool
ON_BinaryArchive::ReadArray( ON_SimpleArray<ON_SurfaceCurvature>& 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<ON_ClippingPlaneInfo>& 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 a single 8 bit char
char c
)
{
return WriteByte(1, &c);
}
bool
ON_BinaryArchive::WriteChar( // Write an array of 8 bit signed chars
size_t count, // number of chars to write
const ON__INT8* 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 ON__UINT8* p
)
{
return WriteByte( count, p );
}
bool
ON_BinaryArchive::WriteChar( // Write a single 8 bit signed char
ON__INT8 c
)
{
return WriteByte( 1, &c );
}
bool
ON_BinaryArchive::WriteChar( // Write a single 8 bit unsigned char
ON__UINT8 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 ON__INT16* 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 ON__UINT16* p
)
{
return WriteShort( count, (const short*)p );
}
bool
ON_BinaryArchive::WriteShort( // Write a single 16 bit short
ON__INT16 s
)
{
return WriteShort( 1, &s );
}
bool
ON_BinaryArchive::WriteShort( // Write a single 16 bit unsigned short
ON__UINT16 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 ON__INT32* 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 ON__UINT32* p
)
{
return WriteInt( count, (const int*)p );
}
bool
ON_BinaryArchive::WriteInt( // Write a single 32 bit signed integer
ON__INT32 i
)
{
return WriteInt( 1, &i );
}
bool
ON_BinaryArchive::WriteInt( // Write a single 32 bit unsigned integer
ON__UINT32 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
)
{
#pragma ON_PRAGMA_WARNING_PUSH
#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4996)
#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
return WriteLong( count, (const long*)p );
#pragma ON_PRAGMA_WARNING_POP
}
bool
ON_BinaryArchive::WriteLong( // Write a single long
long i
)
{
#pragma ON_PRAGMA_WARNING_PUSH
#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4996)
#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
return WriteLong( 1, &i );
#pragma ON_PRAGMA_WARNING_POP
}
bool
ON_BinaryArchive::WriteLong( // Write a single unsigned long
unsigned long i
)
{
#pragma ON_PRAGMA_WARNING_PUSH
#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4996)
#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
return WriteLong( 1, &i );
#pragma ON_PRAGMA_WARNING_POP
}
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<const wchar_t*>(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<ON__UINT16> 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<bool>& 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<char>& 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<ON__INT8>& 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<ON__INT16>& 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<ON__INT32>& 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<ON__UINT8>& 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<ON__UINT16>& 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<ON__UINT32>& 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<float>& 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<double>& 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<ON_Color>& 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<ON_2dPoint>& 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<ON_3dPoint>& 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<ON_4dPoint>& 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<ON_2dVector>& 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<ON_3dVector>& 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<ON_Xform>& 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<ON_2fPoint>& 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<ON_3fPoint>& 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<ON_4fPoint>& 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<ON_2fVector>& 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<ON_3fVector>& 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<ON_SurfaceCurvature>& 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<ON_ClippingPlaneInfo>& 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;
if (m_user_data_filter.Count() <= 0) //ALB - Retaining the less-than from previous code although it seems spurious.
return true; //Empty filter, always serialize. See docs for m_user_data_filter
//Validate the assumptions on the docs for m_user_data_filter.
ON_ASSERT(m_user_data_filter[0].m_application_id == ON_nil_uuid);
ON_ASSERT(m_user_data_filter[0].m_item_id == ON_nil_uuid);
ON_ASSERT(m_user_data_filter[0].m_precedence == 0);
// {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;
}
}
//See the previous version of this code.
const bool bReturnNilItemIdImmediately = (m_3dm_version > 0);
bool bSerialize = m_user_data_filter[0].m_bSerialize;
bool bFoundNilItemId = false;
const int last = m_user_data_filter.Count() - 1;
for (int i = last; i > 0; i--) //ALB: Backwards. The first element of the table is special and should not be considered.
{
const ON_UserDataItemFilter& filter_item = m_user_data_filter[i];
if (application_id == filter_item.m_application_id)
{
const bool bNilId = item_id == ON_nil_uuid;
if (item_id == filter_item.m_item_id || (bNilId && bReturnNilItemIdImmediately))
{
bSerialize = filter_item.m_bSerialize;
break;
}
if (bNilId && !bFoundNilItemId)
{
bFoundNilItemId = true;
bSerialize = filter_item.m_bSerialize;
}
}
}
return bSerialize;
}
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<const ON_Curve*>(&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<const ON_Surface*>(&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;
case ON::object_type::subd_object:
{
if (m_3dm_version >= 60)
break;
const ON_SubD* subd = ON_SubD::Cast(&model_object);
if (nullptr == subd)
break;
// Use a SubD mesh proxy for V5 and earlier file formats.
std::unique_ptr<ON_Mesh> mesh(ON_SubDMeshProxyUserData::MeshProxyFromSubD(subd));
if (nullptr == mesh)
return false;
return Internal_WriteObject(*mesh.get());
}
break;
default:
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<ON_Object&>(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<ON_Object&>(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)
{
for (;;)
{
if (bSupressPartiallyReadChunkWarning)
{
// The calling code expects there to be a partially read chunk.
break;
}
// The calling code had no reason to supress warnings about this chunk
// being partially read.
const bool bIsV1EndOfFile = this->Archive3dmVersion() == 1 && 0 != (m_error_message_mask & 0x02);
if (bIsV1EndOfFile)
{
// 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.
break;
}
// m_3dm_opennurbs_version = version of opennurbs that wrote this 3dm file.
// ON::Version() = this version of opennurbs.
if (ON_VersionNumberCompare(m_3dm_opennurbs_version, ON::Version(), 2) <= 0)
{
// We are reading a file that was written by this version or an earlier version of opennurbs.
// This chunk should have been completely read.
// Typically, this is a bug that can be fixed after carefully studying why it occured.
// Either there is a bug in the reading or writing of the chunk or new informaton was
// added at the end of a chunk and the opennurbs major version or YYMMDD was not
// correctly set.
// In rare cases, somebody did something more seriously wrong and likely harder to figure out.
//
// In any case, issue a warning and continue reading. This is not a fatal problem but
// it indicates information is being lost.
ON_WARNING("ON_BinaryArchive::EndRead3dmChunk: partially read chunk - skipping bytes at end of current chunk.");
}
break;
}
}
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;
if (0 != m_reference_model_serial_number || 0 != m_instance_definition_model_serial_number)
m_bCheckForRemappedIds = true;
}
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;
}
void ON_BinaryArchive::SetCheckForRemappedIds(
bool bCheckForRemappedIds
)
{
this->m_bCheckForRemappedIds = bCheckForRemappedIds ? true : false;
}
bool ON_BinaryArchive::CheckForRemappedIds() const
{
return this->m_bCheckForRemappedIds;
}
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<unsigned int>(Previous3dmTable());
const unsigned int table_index = static_cast<unsigned int>(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<unsigned int>(ON_3dmArchiveTableType::settings_table);
while (previous_index < settings_index)
{
const unsigned int start_index = static_cast<unsigned int>(ON_3dmArchiveTableType::start_section);
const unsigned int properties_index = static_cast<unsigned int>(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<unsigned int>(table) > static_cast<unsigned int>(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);
}
void ON_BinaryArchive::IntentionallyWriteCorrupt3dmStartSectionForExpertTesting()
{
if (ON::archive_mode::write3dm == m_mode)
{
if (0 == m_IntentionallyWriteCorrupt3dmStartSection)
m_IntentionallyWriteCorrupt3dmStartSection = 1; // 1 meas a corrupt start section will be written.
else if (1 == m_IntentionallyWriteCorrupt3dmStartSection)
{
ON_ERROR("Please read the instructions in the header file.");
m_IntentionallyWriteCorrupt3dmStartSection = 2; // 2 indicates the "expert" tester goofed.
}
}
else
{
ON_ERROR("Please read the instructions in the header file.");
m_IntentionallyWriteCorrupt3dmStartSection = 2; // 2 indicates the "expert" tester goofed.
}
}
bool ON_BinaryArchive::Write3dmStartSection(int version, const char* sStartSectionComment)
{
if (!Begin3dmTable(ON::archive_mode::write3dm,ON_3dmArchiveTableType::start_section))
return false;
m_archive_runtime_environment = ON::CurrentRuntimeEnvironment();
m_archive_3dm_start_section_comment = sStartSectionComment;
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))
)
{
// 1, 2, 3, 4 use 32-bit chunk lengths.
// >=50 and a multiple of 10 use 64-bit chunk lengths.
// 64 bit chunk lengths were required in v5 to handle large mesh objects.
ON_ERROR("3dm archive version must be 2, 3, 4, 50, 60, 70, ...");
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 (1 == m_IntentionallyWriteCorrupt3dmStartSection)
{
if (version == ON_BinaryArchive::CurrentArchiveVersion())
{
m_IntentionallyWriteCorrupt3dmStartSection = 3; // 3 indicates the corrupt header was written.
// Change "3D Geometry File Format "
// to "3DXGeometryXFileXFormat "
sVersion[2] = 'X';
sVersion[11] = 'X';
sVersion[16] = 'X';
}
else
{
// intentional corruption is requires working with the current version
m_IntentionallyWriteCorrupt3dmStartSection = 2;
}
}
if (!WriteByte( 32, sVersion ))
return false;
if (!BeginWrite3dmBigChunk( TCODE_COMMENTBLOCK, 0 ))
return false;
bool rc = false;
for (;;)
{
if (sStartSectionComment && sStartSectionComment[0] )
{
if (!WriteByte( strlen(sStartSectionComment), sStartSectionComment) )
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<const char*>(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 ON_String runtime_linux(Internal_RuntimeEnvironmentToString(ON::RuntimeEnvironment::Linux));
const ON_String runtime_wasm(Internal_RuntimeEnvironmentToString(ON::RuntimeEnvironment::WebAssembly));
const char* sRuntimeWindows[]
= {
static_cast<const char*>(runtime_windows),
"windows",
nullptr
};
const char* sRuntimeAndroid[] = {
static_cast<const char*>(runtime_android),
"android",
nullptr
};
const char* sRuntimeApple[] = {
static_cast<const char*>(runtime_apple),
"apple",
"mac rhinoceros",
nullptr
};
const char* sRuntimeLinux[] = {
static_cast<const char*>(runtime_linux),
"linux",
nullptr
};
const char* sRuntimeWebAssembly[] = {
static_cast<const char*>(runtime_wasm),
"wasm",
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 < 5; 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;
case 3:
tokens = sRuntimeLinux;
r = ON::RuntimeEnvironment::Linux;
break;
case 4:
tokens = sRuntimeWebAssembly;
r = ON::RuntimeEnvironment::WebAssembly;
break;
}
for (int i = i0; nullptr != tokens[i]; i++)
{
char prev_c = 0;
for (const char* s = static_cast<const char*>(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_opennurbs7_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.Append( ON_rhino7_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<unsigned int>(tt) <= static_cast<unsigned int>(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;
}
const bool bIsTableTypeCode = (TCODE_TABLE == (0xFFFFF000 & tcode));
if (false == bIsTableTypeCode)
{
// Dale Lear - Nov 8 2022 - ships in Rhino 8.2
// Fix https://mcneel.myjetbrains.com/youtrack/issue/RH-77657
// (detecting corruption in table headers prevents hangs).
// This value isn't a table typecode (even from a table added int the future).
// The file is badly corrupt at this point and we have to quit.
ON_ERROR("tcode is not a table typecode. File is badly corrupted.");
this->Internal_ReportCriticalError();
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<unsigned int>(Previous3dmTable()) > static_cast<unsigned int>(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<unsigned int>(Previous3dmTable());
const unsigned int text_style_table_index = static_cast<unsigned int>(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<ON_2dPoint> 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<ON_2dPoint> 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<ON_2dPoint> 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<ON_2dPoint> 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;
}
//Strip out the $temp_object$ key left over from Block edit
auto* sl = ON_UserStringList::Cast(pAttributes->GetUserData(ON_CLASS_ID(ON_UserStringList)));
if (sl)
{
sl->SetUserString(L"$temp_object$", nullptr);
}
#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<unsigned int>(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;
}
bool
ON_BinaryArchive::SetOpenNURBS3dmVersion(unsigned int v)
{
m_3dm_opennurbs_version = v;
return true;
}
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<const ON_Object*>& 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<const ON_BrepEdge*>(geometry)->Brep();
}
else if ( ON_BrepTrim::Cast(geometry) )
{
geometry = nullptr;
}
else if ( ON_BrepLoop::Cast(geometry) )
{
geometry = static_cast<const ON_BrepLoop*>(geometry)->Brep();
}
else if ( ON_BrepFace::Cast(geometry) )
{
// write parent brep
geometry = static_cast<const ON_BrepFace*>(geometry)->Brep();
}
else if ( ON_CurveProxy::Cast(geometry) )
{
// write actual curve
geometry = static_cast<const ON_CurveProxy*>(geometry)->ProxyCurve();
}
else if ( ON_SurfaceProxy::Cast(geometry) )
{
// write actual surface
geometry = static_cast<const ON_SurfaceProxy*>(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<ON_Geometry*>(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()
{
// NOTE WELL:
// https://mcneel.myjetbrains.com/youtrack/issue/RH-57616
// ON_3dmAnnotationContext::Default is a global const instance.
// Compilers like VS 2019 16.5.0 set the memory for that instance to read-only.
// This destructor must not write to memory used by const instances.
if ( this != &ON_3dmAnnotationContext::Default)
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);
}