Improved robustness when handling malformed stl files

This commit is contained in:
Paolo Cignoni 2018-02-22 19:26:33 +01:00
parent 70d9050ffc
commit a6e8112b6a
1 changed files with 64 additions and 31 deletions

View File

@ -24,6 +24,7 @@
#ifndef __VCGLIB_IMPORT_STL #ifndef __VCGLIB_IMPORT_STL
#define __VCGLIB_IMPORT_STL #define __VCGLIB_IMPORT_STL
#include <stdio.h> #include <stdio.h>
#include<algorithm>
#include <wrap/io_trimesh/io_mask.h> #include <wrap/io_trimesh/io_mask.h>
namespace vcg { namespace vcg {
@ -60,10 +61,11 @@ public:
}; };
enum STLError { enum STLError {
E_NOERROR, // 0 E_NOERROR, // 0
// Errori di open E_CANTOPEN, // 1
E_CANTOPEN, // 1 E_UNESPECTEDEOF, // 2
E_UNESPECTEDEOF // 2 E_MALFORMED, // 3
E_LAST
}; };
static const char *ErrorMsg(int error) static const char *ErrorMsg(int error)
@ -72,20 +74,23 @@ static const char *ErrorMsg(int error)
{ {
"No errors", "No errors",
"Can't open file", "Can't open file",
"Premature End of file", "Premature end of file",
"Malformed file",
}; };
if(error>2 || error<0) return "Unknown error"; if(error>=E_LAST || error<0) return "Unknown error";
else return stl_error_msg[error]; else return stl_error_msg[error];
}; }
static bool LoadMask(const char * filename, int &mask) static bool LoadMask(const char * filename, int &mask)
{ {
bool magicMode; bool magicMode,colored;
mask = Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX; mask = Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX;
if(IsSTLColored(filename,magicMode)) if(!IsSTLColored(filename, colored, magicMode))
mask |= Mask::IOM_FACECOLOR; return false;
return true;
if(colored) mask |= Mask::IOM_FACECOLOR;
return true;
} }
/* Try to guess if a stl has color /* Try to guess if a stl has color
@ -94,11 +99,19 @@ static bool LoadMask(const char * filename, int &mask)
* - It has to be binary * - It has to be binary
* - The per face attribute should be not zero * - The per face attribute should be not zero
* *
* return false in case of malformed files
*/ */
static bool IsSTLColored(const char * filename, bool &magicsMode) static bool IsSTLColored(const char * filename, bool &coloredFlag, bool &magicsMode)
{ {
if(IsSTLBinary(filename)==false) coloredFlag=false;
magicsMode=false;
bool binaryFlag;
if(IsSTLBinary(filename,binaryFlag)==false)
return false; return false;
if(binaryFlag==false)
return true;
FILE *fp = fopen(filename, "rb"); FILE *fp = fopen(filename, "rb");
char buf[STL_LABEL_SIZE+1]; char buf[STL_LABEL_SIZE+1];
fread(buf,sizeof(char),STL_LABEL_SIZE,fp); fread(buf,sizeof(char),STL_LABEL_SIZE,fp);
@ -123,39 +136,54 @@ static bool IsSTLColored(const char * filename, bool &magicsMode)
if(attr!=0) if(attr!=0)
{ {
if(Color4b::FromUnsignedR5G5B5(attr) != Color4b(Color4b::White)) if(Color4b::FromUnsignedR5G5B5(attr) != Color4b(Color4b::White))
return true; coloredFlag=true;
} }
} }
return false; return true;
} }
static bool IsSTLBinary(const char * filename) /* Try to guess if a stl is in binary format
*
* return false in case of malformed files
*/
static bool IsSTLBinary(const char * filename, bool &binaryFlag)
{ {
bool binary=false; binaryFlag=false;
FILE *fp = fopen(filename, "r"); FILE *fp = fopen(filename, "r");
/* Find size of file */ /* Find size of file */
fseek(fp, 0, SEEK_END); fseek(fp, 0, SEEK_END);
int file_size = ftell(fp); long file_size = ftell(fp);
int facenum; unsigned int facenum;
/* Check for binary or ASCII file */ /* Check for binary or ASCII file */
fseek(fp, STL_LABEL_SIZE, SEEK_SET); fseek(fp, STL_LABEL_SIZE, SEEK_SET);
fread(&facenum, sizeof(int), 1, fp); fread(&facenum, sizeof(unsigned int), 1, fp);
int expected_file_size=STL_LABEL_SIZE + 4 + (sizeof(short)+sizeof(STLFacet) )*facenum ; int expected_file_size=STL_LABEL_SIZE + 4 + (sizeof(short)+sizeof(STLFacet) )*facenum ;
if(file_size == expected_file_size) binary = true; if(file_size == expected_file_size)
unsigned char tmpbuf[128]; {
fread(tmpbuf,sizeof(tmpbuf),1,fp); binaryFlag = true;
for(unsigned int i = 0; i < sizeof(tmpbuf); i++) return true;
}
// second check, sometimes the size is a bit wrong,
// lets'make a test to check that we find only ascii stuff before assuming it is ascii
unsigned char tmpbuf[1000];
int byte_to_read = std::min(int(sizeof(tmpbuf)), int(file_size - 80));
fread(tmpbuf, byte_to_read,1,fp);
fclose(fp);
for(int i = 0; i < byte_to_read; i++)
{ {
if(tmpbuf[i] > 127) if(tmpbuf[i] > 127)
{ {
binary=true; binaryFlag=true;
if(abs(file_size-expected_file_size) > file_size/20 )
return false; //
break; break;
} }
} }
// Now we know if the stl file is ascii or binary. // Now we know if the stl file is ascii or binary.
fclose(fp); return true;
return binary;
} }
static int Open( OpenMeshType &m, const char * filename, int &loadMask, CallBackPos *cb=0) static int Open( OpenMeshType &m, const char * filename, int &loadMask, CallBackPos *cb=0)
@ -165,8 +193,11 @@ static int Open( OpenMeshType &m, const char * filename, int &loadMask, CallBack
return E_CANTOPEN; return E_CANTOPEN;
fclose(fp); fclose(fp);
loadMask |= Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX; loadMask |= Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX;
bool binaryFlag;
if(IsSTLBinary(filename)) return OpenBinary(m,filename,loadMask,cb); if(!IsSTLBinary(filename,binaryFlag))
return E_MALFORMED;
if(binaryFlag) return OpenBinary(m,filename,loadMask,cb);
else return OpenAscii(m,filename,cb); else return OpenAscii(m,filename,cb);
} }
@ -179,8 +210,10 @@ static int OpenBinary( OpenMeshType &m, const char * filename, int &loadMask, Ca
return E_CANTOPEN; return E_CANTOPEN;
} }
bool magicsMode; bool magicsMode,coloredFlag;
if(!IsSTLColored(filename,magicsMode)) if(!IsSTLColored(filename,coloredFlag, magicsMode))
return E_MALFORMED;
if(!coloredFlag)
loadMask = loadMask & (~Mask::IOM_FACECOLOR); loadMask = loadMask & (~Mask::IOM_FACECOLOR);
int facenum; int facenum;