From fe96298be57352ed6f78ccd9d69d3452b609be2c Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 3 May 2021 16:22:17 +0100 Subject: [PATCH] Thread-safe error reporting in lowlevel.cxx Use an exception rather than polling a flag, for checking errors during BTG reading. This should allow us to give a correctly identified error, at exactly the point the read fails. --- simgear/io/lowlevel.cxx | 112 ++++--- simgear/io/lowlevel.hxx | 12 +- simgear/io/sg_binobj.cxx | 709 +++++++++++++++++++-------------------- 3 files changed, 424 insertions(+), 409 deletions(-) diff --git a/simgear/io/lowlevel.cxx b/simgear/io/lowlevel.cxx index d41bc801..9ae5bb42 100644 --- a/simgear/io/lowlevel.cxx +++ b/simgear/io/lowlevel.cxx @@ -23,28 +23,41 @@ // $Id$ -#ifdef HAVE_CONFIG_H -# include -#endif +#include #include // for memcpy() +#include -#include "lowlevel.hxx" +#include +#include +#include +#include "lowlevel.hxx" -static int read_error = false ; -static int write_error = false ; +thread_local SGPath thread_gzPath; -void sgClearReadError() { read_error = false; } -void sgClearWriteError() { write_error = false; } -int sgReadError() { return read_error ; } -int sgWriteError() { return write_error ; } +void setThreadLocalSimgearReadPath(const SGPath& path) +{ + thread_gzPath = path; +} +static std::string gzErrorMessage(gzFile fd) +{ + int errNum = 0; + const char *gzMsg = gzerror(fd, &errNum); + + if (errNum == Z_ERRNO) { + return simgear::strutils::error_string(errno); + } else { + return {gzMsg}; + } +} void sgReadChar ( gzFile fd, char *var ) { if ( gzread ( fd, var, sizeof(char) ) != sizeof(char) ) { - read_error = true ; + throw sg_io_exception("sgReadChar: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } } @@ -52,7 +65,7 @@ void sgReadChar ( gzFile fd, char *var ) void sgWriteChar ( gzFile fd, const char var ) { if ( gzwrite ( fd, (void *)(&var), sizeof(char) ) != sizeof(char) ) { - write_error = true ; + throw sg_io_exception("sgWriteChar: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -61,7 +74,8 @@ void sgReadFloat ( gzFile fd, float *var ) { union { float v; uint32_t u; } buf; if ( gzread ( fd, &buf.u, sizeof(float) ) != sizeof(float) ) { - read_error = true ; + throw sg_io_exception("sgReadFloat: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { sgEndianSwap( &buf.u ); @@ -78,7 +92,7 @@ void sgWriteFloat ( gzFile fd, const float var ) sgEndianSwap( &buf.u ); } if ( gzwrite ( fd, (void *)(&buf.u), sizeof(float) ) != sizeof(float) ) { - write_error = true ; + throw sg_io_exception("sgWriteFloat: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -87,7 +101,8 @@ void sgReadDouble ( gzFile fd, double *var ) { union { double v; uint64_t u; } buf; if ( gzread ( fd, &buf.u, sizeof(double) ) != sizeof(double) ) { - read_error = true ; + throw sg_io_exception("sgReadDouble: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { sgEndianSwap( &buf.u ); @@ -104,7 +119,7 @@ void sgWriteDouble ( gzFile fd, const double var ) sgEndianSwap( &buf.u ); } if ( gzwrite ( fd, (void *)(&buf.u), sizeof(double) ) != sizeof(double) ) { - write_error = true ; + throw sg_io_exception("sgWriteDouble: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -112,7 +127,8 @@ void sgWriteDouble ( gzFile fd, const double var ) void sgReadUInt ( gzFile fd, unsigned int *var ) { if ( gzread ( fd, var, sizeof(unsigned int) ) != sizeof(unsigned int) ) { - read_error = true ; + throw sg_io_exception("sgReadUInt: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { sgEndianSwap( (uint32_t *)var); @@ -128,7 +144,7 @@ void sgWriteUInt ( gzFile fd, const unsigned int var ) if ( gzwrite ( fd, (void *)(&var), sizeof(unsigned int) ) != sizeof(unsigned int) ) { - write_error = true ; + throw sg_io_exception("sgWriteUInt: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -136,7 +152,8 @@ void sgWriteUInt ( gzFile fd, const unsigned int var ) void sgReadInt ( gzFile fd, int *var ) { if ( gzread ( fd, var, sizeof(int) ) != sizeof(int) ) { - read_error = true ; + throw sg_io_exception("sgReadInt: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { sgEndianSwap( (uint32_t *)var); @@ -150,7 +167,7 @@ void sgWriteInt ( gzFile fd, const int var ) sgEndianSwap( (uint32_t *)&var); } if ( gzwrite ( fd, (void *)(&var), sizeof(int) ) != sizeof(int) ) { - write_error = true ; + throw sg_io_exception("sgWriteInt: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -158,7 +175,8 @@ void sgWriteInt ( gzFile fd, const int var ) void sgReadLong ( gzFile fd, int32_t *var ) { if ( gzread ( fd, var, sizeof(int32_t) ) != sizeof(int32_t) ) { - read_error = true ; + throw sg_io_exception("sgReadLong: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { sgEndianSwap( (uint32_t *)var); @@ -174,7 +192,7 @@ void sgWriteLong ( gzFile fd, const int32_t var ) if ( gzwrite ( fd, (void *)(&var), sizeof(int32_t) ) != sizeof(int32_t) ) { - write_error = true ; + throw sg_io_exception("sgWriteLong: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -182,7 +200,8 @@ void sgWriteLong ( gzFile fd, const int32_t var ) void sgReadLongLong ( gzFile fd, int64_t *var ) { if ( gzread ( fd, var, sizeof(int64_t) ) != sizeof(int64_t) ) { - read_error = true ; + throw sg_io_exception("sgReadLongLong: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { sgEndianSwap( (uint64_t *)var); @@ -198,7 +217,7 @@ void sgWriteLongLong ( gzFile fd, const int64_t var ) if ( gzwrite ( fd, (void *)(&var), sizeof(int64_t) ) != sizeof(int64_t) ) { - write_error = true ; + throw sg_io_exception("sgWriteLongLong: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -206,7 +225,8 @@ void sgWriteLongLong ( gzFile fd, const int64_t var ) void sgReadUShort ( gzFile fd, unsigned short *var ) { if ( gzread ( fd, var, sizeof(unsigned short) ) != sizeof(unsigned short) ){ - read_error = true ; + throw sg_io_exception("sgReadUShort: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { sgEndianSwap( (uint16_t *)var); @@ -222,7 +242,7 @@ void sgWriteUShort ( gzFile fd, const unsigned short var ) if ( gzwrite ( fd, (void *)(&var), sizeof(unsigned short) ) != sizeof(unsigned short) ) { - write_error = true ; + throw sg_io_exception("sgWriteUShort: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -230,7 +250,8 @@ void sgWriteUShort ( gzFile fd, const unsigned short var ) void sgReadShort ( gzFile fd, short *var ) { if ( gzread ( fd, var, sizeof(short) ) != sizeof(short) ) { - read_error = true ; + throw sg_io_exception("sgReadShort: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { sgEndianSwap( (uint16_t *)var); @@ -244,7 +265,7 @@ void sgWriteShort ( gzFile fd, const short var ) sgEndianSwap( (uint16_t *)&var); } if ( gzwrite ( fd, (void *)(&var), sizeof(short) ) != sizeof(short) ) { - write_error = true ; + throw sg_io_exception("sgWriteShort: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -252,7 +273,8 @@ void sgWriteShort ( gzFile fd, const short var ) void sgReadFloat ( gzFile fd, const unsigned int n, float *var ) { if ( gzread ( fd, var, sizeof(float) * n ) != (int)(sizeof(float) * n) ) { - read_error = true ; + throw sg_io_exception("sgReadFloat array: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { for ( unsigned int i = 0; i < n; ++i ) { @@ -276,14 +298,15 @@ void sgWriteFloat ( gzFile fd, const unsigned int n, const float *var ) if ( gzwrite ( fd, (void *)var, sizeof(float) * n ) != (int)(sizeof(float) * n) ) { - write_error = true ; + throw sg_io_exception("sgWriteFloat array: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } void sgReadDouble ( gzFile fd, const unsigned int n, double *var ) { if ( gzread ( fd, var, sizeof(double) * n ) != (int)(sizeof(double) * n) ) { - read_error = true ; + throw sg_io_exception("sgReadDouble array: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { for ( unsigned int i = 0; i < n; ++i ) { @@ -307,7 +330,7 @@ void sgWriteDouble ( gzFile fd, const unsigned int n, const double *var ) if ( gzwrite ( fd, (void *)var, sizeof(double) * n ) != (int)(sizeof(double) * n) ) { - write_error = true ; + throw sg_io_exception("sgWriteDouble array: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -315,7 +338,8 @@ void sgReadBytes ( gzFile fd, const unsigned int n, void *var ) { if ( n == 0) return; if ( gzread ( fd, var, n ) != (int)n ) { - read_error = true ; + throw sg_io_exception("sgReadBytes: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } } @@ -323,7 +347,7 @@ void sgWriteBytes ( gzFile fd, const unsigned int n, const void *var ) { if ( n == 0) return; if ( gzwrite ( fd, (void *)var, n ) != (int)n ) { - write_error = true ; + throw sg_io_exception("sgWriteBytes: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -333,7 +357,8 @@ void sgReadUShort ( gzFile fd, const unsigned int n, unsigned short *var ) if ( gzread ( fd, var, sizeof(unsigned short) * n ) != (int)(sizeof(unsigned short) * n) ) { - read_error = true ; + throw sg_io_exception("sgReadUShort array: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { for ( unsigned int i = 0; i < n; ++i ) { @@ -357,7 +382,7 @@ void sgWriteUShort ( gzFile fd, const unsigned int n, const unsigned short *var if ( gzwrite ( fd, (void *)var, sizeof(unsigned short) * n ) != (int)(sizeof(unsigned short) * n) ) { - write_error = true ; + throw sg_io_exception("sgWriteUShort array: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -368,7 +393,8 @@ void sgReadShort ( gzFile fd, const unsigned int n, short *var ) if ( gzread ( fd, var, sizeof(short) * n ) != (int)(sizeof(short) * n) ) { - read_error = true ; + throw sg_io_exception("sgReadShort array: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { for ( unsigned int i = 0; i < n; ++i ) { @@ -392,7 +418,7 @@ void sgWriteShort ( gzFile fd, const unsigned int n, const short *var ) if ( gzwrite ( fd, (void *)var, sizeof(short) * n ) != (int)(sizeof(short) * n) ) { - write_error = true ; + throw sg_io_exception("sgWriteShort array: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -402,7 +428,8 @@ void sgReadUInt ( gzFile fd, const unsigned int n, unsigned int *var ) if ( gzread ( fd, var, sizeof(unsigned int) * n ) != (int)(sizeof(unsigned int) * n) ) { - read_error = true ; + throw sg_io_exception("sgReadUInt array: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { for ( unsigned int i = 0; i < n; ++i ) { @@ -426,7 +453,7 @@ void sgWriteUInt ( gzFile fd, const unsigned int n, const unsigned int *var ) if ( gzwrite ( fd, (void *)var, sizeof(unsigned int) * n ) != (int)(sizeof(unsigned int) * n) ) { - write_error = true ; + throw sg_io_exception("sgWriteUInt array: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } @@ -437,7 +464,8 @@ void sgReadInt ( gzFile fd, const unsigned int n, int *var ) if ( gzread ( fd, var, sizeof(int) * n ) != (int)(sizeof(int) * n) ) { - read_error = true ; + throw sg_io_exception("sgReadInt array: GZRead failed:" + gzErrorMessage(fd), + sg_location{thread_gzPath}, nullptr, false); } if ( sgIsBigEndian() ) { for ( unsigned int i = 0; i < n; ++i ) { @@ -461,7 +489,7 @@ void sgWriteInt ( gzFile fd, const unsigned int n, const int *var ) if ( gzwrite ( fd, (void *)var, sizeof(int) * n ) != (int)(sizeof(int) * n) ) { - write_error = true ; + throw sg_io_exception("sgWriteInt array: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false); } } diff --git a/simgear/io/lowlevel.hxx b/simgear/io/lowlevel.hxx index 2367d0d4..d833c4d7 100644 --- a/simgear/io/lowlevel.hxx +++ b/simgear/io/lowlevel.hxx @@ -35,6 +35,9 @@ #include +// forward decls +class SGPath; + // Note that output is written in little endian form (and converted as // necessary for big endian machines) @@ -121,9 +124,10 @@ inline void sgWriteGeod ( gzFile fd, const SGGeod& var ) { sgWriteDouble( fd, var.getElevationM() ); } -void sgClearReadError(); -void sgClearWriteError(); -int sgReadError(); -int sgWriteError(); +/** + @ error aid: allow calling code to specify which file path we're reading from, so that erros we + throw from sgReadXXXX can have a valid location set. + */ +void setThreadLocalSimgearReadPath(const SGPath& path); #endif // _SG_LOWLEVEL_HXX diff --git a/simgear/io/sg_binobj.cxx b/simgear/io/sg_binobj.cxx index 4d6180c1..543b0446 100644 --- a/simgear/io/sg_binobj.cxx +++ b/simgear/io/sg_binobj.cxx @@ -454,10 +454,6 @@ void SGBinObject::read_object( gzFile fp, } } - if ( sgReadError() ) { - throw sg_exception("Error reading object properties"); - } - size_t indexCount = std::bitset<32>((int)idx_mask).count(); if (indexCount == 0) { throw sg_exception("object index mask has no bits set"); @@ -465,18 +461,10 @@ void SGBinObject::read_object( gzFile fp, for ( j = 0; j < nelements; ++j ) { sgReadUInt( fp, &nbytes ); - if ( sgReadError() ) { - throw sg_exception("Error reading element size"); - } - buf.resize( nbytes ); char *ptr = buf.get_ptr(); sgReadBytes( fp, nbytes, ptr ); - if ( sgReadError() ) { - throw sg_exception("Error reading element bytes"); - } - int_list vs; int_list ns; int_list cs; @@ -503,314 +491,309 @@ void SGBinObject::read_object( gzFile fp, // read a binary file and populate the provided structures. -bool SGBinObject::read_bin( const SGPath& file ) { - SGVec3d p; - int i, k; - size_t j; - unsigned int nbytes; - sgSimpleBuffer buf( 32768 ); // 32 Kb +bool SGBinObject::read_bin( const SGPath& file ) +{ + gzFile fp = NULL; + + try { + SGVec3d p; + int i, k; + size_t j; + unsigned int nbytes; + sgSimpleBuffer buf( 32768 ); // 32 Kb - // zero out structures - gbs_center = SGVec3d(0, 0, 0); - gbs_radius = 0.0; + // zero out structures + gbs_center = SGVec3d(0, 0, 0); + gbs_radius = 0.0; - wgs84_nodes.clear(); - normals.clear(); - texcoords.clear(); + wgs84_nodes.clear(); + normals.clear(); + texcoords.clear(); - pts_v.clear(); - pts_n.clear(); - pts_c.clear(); - pts_tcs.clear(); - pts_vas.clear(); - pt_materials.clear(); + pts_v.clear(); + pts_n.clear(); + pts_c.clear(); + pts_tcs.clear(); + pts_vas.clear(); + pt_materials.clear(); - tris_v.clear(); - tris_n.clear(); - tris_c.clear(); - tris_tcs.clear(); - tris_vas.clear(); - tri_materials.clear(); + tris_v.clear(); + tris_n.clear(); + tris_c.clear(); + tris_tcs.clear(); + tris_vas.clear(); + tri_materials.clear(); - strips_v.clear(); - strips_n.clear(); - strips_c.clear(); - strips_tcs.clear(); - strips_vas.clear(); - strip_materials.clear(); + strips_v.clear(); + strips_n.clear(); + strips_c.clear(); + strips_tcs.clear(); + strips_vas.clear(); + strip_materials.clear(); - fans_v.clear(); - fans_n.clear(); - fans_c.clear(); - fans_tcs.clear(); - fans_vas.clear(); - fan_materials.clear(); + fans_v.clear(); + fans_n.clear(); + fans_c.clear(); + fans_tcs.clear(); + fans_vas.clear(); + fan_materials.clear(); - gzFile fp = gzFileFromSGPath(file, "rb"); - if ( fp == NULL ) { - SGPath withGZ = file; - withGZ.concat(".gz"); - fp = gzFileFromSGPath(withGZ, "rb"); - if (fp == nullptr) { - SG_LOG( SG_EVENT, SG_ALERT, - "ERROR: opening " << file << " or " << withGZ << " for reading!"); - - throw sg_io_exception("Error opening for reading (and .gz)", sg_location(file)); + gzFile fp = gzFileFromSGPath(file, "rb"); + if ( fp == NULL ) { + SGPath withGZ = file; + withGZ.concat(".gz"); + fp = gzFileFromSGPath(withGZ, "rb"); + if (fp == nullptr) { + throw sg_io_exception("Error opening for reading (and .gz)", sg_location(file), {}, false); + } } - } + + setThreadLocalSimgearReadPath(file); - sgClearReadError(); + // read headers + unsigned int header; + sgReadUInt( fp, &header ); - // read headers - unsigned int header; - sgReadUInt( fp, &header ); - if (sgReadError()) { - int code = 0; - const char* gzErrorString = gzerror(fp, &code); - gzclose(fp); - throw sg_io_exception("Unable to read BTG header: " + string{gzErrorString} + ", code =" + std::to_string(code), sg_location(file)); - } + if ( ((header & 0xFF000000) >> 24) == 'S' && + ((header & 0x00FF0000) >> 16) == 'G' ) { - if ( ((header & 0xFF000000) >> 24) == 'S' && - ((header & 0x00FF0000) >> 16) == 'G' ) { + // read file version + version = (header & 0x0000FFFF); + } else { + throw sg_io_exception("Bad BTG magic/version", sg_location(file), {}, false); + } - // read file version - version = (header & 0x0000FFFF); - } else { - // close the file before we return - gzclose(fp); - throw sg_io_exception("Bad BTG magic/version", sg_location(file)); - } + // read creation time + unsigned int foo_calendar_time; + sgReadUInt( fp, &foo_calendar_time ); - // read creation time - unsigned int foo_calendar_time; - sgReadUInt( fp, &foo_calendar_time ); + #if 0 + time_t calendar_time = foo_calendar_time; + // The following code has a global effect on the host application + // and can screws up the time elsewhere. It should be avoided + // unless you need this for debugging in which case you should + // disable it again once the debugging task is finished. + struct tm *local_tm; + local_tm = localtime( &calendar_time ); + char time_str[256]; + strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm); + SG_LOG( SG_EVENT, SG_DEBUG, "File created on " << time_str); + #endif -#if 0 - time_t calendar_time = foo_calendar_time; - // The following code has a global effect on the host application - // and can screws up the time elsewhere. It should be avoided - // unless you need this for debugging in which case you should - // disable it again once the debugging task is finished. - struct tm *local_tm; - local_tm = localtime( &calendar_time ); - char time_str[256]; - strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm); - SG_LOG( SG_EVENT, SG_DEBUG, "File created on " << time_str); -#endif - - // read number of top level objects - int nobjects; - if ( version >= 10) { // version 10 extends everything to be 32-bit - sgReadInt( fp, &nobjects ); - } else if ( version >= 7 ) { - uint16_t v; - sgReadUShort( fp, &v ); - nobjects = v; - } else { - int16_t v; - sgReadShort( fp, &v ); - nobjects = v; - } - - SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin Total objects to read = " << nobjects); - - if ( sgReadError() ) { - throw sg_io_exception("Error reading BTG file header", sg_location(file)); - } - - // read in objects - for ( i = 0; i < nobjects; ++i ) { - // read object header - char obj_type; - uint32_t nproperties, nelements; - sgReadChar( fp, &obj_type ); - if ( version >= 10 ) { - sgReadUInt( fp, &nproperties ); - sgReadUInt( fp, &nelements ); + // read number of top level objects + int nobjects; + if ( version >= 10) { // version 10 extends everything to be 32-bit + sgReadInt( fp, &nobjects ); } else if ( version >= 7 ) { uint16_t v; sgReadUShort( fp, &v ); - nproperties = v; - sgReadUShort( fp, &v ); - nelements = v; + nobjects = v; } else { int16_t v; sgReadShort( fp, &v ); - nproperties = v; - sgReadShort( fp, &v ); - nelements = v; + nobjects = v; } - SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin object " << i << - " = " << (int)obj_type << " props = " << nproperties << - " elements = " << nelements); + SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin Total objects to read = " << nobjects); - if ( obj_type == SG_BOUNDING_SPHERE ) { - // read bounding sphere properties - read_properties( fp, nproperties ); - // read bounding sphere elements - for ( j = 0; j < nelements; ++j ) { - sgReadUInt( fp, &nbytes ); - buf.resize( nbytes ); - buf.reset(); - char *ptr = buf.get_ptr(); - sgReadBytes( fp, nbytes, ptr ); - gbs_center = buf.readVec3d(); - gbs_radius = buf.readFloat(); + // read in objects + for ( i = 0; i < nobjects; ++i ) { + // read object header + char obj_type; + uint32_t nproperties, nelements; + sgReadChar( fp, &obj_type ); + if ( version >= 10 ) { + sgReadUInt( fp, &nproperties ); + sgReadUInt( fp, &nelements ); + } else if ( version >= 7 ) { + uint16_t v; + sgReadUShort( fp, &v ); + nproperties = v; + sgReadUShort( fp, &v ); + nelements = v; + } else { + int16_t v; + sgReadShort( fp, &v ); + nproperties = v; + sgReadShort( fp, &v ); + nelements = v; } - } else if ( obj_type == SG_VERTEX_LIST ) { - // read vertex list properties - read_properties( fp, nproperties ); - // read vertex list elements - for ( j = 0; j < nelements; ++j ) { - sgReadUInt( fp, &nbytes ); - buf.resize( nbytes ); - buf.reset(); - char *ptr = buf.get_ptr(); - sgReadBytes( fp, nbytes, ptr ); - int count = nbytes / (sizeof(float) * 3); - wgs84_nodes.reserve( count ); - for ( k = 0; k < count; ++k ) { - SGVec3f v = buf.readVec3f(); - // extend from float to double, hmmm - wgs84_nodes.push_back( SGVec3d(v[0], v[1], v[2]) ); + SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin object " << i << + " = " << (int)obj_type << " props = " << nproperties << + " elements = " << nelements); + + if ( obj_type == SG_BOUNDING_SPHERE ) { + // read bounding sphere properties + read_properties( fp, nproperties ); + + // read bounding sphere elements + for ( j = 0; j < nelements; ++j ) { + sgReadUInt( fp, &nbytes ); + buf.resize( nbytes ); + buf.reset(); + char *ptr = buf.get_ptr(); + sgReadBytes( fp, nbytes, ptr ); + gbs_center = buf.readVec3d(); + gbs_radius = buf.readFloat(); } - } - } else if ( obj_type == SG_COLOR_LIST ) { - // read color list properties - read_properties( fp, nproperties ); + } else if ( obj_type == SG_VERTEX_LIST ) { + // read vertex list properties + read_properties( fp, nproperties ); - // read color list elements - for ( j = 0; j < nelements; ++j ) { - sgReadUInt( fp, &nbytes ); - buf.resize( nbytes ); - buf.reset(); - char *ptr = buf.get_ptr(); - sgReadBytes( fp, nbytes, ptr ); - int count = nbytes / (sizeof(float) * 4); - colors.reserve(count); - for ( k = 0; k < count; ++k ) { - colors.push_back( buf.readVec4f() ); + // read vertex list elements + for ( j = 0; j < nelements; ++j ) { + sgReadUInt( fp, &nbytes ); + buf.resize( nbytes ); + buf.reset(); + char *ptr = buf.get_ptr(); + sgReadBytes( fp, nbytes, ptr ); + int count = nbytes / (sizeof(float) * 3); + wgs84_nodes.reserve( count ); + for ( k = 0; k < count; ++k ) { + SGVec3f v = buf.readVec3f(); + // extend from float to double, hmmm + wgs84_nodes.push_back( SGVec3d(v[0], v[1], v[2]) ); + } } - } - } else if ( obj_type == SG_NORMAL_LIST ) { - // read normal list properties - read_properties( fp, nproperties ); + } else if ( obj_type == SG_COLOR_LIST ) { + // read color list properties + read_properties( fp, nproperties ); - // read normal list elements - for ( j = 0; j < nelements; ++j ) { - sgReadUInt( fp, &nbytes ); - buf.resize( nbytes ); - buf.reset(); - unsigned char *ptr = (unsigned char *)(buf.get_ptr()); - sgReadBytes( fp, nbytes, ptr ); - int count = nbytes / 3; - normals.reserve( count ); - - for ( k = 0; k < count; ++k ) { - SGVec3f normal( (ptr[0]) / 127.5 - 1.0, - (ptr[1]) / 127.5 - 1.0, - (ptr[2]) / 127.5 - 1.0); - normals.push_back(normalize(normal)); - ptr += 3; + // read color list elements + for ( j = 0; j < nelements; ++j ) { + sgReadUInt( fp, &nbytes ); + buf.resize( nbytes ); + buf.reset(); + char *ptr = buf.get_ptr(); + sgReadBytes( fp, nbytes, ptr ); + int count = nbytes / (sizeof(float) * 4); + colors.reserve(count); + for ( k = 0; k < count; ++k ) { + colors.push_back( buf.readVec4f() ); + } } - } - } else if ( obj_type == SG_TEXCOORD_LIST ) { - // read texcoord list properties - read_properties( fp, nproperties ); + } else if ( obj_type == SG_NORMAL_LIST ) { + // read normal list properties + read_properties( fp, nproperties ); - // read texcoord list elements - for ( j = 0; j < nelements; ++j ) { - sgReadUInt( fp, &nbytes ); - buf.resize( nbytes ); - buf.reset(); - char *ptr = buf.get_ptr(); - sgReadBytes( fp, nbytes, ptr ); - int count = nbytes / (sizeof(float) * 2); - texcoords.reserve(count); - for ( k = 0; k < count; ++k ) { - texcoords.push_back( buf.readVec2f() ); + // read normal list elements + for ( j = 0; j < nelements; ++j ) { + sgReadUInt( fp, &nbytes ); + buf.resize( nbytes ); + buf.reset(); + unsigned char *ptr = (unsigned char *)(buf.get_ptr()); + sgReadBytes( fp, nbytes, ptr ); + int count = nbytes / 3; + normals.reserve( count ); + + for ( k = 0; k < count; ++k ) { + SGVec3f normal( (ptr[0]) / 127.5 - 1.0, + (ptr[1]) / 127.5 - 1.0, + (ptr[2]) / 127.5 - 1.0); + normals.push_back(normalize(normal)); + ptr += 3; + } } - } - } else if ( obj_type == SG_VA_FLOAT_LIST ) { - // read vertex attribute (float) properties - read_properties( fp, nproperties ); + } else if ( obj_type == SG_TEXCOORD_LIST ) { + // read texcoord list properties + read_properties( fp, nproperties ); - // read vertex attribute list elements - for ( j = 0; j < nelements; ++j ) { - sgReadUInt( fp, &nbytes ); - buf.resize( nbytes ); - buf.reset(); - char *ptr = buf.get_ptr(); - sgReadBytes( fp, nbytes, ptr ); - int count = nbytes / (sizeof(float)); - va_flt.reserve(count); - for ( k = 0; k < count; ++k ) { - va_flt.push_back( buf.readFloat() ); + // read texcoord list elements + for ( j = 0; j < nelements; ++j ) { + sgReadUInt( fp, &nbytes ); + buf.resize( nbytes ); + buf.reset(); + char *ptr = buf.get_ptr(); + sgReadBytes( fp, nbytes, ptr ); + int count = nbytes / (sizeof(float) * 2); + texcoords.reserve(count); + for ( k = 0; k < count; ++k ) { + texcoords.push_back( buf.readVec2f() ); + } } - } - } else if ( obj_type == SG_VA_INTEGER_LIST ) { - // read vertex attribute (integer) properties - read_properties( fp, nproperties ); + } else if ( obj_type == SG_VA_FLOAT_LIST ) { + // read vertex attribute (float) properties + read_properties( fp, nproperties ); - // read vertex attribute list elements - for ( j = 0; j < nelements; ++j ) { - sgReadUInt( fp, &nbytes ); - buf.resize( nbytes ); - buf.reset(); - char *ptr = buf.get_ptr(); - sgReadBytes( fp, nbytes, ptr ); - int count = nbytes / (sizeof(unsigned int)); - va_int.reserve(count); - for ( k = 0; k < count; ++k ) { - va_int.push_back( buf.readInt() ); + // read vertex attribute list elements + for ( j = 0; j < nelements; ++j ) { + sgReadUInt( fp, &nbytes ); + buf.resize( nbytes ); + buf.reset(); + char *ptr = buf.get_ptr(); + sgReadBytes( fp, nbytes, ptr ); + int count = nbytes / (sizeof(float)); + va_flt.reserve(count); + for ( k = 0; k < count; ++k ) { + va_flt.push_back( buf.readFloat() ); + } } - } - } else if ( obj_type == SG_POINTS ) { - // read point elements - read_object( fp, SG_POINTS, nproperties, nelements, - pts_v, pts_n, pts_c, pts_tcs, - pts_vas, pt_materials ); - } else if ( obj_type == SG_TRIANGLE_FACES ) { - // read triangle face properties - read_object( fp, SG_TRIANGLE_FACES, nproperties, nelements, - tris_v, tris_n, tris_c, tris_tcs, - tris_vas, tri_materials ); - } else if ( obj_type == SG_TRIANGLE_STRIPS ) { - // read triangle strip properties - read_object( fp, SG_TRIANGLE_STRIPS, nproperties, nelements, - strips_v, strips_n, strips_c, strips_tcs, - strips_vas, strip_materials ); - } else if ( obj_type == SG_TRIANGLE_FANS ) { - // read triangle fan properties - read_object( fp, SG_TRIANGLE_FANS, nproperties, nelements, - fans_v, fans_n, fans_c, fans_tcs, - fans_vas, fan_materials ); - } else { - // unknown object type, just skip - read_properties( fp, nproperties ); + } else if ( obj_type == SG_VA_INTEGER_LIST ) { + // read vertex attribute (integer) properties + read_properties( fp, nproperties ); - // read elements - for ( j = 0; j < nelements; ++j ) { - sgReadUInt( fp, &nbytes ); - // cout << "element size = " << nbytes << endl; - if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); } - char *ptr = buf.get_ptr(); - sgReadBytes( fp, nbytes, ptr ); + // read vertex attribute list elements + for ( j = 0; j < nelements; ++j ) { + sgReadUInt( fp, &nbytes ); + buf.resize( nbytes ); + buf.reset(); + char *ptr = buf.get_ptr(); + sgReadBytes( fp, nbytes, ptr ); + int count = nbytes / (sizeof(unsigned int)); + va_int.reserve(count); + for ( k = 0; k < count; ++k ) { + va_int.push_back( buf.readInt() ); + } + } + } else if ( obj_type == SG_POINTS ) { + // read point elements + read_object( fp, SG_POINTS, nproperties, nelements, + pts_v, pts_n, pts_c, pts_tcs, + pts_vas, pt_materials ); + } else if ( obj_type == SG_TRIANGLE_FACES ) { + // read triangle face properties + read_object( fp, SG_TRIANGLE_FACES, nproperties, nelements, + tris_v, tris_n, tris_c, tris_tcs, + tris_vas, tri_materials ); + } else if ( obj_type == SG_TRIANGLE_STRIPS ) { + // read triangle strip properties + read_object( fp, SG_TRIANGLE_STRIPS, nproperties, nelements, + strips_v, strips_n, strips_c, strips_tcs, + strips_vas, strip_materials ); + } else if ( obj_type == SG_TRIANGLE_FANS ) { + // read triangle fan properties + read_object( fp, SG_TRIANGLE_FANS, nproperties, nelements, + fans_v, fans_n, fans_c, fans_tcs, + fans_vas, fan_materials ); + } else { + // unknown object type, just skip + read_properties( fp, nproperties ); + + // read elements + for ( j = 0; j < nelements; ++j ) { + sgReadUInt( fp, &nbytes ); + // cout << "element size = " << nbytes << endl; + if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); } + char *ptr = buf.get_ptr(); + sgReadBytes( fp, nbytes, ptr ); + } } } - if ( sgReadError() ) { - throw sg_io_exception("Error while reading object", sg_location(file, i)); + gzclose(fp); + fp = NULL; + } catch (std::exception&) { + if (fp) { + // close the file + gzclose(fp); } + + throw; // re-throw } - // close the file - gzclose(fp); return true; } @@ -984,112 +967,112 @@ bool SGBinObject::write_bin_file(const SGPath& file) return false; } - sgClearWriteError(); + try { + SG_LOG(SG_IO, SG_DEBUG, "points size = " << pts_v.size() + << " pt_materials = " << pt_materials.size() ); + SG_LOG(SG_IO, SG_DEBUG, "triangles size = " << tris_v.size() + << " tri_materials = " << tri_materials.size() ); + SG_LOG(SG_IO, SG_DEBUG, "strips size = " << strips_v.size() + << " strip_materials = " << strip_materials.size() ); + SG_LOG(SG_IO, SG_DEBUG, "fans size = " << fans_v.size() + << " fan_materials = " << fan_materials.size() ); - SG_LOG(SG_IO, SG_DEBUG, "points size = " << pts_v.size() - << " pt_materials = " << pt_materials.size() ); - SG_LOG(SG_IO, SG_DEBUG, "triangles size = " << tris_v.size() - << " tri_materials = " << tri_materials.size() ); - SG_LOG(SG_IO, SG_DEBUG, "strips size = " << strips_v.size() - << " strip_materials = " << strip_materials.size() ); - SG_LOG(SG_IO, SG_DEBUG, "fans size = " << fans_v.size() - << " fan_materials = " << fan_materials.size() ); + SG_LOG(SG_IO, SG_DEBUG, "nodes = " << wgs84_nodes.size() ); + SG_LOG(SG_IO, SG_DEBUG, "colors = " << colors.size() ); + SG_LOG(SG_IO, SG_DEBUG, "normals = " << normals.size() ); + SG_LOG(SG_IO, SG_DEBUG, "tex coords = " << texcoords.size() ); - SG_LOG(SG_IO, SG_DEBUG, "nodes = " << wgs84_nodes.size() ); - SG_LOG(SG_IO, SG_DEBUG, "colors = " << colors.size() ); - SG_LOG(SG_IO, SG_DEBUG, "normals = " << normals.size() ); - SG_LOG(SG_IO, SG_DEBUG, "tex coords = " << texcoords.size() ); + version = 10; + bool shortMaterialsRanges = + (max_object_size(pt_materials) < VERSION_7_MATERIAL_LIMIT) && + (max_object_size(fan_materials) < VERSION_7_MATERIAL_LIMIT) && + (max_object_size(strip_materials) < VERSION_7_MATERIAL_LIMIT) && + (max_object_size(tri_materials) < VERSION_7_MATERIAL_LIMIT); - version = 10; - bool shortMaterialsRanges = - (max_object_size(pt_materials) < VERSION_7_MATERIAL_LIMIT) && - (max_object_size(fan_materials) < VERSION_7_MATERIAL_LIMIT) && - (max_object_size(strip_materials) < VERSION_7_MATERIAL_LIMIT) && - (max_object_size(tri_materials) < VERSION_7_MATERIAL_LIMIT); + if ((wgs84_nodes.size() < 0xffff) && + (normals.size() < 0xffff) && + (texcoords.size() < 0xffff) && + shortMaterialsRanges) { + version = 7; // use smaller indices if possible + } - if ((wgs84_nodes.size() < 0xffff) && - (normals.size() < 0xffff) && - (texcoords.size() < 0xffff) && - shortMaterialsRanges) { - version = 7; // use smaller indices if possible - } + // write header magic - // write header magic + /** Magic Number for our file format */ + #define SG_FILE_MAGIC_NUMBER ( ('S'<<24) + ('G'<<16) + version ) - /** Magic Number for our file format */ - #define SG_FILE_MAGIC_NUMBER ( ('S'<<24) + ('G'<<16) + version ) + sgWriteUInt( fp, SG_FILE_MAGIC_NUMBER ); + time_t calendar_time = time(NULL); + sgWriteLong( fp, (int32_t)calendar_time ); - sgWriteUInt( fp, SG_FILE_MAGIC_NUMBER ); - time_t calendar_time = time(NULL); - sgWriteLong( fp, (int32_t)calendar_time ); + // calculate and write number of top level objects + int nobjects = 5; // gbs, vertices, colors, normals, texcoords + nobjects += count_objects(pt_materials); + nobjects += count_objects(tri_materials); + nobjects += count_objects(strip_materials); + nobjects += count_objects(fan_materials); - // calculate and write number of top level objects - int nobjects = 5; // gbs, vertices, colors, normals, texcoords - nobjects += count_objects(pt_materials); - nobjects += count_objects(tri_materials); - nobjects += count_objects(strip_materials); - nobjects += count_objects(fan_materials); + SG_LOG(SG_IO, SG_DEBUG, "total top level objects = " << nobjects); - SG_LOG(SG_IO, SG_DEBUG, "total top level objects = " << nobjects); + if (version == 7) { + sgWriteUShort( fp, (uint16_t) nobjects ); + } else { + sgWriteInt( fp, nobjects ); + } - if (version == 7) { - sgWriteUShort( fp, (uint16_t) nobjects ); - } else { - sgWriteInt( fp, nobjects ); - } + // write bounding sphere + write_header( fp, SG_BOUNDING_SPHERE, 0, 1); + sgWriteUInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes + sgWritedVec3( fp, gbs_center ); + sgWriteFloat( fp, gbs_radius ); - // write bounding sphere - write_header( fp, SG_BOUNDING_SPHERE, 0, 1); - sgWriteUInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes - sgWritedVec3( fp, gbs_center ); - sgWriteFloat( fp, gbs_radius ); + // dump vertex list + write_header( fp, SG_VERTEX_LIST, 0, 1); + sgWriteUInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes + for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) { + sgWriteVec3( fp, toVec3f(wgs84_nodes[i] - gbs_center)); + } - // dump vertex list - write_header( fp, SG_VERTEX_LIST, 0, 1); - sgWriteUInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes - for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) { - sgWriteVec3( fp, toVec3f(wgs84_nodes[i] - gbs_center)); - } + // dump vertex color list + write_header( fp, SG_COLOR_LIST, 0, 1); + sgWriteUInt( fp, colors.size() * sizeof(float) * 4 ); // nbytes + for ( i = 0; i < (int)colors.size(); ++i ) { + sgWriteVec4( fp, colors[i]); + } - // dump vertex color list - write_header( fp, SG_COLOR_LIST, 0, 1); - sgWriteUInt( fp, colors.size() * sizeof(float) * 4 ); // nbytes - for ( i = 0; i < (int)colors.size(); ++i ) { - sgWriteVec4( fp, colors[i]); - } + // dump vertex normal list + write_header( fp, SG_NORMAL_LIST, 0, 1); + sgWriteUInt( fp, normals.size() * 3 ); // nbytes + char normal[3]; + for ( i = 0; i < (int)normals.size(); ++i ) { + SGVec3f p = normals[i]; + normal[0] = (unsigned char)((p.x() + 1.0) * 127.5); + normal[1] = (unsigned char)((p.y() + 1.0) * 127.5); + normal[2] = (unsigned char)((p.z() + 1.0) * 127.5); + sgWriteBytes( fp, 3, normal ); + } - // dump vertex normal list - write_header( fp, SG_NORMAL_LIST, 0, 1); - sgWriteUInt( fp, normals.size() * 3 ); // nbytes - char normal[3]; - for ( i = 0; i < (int)normals.size(); ++i ) { - SGVec3f p = normals[i]; - normal[0] = (unsigned char)((p.x() + 1.0) * 127.5); - normal[1] = (unsigned char)((p.y() + 1.0) * 127.5); - normal[2] = (unsigned char)((p.z() + 1.0) * 127.5); - sgWriteBytes( fp, 3, normal ); - } + // dump texture coordinates + write_header( fp, SG_TEXCOORD_LIST, 0, 1); + sgWriteUInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes + for ( i = 0; i < (int)texcoords.size(); ++i ) { + sgWriteVec2( fp, texcoords[i]); + } - // dump texture coordinates - write_header( fp, SG_TEXCOORD_LIST, 0, 1); - sgWriteUInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes - for ( i = 0; i < (int)texcoords.size(); ++i ) { - sgWriteVec2( fp, texcoords[i]); - } + write_objects(fp, SG_POINTS, pts_v, pts_n, pts_c, pts_tcs, pts_vas, pt_materials); + write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tcs, tris_vas, tri_materials); + write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tcs, strips_vas, strip_materials); + write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tcs, fans_vas, fan_materials); - write_objects(fp, SG_POINTS, pts_v, pts_n, pts_c, pts_tcs, pts_vas, pt_materials); - write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tcs, tris_vas, tri_materials); - write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tcs, strips_vas, strip_materials); - write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tcs, fans_vas, fan_materials); - - // close the file - gzclose(fp); - - if ( sgWriteError() ) { - cout << "Error while writing file " << file << endl; + // close the file + gzclose(fp); + fp = NULL; + } catch (std::exception&) { + if (fp) { + gzclose(fp); + } return false; } - return true; }