<commit_before>#include <easylogging.h>

#include <glm/gtc/type_ptr.hpp>

#include "core/model.h"

#include <stdexcept>

std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) {
        elems.push_back(item);
    }
    return elems;
}

std::vector<std::string> split(const std::string &s, char delim) {
    std::vector<std::string> elems;
    split(s, delim, elems);
    return elems;
}

void Model::addGroup(std::string g) {
	groups[g];
}

Model::Model(std::string filename)
    : m_shader("model.vert", "model.frag") {
	std::string line;
	std::ifstream myfile(filename);
	int v1, v2, v3, n1, n2, n3, t1, t2, t3;
	std::string g;
	if (myfile.is_open())
	{
		while (getline(myfile, line))
		{
			//LOG(INFO) << line;
			if (line.size()<=1 || line[0] == '#') continue;
			std::vector<std::string> t = split(line, ' ');
			if (t[0] == "v" && t.size() == 4) {
				vertices.push_back(glm::vec3(std::stof(t[1]),
                                             std::stof(t[2]),
                                             std::stof(t[3])));
			}
			else if (t[0] == "vt" && t.size() == 4) {
				uv.push_back(glm::vec3(std::stof(t[1]),
                                       std::stof(t[2]),
                                       std::stof(t[3])));
			}
			else if (t[0] == "vt" && t.size() == 3) {
				uv.push_back(glm::vec3(std::stof(t[1]),
                                       std::stof(t[2]),
                                       0));
			}
			else if (t[0] == "vn" && t.size() == 4) {
				normals.push_back(glm::vec3(std::stof(t[1]),
                                            std::stof(t[2]),
                                            std::stof(t[3])));
			}
			else if (t[0] == "f") {
				if (t.size() == 5) {
					int v[4];
					int n[4];
					int u[4];
					for (size_t i = 1; i < t.size(); i++) {
						//LOG(INFO) << t[i];
						v[i-1] = std::stoi(t[i].substr(0, t[i].find_first_of('/')),nullptr);
						u[i - 1] = std::stoi(t[i].substr(t[i].find_first_of('/') + 1, t[i].find_last_of('/') - t[i].find_first_of('/') - 1));
						n[i - 1] = std::stoi(t[i].substr(t[i].find_last_of('/') + 1, t[i].size() - t[i].find_last_of('/') - 1));
					}
					if (g.size() == 0) {
						g = "default";
						addGroup(g);
					}
					groups[g].triangles.push_back(Triangle(glm::ivec3(v[0] - 1, v[1] - 1, v[3] - 1),
                                                           glm::ivec3(n[0] - 1, n[1] - 1, n[3] - 1),
                                                           glm::ivec3(u[0] - 1, u[1] - 1, u[3] - 1)));
					groups[g].triangles.push_back(Triangle(glm::ivec3(v[3] - 1, v[1] - 1, v[2] - 1),
                                                           glm::ivec3(n[3] - 1, n[1] - 1, n[2] - 1),
                                                           glm::ivec3(u[3] - 1, u[1] - 1, u[2] - 1)));
				}
				else if (t[0].size()) {
					int scan = sscanf(line.c_str(), "f %d/%d/%d %d/%d/%d %d/%d/%d", &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3);
					if (scan < 9) {
						scan = sscanf(line.c_str(), "f %d//%d %d//%d %d//%d", &v1, &n1, &v2, &n2, &v3, &n3);
						if (scan < 6) {
							scan = sscanf(line.c_str(), "f %d/%d %d/%d %d/%d", &v1, &t1, &v2, &t2, &v3, &t3);
							if (scan < 6) {
								LOG(ERROR) << "Failed to parse '" << line << "'.";
								throw std::runtime_error("");
							}
						}
					}
					if (g.size() == 0) {
						g = "default";
						addGroup(g);
					}
					groups[g].triangles.push_back(Triangle(glm::ivec3(v1 - 1, v2 - 1, v3 - 1),
                                                           glm::ivec3(n1 - 1, n2 - 1, n3 - 1),
                                                           glm::ivec3(t1 - 1, t2 - 1, t3 - 1)));
				}
			}
			else if (t[0] == "mtllib") {
				size_t found = filename.find_last_of("/");
				if (found == std::string::npos) readMTL(t[1]);
				else {
					readMTL(filename.substr(0, found + 1) + t[1]);
				}
			}
			else if (t[0] == "usemtl") {
				if (g.size() == 0) {
					g = "default";
					addGroup(g);
				}
				groups[g].materialIdx = t[1];
				if (materials.find(t[1]) == materials.end()) {
					LOG(ERROR) << "missing material " << t[1];
					throw std::runtime_error("");
				}
			}
			else if (t[0] == "g") {
				g = t[1];
				addGroup(g);
			}
			else if (t[0] == "s") {
				groups[g].s = t[1];
			}
			else {
				LOG(ERROR) << "Failed to parse '" << line << "'.";
				throw std::runtime_error("");
			}
		}
		myfile.close();
	}
	LOG(INFO) << "Finished loading '" << filename << "'.";
	LOG(TRACE) << vertices.size() << " vertices, "
	           << uv.size() << " texcoords, "
	           << normals.size() << " normals.";
}

void Model::readMTL(std::string filename) {
	std::string line;
	std::ifstream myfile(filename);
	int prev = materials.size();
	LOG(INFO) << "reading mtl file" << filename;
	if (myfile.is_open())
	{
		std::string mtl;
		while (getline(myfile, line)) {
			std::vector<std::string> t = split(line, ' ');
			if (t.size() == 0 || t[0] == "#" || t[0] == "\n");
			else if (t[0] == "newmtl") {
				mtl = t[1];
				materials[mtl];
			}
			else if (t[0] == "illum") materials[mtl].illum = std::stoi(t[1]);
			else if (t[0] == "Ka") materials[mtl].Ka = glm::vec3(std::stof(t[1]), std::stof(t[2]), std::stof(t[3]));
			else if (t[0] == "Kd") materials[mtl].Kd = glm::vec3(std::stof(t[1]), std::stof(t[2]), std::stof(t[3]));
			else if (t[0] == "Ks") materials[mtl].Ks = glm::vec3(std::stof(t[1]), std::stof(t[2]), std::stof(t[3]));
			else if (t[0] == "Ke") materials[mtl].Ke = glm::vec3(std::stof(t[1]), std::stof(t[2]), std::stof(t[3]));
			else if (t[0] == "Tf") materials[mtl].Tf = glm::vec3(std::stof(t[1]), std::stof(t[2]), std::stof(t[3]));
			else if (t[0] == "Ns") materials[mtl].Ns = std::stof(t[1]);
			else if (t[0] == "Ni") materials[mtl].Ni = std::stof(t[1]);
			else if (t[0] == "d") materials[mtl].d = std::stof(t[1]);
			else if (t[0] == "Tr") materials[mtl].Tr = std::stof(t[1]);
			/*else if (t2[0] == "map_Kd") {
				m.map_Kd = new gl::texture2D(t2[1], GL_UNSIGNED_BYTE);
			}*/
			else {
				LOG(ERROR) << t[0];
			}
		}
	}
	LOG(INFO) << "finished reading mtl file " << std::to_string(materials.size() - prev);
}

void Model::display(const camera& camera, const std::vector<light>& lights) {
	if (drawLists.empty()) {
		CreateDrawingLists();
	}

	for (auto var : drawLists)
	{
		m_shader.bind();
		glCallList(var.second);
		m_shader.unbind();
	}
	/*m_shader.bind();
    m_shader.set("view", camera.view());
    m_shader.set("proj", camera.proj());

    m_shader.set("camera_pos", camera.pos());
    m_shader.set("light_count", (int)lights.size());

    for (size_t t = 0; t < lights.size(); ++t) {
        m_shader.set("lights[" + std::to_string(t) + "].pos", lights[t].pos);
        // m_shader.set("lights[" + std::to_string(t) + "].falloff", lights[t].falloff);
        // m_shader.set("lights[" + std::to_string(t) + "].intensity", lights[t].intensity);
    }

    m_shader.set("ks", glm::vec3(0.9, 0.9, 0.9));
    m_shader.set("kd", glm::vec3(0.4, 0.3, 0.2));
    m_shader.set("ka", glm::vec3(0.1, 0.1, 0.1));
    m_shader.set("shininess", 32.0f);

	for (auto var : drawLists)
	{
        // set up material

		glCallList(var.second);
	}

	m_shader.unbind(); */
}

void Model::useMTL(std::string mtl) {
	glMaterialfv(GL_FRONT, GL_AMBIENT, glm::value_ptr(materials[mtl].Ka));
	glMaterialfv(GL_FRONT, GL_DIFFUSE, glm::value_ptr(materials[mtl].Kd));
	glMaterialfv(GL_FRONT, GL_SPECULAR, glm::value_ptr(glm::vec4(materials[mtl].Ks, materials[mtl].Ns)));
	//glEnable(GL_TEXTURE_2D);
	//materials[mtl].map_Kd->bind(0);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
void Model::addToList(int v, int n, int u) {
	if (uv.size()>0) glTexCoord2f(uv[u].x, uv[u].y);
	glNormal3f(normals[n].x, normals[n].y, normals[n].z); //Add the normal
	glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z); //Add the vertex
}
void Model::CreateDrawingLists() {
	if (!drawLists.empty()) {
		for (std::pair<std::string,int> var : drawLists)
		{
			glDeleteLists(var.second, 1);
		}
		drawLists.clear();
	}
	for (auto& g : groups) {
		int l = glGenLists(1);
		glBegin(GL_TRIANGLES);
		for (Triangle t : g.second.triangles) {
			addToList(t.v[0], t.n[0], t.t[0]);
			addToList(t.v[1], t.n[1], t.t[1]);
			addToList(t.v[2], t.n[2], t.t[2]);
		}
		glEnd();
		drawLists.push_back(std::pair<std::string, int>(g.second.materialIdx, l));
	}
}
<commit_msg>view/proj passing<commit_after>#include <easylogging.h>

#include <glm/gtc/type_ptr.hpp>

#include "core/model.h"

#include <stdexcept>

std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) {
        elems.push_back(item);
    }
    return elems;
}

std::vector<std::string> split(const std::string &s, char delim) {
    std::vector<std::string> elems;
    split(s, delim, elems);
    return elems;
}

void Model::addGroup(std::string g) {
	groups[g];
}

Model::Model(std::string filename)
    : m_shader("model.vert", "model.frag") {
	std::string line;
	std::ifstream myfile(filename);
	int v1, v2, v3, n1, n2, n3, t1, t2, t3;
	std::string g;
	if (myfile.is_open())
	{
		while (getline(myfile, line))
		{
			//LOG(INFO) << line;
			if (line.size()<=1 || line[0] == '#') continue;
			std::vector<std::string> t = split(line, ' ');
			if (t[0] == "v" && t.size() == 4) {
				vertices.push_back(glm::vec3(std::stof(t[1]),
                                             std::stof(t[2]),
                                             std::stof(t[3])));
			}
			else if (t[0] == "vt" && t.size() == 4) {
				uv.push_back(glm::vec3(std::stof(t[1]),
                                       std::stof(t[2]),
                                       std::stof(t[3])));
			}
			else if (t[0] == "vt" && t.size() == 3) {
				uv.push_back(glm::vec3(std::stof(t[1]),
                                       std::stof(t[2]),
                                       0));
			}
			else if (t[0] == "vn" && t.size() == 4) {
				normals.push_back(glm::vec3(std::stof(t[1]),
                                            std::stof(t[2]),
                                            std::stof(t[3])));
			}
			else if (t[0] == "f") {
				if (t.size() == 5) {
					int v[4];
					int n[4];
					int u[4];
					for (size_t i = 1; i < t.size(); i++) {
						//LOG(INFO) << t[i];
						v[i-1] = std::stoi(t[i].substr(0, t[i].find_first_of('/')),nullptr);
						u[i - 1] = std::stoi(t[i].substr(t[i].find_first_of('/') + 1, t[i].find_last_of('/') - t[i].find_first_of('/') - 1));
						n[i - 1] = std::stoi(t[i].substr(t[i].find_last_of('/') + 1, t[i].size() - t[i].find_last_of('/') - 1));
					}
					if (g.size() == 0) {
						g = "default";
						addGroup(g);
					}
					groups[g].triangles.push_back(Triangle(glm::ivec3(v[0] - 1, v[1] - 1, v[3] - 1),
                                                           glm::ivec3(n[0] - 1, n[1] - 1, n[3] - 1),
                                                           glm::ivec3(u[0] - 1, u[1] - 1, u[3] - 1)));
					groups[g].triangles.push_back(Triangle(glm::ivec3(v[3] - 1, v[1] - 1, v[2] - 1),
                                                           glm::ivec3(n[3] - 1, n[1] - 1, n[2] - 1),
                                                           glm::ivec3(u[3] - 1, u[1] - 1, u[2] - 1)));
				}
				else if (t[0].size()) {
					int scan = sscanf(line.c_str(), "f %d/%d/%d %d/%d/%d %d/%d/%d", &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3);
					if (scan < 9) {
						scan = sscanf(line.c_str(), "f %d//%d %d//%d %d//%d", &v1, &n1, &v2, &n2, &v3, &n3);
						if (scan < 6) {
							scan = sscanf(line.c_str(), "f %d/%d %d/%d %d/%d", &v1, &t1, &v2, &t2, &v3, &t3);
							if (scan < 6) {
								LOG(ERROR) << "Failed to parse '" << line << "'.";
								throw std::runtime_error("");
							}
						}
					}
					if (g.size() == 0) {
						g = "default";
						addGroup(g);
					}
					groups[g].triangles.push_back(Triangle(glm::ivec3(v1 - 1, v2 - 1, v3 - 1),
                                                           glm::ivec3(n1 - 1, n2 - 1, n3 - 1),
                                                           glm::ivec3(t1 - 1, t2 - 1, t3 - 1)));
				}
			}
			else if (t[0] == "mtllib") {
				size_t found = filename.find_last_of("/");
				if (found == std::string::npos) readMTL(t[1]);
				else {
					readMTL(filename.substr(0, found + 1) + t[1]);
				}
			}
			else if (t[0] == "usemtl") {
				if (g.size() == 0) {
					g = "default";
					addGroup(g);
				}
				groups[g].materialIdx = t[1];
				if (materials.find(t[1]) == materials.end()) {
					LOG(ERROR) << "missing material " << t[1];
					throw std::runtime_error("");
				}
			}
			else if (t[0] == "g") {
				g = t[1];
				addGroup(g);
			}
			else if (t[0] == "s") {
				groups[g].s = t[1];
			}
			else {
				LOG(ERROR) << "Failed to parse '" << line << "'.";
				throw std::runtime_error("");
			}
		}
		myfile.close();
	}
	LOG(INFO) << "Finished loading '" << filename << "'.";
	LOG(TRACE) << vertices.size() << " vertices, "
	           << uv.size() << " texcoords, "
	           << normals.size() << " normals.";
}

void Model::readMTL(std::string filename) {
	std::string line;
	std::ifstream myfile(filename);
	int prev = materials.size();
	LOG(INFO) << "reading mtl file" << filename;
	if (myfile.is_open())
	{
		std::string mtl;
		while (getline(myfile, line)) {
			std::vector<std::string> t = split(line, ' ');
			if (t.size() == 0 || t[0] == "#" || t[0] == "\n");
			else if (t[0] == "newmtl") {
				mtl = t[1];
				materials[mtl];
			}
			else if (t[0] == "illum") materials[mtl].illum = std::stoi(t[1]);
			else if (t[0] == "Ka") materials[mtl].Ka = glm::vec3(std::stof(t[1]), std::stof(t[2]), std::stof(t[3]));
			else if (t[0] == "Kd") materials[mtl].Kd = glm::vec3(std::stof(t[1]), std::stof(t[2]), std::stof(t[3]));
			else if (t[0] == "Ks") materials[mtl].Ks = glm::vec3(std::stof(t[1]), std::stof(t[2]), std::stof(t[3]));
			else if (t[0] == "Ke") materials[mtl].Ke = glm::vec3(std::stof(t[1]), std::stof(t[2]), std::stof(t[3]));
			else if (t[0] == "Tf") materials[mtl].Tf = glm::vec3(std::stof(t[1]), std::stof(t[2]), std::stof(t[3]));
			else if (t[0] == "Ns") materials[mtl].Ns = std::stof(t[1]);
			else if (t[0] == "Ni") materials[mtl].Ni = std::stof(t[1]);
			else if (t[0] == "d") materials[mtl].d = std::stof(t[1]);
			else if (t[0] == "Tr") materials[mtl].Tr = std::stof(t[1]);
			/*else if (t2[0] == "map_Kd") {
				m.map_Kd = new gl::texture2D(t2[1], GL_UNSIGNED_BYTE);
			}*/
			else {
				LOG(ERROR) << t[0];
			}
		}
	}
	LOG(INFO) << "finished reading mtl file " << std::to_string(materials.size() - prev);
}

void Model::display(const camera& camera, const std::vector<light>& lights) {
	if (drawLists.empty()) {
		CreateDrawingLists();
	}

	for (auto var : drawLists)
	{
		m_shader.bind();
		m_shader.set("view", camera.view());
		m_shader.set("proj", camera.proj());
		glCallList(var.second);
		m_shader.unbind();
	}
	/*m_shader.bind();

    m_shader.set("camera_pos", camera.pos());
    m_shader.set("light_count", (int)lights.size());

    for (size_t t = 0; t < lights.size(); ++t) {
        m_shader.set("lights[" + std::to_string(t) + "].pos", lights[t].pos);
        // m_shader.set("lights[" + std::to_string(t) + "].falloff", lights[t].falloff);
        // m_shader.set("lights[" + std::to_string(t) + "].intensity", lights[t].intensity);
    }

    m_shader.set("ks", glm::vec3(0.9, 0.9, 0.9));
    m_shader.set("kd", glm::vec3(0.4, 0.3, 0.2));
    m_shader.set("ka", glm::vec3(0.1, 0.1, 0.1));
    m_shader.set("shininess", 32.0f);

	for (auto var : drawLists)
	{
        // set up material

		glCallList(var.second);
	}

	m_shader.unbind(); */
}

void Model::useMTL(std::string mtl) {
	glMaterialfv(GL_FRONT, GL_AMBIENT, glm::value_ptr(materials[mtl].Ka));
	glMaterialfv(GL_FRONT, GL_DIFFUSE, glm::value_ptr(materials[mtl].Kd));
	glMaterialfv(GL_FRONT, GL_SPECULAR, glm::value_ptr(glm::vec4(materials[mtl].Ks, materials[mtl].Ns)));
	//glEnable(GL_TEXTURE_2D);
	//materials[mtl].map_Kd->bind(0);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
void Model::addToList(int v, int n, int u) {
	if (uv.size()>0) glTexCoord2f(uv[u].x, uv[u].y);
	glNormal3f(normals[n].x, normals[n].y, normals[n].z); //Add the normal
	glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z); //Add the vertex
}
void Model::CreateDrawingLists() {
	if (!drawLists.empty()) {
		for (std::pair<std::string,int> var : drawLists)
		{
			glDeleteLists(var.second, 1);
		}
		drawLists.clear();
	}
	for (auto& g : groups) {
		int l = glGenLists(1);
		glBegin(GL_TRIANGLES);
		for (Triangle t : g.second.triangles) {
			addToList(t.v[0], t.n[0], t.t[0]);
			addToList(t.v[1], t.n[1], t.t[1]);
			addToList(t.v[2], t.n[2], t.t[2]);
		}
		glEnd();
		drawLists.push_back(std::pair<std::string, int>(g.second.materialIdx, l));
	}
}
<|endoftext|>


<commit_before>/*! \file polymorphic_impl.hpp
    \brief Internal polymorphism support
    \ingroup Internal */
/*
  Copyright (c) 2013, Randolph Voorhies, Shane Grant
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
      * Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
      * Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
      * Neither the name of cereal nor the
        names of its contributors may be used to endorse or promote products
        derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/* This code is heavily inspired by the boost serialization implementation by the following authors

   (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
   Use, modification and distribution is subject to the Boost Software
   License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)

    See http://www.boost.org for updates, documentation, and revision history.

   (C) Copyright 2006 David Abrahams - http://www.boost.org.

   See /boost/serialization/export.hpp and /boost/archive/detail/register_archive.hpp for their
   implementation.
*/
#ifndef CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
#define CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_

#include <cereal/details/static_object.hpp>
#include <cereal/types/memory.hpp>
#include <cereal/types/string.hpp>
#include <functional>
#include <typeindex>
#include <map>

//! Binds a polymorhic type to all registered archives
/*! This binds a polymorphic type to all registered archives that
    have been registered with CEREAL_REGISTER_ARCHIVE.  This must be called
    after all archives are registered (usually after the archives themselves
    have been included). */
#define CEREAL_BIND_TO_ARCHIVES(T)                           \
    namespace cereal {                                       \
    namespace detail {                                       \
    template<>                                               \
    struct init_binding<T> {                                 \
        static bind_to_archives<T> const & b;                \
    };                                                       \
    bind_to_archives<T> const & init_binding<T>::b =         \
        ::cereal::detail::StaticObject<                      \
            bind_to_archives<T>                              \
        >::getInstance().bind();                             \
    }} // end namespaces

namespace cereal
{
  namespace detail
  {
    //! Binds a compile time type with a user defined string
    template <class T>
    struct binding_name {};

    //! A structure holding a map from type_indices to output serializer functions
    /*! A static object of this map should be created for each registered archive
        type, containing entries for every registered type that describe how to
        properly cast the type to its real type in polymorphic scenarios for
        shared_ptr, weak_ptr, and unique_ptr. */
    template <class Archive>
    struct OutputBindingMap
    {
      //! A serializer function
      /*! Serializer functions return nothing and take an archive as
          their first parameter (will be cast properly inside the function,
          and a pointer to actual data (contents of smart_ptr's get() function)
          as their second parameter */
      typedef std::function<void(void*, void const *)> Serializer;

      //! Struct containing the serializer functions for all pointer types
      struct Serializers
      {
        Serializer shared_ptr, //!< Serializer function for shared/weak pointers
                   unique_ptr; //!< Serializer function for unique pointers
      };

      //! A map of serializers for pointers of all registered types
      std::map<std::type_index, Serializers> map;
    };

    //! An empty noop deleter
    template<class T> struct EmptyDeleter { void operator()(T *) const {} };

    //! A structure holding a map from type name strings to input serializer functions
    /*! A static object of this map should be created for each registered archive
        type, containing entries for every registered type that describe how to
        properly cast the type to its real type in polymorphic scenarios for
        shared_ptr, weak_ptr, and unique_ptr. */
    template <class Archive>
    struct InputBindingMap
    {
      //! Shared ptr serializer function
      /*! Serializer functions return nothing and take an archive as
          their first parameter (will be cast properly inside the function,
          and a shared_ptr (or unique_ptr for the unique case) of any base
          type.  Internally it will properly be loaded and cast to the
          correct type. */
      typedef std::function<void(void*, std::shared_ptr<void> & )> SharedSerializer;
      //! Unique ptr serializer function
      typedef std::function<void(void*, std::unique_ptr<void, EmptyDeleter<void>> & )> UniqueSerializer;

      //! Struct containing the serializer functions for all pointer types
      struct Serializers
      {
        SharedSerializer shared_ptr; //!< Serializer function for shared/weak pointers
        UniqueSerializer unique_ptr; //!< Serializer function for unique pointers
      };

      //! A map of serializers for pointers of all registered types
      std::map<std::string, Serializers> map;
    };

    // forward decls for archives from cereal.hpp
    struct InputArchiveBase;
    struct OutputArchiveBase;

    //! Creates a binding (map entry) between an input archive type and a polymorphic type
    /*! Bindings are made when types are registered, assuming that at least one
        archive has already been registered.  When this struct is created,
        it will insert (at run time) an entry into a map that properly handles
        casting for serializing polymorphic objects */
    template <class Archive, class T> struct InputBindingCreator
    {
      //! Initialize the binding
      InputBindingCreator()
      {
        typename InputBindingMap<Archive>::Serializers serializers;

        serializers.shared_ptr =
          [](void * arptr, std::shared_ptr<void> & dptr)
          {
            Archive & ar = *static_cast<Archive*>(arptr);
            std::shared_ptr<T> ptr;

            ar( _CEREAL_NVP("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) );

            dptr = ptr;
          };

        serializers.unique_ptr =
          [](void * arptr, std::unique_ptr<void, EmptyDeleter<void>> & dptr)
          {
            Archive & ar = *static_cast<Archive*>(arptr);
            std::unique_ptr<T> ptr;

            ar( _CEREAL_NVP("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) );

            dptr.reset(ptr.release());
          };

        StaticObject<InputBindingMap<Archive>>::getInstance().map.insert( { std::string(binding_name<T>::name()), serializers } );
      }
    };

    //! Creates a binding (map entry) between an output archive type and a polymorphic type
    /*! Bindings are made when types are registered, assuming that at least one
        archive has already been registered.  When this struct is created,
        it will insert (at run time) an entry into a map that properly handles
        casting for serializing polymorphic objects */
    template <class Archive, class T> struct OutputBindingCreator
    {
      //! Writes appropriate metadata to the archive for this polymorphic type
      static void writeMetadata(Archive & ar)
      {
        // Register the polymorphic type name with the archive, and get the id
        char const * name = binding_name<T>::name();
        std::uint32_t id = ar.registerPolymorphicType(name);

        // Serialize the id
        ar( _CEREAL_NVP("polymorphic_id", id) );

        // If the msb of the id is 1, then the type name is new, and we should serialize it
        if( id & detail::msb_32bit )
        {
          std::string namestring(name);
          ar( _CEREAL_NVP("polymorphic_name", namestring) );
        }
      }

      //! Constructs the appropriate shared_ptr for the polymorphic type
      /*! @param dptr A void pointer to the contents of the shared_ptr to serialize
          @return A shared_ptr pointing to dptr with an empty deleter */
      static inline std::shared_ptr<T const> createSharedPtr( void const * dptr )
      {
        #ifdef _LIBCPP_VERSION
        // libc++ needs this hacky workaround, see http://llvm.org/bugs/show_bug.cgi?id=18843
        return std::shared_ptr<T const>(
            std::const_pointer_cast<T const>(
              std::shared_ptr<T>(static_cast<T *>(const_cast<void *>(dptr)), EmptyDeleter<T>())));
        #else // NOT _LIBCPP_VERSION
        return std::shared_ptr<T const>(static_cast<T const *>(dptr), EmptyDeleter<T const>());
        #endif // _LIBCPP_VERSION
      }

      //! Does the actual work of saving a polymorphic shared_ptr
      /*! This function will properly create a shared_ptr from the void * that is passed in
          before passing it to the archive for serialization.

          In addition, this will also preserve the state of any internal enable_shared_from_this mechanisms

          @param ar The archive to serialize to
          @param dptr Pointer to the actual data held by the shared_ptr */
      static inline void savePolymorphicSharedPtr( Archive & ar, void const * dptr, std::true_type /* has_shared_from_this */ )
      {
        ::cereal::memory_detail::EnableSharedHelper<T> helper( static_cast<T *>(const_cast<void *>(dptr)) );

        auto const ptr = createSharedPtr( dptr );
        ar( _CEREAL_NVP("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );

        helper.restore();
      }

      //! Does the actual work of saving a polymorphic shared_ptr
      /*! This function will properly create a shared_ptr from the void * that is passed in
          before passing it to the archive for serialization.

          This version is for types that do not inherit from std::enable_shared_from_this.

          @param ar The archive to serialize to
          @param dptr Pointer to the actual data held by the shared_ptr */
      static inline void savePolymorphicSharedPtr( Archive & ar, void const * dptr, std::false_type /* has_shared_from_this */ )
      {
        auto const ptr = createSharedPtr( dptr );
        ar( _CEREAL_NVP("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
      }

      //! Initialize the binding
      OutputBindingCreator()
      {
        typename OutputBindingMap<Archive>::Serializers serializers;

        serializers.shared_ptr =
          [&](void * arptr, void const * dptr)
          {
            Archive & ar = *static_cast<Archive*>(arptr);

            writeMetadata(ar);

            savePolymorphicSharedPtr( ar, dptr, typename ::cereal::traits::has_shared_from_this<T>::type() );
          };

        serializers.unique_ptr =
          [&](void * arptr, void const * dptr)
          {
            Archive & ar = *static_cast<Archive*>(arptr);

            writeMetadata(ar);

            std::unique_ptr<T const, EmptyDeleter<T const>> const ptr(static_cast<T const *>(dptr));

            ar( _CEREAL_NVP("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
          };

        StaticObject<OutputBindingMap<Archive>>::getInstance().map.insert( {std::type_index(typeid(T)), serializers} );
      }
    };

    //! Used to help out argument dependent lookup for finding potential overloads
    //! of instantiate_polymorphic_binding
    struct adl_tag {};

    //! Causes the static object bindings between an archive type and a serializable type T
    template <class Archive, class T>
    struct create_bindings
    {
      static const InputBindingCreator<Archive, T> &
      load(std::true_type)
      {
        return cereal::detail::StaticObject<InputBindingCreator<Archive, T>>::getInstance();
      }

      static const OutputBindingCreator<Archive, T> &
      save(std::true_type)
      {
        return cereal::detail::StaticObject<OutputBindingCreator<Archive, T>>::getInstance();
      }

      inline static void load(std::false_type) {}
      inline static void save(std::false_type) {}
    };

    //! When specialized, causes the compiler to instantiate its parameter
    template <void(*)()>
    struct instantiate_function {};

    /*! This struct is used as the return type of instantiate_polymorphic_binding
        for specific Archive types.  When the compiler looks for overloads of
        instantiate_polymorphic_binding, it will be forced to instantiate this
        struct during overload resolution, even though it will not be part of a valid
        overload */
    template <class Archive, class T>
    struct polymorphic_serialization_support
    {
#ifdef _MSC_VER
      //! Creates the appropriate bindings depending on whether the archive supports
      //! saving or loading
      virtual void instantiate();
#else // NOT _MSC_VER
      //! Creates the appropriate bindings depending on whether the archive supports
      //! saving or loading
      static void instantiate();
      //! This typedef causes the compiler to instantiate this static function
      typedef instantiate_function<instantiate> unused;
#endif // _MSC_VER
    };

    // instantiate implementation
    template <class Archive, class T>
    void polymorphic_serialization_support<Archive,T>::instantiate()
    {
      create_bindings<Archive,T>::save( std::is_base_of<detail::OutputArchiveBase, Archive>() );

      create_bindings<Archive,T>::load( std::is_base_of<detail::InputArchiveBase, Archive>() );
    }

    //! Begins the binding process of a type to all registered archives
    /*! Archives need to be registered prior to this struct being instantiated via
        the CEREAL_REGISTER_ARCHIVE macro.  Overload resolution will then force
        several static objects to be made that allow us to bind together all
        registered archive types with the parameter type T. */
    template <class T>
    struct bind_to_archives
    {
      //! Binding for non abstract types
      void bind(std::false_type) const
	    {
		    instantiate_polymorphic_binding((T*) 0, 0, adl_tag{});
      }

      //! Binding for abstract types
      void bind(std::true_type) const
      { }

      //! Binds the type T to all registered archives
      /*! If T is abstract, we will not serialize it and thus
          do not need to make a binding */
      bind_to_archives const & bind() const
      {
        static_assert( std::is_polymorphic<T>::value,
                       "Attempting to register non polymorphic type" );
        bind( std::is_abstract<T>() );
        return *this;
      }
    };

    //! Used to hide the static object used to bind T to registered archives
    template <class T>
    struct init_binding;

    //! Base case overload for instantiation
    /*! This will end up always being the best overload due to the second
        parameter always being passed as an int.  All other overloads will
        accept pointers to archive types and have lower precedence than int.

        Since the compiler needs to check all possible overloads, the
        other overloads created via CEREAL_REGISTER_ARCHIVE, which will have
        lower precedence due to requring a conversion from int to (Archive*),
        will cause their return types to be instantiated through the static object
        mechanisms even though they are never called.

        See the documentation for the other functions to try and understand this */
    template <class T>
    void instantiate_polymorphic_binding( T*, int, adl_tag ) {}
  } // namespace detail
} // namespace cereal

#endif // CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
<commit_msg>fixing typo for VS compliance (see #68)<commit_after>/*! \file polymorphic_impl.hpp
    \brief Internal polymorphism support
    \ingroup Internal */
/*
  Copyright (c) 2013, Randolph Voorhies, Shane Grant
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
      * Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
      * Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
      * Neither the name of cereal nor the
        names of its contributors may be used to endorse or promote products
        derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/* This code is heavily inspired by the boost serialization implementation by the following authors

   (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
   Use, modification and distribution is subject to the Boost Software
   License, Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)

    See http://www.boost.org for updates, documentation, and revision history.

   (C) Copyright 2006 David Abrahams - http://www.boost.org.

   See /boost/serialization/export.hpp and /boost/archive/detail/register_archive.hpp for their
   implementation.
*/
#ifndef CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
#define CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_

#include <cereal/details/static_object.hpp>
#include <cereal/types/memory.hpp>
#include <cereal/types/string.hpp>
#include <functional>
#include <typeindex>
#include <map>

//! Binds a polymorhic type to all registered archives
/*! This binds a polymorphic type to all registered archives that
    have been registered with CEREAL_REGISTER_ARCHIVE.  This must be called
    after all archives are registered (usually after the archives themselves
    have been included). */
#define CEREAL_BIND_TO_ARCHIVES(T)                           \
    namespace cereal {                                       \
    namespace detail {                                       \
    template<>                                               \
    struct init_binding<T> {                                 \
        static bind_to_archives<T> const & b;                \
    };                                                       \
    bind_to_archives<T> const & init_binding<T>::b =         \
        ::cereal::detail::StaticObject<                      \
            bind_to_archives<T>                              \
        >::getInstance().bind();                             \
    }} // end namespaces

namespace cereal
{
  namespace detail
  {
    //! Binds a compile time type with a user defined string
    template <class T>
    struct binding_name {};

    //! A structure holding a map from type_indices to output serializer functions
    /*! A static object of this map should be created for each registered archive
        type, containing entries for every registered type that describe how to
        properly cast the type to its real type in polymorphic scenarios for
        shared_ptr, weak_ptr, and unique_ptr. */
    template <class Archive>
    struct OutputBindingMap
    {
      //! A serializer function
      /*! Serializer functions return nothing and take an archive as
          their first parameter (will be cast properly inside the function,
          and a pointer to actual data (contents of smart_ptr's get() function)
          as their second parameter */
      typedef std::function<void(void*, void const *)> Serializer;

      //! Struct containing the serializer functions for all pointer types
      struct Serializers
      {
        Serializer shared_ptr, //!< Serializer function for shared/weak pointers
                   unique_ptr; //!< Serializer function for unique pointers
      };

      //! A map of serializers for pointers of all registered types
      std::map<std::type_index, Serializers> map;
    };

    //! An empty noop deleter
    template<class T> struct EmptyDeleter { void operator()(T *) const {} };

    //! A structure holding a map from type name strings to input serializer functions
    /*! A static object of this map should be created for each registered archive
        type, containing entries for every registered type that describe how to
        properly cast the type to its real type in polymorphic scenarios for
        shared_ptr, weak_ptr, and unique_ptr. */
    template <class Archive>
    struct InputBindingMap
    {
      //! Shared ptr serializer function
      /*! Serializer functions return nothing and take an archive as
          their first parameter (will be cast properly inside the function,
          and a shared_ptr (or unique_ptr for the unique case) of any base
          type.  Internally it will properly be loaded and cast to the
          correct type. */
      typedef std::function<void(void*, std::shared_ptr<void> & )> SharedSerializer;
      //! Unique ptr serializer function
      typedef std::function<void(void*, std::unique_ptr<void, EmptyDeleter<void>> & )> UniqueSerializer;

      //! Struct containing the serializer functions for all pointer types
      struct Serializers
      {
        SharedSerializer shared_ptr; //!< Serializer function for shared/weak pointers
        UniqueSerializer unique_ptr; //!< Serializer function for unique pointers
      };

      //! A map of serializers for pointers of all registered types
      std::map<std::string, Serializers> map;
    };

    // forward decls for archives from cereal.hpp
    struct InputArchiveBase;
    struct OutputArchiveBase;

    //! Creates a binding (map entry) between an input archive type and a polymorphic type
    /*! Bindings are made when types are registered, assuming that at least one
        archive has already been registered.  When this struct is created,
        it will insert (at run time) an entry into a map that properly handles
        casting for serializing polymorphic objects */
    template <class Archive, class T> struct InputBindingCreator
    {
      //! Initialize the binding
      InputBindingCreator()
      {
        typename InputBindingMap<Archive>::Serializers serializers;

        serializers.shared_ptr =
          [](void * arptr, std::shared_ptr<void> & dptr)
          {
            Archive & ar = *static_cast<Archive*>(arptr);
            std::shared_ptr<T> ptr;

            ar( _CEREAL_NVP("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) );

            dptr = ptr;
          };

        serializers.unique_ptr =
          [](void * arptr, std::unique_ptr<void, EmptyDeleter<void>> & dptr)
          {
            Archive & ar = *static_cast<Archive*>(arptr);
            std::unique_ptr<T> ptr;

            ar( _CEREAL_NVP("ptr_wrapper", ::cereal::memory_detail::make_ptr_wrapper(ptr)) );

            dptr.reset(ptr.release());
          };

        StaticObject<InputBindingMap<Archive>>::getInstance().map.insert( { std::string(binding_name<T>::name()), serializers } );
      }
    };

    //! Creates a binding (map entry) between an output archive type and a polymorphic type
    /*! Bindings are made when types are registered, assuming that at least one
        archive has already been registered.  When this struct is created,
        it will insert (at run time) an entry into a map that properly handles
        casting for serializing polymorphic objects */
    template <class Archive, class T> struct OutputBindingCreator
    {
      //! Writes appropriate metadata to the archive for this polymorphic type
      static void writeMetadata(Archive & ar)
      {
        // Register the polymorphic type name with the archive, and get the id
        char const * name = binding_name<T>::name();
        std::uint32_t id = ar.registerPolymorphicType(name);

        // Serialize the id
        ar( _CEREAL_NVP("polymorphic_id", id) );

        // If the msb of the id is 1, then the type name is new, and we should serialize it
        if( id & detail::msb_32bit )
        {
          std::string namestring(name);
          ar( _CEREAL_NVP("polymorphic_name", namestring) );
        }
      }

      //! Constructs the appropriate shared_ptr for the polymorphic type
      /*! @param dptr A void pointer to the contents of the shared_ptr to serialize
          @return A shared_ptr pointing to dptr with an empty deleter */
      static inline std::shared_ptr<T const> createSharedPtr( void const * dptr )
      {
        #ifdef _LIBCPP_VERSION
        // libc++ needs this hacky workaround, see http://llvm.org/bugs/show_bug.cgi?id=18843
        return std::shared_ptr<T const>(
            std::const_pointer_cast<T const>(
              std::shared_ptr<T>(static_cast<T *>(const_cast<void *>(dptr)), EmptyDeleter<T>())));
        #else // NOT _LIBCPP_VERSION
        return std::shared_ptr<T const>(static_cast<T const *>(dptr), EmptyDeleter<T const>());
        #endif // _LIBCPP_VERSION
      }

      //! Does the actual work of saving a polymorphic shared_ptr
      /*! This function will properly create a shared_ptr from the void * that is passed in
          before passing it to the archive for serialization.

          In addition, this will also preserve the state of any internal enable_shared_from_this mechanisms

          @param ar The archive to serialize to
          @param dptr Pointer to the actual data held by the shared_ptr */
      static inline void savePolymorphicSharedPtr( Archive & ar, void const * dptr, std::true_type /* has_shared_from_this */ )
      {
        ::cereal::memory_detail::EnableSharedHelper<T> helper( static_cast<T *>(const_cast<void *>(dptr)) );

        auto const ptr = createSharedPtr( dptr );
        ar( _CEREAL_NVP("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );

        helper.restore();
      }

      //! Does the actual work of saving a polymorphic shared_ptr
      /*! This function will properly create a shared_ptr from the void * that is passed in
          before passing it to the archive for serialization.

          This version is for types that do not inherit from std::enable_shared_from_this.

          @param ar The archive to serialize to
          @param dptr Pointer to the actual data held by the shared_ptr */
      static inline void savePolymorphicSharedPtr( Archive & ar, void const * dptr, std::false_type /* has_shared_from_this */ )
      {
        auto const ptr = createSharedPtr( dptr );
        ar( _CEREAL_NVP("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
      }

      //! Initialize the binding
      OutputBindingCreator()
      {
        typename OutputBindingMap<Archive>::Serializers serializers;

        serializers.shared_ptr =
          [&](void * arptr, void const * dptr)
          {
            Archive & ar = *static_cast<Archive*>(arptr);

            writeMetadata(ar);

            savePolymorphicSharedPtr( ar, dptr, ::cereal::traits::has_shared_from_this<T>::type() );
          };

        serializers.unique_ptr =
          [&](void * arptr, void const * dptr)
          {
            Archive & ar = *static_cast<Archive*>(arptr);

            writeMetadata(ar);

            std::unique_ptr<T const, EmptyDeleter<T const>> const ptr(static_cast<T const *>(dptr));

            ar( _CEREAL_NVP("ptr_wrapper", memory_detail::make_ptr_wrapper(ptr)) );
          };

        StaticObject<OutputBindingMap<Archive>>::getInstance().map.insert( {std::type_index(typeid(T)), serializers} );
      }
    };

    //! Used to help out argument dependent lookup for finding potential overloads
    //! of instantiate_polymorphic_binding
    struct adl_tag {};

    //! Causes the static object bindings between an archive type and a serializable type T
    template <class Archive, class T>
    struct create_bindings
    {
      static const InputBindingCreator<Archive, T> &
      load(std::true_type)
      {
        return cereal::detail::StaticObject<InputBindingCreator<Archive, T>>::getInstance();
      }

      static const OutputBindingCreator<Archive, T> &
      save(std::true_type)
      {
        return cereal::detail::StaticObject<OutputBindingCreator<Archive, T>>::getInstance();
      }

      inline static void load(std::false_type) {}
      inline static void save(std::false_type) {}
    };

    //! When specialized, causes the compiler to instantiate its parameter
    template <void(*)()>
    struct instantiate_function {};

    /*! This struct is used as the return type of instantiate_polymorphic_binding
        for specific Archive types.  When the compiler looks for overloads of
        instantiate_polymorphic_binding, it will be forced to instantiate this
        struct during overload resolution, even though it will not be part of a valid
        overload */
    template <class Archive, class T>
    struct polymorphic_serialization_support
    {
#ifdef _MSC_VER
      //! Creates the appropriate bindings depending on whether the archive supports
      //! saving or loading
      virtual void instantiate();
#else // NOT _MSC_VER
      //! Creates the appropriate bindings depending on whether the archive supports
      //! saving or loading
      static void instantiate();
      //! This typedef causes the compiler to instantiate this static function
      typedef instantiate_function<instantiate> unused;
#endif // _MSC_VER
    };

    // instantiate implementation
    template <class Archive, class T>
    void polymorphic_serialization_support<Archive,T>::instantiate()
    {
      create_bindings<Archive,T>::save( std::is_base_of<detail::OutputArchiveBase, Archive>() );

      create_bindings<Archive,T>::load( std::is_base_of<detail::InputArchiveBase, Archive>() );
    }

    //! Begins the binding process of a type to all registered archives
    /*! Archives need to be registered prior to this struct being instantiated via
        the CEREAL_REGISTER_ARCHIVE macro.  Overload resolution will then force
        several static objects to be made that allow us to bind together all
        registered archive types with the parameter type T. */
    template <class T>
    struct bind_to_archives
    {
      //! Binding for non abstract types
      void bind(std::false_type) const
	    {
		    instantiate_polymorphic_binding((T*) 0, 0, adl_tag{});
      }

      //! Binding for abstract types
      void bind(std::true_type) const
      { }

      //! Binds the type T to all registered archives
      /*! If T is abstract, we will not serialize it and thus
          do not need to make a binding */
      bind_to_archives const & bind() const
      {
        static_assert( std::is_polymorphic<T>::value,
                       "Attempting to register non polymorphic type" );
        bind( std::is_abstract<T>() );
        return *this;
      }
    };

    //! Used to hide the static object used to bind T to registered archives
    template <class T>
    struct init_binding;

    //! Base case overload for instantiation
    /*! This will end up always being the best overload due to the second
        parameter always being passed as an int.  All other overloads will
        accept pointers to archive types and have lower precedence than int.

        Since the compiler needs to check all possible overloads, the
        other overloads created via CEREAL_REGISTER_ARCHIVE, which will have
        lower precedence due to requring a conversion from int to (Archive*),
        will cause their return types to be instantiated through the static object
        mechanisms even though they are never called.

        See the documentation for the other functions to try and understand this */
    template <class T>
    void instantiate_polymorphic_binding( T*, int, adl_tag ) {}
  } // namespace detail
} // namespace cereal

#endif // CEREAL_DETAILS_POLYMORPHIC_IMPL_HPP_
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2019, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 *   This file implements lwip net interface with OpenThread.
 */

#include <lwip/mld6.h>
#include <lwip/netif.h>
#include <lwip/tcpip.h>
#include <lwip/udp.h>
#include <semphr.h>

#include <openthread/icmp6.h>
#include <openthread/ip6.h>
#include <openthread/link.h>
#include <openthread/message.h>
#include <openthread/openthread-freertos.h>
#include <openthread/thread.h>
#include <openthread/platform/radio.h>

#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/logging.hpp"

#include "lwip/dns.h"
#include "lwip/sockets.h"

#include "netif.h"

static const size_t kMaxIp6Size = 1500;

/**
 * This structure stores information needed to send a IPv6 packet.
 *
 */
struct OutputEvent
{
    OutputEvent *mNext;
    uint16_t     mLength;
    uint8_t      mData[1];
};

static SemaphoreHandle_t sGuardOutput = NULL;
static OutputEvent *     sHeadOutput  = NULL;
static OutputEvent *     sLastOutput  = NULL;
static struct netif      sNetif;

static bool IsLinkLocal(const struct otIp6Address &aAddress)
{
    return aAddress.mFields.m16[0] == htons(0xfe80);
}

static void HandleNetifStatus(struct netif *aNetif)
{
    // TODO process events from lwip
    (void)aNetif;
    otLogInfoPlat("LwIP netif event");
}

static err_t netifOutputIp6(struct netif *aNetif, struct pbuf *aBuffer, const ip6_addr_t *aPeerAddr)
{
    (void)aPeerAddr;

    err_t        err   = ERR_OK;
    OutputEvent *event = NULL;

    otLogInfoPlat("netif output");
    assert(aNetif == &sNetif);
    event = (OutputEvent *)malloc(sizeof(*event) + aBuffer->tot_len);

    event->mLength = aBuffer->tot_len;
    event->mNext   = NULL;
    VerifyOrExit(event != NULL, err = ERR_BUF);
    VerifyOrExit(aBuffer->tot_len == pbuf_copy_partial(aBuffer, event->mData, aBuffer->tot_len, 0), err = ERR_ARG);

    xSemaphoreTake(sGuardOutput, portMAX_DELAY);
    if (sLastOutput == NULL)
    {
        sHeadOutput = event;
        sLastOutput = event;
    }
    else
    {
        sLastOutput->mNext = event;
    }
    xSemaphoreGive(sGuardOutput);

    otrTaskNotifyGive();

exit:
    if (err != ERR_OK)
    {
        if (event != NULL)
        {
            free(event);
        }
    }
    return err;
}

static err_t netifInit(struct netif *aNetif)
{
    aNetif->name[0]    = 'o';
    aNetif->name[1]    = 't';
    aNetif->hwaddr_len = sizeof(otExtAddress);
    memset(aNetif->hwaddr, 0, sizeof(aNetif->hwaddr));
    aNetif->mtu   = OPENTHREAD_CONFIG_IP6_MAX_DATAGRAM_LENGTH;
    aNetif->flags = NETIF_FLAG_BROADCAST;
#if LWIP_IPV4
    aNetif->output = NULL;
#endif
#if LWIP_IPV6
    aNetif->output_ip6 = netifOutputIp6;
#endif
    aNetif->num = 0;

    return ERR_OK;
}

struct netif *otrGetNetif(void)
{
    return &sNetif;
}

static void addMulticastAddress(const otIp6Address &aAddress)
{
    ip6_addr_t multicast_addr;

    memcpy(&multicast_addr.addr, &aAddress, sizeof(aAddress));
    multicast_addr.zone = IP6_NO_ZONE;
    mld6_joingroup_netif(&sNetif, &multicast_addr);
}

static void addAddress(const otIp6Address &aAddress)
{
    otError error = OT_ERROR_NONE;
    err_t   err   = ERR_OK;

    LOCK_TCPIP_CORE();

    if (IsLinkLocal(aAddress))
    {
        netif_ip6_addr_set(&sNetif, 0, reinterpret_cast<const ip6_addr_t *>(&aAddress));
        netif_ip6_addr_set_state(&sNetif, 0, IP6_ADDR_PREFERRED);
    }
    else
    {
        int8_t                   index  = -1;
        const otMeshLocalPrefix *prefix = otThreadGetMeshLocalPrefix(otrGetInstance());

        err = netif_add_ip6_address(&sNetif, reinterpret_cast<const ip6_addr_t *>(&aAddress), &index);
        VerifyOrExit(err == ERR_OK && index != -1, error = OT_ERROR_FAILED);
        if (memcmp(&aAddress, prefix, sizeof(prefix->m8)) != 0)
        {
            netif_ip6_addr_set_state(&sNetif, index, IP6_ADDR_PREFERRED);
        }
        else
        {
            netif_ip6_addr_set_state(&sNetif, index, IP6_ADDR_VALID);
        }
    }

exit:
    UNLOCK_TCPIP_CORE();

    if (error != OT_ERROR_NONE)
    {
        otLogInfoPlat("Failed to add address: %d", error);
    }
}

static void delMulticastAddress(const otIp6Address &aAddress)
{
    ip6_addr_t multicast_addr;

    memcpy(&multicast_addr.addr, &aAddress, sizeof(aAddress));
    multicast_addr.zone = IP6_NO_ZONE;
    mld6_leavegroup_netif(&sNetif, &multicast_addr);
}

static void delAddress(const otIp6Address &aAddress)
{
    int8_t index;

    LOCK_TCPIP_CORE();
    index = netif_get_ip6_addr_match(&sNetif, reinterpret_cast<const ip6_addr_t *>(&aAddress));
    VerifyOrExit(index != -1);
    netif_ip6_addr_set_state(&sNetif, index, IP6_ADDR_INVALID);

exit:
    UNLOCK_TCPIP_CORE();
}

static void setupDns(void)
{
#if LWIP_IPV4
    ip_addr_t dnsServer;

    inet_pton(AF_INET6, "64:ff9b::808:808", &dnsServer.u_addr.ip6.addr);
    dnsServer.type            = IPADDR_TYPE_V6;
    dnsServer.u_addr.ip6.zone = IP6_NO_ZONE;
#else
    ip6_addr_t dnsServer;

    inet_pton(AF_INET6, "64:ff9b::808:808", &dnsServer.addr);
    dnsServer.zone = IP6_NO_ZONE;
#endif

    dns_init();
    dns_setserver(0, &dnsServer);
}

static void processStateChange(otChangedFlags aFlags, void *aContext)
{
    otInstance *instance = static_cast<otInstance *>(aContext);

    if (OT_CHANGED_THREAD_NETIF_STATE | aFlags)
    {
        LOCK_TCPIP_CORE();
        if (otLinkIsEnabled(instance))
        {
            otLogInfoPlat("netif up");
            netif_set_up(&sNetif);
        }
        else
        {
            otLogInfoPlat("netif down");
            netif_set_down(&sNetif);
        }
        UNLOCK_TCPIP_CORE();
    }
}

static void processAddress(const otIp6Address *aAddress, uint8_t aPrefixLength, bool aIsAdded, void *aContext)
{
    (void)aContext;

    // All multicast addresses have prefix ff00::/8
    bool isMulticast = (aAddress->mFields.m8[0] == 0xff);

    otLogInfoPlat("address changed");

    if (aIsAdded)
    {
        if (isMulticast)
        {
            addMulticastAddress(*aAddress);
        }
        else
        {
            addAddress(*aAddress);
        }
    }
    else
    {
        if (isMulticast)
        {
            delMulticastAddress(*aAddress);
        }
        else
        {
            delAddress(*aAddress);
        }
    }
}

static void processReceive(otMessage *aMessage, void *aContext)
{
    otError      error      = OT_ERROR_NONE;
    err_t        err        = ERR_OK;
    const size_t kBlockSize = 128;
    uint16_t     length     = otMessageGetLength(aMessage);
    struct pbuf *buffer     = NULL;
    otInstance * aInstance  = static_cast<otInstance *>(aContext);

    assert(sNetif.state == aInstance);

    buffer = pbuf_alloc(PBUF_RAW, length, PBUF_POOL);

    VerifyOrExit(buffer != NULL, error = OT_ERROR_NO_BUFS);

    for (uint16_t i = 0; i < length; i += kBlockSize)
    {
        uint8_t block[kBlockSize];
        int     count = otMessageRead(aMessage, i, block, sizeof(block));

        assert(count > 0);
        err = pbuf_take_at(buffer, block, count, i);
        VerifyOrExit(err == ERR_OK, error = OT_ERROR_FAILED);
    }

    VerifyOrExit(ERR_OK == sNetif.input(buffer, &sNetif), error = OT_ERROR_FAILED);

exit:
    if (error != OT_ERROR_NONE)
    {
        if (buffer != NULL)
        {
            pbuf_free(buffer);
        }

        if (error == OT_ERROR_FAILED)
        {
            otLogWarnPlat("%s failed for lwip error %d", __func__, err);
        }

        otLogWarnPlat("%s failed: %s", __func__, otThreadErrorToString(error));
    }

    otMessageFree(aMessage);
}

static void processTransmit(otInstance *aInstance)
{
    otError    error   = OT_ERROR_NONE;
    otMessage *message = NULL;

    VerifyOrExit(sHeadOutput != NULL);

    message = otIp6NewMessage(aInstance, NULL);
    VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS);

    SuccessOrExit(error = otMessageAppend(message, sHeadOutput->mData, sHeadOutput->mLength));

    error   = otIp6Send(aInstance, message);
    message = NULL;

    {
        OutputEvent *nextEvent = sHeadOutput->mNext;

        free(sHeadOutput);
        sHeadOutput = nextEvent;
    }

    // Notify if more
    if (sHeadOutput != NULL)
    {
        otrTaskNotifyGive();
    }
    else
    {
        sLastOutput = NULL;
    }

exit:
    if (error != OT_ERROR_NONE)
    {
        if (message != NULL)
        {
            otMessageFree(message);
        }

        otLogWarnPlat("Failed to transmit IPv6 packet: %s", otThreadErrorToString(error));
    }
}

void netifInit(void *aContext)
{
    otInstance *instance = static_cast<otInstance *>(aContext);

    memset(&sNetif, 0, sizeof(sNetif));
    // LOCK_TCPIP_CORE();
#if LWIP_IPV4
    netif_add(&sNetif, NULL, NULL, NULL, instance, netifInit, tcpip_input);
#else
    netif_add(&sNetif, instance, netifInit, tcpip_input);
#endif
    netif_set_link_up(&sNetif);
    netif_set_status_callback(&sNetif, HandleNetifStatus);
    // UNLOCK_TCPIP_CORE();

    sGuardOutput = xSemaphoreCreateMutex();
    xSemaphoreGive(sGuardOutput);
    assert(sGuardOutput != NULL);
    otLogInfoPlat("Initialize netif");

    otIp6SetAddressCallback(instance, processAddress, instance);
    otIp6SetReceiveCallback(instance, processReceive, instance);
    otSetStateChangedCallback(instance, processStateChange, instance);
    otIp6SetReceiveFilterEnabled(instance, true);
    otIcmp6SetEchoMode(instance, OT_ICMP6_ECHO_HANDLER_DISABLED);

    netif_set_default(&sNetif);

    setupDns();
}

void netifProcess(otInstance *aInstance)
{
    if (sGuardOutput != NULL && xSemaphoreTake(sGuardOutput, portMAX_DELAY) == pdTRUE)
    {
        processTransmit(aInstance);
        xSemaphoreGive(sGuardOutput);
    }
}
<commit_msg>[netif] fix pbuf allocation type and task execution (#33)<commit_after>/*
 *  Copyright (c) 2019, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 *   This file implements lwip net interface with OpenThread.
 */

#include <lwip/mld6.h>
#include <lwip/netif.h>
#include <lwip/tcpip.h>
#include <lwip/udp.h>
#include <semphr.h>

#include <openthread/icmp6.h>
#include <openthread/ip6.h>
#include <openthread/link.h>
#include <openthread/message.h>
#include <openthread/openthread-freertos.h>
#include <openthread/thread.h>
#include <openthread/platform/radio.h>

#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/logging.hpp"

#include "lwip/dns.h"
#include "lwip/sockets.h"

#include "netif.h"

static const size_t kMaxIp6Size = 1500;

/**
 * This structure stores information needed to send a IPv6 packet.
 *
 */
struct OutputEvent
{
    OutputEvent *mNext;
    uint16_t     mLength;
    uint8_t      mData[1];
};

static SemaphoreHandle_t sGuardOutput = NULL;
static OutputEvent *     sHeadOutput  = NULL;
static OutputEvent *     sLastOutput  = NULL;
static struct netif      sNetif;

static bool IsLinkLocal(const struct otIp6Address &aAddress)
{
    return aAddress.mFields.m16[0] == htons(0xfe80);
}

static void HandleNetifStatus(struct netif *aNetif)
{
    // TODO process events from lwip
    (void)aNetif;
    otLogInfoPlat("LwIP netif event");
}

static err_t netifOutputIp6(struct netif *aNetif, struct pbuf *aBuffer, const ip6_addr_t *aPeerAddr)
{
    (void)aPeerAddr;

    err_t        err   = ERR_OK;
    OutputEvent *event = NULL;

    otLogInfoPlat("netif output");
    assert(aNetif == &sNetif);
    event = (OutputEvent *)malloc(sizeof(*event) + aBuffer->tot_len);

    event->mLength = aBuffer->tot_len;
    event->mNext   = NULL;
    VerifyOrExit(event != NULL, err = ERR_BUF);
    VerifyOrExit(aBuffer->tot_len == pbuf_copy_partial(aBuffer, event->mData, aBuffer->tot_len, 0), err = ERR_ARG);

    xSemaphoreTake(sGuardOutput, portMAX_DELAY);
    if (sLastOutput == NULL)
    {
        sHeadOutput = event;
        sLastOutput = event;
    }
    else
    {
        sLastOutput->mNext = event;
    }
    xSemaphoreGive(sGuardOutput);

    otrTaskNotifyGive();

exit:
    if (err != ERR_OK)
    {
        if (event != NULL)
        {
            free(event);
        }
    }
    return err;
}

static err_t netifInit(struct netif *aNetif)
{
    aNetif->name[0]    = 'o';
    aNetif->name[1]    = 't';
    aNetif->hwaddr_len = sizeof(otExtAddress);
    memset(aNetif->hwaddr, 0, sizeof(aNetif->hwaddr));
    aNetif->mtu   = OPENTHREAD_CONFIG_IP6_MAX_DATAGRAM_LENGTH;
    aNetif->flags = NETIF_FLAG_BROADCAST;
#if LWIP_IPV4
    aNetif->output = NULL;
#endif
#if LWIP_IPV6
    aNetif->output_ip6 = netifOutputIp6;
#endif
    aNetif->num = 0;

    return ERR_OK;
}

struct netif *otrGetNetif(void)
{
    return &sNetif;
}

static void addMulticastAddress(const otIp6Address &aAddress)
{
    ip6_addr_t multicast_addr;

    memcpy(&multicast_addr.addr, &aAddress, sizeof(aAddress));
    multicast_addr.zone = IP6_NO_ZONE;
    mld6_joingroup_netif(&sNetif, &multicast_addr);
}

static void addAddress(const otIp6Address &aAddress)
{
    otError error = OT_ERROR_NONE;
    err_t   err   = ERR_OK;

    LOCK_TCPIP_CORE();

    if (IsLinkLocal(aAddress))
    {
        netif_ip6_addr_set(&sNetif, 0, reinterpret_cast<const ip6_addr_t *>(&aAddress));
        netif_ip6_addr_set_state(&sNetif, 0, IP6_ADDR_PREFERRED);
    }
    else
    {
        int8_t                   index  = -1;
        const otMeshLocalPrefix *prefix = otThreadGetMeshLocalPrefix(otrGetInstance());

        err = netif_add_ip6_address(&sNetif, reinterpret_cast<const ip6_addr_t *>(&aAddress), &index);
        VerifyOrExit(err == ERR_OK && index != -1, error = OT_ERROR_FAILED);
        if (memcmp(&aAddress, prefix, sizeof(prefix->m8)) != 0)
        {
            netif_ip6_addr_set_state(&sNetif, index, IP6_ADDR_PREFERRED);
        }
        else
        {
            netif_ip6_addr_set_state(&sNetif, index, IP6_ADDR_VALID);
        }
    }

exit:
    UNLOCK_TCPIP_CORE();

    if (error != OT_ERROR_NONE)
    {
        otLogInfoPlat("Failed to add address: %d", error);
    }
}

static void delMulticastAddress(const otIp6Address &aAddress)
{
    ip6_addr_t multicast_addr;

    memcpy(&multicast_addr.addr, &aAddress, sizeof(aAddress));
    multicast_addr.zone = IP6_NO_ZONE;
    mld6_leavegroup_netif(&sNetif, &multicast_addr);
}

static void delAddress(const otIp6Address &aAddress)
{
    int8_t index;

    LOCK_TCPIP_CORE();
    index = netif_get_ip6_addr_match(&sNetif, reinterpret_cast<const ip6_addr_t *>(&aAddress));
    VerifyOrExit(index != -1);
    netif_ip6_addr_set_state(&sNetif, index, IP6_ADDR_INVALID);

exit:
    UNLOCK_TCPIP_CORE();
}

static void setupDns(void)
{
#if LWIP_IPV4
    ip_addr_t dnsServer;

    inet_pton(AF_INET6, "64:ff9b::808:808", &dnsServer.u_addr.ip6.addr);
    dnsServer.type            = IPADDR_TYPE_V6;
    dnsServer.u_addr.ip6.zone = IP6_NO_ZONE;
#else
    ip6_addr_t dnsServer;

    inet_pton(AF_INET6, "64:ff9b::808:808", &dnsServer.addr);
    dnsServer.zone = IP6_NO_ZONE;
#endif

    dns_init();
    dns_setserver(0, &dnsServer);
}

static void processStateChange(otChangedFlags aFlags, void *aContext)
{
    otInstance *instance = static_cast<otInstance *>(aContext);

    if (OT_CHANGED_THREAD_NETIF_STATE | aFlags)
    {
        LOCK_TCPIP_CORE();
        if (otLinkIsEnabled(instance))
        {
            otLogInfoPlat("netif up");
            netif_set_up(&sNetif);
        }
        else
        {
            otLogInfoPlat("netif down");
            netif_set_down(&sNetif);
        }
        UNLOCK_TCPIP_CORE();
    }
}

static void processAddress(const otIp6Address *aAddress, uint8_t aPrefixLength, bool aIsAdded, void *aContext)
{
    (void)aContext;

    // All multicast addresses have prefix ff00::/8
    bool isMulticast = (aAddress->mFields.m8[0] == 0xff);

    otLogInfoPlat("address changed");

    if (aIsAdded)
    {
        if (isMulticast)
        {
            addMulticastAddress(*aAddress);
        }
        else
        {
            addAddress(*aAddress);
        }
    }
    else
    {
        if (isMulticast)
        {
            delMulticastAddress(*aAddress);
        }
        else
        {
            delAddress(*aAddress);
        }
    }
}

static void processReceive(otMessage *aMessage, void *aContext)
{
    otError      error      = OT_ERROR_NONE;
    err_t        err        = ERR_OK;
    const size_t kBlockSize = 128;
    uint16_t     length     = otMessageGetLength(aMessage);
    struct pbuf *buffer     = NULL;
    otInstance * aInstance  = static_cast<otInstance *>(aContext);

    assert(sNetif.state == aInstance);

    buffer = pbuf_alloc(PBUF_LINK, length, PBUF_POOL);

    VerifyOrExit(buffer != NULL, error = OT_ERROR_NO_BUFS);

    for (uint16_t i = 0; i < length; i += kBlockSize)
    {
        uint8_t block[kBlockSize];
        int     count = otMessageRead(aMessage, i, block, sizeof(block));

        assert(count > 0);
        err = pbuf_take_at(buffer, block, count, i);
        VerifyOrExit(err == ERR_OK, error = OT_ERROR_FAILED);
    }

    VerifyOrExit(ERR_OK == sNetif.input(buffer, &sNetif), error = OT_ERROR_FAILED);

exit:
    if (error != OT_ERROR_NONE)
    {
        if (buffer != NULL)
        {
            pbuf_free(buffer);
        }

        if (error == OT_ERROR_FAILED)
        {
            otLogWarnPlat("%s failed for lwip error %d", __func__, err);
        }

        otLogWarnPlat("%s failed: %s", __func__, otThreadErrorToString(error));
    }

    otMessageFree(aMessage);
}

static void processTransmit(otInstance *aInstance)
{
    otError    error   = OT_ERROR_NONE;
    otMessage *message = NULL;

    VerifyOrExit(sHeadOutput != NULL);

    message = otIp6NewMessage(aInstance, NULL);
    VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS);

    SuccessOrExit(error = otMessageAppend(message, sHeadOutput->mData, sHeadOutput->mLength));

    error   = otIp6Send(aInstance, message);
    message = NULL;

    {
        OutputEvent *nextEvent = sHeadOutput->mNext;

        free(sHeadOutput);
        sHeadOutput = nextEvent;
    }

    // Notify if more
    if (sHeadOutput == NULL)
    {
        sLastOutput = NULL;
    }
    otrTaskNotifyGive();

exit:
    if (error != OT_ERROR_NONE)
    {
        if (message != NULL)
        {
            otMessageFree(message);
        }

        otLogWarnPlat("Failed to transmit IPv6 packet: %s", otThreadErrorToString(error));
    }
}

void netifInit(void *aContext)
{
    otInstance *instance = static_cast<otInstance *>(aContext);

    memset(&sNetif, 0, sizeof(sNetif));
    // LOCK_TCPIP_CORE();
#if LWIP_IPV4
    netif_add(&sNetif, NULL, NULL, NULL, instance, netifInit, tcpip_input);
#else
    netif_add(&sNetif, instance, netifInit, tcpip_input);
#endif
    netif_set_link_up(&sNetif);
    netif_set_status_callback(&sNetif, HandleNetifStatus);
    // UNLOCK_TCPIP_CORE();

    sGuardOutput = xSemaphoreCreateMutex();
    xSemaphoreGive(sGuardOutput);
    assert(sGuardOutput != NULL);
    otLogInfoPlat("Initialize netif");

    otIp6SetAddressCallback(instance, processAddress, instance);
    otIp6SetReceiveCallback(instance, processReceive, instance);
    otSetStateChangedCallback(instance, processStateChange, instance);
    otIp6SetReceiveFilterEnabled(instance, true);
    otIcmp6SetEchoMode(instance, OT_ICMP6_ECHO_HANDLER_DISABLED);

    netif_set_default(&sNetif);

    setupDns();
}

void netifProcess(otInstance *aInstance)
{
    if (sGuardOutput != NULL && xSemaphoreTake(sGuardOutput, portMAX_DELAY) == pdTRUE)
    {
        processTransmit(aInstance);
        xSemaphoreGive(sGuardOutput);
    }
}
<|endoftext|>


<commit_before>#include "Layer.h"
#include "ShapesUD.h"
#include "BaseFileUD.h"

#include "view/LibraryPanel.h"

#include <easyshape.h>

namespace lr
{

Layer::Layer(int id, LibraryPanel* library)
	: m_id(id)
	, m_library(library)
	, m_editable(true)
	, m_visible(true)
	, m_next_id(0)
{
}

void Layer::TraverseSprite(d2d::IVisitor& visitor, bool order/* = true*/) const
{
	m_sprites.Traverse(visitor, order);
	m_layer_mgr.TraverseSprite(visitor, d2d::DT_ALL, order);
}

void Layer::TraverseSprite(d2d::IVisitor& visitor, d2d::DataTraverseType type, bool order) const
{
	m_sprites.Traverse(visitor, type, order);
	m_layer_mgr.TraverseSprite(visitor, type, order);
}

bool Layer::RemoveSprite(Object* obj)
{
	d2d::ISprite* spr = static_cast<d2d::ISprite*>(obj);
	m_name_set.erase(spr->name);

	if (m_layer_mgr.selected) {
		return m_layer_mgr.selected->Remove(spr);
	} else {
		return m_sprites.Remove(spr);
	}
}

bool Layer::InsertSprite(Object* obj, int idx)
{
	d2d::ISprite* spr = static_cast<d2d::ISprite*>(obj);
	CheckSpriteName(spr);

	if (m_layer_mgr.selected) {
		return m_layer_mgr.selected->Insert(spr);
	} else {
		return m_sprites.Insert(spr, idx);
	}
}

bool Layer::ClearSprite()
{
	m_next_id = 0;
	m_name_set.clear();

	m_layer_mgr.Clear();

	return m_sprites.Clear();
}

bool Layer::ResetOrderSprite(const Object* obj, bool up)
{
	return m_sprites.ResetOrder(static_cast<const d2d::ISprite*>(obj), up);
}

bool Layer::ResetOrderSpriteMost(const Object* obj, bool up)
{
	return m_sprites.ResetOrderMost(static_cast<const d2d::ISprite*>(obj), up);
}

void Layer::TraverseShape(d2d::IVisitor& visitor, bool order) const
{
	m_shapes.Traverse(visitor, order);
	m_layer_mgr.TraverseShape(visitor, order);
}

bool Layer::RemoveShape(Object* obj)
{
	d2d::IShape* shape = static_cast<d2d::IShape*>(obj);
	if (m_layer_mgr.selected) {
		return m_layer_mgr.selected->Remove(shape);
	} else {
		return m_shapes.Remove(shape);
	}
}

bool Layer::InsertShape(Object* obj)
{
	d2d::IShape* shape = static_cast<d2d::IShape*>(obj);
	if (m_layer_mgr.selected) {
		return m_layer_mgr.selected->Insert(shape);
	} else {
		return m_shapes.Insert(shape);
	}
}

bool Layer::ClearShape()
{
	m_layer_mgr.Clear();
	return m_shapes.Clear();
}

void Layer::LoadFromFile(const Json::Value& val, const std::string& dir, int layer_idx)
{
	m_name = val["name"].asString();
	m_editable = val["editable"].asBool();
	m_visible = val["visible"].asBool();

	if (!val["base filepath"].isNull()) {
		m_base_filepath = val["base filepath"].asString();
		LoadFromBaseFile(layer_idx, m_base_filepath, dir);
	}

	LoadSprites(val["sprite"], dir);
	LoadShapes(val["shape"], dir);

	if (!val["layers"].isNull()) {
		m_layer_mgr.LoadFromFile(val["layers"], dir);
	}
}

void Layer::StoreToFile(Json::Value& val, const std::string& dir) const
{
	val["name"] = m_name;
	val["visible"] = m_visible;
	val["editable"] = m_editable;
	if (!m_base_filepath.empty()) {
		val["base filepath"] = m_base_filepath;
	}

	m_layer_mgr.StoreToFile(val["layers"], dir);

	std::vector<d2d::ISprite*> sprites;
	m_sprites.Traverse(d2d::FetchAllVisitor<d2d::ISprite>(sprites), d2d::DT_ALL);
	int count = 0;
	for (int i = 0, n = sprites.size(); i < n; ++i)
	{
		d2d::ISprite* spr = sprites[i];
		if (spr->GetUserData()) {
			UserData* ud = static_cast<UserData*>(spr->GetUserData());
			if (ud->type == UT_BASE_FILE) {
				return;
			}
		}

		if (!IsValidFloat(spr->GetPosition().x) ||
			!IsValidFloat(spr->GetPosition().y)) {
			continue;
		}

		Json::Value spr_val;
		spr_val["filepath"] = d2d::FilenameTools::getRelativePath(dir,
			spr->GetSymbol().GetFilepath()).ToStdString();
		spr->Store(spr_val);
		StoreShapesUD(spr, spr_val);

		val["sprite"][count++] = spr_val;
	}

	std::vector<d2d::IShape*> shapes;
	m_shapes.Traverse(d2d::FetchAllVisitor<d2d::IShape>(shapes), true);
	count = 0;
	for (int i = 0, n = shapes.size(); i < n; ++i) {
		d2d::IShape* shape = shapes[i];
		if (shape->GetUserData()) {
			UserData* ud = static_cast<UserData*>(shape->GetUserData());
			if (ud->type == UT_BASE_FILE) {
				return;
			}
		}
		shape->StoreToFile(val["shape"][count++], dir);
	}
}

bool Layer::Update(int version)
{
	bool ret = false;

	std::vector<d2d::ISprite*> sprites;
	TraverseSprite(d2d::FetchAllVisitor<d2d::ISprite>(sprites), true);
	for (int i = 0, n = sprites.size(); i < n; ++i) {
		bool dirty = sprites[i]->Update(version);
		if (dirty) {
			ret = true;
		}
	}

	if (!m_visible) {
		ret = false;
	}
	return ret;
}

d2d::ISprite* Layer::QuerySprite(const std::string& name) const
{
	QueryNameVisitor<d2d::ISprite> visitor(name);
	m_sprites.Traverse(visitor, true);
	return visitor.GetResult();
}

d2d::IShape* Layer::QueryShape(const std::string& name) const
{
	QueryNameVisitor<d2d::IShape> visitor(name);
	m_shapes.Traverse(visitor, true);
	return visitor.GetResult();
}

bool Layer::IsValidFloat(float f)
{
	return (f == f) && (f <= FLT_MAX && f >= -FLT_MAX);
}

void Layer::LoadSprites(const Json::Value& val, const std::string& dir,
						const std::string& base_path)
{
	int idx = 0;
	Json::Value spr_val = val[idx++];
	while (!spr_val.isNull())
	{
		std::string filepath = d2d::SymbolSearcher::GetSymbolPath(dir, spr_val);
		if (filepath.empty()) {
			std::string filepath = spr_val["filepath"].asString();
			throw d2d::Exception("filepath err: %s", filepath.c_str());
		}
		d2d::ISymbol* symbol = NULL;

		std::string shape_tag = d2d::FileNameParser::getFileTag(d2d::FileNameParser::e_shape);
		std::string shape_filepath = d2d::FilenameTools::getFilenameAddTag(filepath, shape_tag, "json");
		std::string spr_tag;
		if (d2d::FilenameTools::IsFileExist(shape_filepath)) {
			symbol = d2d::SymbolMgr::Instance()->FetchSymbol(shape_filepath);
			const std::vector<d2d::IShape*>& shapes = static_cast<libshape::Symbol*>(symbol)->GetShapes();
			if (!shapes.empty()) {
				spr_tag = shapes[0]->name;
			}
		} else {
			symbol = d2d::SymbolMgr::Instance()->FetchSymbol(filepath);
		}

		if (!symbol) {
			throw d2d::Exception("create symbol err: %s", filepath.c_str());
		}

		d2d::ISprite* sprite = d2d::SpriteFactory::Instance()->create(symbol);
		sprite->Load(spr_val);

		if (!sprite->tag.empty() && sprite->tag[sprite->tag.size()-1] != ';') {
			sprite->tag += ";";
		}
		sprite->tag += spr_tag;

		if (!base_path.empty()) {
			BaseFileUD* ud = new BaseFileUD(base_path);
			sprite->SetUserData(ud);
			sprite->editable = false;
		}
		CheckSpriteName(sprite);
		m_sprites.Insert(sprite);

		LoadShapesUD(spr_val, sprite);

		sprite->Release();
		symbol->Release();

		spr_val = val[idx++];
	}
}

void Layer::LoadShapes(const Json::Value& val, const std::string& dir,
					   const std::string& base_path)
{
	int idx = 0;
	Json::Value shape_val = val[idx++];
	while (!shape_val.isNull())
	{
		d2d::IShape* shape = libshape::ShapeFactory::CreateShapeFromFile(shape_val, dir);
		if (!base_path.empty()) {
			BaseFileUD* ud = new BaseFileUD(base_path);
			shape->SetUserData(ud);
		}
		m_shapes.Insert(shape);
		shape->Release();
		shape_val = val[idx++];
	}
}

void Layer::LoadFromBaseFile(int layer_idx, const std::string& filepath, const std::string& dir)
{
	std::string filepath_full = dir + "\\" + filepath;

	Json::Value value;
	Json::Reader reader;
	std::locale::global(std::locale(""));
	std::ifstream fin(filepath_full.c_str());
	std::locale::global(std::locale("C"));
	reader.parse(fin, value);
	fin.close();

	const Json::Value& layer_val = value["layer"][layer_idx];
	if (!layer_val["base filepath"].isNull()) {
		std::string base_path = layer_val["base filepath"].asString();
		LoadFromBaseFile(layer_idx, base_path, dir);
	}

	LoadSprites(layer_val["sprite"], dir, filepath);
	LoadShapes(layer_val["shape"], dir, filepath);
}

void Layer::CheckSpriteName(d2d::ISprite* spr)
{
	std::set<std::string>::iterator itr
		= m_name_set.find(spr->name);
	if (itr != m_name_set.end())
	{
		spr->name = "_sprite"+wxString::FromDouble(++m_next_id);
		assert(m_name_set.find(spr->name) == m_name_set.end());
	}
	else
	{
		int pos = spr->name.find("_sprite");
		if (pos != std::string::npos) {
			std::string str = spr->name.substr(pos + 7);
			int num = atoi(str.c_str());
			if (m_next_id < num) {
				m_next_id = num;
			}
		}
	}
	m_name_set.insert(spr->name);
}

void Layer::LoadShapesUD(const Json::Value& spr_val, d2d::ISprite* spr) const
{
	if (spr_val["ud"].isNull()) {
		return;
	}

	ShapesUD* ud = new ShapesUD;

	const Json::Value& ud_val = spr_val["ud"];
	ud->layer_id = ud_val["layer"].asInt();
	int idx = 0;
	Json::Value shape_val = ud_val["shape"][idx++];
	while (!shape_val.isNull()) {
		ud->shape_names.push_back(shape_val.asString());
		shape_val = ud_val["shape"][idx++];
	}

	spr->SetUserData(ud);
}

void Layer::StoreShapesUD(d2d::ISprite* spr, Json::Value& spr_val) const
{
	if (!spr->GetUserData()) {
		return;
	}

	UserData* ud = static_cast<UserData*>(spr->GetUserData());
	if (ud->type == UT_BASE_FILE) {
		return;
	}

	Json::Value val;
	ShapesUD* sud = static_cast<ShapesUD*>(ud);
	val["layer"] = sud->layer_id;
	for (int i = 0, n = sud->shape_names.size(); i < n; ++i) {
		val["shape"][i] = sud->shape_names[i];
	}

	spr_val["ud"] = val;
}

//////////////////////////////////////////////////////////////////////////
// class Layer::QueryNameVisitor
//////////////////////////////////////////////////////////////////////////

template<typename T>
Layer::QueryNameVisitor<T>::
QueryNameVisitor(const std::string& name)
	: m_name(name)
	, m_result(NULL)
{
}

template<typename T>
void Layer::QueryNameVisitor<T>::
Visit(Object* object, bool& bFetchNext)
{
	T* t = static_cast<T*>(object);
	if (t->name == m_name) {
		m_result = t;
		bFetchNext = false;
	} else {
		bFetchNext = true;
	}
}

}<commit_msg>[FIXED] delete spr from original layer<commit_after>#include "Layer.h"
#include "ShapesUD.h"
#include "BaseFileUD.h"

#include "view/LibraryPanel.h"

#include <easyshape.h>

namespace lr
{

Layer::Layer(int id, LibraryPanel* library)
	: m_id(id)
	, m_library(library)
	, m_editable(true)
	, m_visible(true)
	, m_next_id(0)
{
}

void Layer::TraverseSprite(d2d::IVisitor& visitor, bool order/* = true*/) const
{
	m_sprites.Traverse(visitor, order);
	m_layer_mgr.TraverseSprite(visitor, d2d::DT_ALL, order);
}

void Layer::TraverseSprite(d2d::IVisitor& visitor, d2d::DataTraverseType type, bool order) const
{
	m_sprites.Traverse(visitor, type, order);
	m_layer_mgr.TraverseSprite(visitor, type, order);
}

bool Layer::RemoveSprite(Object* obj)
{
	d2d::ISprite* spr = static_cast<d2d::ISprite*>(obj);
	m_name_set.erase(spr->name);

	if (m_layer_mgr.selected) {
		if (!m_layer_mgr.selected->Remove(spr)) {
			return m_sprites.Remove(spr);
		} else {
			return true;
		}
	} else {
		return m_sprites.Remove(spr);
	}
}

bool Layer::InsertSprite(Object* obj, int idx)
{
	d2d::ISprite* spr = static_cast<d2d::ISprite*>(obj);
	CheckSpriteName(spr);

	if (m_layer_mgr.selected) {
		return m_layer_mgr.selected->Insert(spr);
	} else {
		return m_sprites.Insert(spr, idx);
	}
}

bool Layer::ClearSprite()
{
	m_next_id = 0;
	m_name_set.clear();

	m_layer_mgr.Clear();

	return m_sprites.Clear();
}

bool Layer::ResetOrderSprite(const Object* obj, bool up)
{
	return m_sprites.ResetOrder(static_cast<const d2d::ISprite*>(obj), up);
}

bool Layer::ResetOrderSpriteMost(const Object* obj, bool up)
{
	return m_sprites.ResetOrderMost(static_cast<const d2d::ISprite*>(obj), up);
}

void Layer::TraverseShape(d2d::IVisitor& visitor, bool order) const
{
	m_shapes.Traverse(visitor, order);
	m_layer_mgr.TraverseShape(visitor, order);
}

bool Layer::RemoveShape(Object* obj)
{
	d2d::IShape* shape = static_cast<d2d::IShape*>(obj);
	if (m_layer_mgr.selected) {
		return m_layer_mgr.selected->Remove(shape);
	} else {
		return m_shapes.Remove(shape);
	}
}

bool Layer::InsertShape(Object* obj)
{
	d2d::IShape* shape = static_cast<d2d::IShape*>(obj);
	if (m_layer_mgr.selected) {
		return m_layer_mgr.selected->Insert(shape);
	} else {
		return m_shapes.Insert(shape);
	}
}

bool Layer::ClearShape()
{
	m_layer_mgr.Clear();
	return m_shapes.Clear();
}

void Layer::LoadFromFile(const Json::Value& val, const std::string& dir, int layer_idx)
{
	m_name = val["name"].asString();
	m_editable = val["editable"].asBool();
	m_visible = val["visible"].asBool();

	if (!val["base filepath"].isNull()) {
		m_base_filepath = val["base filepath"].asString();
		LoadFromBaseFile(layer_idx, m_base_filepath, dir);
	}

	LoadSprites(val["sprite"], dir);
	LoadShapes(val["shape"], dir);

	if (!val["layers"].isNull()) {
		m_layer_mgr.LoadFromFile(val["layers"], dir);
	}
}

void Layer::StoreToFile(Json::Value& val, const std::string& dir) const
{
	val["name"] = m_name;
	val["visible"] = m_visible;
	val["editable"] = m_editable;
	if (!m_base_filepath.empty()) {
		val["base filepath"] = m_base_filepath;
	}

	m_layer_mgr.StoreToFile(val["layers"], dir);

	std::vector<d2d::ISprite*> sprites;
	m_sprites.Traverse(d2d::FetchAllVisitor<d2d::ISprite>(sprites), d2d::DT_ALL);
	int count = 0;
	for (int i = 0, n = sprites.size(); i < n; ++i)
	{
		d2d::ISprite* spr = sprites[i];
		if (spr->GetUserData()) {
			UserData* ud = static_cast<UserData*>(spr->GetUserData());
			if (ud->type == UT_BASE_FILE) {
				return;
			}
		}

		if (!IsValidFloat(spr->GetPosition().x) ||
			!IsValidFloat(spr->GetPosition().y)) {
			continue;
		}

		Json::Value spr_val;
		spr_val["filepath"] = d2d::FilenameTools::getRelativePath(dir,
			spr->GetSymbol().GetFilepath()).ToStdString();
		spr->Store(spr_val);
		StoreShapesUD(spr, spr_val);

		val["sprite"][count++] = spr_val;
	}

	std::vector<d2d::IShape*> shapes;
	m_shapes.Traverse(d2d::FetchAllVisitor<d2d::IShape>(shapes), true);
	count = 0;
	for (int i = 0, n = shapes.size(); i < n; ++i) {
		d2d::IShape* shape = shapes[i];
		if (shape->GetUserData()) {
			UserData* ud = static_cast<UserData*>(shape->GetUserData());
			if (ud->type == UT_BASE_FILE) {
				return;
			}
		}
		shape->StoreToFile(val["shape"][count++], dir);
	}
}

bool Layer::Update(int version)
{
	bool ret = false;

	std::vector<d2d::ISprite*> sprites;
	TraverseSprite(d2d::FetchAllVisitor<d2d::ISprite>(sprites), true);
	for (int i = 0, n = sprites.size(); i < n; ++i) {
		bool dirty = sprites[i]->Update(version);
		if (dirty) {
			ret = true;
		}
	}

	if (!m_visible) {
		ret = false;
	}
	return ret;
}

d2d::ISprite* Layer::QuerySprite(const std::string& name) const
{
	QueryNameVisitor<d2d::ISprite> visitor(name);
	m_sprites.Traverse(visitor, true);
	return visitor.GetResult();
}

d2d::IShape* Layer::QueryShape(const std::string& name) const
{
	QueryNameVisitor<d2d::IShape> visitor(name);
	m_shapes.Traverse(visitor, true);
	return visitor.GetResult();
}

bool Layer::IsValidFloat(float f)
{
	return (f == f) && (f <= FLT_MAX && f >= -FLT_MAX);
}

void Layer::LoadSprites(const Json::Value& val, const std::string& dir,
						const std::string& base_path)
{
	int idx = 0;
	Json::Value spr_val = val[idx++];
	while (!spr_val.isNull())
	{
		std::string filepath = d2d::SymbolSearcher::GetSymbolPath(dir, spr_val);
		if (filepath.empty()) {
			std::string filepath = spr_val["filepath"].asString();
			throw d2d::Exception("filepath err: %s", filepath.c_str());
		}
		d2d::ISymbol* symbol = NULL;

		std::string shape_tag = d2d::FileNameParser::getFileTag(d2d::FileNameParser::e_shape);
		std::string shape_filepath = d2d::FilenameTools::getFilenameAddTag(filepath, shape_tag, "json");
		std::string spr_tag;
		if (d2d::FilenameTools::IsFileExist(shape_filepath)) {
			symbol = d2d::SymbolMgr::Instance()->FetchSymbol(shape_filepath);
			const std::vector<d2d::IShape*>& shapes = static_cast<libshape::Symbol*>(symbol)->GetShapes();
			if (!shapes.empty()) {
				spr_tag = shapes[0]->name;
			}
		} else {
			symbol = d2d::SymbolMgr::Instance()->FetchSymbol(filepath);
		}

		if (!symbol) {
			throw d2d::Exception("create symbol err: %s", filepath.c_str());
		}

		d2d::ISprite* sprite = d2d::SpriteFactory::Instance()->create(symbol);
		sprite->Load(spr_val);

		if (!sprite->tag.empty() && sprite->tag[sprite->tag.size()-1] != ';') {
			sprite->tag += ";";
		}
		sprite->tag += spr_tag;

		if (!base_path.empty()) {
			BaseFileUD* ud = new BaseFileUD(base_path);
			sprite->SetUserData(ud);
			sprite->editable = false;
		}
		CheckSpriteName(sprite);
		m_sprites.Insert(sprite);

		LoadShapesUD(spr_val, sprite);

		sprite->Release();
		symbol->Release();

		spr_val = val[idx++];
	}
}

void Layer::LoadShapes(const Json::Value& val, const std::string& dir,
					   const std::string& base_path)
{
	int idx = 0;
	Json::Value shape_val = val[idx++];
	while (!shape_val.isNull())
	{
		d2d::IShape* shape = libshape::ShapeFactory::CreateShapeFromFile(shape_val, dir);
		if (!base_path.empty()) {
			BaseFileUD* ud = new BaseFileUD(base_path);
			shape->SetUserData(ud);
		}
		m_shapes.Insert(shape);
		shape->Release();
		shape_val = val[idx++];
	}
}

void Layer::LoadFromBaseFile(int layer_idx, const std::string& filepath, const std::string& dir)
{
	std::string filepath_full = dir + "\\" + filepath;

	Json::Value value;
	Json::Reader reader;
	std::locale::global(std::locale(""));
	std::ifstream fin(filepath_full.c_str());
	std::locale::global(std::locale("C"));
	reader.parse(fin, value);
	fin.close();

	const Json::Value& layer_val = value["layer"][layer_idx];
	if (!layer_val["base filepath"].isNull()) {
		std::string base_path = layer_val["base filepath"].asString();
		LoadFromBaseFile(layer_idx, base_path, dir);
	}

	LoadSprites(layer_val["sprite"], dir, filepath);
	LoadShapes(layer_val["shape"], dir, filepath);
}

void Layer::CheckSpriteName(d2d::ISprite* spr)
{
	std::set<std::string>::iterator itr
		= m_name_set.find(spr->name);
	if (itr != m_name_set.end())
	{
		spr->name = "_sprite"+wxString::FromDouble(++m_next_id);
		assert(m_name_set.find(spr->name) == m_name_set.end());
	}
	else
	{
		int pos = spr->name.find("_sprite");
		if (pos != std::string::npos) {
			std::string str = spr->name.substr(pos + 7);
			int num = atoi(str.c_str());
			if (m_next_id < num) {
				m_next_id = num;
			}
		}
	}
	m_name_set.insert(spr->name);
}

void Layer::LoadShapesUD(const Json::Value& spr_val, d2d::ISprite* spr) const
{
	if (spr_val["ud"].isNull()) {
		return;
	}

	ShapesUD* ud = new ShapesUD;

	const Json::Value& ud_val = spr_val["ud"];
	ud->layer_id = ud_val["layer"].asInt();
	int idx = 0;
	Json::Value shape_val = ud_val["shape"][idx++];
	while (!shape_val.isNull()) {
		ud->shape_names.push_back(shape_val.asString());
		shape_val = ud_val["shape"][idx++];
	}

	spr->SetUserData(ud);
}

void Layer::StoreShapesUD(d2d::ISprite* spr, Json::Value& spr_val) const
{
	if (!spr->GetUserData()) {
		return;
	}

	UserData* ud = static_cast<UserData*>(spr->GetUserData());
	if (ud->type == UT_BASE_FILE) {
		return;
	}

	Json::Value val;
	ShapesUD* sud = static_cast<ShapesUD*>(ud);
	val["layer"] = sud->layer_id;
	for (int i = 0, n = sud->shape_names.size(); i < n; ++i) {
		val["shape"][i] = sud->shape_names[i];
	}

	spr_val["ud"] = val;
}

//////////////////////////////////////////////////////////////////////////
// class Layer::QueryNameVisitor
//////////////////////////////////////////////////////////////////////////

template<typename T>
Layer::QueryNameVisitor<T>::
QueryNameVisitor(const std::string& name)
	: m_name(name)
	, m_result(NULL)
{
}

template<typename T>
void Layer::QueryNameVisitor<T>::
Visit(Object* object, bool& bFetchNext)
{
	T* t = static_cast<T*>(object);
	if (t->name == m_name) {
		m_result = t;
		bFetchNext = false;
	} else {
		bFetchNext = true;
	}
}

}<|endoftext|>