try
        {
            // Serialize the glTF Document into a JSON manifest
            manifest = Serialize(document, SerializeFlags::Pretty);
        }
        catch (const GLTFException& ex)
        {
            std::stringstream ss;

            ss << "Microsoft::glTF::Serialize failed: ";
            ss << ex.what();

            throw std::runtime_error(ss.str());
        }

#include <GLTFSDK/GLTF.h>
#include <GLTFSDK/BufferBuilder.h>
#include <GLTFSDK/GLTFResourceWriter.h>
#include <GLTFSDK/GLBResourceWriter.h>
#include <GLTFSDK/IStreamWriter.h>
#include <GLTFSDK/Serialize.h>

// Replace this with <filesystem> (and use std::filesystem rather than
// std::experimental::filesystem) if your toolchain fully supports C++17
#include <experimental/filesystem>

#include <fstream>
#include <sstream>
#include <iostream>

#include <cassert>
#include <cstdlib>

using namespace Microsoft::glTF;

namespace
{
    // The glTF SDK is decoupled from all file I/O by the IStreamWriter (and IStreamReader)
    // interface(s) and the C++ stream-based I/O library. This allows the glTF SDK to be used in
    // sandboxed environments, such as WebAssembly modules and UWP apps, where any file I/O code
    // must be platform or use-case specific.
    class StreamWriter : public IStreamWriter
    {
    public:
        StreamWriter(std::experimental::filesystem::path pathBase) : m_pathBase(std::move(pathBase))
        {
            assert(m_pathBase.has_root_path());
        }

        // Resolves the relative URIs of any external resources declared in the glTF manifest
        std::shared_ptr<std::ostream> GetOutputStream(const std::string& filename) const override
        {
            // In order to construct a valid stream:
            // 1. The filename argument will be encoded as UTF-8 so use filesystem::u8path to
            //    correctly construct a path instance.
            // 2. Generate an absolute path by concatenating m_pathBase with the specified filename
            //    path. The filesystem::operator/ uses the platform's preferred directory separator
            // 3. Always open the file stream in binary mode. The glTF SDK will handle any text
            //    encoding issues for us.
            auto streamPath = m_pathBase / std::experimental::filesystem::u8path(filename);
            auto stream = std::make_shared<std::ofstream>(streamPath, std::ios_base::binary);

            // Check if the stream has no errors and is ready for I/O operations
            if (!stream || !(*stream))
            {
                throw std::runtime_error("Unable to create a valid output stream for uri: " + filename);
            }

            return stream;
        }

    private:
        std::experimental::filesystem::path m_pathBase;
    };

    void CreateTriangleResources(Document& document, BufferBuilder& bufferBuilder, std::string& accessorIdIndices, std::string& accessorIdPositions)
    {
        // Create all the resource data (e.g. triangle indices and
        // vertex positions) that will be written to the binary buffer
        const char* bufferId = nullptr;

        // Specify the 'special' GLB buffer ID. This informs the GLBResourceWriter that it should use
        // the GLB container's binary chunk (usually the desired buffer location when creating GLBs)
        if (dynamic_cast<const GLBResourceWriter*>(&bufferBuilder.GetResourceWriter()))
        {
            bufferId = GLB_BUFFER_ID;
        }

        // Create a Buffer - it will be the 'current' Buffer that all the BufferViews
        // created by this BufferBuilder will automatically reference
        bufferBuilder.AddBuffer(bufferId);

        // Create a BufferView with a target of ELEMENT_ARRAY_BUFFER (as it will reference index
        // data) - it will be the 'current' BufferView that all the Accessors created by this
        // BufferBuilder will automatically reference
        bufferBuilder.AddBufferView(BufferViewTarget::ELEMENT_ARRAY_BUFFER);

        // Add an Accessor for the indices
        std::vector<uint16_t> indices = {
            0U, 1U, 2U
        };

        // Copy the Accessor's id - subsequent calls to AddAccessor may invalidate the returned reference
        accessorIdIndices = bufferBuilder.AddAccessor(indices, { TYPE_SCALAR, COMPONENT_UNSIGNED_SHORT }).id;

        // Create a BufferView with target ARRAY_BUFFER (as it will reference vertex attribute data)
        bufferBuilder.AddBufferView(BufferViewTarget::ARRAY_BUFFER);

        // Add an Accessor for the positions
        std::vector<float> positions = {
            0.0f, 0.0f, 0.0f, // Vertex 0
            1.0f, 0.0f, 0.0f, // Vertex 1
            0.0f, 1.0f, 0.0f  // Vertex 2
        };

        std::vector<float> minValues(3U, std::numeric_limits<float>::max());
        std::vector<float> maxValues(3U, std::numeric_limits<float>::lowest());

        const size_t positionCount = positions.size();

        // Accessor min/max properties must be set for vertex position data so calculate them here
        for (size_t i = 0U, j = 0U; i < positionCount; ++i, j = (i % 3U))
        {
            minValues[j] = std::min(positions[i], minValues[j]);
            maxValues[j] = std::max(positions[i], maxValues[j]);
        }

        accessorIdPositions = bufferBuilder.AddAccessor(positions, { TYPE_VEC3, COMPONENT_FLOAT, false, std::move(minValues), std::move(maxValues) }).id;

        // Add all of the Buffers, BufferViews and Accessors that were created using BufferBuilder to
        // the Document. Note that after this point, no further calls should be made to BufferBuilder
        bufferBuilder.Output(document);
    }

    void CreateTriangleEntities(Document& document, const std::string& accessorIdIndices, const std::string& accessorIdPositions)
    {
        // Create a very simple glTF Document with the following hierarchy:
        //  Scene
        //     Node
        //         MeshPrimitive
        // A Document can be constructed top-down or bottom up. However, if constructed top-down
        // then the IDs of child entities must be known in advance, which prevents using the glTF
        // SDK's automatic ID generation functionality.

        // Construct a Material
        Material material;
        material.metallicRoughness.baseColorFactor = Color4(0.0f, 0.0f, 1.0f, 1.0f);
        material.metallicRoughness.metallicFactor = 0.2f;
        material.metallicRoughness.roughnessFactor = 0.4f;
        material.doubleSided = true;

        // Add it to the Document and store the generated ID
        auto materialId = document.materials.Append(std::move(material), AppendIdPolicy::GenerateOnEmpty).id;

        // Construct a MeshPrimitive. Unlike most types in glTF, MeshPrimitives are direct children
        // of their parent Mesh entity rather than being children of the Document. This is why they
        // don't have an ID member.
        MeshPrimitive meshPrimitive;
        meshPrimitive.materialId = materialId;
        meshPrimitive.indicesAccessorId = accessorIdIndices;
        meshPrimitive.attributes[ACCESSOR_POSITION] = accessorIdPositions;

        // Construct a Mesh and add the MeshPrimitive as a child
        Mesh mesh;
        mesh.primitives.push_back(std::move(meshPrimitive));
        // Add it to the Document and store the generated ID
        auto meshId = document.meshes.Append(std::move(mesh), AppendIdPolicy::GenerateOnEmpty).id;

        // Construct a Node adding a reference to the Mesh
        Node node;
        node.meshId = meshId;
        // Add it to the Document and store the generated ID
        auto nodeId = document.nodes.Append(std::move(node), AppendIdPolicy::GenerateOnEmpty).id;

        // Construct a Scene
        Scene scene;
        scene.nodes.push_back(nodeId);
        // Add it to the Document, using a utility method that also sets the Scene as the Document's default
        document.SetDefaultScene(std::move(scene), AppendIdPolicy::GenerateOnEmpty);
    }

    void SerializeTriangle(const std::experimental::filesystem::path& path)
    {
        // Pass the absolute path, without the filename, to the stream writer
        auto streamWriter = std::make_unique<StreamWriter>(path.parent_path());

        std::experimental::filesystem::path pathFile = path.filename();
        std::experimental::filesystem::path pathFileExt = pathFile.extension();

        auto MakePathExt = [](const std::string& ext)
        {
            return "." + ext;
        };

        std::unique_ptr<ResourceWriter> resourceWriter;

        // If the file has a '.gltf' extension then create a GLTFResourceWriter
        if (pathFileExt == MakePathExt(GLTF_EXTENSION))
        {
            resourceWriter = std::make_unique<GLTFResourceWriter>(std::move(streamWriter));
        }

        // If the file has a '.glb' extension then create a GLBResourceWriter. This class derives
        // from GLTFResourceWriter and adds support for writing manifests to a GLB container's
        // JSON chunk and resource data to the binary chunk.
        if (pathFileExt == MakePathExt(GLB_EXTENSION))
        {
            resourceWriter = std::make_unique<GLBResourceWriter>(std::move(streamWriter));
        }

        if (!resourceWriter)
        {
            throw std::runtime_error("Command line argument path filename extension must be .gltf or .glb");
        }

        // The Document instance represents the glTF JSON manifest
        Document document;

        std::string accessorIdIndices;
        std::string accessorIdPositions;

        // Use the BufferBuilder helper class to simplify the process of
        // constructing valid glTF Buffer, BufferView and Accessor entities
        BufferBuilder bufferBuilder(std::move(resourceWriter));

        CreateTriangleResources(document, bufferBuilder, accessorIdIndices, accessorIdPositions);
        CreateTriangleEntities(document, accessorIdIndices, accessorIdPositions);

        std::string manifest;

        auto& gltfResourceWriter = bufferBuilder.GetResourceWriter();

        if (auto glbResourceWriter = dynamic_cast<GLBResourceWriter*>(&gltfResourceWriter))
        {
            glbResourceWriter->Flush(manifest, pathFile.u8string()); // A GLB container isn't created until the GLBResourceWriter::Flush member function is called
        }
        else
        {
            gltfResourceWriter.WriteExternal(pathFile.u8string(), manifest); // Binary resources have already been written, just need to write the manifest
        }
    }
}

#if defined _WIN32 && defined _UNICODE
int wmain(int argc, wchar_t* argv[])
#else // _WIN32 & _UNICODE
int main(int argc, char* argv[])
#endif
{
    try
    {
        if (argc != 2U)
        {
            throw std::runtime_error("Unexpected number of command line arguments");
        }

        std::experimental::filesystem::path path = argv[1U];

        if (path.is_relative())
        {
            auto pathCurrent = std::experimental::filesystem::current_path();

            // Convert the relative path into an absolute path by appending the command line argument to the current path
            pathCurrent /= path;
            pathCurrent.swap(path);
        }

        if (!path.has_filename())
        {
            throw std::runtime_error("Command line argument path has no filename");
        }

        if (!path.has_extension())
        {
            throw std::runtime_error("Command line argument path has no filename extension");
        }

        SerializeTriangle(path);
    }
    catch (const std::runtime_error& ex)
    {
        std::cerr << "Error! - ";
        std::cerr << ex.what() << "\n";

        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}


try {
            int32 tempHandle;
            if(buf.Get(tempHandle))
            {
                writeHandle = true;
                handle = tempHandle;
            }
            else
            {
                handle = -1;
            }
        }
        catch (...) {
            handle = -1;
        }

/* ************************
   unless otherwise specified in writing by the President of TERREX.
   This copyright may be updated in the future, in which case that version
   supercedes this one.
   -------------------
   Terrex Experts Inc.
   4400 East Broadway #314
   Tucson, AZ  85711
   example@nnthink.com
   Tel: (520) 323-7990
   ************************
   */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* trpage_material.cpp
   This source file contains the methods for trpgMatTable, trpgTextureEnv,
   trpgMaterial, and trpgTexTable.
   You should only modify this code if you want to add data to these classes.
*/

#include <trpage_geom.h>
#include <trpage_read.h>

#ifdef _MSC_VER
#define strcasecmp _stricmp
#endif

/* Write Material Table class
   Keeps track of the materials that have been added.
*/

// Constructor
trpgMatTable::trpgMatTable()
{
    numTable = numMat = 0;
}
trpgMatTable::~trpgMatTable()
{
}

// Reset function
void trpgMatTable::Reset()
{
    numTable = 0;
    numMat = 0;
    materialMap.clear();
}

// Validity check
bool trpgMatTable::isValid() const
{
    if(materialMap.size()==0)
    return false;
    // get an iterator for the materialMap
    MaterialMapType::const_iterator itr = materialMap.begin();
    for (  ; itr != materialMap.end( ); itr++) {
        if(!(*itr).second.isValid()) {
            return false;
        }
    }

    return true;
}

// Set functions
void trpgMatTable::SetNumTable(int no)
{
    if ((no < 0) || (no==numTable))
        return;
    numTable = no;
}
void trpgMatTable::SetNumMaterial(int /*no*/)
{
    // This method is obsolete since we're using the project handle
    // and a map to store the materials, instead of a vector
}

void trpgMatTable::SetMaterial(int nm,const trpgMaterial &mat)
{
    materialMap[nm] = mat;
    numMat = materialMap.size();
}

#define CEQ(ca,cb) (ca.red == cb.red && ca.green == cb.green && ca.blue == cb.blue)

int trpgMatTable::AddMaterial(const trpgMaterial &mat,bool lookForExisting)
{
    trpgMaterial cmat = mat; // necessary?

    // having a shadeModel of 999 indicates that the entry is free.  I thought this would
    // work fine, until I realized that evidently most of the time the shademodel isn't set
    // at all.  Now my kludge takes so much work it's almost worth doing it right.

    if (cmat.shadeModel>100) cmat.shadeModel=trpgMaterial::Smooth;

    int baseMat=0;
    //bool spaceInTable=false;

    if (lookForExisting) {
        // Look for a matching base material minus the textures
        MaterialMapType::const_iterator itr = materialMap.begin();
        for (  ; itr != materialMap.end( ); itr++) {
            baseMat = itr->first;
            const trpgMaterial &bm = itr->second;
            if (bm.shadeModel==999) {
                // this is an 'empty' entry.  Means we won't find it, either.
                break;
            }

            // Compare structures
            if (CEQ(cmat.color,bm.color) && CEQ(cmat.ambient,bm.ambient) &&
                CEQ(cmat.diffuse,bm.diffuse) && CEQ(cmat.specular,bm.specular) &&
                CEQ(cmat.emission,bm.emission) && cmat.shininess == bm.shininess &&
                cmat.shadeModel == bm.shadeModel && cmat.pointSize == bm.pointSize &&
                cmat.lineWidth == bm.lineWidth && cmat.cullMode == bm.cullMode &&
                cmat.alphaFunc == bm.alphaFunc && cmat.alphaRef == bm.alphaRef &&
                cmat.attrSet.fid == bm.attrSet.fid && cmat.attrSet.smc == bm.attrSet.smc &&
                cmat.attrSet.stp == bm.attrSet.stp && cmat.attrSet.swc == bm.attrSet.swc &&
                cmat.autoNormal == bm.autoNormal && cmat.texEnvs.size() == bm.texEnvs.size()) {
                // Test the texture envs
                bool isSame=true;
                unsigned int i;
                for (i=0;i<cmat.texEnvs.size();i++) {
                            const trpgTextureEnv &e1 = cmat.texEnvs[i];
                    const trpgTextureEnv &e2 = bm.texEnvs[i];
                    if (e1.envMode != e2.envMode ||
                    e1.minFilter != e2.minFilter ||
                    e1.magFilter != e2.magFilter ||
                    e1.wrapS != e2.wrapS || e1.wrapT != e2.wrapT ||
                    !CEQ(e1.borderCol,e2.borderCol))
                    isSame = false;
                }
                // Test the texture IDs
                for (i=0;i<cmat.texids.size();i++) {
                    if (cmat.texids[i] != bm.texids[i])
                    isSame = false;
                }
                if (isSame)
                    return baseMat;
            }
        }
    }

    // Didn't find it.  Add it
    int idx;
    if(cmat.writeHandle)
        idx = cmat.GetHandle();
    else
        idx = numMat;
    materialMap[idx] = cmat;
    numMat = materialMap.size();
    return idx;
}

// Write out material table
bool trpgMatTable::Write(trpgWriteBuffer &buf)
{
    if (!isValid())
        return false;

    buf.Begin(TRPGMATTABLE);

    // Total number of materials
    buf.Add((int32)numTable);
    buf.Add((int32)numMat);

    // Write the materials
    MaterialMapType::const_iterator itr = materialMap.begin();
    for (  ; itr != materialMap.end( ); itr++) {
        ((trpgMaterial)(*itr).second).Write(buf);
    }
    buf.End();

    return true;
}

/* ************
    Material Table Read
   ***********
   */

// Get functions
bool trpgMatTable::GetNumTable(int &no) const
{
    if (!isValid()) {
        no = 0; // otherwise this causes errors because it is uninitialized.
        return false;
    }
    no = numTable;
    return true;
}
bool trpgMatTable::GetNumMaterial(int &no) const
{
    if (!isValid()) {
        no = 0;
        return false;
    }
    no = numMat;
    return true;
}

bool trpgMatTable::GetMaterial(int nt,int nm,trpgMaterial &mat) const
{
    if (!isValid()) return false;

    MaterialMapType::const_iterator itr = materialMap.find((nt*numMat)+nm);
    if(itr == materialMap.end())
        return false;

    mat = (*itr).second;
    return true;
}

const trpgMaterial *trpgMatTable::GetMaterialRef(int nt,int nm) const
{
    MaterialMapType::const_iterator itr = materialMap.find((nt*numMat)+nm);
    if(itr == materialMap.end())
        return 0;
    return const_cast<trpgMaterial *>(&(*itr).second);
}

bool trpgMatTable::Read(trpgReadBuffer &buf)
{
    trpgMaterial mat;
    trpgToken matTok;
    int32 len;
    bool status;
    int i,j;
    int nMat,nTable;

    try {
        buf.Get(nTable);
        buf.Get(nMat);
        if (nTable <= 0 || nMat < 0) throw 1;
        // Read the materials
        for (i=0;i<nTable;i++)
            for (j=0;j<nMat;j++) {
                buf.GetToken(matTok,len);
                if (matTok != TRPGMATERIAL) throw 1;
                buf.PushLimit(len);
                mat.Reset();
                status = mat.Read(buf);
                buf.PopLimit();
                if (!status) throw 1;
                AddMaterial(mat,false);
            }
            numTable += nTable;
            numMat = materialMap.size();
    }
    catch (...) {
        return false;
    }

    return isValid();
}

/* Write Texture Environment class
    Used to specify how a texture is applied.
    Associated with materials.
    */

// Constructor
trpgTextureEnv::trpgTextureEnv()
{
    Reset();
}
trpgTextureEnv::~trpgTextureEnv()
{
}

// Reset function
void trpgTextureEnv::Reset()
{
    envMode = Decal;
    minFilter = Linear;
    magFilter = MipmapBilinear;
    wrapS = wrapT = Repeat;
    borderCol = trpgColor(0,0,0);
}

bool trpgTextureEnv::isValid() const
{
    return true;
}

// Set functions
void trpgTextureEnv::SetEnvMode(int mode)
{
    envMode = mode;
}
void trpgTextureEnv::SetMinFilter(int fl)
{
    minFilter = fl;
}
void trpgTextureEnv::SetMagFilter(int fl)
{
    magFilter = fl;
}
void trpgTextureEnv::SetWrap(int s,int t)
{
    wrapS = s;
    wrapT = t;
}
void trpgTextureEnv::SetBorderColor(const trpgColor &col)
{
    borderCol = col;
}

// Write function
bool trpgTextureEnv::Write(trpgWriteBuffer &buf)
{
    if (!isValid())
    return false;

    buf.Begin(TRPGMAT_TEXENV);

    buf.Begin(TRPGMAT_TXENV_MODE);
    buf.Add(envMode);
    buf.End();

    buf.Begin(TRPGMAT_TXENV_FILTER);
    buf.Add(minFilter);
    buf.Add(magFilter);
    buf.End();

    buf.Begin(TRPGMAT_TXENV_WRAP);
    buf.Add(wrapS);
    buf.Add(wrapT);
    buf.End();

    buf.Begin(TRPGMAT_TXENV_BORDER);
    buf.Add(borderCol);
    buf.End();

    buf.End();

    return true;
}

/* **************
   Texture Env Read support
   **************
   */
// Get functions
bool trpgTextureEnv::GetEnvMode(int32 &ret) const
{
    if (!isValid()) return false;
    ret = envMode;
    return true;
}
bool trpgTextureEnv::GetMinFilter(int32 &ret) const
{
    if (!isValid()) return false;
    ret = minFilter;
    return true;
}
bool trpgTextureEnv::GetMagFilter(int32 &ret) const
{
    if (!isValid()) return false;
    ret = magFilter;
    return true;
}
bool trpgTextureEnv::GetWrap(int &S,int &T) const
{
    if (!isValid()) return false;
    S = wrapS;
    T = wrapT;
    return true;
}
bool trpgTextureEnv::GetBorderColor(trpgColor &col) const
{
    if (!isValid()) return false;
    col = borderCol;
    return true;
}

/* Texture Env CB
   Used to parse tokens for a texture Env.
*/
class textureEnvCB : public trpgr_Callback {
public:
    void *Parse(trpgToken,trpgReadBuffer &);
    trpgTextureEnv *tenv;
};

void * textureEnvCB::Parse(trpgToken tok,trpgReadBuffer &buf)
{
    int envMode;
    int minFilter;
    int magFilter;
    int wrapS,wrapT;
    trpgColor borderCol;

    try {
        switch (tok) {
        case TRPGMAT_TXENV_MODE:
            buf.Get(envMode);
            tenv->SetEnvMode(envMode);
            break;
        case TRPGMAT_TXENV_FILTER:
            buf.Get(minFilter);
            buf.Get(magFilter);
            tenv->SetMinFilter(minFilter);
            tenv->SetMagFilter(magFilter);
            break;
        case TRPGMAT_TXENV_WRAP:
            buf.Get(wrapS);
            buf.Get(wrapT);
            tenv->SetWrap(wrapS,wrapT);
            break;
        case TRPGMAT_TXENV_BORDER:
            buf.Get(borderCol);
            tenv->SetBorderColor(borderCol);
            break;
        default:
            // Don't know this token.  Skip
            break;
        }
    }
    catch (...) {
    return NULL;
    }

    return tenv;
}

bool trpgTextureEnv::Read(trpgReadBuffer &buf)
{
    trpgr_Parser parse;
    textureEnvCB teCb;

    // Texture environment is a bunch of tokens in random order
    // Interface to it with a parser
    teCb.tenv = this;
    parse.AddCallback(TRPGMAT_TXENV_MODE,&teCb,false);
    parse.AddCallback(TRPGMAT_TXENV_FILTER,&teCb,false);
    parse.AddCallback(TRPGMAT_TXENV_WRAP,&teCb,false);
    parse.AddCallback(TRPGMAT_TXENV_BORDER,&teCb,false);
    parse.Parse(buf);

    return isValid();
}

/* Write Material class
   Material representation.
*/

// Constructor
trpgMaterial::trpgMaterial()
{
    Reset();
}
trpgMaterial::~trpgMaterial()
{
}

// Reset function
void trpgMaterial::Reset()
{
    color = trpgColor(1,1,1);
    ambient = trpgColor(0,0,0);
    diffuse = trpgColor(1,1,1);
    specular = trpgColor(0,0,0);
    emission = trpgColor(0,0,0);
    shininess = 0;
    shadeModel = 999; // kludge to identify 'empty' material table entries
    pointSize = 1;
    lineWidth = 1;
    cullMode = Back;
    alphaFunc = GreaterThan;
    alphaRef = 0;
    alpha = 1.0;
    autoNormal = false;
    numTex = 0;
    texids.resize(0);
    texEnvs.resize(0);
    numTile = 0;
    isBump = false;
    attrSet.fid = -1;
    attrSet.smc = -1;
    attrSet.stp = -1;
    attrSet.swc = -1;
    handle = -1;
    writeHandle = false;
}

// Validity check
bool trpgMaterial::isValid() const
{
    // Only thing we really care about is texture
    if (numTex < 0)
    return false;

    for (int i=0;i<numTex;i++)
    if (!texEnvs[i].isValid())
        return false;

    return true;
}

// Set functions
void trpgMaterial::SetColor(const trpgColor &col)
{
    color = col;
}
void trpgMaterial::SetAmbient(const trpgColor &col)
{
    ambient = col;
}
void trpgMaterial::SetDiffuse(const trpgColor &col)
{
    diffuse = col;
}
void trpgMaterial::SetSpecular(const trpgColor &col)
{
    specular = col;
}
void trpgMaterial::SetEmission(const trpgColor &col)
{
    emission = col;
}
void trpgMaterial::SetShininess(float64 val)
{
    shininess = val;
}
void trpgMaterial::SetShadeModel(int val)
{
    shadeModel = val;
}
void trpgMaterial::SetPointSize(float64 val)
{
    pointSize = val;
}
void trpgMaterial::SetLineWidth(float64 val)
{
    lineWidth = val;
}
void trpgMaterial::SetCullMode(int val)
{
    cullMode = val;
}
void trpgMaterial::SetAlphaFunc(int val)
{
    alphaFunc = val;
}
void trpgMaterial::SetAlphaRef(float64 val)
{
    alphaRef = val;
}
void trpgMaterial::SetAlpha(float64 val)
{
    alpha = val;
}
void trpgMaterial::SetAutoNormal(bool val)
{
    autoNormal = val;
}
void trpgMaterial::SetNumTexture(int no)
{
    if (no < 0)    return;
    numTex = no;
    texids.resize(no);
    texEnvs.resize(no);
}
void trpgMaterial::SetTexture(int no,int id,const trpgTextureEnv &env)
{
    if (no < 0 || (unsigned int)no >= texids.size())
    return;

    texids[no] = id;
    texEnvs[no] = env;
}
int trpgMaterial::AddTexture(int id,const trpgTextureEnv &env)
{
    texids.push_back(id);
    texEnvs.push_back(env);
    numTex++;

    return numTex-1;
}
void trpgMaterial::SetNumTiles(int no)
{
    numTile = no;
}
int trpgMaterial::AddTile()
{
    return(++numTile);
}
void trpgMaterial::SetIsBumpMap(bool val)
{
    isBump = val;
}
void trpgMaterial::SetAttr(int attrCode,int val)
{
    switch (attrCode) {
    case TR_FID:
        attrSet.fid = val;
        break;
    case TR_SMC:
        attrSet.smc = val;
        break;
    case TR_STP:
        attrSet.stp = val;
        break;
    case TR_SWC:
        attrSet.swc = val;
        break;
    }

    return;
}

// Write to buffer
bool trpgMaterial::Write(trpgWriteBuffer &buf)
{
    if (!isValid())
        return false;

    buf.Begin(TRPGMATERIAL);

    // Bundle the basic material parameters together
    buf.Begin(TRPGMAT_BASIC);
    buf.Add(color);
    buf.Add(ambient);
    buf.Add(diffuse);
    buf.Add(specular);
    buf.Add(emission);
    buf.Add(shininess);
    buf.Add(numTile);
    buf.End();

    // Most everything else is a single token.
    // This is inefficient, but expandable
    buf.Begin(TRPGMAT_SHADE);
    buf.Add(shadeModel);
    buf.End();

    buf.Begin(TRPGMAT_SIZES);
    buf.Add(pointSize);
    buf.Add(lineWidth);
    buf.End();

    buf.Begin(TRPGMAT_CULL);
    buf.Add(cullMode);
    buf.End();

    buf.Begin(TRPGMAT_ALPHA);
    buf.Add(alphaFunc);
    buf.Add(alphaRef);
    buf.Add(alpha);
    buf.End();

    buf.Begin(TRPGMAT_NORMAL);
    buf.Add((int32)autoNormal);
    buf.End();

    buf.Begin(TRPGMAT_TEXTURE);
    buf.Add(numTex);
    for (int i=0;i<numTex;i++) {
        buf.Add(texids[i]);
        texEnvs[i].Write(buf);
    }
    buf.End();

    // Bump mapping properties
    buf.Begin(TRPGMAT_BUMP);
    buf.Add((int32)isBump);
    buf.End();

    buf.Begin(TRPGMAT_ATTR);
    buf.Add(attrSet.fid);
    buf.Add(attrSet.smc);
    buf.Add(attrSet.stp);
    buf.Add(attrSet.swc);
    buf.End();

    /**
     * If the terrapage version is >= 2.3, handle will be set to a unique identifier.
     **/
    if(writeHandle) {
        buf.Begin(TRPGMAT_HANDLE);
        buf.Add((int)handle);
        buf.End();
    }

    buf.End();

    return true;
}

/* ***************
   Material Read methods
   ***************
   */
// Get methods
bool trpgMaterial::GetColor(trpgColor &col) const
{
    if (!isValid()) return false;
    col = color;
    return true;
}
bool trpgMaterial::GetAmbient(trpgColor &col) const
{
    if (!isValid()) return false;
    col = ambient;
    return true;
}
bool trpgMaterial::GetDiffuse(trpgColor &col) const
{
    if (!isValid()) return false;
    col = diffuse;
    return true;
}
bool trpgMaterial::GetSpecular(trpgColor &col) const
{
    if (!isValid()) return false;
    col = specular;
    return true;
}
bool trpgMaterial::GetEmission(trpgColor &col) const
{
    if (!isValid()) return false;
    col = emission;
    return true;
}
bool trpgMaterial::GetShininess(float64 &shin) const
{
    if (!isValid()) return false;
    shin = shininess;
    return true;
}
bool trpgMaterial::GetShadeModel(int &sm) const
{
    if (!isValid()) return false;
    sm = shadeModel;
    return true;
}
bool trpgMaterial::GetPointSize(float64 &ps) const
{
    if (!isValid()) return false;
    ps = pointSize;
    return true;
}
bool trpgMaterial::GetLineWidth(float64 &lw) const
{
    if (!isValid()) return false;
    lw = lineWidth;
    return true;
}
bool trpgMaterial::GetCullMode(int32 &cull) const
{
    if (!isValid()) return false;
    cull = cullMode;
    return true;
}
bool trpgMaterial::GetAlphaFunc(int &af) const
{
    if (!isValid()) return false;
    af = alphaFunc;
    return true;
}
bool trpgMaterial::GetAlphaRef(float64 &ar) const
{
    if (!isValid()) return false;
    ar = alphaRef;
    return true;
}
bool trpgMaterial::GetAlpha(float64 &a) const
{
    if (!isValid()) return false;
    a = alpha;
    return true;
}
bool trpgMaterial::GetAutoNormal(bool &an) const
{
    if (!isValid()) return false;
    an = autoNormal;
    return true;
}
bool trpgMaterial::GetNumTexture(int &no) const
{
    if (!isValid()) return false;
    no = numTex;
    return true;
}
bool trpgMaterial::GetTexture(int no,int &id,trpgTextureEnv &te) const
{
    if (!isValid() || no < 0  || no >= numTex)
        return false;
    id = texids[no];
    te = texEnvs[no];
    return true;
}
bool trpgMaterial::GetNumTile(int &no) const
{
    if (!isValid()) return false;
    no = numTile;
    return true;
}
bool trpgMaterial::GetIsBumpMap(bool &ret) const
{
    if (!isValid()) return false;
    ret = isBump;
    return true;
}
bool trpgMaterial::GetAttr(int attrCode,int &ret) const
{
    switch (attrCode) {
    case TR_FID:
        ret = attrSet.fid;
        break;
    case TR_SMC:
        ret = attrSet.smc;
        break;
    case TR_STP:
        ret = attrSet.stp;
        break;
    case TR_SWC:
        ret = attrSet.swc;
        break;
    default:
        return false;
    }

    return true;
}

/* Material CB
   Used to parse tokens for a material.
*/
class materialCB : public trpgr_Callback {
public:
    void * Parse(trpgToken,trpgReadBuffer &);
    trpgMaterial *mat;
};
void * materialCB::Parse(trpgToken tok,trpgReadBuffer &buf)
{
    trpgColor color;
    float64 shininess;
    int shadeModel;
    float64 size;
    int cullMode;
    int alphaFunc;
    float64 alphaRef,alpha;
    bool autoNormal;
    int numTex,texId;
    trpgToken envTok;
    trpgTextureEnv texEnv;
    int32 len,numtile;
    bool status;
    int i;

    try {
        switch (tok) {
        case TRPGMAT_BASIC:
            buf.Get(color);
            mat->SetColor(color);
            buf.Get(color);
            mat->SetAmbient(color);
            buf.Get(color);
            mat->SetDiffuse(color);
            buf.Get(color);
            mat->SetSpecular(color);
            buf.Get(color);
            mat->SetEmission(color);
            buf.Get(shininess);
            mat->SetShininess(shininess);
            buf.Get(numtile);
            mat->SetNumTiles(numtile);
            break;
        case TRPGMAT_SHADE:
            buf.Get(shadeModel);
            mat->SetShadeModel(shadeModel);
            break;
        case TRPGMAT_SIZES:
            buf.Get(size);
            mat->SetPointSize(size);
            buf.Get(size);
            mat->SetLineWidth(size);
            break;
        case TRPGMAT_CULL:
            buf.Get(cullMode);
            mat->SetCullMode(cullMode);
            break;
        case TRPGMAT_ALPHA:
            buf.Get(alphaFunc);
            buf.Get(alphaRef);
            buf.Get(alpha);
            mat->SetAlphaFunc(alphaFunc);
            mat->SetAlphaRef(alphaRef);
            mat->SetAlpha(alpha);
            break;
        case TRPGMAT_NORMAL:
        {
            int32 tmp;
            buf.Get(tmp);
            if (tmp)
            autoNormal = true;
            else
            autoNormal = false;
            mat->SetAutoNormal(autoNormal);
        }
        break;
        case TRPGMAT_TEXTURE:
            buf.Get(numTex);
            for (i=0;i<numTex;i++) {
                buf.Get(texId);
                // Parse the texture Env
                buf.GetToken(envTok,len);
                if (envTok != TRPGMAT_TEXENV)  throw 1;
                buf.PushLimit(len);
                status = texEnv.Read(buf);
                buf.PopLimit();
                if (!status) throw 1;

                mat->AddTexture(texId,texEnv);
            }
            break;
        case TRPGMAT_BUMP:
        {
            int32 tmp;
            buf.Get(tmp);
            bool isBump = (tmp) ? true : false;
            mat->SetIsBumpMap(isBump);
        }
        break;
        case TRPGMAT_ATTR:
        {
            int tmp;
            buf.Get(tmp);
            mat->SetAttr(trpgMaterial::TR_FID,tmp);
            buf.Get(tmp);
            mat->SetAttr(trpgMaterial::TR_SMC,tmp);
            buf.Get(tmp);
            mat->SetAttr(trpgMaterial::TR_STP,tmp);
            buf.Get(tmp);
            mat->SetAttr(trpgMaterial::TR_SWC,tmp);
        }
        break;
        case TRPGMAT_HANDLE:
        {
            int hdl;
            buf.Get(hdl);
            mat->SetHandle(hdl);
        }
        break;
        default:
            break;
        }
    }
    catch (...) {
        return NULL;
    }

    return mat;
}

bool trpgMaterial::Read(trpgReadBuffer &buf)
{
    trpgr_Parser parse;
    materialCB matCb;

    // Material is just a bunch of unordered tokens.
    // Interface to it with a generic parser
    matCb.mat = this;
    parse.AddCallback(TRPGMAT_BASIC,&matCb,false);
    parse.AddCallback(TRPGMAT_SHADE,&matCb,false);
    parse.AddCallback(TRPGMAT_SIZES,&matCb,false);
    parse.AddCallback(TRPGMAT_CULL,&matCb,false);
    parse.AddCallback(TRPGMAT_ALPHA,&matCb,false);
    parse.AddCallback(TRPGMAT_NORMAL,&matCb,false);
    parse.AddCallback(TRPGMAT_TEXTURE,&matCb,false);
    parse.AddCallback(TRPGMAT_BUMP,&matCb,false);
    parse.AddCallback(TRPGMAT_ATTR,&matCb,false);
    parse.AddCallback(TRPGMAT_HANDLE,&matCb,false);
    parse.Parse(buf);

    return isValid();
}

/* Texture
   Really just a container for a texture name and use count.
   The use count is used for paging.
*/

// Constructor
trpgTexture::trpgTexture()
{
    mode = External;
    type = trpg_Unknown;
    numLayer = -1;
    name = NULL;
    useCount = 0;
    sizeX = sizeY = -1;
    addr.file = 0;
    addr.offset = 0;
    addr.col = -1;
    addr.row = -1;
    isMipmap = false;
    numMipMap = 0;
    writeHandle = false;
    handle = -1;
}

// Copy construction
trpgTexture::trpgTexture(const trpgTexture &in):
    trpgReadWriteable(in)
{
    mode = in.mode;
    type = in.type;
    numLayer = in.numLayer; // RGBX
    name = NULL;
    SetName(in.name);
    useCount = in.useCount;
    sizeX = in.sizeX;  sizeY = in.sizeY;
    addr.file = in.addr.file;
    addr.offset = in.addr.offset;
    addr.row = in.addr.row;
    addr.col = in.addr.col;
    isMipmap = in.isMipmap;
    numMipMap = in.numMipMap;
    // storageSize + levelOffset
    handle = in.handle;
    writeHandle = in.writeHandle;
}

// Destruction
trpgTexture::~trpgTexture()
{
    Reset();
}

// Reset
void trpgTexture::Reset()
{
    mode = External;
    type = trpg_Unknown;
    numLayer = -1;
    if (name)
        delete [] name;
    name = NULL;
    useCount = 0;
    sizeX = sizeY = -1;
    addr.file = 0;
    addr.offset = 0;
    addr.row = -1;
    addr.col = -1;
    isMipmap = false;
    storageSize.clear();
    levelOffset.clear();
    handle = -1;
    writeHandle = false;
}

// Valid if we've got a name
bool trpgTexture::isValid() const
{
    switch (mode) {
    case External:
        return (name != NULL);
    case Local:
        return (type != trpg_Unknown && sizeX != -1 && sizeY != -1);
    case Global:
        return (type != trpg_Unknown);
    case Template:
        return (type != trpg_Unknown && sizeX != -1 && sizeY != -1);
    default:
        return false;
    }
}

// Set Name
void trpgTexture::SetName(const char *inName)
{
    if (name)
        delete [] name;
    name = NULL;

    if (!inName)
        return;

    name = new char[strlen(inName)+1];
    strcpy(name,inName);
}

// Get Name
bool trpgTexture::GetName(char *outName,int outLen) const
{
    if (!isValid())
        return false;

    if (!outName)
        return false;

    if (name)
    {
        int len = strlen(name);
        strncpy(outName,name,MIN(len,outLen)+1);
    }
    else
    {
        outName[0] = '\0';
    }
    return true;
}

void trpgTexture::SetImageMode(ImageMode inMode)
{
    mode = inMode;
}
bool trpgTexture::GetImageMode(ImageMode &outMode) const
{
    outMode = mode;

    return true;
}
void trpgTexture::SetImageType(ImageType inType)
{
    type = inType;
}
bool trpgTexture::GetImageType(ImageType &outType) const
{
    outType = type;

    return true;
}

void trpgTexture::SetImageSize(const trpg2iPoint &inSize)
{
    sizeX = inSize.x;
    sizeY = inSize.y;
}
bool trpgTexture::GetImageSize(trpg2iPoint &outSize) const
{
    if (mode != Local && mode != Template)
        return false;
    outSize.x = sizeX;
    outSize.y = sizeY;

    return true;
}
void trpgTexture::SetIsMipmap(bool val)
{
    isMipmap = val;
}
bool trpgTexture::GetIsMipmap(bool &ret) const
{
    ret = isMipmap;
    return true;
}
bool trpgTexture::GetImageAddr(trpgwAppAddress &outAddr) const
{
    if (mode != Local)
        return false;

    outAddr = addr;

    return true;
}
void trpgTexture::SetImageAddr(const trpgwAppAddress &inAddr)
{
    addr = inAddr;
}
bool trpgTexture::GetImageDepth(int32 &depth) const
{
    switch (type) {
    case trpg_RGB8:
        depth = 3;
        break;
    case trpg_RGBA8:
        depth = 4;
        break;
    case trpg_INT8:
        depth = 1;
        break;
    case trpg_INTA8:
        depth = 2;
        break;
    case trpg_FXT1:
        depth = 3;
        break;
    case trpg_RGBX:
        depth = numLayer;
        break;
    case trpg_DXT1:
        depth = 3;
        break;
    case trpg_DXT3:
        depth = 3;
        break;
    case trpg_DXT5:
        depth = 3;
        break;
    case trpg_MCM5:
        depth = 5;
        break;
    case trpg_MCM6R:
    case trpg_MCM6A:
        depth = 6;
        break;
    case trpg_MCM7RA:
    case trpg_MCM7AR:
        depth = 7;
        break;
    default:
        depth = -1;
        break;
    }

    return true;
}

void trpgTexture::SetNumLayer(int layers)
{
    numLayer = layers;
}

bool trpgTexture::GetNumLayer(int &layers) const
{
    if (!isValid())
        return false;
    GetImageDepth(layers);
    return true;
}

// Use count management
void trpgTexture::SetNumTile(int num)
{
    useCount = num;
}
void trpgTexture::AddTile()
{
    useCount++;
}
bool trpgTexture::GetNumTile(int &num) const
{
    if (!isValid())
        return false;
    num = useCount;
    return true;
}

// Copy operator
trpgTexture &trpgTexture::operator = (const trpgTexture &in)
{
    mode = in.mode;
    type = in.type;

    if (in.name)
        SetName(in.name);

    useCount = in.useCount;

    sizeX = in.sizeX;
    sizeY = in.sizeY;

    // RGBX
    numLayer = in.numLayer;

    isMipmap = in.isMipmap;
    addr = in.addr;

    writeHandle = in.writeHandle;
    handle = in.handle;

    return *this;
}

// Equality operator
int trpgTexture::operator == (const trpgTexture &in) const
{
    if (mode != in.mode)
        return 0;

    switch (mode) {
    case External:
        if (!in.name && !name)
            return 1;
        if (!in.name || !name)
            return 0;
        return (!strcmp(in.name,name));
        break;
    case Local:
        if (type == in.type && sizeX == in.sizeX && sizeY == in.sizeY &&
            isMipmap == in.isMipmap &&
            addr.file == in.addr.file && addr.offset == in.addr.offset &&
            addr.row == in.addr.row && addr.col==in.addr.col )
            return 1;
        break;
    case Global:
    case Template:
        if (type == in.type && sizeX == in.sizeX && sizeY == in.sizeY &&
            isMipmap == in.isMipmap)
            return 1;
        break;
    }

    return 0;
}

// Utility functions

int32 trpgTexture::CalcNumMipmaps() const
{
    // We're going to assume these are powers of two.
    // If not, then the writer's a moron.

    // :))) The comment line above made me really loughing, Steve. - Nick
    int bval = MAX(sizeX,sizeY);

    // Now look for the highest bit
    int p2;
    for (p2=0;p2<32;p2++)
        if ((1<<p2) & bval)
            break;

    return p2+1;
}

// Note: replacing this with explicit sizes
int32 trpgTexture::CalcTotalSize() const
{
    (const_cast<trpgTexture *>(this))->CalcMipLevelSizes();

    int totSize = 0;
    for (unsigned int i=0;i<storageSize.size();i++)
        totSize += storageSize[i];

    return totSize;
}

// Calculate the size of a given mip level
int32 trpgTexture::MipLevelSize(int miplevel)
{

    if ( miplevel >= 0 && miplevel < CalcNumMipmaps() ) {
        if ( !storageSize.size() )
            CalcMipLevelSizes();
        return storageSize[miplevel];
    }

    return 0;
}

int32 trpgTexture::MipLevelOffset(int miplevel)
{
    if ( miplevel > 0 && miplevel < CalcNumMipmaps() ) {
        if ( !levelOffset.size() )
            CalcMipLevelSizes();
        return levelOffset[miplevel];
    }

    return 0;
}

// Write function
bool trpgTexture::Write(trpgWriteBuffer &buf)
{
    if (!isValid()) return false;

    buf.Begin(TRPGTEXTURE);

    buf.Add(name);
    buf.Add(useCount);
    // New in 2.0 from here down
    buf.Add((unsigned char)mode);
    buf.Add((unsigned char)type);
    buf.Add(sizeX);
    buf.Add(sizeY);
    buf.Add(addr.file);
    buf.Add(addr.offset);
    buf.Add((int32)isMipmap);
    if(writeHandle) {
        buf.Add((int32)handle);
    }
    buf.End();

    return true;
}

// Read function
bool trpgTexture::Read(trpgReadBuffer &buf)
{
    char texName[1024];

    try {
        buf.Get(texName,1023);
        SetName(texName);
        buf.Get(useCount);

        mode = External;
        // New in 2.0 from here down
        unsigned char bval;
        buf.Get(bval);  mode = (trpgTexture::ImageMode)bval;
        buf.Get(bval);    type = (trpgTexture::ImageType)bval;
        GetImageDepth(numLayer); // heh
        buf.Get(sizeX);
        buf.Get(sizeY);
        buf.Get(addr.file);
        buf.Get(addr.offset);
        int32 ival;
        buf.Get(ival);
        // Read the handle if we can..

        isMipmap = (ival) ? true : false;
    }
    catch (...) {
        return false;
    }

    if (!isValid()) return false;

    // calculate the mip level sizes
    CalcMipLevelSizes();

    return true;
}

void trpgTexture::CalcMipLevelSizes()
{
    int num_miplevels = (isMipmap ? CalcNumMipmaps() : 1);
    int level_size = 0;
    int level_offset = 0;
    int block_size = 0;
    int pixel_size = 0;
    int pad_size = 0;
    bool isDXT = false;
    bool isFXT = false;

    switch (type) {
    case trpg_DXT1:
        isDXT = true;
        block_size = 8;
        break;
    case trpg_DXT3:
    case trpg_DXT5:
        isDXT = true;
        block_size = 16;
        break;
    case trpg_RGB8:
        pad_size = 4;
        pixel_size = 3;
        break;
    case trpg_RGBA8:
        pad_size = 4;
        pixel_size = 4;
        break;
    case trpg_RGBX:
        pad_size = 4;
        pixel_size = numLayer;
        break;
    case trpg_MCM5:
        pad_size = 4;
        pixel_size = 5;
        break;
    case trpg_MCM6R:
    case trpg_MCM6A:
        pad_size = 4;
        pixel_size = 6;
        break;
    case trpg_MCM7RA:
    case trpg_MCM7AR:
        pad_size = 4;
        pixel_size = 7;
        break;
    case trpg_INT8:
        pad_size = 4;
        pixel_size = 1;
        break;
    case trpg_INTA8:
        pad_size = 4;
        pixel_size = 2;
        break;
    case trpg_FXT1:
        isFXT = true;
        break;
    default:
        break;
    }

    levelOffset.clear();
    storageSize.clear();

    levelOffset.push_back(level_offset);

    if ( isDXT ) { // DXT compressed
        int num_x_blocks = ((sizeX/4)+(sizeX%4?1:0));
        int num_y_blocks = ((sizeY/4)+(sizeY%4?1:0));

        level_size = num_x_blocks * num_y_blocks * block_size;
        storageSize.push_back(level_size);

        for ( int i = 1; i < num_miplevels; i++ ) {
            level_offset += level_size;
            levelOffset.push_back(level_offset);

            num_x_blocks /= 2;
            num_y_blocks /= 2;
            num_x_blocks = MAX(1,num_x_blocks);
            num_y_blocks = MAX(1,num_y_blocks);

            level_size = num_x_blocks * num_y_blocks * block_size;
            storageSize.push_back(level_size);
        }

        return;
    }
    if ( isFXT) {
        // bits per pixel and size
        int bpp = 4;
        int x = sizeX;
        int y = sizeY;

        int nummiplevels = (isMipmap ? CalcNumMipmaps() : 1);
        for (int i = 0; i < nummiplevels; i++) {
            if (i > 0)
            levelOffset.push_back(level_offset);

            x = ( x + 0x7 ) & ~0x7;
            y = ( y + 0x3 ) & ~0x3;

            // Number of bytes
            level_size = ( x * y * bpp ) >> 3;
            storageSize.push_back(level_size);
            level_offset += level_size;

            if (x > 1)  x /= 2;
            if (y > 1)  y /= 2;
        }

        return;
    }

    {
        int x_size = sizeX;
        int y_size = sizeY;

        // Pad to a given size, if necessary
        int row_size = x_size * pixel_size;
        if (pad_size > 0) {
            int left = row_size%pad_size;
            if (left)
                row_size += pad_size - left;
        }

        level_size = row_size * y_size;
        storageSize.push_back(level_size);
        for ( int i = 1; i < num_miplevels; i++ ) {
            level_offset += level_size;
            levelOffset.push_back(level_offset);

            x_size /= 2;
            y_size /= 2;
            x_size = MAX(1,x_size);
            y_size = MAX(1,y_size);

            row_size = x_size * pixel_size;
            if (pad_size > 0) {
                int left = row_size%pad_size;
                if (left)
                    row_size += pad_size - left;
            }
            level_size = row_size * y_size;
            storageSize.push_back(level_size);
        }
    }
}

/* Texture Table
   Just a list of texture names so we can index.
*/

// Constructor
trpgTexTable::trpgTexTable()
{
    currentRow = -1;
    currentCol = -1;
}
trpgTexTable::trpgTexTable(const trpgTexTable &in):
    trpgReadWriteable(in)
{
    *this = in;
}

// Reset function
void trpgTexTable::Reset()
{
    errMess[0] = '\0';
    textureMap.clear();
    currentRow = -1;
    currentCol = -1;
}

// Destructor
trpgTexTable::~trpgTexTable()
{
    Reset();
}

// Validity check
bool trpgTexTable::isValid() const
{
    if (!textureMap.size())
    {
        errMess.assign("Texture table list is empty");
        return false;
    }

    TextureMapType::const_iterator itr = textureMap.begin();
    for (  ; itr != textureMap.end( ); itr++) {
        if(!itr->second.isValid()) {
            errMess.assign("A texture in the texture table is invalid");
            return false;
        }
    }
    return true;
}

// Set functions
void trpgTexTable::SetNumTextures(int /*no*/)
{
    // obsolete. This method doesn't need to do anything since we're using a map instead of a vector.
}
int trpgTexTable::AddTexture(const trpgTexture &inTex)
{

    TeAttrHdl hdl = inTex.GetHandle();
    if(hdl==-1) {
        // if no handle is specified, we will use an index as the handle (just like before 2.3)
        hdl = textureMap.size();
    }
    TextureMapType::iterator itr = textureMap.find(hdl);
    // Don't overwrite the texture if it was already there
    if(itr==textureMap.end())
        textureMap[hdl] = inTex;
    return hdl;
}
int trpgTexTable::FindAddTexture(const trpgTexture &inTex)
{
    TextureMapType::iterator itr = textureMap.begin();
    for (  ; itr != textureMap.end( ); itr++) {
        trpgTexture tx = itr->second;
        if(tx == inTex) {
            return itr->first;
        }
    }
    return AddTexture(inTex);
}
void trpgTexTable::SetTexture(int id,const trpgTexture &inTex)
{
    if (id < 0)
        return;
    textureMap[id] = inTex;
}

// Copy operator
trpgTexTable &trpgTexTable::operator = (const trpgTexTable &in)
{
    Reset();
    TextureMapType::const_iterator itr = in.textureMap.begin();
    for (  ; itr != in.textureMap.end( ); itr++) {
        trpgTexture tex = itr->second;
        in.GetTexture(itr->first,tex);
        AddTexture(tex);
    }

    return *this;
}

// Write Texture table
bool trpgTexTable::Write(trpgWriteBuffer &buf)
{
    int32 numTex;

    if (!isValid())
        return false;

    buf.Begin(TRPGTEXTABLE2);
    numTex = textureMap.size();
    buf.Add(numTex);
    TextureMapType::iterator itr = textureMap.begin();
    for (  ; itr != textureMap.end( ); itr++) {
        itr->second.Write(buf);
    }
    buf.End();

    return true;
}

/* ***********
   Read Texture Table
   ***********
   */
// Get functions
bool trpgTexTable::GetNumTextures(int &no) const
{
    no = textureMap.size();
    if (!isValid()) return false;
    return true;
}
bool trpgTexTable::GetTexture(int id,trpgTexture &ret) const
{
    if (!isValid())
        return false;
    if (id < 0)
        return false;
    TextureMapType::const_iterator itr = textureMap.find(id);
    if(itr == textureMap.end()) {
        return false;
    }
    ret = itr->second;

    return true;
}
const trpgTexture *trpgTexTable::GetTextureRef(int id) const
{
    if (id < 0)
        return 0;
    TextureMapType::const_iterator itr = textureMap.find(id);
    if(itr == textureMap.end()) {
        return 0;
    }
    const trpgTexture *ret = &(itr->second);
    return ret;
}

const trpgTexture *trpgTexTable::FindByName(const char *name, int &texid) const
{
    TextureMapType::const_iterator itr = textureMap.begin();
    for (  ; itr != textureMap.end( ); itr++) {
        char thisName[1024];
        thisName[0] = '\0';
        itr->second.GetName(thisName,1023);
        if(strcasecmp(thisName,name)==0)
        {
            texid = itr->first;
            return &(itr->second);
        }
    }
    return 0;

}

bool trpgTexTable::Read(trpgReadBuffer &buf)
{
    int32 numTex;
    trpgToken texTok;
    int32 len;

    try {
        buf.Get(numTex);

        for (int i=0;i<numTex;i++) {
            buf.GetToken(texTok,len);
            if (texTok != TRPGTEXTURE) throw 1;
            buf.PushLimit(len);
            trpgTexture tex;
            bool status = tex.Read(buf);
            //set the block for multi-archive archives (version>=2.3)
            if((currentRow!=-1)&&(currentCol!=-1)) {
                trpgwAppAddress taddr;
                tex.GetImageAddr(taddr);
                taddr.col = currentCol;
                taddr.row = currentRow;
                tex.SetImageAddr(taddr);
            }
            AddTexture(tex);
            buf.PopLimit();
            if (!status) throw 1;
        }
    }
    catch (...) {
        return false;
    }

    return true;
}

/* **************
   Local Material
   **************
   */

trpgLocalMaterial::trpgLocalMaterial()
{
    baseMatTable = -1;
    baseMat = -1;
    sx = sy = ex = ey = destWidth = destHeight = 0;
    addr.resize(1);
    addr[0].file = 0;
    addr[0].offset = 0;
    addr[0].col = -1;
    addr[0].row = -1;
}

trpgLocalMaterial::~trpgLocalMaterial()
{
}

void trpgLocalMaterial::SetBaseMaterial(int32 subTable,int32 inMatID)
{
    baseMatTable = subTable;
    baseMat = inMatID;
}

void trpgLocalMaterial::Reset()
{
    baseMat = -1;
    sx = sy = ex = ey = destWidth = destHeight = 0;
    addr.resize(1);
    addr[0].file = 0;
    addr[0].offset = 0;
    addr[0].col = -1;
    addr[0].row = -1;
}

bool trpgLocalMaterial::GetBaseMaterial(int32 &subTable,int32 &matID) const
{
    if (!isValid()) return false;

    subTable = baseMatTable;
    matID = baseMat;
    return true;
}

void trpgLocalMaterial::SetSubImageInfo(const SubImageInfo &info)
{
    sx = info.sx;
    sy = info.sy;
    ex = info.ex;
    ey = info.ey;
    destWidth = info.destWidth;
    destHeight = info.destHeight;
}

bool trpgLocalMaterial::GetSubImageInfo(SubImageInfo &info) const
{
    if (!isValid()) return false;

    info.sx = sx;
    info.sy = sy;
    info.ex = ex;
    info.ey = ey;
    info.destWidth = destWidth;
    info.destHeight = destHeight;

    return true;
}

void trpgLocalMaterial::SetAddr(const trpgwAppAddress &inAddr)
{
    addr[0] = inAddr;
}

void trpgLocalMaterial::SetNthAddr(unsigned int subtable, const trpgwAppAddress &inAddr)
{
    if (addr.size()<=subtable) addr.resize(subtable+1);
    addr[subtable] = inAddr;
}

bool trpgLocalMaterial::GetAddr(trpgwAppAddress &inAddr) const
{
    if (!isValid()) return false;

    inAddr = addr[0];
    return true;
}

bool trpgLocalMaterial::GetNthAddr(unsigned int subtable, trpgwAppAddress &inAddr) const
{
    if (!isValid()) return false;
    if (addr.size()<=subtable) return false;
    inAddr = addr[subtable];
    return true;
}

bool trpgLocalMaterial::GetNumLocals(int &numLocals) const
{
    if (!isValid()) return false;
    // not checking for tile local, that's up to you.
    numLocals=addr.size();
    return true;
}

/*bool trpgLocalMaterial::SetStorageSizes(int level,vector<int> *inSizes)
  {
  storageSize.resize(inSizes->size());
  for (int i=0;i<inSizes->size();i++)
  storageSize[i] = (*inSizes)[i];

  return true;
  }*/

/*bool trpgLocalMaterial::GetStorageSizes(const vector<int> *retSize)
  {
  if (!isValid()) return false;

  retSize = storageSize;
  return true;
  }*/

bool trpgLocalMaterial::isValid() const
{
    if (baseMat < 0) return false;

    return true;
}

// Write method

bool trpgLocalMaterial::Write(trpgWriteBuffer &buf)
{
    if (!isValid())
        return false;

    buf.Begin(TRPGLOCALMATERIAL);

    // Write the data
    buf.Add(baseMatTable);
    buf.Add(baseMat);
    buf.Add(sx);
    buf.Add(sy);
    buf.Add(ex);
    buf.Add(ey);
    buf.Add(destWidth);
    buf.Add(destHeight);
    buf.Add(addr[0].file);
    buf.Add(addr[0].offset);
    int numAddrs=(int)(addr.size());
    if (numAddrs>1) {
        buf.Add(numAddrs-1); // suppressed due to breaking old readers.
        for (int i=1;i<numAddrs;i++) {
            buf.Add(addr[i].file);
            buf.Add(addr[i].offset);
        }
    }
    buf.End();

    return true;
}

// Read method

bool trpgLocalMaterial::Read(trpgReadBuffer &buf)
{
    try {
        buf.Get(baseMatTable);
        buf.Get(baseMat);
        buf.Get(sx);
        buf.Get(sy);
        buf.Get(ex);
        buf.Get(ey);
        buf.Get(destWidth);
        buf.Get(destHeight);
        buf.Get(addr[0].file);
        buf.Get(addr[0].offset);
        if ( !buf.isEmpty() ) {
            // there might be more
            int extraAddrs;
            buf.Get(extraAddrs);
            if (extraAddrs!=0) {
                addr.resize(extraAddrs+1);
                for (int i=1;i<=extraAddrs;i++) {
                    buf.Get(addr[i].file);
                    buf.Get(addr[i].offset);
                    addr[i].col = -1;
                    addr[i].row = -1;
                }
            }
        }
    }
    catch (...) {
        return false;
    }

    return isValid();
}


try
		{
			Log(LOG_ADMIN, source, this);

			Configuration::Conf *new_config = new Configuration::Conf();
			Configuration::Conf *old = Config;
			Config = new_config;
			Config->Post(old);
			delete old;

			source.Reply(_("Services' configuration has been reloaded."));
		}
		catch (const ConfigException &ex)
		{
			Log(this->owner) << "Error reloading configuration file: " << ex.GetReason();
			source.Reply(_("Error reloading configuration file: %s"), ex.GetReason().c_str());
		}

/* OperServ core functions
 *
 * Contact us at example@nnthink.com
 *
 * Please read COPYING and README for further details.
 *
 * Based on the original code of Epona by Lara.
 * Based on the original code of Services by Andy Church.
 */

#include "module.h"

class CommandOSReload : public Command
{
 public:
	CommandOSReload(Module *creator) : Command(creator, "operserv/reload", 0, 0)
	{
		this->SetDesc(_("Reload services' configuration file"));
	}

	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
	{

	}

	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
	{
		this->SendSyntax(source);
		source.Reply(" ");
		source.Reply(_("Causes Services to reload the configuration file. Note that\n"
				"some directives still need the restart of the Services to\n"
				"take effect (such as Services' nicknames, activation of the\n"
				"session limitation, etc.)."));
		return true;
	}
};

class OSReload : public Module
{
	CommandOSReload commandosreload;

 public:
	OSReload(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
		commandosreload(this)
	{

	}
};

MODULE_INIT(OSReload)


try
      {
         TropModel::setWeather(wx);
         validWeather = true;
         valid = validWeather && validHeights && validRxHeight;
      }
      catch(InvalidParameter& e)
      {
         valid = validWeather = false;
         GPSTK_RETHROW(e);
      }

//  This file is part of GPSTk, the GPS Toolkit.
//  This software was developed by Applied Research Laboratories at the
//  University of Texas at Austin.

//  This software was developed by Applied Research Laboratories at the
//  University of Texas at Austin, under contract to an agency or agencies
//  within the U.S. Department of Defense. The U.S. Government retains all
//  rights to use, duplicate, distribute, disclose, or release this software.
//  Pursuant to DoD Directive 523024
//  DISTRIBUTION STATEMENT A: This software has been approved for public
//                            release, distribution is unlimited.

#include "GPSEllipsoid.hpp"
#include "GGHeightTropModel.hpp"

#define THROW_IF_INVALID_DETAILED() {if (!valid) {                      \
         InvalidTropModel e;                                            \
         if(!validWeather) e.addText("Invalid trop model: weather");    \
         if(!validHeights)  e.addText("Invalid trop model: validHeights"); \
         if(!validRxHeight)   e.addText("Invalid trop model: validRxHeight"); \
         GPSTK_THROW(e);}}

namespace gpstk
{
   // Tropospheric model with heights based on Goad and Goodman(1974),
   // "A Modified Hopfield Tropospheric Refraction Correction Model," Paper
   // presented at the Fall Annual Meeting of the American Geophysical Union,
   // (Not the same as GGTropModel because this has height dependence,
   // and the computation of this model does not break cleanly into
   // wet and dry components.)

   GGHeightTropModel::GGHeightTropModel(void)
   {
      validWeather = false; //setWeather(20.0,980.0,50.0);
      validHeights = false; //setHeights(0.0,0.0,0.0);
      validRxHeight = false;
   }

      // Creates a trop model from a weather observation
      // @param wx the weather to use for this correction.
   GGHeightTropModel::GGHeightTropModel(const WxObservation& wx)
   {
      valid = validRxHeight = validHeights = false;
      setWeather(wx);
   }

      // Create a tropospheric model from explicit weather data
      // @param T temperature in degrees Celsius
      // @param P atmospheric pressure in millibars
      // @param H relative humidity in percent
   GGHeightTropModel::GGHeightTropModel(const double& T,
                                        const double& P,
                                        const double& H)
   {
      validRxHeight = validHeights = false;
      setWeather(T,P,H);
   }

      // Create a valid model from explicit input.
      // @param T temperature in degrees Celsius
      // @param P atmospheric pressure in millibars
      // @param H relative humidity in percent
      // @param hT height at which temperature applies in meters.
      // @param hP height at which atmospheric pressure applies in meters.
      // @param hH height at which relative humidity applies in meters.
   GGHeightTropModel::GGHeightTropModel(const double& T,
                                        const double& P,
                                        const double& H,
                                        const double hT,
                                        const double hP,
                                        const double hH)
   {
      validRxHeight = false;
      setWeather(T,P,H);
      setHeights(hT,hP,hH);
   }

      // re-define this to get the throws
   double GGHeightTropModel::correction(double elevation) const
   {
      THROW_IF_INVALID_DETAILED();

      if(elevation < 0.0) return 0.0;

      return (dry_zenith_delay() * dry_mapping_function(elevation)
            + wet_zenith_delay() * wet_mapping_function(elevation));

   }

      // Compute and return the full tropospheric delay, given the positions of
      // receiver and satellite and the time tag. This version is most useful
      // within positioning algorithms, where the receiver position and timetag
      // may vary; it computes the elevation (and other receiver location
      // information) and passes them to appropriate set...() routines and
      // the correction(elevation) routine.
      // @param RX  Receiver position
      // @param SV  Satellite position
      // @param tt  Time tag of the signal
   double GGHeightTropModel::correction(const Position& RX,
                                        const Position& SV,
                                        const CommonTime& tt)
   {
      THROW_IF_INVALID_DETAILED();

      // compute height from RX
      setReceiverHeight(RX.getHeight());

      return TropModel::correction(RX.elevation(SV));

   }  // end GGHeightTropModel::correction(RX,SV,TT)

      // Compute and return the full tropospheric delay, given the positions of
      // receiver and satellite and the time tag. This version is most useful
      // within positioning algorithms, where the receiver position and timetag
      // may vary; it computes the elevation (and other receiver location
      // information) and passes them to appropriate set...() routines and
      // the correction(elevation) routine.
      // @param RX  Receiver position in ECEF cartesian coordinates (meters)
      // @param SV  Satellite position in ECEF cartesian coordinates (meters)
      // @param tt  Time tag of the signal
      // This function is deprecated; use the Position version
   double GGHeightTropModel::correction(const Xvt& RX,
                                        const Xvt& SV,
                                        const CommonTime& tt)
   {
      Position R(RX),S(SV);
      return GGHeightTropModel::correction(R,S,tt);
   }

      // Compute and return the zenith delay for dry component of the troposphere
   double GGHeightTropModel::dry_zenith_delay(void) const
   {
      THROW_IF_INVALID_DETAILED();
      double hrate=6.5e-3;
      double Ts=temp+hrate*height;
      double em=978.77/(2.8704e4*hrate);
      double Tp=Ts-hrate*hpress;
      double ps=press*std::pow(Ts/Tp,em)/1000.0;
      double rs=77.624e-3/Ts;
      double ho=11.385/rs;
      rs *= ps;
      double zen=(ho-height)/ho;
      zen = rs*zen*zen*zen*zen;
         // normalize
      zen *= (ho-height)/5;
      return zen;

   }

      // Compute and return the zenith delay for wet component of the troposphere
   double GGHeightTropModel::wet_zenith_delay(void) const
   {
      THROW_IF_INVALID_DETAILED();

      double hrate=6.5e-3; //   deg K / m
      double Th=temp-273.15-hrate*(hhumid-htemp);
      double Ta=7.5*Th/(237.3+Th);
         // water vapor partial pressure
      double e0=6.11e-5*humid*std::pow(10.0,Ta);
      double Ts=temp+hrate*htemp;
      double em=978.77/(2.8704e4*hrate);
      double Tk=Ts-hrate*hhumid;
      double es=e0*std::pow(Ts/Tk,4.0*em);
      double rs=(371900.0e-3/Ts-12.92e-3)/Ts;
      double ho=11.385*(1255/Ts+0.05)/rs;
      double zen=(ho-height)/ho;
      zen = rs*es*zen*zen*zen*zen;
         //normalize
      zen *= (ho-height)/5;
      return zen;

   }

      // Compute and return the mapping function for dry component
      // of the troposphere
      // @param elevation Elevation of satellite as seen at receiver,
      //                  in degrees
   double GGHeightTropModel::dry_mapping_function(double elevation) const
   {
      THROW_IF_INVALID_DETAILED();

      if(elevation < 0.0) return 0.0;

      double hrate=6.5e-3;
      double Ts=temp+hrate*htemp;
      double ho=(11.385/77.624e-3)*Ts;
      double se=std::sin(elevation*DEG_TO_RAD);
      if(se < 0.0) se=0.0;

      GPSEllipsoid ell;
      double rt,a,b,rn[8],al[8],er=ell.a();
      rt = (er+ho)/(er+height);
      rt = rt*rt - (1.0-se*se);
      if(rt < 0) rt=0.0;
      rt = (er+height)*(SQRT(rt)-se);
      a = -se/(ho-height);
      b = -(1.0-se*se)/(2.0*er*(ho-height));
      rn[0] = rt*rt;
      for(int j=1; j<8; j++) rn[j]=rn[j-1]*rt;
      al[0] = 2*a;
      al[1] = 2*a*a+4*b/3;
      al[2] = a*(a*a+3*b);
      al[3] = a*a*a*a/5+2.4*a*a*b+1.2*b*b;
      al[4] = 2*a*b*(a*a+3*b)/3;
      al[5] = b*b*(6*a*a+4*b)*0.1428571;
      if(b*b > 1.0e-35) {
         al[6] = a*b*b*b/2;
         al[7] = b*b*b*b/9;
      } else {
         al[6] = 0.0;
         al[7] = 0.0;
      }
      double map=rt;
      for(int k=0; k<8; k++) map += al[k]*rn[k];
         // normalize
      double norm=(ho-height)/5;
      return map/norm;
   }

      // Compute and return the mapping function for wet component
      // of the troposphere
      // @param elevation Elevation of satellite as seen at receiver,
      //                  in degrees
   double GGHeightTropModel::wet_mapping_function(double elevation) const
   {
      THROW_IF_INVALID_DETAILED();
      if(elevation < 0.0) return 0.0;

      double hrate=6.5e-3;
      double Ts=temp+hrate*htemp;
      double rs=(371900.0e-3/Ts-12.92e-3)/Ts;
      double ho=11.385*(1255/Ts+0.05)/rs;
      double se=std::sin(elevation*DEG_TO_RAD);
      if(se < 0.0) se=0.0;

      GPSEllipsoid ell;
      double rt,a,b,rn[8],al[8],er=ell.a();
      rt = (er+ho)/(er+height);
      rt = rt*rt - (1.0-se*se);
      if(rt < 0) rt=0.0;
      rt = (er+height)*(SQRT(rt)-se);
      a = -se/(ho-height);
      b = -(1.0-se*se)/(2.0*er*(ho-height));
      rn[0] = rt*rt;
      for(int i=1; i<8; i++) rn[i]=rn[i-1]*rt;
      al[0] = 2*a;
      al[1] = 2*a*a+4*b/3;
      al[2] = a*(a*a+3*b);
      al[3] = a*a*a*a/5+2.4*a*a*b+1.2*b*b;
      al[4] = 2*a*b*(a*a+3*b)/3;
      al[5] = b*b*(6*a*a+4*b)*0.1428571;
      if(b*b > 1.0e-35) {
         al[6] = a*b*b*b/2;
         al[7] = b*b*b*b/9;
      } else {
         al[6] = 0.0;
         al[7] = 0.0;
      }
      double map=rt;
      for(int j=0; j<8; j++) map += al[j]*rn[j];
         // normalize map function
      double norm=(ho-height)/5;
      return map/norm;

   }

      // Re-define the weather data.
      // Typically called just before correction().
      // @param T temperature in degrees Celsius
      // @param P atmospheric pressure in millibars
      // @param H relative humidity in percent
   void GGHeightTropModel::setWeather(const double& T,
                                      const double& P,
                                      const double& H)
   {
      try
      {
         TropModel::setWeather(T,P,H);
         validWeather = true;
         valid = validWeather && validHeights && validRxHeight;
      }
      catch(InvalidParameter& e)
      {
         valid = validWeather = false;
         GPSTK_RETHROW(e);
      }
   }

      // Re-define the tropospheric model with explicit weather data.
      // Typically called just before correction().
      // @param wx the weather to use for this correction
   void GGHeightTropModel::setWeather(const WxObservation& wx)
   {

   }

      // Re-define the heights at which the weather parameters apply.
      // Typically called just before correction().
      // @param hT height (m) at which temperature applies
      // @param hP height (m) at which atmospheric pressure applies
      // @param hH height (m) at which relative humidity applies
   void GGHeightTropModel::setHeights(const double& hT,
                                      const double& hP,
                                      const double& hH)
   {
      htemp = hT;                 // height (m) at which temp applies
      hpress = hP;                // height (m) at which press applies
      hhumid = hH;                // height (m) at which humid applies
      validHeights = true;
      valid = validWeather && validHeights && validRxHeight;
   }

      // Define the receiver height; this required before calling
      // correction() or any of the zenith_delay or mapping_function routines.
   void GGHeightTropModel::setReceiverHeight(const double& ht)
   {
      height = ht;
      validRxHeight = true;
      if(!validHeights) {
         htemp = hpress = hhumid = ht;
         validHeights = true;
      }
      valid = validWeather && validHeights && validRxHeight;
   }

}


/*
   All rights reserved.

   which can be found in the LICENSE file in the root directory, or at
*/
#include <El.hpp>

namespace El {

template<typename Field>
Base<Field> InfinityCondition( const Matrix<Field>& A )
{
    EL_DEBUG_CSE
    typedef Base<Field> Real;
    Matrix<Field> B( A );
    const Real infNorm = InfinityNorm( B );

    const Real infNormInv = InfinityNorm( B );
    return infNorm*infNormInv;
}

template<typename Field>
Base<Field> InfinityCondition( const AbstractDistMatrix<Field>& A )
{
    EL_DEBUG_CSE
    typedef Base<Field> Real;
    DistMatrix<Field> B( A );
    const Real infNorm = InfinityNorm( B );
    try { Inverse( B ); }
    catch( SingularMatrixException& e )
    { return limits::Infinity<Real>(); }
    const Real infNormInv = InfinityNorm( B );
    return infNorm*infNormInv;
}

#define PROTO(Field) \
  template Base<Field> InfinityCondition( const Matrix<Field>& A ); \
  template Base<Field> InfinityCondition( const AbstractDistMatrix<Field>& A );

#define EL_NO_INT_PROTO
#define EL_ENABLE_DOUBLEDOUBLE
#define EL_ENABLE_QUADDOUBLE
#define EL_ENABLE_QUAD
#define EL_ENABLE_BIGFLOAT
#include <El/macros/Instantiate.h>

} // namespace El