diff --git a/configure.ac b/configure.ac index 6ea00ed1..0748a3f2 100644 --- a/configure.ac +++ b/configure.ac @@ -54,6 +54,9 @@ PKG_CHECK_MODULES(XMLPP, libxml++-2.6 >= 2.10.0) AC_OPENMP() +PKG_CHECK_MODULES(LIBZIP, libzip) + + dnl ------------------------------------------------------------------ dnl libreprap is (for now) built in from a copy in libraries/libreprap dnl thus avoiding cmake, and user confusion. diff --git a/libraries/Makefile.am b/libraries/Makefile.am index 97fa5380..5e781206 100644 --- a/libraries/Makefile.am +++ b/libraries/Makefile.am @@ -3,3 +3,4 @@ include libraries/libreprap/Makefile.am include libraries/vmmlib/Makefile.am include libraries/clipper/Makefile.am include libraries/poly2tri/Makefile.am +include libraries/amf/Makefile.am diff --git a/libraries/amf/Makefile.am b/libraries/amf/Makefile.am new file mode 100644 index 00000000..229e79fe --- /dev/null +++ b/libraries/amf/Makefile.am @@ -0,0 +1,93 @@ +noinst_LTLIBRARIES += libamf.la + +libamf_la_CPPFLAGS = \ + -I$(srcdir) \ + -I$(top_srcdir)/libraries/amf/amftools-code/include/ \ + -I$(top_srcdir)/libraries/amf/amftools-code/include/muparser \ + $(EXTRA_CFLAGS) + +libamf_la_SOURCES = \ + libraries/amf/amftools-code/include/Amf.h \ + libraries/amf/amftools-code/src/Amf.cpp \ + libraries/amf/amftools-code/include/nObject.h \ + libraries/amf/amftools-code/include/stb_image \ + libraries/amf/amftools-code/include/stb_image/stb_image.h \ + libraries/amf/amftools-code/include/nTexmap.h \ + libraries/amf/amftools-code/include/nMesh.h \ + libraries/amf/amftools-code/include/Amf.h \ + libraries/amf/amftools-code/include/nCoordinates.h \ + libraries/amf/amftools-code/include/SimpleImage.h \ + libraries/amf/amftools-code/include/Vec3D.h \ + libraries/amf/amftools-code/include/nComposite.h \ + libraries/amf/amftools-code/include/nNormal.h \ + libraries/amf/amftools-code/include/X3D_File.h \ + libraries/amf/amftools-code/include/nVertex.h \ + libraries/amf/amftools-code/include/XmlCompress.h \ + libraries/amf/amftools-code/include/nMaterial.h \ + libraries/amf/amftools-code/include/muparser/muParser.h \ + libraries/amf/amftools-code/include/muparser/muParserCallback.h \ + libraries/amf/amftools-code/include/muparser/muParserBytecode.h \ + libraries/amf/amftools-code/include/muparser/muParserBase.h \ + libraries/amf/amftools-code/include/muparser/muParserError.h \ + libraries/amf/amftools-code/include/muparser/muParserTokenReader.h \ + libraries/amf/amftools-code/include/muparser/muParserStack.h \ + libraries/amf/amftools-code/include/muparser/muParserDef.h \ + libraries/amf/amftools-code/include/muparser/muParserToken.h \ + libraries/amf/amftools-code/include/muparser/muParserFixes.h \ + libraries/amf/amftools-code/include/nTriangle.h \ + libraries/amf/amftools-code/include/nVertices.h \ + libraries/amf/amftools-code/include/nInstance.h \ + libraries/amf/amftools-code/include/Mesh.h \ + libraries/amf/amftools-code/include/rapidxml/rapidxml_print.h \ + libraries/amf/amftools-code/include/rapidxml/rapidxml.h \ + libraries/amf/amftools-code/include/STL_File.h \ + libraries/amf/amftools-code/include/nConstellation.h \ + libraries/amf/amftools-code/include/nColor.h \ + libraries/amf/amftools-code/include/Equation.h \ + libraries/amf/amftools-code/include/nMetadata.h \ + libraries/amf/amftools-code/include/AMF_File.h \ + libraries/amf/amftools-code/include/nAmf.h \ + libraries/amf/amftools-code/include/nTexture.h \ + libraries/amf/amftools-code/include/nEdge.h \ + libraries/amf/amftools-code/include/MeshSlice.h \ + libraries/amf/amftools-code/include/XmlStream.h \ + libraries/amf/amftools-code/include/zip \ + libraries/amf/amftools-code/include/zip/zip.h \ + libraries/amf/amftools-code/include/zip/unzip.h \ + libraries/amf/amftools-code/include/nVolume.h \ + libraries/amf/amftools-code/src/zip/zip.cpp \ + libraries/amf/amftools-code/src/zip/unzip.cpp \ + libraries/amf/amftools-code/src/nMetadata.cpp \ + libraries/amf/amftools-code/src/nTriangle.cpp \ + libraries/amf/amftools-code/src/nTexmap.cpp \ + libraries/amf/amftools-code/src/nTexture.cpp \ + libraries/amf/amftools-code/src/Equation.cpp \ + libraries/amf/amftools-code/src/nObject.cpp \ + libraries/amf/amftools-code/src/nVertex.cpp \ + libraries/amf/amftools-code/src/nComposite.cpp \ + libraries/amf/amftools-code/src/AMF_File.cpp \ + libraries/amf/amftools-code/src/nInstance.cpp \ + libraries/amf/amftools-code/src/nCoordinates.cpp \ + libraries/amf/amftools-code/src/nAmf.cpp \ + libraries/amf/amftools-code/src/XmlCompress.cpp \ + libraries/amf/amftools-code/src/nMesh.cpp \ + libraries/amf/amftools-code/src/nVertices.cpp \ + libraries/amf/amftools-code/src/nNormal.cpp \ + libraries/amf/amftools-code/src/nColor.cpp \ + libraries/amf/amftools-code/src/STL_File.cpp \ + libraries/amf/amftools-code/src/nVolume.cpp \ + libraries/amf/amftools-code/src/nEdge.cpp \ + libraries/amf/amftools-code/src/SimpleImage.cpp \ + libraries/amf/amftools-code/src/nConstellation.cpp \ + libraries/amf/amftools-code/src/XmlStream.cpp \ + libraries/amf/amftools-code/src/stb_image/stb_image.cpp \ + libraries/amf/amftools-code/src/nMaterial.cpp \ + libraries/amf/amftools-code/src/Mesh.cpp \ + libraries/amf/amftools-code/src/X3D_File.cpp \ + libraries/amf/amftools-code/src/MeshSlice.cpp \ + libraries/amf/amftools-code/src/muparser/muParserError.cpp \ + libraries/amf/amftools-code/src/muparser/muParserBase.cpp \ + libraries/amf/amftools-code/src/muparser/muParser.cpp \ + libraries/amf/amftools-code/src/muparser/muParserTokenReader.cpp \ + libraries/amf/amftools-code/src/muparser/muParserCallback.cpp \ + libraries/amf/amftools-code/src/muparser/muParserBytecode.cpp diff --git a/libraries/amf/amftools-code/include/AMF_File.h b/libraries/amf/amftools-code/include/AMF_File.h new file mode 100644 index 00000000..ce0b1682 --- /dev/null +++ b/libraries/amf/amftools-code/include/AMF_File.h @@ -0,0 +1,270 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef AMFFILE_H +#define AMFFILE_H + +#include "nAmf.h" +#include "Vec3D.h" +#include "MeshSlice.h" + +//for file imports +#include "STL_File.h" +#include "X3D_File.h" + +/*DEPRECATED*/ enum EnvelopeData {ENVL_XMIN, ENVL_YMIN, ENVL_ZMIN, ENVL_XMAX, ENVL_YMAX, ENVL_ZMAX, ENVL_XSIZE, ENVL_YSIZE, ENVL_ZSIZE}; +enum InstanceParamD {INST_DX, INST_DY, INST_DZ, INST_RX, INST_RY, INST_RZ}; +enum ColorView {CV_ALL, CV_VERT, CV_TRI, CV_TRICOLOR, CV_TRITEX, CV_VOL, CV_OBJ, CV_MAT}; //what colors to draw the vertex +enum ViewMode {VM_SOLID, VM_WIREFRAME, VM_SOLIDPLUSWIRE}; + +class nObjectExt; + +class AmfFile : + protected nAmf +{ +public: + AmfFile(void); + ~AmfFile(void); + AmfFile(const AmfFile& In); + AmfFile& operator=(const AmfFile& In); + + //Amf I/O + bool Save(std::string AmfFilePath, bool Compressed = true); //LastError + bool Load(std::string AmfFilePath, bool StrictLoad = true); //LastError + bool ImportAmf(std::string AmfFilePath, bool StrictLoad = true); //merges this AMF with the current AMF + void ClearAll(); + + //importing meshes + bool ImportMesh(std::string MeshFilePath, int AmfObjectIndex=0, int AmfMeshIndex=0); //LastError //imports a mesh into a mesh node specified (stl or x3d only) + bool LoadStl(std::string StlFilePath); //LastError //imports an stl into a mesh node specified + bool GetStlMeshSize(double* XSize, double* YSize, double* ZSize); //LastError + bool ImportStl(int AmfObjectIndex=0, int AmfMeshIndex=0); //LastError //imports an stl into a mesh node specified + bool LoadX3d(std::string X3dFilePath, std::string ImagePath="", std::string* ImgPathErrorReturn = NULL); //LastError //imports an x3d into a mesh node specified + bool GetX3dMeshSize(double* XSize, double* YSize, double* ZSize); //LastError + bool ImportX3d(int AmfObjectIndex=0, int AmfMeshIndex=0); //LastError //imports an x3d into a mesh node specified + + //exporting meshes + bool ExportSTL(std::string StlFilePath); + + //Units + void SetImportUnits(UnitSystem Units); + UnitSystem GetUnits(void) {return aUnit;} + std::string GetUnitsString(void){return GetUnitsString(aUnit);} + std::string GetUnitsString(UnitSystem SysIn); + void SetUnits(UnitSystem UnitSystemIn) {aUnit = UnitSystemIn; UnitsExist = true;} + double ConvertUnits(double Value, UnitSystem OriginalUnits, UnitSystem DesiredUnits); + double ToCurrentUnits(double Value, UnitSystem OriginalUnits){return ConvertUnits(Value, OriginalUnits, aUnit);} + double FromCurrentUnits(double Value, UnitSystem DesiredUnits){return ConvertUnits(Value, aUnit, DesiredUnits);} + + //Size of Amf + double GetEnvelopeData(EnvelopeData Data); //DEPRECATED + + //TODO: rethink these + bool GetEnvlMin(double* pXMinOut, double* pYMinOut, double* pZMinOut, int RenderIndex=-1); + bool GetEnvlMax(double* pXMaxOut, double* pYMaxOut, double* pZMaxOut, int RenderIndex=-1); + bool GetEnvlSize(double* pXSizeOut, double* pYSizeOut, double* pZSizeOut, int RenderIndex=-1); + + bool GetEnvlRotQuat(double* pWRotOut, double* pXRotOut, double* pYRotOut, double* pZRotOut, int RenderIndex=-1); + bool GetEnvlRotAngleAxis(double* pAngleRadOut, double* pNXOut, double* pNYOut, double* pNZOut, int RenderIndex=-1); + bool GetEnvlOrigin(double* pXOriginOut, double* pYOriginOut, double* pZOriginOut, int RenderIndex=-1); + bool GetEnvlDims(double* pIDimOut, double* pJDimOut, double* pKDimOut, int RenderIndex=-1); + + + + bool Scale(double ScaleFactor, bool ScaleConstellations=true, bool ScaleEquations = false) {return Scale(ScaleFactor, ScaleFactor, ScaleFactor, ScaleConstellations, ScaleEquations);} + bool Scale(double ScaleFactorX, double ScaleFactorY, double ScaleFactorZ, bool ScaleConstellations=true, bool ScaleEquations = false); + + //Amf Objects: + int GetObjectCount(void) {return GetNumObjects();} + std::string GetObjectName(int ObjectIndex); + void RenameObject(int ObjectIndex, std::string NewName); + int AddObject(std::string Name = "") {AppendObject(Name); return Objects.size()-1;} + void RemoveObject(int ObjectIndex); + void TranslateObject(int ObjectIndex, double dx, double dy, double dz); + void RotateObject(int ObjectIndex, double rx, double ry, double rz); + + //Amf Meshes + int GetMeshCount(int ObjectIndex); + + //Amf Volumes + int GetVolumeCount(int ObjectIndex, int MeshIndex); + std::string GetVolumeName(int ObjectIndex, int MeshIndex, int VolumeIndex); + void RenameVolume(int ObjectIndex, int MeshIndex, int VolumeIndex, std::string NewName); + int GetVolumeMaterialIndex(int ObjectIndex, int MeshIndex, int VolumeIndex); + bool SetVolumeMaterialIndex(int ObjectIndex, int MeshIndex, int VolumeIndex, int MaterialIndex); + + //Amf Constellations: + int GetConstellationCount(void) {return GetNumConstellations();} + std::string GetConstellationName(int ConstellationIndex); + void RenameConstellation(int ConstellationIndex, std::string NewName); + int AddConstellation(std::string Name = "") {AppendConstellation(Name); return Constellations.size()-1;} + void RemoveConstellation(int ConstellationIndex); + bool IsConstellationReferencedBy(int ConstellationIndex, int ConstellationIndexToCheck); + + //Amf Instances + int GetInstanceCount(int ConstellationIndex); + int AddInstance(int ConstellationIndex); + void RemoveInstance(int ConstellationIndex, int InstanceIndex); + bool SetInstanceObjectIndex(int ConstellationIndex, int InstanceIndex, int InstanceObjectIndex); + bool SetInstanceConstellationIndex(int ConstellationIndex, int InstanceIndex, int InstanceConstellationIndex); + int GetInstanceObjectIndex(int ConstellationIndex, int InstanceIndex); + int GetInstanceConstellationIndex(int ConstellationIndex, int InstanceIndex); + bool SetInstanceParam(int ConstellationIndex, int InstanceIndex, InstanceParamD ParamD, double Value); + double GetInstanceParam(int ConstellationIndex, int InstanceIndex, InstanceParamD ParamD); + + //Amf Materials: + int GetMaterialCount(void) {return GetNumMaterials();} + std::string GetMaterialName(int MaterialIndex); + void RenameMaterial(int MaterialIndex, std::string NewName); + int AddMaterial(std::string Name = "") {AppendMaterial(Name); return Materials.size()-1;} + int AddMaterial(std::string Name, int Red, int Green, int Blue) {int MatIndex=AddMaterial(Name); SetMaterialColorI(MatIndex, Red, Green, Blue); return MatIndex;} + int AddMaterial(std::string Name, double Red, double Green, double Blue) {int MatIndex=AddMaterial(Name); SetMaterialColorD(MatIndex, Red, Green, Blue); return MatIndex;} + void RemoveMaterial(int MaterialIndex); + bool IsMaterialReferencedBy(int MaterialIndex, int MaterialIndexToCheck); + bool SetMaterialColorD(int MaterialIndex, double Red, double Green, double Blue); + bool SetMaterialColorI(int MaterialIndex, int Red, int Green, int Blue){return SetMaterialColorD(MaterialIndex, Red/255.0, Green/255.0, Blue/255.0);} + bool GetMaterialColorD(int MaterialIndex, double *Red, double *Green, double *Blue); + bool GetMaterialColorI(int MaterialIndex, int *Red, int *Green, int *Blue); + + //Amf Composites + int GetCompositeCount(int MaterialIndex); + void ClearComposites(int MaterialIndex); + int AddComposite(int MaterialIndex, int MaterialIndexToComposite = 0); + void RemoveComposite(int MaterialIndex, int CompositeIndex); + bool SetCompositeMaterialIndex(int MaterialIndex, int CompositeIndex, int CompositeMaterialIndex); //1-based (because 0 always VOID material + int GetCompositeMaterialIndex(int MaterialIndex, int CompositeIndex); //1-based (because 0 always VOID material + std::string GetCompositeEquation(int MaterialIndex, int CompositeIndex); //use! ToAmfString() + bool SetCompositeEquation(int MaterialIndex, int CompositeIndex, std::string Equation); + + + //Amf Textures: + int GetTextureCount(void) {return GetNumTextures();} + + //Output utilities + bool SetSubdivisionLevel(int Level=4); + void DrawGL(); //draw everything! +/// int GetRenderedMeshCount(void); //number of meshes drawn on the screen + unsigned char* GetSliceBitmapRGBA(double PixelSizeX, double PixelSizeY, double SliceHeightZ, int* XSizeOut, int* YSizeOut, double SurfaceDepth = 0.0); + int* GetSliceSegmentsXY(double ZHeight, int* NumSegmentsOut); + + //Errors and information + std::string GetInfoString(bool MeshInfo = true); + std::string* pLastErrorMsg() {return &LastError;} + std::string GetLastErrorMsg() {return LastError;} + + //Real time status info on long i/o operations + bool* pCancelIO() {return &CancelIO;} + int* pCurTick(){return &CurTick;} + int* pMaxTick(){return &MaxTick;} + std::string* pStatusMsg(){return &CurrentMessage;} + + + + + + std::vector RenderedObjs; //todo: this should be protected... + +protected: + //Amf I/O utilities + //bool CheckValid(bool Strict=true, std::string* pMessage = 0); + + //Import meshes Utilities/members + CSTL_File StlFile; + CX3D_File X3dFile; + enum Channel{CR, CG, CB, CA}; + int AddTexture(CSimpleImage* pImageIn, Channel ChannelToGet, bool TiledIn = true); //adds texture, returns the ID it was added with. If the volume already has a texture on this channel, it resizes the texture to add this one and adjusts all texmap coordinates accordingly + void ToOneTexturePerVolume(void); //if a volume references more than one texture + void X3dFillImportInfo(nMesh* pMesh, std::vector* pVolumes, std::vector* pCoordsBeginIndex); //resizes pVolume to the number of x3d shapes and fills with a pointer to the volume (creating new volumes) each should be loaded in to. alse fills pCoordsBeginIndex with what vertex index in the AMF mesh vertex list these coordinates start... + + //Units utilities + UnitSystem CurImportUnits; //units system of the current importing mesh... + double GetImportScaleFactor(void); //gets scaling factor based on CurImportUnits and current Amf Units. Sets amf units to CurImportUnits if first mesh to be imported. + + + //Amf Size Utilities/members + bool ComputeBoundingBox(); + void GetMinMax(double& xMinOut, double& yMinOut, double& zMinOut, double& xMaxOut, double& yMaxOut, double& zMaxOut); + void GetSize(double& xOut, double& yOut, double& zOut); + double MinX, MaxX, MinY, MaxY, MinZ, MaxZ; + + //Amf node Utilities + nObject* GetObject(int ObjectIndex, bool CanCreate = false); + nMesh* GetMesh(int ObjectIndex, int MeshIndex, bool CanCreate = false); + nVolume* GetVolume(int ObjectIndex, int MeshIndex, int VolumeIndex, bool CanCreate = false); + nConstellation* GetConstellation(int ConstellationIndex, bool CanCreate = false); + nMaterial* GetMaterial(int MaterialIndex, bool CanCreate = false); + nInstance* GetInstance(int ConstellationIndex, int InstanceIndex, bool CanCreate = false); + nComposite* GetComposite(int MaterialIndex, int CompositeIndex, bool CanCreate = false); + +// int MaterialIDToIndex(int MaterialID); + int MaterialIndexToID(int MaterialIndex) {nMaterial* pMat = GetMaterial(MaterialIndex); if(pMat) return pMat->aID; else return -1;} + + + //Real Time status info utilities + bool CancelIO; //!< Cancels any input/output operations + int CurTick; //!< Current progress (with respect to MaxTick) of the current input/output operation. + int MaxTick; //!< Total possible progress of the current input/output operation. + std::string CurrentMessage; //!< Status of the current input/output operation. + + //Rendering utilities + bool NeedRender; //Flag set to true whenever anything pertaining to the visuals of the amf is changed... + bool Render(); //generates RenderedObjs, calls RenderMeshes() if flag set; + int SubDivLevel; //how many times do we want to subdivide curved triangles? + ColorView CurColorView; + ViewMode CurViewMode; + bool RenderConstellation(nConstellation* pConst, std::vector* pIndexStack, Vec3D CurOff, CQuat CurRot, ColorView CurColorView, ViewMode CurViewMode, int SubDivLevel); //for recursion + bool RenderObject(nObject* pObj, std::vector* pIndexStack, Vec3D CurOff, CQuat CurRot, ColorView CurColorView, ViewMode CurViewMode, int SubDivLevel); //actually adds an object to RenderedObjs + nObjectExt* GetRenderObject(int RenderIndex); + CMeshSlice* GetRenderMesh(int RenderIndex); + + //Slicing Utilitoes + bool GenerateLayer(double PixelSizeX, double PixelSizeY, double SliceHeightZ, double SurfaceDepthIn = 0.0, std::string* pMessage = NULL); + void ImposeBitmap(CSimpleImage* pBase, CSimpleImage* pImposed, int BaseXOrigin, int BaseYOrigin, int XOrigin, int YOrigin); //X, Y origin is the location within pBase that pImposed should be added. + CSimpleImage CurSlice; + + //Errors and information utilities + std::string LastError; + void ClearError() {LastError = "";} +}; + + + + +#define d2r 2*3.1415926/360.0 + +class nObjectExt{ //class to "unwind" an nObject to from its constellations (and render/slice mesh to in absolute coordinates!) +public: + nObjectExt(nAmf* pAmfIn, nObject* ObjIn, std::vector* pIndexStack, Vec3D& OffsetIn, CQuat& RotIn, ColorView& CurColorViewIn, ViewMode& CurViewModeIn, int& SubDivLevelIn) {RenderMesh(pAmfIn, ObjIn, pIndexStack, OffsetIn, RotIn, CurColorViewIn, CurViewModeIn, SubDivLevelIn);} + nObjectExt(const nObjectExt& O) {*this = O;} //copy constructor + nObjectExt& operator=(const nObjectExt& O) {pAmf = O.pAmf; /*Offset = O.Offset; Rot = O.Rot; */Meshes = O.Meshes; MaterialIDs = O.MaterialIDs; return *this; }; //overload equals + + std::vector Meshes; //one for each VOLUME of each mesh... + std::vector MaterialIDs; //store material ID for each mesh + + static CColor nColor2CColor(nColor& ColorIn, Vec3D* pLoc = NULL); +// CQuat GetRot(void) {return Rot;} +// Vec3D GetOffset(void) {return Offset;} + +// Vec3D OriginalLoc(Vec3D& vIn){return (vIn-Offset).Rot(Rot.Inverse());} //returns the location of vIn in the original coordinate system of the object + +// static void GetColorCallback(double xIn, double yIn, double zIn, double* rOut, double* gOut, double* bOut, double* aOut, int aObjectID); //function to get color based on location +// static std::vector aThis; //keep a static array of pointers to all our objects so we can have a static callback function to get colors. + +protected: + nAmf* pAmf; + bool RenderMesh(nAmf* pAmfIn, nObject* pObj, std::vector* pIndexStack, Vec3D& OffsetIn, CQuat& RotIn, ColorView& CurColorView, ViewMode& CurViewMode, int& SubDivLevel); //call on + + bool AddTriangle(bool SubDivide, int CurSubDivLevel); //TODO + +// Vec3D Offset; +// CQuat Rot; +}; + + +#endif //AMFFILE_H diff --git a/libraries/amf/amftools-code/include/Amf.h b/libraries/amf/amftools-code/include/Amf.h new file mode 100644 index 00000000..b71b6f8e --- /dev/null +++ b/libraries/amf/amftools-code/include/Amf.h @@ -0,0 +1,200 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef AMF_H +#define AMF_H + +#include //inlude for std::string parameters + +#if defined(_WIN32) || defined(_WIN64) /*Windows*/ + #ifdef DLL_EXPORT + #define LIBSPEC __declspec(dllexport) + #elif defined(DLL_IMPORT) + #define LIBSPEC __declspec(dllimport) + #else + #define LIBSPEC + #endif +#else /*non-windows*/ + #define LIBSPEC /*TODO: Allow this header file to generate (and be distributed) with non-windows shared objects*/ +#endif + +class AmfFile; + +//!The Amf class encapsulates all the functionality of an AMF file, including the ability to load, save, import other mesh types, and output 3D openGL drawings and 2D color slices. Windows users have the option of #defining LIBSPEC as either (1) DLL_EXPORT to generate a dll using this header or (2) DLL_IMPORT to use this header in conjunction with a compiled dll. +class LIBSPEC Amf /*LIBSPEC macro allows windows users the option of using this class in DLL form */ +{ +public: + Amf(); + ~Amf(); + Amf(const Amf& In); + Amf& operator=(const Amf& In); + + //!Defines a unit system + enum aUnitSystem { + aUNIT_MM, //! for license details. +*******************************************************************************/ + +#ifndef EQUATION_H +#define EQUATION_H + +#include "muparser/muParser.h" + +class nAmf; + +class CEquation +{ +public: + CEquation(void); + ~CEquation(void); + CEquation(const CEquation& In) {*this = In;} //copy constructor + CEquation& operator=(const CEquation& In); //overload Equals + void Clear(void); //clear everything out to blank equation + + bool IsConst(void){return IsConstant;} + void FromConstant(double Value); //set the equation to a constant, non-varying value + void FromAmfString(std::string& EqIn, nAmf* pAmfIn); + std::string ToAmfString(void) const; + std::string Amf2MuParser(std::string& Equation); //translates from AMF syntax to MuParser syntax + std::string MuParser2Amf(std::string& Equation); //translates from MuParser syntax to AMF syntax + + bool CheckParse(std::string* pMessage = 0); //tries an evaluation to see if equation is valid... + double Eval(double x=0, double y=0, double z=0, bool UnitRange = false); //Evaluates at specified location. UnitRange truncates to between 0 and 1. UnitRange flag clamps result to between 0 and 1 + void Scale(double ScaleFactor); + + //list of textures to be able to evaluate tex() function calls in equations. This must be set whenever + nAmf* pAmf; + +private: + bool IsConstant; + double ConstantValue; + std::string* pEqCache; //remember the last equation we imported + + mu::Parser* pP; + void IniParser(); + + double XVar, YVar, ZVar; //these are mapped out directly to x, y, and z in MuParser + + void inline findAndReplace(std::string& source, const std::string& find, + const std::string& replace) { + const int fLen = (int)find.size(); + const int rLen = (int)replace.size(); + for (int pos = 0; + (pos = source.find(find, pos)) != (int)std::string::npos; + pos += rLen) { + source.replace(pos, fLen, replace); + } + } + + + + + typedef struct {unsigned long int s1, s2, s3;} taus_state; + static unsigned long int rand_seed(unsigned long int x); + static unsigned long int taus_get(taus_state* state); + static double prsm(double x, double y, double z=0, int k=0); + + //read and write strings to AMF file with CDATA, etc... + static nAmf* pAmfStatic; //unfortunately this must be static for MuParser to be able to use it... Set it to pTexList within the class whenever we call eval... + static double texture(int textureID, double uIn, double vIn, double wIn = 0); + + //additional functions and operators for MuParser to use for AMF equations + static mu::value_type Mod(mu::value_type Base, mu::value_type Div) {return fmod(Base, Div);} + static mu::value_type AND(mu::value_type v1, mu::value_type v2) {return v1 != 0 && v2 != 0;} + static mu::value_type OR(mu::value_type v1, mu::value_type v2) {return v1 != 0 || v2 != 0;} + static mu::value_type XOR(mu::value_type v1, mu::value_type v2) {return (v1==0 && v2!=0) || (v1 !=0 && v2==0);} + static mu::value_type NOT(mu::value_type v) { return v==0; } + static mu::value_type Floor(mu::value_type In) {return In>=0 ? (double)((int)In) : (double)((int)In-1);} + static mu::value_type Ceil(mu::value_type In) {return In>=0 ? (double)((int)In+1) : (double)((int)In);} + static mu::value_type Abs(mu::value_type In) {return fabs(In);} + static mu::value_type Rand(const mu::value_type* ArgIn, int NumArg) {switch (NumArg){case 0: return prsm(0,0); break; case 1: return prsm(ArgIn[0],0); break; case 2: return prsm(ArgIn[0],ArgIn[1]); break; case 3: return prsm(ArgIn[0],ArgIn[1],ArgIn[2]); break; default: return prsm(ArgIn[0],ArgIn[1],ArgIn[2],(int)(ArgIn[3]+0.5)); break; }} + static mu::value_type Tex(const mu::value_type* ArgIn, int NumArg) {switch (NumArg){case 3: return texture((int)(ArgIn[0]+0.5), ArgIn[1], ArgIn[2]); break; case 4: return texture((int)(ArgIn[0]+0.5), ArgIn[1], ArgIn[2], ArgIn[3]); break; default: return 0; break; }} + +}; + + + + + +#endif diff --git a/libraries/amf/amftools-code/include/Mesh.h b/libraries/amf/amftools-code/include/Mesh.h new file mode 100644 index 00000000..1e2eac2c --- /dev/null +++ b/libraries/amf/amftools-code/include/Mesh.h @@ -0,0 +1,249 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef MESH_H +#define MESH_H + +#include +#include +#include "Vec3D.h" + +#include "XmlStream.h" + +//basic RGB color container +struct CColor { //copied from GL_Utils.h + CColor(){r=-1; g=-1; b=-1;a=-1;}; + CColor (const CColor& C) {*this = C;} + CColor(double rIn, double gIn, double bIn){r=rIn; g=gIn; b=bIn; a=1.0;} + CColor(double rIn, double gIn, double bIn, double aIn){r=rIn; g=gIn; b=bIn; a=aIn;} + inline CColor& operator=(const CColor& C) {r = C.r; g = C.g; b = C.b; a = C.a; return *this; }; //overload equals + + double r, g, b, a; + bool isValid(void) const {if(r>=0.0 && r <=1.0 && g>=0.0 && g <=1.0 && b>=0.0 && b <=1.0 && a>=0.0 && a <=1.0) return true; else return false;}; +}; + +//structure to hold each vertex +struct CVertex { + CVertex() {Clear();} + CVertex(const Vec3D& av) {Clear(); v = av;} + CVertex(const Vec3D& an, const Vec3D& av) {Clear(); n = an; HasNormal=true; v = av;} + CVertex(const Vec3D& av, const CColor& ac) {Clear(); v = av; HasColor = true; VColor = ac;} + inline CVertex& operator=(const CVertex& V) {v=V.v; HasNormal=V.HasNormal; n=V.n;HasColor=V.HasColor; VColor = V.VColor; /*DrawOffset = V.DrawOffset;*/ return *this;}; //overload equals + CVertex(const CVertex& V) {*this = V;} // DrawAxis = V.DrawAxis; DrawAngle = V.DrawAngle;} + inline void Clear(){v = Vec3D(0,0,0); HasNormal = false; n = Vec3D(0,0,0); HasColor = false; VColor = CColor(1,1,1,1); /*DrawOffset = Vec3D(0,0,0);*/} + + void WriteXML(CXmlStreamWrite* pXML); + bool ReadXML(CXmlStreamRead* pXML); + + Vec3D v; //Vertex location + bool HasNormal; + Vec3D n; //normal + bool HasColor; + CColor VColor; +}; + +struct TexMap { + TexMap() {}; +// TexMap(const double U1In, const double U2In, const double U3In, const double V1In, const double V2In, const double V3In, const bool TexTileIn = false) {uc[0]=U1In; uc[1]=U2In; uc[2]=U3In; vc[0]=V1In; vc[1]=V2In; vc[2]=V3In; TexTile=TexTileIn;} + TexMap(const int TexIndexIn, const double U1In, const double U2In, const double U3In, const double V1In, const double V2In, const double V3In, const bool TexTileIn = false) {TexIndex=TexIndexIn; uc[0]=U1In; uc[1]=U2In; uc[2]=U3In; vc[0]=V1In; vc[1]=V2In; vc[2]=V3In; TexTile=TexTileIn;} +// inline TexMap& operator=(const TexMap& t) {uc[0]=t.uc[0]; uc[1]=t.uc[1]; uc[2]=t.uc[2]; vc[0]=t.vc[0]; vc[1]=t.vc[1]; vc[2]=t.vc[2]; TexTile = t.TexTile; return *this;}; //overload equals + inline TexMap& operator=(const TexMap& t) {TexIndex=t.TexIndex; uc[0]=t.uc[0]; uc[1]=t.uc[1]; uc[2]=t.uc[2]; vc[0]=t.vc[0]; vc[1]=t.vc[1]; vc[2]=t.vc[2]; TexTile = t.TexTile; return *this;}; //overload equals + TexMap(const TexMap& t) {*this = t;} + + int TexIndex; + double uc[3]; //u texture coordinates + double vc[3]; //v texture coordinates + bool TexTile; +}; + +//structure to hold each facet +struct CFacet { + CFacet() {Clear();} + CFacet(const int& av1, const int& av2, const int& av3) {Clear(); vi[0] = av1; vi[1] = av2; vi[2] = av3;} + CFacet(const Vec3D& an, const int& av1, const int& av2, const int& av3) {Clear(); n = an; vi[0] = av1; vi[1] = av2; vi[2] = av3;} + CFacet(const Vec3D& an, const int& av1, const int& av2, const int& av3, const int& NIn) {Clear(); n = an; vi[0] = av1; vi[1] = av2; vi[2] = av3; Name = NIn;} + CFacet(const Vec3D& an, const int& av1, const int& av2, const int& av3, const CColor& ac) {Clear(); n = an; vi[0] = av1; vi[1] = av2; vi[2] = av3; FColor = ac;} + inline CFacet& operator=(const CFacet& p) { vi[0]=p.vi[0]; vi[1]=p.vi[1]; vi[2]=p.vi[2]; n=p.n; FColor = p.FColor; HasEdge[0]=p.HasEdge[0]; HasEdge[1]=p.HasEdge[1]; HasEdge[2]=p.HasEdge[2]; ei[0]=p.ei[0]; ei[1]=p.ei[1]; ei[2]=p.ei[2]; HasName=p.HasName; Name = p.Name; HasTexture = p.HasTexture; Map = p.Map; return *this;}; //overload equals + CFacet(const CFacet& p) {*this = p;} + void Clear() {n=Vec3D(0,0,0); FColor = CColor(1,1,1,1); vi[0] = 0; vi[1] = 0; vi[2] = 0; HasEdge[0] = false; HasEdge[1] = false; HasEdge[2] = false; ei[0]=0; ei[1]=0; ei[2]=0; HasTexture = false; HasName = false; Name = -1;} + + int vi[3]; //vertex indices + bool HasEdge[3]; //correspond to indices in ei[] + int ei[3]; //edge indices ei[0] = vi[0] -> vi[1], ei[1] = vi[1] -> vi[2], ei[2] = vi[2] -> vi[0] + bool HasName; + int Name; //my name (for GL picking) + bool HasTexture; //do we want to draw a texture instead of a color for this facet? + TexMap Map; //mapping to a texture + bool HasColor; + CColor FColor; + + Vec3D n; //normal (computed from vertex locations + +}; + +struct CLine { + CLine() {Clear();} + CLine(const int& av1, const int& av2) {Clear(); vi[0] = av1; vi[1] = av2;} + CLine(const int& av1, const int& av2, Vec3D vt1, Vec3D vt2) {Clear(); vi[0] = av1; vi[1] = av2; vt[0]=vt1; vt[1]=vt2; HasTangent[0]=true; HasTangent[1]=true;} + inline CLine& operator=(const CLine& l) { vi[0]=l.vi[0]; vi[1]=l.vi[1]; HasTangent[0]=l.HasTangent[0]; HasTangent[1]=l.HasTangent[1]; vt[0]=l.vt[0]; vt[1]=l.vt[1]; return *this;}; //overload equals + CLine(const CLine& l) {*this = l;} + friend bool operator<(const CLine& L1, const CLine& L2) { if (L1.vi[0] == L2.vi[0]) return L1.vi[1] < L2.vi[1]; else return L1.vi[0] < L2.vi[0];} //for sorting + friend bool operator==(const CLine& L1, const CLine& L2) {return ((L1.vi[0]==L2.vi[0] && L1.vi[1]==L2.vi[1]));} // || (vi[0]==O.vi[1] && vi[1]==O.vi[0]));} //Is equal + void Clear(void) {vi[0]=-1; vi[1]=-1; HasTangent[0]=false; HasTangent[1]=false; vt[0]=Vec3D(0,0,0); vt[1]=Vec3D(0,0,0);} + + int vi[2]; //vertex indices + bool HasTangent[2]; //is this edge curved or not? (if so vt is ignored) + Vec3D vt[2]; //vertex tangents - always must go ccw (same direction as vertices!)) + + +}; + + + +class CTexture { + public: + CTexture() {Clear();} + ~CTexture() {} + CTexture& operator=(const CTexture& t) {Width = t.Width; Height = t.Height; RGBAImage = t.RGBAImage; Tiled = t.Tiled; GlTexInitialized=t.GlTexInitialized; GlTexName=t.GlTexName; return *this;}; //overload equals + CTexture(const CTexture& t) {*this = t;} + void Clear(){Tiled=false; Width=0; Height=0; GlTexInitialized=false; GlTexName=-1;} + + bool Tiled; //show tiled or transparent past edges? + int Width, Height; //, ActWidth, ActHeight; +// double XScale, YScale; + void LoadData(int WidthIn, int HeightIn, unsigned char* RGBAdata, bool TiledIn = true); + void LoadData(int WidthIn, int HeightIn, unsigned char* Rdata, unsigned char* Gdata, unsigned char* Bdata, unsigned char* Adata, bool TiledIn = true); + void LoadData(int WidthIn, int HeightIn, unsigned char* Rdata, unsigned char* Gdata, unsigned char* Bdata, bool TiledIn = true); + + + std::vector RGBAImage; + + bool GlTexInitialized; + unsigned int GlTexName; + unsigned int TexName(void); //returns the openGL texture name, or initializes it in ogl if it hasn't already + bool SetGlBorderColor(float r, float g, float b, float a); + +//private: +// void ResizeToMult2(void); //resizes the internal image to a multiple of 2, stores the factors. + +}; + +class CMesh +{ +public: + CMesh(void); + ~CMesh(void); + + CMesh(CMesh& s); + CMesh& operator=(const CMesh& s); + void Clear(); + + void WriteXML(CXmlStreamWrite* pXML, bool MeshOnly = false); //meshonly only store stl-equiavalent info (no color, normals, etc...) + bool ReadXML(CXmlStreamRead* pXML); + + bool Exists(void) {if (Facets.size() != 0) return true; else return false;} + void SetBBColor(double r, double g, double b){BoundBoxColor = CColor(r, g, b);} + + const CFacet* GetpFacet(int FacetIndex) const {return &Facets[FacetIndex];} + const CVertex* GetpVertex(int VertexIndex) const {return &Vertices[VertexIndex];} + const CLine* GetpLine(int LineIndex) const {return &Lines[LineIndex];} + CFacet* GetpFacet(int FacetIndex) {return &Facets[FacetIndex];} + CVertex* GetpVertex(int VertexIndex) {return &Vertices[VertexIndex];} + CLine* GetpLine(int LineIndex) {return &Lines[LineIndex];} + + + int GetFacetCount(){return (int)Facets.size();} + int GetVertexCount(){return (int)Vertices.size();} + int GetLineCount(){return (int)Lines.size();} + + + // file i/o + bool LoadSTL(std::string filename); + bool SaveSTL(std::string filename, bool Binary = true) const; + + void CalcFaceNormals(); //called to update the face normals... + void CalcVertNormals(); //called once for each new geometry (or after deformed...) (depends on face normals!!!) + + + //bool TexturesChanged; +#ifdef USE_OPEN_GL + void Draw(bool DrawBoundingBox = false); //requires OpenGL libs +#endif + + virtual void MeshChanged(void) {}; + + //add a facet + CFacet* AddFacet(const Vec3D& v1, const Vec3D& v2, const Vec3D& v3, bool QuickAdd = false); //adds a facet, checks vertex list for existing vertices... (should be called with vertices in CCW for proper normal calculation! + CFacet* AddFacet(const Vec3D& v1, const Vec3D& v2, const Vec3D& v3, const CColor& Col1, const CColor& Col2, const CColor& Col3, bool QuickAdd = false); //adds a facet... with color info + CFacet* AddFacet(const Vec3D& v1, const Vec3D& v2, const Vec3D& v3, const TexMap& MapIn); //adds a facet... with color info + + CFacet* AddFacet(const CVertex& v1, const CVertex& v2, const CVertex& v3); + CFacet* AddFacet(const CVertex& v1, const CVertex& v2, const CVertex& v3, const TexMap& MapIn); //adds a facet... with texture map + + void AddQuadFacet(const Vec3D& v1, const Vec3D& v2, const Vec3D& v3, const Vec3D& v4) {AddFacet(v1, v2, v3); AddFacet(v3, v4, v1);}; //Vertices should be CCW from outside + + CLine* AddLine(const CLine& l1){Lines.push_back(l1); return &Lines.back();} + + //Add a texture + CTexture* AddTexture(const CTexture& t1){Textures.push_back(t1); return &Textures.back();} + int GetTextureCount(void) {return Textures.size();} + + + Vec3D GetBBMin(void) {if (NeedBBCalc) UpdateBoundingBox(); return _CurBBMin;} + Vec3D GetBBMax(void) {if (NeedBBCalc) UpdateBoundingBox(); return _CurBBMax;} + Vec3D GetBBSize(void) {if (NeedBBCalc) UpdateBoundingBox(); return _CurBBMax - _CurBBMin;} + + + //manipulation... + void Scale(Vec3D d); + void Translate(Vec3D d); + void Rotate(Vec3D ax, double a); + void Rotate(CQuat QRot); //rotation by quaternion + void RotX(double a); + void RotY(double a); + void RotZ(double a); + + + void WeldClose(float Distance); //welds vertices that are nearby (within Distance). Removes deleted triangles... + void RemoveDupLines(void); + + std::vector GlNameIndexStack; //for OpenGL picking + + void SubdivideMe(void); //tmp + + +protected: + bool NeedBBCalc; +// void ComputeBoundingBox(Vec3D& pmin, Vec3D& pmax); + void UpdateBoundingBox(void); + + + bool LoadBinarySTL(std::string filename); + bool LoadAsciiSTL(std::string filename); + + + bool DrawNormals, DrawTextures, DrawEdges, DrawShaded, DrawSmooth, IgnoreNames; + CColor BodyColor; //base color to use in absence of face/vertex colors + CColor BoundBoxColor; //color when drawing bounding box + + std::vector Facets; + std::vector Vertices; + std::vector Lines; + std::vector Textures; + + Vec3D _CurBBMin, _CurBBMax; //cached bounding box values... (UpdateBoundingBox to update()) + + +//curved triangle stuff: + + void HermiteInterpolation(Vec3D v0, Vec3D n0, Vec3D t0, Vec3D v1, Vec3D n1, Vec3D t1, double s, Vec3D& vs, Vec3D& ts, Vec3D& ns); + + +}; +#endif diff --git a/libraries/amf/amftools-code/include/MeshSlice.h b/libraries/amf/amftools-code/include/MeshSlice.h new file mode 100644 index 00000000..489ba2aa --- /dev/null +++ b/libraries/amf/amftools-code/include/MeshSlice.h @@ -0,0 +1,115 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + + +#ifndef MESHSLICE_H +#define MESHSLICE_H + +#include "Mesh.h" +#include +//#include + +class CSimpleImage; +class nMaterial; //TODO: get rid of AMF dependence. +class nObjectExt; +class nAmf; + +enum IntersectionType {IT_INSIDE, IT_OUTSIDE, IT_EDGE}; + +//structure to hold envelope of each triangle +struct TriEnvelope { + TriEnvelope(const CMesh& m, const int& FacetIndex) {TriIndex=FacetIndex; const CFacet* pTmpFacet = m.GetpFacet(FacetIndex); Vec3D v0 = m.GetpVertex(pTmpFacet->vi[0])->v; Vec3D v1 = m.GetpVertex(pTmpFacet->vi[1])->v; Vec3D v2 = m.GetpVertex(pTmpFacet->vi[2])->v; MinX=v0.xv1.x?v0.x:v1.x; MaxX=MaxX>v2.x?MaxX:v2.x; MaxY=v0.y>v1.y?v0.y:v1.y; MaxY=MaxY>v2.y?MaxY:v2.y; MaxZ=v0.z>v1.z?v0.z:v1.z; MaxZ=MaxZ>v2.z?MaxZ:v2.z; } + inline TriEnvelope& operator=(const TriEnvelope& t) {MinX=t.MinX; MinY=t.MinY; MinZ=t.MinZ; MaxX=t.MaxX; MaxY=t.MaxY; MaxZ=t.MaxZ; TriIndex=t.TriIndex; return *this;}; //overload equals + TriEnvelope(const TriEnvelope& T) {*this = T;} // DrawAxis = V.DrawAxis; DrawAngle = V.DrawAngle;} + + double MinX, MinY, MinZ, MaxX, MaxY, MaxZ; + int TriIndex; + + static const bool MinXLessThan(const TriEnvelope& TriE1, const TriEnvelope& TriE2) {return TriE1.MinXCMesh::operator=(M); Initialized = false; pAmf=M.pAmf; pGetColor=M.pGetColor; pMaterial=M.pMaterial; Offset=M.Offset; Rot=M.Rot; OrigDim=M.OrigDim; /*OrigBBMin=M.OrigBBMin; pObjExt=M.pObjExt; */return *this;} //overload equals +// CMeshSlice& operator=(const CMeshSlice& M) {this->CMesh::operator=(M); pGetColor=M.pGetColor; return *this;} //overload equals + + + + //voxelizing stuff! + void MeshChanged(void); //invalidates all cached voxelizing info! + + bool IsInside(Vec3D* Point, double TexDepth = 0, CColor* pColor = NULL); + std::list CurTriLayer; //list of all triangle indices that cross the current z height (with paddings) + std::list CurTriLine; //array of all intersection points at the current z height at the current Y value + std::list CurTriPoint; //array of all intersection points at the current z height at the current Y value within range of the current X point! + std::vector CurTriPoints; //array of all intersection points at the current z height at the current Y value + double _TriLayerZ, _TriLineY, _TriLayerPZ, _TriLinePY, _TriPointPX; //height we calculated TriHeight, CurTriLine at + double _ZActual, _YActual, _XActual; //actual values of Y and Z, may be offset by epsilon from nominal o avoid hitting vertices dead on... + double _IsCurInside; //as we're iterating through X, are we currently inside or outside the part? + void FillTriLayer(double z, double ZPad = 0); //fills in CurTriLayer with all triangles that bridge this plane +-Pad + bool FillTriLine(double y, double z, double YPad = 0, double ZPad = 0); //fills in CurTriLine with all triangle the bridge the line in the layer + bool FillTriPoints(void); //fills points based on _YActual _ZActual, and the tris in TriLine returns false increments _YActual (to re-do FillTriLine with) and try again. + + //TODO: FillTriPoints should somehow happen within FillTriLine I think. deal with epsilon offsets and returning quickly... + + bool Initialized; + void Initialize(void); //does all precomputing + std::vector Envelopes; + std::vector::iterator ZEnvMaxIterator; //iterator left at the Maximum minZ value of last layer computation + std::list::iterator YEnvMaxIterator, XEnvMaxIterator; + std::vector::iterator XPtIter; + + double GetClosestFreeZ(double ZIn); //returns the next Z value that does not intersect any vertices + + // int GetXIntersections(double z, double y, double* pIntersections, int NumtoCheck, int* pToCheck); //returns the number of intersections, stored in pIntersections + + + IntersectionType IntersectXRay(CFacet* pFacet, double y, double z, double& XIntersect); + bool GetTriDist(CFacet* pFacetCheck, Vec3D* pPointIn, double& UOut, double& VOut, double& Dist2Out); //gets distance of provided point to closest UV coordinate of within the triangle. returns true if sensible distance, otherwise false + + +// static bool InsideTri(Vec3D& p, Vec3D& v0, Vec3D& v1, Vec3D& v2); +// static double Det(Vec3D& v0, Vec3D& v1, Vec3D& v2); +// IntersectionType IntersectXRay(CFacet* pFacet, double y, double z, Vec3D& p, double& pu, double& pv); + + + //TODO: Figure out a way to get volume color info into this class based on position!! + pFuncGetColor pGetColor; + nMaterial* pMaterial; + nAmf* pAmf; + + +// nObjectExt* pObjExt; //to get transforamtion for material + + Vec3D Offset; + CQuat Rot; + Vec3D OrigDim; // Original size BEFORE any rotation! (helpful for bounding boxes showing up rotated) +// Vec3D OrigBBMin; //Original minimum corner of the bounding box + + CQuat GetRot(void) {return Rot;} + Vec3D GetOffset(void) {return Offset;} + Vec3D GetOrigDim(void) {return OrigDim;} +// Vec3D GetOrigBBMin(void) {return OrigBBMin;} + + + Vec3D OriginalLoc(Vec3D& vIn); //returns the location of vIn in the original coordinate system of the object + + bool GetSlice(CSimpleImage* pImgOut, double ZHeight, double SurfDepth, double XMin, double XMax, double YMin, double YMax, int XPix, int YPix, int aObjectID=-1, std::string* pMessage = NULL); + +}; + +#endif //MESHSLICE_H diff --git a/libraries/amf/amftools-code/include/STL_File.h b/libraries/amf/amftools-code/include/STL_File.h new file mode 100644 index 00000000..0bc01bed --- /dev/null +++ b/libraries/amf/amftools-code/include/STL_File.h @@ -0,0 +1,96 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef STL_FILE_H +#define STL_FILE_H + +#include +#include +#include +#include "Vec3D.h" + + +//structure to hold each facet +struct CSTLFacet { + CSTLFacet() {} +// CSTLFacet(const CVec& an, const CVec& av1, const CVec& av2, const CVec& av3) : n(an), v1(av1), v2(av2), v3(av3) {} + CSTLFacet(const Vec3D& an, const Vec3D& av1, const Vec3D& av2, const Vec3D& av3) { n = an; v[0] = av1; v[1] = av2; v[2] = av3;} + CSTLFacet(const CSTLFacet& p) { v[0]=p.v[0]; v[1]=p.v[1]; v[2]=p.v[2]; n=p.n;} + Vec3D n; //normal + Vec3D v[3]; //vertices +}; + +class CSTL_File +{ +public: + CSTL_File(void); + ~CSTL_File(void); + + CSTL_File(CSTL_File& s); + CSTL_File& operator=(const CSTL_File& s); + + std::string ObjectName; + bool IsLoaded; + bool Load(std::string filename); + bool Save(std::string filename, bool Binary = true) const; + + void ComputeBoundingBox(Vec3D& pmin, Vec3D& pmax); + Vec3D GetSize(); + + std::vector Facets; + + void Clear() { Facets.clear(); ObjectName = "Default"; IsLoaded=false;} // clear/reset the list of trianges + int Size() const { return (int)Facets.size(); } + + void AddFacet(const Vec3D& n, const Vec3D& v1, const Vec3D& v2, const Vec3D& v3) {Facets.push_back(CSTLFacet(n,v1,v2,v3)); } + void AddFacet(float nx, float ny, float nz, float v1x, float v1y, float v1z, float v2x, float v2y, float v2z, float v3x, float v3y, float v3z){ AddFacet(Vec3D(nx, ny, nz), Vec3D(v1x,v1y,v1z), Vec3D(v2x,v2y,v2z), Vec3D(v3x,v3y,v3z)); } + +protected: + + // file i/o + bool LoadBinary(std::string filename); + bool LoadAscii(std::string filename); + + + + void Draw(bool bModelhNormals, bool bShaded); + + //add a facet + + + + //manipulation... +// void Rotate(CVec ax, double a); +// void RotX(double a); +// void RotY(double a); +// void RotZ(double a); +// void Scale(CVec d); +// void Translate(CVec d); + + + +}; + + + +class aWeldVertex { +public: + aWeldVertex(Vec3D LocIn, int MyInd) {v = LocIn; OrigIndex = MyInd;} //constructor + aWeldVertex(const aWeldVertex& Vert) {*this = Vert;} //copy constructor + inline aWeldVertex& operator=(const aWeldVertex& vIn) {v = vIn.v; OrigIndex = vIn.OrigIndex; return *this; }; //overload equals + +// aVertex v; + Vec3D v; + int OrigIndex; + + static inline bool IsSoftLessThan(const aWeldVertex& v1, const aWeldVertex& v2){if(abs(v1.v.z - v2.v.z) <= WeldThresh){ if(abs(v1.v.y - v2.v.y) <= WeldThresh){ return v1.v.x < v2.v.x-WeldThresh;}else return (v1.v.y < v2.v.y-WeldThresh);} else return (v1.v.z < v2.v.z-WeldThresh); } //Is less then (generates a "hash" for sorting vertices by z for set + static double WeldThresh; //weld threshold for importing from STL +}; + +#endif //STL_FILE_H diff --git a/libraries/amf/amftools-code/include/SimpleImage.h b/libraries/amf/amftools-code/include/SimpleImage.h new file mode 100644 index 00000000..3049f9be --- /dev/null +++ b/libraries/amf/amftools-code/include/SimpleImage.h @@ -0,0 +1,54 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef SIMPLEIMAGE_H +#define SIMPLEIMAGE_H + +#include + +//enum IndexMode {IM_VBW}; + + +class CSimpleImage +{ +public: + CSimpleImage(void); + ~CSimpleImage(void); + CSimpleImage(const CSimpleImage& In) {*this = In;} + CSimpleImage& operator=(const CSimpleImage& In); + + bool LoadImage(std::string FilePath); + + int Width(void) {return DataWidth;} + int Height(void) {return DataHeight;} + + bool AllocateRGBA(int WidthIn, int HeightIn); +// bool IndexFromRGBA(IndexMode IndexModeIn); + +// bool IndexExists() {if (pDataIndex) return true; else return false;} + bool HasAlphaChannel() {return AlphaPresent;} + unsigned char* GetRGBABits(void) {return pDataRGBA;} +// int* GetIndexBits(void) {return pDataIndex;} + + void Fill(unsigned char R, unsigned char G, unsigned char B, unsigned char A = 255); + +private: +// IndexMode CurIndexMode; + + bool AlphaPresent; + + unsigned char* pDataRGBA; + int DataWidth, DataHeight; +// int* pDataIndex; +// int PixelDataIndexWidth, PixelDataIndexHeight; + + +}; + +#endif //SIMPLEIMAGE_H diff --git a/libraries/amf/amftools-code/include/Vec3D.h b/libraries/amf/amftools-code/include/Vec3D.h new file mode 100644 index 00000000..ce557856 --- /dev/null +++ b/libraries/amf/amftools-code/include/Vec3D.h @@ -0,0 +1,141 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef _VEC3D_H +#define _VEC3D_H + +//Possible Linux portability issues: min, max + +#define __isnand(x) isnan(x) + + +#include +#include + +class CQuat; +class Vec3D { +public: + //Constructors + Vec3D() {x = 0; y = 0; z = 0;} + //Vec3D(const Vec3D& s) {x = s.x; y = s.y; z = s.z;} + Vec3D(const Vec3D& Vec) {*this = Vec;} //copy constructor + + Vec3D(double dx, double dy, double dz) {x = dx; y = dy; z = dz;} + inline Vec3D& operator=(const Vec3D& s) {x = s.x; y = s.y; z = s.z; return *this; }; //overload equals + + +#ifdef WIN32 + bool IsValid() const {return !_isnan(x) && _finite(x) && !_isnan(y) && _finite(y) && !_isnan(z) && _finite(z);} //is a valid vector? (funky windows _ versions...) +#else + bool IsValid() const {return !__isnand(x) && finite(x) && !__isnand(y) && finite(y) && !__isnand(z) && finite(z);} //is a valid vector? +#endif + + bool IsNear(Vec3D& s, double thresh) {return Dist2(s) 0) {x /= l;y /= l;z /= l;} return l;} //normalizes this vector + inline Vec3D Rot(Vec3D u, double a) {double ca = cos(a); double sa = sin(a); double t = 1-ca; return Vec3D((u.x*u.x*t + ca) * x + (u.x*u.y*t - u.z*sa) * y + (u.z*u.x*t + u.y*sa) * z, (u.x*u.y*t + u.z*sa) * x + (u.y*u.y*t+ca) * y + (u.y*u.z*t - u.x*sa) * z, (u.z*u.x*t - u.y*sa) * x + (u.y*u.z*t + u.x*sa) * y + (u.z*u.z*t + ca) * z);} //rotates by arbitrary vector arbitrary amount (http://www.cprogramming.com/tutorial/3d/rotation.html (Errors! LH one is actually RH one)) + inline Vec3D Rot(CQuat& Q); //below CQuat for linking sake... + + void RotZ(double a) {double xt = x*cos(a) - y*sin(a); double yt = x*sin(a) + y*cos(a); x = xt; y = yt;} //rotates about Z axis "a" radians + void RotY(double a) {double xt = x*cos(a) + z*sin(a); double zt = -x*sin(a) + z*cos(a); x = xt; z = zt;} //rotates about Y axis "a" radians + void RotX(double a) {double yt = y*cos(a) + z*sin(a); double zt = -y*sin(a) + z*cos(a); y = yt; z = zt;} //rotates about X axis "a" radians + + //Vector operations (don't change this object!) + inline Vec3D Cross(Vec3D& v) {return Vec3D(y*v.z-z*v.y,z*v.x-x*v.z,x*v.y-y*v.x);} //Cross product + inline double Dot(Vec3D& v) {return (x * v.x + y * v.y + z * v.z);} //Dot product + inline Vec3D Abs() {return Vec3D(fabs(x),fabs(y),fabs(z));} //Absolute value + inline Vec3D Normalized() { double l = sqrt(x*x+y*y+z*z); return (l>0?(*this)/l:(*this));} //returns normalized vec + inline Vec3D ProjXY() { return Vec3D(x,y,0);} //projection into the xy plane + inline double Length() {return sqrt(x*x+y*y+z*z);} //length of the vector + inline double Length2() {return (x*x+y*y+z*z);} //length squared of the vector + inline Vec3D Min(const Vec3D& s) {return Vec3D(xs.x ? x:s.x, y>s.y ? y:s.y, z>s.z ? z:s.z);} //max vector of the two + inline double Min() {double Min1 = (xy ? x:y); return (z>Max1 ? z:Max1);} //max element of this vector + inline Vec3D Scale(const Vec3D& v) {return Vec3D(x*v.x, y*v.y, z*v.z);} //scales by another vector + inline Vec3D ScaleInv(const Vec3D& v) {return Vec3D(x/v.x, y/v.y, z/v.z);} //scales by inverse of another vector + inline double Dist(const Vec3D& v) {return sqrt(Dist2(v));} //distance from another vector + inline double Dist2(const Vec3D& v) {return (v.x-x)*(v.x-x)+(v.y-y)*(v.y-y)+(v.z-z)*(v.z-z);} //distance from another vector + inline double AlignWith(Vec3D target, Vec3D& rotax) {Vec3D thisvec = Normalized(); Vec3D targvec = target.Normalized(); Vec3D rotaxis = thisvec.Cross(targvec); if (rotaxis.Length2() == 0) {rotaxis=target.ArbitraryNormal();} rotax = rotaxis.Normalized(); return acos(thisvec.Dot(targvec));} //returns vector (rotax) and amount (return val) to align this vector with target vector + inline Vec3D ArbitraryNormal() {Vec3D n = Normalized(); if (fabs(n.x) <= fabs(n.y) && fabs(n.x) <= fabs(n.z)){n.x = 1;} else if (fabs(n.y) <= fabs(n.x) && fabs(n.y) <= fabs(n.z)){n.y = 1;} else {n.z = 1;} return Cross(n).Normalized();} //generate arbitrary normal +}; + + +class CQuat // : public Vec3D //extending Vec3D saves us reimplementing some stuff, I think? this is not a comprehensive quaternion class at this point... +{ +public: + CQuat(void) {Clear();} + ~CQuat(void){}; + + CQuat(double dw, double dx, double dy, double dz) {w=dw; x=dx; y=dy; z=dz;} //constructor + CQuat(const CQuat& Quat) {*this = Quat;} //copy constructor + CQuat(const Vec3D& VecIn) {w = 0; x = VecIn.x; y = VecIn.y; z = VecIn.z;} + CQuat(double angle, const Vec3D &axis){Clear(); const double a = angle * 0.5f; const double s = sin(a); const double c = cos(a); w = c; x = axis.x * s; y = axis.y * s; z = axis.z * s;}; + inline CQuat& operator=(const CQuat& s) {w = s.w; x = s.x; y = s.y; z = s.z; return *this; }; //overload equals + inline void Clear() {w=1; x=0; y=0; z=0;} // for (int i=0; i<3; i++){for (int j=0; j<3; j++){M[i][j] = 0;iM[i][j] = 0;}}}; + + inline Vec3D ToVec(void) {return Vec3D(x, y, z);} //shouldnt be necessaty... should be able to just set equal... + + friend CQuat operator+(const CQuat& s1, const CQuat& s2) {return CQuat(s1.w+s2.w, s1.x+s2.x, s1.y+s2.y, s1.z+s2.z);} //Plus + CQuat& operator+=(const CQuat& s) {w += s.w; x += s.x; y += s.y; z += s.z; return *this;} //add and set + friend CQuat operator-(const CQuat& s1, const CQuat& s2) {return CQuat(s1.w-s2.w, s1.x-s2.x, s1.y-s2.y, s1.z-s2.z);} //Minus + CQuat& operator-=(const CQuat& s) {w -= s.w; x -= s.x; y -= s.y; z -= s.z; return *this;} //subract and set + + inline CQuat operator*(double f) {return CQuat(w*f, x*f, y*f, z*f);}; //scalar multiplication + friend CQuat operator*(double f, CQuat v) {return CQuat(v.w*f, v.x*f, v.y*f, v.z*f);}; + inline CQuat operator*(const CQuat& f) {return CQuat(w*f.w - x*f.x - y*f.y - z*f.z, w*f.x + x*f.w + y*f.z - z*f.y, w*f.y - x*f.z + y*f.w + z*f.x, w*f.z + x*f.y - y*f.x + z*f.w);}; //overload Quaternion multiplication! + operator Vec3D() {return Vec3D(x, y, z);}; + + inline double Length() {return sqrt(Length2());} //length of the vector + inline double Length2() {return (w*w+x*x+y*y+z*z);} //length squared of the vector + double Normalize(void) {double l = Length(); if (l == 0){w = 1; x = 0; y = 0; z = 0;} else if (l > 0) {w /= l; x /= l; y /= l; z /= l;} return l;}; + CQuat Inverse(void) {double n = w*w + x*x + y*y + z*z; return CQuat(w/n, -x/n, -y/n, -z/n); }; + CQuat Conjugate(void) {return CQuat(w, -x, -y, -z);}; + + double w, x, y, z; + + double M [3][3]; + double iM [3][3]; + void CalcMatrix(void) {M[0][0] = 1.0f-2.0f*(y*y+z*z); M[0][1] = 2.0f*(y*x-z*w); M[0][2] = 2.0f*(z*x+y*w); M[1][1] = 1.0f-2.0f*(x*x+z*z); M[1][2] = 2.0f*(z*y-x*w); M[2][2] = 1.0f-2.0f*(x*x+y*y); M[1][0] = M[0][1]; M[2][1] = M[1][2]; M[2][0] = M[0][2];}; + void CalciMatrix(void) {double determinant = -M[0][2]*M[1][1]*M[2][0] + M[0][1]*M[1][2]*M[2][0] + M[0][2]*M[1][0]*M[2][1] - M[0][0]*M[1][2]*M[2][1] - M[0][1]*M[1][0]*M[2][2] + M[0][0]*M[1][1]*M[2][2]; double k = 1.0 / determinant; iM[0][0] = (M[1][1]*M[2][2] - M[2][1]*M[1][2])*k; iM[0][1] = (M[2][1]*M[0][2] - M[0][1]*M[2][2])*k; iM[0][2] = (M[0][1]*M[1][2] - M[1][1]*M[0][2])*k; iM[1][0] = (M[1][2]*M[2][0] - M[2][2]*M[1][0])*k; iM[1][1] = (M[2][2]*M[0][0] - M[0][2]*M[2][0])*k; iM[1][2] = (M[0][2]*M[1][0] - M[1][2]*M[0][0])*k; iM[2][0] = (M[1][0]*M[2][1] - M[2][0]*M[1][1])*k; iM[2][1] = (M[2][0]*M[0][1] - M[0][0]*M[2][1])*k; iM[2][2] = (M[0][0]*M[1][1] - M[1][0]*M[0][1])*k;}; + void AngleAxis(double &angle, Vec3D &axis) {if (w>1.0) w = 1.0; double squareLength = x*x + y*y + z*z; if (squareLength>0.0000000000001){angle = 2.0f * (double) acos(w); double inverseLength = 1.0f / (double) pow(squareLength, 0.5); axis.x = x * inverseLength; axis.y = y * inverseLength; axis.z = z * inverseLength;} else{angle = 0.0f; axis.x = 1.0f; axis.y = 0.0f; axis.z = 0.0f;}}; +}; + + +inline Vec3D Vec3D::Rot(CQuat& Q){return (Q*CQuat(*this)*Q.Conjugate()).ToVec();} + + +#endif //_VEC3D_H diff --git a/libraries/amf/amftools-code/include/X3D_File.h b/libraries/amf/amftools-code/include/X3D_File.h new file mode 100644 index 00000000..5aa2ad95 --- /dev/null +++ b/libraries/amf/amftools-code/include/X3D_File.h @@ -0,0 +1,140 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef CX3D_FILE_H +#define CX3D_FILE_H + +#include +#include +#include "SimpleImage.h" +//#include +//#include + + +class CXmlStreamRead; + + +class CX3D_File; + +enum X3dLoadResult {XLR_SUCCESS, XLR_BADFILEPATH, XLR_NOSHAPE, XLR_BADIMAGEPATH}; + +class xAppearanceNode +{ +public: + xAppearanceNode(void){Clear();} + ~xAppearanceNode(void){}; + xAppearanceNode& operator=(const xAppearanceNode& x) {ImageTexture=x.ImageTexture; MatColorRed=x.MatColorRed; MatColorGreen=x.MatColorGreen; MatColorBlue=x.MatColorBlue; repeatS=x.repeatS; repeatT=x.repeatT; return *this;} //overload equals + xAppearanceNode(const xAppearanceNode& x) {*this = x;} + void Clear() {repeatS = true; repeatT = true; MatColorRed = -1; MatColorGreen = -1; MatColorBlue = -1;} + + X3dLoadResult ReadXML(CXmlStreamRead* pXML, CX3D_File* pX3dFile, std::string* pRetMessage = NULL); + + CSimpleImage ImageTexture; +// QImage ImageTexture; + bool repeatS, repeatT; + + double MatColorRed; + double MatColorGreen; + double MatColorBlue; +}; + +enum ColChan {CC_R=0, CC_G=1, CC_B=2, CC_A=3}; +enum TexCoordAxis {TCA_S=0, TCA_T=1}; +enum XYZAxis {AX_X=0, AX_Y=1, AX_Z=2}; + +class xIndexedFaceSetNode +{ +public: + xIndexedFaceSetNode(void){Clear();} + ~xIndexedFaceSetNode(void){}; + xIndexedFaceSetNode& operator=(const xIndexedFaceSetNode& x); //overload equals + xIndexedFaceSetNode(const xIndexedFaceSetNode& x) {*this = x;} + void Clear(); + + X3dLoadResult ReadXML(CXmlStreamRead* pXML, std::string* pRetMessage = NULL); + std::string CoordDef; //the name of this particular coordinate set + std::string CoordUse; //another coordinate set to use + std::vector coordIndex; + std::vector Coordinate; + + std::vector texCoordIndex; + std::vector TextureCoordinate; + + bool colorPerVertex; + std::vector colorIndex; + std::vector Color; + std::vector ColorRGBA; + +//information to load x3d! + bool FillDerivedInfo(); //fills in these calculated other parameters for easy access later. + int NumVertPerFacet; //triangles or quads? + bool Colors; + bool ColByIndex; + bool HasAlpha; + bool HasTexture; + int GetNumTriangles(); //returns 2x number of quads if x3d has quads... + int GetNumCoords(){return Coordinate.size()/3;} //return number of points (coords) + int GetCoordInd(int TriNum, int VertNum); //VertNum is 0, 1, or 2 + inline double GetCoord(int CoordNum, XYZAxis Axis) {return Coordinate[CoordNum*3+(int)Axis];} + + double GetColorFace(int TriNum, ColChan Chan); //triangle color (make sure Color && !colorPerVertex and HasAlpha if requesting alpha) + double GetColorVert(int CoordNum, ColChan Chan); //vertex color (make sure Color && colorPerVertex and HasAlpha if requesting alpha) + double GetColorVert(int TriNum, int VertNum, ColChan Chan); //vertex color (make sure Color && colorPerVertex and Hasplpha if requesting alpha) + int GetTexCoordInd(int TriNum, int VertNum); + inline double GetTexCoord(int TriNum, int VertNum, TexCoordAxis Axis){ return TextureCoordinate[GetTexCoordInd(TriNum, VertNum)*2+(int)Axis];} + + + + +}; + +class xShapeNode +{ +public: + xShapeNode(void) {Clear();}; + ~xShapeNode(void) {}; + xShapeNode& operator=(const xShapeNode& x) {xAppearance=x.xAppearance; xIndexedFaceSet=x.xIndexedFaceSet; return *this;} //overload equals + xShapeNode(const xShapeNode& x) {*this = x;} + void Clear() {xAppearance.Clear(); xIndexedFaceSet.Clear();} + X3dLoadResult ReadXML(CXmlStreamRead* pXML, CX3D_File* pX3dFile, std::string* pRetMessage = NULL); + + xAppearanceNode xAppearance; + xIndexedFaceSetNode xIndexedFaceSet; + bool IsIndexedFaceSet(void) {return !(xIndexedFaceSet.Coordinate.size() == 0 && xIndexedFaceSet.coordIndex.size() == 0);} +}; + + +class CX3D_File +{ +public: + CX3D_File(void); + ~CX3D_File(void); + CX3D_File& operator=(const CX3D_File& x) {xShapes=x.xShapes; filePath=x.filePath; ImagePath=x.ImagePath; errors=x.errors; return *this;} //overload equals + CX3D_File(const CX3D_File& x) {*this = x;} + void Clear() {xShapes.clear(); filePath=""; ImagePath=""; errors=""; IsLoaded=false;} + + std::vector xShapes; + std::string filePath, ImagePath, errors; + + bool IsLoaded; + X3dLoadResult Load(std::string filename, std::string ImgPathIn = ""); + + void GetMinMax(double& minX, double& minY, double& minZ, double& maxX, double& maxY, double& maxZ); + void GetSize(double& sizeX, double& sizeY, double& sizeZ); + + static void Str2Data(std::string* pS, std::vector* pD); // {std::stringstream ss(*pS); int i; while (ss >> i){pD->push_back(i);}} + static void Str2Data(std::string* pS, std::vector* pD); // {std::stringstream ss(*pS); double i; while (ss >> i){pD->push_back(i);}} + +}; + + + + + +#endif //CX3D_FILE_H diff --git a/libraries/amf/amftools-code/include/XmlCompress.h b/libraries/amf/amftools-code/include/XmlCompress.h new file mode 100644 index 00000000..34f3eaa9 --- /dev/null +++ b/libraries/amf/amftools-code/include/XmlCompress.h @@ -0,0 +1,25 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef CXMLCOMPRESS_H +#define CXMLCOMPRESS_H + +#include +#include + +//enum CompressType{COMP_ZIP}; + +bool CompressFile(std::string ZipName, std::string FilePath, std::string* pError = NULL); //filepath string to zip into location of ZipName (also a filepath) +bool CompressFiles(std::string ZipName, std::vector* pFilePaths, std::string* pError = NULL); //pointer to a vector of filepath strings to zip into location of ZipName (also a filepath) +bool GetCompressedFile(std::string ZipName, std::string FileName, std::vector *data, std::string* pError = NULL); +bool GetCompressedFiles(std::string ZipName, std::vector* pFileNames, std::vector >* data, std::string* pError = NULL); +bool UncompressAllFiles(std::string ZipName, std::vector* pFileNames, std::vector >* data, std::string* pError = NULL); + + +#endif //CXMLCOMPRESS_H diff --git a/libraries/amf/amftools-code/include/XmlStream.h b/libraries/amf/amftools-code/include/XmlStream.h new file mode 100644 index 00000000..4c0a85c7 --- /dev/null +++ b/libraries/amf/amftools-code/include/XmlStream.h @@ -0,0 +1,131 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef CXMLSTREAM_H +#define CXMLSTREAM_H + +#include "rapidxml/rapidxml.h" +#include +#include +#include + +static const char Ind[49] = " "; //maximum indent: 24 levels of nesting... + +//for quick run-through xml encoding +//Code by Jonathan Hiller. jdh74@cornell.edu + +//turn off case-sensitivity...? +//CDATA handling for all read strings... + +class CXmlStreamRead +{ +public: + CXmlStreamRead(); //std::string FilePathToRead); + ~CXmlStreamRead(void); + + //for loading + rapidxml::xml_document<> doc; + std::vector *> ElStack; + std::vector StrStack; //used on loading to keep track of which tags were last looked for + std::vector data; //buffer for reading/writing that won't go out of scope. + +// std::string filename; + std::string tmp; //temporary string (to avoid creating a bunch of these...) + rapidxml::xml_attribute<>* pTmpAtt; + rapidxml::xml_node<>* pTmpNode; + + bool LoadFile(std::string filename); + + std::string LastError; //last error encountered in the loading process... + std::string LastTagSearched; //keep track of which tag we last looks for. if new query same as last, look for siblings. + + bool OpenElement(std::string tag, bool RepeatingTag = false); //Find a child node. Subsequent calls will find children of this node with the same name. if RepeatingTag=true, calling repeatedly until it returns false will automatically close the last element it found. + void CloseElement(void); //call to return to parent element level + + //looks for and (if finds) loads entire element with the provided tag + bool GetElementS(std::string tag, std::string* pStringReturn, bool RepeatingTag = false); + inline bool GetElementD(std::string tag, double* pDoubleReturn, bool RepeatingTag = false) {if (GetElementS(tag, &tmp, RepeatingTag)){*pDoubleReturn = atof(tmp.c_str()); return true;} return false;} + inline bool GetElementF(std::string tag, float* pFloatReturn, bool RepeatingTag = false) {if (GetElementS(tag, &tmp, RepeatingTag)){*pFloatReturn = (float)atof(tmp.c_str()); return true;}return false;} + inline bool GetElementI(std::string tag, int* pIntReturn, bool RepeatingTag = false) {if (GetElementS(tag, &tmp, RepeatingTag)){*pIntReturn = atoi(tmp.c_str()); return true;} return false;} + inline bool GetElementB(std::string tag, bool* pBoolReturn, bool RepeatingTag = false) {int tmpInt; if (!GetElementI(tag, &tmpInt, RepeatingTag)) return false; *pBoolReturn=(tmpInt == 0)?false:true; return true;} + + //looks for and (if finds) loads attributes of the open element + //CDATA!!! + bool GetElAttS(std::string Att, std::string* pStringReturn);/* { + *pStringReturn = std::string(ElStack.back()->first_attribute(Att.c_str(), 0, false)->value()); + return ("" == (*pStringReturn))?false:true; + }*/ + inline bool GetElAttD(std::string Att, double* pDoubleReturn) {if (!GetElAttS(Att, &tmp)) return false; *pDoubleReturn = atof(tmp.c_str()); return true;} + inline bool GetElAttF(std::string Att, float* pFloatReturn) {if (!GetElAttS(Att, &tmp)) return false; *pFloatReturn = (float)atof(tmp.c_str()); return true;} + inline bool GetElAttI(std::string Att, int* pIntReturn) {if(!GetElAttS(Att, &tmp)) return false; *pIntReturn = atoi(tmp.c_str()); return true;} + inline bool GetElAttB(std::string Att, bool* pBoolReturn) {int tmpInt; if (!GetElAttI(Att, &tmpInt)) return false; *pBoolReturn=(tmpInt == 0)?false:true; return true;} + + //looks for and (if finds) loads attributes of the open element + bool GetElDataS(std::string* pStringReturn); // {*pStringReturn = std::string(ElStack.back()->first_node()->value()); return (*pStringReturn == "")?false:true;} + inline bool GetElDataD(double* pDoubleReturn) {if (!GetElDataS(&tmp)) return false; *pDoubleReturn = atof(tmp.c_str()); return true;} + inline bool GetElDataF(float* pFloatReturn) {if (!GetElDataS(&tmp)) return false; *pFloatReturn = (float)atof(tmp.c_str()); return true;} + inline bool GetElDataI(int* pIntReturn) {if(!GetElDataS(&tmp)) return false; *pIntReturn = atoi(tmp.c_str()); return true;} + inline bool GetElDataB(bool* pBoolReturn) {int tmpInt; if (!GetElDataI(&tmpInt)) return false; *pBoolReturn=(tmpInt == 0)?false:true; return true;} + +}; + +class CXmlStreamWrite +{ + //todo: enforce open/closing of tags at CloseElement() and Save() + //seperate opening of file to kick back error if no file was created... + //anytowrite can be changes to "needendtag/line, string removed... +public: + CXmlStreamWrite(); //opens the file for writing + ~CXmlStreamWrite(void) {}; + + bool BeginXmlFile(std::string FilePathToWrite, bool Compressed = false); + bool EndXmlFile(); + + int CurIndent; //for formatting... + FILE* fp; + + std::string filename; + std::string LastError; //last error encountered in the loading process... + + bool AnyToWrite; + bool IndentNextClose; + std::string ToWrite; //Buffer to hold closing parts of tags until next tag opened (mostly to allow for writting of attributes) + std::vector StrStack; //used on loading to keep track of which tags were last looked for + + void OpenElement(std::string tag); + void CloseElement(void); + + //should be only called directly after OpenElement()! + inline void SetElAttS(std::string Att, std::string Value) {if(fp) fprintf(fp," %s=\"%s\"", Att.c_str(), Value.c_str());} //adds an attribute to the current element in the stack + inline void SetElAttD(std::string Att, double Value) {if(fp) fprintf(fp," %s=\"%g\"", Att.c_str(), Value);} + inline void SetElAttF(std::string Att, float Value) {if(fp) fprintf(fp," %s=\"%g\"", Att.c_str(), Value);} + inline void SetElAttI(std::string Att, int Value) {if(fp) fprintf(fp," %s=\"%d\"", Att.c_str(), Value);} + inline void SetElAttB(std::string Att, bool Value) {if(fp) if(Value) fprintf(fp," %s=\"1\"", Att.c_str()); else fprintf(fp," %s=\"0\"", Att.c_str());} + + //should be only called directly after OpenElement() and any SetElAttribute()'s! Must be followed directly by CloseElement()! + inline void SetElDataS(std::string Value, bool CDATA = false) {if(fp){if (CDATA) fprintf(fp,">", Value.c_str()); else fprintf(fp,">%s", Value.c_str()); AnyToWrite = false; IndentNextClose = false;}} + inline void SetElDataD(double Value) {if(fp){ fprintf(fp,">%g", Value); AnyToWrite = false; IndentNextClose = false;}} + inline void SetElDataF(float Value) {if(fp){ fprintf(fp,">%g", Value);AnyToWrite = false; IndentNextClose = false;}} + inline void SetElDataI(int Value) {if(fp){ fprintf(fp,">%d", Value); AnyToWrite = false; IndentNextClose = false;}} + inline void SetElDataB(bool Value) {if(fp){if(Value) fprintf(fp,">1"); else fprintf(fp,">0"); AnyToWrite = false; IndentNextClose = false;}} + + //writes a complete Data element + inline void SetElementS(std::string tag, std::string Value, bool CDATA = false) {if(fp){if (AnyToWrite) fprintf(fp,"%s", ToWrite.c_str()); fwrite(Ind, 1, CurIndent, fp); if (CDATA) fprintf(fp,"<%s>\n", tag.c_str(), Value.c_str(), tag.c_str()); else fprintf(fp,"<%s>%s\n", tag.c_str(), Value.c_str(), tag.c_str()); AnyToWrite = false;}} + inline void SetElementD(std::string tag, double Value) {if(fp){if (AnyToWrite) fprintf(fp,"%s", ToWrite.c_str()); fwrite(Ind, 1, CurIndent, fp); fprintf(fp,"<%s>%g\n", tag.c_str(), Value, tag.c_str()); AnyToWrite = false;}} + inline void SetElementF(std::string tag, float Value) {if(fp){if (AnyToWrite) fprintf(fp,"%s", ToWrite.c_str()); fwrite(Ind, 1, CurIndent, fp); fprintf(fp,"<%s>%g\n", tag.c_str(), Value, tag.c_str()); AnyToWrite = false;}} + inline void SetElementI(std::string tag, int Value) {if(fp){if (AnyToWrite) fprintf(fp,"%s", ToWrite.c_str()); fwrite(Ind, 1, CurIndent, fp); fprintf(fp,"<%s>%d\n", tag.c_str(), Value, tag.c_str()); AnyToWrite = false;}} + inline void SetElementB(std::string tag, bool Value) {if(fp){if (AnyToWrite) fprintf(fp,"%s", ToWrite.c_str()); fwrite(Ind, 1, CurIndent, fp); if (Value) fprintf(fp,"<%s>1\n", tag.c_str(), tag.c_str()); else fprintf(fp,"<%s>0\n", tag.c_str(), tag.c_str()); AnyToWrite = false;}} + +protected: + bool WantCompressed; +}; + + + +#endif //CXMLSTREAM_H diff --git a/libraries/amf/amftools-code/include/muparser/muParser.h b/libraries/amf/amftools-code/include/muparser/muParser.h new file mode 100644 index 00000000..4a843fee --- /dev/null +++ b/libraries/amf/amftools-code/include/muparser/muParser.h @@ -0,0 +1,111 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef MU_PARSER_H +#define MU_PARSER_H + +//--- Standard includes ------------------------------------------------------------------------ +#include + +//--- Parser includes -------------------------------------------------------------------------- +#include "muParserBase.h" + + +/** \file + \brief Definition of the standard floating point parser. +*/ + +namespace mu +{ + /** \brief Mathematical expressions parser. + + Standard implementation of the mathematical expressions parser. + Can be used as a reference implementation for subclassing the parser. + + + (C) 2011 Ingo Berg
+ muparser(at)gmx.de +
+ */ + class Parser : public ParserBase + { + public: + + Parser(); + + virtual void InitCharSets(); + virtual void InitFun(); + virtual void InitConst(); + virtual void InitOprt(); + virtual void OnDetectVar(string_type *pExpr, int &nStart, int &nEnd); + + value_type Diff(value_type *a_Var, + value_type a_fPos, + value_type a_fEpsilon = 0) const; + + protected: + + // Trigonometric functions + static value_type Sin(value_type); + static value_type Cos(value_type); + static value_type Tan(value_type); + // arcus functions + static value_type ASin(value_type); + static value_type ACos(value_type); + static value_type ATan(value_type); + // hyperbolic functions + static value_type Sinh(value_type); + static value_type Cosh(value_type); + static value_type Tanh(value_type); + // arcus hyperbolic functions + static value_type ASinh(value_type); + static value_type ACosh(value_type); + static value_type ATanh(value_type); + // Logarithm functions + static value_type Log2(value_type); // Logarithm Base 2 + static value_type Log10(value_type); // Logarithm Base 10 + static value_type Ln(value_type); // Logarithm Base e (natural logarithm) + // misc + static value_type Exp(value_type); + static value_type Abs(value_type); + static value_type Sqrt(value_type); + static value_type Rint(value_type); + static value_type Sign(value_type); + + // Prefix operators + // !!! Unary Minus is a MUST if you want to use negative signs !!! + static value_type UnaryMinus(value_type); + + // Functions with variable number of arguments + static value_type Sum(const value_type*, int); // sum + static value_type Avg(const value_type*, int); // mean value + static value_type Min(const value_type*, int); // minimum + static value_type Max(const value_type*, int); // maximum + + static int IsVal(const char_type* a_szExpr, int *a_iPos, value_type *a_fVal); + }; +} // namespace mu + +#endif + diff --git a/libraries/amf/amftools-code/include/muparser/muParserBase.h b/libraries/amf/amftools-code/include/muparser/muParserBase.h new file mode 100644 index 00000000..5397f9ba --- /dev/null +++ b/libraries/amf/amftools-code/include/muparser/muParserBase.h @@ -0,0 +1,321 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef MU_PARSER_BASE_H +#define MU_PARSER_BASE_H + +//--- Standard includes ------------------------------------------------------------------------ +#include +#include +#include +#include +#include +#include + +//--- Parser includes -------------------------------------------------------------------------- +#include "muParserDef.h" +#include "muParserStack.h" +#include "muParserTokenReader.h" +#include "muParserBytecode.h" +#include "muParserError.h" + + +namespace mu +{ +/** \file + \brief This file contains the class definition of the muparser engine. +*/ + +//-------------------------------------------------------------------------------------------------- +/** \brief Mathematical expressions parser (base parser engine). + \author (C) 2011 Ingo Berg + + This is the implementation of a bytecode based mathematical expressions parser. + The formula will be parsed from string and converted into a bytecode. + Future calculations will be done with the bytecode instead the formula string + resulting in a significant performance increase. + Complementary to a set of internally implemented functions the parser is able to handle + user defined functions and variables. +*/ +class ParserBase +{ +friend class ParserTokenReader; + +private: + + /** \brief Typedef for the parse functions. + + The parse function do the actual work. The parser exchanges + the function pointer to the parser function depending on + which state it is in. (i.e. bytecode parser vs. string parser) + */ + typedef value_type (ParserBase::*ParseFunction)() const; + + /** \brief Type used for storing an array of values. */ + typedef std::vector valbuf_type; + + /** \brief Type for a vector of strings. */ + typedef std::vector stringbuf_type; + + /** \brief Typedef for the token reader. */ + typedef ParserTokenReader token_reader_type; + + /** \brief Type used for parser tokens. */ + typedef ParserToken token_type; + + /** \brief Maximum number of threads spawned by OpenMP when using the bulk mode. */ + static const int s_MaxNumOpenMPThreads = 4; + + public: + + /** \brief Type of the error class. + + Included for backwards compatibility. + */ + typedef ParserError exception_type; + + static void EnableDebugDump(bool bDumpCmd, bool bDumpStack); + + ParserBase(); + ParserBase(const ParserBase &a_Parser); + ParserBase& operator=(const ParserBase &a_Parser); + + virtual ~ParserBase(); + + value_type Eval() const; + value_type* Eval(int &nStackSize) const; + void Eval(value_type *results, int nBulkSize); + + int GetNumResults() const; + + void SetExpr(const string_type &a_sExpr); + void SetVarFactory(facfun_type a_pFactory, void *pUserData = NULL); + + void SetDecSep(char_type cDecSep); + void SetThousandsSep(char_type cThousandsSep = 0); + void ResetLocale(); + + void EnableOptimizer(bool a_bIsOn=true); + void EnableBuiltInOprt(bool a_bIsOn=true); + + bool HasBuiltInOprt() const; + void AddValIdent(identfun_type a_pCallback); + + /** \fn void mu::ParserBase::DefineFun(const string_type &a_strName, fun_type0 a_pFun, bool a_bAllowOpt = true) + \brief Define a parser function without arguments. + \param a_strName Name of the function + \param a_pFun Pointer to the callback function + \param a_bAllowOpt A flag indicating this function may be optimized + */ + template + void DefineFun(const string_type &a_strName, T a_pFun, bool a_bAllowOpt = true) + { + AddCallback( a_strName, ParserCallback(a_pFun, a_bAllowOpt), m_FunDef, ValidNameChars() ); + } + + void DefineOprt(const string_type &a_strName, + fun_type2 a_pFun, + unsigned a_iPri=0, + EOprtAssociativity a_eAssociativity = oaLEFT, + bool a_bAllowOpt = false); + void DefineConst(const string_type &a_sName, value_type a_fVal); + void DefineStrConst(const string_type &a_sName, const string_type &a_strVal); + void DefineVar(const string_type &a_sName, value_type *a_fVar); + void DefinePostfixOprt(const string_type &a_strFun, fun_type1 a_pOprt, bool a_bAllowOpt=true); + void DefineInfixOprt(const string_type &a_strName, fun_type1 a_pOprt, int a_iPrec=prINFIX, bool a_bAllowOpt=true); + + // Clear user defined variables, constants or functions + void ClearVar(); + void ClearFun(); + void ClearConst(); + void ClearInfixOprt(); + void ClearPostfixOprt(); + void ClearOprt(); + + void RemoveVar(const string_type &a_strVarName); + const varmap_type& GetUsedVar() const; + const varmap_type& GetVar() const; + const valmap_type& GetConst() const; + const string_type& GetExpr() const; + const funmap_type& GetFunDef() const; + string_type GetVersion(EParserVersionInfo eInfo = pviFULL) const; + + const char_type ** GetOprtDef() const; + void DefineNameChars(const char_type *a_szCharset); + void DefineOprtChars(const char_type *a_szCharset); + void DefineInfixOprtChars(const char_type *a_szCharset); + + const char_type* ValidNameChars() const; + const char_type* ValidOprtChars() const; + const char_type* ValidInfixOprtChars() const; + + void SetArgSep(char_type cArgSep); + char_type GetArgSep() const; + + void Error(EErrorCodes a_iErrc, + int a_iPos = (int)mu::string_type::npos, + const string_type &a_strTok = string_type() ) const; + + protected: + + void Init(); + + virtual void InitCharSets() = 0; + virtual void InitFun() = 0; + virtual void InitConst() = 0; + virtual void InitOprt() = 0; + + virtual void OnDetectVar(string_type *pExpr, int &nStart, int &nEnd); + + static const char_type *c_DefaultOprt[]; + static std::locale s_locale; ///< The locale used by the parser + static bool g_DbgDumpCmdCode; + static bool g_DbgDumpStack; + + /** \brief A facet class used to change decimal and thousands separator. */ + template + class change_dec_sep : public std::numpunct + { + public: + + explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3) + :std::numpunct() + ,m_cDecPoint(cDecSep) + ,m_cThousandsSep(cThousandsSep) + ,m_nGroup(nGroup) + {} + + protected: + + virtual char_type do_decimal_point() const + { + return m_cDecPoint; + } + + virtual char_type do_thousands_sep() const + { + return m_cThousandsSep; + } + + virtual std::string do_grouping() const + { + return std::string(1, m_nGroup); + } + + private: + + int m_nGroup; + char_type m_cDecPoint; + char_type m_cThousandsSep; + }; + + private: + + static value_type Pow(value_type v1, value_type v2); + + void Assign(const ParserBase &a_Parser); + void InitTokenReader(); + void ReInit() const; + + void AddCallback( const string_type &a_strName, + const ParserCallback &a_Callback, + funmap_type &a_Storage, + const char_type *a_szCharSet ); + + void ApplyRemainingOprt(ParserStack &a_stOpt, + ParserStack &a_stVal) const; + void ApplyBinOprt(ParserStack &a_stOpt, + ParserStack &a_stVal) const; + + void ApplyIfElse(ParserStack &a_stOpt, + ParserStack &a_stVal) const; + + void ApplyFunc(ParserStack &a_stOpt, + ParserStack &a_stVal, + int iArgCount) const; + + token_type ApplyNumFunc(const token_type &a_FunTok, + const std::vector &a_vArg) const; + + token_type ApplyBulkFunc(const token_type &a_FunTok, + const std::vector &a_vArg) const; + + token_type ApplyStrFunc(const token_type &a_FunTok, + const std::vector &a_vArg) const; + + int GetOprtPrecedence(const token_type &a_Tok) const; + EOprtAssociativity GetOprtAssociativity(const token_type &a_Tok) const; + + void CreateRPN() const; + + value_type ParseString() const; + value_type ParseCmdCode() const; + value_type ParseCmdCodeBulk(int nOffset, int nThreadID) const; + + void CheckName(const string_type &a_strName, const string_type &a_CharSet) const; + void CheckOprt(const string_type &a_sName, + const ParserCallback &a_Callback, + const string_type &a_szCharSet) const; + + void StackDump(const ParserStack &a_stVal, + const ParserStack &a_stOprt) const; + + /** \brief Pointer to the parser function. + + Eval() calls the function whose address is stored there. + */ + mutable ParseFunction m_pParseFormula; + mutable const SToken *m_pRPN; + mutable ParserByteCode m_vRPN; ///< The Bytecode class. + mutable stringbuf_type m_vStringBuf; ///< String buffer, used for storing string function arguments + stringbuf_type m_vStringVarBuf; + + std::auto_ptr m_pTokenReader; ///< Managed pointer to the token reader object. + + funmap_type m_FunDef; ///< Map of function names and pointers. + funmap_type m_PostOprtDef; ///< Postfix operator callbacks + funmap_type m_InfixOprtDef; ///< unary infix operator. + funmap_type m_OprtDef; ///< Binary operator callbacks + valmap_type m_ConstDef; ///< user constants. + strmap_type m_StrVarDef; ///< user defined string constants + varmap_type m_VarDef; ///< user defind variables. + + bool m_bOptimize; ///< Flag that indicates if the optimizer is on or off. + bool m_bBuiltInOp; ///< Flag that can be used for switching built in operators on and off + + string_type m_sNameChars; ///< Charset for names + string_type m_sOprtChars; ///< Charset for postfix/ binary operator tokens + string_type m_sInfixOprtChars; ///< Charset for infix operator tokens + + mutable int m_nIfElseCounter; ///< Internal counter for keeping track of nested if-then-else clauses + + // items merely used for caching state information + mutable valbuf_type m_vStackBuffer; ///< This is merely a buffer used for the stack in the cmd parsing routine + mutable int m_nFinalResultIdx; +}; + +} // namespace mu + +#endif + diff --git a/libraries/amf/amftools-code/include/muparser/muParserBytecode.h b/libraries/amf/amftools-code/include/muparser/muParserBytecode.h new file mode 100644 index 00000000..c5ea97fa --- /dev/null +++ b/libraries/amf/amftools-code/include/muparser/muParserBytecode.h @@ -0,0 +1,135 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef MU_PARSER_BYTECODE_H +#define MU_PARSER_BYTECODE_H + +#include +#include +#include +#include + +#include "muParserDef.h" +#include "muParserError.h" +#include "muParserToken.h" + +/** \file + \brief Definition of the parser bytecode class. +*/ + + +namespace mu +{ + struct SToken + { + ECmdCode Cmd; + int StackPos; + + union + { + union //SValData + { + value_type *ptr; + value_type data; + } Val; + + struct //SFunData + { + void* ptr; + int argc; + int idx; + } Fun; + + struct //SOprtData + { + value_type *ptr; + int offset; + } Oprt; + }; + }; + + + /** \brief Bytecode implementation of the Math Parser. + + The bytecode contains the formula converted to revers polish notation stored in a continious + memory area. Associated with this data are operator codes, variable pointers, constant + values and function pointers. Those are necessary in order to calculate the result. + All those data items will be casted to the underlying datatype of the bytecode. + + \author (C) 2004-2011 Ingo Berg +*/ +class ParserByteCode +{ +private: + + /** \brief Token type for internal use only. */ + typedef ParserToken token_type; + + /** \brief Token vector for storing the RPN. */ + typedef std::vector rpn_type; + + /** \brief Position in the Calculation array. */ + unsigned m_iStackPos; + + /** \brief Maximum size needed for the stack. */ + std::size_t m_iMaxStackSize; + + /** \brief The actual rpn storage. */ + rpn_type m_vRPN; + +public: + + ParserByteCode(); + ParserByteCode(const ParserByteCode &a_ByteCode); + ParserByteCode& operator=(const ParserByteCode &a_ByteCode); + void Assign(const ParserByteCode &a_ByteCode); + + void AddVar(value_type *a_pVar); + void AddVal(value_type a_fVal); + void AddOp(ECmdCode a_Oprt); + void AddIfElse(ECmdCode a_Oprt); + void AddAssignOp(value_type *a_pVar); + void AddFun(void *a_pFun, int a_iArgc); + void AddBulkFun(void *a_pFun, int a_iArgc); + void AddStrFun(void *a_pFun, int a_iArgc, int a_iIdx); + + void Finalize(); + void clear(); + std::size_t GetMaxStackSize() const; + + const SToken* GetBase() const; + //rpn_type Clone() const + //{ + // return rpn_type(m_vRPN); + //} + + void RemoveValEntries(unsigned a_iNumber); + void AsciiDump(); +}; + +} // namespace mu + +#endif + + diff --git a/libraries/amf/amftools-code/include/muparser/muParserCallback.h b/libraries/amf/amftools-code/include/muparser/muParserCallback.h new file mode 100644 index 00000000..ef32b498 --- /dev/null +++ b/libraries/amf/amftools-code/include/muparser/muParserCallback.h @@ -0,0 +1,118 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_CALLBACK_H +#define MU_PARSER_CALLBACK_H + +#include "muParserDef.h" + +/** \file + \brief Definition of the parser callback class. +*/ + +namespace mu +{ + +/** \brief Encapsulation of prototypes for a numerical parser function. + + Encapsulates the prototyp for numerical parser functions. The class + stores the number of arguments for parser functions as well + as additional flags indication the function is non optimizeable. + The pointer to the callback function pointer is stored as void* + and needs to be casted according to the argument count. + Negative argument counts indicate a parser function with a variable number + of arguments. + + \author (C) 2004-2011 Ingo Berg +*/ +class ParserCallback +{ +public: + ParserCallback(fun_type0 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec = -1, ECmdCode a_iCode=cmFUNC); + ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti, int a_iPrec, EOprtAssociativity a_eAssociativity); + ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type3 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type4 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type5 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type6 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type7 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type8 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type9 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type10 a_pFun, bool a_bAllowOpti); + + ParserCallback(bulkfun_type0 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type1 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type2 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type3 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type4 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type5 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type6 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type7 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type8 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type9 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type10 a_pFun, bool a_bAllowOpti); + + ParserCallback(multfun_type a_pFun, bool a_bAllowOpti); + ParserCallback(strfun_type1 a_pFun, bool a_bAllowOpti); + ParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti); + ParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti); + ParserCallback(); + ParserCallback(const ParserCallback &a_Fun); + + ParserCallback* Clone() const; + + bool IsOptimizable() const; + void* GetAddr() const; + ECmdCode GetCode() const; + ETypeCode GetType() const; + int GetPri() const; + EOprtAssociativity GetAssociativity() const; + int GetArgc() const; + +private: + void *m_pFun; ///< Pointer to the callback function, casted to void + + /** \brief Number of numeric function arguments + + This number is negative for functions with variable number of arguments. in this cases + they represent the actual number of arguments found. + */ + int m_iArgc; + int m_iPri; ///< Valid only for binary and infix operators; Operator precedence. + EOprtAssociativity m_eOprtAsct; ///< Operator associativity; Valid only for binary operators + ECmdCode m_iCode; + ETypeCode m_iType; + bool m_bAllowOpti; ///< Flag indication optimizeability +}; + +//------------------------------------------------------------------------------ +/** \brief Container for Callback objects. */ +typedef std::map funmap_type; + +} // namespace mu + +#endif + diff --git a/libraries/amf/amftools-code/include/muparser/muParserDef.h b/libraries/amf/amftools-code/include/muparser/muParserDef.h new file mode 100644 index 00000000..b2cfbaec --- /dev/null +++ b/libraries/amf/amftools-code/include/muparser/muParserDef.h @@ -0,0 +1,356 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef MUP_DEF_H +#define MUP_DEF_H + +#include +#include +#include +#include + +#include "muParserFixes.h" + +/** \file + \brief This file contains standard definitions used by the parser. +*/ + +#define MUP_VERSION _T("2.0.0") +#define MUP_VERSION_DATE _T("20110803; SF-SVN") + +#define MUP_CHARS _T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +/** \brief If this macro is defined mathematical exceptions (div by zero) will be thrown as exceptions. */ +//#define MUP_MATH_EXCEPTIONS + +/** \brief Define the base datatype for values. + + This datatype must be a built in value type. You can not use custom classes. + It has been tested with float, double and long double types, int should + work as well. +*/ +#define MUP_BASETYPE double + +/** \brief Activate this option in order to compile with OpenMP support. + + OpenMP is used only in the bulk mode it may increase the performance a bit. +*/ +//#define MUP_USE_OPENMP + +#if defined(_UNICODE) + /** \brief Definition of the basic parser string type. */ + #define MUP_STRING_TYPE std::wstring + + #if !defined(_T) + #define _T(x) L##x + #endif // not defined _T +#else + #ifndef _T + #define _T(x) x + #endif + + /** \brief Definition of the basic parser string type. */ + #define MUP_STRING_TYPE std::string +#endif + +#if defined(_DEBUG) + /** \brief Debug macro to force an abortion of the programm with a certain message. + */ + #define MUP_FAIL(MSG) \ + bool MSG=false; \ + assert(MSG); + + /** \brief An assertion that does not kill the program. + + This macro is neutralised in UNICODE builds. It's + too difficult to translate. + */ + #define MUP_ASSERT(COND) \ + if (!(COND)) \ + { \ + stringstream_type ss; \ + ss << _T("Assertion \"") _T(#COND) _T("\" failed: ") \ + << __FILE__ << _T(" line ") \ + << __LINE__ << _T("."); \ + throw ParserError( ss.str() ); \ + } +#else + #define MUP_FAIL(MSG) + #define MUP_ASSERT(COND) +#endif + + +namespace mu +{ +#if defined(_UNICODE) + + //------------------------------------------------------------------------------ + /** \brief Encapsulate wcout. */ + inline std::wostream& console() + { + return std::wcout; + } + + /** \brief Encapsulate cin. */ + inline std::wistream& console_in() + { + return std::wcin; + } + +#else + + /** \brief Encapsulate cout. + + Used for supporting UNICODE more easily. + */ + inline std::ostream& console() + { + return std::cout; + } + + /** \brief Encapsulate cin. + + Used for supporting UNICODE more easily. + */ + inline std::istream& console_in() + { + return std::cin; + } + +#endif + + //------------------------------------------------------------------------------ + /** \brief Bytecode values. + + \attention The order of the operator entries must match the order in ParserBase::c_DefaultOprt! + */ + enum ECmdCode + { + // The following are codes for built in binary operators + // apart from built in operators the user has the opportunity to + // add user defined operators. + cmLE = 0, ///< Operator item: less or equal + cmGE = 1, ///< Operator item: greater or equal + cmNEQ = 2, ///< Operator item: not equal + cmEQ = 3, ///< Operator item: equals + cmLT = 4, ///< Operator item: less than + cmGT = 5, ///< Operator item: greater than + cmADD = 6, ///< Operator item: add + cmSUB = 7, ///< Operator item: subtract + cmMUL = 8, ///< Operator item: multiply + cmDIV = 9, ///< Operator item: division + cmPOW = 10, ///< Operator item: y to the power of ... + cmLAND = 11, + cmLOR = 12, + cmASSIGN = 13, ///< Operator item: Assignment operator + cmBO = 14, ///< Operator item: opening bracket + cmBC = 15, ///< Operator item: closing bracket + cmIF = 16, ///< For use in the ternary if-then-else operator + cmELSE = 17, ///< For use in the ternary if-then-else operator + cmENDIF = 18, ///< For use in the ternary if-then-else operator + cmARG_SEP, ///< function argument separator + cmVAR, ///< variable item + cmVAL, ///< value item + cmFUNC, ///< Code for a function item + cmFUNC_STR, ///< Code for a function with a string parameter + cmFUNC_BULK, ///< Special callbacks for Bulk mode with an additional parameter for the bulk index + cmSTRING, ///< Code for a string token + cmOPRT_BIN, ///< user defined binary operator + cmOPRT_POSTFIX, ///< code for postfix operators + cmOPRT_INFIX, ///< code for infix operators + cmEND, ///< end of formula + cmUNKNOWN ///< uninitialized item + }; + + //------------------------------------------------------------------------------ + /** \brief Types internally used by the parser. + */ + enum ETypeCode + { + tpSTR = 0, ///< String type (Function arguments and constants only, no string variables) + tpDBL = 1, ///< Floating point variables + tpVOID = 2 ///< Undefined type. + }; + + //------------------------------------------------------------------------------ + enum EParserVersionInfo + { + pviBRIEF, + pviFULL + }; + + //------------------------------------------------------------------------------ + /** \brief Parser operator precedence values. */ + enum EOprtAssociativity + { + oaLEFT = 0, + oaRIGHT = 1, + oaNONE = 2 + }; + + //------------------------------------------------------------------------------ + /** \brief Parser operator precedence values. */ + enum EOprtPrecedence + { + // binary operators + prLOR = 1, + prLAND = 2, + prLOGIC = 3, ///< logic operators + prCMP = 4, ///< comparsion operators + prADD_SUB = 5, ///< addition + prMUL_DIV = 6, ///< multiplication/division + prPOW = 7, ///< power operator priority (highest) + + // infix operators + prINFIX = 6, ///< Signs have a higher priority than ADD_SUB, but lower than power operator + prPOSTFIX = 6 ///< Postfix operator priority (currently unused) + }; + + //------------------------------------------------------------------------------ + // basic types + + /** \brief The numeric datatype used by the parser. + + Normally this is a floating point type either single or double precision. + */ + typedef MUP_BASETYPE value_type; + + /** \brief The stringtype used by the parser. + + Depends on wether UNICODE is used or not. + */ + typedef MUP_STRING_TYPE string_type; + + /** \brief The character type used by the parser. + + Depends on wether UNICODE is used or not. + */ + typedef string_type::value_type char_type; + + /** \brief Typedef for easily using stringstream that respect the parser stringtype. */ + typedef std::basic_stringstream, + std::allocator > stringstream_type; + + // Data container types + + /** \brief Type used for storing variables. */ + typedef std::map varmap_type; + + /** \brief Type used for storing constants. */ + typedef std::map valmap_type; + + /** \brief Type for assigning a string name to an index in the internal string table. */ + typedef std::map strmap_type; + + // Parser callbacks + + /** \brief Callback type used for functions without arguments. */ + typedef value_type (*fun_type0)(); + + /** \brief Callback type used for functions with a single arguments. */ + typedef value_type (*fun_type1)(value_type); + + /** \brief Callback type used for functions with two arguments. */ + typedef value_type (*fun_type2)(value_type, value_type); + + /** \brief Callback type used for functions with three arguments. */ + typedef value_type (*fun_type3)(value_type, value_type, value_type); + + /** \brief Callback type used for functions with four arguments. */ + typedef value_type (*fun_type4)(value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type (*fun_type5)(value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type (*fun_type6)(value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type (*fun_type7)(value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type (*fun_type8)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type (*fun_type9)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type (*fun_type10)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions without arguments. */ + typedef value_type (*bulkfun_type0)(int, int); + + /** \brief Callback type used for functions with a single arguments. */ + typedef value_type (*bulkfun_type1)(int, int, value_type); + + /** \brief Callback type used for functions with two arguments. */ + typedef value_type (*bulkfun_type2)(int, int, value_type, value_type); + + /** \brief Callback type used for functions with three arguments. */ + typedef value_type (*bulkfun_type3)(int, int, value_type, value_type, value_type); + + /** \brief Callback type used for functions with four arguments. */ + typedef value_type (*bulkfun_type4)(int, int, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type (*bulkfun_type5)(int, int, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type (*bulkfun_type6)(int, int, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type (*bulkfun_type7)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type (*bulkfun_type8)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type (*bulkfun_type9)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type (*bulkfun_type10)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with a variable argument list. */ + typedef value_type (*multfun_type)(const value_type*, int); + + /** \brief Callback type used for functions taking a string as an argument. */ + typedef value_type (*strfun_type1)(const char_type*); + + /** \brief Callback type used for functions taking a string and a value as arguments. */ + typedef value_type (*strfun_type2)(const char_type*, value_type); + + /** \brief Callback type used for functions taking a string and two values as arguments. */ + typedef value_type (*strfun_type3)(const char_type*, value_type, value_type); + + /** \brief Callback used for functions that identify values in a string. */ + typedef int (*identfun_type)(const char_type *sExpr, int *nPos, value_type *fVal); + + /** \brief Callback used for variable creation factory functions. */ + typedef value_type* (*facfun_type)(const char_type*, void*); + +} // end fo namespace + +#endif + diff --git a/libraries/amf/amftools-code/include/muparser/muParserError.h b/libraries/amf/amftools-code/include/muparser/muParserError.h new file mode 100644 index 00000000..bb4093a8 --- /dev/null +++ b/libraries/amf/amftools-code/include/muparser/muParserError.h @@ -0,0 +1,174 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_ERROR_H +#define MU_PARSER_ERROR_H + +#include +#include +#include +#include +#include +#include + +#include "muParserDef.h" + +/** \file + \brief This file defines the error class used by the parser. +*/ + +namespace mu +{ + +/** \brief Error codes. */ +enum EErrorCodes +{ + // Formula syntax errors + ecUNEXPECTED_OPERATOR = 0, ///< Unexpected binary operator found + ecUNASSIGNABLE_TOKEN = 1, ///< Token cant be identified. + ecUNEXPECTED_EOF = 2, ///< Unexpected end of formula. (Example: "2+sin(") + ecUNEXPECTED_ARG_SEP = 3, ///< An unexpected comma has been found. (Example: "1,23") + ecUNEXPECTED_ARG = 4, ///< An unexpected argument has been found + ecUNEXPECTED_VAL = 5, ///< An unexpected value token has been found + ecUNEXPECTED_VAR = 6, ///< An unexpected variable token has been found + ecUNEXPECTED_PARENS = 7, ///< Unexpected Parenthesis, opening or closing + ecUNEXPECTED_STR = 8, ///< A string has been found at an inapropriate position + ecSTRING_EXPECTED = 9, ///< A string function has been called with a different type of argument + ecVAL_EXPECTED = 10, ///< A numerical function has been called with a non value type of argument + ecMISSING_PARENS = 11, ///< Missing parens. (Example: "3*sin(3") + ecUNEXPECTED_FUN = 12, ///< Unexpected function found. (Example: "sin(8)cos(9)") + ecUNTERMINATED_STRING = 13, ///< unterminated string constant. (Example: "3*valueof("hello)") + ecTOO_MANY_PARAMS = 14, ///< Too many function parameters + ecTOO_FEW_PARAMS = 15, ///< Too few function parameters. (Example: "ite(1<2,2)") + ecOPRT_TYPE_CONFLICT = 16, ///< binary operators may only be applied to value items of the same type + ecSTR_RESULT = 17, ///< result is a string + + // Invalid Parser input Parameters + ecINVALID_NAME = 18, ///< Invalid function, variable or constant name. + ecINVALID_BINOP_IDENT = 19, ///< Invalid binary operator identifier + ecINVALID_INFIX_IDENT = 20, ///< Invalid function, variable or constant name. + ecINVALID_POSTFIX_IDENT = 21, ///< Invalid function, variable or constant name. + + ecBUILTIN_OVERLOAD = 22, ///< Trying to overload builtin operator + ecINVALID_FUN_PTR = 23, ///< Invalid callback function pointer + ecINVALID_VAR_PTR = 24, ///< Invalid variable pointer + ecEMPTY_EXPRESSION = 25, ///< The Expression is empty + ecNAME_CONFLICT = 26, ///< Name conflict + ecOPT_PRI = 27, ///< Invalid operator priority + // + ecDOMAIN_ERROR = 28, ///< catch division by zero, sqrt(-1), log(0) (currently unused) + ecDIV_BY_ZERO = 29, ///< Division by zero (currently unused) + ecGENERIC = 30, ///< Generic error + ecLOCALE = 31, ///< Conflict with current locale + + ecUNEXPECTED_CONDITIONAL = 32, + ecMISSING_ELSE_CLAUSE = 33, + ecMISPLACED_COLON = 34, + + // internal errors + ecINTERNAL_ERROR = 35, ///< Internal error of any kind. + + // The last two are special entries + ecCOUNT, ///< This is no error code, It just stores just the total number of error codes + ecUNDEFINED = -1 ///< Undefined message, placeholder to detect unassigned error messages +}; + +//--------------------------------------------------------------------------- +/** \brief A class that handles the error messages. +*/ +class ParserErrorMsg +{ +public: + typedef ParserErrorMsg self_type; + + ParserErrorMsg& operator=(const ParserErrorMsg &); + ParserErrorMsg(const ParserErrorMsg&); + ParserErrorMsg(); + + ~ParserErrorMsg(); + + static const ParserErrorMsg& Instance(); + string_type operator[](unsigned a_iIdx) const; + +private: + std::vector m_vErrMsg; ///< A vector with the predefined error messages + static const self_type m_Instance; ///< The instance pointer +}; + +//--------------------------------------------------------------------------- +/** \brief Error class of the parser. + \author Ingo Berg + + Part of the math parser package. +*/ +class ParserError +{ +private: + + /** \brief Replace all ocuurences of a substring with another string. */ + void ReplaceSubString( string_type &strSource, + const string_type &strFind, + const string_type &strReplaceWith); + void Reset(); + +public: + + ParserError(); + explicit ParserError(EErrorCodes a_iErrc); + explicit ParserError(const string_type &sMsg); + ParserError( EErrorCodes a_iErrc, + const string_type &sTok, + const string_type &sFormula = string_type(_T("(formula is not available)")), + int a_iPos = -1); + ParserError( EErrorCodes a_iErrc, + int a_iPos, + const string_type &sTok); + ParserError( const char_type *a_szMsg, + int a_iPos = -1, + const string_type &sTok = string_type()); + ParserError(const ParserError &a_Obj); + ParserError& operator=(const ParserError &a_Obj); + ~ParserError(); + + void SetFormula(const string_type &a_strFormula); + const string_type& GetExpr() const; + const string_type& GetMsg() const; + std::size_t GetPos() const; + const string_type& GetToken() const; + EErrorCodes GetCode() const; + +private: + string_type m_strMsg; ///< The message string + string_type m_strFormula; ///< Formula string + string_type m_strTok; ///< Token related with the error + int m_iPos; ///< Formula position related to the error + EErrorCodes m_iErrc; ///< Error code + const ParserErrorMsg &m_ErrMsg; +}; + +} // namespace mu + +#endif + diff --git a/libraries/amf/amftools-code/include/muparser/muParserFixes.h b/libraries/amf/amftools-code/include/muparser/muParserFixes.h new file mode 100644 index 00000000..572bae48 --- /dev/null +++ b/libraries/amf/amftools-code/include/muparser/muParserFixes.h @@ -0,0 +1,200 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_FIXES_H +#define MU_PARSER_FIXES_H + +/** \file + \brief This file contains compatibility fixes for some platforms. +*/ + +// +// Compatibility fixes +// + +//--------------------------------------------------------------------------- +// +// Intel Compiler +// +//--------------------------------------------------------------------------- + +#ifdef __INTEL_COMPILER + +// remark #981: operands are evaluated in unspecified order +// disabled -> completely pointless if the functions do not have side effects +// +#pragma warning(disable:981) + +// remark #383: value copied to temporary, reference to temporary used +#pragma warning(disable:383) + +// remark #1572: floating-point equality and inequality comparisons are unreliable +// disabled -> everyone knows it, the parser passes this problem +// deliberately to the user +#pragma warning(disable:1572) + +#endif + + +//--------------------------------------------------------------------------- +// +// MSVC6 +// +//--------------------------------------------------------------------------- + + +#if defined(_MSC_VER) && _MSC_VER==1200 + +/** \brief Macro to replace the MSVC6 auto_ptr with the _my_auto_ptr class. + + Hijack auto_ptr and replace it with a version that actually does + what an auto_ptr normally does. If you use std::auto_ptr in your other code + might either explode or work much better. The original crap created + by Microsoft, called auto_ptr and bundled with MSVC6 is not standard compliant. +*/ +#define auto_ptr _my_auto_ptr + +// This is another stupidity that needs to be undone in order to de-pollute +// the global namespace! +#undef min +#undef max + + +namespace std +{ + typedef ::size_t size_t; + + //--------------------------------------------------------------------------- + /** \brief MSVC6 fix: Dummy function to put rand into namespace std. + + This is a hack for MSVC6 only. It's dirty, it's ugly and it works, provided + inlining is enabled. Necessary because I will not pollute or change my + code in order to adopt it to MSVC6 interpretation of how C++ should look like! + */ + inline int rand(void) + { + return ::rand(); + } + + //--------------------------------------------------------------------------- + /** \brief MSVC6 fix: Dummy function to put strlen into namespace std. + + This is a hack for MSVC6 only. It's dirty, it's ugly and it works, provided + inlining is enabled. Necessary because I will not pollute or change my + code in order to adopt it to MSVC6 interpretation of how C++ should look like! + */ + inline size_t strlen(const char *szMsg) + { + return ::strlen(szMsg); + } + + //--------------------------------------------------------------------------- + /** \brief MSVC6 fix: Dummy function to put strncmp into namespace std. + + This is a hack for MSVC6 only. It's dirty, it's ugly and it works, provided + inlining is enabled. Necessary because I will not pollute or change my + code in order to adopt it to MSVC6 interpretation of how C++ should look like! + */ + inline int strncmp(const char *a, const char *b, size_t len) + { + return ::strncmp(a,b,len); + } + + //--------------------------------------------------------------------------- + template + T max(T a, T b) + { + return (a>b) ? a : b; + } + + //--------------------------------------------------------------------------- + template + T min(T a, T b) + { + return (a + class _my_auto_ptr + { + public: + typedef _Ty element_type; + + explicit _my_auto_ptr(_Ty *_Ptr = 0) + :_Myptr(_Ptr) + {} + + _my_auto_ptr(_my_auto_ptr<_Ty>& _Right) + :_Myptr(_Right.release()) + {} + + template + operator _my_auto_ptr<_Other>() + { + return (_my_auto_ptr<_Other>(*this)); + } + + template + _my_auto_ptr<_Ty>& operator=(_my_auto_ptr<_Other>& _Right) + { + reset(_Right.release()); + return (*this); + } + + ~auto_ptr() { delete _Myptr; } + _Ty& operator*() const { return (*_Myptr); } + _Ty *operator->() const { return (&**this); } + _Ty *get() const { return (_Myptr); } + + _Ty *release() + { + _Ty *_Tmp = _Myptr; + _Myptr = 0; + return (_Tmp); + } + + void reset(_Ty* _Ptr = 0) + { + if (_Ptr != _Myptr) + delete _Myptr; + _Myptr = _Ptr; + } + + private: + _Ty *_Myptr; + }; // class _my_auto_ptr +} // namespace std + +#endif // Microsoft Visual Studio Version 6.0 + +#endif // include guard + + diff --git a/libraries/amf/amftools-code/include/muparser/muParserStack.h b/libraries/amf/amftools-code/include/muparser/muParserStack.h new file mode 100644 index 00000000..c97e0929 --- /dev/null +++ b/libraries/amf/amftools-code/include/muparser/muParserStack.h @@ -0,0 +1,125 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_STACK_H +#define MU_PARSER_STACK_H + +#include +#include +#include +#include + +#include "muParserError.h" +#include "muParserToken.h" + +/** \file + \brief This file defines the stack used by muparser. +*/ + +namespace mu +{ + + /** \brief Parser stack implementation. + + Stack implementation based on a std::stack. The behaviour of pop() had been + slightly changed in order to get an error code if the stack is empty. + The stack is used within the Parser both as a value stack and as an operator stack. + + \author (C) 2004-2011 Ingo Berg + */ + template + class ParserStack + { + private: + + /** \brief Type of the underlying stack implementation. */ + typedef std::stack > impl_type; + + impl_type m_Stack; ///< This is the actual stack. + + public: + + //--------------------------------------------------------------------------- + ParserStack() + :m_Stack() + {} + + //--------------------------------------------------------------------------- + virtual ~ParserStack() + {} + + //--------------------------------------------------------------------------- + /** \brief Pop a value from the stack. + + Unlike the standard implementation this function will return the value that + is going to be taken from the stack. + + \throw ParserException in case the stack is empty. + \sa pop(int &a_iErrc) + */ + TValueType pop() + { + if (empty()) + throw ParserError( _T("stack is empty.") ); + + TValueType el = top(); + m_Stack.pop(); + return el; + } + + /** \brief Push an object into the stack. + + \param a_Val object to push into the stack. + \throw nothrow + */ + void push(const TValueType& a_Val) + { + m_Stack.push(a_Val); + } + + /** \brief Return the number of stored elements. */ + unsigned size() const + { + return (unsigned)m_Stack.size(); + } + + /** \brief Returns true if stack is empty false otherwise. */ + bool empty() const + { + return m_Stack.size()==0; + } + + /** \brief Return reference to the top object in the stack. + + The top object is the one pushed most recently. + */ + TValueType& top() + { + return m_Stack.top(); + } + }; +} // namespace MathUtils + +#endif diff --git a/libraries/amf/amftools-code/include/muparser/muParserToken.h b/libraries/amf/amftools-code/include/muparser/muParserToken.h new file mode 100644 index 00000000..bf807b2d --- /dev/null +++ b/libraries/amf/amftools-code/include/muparser/muParserToken.h @@ -0,0 +1,451 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_TOKEN_H +#define MU_PARSER_TOKEN_H + +#include +#include +#include +#include +#include + +#include "muParserError.h" +#include "muParserCallback.h" + +/** \file + \brief This file contains the parser token definition. +*/ + +namespace mu +{ + /** \brief Encapsulation of the data for a single formula token. + + Formula token implementation. Part of the Math Parser Package. + Formula tokens can be either one of the following: +
    +
  • value
  • +
  • variable
  • +
  • function with numerical arguments
  • +
  • functions with a string as argument
  • +
  • prefix operators
  • +
  • infix operators
  • +
  • binary operator
  • +
+ + \author (C) 2004-2011 Ingo Berg + */ + template + class ParserToken + { + public: + + /** \brief Additional token flags. */ + enum ETokFlags + { + flVOLATILE = 1 ///< Mark a token that depends on a variable or a function that is not conservative + }; + + private: + + ECmdCode m_iCode; ///< Type of the token; The token type is a constant of type #ECmdCode. + ETypeCode m_iType; + void *m_pTok; ///< Stores Token pointer; not applicable for all tokens + int m_iFlags; ///< Additional flags for the token. + int m_iIdx; ///< An otional index to an external buffer storing the token data + TString m_strTok; ///< Token string + TString m_strVal; ///< Value for string variables + value_type m_fVal; ///< the value + std::auto_ptr m_pCallback; + + public: + + //--------------------------------------------------------------------------- + /** \brief Constructor (default). + + Sets token to an neutral state of type cmUNKNOWN. + \throw nothrow + \sa ECmdCode + */ + ParserToken() + :m_iCode(cmUNKNOWN) + ,m_iType(tpVOID) + ,m_pTok(0) + ,m_iFlags(0) + ,m_iIdx(-1) + ,m_strTok() + ,m_pCallback() + {} + + //------------------------------------------------------------------------------ + /** \brief Create token from another one. + + Implemented by calling Assign(...) + \throw nothrow + \post m_iType==cmUNKNOWN + \sa #Assign + */ + ParserToken(const ParserToken &a_Tok) + { + Assign(a_Tok); + } + + //------------------------------------------------------------------------------ + /** \brief Assignement operator. + + Copy token state from another token and return this. + Implemented by calling Assign(...). + \throw nothrow + */ + ParserToken& operator=(const ParserToken &a_Tok) + { + Assign(a_Tok); + return *this; + } + + //------------------------------------------------------------------------------ + /** \brief Copy token information from argument. + + \throw nothrow + */ + void Assign(const ParserToken &a_Tok) + { + m_iCode = a_Tok.m_iCode; + m_pTok = a_Tok.m_pTok; + m_iFlags = a_Tok.m_iFlags; + m_strTok = a_Tok.m_strTok; + m_iIdx = a_Tok.m_iIdx; + m_strVal = a_Tok.m_strVal; + m_iType = a_Tok.m_iType; + m_fVal = a_Tok.m_fVal; + // create new callback object if a_Tok has one + m_pCallback.reset(a_Tok.m_pCallback.get() ? a_Tok.m_pCallback->Clone() : 0); + } + + //------------------------------------------------------------------------------ + /** \brief Add additional flags to the token. + + Flags are currently used to mark volatile (non optimizeable) functions. + \sa m_iFlags, ETokFlags + */ + void AddFlags(int a_iFlags) + { + m_iFlags |= a_iFlags; + } + + //------------------------------------------------------------------------------ + /** \brief Check if a certain flag ist set. + + \throw nothrow + */ + bool IsFlagSet(int a_iFlags) const + { + #if defined(_MSC_VER) + #pragma warning( disable : 4800 ) + #endif + + return (bool)(m_iFlags & a_iFlags); + + #if defined(_MSC_VER) + #pragma warning( default : 4800 ) // int: Variable set to boolean value (may degrade performance) + #endif + } + + //------------------------------------------------------------------------------ + /** \brief Assign a token type. + + Token may not be of type value, variable or function. Those have seperate set functions. + + \pre [assert] a_iType!=cmVAR + \pre [assert] a_iType!=cmVAL + \pre [assert] a_iType!=cmFUNC + \post m_fVal = 0 + \post m_pTok = 0 + */ + ParserToken& Set(ECmdCode a_iType, const TString &a_strTok=TString()) + { + // The following types cant be set this way, they have special Set functions + assert(a_iType!=cmVAR); + assert(a_iType!=cmVAL); + assert(a_iType!=cmFUNC); + + m_iCode = a_iType; + m_iType = tpVOID; + m_pTok = 0; + m_iFlags = 0; + m_strTok = a_strTok; + m_iIdx = -1; + + return *this; + } + + //------------------------------------------------------------------------------ + /** \brief Set Callback type. */ + ParserToken& Set(const ParserCallback &a_pCallback, const TString &a_sTok) + { + assert(a_pCallback.GetAddr()); + + m_iCode = a_pCallback.GetCode(); + m_iType = tpVOID; + m_strTok = a_sTok; + m_pCallback.reset(new ParserCallback(a_pCallback)); + + m_pTok = 0; + m_iFlags = 0; + m_iIdx = -1; + + if (!m_pCallback->IsOptimizable()) + AddFlags(flVOLATILE); + + return *this; + } + + //------------------------------------------------------------------------------ + /** \brief Make this token a value token. + + Member variables not necessary for value tokens will be invalidated. + \throw nothrow + */ + ParserToken& SetVal(TBase a_fVal, const TString &a_strTok=TString()) + { + m_iCode = cmVAL; + m_iType = tpDBL; + m_fVal = a_fVal; + m_iFlags = 0; + m_strTok = a_strTok; + m_iIdx = -1; + + m_pTok = 0; + m_pCallback.reset(0); + + return *this; + } + + //------------------------------------------------------------------------------ + /** \brief make this token a variable token. + + Member variables not necessary for variable tokens will be invalidated. + \throw nothrow + */ + ParserToken& SetVar(TBase *a_pVar, const TString &a_strTok) + { + m_iCode = cmVAR; + m_iType = tpDBL; + m_iFlags = 0; + m_strTok = a_strTok; + m_iIdx = -1; + m_pTok = (void*)a_pVar; + m_pCallback.reset(0); + + AddFlags(ParserToken::flVOLATILE); + return *this; + } + + //------------------------------------------------------------------------------ + /** \brief Make this token a variable token. + + Member variables not necessary for variable tokens will be invalidated. + \throw nothrow + */ + ParserToken& SetString(const TString &a_strTok, std::size_t a_iSize) + { + m_iCode = cmSTRING; + m_iType = tpSTR; + m_iFlags = 0; + m_strTok = a_strTok; + m_iIdx = static_cast(a_iSize); + + m_pTok = 0; + m_pCallback.reset(0); + + AddFlags(ParserToken::flVOLATILE); + return *this; + } + + //------------------------------------------------------------------------------ + /** \brief Set an index associated with the token related data. + + In cmSTRFUNC - This is the index to a string table in the main parser. + \param a_iIdx The index the string function result will take in the bytecode parser. + \throw exception_type if #a_iIdx<0 or #m_iType!=cmSTRING + */ + void SetIdx(int a_iIdx) + { + if (m_iCode!=cmSTRING || a_iIdx<0) + throw ParserError(ecINTERNAL_ERROR); + + m_iIdx = a_iIdx; + } + + //------------------------------------------------------------------------------ + /** \brief Return Index associated with the token related data. + + In cmSTRFUNC - This is the index to a string table in the main parser. + + \throw exception_type if #m_iIdx<0 or #m_iType!=cmSTRING + \return The index the result will take in the Bytecode calculatin array (#m_iIdx). + */ + int GetIdx() const + { + if (m_iIdx<0 || m_iCode!=cmSTRING ) + throw ParserError(ecINTERNAL_ERROR); + + return m_iIdx; + } + + //------------------------------------------------------------------------------ + /** \brief Return the token type. + + \return #m_iType + \throw nothrow + */ + ECmdCode GetCode() const + { + if (m_pCallback.get()) + { + return m_pCallback->GetCode(); + } + else + { + return m_iCode; + } + } + + //------------------------------------------------------------------------------ + ETypeCode GetType() const + { + if (m_pCallback.get()) + { + return m_pCallback->GetType(); + } + else + { + return m_iType; + } + } + + //------------------------------------------------------------------------------ + int GetPri() const + { + if ( !m_pCallback.get()) + throw ParserError(ecINTERNAL_ERROR); + + if ( m_pCallback->GetCode()!=cmOPRT_BIN && m_pCallback->GetCode()!=cmOPRT_INFIX) + throw ParserError(ecINTERNAL_ERROR); + + return m_pCallback->GetPri(); + } + + //------------------------------------------------------------------------------ + EOprtAssociativity GetAssociativity() const + { + if (m_pCallback.get()==NULL || m_pCallback->GetCode()!=cmOPRT_BIN) + throw ParserError(ecINTERNAL_ERROR); + + return m_pCallback->GetAssociativity(); + } + + //------------------------------------------------------------------------------ + /** \brief Return the address of the callback function assoziated with + function and operator tokens. + + \return The pointer stored in #m_pTok. + \throw exception_type if token type is non of: +
    +
  • cmFUNC
  • +
  • cmSTRFUNC
  • +
  • cmPOSTOP
  • +
  • cmINFIXOP
  • +
  • cmOPRT_BIN
  • +
+ \sa ECmdCode + */ + void* GetFuncAddr() const + { + return (m_pCallback.get()) ? m_pCallback->GetAddr() : 0; + } + + //------------------------------------------------------------------------------ + /** \biref Get value of the token. + + Only applicable to variable and value tokens. + \throw exception_type if token is no value/variable token. + */ + TBase GetVal() const + { + switch (m_iCode) + { + case cmVAL: return m_fVal; + case cmVAR: return *((TBase*)m_pTok); + default: throw ParserError(ecVAL_EXPECTED); + } + } + + //------------------------------------------------------------------------------ + /** \brief Get address of a variable token. + + Valid only if m_iType==CmdVar. + \throw exception_type if token is no variable token. + */ + TBase* GetVar() const + { + if (m_iCode!=cmVAR) + throw ParserError(ecINTERNAL_ERROR); + + return (TBase*)m_pTok; + } + + //------------------------------------------------------------------------------ + /** \brief Return the number of function arguments. + + Valid only if m_iType==CmdFUNC. + */ + int GetArgCount() const + { + assert(m_pCallback.get()); + + if (!m_pCallback->GetAddr()) + throw ParserError(ecINTERNAL_ERROR); + + return m_pCallback->GetArgc(); + } + + //------------------------------------------------------------------------------ + /** \brief Return the token identifier. + + If #m_iType is cmSTRING the token identifier is the value of the string argument + for a string function. + \return #m_strTok + \throw nothrow + \sa m_strTok + */ + const TString& GetAsString() const + { + return m_strTok; + } + }; +} // namespace mu + +#endif diff --git a/libraries/amf/amftools-code/include/muparser/muParserTokenReader.h b/libraries/amf/amftools-code/include/muparser/muParserTokenReader.h new file mode 100644 index 00000000..e6e15861 --- /dev/null +++ b/libraries/amf/amftools-code/include/muparser/muParserTokenReader.h @@ -0,0 +1,160 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_TOKEN_READER_H +#define MU_PARSER_TOKEN_READER_H + +#include +#include +#include +#include +#include +#include +#include + +#include "muParserDef.h" +#include "muParserToken.h" + +/** \file + \brief This file contains the parser token reader definition. +*/ + + +namespace mu +{ + // Forward declaration + class ParserBase; + + /** \brief Token reader for the ParserBase class. + + */ + class ParserTokenReader + { + private: + + typedef ParserToken token_type; + + public: + + ParserTokenReader(ParserBase *a_pParent); + ParserTokenReader* Clone(ParserBase *a_pParent) const; + + void AddValIdent(identfun_type a_pCallback); + void SetVarCreator(facfun_type a_pFactory, void *pUserData); + void SetFormula(const string_type &a_strFormula); + void SetArgSep(char_type cArgSep); + + int GetPos() const; + const string_type& GetExpr() const; + varmap_type& GetUsedVar(); + char_type GetArgSep() const; + + void IgnoreUndefVar(bool bIgnore); + void ReInit(); + token_type ReadNextToken(); + + private: + + /** \brief Syntax codes. + + The syntax codes control the syntax check done during the first time parsing of + the expression string. They are flags that indicate which tokens are allowed next + if certain tokens are identified. + */ + enum ESynCodes + { + noBO = 1 << 0, ///< to avoid i.e. "cos(7)(" + noBC = 1 << 1, ///< to avoid i.e. "sin)" or "()" + noVAL = 1 << 2, ///< to avoid i.e. "tan 2" or "sin(8)3.14" + noVAR = 1 << 3, ///< to avoid i.e. "sin a" or "sin(8)a" + noARG_SEP = 1 << 4, ///< to avoid i.e. ",," or "+," ... + noFUN = 1 << 5, ///< to avoid i.e. "sqrt cos" or "(1)sin" + noOPT = 1 << 6, ///< to avoid i.e. "(+)" + noPOSTOP = 1 << 7, ///< to avoid i.e. "(5!!)" "sin!" + noINFIXOP = 1 << 8, ///< to avoid i.e. "++4" "!!4" + noEND = 1 << 9, ///< to avoid unexpected end of formula + noSTR = 1 << 10, ///< to block numeric arguments on string functions + noASSIGN = 1 << 11, ///< to block assignement to constant i.e. "4=7" + noIF = 1 << 12, + noELSE = 1 << 13, + sfSTART_OF_LINE = noOPT | noBC | noPOSTOP | noASSIGN | noIF | noELSE | noARG_SEP, + noANY = ~0 ///< All of he above flags set + }; + + ParserTokenReader(const ParserTokenReader &a_Reader); + ParserTokenReader& operator=(const ParserTokenReader &a_Reader); + void Assign(const ParserTokenReader &a_Reader); + + void SetParent(ParserBase *a_pParent); + int ExtractToken(const char_type *a_szCharSet, + string_type &a_strTok, + int a_iPos) const; + int ExtractOperatorToken(string_type &a_sTok, int a_iPos) const; + + bool IsBuiltIn(token_type &a_Tok); + bool IsArgSep(token_type &a_Tok); + bool IsEOF(token_type &a_Tok); + bool IsInfixOpTok(token_type &a_Tok); + bool IsFunTok(token_type &a_Tok); + bool IsPostOpTok(token_type &a_Tok); + bool IsOprt(token_type &a_Tok); + bool IsValTok(token_type &a_Tok); + bool IsVarTok(token_type &a_Tok); + bool IsStrVarTok(token_type &a_Tok); + bool IsUndefVarTok(token_type &a_Tok); + bool IsString(token_type &a_Tok); + void Error(EErrorCodes a_iErrc, + int a_iPos = -1, + const string_type &a_sTok = string_type() ) const; + + token_type& SaveBeforeReturn(const token_type &tok); + + ParserBase *m_pParser; + string_type m_strFormula; + int m_iPos; + int m_iSynFlags; + bool m_bIgnoreUndefVar; + + const funmap_type *m_pFunDef; + const funmap_type *m_pPostOprtDef; + const funmap_type *m_pInfixOprtDef; + const funmap_type *m_pOprtDef; + const valmap_type *m_pConstDef; + const strmap_type *m_pStrVarDef; + varmap_type *m_pVarDef; ///< The only non const pointer to parser internals + facfun_type m_pFactory; + void *m_pFactoryData; + std::vector m_vIdentFun; ///< Value token identification function + varmap_type m_UsedVar; + value_type m_fZero; ///< Dummy value of zero, referenced by undefined variables + int m_iBrackets; + token_type m_lastTok; + char_type m_cArgSep; ///< The character used for separating function arguments + }; +} // namespace mu + +#endif + + diff --git a/libraries/amf/amftools-code/include/nAmf.h b/libraries/amf/amftools-code/include/nAmf.h new file mode 100644 index 00000000..3c2257da --- /dev/null +++ b/libraries/amf/amftools-code/include/nAmf.h @@ -0,0 +1,102 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NAMF_H +#define NAMF_H + +#include +#include + +#include "nMetadata.h" +#include "nObject.h" +#include "nConstellation.h" +#include "nTexture.h" +#include "nMaterial.h" + +class CXmlStreamWrite; +class CXmlStreamRead; + + + +enum UnitSystem {UNIT_MM, UNIT_M, UNIT_IN, UNIT_FT, UNIT_UM}; + +class nAmf +{ + public: + nAmf(void); + ~nAmf(void); + nAmf(const nAmf& In) {*this = In;} //copy constructor + nAmf& operator=(const nAmf& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0, bool* pCancelFlag = 0); + bool ReadXML(CXmlStreamRead* pXML, bool StrictLoad=true, std::string* pMessage = 0, bool* pCancelFlag = 0); + bool CheckValid(bool FixNode=true, std::string* pMessage = 0); + + //required attributes + + //optional attributes + bool UnitsExist; + UnitSystem aUnit; + bool VersionExists; + double aVersion; + + + //required children + std::vector Objects; + + //optional children + std::vector Metadata; + std::vector Constellations; + std::vector Textures; + std::vector Materials; + + + + //Utilities + int GetNumObjects(void) {return (int)Objects.size();} + int GetNumConstellations(void) {return (int)Constellations.size();} + int GetNumMaterials(void) {return (int)Materials.size();} + int GetNumTextures(void) {return (int)Textures.size();} + + + int GetUsedGeoID(void); + int GetUnusedGeoID(void); + int GetUnusedTexID(void); + int GetUnusedMatID(void); + bool IsDuplicateGeoID(int IdToCheck); //returns true if two or more of this ID exist + bool IsDuplicateTexID(int IdToCheck); //returns true if two or more of this ID exist + bool IsDuplicateMatID(int IdToCheck); //returns true if two or more of this ID exist + + + std::string GetGeoNameFromID(int GeometryID); //finds the name of the object or constellation with this internal ID + std::string GetMatNameFromID(int MaterialID); //returns name string for a material if it exists + + nObject* GetObjectByID(int GeometryID); //returns pointer to a constellation that has this ID, or NULL if non exists. + nConstellation* GetConstellationByID(int GeometryID); //returns pointer to a constellation that has this ID, or NULL if non exists. + nMaterial* GetMaterialByID(int MaterialID); //returns a TEMPORARY pointer to the first material with the specified ID, or NULL if not found + nTexture* GetTextureByID(int TextureID); //returns a TEMPORARY pointer to the first texture with the specified ID, or NULL if not found + + int AppendObject(std::string Name = ""); + int AppendConstellation(std::string Name = ""); + int AppendMaterial(std::string Name = ""); + + void DeleteGeometry(int GeometryID); //Deletes by the internal object ID, not index in the vector!! Removes all instances to this object or constellation, too! + void DeleteMaterial(int MaterialID); + + bool IsTopLevelGeo(int GeometryID); //returns true if not referenced by any constellations + +// std::string GetMatName(nVolume* pVolume); //returns name string for material of this volume if it has one. + + + +}; + +#endif //NAMF_H diff --git a/libraries/amf/amftools-code/include/nColor.h b/libraries/amf/amftools-code/include/nColor.h new file mode 100644 index 00000000..cb4a1aa0 --- /dev/null +++ b/libraries/amf/amftools-code/include/nColor.h @@ -0,0 +1,61 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NCOLOR_H +#define NCOLOR_H + +#include +#include "Equation.h" + +class CXmlStreamWrite; +class CXmlStreamRead; + +class nAmf; +//enum Profile {sRGB, AdobeRGB, Wide_Gamut_RGB, CIERGB, CIELAB, CIEXYZ}; + +class nColor +{ +public: + nColor(); + ~nColor(void); + nColor(double RIn, double GIn, double BIn) {Clear(); R.FromConstant(RIn); G.FromConstant(GIn); B.FromConstant(BIn);} + nColor(double RIn, double GIn, double BIn, double AIn) {Clear(); R.FromConstant(RIn); G.FromConstant(GIn); B.FromConstant(BIn); A.FromConstant(AIn); AExists = true;} + nColor(const nColor& In) {*this = In;} //copy constructor + nColor& operator=(const nColor& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //optional attributes +// Profile aProfile; //color space profile + + //required children + CEquation R; //if these equation pointers are constant, caching is used + CEquation G; + CEquation B; + + + //optional children + bool AExists; //flag for if there is alpha data in the file + CEquation A; + + //utilities + void GetColor(double xIn, double yIn, double zIn, double* rOut, double* gOut, double* bOut, double* aOut = NULL) {*rOut = GetR(xIn, yIn, zIn); *gOut = GetG(xIn, yIn, zIn); *bOut = GetB(xIn, yIn, zIn); if(aOut){if (AExists){ *aOut = GetA(xIn, yIn, zIn);} else {*aOut = 1.0;}}} + double GetR(double XLoc = 0, double YLoc = 0, double ZLoc = 0) {return R.Eval(XLoc, YLoc, ZLoc, true);} //returns range form 0 to 1; + double GetG(double XLoc = 0, double YLoc = 0, double ZLoc = 0) {return G.Eval(XLoc, YLoc, ZLoc, true);} //returns range form 0 to 1; + double GetB(double XLoc = 0, double YLoc = 0, double ZLoc = 0) {return B.Eval(XLoc, YLoc, ZLoc, true);} //returns range form 0 to 1; + double GetA(double XLoc = 0, double YLoc = 0, double ZLoc = 0) {if (AExists) return A.Eval(XLoc, YLoc, ZLoc, true); else return 1.0;} //returns range form 0 to 1; + + void SetConstColor(double RIn, double GIn, double BIn, double AIn = 1.0){R.FromConstant(RIn); G.FromConstant(GIn); B.FromConstant(BIn); if(AIn == 1.0) AExists = false; else {A.FromConstant(AIn); AExists = true;}} +}; + +#endif diff --git a/libraries/amf/amftools-code/include/nComposite.h b/libraries/amf/amftools-code/include/nComposite.h new file mode 100644 index 00000000..15e4f55b --- /dev/null +++ b/libraries/amf/amftools-code/include/nComposite.h @@ -0,0 +1,52 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NCOMPOSITE_H +#define NCOMPOSITE_H + +#include +#include "Equation.h" + +class CXmlStreamWrite; +class CXmlStreamRead; + +class nAmf; + +class nComposite +{ +public: + nComposite(void); + ~nComposite(void); +// nComposite(int MaterialIDIn) {Clear(); MaterialID = MaterialIDIn;} + nComposite(const nComposite& In) {*this = In;} //copy constructor + nComposite& operator=(const nComposite& In); //overload Equals + void Clear(void); + + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required attributes + int aMaterialID; //the material that this equation governs the percent of + + //required data + CEquation MatEquation; + + //Utilities + bool SetEquation(std::string AmfEquationIn, nAmf* pAmf, std::string* pMessage = 0); + bool CheckEquation(std::string* pMessage = 0) {return MatEquation.CheckParse(pMessage);} + std::string GetEquation(void) {return MatEquation.ToAmfString();} + double EvalEquation(double x, double y, double z){return MatEquation.Eval(x, y, z);} + bool ScaleEquation(double ScaleFactor, std::string* pMessage = 0); //Scale factor is the factor we're changing the Amf (2.0 = doubling the object size) + +}; + +#endif //NCOMPOSITE_H diff --git a/libraries/amf/amftools-code/include/nConstellation.h b/libraries/amf/amftools-code/include/nConstellation.h new file mode 100644 index 00000000..868871f3 --- /dev/null +++ b/libraries/amf/amftools-code/include/nConstellation.h @@ -0,0 +1,59 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NCONSTELLATION_H +#define NCONSTELLATION_H + +#include +#include + +#include "nInstance.h" +#include "nMetadata.h" + +class CXmlStreamWrite; +class CXmlStreamRead; + + +class nConstellation +{ +public: + nConstellation(nAmf* pnAmfIn); + ~nConstellation(void); + nConstellation(const nConstellation& In) {*this = In;} //copy constructor + nConstellation& operator=(const nConstellation& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required tags + std::vector Instances; + + //required attributes + int aID; + + //optional tags + std::vector Metadata; + + //Utilities + void SetName(std::string NewName); + std::string GetName(void); + void AddInstance(int aObjectID = -1) {Instances.push_back(nInstance(aObjectID));} + void DeleteInstance(int VectorIndex) {Instances.erase(Instances.begin()+VectorIndex);} + int GetNumInstances(void) {return (int)Instances.size();} + + bool IsReferencedBy(nConstellation* pConstellationCheck); //returns true if pUsesCheck references pCheck anywhere in its tree. + + + nAmf* pnAmf; //need to keep pointer to AMF to recurse in constellation tags... +}; + +#endif //NCONSTELLATION_H diff --git a/libraries/amf/amftools-code/include/nCoordinates.h b/libraries/amf/amftools-code/include/nCoordinates.h new file mode 100644 index 00000000..584b97f3 --- /dev/null +++ b/libraries/amf/amftools-code/include/nCoordinates.h @@ -0,0 +1,39 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NCOORDINATES_H +#define NCOORDINATES_H + +#include + +class CXmlStreamWrite; +class CXmlStreamRead; + +class nAmf; + +class nCoordinates +{ +public: + nCoordinates(void); + ~nCoordinates(void); + nCoordinates(const double Xin, const double Yin, const double Zin); + nCoordinates(const nCoordinates& In) {*this = In;} //copy constructor + nCoordinates& operator=(const nCoordinates& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required children + double X, Y, Z; +}; + +#endif diff --git a/libraries/amf/amftools-code/include/nEdge.h b/libraries/amf/amftools-code/include/nEdge.h new file mode 100644 index 00000000..027e365f --- /dev/null +++ b/libraries/amf/amftools-code/include/nEdge.h @@ -0,0 +1,40 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NEDGE_H +#define NEDGE_H + +#include + +class CXmlStreamWrite; +class CXmlStreamRead; + +class nAmf; + +class nEdge +{ +public: + nEdge(void); + ~nEdge(void); + nEdge(const nEdge& In) {*this = In;} //copy constructor + nEdge& operator=(const nEdge& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + int v1, v2; + double dx1, dy1, dz1, dx2, dy2, dz2; + + void SetDirectionVectors(double Dx1In, double Dy1In, double Dz1In, double Dx2In, double Dy2In, double Dz2In){dx1=Dx1In; dy1=Dy1In; dz1=Dz1In; dx2=Dx2In; dy2=Dy2In; dz2=Dz2In;} +}; + +#endif diff --git a/libraries/amf/amftools-code/include/nInstance.h b/libraries/amf/amftools-code/include/nInstance.h new file mode 100644 index 00000000..882b9a86 --- /dev/null +++ b/libraries/amf/amftools-code/include/nInstance.h @@ -0,0 +1,42 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NINSTANCE_H +#define NINSTANCE_H + +#include + +class CXmlStreamWrite; +class CXmlStreamRead; + +class nAmf; + +class nInstance +{ +public: + nInstance(void); + ~nInstance(void); + nInstance(int ObjectIDIn) {Clear(); aObjectID = ObjectIDIn;} + nInstance(const nInstance& In) {*this = In;} //copy constructor + nInstance& operator=(const nInstance& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required attibutes + int aObjectID; + + //optional tags (although without one this would be useless...) + double DeltaX, DeltaY, DeltaZ, rX, rY, rZ; +}; + +#endif //NINSTANCE_H diff --git a/libraries/amf/amftools-code/include/nMaterial.h b/libraries/amf/amftools-code/include/nMaterial.h new file mode 100644 index 00000000..691afceb --- /dev/null +++ b/libraries/amf/amftools-code/include/nMaterial.h @@ -0,0 +1,70 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NMATERIAL_H +#define NMATERIAL_H + +#include +#include +#include "nColor.h" +#include "nComposite.h" +#include "nMetadata.h" + +class CXmlStreamWrite; +class CXmlStreamRead; + + + + + +class nMaterial +{ +public: + nMaterial(nAmf* pnAmfIn); + ~nMaterial(void); + nMaterial(const nMaterial& In) {*this = In;} //copy constructor + nMaterial& operator=(const nMaterial& In); //overload Equals + void Clear(void); + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required attributes + int aID; + + //required tags + std::vector Composites; + + //optional tags + bool ColorExists; + nColor Color; + std::vector Metadata; + + //utilities + int GetNumComposites() {return (int) Composites.size();} + void SetName(std::string NewName); + std::string GetName(void); + void SetConstColor(double RIn, double GIn, double BIn, double AIn = 1.0) {if (aID!=0){Color.SetConstColor(RIn, GIn, BIn, AIn); ColorExists = true;}} + void GetConstColor(double* rOut, double* gOut, double* bOut) {GetColorAt(0,0,0, rOut, gOut, bOut);} + void GetColorAt(double xIn, double yIn, double zIn, double* rOut, double* gOut, double* bOut, double* aOut = NULL); + + //add and delete composite function are in the top level nAmf class to allow us to enforce no recursion + + //composite is only ever a sub of material, but needs functions here to check against recursion... + int AddCompositeInstance(int InstanceMaterialID = 0, std::string AmfEquationIn = "1", std::string* pMessage = 0); //returns the index. + void DeleteCompositeInstance(int CompositeIndex); + bool IsReferencedBy(nMaterial* pMaterialCheck); //returns true if pUsesCheck references this material anywhere in its tree. + + nAmf* pnAmf; //need to keep pointer to AMF to recurse in composite tags... + +}; + +#endif //NMATERIAL_H diff --git a/libraries/amf/amftools-code/include/nMesh.h b/libraries/amf/amftools-code/include/nMesh.h new file mode 100644 index 00000000..11535ee2 --- /dev/null +++ b/libraries/amf/amftools-code/include/nMesh.h @@ -0,0 +1,55 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NMESH_H +#define NMESH_H + +#include +#include +#include "nVertices.h" +#include "nVolume.h" + +class CXmlStreamWrite; +class CXmlStreamRead; + +//class nVolume; + + +class nMesh +{ +public: + nMesh(void); + ~nMesh(void); + nMesh(const nMesh& In) {*this = In;} //copy constructor + nMesh& operator=(const nMesh& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0, bool* pCancelFlag = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0, bool* pCancelFlag = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required tags + nVertices Vertices; + std::vector Volumes; + + //utilities + int GetNumVertices(void){return Vertices.GetNumVertices();} + int GetNumEdges(void){return Vertices.GetNumEdges();} + int AddVertex(nVertex& Vin) {return Vertices.AddVertex(Vin);} + int AddEdge(nEdge& Ein) {return Vertices.AddEdge(Ein);} + void Translate(double dx, double dy, double dz) {Vertices.Translate(dx, dy, dz);} + void Rotate(double rx, double ry, double rz) {Vertices.Rotate(rx, ry, rz);} + + nVolume* NewVolume(std::string Name) {Volumes.push_back(nVolume(Name)); return &Volumes.back();} + + bool Bounds(double* MinX, double* MaxX, double* MinY, double* MaxY, double* MinZ, double* MaxZ); //returns false if no vertices +}; + +#endif //NMESH_H diff --git a/libraries/amf/amftools-code/include/nMetadata.h b/libraries/amf/amftools-code/include/nMetadata.h new file mode 100644 index 00000000..699cb70e --- /dev/null +++ b/libraries/amf/amftools-code/include/nMetadata.h @@ -0,0 +1,47 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NMETADATA_H +#define NMETADATA_H + +#include + +class CXmlStreamWrite; +class CXmlStreamRead; + +class nAmf; + +enum MetaDataType {MD_INVALID, MD_NAME, MD_DESCRIPTION, MD_URL, MD_AUTHOR, MD_COMPANY, MD_CAD, MD_REVISION, MD_TOLERANCE, MD_VOLUME, MD_ELASTICMOD, MD_POISSONRATIO}; + +class nMetadata +{ +public: + nMetadata(void); + ~nMetadata(void); + nMetadata(MetaDataType TypeIn, std::string DataIn){Type = TypeIn; Data = DataIn;} + nMetadata(const nMetadata& In) {*this = In;} //copy constructor + nMetadata& operator=(const nMetadata& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required attributes + MetaDataType Type; + + //required data + std::string Data; + + + +}; + +#endif //NMETADATA_H diff --git a/libraries/amf/amftools-code/include/nNormal.h b/libraries/amf/amftools-code/include/nNormal.h new file mode 100644 index 00000000..4c04649b --- /dev/null +++ b/libraries/amf/amftools-code/include/nNormal.h @@ -0,0 +1,39 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NNORMAL_H +#define NNORMAL_H + +#include + +class CXmlStreamWrite; +class CXmlStreamRead; + +class nAmf; + +class nNormal +{ +public: + nNormal(void); + ~nNormal(void); + nNormal(const nNormal& In) {*this = In;} //copy constructor + nNormal& operator=(const nNormal& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required children + double nX, nY, nZ; + +}; + +#endif diff --git a/libraries/amf/amftools-code/include/nObject.h b/libraries/amf/amftools-code/include/nObject.h new file mode 100644 index 00000000..63ffa74d --- /dev/null +++ b/libraries/amf/amftools-code/include/nObject.h @@ -0,0 +1,66 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NOBJECT_H +#define NOBJECT_H + +#include +#include +#include "nColor.h" +#include "nMesh.h" + +class CXmlStreamWrite; +class CXmlStreamRead; + +class nMetadata; +//class nAmf; + +class nObject +{ +public: + nObject(nAmf* pnAmfIn); + ~nObject(void); + nObject(const nObject& In) {*this = In;} //copy constructor + nObject& operator=(const nObject& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0, bool* pCancelFlag = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0, bool* pCancelFlag = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required tags + std::vector Meshes; + + //optional tags + bool ColorExists; + nColor Color; + std::vector Metadata; + + //required attributes + int aID; + + //Utilities + void SetName(std::string NewName); + std::string GetName(void); + + int GetNumMeshes(void) {return (int)Meshes.size();} + void SetColor(nColor& ColorIn) {Color = ColorIn; ColorExists = true;} + void RemoveColor(void) {ColorExists = false;} + void GetColorAt(double xIn, double yIn, double zIn, double* rOut, double* gOut, double* bOut, double* aOut) {if (ColorExists){Color.GetColor(xIn, yIn, zIn, rOut, gOut, bOut, aOut);} else{*rOut=0; *gOut=0; *bOut=0; *aOut=1.0;}} + void Translate(double dx, double dy, double dz); + void Rotate(double rx, double ry, double rz); + + + + nAmf* pnAmf; //need to keep pointer to parent AMF + +}; + +#endif //NOBJECT_H diff --git a/libraries/amf/amftools-code/include/nTexmap.h b/libraries/amf/amftools-code/include/nTexmap.h new file mode 100644 index 00000000..342f405d --- /dev/null +++ b/libraries/amf/amftools-code/include/nTexmap.h @@ -0,0 +1,50 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NMAP_H +#define NMAP_H + +#include + +class CXmlStreamWrite; +class CXmlStreamRead; + +class nAmf; + +class nTexmap +{ +public: + nTexmap(void); + ~nTexmap(void); + nTexmap(int RTexIdIn, int GTexIdIn, int BTexIdIn, double U1In, double U2In, double U3In, double V1In, double V2In, double V3In) {Clear(); RTexID = RTexIdIn; GTexID = GTexIdIn; BTexID = BTexIdIn; uTex1 = U1In; uTex2 = U2In; uTex3 = U3In; vTex1 = V1In; vTex2 = V2In; vTex3 = V3In;} + nTexmap(const nTexmap& In) {*this = In;} //copy constructor + nTexmap& operator=(const nTexmap& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required attibutes + int RTexID, GTexID, BTexID; + + //optional attributes + int ATexID; + bool ATexIDExists; + + //required tags + double uTex1, uTex2, uTex3, vTex1, vTex2, vTex3; + + //optional tags + double wTex1, wTex2, wTex3; + bool WExists; +}; + +#endif //NMAP_H diff --git a/libraries/amf/amftools-code/include/nTexture.h b/libraries/amf/amftools-code/include/nTexture.h new file mode 100644 index 00000000..f088e25a --- /dev/null +++ b/libraries/amf/amftools-code/include/nTexture.h @@ -0,0 +1,61 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NTEXTURE_H +#define NTEXTURE_H + +#include +#include + +class CXmlStreamWrite; +class CXmlStreamRead; + +class nAmf; + +enum TexType {TT_GRAYSCALE}; +static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +class nTexture +{ +public: + nTexture(nAmf* pnAmfIn); + ~nTexture(); + nTexture(const nTexture& In) {*this = In;} //copy constructor + nTexture& operator=(const nTexture& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required attributes + int aID, aWidth, aHeight, aDepth; + TexType Type; + bool aTiled; + + //required data + std::vector BinaryData; //this must be converted to base 64 before writing to xml. + + //utility functions + std::string DataToBase64(void) {return ToBase64(BinaryData.data(), BinaryData.size());} //converts data to base 64 for writing to XML + bool Base64ToData(std::string inputBase64); //converts base 64 to data. returns false if not valid B64 data + void GetSize(int* WidthOut, int* HeightOut, int* DepthOut = NULL) {*WidthOut = aWidth; *HeightOut = aHeight; if (DepthOut) *DepthOut = aDepth;} + + static inline bool is_base64(unsigned char c) {return (isalnum(c) || (c == '+') || (c == '/'));} + std::string ToBase64(unsigned char const* , unsigned int len); + std::string FromBase64(std::string const& s); + + double GetValue(double uIn, double vIn, double wIn = 0); + + nAmf* pnAmf; //need to keep pointer to parent AMF + +}; + +#endif //NTEXTURE_H diff --git a/libraries/amf/amftools-code/include/nTriangle.h b/libraries/amf/amftools-code/include/nTriangle.h new file mode 100644 index 00000000..7dd71866 --- /dev/null +++ b/libraries/amf/amftools-code/include/nTriangle.h @@ -0,0 +1,57 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NTRIANGLE_H +#define NTRIANGLE_H + +#include +#include "nColor.h" +#include "nTexmap.h" + +class CXmlStreamWrite; +class CXmlStreamRead; + + + +class nTriangle +{ +public: + nTriangle(void); + ~nTriangle(void); + nTriangle(const int v1In, const int v2In, const int v3In) {Clear(); v1 = v1In; v2 = v2In; v3 = v3In;} + nTriangle(const int v1In, const int v2In, const int v3In, const nColor& ColorIn) {Clear(); v1 = v1In; v2 = v2In; v3 = v3In; ColorExists = true; Color = ColorIn;} + + nTriangle(const nTriangle& In) {*this = In;} //copy constructor + nTriangle& operator=(const nTriangle& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required tags + int v1, v2, v3; + + //optional tags; + bool ColorExists; + nColor Color; + + bool TexMapExists; + nTexmap TexMap; + +public: + void SetTexMap(const nTexmap& TexMapIn) {TexMap = TexMapIn; TexMapExists = true;} + nTexmap* GetpTexMap(void) {return &TexMap;} + +// std::vector MapList; + +}; + +#endif diff --git a/libraries/amf/amftools-code/include/nVertex.h b/libraries/amf/amftools-code/include/nVertex.h new file mode 100644 index 00000000..199e4f30 --- /dev/null +++ b/libraries/amf/amftools-code/include/nVertex.h @@ -0,0 +1,61 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NVERTEX_H +#define NVERTEX_H + +#include "nCoordinates.h" +#include "nNormal.h" +#include "nColor.h" + +class CXmlStreamWrite; +class CXmlStreamRead; + + +class nVertex +{ +public: + nVertex(void); + ~nVertex(void); + nVertex(const double X, const double Y, const double Z); + nVertex(const double X, const double Y, const double Z, const nColor& ColorIn); + nVertex(const nVertex& In){*this = In;} //copy constructor + nVertex& operator=(const nVertex& In); //overload Equals + void Clear(void); //clears all data + + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required tags + nCoordinates Coordinates; + + //optional tags + bool NormalExists; + nNormal Normal; + bool ColorExists; + nColor Color; + + //utilities: + double GetX(void) {return Coordinates.X;} + double GetY(void) {return Coordinates.Y;} + double GetZ(void) {return Coordinates.Z;} + double GetNX(void) {return Normal.nX;} + double GetNY(void) {return Normal.nY;} + double GetNZ(void) {return Normal.nZ;} + + void SetCoordinates(double XIn, double YIn, double ZIn){Coordinates.X=XIn; Coordinates.Y=YIn;Coordinates.Z=ZIn;} + void SetNormal(double nXIn, double nYIn, double nZIn){Normal.nX=nXIn; Normal.nY=nYIn;Normal.nZ=nZIn;} + + +}; + +#endif diff --git a/libraries/amf/amftools-code/include/nVertices.h b/libraries/amf/amftools-code/include/nVertices.h new file mode 100644 index 00000000..351537b8 --- /dev/null +++ b/libraries/amf/amftools-code/include/nVertices.h @@ -0,0 +1,58 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NVERTICES_H +#define NVERTICES_H + +#include +#include +#include "nVertex.h" +#include "nEdge.h" + +//class nVertex; +///class nEdge; +class CXmlStreamWrite; +class CXmlStreamRead; + + +class nVertices +{ +public: + nVertices(void); + ~nVertices(void); + nVertices(const nVertices& In) {*this = In;} //copy constructor + nVertices& operator=(const nVertices& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0, bool* pCancelFlag = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0, bool* pCancelFlag = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //required tags + std::vector VertexList; + + //optional tags + std::vector EdgeList; + + //utilities + int GetNumVertices(void){return VertexList.size();} + int GetNumEdges(void){return EdgeList.size();} + int AddVertex(nVertex& VIn){VertexList.push_back(VIn); return VertexList.size()-1;} //returns index + int AddEdge(nEdge& EIn){EdgeList.push_back(EIn); return EdgeList.size()-1;} //returns index + void Translate(double dx, double dy, double dz); + void Rotate(double rx, double ry, double rz); //rotates about origin + //RotateQuat + //RotateAngleAxis + + + +}; + +#endif //NVERTICES_H diff --git a/libraries/amf/amftools-code/include/nVolume.h b/libraries/amf/amftools-code/include/nVolume.h new file mode 100644 index 00000000..4e149b53 --- /dev/null +++ b/libraries/amf/amftools-code/include/nVolume.h @@ -0,0 +1,66 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#ifndef NVOLUME_H +#define NVOLUME_H + +#include +#include +#include "nColor.h" +#include "nMetadata.h" +#include "nTriangle.h" + +class CXmlStreamWrite; +class CXmlStreamRead; + + +enum VolType {VT_OBJECT, VT_SUPPORT}; + +class nVolume +{ +public: + nVolume(void); + ~nVolume(void); + nVolume(std::string Name); + nVolume(const nVolume& In) {*this = In;} //copy constructor + nVolume& operator=(const nVolume& In); //overload Equals + void Clear(void); //clears all data + + //XML read/write + bool WriteXML(CXmlStreamWrite* pXML, std::string* pMessage = 0, bool* pCancelFlag = 0); + bool ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad=true, std::string* pMessage = 0, bool* pCancelFlag = 0); + bool CheckValid(nAmf* pAmf, bool FixNode=true, std::string* pMessage = 0); + + //optional attributes + bool MaterialIDExists; + int aMaterialID; +// bool TypeExists; +// VolType Type; + + //required tags + std::vector Triangles; + + //optional tags + bool ColorExists; + nColor Color; + std::vector Metadata; + + //Utilities + void SetName(std::string NewName); + std::string GetName(void); + void SetMaterialID(int NewMatId); + bool GetMaterialID(int* pMatIdRet); + + nTriangle* AddTriangle(nTriangle& TriIn) {Triangles.push_back(TriIn); return &Triangles.back();} //returns temporary pointer to this triangle + void SetColor(nColor& ColorIn) {Color = ColorIn; ColorExists = true;} + void EraseColor(void) {ColorExists = false;} + void GetColorAt(double xIn, double yIn, double zIn, double* rOut, double* gOut, double* bOut, double* aOut) {if (ColorExists){Color.GetColor(xIn, yIn, zIn, rOut, gOut, bOut, aOut);} else{*rOut=0; *gOut=0; *bOut=0; *aOut=1.0;}} +}; + +#endif diff --git a/libraries/amf/amftools-code/include/rapidxml/rapidxml.h b/libraries/amf/amftools-code/include/rapidxml/rapidxml.h new file mode 100644 index 00000000..6b82f20a --- /dev/null +++ b/libraries/amf/amftools-code/include/rapidxml/rapidxml.h @@ -0,0 +1,2596 @@ +#ifndef RAPIDXML_HPP_INCLUDED +#define RAPIDXML_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation + +// If standard library is disabled, user must provide implementations of required functions and typedefs +#if !defined(RAPIDXML_NO_STDLIB) + #include // For std::size_t + #include // For assert + #include // For placement new +#endif + +// On MSVC, disable "conditional expression is constant" warning (level 4). +// This warning is almost impossible to avoid with certain types of templated code +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) // Conditional expression is constant +#endif + +/////////////////////////////////////////////////////////////////////////// +// RAPIDXML_PARSE_ERROR + +#if defined(RAPIDXML_NO_EXCEPTIONS) + +#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } + +namespace rapidxml +{ + //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, + //! this function is called to notify user about the error. + //! It must be defined by the user. + //!

+ //! This function cannot return. If it does, the results are undefined. + //!

+ //! A very simple definition might look like that: + //!
+    //! void %rapidxml::%parse_error_handler(const char *what, void *where)
+    //! {
+    //!     std::cout << "Parse error: " << what << "\n";
+    //!     std::abort();
+    //! }
+    //! 
+ //! \param what Human readable description of the error. + //! \param where Pointer to character data where error was detected. + void parse_error_handler(const char *what, void *where); +} + +#else + +#include // For std::exception + +#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) + +namespace rapidxml +{ + + //! Parse error exception. + //! This exception is thrown by the parser when an error occurs. + //! Use what() function to get human-readable error message. + //! Use where() function to get a pointer to position within source text where error was detected. + //!

+ //! If throwing exceptions by the parser is undesirable, + //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. + //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. + //! This function must be defined by the user. + //!

+ //! This class derives from std::exception class. + class parse_error: public std::exception + { + + public: + + //! Constructs parse error + parse_error(const char *what, void *where) + : m_what(what) + , m_where(where) + { + } + + //! Gets human readable description of error. + //! \return Pointer to null terminated description of the error. + virtual const char *what() const throw() + { + return m_what; + } + + //! Gets pointer to character data where error happened. + //! Ch should be the same as char type of xml_document that produced the error. + //! \return Pointer to location within the parsed string where error occured. + template + Ch *where() const + { + return reinterpret_cast(m_where); + } + + private: + + const char *m_what; + void *m_where; + + }; +} + +#endif + +/////////////////////////////////////////////////////////////////////////// +// Pool sizes + +#ifndef RAPIDXML_STATIC_POOL_SIZE + // Size of static memory block of memory_pool. + // Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // No dynamic memory allocations are performed by memory_pool until static memory is exhausted. + #define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_DYNAMIC_POOL_SIZE + // Size of dynamic memory block of memory_pool. + // Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. + #define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_ALIGNMENT + // Memory allocation alignment. + // Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. + // All memory allocations for nodes, attributes and strings will be aligned to this value. + // This must be a power of 2 and at least 1, otherwise memory_pool will not work. + #define RAPIDXML_ALIGNMENT sizeof(void *) +#endif + +namespace rapidxml +{ + // Forward declarations + template class xml_node; + template class xml_attribute; + template class xml_document; + + //! Enumeration listing all node types produced by the parser. + //! Use xml_node::type() function to query node type. + enum node_type + { + node_document, //!< A document node. Name and value are empty. + node_element, //!< An element node. Name contains element name. Value contains text of first data node. + node_data, //!< A data node. Name is empty. Value contains data text. + node_cdata, //!< A CDATA node. Name is empty. Value contains data text. + node_comment, //!< A comment node. Name is empty. Value contains comment text. + node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. + node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. + node_pi //!< A PI node. Name contains target. Value contains instructions. + }; + + /////////////////////////////////////////////////////////////////////// + // Parsing flags + + //! Parse flag instructing the parser to not create data nodes. + //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_data_nodes = 0x1; + + //! Parse flag instructing the parser to not use text of first data node as a value of parent element. + //! Can be combined with other flags by use of | operator. + //! Note that child data nodes of element node take precendence over its value when printing. + //! That is, if element has one or more child data nodes and a value, the value will be ignored. + //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. + //!

+ //! See xml_document::parse() function. + const int parse_no_element_values = 0x2; + + //! Parse flag instructing the parser to not place zero terminators after strings in the source text. + //! By default zero terminators are placed, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_string_terminators = 0x4; + + //! Parse flag instructing the parser to not translate entities in the source text. + //! By default entities are translated, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_entity_translation = 0x8; + + //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. + //! By default, UTF-8 handling is enabled. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_utf8 = 0x10; + + //! Parse flag instructing the parser to create XML declaration node. + //! By default, declaration node is not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_declaration_node = 0x20; + + //! Parse flag instructing the parser to create comments nodes. + //! By default, comment nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_comment_nodes = 0x40; + + //! Parse flag instructing the parser to create DOCTYPE node. + //! By default, doctype node is not created. + //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_doctype_node = 0x80; + + //! Parse flag instructing the parser to create PI nodes. + //! By default, PI nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_pi_nodes = 0x100; + + //! Parse flag instructing the parser to validate closing tag names. + //! If not set, name inside closing tag is irrelevant to the parser. + //! By default, closing tags are not validated. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_validate_closing_tags = 0x200; + + //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. + //! By default, whitespace is not trimmed. + //! This flag does not cause the parser to modify source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_trim_whitespace = 0x400; + + //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. + //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. + //! By default, whitespace is not normalized. + //! If this flag is specified, source text will be modified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_normalize_whitespace = 0x800; + + // Compound flags + + //! Parse flags which represent default behaviour of the parser. + //! This is always equal to 0, so that all other flags can be simply ored together. + //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. + //! This also means that meaning of each flag is a negation of the default setting. + //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, + //! and using the flag will disable it. + //!

+ //! See xml_document::parse() function. + const int parse_default = 0; + + //! A combination of parse flags that forbids any modifications of the source text. + //! This also results in faster parsing. However, note that the following will occur: + //!
    + //!
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • + //!
  • entities will not be translated
  • + //!
  • whitespace will not be normalized
  • + //!
+ //! See xml_document::parse() function. + const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; + + //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. + //!

+ //! See xml_document::parse() function. + const int parse_fastest = parse_non_destructive | parse_no_data_nodes; + + //! A combination of parse flags resulting in largest amount of data being extracted. + //! This usually results in slowest parsing. + //!

+ //! See xml_document::parse() function. + const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; + + /////////////////////////////////////////////////////////////////////// + // Internals + + //! \cond internal + namespace internal + { + + // Struct that contains lookup tables for the parser + // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). + template + struct lookup_tables + { + static const unsigned char lookup_whitespace[256]; // Whitespace table + static const unsigned char lookup_node_name[256]; // Node name table + static const unsigned char lookup_text[256]; // Text table + static const unsigned char lookup_text_pure_no_ws[256]; // Text table + static const unsigned char lookup_text_pure_with_ws[256]; // Text table + static const unsigned char lookup_attribute_name[256]; // Attribute name table + static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes + static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes + static const unsigned char lookup_digits[256]; // Digits + static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters + }; + + // Find length of the string + template + inline std::size_t measure(const Ch *p) + { + const Ch *tmp = p; + while (*tmp) + ++tmp; + return tmp - p; + } + + // Compare strings for equality + template + inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive) + { + if (size1 != size2) + return false; + if (case_sensitive) + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (*p1 != *p2) + return false; + } + else + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast(*p2)]) + return false; + } + return true; + } + } + //! \endcond + + /////////////////////////////////////////////////////////////////////// + // Memory pool + + //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. + //! In most cases, you will not need to use this class directly. + //! However, if you need to create nodes manually or modify names/values of nodes, + //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. + //! Not only is this faster than allocating them by using new operator, + //! but also their lifetime will be tied to the lifetime of document, + //! possibly simplyfing memory management. + //!

+ //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. + //! You can also call allocate_string() function to allocate strings. + //! Such strings can then be used as names or values of nodes without worrying about their lifetime. + //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, + //! or when the pool is destroyed. + //!

+ //! It is also possible to create a standalone memory_pool, and use it + //! to allocate nodes, whose lifetime will not be tied to any document. + //!

+ //! Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. + //! Until static memory is exhausted, no dynamic memory allocations are done. + //! When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, + //! by using global new[] and delete[] operators. + //! This behaviour can be changed by setting custom allocation routines. + //! Use set_allocator() function to set them. + //!

+ //! Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. + //! This value defaults to the size of pointer on target architecture. + //!

+ //! To obtain absolutely top performance from the parser, + //! it is important that all nodes are allocated from a single, contiguous block of memory. + //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. + //! If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT + //! to obtain best wasted memory to performance compromise. + //! To do it, define their values before rapidxml.hpp file is included. + //! \param Ch Character type of created nodes. + template + class memory_pool + { + + public: + + //! \cond internal + typedef void *(alloc_func)(std::size_t); // Type of user-defined function used to allocate memory + typedef void (free_func)(void *); // Type of user-defined function used to free memory + //! \endcond + + //! Constructs empty pool with default allocator functions. + memory_pool() + : m_alloc_func(0) + , m_free_func(0) + { + init(); + } + + //! Destroys pool and frees all the memory. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Nodes allocated from the pool are no longer valid. + ~memory_pool() + { + clear(); + } + + //! Allocates a new node from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param type Type of node to create. + //! \param name Name to assign to the node, or 0 to assign no name. + //! \param value Value to assign to the node, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated node. This pointer will never be NULL. + xml_node *allocate_node(node_type type, + const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_node)); + xml_node *node = new(memory) xml_node(type); + if (name) + { + if (name_size > 0) + node->name(name, name_size); + else + node->name(name); + } + if (value) + { + if (value_size > 0) + node->value(value, value_size); + else + node->value(value); + } + return node; + } + + //! Allocates a new attribute from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param name Name to assign to the attribute, or 0 to assign no name. + //! \param value Value to assign to the attribute, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated attribute. This pointer will never be NULL. + xml_attribute *allocate_attribute(const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_attribute)); + xml_attribute *attribute = new(memory) xml_attribute; + if (name) + { + if (name_size > 0) + attribute->name(name, name_size); + else + attribute->name(name); + } + if (value) + { + if (value_size > 0) + attribute->value(value, value_size); + else + attribute->value(value); + } + return attribute; + } + + //! Allocates a char array of given size from the pool, and optionally copies a given string to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param source String to initialize the allocated memory with, or 0 to not initialize it. + //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. + //! \return Pointer to allocated char array. This pointer will never be NULL. + Ch *allocate_string(const Ch *source = 0, std::size_t size = 0) + { + assert(source || size); // Either source or size (or both) must be specified + if (size == 0) + size = internal::measure(source) + 1; + Ch *result = static_cast(allocate_aligned(size * sizeof(Ch))); + if (source) + for (std::size_t i = 0; i < size; ++i) + result[i] = source[i]; + return result; + } + + //! Clones an xml_node and its hierarchy of child nodes and attributes. + //! Nodes and attributes are allocated from this memory pool. + //! Names and values are not cloned, they are shared between the clone and the source. + //! Result node can be optionally specified as a second parameter, + //! in which case its contents will be replaced with cloned source node. + //! This is useful when you want to clone entire document. + //! \param source Node to clone. + //! \param result Node to put results in, or 0 to automatically allocate result node + //! \return Pointer to cloned node. This pointer will never be NULL. + xml_node *clone_node(const xml_node *source, xml_node *result = 0) + { + // Prepare result node + if (result) + { + result->remove_all_attributes(); + result->remove_all_nodes(); + result->type(source->type()); + } + else + result = allocate_node(source->type()); + + // Clone name and value + result->name(source->name(), source->name_size()); + result->value(source->value(), source->value_size()); + + // Clone child nodes and attributes + for (xml_node *child = source->first_node(); child; child = child->next_sibling()) + result->append_node(clone_node(child)); + for (xml_attribute *attr = source->first_attribute(); attr; attr = attr->next_attribute()) + result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); + + return result; + } + + //! Clears the pool. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Any nodes or strings allocated from the pool will no longer be valid. + void clear() + { + while (m_begin != m_static_memory) + { + char *previous_begin = reinterpret_cast
(align(m_begin))->previous_begin; + if (m_free_func) + m_free_func(m_begin); + else + delete[] m_begin; + m_begin = previous_begin; + } + init(); + } + + //! Sets or resets the user-defined memory allocation functions for the pool. + //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. + //! Allocation function must not return invalid pointer on failure. It should either throw, + //! stop the program, or use longjmp() function to pass control to other place of program. + //! If it returns invalid pointer, results are undefined. + //!

+ //! User defined allocation functions must have the following forms: + //!
+ //!
void *allocate(std::size_t size); + //!
void free(void *pointer); + //!

+ //! \param af Allocation function, or 0 to restore default function + //! \param ff Free function, or 0 to restore default function + void set_allocator(alloc_func *af, free_func *ff) + { + assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet + m_alloc_func = af; + m_free_func = ff; + } + + private: + + struct header + { + char *previous_begin; + }; + + void init() + { + m_begin = m_static_memory; + m_ptr = align(m_begin); + m_end = m_static_memory + sizeof(m_static_memory); + } + + char *align(char *ptr) + { + std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1)); + return ptr + alignment; + } + + char *allocate_raw(std::size_t size) + { + // Allocate + void *memory; + if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] + { + memory = m_alloc_func(size); + assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp + } + else + { + memory = new char[size]; +#ifdef RAPIDXML_NO_EXCEPTIONS + if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc + RAPIDXML_PARSE_ERROR("out of memory", 0); +#endif + } + return static_cast(memory); + } + + void *allocate_aligned(std::size_t size) + { + // Calculate aligned pointer + char *result = align(m_ptr); + + // If not enough memory left in current pool, allocate a new pool + if (result + size > m_end) + { + // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) + std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; + if (pool_size < size) + pool_size = size; + + // Allocate + std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation + char *raw_memory = allocate_raw(alloc_size); + + // Setup new pool in allocated memory + char *pool = align(raw_memory); + header *new_header = reinterpret_cast
(pool); + new_header->previous_begin = m_begin; + m_begin = raw_memory; + m_ptr = pool + sizeof(header); + m_end = raw_memory + alloc_size; + + // Calculate aligned pointer again using new pool + result = align(m_ptr); + } + + // Update pool and return aligned pointer + m_ptr = result + size; + return result; + } + + char *m_begin; // Start of raw memory making up current pool + char *m_ptr; // First free byte in current pool + char *m_end; // One past last available byte in current pool + char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory + alloc_func *m_alloc_func; // Allocator function, or 0 if default is to be used + free_func *m_free_func; // Free function, or 0 if default is to be used + }; + + /////////////////////////////////////////////////////////////////////////// + // XML base + + //! Base class for xml_node and xml_attribute implementing common functions: + //! name(), name_size(), value(), value_size() and parent(). + //! \param Ch Character type to use + template + class xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + // Construct a base with empty name, value and parent + xml_base() + : m_name(0) + , m_value(0) + , m_parent(0) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets name of the node. + //! Interpretation of name depends on type of node. + //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use name_size() function to determine length of the name. + //! \return Name of node, or empty string if node has no name. + Ch *name() const + { + return m_name ? m_name : nullstr(); + } + + //! Gets size of node name, not including terminator character. + //! This function works correctly irrespective of whether name is or is not zero terminated. + //! \return Size of node name, in characters. + std::size_t name_size() const + { + return m_name ? m_name_size : 0; + } + + //! Gets value of node. + //! Interpretation of value depends on type of node. + //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use value_size() function to determine length of the value. + //! \return Value of node, or empty string if node has no value. + Ch *value() const + { + return m_value ? m_value : nullstr(); + } + + //! Gets size of node value, not including terminator character. + //! This function works correctly irrespective of whether value is or is not zero terminated. + //! \return Size of node value, in characters. + std::size_t value_size() const + { + return m_value ? m_value_size : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets name of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of name must be specified separately, because name does not have to be zero terminated. + //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //! \param name Name of node to set. Does not have to be zero terminated. + //! \param size Size of name, in characters. This does not include zero terminator, if one is present. + void name(const Ch *name, std::size_t size) + { + m_name = const_cast(name); + m_name_size = size; + } + + //! Sets name of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). + //! \param name Name of node to set. Must be zero terminated. + void name(const Ch *name) + { + this->name(name, internal::measure(name)); + } + + //! Sets value of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of value must be specified separately, because it does not have to be zero terminated. + //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //!

+ //! If an element has a child node of type node_data, it will take precedence over element value when printing. + //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. + //! \param value value of node to set. Does not have to be zero terminated. + //! \param size Size of value, in characters. This does not include zero terminator, if one is present. + void value(const Ch *value, std::size_t size) + { + m_value = const_cast(value); + m_value_size = size; + } + + //! Sets value of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). + //! \param value Vame of node to set. Must be zero terminated. + void value(const Ch *value) + { + this->value(value, internal::measure(value)); + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets node parent. + //! \return Pointer to parent node, or 0 if there is no parent. + xml_node *parent() const + { + return m_parent; + } + + protected: + + // Return empty string + static Ch *nullstr() + { + static Ch zero = Ch('\0'); + return &zero; + } + + Ch *m_name; // Name of node, or 0 if no name + Ch *m_value; // Value of node, or 0 if no value + std::size_t m_name_size; // Length of node name, or undefined of no name + std::size_t m_value_size; // Length of node value, or undefined if no value + xml_node *m_parent; // Pointer to parent node, or 0 if none + + }; + + //! Class representing attribute node of XML document. + //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). + //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. + //! Thus, this text must persist in memory for the lifetime of attribute. + //! \param Ch Character type to use. + template + class xml_attribute: public xml_base + { + + friend class xml_node; + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty attribute with the specified type. + //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. + xml_attribute() + { + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which attribute is a child. + //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. + xml_document *document() const + { + if (xml_node *node = this->parent()) + { + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + else + return 0; + } + + //! Gets previous attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_prev_attribute : 0; + } + + //! Gets next attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_next_attribute : 0; + } + + private: + + xml_attribute *m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero + xml_attribute *m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML node + + //! Class representing a node of XML document. + //! Each node may have associated name and value strings, which are available through name() and value() functions. + //! Interpretation of name and value depends on type of the node. + //! Type of node can be determined by using type() function. + //!

+ //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. + //! Thus, this text must persist in the memory for the lifetime of node. + //! \param Ch Character type to use. + template + class xml_node: public xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty node with the specified type. + //! Consider using memory_pool of appropriate document to allocate nodes manually. + //! \param type Type of node to construct. + xml_node(node_type type) + : m_type(type) + , m_first_node(0) + , m_first_attribute(0) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets type of node. + //! \return Type of node. + node_type type() const + { + return m_type; + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which node is a child. + //! \return Pointer to document that contains this node, or 0 if there is no parent document. + xml_document *document() const + { + xml_node *node = const_cast *>(this); + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + + //! Gets first child node, optionally matching node name. + //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_first_node; child; child = child->next_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_first_node; + } + + //! Gets last child node, optionally matching node name. + //! Behaviour is undefined if node has no children. + //! Use first_node() to test if node has children. + //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(m_first_node); // Cannot query for last child if node has no children + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_last_node; child; child = child->previous_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_last_node; + } + + //! Gets previous sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_prev_sibling; + } + + //! Gets next sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_next_sibling; + } + + //! Gets first attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute; + } + + //! Gets last attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute ? m_last_attribute : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets type of node. + //! \param type Type of node to set. + void type(node_type type) + { + m_type = type; + } + + /////////////////////////////////////////////////////////////////////////// + // Node manipulation + + //! Prepends a new child node. + //! The prepended child becomes the first child, and all existing children are moved one position back. + //! \param child Node to prepend. + void prepend_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_next_sibling = m_first_node; + m_first_node->m_prev_sibling = child; + } + else + { + child->m_next_sibling = 0; + m_last_node = child; + } + m_first_node = child; + child->m_parent = this; + child->m_prev_sibling = 0; + } + + //! Appends a new child node. + //! The appended child becomes the last child. + //! \param child Node to append. + void append_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_prev_sibling = m_last_node; + m_last_node->m_next_sibling = child; + } + else + { + child->m_prev_sibling = 0; + m_first_node = child; + } + m_last_node = child; + child->m_parent = this; + child->m_next_sibling = 0; + } + + //! Inserts a new child node at specified place inside the node. + //! All children after and including the specified node are moved one position back. + //! \param where Place where to insert the child, or 0 to insert at the back. + //! \param child Node to insert. + void insert_node(xml_node *where, xml_node *child) + { + assert(!where || where->parent() == this); + assert(child && !child->parent() && child->type() != node_document); + if (where == m_first_node) + prepend_node(child); + else if (where == 0) + append_node(child); + else + { + child->m_prev_sibling = where->m_prev_sibling; + child->m_next_sibling = where; + where->m_prev_sibling->m_next_sibling = child; + where->m_prev_sibling = child; + child->m_parent = this; + } + } + + //! Removes first child node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_first_node() + { + assert(first_node()); + xml_node *child = m_first_node; + m_first_node = child->m_next_sibling; + if (child->m_next_sibling) + child->m_next_sibling->m_prev_sibling = 0; + else + m_last_node = 0; + child->m_parent = 0; + } + + //! Removes last child of the node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_last_node() + { + assert(first_node()); + xml_node *child = m_last_node; + if (child->m_prev_sibling) + { + m_last_node = child->m_prev_sibling; + child->m_prev_sibling->m_next_sibling = 0; + } + else + m_first_node = 0; + child->m_parent = 0; + } + + //! Removes specified child from the node + // \param where Pointer to child to be removed. + void remove_node(xml_node *where) + { + assert(where && where->parent() == this); + assert(first_node()); + if (where == m_first_node) + remove_first_node(); + else if (where == m_last_node) + remove_last_node(); + else + { + where->m_prev_sibling->m_next_sibling = where->m_next_sibling; + where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; + where->m_parent = 0; + } + } + + //! Removes all child nodes (but not attributes). + void remove_all_nodes() + { + for (xml_node *node = first_node(); node; node = node->m_next_sibling) + node->m_parent = 0; + m_first_node = 0; + } + + //! Prepends a new attribute to the node. + //! \param attribute Attribute to prepend. + void prepend_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_next_attribute = m_first_attribute; + m_first_attribute->m_prev_attribute = attribute; + } + else + { + attribute->m_next_attribute = 0; + m_last_attribute = attribute; + } + m_first_attribute = attribute; + attribute->m_parent = this; + attribute->m_prev_attribute = 0; + } + + //! Appends a new attribute to the node. + //! \param attribute Attribute to append. + void append_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_prev_attribute = m_last_attribute; + m_last_attribute->m_next_attribute = attribute; + } + else + { + attribute->m_prev_attribute = 0; + m_first_attribute = attribute; + } + m_last_attribute = attribute; + attribute->m_parent = this; + attribute->m_next_attribute = 0; + } + + //! Inserts a new attribute at specified place inside the node. + //! All attributes after and including the specified attribute are moved one position back. + //! \param where Place where to insert the attribute, or 0 to insert at the back. + //! \param attribute Attribute to insert. + void insert_attribute(xml_attribute *where, xml_attribute *attribute) + { + assert(!where || where->parent() == this); + assert(attribute && !attribute->parent()); + if (where == m_first_attribute) + prepend_attribute(attribute); + else if (where == 0) + append_attribute(attribute); + else + { + attribute->m_prev_attribute = where->m_prev_attribute; + attribute->m_next_attribute = where; + where->m_prev_attribute->m_next_attribute = attribute; + where->m_prev_attribute = attribute; + attribute->m_parent = this; + } + } + + //! Removes first attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_first_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_first_attribute; + if (attribute->m_next_attribute) + { + attribute->m_next_attribute->m_prev_attribute = 0; + } + else + m_last_attribute = 0; + attribute->m_parent = 0; + m_first_attribute = attribute->m_next_attribute; + } + + //! Removes last attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_last_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_last_attribute; + if (attribute->m_prev_attribute) + { + attribute->m_prev_attribute->m_next_attribute = 0; + m_last_attribute = attribute->m_prev_attribute; + } + else + m_first_attribute = 0; + attribute->m_parent = 0; + } + + //! Removes specified attribute from node. + //! \param where Pointer to attribute to be removed. + void remove_attribute(xml_attribute *where) + { + assert(first_attribute() && where->parent() == this); + if (where == m_first_attribute) + remove_first_attribute(); + else if (where == m_last_attribute) + remove_last_attribute(); + else + { + where->m_prev_attribute->m_next_attribute = where->m_next_attribute; + where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; + where->m_parent = 0; + } + } + + //! Removes all attributes of node. + void remove_all_attributes() + { + for (xml_attribute *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) + attribute->m_parent = 0; + m_first_attribute = 0; + } + + private: + + /////////////////////////////////////////////////////////////////////////// + // Restrictions + + // No copying + xml_node(const xml_node &); + void operator =(const xml_node &); + + /////////////////////////////////////////////////////////////////////////// + // Data members + + // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. + // This is required for maximum performance, as it allows the parser to omit initialization of + // unneded/redundant values. + // + // The rules are as follows: + // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively + // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage + // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage + + node_type m_type; // Type of node; always valid + xml_node *m_first_node; // Pointer to first child node, or 0 if none; always valid + xml_node *m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero + xml_attribute *m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid + xml_attribute *m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero + xml_node *m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + xml_node *m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML document + + //! This class represents root of the DOM hierarchy. + //! It is also an xml_node and a memory_pool through public inheritance. + //! Use parse() function to build a DOM tree from a zero-terminated XML text string. + //! parse() function allocates memory for nodes and attributes by using functions of xml_document, + //! which are inherited from memory_pool. + //! To access root node of the document, use the document itself, as if it was an xml_node. + //! \param Ch Character type to use. + template + class xml_document: public xml_node, public memory_pool + { + + public: + + //! Constructs empty XML document + xml_document() + : xml_node(node_document) + { + } + + //! Parses zero-terminated XML string according to given flags. + //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. + //! The string must persist for the lifetime of the document. + //! In case of error, rapidxml::parse_error exception will be thrown. + //!

+ //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. + //! Make sure that data is zero-terminated. + //!

+ //! Document can be parsed into multiple times. + //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. + //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. + template + void parse(Ch *text) + { + assert(text); + + // Remove current contents + this->remove_all_nodes(); + this->remove_all_attributes(); + + // Parse BOM, if any + parse_bom(text); + + // Parse children + while (1) + { + // Skip whitespace before node + skip(text); + if (*text == 0) + break; + + // Parse and append new child + if (*text == Ch('<')) + { + ++text; // Skip '<' + if (xml_node *node = parse_node(text)) + this->append_node(node); + } + else + RAPIDXML_PARSE_ERROR("expected <", text); + } + + } + + //! Clears the document by deleting all nodes and clearing the memory pool. + //! All nodes owned by document pool are destroyed. + void clear() + { + this->remove_all_nodes(); + this->remove_all_attributes(); + memory_pool::clear(); + } + + private: + + /////////////////////////////////////////////////////////////////////// + // Internal character utility functions + + // Detect whitespace character + struct whitespace_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; + } + }; + + // Detect node name character + struct node_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; + } + }; + + // Detect attribute name character + struct attribute_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) + struct text_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_no_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_with_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; + } + }; + + // Detect attribute value character + template + struct attribute_value_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Detect attribute value character + template + struct attribute_value_pure_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Insert coded character, using UTF8 or 8-bit ASCII + template + static void insert_coded_character(Ch *&text, unsigned long code) + { + if (Flags & parse_no_utf8) + { + // Insert 8-bit ASCII character + // Todo: possibly verify that code is less than 256 and use replacement char otherwise? + text[0] = static_cast(code); + text += 1; + } + else + { + // Insert UTF8 sequence + if (code < 0x80) // 1 byte sequence + { + text[0] = static_cast(code); + text += 1; + } + else if (code < 0x800) // 2 byte sequence + { + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xC0); + text += 2; + } + else if (code < 0x10000) // 3 byte sequence + { + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xE0); + text += 3; + } + else if (code < 0x110000) // 4 byte sequence + { + text[3] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xF0); + text += 4; + } + else // Invalid, only codes up to 0x10FFFF are allowed in Unicode + { + RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); + } + } + } + + // Skip characters until predicate evaluates to true + template + static void skip(Ch *&text) + { + Ch *tmp = text; + while (StopPred::test(*tmp)) + ++tmp; + text = tmp; + } + + // Skip characters until predicate evaluates to true while doing the following: + // - replacing XML character entity references with proper characters (' & " < > &#...;) + // - condensing whitespace sequences to single space character + template + static Ch *skip_and_expand_character_refs(Ch *&text) + { + // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip + if (Flags & parse_no_entity_translation && + !(Flags & parse_normalize_whitespace) && + !(Flags & parse_trim_whitespace)) + { + skip(text); + return text; + } + + // Use simple skip until first modification is detected + skip(text); + + // Use translation skip + Ch *src = text; + Ch *dest = src; + while (StopPred::test(*src)) + { + // If entity translation is enabled + if (!(Flags & parse_no_entity_translation)) + { + // Test if replacement is needed + if (src[0] == Ch('&')) + { + switch (src[1]) + { + + // & ' + case Ch('a'): + if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) + { + *dest = Ch('&'); + ++dest; + src += 5; + continue; + } + if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) + { + *dest = Ch('\''); + ++dest; + src += 6; + continue; + } + break; + + // " + case Ch('q'): + if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) + { + *dest = Ch('"'); + ++dest; + src += 6; + continue; + } + break; + + // > + case Ch('g'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('>'); + ++dest; + src += 4; + continue; + } + break; + + // < + case Ch('l'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('<'); + ++dest; + src += 4; + continue; + } + break; + + // &#...; - assumes ASCII + case Ch('#'): + if (src[2] == Ch('x')) + { + unsigned long code = 0; + src += 3; // Skip &#x + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 16 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + else + { + unsigned long code = 0; + src += 2; // Skip &# + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 10 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + if (*src == Ch(';')) + ++src; + else + RAPIDXML_PARSE_ERROR("expected ;", src); + continue; + + // Something else + default: + // Ignore, just copy '&' verbatim + break; + + } + } + } + + // If whitespace condensing is enabled + if (Flags & parse_normalize_whitespace) + { + // Test if condensing is needed + if (whitespace_pred::test(*src)) + { + *dest = Ch(' '); ++dest; // Put single space in dest + ++src; // Skip first whitespace char + // Skip remaining whitespace chars + while (whitespace_pred::test(*src)) + ++src; + continue; + } + } + + // No replacement, only copy character + *dest++ = *src++; + + } + + // Return new end + text = src; + return dest; + + } + + /////////////////////////////////////////////////////////////////////// + // Internal parsing functions + + // Parse BOM, if any + template + void parse_bom(Ch *&text) + { + // UTF-8? + if (static_cast(text[0]) == 0xEF && + static_cast(text[1]) == 0xBB && + static_cast(text[2]) == 0xBF) + { + text += 3; // Skup utf-8 bom + } + } + + // Parse XML declaration ( + xml_node *parse_xml_declaration(Ch *&text) + { + // If parsing of declaration is disabled + if (!(Flags & parse_declaration_node)) + { + // Skip until end of declaration + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + + // Create declaration + xml_node *declaration = this->allocate_node(node_declaration); + + // Skip whitespace before attributes or ?> + skip(text); + + // Parse declaration attributes + parse_node_attributes(text, declaration); + + // Skip ?> + if (text[0] != Ch('?') || text[1] != Ch('>')) + RAPIDXML_PARSE_ERROR("expected ?>", text); + text += 2; + + return declaration; + } + + // Parse XML comment (' + return 0; // Do not produce comment node + } + + // Remember value start + Ch *value = text; + + // Skip until end of comment + while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create comment node + xml_node *comment = this->allocate_node(node_comment); + comment->value(value, text - value); + + // Place zero terminator after comment value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip '-->' + return comment; + } + + // Parse DOCTYPE + template + xml_node *parse_doctype(Ch *&text) + { + // Remember value start + Ch *value = text; + + // Skip to > + while (*text != Ch('>')) + { + // Determine character type + switch (*text) + { + + // If '[' encountered, scan for matching ending ']' using naive algorithm with depth + // This works for all W3C test files except for 2 most wicked + case Ch('['): + { + ++text; // Skip '[' + int depth = 1; + while (depth > 0) + { + switch (*text) + { + case Ch('['): ++depth; break; + case Ch(']'): --depth; break; + case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + ++text; + } + break; + } + + // Error on end of text + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Other character, skip it + default: + ++text; + + } + } + + // If DOCTYPE nodes enabled + if (Flags & parse_doctype_node) + { + // Create a new doctype node + xml_node *doctype = this->allocate_node(node_doctype); + doctype->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 1; // skip '>' + return doctype; + } + else + { + text += 1; // skip '>' + return 0; + } + + } + + // Parse PI + template + xml_node *parse_pi(Ch *&text) + { + // If creation of PI nodes is enabled + if (Flags & parse_pi_nodes) + { + // Create pi node + xml_node *pi = this->allocate_node(node_pi); + + // Extract PI target name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected PI target", text); + pi->name(name, text - name); + + // Skip whitespace between pi target and pi + skip(text); + + // Remember start of pi + Ch *value = text; + + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Set pi value (verbatim, no entity expansion or whitespace normalization) + pi->value(value, text - value); + + // Place zero terminator after name and value + if (!(Flags & parse_no_string_terminators)) + { + pi->name()[pi->name_size()] = Ch('\0'); + pi->value()[pi->value_size()] = Ch('\0'); + } + + text += 2; // Skip '?>' + return pi; + } + else + { + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + } + + // Parse and append data + // Return character that ends data. + // This is necessary because this character might have been overwritten by a terminating 0 + template + Ch parse_and_append_data(xml_node *node, Ch *&text, Ch *contents_start) + { + // Backup to contents start if whitespace trimming is disabled + if (!(Flags & parse_trim_whitespace)) + text = contents_start; + + // Skip until end of data + Ch *value = text, *end; + if (Flags & parse_normalize_whitespace) + end = skip_and_expand_character_refs(text); + else + end = skip_and_expand_character_refs(text); + + // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > + if (Flags & parse_trim_whitespace) + { + if (Flags & parse_normalize_whitespace) + { + // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end + if (*(end - 1) == Ch(' ')) + --end; + } + else + { + // Backup until non-whitespace character is found + while (whitespace_pred::test(*(end - 1))) + --end; + } + } + + // If characters are still left between end and value (this test is only necessary if normalization is enabled) + // Create new data node + if (!(Flags & parse_no_data_nodes)) + { + xml_node *data = this->allocate_node(node_data); + data->value(value, end - value); + node->append_node(data); + } + + // Add data to parent node if no data exists yet + if (!(Flags & parse_no_element_values)) + if (*node->value() == Ch('\0')) + node->value(value, end - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + { + Ch ch = *text; + *end = Ch('\0'); + return ch; // Return character that ends data; this is required because zero terminator overwritten it + } + + // Return character that ends data + return *text; + } + + // Parse CDATA + template + xml_node *parse_cdata(Ch *&text) + { + // If CDATA is disabled + if (Flags & parse_no_data_nodes) + { + // Skip until end of cdata + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 3; // Skip ]]> + return 0; // Do not produce CDATA node + } + + // Skip until end of cdata + Ch *value = text; + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create new cdata node + xml_node *cdata = this->allocate_node(node_cdata); + cdata->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip ]]> + return cdata; + } + + // Parse element node + template + xml_node *parse_element(Ch *&text) + { + // Create element node + xml_node *element = this->allocate_node(node_element); + + // Extract element name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected element name", text); + element->name(name, text - name); + + // Skip whitespace between element name and attributes or > + skip(text); + + // Parse attributes, if any + parse_node_attributes(text, element); + + // Determine ending type + if (*text == Ch('>')) + { + ++text; + parse_node_contents(text, element); + } + else if (*text == Ch('/')) + { + ++text; + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; + } + else + RAPIDXML_PARSE_ERROR("expected >", text); + + // Place zero terminator after name + if (!(Flags & parse_no_string_terminators)) + element->name()[element->name_size()] = Ch('\0'); + + // Return parsed element + return element; + } + + // Determine node type, and parse it + template + xml_node *parse_node(Ch *&text) + { + // Parse proper node type + switch (text[0]) + { + + // <... + default: + // Parse and append element node + return parse_element(text); + + // (text); + } + else + { + // Parse PI + return parse_pi(text); + } + + // (text); + } + break; + + // (text); + } + break; + + // (text); + } + + } // switch + + // Attempt to skip other, unrecognized node types starting with ')) + { + if (*text == 0) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + ++text; // Skip '>' + return 0; // No node recognized + + } + } + + // Parse contents of the node - children, data etc. + template + void parse_node_contents(Ch *&text, xml_node *node) + { + // For all children and text + while (1) + { + // Skip whitespace between > and node contents + Ch *contents_start = text; // Store start of node contents before whitespace is skipped + skip(text); + Ch next_char = *text; + + // After data nodes, instead of continuing the loop, control jumps here. + // This is because zero termination inside parse_and_append_data() function + // would wreak havoc with the above code. + // Also, skipping whitespace after data nodes is unnecessary. + after_data_node: + + // Determine what comes next: node closing, child node, data node, or 0? + switch (next_char) + { + + // Node closing or child node + case Ch('<'): + if (text[1] == Ch('/')) + { + // Node closing + text += 2; // Skip '(text); + if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) + RAPIDXML_PARSE_ERROR("invalid closing tag name", text); + } + else + { + // No validation, just skip name + skip(text); + } + // Skip remaining whitespace after node name + skip(text); + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; // Skip '>' + return; // Node closed, finished parsing contents + } + else + { + // Child node + ++text; // Skip '<' + if (xml_node *child = parse_node(text)) + node->append_node(child); + } + break; + + // End of data - error + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Data node + default: + next_char = parse_and_append_data(node, text, contents_start); + goto after_data_node; // Bypass regular processing after data nodes + + } + } + } + + // Parse XML attributes of the node + template + void parse_node_attributes(Ch *&text, xml_node *node) + { + // For all attributes + while (attribute_name_pred::test(*text)) + { + // Extract attribute name + Ch *name = text; + ++text; // Skip first character of attribute name + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected attribute name", name); + + // Create new attribute + xml_attribute *attribute = this->allocate_attribute(); + attribute->name(name, text - name); + node->append_attribute(attribute); + + // Skip whitespace after attribute name + skip(text); + + // Skip = + if (*text != Ch('=')) + RAPIDXML_PARSE_ERROR("expected =", text); + ++text; + + // Add terminating zero after name + if (!(Flags & parse_no_string_terminators)) + attribute->name()[attribute->name_size()] = 0; + + // Skip whitespace after = + skip(text); + + // Skip quote and remember if it was ' or " + Ch quote = *text; + if (quote != Ch('\'') && quote != Ch('"')) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; + + // Extract attribute value and expand char refs in it + Ch *value = text, *end; + const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes + if (quote == Ch('\'')) + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + else + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + + // Set attribute value + attribute->value(value, end - value); + + // Make sure that end quote is present + if (*text != quote) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; // Skip quote + + // Add terminating zero after value + if (!(Flags & parse_no_string_terminators)) + attribute->value()[attribute->value_size()] = 0; + + // Skip whitespace after attribute value + skip(text); + } + } + + }; + + //! \cond internal + namespace internal + { + + // Whitespace (space \n \r \t) + template + const unsigned char lookup_tables::lookup_whitespace[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F + }; + + // Node name (anything but space \n \r \t / > ? \0) + template + const unsigned char lookup_tables::lookup_node_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) (anything but < \0) + template + const unsigned char lookup_tables::lookup_text[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled + // (anything but < \0 &) + template + const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled + // (anything but < \0 & space \n \r \t) + template + const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute name (anything but space \n \r \t / < > = ? ! \0) + template + const unsigned char lookup_tables::lookup_attribute_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote (anything but ' \0) + template + const unsigned char lookup_tables::lookup_attribute_data_1[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote that does not require processing (anything but ' \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote (anything but " \0) + template + const unsigned char lookup_tables::lookup_attribute_data_2[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote that does not require processing (anything but " \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Digits (dec and hex, 255 denotes end of numeric character reference) + template + const unsigned char lookup_tables::lookup_digits[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F + }; + + // Upper case conversion + template + const unsigned char lookup_tables::lookup_upcase[256] = + { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 + 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127, // 7 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 8 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 9 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // A + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // B + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // C + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // D + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // E + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // F + }; + } + //! \endcond + +} + +// Undefine internal macros +#undef RAPIDXML_PARSE_ERROR + +// On MSVC, restore warnings state +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif diff --git a/libraries/amf/amftools-code/include/rapidxml/rapidxml_print.h b/libraries/amf/amftools-code/include/rapidxml/rapidxml_print.h new file mode 100644 index 00000000..4d428afe --- /dev/null +++ b/libraries/amf/amftools-code/include/rapidxml/rapidxml_print.h @@ -0,0 +1,432 @@ +#ifndef RAPIDXML_PRINT_HPP_INCLUDED +#define RAPIDXML_PRINT_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_print.hpp This file contains rapidxml printer implementation + +#include "rapidxml.h" + +// Only include streams if not disabled +#ifndef RAPIDXML_NO_STREAMS + #include + #include +#endif + +#define NUMINDENT 2 + +namespace rapidxml +{ + + /////////////////////////////////////////////////////////////////////// + // Printing flags + + const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function. + + /////////////////////////////////////////////////////////////////////// + // Internal + + //! \cond internal + namespace internal + { + + /////////////////////////////////////////////////////////////////////////// + // Internal character operations + + // Copy characters from given range to given output iterator + template + inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out) + { + while (begin != end) + *out++ = *begin++; + return out; + } + + // Copy characters from given range to given output iterator and expand + // characters into references (< > ' " &) + template + inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out) + { + while (begin != end) + { + if (*begin == noexpand) + { + *out++ = *begin; // No expansion, copy character + } + else + { + switch (*begin) + { + case Ch('<'): + *out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('>'): + *out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('\''): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';'); + break; + case Ch('"'): + *out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('&'): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';'); + break; + default: + *out++ = *begin; // No expansion, copy character + } + } + ++begin; // Step to next character + } + return out; + } + + // Fill given output iterator with repetitions of the same character + template + inline OutIt fill_chars(OutIt out, int n, Ch ch) + { + for (int i = 0; i < n; ++i) + *out++ = ch; + return out; + } + + // Find character + template + inline bool find_char(const Ch *begin, const Ch *end) + { + while (begin != end) + if (*begin++ == ch) + return true; + return false; + } + + /////////////////////////////////////////////////////////////////////////// + // Internal printing operations + + // Print node + template + inline OutIt print_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print proper node type + switch (node->type()) + { + + // Document + case node_document: + out = print_children(out, node, flags, indent); + break; + + // Element + case node_element: + out = print_element_node(out, node, flags, indent); + break; + + // Data + case node_data: + out = print_data_node(out, node, flags, indent); + break; + + // CDATA + case node_cdata: + out = print_cdata_node(out, node, flags, indent); + break; + + // Declaration + case node_declaration: + out = print_declaration_node(out, node, flags, indent); + break; + + // Comment + case node_comment: + out = print_comment_node(out, node, flags, indent); + break; + + // Doctype + case node_doctype: + out = print_doctype_node(out, node, flags, indent); + break; + + // Pi + case node_pi: + out = print_pi_node(out, node, flags, indent); + break; + + // Unknown + default: + assert(0); + break; + } + + // If indenting not disabled, add line break after node + if (!(flags & print_no_indenting)){ +#ifdef WIN32 + *out = Ch('\r'), ++out; +#endif + *out = Ch('\n'), ++out; + } + + // Return modified iterator + return out; + } + + // Print children of the node + template + inline OutIt print_children(OutIt out, const xml_node *node, int flags, int indent) + { + for (xml_node *child = node->first_node(); child; child = child->next_sibling()) + out = print_node(out, child, flags, indent); + return out; + } + + // Print attributes of the node + template + inline OutIt print_attributes(OutIt out, const xml_node *node, int flags) + { + for (xml_attribute *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + if (attribute->name() && attribute->value()) + { + // Print attribute name + *out = Ch(' '), ++out; + out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out); + *out = Ch('='), ++out; + // Print attribute value using appropriate quote type + if (find_char(attribute->value(), attribute->value() + attribute->value_size())) + { + *out = Ch('\''), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out); + *out = Ch('\''), ++out; + } + else + { + *out = Ch('"'), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out); + *out = Ch('"'), ++out; + } + } + } + return out; + } + + // Print data node + template + inline OutIt print_data_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_data); + if (!(flags & print_no_indenting)) + out = fill_chars(out, NUMINDENT*indent, Ch(' ')); + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + return out; + } + + // Print data node + template + inline OutIt print_cdata_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_cdata); + if (!(flags & print_no_indenting)) + out = fill_chars(out, NUMINDENT*indent, Ch(' ')); + *out = Ch('<'); ++out; + *out = Ch('!'); ++out; + *out = Ch('['); ++out; + *out = Ch('C'); ++out; + *out = Ch('D'); ++out; + *out = Ch('A'); ++out; + *out = Ch('T'); ++out; + *out = Ch('A'); ++out; + *out = Ch('['); ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch(']'); ++out; + *out = Ch(']'); ++out; + *out = Ch('>'); ++out; + return out; + } + + // Print element node + template + inline OutIt print_element_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_element); + + // Print element name and attributes, if any + if (!(flags & print_no_indenting)) + out = fill_chars(out, NUMINDENT*indent, Ch(' ')); + *out = Ch('<'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + out = print_attributes(out, node, flags); + + // If node is childless + if (node->value_size() == 0 && !node->first_node()) + { + // Print childless node tag ending + *out = Ch('/'), ++out; + *out = Ch('>'), ++out; + } + else + { + // Print normal node tag ending + *out = Ch('>'), ++out; + + // Test if node contains a single data node only (and no other nodes) + xml_node *child = node->first_node(); + if (!child) + { + // If node has no children, only print its value without indenting + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + } + else if (child->next_sibling() == 0 && child->type() == node_data) + { + // If node has a sole data child, only print its value without indenting + out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out); + } + else + { + // Print all children with full indenting + if (!(flags & print_no_indenting)){ +#ifdef WIN32 + *out = Ch('\r'), ++out; //for windows only... +#endif + *out = Ch('\n'), ++out; + + } + out = print_children(out, node, flags, indent + 1); + if (!(flags & print_no_indenting)) + out = fill_chars(out, NUMINDENT*indent, Ch(' ')); + } + + // Print node end + *out = Ch('<'), ++out; + *out = Ch('/'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch('>'), ++out; + } + return out; + } + + // Print declaration node + template + inline OutIt print_declaration_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print declaration start + if (!(flags & print_no_indenting)) + out = fill_chars(out, NUMINDENT*indent, Ch(' ')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + *out = Ch('x'), ++out; + *out = Ch('m'), ++out; + *out = Ch('l'), ++out; + + // Print attributes + out = print_attributes(out, node, flags); + + // Print declaration end + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + + return out; + } + + // Print comment node + template + inline OutIt print_comment_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_comment); + if (!(flags & print_no_indenting)) + out = fill_chars(out, NUMINDENT*indent, Ch(' ')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + *out = Ch('>'), ++out; + return out; + } + + // Print doctype node + template + inline OutIt print_doctype_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_doctype); + if (!(flags & print_no_indenting)) + out = fill_chars(out, NUMINDENT*indent, Ch(' ')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('D'), ++out; + *out = Ch('O'), ++out; + *out = Ch('C'), ++out; + *out = Ch('T'), ++out; + *out = Ch('Y'), ++out; + *out = Ch('P'), ++out; + *out = Ch('E'), ++out; + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('>'), ++out; + return out; + } + + // Print pi node + template + inline OutIt print_pi_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_pi); + if (!(flags & print_no_indenting)) + out = fill_chars(out, NUMINDENT*indent, Ch(' ')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + return out; + } + + } + //! \endcond + + /////////////////////////////////////////////////////////////////////////// + // Printing + + //! Prints XML to given output iterator. + //! \param out Output iterator to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output iterator pointing to position immediately after last character of printed text. + template + inline OutIt print(OutIt out, const xml_node &node, int flags = 0) + { + return internal::print_node(out, &node, flags, 0); + } + +#ifndef RAPIDXML_NO_STREAMS + + //! Prints XML to given output stream. + //! \param out Output stream to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output stream. + template + inline std::basic_ostream &print(std::basic_ostream &out, const xml_node &node, int flags = 0) + { + print(std::ostream_iterator(out), node, flags); + return out; + } + + //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process. + //! \param out Output stream to print to. + //! \param node Node to be printed. + //! \return Output stream. + template + inline std::basic_ostream &operator <<(std::basic_ostream &out, const xml_node &node) + { + return print(out, node); + } + +#endif + +} + +#endif diff --git a/libraries/amf/amftools-code/include/stb_image/stb_image.h b/libraries/amf/amftools-code/include/stb_image/stb_image.h new file mode 100644 index 00000000..9df58d60 --- /dev/null +++ b/libraries/amf/amftools-code/include/stb_image/stb_image.h @@ -0,0 +1,332 @@ +/* stbi-1.33 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c + when you control the images you're loading + no warranty implied; use at your own risk + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline (no JPEG progressive) + PNG 8-bit only + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - overridable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) + + Latest revisions: + 1.33 (2011-07-14) minor fixes suggested by Dave Moore + 1.32 (2011-07-13) info support for all filetypes (SpartanJ) + 1.31 (2011-06-19) a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) added ability to load files via io callbacks (Ben Wenger) + 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) cast-to-uint8 to fix warnings (Laurent Gomila) + allow trailing 0s at end of image data (Laurent Gomila) + 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ + + See end of file for full revision history. + + TODO: + stbi_info support for BMP,PSD,HDR,PIC + + + ============================ Contributors ========================= + + Image formats Optimizations & bugfixes + Sean Barrett (jpeg, png, bmp) Fabian "ryg" Giesen + Nicolas Schulz (hdr, psd) + Jonathan Dummer (tga) Bug fixes & warning fixes + Jean-Marc Lienher (gif) Marc LeBlanc + Tom Seddon (pic) Christpher Lloyd + Thatcher Ulrich (psd) Dave Moore + Won Chun + the Horde3D community + Extensions, features Janez Zemva + Jetro Lauha (stbi_info) Jonathan Blow + James "moose2000" Brown (iPhone PNG) Laurent Gomila + Ben "Disch" Wenger (io callbacks) Aruelien Pocheville + Martin "SpartanJ" Golini Ryamond Barbiero + David Woo + + + If your name should be here but isn't, let Sean know. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// To get a header file for this, either cut and paste the header, +// or create stb_image.h, #define STBI_HEADER_FILE_ONLY, and +// then include stb_image.c from it. + +//// begin header file //////////////////////////////////////////////////// +// +// Limitations: +// - no jpeg progressive support +// - non-HDR formats support 8-bit samples only (jpeg, png) +// - no delayed line count (jpeg) -- IJG doesn't support either +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to easily see if it's opaque. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB; nominally they +// would silently load as BGR, except the existing code should have just +// failed on such iPhone PNGs. But you can disable this conversion by +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through. +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). + + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && _MSC_VER >= 0x1400 +#define _CRT_SECURE_NO_WARNINGS // suppress bogus warnings about fopen() +#endif + +#include +#endif + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,unsigned n); // skip the next 'n' bytes + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +extern stbi_uc *stbi_load_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_HDR + extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif + + extern float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + extern void stbi_hdr_to_ldr_gamma(float gamma); + extern void stbi_hdr_to_ldr_scale(float scale); + + extern void stbi_ldr_to_hdr_gamma(float gamma); + extern void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_HDR + +// stbi_is_hdr is always defined +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename); +extern int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +extern const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +extern void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +extern int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +extern void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +extern void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + + +// ZLIB client - used by PNG, available for other purposes + +extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +// define faster low-level operations (typically SIMD support) +#ifdef STBI_SIMD +typedef void (*stbi_idct_8x8)(stbi_uc *out, int out_stride, short data[64], unsigned short *dequantize); +// compute an integer IDCT on "input" +// input[x] = data[x] * dequantize[x] +// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' +// CLAMP results to 0..255 +typedef void (*stbi_YCbCr_to_RGB_run)(stbi_uc *output, stbi_uc const *y, stbi_uc const *cb, stbi_uc const *cr, int count, int step); +// compute a conversion from YCbCr to RGB +// 'count' pixels +// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B +// y: Y input channel +// cb: Cb input channel; scale/biased to be 0..255 +// cr: Cr input channel; scale/biased to be 0..255 + +extern void stbi_install_idct(stbi_idct_8x8 func); +extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); +#endif // STBI_SIMD + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H diff --git a/libraries/amf/amftools-code/include/zip/unzip.h b/libraries/amf/amftools-code/include/zip/unzip.h new file mode 100644 index 00000000..0986ed26 --- /dev/null +++ b/libraries/amf/amftools-code/include/zip/unzip.h @@ -0,0 +1,219 @@ +#ifdef WIN32 + +#ifndef _unzip_H +#define _unzip_H + + +// UNZIPPING functions -- for unzipping. +// This file is a repackaged form of extracts from the zlib code available +// at www.gzip.org/zlib, by Jean-Loup Gailly and Mark Adler. The original +// copyright notice may be found in unzip.cpp. The repackaging was done +// by Lucian Wischik to simplify and extend its use in Windows/C++. Also +// encryption and unicode filenames have been added. + + +#ifndef _zip_H +DECLARE_HANDLE(HZIP); +#endif +// An HZIP identifies a zip file that has been opened + +typedef DWORD ZRESULT; +// return codes from any of the zip functions. Listed later. + +typedef struct +{ int index; // index of this file within the zip + TCHAR name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; + + +HZIP OpenZip(const TCHAR *fn, const char *password); +HZIP OpenZip(void *z,unsigned int len, const char *password); +HZIP OpenZipHandle(HANDLE h, const char *password); +// OpenZip - opens a zip file and returns a handle with which you can +// subsequently examine its contents. You can open a zip file from: +// from a pipe: OpenZipHandle(hpipe_read,0); +// from a file (by handle): OpenZipHandle(hfile,0); +// from a file (by name): OpenZip("c:\\test.zip","password"); +// from a memory block: OpenZip(bufstart, buflen,0); +// If the file is opened through a pipe, then items may only be +// accessed in increasing order, and an item may only be unzipped once, +// although GetZipItem can be called immediately before and after unzipping +// it. If it's opened in any other way, then full random access is possible. +// Note: pipe input is not yet implemented. +// Note: zip passwords are ascii, not unicode. +// Note: for windows-ce, you cannot close the handle until after CloseZip. +// but for real windows, the zip makes its own copy of your handle, so you +// can close yours anytime. + +ZRESULT GetZipItem(HZIP hz, int index, ZIPENTRY *ze); +// GetZipItem - call this to get information about an item in the zip. +// If index is -1 and the file wasn't opened through a pipe, +// then it returns information about the whole zipfile +// (and in particular ze.index returns the number of index items). +// Note: the item might be a directory (ze.attr & FILE_ATTRIBUTE_DIRECTORY) +// See below for notes on what happens when you unzip such an item. +// Note: if you are opening the zip through a pipe, then random access +// is not possible and GetZipItem(-1) fails and you can't discover the number +// of items except by calling GetZipItem on each one of them in turn, +// starting at 0, until eventually the call fails. Also, in the event that +// you are opening through a pipe and the zip was itself created into a pipe, +// then then comp_size and sometimes unc_size as well may not be known until +// after the item has been unzipped. + +ZRESULT FindZipItem(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze); +// FindZipItem - finds an item by name. ic means 'insensitive to case'. +// It returns the index of the item, and returns information about it. +// If nothing was found, then index is set to -1 and the function returns +// an error code. + +ZRESULT UnzipItem(HZIP hz, int index, const TCHAR *fn); +ZRESULT UnzipItem(HZIP hz, int index, void *z,unsigned int len); +ZRESULT UnzipItemHandle(HZIP hz, int index, HANDLE h); +// UnzipItem - given an index to an item, unzips it. You can unzip to: +// to a pipe: UnzipItemHandle(hz,i, hpipe_write); +// to a file (by handle): UnzipItemHandle(hz,i, hfile); +// to a file (by name): UnzipItem(hz,i, ze.name); +// to a memory block: UnzipItem(hz,i, buf,buflen); +// In the final case, if the buffer isn't large enough to hold it all, +// then the return code indicates that more is yet to come. If it was +// large enough, and you want to know precisely how big, GetZipItem. +// Note: zip files are normally stored with relative pathnames. If you +// unzip with ZIP_FILENAME a relative pathname then the item gets created +// relative to the current directory - it first ensures that all necessary +// subdirectories have been created. Also, the item may itself be a directory. +// If you unzip a directory with ZIP_FILENAME, then the directory gets created. +// If you unzip it to a handle or a memory block, then nothing gets created +// and it emits 0 bytes. +ZRESULT SetUnzipBaseDir(HZIP hz, const TCHAR *dir); +// if unzipping to a filename, and it's a relative filename, then it will be relative to here. +// (defaults to current-directory). + + +ZRESULT CloseZip(HZIP hz); +// CloseZip - the zip handle must be closed with this function. + +unsigned int FormatZipMessage(ZRESULT code, TCHAR *buf,unsigned int len); +// FormatZipMessage - given an error code, formats it as a string. +// It returns the length of the error message. If buf/len points +// to a real buffer, then it also writes as much as possible into there. + + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +#define ZR_PASSWORD 0x00001000 // we didn't get the right password to unzip the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + + + + + +// e.g. +// +// SetCurrentDirectory("c:\\docs\\stuff"); +// HZIP hz = OpenZip("c:\\stuff.zip",0); +// ZIPENTRY ze; GetZipItem(hz,-1,&ze); int numitems=ze.index; +// for (int i=0; i +// ZIP functions -- for creating zip files +// This file is a repackaged form of the Info-Zip source code available +// at www.info-zip.org. The original copyright notice may be found in +// zip.cpp. The repackaging was done by Lucian Wischik to simplify and +// extend its use in Windows/C++. Also to add encryption and unicode. + + +#ifndef _unzip_H +DECLARE_HANDLE(HZIP); +#endif +// An HZIP identifies a zip file that is being created + +typedef DWORD ZRESULT; +// return codes from any of the zip functions. Listed later. + + + +HZIP CreateZip(const TCHAR *fn, const char *password); +HZIP CreateZip(void *buf,unsigned int len, const char *password); +HZIP CreateZipHandle(HANDLE h, const char *password); +// CreateZip - call this to start the creation of a zip file. +// As the zip is being created, it will be stored somewhere: +// to a pipe: CreateZipHandle(hpipe_write); +// in a file (by handle): CreateZipHandle(hfile); +// in a file (by name): CreateZip("c:\\test.zip"); +// in memory: CreateZip(buf, len); +// or in pagefile memory: CreateZip(0, len); +// The final case stores it in memory backed by the system paging file, +// where the zip may not exceed len bytes. This is a bit friendlier than +// allocating memory with new[]: it won't lead to fragmentation, and the +// memory won't be touched unless needed. That means you can give very +// large estimates of the maximum-size without too much worry. +// As for the password, it lets you encrypt every file in the archive. +// (This api doesn't support per-file encryption.) +// Note: because pipes don't allow random access, the structure of a zipfile +// created into a pipe is slightly different from that created into a file +// or memory. In particular, the compressed-size of the item cannot be +// stored in the zipfile until after the item itself. (Also, for an item added +// itself via a pipe, the uncompressed-size might not either be known until +// after.) This is not normally a problem. But if you try to unzip via a pipe +// as well, then the unzipper will not know these things about the item until +// after it has been unzipped. Therefore: for unzippers which don't just write +// each item to disk or to a pipe, but instead pre-allocate memory space into +// which to unzip them, then either you have to create the zip not to a pipe, +// or you have to add items not from a pipe, or at least when adding items +// from a pipe you have to specify the length. +// Note: for windows-ce, you cannot close the handle until after CloseZip. +// but for real windows, the zip makes its own copy of your handle, so you +// can close yours anytime. + + +ZRESULT ZipAdd(HZIP hz,const TCHAR *dstzn, const TCHAR *fn); +ZRESULT ZipAdd(HZIP hz,const TCHAR *dstzn, void *src,unsigned int len); +ZRESULT ZipAddHandle(HZIP hz,const TCHAR *dstzn, HANDLE h); +ZRESULT ZipAddHandle(HZIP hz,const TCHAR *dstzn, HANDLE h, unsigned int len); +ZRESULT ZipAddFolder(HZIP hz,const TCHAR *dstzn); +// ZipAdd - call this for each file to be added to the zip. +// dstzn is the name that the file will be stored as in the zip file. +// The file to be added to the zip can come +// from a pipe: ZipAddHandle(hz,"file.dat", hpipe_read); +// from a file: ZipAddHandle(hz,"file.dat", hfile); +// from a filen: ZipAdd(hz,"file.dat", "c:\\docs\\origfile.dat"); +// from memory: ZipAdd(hz,"subdir\\file.dat", buf,len); +// (folder): ZipAddFolder(hz,"subdir"); +// Note: if adding an item from a pipe, and if also creating the zip file itself +// to a pipe, then you might wish to pass a non-zero length to the ZipAddHandle +// function. This will let the zipfile store the item's size ahead of the +// compressed item itself, which in turn makes it easier when unzipping the +// zipfile from a pipe. + +ZRESULT ZipGetMemory(HZIP hz, void **buf, unsigned long *len); +// ZipGetMemory - If the zip was created in memory, via ZipCreate(0,len), +// then this function will return information about that memory block. +// buf will receive a pointer to its start, and len its length. +// Note: you can't add any more after calling this. + +ZRESULT CloseZip(HZIP hz); +// CloseZip - the zip handle must be closed with this function. + +unsigned int FormatZipMessage(ZRESULT code, TCHAR *buf,unsigned int len); +// FormatZipMessage - given an error code, formats it as a string. +// It returns the length of the error message. If buf/len points +// to a real buffer, then it also writes as much as possible into there. + + + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + + + + + + +// e.g. +// +// (1) Traditional use, creating a zipfile from existing files +// HZIP hz = CreateZip("c:\\simple1.zip",0); +// ZipAdd(hz,"znsimple.bmp", "c:\\simple.bmp"); +// ZipAdd(hz,"znsimple.txt", "c:\\simple.txt"); +// CloseZip(hz); +// +// (2) Memory use, creating an auto-allocated mem-based zip file from various sources +// HZIP hz = CreateZip(0,100000, 0); +// // adding a conventional file... +// ZipAdd(hz,"src1.txt", "c:\\src1.txt"); +// // adding something from memory... +// char buf[1000]; for (int i=0; i<1000; i++) buf[i]=(char)(i&0x7F); +// ZipAdd(hz,"file.dat", buf,1000); +// // adding something from a pipe... +// HANDLE hread,hwrite; CreatePipe(&hread,&hwrite,NULL,0); +// HANDLE hthread = CreateThread(0,0,ThreadFunc,(void*)hwrite,0,0); +// ZipAdd(hz,"unz3.dat", hread,1000); // the '1000' is optional. +// WaitForSingleObject(hthread,INFINITE); +// CloseHandle(hthread); CloseHandle(hread); +// ... meanwhile DWORD WINAPI ThreadFunc(void *dat) +// { HANDLE hwrite = (HANDLE)dat; +// char buf[1000]={17}; +// DWORD writ; WriteFile(hwrite,buf,1000,&writ,NULL); +// CloseHandle(hwrite); +// return 0; +// } +// // and now that the zip is created, let's do something with it: +// void *zbuf; unsigned long zlen; ZipGetMemory(hz,&zbuf,&zlen); +// HANDLE hfz = CreateFile("test2.zip",GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0); +// DWORD writ; WriteFile(hfz,zbuf,zlen,&writ,NULL); +// CloseHandle(hfz); +// CloseZip(hz); +// +// (3) Handle use, for file handles and pipes +// HANDLE hzread,hzwrite; CreatePipe(&hzread,&hzwrite,0,0); +// HANDLE hthread = CreateThread(0,0,ZipReceiverThread,(void*)hzread,0,0); +// HZIP hz = CreateZipHandle(hzwrite,0); +// // ... add to it +// CloseZip(hz); +// CloseHandle(hzwrite); +// WaitForSingleObject(hthread,INFINITE); +// CloseHandle(hthread); +// ... meanwhile DWORD WINAPI ZipReceiverThread(void *dat) +// { HANDLE hread = (HANDLE)dat; +// char buf[1000]; +// while (true) +// { DWORD red; ReadFile(hread,buf,1000,&red,NULL); +// // ... and do something with this zip data we're receiving +// if (red==0) break; +// } +// CloseHandle(hread); +// return 0; +// } + + + +// Now we indulge in a little skullduggery so that the code works whether +// the user has included just zip or both zip and unzip. +// Idea: if header files for both zip and unzip are present, then presumably +// the cpp files for zip and unzip are both present, so we will call +// one or the other of them based on a dynamic choice. If the header file +// for only one is present, then we will bind to that particular one. +ZRESULT CloseZipZ(HZIP hz); +unsigned int FormatZipMessageZ(ZRESULT code, char *buf,unsigned int len); +bool IsZipHandleZ(HZIP hz); +#ifdef _unzip_H +#undef CloseZip +#define CloseZip(hz) (IsZipHandleZ(hz)?CloseZipZ(hz):CloseZipU(hz)) +#else +#define CloseZip CloseZipZ +#define FormatZipMessage FormatZipMessageZ +#endif + + + +#endif + +#endif //WIN32 diff --git a/libraries/amf/amftools-code/src/AMF_File.cpp b/libraries/amf/amftools-code/src/AMF_File.cpp new file mode 100644 index 00000000..a319c653 --- /dev/null +++ b/libraries/amf/amftools-code/src/AMF_File.cpp @@ -0,0 +1,2251 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + + +#include "AMF_File.h" +#include "XmlStream.h" +#include "nTexture.h" + + + +//for sort, etc. +#include + +AmfFile::AmfFile(void)// : Slicer(this) +{ + ClearAll(); +} + +AmfFile::~AmfFile(void) +{ + //if (pPixelDataRGBA) delete [] pPixelDataRGBA; + //if (pPixelDataIndex) delete [] pPixelDataIndex; +} + +AmfFile::AmfFile(const AmfFile& In)// : Slicer(this) +{ + *this = In; +} + +AmfFile& AmfFile::operator=(const AmfFile& In) +{ + RenderedObjs = In.RenderedObjs; + CancelIO = In.CancelIO; + CurTick = In.CurTick; + MaxTick = In.MaxTick; + CurrentMessage = In.CurrentMessage; + + MinX = In.MinX; + MaxX = In.MaxX; + MinY = In.MinY; + MaxY = In.MaxY; + MinZ = In.MinZ; + MaxZ = In.MaxZ; + + StlFile = In.StlFile; + X3dFile = In.X3dFile; + + NeedRender = In.NeedRender; + SubDivLevel = In.SubDivLevel; + CurColorView = In.CurColorView; + CurViewMode = In.CurViewMode; + CurSlice = In.CurSlice; + + CurImportUnits = In.CurImportUnits; + + LastError = In.LastError; + + return *this; +} + +//************************************************************************ +//Amf I/O +//************************************************************************ + +bool AmfFile::Save(std::string AmfFilePath, bool Compressed) +{ + CancelIO = false; + ClearError(); + CurTick = 0; + MaxTick = 2; + CXmlStreamWrite XmlWrite; + std::string LocalError; + + CurrentMessage = "Beginning Xml file..."; + if (!XmlWrite.BeginXmlFile(AmfFilePath, Compressed)){LastError = XmlWrite.LastError; return false;} + if (CancelIO) return false; + + CurrentMessage = "Writing Xml..."; + if (!WriteXML(&XmlWrite, &LocalError, &CancelIO)){LastError = LocalError; return false;} + if (CancelIO) return false; + + CurTick = 1; + CurrentMessage = "Compressing and saving AMF..."; + if (!XmlWrite.EndXmlFile()){LastError = XmlWrite.LastError; return false;} + + return true; +} + +bool AmfFile::Load(std::string AmfFilePath, bool StrictLoad) +{ + Clear(); + ClearError(); + + CancelIO = false; +// ClearError(); + CurTick = 0; + MaxTick = 0; + CXmlStreamRead XmlRead; + std::string LocalError; + + + CurrentMessage = "Uncompressing/Parsing Xml...\n"; + if (!XmlRead.LoadFile(AmfFilePath)){LastError = XmlRead.LastError; return false;} + + CurrentMessage = "Reading Xml...\n"; + if (!ReadXML(&XmlRead, StrictLoad, &LocalError, &CancelIO)) {Clear(); LastError = LocalError; return false;} + LastError = LocalError; + + NeedRender = true; + return true; //CheckValid(StrictLoad, &LocalError); +} + +bool AmfFile::ImportAmf(std::string AmfFilePath, bool StrictLoad) //merges this AMF with the current AMF +{ + ClearError(); + + //TODO: deal with Color: scaling, AmfPointers within color equations. + + //load the file... + AmfFile AmfMerge; + if (!AmfMerge.Load(AmfFilePath, StrictLoad)) return false; + double ScaleFactor = ToCurrentUnits(1.0, AmfMerge.GetUnits()); //ImprtAmf*ScaleFactor = This Amf + + //Textures + std::vector PrevTexIDs, NewTexIDs; + for (std::vector::iterator it = AmfMerge.Textures.begin(); it != AmfMerge.Textures.end(); it++){ + int NewID = GetUnusedTexID(); + PrevTexIDs.push_back(it->aID); + NewTexIDs.push_back(NewID); + it->pnAmf = this; + it->aID = NewID; + Textures.push_back(*it); + } + + //Materials + int NumPrevMats = Materials.size(); + std::vector PrevMatIDs, NewMatIDs; + //set newIDs + for (std::vector::iterator it = AmfMerge.Materials.begin()+1; it != AmfMerge.Materials.end(); it++){ //+1 to avoid the obligatory void material + int NewID = GetUnusedMatID(); + PrevMatIDs.push_back(it->aID); + NewMatIDs.push_back(NewID); + it->aID = NewID; + it->pnAmf = this; + it->SetName("Imported-" + it->GetName()); + Materials.push_back(*it); + } + //set composite material ID references + for (std::vector::iterator it = Materials.begin()+NumPrevMats; it != Materials.end(); it++){ + for (std::vector::iterator jt = it->Composites.begin(); jt != it->Composites.end(); jt++){ + for (int i=0; i<(int)PrevMatIDs.size(); i++){ + if (jt->aMaterialID == PrevMatIDs[i]){jt->aMaterialID = NewMatIDs[i]; break;} + } + //A composite that has a reference to a nonexistant material ID should have been caught on load, so not handled here + + if (!jt->MatEquation.IsConst()){ + jt->ScaleEquation(ScaleFactor); + jt->MatEquation.pAmf = this; + } + } + } + + //Objects and Constellations + std::vector PrevGeoIDs, NewGeoIDs; //order: all objects, then all constellations + for (std::vector::iterator it = AmfMerge.Objects.begin(); it != AmfMerge.Objects.end(); it++){ + int NewID = GetUnusedGeoID(); //keep track of IDs + PrevGeoIDs.push_back(it->aID); + NewGeoIDs.push_back(NewID); + it->pnAmf = this; + it->SetName("Imported-" + it->GetName()); + it->aID=NewID; //set this new ID + + for (std::vector::iterator jt = it->Meshes.begin(); jt != it->Meshes.end(); jt++){ + for (std::vector::iterator kt = jt->Volumes.begin(); kt != jt->Volumes.end(); kt++){ + //materialID of nVolumes + for (int i=0; i<(int)PrevMatIDs.size(); i++){ + if (kt->aMaterialID == PrevMatIDs[i]){kt->aMaterialID = NewMatIDs[i]; break;} + } + + int NumPrevTexIDs = (int)PrevTexIDs.size(); + for (std::vector::iterator lt = kt->Triangles.begin(); lt != kt->Triangles.end(); lt++){ + if (lt->TexMapExists){ + //texID of nTexMap + for (int i=0; iTexMap.RTexID == PrevTexIDs[i]){lt->TexMap.RTexID = NewTexIDs[i];} + if (lt->TexMap.GTexID == PrevTexIDs[i]){lt->TexMap.GTexID = NewTexIDs[i];} + if (lt->TexMap.BTexID == PrevTexIDs[i]){lt->TexMap.BTexID = NewTexIDs[i];} + if (lt->TexMap.ATexIDExists && lt->TexMap.ATexID == PrevTexIDs[i]){lt->TexMap.ATexID = NewTexIDs[i];} + } + } + } + } + + + if (ScaleFactor != 1.0){ + //update vertex positions based on relative units + for (std::vector::iterator kt = jt->Vertices.VertexList.begin(); kt != jt->Vertices.VertexList.end(); kt++){ + kt->Coordinates.X *= ScaleFactor; + kt->Coordinates.Y *= ScaleFactor; + kt->Coordinates.Z *= ScaleFactor; + } + } + } + + Objects.push_back(*it); + } + + + //set newIDs + int NumPrevConsts = Constellations.size(); + for (std::vector::iterator it = AmfMerge.Constellations.begin(); it != AmfMerge.Constellations.end(); it++){ + int NewID = GetUnusedGeoID(); + PrevMatIDs.push_back(it->aID); + NewMatIDs.push_back(NewID); + it->aID = NewID; + it->SetName("Imported-" + it->GetName()); + it->pnAmf = this; + Constellations.push_back(*it); + } + //set instance geometry ID references + for (std::vector::iterator it = Constellations.begin()+NumPrevConsts; it != Constellations.end(); it++){ + for (std::vector::iterator jt = it->Instances.begin(); jt != it->Instances.end(); jt++){ + for (int i=0; i<(int)PrevGeoIDs.size(); i++){ + if (jt->aObjectID == PrevGeoIDs[i]){jt->aObjectID = NewGeoIDs[i]; break;} + } + //An instance that has a reference to a nonexistant geometry ID should have been caught on load, so not handled here + } + } + + //ignore all metadata from file that's merged in. + NeedRender = true; + + return true; +} + + +//behaviors of check valid: +//for load: +//errors would cause errors, not solved by assuming a defualt value, or things explicitely denied in the spec +//warnings would be a source of ambiguity or senslessness, but won't crash anything. + + +//checkvalid has two parameters. +//FixNode: one for whether to modify anything +///IgnoreWarning: output warnings or not. + + +//1) halt on error, show all warnings but don't change anything +//2) try to fix the error, halt if can't do anything to fix, warning if changes made +//3) option to suppress warning (shouldn't hve to propegate) + + + + +//move down! +//bool AmfFile::CheckValid(bool Strict, std::string* pMessage) +//{ +// return true; +//} + + +void AmfFile::ClearAll() +{ + Clear(); //Clears underlying AMF data + CurTick = 0; + MaxTick = 0; + CurrentMessage = ""; + CancelIO = false; + NeedRender = true; + MinX = MaxX = MinY = MaxY = MinZ = MaxZ = 0; + CurImportUnits = UNIT_MM; + CurColorView = CV_ALL; + CurViewMode = VM_SOLID; + SubDivLevel = 4; + ClearError(); +} + +//************************************************************************ +//importing meshes +//************************************************************************ + +bool AmfFile::ImportMesh(std::string MeshFilePath, int AmfObjectIndex, int AmfMeshIndex) //imports a mesh into a mesh node specified (stl or x3d only) +{ + ClearError(); + + std::string FileExtension = MeshFilePath.substr(MeshFilePath.size()-4, 3); + if (FileExtension == "STL" || FileExtension == "stl" || FileExtension == "Stl"){ + if (!LoadStl(MeshFilePath)) return false; + return ImportStl(AmfObjectIndex, AmfMeshIndex); + } + else if (FileExtension == "X3D" || FileExtension == "x3d" || FileExtension == "X3d"){ + if (!LoadX3d(MeshFilePath, "", NULL)) return false; + return ImportX3d(AmfObjectIndex, AmfMeshIndex); + } + else { + LastError += "Error: Unrecognized mesh file type. Currently only stl and x3d are supported. Aborting.\n"; + return false; + } + NeedRender = true; + +} + +bool AmfFile::LoadStl(std::string StlFilePath) +{ + ClearError(); + + //load the stl file into memory + if (!StlFile.Load(StlFilePath)){ + LastError += "Error: Could not load stl file.\nPlease check the file path and integrity of the stl file. Aborting.\n"; + return false; + } + return true; +} + +bool AmfFile::GetStlMeshSize(double* XSize, double* YSize, double* ZSize) +{ + ClearError(); + + if (!StlFile.IsLoaded){ + LastError += "Error: No Stl file has been loaded yet to read dimensions from. Aborting.\n"; + return false; + } + Vec3D Size = StlFile.GetSize(); + *XSize = Size.x; + *YSize = Size.y; + *ZSize = Size.z; + + return true; +} + + +bool AmfFile::ImportStl(int AmfObjectIndex, int AmfMeshIndex) +{ + ClearError(); + + if (!StlFile.IsLoaded){ + LastError += "Error: No Stl file has been loaded yet to import into the amf. Aborting.\n"; + return false; + } + + //make a location to add the stl mesh to in the amf + int PrevNumObj = GetNumObjects(); + nMesh* pnMesh = GetMesh(AmfObjectIndex, AmfMeshIndex, true); + if (!pnMesh){ + LastError += "Error: Could not determine AMF mesh to load STL into.\nPlease check Object and Mesh indices. Aborting.\n"; + return false; + } + + //catch if we added an object and rename it to the mesh we're importing! + if (PrevNumObj != GetNumObjects()) Objects.back().SetName(StlFile.ObjectName); + + int NumPrevVert = pnMesh->GetNumVertices(); + double UnitsScaleFactor = GetImportScaleFactor(); + //Clear(); + + //figure out a reasonable weld distance + Vec3D p1, p2; + StlFile.ComputeBoundingBox(p2, p1); + p1 -= p2; + aWeldVertex::WeldThresh = UnitsScaleFactor*(std::min(std::min(p1.x, p1.y), p1.z))/100000.0; //weld threshold will always ne very small compared to the stl + if (aWeldVertex::WeldThresh == 0) aWeldVertex::WeldThresh = DBL_MIN; //deal with numerical issues with very small objects (very unlikely...) + + CurrentMessage = "Sorting vertices..."; + //put vertices in a temporary container... + std::vector tmp; + std::vector OrigToWeldIndex; //maps original vertex index to new welded vertex index + std::vector Vertices; + + tmp.reserve(3*StlFile.Facets.size()); + OrigToWeldIndex.reserve(3*StlFile.Facets.size()); + for (int i=0; i<(int)StlFile.Facets.size(); i++){ //iterate through all the facets in the STL file + for (int j=0; j<3; j++){ //each point in this facet + tmp.push_back(aWeldVertex(UnitsScaleFactor*StlFile.Facets[i].v[j], 3*i+j)); + OrigToWeldIndex.push_back(3*i+j); + + } + } + std::sort(tmp.begin(), tmp.end(), aWeldVertex::IsSoftLessThan); + + CurrentMessage = "Welding coincident vertices..."; + //put non-duplicates in the actual vertices vector (and keep track of them) + Vertices.reserve(3*StlFile.Facets.size()); + int RevIt = 0; //reverse iterator + int NumUniqueVerts = 0; + for (int i=0; i<(int)tmp.size(); i++){ //iterate through all the facets in the STL file + RevIt = 1; + bool FoundOne = false; //have we found an identical vertex? + while(NumUniqueVerts-RevIt >= 0 && Vertices[NumUniqueVerts-RevIt].z > tmp[i].v.z - aWeldVertex::WeldThresh){ + if (tmp[i].v.IsNear(Vertices[NumUniqueVerts - RevIt], aWeldVertex::WeldThresh)){ //if its NOT near by one already in the vertices array, go add it and move on + FoundOne = true; + OrigToWeldIndex[i] = NumUniqueVerts - RevIt; + RevIt++; + + break; + } + RevIt++; + } + if (!FoundOne){ + Vertices.push_back(tmp[i].v); + OrigToWeldIndex[i] = NumUniqueVerts; + NumUniqueVerts++; + } + } + + CurrentMessage = "Creating AMF mesh: Adding vertices..."; + + pnMesh->Vertices.VertexList.reserve(pnMesh->Vertices.VertexList.size() + Vertices.size()); //save re-allocation as we add vertices... + nVertex TmpVertex; + for (std::vector::iterator it = Vertices.begin(); it != Vertices.end(); it++){ + TmpVertex = nVertex(it->x, it->y, it->z); //linux compat + pnMesh->AddVertex(TmpVertex); + } + + CurrentMessage = "Creating AMF mesh: Resizing mesh facets..."; + + //fill in triangles + nVolume* pNewVolume = pnMesh->NewVolume(StlFile.ObjectName); + pNewVolume->Triangles.resize(StlFile.Facets.size()); //reserve space for all the triangles (saves re-allocating vector) + + CurrentMessage = "Creating AMF mesh: Adding facets..."; + int NumTriangles = (int)StlFile.Facets.size(); + int ThisVNum; + for (int i=0; i<3*NumTriangles; i++){ + ThisVNum = tmp[i].OrigIndex%3; + switch (ThisVNum){ + case 0: pNewVolume->Triangles[tmp[i].OrigIndex/3].v1 = NumPrevVert+OrigToWeldIndex[i]; break; + case 1: pNewVolume->Triangles[tmp[i].OrigIndex/3].v2 = NumPrevVert+OrigToWeldIndex[i]; break; + case 2: pNewVolume->Triangles[tmp[i].OrigIndex/3].v3 = NumPrevVert+OrigToWeldIndex[i]; break; + + } + } + + //assign a material where one exists: + if ((int)Materials.size() > 1) pNewVolume->SetMaterialID(Materials[1].aID); //if there's a material besides the void material... + + NeedRender = true; + return true; +} + + +bool AmfFile::LoadX3d(std::string X3dFilePath, std::string ImagePath, std::string* ImgPathErrorReturn) //imports an x3d into a mesh node specified +{ + ClearError(); + + //load the x3d file into memory + X3dLoadResult X3dLoad = X3dFile.Load(X3dFilePath, ImagePath); + switch (X3dLoad){ + case XLR_BADFILEPATH: + if (ImgPathErrorReturn) *ImgPathErrorReturn = ""; + LastError += "Error: Could not load x3d at the file path. Aborting.\n"; + return false; + case XLR_NOSHAPE: + if (ImgPathErrorReturn) *ImgPathErrorReturn = ""; + LastError += "Error: No valid shape data found in the x3d file. Only indexed face sets are supported. Aborting.\n"; + return false; + case XLR_BADIMAGEPATH: + if (ImgPathErrorReturn) *ImgPathErrorReturn = X3dFile.ImagePath; + LastError += "Error: Image texture not loaded succesfully. Check the validity of the path. Aborting.\n"; + return false; + default: + if (ImgPathErrorReturn) *ImgPathErrorReturn = ""; + return true; + } + + +} + +bool AmfFile::GetX3dMeshSize(double* XSize, double* YSize, double* ZSize) +{ + ClearError(); + + if (!X3dFile.IsLoaded){ + LastError += "Error: No Stl file has been loaded yet to read dimensions from. Aborting.\n"; + return false; + } + X3dFile.GetSize(*XSize, *YSize, *ZSize);; + + return true; +} + +bool AmfFile::ImportX3d(int AmfObjectIndex, int AmfMeshIndex) //imports an x3d into a mesh node specified +{ + ClearError(); + + if (!X3dFile.IsLoaded){ + LastError += "Error: No X3d file has been loaded yet to import into the amf. Aborting.\n"; + return false; + } + + //make a location to add the x3d to in the amf + int PrevNumObj = GetNumObjects(); + nMesh* pnMesh = GetMesh(AmfObjectIndex, AmfMeshIndex, true); + if (!pnMesh){ + LastError += "Error: Could not determine AMF mesh to load X3d into.\nPlease check Object and Mesh indices. Aborting.\n"; + return false; + } + + //catch if we added an object and rename it to the mesh we're importing! + if (PrevNumObj != GetNumObjects()){ + std::string NewObjName = X3dFile.filePath; + int Start = NewObjName.find_last_of("\\/"); + if (Start != std::string::npos) NewObjName = NewObjName.substr(Start + 1, NewObjName.size() - Start - 1); + int End = NewObjName.find_last_of("."); + if (End != std::string::npos) NewObjName = NewObjName.substr(0, End); + + Objects.back().SetName(NewObjName); + } + + + double UnitsScaleFactor = GetImportScaleFactor(); + int RIndex, GIndex, BIndex, AIndex; //AMF texture ID's, to be set as we add textures + + std::vector pVolumes; //a vector of length of number of x3d shapes containing the pointer to the AMF volume they should be loaded in to. + std::vectorCoordsBeginIndex; //a vector of length of number of x3d shapes containing which vertex index the local vertex indices should start at + X3dFillImportInfo(pnMesh, &pVolumes, &CoordsBeginIndex); + + //add each "shape" object of the x3d file as a separate volume within this mesh... + int ShapeCount = 0; + for (std::vector::iterator iS = X3dFile.xShapes.begin(); iS != X3dFile.xShapes.end(); iS++){ + CurrentMessage = "Creating AMF mesh: Adding vertices..."; + + //we've only implemented IndexedFaceSets so far... + if (!iS->IsIndexedFaceSet()) break; + xIndexedFaceSetNode* pFaceSet = &iS->xIndexedFaceSet; + nVolume* pCurVolume = pVolumes[ShapeCount]; + + //flags for if there is face or vertex color info + bool TexturePresent = (pFaceSet->HasTexture && iS->xAppearance.ImageTexture.Width() != 0); //do we have texture mapping data? + + //load in texture data (should only ever be one per x3d shape) + if (TexturePresent){ + bool Tiled = iS->xAppearance.repeatS && iS->xAppearance.repeatT; //if either direction is tiled, then we tile the texture + RIndex = AddTexture(&iS->xAppearance.ImageTexture, CR, Tiled); + GIndex = AddTexture(&iS->xAppearance.ImageTexture, CG, Tiled); + BIndex = AddTexture(&iS->xAppearance.ImageTexture, CB, Tiled); + if (iS->xAppearance.ImageTexture.HasAlphaChannel()) + AIndex = AddTexture(&iS->xAppearance.ImageTexture, CA, Tiled); + } + + //Add vertices to mesh + int NumVerts = pFaceSet->GetNumCoords(); + pnMesh->Vertices.VertexList.reserve(pnMesh->Vertices.VertexList.size() + NumVerts); //save re-allocation as we add vertices... + + nVertex TmpVertex; + for (int i=0; iColors && pFaceSet->colorPerVertex){ + if (pFaceSet->HasAlpha){ + TmpVertex = nVertex(pFaceSet->GetCoord(i, AX_X)*UnitsScaleFactor, pFaceSet->GetCoord(i, AX_Y)*UnitsScaleFactor, pFaceSet->GetCoord(i, AX_Z)*UnitsScaleFactor, nColor(pFaceSet->GetColorVert(i, CC_R), pFaceSet->GetColorVert(i, CC_G), pFaceSet->GetColorVert(i, CC_B),pFaceSet->GetColorVert(i, CC_A))); + pnMesh->AddVertex(TmpVertex); + } + else { + TmpVertex = nVertex(pFaceSet->GetCoord(i, AX_X)*UnitsScaleFactor, pFaceSet->GetCoord(i, AX_Y)*UnitsScaleFactor, pFaceSet->GetCoord(i, AX_Z)*UnitsScaleFactor, nColor(pFaceSet->GetColorVert(i, CC_R), pFaceSet->GetColorVert(i, CC_G), pFaceSet->GetColorVert(i, CC_B))); + pnMesh->AddVertex(TmpVertex); + } + + } + else { + TmpVertex = nVertex(pFaceSet->GetCoord(i, AX_X)*UnitsScaleFactor, pFaceSet->GetCoord(i, AX_Y)*UnitsScaleFactor, pFaceSet->GetCoord(i, AX_Z)*UnitsScaleFactor); + pnMesh->AddVertex(TmpVertex); + } + } + + + CurrentMessage = "Creating AMF mesh: Adding facets..."; + //add material color + double tr = iS->xAppearance.MatColorRed; + double tg = iS->xAppearance.MatColorGreen; + double tb = iS->xAppearance.MatColorBlue; + nColor tmp = nColor(tr, tg, tb); + if (tr != -1 && tg != -1 && tb != -1) pCurVolume->SetColor(tmp); + + //Add the triangles + nTriangle *CurTri; + int NumTri = pFaceSet->GetNumTriangles(); + int NumPrevVert = CoordsBeginIndex[ShapeCount]; //where to start with the triangles we'll be adding + nTriangle TmpTri; + nTexmap TmpTexMap; + for (int i=0; iColors && !pFaceSet->colorPerVertex){ + if (pFaceSet->HasAlpha) ThisColor = nColor(pFaceSet->GetColorFace(i, CC_R), pFaceSet->GetColorFace(i, CC_G), pFaceSet->GetColorFace(i, CC_B), pFaceSet->GetColorFace(i, CC_A)); + else ThisColor = nColor(pFaceSet->GetColorFace(i, CC_R), pFaceSet->GetColorFace(i, CC_G), pFaceSet->GetColorFace(i, CC_B)); + + TmpTri = nTriangle(pFaceSet->GetCoordInd(i, 0)+NumPrevVert, pFaceSet->GetCoordInd(i, 1)+NumPrevVert, pFaceSet->GetCoordInd(i, 2)+NumPrevVert, ThisColor); + CurTri = pCurVolume->AddTriangle(TmpTri); + } + else { + TmpTri = nTriangle(pFaceSet->GetCoordInd(i, 0)+NumPrevVert, pFaceSet->GetCoordInd(i, 1)+NumPrevVert, pFaceSet->GetCoordInd(i, 2)+NumPrevVert); + CurTri = pCurVolume->AddTriangle(TmpTri); + } + + if (TexturePresent){ + TmpTexMap = nTexmap(RIndex, GIndex, BIndex, pFaceSet->GetTexCoord(i,0,TCA_S), pFaceSet->GetTexCoord(i,1,TCA_S), pFaceSet->GetTexCoord(i,2,TCA_S), pFaceSet->GetTexCoord(i,0,TCA_T), pFaceSet->GetTexCoord(i,1,TCA_T), pFaceSet->GetTexCoord(i,2,TCA_T)); + CurTri->SetTexMap(TmpTexMap); + } + } + + //assign a material where one exists: + if ((int)Materials.size() > 1) pCurVolume->SetMaterialID(Materials[1].aID); //if there's a material besides the void material... + + + ShapeCount++; + } + + ToOneTexturePerVolume(); //needed to conform to AMF standards (and to make OpenGL rendering a million times easier + NeedRender = true; + return true; +} + +//move down... + +void AmfFile::ToOneTexturePerVolume(void) +{ + +} + +void AmfFile::X3dFillImportInfo(nMesh* pMesh, std::vector* pVolumes, std::vector* pCoordsBeginIndex) //resizes pVolume to the number of x3d shapes and fills with a pointer to the volume (creating new volumes) each should be loaded in to. alse fills pCoordsBeginIndex with what vertex index in the AMF mesh vertex list these coordinates start... +{ + //figure out which shape objects go into which AMF Volumes (one each, unless they reference the same coordinate set) + int NumX3dShapes = X3dFile.xShapes.size(); + std::vector ToShapes; //a vector of length of number of x3d shapes containing the x3d Shape index this shape will be added to + ToShapes.resize(NumX3dShapes, -1); + int NumVols = 0; + + pCoordsBeginIndex->clear(); + pCoordsBeginIndex->resize(NumX3dShapes, -1); + int CurNumCoord = pMesh->GetNumVertices(); + + for (int i=0; iVolumes.reserve(pMesh->Volumes.size() + NumVols); //reserve AMF volumes so resize doesn't invalidate the pointers... + pVolumes->clear(); + pVolumes->resize(NumX3dShapes, NULL); + for (int i=0; iNewVolume(X3dFile.xShapes[i].xIndexedFaceSet.CoordDef); + } + } + for (int i=0; iGetBBMin(); + *pXMinOut = Min.x; + *pYMinOut = Min.y; + *pZMinOut = Min.z; + return true; + } + else return false; + } + return false; +} + +bool AmfFile::GetEnvlMax(double* pXMaxOut, double* pYMaxOut, double* pZMaxOut, int RenderIndex) +{ + if (NeedRender) Render(); + if (RenderIndex == -1){ + *pXMaxOut = MaxX; + *pYMaxOut = MaxY; + *pZMaxOut = MaxZ; + return true; + } + else { + CMeshSlice* pMesh = GetRenderMesh(RenderIndex); + if (pMesh){ + Vec3D Max = pMesh->GetBBMax(); + *pXMaxOut = Max.x; + *pYMaxOut = Max.y; + *pZMaxOut = Max.z; + return true; + } + else return false; + } + return false; +} + +bool AmfFile::GetEnvlSize(double* pXSizeOut, double* pYSizeOut, double* pZSizeOut, int RenderIndex) +{ + double MaxX, MaxY, MaxZ, MinX, MinY, MinZ = 0; + if (GetEnvlMax(&MaxX, &MaxY, &MaxZ, RenderIndex) && GetEnvlMin(&MinX, &MinY, &MinZ, RenderIndex)){ + *pXSizeOut = MaxX-MinX; + *pXSizeOut = MaxX-MinX; + *pXSizeOut = MaxX-MinX; + return true; + } + else return false; +} + +bool AmfFile::GetEnvlRotQuat(double* pWRotOut, double* pXRotOut, double* pYRotOut, double* pZRotOut, int RenderIndex) +{ + if (NeedRender) Render(); //? + + if (RenderIndex == -1){ + *pWRotOut = 1.0; + *pXRotOut = 0.0; + *pYRotOut = 0.0; + *pZRotOut = 0.0; + return true; + } + else { + CMeshSlice* pMesh = GetRenderMesh(RenderIndex); + if (pMesh){ + CQuat Rot = pMesh->GetRot(); + *pWRotOut = Rot.w; + *pXRotOut = Rot.x; + *pYRotOut = Rot.y; + *pZRotOut = Rot.z; + return true; + } + else return false; + } +} + +bool AmfFile::GetEnvlRotAngleAxis(double* pAngleRadOut, double* pNXOut, double* pNYOut, double* pNZOut, int RenderIndex) +{ + double w, x, y, z, Angle; + Vec3D Axis; + if (GetEnvlRotQuat(&w, &x, &y, &z, RenderIndex)){ + CQuat Rot = CQuat(w, x, y, z); + Rot.AngleAxis(Angle, Axis); + *pAngleRadOut = Angle; + *pNXOut = Axis.x; + *pNYOut = Axis.y; + *pNZOut = Axis.z; + return true; + } + else return false; +} + +bool AmfFile::GetEnvlOrigin(double* pXOriginOut, double* pYOriginOut, double* pZOriginOut, int RenderIndex) +{ + if (NeedRender) Render(); //? + + if (RenderIndex == -1){ + *pXOriginOut = 0.0; + *pYOriginOut = 0.0; + *pZOriginOut = 0.0; + return true; + } + else { + CMeshSlice* pMesh = GetRenderMesh(RenderIndex); + if (pMesh){ + Vec3D Orig = pMesh->GetOffset(); + *pXOriginOut = Orig.x; + *pYOriginOut = Orig.y; + *pZOriginOut = Orig.z; + return true; + } + else return false; + } +} + +bool AmfFile::GetEnvlDims(double* pIDimOut, double* pJDimOut, double* pKDimOut, int RenderIndex) +{ + if (NeedRender) Render(); //? + + if (RenderIndex == -1){ + GetEnvlSize(pIDimOut, pJDimOut, pKDimOut, RenderIndex); + return true; + } + else { + CMeshSlice* pMesh = GetRenderMesh(RenderIndex); + if (pMesh){ + Vec3D Dim = pMesh->GetOrigDim(); + *pIDimOut = Dim.x; + *pJDimOut = Dim.y; + *pKDimOut = Dim.z; + return true; + } + else return false; + } +} + + +bool AmfFile::Scale(double ScaleFactorX, double ScaleFactorY, double ScaleFactorZ, bool ScaleConstellations, bool ScaleEquations) //note: does not scale material equations +{ + ClearError(); + if (ScaleEquations){LastError += "Scaling Equations is not yet supported. Aborting."; return false;} + + + for (std::vector::iterator it = Objects.begin(); it != Objects.end(); it++) { + for (std::vector::iterator jt = it->Meshes.begin(); jt != it->Meshes.end(); jt++) { + for (std::vector::iterator kt = jt->Vertices.VertexList.begin(); kt != jt->Vertices.VertexList.end(); kt++) { + kt->SetCoordinates(kt->GetX()*ScaleFactorX, kt->GetY()*ScaleFactorY, kt->GetZ()*ScaleFactorZ); + } + } + } + + if (ScaleConstellations){ + for (std::vector::iterator it = Constellations.begin(); it != Constellations.end(); it++) { + for (std::vector::iterator jt = it->Instances.begin(); jt != it->Instances.end(); jt++) { + jt->DeltaX *= ScaleFactorX; + jt->DeltaY *= ScaleFactorY; + jt->DeltaZ *= ScaleFactorZ; + } + } + } + NeedRender = true; + + return true; +} + + +//************************************************************************ +//Amf Objects: +//************************************************************************ + +std::string AmfFile::GetObjectName(int ObjectIndex) +{ + nObject* pObj = GetObject(ObjectIndex); + if (pObj) return pObj->GetName(); + else return ""; +} + +void AmfFile::RenameObject(int ObjectIndex, std::string NewName) +{ + nObject* pObj = GetObject(ObjectIndex); + if (pObj) pObj->SetName(NewName); +} + +void AmfFile::RemoveObject(int ObjectIndex) +{ + nObject* pObj = GetObject(ObjectIndex); + if (pObj){ + DeleteGeometry(pObj->aID); + NeedRender = true; + } +} + +void AmfFile::TranslateObject(int ObjectIndex, double dx, double dy, double dz) +{ + nObject* pObj = GetObject(ObjectIndex); + if (pObj){ + pObj->Translate(dx, dy, dz); + NeedRender = true; + } +} + +void AmfFile::RotateObject(int ObjectIndex, double rx, double ry, double rz) +{ + nObject* pObj = GetObject(ObjectIndex); + if (pObj){ + pObj->Rotate(rx, ry, rz); + NeedRender = true; + } +} + + +//************************************************************************ +//Amf Meshes +//************************************************************************ + + +int AmfFile::GetMeshCount(int ObjectIndex) +{ + nObject* pObject = GetObject(ObjectIndex); + if (pObject) return pObject->GetNumMeshes(); + else return -1; +} + +//************************************************************************ +//Amf Volumes +//************************************************************************ + +int AmfFile::GetVolumeCount(int ObjectIndex, int MeshIndex) +{ + nMesh* pMesh = GetMesh(ObjectIndex, MeshIndex); + if (pMesh) return pMesh->Volumes.size(); + else return -1; +} + +std::string AmfFile::GetVolumeName(int ObjectIndex, int MeshIndex, int VolumeIndex) +{ + nVolume* pVol = GetVolume(ObjectIndex, MeshIndex, VolumeIndex); + if (pVol) return pVol->GetName(); + else return ""; +} + +void AmfFile::RenameVolume(int ObjectIndex, int MeshIndex, int VolumeIndex, std::string NewName) +{ + nVolume* pVol = GetVolume(ObjectIndex, MeshIndex, VolumeIndex); + if (pVol) pVol->SetName(NewName); +} + +int AmfFile::GetVolumeMaterialIndex(int ObjectIndex, int MeshIndex, int VolumeIndex) +{ + nVolume* pVol = GetVolume(ObjectIndex, MeshIndex, VolumeIndex); + if (pVol){ + int Index = 0; + for (std::vector::iterator it = Materials.begin(); it != Materials.end(); it++){ + int ThisMatID; + if (pVol->GetMaterialID(&ThisMatID) && it->aID == ThisMatID) return Index; + //if (it->aID == pVol->aMaterialID) return Index; + Index++; + } + } + return -1; +} + +bool AmfFile::SetVolumeMaterialIndex(int ObjectIndex, int MeshIndex, int VolumeIndex, int MaterialIndex) +{ + ClearError(); + + nVolume* pVol = GetVolume(ObjectIndex, MeshIndex, VolumeIndex); + if (pVol){ + nMaterial* pMat = GetMaterial(MaterialIndex); + if (pMat){ + pVol->SetMaterialID(pMat->aID); + NeedRender = true; + return true; + } + } + LastError += "Could not locate the specified volume."; + return false; +} + +//************************************************************************ +//Amf Constellations: +//************************************************************************ + +std::string AmfFile::GetConstellationName(int ConstellationIndex) +{ + nConstellation* pConst = GetConstellation(ConstellationIndex); + if (pConst) return pConst->GetName(); + else return ""; +} + +void AmfFile::RenameConstellation(int ConstellationIndex, std::string NewName) +{ + nConstellation* pConst = GetConstellation(ConstellationIndex); + if (pConst) pConst->SetName(NewName); +} + +void AmfFile::RemoveConstellation(int ConstellationIndex) +{ + nConstellation* pConst = GetConstellation(ConstellationIndex); + if (pConst){ + DeleteGeometry(pConst->aID); + NeedRender = true; + } +} + + +bool AmfFile::IsConstellationReferencedBy(int ConstellationIndex, int ConstellationIndexToCheck) +{ + nConstellation* pConst = GetConstellation(ConstellationIndex); + if (pConst){ + nConstellation* pConstRef = GetConstellation(ConstellationIndexToCheck); + if (pConstRef) return pConst->IsReferencedBy(pConstRef); + else return true; //if not a valid material, it clearly does not reference this material... + } + else return false; +} + +//************************************************************************ +//Amf Constellations: +//************************************************************************ + +int AmfFile::GetInstanceCount(int ConstellationIndex) +{ + nConstellation* pConst = GetConstellation(ConstellationIndex); + if (pConst) return pConst->GetNumInstances(); + else return -1; +} + + +int AmfFile::AddInstance(int ConstellationIndex) +{ + nConstellation* pConst = GetConstellation(ConstellationIndex); + if (pConst){ + int aID = GetUsedGeoID(); + pConst->AddInstance(aID); + NeedRender = true; + return (int)pConst->Instances.size()-1; + } + else return -1; +} + +void AmfFile::RemoveInstance(int ConstellationIndex, int InstanceIndex) +{ + nConstellation* pConst = GetConstellation(ConstellationIndex); + if (pConst){ + if (InstanceIndex < 0 || InstanceIndex >= (int)pConst->Instances.size()) return; + pConst->Instances.erase(pConst->Instances.begin() + InstanceIndex); + NeedRender = true; + } +} + +bool AmfFile::SetInstanceObjectIndex(int ConstellationIndex, int InstanceIndex, int InstanceObjectIndex) +{ + ClearError(); + + nInstance* pInst = GetInstance(ConstellationIndex, InstanceIndex); + if (pInst){ + nObject* pObject = GetObject(InstanceObjectIndex); + if (pObject){ + pInst->aObjectID = pObject->aID; + NeedRender = true; + return true; + } + } + LastError += "Could not locate the specified object."; + + return false; +} + +bool AmfFile::SetInstanceConstellationIndex(int ConstellationIndex, int InstanceIndex, int InstanceConstellationIndex) +{ + ClearError(); + + nInstance* pInst = GetInstance(ConstellationIndex, InstanceIndex); + if (pInst){ + nConstellation* pConst = GetConstellation(InstanceConstellationIndex); + if (pConst){ + pInst->aObjectID = pConst->aID; + NeedRender = true; + return true; + } + } + LastError += "Could not locate the specified constellation."; + + return false; +} + +int AmfFile::GetInstanceObjectIndex(int ConstellationIndex, int InstanceIndex) +{ + nInstance* pInst = GetInstance(ConstellationIndex, InstanceIndex); + if (pInst){ + int Index = 0; + for (std::vector::iterator it = Objects.begin(); it != Objects.end(); it++){ + if (it->aID == pInst->aObjectID) return Index; + Index++; + } + } + return -1; +} + +int AmfFile::GetInstanceConstellationIndex(int ConstellationIndex, int InstanceIndex) +{ + nInstance* pInst = GetInstance(ConstellationIndex, InstanceIndex); + if (pInst){ + int Index = 0; + for (std::vector::iterator it = Constellations.begin(); it != Constellations.end(); it++){ + if (it->aID == pInst->aObjectID) return Index; + Index++; + } + } + return -1; +} + +bool AmfFile::SetInstanceParam(int ConstellationIndex, int InstanceIndex, InstanceParamD ParamD, double Value) +{ + ClearError(); + + nInstance* pInst = GetInstance(ConstellationIndex, InstanceIndex); + if (pInst){ + switch (ParamD){ + case INST_DX: pInst->DeltaX = Value; break; + case INST_DY: pInst->DeltaY = Value; break; + case INST_DZ: pInst->DeltaZ = Value; break; + case INST_RX: pInst->rX = Value; break; + case INST_RY: pInst->rY = Value; break; + case INST_RZ: pInst->rZ = Value; break; + } + NeedRender = true; + return true; + } + LastError += "Could not locate the specified instance."; + return false; +} + + +double AmfFile::GetInstanceParam(int ConstellationIndex, int InstanceIndex, InstanceParamD ParamD) +{ + nInstance* pInst = GetInstance(ConstellationIndex, InstanceIndex); + if (pInst){ + switch (ParamD){ + case INST_DX: return pInst->DeltaX; + case INST_DY: return pInst->DeltaY; + case INST_DZ: return pInst->DeltaZ; + case INST_RX: return pInst->rX; + case INST_RY: return pInst->rY; + case INST_RZ: return pInst->rZ; + } + } + return -1.0; +} + +//************************************************************************ +//Amf Materials: +//************************************************************************ + +std::string AmfFile::GetMaterialName(int MaterialIndex) +{ + nMaterial* pMat = GetMaterial(MaterialIndex); + if (pMat) return pMat->GetName(); + else return ""; +} + +void AmfFile::RenameMaterial(int MaterialIndex, std::string NewName) +{ + if (MaterialIndex == 0) return; //protect the void material + nMaterial* pMat = GetMaterial(MaterialIndex); + if (pMat) pMat->SetName(NewName); +} + +void AmfFile::RemoveMaterial(int MaterialIndex) +{ + if (MaterialIndex == 0) return; //protect the void material + nMaterial* pMat = GetMaterial(MaterialIndex); + if (pMat){ + DeleteMaterial(pMat->aID); + NeedRender = true; + } +} + +bool AmfFile::IsMaterialReferencedBy(int MaterialIndex, int MaterialIndexToCheck) +{ + nMaterial* pMat = GetMaterial(MaterialIndex); + if (pMat){ + nMaterial* pMatRef = GetMaterial(MaterialIndexToCheck); + if (pMatRef) return pMat->IsReferencedBy(pMatRef); + else return true; //if not a valid material, it clearly does not reference this material... + } + else return false; +} + +bool AmfFile::SetMaterialColorD(int MaterialIndex, double Red, double Green, double Blue) +{ + ClearError(); + + if (Red<0) Red = 0; + if (Green<0) Green = 0; + if (Blue<0) Blue = 0; + if (Red>1.0) Red = 1.0; + if (Green>1.0) Green = 1.0; + if (Blue>1.0) Blue = 1.0; + + nMaterial* pMat = GetMaterial(MaterialIndex); + if (pMat && pMat->aID != 0){ //protect the void material + pMat->SetConstColor(Red, Green, Blue); + NeedRender = true; + return true; + } + LastError += "Could not locate the specified material."; + return false; +} + +bool AmfFile::GetMaterialColorD(int MaterialIndex, double *Red, double *Green, double *Blue) +{ + ClearError(); + + nMaterial* pMat = GetMaterial(MaterialIndex); + if (pMat){pMat->GetConstColor(Red, Green, Blue); return true;} + + LastError += "Could not locate the specified material."; + return false; +} + +bool AmfFile::GetMaterialColorI(int MaterialIndex, int *Red, int *Green, int *Blue){ + ClearError(); + + nMaterial* pMat = GetMaterial(MaterialIndex); + if (pMat){ + double R, G, B; + pMat->GetConstColor(&R, &G, &B); + *Red = (int)(R*255.0); + *Green = (int)(G*255.0); + *Blue = (int)(B*255.0); + return true; + } + + LastError += "Could not locate the specified material."; + return false; +} + +//************************************************************************ +//Amf Composites +//************************************************************************ + +int AmfFile::GetCompositeCount(int MaterialIndex) +{ + nMaterial* pMat = GetMaterial(MaterialIndex); + if (pMat) return pMat->GetNumComposites(); + else return -1; +} + + +void AmfFile::ClearComposites(int MaterialIndex) +{ + nMaterial* pMat = GetMaterial(MaterialIndex); + if (pMat){ + NeedRender = true; + pMat->Composites.clear(); + } +} + +int AmfFile::AddComposite(int MaterialIndex, int MaterialIndexToComposite) +{ + ClearError(); + if (MaterialIndex == 0) return -1; //protect the void material + nMaterial* pMat = GetMaterial(MaterialIndex); + if (pMat){ + if (pMat->AddCompositeInstance(MaterialIndexToComposite, "1", &LastError) == -1) return -1; + NeedRender = true; + return (int)pMat->Composites.size()-1; + } + else return -1; +} + +void AmfFile::RemoveComposite(int MaterialIndex, int CompositeIndex) +{ + if (MaterialIndex == 0) return; //protect the void material + nMaterial* pMat = GetMaterial(MaterialIndex); + if (pMat){ + if (CompositeIndex < 0 || CompositeIndex >= (int)pMat->Composites.size()) return; + pMat->Composites.erase(pMat->Composites.begin() + CompositeIndex); + NeedRender = true; + } +} + +bool AmfFile::SetCompositeMaterialIndex(int MaterialIndex, int CompositeIndex, int CompositeMaterialIndex) +{ + if (MaterialIndex == 0) return false; //protect the void material + + nMaterial* pMat = GetMaterial(MaterialIndex); + nMaterial* pMatRef = GetMaterial(CompositeMaterialIndex); + + if (pMat && pMatRef){ + if (pMat->IsReferencedBy(pMatRef)) return false; //don't allow circular references + + nComposite* pComp = GetComposite(MaterialIndex, CompositeIndex); + if (pComp){ + pComp->aMaterialID = pMatRef->aID; + NeedRender = true; + return true; + } + else return false; + } + else return false; +} + +int AmfFile::GetCompositeMaterialIndex(int MaterialIndex, int CompositeIndex) +{ + nComposite* pComp = GetComposite(MaterialIndex, CompositeIndex); + if (pComp){ + int Index = 0; + for (std::vector::iterator it = Materials.begin(); it != Materials.end(); it++){ + if (it->aID == pComp->aMaterialID) return Index; + Index++; + } + } + return -1; +} + +std::string AmfFile::GetCompositeEquation(int MaterialIndex, int CompositeIndex) +{ + nComposite* pComp = GetComposite(MaterialIndex, CompositeIndex); + if (pComp){return pComp->GetEquation();} + else return ""; +} + +bool AmfFile::SetCompositeEquation(int MaterialIndex, int CompositeIndex, std::string Equation) +{ + ClearError(); + nComposite* pComp = GetComposite(MaterialIndex, CompositeIndex); + if (pComp){ + NeedRender = true; + return pComp->SetEquation(Equation, this, &LastError); + } + + LastError += "Could not locate the specified composite."; + return false; +} + +//************************************************************************ +//Output utilities +//************************************************************************ +bool AmfFile::SetSubdivisionLevel(int Level) +{ + NeedRender = true; + ClearError(); + LastError += "Curved triangles coming soon."; + return false; //To be implemented +} + +void AmfFile::DrawGL() +{ + if (NeedRender) Render(); + + for (std::vector::iterator it = RenderedObjs.begin(); it != RenderedObjs.end(); it++){ + for (std::vector::iterator jt = it->Meshes.begin(); jt != it->Meshes.end(); jt++){ + std::cerr << "drawing AmfFile not possible" << std::endl; + //jt->Draw(); //name stack... + } + } +} + +unsigned char* AmfFile::GetSliceBitmapRGBA(double PixelSizeX, double PixelSizeY, double SliceHeightZ, int* XSizeOut, int* YSizeOut, double SurfaceDepth) +{ + ClearError(); + + *XSizeOut = 0; + *YSizeOut = 0; + + if (!GenerateLayer(PixelSizeX, PixelSizeY, SliceHeightZ, SurfaceDepth, &LastError)) return NULL; + + *XSizeOut = CurSlice.Width(); + *YSizeOut = CurSlice.Height(); + return CurSlice.GetRGBABits(); +} + + +int* AmfFile::GetSliceSegmentsXY(double ZHeight, int* NumSegmentsOut) +{ + ClearError(); + LastError += "Slice segment export coming soon..."; + return NULL; +} + +//************************************************************************ +//Errors and information +//************************************************************************ + +std::string AmfFile::GetInfoString(bool MeshInfo) +{ + std::ostringstream os; + os << "Amf Statistics:\n"; + + if (MeshInfo){ + int NumVerts = 0; + int NumTri = 0; + + for (std::vector::iterator it = Objects.begin(); it != Objects.end(); it++) { + for (std::vector::iterator jt = it->Meshes.begin(); jt != it->Meshes.end(); jt++) { + NumVerts += jt->Vertices.VertexList.size(); + for (std::vector::iterator kt = jt->Volumes.begin(); kt != jt->Volumes.end(); kt++) { + NumTri += kt->Triangles.size(); + } + } + } + + os << "Number of Vertices: " << NumVerts << "\nNumber of Triangles: " << NumTri; + + } + return os.str().c_str(); +} + + +//************************************************************************ +//[Preotected functions] +//************************************************************************ + +int AmfFile::AddTexture(CSimpleImage* pImageIn, Channel ChannelToGet, bool TiledIn) //this is really slow right now... optimize later. +{ + Textures.push_back(nTexture(this)); //add the texture! + nTexture* pTex = &(Textures.back()); + + int iw = pImageIn->Width(); + int ih = pImageIn->Height(); + + pTex->aWidth = iw; + pTex->aHeight= ih; + pTex->aDepth = 1; + pTex->aTiled = TiledIn; + pTex->Type = TT_GRAYSCALE; + pTex->aID = GetUnusedTexID(); + + unsigned char* pBits = pImageIn->GetRGBABits(); +// for (int j=0; jpixel(i, j); + switch (ChannelToGet){ + case CR: pTex->BinaryData.push_back(*pBits); break; + case CG: pTex->BinaryData.push_back(*(pBits+1)); break; + case CB: pTex->BinaryData.push_back(*(pBits+2)); break; + case CA: + if (pImageIn->HasAlphaChannel()){ + pTex->BinaryData.push_back(*(pBits+3)); + } + break; + + } + pBits += 4; //always 4 bytes per pixel + } +// } + + NeedRender = true; + return pTex->aID; +} + + + +double AmfFile::GetImportScaleFactor(void) //gets scaling factor based on CurImportUnits and current Amf Units. Sets amf units to CurImportUnits if first mesh to be imported. +{ + if (CurImportUnits == aUnit) return 1.0; + + //see if there's any vertices yet... + bool AnyVerts = false; + for (std::vector::iterator it = Objects.begin(); it != Objects.end(); it++){ + for (std::vector::iterator jt = it->Meshes.begin(); jt != it->Meshes.end(); jt++){ + if (jt->Vertices.VertexList.size() != 0){AnyVerts = true; break; break;} + } + } + + if (!AnyVerts) {aUnit = CurImportUnits; return 1.0;} + + double CurFactor = 1.0; + //convert import units to mm: + switch (CurImportUnits){ + case UNIT_M: CurFactor = 1000.0; break; + case UNIT_IN: CurFactor = 25.4; break; + case UNIT_FT: CurFactor = 12*25.4; break; + case UNIT_UM: CurFactor = 0.001; break; + } + + //convert to Amf units + switch (aUnit){ + case UNIT_M: CurFactor *= 0.001; break; + case UNIT_IN: CurFactor *= 1.0/25.4; break; + case UNIT_FT: CurFactor *= 1.0/(12*25.4); break; + case UNIT_UM: CurFactor *= 1000; break; + } + return CurFactor; +} + + +bool AmfFile::ComputeBoundingBox() +{ + MinX = MaxX = MinY = MaxY = MinZ = MaxZ = 0; + + Vec3D tmpMax, tmpMin; + + bool FoundAny = false; + for (std::vector::iterator it = RenderedObjs.begin(); it != RenderedObjs.end(); it++){ + for (std::vector::iterator jt = it->Meshes.begin(); jt != it->Meshes.end(); jt++){ + FoundAny = true; + tmpMax = jt->GetBBMax(); + tmpMin = jt->GetBBMin(); + MinX = (std::min)(tmpMin.x, MinX); + MaxX = (std::max)(tmpMax.x, MaxX); + MinY = (std::min)(tmpMin.y, MinY); + MaxY = (std::max)(tmpMax.y, MaxY); + MinZ = (std::min)(tmpMin.z, MinZ); + MaxZ = (std::max)(tmpMax.z, MaxZ); + } + } + + return FoundAny; +} + + +void AmfFile::GetMinMax(double& xMinOut, double& yMinOut, double& zMinOut, double& xMaxOut, double& yMaxOut, double& zMaxOut) +{ + if (NeedRender) Render(); + + //TODO: Account for constellations... + xMinOut = yMinOut = zMinOut = xMaxOut = yMaxOut = zMaxOut = 0; + + if (Objects.size() == 0) return; + if (Objects[0].Meshes.size()==0) return; //todo: might be later objects that have a mesh? + if (Objects[0].Meshes[0].Vertices.VertexList.size()==0) return; + + + xMinOut = xMaxOut = Objects[0].Meshes[0].Vertices.VertexList[0].Coordinates.X; + yMinOut = yMaxOut = Objects[0].Meshes[0].Vertices.VertexList[0].Coordinates.Y; + zMinOut = zMaxOut = Objects[0].Meshes[0].Vertices.VertexList[0].Coordinates.Z; + + for (std::vector::iterator it = Objects.begin(); it != Objects.end(); it++) { + for (std::vector::iterator jt = it->Meshes.begin(); jt != it->Meshes.end(); jt++) { + for (std::vector::iterator kt = jt->Vertices.VertexList.begin(); kt != jt->Vertices.VertexList.end(); kt++) { + xMinOut = std::min(xMinOut, kt->GetX()); + yMinOut = std::min(yMinOut, kt->GetY()); + zMinOut = std::min(zMinOut, kt->GetZ()); + xMaxOut = std::max(xMaxOut, kt->GetX()); + yMaxOut = std::max(yMaxOut, kt->GetY()); + zMaxOut = std::max(zMaxOut, kt->GetZ()); + } + } + } + +} + +void AmfFile::GetSize(double& xOut, double& yOut, double& zOut) +{ + double xMin, yMin, zMin, xMax, yMax, zMax; + GetMinMax(xMin, yMin, zMin, xMax, yMax, zMax); + xOut = xMax-xMin; + yOut = yMax-yMin; + zOut = zMax-zMin; + +} + + +nObject* AmfFile::GetObject(int ObjectIndex, bool CanCreate) +{ + if (ObjectIndex < 0) return NULL; + if (ObjectIndex >= (int)Objects.size()){ + if (CanCreate){Objects.resize(ObjectIndex+1, nObject(this)); Objects.back().SetName("Default");} + else return NULL; + } + return &Objects[ObjectIndex]; +} + +nMesh* AmfFile::GetMesh(int ObjectIndex, int MeshIndex, bool CanCreate) +{ + nObject* pObject = GetObject(ObjectIndex, CanCreate); + if (pObject){ + if (MeshIndex < 0) return NULL; + if (MeshIndex >= (int)pObject->Meshes.size()){ + if (CanCreate){pObject->Meshes.resize(MeshIndex+1);} + else return NULL; + } + return &pObject->Meshes[MeshIndex]; + } + else return NULL; +} + +nVolume* AmfFile::GetVolume(int ObjectIndex, int MeshIndex, int VolumeIndex, bool CanCreate) +{ + nMesh* pMesh = GetMesh(ObjectIndex, MeshIndex, CanCreate); + if (pMesh){ + if (VolumeIndex < 0) return NULL; + if (VolumeIndex >= (int)pMesh->Volumes.size()){ + if (CanCreate){pMesh->Volumes.resize(VolumeIndex+1);} + else return NULL; + } + return &pMesh->Volumes[VolumeIndex]; + } + else return NULL; +} + + +nConstellation* AmfFile::GetConstellation(int ConstellationIndex, bool CanCreate) +{ + if (ConstellationIndex < 0) return NULL; + if (ConstellationIndex >= (int)Constellations.size()){ + if (CanCreate){ Constellations.resize(ConstellationIndex+1, nConstellation(this)); Constellations.back().SetName("Default");} + else return NULL; + } + return &Constellations[ConstellationIndex]; +} + +nMaterial* AmfFile::GetMaterial(int MaterialIndex, bool CanCreate) +{ + if (MaterialIndex < 0) return NULL; + if (MaterialIndex >= (int)Materials.size()){ + if (CanCreate){Materials.resize(MaterialIndex+1, nMaterial(this)); Materials.back().SetName("Default");} + else return NULL; + } + return &Materials[MaterialIndex]; +} + +nInstance* AmfFile::GetInstance(int ConstellationIndex, int InstanceIndex, bool CanCreate) +{ + nConstellation* pConst = GetConstellation(ConstellationIndex, CanCreate); + if (pConst){ + if (InstanceIndex < 0) return NULL; + if (InstanceIndex >= (int)pConst->Instances.size()){ + if (CanCreate){pConst->Instances.resize(InstanceIndex+1);} + else return NULL; + } + return &pConst->Instances[InstanceIndex]; + } + else return NULL; +} + +nComposite* AmfFile::GetComposite(int MaterialIndex, int CompositeIndex, bool CanCreate) +{ + nMaterial* pMaterial = GetMaterial(MaterialIndex, CanCreate); + if (pMaterial){ + if (CompositeIndex < 0) return NULL; + if (CompositeIndex >= (int)pMaterial->Composites.size()){ + if (CanCreate){pMaterial->Composites.resize(CompositeIndex+1);} + else return NULL; + } + return &pMaterial->Composites[CompositeIndex]; + } + else return NULL; +} + + +bool AmfFile::Render() //generates RenderedObjs; +{ + RenderedObjs.clear(); //don't keep adding to the list - regenerate it! + Vec3D TotalOff; + CQuat TotalRot; + std::vector IndexStack; + + //render all constellations (recusively) + //int counter = 0; + for (std::vector::iterator it = Constellations.begin(); it != Constellations.end(); it++){ + TotalOff = Vec3D(0,0,0); + TotalRot = CQuat(1,0,0,0); //no rotation... + if (IsTopLevelGeo(it->aID)){ +// IndexStack.push_back(it->aID); + RenderConstellation(&(*it), &IndexStack, TotalOff, TotalRot, CurColorView, CurViewMode, SubDivLevel); +// IndexStack.pop_back(); + } + //counter++; + } + + //render all top level objects (directly) + //counter=0; + for (std::vector::iterator it = Objects.begin(); it != Objects.end(); it++){ + if (IsTopLevelGeo(it->aID)){ +// IndexStack.push_back(it->aID); + RenderObject(&(*it), &IndexStack, Vec3D(0,0,0), CQuat(1,0,0,0), CurColorView, CurViewMode, SubDivLevel); +// IndexStack.pop_back(); + } + //counter++; + } + + //Make specific IDs for each object... + //int ObjNum = 0; + //for (std::vector::iterator it = RenderedObjs.begin(); it != RenderedObjs.end(); it++){ + // for (std::vector::iterator jt = it->Meshes.begin(); jt != it->Meshes.end(); jt++){ + // jt->GlNameIndexStack.push_back(ObjNum); + // } + // ObjNum++; + //} + +// BoundsChanged = true; + ComputeBoundingBox(); + NeedRender = false; + return true; +} + + +//quaternion properties (for my reference) +//1) To rotate a vector V, form a quaternion with w = 0; To rotate by Quaternion Q, do Q*V*Q.Conjugate() and trim off the w component. +//2) To do multiple rotations: To Rotate by Q1 THEN Q2, Q2*Q1*V*Q1.Conjugate*Q2.Conjugate(), or make a Qtot = Q2*Q1 and do Qtot*V*Qtot.Conjucate() +//3) Q1*Q1.Conjugate - Identity +//4) To do a reverse rotation Q1, just do Q1.conjugate*V*Q1 +//http://www.cprogramming.com/tutorial/3d/quaternions.html + + +bool AmfFile::RenderConstellation(nConstellation* pConst, std::vector* pIndexStack, Vec3D CurOff, CQuat CurRot, ColorView CurColorView, ViewMode CurViewMode, int SubDivLev) //for recursion +{ + Vec3D CurOffRef = CurOff; + CQuat CurRotRef = CurRot; + + //get const INDEX + int ConstIndex = -1; + for (int i=0; i<(int)Constellations.size(); i++) if (&Constellations[i] == pConst) ConstIndex = i; + if (ConstIndex == -1) return false; + + pIndexStack->push_back(ConstIndex); + + int InstanceIndex = 0; + for (std::vector::iterator jt = pConst->Instances.begin(); jt != pConst->Instances.end(); jt++){ + pIndexStack->push_back(InstanceIndex); + + CurOff = CurOffRef; + CurRot = CurRotRef; + + //Compute Local rotation: + CQuat XRot = CQuat(jt->rX*d2r, Vec3D(1,0,0)); + CQuat YRot = CQuat(jt->rY*d2r, Vec3D(0,1,0)); + CQuat ZRot = CQuat(jt->rZ*d2r, Vec3D(0,0,1)); + CQuat ThisRot = ZRot*YRot*XRot; //rotation (by X then Y then Z) + + //Compute local translation + Vec3D ThisOff = /*CurUnitsScale*/Vec3D(jt->DeltaX, jt->DeltaY, jt->DeltaZ); + + //order: + //1) rotate local rotation + //2) translate local translation + //3) rotate current rotation (and rotate local translation... + //4) translate current translation + + CQuat Offset = CQuat(ThisOff); + Offset = CurRot*Offset*CurRot.Conjugate(); //rotate previous offsets in proper order + + CurRot = CurRot*ThisRot; //keep track of total rotation (rotate this amount, then everything previous + CurOff = Offset.ToVec() + CurOff; //add in this offset + + //if this id is a constellation: + nConstellation* pnConstTmp = GetConstellationByID(jt->aObjectID); + if (pnConstTmp){ //this is a constellation + if (!RenderConstellation(pnConstTmp, pIndexStack, CurOff, CurRot, CurColorView, CurViewMode, SubDivLev)) return false; //recurse! + } + else { //this is an object + if (!RenderObject(GetObjectByID(jt->aObjectID), pIndexStack, CurOff, CurRot, CurColorView, CurViewMode, SubDivLev)) return false; + } + + pIndexStack->pop_back(); + InstanceIndex++; + } + + pIndexStack->pop_back(); + return true; +} + +bool AmfFile::RenderObject(nObject* pObj, std::vector* pIndexStack, Vec3D CurOff, CQuat CurRot, ColorView CurColorView, ViewMode CurViewMode, int SubDivLev) //actually adds an object to RenderedObjs (rotate first, then offset) +{ + if (!pObj) return false; + + //get object INDEX + int ObjIndex = -1; + for (int i=0; i<(int)Objects.size(); i++) if (&Objects[i] == pObj) ObjIndex = i; + if (ObjIndex == -1) return false; + + pIndexStack->push_back(ObjIndex); +// pIndexStack->push_back(pObj->aID); + + + pIndexStack->push_back(RenderedObjs.size()); //last integer is the GuiIndex + RenderedObjs.push_back(nObjectExt(this, pObj, pIndexStack, CurOff, CurRot, CurColorView, CurViewMode, SubDivLev)); + pIndexStack->pop_back(); + pIndexStack->pop_back(); + return true; +} + +nObjectExt* AmfFile::GetRenderObject(int RenderIndex) +{ + if (RenderIndex <0 || RenderIndex >= (int)RenderedObjs.size()) return NULL; + + if (NeedRender) Render(); +// return &RenderedObjs[RenderIndex]; + + int Counter = 0; + for (std::vector::iterator it = RenderedObjs.begin(); it != RenderedObjs.end(); it++){ + int NumMesh = it->Meshes.size(); + if (RenderIndex < Counter+NumMesh) return &(*it); + Counter += NumMesh; + } + return NULL; +} + +CMeshSlice* AmfFile::GetRenderMesh(int RenderIndex) +{ + if (RenderIndex <0 || RenderIndex >= (int)RenderedObjs.size()) return NULL; + + if (NeedRender) Render(); + + int Counter = 0; + for (std::vector::iterator it = RenderedObjs.begin(); it != RenderedObjs.end(); it++){ + int NumMesh = it->Meshes.size(); + for (int i=0; iMeshes[i]; + Counter++; + } + } + return NULL; +} + + +bool AmfFile::GenerateLayer(double PixelSizeX, double PixelSizeY, double SliceHeightZ, double SurfaceDepthIn, std::string* pMessage) +{ + ClearError(); + + Vec3D BMin = Vec3D(MinX, MinY, MinZ); + Vec3D BMax = Vec3D(MaxX, MaxY, MaxZ); + +// SurfDepth = SurfaceDepthIn; + Vec3D PrintOrigin = BMin; + Vec3D PrintSize = BMax-BMin; + + int XOrigin = (int)(PrintOrigin.x/PixelSizeX); + int YOrigin = (int)(PrintOrigin.y/PixelSizeY); + int XPix = (int)(PrintSize.x/PixelSizeX) + 1; + int YPix = (int)(PrintSize.y/PixelSizeY) + 1; +// ZPix = (int)(PrintSize.z/VoxSize.z) + 1; + + if (!CurSlice.AllocateRGBA(XPix, YPix)){ // = QImage(XPix, YPix, QImage::Format_ARGB32); //32 bit Color + if (pMessage) *pMessage += "Insufficient memory to create bitmap. Please lower resolution.\n"; + return false; + } + + CurSlice.Fill(255, 255, 255, 0); //erase bitmap + + if (SliceHeightZ < BMin.z || SliceHeightZ > BMax.z){*pMessage += "Requested layer out of bounds.\n"; return false;} //out of bounds! +// CurLayer = Layer; + double ZHeight = SliceHeightZ; //(Layer + 0.5)*VoxSize.z + PrintOrigin.z; //get center of voxels... + int XMinPix, YMinPix, XMaxPix, YMaxPix; //pixel coords for envelope of sub-meshes + +// nObjectExt::aThis.clear(); + int ThisObjID = 0; +// int RenderIndex = 0; + + //make individual bitmaps + for (std::vector::iterator it = RenderedObjs.begin(); it != RenderedObjs.end(); it++){ + int ThisMeshID = 0; + for (std::vector::iterator jt = it->Meshes.begin(); jt != it->Meshes.end(); jt++){ + if (ZHeight < jt->GetBBMin().z || ZHeight > jt->GetBBMax().z) continue; //if slice is above or below this mesh don't bother slicing! + + CSimpleImage tmp; //TODO: this has to get memory every time... + + //round down mins to pixel below... + XMinPix = (int)((jt->GetBBMin().x)/PixelSizeX); + XMaxPix = (int)((jt->GetBBMax().x)/PixelSizeX)+1; //if its exact, we do an extra column of pixels... + YMinPix = (int)((jt->GetBBMin().y)/PixelSizeY); + YMaxPix = (int)((jt->GetBBMax().y)/PixelSizeY)+1; //if its exact, we do an extra row of pixels... + + //temp... +// jt->pObjExt = &(*it); +// nObjectExt::aThis.push_back(&(*it)); //remember staticly which member of this class we are based on ID... +// jt->pGetColor = &it->GetColorCallback; + + if(jt->GetSlice(&tmp, ZHeight, SurfaceDepthIn, XMinPix*PixelSizeX, XMaxPix*PixelSizeX, YMinPix*PixelSizeY, YMaxPix*PixelSizeY, XMaxPix-XMinPix, YMaxPix-YMinPix, ThisObjID, pMessage)){ + ImposeBitmap(&CurSlice, &tmp, XOrigin, YOrigin, XMinPix, YMinPix); + } + else return false; + +// RenderIndex++; + ThisMeshID++; + } + ThisObjID++; + } + + return true; +} + +void AmfFile::ImposeBitmap(CSimpleImage* pBase, CSimpleImage* pImposed, int BaseXOrigin, int BaseYOrigin, int ImpXOrigin, int ImpYOrigin) //X, Y origin is the location within pBase that pImposed should be added. +{ + int ImposedXSize = pImposed->Width(); + int ImposedYSize = pImposed->Height(); + int BaseXSize = pBase->Width(); + int BaseYSize = pBase->Height(); + int CurAbsX, CurAbsY; + + unsigned char* pDataRGBABase = pBase->GetRGBABits(); + unsigned char* pDataRGBAImposed = pImposed->GetRGBABits(); + unsigned char *pCurRowImposed, *pCurRowBase; + + for(int iy=0; iy= BaseYSize) continue; + + pCurRowImposed = pDataRGBAImposed+iy*4*ImposedXSize; + pCurRowBase = pDataRGBABase+CurAbsY*4*BaseXSize; + + for(int ix=0; ix= BaseXSize) continue; + + //could do some smarter compositing here... + if (*(pCurRowImposed+4*ix+3)== 255){ //if this pixel in the image to add is not transparent (at all, for now...) + *(pCurRowBase + 4*CurAbsX) = *(pCurRowImposed+4*ix); + *(pCurRowBase + 4*CurAbsX+1) = *(pCurRowImposed+4*ix+1); + *(pCurRowBase + 4*CurAbsX+2) = *(pCurRowImposed+4*ix+2); + *(pCurRowBase + 4*CurAbsX+3) = *(pCurRowImposed+4*ix+3); + } + else { //if it is transparent, don't mess with the base image! + // *(pCurRowBase + 4*CurAbsX) = *(pCurRowBase + 4*CurAbsX+1) = *(pCurRowBase + 4*CurAbsX+2) = 255; + // *(pCurRowBase + 4*CurAbsX+3) = 0; + } + + } + } +} + + + + + + + + + + + + + + + + +//************************************************************************ +//nObjectExt class +//************************************************************************ + +//std::vector nObjectExt::aThis; + + + + +CColor nObjectExt::nColor2CColor(nColor& ColorIn, Vec3D* pLoc) +{ + if (pLoc) return CColor(ColorIn.GetR(pLoc->X(), pLoc->Y(), pLoc->Z()), ColorIn.GetG(pLoc->X(), pLoc->Y(), pLoc->Z()), ColorIn.GetB(pLoc->X(), pLoc->Y(), pLoc->Z())); + else return CColor(ColorIn.GetR(), ColorIn.GetG(), ColorIn.GetB()); +} + +//void nObjectExt::GetColorCallback(double xIn, double yIn, double zIn, double* rOut, double* gOut, double* bOut, double* aOut, int aObjectID) //function to get color based on location +//{ +//// if (aObjectID >= aThis.size()){ //if no valid pointer back to an actuall class instanct... +//// *rOut = 0; +//// *gOut = 0; +//// *bOut = 0; +//// *aOut = 1.0; +//// } +//// else { +// int nObjectID = aObjectID/MaxMeshPerObj; +// int nMeshID = aObjectID-nObjectID*MaxMeshPerObj; +// +// nObjectExt* pCurThis = nObjectExt::aThis[nObjectID]; +// Vec3D TmpPt = Vec3D(xIn, yIn, zIn); +// TmpPt = /*pCurThis->pAmf->GetUnitsScaleFromMm()*/pCurThis->OriginalLoc(TmpPt); +// pCurThis->pAmf->GetMaterialByID(pCurThis->MaterialIDs[nMeshID])->GetColorAt(TmpPt.x, TmpPt.y, TmpPt.z, rOut, gOut, bOut, aOut); +//// } +//} + +typedef struct{int ix[4];} inds; //container for the four Amf indicies that will make up a texture... + +bool nObjectExt::RenderMesh(nAmf* pAmfIn, nObject* pObj, std::vector* pIndexStack, Vec3D& OffsetIn, CQuat& RotIn, ColorView& CurColorView, ViewMode& CurViewMode, int& SubDivLevel) +{ + pAmf = pAmfIn; + //Offset = OffsetIn; + //Rot = RotIn; + + //will do mesh subdivide in here eventually...! + + //options here for how we color... + CVertex v[3]; + nVertex* pCurVert; + bool NormExists[3]; + Vec3D TriNormal; + CColor DefaultColor = CColor(0.5, 0.5, 0.5); + + for (std::vector::iterator jt = pObj->Meshes.begin(); jt != pObj->Meshes.end(); jt++){ //meshes + for (std::vector::iterator kt = jt->Volumes.begin(); kt != jt->Volumes.end(); kt++){ //Volumes + Meshes.push_back(CMeshSlice()); //add a mesh object for each volume, since we're only currently supporting a single texture per mesh... + + CMesh* CurMesh = &Meshes.back(); //for everythign else which is just CMesh member + CurMesh->GlNameIndexStack = *pIndexStack; + +// CurMesh->GlNameIndex = pObj->aID; //But, our opengl picking will always be by Object level + + std::vector AddedTextures; //keep a list of which AMF tex ID's we've added as textures to mesh class the index of these should match the index in the Textures[] array in the mesh + + nMaterial* pMat = NULL; + MaterialIDs.push_back(-1); //no material + if (kt->MaterialIDExists){ + pMat = pAmf->GetMaterialByID(kt->aMaterialID); + MaterialIDs.back() = kt->aMaterialID; //set to this material ID + } + + //TEMP!!! (allows slicer to get colors for bitmap...) + Meshes.back().pMaterial = pMat; +// Meshes.back().pObjExt = this; + Meshes.back().pAmf = pAmfIn; + + + for (std::vector::iterator lt = kt->Triangles.begin(); lt != kt->Triangles.end(); lt++){ //Triangles + for(int i=0; i<3; i++){ + v[i].Clear(); //make sure no lingering data in our re-usable vertex object... + + switch (i){ + case 0: pCurVert = &(jt->Vertices.VertexList[lt->v1]); break; + case 1: pCurVert = &(jt->Vertices.VertexList[lt->v2]); break; + case 2: pCurVert = &(jt->Vertices.VertexList[lt->v3]); break; + } + + v[i].v = /*UnitsScale*/Vec3D(pCurVert->GetX(), pCurVert->GetY(), pCurVert->GetZ()); + + //todo:get normal from AMF! + NormExists[i] = false; + if (pCurVert->NormalExists){ //got lots to do as far as subdividing to do here........... + v[i].n = Vec3D(pCurVert->Normal.nX, pCurVert->Normal.nY, pCurVert->Normal.nZ); + v[i].HasNormal = true; + NormExists[i] = true; //flag to not apply triangle normal later... + } + + + //set color based on view option... + v[i].HasColor = true; + switch (CurColorView){ + case CV_TRI: case CV_TRICOLOR: case CV_TRITEX: if (lt->ColorExists) v[i].VColor = nColor2CColor(lt->Color, &v[i].v); else v[i].VColor = DefaultColor; break; + case CV_VERT: if (pCurVert->ColorExists) v[i].VColor = nColor2CColor(pCurVert->Color, &v[i].v); else v[i].VColor = DefaultColor; break; + case CV_VOL: if (kt->ColorExists) v[i].VColor = nColor2CColor(kt->Color, &v[i].v); else v[i].VColor = DefaultColor; break; + case CV_OBJ: if (pObj->ColorExists) v[i].VColor = nColor2CColor(pObj->Color, &v[i].v); else v[i].VColor = DefaultColor; break; + case CV_MAT: if (kt->MaterialIDExists && pMat != NULL && pMat->ColorExists) v[i].VColor = nColor2CColor(pMat->Color, &v[i].v); else v[i].VColor = DefaultColor; break; + case CV_ALL: + //order of precedence: material < object < volume < vertex < triangle + if (lt->ColorExists) v[i].VColor = nColor2CColor(lt->Color, &v[i].v); + else if (pCurVert->ColorExists) v[i].VColor = nColor2CColor(pCurVert->Color, &v[i].v); + else if (kt->ColorExists) v[i].VColor = nColor2CColor(kt->Color, &v[i].v); + else if (pObj->ColorExists) v[i].VColor = nColor2CColor(pObj->Color, &v[i].v); + else if (kt->MaterialIDExists && pMat != NULL && pMat->ColorExists) v[i].VColor = nColor2CColor(pMat->Color, &v[i].v); + else v[i].VColor = DefaultColor; //set the color to some + break; + default: v[i].VColor = DefaultColor; break; + } + } + + //NORMALS! + if (!NormExists[0] || NormExists[1] || NormExists[2]){ //if any of the normals weren't set from the AMF apply triangle normal + Vec3D tmp = v[2].v-v[0].v; //gcc compat + TriNormal = ((v[1].v-v[0].v).Cross(tmp)).Normalized(); + if (!NormExists[0]) v[0].n = TriNormal; + if (!NormExists[1]) v[1].n = TriNormal; + if (!NormExists[2]) v[2].n = TriNormal; + } + + + //TEXTURES! + if (lt->TexMapExists && (CurColorView == CV_TRITEX || CurColorView == CV_TRI || CurColorView == CV_ALL)){ //if there's a texture map and we want to see textures... + nTexmap* pMap = lt->GetpTexMap(); + int aRID = pMap->RTexID; + int aGID = pMap->GTexID; + int aBID = pMap->BTexID; + int aAID = pMap->ATexID; + + if (aRID == -1 || aGID == -1 || aBID == -1) return false; //Technically this is allowed, but not handled correctly yet. + + int MeshTexIndex = -1; //what mesh texture index does this triangle reference? + //check and return index if we've already added a texture to this mesh with the same indices... + int NumAdded = AddedTextures.size(); + for (int i=0; iGetTextureByID(aRID); + if (pTex) pTex->GetSize(&w[0], &h[0]); else return false; + pTex = pAmf->GetTextureByID(aGID); + if (pTex) pTex->GetSize(&w[1], &h[1]); else return false; + pTex= pAmf->GetTextureByID(aBID); + if (pTex) pTex->GetSize(&w[2], &h[2]); else return false; + if (pMap->ATexIDExists) pAmf->GetTextureByID(aAID)->GetSize(&w[3], &h[3]); + if (w[0] != w[1] || w[0] != w[2] || (w[3] != -1 && w[0] != w[3])) return false; //a width did not match up! + if (h[0] != h[1] || h[0] != h[2] || (h[3] != -1 && h[0] != h[3])) return false; //a height did not match up! + + //tile this texture if R, G, or B texture in AMF is tiled... + bool TileThisOne = (pAmf->GetTextureByID(pMap->RTexID)->aTiled || pAmf->GetTextureByID(pMap->GTexID)->aTiled || pAmf->GetTextureByID(pMap->BTexID)->aTiled); + + CTexture* NewTexture = CurMesh->AddTexture(CTexture()); + + // CurMesh->Textures.push_back(CTexture()); //Add the texture! + inds TheseInds = {{aRID, aGID, aBID, aAID}}; + AddedTextures.push_back(TheseInds); + MeshTexIndex = CurMesh->GetTextureCount()-1; + + if (pMap->ATexIDExists) NewTexture->LoadData(w[0], h[0], pAmf->GetTextureByID(aRID)->BinaryData.data(), pAmf->GetTextureByID(aGID)->BinaryData.data(), pAmf->GetTextureByID(aBID)->BinaryData.data(), pAmf->GetTextureByID(aAID)->BinaryData.data(), TileThisOne); + else NewTexture->LoadData(w[0], h[0], pAmf->GetTextureByID(aRID)->BinaryData.data(), pAmf->GetTextureByID(aGID)->BinaryData.data(), pAmf->GetTextureByID(aBID)->BinaryData.data(), TileThisOne); + + } + + try{CurMesh->AddFacet(v[0], v[1], v[2], TexMap(MeshTexIndex, pMap->uTex1, pMap->uTex2, pMap->uTex3, pMap->vTex1, pMap->vTex2, pMap->vTex3));} + catch(std::bad_alloc){return false;} + } + + else { //otherwise + try{CurMesh->AddFacet(v[0], v[1], v[2]);} + catch(std::bad_alloc){return false;} + + } + + + //TODO: get edges into mesh object... + //TEMP: exhaustive check of edges! + CLine tmpLine; + for (std::vector::iterator mt = jt->Vertices.EdgeList.begin(); mt != jt->Vertices.EdgeList.end(); mt++){ + CFacet* pCurFacet = CurMesh->GetpFacet(CurMesh->GetFacetCount()-1); // Facets.back(); + //from v0 to v1 + if (mt->v1 == lt->v1 && mt->v2 == lt->v2){ //v1, v2 may not be in correct order!! + tmpLine = CLine(pCurFacet->vi[0], pCurFacet->vi[1], Vec3D(mt->dx1, mt->dy1, mt->dz1), Vec3D(mt->dx2, mt->dy2, mt->dz2)); + CurMesh->AddLine(tmpLine); + pCurFacet->HasEdge[0] = true; + pCurFacet->ei[0] = CurMesh->GetLineCount()-1; + } + else if (mt->v2 == lt->v1 && mt->v1 == lt->v2){ //v1, v2 may not be in correct order!! + tmpLine = CLine(pCurFacet->vi[0], pCurFacet->vi[1], -Vec3D(mt->dx2, mt->dy2, mt->dz2), -Vec3D(mt->dx1, mt->dy1, mt->dz1)); + CurMesh->AddLine(tmpLine); + pCurFacet->HasEdge[0] = true; + pCurFacet->ei[0] = CurMesh->GetLineCount()-1; + } + + + //from v1 to v2 + else if (mt->v1 == lt->v2 && mt->v2 == lt->v3){ //v1, v2 may not be in correct order!! + tmpLine = CLine(pCurFacet->vi[1], pCurFacet->vi[2], Vec3D(mt->dx1, mt->dy1, mt->dz1), Vec3D(mt->dx2, mt->dy2, mt->dz2)); + CurMesh->AddLine(tmpLine); + pCurFacet->HasEdge[1] = true; + pCurFacet->ei[1] = CurMesh->GetLineCount()-1; + } + else if (mt->v2 == lt->v2 && mt->v1 == lt->v3){ //v1, v2 may not be in correct order!! + tmpLine = CLine(pCurFacet->vi[1], pCurFacet->vi[2], -Vec3D(mt->dx2, mt->dy2, mt->dz2), -Vec3D(mt->dx1, mt->dy1, mt->dz1)); + CurMesh->AddLine(tmpLine); + pCurFacet->HasEdge[1] = true; + pCurFacet->ei[1] = CurMesh->GetLineCount()-1; + } + + + //from v2 to v0 + else if (mt->v1 == lt->v3 && mt->v2 == lt->v1){ //v1, v2 may not be in correct order!! + tmpLine = CLine(pCurFacet->vi[2], pCurFacet->vi[0], Vec3D(mt->dx1, mt->dy1, mt->dz1), Vec3D(mt->dx2, mt->dy2, mt->dz2)); + CurMesh->AddLine(tmpLine); + pCurFacet->HasEdge[2] = true; + pCurFacet->ei[2] = CurMesh->GetLineCount()-1; + } + else if (mt->v2 == lt->v3 && mt->v1 == lt->v1){ //v1, v2 may not be in correct order!! + tmpLine = CLine(pCurFacet->vi[2], pCurFacet->vi[0], -Vec3D(mt->dx2, mt->dy2, mt->dz2), -Vec3D(mt->dx1, mt->dy1, mt->dz1)); + CurMesh->AddLine(tmpLine); + pCurFacet->HasEdge[2] = true; + pCurFacet->ei[2] = CurMesh->GetLineCount()-1; + } + + } + } + + //do specified translations and rotations... +//experiement with curved triangles... +// CurMesh->SubdivideMe(); +// CurMesh->SubdivideMe(); +// CurMesh->SubdivideMe(); +// CurMesh->SubdivideMe(); +// CurMesh->SubdivideMe(); +// CurMesh->SubdivideMe(); + + +// CurMesh->DrawSmooth = false; + + Meshes.back().Rot = RotIn; + Meshes.back().Offset = OffsetIn + CurMesh->GetBBMin().Rot(RotIn); + + + Meshes.back().OrigDim = CurMesh->GetBBSize(); +// Meshes.back().OrigBBMin = CurMesh->GetBBMin(); + + CurMesh->Rotate(RotIn); + CurMesh->Translate(OffsetIn); + } + + } + return true; +} diff --git a/libraries/amf/amftools-code/src/Amf.cpp b/libraries/amf/amftools-code/src/Amf.cpp new file mode 100644 index 00000000..fee4f124 --- /dev/null +++ b/libraries/amf/amftools-code/src/Amf.cpp @@ -0,0 +1,150 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +// Amf_WinDll.cpp : Defines the exported functions for the DLL application. +// + +#include "Amf.h" +#include "AMF_File.h" + +//Need to translate. Can't use the same enums because must be defined in interface header as well as within library. +static UnitSystem TranslateEnum(Amf::aUnitSystem UnitsIn){switch (UnitsIn){case Amf::aUNIT_MM: return UNIT_MM; case Amf::aUNIT_M: return UNIT_M; case Amf::aUNIT_IN: return UNIT_IN; case Amf::aUNIT_FT: return UNIT_FT; case Amf::aUNIT_UM: return UNIT_UM; default: return UNIT_MM;}} +static Amf::aUnitSystem TranslateEnum(UnitSystem UnitsIn){switch (UnitsIn){case UNIT_MM: return Amf::aUNIT_MM; case UNIT_M: return Amf::aUNIT_M; case UNIT_IN: return Amf::aUNIT_IN; case UNIT_FT: return Amf::aUNIT_FT; case UNIT_UM: return Amf::aUNIT_UM; default: return Amf::aUNIT_MM;}} +static InstanceParamD TranslateEnum(Amf::aInstanceParamD ParamIn){switch (ParamIn){case Amf::aINST_DX: return INST_DX; case Amf::aINST_DY: return INST_DY; case Amf::aINST_DZ: return INST_DZ; case Amf::aINST_RX: return INST_RX; case Amf::aINST_RY: return INST_RY; case Amf::aINST_RZ: return INST_RZ; default: return INST_DX;}} +static Amf::aInstanceParamD TranslateEnum(InstanceParamD ParamIn){switch (ParamIn){case INST_DX: return Amf::aINST_DX; case INST_DY: return Amf::aINST_DY; case INST_DZ: return Amf::aINST_DZ; case INST_RX: return Amf::aINST_RX; case INST_RY: return Amf::aINST_RY; case INST_RZ: return Amf::aINST_RZ; default: return Amf::aINST_DX;}} +static EnvelopeData TranslateEnum(Amf::aEnvelopeData DataIn){switch (DataIn){case Amf::aENVL_XMIN: return ENVL_XMIN; case Amf::aENVL_YMIN: return ENVL_YMIN; case Amf::aENVL_ZMIN: return ENVL_ZMIN; case Amf::aENVL_XMAX: return ENVL_XMAX; case Amf::aENVL_YMAX: return ENVL_YMAX; case Amf::aENVL_ZMAX: return ENVL_ZMAX; case Amf::aENVL_XSIZE: return ENVL_XSIZE; case Amf::aENVL_YSIZE: return ENVL_YSIZE; case Amf::aENVL_ZSIZE: return ENVL_ZSIZE; default: return ENVL_XMIN;}} + + +Amf::Amf() {pData = new AmfFile();} +Amf::~Amf() {delete pData;} +Amf::Amf(const Amf& In) {*this = In;} +Amf& Amf::operator=(const Amf& In){*pData = *In.pData; return *this;} + +//Amf I/O +bool Amf::Save(std::string AmfFilePath, bool Compressed){return pData->Save(AmfFilePath, Compressed);} +bool Amf::Load(std::string AmfFilePath, bool StrictLoad){return pData->Load(AmfFilePath, StrictLoad);} +bool Amf::ImportAmf(std::string AmfFilePath, bool StrictLoad) {return pData->ImportAmf(AmfFilePath, StrictLoad);} +void Amf::ClearAll() {pData->ClearAll();} + +//importing meshes +bool Amf::ImportMesh(std::string MeshFilePath, int AmfObjectIndex, int AmfMeshIndex) {return pData->ImportMesh(MeshFilePath, AmfObjectIndex, AmfMeshIndex);} //imports a mesh into a mesh node specified (stl or x3d only) +bool Amf::LoadStl(std::string StlFilePath){return pData->LoadStl(StlFilePath);} //imports an stl into a mesh node specified +bool Amf::GetStlMeshSize(double* XSize, double* YSize, double* ZSize){return pData->GetStlMeshSize(XSize, YSize, ZSize);} +bool Amf::ImportStl(int AmfObjectIndex, int AmfMeshIndex) {return pData->ImportStl(AmfObjectIndex, AmfMeshIndex);} //imports an stl into a mesh node specified +bool Amf::LoadX3d(std::string X3dFilePath, std::string ImagePath, std::string* ImgPathErrorReturn){return pData->LoadX3d(X3dFilePath, ImagePath, ImgPathErrorReturn);} //imports an x3d into a mesh node specified +bool Amf::GetX3dMeshSize(double* XSize, double* YSize, double* ZSize){return pData->GetX3dMeshSize(XSize, YSize, ZSize);} +bool Amf::ImportX3d(int AmfObjectIndex, int AmfMeshIndex){return pData->ImportX3d(AmfObjectIndex, AmfMeshIndex);} //imports an x3d into a mesh node specified + +//exporting meshes +bool Amf::ExportSTL(std::string StlFilePath){return pData->ExportSTL(StlFilePath);} + +//Units +void Amf::SetImportUnits(aUnitSystem Units) {pData->SetImportUnits(TranslateEnum(Units));} +Amf::aUnitSystem Amf::GetUnits(void){return TranslateEnum(pData->GetUnits());} +std::string Amf::GetUnitsString(void){return pData->GetUnitsString();} +std::string Amf::GetUnitsString(aUnitSystem Units){return pData->GetUnitsString(TranslateEnum(Units));} +void Amf::SetUnits(aUnitSystem Units){pData->SetUnits(TranslateEnum(Units));} +double Amf::ConvertUnits(double Value, aUnitSystem OriginalUnits, aUnitSystem DesiredUnits){return pData->ConvertUnits(Value, TranslateEnum(OriginalUnits), TranslateEnum(DesiredUnits));} +double Amf::ToCurrentUnits(double Value, aUnitSystem OriginalUnits){return pData->ToCurrentUnits(Value, TranslateEnum(OriginalUnits));} +double Amf::FromCurrentUnits(double Value, aUnitSystem DesiredUnits){return pData->FromCurrentUnits(Value, TranslateEnum(DesiredUnits));} + +//Size of Amf +double Amf::GetEnvelopeData(aEnvelopeData Data) {return pData->GetEnvelopeData(TranslateEnum(Data));} +bool Amf::GetEnvlMin(double* pXMinOut, double* pYMinOut, double* pZMinOut, int RenderIndex) {return pData->GetEnvlMin(pXMinOut, pYMinOut, pZMinOut, RenderIndex);} +bool Amf::GetEnvlMax(double* pXMaxOut, double* pYMaxOut, double* pZMaxOut, int RenderIndex) {return pData->GetEnvlMax(pXMaxOut, pYMaxOut, pZMaxOut, RenderIndex);} +bool Amf::GetEnvlSize(double* pXSizeOut, double* pYSizeOut, double* pZSizeOut, int RenderIndex) {return pData->GetEnvlSize(pXSizeOut, pYSizeOut, pZSizeOut, RenderIndex);} +bool Amf::GetEnvlRotQuat(double* pWRotOut, double* pXRotOut, double* pYRotOut, double* pZRotOut, int RenderIndex) {return pData->GetEnvlRotQuat(pWRotOut, pXRotOut, pYRotOut, pZRotOut, RenderIndex);} +bool Amf::GetEnvlRotAngleAxis(double* pAngleRadOut, double* pNXOut, double* pNYOut, double* pNZOut, int RenderIndex) {return pData->GetEnvlRotAngleAxis(pAngleRadOut, pNXOut, pNYOut, pNZOut, RenderIndex);} +bool Amf::GetEnvlOrigin(double* pXOriginOut, double* pYOriginOut, double* pZOriginOut, int RenderIndex) {return pData->GetEnvlOrigin(pXOriginOut, pYOriginOut, pZOriginOut, RenderIndex);} +bool Amf::GetEnvlDims(double* pIDimOut, double* pJDimOut, double* pKDimOut, int RenderIndex) {return pData->GetEnvlDims(pIDimOut, pJDimOut, pKDimOut, RenderIndex);} + +bool Amf::Scale(double ScaleFactor, bool ScaleConstellations, bool ScaleEquations){return pData->Scale(ScaleFactor, ScaleConstellations, ScaleEquations);} +bool Amf::Scale(double XScaleFactor, double YScaleFactor, double ZScaleFactor, bool ScaleConstellations, bool ScaleEquations){return pData->Scale(XScaleFactor, YScaleFactor, ZScaleFactor, ScaleConstellations, ScaleEquations);} + +//Amf Objects: +int Amf::GetObjectCount(void) {return pData->GetObjectCount();} +std::string Amf::GetObjectName(int ObjectIndex) {return pData->GetObjectName(ObjectIndex);} +void Amf::RenameObject(int ObjectIndex, std::string NewName){pData->RenameObject(ObjectIndex, NewName);} +int Amf::AddObject(std::string ObjectName){return pData->AddObject(ObjectName);} +void Amf::RemoveObject(int ObjectIndex) {pData->RemoveObject(ObjectIndex);} +void Amf::TranslateObject(int ObjectIndex, double dx, double dy, double dz) {pData->TranslateObject(ObjectIndex, dx, dy, dz);} +void Amf::RotateObject(int ObjectIndex, double rx, double ry, double rz) {pData->RotateObject(ObjectIndex, rx, ry, rz);} + +//Amf Meshes +int Amf::GetMeshCount(int ObjectIndex){return pData->GetMeshCount(ObjectIndex);} + +//Amf Volumes +int Amf::GetVolumeCount(int ObjectIndex, int MeshIndex) {return pData->GetVolumeCount(ObjectIndex, MeshIndex);} +std::string Amf::GetVolumeName(int ObjectIndex, int MeshIndex, int VolumeIndex){return pData->GetVolumeName(ObjectIndex, MeshIndex, VolumeIndex);} +void Amf::RenameVolume(int ObjectIndex, int MeshIndex, int VolumeIndex, std::string NewName){return pData->RenameVolume(ObjectIndex, MeshIndex, VolumeIndex, NewName);} +int Amf::GetVolumeMaterialIndex(int ObjectIndex, int MeshIndex, int VolumeIndex){return pData->GetVolumeMaterialIndex(ObjectIndex, MeshIndex, VolumeIndex);} +bool Amf::SetVolumeMaterialIndex(int ObjectIndex, int MeshIndex, int VolumeIndex, int MaterialIndex){return pData->SetVolumeMaterialIndex(ObjectIndex, MeshIndex, VolumeIndex, MaterialIndex);} + +//Amf Constellations: +int Amf::GetConstellationCount(void) {return pData->GetConstellationCount();} +std::string Amf::GetConstellationName(int ConstellationIndex) {return pData->GetConstellationName(ConstellationIndex);} +void Amf::RenameConstellation(int ConstellationIndex, std::string NewName){pData->RenameConstellation(ConstellationIndex, NewName);} +int Amf::AddConstellation(std::string ConstellationName){return pData->AddConstellation(ConstellationName);} +void Amf::RemoveConstellation(int ConstellationIndex) {pData->RemoveConstellation(ConstellationIndex);} +bool Amf::IsConstellationReferencedBy(int ConstellationIndex, int ConstellationIndexToCheck){return pData->IsConstellationReferencedBy(ConstellationIndex, ConstellationIndexToCheck);} + +//Amf Instances +int Amf::GetInstanceCount(int ConstellationIndex){return pData->GetInstanceCount(ConstellationIndex);} +int Amf::AddInstance(int ConstellationIndex) {return pData->AddInstance(ConstellationIndex);} +void Amf::RemoveInstance(int ConstellationIndex, int InstanceIndex) {pData->RemoveInstance(ConstellationIndex, InstanceIndex);} +bool Amf::SetInstanceObjectIndex(int ConstellationIndex, int InstanceIndex, int InstanceObjectIndex) {return pData->SetInstanceObjectIndex(ConstellationIndex, InstanceIndex, InstanceObjectIndex);} +bool Amf::SetInstanceConstellationIndex(int ConstellationIndex, int InstanceIndex, int InstanceConstellationIndex) {return pData->SetInstanceConstellationIndex(ConstellationIndex, InstanceIndex, InstanceConstellationIndex);} +int Amf::GetInstanceObjectIndex(int ConstellationIndex, int InstanceIndex) {return pData->GetInstanceObjectIndex(ConstellationIndex, InstanceIndex);} +int Amf::GetInstanceConstellationIndex(int ConstellationIndex, int InstanceIndex) {return pData->GetInstanceConstellationIndex(ConstellationIndex, InstanceIndex);} +bool Amf::SetInstanceParam(int ConstellationIndex, int InstanceIndex, aInstanceParamD ParamD, double Value) {return pData->SetInstanceParam(ConstellationIndex, InstanceIndex, TranslateEnum(ParamD), Value);} +double Amf::GetInstanceParam(int ConstellationIndex, int InstanceIndex, aInstanceParamD ParamD) {return pData->GetInstanceParam(ConstellationIndex, InstanceIndex, TranslateEnum(ParamD));} + +//Amf Materials: +int Amf::GetMaterialCount(void) {return pData->GetMaterialCount();} +std::string Amf::GetMaterialName(int MaterialIndex) {return pData->GetMaterialName(MaterialIndex);} +void Amf::RenameMaterial(int MaterialIndex, std::string NewName){pData->RenameMaterial(MaterialIndex, NewName);} +int Amf::AddMaterial(std::string MaterialName){return pData->AddMaterial(MaterialName);} +int Amf::AddMaterial(std::string MaterialName, int Red, int Green, int Blue) {return pData->AddMaterial(MaterialName, Red, Green, Blue);} +int Amf::AddMaterial(std::string MaterialName, double Red, double Green, double Blue) {return pData->AddMaterial(MaterialName, Red, Green, Blue);} +void Amf::RemoveMaterial(int MaterialIndex) {pData->RemoveMaterial(MaterialIndex);} +bool Amf::IsMaterialReferencedBy(int MaterialIndex, int MaterialIndexToCheck) {return pData->IsMaterialReferencedBy(MaterialIndex, MaterialIndexToCheck);} +bool Amf::SetMaterialColorD(int MaterialIndex, double Red, double Green, double Blue){return pData->SetMaterialColorD(MaterialIndex, Red, Green, Blue);} +bool Amf::SetMaterialColorI(int MaterialIndex, int Red, int Green, int Blue){return pData->SetMaterialColorI(MaterialIndex, Red, Green, Blue);} +bool Amf::GetMaterialColorD(int MaterialIndex, double *Red, double *Green, double *Blue){return pData->GetMaterialColorD(MaterialIndex, Red, Green, Blue);} +bool Amf::GetMaterialColorI(int MaterialIndex, int *Red, int *Green, int *Blue){return pData->GetMaterialColorI(MaterialIndex, Red, Green, Blue);} + +//Amf Composites +int Amf::GetCompositeCount(int MaterialIndex){return pData->GetCompositeCount(MaterialIndex);} +void Amf::ClearComposites(int MaterialIndex){pData->ClearComposites(MaterialIndex);} +int Amf::AddComposite(int MaterialIndex, int MaterialIndexToComposite){return pData->AddComposite(MaterialIndex, MaterialIndexToComposite);} +void Amf::RemoveComposite(int MaterialIndex, int CompositeIndex){pData->RemoveComposite(MaterialIndex, CompositeIndex);} +bool Amf::SetCompositeMaterialIndex(int MaterialIndex, int CompositeIndex, int CompositeMaterialIndex){return pData->SetCompositeMaterialIndex(MaterialIndex, CompositeIndex, CompositeMaterialIndex);} //1-based (because 0 always VOID material +int Amf::GetCompositeMaterialIndex(int MaterialIndex, int CompositeIndex){return pData->GetCompositeMaterialIndex(MaterialIndex, CompositeIndex);} //1-based (because 0 always VOID material +std::string Amf::GetCompositeEquation(int MaterialIndex, int CompositeIndex){return pData->GetCompositeEquation(MaterialIndex, CompositeIndex);} //use! ToAmfString() +bool Amf::SetCompositeEquation(int MaterialIndex, int CompositeIndex, std::string Equation) {return pData->SetCompositeEquation(MaterialIndex, CompositeIndex, Equation);} + +//Amf Textures: +int Amf::GetTextureCount(void) {return pData->GetTextureCount();} + +//Output utilities +bool Amf::SetSubdivisionLevel(int Level){return pData->SetSubdivisionLevel(Level);} +void Amf::DrawGL(){pData->DrawGL();} +unsigned char* Amf::GetSliceBitmapRGBA(double PixelSizeX, double PixelSizeY, double SliceHeightZ, int* XSizeOut, int* YSizeOut, double SurfaceDepth){return pData->GetSliceBitmapRGBA(PixelSizeX, PixelSizeY, SliceHeightZ, XSizeOut, YSizeOut, SurfaceDepth);} +int* Amf::GetSliceSegmentsXY(double ZHeight, int* NumSegmentsOut){return pData->GetSliceSegmentsXY(ZHeight, NumSegmentsOut);} + +//Errors and information +std::string Amf::GetInfoString(bool MeshInfo) {return pData->GetInfoString(MeshInfo);} +std::string* Amf::pLastErrorMsg(){return pData->pLastErrorMsg();} +std::string Amf::GetLastErrorMsg(){return pData->GetLastErrorMsg();} + +//Real time status info on long i/o operations +bool* Amf::pCancelIO(){return pData->pCancelIO();} +int* Amf::pCurTick(){return pData->pCurTick();} +int* Amf::pMaxTick(){return pData->pMaxTick();} +std::string* Amf::pStatusMsg(){return pData->pStatusMsg();} diff --git a/libraries/amf/amftools-code/src/Equation.cpp b/libraries/amf/amftools-code/src/Equation.cpp new file mode 100644 index 00000000..bdb57744 --- /dev/null +++ b/libraries/amf/amftools-code/src/Equation.cpp @@ -0,0 +1,273 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "Equation.h" +#include "nTexture.h" +#include "nAmf.h" +#include +#ifdef WIN32 +//for UINT_MAX (windows finds automagically) +#else +#include //for UINT_MAX +#endif + +nAmf* CEquation::pAmfStatic = NULL; + +CEquation::CEquation(void) +{ + pP = NULL; + pEqCache = NULL; + Clear(); +} + + +CEquation::~CEquation(void) +{ + if (pP) delete pP; +// pP = NULL; + if (pEqCache) delete pEqCache; +// pEqCache = NULL; +} + +void CEquation::Clear(void) +{ + XVar = 0; + YVar = 0; + ZVar = 0; + + IsConstant = true; + ConstantValue = 0.0; + + pAmf = NULL; + + if (pP) delete pP; + pP = NULL; + if (pEqCache) delete pEqCache; + pEqCache = NULL; + +} + +CEquation& CEquation::operator=(const CEquation& In) +{ + if (pP) delete pP; + pP = NULL; + if (pEqCache) delete pEqCache; + pEqCache = NULL; + + IsConstant = In.IsConstant; + ConstantValue = In.ConstantValue; + + pAmf = In.pAmf; + + std::string tmp = In.ToAmfString(); + if (In.pP) FromAmfString(tmp, In.pAmf); + + + return *this; +} + +void CEquation::FromConstant(double Value) //set the equation to a constant, non-varying value +{ + IsConstant = true; + ConstantValue = Value; +} + +void CEquation::FromAmfString(std::string& EqIn, nAmf* pAmfIn) +{ + if (EqIn == "") return; //catch invalid equations! + + pAmf = pAmfIn; + + //figure out if its constant + if (EqIn.find_first_of("(^*/%+-=<>&|\\!xXyYzZ") == EqIn.npos){ //if we don't find any of these... (this may be inefficient...) + IsConstant = true; + ConstantValue = atof(EqIn.c_str()); + } + else { //is an actual equation... + pEqCache = new std::string(EqIn); + IniParser(); + pP->SetExpr(Amf2MuParser(EqIn)); + IsConstant = false; + + } +} + +std::string CEquation::ToAmfString(void) const +{ + if (IsConstant){ + std::ostringstream strs; + strs << ConstantValue; + return strs.str(); + } + else if (pEqCache) return *pEqCache; + else return ""; +} + +bool CEquation::CheckParse(std::string* pMessage) //tries an evaluation to see if equation is valid... +{ + pAmfStatic = pAmf; //make sure MuParser uses the right set of textures... + + if (IsConstant) return true; + else { //if evaluating equation + if (!pP){ + if (pMessage) *pMessage += "Error: Equation parser not initialized\n"; + return false; //parser has not been initialized yet... + } + + XVar = 0; + YVar = 0; + ZVar = 0; + double result; + try{result = pP->Eval();} + catch (mu::Parser::exception_type &e){ + if (pMessage) *pMessage += "Error: " + e.GetMsg() + "\n"; + return false; + } + return true; + } +} + +double CEquation::Eval(double x, double y, double z, bool UnitRange) //Evaluates at specified location. UnitRange truncates to between 0 and 1. +{ + + pAmfStatic = pAmf; //make sure MuParser uses the right set of textures... + + if (IsConstant) return ConstantValue; + else { //if evaluating equation + if (!pP) return 0; //parser has not been initialized yet... + + XVar = x; + YVar = y; + ZVar = z; + double result; + try{result = pP->Eval();} + catch (mu::Parser::exception_type &e){ + return 0; + } + + + if (UnitRange){ + if (result<0) result = 0; + if (result > 1.0) result = 1.0; + } + return result; + } +} + +void CEquation::Scale(double ScaleFactor) +{ + //TODO: do manual equation simplification to avoid bloat scaling back and forth? + + std::ostringstream strs; + strs << 1.0/ScaleFactor; + std::string Factor = strs.str(); + + std::string TmpEq = ToAmfString(); + findAndReplace(TmpEq, "x", "("+Factor+"*x)"); + findAndReplace(TmpEq, "X", "("+Factor+"*X)"); + findAndReplace(TmpEq, "y", "("+Factor+"*y)"); + findAndReplace(TmpEq, "Y", "("+Factor+"*Y)"); + findAndReplace(TmpEq, "z", "("+Factor+"*z)"); + findAndReplace(TmpEq, "Z", "("+Factor+"*Z)"); + FromAmfString(TmpEq, pAmf); +} + +void CEquation::IniParser() +{ + if (!pP){ //if not already initialized... + pP = new mu::Parser; + + pP->DefineOprt("%", CEquation::Mod, 6); + pP->DefineOprt("&", AND, 1); + pP->DefineOprt("|", OR, 1); + // pP->DefineOprt("\\", XOR, 1); // = "\" with escape sequence + pP->DefineInfixOprt("!", NOT); + pP->DefineFun("floor", &CEquation::Floor, false); + pP->DefineFun("ceil", &CEquation::Ceil, false); + pP->DefineFun("abs", &CEquation::Abs, false); + pP->DefineFun("rand", &CEquation::Rand, false); + pP->DefineFun("tex", &CEquation::Tex, false); + + pP->DefineVar("x", &XVar); + pP->DefineVar("y", &YVar); + pP->DefineVar("z", &ZVar); + } + +} + + +std::string CEquation::Amf2MuParser(std::string& Equation) //translates from AMF syntax to MuParser syntax +{ + std::string tmp = Equation; + findAndReplace(tmp, "=", "=="); //replace isEqual operator + +// other filtering here of bad AMF equations.... + + return tmp; +} + +std::string CEquation::MuParser2Amf(std::string& Equation) //translates from MuParser syntax to AMF syntax +{ + return ""; +} + +double CEquation::texture(int textureID, double uIn, double vIn, double wIn) +{ + return pAmfStatic->GetTextureByID(textureID)->GetValue(uIn, vIn, wIn); +} + + +//Pseudo-random spatial map stuff: +unsigned long int CEquation::rand_seed(unsigned long int x) { + return (1664525*x+1013904223) & 0x7fffffffUL; +} + +unsigned long int CEquation::taus_get(taus_state* state){ + unsigned long b; + b = (((state->s1 << 13UL) & 0xffffffffUL) ^ state->s1) >> 19UL; + state->s1 = (((state->s1 & 0xfffffffeUL) << 12UL) & 0xffffffffUL) ^ b; + b = (((state->s2 << 2UL) & 0xffffffffUL) ^ state->s2) >> 25UL; + state->s2 = (((state->s2 & 0xfffffff8UL) << 4UL) & 0xffffffffUL) ^ b; + b = (((state->s3 << 3UL) & 0xffffffffUL) ^ state->s3) >> 11UL; + state->s3 = (((state->s3 & 0xfffffff0UL) << 17UL) & 0xffffffffUL) ^ b; + return (state->s1 ^ state->s2 ^ state->s3); +} + +double CEquation::prsm(double x, double y, double z, int k) +{ + taus_state state; + + float tx, ty, tz; + tx = (float) x; + ty = (float) y; + tz = (float) z; + + /* Convert floating point numbers to ints*/ + unsigned long int ts1 = *(unsigned int*)&tx; + unsigned long int ts2 = *(unsigned int*)&ty; + unsigned long int ts3 = *(unsigned int*)&tz; + + /* Convert coordinates to random seeds */ + state.s1 = rand_seed(ts1); + state.s2 = rand_seed(ts2); + state.s3 = rand_seed(ts3); + + state.s1 = rand_seed(state.s1 ^ state.s3); + state.s2 = rand_seed(state.s2 ^ state.s1); + state.s3 = rand_seed(state.s3 ^ state.s2); + + state.s1 = rand_seed(state.s1 ^ state.s3); + state.s2 = rand_seed(state.s2 ^ state.s1); + state.s3 = rand_seed(state.s3 ^ state.s2); + + /* "warm up" generator and generate k-th number */ + for (int i=0; i for license details. +*******************************************************************************/ + +#include "Mesh.h" +#include +#include + +#ifdef USE_OPEN_GL +#ifdef WIN32 + #include +#endif + +#include + + +#endif + +#define STL_LABEL_SIZE 80 + +CMesh::CMesh(void) +{ + Clear(); +} + +CMesh::~CMesh(void) +{ +} + +//copy constructure +CMesh::CMesh(CMesh& s) { + *this = s; +} + +//overload = +CMesh& CMesh::operator=(const CMesh& s) { + + Facets.resize(s.Facets.size()); + for (int i = 0; i<(int)Facets.size(); i++) + Facets[i] = s.Facets[i]; + + Vertices.resize(s.Vertices.size()); + for (int i = 0; i<(int)Vertices.size(); i++) + Vertices[i] = s.Vertices[i]; + + Lines.resize(s.Lines.size()); + for (int i = 0; i<(int)Lines.size(); i++) + Lines[i] = s.Lines[i]; + + Textures = s.Textures; + //Textures.resize(s.Textures.size()); + //for (int i = 0; i<(int)Textures.size(); i++) + // Textures[i] = s.Textures[i]; + + DrawNormals = s.DrawNormals; + DrawTextures = s.DrawTextures; + DrawSmooth = s.DrawSmooth; + DrawEdges = s.DrawEdges; + DrawShaded = s.DrawShaded; + IgnoreNames = s.IgnoreNames; + + BodyColor = s.BodyColor; //default base color + BoundBoxColor = s.BoundBoxColor; //default bounding box color + + _CurBBMin = s._CurBBMin; + _CurBBMax = s._CurBBMax; + GlNameIndexStack = s.GlNameIndexStack; + + NeedBBCalc = s.NeedBBCalc; + + MeshChanged(); + + return *this; +} + +void CMesh::Clear() +{ + Facets.clear(); + Vertices.clear(); + Lines.clear(); + Textures.clear(); + + DrawNormals = false; + DrawTextures = true; + DrawSmooth = true; + DrawEdges = false; + DrawShaded = true; + IgnoreNames = false; + + + BodyColor = CColor(1, 1, 1, 1); //default base color + BoundBoxColor = CColor(0, 0, 0, 1); //default bounding box color + + _CurBBMin = Vec3D(0,0,0); + _CurBBMax = Vec3D(0,0,0); + + NeedBBCalc = false; + + GlNameIndexStack.clear(); +} + +void CMesh::WriteXML(CXmlStreamWrite* pXML, bool MeshOnly) +{ + pXML->OpenElement("CMesh"); + pXML->SetElementB("DrawSmooth", DrawSmooth); + pXML->OpenElement("BodyColor"); + pXML->SetElementD("R", BodyColor.r); + pXML->SetElementD("G", BodyColor.g); + pXML->SetElementD("B", BodyColor.b); + pXML->SetElementD("A", BodyColor.a); + pXML->CloseElement(); + pXML->OpenElement("Vertices"); + std::vector::iterator VIt; + for(VIt=Vertices.begin(); VIt != Vertices.end(); VIt++){ + pXML->OpenElement("Vertex"); + pXML->SetElementD("Vx", VIt->v.x); + pXML->SetElementD("Vy", VIt->v.y); + pXML->SetElementD("Vz", VIt->v.z); + if (!MeshOnly){ + if (VIt->n != Vec3D(0,0,0)){ + pXML->SetElementD("Nx", VIt->n.x); + pXML->SetElementD("Ny", VIt->n.y); + pXML->SetElementD("Nz", VIt->n.z); + } + pXML->SetElementD("R", VIt->VColor.r); + pXML->SetElementD("G", VIt->VColor.g); + pXML->SetElementD("B", VIt->VColor.b); + pXML->SetElementD("A", VIt->VColor.a); +// if (VIt->DrawOffset != Vec3D(0,0,0)){ +// pXML->SetElementD("DOx", VIt->DrawOffset.x); +// pXML->SetElementD("DOy", VIt->DrawOffset.y); +// pXML->SetElementD("DOz", VIt->DrawOffset.z); +// } + } + pXML->CloseElement(); + } + pXML->CloseElement(); + + pXML->OpenElement("Facets"); + std::vector::iterator FIt; + for(FIt=Facets.begin(); FIt != Facets.end(); FIt++){ + pXML->OpenElement("Facet"); + pXML->SetElementI("V0", FIt->vi[0]); + pXML->SetElementI("V1", FIt->vi[1]); + pXML->SetElementI("V2", FIt->vi[2]); + if (!MeshOnly){ + if (FIt->n != Vec3D(0,0,0)){ + pXML->SetElementD("Nx", FIt->n.x); + pXML->SetElementD("Ny", FIt->n.y); + pXML->SetElementD("Nz", FIt->n.z); + } + pXML->SetElementD("R", FIt->FColor.r); + pXML->SetElementD("G", FIt->FColor.g); + pXML->SetElementD("B", FIt->FColor.b); + pXML->SetElementD("A", FIt->FColor.a); + pXML->SetElementI("Name", FIt->Name); + } + pXML->CloseElement(); + } + pXML->CloseElement(); + + pXML->OpenElement("Lines"); + std::vector::iterator LIt; + for(LIt=Lines.begin(); LIt != Lines.end(); LIt++){ + pXML->OpenElement("Line"); + pXML->SetElementI("V0", LIt->vi[0]); + pXML->SetElementI("V1", LIt->vi[1]); + pXML->CloseElement(); + } + pXML->CloseElement(); + pXML->CloseElement(); +} + +bool CMesh::ReadXML(CXmlStreamRead* pXML) +{ + Clear(); + + if (!pXML->GetElementB("DrawSmooth", &DrawSmooth)) DrawSmooth = false; + if (pXML->OpenElement("BodyColor")){ + if (!pXML->GetElementD("R", &BodyColor.r)) BodyColor.r = 1.0; + if (!pXML->GetElementD("G", &BodyColor.g)) BodyColor.g = 1.0; + if (!pXML->GetElementD("B", &BodyColor.b)) BodyColor.b = 1.0; + if (!pXML->GetElementD("A", &BodyColor.a)) BodyColor.a = 1.0; + pXML->CloseElement(); + } + CVertex tmp; + if (pXML->OpenElement("Vertices")){ + while (pXML->OpenElement("Vertex", true)){ + if (!pXML->GetElementD("Vx", &tmp.v.x)) tmp.v.x = 0.0; + if (!pXML->GetElementD("Vy", &tmp.v.y)) tmp.v.y = 0.0; + if (!pXML->GetElementD("Vz", &tmp.v.z)) tmp.v.z = 0.0; + if (!pXML->GetElementD("Nx", &tmp.n.x)) tmp.n.x = 0.0; + if (!pXML->GetElementD("Ny", &tmp.n.y)) tmp.n.y = 0.0; + if (!pXML->GetElementD("Nz", &tmp.n.z)) tmp.n.z = 0.0; + if (!pXML->GetElementD("R", &tmp.VColor.r)) tmp.VColor.r = 1.0; + if (!pXML->GetElementD("G", &tmp.VColor.g)) tmp.VColor.g = 1.0; + if (!pXML->GetElementD("B", &tmp.VColor.b)) tmp.VColor.b = 1.0; + if (!pXML->GetElementD("A", &tmp.VColor.a)) tmp.VColor.a = 1.0; +// if (!pXML->GetElementD("DOx", &tmp.DrawOffset.x)) tmp.DrawOffset.x = 0.0; +// if (!pXML->GetElementD("DOy", &tmp.DrawOffset.y)) tmp.DrawOffset.y = 0.0; +// if (!pXML->GetElementD("DOz", &tmp.DrawOffset.z)) tmp.DrawOffset.z = 0.0; + Vertices.push_back(tmp); + } + pXML->CloseElement(); + } + + CFacet Ftmp; + if (pXML->OpenElement("Facets")){ + while (pXML->OpenElement("Facet", true)){ + if (!pXML->GetElementI("V0", &Ftmp.vi[0])) Ftmp.vi[0] = 0; + if (!pXML->GetElementI("V1", &Ftmp.vi[1])) Ftmp.vi[1] = 0; + if (!pXML->GetElementI("V2", &Ftmp.vi[2])) Ftmp.vi[2] = 0; + if (!pXML->GetElementD("Nx", &Ftmp.n.x)) Ftmp.n.x = 0.0; + if (!pXML->GetElementD("Ny", &Ftmp.n.y)) Ftmp.n.y = 0.0; + if (!pXML->GetElementD("Nz", &Ftmp.n.z)) Ftmp.n.z = 0.0; + if (!pXML->GetElementD("R", &Ftmp.FColor.r)) Ftmp.FColor.r = 1.0; + if (!pXML->GetElementD("G", &Ftmp.FColor.g)) Ftmp.FColor.g = 1.0; + if (!pXML->GetElementD("B", &Ftmp.FColor.b)) Ftmp.FColor.b = 1.0; + if (!pXML->GetElementD("A", &Ftmp.FColor.a)) Ftmp.FColor.a = 1.0; + if (!pXML->GetElementI("Name", &Ftmp.Name)) Ftmp.Name = -1; + + Facets.push_back(Ftmp); + } + pXML->CloseElement(); + + } + + CLine Ltmp; + if (pXML->OpenElement("Lines")){ + while (pXML->OpenElement("Line", true)){ + if (!pXML->GetElementI("V0", &Ltmp.vi[0])) Ltmp.vi[0] = 0; + if (!pXML->GetElementI("V1", &Ltmp.vi[1])) Ltmp.vi[1] = 0; + Lines.push_back(Ltmp); + } + pXML->CloseElement(); + + } + + NeedBBCalc = true; + + CalcFaceNormals(); + CalcVertNormals(); + + return true; +} + + +bool CMesh::LoadSTL(std::string filename) +{ + FILE *fp; + bool binary=false; +#ifdef WIN32 + fopen_s(&fp, filename.c_str(), "r"); //secure version. preferred on windows platforms... +#else + fp = fopen(filename.c_str(), "r"); +#endif + + if(fp == NULL) return false; + + /* Find size of file */ + fseek(fp, 0, SEEK_END); + int file_size = ftell(fp); + int facenum; + /* Check for binary or ASCII file */ + fseek(fp, STL_LABEL_SIZE, SEEK_SET); + fread(&facenum, sizeof(int), 1, fp); + int expected_file_size=STL_LABEL_SIZE + 4 + (sizeof(short)+12*sizeof(float) )*facenum ; + if(file_size == expected_file_size) binary = true; + unsigned char tmpbuf[128]; + fread(tmpbuf,sizeof(tmpbuf),1,fp); + for(unsigned int i = 0; i < sizeof(tmpbuf); i++){ + if(tmpbuf[i] > 127){ + binary=true; + break; + } + } + // Now we know if the stl file is ascii or binary. + fclose(fp); + bool RetVal; + if(binary) RetVal = LoadBinarySTL(filename); + else RetVal = LoadAsciiSTL(filename); + +// UpdateBoundingBox(); //get the bounding box here and now... + NeedBBCalc = true; //AddFacet should have set this, but just to make sure! + MeshChanged(); + return RetVal; +} + +bool CMesh::LoadBinarySTL(std::string filename) +{ + FILE *fp; + +#ifdef WIN32 + fopen_s(&fp, filename.c_str(), "rb"); //secure version. preferred on windows platforms... +#else + fp = fopen(filename.c_str(), "rb"); +#endif + + + if(fp == NULL) return false; + + int facenum; + fseek(fp, STL_LABEL_SIZE, SEEK_SET); + fread(&facenum, sizeof(int), 1, fp); + + Clear(); + + // For each triangle read the normal, the three coords and a short set to zero + float N[3]; + float P[9]; + short attr; + + for(int i=0;i "facet normal 0 0 0" (We throw this out and recalculate based on vertices) + if(ret!=3){ + // we could be in the case of a multiple solid object, where after a endfaced instead of another facet we have to skip two lines: + // endloop + // endfacet + //endsolid <- continue on ret==0 will skip this line + //solid ascii <- and this one. + // facet normal 0.000000e+000 7.700727e-001 -6.379562e-001 + lineCnt++; + continue; + } + ret=fscanf(fp, "%*s %*s"); // --> "outer loop" + ret=fscanf(fp, "%*s %f %f %f\n", &P[0], &P[1], &P[2]); // --> "vertex x y z" + if(ret!=3) return false; + ret=fscanf(fp, "%*s %f %f %f\n", &P[3], &P[4], &P[5]); // --> "vertex x y z" + if(ret!=3) return false; + ret=fscanf(fp, "%*s %f %f %f\n", &P[6], &P[7], &P[8]); // --> "vertex x y z" + if(ret!=3) return false; + ret=fscanf(fp, "%*s"); // --> "endloop" + ret=fscanf(fp, "%*s"); // --> "endfacet" + lineCnt+=7; + if(feof(fp)) break; + + AddFacet(Vec3D(P[0], P[1], P[2]), Vec3D(P[3], P[4], P[5]), Vec3D(P[6], P[7], P[8])); + + } + fclose(fp); + + CalcFaceNormals(); + + return true; +} + + +bool CMesh::SaveSTL(std::string filename, bool Binary) const { //writes ascii stl file... + + FILE *fp; + +#ifdef WIN32 + if (Binary) fopen_s (&fp, filename.c_str(),"wb"); //secure version. preferred on windows platforms... + else fopen_s(&fp, filename.c_str(),"w"); +#else + if (Binary) fp = fopen(filename.c_str(),"wb"); + else fp = fopen(filename.c_str(),"w"); +#endif + + if(fp==0) return false; + int NumFaces = (int)Facets.size(); + + if(Binary){ + // Write Header + std::string tmp = "DefaultSTL "; + //char header[128]=; + fwrite(tmp.c_str(),80,1,fp); + // write number of facets + fwrite(&NumFaces,1,sizeof(int),fp); + unsigned short attributes=0; + + for(int i=0; i::iterator it = Facets.begin(); it != Facets.end(); it++){ + + if (DrawTextures && it->HasTexture){ + glEnable(GL_TEXTURE_2D); + + int ThisTexInd = it->Map.TexIndex; + if (ThisTexInd != CurTex && ThisTexInd < NumTex){ + glBindTexture(GL_TEXTURE_2D, Textures[ThisTexInd].TexName()); // TexNames[ThisTexInd]); + Textures[ThisTexInd].SetGlBorderColor(BodyColor.r, BodyColor.g, BodyColor.b, BodyColor.a); + CurTex = ThisTexInd; + } + + } + + glBegin(GL_TRIANGLES); + if (!DrawSmooth){ //if setting things per triangle, can do normal and color here... + glNormal3d(it->n.x, it->n.y, it->n.z); +// if (!bIngoreColors) glColor3d(Facets[i].FColor.r, Facets[i].FColor.g, Facets[i].FColor.b); + } + for (int j=0; j<3; j++) { + CVertex& CurVert = Vertices[it->vi[j]]; //just a local reference for readability + + if (DrawSmooth){ //if we want to draw smoothed normals/colors per vertex + glNormal3d(CurVert.n.x, CurVert.n.y, CurVert.n.z); + } + + if (CurVert.HasColor) glColor3d(CurVert.VColor.r, CurVert.VColor.g, CurVert.VColor.b); + if (DrawTextures && it->HasTexture){ + glTexCoord2d(it->Map.uc[j], it->Map.vc[j]); + glColor3f(1.0, 1.0, 1.0); //white background for all textured polys so they don't get tinted + } + + glVertex3d(CurVert.v.x/* + CurVert.DrawOffset.x*/, CurVert.v.y/* + CurVert.DrawOffset.y*/, CurVert.v.z/* + CurVert.DrawOffset.z*/); + + } + glEnd(); + + + glDisable(GL_TEXTURE_2D); //end textures... +// if (!bIgnoreNames && it->HasName) glPopName(); + + } + + //draw any lines that are defined.... + if (DrawEdges){ + glLineWidth(1.0); + + glBegin(GL_LINES); + glColor3d(0, 0, 0); //black only for now... + + for (int i=0; i<(int)Lines.size(); i++) { + for (int j=0; j<2; j++) { + CVertex& CurVert = Vertices[Lines[i].vi[j]]; //just a local reference for readability + glVertex3d(CurVert.v.x/* + CurVert.DrawOffset.x*/, CurVert.v.y/* + CurVert.DrawOffset.y*/, CurVert.v.z/* + CurVert.DrawOffset.z*/); + } + } + glEnd(); + } + + } + else { // wireframe + for (int i=0; i<(int)Facets.size(); i++) { + glBegin(GL_LINE_LOOP); + glNormal3d(Facets[i].n.x, Facets[i].n.y, Facets[i].n.z); + for (int j=0; j<3; j++) { + CVertex& CurVert = Vertices[Facets[i].vi[j]]; //just a local reference for readability + glColor3d(CurVert.VColor.r, CurVert.VColor.g, CurVert.VColor.b); + glVertex3d(CurVert.v.x/* + CurVert.DrawOffset.x*/, CurVert.v.y/* + CurVert.DrawOffset.y*/, CurVert.v.z/* + CurVert.DrawOffset.z*/); + } + glEnd(); + } + } + + if (DrawNormals) { + glColor3d(1,1,0); + glBegin(GL_LINES); + for (int i=0; i<(int)Facets.size(); i++) { + Vec3D c = (Vertices[Facets[i].vi[0]].v + Vertices[Facets[i].vi[1]].v + Vertices[Facets[i].vi[2]].v)/3; + Vec3D c2 = c - Facets[i].n*3; + glVertex3d(c.x, c.y, c.z); + glVertex3d(c2.x, c2.y, c2.z); + } + glEnd(); + } + + /* + //draw bounding box? + if (DrawBoundingBox){ + //todo: manage bounding box recalculation automatically + glColor3d(BoundBoxColor.r,BoundBoxColor.g,BoundBoxColor.b); + glBegin(GL_LINE_LOOP); + //bottom square + glVertex3d(_CurBBMin.x, _CurBBMin.y, _CurBBMin.z); + glVertex3d(_CurBBMin.x, _CurBBMax.y, _CurBBMin.z); + glVertex3d(_CurBBMax.x, _CurBBMax.y, _CurBBMin.z); + glVertex3d(_CurBBMax.x, _CurBBMin.y, _CurBBMin.z); + glVertex3d(_CurBBMin.x, _CurBBMin.y, _CurBBMin.z); + + glVertex3d(_CurBBMin.x, _CurBBMin.y, _CurBBMax.z); //up to top square + glVertex3d(_CurBBMin.x, _CurBBMax.y, _CurBBMax.z); + glVertex3d(_CurBBMax.x, _CurBBMax.y, _CurBBMax.z); + glVertex3d(_CurBBMax.x, _CurBBMin.y, _CurBBMax.z); + glVertex3d(_CurBBMin.x, _CurBBMin.y, _CurBBMax.z); + + glEnd(); + + glBegin(GL_LINES); + glVertex3d(_CurBBMin.x, _CurBBMax.y, _CurBBMin.z); + glVertex3d(_CurBBMin.x, _CurBBMax.y, _CurBBMax.z); + glVertex3d(_CurBBMax.x, _CurBBMax.y, _CurBBMin.z); + glVertex3d(_CurBBMax.x, _CurBBMax.y, _CurBBMax.z); + glVertex3d(_CurBBMax.x, _CurBBMin.y, _CurBBMin.z); + glVertex3d(_CurBBMax.x, _CurBBMin.y, _CurBBMax.z); + glEnd(); + + } + */ +// delete [] TexNames; + + if (!IgnoreNames) for (int i=0; i=0; k--){ //DO THIS BACKWARDS!!!! (more likely to have just added one next to us...) + if (abs(Points[j].x - Vertices[k].v.x) < WeldThresh && abs(Points[j].y - Vertices[k].v.y) < WeldThresh && abs(Points[j].z - Vertices[k].v.z) < WeldThresh){ //if points are identical... + FoundIndex[j] = k; + break; //kicks out of for loop, because we've found! + } + } + } + + if (FoundIndex[j] == -1){ //if we didn't find one... + CVertex ThisPoint; + ThisPoint.v.x = Points[j].x; + ThisPoint.v.y = Points[j].y; + ThisPoint.v.z = Points[j].z; + ThisPoint.VColor = Colors[j]; + + Vertices.push_back(ThisPoint); + FoundIndex[j] = (int)Vertices.size() - 1; //-1 because zero-index based. + + //TODO fail gracefully if we run out of memory so that vector can't allocate. + + } + + } + + + +// CFacet ThisFacet; +// for (int m=0; m<3; m++) ThisFacet.vi[m] = FoundIndex[m]; + + Facets.push_back(CFacet(FoundIndex[0], FoundIndex[1], FoundIndex[2])); //TODO... select whether to create new object or add to existing... + NeedBBCalc = true; + MeshChanged(); + return &Facets.back(); + +} + +CFacet* CMesh::AddFacet(const Vec3D& v1, const Vec3D& v2, const Vec3D& v3, const TexMap& MapIn) //adds a facet... with color info +{ + CFacet* pFacet = AddFacet(v1, v2, v3, true); + pFacet->HasTexture = true; + pFacet->Map = MapIn; + return pFacet; +} + +CFacet* CMesh::AddFacet(const CVertex& v1, const CVertex& v2, const CVertex& v3) +{ + Vertices.push_back(v1); + Vertices.push_back(v2); + Vertices.push_back(v3); + + Facets.push_back(CFacet((int)Vertices.size() - 3, (int)Vertices.size() - 2, (int)Vertices.size() - 1)); //TODO... select whether to create new object or add to existing... + NeedBBCalc = true; + MeshChanged(); + return &Facets.back(); +} + +CFacet* CMesh::AddFacet(const CVertex& v1, const CVertex& v2, const CVertex& v3, const TexMap& MapIn) //adds a facet... with texture map +{ + CFacet* pFacet = AddFacet(v1, v2, v3); + pFacet->HasTexture = true; + pFacet->Map = MapIn; + return pFacet; +} + +////--------------------------------------------------------------------------- +//void CMesh::ComputeBoundingBox(Vec3D &pmin, Vec3D &pmax) +////--------------------------------------------------------------------------- +//{ +// UpdateBoundingBox(); +// pmin = _CurBBMin; +// pmax = _CurBBMax; +// +//} + +//--------------------------------------------------------------------------- +void CMesh::UpdateBoundingBox(void) +//--------------------------------------------------------------------------- +{ + if (Vertices.size() == 0){ + _CurBBMin = _CurBBMax = Vec3D(0,0,0); + return; + } + + _CurBBMin = _CurBBMax = Vertices[0].v; + + for (int i=0; i<(int)Vertices.size(); i++) { + _CurBBMin.x = _CurBBMin.x < Vertices[i].v.x ? _CurBBMin.x : Vertices[i].v.x; + _CurBBMin.y = _CurBBMin.y < Vertices[i].v.y ? _CurBBMin.y : Vertices[i].v.y; + _CurBBMin.z = _CurBBMin.z < Vertices[i].v.z ? _CurBBMin.z : Vertices[i].v.z; + _CurBBMax.x = _CurBBMax.x > Vertices[i].v.x ? _CurBBMax.x : Vertices[i].v.x; + _CurBBMax.y = _CurBBMax.y > Vertices[i].v.y ? _CurBBMax.y : Vertices[i].v.y; + _CurBBMax.z = _CurBBMax.z > Vertices[i].v.z ? _CurBBMax.z : Vertices[i].v.z; + } +} + + + +//--------------------------------------------------------------------------- +void CMesh::Translate(Vec3D d) +//--------------------------------------------------------------------------- +{// translate geometry + for (int i=0; i<(int)Vertices.size(); i++) { + Vertices[i].v += d; + } + NeedBBCalc = true; + MeshChanged(); +} + +//--------------------------------------------------------------------------- +void CMesh::Scale(Vec3D s) +//--------------------------------------------------------------------------- +{// scale geometry + + //check for zero scale factor + if(s.x==0 || s.y==0 || s.z==0) return; + for (int i=0; i<(int)Vertices.size(); i++) { + Vertices[i].v.x *= s.x; + Vertices[i].v.y *= s.y; + Vertices[i].v.z *= s.z; +// Vertices[i].n.x *= s.x; //do we really want to scale these? +// Vertices[i].n.y *= s.y; +// Vertices[i].n.z *= s.z; +/// Facets[i].n.Normalize(); + } + NeedBBCalc = true; + MeshChanged(); + +} + +//--------------------------------------------------------------------------- +void CMesh::Rotate(Vec3D ax, double a) +//--------------------------------------------------------------------------- +{ + Rotate(CQuat(a, ax)); + //for (int i=0; i<(int)Vertices.size(); i++) { + // Vertices[i].v = Vertices[i].v.Rot(ax, a); + // Vertices[i].n = Vertices[i].n.Rot(ax, a); + // Vertices[i].DrawOffset = Vertices[i].DrawOffset.Rot(ax, a); + + // + //} + //for (int i=0; i<(int)Facets.size(); i++) { + // Facets[i].n = Facets[i].n.Rot(ax, a); + //} + + //UpdateBoundingBox(); + //MeshChanged(); + +} + +//--------------------------------------------------------------------------- +void CMesh::Rotate(CQuat QRot) +//--------------------------------------------------------------------------- +{ + + for (int i=0; i<(int)Vertices.size(); i++) { + Vertices[i].v = Vertices[i].v.Rot(QRot); + Vertices[i].n = Vertices[i].n.Rot(QRot); +// Vertices[i].DrawOffset = Vertices[i].DrawOffset.Rot(QRot); + + + } + for (int i=0; i<(int)Facets.size(); i++) { + Facets[i].n = Facets[i].n.Rot(QRot); + } + + NeedBBCalc = true; + MeshChanged(); + +} + + +//--------------------------------------------------------------------------- +void CMesh::RotX(double a) +//--------------------------------------------------------------------------- +{ + for (int i=0; i<(int)Vertices.size(); i++) { + Vertices[i].v.RotX(a); + Vertices[i].n.RotX(a); + } + for (int i=0; i<(int)Facets.size(); i++) { + Facets[i].n.RotX(a); + } + + NeedBBCalc = true; + MeshChanged(); + +} + + +//--------------------------------------------------------------------------- +void CMesh::RotY(double a) +//--------------------------------------------------------------------------- +{ + for (int i=0; i<(int)Vertices.size(); i++) { + Vertices[i].v.RotY(a); + Vertices[i].n.RotY(a); + } + for (int i=0; i<(int)Facets.size(); i++) { + Facets[i].n.RotY(a); + } + + NeedBBCalc = true; + MeshChanged(); + +} + + +//--------------------------------------------------------------------------- +void CMesh::RotZ(double a) +//--------------------------------------------------------------------------- +{ + for (int i=0; i<(int)Vertices.size(); i++) { + Vertices[i].v.RotZ(a); + Vertices[i].n.RotZ(a); + } + for (int i=0; i<(int)Facets.size(); i++) { + Facets[i].n.RotZ(a); + } + + NeedBBCalc = true; + MeshChanged(); + +} + + + +void CMesh::WeldClose(float Distance) +{ + + int* NumVertHere = new int[Vertices.size()]; //keeps track of how many vertices have been averaged to get here... + int* ConsolidateMap = new int[Vertices.size()]; //maps the whole vertex list to the welded vertex list (IE has holes) + int* OldNewMap = new int [Vertices.size()]; //maps the old, larger vertex list to the new, smaller one. + for (int i=0; i<(int)Vertices.size(); i++){ + NumVertHere[i] = 1; + ConsolidateMap[i] = i; + OldNewMap[i] = -1; + } + + for (int i=0; i<(int)Facets.size(); i++){ //look through facets so we don't have to do exhaustive On2 search of all vertex combos + for (int j=0; j<3; j++){ //look at all three combinations of vertices... + int Vi1 = Facets[i].vi[j]; + int np = -1; while (np != Vi1){ np = Vi1; Vi1 = ConsolidateMap[Vi1]; } //iterates NewMap to get the final value... + + int Vi2 = Facets[i].vi[(j+1)%3]; + np = -1; while (np != Vi2){ np = Vi2; Vi2 = ConsolidateMap[Vi2]; } //iterates NewMap to get the final value... + + if (Vi1 != Vi2 && (Vertices[Vi1].v-Vertices[Vi2].v).Length() < Distance){ //if they are close enough but not already the same... + Vertices[Vi1].v = (Vertices[Vi1].v*NumVertHere[Vi1] + Vertices[Vi2].v*NumVertHere[Vi2]) / (NumVertHere[Vi1]+NumVertHere[Vi2]); //Vertex 1 is the weighted average + NumVertHere[Vi1] = NumVertHere[Vi1] + NumVertHere[Vi2]; //count how many vertices make up this point now... + + ConsolidateMap[Vi2] = Vi1; //effectively deletes Vi2... (points to Vi1) + } + } + } + + std::vector NewFacets; + std::vector NewVertices; + + for (int i=0; i<(int)Vertices.size(); i++){ + if (ConsolidateMap[i] == i) { //if this vertex ended up being part of the welded part + NewVertices.push_back(Vertices[i]); //add to the new vertex list + OldNewMap[i] = NewVertices.size()-1; + } + } + + //update the vertex indices + for (int i=0; i<(int)Facets.size(); i++){ //look through facets so we don't have to do exhaustive On2 search of all vertex combos + for (int j=0; j<3; j++){ //look at all three combinations of vertices... + int n = Facets[i].vi[j]; + int np = -1; while (np != n){ np = n; n = ConsolidateMap[n]; } //iterates NewMap to get the final value... + + Facets[i].vi[j] = OldNewMap[n]; + } + if (!(Facets[i].vi[0] == Facets[i].vi[1] || Facets[i].vi[0] == Facets[i].vi[2] || Facets[i].vi[2] == Facets[i].vi[1])) //if there aren't any the same... + NewFacets.push_back(Facets[i]); + } + + Facets = NewFacets; + Vertices = NewVertices; + + delete [] NumVertHere; + NumVertHere = NULL; + delete [] ConsolidateMap; + ConsolidateMap = NULL; + delete [] OldNewMap; + OldNewMap = NULL; + + CalcVertNormals(); //re-calculate normals! + MeshChanged(); + +} + + +void CMesh::RemoveDupLines(void) +{ + //first order lines so lower index is first: + int tmpHold; + for (int i=0; i<(int)Lines.size(); i++){ + if(Lines[i].vi[0] > Lines[i].vi[1]){ + tmpHold = Lines[i].vi[0]; + Lines[i].vi[0] = Lines[i].vi[1]; + Lines[i].vi[1] = tmpHold; + } + } + + //now sort them... + std::sort(Lines.begin(), Lines.end()); + + //iterate up, checking for duplicates and removing... + for (int i=1; i<(int)Lines.size(); i++){ //size changes, but that's ok! + if (Lines[i] == Lines[i-1]){ + Lines.erase(Lines.begin()+i); + i--; + } + } + +} + +void CMesh::SubdivideMe(void) +{ + std::vector NewFacets; + std::vector NewVertices; + std::vector NewLines; + + for (std::vector::iterator it = Facets.begin(); it != Facets.end(); it++){ + CVertex tmpOldVerts[3]; + CLine tmpOldLines[3]; + + //check lines + for(int i=0; i<3; i++){ + tmpOldVerts[i] = Vertices[it->vi[i]]; + if (it->HasEdge[i]) tmpOldLines[i] = Lines[it->ei[i]]; + //else default empty line and no tangents + } + + // normalize edge tangets if specified + for (int i=0; i<3; i++) { //each edge + Vec3D ThisEdge = (tmpOldVerts[(i+1)%3].v-tmpOldVerts[i].v); + for (int j=0; j<2; j++){ //each tangent + if (tmpOldLines[i].vt[j] == Vec3D(0,0,0)) tmpOldLines[i].HasTangent[j] = false; + if (tmpOldLines[i].HasTangent[j]){ + tmpOldLines[i].vt[j].Normalize(); // *(p[(i+1)%3]-p[i]).Length(); <<<<<< Normalize by length????? + if (tmpOldLines[i].vt[j].Dot(ThisEdge) < 0) tmpOldLines[i].vt[j] = -tmpOldLines[i].vt[j]; //make them all ccw as view from outside of triangle + } + } + } + +// for each vertex... +// if both edge tangents specified: overwrite normal +// if one edge tangent and normal: bring normal into perpendicular with the one edge, then calculate other edge +// if one edge tangent and no normal: Other edge tangent is the edge. Set it as such and calculate normall accordingly +// if no edge tangent and normal: Calculate edge tangents from normal and edge directions +// if no edge tangents, no normal: set tangents to edges and normal accordingly + + + //for each vertex + //if normal + //if 1et: bring normal perp, calc other et + //else if 0et: calc both from normal + + //set any nonexistant et's to its edge + //calc normal from et's + + + for (int i=0; i<3; i++){ //for each vertex + CLine* pL0 = &tmpOldLines[i]; //line 0 (dealing w tangent 0) + CLine* pL1 = &tmpOldLines[(i-1+3)%3]; //line 1 (dealing w tangent 1) + CVertex* pV = &tmpOldVerts[i]; + Vec3D EdgeAhead = (tmpOldVerts[(i+1)%3].v-pV->v); + Vec3D EdgeBehind = (pV->v-tmpOldVerts[(i-1+3)%3].v); //POINTING OUT FROM TRIANGLE as vertex tangent would + + if (pV->HasNormal){ + if (pL0->HasTangent[0] && !pL1->HasTangent[1]){ //first has tangent, second doesn't + Vec3D Perp = pV->n.Cross(pL0->vt[0]); + Vec3D NewNorm = pL0->vt[0].Cross(Perp); + Perp = NewNorm.Cross(EdgeBehind); + pL1->vt[1] = Perp.Cross(NewNorm).Normalized(); + pL1->HasTangent[1] = true; + } + else if (!pL0->HasTangent[0] && pL1->HasTangent[1]){ //second has tangent, first doesn't + Vec3D Perp = pV->n.Cross(pL1->vt[1]); + Vec3D NewNorm = pL1->vt[1].Cross(Perp); + Perp = EdgeAhead.Cross(NewNorm); + pL0->vt[0] = NewNorm.Cross(Perp).Normalized(); + pL0->HasTangent[0] = true; + } + else if (!pL0->HasTangent[0] && !pL1->HasTangent[1]){ //neither has tangent + Vec3D Perp = EdgeAhead.Cross(pV->n); //Edge ahead... + pL0->vt[0] = pV->n.Cross(Perp).Normalized(); + Perp = pV->n.Cross(EdgeBehind); //Edge ahead... + pL1->vt[1] = Perp.Cross(pV->n).Normalized(); + pL0->HasTangent[0] = true; + pL1->HasTangent[1] = true; + } + //else (both have tangents) we will recalculate the normal according to the tangents later + } + else { //no Normal info, set nonexistant edge tangents to the straight edges. (todo: optimize by doing quick interpolation later) + if (!pL0->HasTangent[0]){ + pL0->vt[0] = EdgeAhead.Normalized(); //unnecessary normalization) + pL0->HasTangent[0] = true; + } + if (!pL1->HasTangent[1]){ + pL1->vt[1] = EdgeBehind.Normalized(); + pL1->HasTangent[1] = true; + } + } + + //All edge tangetns are normalized to 1 at this point. multply each by edge length to make hermite work + pL0->vt[0] *= EdgeAhead.Length(); + pL1->vt[1] *= EdgeBehind.Length(); + + //calc normal! + pV->n = pL1->vt[1].Cross(pL0->vt[0]).Normalized(); + pV->HasNormal = true; + } + + //CLine tmpLines[9]; + //CFacet tmpFacets[4]; + + Vec3D NewV[3], NewN[3], NewT[3]; + //edge 1! + HermiteInterpolation(tmpOldVerts[0].v, tmpOldVerts[0].n, tmpOldLines[0].vt[0], tmpOldVerts[1].v, tmpOldVerts[1].n, tmpOldLines[0].vt[1], 0.5, NewV[0], NewN[0], NewT[0]); + HermiteInterpolation(tmpOldVerts[1].v, tmpOldVerts[1].n, tmpOldLines[1].vt[0], tmpOldVerts[2].v, tmpOldVerts[2].n, tmpOldLines[1].vt[1], 0.5, NewV[1], NewN[1], NewT[1]); + HermiteInterpolation(tmpOldVerts[2].v, tmpOldVerts[2].n, tmpOldLines[2].vt[0], tmpOldVerts[0].v, tmpOldVerts[0].n, tmpOldLines[2].vt[1], 0.5, NewV[2], NewN[2], NewT[2]); + + int NumPrevVerts = NewVertices.size(); + int NumPrevLines = NewLines.size(); + + CVertex tmpVerts[6]; //stored ccw as viewed from outside + tmpVerts[0] = tmpOldVerts[0]; + tmpVerts[1] = CVertex(NewN[0], NewV[0]); + tmpVerts[1].HasColor = tmpOldVerts[0].HasColor; //todo: average eventually! + tmpVerts[1].VColor = tmpOldVerts[0].VColor; + + tmpVerts[2] = tmpOldVerts[1]; + tmpVerts[3] = CVertex(NewN[1], NewV[1]); + tmpVerts[3].HasColor = tmpOldVerts[1].HasColor; //todo: average eventually! + tmpVerts[3].VColor = tmpOldVerts[1].VColor; + + tmpVerts[4] = tmpOldVerts[2]; + tmpVerts[5] = CVertex(NewN[2], NewV[2]); + tmpVerts[5].HasColor = tmpOldVerts[2].HasColor; //todo: average eventually! + tmpVerts[5].VColor = tmpOldVerts[2].VColor; + for (int i=0; i<6; i++) NewVertices.push_back(tmpVerts[i]); + + //stored as ccw as viewed from outside... + NewLines.push_back(CLine(NumPrevVerts, NumPrevVerts+1, tmpOldLines[0].vt[0], NewT[0])); + NewLines.push_back(CLine(NumPrevVerts+1, NumPrevVerts+2, NewT[0], tmpOldLines[0].vt[1])); + NewLines.push_back(CLine(NumPrevVerts+2, NumPrevVerts+3, tmpOldLines[1].vt[0], NewT[1])); + NewLines.push_back(CLine(NumPrevVerts+3, NumPrevVerts+4, NewT[1], tmpOldLines[1].vt[1])); + NewLines.push_back(CLine(NumPrevVerts+4, NumPrevVerts+5, tmpOldLines[2].vt[0], NewT[2])); + NewLines.push_back(CLine(NumPrevVerts+5, NumPrevVerts, NewT[2], tmpOldLines[2].vt[1])); + NewLines.push_back(CLine(NumPrevVerts+1, NumPrevVerts+3)); + NewLines.push_back(CLine(NumPrevVerts+3, NumPrevVerts+5)); + NewLines.push_back(CLine(NumPrevVerts+5, NumPrevVerts+1)); + + + + CFacet tmpFacets[4]; //0, 1, 2, middle + Vec3D FacetNorms[4]; + Vec3D tmp; + tmp = tmpVerts[5].v-tmpVerts[0].v; + FacetNorms[0] = (tmpVerts[1].v-tmpVerts[0].v).Cross(tmp).Normalized(); + tmp = tmpVerts[3].v-tmpVerts[1].v; + FacetNorms[1] = (tmpVerts[2].v-tmpVerts[1].v).Cross(tmp).Normalized(); + tmp = tmpVerts[4].v-tmpVerts[5].v; + FacetNorms[2] = (tmpVerts[3].v-tmpVerts[5].v).Cross(tmp).Normalized(); + tmp = tmpVerts[3].v-tmpVerts[5].v; + FacetNorms[3] = (tmpVerts[1].v-tmpVerts[5].v).Cross(tmp).Normalized(); + + tmpFacets[0] = CFacet(FacetNorms[0], NumPrevVerts+0, NumPrevVerts+1, NumPrevVerts+5); + tmpFacets[0].ei[0] = NumPrevLines; tmpFacets[0].HasEdge[0] = true; + tmpFacets[0].ei[2] = NumPrevLines+5; tmpFacets[0].HasEdge[2] = true; + + tmpFacets[1] = CFacet(FacetNorms[1], NumPrevVerts+1, NumPrevVerts+2, NumPrevVerts+3); + tmpFacets[1].ei[0] = NumPrevLines+1; tmpFacets[1].HasEdge[0] = true; + tmpFacets[1].ei[1] = NumPrevLines+2; tmpFacets[1].HasEdge[1] = true; + + tmpFacets[2] = CFacet(FacetNorms[2], NumPrevVerts+5, NumPrevVerts+3, NumPrevVerts+4); + tmpFacets[2].ei[1] = NumPrevLines+3; tmpFacets[2].HasEdge[1] = true; + tmpFacets[2].ei[2] = NumPrevLines+4; tmpFacets[2].HasEdge[2] = true; + + tmpFacets[3] = CFacet(FacetNorms[3], NumPrevVerts+5, NumPrevVerts+1, NumPrevVerts+3); + + NewFacets.push_back(tmpFacets[0]); + NewFacets.push_back(tmpFacets[1]); + NewFacets.push_back(tmpFacets[2]); + NewFacets.push_back(tmpFacets[3]); + + } + + //commit changes!! + Facets = NewFacets; + Vertices = NewVertices; + Lines = NewLines; + + NeedBBCalc = true; + MeshChanged(); +} + + + +//--------------------------------------------------------------------------- +void CMesh::HermiteInterpolation(Vec3D v0, Vec3D n0, Vec3D t0, Vec3D v1, Vec3D n1, Vec3D t1, double s, Vec3D& vs, Vec3D& ns, Vec3D& ts) +//--------------------------------------------------------------------------- +{// interpolate between two vertices at pointion s (0-1), given position, tangent, and normal + + double s2 = s*s; + double s3 = s2*s; + + // compute new point + + double h1 = 2*s3 - 3*s2 + 1; + double h2 = -2*s3 + 3*s2; + double h3 = s3 - 2*s2 + s; + double h4 = s3 - s2; + + vs = h1*v0 + h2*v1 + h3*t0 + h4*t1; + + // compute tangent + + double dh1 = 3*2*s2 - 2*3*s; + double dh2 = -3*2*s2 + 2*3*s; + double dh3 = 3*s2 - 2*2*s + 1; + double dh4 = 3*s2 - 2*s; + + ts = (dh1*v0 + dh2*v1 + dh3*t0 + dh4*t1).Normalized(); + + Vec3D nstemp = (n0*(1-s)+n1*s).Normalized(); + Vec3D sd = nstemp.Cross(ts); + ns = (ts.Cross(sd)).Normalized(); +// ns = nstemp; + +} + + + + +////////////////////CTEXTURE///////////////// +void CTexture::LoadData(int WidthIn, int HeightIn, unsigned char* RGBAdata, bool TiledIn) +{ + + Width = WidthIn; + Height = HeightIn; + Tiled = TiledIn; + + RGBAImage.clear(); + for (int j=HeightIn-1; j>=0; j--){ //AMF starts pixel data from the upper left. Open GL expects lower left. + for (int i=0; i=0; j--){ //AMF starts pixel data from the upper left. Open GL expects lower left. + for (int i=0; i AData(WidthIn*HeightIn, 255); //make alpha data... all opaque. + LoadData(WidthIn, HeightIn, Rdata, Gdata, Bdata, AData.data(), TiledIn); +} +// +//void CTexture::ResizeToMult2(void) //resizes the internal image to a multiple of 2, stores the factors. +//{ +// //TODO:: the right thing to do here would be to actually resize the image!! not just pad it... need resizing library/algorithm though. +// +// //find closest multiple of 2 equal or greater to image size +// ActWidth = 4; +// while (ActWidth NewImg = std::vector(4*ActWidth*ActHeight, 0); //create and initialize with zeros +// for (int j=0; j for license details. +*******************************************************************************/ + +#include "MeshSlice.h" +#include +#include "SimpleImage.h" + +//#include "nMaterial.h" //TODO: Get rid of this AMF dependence!!! +//#include "AMF_Render.h" //GET rid of this, too... +#include "AMF_File.h" //...and this... + + + + + + +CMeshSlice::CMeshSlice(void) +{ + pMaterial = NULL; +// pObjExt = NULL; + Initialized = false; + pGetColor = NULL; + +} + + +CMeshSlice::~CMeshSlice(void) +{ +} + +void CMeshSlice::MeshChanged(void) //invalidates all cached voxelizing info! +{ + if (Initialized){ + Initialized = false; + _TriLayerZ = -1; + _TriLayerPZ = -1; + CurTriLayer.clear(); + YEnvMaxIterator = CurTriLayer.begin(); + _TriLineY = -1; + _TriLinePY = -1; + CurTriLine.clear(); + XEnvMaxIterator = CurTriLine.begin(); + Envelopes.clear(); + ZEnvMaxIterator = Envelopes.begin(); //but no elements... basically just voids it! + _ZActual = -1; + _YActual = -1; + _XActual = -1; + CurTriPoints.clear(); + XPtIter = CurTriPoints.begin(); + _TriPointPX = -1; + _IsCurInside = false; + CurTriPoint.clear(); + } +} + +double CMeshSlice::GetClosestFreeZ(double ZIn) //returns the next Z value that does not intersect any vertices +{ + double CurZ = ZIn; +// double MultFact = 1.0; + for (std::vector::iterator it = Vertices.begin(); it != Vertices.end(); it++){ + if (it->v.z == CurZ){ + CurZ += 2*CurZ*DBL_EPSILON; + //MultFact *= 2.0; + it = Vertices.begin(); //-1 is because it will increment before next time around, and we want to start over from 0 + } + } + return CurZ; +} + +//double CMeshSlice::GetClosestFreeY(double YIn) //returns the next Y value that does not intersect any vertices IN CurTriLayer! +//{ +// double CurY = YIn; +// for (std::vector::iterator it = Facets.begin(); it != Facets.end(); it++){ +// if (Vertices[it->vi[0]].v.y == CurY || Vertices[it->vi[1]].v.y == CurY || Vertices[it->vi[2]].v.y == CurY){ +// CurY += DBL_EPSILON; +// it = Facets.begin()-1; //-1 is because it will increment before next time around, and we want to start over from 0 +// } +// } +// return CurY; +//} + +void CMeshSlice::FillTriLayer(double z, double ZPad) //fills in TriHeight with all triangles that bridge this plane +{ + if (ZPad < 0) ZPad = -ZPad; // only positive! + if(z == _TriLayerZ && ZPad == _TriLayerPZ) return; //if we've already done this... + double LastMaxZ = _ZActual + _TriLayerPZ; + double LastMinZ = _ZActual - _TriLayerPZ; + _ZActual = GetClosestFreeZ(z); + double CurMaxZ = _ZActual+ZPad; + double CurMinZ = _ZActual-ZPad; + _TriLayerZ = z; //cache these calls + _TriLayerPZ = ZPad; + + //optimized for sequential ascending Z steps, but works otherwise... + if (CurMaxZ < LastMaxZ || CurMinZ < LastMinZ){ //if we've gone backwards (downwards) we have to start over from the beginning! + CurTriLayer.clear(); //start over... + ZEnvMaxIterator = Envelopes.begin(); + } + + //Add in triangles with minimum Z envelopes less than the CurMaxZ + while (ZEnvMaxIterator != Envelopes.end() && ZEnvMaxIterator->MinZ <= CurMaxZ){ //add in all newly within range, just from where we left the iterator... + if(ZEnvMaxIterator->MaxZ >= CurMinZ) CurTriLayer.push_back(*ZEnvMaxIterator); // only add if Max Z Envelope is less than CurMinZ before adding + ZEnvMaxIterator++; + } + + //remove elements elready in list with max Z envelope less than CurMinZ + std::list::iterator it = CurTriLayer.begin(); + while (it != CurTriLayer.end()){ //get rid of all elements in the list with maximum values less than MinZ + if (it->MaxZ::iterator FIter; + //bool NoneEqual = false; + // + //while (!NoneEqual){ + // NoneEqual = true; + // CurTriLayer.clear(); //clear previous list + // m=0; + // for (FIter = Facets.begin(); FIter != Facets.end(); FIter++){ + // IsAbove = true; IsBelow = true; + + // for (int n=0; n<3; n++){ + // CurZ = Vertices[FIter->vi[n]].v.z; + // + // if(CurZ > z) IsBelow = false; + // else if(CurZ < z) IsAbove = false; + // else /* if(CurZ == z) */{ //START OVER with infintesimebly incremented Z value + // z += FLT_EPSILON; + // NoneEqual = false; + // break; break; //start the whole process over + // }; + // } + // if (!IsAbove && !IsBelow) CurTriLayer.push_back(m); //if this facet is not fully above or fully below our ZPlane + // + // m++; + // } + //} +} + +bool CMeshSlice::FillTriLine(double y, double z, double YPad, double ZPad) //fills in TriHeight with all triangles that bridge this plane +{ + if (YPad < 0) YPad = -YPad; // only positive! + if(y == _TriLineY && z == _TriLayerZ && YPad == _TriLinePY && ZPad == _TriLayerPZ) return true; //if we've already done this... + FillTriLayer(z, ZPad); //exits immediately if z is the same as cached... + if (CurTriLayer.size() == 0){CurTriLine.clear(); return true;} //if no triangles in this layer, no lines! easy! + + double LastMaxY = _YActual + _TriLinePY; + double LastMinY = _YActual - _TriLinePY; + _YActual = y; //start here. We will increment if we hit an edge dead on... + _TriLineY = y; //cache these calls + _TriLinePY = YPad; + + bool HitEdge = true; + while(HitEdge){ + HitEdge = false; + double CurMaxY = _YActual+YPad; + double CurMinY = _YActual-YPad; + + + //optimized for sequential ascending Y steps, but works otherwise... + if (CurMaxY < LastMaxY || CurMinY < LastMinY){ //if we've gone backwards (downwards) we have to start over from the beginning! + CurTriLine.clear(); //start over... + YEnvMaxIterator = CurTriLayer.begin(); + } + + //Add in triangles with minimum Y envelopes less than the CurMaxY + while (YEnvMaxIterator != CurTriLayer.end() && YEnvMaxIterator->MinY <= CurMaxY){ //add in all newly within range, just from where we left the iterator... + if(YEnvMaxIterator->MaxY >= CurMinY) CurTriLine.push_back(*YEnvMaxIterator); // only add if Max Y Envelope is less than CurMinY before adding + YEnvMaxIterator++; + } + + //remove elements elready in list with max Y envelope less than CurMinY + std::list::iterator it = CurTriLine.begin(); + while (it != CurTriLine.end()){ //get rid of all elements in the list with maximum values less than MinY + if (it->MaxY::iterator ZFIter; + for (ZFIter = CurTriLayer.begin(); ZFIter != CurTriLayer.end(); ZFIter++){ + V1y = Vertices[Facets[ZFIter->TriIndex].vi[0]].v.y; + V2y = Vertices[Facets[ZFIter->TriIndex].vi[1]].v.y; + V3y = Vertices[Facets[ZFIter->TriIndex].vi[2]].v.y; + //trivial checks (should get most of them...) + if (V1y < y && V2y < y && V3y < y) + continue; + if (V1y > y && V2y > y && V3y > y) + continue; + + //IntersectLine( + double tmp; +// IntType = IntersectXRay(&Facets[*ZFIter], y, z, p, pu, pv); + IntersectionType IntType2 = IntersectXRay(&Facets[ZFIter->TriIndex], y, z, tmp); +// if (IntType != IntType2 || (IntType==IT_INSIDE && p.x != tmp)) +// int UhOh=1; + + IntType = IntType2; + p.x = tmp; + + if (IntType == IT_EDGE){ //if any equal, add a tiny amount to Y + y += FLT_EPSILON; + NoneEqual = false; + break; break; + } + else if (IntType == IT_INSIDE){ + // if(IntersectXRay(&Facets[*ZFIter], y, z, p, pu, pv)) { //if it intersects +// if (InsideTri(p, Vertices[Facets[*ZFIter].vi[0]].v, Vertices[Facets[*ZFIter].vi[1]].v, Vertices[Facets[*ZFIter].vi[2]].v)){ + CurTriLine.push_back(p.x); + } +// } + } + } + if (CurTriLine.size()%2 ==1) return false; + + std::sort(CurTriLine.begin(), CurTriLine.end()); + + return true; + */ +} + +bool CMeshSlice::FillTriPoints(void) //fills points based on _YActual _ZActual, and the tris in TriLine. returns false increments _YActual (to re-do FillTriLine with) and try again. +{ + IntersectionType IntType; + double TmpXIntersect; + + CurTriPoints.clear(); + for (std::list::iterator it = CurTriLine.begin(); it != CurTriLine.end(); it++){ + if (_YActual <= it->MaxY && _YActual >= it->MinY && _ZActual <= it->MaxZ && _ZActual >= it->MinZ){ + IntType = IntersectXRay(&Facets[it->TriIndex], _YActual, _ZActual, TmpXIntersect); + if (IntType == IT_EDGE) return false; + else if (IntType == IT_INSIDE) CurTriPoints.push_back(TmpXIntersect); + } + } + + std::sort(CurTriPoints.begin(), CurTriPoints.end()); + XPtIter = CurTriPoints.begin(); //ready to iterate + _IsCurInside = false; //not inside the part if we just reset this array!! + return true; +} + +//--------------------------------------------------------------------------- +bool CMeshSlice::IsInside(Vec3D* Point, double TexDepth, CColor* pColor) +//--------------------------------------------------------------------------- +{ + if (TexDepth<0) TexDepth = -TexDepth; + FillTriLine(Point->y, Point->z, TexDepth, TexDepth); //returns very fast if previously used z or y layers... + + + + ////// + double LastMaxX = _XActual + _TriPointPX; + double LastMinX = _XActual - _TriPointPX; + _XActual = Point->x; + _TriPointPX = TexDepth; + double CurMaxX = _XActual + _TriPointPX; + double CurMinX = _XActual - _TriPointPX; + + + //optimized for sequential ascending X steps, but works otherwise... + if (CurMaxX < LastMaxX || CurMinX < LastMinX){ //if we've gone backwards (downwards) we have to start over from the beginning! + _IsCurInside = false; + CurTriPoint.clear(); + XPtIter = CurTriPoints.begin(); //reset the iterators + XEnvMaxIterator = CurTriLine.begin(); + } + + //Add in triangles with minimum X envelopes less than the CurMaxX + while (XEnvMaxIterator != CurTriLine.end() && XEnvMaxIterator->MinX <= CurMaxX){ //add in all newly within range, just from where we left the iterator... + if(XEnvMaxIterator->MaxX >= CurMinX) CurTriPoint.push_back(*XEnvMaxIterator); // only add if Max Z Envelope is less than CurMinZ before adding + XEnvMaxIterator++; + } + + //remove elements elready in list with max X envelope less than CurMinZ + std::list::iterator it = CurTriPoint.begin(); + while (it != CurTriPoint.end()){ //get rid of all elements in the list with maximum values less than MinZ + if (it->MaxX::iterator it = CurTriPoint.begin(); it != CurTriPoint.end(); it++){ //iterate through all the triangle within scope + CFacet* pCurFacet = &Facets[it->TriIndex]; + GetTriDist(pCurFacet, Point, u, v, dist2); + if (dist2HasTexture){ + if (pCurFacet->Map.TexIndex < (int)Textures.size()) pCurTexture = &Textures[pCurFacet->Map.TexIndex]; + + //u coord is from vertex 1 to vertex 2, v coord is v1 to v3 + PicU = pCurFacet->Map.uc[0] + u*(pCurFacet->Map.uc[1]-pCurFacet->Map.uc[0]) + v*(pCurFacet->Map.uc[2]-pCurFacet->Map.uc[0]); + PicV = pCurFacet->Map.vc[0] + u*(pCurFacet->Map.vc[1]-pCurFacet->Map.vc[0]) + v*(pCurFacet->Map.vc[2]-pCurFacet->Map.vc[0]); + if (PicU == 1.0) PicU = 1.0-DBL_MIN; //stay off the upper edge without grabbing a pix from 0.0... + if (PicV == 1.0) PicV = 1.0-DBL_MIN; //stay off the upper edge... + + if (pCurTexture && pCurTexture->Tiled){ //if we're tiling the textures do the mod... + UseTexColors = true; + PicU = fmod(PicU, 1.0); + if (PicU < 0) PicU += 1.0; //becuase fmod of a negative number is negative remainder + PicV = fmod(PicV, 1.0); + if (PicV < 0) PicV += 1.0; //becuase fmod of a negative number is negative remainder + + } + else { //not tiling texture, only do texture if we're within [0, 1] + if (PicU >= 0 && PicU < 1 && PicV >= 0 && PicV < 1) UseTexColors = true; + } + + } + if (UseTexColors && pCurTexture){ + int XPix = (int)(PicU*pCurTexture->Width); + //int YPix = Texture.Height - (int)(PicV*Texture.Height)-1; + int YPix = (int)(PicV*pCurTexture->Height); + + unsigned char* pPix = &(pCurTexture->RGBAImage[4*(YPix*pCurTexture->Width + XPix)]); + *pColor = CColor(*pPix/255.0, *(pPix+1)/255.0, *(pPix+2)/255.0, *(pPix+3)/255.0); + } + else { //no texture, so linearly interpolate the vertices! + CColor V0 = Vertices[pCurFacet->vi[0]].VColor; + CColor V1 = Vertices[pCurFacet->vi[1]].VColor; + CColor V2 = Vertices[pCurFacet->vi[2]].VColor; + + double R = V0.r + u*(V1.r-V0.r) + v*(V2.r-V0.r); + double G = V0.g + u*(V1.g-V0.g) + v*(V2.g-V0.g); + double B = V0.b + u*(V1.b-V0.b) + v*(V2.b-V0.b); + *pColor = CColor(R, G, B, 1.0); + } + } + } + } + + + } + return true; + } + else return false; + + + ////// + + + + + //std::vector::iterator LIter; + //int count = 0; + //for (LIter = CurTriLine.begin(); LIter != CurTriLine.end(); LIter++){ + // if (Point->x < *LIter) break; + // count ++; + + //} + //if (count%2 == 1) return true; //if we've passed an odd number of facets... + //else return false; + + +} +/* + +//--------------------------------------------------------------------------- +int CMeshSlice::GetXIntersections(double z, double y, double* pIntersections, int NumtoCheck, int* pToCheck) +//--------------------------------------------------------------------------- +{ //returns the number of intersections, stored in pIntersections. pToCheck is a vector of facet indices that are in this Z plane... + Vec3D p; + double pu, pv, V1y, V2y, V3y; + int NumFound = 0; + + for (int i=0; i y && V2y > y && V3y > y) + continue; + + if(IntersectXRay(&Facets[pToCheck[i]], y, z, p, pu, pv)) { //if it intersects + if (InsideTri(p, Vertices[Facets[pToCheck[i]].vi[0]].v, Vertices[Facets[pToCheck[i]].vi[1]].v, Vertices[Facets[pToCheck[i]].vi[2]].v)){ + pIntersections[NumFound++] = p.x; //(1.0 - pu - pv)*Vertices[Facets[pToCheck[i]].vi[0]].v.x + pu*Vertices[Facets[pToCheck[i]].vi[1]].v.x + pv*Vertices[Facets[pToCheck[i]].vi[2]].v.x; + } + } + } + +// if (NumFound%2 != 0) std::cout << "Uh-oh! Found an odd number of intersections!"; + + //sort intersections... (bubble sort = slow, but these should be super small... + double tmp; + for (int i=0; i pIntersections[j+1]){ + tmp = pIntersections[j+1]; + pIntersections[j+1] = pIntersections[j]; + pIntersections[j] = tmp; + } + } + } + + return NumFound; +} + +//--------------------------------------------------------------------------- +bool CMeshSlice::InsideTri(Vec3D& p, Vec3D& v0, Vec3D& v1, Vec3D& v2) +//--------------------------------------------------------------------------- +{// True if point p projects to within triangle (v0;v1;v2) + + Vec3D xax = (v1-v0).Normalized(); + Vec3D zax = ((v2-v0).Cross(xax)).Normalized(); + Vec3D yax = zax.Cross(xax).Normalized(); + + Vec3D p0(0,0,1); + Vec3D p1((v1-v0).Dot(xax),(v1-v0).Dot(yax),1); + Vec3D p2((v2-v0).Dot(xax),(v2-v0).Dot(yax),1); + Vec3D pt((p-v0).Dot(xax),(p-v0).Dot(yax),1); + + double d0 = Det(p0,p1,pt); + double d1 = Det(p1,p2,pt); + double d2 = Det(p2,p0,pt); + + if (d0<=0 && d1<=0 && d2<=0) + return true; + if (d0>=0 && d1>=0 && d2>=0) + return true; + + return false; + +} + +//--------------------------------------------------------------------------- +double CMeshSlice::Det(Vec3D& v0, Vec3D& v1, Vec3D& v2) +//--------------------------------------------------------------------------- +{ // Compute determinant of 3x3 matrix v0,v1,v2 + + return + + v0.x*(v1.y*v2.z-v1.z*v2.y) + + v0.y*(v1.z*v2.x-v1.x*v2.z) + + v0.z*(v1.x*v2.y-v1.y*v2.x); + +} +*/ +IntersectionType CMeshSlice::IntersectXRay(CFacet* pFacet, double y, double z, double& XIntersect) +{ + //http://www.blackpawn.com/texts/pointinpoly/default.html + + Vec3D vA = Vertices[pFacet->vi[0]].v; + Vec3D vB = Vertices[pFacet->vi[1]].v; + Vec3D vC = Vertices[pFacet->vi[2]].v; + + double v0y = vC.y-vA.y; //u + double v0z = vC.z-vA.z; + double v1y = vB.y-vA.y; //v + double v1z = vB.z-vA.z; + double v2y = y-vA.y; + double v2z = z-vA.z; + + double dot00=v0y*v0y+v0z*v0z; + double dot01=v0y*v1y+v0z*v1z; + double dot02=v0y*v2y+v0z*v2z; + double dot11=v1y*v1y+v1z*v1z; + double dot12=v1y*v2y+v1z*v2z; + + double invDenom = 1.0/(dot00*dot11-dot01*dot01); + double u=(dot11*dot02-dot01*dot12)*invDenom; + double v=(dot00*dot12-dot01*dot02)*invDenom; + + if ((u >= 0) && (v >= 0) && (u + v <= 1)){ + if (u == 0 || v == 0 || u+v==1) + return IT_EDGE; + else { + XIntersect = vA.x+u*(vC.x-vA.x)+v*(vB.x-vA.x); + return IT_INSIDE; + } + } + else return IT_OUTSIDE; +} + +bool CMeshSlice::GetTriDist(CFacet* pFacetCheck, Vec3D* pPointIn, double& UOut, double& VOut, double& Dist2Out) //gets distance of provided point to closest UV coordinate of within the triangle. returns true if sensible distance, otherwise false +{ + //http://www.geometrictools.com/Documentation/DistancePoint3Triangle3.pdf + + //Regions: + // E1 (t) + // \ 2| + // \| + // |\ + // | \ 1 + // 3 | \ + // | 0 \ + // ___|________\____ E0 (s) + // | \ 6 + // 4 | 5 \ + + + + Vec3D B = Vertices[pFacetCheck->vi[0]].v; + Vec3D E0 = Vertices[pFacetCheck->vi[1]].v - B; //(s) + Vec3D E1 = Vertices[pFacetCheck->vi[2]].v - B; //(t) + Vec3D D = B - *pPointIn; + + double a = E0.Dot(E0); + double b = E0.Dot(E1); + double c = E1.Dot(E1); + double d = E0.Dot(D); + double e = E1.Dot(D); + double f = D.Dot(D); + + double det=a*c-b*b; + double s = b*e-c*d; + double t = b*d-a*e; + + //determine which region... + //Q(s,t) = as^2+2bst+ct^2+2ds+2et+f + if (s+t <= det){ + if (s<0){ + if (t<0){ //Region 4 //TODO: CHECK ACCURATE! + // Grad(Q) = 2(as+bt+d,bs+ct+e) :: (derivative w respect to s, t) + // (0,1)*Grad(Q(0,0)) = (0,1)*(d,e) = e :: is grad from tip of region 2 in directon of leg toward origin (0,1)... + // (1,0)*Grad(Q(0,0)) = (1,0)*(d,e) = d + // min on edge t=0 if (0,1)*Grad(Q(0,0)) < 0 ) + // min on edge s=0 otherwise + if (e<0){ // minimum on edge t=0 + t=0; + t=(d >=0 ? 0 :(-d >=a ? 1:-d/a)); + } + else { // minimum on edge s=0 + s=0; + t=(e >=0 ? 0 :(-e >=c ? 1:-e/c)); + } + } + else { //Region 3 + // F(t) = Q(0,t) = ct^2 + 2et + f + // F(t)/2 = ct+e + // F(T) = 0 when T = -e/c + s=0; + t=(e >=0 ? 0 :(-e >=c ? 1:-e/c)); + } + } + else if (t<0){ //Region 5 (like region 3) + // F(s) = Q(s,0) = as^2 + 2ds + f + // F(s)/2 = as+d + // F(S) = 0 when S = -d/a + t=0; + t=(d >=0 ? 0 :(-d >=a ? 1:-d/a)); + } + else { //Region 0 + double invdet = 1/det; + s *= invdet; + t *= invdet; + } + } + else { + if (s<0){ //Region 2 + // Grad(Q) = 2(as+bt+d,bs+ct+e) :: (derivative w respect to s, t) + // (0,-1)*Grad(Q(0,1)) = (0,-1)*(b+d,c+e) = -(c+e) :: is grad from tip of region 2 in directon of leg toward origin (0,1)... + // (1,-1)*Grad(Q(0,1)) = (1,-1)*(b+d,c+e) = (b+d)-(c+e) + // min on edge s+t=1 if (1,-1)*Grad(Q(0,1)) < 0 ) + // min on edge s=0 otherwise + double tmp0 = b+d; + double tmp1 = c+e; + if ( tmp1 > tmp0 ){ // minimum on edge s+t=1 + double numer = tmp1 - tmp0; + double denom = a-2*b+c; + s = ( numer >= denom ? 1 : numer/denom ); + t = 1-s; + } + else { // minimum on edge s=0 + s = 0; + t = ( tmp1 <= 0 ? 1 : ( e >= 0 ? 0 : -e/c ) ); + } + } + else if (t<0){ //Region 6 //TODO: CHECK ACCURATE! + // Grad(Q) = 2(as+bt+d,bs+ct+e) :: (derivative w respect to s, t) + // (-1,0)*Grad(Q(1,0)) = (-1,0)*(a+d,b+e) = -(a+d) :: is grad from tip of region 2 in directon of leg toward origin (0,1)... + // (-1,1)*Grad(Q(1,0)) = (-1,1)*(a+d,b+e) = (b+e)-(a+d) + // min on edge s+t=1 if (-1,1)*Grad(Q(0,1)) < 0 ) + // min on edge t=0 otherwise + double tmp0 = b+e; + double tmp1 = a+d; + if ( tmp1 > tmp0 ){ // minimum on edge s+t=1 + double numer = tmp1 - tmp0; + double denom = a-2*b+c; + t = ( numer >= denom ? 1 : numer/denom ); + s = 1-t; + } + else { // minimum on edge t=0 + t = 0; + s = ( tmp1 <= 0 ? 1 : ( d >= 0 ? 0 : -d/a ) ); + } + } + else { //Region 1 + // F(s) = Q(s,1-s) = (a-2b+c)s^2 + 2(b-c+d-e)s + (c+2e+f) + // F(s)/2 = (a-2b+c)s + (b-c+d-e) + // F(S) = 0 when S = (c+e-b-d)/(a-2b+c) + // a-2b+c = |E0-E1|^2 > 0, so only sign of c+e-b-d need be considered + double numer = c+e-b-d; + if (numer <= 0) s=0; + else { + double denom = a-2*b+c; + s = (numer >= denom ? 1 : numer/denom); + } + t=1-s; + } + } + + //check one or the other? should be same! + Vec3D ClosestPoint = B+s*E0+t*E1; + double D2 = (ClosestPoint-*pPointIn).Length2(); + double D22 = a*s*s+2*b*s*t+c*t*t+2*d*s+2*e*t+f; + + UOut = s; + VOut = t; + Dist2Out = D22; + return true; +} + + +/* +//--------------------------------------------------------------------------- +IntersectionType CMeshSlice::IntersectXRay(CFacet* pFacet, double y, double z, Vec3D& p, double& pu, double& pv) +//--------------------------------------------------------------------------- +{ + // compute intersection point P of triangle plane with ray from origin O in direction D + // D assumed to be normalized + // if no interstion, return false + // u and v are barycentric coordinates of the intersection point P = (1 - u - v)A + uB + vC + // see http://www.devmaster.net/wiki/Ray-triangle_intersection + Vec3D d = Vec3D(1,0,0); + Vec3D o = Vec3D(-1e9, y, z); + + Vec3D a = Vertices[pFacet->vi[0]].v; + Vec3D b = Vertices[pFacet->vi[1]].v; + Vec3D c = Vertices[pFacet->vi[2]].v; + + //Vec3D n = pFacet->n; //((b-a).Cross(c-a)).Normalized(); + Vec3D n = ((b-a).Cross(c-a)).Normalized(); + //if (n.x > 0){ //flip vertices... + // Vec3D tmp = a; + // a = b; + // b = tmp; + // n = ((b-a).Cross(c-a)).Normalized(); + //} + + double dn = d.Dot(n); + if (fabs(dn)<1E-5) + return IT_OUTSIDE; //parallel + + double dist = -(o-a).Dot(n)/dn; + Vec3D sD = d*dist; + p = o+sD; + + double V1, V2, V3; + V1 = (b-a).Cross(p-a).Dot(n); + V2 = (c-b).Cross(p-b).Dot(n); + V3 = (a-c).Cross(p-c).Dot(n); + + if (abs(V1)<1e-12 || abs(V2)<1e-12 || abs(V3)<1e-12) return IT_EDGE; //TODO:FISHY THRESHHOLD! + if (V1 >=0 && V2 >=0 && V3 >=0) return IT_INSIDE; +// if (V1 >=0 && V2 >=0 && V2 >=0) return true; <-Was this, but pretty obviously typo I think!!! + //if (V1 <=0 && V2 <=0 && V2 <=0) return true; + else return IT_OUTSIDE; + +} +*/ +bool CMeshSlice::GetSlice(CSimpleImage* pImgOut, double ZHeight, double SurfDepth, double XMin, double XMax, double YMin, double YMax, int XPix, int YPix, int aObjectID, std::string* pMessage) +{ + //initial checks... + if (XMax == XMin || YMax == YMin || XPix <= 0 || YPix <= 0) return false; + if (XMin > XMax){ double tmp = XMin; XMin = XMax; XMax = tmp;} //make sure Max is bigger then Min... + if (YMin > YMax){ double tmp = YMin; YMin = YMax; YMax = tmp;} //make sure Max is bigger then Min... + + if (!Initialized) Initialize(); + + //prepare the image (should already exist...) + //*pImgOut = QImage(XPix, YPix, QImage::Format_ARGB32); + if (!pImgOut->AllocateRGBA(XPix, YPix)){ + if (pMessage) *pMessage += "Insufficient memory to create bitmap. Please lower resolution.\n"; + return false; + } + + //*pImgOut = QImage(XPix, YPix, QImage::Format_Mono); + //pImgOut->setColor(0, qRgb(255, 255, 255)); //for now, 1-bit image, index 0 is white + //pImgOut->setColor(1, qRgb(0, 0, 0)); //index 1 is black + //pImgOut->fill(0); + + CColor CurColor; + + //Min and Max sections are the very edge of the picture... but we must sample at the center of pixels! + Vec3D CurPoint = Vec3D(0,0,ZHeight); + double XStep = (XMax-XMin)/XPix; + double YStep = (YMax-YMin)/YPix; + double XBegin = XMin + XStep/2; //offset half pixel to sample at center of grid points + double YBegin = YMin + YStep/2; //offset half pixel to sample at center of grid points + + double tR, tG, tB, tA; + + //do each pixel... + //QRgb* pCurPixel; + unsigned char* pCurPixel = pImgOut->GetRGBABits(); + for (int j=0; jscanLine(j); + for (int i=0; iFromCurrentUnits(1.0, UNIT_MM) +// double UnitScale = pMaterial->pnAmf->GetUnitsScaleFromMm(); + +// if (pObjExt) TmpPt = /*UnitScale*pObjExt-> */OriginalLoc(TmpPt); + TmpPt = OriginalLoc(TmpPt); + + pMaterial->GetColorAt(TmpPt.x, TmpPt.y, TmpPt.z, &tR, &tG, &tB, &tA); + *pCurPixel = (unsigned char)(tR*255); + *(pCurPixel+1) = (unsigned char)(tG*255); + *(pCurPixel+2) = (unsigned char)(tB*255); + *(pCurPixel+3) = (unsigned char)(tA*255); + } + else { //if no material defined, put opaque black + *pCurPixel = *(pCurPixel+1) = *(pCurPixel+2) = 0; + *(pCurPixel+3) = 255; + } + +//using agnostic info... + //if (pGetColor){ + // pGetColor(CurPoint.x, CurPoint.y, CurPoint.z, &tR, &tG, &tB, &tA, aObjectID); + // *pCurPixel = qRgba(tR*255, tG*255, tB*255, tA*255); + //} + //else *pCurPixel = qRgba(0, 0, 0, 255); + } + else { //if there was a surface texture here, use the color returned by IsInside() + *pCurPixel = (unsigned char)(CurColor.r*255); + *(pCurPixel+1) = (unsigned char)(CurColor.g*255); + *(pCurPixel+2) = (unsigned char)(CurColor.b*255); + *(pCurPixel+3) = (unsigned char)(CurColor.a*255); + } + } + else { //if not inside, make it transparent! + *pCurPixel = *(pCurPixel+1) = *(pCurPixel+2) = *(pCurPixel+3) = 0; + } + pCurPixel += 4; //go to next pixel + } + } + return true; +} + +void CMeshSlice::Initialize() +{ + Initialized = true; + + int NumFacets = Facets.size(); + Envelopes.reserve(NumFacets); + for (int i=0; i for license details. +*******************************************************************************/ + +#include "STL_File.h" + +#ifdef WIN32 + #include +#else + #include +#endif + +#include +//#include +//#include "QOpenGL.h" + +//Elements adapted from vcg code... + +double aWeldVertex::WeldThresh = 0; + + +#define STL_LABEL_SIZE 80 + +CSTL_File::CSTL_File(void) +{ + Clear(); +} + +CSTL_File::~CSTL_File(void) +{ +} + +//copy constructure +CSTL_File::CSTL_File(CSTL_File& s) { + *this = s; +} + +//overload = +CSTL_File& CSTL_File::operator=(const CSTL_File& s) { + + Facets.resize(s.Size()); + int Size = Facets.size(); + for (int i = 0; i 127){ + binary=true; + break; + } + } + // Now we know if the stl file is ascii or binary. + fclose(fp); + bool RetVal; + if(binary) RetVal = LoadBinary(filename); + else RetVal = LoadAscii(filename); + + if (RetVal) IsLoaded=true; + + //extract object name from path + std::string NewObjName = filename; + int Start = NewObjName.find_last_of("\\/"); + if (Start != std::string::npos) NewObjName = NewObjName.substr(Start + 1, NewObjName.size() - Start - 1); + int End = NewObjName.find_last_of("."); + if (End != std::string::npos) NewObjName = NewObjName.substr(0, End); + ObjectName = NewObjName; + + + return RetVal; +} + +bool CSTL_File::LoadBinary(std::string filename) +{ + FILE *fp; + fp = fopen(filename.c_str(), "rb"); + if(fp == NULL) return false; + + int facenum; + fseek(fp, STL_LABEL_SIZE, SEEK_SET); + fread(&facenum, sizeof(int), 1, fp); + + Clear(); + + // For each triangle read the normal, the three coords and a short set to zero + float N[3]; + float P[9]; + short attr; + + for(int i=0;i "facet normal 0 0 0" + if(ret!=3){ + // we could be in the case of a multiple solid object, where after a endfaced instead of another facet we have to skip two lines: + // endloop + // endfacet + //endsolid <- continue on ret==0 will skip this line + //solid ascii <- and this one. + // facet normal 0.000000e+000 7.700727e-001 -6.379562e-001 + lineCnt++; + continue; + } + ret=fscanf(fp, "%*s %*s"); // --> "outer loop" + ret=fscanf(fp, "%*s %f %f %f\n", &P[0], &P[1], &P[2]); // --> "vertex x y z" + if(ret!=3) return false; + ret=fscanf(fp, "%*s %f %f %f\n", &P[3], &P[4], &P[5]); // --> "vertex x y z" + if(ret!=3) return false; + ret=fscanf(fp, "%*s %f %f %f\n", &P[6], &P[7], &P[8]); // --> "vertex x y z" + if(ret!=3) return false; + ret=fscanf(fp, "%*s"); // --> "endloop" + ret=fscanf(fp, "%*s"); // --> "endfacet" + lineCnt+=7; + if(feof(fp)) break; + + AddFacet(N[0], N[1], N[2], P[0], P[1], P[2], P[3], P[4], P[5], P[6], P[7], P[8]); + + } + fclose(fp); + return true; +} + +bool CSTL_File::Save(std::string filename, bool Binary) const { //writes ascii stl file... + + FILE *fp; + + if (Binary) fp = fopen(filename.c_str(),"wb"); + else fp = fopen(filename.c_str(),"w"); + + if(fp==0) return false; + int NumFaces = (int)Facets.size(); + + if(Binary){ + // Write Header + std::string tmp = ObjectName; + tmp += " "; + //char header[128]=; + fwrite(tmp.c_str(),80,1,fp); + // write number of facets + fwrite(&NumFaces,1,sizeof(int),fp); + unsigned short attributes=0; + + for(int i=0; i for license details. +*******************************************************************************/ + +#include "SimpleImage.h" +#include +#include + +#define STBI_NO_HDR +#include "stb_image/stb_image.h" + +CSimpleImage::CSimpleImage(void) +{ + pDataRGBA = NULL; +// pDataIndex = NULL; + DataWidth = 0; + DataHeight = 0; + AlphaPresent = false; +// CurIndexMode = IM_VBW; +} + + +CSimpleImage::~CSimpleImage(void) +{ + if (pDataRGBA) delete [] pDataRGBA; +// if (pDataIndex) delete [] pDataIndex; +// DataWidth = 0; +// DataHeight = 0; + +} + + +CSimpleImage& CSimpleImage::operator=(const CSimpleImage& In) +{ + AllocateRGBA(In.DataWidth, In.DataHeight); + memcpy(pDataRGBA, In.pDataRGBA, DataWidth*DataHeight*4); + AlphaPresent = In.AlphaPresent; + +// CurIndexMode = In.CurIndexMode; +// if (In.pDataIndex){ // if there's index data +// IndexFromRGBA(CurIndexMode); +// } + return *this; +} + +bool CSimpleImage::LoadImage(std::string FilePath) +{ + int x,y,n; + unsigned char *data = stbi_load(FilePath.c_str(), &x, &y, &n, 4); + if (data == NULL) return false; + + if (n==2 || n==4) AlphaPresent = true; + else AlphaPresent = false; + + if (!AllocateRGBA(x, y)) return false; + memcpy(pDataRGBA, data, x*y*4); + + stbi_image_free(data); + return true; +} + +bool CSimpleImage::AllocateRGBA(int WidthIn, int HeightIn) +{ + if (WidthIn<0 || HeightIn<0) return false; + if (WidthIn*HeightIn == DataWidth*DataHeight){ + DataWidth = WidthIn; + DataHeight = HeightIn; + return true; //already correct size allocated + } + + if (pDataRGBA) delete [] pDataRGBA; + pDataRGBA = NULL; //not necessary, but good habit +// if (pDataIndex) delete [] pDataIndex; +// pDataIndex = NULL; + + if (WidthIn != 0 && HeightIn != 0){ + try {pDataRGBA = new unsigned char [WidthIn*HeightIn*4];} + catch (std::bad_alloc&){return false;} + } + + DataWidth = WidthIn; + DataHeight = HeightIn; + + return true; +} + + + +void CSimpleImage::Fill(unsigned char R, unsigned char G, unsigned char B, unsigned char A) +{ + if (A == 255) AlphaPresent = false; + unsigned char* pDataRGBAIterator = pDataRGBA; + for (int i=0; i for license details. +*******************************************************************************/ + +#include "X3D_File.h" +#include +//#include "QOpenGL.h" + +#include "XmlStream.h" + +//#include + +//#include +//#include + +//repeated... +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + + +CX3D_File::CX3D_File(void) +{ + Clear(); +} + +CX3D_File::~CX3D_File(void) +{ +} + +X3dLoadResult CX3D_File::Load(std::string filename, std::string ImgPathIn) +{ + Clear(); + CXmlStreamRead XML; + if (!XML.LoadFile(filename)) return XLR_BADFILEPATH; + filePath = filename; + ImagePath = ImgPathIn; + errors = ""; + + X3dLoadResult tmpRes; + + if (XML.OpenElement("Scene")){ + int NumDown = 0; + while (XML.OpenElement("Transform")) NumDown++; //ignore groups and transforms for now... + while (XML.OpenElement("Group")) NumDown++; //ignore groups and transforms for now... + while (XML.OpenElement("Transform")) NumDown++; //ignore groups and transforms for now... + while (XML.OpenElement("Group")) NumDown++; //ignore groups and transforms for now... + while (XML.OpenElement("Transform")) NumDown++; //ignore groups and transforms for now... + while (XML.OpenElement("Group")) NumDown++; //ignore groups and transforms for now... + + + //read as many shape tags as there are... + xShapeNode tmpShape; + while(XML.OpenElement("Shape", true)){ // + tmpRes = tmpShape.ReadXML(&XML, this, &errors); + if (tmpRes != XLR_SUCCESS) return tmpRes; + xShapes.push_back(tmpShape); + } +// if (xShapes.size() != 0) XML.UpLevel(); // + + for (int i=0; i::iterator it = xShapes.begin(); it != xShapes.end(); it++) { + if (it->xIndexedFaceSet.Coordinate.size()%3 != 0) continue; + + for (std::vector::iterator jt = it->xIndexedFaceSet.Coordinate.begin(); jt < it->xIndexedFaceSet.Coordinate.end(); jt += 3) { + minX = (std::min)(minX, *jt); + minY = (std::min)(minY, *(jt+1)); + minZ = (std::min)(minZ, *(jt+2)); + maxX = (std::max)(maxX, *jt); + maxY = (std::max)(maxY, *(jt+1)); + maxZ = (std::max)(maxZ, *(jt+2)); + } + } +} + +void CX3D_File::GetSize(double& sizeX, double& sizeY, double& sizeZ) +{ + double xmin, ymin, zmin, xmax, ymax, zmax; + GetMinMax(xmin, ymin, zmin, xmax, ymax, zmax); + sizeX = xmax-xmin; + sizeY = ymax-ymin; + sizeZ = zmax-zmin; + +} + +void CX3D_File::Str2Data(std::string* pS, std::vector* pD) +{ + int Size = pS->size(); + char *a=new char[Size+1]; + a[Size]=0; + memcpy(a,pS->c_str(),Size); + + char* pEnd = a; + while (*pEnd != '\0'){ + pD->push_back(strtol(pEnd, &pEnd, 10)); + while (*pEnd == ' ' || *pEnd == '\t' || *pEnd == '\n') pEnd++; + } +// +// std::stringstream ss(*pS); +// int i; +// while (ss >> i) pD->push_back(i); +} + +void CX3D_File::Str2Data(std::string* pS, std::vector* pD) +{ + int Size = pS->size(); + char *a=new char[Size+1]; + a[Size]=0; + memcpy(a,pS->c_str(),Size); + + char* pEnd = a; + while (*pEnd != '\0'){ + pD->push_back(strtod(pEnd, &pEnd)); + while (*pEnd != 0 && *pEnd <= 33 ) pEnd++; //includes all whitespace, including space. + } + +// std::stringstream ss(*pS); +// double i; +// while (ss >> i) pD->push_back(i); +} + + + +X3dLoadResult xAppearanceNode::ReadXML(CXmlStreamRead* pXML, CX3D_File* pX3dFile, std::string* pRetMessage) +{ + Clear(); + + if (pXML->OpenElement("ImageTexture")){ + std::string ImgPath; + pXML->GetElAttS("url", &ImgPath); + + if (!pXML->GetElAttB("repeatS", &repeatS)) repeatS = true; //defaults to true! + if (!pXML->GetElAttB("repeatT", &repeatT)) repeatT = true; //defaults to true! + +// QList test = QImageReader::supportedImageFormats(); + + //if we've provided an image path, load that! + if (pX3dFile->ImagePath != ""){ + if (!ImageTexture.LoadImage(pX3dFile->ImagePath)) return XLR_BADIMAGEPATH; + } + //otherwise try loading as is in the x3d file: + else if (!ImageTexture.LoadImage(ImgPath)){ //if not at the absolute path... + + //get image filename (without original path) and try in the folder the x3d file was in + size_t start = ImgPath.find_last_of("/\\") +1; + if (start == 0) start = ImgPath.find_first_of("\"")+1;//nothing found... + + size_t end = start; + while (ImgPath[end] != '\"') end++; + std::string imgName = ImgPath.substr(start, end-start); + + //get x3d file path + std::string FilePath = pX3dFile->filePath.substr(0, pX3dFile->filePath.find_last_of("/\\")+1); + //load the image here, or give an error if cannot find it... + if (!ImageTexture.LoadImage(FilePath + imgName)){ //check the folder that the x3d file was located in + pX3dFile->ImagePath = FilePath + imgName; + *pRetMessage += "Could not find texture file " + imgName + ".\n"; + return XLR_BADIMAGEPATH; + } + } +// pX3dFile->imagePath = ""; //flag for succesful image load... + + pXML->CloseElement(); + } + if (pXML->OpenElement("Material")){ + std::string Data; + if (pXML->GetElAttS("diffuseColor", &Data)){ + std::vector Color; + CX3D_File::Str2Data(&Data, &Color); + MatColorRed = Color[0]; + MatColorGreen = Color[1]; + MatColorBlue = Color[2]; + } + pXML->CloseElement(); + } + return XLR_SUCCESS; +} + +xIndexedFaceSetNode& xIndexedFaceSetNode::operator=(const xIndexedFaceSetNode& x) +{ + coordIndex=x.coordIndex; + texCoordIndex=x.texCoordIndex; + Coordinate=x.Coordinate; + TextureCoordinate=x.TextureCoordinate; + colorPerVertex=x.colorPerVertex; + colorIndex=x.colorIndex; + Color=x.Color; + ColorRGBA=x.ColorRGBA; + CoordDef=x.CoordDef; + CoordUse=x.CoordUse; + NumVertPerFacet=x.NumVertPerFacet; + Colors=x.Colors; + ColByIndex=x.ColByIndex; + HasAlpha=x.HasAlpha; + HasTexture=x.HasTexture; + return *this; +} + +void xIndexedFaceSetNode::Clear() +{ + coordIndex.clear(); + texCoordIndex.clear(); + Coordinate.clear(); + TextureCoordinate.clear(); + colorPerVertex = true; + colorIndex.clear(); + Color.clear(); + ColorRGBA.clear(); + CoordDef=""; + CoordUse=""; + + NumVertPerFacet = 0; + Colors = false; + ColByIndex = false; + HasAlpha = false; + HasTexture = false; +} + + +X3dLoadResult xIndexedFaceSetNode::ReadXML(CXmlStreamRead* pXML, std::string* pRetMessage) +{ + Clear(); + std::string tmp; + if (pXML->GetElAttS("coordIndex", &tmp)) CX3D_File::Str2Data(&tmp, &coordIndex); + if (pXML->GetElAttS("colorIndex", &tmp)) CX3D_File::Str2Data(&tmp, &colorIndex); + if (pXML->GetElAttS("texCoordIndex", &tmp)) CX3D_File::Str2Data(&tmp, &texCoordIndex); + + colorPerVertex = true; + if (pXML->GetElAttS("colorPerVertex", &tmp)){ + if (tmp == "false" || tmp == "False") colorPerVertex = false; //default is true... + } + + if (pXML->OpenElement("Coordinate")){ + if (!pXML->GetElAttS("DEF", &CoordDef)) CoordDef=""; + if (!pXML->GetElAttS("USE", &CoordUse)) CoordUse=""; + if (pXML->GetElAttS("point", &tmp)) CX3D_File::Str2Data(&tmp, &Coordinate); + pXML->CloseElement(); + } + + if (pXML->OpenElement("TextureCoordinate")){ + if (pXML->GetElAttS("point", &tmp)) CX3D_File::Str2Data(&tmp, &TextureCoordinate); + pXML->CloseElement(); + } + + if (pXML->OpenElement("Color")){ + if (pXML->GetElAttS("color", &tmp)) CX3D_File::Str2Data(&tmp, &Color); + pXML->CloseElement(); + } + if (pXML->OpenElement("ColorRGBA")){ + if (pXML->GetElAttS("color", &tmp)) CX3D_File::Str2Data(&tmp, &ColorRGBA); + pXML->CloseElement(); + } + if (!FillDerivedInfo()) return XLR_NOSHAPE; + return XLR_SUCCESS; +} + +bool xIndexedFaceSetNode::FillDerivedInfo() //fills in these calculated other parameters for easy access later. +{ + NumVertPerFacet = 0; //triangles or quads? + if (coordIndex.size() > 3 && coordIndex[3] == -1) NumVertPerFacet = 3; //triangles! + else if (coordIndex.size() > 4 && coordIndex[4] == -1) NumVertPerFacet = 4; //quads! + else return false; + + + + Colors = false; + if (Color.size() != 0 || ColorRGBA.size() != 0) Colors = true; + + ColByIndex = false; + if (colorIndex.size() != 0) ColByIndex = true; + + HasAlpha = false; + if (ColorRGBA.size() != 0) HasAlpha = true; + + HasTexture = false; + if (texCoordIndex.size() != 0 && TextureCoordinate.size() != 0) HasTexture = true; + + return true; +} + +int xIndexedFaceSetNode::GetNumTriangles() //returns 2x number of quads if x3d has quads... +{ + if (NumVertPerFacet == 3) return coordIndex.size()/4; + else if (NumVertPerFacet == 4) return coordIndex.size()*2/5; + else return 0; +} + +int xIndexedFaceSetNode::GetCoordInd(int TriNum, int VertNum) //VertNum is 0, 1, or 2 (copy to GetTexCoordInd()) +{ + if (NumVertPerFacet == 3) return coordIndex[TriNum*4+VertNum]; + else if (NumVertPerFacet == 4){ + if(TriNum%2 == 0) return coordIndex[(TriNum/2)*5+VertNum]; //first triangle of quad (012) + else if (VertNum != 0) return coordIndex[(TriNum/2)*5+1+VertNum]; //second triangle of quad, last two verts ([0]23) + else return coordIndex[(TriNum/2)*5]; //second triangle of quad, first vert (0[23]) + } + else return -1; +} + +double xIndexedFaceSetNode::GetColorFace(int TriNum, ColChan Chan) //triangle color (make sure Color && !colorPerVertex and HasAplpha if requesting alpha) +{ + if (NumVertPerFacet == 4) TriNum /=2; + + if (ColByIndex){ + if (HasAlpha) return ColorRGBA[colorIndex[TriNum]*4+(int)Chan]; + else return Color[colorIndex[TriNum]*3+(int)Chan]; + } + else { + if (HasAlpha) return ColorRGBA[TriNum*4+(int)Chan]; + else return Color[TriNum*3+(int)Chan]; + } +} + +double xIndexedFaceSetNode::GetColorVert(int CoordNum, ColChan Chan) //vertex color (make sure Color && colorPerVertex and HasAlpha if requesting alpha) +{ + if (ColByIndex){ + //AMF does not support vertixes being mapped to different colors for different tirangles. + if (HasAlpha) return ColorRGBA[0*4+(int)Chan]; + else return Color[0*3+(int)Chan]; + } + else { + if (HasAlpha) return ColorRGBA[CoordNum*4+(int)Chan]; + else return Color[CoordNum*3+(int)Chan]; + } +} + +double xIndexedFaceSetNode::GetColorVert(int TriNum, int VertNum, ColChan Chan) //triangle color (make sure Color && colorPerVertex and HasAplpha if requesting alpha) +{ + if (ColByIndex){ + if (HasAlpha) return ColorRGBA[colorIndex[GetCoordInd(TriNum, VertNum)]*4+(int)Chan]; + else return Color[colorIndex[GetCoordInd(TriNum, VertNum)]*3+(int)Chan]; + } + else { + if (HasAlpha) return ColorRGBA[GetCoordInd(TriNum, VertNum)*4+(int)Chan]; + else return Color[GetCoordInd(TriNum, VertNum)*3+(int)Chan]; + } +} + +int xIndexedFaceSetNode::GetTexCoordInd(int TriNum, int VertNum) //copied fromG etCoordInd () +{ + if (NumVertPerFacet == 3) return texCoordIndex[TriNum*4+VertNum]; + else if (NumVertPerFacet == 4){ + if(TriNum%2 == 0) return texCoordIndex[(TriNum/2)*5+VertNum]; //first triangle of quad (012) + else if (VertNum != 0) return texCoordIndex[(TriNum/2)*5+1+VertNum]; //second triangle of quad, last two verts ([0]23) + else return texCoordIndex[(TriNum/2)*5]; //second triangle of quad, first vert (0[23]) + } + else return -1; +} + +X3dLoadResult xShapeNode::ReadXML(CXmlStreamRead* pXML, CX3D_File* pX3dFile, std::string* pRetMessage) +{ + Clear(); + X3dLoadResult tmpRes; + if (pXML->OpenElement("IndexedFaceSet")){ + tmpRes = xIndexedFaceSet.ReadXML(pXML, pRetMessage); + if (tmpRes != XLR_SUCCESS) return tmpRes; + pXML->CloseElement(); + } + if (pXML->OpenElement("Appearance")){ + tmpRes = xAppearance.ReadXML(pXML, pX3dFile, pRetMessage); + if (tmpRes != XLR_SUCCESS) return tmpRes; + pXML->CloseElement(); + } + + return XLR_SUCCESS; +} diff --git a/libraries/amf/amftools-code/src/XmlCompress.cpp b/libraries/amf/amftools-code/src/XmlCompress.cpp new file mode 100644 index 00000000..7d3001d3 --- /dev/null +++ b/libraries/amf/amftools-code/src/XmlCompress.cpp @@ -0,0 +1,250 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "XmlCompress.h" + +#ifdef WIN32 +#include "zip/zip.h" +#include "zip/unzip.h" +#else +//Mac/Linux zip header includes here +// using libzip http://nih.at/libzip/ +#include "zip.h" +#endif + +bool CompressFile(std::string ZipName, std::string FilePath, std::string* pError) //filepath string to zip into location of ZipName (also a filepath) +{ + std::vector Files; + Files.push_back(FilePath); + + return CompressFiles(ZipName, &Files, pError); +} + +bool CompressFiles(std::string ZipName, std::vector* pFilePaths, std::string* pError) //pointer to a vector of filepath strings to zip into location of ZipName (also a filepath) +{ +#ifdef WIN32 + + HZIP hz = CreateZip(ZipName.c_str(),0); + if (!hz){if (pError) *pError += "Could not create ZIP archive. Aborting\n"; return false;} + + int NumFiles = pFilePaths->size(); + for (int i=0; isize(); + for (int i=0; i *data, std::string* pError) +{ + std::vector FileNames; + FileNames.push_back(FileName); + + std::vector > Datas; + + bool RetRes = GetCompressedFiles(ZipName, &FileNames, &Datas, pError); + + if (RetRes){ + *data = Datas[0]; + return true; + } + else return false; +} + +bool GetCompressedFiles(std::string ZipName, std::vector* pFileNames, + std::vector >* data, std::string* pError) +{ +#ifdef WIN32 + + HZIP hz = OpenZip(ZipName.c_str(),0); + if (!hz){if(pError) *pError += ("Unable to open ZIP archive. Aborting.\n"); return false;} + + ZIPENTRY ze; + if (GetZipItem(hz, -1, &ze) != ZR_OK) {if(pError) *pError += ("Unable to return information about ZIP archive. Aborting.\n"); return false;} + int NumFiles = ze.index; + + //set up returns for data... + data->resize(NumFiles); + + for (int i=0; isize(); + for (int j=0; jresize(NumFiles); + + for (uint i=0; isize(); + const char * entryname = zip_get_name(hz, i, 0); + struct zip_stat stat; + err = zip_stat_index(hz, i, 0, &stat); + + for (int j=0; j* pFileNames, std::vector >* data, std::string* pError) +{ +#ifdef WIN32 + + HZIP hz = OpenZip(ZipName.c_str(),0); + if (!hz){if(pError) *pError += ("Unable to open ZIP archive. Aborting.\n"); return false;} + + ZIPENTRY ze; + if (GetZipItem(hz, -1, &ze) != ZR_OK) {if(pError) *pError += ("Unable to return information about ZIP archive. Aborting.\n"); return false;} + int NumFiles = ze.index; + + //set up returns for the number of files... + pFileNames->resize(NumFiles); + data->resize(NumFiles); + + for (int i=0; iresize(NumFiles); + data->resize(NumFiles); + + for (int i=0; i for license details. +*******************************************************************************/ + +#include "XmlStream.h" +#include +#include + +#include "rapidxml/rapidxml_print.h" + +#include "XmlCompress.h" + +////////////////////////////////CXmlStreamRead///////////////////////////// + + + +CXmlStreamRead::CXmlStreamRead() //std::string FilePathToRead) +{ + LastTagSearched = ""; + tmp = ""; + LastError = ""; +} + +CXmlStreamRead::~CXmlStreamRead(void) +{ +} + +bool CXmlStreamRead::LoadFile(std::string FilePathToRead) +{ + //determine if its compressed or not... + FILE *fp; + bool binary=false; + fp = fopen(FilePathToRead.c_str(), "r"); + if(fp == NULL){ + LastError = "Error: Could not open file. Aborting.\n"; + return false; + } + + std::string BeginFile; + std::string tmp; + tmp.resize(5); + fread((void*)tmp.data(), 5, 1, fp); + fclose(fp); + + + if (tmp == "(size)); + data[size] = '\0'; + stream.close(); + } + else { //see if it's compressed as zip + //filename within zip is the name of zip stripped of any folders... + int StartName = FilePathToRead.find_last_of('/')+1; + int EndName = FilePathToRead.size(); + std::string Name = FilePathToRead.substr(StartName, EndName-StartName); + + if (!GetCompressedFile(FilePathToRead, Name, &data, &LastError)){ + LastError += "Error: File identified as compressed AMF (does not begin with \" data_output = data; //buffer for reading/writing that won't get changed by parser (possible big allocation = slow here!) + + try{ + doc.parse(&data.front()); + //doc.parse<0>(&data.front()); + + } + catch (rapidxml::parse_error& Error){ + int ErrLoc = Error.where() - &data.front(); + + //find line number: + int LineNumber = 0; + for(int i=0; i= 0){ + if(data_output[ErrLoc + RelIndex1] == '\n') Counter++; + if (RelIndex1 < 200) break; + } + RelIndex1++; //step back up the last decrement and the newline + if(Counter < NumLinesUp) NumLinesUp = Counter; //only needed if we hit the beginning of the file + Counter = 0; + while(Counter <= NumLinesDown && ErrLoc+ ++RelIndex2 < (int)data_output.size()){ + if(data_output[ErrLoc + RelIndex2] == '\n') Counter++; + if (RelIndex2 > 200) break; + + } + RelIndex2--; //undo last increment + if(Counter < NumLinesDown) NumLinesDown = Counter; //only needed if we hit the beginning of the file + + + int CurLineNum = LineNumber - NumLinesUp+1; + + os << CurLineNum++ << ":"; //stat off right if we're at beginning of file + for(int i=RelIndex1; i* tmpElement; + bool IsSameTag = (tag == StrStack.back()); //flag to see if we just searched for this one + + if (IsSameTag && RepeatingTag){ //if this IS the last tag we searched for and we're looking for siblings... + tmpElement = ElStack.back()->next_sibling(tag.c_str(), 0, false); //try to find a next sibling + if (tmpElement){ //if we find it, move the top of the stack to the new sibling + ElStack.back() = tmpElement; + return true; + } + } + else { //If not the same tag we searched for (or we searched for the same tag name as a child element) + tmpElement = ElStack.back()->first_node(tag.c_str(), 0, false); //if this is the first time we've searched for the tag + if (tmpElement){ + ElStack.push_back(tmpElement); //if first element of this type + StrStack.push_back(tag); + return true; + } + } + + if (IsSameTag && RepeatingTag) CloseElement(); //automatically close the tag we were searching repeatedly for... + return false; +} + +bool CXmlStreamRead::GetElementS(std::string tag, std::string* pString, bool RepeatingTag) +{ + if (!OpenElement(tag, RepeatingTag)) return false; + *pString = std::string(ElStack.back()->first_node()->value()); + if (*pString == "") return false; + + if (!RepeatingTag) CloseElement(); //if we're not expecting to load more tags of the same name... + return true; +} + +bool CXmlStreamRead::GetElAttS(std::string Att, std::string* pStringReturn) +{ + pTmpAtt = ElStack.back()->first_attribute(Att.c_str(), 0, false); + if (!pTmpAtt){ //right now, ignore emtpy tags, just return nothing + *pStringReturn = ""; + return false; + } + *pStringReturn = std::string(pTmpAtt->value()); + return ("" == (*pStringReturn))?false:true; +} + +bool CXmlStreamRead::GetElDataS(std::string* pStringReturn) +{ + pTmpNode = ElStack.back()->first_node(); + if (!pTmpNode){ + *pStringReturn = ""; + return false; + } + *pStringReturn = std::string(pTmpNode->value()); + return (*pStringReturn == "")?false:true; +} + + + + + +////////////////////////////////CXmlStreamWrite///////////////////////////// + + + +CXmlStreamWrite::CXmlStreamWrite() +{ + filename = ""; + LastError = ""; + ToWrite = ""; + AnyToWrite = false; + IndentNextClose = true; + WantCompressed = false; + CurIndent = 0; +} + +bool CXmlStreamWrite::BeginXmlFile(std::string FilePathToWrite, bool Compressed) +{ + filename = FilePathToWrite; + WantCompressed = Compressed; + + std::string ThisFile = filename; + if (WantCompressed) ThisFile += ".tmp"; //if compressed, create a temporary file... + + CurIndent = 0; + fp = fopen(ThisFile.c_str(),"w"); + if(fp){ + fprintf(fp,"\n"); + fprintf(fp,"\n"); + } + else {LastError += "Could not begin file at this path. Aborting.\n"; return false;} + + return true; +} + +bool CXmlStreamWrite::EndXmlFile() +{ + if (fp){ + if (AnyToWrite) fprintf(fp,"%s", ToWrite.c_str()); //write any remaining part of the last tag... + fclose(fp); + } + else {LastError += "Invalid file pointer encountered on saving XML. Aborting.\n"; return false;} + + if (WantCompressed){ + if (CompressFile(filename, filename+".tmp", &LastError)){ + remove((filename+".tmp").c_str()); //delete the temp file + return true; + } + else { //if no commpression (IE on a platform with zip compress not yet implemented) just use the ascii version + LastError += "Could not compress AMF. Saved uncompressed version instead.\n"; + rename((filename+".tmp").c_str(), filename.c_str()); + return false; + } + } + return true; +} + +void CXmlStreamWrite::OpenElement(std::string tag) +{ + if(fp){ //if write... + if (AnyToWrite) fprintf(fp,"%s", ToWrite.c_str()); //write any remaining part of the last tag... + fwrite(Ind, 1, CurIndent, fp); + IndentNextClose = true; + + fprintf(fp,"<%s", tag.c_str()); + AnyToWrite = true; + ToWrite = ">\n"; + CurIndent += 2; + StrStack.push_back(tag); + + } + +} + +void CXmlStreamWrite::CloseElement(void) +{ + if(fp){ + if (AnyToWrite) fprintf(fp,"%s", ToWrite.c_str()); //write any remaining part of the last tag... + CurIndent -= 2; + if (IndentNextClose) fwrite(Ind, 1, CurIndent, fp); + fprintf(fp,"\n", StrStack.back().c_str()); + AnyToWrite = false; + StrStack.pop_back(); + } +} diff --git a/libraries/amf/amftools-code/src/muparser/muParser.cpp b/libraries/amf/amftools-code/src/muparser/muParser.cpp new file mode 100644 index 00000000..0420703d --- /dev/null +++ b/libraries/amf/amftools-code/src/muparser/muParser.cpp @@ -0,0 +1,330 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + + Copyright (C) 2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include "muParser.h" + +//--- Standard includes ------------------------------------------------------------------------ +#include +#include +#include + +/** \brief Pi (what else?). */ +#define PARSER_CONST_PI 3.141592653589793238462643 + +/** \brief The eulerian number. */ +#define PARSER_CONST_E 2.718281828459045235360287 + +using namespace std; + +/** \file + \brief Implementation of the standard floating point parser. +*/ + + +/** \brief Namespace for mathematical applications. */ +namespace mu +{ + //--------------------------------------------------------------------------- + // Trigonometric function + value_type Parser::Sin(value_type v) { return sin(v); } + value_type Parser::Cos(value_type v) { return cos(v); } + value_type Parser::Tan(value_type v) { return tan(v); } + value_type Parser::ASin(value_type v) { return asin(v); } + value_type Parser::ACos(value_type v) { return acos(v); } + value_type Parser::ATan(value_type v) { return atan(v); } + value_type Parser::Sinh(value_type v) { return sinh(v); } + value_type Parser::Cosh(value_type v) { return cosh(v); } + value_type Parser::Tanh(value_type v) { return tanh(v); } + value_type Parser::ASinh(value_type v) { return log(v + sqrt(v * v + 1)); } + value_type Parser::ACosh(value_type v) { return log(v + sqrt(v * v - 1)); } + value_type Parser::ATanh(value_type v) { return ((value_type)0.5 * log((1 + v) / (1 - v))); } + + //--------------------------------------------------------------------------- + // Logarithm functions + value_type Parser::Log2(value_type v) { return log(v)/log((value_type)2); } // Logarithm base 2 + value_type Parser::Log10(value_type v) { return log10(v); } // Logarithm base 10 + value_type Parser::Ln(value_type v) { return log(v); } // Logarithm base e (natural logarithm) + + //--------------------------------------------------------------------------- + // misc + value_type Parser::Exp(value_type v) { return exp(v); } + value_type Parser::Abs(value_type v) { return fabs(v); } + value_type Parser::Sqrt(value_type v) { return sqrt(v); } + value_type Parser::Rint(value_type v) { return floor(v + (value_type)0.5); } + value_type Parser::Sign(value_type v) { return (value_type)((v<0) ? -1 : (v>0) ? 1 : 0); } + + //--------------------------------------------------------------------------- + /** \brief Callback for the unary minus operator. + \param v The value to negate + \return -v + */ + value_type Parser::UnaryMinus(value_type v) + { + return -v; + } + + //--------------------------------------------------------------------------- + /** \brief Callback for adding multiple values. + \param [in] a_afArg Vector with the function arguments + \param [in] a_iArgc The size of a_afArg + */ + value_type Parser::Sum(const value_type *a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw exception_type(_T("too few arguments for function sum.")); + + value_type fRes=0; + for (int i=0; i> fVal; + stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading + + if (iEnd==(stringstream_type::pos_type)-1) + return 0; + + *a_iPos += (int)iEnd; + *a_fVal = fVal; + return 1; + } + + + //--------------------------------------------------------------------------- + /** \brief Constructor. + + Call ParserBase class constructor and trigger Function, Operator and Constant initialization. + */ + Parser::Parser() + :ParserBase() + { + AddValIdent(IsVal); + + InitCharSets(); + InitFun(); + InitConst(); + InitOprt(); + } + + //--------------------------------------------------------------------------- + /** \brief Define the character sets. + \sa DefineNameChars, DefineOprtChars, DefineInfixOprtChars + + This function is used for initializing the default character sets that define + the characters to be useable in function and variable names and operators. + */ + void Parser::InitCharSets() + { + DefineNameChars( _T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") ); + DefineOprtChars( _T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-*^/?<>=#!$%&|~'_{}") ); + DefineInfixOprtChars( _T("/+-*^?<>=#!$%&|~'_") ); + } + + //--------------------------------------------------------------------------- + /** \brief Initialize the default functions. */ + void Parser::InitFun() + { + // trigonometric functions + DefineFun(_T("sin"), Sin); + DefineFun(_T("cos"), Cos); + DefineFun(_T("tan"), Tan); + // arcus functions + DefineFun(_T("asin"), ASin); + DefineFun(_T("acos"), ACos); + DefineFun(_T("atan"), ATan); + // hyperbolic functions + DefineFun(_T("sinh"), Sinh); + DefineFun(_T("cosh"), Cosh); + DefineFun(_T("tanh"), Tanh); + // arcus hyperbolic functions + DefineFun(_T("asinh"), ASinh); + DefineFun(_T("acosh"), ACosh); + DefineFun(_T("atanh"), ATanh); + // Logarithm functions + DefineFun(_T("log2"), Log2); + DefineFun(_T("log10"), Log10); + DefineFun(_T("log"), Log10); + DefineFun(_T("ln"), Ln); + // misc + DefineFun(_T("exp"), Exp); + DefineFun(_T("sqrt"), Sqrt); + DefineFun(_T("sign"), Sign); + DefineFun(_T("rint"), Rint); + DefineFun(_T("abs"), Abs); + // Functions with variable number of arguments + DefineFun(_T("sum"), Sum); + DefineFun(_T("avg"), Avg); + DefineFun(_T("min"), Min); + DefineFun(_T("max"), Max); + } + + //--------------------------------------------------------------------------- + /** \brief Initialize constants. + + By default the parser recognizes two constants. Pi ("pi") and the eulerian + number ("_e"). + */ + void Parser::InitConst() + { + DefineConst(_T("_pi"), (value_type)PARSER_CONST_PI); + DefineConst(_T("_e"), (value_type)PARSER_CONST_E); + } + + //--------------------------------------------------------------------------- + /** \brief Initialize operators. + + By default only the unary minus operator is added. + */ + void Parser::InitOprt() + { + DefineInfixOprt(_T("-"), UnaryMinus); + } + + //--------------------------------------------------------------------------- + void Parser::OnDetectVar(string_type * /*pExpr*/, int & /*nStart*/, int & /*nEnd*/) + { + // this is just sample code to illustrate modifying variable names on the fly. + // I'm not sure anyone really needs such a feature... + /* + + + string sVar(pExpr->begin()+nStart, pExpr->begin()+nEnd); + string sRepl = std::string("_") + sVar + "_"; + + int nOrigVarEnd = nEnd; + cout << "variable detected!\n"; + cout << " Expr: " << *pExpr << "\n"; + cout << " Start: " << nStart << "\n"; + cout << " End: " << nEnd << "\n"; + cout << " Var: \"" << sVar << "\"\n"; + cout << " Repl: \"" << sRepl << "\"\n"; + nEnd = nStart + sRepl.length(); + cout << " End: " << nEnd << "\n"; + pExpr->replace(pExpr->begin()+nStart, pExpr->begin()+nOrigVarEnd, sRepl); + cout << " New expr: " << *pExpr << "\n"; + */ + } + + //--------------------------------------------------------------------------- + /** \brief Numerically differentiate with regard to a variable. + \param [in] a_Var Pointer to the differentiation variable. + \param [in] a_fPos Position at which the differentiation should take place. + \param [in] a_fEpsilon Epsilon used for the numerical differentiation. + + Numerical differentiation uses a 5 point operator yielding a 4th order + formula. The default value for epsilon is 0.00074 which is + numeric_limits::epsilon() ^ (1/5) as suggested in the muparser + forum: + + http://sourceforge.net/forum/forum.php?thread_id=1994611&forum_id=462843 + */ + value_type Parser::Diff(value_type *a_Var, + value_type a_fPos, + value_type a_fEpsilon) const + { + value_type fRes(0), + fBuf(*a_Var), + f[4] = {0,0,0,0}, + fEpsilon(a_fEpsilon); + + // Backwards compatible calculation of epsilon inc case the user doesnt provide + // his own epsilon + if (fEpsilon==0) + fEpsilon = (a_fPos==0) ? (value_type)1e-10 : (value_type)1e-7 * a_fPos; + + *a_Var = a_fPos+2 * fEpsilon; f[0] = Eval(); + *a_Var = a_fPos+1 * fEpsilon; f[1] = Eval(); + *a_Var = a_fPos-1 * fEpsilon; f[2] = Eval(); + *a_Var = a_fPos-2 * fEpsilon; f[3] = Eval(); + *a_Var = fBuf; // restore variable + + fRes = (-f[0] + 8*f[1] - 8*f[2] + f[3]) / (12*fEpsilon); + return fRes; + } +} // namespace mu diff --git a/libraries/amf/amftools-code/src/muparser/muParserBase.cpp b/libraries/amf/amftools-code/src/muparser/muParserBase.cpp new file mode 100644 index 00000000..21f1d6e0 --- /dev/null +++ b/libraries/amf/amftools-code/src/muparser/muParserBase.cpp @@ -0,0 +1,1978 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "muParserBase.h" + +//--- Standard includes ------------------------------------------------------------------------ +#include +#include +#include +#include +#include +#include +#include + +#ifdef MUP_USE_OPENMP + #include +#endif + +using namespace std; + +/** \file + \brief This file contains the basic implementation of the muparser engine. +*/ + +namespace mu +{ + std::locale ParserBase::s_locale = std::locale(std::locale::classic(), new change_dec_sep('.')); + + bool ParserBase::g_DbgDumpCmdCode = false; + bool ParserBase::g_DbgDumpStack = false; + + //------------------------------------------------------------------------------ + /** \brief Identifiers for built in binary operators. + + When defining custom binary operators with #AddOprt(...) make sure not to choose + names conflicting with these definitions. + */ + const char_type* ParserBase::c_DefaultOprt[] = + { + _T("<="), _T(">="), _T("!="), + _T("=="), _T("<"), _T(">"), + _T("+"), _T("-"), _T("*"), + _T("/"), _T("^"), /*_T("and"), + _T("or"), _T("xor"),*/ _T("&&"), + _T("||"), _T("="), _T("("), + _T(")"), _T("?"), _T(":"), 0 + }; + + //------------------------------------------------------------------------------ + /** \brief Constructor. + \param a_szFormula the formula to interpret. + \throw ParserException if a_szFormula is null. + */ + ParserBase::ParserBase() + :m_pParseFormula(&ParserBase::ParseString) + ,m_pRPN(NULL) + ,m_vRPN() + ,m_vStringBuf() + ,m_pTokenReader() + ,m_FunDef() + ,m_PostOprtDef() + ,m_InfixOprtDef() + ,m_OprtDef() + ,m_ConstDef() + ,m_StrVarDef() + ,m_VarDef() + ,m_bOptimize(true) + ,m_bBuiltInOp(true) + ,m_sNameChars() + ,m_sOprtChars() + ,m_sInfixOprtChars() + ,m_nIfElseCounter(0) + ,m_vStackBuffer() + ,m_nFinalResultIdx(0) + { + InitTokenReader(); + } + + //--------------------------------------------------------------------------- + /** \brief Copy constructor. + + Tha parser can be safely copy constructed but the bytecode is reset during + copy construction. + */ + ParserBase::ParserBase(const ParserBase &a_Parser) + :m_pParseFormula(&ParserBase::ParseString) + ,m_pRPN(NULL) + ,m_vRPN() + ,m_vStringBuf() + ,m_pTokenReader() + ,m_FunDef() + ,m_PostOprtDef() + ,m_InfixOprtDef() + ,m_nIfElseCounter(0) + ,m_OprtDef() + ,m_ConstDef() + ,m_StrVarDef() + ,m_VarDef() + ,m_bOptimize(true) + ,m_bBuiltInOp(true) + { + m_pTokenReader.reset(new token_reader_type(this)); + Assign(a_Parser); + } + + //--------------------------------------------------------------------------- + ParserBase::~ParserBase() + {} + + //--------------------------------------------------------------------------- + /** \brief Assignement operator. + + Implemented by calling Assign(a_Parser). Self assignement is suppressed. + \param a_Parser Object to copy to this. + \return *this + \throw nothrow + */ + ParserBase& ParserBase::operator=(const ParserBase &a_Parser) + { + Assign(a_Parser); + return *this; + } + + //--------------------------------------------------------------------------- + /** \brief Copy state of a parser object to this. + + Clears Variables and Functions of this parser. + Copies the states of all internal variables. + Resets parse function to string parse mode. + + \param a_Parser the source object. + */ + void ParserBase::Assign(const ParserBase &a_Parser) + { + if (&a_Parser==this) + return; + + // Don't copy bytecode instead cause the parser to create new bytecode + // by resetting the parse function. + ReInit(); + + m_ConstDef = a_Parser.m_ConstDef; // Copy user define constants + m_VarDef = a_Parser.m_VarDef; // Copy user defined variables + m_bOptimize = a_Parser.m_bOptimize; + m_bBuiltInOp = a_Parser.m_bBuiltInOp; + m_vStringBuf = a_Parser.m_vStringBuf; + m_vStackBuffer = a_Parser.m_vStackBuffer; + m_nFinalResultIdx = a_Parser.m_nFinalResultIdx; + m_StrVarDef = a_Parser.m_StrVarDef; + m_vStringVarBuf = a_Parser.m_vStringVarBuf; + m_nIfElseCounter = a_Parser.m_nIfElseCounter; + m_pTokenReader.reset(a_Parser.m_pTokenReader->Clone(this)); + + // Copy function and operator callbacks + m_FunDef = a_Parser.m_FunDef; // Copy function definitions + m_PostOprtDef = a_Parser.m_PostOprtDef; // post value unary operators + m_InfixOprtDef = a_Parser.m_InfixOprtDef; // unary operators for infix notation + m_OprtDef = a_Parser.m_OprtDef; // binary operators + + m_sNameChars = a_Parser.m_sNameChars; + m_sOprtChars = a_Parser.m_sOprtChars; + m_sInfixOprtChars = a_Parser.m_sInfixOprtChars; + } + + //--------------------------------------------------------------------------- + /** \brief Set the decimal separator. + \param cDecSep Decimal separator as a character value. + \sa SetThousandsSep + + By default muparser uses the "C" locale. The decimal separator of this + locale is overwritten by the one provided here. + */ + void ParserBase::SetDecSep(char_type cDecSep) + { + char_type cThousandsSep = std::use_facet< change_dec_sep >(s_locale).thousands_sep(); + s_locale = std::locale(std::locale("C"), new change_dec_sep(cDecSep, cThousandsSep)); + } + + //--------------------------------------------------------------------------- + /** \brief Sets the thousands operator. + \param cThousandsSep The thousands separator as a character + \sa SetDecSep + + By default muparser uses the "C" locale. The thousands separator of this + locale is overwritten by the one provided here. + */ + void ParserBase::SetThousandsSep(char_type cThousandsSep) + { + char_type cDecSep = std::use_facet< change_dec_sep >(s_locale).decimal_point(); + s_locale = std::locale(std::locale("C"), new change_dec_sep(cDecSep, cThousandsSep)); + } + + //--------------------------------------------------------------------------- + /** \brief Resets the locale. + + The default locale used "." as decimal separator, no thousands separator and + "," as function argument separator. + */ + void ParserBase::ResetLocale() + { + s_locale = std::locale(std::locale("C"), new change_dec_sep('.')); + SetArgSep(','); + } + + //--------------------------------------------------------------------------- + /** \brief Initialize the token reader. + + Create new token reader object and submit pointers to function, operator, + constant and variable definitions. + + \post m_pTokenReader.get()!=0 + \throw nothrow + */ + void ParserBase::InitTokenReader() + { + m_pTokenReader.reset(new token_reader_type(this)); + } + + //--------------------------------------------------------------------------- + /** \brief Reset parser to string parsing mode and clear internal buffers. + + Clear bytecode, reset the token reader. + \throw nothrow + */ + void ParserBase::ReInit() const + { + m_pParseFormula = &ParserBase::ParseString; + m_vStringBuf.clear(); + m_vRPN.clear(); + m_pTokenReader->ReInit(); + m_nIfElseCounter = 0; + } + + //--------------------------------------------------------------------------- + void ParserBase::OnDetectVar(string_type * /*pExpr*/, int & /*nStart*/, int & /*nEnd*/) + {} + + //--------------------------------------------------------------------------- + /** \brief Returns the version of muparser. + \param eInfo A flag indicating whether the full version info should be + returned or not. + + Format is as follows: "MAJOR.MINOR (COMPILER_FLAGS)" The COMPILER_FLAGS + are returned only if eInfo==pviFULL. + */ + string_type ParserBase::GetVersion(EParserVersionInfo eInfo) const + { + string_type sCompileTimeSettings; + + stringstream_type ss; + + ss << MUP_VERSION; + + if (eInfo==pviFULL) + { + ss << _T(" (") << MUP_VERSION_DATE; + ss << std::dec << _T("; ") << sizeof(void*)*8 << _T("BIT"); + +#ifdef _DEBUG + ss << _T("; DEBUG"); +#else + ss << _T("; RELEASE"); +#endif + +#ifdef _UNICODE + ss << _T("; UNICODE"); +#else + #ifdef _MBCS + ss << _T("; MBCS"); + #else + ss << _T("; ASCII"); + #endif +#endif + +#ifdef MUP_USE_OPENMP + ss << _T("; OPENMP"); +//#else +// ss << _T("; NO_OPENMP"); +#endif + +#if defined(MUP_MATH_EXCEPTIONS) + ss << _T("; MATHEXC"); +//#else +// ss << _T("; NO_MATHEXC"); +#endif + + ss << _T(")"); + } + + return ss.str(); + } + + //--------------------------------------------------------------------------- + /** \brief Add a value parsing function. + + When parsing an expression muParser tries to detect values in the expression + string using different valident callbacks. Thuis it's possible to parse + for hex values, binary values and floating point values. + */ + void ParserBase::AddValIdent(identfun_type a_pCallback) + { + m_pTokenReader->AddValIdent(a_pCallback); + } + + //--------------------------------------------------------------------------- + /** \brief Set a function that can create variable pointer for unknown expression variables. + \param a_pFactory A pointer to the variable factory. + \param pUserData A user defined context pointer. + */ + void ParserBase::SetVarFactory(facfun_type a_pFactory, void *pUserData) + { + m_pTokenReader->SetVarCreator(a_pFactory, pUserData); + } + + //--------------------------------------------------------------------------- + /** \brief Add a function or operator callback to the parser. */ + void ParserBase::AddCallback( const string_type &a_strName, + const ParserCallback &a_Callback, + funmap_type &a_Storage, + const char_type *a_szCharSet ) + { + if (a_Callback.GetAddr()==0) + Error(ecINVALID_FUN_PTR); + + const funmap_type *pFunMap = &a_Storage; + + // Check for conflicting operator or function names + if ( pFunMap!=&m_FunDef && m_FunDef.find(a_strName)!=m_FunDef.end() ) + Error(ecNAME_CONFLICT, -1, a_strName); + + if ( pFunMap!=&m_PostOprtDef && m_PostOprtDef.find(a_strName)!=m_PostOprtDef.end() ) + Error(ecNAME_CONFLICT, -1, a_strName); + + if ( pFunMap!=&m_InfixOprtDef && pFunMap!=&m_OprtDef && m_InfixOprtDef.find(a_strName)!=m_InfixOprtDef.end() ) + Error(ecNAME_CONFLICT, -1, a_strName); + + if ( pFunMap!=&m_InfixOprtDef && pFunMap!=&m_OprtDef && m_OprtDef.find(a_strName)!=m_OprtDef.end() ) + Error(ecNAME_CONFLICT, -1, a_strName); + + CheckOprt(a_strName, a_Callback, a_szCharSet); + a_Storage[a_strName] = a_Callback; + ReInit(); + } + + //--------------------------------------------------------------------------- + /** \brief Check if a name contains invalid characters. + + \throw ParserException if the name contains invalid charakters. + */ + void ParserBase::CheckOprt(const string_type &a_sName, + const ParserCallback &a_Callback, + const string_type &a_szCharSet) const + { + if ( !a_sName.length() || + (a_sName.find_first_not_of(a_szCharSet)!=string_type::npos) || + (a_sName[0]>='0' && a_sName[0]<='9')) + { + switch(a_Callback.GetCode()) + { + case cmOPRT_POSTFIX: Error(ecINVALID_POSTFIX_IDENT, -1, a_sName); + case cmOPRT_INFIX: Error(ecINVALID_INFIX_IDENT, -1, a_sName); + default: Error(ecINVALID_NAME, -1, a_sName); + } + } + } + + //--------------------------------------------------------------------------- + /** \brief Check if a name contains invalid characters. + + \throw ParserException if the name contains invalid charakters. + */ + void ParserBase::CheckName(const string_type &a_sName, + const string_type &a_szCharSet) const + { + if ( !a_sName.length() || + (a_sName.find_first_not_of(a_szCharSet)!=string_type::npos) || + (a_sName[0]>='0' && a_sName[0]<='9')) + { + Error(ecINVALID_NAME); + } + } + + //--------------------------------------------------------------------------- + /** \brief Set the formula. + \param a_strFormula Formula as string_type + \throw ParserException in case of syntax errors. + + Triggers first time calculation thus the creation of the bytecode and + scanning of used variables. + */ + void ParserBase::SetExpr(const string_type &a_sExpr) + { + // Check locale compatibility + std::locale loc; + if (m_pTokenReader->GetArgSep()==std::use_facet >(loc).decimal_point()) + Error(ecLOCALE); + + // 20060222: Bugfix for Borland-Kylix: + // adding a space to the expression will keep Borlands KYLIX from going wild + // when calling tellg on a stringstream created from the expression after + // reading a value at the end of an expression. (mu::Parser::IsVal function) + // (tellg returns -1 otherwise causing the parser to ignore the value) + string_type sBuf(a_sExpr + _T(" ") ); + m_pTokenReader->SetFormula(sBuf); + ReInit(); + } + + //--------------------------------------------------------------------------- + /** \brief Get the default symbols used for the built in operators. + \sa c_DefaultOprt + */ + const char_type** ParserBase::GetOprtDef() const + { + return (const char_type **)(&c_DefaultOprt[0]); + } + + //--------------------------------------------------------------------------- + /** \brief Define the set of valid characters to be used in names of + functions, variables, constants. + */ + void ParserBase::DefineNameChars(const char_type *a_szCharset) + { + m_sNameChars = a_szCharset; + } + + //--------------------------------------------------------------------------- + /** \brief Define the set of valid characters to be used in names of + binary operators and postfix operators. + */ + void ParserBase::DefineOprtChars(const char_type *a_szCharset) + { + m_sOprtChars = a_szCharset; + } + + //--------------------------------------------------------------------------- + /** \brief Define the set of valid characters to be used in names of + infix operators. + */ + void ParserBase::DefineInfixOprtChars(const char_type *a_szCharset) + { + m_sInfixOprtChars = a_szCharset; + } + + //--------------------------------------------------------------------------- + /** \brief Virtual function that defines the characters allowed in name identifiers. + \sa #ValidOprtChars, #ValidPrefixOprtChars + */ + const char_type* ParserBase::ValidNameChars() const + { + assert(m_sNameChars.size()); + return m_sNameChars.c_str(); + } + + //--------------------------------------------------------------------------- + /** \brief Virtual function that defines the characters allowed in operator definitions. + \sa #ValidNameChars, #ValidPrefixOprtChars + */ + const char_type* ParserBase::ValidOprtChars() const + { + assert(m_sOprtChars.size()); + return m_sOprtChars.c_str(); + } + + //--------------------------------------------------------------------------- + /** \brief Virtual function that defines the characters allowed in infix operator definitions. + \sa #ValidNameChars, #ValidOprtChars + */ + const char_type* ParserBase::ValidInfixOprtChars() const + { + assert(m_sInfixOprtChars.size()); + return m_sInfixOprtChars.c_str(); + } + + //--------------------------------------------------------------------------- + /** \brief Add a user defined operator. + \post Will reset the Parser to string parsing mode. + */ + void ParserBase::DefinePostfixOprt(const string_type &a_sName, + fun_type1 a_pFun, + bool a_bAllowOpt) + { + AddCallback(a_sName, + ParserCallback(a_pFun, a_bAllowOpt, prPOSTFIX, cmOPRT_POSTFIX), + m_PostOprtDef, + ValidOprtChars() ); + } + + //--------------------------------------------------------------------------- + /** \brief Initialize user defined functions. + + Calls the virtual functions InitFun(), InitConst() and InitOprt(). + */ + void ParserBase::Init() + { + InitCharSets(); + InitFun(); + InitConst(); + InitOprt(); + } + + //--------------------------------------------------------------------------- + /** \brief Add a user defined operator. + \post Will reset the Parser to string parsing mode. + \param [in] a_sName operator Identifier + \param [in] a_pFun Operator callback function + \param [in] a_iPrec Operator Precedence (default=prSIGN) + \param [in] a_bAllowOpt True if operator is volatile (default=false) + \sa EPrec + */ + void ParserBase::DefineInfixOprt(const string_type &a_sName, + fun_type1 a_pFun, + int a_iPrec, + bool a_bAllowOpt) + { + AddCallback(a_sName, + ParserCallback(a_pFun, a_bAllowOpt, a_iPrec, cmOPRT_INFIX), + m_InfixOprtDef, + ValidInfixOprtChars() ); + } + + + //--------------------------------------------------------------------------- + /** \brief Define a binary operator. + \param [in] a_pFun Pointer to the callback function. + \param [in] a_iPrec Precedence of the operator. + \param [in] a_bAllowOpt If this is true the operator may be optimized away. + */ + void ParserBase::DefineOprt( const string_type &a_sName, + fun_type2 a_pFun, + unsigned a_iPrec, + EOprtAssociativity a_eAssociativity, + bool a_bAllowOpt ) + { + // Check for conflicts with built in operator names + for (int i=0; m_bBuiltInOp && iIgnoreUndefVar(true); + ParseString(); // implicitely create or update the map with the + // used variables stored in the token reader if not already done + m_pTokenReader->IgnoreUndefVar(false); + } + catch(exception_type &e) + { + m_pTokenReader->IgnoreUndefVar(false); + throw e; + } + + // Make sure to stay in string parse mode, dont call ReInit() + // because it deletes the array with the used variables + m_pParseFormula = &ParserBase::ParseString; + + return m_pTokenReader->GetUsedVar(); + } + + //--------------------------------------------------------------------------- + /** \brief Return a map containing the used variables only. */ + const varmap_type& ParserBase::GetVar() const + { + return m_VarDef; + } + + //--------------------------------------------------------------------------- + /** \brief Return a map containing all parser constants. */ + const valmap_type& ParserBase::GetConst() const + { + return m_ConstDef; + } + + //--------------------------------------------------------------------------- + /** \brief Return prototypes of all parser functions. + \return #m_FunDef + \sa FunProt + \throw nothrow + + The return type is a map of the public type #funmap_type containing the prototype + definitions for all numerical parser functions. String functions are not part of + this map. The Prototype definition is encapsulated in objects of the class FunProt + one per parser function each associated with function names via a map construct. + */ + const funmap_type& ParserBase::GetFunDef() const + { + return m_FunDef; + } + + //--------------------------------------------------------------------------- + /** \brief Retrieve the formula. */ + const string_type& ParserBase::GetExpr() const + { + return m_pTokenReader->GetExpr(); + } + + //--------------------------------------------------------------------------- + ParserBase::token_type ParserBase::ApplyNumFunc(const token_type &a_FunTok, + const std::vector &a_vArg) const + { + token_type valTok; + int iArgCount = (unsigned)a_vArg.size(); + void *pFunc = a_FunTok.GetFuncAddr(); + assert(pFunc); + + // Collect the function arguments from the value stack + switch(a_FunTok.GetArgCount()) + { + case -1: + // Function with variable argument count + // copy arguments into a vector + { + if (iArgCount==0) + Error(ecTOO_FEW_PARAMS, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); + + std::vector vArg(iArgCount); + for (int i=0; i &a_vArg) const + { + // I dont't really do any calculation here. A dummy value serving as a placeholder + // for the bulk functions return value is returned. This works as long as the optimizer + // is deactivated with the flVOLATILE flag. This is ok since the result of the first + // parsing run from string is now discarded anyway. (since V1.35) + token_type valTok; + valTok.SetVal(1); + valTok.AddFlags(token_type::flVOLATILE); + m_vRPN.AddBulkFun(a_FunTok.GetFuncAddr(), (int)a_vArg.size()); + return valTok; + } + + //--------------------------------------------------------------------------- + /** \brief Execute a function that takes a single string argument. + \param a_FunTok Function token. + \throw exception_type If the function token is not a string function + */ + ParserBase::token_type ParserBase::ApplyStrFunc(const token_type &a_FunTok, + const std::vector &a_vArg) const + { + if (a_vArg.back().GetCode()!=cmSTRING) + Error(ecSTRING_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); + + token_type valTok; + int iArgCount = (unsigned)a_vArg.size(); + void *pFunc = a_FunTok.GetFuncAddr(); + assert(pFunc); + + try + { + // Collect the function arguments from the value stack + switch(a_FunTok.GetArgCount()) + { + case 0: valTok.SetVal( ((strfun_type1)pFunc)(a_vArg[0].GetAsString().c_str()) ); break; + case 1: valTok.SetVal( ((strfun_type2)pFunc)(a_vArg[1].GetAsString().c_str(), + a_vArg[0].GetVal()) ); break; + case 2: valTok.SetVal( ((strfun_type3)pFunc)(a_vArg[2].GetAsString().c_str(), + a_vArg[1].GetVal(), + a_vArg[0].GetVal()) ); break; + default: Error(ecINTERNAL_ERROR); + } + } + catch(ParserError& /*e*/) + { + Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); + } + + // Find out if the result will depend on a variable + /** \todo remove this loop, put content in the loop that takes the argument values. + + (Attention: SetVal will reset Flags.) + */ + bool bVolatile = a_FunTok.IsFlagSet(token_type::flVOLATILE); + for (int i=0; (bVolatile==false) && (i &a_stOpt, + ParserStack &a_stVal, + int a_iArgCount) const + { + assert(m_pTokenReader.get()); + + // Operator stack empty or does not contain tokens with callback functions + if (a_stOpt.empty() || a_stOpt.top().GetFuncAddr()==0 ) + return; + + token_type funTok = a_stOpt.pop(); + assert(funTok.GetFuncAddr()); + + // Binary operators must rely on their internal operator number + // since counting of operators relies on commas for function arguments + // binary operators do not have commas in their expression + int iArgCount = (funTok.GetCode()==cmOPRT_BIN) ? funTok.GetArgCount() : a_iArgCount; + + // determine how many parameters the function needs. To remember iArgCount includes the + // string parameter whilst GetArgCount() counts only numeric parameters. + int iArgRequired = funTok.GetArgCount() + ((funTok.GetType()==tpSTR) ? 1 : 0); + + // Thats the number of numerical parameters + int iArgNumerical = iArgCount - ((funTok.GetType()==tpSTR) ? 1 : 0); + + if (funTok.GetCode()==cmFUNC_STR && iArgCount-iArgNumerical>1) + Error(ecINTERNAL_ERROR); + + if (funTok.GetArgCount()>=0 && iArgCount>iArgRequired) + Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos()-1, funTok.GetAsString()); + + if (funTok.GetCode()!=cmOPRT_BIN && iArgCountGetPos()-1, funTok.GetAsString()); + + if (funTok.GetCode()==cmFUNC_STR && iArgCount>iArgRequired ) + Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos()-1, funTok.GetAsString()); + + // Collect the numeric function arguments from the value stack and store them + // in a vector + std::vector stArg; + for (int i=0; iGetPos(), funTok.GetAsString()); + } + + // for string functions add the string argument + if (funTok.GetCode()==cmFUNC_STR) + { + stArg.push_back( a_stVal.pop() ); + if ( stArg.back().GetType()==tpSTR && funTok.GetType()!=tpSTR ) + Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString()); + } + + // String functions accept only one parameter + if (funTok.GetType()==tpSTR) + { + token_type token( ApplyStrFunc(funTok, stArg) ); + a_stVal.push( token ); + } + else + { + token_type token( (funTok.GetCode()==cmFUNC_BULK) ? ApplyBulkFunc(funTok, stArg) : ApplyNumFunc(funTok, stArg) ); + a_stVal.push( token ); + } + } + + //--------------------------------------------------------------------------- + void ParserBase::ApplyIfElse(ParserStack &a_stOpt, + ParserStack &a_stVal) const + { + // Check if there is an if Else clause to be calculated + while (a_stOpt.size() && a_stOpt.top().GetCode()==cmELSE) + { + token_type opElse = a_stOpt.pop(); + MUP_ASSERT(a_stOpt.size()>0); + + // Take the value associated with the else branch from the value stack + token_type vVal2 = a_stVal.pop(); + + MUP_ASSERT(a_stOpt.size()>0); + MUP_ASSERT(a_stVal.size()>=2); + + // it then else is a ternary operator Pop all three values from the value s + // tack and just return the right value + token_type vVal1 = a_stVal.pop(); + token_type vExpr = a_stVal.pop(); + + a_stVal.push( (vExpr.GetVal()!=0) ? vVal1 : vVal2); + // Result of if then else is always volatile, the + // function optimizer won't handle it properly + a_stVal.top().AddFlags(token_type::flVOLATILE); + + token_type opIf = a_stOpt.pop(); + MUP_ASSERT(opElse.GetCode()==cmELSE); + MUP_ASSERT(opIf.GetCode()==cmIF); + + m_vRPN.AddIfElse(cmENDIF); + } // while pending if-else-clause found + } + //--------------------------------------------------------------------------- + void ParserBase::ApplyBinOprt(ParserStack &a_stOpt, + ParserStack &a_stVal) const + { + if (a_stOpt.top().GetCode()==cmOPRT_INFIX) + { + // First check for presence of an infix operator + ApplyFunc(a_stOpt, a_stVal, 1); + } + else + { + // user defined binary operator + if (a_stOpt.top().GetCode()==cmOPRT_BIN) + { + ApplyFunc(a_stOpt, a_stVal, 2); + } + else + { + // internal binary operator + MUP_ASSERT(a_stVal.size()>=2); + + token_type valTok1 = a_stVal.pop(), + valTok2 = a_stVal.pop(), + optTok = a_stOpt.pop(), + resTok; + + if ( valTok1.GetType()!=valTok2.GetType() || + (valTok1.GetType()==tpSTR && valTok2.GetType()==tpSTR) ) + Error(ecOPRT_TYPE_CONFLICT, m_pTokenReader->GetPos(), optTok.GetAsString()); + + value_type x = valTok2.GetVal(), + y = valTok1.GetVal(); + + switch (optTok.GetCode()) + { + // built in binary operators + //case cmAND: resTok.SetVal( (int)x & (int)y ); break; + //case cmOR: resTok.SetVal( (int)x | (int)y ); break; + //case cmXOR: resTok.SetVal( (int)x ^ (int)y ); break; + case cmLAND: resTok.SetVal( (int)x && (int)y ); break; + case cmLOR: resTok.SetVal( (int)x || (int)y ); break; + case cmLT: resTok.SetVal( x < y ); break; + case cmGT: resTok.SetVal( x > y ); break; + case cmLE: resTok.SetVal( x <= y ); break; + case cmGE: resTok.SetVal( x >= y ); break; + case cmNEQ: resTok.SetVal( x != y ); break; + case cmEQ: resTok.SetVal( x == y ); break; + case cmADD: resTok.SetVal( x + y ); break; + case cmSUB: resTok.SetVal( x - y ); break; + case cmMUL: resTok.SetVal( x * y ); break; + case cmDIV: + #if defined(MUP_MATH_EXCEPTIONS) + if (y==0) + { + Error(ecDIV_BY_ZERO); + } + #endif + resTok.SetVal( x / y ); + break; + case cmPOW: resTok.SetVal(pow(x, y)); break; + + case cmASSIGN: + // The assignement operator needs special treatment + // it uses a different format when stored in the bytecode! + { + if (valTok2.GetCode()!=cmVAR) + Error(ecUNEXPECTED_OPERATOR, -1, _T("=")); + + // + // The Assignment does no longer take place in the string parsing step. This would + // change the variable value when performing lazy evaluation of if-then-else clauses. + // Consequently the numerical result from the string parsing step maybe incorrect in such + // cases. This is not a problem it is discarded anyway! + // Problem case: "0? a=10:0,a" -> Would return 10 instead of 0 which is incorrect! + // + + value_type *pVar = valTok2.GetVar(); + resTok.SetVal( /*pVar =*/ y ); + resTok.AddFlags(token_type::flVOLATILE); + a_stVal.push( resTok ); + + m_vRPN.AddAssignOp(pVar); + return; // we must return since the following + // stuff does not apply + } + + default: Error(ecINTERNAL_ERROR, 8); + } + + // Create the bytecode entries + if (!m_bOptimize) + { + // Optimization flag is not set + m_vRPN.AddOp(optTok.GetCode()); + } + else if ( valTok1.IsFlagSet(token_type::flVOLATILE) || + valTok2.IsFlagSet(token_type::flVOLATILE) ) + { + // Optimization flag is not set, but one of the value + // depends on a variable + m_vRPN.AddOp(optTok.GetCode()); + resTok.AddFlags(token_type::flVOLATILE); + } + else + { + // operator call can be optimized; If optimization is possible + // the two previous tokens must be value tokens / they will be removed + // and replaced with the result of the pending operation. + m_vRPN.RemoveValEntries(2); + m_vRPN.AddVal(resTok.GetVal()); + } + + a_stVal.push( resTok ); + } + } + } + + //--------------------------------------------------------------------------- + /** \brief Apply a binary operator. + \param a_stOpt The operator stack + \param a_stVal The value stack + */ + void ParserBase::ApplyRemainingOprt(ParserStack &stOpt, + ParserStack &stVal) const + { + while (stOpt.size() && + stOpt.top().GetCode() != cmBO && + stOpt.top().GetCode() != cmIF) + { + token_type tok = stOpt.top(); + switch (tok.GetCode()) + { + case cmOPRT_INFIX: + case cmOPRT_BIN: + case cmLE: + case cmGE: + case cmNEQ: + case cmEQ: + case cmLT: + case cmGT: + case cmADD: + case cmSUB: + case cmMUL: + case cmDIV: + case cmPOW: + //case cmAND: + //case cmOR: + //case cmXOR: + case cmLAND: + case cmLOR: + case cmASSIGN: + ApplyBinOprt(stOpt, stVal); + break; + + case cmELSE: + ApplyIfElse(stOpt, stVal); + break; + + default: + Error(ecINTERNAL_ERROR); + } + } + } + + //--------------------------------------------------------------------------- + /** \brief Parse the command code. + \sa ParseString(...) + + Command code contains precalculated stack positions of the values and the + associated operators. The Stack is filled beginning from index one the + value at index zero is not used at all. + */ + value_type ParserBase::ParseCmdCode() const + { + return ParseCmdCodeBulk(0, 0); + } + + //--------------------------------------------------------------------------- + /** \brief Custum Pow function with optimization if the power is an integer value. */ + value_type ParserBase::Pow(value_type v1, value_type v2) + { + int v2i = (int)v2; + if (v2==v2i) + { + switch(v2i) + { + case 0: return 1; + case 1: return v1; + case 2: return v1*v1; + case 3: return v1*v1*v1; + case 4: return v1*v1*v1*v1; + case 5: return v1*v1*v1*v1*v1; + default: return std::pow(v1, v2i); + } + } + else + return std::pow(v1, v2); + } + + //--------------------------------------------------------------------------- + /** \brief Evaluate the RPN. + \param nOffset The offset added to variable addresses (for bulk mode) + \param nThreadID OpenMP Thread id of the calling thread + */ + value_type ParserBase::ParseCmdCodeBulk(int nOffset, int nThreadID) const + { + assert(nThreadID<=s_MaxNumOpenMPThreads); + + value_type *Stack = &m_vStackBuffer[nThreadID * (m_vStackBuffer.size() / s_MaxNumOpenMPThreads)]; + int sidx(0); + + for (const SToken *pTok = m_pRPN; ; ++pTok) + { + switch (pTok->Cmd) + { + // built in binary operators + case cmLAND: --sidx; Stack[sidx] = Stack[sidx] && Stack[sidx+1]; continue; + case cmLOR: --sidx; Stack[sidx] = Stack[sidx] || Stack[sidx+1]; continue; + case cmLE: --sidx; Stack[sidx] = Stack[sidx] <= Stack[sidx+1]; continue; + case cmGE: --sidx; Stack[sidx] = Stack[sidx] >= Stack[sidx+1]; continue; + case cmNEQ: --sidx; Stack[sidx] = Stack[sidx] != Stack[sidx+1]; continue; + case cmEQ: --sidx; Stack[sidx] = Stack[sidx] == Stack[sidx+1]; continue; + case cmLT: --sidx; Stack[sidx] = Stack[sidx] < Stack[sidx+1]; continue; + case cmGT: --sidx; Stack[sidx] = Stack[sidx] > Stack[sidx+1]; continue; + case cmADD: --sidx; Stack[sidx] += Stack[1+sidx]; continue; + case cmSUB: --sidx; Stack[sidx] -= Stack[1+sidx]; continue; + case cmMUL: --sidx; Stack[sidx] *= Stack[1+sidx]; continue; + case cmDIV: --sidx; + + #if defined(MUP_MATH_EXCEPTIONS) + if (Stack[1+sidx]==0) + Error(ecDIV_BY_ZERO); + #endif + Stack[sidx] /= Stack[1+sidx]; + continue; + + case cmPOW: + { + --sidx; + Stack[sidx] = ParserBase::Pow(Stack[sidx], Stack[1+sidx]); + continue; + } + + case cmASSIGN: + --sidx; Stack[sidx] = *pTok->Oprt.ptr = Stack[sidx+1]; continue; + + case cmIF: + if (Stack[sidx--]==0) + pTok += pTok->Oprt.offset; + + continue; + + case cmELSE: + pTok += pTok->Oprt.offset; + continue; + + case cmENDIF: + continue; + + // value and variable tokens + case cmVAR: Stack[++sidx] = *(pTok->Val.ptr + nOffset); continue; + case cmVAL: Stack[++sidx] = pTok->Val.data; continue; + + // Next is treatment of string functions + case cmFUNC_STR: + { + sidx -= pTok->Fun.argc -1; + + // The index of the string argument in the string table + int iIdxStack = pTok->Fun.idx; + MUP_ASSERT( iIdxStack>=0 && iIdxStack<(int)m_vStringBuf.size() ); + + switch(pTok->Fun.argc) // switch according to argument count + { + case 0: Stack[sidx] = (*(strfun_type1)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str()); continue; + case 1: Stack[sidx] = (*(strfun_type2)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx]); continue; + case 2: Stack[sidx] = (*(strfun_type3)pTok->Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx], Stack[sidx+1]); continue; + } + + continue; + } + + // Next is treatment of numeric functions + case cmFUNC: + { + int iArgCount = pTok->Fun.argc; + + // switch according to argument count + switch(iArgCount) + { + case 0: sidx -= iArgCount -1; Stack[sidx] = (*(fun_type0)pTok->Fun.ptr)(); continue; + case 1: sidx -= iArgCount -1; Stack[sidx] = (*(fun_type1)pTok->Fun.ptr)(Stack[sidx]); continue; + case 2: sidx -= iArgCount -1; Stack[sidx] = (*(fun_type2)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1]); continue; + case 3: sidx -= iArgCount -1; Stack[sidx] = (*(fun_type3)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2]); continue; + case 4: sidx -= iArgCount -1; Stack[sidx] = (*(fun_type4)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3]); continue; + case 5: sidx -= iArgCount -1; Stack[sidx] = (*(fun_type5)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4]); continue; + case 6: sidx -= iArgCount -1; Stack[sidx] = (*(fun_type6)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5]); continue; + case 7: sidx -= iArgCount -1; Stack[sidx] = (*(fun_type7)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6]); continue; + case 8: sidx -= iArgCount -1; Stack[sidx] = (*(fun_type8)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7]); continue; + case 9: sidx -= iArgCount -1; Stack[sidx] = (*(fun_type9)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7], Stack[sidx+8]); continue; + case 10:sidx -= iArgCount -1; Stack[sidx] = (*(fun_type10)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7], Stack[sidx+8], Stack[sidx+9]); continue; + default: + if (iArgCount>0) // function with variable arguments store the number as a negative value + Error(ecINTERNAL_ERROR, 1); + + sidx -= -iArgCount - 1; + Stack[sidx] =(*(multfun_type)pTok->Fun.ptr)(&Stack[sidx], -iArgCount); + continue; + } + } + + case cmFUNC_BULK: + { + int iArgCount = pTok->Fun.argc; + + // switch according to argument count + switch(iArgCount) + { + case 0: sidx -= iArgCount -1; Stack[sidx] = (*(bulkfun_type0 )pTok->Fun.ptr)(nOffset, nThreadID); continue; + case 1: sidx -= iArgCount -1; Stack[sidx] = (*(bulkfun_type1 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx]); continue; + case 2: sidx -= iArgCount -1; Stack[sidx] = (*(bulkfun_type2 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1]); continue; + case 3: sidx -= iArgCount -1; Stack[sidx] = (*(bulkfun_type3 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2]); continue; + case 4: sidx -= iArgCount -1; Stack[sidx] = (*(bulkfun_type4 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3]); continue; + case 5: sidx -= iArgCount -1; Stack[sidx] = (*(bulkfun_type5 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4]); continue; + case 6: sidx -= iArgCount -1; Stack[sidx] = (*(bulkfun_type6 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5]); continue; + case 7: sidx -= iArgCount -1; Stack[sidx] = (*(bulkfun_type7 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6]); continue; + case 8: sidx -= iArgCount -1; Stack[sidx] = (*(bulkfun_type8 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7]); continue; + case 9: sidx -= iArgCount -1; Stack[sidx] = (*(bulkfun_type9 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7], Stack[sidx+8]); continue; + case 10:sidx -= iArgCount -1; Stack[sidx] = (*(bulkfun_type10)pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7], Stack[sidx+8], Stack[sidx+9]); continue; + default: + Error(ecINTERNAL_ERROR, 2); + continue; + } + } + + case cmEND: + return Stack[m_nFinalResultIdx]; + + default: + Error(ecINTERNAL_ERROR, 3); + return 0; + } // switch CmdCode + } + } + + //--------------------------------------------------------------------------- + void ParserBase::CreateRPN() const + { + if (!m_pTokenReader->GetExpr().length()) + Error(ecUNEXPECTED_EOF, 0); + + ParserStack stOpt, stVal; + ParserStack stArgCount; + token_type opta, opt; // for storing operators + token_type val, tval; // for storing value + string_type strBuf; // buffer for string function arguments + + ReInit(); + + // The outermost counter counts the number of seperated items + // such as in "a=10,b=20,c=c+a" + stArgCount.push(1); + + for(;;) + { + opt = m_pTokenReader->ReadNextToken(); + + switch (opt.GetCode()) + { + // + // Next three are different kind of value entries + // + case cmSTRING: + opt.SetIdx((int)m_vStringBuf.size()); // Assign buffer index to token + stVal.push(opt); + m_vStringBuf.push_back(opt.GetAsString()); // Store string in internal buffer + break; + + case cmVAR: + stVal.push(opt); + m_vRPN.AddVar( static_cast(opt.GetVar()) ); + break; + + case cmVAL: + stVal.push(opt); + m_vRPN.AddVal( opt.GetVal() ); + break; + + case cmELSE: + m_nIfElseCounter--; + if (m_nIfElseCounter<0) + Error(ecMISPLACED_COLON, m_pTokenReader->GetPos()); + + ApplyRemainingOprt(stOpt, stVal); + m_vRPN.AddIfElse(cmELSE); + stOpt.push(opt); + break; + + + case cmARG_SEP: + if (stArgCount.empty()) + Error(ecUNEXPECTED_ARG_SEP, m_pTokenReader->GetPos()); + + ++stArgCount.top(); + // fallthrough intentional (no break!) + + case cmEND: + ApplyRemainingOprt(stOpt, stVal); + break; + + case cmBC: + { + // The argument count for parameterless functions is zero + // by default an opening bracket sets parameter count to 1 + // in preparation of arguments to come. If the last token + // was an opening bracket we know better... + if (opta.GetCode()==cmBO) + --stArgCount.top(); + + ApplyRemainingOprt(stOpt, stVal); + + // Check if the bracket content has been evaluated completely + if (stOpt.size() && stOpt.top().GetCode()==cmBO) + { + // if opt is ")" and opta is "(" the bracket has been evaluated, now its time to check + // if there is either a function or a sign pending + // neither the opening nor the closing bracket will be pushed back to + // the operator stack + // Check if a function is standing in front of the opening bracket, + // if yes evaluate it afterwards check for infix operators + assert(stArgCount.size()); + int iArgCount = stArgCount.pop(); + + stOpt.pop(); // Take opening bracket from stack + + if (iArgCount>1 && ( stOpt.size()==0 || + (stOpt.top().GetCode()!=cmFUNC && + stOpt.top().GetCode()!=cmFUNC_BULK && + stOpt.top().GetCode()!=cmFUNC_STR) ) ) + Error(ecUNEXPECTED_ARG, m_pTokenReader->GetPos()); + + // The opening bracket was popped from the stack now check if there + // was a function before this bracket + if (stOpt.size() && + stOpt.top().GetCode()!=cmOPRT_INFIX && + stOpt.top().GetCode()!=cmOPRT_BIN && + stOpt.top().GetFuncAddr()!=0) + { + ApplyFunc(stOpt, stVal, iArgCount); + } + } + } // if bracket content is evaluated + break; + + // + // Next are the binary operator entries + // + //case cmAND: // built in binary operators + //case cmOR: + //case cmXOR: + case cmIF: + m_nIfElseCounter++; + // fallthrough intentional (no break!) + + case cmLAND: + case cmLOR: + case cmLT: + case cmGT: + case cmLE: + case cmGE: + case cmNEQ: + case cmEQ: + case cmADD: + case cmSUB: + case cmMUL: + case cmDIV: + case cmPOW: + case cmASSIGN: + case cmOPRT_BIN: + + // A binary operator (user defined or built in) has been found. + while ( stOpt.size() && + stOpt.top().GetCode() != cmBO && + stOpt.top().GetCode() != cmELSE && + stOpt.top().GetCode() != cmIF) + { + int nPrec1 = GetOprtPrecedence(stOpt.top()), + nPrec2 = GetOprtPrecedence(opt); + + if (stOpt.top().GetCode()==opt.GetCode()) + { + + // Deal with operator associativity + EOprtAssociativity eOprtAsct = GetOprtAssociativity(opt); + if ( (eOprtAsct==oaRIGHT && (nPrec1 <= nPrec2)) || + (eOprtAsct==oaLEFT && (nPrec1 < nPrec2)) ) + { + break; + } + } + else if (nPrec1 < nPrec2) + { + // In case the operators are not equal the precedence decides alone... + break; + } + + ApplyBinOprt(stOpt, stVal); + } // while ( ... ) + + if (opt.GetCode()==cmIF) + m_vRPN.AddIfElse(opt.GetCode()); + + // The operator can't be evaluated right now, push back to the operator stack + stOpt.push(opt); + break; + + // + // Last section contains functions and operators implicitely mapped to functions + // + case cmBO: + stArgCount.push(1); + stOpt.push(opt); + break; + + case cmOPRT_INFIX: + case cmFUNC: + case cmFUNC_BULK: + case cmFUNC_STR: + stOpt.push(opt); + break; + + case cmOPRT_POSTFIX: + stOpt.push(opt); + ApplyFunc(stOpt, stVal, 1); // this is the postfix operator + break; + + default: Error(ecINTERNAL_ERROR, 3); + } // end of switch operator-token + + opta = opt; + + if ( opt.GetCode() == cmEND ) + { + m_vRPN.Finalize(); + break; + } + + if (ParserBase::g_DbgDumpStack) + { + StackDump(stVal, stOpt); + m_vRPN.AsciiDump(); + } + } // while (true) + + // Store pointer to start of bytecode + m_pRPN = m_vRPN.GetBase(); + + if (ParserBase::g_DbgDumpCmdCode) + { + m_vRPN.AsciiDump(); + } + + if (m_nIfElseCounter>0) + Error(ecMISSING_ELSE_CLAUSE); + + // get the last value (= final result) from the stack + MUP_ASSERT(stArgCount.size()==1); + m_nFinalResultIdx = stArgCount.top(); + if (m_nFinalResultIdx==0) + Error(ecINTERNAL_ERROR, 9); + + if (stVal.size()==0) + Error(ecEMPTY_EXPRESSION); + + if (stVal.top().GetType()!=tpDBL) + Error(ecSTR_RESULT); + + m_vStackBuffer.resize(m_vRPN.GetMaxStackSize() * s_MaxNumOpenMPThreads); + } + + //--------------------------------------------------------------------------- + /** \brief One of the two main parse functions. + \sa ParseCmdCode(...) + + Parse expression from input string. Perform syntax checking and create + bytecode. After parsing the string and creating the bytecode the function + pointer #m_pParseFormula will be changed to the second parse routine the + uses bytecode instead of string parsing. + */ + value_type ParserBase::ParseString() const + { + CreateRPN(); + m_pParseFormula = &ParserBase::ParseCmdCode; + return (this->*m_pParseFormula)(); + } + + //--------------------------------------------------------------------------- + /** \brief Create an error containing the parse error position. + + This function will create an Parser Exception object containing the error text and + its position. + + \param a_iErrc [in] The error code of type #EErrorCodes. + \param a_iPos [in] The position where the error was detected. + \param a_strTok [in] The token string representation associated with the error. + \throw ParserException always throws thats the only purpose of this function. + */ + void ParserBase::Error(EErrorCodes a_iErrc, int a_iPos, const string_type &a_sTok) const + { + throw exception_type(a_iErrc, a_sTok, m_pTokenReader->GetExpr(), a_iPos); + } + + //------------------------------------------------------------------------------ + /** \brief Clear all user defined variables. + \throw nothrow + + Resets the parser to string parsing mode by calling #ReInit. + */ + void ParserBase::ClearVar() + { + m_VarDef.clear(); + ReInit(); + } + + //------------------------------------------------------------------------------ + /** \brief Remove a variable from internal storage. + \throw nothrow + + Removes a variable if it exists. If the Variable does not exist nothing will be done. + */ + void ParserBase::RemoveVar(const string_type &a_strVarName) + { + varmap_type::iterator item = m_VarDef.find(a_strVarName); + if (item!=m_VarDef.end()) + { + m_VarDef.erase(item); + ReInit(); + } + } + + //------------------------------------------------------------------------------ + /** \brief Clear all functions. + \post Resets the parser to string parsing mode. + \throw nothrow + */ + void ParserBase::ClearFun() + { + m_FunDef.clear(); + ReInit(); + } + + //------------------------------------------------------------------------------ + /** \brief Clear all user defined constants. + + Both numeric and string constants will be removed from the internal storage. + \post Resets the parser to string parsing mode. + \throw nothrow + */ + void ParserBase::ClearConst() + { + m_ConstDef.clear(); + m_StrVarDef.clear(); + ReInit(); + } + + //------------------------------------------------------------------------------ + /** \brief Clear all user defined postfix operators. + \post Resets the parser to string parsing mode. + \throw nothrow + */ + void ParserBase::ClearPostfixOprt() + { + m_PostOprtDef.clear(); + ReInit(); + } + + //------------------------------------------------------------------------------ + /** \brief Clear all user defined binary operators. + \post Resets the parser to string parsing mode. + \throw nothrow + */ + void ParserBase::ClearOprt() + { + m_OprtDef.clear(); + ReInit(); + } + + //------------------------------------------------------------------------------ + /** \brief Clear the user defined Prefix operators. + \post Resets the parser to string parser mode. + \throw nothrow + */ + void ParserBase::ClearInfixOprt() + { + m_InfixOprtDef.clear(); + ReInit(); + } + + //------------------------------------------------------------------------------ + /** \brief Enable or disable the formula optimization feature. + \post Resets the parser to string parser mode. + \throw nothrow + */ + void ParserBase::EnableOptimizer(bool a_bIsOn) + { + m_bOptimize = a_bIsOn; + ReInit(); + } + + //--------------------------------------------------------------------------- + /** \brief Enable the dumping of bytecode amd stack content on the console. + \param bDumpCmd Flag to enable dumping of the current bytecode to the console. + \param bDumpStack Flag to enable dumping of the stack content is written to the console. + + This function is for debug purposes only! + */ + void ParserBase::EnableDebugDump(bool bDumpCmd, bool bDumpStack) + { + ParserBase::g_DbgDumpCmdCode = bDumpCmd; + ParserBase::g_DbgDumpStack = bDumpStack; + } + + //------------------------------------------------------------------------------ + /** \brief Enable or disable the built in binary operators. + \throw nothrow + \sa m_bBuiltInOp, ReInit() + + If you disable the built in binary operators there will be no binary operators + defined. Thus you must add them manually one by one. It is not possible to + disable built in operators selectively. This function will Reinitialize the + parser by calling ReInit(). + */ + void ParserBase::EnableBuiltInOprt(bool a_bIsOn) + { + m_bBuiltInOp = a_bIsOn; + ReInit(); + } + + //------------------------------------------------------------------------------ + /** \brief Query status of built in variables. + \return #m_bBuiltInOp; true if built in operators are enabled. + \throw nothrow + */ + bool ParserBase::HasBuiltInOprt() const + { + return m_bBuiltInOp; + } + + //------------------------------------------------------------------------------ + /** \brief Get the argument separator character. + */ + char_type ParserBase::GetArgSep() const + { + return m_pTokenReader->GetArgSep(); + } + + //------------------------------------------------------------------------------ + /** \brief Set argument separator. + \param cArgSep the argument separator character. + */ + void ParserBase::SetArgSep(char_type cArgSep) + { + m_pTokenReader->SetArgSep(cArgSep); + } + + //------------------------------------------------------------------------------ + /** \brief Dump stack content. + + This function is used for debugging only. + */ + void ParserBase::StackDump(const ParserStack &a_stVal, + const ParserStack &a_stOprt) const + { + ParserStack stOprt(a_stOprt), + stVal(a_stVal); + + mu::console() << _T("\nValue stack:\n"); + while ( !stVal.empty() ) + { + token_type val = stVal.pop(); + if (val.GetType()==tpSTR) + mu::console() << _T(" \"") << val.GetAsString() << _T("\" "); + else + mu::console() << _T(" ") << val.GetVal() << _T(" "); + } + mu::console() << "\nOperator stack:\n"; + + while ( !stOprt.empty() ) + { + if (stOprt.top().GetCode()<=cmASSIGN) + { + mu::console() << _T("OPRT_INTRNL \"") + << ParserBase::c_DefaultOprt[stOprt.top().GetCode()] + << _T("\" \n"); + } + else + { + switch(stOprt.top().GetCode()) + { + case cmVAR: mu::console() << _T("VAR\n"); break; + case cmVAL: mu::console() << _T("VAL\n"); break; + case cmFUNC: mu::console() << _T("FUNC \"") + << stOprt.top().GetAsString() + << _T("\"\n"); break; + case cmFUNC_BULK: mu::console() << _T("FUNC_BULK \"") + << stOprt.top().GetAsString() + << _T("\"\n"); break; + case cmOPRT_INFIX: mu::console() << _T("OPRT_INFIX \"") + << stOprt.top().GetAsString() + << _T("\"\n"); break; + case cmOPRT_BIN: mu::console() << _T("OPRT_BIN \"") + << stOprt.top().GetAsString() + << _T("\"\n"); break; + case cmFUNC_STR: mu::console() << _T("FUNC_STR\n"); break; + case cmEND: mu::console() << _T("END\n"); break; + case cmUNKNOWN: mu::console() << _T("UNKNOWN\n"); break; + case cmBO: mu::console() << _T("BRACKET \"(\"\n"); break; + case cmBC: mu::console() << _T("BRACKET \")\"\n"); break; + case cmIF: mu::console() << _T("IF\n"); break; + case cmELSE: mu::console() << _T("ELSE\n"); break; + case cmENDIF: mu::console() << _T("ENDIF\n"); break; + default: mu::console() << stOprt.top().GetCode() << _T(" "); break; + } + } + stOprt.pop(); + } + + mu::console() << dec << endl; + } + + //------------------------------------------------------------------------------ + /** \brief Evaluate an expression containing comma seperated subexpressions + \param [out] nStackSize The total number of results available + \return Pointer to the array containing all expression results + + This member function can be used to retriev all results of an expression + made up of multiple comma seperated subexpressions (i.e. "x+y,sin(x),cos(y)") + */ + value_type* ParserBase::Eval(int &nStackSize) const + { + (this->*m_pParseFormula)(); + nStackSize = m_nFinalResultIdx; + + // (for historic reasons the stack starts at position 1) + return &m_vStackBuffer[1]; + } + + //--------------------------------------------------------------------------- + /** \brief Return the number of results on the calculation stack. + + If the expression contains comma seperated subexpressions (i.e. "sin(y), x+y"). + There mey be more than one return value. This function returns the number of + available results. + */ + int ParserBase::GetNumResults() const + { + return m_nFinalResultIdx; + } + + //--------------------------------------------------------------------------- + /** \brief Calculate the result. + + A note on const correctness: + I consider it important that Calc is a const function. + Due to caching operations Calc changes only the state of internal variables with one exception + m_UsedVar this is reset during string parsing and accessible from the outside. Instead of making + Calc non const GetUsedVar is non const because it explicitely calls Eval() forcing this update. + + \pre A formula must be set. + \pre Variables must have been set (if needed) + + \sa #m_pParseFormula + \return The evaluation result + \throw ParseException if no Formula is set or in case of any other error related to the formula. + */ + value_type ParserBase::Eval() const + { + return (this->*m_pParseFormula)(); + } + + //--------------------------------------------------------------------------- + void ParserBase::Eval(value_type *results, int nBulkSize) + { + CreateRPN(); + + int i = 0; + +#ifdef MUP_USE_OPENMP +//#define DEBUG_OMP_STUFF + #ifdef DEBUG_OMP_STUFF + int *pThread = new int[nBulkSize]; + int *pIdx = new int[nBulkSize]; + #endif + + int nMaxThreads = std::min(omp_get_max_threads(), s_MaxNumOpenMPThreads); + int nThreadID, ct=0; + omp_set_num_threads(nMaxThreads); + + #pragma omp parallel for schedule(static, nBulkSize/nMaxThreads) private(nThreadID) + for (i=0; i \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "muParserBytecode.h" + +#include +#include +#include +#include +#include + +#include "muParserDef.h" +#include "muParserError.h" +#include "muParserToken.h" +#include "muParserStack.h" + +/** \file + \brief Implementation of the parser bytecode class. +*/ + + +namespace mu +{ + //--------------------------------------------------------------------------- + /** \brief Bytecode default constructor. */ + ParserByteCode::ParserByteCode() + :m_iStackPos(0) + ,m_iMaxStackSize(0) + ,m_vRPN() + { + m_vRPN.reserve(50); + } + + //--------------------------------------------------------------------------- + /** \brief Copy constructor. + + Implemented in Terms of Assign(const ParserByteCode &a_ByteCode) + */ + ParserByteCode::ParserByteCode(const ParserByteCode &a_ByteCode) + { + Assign(a_ByteCode); + } + + //--------------------------------------------------------------------------- + /** \brief Assignment operator. + + Implemented in Terms of Assign(const ParserByteCode &a_ByteCode) + */ + ParserByteCode& ParserByteCode::operator=(const ParserByteCode &a_ByteCode) + { + Assign(a_ByteCode); + return *this; + } + + //--------------------------------------------------------------------------- + /** \brief Copy state of another object to this. + + \throw nowthrow + */ + void ParserByteCode::Assign(const ParserByteCode &a_ByteCode) + { + if (this==&a_ByteCode) + return; + + m_iStackPos = a_ByteCode.m_iStackPos; + m_vRPN = a_ByteCode.m_vRPN; + m_iMaxStackSize = a_ByteCode.m_iMaxStackSize; + } + + //--------------------------------------------------------------------------- + /** \brief Add a Variable pointer to bytecode. + \param a_pVar Pointer to be added. + \throw nothrow + */ + void ParserByteCode::AddVar(value_type *a_pVar) + { + ++m_iStackPos; + + SToken tok; + tok.Cmd = cmVAR; + tok.Val.ptr = a_pVar; + m_vRPN.push_back(tok); + + m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); + } + + //--------------------------------------------------------------------------- + /** \brief Add a Variable pointer to bytecode. + + Value entries in byte code consist of: +
    +
  • value array position of the value
  • +
  • the operator code according to ParserToken::cmVAL
  • +
  • the value stored in #mc_iSizeVal number of bytecode entries.
  • +
+ + \param a_pVal Value to be added. + \throw nothrow + */ + void ParserByteCode::AddVal(value_type a_fVal) + { + ++m_iStackPos; + + SToken tok; + tok.Cmd = cmVAL; + tok.Val.data = a_fVal; + m_vRPN.push_back(tok); + + m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); + } + + //--------------------------------------------------------------------------- + /** \brief Add an operator identifier to bytecode. + + Operator entries in byte code consist of: +
    +
  • value array position of the result
  • +
  • the operator code according to ParserToken::ECmdCode
  • +
+ + \sa ParserToken::ECmdCode + */ + void ParserByteCode::AddOp(ECmdCode a_Oprt) + { + --m_iStackPos; + SToken tok; + tok.Cmd = a_Oprt; + m_vRPN.push_back(tok); + +/* Testcode fr RPN optimierung. Ist zwar funktionsfhig, wird + aber derzeit nicht verwendet. (pat hier nicht richtig rein...) + + std::size_t sz = m_vRPN.size(); + + // reorder RPN to make optimization easier + // Problemflle: + // 1+sin(a)+2 + // 1+(2+a) + // (a+2)+2 + // 1+sin(a)+cos(a)+2 + switch(a_Oprt) + { + case cmADD: + if (sz>=3 && m_vRPN[sz-2].Cmd==cmVAL && m_vRPN[sz-3].Cmd==cmVAL) + { + m_vRPN[sz-3].Val.data += m_vRPN[sz-2].Val.data; + m_vRPN.pop_back(); + m_vRPN.pop_back(); + m_iStackPos++; + } + else if ( sz>=4 && + m_vRPN[sz-2].Cmd==cmVAL && + (m_vRPN[sz-3].Cmd==cmADD || m_vRPN[sz-3].Cmd==cmSUB) && + m_vRPN[sz-4].Cmd==cmVAR && + m_vRPN[sz-5].Cmd==cmVAL) + { + m_vRPN[sz-5].Val.data += m_vRPN[sz-2].Val.data; + m_vRPN.pop_back(); + m_vRPN.pop_back(); + m_iStackPos++; + } + break; + + case cmSUB: + if (sz>=3 && m_vRPN[sz-2].Cmd==cmVAL && m_vRPN[sz-3].Cmd==cmVAL) + { + m_vRPN[sz-3].Val.data -= m_vRPN[sz-2].Val.data; + m_vRPN.pop_back(); + m_vRPN.pop_back(); + m_iStackPos++; + } + else if ( sz>=4 && + m_vRPN[sz-2].Cmd==cmVAL && + (m_vRPN[sz-3].Cmd==cmADD || m_vRPN[sz-3].Cmd==cmSUB) && + m_vRPN[sz-4].Cmd==cmVAR && + m_vRPN[sz-5].Cmd==cmVAL) + { + m_vRPN[sz-5].Val.data -= m_vRPN[sz-2].Val.data; + m_vRPN.pop_back(); + m_vRPN.pop_back(); + m_iStackPos++; + } + break; + + case cmMUL: + if (sz>=3 && m_vRPN[sz-2].Cmd==cmVAL && m_vRPN[sz-3].Cmd==cmVAL) + { + m_vRPN[sz-3].Val.data *= m_vRPN[sz-2].Val.data; + m_vRPN.pop_back(); + m_vRPN.pop_back(); + m_iStackPos++; + } + else if ( sz>=4 && + m_vRPN[sz-2].Cmd==cmVAL && + (m_vRPN[sz-3].Cmd==cmMUL || m_vRPN[sz-3].Cmd==cmDIV) && + m_vRPN[sz-4].Cmd==cmVAR && + m_vRPN[sz-5].Cmd==cmVAL) + { + m_vRPN[sz-5].Val.data *= m_vRPN[sz-2].Val.data; + m_vRPN.pop_back(); + m_vRPN.pop_back(); + m_iStackPos++; + } + break; + + case cmDIV: + if (sz>=3 && m_vRPN[sz-2].Cmd==cmVAL && m_vRPN[sz-3].Cmd==cmVAL) + { + m_vRPN[sz-3].Val.data /= m_vRPN[sz-2].Val.data; + m_vRPN.pop_back(); + m_vRPN.pop_back(); + m_iStackPos++; + } + else if ( sz>=4 && + m_vRPN[sz-2].Cmd==cmVAL && + (m_vRPN[sz-3].Cmd==cmMUL || m_vRPN[sz-3].Cmd==cmDIV) && + m_vRPN[sz-4].Cmd==cmVAR && + m_vRPN[sz-5].Cmd==cmVAL) + { + m_vRPN[sz-5].Val.data /= m_vRPN[sz-2].Val.data; + m_vRPN.pop_back(); + m_vRPN.pop_back(); + m_iStackPos++; + } + break; + } +*/ + } + + //--------------------------------------------------------------------------- + void ParserByteCode::AddIfElse(ECmdCode a_Oprt) + { + SToken tok; + tok.Cmd = a_Oprt; + m_vRPN.push_back(tok); + } + + //--------------------------------------------------------------------------- + /** \brief Add an assignement operator + + Operator entries in byte code consist of: +
    +
  • cmASSIGN code
  • +
  • the pointer of the destination variable
  • +
+ + \sa ParserToken::ECmdCode + */ + void ParserByteCode::AddAssignOp(value_type *a_pVar) + { + --m_iStackPos; + + SToken tok; + tok.Cmd = cmASSIGN; + tok.Val.ptr = a_pVar; + m_vRPN.push_back(tok); + } + + //--------------------------------------------------------------------------- + /** \brief Add function to bytecode. + + \param a_iArgc Number of arguments, negative numbers indicate multiarg functions. + \param a_pFun Pointer to function callback. + */ + void ParserByteCode::AddFun(void *a_pFun, int a_iArgc) + { + if (a_iArgc>=0) + { + m_iStackPos = m_iStackPos - a_iArgc + 1; + } + else + { + m_iStackPos = m_iStackPos + a_iArgc + 1; + } + m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); + + SToken tok; + tok.Cmd = cmFUNC; + tok.Fun.argc = a_iArgc; + tok.Fun.ptr = a_pFun; + m_vRPN.push_back(tok); + } + + //--------------------------------------------------------------------------- + /** \brief Add a bulk function to bytecode. + + \param a_iArgc Number of arguments, negative numbers indicate multiarg functions. + \param a_pFun Pointer to function callback. + */ + void ParserByteCode::AddBulkFun(void *a_pFun, int a_iArgc) + { + m_iStackPos = m_iStackPos - a_iArgc + 1; + m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); + + SToken tok; + tok.Cmd = cmFUNC_BULK; + tok.Fun.argc = a_iArgc; + tok.Fun.ptr = a_pFun; + m_vRPN.push_back(tok); + } + + //--------------------------------------------------------------------------- + /** \brief Add Strung function entry to the parser bytecode. + \throw nothrow + + A string function entry consists of the stack position of the return value, + followed by a cmSTRFUNC code, the function pointer and an index into the + string buffer maintained by the parser. + */ + void ParserByteCode::AddStrFun(void *a_pFun, int a_iArgc, int a_iIdx) + { + m_iStackPos = m_iStackPos - a_iArgc + 1; + + SToken tok; + tok.Cmd = cmFUNC_STR; + tok.Fun.argc = a_iArgc; + tok.Fun.idx = a_iIdx; + tok.Fun.ptr = a_pFun; + m_vRPN.push_back(tok); + + m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); + } + + //--------------------------------------------------------------------------- + /** \brief Add end marker to bytecode. + + \throw nothrow + */ + void ParserByteCode::Finalize() + { + SToken tok; + tok.Cmd = cmEND; + m_vRPN.push_back(tok); + rpn_type(m_vRPN).swap(m_vRPN); // shrink bytecode vector to fit + + // Determine the if-then-else jump offsets + ParserStack stIf, stElse; + int idx; + for (int i=0; i<(int)m_vRPN.size(); ++i) + { + switch(m_vRPN[i].Cmd) + { + case cmIF: + stIf.push(i); + break; + + case cmELSE: + stElse.push(i); + idx = stIf.pop(); + m_vRPN[idx].Oprt.offset = i - idx; + break; + + case cmENDIF: + idx = stElse.pop(); + m_vRPN[idx].Oprt.offset = i - idx; + break; + } + } + } + + //--------------------------------------------------------------------------- + const SToken* ParserByteCode::GetBase() const + { + if (m_vRPN.size()==0) + throw ParserError(ecINTERNAL_ERROR); + else + return &m_vRPN[0]; + } + + //--------------------------------------------------------------------------- + std::size_t ParserByteCode::GetMaxStackSize() const + { + return m_iMaxStackSize+1; + } + + //--------------------------------------------------------------------------- + /** \brief Delete the bytecode. + + \throw nothrow + + The name of this function is a violation of my own coding guidelines + but this way it's more in line with the STL functions thus more + intuitive. + */ + void ParserByteCode::clear() + { + m_vRPN.clear(); + m_iStackPos = 0; + m_iMaxStackSize = 0; + } + + //--------------------------------------------------------------------------- + /** \brief Remove a value number of entries from the bytecode. + + \attention Currently I don't test if the entries are really value entries. + */ + void ParserByteCode::RemoveValEntries(unsigned a_iNumber) + { + assert(m_iStackPos >= a_iNumber); + m_vRPN.resize(m_vRPN.size()-a_iNumber); + m_iStackPos -= (a_iNumber); + } + + //--------------------------------------------------------------------------- + /** \brief Dump bytecode (for debugging only!). */ + void ParserByteCode::AsciiDump() + { + if (!m_vRPN.size()) + { + mu::console() << _T("No bytecode available\n"); + return; + } + + mu::console() << _T("Number of RPN tokens:") << (int)m_vRPN.size() << _T("\n"); + for (std::size_t i=0; i \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "muParserCallback.h" + +/** \file + \brief Implementation of the parser callback class. +*/ + + +namespace mu +{ + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(fun_type0 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(0) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec, ECmdCode a_iCode) + :m_pFun((void*)a_pFun) + ,m_iArgc(1) + ,m_iPri(a_iPrec) + ,m_eOprtAsct(oaNONE) + ,m_iCode(a_iCode) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + + //--------------------------------------------------------------------------- + /** \brief Constructor for constructing funcstion callbacks taking two arguments. + \throw nothrow + */ + ParserCallback::ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(2) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + /** \brief Constructor for constructing binary operator callbacks. + \param a_pFun Pointer to a static function taking two arguments + \param a_bAllowOpti A flag indicating this funcation can be optimized + \param a_iPrec The operator precedence + \param a_eOprtAsct The operators associativity + \throw nothrow + */ + ParserCallback::ParserCallback(fun_type2 a_pFun, + bool a_bAllowOpti, + int a_iPrec, + EOprtAssociativity a_eOprtAsct) + :m_pFun((void*)a_pFun) + ,m_iArgc(2) + ,m_iPri(a_iPrec) + ,m_eOprtAsct(a_eOprtAsct) + ,m_iCode(cmOPRT_BIN) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(fun_type3 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(3) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(fun_type4 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(4) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(fun_type5 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(5) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(fun_type6 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(6) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(fun_type7 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(7) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(fun_type8 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(8) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(fun_type9 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(9) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(fun_type10 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(10) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(bulkfun_type0 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(0) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_BULK) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(bulkfun_type1 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(1) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_BULK) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + + //--------------------------------------------------------------------------- + /** \brief Constructor for constructing funcstion callbacks taking two arguments. + \throw nothrow + */ + ParserCallback::ParserCallback(bulkfun_type2 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(2) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_BULK) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(bulkfun_type3 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(3) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_BULK) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(bulkfun_type4 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(4) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_BULK) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(bulkfun_type5 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(5) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_BULK) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(bulkfun_type6 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(6) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_BULK) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(bulkfun_type7 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(7) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_BULK) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(bulkfun_type8 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(8) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_BULK) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(bulkfun_type9 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(9) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_BULK) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(bulkfun_type10 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(10) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_BULK) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(multfun_type a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(-1) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(strfun_type1 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(0) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_STR) + ,m_iType(tpSTR) + ,m_bAllowOpti(a_bAllowOpti) + {} + + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(1) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_STR) + ,m_iType(tpSTR) + ,m_bAllowOpti(a_bAllowOpti) + {} + + + //--------------------------------------------------------------------------- + ParserCallback::ParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(2) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmFUNC_STR) + ,m_iType(tpSTR) + ,m_bAllowOpti(a_bAllowOpti) + {} + + + //--------------------------------------------------------------------------- + /** \brief Default constructor. + \throw nothrow + */ + ParserCallback::ParserCallback() + :m_pFun(0) + ,m_iArgc(0) + ,m_iPri(-1) + ,m_eOprtAsct(oaNONE) + ,m_iCode(cmUNKNOWN) + ,m_iType(tpVOID) + ,m_bAllowOpti(0) + {} + + + //--------------------------------------------------------------------------- + /** \brief Copy constructor. + \throw nothrow + */ + ParserCallback::ParserCallback(const ParserCallback &ref) + { + m_pFun = ref.m_pFun; + m_iArgc = ref.m_iArgc; + m_bAllowOpti = ref.m_bAllowOpti; + m_iCode = ref.m_iCode; + m_iType = ref.m_iType; + m_iPri = ref.m_iPri; + m_eOprtAsct = ref.m_eOprtAsct; + } + + //--------------------------------------------------------------------------- + /** \brief Clone this instance and return a pointer to the new instance. */ + ParserCallback* ParserCallback::Clone() const + { + return new ParserCallback(*this); + } + + //--------------------------------------------------------------------------- + /** \brief Return tru if the function is conservative. + + Conservative functions return always the same result for the same argument. + \throw nothrow + */ + bool ParserCallback::IsOptimizable() const + { + return m_bAllowOpti; + } + + //--------------------------------------------------------------------------- + /** \brief Get the callback address for the parser function. + + The type of the address is void. It needs to be recasted according to the + argument number to the right type. + + \throw nothrow + \return #pFun + */ + void* ParserCallback::GetAddr() const + { + return m_pFun; + } + + //--------------------------------------------------------------------------- + /** \brief Return the callback code. */ + ECmdCode ParserCallback::GetCode() const + { + return m_iCode; + } + + //--------------------------------------------------------------------------- + ETypeCode ParserCallback::GetType() const + { + return m_iType; + } + + + //--------------------------------------------------------------------------- + /** \brief Return the operator precedence. + \throw nothrown + + Only valid if the callback token is an operator token (binary or infix). + */ + int ParserCallback::GetPri() const + { + return m_iPri; + } + + //--------------------------------------------------------------------------- + /** \brief Return the operators associativity. + \throw nothrown + + Only valid if the callback token is a binary operator token. + */ + EOprtAssociativity ParserCallback::GetAssociativity() const + { + return m_eOprtAsct; + } + + //--------------------------------------------------------------------------- + /** \brief Returns the number of function Arguments. */ + int ParserCallback::GetArgc() const + { + return m_iArgc; + } +} // namespace mu diff --git a/libraries/amf/amftools-code/src/muparser/muParserError.cpp b/libraries/amf/amftools-code/src/muparser/muParserError.cpp new file mode 100644 index 00000000..b879edda --- /dev/null +++ b/libraries/amf/amftools-code/src/muparser/muParserError.cpp @@ -0,0 +1,328 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include "muParserError.h" + + +namespace mu +{ + const ParserErrorMsg ParserErrorMsg::m_Instance; + + //------------------------------------------------------------------------------ + const ParserErrorMsg& ParserErrorMsg::Instance() + { + return m_Instance; + } + + //------------------------------------------------------------------------------ + string_type ParserErrorMsg::operator[](unsigned a_iIdx) const + { + return (a_iIdx \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2011 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include +#include +#include +#include +#include +#include + +#include "muParserTokenReader.h" +#include "muParserBase.h" + +/** \file + \brief This file contains the parser token reader implementation. +*/ + + +namespace mu +{ + + // Forward declaration + class ParserBase; + + //--------------------------------------------------------------------------- + /** \brief Copy constructor. + + \sa Assign + \throw nothrow + */ + ParserTokenReader::ParserTokenReader(const ParserTokenReader &a_Reader) + { + Assign(a_Reader); + } + + //--------------------------------------------------------------------------- + /** \brief Assignement operator. + + Self assignement will be suppressed otherwise #Assign is called. + + \param a_Reader Object to copy to this token reader. + \throw nothrow + */ + ParserTokenReader& ParserTokenReader::operator=(const ParserTokenReader &a_Reader) + { + if (&a_Reader!=this) + Assign(a_Reader); + + return *this; + } + + //--------------------------------------------------------------------------- + /** \brief Assign state of a token reader to this token reader. + + \param a_Reader Object from which the state should be copied. + \throw nothrow + */ + void ParserTokenReader::Assign(const ParserTokenReader &a_Reader) + { + m_pParser = a_Reader.m_pParser; + m_strFormula = a_Reader.m_strFormula; + m_iPos = a_Reader.m_iPos; + m_iSynFlags = a_Reader.m_iSynFlags; + + m_UsedVar = a_Reader.m_UsedVar; + m_pFunDef = a_Reader.m_pFunDef; + m_pConstDef = a_Reader.m_pConstDef; + m_pVarDef = a_Reader.m_pVarDef; + m_pStrVarDef = a_Reader.m_pStrVarDef; + m_pPostOprtDef = a_Reader.m_pPostOprtDef; + m_pInfixOprtDef = a_Reader.m_pInfixOprtDef; + m_pOprtDef = a_Reader.m_pOprtDef; + m_bIgnoreUndefVar = a_Reader.m_bIgnoreUndefVar; + m_vIdentFun = a_Reader.m_vIdentFun; + m_pFactory = a_Reader.m_pFactory; + m_pFactoryData = a_Reader.m_pFactoryData; + m_iBrackets = a_Reader.m_iBrackets; + m_cArgSep = a_Reader.m_cArgSep; + } + + //--------------------------------------------------------------------------- + /** \brief Constructor. + + Create a Token reader and bind it to a parser object. + + \pre [assert] a_pParser may not be NULL + \post #m_pParser==a_pParser + \param a_pParent Parent parser object of the token reader. + */ + ParserTokenReader::ParserTokenReader(ParserBase *a_pParent) + :m_pParser(a_pParent) + ,m_strFormula() + ,m_iPos(0) + ,m_iSynFlags(0) + ,m_bIgnoreUndefVar(false) + ,m_pFunDef(NULL) + ,m_pPostOprtDef(NULL) + ,m_pInfixOprtDef(NULL) + ,m_pOprtDef(NULL) + ,m_pConstDef(NULL) + ,m_pStrVarDef(NULL) + ,m_pVarDef(NULL) + ,m_pFactory(NULL) + ,m_pFactoryData(NULL) + ,m_vIdentFun() + ,m_UsedVar() + ,m_fZero(0) + ,m_iBrackets(0) + ,m_lastTok() + ,m_cArgSep(',') + { + assert(m_pParser); + SetParent(m_pParser); + } + + //--------------------------------------------------------------------------- + /** \brief Create instance of a ParserTokenReader identical with this + and return its pointer. + + This is a factory method the calling function must take care of the object destruction. + + \return A new ParserTokenReader object. + \throw nothrow + */ + ParserTokenReader* ParserTokenReader::Clone(ParserBase *a_pParent) const + { + std::auto_ptr ptr(new ParserTokenReader(*this)); + ptr->SetParent(a_pParent); + return ptr.release(); + } + + //--------------------------------------------------------------------------- + ParserTokenReader::token_type& ParserTokenReader::SaveBeforeReturn(const token_type &tok) + { + m_lastTok = tok; + return m_lastTok; + } + + //--------------------------------------------------------------------------- + void ParserTokenReader::AddValIdent(identfun_type a_pCallback) + { + m_vIdentFun.push_back(a_pCallback); + } + + //--------------------------------------------------------------------------- + void ParserTokenReader::SetVarCreator(facfun_type a_pFactory, void *pUserData) + { + m_pFactory = a_pFactory; + m_pFactoryData = pUserData; + } + + //--------------------------------------------------------------------------- + /** \brief Return the current position of the token reader in the formula string. + + \return #m_iPos + \throw nothrow + */ + int ParserTokenReader::GetPos() const + { + return m_iPos; + } + + //--------------------------------------------------------------------------- + /** \brief Return a reference to the formula. + + \return #m_strFormula + \throw nothrow + */ + const string_type& ParserTokenReader::GetExpr() const + { + return m_strFormula; + } + + //--------------------------------------------------------------------------- + /** \brief Return a map containing the used variables only. */ + varmap_type& ParserTokenReader::GetUsedVar() + { + return m_UsedVar; + } + + //--------------------------------------------------------------------------- + /** \brief Initialize the token Reader. + + Sets the formula position index to zero and set Syntax flags to default for initial formula parsing. + \pre [assert] triggered if a_szFormula==0 + */ + void ParserTokenReader::SetFormula(const string_type &a_strFormula) + { + m_strFormula = a_strFormula; + ReInit(); + } + + //--------------------------------------------------------------------------- + /** \brief Set Flag that contronls behaviour in case of undefined variables beeing found. + + If true, the parser does not throw an exception if an undefined variable is found. + otherwise it does. This variable is used internally only! + It supresses a "undefined variable" exception in GetUsedVar(). + Those function should return a complete list of variables including + those the are not defined by the time of it's call. + */ + void ParserTokenReader::IgnoreUndefVar(bool bIgnore) + { + m_bIgnoreUndefVar = bIgnore; + } + + //--------------------------------------------------------------------------- + /** \brief Reset the token reader to the start of the formula. + + The syntax flags will be reset to a value appropriate for the + start of a formula. + \post #m_iPos==0, #m_iSynFlags = noOPT | noBC | noPOSTOP | noSTR + \throw nothrow + \sa ESynCodes + */ + void ParserTokenReader::ReInit() + { + m_iPos = 0; + m_iSynFlags = sfSTART_OF_LINE; + m_iBrackets = 0; + m_UsedVar.clear(); + m_lastTok = token_type(); + } + + //--------------------------------------------------------------------------- + /** \brief Read the next token from the string. */ + ParserTokenReader::token_type ParserTokenReader::ReadNextToken() + { + assert(m_pParser); + + std::stack FunArgs; + const char_type *szFormula = m_strFormula.c_str(); + token_type tok; + + // Ignore all non printable characters when reading the expression + while (szFormula[m_iPos]>0 && szFormula[m_iPos]<=0x20) + ++m_iPos; + + if ( IsEOF(tok) ) return SaveBeforeReturn(tok); // Check for end of formula + if ( IsOprt(tok) ) return SaveBeforeReturn(tok); // Check for user defined binary operator + if ( IsFunTok(tok) ) return SaveBeforeReturn(tok); // Check for function token + if ( IsBuiltIn(tok) ) return SaveBeforeReturn(tok); // Check built in operators / tokens + if ( IsArgSep(tok) ) return SaveBeforeReturn(tok); // Check for function argument separators + if ( IsValTok(tok) ) return SaveBeforeReturn(tok); // Check for values / constant tokens + if ( IsVarTok(tok) ) return SaveBeforeReturn(tok); // Check for variable tokens + if ( IsStrVarTok(tok) ) return SaveBeforeReturn(tok); // Check for string variables + if ( IsString(tok) ) return SaveBeforeReturn(tok); // Check for String tokens + if ( IsInfixOpTok(tok) ) return SaveBeforeReturn(tok); // Check for unary operators + if ( IsPostOpTok(tok) ) return SaveBeforeReturn(tok); // Check for unary operators + + // Check String for undefined variable token. Done only if a + // flag is set indicating to ignore undefined variables. + // This is a way to conditionally avoid an error if + // undefined variables occur. + // (The GetUsedVar function must suppress the error for + // undefined variables in order to collect all variable + // names including the undefined ones.) + if ( (m_bIgnoreUndefVar || m_pFactory) && IsUndefVarTok(tok) ) + return SaveBeforeReturn(tok); + + // Check for unknown token + // + // !!! From this point on there is no exit without an exception possible... + // + string_type strTok; + int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); + if (iEnd!=m_iPos) + Error(ecUNASSIGNABLE_TOKEN, m_iPos, strTok); + + Error(ecUNASSIGNABLE_TOKEN, m_iPos, m_strFormula.substr(m_iPos)); + return token_type(); // never reached + } + + //--------------------------------------------------------------------------- + void ParserTokenReader::SetParent(ParserBase *a_pParent) + { + m_pParser = a_pParent; + m_pFunDef = &a_pParent->m_FunDef; + m_pOprtDef = &a_pParent->m_OprtDef; + m_pInfixOprtDef = &a_pParent->m_InfixOprtDef; + m_pPostOprtDef = &a_pParent->m_PostOprtDef; + m_pVarDef = &a_pParent->m_VarDef; + m_pStrVarDef = &a_pParent->m_StrVarDef; + m_pConstDef = &a_pParent->m_ConstDef; + } + + //--------------------------------------------------------------------------- + /** \brief Extract all characters that belong to a certain charset. + + \param a_szCharSet [in] Const char array of the characters allowed in the token. + \param a_strTok [out] The string that consists entirely of characters listed in a_szCharSet. + \param a_iPos [in] Position in the string from where to start reading. + \return The Position of the first character not listed in a_szCharSet. + \throw nothrow + */ + int ParserTokenReader::ExtractToken(const char_type *a_szCharSet, + string_type &a_sTok, + int a_iPos) const + { + int iEnd = (int)m_strFormula.find_first_not_of(a_szCharSet, a_iPos); + + if (iEnd==(int)string_type::npos) + iEnd = (int)m_strFormula.length(); + + // Assign token string if there was something found + if (a_iPos!=iEnd) + a_sTok = string_type( m_strFormula.begin()+a_iPos, m_strFormula.begin()+iEnd); + + return iEnd; + } + + //--------------------------------------------------------------------------- + /** \brief Check Expression for the presence of a binary operator token. + + Userdefined binary operator "++" gives inconsistent parsing result for + the equations "a++b" and "a ++ b" if alphabetic characters are allowed + in operator tokens. To avoid this this function checks specifically + for operator tokens. + */ + int ParserTokenReader::ExtractOperatorToken(string_type &a_sTok, + int a_iPos) const + { + int iEnd = (int)m_strFormula.find_first_not_of(m_pParser->ValidInfixOprtChars(), a_iPos); + if (iEnd==(int)string_type::npos) + iEnd = (int)m_strFormula.length(); + + // Assign token string if there was something found + if (a_iPos!=iEnd) + { + a_sTok = string_type( m_strFormula.begin() + a_iPos, m_strFormula.begin() + iEnd); + return iEnd; + } + else + { + // There is still the chance of having to deal with an operator consisting exclusively + // of alphabetic characters. + return ExtractToken(MUP_CHARS, a_sTok, a_iPos); + } + } + + //--------------------------------------------------------------------------- + /** \brief Check if a built in operator or other token can be found + \param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token. + \return true if an operator token has been found. + */ + bool ParserTokenReader::IsBuiltIn(token_type &a_Tok) + { + const char_type **const pOprtDef = m_pParser->GetOprtDef(), + *const szFormula = m_strFormula.c_str(); + + // Compare token with function and operator strings + // check string for operator/function + for (int i=0; pOprtDef[i]; i++) + { + std::size_t len( std::char_traits::length(pOprtDef[i]) ); + if ( string_type(pOprtDef[i]) == string_type(szFormula + m_iPos, szFormula + m_iPos + len) ) + { + switch(i) + { + //case cmAND: + //case cmOR: + //case cmXOR: + case cmLAND: + case cmLOR: + case cmLT: + case cmGT: + case cmLE: + case cmGE: + case cmNEQ: + case cmEQ: + case cmADD: + case cmSUB: + case cmMUL: + case cmDIV: + case cmPOW: + case cmASSIGN: + //if (len!=sTok.length()) + // continue; + + // The assignement operator need special treatment + if (i==cmASSIGN && m_iSynFlags & noASSIGN) + Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]); + + if (!m_pParser->HasBuiltInOprt()) continue; + if (m_iSynFlags & noOPT) + { + // Maybe its an infix operator not an operator + // Both operator types can share characters in + // their identifiers + if ( IsInfixOpTok(a_Tok) ) + return true; + + Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]); + } + + m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE; + m_iSynFlags |= ( (i != cmEND) && ( i != cmBC) ) ? noEND : 0; + break; + + case cmBO: + if (m_iSynFlags & noBO) + Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); + + if (m_lastTok.GetCode()==cmFUNC) + m_iSynFlags = noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE; + else + m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN| noIF | noELSE; + + ++m_iBrackets; + break; + + case cmBC: + if (m_iSynFlags & noBC) + Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); + + m_iSynFlags = noBO | noVAR | noVAL | noFUN | noINFIXOP | noSTR | noASSIGN; + + if (--m_iBrackets<0) + Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); + break; + + case cmELSE: + if (m_iSynFlags & noELSE) + Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]); + + m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE; + break; + + case cmIF: + if (m_iSynFlags & noIF) + Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]); + + m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE; + break; + + default: // The operator is listed in c_DefaultOprt, but not here. This is a bad thing... + Error(ecINTERNAL_ERROR); + } // switch operator id + + m_iPos += (int)len; + a_Tok.Set( (ECmdCode)i, pOprtDef[i] ); + return true; + } // if operator string found + } // end of for all operator strings + + return false; + } + + //--------------------------------------------------------------------------- + bool ParserTokenReader::IsArgSep(token_type &a_Tok) + { + const char_type* szFormula = m_strFormula.c_str(); + + if (szFormula[m_iPos]==m_cArgSep) + { + // copy the separator into null terminated string + char_type szSep[2]; + szSep[0] = m_cArgSep; + szSep[1] = 0; + + if (m_iSynFlags & noARG_SEP) + Error(ecUNEXPECTED_ARG_SEP, m_iPos, szSep); + + m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN; + m_iPos++; + a_Tok.Set(cmARG_SEP, szSep); + return true; + } + + return false; + } + + //--------------------------------------------------------------------------- + /** \brief Check for End of Formula. + + \return true if an end of formula is found false otherwise. + \param a_Tok [out] If an eof is found the corresponding token will be stored there. + \throw nothrow + \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsString, IsInfixOpTok, IsPostOpTok + */ + bool ParserTokenReader::IsEOF(token_type &a_Tok) + { + const char_type* szFormula = m_strFormula.c_str(); + + // check for EOF + if ( !szFormula[m_iPos] /*|| szFormula[m_iPos] == '\n'*/) + { + if ( m_iSynFlags & noEND ) + Error(ecUNEXPECTED_EOF, m_iPos); + + if (m_iBrackets>0) + Error(ecMISSING_PARENS, m_iPos, _T(")")); + + m_iSynFlags = 0; + a_Tok.Set(cmEND); + return true; + } + + return false; + } + + //--------------------------------------------------------------------------- + /** \brief Check if a string position contains a unary infix operator. + \return true if a function token has been found false otherwise. + */ + bool ParserTokenReader::IsInfixOpTok(token_type &a_Tok) + { + string_type sTok; + int iEnd = ExtractToken(m_pParser->ValidInfixOprtChars(), sTok, m_iPos); + if (iEnd==m_iPos) + return false; + + funmap_type::const_iterator item = m_pInfixOprtDef->find(sTok); + if (item==m_pInfixOprtDef->end()) + return false; + + a_Tok.Set(item->second, sTok); + m_iPos = (int)iEnd; + + if (m_iSynFlags & noINFIXOP) + Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); + + m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN; + return true; + } + + //--------------------------------------------------------------------------- + /** \brief Check whether the token at a given position is a function token. + \param a_Tok [out] If a value token is found it will be placed here. + \throw ParserException if Syntaxflags do not allow a function at a_iPos + \return true if a function token has been found false otherwise. + \pre [assert] m_pParser!=0 + */ + bool ParserTokenReader::IsFunTok(token_type &a_Tok) + { + string_type strTok; + int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); + if (iEnd==m_iPos) + return false; + + funmap_type::const_iterator item = m_pFunDef->find(strTok); + if (item==m_pFunDef->end()) + return false; + + // Check if the next sign is an opening bracket + const char_type *szFormula = m_strFormula.c_str(); + if (szFormula[iEnd]!='(') + return false; + + a_Tok.Set(item->second, strTok); + + m_iPos = (int)iEnd; + if (m_iSynFlags & noFUN) + Error(ecUNEXPECTED_FUN, m_iPos-(int)a_Tok.GetAsString().length(), a_Tok.GetAsString()); + + m_iSynFlags = noANY ^ noBO; + return true; + } + + //--------------------------------------------------------------------------- + /** \brief Check if a string position contains a binary operator. + \param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token. + \return true if an operator token has been found. + */ + bool ParserTokenReader::IsOprt(token_type &a_Tok) + { + const char_type *const szExpr = m_strFormula.c_str(); + string_type strTok; + + int iEnd = ExtractOperatorToken(strTok, m_iPos); + if (iEnd==m_iPos) + return false; + + // Check if the operator is a built in operator, if so ignore it here + const char_type **const pOprtDef = m_pParser->GetOprtDef(); + for (int i=0; m_pParser->HasBuiltInOprt() && pOprtDef[i]; ++i) + { + if (string_type(pOprtDef[i])==strTok) + return false; + } + + // Note: + // All tokens in oprt_bin_maptype are have been sorted by their length + // Long operators must come first! Otherwise short names (like: "add") that + // are part of long token names (like: "add123") will be found instead + // of the long ones. + // Length sorting is done with ascending length so we use a reverse iterator here. + funmap_type::const_reverse_iterator it = m_pOprtDef->rbegin(); + for ( ; it!=m_pOprtDef->rend(); ++it) + { + const string_type &sID = it->first; + if ( sID == string_type(szExpr + m_iPos, szExpr + m_iPos + sID.length()) ) + { + a_Tok.Set(it->second, strTok); + + // operator was found + if (m_iSynFlags & noOPT) + { + // An operator was found but is not expected to occur at + // this position of the formula, maybe it is an infix + // operator, not a binary operator. Both operator types + // can share characters in their identifiers. + if ( IsInfixOpTok(a_Tok) ) + return true; + else + { + // nope, no infix operator + return false; + //Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); + } + + } + + m_iPos += (int)sID.length(); + m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noEND | noBC | noASSIGN; + return true; + } + } + + return false; + } + + //--------------------------------------------------------------------------- + /** \brief Check if a string position contains a unary post value operator. */ + bool ParserTokenReader::IsPostOpTok(token_type &a_Tok) + { + // Do not check for postfix operators if they are not allowed at + // the current expression index. + // + // This will fix the bug reported here: + // + // http://sourceforge.net/tracker/index.php?func=detail&aid=3343891&group_id=137191&atid=737979 + // + if (m_iSynFlags & noPOSTOP) + return false; + // + + // Tricky problem with equations like "3m+5": + // m is a postfix operator, + is a valid sign for postfix operators and + // for binary operators parser detects "m+" as operator string and + // finds no matching postfix operator. + // + // This is a special case so this routine slightly differs from the other + // token readers. + + // Test if there could be a postfix operator + string_type sTok; + int iEnd = ExtractToken(m_pParser->ValidOprtChars(), sTok, m_iPos); + if (iEnd==m_iPos) + return false; + + // iteraterate over all postfix operator strings + funmap_type::const_reverse_iterator it = m_pPostOprtDef->rbegin(); + for ( ; it!=m_pPostOprtDef->rend(); ++it) + { + if (sTok.find(it->first)!=0) + continue; + + a_Tok.Set(it->second, sTok); + m_iPos += (int)it->first.length(); + + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noSTR | noASSIGN; + return true; + } + + return false; + } + + //--------------------------------------------------------------------------- + /** \brief Check whether the token at a given position is a value token. + + Value tokens are either values or constants. + + \param a_Tok [out] If a value token is found it will be placed here. + \return true if a value token has been found. + */ + bool ParserTokenReader::IsValTok(token_type &a_Tok) + { + assert(m_pConstDef); + assert(m_pParser); + + #if defined(_MSC_VER) + #pragma warning( disable : 4244 ) + #endif + + string_type strTok; + value_type fVal(0); + int iEnd(0); + + // 2.) Check for user defined constant + // Read everything that could be a constant name + iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); + if (iEnd!=m_iPos) + { + valmap_type::const_iterator item = m_pConstDef->find(strTok); + if (item!=m_pConstDef->end()) + { + m_iPos = iEnd; + a_Tok.SetVal(item->second, strTok); + + if (m_iSynFlags & noVAL) + Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok); + + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; + return true; + } + } + + // 3.call the value recognition functions provided by the user + // Call user defined value recognition functions + std::vector::const_iterator item = m_vIdentFun.begin(); + for (item = m_vIdentFun.begin(); item!=m_vIdentFun.end(); ++item) + { + int iStart = m_iPos; + if ( (*item)(m_strFormula.c_str() + m_iPos, &m_iPos, &fVal)==1 ) + { + strTok.assign(m_strFormula.c_str(), iStart, m_iPos); + if (m_iSynFlags & noVAL) + Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok); + + a_Tok.SetVal(fVal, strTok); + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; + return true; + } + } + + return false; + + #if defined(_MSC_VER) + #pragma warning( default : 4244 ) + #endif + } + + //--------------------------------------------------------------------------- + /** \brief Check wheter a token at a given position is a variable token. + \param a_Tok [out] If a variable token has been found it will be placed here. + \return true if a variable token has been found. + */ + bool ParserTokenReader::IsVarTok(token_type &a_Tok) + { + if (!m_pVarDef->size()) + return false; + + string_type strTok; + int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); + if (iEnd==m_iPos) + return false; + + varmap_type::const_iterator item = m_pVarDef->find(strTok); + if (item==m_pVarDef->end()) + return false; + + if (m_iSynFlags & noVAR) + Error(ecUNEXPECTED_VAR, m_iPos, strTok); + + m_pParser->OnDetectVar(&m_strFormula, m_iPos, iEnd); + + m_iPos = iEnd; + a_Tok.SetVar(item->second, strTok); + m_UsedVar[item->first] = item->second; // Add variable to used-var-list + + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR; + +// Zur Info hier die SynFlags von IsVal(): +// m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; + return true; + } + + //--------------------------------------------------------------------------- + bool ParserTokenReader::IsStrVarTok(token_type &a_Tok) + { + if (!m_pStrVarDef || !m_pStrVarDef->size()) + return false; + + string_type strTok; + int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); + if (iEnd==m_iPos) + return false; + + strmap_type::const_iterator item = m_pStrVarDef->find(strTok); + if (item==m_pStrVarDef->end()) + return false; + + if (m_iSynFlags & noSTR) + Error(ecUNEXPECTED_VAR, m_iPos, strTok); + + m_iPos = iEnd; + if (!m_pParser->m_vStringVarBuf.size()) + Error(ecINTERNAL_ERROR); + + a_Tok.SetString(m_pParser->m_vStringVarBuf[item->second], m_pParser->m_vStringVarBuf.size() ); + + m_iSynFlags = noANY ^ ( noBC | noOPT | noEND | noARG_SEP); + return true; + } + + + //--------------------------------------------------------------------------- + /** \brief Check wheter a token at a given position is an undefined variable. + + \param a_Tok [out] If a variable tom_pParser->m_vStringBufken has been found it will be placed here. + \return true if a variable token has been found. + \throw nothrow + */ + bool ParserTokenReader::IsUndefVarTok(token_type &a_Tok) + { + string_type strTok; + int iEnd( ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos) ); + if ( iEnd==m_iPos ) + return false; + + if (m_iSynFlags & noVAR) + { + // 20061021 added token string strTok instead of a_Tok.GetAsString() as the + // token identifier. + // related bug report: + // http://sourceforge.net/tracker/index.php?func=detail&aid=1578779&group_id=137191&atid=737979 + Error(ecUNEXPECTED_VAR, m_iPos - (int)a_Tok.GetAsString().length(), strTok); + } + + // If a factory is available implicitely create new variables + if (m_pFactory) + { + value_type *fVar = m_pFactory(strTok.c_str(), m_pFactoryData); + a_Tok.SetVar(fVar, strTok ); + + // Do not use m_pParser->DefineVar( strTok, fVar ); + // in order to define the new variable, it will clear the + // m_UsedVar array which will kill previousely defined variables + // from the list + // This is safe because the new variable can never override an existing one + // because they are checked first! + (*m_pVarDef)[strTok] = fVar; + m_UsedVar[strTok] = fVar; // Add variable to used-var-list + } + else + { + a_Tok.SetVar((value_type*)&m_fZero, strTok); + m_UsedVar[strTok] = 0; // Add variable to used-var-list + } + + m_iPos = iEnd; + + // Call the variable factory in order to let it define a new parser variable + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noINFIXOP | noSTR; + return true; + } + + + //--------------------------------------------------------------------------- + /** \brief Check wheter a token at a given position is a string. + \param a_Tok [out] If a variable token has been found it will be placed here. + \return true if a string token has been found. + \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsEOF, IsInfixOpTok, IsPostOpTok + \throw nothrow + */ + bool ParserTokenReader::IsString(token_type &a_Tok) + { + if (m_strFormula[m_iPos]!='"') + return false; + + string_type strBuf(&m_strFormula[m_iPos+1]); + std::size_t iEnd(0), iSkip(0); + + // parser over escaped '\"' end replace them with '"' + for(iEnd=(int)strBuf.find( _T("\"") ); iEnd!=0 && iEnd!=string_type::npos; iEnd=(int)strBuf.find( _T("\""), iEnd)) + { + if (strBuf[iEnd-1]!='\\') break; + strBuf.replace(iEnd-1, 2, _T("\"") ); + iSkip++; + } + + if (iEnd==string_type::npos) + Error(ecUNTERMINATED_STRING, m_iPos, _T("\"") ); + + string_type strTok(strBuf.begin(), strBuf.begin()+iEnd); + + if (m_iSynFlags & noSTR) + Error(ecUNEXPECTED_STR, m_iPos, strTok); + + m_pParser->m_vStringBuf.push_back(strTok); // Store string in internal buffer + a_Tok.SetString(strTok, m_pParser->m_vStringBuf.size()); + + m_iPos += (int)strTok.length() + 2 + (int)iSkip; // +2 wg Anfhrungszeichen; +iSkip fr entfernte escape zeichen + m_iSynFlags = noANY ^ ( noARG_SEP | noBC | noOPT | noEND ); + + return true; + } + + //--------------------------------------------------------------------------- + /** \brief Create an error containing the parse error position. + + This function will create an Parser Exception object containing the error text and its position. + + \param a_iErrc [in] The error code of type #EErrorCodes. + \param a_iPos [in] The position where the error was detected. + \param a_strTok [in] The token string representation associated with the error. + \throw ParserException always throws thats the only purpose of this function. + */ + void ParserTokenReader::Error( EErrorCodes a_iErrc, + int a_iPos, + const string_type &a_sTok) const + { + m_pParser->Error(a_iErrc, a_iPos, a_sTok); + } + + //--------------------------------------------------------------------------- + void ParserTokenReader::SetArgSep(char_type cArgSep) + { + m_cArgSep = cArgSep; + } + + //--------------------------------------------------------------------------- + char_type ParserTokenReader::GetArgSep() const + { + return m_cArgSep; + } +} // namespace mu + diff --git a/libraries/amf/amftools-code/src/nAmf.cpp b/libraries/amf/amftools-code/src/nAmf.cpp new file mode 100644 index 00000000..d7ec34ae --- /dev/null +++ b/libraries/amf/amftools-code/src/nAmf.cpp @@ -0,0 +1,504 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nAmf.h" +#include "XmlStream.h" + +#define THISAMFVERSION 1.1 //the version of the amf standard implemented in this code. + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +nAmf::nAmf(void) +{ + Clear(); +} + +nAmf::~nAmf(void) +{ + +} + +nAmf& nAmf::operator=(const nAmf& In) +{ + UnitsExist = In.UnitsExist; + aUnit = In.aUnit; + VersionExists = In.VersionExists; + aVersion = In.aVersion; + Objects = In.Objects; + Metadata = In.Metadata; + Constellations = In.Constellations; + Textures = In.Textures; + Materials = In.Materials; + + //MUST update all the internal pointer to AMF root (this) within the different classes + for (std::vector::iterator it = Objects.begin(); it != Objects.end(); it++) it->pnAmf = this; + for (std::vector::iterator it = Constellations.begin(); it != Constellations.end(); it++) it->pnAmf = this; + for (std::vector::iterator it = Materials.begin(); it != Materials.end(); it++) it->pnAmf = this; + for (std::vector::iterator it = Textures.begin(); it != Textures.end(); it++) it->pnAmf = this; + + + return *this; +} + + +void nAmf::Clear(void) +{ + UnitsExist = false; + aUnit = UNIT_MM; //default + VersionExists = true; + aVersion = THISAMFVERSION; + + Objects.clear(); + Metadata.clear(); + Constellations.clear(); + Textures.clear(); + + Materials.clear(); + Materials.push_back(nMaterial(this)); + Materials.back().aID = 0; + Materials.back().Metadata.push_back(nMetadata(MD_NAME, "[Void]")); //this is the only way to set the name of the protected null material + Materials.back().SetConstColor(0.0, 0.0, 0.0, 0.0); //transparent! + +// AppendObject(); //always need at least one object to be valid AMF + + +} + +bool nAmf::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage, bool* pCancelFlag) +{ + if (pCancelFlag && *pCancelFlag) return false; + + pXML->OpenElement("amf"); + if (UnitsExist){ + switch (aUnit){ + case UNIT_MM: pXML->SetElAttS("unit", "millimeter"); break; + case UNIT_M: pXML->SetElAttS("unit", "meter"); break; + case UNIT_IN: pXML->SetElAttS("unit", "inch"); break; + case UNIT_FT: pXML->SetElAttS("unit", "feet"); break; + case UNIT_UM: pXML->SetElAttS("unit", "micron"); break; + default: break; + } + } + + if (VersionExists) pXML->SetElAttD("version", aVersion); + + for(std::vector::iterator it = Metadata.begin(); it != Metadata.end(); it++){ + if (!it->WriteXML(pXML, pMessage)) return false; + } + + for(std::vector::iterator it = Objects.begin(); it != Objects.end(); it++){ + if (it->aID == -1) it->aID = GetUnusedGeoID(); //make extra sure we don't write a bad object ID! + if (!it->WriteXML(pXML, pMessage, pCancelFlag)) return false; + } + + for(std::vector::iterator it = Constellations.begin(); it != Constellations.end(); it++){ + if (!it->WriteXML(pXML, pMessage)) return false; + } + + for(std::vector::iterator it = Textures.begin(); it != Textures.end(); it++){ + if (!it->WriteXML(pXML, pMessage)) return false; + if (pCancelFlag && *pCancelFlag) return false; + } + + if (Materials.size()>1){ //There will always be a reserved "void" material at index 0 of this array that should not be written to the amf file + for(std::vector::iterator it = Materials.begin()+1; it != Materials.end(); it++){ + if (!it->WriteXML(pXML, pMessage)) return false; + if (pCancelFlag && *pCancelFlag) return false; + } + } + + pXML->CloseElement(); + return true; +} + + +bool nAmf::ReadXML(CXmlStreamRead* pXML, bool StrictLoad, std::string* pMessage, bool* pCancelFlag) +{ + if (pCancelFlag && *pCancelFlag) return false; + + Clear(); + + + std::string tmpUnits; + if (pXML->GetElAttS("unit", &tmpUnits)){ + UnitsExist = true; + if (tmpUnits == "millimeter") aUnit = UNIT_MM; + else if (tmpUnits == "meter") aUnit = UNIT_M; + else if (tmpUnits == "inch") aUnit = UNIT_IN; + else if (tmpUnits == "feet") aUnit = UNIT_FT; + else if (tmpUnits == "micron") aUnit = UNIT_UM; + } + + if (pXML->GetElAttD("version", &aVersion)) VersionExists = true; //todo: check against version numbers like 1.2.1 + + //read as many metadata tags as there are... + nMetadata tmpMeta; + while(pXML->OpenElement("metadata", true)){ // + if (!tmpMeta.ReadXML(pXML, this, StrictLoad, pMessage)) return false; + Metadata.push_back(tmpMeta); + } + + //read as many object tags as there are... + nObject tmpObj(this); + while(pXML->OpenElement("object", true)){ // + if (!tmpObj.ReadXML(pXML, this, StrictLoad, pMessage, pCancelFlag)) return false; + Objects.push_back(tmpObj); + if (pCancelFlag && *pCancelFlag) return false; + + } + + //read as many constellation tags as there are... + nConstellation tmpCon(this); + while(pXML->OpenElement("constellation", true)){ // + if (!tmpCon.ReadXML(pXML, this, StrictLoad, pMessage)) return false; + Constellations.push_back(tmpCon); + if (pCancelFlag && *pCancelFlag) return false; + + } + + //read as many texture tags as there are... + nTexture tmpTex(this); + while(pXML->OpenElement("texture", true)){ // + if (!tmpTex.ReadXML(pXML, this, StrictLoad, pMessage)) return false; + Textures.push_back(tmpTex); + if (pCancelFlag && *pCancelFlag) return false; + + } + + //read as many material tags as there are... + nMaterial tmpMat(this); + while(pXML->OpenElement("material", true)){ // + if (!tmpMat.ReadXML(pXML, this, StrictLoad, pMessage)) return false; + Materials.push_back(tmpMat); + if (pCancelFlag && *pCancelFlag) return false; + + } + + return CheckValid(!StrictLoad, pMessage); +} + +bool nAmf::CheckValid(bool FixNode, std::string* pMessage) +{ + //Check if units exist + if (!UnitsExist){ + if (FixNode) {UnitsExist = true; aUnit = UNIT_MM;} + if (pMessage){ + *pMessage += "Warning: No physical units specified. "; + if (FixNode) *pMessage += "Defaulting to mm.\n"; else *pMessage += "\n"; + } + } + + //Check if version info exists + if (!VersionExists){ + if (FixNode) {VersionExists = true; aVersion = THISAMFVERSION;} + if (pMessage){ + *pMessage += "Warning: No Amf version specified. "; + if (FixNode) *pMessage += "Parser will attempt to harmonize with current AMF version.\n"; else *pMessage += "\n"; + } + } + + //Check for valid version + if (VersionExists && aVersion != THISAMFVERSION){ + if (FixNode) {aVersion = THISAMFVERSION;} + if (pMessage){ + *pMessage += "Warning: Outdated or invalid Amf version designation. "; + if (FixNode) *pMessage += "Parser will attempt to harmonize with current AMF version.\n"; else *pMessage += "\n"; + } + } + + //Check if file contains geometry (nothing to fix. Just report this warning + if (Objects.size() == 0){ + if (FixNode) {AppendObject("Default");} + if (pMessage){ + *pMessage += "Warning: No objects found in amf. "; + if (FixNode) *pMessage += "Adding a default object.\n"; else *pMessage += "\n"; + } + } + + return true; +} + + + +//UTILITIES + + +int nAmf::GetUsedGeoID(void){ + //return first object in Objects + if (Objects.size() != 0) return Objects.front().aID; + else if (Constellations.size() != 0) return Constellations.front().aID; + else return -1; +} + + +int nAmf::GetUnusedGeoID(void) +{ + int CandidateID = 0; + bool CandidateInUse = true; + while (CandidateInUse){ + CandidateID++; + CandidateInUse = false; + //check object vector + for (std::vector::iterator it = Objects.begin(); it != Objects.end(); it++){ + if (it->aID == CandidateID){ + CandidateInUse = true; + break; + } + } + + //check constellation vector + if (!CandidateInUse){ //if we didn't find one in the object vector + for (std::vector::iterator it = Constellations.begin(); it != Constellations.end(); it++){ + if (it->aID == CandidateID){ + CandidateInUse = true; + break; + } + } + } + } + return CandidateID; +} + + +int nAmf::GetUnusedTexID(void){ + int CandidateID = 0; + bool CandidateInUse = true; + while (CandidateInUse){ + CandidateID++; + CandidateInUse = false; + //cycle through texture vector + for (std::vector::iterator it = Textures.begin(); it != Textures.end(); it++){ + if (it->aID == CandidateID){ + CandidateInUse = true; + break; + } + } + + } + return CandidateID; +} + +int nAmf::GetUnusedMatID(void){ + int CandidateID = 0; + bool CandidateInUse = true; + while (CandidateInUse){ + CandidateID++; + CandidateInUse = false; + //cycle through texture vector + for (std::vector::iterator it = Materials.begin(); it != Materials.end(); it++){ + if (it->aID == CandidateID){ + CandidateInUse = true; + break; + } + } + + } + return CandidateID; +} + +bool nAmf::IsDuplicateGeoID(int IdToCheck) +{ + int NumFound = 0; + for (std::vector::iterator it = Objects.begin(); it!=Objects.end(); it++){if(it->aID==IdToCheck) NumFound++;} + for (std::vector::iterator it = Constellations.begin(); it!=Constellations.end(); it++){if(it->aID==IdToCheck) NumFound++;} + + if (NumFound > 1) return true; + else return false; +} + +bool nAmf::IsDuplicateTexID(int IdToCheck) +{ + int NumFound = 0; + for (std::vector::iterator it = Textures.begin(); it!=Textures.end(); it++){if(it->aID==IdToCheck) NumFound++;} + + if (NumFound > 1) return true; + else return false; +} + +bool nAmf::IsDuplicateMatID(int IdToCheck) +{ + int NumFound = 0; + for (std::vector::iterator it = Materials.begin(); it!=Materials.end(); it++){if(it->aID==IdToCheck) NumFound++;} + + if (NumFound > 1) return true; + else return false; +} + +std::string nAmf::GetGeoNameFromID(int GeometryID) //finds the name of the object or constellation with this internal ID +{ + //Look in the objects + nObject* pObj = GetObjectByID(GeometryID); + if (pObj) return "Obj: " + pObj->GetName(); + + //Look in the constellations + nConstellation* pConst = GetConstellationByID(GeometryID); + if (pConst) return "Const: " + pConst->GetName(); + + //Didn't find this id... + return "Invalid ID"; +} + + +std::string nAmf::GetMatNameFromID(int MaterialID) //returns name string for a material if it exists +{ + nMaterial* tmpMat = GetMaterialByID(MaterialID); + if (tmpMat) return tmpMat->GetName(); + else if (MaterialID == 0) return "Void"; + else return "Invalid Material ID"; +} + +nObject* nAmf::GetObjectByID(int GeometryID) //returns pointer to a constellation that has this ID, or NULL if non exists. +{ + for(std::vector::iterator it = Objects.begin(); it != Objects.end(); it++){ + if (it->aID == GeometryID) return &(*it); + } + return NULL; +} + +nConstellation* nAmf::GetConstellationByID(int GeometryID) //returns pointer to a constellation that has this ID, or NULL if non exists. +{ + for(std::vector::iterator it = Constellations.begin(); it != Constellations.end(); it++){ + if (it->aID == GeometryID) return &(*it); + } + return NULL; +} + +nMaterial* nAmf::GetMaterialByID(int MaterialID) //returns a TEMPORARY pointer to the first material with the specified ID, or NULL if not found +{ + for (std::vector::iterator it = Materials.begin(); it != Materials.end(); it++){ + if(it->aID == MaterialID) return &(*it); + } + return NULL; //no valid material found with this ID +} + +nTexture* nAmf::GetTextureByID(int TextureID) //returns a TEMPORARY pointer to the first texture with the specified ID, or NULL if not found +{ + for (std::vector::iterator it = Textures.begin(); it != Textures.end(); it++){ + if(it->aID == TextureID) return &(*it); + } + return NULL; //no valid material found with this ID +} + + + + +int nAmf::AppendObject(std::string NameIn){ + Objects.push_back(nObject(this)); +// int MyID = GetUnusedGeoID(); +// Objects.back().aID = MyID; + if (NameIn == "") NameIn = "Default"; + Objects.back().SetName(NameIn); + return Objects.back().aID; +} + + + + +int nAmf::AppendConstellation(std::string NameIn) +{ + Constellations.push_back(nConstellation(this)); +// int MyID = GetUnusedGeoID(); +// Constellations.back().aID = MyID; + if (NameIn == "") NameIn = "Default"; + Constellations.back().SetName(NameIn); + return Constellations.back().aID; +} + +int nAmf::AppendMaterial(std::string NameIn) +{ + Materials.push_back(nMaterial(this)); +// int MyID = GetUnusedMatID(); +// Materials.back().aID = MyID; + if (NameIn == "") NameIn = "Default"; + Materials.back().SetName(NameIn); + return Materials.back().aID; +} + + +void nAmf::DeleteGeometry(int GeometryID) //Deletes by the internal object ID, not index in the vector!! +{ + //remove all constellation instances to avoid having invalid ID's! + for(std::vector::iterator it = Constellations.begin(); it != Constellations.end(); it++){ + for (std::vector::iterator jt = it->Instances.begin(); jt != it->Instances.end()-1;){ //reverse iteration so erasing elements doesn't change index... + if (jt->aObjectID == GeometryID){ + it->Instances.erase(jt); //erase me!! + //todo: Make sure +// if (it->Instances.size() == 0){break; break;} +// else if (jt != it->Instances.begin()) jt--; //because everything has shifted up by one... + } + else jt++; + } + } + + //find the object and remove it: in Object list + for(std::vector::iterator it = Objects.begin(); it != Objects.end(); it++){ + if (it->aID == GeometryID){ + Objects.erase(it); + break; //second break?? + } + } + + //or delete constellation in constellation list... + for(std::vector::iterator it = Constellations.begin(); it != Constellations.end(); it++){ + if (it->aID == GeometryID){ + Constellations.erase(it); + break; //second break?? + } + } +} + +void nAmf::DeleteMaterial(int MaterialID) +{ + if (MaterialID == 0) return; //can't delete the reserved void material + + //find the material and remove it: in Material list + for(std::vector::iterator it = Materials.begin(); it != Materials.end(); it++){ + if (it->aID == MaterialID){ + Materials.erase(it, it+1); + break; //second break?? + } + } +} + + + + + + + +bool nAmf::IsTopLevelGeo(int GeometryID) +{ + bool IsInstanced = false; + for (std::vector::iterator it=Constellations.begin(); it != Constellations.end(); it++){ + for (std::vector::iterator jt = it->Instances.begin(); jt != it->Instances.end(); jt++){ + if (jt->aObjectID == GeometryID){ + IsInstanced = true; + break; + } + } + } + return !IsInstanced; +} + + + + + +//std::string nAmf::GetMatName(nVolume* pVolume) //returns name string for material of this volume if it has one. +//{ +// nMaterial* tmpMat = GetMaterialByID(pVolume->aMaterialID); +// if (tmpMat) return tmpMat->GetName(); +// else return "No Material"; +//} diff --git a/libraries/amf/amftools-code/src/nColor.cpp b/libraries/amf/amftools-code/src/nColor.cpp new file mode 100644 index 00000000..ac75ccb9 --- /dev/null +++ b/libraries/amf/amftools-code/src/nColor.cpp @@ -0,0 +1,93 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nColor.h" +#include "XmlStream.h" + +#include "nAmf.h" + +nColor::nColor() +{ + Clear(); +} + + +nColor::~nColor(void) +{ + +} + +nColor& nColor::operator=(const nColor& In) +{ + R = In.R; + G = In.G; + B = In.B; + + AExists = In.AExists; + A = In.A; + + + return *this; +} + +void nColor::Clear(void) +{ + R.Clear(); + G.Clear(); + B.Clear(); + + AExists = false; + A.Clear(); + +} + +bool nColor::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage) +{ + pXML->OpenElement("color"); + + if (R.IsConst()) pXML->SetElementS("r", R.ToAmfString()); + else pXML->SetElementS("r", R.ToAmfString(), true); + + if (G.IsConst()) pXML->SetElementS("g", G.ToAmfString()); + else pXML->SetElementS("g", G.ToAmfString(), true); + + if (B.IsConst()) pXML->SetElementS("b", B.ToAmfString()); + else pXML->SetElementS("b", B.ToAmfString(), true); + + if (AExists){ + if (A.IsConst()) pXML->SetElementS("a", A.ToAmfString()); + else pXML->SetElementS("a", A.ToAmfString(), true); + } + + pXML->CloseElement(); + return true; +} + +bool nColor::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage) +{ + Clear(); + std::string tmp; + if (pXML->GetElementS("r", &tmp)) R.FromAmfString(tmp, pAmf); //CData should already be stripped off.... + if (pXML->GetElementS("g", &tmp)) G.FromAmfString(tmp, pAmf); + if (pXML->GetElementS("b", &tmp)) B.FromAmfString(tmp, pAmf); + if (pXML->GetElementS("a", &tmp)){ + AExists = true; + A.FromAmfString(tmp, pAmf); + } + //do in material composite, too! + return CheckValid(pAmf, !StrictLoad, pMessage); + + +} + +bool nColor::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + //TODO: + return true; +} diff --git a/libraries/amf/amftools-code/src/nComposite.cpp b/libraries/amf/amftools-code/src/nComposite.cpp new file mode 100644 index 00000000..59058869 --- /dev/null +++ b/libraries/amf/amftools-code/src/nComposite.cpp @@ -0,0 +1,87 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nComposite.h" +#include "XmlStream.h" + +#include "nAmf.h" + + +nComposite::nComposite(void) +{ + Clear(); +} + + +nComposite::~nComposite(void) +{ +} + +nComposite& nComposite::operator=(const nComposite& In) +{ + aMaterialID = In.aMaterialID; + MatEquation = In.MatEquation; + return *this; +} + +void nComposite::Clear(void) +{ + aMaterialID = -1; + MatEquation.Clear(); +} + + +bool nComposite::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage) +{ + pXML->OpenElement("composite"); + pXML->SetElAttI("materialid", aMaterialID); + pXML->SetElDataS(MatEquation.ToAmfString(), true); + pXML->CloseElement(); + + return true; +} + + +bool nComposite::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage) +{ + Clear(); + + pXML->GetElAttI("materialid", &aMaterialID); + + std::string tmp; + if (pXML->GetElDataS(&tmp)) MatEquation.FromAmfString(tmp, pAmf); + else {//loads equation if it exists (todo: move to check...) + *pMessage += "Invalid equation in composite tag. Aborting. \n"; + return false; + } + + return CheckValid(pAmf, !StrictLoad, pMessage); +} + +bool nComposite::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + if (!CheckEquation(pMessage)){ + if (FixNode){ MatEquation.Clear(); if(pMessage) *pMessage += "Warning: Error found in equation. Removing Equation.\n";} + else {if(pMessage) *pMessage += "Error: Error found in equation.\n"; return false;} + } + + return true; +} + +bool nComposite::SetEquation(std::string AmfEquationIn, nAmf* pAmf, std::string* pMessage) +{ + MatEquation.FromAmfString(AmfEquationIn, pAmf); + return CheckEquation(pMessage); +} + +bool nComposite::ScaleEquation(double ScaleFactor, std::string* pMessage) //Scale factor is the factor we're changing the Amf (2.0 = doubling the object size) +{ + MatEquation.Scale(ScaleFactor); + return CheckEquation(pMessage); +} diff --git a/libraries/amf/amftools-code/src/nConstellation.cpp b/libraries/amf/amftools-code/src/nConstellation.cpp new file mode 100644 index 00000000..3ece455b --- /dev/null +++ b/libraries/amf/amftools-code/src/nConstellation.cpp @@ -0,0 +1,182 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nConstellation.h" +#include "XmlStream.h" +#include "nAmf.h" + +nConstellation::nConstellation(nAmf* pnAmfIn) +{ + pnAmf = pnAmfIn; + Clear(); +} + + +nConstellation::~nConstellation(void) +{ +} + +nConstellation& nConstellation::operator=(const nConstellation& In) +{ + aID = In.aID; + Instances = In.Instances; + Metadata = In.Metadata; + + pnAmf = In.pnAmf; + + return *this; +} + + +void nConstellation::Clear(void) +{ + aID = pnAmf->GetUnusedGeoID(); + Instances.clear(); + Metadata.clear(); + +} + +bool nConstellation::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage) +{ + pXML->OpenElement("constellation"); + pXML->SetElAttI("id", aID); + + for(std::vector::iterator it = Metadata.begin(); it != Metadata.end(); it++){ + if (!it->WriteXML(pXML, pMessage)) return false; + } + + for(std::vector::iterator it = Instances.begin(); it != Instances.end(); it++){ + if (!it->WriteXML(pXML, pMessage)) return false; + } + + pXML->CloseElement(); + return true; +} + + +bool nConstellation::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage) +{ + Clear(); + + if (!pXML->GetElAttI("id", &aID)) aID = -1; + + //read as many metadata tags as there are... + nMetadata tmpMeta; + while(pXML->OpenElement("metadata", true)){ // + if (!tmpMeta.ReadXML(pXML, pAmf, StrictLoad, pMessage)) return false; + Metadata.push_back(tmpMeta); + } +// if (Metadata.size() != 0) pXML->UpLevel(); // + + //read as many instance tags as there are... + nInstance tmpInst; + while(pXML->OpenElement("instance", true)){ // + if (!tmpInst.ReadXML(pXML, pAmf, StrictLoad, pMessage)) return false; + Instances.push_back(tmpInst); + } +// if (Instances.size() != 0) pXML->UpLevel(); // + + + return CheckValid(pAmf, !StrictLoad, pMessage); +} + +bool nConstellation::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + + + //Check for invalid geometry ID + if (aID < 0 || pAmf->IsDuplicateGeoID(aID)){ + if (FixNode) { + aID = pAmf->GetUnusedGeoID(); + if (pMessage) *pMessage += "Warning: Invalid or duplicate geometry ID found. Setting to an unused ID. Constellations may need to be adjusted.\n"; + } + else { + if (pMessage) *pMessage += "Error: Invalid or duplicate geometry ID found.\n"; + return false; + } + } + + //check for no instances (just a warning...) + if (Instances.size() == 0 && pMessage) {*pMessage += "Constellation found with no instances.\n"; } + + //Check for invalid geometry IDs in instances (here instead of nInstance so we can delete effonding ones to fix Amf) + std::vector InsIndToDelete; + int count=0; + for (std::vector::iterator it = Instances.begin(); it!=Instances.end(); it++){ + if (it->aObjectID < 0 || (pAmf->GetObjectByID(it->aObjectID) == NULL && pAmf->GetConstellationByID(it->aObjectID) == NULL)){ + if (FixNode) { + InsIndToDelete.push_back(count); + if (pMessage) *pMessage += "Warning: Invalid geometry ID in instance. Deleting instance.\n"; + } + else { + if (pMessage) *pMessage += "Error: Invalid geometry ID in instance.\n"; + return false; + } + } + count++; + } + for (int i=InsIndToDelete.size()-1; i >= 0; i--) Instances.erase(Instances.begin()+InsIndToDelete[i]); //go backwards so out indices stay correct... + + + return true; +} + +void nConstellation::SetName(std::string NewName) +{ + for (std::vector::iterator it = Metadata.begin(); it != Metadata.end(); it++){ + if(it->Type == MD_NAME){ + it->Data = NewName; //replace the name!! + return; + } + } + + //if we get here there was no existing Name metadata so add one... + Metadata.push_back(nMetadata(MD_NAME, NewName)); +} + +std::string nConstellation::GetName(void) +{ + for (std::vector::iterator it = Metadata.begin(); it != Metadata.end(); it++){ + if(it->Type == MD_NAME) return it->Data; + } + return ""; +} + +bool nConstellation::IsReferencedBy(nConstellation* pConstellationCheck) //returns true if pUsesCheck references pCheck anywhere in its tree. +{ + if (aID == pConstellationCheck->aID) return true; + + nConstellation* pTmpConst; + std::vector OpenList; //list of constellations referenced by pUsesCheck + OpenList.reserve(1000); //allcoate maximum reasonable number of materials because using iterators with a push-back inside loop can cause re-allocation and therefore bad iterator. + OpenList.push_back(pConstellationCheck); + + for(std::vector::iterator jt = OpenList.begin(); jt != OpenList.end(); jt++){ + for(std::vector::iterator it = (*jt)->Instances.begin(); it != (*jt)->Instances.end(); it++){ +// for (int i=0; i<(*jt)->Instances.size(); i++){ + pTmpConst = pnAmf->GetConstellationByID(it->aObjectID); + if (pTmpConst){ //if this id is a constellation + //check to see if it's already on the open list. If not, add it. + bool inList = false; + for (int i=0; i<(int)OpenList.size(); i++){ + if (OpenList[i] == pTmpConst){ + inList = true; + break; + } + } + if (!inList){ + OpenList.push_back(pTmpConst); + if (aID == pTmpConst->aID) return true; //we've found a recursion back to the one we're checking against! + } + } + } + + } + return false; +} diff --git a/libraries/amf/amftools-code/src/nCoordinates.cpp b/libraries/amf/amftools-code/src/nCoordinates.cpp new file mode 100644 index 00000000..248e6a0b --- /dev/null +++ b/libraries/amf/amftools-code/src/nCoordinates.cpp @@ -0,0 +1,77 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nCoordinates.h" +#include "XmlStream.h" + + + +nCoordinates::nCoordinates(void) +{ + Clear(); +} + + +nCoordinates::~nCoordinates(void) +{ +} +nCoordinates::nCoordinates(const double Xin, const double Yin, const double Zin) +{ + Clear(); + X = Xin; + Y = Yin; + Z = Zin; +} + +nCoordinates& nCoordinates::operator=(const nCoordinates& In) +{ + X = In.X; + Y = In.Y; + Z = In.Z; + + return *this; +} + + +void nCoordinates::Clear(void) +{ + X = 0; + Y = 0; + Z = 0; +} + + +bool nCoordinates::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage) +{ + pXML->OpenElement("coordinates"); + + pXML->SetElementD("x", X); + pXML->SetElementD("y", Y); + pXML->SetElementD("z", Z); + + pXML->CloseElement(); + return true; +} + + +bool nCoordinates::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage) +{ + Clear(); + + if (!pXML->GetElementD("x", &X)) {X = 0; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + if (!pXML->GetElementD("y", &Y)) {Y = 0; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + if (!pXML->GetElementD("z", &Z)) {Z = 0; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + + return CheckValid(pAmf, !StrictLoad, pMessage); +} + +bool nCoordinates::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + return true; +} diff --git a/libraries/amf/amftools-code/src/nEdge.cpp b/libraries/amf/amftools-code/src/nEdge.cpp new file mode 100644 index 00000000..7b0cb638 --- /dev/null +++ b/libraries/amf/amftools-code/src/nEdge.cpp @@ -0,0 +1,96 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nEdge.h" +#include "XmlStream.h" + + + +nEdge::nEdge(void) +{ + Clear(); +} + + +nEdge::~nEdge(void) +{ +} + +nEdge& nEdge::operator=(const nEdge& In) +{ + v1 = In.v1; + v2 = In.v2; + dx1 = In.dx1; + dy1 = In.dy1; + dz1 = In.dz1; + dx2 = In.dx2; + dy2 = In.dy2; + dz2 = In.dz2; + + return *this; +} + + +void nEdge::Clear(void) +{ + v1 = -1; + v2 = -1; + dx1 = 0.0; + dy1 = 0.0; + dz1 = 0.0; + dx2 = 0.0; + dy2 = 0.0; + dz2 = 0.0; +} + + +bool nEdge::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage) +{ + pXML->OpenElement("edge"); + + pXML->SetElementI("v1", v1); + pXML->SetElementD("dx1", dx1); + pXML->SetElementD("dy1", dy1); + pXML->SetElementD("dz1", dz1); + + pXML->SetElementI("v2", v2); + pXML->SetElementD("dx2", dx2); + pXML->SetElementD("dy2", dy2); + pXML->SetElementD("dz2", dz2); + + pXML->CloseElement(); + return true; +} + +bool nEdge::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage) +{ + Clear(); + + //required tags + if (!pXML->GetElementI("v1", &v1)) {v1 = -1; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + if (!pXML->GetElementD("dx1", &dx1)) {dx1 = 0.0; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + if (!pXML->GetElementD("dy1", &dy1)) {dy1 = 0.0; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + if (!pXML->GetElementD("dz1", &dz1)) {dz1 = 0.0; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + + if (!pXML->GetElementI("v2", &v2)) {v2 = -1; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + if (!pXML->GetElementD("dx2", &dx2)) {dx2 = 0.0; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + if (!pXML->GetElementD("dy2", &dy2)) {dy2 = 0.0; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + if (!pXML->GetElementD("dz2", &dz2)) {dz2 = 0.0; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + + return CheckValid(pAmf, !StrictLoad, pMessage); + +} + +bool nEdge::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + if (v1 == -1 || v2 == -1) return false; + + + else return true; +} diff --git a/libraries/amf/amftools-code/src/nInstance.cpp b/libraries/amf/amftools-code/src/nInstance.cpp new file mode 100644 index 00000000..8dcc2b5d --- /dev/null +++ b/libraries/amf/amftools-code/src/nInstance.cpp @@ -0,0 +1,89 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nInstance.h" +#include "XmlStream.h" + + + +nInstance::nInstance(void) +{ + Clear(); +} + + +nInstance::~nInstance(void) +{ +} + +nInstance& nInstance::operator=(const nInstance& In) +{ + aObjectID = In.aObjectID; + DeltaX = In.DeltaX; + DeltaY = In.DeltaY; + DeltaZ = In.DeltaZ; + rX = In.rX; + rY = In.rY; + rZ = In.rZ; + + return *this; +} + +void nInstance::Clear(void) +{ + aObjectID = -1; + + DeltaX = 0.0; + DeltaY = 0.0; + DeltaZ = 0.0; + rX = 0.0; + rY = 0.0; + rZ = 0.0; + +} + +bool nInstance::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage) +{ + pXML->OpenElement("instance"); + pXML->SetElAttI("objectid", aObjectID); + + pXML->SetElementD("deltax", DeltaX); + pXML->SetElementD("deltay", DeltaY); + pXML->SetElementD("deltaz", DeltaZ); + pXML->SetElementD("rx", rX); + pXML->SetElementD("ry", rY); + pXML->SetElementD("rz", rZ); + + pXML->CloseElement(); + return true; +} + +bool nInstance::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage) +{ + Clear(); + + if (!pXML->GetElAttI("objectid", &aObjectID)) aObjectID = -1; + + //required tags + if (!pXML->GetElementD("deltax", &DeltaX)) DeltaX = 0; + if (!pXML->GetElementD("deltay", &DeltaY)) DeltaY = 0; + if (!pXML->GetElementD("deltaz", &DeltaZ)) DeltaZ = 0; + if (!pXML->GetElementD("rx", &rX)) rX = 0; + if (!pXML->GetElementD("ry", &rY)) rY = 0; + if (!pXML->GetElementD("rz", &rZ)) rZ = 0; + + return CheckValid(pAmf, !StrictLoad, pMessage); + +} + +bool nInstance::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + //valid aObjectID checked in nConstellation so we can delete bad instances to fix the Amf file. + return true; +} diff --git a/libraries/amf/amftools-code/src/nMaterial.cpp b/libraries/amf/amftools-code/src/nMaterial.cpp new file mode 100644 index 00000000..eae0d6d9 --- /dev/null +++ b/libraries/amf/amftools-code/src/nMaterial.cpp @@ -0,0 +1,272 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nMaterial.h" +#include "XmlStream.h" + +#include "nAmf.h" + +nMaterial::nMaterial(nAmf* pnAmfIn) +{ + pnAmf = pnAmfIn; + Clear(); +} + + +nMaterial::~nMaterial(void) +{ +} + +nMaterial& nMaterial::operator=(const nMaterial& In) +{ + aID = In.aID; + Composites = In.Composites; + ColorExists = In.ColorExists; + Color = In.Color; + Metadata = In.Metadata; + + pnAmf = In.pnAmf; + + return *this; +} + +void nMaterial::Clear(void) +{ + aID = pnAmf->GetUnusedMatID(); + + Composites.clear(); + + ColorExists = true; + Color.Clear(); + Metadata.clear(); + +} + +bool nMaterial::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage) +{ + pXML->OpenElement("material"); + pXML->SetElAttI("id", aID); + + for(std::vector::iterator it = Metadata.begin(); it != Metadata.end(); it++){ + if (!it->WriteXML(pXML, pMessage)) return false; + } + + if (ColorExists) if (!Color.WriteXML(pXML, pMessage)) return false; + + for(std::vector::iterator it = Composites.begin(); it != Composites.end(); it++){ + if (!it->WriteXML(pXML, pMessage)) return false; + } + + pXML->CloseElement(); + return true; +} + + +bool nMaterial::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage) +{ + Clear(); + + if (!pXML->GetElAttI("id", &aID)) aID = -1; + + //read as many metadata tags as there are... + nMetadata tmpMeta; + while(pXML->OpenElement("metadata", true)){ // + if (!tmpMeta.ReadXML(pXML, pAmf, StrictLoad, pMessage)) return false; + Metadata.push_back(tmpMeta); + } +// if (Metadata.size() != 0) pXML->UpLevel(); // + + if (pXML->OpenElement("color")){Color.ReadXML(pXML, pAmf, StrictLoad, pMessage); ColorExists=true; pXML->CloseElement();} + + + //read as many composite tags as there are... + nComposite tmpComp; + while(pXML->OpenElement("composite", true)){ // + if (!tmpComp.ReadXML(pXML, pAmf, StrictLoad, pMessage)) return false; + Composites.push_back(tmpComp); + } +// if (Composites.size() != 0) pXML->UpLevel(); // + + + return CheckValid(pAmf, !StrictLoad, pMessage); +} + +bool nMaterial::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + //Check for invalid material ID + if (aID < 0 || pAmf->IsDuplicateMatID(aID)){ + if (FixNode) { + aID = pAmf->GetUnusedMatID(); + if (pMessage) *pMessage += "Warning: Invalid or duplicate material ID found. Setting to an unused ID. Composite equations may need to be adjusted.\n"; + } + else { + if (pMessage) *pMessage += "Error: Invalid or duplicate material ID found.\n"; + return false; + } + } + + //Check for invalid material IDs in composites (here instead of nComposite so we can delete effonding ones to fix Amf) + std::vector CompIndToDelete; + int count=0; + for (std::vector::iterator it = Composites.begin(); it!=Composites.end(); it++){ + if (it->aMaterialID < 0 || pAmf->GetMaterialByID(it->aMaterialID) == NULL){ + if (FixNode) { + CompIndToDelete.push_back(count); + if (pMessage) *pMessage += "Warning: Invalid material ID in composite. Deleting composite.\n"; + } + else { + if (pMessage) *pMessage += "Error: Invalid material ID in composite.\n"; + return false; + } + } + count++; + } + for (int i=CompIndToDelete.size()-1; i >= 0; i--) Composites.erase(Composites.begin()+CompIndToDelete[i]); //go backwards so out indices stay correct... + + return true; +} + +void nMaterial::SetName(std::string NewName) +{ + if (aID == 0) return; //protect the void material + for (std::vector::iterator it = Metadata.begin(); it != Metadata.end(); it++){ + if(it->Type == MD_NAME){ + it->Data = NewName; //replace the name!! + return; + } + } + + //if we get here there was no existing Name metadata so add one... + Metadata.push_back(nMetadata(MD_NAME, NewName)); +} + +std::string nMaterial::GetName(void) +{ + for (std::vector::iterator it = Metadata.begin(); it != Metadata.end(); it++){ + if(it->Type == MD_NAME) return it->Data; + } + return ""; +} + +void nMaterial::GetColorAt(double xIn, double yIn, double zIn, double* rOut, double* gOut, double* bOut, double* aOut) +{ + double tmpR, tmpG, tmpB, tmpA, tmpWeight; + if (Composites.size() == 0){ + if (ColorExists){Color.GetColor(xIn, yIn, zIn, rOut, gOut, bOut, aOut);} + else{*rOut=0; *gOut=0; *bOut=0; if (aOut) *aOut=1.0;} + } + else { //do weighted average + double SumR = 0; + double SumG = 0; + double SumB = 0; + double SumA = 0; + double SumWeight = 0; + for (std::vector::iterator it = Composites.begin(); it != Composites.end(); it++){ + tmpWeight = it->EvalEquation(xIn, yIn, zIn); + if (tmpWeight < 0) tmpWeight = 0; //as per spec... up for discussion? + pnAmf->GetMaterialByID(it->aMaterialID)->GetColorAt(xIn, yIn, zIn, &tmpR, &tmpG, &tmpB, &tmpA); + SumR += tmpR*tmpWeight; + SumG += tmpG*tmpWeight; + SumB += tmpB*tmpWeight; + SumA += tmpA*tmpWeight; + SumWeight += tmpWeight; + } + *rOut = SumR/SumWeight; + *gOut = SumG/SumWeight; + *bOut = SumB/SumWeight; + if (aOut) *aOut = SumA/SumWeight; + + //kep within range of 0 to 1 + if (*rOut > 1.0) *rOut = 1.0; + else if (*rOut < 0.0) *rOut = 0.0; + if (*gOut > 1.0) *gOut = 1.0; + else if (*gOut < 0.0) *gOut = 0.0; + if (*bOut > 1.0) *bOut = 1.0; + else if (*bOut < 0.0) *bOut = 0.0; + if (aOut){ + if (*aOut > 1.0) *aOut = 1.0; + else if (*aOut < 0.0) *aOut = 0.0; + } + } +} + +int nMaterial::AddCompositeInstance(int InstanceMaterialID, std::string AmfEquationIn, std::string* pMessage) //returns the index. Can't set a id here since we need top level access to make sure we can't create a recursive situation +{ + if (aID == 0) -1; //protect the void material + +// nMaterial* pCurMat = GetMaterialByID(MaterialID); + if (InstanceMaterialID != 0){ + nMaterial* pInstanceMat = pnAmf->GetMaterialByID(InstanceMaterialID); + if (!pInstanceMat){ + if (pMessage) *pMessage += "Invalid material ID.\n"; + return -1; //invalid material ID somewhere along the line + } + + if(IsReferencedBy(pInstanceMat)){ + if (pMessage) *pMessage += "Self reference (direct or recursive)\n"; + return -1; //no self references/recursive self references + } + } + + nComposite tmpComp; + tmpComp.aMaterialID = InstanceMaterialID; + if (!tmpComp.SetEquation(AmfEquationIn, pnAmf, pMessage)) return -1; + + Composites.push_back(tmpComp); + return Composites.size(); +} + +void nMaterial::DeleteCompositeInstance(int CompositeIndex) +{ + if (CompositeIndex < 0 || CompositeIndex >= (int)Composites.size()) return; + Composites.erase(Composites.begin()+CompositeIndex); +} + + +//void nMaterial::DeleteCompositeInstance(int CompositeIndex) +//{ +//// nMaterial* pCurMat = GetMaterialByID(MaterialID); +//// if (!pCurMat) return; //invalid material ID +// std::vector::iterator CompDelIt = pCurMat->Composites.begin()+CompositeIndex; +// pCurMat->Composites.erase(pCurMat->Composites.begin()+CompositeIndex); +// +//} + +bool nMaterial::IsReferencedBy(/*nMaterial* pCheck, */nMaterial* pMaterialCheck) +{ + if (this == pMaterialCheck) return true; + + nMaterial* pTmpMat; + std::vector OpenList; //list of constellations referenced by pUsesCheck + OpenList.reserve(1000); //allcoate maximum reasonable number of materials because using iterators with a push-back inside loop can cause re-allocation and therefore bad iterator. + OpenList.push_back(pMaterialCheck); + + for(std::vector::iterator jt = OpenList.begin(); jt < OpenList.end(); jt++){ + for(std::vector::iterator it = (*jt)->Composites.begin(); it != (*jt)->Composites.end(); it++){ + pTmpMat = pnAmf->GetMaterialByID(it->aMaterialID); + if (pTmpMat){ //if this id is a constellation + //check to see if it's already on the open list. If not, add it. + bool inList = false; + for (int i=0; i<(int)OpenList.size(); i++){ + if (OpenList[i] == pTmpMat){ + inList = true; + break; + } + } + if (!inList){ + OpenList.push_back(pTmpMat); + if (this == pTmpMat) return true; //we've found a recursion back to the one we're checking against! + + //OpenList may have re-allocated! + } + } + } + } + return false; +} diff --git a/libraries/amf/amftools-code/src/nMesh.cpp b/libraries/amf/amftools-code/src/nMesh.cpp new file mode 100644 index 00000000..00ef1eaa --- /dev/null +++ b/libraries/amf/amftools-code/src/nMesh.cpp @@ -0,0 +1,152 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nMesh.h" +#include "XmlStream.h" + +#include "nVolume.h" + +nMesh::nMesh(void) +{ + Clear(); +} + + +nMesh::~nMesh(void) +{ +} + +nMesh& nMesh::operator=(const nMesh& In) +{ + Vertices = In.Vertices; + Volumes = In.Volumes; + + return *this; +} + + +void nMesh::Clear(void) +{ + Vertices.Clear(); + Volumes.clear(); + +} + +bool nMesh::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage, bool* pCancelFlag) +{ + if (pCancelFlag && *pCancelFlag) return false; + + pXML->OpenElement("mesh"); + + //vertices... + if (!Vertices.WriteXML(pXML, pMessage, pCancelFlag)) return false; + + //volumes + for(std::vector::iterator it = Volumes.begin(); it != Volumes.end(); it++){ + if (!it->WriteXML(pXML, pMessage, pCancelFlag)) return false; + } + + pXML->CloseElement(); // + return true; +} + +bool nMesh::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage, bool* pCancelFlag) +{ + if (pCancelFlag && *pCancelFlag) return false; + Clear(); + + //load vertices... + if (pXML->OpenElement("vertices") && Vertices.ReadXML(pXML, pAmf, StrictLoad, pMessage, pCancelFlag)) pXML->CloseElement(); + else return false; + + //read as many volume tags as there are... + nVolume tmpVol; + while(pXML->OpenElement("volume", true)){ // + if (!tmpVol.ReadXML(pXML, pAmf, StrictLoad, pMessage, pCancelFlag)) return false; + Volumes.push_back(tmpVol); + } +// if (Volumes.size() != 0) pXML->UpLevel(); // + + +// pXML->UpLevel(); // + + + return CheckValid(pAmf, !StrictLoad, pMessage); + +} + +bool nMesh::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + //check if it contains any volumes + if (Volumes.size() == 0){ + if (pMessage) *pMessage += "Warning: No volumes in mesh tag.\n"; + } + + //check if volume references any vertices that don't exist + int NumVert = Vertices.VertexList.size(); + for (std::vector::iterator it = Volumes.begin(); it!=Volumes.end(); it++){ + std::vectorTriToDelete; + int count=0; + for (std::vector::iterator jt = it->Triangles.begin(); jt!=it->Triangles.end(); jt++){ + bool DeleteTri = false; + for (int i=0; i<3; i++){ + int ThisInd; + switch (i){ + case 0: ThisInd = jt->v1; break; + case 1: ThisInd = jt->v2; break; + case 2: ThisInd = jt->v3; break; + } + if (ThisInd >= NumVert){ + if (FixNode) { + DeleteTri = true; + if (pMessage) *pMessage += "Warning: Triangle references non-existent vertex. Deleting triangle.\n"; + } + else { + if (pMessage) *pMessage += "Error: Triangle references non-existent vertex.\n"; + return false; + } + } + } + if (DeleteTri) TriToDelete.push_back(count); + count++; + } + for (int i=TriToDelete.size()-1; i >= 0; i--){ //go backwards so out indices stay correct... + it->Triangles.erase(it->Triangles.begin()+TriToDelete[i]); + } + } + + + return true; +} + +//Utilities +bool nMesh::Bounds(double* MinX, double* MaxX, double* MinY, double* MaxY, double* MinZ, double* MaxZ) //returns false if no vertices +{ + if(Vertices.VertexList.size() == 0) return false; + + for (std::vector::iterator it = Vertices.VertexList.begin(); it!=Vertices.VertexList.end(); it++) { + if (it == Vertices.VertexList.begin()){ //first time thru + *MinX = it->GetX(); + *MaxX = it->GetX(); + *MinY = it->GetY(); + *MaxY = it->GetY(); + *MinZ = it->GetZ(); + *MaxZ = it->GetZ(); + } + else { + *MinX = std::min(*MinX, it->GetX()); + *MaxX = std::max(*MaxX, it->GetX()); + *MinY = std::min(*MinY, it->GetY()); + *MaxY = std::max(*MaxY, it->GetY()); + *MinZ = std::min(*MinZ, it->GetZ()); + *MaxZ = std::max(*MaxZ, it->GetZ()); + } + } + return true; +} diff --git a/libraries/amf/amftools-code/src/nMetadata.cpp b/libraries/amf/amftools-code/src/nMetadata.cpp new file mode 100644 index 00000000..13543959 --- /dev/null +++ b/libraries/amf/amftools-code/src/nMetadata.cpp @@ -0,0 +1,96 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nMetadata.h" +#include "XmlStream.h" + + + +nMetadata::nMetadata(void) +{ + Clear(); +} + + +nMetadata::~nMetadata(void) +{ +} + +void nMetadata::Clear(void) +{ + Type = MD_INVALID; + Data = ""; +} + +nMetadata& nMetadata::operator=(const nMetadata& In) +{ + Type = In.Type; + Data = In.Data; + + return *this; +} + + +bool nMetadata::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage) +{ + if (Data == ""){ + *pMessage += "Warning: MetaData tag found with no data. Ignoring."; + return true; //quick check to make sure we're not saving an empty element (crashes on load) + } + + pXML->OpenElement("metadata"); + + switch (Type){ + case MD_NAME: pXML->SetElAttS("type", "name"); break; + case MD_DESCRIPTION: pXML->SetElAttS("type", "description"); break; + case MD_URL: pXML->SetElAttS("type", "url"); break; + case MD_AUTHOR: pXML->SetElAttS("type", "author"); break; + case MD_COMPANY: pXML->SetElAttS("type", "company"); break; + case MD_CAD: pXML->SetElAttS("type", "cad"); break; + case MD_REVISION: pXML->SetElAttS("type", "revision"); break; + case MD_TOLERANCE: pXML->SetElAttS("type", "tolerance"); break; + case MD_VOLUME: pXML->SetElAttS("type", "volume"); break; + case MD_ELASTICMOD: pXML->SetElAttS("type", "elasticmodulus"); break; + case MD_POISSONRATIO: pXML->SetElAttS("type", "poissonratio"); break; + default: break; + } + + pXML->SetElDataS(Data); + pXML->CloseElement(); + return true; +} + + +bool nMetadata::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage) +{ + Clear(); + + std::string tmp; + pXML->GetElAttS("type", &tmp); + if (tmp == "name") Type = MD_NAME; + else if (tmp == "description") Type = MD_DESCRIPTION; + else if (tmp == "url") Type = MD_URL; + else if (tmp == "author") Type = MD_AUTHOR; + else if (tmp == "company") Type = MD_COMPANY; + else if (tmp == "cad") Type = MD_CAD; + else if (tmp == "revision") Type = MD_REVISION; + else if (tmp == "tolerance") Type = MD_TOLERANCE; + else if (tmp == "volume") Type = MD_VOLUME; + else if (tmp == "elasticmodulus") Type = MD_ELASTICMOD; + else if (tmp == "poissonratio") Type = MD_POISSONRATIO; + + pXML->GetElDataS(&Data); //loads name tag if it exists + + return CheckValid(pAmf, !StrictLoad, pMessage); +} + +bool nMetadata::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + return true; +} diff --git a/libraries/amf/amftools-code/src/nNormal.cpp b/libraries/amf/amftools-code/src/nNormal.cpp new file mode 100644 index 00000000..3b7cdf55 --- /dev/null +++ b/libraries/amf/amftools-code/src/nNormal.cpp @@ -0,0 +1,82 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nNormal.h" +#include "XmlStream.h" +#include + + +nNormal::nNormal(void) +{ + Clear(); +} + + +nNormal::~nNormal(void) +{ +} + +nNormal& nNormal::operator=(const nNormal& In) +{ + nX = In.nX; + nY = In.nY; + nZ = In.nZ; + + return *this; +} + + +void nNormal::Clear(void) +{ + nX = 0.0; + nY = 0.0; + nZ = 0.0; +} + + +bool nNormal::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage) +{ + pXML->OpenElement("normal"); + + pXML->SetElementD("nx", nX); + pXML->SetElementD("ny", nY); + pXML->SetElementD("nz", nZ); + + pXML->CloseElement(); + return true; +} + + +bool nNormal::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage) +{ + Clear(); + + if (!pXML->GetElementD("nx", &nX)) {nX = 0; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + if (!pXML->GetElementD("ny", &nY)) {nY = 0; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + if (!pXML->GetElementD("nz", &nZ)) {nZ = 0; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + + return CheckValid(pAmf, !StrictLoad, pMessage); +} + +bool nNormal::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + double Eps = 1e-10; //Threshold for how close to unit length is close enough + double Length2 = nX*nX+nY*nY+nZ*nZ; + if (Length2<1-Eps || Length2 > 1+Eps){ + if (pMessage) *pMessage += "Non unit-length normal.\n"; + if (FixNode){ + double Length = sqrt(Length2); + nX /= Length; + nY /= Length; + nZ /= Length; + } + else return false; + } + return true; +} diff --git a/libraries/amf/amftools-code/src/nObject.cpp b/libraries/amf/amftools-code/src/nObject.cpp new file mode 100644 index 00000000..fd6483cc --- /dev/null +++ b/libraries/amf/amftools-code/src/nObject.cpp @@ -0,0 +1,162 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nObject.h" +#include "XmlStream.h" + +#include "nMesh.h" +#include "nMetadata.h" +#include "nAmf.h" + +nObject::nObject(nAmf* pnAmfIn) +{ + pnAmf = pnAmfIn; + Clear(); +} + + +nObject::~nObject(void) +{ +} + +nObject& nObject::operator=(const nObject& In) +{ + Meshes = In.Meshes; + ColorExists = In.ColorExists; + Color = In.Color; + Metadata = In.Metadata; + aID = In.aID; + + pnAmf = In.pnAmf; + + + return *this; +} + + +void nObject::Clear(void) +{ + Meshes.clear(); + ColorExists = false; + Color.Clear(); + Metadata.clear(); + aID = pnAmf->GetUnusedGeoID(); + +} + + +bool nObject::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage, bool* pCancelFlag) +{ + if (pCancelFlag && *pCancelFlag) return false; + + pXML->OpenElement("object"); + + pXML->SetElAttI("id", aID); + + for(std::vector::iterator it = Metadata.begin(); it != Metadata.end(); it++){ + if (!it->WriteXML(pXML, pMessage)) return false; + } + + if (ColorExists) Color.WriteXML(pXML, pMessage); + + for(std::vector::iterator it = Meshes.begin(); it != Meshes.end(); it++){ + if (!it->WriteXML(pXML, pMessage, pCancelFlag)) return false; + } + + pXML->CloseElement(); + return true; +} + + +bool nObject::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage, bool* pCancelFlag) +{ + if (pCancelFlag && *pCancelFlag) return false; + Clear(); + + if (!pXML->GetElAttI("id", &aID)) aID = -1; + + //read as many metadata tags as there are... + nMetadata tmpMeta; + while(pXML->OpenElement("metadata", true)){ // + if (!tmpMeta.ReadXML(pXML, pAmf, StrictLoad, pMessage)) return false; + Metadata.push_back(tmpMeta); + } + + if (pXML->OpenElement("color")){Color.ReadXML(pXML, pAmf, StrictLoad, pMessage); ColorExists=true; pXML->CloseElement();} + + + //read as many triangle tags as there are... + nMesh tmpMesh; + while(pXML->OpenElement("mesh", true)){ // + if (!tmpMesh.ReadXML(pXML, pAmf, StrictLoad, pMessage, pCancelFlag)) return false; + Meshes.push_back(tmpMesh); + } + + + return CheckValid(pAmf, !StrictLoad, pMessage); +} + +bool nObject::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + //Check for invalid geometry ID + if (aID < 0 || pAmf->IsDuplicateGeoID(aID)){ + if (FixNode) { + aID = pAmf->GetUnusedGeoID(); + if (pMessage) *pMessage += "Warning: Invalid or duplicate geometry ID found. Setting to an unused ID. Constellations may need to be adjusted.\n"; + } + else { + if (pMessage) *pMessage += "Error: Invalid or duplicate geometry ID found.\n"; + return false; + } + } + + + + //check if it contains any meshes + if (Meshes.size() == 0){ + if (pMessage) *pMessage += "Warning: No meshes in object tag.\n"; + } + + return true; +} + +void nObject::SetName(std::string NewName) +{ + for (std::vector::iterator it = Metadata.begin(); it != Metadata.end(); it++){ + if(it->Type == MD_NAME){ + it->Data = NewName; //replace the name!! + return; + } + } + + //if we get here there was no existing Name metadata so add one... + Metadata.push_back(nMetadata(MD_NAME, NewName)); +} + +std::string nObject::GetName(void) +{ + for (std::vector::iterator it = Metadata.begin(); it != Metadata.end(); it++){ + if(it->Type == MD_NAME) return it->Data; + } + return ""; +} + +void nObject::Translate(double dx, double dy, double dz) +{ + for (std::vector::iterator it = Meshes.begin(); it != Meshes.end(); it++){ + it->Translate(dx, dy, dz); + } +} + +void nObject::Rotate(double rx, double ry, double rz) +{ + for (std::vector::iterator it = Meshes.begin(); it != Meshes.end(); it++){ + it->Rotate(rx, ry, rz); + } +} diff --git a/libraries/amf/amftools-code/src/nTexmap.cpp b/libraries/amf/amftools-code/src/nTexmap.cpp new file mode 100644 index 00000000..268687ca --- /dev/null +++ b/libraries/amf/amftools-code/src/nTexmap.cpp @@ -0,0 +1,158 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nTexmap.h" +#include "XmlStream.h" + + + +nTexmap::nTexmap(void) +{ + Clear(); +} + + +nTexmap::~nTexmap(void) +{ +} + +nTexmap& nTexmap::operator=(const nTexmap& In) +{ + RTexID = In.RTexID; + GTexID = In.GTexID; + BTexID = In.BTexID; + + ATexID = In.ATexID; + ATexIDExists = In.ATexIDExists; + + uTex1 = In.uTex1; + uTex2 = In.uTex2; + uTex3 = In.uTex3; + vTex1 = In.vTex1; + vTex2 = In.vTex2; + vTex3 = In.vTex3; + + wTex1 = In.wTex1; + wTex2 = In.wTex2; + wTex3 = In.wTex3; + WExists = In.WExists; + + + return *this; +} + +void nTexmap::Clear(void) +{ + RTexID = -1; + GTexID = -1; + BTexID = -1; + + ATexID = -1; + ATexIDExists = false; + + uTex1 = 0.0; + uTex2 = 0.0; + uTex3 = 0.0; + vTex1 = 0.0; + vTex2 = 0.0; + vTex3 = 0.0; + + wTex1 = 0.0; + wTex2 = 0.0; + wTex3 = 0.0; + WExists = false; + +} + +bool nTexmap::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage) +{ + pXML->OpenElement("texmap"); + pXML->SetElAttI("rtexid", RTexID); + pXML->SetElAttI("gtexid", GTexID); + pXML->SetElAttI("btexid", BTexID); + if (ATexIDExists) pXML->SetElAttI("atexid", ATexID); + + pXML->SetElementD("utex1", uTex1); + pXML->SetElementD("utex2", uTex2); + pXML->SetElementD("utex3", uTex3); + pXML->SetElementD("vtex1", vTex1); + pXML->SetElementD("vtex2", vTex2); + pXML->SetElementD("vtex3", vTex3); + if (WExists){ + pXML->SetElementD("wtex1", wTex1); + pXML->SetElementD("wtex2", wTex2); + pXML->SetElementD("wtex3", wTex3); + } + + pXML->CloseElement(); + return true; +} + +bool nTexmap::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage) +{ + Clear(); + + //required attributes + if (!pXML->GetElAttI("rtexid", &RTexID)) RTexID = -1; + if (!pXML->GetElAttI("gtexid", >exID)) GTexID = -1; + if (!pXML->GetElAttI("btexid", &BTexID)) BTexID = -1; + + //optional attributes + if (pXML->GetElAttI("atexid", &ATexID)) + ATexIDExists = true; + else ATexID = -1; + + //required tags + if (!pXML->GetElementD("utex1", &uTex1)) uTex1 = 0.0; + if (!pXML->GetElementD("utex2", &uTex2)) uTex2 = 0.0; + if (!pXML->GetElementD("utex3", &uTex3)) uTex3 = 0.0; + if (!pXML->GetElementD("vtex1", &vTex1)) vTex1 = 0.0; + if (!pXML->GetElementD("vtex2", &vTex2)) vTex2 = 0.0; + if (!pXML->GetElementD("vtex3", &vTex3)) vTex3 = 0.0; + + //optional tags + if (pXML->GetElementD("wtex1", &wTex1)) WExists = true; + else wTex1 = 0.0; + if (pXML->GetElementD("wtex2", &wTex2)) WExists = true; + else wTex2 = 0.0; + if (pXML->GetElementD("wtex3", &wTex3)) WExists = true; + else wTex3 = 0.0; + + //First draft AMF version of this tag: deprecated, but make sure we still read... + //required tags + if (vTex1 == 0 && vTex2 == 0 && vTex3 == 0){ + if (!pXML->GetElementD("u1", &uTex1)) uTex1 = 0.0; + if (!pXML->GetElementD("u2", &uTex2)) uTex2 = 0.0; + if (!pXML->GetElementD("u3", &uTex3)) uTex3 = 0.0; + if (!pXML->GetElementD("v1", &vTex1)) vTex1 = 0.0; + if (!pXML->GetElementD("v2", &vTex2)) vTex2 = 0.0; + if (!pXML->GetElementD("v3", &vTex3)) vTex3 = 0.0; + + //optional tags + if (pXML->GetElementD("w1", &wTex1)) WExists = true; + else wTex1 = 0.0; + if (pXML->GetElementD("w2", &wTex2)) WExists = true; + else wTex2 = 0.0; + if (pXML->GetElementD("w3", &wTex3)) WExists = true; + else wTex3 = 0.0; + //end first AMF version tags + } + return CheckValid(pAmf, !StrictLoad, pMessage); + +} + +bool nTexmap::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + if (RTexID == -1) return false; + if (GTexID == -1) return false; + if (BTexID == -1) return false; + if (ATexIDExists && ATexID == -1) return false; + + return true; +} diff --git a/libraries/amf/amftools-code/src/nTexture.cpp b/libraries/amf/amftools-code/src/nTexture.cpp new file mode 100644 index 00000000..90e956a4 --- /dev/null +++ b/libraries/amf/amftools-code/src/nTexture.cpp @@ -0,0 +1,242 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nTexture.h" +#include "XmlStream.h" +#include "nAmf.h" + + + +nTexture::nTexture(nAmf* pnAmfIn) +{ + pnAmf = pnAmfIn; + Clear(); +} + + +nTexture::~nTexture(void) +{ +} + +void nTexture::Clear(void) +{ + aID = -1; + aWidth = 1; + aHeight = 1; + aDepth = 1; + Type = TT_GRAYSCALE; + aTiled = true; + + aID = pnAmf->GetUnusedTexID(); + + BinaryData.clear(); +} + +nTexture& nTexture::operator=(const nTexture& In) +{ + aID = In.aID; + aWidth = In.aWidth; + aHeight = In.aHeight; + aDepth = In.aDepth; + Type = In.Type; + aTiled = In.aTiled; + + BinaryData = In.BinaryData; + + pnAmf = In.pnAmf; + + + return *this; +} + + +bool nTexture::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage) +{ + pXML->OpenElement("texture"); + pXML->SetElAttI("id", aID); + pXML->SetElAttI("width", aWidth); + pXML->SetElAttI("height", aHeight); + pXML->SetElAttI("depth", aDepth); + pXML->SetElAttB("tiled", aTiled); + + switch (Type){ + case TT_GRAYSCALE: pXML->SetElAttS("type", "grayscale"); + default: break; + } + + pXML->SetElDataS(DataToBase64()); + + pXML->CloseElement(); + return true; +} + + +bool nTexture::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage) +{ + Clear(); + std::string tmp; + + if (!pXML->GetElAttI("id", &aID)) {*pMessage += "Did not find valid ID for texture."; return false;} + if (!pXML->GetElAttI("width", &aWidth)) aWidth = 1; + if (!pXML->GetElAttI("height", &aHeight)) aHeight = 1; + if (!pXML->GetElAttI("depth", &aDepth)) aDepth = 1; + if (!pXML->GetElAttB("tiled", &aTiled)) aTiled = true; + + std::string tmpType; + if (pXML->GetElAttS("type", &tmpType)){ + if(tmpType == "grayscale") Type = TT_GRAYSCALE; + else {*pMessage += "Invalid texture type attribute encountered.\n";} + } + else Type = TT_GRAYSCALE; + + std::string tmpData; + if (pXML->GetElDataS(&tmpData)) Base64ToData(tmpData); //todo: handle if CDATA! + else {//loads equation if it exists + *pMessage += "Load Error: Invalid data in texture tag. Aborting. \n"; + return false; + } + + + return CheckValid(pAmf, !StrictLoad, pMessage); +} + +bool nTexture::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + //Check stated sizes: Width + if (aWidth <= 0){ + if (FixNode) {aWidth = 1; if (pMessage) *pMessage += "Warning: Invalid Texture width specified. Setting to 1.\n";} + else {if (pMessage) *pMessage += "Error: Invalid Texture width specified.\n"; return false;} + } + //Check stated sizes: Height + if (aHeight <= 0){ + if (FixNode) {aHeight = 1; if (pMessage) *pMessage += "Warning: Invalid Texture height specified. Setting to 1.\n";} + else {if (pMessage) *pMessage += "Error: Invalid Texture height specified.\n"; return false;} + } + //Check stated sizes: Depth + if (aDepth <= 0){ + if (FixNode) {aDepth = 1; if (pMessage) *pMessage += "Warning: Invalid Texture depth specified. Setting to 1.\n";} + else {if (pMessage) *pMessage += "Error: Invalid Texture depth specified.\n"; return false; } + } + + //Check data size + int NumSpecPic = aWidth*aHeight*aDepth; + if ((int)BinaryData.size() < NumSpecPic){ //too little data. + BinaryData.resize(NumSpecPic, 0); if (pMessage) *pMessage += "Warning: Not enough texture data for the specified size. Padded with null pixels.\n"; +// if (FixNode) {BinaryData.resize(NumSpecPic, 0); if (pMessage) *pMessage += "Warning: Not enough texture data for the specified size. Padded with black pixels.\n";} +// else {if (pMessage) *pMessage += "Error: Not enough texture data for the specified size.\n"; return false; } + } + else if ((int)BinaryData.size() > NumSpecPic){ + BinaryData.resize(NumSpecPic); if (pMessage) *pMessage += "Warning: Too much texture data for the specified size. Data truncated accordingly.\n"; +// if (FixNode) {BinaryData.resize(NumSpecPic); if (pMessage) *pMessage += "Warning: Too much texture data for the specified size. Data truncated accordingly.\n";} +// else {if (pMessage) *pMessage += "Error: Too much texture data for the specified size.\n"; return false; } + } + + //Check for invalid geometry ID + if (aID < 0 || pAmf->IsDuplicateTexID(aID)){ + if (FixNode) { + aID = pAmf->GetUnusedTexID(); + if (pMessage) *pMessage += "Warning: Invalid or duplicate texture ID found. Setting to an unused ID. TexMap elements may need to be adjusted.\n"; + } + else { + if (pMessage) *pMessage += "Error: Invalid or duplicate texture ID found.\n"; + return false; + } + } + + return true; +} + +bool nTexture::Base64ToData(std::string inputBase64) //converts base 64 to data. returns false if not valid B64 data +{ + std::string decoded = FromBase64(inputBase64); + if (decoded == "") return false; + + BinaryData = std::vector(decoded.begin(), decoded.end()); + return true; +} + +std::string nTexture::ToBase64(unsigned char const* bytes_to_encode, unsigned int in_len) // Ren Nyffenegger http://www.adp-gmbh.ch/cpp/common/base64.html +{ + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + for(i = 0; i<4; i++) ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i){ + for(j = i; j < 3; j++) char_array_3[j] = '\0'; + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + for (j = 0; (j> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) char_array_4[j] = 0; + for (j = 0; j <4; j++) char_array_4[j] = base64_chars.find(char_array_4[j]); + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} + +double nTexture::GetValue(double uIn, double vIn, double wIn) +{ + //get closest value (as opposed to interpolation) + int uCoord, vCoord, wCoord; + + if (uIn == 1.0) uCoord = aWidth-1; else uCoord = (int)(uIn*aWidth); + if (vIn == 1.0) vCoord = aHeight-1; else vCoord = (int)(vIn*aHeight); + if (wIn == 1.0) wCoord = aDepth-1; else wCoord = (int)(wIn*aDepth); + + unsigned char val = BinaryData[aWidth*aHeight*wCoord+aWidth*vCoord+uCoord]; + return (double)val/255.0; +} diff --git a/libraries/amf/amftools-code/src/nTriangle.cpp b/libraries/amf/amftools-code/src/nTriangle.cpp new file mode 100644 index 00000000..3cf66357 --- /dev/null +++ b/libraries/amf/amftools-code/src/nTriangle.cpp @@ -0,0 +1,112 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nTriangle.h" +#include "XmlStream.h" + + + +nTriangle::nTriangle(void) +{ + Clear(); +} + + +nTriangle::~nTriangle(void) +{ +} + +nTriangle& nTriangle::operator=(const nTriangle& In) +{ + v1 = In.v1; + v2 = In.v2; + v3 = In.v3; + ColorExists = In.ColorExists; + Color = In.Color; + TexMapExists = In.TexMapExists; + TexMap = In.TexMap; +// MapList = In.MapList; + + + return *this; +} + + +void nTriangle::Clear(void) +{ + v1 = 0; + v2 = 0; + v3 = 0; + + ColorExists = false; + Color.Clear(); + + TexMapExists = false; + TexMap.Clear(); +// MapList.clear(); + +} + + +bool nTriangle::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage) +{ + pXML->OpenElement("triangle"); + + if (ColorExists) if (!Color.WriteXML(pXML, pMessage)) return false; + + pXML->SetElementI("v1", v1); + pXML->SetElementI("v2", v2); + pXML->SetElementI("v3", v3); + + if (TexMapExists) if (!TexMap.WriteXML(pXML, pMessage)) return false; + + pXML->CloseElement(); + return true; +} + + +bool nTriangle::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage) +{ + Clear(); + + if (!pXML->GetElementI("v1", &v1)) {v1 = -1; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + if (!pXML->GetElementI("v2", &v2)) {v2 = -1; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + if (!pXML->GetElementI("v3", &v3)) {v3 = -1; *pMessage = " tag found without a tag. Load canceled.\n"; return false;} + + if (pXML->OpenElement("color")){Color.ReadXML(pXML, pAmf, StrictLoad, pMessage); ColorExists=true; pXML->CloseElement();} + + if (pXML->OpenElement("texmap")){TexMap.ReadXML(pXML, pAmf, StrictLoad, pMessage); TexMapExists=true; pXML->CloseElement();} + + //read as many map tags as there are... + //nTexmap tmpMap; + // while(pXML->OpenElement("texmap", true)){ // + // if (!tmpMap.ReadXML(pXML, pAmf, pMessage)) return false; + // MapList.push_back(tmpMap); + // MapListTest.push_back(0); + + //} + + //deprecated old tag now changed to Delete this eventually! + if (pXML->OpenElement("map")){TexMap.ReadXML(pXML, pAmf, StrictLoad, pMessage); TexMapExists=true; pXML->CloseElement();} + + //while(pXML->OpenElement("map", true)){ // + // if (!tmpMap.ReadXML(pXML, pAmf, pMessage)) return false; + // MapList.push_back(tmpMap); + // MapListTest.push_back(0); + + //} + + return true; +} + +bool nTriangle::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + if (v1 == -1 || v2 == -1 || v3 == -1) return false; + else return true; +} diff --git a/libraries/amf/amftools-code/src/nVertex.cpp b/libraries/amf/amftools-code/src/nVertex.cpp new file mode 100644 index 00000000..1aba0fa9 --- /dev/null +++ b/libraries/amf/amftools-code/src/nVertex.cpp @@ -0,0 +1,101 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nVertex.h" +#include "XmlStream.h" + + + +nVertex::nVertex(void) +{ + Clear(); + +} + + +nVertex::~nVertex(void) +{ +} + +nVertex::nVertex(const double X, const double Y, const double Z) +{ + Clear(); + Coordinates = nCoordinates(X, Y, Z); + +} + +nVertex::nVertex(const double X, const double Y, const double Z, const nColor& ColorIn) +{ + Clear(); + Coordinates = nCoordinates(X, Y, Z); + ColorExists = true; + Color = ColorIn; + +} + + +nVertex& nVertex::operator=(const nVertex& In) +{ + Coordinates = In.Coordinates; + NormalExists = In.NormalExists; + Normal = In.Normal; + ColorExists = In.ColorExists; + Color = In.Color; + + return *this; +} + + +void nVertex::Clear(void) +{ + Coordinates.Clear(); + + NormalExists = false; + Normal.Clear(); + + ColorExists = false; + Color.Clear(); + + +} + + +bool nVertex::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage) +{ + pXML->OpenElement("vertex"); + + if (!Coordinates.WriteXML(pXML, pMessage)) return false; + if (NormalExists) if (!Normal.WriteXML(pXML, pMessage)) return false; + if (ColorExists) if(!Color.WriteXML(pXML, pMessage)) return false; + + pXML->CloseElement(); + return true; +} + +bool nVertex::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage) +{ + Clear(); + + //required tags + if (pXML->OpenElement("coordinates")){Coordinates.ReadXML(pXML, pAmf, StrictLoad, pMessage); pXML->CloseElement();} + else {*pMessage += "No coordinates found in tag."; return false;} + + //optional tags + if (pXML->OpenElement("normal")){Normal.ReadXML(pXML, pAmf, StrictLoad, pMessage); NormalExists=true; pXML->CloseElement();} + if (pXML->OpenElement("color")){Color.ReadXML(pXML, pAmf, StrictLoad, pMessage); ColorExists=true; pXML->CloseElement();} + + return CheckValid(pAmf, !StrictLoad, pMessage); + +} + +bool nVertex::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + //TODO: + return true; +} diff --git a/libraries/amf/amftools-code/src/nVertices.cpp b/libraries/amf/amftools-code/src/nVertices.cpp new file mode 100644 index 00000000..62ead092 --- /dev/null +++ b/libraries/amf/amftools-code/src/nVertices.cpp @@ -0,0 +1,159 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nVertices.h" +#include "XmlStream.h" + +#include "nVertex.h" +#include "nEdge.h" +#include "Vec3D.h" + +nVertices::nVertices(void) +{ + Clear(); +} + + +nVertices::~nVertices(void) +{ +} + +nVertices& nVertices::operator=(const nVertices& In) +{ + VertexList = In.VertexList; + EdgeList = In.EdgeList; + + return *this; +} + + +void nVertices::Clear(void) +{ + VertexList.clear(); + EdgeList.clear(); +} + + +bool nVertices::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage, bool* pCancelFlag) +{ + if (pCancelFlag && *pCancelFlag) return false; + + pXML->OpenElement("vertices"); + + //vertices... + for(std::vector::iterator it = VertexList.begin(); it != VertexList.end(); it++){ + if (!it->WriteXML(pXML, pMessage)) return false; + if (pCancelFlag && *pCancelFlag) return false; + } + + //edges + for(std::vector::iterator it = EdgeList.begin(); it != EdgeList.end(); it++){ + if (!it->WriteXML(pXML, pMessage)) return false; + if (pCancelFlag && *pCancelFlag) return false; + } + + pXML->CloseElement(); + return true; +} + +bool nVertices::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage, bool* pCancelFlag) +{ + if (pCancelFlag && *pCancelFlag) return false; + Clear(); + + //read as many vertex tags as there are... + nVertex tmpVert; + while(pXML->OpenElement("vertex", true)){ // + if (!tmpVert.ReadXML(pXML, pAmf, StrictLoad, pMessage)) return false; + VertexList.push_back(tmpVert); + if (pCancelFlag && *pCancelFlag) return false; + } + + //read as many edge tags as there are... + nEdge tmpEdge; + while(pXML->OpenElement("edge", true)){ // + if (!tmpEdge.ReadXML(pXML, pAmf, StrictLoad, pMessage)) return false; + EdgeList.push_back(tmpEdge); + if (pCancelFlag && *pCancelFlag) return false; + } + + return CheckValid(pAmf, !StrictLoad, pMessage); + +} + +bool nVertices::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + if (VertexList.size() == 0){ + if (pMessage) *pMessage += "Warning: No vertices found in vertices tag.\n"; + } + + //check if edges reference any vertices that don't exist + int NumVert = VertexList.size(); + + std::vectorEdgeToDelete; + int count=0; + for (std::vector::iterator it = EdgeList.begin(); it!=EdgeList.end(); it++){ + bool DeleteEdge = false; + for (int i=0; i<2; i++){ + int ThisInd; + switch (i){ + case 0: ThisInd = it->v1; break; + case 1: ThisInd = it->v2; break; + } + if (ThisInd >= NumVert){ + if (FixNode) { + DeleteEdge = true; + if (pMessage) *pMessage += "Warning: Edge references non-existent vertex. Deleting edge.\n"; + } + else { + if (pMessage) *pMessage += "Error: Edge references non-existent vertex.\n"; + return false; + } + } + } + if (DeleteEdge) EdgeToDelete.push_back(count); + count++; + } + for (int i=EdgeToDelete.size()-1; i >= 0; i--){ //go backwards so out indices stay correct... + EdgeList.erase(EdgeList.begin()+EdgeToDelete[i]); + } + + //Todo: + //check for duplicate edges (that reference the same two vertices) + + return true; +} + +void nVertices::Translate(double dx, double dy, double dz) +{ + for(std::vector::iterator it = VertexList.begin(); it != VertexList.end(); it++){ + it->SetCoordinates(it->GetX()+dx, it->GetY()+dy, it->GetZ()+dz); + } +} + +void nVertices::Rotate(double rx, double ry, double rz) +{ + for(std::vector::iterator it = VertexList.begin(); it != VertexList.end(); it++){ + Vec3D Loc = Vec3D(it->GetX(), it->GetY(), it->GetZ()); + Vec3D Normal = Vec3D(it->GetNX(), it->GetNY(), it->GetNZ()); + Loc.RotX(rx); Loc.RotY(ry); Loc.RotZ(rz); + Normal.RotX(rx); Normal.RotY(ry); Normal.RotZ(rz); + it->SetCoordinates(Loc.x, Loc.y, Loc.z); + it->SetNormal(Normal.x, Normal.y, Normal.z); + } + + for (std::vector::iterator it = EdgeList.begin(); it!=EdgeList.end(); it++){ + Vec3D t1=Vec3D(it->dx1, it->dy1, it->dz1); + Vec3D t2=Vec3D(it->dx2, it->dy2, it->dz2); + t1.RotX(rx); t1.RotY(ry); t1.RotZ(rz); + t2.RotX(rx); t2.RotY(ry); t2.RotZ(rz); + it->SetDirectionVectors(t1.x, t1.y, t1.z, t2.x, t2.y, t2.z); + } + +} diff --git a/libraries/amf/amftools-code/src/nVolume.cpp b/libraries/amf/amftools-code/src/nVolume.cpp new file mode 100644 index 00000000..0fe78588 --- /dev/null +++ b/libraries/amf/amftools-code/src/nVolume.cpp @@ -0,0 +1,175 @@ +/******************************************************************************* +Copyright (c) 2012, Jonathan Hiller + +This file is part of the AMF Tools suite. http://amf.wikispaces.com/ +AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +See for license details. +*******************************************************************************/ + +#include "nVolume.h" +#include "XmlStream.h" + +#include "nTriangle.h" + + +nVolume::nVolume(void) +{ + Clear(); + +} + + +nVolume::~nVolume(void) +{ +} + +nVolume::nVolume(std::string Name) +{ + Clear(); + if (Name != "") Metadata.push_back(nMetadata(MD_NAME, Name)); +} + + +nVolume& nVolume::operator=(const nVolume& In) +{ + MaterialIDExists = In.MaterialIDExists; + aMaterialID = In.aMaterialID; +// TypeExists = In.TypeExists; +// Type = In.Type; + Triangles = In.Triangles; + ColorExists = In.ColorExists; + Color = In.Color; + Metadata = In.Metadata; + + return *this; +} + + +void nVolume::Clear(void) +{ + MaterialIDExists = false; + aMaterialID = -1; +// TypeExists = false; +// Type = VT_OBJECT; + + Triangles.clear(); + ColorExists = false; + Color.Clear(); + Metadata.clear(); +} + + +bool nVolume::WriteXML(CXmlStreamWrite* pXML, std::string* pMessage, bool* pCancelFlag) +{ + if (pCancelFlag && *pCancelFlag) return false; + + pXML->OpenElement("volume"); + if (MaterialIDExists) pXML->SetElAttI("materialid", aMaterialID); +// if (TypeExists){ +// if (Type == VT_OBJECT) pXML->SetElAttS("type", "object"); +// else if (Type == VT_SUPPORT) pXML->SetElAttS("type", "support"); +// } + + for(std::vector::iterator it = Metadata.begin(); it != Metadata.end(); it++){ + if (!it->WriteXML(pXML, pMessage)) return false; + } + + if (ColorExists) if (!Color.WriteXML(pXML, pMessage)) return false; + + for(std::vector::iterator it = Triangles.begin(); it != Triangles.end(); it++){ + if (!it->WriteXML(pXML, pMessage)) return false; + if (pCancelFlag && *pCancelFlag) return false; + } + + pXML->CloseElement(); + return true; +} + + +bool nVolume::ReadXML(CXmlStreamRead* pXML, nAmf* pAmf, bool StrictLoad, std::string* pMessage, bool* pCancelFlag) +{ + if (pCancelFlag && *pCancelFlag) return false; + Clear(); + + if (pXML->GetElAttI("materialid", &aMaterialID)) MaterialIDExists = true; +// std::string GetType; +// if (pXML->GetElAttS("type", &GetType)){ + // TypeExists = true; + // if (GetType == "object") Type = VT_OBJECT; + // else if(GetType == "support") Type = VT_SUPPORT; + // else { + // TypeExists = false; //invalid type + // *pMessage += "invalid type attribute encountered. \"object\" and \"support\" are only supported values.\n"; + // } + //} + + //read as many metadata tags as there are... + nMetadata tmpMeta; + while(pXML->OpenElement("metadata", true)){ // + if (!tmpMeta.ReadXML(pXML, pAmf, StrictLoad, pMessage)) return false; + Metadata.push_back(tmpMeta); + } +// if (Metadata.size() != 0) pXML->UpLevel(); // + + if (pXML->OpenElement("color")){Color.ReadXML(pXML, pAmf, StrictLoad, pMessage); ColorExists=true; pXML->CloseElement();} + + + //read as many triangle tags as there are... + nTriangle tmpTri; + while(pXML->OpenElement("triangle", true)){ // + if (!tmpTri.ReadXML(pXML, pAmf, StrictLoad, pMessage)) return false; + Triangles.push_back(tmpTri); + if (pCancelFlag && *pCancelFlag) return false; + } +// if (Triangles.size() != 0) pXML->UpLevel(); // + + + return CheckValid(pAmf, !StrictLoad, pMessage); +} + +bool nVolume::CheckValid(nAmf* pAmf, bool FixNode, std::string* pMessage) +{ + //copied from load... + if (Triangles.size() == 0) {*pMessage += "No triangles in volume tag.\n"; return false;} + + //check for duplicate triangles? (i.e. referencing same vertices) + + else return true; +} + +void nVolume::SetName(std::string NewName) +{ + for (std::vector::iterator it = Metadata.begin(); it != Metadata.end(); it++){ + if(it->Type == MD_NAME){ + it->Data = NewName; //replace the name!! + return; + } + } + + //if we get here there was no existing Name metadata so add one... + Metadata.push_back(nMetadata(MD_NAME, NewName)); +} + +std::string nVolume::GetName(void) +{ + for (std::vector::iterator it = Metadata.begin(); it != Metadata.end(); it++){ + if(it->Type == MD_NAME) return it->Data; + } + return ""; +} + +void nVolume::SetMaterialID(int NewMatId) +{ + aMaterialID = NewMatId; + MaterialIDExists = true; +} + +bool nVolume::GetMaterialID(int* pMatIdRet) +{ + if (MaterialIDExists){ + *pMatIdRet = aMaterialID; + return true; + } + else return false; //no materialID +} diff --git a/libraries/amf/amftools-code/src/stb_image/stb_image.cpp b/libraries/amf/amftools-code/src/stb_image/stb_image.cpp new file mode 100644 index 00000000..24c55a85 --- /dev/null +++ b/libraries/amf/amftools-code/src/stb_image/stb_image.cpp @@ -0,0 +1,4342 @@ +#include "stb_image/stb_image.h" + +#ifndef STBI_HEADER_FILE_ONLY + +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp, strtok +#endif + +#ifndef STBI_NO_STDIO +#include +#endif +#include +#include +#include +#include + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +// implementation: +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +typedef unsigned int uint; + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(uint32)==4 ? 1 : -1]; + +#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE) +#define STBI_NO_WRITE +#endif + +#define STBI_NOTUSED(v) (void)sizeof(v) + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +/////////////////////////////////////////////// +// +// stbi struct and start_xxx functions + +// stbi structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + uint8 buffer_start[128]; + + uint8 *img_buffer, *img_buffer_end; + uint8 *img_buffer_original; +} stbi; + + +static void refill_buffer(stbi *s); + +// initialize a memory-decode context +static void start_mem(stbi *s, uint8 const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (uint8 *) buffer; + s->img_buffer_end = (uint8 *) buffer+len; +} + +// initialize a callback-based context +static void start_callbacks(stbi *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + refill_buffer(s); +} + +#ifndef STBI_NO_STDIO + +static int stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stdio_skip(void *user, unsigned n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi_stdio_callbacks = +{ + stdio_read, + stdio_skip, + stdio_eof, +}; + +static void start_file(stbi *s, FILE *f) +{ + start_callbacks(s, &stbi_stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi_rewind(stbi *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; +} + +static int stbi_jpeg_test(stbi *s); +static stbi_uc *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp); +static int stbi_png_test(stbi *s); +static stbi_uc *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_png_info(stbi *s, int *x, int *y, int *comp); +static int stbi_bmp_test(stbi *s); +static stbi_uc *stbi_bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_test(stbi *s); +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_info(stbi *s, int *x, int *y, int *comp); +static int stbi_psd_test(stbi *s); +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_hdr_test(stbi *s); +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_pic_test(stbi *s); +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_test(stbi *s); +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp); + + +// this is not threadsafe +static const char *failure_reason; + +const char *stbi_failure_reason(void) +{ + return failure_reason; +} + +static int e(const char *str) +{ + failure_reason = str; + return 0; +} + +// e - error +// epf - error returning pointer to float +// epuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define e(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define e(x,y) e(y) +#else + #define e(x,y) e(x) +#endif + +#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) +#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) + +void stbi_image_free(void *retval_from_stbi_load) +{ + free(retval_from_stbi_load); +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + if (stbi_jpeg_test(s)) return stbi_jpeg_load(s,x,y,comp,req_comp); + if (stbi_png_test(s)) return stbi_png_load(s,x,y,comp,req_comp); + if (stbi_bmp_test(s)) return stbi_bmp_load(s,x,y,comp,req_comp); + if (stbi_gif_test(s)) return stbi_gif_load(s,x,y,comp,req_comp); + if (stbi_psd_test(s)) return stbi_psd_load(s,x,y,comp,req_comp); + if (stbi_pic_test(s)) return stbi_pic_load(s,x,y,comp,req_comp); + + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) { + float *hdr = stbi_hdr_load(s, x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + // test tga last because it's a crappy test! + if (stbi_tga_test(s)) + return stbi_tga_load(s,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + unsigned char *result; + if (!f) return epuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_load_main(&s,x,y,comp,req_comp); +} +#endif //!STBI_NO_STDIO + +unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_HDR + +float *stbi_loadf_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) + return stbi_hdr_load(s,x,y,comp,req_comp); + #endif + data = stbi_load_main(s, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} + +float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + float *result; + if (!f) return epf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_HDR + +// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is +// defined, for API simplicity; if STBI_NO_HDR is defined, it always +// reports false! + +int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi s; + start_mem(&s,buffer,len); + return stbi_hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename) +{ + FILE *f = fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +extern int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi s; + start_file(&s,f); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} + +#ifndef STBI_NO_HDR +static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; +static float l2h_gamma=2.2f, l2h_scale=1.0f; + +void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } +void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } + +void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } +void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + SCAN_load=0, + SCAN_type, + SCAN_header +}; + +static void refill_buffer(stbi *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_end-1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static int get8(stbi *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int at_eof(stbi *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +stbi_inline static uint8 get8u(stbi *s) +{ + return (uint8) get8(s); +} + +static void skip(stbi *s, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int getn(stbi *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int get16(stbi *s) +{ + int z = get8(s); + return (z << 8) + get8(s); +} + +static uint32 get32(stbi *s) +{ + uint32 z = get16(s); + return (z << 16) + get16(s); +} + +static int get16le(stbi *s) +{ + int z = get8(s); + return z + (get8(s) << 8); +} + +static uint32 get32le(stbi *s) +{ + uint32 z = get16le(s); + return z + (get16le(s) << 16); +} + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static uint8 compute_y(int r, int g, int b) +{ + return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, uint x, uint y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + assert(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) malloc(req_comp * x * y); + if (good == NULL) { + free(data); + return epuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: assert(0); + } + #undef CASE + } + + free(data); + return good; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) malloc(x * y * comp * sizeof(float)); + if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + free(data); + return output; +} + +#define float2int(x) ((int) (x)) +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) malloc(x * y * comp); + if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + } + free(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) +// +// simple implementation +// - channel subsampling of at most 2 in each dimension +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - uses a lot of intermediate memory, could cache poorly +// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 +// stb_jpeg: 1.34 seconds (MSVC6, default release build) +// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) +// IJL11.dll: 1.08 seconds (compiled by intel) +// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) +// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + uint8 fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + uint16 code[256]; + uint8 values[256]; + uint8 size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} huffman; + +typedef struct +{ + #ifdef STBI_SIMD + unsigned short dequant2[4][64]; + #endif + stbi *s; + huffman huff_dc[4]; + huffman huff_ac[4]; + uint8 dequant[4][64]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + uint8 *data; + void *raw_data; + uint8 *linebuf; + } img_comp[4]; + + uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int scan_n, order[4]; + int restart_interval, todo; +} jpeg; + +static int build_huffman(huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (uint8) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (uint16) (code++); + if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (uint8) i; + } + } + } + return 1; +} + +static void grow_buffer_unsafe(jpeg *j) +{ + do { + int b = j->nomore ? 0 : get8(j->s); + if (b == 0xff) { + int c = get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int decode(jpeg *j, huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & bmask[k]) + h->delta[k]; + assert((((j->code_buffer) >> (32 - h->size[c])) & bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +stbi_inline static int extend_receive(jpeg *j, int n) +{ + unsigned int m = 1 << (n-1); + unsigned int k; + if (j->code_bits < n) grow_buffer_unsafe(j); + + #if 1 + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~bmask[n]; + k &= bmask[n]; + j->code_bits -= n; + #else + k = (j->code_buffer >> (32 - n)) & bmask[n]; + j->code_bits -= n; + j->code_buffer <<= n; + #endif + // the following test is probably a random branch that won't + // predict well. I tried to table accelerate it but failed. + // maybe it's compiling as a conditional move? + if (k < m) + return (-1 << n) + k + 1; + else + return k; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static uint8 dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b) +{ + int diff,dc,k; + int t = decode(j, hdc); + if (t < 0) return e("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) dc; + + // decode AC components, see JPEG spec + k = 1; + do { + int r,s; + int rs = decode(j, hac); + if (rs < 0) return e("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + data[dezigzag[k++]] = (short) extend_receive(j,s); + } + } while (k < 64); + return 1; +} + +// take a -128..127 value and clamp it and convert to 0..255 +stbi_inline static uint8 clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (uint8) x; +} + +#define f2f(x) (int) (((x) * 4096 + 0.5)) +#define fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * f2f(0.5411961f); \ + t2 = p1 + p3*f2f(-1.847759065f); \ + t3 = p1 + p2*f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = fsh(p2+p3); \ + t1 = fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*f2f( 1.175875602f); \ + t0 = t0*f2f( 0.298631336f); \ + t1 = t1*f2f( 2.053119869f); \ + t2 = t2*f2f( 3.072711026f); \ + t3 = t3*f2f( 1.501321110f); \ + p1 = p5 + p1*f2f(-0.899976223f); \ + p2 = p5 + p2*f2f(-2.562915447f); \ + p3 = p3*f2f(-1.961570560f); \ + p4 = p4*f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +#ifdef STBI_SIMD +typedef unsigned short stbi_dequantize_t; +#else +typedef uint8 stbi_dequantize_t; +#endif + +// .344 seconds on 3*anemones.jpg +static void idct_block(uint8 *out, int out_stride, short data[64], stbi_dequantize_t *dequantize) +{ + int i,val[64],*v=val; + stbi_dequantize_t *dq = dequantize; + uint8 *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SIMD +static stbi_idct_8x8 stbi_idct_installed = idct_block; + +void stbi_install_idct(stbi_idct_8x8 func) +{ + stbi_idct_installed = func; +} +#endif + +#define MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static uint8 get_marker(jpeg *j) +{ + uint8 x; + if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; } + x = get8u(j->s); + if (x != 0xff) return MARKER_none; + while (x == 0xff) + x = get8u(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, reset the entropy decoder and +// the dc prediction +static void reset(jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int parse_entropy_coded_data(jpeg *z) +{ + reset(z); + if (z->scan_n == 1) { + int i,j; + #ifdef STBI_SIMD + __declspec(align(16)) + #endif + short data[64]; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } else { // interleaved! + int i,j,k,x,y; + short data[64]; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } + return 1; +} + +static int process_marker(jpeg *z, int m) +{ + int L; + switch (m) { + case MARKER_none: // no marker found + return e("expected marker","Corrupt JPEG"); + + case 0xC2: // SOF - progressive + return e("progressive jpeg","JPEG format not supported (progressive)"); + + case 0xDD: // DRI - specify restart interval + if (get16(z->s) != 4) return e("bad DRI len","Corrupt JPEG"); + z->restart_interval = get16(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = get16(z->s)-2; + while (L > 0) { + int q = get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return e("bad DQT type","Corrupt JPEG"); + if (t > 3) return e("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][dezigzag[i]] = get8u(z->s); + #ifdef STBI_SIMD + for (i=0; i < 64; ++i) + z->dequant2[t][i] = z->dequant[t][i]; + #endif + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = get16(z->s)-2; + while (L > 0) { + uint8 *v; + int sizes[16],i,m=0; + int q = get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = get8(z->s); + m += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < m; ++i) + v[i] = get8u(z->s); + L -= m; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + skip(z->s, get16(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int process_scan_header(jpeg *z) +{ + int i; + int Ls = get16(z->s); + z->scan_n = get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return e("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = get8(z->s), which; + int q = get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + get8(z->s); // should be 63, but might be 0 + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + + return 1; +} + +static int process_frame_header(jpeg *z, int scan) +{ + stbi *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG + p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires + c = get8(s); + if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return e("bad component ID","Corrupt JPEG"); + q = get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); + z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); + } + + if (scan != SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + free(z->img_comp[i].raw_data); + z->img_comp[i].data = NULL; + } + return e("outofmem", "Out of memory"); + } + // align blocks for installable-idct using mmx/sse + z->img_comp[i].data = (uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define DNL(x) ((x) == 0xdc) +#define SOI(x) ((x) == 0xd8) +#define EOI(x) ((x) == 0xd9) +#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) +#define SOS(x) ((x) == 0xda) + +static int decode_jpeg_header(jpeg *z, int scan) +{ + int m; + z->marker = MARKER_none; // initialize cached marker to empty + m = get_marker(z); + if (!SOI(m)) return e("no SOI","Corrupt JPEG"); + if (scan == SCAN_type) return 1; + m = get_marker(z); + while (!SOF(m)) { + if (!process_marker(z,m)) return 0; + m = get_marker(z); + while (m == MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (at_eof(z->s)) return e("no SOF", "Corrupt JPEG"); + m = get_marker(z); + } + } + if (!process_frame_header(z, scan)) return 0; + return 1; +} + +static int decode_jpeg_image(jpeg *j) +{ + int m; + j->restart_interval = 0; + if (!decode_jpeg_header(j, SCAN_load)) return 0; + m = get_marker(j); + while (!EOI(m)) { + if (SOS(m)) { + if (!process_scan_header(j)) return 0; + if (!parse_entropy_coded_data(j)) return 0; + if (j->marker == MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!at_eof(j->s)) { + int x = get8(j->s); + if (x == 255) { + j->marker = get8u(j->s); + break; + } else if (x != 0) { + return 0; + } + } + // if we reach eof without hitting a marker, get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!process_marker(j, m)) return 0; + } + m = get_marker(j); + } + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, + int w, int hs); + +#define div4(x) ((uint8) ((x) >> 2)) + +static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + uint8 *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = div4(n+input[i-1]); + out[i*2+1] = div4(n+input[i+1]); + } + out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define div16(x) ((uint8) ((x) >> 4)) + +static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = div16(3*t0 + t1 + 8); + out[i*2 ] = div16(3*t1 + t0 + 8); + } + out[w*2-1] = div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + in_far = in_far; + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) + +// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) +// VC6 without processor=Pro is generating multiple LEAs per multiply! +static void YCbCr_to_RGB_row(uint8 *out, const uint8 *y, const uint8 *pcb, const uint8 *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (uint8)r; + out[1] = (uint8)g; + out[2] = (uint8)b; + out[3] = 255; + out += step; + } +} + +#ifdef STBI_SIMD +static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; + +void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) +{ + stbi_YCbCr_installed = func; +} +#endif + + +// clean up the temporary component buffers +static void cleanup_jpeg(jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].data) { + free(j->img_comp[i].raw_data); + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].linebuf) { + free(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + uint8 *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi_resample; + +static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + // validate req_comp + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + z->s->img_n = 0; + + // load a jpeg image from whichever source + if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + uint i,j; + uint8 *output; + uint8 *coutput[4]; + + stbi_resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (uint8 *) malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; + else r->resample = resample_row_generic; + } + + // can't error after this so, this is safe + output = (uint8 *) malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + uint8 *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + uint8 *y = coutput[0]; + if (z->s->img_n == 3) { + #ifdef STBI_SIMD + stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s.img_x, n); + #else + YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s->img_x, n); + #endif + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + uint8 *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + jpeg j; + j.s = s; + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi_jpeg_test(stbi *s) +{ + int r; + jpeg j; + j.s = s; + r = decode_jpeg_header(&j, SCAN_type); + stbi_rewind(s); + return r; +} + +static int stbi_jpeg_info_raw(jpeg *j, int *x, int *y, int *comp) +{ + if (!decode_jpeg_header(j, SCAN_header)) { + stbi_rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp) +{ + jpeg j; + j.s = s; + return stbi_jpeg_info_raw(&j, x, y, comp); +} + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define ZFAST_BITS 9 // accelerate all cases in default tables +#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + uint16 fast[1 << ZFAST_BITS]; + uint16 firstcode[16]; + int maxcode[17]; + uint16 firstsymbol[16]; + uint8 size[288]; + uint16 value[288]; +} zhuffman; + +stbi_inline static int bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int bit_reverse(int v, int bits) +{ + assert(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return bitreverse16(v) >> (16-bits); +} + +static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 255, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + assert(sizes[i] <= (1 << i)); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (uint16) code; + z->firstsymbol[i] = (uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + z->size[c] = (uint8)s; + z->value[c] = (uint16)i; + if (s <= ZFAST_BITS) { + int k = bit_reverse(next_code[s],s); + while (k < (1 << ZFAST_BITS)) { + z->fast[k] = (uint16) c; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + uint8 *zbuffer, *zbuffer_end; + int num_bits; + uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + zhuffman z_length, z_distance; +} zbuf; + +stbi_inline static int zget8(zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void fill_bits(zbuf *z) +{ + do { + assert(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int zreceive(zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +stbi_inline static int zhuffman_decode(zbuf *a, zhuffman *z) +{ + int b,s,k; + if (a->num_bits < 16) fill_bits(a); + b = z->fast[a->code_buffer & ZFAST_MASK]; + if (b < 0xffff) { + s = z->size[b]; + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; + } + + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = bit_reverse(a->code_buffer, 16); + for (s=ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + assert(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +static int expand(zbuf *z, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + if (!z->z_expandable) return e("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) realloc(z->zout_start, limit); + if (q == NULL) return e("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int length_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int length_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int dist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int parse_huffman_block(zbuf *a) +{ + for(;;) { + int z = zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes + if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0; + *a->zout++ = (char) z; + } else { + uint8 *p; + int len,dist; + if (z == 256) return 1; + z -= 257; + len = length_base[z]; + if (length_extra[z]) len += zreceive(a, length_extra[z]); + z = zhuffman_decode(a, &a->z_distance); + if (z < 0) return e("bad huffman code","Corrupt PNG"); + dist = dist_base[z]; + if (dist_extra[z]) dist += zreceive(a, dist_extra[z]); + if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG"); + if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0; + p = (uint8 *) (a->zout - dist); + while (len--) + *a->zout++ = *p++; + } + } +} + +static int compute_huffman_codes(zbuf *a) +{ + static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + zhuffman z_codelength; + uint8 lencodes[286+32+137];//padding for maximum single op + uint8 codelength_sizes[19]; + int i,n; + + int hlit = zreceive(a,5) + 257; + int hdist = zreceive(a,5) + 1; + int hclen = zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (uint8) s; + } + if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = zhuffman_decode(a, &z_codelength); + assert(c >= 0 && c < 19); + if (c < 16) + lencodes[n++] = (uint8) c; + else if (c == 16) { + c = zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + assert(c == 18); + c = zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); + if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int parse_uncompressed_block(zbuf *a) +{ + uint8 header[4]; + int len,nlen,k; + if (a->num_bits & 7) + zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (uint8) (a->code_buffer & 255); // wtf this warns? + a->code_buffer >>= 8; + a->num_bits -= 8; + } + assert(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = (uint8) zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!expand(a, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int parse_zlib_header(zbuf *a) +{ + int cmf = zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = zget8(a); + if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static uint8 default_length[288], default_distance[32]; +static void init_defaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) default_length[i] = 8; + for ( ; i <= 255; ++i) default_length[i] = 9; + for ( ; i <= 279; ++i) default_length[i] = 7; + for ( ; i <= 287; ++i) default_length[i] = 8; + + for (i=0; i <= 31; ++i) default_distance[i] = 5; +} + +int stbi_png_partial; // a quick hack to only allow decoding some of a PNG... I should implement real streaming support instead +static int parse_zlib(zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = zreceive(a,1); + type = zreceive(a,2); + if (type == 0) { + if (!parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!default_distance[31]) init_defaults(); + if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0; + if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0; + } else { + if (!compute_huffman_codes(a)) return 0; + } + if (!parse_huffman_block(a)) return 0; + } + if (stbi_png_partial && a->zout - a->zout_start > 65536) + break; + } while (!final); + return 1; +} + +static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return parse_zlib(a, parse_header); +} + +char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer+len; + if (do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + + +typedef struct +{ + uint32 length; + uint32 type; +} chunk; + +#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static chunk get_chunk_header(stbi *s) +{ + chunk c; + c.length = get32(s); + c.type = get32(s); + return c; +} + +static int check_png_header(stbi *s) +{ + static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (get8u(s) != png_sig[i]) return e("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi *s; + uint8 *idata, *expanded, *out; +} png; + + +enum { + F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, + F_avg_first, F_paeth_first +}; + +static uint8 first_row_filter[5] = +{ + F_none, F_sub, F_none, F_avg_first, F_paeth_first +}; + +static int paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +// create the png data from post-deflated data +static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, uint32 x, uint32 y) +{ + stbi *s = a->s; + uint32 i,j,stride = x*out_n; + int k; + int img_n = s->img_n; // copy it into a local for later + assert(out_n == s->img_n || out_n == s->img_n+1); + if (stbi_png_partial) y = 1; + a->out = (uint8 *) malloc(x * y * out_n); + if (!a->out) return e("outofmem", "Out of memory"); + if (!stbi_png_partial) { + if (s->img_x == x && s->img_y == y) { + if (raw_len != (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } + } + for (j=0; j < y; ++j) { + uint8 *cur = a->out + stride*j; + uint8 *prior = cur - stride; + int filter = *raw++; + if (filter > 4) return e("invalid filter","Corrupt PNG"); + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + // handle first pixel explicitly + for (k=0; k < img_n; ++k) { + switch (filter) { + case F_none : cur[k] = raw[k]; break; + case F_sub : cur[k] = raw[k]; break; + case F_up : cur[k] = raw[k] + prior[k]; break; + case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; + case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break; + case F_avg_first : cur[k] = raw[k]; break; + case F_paeth_first: cur[k] = raw[k]; break; + } + } + if (img_n != out_n) cur[img_n] = 255; + raw += img_n; + cur += out_n; + prior += out_n; + // this is a little gross, so that we don't switch per-pixel or per-component + if (img_n == out_n) { + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; + } + #undef CASE + } else { + assert(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + return 1; +} + +static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int interlaced) +{ + uint8 *final; + int p; + int save; + if (!interlaced) + return create_png_image_raw(a, raw, raw_len, out_n, a->s->img_x, a->s->img_y); + save = stbi_png_partial; + stbi_png_partial = 0; + + // de-interlacing + final = (uint8 *) malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + if (!create_png_image_raw(a, raw, raw_len, out_n, x, y)) { + free(final); + return 0; + } + for (j=0; j < y; ++j) + for (i=0; i < x; ++i) + memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n, + a->out + (j*x+i)*out_n, out_n); + free(a->out); + raw += (x*out_n+1)*y; + raw_len -= (x*out_n+1)*y; + } + } + a->out = final; + + stbi_png_partial = save; + return 1; +} + +static int compute_transparency(png *z, uint8 tc[3], int out_n) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + assert(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int expand_palette(png *a, uint8 *palette, int len, int pal_img_n) +{ + uint32 i, pixel_count = a->s->img_x * a->s->img_y; + uint8 *p, *temp_out, *orig = a->out; + + p = (uint8 *) malloc(pixel_count * pal_img_n); + if (p == NULL) return e("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + free(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi_unpremultiply_on_load = 0; +static int stbi_de_iphone_flag = 0; + +void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi_unpremultiply_on_load = flag_true_if_should_unpremultiply; +} +void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi_de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi_de_iphone(png *z) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + assert(s->img_out_n == 4); + if (stbi_unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + uint8 a = p[3]; + uint8 t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +static int parse_png_file(png *z, int scan, int req_comp) +{ + uint8 palette[1024], pal_img_n=0; + uint8 has_trans=0, tc[3]; + uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, iphone=0; + stbi *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!check_png_header(s)) return 0; + + if (scan == SCAN_type) return 1; + + for (;;) { + chunk c = get_chunk_header(s); + switch (c.type) { + case PNG_TYPE('C','g','B','I'): + iphone = stbi_de_iphone_flag; + skip(s, c.length); + break; + case PNG_TYPE('I','H','D','R'): { + int depth,color,comp,filter; + if (!first) return e("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); + s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); + s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); + depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); + color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); + comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG"); + filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG"); + interlace = get8(s); if (interlace>1) return e("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + if (scan == SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case PNG_TYPE('P','L','T','E'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = get8u(s); + palette[i*4+1] = get8u(s); + palette[i*4+2] = get8u(s); + palette[i*4+3] = 255; + } + break; + } + + case PNG_TYPE('t','R','N','S'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (z->idata) return e("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = get8u(s); + } else { + if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); + if (c.length != (uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (uint8) get16(s); // non 8-bit images will be larger + } + break; + } + + case PNG_TYPE('I','D','A','T'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); + if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } + if (ioff + c.length > idata_limit) { + uint8 *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); + z->idata = p; + } + if (!getn(s, z->idata+ioff,c.length)) return e("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case PNG_TYPE('I','E','N','D'): { + uint32 raw_len; + if (first) return e("first not IHDR", "Corrupt PNG"); + if (scan != SCAN_load) return 1; + if (z->idata == NULL) return e("no IDAT","Corrupt PNG"); + z->expanded = (uint8 *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !iphone); + if (z->expanded == NULL) return 0; // zlib should set error + free(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0; + if (has_trans) + if (!compute_transparency(z, tc, s->img_out_n)) return 0; + if (iphone && s->img_out_n > 2) + stbi_de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!expand_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + free(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return e("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX chunk not known"; + invalid_chunk[0] = (uint8) (c.type >> 24); + invalid_chunk[1] = (uint8) (c.type >> 16); + invalid_chunk[2] = (uint8) (c.type >> 8); + invalid_chunk[3] = (uint8) (c.type >> 0); + #endif + return e(invalid_chunk, "PNG not supported: unknown chunk type"); + } + skip(s, c.length); + break; + } + // end of chunk, read and skip CRC + get32(s); + } +} + +static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + if (parse_png_file(p, SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + free(p->out); p->out = NULL; + free(p->expanded); p->expanded = NULL; + free(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + png p; + p.s = s; + return do_png(&p, x,y,comp,req_comp); +} + +static int stbi_png_test(stbi *s) +{ + int r; + r = check_png_header(s); + stbi_rewind(s); + return r; +} + +static int stbi_png_info_raw(png *p, int *x, int *y, int *comp) +{ + if (!parse_png_file(p, SCAN_header, 0)) { + stbi_rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi_png_info(stbi *s, int *x, int *y, int *comp) +{ + png p; + p.s = s; + return stbi_png_info_raw(&p, x, y, comp); +} + +// Microsoft/Windows BMP image + +static int bmp_test(stbi *s) +{ + int sz; + if (get8(s) != 'B') return 0; + if (get8(s) != 'M') return 0; + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + get32le(s); // discard data offset + sz = get32le(s); + if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; + return 0; +} + +static int stbi_bmp_test(stbi *s) +{ + int r = bmp_test(s); + stbi_rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *out; + unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP"); + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + offset = get32le(s); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = get16le(s); + s->img_y = get16le(s); + } else { + s->img_x = get32le(s); + s->img_y = get32le(s); + } + if (get16le(s) != 1) return epuc("bad BMP", "bad BMP"); + bpp = get16le(s); + if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = get32le(s); + if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); + get32le(s); // discard sizeof + get32le(s); // discard hres + get32le(s); // discard vres + get32le(s); // discard colorsused + get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + get32le(s); + get32le(s); + get32le(s); + get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 + } else { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + } else if (compress == 3) { + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return epuc("bad BMP", "bad BMP"); + } + } else + return epuc("bad BMP", "bad BMP"); + } + } else { + assert(hsz == 108); + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + ma = get32le(s); + get32le(s); // discard color space + for (i=0; i < 12; ++i) + get32le(s); // discard color space parameters + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) malloc(target * s->img_x * s->img_y); + if (!out) return epuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + if (hsz != 12) get8(s); + pal[i][3] = 255; + } + skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { free(out); return epuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { free(out); return epuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = high_bit(mr)-7; rcount = bitcount(mr); + gshift = high_bit(mg)-7; gcount = bitcount(mr); + bshift = high_bit(mb)-7; bcount = bitcount(mr); + ashift = high_bit(ma)-7; acount = bitcount(mr); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + int a; + out[z+2] = get8u(s); + out[z+1] = get8u(s); + out[z+0] = get8u(s); + z += 3; + a = (easy == 2 ? get8(s) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + uint32 v = (bpp == 16 ? get16le(s) : get32le(s)); + int a; + out[z++] = (uint8) shiftsigned(v & mr, rshift, rcount); + out[z++] = (uint8) shiftsigned(v & mg, gshift, gcount); + out[z++] = (uint8) shiftsigned(v & mb, bshift, bcount); + a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } + skip(s, pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} + +static stbi_uc *stbi_bmp_load(stbi *s,int *x, int *y, int *comp, int req_comp) +{ + return bmp_load(s, x,y,comp,req_comp); +} + + +// Targa Truevision - TGA +// by Jonathan Dummer + +static int tga_info(stbi *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp; + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if( sz > 1 ) { + stbi_rewind(s); + return 0; // only RGB or indexed allowed + } + sz = get8u(s); // image type + // only RGB or grey allowed, +/- RLE + if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; + skip(s,9); + tga_w = get16le(s); + if( tga_w < 1 ) { + stbi_rewind(s); + return 0; // test width + } + tga_h = get16le(s); + if( tga_h < 1 ) { + stbi_rewind(s); + return 0; // test height + } + sz = get8(s); // bits per pixel + // only RGB or RGBA or grey allowed + if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi_rewind(s); + return 0; + } + tga_comp = sz; + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp / 8; + return 1; // seems to have passed everything +} + +int stbi_tga_info(stbi *s, int *x, int *y, int *comp) +{ + return tga_info(s, x, y, comp); +} + +static int tga_test(stbi *s) +{ + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if ( sz > 1 ) return 0; // only RGB or indexed allowed + sz = get8u(s); // image type + if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + get16(s); // discard palette start + get16(s); // discard palette length + get8(s); // discard bits per palette color entry + get16(s); // discard x origin + get16(s); // discard y origin + if ( get16(s) < 1 ) return 0; // test width + if ( get16(s) < 1 ) return 0; // test height + sz = get8(s); // bits per pixel + if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed + return 1; // seems to have passed everything +} + +static int stbi_tga_test(stbi *s) +{ + int res = tga_test(s); + stbi_rewind(s); + return res; +} + +static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = get8u(s); + int tga_indexed = get8u(s); + int tga_image_type = get8u(s); + int tga_is_RLE = 0; + int tga_palette_start = get16le(s); + int tga_palette_len = get16le(s); + int tga_palette_bits = get8u(s); + int tga_x_origin = get16le(s); + int tga_y_origin = get16le(s); + int tga_width = get16le(s); + int tga_height = get16le(s); + int tga_bits_per_pixel = get8u(s); + int tga_inverted = get8u(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + unsigned char trans_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if ( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA + } + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) + { + tga_bits_per_pixel = tga_palette_bits; + } + + // tga info + *x = tga_width; + *y = tga_height; + if ( (req_comp < 1) || (req_comp > 4) ) + { + // just use whatever the file was + req_comp = tga_bits_per_pixel / 8; + *comp = req_comp; + } else + { + // force a new number of components + *comp = tga_bits_per_pixel/8; + } + tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); + if (!tga_data) return epuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + skip(s, tga_offset ); + // do I need to load a palette? + if ( tga_indexed ) + { + // any data to skip? (offset usually = 0) + skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); + if (!tga_palette) return epuc("outofmem", "Out of memory"); + if (!getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { + free(tga_data); + free(tga_palette); + return epuc("bad palette", "Corrupt TGA"); + } + } + // load the data + trans_data[0] = trans_data[1] = trans_data[2] = trans_data[3] = 0; + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE chunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = get8u(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = get8u(s); + if ( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = get8u(s); + } + } + // convert raw to the intermediate format + switch (tga_bits_per_pixel) + { + case 8: + // Luminous => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 16: + // Luminous,Alpha => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[1]; + break; + case 24: + // BGR => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 32: + // BGRA => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[3]; + break; + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + // convert to final format + switch (req_comp) + { + case 1: + // RGBA => Luminance + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + break; + case 2: + // RGBA => Luminance,Alpha + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + tga_data[i*req_comp+1] = trans_data[3]; + break; + case 3: + // RGBA => RGB + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + break; + case 4: + // RGBA => RGBA + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + tga_data[i*req_comp+3] = trans_data[3]; + break; + } + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * req_comp; + int index2 = (tga_height - 1 - j) * tga_width * req_comp; + for (i = tga_width * req_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + free( tga_palette ); + } + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} + +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return tga_load(s,x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +static int psd_test(stbi *s) +{ + if (get32(s) != 0x38425053) return 0; // "8BPS" + else return 1; +} + +static int stbi_psd_test(stbi *s) +{ + int r = psd_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + uint8 *out; + + // Check identifier + if (get32(s) != 0x38425053) // "8BPS" + return epuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (get16(s) != 1) + return epuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) + return epuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = get32(s); + w = get32(s); + + // Make sure the depth is 8 bits. + if (get16(s) != 8) + return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (get16(s) != 3) + return epuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + skip(s,get32(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + skip(s, get32(s) ); + + // Skip the reserved data. + skip(s, get32(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = get16(s); + if (compression > 1) + return epuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) malloc(4 * w*h); + if (!out) return epuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = get8u(s); + p += 4; + len--; + } + } else if (len > 128) { + uint8 val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = get8u(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + for (i = 0; i < pixelCount; i++) + *p = get8u(s), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + out = convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} + +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return psd_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +static int pic_is4(stbi *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int pic_test(stbi *s) +{ + int i; + + if (!pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + get8(s); + + if (!pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} pic_packet_t; + +static stbi_uc *pic_readval(stbi *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (at_eof(s)) return epuc("bad file","PIC file too short"); + dest[i]=get8u(s); + } + } + + return dest; +} + +static void pic_copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *pic_load2(stbi *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + pic_packet_t packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return epuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + + act_comp |= packet->channel; + + if (at_eof(s)) return epuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return epuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return epuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=get8u(s); + if (at_eof(s)) return epuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (uint8) left; + + if (!pic_readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = get8(s), i; + if (at_eof(s)) return epuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + int i; + + if (count==128) + count = get16(s); + else + count -= 127; + if (count > left) + return epuc("bad file","scanline overrun"); + + if (!pic_readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return epuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *pic_load(stbi *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + get8(s); + + x = get16(s); + y = get16(s); + if (at_eof(s)) return epuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return epuc("too large", "Image too large to decode"); + + get32(s); //skip `ratio' + get16(s); //skip `fields' + get16(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!pic_load2(s,x,y,comp, result)) { + free(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi_pic_test(stbi *s) +{ + int r = pic_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return pic_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb +typedef struct stbi_gif_lzw_struct { + int16 prefix; + uint8 first; + uint8 suffix; +} stbi_gif_lzw; + +typedef struct stbi_gif_struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags; + uint8 pal[256][4]; + uint8 lpal[256][4]; + stbi_gif_lzw codes[4096]; + uint8 *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi_gif; + +static int gif_test(stbi *s) +{ + int sz; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') return 0; + sz = get8(s); + if (sz != '9' && sz != '7') return 0; + if (get8(s) != 'a') return 0; + return 1; +} + +static int stbi_gif_test(stbi *s) +{ + int r = gif_test(s); + stbi_rewind(s); + return r; +} + +static void stbi_gif_parse_colortable(stbi *s, uint8 pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + pal[i][3] = transp ? 0 : 255; + } +} + +static int stbi_gif_header(stbi *s, stbi_gif *g, int *comp, int is_info) +{ + uint8 version; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') + return e("not GIF", "Corrupt GIF"); + + version = get8u(s); + if (version != '7' && version != '9') return e("not GIF", "Corrupt GIF"); + if (get8(s) != 'a') return e("not GIF", "Corrupt GIF"); + + failure_reason = ""; + g->w = get16le(s); + g->h = get16le(s); + g->flags = get8(s); + g->bgindex = get8(s); + g->ratio = get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi_gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi_gif_info_raw(stbi *s, int *x, int *y, int *comp) +{ + stbi_gif g; + if (!stbi_gif_header(s, &g, comp, 1)) { + stbi_rewind( s ); + return 0; + } + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi_out_gif_code(stbi_gif *g, uint16 code) +{ + uint8 *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi_out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static uint8 *stbi_process_gif_raster(stbi *s, stbi_gif *g) +{ + uint8 lzw_cs; + int32 len, code; + uint32 first; + int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi_gif_lzw *p; + + lzw_cs = get8u(s); + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (code = 0; code < clear; code++) { + g->codes[code].prefix = -1; + g->codes[code].first = (uint8) code; + g->codes[code].suffix = (uint8) code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (int32) get8(s) << valid_bits; + valid_bits += 8; + } else { + int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + skip(s, len); + while ((len = get8(s)) > 0) + skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return epuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return epuc("too many codes", "Corrupt GIF"); + p->prefix = (int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return epuc("illegal code in raster", "Corrupt GIF"); + + stbi_out_gif_code(g, (uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return epuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi_fill_gif_background(stbi_gif *g) +{ + int i; + uint8 *c = g->pal[g->bgindex]; + // @OPTIMIZE: write a dword at a time + for (i = 0; i < g->w * g->h * 4; i += 4) { + uint8 *p = &g->out[i]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static uint8 *stbi_gif_load_next(stbi *s, stbi_gif *g, int *comp, int req_comp) +{ + int i; + uint8 *old_out = 0; + + if (g->out == 0) { + if (!stbi_gif_header(s, g, comp,0)) return 0; // failure_reason set by stbi_gif_header + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + stbi_fill_gif_background(g); + } else { + // animated-gif-only path + if (((g->eflags & 0x1C) >> 2) == 3) { + old_out = g->out; + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + memcpy(g->out, old_out, g->w*g->h*4); + } + } + + for (;;) { + switch (get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int32 x, y, w, h; + uint8 *o; + + x = get16le(s); + y = get16le(s); + w = get16le(s); + h = get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return epuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi_gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (uint8 *) g->lpal; + } else if (g->flags & 0x80) { + for (i=0; i < 256; ++i) // @OPTIMIZE: reset only the previous transparent + g->pal[i][3] = 255; + if (g->transparent >= 0 && (g->eflags & 0x01)) + g->pal[g->transparent][3] = 0; + g->color_table = (uint8 *) g->pal; + } else + return epuc("missing color table", "Corrupt GIF"); + + o = stbi_process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (req_comp && req_comp != 4) + o = convert_format(o, 4, req_comp, g->w, g->h); + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (get8(s) == 0xF9) { // Graphic Control Extension. + len = get8(s); + if (len == 4) { + g->eflags = get8(s); + get16le(s); // delay + g->transparent = get8(s); + } else { + skip(s, len); + break; + } + } + while ((len = get8(s)) != 0) + skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (uint8 *) 1; + + default: + return epuc("unknown code", "Corrupt GIF"); + } + } +} + +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *u = 0; + stbi_gif g={0}; + + u = stbi_gif_load_next(s, &g, comp, req_comp); + if (u == (void *) 1) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + } + + return u; +} + +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp) +{ + return stbi_gif_info_raw(s,x,y,comp); +} + + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int hdr_test(stbi *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi_hdr_test(stbi* s) +{ + int r = hdr_test(s); + stbi_rewind(s); + return r; +} + +#define HDR_BUFLEN 1024 +static char *hdr_gettoken(stbi *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) get8(z); + + while (!at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == HDR_BUFLEN-1) { + // flush to end of line + while (!at_eof(z) && get8(z) != '\n') + ; + break; + } + c = (char) get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return epf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return epf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = strtol(token, NULL, 10); + + *x = width; + *y = height; + + *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + getn(s, rgbe, 4); + hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = get8(s); + c2 = get8(s); + len = get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + uint8 rgbe[4]; + rgbe[0] = (uint8) c1; + rgbe[1] = (uint8) c2; + rgbe[2] = (uint8) len; + rgbe[3] = (uint8) get8u(s); + hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + free(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= get8(s); + if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8u(s); + if (count > 128) { + // Run + value = get8u(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = get8u(s); + } + } + } + for (i=0; i < width; ++i) + hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + free(scanline); + } + + return hdr_data; +} + +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return hdr_load(s,x,y,comp,req_comp); +} + +static int stbi_hdr_info(stbi *s, int *x, int *y, int *comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + stbi_rewind( s ); + return 0; + } + + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi_rewind( s ); + return 0; + } + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *y = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *x = strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +static int stbi_bmp_info(stbi *s, int *x, int *y, int *comp) +{ + int hsz; + if (get8(s) != 'B' || get8(s) != 'M') { + stbi_rewind( s ); + return 0; + } + skip(s,12); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) { + stbi_rewind( s ); + return 0; + } + if (hsz == 12) { + *x = get16le(s); + *y = get16le(s); + } else { + *x = get32le(s); + *y = get32le(s); + } + if (get16le(s) != 1) { + stbi_rewind( s ); + return 0; + } + *comp = get16le(s) / 8; + return 1; +} + +static int stbi_psd_info(stbi *s, int *x, int *y, int *comp) +{ + int channelCount; + if (get32(s) != 0x38425053) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 1) { + stbi_rewind( s ); + return 0; + } + skip(s, 6); + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) { + stbi_rewind( s ); + return 0; + } + *y = get32(s); + *x = get32(s); + if (get16(s) != 8) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 3) { + stbi_rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi_pic_info(stbi *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + pic_packet_t packets[10]; + + skip(s, 92); + + *x = get16(s); + *y = get16(s); + if (at_eof(s)) return 0; + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi_rewind( s ); + return 0; + } + + skip(s, 8); + + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + act_comp |= packet->channel; + + if (at_eof(s)) { + stbi_rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi_rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} + +static int stbi_info_main(stbi *s, int *x, int *y, int *comp) +{ + if (stbi_jpeg_info(s, x, y, comp)) + return 1; + if (stbi_png_info(s, x, y, comp)) + return 1; + if (stbi_gif_info(s, x, y, comp)) + return 1; + if (stbi_bmp_info(s, x, y, comp)) + return 1; + if (stbi_psd_info(s, x, y, comp)) + return 1; + if (stbi_pic_info(s, x, y, comp)) + return 1; + #ifndef STBI_NO_HDR + if (stbi_hdr_info(s, x, y, comp)) + return 1; + #endif + // test tga last because it's a crappy test! + if (stbi_tga_info(s, x, y, comp)) + return 1; + return e("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = fopen(filename, "rb"); + int result; + if (!f) return e("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi s; + long pos = ftell(f); + start_file(&s, f); + r = stbi_info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_info_main(&s,x,y,comp); +} + +int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi_info_main(&s,x,y,comp); +} + +#endif // STBI_HEADER_FILE_ONLY + +/* + revision history: + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-uint8 to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.e. Janez (U+017D)emva) + 1.21 fix use of 'uint8' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 2008-08-02 + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 first released version +*/ diff --git a/libraries/amf/amftools-code/src/zip/unzip.cpp b/libraries/amf/amftools-code/src/zip/unzip.cpp new file mode 100644 index 00000000..04c42f13 --- /dev/null +++ b/libraries/amf/amftools-code/src/zip/unzip.cpp @@ -0,0 +1,4164 @@ +#ifdef WIN32 + +#include +#include +#include +#include +#include +#include "zip/unzip.h" + +// THIS FILE is almost entirely based upon code by Jean-loup Gailly +// and Mark Adler. It has been modified by Lucian Wischik. +// The modifications were: incorporate the bugfixes of 1.1.4, allow +// unzipping to/from handles/pipes/files/memory, encryption, unicode, +// a windowsish api, and putting everything into a single .cpp file. +// The original code may be found at http://www.gzip.org/zlib/ +// The original copyright text follows. +// +// +// +// zlib.h -- interface of the 'zlib' general purpose compression library +// version 1.1.3, July 9th, 1998 +// +// Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Jean-loup Gailly Mark Adler +// jloup@gzip.org madler@alumni.caltech.edu +// +// +// The data format used by the zlib library is described by RFCs (Request for +// Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt +// (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +// +// +// The 'zlib' compression library provides in-memory compression and +// decompression functions, including integrity checks of the uncompressed +// data. This version of the library supports only one compression method +// (deflation) but other algorithms will be added later and will have the same +// stream interface. +// +// Compression can be done in a single step if the buffers are large +// enough (for example if an input file is mmap'ed), or can be done by +// repeated calls of the compression function. In the latter case, the +// application must provide more input and/or consume the output +// (providing more output space) before each call. +// +// The library also supports reading and writing files in gzip (.gz) format +// with an interface similar to that of stdio. +// +// The library does not install any signal handler. The decoder checks +// the consistency of the compressed data, so the library should never +// crash even in case of corrupted input. +// +// for more info about .ZIP format, see ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip +// PkWare has also a specification at ftp://ftp.pkware.com/probdesc.zip + +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 + + +#define zmalloc(len) malloc(len) + +#define zfree(p) free(p) + +/* +void *zmalloc(unsigned int len) +{ char *buf = new char[len+32]; + for (int i=0; i<16; i++) + { buf[i]=i; + buf[len+31-i]=i; + } + *((unsigned int*)buf) = len; + char c[1000]; wsprintf(c,"malloc 0x%lx - %lu",buf+16,len); + OutputDebugString(c); + return buf+16; +} + +void zfree(void *buf) +{ char c[1000]; wsprintf(c,"free 0x%lx",buf); + OutputDebugString(c); + char *p = ((char*)buf)-16; + unsigned int len = *((unsigned int*)p); + bool blown=false; + for (int i=0; i<16; i++) + { char lo = p[i]; + char hi = p[len+31-i]; + if (hi!=i || (lo!=i && i>4)) blown=true; + } + if (blown) + { OutputDebugString("BLOWN!!!"); + } + delete[] p; +} +*/ + + +typedef struct tm_unz_s +{ unsigned int tm_sec; // seconds after the minute - [0,59] + unsigned int tm_min; // minutes after the hour - [0,59] + unsigned int tm_hour; // hours since midnight - [0,23] + unsigned int tm_mday; // day of the month - [1,31] + unsigned int tm_mon; // months since January - [0,11] + unsigned int tm_year; // years - [1980..2044] +} tm_unz; + + +// unz_global_info structure contain global data about the ZIPfile +typedef struct unz_global_info_s +{ unsigned long number_entry; // total number of entries in the central dir on this disk + unsigned long size_comment; // size of the global comment of the zipfile +} unz_global_info; + +// unz_file_info contain information about a file in the zipfile +typedef struct unz_file_info_s +{ unsigned long version; // version made by 2 bytes + unsigned long version_needed; // version needed to extract 2 bytes + unsigned long flag; // general purpose bit flag 2 bytes + unsigned long compression_method; // compression method 2 bytes + unsigned long dosDate; // last mod file date in Dos fmt 4 bytes + unsigned long crc; // crc-32 4 bytes + unsigned long compressed_size; // compressed size 4 bytes + unsigned long uncompressed_size; // uncompressed size 4 bytes + unsigned long size_filename; // filename length 2 bytes + unsigned long size_file_extra; // extra field length 2 bytes + unsigned long size_file_comment; // file comment length 2 bytes + unsigned long disk_num_start; // disk number start 2 bytes + unsigned long internal_fa; // internal file attributes 2 bytes + unsigned long external_fa; // external file attributes 4 bytes + tm_unz tmu_date; +} unz_file_info; + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) +#define UNZ_PASSWORD (-106) + + + + + + + +#define ZLIB_VERSION "1.1.3" + + +// Allowed flush values; see deflate() for details +#define Z_NO_FLUSH 0 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 + + +// compression levels +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) + +// compression strategy; see deflateInit2() for details +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +// Possible values of the data_type field +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 + +// The deflate compression method (the only one supported in this version) +#define Z_DEFLATED 8 + +// for initializing zalloc, zfree, opaque +#define Z_NULL 0 + +// case sensitivity when searching for filenames +#define CASE_SENSITIVE 1 +#define CASE_INSENSITIVE 2 + + +// Return codes for the compression/decompression functions. Negative +// values are errors, positive values are used for special but normal events. +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) + + + +// Basic data types +typedef unsigned char Byte; // 8 bits +typedef unsigned int uInt; // 16 bits or more +typedef unsigned long uLong; // 32 bits or more +typedef void *voidpf; +typedef void *voidp; +typedef long z_off_t; + + + + + + + + + + + + +typedef voidpf (*alloc_func) (voidpf opaque, uInt items, uInt size); +typedef void (*free_func) (voidpf opaque, voidpf address); + +struct internal_state; + +typedef struct z_stream_s { + Byte *next_in; // next input byte + uInt avail_in; // number of bytes available at next_in + uLong total_in; // total nb of input bytes read so far + + Byte *next_out; // next output byte should be put there + uInt avail_out; // remaining free space at next_out + uLong total_out; // total nb of bytes output so far + + char *msg; // last error message, NULL if no error + struct internal_state *state; // not visible by applications + + alloc_func zalloc; // used to allocate the internal state + free_func zfree; // used to free the internal state + voidpf opaque; // private data object passed to zalloc and zfree + + int data_type; // best guess about the data type: ascii or binary + uLong adler; // adler32 value of the uncompressed data + uLong reserved; // reserved for future use +} z_stream; + +typedef z_stream *z_streamp; + + +// The application must update next_in and avail_in when avail_in has +// dropped to zero. It must update next_out and avail_out when avail_out +// has dropped to zero. The application must initialize zalloc, zfree and +// opaque before calling the init function. All other fields are set by the +// compression library and must not be updated by the application. +// +// The opaque value provided by the application will be passed as the first +// parameter for calls of zalloc and zfree. This can be useful for custom +// memory management. The compression library attaches no meaning to the +// opaque value. +// +// zalloc must return Z_NULL if there is not enough memory for the object. +// If zlib is used in a multi-threaded application, zalloc and zfree must be +// thread safe. +// +// The fields total_in and total_out can be used for statistics or +// progress reports. After compression, total_in holds the total size of +// the uncompressed data and may be saved for use in the decompressor +// (particularly if the decompressor wants to decompress everything in +// a single step). +// + + +// basic functions + +const char *zlibVersion (); +// The application can compare zlibVersion and ZLIB_VERSION for consistency. +// If the first character differs, the library code actually used is +// not compatible with the zlib.h header file used by the application. +// This check is automatically made by inflateInit. + + + + + + +int inflate (z_streamp strm, int flush); +// +// inflate decompresses as much data as possible, and stops when the input +// buffer becomes empty or the output buffer becomes full. It may some +// introduce some output latency (reading input without producing any output) +// except when forced to flush. +// +// The detailed semantics are as follows. inflate performs one or both of the +// following actions: +// +// - Decompress more input starting at next_in and update next_in and avail_in +// accordingly. If not all input can be processed (because there is not +// enough room in the output buffer), next_in is updated and processing +// will resume at this point for the next call of inflate(). +// +// - Provide more output starting at next_out and update next_out and avail_out +// accordingly. inflate() provides as much output as possible, until there +// is no more input data or no more space in the output buffer (see below +// about the flush parameter). +// +// Before the call of inflate(), the application should ensure that at least +// one of the actions is possible, by providing more input and/or consuming +// more output, and updating the next_* and avail_* values accordingly. +// The application can consume the uncompressed output when it wants, for +// example when the output buffer is full (avail_out == 0), or after each +// call of inflate(). If inflate returns Z_OK and with zero avail_out, it +// must be called again after making room in the output buffer because there +// might be more output pending. +// +// If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much +// output as possible to the output buffer. The flushing behavior of inflate is +// not specified for values of the flush parameter other than Z_SYNC_FLUSH +// and Z_FINISH, but the current implementation actually flushes as much output +// as possible anyway. +// +// inflate() should normally be called until it returns Z_STREAM_END or an +// error. However if all decompression is to be performed in a single step +// (a single call of inflate), the parameter flush should be set to +// Z_FINISH. In this case all pending input is processed and all pending +// output is flushed; avail_out must be large enough to hold all the +// uncompressed data. (The size of the uncompressed data may have been saved +// by the compressor for this purpose.) The next operation on this stream must +// be inflateEnd to deallocate the decompression state. The use of Z_FINISH +// is never required, but can be used to inform inflate that a faster routine +// may be used for the single inflate() call. +// +// If a preset dictionary is needed at this point (see inflateSetDictionary +// below), inflate sets strm-adler to the adler32 checksum of the +// dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise +// it sets strm->adler to the adler32 checksum of all output produced +// so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or +// an error code as described below. At the end of the stream, inflate() +// checks that its computed adler32 checksum is equal to that saved by the +// compressor and returns Z_STREAM_END only if the checksum is correct. +// +// inflate() returns Z_OK if some progress has been made (more input processed +// or more output produced), Z_STREAM_END if the end of the compressed data has +// been reached and all uncompressed output has been produced, Z_NEED_DICT if a +// preset dictionary is needed at this point, Z_DATA_ERROR if the input data was +// corrupted (input stream not conforming to the zlib format or incorrect +// adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent +// (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not +// enough memory, Z_BUF_ERROR if no progress is possible or if there was not +// enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR +// case, the application may then call inflateSync to look for a good +// compression block. +// + + +int inflateEnd (z_streamp strm); +// +// All dynamically allocated data structures for this stream are freed. +// This function discards any unprocessed input and does not flush any +// pending output. +// +// inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state +// was inconsistent. In the error case, msg may be set but then points to a +// static string (which must not be deallocated). + + // Advanced functions + +// The following functions are needed only in some special applications. + + + + + +int inflateSetDictionary (z_streamp strm, + const Byte *dictionary, + uInt dictLength); +// +// Initializes the decompression dictionary from the given uncompressed byte +// sequence. This function must be called immediately after a call of inflate +// if this call returned Z_NEED_DICT. The dictionary chosen by the compressor +// can be determined from the Adler32 value returned by this call of +// inflate. The compressor and decompressor must use exactly the same +// dictionary. +// +// inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a +// parameter is invalid (such as NULL dictionary) or the stream state is +// inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the +// expected one (incorrect Adler32 value). inflateSetDictionary does not +// perform any decompression: this will be done by subsequent calls of +// inflate(). + + +int inflateSync (z_streamp strm); +// +// Skips invalid compressed data until a full flush point can be found, or until all +// available input is skipped. No output is provided. +// +// inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR +// if no more input was provided, Z_DATA_ERROR if no flush point has been found, +// or Z_STREAM_ERROR if the stream structure was inconsistent. In the success +// case, the application may save the current current value of total_in which +// indicates where valid compressed data was found. In the error case, the +// application may repeatedly call inflateSync, providing more input each time, +// until success or end of the input data. + + +int inflateReset (z_streamp strm); +// This function is equivalent to inflateEnd followed by inflateInit, +// but does not free and reallocate all the internal decompression state. +// The stream will keep attributes that may have been set by inflateInit2. +// +// inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source +// stream state was inconsistent (such as zalloc or state being NULL). +// + + + +// checksum functions +// These functions are not related to compression but are exported +// anyway because they might be useful in applications using the +// compression library. + +uLong adler32 (uLong adler, const Byte *buf, uInt len); +// Update a running Adler-32 checksum with the bytes buf[0..len-1] and +// return the updated checksum. If buf is NULL, this function returns +// the required initial value for the checksum. +// An Adler-32 checksum is almost as reliable as a CRC32 but can be computed +// much faster. Usage example: +// +// uLong adler = adler32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// adler = adler32(adler, buffer, length); +// } +// if (adler != original_adler) error(); + +uLong ucrc32 (uLong crc, const Byte *buf, uInt len); +// Update a running crc with the bytes buf[0..len-1] and return the updated +// crc. If buf is NULL, this function returns the required initial value +// for the crc. Pre- and post-conditioning (one's complement) is performed +// within this function so it shouldn't be done by the application. +// Usage example: +// +// uLong crc = crc32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// crc = crc32(crc, buffer, length); +// } +// if (crc != original_crc) error(); + + + + +const char *zError (int err); +int inflateSyncPoint (z_streamp z); +const uLong *get_crc_table (void); + + + +typedef unsigned char uch; +typedef uch uchf; +typedef unsigned short ush; +typedef ush ushf; +typedef unsigned long ulg; + + + +const char * const z_errmsg[10] = { // indexed by 2-zlib_error +"need dictionary", // Z_NEED_DICT 2 +"stream end", // Z_STREAM_END 1 +"", // Z_OK 0 +"file error", // Z_ERRNO (-1) +"stream error", // Z_STREAM_ERROR (-2) +"data error", // Z_DATA_ERROR (-3) +"insufficient memory", // Z_MEM_ERROR (-4) +"buffer error", // Z_BUF_ERROR (-5) +"incompatible version",// Z_VERSION_ERROR (-6) +""}; + + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +// To be used only when the state is known to be valid + + // common constants + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + +#define PRESET_DICT 0x20 // preset dictionary flag in zlib header + + // target dependencies + +#define OS_CODE 0x0b // Window 95 & Windows NT + + + + // functions + +#define zmemzero(dest, len) memset(dest, 0, len) + +// Diagnostic functions +#define LuAssert(cond,msg) +#define LuTrace(x) +#define LuTracev(x) +#define LuTracevv(x) +#define LuTracec(c,x) +#define LuTracecv(c,x) + + +typedef uLong (*check_func) (uLong check, const Byte *buf, uInt len); +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size); +void zcfree (voidpf opaque, voidpf ptr); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) + +//void ZFREE(z_streamp strm,voidpf addr) +//{ *((strm)->zfree))((strm)->opaque, addr); +//} + +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + + + + +// Huffman code lookup table entry--this entry is four bytes for machines +// that have 16-bit pointers (e.g. PC's in the small or medium model). + + +typedef struct inflate_huft_s inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; // number of extra bits or operation + Byte Bits; // number of bits in this code or subcode + } what; + uInt pad; // pad structure to a power of 2 (4 bytes for + } word; // 16-bit, 8 bytes for 32-bit int's) + uInt base; // literal, length base, distance base, or table offset +}; + +// Maximum size of dynamic tree. The maximum found in a long but non- +// exhaustive search was 1004 huft structures (850 for length/literals +// and 154 for distances, the latter actually the result of an +// exhaustive search). The actual maximum is not known, but the +// value below is more than safe. +#define MANY 1440 + +int inflate_trees_bits ( + uInt *, // 19 code lengths + uInt *, // bits tree desired/actual depth + inflate_huft * *, // bits tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_dynamic ( + uInt, // number of literal/length codes + uInt, // number of distance codes + uInt *, // that many (total) code lengths + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + inflate_huft * *, // literal/length tree result + inflate_huft * *, // distance tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_fixed ( + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + const inflate_huft * *, // literal/length tree result + const inflate_huft * *, // distance tree result + z_streamp); // for memory allocation + + + + + +struct inflate_blocks_state; +typedef struct inflate_blocks_state inflate_blocks_statef; + +inflate_blocks_statef * inflate_blocks_new ( + z_streamp z, + check_func c, // check function + uInt w); // window size + +int inflate_blocks ( + inflate_blocks_statef *, + z_streamp , + int); // initial return code + +void inflate_blocks_reset ( + inflate_blocks_statef *, + z_streamp , + uLong *); // check value on output + +int inflate_blocks_free ( + inflate_blocks_statef *, + z_streamp); + +void inflate_set_dictionary ( + inflate_blocks_statef *s, + const Byte *d, // dictionary + uInt n); // dictionary length + +int inflate_blocks_sync_point ( + inflate_blocks_statef *s); + + + + +struct inflate_codes_state; +typedef struct inflate_codes_state inflate_codes_statef; + +inflate_codes_statef *inflate_codes_new ( + uInt, uInt, + const inflate_huft *, const inflate_huft *, + z_streamp ); + +int inflate_codes ( + inflate_blocks_statef *, + z_streamp , + int); + +void inflate_codes_free ( + inflate_codes_statef *, + z_streamp ); + + + + +typedef enum { + IBM_TYPE, // get type bits (3, including end bit) + IBM_LENS, // get lengths for stored + IBM_STORED, // processing stored block + IBM_TABLE, // get table lengths + IBM_BTREE, // get bit lengths tree for a dynamic block + IBM_DTREE, // get length, distance trees for a dynamic block + IBM_CODES, // processing fixed or dynamic block + IBM_DRY, // output remaining window bytes + IBM_DONE, // finished last block, done + IBM_BAD} // got a data error--stuck here +inflate_block_mode; + +// inflate blocks semi-private state +struct inflate_blocks_state { + + // mode + inflate_block_mode mode; // current inflate_block mode + + // mode dependent information + union { + uInt left; // if STORED, bytes left to copy + struct { + uInt table; // table lengths (14 bits) + uInt index; // index into blens (or border) + uInt *blens; // bit lengths of codes + uInt bb; // bit length tree depth + inflate_huft *tb; // bit length decoding tree + } trees; // if DTREE, decoding info for trees + struct { + inflate_codes_statef + *codes; + } decode; // if CODES, current state + } sub; // submode + uInt last; // true if this block is the last block + + // mode independent information + uInt bitk; // bits in bit buffer + uLong bitb; // bit buffer + inflate_huft *hufts; // single malloc for tree space + Byte *window; // sliding window + Byte *end; // one byte after sliding window + Byte *read; // window read pointer + Byte *write; // window write pointer + check_func checkfn; // check function + uLong check; // check on output + +}; + + +// defines for inflate input/output +// update pointers and return +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=(uLong)(p-z->next_in);z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +// get bytes and bits +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +// output bytes +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;m;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +// load local pointers +#define LOAD {LOADIN LOADOUT} + +// masks for lower bits (size given to avoid silly warnings with Visual C++) +// And'ing with mask[n] masks the lower n bits +const uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +// copy as much as possible from the sliding window to the output area +int inflate_flush (inflate_blocks_statef *, z_streamp, int); + +int inflate_fast (uInt, uInt, const inflate_huft *, const inflate_huft *, inflate_blocks_statef *, z_streamp ); + + + +const uInt fixed_bl = 9; +const uInt fixed_bd = 5; +const inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +const inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; + + + + + + + +// copy as much as possible from the sliding window to the output area +int inflate_flush(inflate_blocks_statef *s,z_streamp z,int r) +{ + uInt n; + Byte *p; + Byte *q; + + // local copies of source and destination pointers + p = z->next_out; + q = s->read; + + // compute number of bytes to copy as far as end of window + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy as far as end of window + if (n!=0) // check for n!=0 to avoid waking up CodeGuard + { memcpy(p, q, n); + p += n; + q += n; + } + + // see if more to copy at beginning of window + if (q == s->end) + { + // wrap pointers + q = s->window; + if (s->write == s->end) + s->write = s->window; + + // compute bytes to copy + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy + if (n!=0) {memcpy(p,q,n); p+=n; q+=n;} + } + + // update pointers + z->next_out = p; + s->read = q; + + // done + return r; +} + + + + + + +// simplify the use of the inflate_huft type with some defines +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { // waiting for "i:"=input, "o:"=output, "x:"=nothing + START, // x: set up for LEN + LEN, // i: get length/literal/eob next + LENEXT, // i: getting length extra (have base) + DIST, // i: get distance next + DISTEXT, // i: getting distance extra + COPY, // o: copying bytes in window, waiting for space + LIT, // o: got literal, waiting for output space + WASH, // o: got eob, possibly still output waiting + END, // x: got eob and all data flushed + BADCODE} // x: got error +inflate_codes_mode; + +// inflate codes private state +struct inflate_codes_state { + + // mode + inflate_codes_mode mode; // current inflate_codes mode + + // mode dependent information + uInt len; + union { + struct { + const inflate_huft *tree; // pointer into tree + uInt need; // bits needed + } code; // if LEN or DIST, where in tree + uInt lit; // if LIT, literal + struct { + uInt get; // bits to get for extra + uInt dist; // distance back to copy from + } copy; // if EXT or COPY, where and how much + } sub; // submode + + // mode independent information + Byte lbits; // ltree bits decoded per branch + Byte dbits; // dtree bits decoder per branch + const inflate_huft *ltree; // literal/length/eob tree + const inflate_huft *dtree; // distance tree + +}; + + +inflate_codes_statef *inflate_codes_new( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +z_streamp z) +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + LuTracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt j; // temporary storage + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + Byte *f; // pointer to copy strings from + inflate_codes_statef *c = s->sub.decode.codes; // codes state + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input and output based on current state + for(;;) switch (c->mode) + { // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif // !SLOW + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: // i: get length/literal/eob next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) // literal + { + c->sub.lit = t->base; + LuTracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) // length + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) // end of block + { + LuTracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: // i: getting length extra (have base) + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + LuTracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: // i: get distance next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) // distance + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: // i: getting distance extra + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + LuTracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = q - c->sub.copy.dist; + while (f < s->window) // modulo window size-"while" instead + f += s->end - s->window; // of "if" handles invalid distances + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: // o: got literal, waiting for output space + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: // o: got eob, possibly more output + if (k > 7) // return unused byte, if any + { + //Assert(k < 16, "inflate_codes grabbed too many bytes") + k -= 8; + n++; + p--; // can always return one + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: // x: got error + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +void inflate_codes_free(inflate_codes_statef *c,z_streamp z) +{ ZFREE(z, c); + LuTracev((stderr, "inflate: codes free\n")); +} + + + +// infblock.c -- interpret and process block types to last block +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + + +// Table for deflate from PKZIP's appnote.txt. +const uInt border[] = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +// +// Notes beyond the 1.93a appnote.txt: +// +// 1. Distance pointers never point before the beginning of the output stream. +// 2. Distance pointers can point back across blocks, up to 32k away. +// 3. There is an implied maximum of 7 bits for the bit length table and +// 15 bits for the actual data. +// 4. If only one code exists, then it is encoded using one bit. (Zero +// would be more efficient, but perhaps a little confusing.) If two +// codes exist, they are coded using one bit each (0 and 1). +// 5. There is no way of sending zero distance codes--a dummy must be +// sent if there are none. (History: a pre 2.0 version of PKZIP would +// store blocks with no distance codes, but this was discovered to be +// too harsh a criterion.) Valid only for 1.93a. 2.04c does allow +// zero distance codes, which is sent as one code of zero bits in +// length. +// 6. There are up to 286 literal/length codes. Code 256 represents the +// end-of-block. Note however that the static length tree defines +// 288 codes just to fill out the Huffman codes. Codes 286 and 287 +// cannot be used though, since there is no length base or extra bits +// defined for them. Similarily, there are up to 30 distance codes. +// However, static trees define 32 codes (all 5 bits) to fill out the +// Huffman codes, but the last two had better not show up in the data. +// 7. Unzip can check dynamic Huffman blocks for complete code sets. +// The exception is that a single code would not be complete (see #4). +// 8. The five bits following the block type is really the number of +// literal codes sent minus 257. +// 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits +// (1+6+6). Therefore, to output three times the length, you output +// three codes (1+1+1), whereas to output four times the same length, +// you only need two codes (1+3). Hmm. +//10. In the tree reconstruction algorithm, Code = Code + Increment +// only if BitLength(i) is not zero. (Pretty obvious.) +//11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) +//12. Note: length code 284 can represent 227-258, but length code 285 +// really is 258. The last length deserves its own, short code +// since it gets used a lot in very redundant files. The length +// 258 is special since 258 - 3 (the min match length) is 255. +//13. The literal/length and distance code bit lengths are read as a +// single stream of lengths. It is possible (and advantageous) for +// a repeat code (16, 17, or 18) to go across the boundary between +// the two sets of lengths. + + +void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == IBM_BTREE || s->mode == IBM_DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == IBM_CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = IBM_TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); + LuTracev((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = IBM_TYPE; + LuTracev((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt t; // temporary storage + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input based on current state + for(;;) switch (s->mode) + { + case IBM_TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: // stored + LuTracev((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; // go to byte boundary + DUMPBITS(t) + s->mode = IBM_LENS; // get length of stored block + break; + case 1: // fixed + LuTracev((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + const inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = IBM_CODES; + break; + case 2: // dynamic + LuTracev((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = IBM_TABLE; + break; + case 3: // illegal + DUMPBITS(3) + s->mode = IBM_BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case IBM_LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = IBM_BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; // dump bits + LuTracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? IBM_STORED : (s->last ? IBM_DRY : IBM_TYPE); + break; + case IBM_STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + memcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + LuTracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? IBM_DRY : IBM_TYPE; + break; + case IBM_TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; + // remove this section to workaround bug in pkzip + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = IBM_BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } + // end remove + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + LuTracev((stderr, "inflate: table sizes ok\n")); + s->mode = IBM_BTREE; + case IBM_BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + r = t; + if (r == Z_DATA_ERROR) + { + ZFREE(z, s->sub.trees.blens); + s->mode = IBM_BAD; + } + LEAVE + } + s->sub.trees.index = 0; + LuTracev((stderr, "inflate: bits tree ok\n")); + s->mode = IBM_DTREE; + case IBM_DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else // c == 16..18 + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = IBM_BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; // must be <= 9 for lookahead assumptions + bd = 6; // must be <= 9 for lookahead assumptions + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + { + ZFREE(z, s->sub.trees.blens); + s->mode = IBM_BAD; + } + r = t; + LEAVE + } + LuTracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + ZFREE(z, s->sub.trees.blens); + s->mode = IBM_CODES; + case IBM_CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + LuTracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = IBM_TYPE; + break; + } + s->mode = IBM_DRY; + case IBM_DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = IBM_DONE; + case IBM_DONE: + r = Z_STREAM_END; + LEAVE + case IBM_BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + LuTracev((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + + +// inftrees.c -- generate Huffman trees for efficient decoding +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + + +extern const char inflate_copyright[] = + " inflate 1.1.3 Copyright 1995-1998 Mark Adler "; +// If you use the zlib library in a product, an acknowledgment is welcome +// in the documentation of your product. If for some reason you cannot +// include such an acknowledgment, I would appreciate that you keep this +// copyright string in the executable of your product. + + + +int huft_build ( + uInt *, // code lengths in bits + uInt, // number of codes + uInt, // number of "simple" codes + const uInt *, // list of base values for non-simple codes + const uInt *, // list of extra bits for non-simple codes + inflate_huft **,// result: starting table + uInt *, // maximum lookup bits (returns actual) + inflate_huft *, // space for trees + uInt *, // hufts used in space + uInt * ); // space for values + +// Tables for deflate from PKZIP's appnote.txt. +const uInt cplens[31] = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + // see note #13 above about 258 +const uInt cplext[31] = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; // 112==invalid +const uInt cpdist[30] = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +const uInt cpdext[30] = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +// +// Huffman code decoding is performed using a multi-level table lookup. +// The fastest way to decode is to simply build a lookup table whose +// size is determined by the longest code. However, the time it takes +// to build this table can also be a factor if the data being decoded +// is not very long. The most common codes are necessarily the +// shortest codes, so those codes dominate the decoding time, and hence +// the speed. The idea is you can have a shorter table that decodes the +// shorter, more probable codes, and then point to subsidiary tables for +// the longer codes. The time it costs to decode the longer codes is +// then traded against the time it takes to make longer tables. +// +// This results of this trade are in the variables lbits and dbits +// below. lbits is the number of bits the first level table for literal/ +// length codes can decode in one step, and dbits is the same thing for +// the distance codes. Subsequent tables are also less than or equal to +// those sizes. These values may be adjusted either when all of the +// codes are shorter than that, in which case the longest code length in +// bits is used, or when the shortest code is *longer* than the requested +// table size, in which case the length of the shortest code in bits is +// used. +// +// There are two different values for the two tables, since they code a +// different number of possibilities each. The literal/length table +// codes 286 possible values, or in a flat code, a little over eight +// bits. The distance table codes 30 possible values, or a little less +// than five bits, flat. The optimum values for speed end up being +// about one bit more than those, so lbits is 8+1 and dbits is 5+1. +// The optimum values may differ though from machine to machine, and +// possibly even between compilers. Your mileage may vary. +// + + +// If BMAX needs to be larger than 16, then h and x[] should be uLong. +#define BMAX 15 // maximum bit length of any code + +int huft_build( +uInt *b, // code lengths in bits (all assumed <= BMAX) +uInt n, // number of codes (assumed <= 288) +uInt s, // number of simple-valued codes (0..s-1) +const uInt *d, // list of base values for non-simple codes +const uInt *e, // list of extra bits for non-simple codes +inflate_huft * *t, // result: starting table +uInt *m, // maximum lookup bits, returns actual +inflate_huft *hp, // space for trees +uInt *hn, // hufts used in space +uInt *v) // working area: values in order of bit length +// Given a list of code lengths and a maximum table size, make a set of +// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR +// if the given code set is incomplete (the tables are still built in this +// case), or Z_DATA_ERROR if the input is invalid. +{ + + uInt a; // counter for codes of length k + uInt c[BMAX+1]; // bit length count table + uInt f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + register uInt i; // counter, current code + register uInt j; // counter + register int k; // number of bits in current code + int l; // bits per table (returned in m) + uInt mask; // (1 << w) - 1, to avoid cc -O bug on HP + register uInt *p; // pointer into c[], b[], or v[] + inflate_huft *q; // points to current table + struct inflate_huft_s r; // table entry for structure assignment + inflate_huft *u[BMAX]; // table stack + register int w; // bits before this table == (l * h) + uInt x[BMAX+1]; // bit offsets, then code stack + uInt *xp; // pointer into x + int y; // number of dummy codes added + uInt z; // number of entries in current table + + + // Generate counts for each bit length + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4; p; // clear c[]--assume BMAX+1 is 16 + p = b; i = n; + do { + c[*p++]++; // assume all entries <= BMAX + } while (--i); + if (c[0] == n) // null input--all zero length codes + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + // Find minimum and maximum length, bound *m by those + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; // minimum code length + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; // maximum code length + if ((uInt)l > i) + l = i; + *m = l; + + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { // note that i == g from above + *xp++ = (j += *p++); + } + + + // Make a table of values in order of bit lengths + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; // set n to length of v + + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = v; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = (inflate_huft *)Z_NULL; // just to keep compilers happy + q = (inflate_huft *)Z_NULL; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + + // compute minimum size table less than or equal to l bits + z = g - w; + z = z > (uInt)l ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) // try a k-w bit table + { // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = c + k; + if (j < z) + while (++j < z) // try smaller tables up to z bits + { + if ((f <<= 1) <= *++xp) + break; // enough codes to use up j bits + f -= *xp; // else deduct codes from patterns + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (*hn + z > MANY) // (note: doesn't matter for fixed) + return Z_DATA_ERROR; // overflow of MANY + u[h] = q = hp + *hn; + *hn += z; + + // connect to last table, if there is one + if (h) + { + x[h] = i; // save pattern for backing up + r.bits = (Byte)l; // bits to dump before this table + r.exop = (Byte)j; // bits in this table + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); // offset to this table + u[h-1][j] = r; // connect to last table + } + else + *t = q; // first table is returned result + } + + // set up table entry in r + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; // out of values--invalid code + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); // 256 is end-of-block + r.base = *p++; // simple code is just the value + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);// non-simple--look up in lists + r.base = d[*p++ - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + + + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits( +uInt *c, // 19 code lengths +uInt *bb, // bits tree desired/actual depth +inflate_huft * *tb, // bits tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +int inflate_trees_dynamic( +uInt nl, // number of literal/length codes +uInt nd, // number of distance codes +uInt *c, // that many (total) code lengths +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +inflate_huft * *tl, // literal/length tree result +inflate_huft * *td, // distance tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + // allocate work area + if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + // build literal/length tree + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // build distance tree + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // done + ZFREE(z, v); + return Z_OK; +} + + + + + +int inflate_trees_fixed( +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +const inflate_huft * * tl, // literal/length tree result +const inflate_huft * *td, // distance tree result +z_streamp ) // for memory allocation +{ + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +// inffast.c -- process literals and length/distance pairs fast +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + +// macros for bit input with no checking and for returning unused bytes +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} + +// Called with number of bytes left to write in window at least 258 +// (the maximum string length) and number of input bytes available +// at least ten. The ten bytes are six bytes for the longest length/ +// distance pair plus four bytes for overloading the bit buffer. + +int inflate_fast( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +inflate_blocks_statef *s, +z_streamp z) +{ + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + uInt ml; // mask for literal/length tree + uInt md; // mask for distance tree + uInt c; // bytes to copy + uInt d; // distance back to copy from + Byte *r; // copy source pointer + + // load input, output, bit values + LOAD + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do { // assume called with m >= 258 && n >= 10 + // get literal/length code + GRABBITS(20) // max bits for literal/length code + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + LuTracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits for length + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + LuTracevv((stderr, "inflate: * length %u\n", c)); + + // decode distance base of block to copy + GRABBITS(15); // max bits for distance code + e = (t = td + ((uInt)b & md))->exop; + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits to add to distance base + e &= 15; + GRABBITS(e) // get extra bits (up to 13) + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + LuTracevv((stderr, "inflate: * distance %u\n", d)); + + // do the copy + m -= c; + r = q - d; + if (r < s->window) // wrap if needed + { + do { + r += s->end - s->window; // force pointer in window + } while (r < s->window); // covers invalid distances + e = (uInt) (s->end - r); + if (c > e) + { + c -= e; // wrapped copy + do { + *q++ = *r++; + } while (--e); + r = s->window; + do { + *q++ = *r++; + } while (--c); + } + else // normal copy + { + *q++ = *r++; c--; + *q++ = *r++; c--; + do { + *q++ = *r++; + } while (--c); + } + } + else /* normal copy */ + { + *q++ = *r++; c--; + *q++ = *r++; c--; + do { + *q++ = *r++; + } while (--c); + } + break; + } + else if ((e & 64) == 0) + { + t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop; + } + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + break; + } + if ((e & 64) == 0) + { + t += t->base; + if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + LuTracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + LuTracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + } while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + UNGRAB + UPDATE + return Z_OK; +} + + + + + + +// crc32.c -- compute the CRC-32 of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + + + + + +// Table of CRC-32's of all single-byte values (made by make_crc_table) +const uLong crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +const uLong * get_crc_table() +{ return (const uLong *)crc_table; +} + +#define CRC_DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define CRC_DO2(buf) CRC_DO1(buf); CRC_DO1(buf); +#define CRC_DO4(buf) CRC_DO2(buf); CRC_DO2(buf); +#define CRC_DO8(buf) CRC_DO4(buf); CRC_DO4(buf); + +uLong ucrc32(uLong crc, const Byte *buf, uInt len) +{ if (buf == Z_NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {CRC_DO8(buf); len -= 8;} + if (len) do {CRC_DO1(buf);} while (--len); + return crc ^ 0xffffffffL; +} + + + +// ============================================================= +// some decryption routines +#define CRC32(c, b) (crc_table[((int)(c)^(b))&0xff]^((c)>>8)) +void Uupdate_keys(unsigned long *keys, char c) +{ keys[0] = CRC32(keys[0],c); + keys[1] += keys[0] & 0xFF; + keys[1] = keys[1]*134775813L +1; + keys[2] = CRC32(keys[2], keys[1] >> 24); +} +char Udecrypt_byte(unsigned long *keys) +{ unsigned temp = ((unsigned)keys[2] & 0xffff) | 2; + return (char)(((temp * (temp ^ 1)) >> 8) & 0xff); +} +char zdecode(unsigned long *keys, char c) +{ c^=Udecrypt_byte(keys); + Uupdate_keys(keys,c); + return c; +} + + + +// adler32.c -- compute the Adler-32 checksum of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + +#define BASE 65521L // largest prime smaller than 65536 +#define NMAX 5552 +// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + +#define AD_DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define AD_DO2(buf,i) AD_DO1(buf,i); AD_DO1(buf,i+1); +#define AD_DO4(buf,i) AD_DO2(buf,i); AD_DO2(buf,i+2); +#define AD_DO8(buf,i) AD_DO4(buf,i); AD_DO4(buf,i+4); +#define AD_DO16(buf) AD_DO8(buf,0); AD_DO8(buf,8); + +// ========================================================================= +uLong adler32(uLong adler, const Byte *buf, uInt len) +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + AD_DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} + + + +// zutil.c -- target dependent utility functions for the compression library +// Copyright (C) 1995-1998 Jean-loup Gailly. +// For conditions of distribution and use, see copyright notice in zlib.h +// @(#) $Id$ + + + + + + +const char * zlibVersion() +{ + return ZLIB_VERSION; +} + +// exported to allow conversion of error code to string for compress() and +// uncompress() +const char * zError(int err) +{ return ERR_MSG(err); +} + + + + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) items += size - size; // make compiler happy + return (voidpf)calloc(items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + zfree(ptr); + if (opaque) return; // make compiler happy +} + + + +// inflate.c -- zlib interface to inflate modules +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_blocks_state {int dummy;}; // for buggy compilers + +typedef enum { + IM_METHOD, // waiting for method byte + IM_FLAG, // waiting for flag byte + IM_DICT4, // four dictionary check bytes to go + IM_DICT3, // three dictionary check bytes to go + IM_DICT2, // two dictionary check bytes to go + IM_DICT1, // one dictionary check byte to go + IM_DICT0, // waiting for inflateSetDictionary + IM_BLOCKS, // decompressing blocks + IM_CHECK4, // four check bytes to go + IM_CHECK3, // three check bytes to go + IM_CHECK2, // two check bytes to go + IM_CHECK1, // one check byte to go + IM_DONE, // finished check, done + IM_BAD} // got an error--stay here +inflate_mode; + +// inflate private state +struct internal_state { + + // mode + inflate_mode mode; // current inflate mode + + // mode dependent information + union { + uInt method; // if IM_FLAGS, method byte + struct { + uLong was; // computed check value + uLong need; // stream check value + } check; // if CHECK, check values to compare + uInt marker; // if IM_BAD, inflateSync's marker bytes count + } sub; // submode + + // mode independent information + int nowrap; // flag for no wrapper + uInt wbits; // log2(window size) (8..15, defaults to 15) + inflate_blocks_statef + *blocks; // current inflate_blocks state + +}; + +int inflateReset(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? IM_BLOCKS : IM_METHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + LuTracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int inflateEnd(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + LuTracev((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z_streamp z) +{ const char *version = ZLIB_VERSION; int stream_size = sizeof(z_stream); + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != sizeof(z_stream)) return Z_VERSION_ERROR; + + int w = -15; // MAX_WBITS: 32K LZ77 window. + // Warning: reducing MAX_WBITS makes minigzip unable to extract .gz files created by gzip. + // The memory requirements for deflate are (in bytes): + // (1 << (windowBits+2)) + (1 << (memLevel+9)) + // that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + // plus a few kilobytes for small objects. For example, if you want to reduce + // the default memory requirements from 256K to 128K, compile with + // make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + // Of course this will generally degrade compression (there's no free lunch). + // + // The memory requirements for inflate are (in bytes) 1 << windowBits + // that is, 32K for windowBits=15 (default value) plus a few kilobytes + // for small objects. + + // initialize state + if (z == Z_NULL) return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; + if ((z->state = (struct internal_state *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + // handle undocumented nowrap option (no zlib header or check) + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + // create inflate_blocks state + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + LuTracev((stderr, "inflate: allocated\n")); + + // reset state + inflateReset(z); + return Z_OK; +} + + + +#define IM_NEEDBYTE {if(z->avail_in==0)return r;r=f;} +#define IM_NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z_streamp z, int f) +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + for (;;) switch (z->state->mode) + { + case IM_METHOD: + IM_NEEDBYTE + if (((z->state->sub.method = IM_NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = IM_BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = IM_BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + z->state->mode = IM_FLAG; + case IM_FLAG: + IM_NEEDBYTE + b = IM_NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + LuTracev((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = IM_BLOCKS; + break; + } + z->state->mode = IM_DICT4; + case IM_DICT4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_DICT3; + case IM_DICT3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_DICT2; + case IM_DICT2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_DICT1; + case IM_DICT1: + IM_NEEDBYTE; r; + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = IM_DICT0; + return Z_NEED_DICT; + case IM_DICT0: + z->state->mode = IM_BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case IM_BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = IM_BAD; + z->state->sub.marker = 0; // can try inflateSync + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = IM_DONE; + break; + } + z->state->mode = IM_CHECK4; + case IM_CHECK4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_CHECK3; + case IM_CHECK3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_CHECK2; + case IM_CHECK2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_CHECK1; + case IM_CHECK1: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + LuTracev((stderr, "inflate: zlib check ok\n")); + z->state->mode = IM_DONE; + case IM_DONE: + return Z_STREAM_END; + case IM_BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +} + + + + + +// unzip.c -- IO on .zip files using zlib +// Version 0.15 beta, Mar 19th, 1998, +// Read unzip.h for more info + + + + +#define UNZ_BUFSIZE (16384) +#define UNZ_MAXFILENAMEINZIP (256) +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = " unzip 0.15 Copyright 1998 Gilles Vollant "; + +// unz_file_info_interntal contain internal info about a file in zipfile +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;// relative offset of local header 4 bytes +} unz_file_info_internal; + + +typedef struct +{ bool is_handle; // either a handle or memory + bool canseek; + // for handles: + HANDLE h; bool herr; unsigned long initial_offset; bool mustclosehandle; + // for memory: + void *buf; unsigned int len,pos; // if it's a memory block +} LUFILE; + + +LUFILE *lufopen(void *z,unsigned int len,DWORD flags,ZRESULT *err) +{ if (flags!=ZIP_HANDLE && flags!=ZIP_FILENAME && flags!=ZIP_MEMORY) {*err=ZR_ARGS; return NULL;} + // + HANDLE h=0; bool canseek=false; *err=ZR_OK; + bool mustclosehandle=false; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { if (flags==ZIP_HANDLE) + { HANDLE hf = z; + h=hf; mustclosehandle=false; +#ifdef DuplicateHandle + BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&h,0,FALSE,DUPLICATE_SAME_ACCESS); + if (!res) mustclosehandle=true; +#endif + } + else + { h=CreateFile((const TCHAR*)z,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); + if (h==INVALID_HANDLE_VALUE) {*err=ZR_NOFILE; return NULL;} + mustclosehandle=true; + } + // test if we can seek on it. We can't use GetFileType(h)==FILE_TYPE_DISK since it's not on CE. + DWORD res = SetFilePointer(h,0,0,FILE_CURRENT); + canseek = (res!=0xFFFFFFFF); + } + LUFILE *lf = new LUFILE; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { lf->is_handle=true; lf->mustclosehandle=mustclosehandle; + lf->canseek=canseek; + lf->h=h; lf->herr=false; + lf->initial_offset=0; + if (canseek) lf->initial_offset = SetFilePointer(h,0,NULL,FILE_CURRENT); + } + else + { lf->is_handle=false; + lf->canseek=true; + lf->mustclosehandle=false; + lf->buf=z; lf->len=len; lf->pos=0; lf->initial_offset=0; + } + *err=ZR_OK; + return lf; +} + + +int lufclose(LUFILE *stream) +{ if (stream==NULL) return EOF; + if (stream->mustclosehandle) CloseHandle(stream->h); + delete stream; + return 0; +} + +int luferror(LUFILE *stream) +{ if (stream->is_handle && stream->herr) return 1; + else return 0; +} + +long int luftell(LUFILE *stream) +{ if (stream->is_handle && stream->canseek) return SetFilePointer(stream->h,0,NULL,FILE_CURRENT)-stream->initial_offset; + else if (stream->is_handle) return 0; + else return stream->pos; +} + +int lufseek(LUFILE *stream, long offset, int whence) +{ if (stream->is_handle && stream->canseek) + { if (whence==SEEK_SET) SetFilePointer(stream->h,stream->initial_offset+offset,0,FILE_BEGIN); + else if (whence==SEEK_CUR) SetFilePointer(stream->h,offset,NULL,FILE_CURRENT); + else if (whence==SEEK_END) SetFilePointer(stream->h,offset,NULL,FILE_END); + else return 19; // EINVAL + return 0; + } + else if (stream->is_handle) return 29; // ESPIPE + else + { if (whence==SEEK_SET) stream->pos=offset; + else if (whence==SEEK_CUR) stream->pos+=offset; + else if (whence==SEEK_END) stream->pos=stream->len+offset; + return 0; + } +} + + +size_t lufread(void *ptr,size_t size,size_t n,LUFILE *stream) +{ unsigned int toread = (unsigned int)(size*n); + if (stream->is_handle) + { DWORD red; BOOL res = ReadFile(stream->h,ptr,toread,&red,NULL); + if (!res) stream->herr=true; + return red/size; + } + if (stream->pos+toread > stream->len) toread = stream->len-stream->pos; + memcpy(ptr, (char*)stream->buf + stream->pos, toread); DWORD red = toread; + stream->pos += red; + return red/size; +} + + + + +// file_in_zip_read_info_s contain internal information about a file in zipfile, +// when reading and decompress it +typedef struct +{ + char *read_buffer; // internal buffer for compressed data + z_stream stream; // zLib stream structure for inflate + + uLong pos_in_zipfile; // position in byte on the zipfile, for fseek + uLong stream_initialised; // flag set if stream structure is initialised + + uLong offset_local_extrafield;// offset of the local extra field + uInt size_local_extrafield;// size of the local extra field + uLong pos_local_extrafield; // position in the local extra field in read + + uLong crc32; // crc32 of all data uncompressed + uLong crc32_wait; // crc32 we must obtain after decompress all + uLong rest_read_compressed; // number of byte to be decompressed + uLong rest_read_uncompressed;//number of byte to be obtained after decomp + LUFILE* file; // io structore of the zipfile + uLong compression_method; // compression method (0==store) + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) + bool encrypted; // is it encrypted? + unsigned long keys[3]; // decryption keys, initialized by unzOpenCurrentFile + int encheadleft; // the first call(s) to unzReadCurrentFile will read this many encryption-header bytes first + char crcenctest; // if encrypted, we'll check the encryption buffer against this +} file_in_zip_read_info_s; + + +// unz_s contain internal information about the zipfile +typedef struct +{ + LUFILE* file; // io structore of the zipfile + unz_global_info gi; // public global information + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) + uLong num_file; // number of the current file in the zipfile + uLong pos_in_central_dir; // pos of the current file in the central dir + uLong current_file_ok; // flag about the usability of the current file + uLong central_pos; // position of the beginning of the central dir + + uLong size_central_dir; // size of the central directory + uLong offset_central_dir; // offset of start of central directory with respect to the starting disk number + + unz_file_info cur_file_info; // public info about the current file in zip + unz_file_info_internal cur_file_info_internal; // private info about it + file_in_zip_read_info_s* pfile_in_zip_read; // structure about the current file if we are decompressing it +} unz_s, *unzFile; + + +int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity); +// Compare two filename (fileName1,fileName2). + +z_off_t unztell (unzFile file); +// Give the current position in uncompressed data + +int unzeof (unzFile file); +// return 1 if the end of file was reached, 0 elsewhere + +int unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len); +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// +// if buf==NULL, it return the size of the local extra field +// +// if buf!=NULL, len is the size of the buffer, the extra header is copied in +// buf. +// the return value is the number of bytes copied in buf, or (if <0) +// the error code + + + +// =========================================================================== +// Read a byte from a gz_stream; update next_in and avail_in. Return EOF +// for end of file. +// IN assertion: the stream s has been sucessfully opened for reading. + +int unzlocal_getByte(LUFILE *fin,int *pi) +{ unsigned char c; + int err = (int)lufread(&c, 1, 1, fin); + if (err==1) + { *pi = (int)c; + return UNZ_OK; + } + else + { if (luferror(fin)) return UNZ_ERRNO; + else return UNZ_EOF; + } +} + + +// =========================================================================== +// Reads a long in LSB order from the given gz_stream. Sets +int unzlocal_getShort (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +int unzlocal_getLong (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +// My own strcmpi / strcasecmp +int strcmpcasenosensitive_internal (const char* fileName1,const char *fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= (char)0x20; + if ((c2>='a') && (c2<='z')) + c2 -= (char)0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + + + +// +// Compare two filename (fileName1,fileName2). +// If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) +// If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) +// +int unzStringFileNameCompare (const char*fileName1,const char*fileName2,int iCaseSensitivity) +{ if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); + else return strcmpcasenosensitive_internal(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + + +// Locate the Central directory of a zipfile (at the end, just before +// the global comment). Lu bugfix 2005.07.26 - returns 0xFFFFFFFF if not found, +// rather than 0, since 0 is a valid central-dir-location for an empty zipfile. +uLong unzlocal_SearchCentralDir(LUFILE *fin) +{ if (lufseek(fin,0,SEEK_END) != 0) return 0xFFFFFFFF; + uLong uSizeFile = luftell(fin); + + uLong uMaxBack=0xffff; // maximum size of global comment + if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; + + unsigned char *buf = (unsigned char*)zmalloc(BUFREADCOMMENT+4); + if (buf==NULL) return 0xFFFFFFFF; + uLong uPosFound=0xFFFFFFFF; + + uLong uBackRead = 4; + while (uBackReaduMaxBack) uBackRead = uMaxBack; + else uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (lufseek(fin,uReadPos,SEEK_SET)!=0) break; + if (lufread(buf,(uInt)uReadSize,1,fin)!=1) break; + for (i=(int)uReadSize-3; (i--)>=0;) + { if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { uPosFound = uReadPos+i; break; + } + } + if (uPosFound!=0) break; + } + if (buf) zfree(buf); + return uPosFound; +} + + +int unzGoToFirstFile (unzFile file); +int unzCloseCurrentFile (unzFile file); + +// Open a Zip file. +// If the zipfile cannot be opened (file don't exist or in not valid), return NULL. +// Otherwise, the return value is a unzFile Handle, usable with other unzip functions +unzFile unzOpenInternal(LUFILE *fin) +{ if (fin==NULL) return NULL; + if (unz_copyright[0]!=' ') {lufclose(fin); return NULL;} + + int err=UNZ_OK; + unz_s us; + uLong central_pos,uL; + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0xFFFFFFFF) err=UNZ_ERRNO; + if (lufseek(fin,central_pos,SEEK_SET)!=0) err=UNZ_ERRNO; + // the signature, already checked + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) err=UNZ_ERRNO; + // number of this disk + uLong number_disk; // number of the current dist, used for spanning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; + // number of the disk with the start of the central directory + uLong number_disk_with_CD; // number the the disk with central dir, used for spaning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir on this disk + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir + uLong number_entry_CD; // total number of entries in the central dir (same than number_entry on nospan) + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; + if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; + // size of the central directory + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // offset of start of central directory with respect to the starting disk number + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // zipfile comment length + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; + if ((central_pos+fin->initial_offsetinitial_offset - (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + fin->initial_offset = 0; // since the zipfile itself is expected to handle this + + unz_s *s = (unz_s*)zmalloc(sizeof(unz_s)); + *s=us; + unzGoToFirstFile((unzFile)s); + return (unzFile)s; +} + + + +// Close a ZipFile opened with unzipOpen. +// If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), +// these files MUST be closed with unzipCloseCurrentFile before call unzipClose. +// return UNZ_OK if there is no problem. +int unzClose (unzFile file) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + lufclose(s->file); + if (s) zfree(s); // unused s=0; + return UNZ_OK; +} + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +// Translate date/time from Dos format to tm_unz (readable more easilty) +void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +// Get Info about the current file in the zipfile, with internal only info +int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize); + +int unzlocal_GetCurrentFileInfoInternal (unzFile file, unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, char *szFileName, + uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (lufseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + // we check the magic + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (lufread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (lufread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + {} // unused lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (lufread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + //unused lSeek+=file_info.size_file_comment - uSizeRead; + } + else {} //unused lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, + char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, szComment,commentBufferSize); +} + + +// Set the current file of the zipfile to the first file. +// return UNZ_OK if there is no problem +int unzGoToFirstFile (unzFile file) +{ + int err; + unz_s* s; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Set the current file of the zipfile to the next file. +// return UNZ_OK if there is no problem +// return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +int unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Try locate the file szFileName in the zipfile. +// For the iCaseSensitivity signification, see unzStringFileNameCompare +// return value : +// UNZ_OK if the file is found. It becomes the current file. +// UNZ_END_OF_LIST_OF_FILE if the file is not found +int unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz_s* s; + int err; + + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName,szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +// Read the local header of the current zipfile +// Check the coherency of the local header and info in the end of central +// directory about this file +// store in *piSizeVar the size of extra info in local header +// (filename and size of extra field data) +int unzlocal_CheckCurrentFileCoherencyHeader (unz_s *s,uInt *piSizeVar, + uLong *poffset_local_extrafield, uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (lufseek(s->file,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +// else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) +// err=UNZ_BADZIPFILE; + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // date/time + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // crc + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size compr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size uncompr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + + + + + +// Open for reading data the current file in the zipfile. +// If there is no error and the file is opened, the return value is UNZ_OK. +int unzOpenCurrentFile (unzFile file, const char *password) +{ + int err; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; // offset of the local extra field + uInt size_local_extrafield; // size of the local extra field + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*)zmalloc(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)zmalloc(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); //unused pfile_in_zip_read_info=0; + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) + { // unused err=UNZ_BADZIPFILE; + } + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + // windowBits is passed < 0 to tell that there is no zlib header. + // Note that in this case inflate *requires* an extra "dummy" byte + // after the compressed stream in order to complete decompression and + // return Z_STREAM_END. + // In unzip, i don't wait absolutely Z_STREAM_END because I known the + // size of both compressed and uncompressed data + } + pfile_in_zip_read_info->rest_read_compressed = s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = s->cur_file_info.uncompressed_size ; + pfile_in_zip_read_info->encrypted = (s->cur_file_info.flag&1)!=0; + bool extlochead = (s->cur_file_info.flag&8)!=0; + if (extlochead) pfile_in_zip_read_info->crcenctest = (char)((s->cur_file_info.dosDate>>8)&0xff); + else pfile_in_zip_read_info->crcenctest = (char)(s->cur_file_info.crc >> 24); + pfile_in_zip_read_info->encheadleft = (pfile_in_zip_read_info->encrypted?12:0); + pfile_in_zip_read_info->keys[0] = 305419896L; + pfile_in_zip_read_info->keys[1] = 591751049L; + pfile_in_zip_read_info->keys[2] = 878082192L; + for (const char *cp=password; cp!=0 && *cp!=0; cp++) Uupdate_keys(pfile_in_zip_read_info->keys,*cp); + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + + return UNZ_OK; +} + + +// Read bytes from the current file. +// buf contain buffer where data must be copied +// len the size of buf. +// return the number of byte copied if somes bytes are copied (and also sets *reached_eof) +// return 0 if the end of file was reached. (and also sets *reached_eof). +// return <0 with error code if there is an error. (in which case *reached_eof is meaningless) +// (UNZ_ERRNO for IO error, or zLib error for uncompress error) +int unzReadCurrentFile (unzFile file, voidp buf, unsigned len, bool *reached_eof) +{ int err=UNZ_OK; + uInt iRead = 0; + if (reached_eof!=0) *reached_eof=false; + + unz_s *s = (unz_s*)file; + if (s==NULL) return UNZ_PARAMERROR; + + file_in_zip_read_info_s* pfile_in_zip_read_info = s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; + if ((pfile_in_zip_read_info->read_buffer == NULL)) return UNZ_END_OF_LIST_OF_FILE; + if (len==0) return 0; + + pfile_in_zip_read_info->stream.next_out = (Byte*)buf; + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + { pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + } + + while (pfile_in_zip_read_info->stream.avail_out>0) + { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) + { uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) {if (reached_eof!=0) *reached_eof=true; return UNZ_EOF;} + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; + if (lufread(pfile_in_zip_read_info->read_buffer,uReadThis,1,pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + pfile_in_zip_read_info->stream.next_in = (Byte*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + // + if (pfile_in_zip_read_info->encrypted) + { char *buf = (char*)pfile_in_zip_read_info->stream.next_in; + for (unsigned int i=0; ikeys,buf[i]); + } + } + + unsigned int uDoEncHead = pfile_in_zip_read_info->encheadleft; + if (uDoEncHead>pfile_in_zip_read_info->stream.avail_in) uDoEncHead=pfile_in_zip_read_info->stream.avail_in; + if (uDoEncHead>0) + { char bufcrc=pfile_in_zip_read_info->stream.next_in[uDoEncHead-1]; + pfile_in_zip_read_info->rest_read_uncompressed-=uDoEncHead; + pfile_in_zip_read_info->stream.avail_in -= uDoEncHead; + pfile_in_zip_read_info->stream.next_in += uDoEncHead; + pfile_in_zip_read_info->encheadleft -= uDoEncHead; + if (pfile_in_zip_read_info->encheadleft==0) + { if (bufcrc!=pfile_in_zip_read_info->crcenctest) return UNZ_PASSWORD; + } + } + + if (pfile_in_zip_read_info->compression_method==0) + { uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) + { uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + } + else + { uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + } + for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,pfile_in_zip_read_info->stream.next_out,uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + if (pfile_in_zip_read_info->rest_read_uncompressed==0) {if (reached_eof!=0) *reached_eof=true;} + } + else + { uLong uTotalOutBefore,uTotalOutAfter; + const Byte *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + // + err=inflate(&pfile_in_zip_read_info->stream,flush); + // + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,bufBefore,(uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + if (err==Z_STREAM_END || pfile_in_zip_read_info->rest_read_uncompressed==0) + { if (reached_eof!=0) *reached_eof=true; + return iRead; + } + if (err!=Z_OK) break; + } + } + + if (err==Z_OK) return iRead; + return err; +} + + +// Give the current position in uncompressed data +z_off_t unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +// return 1 if the end of file was reached, 0 elsewhere +int unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// if buf==NULL, it return the size of the local extra field that can be read +// if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. +// the return value is the number of bytes copied in buf, or (if <0) the error code +int unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (lufread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +// Close the file in zip opened with unzipOpenCurrentFile +// Return UNZ_CRCERROR if all the file was read but the CRC is not good +int unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + if (pfile_in_zip_read_info->read_buffer!=0) + { void *buf = pfile_in_zip_read_info->read_buffer; + zfree(buf); + pfile_in_zip_read_info->read_buffer=0; + } + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); // unused pfile_in_zip_read_info=0; + + s->pfile_in_zip_read=NULL; + + return err; +} + + +// Get the global comment string of the ZipFile, in the szComment buffer. +// uSizeBuf is the size of the szComment buffer. +// return the number of byte copied or an error code <0 +int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ //int err=UNZ_OK; + unz_s* s; + uLong uReadThis ; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; + if (lufseek(s->file,s->central_pos+22,SEEK_SET)!=0) return UNZ_ERRNO; + if (uReadThis>0) + { *szComment='\0'; + if (lufread(szComment,(uInt)uReadThis,1,s->file)!=1) return UNZ_ERRNO; + } + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + + + + + +int unzOpenCurrentFile (unzFile file, const char *password); +int unzReadCurrentFile (unzFile file, void *buf, unsigned len); +int unzCloseCurrentFile (unzFile file); + + +typedef unsigned __int32 lutime_t; // define it ourselves since we don't include time.h + +FILETIME timet2filetime(const lutime_t t) +{ LONGLONG i = Int32x32To64(t,10000000) + 116444736000000000; + FILETIME ft; + ft.dwLowDateTime = (DWORD) i; + ft.dwHighDateTime = (DWORD)(i >>32); + return ft; +} + +FILETIME dosdatetime2filetime(WORD dosdate,WORD dostime) +{ // date: bits 0-4 are day of month 1-31. Bits 5-8 are month 1..12. Bits 9-15 are year-1980 + // time: bits 0-4 are seconds/2, bits 5-10 are minute 0..59. Bits 11-15 are hour 0..23 + SYSTEMTIME st; + st.wYear = (WORD)(((dosdate>>9)&0x7f) + 1980); + st.wMonth = (WORD)((dosdate>>5)&0xf); + st.wDay = (WORD)(dosdate&0x1f); + st.wHour = (WORD)((dostime>>11)&0x1f); + st.wMinute = (WORD)((dostime>>5)&0x3f); + st.wSecond = (WORD)((dostime&0x1f)*2); + st.wMilliseconds = 0; + FILETIME ft; SystemTimeToFileTime(&st,&ft); + return ft; +} + + + +class TUnzip +{ public: + TUnzip(const char *pwd) : uf(0), unzbuf(0), currentfile(-1), czei(-1), password(0) {if (pwd!=0) {password=new char[strlen(pwd)+1]; strcpy(password,pwd);}} + ~TUnzip() {if (password!=0) delete[] password; password=0; if (unzbuf!=0) delete[] unzbuf; unzbuf=0;} + + unzFile uf; int currentfile; ZIPENTRY cze; int czei; + char *password; + char *unzbuf; // lazily created and destroyed, used by Unzip + TCHAR rootdir[MAX_PATH]; // includes a trailing slash + + ZRESULT Open(void *z,unsigned int len,DWORD flags); + ZRESULT Get(int index,ZIPENTRY *ze); + ZRESULT Find(const TCHAR *name,bool ic,int *index,ZIPENTRY *ze); + ZRESULT Unzip(int index,void *dst,unsigned int len,DWORD flags); + ZRESULT SetUnzipBaseDir(const TCHAR *dir); + ZRESULT Close(); +}; + + +ZRESULT TUnzip::Open(void *z,unsigned int len,DWORD flags) +{ if (uf!=0 || currentfile!=-1) return ZR_NOTINITED; + // +#ifdef GetCurrentDirectory + GetCurrentDirectory(MAX_PATH,rootdir); +#else + _tcscpy(rootdir,_T("\\")); +#endif + TCHAR lastchar = rootdir[_tcslen(rootdir)-1]; + if (lastchar!='\\' && lastchar!='/') _tcscat(rootdir,_T("\\")); + // + if (flags==ZIP_HANDLE) + { // test if we can seek on it. We can't use GetFileType(h)==FILE_TYPE_DISK since it's not on CE. + DWORD res = SetFilePointer(z,0,0,FILE_CURRENT); + bool canseek = (res!=0xFFFFFFFF); + if (!canseek) return ZR_SEEK; + } + ZRESULT e; LUFILE *f = lufopen(z,len,flags,&e); + if (f==NULL) return e; + uf = unzOpenInternal(f); + if (uf==0) return ZR_NOFILE; + return ZR_OK; +} + +ZRESULT TUnzip::SetUnzipBaseDir(const TCHAR *dir) +{ _tcscpy(rootdir,dir); + TCHAR lastchar = rootdir[_tcslen(rootdir)-1]; + if (lastchar!='\\' && lastchar!='/') _tcscat(rootdir,_T("\\")); + return ZR_OK; +} + +ZRESULT TUnzip::Get(int index,ZIPENTRY *ze) +{ if (index<-1 || index>=(int)uf->gi.number_entry) return ZR_ARGS; + if (currentfile!=-1) unzCloseCurrentFile(uf); currentfile=-1; + if (index==czei && index!=-1) {memcpy(ze,&cze,sizeof(ZIPENTRY)); return ZR_OK;} + if (index==-1) + { ze->index = uf->gi.number_entry; + ze->name[0]=0; + ze->attr=0; + ze->atime.dwLowDateTime=0; ze->atime.dwHighDateTime=0; + ze->ctime.dwLowDateTime=0; ze->ctime.dwHighDateTime=0; + ze->mtime.dwLowDateTime=0; ze->mtime.dwHighDateTime=0; + ze->comp_size=0; + ze->unc_size=0; + return ZR_OK; + } + if (index<(int)uf->num_file) unzGoToFirstFile(uf); + while ((int)uf->num_filefile,offset,SEEK_SET)!=0) return ZR_READ; + unsigned char *extra = new unsigned char[extralen]; + if (lufread(extra,1,(uInt)extralen,uf->file)!=extralen) {delete[] extra; return ZR_READ;} + // + ze->index=uf->num_file; + TCHAR tfn[MAX_PATH]; +#ifdef UNICODE + MultiByteToWideChar(CP_UTF8,0,fn,-1,tfn,MAX_PATH); +#else + strcpy(tfn,fn); +#endif + // As a safety feature: if the zip filename had sneaky stuff + // like "c:\windows\file.txt" or "\windows\file.txt" or "fred\..\..\..\windows\file.txt" + // then we get rid of them all. That way, when the programmer does UnzipItem(hz,i,ze.name), + // it won't be a problem. (If the programmer really did want to get the full evil information, + // then they can edit out this security feature from here). + // In particular, we chop off any prefixes that are "c:\" or "\" or "/" or "[stuff]\.." or "[stuff]/.." + const TCHAR *sfn=tfn; + for (;;) + { if (sfn[0]!=0 && sfn[1]==':') {sfn+=2; continue;} + if (sfn[0]=='\\') {sfn++; continue;} + if (sfn[0]=='/') {sfn++; continue;} + const TCHAR *c; + c=_tcsstr(sfn,_T("\\..\\")); if (c!=0) {sfn=c+4; continue;} + c=_tcsstr(sfn,_T("\\../")); if (c!=0) {sfn=c+4; continue;} + c=_tcsstr(sfn,_T("/../")); if (c!=0) {sfn=c+4; continue;} + c=_tcsstr(sfn,_T("/..\\")); if (c!=0) {sfn=c+4; continue;} + break; + } + _tcscpy(ze->name, sfn); + + + // zip has an 'attribute' 32bit value. Its lower half is windows stuff + // its upper half is standard unix stat.st_mode. We'll start trying + // to read it in unix mode + unsigned long a = ufi.external_fa; + bool isdir = (a&0x40000000)!=0; + bool readonly= (a&0x00800000)==0; + //bool readable= (a&0x01000000)!=0; // unused + //bool executable=(a&0x00400000)!=0; // unused + bool hidden=false, system=false, archive=true; + // but in normal hostmodes these are overridden by the lower half... + int host = ufi.version>>8; + if (host==0 || host==7 || host==11 || host==14) + { readonly= (a&0x00000001)!=0; + hidden= (a&0x00000002)!=0; + system= (a&0x00000004)!=0; + isdir= (a&0x00000010)!=0; + archive= (a&0x00000020)!=0; + } + ze->attr=0; + if (isdir) ze->attr |= FILE_ATTRIBUTE_DIRECTORY; + if (archive) ze->attr|=FILE_ATTRIBUTE_ARCHIVE; + if (hidden) ze->attr|=FILE_ATTRIBUTE_HIDDEN; + if (readonly) ze->attr|=FILE_ATTRIBUTE_READONLY; + if (system) ze->attr|=FILE_ATTRIBUTE_SYSTEM; + ze->comp_size = ufi.compressed_size; + ze->unc_size = ufi.uncompressed_size; + // + WORD dostime = (WORD)(ufi.dosDate&0xFFFF); + WORD dosdate = (WORD)((ufi.dosDate>>16)&0xFFFF); + FILETIME ftd = dosdatetime2filetime(dosdate,dostime); + FILETIME ft; LocalFileTimeToFileTime(&ftd,&ft); + ze->atime=ft; ze->ctime=ft; ze->mtime=ft; + // the zip will always have at least that dostime. But if it also has + // an extra header, then we'll instead get the info from that. + unsigned int epos=0; + while (epos+4mtime = timet2filetime(mtime); + } + if (hasatime) + { lutime_t atime = ((extra[epos+0])<<0) | ((extra[epos+1])<<8) |((extra[epos+2])<<16) | ((extra[epos+3])<<24); + epos+=4; + ze->atime = timet2filetime(atime); + } + if (hasctime) + { lutime_t ctime = ((extra[epos+0])<<0) | ((extra[epos+1])<<8) |((extra[epos+2])<<16) | ((extra[epos+3])<<24); + epos+=4; + ze->ctime = timet2filetime(ctime); + } + break; + } + // + if (extra!=0) delete[] extra; + memcpy(&cze,ze,sizeof(ZIPENTRY)); czei=index; + return ZR_OK; +} + +ZRESULT TUnzip::Find(const TCHAR *tname,bool ic,int *index,ZIPENTRY *ze) +{ char name[MAX_PATH]; +#ifdef UNICODE + WideCharToMultiByte(CP_UTF8,0,tname,-1,name,MAX_PATH,0,0); +#else + strcpy(name,tname); +#endif + int res = unzLocateFile(uf,name,ic?CASE_INSENSITIVE:CASE_SENSITIVE); + if (res!=UNZ_OK) + { if (index!=0) *index=-1; + if (ze!=NULL) {ZeroMemory(ze,sizeof(ZIPENTRY)); ze->index=-1;} + return ZR_NOTFOUND; + } + if (currentfile!=-1) unzCloseCurrentFile(uf); currentfile=-1; + int i = (int)uf->num_file; + if (index!=NULL) *index=i; + if (ze!=NULL) + { ZRESULT zres = Get(i,ze); + if (zres!=ZR_OK) return zres; + } + return ZR_OK; +} + +void EnsureDirectory(const TCHAR *rootdir, const TCHAR *dir) +{ if (rootdir!=0 && GetFileAttributes(rootdir)==0xFFFFFFFF) CreateDirectory(rootdir,0); + if (*dir==0) return; + const TCHAR *lastslash=dir, *c=lastslash; + while (*c!=0) {if (*c=='/' || *c=='\\') lastslash=c; c++;} + const TCHAR *name=lastslash; + if (lastslash!=dir) + { TCHAR tmp[MAX_PATH]; memcpy(tmp,dir,sizeof(TCHAR)*(lastslash-dir)); + tmp[lastslash-dir]=0; + EnsureDirectory(rootdir,tmp); + name++; + } + TCHAR cd[MAX_PATH]; *cd=0; if (rootdir!=0) _tcscpy(cd,rootdir); _tcscat(cd,dir); + if (GetFileAttributes(cd)==0xFFFFFFFF) CreateDirectory(cd,NULL); +} + + + +ZRESULT TUnzip::Unzip(int index,void *dst,unsigned int len,DWORD flags) +{ if (flags!=ZIP_MEMORY && flags!=ZIP_FILENAME && flags!=ZIP_HANDLE) return ZR_ARGS; + if (flags==ZIP_MEMORY) + { if (index!=currentfile) + { if (currentfile!=-1) unzCloseCurrentFile(uf); currentfile=-1; + if (index>=(int)uf->gi.number_entry) return ZR_ARGS; + if (index<(int)uf->num_file) unzGoToFirstFile(uf); + while ((int)uf->num_file0) return ZR_MORE; + if (res==UNZ_PASSWORD) return ZR_PASSWORD; + return ZR_FLATE; + } + // otherwise we're writing to a handle or a file + if (currentfile!=-1) unzCloseCurrentFile(uf); currentfile=-1; + if (index>=(int)uf->gi.number_entry) return ZR_ARGS; + if (index<(int)uf->num_file) unzGoToFirstFile(uf); + while ((int)uf->num_file0) {DWORD writ; BOOL bres=WriteFile(h,unzbuf,res,&writ,NULL); if (!bres) {haderr=ZR_WRITE; break;}} + if (reached_eof) break; + if (res==0) {haderr=ZR_FLATE; break;} + } + if (!haderr) SetFileTime(h,&ze.ctime,&ze.atime,&ze.mtime); // may fail if it was a pipe + if (flags!=ZIP_HANDLE) CloseHandle(h); + unzCloseCurrentFile(uf); + if (haderr!=0) return haderr; + return ZR_OK; +} + +ZRESULT TUnzip::Close() +{ if (currentfile!=-1) unzCloseCurrentFile(uf); currentfile=-1; + if (uf!=0) unzClose(uf); uf=0; + return ZR_OK; +} + + + + + +ZRESULT lasterrorU=ZR_OK; + +unsigned int FormatZipMessageU(ZRESULT code, TCHAR *buf,unsigned int len) +{ if (code==ZR_RECENT) code=lasterrorU; + const TCHAR *msg=_T("unknown zip result code"); + switch (code) + { case ZR_OK: msg=_T("Success"); break; + case ZR_NODUPH: msg=_T("Culdn't duplicate handle"); break; + case ZR_NOFILE: msg=_T("Couldn't create/open file"); break; + case ZR_NOALLOC: msg=_T("Failed to allocate memory"); break; + case ZR_WRITE: msg=_T("Error writing to file"); break; + case ZR_NOTFOUND: msg=_T("File not found in the zipfile"); break; + case ZR_MORE: msg=_T("Still more data to unzip"); break; + case ZR_CORRUPT: msg=_T("Zipfile is corrupt or not a zipfile"); break; + case ZR_READ: msg=_T("Error reading file"); break; + case ZR_PASSWORD: msg=_T("Correct password required"); break; + case ZR_ARGS: msg=_T("Caller: faulty arguments"); break; + case ZR_PARTIALUNZ: msg=_T("Caller: the file had already been partially unzipped"); break; + case ZR_NOTMMAP: msg=_T("Caller: can only get memory of a memory zipfile"); break; + case ZR_MEMSIZE: msg=_T("Caller: not enough space allocated for memory zipfile"); break; + case ZR_FAILED: msg=_T("Caller: there was a previous error"); break; + case ZR_ENDED: msg=_T("Caller: additions to the zip have already been ended"); break; + case ZR_ZMODE: msg=_T("Caller: mixing creation and opening of zip"); break; + case ZR_NOTINITED: msg=_T("Zip-bug: internal initialisation not completed"); break; + case ZR_SEEK: msg=_T("Zip-bug: trying to seek the unseekable"); break; + case ZR_MISSIZE: msg=_T("Zip-bug: the anticipated size turned out wrong"); break; + case ZR_NOCHANGE: msg=_T("Zip-bug: tried to change mind, but not allowed"); break; + case ZR_FLATE: msg=_T("Zip-bug: an internal error during flation"); break; + } + unsigned int mlen=(unsigned int)_tcslen(msg); + if (buf==0 || len==0) return mlen; + unsigned int n=mlen; if (n+1>len) n=len-1; + _tcsncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + +typedef struct +{ DWORD flag; + TUnzip *unz; +} TUnzipHandleData; + +HZIP OpenZipInternal(void *z,unsigned int len,DWORD flags, const char *password) +{ TUnzip *unz = new TUnzip(password); + lasterrorU = unz->Open(z,len,flags); + if (lasterrorU!=ZR_OK) {delete unz; return 0;} + TUnzipHandleData *han = new TUnzipHandleData; + han->flag=1; han->unz=unz; return (HZIP)han; +} +HZIP OpenZipHandle(HANDLE h, const char *password) {return OpenZipInternal((void*)h,0,ZIP_HANDLE,password);} +HZIP OpenZip(const TCHAR *fn, const char *password) {return OpenZipInternal((void*)fn,0,ZIP_FILENAME,password);} +HZIP OpenZip(void *z,unsigned int len, const char *password) {return OpenZipInternal(z,len,ZIP_MEMORY,password);} + + +ZRESULT GetZipItem(HZIP hz, int index, ZIPENTRY *ze) +{ ze->index=0; *ze->name=0; ze->unc_size=0; + if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->Get(index,ze); + return lasterrorU; +} + +ZRESULT FindZipItem(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->Find(name,ic,index,ze); + return lasterrorU; +} + +ZRESULT UnzipItemInternal(HZIP hz, int index, void *dst, unsigned int len, DWORD flags) +{ if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->Unzip(index,dst,len,flags); + return lasterrorU; +} +ZRESULT UnzipItemHandle(HZIP hz, int index, HANDLE h) {return UnzipItemInternal(hz,index,(void*)h,0,ZIP_HANDLE);} +ZRESULT UnzipItem(HZIP hz, int index, const TCHAR *fn) {return UnzipItemInternal(hz,index,(void*)fn,0,ZIP_FILENAME);} +ZRESULT UnzipItem(HZIP hz, int index, void *z,unsigned int len) {return UnzipItemInternal(hz,index,z,len,ZIP_MEMORY);} + +ZRESULT SetUnzipBaseDir(HZIP hz, const TCHAR *dir) +{ if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->SetUnzipBaseDir(dir); + return lasterrorU; +} + + +ZRESULT CloseZipU(HZIP hz) +{ if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->Close(); + delete unz; + delete han; + return lasterrorU; +} + +bool IsZipHandleU(HZIP hz) +{ if (hz==0) return false; + TUnzipHandleData *han = (TUnzipHandleData*)hz; + return (han->flag==1); +} + +#endif //WIN32 diff --git a/libraries/amf/amftools-code/src/zip/zip.cpp b/libraries/amf/amftools-code/src/zip/zip.cpp new file mode 100644 index 00000000..fa2ea895 --- /dev/null +++ b/libraries/amf/amftools-code/src/zip/zip.cpp @@ -0,0 +1,2834 @@ +#ifdef WIN32 + +#include +#include +#include +#include "zip/zip.h" + + +// THIS FILE is almost entirely based upon code by info-zip. +// It has been modified by Lucian Wischik. The modifications +// were a complete rewrite of the bit of code that generates the +// layout of the zipfile, and support for zipping to/from memory +// or handles or pipes or pagefile or diskfiles, encryption, unicode. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +// +// +// This is version 1999-Oct-05 of the Info-ZIP copyright and license. +// The definitive version of this document should be available at +// ftp://ftp.cdrom.com/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-1999 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, David Kirschbaum, +// Johnny Lee, Onno van der Linden, Igor Mandrichenko, Steve P. Miller, +// Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, Kai Uwe Rommel, +// Steve Salisbury, Dave Smith, Christian Spieler, Antoine Verheijen, +// Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is," without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, definition, disclaimer, and this list of conditions in +// documentation and/or other materials provided with the distribution. +// +// 3. Altered versions--including, but not limited to, ports to new operating +// systems, existing ports with new graphical interfaces, and dynamic, +// shared, or static library versions--must be plainly marked as such +// and must not be misrepresented as being the original source. Such +// altered versions also must not be misrepresented as being Info-ZIP +// releases--including, but not limited to, labeling of the altered +// versions with the names "Info-ZIP" (or any variation thereof, including, +// but not limited to, different capitalizations), "Pocket UnZip," "WiZ" +// or "MacZip" without the explicit permission of Info-ZIP. Such altered +// versions are further prohibited from misrepresentative use of the +// Zip-Bugs or Info-ZIP e-mail addresses or of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP," "Zip," "UnZip," +// "WiZ," "Pocket UnZip," "Pocket Zip," and "MacZip" for its own source and +// binary releases. +// + + +typedef unsigned char uch; // unsigned 8-bit value +typedef unsigned short ush; // unsigned 16-bit value +typedef unsigned long ulg; // unsigned 32-bit value +typedef size_t extent; // file size +typedef unsigned Pos; // must be at least 32 bits +typedef unsigned IPos; // A Pos is an index in the character window. Pos is used only for parameter passing + +#ifndef EOF +#define EOF (-1) +#endif + + +// Error return values. The values 0..4 and 12..18 follow the conventions +// of PKZIP. The values 4..10 are all assigned to "insufficient memory" +// by PKZIP, so the codes 5..10 are used here for other purposes. +#define ZE_MISS -1 // used by procname(), zipbare() +#define ZE_OK 0 // success +#define ZE_EOF 2 // unexpected end of zip file +#define ZE_FORM 3 // zip file structure error +#define ZE_MEM 4 // out of memory +#define ZE_LOGIC 5 // internal logic error +#define ZE_BIG 6 // entry too large to split +#define ZE_NOTE 7 // invalid comment format +#define ZE_TEST 8 // zip test (-T) failed or out of memory +#define ZE_ABORT 9 // user interrupt or termination +#define ZE_TEMP 10 // error using a temp file +#define ZE_READ 11 // read or seek error +#define ZE_NONE 12 // nothing to do +#define ZE_NAME 13 // missing or empty zip file +#define ZE_WRITE 14 // error writing to a file +#define ZE_CREAT 15 // couldn't open to write +#define ZE_PARMS 16 // bad command line +#define ZE_OPEN 18 // could not open a specified file to read +#define ZE_MAXERR 18 // the highest error number + + +// internal file attribute +#define UNKNOWN (-1) +#define BINARY 0 +#define ASCII 1 + +#define BEST -1 // Use best method (deflation or store) +#define STORE 0 // Store method +#define DEFLATE 8 // Deflation method + +#define CRCVAL_INITIAL 0L + +// MSDOS file or directory attributes +#define MSDOS_HIDDEN_ATTR 0x02 +#define MSDOS_DIR_ATTR 0x10 + +// Lengths of headers after signatures in bytes +#define LOCHEAD 26 +#define CENHEAD 42 +#define ENDHEAD 18 + +// Definitions for extra field handling: +#define EB_HEADSIZE 4 /* length of a extra field block header */ +#define EB_LEN 2 /* offset of data length field in header */ +#define EB_UT_MINLEN 1 /* minimal UT field contains Flags byte */ +#define EB_UT_FLAGS 0 /* byte offset of Flags field */ +#define EB_UT_TIME1 1 /* byte offset of 1st time value */ +#define EB_UT_FL_MTIME (1 << 0) /* mtime present */ +#define EB_UT_FL_ATIME (1 << 1) /* atime present */ +#define EB_UT_FL_CTIME (1 << 2) /* ctime present */ +#define EB_UT_LEN(n) (EB_UT_MINLEN + 4 * (n)) +#define EB_L_UT_SIZE (EB_HEADSIZE + EB_UT_LEN(3)) +#define EB_C_UT_SIZE (EB_HEADSIZE + EB_UT_LEN(1)) + + +// Macros for writing machine integers to little-endian format +#define PUTSH(a,f) {char _putsh_c=(char)((a)&0xff); wfunc(param,&_putsh_c,1); _putsh_c=(char)((a)>>8); wfunc(param,&_putsh_c,1);} +#define PUTLG(a,f) {PUTSH((a) & 0xffff,(f)) PUTSH((a) >> 16,(f))} + + +// -- Structure of a ZIP file -- +// Signatures for zip file information headers +#define LOCSIG 0x04034b50L +#define CENSIG 0x02014b50L +#define ENDSIG 0x06054b50L +#define EXTLOCSIG 0x08074b50L + + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + + +#define WSIZE (0x8000) +// Maximum window size = 32K. If you are really short of memory, compile +// with a smaller WSIZE but this reduces the compression ratio for files +// of size > WSIZE. WSIZE must be a power of two in the current implementation. +// + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +// Minimum amount of lookahead, except at the end of the input file. +// See deflate.c for comments about the MIN_MATCH+1. +// + +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) +// In order to simplify the code, particularly on 16 bit machines, match +// distances are limited to MAX_DIST instead of WSIZE. +// + + +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 +#define ZIP_FOLDER 4 + + + +// =========================================================================== +// Constants +// + +#define MAX_BITS 15 +// All codes must not exceed MAX_BITS bits + +#define MAX_BL_BITS 7 +// Bit length codes must not exceed MAX_BL_BITS bits + +#define LENGTH_CODES 29 +// number of length codes, not counting the special END_BLOCK code + +#define LITERALS 256 +// number of literal bytes 0..255 + +#define END_BLOCK 256 +// end of block literal code + +#define L_CODES (LITERALS+1+LENGTH_CODES) +// number of Literal or Length codes, including the END_BLOCK code + +#define D_CODES 30 +// number of distance codes + +#define BL_CODES 19 +// number of codes used to transfer the bit lengths + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define LIT_BUFSIZE 0x8000 +#define DIST_BUFSIZE LIT_BUFSIZE +// Sizes of match buffers for literals/lengths and distances. There are +// 4 reasons for limiting LIT_BUFSIZE to 64K: +// - frequencies can be kept in 16 bit counters +// - if compression is not successful for the first block, all input data is +// still in the window so we can still emit a stored block even when input +// comes from standard input. (This can also be done for all blocks if +// LIT_BUFSIZE is not greater than 32K.) +// - if compression is not successful for a file smaller than 64K, we can +// even emit a stored file instead of a stored block (saving 5 bytes). +// - creating new Huffman trees less frequently may not provide fast +// adaptation to changes in the input data statistics. (Take for +// example a binary file with poorly compressible code followed by +// a highly compressible string table.) Smaller buffer sizes give +// fast adaptation but have of course the overhead of transmitting trees +// more frequently. +// - I can't count above 4 +// The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save +// memory at the expense of compression). Some optimizations would be possible +// if we rely on DIST_BUFSIZE == LIT_BUFSIZE. +// + +#define REP_3_6 16 +// repeat previous bit length 3-6 times (2 bits of repeat count) + +#define REPZ_3_10 17 +// repeat a zero length 3-10 times (3 bits of repeat count) + +#define REPZ_11_138 18 +// repeat a zero length 11-138 times (7 bits of repeat count) + +#define HEAP_SIZE (2*L_CODES+1) +// maximum heap size + + +// =========================================================================== +// Local data used by the "bit string" routines. +// + +#define Buf_size (8 * 2*sizeof(char)) +// Number of bits used within bi_buf. (bi_buf may be implemented on +// more than 16 bits on some systems.) + +// Output a 16 bit value to the bit stream, lower (oldest) byte first +#define PUTSHORT(state,w) \ +{ if (state.bs.out_offset >= state.bs.out_size-1) \ + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \ + state.bs.out_buf[state.bs.out_offset++] = (char) ((w) & 0xff); \ + state.bs.out_buf[state.bs.out_offset++] = (char) ((ush)(w) >> 8); \ +} + +#define PUTBYTE(state,b) \ +{ if (state.bs.out_offset >= state.bs.out_size) \ + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \ + state.bs.out_buf[state.bs.out_offset++] = (char) (b); \ +} + +// DEFLATE.CPP HEADER + +#define HASH_BITS 15 +// For portability to 16 bit machines, do not use values above 15. + +#define HASH_SIZE (unsigned)(1<= HASH_BITS + +#define max_insert_length max_lazy_match +// Insert new strings in the hash table only if the match length +// is not greater than this length. This saves time but degrades compression. +// max_insert_length is used only for compression levels <= 3. + + + +const int extra_lbits[LENGTH_CODES] // extra bits for each length code + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +const int extra_dbits[D_CODES] // extra bits for each distance code + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +const int extra_blbits[BL_CODES]// extra bits for each bit length code + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +const uch bl_order[BL_CODES] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +// The lengths of the bit length codes are sent in order of decreasing +// probability, to avoid transmitting the lengths for unused bit length codes. + + +typedef struct config { + ush good_length; // reduce lazy search above this match length + ush max_lazy; // do not perform lazy search above this match length + ush nice_length; // quit search above this match length + ush max_chain; +} config; + +// Values for max_lazy_match, good_match, nice_match and max_chain_length, +// depending on the desired pack level (0..9). The values given below have +// been tuned to exclude worst case performance for pathological files. +// Better values may be found for specific files. +// + +const config configuration_table[10] = { +// good lazy nice chain + {0, 0, 0, 0}, // 0 store only + {4, 4, 8, 4}, // 1 maximum speed, no lazy matches + {4, 5, 16, 8}, // 2 + {4, 6, 32, 32}, // 3 + {4, 4, 16, 16}, // 4 lazy matches */ + {8, 16, 32, 32}, // 5 + {8, 16, 128, 128}, // 6 + {8, 32, 128, 256}, // 7 + {32, 128, 258, 1024}, // 8 + {32, 258, 258, 4096}};// 9 maximum compression */ + +// Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 +// For deflate_fast() (levels <= 3) good is ignored and lazy has a different meaning. + + + + + + + +// Data structure describing a single value and its code string. +typedef struct ct_data { + union { + ush freq; // frequency count + ush code; // bit string + } fc; + union { + ush dad; // father node in Huffman tree + ush len; // length of bit string + } dl; +} ct_data; + +typedef struct tree_desc { + ct_data *dyn_tree; // the dynamic tree + ct_data *static_tree; // corresponding static tree or NULL + const int *extra_bits; // extra bits for each code or NULL + int extra_base; // base index for extra_bits + int elems; // max number of elements in the tree + int max_length; // max bit length for the codes + int max_code; // largest code with non zero frequency +} tree_desc; + + + + +class TTreeState +{ public: + TTreeState(); + + ct_data dyn_ltree[HEAP_SIZE]; // literal and length tree + ct_data dyn_dtree[2*D_CODES+1]; // distance tree + ct_data static_ltree[L_CODES+2]; // the static literal tree... + // ... Since the bit lengths are imposed, there is no need for the L_CODES + // extra codes used during heap construction. However the codes 286 and 287 + // are needed to build a canonical tree (see ct_init below). + ct_data static_dtree[D_CODES]; // the static distance tree... + // ... (Actually a trivial tree since all codes use 5 bits.) + ct_data bl_tree[2*BL_CODES+1]; // Huffman tree for the bit lengths + + tree_desc l_desc; + tree_desc d_desc; + tree_desc bl_desc; + + ush bl_count[MAX_BITS+1]; // number of codes at each bit length for an optimal tree + + int heap[2*L_CODES+1]; // heap used to build the Huffman trees + int heap_len; // number of elements in the heap + int heap_max; // element of largest frequency + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + uch depth[2*L_CODES+1]; + // Depth of each subtree used as tie breaker for trees of equal frequency + + uch length_code[MAX_MATCH-MIN_MATCH+1]; + // length code for each normalized match length (0 == MIN_MATCH) + + uch dist_code[512]; + // distance codes. The first 256 values correspond to the distances + // 3 .. 258, the last 256 values correspond to the top 8 bits of + // the 15 bit distances. + + int base_length[LENGTH_CODES]; + // First normalized length for each code (0 = MIN_MATCH) + + int base_dist[D_CODES]; + // First normalized distance for each code (0 = distance of 1) + + uch far l_buf[LIT_BUFSIZE]; // buffer for literals/lengths + ush far d_buf[DIST_BUFSIZE]; // buffer for distances + + uch flag_buf[(LIT_BUFSIZE/8)]; + // flag_buf is a bit array distinguishing literals from lengths in + // l_buf, and thus indicating the presence or absence of a distance. + + unsigned last_lit; // running index in l_buf + unsigned last_dist; // running index in d_buf + unsigned last_flags; // running index in flag_buf + uch flags; // current flags not yet saved in flag_buf + uch flag_bit; // current bit used in flags + // bits are filled in flags starting at bit 0 (least significant). + // Note: these flags are overkill in the current code since we don't + // take advantage of DIST_BUFSIZE == LIT_BUFSIZE. + + ulg opt_len; // bit length of current block with optimal trees + ulg static_len; // bit length of current block with static trees + + ulg cmpr_bytelen; // total byte length of compressed file + ulg cmpr_len_bits; // number of bits past 'cmpr_bytelen' + + ulg input_len; // total byte length of input file + // input_len is for debugging only since we can get it by other means. + + ush *file_type; // pointer to UNKNOWN, BINARY or ASCII +// int *file_method; // pointer to DEFLATE or STORE +}; + +TTreeState::TTreeState() +{ tree_desc a = {dyn_ltree, static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS, 0}; l_desc = a; + tree_desc b = {dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0}; d_desc = b; + tree_desc c = {bl_tree, NULL, extra_blbits, 0, BL_CODES, MAX_BL_BITS, 0}; bl_desc = c; + last_lit=0; + last_dist=0; + last_flags=0; +} + + + +class TBitState +{ public: + + int flush_flg; + // + unsigned bi_buf; + // Output buffer. bits are inserted starting at the bottom (least significant + // bits). The width of bi_buf must be at least 16 bits. + int bi_valid; + // Number of valid bits in bi_buf. All bits above the last valid bit + // are always zero. + char *out_buf; + // Current output buffer. + unsigned out_offset; + // Current offset in output buffer. + // On 16 bit machines, the buffer is limited to 64K. + unsigned out_size; + // Size of current output buffer + ulg bits_sent; // bit length of the compressed data only needed for debugging??? +}; + + + + + + + +class TDeflateState +{ public: + TDeflateState() {window_size=0;} + + uch window[2L*WSIZE]; + // Sliding window. Input bytes are read into the second half of the window, + // and move to the first half later to keep a dictionary of at least WSIZE + // bytes. With this organization, matches are limited to a distance of + // WSIZE-MAX_MATCH bytes, but this ensures that IO is always + // performed with a length multiple of the block size. Also, it limits + // the window size to 64K, which is quite useful on MSDOS. + // To do: limit the window size to WSIZE+CBSZ if SMALL_MEM (the code would + // be less efficient since the data would have to be copied WSIZE/CBSZ times) + Pos prev[WSIZE]; + // Link to older string with same hash index. To limit the size of this + // array to 64K, this link is maintained only for the last 32K strings. + // An index in this array is thus a window index modulo 32K. + Pos head[HASH_SIZE]; + // Heads of the hash chains or NIL. If your compiler thinks that + // HASH_SIZE is a dynamic value, recompile with -DDYN_ALLOC. + + ulg window_size; + // window size, 2*WSIZE except for MMAP or BIG_MEM, where it is the + // input file length plus MIN_LOOKAHEAD. + + long block_start; + // window position at the beginning of the current output block. Gets + // negative when the window is moved backwards. + + int sliding; + // Set to false when the input file is already in memory + + unsigned ins_h; // hash index of string to be inserted + + unsigned int prev_length; + // Length of the best match at previous step. Matches not greater than this + // are discarded. This is used in the lazy match evaluation. + + unsigned strstart; // start of string to insert + unsigned match_start; // start of matching string + int eofile; // flag set at end of input file + unsigned lookahead; // number of valid bytes ahead in window + + unsigned max_chain_length; + // To speed up deflation, hash chains are never searched beyond this length. + // A higher limit improves compression ratio but degrades the speed. + + unsigned int max_lazy_match; + // Attempt to find a better match only when the current match is strictly + // smaller than this value. This mechanism is used only for compression + // levels >= 4. + + unsigned good_match; + // Use a faster search when the previous match is longer than this + + int nice_match; // Stop searching when current match exceeds this +}; + +typedef __int64 lutime_t; // define it ourselves since we don't include time.h + +typedef struct iztimes { + lutime_t atime,mtime,ctime; +} iztimes; // access, modify, create times + +typedef struct zlist { + ush vem, ver, flg, how; // See central header in zipfile.c for what vem..off are + ulg tim, crc, siz, len; + extent nam, ext, cext, com; // offset of ext must be >= LOCHEAD + ush dsk, att, lflg; // offset of lflg must be >= LOCHEAD + ulg atx, off; + char name[MAX_PATH]; // File name in zip file + char *extra; // Extra field (set only if ext != 0) + char *cextra; // Extra in central (set only if cext != 0) + char *comment; // Comment (set only if com != 0) + char iname[MAX_PATH]; // Internal file name after cleanup + char zname[MAX_PATH]; // External version of internal name + int mark; // Marker for files to operate on + int trash; // Marker for files to delete + int dosflag; // Set to force MSDOS file attributes + struct zlist far *nxt; // Pointer to next header in list +} TZipFileInfo; + + +struct TState; +typedef unsigned (*READFUNC)(TState &state, char *buf,unsigned size); +typedef unsigned (*FLUSHFUNC)(void *param, const char *buf, unsigned *size); +typedef unsigned (*WRITEFUNC)(void *param, const char *buf, unsigned size); +struct TState +{ void *param; + int level; bool seekable; + READFUNC readfunc; FLUSHFUNC flush_outbuf; + TTreeState ts; TBitState bs; TDeflateState ds; + const char *err; +}; + + + + + + + + + +void Assert(TState &state,bool cond, const char *msg) +{ if (cond) return; + state.err=msg; +} +void __cdecl Trace(const char *x, ...) {va_list paramList; va_start(paramList, x); paramList; va_end(paramList);} +void __cdecl Tracec(bool ,const char *x, ...) {va_list paramList; va_start(paramList, x); paramList; va_end(paramList);} + + + +// =========================================================================== +// Local (static) routines in this file. +// + +void init_block (TState &); +void pqdownheap (TState &,ct_data *tree, int k); +void gen_bitlen (TState &,tree_desc *desc); +void gen_codes (TState &state,ct_data *tree, int max_code); +void build_tree (TState &,tree_desc *desc); +void scan_tree (TState &,ct_data *tree, int max_code); +void send_tree (TState &state,ct_data *tree, int max_code); +int build_bl_tree (TState &); +void send_all_trees (TState &state,int lcodes, int dcodes, int blcodes); +void compress_block (TState &state,ct_data *ltree, ct_data *dtree); +void set_file_type (TState &); +void send_bits (TState &state, int value, int length); +unsigned bi_reverse (unsigned code, int len); +void bi_windup (TState &state); +void copy_block (TState &state,char *buf, unsigned len, int header); + + +#define send_code(state, c, tree) send_bits(state, tree[c].fc.code, tree[c].dl.len) +// Send a code of the given tree. c and tree must not have side effects + +// alternatively... +//#define send_code(state, c, tree) +// { if (state.verbose>1) fprintf(stderr,"\ncd %3d ",(c)); +// send_bits(state, tree[c].fc.code, tree[c].dl.len); } + +#define d_code(dist) ((dist) < 256 ? state.ts.dist_code[dist] : state.ts.dist_code[256+((dist)>>7)]) +// Mapping from a distance to a distance code. dist is the distance - 1 and +// must not have side effects. dist_code[256] and dist_code[257] are never used. + +#define Max(a,b) (a >= b ? a : b) +/* the arguments must not have side effects */ + +/* =========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + */ +void ct_init(TState &state, ush *attr) +{ + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + + state.ts.file_type = attr; + //state.ts.file_method = method; + state.ts.cmpr_bytelen = state.ts.cmpr_len_bits = 0L; + state.ts.input_len = 0L; + + if (state.ts.static_dtree[0].dl.len != 0) return; /* ct_init already called */ + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + state.ts.base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + state.ts.base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + state.ts.base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + state.ts.dist_code[256 + dist++] = (uch)code; + } + } + Assert(state,dist == 256, "ct_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) state.ts.bl_count[bits] = 0; + n = 0; + while (n <= 143) state.ts.static_ltree[n++].dl.len = 8, state.ts.bl_count[8]++; + while (n <= 255) state.ts.static_ltree[n++].dl.len = 9, state.ts.bl_count[9]++; + while (n <= 279) state.ts.static_ltree[n++].dl.len = 7, state.ts.bl_count[7]++; + while (n <= 287) state.ts.static_ltree[n++].dl.len = 8, state.ts.bl_count[8]++; + /* fc.codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes(state,(ct_data *)state.ts.static_ltree, L_CODES+1); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + state.ts.static_dtree[n].dl.len = 5; + state.ts.static_dtree[n].fc.code = (ush)bi_reverse(n, 5); + } + + /* Initialize the first block of the first file: */ + init_block(state); +} + +/* =========================================================================== + * Initialize a new block. + */ +void init_block(TState &state) +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) state.ts.dyn_ltree[n].fc.freq = 0; + for (n = 0; n < D_CODES; n++) state.ts.dyn_dtree[n].fc.freq = 0; + for (n = 0; n < BL_CODES; n++) state.ts.bl_tree[n].fc.freq = 0; + + state.ts.dyn_ltree[END_BLOCK].fc.freq = 1; + state.ts.opt_len = state.ts.static_len = 0L; + state.ts.last_lit = state.ts.last_dist = state.ts.last_flags = 0; + state.ts.flags = 0; state.ts.flag_bit = 1; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(tree, top) \ +{\ + top = state.ts.heap[SMALLEST]; \ + state.ts.heap[SMALLEST] = state.ts.heap[state.ts.heap_len--]; \ + pqdownheap(state,tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m) \ + (tree[n].fc.freq < tree[m].fc.freq || \ + (tree[n].fc.freq == tree[m].fc.freq && state.ts.depth[n] <= state.ts.depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +void pqdownheap(TState &state,ct_data *tree, int k) +{ + int v = state.ts.heap[k]; + int j = k << 1; /* left son of k */ + int htemp; /* required because of bug in SASC compiler */ + + while (j <= state.ts.heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < state.ts.heap_len && smaller(tree, state.ts.heap[j+1], state.ts.heap[j])) j++; + + /* Exit if v is smaller than both sons */ + htemp = state.ts.heap[j]; + if (smaller(tree, v, htemp)) break; + + /* Exchange v with the smallest son */ + state.ts.heap[k] = htemp; + k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + state.ts.heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +void gen_bitlen(TState &state,tree_desc *desc) +{ + ct_data *tree = desc->dyn_tree; + const int *extra = desc->extra_bits; + int base = desc->extra_base; + int max_code = desc->max_code; + int max_length = desc->max_length; + ct_data *stree = desc->static_tree; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) state.ts.bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[state.ts.heap[state.ts.heap_max]].dl.len = 0; /* root of the heap */ + + for (h = state.ts.heap_max+1; h < HEAP_SIZE; h++) { + n = state.ts.heap[h]; + bits = tree[tree[n].dl.dad].dl.len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].dl.len = (ush)bits; + /* We overwrite tree[n].dl.dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + state.ts.bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].fc.freq; + state.ts.opt_len += (ulg)f * (bits + xbits); + if (stree) state.ts.static_len += (ulg)f * (stree[n].dl.len + xbits); + } + if (overflow == 0) return; + + Trace("\nbit length overflow\n"); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (state.ts.bl_count[bits] == 0) bits--; + state.ts.bl_count[bits]--; /* move one leaf down the tree */ + state.ts.bl_count[bits+1] += (ush)2; /* move one overflow item as its brother */ + state.ts.bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = state.ts.bl_count[bits]; + while (n != 0) { + m = state.ts.heap[--h]; + if (m > max_code) continue; + if (tree[m].dl.len != (ush)bits) { + Trace("code %d bits %d->%d\n", m, tree[m].dl.len, bits); + state.ts.opt_len += ((long)bits-(long)tree[m].dl.len)*(long)tree[m].fc.freq; + tree[m].dl.len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +void gen_codes (TState &state, ct_data *tree, int max_code) +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (ush)((code + state.ts.bl_count[bits-1]) << 1); + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert(state,code + state.ts.bl_count[MAX_BITS]-1 == (1<< ((ush) MAX_BITS)) - 1, + "inconsistent bit counts"); + Trace("\ngen_codes: max_code %d ", max_code); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].dl.len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].fc.code = (ush)bi_reverse(next_code[len]++, len); + + //Tracec(tree != state.ts.static_ltree, "\nn %3d %c l %2d c %4x (%x) ", n, (isgraph(n) ? n : ' '), len, tree[n].fc.code, next_code[len]-1); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +void build_tree(TState &state,tree_desc *desc) +{ + ct_data *tree = desc->dyn_tree; + ct_data *stree = desc->static_tree; + int elems = desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node = elems; /* next internal node of the tree */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + state.ts.heap_len = 0, state.ts.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].fc.freq != 0) { + state.ts.heap[++state.ts.heap_len] = max_code = n; + state.ts.depth[n] = 0; + } else { + tree[n].dl.len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (state.ts.heap_len < 2) { + int newcp = state.ts.heap[++state.ts.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[newcp].fc.freq = 1; + state.ts.depth[newcp] = 0; + state.ts.opt_len--; if (stree) state.ts.static_len -= stree[newcp].dl.len; + /* new is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = state.ts.heap_len/2; n >= 1; n--) pqdownheap(state,tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + pqremove(tree, n); /* n = node of least frequency */ + m = state.ts.heap[SMALLEST]; /* m = node of next least frequency */ + + state.ts.heap[--state.ts.heap_max] = n; /* keep the nodes sorted by frequency */ + state.ts.heap[--state.ts.heap_max] = m; + + /* Create a new node father of n and m */ + tree[node].fc.freq = (ush)(tree[n].fc.freq + tree[m].fc.freq); + state.ts.depth[node] = (uch) (Max(state.ts.depth[n], state.ts.depth[m]) + 1); + tree[n].dl.dad = tree[m].dl.dad = (ush)node; + /* and insert the new node in the heap */ + state.ts.heap[SMALLEST] = node++; + pqdownheap(state,tree, SMALLEST); + + } while (state.ts.heap_len >= 2); + + state.ts.heap[--state.ts.heap_max] = state.ts.heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(state,(tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes (state,(ct_data *)tree, max_code); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ +void scan_tree (TState &state,ct_data *tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].dl.len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].dl.len = (ush)-1; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].dl.len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + state.ts.bl_tree[curlen].fc.freq = (ush)(state.ts.bl_tree[curlen].fc.freq + count); + } else if (curlen != 0) { + if (curlen != prevlen) state.ts.bl_tree[curlen].fc.freq++; + state.ts.bl_tree[REP_3_6].fc.freq++; + } else if (count <= 10) { + state.ts.bl_tree[REPZ_3_10].fc.freq++; + } else { + state.ts.bl_tree[REPZ_11_138].fc.freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +void send_tree (TState &state, ct_data *tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].dl.len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].dl.len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].dl.len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(state, curlen, state.ts.bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(state, curlen, state.ts.bl_tree); count--; + } + Assert(state,count >= 3 && count <= 6, " 3_6?"); + send_code(state,REP_3_6, state.ts.bl_tree); send_bits(state,count-3, 2); + + } else if (count <= 10) { + send_code(state,REPZ_3_10, state.ts.bl_tree); send_bits(state,count-3, 3); + + } else { + send_code(state,REPZ_11_138, state.ts.bl_tree); send_bits(state,count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +int build_bl_tree(TState &state) +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(state,(ct_data *)state.ts.dyn_ltree, state.ts.l_desc.max_code); + scan_tree(state,(ct_data *)state.ts.dyn_dtree, state.ts.d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(state,(tree_desc *)(&state.ts.bl_desc)); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (state.ts.bl_tree[bl_order[max_blindex]].dl.len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + state.ts.opt_len += 3*(max_blindex+1) + 5+5+4; + Trace("\ndyn trees: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +void send_all_trees(TState &state,int lcodes, int dcodes, int blcodes) +{ + int rank; /* index in bl_order */ + + Assert(state,lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert(state,lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Trace("\nbl counts: "); + send_bits(state,lcodes-257, 5); + /* not +255 as stated in appnote.txt 1.93a or -256 in 2.04c */ + send_bits(state,dcodes-1, 5); + send_bits(state,blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Trace("\nbl code %2d ", bl_order[rank]); + send_bits(state,state.ts.bl_tree[bl_order[rank]].dl.len, 3); + } + Trace("\nbl tree: sent %ld", state.bs.bits_sent); + + send_tree(state,(ct_data *)state.ts.dyn_ltree, lcodes-1); /* send the literal tree */ + Trace("\nlit tree: sent %ld", state.bs.bits_sent); + + send_tree(state,(ct_data *)state.ts.dyn_dtree, dcodes-1); /* send the distance tree */ + Trace("\ndist tree: sent %ld", state.bs.bits_sent); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length (in bytes) for the file so far. + */ +ulg flush_block(TState &state,char *buf, ulg stored_len, int eof) +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex; /* index of last bit length code of non zero freq */ + + state.ts.flag_buf[state.ts.last_flags] = state.ts.flags; /* Save the flags for the last 8 items */ + + /* Check if the file is ascii or binary */ + if (*state.ts.file_type == (ush)UNKNOWN) set_file_type(state); + + /* Construct the literal and distance trees */ + build_tree(state,(tree_desc *)(&state.ts.l_desc)); + Trace("\nlit data: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + + build_tree(state,(tree_desc *)(&state.ts.d_desc)); + Trace("\ndist data: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(state); + + /* Determine the best encoding. Compute first the block length in bytes */ + opt_lenb = (state.ts.opt_len+3+7)>>3; + static_lenb = (state.ts.static_len+3+7)>>3; + state.ts.input_len += stored_len; /* for debugging only */ + + Trace("\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ", + opt_lenb, state.ts.opt_len, static_lenb, state.ts.static_len, stored_len, + state.ts.last_lit, state.ts.last_dist); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + // Originally, zip allowed the file to be transformed from a compressed + // into a stored file in the case where compression failed, there + // was only one block, and it was allowed to change. I've removed this + // possibility since the code's cleaner if no changes are allowed. + //if (stored_len <= opt_lenb && eof && state.ts.cmpr_bytelen == 0L + // && state.ts.cmpr_len_bits == 0L && state.seekable) + //{ // && state.ts.file_method != NULL + // // Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: + // Assert(state,buf!=NULL,"block vanished"); + // copy_block(state,buf, (unsigned)stored_len, 0); // without header + // state.ts.cmpr_bytelen = stored_len; + // Assert(state,false,"unimplemented *state.ts.file_method = STORE;"); + // //*state.ts.file_method = STORE; + //} + //else + if (stored_len+4 <= opt_lenb && buf != (char*)NULL) { + /* 4: two words for the lengths */ + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + send_bits(state,(STORED_BLOCK<<1)+eof, 3); /* send block type */ + state.ts.cmpr_bytelen += ((state.ts.cmpr_len_bits + 3 + 7) >> 3) + stored_len + 4; + state.ts.cmpr_len_bits = 0L; + + copy_block(state,buf, (unsigned)stored_len, 1); /* with header */ + } + else if (static_lenb == opt_lenb) { + send_bits(state,(STATIC_TREES<<1)+eof, 3); + compress_block(state,(ct_data *)state.ts.static_ltree, (ct_data *)state.ts.static_dtree); + state.ts.cmpr_len_bits += 3 + state.ts.static_len; + state.ts.cmpr_bytelen += state.ts.cmpr_len_bits >> 3; + state.ts.cmpr_len_bits &= 7L; + } + else { + send_bits(state,(DYN_TREES<<1)+eof, 3); + send_all_trees(state,state.ts.l_desc.max_code+1, state.ts.d_desc.max_code+1, max_blindex+1); + compress_block(state,(ct_data *)state.ts.dyn_ltree, (ct_data *)state.ts.dyn_dtree); + state.ts.cmpr_len_bits += 3 + state.ts.opt_len; + state.ts.cmpr_bytelen += state.ts.cmpr_len_bits >> 3; + state.ts.cmpr_len_bits &= 7L; + } + Assert(state,((state.ts.cmpr_bytelen << 3) + state.ts.cmpr_len_bits) == state.bs.bits_sent, "bad compressed size"); + init_block(state); + + if (eof) { + // Assert(state,input_len == isize, "bad input size"); + bi_windup(state); + state.ts.cmpr_len_bits += 7; /* align on byte boundary */ + } + Trace("\n"); + + return state.ts.cmpr_bytelen + (state.ts.cmpr_len_bits >> 3); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ct_tally (TState &state,int dist, int lc) +{ + state.ts.l_buf[state.ts.last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + state.ts.dyn_ltree[lc].fc.freq++; + } else { + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert(state,(ush)dist < (ush)MAX_DIST && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "ct_tally: bad match"); + + state.ts.dyn_ltree[state.ts.length_code[lc]+LITERALS+1].fc.freq++; + state.ts.dyn_dtree[d_code(dist)].fc.freq++; + + state.ts.d_buf[state.ts.last_dist++] = (ush)dist; + state.ts.flags |= state.ts.flag_bit; + } + state.ts.flag_bit <<= 1; + + /* Output the flags if they fill a byte: */ + if ((state.ts.last_lit & 7) == 0) { + state.ts.flag_buf[state.ts.last_flags++] = state.ts.flags; + state.ts.flags = 0, state.ts.flag_bit = 1; + } + /* Try to guess if it is profitable to stop the current block here */ + if (state.level > 2 && (state.ts.last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)state.ts.last_lit*8L; + ulg in_length = (ulg)state.ds.strstart-state.ds.block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)state.ts.dyn_dtree[dcode].fc.freq*(5L+extra_dbits[dcode]); + } + out_length >>= 3; + Trace("\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ", + state.ts.last_lit, state.ts.last_dist, in_length, out_length, + 100L - out_length*100L/in_length); + if (state.ts.last_dist < state.ts.last_lit/2 && out_length < in_length/2) return 1; + } + return (state.ts.last_lit == LIT_BUFSIZE-1 || state.ts.last_dist == DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +void compress_block(TState &state,ct_data *ltree, ct_data *dtree) +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned dx = 0; /* running index in d_buf */ + unsigned fx = 0; /* running index in flag_buf */ + uch flag = 0; /* current flags */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (state.ts.last_lit != 0) do { + if ((lx & 7) == 0) flag = state.ts.flag_buf[fx++]; + lc = state.ts.l_buf[lx++]; + if ((flag & 1) == 0) { + send_code(state,lc, ltree); /* send a literal byte */ + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = state.ts.length_code[lc]; + send_code(state,code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= state.ts.base_length[code]; + send_bits(state,lc, extra); /* send the extra length bits */ + } + dist = state.ts.d_buf[dx++]; + /* Here, dist is the match distance - 1 */ + code = d_code(dist); + Assert(state,code < D_CODES, "bad d_code"); + + send_code(state,code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= state.ts.base_dist[code]; + send_bits(state,dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + flag >>= 1; + } while (lx < state.ts.last_lit); + + send_code(state,END_BLOCK, ltree); +} + +/* =========================================================================== + * Set the file type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +void set_file_type(TState &state) +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += state.ts.dyn_ltree[n++].fc.freq; + while (n < 128) ascii_freq += state.ts.dyn_ltree[n++].fc.freq; + while (n < LITERALS) bin_freq += state.ts.dyn_ltree[n++].fc.freq; + *state.ts.file_type = (ush)(bin_freq > (ascii_freq >> 2) ? BINARY : ASCII); +} + + +/* =========================================================================== + * Initialize the bit string routines. + */ +void bi_init (TState &state,char *tgt_buf, unsigned tgt_size, int flsh_allowed) +{ + state.bs.out_buf = tgt_buf; + state.bs.out_size = tgt_size; + state.bs.out_offset = 0; + state.bs.flush_flg = flsh_allowed; + + state.bs.bi_buf = 0; + state.bs.bi_valid = 0; + state.bs.bits_sent = 0L; +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +void send_bits(TState &state,int value, int length) +{ + Assert(state,length > 0 && length <= 15, "invalid length"); + state.bs.bits_sent += (ulg)length; + /* If not enough room in bi_buf, use (bi_valid) bits from bi_buf and + * (Buf_size - bi_valid) bits from value to flush the filled bi_buf, + * then fill in the rest of (value), leaving (length - (Buf_size-bi_valid)) + * unused bits in bi_buf. + */ + state.bs.bi_buf |= (value << state.bs.bi_valid); + state.bs.bi_valid += length; + if (state.bs.bi_valid > (int)Buf_size) { + PUTSHORT(state,state.bs.bi_buf); + state.bs.bi_valid -= Buf_size; + state.bs.bi_buf = (unsigned)value >> (length - state.bs.bi_valid); + } +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +unsigned bi_reverse(unsigned code, int len) +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Write out any remaining bits in an incomplete byte. + */ +void bi_windup(TState &state) +{ + if (state.bs.bi_valid > 8) { + PUTSHORT(state,state.bs.bi_buf); + } else if (state.bs.bi_valid > 0) { + PUTBYTE(state,state.bs.bi_buf); + } + if (state.bs.flush_flg) { + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); + } + state.bs.bi_buf = 0; + state.bs.bi_valid = 0; + state.bs.bits_sent = (state.bs.bits_sent+7) & ~7; +} + +/* =========================================================================== + * Copy a stored block to the zip file, storing first the length and its + * one's complement if requested. + */ +void copy_block(TState &state, char *block, unsigned len, int header) +{ + bi_windup(state); /* align on byte boundary */ + + if (header) { + PUTSHORT(state,(ush)len); + PUTSHORT(state,(ush)~len); + state.bs.bits_sent += 2*16; + } + if (state.bs.flush_flg) { + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); + state.bs.out_offset = len; + state.flush_outbuf(state.param,block, &state.bs.out_offset); + } else if (state.bs.out_offset + len > state.bs.out_size) { + Assert(state,false,"output buffer too small for in-memory compression"); + } else { + memcpy(state.bs.out_buf + state.bs.out_offset, block, len); + state.bs.out_offset += len; + } + state.bs.bits_sent += (ulg)len<<3; +} + + + + + + + + +/* =========================================================================== + * Prototypes for functions. + */ + +void fill_window (TState &state); +ulg deflate_fast (TState &state); + +int longest_match (TState &state,IPos cur_match); + + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(h,c) (h = (((h)< 0 if the input file is already read or + * mmap'ed in the window[] array, 0 otherwise. In the first case, + * window_size is sufficient to contain the whole input file plus + * MIN_LOOKAHEAD bytes (to avoid referencing memory beyond the end + * of window[] when looking for matches towards the end). + */ +void lm_init (TState &state, int pack_level, ush *flags) +{ + register unsigned j; + + Assert(state,pack_level>=1 && pack_level<=8,"bad pack level"); + + /* Do not slide the window if the whole input is already in memory + * (window_size > 0) + */ + state.ds.sliding = 0; + if (state.ds.window_size == 0L) { + state.ds.sliding = 1; + state.ds.window_size = (ulg)2L*WSIZE; + } + + /* Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ + state.ds.head[HASH_SIZE-1] = NIL; + memset((char*)state.ds.head, NIL, (unsigned)(HASH_SIZE-1)*sizeof(*state.ds.head)); + + /* Set the default configuration parameters: + */ + state.ds.max_lazy_match = configuration_table[pack_level].max_lazy; + state.ds.good_match = configuration_table[pack_level].good_length; + state.ds.nice_match = configuration_table[pack_level].nice_length; + state.ds.max_chain_length = configuration_table[pack_level].max_chain; + if (pack_level <= 2) { + *flags |= FAST; + } else if (pack_level >= 8) { + *flags |= SLOW; + } + /* ??? reduce max_chain_length for binary files */ + + state.ds.strstart = 0; + state.ds.block_start = 0L; + + j = WSIZE; + j <<= 1; // Can read 64K in one step + state.ds.lookahead = state.readfunc(state, (char*)state.ds.window, j); + + if (state.ds.lookahead == 0 || state.ds.lookahead == (unsigned)EOF) { + state.ds.eofile = 1, state.ds.lookahead = 0; + return; + } + state.ds.eofile = 0; + /* Make sure that we always have enough lookahead. This is important + * if input comes from a device such as a tty. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + + state.ds.ins_h = 0; + for (j=0; j= 1 + */ +// For 80x86 and 680x0 and ARM, an optimized version is in match.asm or +// match.S. The code is functionally equivalent, so you can use the C version +// if desired. Which I do so desire! +int longest_match(TState &state,IPos cur_match) +{ + unsigned chain_length = state.ds.max_chain_length; /* max hash chain length */ + register uch far *scan = state.ds.window + state.ds.strstart; /* current string */ + register uch far *match; /* matched string */ + register int len; /* length of current match */ + int best_len = state.ds.prev_length; /* best match length so far */ + IPos limit = state.ds.strstart > (IPos)MAX_DIST ? state.ds.strstart - (IPos)MAX_DIST : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + Assert(state,HASH_BITS>=8 && MAX_MATCH==258,"Code too clever"); + + + + register uch far *strend = state.ds.window + state.ds.strstart + MAX_MATCH; + register uch scan_end1 = scan[best_len-1]; + register uch scan_end = scan[best_len]; + + /* Do not waste too much time if we already have a good match: */ + if (state.ds.prev_length >= state.ds.good_match) { + chain_length >>= 2; + } + + Assert(state,state.ds.strstart <= state.ds.window_size-MIN_LOOKAHEAD, "insufficient lookahead"); + + do { + Assert(state,cur_match < state.ds.strstart, "no future"); + match = state.ds.window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(state,scan <= state.ds.window+(unsigned)(state.ds.window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + + if (len > best_len) { + state.ds.match_start = cur_match; + best_len = len; + if (len >= state.ds.nice_match) break; + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; + } + } while ((cur_match = state.ds.prev[cur_match & WMASK]) > limit + && --chain_length != 0); + + return best_len; +} + + + +#define check_match(state,start, match, length) +// or alternatively... +//void check_match(TState &state,IPos start, IPos match, int length) +//{ // check that the match is indeed a match +// if (memcmp((char*)state.ds.window + match, +// (char*)state.ds.window + start, length) != EQUAL) { +// fprintf(stderr, +// " start %d, match %d, length %d\n", +// start, match, length); +// error("invalid match"); +// } +// if (state.verbose > 1) { +// fprintf(stderr,"\\[%d,%d]", start-match, length); +// do { fprintf(stdout,"%c",state.ds.window[start++]); } while (--length != 0); +// } +//} + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or eofile is set; file reads are + * performed for at least two bytes (required for the translate_eol option). + */ +void fill_window(TState &state) +{ + register unsigned n, m; + unsigned more; /* Amount of free space at the end of the window. */ + + do { + more = (unsigned)(state.ds.window_size - (ulg)state.ds.lookahead - (ulg)state.ds.strstart); + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == (unsigned)EOF) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + + /* For MMAP or BIG_MEM, the whole input file is already in memory so + * we must not perform sliding. We must however call (*read_buf)() in + * order to compute the crc, update lookahead and possibly set eofile. + */ + } else if (state.ds.strstart >= WSIZE+MAX_DIST && state.ds.sliding) { + + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + memcpy((char*)state.ds.window, (char*)state.ds.window+WSIZE, (unsigned)WSIZE); + state.ds.match_start -= WSIZE; + state.ds.strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */ + + state.ds.block_start -= (long) WSIZE; + + for (n = 0; n < HASH_SIZE; n++) { + m = state.ds.head[n]; + state.ds.head[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + } + for (n = 0; n < WSIZE; n++) { + m = state.ds.prev[n]; + state.ds.prev[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } + more += WSIZE; + } + if (state.ds.eofile) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the MMAP or BIG_MEM case (not yet supported in gzip), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(state,more >= 2, "more < 2"); + + n = state.readfunc(state, (char*)state.ds.window+state.ds.strstart+state.ds.lookahead, more); + + if (n == 0 || n == (unsigned)EOF) { + state.ds.eofile = 1; + } else { + state.ds.lookahead += n; + } + } while (state.ds.lookahead < MIN_LOOKAHEAD && !state.ds.eofile); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK(state,eof) \ + flush_block(state,state.ds.block_start >= 0L ? (char*)&state.ds.window[(unsigned)state.ds.block_start] : \ + (char*)NULL, (long)state.ds.strstart - state.ds.block_start, (eof)) + +/* =========================================================================== + * Processes a new input file and return its compressed length. This + * function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +ulg deflate_fast(TState &state) +{ + IPos hash_head = NIL; /* head of the hash chain */ + int flush; /* set if current block must be flushed */ + unsigned match_length = 0; /* length of best match */ + + state.ds.prev_length = MIN_MATCH-1; + while (state.ds.lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (state.ds.lookahead >= MIN_MATCH) + INSERT_STRING(state.ds.strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && state.ds.strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + /* Do not look for matches beyond the end of the input. + * This is necessary to make deflate deterministic. + */ + if ((unsigned)state.ds.nice_match > state.ds.lookahead) state.ds.nice_match = (int)state.ds.lookahead; + match_length = longest_match (state,hash_head); + /* longest_match() sets match_start */ + if (match_length > state.ds.lookahead) match_length = state.ds.lookahead; + } + if (match_length >= MIN_MATCH) { + check_match(state,state.ds.strstart, state.ds.match_start, match_length); + + flush = ct_tally(state,state.ds.strstart-state.ds.match_start, match_length - MIN_MATCH); + + state.ds.lookahead -= match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (match_length <= state.ds.max_insert_length + && state.ds.lookahead >= MIN_MATCH) { + match_length--; /* string at strstart already in hash table */ + do { + state.ds.strstart++; + INSERT_STRING(state.ds.strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--match_length != 0); + state.ds.strstart++; + } else { + state.ds.strstart += match_length; + match_length = 0; + state.ds.ins_h = state.ds.window[state.ds.strstart]; + UPDATE_HASH(state.ds.ins_h, state.ds.window[state.ds.strstart+1]); + Assert(state,MIN_MATCH==3,"Call UPDATE_HASH() MIN_MATCH-3 more times"); + } + } else { + /* No match, output a literal byte */ + flush = ct_tally (state,0, state.ds.window[state.ds.strstart]); + state.ds.lookahead--; + state.ds.strstart++; + } + if (flush) FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart; + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + } + return FLUSH_BLOCK(state,1); /* eof */ +} + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +ulg deflate(TState &state) +{ + IPos hash_head = NIL; /* head of hash chain */ + IPos prev_match; /* previous match */ + int flush; /* set if current block must be flushed */ + int match_available = 0; /* set if previous match exists */ + register unsigned match_length = MIN_MATCH-1; /* length of best match */ + + if (state.level <= 3) return deflate_fast(state); /* optimized for speed */ + + /* Process the input block. */ + while (state.ds.lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (state.ds.lookahead >= MIN_MATCH) + INSERT_STRING(state.ds.strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + */ + state.ds.prev_length = match_length, prev_match = state.ds.match_start; + match_length = MIN_MATCH-1; + + if (hash_head != NIL && state.ds.prev_length < state.ds.max_lazy_match && + state.ds.strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + /* Do not look for matches beyond the end of the input. + * This is necessary to make deflate deterministic. + */ + if ((unsigned)state.ds.nice_match > state.ds.lookahead) state.ds.nice_match = (int)state.ds.lookahead; + match_length = longest_match (state,hash_head); + /* longest_match() sets match_start */ + if (match_length > state.ds.lookahead) match_length = state.ds.lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (match_length == MIN_MATCH && state.ds.strstart-state.ds.match_start > TOO_FAR){ + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (state.ds.prev_length >= MIN_MATCH && match_length <= state.ds.prev_length) { + unsigned max_insert = state.ds.strstart + state.ds.lookahead - MIN_MATCH; + check_match(state,state.ds.strstart-1, prev_match, state.ds.prev_length); + flush = ct_tally(state,state.ds.strstart-1-prev_match, state.ds.prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + state.ds.lookahead -= state.ds.prev_length-1; + state.ds.prev_length -= 2; + do { + if (++state.ds.strstart <= max_insert) { + INSERT_STRING(state.ds.strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } + } while (--state.ds.prev_length != 0); + state.ds.strstart++; + match_available = 0; + match_length = MIN_MATCH-1; + + if (flush) FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart; + + } else if (match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + if (ct_tally (state,0, state.ds.window[state.ds.strstart-1])) { + FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart; + } + state.ds.strstart++; + state.ds.lookahead--; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + match_available = 1; + state.ds.strstart++; + state.ds.lookahead--; + } +// Assert(state,strstart <= isize && lookahead <= isize, "a bit too far"); + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + } + if (match_available) ct_tally (state,0, state.ds.window[state.ds.strstart-1]); + + return FLUSH_BLOCK(state,1); /* eof */ +} + + + + + + + + + + + + +int putlocal(struct zlist far *z, WRITEFUNC wfunc,void *param) +{ // Write a local header described by *z to file *f. Return a ZE_ error code. + PUTLG(LOCSIG, f); + PUTSH(z->ver, f); + PUTSH(z->lflg, f); + PUTSH(z->how, f); + PUTLG(z->tim, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + PUTSH(z->nam, f); + PUTSH(z->ext, f); + size_t res = (size_t)wfunc(param, z->iname, (unsigned int)z->nam); + if (res!=z->nam) return ZE_TEMP; + if (z->ext) + { res = (size_t)wfunc(param, z->extra, (unsigned int)z->ext); + if (res!=z->ext) return ZE_TEMP; + } + return ZE_OK; +} + +int putextended(struct zlist far *z, WRITEFUNC wfunc, void *param) +{ // Write an extended local header described by *z to file *f. Returns a ZE_ code + PUTLG(EXTLOCSIG, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + return ZE_OK; +} + +int putcentral(struct zlist far *z, WRITEFUNC wfunc, void *param) +{ // Write a central header entry of *z to file *f. Returns a ZE_ code. + PUTLG(CENSIG, f); + PUTSH(z->vem, f); + PUTSH(z->ver, f); + PUTSH(z->flg, f); + PUTSH(z->how, f); + PUTLG(z->tim, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + PUTSH(z->nam, f); + PUTSH(z->cext, f); + PUTSH(z->com, f); + PUTSH(z->dsk, f); + PUTSH(z->att, f); + PUTLG(z->atx, f); + PUTLG(z->off, f); + if ((size_t)wfunc(param, z->iname, (unsigned int)z->nam) != z->nam || + (z->cext && (size_t)wfunc(param, z->cextra, (unsigned int)z->cext) != z->cext) || + (z->com && (size_t)wfunc(param, z->comment, (unsigned int)z->com) != z->com)) + return ZE_TEMP; + return ZE_OK; +} + + +int putend(int n, ulg s, ulg c, extent m, char *z, WRITEFUNC wfunc, void *param) +{ // write the end of the central-directory-data to file *f. + PUTLG(ENDSIG, f); + PUTSH(0, f); + PUTSH(0, f); + PUTSH(n, f); + PUTSH(n, f); + PUTLG(s, f); + PUTLG(c, f); + PUTSH(m, f); + // Write the comment, if any + if (m && wfunc(param, z, (unsigned int)m) != m) return ZE_TEMP; + return ZE_OK; +} + + + + + + +const ulg crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +#define CRC32(c, b) (crc_table[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) +#define DO1(buf) crc = CRC32(crc, *buf++) +#define DO2(buf) DO1(buf); DO1(buf) +#define DO4(buf) DO2(buf); DO2(buf) +#define DO8(buf) DO4(buf); DO4(buf) + +ulg crc32(ulg crc, const uch *buf, extent len) +{ if (buf==NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {DO8(buf); len -= 8;} + if (len) do {DO1(buf);} while (--len); + return crc ^ 0xffffffffL; // (instead of ~c for 64-bit machines) +} + + +void update_keys(unsigned long *keys, char c) +{ keys[0] = CRC32(keys[0],c); + keys[1] += keys[0] & 0xFF; + keys[1] = keys[1]*134775813L +1; + keys[2] = CRC32(keys[2], keys[1] >> 24); +} +char decrypt_byte(unsigned long *keys) +{ unsigned temp = ((unsigned)keys[2] & 0xffff) | 2; + return (char)(((temp * (temp ^ 1)) >> 8) & 0xff); +} +char zencode(unsigned long *keys, char c) +{ int t=decrypt_byte(keys); + update_keys(keys,c); + return (char)(t^c); +} + + + + + + + +bool HasZipSuffix(const TCHAR *fn) +{ const TCHAR *ext = fn+_tcslen(fn); + while (ext>fn && *ext!='.') ext--; + if (ext==fn && *ext!='.') return false; + if (_tcsicmp(ext,_T(".Z"))==0) return true; + if (_tcsicmp(ext,_T(".zip"))==0) return true; + if (_tcsicmp(ext,_T(".zoo"))==0) return true; + if (_tcsicmp(ext,_T(".arc"))==0) return true; + if (_tcsicmp(ext,_T(".lzh"))==0) return true; + if (_tcsicmp(ext,_T(".arj"))==0) return true; + if (_tcsicmp(ext,_T(".gz"))==0) return true; + if (_tcsicmp(ext,_T(".tgz"))==0) return true; + return false; +} + + +lutime_t filetime2timet(const FILETIME ft) +{ __int64 i = *(__int64*)&ft; + return (lutime_t)((i-116444736000000000)/10000000); +} + +void filetime2dosdatetime(const FILETIME ft, WORD *dosdate,WORD *dostime) +{ // date: bits 0-4 are day of month 1-31. Bits 5-8 are month 1..12. Bits 9-15 are year-1980 + // time: bits 0-4 are seconds/2, bits 5-10 are minute 0..59. Bits 11-15 are hour 0..23 + SYSTEMTIME st; FileTimeToSystemTime(&ft,&st); + *dosdate = (WORD)(((st.wYear-1980)&0x7f) << 9); + *dosdate |= (WORD)((st.wMonth&0xf) << 5); + *dosdate |= (WORD)((st.wDay&0x1f)); + *dostime = (WORD)((st.wHour&0x1f) << 11); + *dostime |= (WORD)((st.wMinute&0x3f) << 5); + *dostime |= (WORD)((st.wSecond*2)&0x1f); +} + + +ZRESULT GetFileInfo(HANDLE hf, ulg *attr, long *size, iztimes *times, ulg *timestamp) +{ // The handle must be a handle to a file + // The date and time is returned in a long with the date most significant to allow + // unsigned integer comparison of absolute times. The attributes have two + // high bytes unix attr, and two low bytes a mapping of that to DOS attr. + //struct stat s; int res=stat(fn,&s); if (res!=0) return false; + // translate windows file attributes into zip ones. + BY_HANDLE_FILE_INFORMATION bhi; BOOL res=GetFileInformationByHandle(hf,&bhi); + if (!res) return ZR_NOFILE; + DWORD fa=bhi.dwFileAttributes; ulg a=0; + // Zip uses the lower word for its interpretation of windows stuff + if (fa&FILE_ATTRIBUTE_READONLY) a|=0x01; + if (fa&FILE_ATTRIBUTE_HIDDEN) a|=0x02; + if (fa&FILE_ATTRIBUTE_SYSTEM) a|=0x04; + if (fa&FILE_ATTRIBUTE_DIRECTORY)a|=0x10; + if (fa&FILE_ATTRIBUTE_ARCHIVE) a|=0x20; + // It uses the upper word for standard unix attr, which we manually construct + if (fa&FILE_ATTRIBUTE_DIRECTORY)a|=0x40000000; // directory + else a|=0x80000000; // normal file + a|=0x01000000; // readable + if (fa&FILE_ATTRIBUTE_READONLY) {} else a|=0x00800000; // writeable + // now just a small heuristic to check if it's an executable: + DWORD red, hsize=GetFileSize(hf,NULL); if (hsize>40) + { SetFilePointer(hf,0,NULL,FILE_BEGIN); unsigned short magic; ReadFile(hf,&magic,sizeof(magic),&red,NULL); + SetFilePointer(hf,36,NULL,FILE_BEGIN); unsigned long hpos; ReadFile(hf,&hpos,sizeof(hpos),&red,NULL); + if (magic==0x54AD && hsize>hpos+4+20+28) + { SetFilePointer(hf,hpos,NULL,FILE_BEGIN); unsigned long signature; ReadFile(hf,&signature,sizeof(signature),&red,NULL); + if (signature==IMAGE_DOS_SIGNATURE || signature==IMAGE_OS2_SIGNATURE + || signature==IMAGE_OS2_SIGNATURE_LE || signature==IMAGE_NT_SIGNATURE) + { a |= 0x00400000; // executable + } + } + } + // + if (attr!=NULL) *attr = a; + if (size!=NULL) *size = hsize; + if (times!=NULL) + { // lutime_t is 32bit number of seconds elapsed since 0:0:0GMT, Jan1, 1970. + // but FILETIME is 64bit number of 100-nanosecs since Jan1, 1601 + times->atime = filetime2timet(bhi.ftLastAccessTime); + times->mtime = filetime2timet(bhi.ftLastWriteTime); + times->ctime = filetime2timet(bhi.ftCreationTime); + } + if (timestamp!=NULL) + { WORD dosdate,dostime; + filetime2dosdatetime(bhi.ftLastWriteTime,&dosdate,&dostime); + *timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + } + return ZR_OK; +} + + + + + + + + +class TZip +{ public: + TZip(const char *pwd) : hfout(0),mustclosehfout(false),hmapout(0),zfis(0),obuf(0),hfin(0),writ(0),oerr(false),hasputcen(false),ooffset(0),encwriting(false),encbuf(0),password(0), state(0) {if (pwd!=0 && *pwd!=0) {password=new char[strlen(pwd)+1]; strcpy(password,pwd);}} + ~TZip() {if (state!=0) delete state; state=0; if (encbuf!=0) delete[] encbuf; encbuf=0; if (password!=0) delete[] password; password=0;} + + // These variables say about the file we're writing into + // We can write to pipe, file-by-handle, file-by-name, memory-to-memmapfile + char *password; // keep a copy of the password + HANDLE hfout; // if valid, we'll write here (for files or pipes) + bool mustclosehfout; // if true, we are responsible for closing hfout + HANDLE hmapout; // otherwise, we'll write here (for memmap) + unsigned ooffset; // for hfout, this is where the pointer was initially + ZRESULT oerr; // did a write operation give rise to an error? + unsigned writ; // how far have we written. This is maintained by Add, not write(), to avoid confusion over seeks + bool ocanseek; // can we seek? + char *obuf; // this is where we've locked mmap to view. + unsigned int opos; // current pos in the mmap + unsigned int mapsize; // the size of the map we created + bool hasputcen; // have we yet placed the central directory? + bool encwriting; // if true, then we'll encrypt stuff using 'keys' before we write it to disk + unsigned long keys[3]; // keys are initialised inside Add() + char *encbuf; // if encrypting, then this is a temporary workspace for encrypting the data + unsigned int encbufsize; // (to be used and resized inside write(), and deleted in the destructor) + // + TZipFileInfo *zfis; // each file gets added onto this list, for writing the table at the end + TState *state; // we use just one state object per zip, because it's big (500k) + + ZRESULT Create(void *z,unsigned int len,DWORD flags); + static unsigned sflush(void *param,const char *buf, unsigned *size); + static unsigned swrite(void *param,const char *buf, unsigned size); + unsigned int write(const char *buf,unsigned int size); + bool oseek(unsigned int pos); + ZRESULT GetMemory(void **pbuf, unsigned long *plen); + ZRESULT Close(); + + // some variables to do with the file currently being read: + // I haven't done it object-orientedly here, just put them all + // together, since OO didn't seem to make the design any clearer. + ulg attr; iztimes times; ulg timestamp; // all open_* methods set these + bool iseekable; long isize,ired; // size is not set until close() on pips + ulg crc; // crc is not set until close(). iwrit is cumulative + HANDLE hfin; bool selfclosehf; // for input files and pipes + const char *bufin; unsigned int lenin,posin; // for memory + // and a variable for what we've done with the input: (i.e. compressed it!) + ulg csize; // compressed size, set by the compression routines + // and this is used by some of the compression routines + char buf[16384]; + + + ZRESULT open_file(const TCHAR *fn); + ZRESULT open_handle(HANDLE hf,unsigned int len); + ZRESULT open_mem(void *src,unsigned int len); + ZRESULT open_dir(); + static unsigned sread(TState &s,char *buf,unsigned size); + unsigned read(char *buf, unsigned size); + ZRESULT iclose(); + + ZRESULT ideflate(TZipFileInfo *zfi); + ZRESULT istore(); + + ZRESULT Add(const TCHAR *odstzn, void *src,unsigned int len, DWORD flags); + ZRESULT AddCentral(); + +}; + + + +ZRESULT TZip::Create(void *z,unsigned int len,DWORD flags) +{ if (hfout!=0 || hmapout!=0 || obuf!=0 || writ!=0 || oerr!=ZR_OK || hasputcen) return ZR_NOTINITED; + // + if (flags==ZIP_HANDLE) + { HANDLE hf = (HANDLE)z; + hfout=hf; mustclosehfout=false; +#ifdef DuplicateHandle + BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&hfout,0,FALSE,DUPLICATE_SAME_ACCESS); + if (res) mustclosehandle=true; +#endif + // now we have hfout. Either we duplicated the handle and we close it ourselves + // (while the caller closes h themselves), or we couldn't duplicate it. + DWORD res = SetFilePointer(hfout,0,0,FILE_CURRENT); + ocanseek = (res!=0xFFFFFFFF); + if (ocanseek) ooffset=res; else ooffset=0; + return ZR_OK; + } + else if (flags==ZIP_FILENAME) + { const TCHAR *fn = (const TCHAR*)z; + hfout = CreateFile(fn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); + if (hfout==INVALID_HANDLE_VALUE) {hfout=0; return ZR_NOFILE;} + ocanseek=true; + ooffset=0; + mustclosehfout=true; + return ZR_OK; + } + else if (flags==ZIP_MEMORY) + { unsigned int size = len; + if (size==0) return ZR_MEMSIZE; + if (z!=0) obuf=(char*)z; + else + { hmapout = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,size,NULL); + if (hmapout==NULL) return ZR_NOALLOC; + obuf = (char*)MapViewOfFile(hmapout,FILE_MAP_ALL_ACCESS,0,0,size); + if (obuf==0) {CloseHandle(hmapout); hmapout=0; return ZR_NOALLOC;} + } + ocanseek=true; + opos=0; mapsize=size; + return ZR_OK; + } + else return ZR_ARGS; +} + +unsigned TZip::sflush(void *param,const char *buf, unsigned *size) +{ // static + if (*size==0) return 0; + TZip *zip = (TZip*)param; + unsigned int writ = zip->write(buf,*size); + if (writ!=0) *size=0; + return writ; +} +unsigned TZip::swrite(void *param,const char *buf, unsigned size) +{ // static + if (size==0) return 0; + TZip *zip=(TZip*)param; return zip->write(buf,size); +} +unsigned int TZip::write(const char *buf,unsigned int size) +{ const char *srcbuf=buf; + if (encwriting) + { if (encbuf!=0 && encbufsize=mapsize) {oerr=ZR_MEMSIZE; return 0;} + memcpy(obuf+opos, srcbuf, size); + opos+=size; + return size; + } + else if (hfout!=0) + { DWORD writ; WriteFile(hfout,srcbuf,size,&writ,NULL); + return writ; + } + oerr=ZR_NOTINITED; return 0; +} + +bool TZip::oseek(unsigned int pos) +{ if (!ocanseek) {oerr=ZR_SEEK; return false;} + if (obuf!=0) + { if (pos>=mapsize) {oerr=ZR_MEMSIZE; return false;} + opos=pos; + return true; + } + else if (hfout!=0) + { SetFilePointer(hfout,pos+ooffset,NULL,FILE_BEGIN); + return true; + } + oerr=ZR_NOTINITED; return 0; +} + +ZRESULT TZip::GetMemory(void **pbuf, unsigned long *plen) +{ // When the user calls GetMemory, they're presumably at the end + // of all their adding. In any case, we have to add the central + // directory now, otherwise the memory we tell them won't be complete. + if (!hasputcen) AddCentral(); hasputcen=true; + if (pbuf!=NULL) *pbuf=(void*)obuf; + if (plen!=NULL) *plen=writ; + if (obuf==NULL) return ZR_NOTMMAP; + return ZR_OK; +} + +ZRESULT TZip::Close() +{ // if the directory hadn't already been added through a call to GetMemory, + // then we do it now + ZRESULT res=ZR_OK; if (!hasputcen) res=AddCentral(); hasputcen=true; + if (obuf!=0 && hmapout!=0) UnmapViewOfFile(obuf); obuf=0; + if (hmapout!=0) CloseHandle(hmapout); hmapout=0; + if (hfout!=0 && mustclosehfout) CloseHandle(hfout); hfout=0; mustclosehfout=false; + return res; +} + + + + +ZRESULT TZip::open_file(const TCHAR *fn) +{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0; + if (fn==0) return ZR_ARGS; + HANDLE hf = CreateFile(fn,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL); + if (hf==INVALID_HANDLE_VALUE) return ZR_NOFILE; + ZRESULT res = open_handle(hf,0); + if (res!=ZR_OK) {CloseHandle(hf); return res;} + selfclosehf=true; + return ZR_OK; +} +ZRESULT TZip::open_handle(HANDLE hf,unsigned int len) +{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0; + if (hf==0 || hf==INVALID_HANDLE_VALUE) return ZR_ARGS; + DWORD res = SetFilePointer(hfout,0,0,FILE_CURRENT); + if (res!=0xFFFFFFFF) + { ZRESULT res = GetFileInfo(hf,&attr,&isize,×,×tamp); + if (res!=ZR_OK) return res; + SetFilePointer(hf,0,NULL,FILE_BEGIN); // because GetFileInfo will have screwed it up + iseekable=true; hfin=hf; + return ZR_OK; + } + else + { attr= 0x80000000; // just a normal file + isize = -1; // can't know size until at the end + if (len!=0) isize=len; // unless we were told explicitly! + iseekable=false; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st,&ft); + WORD dosdate,dostime; filetime2dosdatetime(ft,&dosdate,&dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + hfin=hf; + return ZR_OK; + } +} +ZRESULT TZip::open_mem(void *src,unsigned int len) +{ hfin=0; bufin=(const char*)src; selfclosehf=false; crc=CRCVAL_INITIAL; ired=0; csize=0; ired=0; + lenin=len; posin=0; + if (src==0 || len==0) return ZR_ARGS; + attr= 0x80000000; // just a normal file + isize = len; + iseekable=true; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st,&ft); + WORD dosdate,dostime; filetime2dosdatetime(ft,&dosdate,&dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + return ZR_OK; +} +ZRESULT TZip::open_dir() +{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0; + attr= 0x41C00010; // a readable writable directory, and again directory + isize = 0; + iseekable=false; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st,&ft); + WORD dosdate,dostime; filetime2dosdatetime(ft,&dosdate,&dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + return ZR_OK; +} + +unsigned TZip::sread(TState &s,char *buf,unsigned size) +{ // static + TZip *zip = (TZip*)s.param; + return zip->read(buf,size); +} + +unsigned TZip::read(char *buf, unsigned size) +{ if (bufin!=0) + { if (posin>=lenin) return 0; // end of input + ulg red = lenin-posin; + if (red>size) red=size; + memcpy(buf, bufin+posin, red); + posin += red; + ired += red; + crc = crc32(crc, (uch*)buf, red); + return red; + } + else if (hfin!=0) + { DWORD red; + BOOL ok = ReadFile(hfin,buf,size,&red,NULL); + if (!ok) return 0; + ired += red; + crc = crc32(crc, (uch*)buf, red); + return red; + } + else {oerr=ZR_NOTINITED; return 0;} +} + +ZRESULT TZip::iclose() +{ if (selfclosehf && hfin!=0) CloseHandle(hfin); hfin=0; + bool mismatch = (isize!=-1 && isize!=ired); + isize=ired; // and crc has been being updated anyway + if (mismatch) return ZR_MISSIZE; + else return ZR_OK; +} + + + +ZRESULT TZip::ideflate(TZipFileInfo *zfi) +{ if (state==0) state=new TState(); + // It's a very big object! 500k! We allocate it on the heap, because PocketPC's + // stack breaks if we try to put it all on the stack. It will be deleted lazily + state->err=0; + state->readfunc=sread; state->flush_outbuf=sflush; + state->param=this; state->level=8; state->seekable=iseekable; state->err=NULL; + // the following line will make ct_init realise it has to perform the init + state->ts.static_dtree[0].dl.len = 0; + // Thanks to Alvin77 for this crucial fix: + state->ds.window_size=0; + // I think that covers everything that needs to be initted. + // + bi_init(*state,buf, sizeof(buf), TRUE); // it used to be just 1024-size, not 16384 as here + ct_init(*state,&zfi->att); + lm_init(*state,state->level, &zfi->flg); + ulg sz = deflate(*state); + csize=sz; + ZRESULT r=ZR_OK; if (state->err!=NULL) r=ZR_FLATE; + return r; +} + +ZRESULT TZip::istore() +{ ulg size=0; + for (;;) + { unsigned int cin=read(buf,16384); if (cin<=0 || cin==(unsigned int)EOF) break; + unsigned int cout = write(buf,cin); if (cout!=cin) return ZR_MISSIZE; + size += cin; + } + csize=size; + return ZR_OK; +} + + + + + +bool has_seeded=false; +ZRESULT TZip::Add(const TCHAR *odstzn, void *src,unsigned int len, DWORD flags) +{ if (oerr) return ZR_FAILED; + if (hasputcen) return ZR_ENDED; + + // if we use password encryption, then every isize and csize is 12 bytes bigger + int passex=0; if (password!=0 && flags!=ZIP_FOLDER) passex=12; + + // zip has its own notion of what its names should look like: i.e. dir/file.stuff + TCHAR dstzn[MAX_PATH]; _tcscpy(dstzn,odstzn); + if (*dstzn==0) return ZR_ARGS; + TCHAR *d=dstzn; while (*d!=0) {if (*d=='\\') *d='/'; d++;} + bool isdir = (flags==ZIP_FOLDER); + bool needs_trailing_slash = (isdir && dstzn[_tcslen(dstzn)-1]!='/'); + int method=DEFLATE; if (isdir || HasZipSuffix(dstzn)) method=STORE; + + // now open whatever was our input source: + ZRESULT openres; + if (flags==ZIP_FILENAME) openres=open_file((const TCHAR*)src); + else if (flags==ZIP_HANDLE) openres=open_handle((HANDLE)src,len); + else if (flags==ZIP_MEMORY) openres=open_mem(src,len); + else if (flags==ZIP_FOLDER) openres=open_dir(); + else return ZR_ARGS; + if (openres!=ZR_OK) return openres; + + // A zip "entry" consists of a local header (which includes the file name), + // then the compressed data, and possibly an extended local header. + + // Initialize the local header + TZipFileInfo zfi; zfi.nxt=NULL; + strcpy(zfi.name,""); +#ifdef UNICODE + WideCharToMultiByte(CP_UTF8,0,dstzn,-1,zfi.iname,MAX_PATH,0,0); +#else + strcpy(zfi.iname,dstzn); +#endif + zfi.nam=strlen(zfi.iname); + if (needs_trailing_slash) {strcat(zfi.iname,"/"); zfi.nam++;} + strcpy(zfi.zname,""); + zfi.extra=NULL; zfi.ext=0; // extra header to go after this compressed data, and its length + zfi.cextra=NULL; zfi.cext=0; // extra header to go in the central end-of-zip directory, and its length + zfi.comment=NULL; zfi.com=0; // comment, and its length + zfi.mark = 1; + zfi.dosflag = 0; + zfi.att = (ush)BINARY; + zfi.vem = (ush)0xB17; // 0xB00 is win32 os-code. 0x17 is 23 in decimal: zip 2.3 + zfi.ver = (ush)20; // Needs PKUNZIP 2.0 to unzip it + zfi.tim = timestamp; + // Even though we write the header now, it will have to be rewritten, since we don't know compressed size or crc. + zfi.crc = 0; // to be updated later + zfi.flg = 8; // 8 means 'there is an extra header'. Assume for the moment that we need it. + if (password!=0 && !isdir) zfi.flg=9; // and 1 means 'password-encrypted' + zfi.lflg = zfi.flg; // to be updated later + zfi.how = (ush)method; // to be updated later + zfi.siz = (ulg)(method==STORE && isize>=0 ? isize+passex : 0); // to be updated later + zfi.len = (ulg)(isize); // to be updated later + zfi.dsk = 0; + zfi.atx = attr; + zfi.off = writ+ooffset; // offset within file of the start of this local record + // stuff the 'times' structure into zfi.extra + + // nb. apparently there's a problem with PocketPC CE(zip)->CE(unzip) fails. And removing the following block fixes it up. + char xloc[EB_L_UT_SIZE]; zfi.extra=xloc; zfi.ext=EB_L_UT_SIZE; + char xcen[EB_C_UT_SIZE]; zfi.cextra=xcen; zfi.cext=EB_C_UT_SIZE; + xloc[0] = 'U'; + xloc[1] = 'T'; + xloc[2] = EB_UT_LEN(3); // length of data part of e.f. + xloc[3] = 0; + xloc[4] = EB_UT_FL_MTIME | EB_UT_FL_ATIME | EB_UT_FL_CTIME; + xloc[5] = (char)(times.mtime); + xloc[6] = (char)(times.mtime >> 8); + xloc[7] = (char)(times.mtime >> 16); + xloc[8] = (char)(times.mtime >> 24); + xloc[9] = (char)(times.atime); + xloc[10] = (char)(times.atime >> 8); + xloc[11] = (char)(times.atime >> 16); + xloc[12] = (char)(times.atime >> 24); + xloc[13] = (char)(times.ctime); + xloc[14] = (char)(times.ctime >> 8); + xloc[15] = (char)(times.ctime >> 16); + xloc[16] = (char)(times.ctime >> 24); + memcpy(zfi.cextra,zfi.extra,EB_C_UT_SIZE); + zfi.cextra[EB_LEN] = EB_UT_LEN(1); + + + // (1) Start by writing the local header: + int r = putlocal(&zfi,swrite,this); + if (r!=ZE_OK) {iclose(); return ZR_WRITE;} + writ += 4 + LOCHEAD + (unsigned int)zfi.nam + (unsigned int)zfi.ext; + if (oerr!=ZR_OK) {iclose(); return oerr;} + + // (1.5) if necessary, write the encryption header + keys[0]=305419896L; + keys[1]=591751049L; + keys[2]=878082192L; + for (const char *cp=password; cp!=0 && *cp!=0; cp++) update_keys(keys,*cp); + // generate some random bytes + if (!has_seeded) srand(GetTickCount()^(unsigned long)GetDesktopWindow()); + char encbuf[12]; for (int i=0; i<12; i++) encbuf[i]=(char)((rand()>>7)&0xff); + encbuf[11] = (char)((zfi.tim>>8)&0xff); + for (int ei=0; ei<12; ei++) encbuf[ei]=zencode(keys,encbuf[ei]); + if (password!=0 && !isdir) {swrite(this,encbuf,12); writ+=12;} + + //(2) Write deflated/stored file to zip file + ZRESULT writeres=ZR_OK; + encwriting = (password!=0 && !isdir); // an object member variable to say whether we write to disk encrypted + if (!isdir && method==DEFLATE) writeres=ideflate(&zfi); + else if (!isdir && method==STORE) writeres=istore(); + else if (isdir) csize=0; + encwriting = false; + iclose(); + writ += csize; + if (oerr!=ZR_OK) return oerr; + if (writeres!=ZR_OK) return ZR_WRITE; + + // (3) Either rewrite the local header with correct information... + bool first_header_has_size_right = (zfi.siz==csize+passex); + zfi.crc = crc; + zfi.siz = csize+passex; + zfi.len = isize; + if (ocanseek && (password==0 || isdir)) + { zfi.how = (ush)method; + if ((zfi.flg & 1) == 0) zfi.flg &= ~8; // clear the extended local header flag + zfi.lflg = zfi.flg; + // rewrite the local header: + if (!oseek(zfi.off-ooffset)) return ZR_SEEK; + if ((r = putlocal(&zfi, swrite,this)) != ZE_OK) return ZR_WRITE; + if (!oseek(writ)) return ZR_SEEK; + } + else + { // (4) ... or put an updated header at the end + if (zfi.how != (ush) method) return ZR_NOCHANGE; + if (method==STORE && !first_header_has_size_right) return ZR_NOCHANGE; + if ((r = putextended(&zfi, swrite,this)) != ZE_OK) return ZR_WRITE; + writ += 16L; + zfi.flg = zfi.lflg; // if flg modified by inflate, for the central index + } + if (oerr!=ZR_OK) return oerr; + + // Keep a copy of the zipfileinfo, for our end-of-zip directory + char *cextra = new char[zfi.cext]; memcpy(cextra,zfi.cextra,zfi.cext); zfi.cextra=cextra; + TZipFileInfo *pzfi = new TZipFileInfo; memcpy(pzfi,&zfi,sizeof(zfi)); + if (zfis==NULL) zfis=pzfi; + else {TZipFileInfo *z=zfis; while (z->nxt!=NULL) z=z->nxt; z->nxt=pzfi;} + return ZR_OK; +} + +ZRESULT TZip::AddCentral() +{ // write central directory + int numentries = 0; + ulg pos_at_start_of_central = writ; + //ulg tot_unc_size=0, tot_compressed_size=0; + bool okay=true; + for (TZipFileInfo *zfi=zfis; zfi!=NULL; ) + { if (okay) + { int res = putcentral(zfi, swrite,this); + if (res!=ZE_OK) okay=false; + } + writ += 4 + CENHEAD + (unsigned int)zfi->nam + (unsigned int)zfi->cext + (unsigned int)zfi->com; + //tot_unc_size += zfi->len; + //tot_compressed_size += zfi->siz; + numentries++; + // + TZipFileInfo *zfinext = zfi->nxt; + if (zfi->cextra!=0) delete[] zfi->cextra; + delete zfi; + zfi = zfinext; + } + ulg center_size = writ - pos_at_start_of_central; + if (okay) + { int res = putend(numentries, center_size, pos_at_start_of_central+ooffset, 0, NULL, swrite,this); + if (res!=ZE_OK) okay=false; + writ += 4 + ENDHEAD + 0; + } + if (!okay) return ZR_WRITE; + return ZR_OK; +} + + + + + +ZRESULT lasterrorZ=ZR_OK; + +unsigned int FormatZipMessageZ(ZRESULT code, char *buf,unsigned int len) +{ if (code==ZR_RECENT) code=lasterrorZ; + const char *msg="unknown zip result code"; + switch (code) + { case ZR_OK: msg="Success"; break; + case ZR_NODUPH: msg="Culdn't duplicate handle"; break; + case ZR_NOFILE: msg="Couldn't create/open file"; break; + case ZR_NOALLOC: msg="Failed to allocate memory"; break; + case ZR_WRITE: msg="Error writing to file"; break; + case ZR_NOTFOUND: msg="File not found in the zipfile"; break; + case ZR_MORE: msg="Still more data to unzip"; break; + case ZR_CORRUPT: msg="Zipfile is corrupt or not a zipfile"; break; + case ZR_READ: msg="Error reading file"; break; + case ZR_ARGS: msg="Caller: faulty arguments"; break; + case ZR_PARTIALUNZ: msg="Caller: the file had already been partially unzipped"; break; + case ZR_NOTMMAP: msg="Caller: can only get memory of a memory zipfile"; break; + case ZR_MEMSIZE: msg="Caller: not enough space allocated for memory zipfile"; break; + case ZR_FAILED: msg="Caller: there was a previous error"; break; + case ZR_ENDED: msg="Caller: additions to the zip have already been ended"; break; + case ZR_ZMODE: msg="Caller: mixing creation and opening of zip"; break; + case ZR_NOTINITED: msg="Zip-bug: internal initialisation not completed"; break; + case ZR_SEEK: msg="Zip-bug: trying to seek the unseekable"; break; + case ZR_MISSIZE: msg="Zip-bug: the anticipated size turned out wrong"; break; + case ZR_NOCHANGE: msg="Zip-bug: tried to change mind, but not allowed"; break; + case ZR_FLATE: msg="Zip-bug: an internal error during flation"; break; + } + unsigned int mlen=(unsigned int)strlen(msg); + if (buf==0 || len==0) return mlen; + unsigned int n=mlen; if (n+1>len) n=len-1; + strncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + + +typedef struct +{ DWORD flag; + TZip *zip; +} TZipHandleData; + + +HZIP CreateZipInternal(void *z,unsigned int len,DWORD flags, const char *password) +{ TZip *zip = new TZip(password); + lasterrorZ = zip->Create(z,len,flags); + if (lasterrorZ!=ZR_OK) {delete zip; return 0;} + TZipHandleData *han = new TZipHandleData; + han->flag=2; han->zip=zip; return (HZIP)han; +} +HZIP CreateZipHandle(HANDLE h, const char *password) {return CreateZipInternal(h,0,ZIP_HANDLE,password);} +HZIP CreateZip(const TCHAR *fn, const char *password) {return CreateZipInternal((void*)fn,0,ZIP_FILENAME,password);} +HZIP CreateZip(void *z,unsigned int len, const char *password) {return CreateZipInternal(z,len,ZIP_MEMORY,password);} + + +ZRESULT ZipAddInternal(HZIP hz,const TCHAR *dstzn, void *src,unsigned int len, DWORD flags) +{ if (hz==0) {lasterrorZ=ZR_ARGS;return ZR_ARGS;} + TZipHandleData *han = (TZipHandleData*)hz; + if (han->flag!=2) {lasterrorZ=ZR_ZMODE;return ZR_ZMODE;} + TZip *zip = han->zip; + lasterrorZ = zip->Add(dstzn,src,len,flags); + return lasterrorZ; +} +ZRESULT ZipAdd(HZIP hz,const TCHAR *dstzn, const TCHAR *fn) {return ZipAddInternal(hz,dstzn,(void*)fn,0,ZIP_FILENAME);} +ZRESULT ZipAdd(HZIP hz,const TCHAR *dstzn, void *src,unsigned int len) {return ZipAddInternal(hz,dstzn,src,len,ZIP_MEMORY);} +ZRESULT ZipAddHandle(HZIP hz,const TCHAR *dstzn, HANDLE h) {return ZipAddInternal(hz,dstzn,h,0,ZIP_HANDLE);} +ZRESULT ZipAddHandle(HZIP hz,const TCHAR *dstzn, HANDLE h, unsigned int len) {return ZipAddInternal(hz,dstzn,h,len,ZIP_HANDLE);} +ZRESULT ZipAddFolder(HZIP hz,const TCHAR *dstzn) {return ZipAddInternal(hz,dstzn,0,0,ZIP_FOLDER);} + + + +ZRESULT ZipGetMemory(HZIP hz, void **buf, unsigned long *len) +{ if (hz==0) {if (buf!=0) *buf=0; if (len!=0) *len=0; lasterrorZ=ZR_ARGS;return ZR_ARGS;} + TZipHandleData *han = (TZipHandleData*)hz; + if (han->flag!=2) {lasterrorZ=ZR_ZMODE;return ZR_ZMODE;} + TZip *zip = han->zip; + lasterrorZ = zip->GetMemory(buf,len); + return lasterrorZ; +} + +ZRESULT CloseZipZ(HZIP hz) +{ if (hz==0) {lasterrorZ=ZR_ARGS;return ZR_ARGS;} + TZipHandleData *han = (TZipHandleData*)hz; + if (han->flag!=2) {lasterrorZ=ZR_ZMODE;return ZR_ZMODE;} + TZip *zip = han->zip; + lasterrorZ = zip->Close(); + delete zip; + delete han; + return lasterrorZ; +} + +bool IsZipHandleZ(HZIP hz) +{ if (hz==0) return false; + TZipHandleData *han = (TZipHandleData*)hz; + return (han->flag==2); +} + +#endif //WIN32 + diff --git a/src/Makefile.am b/src/Makefile.am index dadb912d..28e6288d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -28,6 +28,7 @@ repsnapper_CPPFLAGS = \ -DLOCALEDIR='"$(localedir)"' \ $(XMLPP_CFLAGS) \ $(OPENMP_CFLAGS) \ + $(LIBZIP_CFLAGS) \ -g -O3 $(WARNING_FLAGS) SHARED_SRC= \ @@ -99,7 +100,7 @@ EXTRA_DIST += $(built_header_make) \ repsnapper_LDFLAGS = $(EXTRA_LDFLAGS) -repsnapper_LDADD = libreprap.la libclipper.la libpoly2tri.la liblmfit.la $(OPENMP_CFLAGS) $(OPENVRML_LIBS) $(GTKMM_LIBS) $(GL_LIBS) $(XMLPP_LIBS) $(BOOST_LDFLAGS) +repsnapper_LDADD = libreprap.la libclipper.la libpoly2tri.la liblmfit.la libamf.la $(OPENMP_CFLAGS) $(OPENVRML_LIBS) $(GTKMM_LIBS) $(GL_LIBS) $(XMLPP_LIBS) $(LIBZIP_LIBS) $(BOOST_LDFLAGS) repsnapperdatadir = $(datadir)/@PACKAGE@ dist_repsnapperdata_DATA = src/repsnapper.ui diff --git a/src/filechooser.cpp b/src/filechooser.cpp index b5e50972..05d0458b 100644 --- a/src/filechooser.cpp +++ b/src/filechooser.cpp @@ -42,6 +42,8 @@ RSFilechooser::RSFilechooser(View * view_) allfiles.add_pattern("*"); modelfiles.set_name(_("Models")); + modelfiles.add_pattern("*.amf"); + modelfiles.add_pattern("*.AMF"); modelfiles.add_pattern("*.stl"); modelfiles.add_pattern("*.STL"); modelfiles.add_pattern("*.svg"); diff --git a/src/files.cpp b/src/files.cpp index 7d0a3895..6c919a14 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -76,7 +76,12 @@ void File::loadTriangles(vector< vector > &triangles, if(_type == ASCII_STL) { // multiple shapes per file load_asciiSTL(triangles, names, max_triangles); - if (names.size() == 1) // if single shape name by filename + if (names.size() == 1) // if single shape name by file + names[0] = name_by_file; + } else if (_type == AMF) { + // multiple shapes per file + load_AMF(triangles, names, max_triangles); + if (names.size() == 1) // if single shape name by file names[0] = name_by_file; } else { // single shape per file @@ -87,13 +92,12 @@ void File::loadTriangles(vector< vector > &triangles, load_binarySTL(triangles[0], max_triangles); } else if (_type == VRML) { load_VRML(triangles[0], max_triangles); - } else if (_type == AMF) { - load_AMF(triangles[0], max_triangles); } else { cerr << _("Unrecognized file - ") << _file->get_parse_name() << endl; cerr << _("Known extensions: ") << "STL, WRL, AMF." << endl; } } + setlocale(LC_NUMERIC, numlocale); setlocale(LC_COLLATE, colllocale); setlocale(LC_CTYPE, ctypelocale); @@ -409,7 +413,6 @@ bool File::parseSTLtriangles_ascii (istream &text, return true; } - bool File::load_VRML(vector &triangles, uint max_triangles) { @@ -485,10 +488,139 @@ bool File::load_VRML(vector &triangles, uint max_triangles) return true; } +#define ENABLE_AMF 1 + +#if ENABLE_AMF +#include "amf/amftools-code/include/AMF_File.h" + class AMFLoader : public AmfFile + { + double _scale; + public: + AMFLoader() : _scale(1.) {}; + ~AMFLoader(){}; + bool open(ustring path) { + bool ok = Load(path); + if (!ok) + cerr << "AMF Error: " << GetLastErrorMsg() << endl; + return ok; + } + Vector3d getVertex(const nMesh &mesh, uint i) const + { + nCoordinates c = mesh.Vertices.VertexList[i].Coordinates; + return Vector3d(_scale * c.X, _scale * c.Y, _scale * c.Z); + } + Triangle getTriangle(const nMesh &mesh, const nTriangle &t) + { + return Triangle(getVertex(mesh, t.v1), + getVertex(mesh, t.v2), + getVertex(mesh, t.v3)); + } + + bool getObjectTriangles(uint onum, vector &triangles) + { + nObject* object = GetObject(onum); + uint nmeshes = object->Meshes.size(); + for (uint m = 0; m < nmeshes; m++) { + const nMesh mesh = object->Meshes[m]; + //cerr << "Units "<< GetUnitsString() << endl; + switch (aUnit) { + case UNIT_M: _scale = 1000.; break; + case UNIT_IN: _scale = 25.4; break; + case UNIT_FT: _scale = 304.8; break; + case UNIT_UM: _scale = 0.001; break; + case UNIT_MM: + default: _scale = 1.; break; + } + uint nvolumes = mesh.Volumes.size(); + for (uint v = 0; v < nvolumes; v++) { + uint ntria = mesh.Volumes[v].Triangles.size(); + for (uint t = 0; t < ntria; t++) { + triangles.push_back( getTriangle(mesh, mesh.Volumes[v].Triangles[t]) ); + } + } + } + return true; + } + + }; + class AMFWriter : public AmfFile + { + public: + AMFWriter() {}; + ~AMFWriter() {}; + + nVertex vertex(const Vector3d &v) const { + return nVertex(v.x(), v.y(), v.z()); + } + + bool AddObject(const vector &triangles, + const ustring name) + { + int num = AmfFile::AddObject(string(name)); + if (num<0) return false; + nObject* object = GetObject(num, true); + if (!object) return false; + nMesh mesh; + for (uint t = 0; t < triangles.size(); t++) { + nVertex A = vertex(triangles[t].A); + mesh.AddVertex(A); + nVertex B = vertex(triangles[t].B); + mesh.AddVertex(B); + nVertex C = vertex(triangles[t].C); + mesh.AddVertex(C); + } + nVolume* vol = mesh.NewVolume(name); + for (uint t = 0; t < triangles.size(); t++) { + nTriangle tr(3*t, 3*t+1, 3*t+2); + vol->AddTriangle(tr); + } + object->Meshes.push_back(mesh); + return true; + } + + }; +#endif + +bool File::load_AMF(vector< vector > &triangles, + vector &names, + uint max_triangles) +{ +#if ENABLE_AMF + AMFLoader amf; + if (!amf.open(_file->get_path())) + return false; + uint nobjs = amf.GetObjectCount(); + //cerr << nobjs << " objs" << endl; + for (uint o = 0; o < nobjs; o++) { + vector otr; + amf.getObjectTriangles(o,otr); + triangles.push_back(otr); + names.push_back(ustring(amf.GetObjectName(o))); + } + return true; +#else + return false; +#endif +} -bool File::load_AMF(vector &triangles, uint max_triangles) +bool File::save_AMF (ustring filename, + const vector< vector > &triangles, + const vector &names, + bool compressed) { +#if ENABLE_AMF + AMFWriter amf; + amf.SetUnits(UNIT_MM); + uint nobjs = triangles.size(); + for (uint o = 0; o < nobjs; o++) { + bool ok = amf.AddObject(triangles[o],names[o]); + if (!ok) return false; + } + amf.Save(filename, compressed); + return true; +#else return false; +#endif } diff --git a/src/files.h b/src/files.h index 96b5ca82..24f27a2d 100644 --- a/src/files.h +++ b/src/files.h @@ -64,8 +64,15 @@ class File uint max_triangles=0, bool readnormals=false); bool load_VRML(vector &triangles, uint max_triangles=0); - bool load_AMF (vector &triangles, uint max_triangles=0); + bool load_AMF (vector< vector > &triangles, + vector &names, + uint max_triangles=0); + + static bool save_AMF (ustring filename, + const vector< vector > &triangles, + const vector &names, + bool compressed = true); static bool parseSTLtriangles_ascii(istream &text, uint max_triangles, bool readnormals, diff --git a/src/gcode.cpp b/src/gcode.cpp index 00297a17..59b3ad02 100644 --- a/src/gcode.cpp +++ b/src/gcode.cpp @@ -458,7 +458,7 @@ void GCode::drawCommands(const Settings &settings, uint start, uint end, double extrusionwidth = 0; if (boundary) extrusionwidth = - settings.Hardware.GetExtrudedMaterialWidth(settings.Hardware.LayerThickness); + settings.Extruder.GetExtrudedMaterialWidth(settings.Slicing.LayerThickness); start = CLAMP (start, 0, n_cmds-1); end = CLAMP (end, 0, n_cmds-1); diff --git a/src/model.cpp b/src/model.cpp index 677f2191..4ca72447 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -162,6 +162,7 @@ void Model::WriteGCode(Glib::RefPtr file) { Glib::ustring contents = gcode.get_text(); Glib::file_set_contents (file->get_path(), contents); + settings.GCodePath = file->get_parent()->get_path(); } void Model::ReadSVG(Glib::RefPtr file) @@ -237,6 +238,21 @@ void Model::SaveStl(Glib::RefPtr file) setlocale(LC_COLLATE, colllocale); setlocale(LC_CTYPE, ctypelocale); } + settings.STLPath = file->get_parent()->get_path(); +} + +void Model::SaveAMF(Glib::RefPtr file) +{ + vector shapes; + vector transforms; + objtree.get_all_shapes(shapes,transforms); + vector< vector > triangles; + vector names; + for(uint s = 0; s < shapes.size(); s++) { + triangles.push_back(shapes[s]->getTriangles(transforms[s])); + names.push_back(shapes[s]->filename); + } + File::save_AMF(file->get_path(), triangles, names); } void Model::Read(Glib::RefPtr file) @@ -540,7 +556,7 @@ int Model::MergeShapes(TreeObject *parent, const vector shapes) { Shape * shape = new Shape(); for (uint s = 0; s < shapes.size(); s++) { - vector str = shapes[s]->getTriangles(shapes[s]->transform3D.transform); + vector str = shapes[s]->getTriangles(); shape->addTriangles(str); } AddShape(parent, shape, "merged", true); @@ -926,7 +942,7 @@ int Model::draw (vector &iter) if ( m_previewGCode.size() != 0 || ( layers.size() == 0 && gcode.commands.size() == 0 ) ) { Vector3d start(0,0,0); - const double thickness = settings.Hardware.LayerThickness; + const double thickness = settings.Slicing.LayerThickness; const double z = settings.Display.GCodeDrawStart + thickness/2; const int LayerCount = (int)ceil(Max.z()/thickness)-1; const uint LayerNo = (uint)ceil(settings.Display.GCodeDrawStart*(LayerCount-1)); @@ -968,7 +984,7 @@ int Model::drawLayers(double height, const Vector3d &offset, bool calconly) double minZ = 0;//max(0.0, Min.z()); double z; - double zStep = settings.Hardware.LayerThickness; + double zStep = settings.Slicing.LayerThickness; double zSize = (Max.z() - minZ - zStep*0.5); int LayerCount = (int)ceil((zSize - zStep*0.5)/zStep)-1; double sel_Z = height; //*zSize; @@ -1013,16 +1029,14 @@ int Model::drawLayers(double height, const Vector3d &offset, bool calconly) } else { + const float lthickness = settings.Slicing.LayerThickness; if (!m_previewLayer || m_previewLayer->getZ() != z) { - m_previewLayer = calcSingleLayer(z, LayerNr, - settings.Hardware.LayerThickness, + m_previewLayer = calcSingleLayer(z, LayerNr, lthickness, settings.Display.DisplayinFill, false); layer = m_previewLayer; Layer * previous = NULL; - if (LayerNr>0 && z >= settings.Hardware.LayerThickness) - previous = calcSingleLayer(z-settings.Hardware.LayerThickness, - LayerNr-1, - settings.Hardware.LayerThickness, + if (LayerNr>0 && z >= lthickness) + previous = calcSingleLayer(z-lthickness, LayerNr-1, lthickness, false, false); layer->setPrevious(previous); } diff --git a/src/model.h b/src/model.h index daaa3015..c9a6abae 100644 --- a/src/model.h +++ b/src/model.h @@ -61,9 +61,10 @@ class Model // STL Functions void ReadStl(Glib::RefPtr file); - void SaveStl(Glib::RefPtr file); vector ReadShapes(Glib::RefPtr file, uint max_triangles = 0); + void SaveStl(Glib::RefPtr file); + void SaveAMF(Glib::RefPtr file); int AddShape(TreeObject *parent, Shape * shape, string filename, bool autoplace = true); diff --git a/src/model2.cpp b/src/model2.cpp index f6150e8d..deca5e72 100644 --- a/src/model2.cpp +++ b/src/model2.cpp @@ -54,7 +54,7 @@ void Model::MakeRaft(GCodeState &state, double &z) vector raftpolys = Clipping::getOffset(layers[0]->GetHullPolygon(), settings.Raft.Size, jround); for (uint i = 0; i< raftpolys.size(); i++) - raftpolys[i].cleanup(settings.Hardware.LayerThickness/4); + raftpolys[i].cleanup(settings.Slicing.LayerThickness/4); Settings::RaftSettings::PhasePropertiesType basesettings = settings.Raft.Phase[0]; @@ -64,9 +64,9 @@ void Model::MakeRaft(GCodeState &state, double &z) vector raft_layers; double rotation = basesettings.Rotation; - double basethickness = settings.Hardware.LayerThickness + double basethickness = settings.Slicing.LayerThickness * basesettings.Thickness; - double interthickness = settings.Hardware.LayerThickness + double interthickness = settings.Slicing.LayerThickness * interfacesettings.Thickness; double totalthickness = basesettings.LayerCount * basethickness @@ -302,7 +302,7 @@ void Model::Slice() bool varSlicing = settings.Slicing.Varslicing; uint max_skins = max(1, settings.Slicing.Skins); - double thickness = (double)settings.Hardware.LayerThickness; + double thickness = (double)settings.Slicing.LayerThickness; double skin_thickness = thickness / max_skins; uint skins = max_skins; // probably variable @@ -553,7 +553,7 @@ void Model::MultiplyUncoveredPolygons() { if (!settings.Slicing.DoInfill && settings.Slicing.SolidThickness == 0.0) return; if (settings.Slicing.NoTopAndBottom) return; - int shells = (int)ceil(settings.Slicing.SolidThickness/settings.Hardware.LayerThickness); + int shells = (int)ceil(settings.Slicing.SolidThickness/settings.Slicing.LayerThickness); shells = max(shells, (int)settings.Slicing.ShellCount); if (shells<1) return; int count = (int)layers.size(); @@ -633,7 +633,7 @@ void Model::MakeSupportPolygons(Layer * layer, // lower -> will change double widen) { const double distance = - settings.Hardware.GetExtrudedMaterialWidth(layer->thickness); + settings.Extruder.GetExtrudedMaterialWidth(layer->thickness); // vector tosupport = Clipping::getOffset(layerabove->GetToSupportPolygons(), // distance/2.); //vector tosupport = Clipping::getMerged(layerabove->GetToSupportPolygons(), @@ -917,7 +917,7 @@ void Model::ConvertToGCode() double totlength = gcode.GetTotalExtruded(settings.Slicing.RelativeEcode); ostr << _(" - total extruded: ") << totlength << "mm"; - double ccm = totlength*settings.Hardware.FilamentDiameter*settings.Hardware.FilamentDiameter/4.*M_PI/1000 ; + double ccm = totlength*settings.Extruder.FilamentDiameter*settings.Extruder.FilamentDiameter/4.*M_PI/1000 ; ostr << " = " << ccm << "cm^3 "; ostr << "(ABS~" << ccm*1.08 << "g, PLA~" << ccm*1.25 << "g)"; if (statusbar) diff --git a/src/objtree.cpp b/src/objtree.cpp index e1151ea6..67129cdd 100644 --- a/src/objtree.cpp +++ b/src/objtree.cpp @@ -133,6 +133,7 @@ void ObjectsTree::update_model() row[m_cols->m_object] = -1; row[m_cols->m_shape] = -1; row[m_cols->m_pickindex] = 0; + row[m_cols->m_material] = 0; gint index = 1; // pick/select index. matches computation in draw() @@ -145,6 +146,7 @@ void ObjectsTree::update_model() orow[m_cols->m_object] = i; orow[m_cols->m_shape] = -1; orow[m_cols->m_pickindex] = index++; + orow[m_cols->m_material] = 0; for (guint j = 0; j < Objects[i]->shapes.size(); j++) { Objects[i]->shapes[j]->idx = j; @@ -154,6 +156,7 @@ void ObjectsTree::update_model() row[m_cols->m_object] = i; row[m_cols->m_shape] = j; row[m_cols->m_pickindex] = index++; + row[m_cols->m_material] = 0; } } inhibit_row_changed = false; diff --git a/src/objtree.h b/src/objtree.h index 22bf08be..362ff0cb 100644 --- a/src/objtree.h +++ b/src/objtree.h @@ -64,11 +64,16 @@ class ObjectsTree class ModelColumns : public Gtk::TreeModelColumnRecord { public: - ModelColumns() { add (m_name); add (m_object); add (m_shape); add(m_pickindex); } + ModelColumns() { + add (m_name); add (m_object); + add (m_shape); add(m_pickindex); + add (m_material); + } Gtk::TreeModelColumn m_name; Gtk::TreeModelColumn m_object; Gtk::TreeModelColumn m_shape; Gtk::TreeModelColumn m_pickindex; + Gtk::TreeModelColumn m_material; }; ObjectsTree(); diff --git a/src/repsnapper.ui b/src/repsnapper.ui index 77f00078..a5ec0e6a 100644 --- a/src/repsnapper.ui +++ b/src/repsnapper.ui @@ -13,6 +13,11 @@ Calibrate An interactive calibration routine + + Fullscreen + Fullscreen + Toggle Fullscreen + Load Settings Load Settings @@ -74,11 +79,6 @@ 0.10000000000000001 10 - - Fullscreen - Fullscreen - Toggle Fullscreen - False 200 @@ -1503,7 +1503,7 @@ and others I forgot... (needs updating) False - Save Model as STL + Save as STL/AMF False True True @@ -1988,6 +1988,12 @@ and others I forgot... (needs updating) + + + + + + False @@ -2302,7 +2308,7 @@ and others I forgot... (needs updating) - + True True @@ -2570,6 +2576,30 @@ and others I forgot... (needs updating) + + + + + + + + + + + + + + + + + + + + + + + + False @@ -3343,6 +3373,24 @@ and others I forgot... (needs updating) + + + + + + + + + + + + + + + + + + True @@ -3615,7 +3663,7 @@ and others I forgot... (needs updating) - + True True @@ -3633,7 +3681,7 @@ and others I forgot... (needs updating) - + True True @@ -4921,7 +4969,7 @@ and others I forgot... (needs updating) - + True True Manual tweak-able to increase or decrease the flow rate beyond what is specified above. @@ -4953,7 +5001,7 @@ and others I forgot... (needs updating) - + True True Manual tweak-able to increase or decrease the flow rate beyond what is specified above. @@ -4972,7 +5020,7 @@ and others I forgot... (needs updating) - + Calibrate mm as input False True @@ -5041,7 +5089,7 @@ and others I forgot... (needs updating) - + True True @@ -5059,7 +5107,7 @@ and others I forgot... (needs updating) - + True True @@ -5735,6 +5783,48 @@ and others I forgot... (needs updating) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True @@ -5750,7 +5840,7 @@ and others I forgot... (needs updating) - + True True @@ -5858,7 +5948,7 @@ and others I forgot... (needs updating) - + True True @@ -5885,7 +5975,7 @@ and others I forgot... (needs updating) - + True True True @@ -6918,7 +7008,7 @@ and others I forgot... (needs updating) - + True True @@ -8299,6 +8389,12 @@ and others I forgot... (needs updating) + + + + + + False @@ -8589,6 +8685,48 @@ and others I forgot... (needs updating) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True diff --git a/src/settings.cpp b/src/settings.cpp index 2babd674..928310e8 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -114,22 +114,13 @@ static struct { #undef FLOAT_PHASE_MEMBER // Hardware - BOOL_MEMBER (Hardware.CalibrateInput, "CalibrateInput", false, true), FLOAT_MEMBER (Hardware.MinPrintSpeedXY, "MinPrintSpeedXY", 1000, true), FLOAT_MEMBER (Hardware.MaxPrintSpeedXY, "MaxPrintSpeedXY", 4000, true), FLOAT_MEMBER (Hardware.MoveSpeed, "MoveSpeed", 4000, true), FLOAT_MEMBER (Hardware.MinPrintSpeedZ, "MinPrintSpeedZ", 50, true), FLOAT_MEMBER (Hardware.MaxPrintSpeedZ, "MaxPrintSpeedZ", 150, true), - FLOAT_MEMBER (Hardware.EMaxSpeed, "EMaxSpeed", 100, true), - FLOAT_MEMBER (Hardware.MaxShellSpeed, "MaxShellSpeed", 3000, true), // FLOAT_MEMBER (Hardware.DistanceToReachFullSpeed, "DistanceToReachFullSpeed", 1.5, false), - FLOAT_MEMBER (Hardware.ExtrusionFactor, "ExtrusionFactor", 1.0, true), - FLOAT_MEMBER (Hardware.FilamentDiameter, "FilamentDiameter", 3.0, true), - FLOAT_MEMBER (Hardware.LayerThickness, "LayerThickness", 0.4, true), - FLOAT_MEMBER (Hardware.DownstreamMultiplier, "DownstreamMultiplier", 1.0, true), - FLOAT_MEMBER (Hardware.DownstreamExtrusionMultiplier, "DownstreamExtrusionMultiplier", 1.0, true), - // Volume. { OFFSET (Hardware.Volume.array[0]), T_DOUBLE, "mfVolumeX", "Hardware.Volume.X", 200, NULL, true }, { OFFSET (Hardware.Volume.array[1]), T_DOUBLE, "mfVolumeY", "Hardware.Volume.Y", 200, NULL, true }, @@ -140,15 +131,25 @@ static struct { { OFFSET (Hardware.PrintMargin.array[1]), T_DOUBLE, "PrintMarginY", "Hardware.PrintMargin.Y", 10, NULL, true }, { OFFSET (Hardware.PrintMargin.array[2]), T_DOUBLE, "PrintMarginZ", "Hardware.PrintMargin.Z", 0, NULL, true }, - FLOAT_MEMBER (Hardware.ExtrudedMaterialWidthRatio, "ExtrudedMaterialWidthRatio", 1.8, true), - FLOAT_MEMBER (Hardware.MinimumLineWidth, "MinimumLineWidth", 0.4, true), - FLOAT_MEMBER (Hardware.MaximumLineWidth, "MaximumLineWidth", 0.7, true), + { OFFSET (Hardware.PortName), T_STRING, "Hardware.PortName", NULL, 0, DEFAULT_COM_PORT, false }, { OFFSET (Hardware.SerialSpeed), T_INT, "Hardware.SerialSpeed", NULL, 115200, NULL, false }, BOOL_MEMBER (Hardware.ValidateConnection, "ValidateConnection", true, false), INT_MEMBER (Hardware.KeepLines, "KeepLines", 1000, false), INT_MEMBER (Hardware.ReceivingBufferSize, "ReceivingBufferSize", 4, false), + // Extruder + BOOL_MEMBER (Extruder.CalibrateInput, "CalibrateInput", false, true), + FLOAT_MEMBER (Extruder.ExtrusionFactor, "ExtrusionFactor", 1.0, true), + FLOAT_MEMBER (Extruder.FilamentDiameter, "FilamentDiameter", 3.0, true), + FLOAT_MEMBER (Extruder.DownstreamMultiplier, "DownstreamMultiplier", 1.0, true), + FLOAT_MEMBER (Extruder.DownstreamExtrusionMultiplier, "DownstreamExtrusionMultiplier", 1.0, true), + FLOAT_MEMBER (Extruder.ExtrudedMaterialWidthRatio, "ExtrudedMaterialWidthRatio", 1.8, true), + FLOAT_MEMBER (Extruder.MinimumLineWidth, "MinimumLineWidth", 0.4, true), + FLOAT_MEMBER (Extruder.MaximumLineWidth, "MaximumLineWidth", 0.7, true), + FLOAT_MEMBER (Extruder.EMaxSpeed, "EMaxSpeed", 100, true), + FLOAT_MEMBER (Extruder.MaxShellSpeed, "MaxShellSpeed", 3000, true), + // Printer FLOAT_MEMBER (Printer.ExtrudeAmount, "Printer.ExtrudeAmount", 5, false), FLOAT_MEMBER (Printer.ExtrudeSpeed, "Printer.ExtrudeSpeed", 100, false), @@ -159,6 +160,7 @@ static struct { { OFFSET (Printer.BedTemp) , T_FLOAT, "Printer.BedTemp", NULL, 60, NULL, false }, // Slicing + FLOAT_MEMBER (Slicing.LayerThickness, "LayerThickness", 0.4, true), BOOL_MEMBER (Slicing.RelativeEcode, "RelativeEcode", false, false), BOOL_MEMBER (Slicing.EnableAntiooze, "EnableAntiooze", false, true), FLOAT_MEMBER (Slicing.AntioozeDistance, "AntioozeDistance", 4.5, true), @@ -319,6 +321,7 @@ static struct { { "InterfaceTemperature", 0.9, 1.2, 0.01, 0.1 }, // Slicing + { "Slicing.LayerThickness", 0.01, 3.0, 0.01, 0.2 }, { "Slicing.ShellCount", 0, 100, 1, 5 }, // { "Slicing.SolidLayers", 0, 100, 1, 5 }, { "Slicing.SolidThickness", 0, 10, 0.01, 0.1 }, @@ -372,27 +375,25 @@ static struct { { "Hardware.PrintMargin.X", 0.0, 100.0, 1.0, 5.0 }, { "Hardware.PrintMargin.Y", 0.0, 100.0, 1.0, 5.0 }, { "Hardware.PrintMargin.Z", 0.0, 100.0, 1.0, 5.0 }, - { "Hardware.DistanceToReachFullSpeed", 0.0, 10.0, 0.1, 1.0 }, - { "Hardware.ExtrudedMaterialWidthRatio", 0.0, 10.0, 0.01, 0.1 }, - { "Hardware.MinimumLineWidth", 0.0, 10.0, 0.01, 0.1 }, - { "Hardware.MaximumLineWidth", 0.0, 10.0, 0.01, 0.1 }, - { "Hardware.LayerThickness", 0.01, 3.0, 0.01, 0.2 }, - { "Hardware.ExtrusionFactor", 0.0, 2.0, 0.1, 0.2 }, - { "Hardware.FilamentDiameter", 0.5, 5.0, 0.01, 0.05 }, - { "Hardware.MinPrintSpeedXY", 1.0, 20000.0, 10.0, 100.0 }, { "Hardware.MaxPrintSpeedXY", 1.0, 20000.0, 10.0, 100.0 }, { "Hardware.MoveSpeed", 1.0, 20000.0, 10.0, 100.0 }, { "Hardware.MinPrintSpeedZ", 1.0, 2500.0, 10.0, 100.0 }, { "Hardware.MaxPrintSpeedZ", 1.0, 2500.0, 10.0, 100.0 }, - { "Hardware.EMaxSpeed", 1.0, 20000.0, 10.0, 100.0 }, - { "Hardware.MaxShellSpeed", 1.0, 20000.0, 10.0, 100.0 }, - { "Hardware.ReceivingBufferSize", 1.0, 100.0, 1.0, 5.0 }, { "Hardware.KeepLines", 100.0, 100000.0, 1.0, 500.0 }, - { "Hardware.DownstreamMultiplier", 0.01, 25.0, 0.01, 0.1 }, - { "Hardware.DownstreamExtrusionMultiplier", 0.01, 25.0, 0.01, 0.1 }, + // Extruder + { "Extruder.DistanceToReachFullSpeed", 0.0, 10.0, 0.1, 1.0 }, + { "Extruder.ExtrudedMaterialWidthRatio", 0.0, 10.0, 0.01, 0.1 }, + { "Extruder.MinimumLineWidth", 0.0, 10.0, 0.01, 0.1 }, + { "Extruder.MaximumLineWidth", 0.0, 10.0, 0.01, 0.1 }, + { "Extruder.ExtrusionFactor", 0.0, 2.0, 0.1, 0.2 }, + { "Extruder.FilamentDiameter", 0.5, 5.0, 0.01, 0.05 }, + { "Extruder.EMaxSpeed", 1.0, 20000.0, 10.0, 100.0 }, + { "Extruder.MaxShellSpeed", 1.0, 20000.0, 10.0, 100.0 }, + // { "Extruder.DownstreamMultiplier", 0.01, 25.0, 0.01, 0.1 }, + // { "Extruder.DownstreamExtrusionMultiplier", 0.01, 25.0, 0.01, 0.1 }, //Printer { "Printer.ExtrudeAmount", 0.0, 1000.0, 1.0, 10.0 }, @@ -507,7 +508,7 @@ std::string Settings::GCodeType::getText(GCodeTextType t) double Settings::GetInfillDistance(double layerthickness, float percent) const { double fullInfillDistance = - Hardware.GetExtrudedMaterialWidth(layerthickness); + Extruder.GetExtrudedMaterialWidth(layerthickness); if (percent == 0) return 10000000; return fullInfillDistance * (100./percent); } @@ -1299,7 +1300,7 @@ void Settings::connect_to_ui (Builder &builder) // extrusion ratio for round-edge lines -double Settings::HardwareSettings::RoundedLinewidthCorrection(double extr_width, +double Settings::ExtruderSettings::RoundedLinewidthCorrection(double extr_width, double layerheight) { double factor = 1 + (M_PI/4.-1) * layerheight/extr_width; @@ -1312,7 +1313,7 @@ double Settings::HardwareSettings::RoundedLinewidthCorrection(double extr_width, } -double Settings::HardwareSettings::GetExtrudedMaterialWidth(double layerheight) const +double Settings::ExtruderSettings::GetExtrudedMaterialWidth(double layerheight) const { // ExtrudedMaterialWidthRatio is preset by user return min(max((double)MinimumLineWidth, @@ -1323,7 +1324,7 @@ double Settings::HardwareSettings::GetExtrudedMaterialWidth(double layerheight) // TODO This depends whether lines are packed or not - ellipsis/rectangle // how much mm filament material per extruded line length mm -> E gcode -double Settings::HardwareSettings::GetExtrudeFactor(double layerheight) const +double Settings::ExtruderSettings::GetExtrudeFactor(double layerheight) const { double f = ExtrusionFactor; // overall factor if (CalibrateInput) { // means we use input filament diameter diff --git a/src/settings.h b/src/settings.h index 35758610..ff820ea4 100644 --- a/src/settings.h +++ b/src/settings.h @@ -59,29 +59,29 @@ class Settings { }; RaftSettings Raft; + struct PrinterSettings { + float ExtrudeAmount; + float ExtrudeSpeed; + float NozzleTemp; + float BedTemp; + int FanVoltage; + bool Logging; + bool ClearLogOnPrintStart; + }; + PrinterSettings Printer; + + struct HardwareSettings { float MinPrintSpeedXY; float MaxPrintSpeedXY; float MoveSpeed; float MinPrintSpeedZ; float MaxPrintSpeedZ; - float EMaxSpeed; - float MaxShellSpeed; - - bool CalibrateInput; // hardware treats 'mm' as filament input mm not of nozzle output. float DistanceToReachFullSpeed; - float ExtrusionFactor; - float FilamentDiameter; - - float LayerThickness; vmml::vec3d Volume; // Print volume vmml::vec3d PrintMargin; - float ExtrudedMaterialWidthRatio; // ratio of with to (layer) height - double GetExtrudedMaterialWidth(const double layerheight) const; - float MinimumLineWidth; - float MaximumLineWidth; std::string PortName; int SerialSpeed; @@ -90,25 +90,30 @@ class Settings { int ReceivingBufferSize; + int NumExtruders; + }; + HardwareSettings Hardware; + + struct ExtruderSettings { + bool CalibrateInput; // hardware treats 'mm' as filament input mm not of nozzle output. + float EMaxSpeed; + float MaxShellSpeed; + float ExtrusionFactor; + float FilamentDiameter; + float ExtrudedMaterialWidthRatio; // ratio of with to (layer) height + double GetExtrudedMaterialWidth(const double layerheight) const; + float MinimumLineWidth; + float MaximumLineWidth; float DownstreamMultiplier; float DownstreamExtrusionMultiplier; static double RoundedLinewidthCorrection(double extr_width, double layerheight); double GetExtrudeFactor(double layerheight) const; }; - HardwareSettings Hardware; - - struct PrinterSettings { - float ExtrudeAmount; - float ExtrudeSpeed; - float NozzleTemp; - float BedTemp; - int FanVoltage; - bool Logging; - bool ClearLogOnPrintStart; - }; - PrinterSettings Printer; + ExtruderSettings Extruder; struct SlicingSettings { + float LayerThickness; + bool RelativeEcode; bool UseArcs; float ArcsMaxAngle; diff --git a/src/shape.cpp b/src/shape.cpp index 098fea5f..d0f7488b 100644 --- a/src/shape.cpp +++ b/src/shape.cpp @@ -272,7 +272,7 @@ vector Shape::getTriangles(const Matrix4d &T) const { vector tr(triangles.size()); for (uint i = 0; i < triangles.size(); i++) { - tr[i] = triangles[i].transformed(T); + tr[i] = triangles[i].transformed(T*transform3D.transform); } return tr; } diff --git a/src/shape.h b/src/shape.h index e011065e..04a33eeb 100644 --- a/src/shape.h +++ b/src/shape.h @@ -137,7 +137,7 @@ class Shape bool slow_drawing; virtual string info() const; - vector getTriangles(const Matrix4d &T) const; + vector getTriangles(const Matrix4d &T=Matrix4d::IDENTITY) const; void addTriangles(const vector &tr); void setTriangles(const vector &triangles_); diff --git a/src/slicer/layer.cpp b/src/slicer/layer.cpp index 4c4e925a..e3c01367 100644 --- a/src/slicer/layer.cpp +++ b/src/slicer/layer.cpp @@ -582,9 +582,9 @@ void FindThinpolys(const vector &polys, double extrwidth, void Layer::MakeShells(const Settings &settings) { - double extrudedWidth = settings.Hardware.GetExtrudedMaterialWidth(thickness); + double extrudedWidth = settings.Extruder.GetExtrudedMaterialWidth(thickness); double roundline_extrfactor = - settings.Hardware.RoundedLinewidthCorrection(extrudedWidth,thickness); + settings.Extruder.RoundedLinewidthCorrection(extrudedWidth,thickness); double distance = 0.5 * extrudedWidth; double cleandist = min(distance/CLEANFACTOR, thickness/CLEANFACTOR); double shelloffset = settings.Slicing.ShellOffset; @@ -711,14 +711,14 @@ void Layer::MakeGcode(Vector3d &lastPos, //GCodeState &state, const Settings &settings) const { - const double linewidth = settings.Hardware.GetExtrudedMaterialWidth(thickness); + const double linewidth = settings.Extruder.GetExtrudedMaterialWidth(thickness); const double cornerradius = linewidth*settings.Slicing.CornerRadius; const bool clipnearest = settings.Slicing.MoveNearest; Vector2d startPoint(lastPos.x(),lastPos.y()); - const double extrf = settings.Hardware.GetExtrudeFactor(thickness); + const double extrf = settings.Extruder.GetExtrudeFactor(thickness); vector lines3; Printlines printlines(this, &settings, offsetZ); @@ -758,7 +758,7 @@ void Layer::MakeGcode(Vector3d &lastPos, //GCodeState &state, // add skin to lines printlines.addPolys(SKIN, polys, (s==0), // displace at first skin - settings.Hardware.MaxShellSpeed, + settings.Extruder.MaxShellSpeed, settings.Slicing.MinShelltime); if (s < skins-1) { // not on the last layer, this handle with all other lines // have to get all these separately because z changes @@ -779,7 +779,7 @@ void Layer::MakeGcode(Vector3d &lastPos, //GCodeState &state, // 2. Skirt vector skirts(1); skirts[0] = skirtPolygon; printlines.addPolys(SKIRT, skirts, false, - settings.Hardware.MaxShellSpeed, + settings.Extruder.MaxShellSpeed, settings.Slicing.MinShelltime); // 3. Support @@ -792,7 +792,7 @@ void Layer::MakeGcode(Vector3d &lastPos, //GCodeState &state, for(int p=shellPolygons.size()-1; p>=0; p--) { // inner to outer printlines.addPolys(SHELL, shellPolygons[p], (p==(int)(shellPolygons.size())-1), - settings.Hardware.MaxShellSpeed, + settings.Extruder.MaxShellSpeed, settings.Slicing.MinShelltime); } diff --git a/src/slicer/printlines.cpp b/src/slicer/printlines.cpp index 7d387c66..09c9f194 100644 --- a/src/slicer/printlines.cpp +++ b/src/slicer/printlines.cpp @@ -62,7 +62,7 @@ int PLine3::getCommands(Vector3d &lastpos, vector &commands, movespeed = settings.Hardware.MoveSpeed, minZspeed = settings.Hardware.MinPrintSpeedZ, maxZspeed = settings.Hardware.MaxPrintSpeedZ, - maxEspeed = settings.Hardware.EMaxSpeed; + maxEspeed = settings.Extruder.EMaxSpeed; int count=0; diff --git a/src/slicer/triangle.cpp b/src/slicer/triangle.cpp index 336671b2..cf5b590d 100644 --- a/src/slicer/triangle.cpp +++ b/src/slicer/triangle.cpp @@ -33,13 +33,12 @@ void Triangle::calcNormal() Normal = normalized(AA.cross(BB)); } - - Triangle Triangle::transformed(const Matrix4d &T) const { return Triangle(T*A,T*B,T*C); } + void Triangle::invertNormal() { Vector3d swap = A; @@ -49,7 +48,7 @@ void Triangle::invertNormal() void Triangle::mirrorX(const Vector3d ¢er) { - for (uint i = 0; i < 3; i++) + for (uint i = 0; i < 3; i++) operator[](i).x() = center.x()-operator[](i).x(); invertNormal(); } @@ -95,9 +94,9 @@ bool Triangle::wrongOrientationWith(Triangle const &other, double maxsqerr) cons } int diff = thisv[1] - thisv[0]; - const bool thisorient = ( diff == 1 || diff == -2 ); + const bool thisorient = ( diff == 1 || diff == -2 ); diff = otherv[1] - otherv[0]; - const bool otherorient = ( diff == 1 || diff == -2 ); + const bool otherorient = ( diff == 1 || diff == -2 ); // cerr << "have 2: " << thisorient <<" / " << otherorient << endl; return (thisorient == otherorient); // they have different(!) orientation } @@ -126,7 +125,7 @@ double Triangle::slopeAngle(const Matrix4d &T) const { const double scale = T(3,3); Vector3d trans; - T.get_translation(trans); + T.get_translation(trans); // get scaled translation out of matrix const Vector3d n = T * Normal - trans/scale; return asin(n.z()/n.length()); @@ -148,7 +147,7 @@ double Triangle::projectedvolume(const Matrix4d &T) const Vector3d min = GetMin(T); Vector3d max = GetMax(T); double vol = xyproj.area()*0.5*(max.z()+min.z()); - if (Normal.z()<0) vol=-vol; + if (Normal.z()<0) vol=-vol; return vol; } @@ -193,7 +192,7 @@ void Triangle::Translate(const Vector3d &vector) C += vector; } -void Triangle::rotate(const Vector3d &axis, double angle) +void Triangle::rotate(const Vector3d &axis, double angle) { A = A.rotate(angle, axis); B = B.rotate(angle, axis); @@ -208,41 +207,41 @@ void triangulateQuadrilateral(vector fourpoints, vector &tri double SMALL = 0.01; // find diagonals double dist = dist3D_Segment_to_Segment(fourpoints[0],fourpoints[2], - fourpoints[1],fourpoints[3], + fourpoints[1],fourpoints[3], SMALL*SMALL); if (dist < SMALL) - { // found -> divide at shorter diagonal - if ((fourpoints[0]-fourpoints[2]).squared_length() - < (fourpoints[1]-fourpoints[3]).squared_length()) { + { // found -> divide at shorter diagonal + if ((fourpoints[0]-fourpoints[2]).squared_length() + < (fourpoints[1]-fourpoints[3]).squared_length()) { tr[0] = Triangle(fourpoints[0],fourpoints[1],fourpoints[2]); tr[1] = Triangle(fourpoints[2],fourpoints[3],fourpoints[0]); } else { tr[0] = Triangle(fourpoints[0],fourpoints[1],fourpoints[3]); tr[1] = Triangle(fourpoints[1],fourpoints[2],fourpoints[3]); } - } + } else { // take other 2 double dist = dist3D_Segment_to_Segment(fourpoints[1],fourpoints[2], - fourpoints[0],fourpoints[3], + fourpoints[0],fourpoints[3], SMALL*SMALL); if (dist < SMALL) { - if ((fourpoints[1]-fourpoints[2]).squared_length() + if ((fourpoints[1]-fourpoints[2]).squared_length() < (fourpoints[0]-fourpoints[3]).squared_length()) { tr[0] = Triangle(fourpoints[1],fourpoints[2],fourpoints[3]); tr[1] = Triangle(fourpoints[0],fourpoints[1],fourpoints[2]); } else { tr[0] = Triangle(fourpoints[1],fourpoints[0],fourpoints[3]); tr[1] = Triangle(fourpoints[0],fourpoints[2],fourpoints[3]); - } - } - else + } + } + else { // take 3rd possibility, not the case here, because 2-3 is cut line double dist = dist3D_Segment_to_Segment(fourpoints[0],fourpoints[1], fourpoints[2],fourpoints[3], SMALL*SMALL); - if (dist < SMALL) + if (dist < SMALL) { tr[0] = Triangle(fourpoints[0],fourpoints[2],fourpoints[3]); tr[1] = Triangle(fourpoints[2],fourpoints[1],fourpoints[3]); @@ -256,7 +255,7 @@ void triangulateQuadrilateral(vector fourpoints, vector &tri triangles.insert(triangles.end(), tr.begin(), tr.end()); } -int Triangle::SplitAtPlane(double z, +int Triangle::SplitAtPlane(double z, vector &uppertriangles, vector &lowertriangles, const Matrix4d &T) const @@ -268,7 +267,7 @@ int Triangle::SplitAtPlane(double z, Vector2d lstart,lend; int cut = CutWithPlane(z,T,lstart,lend); if (cut==0) return 0; - else if (cut==1) { // cut at a triangle point + else if (cut==1) { // cut at a triangle point if (upper.size()>lower.size()) upper.push_back(Vector3d(lstart.x(),lstart.y(),z)); else @@ -296,12 +295,12 @@ int Triangle::SplitAtPlane(double z, } else cerr << "lower size " << lower.size() << endl; Vector3d TN = T*Normal; TN.normalize(); - for (guint i=0; i < uppertr.size(); i++) + for (guint i=0; i < uppertr.size(); i++) if ((uppertr[i].Normal + TN).length()<0.1) uppertr[i].invertNormal(); - for (guint i=0; i < lowertr.size(); i++) + for (guint i=0; i < lowertr.size(); i++) if ((lowertr[i].Normal + TN).length()<0.1) lowertr[i].invertNormal(); - uppertriangles.insert(uppertriangles.end(),uppertr.begin(),uppertr.end()); - lowertriangles.insert(lowertriangles.end(),lowertr.begin(),lowertr.end()); + uppertriangles.insert(uppertriangles.end(),uppertr.begin(),uppertr.end()); + lowertriangles.insert(lowertriangles.end(),lowertr.begin(),lowertr.end()); return cut; } @@ -316,7 +315,7 @@ bool Triangle::isInZrange(double zmin, double zmax, const Matrix4d &T) const return true; } -int Triangle::CutWithPlane(double z, const Matrix4d &T, +int Triangle::CutWithPlane(double z, const Matrix4d &T, Vector2d &lineStart, Vector2d &lineEnd) const { @@ -358,13 +357,13 @@ int Triangle::CutWithPlane(double z, const Matrix4d &T, lineEnd = Vector2d(p.x(),p.y()); if( lineEnd != lineStart ) num_cutpoints = 2; } - + return num_cutpoints; } void Triangle::draw(int gl_type) const { - glBegin(gl_type); + glBegin(gl_type); glVertex3f(A.x(),A.y(),A.z()); glVertex3f(B.x(),B.y(),B.z()); glVertex3f(C.x(),C.y(),C.z()); @@ -390,8 +389,8 @@ string Triangle::info() const { ostringstream ostr; ostr <<"Triangle A="<< A - <<", B="<< B - <<", C="<< C + <<", B="<< B + <<", C="<< C <<", N="<< Normal ; return ostr.str(); } diff --git a/src/slicer/triangle.h b/src/slicer/triangle.h index c027d3b6..bd107755 100644 --- a/src/slicer/triangle.h +++ b/src/slicer/triangle.h @@ -31,7 +31,7 @@ class Triangle { public: Triangle(const Vector3d &Norml, const Vector3d &Point1, - const Vector3d &Point2, const Vector3d &Point3) + const Vector3d &Point2, const Vector3d &Point3) { Normal = Norml ; A=Point1;B=Point2;C=Point3;} Triangle(const Vector3d &Point1, const Vector3d &Point2, const Vector3d &Point3); @@ -57,14 +57,14 @@ class Triangle Vector3d A,B,C,Normal; // p1,p2,p3, Normal Vector3d GetMax(const Matrix4d &T=Matrix4d::IDENTITY) const; Vector3d GetMin(const Matrix4d &T=Matrix4d::IDENTITY) const; - - void AccumulateMinMax(Vector3d &min, Vector3d &max, + + void AccumulateMinMax(Vector3d &min, Vector3d &max, const Matrix4d &T=Matrix4d::IDENTITY); void Translate(const Vector3d &vector); - int CutWithPlane(double z, const Matrix4d &T, + int CutWithPlane(double z, const Matrix4d &T, Vector2d &lineStart, Vector2d &lineEnd) const; bool isInZrange(double zmin, double zmax, const Matrix4d &T) const; - int SplitAtPlane(double z, + int SplitAtPlane(double z, vector &uppertriangles, vector &lowertriangles, const Matrix4d &T=Matrix4d::IDENTITY) const; diff --git a/src/view.cpp b/src/view.cpp index b225cec7..da82042e 100644 --- a/src/view.cpp +++ b/src/view.cpp @@ -78,7 +78,7 @@ void View::connect_action(const char *name, const sigc::slot &slot) void View::connect_toggled(const char *name, const sigc::slot &slot) { - Gtk::ToggleButton *button; + Gtk::ToggleButton *button = NULL; m_builder->get_widget (name, button); if (button) button->signal_toggled().connect (sigc::bind(slot, button)); @@ -89,7 +89,7 @@ void View::connect_toggled(const char *name, const sigc::slot &slot) { - Gtk::ToggleToolButton *button; + Gtk::ToggleToolButton *button = NULL; m_builder->get_widget (name, button); if (button) button->signal_toggled().connect (sigc::bind(slot, button)); @@ -231,11 +231,16 @@ void View::do_save_stl () if (files[0]->query_exists()) if (!get_userconfirm(_("Overwrite File?"), files[0]->get_basename())) return; - string directory_path = files[0]->get_parent()->get_path(); - m_model->settings.STLPath = directory_path; - m_model->SaveStl (files[0]); + + string file_path = files[0]->get_path(); + uint len = file_path.length(); + if (file_path.find(".amf") == len-4 || file_path.find(".AMF") == len-4) + m_model->SaveAMF (files[0]); + else + m_model->SaveStl (files[0]); } } + void View::do_save_gcode () { PrintInhibitor inhibitPrint(m_printer); @@ -245,8 +250,6 @@ void View::do_save_gcode () if (files[0]->query_exists()) if (!get_userconfirm(_("Overwrite File?"), files[0]->get_basename())) return; - string directory_path = files[0]->get_parent()->get_path(); - m_model->settings.GCodePath = directory_path; m_model->WriteGCode (files[0]); } } @@ -1626,6 +1629,8 @@ void View::setModel(Model *model) m_treeview->set_model (m_model->objtree.m_model); m_treeview->append_column_editable("Name", m_model->objtree.m_cols->m_name); + // m_treeview->append_column_editable("Extruder", m_model->objtree.m_cols->m_material); + // m_treeview->set_headers_visible(true); m_gcodetextview = NULL; m_builder->get_widget ("txt_gcode_result", m_gcodetextview);