<commit_before>// -*-c++-*-

/*
 * Wavefront OBJ loader for Open Scene Graph
 *
 * Copyright (C) 2001 Ulrich Hertlein <u.hertlein@web.de>
 *
 * Modified by Robert Osfield to support per Drawable coord, normal and
 * texture coord arrays, bug fixes, and support for texture mapping.
 *
 * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for
 * real-time rendering of large 3D photo-realistic models.
 * The OSG homepage is http://www.openscenegraph.org/
 */

#if defined(_MSC_VER)
    #pragma warning( disable : 4786 )
#endif

#include <string>

#include <osg/Notify>
#include <osg/Node>
#include <osg/MatrixTransform>
#include <osg/Geode>

#include <osg/Geometry>
#include <osg/StateSet>
#include <osg/Material>
#include <osg/Texture2D>
#include <osg/TexGen>
#include <osg/TexMat>

#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>

#include <osgUtil/TriStripVisitor>
#include <osgUtil/SmoothingVisitor>
#include <osgUtil/Tesselator>

#include "obj.h"

#include <map>
#include <set>

class ReaderWriterOBJ : public osgDB::ReaderWriter
{
public:
    ReaderWriterOBJ() { }

    virtual const char* className() { return "Wavefront OBJ Reader"; }
    virtual bool acceptsExtension(const std::string& extension) {
        return osgDB::equalCaseInsensitive(extension,"obj");
    }

    virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options*);

protected:

    typedef std::map< std::string, osg::ref_ptr<osg::StateSet> > MaterialToStateSetMap;

    void buildMaterialToStateSetMap(obj::Model& model, MaterialToStateSetMap& materialToSetSetMap);

    osg::Geometry* convertElementListToGeometry(obj::Model& model, obj::Model::ElementList& elementList);

    osg::Node* convertModelToSceneGraph(obj::Model& model);

    inline osg::Vec3 transformVertex(const osg::Vec3& vec) { return osg::Vec3(vec.x(),-vec.z(),vec.y()); }
    inline osg::Vec3 transformNormal(const osg::Vec3& vec) { return osg::Vec3(vec.x(),-vec.z(),vec.y()); }

};

// register with Registry to instantiate the above reader/writer.
osgDB::RegisterReaderWriterProxy<ReaderWriterOBJ> g_objReaderWriterProxy;

void ReaderWriterOBJ::buildMaterialToStateSetMap(obj::Model& model, MaterialToStateSetMap& materialToStateSetMap)
{
    for(obj::Model::MaterialMap::iterator itr = model.materialMap.begin();
        itr != model.materialMap.end();
        ++itr)
    {
        obj::Material& material = itr->second;

        osg::StateSet* stateset = new osg::StateSet;

        // handle material colors
        {
            osg::Material* osg_material = new osg::Material;
            stateset->setAttribute(osg_material);

            osg_material->setAmbient(osg::Material::FRONT_AND_BACK,material.ambient);
            osg_material->setDiffuse(osg::Material::FRONT_AND_BACK,material.diffuse);
            osg_material->setSpecular(osg::Material::FRONT_AND_BACK,material.specular);
            osg_material->setShininess(osg::Material::FRONT_AND_BACK,(material.shininess/1000.0f)*128.0f ); // note OBJ shiniess is 0..1000.
        }

        // handle textures
        if (!material.map_Kd.empty())
        {
            std::string filename = material.map_Kd;
            osg::Image* image = osgDB::readImageFile(filename);
            if (image)
            {
                osg::Texture2D* texture = new osg::Texture2D(image);
                stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
            }
        }

        materialToStateSetMap[material.name] = stateset;

    }
}

osg::Geometry* ReaderWriterOBJ::convertElementListToGeometry(obj::Model& model, obj::Model::ElementList& elementList)
{

    unsigned int numVertexIndices = 0;
    unsigned int numNormalIndices = 0;
    unsigned int numTexCoordIndices = 0;

    unsigned int numPointElements = 0;
    unsigned int numPolylineElements = 0;
    unsigned int numPolygonElements = 0;

    obj::Model::ElementList::iterator itr;
    for(itr=elementList.begin();
        itr!=elementList.end();
        ++itr)
    {
        obj::Element& element = *(*itr);

        numVertexIndices += element.vertexIndices.size();
        numNormalIndices += element.normalIndices.size();
        numTexCoordIndices += element.texCoordIndices.size();

        numPointElements += (element.dataType==obj::Element::POINTS) ? 1 : 0;
        numPolylineElements += (element.dataType==obj::Element::POLYLINE) ? 1 : 0;
        numPolygonElements += (element.dataType==obj::Element::POLYGON) ? 1 : 0;

    }

    if (numVertexIndices==0) return 0;

    if (numNormalIndices!=0 && numNormalIndices!=numVertexIndices)
    {
        osg::notify(osg::NOTICE)<<"Incorrect number of normals, ignore them"<<std::endl;
        numNormalIndices = 0;
    }

    if (numTexCoordIndices!=0 && numTexCoordIndices!=numVertexIndices)
    {
        osg::notify(osg::NOTICE)<<"Incorrect number of normals, ignore them"<<std::endl;
        numTexCoordIndices = 0;
    }

    osg::Vec3Array* vertices = numVertexIndices ? new osg::Vec3Array : 0;
    osg::Vec3Array* normals = numNormalIndices ? new osg::Vec3Array : 0;
    osg::Vec2Array* texcoords = numTexCoordIndices ? new osg::Vec2Array : 0;

    if (vertices) vertices->reserve(numVertexIndices);
    if (normals) normals->reserve(numNormalIndices);
    if (texcoords) texcoords->reserve(numTexCoordIndices);

    osg::Geometry* geometry = new osg::Geometry;
    if (vertices) geometry->setVertexArray(vertices);
    if (normals)
    {
        geometry->setNormalArray(normals);
        geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
    }
    if (texcoords)
    {
        geometry->setTexCoordArray(0,texcoords);
    }

    if (numPointElements>0)
    {
        unsigned int startPos = vertices->size();
        unsigned int numPoints = 0;
        for(itr=elementList.begin();
            itr!=elementList.end();
            ++itr)
        {
            obj::Element& element = *(*itr);
            if (element.dataType==obj::Element::POINTS)
            {
                for(obj::Element::IndexList::iterator index_itr = element.vertexIndices.begin();
                    index_itr != element.vertexIndices.end();
                    ++index_itr)
                {
                    vertices->push_back(transformVertex(model.vertices[*index_itr]));
                    ++numPoints;
                }
                if (numNormalIndices)
                {
                    for(obj::Element::IndexList::iterator index_itr = element.normalIndices.begin();
                        index_itr != element.normalIndices.end();
                        ++index_itr)
                    {
                        normals->push_back(transformNormal(model.normals[*index_itr]));
                    }
                }
                if (numTexCoordIndices)
                {
                    for(obj::Element::IndexList::iterator index_itr = element.texCoordIndices.begin();
                        index_itr != element.texCoordIndices.end();
                        ++index_itr)
                    {
                        texcoords->push_back(model.texcoords[*index_itr]);
                    }
                }
            }
        }

        osg::DrawArrays* drawArrays = new osg::DrawArrays(GL_POINTS,startPos,numPoints);
        geometry->addPrimitiveSet(drawArrays);
    }

    if (numPolylineElements>0)
    {
        unsigned int startPos = vertices->size();
        osg::DrawArrayLengths* drawArrayLengths = new osg::DrawArrayLengths(GL_LINES,startPos);

        for(itr=elementList.begin();
            itr!=elementList.end();
            ++itr)
        {
            obj::Element& element = *(*itr);
            if (element.dataType==obj::Element::POLYLINE)
            {
                drawArrayLengths->push_back(element.vertexIndices.size());

                for(obj::Element::IndexList::iterator index_itr = element.vertexIndices.begin();
                    index_itr != element.vertexIndices.end();
                    ++index_itr)
                {
                    vertices->push_back(transformVertex(model.vertices[*index_itr]));
                }
                if (numNormalIndices)
                {
                    for(obj::Element::IndexList::iterator index_itr = element.normalIndices.begin();
                        index_itr != element.normalIndices.end();
                        ++index_itr)
                    {
                        normals->push_back(transformNormal(model.normals[*index_itr]));
                    }
                }
                if (numTexCoordIndices)
                {
                    for(obj::Element::IndexList::iterator index_itr = element.texCoordIndices.begin();
                        index_itr != element.texCoordIndices.end();
                        ++index_itr)
                    {
                        texcoords->push_back(model.texcoords[*index_itr]);
                    }
                }
            }
        }

        geometry->addPrimitiveSet(drawArrayLengths);

    }

    if (numPolygonElements>0)
    {
        unsigned int startPos = vertices->size();
        osg::DrawArrayLengths* drawArrayLengths = new osg::DrawArrayLengths(GL_POLYGON,startPos);

        for(itr=elementList.begin();
            itr!=elementList.end();
            ++itr)
        {
            obj::Element& element = *(*itr);
            if (element.dataType==obj::Element::POLYGON)
            {
                drawArrayLengths->push_back(element.vertexIndices.size());

                if (model.needReverse(element))
                {
                    // need to reverse so add to OSG arrays in same order as in OBJ, as OSG assume anticlockwise ordering.
                    for(obj::Element::IndexList::reverse_iterator index_itr = element.vertexIndices.rbegin();
                        index_itr != element.vertexIndices.rend();
                        ++index_itr)
                    {
                        vertices->push_back(transformVertex(model.vertices[*index_itr]));
                    }
                    if (numNormalIndices)
                    {
                        for(obj::Element::IndexList::reverse_iterator index_itr = element.normalIndices.rbegin();
                            index_itr != element.normalIndices.rend();
                            ++index_itr)
                        {
                            normals->push_back(transformNormal(model.normals[*index_itr]));
                        }
                    }
                    if (numTexCoordIndices)
                    {
                        for(obj::Element::IndexList::reverse_iterator index_itr = element.texCoordIndices.rbegin();
                            index_itr != element.texCoordIndices.rend();
                            ++index_itr)
                        {
                            texcoords->push_back(model.texcoords[*index_itr]);
                        }
                    }
                }
                else
                {
                    // no need to reverse so add to OSG arrays in same order as in OBJ.
                    for(obj::Element::IndexList::iterator index_itr = element.vertexIndices.begin();
                        index_itr != element.vertexIndices.end();
                        ++index_itr)
                    {
                        vertices->push_back(transformVertex(model.vertices[*index_itr]));
                    }
                    if (numNormalIndices)
                    {
                        for(obj::Element::IndexList::iterator index_itr = element.normalIndices.begin();
                            index_itr != element.normalIndices.end();
                            ++index_itr)
                        {
                            normals->push_back(transformNormal(model.normals[*index_itr]));
                        }
                    }
                    if (numTexCoordIndices)
                    {
                        for(obj::Element::IndexList::iterator index_itr = element.texCoordIndices.begin();
                            index_itr != element.texCoordIndices.end();
                            ++index_itr)
                        {
                            texcoords->push_back(model.texcoords[*index_itr]);
                        }
                    }
                }
            }
        }

        geometry->addPrimitiveSet(drawArrayLengths);

    }

    return geometry;
}

osg::Node* ReaderWriterOBJ::convertModelToSceneGraph(obj::Model& model)
{

    if (model.elementStateMap.empty()) return 0;

    osg::Group* group = new osg::Group;

    // set up the materials
    MaterialToStateSetMap materialToSetSetMap;
    buildMaterialToStateSetMap(model, materialToSetSetMap);

    // go through the groups of related elements and build geometry from them.
    for(obj::Model::ElementStateMap::iterator itr=model.elementStateMap.begin();
        itr!=model.elementStateMap.end();
        ++itr)
    {

        const obj::ElementState& es = itr->first;
        obj::Model::ElementList& el = itr->second;

        osg::Geometry* geometry = convertElementListToGeometry(model,el);

        if (geometry)
        {

            osg::StateSet* stateset = materialToSetSetMap[es.materialName].get();
            geometry->setStateSet(stateset);

//            osgUtil::Tesselator tesselator;
//            tesselator.retesselatePolygons(*geometry);

            osgUtil::TriStripVisitor tsv;
            tsv.stripify(*geometry);

            if (!geometry->getNormalArray() || geometry->getNormalArray()->getNumElements()==0)
            {
                osgUtil::SmoothingVisitor tsv;
                tsv.smooth(*geometry);
            }

            osg::Geode* geode = new osg::Geode;
            geode->addDrawable(geometry);
            geode->setName(es.objectName);

            group->addChild(geode);

        }
    }

    return group;
}

// read file and convert to OSG.
osgDB::ReaderWriter::ReadResult ReaderWriterOBJ::readNode(const std::string& file, const osgDB::ReaderWriter::Options*)
{
    std::string ext = osgDB::getLowerCaseFileExtension(file);
    if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;

    std::string fileName = osgDB::findDataFile( file );
    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;

    std::ifstream fin(fileName.c_str());
    if (fin)
    {
        obj::Model model;
        model.readOBJ(fin);

        osg::Node* node = convertModelToSceneGraph(model);
        return node;
    }

    return ReadResult::FILE_NOT_HANDLED;
}

<commit_msg>Added handling of short (<=4 points) polygons as tri fans and the rest as polygons which are tesselated, to improve load and build time, yet still resselating the large polygons that need it.<commit_after>// -*-c++-*-

/*
 * Wavefront OBJ loader for Open Scene Graph
 *
 * Copyright (C) 2001 Ulrich Hertlein <u.hertlein@web.de>
 *
 * Modified by Robert Osfield to support per Drawable coord, normal and
 * texture coord arrays, bug fixes, and support for texture mapping.
 *
 * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for
 * real-time rendering of large 3D photo-realistic models.
 * The OSG homepage is http://www.openscenegraph.org/
 */

#if defined(_MSC_VER)
    #pragma warning( disable : 4786 )
#endif

#include <string>

#include <osg/Notify>
#include <osg/Node>
#include <osg/MatrixTransform>
#include <osg/Geode>

#include <osg/Geometry>
#include <osg/StateSet>
#include <osg/Material>
#include <osg/Texture2D>
#include <osg/TexGen>
#include <osg/TexMat>

#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>

#include <osgUtil/TriStripVisitor>
#include <osgUtil/SmoothingVisitor>
#include <osgUtil/Tesselator>

#include "obj.h"

#include <map>
#include <set>

class ReaderWriterOBJ : public osgDB::ReaderWriter
{
public:
    ReaderWriterOBJ() { }

    virtual const char* className() { return "Wavefront OBJ Reader"; }
    virtual bool acceptsExtension(const std::string& extension) {
        return osgDB::equalCaseInsensitive(extension,"obj");
    }

    virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options*);

protected:

    typedef std::map< std::string, osg::ref_ptr<osg::StateSet> > MaterialToStateSetMap;

    void buildMaterialToStateSetMap(obj::Model& model, MaterialToStateSetMap& materialToSetSetMap);

    osg::Geometry* convertElementListToGeometry(obj::Model& model, obj::Model::ElementList& elementList);

    osg::Node* convertModelToSceneGraph(obj::Model& model);

    inline osg::Vec3 transformVertex(const osg::Vec3& vec) { return osg::Vec3(vec.x(),-vec.z(),vec.y()); }
    inline osg::Vec3 transformNormal(const osg::Vec3& vec) { return osg::Vec3(vec.x(),-vec.z(),vec.y()); }

};

// register with Registry to instantiate the above reader/writer.
osgDB::RegisterReaderWriterProxy<ReaderWriterOBJ> g_objReaderWriterProxy;

void ReaderWriterOBJ::buildMaterialToStateSetMap(obj::Model& model, MaterialToStateSetMap& materialToStateSetMap)
{
    for(obj::Model::MaterialMap::iterator itr = model.materialMap.begin();
        itr != model.materialMap.end();
        ++itr)
    {
        obj::Material& material = itr->second;

        osg::StateSet* stateset = new osg::StateSet;

        // handle material colors
        {
            osg::Material* osg_material = new osg::Material;
            stateset->setAttribute(osg_material);

            osg_material->setAmbient(osg::Material::FRONT_AND_BACK,material.ambient);
            osg_material->setDiffuse(osg::Material::FRONT_AND_BACK,material.diffuse);
            osg_material->setSpecular(osg::Material::FRONT_AND_BACK,material.specular);
            osg_material->setShininess(osg::Material::FRONT_AND_BACK,(material.shininess/1000.0f)*128.0f ); // note OBJ shiniess is 0..1000.
        }

        // handle textures
        if (!material.map_Kd.empty())
        {
            std::string filename = material.map_Kd;
            osg::Image* image = osgDB::readImageFile(filename);
            if (image)
            {
                osg::Texture2D* texture = new osg::Texture2D(image);
                stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
            }
        }

        materialToStateSetMap[material.name] = stateset;

    }
}

osg::Geometry* ReaderWriterOBJ::convertElementListToGeometry(obj::Model& model, obj::Model::ElementList& elementList)
{

    unsigned int numVertexIndices = 0;
    unsigned int numNormalIndices = 0;
    unsigned int numTexCoordIndices = 0;

    unsigned int numPointElements = 0;
    unsigned int numPolylineElements = 0;
    unsigned int numPolygonElements = 0;

    obj::Model::ElementList::iterator itr;
    for(itr=elementList.begin();
        itr!=elementList.end();
        ++itr)
    {
        obj::Element& element = *(*itr);

        numVertexIndices += element.vertexIndices.size();
        numNormalIndices += element.normalIndices.size();
        numTexCoordIndices += element.texCoordIndices.size();

        numPointElements += (element.dataType==obj::Element::POINTS) ? 1 : 0;
        numPolylineElements += (element.dataType==obj::Element::POLYLINE) ? 1 : 0;
        numPolygonElements += (element.dataType==obj::Element::POLYGON) ? 1 : 0;

    }

    if (numVertexIndices==0) return 0;

    if (numNormalIndices!=0 && numNormalIndices!=numVertexIndices)
    {
        osg::notify(osg::NOTICE)<<"Incorrect number of normals, ignore them"<<std::endl;
        numNormalIndices = 0;
    }

    if (numTexCoordIndices!=0 && numTexCoordIndices!=numVertexIndices)
    {
        osg::notify(osg::NOTICE)<<"Incorrect number of normals, ignore them"<<std::endl;
        numTexCoordIndices = 0;
    }

    osg::Vec3Array* vertices = numVertexIndices ? new osg::Vec3Array : 0;
    osg::Vec3Array* normals = numNormalIndices ? new osg::Vec3Array : 0;
    osg::Vec2Array* texcoords = numTexCoordIndices ? new osg::Vec2Array : 0;

    if (vertices) vertices->reserve(numVertexIndices);
    if (normals) normals->reserve(numNormalIndices);
    if (texcoords) texcoords->reserve(numTexCoordIndices);

    osg::Geometry* geometry = new osg::Geometry;
    if (vertices) geometry->setVertexArray(vertices);
    if (normals)
    {
        geometry->setNormalArray(normals);
        geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
    }
    if (texcoords)
    {
        geometry->setTexCoordArray(0,texcoords);
    }

    if (numPointElements>0)
    {
        unsigned int startPos = vertices->size();
        unsigned int numPoints = 0;
        for(itr=elementList.begin();
            itr!=elementList.end();
            ++itr)
        {
            obj::Element& element = *(*itr);
            if (element.dataType==obj::Element::POINTS)
            {
                for(obj::Element::IndexList::iterator index_itr = element.vertexIndices.begin();
                    index_itr != element.vertexIndices.end();
                    ++index_itr)
                {
                    vertices->push_back(transformVertex(model.vertices[*index_itr]));
                    ++numPoints;
                }
                if (numNormalIndices)
                {
                    for(obj::Element::IndexList::iterator index_itr = element.normalIndices.begin();
                        index_itr != element.normalIndices.end();
                        ++index_itr)
                    {
                        normals->push_back(transformNormal(model.normals[*index_itr]));
                    }
                }
                if (numTexCoordIndices)
                {
                    for(obj::Element::IndexList::iterator index_itr = element.texCoordIndices.begin();
                        index_itr != element.texCoordIndices.end();
                        ++index_itr)
                    {
                        texcoords->push_back(model.texcoords[*index_itr]);
                    }
                }
            }
        }

        osg::DrawArrays* drawArrays = new osg::DrawArrays(GL_POINTS,startPos,numPoints);
        geometry->addPrimitiveSet(drawArrays);
    }

    if (numPolylineElements>0)
    {
        unsigned int startPos = vertices->size();
        osg::DrawArrayLengths* drawArrayLengths = new osg::DrawArrayLengths(GL_LINES,startPos);

        for(itr=elementList.begin();
            itr!=elementList.end();
            ++itr)
        {
            obj::Element& element = *(*itr);
            if (element.dataType==obj::Element::POLYLINE)
            {
                drawArrayLengths->push_back(element.vertexIndices.size());

                for(obj::Element::IndexList::iterator index_itr = element.vertexIndices.begin();
                    index_itr != element.vertexIndices.end();
                    ++index_itr)
                {
                    vertices->push_back(transformVertex(model.vertices[*index_itr]));
                }
                if (numNormalIndices)
                {
                    for(obj::Element::IndexList::iterator index_itr = element.normalIndices.begin();
                        index_itr != element.normalIndices.end();
                        ++index_itr)
                    {
                        normals->push_back(transformNormal(model.normals[*index_itr]));
                    }
                }
                if (numTexCoordIndices)
                {
                    for(obj::Element::IndexList::iterator index_itr = element.texCoordIndices.begin();
                        index_itr != element.texCoordIndices.end();
                        ++index_itr)
                    {
                        texcoords->push_back(model.texcoords[*index_itr]);
                    }
                }
            }
        }

        geometry->addPrimitiveSet(drawArrayLengths);

    }

    // #define USE_DRAWARRAYLENGTHS

    if (numPolygonElements>0)
    {
        unsigned int startPos = vertices->size();

        #ifdef USE_DRAWARRAYLENGTHS
            osg::DrawArrayLengths* drawArrayLengths = new osg::DrawArrayLengths(GL_POLYGON,startPos);
            geometry->addPrimitiveSet(drawArrayLengths);
        #endif

        for(itr=elementList.begin();
            itr!=elementList.end();
            ++itr)
        {
            obj::Element& element = *(*itr);
            if (element.dataType==obj::Element::POLYGON)
            {

                #ifdef USE_DRAWARRAYLENGTHS
                    drawArrayLengths->push_back(element.vertexIndices.size());
                #else
                    if (element.vertexIndices.size()>4)
                    {
                        osg::DrawArrays* drawArrays = new osg::DrawArrays(GL_POLYGON,startPos,element.vertexIndices.size());
                        startPos += element.vertexIndices.size();
                        geometry->addPrimitiveSet(drawArrays);
                    }
                    else
                    {
                        osg::DrawArrays* drawArrays = new osg::DrawArrays(GL_TRIANGLE_FAN,startPos,element.vertexIndices.size());
                        startPos += element.vertexIndices.size();
                        geometry->addPrimitiveSet(drawArrays);
                    }
                #endif

                if (model.needReverse(element))
                {
                    // need to reverse so add to OSG arrays in same order as in OBJ, as OSG assume anticlockwise ordering.
                    for(obj::Element::IndexList::reverse_iterator index_itr = element.vertexIndices.rbegin();
                        index_itr != element.vertexIndices.rend();
                        ++index_itr)
                    {
                        vertices->push_back(transformVertex(model.vertices[*index_itr]));
                    }
                    if (numNormalIndices)
                    {
                        for(obj::Element::IndexList::reverse_iterator index_itr = element.normalIndices.rbegin();
                            index_itr != element.normalIndices.rend();
                            ++index_itr)
                        {
                            normals->push_back(transformNormal(model.normals[*index_itr]));
                        }
                    }
                    if (numTexCoordIndices)
                    {
                        for(obj::Element::IndexList::reverse_iterator index_itr = element.texCoordIndices.rbegin();
                            index_itr != element.texCoordIndices.rend();
                            ++index_itr)
                        {
                            texcoords->push_back(model.texcoords[*index_itr]);
                        }
                    }
                }
                else
                {
                    // no need to reverse so add to OSG arrays in same order as in OBJ.
                    for(obj::Element::IndexList::iterator index_itr = element.vertexIndices.begin();
                        index_itr != element.vertexIndices.end();
                        ++index_itr)
                    {
                        vertices->push_back(transformVertex(model.vertices[*index_itr]));
                    }
                    if (numNormalIndices)
                    {
                        for(obj::Element::IndexList::iterator index_itr = element.normalIndices.begin();
                            index_itr != element.normalIndices.end();
                            ++index_itr)
                        {
                            normals->push_back(transformNormal(model.normals[*index_itr]));
                        }
                    }
                    if (numTexCoordIndices)
                    {
                        for(obj::Element::IndexList::iterator index_itr = element.texCoordIndices.begin();
                            index_itr != element.texCoordIndices.end();
                            ++index_itr)
                        {
                            texcoords->push_back(model.texcoords[*index_itr]);
                        }
                    }
                }
            }
        }

    }

    return geometry;
}

osg::Node* ReaderWriterOBJ::convertModelToSceneGraph(obj::Model& model)
{

    if (model.elementStateMap.empty()) return 0;

    osg::Group* group = new osg::Group;

    // set up the materials
    MaterialToStateSetMap materialToSetSetMap;
    buildMaterialToStateSetMap(model, materialToSetSetMap);

    // go through the groups of related elements and build geometry from them.
    for(obj::Model::ElementStateMap::iterator itr=model.elementStateMap.begin();
        itr!=model.elementStateMap.end();
        ++itr)
    {

        const obj::ElementState& es = itr->first;
        obj::Model::ElementList& el = itr->second;

        osg::Geometry* geometry = convertElementListToGeometry(model,el);

        if (geometry)
        {

            osg::StateSet* stateset = materialToSetSetMap[es.materialName].get();
            geometry->setStateSet(stateset);

            // tesseleate any large polygons
            osgUtil::Tesselator tesselator;
            tesselator.retesselatePolygons(*geometry);

            // tri strip polygons to improve graphics peformance
            osgUtil::TriStripVisitor tsv;
            tsv.stripify(*geometry);

            // if no normals present add them.
            if (!geometry->getNormalArray() || geometry->getNormalArray()->getNumElements()==0)
            {
                osgUtil::SmoothingVisitor tsv;
                tsv.smooth(*geometry);
            }

            osg::Geode* geode = new osg::Geode;
            geode->addDrawable(geometry);
            geode->setName(es.objectName);

            group->addChild(geode);

        }
    }

    return group;
}

// read file and convert to OSG.
osgDB::ReaderWriter::ReadResult ReaderWriterOBJ::readNode(const std::string& file, const osgDB::ReaderWriter::Options*)
{
    std::string ext = osgDB::getLowerCaseFileExtension(file);
    if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;

    std::string fileName = osgDB::findDataFile( file );
    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;

    std::ifstream fin(fileName.c_str());
    if (fin)
    {
        obj::Model model;
        model.readOBJ(fin);

        osg::Node* node = convertModelToSceneGraph(model);
        return node;
    }

    return ReadResult::FILE_NOT_HANDLED;
}

<|endoftext|>


<commit_before>// Copyright (c) 2009-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <core_io.h>

#include <base58.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <script/script.h>
#include <script/standard.h>
#include <serialize.h>
#include <streams.h>
#include <univalue.h>
#include <util.h>
#include <utilmoneystr.h>
#include <utilstrencodings.h>

UniValue ValueFromAmount(const CAmount& amount)
{
    bool sign = amount < 0;
    int64_t n_abs = (sign ? -amount : amount);
    int64_t quotient = n_abs / COIN;
    int64_t remainder = n_abs % COIN;
    return UniValue(UniValue::VNUM,
            strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder));
}

std::string FormatScript(const CScript& script)
{
    std::string ret;
    CScript::const_iterator it = script.begin();
    opcodetype op;
    while (it != script.end()) {
        CScript::const_iterator it2 = it;
        std::vector<unsigned char> vch;
        if (script.GetOp2(it, op, &vch)) {
            if (op == OP_0) {
                ret += "0 ";
                continue;
            } else if ((op >= OP_1 && op <= OP_16) || op == OP_1NEGATE) {
                ret += strprintf("%i ", op - OP_1NEGATE - 1);
                continue;
            } else if (op >= OP_NOP && op <= OP_NOP10) {
                std::string str(GetOpName(op));
                if (str.substr(0, 3) == std::string("OP_")) {
                    ret += str.substr(3, std::string::npos) + " ";
                    continue;
                }
            }
            if (vch.size() > 0) {
                ret += strprintf("0x%x 0x%x ", HexStr(it2, it - vch.size()), HexStr(it - vch.size(), it));
            } else {
                ret += strprintf("0x%x ", HexStr(it2, it));
            }
            continue;
        }
        ret += strprintf("0x%x ", HexStr(it2, script.end()));
        break;
    }
    return ret.substr(0, ret.size() - 1);
}

const std::map<unsigned char, std::string> mapSigHashTypes = {
    {static_cast<unsigned char>(SIGHASH_ALL), std::string("ALL")},
    {static_cast<unsigned char>(SIGHASH_ALL|SIGHASH_ANYONECANPAY), std::string("ALL|ANYONECANPAY")},
    {static_cast<unsigned char>(SIGHASH_NONE), std::string("NONE")},
    {static_cast<unsigned char>(SIGHASH_NONE|SIGHASH_ANYONECANPAY), std::string("NONE|ANYONECANPAY")},
    {static_cast<unsigned char>(SIGHASH_SINGLE), std::string("SINGLE")},
    {static_cast<unsigned char>(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY), std::string("SINGLE|ANYONECANPAY")},
};

/**
 * Create the assembly string representation of a CScript object.
 * @param[in] script    CScript object to convert into the asm string representation.
 * @param[in] fAttemptSighashDecode    Whether to attempt to decode sighash types on data within the script that matches the format
 *                                     of a signature. Only pass true for scripts you believe could contain signatures. For example,
 *                                     pass false, or omit the this argument (defaults to false), for scriptPubKeys.
 */
std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode)
{
    std::string str;
    opcodetype opcode;
    std::vector<unsigned char> vch;
    CScript::const_iterator pc = script.begin();
    while (pc < script.end()) {
        if (!str.empty()) {
            str += " ";
        }
        if (!script.GetOp(pc, opcode, vch)) {
            str += "[error]";
            return str;
        }
        if (0 <= opcode && opcode <= OP_PUSHDATA4) {
            if (vch.size() <= static_cast<std::vector<unsigned char>::size_type>(4)) {
                str += strprintf("%d", CScriptNum(vch, false).getint());
            } else {
                // the IsUnspendable check makes sure not to try to decode OP_RETURN data that may match the format of a signature
                if (fAttemptSighashDecode && !script.IsUnspendable()) {
                    std::string strSigHashDecode;
                    // goal: only attempt to decode a defined sighash type from data that looks like a signature within a scriptSig.
                    // this won't decode correctly formatted public keys in Pubkey or Multisig scripts due to
                    // the restrictions on the pubkey formats (see IsCompressedOrUncompressedPubKey) being incongruous with the
                    // checks in CheckSignatureEncoding.
                    if (CheckSignatureEncoding(vch, SCRIPT_VERIFY_STRICTENC, nullptr)) {
                        const unsigned char chSigHashType = vch.back();
                        if (mapSigHashTypes.count(chSigHashType)) {
                            strSigHashDecode = "[" + mapSigHashTypes.find(chSigHashType)->second + "]";
                            vch.pop_back(); // remove the sighash type byte. it will be replaced by the decode.
                        }
                    }
                    str += HexStr(vch) + strSigHashDecode;
                } else {
                    str += HexStr(vch);
                }
            }
        } else {
            str += GetOpName(opcode);
        }
    }
    return str;
}

std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags)
{
    CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | serializeFlags);
    ssTx << tx;
    return HexStr(ssTx.begin(), ssTx.end());
}

void ScriptPubKeyToUniv(const CScript& scriptPubKey,
                        UniValue& out, bool fIncludeHex)
{
    txnouttype type;
    std::vector<CTxDestination> addresses;
    int nRequired;

    out.pushKV("asm", ScriptToAsmStr(scriptPubKey));
    if (fIncludeHex)
        out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()));

    if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
        out.pushKV("type", GetTxnOutputType(type));
        return;
    }

    out.pushKV("reqSigs", nRequired);
    out.pushKV("type", GetTxnOutputType(type));

    UniValue a(UniValue::VARR);
    for (const CTxDestination& addr : addresses) {
        a.push_back(EncodeDestination(addr));
    }
    out.pushKV("addresses", a);
}

void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags)
{
    entry.pushKV("txid", tx.GetHash().GetHex());
    entry.pushKV("hash", tx.GetWitnessHash().GetHex());
    entry.pushKV("version", tx.nVersion);
    entry.pushKV("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION));
    entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR);
    entry.pushKV("locktime", (int64_t)tx.nLockTime);

    UniValue vin(UniValue::VARR);
    for (unsigned int i = 0; i < tx.vin.size(); i++) {
        const CTxIn& txin = tx.vin[i];
        UniValue in(UniValue::VOBJ);
        if (tx.IsCoinBase())
            in.pushKV("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
        else {
            in.pushKV("txid", txin.prevout.hash.GetHex());
            in.pushKV("vout", (int64_t)txin.prevout.n);
            UniValue o(UniValue::VOBJ);
            o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true));
            o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
            in.pushKV("scriptSig", o);
            if (!tx.vin[i].scriptWitness.IsNull()) {
                UniValue txinwitness(UniValue::VARR);
                for (const auto& item : tx.vin[i].scriptWitness.stack) {
                    txinwitness.push_back(HexStr(item.begin(), item.end()));
                }
                in.pushKV("txinwitness", txinwitness);
            }
        }
        in.pushKV("sequence", (int64_t)txin.nSequence);
        vin.push_back(in);
    }
    entry.pushKV("vin", vin);

    UniValue vout(UniValue::VARR);
    for (unsigned int i = 0; i < tx.vout.size(); i++) {
        const CTxOut& txout = tx.vout[i];

        UniValue out(UniValue::VOBJ);

        out.pushKV("value", ValueFromAmount(txout.nValue));
        out.pushKV("n", (int64_t)i);

        UniValue o(UniValue::VOBJ);
        ScriptPubKeyToUniv(txout.scriptPubKey, o, true);
        out.pushKV("scriptPubKey", o);
        vout.push_back(out);
    }
    entry.pushKV("vout", vout);

    if (!hashBlock.IsNull())
        entry.pushKV("blockhash", hashBlock.GetHex());

    if (include_hex) {
        entry.pushKV("hex", EncodeHexTx(tx, serialize_flags)); // the hex-encoded transaction. used the name "hex" to be consistent with the verbose output of "getrawtransaction".
    }
}
<commit_msg>our smallest unit is 1 / 1000000 of peercoin<commit_after>// Copyright (c) 2009-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <core_io.h>

#include <base58.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <script/script.h>
#include <script/standard.h>
#include <serialize.h>
#include <streams.h>
#include <univalue.h>
#include <util.h>
#include <utilmoneystr.h>
#include <utilstrencodings.h>

UniValue ValueFromAmount(const CAmount& amount)
{
    bool sign = amount < 0;
    int64_t n_abs = (sign ? -amount : amount);
    int64_t quotient = n_abs / COIN;
    int64_t remainder = n_abs % COIN;
    return UniValue(UniValue::VNUM,
            strprintf("%s%d.%06d", sign ? "-" : "", quotient, remainder));
}

std::string FormatScript(const CScript& script)
{
    std::string ret;
    CScript::const_iterator it = script.begin();
    opcodetype op;
    while (it != script.end()) {
        CScript::const_iterator it2 = it;
        std::vector<unsigned char> vch;
        if (script.GetOp2(it, op, &vch)) {
            if (op == OP_0) {
                ret += "0 ";
                continue;
            } else if ((op >= OP_1 && op <= OP_16) || op == OP_1NEGATE) {
                ret += strprintf("%i ", op - OP_1NEGATE - 1);
                continue;
            } else if (op >= OP_NOP && op <= OP_NOP10) {
                std::string str(GetOpName(op));
                if (str.substr(0, 3) == std::string("OP_")) {
                    ret += str.substr(3, std::string::npos) + " ";
                    continue;
                }
            }
            if (vch.size() > 0) {
                ret += strprintf("0x%x 0x%x ", HexStr(it2, it - vch.size()), HexStr(it - vch.size(), it));
            } else {
                ret += strprintf("0x%x ", HexStr(it2, it));
            }
            continue;
        }
        ret += strprintf("0x%x ", HexStr(it2, script.end()));
        break;
    }
    return ret.substr(0, ret.size() - 1);
}

const std::map<unsigned char, std::string> mapSigHashTypes = {
    {static_cast<unsigned char>(SIGHASH_ALL), std::string("ALL")},
    {static_cast<unsigned char>(SIGHASH_ALL|SIGHASH_ANYONECANPAY), std::string("ALL|ANYONECANPAY")},
    {static_cast<unsigned char>(SIGHASH_NONE), std::string("NONE")},
    {static_cast<unsigned char>(SIGHASH_NONE|SIGHASH_ANYONECANPAY), std::string("NONE|ANYONECANPAY")},
    {static_cast<unsigned char>(SIGHASH_SINGLE), std::string("SINGLE")},
    {static_cast<unsigned char>(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY), std::string("SINGLE|ANYONECANPAY")},
};

/**
 * Create the assembly string representation of a CScript object.
 * @param[in] script    CScript object to convert into the asm string representation.
 * @param[in] fAttemptSighashDecode    Whether to attempt to decode sighash types on data within the script that matches the format
 *                                     of a signature. Only pass true for scripts you believe could contain signatures. For example,
 *                                     pass false, or omit the this argument (defaults to false), for scriptPubKeys.
 */
std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode)
{
    std::string str;
    opcodetype opcode;
    std::vector<unsigned char> vch;
    CScript::const_iterator pc = script.begin();
    while (pc < script.end()) {
        if (!str.empty()) {
            str += " ";
        }
        if (!script.GetOp(pc, opcode, vch)) {
            str += "[error]";
            return str;
        }
        if (0 <= opcode && opcode <= OP_PUSHDATA4) {
            if (vch.size() <= static_cast<std::vector<unsigned char>::size_type>(4)) {
                str += strprintf("%d", CScriptNum(vch, false).getint());
            } else {
                // the IsUnspendable check makes sure not to try to decode OP_RETURN data that may match the format of a signature
                if (fAttemptSighashDecode && !script.IsUnspendable()) {
                    std::string strSigHashDecode;
                    // goal: only attempt to decode a defined sighash type from data that looks like a signature within a scriptSig.
                    // this won't decode correctly formatted public keys in Pubkey or Multisig scripts due to
                    // the restrictions on the pubkey formats (see IsCompressedOrUncompressedPubKey) being incongruous with the
                    // checks in CheckSignatureEncoding.
                    if (CheckSignatureEncoding(vch, SCRIPT_VERIFY_STRICTENC, nullptr)) {
                        const unsigned char chSigHashType = vch.back();
                        if (mapSigHashTypes.count(chSigHashType)) {
                            strSigHashDecode = "[" + mapSigHashTypes.find(chSigHashType)->second + "]";
                            vch.pop_back(); // remove the sighash type byte. it will be replaced by the decode.
                        }
                    }
                    str += HexStr(vch) + strSigHashDecode;
                } else {
                    str += HexStr(vch);
                }
            }
        } else {
            str += GetOpName(opcode);
        }
    }
    return str;
}

std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags)
{
    CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | serializeFlags);
    ssTx << tx;
    return HexStr(ssTx.begin(), ssTx.end());
}

void ScriptPubKeyToUniv(const CScript& scriptPubKey,
                        UniValue& out, bool fIncludeHex)
{
    txnouttype type;
    std::vector<CTxDestination> addresses;
    int nRequired;

    out.pushKV("asm", ScriptToAsmStr(scriptPubKey));
    if (fIncludeHex)
        out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()));

    if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
        out.pushKV("type", GetTxnOutputType(type));
        return;
    }

    out.pushKV("reqSigs", nRequired);
    out.pushKV("type", GetTxnOutputType(type));

    UniValue a(UniValue::VARR);
    for (const CTxDestination& addr : addresses) {
        a.push_back(EncodeDestination(addr));
    }
    out.pushKV("addresses", a);
}

void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags)
{
    entry.pushKV("txid", tx.GetHash().GetHex());
    entry.pushKV("hash", tx.GetWitnessHash().GetHex());
    entry.pushKV("version", tx.nVersion);
    entry.pushKV("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION));
    entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR);
    entry.pushKV("locktime", (int64_t)tx.nLockTime);

    UniValue vin(UniValue::VARR);
    for (unsigned int i = 0; i < tx.vin.size(); i++) {
        const CTxIn& txin = tx.vin[i];
        UniValue in(UniValue::VOBJ);
        if (tx.IsCoinBase())
            in.pushKV("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
        else {
            in.pushKV("txid", txin.prevout.hash.GetHex());
            in.pushKV("vout", (int64_t)txin.prevout.n);
            UniValue o(UniValue::VOBJ);
            o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true));
            o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
            in.pushKV("scriptSig", o);
            if (!tx.vin[i].scriptWitness.IsNull()) {
                UniValue txinwitness(UniValue::VARR);
                for (const auto& item : tx.vin[i].scriptWitness.stack) {
                    txinwitness.push_back(HexStr(item.begin(), item.end()));
                }
                in.pushKV("txinwitness", txinwitness);
            }
        }
        in.pushKV("sequence", (int64_t)txin.nSequence);
        vin.push_back(in);
    }
    entry.pushKV("vin", vin);

    UniValue vout(UniValue::VARR);
    for (unsigned int i = 0; i < tx.vout.size(); i++) {
        const CTxOut& txout = tx.vout[i];

        UniValue out(UniValue::VOBJ);

        out.pushKV("value", ValueFromAmount(txout.nValue));
        out.pushKV("n", (int64_t)i);

        UniValue o(UniValue::VOBJ);
        ScriptPubKeyToUniv(txout.scriptPubKey, o, true);
        out.pushKV("scriptPubKey", o);
        vout.push_back(out);
    }
    entry.pushKV("vout", vout);

    if (!hashBlock.IsNull())
        entry.pushKV("blockhash", hashBlock.GetHex());

    if (include_hex) {
        entry.pushKV("hex", EncodeHexTx(tx, serialize_flags)); // the hex-encoded transaction. used the name "hex" to be consistent with the verbose output of "getrawtransaction".
    }
}
<|endoftext|>


<commit_before>#include "pd_view.h"
#include "pd_view.h"
#include "pd_backend.h"
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <vector>

#if defined(_WIN32)
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#endif

#ifdef __clang__
#pragma clang diagnostic ignored "-Wformat-nonliteral" //error: format string is not a string literal [-Werror,-Wformat-nonliteral]

#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct ConsoleData
{
	ConsoleData() : historyPos(0), scrollToBottom(0)
	{
		memset(inputBuffer, 0, sizeof(inputBuffer));
	}

    char inputBuffer[256];
    std::vector<char*> items;
    std::vector<char*> commands;
    std::vector<char*> history;
    std::vector<char*> scripts; // TODO: Temp, for testing
    int historyPos; // -1: New Line, 0...history.size()-1 Browsing History
    bool scrollToBottom;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void clearLog(ConsoleData* consoleData)
{
    for (size_t i = 0; i < consoleData->items.size(); ++i)
        free(consoleData->items[i]);
    consoleData->items.clear();
    consoleData->scrollToBottom = true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void addLog(ConsoleData* consoleData, const char* fmt, ...)
{
    char buffer[1024];
    va_list args;
    va_start(args, fmt);
    int w = vsnprintf(buffer, 1024, fmt, args);
    (void)w;
    buffer[1024 - 1] = 0;
    va_end(args);
    consoleData->items.push_back(strdup(buffer));
    consoleData->scrollToBottom = true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void execCommand(ConsoleData* consoleData, const char* commandLine)
{
    // TODO: Hook up as PD event

    addLog(consoleData, "# %s\n", commandLine);

    // Insert history. First find match and delete it, so it can be pushed to the back.
    consoleData->historyPos = -1;
    for (int i = int(consoleData->history.size()) - 1; i >= 0; --i)
    {
        if (strcasecmp(consoleData->history[(size_t)i], commandLine) == 0)
        {
            free(consoleData->history[(size_t)i]);
            consoleData->history.erase(consoleData->history.begin() + i);
            break;
        }
    }

    consoleData->history.push_back(strdup(commandLine));

    // Process the command

    if (strcasecmp(commandLine, "clear") == 0)
    {
        clearLog(consoleData);
    }
    else if (strcasecmp(commandLine, "help") == 0)
    {
        addLog(consoleData, "Commands:");
        for (size_t i = 0; i < consoleData->commands.size(); i++)
            addLog(consoleData, "- %s", consoleData->commands[i]);
    }
    else if (strcasecmp(commandLine, "history") == 0)
    {
        for (size_t i = consoleData->history.size() >= 10 ? consoleData->history.size() - 10 : 0; i < consoleData->history.size(); i++)
            addLog(consoleData, "%3d: %s\n", i, consoleData->history[i]);
    }
    else if (strcasecmp(commandLine, "testText") == 0) // TODO: Temp for testing
    {
        addLog(consoleData, "Some text\nSome more text\nDisplay very important message here!\n");
    }
    else if (strcasecmp(commandLine, "testError") == 0) // TODO: Temp for testing
    {
        addLog(consoleData, "[Error] Something went wrong!\n");
    }
    else if (strcasecmp(commandLine, "testScript") == 0) // TODO: Temp for testing
    {
        consoleData->scripts.push_back((char*)"print(\"Hello ProDBG Lua World!\")");
    }
    else
    {
        addLog(consoleData, "Unknown command: '%s'\n", commandLine);
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void textEditCallbackStub(PDInputTextCallbackData* data)
{
    ConsoleData* consoleData = (ConsoleData*)data->userData;

    //addLog(consoleData, "Cursor: %d, EventKey: %d, Selection: %d-%d", data->cursorPos, data->eventKey, data->selectionStart, data->selectionEnd);

    std::vector<const char*> candidates;
    const char* wordEnd   = nullptr;
    const char* wordStart = nullptr;
    switch (data->eventKey)
    {
        default:
            break;

        // Tab Completion
        case PDKEY_TAB:
		{
            // Locate beginning of current word
            wordEnd   = data->buffer + data->cursorPos;
            wordStart = wordEnd;
            while (wordStart > data->buffer)
            {
                const char c = wordStart[-1];
                if (c == ' ' || c == '\t' || c == ',' || c == ';')
                    break;
                wordStart--;
            }

            // Build a list of candidates

            for (size_t i = 0; i < consoleData->commands.size(); ++i)
                if (strncasecmp(consoleData->commands[i], wordStart, (size_t)(int(wordEnd - wordStart))) == 0)
                    candidates.push_back(consoleData->commands[i]);

            if (candidates.size() == 0)
            {
                // No match
                addLog(consoleData, "No match for \"%.*s\"!\n", wordEnd - wordStart, wordStart);
            }
            else if (candidates.size() == 1)
            {
                // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing

                data->deleteChars(data, int(wordStart - data->buffer), int(wordEnd - wordStart));
                data->insertChars(data, data->cursorPos, candidates[0], 0);
                data->insertChars(data, data->cursorPos, " ", 0);
            }
            else
            {
                // Multiple matches. Complete as much as we can, so inputing "C" will complete to "CL" and display "CLEAR" and "CLASSIFY"
                int matchLen = int(wordEnd - wordStart);
                while (true)
                {
                    int c = 0;
                    bool allCandidatesMatches = true;
                    for (size_t i = 0; i < candidates.size() && allCandidatesMatches; i++)
                    {
                        if (i == 0)
                            c = toupper(candidates[i][matchLen]);
                        else if (c != toupper(candidates[i][matchLen]))
                            allCandidatesMatches = false;
                    }

                    if (!allCandidatesMatches)
                        break;

                    matchLen++;
                }

                if (matchLen > 0)
                {
                    data->deleteChars(data, int(wordStart - data->buffer), int(wordEnd - wordStart));
                    data->insertChars(data, data->cursorPos, candidates[0], candidates[0] + matchLen);
                }

                // List matches
                addLog(consoleData, "Possible matches:\n");
                for (size_t i = 0; i < candidates.size(); i++)
                    addLog(consoleData, "- %s\n", candidates[i]);
            }

            break;
        }

        // Command History
        case PDKEY_UP:
        case PDKEY_DOWN:
		{
            const int prevHistoryPos = consoleData->historyPos;
            if (data->eventKey == PDKEY_UP)
            {
                if (consoleData->historyPos == -1)
                    consoleData->historyPos = int(consoleData->history.size()) - 1;
                else if (consoleData->historyPos > 0)
                    consoleData->historyPos--;
            }
            else if (data->eventKey == PDKEY_DOWN)
            {
                if (consoleData->historyPos != -1)
                {
                    if (++consoleData->historyPos >= int(consoleData->history.size()))
                        consoleData->historyPos = -1;
                }
            }

            // TODO: A better implementation would preserve the data on the current input line along with cursor position
            if (prevHistoryPos != consoleData->historyPos)
            {
                strcpy(data->buffer, (consoleData->historyPos >= 0) ? consoleData->history[(size_t)consoleData->historyPos] : "");
                data->bufferDirty = true;
                data->cursorPos = data->selectionStart = data->selectionEnd = int(strlen(data->buffer));
            }

            break;
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void* createInstance(PDUI* uiFuncs, ServiceFunc* serviceFunc)
{
    (void)serviceFunc;
    (void)uiFuncs;

    ConsoleData* consoleData = new ConsoleData;
    clearLog(consoleData);

    consoleData->historyPos = -1;
    consoleData->commands.push_back((char*)"HELP");
    consoleData->commands.push_back((char*)"HISTORY");
    consoleData->commands.push_back((char*)"CLEAR");
    consoleData->commands.push_back((char*)"CLASSIFY");   // TODO: "classify" is here to provide an example of "C"+[tab] completing to "CL" and displaying matches.
    consoleData->commands.push_back((char*)"TESTTEXT");   // TODO: Temp, for testing
    consoleData->commands.push_back((char*)"TESTERROR");  // TODO: Temp, for testing
    consoleData->commands.push_back((char*)"TESTSCRIPT"); // TODO: Temp, for testing

    return consoleData;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void destroyInstance(void* userData)
{
    ConsoleData* consoleData = (ConsoleData*)userData;
    clearLog(consoleData);
    free(consoleData);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void showInUI(ConsoleData* consoleData, PDReader* reader, PDUI* uiFuncs)
{
    (void)consoleData;
    (void)reader;

    uiFuncs->textWrapped("ProDBG Scripting Console");
    uiFuncs->textWrapped("Enter 'HELP' for help. Press TAB to use text completion.");

    // TODO: display from bottom
    // TODO: clip manually

    if (PDUI_buttonSmall(uiFuncs, "Clear"))
        clearLog(consoleData);

    uiFuncs->sameLine(0, -1);
    uiFuncs->separator();

    PDVec2 pad = { 0.0f, 0.0f };

    uiFuncs->pushStyleVarV(PDStyleVar_FramePadding, pad);

    //static ImGuiTextFilter filter;
    //filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180);

    if (uiFuncs->isItemHovered())
        uiFuncs->setKeyboardFocusHere(-1); // Auto focus on hover

    uiFuncs->popStyleVar(1);

    uiFuncs->separator();

    PDVec2 spacing = { 0, -uiFuncs->getTextLineSpacing() * 2 };
    PDVec2 itemSpacing = { 4.0f, 1.0f };

    uiFuncs->beginChild("ScrollingRegion", spacing, false, PDWindowFlags(0));
    uiFuncs->pushStyleVarV(PDStyleVar_ItemSpacing, itemSpacing); // Tighten spacing

    for (size_t i = 0; i < consoleData->items.size(); i++)
    {
        const char* item = consoleData->items[i];
        //if (!filter.PassFilter(item))
        //    continue;
        PDVec4 col = { 1.0f, 1.0f, 1.0f, 1.0f }; // A better implementation may store a type per-item. For now let's just parse the text.
        if (strstr(item, "[Error]"))
            col = { 1.0f, 0.4f, 0.4f, 1.0f };
        else if (strncmp(item, "# ", 2) == 0)
            col = { 1.0f, 0.8f, 0.6f, 1.0f };
        uiFuncs->textColored(col, item);
    }

    if (consoleData->scrollToBottom)
        uiFuncs->setScrollHere();

    consoleData->scrollToBottom = false;

    uiFuncs->popStyleVar(1);
    uiFuncs->endChild();
    uiFuncs->separator();

    // Command Line

    if (uiFuncs->inputText("Input", consoleData->inputBuffer, sizeof(consoleData->inputBuffer), PDInputTextFlags_EnterReturnsTrue | PDInputTextFlags_CallbackCompletion | PDInputTextFlags_CallbackHistory, &textEditCallbackStub, (void*)consoleData))
    {
        char* inputEnd = consoleData->inputBuffer + strlen(consoleData->inputBuffer);

        while (inputEnd > consoleData->inputBuffer && inputEnd[-1] == ' ')
            inputEnd--;

        *inputEnd = 0;

        if (consoleData->inputBuffer[0])
            execCommand(consoleData, consoleData->inputBuffer);

        strcpy(consoleData->inputBuffer, "");
    }

    if (uiFuncs->isItemHovered())
        uiFuncs->setKeyboardFocusHere(-1); // Auto focus on hover
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static int update(void* userData, PDUI* uiFuncs, PDReader* inEvents, PDWriter* outEvents)
{
    ConsoleData* consoleData = (ConsoleData*)userData;

    uint32_t event = 0;
    (void)event;

    sbuseouo

    // testing mail

    /*while ((event = PDRead_getEvent(inEvents)) != 0)
       {
        switch (event)
        {
            case PDEventType_setConsole:
            {
                //showInUI((ConsoleData*)userData, inEvents, uiFuncs);
                break;
            }
        }
       }*/

    showInUI(consoleData, inEvents, uiFuncs);

    for (size_t i = 0; i < consoleData->scripts.size(); ++i)
    {
        PDWrite_eventBegin(outEvents, PDEventType_executeConsole);
        PDWrite_string(outEvents, "command", consoleData->scripts[i]);   // TODO: Remove me
        PDWrite_eventEnd(outEvents);
        //free(consoleData->scripts[i]);
    }

    consoleData->scripts.clear();

    // Request console data

    PDWrite_eventBegin(outEvents, PDEventType_getConsole);
    PDWrite_u8(outEvents, "dummy_remove", 0);   // TODO: Remove me
    PDWrite_eventEnd(outEvents);

    return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static PDViewPlugin plugin =
{
    "Console",
    createInstance,
    destroyInstance,
    update,
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

extern "C"
{

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

PD_EXPORT void InitPlugin(RegisterPlugin* registerPlugin, void* privateData)
{
	registerPlugin(PD_VIEW_API_VERSION, &plugin, privateData);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}

<commit_msg>Fixed build error<commit_after>#include "pd_view.h"
#include "pd_view.h"
#include "pd_backend.h"
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <vector>

#if defined(_WIN32)
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#endif

#ifdef __clang__
#pragma clang diagnostic ignored "-Wformat-nonliteral" //error: format string is not a string literal [-Werror,-Wformat-nonliteral]

#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct ConsoleData
{
	ConsoleData() : historyPos(0), scrollToBottom(0)
	{
		memset(inputBuffer, 0, sizeof(inputBuffer));
	}

    char inputBuffer[256];
    std::vector<char*> items;
    std::vector<char*> commands;
    std::vector<char*> history;
    std::vector<char*> scripts; // TODO: Temp, for testing
    int historyPos; // -1: New Line, 0...history.size()-1 Browsing History
    bool scrollToBottom;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void clearLog(ConsoleData* consoleData)
{
    for (size_t i = 0; i < consoleData->items.size(); ++i)
        free(consoleData->items[i]);
    consoleData->items.clear();
    consoleData->scrollToBottom = true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void addLog(ConsoleData* consoleData, const char* fmt, ...)
{
    char buffer[1024];
    va_list args;
    va_start(args, fmt);
    int w = vsnprintf(buffer, 1024, fmt, args);
    (void)w;
    buffer[1024 - 1] = 0;
    va_end(args);
    consoleData->items.push_back(strdup(buffer));
    consoleData->scrollToBottom = true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void execCommand(ConsoleData* consoleData, const char* commandLine)
{
    // TODO: Hook up as PD event

    addLog(consoleData, "# %s\n", commandLine);

    // Insert history. First find match and delete it, so it can be pushed to the back.
    consoleData->historyPos = -1;
    for (int i = int(consoleData->history.size()) - 1; i >= 0; --i)
    {
        if (strcasecmp(consoleData->history[(size_t)i], commandLine) == 0)
        {
            free(consoleData->history[(size_t)i]);
            consoleData->history.erase(consoleData->history.begin() + i);
            break;
        }
    }

    consoleData->history.push_back(strdup(commandLine));

    // Process the command

    if (strcasecmp(commandLine, "clear") == 0)
    {
        clearLog(consoleData);
    }
    else if (strcasecmp(commandLine, "help") == 0)
    {
        addLog(consoleData, "Commands:");
        for (size_t i = 0; i < consoleData->commands.size(); i++)
            addLog(consoleData, "- %s", consoleData->commands[i]);
    }
    else if (strcasecmp(commandLine, "history") == 0)
    {
        for (size_t i = consoleData->history.size() >= 10 ? consoleData->history.size() - 10 : 0; i < consoleData->history.size(); i++)
            addLog(consoleData, "%3d: %s\n", i, consoleData->history[i]);
    }
    else if (strcasecmp(commandLine, "testText") == 0) // TODO: Temp for testing
    {
        addLog(consoleData, "Some text\nSome more text\nDisplay very important message here!\n");
    }
    else if (strcasecmp(commandLine, "testError") == 0) // TODO: Temp for testing
    {
        addLog(consoleData, "[Error] Something went wrong!\n");
    }
    else if (strcasecmp(commandLine, "testScript") == 0) // TODO: Temp for testing
    {
        consoleData->scripts.push_back((char*)"print(\"Hello ProDBG Lua World!\")");
    }
    else
    {
        addLog(consoleData, "Unknown command: '%s'\n", commandLine);
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void textEditCallbackStub(PDInputTextCallbackData* data)
{
    ConsoleData* consoleData = (ConsoleData*)data->userData;

    //addLog(consoleData, "Cursor: %d, EventKey: %d, Selection: %d-%d", data->cursorPos, data->eventKey, data->selectionStart, data->selectionEnd);

    std::vector<const char*> candidates;
    const char* wordEnd   = nullptr;
    const char* wordStart = nullptr;
    switch (data->eventKey)
    {
        default:
            break;

        // Tab Completion
        case PDKEY_TAB:
		{
            // Locate beginning of current word
            wordEnd   = data->buffer + data->cursorPos;
            wordStart = wordEnd;
            while (wordStart > data->buffer)
            {
                const char c = wordStart[-1];
                if (c == ' ' || c == '\t' || c == ',' || c == ';')
                    break;
                wordStart--;
            }

            // Build a list of candidates

            for (size_t i = 0; i < consoleData->commands.size(); ++i)
                if (strncasecmp(consoleData->commands[i], wordStart, (size_t)(int(wordEnd - wordStart))) == 0)
                    candidates.push_back(consoleData->commands[i]);

            if (candidates.size() == 0)
            {
                // No match
                addLog(consoleData, "No match for \"%.*s\"!\n", wordEnd - wordStart, wordStart);
            }
            else if (candidates.size() == 1)
            {
                // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing

                data->deleteChars(data, int(wordStart - data->buffer), int(wordEnd - wordStart));
                data->insertChars(data, data->cursorPos, candidates[0], 0);
                data->insertChars(data, data->cursorPos, " ", 0);
            }
            else
            {
                // Multiple matches. Complete as much as we can, so inputing "C" will complete to "CL" and display "CLEAR" and "CLASSIFY"
                int matchLen = int(wordEnd - wordStart);
                while (true)
                {
                    int c = 0;
                    bool allCandidatesMatches = true;
                    for (size_t i = 0; i < candidates.size() && allCandidatesMatches; i++)
                    {
                        if (i == 0)
                            c = toupper(candidates[i][matchLen]);
                        else if (c != toupper(candidates[i][matchLen]))
                            allCandidatesMatches = false;
                    }

                    if (!allCandidatesMatches)
                        break;

                    matchLen++;
                }

                if (matchLen > 0)
                {
                    data->deleteChars(data, int(wordStart - data->buffer), int(wordEnd - wordStart));
                    data->insertChars(data, data->cursorPos, candidates[0], candidates[0] + matchLen);
                }

                // List matches
                addLog(consoleData, "Possible matches:\n");
                for (size_t i = 0; i < candidates.size(); i++)
                    addLog(consoleData, "- %s\n", candidates[i]);
            }

            break;
        }

        // Command History
        case PDKEY_UP:
        case PDKEY_DOWN:
		{
            const int prevHistoryPos = consoleData->historyPos;
            if (data->eventKey == PDKEY_UP)
            {
                if (consoleData->historyPos == -1)
                    consoleData->historyPos = int(consoleData->history.size()) - 1;
                else if (consoleData->historyPos > 0)
                    consoleData->historyPos--;
            }
            else if (data->eventKey == PDKEY_DOWN)
            {
                if (consoleData->historyPos != -1)
                {
                    if (++consoleData->historyPos >= int(consoleData->history.size()))
                        consoleData->historyPos = -1;
                }
            }

            // TODO: A better implementation would preserve the data on the current input line along with cursor position
            if (prevHistoryPos != consoleData->historyPos)
            {
                strcpy(data->buffer, (consoleData->historyPos >= 0) ? consoleData->history[(size_t)consoleData->historyPos] : "");
                data->bufferDirty = true;
                data->cursorPos = data->selectionStart = data->selectionEnd = int(strlen(data->buffer));
            }

            break;
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void* createInstance(PDUI* uiFuncs, ServiceFunc* serviceFunc)
{
    (void)serviceFunc;
    (void)uiFuncs;

    ConsoleData* consoleData = new ConsoleData;
    clearLog(consoleData);

    consoleData->historyPos = -1;
    consoleData->commands.push_back((char*)"HELP");
    consoleData->commands.push_back((char*)"HISTORY");
    consoleData->commands.push_back((char*)"CLEAR");
    consoleData->commands.push_back((char*)"CLASSIFY");   // TODO: "classify" is here to provide an example of "C"+[tab] completing to "CL" and displaying matches.
    consoleData->commands.push_back((char*)"TESTTEXT");   // TODO: Temp, for testing
    consoleData->commands.push_back((char*)"TESTERROR");  // TODO: Temp, for testing
    consoleData->commands.push_back((char*)"TESTSCRIPT"); // TODO: Temp, for testing

    return consoleData;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void destroyInstance(void* userData)
{
    ConsoleData* consoleData = (ConsoleData*)userData;
    clearLog(consoleData);
    free(consoleData);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void showInUI(ConsoleData* consoleData, PDReader* reader, PDUI* uiFuncs)
{
    (void)consoleData;
    (void)reader;

    uiFuncs->textWrapped("ProDBG Scripting Console");
    uiFuncs->textWrapped("Enter 'HELP' for help. Press TAB to use text completion.");

    // TODO: display from bottom
    // TODO: clip manually

    if (PDUI_buttonSmall(uiFuncs, "Clear"))
        clearLog(consoleData);

    uiFuncs->sameLine(0, -1);
    uiFuncs->separator();

    PDVec2 pad = { 0.0f, 0.0f };

    uiFuncs->pushStyleVarV(PDStyleVar_FramePadding, pad);

    //static ImGuiTextFilter filter;
    //filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180);

    if (uiFuncs->isItemHovered())
        uiFuncs->setKeyboardFocusHere(-1); // Auto focus on hover

    uiFuncs->popStyleVar(1);

    uiFuncs->separator();

    PDVec2 spacing = { 0, -uiFuncs->getTextLineSpacing() * 2 };
    PDVec2 itemSpacing = { 4.0f, 1.0f };

    uiFuncs->beginChild("ScrollingRegion", spacing, false, PDWindowFlags(0));
    uiFuncs->pushStyleVarV(PDStyleVar_ItemSpacing, itemSpacing); // Tighten spacing

    for (size_t i = 0; i < consoleData->items.size(); i++)
    {
        const char* item = consoleData->items[i];
        //if (!filter.PassFilter(item))
        //    continue;
        PDVec4 col = { 1.0f, 1.0f, 1.0f, 1.0f }; // A better implementation may store a type per-item. For now let's just parse the text.
        if (strstr(item, "[Error]"))
            col = { 1.0f, 0.4f, 0.4f, 1.0f };
        else if (strncmp(item, "# ", 2) == 0)
            col = { 1.0f, 0.8f, 0.6f, 1.0f };
        uiFuncs->textColored(col, item);
    }

    if (consoleData->scrollToBottom)
        uiFuncs->setScrollHere();

    consoleData->scrollToBottom = false;

    uiFuncs->popStyleVar(1);
    uiFuncs->endChild();
    uiFuncs->separator();

    // Command Line

    if (uiFuncs->inputText("Input", consoleData->inputBuffer, sizeof(consoleData->inputBuffer), PDInputTextFlags_EnterReturnsTrue | PDInputTextFlags_CallbackCompletion | PDInputTextFlags_CallbackHistory, &textEditCallbackStub, (void*)consoleData))
    {
        char* inputEnd = consoleData->inputBuffer + strlen(consoleData->inputBuffer);

        while (inputEnd > consoleData->inputBuffer && inputEnd[-1] == ' ')
            inputEnd--;

        *inputEnd = 0;

        if (consoleData->inputBuffer[0])
            execCommand(consoleData, consoleData->inputBuffer);

        strcpy(consoleData->inputBuffer, "");
    }

    if (uiFuncs->isItemHovered())
        uiFuncs->setKeyboardFocusHere(-1); // Auto focus on hover
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static int update(void* userData, PDUI* uiFuncs, PDReader* inEvents, PDWriter* outEvents)
{
    ConsoleData* consoleData = (ConsoleData*)userData;

    uint32_t event = 0;
    (void)event;

    /*while ((event = PDRead_getEvent(inEvents)) != 0)
       {
        switch (event)
        {
            case PDEventType_setConsole:
            {
                //showInUI((ConsoleData*)userData, inEvents, uiFuncs);
                break;
            }
        }
       }*/

    showInUI(consoleData, inEvents, uiFuncs);

    for (size_t i = 0; i < consoleData->scripts.size(); ++i)
    {
        PDWrite_eventBegin(outEvents, PDEventType_executeConsole);
        PDWrite_string(outEvents, "command", consoleData->scripts[i]);   // TODO: Remove me
        PDWrite_eventEnd(outEvents);
        //free(consoleData->scripts[i]);
    }

    consoleData->scripts.clear();

    // Request console data

    PDWrite_eventBegin(outEvents, PDEventType_getConsole);
    PDWrite_u8(outEvents, "dummy_remove", 0);   // TODO: Remove me
    PDWrite_eventEnd(outEvents);

    return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static PDViewPlugin plugin =
{
    "Console",
    createInstance,
    destroyInstance,
    update,
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

extern "C"
{

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

PD_EXPORT void InitPlugin(RegisterPlugin* registerPlugin, void* privateData)
{
	registerPlugin(PD_VIEW_API_VERSION, &plugin, privateData);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}

<|endoftext|>


<commit_before>//Tested on:
//	FreeBSD 9.2 (g++)
//	FreeBSD 10.0 (g++)

#include "netstat.hpp"

#include <cstdlib>
#include <stdexcept>
#include <iomanip>
#include <iostream>
#include <string>
#include <stdint.h>
#include <sstream>
#include <vector>

#include <sys/file.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <sys/socketvar.h>

#include <netinet/in.h>
#include <netinet/in_pcb.h>
#include <netinet/tcp_var.h>

#include "netstat_util.hpp"
#include "string_util.hpp"

typedef std::vector<xfile*> xfilep_list_t;

static netstat_list_t netstat_bsd_parse(const std::string& proto)
{
	size_t xflen=0;

	if(sysctlbyname("kern.file",NULL,&xflen,NULL,0)==-1)
		throw std::runtime_error("error getting data size.");

	std::vector<uint8_t> xfbuf;
	xfbuf.resize(xflen);

	if(sysctlbyname("kern.file",(char*)&xfbuf[0],&xflen,NULL,0)==-1)
		throw std::runtime_error("error getting data.");

	netstat_list_t netstats;

	size_t len=0;
	std::string sysctl_path="";

	if(proto=="tcp4")
		sysctl_path="net.inet.tcp.pcblist";
	if(proto=="udp4")
		sysctl_path="net.inet.udp.pcblist";

	#if(defined(AF_INET6))
		if(proto=="tcp6")
			sysctl_path="net.inet.tcp.pcblist";
		if(proto=="udp6")
			sysctl_path="net.inet.udp.pcblist";
	#endif

	if(sysctl_path=="")
		throw std::runtime_error("Invalid protocol.");

	if(sysctlbyname(sysctl_path.c_str(),NULL,&len,NULL,0)==-1)
		throw std::runtime_error("error getting data size.");

	std::vector<uint8_t> buf;
	buf.resize(len);

	if(sysctlbyname(sysctl_path.c_str(),(char*)&buf[0],&len,NULL,0)==-1)
		throw std::runtime_error("error getting data.");

	for(size_t ii=0;ii<len;)
	{
		xtcpcb* entry_tcp=(xtcpcb*)((char*)&buf[0]+ii);
		xinpcb* entry_udp=(xinpcb*)((char*)&buf[0]+ii);

		if(proto=="tcp4"&&entry_tcp->xt_inp.inp_vflag&INP_IPV4)
		{
			netstat_t netstat;
			netstat.proto=proto;
			netstat.local_address=uint32_t_to_ipv4(*(uint32_t*)&entry_tcp->xt_inp.inp_laddr);
			netstat.foreign_address=uint32_t_to_ipv4(*(uint32_t*)&entry_tcp->xt_inp.inp_faddr);
			netstat.local_port=uint16_t_to_port(entry_tcp->xt_inp.inp_lport);
			netstat.foreign_port=uint16_t_to_port(entry_tcp->xt_inp.inp_fport);
			netstat.state="ESTABLISHED";

			if(netstat.foreign_address=="0.0.0.0")
				netstat.state="LISTEN";

			netstat.pid="-";

			for(size_t ii=0;ii<xflen;ii+=sizeof(xfile))
			{
				xfile* xf=(xfile*)((char*)&xfbuf[0]+ii);

				if((void*)entry_tcp->xt_socket.xso_so==xf->xf_data)
				{
					netstat.pid=to_string(xf->xf_pid);
					break;
				}
			}

			netstats.push_back(netstat);
		}

		if(proto=="udp4"&&entry_udp->xi_inp.inp_vflag&INP_IPV4)
		{
			netstat_t netstat;
			netstat.proto=proto;
			netstat.local_address=uint32_t_to_ipv4(*(uint32_t*)&entry_udp->xi_inp.inp_laddr);
			netstat.foreign_address="0.0.0.0";
			netstat.local_port=uint16_t_to_port(entry_udp->xi_inp.inp_lport);
			netstat.foreign_port="0";
			netstat.state="-";
			netstat.pid="-";

			for(size_t ii=0;ii<xflen;ii+=sizeof(xfile))
			{
				xfile* xf=(xfile*)((char*)&xfbuf[0]+ii);

				if((void*)entry_udp->xi_socket.xso_so==xf->xf_data)
				{
					netstat.pid=to_string(xf->xf_pid);
					break;
				}
			}

			netstats.push_back(netstat);
		}

		#if(defined(AF_INET6))
			if(proto=="tcp6"&&entry_tcp->xt_inp.inp_vflag&INP_IPV6)
			{
				netstat_t netstat;
				netstat.proto=proto;
				netstat.local_address=uint8_t_16_to_ipv6((uint8_t*)&entry_tcp->xt_inp.in6p_laddr);
				netstat.foreign_address=uint8_t_16_to_ipv6((uint8_t*)&entry_tcp->xt_inp.in6p_faddr);
				netstat.local_port=uint16_t_to_port(entry_tcp->xt_inp.inp_lport);
				netstat.foreign_port=uint16_t_to_port(entry_tcp->xt_inp.inp_fport);
				netstat.state="ESTABLISHED";

				if(netstat.foreign_address=="0000:0000:0000:0000:0000:0000:0000:0000")
					netstat.state="LISTEN";

				netstat.pid="-";

				for(size_t ii=0;ii<xflen;ii+=sizeof(xfile))
				{
					xfile* xf=(xfile*)((char*)&xfbuf[0]+ii);

					if((void*)entry_tcp->xt_socket.xso_so==xf->xf_data)
					{
						netstat.pid=to_string(xf->xf_pid);
						break;
					}
				}

				netstats.push_back(netstat);
			}

			if(proto=="udp6"&&entry_udp->xi_inp.inp_vflag&INP_IPV6)
			{
				netstat_t netstat;
				netstat.proto=proto;
				netstat.local_address=uint8_t_16_to_ipv6((uint8_t*)&entry_udp->xi_inp.in6p_laddr);
				netstat.foreign_address="0000:0000:0000:0000:0000:0000:0000:0000";
				netstat.local_port=uint16_t_to_port(entry_udp->xi_inp.inp_lport);
				netstat.foreign_port="0";
				netstat.state="-";
				netstat.pid="-";

				for(size_t ii=0;ii<xflen;ii+=sizeof(xfile))
				{
					xfile* xf=(xfile*)((char*)&xfbuf[0]+ii);

					if((void*)entry_udp->xi_socket.xso_so==xf->xf_data)
					{
						netstat.pid=to_string(xf->xf_pid);
						break;
					}
				}

				netstats.push_back(netstat);
			}
		#endif

		ii+=((xinpgen*)((char*)&buf[0]+ii))->xig_len;
	}

	return netstats;
}

netstat_list_t netstat()
{
	netstat_list_t netstats;
	netstat_list_t tcp4=netstat_bsd_parse("tcp4");
	netstat_list_t udp4=netstat_bsd_parse("udp4");

	#if(defined(AF_INET6))
		netstat_list_t tcp6=netstat_bsd_parse("tcp6");
		netstat_list_t udp6=netstat_bsd_parse("udp6");
	#endif

	for(size_t ii=0;ii<tcp4.size();++ii)
		netstats.push_back(tcp4[ii]);

	#if(defined(AF_INET6))
		for(size_t ii=0;ii<tcp6.size();++ii)
			netstats.push_back(tcp6[ii]);
	#endif

	for(size_t ii=0;ii<udp4.size();++ii)
		netstats.push_back(udp4[ii]);

	#if(defined(AF_INET6))
		for(size_t ii=0;ii<udp6.size();++ii)
			netstats.push_back(udp6[ii]);
	#endif

	return netstats;
}<commit_msg>Making states homogeneous...<commit_after>//Tested on:
//	FreeBSD 9.2 (g++)
//	FreeBSD 10.0 (g++)

#include "netstat.hpp"

#include <cstdlib>
#include <stdexcept>
#include <iomanip>
#include <iostream>
#include <string>
#include <stdint.h>
#include <sstream>
#include <vector>

#include <sys/file.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <sys/socketvar.h>

#include <netinet/in.h>
#include <netinet/in_pcb.h>
#include <netinet/tcp_var.h>

#include "netstat_util.hpp"
#include "string_util.hpp"

typedef std::vector<xfile*> xfilep_list_t;

static netstat_list_t netstat_bsd_parse(const std::string& proto)
{
	size_t xflen=0;

	if(sysctlbyname("kern.file",NULL,&xflen,NULL,0)==-1)
		throw std::runtime_error("error getting data size.");

	std::vector<uint8_t> xfbuf;
	xfbuf.resize(xflen);

	if(sysctlbyname("kern.file",(char*)&xfbuf[0],&xflen,NULL,0)==-1)
		throw std::runtime_error("error getting data.");

	netstat_list_t netstats;

	size_t len=0;
	std::string sysctl_path="";

	if(proto=="tcp4")
		sysctl_path="net.inet.tcp.pcblist";
	if(proto=="udp4")
		sysctl_path="net.inet.udp.pcblist";

	#if(defined(AF_INET6))
		if(proto=="tcp6")
			sysctl_path="net.inet.tcp.pcblist";
		if(proto=="udp6")
			sysctl_path="net.inet.udp.pcblist";
	#endif

	if(sysctl_path=="")
		throw std::runtime_error("Invalid protocol.");

	if(sysctlbyname(sysctl_path.c_str(),NULL,&len,NULL,0)==-1)
		throw std::runtime_error("error getting data size.");

	std::vector<uint8_t> buf;
	buf.resize(len);

	if(sysctlbyname(sysctl_path.c_str(),(char*)&buf[0],&len,NULL,0)==-1)
		throw std::runtime_error("error getting data.");

	for(size_t ii=0;ii<len;)
	{
		xtcpcb* entry_tcp=(xtcpcb*)((char*)&buf[0]+ii);
		xinpcb* entry_udp=(xinpcb*)((char*)&buf[0]+ii);

		if(proto=="tcp4"&&entry_tcp->xt_inp.inp_vflag&INP_IPV4)
		{
			netstat_t netstat;
			netstat.proto=proto;
			netstat.local_address=uint32_t_to_ipv4(*(uint32_t*)&entry_tcp->xt_inp.inp_laddr);
			netstat.foreign_address=uint32_t_to_ipv4(*(uint32_t*)&entry_tcp->xt_inp.inp_faddr);
			netstat.local_port=uint16_t_to_port(entry_tcp->xt_inp.inp_lport);
			netstat.foreign_port=uint16_t_to_port(entry_tcp->xt_inp.inp_fport);
			netstat.state="ESTABLISHED";

			if(netstat.foreign_address=="0.0.0.0")
				netstat.state="LISTEN";

			netstat.pid="-";

			for(size_t ii=0;ii<xflen;ii+=sizeof(xfile))
			{
				xfile* xf=(xfile*)((char*)&xfbuf[0]+ii);

				if((void*)entry_tcp->xt_socket.xso_so==xf->xf_data)
				{
					netstat.pid=to_string(xf->xf_pid);
					break;
				}
			}

			if(netstat.pid=="-")
				netstat.state="TIME_WAIT";

			netstats.push_back(netstat);
		}

		if(proto=="udp4"&&entry_udp->xi_inp.inp_vflag&INP_IPV4)
		{
			netstat_t netstat;
			netstat.proto=proto;
			netstat.local_address=uint32_t_to_ipv4(*(uint32_t*)&entry_udp->xi_inp.inp_laddr);
			netstat.foreign_address="0.0.0.0";
			netstat.local_port=uint16_t_to_port(entry_udp->xi_inp.inp_lport);
			netstat.foreign_port="0";
			netstat.state="-";
			netstat.pid="-";

			for(size_t ii=0;ii<xflen;ii+=sizeof(xfile))
			{
				xfile* xf=(xfile*)((char*)&xfbuf[0]+ii);

				if((void*)entry_udp->xi_socket.xso_so==xf->xf_data)
				{
					netstat.pid=to_string(xf->xf_pid);
					break;
				}
			}

			netstats.push_back(netstat);
		}

		#if(defined(AF_INET6))
			if(proto=="tcp6"&&entry_tcp->xt_inp.inp_vflag&INP_IPV6)
			{
				netstat_t netstat;
				netstat.proto=proto;
				netstat.local_address=uint8_t_16_to_ipv6((uint8_t*)&entry_tcp->xt_inp.in6p_laddr);
				netstat.foreign_address=uint8_t_16_to_ipv6((uint8_t*)&entry_tcp->xt_inp.in6p_faddr);
				netstat.local_port=uint16_t_to_port(entry_tcp->xt_inp.inp_lport);
				netstat.foreign_port=uint16_t_to_port(entry_tcp->xt_inp.inp_fport);
				netstat.state="ESTABLISHED";

				if(netstat.foreign_address=="0000:0000:0000:0000:0000:0000:0000:0000")
					netstat.state="LISTEN";

				netstat.pid="-";

				for(size_t ii=0;ii<xflen;ii+=sizeof(xfile))
				{
					xfile* xf=(xfile*)((char*)&xfbuf[0]+ii);

					if((void*)entry_tcp->xt_socket.xso_so==xf->xf_data)
					{
						netstat.pid=to_string(xf->xf_pid);
						break;
					}
				}

				if(netstat.pid=="-")
					netstat.state="TIME_WAIT";

				netstats.push_back(netstat);
			}

			if(proto=="udp6"&&entry_udp->xi_inp.inp_vflag&INP_IPV6)
			{
				netstat_t netstat;
				netstat.proto=proto;
				netstat.local_address=uint8_t_16_to_ipv6((uint8_t*)&entry_udp->xi_inp.in6p_laddr);
				netstat.foreign_address="0000:0000:0000:0000:0000:0000:0000:0000";
				netstat.local_port=uint16_t_to_port(entry_udp->xi_inp.inp_lport);
				netstat.foreign_port="0";
				netstat.state="-";
				netstat.pid="-";

				for(size_t ii=0;ii<xflen;ii+=sizeof(xfile))
				{
					xfile* xf=(xfile*)((char*)&xfbuf[0]+ii);

					if((void*)entry_udp->xi_socket.xso_so==xf->xf_data)
					{
						netstat.pid=to_string(xf->xf_pid);
						break;
					}
				}

				netstats.push_back(netstat);
			}
		#endif

		ii+=((xinpgen*)((char*)&buf[0]+ii))->xig_len;
	}

	return netstats;
}

netstat_list_t netstat()
{
	netstat_list_t netstats;
	netstat_list_t tcp4=netstat_bsd_parse("tcp4");
	netstat_list_t udp4=netstat_bsd_parse("udp4");

	#if(defined(AF_INET6))
		netstat_list_t tcp6=netstat_bsd_parse("tcp6");
		netstat_list_t udp6=netstat_bsd_parse("udp6");
	#endif

	for(size_t ii=0;ii<tcp4.size();++ii)
		netstats.push_back(tcp4[ii]);

	#if(defined(AF_INET6))
		for(size_t ii=0;ii<tcp6.size();++ii)
			netstats.push_back(tcp6[ii]);
	#endif

	for(size_t ii=0;ii<udp4.size();++ii)
		netstats.push_back(udp4[ii]);

	#if(defined(AF_INET6))
		for(size_t ii=0;ii<udp6.size();++ii)
			netstats.push_back(udp6[ii]);
	#endif

	return netstats;
}<|endoftext|>