mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 03:26:09 +08:00
735 lines
18 KiB
C++
735 lines
18 KiB
C++
#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
|
|
|
|
#define BRANCH_MAX 0x03
|
|
#define TIME_MAX 0x0FFFF
|
|
|
|
|
|
static
|
|
unsigned int ON_VersionNumberTime(
|
|
unsigned int year,
|
|
unsigned int month,
|
|
unsigned int day_of_month
|
|
)
|
|
{
|
|
unsigned int time;
|
|
unsigned int day_of_year;
|
|
|
|
day_of_year = (year < 2100 && year >= 2000)
|
|
? ON_DayOfGregorianYear(year,month,day_of_month)
|
|
: 0;
|
|
|
|
if ( day_of_year > 0 )
|
|
{
|
|
time = (year - 2000)*367 + day_of_year;
|
|
}
|
|
else
|
|
{
|
|
time = 0;
|
|
}
|
|
|
|
return time;
|
|
}
|
|
|
|
static
|
|
bool ON_VersionNumberTimeDeconstruct(
|
|
unsigned int time,
|
|
unsigned int* time_year,
|
|
unsigned int* time_month,
|
|
unsigned int* time_day_of_month
|
|
)
|
|
{
|
|
unsigned int year = 0;
|
|
unsigned int month = 0;
|
|
unsigned int day_of_month = 0;
|
|
unsigned int u = time;
|
|
|
|
if (u > 0)
|
|
{
|
|
unsigned int day_of_year = (u%367);
|
|
u /= 367;
|
|
|
|
if (day_of_year > 0 && u < 100)
|
|
{
|
|
year = u + 2000;
|
|
if ( !ON_GetGregorianMonthAndDayOfMonth(year,day_of_year,&month,&day_of_month) )
|
|
{
|
|
year = 0;
|
|
month = 0;
|
|
day_of_month = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( 0!= time_year)
|
|
*time_year = year;
|
|
if ( 0!= time_month)
|
|
*time_month = month;
|
|
if ( 0!= time_day_of_month)
|
|
*time_day_of_month = day_of_month;
|
|
|
|
return (0 != month);
|
|
}
|
|
|
|
unsigned int ON_VersionNumberConstruct(
|
|
unsigned int major_version,
|
|
unsigned int minor_version,
|
|
unsigned int year,
|
|
unsigned int month,
|
|
unsigned int day_of_month,
|
|
unsigned int platform_branch
|
|
)
|
|
{
|
|
unsigned int version_number = 0;
|
|
unsigned int version_time = ON_VersionNumberTime(year,month,day_of_month);
|
|
|
|
if (version_time > TIME_MAX)
|
|
version_time = TIME_MAX;
|
|
|
|
// platform
|
|
// 0 = developer
|
|
// 1 = Windows Commercial/Beta/WIP
|
|
// 2 = Mac OS Commercial/Beta/WIP
|
|
unsigned int platform
|
|
= (platform_branch > 0)
|
|
? (2 - (platform_branch % 2) )
|
|
: 0;
|
|
if ( platform > BRANCH_MAX )
|
|
platform = 0; // all invalid branch numbers get mapped to "developer"
|
|
if ( minor_version > ON::VersionMinorMaximum())
|
|
minor_version = ON::VersionMinorMaximum();
|
|
if ( major_version > ON::VersionMajorMaximum())
|
|
major_version = ON::VersionMajorMaximum();
|
|
version_number = ((major_version*(ON::VersionMinorMaximum()+1) + minor_version)*(TIME_MAX+1) + version_time)*(BRANCH_MAX+1) + platform;
|
|
version_number |= 0x80000000;
|
|
return version_number;
|
|
}
|
|
|
|
unsigned int ON_VersionNumberFromYearMonthDateFormat(
|
|
unsigned int version_number_major,
|
|
unsigned int YYYYMMDDn_version_number
|
|
)
|
|
{
|
|
unsigned int version_number = 0;
|
|
if ( ON_VersionNumberIsYearMonthDateFormat(version_number_major,YYYYMMDDn_version_number) )
|
|
{
|
|
// convert old format to new format
|
|
unsigned int n = (YYYYMMDDn_version_number % 10);
|
|
unsigned int day_of_month = (YYYYMMDDn_version_number/10) % 100;
|
|
unsigned int month = (YYYYMMDDn_version_number/1000) % 100;
|
|
unsigned int year = (YYYYMMDDn_version_number/100000);
|
|
|
|
unsigned int major_version = (version_number_major > 0 )
|
|
? version_number_major
|
|
: ((n > 0 && n < 9) ? n : 5);
|
|
unsigned int minor_version = 0;
|
|
unsigned int branch = 0; // 0 = developer
|
|
version_number = ON_VersionNumberConstruct(
|
|
major_version,
|
|
minor_version,
|
|
year,
|
|
month,
|
|
day_of_month,
|
|
branch
|
|
);
|
|
}
|
|
return version_number;
|
|
}
|
|
|
|
unsigned int ON_VersionNumberMinimum(unsigned int major_version)
|
|
{
|
|
unsigned int version_number_minimum = ON_VersionNumberConstruct(
|
|
major_version,0,
|
|
2000,1,1,
|
|
0
|
|
);
|
|
return version_number_minimum;
|
|
}
|
|
|
|
bool ON_VersionNumberIsYearMonthDateFormat(
|
|
unsigned int archive_3dm_version,
|
|
unsigned int version_number
|
|
)
|
|
{
|
|
// Note:
|
|
// When new versions of opennurbs save V2,V3,V4 or V5 files,
|
|
// they have to write old yyyymmddn version numbers so the
|
|
// old code will read the file. In this case, n = 6,7,8,9.
|
|
// (n=9 meant debug prior to 2014);
|
|
//
|
|
// 200012210 is the earliest known opennurbs version number
|
|
bool rc = false;
|
|
unsigned int min_old_format_year = 2000;
|
|
unsigned int max_old_format_year = 2100;
|
|
if ( version_number >= min_old_format_year*100000 + 1010
|
|
&& version_number < max_old_format_year*100000
|
|
)
|
|
{
|
|
unsigned int day_of_month = (version_number/10) % 100;
|
|
unsigned int month = (version_number/1000) % 100;
|
|
unsigned int year = (version_number/100000);
|
|
if ( year >= min_old_format_year
|
|
&& year < max_old_format_year
|
|
&& month >= 1 && month <= 12
|
|
&& day_of_month >= 1 && day_of_month <= ON_DaysInMonthOfGregorianYear(year,month)
|
|
)
|
|
{
|
|
unsigned int version_major = (archive_3dm_version >= 50 && 0 == (archive_3dm_version%10)) ? (archive_3dm_version/10) : archive_3dm_version;
|
|
// valid yyyy-mm-dd - check n
|
|
unsigned int n = version_number%10;
|
|
|
|
if (0 == n && 200612060 == version_number)
|
|
n = 5; // bug in 32-bit Rhino 5 SR12 SDK version number (2006 is also a bogus year for V5!)
|
|
else if ( 9 == n && year <= 2016 )
|
|
{
|
|
// 9 was used by debug builds in V2, V3, V4 and V5.
|
|
n = version_major;
|
|
}
|
|
|
|
if ( (0 != n && n == version_major)
|
|
|| (n <= 4 && year <= 2011)
|
|
|| (5==n && year >= 2006)
|
|
|| (6==n && year >= 2012)
|
|
|| (n>=7 && year >= 2018)
|
|
)
|
|
{
|
|
rc = true;
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ON_VersionNumberIsValid(
|
|
unsigned int version_number
|
|
)
|
|
{
|
|
bool rc = ( version_number >= ON_VersionNumberMinimum(1) );
|
|
if (rc)
|
|
{
|
|
unsigned int version_major = 0;
|
|
unsigned int version_minor = 0;
|
|
unsigned int version_branch = 0;
|
|
unsigned int version_year = 0;
|
|
unsigned int version_month = 0;
|
|
unsigned int version_day_of_month = 0;
|
|
rc = ON_VersionNumberParse(
|
|
version_number,
|
|
&version_major,
|
|
&version_minor,
|
|
&version_year,
|
|
&version_month,
|
|
&version_day_of_month,
|
|
&version_branch
|
|
);
|
|
if (rc)
|
|
{
|
|
const unsigned int v = ON_VersionNumberConstruct(version_major,version_minor,version_year,version_month,version_day_of_month,version_branch);
|
|
if ( v != version_number )
|
|
rc = false;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_VersionNumberParse(
|
|
unsigned int version_number,
|
|
unsigned int* version_major,
|
|
unsigned int* version_minor,
|
|
unsigned int* version_year,
|
|
unsigned int* version_month,
|
|
unsigned int* version_day_of_month,
|
|
unsigned int* version_branch
|
|
)
|
|
{
|
|
unsigned int u;
|
|
unsigned int version_time;
|
|
unsigned int max;
|
|
bool rc;
|
|
|
|
u = version_number;
|
|
|
|
if ( ON_VersionNumberIsYearMonthDateFormat(0,u) )
|
|
{
|
|
unsigned int n = u%10;
|
|
u /= 10;
|
|
unsigned int dd = u % 100;
|
|
u /= 100;
|
|
unsigned int mm = u %100;
|
|
u /= 100;
|
|
if (0 == n && 200612060 == version_number)
|
|
n = 5; // 32-bit Rhino V5 SR12 version number bug
|
|
else if (0 == n)
|
|
n = 2;
|
|
else if ( 9 == n && u < 2017 )
|
|
n = 5; // debug maps to 5
|
|
u = ON_VersionNumberConstruct(n,0,u,mm,dd,0);
|
|
}
|
|
|
|
if ( 0 == (0x80000000 & u) )
|
|
{
|
|
// This number was not created by ON_VersionNumberConstruct()
|
|
u = 0;
|
|
}
|
|
else
|
|
{
|
|
// Clear the bit that makes new version numbers
|
|
// created ON_VersionNumberConstruct()
|
|
// bigger than old yyyymmddn version numbers.
|
|
u &= 0x7FFFFFFF;
|
|
}
|
|
|
|
max = BRANCH_MAX+1;
|
|
if ( 0 != version_branch )
|
|
*version_branch = u % max;
|
|
u /= max;
|
|
|
|
max = TIME_MAX+1;
|
|
version_time = u % max;
|
|
u /= max;
|
|
|
|
rc = ON_VersionNumberTimeDeconstruct(
|
|
version_time,
|
|
version_year,
|
|
version_month,
|
|
version_day_of_month);
|
|
|
|
max = ON::VersionMinorMaximum()+1;
|
|
if ( 0 != version_minor )
|
|
*version_minor = u % max;
|
|
u /= max;
|
|
|
|
if ( 0 != version_major )
|
|
*version_major = u;
|
|
|
|
return rc;
|
|
}
|
|
|
|
int ON_VersionNumberCompare(
|
|
unsigned int lhs_version_number,
|
|
unsigned int rhs_version_number,
|
|
int invalid_input_result
|
|
)
|
|
{
|
|
// Ignore the minor version. The major version and date determine
|
|
// what code is newer/older when it comes to file contents.
|
|
// The "branch" is meaningless.
|
|
unsigned int lhs_major_version = 0;
|
|
unsigned int lhs_year = 0;
|
|
unsigned int lhs_month = 0;
|
|
unsigned int lhs_date = 0;
|
|
const bool bLHSIsValid = ON_VersionNumberParse(
|
|
lhs_version_number,
|
|
&lhs_major_version,
|
|
nullptr,
|
|
&lhs_year,
|
|
&lhs_month,
|
|
&lhs_date,
|
|
nullptr
|
|
);
|
|
if (false == bLHSIsValid)
|
|
return invalid_input_result;
|
|
|
|
// Ignore the minor version. The major version and date determine
|
|
// what code is newer/older when it comes to file contents.
|
|
// The "branch" is meaningless.
|
|
unsigned int rhs_major_version = 0;
|
|
unsigned int rhs_year = 0;
|
|
unsigned int rhs_month = 0;
|
|
unsigned int rhs_date = 0;
|
|
const bool bRHSIsValid = ON_VersionNumberParse(
|
|
rhs_version_number,
|
|
&rhs_major_version,
|
|
nullptr,
|
|
&rhs_year,
|
|
&rhs_month,
|
|
&rhs_date,
|
|
nullptr
|
|
);
|
|
if (false == bRHSIsValid)
|
|
return invalid_input_result;
|
|
|
|
if (lhs_major_version < rhs_major_version)
|
|
return -1;
|
|
|
|
if (lhs_major_version > rhs_major_version)
|
|
return 1;
|
|
|
|
// The minor version number does not tell us which version of opennurbs is older/newer.
|
|
// The date is the only way to determine this.
|
|
|
|
const unsigned int lhs_ymd = ((lhs_year * 100 + lhs_month) * 100 + lhs_date);
|
|
const unsigned int rhs_ymd = ((rhs_year * 100 + rhs_month) * 100 + rhs_date);
|
|
|
|
if (lhs_ymd < rhs_ymd)
|
|
return -1;
|
|
|
|
if (lhs_ymd > rhs_ymd)
|
|
return 1;
|
|
|
|
// The branch is meaningless.
|
|
|
|
return 0;
|
|
}
|
|
|
|
const ON_String ON_VersionNumberToString(
|
|
unsigned int version_number,
|
|
bool bUnsignedFormat,
|
|
bool bDateFormat
|
|
)
|
|
{
|
|
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;
|
|
|
|
if (false == ON_VersionNumberIsValid(version_number))
|
|
{
|
|
bUnsignedFormat = false;
|
|
bDateFormat = false;
|
|
}
|
|
else if (bDateFormat)
|
|
{
|
|
bDateFormat = ON_VersionNumberParse(
|
|
version_number,
|
|
&version_major,
|
|
&version_minor,
|
|
&version_year,
|
|
&version_month,
|
|
&version_day_of_month,
|
|
&version_branch
|
|
);
|
|
}
|
|
|
|
ON_String str_version;
|
|
if (bUnsignedFormat)
|
|
{
|
|
str_version = ON_String::FormatToString("%u", version_number);
|
|
if (bDateFormat)
|
|
str_version += " (";
|
|
}
|
|
|
|
if (bDateFormat)
|
|
{
|
|
str_version += ON_String::FormatToString(
|
|
"%u.%u %04u-%02u-%02u:%u",
|
|
version_major,
|
|
version_minor,
|
|
version_year,
|
|
version_month,
|
|
version_day_of_month,
|
|
version_branch
|
|
);
|
|
if (bUnsignedFormat)
|
|
str_version += ")";
|
|
}
|
|
|
|
if (str_version.IsEmpty())
|
|
{
|
|
str_version =
|
|
(0 != version_number)
|
|
? ON_String::FormatToString("0x%08X", version_number)
|
|
: "0";
|
|
}
|
|
|
|
return str_version;
|
|
}
|
|
|
|
const ON_wString ON_VersionNumberToWideString(
|
|
unsigned int version_number,
|
|
bool bUnsignedFormat,
|
|
bool bDateFormat
|
|
)
|
|
{
|
|
return ON_wString(ON_VersionNumberToString(version_number, bUnsignedFormat, bDateFormat));
|
|
}
|
|
|
|
const ON_String ON_SdkVersionNumberToString(
|
|
unsigned int sdk_version_number,
|
|
unsigned int sdk_service_release_number
|
|
)
|
|
{
|
|
const bool bValidVersionNumber = ON_VersionNumberIsValid(sdk_version_number);
|
|
const bool bValidServiceReleaseNumber = ON_VersionNumberIsValid(sdk_service_release_number);
|
|
ON_String str_version = ON_VersionNumberToString(sdk_version_number, true, false);
|
|
str_version += ".";
|
|
str_version += ON_VersionNumberToString(sdk_service_release_number, true, false);
|
|
if (bValidVersionNumber || bValidServiceReleaseNumber)
|
|
{
|
|
str_version += " (";
|
|
if (bValidVersionNumber != bValidServiceReleaseNumber || sdk_version_number == sdk_service_release_number )
|
|
{
|
|
str_version += ON_VersionNumberToString(bValidVersionNumber ? sdk_version_number : sdk_service_release_number, false, true);
|
|
}
|
|
else
|
|
{
|
|
str_version += ON_VersionNumberToString(sdk_version_number, false, true);
|
|
str_version += ", ";
|
|
str_version += ON_VersionNumberToString(sdk_service_release_number, false, true);
|
|
}
|
|
str_version += ")";
|
|
}
|
|
return str_version;
|
|
}
|
|
|
|
const ON_wString ON_SdkVersionNumberToWideString(
|
|
unsigned int sdk_version_number,
|
|
unsigned int sdk_service_release_number
|
|
)
|
|
{
|
|
return ON_wString(ON_SdkVersionNumberToString(sdk_version_number, sdk_service_release_number));
|
|
}
|
|
|
|
|
|
static
|
|
unsigned int AppendValueToString(
|
|
unsigned int u,
|
|
size_t string_buffer_capacity,
|
|
char* string_buffer
|
|
)
|
|
{
|
|
unsigned int digit_count = 0;
|
|
unsigned int i;
|
|
|
|
if ( string_buffer_capacity > 0 && 0 != string_buffer )
|
|
{
|
|
i = u;
|
|
for(;;)
|
|
{
|
|
digit_count++;
|
|
i /= 10;
|
|
if ( i < 1 )
|
|
break;
|
|
}
|
|
|
|
if ( digit_count >= string_buffer_capacity )
|
|
digit_count = 0;
|
|
else
|
|
{
|
|
i = digit_count;
|
|
string_buffer[i] = 0;
|
|
for (;;)
|
|
{
|
|
i--;
|
|
string_buffer[i] = (u % 10) + '0';
|
|
u /= 10;
|
|
if ( u < 1 )
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return digit_count;
|
|
}
|
|
|
|
unsigned int ON_GetVersionQuartet(
|
|
unsigned int major_version,
|
|
unsigned int minor_version,
|
|
unsigned int year,
|
|
unsigned int month,
|
|
unsigned int day_of_month,
|
|
unsigned int hour,
|
|
unsigned int minute,
|
|
unsigned int branch,
|
|
unsigned short quartet_values[4]
|
|
)
|
|
{
|
|
unsigned int i;
|
|
unsigned int item_value;
|
|
unsigned int version_number = ON_VersionNumberConstruct(
|
|
major_version,
|
|
minor_version,
|
|
year,
|
|
month,
|
|
day_of_month,
|
|
branch
|
|
);
|
|
|
|
if ( hour >= 24 || minute >= 60 )
|
|
{
|
|
if ( 24 == hour && 0 == minute )
|
|
{
|
|
// leap second
|
|
hour = 23;
|
|
minute = 60;
|
|
}
|
|
if ( 23 != hour || 60 != minute )
|
|
{
|
|
hour = 0;
|
|
minute = 0;
|
|
}
|
|
}
|
|
|
|
for(i = 0; i < 4; i++ )
|
|
{
|
|
switch(i)
|
|
{
|
|
case 0:
|
|
item_value = major_version;
|
|
break;
|
|
case 1:
|
|
item_value = minor_version;
|
|
break;
|
|
case 2:
|
|
item_value = (year-2000)*1000 + ON_DayOfGregorianYear(year,month,day_of_month);
|
|
break;
|
|
case 3:
|
|
item_value = (hour*100 + minute)*10 + branch;
|
|
break;
|
|
default:
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
if ( item_value > 0xFFFF )
|
|
{
|
|
version_number = 0;
|
|
item_value = 0;
|
|
}
|
|
|
|
quartet_values[i] = (unsigned short)item_value;
|
|
}
|
|
|
|
return version_number;
|
|
}
|
|
|
|
unsigned int ON_GetVersionString(
|
|
unsigned int major_version,
|
|
unsigned int minor_version,
|
|
unsigned int year,
|
|
unsigned int month,
|
|
unsigned int day_of_month,
|
|
unsigned int hour,
|
|
unsigned int minute,
|
|
unsigned int branch,
|
|
size_t string_buffer_capacity,
|
|
char* string_buffer
|
|
)
|
|
{
|
|
char s[24] = {0};
|
|
const size_t s_capacity = sizeof(s)/sizeof(s[0]);
|
|
unsigned int string_length = 0;
|
|
unsigned int i, j;
|
|
unsigned short quartet_values[4] = {0};
|
|
unsigned int version_number = ON_GetVersionQuartet(
|
|
major_version,
|
|
minor_version,
|
|
year,
|
|
month,
|
|
day_of_month,
|
|
hour,
|
|
minute,
|
|
branch,
|
|
quartet_values
|
|
);
|
|
|
|
if (string_buffer_capacity <= 0 || nullptr == string_buffer)
|
|
{
|
|
string_buffer_capacity = s_capacity;
|
|
string_buffer = s;
|
|
}
|
|
|
|
if ( version_number > 0 )
|
|
{
|
|
for(i = 0; i < 4 && string_length < string_buffer_capacity; i++ )
|
|
{
|
|
if ( i > 0 )
|
|
string_buffer[string_length++] = '.';
|
|
|
|
j = AppendValueToString(
|
|
quartet_values[i],
|
|
string_buffer_capacity - string_length,
|
|
string_buffer + string_length
|
|
);
|
|
|
|
if ( j <= 0 )
|
|
{
|
|
string_length = 0;
|
|
break;
|
|
}
|
|
|
|
string_length += j;
|
|
|
|
}
|
|
}
|
|
|
|
if ( string_length > string_buffer_capacity )
|
|
string_length = 0;
|
|
|
|
for ( i = string_length; i < string_buffer_capacity; i++ )
|
|
string_buffer[i] = 0;
|
|
|
|
return string_length;
|
|
}
|
|
|
|
unsigned int ON_GetVersionWideString(
|
|
unsigned int major_version,
|
|
unsigned int minor_version,
|
|
unsigned int year,
|
|
unsigned int month,
|
|
unsigned int day_of_month,
|
|
unsigned int hour,
|
|
unsigned int minute,
|
|
unsigned int branch,
|
|
size_t string_buffer_capacity,
|
|
wchar_t* string_buffer
|
|
)
|
|
{
|
|
char s[24] = {0};
|
|
const size_t s_capacity = sizeof(s)/sizeof(s[0]);
|
|
unsigned int string_length;
|
|
unsigned int i;
|
|
|
|
string_length = ON_GetVersionString(
|
|
major_version,
|
|
minor_version,
|
|
year,
|
|
month,
|
|
day_of_month,
|
|
hour,
|
|
minute,
|
|
branch,
|
|
s_capacity, s
|
|
);
|
|
|
|
if (string_buffer_capacity > 0 && nullptr != string_buffer)
|
|
{
|
|
if (((size_t)string_length) <= string_buffer_capacity)
|
|
{
|
|
for (i = 0; i < string_length && 0 != s[i]; i++)
|
|
{
|
|
string_buffer[i] = s[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string_length = 0;
|
|
i = 0;
|
|
}
|
|
|
|
while (i < string_buffer_capacity)
|
|
string_buffer[i++] = 0;
|
|
}
|
|
|
|
return string_length;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|